Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/performance_monitor/performance_monitor.h" | 5 #include "chrome/browser/performance_monitor/performance_monitor.h" |
| 6 | 6 |
| 7 #include <set> | 7 #include <set> |
| 8 #include <vector> | 8 #include <vector> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
|
Devlin
2014/09/09 02:12:57
Do we really need all these includes still?
tonyg
2014/09/09 18:28:10
Good catch, I removed most of them.
| |
| 11 #include "base/command_line.h" | 11 #include "base/command_line.h" |
| 12 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "base/memory/singleton.h" | 13 #include "base/memory/singleton.h" |
| 14 #include "base/process/process_iterator.h" | 14 #include "base/process/process_iterator.h" |
| 15 #include "base/stl_util.h" | 15 #include "base/stl_util.h" |
| 16 #include "base/strings/string_number_conversions.h" | 16 #include "base/strings/string_number_conversions.h" |
| 17 #include "base/threading/worker_pool.h" | 17 #include "base/threading/worker_pool.h" |
| 18 #include "base/time/time.h" | 18 #include "base/time/time.h" |
| 19 #include "chrome/browser/browser_process.h" | 19 #include "chrome/browser/browser_process.h" |
| 20 #include "chrome/browser/browser_shutdown.h" | 20 #include "chrome/browser/browser_shutdown.h" |
| 21 #include "chrome/browser/chrome_notification_types.h" | 21 #include "chrome/browser/chrome_notification_types.h" |
| 22 #include "chrome/browser/performance_monitor/constants.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" | 23 #include "chrome/browser/profiles/profile.h" |
| 25 #include "chrome/browser/profiles/profile_manager.h" | 24 #include "chrome/browser/profiles/profile_manager.h" |
| 26 #include "chrome/browser/ui/browser.h" | 25 #include "chrome/browser/ui/browser.h" |
| 27 #include "chrome/browser/ui/browser_iterator.h" | 26 #include "chrome/browser/ui/browser_iterator.h" |
| 28 #include "chrome/common/chrome_switches.h" | 27 #include "chrome/common/chrome_switches.h" |
| 29 #include "chrome/common/chrome_version_info.h" | 28 #include "chrome/common/chrome_version_info.h" |
| 30 #include "content/public/browser/browser_child_process_host.h" | 29 #include "content/public/browser/browser_child_process_host.h" |
| 31 #include "content/public/browser/browser_child_process_host_iterator.h" | 30 #include "content/public/browser/browser_child_process_host_iterator.h" |
| 32 #include "content/public/browser/browser_thread.h" | 31 #include "content/public/browser/browser_thread.h" |
| 33 #include "content/public/browser/child_process_data.h" | 32 #include "content/public/browser/child_process_data.h" |
| 34 #include "content/public/browser/load_notification_details.h" | 33 #include "content/public/browser/load_notification_details.h" |
| 35 #include "content/public/browser/notification_service.h" | 34 #include "content/public/browser/notification_service.h" |
| 36 #include "content/public/browser/notification_types.h" | 35 #include "content/public/browser/notification_types.h" |
| 36 #include "content/public/browser/render_process_host.h" | |
| 37 #include "content/public/browser/render_view_host.h" | 37 #include "content/public/browser/render_view_host.h" |
| 38 #include "content/public/browser/render_widget_host.h" | 38 #include "content/public/browser/render_widget_host.h" |
| 39 #include "content/public/browser/render_widget_host_iterator.h" | 39 #include "content/public/browser/render_widget_host_iterator.h" |
| 40 #include "content/public/browser/web_contents.h" | 40 #include "content/public/browser/web_contents.h" |
| 41 #include "net/url_request/url_request.h" | 41 #include "net/url_request/url_request.h" |
| 42 | 42 |
| 43 #if defined(ENABLE_EXTENSIONS) | |
| 44 #include "chrome/browser/extensions/crx_installer.h" | |
| 45 #include "chrome/common/extensions/extension_constants.h" | |
| 46 #include "extensions/common/extension.h" | |
| 47 #endif | |
| 48 | |
| 49 using content::BrowserThread; | 43 using content::BrowserThread; |
| 50 | 44 |
| 51 #if defined(ENABLE_EXTENSIONS) | |
| 52 using extensions::Extension; | |
| 53 using extensions::UnloadedExtensionInfo; | |
| 54 #endif | |
| 55 | |
| 56 namespace performance_monitor { | 45 namespace performance_monitor { |
| 57 | 46 |
| 58 namespace { | 47 PerformanceMonitor::PerformanceMonitor() { |
| 59 | |
| 60 #if !defined(OS_ANDROID) | |
| 61 std::string TimeToString(base::Time time) { | |
| 62 int64 time_int64 = time.ToInternalValue(); | |
| 63 return base::Int64ToString(time_int64); | |
| 64 } | |
| 65 #endif // !defined(OS_ANDROID) | |
| 66 | |
| 67 bool StringToTime(std::string time, base::Time* output) { | |
| 68 int64 time_int64 = 0; | |
| 69 if (!base::StringToInt64(time, &time_int64)) | |
| 70 return false; | |
| 71 *output = base::Time::FromInternalValue(time_int64); | |
| 72 return true; | |
| 73 } | |
| 74 | |
| 75 // Try to get the URL for the RenderViewHost if the host does not correspond to | |
| 76 // an incognito profile (we don't store URLs from incognito sessions). Returns | |
| 77 // true if url has been populated, and false otherwise. | |
| 78 bool MaybeGetURLFromRenderView(const content::RenderViewHost* view, | |
| 79 std::string* url) { | |
| 80 content::WebContents* web_contents = | |
| 81 content::WebContents::FromRenderViewHost(view); | |
| 82 | |
| 83 if (Profile::FromBrowserContext( | |
| 84 web_contents->GetBrowserContext())->IsOffTheRecord()) { | |
| 85 return false; | |
| 86 } | |
| 87 | |
| 88 *url = web_contents->GetURL().spec(); | |
| 89 return true; | |
| 90 } | |
| 91 | |
| 92 // Takes ownership of and deletes |database| on the background thread, to | |
| 93 // avoid destruction in the middle of an operation. | |
| 94 void DeleteDatabaseOnBackgroundThread(Database* database) { | |
| 95 delete database; | |
| 96 } | |
| 97 | |
| 98 } // namespace | |
| 99 | |
| 100 bool PerformanceMonitor::initialized_ = false; | |
| 101 | |
| 102 PerformanceMonitor::PerformanceDataForIOThread::PerformanceDataForIOThread() | |
| 103 : network_bytes_read(0) { | |
| 104 } | |
| 105 | |
| 106 PerformanceMonitor::PerformanceMonitor() | |
| 107 : gather_interval_in_seconds_(kDefaultGatherIntervalInSeconds), | |
| 108 database_logging_enabled_(false), | |
| 109 timer_(FROM_HERE, | |
| 110 base::TimeDelta::FromSeconds(kSampleIntervalInSeconds), | |
| 111 this, | |
| 112 &PerformanceMonitor::DoTimedCollections), | |
| 113 disable_timer_autostart_for_testing_(false) { | |
| 114 } | 48 } |
| 115 | 49 |
| 116 PerformanceMonitor::~PerformanceMonitor() { | 50 PerformanceMonitor::~PerformanceMonitor() { |
| 117 BrowserThread::PostBlockingPoolSequencedTask( | |
| 118 Database::kDatabaseSequenceToken, | |
| 119 FROM_HERE, | |
| 120 base::Bind(&DeleteDatabaseOnBackgroundThread, database_.release())); | |
| 121 } | |
| 122 | |
| 123 bool PerformanceMonitor::SetDatabasePath(const base::FilePath& path) { | |
| 124 if (!database_.get()) { | |
| 125 database_path_ = path; | |
| 126 return true; | |
| 127 } | |
| 128 | |
| 129 // PerformanceMonitor already initialized with another path. | |
| 130 return false; | |
| 131 } | 51 } |
| 132 | 52 |
| 133 // static | 53 // static |
| 134 PerformanceMonitor* PerformanceMonitor::GetInstance() { | 54 PerformanceMonitor* PerformanceMonitor::GetInstance() { |
| 135 return Singleton<PerformanceMonitor>::get(); | 55 return Singleton<PerformanceMonitor>::get(); |
| 136 } | 56 } |
| 137 | 57 |
| 138 void PerformanceMonitor::Initialize() { | |
| 139 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 140 | |
| 141 if (CommandLine::ForCurrentProcess()->HasSwitch( | |
| 142 switches::kPerformanceMonitorGathering)) { | |
| 143 database_logging_enabled_ = true; | |
| 144 | |
| 145 std::string switch_value = CommandLine::ForCurrentProcess()-> | |
| 146 GetSwitchValueASCII(switches::kPerformanceMonitorGathering); | |
| 147 | |
| 148 if (!switch_value.empty()) { | |
| 149 int specified_interval = 0; | |
| 150 if (!base::StringToInt(switch_value, &specified_interval) || | |
| 151 specified_interval <= 0) { | |
| 152 LOG(ERROR) << "Invalid value for switch: '" | |
| 153 << switches::kPerformanceMonitorGathering | |
| 154 << "'; please use an integer greater than 0."; | |
| 155 } else { | |
| 156 gather_interval_in_seconds_ = std::max(specified_interval, | |
| 157 kSampleIntervalInSeconds); | |
| 158 } | |
| 159 } | |
| 160 } | |
| 161 | |
| 162 DCHECK(gather_interval_in_seconds_ >= kSampleIntervalInSeconds); | |
| 163 | |
| 164 next_collection_time_ = base::Time::Now() + | |
| 165 base::TimeDelta::FromSeconds(gather_interval_in_seconds_); | |
| 166 | |
| 167 util::PostTaskToDatabaseThreadAndReply( | |
| 168 FROM_HERE, | |
| 169 base::Bind(&PerformanceMonitor::InitOnBackgroundThread, | |
| 170 base::Unretained(this)), | |
| 171 base::Bind(&PerformanceMonitor::FinishInit, | |
| 172 base::Unretained(this))); | |
| 173 } | |
| 174 | |
| 175 void PerformanceMonitor::InitOnBackgroundThread() { | |
| 176 CHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 177 | |
| 178 if (database_logging_enabled_) { | |
| 179 if (!database_) | |
| 180 database_ = Database::Create(database_path_); | |
| 181 | |
| 182 if (!database_) { | |
| 183 LOG(ERROR) << "Could not initialize database; aborting initialization."; | |
| 184 database_logging_enabled_ = false; | |
| 185 return; | |
| 186 } | |
| 187 | |
| 188 // Initialize the io thread's performance data to the value in the database; | |
| 189 // if there isn't a recording in the database, the value stays at 0. | |
| 190 Metric metric; | |
| 191 if (database_->GetRecentStatsForActivityAndMetric(METRIC_NETWORK_BYTES_READ, | |
| 192 &metric)) { | |
| 193 performance_data_for_io_thread_.network_bytes_read = metric.value; | |
| 194 } | |
| 195 } | |
| 196 } | |
| 197 | |
| 198 void PerformanceMonitor::FinishInit() { | |
| 199 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 200 | |
| 201 // Events and notifications are only useful if we're logging to the database. | |
| 202 if (database_logging_enabled_) { | |
| 203 RegisterForNotifications(); | |
| 204 CheckForUncleanExits(); | |
| 205 BrowserThread::PostBlockingPoolSequencedTask( | |
| 206 Database::kDatabaseSequenceToken, | |
| 207 FROM_HERE, | |
| 208 base::Bind(&PerformanceMonitor::CheckForVersionUpdateOnBackgroundThread, | |
| 209 base::Unretained(this))); | |
| 210 } | |
| 211 | |
| 212 // Post a task to the background thread to a function which does nothing. | |
| 213 // This will force any tasks the database is performing to finish prior to | |
| 214 // the reply being sent, since they use the same thread. | |
| 215 // | |
| 216 // Important! Make sure that methods in FinishInit() only rely on posting | |
| 217 // to the background thread, and do not rely upon a reply from the background | |
| 218 // thread; this is necessary for this notification to be valid. | |
| 219 util::PostTaskToDatabaseThreadAndReply( | |
| 220 FROM_HERE, | |
| 221 base::Bind(&base::DoNothing), | |
| 222 base::Bind(&PerformanceMonitor::NotifyInitialized, | |
| 223 base::Unretained(this))); | |
| 224 } | |
| 225 | |
| 226 void PerformanceMonitor::StartGatherCycle() { | 58 void PerformanceMonitor::StartGatherCycle() { |
| 227 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 59 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 228 | 60 repeating_timer_.Start(FROM_HERE, |
| 229 // Start our periodic gathering of metrics. | 61 base::TimeDelta::FromSeconds(kGatherIntervalInSeconds), |
|
Devlin
2014/09/09 02:12:57
We can probably safely move this constant into thi
tonyg
2014/09/09 18:28:10
Done.
| |
| 230 if (!disable_timer_autostart_for_testing_) | 62 this, |
| 231 timer_.Reset(); | 63 &PerformanceMonitor::GatherMetricsMapOnUIThread); |
| 232 } | |
| 233 | |
| 234 void PerformanceMonitor::RegisterForNotifications() { | |
| 235 DCHECK(database_logging_enabled_); | |
| 236 | |
| 237 // Extensions | |
| 238 registrar_.Add( | |
| 239 this, | |
| 240 extensions::NOTIFICATION_EXTENSION_WILL_BE_INSTALLED_DEPRECATED, | |
| 241 content::NotificationService::AllSources()); | |
| 242 registrar_.Add(this, | |
| 243 extensions::NOTIFICATION_EXTENSION_ENABLED, | |
| 244 content::NotificationService::AllSources()); | |
| 245 registrar_.Add(this, | |
| 246 extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED, | |
| 247 content::NotificationService::AllSources()); | |
| 248 registrar_.Add(this, | |
| 249 extensions::NOTIFICATION_CRX_INSTALLER_DONE, | |
| 250 content::NotificationService::AllSources()); | |
| 251 registrar_.Add(this, | |
| 252 extensions::NOTIFICATION_EXTENSION_UNINSTALLED_DEPRECATED, | |
| 253 content::NotificationService::AllSources()); | |
| 254 | |
| 255 // Crashes | |
| 256 registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG, | |
| 257 content::NotificationService::AllSources()); | |
| 258 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, | |
| 259 content::NotificationService::AllSources()); | |
| 260 | |
| 261 // Profiles (for unclean exit) | |
| 262 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_ADDED, | |
| 263 content::NotificationService::AllSources()); | |
| 264 | |
| 265 // Page load times | |
| 266 registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, | |
| 267 content::NotificationService::AllSources()); | |
| 268 } | |
| 269 | |
| 270 // We check if profiles exited cleanly initialization time in case they were | |
| 271 // loaded prior to PerformanceMonitor's initialization. Later profiles will be | |
| 272 // checked through the PROFILE_ADDED notification. | |
| 273 void PerformanceMonitor::CheckForUncleanExits() { | |
| 274 DCHECK(database_logging_enabled_); | |
| 275 | |
| 276 std::vector<Profile*> profiles = | |
| 277 g_browser_process->profile_manager()->GetLoadedProfiles(); | |
| 278 | |
| 279 for (std::vector<Profile*>::const_iterator iter = profiles.begin(); | |
| 280 iter != profiles.end(); ++iter) { | |
| 281 if ((*iter)->GetLastSessionExitType() == Profile::EXIT_CRASHED) { | |
| 282 BrowserThread::PostBlockingPoolSequencedTask( | |
| 283 Database::kDatabaseSequenceToken, | |
| 284 FROM_HERE, | |
| 285 base::Bind(&PerformanceMonitor::AddUncleanExitEventOnBackgroundThread, | |
| 286 base::Unretained(this), | |
| 287 (*iter)->GetDebugName())); | |
| 288 } | |
| 289 } | |
| 290 } | |
| 291 | |
| 292 void PerformanceMonitor::AddUncleanExitEventOnBackgroundThread( | |
| 293 const std::string& profile_name) { | |
| 294 DCHECK(database_logging_enabled_); | |
| 295 std::string database_key = kStateProfilePrefix + profile_name; | |
| 296 std::string last_active_string = database_->GetStateValue(database_key); | |
| 297 | |
| 298 // Check if there was no previous time; this should only happen if the profile | |
| 299 // was last used prior to PerformanceMonitor's integration. Do nothing in this | |
| 300 // case, since the event was prior to the beginning of our recording. | |
| 301 if (last_active_string.empty()) | |
| 302 return; | |
| 303 | |
| 304 base::Time last_active_time; | |
| 305 CHECK(StringToTime(last_active_string, &last_active_time)); | |
| 306 | |
| 307 scoped_ptr<Event> event = | |
| 308 util::CreateUncleanExitEvent(last_active_time, profile_name); | |
| 309 | |
| 310 database_->AddEvent(*event.get()); | |
| 311 } | |
| 312 | |
| 313 void PerformanceMonitor::CheckForVersionUpdateOnBackgroundThread() { | |
| 314 CHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 315 DCHECK(database_logging_enabled_); | |
| 316 | |
| 317 chrome::VersionInfo version; | |
| 318 DCHECK(version.is_valid()); | |
| 319 std::string current_version = version.Version(); | |
| 320 | |
| 321 std::string previous_version = database_->GetStateValue(kStateChromeVersion); | |
| 322 | |
| 323 // We should never have a current_version which is older than the | |
| 324 // previous_version. | |
| 325 DCHECK(current_version >= previous_version); | |
| 326 | |
| 327 // If this is the first run, there will not be a stored value for Chrome | |
| 328 // version; we insert the current version and will insert an event for the | |
| 329 // next update of Chrome. If the previous version is older than the current | |
| 330 // version, update the state in the database and insert an event. | |
| 331 if (current_version > previous_version) { | |
| 332 database_->AddStateValue(kStateChromeVersion, current_version); | |
| 333 if (!previous_version.empty()) { | |
| 334 scoped_ptr<Event> event = util::CreateChromeUpdateEvent( | |
| 335 base::Time::Now(), previous_version, current_version); | |
| 336 database_->AddEvent(*event.get()); | |
| 337 } | |
| 338 } | |
| 339 } | |
| 340 | |
| 341 void PerformanceMonitor::AddEvent(scoped_ptr<Event> event) { | |
| 342 DCHECK(database_logging_enabled_); | |
| 343 | |
| 344 BrowserThread::PostBlockingPoolSequencedTask( | |
| 345 Database::kDatabaseSequenceToken, | |
| 346 FROM_HERE, | |
| 347 base::Bind(&PerformanceMonitor::AddEventOnBackgroundThread, | |
| 348 base::Unretained(this), | |
| 349 base::Passed(&event))); | |
| 350 } | |
| 351 | |
| 352 void PerformanceMonitor::AddEventOnBackgroundThread(scoped_ptr<Event> event) { | |
| 353 database_->AddEvent(*event.get()); | |
| 354 } | |
| 355 | |
| 356 void PerformanceMonitor::AddMetricOnBackgroundThread(const Metric& metric) { | |
| 357 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 358 DCHECK(database_logging_enabled_); | |
| 359 | |
| 360 database_->AddMetric(metric); | |
| 361 } | |
| 362 | |
| 363 void PerformanceMonitor::NotifyInitialized() { | |
| 364 content::NotificationService::current()->Notify( | |
| 365 chrome::NOTIFICATION_PERFORMANCE_MONITOR_INITIALIZED, | |
| 366 content::Source<PerformanceMonitor>(this), | |
| 367 content::NotificationService::NoDetails()); | |
| 368 | |
| 369 initialized_ = true; | |
| 370 } | |
| 371 | |
| 372 void PerformanceMonitor::DoTimedCollections() { | |
| 373 #if !defined(OS_ANDROID) | |
| 374 // The profile list is only useful for the logged events. | |
| 375 if (database_logging_enabled_) | |
| 376 UpdateLiveProfiles(); | |
| 377 #endif | |
| 378 | |
| 379 GatherMetricsMapOnUIThread(); | |
| 380 } | 64 } |
| 381 | 65 |
| 382 void PerformanceMonitor::GatherMetricsMapOnUIThread() { | 66 void PerformanceMonitor::GatherMetricsMapOnUIThread() { |
| 383 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 67 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 68 | |
| 384 static int current_update_sequence = 0; | 69 static int current_update_sequence = 0; |
| 385 // Even in the "somewhat" unlikely event this wraps around, | 70 // Even in the "somewhat" unlikely event this wraps around, |
| 386 // it doesn't matter. We just check it for inequality. | 71 // it doesn't matter. We just check it for inequality. |
| 387 current_update_sequence++; | 72 current_update_sequence++; |
| 388 | 73 |
| 389 // Find all render child processes; has to be done on the UI thread. | 74 // Find all render child processes; has to be done on the UI thread. |
| 390 for (content::RenderProcessHost::iterator rph_iter = | 75 for (content::RenderProcessHost::iterator rph_iter = |
| 391 content::RenderProcessHost::AllHostsIterator(); | 76 content::RenderProcessHost::AllHostsIterator(); |
| 392 !rph_iter.IsAtEnd(); rph_iter.Advance()) { | 77 !rph_iter.IsAtEnd(); rph_iter.Advance()) { |
| 393 base::ProcessHandle handle = rph_iter.GetCurrentValue()->GetHandle(); | 78 base::ProcessHandle handle = rph_iter.GetCurrentValue()->GetHandle(); |
| 394 MarkProcessAsAlive(handle, content::PROCESS_TYPE_RENDERER, | 79 MarkProcessAsAlive(handle, content::PROCESS_TYPE_RENDERER, |
| 395 current_update_sequence); | 80 current_update_sequence); |
| 396 } | 81 } |
| 397 | 82 |
| 398 BrowserThread::PostTask( | 83 BrowserThread::PostTask( |
| 399 BrowserThread::IO, | 84 BrowserThread::IO, |
| 400 FROM_HERE, | 85 FROM_HERE, |
| 401 base::Bind(&PerformanceMonitor::GatherMetricsMapOnIOThread, | 86 base::Bind(&PerformanceMonitor::GatherMetricsMapOnIOThread, |
| 402 base::Unretained(this), | 87 base::Unretained(this), |
| 403 current_update_sequence)); | 88 current_update_sequence)); |
| 404 } | 89 } |
| 405 | 90 |
| 406 void PerformanceMonitor::MarkProcessAsAlive(const base::ProcessHandle& handle, | 91 void PerformanceMonitor::MarkProcessAsAlive(const base::ProcessHandle& handle, |
| 407 int process_type, | 92 int process_type, |
| 408 int current_update_sequence) { | 93 int current_update_sequence) { |
| 409 if (handle == 0) { | 94 if (handle == 0) { |
| 410 // Process may not be valid yet. | 95 // Process may not be valid yet. |
| 411 return; | 96 return; |
| 412 } | 97 } |
| 413 | 98 |
| 414 MetricsMap::iterator process_metrics_iter = metrics_map_.find(handle); | 99 MetricsMap::iterator process_metrics_iter = metrics_map_.find(handle); |
| 415 if (process_metrics_iter == metrics_map_.end()) { | 100 if (process_metrics_iter == metrics_map_.end()) { |
| 416 // If we're not already watching the process, let's initialize it. | 101 // If we're not already watching the process, let's initialize it. |
| 417 metrics_map_[handle] | 102 metrics_map_[handle] |
| 418 .Initialize(handle, process_type, current_update_sequence); | 103 .Initialize(handle, process_type, current_update_sequence); |
| 419 } else { | 104 } else { |
| 420 // If we are watching the process, touch it to keep it alive. | 105 // If we are watching the process, touch it to keep it alive. |
| 421 ProcessMetricsHistory& process_metrics = process_metrics_iter->second; | 106 ProcessMetricsHistory& process_metrics = process_metrics_iter->second; |
| 422 process_metrics.set_last_update_sequence(current_update_sequence); | 107 process_metrics.set_last_update_sequence(current_update_sequence); |
| 423 } | 108 } |
| 424 } | 109 } |
| 425 | 110 |
| 426 #if !defined(OS_ANDROID) | |
| 427 void PerformanceMonitor::UpdateLiveProfiles() { | |
| 428 std::string time = TimeToString(base::Time::Now()); | |
| 429 scoped_ptr<std::set<std::string> > active_profiles( | |
| 430 new std::set<std::string>()); | |
| 431 | |
| 432 for (chrome::BrowserIterator it; !it.done(); it.Next()) | |
| 433 active_profiles->insert(it->profile()->GetDebugName()); | |
| 434 | |
| 435 BrowserThread::PostBlockingPoolSequencedTask( | |
| 436 Database::kDatabaseSequenceToken, | |
| 437 FROM_HERE, | |
| 438 base::Bind(&PerformanceMonitor::UpdateLiveProfilesHelper, | |
| 439 base::Unretained(this), | |
| 440 base::Passed(&active_profiles), | |
| 441 time)); | |
| 442 } | |
| 443 | |
| 444 void PerformanceMonitor::UpdateLiveProfilesHelper( | |
| 445 scoped_ptr<std::set<std::string> > active_profiles, | |
| 446 std::string time) { | |
| 447 CHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 448 DCHECK(database_logging_enabled_); | |
| 449 | |
| 450 for (std::set<std::string>::const_iterator iter = active_profiles->begin(); | |
| 451 iter != active_profiles->end(); ++iter) { | |
| 452 database_->AddStateValue(kStateProfilePrefix + *iter, time); | |
| 453 } | |
| 454 } | |
| 455 #endif | |
| 456 | |
| 457 void PerformanceMonitor::GatherMetricsMapOnIOThread( | 111 void PerformanceMonitor::GatherMetricsMapOnIOThread( |
| 458 int current_update_sequence) { | 112 int current_update_sequence) { |
| 459 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 113 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 460 | 114 |
| 461 // Find all child processes (does not include renderers), which has to be | 115 // Find all child processes (does not include renderers), which has to be |
| 462 // done on the IO thread. | 116 // done on the IO thread. |
| 463 for (content::BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) { | 117 for (content::BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) { |
| 464 const content::ChildProcessData& child_process_data = iter.GetData(); | 118 const content::ChildProcessData& child_process_data = iter.GetData(); |
| 465 base::ProcessHandle handle = child_process_data.handle; | 119 base::ProcessHandle handle = child_process_data.handle; |
| 466 MarkProcessAsAlive(handle, child_process_data.process_type, | 120 MarkProcessAsAlive(handle, child_process_data.process_type, |
| 467 current_update_sequence); | 121 current_update_sequence); |
| 468 } | 122 } |
| 469 | 123 |
| 470 // Add the current (browser) process. | 124 // Add the current (browser) process. |
| 471 MarkProcessAsAlive(base::GetCurrentProcessHandle(), | 125 MarkProcessAsAlive(base::GetCurrentProcessHandle(), |
| 472 content::PROCESS_TYPE_BROWSER, current_update_sequence); | 126 content::PROCESS_TYPE_BROWSER, current_update_sequence); |
| 473 | 127 |
| 474 BrowserThread::PostBlockingPoolSequencedTask( | |
| 475 Database::kDatabaseSequenceToken, | |
| 476 FROM_HERE, | |
| 477 base::Bind(&PerformanceMonitor::StoreMetricsOnBackgroundThread, | |
| 478 base::Unretained(this), current_update_sequence, | |
| 479 performance_data_for_io_thread_)); | |
| 480 } | |
| 481 | |
| 482 void PerformanceMonitor::StoreMetricsOnBackgroundThread( | |
| 483 int current_update_sequence, | |
| 484 const PerformanceDataForIOThread& performance_data_for_io_thread) { | |
| 485 CHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 486 | |
| 487 base::Time time_now = base::Time::Now(); | |
| 488 | |
| 489 // The timing can be off by kSampleIntervalInSeconds during any one particular | |
| 490 // run, but will be correct over time. | |
| 491 bool end_of_cycle = time_now >= next_collection_time_; | |
| 492 if (end_of_cycle) { | |
| 493 next_collection_time_ += | |
| 494 base::TimeDelta::FromSeconds(gather_interval_in_seconds_); | |
| 495 } | |
| 496 | |
| 497 double cpu_usage = 0.0; | 128 double cpu_usage = 0.0; |
| 498 size_t private_memory_sum = 0; | 129 size_t private_memory_sum = 0; |
| 499 size_t shared_memory_sum = 0; | 130 size_t shared_memory_sum = 0; |
| 500 | 131 |
| 501 // Update metrics for all watched processes; remove dead entries from the map. | 132 // Update metrics for all watched processes; remove dead entries from the map. |
| 502 MetricsMap::iterator iter = metrics_map_.begin(); | 133 MetricsMap::iterator iter = metrics_map_.begin(); |
| 503 while (iter != metrics_map_.end()) { | 134 while (iter != metrics_map_.end()) { |
| 504 ProcessMetricsHistory& process_metrics = iter->second; | 135 ProcessMetricsHistory& process_metrics = iter->second; |
| 505 if (process_metrics.last_update_sequence() != current_update_sequence) { | 136 if (process_metrics.last_update_sequence() != current_update_sequence) { |
| 506 // Not touched this iteration; let's get rid of it. | 137 // Not touched this iteration; let's get rid of it. |
| 507 metrics_map_.erase(iter++); | 138 metrics_map_.erase(iter++); |
| 508 } else { | 139 } else { |
| 509 process_metrics.SampleMetrics(); | 140 process_metrics.SampleMetrics(); |
| 510 | 141 |
| 511 if (end_of_cycle) { | 142 // Gather averages of previously sampled metrics. |
| 512 // Gather averages of previously sampled metrics. | 143 cpu_usage += process_metrics.GetAverageCPUUsage(); |
| 513 cpu_usage += process_metrics.GetAverageCPUUsage(); | |
| 514 | 144 |
| 515 size_t private_memory = 0; | 145 size_t private_memory = 0; |
| 516 size_t shared_memory = 0; | 146 size_t shared_memory = 0; |
| 517 process_metrics.GetAverageMemoryBytes(&private_memory, &shared_memory); | 147 process_metrics.GetAverageMemoryBytes(&private_memory, &shared_memory); |
| 518 private_memory_sum += private_memory; | 148 private_memory_sum += private_memory; |
| 519 shared_memory_sum += shared_memory; | 149 shared_memory_sum += shared_memory; |
| 520 | 150 |
| 521 process_metrics.EndOfCycle(); | 151 process_metrics.EndOfCycle(); |
| 522 } | |
| 523 | 152 |
| 524 ++iter; | 153 ++iter; |
| 525 } | 154 } |
| 526 } | 155 } |
| 527 | |
| 528 // Store previously-sampled metrics. | |
| 529 if (end_of_cycle && database_logging_enabled_) { | |
| 530 if (!metrics_map_.empty()) { | |
| 531 database_->AddMetric(Metric(METRIC_CPU_USAGE, time_now, cpu_usage)); | |
| 532 database_->AddMetric(Metric(METRIC_PRIVATE_MEMORY_USAGE, | |
| 533 time_now, | |
| 534 static_cast<double>(private_memory_sum))); | |
| 535 database_->AddMetric(Metric(METRIC_SHARED_MEMORY_USAGE, | |
| 536 time_now, | |
| 537 static_cast<double>(shared_memory_sum))); | |
| 538 } | |
| 539 | |
| 540 database_->AddMetric( | |
| 541 Metric(METRIC_NETWORK_BYTES_READ, | |
| 542 time_now, | |
| 543 static_cast<double>( | |
| 544 performance_data_for_io_thread.network_bytes_read))); | |
| 545 } | |
| 546 | |
| 547 BrowserThread::PostTask( | |
| 548 BrowserThread::UI, | |
| 549 FROM_HERE, | |
| 550 base::Bind(&PerformanceMonitor::StartGatherCycle, | |
| 551 base::Unretained(this))); | |
| 552 } | |
| 553 | |
| 554 void PerformanceMonitor::BytesReadOnIOThread(const net::URLRequest& request, | |
| 555 const int bytes_read) { | |
| 556 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 557 | |
| 558 if (initialized_ && !request.url().SchemeIsFile()) | |
| 559 performance_data_for_io_thread_.network_bytes_read += bytes_read; | |
| 560 } | |
| 561 | |
| 562 void PerformanceMonitor::Observe(int type, | |
| 563 const content::NotificationSource& source, | |
| 564 const content::NotificationDetails& details) { | |
| 565 DCHECK(database_logging_enabled_); | |
| 566 | |
| 567 switch (type) { | |
| 568 #if defined(ENABLE_EXTENSIONS) | |
| 569 case extensions::NOTIFICATION_EXTENSION_WILL_BE_INSTALLED_DEPRECATED: { | |
| 570 AddExtensionEvent( | |
| 571 EVENT_EXTENSION_INSTALL, | |
| 572 content::Details<const extensions::InstalledExtensionInfo>(details)-> | |
| 573 extension); | |
| 574 break; | |
| 575 } | |
| 576 case extensions::NOTIFICATION_EXTENSION_ENABLED: { | |
| 577 AddExtensionEvent(EVENT_EXTENSION_ENABLE, | |
| 578 content::Details<Extension>(details).ptr()); | |
| 579 break; | |
| 580 } | |
| 581 case extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: { | |
| 582 const UnloadedExtensionInfo* info = | |
| 583 content::Details<UnloadedExtensionInfo>(details).ptr(); | |
| 584 | |
| 585 // Check if the extension was unloaded because it was disabled. | |
| 586 if (info->reason == UnloadedExtensionInfo::REASON_DISABLE) { | |
| 587 AddExtensionEvent(EVENT_EXTENSION_DISABLE, | |
| 588 info->extension); | |
| 589 } | |
| 590 break; | |
| 591 } | |
| 592 case extensions::NOTIFICATION_CRX_INSTALLER_DONE: { | |
| 593 const extensions::CrxInstaller* installer = | |
| 594 content::Source<extensions::CrxInstaller>(source).ptr(); | |
| 595 const extensions::Extension* extension = | |
| 596 content::Details<Extension>(details).ptr(); | |
| 597 | |
| 598 // Check if the reason for the install was due to a successful | |
| 599 // extension update. |extension| is NULL in case of install failure. | |
| 600 if (extension && | |
| 601 installer->install_cause() == extension_misc::INSTALL_CAUSE_UPDATE) { | |
| 602 AddExtensionEvent(EVENT_EXTENSION_UPDATE, extension); | |
| 603 } | |
| 604 break; | |
| 605 } | |
| 606 case extensions::NOTIFICATION_EXTENSION_UNINSTALLED_DEPRECATED: { | |
| 607 AddExtensionEvent(EVENT_EXTENSION_UNINSTALL, | |
| 608 content::Details<Extension>(details).ptr()); | |
| 609 break; | |
| 610 } | |
| 611 #endif // defined(ENABLE_EXTENSIONS) | |
| 612 case content::NOTIFICATION_RENDER_WIDGET_HOST_HANG: { | |
| 613 std::string url; | |
| 614 content::RenderWidgetHost* widget = | |
| 615 content::Source<content::RenderWidgetHost>(source).ptr(); | |
| 616 if (widget->IsRenderView()) { | |
| 617 content::RenderViewHost* view = content::RenderViewHost::From(widget); | |
| 618 MaybeGetURLFromRenderView(view, &url); | |
| 619 } | |
| 620 AddEvent(util::CreateRendererFailureEvent(base::Time::Now(), | |
| 621 EVENT_RENDERER_HANG, | |
| 622 url)); | |
| 623 break; | |
| 624 } | |
| 625 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: { | |
| 626 AddRendererClosedEvent( | |
| 627 content::Source<content::RenderProcessHost>(source).ptr(), | |
| 628 *content::Details<content::RenderProcessHost::RendererClosedDetails>( | |
| 629 details).ptr()); | |
| 630 break; | |
| 631 } | |
| 632 case chrome::NOTIFICATION_PROFILE_ADDED: { | |
| 633 Profile* profile = content::Source<Profile>(source).ptr(); | |
| 634 if (profile->GetLastSessionExitType() == Profile::EXIT_CRASHED) { | |
| 635 BrowserThread::PostBlockingPoolSequencedTask( | |
| 636 Database::kDatabaseSequenceToken, | |
| 637 FROM_HERE, | |
| 638 base::Bind( | |
| 639 &PerformanceMonitor::AddUncleanExitEventOnBackgroundThread, | |
| 640 base::Unretained(this), | |
| 641 profile->GetDebugName())); | |
| 642 } | |
| 643 break; | |
| 644 } | |
| 645 case content::NOTIFICATION_LOAD_STOP: { | |
| 646 const content::LoadNotificationDetails* load_details = | |
| 647 content::Details<content::LoadNotificationDetails>(details).ptr(); | |
| 648 if (!load_details) | |
| 649 break; | |
| 650 BrowserThread::PostBlockingPoolSequencedTask( | |
| 651 Database::kDatabaseSequenceToken, | |
| 652 FROM_HERE, | |
| 653 base::Bind( | |
| 654 &PerformanceMonitor::AddMetricOnBackgroundThread, | |
| 655 base::Unretained(this), | |
| 656 Metric(METRIC_PAGE_LOAD_TIME, | |
| 657 base::Time::Now(), | |
| 658 static_cast<double>( | |
| 659 load_details->load_time.ToInternalValue())))); | |
| 660 break; | |
| 661 } | |
| 662 default: { | |
| 663 NOTREACHED(); | |
| 664 break; | |
| 665 } | |
| 666 } | |
| 667 } | |
| 668 | |
| 669 #if defined(ENABLE_EXTENSIONS) | |
| 670 void PerformanceMonitor::AddExtensionEvent(EventType type, | |
| 671 const Extension* extension) { | |
| 672 DCHECK(type == EVENT_EXTENSION_INSTALL || | |
| 673 type == EVENT_EXTENSION_UNINSTALL || | |
| 674 type == EVENT_EXTENSION_UPDATE || | |
| 675 type == EVENT_EXTENSION_ENABLE || | |
| 676 type == EVENT_EXTENSION_DISABLE); | |
| 677 AddEvent(util::CreateExtensionEvent(type, | |
| 678 base::Time::Now(), | |
| 679 extension->id(), | |
| 680 extension->name(), | |
| 681 extension->url().spec(), | |
| 682 extension->location(), | |
| 683 extension->VersionString(), | |
| 684 extension->description())); | |
| 685 } | |
| 686 #endif // defined(ENABLE_EXTENSIONS) | |
| 687 | |
| 688 void PerformanceMonitor::AddRendererClosedEvent( | |
| 689 content::RenderProcessHost* host, | |
| 690 const content::RenderProcessHost::RendererClosedDetails& details) { | |
| 691 // We only care if this is an invalid termination. | |
| 692 if (details.status == base::TERMINATION_STATUS_NORMAL_TERMINATION || | |
| 693 details.status == base::TERMINATION_STATUS_STILL_RUNNING) | |
| 694 return; | |
| 695 | |
| 696 // Determine the type of crash. | |
| 697 EventType type = | |
| 698 details.status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED ? | |
| 699 EVENT_RENDERER_KILLED : EVENT_RENDERER_CRASH; | |
| 700 | |
| 701 // A RenderProcessHost may contain multiple render views - for each valid | |
| 702 // render view, extract the url, and append it to the string, comma-separating | |
| 703 // the entries. | |
| 704 std::string url_list; | |
| 705 scoped_ptr<content::RenderWidgetHostIterator> widgets( | |
| 706 content::RenderWidgetHost::GetRenderWidgetHosts()); | |
| 707 while (content::RenderWidgetHost* widget = widgets->GetNextHost()) { | |
| 708 if (widget->GetProcess()->GetID() != host->GetID()) | |
| 709 continue; | |
| 710 if (!widget->IsRenderView()) | |
| 711 continue; | |
| 712 | |
| 713 content::RenderViewHost* view = content::RenderViewHost::From(widget); | |
| 714 std::string url; | |
| 715 if (!MaybeGetURLFromRenderView(view, &url)) | |
| 716 continue; | |
| 717 | |
| 718 if (!url_list.empty()) | |
| 719 url_list += ", "; | |
| 720 | |
| 721 url_list += url; | |
| 722 } | |
| 723 | |
| 724 AddEvent(util::CreateRendererFailureEvent(base::Time::Now(), type, url_list)); | |
| 725 } | 156 } |
| 726 | 157 |
| 727 } // namespace performance_monitor | 158 } // namespace performance_monitor |
| OLD | NEW |