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