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