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
46using content::BrowserThread;
47using extensions::Extension;
48using extensions::UnloadedExtensionInfo;
49
50namespace performance_monitor {
51
52namespace {
53
54#if !defined(OS_ANDROID)
55std::string TimeToString(base::Time time) {
56  int64 time_int64 = time.ToInternalValue();
57  return base::Int64ToString(time_int64);
58}
59#endif  // !defined(OS_ANDROID)
60
61bool 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.
72bool 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.
88void DeleteDatabaseOnBackgroundThread(Database* database) {
89  delete database;
90}
91
92}  // namespace
93
94bool PerformanceMonitor::initialized_ = false;
95
96PerformanceMonitor::PerformanceDataForIOThread::PerformanceDataForIOThread()
97    : network_bytes_read(0) {
98}
99
100PerformanceMonitor::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
110PerformanceMonitor::~PerformanceMonitor() {
111  BrowserThread::PostBlockingPoolSequencedTask(
112      Database::kDatabaseSequenceToken,
113      FROM_HERE,
114      base::Bind(&DeleteDatabaseOnBackgroundThread, database_.release()));
115}
116
117bool 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
128PerformanceMonitor* PerformanceMonitor::GetInstance() {
129  return Singleton<PerformanceMonitor>::get();
130}
131
132void 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
169void 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
192void 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
220void 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
228void 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.
263void 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
282void 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
303void 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
331void 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
342void PerformanceMonitor::AddEventOnBackgroundThread(scoped_ptr<Event> event) {
343  database_->AddEvent(*event.get());
344}
345
346void PerformanceMonitor::AddMetricOnBackgroundThread(const Metric& metric) {
347  DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
348  DCHECK(database_logging_enabled_);
349
350  database_->AddMetric(metric);
351}
352
353void 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
362void 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
372void 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
396void 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)
418void 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
435void 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
448void 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
473void 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
545void 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
553void 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
658void 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
675void 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