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/process/process_iterator.h"
     14 #include "base/stl_util.h"
     15 #include "base/strings/string_number_conversions.h"
     16 #include "base/threading/worker_pool.h"
     17 #include "base/time/time.h"
     18 #include "chrome/browser/browser_process.h"
     19 #include "chrome/browser/browser_shutdown.h"
     20 #include "chrome/browser/chrome_notification_types.h"
     21 #include "chrome/browser/extensions/crx_installer.h"
     22 #include "chrome/browser/performance_monitor/constants.h"
     23 #include "chrome/browser/performance_monitor/performance_monitor_util.h"
     24 #include "chrome/browser/profiles/profile.h"
     25 #include "chrome/browser/profiles/profile_manager.h"
     26 #include "chrome/browser/ui/browser.h"
     27 #include "chrome/browser/ui/browser_iterator.h"
     28 #include "chrome/common/chrome_switches.h"
     29 #include "chrome/common/chrome_version_info.h"
     30 #include "chrome/common/extensions/extension.h"
     31 #include "chrome/common/extensions/extension_constants.h"
     32 #include "chrome/test/base/chrome_process_util.h"
     33 #include "content/public/browser/browser_child_process_host.h"
     34 #include "content/public/browser/browser_thread.h"
     35 #include "content/public/browser/load_notification_details.h"
     36 #include "content/public/browser/notification_service.h"
     37 #include "content/public/browser/notification_types.h"
     38 #include "content/public/browser/render_view_host.h"
     39 #include "content/public/browser/render_widget_host.h"
     40 #include "content/public/browser/web_contents.h"
     41 #include "net/url_request/url_request.h"
     42 
     43 using content::BrowserThread;
     44 using extensions::Extension;
     45 
     46 namespace performance_monitor {
     47 
     48 namespace {
     49 
     50 const uint32 kAccessFlags = base::kProcessAccessDuplicateHandle |
     51                             base::kProcessAccessQueryInformation |
     52                             base::kProcessAccessTerminate |
     53                             base::kProcessAccessWaitForTermination;
     54 
     55 bool g_started_initialization = false;
     56 
     57 std::string TimeToString(base::Time time) {
     58   int64 time_int64 = time.ToInternalValue();
     59   return base::Int64ToString(time_int64);
     60 }
     61 
     62 bool StringToTime(std::string time, base::Time* output) {
     63   int64 time_int64 = 0;
     64   if (!base::StringToInt64(time, &time_int64))
     65     return false;
     66   *output = base::Time::FromInternalValue(time_int64);
     67   return true;
     68 }
     69 
     70 // Try to get the URL for the RenderViewHost if the host does not correspond to
     71 // an incognito profile (we don't store URLs from incognito sessions). Returns
     72 // true if url has been populated, and false otherwise.
     73 bool MaybeGetURLFromRenderView(const content::RenderViewHost* view,
     74                                std::string* url) {
     75   content::WebContents* web_contents =
     76       content::WebContents::FromRenderViewHost(view);
     77 
     78   if (Profile::FromBrowserContext(
     79           web_contents->GetBrowserContext())->IsOffTheRecord()) {
     80     return false;
     81   }
     82 
     83   *url = web_contents->GetURL().spec();
     84   return true;
     85 }
     86 
     87 // Takes ownership of and deletes |database| on the background thread, to
     88 // avoid destruction in the middle of an operation.
     89 void DeleteDatabaseOnBackgroundThread(Database* database) {
     90   delete database;
     91 }
     92 
     93 }  // namespace
     94 
     95 bool PerformanceMonitor::initialized_ = false;
     96 
     97 PerformanceMonitor::PerformanceDataForIOThread::PerformanceDataForIOThread()
     98     : network_bytes_read(0) {
     99 }
    100 
    101 PerformanceMonitor::PerformanceMonitor() : metrics_map_(new MetricsMap) {}
    102 
    103 PerformanceMonitor::~PerformanceMonitor() {
    104   BrowserThread::PostBlockingPoolSequencedTask(
    105       Database::kDatabaseSequenceToken,
    106       FROM_HERE,
    107       base::Bind(&DeleteDatabaseOnBackgroundThread, database_.release()));
    108 }
    109 
    110 bool PerformanceMonitor::SetDatabasePath(const base::FilePath& path) {
    111   if (!database_.get()) {
    112     database_path_ = path;
    113     return true;
    114   }
    115 
    116   // PerformanceMonitor already initialized with another path.
    117   return false;
    118 }
    119 
    120 // static
    121 PerformanceMonitor* PerformanceMonitor::GetInstance() {
    122   return Singleton<PerformanceMonitor>::get();
    123 }
    124 
    125 void PerformanceMonitor::Start() {
    126   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    127 
    128   // Avoid responding to multiple calls to Start().
    129   if (g_started_initialization)
    130     return;
    131 
    132   g_started_initialization = true;
    133   util::PostTaskToDatabaseThreadAndReply(
    134       FROM_HERE,
    135       base::Bind(&PerformanceMonitor::InitOnBackgroundThread,
    136                  base::Unretained(this)),
    137       base::Bind(&PerformanceMonitor::FinishInit,
    138                  base::Unretained(this)));
    139 }
    140 
    141 void PerformanceMonitor::InitOnBackgroundThread() {
    142   CHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
    143   database_ = Database::Create(database_path_);
    144   if (!database_) {
    145     LOG(ERROR) << "Could not initialize database; aborting initialization.";
    146     return;
    147   }
    148 
    149   // Initialize the io thread's performance data to the value in the database;
    150   // if there isn't a recording in the database, the value stays at 0.
    151   Metric metric;
    152   if (database_->GetRecentStatsForActivityAndMetric(METRIC_NETWORK_BYTES_READ,
    153                                                     &metric)) {
    154     performance_data_for_io_thread_.network_bytes_read = metric.value;
    155   }
    156 }
    157 
    158 void PerformanceMonitor::FinishInit() {
    159   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    160   // Short-circuit the initialization process if the database wasn't properly
    161   // created. This will prevent PerformanceMonitor from performing any actions,
    162   // including observing events.
    163   if (!database_)
    164     return;
    165 
    166   RegisterForNotifications();
    167   CheckForUncleanExits();
    168   BrowserThread::PostBlockingPoolSequencedTask(
    169       Database::kDatabaseSequenceToken,
    170       FROM_HERE,
    171       base::Bind(&PerformanceMonitor::CheckForVersionUpdateOnBackgroundThread,
    172                  base::Unretained(this)));
    173 
    174   int gather_interval_in_seconds = kDefaultGatherIntervalInSeconds;
    175   if (CommandLine::ForCurrentProcess()->HasSwitch(
    176           switches::kPerformanceMonitorGathering) &&
    177       !CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
    178           switches::kPerformanceMonitorGathering).empty()) {
    179     int specified_interval = 0;
    180     if (!base::StringToInt(
    181             CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
    182                 switches::kPerformanceMonitorGathering),
    183             &specified_interval) || specified_interval <= 0) {
    184       LOG(ERROR) << "Invalid value for switch: '"
    185                  << switches::kPerformanceMonitorGathering
    186                  << "'; please use an integer greater than 0.";
    187     } else {
    188       gather_interval_in_seconds = specified_interval;
    189     }
    190   }
    191 
    192   timer_.Start(FROM_HERE,
    193                base::TimeDelta::FromSeconds(gather_interval_in_seconds),
    194                this,
    195                &PerformanceMonitor::DoTimedCollections);
    196 
    197   // Post a task to the background thread to a function which does nothing.
    198   // This will force any tasks the database is performing to finish prior to
    199   // the reply being sent, since they use the same thread.
    200   //
    201   // Important! Make sure that methods in FinishInit() only rely on posting
    202   // to the background thread, and do not rely upon a reply from the background
    203   // thread; this is necessary for this notification to be valid.
    204   util::PostTaskToDatabaseThreadAndReply(
    205       FROM_HERE,
    206       base::Bind(&base::DoNothing),
    207       base::Bind(&PerformanceMonitor::NotifyInitialized,
    208                  base::Unretained(this)));
    209 }
    210 
    211 void PerformanceMonitor::RegisterForNotifications() {
    212   // Extensions
    213   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED,
    214       content::NotificationService::AllSources());
    215   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_ENABLED,
    216       content::NotificationService::AllSources());
    217   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
    218       content::NotificationService::AllSources());
    219   registrar_.Add(this, chrome::NOTIFICATION_CRX_INSTALLER_DONE,
    220       content::NotificationService::AllSources());
    221   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
    222       content::NotificationService::AllSources());
    223 
    224   // Crashes
    225   registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_HANG,
    226       content::NotificationService::AllSources());
    227   registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
    228       content::NotificationService::AllSources());
    229 
    230   // Profiles (for unclean exit)
    231   registrar_.Add(this, chrome::NOTIFICATION_PROFILE_ADDED,
    232       content::NotificationService::AllSources());
    233 
    234   // Page load times
    235   registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
    236       content::NotificationService::AllSources());
    237 }
    238 
    239 // We check if profiles exited cleanly initialization time in case they were
    240 // loaded prior to PerformanceMonitor's initialization. Later profiles will be
    241 // checked through the PROFILE_ADDED notification.
    242 void PerformanceMonitor::CheckForUncleanExits() {
    243   std::vector<Profile*> profiles =
    244       g_browser_process->profile_manager()->GetLoadedProfiles();
    245 
    246   for (std::vector<Profile*>::const_iterator iter = profiles.begin();
    247        iter != profiles.end(); ++iter) {
    248     if ((*iter)->GetLastSessionExitType() == Profile::EXIT_CRASHED) {
    249       BrowserThread::PostBlockingPoolSequencedTask(
    250           Database::kDatabaseSequenceToken,
    251           FROM_HERE,
    252           base::Bind(&PerformanceMonitor::AddUncleanExitEventOnBackgroundThread,
    253                      base::Unretained(this),
    254                      (*iter)->GetDebugName()));
    255     }
    256   }
    257 }
    258 
    259 void PerformanceMonitor::AddUncleanExitEventOnBackgroundThread(
    260   const std::string& profile_name) {
    261   std::string database_key = kStateProfilePrefix + profile_name;
    262   std::string last_active_string = database_->GetStateValue(database_key);
    263 
    264   // Check if there was no previous time; this should only happen if the profile
    265   // was last used prior to PerformanceMonitor's integration. Do nothing in this
    266   // case, since the event was prior to the beginning of our recording.
    267   if (last_active_string.empty())
    268     return;
    269 
    270   base::Time last_active_time;
    271   CHECK(StringToTime(last_active_string, &last_active_time));
    272 
    273   scoped_ptr<Event> event =
    274       util::CreateUncleanExitEvent(last_active_time, profile_name);
    275 
    276   database_->AddEvent(*event.get());
    277 }
    278 
    279 void PerformanceMonitor::CheckForVersionUpdateOnBackgroundThread() {
    280   CHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
    281 
    282   chrome::VersionInfo version;
    283   DCHECK(version.is_valid());
    284   std::string current_version = version.Version();
    285 
    286   std::string previous_version = database_->GetStateValue(kStateChromeVersion);
    287 
    288   // We should never have a current_version which is older than the
    289   // previous_version.
    290   DCHECK(current_version >= previous_version);
    291 
    292   // If this is the first run, there will not be a stored value for Chrome
    293   // version; we insert the current version and will insert an event for the
    294   // next update of Chrome. If the previous version is older than the current
    295   // version, update the state in the database and insert an event.
    296   if (current_version > previous_version) {
    297     database_->AddStateValue(kStateChromeVersion, current_version);
    298     if (!previous_version.empty()) {
    299       scoped_ptr<Event> event = util::CreateChromeUpdateEvent(
    300           base::Time::Now(), previous_version, current_version);
    301       database_->AddEvent(*event.get());
    302     }
    303   }
    304 }
    305 
    306 void PerformanceMonitor::AddEvent(scoped_ptr<Event> event) {
    307   BrowserThread::PostBlockingPoolSequencedTask(
    308       Database::kDatabaseSequenceToken,
    309       FROM_HERE,
    310       base::Bind(&PerformanceMonitor::AddEventOnBackgroundThread,
    311                  base::Unretained(this),
    312                  base::Passed(&event)));
    313 }
    314 
    315 void PerformanceMonitor::AddEventOnBackgroundThread(scoped_ptr<Event> event) {
    316   database_->AddEvent(*event.get());
    317 }
    318 
    319 void PerformanceMonitor::AddMetricOnBackgroundThread(const Metric& metric) {
    320   DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
    321   database_->AddMetric(metric);
    322 }
    323 
    324 void PerformanceMonitor::NotifyInitialized() {
    325   content::NotificationService::current()->Notify(
    326       chrome::NOTIFICATION_PERFORMANCE_MONITOR_INITIALIZED,
    327       content::Source<PerformanceMonitor>(this),
    328       content::NotificationService::NoDetails());
    329 
    330   initialized_ = true;
    331 }
    332 
    333 void PerformanceMonitor::GatherStatisticsOnBackgroundThread() {
    334   CHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
    335 
    336   // Because the CPU usage is gathered as an average since the last time the
    337   // function was called, while the memory usage is gathered as an instantaneous
    338   // usage, the CPU usage is gathered before the metrics map is wiped.
    339   GatherCPUUsageOnBackgroundThread();
    340   UpdateMetricsMapOnBackgroundThread();
    341   GatherMemoryUsageOnBackgroundThread();
    342 }
    343 
    344 void PerformanceMonitor::GatherCPUUsageOnBackgroundThread() {
    345   if (metrics_map_->size()) {
    346     double cpu_usage = 0;
    347     for (MetricsMap::const_iterator iter = metrics_map_->begin();
    348          iter != metrics_map_->end(); ++iter) {
    349       cpu_usage += iter->second->GetCPUUsage();
    350     }
    351 
    352     database_->AddMetric(Metric(METRIC_CPU_USAGE,
    353                                 base::Time::Now(),
    354                                 cpu_usage));
    355   }
    356 }
    357 
    358 void PerformanceMonitor::GatherMemoryUsageOnBackgroundThread() {
    359   size_t private_memory_sum = 0;
    360   size_t shared_memory_sum = 0;
    361   for (MetricsMap::const_iterator iter = metrics_map_->begin();
    362        iter != metrics_map_->end(); ++iter) {
    363     size_t private_memory = 0;
    364     size_t shared_memory = 0;
    365     if (iter->second->GetMemoryBytes(&private_memory, &shared_memory)) {
    366       private_memory_sum += private_memory;
    367       shared_memory_sum += shared_memory;
    368     } else {
    369       LOG(WARNING) << "GetMemoryBytes returned NULL (platform-specific error)";
    370     }
    371   }
    372 
    373   database_->AddMetric(Metric(METRIC_PRIVATE_MEMORY_USAGE,
    374                               base::Time::Now(),
    375                               static_cast<double>(private_memory_sum)));
    376   database_->AddMetric(Metric(METRIC_SHARED_MEMORY_USAGE,
    377                               base::Time::Now(),
    378                               static_cast<double>(shared_memory_sum)));
    379 }
    380 
    381 void PerformanceMonitor::UpdateMetricsMapOnBackgroundThread() {
    382   MetricsMap* new_map = new MetricsMap;
    383 
    384   base::ProcessId browser_pid = base::GetCurrentProcId();
    385   ChromeProcessList chrome_processes(GetRunningChromeProcesses(browser_pid));
    386   for (ChromeProcessList::const_iterator pid_iter = chrome_processes.begin();
    387        pid_iter != chrome_processes.end(); ++pid_iter) {
    388     base::ProcessHandle handle;
    389     if (base::OpenProcessHandleWithAccess(*pid_iter, kAccessFlags, &handle)) {
    390       // If we were already watching this process, transfer it to the new map.
    391       if (ContainsKey(*metrics_map_, handle)) {
    392         (*new_map)[handle] = (*metrics_map_)[handle];
    393         continue;
    394       }
    395 
    396       // Otherwise, gather information and prime the CPU usage to be gathered.
    397 #if defined (OS_MACOSX)
    398       linked_ptr<base::ProcessMetrics> process_metrics(
    399           base::ProcessMetrics::CreateProcessMetrics(
    400               handle, content::BrowserChildProcessHost::GetPortProvider()));
    401 #else
    402       linked_ptr<base::ProcessMetrics> process_metrics(
    403           base::ProcessMetrics::CreateProcessMetrics(handle));
    404 #endif
    405 
    406       process_metrics->GetCPUUsage();
    407 
    408       (*new_map)[handle] = process_metrics;
    409     }
    410   }
    411 
    412   metrics_map_.reset(new_map);
    413 }
    414 
    415 void PerformanceMonitor::UpdateLiveProfiles() {
    416   std::string time = TimeToString(base::Time::Now());
    417   scoped_ptr<std::set<std::string> > active_profiles(
    418       new std::set<std::string>());
    419 
    420   for (chrome::BrowserIterator it; !it.done(); it.Next())
    421     active_profiles->insert(it->profile()->GetDebugName());
    422 
    423   BrowserThread::PostBlockingPoolSequencedTask(
    424       Database::kDatabaseSequenceToken,
    425       FROM_HERE,
    426       base::Bind(&PerformanceMonitor::UpdateLiveProfilesHelper,
    427                  base::Unretained(this),
    428                  base::Passed(&active_profiles),
    429                  time));
    430 }
    431 
    432 void PerformanceMonitor::UpdateLiveProfilesHelper(
    433     scoped_ptr<std::set<std::string> > active_profiles,
    434     std::string time) {
    435   CHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
    436 
    437   for (std::set<std::string>::const_iterator iter = active_profiles->begin();
    438        iter != active_profiles->end(); ++iter) {
    439     database_->AddStateValue(kStateProfilePrefix + *iter, time);
    440   }
    441 }
    442 
    443 void PerformanceMonitor::DoTimedCollections() {
    444   UpdateLiveProfiles();
    445 
    446   BrowserThread::PostBlockingPoolSequencedTask(
    447       Database::kDatabaseSequenceToken,
    448       FROM_HERE,
    449       base::Bind(&PerformanceMonitor::GatherStatisticsOnBackgroundThread,
    450                  base::Unretained(this)));
    451 
    452   BrowserThread::PostTask(
    453       BrowserThread::IO,
    454       FROM_HERE,
    455       base::Bind(&PerformanceMonitor::CallInsertIOData,
    456                  base::Unretained(this)));
    457 }
    458 
    459 void PerformanceMonitor::CallInsertIOData() {
    460   CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    461 
    462   BrowserThread::PostBlockingPoolSequencedTask(
    463       Database::kDatabaseSequenceToken,
    464       FROM_HERE,
    465       base::Bind(&PerformanceMonitor::InsertIOData,
    466                  base::Unretained(this),
    467                  performance_data_for_io_thread_));
    468 }
    469 
    470 void PerformanceMonitor::InsertIOData(
    471     const PerformanceDataForIOThread& performance_data_for_io_thread) {
    472   CHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
    473   database_->AddMetric(
    474       Metric(METRIC_NETWORK_BYTES_READ,
    475              base::Time::Now(),
    476              static_cast<double>(
    477                  performance_data_for_io_thread.network_bytes_read)));
    478 }
    479 
    480 void PerformanceMonitor::BytesReadOnIOThread(const net::URLRequest& request,
    481                                              const int bytes_read) {
    482   CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    483 
    484   if (initialized_ && !request.url().SchemeIsFile())
    485     performance_data_for_io_thread_.network_bytes_read += bytes_read;
    486 }
    487 
    488 void PerformanceMonitor::Observe(int type,
    489                                  const content::NotificationSource& source,
    490                                  const content::NotificationDetails& details) {
    491   switch (type) {
    492     case chrome::NOTIFICATION_EXTENSION_INSTALLED: {
    493       AddExtensionEvent(
    494           EVENT_EXTENSION_INSTALL,
    495           content::Details<const extensions::InstalledExtensionInfo>(details)->
    496               extension);
    497       break;
    498     }
    499     case chrome::NOTIFICATION_EXTENSION_ENABLED: {
    500       AddExtensionEvent(EVENT_EXTENSION_ENABLE,
    501                         content::Details<Extension>(details).ptr());
    502       break;
    503     }
    504     case chrome::NOTIFICATION_EXTENSION_UNLOADED: {
    505       const extensions::UnloadedExtensionInfo* info =
    506           content::Details<extensions::UnloadedExtensionInfo>(details).ptr();
    507 
    508       // Check if the extension was unloaded because it was disabled.
    509       if (info->reason == extension_misc::UNLOAD_REASON_DISABLE) {
    510         AddExtensionEvent(EVENT_EXTENSION_DISABLE,
    511                           info->extension);
    512       }
    513       break;
    514     }
    515     case chrome::NOTIFICATION_CRX_INSTALLER_DONE: {
    516       const extensions::CrxInstaller* installer =
    517           content::Source<extensions::CrxInstaller>(source).ptr();
    518       const extensions::Extension* extension =
    519           content::Details<Extension>(details).ptr();
    520 
    521       // Check if the reason for the install was due to a successful
    522       // extension update. |extension| is NULL in case of install failure.
    523       if (extension &&
    524           installer->install_cause() == extension_misc::INSTALL_CAUSE_UPDATE) {
    525         AddExtensionEvent(EVENT_EXTENSION_UPDATE, extension);
    526       }
    527       break;
    528     }
    529     case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: {
    530       AddExtensionEvent(EVENT_EXTENSION_UNINSTALL,
    531                         content::Details<Extension>(details).ptr());
    532       break;
    533     }
    534     case content::NOTIFICATION_RENDERER_PROCESS_HANG: {
    535       std::string url;
    536       content::RenderWidgetHost* widget =
    537           content::Source<content::RenderWidgetHost>(source).ptr();
    538       if (widget->IsRenderView()) {
    539         content::RenderViewHost* view = content::RenderViewHost::From(widget);
    540         MaybeGetURLFromRenderView(view, &url);
    541       }
    542       AddEvent(util::CreateRendererFailureEvent(base::Time::Now(),
    543                                                 EVENT_RENDERER_HANG,
    544                                                 url));
    545       break;
    546     }
    547     case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: {
    548       AddRendererClosedEvent(
    549           content::Source<content::RenderProcessHost>(source).ptr(),
    550           *content::Details<content::RenderProcessHost::RendererClosedDetails>(
    551               details).ptr());
    552       break;
    553     }
    554     case chrome::NOTIFICATION_PROFILE_ADDED: {
    555       Profile* profile = content::Source<Profile>(source).ptr();
    556       if (profile->GetLastSessionExitType() == Profile::EXIT_CRASHED) {
    557         BrowserThread::PostBlockingPoolSequencedTask(
    558             Database::kDatabaseSequenceToken,
    559             FROM_HERE,
    560             base::Bind(
    561                 &PerformanceMonitor::AddUncleanExitEventOnBackgroundThread,
    562                 base::Unretained(this),
    563                 profile->GetDebugName()));
    564       }
    565       break;
    566     }
    567     case content::NOTIFICATION_LOAD_STOP: {
    568       const content::LoadNotificationDetails* load_details =
    569           content::Details<content::LoadNotificationDetails>(details).ptr();
    570       if (!load_details)
    571         break;
    572       BrowserThread::PostBlockingPoolSequencedTask(
    573           Database::kDatabaseSequenceToken,
    574           FROM_HERE,
    575           base::Bind(
    576               &PerformanceMonitor::AddMetricOnBackgroundThread,
    577               base::Unretained(this),
    578               Metric(METRIC_PAGE_LOAD_TIME,
    579                      base::Time::Now(),
    580                      static_cast<double>(
    581                          load_details->load_time.ToInternalValue()))));
    582       break;
    583     }
    584     default: {
    585       NOTREACHED();
    586       break;
    587     }
    588   }
    589 }
    590 
    591 void PerformanceMonitor::AddExtensionEvent(EventType type,
    592                                               const Extension* extension) {
    593   DCHECK(type == EVENT_EXTENSION_INSTALL ||
    594          type == EVENT_EXTENSION_UNINSTALL ||
    595          type == EVENT_EXTENSION_UPDATE ||
    596          type == EVENT_EXTENSION_ENABLE ||
    597          type == EVENT_EXTENSION_DISABLE);
    598   AddEvent(util::CreateExtensionEvent(type,
    599                                       base::Time::Now(),
    600                                       extension->id(),
    601                                       extension->name(),
    602                                       extension->url().spec(),
    603                                       extension->location(),
    604                                       extension->VersionString(),
    605                                       extension->description()));
    606 }
    607 
    608 void PerformanceMonitor::AddRendererClosedEvent(
    609     content::RenderProcessHost* host,
    610     const content::RenderProcessHost::RendererClosedDetails& details) {
    611   // We only care if this is an invalid termination.
    612   if (details.status == base::TERMINATION_STATUS_NORMAL_TERMINATION ||
    613       details.status == base::TERMINATION_STATUS_STILL_RUNNING)
    614     return;
    615 
    616   // Determine the type of crash.
    617   EventType type =
    618       details.status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED ?
    619       EVENT_RENDERER_KILLED : EVENT_RENDERER_CRASH;
    620 
    621   // A RenderProcessHost may contain multiple render views - for each valid
    622   // render view, extract the url, and append it to the string, comma-separating
    623   // the entries.
    624   std::string url_list;
    625   content::RenderWidgetHost::List widgets =
    626       content::RenderWidgetHost::GetRenderWidgetHosts();
    627   for (size_t i = 0; i < widgets.size(); ++i) {
    628     if (widgets[i]->GetProcess()->GetID() != host->GetID())
    629       continue;
    630     if (!widgets[i]->IsRenderView())
    631       continue;
    632 
    633     content::RenderViewHost* view = content::RenderViewHost::From(widgets[i]);
    634     std::string url;
    635     if (!MaybeGetURLFromRenderView(view, &url))
    636       continue;
    637 
    638     if (!url_list.empty())
    639       url_list += ", ";
    640 
    641     url_list += url;
    642   }
    643 
    644   AddEvent(util::CreateRendererFailureEvent(base::Time::Now(), type, url_list));
    645 }
    646 
    647 }  // namespace performance_monitor
    648