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