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