• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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