1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/task_manager/task_manager.h" 6 7 #include "base/bind.h" 8 #include "base/i18n/number_formatting.h" 9 #include "base/i18n/rtl.h" 10 #include "base/prefs/pref_registry_simple.h" 11 #include "base/process/process_metrics.h" 12 #include "base/stl_util.h" 13 #include "base/strings/string16.h" 14 #include "base/strings/string_number_conversions.h" 15 #include "base/strings/stringprintf.h" 16 #include "base/strings/utf_string_conversions.h" 17 #include "chrome/browser/browser_process.h" 18 #include "chrome/browser/profiles/profile_manager.h" 19 #include "chrome/browser/task_manager/background_information.h" 20 #include "chrome/browser/task_manager/browser_process_resource_provider.h" 21 #include "chrome/browser/task_manager/child_process_resource_provider.h" 22 #include "chrome/browser/task_manager/extension_information.h" 23 #include "chrome/browser/task_manager/guest_information.h" 24 #include "chrome/browser/task_manager/panel_information.h" 25 #include "chrome/browser/task_manager/printing_information.h" 26 #include "chrome/browser/task_manager/resource_provider.h" 27 #include "chrome/browser/task_manager/tab_contents_information.h" 28 #include "chrome/browser/task_manager/web_contents_resource_provider.h" 29 #include "chrome/browser/ui/browser_navigator.h" 30 #include "chrome/common/pref_names.h" 31 #include "chrome/common/url_constants.h" 32 #include "chrome/grit/generated_resources.h" 33 #include "components/nacl/browser/nacl_browser.h" 34 #include "content/public/browser/browser_thread.h" 35 #include "content/public/browser/gpu_data_manager.h" 36 #include "content/public/browser/gpu_data_manager_observer.h" 37 #include "content/public/browser/resource_request_info.h" 38 #include "content/public/browser/web_contents.h" 39 #include "content/public/browser/web_contents_delegate.h" 40 #include "content/public/browser/worker_service.h" 41 #include "content/public/common/result_codes.h" 42 #include "extensions/browser/extension_system.h" 43 #include "third_party/icu/source/i18n/unicode/coll.h" 44 #include "ui/base/l10n/l10n_util.h" 45 #include "ui/base/resource/resource_bundle.h" 46 #include "ui/base/text/bytes_formatting.h" 47 #include "ui/gfx/image/image_skia.h" 48 #include "ui/resources/grit/ui_resources.h" 49 50 #if defined(OS_MACOSX) 51 #include "content/public/browser/browser_child_process_host.h" 52 #endif 53 54 using content::BrowserThread; 55 using content::ResourceRequestInfo; 56 using content::WebContents; 57 using task_manager::Resource; 58 using task_manager::ResourceProvider; 59 using task_manager::WebContentsInformation; 60 61 class Profile; 62 63 namespace { 64 65 template <class T> 66 int ValueCompare(T value1, T value2) { 67 if (value1 < value2) 68 return -1; 69 if (value1 == value2) 70 return 0; 71 return 1; 72 } 73 74 // Used when one or both of the results to compare are unavailable. 75 int OrderUnavailableValue(bool v1, bool v2) { 76 if (!v1 && !v2) 77 return 0; 78 return v1 ? 1 : -1; 79 } 80 81 // Used by TaskManagerModel::CompareValues(). See it for details of return 82 // value. 83 template <class T> 84 int ValueCompareMember(const TaskManagerModel* model, 85 bool (TaskManagerModel::*f)(int, T*) const, 86 int row1, 87 int row2) { 88 T value1; 89 T value2; 90 bool value1_valid = (model->*f)(row1, &value1); 91 bool value2_valid = (model->*f)(row2, &value2); 92 return value1_valid && value2_valid ? ValueCompare(value1, value2) : 93 OrderUnavailableValue(value1_valid, value2_valid); 94 } 95 96 base::string16 FormatStatsSize(const blink::WebCache::ResourceTypeStat& stat) { 97 return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_CACHE_SIZE_CELL_TEXT, 98 ui::FormatBytesWithUnits(stat.size, ui::DATA_UNITS_KIBIBYTE, false), 99 ui::FormatBytesWithUnits(stat.liveSize, ui::DATA_UNITS_KIBIBYTE, false)); 100 } 101 102 // Returns true if the specified id should use the first value in the group. 103 bool IsSharedByGroup(int col_id) { 104 switch (col_id) { 105 case IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN: 106 case IDS_TASK_MANAGER_SHARED_MEM_COLUMN: 107 case IDS_TASK_MANAGER_PHYSICAL_MEM_COLUMN: 108 case IDS_TASK_MANAGER_CPU_COLUMN: 109 case IDS_TASK_MANAGER_PROCESS_ID_COLUMN: 110 case IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN: 111 case IDS_TASK_MANAGER_VIDEO_MEMORY_COLUMN: 112 case IDS_TASK_MANAGER_SQLITE_MEMORY_USED_COLUMN: 113 case IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN: 114 case IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN: 115 case IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN: 116 case IDS_TASK_MANAGER_NACL_DEBUG_STUB_PORT_COLUMN: 117 return true; 118 default: 119 return false; 120 } 121 } 122 123 #if defined(OS_WIN) 124 void GetWinGDIHandles(base::ProcessHandle process, 125 size_t* current, 126 size_t* peak) { 127 *current = 0; 128 *peak = 0; 129 // Get a handle to |process| that has PROCESS_QUERY_INFORMATION rights. 130 HANDLE current_process = GetCurrentProcess(); 131 HANDLE process_with_query_rights; 132 if (DuplicateHandle(current_process, process, current_process, 133 &process_with_query_rights, PROCESS_QUERY_INFORMATION, 134 false, 0)) { 135 *current = GetGuiResources(process_with_query_rights, GR_GDIOBJECTS); 136 *peak = GetGuiResources(process_with_query_rights, GR_GDIOBJECTS_PEAK); 137 CloseHandle(process_with_query_rights); 138 } 139 } 140 141 void GetWinUSERHandles(base::ProcessHandle process, 142 size_t* current, 143 size_t* peak) { 144 *current = 0; 145 *peak = 0; 146 // Get a handle to |process| that has PROCESS_QUERY_INFORMATION rights. 147 HANDLE current_process = GetCurrentProcess(); 148 HANDLE process_with_query_rights; 149 if (DuplicateHandle(current_process, process, current_process, 150 &process_with_query_rights, PROCESS_QUERY_INFORMATION, 151 false, 0)) { 152 *current = GetGuiResources(process_with_query_rights, GR_USEROBJECTS); 153 *peak = GetGuiResources(process_with_query_rights, GR_USEROBJECTS_PEAK); 154 CloseHandle(process_with_query_rights); 155 } 156 } 157 #endif 158 159 } // namespace 160 161 class TaskManagerModelGpuDataManagerObserver 162 : public content::GpuDataManagerObserver { 163 public: 164 TaskManagerModelGpuDataManagerObserver() { 165 content::GpuDataManager::GetInstance()->AddObserver(this); 166 } 167 168 virtual ~TaskManagerModelGpuDataManagerObserver() { 169 content::GpuDataManager::GetInstance()->RemoveObserver(this); 170 } 171 172 static void NotifyVideoMemoryUsageStats( 173 const content::GPUVideoMemoryUsageStats& video_memory_usage_stats) { 174 TaskManager::GetInstance()->model()->NotifyVideoMemoryUsageStats( 175 video_memory_usage_stats); 176 } 177 178 virtual void OnVideoMemoryUsageStatsUpdate( 179 const content::GPUVideoMemoryUsageStats& video_memory_usage_stats) 180 OVERRIDE { 181 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { 182 NotifyVideoMemoryUsageStats(video_memory_usage_stats); 183 } else { 184 BrowserThread::PostTask( 185 BrowserThread::UI, FROM_HERE, base::Bind( 186 &TaskManagerModelGpuDataManagerObserver:: 187 NotifyVideoMemoryUsageStats, 188 video_memory_usage_stats)); 189 } 190 } 191 }; 192 193 TaskManagerModel::PerResourceValues::PerResourceValues() 194 : is_title_valid(false), 195 is_profile_name_valid(false), 196 network_usage(0), 197 is_process_id_valid(false), 198 process_id(0), 199 is_webcore_stats_valid(false), 200 is_sqlite_memory_bytes_valid(false), 201 sqlite_memory_bytes(0), 202 is_v8_memory_valid(false), 203 v8_memory_allocated(0), 204 v8_memory_used(0) {} 205 206 TaskManagerModel::PerResourceValues::~PerResourceValues() {} 207 208 TaskManagerModel::PerProcessValues::PerProcessValues() 209 : is_cpu_usage_valid(false), 210 cpu_usage(0), 211 is_idle_wakeups_valid(false), 212 idle_wakeups(0), 213 is_private_and_shared_valid(false), 214 private_bytes(0), 215 shared_bytes(0), 216 is_physical_memory_valid(false), 217 physical_memory(0), 218 is_video_memory_valid(false), 219 video_memory(0), 220 video_memory_has_duplicates(false), 221 is_gdi_handles_valid(false), 222 gdi_handles(0), 223 gdi_handles_peak(0), 224 is_user_handles_valid(0), 225 user_handles(0), 226 user_handles_peak(0), 227 is_nacl_debug_stub_port_valid(false), 228 nacl_debug_stub_port(0) {} 229 230 TaskManagerModel::PerProcessValues::~PerProcessValues() {} 231 232 //////////////////////////////////////////////////////////////////////////////// 233 // TaskManagerModel class 234 //////////////////////////////////////////////////////////////////////////////// 235 236 TaskManagerModel::TaskManagerModel(TaskManager* task_manager) 237 : pending_video_memory_usage_stats_update_(false), 238 update_requests_(0), 239 listen_requests_(0), 240 update_state_(IDLE), 241 is_updating_byte_count_(false) { 242 AddResourceProvider( 243 new task_manager::BrowserProcessResourceProvider(task_manager)); 244 AddResourceProvider(new task_manager::WebContentsResourceProvider( 245 task_manager, 246 scoped_ptr<WebContentsInformation>( 247 new task_manager::BackgroundInformation()))); 248 AddResourceProvider(new task_manager::WebContentsResourceProvider( 249 task_manager, 250 scoped_ptr<WebContentsInformation>( 251 new task_manager::TabContentsInformation()))); 252 #if defined(ENABLE_FULL_PRINTING) 253 AddResourceProvider(new task_manager::WebContentsResourceProvider( 254 task_manager, 255 scoped_ptr<WebContentsInformation>( 256 new task_manager::PrintingInformation()))); 257 #endif // ENABLE_FULL_PRINTING 258 AddResourceProvider(new task_manager::WebContentsResourceProvider( 259 task_manager, 260 scoped_ptr<WebContentsInformation>( 261 new task_manager::PanelInformation()))); 262 AddResourceProvider( 263 new task_manager::ChildProcessResourceProvider(task_manager)); 264 AddResourceProvider(new task_manager::WebContentsResourceProvider( 265 task_manager, 266 scoped_ptr<WebContentsInformation>( 267 new task_manager::ExtensionInformation()))); 268 AddResourceProvider(new task_manager::WebContentsResourceProvider( 269 task_manager, 270 scoped_ptr<WebContentsInformation>( 271 new task_manager::GuestInformation()))); 272 } 273 274 void TaskManagerModel::AddObserver(TaskManagerModelObserver* observer) { 275 observer_list_.AddObserver(observer); 276 } 277 278 void TaskManagerModel::RemoveObserver(TaskManagerModelObserver* observer) { 279 observer_list_.RemoveObserver(observer); 280 } 281 282 int TaskManagerModel::ResourceCount() const { 283 return resources_.size(); 284 } 285 286 int TaskManagerModel::GroupCount() const { 287 return group_map_.size(); 288 } 289 290 int TaskManagerModel::GetNaClDebugStubPort(int index) const { 291 base::ProcessHandle handle = GetResource(index)->GetProcess(); 292 PerProcessValues& values(per_process_cache_[handle]); 293 if (!values.is_nacl_debug_stub_port_valid) { 294 return nacl::kGdbDebugStubPortUnknown; 295 } 296 return values.nacl_debug_stub_port; 297 } 298 299 int64 TaskManagerModel::GetNetworkUsage(int index) const { 300 return GetNetworkUsage(GetResource(index)); 301 } 302 303 double TaskManagerModel::GetCPUUsage(int index) const { 304 return GetCPUUsage(GetResource(index)); 305 } 306 307 int TaskManagerModel::GetIdleWakeupsPerSecond(int index) const { 308 return GetIdleWakeupsPerSecond(GetResource(index)); 309 } 310 311 base::ProcessId TaskManagerModel::GetProcessId(int index) const { 312 PerResourceValues& values(GetPerResourceValues(index)); 313 if (!values.is_process_id_valid) { 314 values.is_process_id_valid = true; 315 values.process_id = base::GetProcId(GetResource(index)->GetProcess()); 316 } 317 return values.process_id; 318 } 319 320 base::ProcessHandle TaskManagerModel::GetProcess(int index) const { 321 return GetResource(index)->GetProcess(); 322 } 323 324 base::string16 TaskManagerModel::GetResourceById(int index, int col_id) const { 325 if (IsSharedByGroup(col_id) && !IsResourceFirstInGroup(index)) 326 return base::string16(); 327 328 switch (col_id) { 329 case IDS_TASK_MANAGER_TASK_COLUMN: 330 return GetResourceTitle(index); 331 332 case IDS_TASK_MANAGER_PROFILE_NAME_COLUMN: 333 return GetResourceProfileName(index); 334 335 case IDS_TASK_MANAGER_NET_COLUMN: 336 return GetResourceNetworkUsage(index); 337 338 case IDS_TASK_MANAGER_CPU_COLUMN: 339 return GetResourceCPUUsage(index); 340 341 case IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN: 342 return GetResourcePrivateMemory(index); 343 344 case IDS_TASK_MANAGER_SHARED_MEM_COLUMN: 345 return GetResourceSharedMemory(index); 346 347 case IDS_TASK_MANAGER_PHYSICAL_MEM_COLUMN: 348 return GetResourcePhysicalMemory(index); 349 350 case IDS_TASK_MANAGER_PROCESS_ID_COLUMN: 351 return GetResourceProcessId(index); 352 353 case IDS_TASK_MANAGER_GDI_HANDLES_COLUMN: 354 return GetResourceGDIHandles(index); 355 356 case IDS_TASK_MANAGER_USER_HANDLES_COLUMN: 357 return GetResourceUSERHandles(index); 358 359 case IDS_TASK_MANAGER_IDLE_WAKEUPS_COLUMN: 360 return GetResourceIdleWakeupsPerSecond(index); 361 362 case IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN: 363 return GetResourceWebCoreImageCacheSize(index); 364 365 case IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN: 366 return GetResourceWebCoreScriptsCacheSize(index); 367 368 case IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN: 369 return GetResourceWebCoreCSSCacheSize(index); 370 371 case IDS_TASK_MANAGER_VIDEO_MEMORY_COLUMN: 372 return GetResourceVideoMemory(index); 373 374 case IDS_TASK_MANAGER_SQLITE_MEMORY_USED_COLUMN: 375 return GetResourceSqliteMemoryUsed(index); 376 377 case IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN: 378 return GetResourceV8MemoryAllocatedSize(index); 379 380 case IDS_TASK_MANAGER_NACL_DEBUG_STUB_PORT_COLUMN: 381 return GetResourceNaClDebugStubPort(index); 382 383 default: 384 NOTREACHED(); 385 return base::string16(); 386 } 387 } 388 389 const base::string16& TaskManagerModel::GetResourceTitle(int index) const { 390 PerResourceValues& values = GetPerResourceValues(index); 391 if (!values.is_title_valid) { 392 values.is_title_valid = true; 393 values.title = GetResource(index)->GetTitle(); 394 } 395 return values.title; 396 } 397 398 const base::string16& TaskManagerModel::GetResourceProfileName( 399 int index) const { 400 PerResourceValues& values(GetPerResourceValues(index)); 401 if (!values.is_profile_name_valid) { 402 values.is_profile_name_valid = true; 403 values.profile_name = GetResource(index)->GetProfileName(); 404 } 405 return values.profile_name; 406 } 407 408 base::string16 TaskManagerModel::GetResourceNaClDebugStubPort(int index) const { 409 int port = GetNaClDebugStubPort(index); 410 if (port == nacl::kGdbDebugStubPortUnknown) { 411 return base::ASCIIToUTF16("Unknown"); 412 } else if (port == nacl::kGdbDebugStubPortUnused) { 413 return base::ASCIIToUTF16("N/A"); 414 } else { 415 return base::IntToString16(port); 416 } 417 } 418 419 base::string16 TaskManagerModel::GetResourceNetworkUsage(int index) const { 420 int64 net_usage = GetNetworkUsage(index); 421 if (net_usage == -1) 422 return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT); 423 if (net_usage == 0) 424 return base::ASCIIToUTF16("0"); 425 base::string16 net_byte = ui::FormatSpeed(net_usage); 426 // Force number string to have LTR directionality. 427 return base::i18n::GetDisplayStringInLTRDirectionality(net_byte); 428 } 429 430 base::string16 TaskManagerModel::GetResourceCPUUsage(int index) const { 431 return base::UTF8ToUTF16(base::StringPrintf( 432 #if defined(OS_MACOSX) 433 // Activity Monitor shows %cpu with one decimal digit -- be 434 // consistent with that. 435 "%.1f", 436 #else 437 "%.0f", 438 #endif 439 GetCPUUsage(GetResource(index)))); 440 } 441 442 base::string16 TaskManagerModel::GetResourcePrivateMemory(int index) const { 443 size_t private_mem; 444 if (!GetPrivateMemory(index, &private_mem)) 445 return base::ASCIIToUTF16("N/A"); 446 return GetMemCellText(private_mem); 447 } 448 449 base::string16 TaskManagerModel::GetResourceSharedMemory(int index) const { 450 size_t shared_mem; 451 if (!GetSharedMemory(index, &shared_mem)) 452 return base::ASCIIToUTF16("N/A"); 453 return GetMemCellText(shared_mem); 454 } 455 456 base::string16 TaskManagerModel::GetResourcePhysicalMemory(int index) const { 457 size_t phys_mem; 458 GetPhysicalMemory(index, &phys_mem); 459 return GetMemCellText(phys_mem); 460 } 461 462 base::string16 TaskManagerModel::GetResourceProcessId(int index) const { 463 return base::IntToString16(GetProcessId(index)); 464 } 465 466 base::string16 TaskManagerModel::GetResourceGDIHandles(int index) const { 467 size_t current, peak; 468 GetGDIHandles(index, ¤t, &peak); 469 return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_HANDLES_CELL_TEXT, 470 base::IntToString16(current), base::IntToString16(peak)); 471 } 472 473 base::string16 TaskManagerModel::GetResourceUSERHandles(int index) const { 474 size_t current, peak; 475 GetUSERHandles(index, ¤t, &peak); 476 return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_HANDLES_CELL_TEXT, 477 base::IntToString16(current), base::IntToString16(peak)); 478 } 479 480 base::string16 TaskManagerModel::GetResourceWebCoreImageCacheSize( 481 int index) const { 482 if (!CacheWebCoreStats(index)) 483 return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT); 484 return FormatStatsSize(GetPerResourceValues(index).webcore_stats.images); 485 } 486 487 base::string16 TaskManagerModel::GetResourceWebCoreScriptsCacheSize( 488 int index) const { 489 if (!CacheWebCoreStats(index)) 490 return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT); 491 return FormatStatsSize(GetPerResourceValues(index).webcore_stats.scripts); 492 } 493 494 base::string16 TaskManagerModel::GetResourceWebCoreCSSCacheSize( 495 int index) const { 496 if (!CacheWebCoreStats(index)) 497 return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT); 498 return FormatStatsSize( 499 GetPerResourceValues(index).webcore_stats.cssStyleSheets); 500 } 501 502 base::string16 TaskManagerModel::GetResourceVideoMemory(int index) const { 503 size_t video_memory; 504 bool has_duplicates; 505 if (!GetVideoMemory(index, &video_memory, &has_duplicates) || !video_memory) 506 return base::ASCIIToUTF16("N/A"); 507 if (has_duplicates) { 508 return GetMemCellText(video_memory) + base::ASCIIToUTF16("*"); 509 } 510 return GetMemCellText(video_memory); 511 } 512 513 base::string16 TaskManagerModel::GetResourceSqliteMemoryUsed(int index) const { 514 size_t bytes = 0; 515 if (!GetSqliteMemoryUsedBytes(index, &bytes)) 516 return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT); 517 return GetMemCellText(bytes); 518 } 519 520 base::string16 TaskManagerModel::GetResourceIdleWakeupsPerSecond(int index) 521 const { 522 return base::FormatNumber(GetIdleWakeupsPerSecond(GetResource(index))); 523 } 524 525 base::string16 TaskManagerModel::GetResourceV8MemoryAllocatedSize( 526 int index) const { 527 size_t memory_allocated = 0, memory_used = 0; 528 if (!GetV8MemoryUsed(index, &memory_used) || 529 !GetV8Memory(index, &memory_allocated)) 530 return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT); 531 return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_CACHE_SIZE_CELL_TEXT, 532 ui::FormatBytesWithUnits(memory_allocated, 533 ui::DATA_UNITS_KIBIBYTE, 534 false), 535 ui::FormatBytesWithUnits(memory_used, 536 ui::DATA_UNITS_KIBIBYTE, 537 false)); 538 } 539 540 bool TaskManagerModel::GetPrivateMemory(int index, size_t* result) const { 541 *result = 0; 542 base::ProcessHandle handle = GetResource(index)->GetProcess(); 543 if (!CachePrivateAndSharedMemory(handle)) 544 return false; 545 *result = per_process_cache_[handle].private_bytes; 546 return true; 547 } 548 549 bool TaskManagerModel::GetSharedMemory(int index, size_t* result) const { 550 *result = 0; 551 base::ProcessHandle handle = GetResource(index)->GetProcess(); 552 if (!CachePrivateAndSharedMemory(handle)) 553 return false; 554 *result = per_process_cache_[handle].shared_bytes; 555 return true; 556 } 557 558 bool TaskManagerModel::GetPhysicalMemory(int index, size_t* result) const { 559 *result = 0; 560 561 base::ProcessHandle handle = GetResource(index)->GetProcess(); 562 PerProcessValues& values(per_process_cache_[handle]); 563 564 if (!values.is_physical_memory_valid) { 565 base::WorkingSetKBytes ws_usage; 566 MetricsMap::const_iterator iter = metrics_map_.find(handle); 567 if (iter == metrics_map_.end() || 568 !iter->second->GetWorkingSetKBytes(&ws_usage)) 569 return false; 570 571 values.is_physical_memory_valid = true; 572 #if defined(OS_LINUX) 573 // On Linux private memory is also resident. Just use it. 574 values.physical_memory = ws_usage.priv * 1024; 575 #else 576 // Memory = working_set.private + working_set.shareable. 577 // We exclude the shared memory. 578 values.physical_memory = iter->second->GetWorkingSetSize(); 579 values.physical_memory -= ws_usage.shared * 1024; 580 #endif 581 } 582 *result = values.physical_memory; 583 return true; 584 } 585 586 void TaskManagerModel::GetGDIHandles(int index, 587 size_t* current, 588 size_t* peak) const { 589 *current = 0; 590 *peak = 0; 591 #if defined(OS_WIN) 592 base::ProcessHandle handle = GetResource(index)->GetProcess(); 593 PerProcessValues& values(per_process_cache_[handle]); 594 595 if (!values.is_gdi_handles_valid) { 596 GetWinGDIHandles(GetResource(index)->GetProcess(), 597 &values.gdi_handles, 598 &values.gdi_handles_peak); 599 values.is_gdi_handles_valid = true; 600 } 601 *current = values.gdi_handles; 602 *peak = values.gdi_handles_peak; 603 #endif 604 } 605 606 void TaskManagerModel::GetUSERHandles(int index, 607 size_t* current, 608 size_t* peak) const { 609 *current = 0; 610 *peak = 0; 611 #if defined(OS_WIN) 612 base::ProcessHandle handle = GetResource(index)->GetProcess(); 613 PerProcessValues& values(per_process_cache_[handle]); 614 615 if (!values.is_user_handles_valid) { 616 GetWinUSERHandles(GetResource(index)->GetProcess(), 617 &values.user_handles, 618 &values.user_handles_peak); 619 values.is_user_handles_valid = true; 620 } 621 *current = values.user_handles; 622 *peak = values.user_handles_peak; 623 #endif 624 } 625 626 bool TaskManagerModel::GetWebCoreCacheStats( 627 int index, 628 blink::WebCache::ResourceTypeStats* result) const { 629 if (!CacheWebCoreStats(index)) 630 return false; 631 *result = GetPerResourceValues(index).webcore_stats; 632 return true; 633 } 634 635 bool TaskManagerModel::GetVideoMemory(int index, 636 size_t* video_memory, 637 bool* has_duplicates) const { 638 *video_memory = 0; 639 *has_duplicates = false; 640 641 base::ProcessId pid = GetProcessId(index); 642 PerProcessValues& values( 643 per_process_cache_[GetResource(index)->GetProcess()]); 644 if (!values.is_video_memory_valid) { 645 content::GPUVideoMemoryUsageStats::ProcessMap::const_iterator i = 646 video_memory_usage_stats_.process_map.find(pid); 647 if (i == video_memory_usage_stats_.process_map.end()) 648 return false; 649 values.is_video_memory_valid = true; 650 values.video_memory = i->second.video_memory; 651 values.video_memory_has_duplicates = i->second.has_duplicates; 652 } 653 *video_memory = values.video_memory; 654 *has_duplicates = values.video_memory_has_duplicates; 655 return true; 656 } 657 658 bool TaskManagerModel::GetSqliteMemoryUsedBytes( 659 int index, 660 size_t* result) const { 661 *result = 0; 662 PerResourceValues& values(GetPerResourceValues(index)); 663 if (!values.is_sqlite_memory_bytes_valid) { 664 if (!GetResource(index)->ReportsSqliteMemoryUsed()) 665 return false; 666 values.is_sqlite_memory_bytes_valid = true; 667 values.sqlite_memory_bytes = GetResource(index)->SqliteMemoryUsedBytes(); 668 } 669 *result = values.sqlite_memory_bytes; 670 return true; 671 } 672 673 bool TaskManagerModel::GetV8Memory(int index, size_t* result) const { 674 *result = 0; 675 if (!CacheV8Memory(index)) 676 return false; 677 *result = GetPerResourceValues(index).v8_memory_allocated; 678 return true; 679 } 680 681 bool TaskManagerModel::GetV8MemoryUsed(int index, size_t* result) const { 682 *result = 0; 683 if (!CacheV8Memory(index)) 684 return false; 685 *result = GetPerResourceValues(index).v8_memory_used; 686 return true; 687 } 688 689 bool TaskManagerModel::CanActivate(int index) const { 690 CHECK_LT(index, ResourceCount()); 691 return GetResourceWebContents(index) != NULL; 692 } 693 694 bool TaskManagerModel::IsResourceFirstInGroup(int index) const { 695 Resource* resource = GetResource(index); 696 GroupMap::const_iterator iter = group_map_.find(resource->GetProcess()); 697 DCHECK(iter != group_map_.end()); 698 const ResourceList& group = iter->second; 699 return (group[0] == resource); 700 } 701 702 bool TaskManagerModel::IsResourceLastInGroup(int index) const { 703 Resource* resource = GetResource(index); 704 GroupMap::const_iterator iter = group_map_.find(resource->GetProcess()); 705 DCHECK(iter != group_map_.end()); 706 const ResourceList& group = iter->second; 707 return (group.back() == resource); 708 } 709 710 gfx::ImageSkia TaskManagerModel::GetResourceIcon(int index) const { 711 gfx::ImageSkia icon = GetResource(index)->GetIcon(); 712 if (!icon.isNull()) 713 return icon; 714 715 static const gfx::ImageSkia* default_icon = 716 ResourceBundle::GetSharedInstance(). 717 GetNativeImageNamed(IDR_DEFAULT_FAVICON).ToImageSkia(); 718 return *default_icon; 719 } 720 721 TaskManagerModel::GroupRange 722 TaskManagerModel::GetGroupRangeForResource(int index) const { 723 Resource* resource = GetResource(index); 724 GroupMap::const_iterator group_iter = 725 group_map_.find(resource->GetProcess()); 726 DCHECK(group_iter != group_map_.end()); 727 const ResourceList& group = group_iter->second; 728 if (group.size() == 1) { 729 return std::make_pair(index, 1); 730 } else { 731 for (int i = index; i >= 0; --i) { 732 if (GetResource(i) == group[0]) 733 return std::make_pair(i, group.size()); 734 } 735 NOTREACHED(); 736 return std::make_pair(-1, -1); 737 } 738 } 739 740 int TaskManagerModel::GetGroupIndexForResource(int index) const { 741 int group_index = -1; 742 for (int i = 0; i <= index; ++i) { 743 if (IsResourceFirstInGroup(i)) 744 group_index++; 745 } 746 747 DCHECK_NE(group_index, -1); 748 return group_index; 749 } 750 751 int TaskManagerModel::GetResourceIndexForGroup(int group_index, 752 int index_in_group) const { 753 int group_count = -1; 754 int count_in_group = -1; 755 for (int i = 0; i < ResourceCount(); ++i) { 756 if (IsResourceFirstInGroup(i)) 757 group_count++; 758 759 if (group_count == group_index) { 760 count_in_group++; 761 if (count_in_group == index_in_group) 762 return i; 763 } else if (group_count > group_index) { 764 break; 765 } 766 } 767 768 NOTREACHED(); 769 return -1; 770 } 771 772 int TaskManagerModel::CompareValues(int row1, int row2, int col_id) const { 773 CHECK(row1 < ResourceCount() && row2 < ResourceCount()); 774 switch (col_id) { 775 case IDS_TASK_MANAGER_TASK_COLUMN: { 776 static icu::Collator* collator = NULL; 777 if (!collator) { 778 UErrorCode create_status = U_ZERO_ERROR; 779 collator = icu::Collator::createInstance(create_status); 780 if (!U_SUCCESS(create_status)) { 781 collator = NULL; 782 NOTREACHED(); 783 } 784 } 785 const base::string16& title1 = GetResourceTitle(row1); 786 const base::string16& title2 = GetResourceTitle(row2); 787 UErrorCode compare_status = U_ZERO_ERROR; 788 UCollationResult compare_result = collator->compare( 789 static_cast<const UChar*>(title1.c_str()), 790 static_cast<int>(title1.length()), 791 static_cast<const UChar*>(title2.c_str()), 792 static_cast<int>(title2.length()), 793 compare_status); 794 DCHECK(U_SUCCESS(compare_status)); 795 return compare_result; 796 } 797 798 case IDS_TASK_MANAGER_PROFILE_NAME_COLUMN: { 799 const base::string16& profile1 = GetResourceProfileName(row1); 800 const base::string16& profile2 = GetResourceProfileName(row2); 801 return profile1.compare(0, profile1.length(), profile2, 0, 802 profile2.length()); 803 } 804 805 case IDS_TASK_MANAGER_NET_COLUMN: 806 return ValueCompare(GetNetworkUsage(GetResource(row1)), 807 GetNetworkUsage(GetResource(row2))); 808 809 case IDS_TASK_MANAGER_CPU_COLUMN: 810 return ValueCompare(GetCPUUsage(GetResource(row1)), 811 GetCPUUsage(GetResource(row2))); 812 813 case IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN: 814 return ValueCompareMember( 815 this, &TaskManagerModel::GetPrivateMemory, row1, row2); 816 817 case IDS_TASK_MANAGER_SHARED_MEM_COLUMN: 818 return ValueCompareMember( 819 this, &TaskManagerModel::GetSharedMemory, row1, row2); 820 821 case IDS_TASK_MANAGER_PHYSICAL_MEM_COLUMN: 822 return ValueCompareMember( 823 this, &TaskManagerModel::GetPhysicalMemory, row1, row2); 824 825 case IDS_TASK_MANAGER_NACL_DEBUG_STUB_PORT_COLUMN: 826 return ValueCompare(GetNaClDebugStubPort(row1), 827 GetNaClDebugStubPort(row2)); 828 829 case IDS_TASK_MANAGER_PROCESS_ID_COLUMN: 830 return ValueCompare(GetProcessId(row1), GetProcessId(row2)); 831 832 case IDS_TASK_MANAGER_GDI_HANDLES_COLUMN: { 833 size_t current1, peak1; 834 size_t current2, peak2; 835 GetGDIHandles(row1, ¤t1, &peak1); 836 GetGDIHandles(row2, ¤t2, &peak2); 837 return ValueCompare(current1, current2); 838 } 839 840 case IDS_TASK_MANAGER_USER_HANDLES_COLUMN: { 841 size_t current1, peak1; 842 size_t current2, peak2; 843 GetUSERHandles(row1, ¤t1, &peak1); 844 GetUSERHandles(row2, ¤t2, &peak2); 845 return ValueCompare(current1, current2); 846 } 847 848 case IDS_TASK_MANAGER_IDLE_WAKEUPS_COLUMN: 849 return ValueCompare(GetIdleWakeupsPerSecond(row1), 850 GetIdleWakeupsPerSecond(row2)); 851 852 case IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN: 853 case IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN: 854 case IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN: { 855 bool row1_stats_valid = CacheWebCoreStats(row1); 856 bool row2_stats_valid = CacheWebCoreStats(row2); 857 if (row1_stats_valid && row2_stats_valid) { 858 const blink::WebCache::ResourceTypeStats& stats1( 859 GetPerResourceValues(row1).webcore_stats); 860 const blink::WebCache::ResourceTypeStats& stats2( 861 GetPerResourceValues(row2).webcore_stats); 862 switch (col_id) { 863 case IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN: 864 return ValueCompare(stats1.images.size, stats2.images.size); 865 case IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN: 866 return ValueCompare(stats1.scripts.size, stats2.scripts.size); 867 case IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN: 868 return ValueCompare(stats1.cssStyleSheets.size, 869 stats2.cssStyleSheets.size); 870 default: 871 NOTREACHED(); 872 return 0; 873 } 874 } 875 return OrderUnavailableValue(row1_stats_valid, row2_stats_valid); 876 } 877 878 case IDS_TASK_MANAGER_VIDEO_MEMORY_COLUMN: { 879 size_t value1; 880 size_t value2; 881 bool has_duplicates; 882 bool value1_valid = GetVideoMemory(row1, &value1, &has_duplicates); 883 bool value2_valid = GetVideoMemory(row2, &value2, &has_duplicates); 884 return value1_valid && value2_valid ? ValueCompare(value1, value2) : 885 OrderUnavailableValue(value1_valid, value2_valid); 886 } 887 888 case IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN: 889 return ValueCompareMember( 890 this, &TaskManagerModel::GetV8Memory, row1, row2); 891 892 case IDS_TASK_MANAGER_SQLITE_MEMORY_USED_COLUMN: 893 return ValueCompareMember( 894 this, &TaskManagerModel::GetSqliteMemoryUsedBytes, row1, row2); 895 896 default: 897 NOTREACHED(); 898 break; 899 } 900 return 0; 901 } 902 903 int TaskManagerModel::GetUniqueChildProcessId(int index) const { 904 return GetResource(index)->GetUniqueChildProcessId(); 905 } 906 907 Resource::Type TaskManagerModel::GetResourceType(int index) const { 908 return GetResource(index)->GetType(); 909 } 910 911 WebContents* TaskManagerModel::GetResourceWebContents(int index) const { 912 return GetResource(index)->GetWebContents(); 913 } 914 915 void TaskManagerModel::AddResource(Resource* resource) { 916 base::ProcessHandle process = resource->GetProcess(); 917 918 GroupMap::iterator group_iter = group_map_.find(process); 919 int new_entry_index = 0; 920 if (group_iter == group_map_.end()) { 921 group_map_.insert(make_pair(process, ResourceList(1, resource))); 922 923 // Not part of a group, just put at the end of the list. 924 resources_.push_back(resource); 925 new_entry_index = static_cast<int>(resources_.size() - 1); 926 } else { 927 ResourceList* group_entries = &(group_iter->second); 928 group_entries->push_back(resource); 929 930 // Insert the new entry right after the last entry of its group. 931 ResourceList::iterator iter = 932 std::find(resources_.begin(), 933 resources_.end(), 934 (*group_entries)[group_entries->size() - 2]); 935 DCHECK(iter != resources_.end()); 936 new_entry_index = static_cast<int>(iter - resources_.begin()) + 1; 937 resources_.insert(++iter, resource); 938 } 939 940 // Create the ProcessMetrics for this process if needed (not in map). 941 if (metrics_map_.find(process) == metrics_map_.end()) { 942 base::ProcessMetrics* pm = 943 #if !defined(OS_MACOSX) 944 base::ProcessMetrics::CreateProcessMetrics(process); 945 #else 946 base::ProcessMetrics::CreateProcessMetrics( 947 process, content::BrowserChildProcessHost::GetPortProvider()); 948 #endif 949 950 metrics_map_[process] = pm; 951 } 952 953 // Notify the table that the contents have changed for it to redraw. 954 FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_, 955 OnItemsAdded(new_entry_index, 1)); 956 } 957 958 void TaskManagerModel::RemoveResource(Resource* resource) { 959 base::ProcessHandle process = resource->GetProcess(); 960 961 // Find the associated group. 962 GroupMap::iterator group_iter = group_map_.find(process); 963 DCHECK(group_iter != group_map_.end()); 964 if (group_iter == group_map_.end()) 965 return; 966 ResourceList& group_entries = group_iter->second; 967 968 // Remove the entry from the group map. 969 ResourceList::iterator iter = std::find(group_entries.begin(), 970 group_entries.end(), 971 resource); 972 DCHECK(iter != group_entries.end()); 973 if (iter != group_entries.end()) 974 group_entries.erase(iter); 975 976 // If there are no more entries for that process, do the clean-up. 977 if (group_entries.empty()) { 978 group_map_.erase(group_iter); 979 980 // Nobody is using this process, we don't need the process metrics anymore. 981 MetricsMap::iterator pm_iter = metrics_map_.find(process); 982 DCHECK(pm_iter != metrics_map_.end()); 983 if (pm_iter != metrics_map_.end()) { 984 delete pm_iter->second; 985 metrics_map_.erase(process); 986 } 987 } 988 989 // Remove the entry from the model list. 990 iter = std::find(resources_.begin(), resources_.end(), resource); 991 DCHECK(iter != resources_.end()); 992 if (iter != resources_.end()) { 993 int index = static_cast<int>(iter - resources_.begin()); 994 // Notify the observers that the contents will change. 995 FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_, 996 OnItemsToBeRemoved(index, 1)); 997 // Now actually remove the entry from the model list. 998 resources_.erase(iter); 999 // Notify the table that the contents have changed. 1000 FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_, 1001 OnItemsRemoved(index, 1)); 1002 } 1003 1004 // Remove the entry from the network maps. 1005 ResourceValueMap::iterator net_iter = 1006 current_byte_count_map_.find(resource); 1007 if (net_iter != current_byte_count_map_.end()) 1008 current_byte_count_map_.erase(net_iter); 1009 } 1010 1011 void TaskManagerModel::StartUpdating() { 1012 // Multiple StartUpdating requests may come in, and we only need to take 1013 // action the first time. 1014 update_requests_++; 1015 if (update_requests_ > 1) 1016 return; 1017 DCHECK_EQ(1, update_requests_); 1018 DCHECK_NE(TASK_PENDING, update_state_); 1019 1020 // If update_state_ is STOPPING, it means a task is still pending. Setting 1021 // it to TASK_PENDING ensures the tasks keep being posted (by Refresh()). 1022 if (update_state_ == IDLE) { 1023 base::MessageLoop::current()->PostTask( 1024 FROM_HERE, 1025 base::Bind(&TaskManagerModel::RefreshCallback, this)); 1026 } 1027 update_state_ = TASK_PENDING; 1028 1029 // Notify resource providers that we are updating. 1030 StartListening(); 1031 1032 if (!resources_.empty()) { 1033 FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_, 1034 OnReadyPeriodicalUpdate()); 1035 } 1036 1037 BrowserThread::PostTask( 1038 BrowserThread::IO, FROM_HERE, 1039 base::Bind(&TaskManagerModel::SetUpdatingByteCount, this, true)); 1040 } 1041 1042 void TaskManagerModel::StopUpdating() { 1043 // Don't actually stop updating until we have heard as many calls as those 1044 // to StartUpdating. 1045 update_requests_--; 1046 if (update_requests_ > 0) 1047 return; 1048 // Make sure that update_requests_ cannot go negative. 1049 CHECK_EQ(0, update_requests_); 1050 DCHECK_EQ(TASK_PENDING, update_state_); 1051 update_state_ = STOPPING; 1052 1053 // Notify resource providers that we are done updating. 1054 StopListening(); 1055 1056 BrowserThread::PostTask( 1057 BrowserThread::IO, FROM_HERE, 1058 base::Bind(&TaskManagerModel::SetUpdatingByteCount, this, false)); 1059 } 1060 1061 void TaskManagerModel::StartListening() { 1062 // Multiple StartListening requests may come in and we only need to take 1063 // action the first time. 1064 listen_requests_++; 1065 if (listen_requests_ > 1) 1066 return; 1067 DCHECK_EQ(1, listen_requests_); 1068 1069 // Notify resource providers that we should start listening to events. 1070 for (ResourceProviderList::iterator iter = providers_.begin(); 1071 iter != providers_.end(); ++iter) { 1072 (*iter)->StartUpdating(); 1073 } 1074 } 1075 1076 void TaskManagerModel::StopListening() { 1077 // Don't actually stop listening until we have heard as many calls as those 1078 // to StartListening. 1079 listen_requests_--; 1080 if (listen_requests_ > 0) 1081 return; 1082 1083 DCHECK_EQ(0, listen_requests_); 1084 1085 // Notify resource providers that we are done listening. 1086 for (ResourceProviderList::const_iterator iter = providers_.begin(); 1087 iter != providers_.end(); ++iter) { 1088 (*iter)->StopUpdating(); 1089 } 1090 1091 // Must clear the resources before the next attempt to start listening. 1092 Clear(); 1093 } 1094 1095 void TaskManagerModel::Clear() { 1096 int size = ResourceCount(); 1097 if (size > 0) { 1098 resources_.clear(); 1099 1100 // Clear the groups. 1101 group_map_.clear(); 1102 1103 // Clear the process related info. 1104 STLDeleteValues(&metrics_map_); 1105 1106 // Clear the network maps. 1107 current_byte_count_map_.clear(); 1108 1109 per_resource_cache_.clear(); 1110 per_process_cache_.clear(); 1111 1112 FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_, 1113 OnItemsRemoved(0, size)); 1114 } 1115 } 1116 1117 void TaskManagerModel::ModelChanged() { 1118 // Notify the table that the contents have changed for it to redraw. 1119 FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_, OnModelChanged()); 1120 } 1121 1122 void TaskManagerModel::Refresh() { 1123 per_resource_cache_.clear(); 1124 per_process_cache_.clear(); 1125 1126 #if !defined(DISABLE_NACL) 1127 nacl::NaClBrowser* nacl_browser = nacl::NaClBrowser::GetInstance(); 1128 #endif // !defined(DISABLE_NACL) 1129 1130 // Compute the CPU usage values and check if NaCl GDB debug stub port is 1131 // known. 1132 // Note that we compute the CPU usage for all resources (instead of doing it 1133 // lazily) as process_util::GetCPUUsage() returns the CPU usage since the last 1134 // time it was called, and not calling it everytime would skew the value the 1135 // next time it is retrieved (as it would be for more than 1 cycle). 1136 // The same is true for idle wakeups. 1137 for (ResourceList::iterator iter = resources_.begin(); 1138 iter != resources_.end(); ++iter) { 1139 base::ProcessHandle process = (*iter)->GetProcess(); 1140 PerProcessValues& values(per_process_cache_[process]); 1141 #if !defined(DISABLE_NACL) 1142 // Debug stub port doesn't change once known. 1143 if (!values.is_nacl_debug_stub_port_valid) { 1144 values.nacl_debug_stub_port = nacl_browser->GetProcessGdbDebugStubPort( 1145 (*iter)->GetUniqueChildProcessId()); 1146 if (values.nacl_debug_stub_port != nacl::kGdbDebugStubPortUnknown) { 1147 values.is_nacl_debug_stub_port_valid = true; 1148 } 1149 } 1150 #endif // !defined(DISABLE_NACL) 1151 if (values.is_cpu_usage_valid && values.is_idle_wakeups_valid) 1152 continue; 1153 MetricsMap::iterator metrics_iter = metrics_map_.find(process); 1154 DCHECK(metrics_iter != metrics_map_.end()); 1155 if (!values.is_cpu_usage_valid) { 1156 values.is_cpu_usage_valid = true; 1157 values.cpu_usage = metrics_iter->second->GetCPUUsage(); 1158 } 1159 #if defined(OS_MACOSX) || defined(OS_LINUX) 1160 // TODO(port): Implement GetIdleWakeupsPerSecond() on other platforms, 1161 // crbug.com/120488 1162 if (!values.is_idle_wakeups_valid) { 1163 values.is_idle_wakeups_valid = true; 1164 values.idle_wakeups = metrics_iter->second->GetIdleWakeupsPerSecond(); 1165 } 1166 #endif // defined(OS_MACOSX) || defined(OS_LINUX) 1167 } 1168 1169 // Send a request to refresh GPU memory consumption values 1170 RefreshVideoMemoryUsageStats(); 1171 1172 // Compute the new network usage values. 1173 base::TimeDelta update_time = 1174 base::TimeDelta::FromMilliseconds(kUpdateTimeMs); 1175 for (ResourceValueMap::iterator iter = current_byte_count_map_.begin(); 1176 iter != current_byte_count_map_.end(); ++iter) { 1177 PerResourceValues* values = &(per_resource_cache_[iter->first]); 1178 if (update_time > base::TimeDelta::FromSeconds(1)) 1179 values->network_usage = iter->second / update_time.InSeconds(); 1180 else 1181 values->network_usage = iter->second * (1 / update_time.InSeconds()); 1182 1183 // Then we reset the current byte count. 1184 iter->second = 0; 1185 } 1186 1187 // Let resources update themselves if they need to. 1188 for (ResourceList::iterator iter = resources_.begin(); 1189 iter != resources_.end(); ++iter) { 1190 (*iter)->Refresh(); 1191 } 1192 1193 if (!resources_.empty()) { 1194 FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_, 1195 OnItemsChanged(0, ResourceCount())); 1196 } 1197 } 1198 1199 void TaskManagerModel::NotifyResourceTypeStats( 1200 base::ProcessId renderer_id, 1201 const blink::WebCache::ResourceTypeStats& stats) { 1202 for (ResourceList::iterator it = resources_.begin(); 1203 it != resources_.end(); ++it) { 1204 if (base::GetProcId((*it)->GetProcess()) == renderer_id) { 1205 (*it)->NotifyResourceTypeStats(stats); 1206 } 1207 } 1208 } 1209 1210 void TaskManagerModel::NotifyVideoMemoryUsageStats( 1211 const content::GPUVideoMemoryUsageStats& video_memory_usage_stats) { 1212 DCHECK(pending_video_memory_usage_stats_update_); 1213 video_memory_usage_stats_ = video_memory_usage_stats; 1214 pending_video_memory_usage_stats_update_ = false; 1215 } 1216 1217 void TaskManagerModel::NotifyV8HeapStats(base::ProcessId renderer_id, 1218 size_t v8_memory_allocated, 1219 size_t v8_memory_used) { 1220 for (ResourceList::iterator it = resources_.begin(); 1221 it != resources_.end(); ++it) { 1222 if (base::GetProcId((*it)->GetProcess()) == renderer_id) { 1223 (*it)->NotifyV8HeapStats(v8_memory_allocated, v8_memory_used); 1224 } 1225 } 1226 } 1227 1228 void TaskManagerModel::NotifyBytesRead(const net::URLRequest& request, 1229 int byte_count) { 1230 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 1231 if (!is_updating_byte_count_) 1232 return; 1233 1234 // Only net::URLRequestJob instances created by the ResourceDispatcherHost 1235 // have an associated ResourceRequestInfo and a render frame associated. 1236 // All other jobs will have -1 returned for the render process child and 1237 // routing ids - the jobs may still match a resource based on their origin id, 1238 // otherwise BytesRead() will attribute the activity to the Browser resource. 1239 const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(&request); 1240 int child_id = -1, route_id = -1; 1241 if (info) 1242 info->GetAssociatedRenderFrame(&child_id, &route_id); 1243 1244 // Get the origin PID of the request's originator. This will only be set for 1245 // plugins - for renderer or browser initiated requests it will be zero. 1246 int origin_pid = 0; 1247 if (info) 1248 origin_pid = info->GetOriginPID(); 1249 1250 if (bytes_read_buffer_.empty()) { 1251 base::MessageLoop::current()->PostDelayedTask( 1252 FROM_HERE, 1253 base::Bind(&TaskManagerModel::NotifyMultipleBytesRead, this), 1254 base::TimeDelta::FromSeconds(1)); 1255 } 1256 1257 bytes_read_buffer_.push_back( 1258 BytesReadParam(origin_pid, child_id, route_id, byte_count)); 1259 } 1260 1261 // This is called on the UI thread. 1262 void TaskManagerModel::NotifyDataReady() { 1263 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1264 for (size_t i = 0; i < on_data_ready_callbacks_.size(); ++i) { 1265 if (!on_data_ready_callbacks_[i].is_null()) 1266 on_data_ready_callbacks_[i].Run(); 1267 } 1268 1269 on_data_ready_callbacks_.clear(); 1270 } 1271 1272 void TaskManagerModel::RegisterOnDataReadyCallback( 1273 const base::Closure& callback) { 1274 on_data_ready_callbacks_.push_back(callback); 1275 } 1276 1277 TaskManagerModel::~TaskManagerModel() { 1278 on_data_ready_callbacks_.clear(); 1279 } 1280 1281 void TaskManagerModel::RefreshCallback() { 1282 DCHECK_NE(IDLE, update_state_); 1283 1284 if (update_state_ == STOPPING) { 1285 // We have been asked to stop. 1286 update_state_ = IDLE; 1287 return; 1288 } 1289 1290 Refresh(); 1291 1292 // Schedule the next update. 1293 base::MessageLoop::current()->PostDelayedTask( 1294 FROM_HERE, 1295 base::Bind(&TaskManagerModel::RefreshCallback, this), 1296 base::TimeDelta::FromMilliseconds(kUpdateTimeMs)); 1297 } 1298 1299 void TaskManagerModel::RefreshVideoMemoryUsageStats() { 1300 if (pending_video_memory_usage_stats_update_) 1301 return; 1302 1303 if (!video_memory_usage_stats_observer_.get()) { 1304 video_memory_usage_stats_observer_.reset( 1305 new TaskManagerModelGpuDataManagerObserver()); 1306 } 1307 pending_video_memory_usage_stats_update_ = true; 1308 content::GpuDataManager::GetInstance()->RequestVideoMemoryUsageStatsUpdate(); 1309 } 1310 1311 int64 TaskManagerModel::GetNetworkUsageForResource(Resource* resource) const { 1312 // Returns default of 0 if no network usage. 1313 return per_resource_cache_[resource].network_usage; 1314 } 1315 1316 void TaskManagerModel::BytesRead(BytesReadParam param) { 1317 if (update_state_ != TASK_PENDING || listen_requests_ == 0) { 1318 // A notification sneaked in while we were stopping the updating, just 1319 // ignore it. 1320 return; 1321 } 1322 1323 // TODO(jcampan): this should be improved once we have a better way of 1324 // linking a network notification back to the object that initiated it. 1325 Resource* resource = NULL; 1326 for (ResourceProviderList::iterator iter = providers_.begin(); 1327 iter != providers_.end(); ++iter) { 1328 resource = (*iter)->GetResource(param.origin_pid, 1329 param.child_id, 1330 param.route_id); 1331 if (resource) 1332 break; 1333 } 1334 1335 if (resource == NULL) { 1336 // We can't match a resource to the notification. That might mean the 1337 // tab that started a download was closed, or the request may have had 1338 // no originating resource associated with it in the first place. 1339 // We attribute orphaned/unaccounted activity to the Browser process. 1340 CHECK(param.origin_pid || (param.child_id != -1)); 1341 param.origin_pid = 0; 1342 param.child_id = param.route_id = -1; 1343 BytesRead(param); 1344 return; 1345 } 1346 1347 // We do support network usage, mark the resource as such so it can report 0 1348 // instead of N/A. 1349 if (!resource->SupportNetworkUsage()) 1350 resource->SetSupportNetworkUsage(); 1351 1352 ResourceValueMap::const_iterator iter_res = 1353 current_byte_count_map_.find(resource); 1354 if (iter_res == current_byte_count_map_.end()) 1355 current_byte_count_map_[resource] = param.byte_count; 1356 else 1357 current_byte_count_map_[resource] = iter_res->second + param.byte_count; 1358 } 1359 1360 void TaskManagerModel::MultipleBytesRead( 1361 const std::vector<BytesReadParam>* params) { 1362 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1363 for (std::vector<BytesReadParam>::const_iterator it = params->begin(); 1364 it != params->end(); ++it) { 1365 BytesRead(*it); 1366 } 1367 } 1368 1369 void TaskManagerModel::NotifyMultipleBytesRead() { 1370 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 1371 DCHECK(!bytes_read_buffer_.empty()); 1372 1373 std::vector<BytesReadParam>* bytes_read_buffer = 1374 new std::vector<BytesReadParam>; 1375 bytes_read_buffer_.swap(*bytes_read_buffer); 1376 BrowserThread::PostTask( 1377 BrowserThread::UI, FROM_HERE, 1378 base::Bind(&TaskManagerModel::MultipleBytesRead, this, 1379 base::Owned(bytes_read_buffer))); 1380 } 1381 1382 void TaskManagerModel::SetUpdatingByteCount(bool is_updating) { 1383 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 1384 is_updating_byte_count_ = is_updating; 1385 } 1386 1387 int64 TaskManagerModel::GetNetworkUsage(Resource* resource) const { 1388 int64 net_usage = GetNetworkUsageForResource(resource); 1389 if (net_usage == 0 && !resource->SupportNetworkUsage()) 1390 return -1; 1391 return net_usage; 1392 } 1393 1394 double TaskManagerModel::GetCPUUsage(Resource* resource) const { 1395 const PerProcessValues& values(per_process_cache_[resource->GetProcess()]); 1396 // Returns 0 if not valid, which is fine. 1397 return values.cpu_usage; 1398 } 1399 1400 int TaskManagerModel::GetIdleWakeupsPerSecond(Resource* resource) const { 1401 const PerProcessValues& values(per_process_cache_[resource->GetProcess()]); 1402 // Returns 0 if not valid, which is fine. 1403 return values.idle_wakeups; 1404 } 1405 1406 base::string16 TaskManagerModel::GetMemCellText(int64 number) const { 1407 #if !defined(OS_MACOSX) 1408 base::string16 str = base::FormatNumber(number / 1024); 1409 1410 // Adjust number string if necessary. 1411 base::i18n::AdjustStringForLocaleDirection(&str); 1412 return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_MEM_CELL_TEXT, str); 1413 #else 1414 // System expectation is to show "100 kB", "200 MB", etc. 1415 // TODO(thakis): Switch to metric units (as opposed to powers of two). 1416 return ui::FormatBytes(number); 1417 #endif 1418 } 1419 1420 bool TaskManagerModel::CachePrivateAndSharedMemory( 1421 base::ProcessHandle handle) const { 1422 PerProcessValues& values(per_process_cache_[handle]); 1423 if (values.is_private_and_shared_valid) 1424 return true; 1425 1426 MetricsMap::const_iterator iter = metrics_map_.find(handle); 1427 if (iter == metrics_map_.end() || 1428 !iter->second->GetMemoryBytes(&values.private_bytes, 1429 &values.shared_bytes)) { 1430 return false; 1431 } 1432 1433 values.is_private_and_shared_valid = true; 1434 return true; 1435 } 1436 1437 bool TaskManagerModel::CacheWebCoreStats(int index) const { 1438 PerResourceValues& values(GetPerResourceValues(index)); 1439 if (!values.is_webcore_stats_valid) { 1440 if (!GetResource(index)->ReportsCacheStats()) 1441 return false; 1442 values.is_webcore_stats_valid = true; 1443 values.webcore_stats = GetResource(index)->GetWebCoreCacheStats(); 1444 } 1445 return true; 1446 } 1447 1448 bool TaskManagerModel::CacheV8Memory(int index) const { 1449 PerResourceValues& values(GetPerResourceValues(index)); 1450 if (!values.is_v8_memory_valid) { 1451 if (!GetResource(index)->ReportsV8MemoryStats()) 1452 return false; 1453 values.is_v8_memory_valid = true; 1454 values.v8_memory_allocated = GetResource(index)->GetV8MemoryAllocated(); 1455 values.v8_memory_used = GetResource(index)->GetV8MemoryUsed(); 1456 } 1457 return true; 1458 } 1459 1460 void TaskManagerModel::AddResourceProvider(ResourceProvider* provider) { 1461 DCHECK(provider); 1462 providers_.push_back(provider); 1463 } 1464 1465 TaskManagerModel::PerResourceValues& TaskManagerModel::GetPerResourceValues( 1466 int index) const { 1467 return per_resource_cache_[GetResource(index)]; 1468 } 1469 1470 Resource* TaskManagerModel::GetResource(int index) const { 1471 CHECK_GE(index, 0); 1472 CHECK_LT(index, static_cast<int>(resources_.size())); 1473 return resources_[index]; 1474 } 1475 1476 //////////////////////////////////////////////////////////////////////////////// 1477 // TaskManager class 1478 //////////////////////////////////////////////////////////////////////////////// 1479 // static 1480 void TaskManager::RegisterPrefs(PrefRegistrySimple* registry) { 1481 registry->RegisterDictionaryPref(prefs::kTaskManagerWindowPlacement); 1482 } 1483 1484 bool TaskManager::IsBrowserProcess(int index) const { 1485 // If some of the selection is out of bounds, ignore. This may happen when 1486 // killing a process that manages several pages. 1487 return index < model_->ResourceCount() && 1488 model_->GetProcess(index) == base::GetCurrentProcessHandle(); 1489 } 1490 1491 void TaskManager::KillProcess(int index) { 1492 base::ProcessHandle process = model_->GetProcess(index); 1493 DCHECK(process); 1494 if (process != base::GetCurrentProcessHandle()) 1495 base::KillProcess(process, content::RESULT_CODE_KILLED, false); 1496 } 1497 1498 void TaskManager::ActivateProcess(int index) { 1499 // GetResourceWebContents returns a pointer to the relevant web contents for 1500 // the resource. If the index doesn't correspond to any web contents 1501 // (i.e. refers to the Browser process or a plugin), GetWebContents will 1502 // return NULL. 1503 WebContents* chosen_web_contents = model_->GetResourceWebContents(index); 1504 if (chosen_web_contents && chosen_web_contents->GetDelegate()) 1505 chosen_web_contents->GetDelegate()->ActivateContents(chosen_web_contents); 1506 } 1507 1508 void TaskManager::AddResource(Resource* resource) { 1509 model_->AddResource(resource); 1510 } 1511 1512 void TaskManager::RemoveResource(Resource* resource) { 1513 model_->RemoveResource(resource); 1514 } 1515 1516 void TaskManager::OnWindowClosed() { 1517 model_->StopUpdating(); 1518 } 1519 1520 void TaskManager::ModelChanged() { 1521 model_->ModelChanged(); 1522 } 1523 1524 // static 1525 TaskManager* TaskManager::GetInstance() { 1526 return Singleton<TaskManager>::get(); 1527 } 1528 1529 void TaskManager::OpenAboutMemory(chrome::HostDesktopType desktop_type) { 1530 chrome::NavigateParams params( 1531 ProfileManager::GetLastUsedProfileAllowedByPolicy(), 1532 GURL(chrome::kChromeUIMemoryURL), 1533 ui::PAGE_TRANSITION_LINK); 1534 params.disposition = NEW_FOREGROUND_TAB; 1535 params.host_desktop_type = desktop_type; 1536 chrome::Navigate(¶ms); 1537 } 1538 1539 TaskManager::TaskManager() 1540 : model_(new TaskManagerModel(this)) { 1541 } 1542 1543 TaskManager::~TaskManager() { 1544 } 1545