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