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