blob: 637eb299f8ad1a3d51be8dc65784b2adc2a5e721 [file] [log] [blame]
// 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/persistent_histogram_allocator.h"
#include "base/metrics/persistent_memory_allocator.h"
#include "base/observer_list.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 "build/chromeos_buildflags.h"
#include "components/tracing/common/trace_startup_config.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_controller.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_message_filter.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"
#if BUILDFLAG(IS_APPLE)
#include "content/browser/child_process_task_port_provider_mac.h"
#endif
#if BUILDFLAG(IS_MAC)
#include "content/browser/sandbox_support_mac_impl.h"
#include "content/common/sandbox_support_mac.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
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) {
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();
return memory_instrumentation::mojom::ProcessType::OTHER;
}
}
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), delegate_(delegate) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
data_.id = ChildProcessHostImpl::GenerateChildProcessUniqueId();
child_process_host_ = ChildProcessHost::Create(this, ipc_mode);
g_child_process_list.Get().push_back(this);
GetContentClient()->browser()->BrowserChildProcessHostCreated(this);
// Create a persistent memory segment for subprocess histograms.
CreateMetricsAllocator();
}
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.
}
}
// static
void BrowserChildProcessHostImpl::CopyTraceStartupFlags(
base::CommandLine* cmd_line) {
tracing::PropagateTracingFlagsToChildProcessCmdLine(cmd_line);
}
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();
}
void BrowserChildProcessHostImpl::AddFilter(BrowserMessageFilter* filter) {
child_process_host_->AddFilter(filter->GetFilter());
}
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::kDisableLogging,
switches::kEnableLogging,
switches::kIPCConnectionTimeout,
switches::kLogBestEffortTasks,
switches::kLogFile,
switches::kLoggingLevel,
switches::kMojoCoreLibraryPath,
switches::kPerfettoDisableInterning,
switches::kTraceToConsole,
switches::kV,
switches::kVModule,
};
cmd_line->CopySwitchesFrom(browser_command_line, kForwardSwitches,
std::size(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
child_process_ = 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), terminate_on_shutdown);
ShareMetricsAllocatorToProcess();
if (!has_legacy_ipc_channel_)
OnProcessConnected();
}
#if !BUILDFLAG(IS_ANDROID)
void BrowserChildProcessHostImpl::SetProcessBackgrounded(bool is_background) {
DCHECK(child_process_);
DCHECK(!child_process_->IsStarting());
child_process_->SetProcessBackgrounded(is_background);
}
#endif // !BUILDFLAG(IS_ANDROID)
#if BUILDFLAG(IS_ANDROID)
void BrowserChildProcessHostImpl::EnableWarmUpConnection() {
can_use_warm_up_connection_ = true;
}
void BrowserChildProcessHostImpl::DumpProcessStack() {
if (!child_process_)
return;
child_process_->DumpProcessStack();
}
#endif
ChildProcessTerminationInfo BrowserChildProcessHostImpl::GetTerminationInfo(
bool known_dead) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!child_process_) {
// If the delegate doesn't use Launch() helper.
ChildProcessTerminationInfo info;
// TODO(crbug.com/1412835): 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_->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 += std::to_string(message.type());
} else {
log_message += "unknown";
}
TerminateOnBadMessageReceived(log_message);
}
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_.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();
break;
}
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();
break;
}
}
#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/1290457): Remove this.
if (!base::GlobalHistogramAllocator::Get())
return;
// Determine the correct parameters based on the process type.
size_t memory_size;
base::StringPiece metrics_name;
switch (data_.process_type) {
case PROCESS_TYPE_UTILITY:
// This needs to be larger for the network service.
memory_size = 256 << 10; // 256 KiB
metrics_name = "UtilityMetrics";
break;
case PROCESS_TYPE_ZYGOTE:
memory_size = 64 << 10; // 64 KiB
metrics_name = "ZygoteMetrics";
break;
case PROCESS_TYPE_SANDBOX_HELPER:
memory_size = 64 << 10; // 64 KiB
metrics_name = "SandboxHelperMetrics";
break;
case PROCESS_TYPE_GPU:
// This needs to be larger for the display-compositor in the gpu process.
memory_size = 256 << 10; // 256 KiB
metrics_name = "GpuMetrics";
break;
case PROCESS_TYPE_PPAPI_PLUGIN:
memory_size = 64 << 10; // 64 KiB
metrics_name = "PpapiPluginMetrics";
break;
case PROCESS_TYPE_PPAPI_BROKER:
memory_size = 64 << 10; // 64 KiB
metrics_name = "PpapiBrokerMetrics";
break;
default:
return;
}
// Create the shared memory segment and attach an allocator to it.
// Mapping the memory shouldn't fail but be safe if it does; everything
// will continue to work but just as if persistence weren't available.
base::WritableSharedMemoryRegion shm_region =
base::WritableSharedMemoryRegion::Create(memory_size);
base::WritableSharedMemoryMapping shm_mapping = shm_region.Map();
if (!shm_region.IsValid() || !shm_mapping.IsValid())
return;
metrics_allocator_ =
std::make_unique<base::WritableSharedPersistentMemoryAllocator>(
std::move(shm_mapping), static_cast<uint64_t>(data_.id),
metrics_name);
metrics_shared_region_ = std::move(shm_region);
}
void BrowserChildProcessHostImpl::ShareMetricsAllocatorToProcess() {
if (metrics_allocator_) {
HistogramController::GetInstance()->SetHistogramMemory<ChildProcessHost>(
GetHost(), std::move(metrics_shared_region_));
} else {
HistogramController::GetInstance()->SetHistogramMemory<ChildProcessHost>(
GetHost(), base::WritableSharedMemoryRegion());
}
}
void BrowserChildProcessHostImpl::OnProcessLaunchFailed(int error_code) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
delegate_->OnProcessLaunchFailed(error_code);
ChildProcessTerminationInfo info =
child_process_->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_->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_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 detecter 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_ASH)
// 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,
absl::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_->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;
}
LOG(ERROR) << "Terminating child process for bad message: " << error;
process->child_process_->Terminate(RESULT_CODE_KILLED_BAD_MESSAGE);
}
#if BUILDFLAG(IS_WIN)
void BrowserChildProcessHostImpl::OnObjectSignaled(HANDLE object) {
OnChildDisconnected();
}
#endif
} // namespace content