diff options
author | evan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-23 19:34:12 +0000 |
---|---|---|
committer | evan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-23 19:34:12 +0000 |
commit | a5d1e1e2cf6deb70a8476fd1f970c697274cbca7 (patch) | |
tree | f2842a6ea90359429a5c9f46c889a8a943861929 /chrome/browser/task_manager/task_manager.cc | |
parent | a4a5740d7ae8c5b54bc4dea0b3ea2deb43edad88 (diff) | |
download | chromium_src-a5d1e1e2cf6deb70a8476fd1f970c697274cbca7.zip chromium_src-a5d1e1e2cf6deb70a8476fd1f970c697274cbca7.tar.gz chromium_src-a5d1e1e2cf6deb70a8476fd1f970c697274cbca7.tar.bz2 |
Move task manager code into a subdirectory.
BUG=50548
TEST=compiles
Review URL: http://codereview.chromium.org/3454028
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@60325 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/task_manager/task_manager.cc')
-rw-r--r-- | chrome/browser/task_manager/task_manager.cc | 981 |
1 files changed, 981 insertions, 0 deletions
diff --git a/chrome/browser/task_manager/task_manager.cc b/chrome/browser/task_manager/task_manager.cc new file mode 100644 index 0000000..6d4147c --- /dev/null +++ b/chrome/browser/task_manager/task_manager.cc @@ -0,0 +1,981 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/task_manager/task_manager.h" + +#include "app/l10n_util.h" +#include "app/resource_bundle.h" +#include "base/compiler_specific.h" +#include "base/i18n/number_formatting.h" +#include "base/i18n/rtl.h" +#include "base/process_util.h" +#include "base/string_number_conversions.h" +#include "base/string_util.h" +#include "base/thread.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/browser_window.h" +#include "chrome/browser/chrome_thread.h" +#include "chrome/browser/net/url_request_tracking.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/profile_manager.h" +#include "chrome/browser/renderer_host/render_process_host.h" +#include "chrome/browser/renderer_host/resource_dispatcher_host.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/task_manager/task_manager_resource_providers.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/url_constants.h" +#include "grit/app_resources.h" +#include "grit/chromium_strings.h" +#include "grit/generated_resources.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_job.h" +#include "unicode/coll.h" + +#if defined(OS_MACOSX) +#include "chrome/browser/mach_broker_mac.h" +#endif + +namespace { + +// The delay between updates of the information (in ms). +#if defined(OS_MACOSX) +// Match Activity Monitor's default refresh rate. +const int kUpdateTimeMs = 2000; +#else +const int kUpdateTimeMs = 1000; +#endif + +template <class T> +int ValueCompare(T value1, T value2) { + if (value1 < value2) + return -1; + if (value1 == value2) + return 0; + return 1; +} + +string16 FormatStatsSize(const WebKit::WebCache::ResourceTypeStat& stat) { + return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_CACHE_SIZE_CELL_TEXT, + FormatBytes(stat.size, DATA_UNITS_KIBIBYTE, false), + FormatBytes(stat.liveSize, DATA_UNITS_KIBIBYTE, false)); +} + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// +// TaskManagerModel class +//////////////////////////////////////////////////////////////////////////////// + +TaskManagerModel::TaskManagerModel(TaskManager* task_manager) + : update_state_(IDLE), + goat_salt_(rand()) { + + TaskManagerBrowserProcessResourceProvider* browser_provider = + new TaskManagerBrowserProcessResourceProvider(task_manager); + browser_provider->AddRef(); + providers_.push_back(browser_provider); + TaskManagerTabContentsResourceProvider* wc_provider = + new TaskManagerTabContentsResourceProvider(task_manager); + wc_provider->AddRef(); + providers_.push_back(wc_provider); + TaskManagerChildProcessResourceProvider* child_process_provider = + new TaskManagerChildProcessResourceProvider(task_manager); + child_process_provider->AddRef(); + providers_.push_back(child_process_provider); + TaskManagerExtensionProcessResourceProvider* extension_process_provider = + new TaskManagerExtensionProcessResourceProvider(task_manager); + extension_process_provider->AddRef(); + providers_.push_back(extension_process_provider); + TaskManagerNotificationResourceProvider* notification_provider = + new TaskManagerNotificationResourceProvider(task_manager); + notification_provider->AddRef(); + providers_.push_back(notification_provider); +} + +TaskManagerModel::~TaskManagerModel() { + for (ResourceProviderList::iterator iter = providers_.begin(); + iter != providers_.end(); ++iter) { + (*iter)->Release(); + } +} + +int TaskManagerModel::ResourceCount() const { + return resources_.size(); +} + +void TaskManagerModel::AddObserver(TaskManagerModelObserver* observer) { + observer_list_.AddObserver(observer); +} + +void TaskManagerModel::RemoveObserver(TaskManagerModelObserver* observer) { + observer_list_.RemoveObserver(observer); +} + +string16 TaskManagerModel::GetResourceTitle(int index) const { + DCHECK(index < ResourceCount()); + return WideToUTF16Hack(resources_[index]->GetTitle()); +} + +string16 TaskManagerModel::GetResourceNetworkUsage(int index) const { + DCHECK(index < ResourceCount()); + int64 net_usage = GetNetworkUsage(resources_[index]); + if (net_usage == -1) + return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT); + if (net_usage == 0) + return ASCIIToUTF16("0"); + string16 net_byte = FormatSpeed(net_usage, GetByteDisplayUnits(net_usage), + true); + // Force number string to have LTR directionality. + return base::i18n::GetDisplayStringInLTRDirectionality(net_byte); +} + +string16 TaskManagerModel::GetResourceCPUUsage(int index) const { + DCHECK(index < ResourceCount()); + return WideToUTF16Hack(StringPrintf( +#if defined(OS_MACOSX) + // Activity Monitor shows %cpu with one decimal digit -- be + // consistent with that. + L"%.1f", +#else + L"%.0f", +#endif + GetCPUUsage(resources_[index]))); +} + +string16 TaskManagerModel::GetResourcePrivateMemory(int index) const { + size_t private_mem; + if (!GetPrivateMemory(index, &private_mem)) + return ASCIIToUTF16("N/A"); + return GetMemCellText(private_mem); +} + +string16 TaskManagerModel::GetResourceSharedMemory(int index) const { + size_t shared_mem; + if (!GetSharedMemory(index, &shared_mem)) + return ASCIIToUTF16("N/A"); + return GetMemCellText(shared_mem); +} + +string16 TaskManagerModel::GetResourcePhysicalMemory(int index) const { + size_t phys_mem; + GetPhysicalMemory(index, &phys_mem); + return GetMemCellText(phys_mem); +} + +string16 TaskManagerModel::GetResourceProcessId(int index) const { + DCHECK(index < ResourceCount()); + return base::IntToString16(base::GetProcId(resources_[index]->GetProcess())); +} + +string16 TaskManagerModel::GetResourceGoatsTeleported(int index) const { + DCHECK(index < ResourceCount()); + return base::FormatNumber(GetGoatsTeleported(index)); +} + +string16 TaskManagerModel::GetResourceWebCoreImageCacheSize( + int index) const { + DCHECK(index < ResourceCount()); + if (!resources_[index]->ReportsCacheStats()) + return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT); + const WebKit::WebCache::ResourceTypeStats stats( + resources_[index]->GetWebCoreCacheStats()); + return FormatStatsSize(stats.images); +} + +string16 TaskManagerModel::GetResourceWebCoreScriptsCacheSize( + int index) const { + DCHECK(index < ResourceCount()); + if (!resources_[index]->ReportsCacheStats()) + return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT); + const WebKit::WebCache::ResourceTypeStats stats( + resources_[index]->GetWebCoreCacheStats()); + return FormatStatsSize(stats.scripts); +} + +string16 TaskManagerModel::GetResourceWebCoreCSSCacheSize( + int index) const { + DCHECK(index < ResourceCount()); + if (!resources_[index]->ReportsCacheStats()) + return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT); + const WebKit::WebCache::ResourceTypeStats stats( + resources_[index]->GetWebCoreCacheStats()); + return FormatStatsSize(stats.cssStyleSheets); +} + +string16 TaskManagerModel::GetResourceSqliteMemoryUsed(int index) const { + DCHECK(index < ResourceCount()); + if (!resources_[index]->ReportsSqliteMemoryUsed()) + return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT); + return GetMemCellText(resources_[index]->SqliteMemoryUsedBytes()); +} + +string16 TaskManagerModel::GetResourceV8MemoryAllocatedSize( + int index) const { + if (!resources_[index]->ReportsV8MemoryStats()) + return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT); + return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_CACHE_SIZE_CELL_TEXT, + FormatBytes(resources_[index]->GetV8MemoryAllocated(), + DATA_UNITS_KIBIBYTE, + false), + FormatBytes(resources_[index]->GetV8MemoryUsed(), + DATA_UNITS_KIBIBYTE, + false)); +} + +bool TaskManagerModel::IsResourceFirstInGroup(int index) const { + DCHECK(index < ResourceCount()); + TaskManager::Resource* resource = resources_[index]; + GroupMap::const_iterator iter = group_map_.find(resource->GetProcess()); + DCHECK(iter != group_map_.end()); + const ResourceList* group = iter->second; + return ((*group)[0] == resource); +} + +SkBitmap TaskManagerModel::GetResourceIcon(int index) const { + DCHECK(index < ResourceCount()); + SkBitmap icon = resources_[index]->GetIcon(); + if (!icon.isNull()) + return icon; + + static SkBitmap* default_icon = ResourceBundle::GetSharedInstance(). + GetBitmapNamed(IDR_DEFAULT_FAVICON); + return *default_icon; +} + +std::pair<int, int> TaskManagerModel::GetGroupRangeForResource(int index) + const { + DCHECK(index < ResourceCount()); + TaskManager::Resource* resource = resources_[index]; + GroupMap::const_iterator group_iter = + group_map_.find(resource->GetProcess()); + DCHECK(group_iter != group_map_.end()); + ResourceList* group = group_iter->second; + DCHECK(group); + if (group->size() == 1) { + return std::make_pair(index, 1); + } else { + for (int i = index; i >= 0; --i) { + if (resources_[i] == (*group)[0]) + return std::make_pair(i, group->size()); + } + NOTREACHED(); + return std::make_pair(-1, -1); + } +} + +int TaskManagerModel::CompareValues(int row1, int row2, int col_id) const { + DCHECK(row1 < ResourceCount() && row2 < ResourceCount()); + switch (col_id) { + case IDS_TASK_MANAGER_PAGE_COLUMN: { + // Let's do the default, string compare on the resource title. + static icu::Collator* collator = NULL; + if (!collator) { + UErrorCode create_status = U_ZERO_ERROR; + collator = icu::Collator::createInstance(create_status); + if (!U_SUCCESS(create_status)) { + collator = NULL; + NOTREACHED(); + } + } + string16 title1 = GetResourceTitle(row1); + string16 title2 = GetResourceTitle(row2); + UErrorCode compare_status = U_ZERO_ERROR; + UCollationResult compare_result = collator->compare( + static_cast<const UChar*>(title1.c_str()), + static_cast<int>(title1.length()), + static_cast<const UChar*>(title2.c_str()), + static_cast<int>(title2.length()), + compare_status); + DCHECK(U_SUCCESS(compare_status)); + return compare_result; + } + + case IDS_TASK_MANAGER_NET_COLUMN: + return ValueCompare<int64>(GetNetworkUsage(resources_[row1]), + GetNetworkUsage(resources_[row2])); + + case IDS_TASK_MANAGER_CPU_COLUMN: + return ValueCompare<double>(GetCPUUsage(resources_[row1]), + GetCPUUsage(resources_[row2])); + + case IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN: { + size_t value1; + size_t value2; + if (!GetPrivateMemory(row1, &value1) || !GetPrivateMemory(row2, &value2)) + return 0; + return ValueCompare<size_t>(value1, value2); + } + + case IDS_TASK_MANAGER_SHARED_MEM_COLUMN: { + size_t value1; + size_t value2; + if (!GetSharedMemory(row1, &value1) || !GetSharedMemory(row2, &value2)) + return 0; + return ValueCompare<size_t>(value1, value2); + } + + case IDS_TASK_MANAGER_PHYSICAL_MEM_COLUMN: { + size_t value1; + size_t value2; + if (!GetPhysicalMemory(row1, &value1) || + !GetPhysicalMemory(row2, &value2)) + return 0; + return ValueCompare<size_t>(value1, value2); + } + + case IDS_TASK_MANAGER_PROCESS_ID_COLUMN: { + int proc1_id = base::GetProcId(resources_[row1]->GetProcess()); + int proc2_id = base::GetProcId(resources_[row2]->GetProcess()); + return ValueCompare<int>(proc1_id, proc2_id); + } + + case IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN: + case IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN: + case IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN: { + WebKit::WebCache::ResourceTypeStats stats1 = { { 0 } }; + WebKit::WebCache::ResourceTypeStats stats2 = { { 0 } }; + if (resources_[row1]->ReportsCacheStats()) + stats1 = resources_[row1]->GetWebCoreCacheStats(); + if (resources_[row2]->ReportsCacheStats()) + stats2 = resources_[row2]->GetWebCoreCacheStats(); + if (IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN == col_id) + return ValueCompare<size_t>(stats1.images.size, stats2.images.size); + if (IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN == col_id) + return ValueCompare<size_t>(stats1.scripts.size, stats2.scripts.size); + DCHECK_EQ(IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN, col_id); + return ValueCompare<size_t>(stats1.cssStyleSheets.size, + stats2.cssStyleSheets.size); + } + + case IDS_TASK_MANAGER_GOATS_TELEPORTED_COLUMN: { + return ValueCompare<int>(GetGoatsTeleported(row1), + GetGoatsTeleported(row2)); + } + + case IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN: { + size_t value1; + size_t value2; + bool reports_v8_memory1 = GetV8Memory(row1, &value1); + bool reports_v8_memory2 = GetV8Memory(row2, &value2); + if (reports_v8_memory1 == reports_v8_memory2) + return ValueCompare<size_t>(value1, value2); + else + return reports_v8_memory1 ? 1 : -1; + } + + default: + NOTREACHED(); + return 0; + } +} + +base::ProcessHandle TaskManagerModel::GetResourceProcessHandle(int index) + const { + DCHECK(index < ResourceCount()); + return resources_[index]->GetProcess(); +} + +TabContents* TaskManagerModel::GetResourceTabContents(int index) const { + DCHECK(index < ResourceCount()); + return resources_[index]->GetTabContents(); +} + +const Extension* TaskManagerModel::GetResourceExtension(int index) const { + DCHECK(index < ResourceCount()); + return resources_[index]->GetExtension(); +} + +int64 TaskManagerModel::GetNetworkUsage(TaskManager::Resource* resource) + const { + int64 net_usage = GetNetworkUsageForResource(resource); + if (net_usage == 0 && !resource->SupportNetworkUsage()) + return -1; + return net_usage; +} + +double TaskManagerModel::GetCPUUsage(TaskManager::Resource* resource) const { + CPUUsageMap::const_iterator iter = + cpu_usage_map_.find(resource->GetProcess()); + if (iter == cpu_usage_map_.end()) + return 0; + return iter->second; +} + +bool TaskManagerModel::GetPrivateMemory(int index, size_t* result) const { + base::ProcessHandle handle = resources_[index]->GetProcess(); + MemoryUsageMap::const_iterator iter = memory_usage_map_.find(handle); + if (iter == memory_usage_map_.end()) { + std::pair<size_t, size_t> usage; + if (!GetAndCacheMemoryMetrics(handle, &usage)) + return false; + + *result = usage.first; + } else { + *result = iter->second.first; + } + + return true; +} + +bool TaskManagerModel::GetSharedMemory(int index, size_t* result) const { + base::ProcessHandle handle = resources_[index]->GetProcess(); + MemoryUsageMap::const_iterator iter = memory_usage_map_.find(handle); + if (iter == memory_usage_map_.end()) { + std::pair<size_t, size_t> usage; + if (!GetAndCacheMemoryMetrics(handle, &usage)) + return false; + + *result = usage.second; + } else { + *result = iter->second.second; + } + + return true; +} + +bool TaskManagerModel::GetPhysicalMemory(int index, size_t* result) const { + *result = 0; + base::ProcessMetrics* process_metrics; + if (!GetProcessMetricsForRow(index, &process_metrics)) + return false; + base::WorkingSetKBytes ws_usage; + if (!process_metrics->GetWorkingSetKBytes(&ws_usage)) + return false; + + // Memory = working_set.private + working_set.shareable. + // We exclude the shared memory. + size_t total_bytes = process_metrics->GetWorkingSetSize(); + total_bytes -= ws_usage.shared * 1024; + *result = total_bytes; + return true; +} + +bool TaskManagerModel::GetV8Memory(int index, size_t* result) const { + *result = 0; + if (!resources_[index]->ReportsV8MemoryStats()) + return false; + + *result = resources_[index]->GetV8MemoryAllocated(); + return true; +} + +int TaskManagerModel::GetGoatsTeleported(int index) const { + int seed = goat_salt_ * (index + 1); + return (seed >> 16) & 255; +} + +string16 TaskManagerModel::GetMemCellText(int64 number) const { +#if !defined(OS_MACOSX) + string16 str = base::FormatNumber(number / 1024); + + // Adjust number string if necessary. + base::i18n::AdjustStringForLocaleDirection(str, &str); + return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_MEM_CELL_TEXT, str); +#else + // System expectation is to show "100 KB", "200 MB", etc. + // TODO(thakis): Switch to metric units (as opposed to powers of two). + return FormatBytes(number, GetByteDisplayUnits(number), /*show_units=*/true); +#endif +} + +void TaskManagerModel::StartUpdating() { + DCHECK_NE(TASK_PENDING, update_state_); + + // If update_state_ is STOPPING, it means a task is still pending. Setting + // it to TASK_PENDING ensures the tasks keep being posted (by Refresh()). + if (update_state_ == IDLE) { + MessageLoop::current()->PostDelayedTask(FROM_HERE, + NewRunnableMethod(this, &TaskManagerModel::Refresh), + kUpdateTimeMs); + } + update_state_ = TASK_PENDING; + + // Register jobs notifications so we can compute network usage (it must be + // done from the IO thread). + ChromeThread::PostTask( + ChromeThread::IO, FROM_HERE, + NewRunnableMethod( + this, &TaskManagerModel::RegisterForJobDoneNotifications)); + + // Notify resource providers that we are updating. + for (ResourceProviderList::iterator iter = providers_.begin(); + iter != providers_.end(); ++iter) { + (*iter)->StartUpdating(); + } +} + +void TaskManagerModel::StopUpdating() { + DCHECK_EQ(TASK_PENDING, update_state_); + update_state_ = STOPPING; + + // Notify resource providers that we are done updating. + for (ResourceProviderList::const_iterator iter = providers_.begin(); + iter != providers_.end(); ++iter) { + (*iter)->StopUpdating(); + } + + // Unregister jobs notification (must be done from the IO thread). + ChromeThread::PostTask( + ChromeThread::IO, FROM_HERE, + NewRunnableMethod( + this, &TaskManagerModel::UnregisterForJobDoneNotifications)); +} + +void TaskManagerModel::AddResourceProvider( + TaskManager::ResourceProvider* provider) { + DCHECK(provider); + providers_.push_back(provider); +} + +void TaskManagerModel::RemoveResourceProvider( + TaskManager::ResourceProvider* provider) { + DCHECK(provider); + ResourceProviderList::iterator iter = std::find(providers_.begin(), + providers_.end(), + provider); + DCHECK(iter != providers_.end()); + providers_.erase(iter); +} + +void TaskManagerModel::RegisterForJobDoneNotifications() { + g_url_request_job_tracker.AddObserver(this); +} + +void TaskManagerModel::UnregisterForJobDoneNotifications() { + g_url_request_job_tracker.RemoveObserver(this); +} + +void TaskManagerModel::AddResource(TaskManager::Resource* resource) { + base::ProcessHandle process = resource->GetProcess(); + + ResourceList* group_entries = NULL; + GroupMap::const_iterator group_iter = group_map_.find(process); + int new_entry_index = 0; + if (group_iter == group_map_.end()) { + group_entries = new ResourceList(); + group_map_[process] = group_entries; + group_entries->push_back(resource); + + // Not part of a group, just put at the end of the list. + resources_.push_back(resource); + new_entry_index = static_cast<int>(resources_.size() - 1); + } else { + group_entries = group_iter->second; + group_entries->push_back(resource); + + // Insert the new entry right after the last entry of its group. + ResourceList::iterator iter = + std::find(resources_.begin(), + resources_.end(), + (*group_entries)[group_entries->size() - 2]); + DCHECK(iter != resources_.end()); + new_entry_index = static_cast<int>(iter - resources_.begin()); + resources_.insert(++iter, resource); + } + + // Create the ProcessMetrics for this process if needed (not in map). + if (metrics_map_.find(process) == metrics_map_.end()) { + base::ProcessMetrics* pm = +#if !defined(OS_MACOSX) + base::ProcessMetrics::CreateProcessMetrics(process); +#else + base::ProcessMetrics::CreateProcessMetrics(process, + MachBroker::instance()); +#endif + + metrics_map_[process] = pm; + } + + // Notify the table that the contents have changed for it to redraw. + FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_, + OnItemsAdded(new_entry_index, 1)); +} + +void TaskManagerModel::RemoveResource(TaskManager::Resource* resource) { + base::ProcessHandle process = resource->GetProcess(); + + // Find the associated group. + GroupMap::iterator group_iter = group_map_.find(process); + DCHECK(group_iter != group_map_.end()); + ResourceList* group_entries = group_iter->second; + + // Remove the entry from the group map. + ResourceList::iterator iter = std::find(group_entries->begin(), + group_entries->end(), + resource); + DCHECK(iter != group_entries->end()); + group_entries->erase(iter); + + // If there are no more entries for that process, do the clean-up. + if (group_entries->empty()) { + delete group_entries; + group_map_.erase(process); + + // Nobody is using this process, we don't need the process metrics anymore. + MetricsMap::iterator pm_iter = metrics_map_.find(process); + DCHECK(pm_iter != metrics_map_.end()); + if (pm_iter != metrics_map_.end()) { + delete pm_iter->second; + metrics_map_.erase(process); + } + // And we don't need the CPU usage anymore either. + CPUUsageMap::iterator cpu_iter = cpu_usage_map_.find(process); + if (cpu_iter != cpu_usage_map_.end()) + cpu_usage_map_.erase(cpu_iter); + } + + // Remove the entry from the model list. + iter = std::find(resources_.begin(), resources_.end(), resource); + DCHECK(iter != resources_.end()); + int index = static_cast<int>(iter - resources_.begin()); + resources_.erase(iter); + + // Remove the entry from the network maps. + ResourceValueMap::iterator net_iter = + current_byte_count_map_.find(resource); + if (net_iter != current_byte_count_map_.end()) + current_byte_count_map_.erase(net_iter); + net_iter = displayed_network_usage_map_.find(resource); + if (net_iter != displayed_network_usage_map_.end()) + displayed_network_usage_map_.erase(net_iter); + + // Notify the table that the contents have changed. + FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_, + OnItemsRemoved(index, 1)); +} + +void TaskManagerModel::Clear() { + int size = ResourceCount(); + if (size > 0) { + resources_.clear(); + + // Clear the groups. + for (GroupMap::iterator iter = group_map_.begin(); + iter != group_map_.end(); ++iter) { + delete iter->second; + } + group_map_.clear(); + + // Clear the process related info. + for (MetricsMap::iterator iter = metrics_map_.begin(); + iter != metrics_map_.end(); ++iter) { + delete iter->second; + } + metrics_map_.clear(); + cpu_usage_map_.clear(); + + // Clear the network maps. + current_byte_count_map_.clear(); + displayed_network_usage_map_.clear(); + + FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_, + OnItemsRemoved(0, size)); + } +} + +void TaskManagerModel::NotifyResourceTypeStats( + base::ProcessId renderer_id, + const WebKit::WebCache::ResourceTypeStats& stats) { + for (ResourceList::iterator it = resources_.begin(); + it != resources_.end(); ++it) { + if (base::GetProcId((*it)->GetProcess()) == renderer_id) { + (*it)->NotifyResourceTypeStats(stats); + } + } +} + +void TaskManagerModel::NotifyV8HeapStats(base::ProcessId renderer_id, + size_t v8_memory_allocated, + size_t v8_memory_used) { + for (ResourceList::iterator it = resources_.begin(); + it != resources_.end(); ++it) { + if (base::GetProcId((*it)->GetProcess()) == renderer_id) { + (*it)->NotifyV8HeapStats(v8_memory_allocated, v8_memory_used); + } + } +} + +void TaskManagerModel::Refresh() { + DCHECK_NE(IDLE, update_state_); + + if (update_state_ == STOPPING) { + // We have been asked to stop. + update_state_ = IDLE; + return; + } + + goat_salt_ = rand(); + + // Compute the CPU usage values. + // Note that we compute the CPU usage for all resources (instead of doing it + // lazily) as process_util::GetCPUUsage() returns the CPU usage since the last + // time it was called, and not calling it everytime would skew the value the + // next time it is retrieved (as it would be for more than 1 cycle). + cpu_usage_map_.clear(); + for (ResourceList::iterator iter = resources_.begin(); + iter != resources_.end(); ++iter) { + base::ProcessHandle process = (*iter)->GetProcess(); + CPUUsageMap::iterator cpu_iter = cpu_usage_map_.find(process); + if (cpu_iter != cpu_usage_map_.end()) + continue; // Already computed. + + MetricsMap::iterator metrics_iter = metrics_map_.find(process); + DCHECK(metrics_iter != metrics_map_.end()); + cpu_usage_map_[process] = metrics_iter->second->GetCPUUsage(); + } + + // Clear the memory values so they can be querried lazily. + memory_usage_map_.clear(); + + // Compute the new network usage values. + displayed_network_usage_map_.clear(); + for (ResourceValueMap::iterator iter = current_byte_count_map_.begin(); + iter != current_byte_count_map_.end(); ++iter) { + if (kUpdateTimeMs > 1000) { + int divider = (kUpdateTimeMs / 1000); + displayed_network_usage_map_[iter->first] = iter->second / divider; + } else { + displayed_network_usage_map_[iter->first] = iter->second * + (1000 / kUpdateTimeMs); + } + + // Then we reset the current byte count. + iter->second = 0; + } + + // Let resources update themselves if they need to. + for (ResourceList::iterator iter = resources_.begin(); + iter != resources_.end(); ++iter) { + (*iter)->Refresh(); + } + + if (!resources_.empty()) { + FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_, + OnItemsChanged(0, ResourceCount())); + } + + // Schedule the next update. + MessageLoop::current()->PostDelayedTask(FROM_HERE, + NewRunnableMethod(this, &TaskManagerModel::Refresh), + kUpdateTimeMs); +} + +int64 TaskManagerModel::GetNetworkUsageForResource( + TaskManager::Resource* resource) const { + ResourceValueMap::const_iterator iter = + displayed_network_usage_map_.find(resource); + if (iter == displayed_network_usage_map_.end()) + return 0; + return iter->second; +} + +void TaskManagerModel::BytesRead(BytesReadParam param) { + if (update_state_ != TASK_PENDING) { + // A notification sneaked in while we were stopping the updating, just + // ignore it. + return; + } + + if (param.byte_count == 0) { + // Nothing to do if no bytes were actually read. + return; + } + + // TODO(jcampan): this should be improved once we have a better way of + // linking a network notification back to the object that initiated it. + TaskManager::Resource* resource = NULL; + for (ResourceProviderList::iterator iter = providers_.begin(); + iter != providers_.end(); ++iter) { + resource = (*iter)->GetResource(param.origin_child_id, + param.render_process_host_child_id, + param.routing_id); + if (resource) + break; + } + if (resource == NULL) { + // We may not have that resource anymore (example: close a tab while a + // a network resource is being retrieved), in which case we just ignore the + // notification. + return; + } + + // We do support network usage, mark the resource as such so it can report 0 + // instead of N/A. + if (!resource->SupportNetworkUsage()) + resource->SetSupportNetworkUsage(); + + ResourceValueMap::const_iterator iter_res = + current_byte_count_map_.find(resource); + if (iter_res == current_byte_count_map_.end()) + current_byte_count_map_[resource] = param.byte_count; + else + current_byte_count_map_[resource] = iter_res->second + param.byte_count; +} + + +// In order to retrieve the network usage, we register for URLRequestJob +// notifications. Every time we get notified some bytes were read we bump a +// counter of read bytes for the associated resource. When the timer ticks, +// we'll compute the actual network usage (see the Refresh method). +void TaskManagerModel::OnJobAdded(URLRequestJob* job) { +} + +void TaskManagerModel::OnJobRemoved(URLRequestJob* job) { +} + +void TaskManagerModel::OnJobDone(URLRequestJob* job, + const URLRequestStatus& status) { +} + +void TaskManagerModel::OnJobRedirect(URLRequestJob* job, + const GURL& location, + int status_code) { +} + +void TaskManagerModel::OnBytesRead(URLRequestJob* job, const char* buf, + int byte_count) { + int render_process_host_child_id = -1, routing_id = -1; + ResourceDispatcherHost::RenderViewForRequest(job->request(), + &render_process_host_child_id, + &routing_id); + // This happens in the IO thread, post it to the UI thread. + int origin_child_id = + chrome_browser_net::GetOriginProcessUniqueIDForRequest(job->request()); + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod( + this, + &TaskManagerModel::BytesRead, + BytesReadParam(origin_child_id, + render_process_host_child_id, + routing_id, byte_count))); +} + +bool TaskManagerModel::GetProcessMetricsForRow( + int row, base::ProcessMetrics** proc_metrics) const { + DCHECK(row < ResourceCount()); + *proc_metrics = NULL; + + MetricsMap::const_iterator iter = + metrics_map_.find(resources_[row]->GetProcess()); + if (iter == metrics_map_.end()) + return false; + *proc_metrics = iter->second; + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// TaskManager class +//////////////////////////////////////////////////////////////////////////////// + +// static +void TaskManager::RegisterPrefs(PrefService* prefs) { + prefs->RegisterDictionaryPref(prefs::kTaskManagerWindowPlacement); +} + +TaskManager::TaskManager() + : ALLOW_THIS_IN_INITIALIZER_LIST(model_(new TaskManagerModel(this))) { +} + +TaskManager::~TaskManager() { +} + +bool TaskManager::IsBrowserProcess(int index) const { + // If some of the selection is out of bounds, ignore. This may happen when + // killing a process that manages several pages. + return index < model_->ResourceCount() && + model_->GetResourceProcessHandle(index) == + base::GetCurrentProcessHandle(); +} + +void TaskManager::KillProcess(int index) { + base::ProcessHandle process = model_->GetResourceProcessHandle(index); + DCHECK(process); + if (process != base::GetCurrentProcessHandle()) + base::KillProcess(process, base::PROCESS_END_KILLED_BY_USER, false); +} + +void TaskManager::ActivateProcess(int index) { + // GetResourceTabContents returns a pointer to the relevant tab contents for + // the resource. If the index doesn't correspond to a Tab (i.e. refers to + // the Browser process or a plugin), GetTabContents will return NULL. + TabContents* chosen_tab_contents = model_->GetResourceTabContents(index); + if (chosen_tab_contents) + chosen_tab_contents->Activate(); +} + +void TaskManager::AddResourceProvider(ResourceProvider* provider) { + model_->AddResourceProvider(provider); +} + +void TaskManager::RemoveResourceProvider(ResourceProvider* provider) { + model_->RemoveResourceProvider(provider); +} + +void TaskManager::AddResource(Resource* resource) { + model_->AddResource(resource); +} + +void TaskManager::RemoveResource(Resource* resource) { + model_->RemoveResource(resource); +} + +void TaskManager::OnWindowClosed() { + model_->StopUpdating(); + model_->Clear(); +} + +// static +TaskManager* TaskManager::GetInstance() { + return Singleton<TaskManager>::get(); +} + +void TaskManager::OpenAboutMemory() { + Browser* browser = BrowserList::GetLastActive(); + + if (!browser) { + // On OS X, the task manager can be open without any open browser windows. + if (!g_browser_process || + !g_browser_process->profile_manager() || + g_browser_process->profile_manager()->begin() == + g_browser_process->profile_manager()->end()) + return; + browser = Browser::Create(*g_browser_process->profile_manager()->begin()); + browser->OpenURL(GURL(chrome::kAboutMemoryURL), GURL(), NEW_WINDOW, + PageTransition::LINK); + } else { + browser->OpenURL(GURL(chrome::kAboutMemoryURL), GURL(), NEW_FOREGROUND_TAB, + PageTransition::LINK); + + // In case the browser window is minimzed, show it. If |browser| is a + // non-tabbed window, the call to OpenURL above will have opened a + // TabContents in a tabbed browser, so we need to grab it with GetLastActive + // before the call to show(). + if (browser->type() & (Browser::TYPE_APP | + Browser::TYPE_APP_PANEL | + Browser::TYPE_DEVTOOLS | + Browser::TYPE_POPUP)) { + browser = BrowserList::GetLastActive(); + DCHECK(browser); + } + + browser->window()->Show(); + } +} + +bool TaskManagerModel::GetAndCacheMemoryMetrics( + base::ProcessHandle handle, + std::pair<size_t, size_t>* usage) const { + MetricsMap::const_iterator iter = metrics_map_.find(handle); + if (iter == metrics_map_.end()) + return false; + + if (!iter->second->GetMemoryBytes(&usage->first, &usage->second)) + return false; + + memory_usage_map_.insert(std::make_pair(handle, *usage)); + return true; +} |