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