0

[tracing] Add trace-buffer-handle to child process launch

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}
This commit is contained in:
Kramer Ge
2025-01-23 09:19:53 -08:00
committed by Chromium LUCI CQ
parent a6770bb4a7
commit c80f84e4c9
20 changed files with 378 additions and 11 deletions

@ -69,7 +69,7 @@ constexpr size_t kMaxInfoPlistDataSize = 18 * 1024;
#endif
// This limit is arbitrary and can be safely increased in the future.
constexpr size_t kMaximumRendezvousPorts = 5;
constexpr size_t kMaximumRendezvousPorts = 6;
enum MachRendezvousMsgId : mach_msg_id_t {
kMachRendezvousMsgIdRequest = 'mrzv',

@ -46,6 +46,11 @@ const char kEnableTracing[] = "enable-tracing";
// ignored if --trace-startup or --trace-shutdown is provided.
const char kTraceConfigHandle[] = "trace-config-handle";
// Handle to the shared memory segment a child process should use to transmit
// tracing data back to the tracing service. This flag allows tracing to be
// recorded before sandbox setup.
const char kTraceBufferHandle[] = "trace-buffer-handle";
// Sets the time in seconds until startup tracing ends. If omitted:
// - if --trace-startup is specified, a default of 5 seconds is used.
// - if --enable-tracing is specified, tracing lasts until the browser is

@ -14,6 +14,7 @@ TRACING_EXPORT extern const char kTraceConfigFile[];
TRACING_EXPORT extern const char kTraceStartup[];
TRACING_EXPORT extern const char kTraceConfigHandle[];
TRACING_EXPORT extern const char kEnableTracing[];
TRACING_EXPORT extern const char kTraceBufferHandle[];
TRACING_EXPORT extern const char kTraceStartupDuration[];
TRACING_EXPORT extern const char kTraceStartupFile[];
TRACING_EXPORT extern const char kEnableTracingOutput[];

@ -850,6 +850,9 @@ int ContentMainRunnerImpl::Initialize(ContentMainParams params) {
g_fds->Set(kTraceConfigSharedMemoryDescriptor,
kTraceConfigSharedMemoryDescriptor +
base::GlobalDescriptors::kBaseDescriptor);
g_fds->Set(kTraceOutputSharedMemoryDescriptor,
kTraceOutputSharedMemoryDescriptor +
base::GlobalDescriptors::kBaseDescriptor);
#endif // !BUILDFLAG(IS_ANDROID)
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_OPENBSD)

@ -350,6 +350,12 @@ void BrowserChildProcessHostImpl::LaunchWithoutExtraCommandLineSwitches(
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,
@ -362,7 +368,8 @@ void BrowserChildProcessHostImpl::LaunchWithoutExtraCommandLineSwitches(
data_.process_type)
? metrics_shared_region_
: nullptr,
tracing_config_memory_region_, terminate_on_shutdown);
tracing_config_memory_region_, tracing_output_memory_region_,
terminate_on_shutdown);
ShareMetricsAllocatorToProcess();
if (!has_legacy_ipc_channel_)

@ -245,6 +245,12 @@ class BrowserChildProcessHostImpl
scoped_refptr<base::RefCountedData<base::ReadOnlySharedMemoryRegion>>
tracing_config_memory_region_;
// The tracing output memory region to transmit traces. Ownership of the
// memory region object is shared with the child process launcher/helper which
// runs, and is destroyed, asynchronously.
scoped_refptr<base::RefCountedData<base::UnsafeSharedMemoryRegion>>
tracing_output_memory_region_;
// Indicates if the main browser process is used instead of a dedicated child
// process.
bool in_process_ = false;

@ -104,6 +104,8 @@ ChildProcessLauncher::ChildProcessLauncher(
histogram_memory_region,
scoped_refptr<base::RefCountedData<base::ReadOnlySharedMemoryRegion>>
tracing_config_memory_region,
scoped_refptr<base::RefCountedData<base::UnsafeSharedMemoryRegion>>
tracing_output_memory_region,
bool terminate_on_shutdown)
: client_(client),
starting_(true),
@ -130,7 +132,8 @@ ChildProcessLauncher::ChildProcessLauncher(
#endif
std::move(mojo_invitation), process_error_callback, std::move(file_data),
std::move(histogram_memory_region),
std::move(tracing_config_memory_region));
std::move(tracing_config_memory_region),
std::move(tracing_output_memory_region));
helper_->StartLaunchOnClientThread();
}

@ -249,6 +249,8 @@ class CONTENT_EXPORT ChildProcessLauncher {
histogram_memory_region = nullptr,
scoped_refptr<base::RefCountedData<base::ReadOnlySharedMemoryRegion>>
trace_config_memory_region = nullptr,
scoped_refptr<base::RefCountedData<base::UnsafeSharedMemoryRegion>>
trace_output_memory_region = nullptr,
bool terminate_on_shutdown = true);
ChildProcessLauncher(const ChildProcessLauncher&) = delete;

@ -196,6 +196,51 @@ void PassStartupTracingConfigSharedMemoryHandle(
#endif // BUILDFLAG(USE_BLINK)
}
// This function is NOP if the platform does not use Blink.
void PassStartupOutputSharedMemoryHandle(
[[maybe_unused]] const base::UnsafeSharedMemoryRegion*
trace_output_memory_region,
[[maybe_unused]] base::CommandLine* command_line,
[[maybe_unused]] base::LaunchOptions* launch_options,
[[maybe_unused]] FileMappedForLaunch* files_to_register) {
#if BUILDFLAG(USE_BLINK)
CHECK(command_line);
if (!trace_output_memory_region || !trace_output_memory_region->IsValid()) {
return;
}
#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
// TODO(crbug.com/40109064): content::FileMappedForLaunch (POSIX) is redundant
// wrt the base::LaunchOptions::<platform-specific-handles-to-transfer>
// members. Refactor this so that the details of base::Launch vs Zygote on
// (some) POSIX platforms is an implementation detail and not exposed here.
// I.e., populate launch options (like for all other platforms) then if it's
// a Zygote launch pull out the handles to transfer and send them to the
// zygote, instead of (for posix only) ignoring the launch-options here,
// populating the |files_to_register| param then (if there's no zygote)
// filling in |launch_options|
CHECK(files_to_register);
base::ScopedFD descriptor_to_transfer;
#else
CHECK(launch_options);
#endif // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
tracing::AddTraceOutputToLaunchParameters(*trace_output_memory_region,
#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
kTraceOutputSharedMemoryDescriptor,
descriptor_to_transfer,
#endif // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
command_line, launch_options);
#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
if (descriptor_to_transfer.is_valid()) {
files_to_register->Transfer(kTraceOutputSharedMemoryDescriptor,
std::move(descriptor_to_transfer));
}
#endif // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
#endif // BUILDFLAG(USE_BLINK)
}
} // namespace
ChildProcessLauncherHelper::Process::Process() = default;
@ -234,7 +279,9 @@ ChildProcessLauncherHelper::ChildProcessLauncherHelper(
scoped_refptr<base::RefCountedData<base::UnsafeSharedMemoryRegion>>
histogram_memory_region,
scoped_refptr<base::RefCountedData<base::ReadOnlySharedMemoryRegion>>
tracing_config_memory_region)
tracing_config_memory_region,
scoped_refptr<base::RefCountedData<base::UnsafeSharedMemoryRegion>>
tracing_output_memory_region)
: child_process_id_(child_process_id),
client_task_runner_(base::SequencedTaskRunner::GetCurrentDefault()),
command_line_(std::move(command_line)),
@ -249,6 +296,7 @@ ChildProcessLauncherHelper::ChildProcessLauncherHelper(
#endif
histogram_memory_region_(std::move(histogram_memory_region)),
tracing_config_memory_region_(std::move(tracing_config_memory_region)),
tracing_output_memory_region_(std::move(tracing_output_memory_region)),
init_start_time_(base::TimeTicks::Now()) {
if (!mojo::core::GetConfiguration().is_broker_process &&
!command_line_->HasSwitch(switches::kDisableMojoBroker)) {
@ -329,6 +377,10 @@ void ChildProcessLauncherHelper::LaunchOnLauncherThread() {
tracing_config_memory_region_ ? &tracing_config_memory_region_->data
: nullptr,
command_line(), options_ptr, files_to_register.get());
PassStartupOutputSharedMemoryHandle(
tracing_output_memory_region_ ? &tracing_output_memory_region_->data
: nullptr,
command_line(), options_ptr, files_to_register.get());
// Transfer logging switches & handles if necessary.
PassLoggingSwitches(options_ptr, command_line());

@ -136,7 +136,9 @@ class ChildProcessLauncherHelper
scoped_refptr<base::RefCountedData<base::UnsafeSharedMemoryRegion>>
histogram_memory_region,
scoped_refptr<base::RefCountedData<base::ReadOnlySharedMemoryRegion>>
tracing_config_memory_region);
tracing_config_memory_region,
scoped_refptr<base::RefCountedData<base::UnsafeSharedMemoryRegion>>
tracing_output_memory_region);
// The methods below are defined in the order they are called.
@ -367,6 +369,12 @@ class ChildProcessLauncherHelper
scoped_refptr<base::RefCountedData<base::ReadOnlySharedMemoryRegion>>
tracing_config_memory_region_;
// Startup tracing output shared memory region. Ownership of the memory region
// object is shared with the process host which runs, and is destroyed,
// asynchronously.
scoped_refptr<base::RefCountedData<base::UnsafeSharedMemoryRegion>>
tracing_output_memory_region_;
// Creation time of the helper, used for metrics.
// TODO(crbug.com/40287847): Remove when parallel launching is finished.
base::TimeTicks init_start_time_;

@ -1775,6 +1775,12 @@ bool RenderProcessHostImpl::Init() {
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;
auto file_data = std::make_unique<ChildProcessLauncherFileData>();
#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC)
@ -1793,7 +1799,7 @@ bool RenderProcessHostImpl::Init() {
PROCESS_TYPE_RENDERER)
? metrics_memory_region_
: nullptr,
tracing_config_memory_region_);
tracing_config_memory_region_, tracing_output_memory_region_);
channel_->Pause();
// In single process mode, browser-side tracing and memory will cover the

@ -1472,6 +1472,13 @@ class CONTENT_EXPORT RenderProcessHostImpl
scoped_refptr<base::RefCountedData<base::ReadOnlySharedMemoryRegion>>
tracing_config_memory_region_;
// The tracing output memory region. Ownership of the memory region is
// allocated by the process host (this object) but ownership is shared with
// the child process launcher/helper which runs, and is destroyed,
// asynchronously.
scoped_refptr<base::RefCountedData<base::UnsafeSharedMemoryRegion>>
tracing_output_memory_region_;
bool channel_connected_ = false;
bool sent_render_process_ready_ = false;
bool sent_process_created_ = false;

@ -21,6 +21,7 @@ enum {
kHistogramSharedMemoryDescriptor,
kTraceConfigSharedMemoryDescriptor,
kTraceOutputSharedMemoryDescriptor,
// Reserves 100 to 199 for dynamically generated IDs.
kContentDynamicDescriptorStart = 100,

@ -94,6 +94,7 @@ source_set("tests") {
"public/cpp/perfetto/trace_packet_tokenizer_unittest.cc",
"public/cpp/perfetto/traced_value_proto_writer_unittest.cc",
"public/cpp/stack_sampling/tracing_sampler_profiler_unittest.cc",
"public/cpp/trace_startup_shared_memory_unittest.cc",
]
deps = [

@ -5,15 +5,18 @@
#include "services/tracing/public/cpp/perfetto/perfetto_tracing_backend.h"
#include "base/auto_reset.h"
#include "base/command_line.h"
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/shared_memory_switch.h"
#include "base/memory/weak_ptr.h"
#include "base/task/sequenced_task_runner.h"
#include "base/tracing/tracing_tls.h"
#include "base/unguessable_token.h"
#include "build/build_config.h"
#include "components/tracing/common/tracing_switches.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/system/data_pipe_drainer.h"
#include "services/tracing/public/cpp/perfetto/shared_memory.h"
@ -45,9 +48,6 @@ constexpr size_t kDefaultSMBPageSizeBytes = 4 * 1024;
constexpr size_t kDefaultSMBPageSizeBytes = 32 * 1024;
#endif
// TODO(crbug.com/40574594): Figure out a good buffer size.
constexpr size_t kDefaultSMBSizeBytes = 4 * 1024 * 1024;
constexpr char kErrorTracingFailed[] = "Tracing failed";
} // namespace
@ -683,12 +683,25 @@ PerfettoTracingBackend::ConnectProducer(const ConnectProducerArgs& args) {
uint32_t shmem_size_hint = args.shmem_size_hint_bytes;
uint32_t shmem_page_size_hint = args.shmem_page_size_hint_bytes;
if (shmem_size_hint == 0)
shmem_size_hint = kDefaultSMBSizeBytes;
shmem_size_hint = kDefaultSharedMemorySize;
if (shmem_page_size_hint == 0)
shmem_page_size_hint = kDefaultSMBPageSizeBytes;
if (args.use_producer_provided_smb) {
shm = std::make_unique<ChromeBaseSharedMemory>(shmem_size_hint);
auto* command_line = base::CommandLine::ForCurrentProcess();
base::UnsafeSharedMemoryRegion unsafe_shm;
if (command_line->HasSwitch(switches::kTraceBufferHandle)) {
auto shmem_region = base::shared_memory::UnsafeSharedMemoryRegionFrom(
command_line->GetSwitchValueASCII(switches::kTraceBufferHandle));
if (shmem_region->IsValid()) {
DCHECK_EQ(shmem_size_hint, shmem_region->GetSize());
unsafe_shm = std::move(shmem_region.value());
}
}
if (!unsafe_shm.IsValid()) {
unsafe_shm = base::UnsafeSharedMemoryRegion::Create(shmem_size_hint);
}
shm = std::make_unique<ChromeBaseSharedMemory>(std::move(unsafe_shm));
arbiter = perfetto::SharedMemoryArbiter::CreateUnboundInstance(
shm.get(), shmem_page_size_hint, ShmemMode::kDefault);
}

@ -14,6 +14,9 @@
namespace tracing {
// TODO(crbug.com/40574594): Figure out a good buffer size.
inline constexpr size_t kDefaultSharedMemorySize = 4 * 1024 * 1024; // 4 KB
// This wraps //base's shmem implementation for Perfetto to consume.
class COMPONENT_EXPORT(TRACING_CPP) ChromeBaseSharedMemory
: public perfetto::SharedMemory {

@ -14,6 +14,7 @@
#include "components/tracing/common/tracing_switches.h"
#include "services/tracing/public/cpp/perfetto/perfetto_config.h"
#include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h"
#include "services/tracing/public/cpp/perfetto/shared_memory.h"
#include "services/tracing/public/cpp/perfetto/traced_value_proto_writer.h"
#include "services/tracing/public/cpp/trace_event_args_allowlist.h"
#include "services/tracing/public/cpp/trace_startup_config.h"
@ -34,6 +35,8 @@ namespace {
#if BUILDFLAG(IS_APPLE)
constexpr base::MachPortsForRendezvous::key_type kTraceConfigRendezvousKey =
'trcc';
constexpr base::MachPortsForRendezvous::key_type kTraceBufferRendezvousKey =
'trbc';
#endif
constexpr uint32_t kStartupTracingTimeoutMs = 30 * 1000; // 30 sec
@ -145,6 +148,32 @@ base::ReadOnlySharedMemoryRegion CreateTracingConfigSharedMemory() {
return std::move(shm.region);
}
base::UnsafeSharedMemoryRegion CreateTracingOutputSharedMemory() {
#if DCHECK_IS_ON()
// This should not be called if tracing config shm was not created beforehand.
base::trace_event::TraceLog* trace_log =
base::trace_event::TraceLog::GetInstance();
const auto& startup_config = TraceStartupConfig::GetInstance();
DCHECK(startup_config.IsEnabled() || trace_log->IsEnabled());
if (!startup_config.IsEnabled()) {
bool has_relevant_config = std::any_of(
trace_log->GetTrackEventSessions().begin(),
trace_log->GetTrackEventSessions().end(), [](const auto& session) {
return session.backend_type == perfetto::kCustomBackend &&
!session.config.has_interceptor_config();
});
DCHECK(has_relevant_config);
}
#endif // DCHECK_IS_ON()
auto shm = base::UnsafeSharedMemoryRegion::Create(kDefaultSharedMemorySize);
if (!shm.IsValid()) {
return base::UnsafeSharedMemoryRegion();
}
return shm;
}
void COMPONENT_EXPORT(TRACING_CPP) AddTraceConfigToLaunchParameters(
const base::ReadOnlySharedMemoryRegion& read_only_memory_region,
#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
@ -164,4 +193,23 @@ void COMPONENT_EXPORT(TRACING_CPP) AddTraceConfigToLaunchParameters(
command_line, launch_options);
}
void COMPONENT_EXPORT(TRACING_CPP) AddTraceOutputToLaunchParameters(
const base::UnsafeSharedMemoryRegion& unsafe_memory_region,
#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
base::GlobalDescriptors::Key descriptor_key,
base::ScopedFD& out_descriptor_to_share,
#endif
base::CommandLine* command_line,
base::LaunchOptions* launch_options) {
base::shared_memory::AddToLaunchParameters(switches::kTraceBufferHandle,
unsafe_memory_region,
#if BUILDFLAG(IS_APPLE)
kTraceBufferRendezvousKey,
#elif BUILDFLAG(IS_POSIX)
descriptor_key,
out_descriptor_to_share,
#endif
command_line, launch_options);
}
} // namespace tracing

@ -7,6 +7,7 @@
#include "base/component_export.h"
#include "base/memory/read_only_shared_memory_region.h"
#include "base/memory/unsafe_shared_memory_region.h"
#include "base/process/launch.h"
#include "build/build_config.h"
#include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h"
@ -61,6 +62,11 @@ void COMPONENT_EXPORT(TRACING_CPP)
base::ReadOnlySharedMemoryRegion COMPONENT_EXPORT(TRACING_CPP)
CreateTracingConfigSharedMemory();
// If tracing is enabled, returns a writeable SMB as destination of tracing
// data, to be forwarded at child process creation.
base::UnsafeSharedMemoryRegion COMPONENT_EXPORT(TRACING_CPP)
CreateTracingOutputSharedMemory();
// Tells the child process to begin tracing right away via command line
// flags and launch options, given a SMB config obtained with
// CreateTracingConfigSharedMemory().
@ -73,6 +79,16 @@ void COMPONENT_EXPORT(TRACING_CPP) AddTraceConfigToLaunchParameters(
base::CommandLine* command_line,
base::LaunchOptions* launch_options);
// Tells the child process to write tracing data to this SMB.
void COMPONENT_EXPORT(TRACING_CPP) AddTraceOutputToLaunchParameters(
const base::UnsafeSharedMemoryRegion& unsafe_memory_region,
#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
base::GlobalDescriptors::Key descriptor_key,
base::ScopedFD& out_descriptor_to_share,
#endif
base::CommandLine* command_line,
base::LaunchOptions* launch_options);
} // namespace tracing
#endif // SERVICES_TRACING_PUBLIC_CPP_TRACE_STARTUP_H_

@ -23,6 +23,7 @@ class BackgroundStartupTracingTest;
namespace tracing {
class TraceStartupConfigTest;
class TraceStartupSharedMemoryTest;
// TraceStartupConfig is a singleton that contains the configurations of startup
// tracing. One can use --trace-startup flag or, for more complicated
@ -146,6 +147,7 @@ class COMPONENT_EXPORT(TRACING_CPP) TraceStartupConfig {
friend class content::CommandlineStartupTracingTest;
friend class content::BackgroundStartupTracingTest;
friend class ::tracing::TraceStartupConfigTest;
friend class ::tracing::TraceStartupSharedMemoryTest;
constexpr static int kDefaultStartupDurationInSeconds = 5;

@ -0,0 +1,183 @@
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/memory/ptr_util.h"
#include "base/memory/shared_memory_switch.h"
#include "base/memory/unsafe_shared_memory_region.h"
#include "base/process/launch.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/test/multiprocess_test.h"
#include "base/test/test_timeouts.h"
#include "base/trace_event/trace_log.h"
#include "build/build_config.h"
#include "components/tracing/common/tracing_switches.h"
#include "mojo/core/embedder/embedder.h"
#include "services/tracing/public/cpp/perfetto/shared_memory.h"
#include "services/tracing/public/cpp/trace_startup.h"
#include "services/tracing/public/cpp/trace_startup_config.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/multiprocess_func_list.h"
#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
#include "base/posix/global_descriptors.h"
#endif
namespace tracing {
namespace {
#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
constexpr base::GlobalDescriptors::Key kArbitraryDescriptorKey = 42;
#endif
} // namespace
TEST(TraceStartupSharedMemoryTest, Create) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(switches::kTraceStartup);
auto shared_memory = CreateTracingOutputSharedMemory();
ASSERT_TRUE(shared_memory.IsValid());
EXPECT_EQ(kDefaultSharedMemorySize, shared_memory.GetSize());
}
MULTIPROCESS_TEST_MAIN(InitFromLaunchParameters) {
// On POSIX we generally use the descriptor map to look up inherited handles.
// On most POSIX platforms we have to manually make sure the mapping is updated,
// for the purposes of this test.
//
// Note:
// - This doesn't apply on Apple platforms (which use Rendezvous Keys)
// - On Android the global descriptor table is managed by the launcher
// service, so we don't have to manually update the mapping here.
#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID)
base::GlobalDescriptors::GetInstance()->Set(
kArbitraryDescriptorKey,
kArbitraryDescriptorKey + base::GlobalDescriptors::kBaseDescriptor);
#endif
EXPECT_FALSE(IsTracingInitialized());
// On Windows and Fuchsia getting shmem handle from --trace-buffer-handle can
// only be done once and subsequent calls `UnsafeSharedMemoryRegionFrom()`
// calls will not get a valid `shmem_region`. So we skip tracing init, to
// avoid `ConnectProducer()` grabbing the shmem first.
#if !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_FUCHSIA)
base::FeatureList::InitInstance("", "");
base::ThreadPoolInstance::CreateAndStartWithDefaultParams("StartupTraceTest");
tracing::InitTracingPostThreadPoolStartAndFeatureList(
/*enable_consumer=*/false);
// Simulate launching with the serialized parameters.
EnableStartupTracingIfNeeded();
EXPECT_TRUE(IsTracingInitialized());
EXPECT_TRUE(base::trace_event::TraceLog::GetInstance()->IsEnabled());
#endif // !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_FUCHSIA)
auto* command_line = base::CommandLine::ForCurrentProcess();
base::UnsafeSharedMemoryRegion unsafe_shm;
EXPECT_TRUE(command_line->HasSwitch(switches::kTraceBufferHandle));
auto shmem_region = base::shared_memory::UnsafeSharedMemoryRegionFrom(
command_line->GetSwitchValueASCII(switches::kTraceBufferHandle));
EXPECT_TRUE(shmem_region->IsValid());
EXPECT_EQ(kDefaultSharedMemorySize, shmem_region->GetSize());
return 0;
}
class TraceStartupSharedMemoryTest : public ::testing::TestWithParam<bool> {
protected:
void Initialize() {
startup_config_ = base::WrapUnique(new TraceStartupConfig());
}
std::unique_ptr<TraceStartupConfig> startup_config_;
};
INSTANTIATE_TEST_SUITE_P(All,
TraceStartupSharedMemoryTest,
::testing::Values(/*launch_options.elevated=*/false
#if BUILDFLAG(IS_WIN)
,
/*launch_options.elevated=*/true
#endif
));
TEST_P(TraceStartupSharedMemoryTest, PassSharedMemoryRegion) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(switches::kTraceStartup);
Initialize();
ASSERT_TRUE(startup_config_->IsEnabled());
auto shm = CreateTracingOutputSharedMemory();
ASSERT_TRUE(shm.IsValid());
// Initialize the command line and launch options.
base::CommandLine command_line =
base::GetMultiProcessTestChildBaseCommandLine();
command_line.AppendSwitchASCII("type", "test-child");
base::LaunchOptions launch_options;
// On windows, check both the elevated and non-elevated launches.
#if BUILDFLAG(IS_WIN)
launch_options.start_hidden = true;
launch_options.elevated = GetParam();
#elif BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
base::ScopedFD descriptor_to_share;
#endif
// Update the launch parameters.
AddTraceOutputToLaunchParameters(shm,
#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
kArbitraryDescriptorKey, descriptor_to_share,
#endif
&command_line, &launch_options);
#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
// On posix, AddToLaunchParameters() ignores the launch options and instead
// returns the descriptor to be shared. This is because the browser child
// launcher helper manages a separate list of files to share via the zygote,
// if available. If, like in this test scenario, there's ultimately no zygote
// to use, launch helper updates the launch options to share the descriptor
// mapping relative to a base descriptor.
launch_options.fds_to_remap.emplace_back(descriptor_to_share.get(),
kArbitraryDescriptorKey);
#if !BUILDFLAG(IS_ANDROID)
for (auto& pair : launch_options.fds_to_remap) {
pair.second += base::GlobalDescriptors::kBaseDescriptor;
}
#endif // !BUILDFLAG(IS_ANDROID)
#endif // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED);
// services/test/run_all_unittests.cc sets up ipc_thread, and android's
// MultiprocessTestClientLauncher.launchClient asserts that child_process
// cannot be called from main thread, so send this task to the io task runner.
bool success = mojo::core::GetIOTaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](base::WaitableEvent* wait, const base::CommandLine& command_line,
const base::LaunchOptions& launch_options) {
// Launch the child process.
base::Process process = base::SpawnMultiProcessTestChild(
"InitFromLaunchParameters", command_line, launch_options);
// The child process returns non-zero if it could not open the
// shared memory region based on the launch parameters.
int exit_code = -1;
EXPECT_TRUE(WaitForMultiprocessTestChildExit(
process, TestTimeouts::action_timeout(), &exit_code));
EXPECT_EQ(0, exit_code);
},
&wait, command_line, launch_options));
EXPECT_TRUE(success);
wait.TimedWait(TestTimeouts::action_timeout());
}
} // namespace tracing