
Add child process launch parameter --trace-buffer-handle to facilitate writing tracing data before the child process is sandboxed. This is [1/3] CL of enabling tracing prior to sandboxing. Bug: 380411640 Change-Id: Ifadc5435c61cb2662ad3ee5b575ff477fccdb788 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6159045 Reviewed-by: Eric Seckler <eseckler@chromium.org> Reviewed-by: Alexander Timin <altimin@chromium.org> Reviewed-by: Etienne Pierre-Doray <etiennep@chromium.org> Commit-Queue: Kramer Ge <fangzhoug@chromium.org> Cr-Commit-Position: refs/heads/main@{#1410401}
827 lines
30 KiB
C++
827 lines
30 KiB
C++
// Copyright 2012 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "content/browser/browser_child_process_host_impl.h"
|
|
|
|
#include <memory>
|
|
|
|
#include "base/base_switches.h"
|
|
#include "base/clang_profiling_buildflags.h"
|
|
#include "base/command_line.h"
|
|
#include "base/debug/dump_without_crashing.h"
|
|
#include "base/feature_list.h"
|
|
#include "base/files/file_path.h"
|
|
#include "base/functional/bind.h"
|
|
#include "base/lazy_instance.h"
|
|
#include "base/logging.h"
|
|
#include "base/metrics/histogram_functions.h"
|
|
#include "base/metrics/histogram_macros.h"
|
|
#include "base/metrics/histogram_shared_memory.h"
|
|
#include "base/metrics/persistent_histogram_allocator.h"
|
|
#include "base/metrics/persistent_memory_allocator.h"
|
|
#include "base/not_fatal_until.h"
|
|
#include "base/observer_list.h"
|
|
#include "base/strings/string_number_conversions.h"
|
|
#include "base/strings/string_util.h"
|
|
#include "base/synchronization/waitable_event.h"
|
|
#include "base/task/single_thread_task_runner.h"
|
|
#include "base/token.h"
|
|
#include "base/trace_event/memory_dump_manager.h"
|
|
#include "build/build_config.h"
|
|
#include "components/metrics/histogram_controller.h"
|
|
#include "components/tracing/common/tracing_switches.h"
|
|
#include "content/browser/browser_main_loop.h"
|
|
#include "content/browser/child_process_host_impl.h"
|
|
#include "content/browser/metrics/histogram_shared_memory_config.h"
|
|
#include "content/browser/tracing/background_tracing_manager_impl.h"
|
|
#include "content/public/browser/browser_child_process_host_delegate.h"
|
|
#include "content/public/browser/browser_child_process_observer.h"
|
|
#include "content/public/browser/browser_task_traits.h"
|
|
#include "content/public/browser/browser_thread.h"
|
|
#include "content/public/browser/child_process_data.h"
|
|
#include "content/public/browser/content_browser_client.h"
|
|
#include "content/public/browser/resource_coordinator_service.h"
|
|
#include "content/public/common/content_client.h"
|
|
#include "content/public/common/content_features.h"
|
|
#include "content/public/common/content_switches.h"
|
|
#include "content/public/common/process_type.h"
|
|
#include "content/public/common/result_codes.h"
|
|
#include "content/public/common/sandboxed_process_launcher_delegate.h"
|
|
#include "mojo/public/cpp/bindings/scoped_message_error_crash_key.h"
|
|
#include "mojo/public/cpp/system/platform_handle.h"
|
|
#include "services/tracing/public/cpp/trace_startup.h"
|
|
#include "services/tracing/public/cpp/trace_startup_config.h"
|
|
|
|
#if BUILDFLAG(IS_APPLE)
|
|
#include "content/browser/child_process_task_port_provider_mac.h"
|
|
#endif
|
|
|
|
#if BUILDFLAG(IS_MAC)
|
|
#include "content/browser/sandbox_support_impl.h"
|
|
#include "content/common/sandbox_support.mojom.h"
|
|
#endif
|
|
|
|
#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
|
|
#include "services/tracing/public/cpp/system_tracing_service.h"
|
|
#endif
|
|
|
|
#if BUILDFLAG(IS_WIN)
|
|
#include "content/browser/renderer_host/dwrite_font_proxy_impl_win.h"
|
|
#include "content/public/common/font_cache_dispatcher_win.h"
|
|
#include "content/public/common/font_cache_win.mojom.h"
|
|
#endif
|
|
|
|
#if BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX)
|
|
#include "content/public/common/profiling_utils.h"
|
|
#endif
|
|
|
|
#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
|
|
#include "content/public/browser/browser_message_filter.h"
|
|
#endif
|
|
|
|
namespace content {
|
|
namespace {
|
|
|
|
static base::LazyInstance<
|
|
BrowserChildProcessHostImpl::BrowserChildProcessList>::DestructorAtExit
|
|
g_child_process_list = LAZY_INSTANCE_INITIALIZER;
|
|
|
|
base::LazyInstance<base::ObserverList<BrowserChildProcessObserver>::Unchecked>::
|
|
DestructorAtExit g_browser_child_process_observers =
|
|
LAZY_INSTANCE_INITIALIZER;
|
|
|
|
void NotifyProcessLaunchedAndConnected(const ChildProcessData& data) {
|
|
// Assert that the process is valid, as guaranteed in a comment on the
|
|
// declaration of `BrowserChildProcessLaunchedAndConnected()`.
|
|
CHECK(data.GetProcess().IsValid(), base::NotFatalUntil::M130);
|
|
|
|
for (auto& observer : g_browser_child_process_observers.Get())
|
|
observer.BrowserChildProcessLaunchedAndConnected(data);
|
|
}
|
|
|
|
void NotifyProcessKilled(const ChildProcessData& data,
|
|
const ChildProcessTerminationInfo& info) {
|
|
for (auto& observer : g_browser_child_process_observers.Get())
|
|
observer.BrowserChildProcessKilled(data, info);
|
|
}
|
|
|
|
memory_instrumentation::mojom::ProcessType GetCoordinatorClientProcessType(
|
|
ProcessType process_type) {
|
|
switch (process_type) {
|
|
case PROCESS_TYPE_RENDERER:
|
|
return memory_instrumentation::mojom::ProcessType::RENDERER;
|
|
case PROCESS_TYPE_UTILITY:
|
|
return memory_instrumentation::mojom::ProcessType::UTILITY;
|
|
case PROCESS_TYPE_GPU:
|
|
return memory_instrumentation::mojom::ProcessType::GPU;
|
|
case PROCESS_TYPE_PPAPI_PLUGIN:
|
|
case PROCESS_TYPE_PPAPI_BROKER:
|
|
return memory_instrumentation::mojom::ProcessType::PLUGIN;
|
|
default:
|
|
NOTREACHED();
|
|
}
|
|
}
|
|
void BindTracedProcessFromUIThread(
|
|
base::WeakPtr<BrowserChildProcessHostImpl> weak_host,
|
|
mojo::PendingReceiver<tracing::mojom::TracedProcess> receiver) {
|
|
if (!weak_host)
|
|
return;
|
|
|
|
weak_host->GetHost()->BindReceiver(std::move(receiver));
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// static
|
|
std::unique_ptr<BrowserChildProcessHost> BrowserChildProcessHost::Create(
|
|
content::ProcessType process_type,
|
|
BrowserChildProcessHostDelegate* delegate,
|
|
ChildProcessHost::IpcMode ipc_mode) {
|
|
return std::make_unique<BrowserChildProcessHostImpl>(process_type, delegate,
|
|
ipc_mode);
|
|
}
|
|
|
|
BrowserChildProcessHost* BrowserChildProcessHost::FromID(int child_process_id) {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
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 BUILDFLAG(IS_APPLE)
|
|
base::PortProvider* BrowserChildProcessHost::GetPortProvider() {
|
|
return ChildProcessTaskPortProvider::GetInstance();
|
|
}
|
|
#endif
|
|
|
|
// static
|
|
BrowserChildProcessHostImpl::BrowserChildProcessList*
|
|
BrowserChildProcessHostImpl::GetIterator() {
|
|
return g_child_process_list.Pointer();
|
|
}
|
|
|
|
// static
|
|
void BrowserChildProcessHostImpl::AddObserver(
|
|
BrowserChildProcessObserver* observer) {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
g_browser_child_process_observers.Get().AddObserver(observer);
|
|
}
|
|
|
|
// static
|
|
void BrowserChildProcessHostImpl::RemoveObserver(
|
|
BrowserChildProcessObserver* observer) {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
g_browser_child_process_observers.Get().RemoveObserver(observer);
|
|
}
|
|
|
|
BrowserChildProcessHostImpl::BrowserChildProcessHostImpl(
|
|
content::ProcessType process_type,
|
|
BrowserChildProcessHostDelegate* delegate,
|
|
ChildProcessHost::IpcMode ipc_mode)
|
|
: data_(process_type, ChildProcessHostImpl::GenerateChildProcessUniqueId()),
|
|
delegate_(delegate) {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
|
|
// Create a persistent memory segment for subprocess histograms.
|
|
CreateMetricsAllocator();
|
|
|
|
child_process_host_ = ChildProcessHost::Create(this, ipc_mode);
|
|
|
|
g_child_process_list.Get().push_back(this);
|
|
GetContentClient()->browser()->BrowserChildProcessHostCreated(this);
|
|
GetContentClient()->browser()->ExposeInterfacesToChild(&binder_map_);
|
|
}
|
|
|
|
BrowserChildProcessHostImpl::~BrowserChildProcessHostImpl() {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
|
|
g_child_process_list.Get().remove(this);
|
|
|
|
// Skip sending the disconnected notification if the connected notification
|
|
// was never sent. The only exception here is when the main browser process
|
|
// hosts the child, since InProcessUtilityThreadHelper still depends on this
|
|
// behavior to know when the utility service was shut down.
|
|
if (!launched_and_connected_ && !in_process_)
|
|
return;
|
|
|
|
if (launched_and_connected_ && !exited_abnormally_) {
|
|
for (auto& observer : g_browser_child_process_observers.Get()) {
|
|
observer.BrowserChildProcessExitedNormally(data_,
|
|
GetTerminationInfo(false));
|
|
}
|
|
}
|
|
|
|
for (auto& observer : g_browser_child_process_observers.Get())
|
|
observer.BrowserChildProcessHostDisconnected(data_);
|
|
}
|
|
|
|
// static
|
|
void BrowserChildProcessHostImpl::TerminateAll() {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
// Make a copy since the BrowserChildProcessHost dtor mutates the original
|
|
// list.
|
|
BrowserChildProcessList copy = g_child_process_list.Get();
|
|
for (auto it = copy.begin(); it != copy.end(); ++it) {
|
|
delete (*it)->delegate(); // ~*HostDelegate deletes *HostImpl.
|
|
}
|
|
}
|
|
|
|
void BrowserChildProcessHostImpl::Launch(
|
|
std::unique_ptr<SandboxedProcessLauncherDelegate> delegate,
|
|
std::unique_ptr<base::CommandLine> cmd_line,
|
|
bool terminate_on_shutdown) {
|
|
LaunchWithFileData(
|
|
std::move(delegate), std::move(cmd_line),
|
|
/*file_data=*/std::make_unique<ChildProcessLauncherFileData>(),
|
|
terminate_on_shutdown);
|
|
}
|
|
|
|
const ChildProcessData& BrowserChildProcessHostImpl::GetData() {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
return data_;
|
|
}
|
|
|
|
ChildProcessHost* BrowserChildProcessHostImpl::GetHost() {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
return child_process_host_.get();
|
|
}
|
|
|
|
const base::Process& BrowserChildProcessHostImpl::GetProcess() {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
return data_.GetProcess();
|
|
}
|
|
|
|
std::unique_ptr<base::PersistentMemoryAllocator>
|
|
BrowserChildProcessHostImpl::TakeMetricsAllocator() {
|
|
return std::move(metrics_allocator_);
|
|
}
|
|
|
|
void BrowserChildProcessHostImpl::SetName(const std::u16string& name) {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
data_.name = name;
|
|
}
|
|
|
|
void BrowserChildProcessHostImpl::SetMetricsName(
|
|
const std::string& metrics_name) {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
data_.metrics_name = metrics_name;
|
|
}
|
|
|
|
void BrowserChildProcessHostImpl::SetProcess(base::Process process) {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
DCHECK(!in_process_);
|
|
|
|
// Only NaClProcessHost uses SetProcess(), and it always involve a legacy IPC
|
|
// channel. The channel is never connected at the time of the call, so
|
|
// NotifyProcessLaunchedAndConnected() never has to be invoked here.
|
|
DCHECK(has_legacy_ipc_channel_ && !is_channel_connected_);
|
|
|
|
DCHECK(!process.is_current());
|
|
data_.SetProcess(std::move(process));
|
|
}
|
|
|
|
void BrowserChildProcessHostImpl::ForceShutdown() {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
g_child_process_list.Get().remove(this);
|
|
child_process_host_->ForceShutdown();
|
|
}
|
|
|
|
#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
|
|
void BrowserChildProcessHostImpl::AddFilter(BrowserMessageFilter* filter) {
|
|
child_process_host_->AddFilter(filter->GetFilter());
|
|
}
|
|
#endif
|
|
|
|
void BrowserChildProcessHostImpl::LaunchWithFileData(
|
|
std::unique_ptr<SandboxedProcessLauncherDelegate> delegate,
|
|
std::unique_ptr<base::CommandLine> cmd_line,
|
|
std::unique_ptr<ChildProcessLauncherFileData> file_data,
|
|
bool terminate_on_shutdown) {
|
|
GetContentClient()->browser()->AppendExtraCommandLineSwitches(cmd_line.get(),
|
|
data_.id);
|
|
LaunchWithoutExtraCommandLineSwitches(
|
|
std::move(delegate), std::move(cmd_line), std::move(file_data),
|
|
terminate_on_shutdown);
|
|
}
|
|
|
|
void BrowserChildProcessHostImpl::LaunchWithoutExtraCommandLineSwitches(
|
|
std::unique_ptr<SandboxedProcessLauncherDelegate> delegate,
|
|
std::unique_ptr<base::CommandLine> cmd_line,
|
|
std::unique_ptr<ChildProcessLauncherFileData> file_data,
|
|
bool terminate_on_shutdown) {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
DCHECK(!in_process_);
|
|
|
|
const base::CommandLine& browser_command_line =
|
|
*base::CommandLine::ForCurrentProcess();
|
|
static const char* const kForwardSwitches[] = {
|
|
switches::kDisableInProcessStackTraces,
|
|
switches::kDisableBestEffortTasks,
|
|
switches::kIPCConnectionTimeout,
|
|
switches::kLogBestEffortTasks,
|
|
switches::kPerfettoDisableInterning,
|
|
switches::kTraceToConsole,
|
|
};
|
|
cmd_line->CopySwitchesFrom(browser_command_line, kForwardSwitches);
|
|
|
|
// All processes should have a non-empty metrics name.
|
|
if (data_.metrics_name.empty())
|
|
data_.metrics_name = GetProcessTypeNameInEnglish(data_.process_type);
|
|
|
|
data_.sandbox_type = delegate->GetSandboxType();
|
|
|
|
// Note that if this host has a legacy IPC Channel, we don't dispatch any
|
|
// connection status notifications until we observe OnChannelConnected().
|
|
#if BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX)
|
|
bool is_elevated = false;
|
|
#if BUILDFLAG(IS_WIN)
|
|
is_elevated = (delegate->GetSandboxType() ==
|
|
sandbox::mojom::Sandbox::kNoSandboxAndElevatedPrivileges);
|
|
#endif
|
|
if (!is_elevated)
|
|
child_process_host_->SetProfilingFile(OpenProfilingFile());
|
|
#endif
|
|
|
|
tracing_config_memory_region_ =
|
|
MakeRefCounted<base::RefCountedData<base::ReadOnlySharedMemoryRegion>>(
|
|
tracing::CreateTracingConfigSharedMemory());
|
|
tracing_output_memory_region_ =
|
|
tracing_config_memory_region_->data.IsValid()
|
|
? MakeRefCounted<
|
|
base::RefCountedData<base::UnsafeSharedMemoryRegion>>(
|
|
tracing::CreateTracingOutputSharedMemory())
|
|
: nullptr;
|
|
|
|
child_process_launcher_ = std::make_unique<ChildProcessLauncher>(
|
|
std::move(delegate), std::move(cmd_line), data_.id, this,
|
|
std::move(*child_process_host_->GetMojoInvitation()),
|
|
base::BindRepeating(&BrowserChildProcessHostImpl::OnMojoError,
|
|
weak_factory_.GetWeakPtr(),
|
|
base::SingleThreadTaskRunner::GetCurrentDefault()),
|
|
std::move(file_data),
|
|
base::HistogramSharedMemory::PassOnCommandLineIsEnabled(
|
|
data_.process_type)
|
|
? metrics_shared_region_
|
|
: nullptr,
|
|
tracing_config_memory_region_, tracing_output_memory_region_,
|
|
terminate_on_shutdown);
|
|
ShareMetricsAllocatorToProcess();
|
|
|
|
if (!has_legacy_ipc_channel_)
|
|
OnProcessConnected();
|
|
}
|
|
|
|
#if !BUILDFLAG(IS_ANDROID)
|
|
void BrowserChildProcessHostImpl::SetProcessPriority(
|
|
base::Process::Priority priority) {
|
|
DCHECK(child_process_launcher_);
|
|
DCHECK(!child_process_launcher_->IsStarting());
|
|
child_process_launcher_->SetProcessPriority(priority);
|
|
}
|
|
#endif // !BUILDFLAG(IS_ANDROID)
|
|
|
|
#if BUILDFLAG(IS_ANDROID)
|
|
void BrowserChildProcessHostImpl::EnableWarmUpConnection() {
|
|
can_use_warm_up_connection_ = true;
|
|
}
|
|
|
|
void BrowserChildProcessHostImpl::DumpProcessStack() {
|
|
if (!child_process_launcher_) {
|
|
return;
|
|
}
|
|
child_process_launcher_->DumpProcessStack();
|
|
}
|
|
#endif
|
|
|
|
ChildProcessTerminationInfo BrowserChildProcessHostImpl::GetTerminationInfo(
|
|
bool known_dead) {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
if (!child_process_launcher_) {
|
|
// If the delegate doesn't use Launch() helper.
|
|
ChildProcessTerminationInfo info;
|
|
// TODO(crbug.com/40255458): iOS is single process mode for now.
|
|
#if !BUILDFLAG(IS_IOS)
|
|
info.status = base::GetTerminationStatus(data_.GetProcess().Handle(),
|
|
&info.exit_code);
|
|
#endif
|
|
return info;
|
|
}
|
|
return child_process_launcher_->GetChildTerminationInfo(known_dead);
|
|
}
|
|
|
|
bool BrowserChildProcessHostImpl::OnMessageReceived(
|
|
const IPC::Message& message) {
|
|
return delegate_->OnMessageReceived(message);
|
|
}
|
|
|
|
void BrowserChildProcessHostImpl::OnChannelConnected(int32_t peer_pid) {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
|
|
DCHECK(has_legacy_ipc_channel_);
|
|
is_channel_connected_ = true;
|
|
|
|
delegate_->OnChannelConnected(peer_pid);
|
|
|
|
OnProcessConnected();
|
|
}
|
|
|
|
void BrowserChildProcessHostImpl::OnProcessConnected() {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
#if BUILDFLAG(IS_WIN)
|
|
// From this point onward, the exit of the child process is detected by an
|
|
// error on the IPC channel or ChildProcessHost pipe.
|
|
early_exit_watcher_.StopWatching();
|
|
#endif
|
|
|
|
if (IsProcessLaunched()) {
|
|
launched_and_connected_ = true;
|
|
NotifyProcessLaunchedAndConnected(data_);
|
|
}
|
|
}
|
|
|
|
void BrowserChildProcessHostImpl::OnChannelError() {
|
|
delegate_->OnChannelError();
|
|
}
|
|
|
|
void BrowserChildProcessHostImpl::OnBadMessageReceived(
|
|
const IPC::Message& message) {
|
|
std::string log_message = "Bad message received of type: ";
|
|
if (message.IsValid()) {
|
|
log_message += base::NumberToString(message.type());
|
|
} else {
|
|
log_message += "unknown";
|
|
}
|
|
TerminateOnBadMessageReceived(log_message);
|
|
}
|
|
|
|
void BrowserChildProcessHostImpl::BindChildHistogramFetcherFactory(
|
|
mojo::PendingReceiver<metrics::mojom::ChildHistogramFetcherFactory>
|
|
factory) {
|
|
GetHost()->BindReceiver(std::move(factory));
|
|
}
|
|
|
|
void BrowserChildProcessHostImpl::TerminateOnBadMessageReceived(
|
|
const std::string& error) {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
|
|
// Create a memory dump. This will contain enough stack frames to work out
|
|
// what the bad message was.
|
|
base::debug::DumpWithoutCrashing();
|
|
|
|
TerminateProcessForBadMessage(weak_factory_.GetWeakPtr(), error);
|
|
}
|
|
|
|
void BrowserChildProcessHostImpl::OnChannelInitialized(IPC::Channel* channel) {
|
|
has_legacy_ipc_channel_ = true;
|
|
|
|
// When using a legacy IPC Channel, we defer any notifications until the
|
|
// Channel handshake is complete. See OnChannelConnected().
|
|
is_channel_connected_ = false;
|
|
}
|
|
|
|
void BrowserChildProcessHostImpl::OnChildDisconnected() {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
|
|
tracing_registration_.reset();
|
|
|
|
#if BUILDFLAG(IS_WIN)
|
|
// OnChildDisconnected may be called without OnChannelConnected, so stop the
|
|
// early exit watcher so GetTerminationStatus can close the process handle.
|
|
early_exit_watcher_.StopWatching();
|
|
#endif
|
|
|
|
if (child_process_launcher_.get() || IsProcessLaunched()) {
|
|
ChildProcessTerminationInfo info =
|
|
GetTerminationInfo(true /* known_dead */);
|
|
#if BUILDFLAG(IS_ANDROID)
|
|
exited_abnormally_ = true;
|
|
// Do not treat clean_exit, ie when child process exited due to quitting
|
|
// its main loop, as a crash.
|
|
if (!info.clean_exit) {
|
|
delegate_->OnProcessCrashed(info.exit_code);
|
|
}
|
|
NotifyProcessKilled(data_, info);
|
|
#else // BUILDFLAG(IS_ANDROID)
|
|
switch (info.status) {
|
|
case base::TERMINATION_STATUS_PROCESS_CRASHED:
|
|
case base::TERMINATION_STATUS_ABNORMAL_TERMINATION: {
|
|
exited_abnormally_ = true;
|
|
delegate_->OnProcessCrashed(info.exit_code);
|
|
for (auto& observer : g_browser_child_process_observers.Get())
|
|
observer.BrowserChildProcessCrashed(data_, info);
|
|
UMA_HISTOGRAM_ENUMERATION("ChildProcess.Crashed2",
|
|
static_cast<ProcessType>(data_.process_type),
|
|
PROCESS_TYPE_MAX);
|
|
break;
|
|
}
|
|
#if BUILDFLAG(IS_CHROMEOS)
|
|
case base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM:
|
|
#endif
|
|
case base::TERMINATION_STATUS_PROCESS_WAS_KILLED: {
|
|
exited_abnormally_ = true;
|
|
delegate_->OnProcessCrashed(info.exit_code);
|
|
NotifyProcessKilled(data_, info);
|
|
// Report that this child process was killed.
|
|
UMA_HISTOGRAM_ENUMERATION("ChildProcess.Killed2",
|
|
static_cast<ProcessType>(data_.process_type),
|
|
PROCESS_TYPE_MAX);
|
|
break;
|
|
}
|
|
case base::TERMINATION_STATUS_STILL_RUNNING: {
|
|
UMA_HISTOGRAM_ENUMERATION("ChildProcess.DisconnectedAlive2",
|
|
static_cast<ProcessType>(data_.process_type),
|
|
PROCESS_TYPE_MAX);
|
|
break;
|
|
}
|
|
case base::TERMINATION_STATUS_LAUNCH_FAILED: {
|
|
// This is handled in OnProcessLaunchFailed.
|
|
NOTREACHED();
|
|
}
|
|
case base::TERMINATION_STATUS_NORMAL_TERMINATION: {
|
|
// TODO(wfh): This should not be hit but is sometimes. Investigate.
|
|
break;
|
|
}
|
|
case base::TERMINATION_STATUS_OOM: {
|
|
// TODO(wfh): Decide to what to do with OOMs here.
|
|
break;
|
|
}
|
|
#if BUILDFLAG(IS_WIN)
|
|
case base::TERMINATION_STATUS_INTEGRITY_FAILURE: {
|
|
// TODO(wfh): Decide to what to do with CIG failures here.
|
|
break;
|
|
}
|
|
#endif // BUILDFLAG(IS_WIN)
|
|
case base::TERMINATION_STATUS_MAX_ENUM: {
|
|
NOTREACHED();
|
|
}
|
|
}
|
|
#endif // BUILDFLAG(IS_ANDROID)
|
|
}
|
|
delete delegate_; // Will delete us
|
|
}
|
|
|
|
bool BrowserChildProcessHostImpl::Send(IPC::Message* message) {
|
|
DCHECK(has_legacy_ipc_channel_);
|
|
return child_process_host_->Send(message);
|
|
}
|
|
|
|
void BrowserChildProcessHostImpl::CreateMetricsAllocator() {
|
|
// Create a persistent memory segment for subprocess histograms only if
|
|
// they're active in the browser.
|
|
// TODO(crbug.com/40818143): Remove this.
|
|
if (!base::GlobalHistogramAllocator::Get()) {
|
|
DVLOG(1) << "GlobalHistogramAllocator not configured";
|
|
return;
|
|
}
|
|
|
|
// This class is not expected to be used for renderer child processes.
|
|
// TODO(crbug.com/40109064): CHECK, once proven that this scenario does not
|
|
// occur in the wild, else remove dump and just return early if disproven.
|
|
if (data_.process_type == PROCESS_TYPE_RENDERER) {
|
|
base::debug::DumpWithoutCrashing();
|
|
return;
|
|
}
|
|
|
|
// Get the shared memory configuration for this process type, if any,
|
|
auto shared_memory_config =
|
|
GetHistogramSharedMemoryConfig(data_.process_type);
|
|
if (!shared_memory_config.has_value()) {
|
|
DVLOG(1) << "No histogram shared memory configured: " << "pid=" << data_.id
|
|
<< "; process_type='"
|
|
<< GetProcessTypeNameInEnglish(data_.process_type) << "'";
|
|
return;
|
|
}
|
|
|
|
// Create the shared memory region and histogram allocator.
|
|
auto shared_memory = base::HistogramSharedMemory::Create(
|
|
data_.id, shared_memory_config.value());
|
|
|
|
if (!shared_memory.has_value()) {
|
|
DVLOG(1) << "Failed to create histogram shared memory for pid=" << data_.id
|
|
<< "; process_type='"
|
|
<< GetProcessTypeNameInEnglish(data_.process_type) << "'";
|
|
return;
|
|
}
|
|
|
|
DVLOG(1) << "Createdhistogram shared memory for pid=" << data_.id
|
|
<< "; process_type='"
|
|
<< GetProcessTypeNameInEnglish(data_.process_type) << "'";
|
|
|
|
metrics_shared_region_ =
|
|
MakeRefCounted<base::RefCountedData<base::UnsafeSharedMemoryRegion>>(
|
|
std::move(shared_memory->region));
|
|
metrics_allocator_ = std::move(shared_memory->allocator);
|
|
}
|
|
|
|
void BrowserChildProcessHostImpl::ShareMetricsAllocatorToProcess() {
|
|
// Only get histograms from content process types; skip "embedder" process
|
|
// types.
|
|
const bool is_content_process =
|
|
(data_.process_type < PROCESS_TYPE_CONTENT_END);
|
|
|
|
// Get histogram data from content processes; exchange pings with embedder
|
|
// processes.
|
|
const auto histogram_mode =
|
|
is_content_process
|
|
? metrics::HistogramController::ChildProcessMode::kGetHistogramData
|
|
: metrics::HistogramController::ChildProcessMode::kPingOnly;
|
|
|
|
// If this is a content process, but passing the shared memory region on the
|
|
// command line is NOT enabled for this process type, then we pass the region
|
|
// via the child's HistogramController, below; otherwise, we give the
|
|
// HistogramController a default (invalid) region.
|
|
// TODO(crbug.com/40818143): simplify to always pass an empty region or to
|
|
// elide that param once passing the region via the command line is fully
|
|
// launched for all content process types.
|
|
auto memory_region =
|
|
is_content_process && metrics_shared_region_ &&
|
|
!base::HistogramSharedMemory::PassOnCommandLineIsEnabled(
|
|
data_.process_type)
|
|
? std::move(metrics_shared_region_->data)
|
|
: base::UnsafeSharedMemoryRegion();
|
|
|
|
// Pass the shared memory region to use for future histogram transmission
|
|
// (an invalid region if the region was already passed via the command line)
|
|
// and ask the child to transmit any early histograms that did not get stored
|
|
// in shared memory. This happens exactly once for each child process.
|
|
metrics::HistogramController::GetInstance()->SetHistogramMemory(
|
|
this, std::move(memory_region), histogram_mode);
|
|
|
|
// At this point the shared memory region has either been shared via command
|
|
// line, or it has been given (moved) to the histogram controller. The child
|
|
// process host no longer needs to track it. We can safely release the host's
|
|
// reference.
|
|
metrics_shared_region_.reset();
|
|
}
|
|
|
|
void BrowserChildProcessHostImpl::OnProcessLaunchFailed(int error_code) {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
delegate_->OnProcessLaunchFailed(error_code);
|
|
ChildProcessTerminationInfo info =
|
|
child_process_launcher_->GetChildTerminationInfo(/*known_dead=*/true);
|
|
DCHECK_EQ(info.status, base::TERMINATION_STATUS_LAUNCH_FAILED);
|
|
|
|
for (auto& observer : g_browser_child_process_observers.Get())
|
|
observer.BrowserChildProcessLaunchFailed(data_, info);
|
|
delete delegate_; // Will delete us
|
|
}
|
|
|
|
#if BUILDFLAG(IS_ANDROID)
|
|
bool BrowserChildProcessHostImpl::CanUseWarmUpConnection() {
|
|
return can_use_warm_up_connection_;
|
|
}
|
|
#endif
|
|
|
|
void BrowserChildProcessHostImpl::OnProcessLaunched() {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
|
|
const base::Process& process = child_process_launcher_->GetProcess();
|
|
DCHECK(process.IsValid());
|
|
|
|
#if BUILDFLAG(IS_MAC)
|
|
ChildProcessTaskPortProvider::GetInstance()->OnChildProcessLaunched(
|
|
process.Pid(),
|
|
static_cast<ChildProcessHostImpl*>(child_process_host_.get())
|
|
->child_process());
|
|
#endif
|
|
|
|
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
|
|
child_thread_type_switcher_.SetPid(process.Pid());
|
|
#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
|
|
|
|
#if BUILDFLAG(IS_WIN)
|
|
// Start a WaitableEventWatcher that will invoke OnProcessExitedEarly if the
|
|
// child process exits. This watcher is stopped once the IPC channel is
|
|
// connected and the exit of the child process is detected by an error on the
|
|
// IPC channel thereafter.
|
|
DCHECK(!early_exit_watcher_.GetWatchedObject());
|
|
early_exit_watcher_.StartWatchingOnce(process.Handle(), this);
|
|
#endif
|
|
|
|
DCHECK(!process.is_current());
|
|
data_.SetProcess(process.Duplicate());
|
|
delegate_->OnProcessLaunched();
|
|
|
|
if (is_channel_connected_) {
|
|
launched_and_connected_ = true;
|
|
NotifyProcessLaunchedAndConnected(data_);
|
|
}
|
|
|
|
#if BUILDFLAG(IS_CHROMEOS)
|
|
// In ChromeOS, there are still child processes of NaCl modules, and they
|
|
// don't contribute to tracing actually. So do not register those clients
|
|
// to the tracing service. See https://crbug.com/1101468.
|
|
if (data_.process_type >= PROCESS_TYPE_CONTENT_END)
|
|
return;
|
|
#endif
|
|
|
|
tracing_registration_ = TracingServiceController::Get().RegisterClient(
|
|
process.Pid(), base::BindRepeating(&BindTracedProcessFromUIThread,
|
|
weak_factory_.GetWeakPtr()));
|
|
BackgroundTracingManagerImpl::ActivateForProcess(
|
|
GetData().id,
|
|
static_cast<ChildProcessHostImpl*>(GetHost())->child_process());
|
|
|
|
#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
|
|
system_tracing_service_ = std::make_unique<tracing::SystemTracingService>();
|
|
child_process()->EnableSystemTracingService(
|
|
system_tracing_service_->BindAndPassPendingRemote());
|
|
#endif
|
|
}
|
|
|
|
void BrowserChildProcessHostImpl::RegisterCoordinatorClient(
|
|
mojo::PendingReceiver<memory_instrumentation::mojom::Coordinator> receiver,
|
|
mojo::PendingRemote<memory_instrumentation::mojom::ClientProcess>
|
|
client_process) {
|
|
// Intentionally disallow non-browser processes from getting a Coordinator.
|
|
receiver.reset();
|
|
|
|
// The child process may have already terminated by the time this message is
|
|
// dispatched. We do nothing in that case.
|
|
if (!IsProcessLaunched())
|
|
return;
|
|
|
|
base::trace_event::MemoryDumpManager::GetInstance()
|
|
->GetDumpThreadTaskRunner()
|
|
->PostTask(
|
|
FROM_HERE,
|
|
base::BindOnce(
|
|
[](mojo::PendingReceiver<
|
|
memory_instrumentation::mojom::Coordinator> receiver,
|
|
mojo::PendingRemote<
|
|
memory_instrumentation::mojom::ClientProcess>
|
|
client_process,
|
|
memory_instrumentation::mojom::ProcessType process_type,
|
|
base::ProcessId process_id,
|
|
std::optional<std::string> service_name) {
|
|
GetMemoryInstrumentationRegistry()->RegisterClientProcess(
|
|
std::move(receiver), std::move(client_process),
|
|
process_type, process_id, std::move(service_name));
|
|
},
|
|
std::move(receiver), std::move(client_process),
|
|
GetCoordinatorClientProcessType(
|
|
static_cast<ProcessType>(data_.process_type)),
|
|
child_process_launcher_->GetProcess().Pid(),
|
|
delegate_->GetServiceName()));
|
|
}
|
|
|
|
bool BrowserChildProcessHostImpl::IsProcessLaunched() const {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
return data_.GetProcess().IsValid();
|
|
}
|
|
|
|
// static
|
|
void BrowserChildProcessHostImpl::OnMojoError(
|
|
base::WeakPtr<BrowserChildProcessHostImpl> process,
|
|
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
|
|
const std::string& error) {
|
|
// Create a memory dump with the error message captured in a crash key value.
|
|
// This will make it easy to determine details about what interface call
|
|
// failed.
|
|
//
|
|
// It is important to call DumpWithoutCrashing synchronously - this will help
|
|
// to preserve the callstack and the crash keys present when the bad mojo
|
|
// message was received.
|
|
mojo::debug::ScopedMessageErrorCrashKey scoped_error_key(error);
|
|
base::debug::DumpWithoutCrashing();
|
|
|
|
if (task_runner->BelongsToCurrentThread()) {
|
|
TerminateProcessForBadMessage(process, error);
|
|
} else {
|
|
task_runner->PostTask(
|
|
FROM_HERE,
|
|
base::BindOnce(
|
|
&BrowserChildProcessHostImpl::TerminateProcessForBadMessage,
|
|
process, error));
|
|
}
|
|
}
|
|
|
|
// static
|
|
void BrowserChildProcessHostImpl::TerminateProcessForBadMessage(
|
|
base::WeakPtr<BrowserChildProcessHostImpl> process,
|
|
const std::string& error) {
|
|
if (!process)
|
|
return;
|
|
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
|
|
switches::kDisableKillAfterBadIPC)) {
|
|
return;
|
|
}
|
|
DVLOG(1) << "Terminating child process for bad message: " << error;
|
|
process->child_process_launcher_->Terminate(RESULT_CODE_KILLED_BAD_MESSAGE);
|
|
}
|
|
|
|
#if BUILDFLAG(IS_WIN)
|
|
|
|
void BrowserChildProcessHostImpl::OnObjectSignaled(HANDLE object) {
|
|
OnChildDisconnected();
|
|
}
|
|
|
|
#endif
|
|
|
|
} // namespace content
|