blob: a0b9f3d6fbce4a0879949e45d374aa60fa517442 [file] [log] [blame]
// Copyright 2015 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_management/providers/child_process_task.h"
#include <utility>
#include "base/i18n/rtl.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/process_resource_usage.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/task_management/task_manager_observer.h"
#include "chrome/grit/generated_resources.h"
#include "components/nacl/common/nacl_process_type.h"
#include "content/public/browser/browser_child_process_host.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_data.h"
#include "content/public/common/process_type.h"
#include "content/public/common/service_registry.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/common/extension_set.h"
#include "grit/theme_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
namespace task_management {
namespace {
gfx::ImageSkia* g_default_icon = nullptr;
gfx::ImageSkia* GetDefaultIcon() {
if (!g_default_icon && ResourceBundle::HasSharedInstance()) {
g_default_icon = ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
IDR_PLUGINS_FAVICON);
}
return g_default_icon;
}
base::string16 GetLocalizedTitle(const base::string16& title,
int process_type) {
base::string16 result_title = title;
if (result_title.empty()) {
switch (process_type) {
case content::PROCESS_TYPE_PPAPI_PLUGIN:
case content::PROCESS_TYPE_PPAPI_BROKER:
result_title = l10n_util::GetStringUTF16(
IDS_TASK_MANAGER_UNKNOWN_PLUGIN_NAME);
break;
default:
// Nothing to do for non-plugin processes.
break;
}
}
// Explicitly mark name as LTR if there is no strong RTL character,
// to avoid the wrong concatenation result similar to "!Yahoo Mail: the
// best web-based Email: NIGULP", in which "NIGULP" stands for the Hebrew
// or Arabic word for "plugin".
base::i18n::AdjustStringForLocaleDirection(&result_title);
switch (process_type) {
case content::PROCESS_TYPE_UTILITY:
return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_UTILITY_PREFIX,
result_title);
case content::PROCESS_TYPE_GPU:
return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_GPU_PREFIX);
case content::PROCESS_TYPE_PPAPI_PLUGIN:
return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_PLUGIN_PREFIX,
result_title);
case content::PROCESS_TYPE_PPAPI_BROKER:
return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_PLUGIN_BROKER_PREFIX,
result_title);
case PROCESS_TYPE_NACL_BROKER:
return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NACL_BROKER_PREFIX);
case PROCESS_TYPE_NACL_LOADER: {
auto* profile_manager = g_browser_process->profile_manager();
if (profile_manager) {
// TODO(afakhry): Fix the below looping by plumbing a way to get the
// profile or the profile path from the child process host if any.
auto loaded_profiles = profile_manager->GetLoadedProfiles();
for (auto* profile : loaded_profiles) {
const extensions::ExtensionSet& enabled_extensions =
extensions::ExtensionRegistry::Get(profile)->enabled_extensions();
const extensions::Extension* extension =
enabled_extensions.GetExtensionOrAppByURL(GURL(result_title));
if (extension) {
result_title = base::UTF8ToUTF16(extension->name());
break;
}
}
}
return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_NACL_PREFIX,
result_title);
}
// These types don't need display names or get them from elsewhere.
case content::PROCESS_TYPE_BROWSER:
case content::PROCESS_TYPE_RENDERER:
case content::PROCESS_TYPE_ZYGOTE:
case content::PROCESS_TYPE_SANDBOX_HELPER:
case content::PROCESS_TYPE_MAX:
break;
case content::PROCESS_TYPE_UNKNOWN:
NOTREACHED() << "Need localized name for child process type.";
}
return result_title;
}
// Connects the |resource_reporter| to the ServiceRegistry of the
// BrowserChildProcessHost whose unique ID is |unique_child_process_id|.
void ConnectResourceReporterOnIOThread(
int unique_child_process_id,
mojo::InterfaceRequest<ResourceUsageReporter> resource_reporter) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
content::BrowserChildProcessHost* host =
content::BrowserChildProcessHost::FromID(unique_child_process_id);
if (!host)
return;
content::ServiceRegistry* registry = host->GetServiceRegistry();
if (!registry)
return;
registry->ConnectToRemoteService(std::move(resource_reporter));
}
// Creates the Mojo service wrapper that will be used to sample the V8 memory
// usage of the browser child process whose unique ID is
// |unique_child_process_id|.
ProcessResourceUsage* CreateProcessResourcesSampler(
int unique_child_process_id) {
ResourceUsageReporterPtr service;
mojo::InterfaceRequest<ResourceUsageReporter> usage_reporter =
mojo::GetProxy(&service);
content::BrowserThread::PostTask(
content::BrowserThread::IO,
FROM_HERE,
base::Bind(&ConnectResourceReporterOnIOThread,
unique_child_process_id,
base::Passed(&usage_reporter)));
return new ProcessResourceUsage(std::move(service));
}
bool UsesV8Memory(int process_type) {
switch (process_type) {
case content::PROCESS_TYPE_UTILITY:
case content::PROCESS_TYPE_BROWSER:
case content::PROCESS_TYPE_RENDERER:
return true;
default:
return false;
}
}
} // namespace
ChildProcessTask::ChildProcessTask(const content::ChildProcessData& data)
: Task(GetLocalizedTitle(data.name, data.process_type),
base::UTF16ToUTF8(data.name),
GetDefaultIcon(),
data.handle),
process_resources_sampler_(CreateProcessResourcesSampler(data.id)),
v8_memory_allocated_(-1),
v8_memory_used_(-1),
unique_child_process_id_(data.id),
process_type_(data.process_type),
uses_v8_memory_(UsesV8Memory(process_type_)) {
}
ChildProcessTask::~ChildProcessTask() {
}
void ChildProcessTask::Refresh(const base::TimeDelta& update_interval,
int64_t refresh_flags) {
Task::Refresh(update_interval, refresh_flags);
if ((refresh_flags & REFRESH_TYPE_V8_MEMORY) == 0)
return;
if (!uses_v8_memory_)
return;
// The child process resources refresh is performed asynchronously, we will
// invoke it and record the current values (which might be invalid at the
// moment. We can safely ignore that and count on future refresh cycles
// potentially having valid values).
process_resources_sampler_->Refresh(base::Closure());
v8_memory_allocated_ = base::saturated_cast<int64_t>(
process_resources_sampler_->GetV8MemoryAllocated());
v8_memory_used_ = base::saturated_cast<int64_t>(
process_resources_sampler_->GetV8MemoryUsed());
}
Task::Type ChildProcessTask::GetType() const {
// Convert |content::ProcessType| to |task_management::Task::Type|.
switch (process_type_) {
case content::PROCESS_TYPE_PPAPI_PLUGIN:
case content::PROCESS_TYPE_PPAPI_BROKER:
return Task::PLUGIN;
case content::PROCESS_TYPE_UTILITY:
return Task::UTILITY;
case content::PROCESS_TYPE_ZYGOTE:
return Task::ZYGOTE;
case content::PROCESS_TYPE_SANDBOX_HELPER:
return Task::SANDBOX_HELPER;
case content::PROCESS_TYPE_GPU:
return Task::GPU;
case PROCESS_TYPE_NACL_LOADER:
case PROCESS_TYPE_NACL_BROKER:
return Task::NACL;
default:
return Task::UNKNOWN;
}
}
int ChildProcessTask::GetChildProcessUniqueID() const {
return unique_child_process_id_;
}
bool ChildProcessTask::ReportsV8Memory() const {
return uses_v8_memory_ && process_resources_sampler_->ReportsV8MemoryStats();
}
int64_t ChildProcessTask::GetV8MemoryAllocated() const {
return v8_memory_allocated_;
}
int64_t ChildProcessTask::GetV8MemoryUsed() const {
return v8_memory_used_;
}
} // namespace task_management