1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
|
// Copyright 2023 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/metrics/histogram_shared_memory.h"
#include <string_view>
#include "base/base_switches.h"
#include "base/debug/crash_logging.h"
#include "base/logging.h"
#include "base/memory/shared_memory_mapping.h"
#include "base/memory/writable_shared_memory_region.h"
#include "base/metrics/histogram_macros_local.h"
#include "base/metrics/persistent_histogram_allocator.h"
#include "base/metrics/persistent_memory_allocator.h"
#include "base/process/launch.h"
#include "base/process/process_handle.h"
#include "base/process/process_info.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/unguessable_token.h"
// On Apple platforms, the shared memory handle is shared using a Mach port
// rendezvous key.
#if BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_IOS_TVOS)
#include "base/apple/mach_port_rendezvous.h"
#endif
// On POSIX, the shared memory handle is a file_descriptor mapped in the
// GlobalDescriptors table.
#if BUILDFLAG(IS_POSIX)
#include "base/posix/global_descriptors.h"
#endif
#if BUILDFLAG(IS_WIN)
#include <windows.h>
#include "base/win/win_util.h"
#endif
#if BUILDFLAG(IS_FUCHSIA)
#include <lib/zx/vmo.h>
#include <zircon/process.h>
#include "base/fuchsia/fuchsia_logging.h"
#endif
// This file supports passing a read/write histogram shared memory region
// between a parent process and child process. The information about the
// shared memory region is encoded into a command-line switch value.
//
// Format: "handle,[irp],guid-high,guid-low,size".
//
// The switch value is composed of 5 segments, separated by commas:
//
// 1. The platform-specific handle id for the shared memory as a string.
// 2. [irp] to indicate whether the handle is inherited (i, most platforms),
// sent via rendezvous (r, MacOS), or should be queried from the parent
// (p, Windows).
// 3. The high 64 bits of the shared memory block GUID.
// 4. The low 64 bits of the shared memory block GUID.
// 5. The size of the shared memory segment as a string.
//
// TODO(crbug.com/40109064): Refactor the common logic here and in
// base/metrics/field_trial.cc
namespace base {
BASE_FEATURE(kPassHistogramSharedMemoryOnLaunch,
"PassHistogramSharedMemoryOnLaunch",
#if BUILDFLAG(IS_ANDROID)
FEATURE_DISABLED_BY_DEFAULT
#else
FEATURE_ENABLED_BY_DEFAULT
#endif
);
#if BUILDFLAG(IS_APPLE)
const shared_memory::SharedMemoryMachPortRendezvousKey
HistogramSharedMemory::kRendezvousKey = 'hsmr';
#endif
HistogramSharedMemory::SharedMemory::SharedMemory(
UnsafeSharedMemoryRegion r,
std::unique_ptr<PersistentMemoryAllocator> a)
: region(std::move(r)), allocator(std::move(a)) {
CHECK(region.IsValid());
CHECK(allocator);
}
HistogramSharedMemory::SharedMemory::~SharedMemory() = default;
HistogramSharedMemory::SharedMemory::SharedMemory(
HistogramSharedMemory::SharedMemory&&) = default;
HistogramSharedMemory::SharedMemory&
HistogramSharedMemory::SharedMemory::operator=(
HistogramSharedMemory::SharedMemory&&) = default;
// static
std::optional<HistogramSharedMemory::SharedMemory>
HistogramSharedMemory::Create(int process_id,
const HistogramSharedMemory::Config& config) {
auto region = UnsafeSharedMemoryRegion::Create(config.memory_size_bytes);
if (!region.IsValid()) {
DVLOG(1) << "Failed to create shared memory region.";
return std::nullopt;
}
auto mapping = region.Map();
if (!mapping.IsValid()) {
DVLOG(1) << "Failed to create shared memory mapping.";
return std::nullopt;
}
return SharedMemory{std::move(region),
std::make_unique<WritableSharedPersistentMemoryAllocator>(
std::move(mapping), static_cast<uint64_t>(process_id),
config.allocator_name)};
}
// static
bool HistogramSharedMemory::PassOnCommandLineIsEnabled(int process_type) {
// On ChromeOS and for "utility" processes on other platforms there seems to
// be one or more mechanisms on startup which walk through all inherited
// shared memory regions and take a read-only handle to them. When we later
// attempt to deserialize the handle info and take a writable handle we
// find that the handle is already owned in read-only mode, triggering
// a crash due to "FD ownership violation".
//
// Example: The call to OpenSymbolFiles() in base/debug/stack_trace_posix.cc
// grabs a read-only handle to the shmem region for some process types.
//
// TODO(crbug.com/40109064): Fix ChromeOS GPU and Android utility processes.
// Constants from content::ProcessType;
[[maybe_unused]] constexpr int PROCESS_TYPE_GPU = 9;
[[maybe_unused]] constexpr int PROCESS_TYPE_UTILITY = 6;
return (FeatureList::IsEnabled(kPassHistogramSharedMemoryOnLaunch)
#if BUILDFLAG(IS_CHROMEOS)
&& process_type != PROCESS_TYPE_GPU
#elif BUILDFLAG(IS_ANDROID)
&& process_type != PROCESS_TYPE_UTILITY
#endif
);
}
// static
void HistogramSharedMemory::AddToLaunchParameters(
const UnsafeSharedMemoryRegion& histogram_shmem_region,
#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
GlobalDescriptors::Key descriptor_key,
ScopedFD& descriptor_to_share,
#endif
CommandLine* command_line,
LaunchOptions* launch_options) {
CHECK(histogram_shmem_region.IsValid());
CHECK(command_line);
shared_memory::AddToLaunchParameters(::switches::kMetricsSharedMemoryHandle,
histogram_shmem_region,
#if BUILDFLAG(IS_APPLE)
kRendezvousKey,
#elif BUILDFLAG(IS_POSIX)
descriptor_key, descriptor_to_share,
#endif
command_line, launch_options);
}
// static
void HistogramSharedMemory::InitFromLaunchParameters(
const CommandLine& command_line) {
// TODO(crbug.com/40109064): Clean up once fully launched.
if (!command_line.HasSwitch(switches::kMetricsSharedMemoryHandle)) {
return;
}
CHECK(!GlobalHistogramAllocator::Get());
DVLOG(1) << "Initializing histogram shared memory from command line for "
<< command_line.GetSwitchValueASCII("type");
auto shmem_region = shared_memory::UnsafeSharedMemoryRegionFrom(
command_line.GetSwitchValueASCII(switches::kMetricsSharedMemoryHandle));
SCOPED_CRASH_KEY_NUMBER(
"HistogramAllocator", "SharedMemError",
static_cast<int>(shmem_region.has_value()
? shared_memory::SharedMemoryError::kNoError
: shmem_region.error()));
CHECK(shmem_region.has_value() && shmem_region.value().IsValid())
<< "Invald memory region passed on command line.";
GlobalHistogramAllocator::CreateWithSharedMemoryRegion(shmem_region.value());
auto* global_allocator = GlobalHistogramAllocator::Get();
CHECK(global_allocator);
global_allocator->CreateTrackingHistograms(global_allocator->Name());
}
} // namespace base
|