xref: /aosp_15_r20/external/cronet/base/metrics/histogram_shared_memory_unittest.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 "base/base_switches.h"
8 #include "base/command_line.h"
9 #include "base/metrics/persistent_histogram_allocator.h"
10 #include "base/process/launch.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_split.h"
13 #include "base/test/multiprocess_test.h"
14 #include "base/test/scoped_feature_list.h"
15 #include "base/test/test_timeouts.h"
16 #include "base/unguessable_token.h"
17 #include "build/build_config.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "testing/multiprocess_func_list.h"
20 
21 #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
22 #include "base/files/platform_file.h"
23 #include "base/posix/global_descriptors.h"
24 #endif
25 
26 namespace base {
27 namespace {
28 
29 constexpr size_t kArbitrarySize = 64 << 10;
30 
31 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
32 constexpr GlobalDescriptors::Key kArbitraryDescriptorKey = 42;
33 #endif
34 
35 }  // namespace
36 
TEST(HistogramSharedMemoryTest,Create)37 TEST(HistogramSharedMemoryTest, Create) {
38   UnsafeSharedMemoryRegion region;
39 
40   constexpr int kProcessId = 1234;
41   constexpr int kProcessType = 5678;
42   constexpr char kProcessName[] = "TestProcess";
43 
44   auto shared_memory = HistogramSharedMemory::Create(
45       kProcessId, {kProcessType, kProcessName, kArbitrarySize});
46 
47   ASSERT_TRUE(shared_memory.has_value());
48 
49   ASSERT_TRUE(shared_memory->region.IsValid());
50   EXPECT_EQ(kArbitrarySize, shared_memory->region.GetSize());
51 
52   ASSERT_TRUE(shared_memory->allocator);
53   EXPECT_EQ(kArbitrarySize, shared_memory->allocator->size());
54 }
55 
TEST(HistogramSharedMemoryTest,PassSharedMemoryRegion_Disabled)56 TEST(HistogramSharedMemoryTest, PassSharedMemoryRegion_Disabled) {
57   // Ensure the feature is disabled.
58   test::ScopedFeatureList feature_list;
59   feature_list.InitAndDisableFeature(kPassHistogramSharedMemoryOnLaunch);
60 
61   // Create a shared memory region to pass.
62   auto memory = UnsafeSharedMemoryRegion::Create(kArbitrarySize);
63   ASSERT_TRUE(memory.IsValid());
64 
65   // Initialize the command line and launch options.
66   CommandLine command_line = GetMultiProcessTestChildBaseCommandLine();
67   command_line.AppendSwitchASCII("type", "test-child");
68   LaunchOptions launch_options;
69 
70 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
71   ScopedFD descriptor_to_share;
72 #endif
73 
74   // Update the launch parameters.
75   HistogramSharedMemory::AddToLaunchParameters(memory.Duplicate(),
76 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
77                                                kArbitraryDescriptorKey,
78                                                descriptor_to_share,
79 #endif  // BUILDFLAG(IS_POSIX)
80                                                &command_line, &launch_options);
81 
82   // The metrics shared memory handle should NOT be added to the command line.
83   EXPECT_FALSE(command_line.HasSwitch(switches::kMetricsSharedMemoryHandle));
84 }
85 
MULTIPROCESS_TEST_MAIN(InitFromLaunchParameters)86 MULTIPROCESS_TEST_MAIN(InitFromLaunchParameters) {
87 // On POSIX we generally use the descriptor map to look up inherited handles.
88 // On most POSIX platforms we have to manually sure the mapping is updated,
89 // for the purposes of this test.
90 //
91 // Note:
92 //  - This doesn't apply on Apple platforms (which use Rendezvous Keys)
93 //  - On Android the global descriptor table is managed by the launcher
94 //    service, so we don't have to manually update the mapping here.
95 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID)
96   GlobalDescriptors::GetInstance()->Set(
97       kArbitraryDescriptorKey,
98       kArbitraryDescriptorKey + GlobalDescriptors::kBaseDescriptor);
99 #endif
100 
101   EXPECT_FALSE(GlobalHistogramAllocator::Get());
102   // Simulate launching with the serialized parameters.
103 
104   HistogramSharedMemory::InitFromLaunchParameters(
105       *CommandLine::ForCurrentProcess());
106   EXPECT_TRUE(GlobalHistogramAllocator::Get());
107   return 0;
108 }
109 
110 #if !BUILDFLAG(IS_IOS)
111 using HistogramSharedMemoryTest = ::testing::TestWithParam<bool>;
112 
113 INSTANTIATE_TEST_SUITE_P(All,
114                          HistogramSharedMemoryTest,
115                          ::testing::Values(/*launch_options.elevated=*/false
116 #if BUILDFLAG(IS_WIN)
117                                            ,
118                                            /*launch_options.elevated=*/true
119 #endif
120                                            ));
121 
TEST_P(HistogramSharedMemoryTest,PassSharedMemoryRegion_Enabled)122 TEST_P(HistogramSharedMemoryTest, PassSharedMemoryRegion_Enabled) {
123   // Ensure the feature is enabled.
124   test::ScopedFeatureList feature_list;
125   feature_list.InitAndEnableFeature(kPassHistogramSharedMemoryOnLaunch);
126 
127   // Create a shared memory region to pass.
128   auto memory = UnsafeSharedMemoryRegion::Create(kArbitrarySize);
129   ASSERT_TRUE(memory.IsValid());
130 
131   // Initialize the command line and launch options.
132   CommandLine command_line = GetMultiProcessTestChildBaseCommandLine();
133   command_line.AppendSwitchASCII("type", "test-child");
134   LaunchOptions launch_options;
135 
136   // On windows, check both the elevated and non-elevated launches.
137 #if BUILDFLAG(IS_WIN)
138   launch_options.start_hidden = true;
139   launch_options.elevated = GetParam();
140 #elif BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
141   ScopedFD descriptor_to_share;
142 #endif
143 
144   // Update the launch parameters.
145   HistogramSharedMemory::AddToLaunchParameters(memory.Duplicate(),
146 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
147                                                kArbitraryDescriptorKey,
148                                                descriptor_to_share,
149 #endif
150                                                &command_line, &launch_options);
151 
152   // The metrics shared memory handle should be added to the command line.
153   ASSERT_TRUE(command_line.HasSwitch(switches::kMetricsSharedMemoryHandle));
154   SCOPED_TRACE(
155       command_line.GetSwitchValueASCII(switches::kMetricsSharedMemoryHandle));
156 
157 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
158   // On posix, AddToLaunchParameters() ignores the launch options and instead
159   // returns the descriptor to be shared. This is because the browser child
160   // launcher helper manages a separate list of files to share via the zygote,
161   // if available. If, like in this test scenario, there's ultimately no zygote
162   // to use, launch helper updates the launch options to share the descriptor
163   // mapping relative to a base descriptor.
164   launch_options.fds_to_remap.emplace_back(descriptor_to_share.get(),
165                                            kArbitraryDescriptorKey);
166   //  GlobalDescriptors::GetInstance()->Set(kArbitraryDescriptorKey,
167   //  descriptor_to_share);
168 #if !BUILDFLAG(IS_ANDROID)
169   for (auto& pair : launch_options.fds_to_remap) {
170     pair.second += base::GlobalDescriptors::kBaseDescriptor;
171   }
172 #endif  // !BUILDFLAG(IS_ANDROID)
173 #endif  // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
174 
175   // Launch the child process.
176   Process process = SpawnMultiProcessTestChild("InitFromLaunchParameters",
177                                                command_line, launch_options);
178 
179   // The child process returns non-zero if it could not open the shared memory
180   // region based on the launch parameters.
181   int exit_code;
182   EXPECT_TRUE(WaitForMultiprocessTestChildExit(
183       process, TestTimeouts::action_timeout(), &exit_code));
184   EXPECT_EQ(0, exit_code);
185 }
186 #endif
187 
188 }  // namespace base
189