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.
5 #include "chrome/browser/performance_monitor/performance_monitor.h"
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"
46 using content::BrowserThread
;
47 using extensions::Extension
;
48 using extensions::UnloadedExtensionInfo
;
50 namespace performance_monitor
{
54 #if !defined(OS_ANDROID)
55 std::string
TimeToString(base::Time time
) {
56 int64 time_int64
= time
.ToInternalValue();
57 return base::Int64ToString(time_int64
);
59 #endif // !defined(OS_ANDROID)
61 bool StringToTime(std::string time
, base::Time
* output
) {
63 if (!base::StringToInt64(time
, &time_int64
))
65 *output
= base::Time::FromInternalValue(time_int64
);
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
,
74 content::WebContents
* web_contents
=
75 content::WebContents::FromRenderViewHost(view
);
77 if (Profile::FromBrowserContext(
78 web_contents
->GetBrowserContext())->IsOffTheRecord()) {
82 *url
= web_contents
->GetURL().spec();
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
) {
94 bool PerformanceMonitor::initialized_
= false;
96 PerformanceMonitor::PerformanceDataForIOThread::PerformanceDataForIOThread()
97 : network_bytes_read(0) {
100 PerformanceMonitor::PerformanceMonitor()
101 : gather_interval_in_seconds_(kDefaultGatherIntervalInSeconds
),
102 database_logging_enabled_(false),
104 base::TimeDelta::FromSeconds(kSampleIntervalInSeconds
),
106 &PerformanceMonitor::DoTimedCollections
),
107 disable_timer_autostart_for_testing_(false) {
110 PerformanceMonitor::~PerformanceMonitor() {
111 BrowserThread::PostBlockingPoolSequencedTask(
112 Database::kDatabaseSequenceToken
,
114 base::Bind(&DeleteDatabaseOnBackgroundThread
, database_
.release()));
117 bool PerformanceMonitor::SetDatabasePath(const base::FilePath
& path
) {
118 if (!database_
.get()) {
119 database_path_
= path
;
123 // PerformanceMonitor already initialized with another path.
128 PerformanceMonitor
* PerformanceMonitor::GetInstance() {
129 return Singleton
<PerformanceMonitor
>::get();
132 void PerformanceMonitor::Initialize() {
133 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
135 if (CommandLine::ForCurrentProcess()->HasSwitch(
136 switches::kPerformanceMonitorGathering
)) {
137 database_logging_enabled_
= true;
139 std::string switch_value
= CommandLine::ForCurrentProcess()->
140 GetSwitchValueASCII(switches::kPerformanceMonitorGathering
);
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.";
150 gather_interval_in_seconds_
= std::max(specified_interval
,
151 kSampleIntervalInSeconds
);
156 DCHECK(gather_interval_in_seconds_
>= kSampleIntervalInSeconds
);
158 next_collection_time_
= base::Time::Now() +
159 base::TimeDelta::FromSeconds(gather_interval_in_seconds_
);
161 util::PostTaskToDatabaseThreadAndReply(
163 base::Bind(&PerformanceMonitor::InitOnBackgroundThread
,
164 base::Unretained(this)),
165 base::Bind(&PerformanceMonitor::FinishInit
,
166 base::Unretained(this)));
169 void PerformanceMonitor::InitOnBackgroundThread() {
170 CHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI
));
172 if (database_logging_enabled_
) {
174 database_
= Database::Create(database_path_
);
177 LOG(ERROR
) << "Could not initialize database; aborting initialization.";
178 database_logging_enabled_
= false;
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.
185 if (database_
->GetRecentStatsForActivityAndMetric(METRIC_NETWORK_BYTES_READ
,
187 performance_data_for_io_thread_
.network_bytes_read
= metric
.value
;
192 void PerformanceMonitor::FinishInit() {
193 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
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
,
202 base::Bind(&PerformanceMonitor::CheckForVersionUpdateOnBackgroundThread
,
203 base::Unretained(this)));
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.
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(
215 base::Bind(&base::DoNothing
),
216 base::Bind(&PerformanceMonitor::NotifyInitialized
,
217 base::Unretained(this)));
220 void PerformanceMonitor::StartGatherCycle() {
221 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
223 // Start our periodic gathering of metrics.
224 if (!disable_timer_autostart_for_testing_
)
228 void PerformanceMonitor::RegisterForNotifications() {
229 DCHECK(database_logging_enabled_
);
232 registrar_
.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED
,
233 content::NotificationService::AllSources());
234 registrar_
.Add(this, chrome::NOTIFICATION_EXTENSION_ENABLED
,
235 content::NotificationService::AllSources());
236 registrar_
.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED
,
237 content::NotificationService::AllSources());
238 registrar_
.Add(this, chrome::NOTIFICATION_CRX_INSTALLER_DONE
,
239 content::NotificationService::AllSources());
240 registrar_
.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED
,
241 content::NotificationService::AllSources());
244 registrar_
.Add(this, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG
,
245 content::NotificationService::AllSources());
246 registrar_
.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED
,
247 content::NotificationService::AllSources());
249 // Profiles (for unclean exit)
250 registrar_
.Add(this, chrome::NOTIFICATION_PROFILE_ADDED
,
251 content::NotificationService::AllSources());
254 registrar_
.Add(this, content::NOTIFICATION_LOAD_STOP
,
255 content::NotificationService::AllSources());
258 // We check if profiles exited cleanly initialization time in case they were
259 // loaded prior to PerformanceMonitor's initialization. Later profiles will be
260 // checked through the PROFILE_ADDED notification.
261 void PerformanceMonitor::CheckForUncleanExits() {
262 DCHECK(database_logging_enabled_
);
264 std::vector
<Profile
*> profiles
=
265 g_browser_process
->profile_manager()->GetLoadedProfiles();
267 for (std::vector
<Profile
*>::const_iterator iter
= profiles
.begin();
268 iter
!= profiles
.end(); ++iter
) {
269 if ((*iter
)->GetLastSessionExitType() == Profile::EXIT_CRASHED
) {
270 BrowserThread::PostBlockingPoolSequencedTask(
271 Database::kDatabaseSequenceToken
,
273 base::Bind(&PerformanceMonitor::AddUncleanExitEventOnBackgroundThread
,
274 base::Unretained(this),
275 (*iter
)->GetDebugName()));
280 void PerformanceMonitor::AddUncleanExitEventOnBackgroundThread(
281 const std::string
& profile_name
) {
282 DCHECK(database_logging_enabled_
);
283 std::string database_key
= kStateProfilePrefix
+ profile_name
;
284 std::string last_active_string
= database_
->GetStateValue(database_key
);
286 // Check if there was no previous time; this should only happen if the profile
287 // was last used prior to PerformanceMonitor's integration. Do nothing in this
288 // case, since the event was prior to the beginning of our recording.
289 if (last_active_string
.empty())
292 base::Time last_active_time
;
293 CHECK(StringToTime(last_active_string
, &last_active_time
));
295 scoped_ptr
<Event
> event
=
296 util::CreateUncleanExitEvent(last_active_time
, profile_name
);
298 database_
->AddEvent(*event
.get());
301 void PerformanceMonitor::CheckForVersionUpdateOnBackgroundThread() {
302 CHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI
));
303 DCHECK(database_logging_enabled_
);
305 chrome::VersionInfo version
;
306 DCHECK(version
.is_valid());
307 std::string current_version
= version
.Version();
309 std::string previous_version
= database_
->GetStateValue(kStateChromeVersion
);
311 // We should never have a current_version which is older than the
313 DCHECK(current_version
>= previous_version
);
315 // If this is the first run, there will not be a stored value for Chrome
316 // version; we insert the current version and will insert an event for the
317 // next update of Chrome. If the previous version is older than the current
318 // version, update the state in the database and insert an event.
319 if (current_version
> previous_version
) {
320 database_
->AddStateValue(kStateChromeVersion
, current_version
);
321 if (!previous_version
.empty()) {
322 scoped_ptr
<Event
> event
= util::CreateChromeUpdateEvent(
323 base::Time::Now(), previous_version
, current_version
);
324 database_
->AddEvent(*event
.get());
329 void PerformanceMonitor::AddEvent(scoped_ptr
<Event
> event
) {
330 DCHECK(database_logging_enabled_
);
332 BrowserThread::PostBlockingPoolSequencedTask(
333 Database::kDatabaseSequenceToken
,
335 base::Bind(&PerformanceMonitor::AddEventOnBackgroundThread
,
336 base::Unretained(this),
337 base::Passed(&event
)));
340 void PerformanceMonitor::AddEventOnBackgroundThread(scoped_ptr
<Event
> event
) {
341 database_
->AddEvent(*event
.get());
344 void PerformanceMonitor::AddMetricOnBackgroundThread(const Metric
& metric
) {
345 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI
));
346 DCHECK(database_logging_enabled_
);
348 database_
->AddMetric(metric
);
351 void PerformanceMonitor::NotifyInitialized() {
352 content::NotificationService::current()->Notify(
353 chrome::NOTIFICATION_PERFORMANCE_MONITOR_INITIALIZED
,
354 content::Source
<PerformanceMonitor
>(this),
355 content::NotificationService::NoDetails());
360 void PerformanceMonitor::DoTimedCollections() {
361 #if !defined(OS_ANDROID)
362 // The profile list is only useful for the logged events.
363 if (database_logging_enabled_
)
364 UpdateLiveProfiles();
367 GatherMetricsMapOnUIThread();
370 void PerformanceMonitor::GatherMetricsMapOnUIThread() {
371 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
372 static int current_update_sequence
= 0;
373 // Even in the "somewhat" unlikely event this wraps around,
374 // it doesn't matter. We just check it for inequality.
375 current_update_sequence
++;
377 // Find all render child processes; has to be done on the UI thread.
378 for (content::RenderProcessHost::iterator rph_iter
=
379 content::RenderProcessHost::AllHostsIterator();
380 !rph_iter
.IsAtEnd(); rph_iter
.Advance()) {
381 base::ProcessHandle handle
= rph_iter
.GetCurrentValue()->GetHandle();
382 MarkProcessAsAlive(handle
, content::PROCESS_TYPE_RENDERER
,
383 current_update_sequence
);
386 BrowserThread::PostTask(
389 base::Bind(&PerformanceMonitor::GatherMetricsMapOnIOThread
,
390 base::Unretained(this),
391 current_update_sequence
));
394 void PerformanceMonitor::MarkProcessAsAlive(const base::ProcessHandle
& handle
,
396 int current_update_sequence
) {
399 // Process may not be valid yet.
403 MetricsMap::iterator process_metrics_iter
= metrics_map_
.find(handle
);
404 if (process_metrics_iter
== metrics_map_
.end()) {
405 // If we're not already watching the process, let's initialize it.
407 .Initialize(handle
, process_type
, current_update_sequence
);
409 // If we are watching the process, touch it to keep it alive.
410 ProcessMetricsHistory
& process_metrics
= process_metrics_iter
->second
;
411 process_metrics
.set_last_update_sequence(current_update_sequence
);
415 #if !defined(OS_ANDROID)
416 void PerformanceMonitor::UpdateLiveProfiles() {
417 std::string time
= TimeToString(base::Time::Now());
418 scoped_ptr
<std::set
<std::string
> > active_profiles(
419 new std::set
<std::string
>());
421 for (chrome::BrowserIterator it
; !it
.done(); it
.Next())
422 active_profiles
->insert(it
->profile()->GetDebugName());
424 BrowserThread::PostBlockingPoolSequencedTask(
425 Database::kDatabaseSequenceToken
,
427 base::Bind(&PerformanceMonitor::UpdateLiveProfilesHelper
,
428 base::Unretained(this),
429 base::Passed(&active_profiles
),
433 void PerformanceMonitor::UpdateLiveProfilesHelper(
434 scoped_ptr
<std::set
<std::string
> > active_profiles
,
436 CHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI
));
437 DCHECK(database_logging_enabled_
);
439 for (std::set
<std::string
>::const_iterator iter
= active_profiles
->begin();
440 iter
!= active_profiles
->end(); ++iter
) {
441 database_
->AddStateValue(kStateProfilePrefix
+ *iter
, time
);
446 void PerformanceMonitor::GatherMetricsMapOnIOThread(
447 int current_update_sequence
) {
448 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
450 // Find all child processes (does not include renderers), which has to be
451 // done on the IO thread.
452 for (content::BrowserChildProcessHostIterator iter
; !iter
.Done(); ++iter
) {
453 const content::ChildProcessData
& child_process_data
= iter
.GetData();
454 base::ProcessHandle handle
= child_process_data
.handle
;
455 MarkProcessAsAlive(handle
, child_process_data
.process_type
,
456 current_update_sequence
);
459 // Add the current (browser) process.
460 MarkProcessAsAlive(base::GetCurrentProcessHandle(),
461 content::PROCESS_TYPE_BROWSER
, current_update_sequence
);
463 BrowserThread::PostBlockingPoolSequencedTask(
464 Database::kDatabaseSequenceToken
,
466 base::Bind(&PerformanceMonitor::StoreMetricsOnBackgroundThread
,
467 base::Unretained(this), current_update_sequence
,
468 performance_data_for_io_thread_
));
471 void PerformanceMonitor::StoreMetricsOnBackgroundThread(
472 int current_update_sequence
,
473 const PerformanceDataForIOThread
& performance_data_for_io_thread
) {
474 CHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI
));
476 base::Time time_now
= base::Time::Now();
478 // The timing can be off by kSampleIntervalInSeconds during any one particular
479 // run, but will be correct over time.
480 bool end_of_cycle
= time_now
>= next_collection_time_
;
482 next_collection_time_
+=
483 base::TimeDelta::FromSeconds(gather_interval_in_seconds_
);
486 double cpu_usage
= 0.0;
487 size_t private_memory_sum
= 0;
488 size_t shared_memory_sum
= 0;
490 // Update metrics for all watched processes; remove dead entries from the map.
491 MetricsMap::iterator iter
= metrics_map_
.begin();
492 while (iter
!= metrics_map_
.end()) {
493 ProcessMetricsHistory
& process_metrics
= iter
->second
;
494 if (process_metrics
.last_update_sequence() != current_update_sequence
) {
495 // Not touched this iteration; let's get rid of it.
496 metrics_map_
.erase(iter
++);
498 process_metrics
.SampleMetrics();
501 // Gather averages of previously sampled metrics.
502 cpu_usage
+= process_metrics
.GetAverageCPUUsage();
504 size_t private_memory
= 0;
505 size_t shared_memory
= 0;
506 process_metrics
.GetAverageMemoryBytes(&private_memory
, &shared_memory
);
507 private_memory_sum
+= private_memory
;
508 shared_memory_sum
+= shared_memory
;
510 process_metrics
.EndOfCycle();
517 // Store previously-sampled metrics.
518 if (end_of_cycle
&& database_logging_enabled_
) {
519 if (!metrics_map_
.empty()) {
520 database_
->AddMetric(Metric(METRIC_CPU_USAGE
, time_now
, cpu_usage
));
521 database_
->AddMetric(Metric(METRIC_PRIVATE_MEMORY_USAGE
,
523 static_cast<double>(private_memory_sum
)));
524 database_
->AddMetric(Metric(METRIC_SHARED_MEMORY_USAGE
,
526 static_cast<double>(shared_memory_sum
)));
529 database_
->AddMetric(
530 Metric(METRIC_NETWORK_BYTES_READ
,
533 performance_data_for_io_thread
.network_bytes_read
)));
536 BrowserThread::PostTask(
539 base::Bind(&PerformanceMonitor::StartGatherCycle
,
540 base::Unretained(this)));
543 void PerformanceMonitor::BytesReadOnIOThread(const net::URLRequest
& request
,
544 const int bytes_read
) {
545 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
547 if (initialized_
&& !request
.url().SchemeIsFile())
548 performance_data_for_io_thread_
.network_bytes_read
+= bytes_read
;
551 void PerformanceMonitor::Observe(int type
,
552 const content::NotificationSource
& source
,
553 const content::NotificationDetails
& details
) {
554 DCHECK(database_logging_enabled_
);
557 case chrome::NOTIFICATION_EXTENSION_INSTALLED
: {
559 EVENT_EXTENSION_INSTALL
,
560 content::Details
<const extensions::InstalledExtensionInfo
>(details
)->
564 case chrome::NOTIFICATION_EXTENSION_ENABLED
: {
565 AddExtensionEvent(EVENT_EXTENSION_ENABLE
,
566 content::Details
<Extension
>(details
).ptr());
569 case chrome::NOTIFICATION_EXTENSION_UNLOADED
: {
570 const UnloadedExtensionInfo
* info
=
571 content::Details
<UnloadedExtensionInfo
>(details
).ptr();
573 // Check if the extension was unloaded because it was disabled.
574 if (info
->reason
== UnloadedExtensionInfo::REASON_DISABLE
) {
575 AddExtensionEvent(EVENT_EXTENSION_DISABLE
,
580 case chrome::NOTIFICATION_CRX_INSTALLER_DONE
: {
581 const extensions::CrxInstaller
* installer
=
582 content::Source
<extensions::CrxInstaller
>(source
).ptr();
583 const extensions::Extension
* extension
=
584 content::Details
<Extension
>(details
).ptr();
586 // Check if the reason for the install was due to a successful
587 // extension update. |extension| is NULL in case of install failure.
589 installer
->install_cause() == extension_misc::INSTALL_CAUSE_UPDATE
) {
590 AddExtensionEvent(EVENT_EXTENSION_UPDATE
, extension
);
594 case chrome::NOTIFICATION_EXTENSION_UNINSTALLED
: {
595 AddExtensionEvent(EVENT_EXTENSION_UNINSTALL
,
596 content::Details
<Extension
>(details
).ptr());
599 case content::NOTIFICATION_RENDER_WIDGET_HOST_HANG
: {
601 content::RenderWidgetHost
* widget
=
602 content::Source
<content::RenderWidgetHost
>(source
).ptr();
603 if (widget
->IsRenderView()) {
604 content::RenderViewHost
* view
= content::RenderViewHost::From(widget
);
605 MaybeGetURLFromRenderView(view
, &url
);
607 AddEvent(util::CreateRendererFailureEvent(base::Time::Now(),
612 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED
: {
613 AddRendererClosedEvent(
614 content::Source
<content::RenderProcessHost
>(source
).ptr(),
615 *content::Details
<content::RenderProcessHost::RendererClosedDetails
>(
619 case chrome::NOTIFICATION_PROFILE_ADDED
: {
620 Profile
* profile
= content::Source
<Profile
>(source
).ptr();
621 if (profile
->GetLastSessionExitType() == Profile::EXIT_CRASHED
) {
622 BrowserThread::PostBlockingPoolSequencedTask(
623 Database::kDatabaseSequenceToken
,
626 &PerformanceMonitor::AddUncleanExitEventOnBackgroundThread
,
627 base::Unretained(this),
628 profile
->GetDebugName()));
632 case content::NOTIFICATION_LOAD_STOP
: {
633 const content::LoadNotificationDetails
* load_details
=
634 content::Details
<content::LoadNotificationDetails
>(details
).ptr();
637 BrowserThread::PostBlockingPoolSequencedTask(
638 Database::kDatabaseSequenceToken
,
641 &PerformanceMonitor::AddMetricOnBackgroundThread
,
642 base::Unretained(this),
643 Metric(METRIC_PAGE_LOAD_TIME
,
646 load_details
->load_time
.ToInternalValue()))));
656 void PerformanceMonitor::AddExtensionEvent(EventType type
,
657 const Extension
* extension
) {
658 DCHECK(type
== EVENT_EXTENSION_INSTALL
||
659 type
== EVENT_EXTENSION_UNINSTALL
||
660 type
== EVENT_EXTENSION_UPDATE
||
661 type
== EVENT_EXTENSION_ENABLE
||
662 type
== EVENT_EXTENSION_DISABLE
);
663 AddEvent(util::CreateExtensionEvent(type
,
667 extension
->url().spec(),
668 extension
->location(),
669 extension
->VersionString(),
670 extension
->description()));
673 void PerformanceMonitor::AddRendererClosedEvent(
674 content::RenderProcessHost
* host
,
675 const content::RenderProcessHost::RendererClosedDetails
& details
) {
676 // We only care if this is an invalid termination.
677 if (details
.status
== base::TERMINATION_STATUS_NORMAL_TERMINATION
||
678 details
.status
== base::TERMINATION_STATUS_STILL_RUNNING
)
681 // Determine the type of crash.
683 details
.status
== base::TERMINATION_STATUS_PROCESS_WAS_KILLED
?
684 EVENT_RENDERER_KILLED
: EVENT_RENDERER_CRASH
;
686 // A RenderProcessHost may contain multiple render views - for each valid
687 // render view, extract the url, and append it to the string, comma-separating
689 std::string url_list
;
690 scoped_ptr
<content::RenderWidgetHostIterator
> widgets(
691 content::RenderWidgetHost::GetRenderWidgetHosts());
692 while (content::RenderWidgetHost
* widget
= widgets
->GetNextHost()) {
693 if (widget
->GetProcess()->GetID() != host
->GetID())
695 if (!widget
->IsRenderView())
698 content::RenderViewHost
* view
= content::RenderViewHost::From(widget
);
700 if (!MaybeGetURLFromRenderView(view
, &url
))
703 if (!url_list
.empty())
709 AddEvent(util::CreateRendererFailureEvent(base::Time::Now(), type
, url_list
));
712 } // namespace performance_monitor