1 // Copyright (c) 2011 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/task_manager/task_manager.h" 6 7 #include "base/compiler_specific.h" 8 #include "base/i18n/number_formatting.h" 9 #include "base/i18n/rtl.h" 10 #include "base/process_util.h" 11 #include "base/string_number_conversions.h" 12 #include "base/string_util.h" 13 #include "base/threading/thread.h" 14 #include "base/utf_string_conversions.h" 15 #include "chrome/browser/browser_process.h" 16 #include "chrome/browser/net/url_request_tracking.h" 17 #include "chrome/browser/prefs/pref_service.h" 18 #include "chrome/browser/profiles/profile_manager.h" 19 #include "chrome/browser/task_manager/task_manager_resource_providers.h" 20 #include "chrome/browser/ui/browser_list.h" 21 #include "chrome/browser/ui/browser_window.h" 22 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" 23 #include "chrome/common/pref_names.h" 24 #include "chrome/common/url_constants.h" 25 #include "content/browser/browser_thread.h" 26 #include "content/browser/renderer_host/render_process_host.h" 27 #include "content/browser/renderer_host/resource_dispatcher_host.h" 28 #include "content/browser/tab_contents/tab_contents.h" 29 #include "content/common/result_codes.h" 30 #include "grit/app_resources.h" 31 #include "grit/chromium_strings.h" 32 #include "grit/generated_resources.h" 33 #include "net/url_request/url_request.h" 34 #include "net/url_request/url_request_job.h" 35 #include "ui/base/l10n/l10n_util.h" 36 #include "ui/base/resource/resource_bundle.h" 37 #include "unicode/coll.h" 38 39 #if defined(OS_MACOSX) 40 #include "chrome/browser/mach_broker_mac.h" 41 #endif 42 43 namespace { 44 45 // The delay between updates of the information (in ms). 46 #if defined(OS_MACOSX) 47 // Match Activity Monitor's default refresh rate. 48 const int kUpdateTimeMs = 2000; 49 #else 50 const int kUpdateTimeMs = 1000; 51 #endif 52 53 template <class T> 54 int ValueCompare(T value1, T value2) { 55 if (value1 < value2) 56 return -1; 57 if (value1 == value2) 58 return 0; 59 return 1; 60 } 61 62 string16 FormatStatsSize(const WebKit::WebCache::ResourceTypeStat& stat) { 63 return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_CACHE_SIZE_CELL_TEXT, 64 FormatBytes(stat.size, DATA_UNITS_KIBIBYTE, false), 65 FormatBytes(stat.liveSize, DATA_UNITS_KIBIBYTE, false)); 66 } 67 68 } // namespace 69 70 //////////////////////////////////////////////////////////////////////////////// 71 // TaskManagerModel class 72 //////////////////////////////////////////////////////////////////////////////// 73 74 TaskManagerModel::TaskManagerModel(TaskManager* task_manager) 75 : update_requests_(0), 76 update_state_(IDLE), 77 goat_salt_(rand()) { 78 AddResourceProvider( 79 new TaskManagerBrowserProcessResourceProvider(task_manager)); 80 AddResourceProvider( 81 new TaskManagerBackgroundContentsResourceProvider(task_manager)); 82 AddResourceProvider(new TaskManagerTabContentsResourceProvider(task_manager)); 83 AddResourceProvider(new TaskManagerPrerenderResourceProvider(task_manager)); 84 AddResourceProvider( 85 new TaskManagerChildProcessResourceProvider(task_manager)); 86 AddResourceProvider( 87 new TaskManagerExtensionProcessResourceProvider(task_manager)); 88 AddResourceProvider( 89 new TaskManagerNotificationResourceProvider(task_manager)); 90 } 91 92 TaskManagerModel::~TaskManagerModel() { 93 for (ResourceProviderList::iterator iter = providers_.begin(); 94 iter != providers_.end(); ++iter) { 95 (*iter)->Release(); 96 } 97 } 98 99 int TaskManagerModel::ResourceCount() const { 100 return resources_.size(); 101 } 102 103 void TaskManagerModel::AddObserver(TaskManagerModelObserver* observer) { 104 observer_list_.AddObserver(observer); 105 } 106 107 void TaskManagerModel::RemoveObserver(TaskManagerModelObserver* observer) { 108 observer_list_.RemoveObserver(observer); 109 } 110 111 string16 TaskManagerModel::GetResourceTitle(int index) const { 112 CHECK_LT(index, ResourceCount()); 113 return resources_[index]->GetTitle(); 114 } 115 116 int64 TaskManagerModel::GetNetworkUsage(int index) const { 117 CHECK_LT(index, ResourceCount()); 118 return GetNetworkUsage(resources_[index]); 119 } 120 121 string16 TaskManagerModel::GetResourceNetworkUsage(int index) const { 122 int64 net_usage = GetNetworkUsage(index); 123 if (net_usage == -1) 124 return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT); 125 if (net_usage == 0) 126 return ASCIIToUTF16("0"); 127 string16 net_byte = FormatSpeed(net_usage, GetByteDisplayUnits(net_usage), 128 true); 129 // Force number string to have LTR directionality. 130 return base::i18n::GetDisplayStringInLTRDirectionality(net_byte); 131 } 132 133 double TaskManagerModel::GetCPUUsage(int index) const { 134 CHECK_LT(index, ResourceCount()); 135 return GetCPUUsage(resources_[index]); 136 } 137 138 string16 TaskManagerModel::GetResourceCPUUsage(int index) const { 139 CHECK_LT(index, ResourceCount()); 140 return UTF8ToUTF16(StringPrintf( 141 #if defined(OS_MACOSX) 142 // Activity Monitor shows %cpu with one decimal digit -- be 143 // consistent with that. 144 "%.1f", 145 #else 146 "%.0f", 147 #endif 148 GetCPUUsage(resources_[index]))); 149 } 150 151 string16 TaskManagerModel::GetResourcePrivateMemory(int index) const { 152 size_t private_mem; 153 if (!GetPrivateMemory(index, &private_mem)) 154 return ASCIIToUTF16("N/A"); 155 return GetMemCellText(private_mem); 156 } 157 158 string16 TaskManagerModel::GetResourceSharedMemory(int index) const { 159 size_t shared_mem; 160 if (!GetSharedMemory(index, &shared_mem)) 161 return ASCIIToUTF16("N/A"); 162 return GetMemCellText(shared_mem); 163 } 164 165 string16 TaskManagerModel::GetResourcePhysicalMemory(int index) const { 166 size_t phys_mem; 167 GetPhysicalMemory(index, &phys_mem); 168 return GetMemCellText(phys_mem); 169 } 170 171 int TaskManagerModel::GetProcessId(int index) const { 172 CHECK_LT(index, ResourceCount()); 173 return base::GetProcId(resources_[index]->GetProcess()); 174 } 175 176 string16 TaskManagerModel::GetResourceProcessId(int index) const { 177 return base::IntToString16(GetProcessId(index)); 178 } 179 180 string16 TaskManagerModel::GetResourceGoatsTeleported(int index) const { 181 CHECK_LT(index, ResourceCount()); 182 return base::FormatNumber(GetGoatsTeleported(index)); 183 } 184 185 string16 TaskManagerModel::GetResourceWebCoreImageCacheSize( 186 int index) const { 187 CHECK_LT(index, ResourceCount()); 188 if (!resources_[index]->ReportsCacheStats()) 189 return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT); 190 const WebKit::WebCache::ResourceTypeStats stats( 191 resources_[index]->GetWebCoreCacheStats()); 192 return FormatStatsSize(stats.images); 193 } 194 195 string16 TaskManagerModel::GetResourceWebCoreScriptsCacheSize( 196 int index) const { 197 CHECK_LT(index, ResourceCount()); 198 if (!resources_[index]->ReportsCacheStats()) 199 return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT); 200 const WebKit::WebCache::ResourceTypeStats stats( 201 resources_[index]->GetWebCoreCacheStats()); 202 return FormatStatsSize(stats.scripts); 203 } 204 205 string16 TaskManagerModel::GetResourceWebCoreCSSCacheSize( 206 int index) const { 207 CHECK_LT(index, ResourceCount()); 208 if (!resources_[index]->ReportsCacheStats()) 209 return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT); 210 const WebKit::WebCache::ResourceTypeStats stats( 211 resources_[index]->GetWebCoreCacheStats()); 212 return FormatStatsSize(stats.cssStyleSheets); 213 } 214 215 string16 TaskManagerModel::GetResourceSqliteMemoryUsed(int index) const { 216 CHECK_LT(index, ResourceCount()); 217 if (!resources_[index]->ReportsSqliteMemoryUsed()) 218 return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT); 219 return GetMemCellText(resources_[index]->SqliteMemoryUsedBytes()); 220 } 221 222 string16 TaskManagerModel::GetResourceV8MemoryAllocatedSize( 223 int index) const { 224 if (!resources_[index]->ReportsV8MemoryStats()) 225 return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT); 226 return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_CACHE_SIZE_CELL_TEXT, 227 FormatBytes(resources_[index]->GetV8MemoryAllocated(), 228 DATA_UNITS_KIBIBYTE, 229 false), 230 FormatBytes(resources_[index]->GetV8MemoryUsed(), 231 DATA_UNITS_KIBIBYTE, 232 false)); 233 } 234 235 bool TaskManagerModel::IsResourceFirstInGroup(int index) const { 236 CHECK_LT(index, ResourceCount()); 237 TaskManager::Resource* resource = resources_[index]; 238 GroupMap::const_iterator iter = group_map_.find(resource->GetProcess()); 239 DCHECK(iter != group_map_.end()); 240 const ResourceList* group = iter->second; 241 return ((*group)[0] == resource); 242 } 243 244 bool TaskManagerModel::IsBackgroundResource(int index) const { 245 CHECK_LT(index, ResourceCount()); 246 return resources_[index]->IsBackground(); 247 } 248 249 SkBitmap TaskManagerModel::GetResourceIcon(int index) const { 250 CHECK_LT(index, ResourceCount()); 251 SkBitmap icon = resources_[index]->GetIcon(); 252 if (!icon.isNull()) 253 return icon; 254 255 static SkBitmap* default_icon = ResourceBundle::GetSharedInstance(). 256 GetBitmapNamed(IDR_DEFAULT_FAVICON); 257 return *default_icon; 258 } 259 260 std::pair<int, int> TaskManagerModel::GetGroupRangeForResource(int index) 261 const { 262 CHECK_LT(index, ResourceCount()); 263 TaskManager::Resource* resource = resources_[index]; 264 GroupMap::const_iterator group_iter = 265 group_map_.find(resource->GetProcess()); 266 DCHECK(group_iter != group_map_.end()); 267 ResourceList* group = group_iter->second; 268 DCHECK(group); 269 if (group->size() == 1) { 270 return std::make_pair(index, 1); 271 } else { 272 for (int i = index; i >= 0; --i) { 273 if (resources_[i] == (*group)[0]) 274 return std::make_pair(i, group->size()); 275 } 276 NOTREACHED(); 277 return std::make_pair(-1, -1); 278 } 279 } 280 281 int TaskManagerModel::CompareValues(int row1, int row2, int col_id) const { 282 CHECK(row1 < ResourceCount() && row2 < ResourceCount()); 283 if (col_id == IDS_TASK_MANAGER_PAGE_COLUMN) { 284 // Let's do the default, string compare on the resource title. 285 static icu::Collator* collator = NULL; 286 if (!collator) { 287 UErrorCode create_status = U_ZERO_ERROR; 288 collator = icu::Collator::createInstance(create_status); 289 if (!U_SUCCESS(create_status)) { 290 collator = NULL; 291 NOTREACHED(); 292 } 293 } 294 string16 title1 = GetResourceTitle(row1); 295 string16 title2 = GetResourceTitle(row2); 296 UErrorCode compare_status = U_ZERO_ERROR; 297 UCollationResult compare_result = collator->compare( 298 static_cast<const UChar*>(title1.c_str()), 299 static_cast<int>(title1.length()), 300 static_cast<const UChar*>(title2.c_str()), 301 static_cast<int>(title2.length()), 302 compare_status); 303 DCHECK(U_SUCCESS(compare_status)); 304 return compare_result; 305 } else if (col_id == IDS_TASK_MANAGER_NET_COLUMN) { 306 return ValueCompare<int64>(GetNetworkUsage(resources_[row1]), 307 GetNetworkUsage(resources_[row2])); 308 } else if (col_id == IDS_TASK_MANAGER_CPU_COLUMN) { 309 return ValueCompare<double>(GetCPUUsage(resources_[row1]), 310 GetCPUUsage(resources_[row2])); 311 } else if (col_id == IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN) { 312 size_t value1; 313 size_t value2; 314 if (!GetPrivateMemory(row1, &value1) || !GetPrivateMemory(row2, &value2)) 315 return 0; 316 return ValueCompare<size_t>(value1, value2); 317 } else if (col_id == IDS_TASK_MANAGER_SHARED_MEM_COLUMN) { 318 size_t value1; 319 size_t value2; 320 if (!GetSharedMemory(row1, &value1) || !GetSharedMemory(row2, &value2)) 321 return 0; 322 return ValueCompare<size_t>(value1, value2); 323 } else if (col_id == IDS_TASK_MANAGER_PHYSICAL_MEM_COLUMN) { 324 size_t value1; 325 size_t value2; 326 if (!GetPhysicalMemory(row1, &value1) || 327 !GetPhysicalMemory(row2, &value2)) 328 return 0; 329 return ValueCompare<size_t>(value1, value2); 330 } else if (col_id == IDS_TASK_MANAGER_PROCESS_ID_COLUMN) { 331 int proc1_id = base::GetProcId(resources_[row1]->GetProcess()); 332 int proc2_id = base::GetProcId(resources_[row2]->GetProcess()); 333 return ValueCompare<int>(proc1_id, proc2_id); 334 } else if (col_id == IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN || 335 col_id == IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN || 336 col_id == IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN) { 337 WebKit::WebCache::ResourceTypeStats stats1 = { { 0 } }; 338 WebKit::WebCache::ResourceTypeStats stats2 = { { 0 } }; 339 if (resources_[row1]->ReportsCacheStats()) 340 stats1 = resources_[row1]->GetWebCoreCacheStats(); 341 if (resources_[row2]->ReportsCacheStats()) 342 stats2 = resources_[row2]->GetWebCoreCacheStats(); 343 if (IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN == col_id) 344 return ValueCompare<size_t>(stats1.images.size, stats2.images.size); 345 if (IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN == col_id) 346 return ValueCompare<size_t>(stats1.scripts.size, stats2.scripts.size); 347 DCHECK_EQ(IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN, col_id); 348 return ValueCompare<size_t>(stats1.cssStyleSheets.size, 349 stats2.cssStyleSheets.size); 350 } else if (col_id == IDS_TASK_MANAGER_GOATS_TELEPORTED_COLUMN) { 351 return ValueCompare<int>(GetGoatsTeleported(row1), 352 GetGoatsTeleported(row2)); 353 } else if (col_id == IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN) { 354 size_t value1; 355 size_t value2; 356 bool reports_v8_memory1 = GetV8Memory(row1, &value1); 357 bool reports_v8_memory2 = GetV8Memory(row2, &value2); 358 if (reports_v8_memory1 == reports_v8_memory2) 359 return ValueCompare<size_t>(value1, value2); 360 else 361 return reports_v8_memory1 ? 1 : -1; 362 } else { 363 NOTREACHED(); 364 return 0; 365 } 366 } 367 368 base::ProcessHandle TaskManagerModel::GetResourceProcessHandle(int index) 369 const { 370 CHECK_LT(index, ResourceCount()); 371 return resources_[index]->GetProcess(); 372 } 373 374 TaskManager::Resource::Type TaskManagerModel::GetResourceType(int index) const { 375 CHECK_LT(index, ResourceCount()); 376 return resources_[index]->GetType(); 377 } 378 379 TabContentsWrapper* TaskManagerModel::GetResourceTabContents(int index) const { 380 CHECK_LT(index, ResourceCount()); 381 return resources_[index]->GetTabContents(); 382 } 383 384 const Extension* TaskManagerModel::GetResourceExtension(int index) const { 385 CHECK_LT(index, ResourceCount()); 386 return resources_[index]->GetExtension(); 387 } 388 389 int64 TaskManagerModel::GetNetworkUsage(TaskManager::Resource* resource) 390 const { 391 int64 net_usage = GetNetworkUsageForResource(resource); 392 if (net_usage == 0 && !resource->SupportNetworkUsage()) 393 return -1; 394 return net_usage; 395 } 396 397 double TaskManagerModel::GetCPUUsage(TaskManager::Resource* resource) const { 398 CPUUsageMap::const_iterator iter = 399 cpu_usage_map_.find(resource->GetProcess()); 400 if (iter == cpu_usage_map_.end()) 401 return 0; 402 return iter->second; 403 } 404 405 bool TaskManagerModel::GetPrivateMemory(int index, size_t* result) const { 406 base::ProcessHandle handle = resources_[index]->GetProcess(); 407 MemoryUsageMap::const_iterator iter = memory_usage_map_.find(handle); 408 if (iter == memory_usage_map_.end()) { 409 std::pair<size_t, size_t> usage; 410 if (!GetAndCacheMemoryMetrics(handle, &usage)) 411 return false; 412 413 *result = usage.first; 414 } else { 415 *result = iter->second.first; 416 } 417 418 return true; 419 } 420 421 bool TaskManagerModel::GetSharedMemory(int index, size_t* result) const { 422 base::ProcessHandle handle = resources_[index]->GetProcess(); 423 MemoryUsageMap::const_iterator iter = memory_usage_map_.find(handle); 424 if (iter == memory_usage_map_.end()) { 425 std::pair<size_t, size_t> usage; 426 if (!GetAndCacheMemoryMetrics(handle, &usage)) 427 return false; 428 429 *result = usage.second; 430 } else { 431 *result = iter->second.second; 432 } 433 434 return true; 435 } 436 437 bool TaskManagerModel::GetPhysicalMemory(int index, size_t* result) const { 438 *result = 0; 439 base::ProcessMetrics* process_metrics; 440 if (!GetProcessMetricsForRow(index, &process_metrics)) 441 return false; 442 base::WorkingSetKBytes ws_usage; 443 if (!process_metrics->GetWorkingSetKBytes(&ws_usage)) 444 return false; 445 446 // Memory = working_set.private + working_set.shareable. 447 // We exclude the shared memory. 448 size_t total_bytes = process_metrics->GetWorkingSetSize(); 449 total_bytes -= ws_usage.shared * 1024; 450 *result = total_bytes; 451 return true; 452 } 453 454 bool TaskManagerModel::GetV8Memory(int index, size_t* result) const { 455 *result = 0; 456 if (!resources_[index]->ReportsV8MemoryStats()) 457 return false; 458 459 *result = resources_[index]->GetV8MemoryAllocated(); 460 return true; 461 } 462 463 int TaskManagerModel::GetGoatsTeleported(int index) const { 464 int seed = goat_salt_ * (index + 1); 465 return (seed >> 16) & 255; 466 } 467 468 string16 TaskManagerModel::GetMemCellText(int64 number) const { 469 #if !defined(OS_MACOSX) 470 string16 str = base::FormatNumber(number / 1024); 471 472 // Adjust number string if necessary. 473 base::i18n::AdjustStringForLocaleDirection(&str); 474 return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_MEM_CELL_TEXT, str); 475 #else 476 // System expectation is to show "100 KB", "200 MB", etc. 477 // TODO(thakis): Switch to metric units (as opposed to powers of two). 478 return FormatBytes(number, GetByteDisplayUnits(number), /*show_units=*/true); 479 #endif 480 } 481 482 void TaskManagerModel::StartUpdating() { 483 // Multiple StartUpdating requests may come in, and we only need to take 484 // action the first time. 485 update_requests_++; 486 if (update_requests_ > 1) 487 return; 488 DCHECK_EQ(1, update_requests_); 489 DCHECK_NE(TASK_PENDING, update_state_); 490 491 // If update_state_ is STOPPING, it means a task is still pending. Setting 492 // it to TASK_PENDING ensures the tasks keep being posted (by Refresh()). 493 if (update_state_ == IDLE) { 494 MessageLoop::current()->PostDelayedTask(FROM_HERE, 495 NewRunnableMethod(this, &TaskManagerModel::Refresh), 496 kUpdateTimeMs); 497 } 498 update_state_ = TASK_PENDING; 499 500 // Register jobs notifications so we can compute network usage (it must be 501 // done from the IO thread). 502 BrowserThread::PostTask( 503 BrowserThread::IO, FROM_HERE, 504 NewRunnableMethod( 505 this, &TaskManagerModel::RegisterForJobDoneNotifications)); 506 507 // Notify resource providers that we are updating. 508 for (ResourceProviderList::iterator iter = providers_.begin(); 509 iter != providers_.end(); ++iter) { 510 (*iter)->StartUpdating(); 511 } 512 } 513 514 void TaskManagerModel::StopUpdating() { 515 // Don't actually stop updating until we have heard as many calls as those 516 // to StartUpdating. 517 update_requests_--; 518 if (update_requests_ > 0) 519 return; 520 // Make sure that update_requests_ cannot go negative. 521 CHECK_EQ(0, update_requests_); 522 DCHECK_EQ(TASK_PENDING, update_state_); 523 update_state_ = STOPPING; 524 525 // Notify resource providers that we are done updating. 526 for (ResourceProviderList::const_iterator iter = providers_.begin(); 527 iter != providers_.end(); ++iter) { 528 (*iter)->StopUpdating(); 529 } 530 531 // Unregister jobs notification (must be done from the IO thread). 532 BrowserThread::PostTask( 533 BrowserThread::IO, FROM_HERE, 534 NewRunnableMethod( 535 this, &TaskManagerModel::UnregisterForJobDoneNotifications)); 536 537 // Must clear the resources before the next attempt to start updating. 538 Clear(); 539 } 540 541 void TaskManagerModel::AddResourceProvider( 542 TaskManager::ResourceProvider* provider) { 543 DCHECK(provider); 544 // AddRef matched with Release in destructor. 545 provider->AddRef(); 546 providers_.push_back(provider); 547 } 548 549 void TaskManagerModel::RegisterForJobDoneNotifications() { 550 net::g_url_request_job_tracker.AddObserver(this); 551 } 552 553 void TaskManagerModel::UnregisterForJobDoneNotifications() { 554 net::g_url_request_job_tracker.RemoveObserver(this); 555 } 556 557 void TaskManagerModel::AddResource(TaskManager::Resource* resource) { 558 base::ProcessHandle process = resource->GetProcess(); 559 560 ResourceList* group_entries = NULL; 561 GroupMap::const_iterator group_iter = group_map_.find(process); 562 int new_entry_index = 0; 563 if (group_iter == group_map_.end()) { 564 group_entries = new ResourceList(); 565 group_map_[process] = group_entries; 566 group_entries->push_back(resource); 567 568 // Not part of a group, just put at the end of the list. 569 resources_.push_back(resource); 570 new_entry_index = static_cast<int>(resources_.size() - 1); 571 } else { 572 group_entries = group_iter->second; 573 group_entries->push_back(resource); 574 575 // Insert the new entry right after the last entry of its group. 576 ResourceList::iterator iter = 577 std::find(resources_.begin(), 578 resources_.end(), 579 (*group_entries)[group_entries->size() - 2]); 580 DCHECK(iter != resources_.end()); 581 new_entry_index = static_cast<int>(iter - resources_.begin()) + 1; 582 resources_.insert(++iter, resource); 583 } 584 585 // Create the ProcessMetrics for this process if needed (not in map). 586 if (metrics_map_.find(process) == metrics_map_.end()) { 587 base::ProcessMetrics* pm = 588 #if !defined(OS_MACOSX) 589 base::ProcessMetrics::CreateProcessMetrics(process); 590 #else 591 base::ProcessMetrics::CreateProcessMetrics(process, 592 MachBroker::GetInstance()); 593 #endif 594 595 metrics_map_[process] = pm; 596 } 597 598 // Notify the table that the contents have changed for it to redraw. 599 FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_, 600 OnItemsAdded(new_entry_index, 1)); 601 } 602 603 void TaskManagerModel::RemoveResource(TaskManager::Resource* resource) { 604 base::ProcessHandle process = resource->GetProcess(); 605 606 // Find the associated group. 607 GroupMap::iterator group_iter = group_map_.find(process); 608 DCHECK(group_iter != group_map_.end()); 609 ResourceList* group_entries = group_iter->second; 610 611 // Remove the entry from the group map. 612 ResourceList::iterator iter = std::find(group_entries->begin(), 613 group_entries->end(), 614 resource); 615 DCHECK(iter != group_entries->end()); 616 group_entries->erase(iter); 617 618 // If there are no more entries for that process, do the clean-up. 619 if (group_entries->empty()) { 620 delete group_entries; 621 group_map_.erase(process); 622 623 // Nobody is using this process, we don't need the process metrics anymore. 624 MetricsMap::iterator pm_iter = metrics_map_.find(process); 625 DCHECK(pm_iter != metrics_map_.end()); 626 if (pm_iter != metrics_map_.end()) { 627 delete pm_iter->second; 628 metrics_map_.erase(process); 629 } 630 // And we don't need the CPU usage anymore either. 631 CPUUsageMap::iterator cpu_iter = cpu_usage_map_.find(process); 632 if (cpu_iter != cpu_usage_map_.end()) 633 cpu_usage_map_.erase(cpu_iter); 634 } 635 636 // Remove the entry from the model list. 637 iter = std::find(resources_.begin(), resources_.end(), resource); 638 DCHECK(iter != resources_.end()); 639 int index = static_cast<int>(iter - resources_.begin()); 640 resources_.erase(iter); 641 642 // Remove the entry from the network maps. 643 ResourceValueMap::iterator net_iter = 644 current_byte_count_map_.find(resource); 645 if (net_iter != current_byte_count_map_.end()) 646 current_byte_count_map_.erase(net_iter); 647 net_iter = displayed_network_usage_map_.find(resource); 648 if (net_iter != displayed_network_usage_map_.end()) 649 displayed_network_usage_map_.erase(net_iter); 650 651 // Notify the table that the contents have changed. 652 FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_, 653 OnItemsRemoved(index, 1)); 654 } 655 656 void TaskManagerModel::Clear() { 657 int size = ResourceCount(); 658 if (size > 0) { 659 resources_.clear(); 660 661 // Clear the groups. 662 for (GroupMap::iterator iter = group_map_.begin(); 663 iter != group_map_.end(); ++iter) { 664 delete iter->second; 665 } 666 group_map_.clear(); 667 668 // Clear the process related info. 669 for (MetricsMap::iterator iter = metrics_map_.begin(); 670 iter != metrics_map_.end(); ++iter) { 671 delete iter->second; 672 } 673 metrics_map_.clear(); 674 cpu_usage_map_.clear(); 675 676 // Clear the network maps. 677 current_byte_count_map_.clear(); 678 displayed_network_usage_map_.clear(); 679 680 FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_, 681 OnItemsRemoved(0, size)); 682 } 683 } 684 685 void TaskManagerModel::ModelChanged() { 686 // Notify the table that the contents have changed for it to redraw. 687 FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_, OnModelChanged()); 688 } 689 690 void TaskManagerModel::NotifyResourceTypeStats( 691 base::ProcessId renderer_id, 692 const WebKit::WebCache::ResourceTypeStats& stats) { 693 for (ResourceList::iterator it = resources_.begin(); 694 it != resources_.end(); ++it) { 695 if (base::GetProcId((*it)->GetProcess()) == renderer_id) { 696 (*it)->NotifyResourceTypeStats(stats); 697 } 698 } 699 } 700 701 void TaskManagerModel::NotifyV8HeapStats(base::ProcessId renderer_id, 702 size_t v8_memory_allocated, 703 size_t v8_memory_used) { 704 for (ResourceList::iterator it = resources_.begin(); 705 it != resources_.end(); ++it) { 706 if (base::GetProcId((*it)->GetProcess()) == renderer_id) { 707 (*it)->NotifyV8HeapStats(v8_memory_allocated, v8_memory_used); 708 } 709 } 710 } 711 712 void TaskManagerModel::Refresh() { 713 DCHECK_NE(IDLE, update_state_); 714 715 if (update_state_ == STOPPING) { 716 // We have been asked to stop. 717 update_state_ = IDLE; 718 return; 719 } 720 721 goat_salt_ = rand(); 722 723 // Compute the CPU usage values. 724 // Note that we compute the CPU usage for all resources (instead of doing it 725 // lazily) as process_util::GetCPUUsage() returns the CPU usage since the last 726 // time it was called, and not calling it everytime would skew the value the 727 // next time it is retrieved (as it would be for more than 1 cycle). 728 cpu_usage_map_.clear(); 729 for (ResourceList::iterator iter = resources_.begin(); 730 iter != resources_.end(); ++iter) { 731 base::ProcessHandle process = (*iter)->GetProcess(); 732 CPUUsageMap::iterator cpu_iter = cpu_usage_map_.find(process); 733 if (cpu_iter != cpu_usage_map_.end()) 734 continue; // Already computed. 735 736 MetricsMap::iterator metrics_iter = metrics_map_.find(process); 737 DCHECK(metrics_iter != metrics_map_.end()); 738 cpu_usage_map_[process] = metrics_iter->second->GetCPUUsage(); 739 } 740 741 // Clear the memory values so they can be querried lazily. 742 memory_usage_map_.clear(); 743 744 // Compute the new network usage values. 745 displayed_network_usage_map_.clear(); 746 for (ResourceValueMap::iterator iter = current_byte_count_map_.begin(); 747 iter != current_byte_count_map_.end(); ++iter) { 748 if (kUpdateTimeMs > 1000) { 749 int divider = (kUpdateTimeMs / 1000); 750 displayed_network_usage_map_[iter->first] = iter->second / divider; 751 } else { 752 displayed_network_usage_map_[iter->first] = iter->second * 753 (1000 / kUpdateTimeMs); 754 } 755 756 // Then we reset the current byte count. 757 iter->second = 0; 758 } 759 760 // Let resources update themselves if they need to. 761 for (ResourceList::iterator iter = resources_.begin(); 762 iter != resources_.end(); ++iter) { 763 (*iter)->Refresh(); 764 } 765 766 if (!resources_.empty()) { 767 FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_, 768 OnItemsChanged(0, ResourceCount())); 769 } 770 771 // Schedule the next update. 772 MessageLoop::current()->PostDelayedTask(FROM_HERE, 773 NewRunnableMethod(this, &TaskManagerModel::Refresh), 774 kUpdateTimeMs); 775 } 776 777 int64 TaskManagerModel::GetNetworkUsageForResource( 778 TaskManager::Resource* resource) const { 779 ResourceValueMap::const_iterator iter = 780 displayed_network_usage_map_.find(resource); 781 if (iter == displayed_network_usage_map_.end()) 782 return 0; 783 return iter->second; 784 } 785 786 void TaskManagerModel::BytesRead(BytesReadParam param) { 787 if (update_state_ != TASK_PENDING) { 788 // A notification sneaked in while we were stopping the updating, just 789 // ignore it. 790 return; 791 } 792 793 if (param.byte_count == 0) { 794 // Nothing to do if no bytes were actually read. 795 return; 796 } 797 798 // TODO(jcampan): this should be improved once we have a better way of 799 // linking a network notification back to the object that initiated it. 800 TaskManager::Resource* resource = NULL; 801 for (ResourceProviderList::iterator iter = providers_.begin(); 802 iter != providers_.end(); ++iter) { 803 resource = (*iter)->GetResource(param.origin_pid, 804 param.render_process_host_child_id, 805 param.routing_id); 806 if (resource) 807 break; 808 } 809 810 if (resource == NULL) { 811 // We can't match a resource to the notification. That might mean the 812 // tab that started a download was closed, or the request may have had 813 // no originating resource associated with it in the first place. 814 // We attribute orphaned/unaccounted activity to the Browser process. 815 CHECK(param.origin_pid || (param.render_process_host_child_id != -1)); 816 param.origin_pid = 0; 817 param.render_process_host_child_id = param.routing_id = -1; 818 BytesRead(param); 819 return; 820 } 821 822 // We do support network usage, mark the resource as such so it can report 0 823 // instead of N/A. 824 if (!resource->SupportNetworkUsage()) 825 resource->SetSupportNetworkUsage(); 826 827 ResourceValueMap::const_iterator iter_res = 828 current_byte_count_map_.find(resource); 829 if (iter_res == current_byte_count_map_.end()) 830 current_byte_count_map_[resource] = param.byte_count; 831 else 832 current_byte_count_map_[resource] = iter_res->second + param.byte_count; 833 } 834 835 836 // In order to retrieve the network usage, we register for net::URLRequestJob 837 // notifications. Every time we get notified some bytes were read we bump a 838 // counter of read bytes for the associated resource. When the timer ticks, 839 // we'll compute the actual network usage (see the Refresh method). 840 void TaskManagerModel::OnJobAdded(net::URLRequestJob* job) { 841 } 842 843 void TaskManagerModel::OnJobRemoved(net::URLRequestJob* job) { 844 } 845 846 void TaskManagerModel::OnJobDone(net::URLRequestJob* job, 847 const net::URLRequestStatus& status) { 848 } 849 850 void TaskManagerModel::OnJobRedirect(net::URLRequestJob* job, 851 const GURL& location, 852 int status_code) { 853 } 854 855 void TaskManagerModel::OnBytesRead(net::URLRequestJob* job, const char* buf, 856 int byte_count) { 857 // Only net::URLRequestJob instances created by the ResourceDispatcherHost 858 // have a render view associated. All other jobs will have -1 returned for 859 // the render process child and routing ids - the jobs may still match a 860 // resource based on their origin id, otherwise BytesRead() will attribute 861 // the activity to the Browser resource. 862 int render_process_host_child_id = -1, routing_id = -1; 863 ResourceDispatcherHost::RenderViewForRequest(job->request(), 864 &render_process_host_child_id, 865 &routing_id); 866 867 // Get the origin PID of the request's originator. This will only be set for 868 // plugins - for renderer or browser initiated requests it will be zero. 869 int origin_pid = 870 chrome_browser_net::GetOriginPIDForRequest(job->request()); 871 872 // This happens in the IO thread, post it to the UI thread. 873 BrowserThread::PostTask( 874 BrowserThread::UI, FROM_HERE, 875 NewRunnableMethod( 876 this, 877 &TaskManagerModel::BytesRead, 878 BytesReadParam(origin_pid, 879 render_process_host_child_id, 880 routing_id, byte_count))); 881 } 882 883 bool TaskManagerModel::GetProcessMetricsForRow( 884 int row, base::ProcessMetrics** proc_metrics) const { 885 DCHECK(row < ResourceCount()); 886 *proc_metrics = NULL; 887 888 MetricsMap::const_iterator iter = 889 metrics_map_.find(resources_[row]->GetProcess()); 890 if (iter == metrics_map_.end()) 891 return false; 892 *proc_metrics = iter->second; 893 return true; 894 } 895 896 //////////////////////////////////////////////////////////////////////////////// 897 // TaskManager class 898 //////////////////////////////////////////////////////////////////////////////// 899 900 // static 901 void TaskManager::RegisterPrefs(PrefService* prefs) { 902 prefs->RegisterDictionaryPref(prefs::kTaskManagerWindowPlacement); 903 } 904 905 TaskManager::TaskManager() 906 : ALLOW_THIS_IN_INITIALIZER_LIST(model_(new TaskManagerModel(this))) { 907 } 908 909 TaskManager::~TaskManager() { 910 } 911 912 bool TaskManager::IsBrowserProcess(int index) const { 913 // If some of the selection is out of bounds, ignore. This may happen when 914 // killing a process that manages several pages. 915 return index < model_->ResourceCount() && 916 model_->GetResourceProcessHandle(index) == 917 base::GetCurrentProcessHandle(); 918 } 919 920 void TaskManager::KillProcess(int index) { 921 base::ProcessHandle process = model_->GetResourceProcessHandle(index); 922 DCHECK(process); 923 if (process != base::GetCurrentProcessHandle()) 924 base::KillProcess(process, ResultCodes::KILLED, false); 925 } 926 927 void TaskManager::ActivateProcess(int index) { 928 // GetResourceTabContents returns a pointer to the relevant tab contents for 929 // the resource. If the index doesn't correspond to a Tab (i.e. refers to 930 // the Browser process or a plugin), GetTabContents will return NULL. 931 TabContentsWrapper* chosen_tab_contents = 932 model_->GetResourceTabContents(index); 933 if (chosen_tab_contents) 934 chosen_tab_contents->tab_contents()->Activate(); 935 } 936 937 void TaskManager::AddResource(Resource* resource) { 938 model_->AddResource(resource); 939 } 940 941 void TaskManager::RemoveResource(Resource* resource) { 942 model_->RemoveResource(resource); 943 } 944 945 void TaskManager::OnWindowClosed() { 946 model_->StopUpdating(); 947 } 948 949 void TaskManager::ModelChanged() { 950 model_->ModelChanged(); 951 } 952 953 // static 954 TaskManager* TaskManager::GetInstance() { 955 return Singleton<TaskManager>::get(); 956 } 957 958 void TaskManager::OpenAboutMemory() { 959 Browser* browser = BrowserList::GetLastActive(); 960 961 if (!browser) { 962 // On OS X, the task manager can be open without any open browser windows. 963 if (!g_browser_process || !g_browser_process->profile_manager()) 964 return; 965 Profile* profile = 966 g_browser_process->profile_manager()->GetDefaultProfile(); 967 if (!profile) 968 return; 969 browser = Browser::Create(profile); 970 browser->OpenURL(GURL(chrome::kAboutMemoryURL), GURL(), NEW_FOREGROUND_TAB, 971 PageTransition::LINK); 972 browser->window()->Show(); 973 } else { 974 browser->OpenURL(GURL(chrome::kAboutMemoryURL), GURL(), NEW_FOREGROUND_TAB, 975 PageTransition::LINK); 976 977 // In case the browser window is minimzed, show it. If |browser| is a 978 // non-tabbed window, the call to OpenURL above will have opened a 979 // TabContents in a tabbed browser, so we need to grab it with GetLastActive 980 // before the call to show(). 981 if (browser->type() & (Browser::TYPE_APP | 982 Browser::TYPE_DEVTOOLS | 983 Browser::TYPE_POPUP)) { 984 browser = BrowserList::GetLastActive(); 985 DCHECK(browser); 986 } 987 988 browser->window()->Show(); 989 } 990 } 991 992 bool TaskManagerModel::GetAndCacheMemoryMetrics( 993 base::ProcessHandle handle, 994 std::pair<size_t, size_t>* usage) const { 995 MetricsMap::const_iterator iter = metrics_map_.find(handle); 996 if (iter == metrics_map_.end()) 997 return false; 998 999 if (!iter->second->GetMemoryBytes(&usage->first, &usage->second)) 1000 return false; 1001 1002 memory_usage_map_.insert(std::make_pair(handle, *usage)); 1003 return true; 1004 } 1005