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