OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/performance_monitor/performance_monitor.h" | 5 #include "chrome/browser/performance_monitor/performance_monitor.h" |
6 | 6 |
7 #include <set> | |
8 #include <vector> | |
9 | |
10 #include "base/bind.h" | |
11 #include "base/command_line.h" | |
12 #include "base/logging.h" | |
13 #include "base/memory/singleton.h" | 7 #include "base/memory/singleton.h" |
14 #include "base/process/process_iterator.h" | 8 #include "base/process/process_iterator.h" |
15 #include "base/stl_util.h" | |
16 #include "base/strings/string_number_conversions.h" | |
17 #include "base/threading/worker_pool.h" | |
18 #include "base/time/time.h" | 9 #include "base/time/time.h" |
19 #include "chrome/browser/browser_process.h" | |
20 #include "chrome/browser/browser_shutdown.h" | |
21 #include "chrome/browser/chrome_notification_types.h" | |
22 #include "chrome/browser/performance_monitor/constants.h" | |
23 #include "chrome/browser/performance_monitor/performance_monitor_util.h" | |
24 #include "chrome/browser/profiles/profile.h" | |
25 #include "chrome/browser/profiles/profile_manager.h" | |
26 #include "chrome/browser/ui/browser.h" | |
27 #include "chrome/browser/ui/browser_iterator.h" | |
28 #include "chrome/common/chrome_switches.h" | |
29 #include "chrome/common/chrome_version_info.h" | |
30 #include "content/public/browser/browser_child_process_host.h" | 10 #include "content/public/browser/browser_child_process_host.h" |
31 #include "content/public/browser/browser_child_process_host_iterator.h" | 11 #include "content/public/browser/browser_child_process_host_iterator.h" |
32 #include "content/public/browser/browser_thread.h" | 12 #include "content/public/browser/browser_thread.h" |
33 #include "content/public/browser/child_process_data.h" | 13 #include "content/public/browser/child_process_data.h" |
34 #include "content/public/browser/load_notification_details.h" | 14 #include "content/public/browser/render_process_host.h" |
35 #include "content/public/browser/notification_service.h" | |
36 #include "content/public/browser/notification_types.h" | |
37 #include "content/public/browser/render_view_host.h" | |
38 #include "content/public/browser/render_widget_host.h" | |
39 #include "content/public/browser/render_widget_host_iterator.h" | |
40 #include "content/public/browser/web_contents.h" | |
41 #include "net/url_request/url_request.h" | |
42 | |
43 #if defined(ENABLE_EXTENSIONS) | |
44 #include "chrome/browser/extensions/crx_installer.h" | |
45 #include "chrome/common/extensions/extension_constants.h" | |
46 #include "extensions/common/extension.h" | |
47 #endif | |
48 | 15 |
49 using content::BrowserThread; | 16 using content::BrowserThread; |
50 | 17 |
51 #if defined(ENABLE_EXTENSIONS) | 18 namespace { |
52 using extensions::Extension; | 19 |
53 using extensions::UnloadedExtensionInfo; | 20 // The default interval at which PerformanceMonitor performs its timed |
54 #endif | 21 // collections. |
| 22 const int kGatherIntervalInSeconds = 120; |
| 23 } |
55 | 24 |
56 namespace performance_monitor { | 25 namespace performance_monitor { |
57 | 26 |
58 namespace { | 27 PerformanceMonitor::PerformanceMonitor() { |
59 | |
60 #if !defined(OS_ANDROID) | |
61 std::string TimeToString(base::Time time) { | |
62 int64 time_int64 = time.ToInternalValue(); | |
63 return base::Int64ToString(time_int64); | |
64 } | |
65 #endif // !defined(OS_ANDROID) | |
66 | |
67 bool StringToTime(std::string time, base::Time* output) { | |
68 int64 time_int64 = 0; | |
69 if (!base::StringToInt64(time, &time_int64)) | |
70 return false; | |
71 *output = base::Time::FromInternalValue(time_int64); | |
72 return true; | |
73 } | |
74 | |
75 // Try to get the URL for the RenderViewHost if the host does not correspond to | |
76 // an incognito profile (we don't store URLs from incognito sessions). Returns | |
77 // true if url has been populated, and false otherwise. | |
78 bool MaybeGetURLFromRenderView(const content::RenderViewHost* view, | |
79 std::string* url) { | |
80 content::WebContents* web_contents = | |
81 content::WebContents::FromRenderViewHost(view); | |
82 | |
83 if (Profile::FromBrowserContext( | |
84 web_contents->GetBrowserContext())->IsOffTheRecord()) { | |
85 return false; | |
86 } | |
87 | |
88 *url = web_contents->GetURL().spec(); | |
89 return true; | |
90 } | |
91 | |
92 // Takes ownership of and deletes |database| on the background thread, to | |
93 // avoid destruction in the middle of an operation. | |
94 void DeleteDatabaseOnBackgroundThread(Database* database) { | |
95 delete database; | |
96 } | |
97 | |
98 } // namespace | |
99 | |
100 bool PerformanceMonitor::initialized_ = false; | |
101 | |
102 PerformanceMonitor::PerformanceDataForIOThread::PerformanceDataForIOThread() | |
103 : network_bytes_read(0) { | |
104 } | |
105 | |
106 PerformanceMonitor::PerformanceMonitor() | |
107 : gather_interval_in_seconds_(kDefaultGatherIntervalInSeconds), | |
108 database_logging_enabled_(false), | |
109 timer_(FROM_HERE, | |
110 base::TimeDelta::FromSeconds(kSampleIntervalInSeconds), | |
111 this, | |
112 &PerformanceMonitor::DoTimedCollections), | |
113 disable_timer_autostart_for_testing_(false) { | |
114 } | 28 } |
115 | 29 |
116 PerformanceMonitor::~PerformanceMonitor() { | 30 PerformanceMonitor::~PerformanceMonitor() { |
117 BrowserThread::PostBlockingPoolSequencedTask( | |
118 Database::kDatabaseSequenceToken, | |
119 FROM_HERE, | |
120 base::Bind(&DeleteDatabaseOnBackgroundThread, database_.release())); | |
121 } | |
122 | |
123 bool PerformanceMonitor::SetDatabasePath(const base::FilePath& path) { | |
124 if (!database_.get()) { | |
125 database_path_ = path; | |
126 return true; | |
127 } | |
128 | |
129 // PerformanceMonitor already initialized with another path. | |
130 return false; | |
131 } | 31 } |
132 | 32 |
133 // static | 33 // static |
134 PerformanceMonitor* PerformanceMonitor::GetInstance() { | 34 PerformanceMonitor* PerformanceMonitor::GetInstance() { |
135 return Singleton<PerformanceMonitor>::get(); | 35 return Singleton<PerformanceMonitor>::get(); |
136 } | 36 } |
137 | 37 |
138 void PerformanceMonitor::Initialize() { | |
139 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
140 | |
141 if (CommandLine::ForCurrentProcess()->HasSwitch( | |
142 switches::kPerformanceMonitorGathering)) { | |
143 database_logging_enabled_ = true; | |
144 | |
145 std::string switch_value = CommandLine::ForCurrentProcess()-> | |
146 GetSwitchValueASCII(switches::kPerformanceMonitorGathering); | |
147 | |
148 if (!switch_value.empty()) { | |
149 int specified_interval = 0; | |
150 if (!base::StringToInt(switch_value, &specified_interval) || | |
151 specified_interval <= 0) { | |
152 LOG(ERROR) << "Invalid value for switch: '" | |
153 << switches::kPerformanceMonitorGathering | |
154 << "'; please use an integer greater than 0."; | |
155 } else { | |
156 gather_interval_in_seconds_ = std::max(specified_interval, | |
157 kSampleIntervalInSeconds); | |
158 } | |
159 } | |
160 } | |
161 | |
162 DCHECK(gather_interval_in_seconds_ >= kSampleIntervalInSeconds); | |
163 | |
164 next_collection_time_ = base::Time::Now() + | |
165 base::TimeDelta::FromSeconds(gather_interval_in_seconds_); | |
166 | |
167 util::PostTaskToDatabaseThreadAndReply( | |
168 FROM_HERE, | |
169 base::Bind(&PerformanceMonitor::InitOnBackgroundThread, | |
170 base::Unretained(this)), | |
171 base::Bind(&PerformanceMonitor::FinishInit, | |
172 base::Unretained(this))); | |
173 } | |
174 | |
175 void PerformanceMonitor::InitOnBackgroundThread() { | |
176 CHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
177 | |
178 if (database_logging_enabled_) { | |
179 if (!database_) | |
180 database_ = Database::Create(database_path_); | |
181 | |
182 if (!database_) { | |
183 LOG(ERROR) << "Could not initialize database; aborting initialization."; | |
184 database_logging_enabled_ = false; | |
185 return; | |
186 } | |
187 | |
188 // Initialize the io thread's performance data to the value in the database; | |
189 // if there isn't a recording in the database, the value stays at 0. | |
190 Metric metric; | |
191 if (database_->GetRecentStatsForActivityAndMetric(METRIC_NETWORK_BYTES_READ, | |
192 &metric)) { | |
193 performance_data_for_io_thread_.network_bytes_read = metric.value; | |
194 } | |
195 } | |
196 } | |
197 | |
198 void PerformanceMonitor::FinishInit() { | |
199 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
200 | |
201 // Events and notifications are only useful if we're logging to the database. | |
202 if (database_logging_enabled_) { | |
203 RegisterForNotifications(); | |
204 CheckForUncleanExits(); | |
205 BrowserThread::PostBlockingPoolSequencedTask( | |
206 Database::kDatabaseSequenceToken, | |
207 FROM_HERE, | |
208 base::Bind(&PerformanceMonitor::CheckForVersionUpdateOnBackgroundThread, | |
209 base::Unretained(this))); | |
210 } | |
211 | |
212 // Post a task to the background thread to a function which does nothing. | |
213 // This will force any tasks the database is performing to finish prior to | |
214 // the reply being sent, since they use the same thread. | |
215 // | |
216 // Important! Make sure that methods in FinishInit() only rely on posting | |
217 // to the background thread, and do not rely upon a reply from the background | |
218 // thread; this is necessary for this notification to be valid. | |
219 util::PostTaskToDatabaseThreadAndReply( | |
220 FROM_HERE, | |
221 base::Bind(&base::DoNothing), | |
222 base::Bind(&PerformanceMonitor::NotifyInitialized, | |
223 base::Unretained(this))); | |
224 } | |
225 | |
226 void PerformanceMonitor::StartGatherCycle() { | 38 void PerformanceMonitor::StartGatherCycle() { |
227 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 39 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
228 | 40 repeating_timer_.Start(FROM_HERE, |
229 // Start our periodic gathering of metrics. | 41 base::TimeDelta::FromSeconds(kGatherIntervalInSeconds), |
230 if (!disable_timer_autostart_for_testing_) | 42 this, |
231 timer_.Reset(); | 43 &PerformanceMonitor::GatherMetricsMapOnUIThread); |
232 } | |
233 | |
234 void PerformanceMonitor::RegisterForNotifications() { | |
235 DCHECK(database_logging_enabled_); | |
236 | |
237 // Extensions | |
238 registrar_.Add( | |
239 this, | |
240 extensions::NOTIFICATION_EXTENSION_WILL_BE_INSTALLED_DEPRECATED, | |
241 content::NotificationService::AllSources()); | |
242 registrar_.Add(this, | |
243 extensions::NOTIFICATION_EXTENSION_ENABLED, | |
244 content::NotificationService::AllSources()); | |
245 registrar_.Add(this, | |
246 extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED, | |
247 content::NotificationService::AllSources()); | |
248 registrar_.Add(this, | |
249 extensions::NOTIFICATION_CRX_INSTALLER_DONE, | |
250 content::NotificationService::AllSources()); | |
251 registrar_.Add(this, | |
252 extensions::NOTIFICATION_EXTENSION_UNINSTALLED_DEPRECATED, | |
253 content::NotificationService::AllSources()); | |
254 | |
255 // Crashes | |
256 registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG, | |
257 content::NotificationService::AllSources()); | |
258 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, | |
259 content::NotificationService::AllSources()); | |
260 | |
261 // Profiles (for unclean exit) | |
262 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_ADDED, | |
263 content::NotificationService::AllSources()); | |
264 | |
265 // Page load times | |
266 registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, | |
267 content::NotificationService::AllSources()); | |
268 } | |
269 | |
270 // We check if profiles exited cleanly initialization time in case they were | |
271 // loaded prior to PerformanceMonitor's initialization. Later profiles will be | |
272 // checked through the PROFILE_ADDED notification. | |
273 void PerformanceMonitor::CheckForUncleanExits() { | |
274 DCHECK(database_logging_enabled_); | |
275 | |
276 std::vector<Profile*> profiles = | |
277 g_browser_process->profile_manager()->GetLoadedProfiles(); | |
278 | |
279 for (std::vector<Profile*>::const_iterator iter = profiles.begin(); | |
280 iter != profiles.end(); ++iter) { | |
281 if ((*iter)->GetLastSessionExitType() == Profile::EXIT_CRASHED) { | |
282 BrowserThread::PostBlockingPoolSequencedTask( | |
283 Database::kDatabaseSequenceToken, | |
284 FROM_HERE, | |
285 base::Bind(&PerformanceMonitor::AddUncleanExitEventOnBackgroundThread, | |
286 base::Unretained(this), | |
287 (*iter)->GetDebugName())); | |
288 } | |
289 } | |
290 } | |
291 | |
292 void PerformanceMonitor::AddUncleanExitEventOnBackgroundThread( | |
293 const std::string& profile_name) { | |
294 DCHECK(database_logging_enabled_); | |
295 std::string database_key = kStateProfilePrefix + profile_name; | |
296 std::string last_active_string = database_->GetStateValue(database_key); | |
297 | |
298 // Check if there was no previous time; this should only happen if the profile | |
299 // was last used prior to PerformanceMonitor's integration. Do nothing in this | |
300 // case, since the event was prior to the beginning of our recording. | |
301 if (last_active_string.empty()) | |
302 return; | |
303 | |
304 base::Time last_active_time; | |
305 CHECK(StringToTime(last_active_string, &last_active_time)); | |
306 | |
307 scoped_ptr<Event> event = | |
308 util::CreateUncleanExitEvent(last_active_time, profile_name); | |
309 | |
310 database_->AddEvent(*event.get()); | |
311 } | |
312 | |
313 void PerformanceMonitor::CheckForVersionUpdateOnBackgroundThread() { | |
314 CHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
315 DCHECK(database_logging_enabled_); | |
316 | |
317 chrome::VersionInfo version; | |
318 DCHECK(version.is_valid()); | |
319 std::string current_version = version.Version(); | |
320 | |
321 std::string previous_version = database_->GetStateValue(kStateChromeVersion); | |
322 | |
323 // We should never have a current_version which is older than the | |
324 // previous_version. | |
325 DCHECK(current_version >= previous_version); | |
326 | |
327 // If this is the first run, there will not be a stored value for Chrome | |
328 // version; we insert the current version and will insert an event for the | |
329 // next update of Chrome. If the previous version is older than the current | |
330 // version, update the state in the database and insert an event. | |
331 if (current_version > previous_version) { | |
332 database_->AddStateValue(kStateChromeVersion, current_version); | |
333 if (!previous_version.empty()) { | |
334 scoped_ptr<Event> event = util::CreateChromeUpdateEvent( | |
335 base::Time::Now(), previous_version, current_version); | |
336 database_->AddEvent(*event.get()); | |
337 } | |
338 } | |
339 } | |
340 | |
341 void PerformanceMonitor::AddEvent(scoped_ptr<Event> event) { | |
342 DCHECK(database_logging_enabled_); | |
343 | |
344 BrowserThread::PostBlockingPoolSequencedTask( | |
345 Database::kDatabaseSequenceToken, | |
346 FROM_HERE, | |
347 base::Bind(&PerformanceMonitor::AddEventOnBackgroundThread, | |
348 base::Unretained(this), | |
349 base::Passed(&event))); | |
350 } | |
351 | |
352 void PerformanceMonitor::AddEventOnBackgroundThread(scoped_ptr<Event> event) { | |
353 database_->AddEvent(*event.get()); | |
354 } | |
355 | |
356 void PerformanceMonitor::AddMetricOnBackgroundThread(const Metric& metric) { | |
357 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
358 DCHECK(database_logging_enabled_); | |
359 | |
360 database_->AddMetric(metric); | |
361 } | |
362 | |
363 void PerformanceMonitor::NotifyInitialized() { | |
364 content::NotificationService::current()->Notify( | |
365 chrome::NOTIFICATION_PERFORMANCE_MONITOR_INITIALIZED, | |
366 content::Source<PerformanceMonitor>(this), | |
367 content::NotificationService::NoDetails()); | |
368 | |
369 initialized_ = true; | |
370 } | |
371 | |
372 void PerformanceMonitor::DoTimedCollections() { | |
373 #if !defined(OS_ANDROID) | |
374 // The profile list is only useful for the logged events. | |
375 if (database_logging_enabled_) | |
376 UpdateLiveProfiles(); | |
377 #endif | |
378 | |
379 GatherMetricsMapOnUIThread(); | |
380 } | 44 } |
381 | 45 |
382 void PerformanceMonitor::GatherMetricsMapOnUIThread() { | 46 void PerformanceMonitor::GatherMetricsMapOnUIThread() { |
383 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 47 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 48 |
384 static int current_update_sequence = 0; | 49 static int current_update_sequence = 0; |
385 // Even in the "somewhat" unlikely event this wraps around, | 50 // Even in the "somewhat" unlikely event this wraps around, |
386 // it doesn't matter. We just check it for inequality. | 51 // it doesn't matter. We just check it for inequality. |
387 current_update_sequence++; | 52 current_update_sequence++; |
388 | 53 |
389 // Find all render child processes; has to be done on the UI thread. | 54 // Find all render child processes; has to be done on the UI thread. |
390 for (content::RenderProcessHost::iterator rph_iter = | 55 for (content::RenderProcessHost::iterator rph_iter = |
391 content::RenderProcessHost::AllHostsIterator(); | 56 content::RenderProcessHost::AllHostsIterator(); |
392 !rph_iter.IsAtEnd(); rph_iter.Advance()) { | 57 !rph_iter.IsAtEnd(); rph_iter.Advance()) { |
393 base::ProcessHandle handle = rph_iter.GetCurrentValue()->GetHandle(); | 58 base::ProcessHandle handle = rph_iter.GetCurrentValue()->GetHandle(); |
394 MarkProcessAsAlive(handle, content::PROCESS_TYPE_RENDERER, | 59 MarkProcessAsAlive(handle, content::PROCESS_TYPE_RENDERER, |
395 current_update_sequence); | 60 current_update_sequence); |
396 } | 61 } |
397 | 62 |
398 BrowserThread::PostTask( | 63 BrowserThread::PostTask( |
399 BrowserThread::IO, | 64 BrowserThread::IO, |
400 FROM_HERE, | 65 FROM_HERE, |
401 base::Bind(&PerformanceMonitor::GatherMetricsMapOnIOThread, | 66 base::Bind(&PerformanceMonitor::GatherMetricsMapOnIOThread, |
402 base::Unretained(this), | 67 base::Unretained(this), |
403 current_update_sequence)); | 68 current_update_sequence)); |
404 } | 69 } |
405 | 70 |
406 void PerformanceMonitor::MarkProcessAsAlive(const base::ProcessHandle& handle, | 71 void PerformanceMonitor::MarkProcessAsAlive(const base::ProcessHandle& handle, |
407 int process_type, | 72 int process_type, |
408 int current_update_sequence) { | 73 int current_update_sequence) { |
409 if (handle == 0) { | 74 if (handle == 0) { |
410 // Process may not be valid yet. | 75 // Process may not be valid yet. |
411 return; | 76 return; |
412 } | 77 } |
413 | 78 |
414 MetricsMap::iterator process_metrics_iter = metrics_map_.find(handle); | 79 MetricsMap::iterator process_metrics_iter = metrics_map_.find(handle); |
415 if (process_metrics_iter == metrics_map_.end()) { | 80 if (process_metrics_iter == metrics_map_.end()) { |
416 // If we're not already watching the process, let's initialize it. | 81 // If we're not already watching the process, let's initialize it. |
417 metrics_map_[handle] | 82 metrics_map_[handle] |
418 .Initialize(handle, process_type, current_update_sequence); | 83 .Initialize(handle, process_type, current_update_sequence); |
419 } else { | 84 } else { |
420 // If we are watching the process, touch it to keep it alive. | 85 // If we are watching the process, touch it to keep it alive. |
421 ProcessMetricsHistory& process_metrics = process_metrics_iter->second; | 86 ProcessMetricsHistory& process_metrics = process_metrics_iter->second; |
422 process_metrics.set_last_update_sequence(current_update_sequence); | 87 process_metrics.set_last_update_sequence(current_update_sequence); |
423 } | 88 } |
424 } | 89 } |
425 | 90 |
426 #if !defined(OS_ANDROID) | |
427 void PerformanceMonitor::UpdateLiveProfiles() { | |
428 std::string time = TimeToString(base::Time::Now()); | |
429 scoped_ptr<std::set<std::string> > active_profiles( | |
430 new std::set<std::string>()); | |
431 | |
432 for (chrome::BrowserIterator it; !it.done(); it.Next()) | |
433 active_profiles->insert(it->profile()->GetDebugName()); | |
434 | |
435 BrowserThread::PostBlockingPoolSequencedTask( | |
436 Database::kDatabaseSequenceToken, | |
437 FROM_HERE, | |
438 base::Bind(&PerformanceMonitor::UpdateLiveProfilesHelper, | |
439 base::Unretained(this), | |
440 base::Passed(&active_profiles), | |
441 time)); | |
442 } | |
443 | |
444 void PerformanceMonitor::UpdateLiveProfilesHelper( | |
445 scoped_ptr<std::set<std::string> > active_profiles, | |
446 std::string time) { | |
447 CHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
448 DCHECK(database_logging_enabled_); | |
449 | |
450 for (std::set<std::string>::const_iterator iter = active_profiles->begin(); | |
451 iter != active_profiles->end(); ++iter) { | |
452 database_->AddStateValue(kStateProfilePrefix + *iter, time); | |
453 } | |
454 } | |
455 #endif | |
456 | |
457 void PerformanceMonitor::GatherMetricsMapOnIOThread( | 91 void PerformanceMonitor::GatherMetricsMapOnIOThread( |
458 int current_update_sequence) { | 92 int current_update_sequence) { |
459 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 93 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
460 | 94 |
461 // Find all child processes (does not include renderers), which has to be | 95 // Find all child processes (does not include renderers), which has to be |
462 // done on the IO thread. | 96 // done on the IO thread. |
463 for (content::BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) { | 97 for (content::BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) { |
464 const content::ChildProcessData& child_process_data = iter.GetData(); | 98 const content::ChildProcessData& child_process_data = iter.GetData(); |
465 base::ProcessHandle handle = child_process_data.handle; | 99 base::ProcessHandle handle = child_process_data.handle; |
466 MarkProcessAsAlive(handle, child_process_data.process_type, | 100 MarkProcessAsAlive(handle, child_process_data.process_type, |
467 current_update_sequence); | 101 current_update_sequence); |
468 } | 102 } |
469 | 103 |
470 // Add the current (browser) process. | 104 // Add the current (browser) process. |
471 MarkProcessAsAlive(base::GetCurrentProcessHandle(), | 105 MarkProcessAsAlive(base::GetCurrentProcessHandle(), |
472 content::PROCESS_TYPE_BROWSER, current_update_sequence); | 106 content::PROCESS_TYPE_BROWSER, current_update_sequence); |
473 | 107 |
474 BrowserThread::PostBlockingPoolSequencedTask( | |
475 Database::kDatabaseSequenceToken, | |
476 FROM_HERE, | |
477 base::Bind(&PerformanceMonitor::StoreMetricsOnBackgroundThread, | |
478 base::Unretained(this), current_update_sequence, | |
479 performance_data_for_io_thread_)); | |
480 } | |
481 | |
482 void PerformanceMonitor::StoreMetricsOnBackgroundThread( | |
483 int current_update_sequence, | |
484 const PerformanceDataForIOThread& performance_data_for_io_thread) { | |
485 CHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
486 | |
487 base::Time time_now = base::Time::Now(); | |
488 | |
489 // The timing can be off by kSampleIntervalInSeconds during any one particular | |
490 // run, but will be correct over time. | |
491 bool end_of_cycle = time_now >= next_collection_time_; | |
492 if (end_of_cycle) { | |
493 next_collection_time_ += | |
494 base::TimeDelta::FromSeconds(gather_interval_in_seconds_); | |
495 } | |
496 | |
497 double cpu_usage = 0.0; | 108 double cpu_usage = 0.0; |
498 size_t private_memory_sum = 0; | 109 size_t private_memory_sum = 0; |
499 size_t shared_memory_sum = 0; | 110 size_t shared_memory_sum = 0; |
500 | 111 |
501 // Update metrics for all watched processes; remove dead entries from the map. | 112 // Update metrics for all watched processes; remove dead entries from the map. |
502 MetricsMap::iterator iter = metrics_map_.begin(); | 113 MetricsMap::iterator iter = metrics_map_.begin(); |
503 while (iter != metrics_map_.end()) { | 114 while (iter != metrics_map_.end()) { |
504 ProcessMetricsHistory& process_metrics = iter->second; | 115 ProcessMetricsHistory& process_metrics = iter->second; |
505 if (process_metrics.last_update_sequence() != current_update_sequence) { | 116 if (process_metrics.last_update_sequence() != current_update_sequence) { |
506 // Not touched this iteration; let's get rid of it. | 117 // Not touched this iteration; let's get rid of it. |
507 metrics_map_.erase(iter++); | 118 metrics_map_.erase(iter++); |
508 } else { | 119 } else { |
509 process_metrics.SampleMetrics(); | 120 process_metrics.SampleMetrics(); |
510 | 121 |
511 if (end_of_cycle) { | 122 // Gather averages of previously sampled metrics. |
512 // Gather averages of previously sampled metrics. | 123 cpu_usage += process_metrics.GetAverageCPUUsage(); |
513 cpu_usage += process_metrics.GetAverageCPUUsage(); | |
514 | 124 |
515 size_t private_memory = 0; | 125 size_t private_memory = 0; |
516 size_t shared_memory = 0; | 126 size_t shared_memory = 0; |
517 process_metrics.GetAverageMemoryBytes(&private_memory, &shared_memory); | 127 process_metrics.GetAverageMemoryBytes(&private_memory, &shared_memory); |
518 private_memory_sum += private_memory; | 128 private_memory_sum += private_memory; |
519 shared_memory_sum += shared_memory; | 129 shared_memory_sum += shared_memory; |
520 | 130 |
521 process_metrics.EndOfCycle(); | 131 process_metrics.EndOfCycle(); |
522 } | |
523 | 132 |
524 ++iter; | 133 ++iter; |
525 } | 134 } |
526 } | 135 } |
527 | |
528 // Store previously-sampled metrics. | |
529 if (end_of_cycle && database_logging_enabled_) { | |
530 if (!metrics_map_.empty()) { | |
531 database_->AddMetric(Metric(METRIC_CPU_USAGE, time_now, cpu_usage)); | |
532 database_->AddMetric(Metric(METRIC_PRIVATE_MEMORY_USAGE, | |
533 time_now, | |
534 static_cast<double>(private_memory_sum))); | |
535 database_->AddMetric(Metric(METRIC_SHARED_MEMORY_USAGE, | |
536 time_now, | |
537 static_cast<double>(shared_memory_sum))); | |
538 } | |
539 | |
540 database_->AddMetric( | |
541 Metric(METRIC_NETWORK_BYTES_READ, | |
542 time_now, | |
543 static_cast<double>( | |
544 performance_data_for_io_thread.network_bytes_read))); | |
545 } | |
546 | |
547 BrowserThread::PostTask( | |
548 BrowserThread::UI, | |
549 FROM_HERE, | |
550 base::Bind(&PerformanceMonitor::StartGatherCycle, | |
551 base::Unretained(this))); | |
552 } | |
553 | |
554 void PerformanceMonitor::BytesReadOnIOThread(const net::URLRequest& request, | |
555 const int bytes_read) { | |
556 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
557 | |
558 if (initialized_ && !request.url().SchemeIsFile()) | |
559 performance_data_for_io_thread_.network_bytes_read += bytes_read; | |
560 } | |
561 | |
562 void PerformanceMonitor::Observe(int type, | |
563 const content::NotificationSource& source, | |
564 const content::NotificationDetails& details) { | |
565 DCHECK(database_logging_enabled_); | |
566 | |
567 switch (type) { | |
568 #if defined(ENABLE_EXTENSIONS) | |
569 case extensions::NOTIFICATION_EXTENSION_WILL_BE_INSTALLED_DEPRECATED: { | |
570 AddExtensionEvent( | |
571 EVENT_EXTENSION_INSTALL, | |
572 content::Details<const extensions::InstalledExtensionInfo>(details)-> | |
573 extension); | |
574 break; | |
575 } | |
576 case extensions::NOTIFICATION_EXTENSION_ENABLED: { | |
577 AddExtensionEvent(EVENT_EXTENSION_ENABLE, | |
578 content::Details<Extension>(details).ptr()); | |
579 break; | |
580 } | |
581 case extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: { | |
582 const UnloadedExtensionInfo* info = | |
583 content::Details<UnloadedExtensionInfo>(details).ptr(); | |
584 | |
585 // Check if the extension was unloaded because it was disabled. | |
586 if (info->reason == UnloadedExtensionInfo::REASON_DISABLE) { | |
587 AddExtensionEvent(EVENT_EXTENSION_DISABLE, | |
588 info->extension); | |
589 } | |
590 break; | |
591 } | |
592 case extensions::NOTIFICATION_CRX_INSTALLER_DONE: { | |
593 const extensions::CrxInstaller* installer = | |
594 content::Source<extensions::CrxInstaller>(source).ptr(); | |
595 const extensions::Extension* extension = | |
596 content::Details<Extension>(details).ptr(); | |
597 | |
598 // Check if the reason for the install was due to a successful | |
599 // extension update. |extension| is NULL in case of install failure. | |
600 if (extension && | |
601 installer->install_cause() == extension_misc::INSTALL_CAUSE_UPDATE) { | |
602 AddExtensionEvent(EVENT_EXTENSION_UPDATE, extension); | |
603 } | |
604 break; | |
605 } | |
606 case extensions::NOTIFICATION_EXTENSION_UNINSTALLED_DEPRECATED: { | |
607 AddExtensionEvent(EVENT_EXTENSION_UNINSTALL, | |
608 content::Details<Extension>(details).ptr()); | |
609 break; | |
610 } | |
611 #endif // defined(ENABLE_EXTENSIONS) | |
612 case content::NOTIFICATION_RENDER_WIDGET_HOST_HANG: { | |
613 std::string url; | |
614 content::RenderWidgetHost* widget = | |
615 content::Source<content::RenderWidgetHost>(source).ptr(); | |
616 if (widget->IsRenderView()) { | |
617 content::RenderViewHost* view = content::RenderViewHost::From(widget); | |
618 MaybeGetURLFromRenderView(view, &url); | |
619 } | |
620 AddEvent(util::CreateRendererFailureEvent(base::Time::Now(), | |
621 EVENT_RENDERER_HANG, | |
622 url)); | |
623 break; | |
624 } | |
625 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: { | |
626 AddRendererClosedEvent( | |
627 content::Source<content::RenderProcessHost>(source).ptr(), | |
628 *content::Details<content::RenderProcessHost::RendererClosedDetails>( | |
629 details).ptr()); | |
630 break; | |
631 } | |
632 case chrome::NOTIFICATION_PROFILE_ADDED: { | |
633 Profile* profile = content::Source<Profile>(source).ptr(); | |
634 if (profile->GetLastSessionExitType() == Profile::EXIT_CRASHED) { | |
635 BrowserThread::PostBlockingPoolSequencedTask( | |
636 Database::kDatabaseSequenceToken, | |
637 FROM_HERE, | |
638 base::Bind( | |
639 &PerformanceMonitor::AddUncleanExitEventOnBackgroundThread, | |
640 base::Unretained(this), | |
641 profile->GetDebugName())); | |
642 } | |
643 break; | |
644 } | |
645 case content::NOTIFICATION_LOAD_STOP: { | |
646 const content::LoadNotificationDetails* load_details = | |
647 content::Details<content::LoadNotificationDetails>(details).ptr(); | |
648 if (!load_details) | |
649 break; | |
650 BrowserThread::PostBlockingPoolSequencedTask( | |
651 Database::kDatabaseSequenceToken, | |
652 FROM_HERE, | |
653 base::Bind( | |
654 &PerformanceMonitor::AddMetricOnBackgroundThread, | |
655 base::Unretained(this), | |
656 Metric(METRIC_PAGE_LOAD_TIME, | |
657 base::Time::Now(), | |
658 static_cast<double>( | |
659 load_details->load_time.ToInternalValue())))); | |
660 break; | |
661 } | |
662 default: { | |
663 NOTREACHED(); | |
664 break; | |
665 } | |
666 } | |
667 } | |
668 | |
669 #if defined(ENABLE_EXTENSIONS) | |
670 void PerformanceMonitor::AddExtensionEvent(EventType type, | |
671 const Extension* extension) { | |
672 DCHECK(type == EVENT_EXTENSION_INSTALL || | |
673 type == EVENT_EXTENSION_UNINSTALL || | |
674 type == EVENT_EXTENSION_UPDATE || | |
675 type == EVENT_EXTENSION_ENABLE || | |
676 type == EVENT_EXTENSION_DISABLE); | |
677 AddEvent(util::CreateExtensionEvent(type, | |
678 base::Time::Now(), | |
679 extension->id(), | |
680 extension->name(), | |
681 extension->url().spec(), | |
682 extension->location(), | |
683 extension->VersionString(), | |
684 extension->description())); | |
685 } | |
686 #endif // defined(ENABLE_EXTENSIONS) | |
687 | |
688 void PerformanceMonitor::AddRendererClosedEvent( | |
689 content::RenderProcessHost* host, | |
690 const content::RenderProcessHost::RendererClosedDetails& details) { | |
691 // We only care if this is an invalid termination. | |
692 if (details.status == base::TERMINATION_STATUS_NORMAL_TERMINATION || | |
693 details.status == base::TERMINATION_STATUS_STILL_RUNNING) | |
694 return; | |
695 | |
696 // Determine the type of crash. | |
697 EventType type = | |
698 details.status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED ? | |
699 EVENT_RENDERER_KILLED : EVENT_RENDERER_CRASH; | |
700 | |
701 // A RenderProcessHost may contain multiple render views - for each valid | |
702 // render view, extract the url, and append it to the string, comma-separating | |
703 // the entries. | |
704 std::string url_list; | |
705 scoped_ptr<content::RenderWidgetHostIterator> widgets( | |
706 content::RenderWidgetHost::GetRenderWidgetHosts()); | |
707 while (content::RenderWidgetHost* widget = widgets->GetNextHost()) { | |
708 if (widget->GetProcess()->GetID() != host->GetID()) | |
709 continue; | |
710 if (!widget->IsRenderView()) | |
711 continue; | |
712 | |
713 content::RenderViewHost* view = content::RenderViewHost::From(widget); | |
714 std::string url; | |
715 if (!MaybeGetURLFromRenderView(view, &url)) | |
716 continue; | |
717 | |
718 if (!url_list.empty()) | |
719 url_list += ", "; | |
720 | |
721 url_list += url; | |
722 } | |
723 | |
724 AddEvent(util::CreateRendererFailureEvent(base::Time::Now(), type, url_list)); | |
725 } | 136 } |
726 | 137 |
727 } // namespace performance_monitor | 138 } // namespace performance_monitor |
OLD | NEW |