[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:

committed by
Chromium LUCI CQ

parent
a6770bb4a7
commit
c80f84e4c9
base/apple
components/tracing/common
content
app
browser
browser_child_process_host_impl.ccbrowser_child_process_host_impl.hchild_process_launcher.ccchild_process_launcher.hchild_process_launcher_helper.ccchild_process_launcher_helper.h
renderer_host
public
common
services/tracing
@ -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
|
Reference in New Issue
Block a user