Home | History | Annotate | Download | only in performance_monitor
      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)
     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 
     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.
     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.
     88 void DeleteDatabaseOnBackgroundThread(Database* database) {
     89   delete database;
     90 }
     91 
     92 }  // namespace
     93 
     94 bool PerformanceMonitor::initialized_ = false;
     95 
     96 PerformanceMonitor::PerformanceDataForIOThread::PerformanceDataForIOThread()
     97     : network_bytes_read(0) {
     98 }
     99 
    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 
    110 PerformanceMonitor::~PerformanceMonitor() {
    111   BrowserThread::PostBlockingPoolSequencedTask(
    112       Database::kDatabaseSequenceToken,
    113       FROM_HERE,
    114       base::Bind(&DeleteDatabaseOnBackgroundThread, database_.release()));
    115 }
    116 
    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
    128 PerformanceMonitor* PerformanceMonitor::GetInstance() {
    129   return Singleton<PerformanceMonitor>::get();
    130 }
    131 
    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 
    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 
    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 
    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 
    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.
    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 
    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 
    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 
    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 
    342 void PerformanceMonitor::AddEventOnBackgroundThread(scoped_ptr<Event> event) {
    343   database_->AddEvent(*event.get());
    344 }
    345 
    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 
    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 
    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 
    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 
    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)
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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