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