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
43namespace {
44
45// The delay between updates of the information (in ms).
46#if defined(OS_MACOSX)
47// Match Activity Monitor's default refresh rate.
48const int kUpdateTimeMs = 2000;
49#else
50const int kUpdateTimeMs = 1000;
51#endif
52
53template <class T>
54int ValueCompare(T value1, T value2) {
55  if (value1 < value2)
56    return -1;
57  if (value1 == value2)
58    return 0;
59  return 1;
60}
61
62string16 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
74TaskManagerModel::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
92TaskManagerModel::~TaskManagerModel() {
93  for (ResourceProviderList::iterator iter = providers_.begin();
94       iter != providers_.end(); ++iter) {
95    (*iter)->Release();
96  }
97}
98
99int TaskManagerModel::ResourceCount() const {
100  return resources_.size();
101}
102
103void TaskManagerModel::AddObserver(TaskManagerModelObserver* observer) {
104  observer_list_.AddObserver(observer);
105}
106
107void TaskManagerModel::RemoveObserver(TaskManagerModelObserver* observer) {
108  observer_list_.RemoveObserver(observer);
109}
110
111string16 TaskManagerModel::GetResourceTitle(int index) const {
112  CHECK_LT(index, ResourceCount());
113  return resources_[index]->GetTitle();
114}
115
116int64 TaskManagerModel::GetNetworkUsage(int index) const {
117  CHECK_LT(index, ResourceCount());
118  return GetNetworkUsage(resources_[index]);
119}
120
121string16 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
133double TaskManagerModel::GetCPUUsage(int index) const {
134  CHECK_LT(index, ResourceCount());
135  return GetCPUUsage(resources_[index]);
136}
137
138string16 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
151string16 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
158string16 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
165string16 TaskManagerModel::GetResourcePhysicalMemory(int index) const {
166  size_t phys_mem;
167  GetPhysicalMemory(index, &phys_mem);
168  return GetMemCellText(phys_mem);
169}
170
171int TaskManagerModel::GetProcessId(int index) const {
172  CHECK_LT(index, ResourceCount());
173  return base::GetProcId(resources_[index]->GetProcess());
174}
175
176string16 TaskManagerModel::GetResourceProcessId(int index) const {
177  return base::IntToString16(GetProcessId(index));
178}
179
180string16 TaskManagerModel::GetResourceGoatsTeleported(int index) const {
181  CHECK_LT(index, ResourceCount());
182  return base::FormatNumber(GetGoatsTeleported(index));
183}
184
185string16 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
195string16 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
205string16 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
215string16 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
222string16 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
235bool 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
244bool TaskManagerModel::IsBackgroundResource(int index) const {
245  CHECK_LT(index, ResourceCount());
246  return resources_[index]->IsBackground();
247}
248
249SkBitmap 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
260std::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
281int 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
368base::ProcessHandle TaskManagerModel::GetResourceProcessHandle(int index)
369    const {
370  CHECK_LT(index, ResourceCount());
371  return resources_[index]->GetProcess();
372}
373
374TaskManager::Resource::Type TaskManagerModel::GetResourceType(int index) const {
375  CHECK_LT(index, ResourceCount());
376  return resources_[index]->GetType();
377}
378
379TabContentsWrapper* TaskManagerModel::GetResourceTabContents(int index) const {
380  CHECK_LT(index, ResourceCount());
381  return resources_[index]->GetTabContents();
382}
383
384const Extension* TaskManagerModel::GetResourceExtension(int index) const {
385  CHECK_LT(index, ResourceCount());
386  return resources_[index]->GetExtension();
387}
388
389int64 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
397double 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
405bool 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
421bool 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
437bool 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
454bool 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
463int TaskManagerModel::GetGoatsTeleported(int index) const {
464  int seed = goat_salt_ * (index + 1);
465  return (seed >> 16) & 255;
466}
467
468string16 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
482void 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
514void 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
541void 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
549void TaskManagerModel::RegisterForJobDoneNotifications() {
550  net::g_url_request_job_tracker.AddObserver(this);
551}
552
553void TaskManagerModel::UnregisterForJobDoneNotifications() {
554  net::g_url_request_job_tracker.RemoveObserver(this);
555}
556
557void 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
603void 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
656void 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
685void 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
690void 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
701void 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
712void 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
777int64 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
786void 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).
840void TaskManagerModel::OnJobAdded(net::URLRequestJob* job) {
841}
842
843void TaskManagerModel::OnJobRemoved(net::URLRequestJob* job) {
844}
845
846void TaskManagerModel::OnJobDone(net::URLRequestJob* job,
847                                 const net::URLRequestStatus& status) {
848}
849
850void TaskManagerModel::OnJobRedirect(net::URLRequestJob* job,
851                                     const GURL& location,
852                                     int status_code) {
853}
854
855void 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
883bool 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
901void TaskManager::RegisterPrefs(PrefService* prefs) {
902  prefs->RegisterDictionaryPref(prefs::kTaskManagerWindowPlacement);
903}
904
905TaskManager::TaskManager()
906    : ALLOW_THIS_IN_INITIALIZER_LIST(model_(new TaskManagerModel(this))) {
907}
908
909TaskManager::~TaskManager() {
910}
911
912bool 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
920void 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
927void 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
937void TaskManager::AddResource(Resource* resource) {
938  model_->AddResource(resource);
939}
940
941void TaskManager::RemoveResource(Resource* resource) {
942  model_->RemoveResource(resource);
943}
944
945void TaskManager::OnWindowClosed() {
946  model_->StopUpdating();
947}
948
949void TaskManager::ModelChanged() {
950  model_->ModelChanged();
951}
952
953// static
954TaskManager* TaskManager::GetInstance() {
955  return Singleton<TaskManager>::get();
956}
957
958void 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
992bool 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