• 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, chrome::NOTIFICATION_EXTENSION_INSTALLED,
233       content::NotificationService::AllSources());
234   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_ENABLED,
235       content::NotificationService::AllSources());
236   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
237       content::NotificationService::AllSources());
238   registrar_.Add(this, chrome::NOTIFICATION_CRX_INSTALLER_DONE,
239       content::NotificationService::AllSources());
240   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
241       content::NotificationService::AllSources());
242 
243   // Crashes
244   registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG,
245       content::NotificationService::AllSources());
246   registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
247       content::NotificationService::AllSources());
248 
249   // Profiles (for unclean exit)
250   registrar_.Add(this, chrome::NOTIFICATION_PROFILE_ADDED,
251       content::NotificationService::AllSources());
252 
253   // Page load times
254   registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
255       content::NotificationService::AllSources());
256 }
257 
258 // We check if profiles exited cleanly initialization time in case they were
259 // loaded prior to PerformanceMonitor's initialization. Later profiles will be
260 // checked through the PROFILE_ADDED notification.
CheckForUncleanExits()261 void PerformanceMonitor::CheckForUncleanExits() {
262   DCHECK(database_logging_enabled_);
263 
264   std::vector<Profile*> profiles =
265       g_browser_process->profile_manager()->GetLoadedProfiles();
266 
267   for (std::vector<Profile*>::const_iterator iter = profiles.begin();
268        iter != profiles.end(); ++iter) {
269     if ((*iter)->GetLastSessionExitType() == Profile::EXIT_CRASHED) {
270       BrowserThread::PostBlockingPoolSequencedTask(
271           Database::kDatabaseSequenceToken,
272           FROM_HERE,
273           base::Bind(&PerformanceMonitor::AddUncleanExitEventOnBackgroundThread,
274                      base::Unretained(this),
275                      (*iter)->GetDebugName()));
276     }
277   }
278 }
279 
AddUncleanExitEventOnBackgroundThread(const std::string & profile_name)280 void PerformanceMonitor::AddUncleanExitEventOnBackgroundThread(
281     const std::string& profile_name) {
282   DCHECK(database_logging_enabled_);
283   std::string database_key = kStateProfilePrefix + profile_name;
284   std::string last_active_string = database_->GetStateValue(database_key);
285 
286   // Check if there was no previous time; this should only happen if the profile
287   // was last used prior to PerformanceMonitor's integration. Do nothing in this
288   // case, since the event was prior to the beginning of our recording.
289   if (last_active_string.empty())
290     return;
291 
292   base::Time last_active_time;
293   CHECK(StringToTime(last_active_string, &last_active_time));
294 
295   scoped_ptr<Event> event =
296       util::CreateUncleanExitEvent(last_active_time, profile_name);
297 
298   database_->AddEvent(*event.get());
299 }
300 
CheckForVersionUpdateOnBackgroundThread()301 void PerformanceMonitor::CheckForVersionUpdateOnBackgroundThread() {
302   CHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
303   DCHECK(database_logging_enabled_);
304 
305   chrome::VersionInfo version;
306   DCHECK(version.is_valid());
307   std::string current_version = version.Version();
308 
309   std::string previous_version = database_->GetStateValue(kStateChromeVersion);
310 
311   // We should never have a current_version which is older than the
312   // previous_version.
313   DCHECK(current_version >= previous_version);
314 
315   // If this is the first run, there will not be a stored value for Chrome
316   // version; we insert the current version and will insert an event for the
317   // next update of Chrome. If the previous version is older than the current
318   // version, update the state in the database and insert an event.
319   if (current_version > previous_version) {
320     database_->AddStateValue(kStateChromeVersion, current_version);
321     if (!previous_version.empty()) {
322       scoped_ptr<Event> event = util::CreateChromeUpdateEvent(
323           base::Time::Now(), previous_version, current_version);
324       database_->AddEvent(*event.get());
325     }
326   }
327 }
328 
AddEvent(scoped_ptr<Event> event)329 void PerformanceMonitor::AddEvent(scoped_ptr<Event> event) {
330   DCHECK(database_logging_enabled_);
331 
332   BrowserThread::PostBlockingPoolSequencedTask(
333       Database::kDatabaseSequenceToken,
334       FROM_HERE,
335       base::Bind(&PerformanceMonitor::AddEventOnBackgroundThread,
336                  base::Unretained(this),
337                  base::Passed(&event)));
338 }
339 
AddEventOnBackgroundThread(scoped_ptr<Event> event)340 void PerformanceMonitor::AddEventOnBackgroundThread(scoped_ptr<Event> event) {
341   database_->AddEvent(*event.get());
342 }
343 
AddMetricOnBackgroundThread(const Metric & metric)344 void PerformanceMonitor::AddMetricOnBackgroundThread(const Metric& metric) {
345   DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
346   DCHECK(database_logging_enabled_);
347 
348   database_->AddMetric(metric);
349 }
350 
NotifyInitialized()351 void PerformanceMonitor::NotifyInitialized() {
352   content::NotificationService::current()->Notify(
353       chrome::NOTIFICATION_PERFORMANCE_MONITOR_INITIALIZED,
354       content::Source<PerformanceMonitor>(this),
355       content::NotificationService::NoDetails());
356 
357   initialized_ = true;
358 }
359 
DoTimedCollections()360 void PerformanceMonitor::DoTimedCollections() {
361 #if !defined(OS_ANDROID)
362   // The profile list is only useful for the logged events.
363   if (database_logging_enabled_)
364     UpdateLiveProfiles();
365 #endif
366 
367   GatherMetricsMapOnUIThread();
368 }
369 
GatherMetricsMapOnUIThread()370 void PerformanceMonitor::GatherMetricsMapOnUIThread() {
371   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
372   static int current_update_sequence = 0;
373   // Even in the "somewhat" unlikely event this wraps around,
374   // it doesn't matter. We just check it for inequality.
375   current_update_sequence++;
376 
377   // Find all render child processes; has to be done on the UI thread.
378   for (content::RenderProcessHost::iterator rph_iter =
379            content::RenderProcessHost::AllHostsIterator();
380        !rph_iter.IsAtEnd(); rph_iter.Advance()) {
381     base::ProcessHandle handle = rph_iter.GetCurrentValue()->GetHandle();
382     MarkProcessAsAlive(handle, content::PROCESS_TYPE_RENDERER,
383                        current_update_sequence);
384   }
385 
386   BrowserThread::PostTask(
387       BrowserThread::IO,
388       FROM_HERE,
389       base::Bind(&PerformanceMonitor::GatherMetricsMapOnIOThread,
390                  base::Unretained(this),
391                  current_update_sequence));
392 }
393 
MarkProcessAsAlive(const base::ProcessHandle & handle,int process_type,int current_update_sequence)394 void PerformanceMonitor::MarkProcessAsAlive(const base::ProcessHandle& handle,
395                                         int process_type,
396                                         int current_update_sequence) {
397 
398   if (handle == 0) {
399     // Process may not be valid yet.
400     return;
401   }
402 
403   MetricsMap::iterator process_metrics_iter = metrics_map_.find(handle);
404   if (process_metrics_iter == metrics_map_.end()) {
405     // If we're not already watching the process, let's initialize it.
406     metrics_map_[handle]
407         .Initialize(handle, process_type, current_update_sequence);
408   } else {
409     // If we are watching the process, touch it to keep it alive.
410     ProcessMetricsHistory& process_metrics = process_metrics_iter->second;
411     process_metrics.set_last_update_sequence(current_update_sequence);
412   }
413 }
414 
415 #if !defined(OS_ANDROID)
UpdateLiveProfiles()416 void PerformanceMonitor::UpdateLiveProfiles() {
417   std::string time = TimeToString(base::Time::Now());
418   scoped_ptr<std::set<std::string> > active_profiles(
419       new std::set<std::string>());
420 
421   for (chrome::BrowserIterator it; !it.done(); it.Next())
422     active_profiles->insert(it->profile()->GetDebugName());
423 
424   BrowserThread::PostBlockingPoolSequencedTask(
425       Database::kDatabaseSequenceToken,
426       FROM_HERE,
427       base::Bind(&PerformanceMonitor::UpdateLiveProfilesHelper,
428                  base::Unretained(this),
429                  base::Passed(&active_profiles),
430                  time));
431 }
432 
UpdateLiveProfilesHelper(scoped_ptr<std::set<std::string>> active_profiles,std::string time)433 void PerformanceMonitor::UpdateLiveProfilesHelper(
434     scoped_ptr<std::set<std::string> > active_profiles,
435     std::string time) {
436   CHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
437   DCHECK(database_logging_enabled_);
438 
439   for (std::set<std::string>::const_iterator iter = active_profiles->begin();
440        iter != active_profiles->end(); ++iter) {
441     database_->AddStateValue(kStateProfilePrefix + *iter, time);
442   }
443 }
444 #endif
445 
GatherMetricsMapOnIOThread(int current_update_sequence)446 void PerformanceMonitor::GatherMetricsMapOnIOThread(
447     int current_update_sequence) {
448   CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
449 
450   // Find all child processes (does not include renderers), which has to be
451   // done on the IO thread.
452   for (content::BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) {
453     const content::ChildProcessData& child_process_data = iter.GetData();
454     base::ProcessHandle handle = child_process_data.handle;
455     MarkProcessAsAlive(handle, child_process_data.process_type,
456                        current_update_sequence);
457   }
458 
459   // Add the current (browser) process.
460   MarkProcessAsAlive(base::GetCurrentProcessHandle(),
461                      content::PROCESS_TYPE_BROWSER, current_update_sequence);
462 
463   BrowserThread::PostBlockingPoolSequencedTask(
464       Database::kDatabaseSequenceToken,
465       FROM_HERE,
466       base::Bind(&PerformanceMonitor::StoreMetricsOnBackgroundThread,
467                  base::Unretained(this), current_update_sequence,
468                  performance_data_for_io_thread_));
469 }
470 
StoreMetricsOnBackgroundThread(int current_update_sequence,const PerformanceDataForIOThread & performance_data_for_io_thread)471 void PerformanceMonitor::StoreMetricsOnBackgroundThread(
472     int current_update_sequence,
473     const PerformanceDataForIOThread& performance_data_for_io_thread) {
474   CHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
475 
476   base::Time time_now = base::Time::Now();
477 
478   // The timing can be off by kSampleIntervalInSeconds during any one particular
479   // run, but will be correct over time.
480   bool end_of_cycle = time_now >= next_collection_time_;
481   if (end_of_cycle) {
482     next_collection_time_ +=
483         base::TimeDelta::FromSeconds(gather_interval_in_seconds_);
484   }
485 
486   double cpu_usage = 0.0;
487   size_t private_memory_sum = 0;
488   size_t shared_memory_sum = 0;
489 
490   // Update metrics for all watched processes; remove dead entries from the map.
491   MetricsMap::iterator iter = metrics_map_.begin();
492   while (iter != metrics_map_.end()) {
493     ProcessMetricsHistory& process_metrics = iter->second;
494     if (process_metrics.last_update_sequence() != current_update_sequence) {
495       // Not touched this iteration; let's get rid of it.
496       metrics_map_.erase(iter++);
497     } else {
498       process_metrics.SampleMetrics();
499 
500       if (end_of_cycle) {
501         // Gather averages of previously sampled metrics.
502         cpu_usage += process_metrics.GetAverageCPUUsage();
503 
504         size_t private_memory = 0;
505         size_t shared_memory = 0;
506         process_metrics.GetAverageMemoryBytes(&private_memory, &shared_memory);
507         private_memory_sum += private_memory;
508         shared_memory_sum += shared_memory;
509 
510         process_metrics.EndOfCycle();
511       }
512 
513       ++iter;
514     }
515   }
516 
517   // Store previously-sampled metrics.
518   if (end_of_cycle && database_logging_enabled_) {
519     if (!metrics_map_.empty()) {
520       database_->AddMetric(Metric(METRIC_CPU_USAGE, time_now, cpu_usage));
521       database_->AddMetric(Metric(METRIC_PRIVATE_MEMORY_USAGE,
522                                   time_now,
523                                   static_cast<double>(private_memory_sum)));
524       database_->AddMetric(Metric(METRIC_SHARED_MEMORY_USAGE,
525                                   time_now,
526                                   static_cast<double>(shared_memory_sum)));
527     }
528 
529     database_->AddMetric(
530         Metric(METRIC_NETWORK_BYTES_READ,
531                time_now,
532                static_cast<double>(
533                    performance_data_for_io_thread.network_bytes_read)));
534   }
535 
536   BrowserThread::PostTask(
537       BrowserThread::UI,
538       FROM_HERE,
539       base::Bind(&PerformanceMonitor::StartGatherCycle,
540                  base::Unretained(this)));
541 }
542 
BytesReadOnIOThread(const net::URLRequest & request,const int bytes_read)543 void PerformanceMonitor::BytesReadOnIOThread(const net::URLRequest& request,
544                                              const int bytes_read) {
545   CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
546 
547   if (initialized_ && !request.url().SchemeIsFile())
548     performance_data_for_io_thread_.network_bytes_read += bytes_read;
549 }
550 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)551 void PerformanceMonitor::Observe(int type,
552                                  const content::NotificationSource& source,
553                                  const content::NotificationDetails& details) {
554   DCHECK(database_logging_enabled_);
555 
556   switch (type) {
557     case chrome::NOTIFICATION_EXTENSION_INSTALLED: {
558       AddExtensionEvent(
559           EVENT_EXTENSION_INSTALL,
560           content::Details<const extensions::InstalledExtensionInfo>(details)->
561               extension);
562       break;
563     }
564     case chrome::NOTIFICATION_EXTENSION_ENABLED: {
565       AddExtensionEvent(EVENT_EXTENSION_ENABLE,
566                         content::Details<Extension>(details).ptr());
567       break;
568     }
569     case chrome::NOTIFICATION_EXTENSION_UNLOADED: {
570       const UnloadedExtensionInfo* info =
571           content::Details<UnloadedExtensionInfo>(details).ptr();
572 
573       // Check if the extension was unloaded because it was disabled.
574       if (info->reason == UnloadedExtensionInfo::REASON_DISABLE) {
575         AddExtensionEvent(EVENT_EXTENSION_DISABLE,
576                           info->extension);
577       }
578       break;
579     }
580     case chrome::NOTIFICATION_CRX_INSTALLER_DONE: {
581       const extensions::CrxInstaller* installer =
582           content::Source<extensions::CrxInstaller>(source).ptr();
583       const extensions::Extension* extension =
584           content::Details<Extension>(details).ptr();
585 
586       // Check if the reason for the install was due to a successful
587       // extension update. |extension| is NULL in case of install failure.
588       if (extension &&
589           installer->install_cause() == extension_misc::INSTALL_CAUSE_UPDATE) {
590         AddExtensionEvent(EVENT_EXTENSION_UPDATE, extension);
591       }
592       break;
593     }
594     case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: {
595       AddExtensionEvent(EVENT_EXTENSION_UNINSTALL,
596                         content::Details<Extension>(details).ptr());
597       break;
598     }
599     case content::NOTIFICATION_RENDER_WIDGET_HOST_HANG: {
600       std::string url;
601       content::RenderWidgetHost* widget =
602           content::Source<content::RenderWidgetHost>(source).ptr();
603       if (widget->IsRenderView()) {
604         content::RenderViewHost* view = content::RenderViewHost::From(widget);
605         MaybeGetURLFromRenderView(view, &url);
606       }
607       AddEvent(util::CreateRendererFailureEvent(base::Time::Now(),
608                                                 EVENT_RENDERER_HANG,
609                                                 url));
610       break;
611     }
612     case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: {
613       AddRendererClosedEvent(
614           content::Source<content::RenderProcessHost>(source).ptr(),
615           *content::Details<content::RenderProcessHost::RendererClosedDetails>(
616               details).ptr());
617       break;
618     }
619     case chrome::NOTIFICATION_PROFILE_ADDED: {
620       Profile* profile = content::Source<Profile>(source).ptr();
621       if (profile->GetLastSessionExitType() == Profile::EXIT_CRASHED) {
622         BrowserThread::PostBlockingPoolSequencedTask(
623             Database::kDatabaseSequenceToken,
624             FROM_HERE,
625             base::Bind(
626                 &PerformanceMonitor::AddUncleanExitEventOnBackgroundThread,
627                 base::Unretained(this),
628                 profile->GetDebugName()));
629       }
630       break;
631     }
632     case content::NOTIFICATION_LOAD_STOP: {
633       const content::LoadNotificationDetails* load_details =
634           content::Details<content::LoadNotificationDetails>(details).ptr();
635       if (!load_details)
636         break;
637       BrowserThread::PostBlockingPoolSequencedTask(
638           Database::kDatabaseSequenceToken,
639           FROM_HERE,
640           base::Bind(
641               &PerformanceMonitor::AddMetricOnBackgroundThread,
642               base::Unretained(this),
643               Metric(METRIC_PAGE_LOAD_TIME,
644                      base::Time::Now(),
645                      static_cast<double>(
646                          load_details->load_time.ToInternalValue()))));
647       break;
648     }
649     default: {
650       NOTREACHED();
651       break;
652     }
653   }
654 }
655 
AddExtensionEvent(EventType type,const Extension * extension)656 void PerformanceMonitor::AddExtensionEvent(EventType type,
657                                               const Extension* extension) {
658   DCHECK(type == EVENT_EXTENSION_INSTALL ||
659          type == EVENT_EXTENSION_UNINSTALL ||
660          type == EVENT_EXTENSION_UPDATE ||
661          type == EVENT_EXTENSION_ENABLE ||
662          type == EVENT_EXTENSION_DISABLE);
663   AddEvent(util::CreateExtensionEvent(type,
664                                       base::Time::Now(),
665                                       extension->id(),
666                                       extension->name(),
667                                       extension->url().spec(),
668                                       extension->location(),
669                                       extension->VersionString(),
670                                       extension->description()));
671 }
672 
AddRendererClosedEvent(content::RenderProcessHost * host,const content::RenderProcessHost::RendererClosedDetails & details)673 void PerformanceMonitor::AddRendererClosedEvent(
674     content::RenderProcessHost* host,
675     const content::RenderProcessHost::RendererClosedDetails& details) {
676   // We only care if this is an invalid termination.
677   if (details.status == base::TERMINATION_STATUS_NORMAL_TERMINATION ||
678       details.status == base::TERMINATION_STATUS_STILL_RUNNING)
679     return;
680 
681   // Determine the type of crash.
682   EventType type =
683       details.status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED ?
684       EVENT_RENDERER_KILLED : EVENT_RENDERER_CRASH;
685 
686   // A RenderProcessHost may contain multiple render views - for each valid
687   // render view, extract the url, and append it to the string, comma-separating
688   // the entries.
689   std::string url_list;
690   scoped_ptr<content::RenderWidgetHostIterator> widgets(
691       content::RenderWidgetHost::GetRenderWidgetHosts());
692   while (content::RenderWidgetHost* widget = widgets->GetNextHost()) {
693     if (widget->GetProcess()->GetID() != host->GetID())
694       continue;
695     if (!widget->IsRenderView())
696       continue;
697 
698     content::RenderViewHost* view = content::RenderViewHost::From(widget);
699     std::string url;
700     if (!MaybeGetURLFromRenderView(view, &url))
701       continue;
702 
703     if (!url_list.empty())
704       url_list += ", ";
705 
706     url_list += url;
707   }
708 
709   AddEvent(util::CreateRendererFailureEvent(base::Time::Now(), type, url_list));
710 }
711 
712 }  // namespace performance_monitor
713