Commit faa231a4 authored by amistry's avatar amistry Committed by Commit bot

Report utility process JS memory in task manager.

BUG=467832

Review URL: https://codereview.chromium.org/972083002

Cr-Commit-Position: refs/heads/master@{#330670}
parent 7e4f0459
// 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/process_resource_usage.h"
#include "base/bind.h"
#include "base/logging.h"
ProcessResourceUsage::ProcessResourceUsage(ResourceUsageReporterPtr service)
: service_(service.Pass()), update_in_progress_(false) {
}
ProcessResourceUsage::~ProcessResourceUsage() {
DCHECK(thread_checker_.CalledOnValidThread());
}
void ProcessResourceUsage::Refresh() {
DCHECK(thread_checker_.CalledOnValidThread());
if (!update_in_progress_ && service_) {
update_in_progress_ = true;
service_->GetUsageData(base::Bind(&ProcessResourceUsage::OnRefreshDone,
base::Unretained(this)));
}
}
void ProcessResourceUsage::OnRefreshDone(ResourceUsageDataPtr data) {
DCHECK(thread_checker_.CalledOnValidThread());
update_in_progress_ = false;
stats_ = data.Pass();
}
bool ProcessResourceUsage::ReportsV8MemoryStats() const {
DCHECK(thread_checker_.CalledOnValidThread());
if (stats_)
return stats_->reports_v8_stats;
return false;
}
size_t ProcessResourceUsage::GetV8MemoryAllocated() const {
DCHECK(thread_checker_.CalledOnValidThread());
if (stats_ && stats_->reports_v8_stats)
return stats_->v8_bytes_allocated;
return 0;
}
size_t ProcessResourceUsage::GetV8MemoryUsed() const {
DCHECK(thread_checker_.CalledOnValidThread());
if (stats_ && stats_->reports_v8_stats)
return stats_->v8_bytes_used;
return 0;
}
// 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.
#ifndef CHROME_BROWSER_PROCESS_RESOURCE_USAGE_H_
#define CHROME_BROWSER_PROCESS_RESOURCE_USAGE_H_
#include "base/basictypes.h"
#include "base/threading/thread_checker.h"
#include "chrome/common/resource_usage_reporter.mojom.h"
// Provides resource usage information about a child process.
//
// This is a wrapper around the ResourceUsageReporter Mojo service that exposes
// information about resources used by a child process. Currently, this is only
// V8 memory usage, but could be expanded to include other resources such as web
// cache. This is intended for status viewers such as the task manager and
// about://memory-internals.
//
// To create:
// 1. Obtain a ResourceUsageReporter connection using the child process's
// service registry. i.e:
// ResourceUsageReporterPtr service;
// process->GetServiceRegistry()->ConnectToRemoteService(&service);
// 2. If needed, the connection can be passed to another thread using
// ResourceUsageReporterPtr::PassInterface().
// 3. Pass the service to the constructor.
//
// Note: ProcessResourceUsage is thread-hostile and must live on a single
// thread.
class ProcessResourceUsage {
public:
// Must be called from the same thread that created |service|.
explicit ProcessResourceUsage(ResourceUsageReporterPtr service);
~ProcessResourceUsage();
// Refresh the resource usage information.
void Refresh();
// Get V8 memory usage information.
bool ReportsV8MemoryStats() const;
size_t GetV8MemoryAllocated() const;
size_t GetV8MemoryUsed() const;
private:
// Mojo IPC callback.
void OnRefreshDone(ResourceUsageDataPtr data);
ResourceUsageReporterPtr service_;
bool update_in_progress_;
ResourceUsageDataPtr stats_;
base::ThreadChecker thread_checker_;
DISALLOW_COPY_AND_ASSIGN(ProcessResourceUsage);
};
#endif // CHROME_BROWSER_PROCESS_RESOURCE_USAGE_H_
......@@ -7,14 +7,18 @@
#include <vector>
#include "base/i18n/rtl.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string16.h"
#include "chrome/browser/process_resource_usage.h"
#include "chrome/browser/task_manager/resource_provider.h"
#include "chrome/browser/task_manager/task_manager.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_child_process_host_iterator.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_data.h"
#include "content/public/common/service_registry.h"
#include "grit/theme_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
......@@ -43,6 +47,10 @@ class ChildProcessResource : public Resource {
Type GetType() const override;
bool SupportNetworkUsage() const override;
void SetSupportNetworkUsage() override;
void Refresh() override;
bool ReportsV8MemoryStats() const override;
size_t GetV8MemoryAllocated() const override;
size_t GetV8MemoryUsed() const override;
// Returns the pid of the child process.
int process_id() const { return pid_; }
......@@ -52,6 +60,12 @@ class ChildProcessResource : public Resource {
// process would be "Plugin: Flash" when name is "Flash".
base::string16 GetLocalizedTitle() const;
static mojo::InterfacePtrInfo<ResourceUsageReporter>
GetProcessUsageOnIOThread(int id);
void OnGetProcessUsageDone(
mojo::InterfacePtrInfo<ResourceUsageReporter> info);
int process_type_;
base::string16 name_;
base::ProcessHandle handle_;
......@@ -59,27 +73,48 @@ class ChildProcessResource : public Resource {
int unique_process_id_;
mutable base::string16 title_;
bool network_usage_support_;
scoped_ptr<ProcessResourceUsage> resource_usage_;
// The icon painted for the child processs.
// TODO(jcampan): we should have plugin specific icons for well-known
// plugins.
static gfx::ImageSkia* default_icon_;
base::WeakPtrFactory<ChildProcessResource> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(ChildProcessResource);
};
gfx::ImageSkia* ChildProcessResource::default_icon_ = NULL;
ChildProcessResource::ChildProcessResource(
int process_type,
const base::string16& name,
base::ProcessHandle handle,
int unique_process_id)
// static
mojo::InterfacePtrInfo<ResourceUsageReporter>
ChildProcessResource::GetProcessUsageOnIOThread(int id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
content::BrowserChildProcessHost* host =
content::BrowserChildProcessHost::FromID(id);
if (!host)
return mojo::InterfacePtrInfo<ResourceUsageReporter>();
content::ServiceRegistry* registry = host->GetServiceRegistry();
if (!registry)
return mojo::InterfacePtrInfo<ResourceUsageReporter>();
ResourceUsageReporterPtr service;
registry->ConnectToRemoteService(&service);
return service.PassInterface().Pass();
}
ChildProcessResource::ChildProcessResource(int process_type,
const base::string16& name,
base::ProcessHandle handle,
int unique_process_id)
: process_type_(process_type),
name_(name),
handle_(handle),
unique_process_id_(unique_process_id),
network_usage_support_(false) {
network_usage_support_(false),
weak_factory_(this) {
// 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_ = base::GetProcId(handle);
......@@ -88,11 +123,27 @@ ChildProcessResource::ChildProcessResource(
default_icon_ = rb.GetImageSkiaNamed(IDR_PLUGINS_FAVICON);
// TODO(jabdelmalek): use different icon for web workers.
}
BrowserThread::PostTaskAndReplyWithResult(
BrowserThread::IO, FROM_HERE,
base::Bind(&ChildProcessResource::GetProcessUsageOnIOThread,
unique_process_id),
base::Bind(&ChildProcessResource::OnGetProcessUsageDone,
weak_factory_.GetWeakPtr()));
}
ChildProcessResource::~ChildProcessResource() {
}
void ChildProcessResource::OnGetProcessUsageDone(
mojo::InterfacePtrInfo<ResourceUsageReporter> info) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (info.is_valid()) {
ResourceUsageReporterPtr service;
service.Bind(info.Pass());
resource_usage_.reset(new ProcessResourceUsage(service.Pass()));
}
}
// Resource methods:
base::string16 ChildProcessResource::GetTitle() const {
if (title_.empty())
......@@ -200,6 +251,29 @@ base::string16 ChildProcessResource::GetLocalizedTitle() const {
return title;
}
void ChildProcessResource::Refresh() {
if (resource_usage_)
resource_usage_->Refresh();
}
bool ChildProcessResource::ReportsV8MemoryStats() const {
if (resource_usage_)
return resource_usage_->ReportsV8MemoryStats();
return false;
}
size_t ChildProcessResource::GetV8MemoryAllocated() const {
if (resource_usage_)
return resource_usage_->GetV8MemoryAllocated();
return 0;
}
size_t ChildProcessResource::GetV8MemoryUsed() const {
if (resource_usage_)
return resource_usage_->GetV8MemoryUsed();
return 0;
}
////////////////////////////////////////////////////////////////////////////////
// ChildProcessResourceProvider class
////////////////////////////////////////////////////////////////////////////////
......
......@@ -55,10 +55,12 @@ using task_manager::browsertest_util::MatchAnyApp;
using task_manager::browsertest_util::MatchAnyExtension;
using task_manager::browsertest_util::MatchAnySubframe;
using task_manager::browsertest_util::MatchAnyTab;
using task_manager::browsertest_util::MatchAnyUtility;
using task_manager::browsertest_util::MatchApp;
using task_manager::browsertest_util::MatchExtension;
using task_manager::browsertest_util::MatchSubframe;
using task_manager::browsertest_util::MatchTab;
using task_manager::browsertest_util::MatchUtility;
using task_manager::browsertest_util::WaitForTaskManagerRows;
using task_manager::browsertest_util::WaitForTaskManagerStatToExceed;
......@@ -126,6 +128,26 @@ class TaskManagerBrowserTest : public ExtensionBrowserTest {
DISALLOW_COPY_AND_ASSIGN(TaskManagerBrowserTest);
};
class TaskManagerUtilityProcessBrowserTest : public TaskManagerBrowserTest {
public:
TaskManagerUtilityProcessBrowserTest() {}
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
TaskManagerBrowserTest::SetUpCommandLine(command_line);
// Enable out-of-process proxy resolver. Use a trivial PAC script to ensure
// that some javascript is being executed.
command_line->AppendSwitch(switches::kV8PacMojoOutOfProcess);
command_line->AppendSwitchASCII(
switches::kProxyPacUrl,
"data:,function FindProxyForURL(url, host){return \"DIRECT;\";}");
}
private:
DISALLOW_COPY_AND_ASSIGN(TaskManagerUtilityProcessBrowserTest);
};
// Parameterized variant of TaskManagerBrowserTest which runs with/without
// --site-per-process, which enables out of process iframes (OOPIFs).
class TaskManagerOOPIFBrowserTest : public TaskManagerBrowserTest,
......@@ -841,6 +863,29 @@ IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, JSHeapMemory) {
ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchTab("title1.html")));
}
// Checks that task manager counts utility process JS heap size.
IN_PROC_BROWSER_TEST_F(TaskManagerUtilityProcessBrowserTest,
UtilityJSHeapMemory) {
ShowTaskManager();
ui_test_utils::NavigateToURL(browser(), GetTestURL());
// The PAC script is trivial, so don't expect a large heap.
size_t minimal_heap_size = 1024;
ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerStatToExceed(
MatchUtility(
l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_PROXY_RESOLVER_NAME)),
task_manager::browsertest_util::V8_MEMORY,
minimal_heap_size));
ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerStatToExceed(
MatchUtility(
l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_PROXY_RESOLVER_NAME)),
task_manager::browsertest_util::V8_MEMORY_USED,
minimal_heap_size));
ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyUtility()));
ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(
1, MatchUtility(l10n_util::GetStringUTF16(
IDS_UTILITY_PROCESS_PROXY_RESOLVER_NAME))));
}
IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, DevToolsNewDockedWindow) {
ShowTaskManager(); // Task manager shown BEFORE dev tools window.
......
......@@ -251,5 +251,13 @@ base::string16 MatchAnySubframe() {
return MatchSubframe("*");
}
base::string16 MatchUtility(const base::string16& title) {
return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_UTILITY_PREFIX, title);
}
base::string16 MatchAnyUtility() {
return MatchUtility(base::ASCIIToUTF16("*"));
}
} // namespace browsertest_util
} // namespace task_manager
......@@ -58,6 +58,9 @@ base::string16 MatchPrint(const char* title); // "Print: " + title
base::string16 MatchAnyPrint(); // "Print: *"
base::string16 MatchSubframe(const char* title); // "Subframe: " + title
base::string16 MatchAnySubframe(); // "Subframe: *"
// "Utility: " + title
base::string16 MatchUtility(const base::string16& title);
base::string16 MatchAnyUtility(); // "Utility: *"
} // namespace browsertest_util
} // namespace task_manager
......
......@@ -618,6 +618,8 @@
'browser/prerender/prerender_util.h',
'browser/process_info_snapshot.h',
'browser/process_info_snapshot_mac.cc',
'browser/process_resource_usage.cc',
'browser/process_resource_usage.h',
'browser/process_singleton.h',
'browser/process_singleton_win.cc',
'browser/push_messaging/push_messaging_app_identifier.cc',
......@@ -3147,6 +3149,7 @@
['OS != "ios"', {
'dependencies': [
'apps',
'common_mojo_bindings',
'debugger',
'installer_util',
'../third_party/re2/re2.gyp:re2',
......
......@@ -687,5 +687,20 @@
},
'includes': [ '../build/protoc.gypi' ],
},
{
# GN version: //chrome/common:mojo_bindings
'target_name': 'common_mojo_bindings',
'type': 'static_library',
'includes': [
'../third_party/mojo/mojom_bindings_generator.gypi'
],
'sources': [
'common/resource_usage_reporter.mojom',
],
'dependencies': [
'../mojo/mojo_base.gyp:mojo_environment_chromium',
'../third_party/mojo/mojo_public.gyp:mojo_cpp_bindings',
],
},
],
}
......@@ -135,6 +135,7 @@
}],
['OS!="android"', {
'dependencies': [
'common_mojo_bindings',
'../net/net.gyp:net_utility_services',
],
'sources': [
......
......@@ -3,6 +3,7 @@
# found in the LICENSE file.
import("//tools/grit/grit_rule.gni")
import("//third_party/mojo/src/mojo/public/tools/bindings/mojom.gni")
gypi_values = exec_script("//build/gypi_to_gn.py",
[ rebase_path("../chrome_common.gypi") ],
......@@ -84,6 +85,7 @@ static_library("common") {
} else {
# Non-iOS.
deps += [
":mojo_bindings",
"//components/visitedlink/common",
"//components/autofill/content/common",
"//components/autofill/core/common",
......@@ -95,6 +97,9 @@ static_library("common") {
"//third_party/re2",
"//third_party/widevine/cdm:version_h",
]
public_deps = [
"//third_party/mojo/src/mojo/public/cpp/bindings",
]
}
if (enable_extensions) {
......@@ -340,3 +345,9 @@ static_library("app_mode_app_support") {
"//base",
]
}
mojom("mojo_bindings") {
sources = [
"resource_usage_reporter.mojom",
]
}
// 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.
struct ResourceUsageData {
bool reports_v8_stats = false;
uint64 v8_bytes_allocated = 0;
uint64 v8_bytes_used = 0;
// TODO(amistry): Consider using this for the renderer process.
};
interface ResourceUsageReporter {
// NOTE: For utility processes, this only reports V8 memory used by the proxy
// resolver.
GetUsageData() => (ResourceUsageData data);
};
......@@ -36,7 +36,10 @@ static_library("utility") {
defines += [ "USE_SECCOMP_BPF" ]
}
} else {
deps += [ "//net:net_utility_services" ]
deps += [
"//chrome/common:mojo_bindings",
"//net:net_utility_services",
]
sources +=
rebase_path(gypi_values.chrome_utility_importer_sources, ".", "..")
}
......
......@@ -31,8 +31,11 @@
#endif
#if !defined(OS_ANDROID)
#include "chrome/common/resource_usage_reporter.mojom.h"
#include "chrome/utility/profile_import_handler.h"
#include "net/proxy/mojo_proxy_resolver_factory_impl.h"
#include "net/proxy/proxy_resolver_v8.h"
#include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h"
#endif
#if defined(OS_ANDROID) && defined(USE_SECCOMP_BPF)
......@@ -90,6 +93,34 @@ void CreateProxyResolverFactory(
// will be destroyed.
new net::MojoProxyResolverFactoryImpl(request.Pass());
}
class ResourceUsageReporterImpl : public ResourceUsageReporter {
public:
explicit ResourceUsageReporterImpl(
mojo::InterfaceRequest<ResourceUsageReporter> req)
: binding_(this, req.Pass()) {}
~ResourceUsageReporterImpl() override {}
private:
void GetUsageData(
const mojo::Callback<void(ResourceUsageDataPtr)>& callback) override {
ResourceUsageDataPtr data = ResourceUsageData::New();
size_t total_heap_size = net::ProxyResolverV8::GetTotalHeapSize();
if (total_heap_size) {
data->reports_v8_stats = true;
data->v8_bytes_allocated = total_heap_size;
data->v8_bytes_used = net::ProxyResolverV8::GetUsedHeapSize();
}
callback.Run(data.Pass());
}
mojo::StrongBinding<ResourceUsageReporter> binding_;
};
void CreateResourceUsageReporter(
mojo::InterfaceRequest<ResourceUsageReporter> request) {
new ResourceUsageReporterImpl(request.Pass());
}
#endif // OS_ANDROID
} // namespace
......@@ -192,6 +223,8 @@ void ChromeContentUtilityClient::RegisterMojoServices(
#if !defined(OS_ANDROID)
registry->AddService<net::interfaces::ProxyResolverFactory>(
base::Bind(CreateProxyResolverFactory));
registry->AddService<ResourceUsageReporter>(
base::Bind(CreateResourceUsageReporter));
#endif
}
......
......@@ -66,6 +66,17 @@ BrowserChildProcessHost* BrowserChildProcessHost::Create(
return new BrowserChildProcessHostImpl(process_type, delegate);
}
BrowserChildProcessHost* BrowserChildProcessHost::FromID(int child_process_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
BrowserChildProcessHostImpl::BrowserChildProcessList* process_list =
g_child_process_list.Pointer();
for (BrowserChildProcessHostImpl* host : *process_list) {
if (host->GetData().id == child_process_id)
return host;
}
return nullptr;
}
#if defined(OS_MACOSX)
base::ProcessMetrics::PortProvider* BrowserChildProcessHost::GetPortProvider() {
return MachBroker::GetInstance();
......@@ -187,6 +198,11 @@ void BrowserChildProcessHostImpl::SetHandle(base::ProcessHandle handle) {
data_.handle = handle;
}
ServiceRegistry* BrowserChildProcessHostImpl::GetServiceRegistry() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
return delegate_->GetServiceRegistry();
}
void BrowserChildProcessHostImpl::ForceShutdown() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
g_child_process_list.Get().remove(this);
......
......@@ -30,6 +30,7 @@ namespace content {
class BrowserChildProcessHostIterator;
class BrowserChildProcessObserver;
class BrowserMessageFilter;
class ServiceRegistry;
// Plugins/workers and other child processes that live on the IO thread use this
// class. RenderProcessHostImpl is the main exception that doesn't use this
......@@ -61,6 +62,7 @@ class CONTENT_EXPORT BrowserChildProcessHostImpl
int* exit_code) override;
void SetName(const base::string16& name) override;
void SetHandle(base::ProcessHandle handle) override;
ServiceRegistry* GetServiceRegistry() override;
// ChildProcessHostDelegate implementation:
bool CanShutdown() override;
......
......@@ -188,8 +188,9 @@ bool UtilityProcessHostImpl::StartMojoMode() {
}
ServiceRegistry* UtilityProcessHostImpl::GetServiceRegistry() {
DCHECK(mojo_application_host_);
return mojo_application_host_->service_registry();
if (mojo_application_host_)
return mojo_application_host_->service_registry();
return nullptr;
}
void UtilityProcessHostImpl::SetName(const base::string16& name) {
......
......@@ -25,6 +25,7 @@ namespace content {
class BrowserChildProcessHostDelegate;
class ChildProcessHost;
class SandboxedProcessLauncherDelegate;
class ServiceRegistry;
struct ChildProcessData;
// This represents child processes of the browser process, i.e. plugins. They
......@@ -38,6 +39,11 @@ class CONTENT_EXPORT BrowserChildProcessHost : public IPC::Sender {
content::ProcessType process_type,
BrowserChildProcessHostDelegate* delegate);
// Returns the child process host with unique id |child_process_id|, or
// nullptr if it doesn't exist. |child_process_id| is NOT the process ID, but
// is the same unique ID as |ChildProcessData::id|.
static BrowserChildProcessHost* FromID(int child_process_id);
~BrowserChildProcessHost() override {}
// Derived classes call this to launch the child process asynchronously.
......@@ -72,6 +78,10 @@ class CONTENT_EXPORT BrowserChildProcessHost : public IPC::Sender {
// this object.
virtual void SetHandle(base::ProcessHandle handle) = 0;
// Get the Mojo service registry connected to the child process. Returns
// nullptr if no service registry exists.
virtual ServiceRegistry* GetServiceRegistry() = 0;
#if defined(OS_MACOSX) && !defined(OS_IOS)
// Returns a PortProvider used to get process metrics for child processes.
static base::ProcessMetrics::PortProvider* GetPortProvider();
......
......@@ -10,4 +10,8 @@ bool BrowserChildProcessHostDelegate::CanShutdown() {
return true;
}
ServiceRegistry* BrowserChildProcessHostDelegate::GetServiceRegistry() {
return nullptr;
}
} // namespace content
......@@ -10,6 +10,8 @@
namespace content {
class ServiceRegistry;
// Interface that all users of BrowserChildProcessHost need to provide.
class CONTENT_EXPORT BrowserChildProcessHostDelegate : public IPC::Listener {
public:
......@@ -32,6 +34,9 @@ class CONTENT_EXPORT BrowserChildProcessHostDelegate : public IPC::Listener {
// process crashed (for posix, as returned from waitpid(), for Windows, as
// returned from GetExitCodeProcess()).
virtual void OnProcessCrashed(int exit_code) {}
// Returns the ServiceRegistry for this child process.
virtual ServiceRegistry* GetServiceRegistry();
};
}; // namespace content
......
......@@ -80,8 +80,8 @@ class UtilityProcessHost : public IPC::Sender,
// Starts the utility process in Mojo mode.
virtual bool StartMojoMode() = 0;
// Returns the ServiceRegistry for this process. Only valid to call this if
// the process was started with StartMojoMode().
// Returns the ServiceRegistry for this process. Will return nullptr if
// the process was not started with StartMojoMode().
virtual ServiceRegistry* GetServiceRegistry() = 0;
// Set the name of the process to appear in the task manager.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment