0

Sampling profiler: profile the NetworkService process

In the out-of-process case, network service runs on the IO thread of a
utility process handling network-related tasks. See
services/network/README.md for more details.
To track the utility network service process, this CL enables sampling
profiler in this process. It updates the StackSamplingConfiguration to
turn on the switch of profiler in the network service process. Then when
the switch is on, ChromeContentUtilityClient will create and start a
profiler for the IO thread, and build a message pipe between the utility
network service process and the browser process to pass the collected
profile data.
If the switch is on, the procedure will be:
1. In the |UtilityMain| function, which is the mainline routine of a
utility process, a |ChildProcess| is created. This instance contains the
delegate of the IO thread and its task runner. Then
|ChromeContentUtilityClient::PostIOThreadCreated| will create
and start a profiler for the IO thread.
2. A |UtilityThreadImpl| instance is created in the |UtilityMain| as
well, it will be the main thread of the process. To initialize the
instance, |UtilityThreadImpl::Init| function is called. This function
only runs once in every utility process so, in this function,
|ChromeContentUtilityClient::UtilityThreadStarted| will create a message
pipe between the utility network service process and the browser process
to pass the collected profile data.
Note that there are some in-process utility threads run in other
processes, so please check the process type before setting up the
collector and pipe.
3. More specifically, it will create a |PendingReceiver| of
|CallStackProfileCollector|. And the browser process will receive and
handle this receiver. The path is:
  |ChildThread::BindHostReceiver| =>
  |UtilityProcessHost::BindHostReceiver| =>
  |ChromeContentBrowserClient::BindUtilityHostReceiver|.
In the utility process side, |SetCollectorForChildProcess| is called to
supply the Service Manager's connector; and in the browser process side,
a |CallStackProfileCollector| will be created with the receiver.

Bug: 931432
Change-Id: I245f864089d82aee09c30a42c1606a93cf5ea1c6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2186614
Commit-Queue: Lingqi Chi <lingqi@google.com>
Reviewed-by: Matt Falkenhagen <falken@chromium.org>
Reviewed-by: Ken Rockot <rockot@google.com>
Reviewed-by: Takashi Toyoshima <toyoshim@chromium.org>
Reviewed-by: Mike Wittman <wittman@chromium.org>
Reviewed-by: Kinuko Yasuda <kinuko@chromium.org>
Cr-Commit-Position: refs/heads/master@{#777632}
This commit is contained in:
Lingqi Chi
2020-06-12 02:04:46 +00:00
committed by Commit Bot
parent af1b79aa76
commit f7ef80f606
13 changed files with 79 additions and 2 deletions

@ -412,6 +412,7 @@ class ChromeContentBrowserClient : public content::ContentBrowserClient {
const GURL& service_worker_scope,
mojo::PendingReceiver<blink::mojom::BadgeService> receiver) override;
void BindGpuHostReceiver(mojo::GenericPendingReceiver receiver) override;
void BindUtilityHostReceiver(mojo::GenericPendingReceiver receiver) override;
void BindHostReceiverForRenderer(
content::RenderProcessHost* render_process_host,
mojo::GenericPendingReceiver receiver) override;

@ -278,6 +278,12 @@ void ChromeContentBrowserClient::BindGpuHostReceiver(
metrics::CallStackProfileCollector::Create(std::move(r));
}
void ChromeContentBrowserClient::BindUtilityHostReceiver(
mojo::GenericPendingReceiver receiver) {
if (auto r = receiver.As<metrics::mojom::CallStackProfileCollector>())
metrics::CallStackProfileCollector::Create(std::move(r));
}
void ChromeContentBrowserClient::BindHostReceiverForRenderer(
content::RenderProcessHost* render_process_host,
mojo::GenericPendingReceiver receiver) {

@ -161,3 +161,10 @@ IN_PROC_BROWSER_TEST_F(StackSamplingBrowserTest,
metrics::RENDERER_PROCESS,
metrics::COMPOSITOR_THREAD));
}
IN_PROC_BROWSER_TEST_F(StackSamplingBrowserTest,
NetworkServiceProcessIOThread) {
EXPECT_TRUE(WaitForProfile(metrics::SampledProfile::PROCESS_STARTUP,
metrics::NETWORK_SERVICE_PROCESS,
metrics::IO_THREAD));
}

@ -14,6 +14,7 @@
#include "components/version_info/version_info.h"
#include "content/public/common/content_switches.h"
#include "extensions/buildflags/buildflags.h"
#include "services/service_manager/sandbox/sandbox.h"
#if defined(OS_ANDROID)
#include "chrome/android/modules/stack_unwinder/public/module.h"
@ -170,6 +171,11 @@ void StackSamplingConfiguration::AppendCommandLineSwitchForChildProcess(
if (!enable)
return;
if (process_type == switches::kGpuProcess ||
(process_type == switches::kUtilityProcess &&
// The network service is the only utility process that is profiled for
// now.
service_manager::SandboxTypeFromCommandLine(*command_line) ==
service_manager::SandboxType::kNetwork) ||
(process_type == switches::kRendererProcess &&
// Do not start the profiler for extension processes since profiling the
// compositor thread in them is not useful.

@ -26,6 +26,7 @@
#include "content/public/common/content_switches.h"
#include "content/public/common/service_names.mojom.h"
#include "services/service_manager/embedder/switches.h"
#include "services/service_manager/sandbox/sandbox.h"
#if defined(OS_ANDROID) && BUILDFLAG(ENABLE_ARM_CFI_TABLE)
#include "base/android/apk_assets.h"
@ -66,8 +67,13 @@ CallStackProfileParams::Process GetProcess() {
return CallStackProfileParams::RENDERER_PROCESS;
if (process_type == switches::kGpuProcess)
return CallStackProfileParams::GPU_PROCESS;
if (process_type == switches::kUtilityProcess)
if (process_type == switches::kUtilityProcess) {
auto sandbox_type =
service_manager::SandboxTypeFromCommandLine(*command_line);
if (sandbox_type == service_manager::SandboxType::kNetwork)
return CallStackProfileParams::NETWORK_SERVICE_PROCESS;
return CallStackProfileParams::UTILITY_PROCESS;
}
if (process_type == service_manager::switches::kZygoteProcess)
return CallStackProfileParams::ZYGOTE_PROCESS;
if (process_type == switches::kPpapiPluginProcess)

@ -11,8 +11,12 @@
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/lazy_instance.h"
#include "chrome/common/profiler/stack_sampling_configuration.h"
#include "chrome/common/profiler/thread_profiler.h"
#include "chrome/utility/browser_exposed_utility_interfaces.h"
#include "chrome/utility/services.h"
#include "content/public/child/child_thread.h"
#include "content/public/common/content_switches.h"
#include "services/service_manager/sandbox/switches.h"
#if BUILDFLAG(ENABLE_PRINT_PREVIEW) && defined(OS_WIN)
@ -71,6 +75,25 @@ void ChromeContentUtilityClient::RegisterNetworkBinders(
std::move(g_network_binder_creation_callback.Get()).Run(registry);
}
void ChromeContentUtilityClient::UtilityThreadStarted() {
// Only builds message pipes for utility processes which enable sampling
// profilers.
const base::CommandLine* command_line =
base::CommandLine::ForCurrentProcess();
const std::string process_type =
command_line->GetSwitchValueASCII(switches::kProcessType);
if (process_type ==
switches::kUtilityProcess && // An in-process utility thread may run
// in other processes, only set up
// collector in a utility process.
StackSamplingConfiguration::Get()->IsProfilerEnabledForCurrentProcess()) {
mojo::PendingRemote<metrics::mojom::CallStackProfileCollector> collector;
content::ChildThread::Get()->BindHostReceiver(
collector.InitWithNewPipeAndPassReceiver());
ThreadProfiler::SetCollectorForChildProcess(std::move(collector));
}
}
mojo::ServiceFactory*
ChromeContentUtilityClient::GetMainThreadServiceFactory() {
if (utility_process_running_elevated_)
@ -78,6 +101,13 @@ ChromeContentUtilityClient::GetMainThreadServiceFactory() {
return ::GetMainThreadServiceFactory();
}
void ChromeContentUtilityClient::PostIOThreadCreated(
base::SingleThreadTaskRunner* io_thread_task_runner) {
io_thread_task_runner->PostTask(
FROM_HERE, base::BindOnce(&ThreadProfiler::StartOnChildThread,
metrics::CallStackProfileParams::IO_THREAD));
}
mojo::ServiceFactory* ChromeContentUtilityClient::GetIOThreadServiceFactory() {
return ::GetIOThreadServiceFactory();
}

@ -28,8 +28,11 @@ class ChromeContentUtilityClient : public content::ContentUtilityClient {
// content::ContentUtilityClient:
void ExposeInterfacesToBrowser(mojo::BinderMap* binders) override;
bool OnMessageReceived(const IPC::Message& message) override;
void PostIOThreadCreated(
base::SingleThreadTaskRunner* io_thread_task_runner) override;
void RegisterNetworkBinders(
service_manager::BinderRegistry* registry) override;
void UtilityThreadStarted() override;
mojo::ServiceFactory* GetMainThreadServiceFactory() override;
mojo::ServiceFactory* GetIOThreadServiceFactory() override;

@ -19,6 +19,8 @@ Process ToExecutionContextProcess(CallStackProfileParams::Process process) {
return GPU_PROCESS;
case CallStackProfileParams::UTILITY_PROCESS:
return UTILITY_PROCESS;
case CallStackProfileParams::NETWORK_SERVICE_PROCESS:
return NETWORK_SERVICE_PROCESS;
case CallStackProfileParams::ZYGOTE_PROCESS:
return ZYGOTE_PROCESS;
case CallStackProfileParams::SANDBOX_HELPER_PROCESS:

@ -21,7 +21,8 @@ struct CallStackProfileParams {
ZYGOTE_PROCESS,
SANDBOX_HELPER_PROCESS,
PPAPI_PLUGIN_PROCESS,
PPAPI_BROKER_PROCESS
PPAPI_BROKER_PROCESS,
NETWORK_SERVICE_PROCESS,
};
// The thread from which the collection occurred.

@ -7,6 +7,8 @@
#include "content/browser/utility_process_host.h"
#include "build/build_config.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/content_client.h"
#if defined(OS_LINUX)
#include "components/services/font/public/mojom/font_service.mojom.h" // nogncheck
@ -23,6 +25,7 @@ void UtilityProcessHost::BindHostReceiver(
return;
}
#endif
GetContentClient()->browser()->BindUtilityHostReceiver(std::move(receiver));
}
} // namespace content

@ -1039,6 +1039,10 @@ class CONTENT_EXPORT ContentBrowserClient {
// process. Called on the IO thread.
virtual void BindGpuHostReceiver(mojo::GenericPendingReceiver receiver) {}
// Handles an unhandled incoming interface binding request from a Utility
// process. Called on the IO thread.
virtual void BindUtilityHostReceiver(mojo::GenericPendingReceiver receiver) {}
// Called on the main thread to handle an unhandled interface receiver binding
// request from a render process. See |RenderThread::BindHostReceiver()|.
virtual void BindHostReceiverForRenderer(

@ -44,6 +44,10 @@ class CONTENT_EXPORT ContentUtilityClient {
// corresponding UtilityProcessHost.
virtual void ExposeInterfacesToBrowser(mojo::BinderMap* binders) {}
// Called on the main thread immediately after the IO thread is created.
virtual void PostIOThreadCreated(
base::SingleThreadTaskRunner* io_thread_task_runner) {}
// Allows the embedder to handle an incoming service request. If this is
// called, this utility process was started for the sole purpose of running
// the service identified by |service_name|.

@ -15,9 +15,11 @@
#include "build/build_config.h"
#include "content/child/child_process.h"
#include "content/common/content_switches_internal.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/main_function_params.h"
#include "content/public/common/sandbox_init.h"
#include "content/public/utility/content_utility_client.h"
#include "content/utility/utility_thread_impl.h"
#include "services/service_manager/sandbox/sandbox.h"
#include "services/tracing/public/cpp/trace_startup.h"
@ -118,6 +120,8 @@ int UtilityMain(const MainFunctionParams& parameters) {
#endif
ChildProcess utility_process;
GetContentClient()->utility()->PostIOThreadCreated(
utility_process.io_task_runner());
base::RunLoop run_loop;
utility_process.set_main_thread(
new UtilityThreadImpl(run_loop.QuitClosure()));