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 | |
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')
-rw-r--r-- | chrome/browser/task_manager/task_manager.cc | 981 | ||||
-rw-r--r-- | chrome/browser/task_manager/task_manager.h | 415 | ||||
-rw-r--r-- | chrome/browser/task_manager/task_manager_browsertest.cc | 265 | ||||
-rw-r--r-- | chrome/browser/task_manager/task_manager_resource_providers.cc | 935 | ||||
-rw-r--r-- | chrome/browser/task_manager/task_manager_resource_providers.h | 396 | ||||
-rw-r--r-- | chrome/browser/task_manager/task_manager_unittest.cc | 110 |
6 files changed, 3102 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; +} diff --git a/chrome/browser/task_manager/task_manager.h b/chrome/browser/task_manager/task_manager.h new file mode 100644 index 0000000..b7b6890 --- /dev/null +++ b/chrome/browser/task_manager/task_manager.h @@ -0,0 +1,415 @@ +// 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. + +#ifndef CHROME_BROWSER_TASK_MANAGER_TASK_MANAGER_H_ +#define CHROME_BROWSER_TASK_MANAGER_TASK_MANAGER_H_ +#pragma once + +#include <map> +#include <string> +#include <utility> +#include <vector> + +#include "base/basictypes.h" +#include "base/gtest_prod_util.h" +#include "base/lock.h" +#include "base/observer_list.h" +#include "base/process_util.h" +#include "base/ref_counted.h" +#include "base/singleton.h" +#include "base/string16.h" +#include "base/timer.h" +#include "chrome/browser/renderer_host/web_cache_manager.h" +#include "net/url_request/url_request_job_tracker.h" +#include "third_party/WebKit/WebKit/chromium/public/WebCache.h" + +class Extension; +class SkBitmap; +class TabContents; +class TaskManagerModel; + +namespace base { +class ProcessMetrics; +} + +// This class is a singleton. +class TaskManager { + public: + // A resource represents one row in the task manager. + // Resources from similar processes are grouped together by the task manager. + class Resource { + public: + virtual ~Resource() {} + + virtual std::wstring GetTitle() const = 0; + virtual SkBitmap GetIcon() const = 0; + virtual base::ProcessHandle GetProcess() const = 0; + + virtual bool ReportsCacheStats() const { return false; } + virtual WebKit::WebCache::ResourceTypeStats GetWebCoreCacheStats() const { + return WebKit::WebCache::ResourceTypeStats(); + } + + virtual bool ReportsSqliteMemoryUsed() const { return false; } + virtual size_t SqliteMemoryUsedBytes() const { return 0; } + + // Return extension associated with the resource, or NULL + // if not applicable. + virtual const Extension* GetExtension() const { return NULL; } + + virtual bool ReportsV8MemoryStats() const { return false; } + virtual size_t GetV8MemoryAllocated() const { return 0; } + virtual size_t GetV8MemoryUsed() const { return 0; } + + // A helper function for ActivateFocusedTab. Returns NULL by default + // because not all resources have an associated tab. + virtual TabContents* GetTabContents() const { return NULL; } + + // Whether this resource does report the network usage accurately. + // This controls whether 0 or N/A is displayed when no bytes have been + // reported as being read. This is because some plugins do not report the + // bytes read and we don't want to display a misleading 0 value in that + // case. + virtual bool SupportNetworkUsage() const = 0; + + // Called when some bytes have been read and support_network_usage returns + // false (meaning we do have network usage support). + virtual void SetSupportNetworkUsage() = 0; + + // The TaskManagerModel periodically refreshes its data and call this + // on all live resources. + virtual void Refresh() {} + + virtual void NotifyResourceTypeStats( + const WebKit::WebCache::ResourceTypeStats& stats) {} + virtual void NotifyV8HeapStats(size_t v8_memory_allocated, + size_t v8_memory_used) {} + }; + + // ResourceProviders are responsible for adding/removing resources to the task + // manager. The task manager notifies the ResourceProvider that it is ready + // to receive resource creation/termination notifications with a call to + // StartUpdating(). At that point, the resource provider should call + // AddResource with all the existing resources, and after that it should call + // AddResource/RemoveResource as resources are created/terminated. + // The provider remains the owner of the resource objects and is responsible + // for deleting them (when StopUpdating() is called). + // After StopUpdating() is called the provider should also stop reporting + // notifications to the task manager. + // Note: ResourceProviders have to be ref counted as they are used in + // MessageLoop::InvokeLater(). + class ResourceProvider : public base::RefCountedThreadSafe<ResourceProvider> { + public: + // Should return the resource associated to the specified ids, or NULL if + // the resource does not belong to this provider. + virtual TaskManager::Resource* GetResource(int process_id, + int render_process_host_id, + int routing_id) = 0; + virtual void StartUpdating() = 0; + virtual void StopUpdating() = 0; + + protected: + friend class base::RefCountedThreadSafe<ResourceProvider>; + + virtual ~ResourceProvider() {} + }; + + static void RegisterPrefs(PrefService* prefs); + + // Returns true if the process at the specified index is the browser process. + bool IsBrowserProcess(int index) const; + + // Terminates the process at the specified index. + void KillProcess(int index); + + // Activates the browser tab associated with the process in the specified + // index. + void ActivateProcess(int index); + + void AddResourceProvider(ResourceProvider* provider); + void RemoveResourceProvider(ResourceProvider* provider); + + // These methods are invoked by the resource providers to add/remove resources + // to the Task Manager. Note that the resources are owned by the + // ResourceProviders and are not valid after StopUpdating() has been called + // on the ResourceProviders. + void AddResource(Resource* resource); + void RemoveResource(Resource* resource); + + void OnWindowClosed(); + + // Returns the singleton instance (and initializes it if necessary). + static TaskManager* GetInstance(); + + TaskManagerModel* model() const { return model_.get(); } + + void OpenAboutMemory(); + + private: + FRIEND_TEST_ALL_PREFIXES(TaskManagerTest, Basic); + FRIEND_TEST_ALL_PREFIXES(TaskManagerTest, Resources); + FRIEND_TEST_ALL_PREFIXES(TaskManagerTest, RefreshCalled); + FRIEND_TEST_ALL_PREFIXES(TaskManagerWindowControllerTest, Init); + FRIEND_TEST_ALL_PREFIXES(TaskManagerWindowControllerTest, Sort); + FRIEND_TEST_ALL_PREFIXES(TaskManagerWindowControllerTest, + SelectionAdaptsToSorting); + + // Obtain an instance via GetInstance(). + TaskManager(); + friend struct DefaultSingletonTraits<TaskManager>; + + ~TaskManager(); + + // The model used for gathering and processing task data. It is ref counted + // because it is passed as a parameter to MessageLoop::InvokeLater(). + scoped_refptr<TaskManagerModel> model_; + + DISALLOW_COPY_AND_ASSIGN(TaskManager); +}; + +class TaskManagerModelObserver { + public: + virtual ~TaskManagerModelObserver() {} + + // Invoked when the model has been completely changed. + virtual void OnModelChanged() = 0; + + // Invoked when a range of items has changed. + virtual void OnItemsChanged(int start, int length) = 0; + + // Invoked when new items are added. + virtual void OnItemsAdded(int start, int length) = 0; + + // Invoked when a range of items has been removed. + virtual void OnItemsRemoved(int start, int length) = 0; +}; + +// The model that the TaskManager is using. +class TaskManagerModel : public URLRequestJobTracker::JobObserver, + public base::RefCountedThreadSafe<TaskManagerModel> { + public: + explicit TaskManagerModel(TaskManager* task_manager); + + void AddObserver(TaskManagerModelObserver* observer); + void RemoveObserver(TaskManagerModelObserver* observer); + + // Returns number of registered resources. + int ResourceCount() const; + + // Methods to return formatted resource information. + string16 GetResourceTitle(int index) const; + string16 GetResourceNetworkUsage(int index) const; + string16 GetResourceCPUUsage(int index) const; + string16 GetResourcePrivateMemory(int index) const; + string16 GetResourceSharedMemory(int index) const; + string16 GetResourcePhysicalMemory(int index) const; + string16 GetResourceProcessId(int index) const; + string16 GetResourceWebCoreImageCacheSize(int index) const; + string16 GetResourceWebCoreScriptsCacheSize(int index) const; + string16 GetResourceWebCoreCSSCacheSize(int index) const; + string16 GetResourceSqliteMemoryUsed(int index) const; + string16 GetResourceGoatsTeleported(int index) const; + string16 GetResourceV8MemoryAllocatedSize(int index) const; + + // Returns true if the resource is first in its group (resources + // rendered by the same process are groupped together). + bool IsResourceFirstInGroup(int index) const; + + // Returns icon to be used for resource (for example a favicon). + SkBitmap GetResourceIcon(int index) const; + + // Returns a pair (start, length) of the group range of resource. + std::pair<int, int> GetGroupRangeForResource(int index) const; + + // Compares values in column |col_id| and rows |row1|, |row2|. + // Returns -1 if value in |row1| is less than value in |row2|, + // 0 if they are equal, and 1 otherwise. + int CompareValues(int row1, int row2, int col_id) const; + + // Returns process handle for given resource. + base::ProcessHandle GetResourceProcessHandle(int index) const; + + // Returns TabContents of given resource or NULL if not applicable. + TabContents* GetResourceTabContents(int index) const; + + // Returns Extension of given resource or NULL if not applicable. + const Extension* GetResourceExtension(int index) const; + + // JobObserver methods: + void OnJobAdded(URLRequestJob* job); + void OnJobRemoved(URLRequestJob* job); + void OnJobDone(URLRequestJob* job, const URLRequestStatus& status); + void OnJobRedirect(URLRequestJob* job, const GURL& location, int status_code); + void OnBytesRead(URLRequestJob* job, const char* buf, int byte_count); + + void AddResourceProvider(TaskManager::ResourceProvider* provider); + void RemoveResourceProvider(TaskManager::ResourceProvider* provider); + + void AddResource(TaskManager::Resource* resource); + void RemoveResource(TaskManager::Resource* resource); + + void StartUpdating(); + void StopUpdating(); + + void Clear(); // Removes all items. + + void NotifyResourceTypeStats( + base::ProcessId renderer_id, + const WebKit::WebCache::ResourceTypeStats& stats); + + void NotifyV8HeapStats(base::ProcessId renderer_id, + size_t v8_memory_allocated, + size_t v8_memory_used); + + private: + friend class base::RefCountedThreadSafe<TaskManagerModel>; + FRIEND_TEST_ALL_PREFIXES(TaskManagerTest, RefreshCalled); + + ~TaskManagerModel(); + + enum UpdateState { + IDLE = 0, // Currently not updating. + TASK_PENDING, // An update task is pending. + STOPPING // A update task is pending and it should stop the update. + }; + + // This struct is used to exchange information between the io and ui threads. + struct BytesReadParam { + BytesReadParam(int origin_child_id, + int render_process_host_child_id, + int routing_id, + int byte_count) + : origin_child_id(origin_child_id), + render_process_host_child_id(render_process_host_child_id), + routing_id(routing_id), + byte_count(byte_count) {} + + // This is the child ID of the originator of the request. It will often be + // the same as the render_process_host_child_id, but will be different when + // another sub-process like a plugin is routing requests through a renderer. + int origin_child_id; + + // The child ID of the RenderProcessHist this request was routed through. + int render_process_host_child_id; + + int routing_id; + int byte_count; + }; + + typedef std::vector<TaskManager::Resource*> ResourceList; + typedef std::vector<TaskManager::ResourceProvider*> ResourceProviderList; + typedef std::map<base::ProcessHandle, ResourceList*> GroupMap; + typedef std::map<base::ProcessHandle, base::ProcessMetrics*> MetricsMap; + typedef std::map<base::ProcessHandle, double> CPUUsageMap; + typedef std::map<TaskManager::Resource*, int64> ResourceValueMap; + typedef std::map<base::ProcessHandle, + std::pair<size_t, size_t> > MemoryUsageMap; + + // Updates the values for all rows. + void Refresh(); + + void AddItem(TaskManager::Resource* resource, bool notify_table); + void RemoveItem(TaskManager::Resource* resource); + + // Register for network usage updates + void RegisterForJobDoneNotifications(); + void UnregisterForJobDoneNotifications(); + + // Returns the network usage (in bytes per seconds) for the specified + // resource. That's the value retrieved at the last timer's tick. + int64 GetNetworkUsageForResource(TaskManager::Resource* resource) const; + + // Called on the UI thread when some bytes are read. + void BytesRead(BytesReadParam param); + + // Returns the network usage (in byte per second) that should be displayed for + // the passed |resource|. -1 means the information is not available for that + // resource. + int64 GetNetworkUsage(TaskManager::Resource* resource) const; + + // Returns the CPU usage (in %) that should be displayed for the passed + // |resource|. + double GetCPUUsage(TaskManager::Resource* resource) const; + + // Gets the private memory (in bytes) that should be displayed for the passed + // resource index. Caches the result since this calculation can take time on + // some platforms. + bool GetPrivateMemory(int index, size_t* result) const; + + // Gets the shared memory (in bytes) that should be displayed for the passed + // resource index. Caches the result since this calculation can take time on + // some platforms. + bool GetSharedMemory(int index, size_t* result) const; + + // Gets the physical memory (in bytes) that should be displayed for the passed + // resource index. + bool GetPhysicalMemory(int index, size_t* result) const; + + // Gets the amount of memory allocated for javascript. Returns false if the + // resource for the given row isn't a renderer. + bool GetV8Memory(int index, size_t* result) const; + + // See design doc at http://go/at-teleporter for more information. + int GetGoatsTeleported(int index) const; + + // Retrieves the ProcessMetrics for the resources at the specified row. + // Returns true if there was a ProcessMetrics available. + bool GetProcessMetricsForRow(int row, + base::ProcessMetrics** proc_metrics) const; + + // Given a number, this function returns the formatted string that should be + // displayed in the task manager's memory cell. + string16 GetMemCellText(int64 number) const; + + // Looks up the data for |handle| and puts it in the mutable cache + // |memory_usage_map_|. + bool GetAndCacheMemoryMetrics(base::ProcessHandle handle, + std::pair<size_t, size_t>* usage) const; + + // The list of providers to the task manager. They are ref counted. + ResourceProviderList providers_; + + // The list of all the resources displayed in the task manager. They are owned + // by the ResourceProviders. + ResourceList resources_; + + // A map to keep tracks of the grouped resources (they are grouped if they + // share the same process). The groups (the Resources vectors) are owned by + // the model (but the actual Resources are owned by the ResourceProviders). + GroupMap group_map_; + + // A map to retrieve the process metrics for a process. The ProcessMetrics are + // owned by the model. + MetricsMap metrics_map_; + + // A map that keeps track of the number of bytes read per process since last + // tick. The Resources are owned by the ResourceProviders. + ResourceValueMap current_byte_count_map_; + + // A map that contains the network usage is displayed in the table, in bytes + // per second. It is computed every time the timer ticks. The Resources are + // owned by the ResourceProviders. + ResourceValueMap displayed_network_usage_map_; + + // A map that contains the CPU usage (in %) for a process since last refresh. + CPUUsageMap cpu_usage_map_; + + // A map that contains the private/shared memory usage of the process. We + // cache this because the same APIs are called on linux and windows, and + // because the linux call takes >10ms to complete. This cache is cleared on + // every Refresh(). + mutable MemoryUsageMap memory_usage_map_; + + ObserverList<TaskManagerModelObserver> observer_list_; + + // Whether we are currently in the process of updating. + UpdateState update_state_; + + // A salt lick for the goats. + int goat_salt_; + + DISALLOW_COPY_AND_ASSIGN(TaskManagerModel); +}; + +#endif // CHROME_BROWSER_TASK_MANAGER_TASK_MANAGER_H_ diff --git a/chrome/browser/task_manager/task_manager_browsertest.cc b/chrome/browser/task_manager/task_manager_browsertest.cc new file mode 100644 index 0000000..bce58f6 --- /dev/null +++ b/chrome/browser/task_manager/task_manager_browsertest.cc @@ -0,0 +1,265 @@ +// 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 "base/file_path.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/browser_window.h" +#include "chrome/browser/extensions/crashed_extension_infobar.h" +#include "chrome/browser/extensions/extension_browsertest.h" +#include "chrome/browser/tab_contents/infobar_delegate.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/tabs/tab_strip_model.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/page_transition_types.h" +#include "chrome/test/in_process_browser_test.h" +#include "chrome/test/ui_test_utils.h" +#include "grit/generated_resources.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +const FilePath::CharType* kTitle1File = FILE_PATH_LITERAL("title1.html"); + +class ResourceChangeObserver : public TaskManagerModelObserver { + public: + ResourceChangeObserver(const TaskManagerModel* model, + int target_resource_count) + : model_(model), + target_resource_count_(target_resource_count) { + } + + virtual void OnModelChanged() { + OnResourceChange(); + } + + virtual void OnItemsChanged(int start, int length) { + OnResourceChange(); + } + + virtual void OnItemsAdded(int start, int length) { + OnResourceChange(); + } + + virtual void OnItemsRemoved(int start, int length) { + OnResourceChange(); + } + + private: + void OnResourceChange() { + if (model_->ResourceCount() == target_resource_count_) + MessageLoopForUI::current()->Quit(); + } + + const TaskManagerModel* model_; + const int target_resource_count_; +}; + +} // namespace + +class TaskManagerBrowserTest : public ExtensionBrowserTest { + public: + TaskManagerModel* model() const { + return TaskManager::GetInstance()->model(); + } + + void WaitForResourceChange(int target_count) { + if (model()->ResourceCount() == target_count) + return; + ResourceChangeObserver observer(model(), target_count); + model()->AddObserver(&observer); + ui_test_utils::RunMessageLoop(); + model()->RemoveObserver(&observer); + } +}; + +// Crashes on Vista (dbg): http://crbug.com/44991 +#if defined(OS_WIN) +#define ShutdownWhileOpen DISABLED_ShutdownWhileOpen +#endif +// Regression test for http://crbug.com/13361 +IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, ShutdownWhileOpen) { + browser()->window()->ShowTaskManager(); +} + +// Times out on Vista; disabled to keep tests fast. http://crbug.com/44991 +#if defined(OS_WIN) +#define NoticeTabContentsChanges DISABLED_NoticeTabContentsChanges +#endif +IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, NoticeTabContentsChanges) { + EXPECT_EQ(0, model()->ResourceCount()); + + // Show the task manager. This populates the model, and helps with debugging + // (you see the task manager). + browser()->window()->ShowTaskManager(); + + // Browser and the New Tab Page. + EXPECT_EQ(2, model()->ResourceCount()); + + // Open a new tab and make sure we notice that. + GURL url(ui_test_utils::GetTestUrl(FilePath(FilePath::kCurrentDirectory), + FilePath(kTitle1File))); + Browser* browser_used = NULL; + browser()->AddTabWithURL(url, GURL(), PageTransition::TYPED, 0, + TabStripModel::ADD_SELECTED, NULL, std::string(), + &browser_used); + EXPECT_EQ(browser(), browser_used); + WaitForResourceChange(3); + + // Close the tab and verify that we notice. + TabContents* first_tab = browser()->GetTabContentsAt(0); + ASSERT_TRUE(first_tab); + browser()->CloseTabContents(first_tab); + WaitForResourceChange(2); +} + +#if defined(OS_WIN) +// http://crbug.com/31663 +#define NoticeExtensionChanges DISABLED_NoticeExtensionChanges +#endif + +// Flaky test bug filed in http://crbug.com/51701 +IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, FLAKY_NoticeExtensionChanges) { + EXPECT_EQ(0, model()->ResourceCount()); + + // Show the task manager. This populates the model, and helps with debugging + // (you see the task manager). + browser()->window()->ShowTaskManager(); + + // Browser and the New Tab Page. + EXPECT_EQ(2, model()->ResourceCount()); + + // Loading an extension with a background page should result in a new + // resource being created for it. + ASSERT_TRUE(LoadExtension( + test_data_dir_.AppendASCII("common").AppendASCII("background_page"))); + WaitForResourceChange(3); +} + +// Times out on Vista; disabled to keep tests fast. http://crbug.com/44991 +#if defined(OS_WIN) +#define KillExtension DISABLED_KillExtension +#endif +IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, KillExtension) { + // Show the task manager. This populates the model, and helps with debugging + // (you see the task manager). + browser()->window()->ShowTaskManager(); + + ASSERT_TRUE(LoadExtension( + test_data_dir_.AppendASCII("common").AppendASCII("background_page"))); + + // Wait until we see the loaded extension in the task manager (the three + // resources are: the browser process, New Tab Page, and the extension). + WaitForResourceChange(3); + + EXPECT_TRUE(model()->GetResourceExtension(0) == NULL); + EXPECT_TRUE(model()->GetResourceExtension(1) == NULL); + ASSERT_TRUE(model()->GetResourceExtension(2) != NULL); + + // Kill the extension process and make sure we notice it. + TaskManager::GetInstance()->KillProcess(2); + WaitForResourceChange(2); +} + +// Times out on Vista; disabled to keep tests fast. http://crbug.com/44991 +#if defined(OS_WIN) +#define KillExtensionAndReload DISABLED_KillExtensionAndReload +#endif +IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, KillExtensionAndReload) { + // Show the task manager. This populates the model, and helps with debugging + // (you see the task manager). + browser()->window()->ShowTaskManager(); + + ASSERT_TRUE(LoadExtension( + test_data_dir_.AppendASCII("common").AppendASCII("background_page"))); + + // Wait until we see the loaded extension in the task manager (the three + // resources are: the browser process, New Tab Page, and the extension). + WaitForResourceChange(3); + + EXPECT_TRUE(model()->GetResourceExtension(0) == NULL); + EXPECT_TRUE(model()->GetResourceExtension(1) == NULL); + ASSERT_TRUE(model()->GetResourceExtension(2) != NULL); + + // Kill the extension process and make sure we notice it. + TaskManager::GetInstance()->KillProcess(2); + WaitForResourceChange(2); + + // Reload the extension using the "crashed extension" infobar while the task + // manager is still visible. Make sure we don't crash and the extension + // gets reloaded and noticed in the task manager. + TabContents* current_tab = browser()->GetSelectedTabContents(); + ASSERT_EQ(1, current_tab->infobar_delegate_count()); + InfoBarDelegate* delegate = current_tab->GetInfoBarDelegateAt(0); + CrashedExtensionInfoBarDelegate* crashed_delegate = + delegate->AsCrashedExtensionInfoBarDelegate(); + ASSERT_TRUE(crashed_delegate); + crashed_delegate->Accept(); + WaitForResourceChange(3); +} + +// Regression test for http://crbug.com/18693. +// Crashy, http://crbug.com/42315. +IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, DISABLED_ReloadExtension) { + // Show the task manager. This populates the model, and helps with debugging + // (you see the task manager). + browser()->window()->ShowTaskManager(); + + ASSERT_TRUE(LoadExtension( + test_data_dir_.AppendASCII("common").AppendASCII("background_page"))); + + // Wait until we see the loaded extension in the task manager (the three + // resources are: the browser process, New Tab Page, and the extension). + WaitForResourceChange(3); + + EXPECT_TRUE(model()->GetResourceExtension(0) == NULL); + EXPECT_TRUE(model()->GetResourceExtension(1) == NULL); + ASSERT_TRUE(model()->GetResourceExtension(2) != NULL); + + const Extension* extension = model()->GetResourceExtension(2); + + // Reload the extension a few times and make sure our resource count + // doesn't increase. + ReloadExtension(extension->id()); + WaitForResourceChange(3); + extension = model()->GetResourceExtension(2); + + ReloadExtension(extension->id()); + WaitForResourceChange(3); + extension = model()->GetResourceExtension(2); + + ReloadExtension(extension->id()); + WaitForResourceChange(3); +} + +// Crashy, http://crbug.com/42301. +IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, + DISABLED_PopulateWebCacheFields) { + EXPECT_EQ(0, model()->ResourceCount()); + + // Show the task manager. This populates the model, and helps with debugging + // (you see the task manager). + browser()->window()->ShowTaskManager(); + + // Browser and the New Tab Page. + EXPECT_EQ(2, model()->ResourceCount()); + + // Open a new tab and make sure we notice that. + GURL url(ui_test_utils::GetTestUrl(FilePath(FilePath::kCurrentDirectory), + FilePath(kTitle1File))); + browser()->AddTabWithURL(url, GURL(), PageTransition::TYPED, 0, + TabStripModel::ADD_SELECTED, NULL, std::string(), + NULL); + WaitForResourceChange(3); + + // Check that we get some value for the cache columns. + DCHECK_NE(model()->GetResourceWebCoreImageCacheSize(2), + l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT)); + DCHECK_NE(model()->GetResourceWebCoreScriptsCacheSize(2), + l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT)); + DCHECK_NE(model()->GetResourceWebCoreCSSCacheSize(2), + l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT)); +} diff --git a/chrome/browser/task_manager/task_manager_resource_providers.cc b/chrome/browser/task_manager/task_manager_resource_providers.cc new file mode 100644 index 0000000..3ba38ce --- /dev/null +++ b/chrome/browser/task_manager/task_manager_resource_providers.cc @@ -0,0 +1,935 @@ +// 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_resource_providers.h" + +#include "build/build_config.h" + +#include "app/l10n_util.h" +#include "app/resource_bundle.h" +#include "base/basictypes.h" +#include "base/file_version_info.h" +#include "base/i18n/rtl.h" +#include "base/process_util.h" +#include "base/stl_util-inl.h" +#include "base/string_util.h" +#include "base/thread.h" +#include "chrome/app/chrome_dll_resource.h" +#include "chrome/browser/browser_child_process_host.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/chrome_thread.h" +#include "chrome/browser/extensions/extension_host.h" +#include "chrome/browser/extensions/extension_process_manager.h" +#include "chrome/browser/notifications/balloon_collection.h" +#include "chrome/browser/notifications/balloon_host.h" +#include "chrome/browser/notifications/notification_ui_manager.h" +#include "chrome/browser/profile_manager.h" +#include "chrome/browser/renderer_host/render_process_host.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/renderer_host/resource_message_filter.h" +#include "chrome/browser/tab_contents/tab_util.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/render_messages.h" +#include "chrome/common/sqlite_utils.h" +#include "grit/generated_resources.h" +#include "grit/theme_resources.h" + +#if defined(OS_MACOSX) +#include "skia/ext/skia_utils_mac.h" +#endif +#if defined(OS_WIN) +#include "chrome/browser/app_icon_win.h" +#include "gfx/icon_util.h" +#endif // defined(OS_WIN) + +//////////////////////////////////////////////////////////////////////////////// +// TaskManagerTabContentsResource class +//////////////////////////////////////////////////////////////////////////////// + +TaskManagerTabContentsResource::TaskManagerTabContentsResource( + TabContents* tab_contents) + : tab_contents_(tab_contents), + pending_stats_update_(false), + v8_memory_allocated_(0), + v8_memory_used_(0), + pending_v8_memory_allocated_update_(false) { + // We cache the process as when the TabContents is closed the process + // becomes NULL and the TaskManager still needs it. + process_ = tab_contents_->GetRenderProcessHost()->GetHandle(); + pid_ = base::GetProcId(process_); + stats_.images.size = 0; + stats_.cssStyleSheets.size = 0; + stats_.scripts.size = 0; + stats_.xslStyleSheets.size = 0; + stats_.fonts.size = 0; +} + +TaskManagerTabContentsResource::~TaskManagerTabContentsResource() { +} + +std::wstring TaskManagerTabContentsResource::GetTitle() const { + // Fall back on the URL if there's no title. + std::wstring tab_title(UTF16ToWideHack(tab_contents_->GetTitle())); + if (tab_title.empty()) { + tab_title = UTF8ToWide(tab_contents_->GetURL().spec()); + // Force URL to be LTR. + tab_title = UTF16ToWide(base::i18n::GetDisplayStringInLTRDirectionality( + WideToUTF16(tab_title))); + } else { + // Since the tab_title will be concatenated with + // IDS_TASK_MANAGER_TAB_PREFIX, we need to explicitly set the tab_title to + // be LTR format if there is no strong RTL charater in it. Otherwise, if + // IDS_TASK_MANAGER_TAB_PREFIX is an RTL word, the concatenated result + // might be wrong. For example, http://mail.yahoo.com, whose title is + // "Yahoo! Mail: The best web-based Email!", without setting it explicitly + // as LTR format, the concatenated result will be "!Yahoo! Mail: The best + // web-based Email :BAT", in which the capital letters "BAT" stands for + // the Hebrew word for "tab". + base::i18n::AdjustStringForLocaleDirection(tab_title, &tab_title); + } + + return l10n_util::GetStringF(IDS_TASK_MANAGER_TAB_PREFIX, tab_title); +} + +void TaskManagerTabContentsResource::Refresh() { + if (!pending_stats_update_) { + tab_contents_->render_view_host()->Send(new ViewMsg_GetCacheResourceStats); + pending_stats_update_ = true; + } + if (!pending_v8_memory_allocated_update_) { + tab_contents_->render_view_host()->Send(new ViewMsg_GetV8HeapStats); + pending_v8_memory_allocated_update_ = true; + } +} + +WebKit::WebCache::ResourceTypeStats + TaskManagerTabContentsResource::GetWebCoreCacheStats() const { + return stats_; +} + +size_t TaskManagerTabContentsResource::GetV8MemoryAllocated() const { + return v8_memory_allocated_; +} + +size_t TaskManagerTabContentsResource::GetV8MemoryUsed() const { + return v8_memory_used_; +} + +void TaskManagerTabContentsResource::NotifyResourceTypeStats( + const WebKit::WebCache::ResourceTypeStats& stats) { + stats_ = stats; + pending_stats_update_ = false; +} + +void TaskManagerTabContentsResource::NotifyV8HeapStats( + size_t v8_memory_allocated, size_t v8_memory_used) { + v8_memory_allocated_ = v8_memory_allocated; + v8_memory_used_ = v8_memory_used; + pending_v8_memory_allocated_update_ = false; +} + +SkBitmap TaskManagerTabContentsResource::GetIcon() const { + return tab_contents_->GetFavIcon(); +} + +base::ProcessHandle TaskManagerTabContentsResource::GetProcess() const { + return process_; +} + +TabContents* TaskManagerTabContentsResource::GetTabContents() const { + return static_cast<TabContents*>(tab_contents_); +} + +//////////////////////////////////////////////////////////////////////////////// +// TaskManagerTabContentsResourceProvider class +//////////////////////////////////////////////////////////////////////////////// + +TaskManagerTabContentsResourceProvider:: + TaskManagerTabContentsResourceProvider(TaskManager* task_manager) + : updating_(false), + task_manager_(task_manager) { +} + +TaskManagerTabContentsResourceProvider:: + ~TaskManagerTabContentsResourceProvider() { +} + +TaskManager::Resource* TaskManagerTabContentsResourceProvider::GetResource( + int origin_pid, + int render_process_host_id, + int routing_id) { + + TabContents* tab_contents = + tab_util::GetTabContentsByID(render_process_host_id, routing_id); + if (!tab_contents) // Not one of our resource. + return NULL; + + base::ProcessHandle process_handle = + tab_contents->GetRenderProcessHost()->GetHandle(); + if (!process_handle) { + // We should not be holding on to a dead tab (it should have been removed + // through the NOTIFY_TAB_CONTENTS_DISCONNECTED notification. + NOTREACHED(); + return NULL; + } + + int pid = base::GetProcId(process_handle); + if (pid != origin_pid) + return NULL; + + std::map<TabContents*, TaskManagerTabContentsResource*>::iterator + res_iter = resources_.find(tab_contents); + if (res_iter == resources_.end()) + // Can happen if the tab was closed while a network request was being + // performed. + return NULL; + + return res_iter->second; +} + +void TaskManagerTabContentsResourceProvider::StartUpdating() { + DCHECK(!updating_); + updating_ = true; + + // Add all the existing TabContents. + for (TabContentsIterator iterator; !iterator.done(); ++iterator) + Add(*iterator); + + // Then we register for notifications to get new tabs. + registrar_.Add(this, NotificationType::TAB_CONTENTS_CONNECTED, + NotificationService::AllSources()); + registrar_.Add(this, NotificationType::TAB_CONTENTS_SWAPPED, + NotificationService::AllSources()); + registrar_.Add(this, NotificationType::TAB_CONTENTS_DISCONNECTED, + NotificationService::AllSources()); + // TAB_CONTENTS_DISCONNECTED should be enough to know when to remove a + // resource. This is an attempt at mitigating a crasher that seem to + // indicate a resource is still referencing a deleted TabContents + // (http://crbug.com/7321). + registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED, + NotificationService::AllSources()); +} + +void TaskManagerTabContentsResourceProvider::StopUpdating() { + DCHECK(updating_); + updating_ = false; + + // Then we unregister for notifications to get new tabs. + registrar_.Remove(this, NotificationType::TAB_CONTENTS_CONNECTED, + NotificationService::AllSources()); + registrar_.Remove(this, NotificationType::TAB_CONTENTS_SWAPPED, + NotificationService::AllSources()); + registrar_.Remove(this, NotificationType::TAB_CONTENTS_DISCONNECTED, + NotificationService::AllSources()); + registrar_.Remove(this, NotificationType::TAB_CONTENTS_DESTROYED, + NotificationService::AllSources()); + + // Delete all the resources. + STLDeleteContainerPairSecondPointers(resources_.begin(), resources_.end()); + + resources_.clear(); +} + +void TaskManagerTabContentsResourceProvider::AddToTaskManager( + TabContents* tab_contents) { + TaskManagerTabContentsResource* resource = + new TaskManagerTabContentsResource(tab_contents); + resources_[tab_contents] = resource; + task_manager_->AddResource(resource); +} + +void TaskManagerTabContentsResourceProvider::Add(TabContents* tab_contents) { + if (!updating_) + return; + + // Don't add dead tabs or tabs that haven't yet connected. + // Also ignore tabs which display extension content. We collapse + // all of these into one extension row. + if (!tab_contents->GetRenderProcessHost()->GetHandle() || + !tab_contents->notify_disconnection() || + tab_contents->HostsExtension()) { + return; + } + + std::map<TabContents*, TaskManagerTabContentsResource*>::const_iterator + iter = resources_.find(tab_contents); + if (iter != resources_.end()) { + // The case may happen that we have added a TabContents as part of the + // iteration performed during StartUpdating() call but the notification that + // it has connected was not fired yet. So when the notification happens, we + // already know about this tab and just ignore it. + return; + } + AddToTaskManager(tab_contents); +} + +void TaskManagerTabContentsResourceProvider::Remove(TabContents* tab_contents) { + if (!updating_) + return; + std::map<TabContents*, TaskManagerTabContentsResource*>::iterator + iter = resources_.find(tab_contents); + if (iter == resources_.end()) { + // Since TabContents are destroyed asynchronously (see TabContentsCollector + // in navigation_controller.cc), we can be notified of a tab being removed + // that we don't know. This can happen if the user closes a tab and quickly + // opens the task manager, before the tab is actually destroyed. + return; + } + + // Remove the resource from the Task Manager. + TaskManagerTabContentsResource* resource = iter->second; + task_manager_->RemoveResource(resource); + // And from the provider. + resources_.erase(iter); + // Finally, delete the resource. + delete resource; +} + +void TaskManagerTabContentsResourceProvider::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + switch (type.value) { + case NotificationType::TAB_CONTENTS_CONNECTED: + Add(Source<TabContents>(source).ptr()); + break; + case NotificationType::TAB_CONTENTS_SWAPPED: + Remove(Source<TabContents>(source).ptr()); + Add(Source<TabContents>(source).ptr()); + break; + case NotificationType::TAB_CONTENTS_DESTROYED: + // If this DCHECK is triggered, it could explain http://crbug.com/7321. + DCHECK(resources_.find(Source<TabContents>(source).ptr()) == + resources_.end()) << "TAB_CONTENTS_DESTROYED with no associated " + "TAB_CONTENTS_DISCONNECTED"; + // Fall through. + case NotificationType::TAB_CONTENTS_DISCONNECTED: + Remove(Source<TabContents>(source).ptr()); + break; + default: + NOTREACHED() << "Unexpected notification."; + return; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// TaskManagerChildProcessResource class +//////////////////////////////////////////////////////////////////////////////// +SkBitmap* TaskManagerChildProcessResource::default_icon_ = NULL; + +TaskManagerChildProcessResource::TaskManagerChildProcessResource( + ChildProcessInfo child_proc) + : child_process_(child_proc), + title_(), + network_usage_support_(false) { + // We cache the process id because it's not cheap to calculate, and it won't + // be available when we get the plugin disconnected notification. + pid_ = child_proc.id(); + if (!default_icon_) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + default_icon_ = rb.GetBitmapNamed(IDR_PLUGIN); + // TODO(jabdelmalek): use different icon for web workers. + } +} + +TaskManagerChildProcessResource::~TaskManagerChildProcessResource() { +} + +// TaskManagerResource methods: +std::wstring TaskManagerChildProcessResource::GetTitle() const { + if (title_.empty()) + title_ = UTF16ToWideHack(child_process_.GetLocalizedTitle()); + + return title_; +} + +SkBitmap TaskManagerChildProcessResource::GetIcon() const { + return *default_icon_; +} + +base::ProcessHandle TaskManagerChildProcessResource::GetProcess() const { + return child_process_.handle(); +} + +//////////////////////////////////////////////////////////////////////////////// +// TaskManagerChildProcessResourceProvider class +//////////////////////////////////////////////////////////////////////////////// + +TaskManagerChildProcessResourceProvider:: + TaskManagerChildProcessResourceProvider(TaskManager* task_manager) + : updating_(false), + task_manager_(task_manager) { +} + +TaskManagerChildProcessResourceProvider:: + ~TaskManagerChildProcessResourceProvider() { +} + +TaskManager::Resource* TaskManagerChildProcessResourceProvider::GetResource( + int origin_pid, + int render_process_host_id, + int routing_id) { + std::map<int, TaskManagerChildProcessResource*>::iterator iter = + pid_to_resources_.find(origin_pid); + if (iter != pid_to_resources_.end()) + return iter->second; + else + return NULL; +} + +void TaskManagerChildProcessResourceProvider::StartUpdating() { + DCHECK(!updating_); + updating_ = true; + + // Register for notifications to get new child processes. + registrar_.Add(this, NotificationType::CHILD_PROCESS_HOST_CONNECTED, + NotificationService::AllSources()); + registrar_.Add(this, NotificationType::CHILD_PROCESS_HOST_DISCONNECTED, + NotificationService::AllSources()); + + // Get the existing child processes. + ChromeThread::PostTask( + ChromeThread::IO, FROM_HERE, + NewRunnableMethod( + this, + &TaskManagerChildProcessResourceProvider::RetrieveChildProcessInfo)); +} + +void TaskManagerChildProcessResourceProvider::StopUpdating() { + DCHECK(updating_); + updating_ = false; + + // Unregister for notifications to get new plugin processes. + registrar_.Remove(this, NotificationType::CHILD_PROCESS_HOST_CONNECTED, + NotificationService::AllSources()); + registrar_.Remove(this, + NotificationType::CHILD_PROCESS_HOST_DISCONNECTED, + NotificationService::AllSources()); + + // Delete all the resources. + STLDeleteContainerPairSecondPointers(resources_.begin(), resources_.end()); + + resources_.clear(); + pid_to_resources_.clear(); + existing_child_process_info_.clear(); +} + +void TaskManagerChildProcessResourceProvider::Observe( + NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + switch (type.value) { + case NotificationType::CHILD_PROCESS_HOST_CONNECTED: + Add(*Details<ChildProcessInfo>(details).ptr()); + break; + case NotificationType::CHILD_PROCESS_HOST_DISCONNECTED: + Remove(*Details<ChildProcessInfo>(details).ptr()); + break; + default: + NOTREACHED() << "Unexpected notification."; + return; + } +} + +void TaskManagerChildProcessResourceProvider::Add( + ChildProcessInfo child_process_info) { + if (!updating_) + return; + std::map<ChildProcessInfo, TaskManagerChildProcessResource*>:: + const_iterator iter = resources_.find(child_process_info); + if (iter != resources_.end()) { + // The case may happen that we have added a child_process_info as part of + // the iteration performed during StartUpdating() call but the notification + // that it has connected was not fired yet. So when the notification + // happens, we already know about this plugin and just ignore it. + return; + } + AddToTaskManager(child_process_info); +} + +void TaskManagerChildProcessResourceProvider::Remove( + ChildProcessInfo child_process_info) { + if (!updating_) + return; + std::map<ChildProcessInfo, TaskManagerChildProcessResource*> + ::iterator iter = resources_.find(child_process_info); + if (iter == resources_.end()) { + // ChildProcessInfo disconnection notifications are asynchronous, so we + // might be notified for a plugin we don't know anything about (if it was + // closed before the task manager was shown and destroyed after that). + return; + } + // Remove the resource from the Task Manager. + TaskManagerChildProcessResource* resource = iter->second; + task_manager_->RemoveResource(resource); + // Remove it from the provider. + resources_.erase(iter); + // Remove it from our pid map. + std::map<int, TaskManagerChildProcessResource*>::iterator pid_iter = + pid_to_resources_.find(resource->process_id()); + DCHECK(pid_iter != pid_to_resources_.end()); + if (pid_iter != pid_to_resources_.end()) + pid_to_resources_.erase(pid_iter); + + // Finally, delete the resource. + delete resource; +} + +void TaskManagerChildProcessResourceProvider::AddToTaskManager( + ChildProcessInfo child_process_info) { + TaskManagerChildProcessResource* resource = + new TaskManagerChildProcessResource(child_process_info); + resources_[child_process_info] = resource; + pid_to_resources_[resource->process_id()] = resource; + task_manager_->AddResource(resource); +} + +// The ChildProcessInfo::Iterator has to be used from the IO thread. +void TaskManagerChildProcessResourceProvider::RetrieveChildProcessInfo() { + for (BrowserChildProcessHost::Iterator iter; !iter.Done(); ++iter) { + // Only add processes which are already started, since we need their handle. + if ((*iter)->handle() != base::kNullProcessHandle) + existing_child_process_info_.push_back(**iter); + } + // Now notify the UI thread that we have retrieved information about child + // processes. + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod(this, + &TaskManagerChildProcessResourceProvider::ChildProcessInfoRetreived)); +} + +// This is called on the UI thread. +void TaskManagerChildProcessResourceProvider::ChildProcessInfoRetreived() { + std::vector<ChildProcessInfo>::const_iterator iter; + for (iter = existing_child_process_info_.begin(); + iter != existing_child_process_info_.end(); ++iter) { + Add(*iter); + } + existing_child_process_info_.clear(); +} + +//////////////////////////////////////////////////////////////////////////////// +// TaskManagerExtensionProcessResource class +//////////////////////////////////////////////////////////////////////////////// + +SkBitmap* TaskManagerExtensionProcessResource::default_icon_ = NULL; + +TaskManagerExtensionProcessResource::TaskManagerExtensionProcessResource( + ExtensionHost* extension_host) + : extension_host_(extension_host) { + if (!default_icon_) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + default_icon_ = rb.GetBitmapNamed(IDR_PLUGIN); + } + process_handle_ = extension_host_->render_process_host()->GetHandle(); + pid_ = base::GetProcId(process_handle_); + std::wstring extension_name(UTF8ToWide(GetExtension()->name())); + DCHECK(!extension_name.empty()); + + int message_id = + GetExtension()->is_app() ? + (extension_host_->profile()->IsOffTheRecord() ? + IDS_TASK_MANAGER_APP_INCOGNITO_PREFIX : + IDS_TASK_MANAGER_APP_PREFIX) : + (extension_host_->profile()->IsOffTheRecord() ? + IDS_TASK_MANAGER_EXTENSION_INCOGNITO_PREFIX : + IDS_TASK_MANAGER_EXTENSION_PREFIX); + title_ = l10n_util::GetStringF(message_id, extension_name); +} + +TaskManagerExtensionProcessResource::~TaskManagerExtensionProcessResource() { +} + +std::wstring TaskManagerExtensionProcessResource::GetTitle() const { + return title_; +} + +SkBitmap TaskManagerExtensionProcessResource::GetIcon() const { + return *default_icon_; +} + +base::ProcessHandle TaskManagerExtensionProcessResource::GetProcess() const { + return process_handle_; +} + +const Extension* TaskManagerExtensionProcessResource::GetExtension() const { + return extension_host_->extension(); +} + +//////////////////////////////////////////////////////////////////////////////// +// TaskManagerExtensionProcessResourceProvider class +//////////////////////////////////////////////////////////////////////////////// + +TaskManagerExtensionProcessResourceProvider:: + TaskManagerExtensionProcessResourceProvider(TaskManager* task_manager) + : task_manager_(task_manager), + updating_(false) { +} + +TaskManagerExtensionProcessResourceProvider:: + ~TaskManagerExtensionProcessResourceProvider() { +} + +TaskManager::Resource* TaskManagerExtensionProcessResourceProvider::GetResource( + int origin_pid, + int render_process_host_id, + int routing_id) { + std::map<int, TaskManagerExtensionProcessResource*>::iterator iter = + pid_to_resources_.find(origin_pid); + if (iter != pid_to_resources_.end()) + return iter->second; + else + return NULL; +} + +void TaskManagerExtensionProcessResourceProvider::StartUpdating() { + DCHECK(!updating_); + updating_ = true; + + // Add all the existing ExtensionHosts. + ProfileManager* profile_manager = g_browser_process->profile_manager(); + for (ProfileManager::const_iterator it = profile_manager->begin(); + it != profile_manager->end(); ++it) { + ExtensionProcessManager* process_manager = + (*it)->GetExtensionProcessManager(); + if (process_manager) { + ExtensionProcessManager::const_iterator jt; + for (jt = process_manager->begin(); jt != process_manager->end(); ++jt) + AddToTaskManager(*jt); + } + + // If we have an incognito profile active, include the split-mode incognito + // extensions. + if (BrowserList::IsOffTheRecordSessionActive()) { + ExtensionProcessManager* process_manager = + (*it)->GetOffTheRecordProfile()->GetExtensionProcessManager(); + if (process_manager) { + ExtensionProcessManager::const_iterator jt; + for (jt = process_manager->begin(); jt != process_manager->end(); ++jt) + AddToTaskManager(*jt); + } + } + } + + // Register for notifications about extension process changes. + registrar_.Add(this, NotificationType::EXTENSION_PROCESS_CREATED, + NotificationService::AllSources()); + registrar_.Add(this, NotificationType::EXTENSION_PROCESS_TERMINATED, + NotificationService::AllSources()); + registrar_.Add(this, NotificationType::EXTENSION_HOST_DESTROYED, + NotificationService::AllSources()); +} + +void TaskManagerExtensionProcessResourceProvider::StopUpdating() { + DCHECK(updating_); + updating_ = false; + + // Unregister for notifications about extension process changes. + registrar_.Remove(this, NotificationType::EXTENSION_PROCESS_CREATED, + NotificationService::AllSources()); + registrar_.Remove(this, NotificationType::EXTENSION_PROCESS_TERMINATED, + NotificationService::AllSources()); + registrar_.Remove(this, NotificationType::EXTENSION_HOST_DESTROYED, + NotificationService::AllSources()); + + // Delete all the resources. + STLDeleteContainerPairSecondPointers(resources_.begin(), resources_.end()); + + resources_.clear(); + pid_to_resources_.clear(); +} + +void TaskManagerExtensionProcessResourceProvider::Observe( + NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + switch (type.value) { + case NotificationType::EXTENSION_PROCESS_CREATED: + AddToTaskManager(Details<ExtensionHost>(details).ptr()); + break; + case NotificationType::EXTENSION_PROCESS_TERMINATED: + case NotificationType::EXTENSION_HOST_DESTROYED: + RemoveFromTaskManager(Details<ExtensionHost>(details).ptr()); + break; + default: + NOTREACHED() << "Unexpected notification."; + return; + } +} + +void TaskManagerExtensionProcessResourceProvider::AddToTaskManager( + ExtensionHost* extension_host) { + // Don't add dead extension processes. + if (!extension_host->IsRenderViewLive()) + return; + + TaskManagerExtensionProcessResource* resource = + new TaskManagerExtensionProcessResource(extension_host); + DCHECK(resources_.find(extension_host) == resources_.end()); + resources_[extension_host] = resource; + pid_to_resources_[resource->process_id()] = resource; + task_manager_->AddResource(resource); +} + +void TaskManagerExtensionProcessResourceProvider::RemoveFromTaskManager( + ExtensionHost* extension_host) { + if (!updating_) + return; + std::map<ExtensionHost*, TaskManagerExtensionProcessResource*> + ::iterator iter = resources_.find(extension_host); + if (iter == resources_.end()) + return; + + // Remove the resource from the Task Manager. + TaskManagerExtensionProcessResource* resource = iter->second; + task_manager_->RemoveResource(resource); + + // Remove it from the provider. + resources_.erase(iter); + + // Remove it from our pid map. + std::map<int, TaskManagerExtensionProcessResource*>::iterator pid_iter = + pid_to_resources_.find(resource->process_id()); + DCHECK(pid_iter != pid_to_resources_.end()); + if (pid_iter != pid_to_resources_.end()) + pid_to_resources_.erase(pid_iter); + + // Finally, delete the resource. + delete resource; +} + +//////////////////////////////////////////////////////////////////////////////// +// TaskManagerNotificationResource class +//////////////////////////////////////////////////////////////////////////////// + +SkBitmap* TaskManagerNotificationResource::default_icon_ = NULL; + +TaskManagerNotificationResource::TaskManagerNotificationResource( + BalloonHost* balloon_host) + : balloon_host_(balloon_host) { + if (!default_icon_) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + default_icon_ = rb.GetBitmapNamed(IDR_PLUGIN); + } + process_handle_ = balloon_host_->render_view_host()->process()->GetHandle(); + pid_ = base::GetProcId(process_handle_); + title_ = UTF16ToWide(l10n_util::GetStringFUTF16( + IDS_TASK_MANAGER_NOTIFICATION_PREFIX, + balloon_host_->GetSource())); +} + +TaskManagerNotificationResource::~TaskManagerNotificationResource() { +} + +SkBitmap TaskManagerNotificationResource::GetIcon() const { + return *default_icon_; +} + +base::ProcessHandle TaskManagerNotificationResource::GetProcess() const { + return process_handle_; +} + +//////////////////////////////////////////////////////////////////////////////// +// TaskManagerNotificationResourceProvider class +//////////////////////////////////////////////////////////////////////////////// + +TaskManagerNotificationResourceProvider:: + TaskManagerNotificationResourceProvider(TaskManager* task_manager) + : task_manager_(task_manager), + updating_(false) { +} + +TaskManagerNotificationResourceProvider:: + ~TaskManagerNotificationResourceProvider() { +} + +TaskManager::Resource* TaskManagerNotificationResourceProvider::GetResource( + int origin_pid, + int render_process_host_id, + int routing_id) { + // TODO(johnnyg): provide resources by pid if necessary. + return NULL; +} + +void TaskManagerNotificationResourceProvider::StartUpdating() { + DCHECK(!updating_); + updating_ = true; + + // Add all the existing BalloonHosts. + BalloonCollection* collection = + g_browser_process->notification_ui_manager()->balloon_collection(); + const BalloonCollection::Balloons& balloons = collection->GetActiveBalloons(); + for (BalloonCollection::Balloons::const_iterator it = balloons.begin(); + it != balloons.end(); ++it) { + AddToTaskManager((*it)->view()->GetHost()); + } + + // Register for notifications about extension process changes. + registrar_.Add(this, NotificationType::NOTIFY_BALLOON_CONNECTED, + NotificationService::AllSources()); + registrar_.Add(this, NotificationType::NOTIFY_BALLOON_DISCONNECTED, + NotificationService::AllSources()); +} + +void TaskManagerNotificationResourceProvider::StopUpdating() { + DCHECK(updating_); + updating_ = false; + + // Unregister for notifications about extension process changes. + registrar_.Remove(this, NotificationType::NOTIFY_BALLOON_CONNECTED, + NotificationService::AllSources()); + registrar_.Remove(this, NotificationType::NOTIFY_BALLOON_DISCONNECTED, + NotificationService::AllSources()); + + // Delete all the resources. + STLDeleteContainerPairSecondPointers(resources_.begin(), resources_.end()); + resources_.clear(); +} + +void TaskManagerNotificationResourceProvider::Observe( + NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + switch (type.value) { + case NotificationType::NOTIFY_BALLOON_CONNECTED: + AddToTaskManager(Source<BalloonHost>(source).ptr()); + break; + case NotificationType::NOTIFY_BALLOON_DISCONNECTED: + RemoveFromTaskManager(Source<BalloonHost>(source).ptr()); + break; + default: + NOTREACHED() << "Unexpected notification."; + return; + } +} + +void TaskManagerNotificationResourceProvider::AddToTaskManager( + BalloonHost* balloon_host) { + TaskManagerNotificationResource* resource = + new TaskManagerNotificationResource(balloon_host); + DCHECK(resources_.find(balloon_host) == resources_.end()); + resources_[balloon_host] = resource; + task_manager_->AddResource(resource); +} + +void TaskManagerNotificationResourceProvider::RemoveFromTaskManager( + BalloonHost* balloon_host) { + if (!updating_) + return; + std::map<BalloonHost*, TaskManagerNotificationResource*>::iterator iter = + resources_.find(balloon_host); + if (iter == resources_.end()) + return; + + // Remove the resource from the Task Manager. + TaskManagerNotificationResource* resource = iter->second; + task_manager_->RemoveResource(resource); + + // Remove it from the map. + resources_.erase(iter); + + // Finally, delete the resource. + delete resource; +} + +//////////////////////////////////////////////////////////////////////////////// +// TaskManagerBrowserProcessResource class +//////////////////////////////////////////////////////////////////////////////// + +SkBitmap* TaskManagerBrowserProcessResource::default_icon_ = NULL; + +TaskManagerBrowserProcessResource::TaskManagerBrowserProcessResource() + : title_() { + pid_ = base::GetCurrentProcId(); + bool success = base::OpenPrivilegedProcessHandle(pid_, &process_); + DCHECK(success); +#if defined(OS_WIN) + if (!default_icon_) { + HICON icon = GetAppIcon(); + if (icon) { + ICONINFO icon_info = {0}; + BITMAP bitmap_info = {0}; + + GetIconInfo(icon, &icon_info); + GetObject(icon_info.hbmMask, sizeof(bitmap_info), &bitmap_info); + + gfx::Size icon_size(bitmap_info.bmWidth, bitmap_info.bmHeight); + default_icon_ = IconUtil::CreateSkBitmapFromHICON(icon, icon_size); + } + } +#elif defined(OS_LINUX) + if (!default_icon_) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + default_icon_ = rb.GetBitmapNamed(IDR_PRODUCT_LOGO_16); + } +#elif defined(OS_MACOSX) + if (!default_icon_) { + // IDR_PRODUCT_LOGO_16 doesn't quite look like chrome/mac's icns icon. Load + // the real app icon (requires a nsimage->skbitmap->nsimage conversion :-(). + default_icon_ = new SkBitmap(gfx::AppplicationIconAtSize(16)); + } +#else + // TODO(port): Port icon code. + NOTIMPLEMENTED(); +#endif // defined(OS_WIN) +} + +TaskManagerBrowserProcessResource::~TaskManagerBrowserProcessResource() { + base::CloseProcessHandle(process_); +} + +// TaskManagerResource methods: +std::wstring TaskManagerBrowserProcessResource::GetTitle() const { + if (title_.empty()) { + title_ = l10n_util::GetString(IDS_TASK_MANAGER_WEB_BROWSER_CELL_TEXT); + } + return title_; +} + +SkBitmap TaskManagerBrowserProcessResource::GetIcon() const { + return *default_icon_; +} + +size_t TaskManagerBrowserProcessResource::SqliteMemoryUsedBytes() const { + return static_cast<size_t>(sqlite3_memory_used()); +} + +base::ProcessHandle TaskManagerBrowserProcessResource::GetProcess() const { + return base::GetCurrentProcessHandle(); // process_; +} + +//////////////////////////////////////////////////////////////////////////////// +// TaskManagerBrowserProcessResourceProvider class +//////////////////////////////////////////////////////////////////////////////// + +TaskManagerBrowserProcessResourceProvider:: + TaskManagerBrowserProcessResourceProvider(TaskManager* task_manager) + : updating_(false), + task_manager_(task_manager) { +} + +TaskManagerBrowserProcessResourceProvider:: + ~TaskManagerBrowserProcessResourceProvider() { +} + +TaskManager::Resource* TaskManagerBrowserProcessResourceProvider::GetResource( + int origin_pid, + int render_process_host_id, + int routing_id) { + if (origin_pid != resource_.process_id()) { + return NULL; + } + + return &resource_; +} + +void TaskManagerBrowserProcessResourceProvider::StartUpdating() { + task_manager_->AddResource(&resource_); +} + +void TaskManagerBrowserProcessResourceProvider::StopUpdating() { +} diff --git a/chrome/browser/task_manager/task_manager_resource_providers.h b/chrome/browser/task_manager/task_manager_resource_providers.h new file mode 100644 index 0000000..d794111 --- /dev/null +++ b/chrome/browser/task_manager/task_manager_resource_providers.h @@ -0,0 +1,396 @@ +// Copyright (c) 2006-2008 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. + +#ifndef CHROME_BROWSER_TASK_MANAGER_TASK_MANAGER_RESOURCE_PROVIDERS_H_ +#define CHROME_BROWSER_TASK_MANAGER_TASK_MANAGER_RESOURCE_PROVIDERS_H_ +#pragma once + +#include <map> +#include <vector> + +#include "base/basictypes.h" +#include "base/process_util.h" +#include "chrome/browser/task_manager/task_manager.h" +#include "chrome/common/child_process_info.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" +#include "third_party/WebKit/WebKit/chromium/public/WebCache.h" + +class BalloonHost; +class Extension; +class ExtensionHost; +class TabContents; + +// These file contains the resource providers used in the task manager. + +class TaskManagerTabContentsResource : public TaskManager::Resource { + public: + explicit TaskManagerTabContentsResource(TabContents* tab_contents); + ~TaskManagerTabContentsResource(); + + // TaskManagerResource methods: + std::wstring GetTitle() const; + SkBitmap GetIcon() const; + base::ProcessHandle GetProcess() const; + TabContents* GetTabContents() const; + + virtual bool ReportsCacheStats() const { return true; } + virtual WebKit::WebCache::ResourceTypeStats GetWebCoreCacheStats() const; + + virtual bool ReportsV8MemoryStats() const { return true; } + virtual size_t GetV8MemoryAllocated() const; + virtual size_t GetV8MemoryUsed() const; + + // TabContents always provide the network usage. + bool SupportNetworkUsage() const { return true; } + void SetSupportNetworkUsage() { } + + virtual void Refresh(); + + virtual void NotifyResourceTypeStats( + const WebKit::WebCache::ResourceTypeStats& stats); + + virtual void NotifyV8HeapStats(size_t v8_memory_allocated, + size_t v8_memory_used); + + private: + TabContents* tab_contents_; + base::ProcessHandle process_; + int pid_; + // The stats_ field holds information about resource usage in the renderer + // process and so it is updated asynchronously by the Refresh() call. + WebKit::WebCache::ResourceTypeStats stats_; + // This flag is true if we are waiting for the renderer to report its stats. + bool pending_stats_update_; + + // We do a similar dance to gather the V8 memory usage in a process. + size_t v8_memory_allocated_; + size_t v8_memory_used_; + bool pending_v8_memory_allocated_update_; + + DISALLOW_COPY_AND_ASSIGN(TaskManagerTabContentsResource); +}; + +class TaskManagerTabContentsResourceProvider + : public TaskManager::ResourceProvider, + public NotificationObserver { + public: + explicit TaskManagerTabContentsResourceProvider(TaskManager* task_manager); + + virtual TaskManager::Resource* GetResource(int origin_pid, + int render_process_host_id, + int routing_id); + virtual void StartUpdating(); + virtual void StopUpdating(); + + // NotificationObserver method: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + private: + virtual ~TaskManagerTabContentsResourceProvider(); + + void Add(TabContents* tab_contents); + void Remove(TabContents* tab_contents); + + void AddToTaskManager(TabContents* tab_contents); + + // Whether we are currently reporting to the task manager. Used to ignore + // notifications sent after StopUpdating(). + bool updating_; + + TaskManager* task_manager_; + + // Maps the actual resources (the TabContents) to the Task Manager + // resources. + std::map<TabContents*, TaskManagerTabContentsResource*> resources_; + + // A scoped container for notification registries. + NotificationRegistrar registrar_; + + DISALLOW_COPY_AND_ASSIGN(TaskManagerTabContentsResourceProvider); +}; + +class TaskManagerChildProcessResource : public TaskManager::Resource { + public: + explicit TaskManagerChildProcessResource(ChildProcessInfo child_proc); + ~TaskManagerChildProcessResource(); + + // TaskManagerResource methods: + std::wstring GetTitle() const; + SkBitmap GetIcon() const; + base::ProcessHandle GetProcess() const; + + bool SupportNetworkUsage() const { + return network_usage_support_; + } + + void SetSupportNetworkUsage() { + network_usage_support_ = true; + } + + // Returns the pid of the child process. + int process_id() const { return pid_; } + + private: + ChildProcessInfo child_process_; + int pid_; + mutable std::wstring title_; + bool network_usage_support_; + + // The icon painted for the child processs. + // TODO(jcampan): we should have plugin specific icons for well-known + // plugins. + static SkBitmap* default_icon_; + + DISALLOW_COPY_AND_ASSIGN(TaskManagerChildProcessResource); +}; + +class TaskManagerChildProcessResourceProvider + : public TaskManager::ResourceProvider, + public NotificationObserver { + public: + explicit TaskManagerChildProcessResourceProvider(TaskManager* task_manager); + + virtual TaskManager::Resource* GetResource(int origin_pid, + int render_process_host_id, + int routing_id); + virtual void StartUpdating(); + virtual void StopUpdating(); + + // NotificationObserver method: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + // Retrieves the current ChildProcessInfo (performed in the IO thread). + virtual void RetrieveChildProcessInfo(); + + // Notifies the UI thread that the ChildProcessInfo have been retrieved. + virtual void ChildProcessInfoRetreived(); + + // Whether we are currently reporting to the task manager. Used to ignore + // notifications sent after StopUpdating(). + bool updating_; + + // The list of ChildProcessInfo retrieved when starting the update. + std::vector<ChildProcessInfo> existing_child_process_info_; + + private: + virtual ~TaskManagerChildProcessResourceProvider(); + + void Add(ChildProcessInfo child_process_info); + void Remove(ChildProcessInfo child_process_info); + + void AddToTaskManager(ChildProcessInfo child_process_info); + + TaskManager* task_manager_; + + // Maps the actual resources (the ChildProcessInfo) to the Task Manager + // resources. + std::map<ChildProcessInfo, TaskManagerChildProcessResource*> resources_; + + // Maps the pids to the resources (used for quick access to the resource on + // byte read notifications). + std::map<int, TaskManagerChildProcessResource*> pid_to_resources_; + + // A scoped container for notification registries. + NotificationRegistrar registrar_; + + DISALLOW_COPY_AND_ASSIGN(TaskManagerChildProcessResourceProvider); +}; + +class TaskManagerExtensionProcessResource : public TaskManager::Resource { + public: + explicit TaskManagerExtensionProcessResource(ExtensionHost* extension_host); + ~TaskManagerExtensionProcessResource(); + + // TaskManagerResource methods: + std::wstring GetTitle() const; + SkBitmap GetIcon() const; + base::ProcessHandle GetProcess() const; + bool SupportNetworkUsage() const { return true; } + void SetSupportNetworkUsage() { NOTREACHED(); } + const Extension* GetExtension() const; + + // Returns the pid of the extension process. + int process_id() const { return pid_; } + + private: + // The icon painted for the extension process. + static SkBitmap* default_icon_; + + ExtensionHost* extension_host_; + + // Cached data about the extension. + base::ProcessHandle process_handle_; + int pid_; + std::wstring title_; + + DISALLOW_COPY_AND_ASSIGN(TaskManagerExtensionProcessResource); +}; + +class TaskManagerExtensionProcessResourceProvider + : public TaskManager::ResourceProvider, + public NotificationObserver { + public: + explicit TaskManagerExtensionProcessResourceProvider( + TaskManager* task_manager); + + virtual TaskManager::Resource* GetResource(int origin_pid, + int render_process_host_id, + int routing_id); + virtual void StartUpdating(); + virtual void StopUpdating(); + + // NotificationObserver method: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + private: + virtual ~TaskManagerExtensionProcessResourceProvider(); + + void AddToTaskManager(ExtensionHost* extension_host); + void RemoveFromTaskManager(ExtensionHost* extension_host); + + TaskManager* task_manager_; + + // Maps the actual resources (ExtensionHost*) to the Task Manager resources. + std::map<ExtensionHost*, TaskManagerExtensionProcessResource*> resources_; + + // Maps the pids to the resources (used for quick access to the resource on + // byte read notifications). + std::map<int, TaskManagerExtensionProcessResource*> pid_to_resources_; + + // A scoped container for notification registries. + NotificationRegistrar registrar_; + + bool updating_; + + DISALLOW_COPY_AND_ASSIGN(TaskManagerExtensionProcessResourceProvider); +}; + +class TaskManagerNotificationResource : public TaskManager::Resource { + public: + explicit TaskManagerNotificationResource(BalloonHost* balloon_host); + ~TaskManagerNotificationResource(); + + // TaskManager::Resource interface + std::wstring GetTitle() const { return title_; } + SkBitmap GetIcon() const; + base::ProcessHandle GetProcess() const; + virtual bool SupportNetworkUsage() const { return false; } + virtual void SetSupportNetworkUsage() { } + + private: + // The icon painted for notifications. . + static SkBitmap* default_icon_; + + // Non-owned pointer to the balloon host. + BalloonHost* balloon_host_; + + // Cached data about the balloon host. + base::ProcessHandle process_handle_; + int pid_; + std::wstring title_; + + DISALLOW_COPY_AND_ASSIGN(TaskManagerNotificationResource); +}; + +class TaskManagerNotificationResourceProvider + : public TaskManager::ResourceProvider, + public NotificationObserver { + public: + explicit TaskManagerNotificationResourceProvider(TaskManager* task_manager); + + // TaskManager::ResourceProvider interface + virtual TaskManager::Resource* GetResource(int origin_pid, + int render_process_host_id, + int routing_id); + virtual void StartUpdating(); + virtual void StopUpdating(); + + // NotificationObserver interface + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + private: + virtual ~TaskManagerNotificationResourceProvider(); + + void AddToTaskManager(BalloonHost* balloon_host); + void RemoveFromTaskManager(BalloonHost* balloon_host); + + TaskManager* task_manager_; + + // Maps the actual resources (BalloonHost*) to the Task Manager resources. + std::map<BalloonHost*, TaskManagerNotificationResource*> resources_; + + // A scoped container for notification registries. + NotificationRegistrar registrar_; + + bool updating_; + + DISALLOW_COPY_AND_ASSIGN(TaskManagerNotificationResourceProvider); +}; + +class TaskManagerBrowserProcessResource : public TaskManager::Resource { + public: + TaskManagerBrowserProcessResource(); + ~TaskManagerBrowserProcessResource(); + + // TaskManagerResource methods: + std::wstring GetTitle() const; + SkBitmap GetIcon() const; + base::ProcessHandle GetProcess() const; + + bool SupportNetworkUsage() const { return true; } + void SetSupportNetworkUsage() { NOTREACHED(); } + + bool ReportsSqliteMemoryUsed() const { return true; } + size_t SqliteMemoryUsedBytes() const; + + // Returns the pid of the browser process. + int process_id() const { return pid_; } + + private: + base::ProcessHandle process_; + int pid_; + mutable std::wstring title_; + + static SkBitmap* default_icon_; + + DISALLOW_COPY_AND_ASSIGN(TaskManagerBrowserProcessResource); +}; + +class TaskManagerBrowserProcessResourceProvider + : public TaskManager::ResourceProvider { + public: + explicit TaskManagerBrowserProcessResourceProvider( + TaskManager* task_manager); + + virtual TaskManager::Resource* GetResource(int origin_pid, + int render_process_host_id, + int routing_id); + virtual void StartUpdating(); + virtual void StopUpdating(); + + // Whether we are currently reporting to the task manager. Used to ignore + // notifications sent after StopUpdating(). + bool updating_; + + private: + virtual ~TaskManagerBrowserProcessResourceProvider(); + + void AddToTaskManager(ChildProcessInfo child_process_info); + + TaskManager* task_manager_; + TaskManagerBrowserProcessResource resource_; + + DISALLOW_COPY_AND_ASSIGN(TaskManagerBrowserProcessResourceProvider); +}; + +#endif // CHROME_BROWSER_TASK_MANAGER_TASK_MANAGER_RESOURCE_PROVIDERS_H_ diff --git a/chrome/browser/task_manager/task_manager_unittest.cc b/chrome/browser/task_manager/task_manager_unittest.cc new file mode 100644 index 0000000..44d0e31 --- /dev/null +++ b/chrome/browser/task_manager/task_manager_unittest.cc @@ -0,0 +1,110 @@ +// 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 <string> + +#include "app/l10n_util.h" +#include "base/message_loop.h" +#include "base/process_util.h" +#include "base/utf_string_conversions.h" +#include "grit/chromium_strings.h" +#include "grit/generated_resources.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkBitmap.h" + +namespace { + +#if defined(OS_MACOSX) +// From task_manager.cc: +// Activity Monitor shows %cpu with one decimal digit -- be +// consistent with that. +const char* kZeroCPUUsage = "0.0"; +#else +const char* kZeroCPUUsage = "0"; +#endif + +class TestResource : public TaskManager::Resource { + public: + TestResource() : refresh_called_(false) {} + + virtual std::wstring GetTitle() const { return L"test title"; } + virtual SkBitmap GetIcon() const { return SkBitmap(); } + virtual base::ProcessHandle GetProcess() const { + return base::GetCurrentProcessHandle(); + } + virtual bool SupportNetworkUsage() const { return false; } + virtual void SetSupportNetworkUsage() { NOTREACHED(); } + virtual void Refresh() { refresh_called_ = true; } + bool refresh_called() const { return refresh_called_; } + void set_refresh_called(bool refresh_called) { + refresh_called_ = refresh_called; + } + + private: + bool refresh_called_; +}; + +} // namespace + +class TaskManagerTest : public testing::Test { +}; + +TEST_F(TaskManagerTest, Basic) { + TaskManager task_manager; + TaskManagerModel* model = task_manager.model_; + EXPECT_EQ(0, model->ResourceCount()); +} + +TEST_F(TaskManagerTest, Resources) { + TaskManager task_manager; + TaskManagerModel* model = task_manager.model_; + + TestResource resource1, resource2; + + task_manager.AddResource(&resource1); + ASSERT_EQ(1, model->ResourceCount()); + EXPECT_TRUE(model->IsResourceFirstInGroup(0)); + EXPECT_EQ(ASCIIToUTF16("test title"), model->GetResourceTitle(0)); + EXPECT_EQ(l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT), + model->GetResourceNetworkUsage(0)); + EXPECT_EQ(ASCIIToUTF16(kZeroCPUUsage), model->GetResourceCPUUsage(0)); + + task_manager.AddResource(&resource2); // Will be in the same group. + ASSERT_EQ(2, model->ResourceCount()); + EXPECT_TRUE(model->IsResourceFirstInGroup(0)); + EXPECT_FALSE(model->IsResourceFirstInGroup(1)); + EXPECT_EQ(ASCIIToUTF16("test title"), model->GetResourceTitle(1)); + EXPECT_EQ(l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT).c_str(), + model->GetResourceNetworkUsage(1)); + EXPECT_EQ(ASCIIToUTF16(kZeroCPUUsage), model->GetResourceCPUUsage(1)); + + task_manager.RemoveResource(&resource1); + // Now resource2 will be first in group. + ASSERT_EQ(1, model->ResourceCount()); + EXPECT_TRUE(model->IsResourceFirstInGroup(0)); + EXPECT_EQ(ASCIIToUTF16("test title"), model->GetResourceTitle(0)); + EXPECT_EQ(l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT), + model->GetResourceNetworkUsage(0)); + EXPECT_EQ(ASCIIToUTF16(kZeroCPUUsage), model->GetResourceCPUUsage(0)); + + task_manager.RemoveResource(&resource2); + EXPECT_EQ(0, model->ResourceCount()); +} + +// Tests that the model is calling Refresh() on its resources. +TEST_F(TaskManagerTest, RefreshCalled) { + MessageLoop loop; + TaskManager task_manager; + TaskManagerModel* model = task_manager.model_; + TestResource resource; + + task_manager.AddResource(&resource); + ASSERT_FALSE(resource.refresh_called()); + model->update_state_ = TaskManagerModel::TASK_PENDING; + model->Refresh(); + ASSERT_TRUE(resource.refresh_called()); + task_manager.RemoveResource(&resource); +} |