1 // Copyright 2018 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 <iomanip>
6 #include <map>
7 #include <memory>
8 #include <utility>
9 #include <vector>
10
11 #include "base/containers/adapters.h"
12 #include "base/functional/callback.h"
13 #include "base/functional/callback_helpers.h"
14 #include "base/profiler/module_cache.h"
15 #include "base/ranges/algorithm.h"
16 #include "base/strings/string_piece.h"
17 #include "base/test/bind.h"
18 #include "build/build_config.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20
21 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
22 #include "base/debug/proc_maps_linux.h"
23 #endif
24
25 // Note: The special-case IS_CHROMEOS code inside GetDebugBasenameForModule to
26 // handle the interaction between that function and
27 // SetProcessTitleFromCommandLine() is tested in
28 // content/common/set_process_title_linux_unittest.cc due to dependency issues.
29
30 namespace base {
31 namespace {
32
AFunctionForTest()33 int AFunctionForTest() {
34 return 42;
35 }
36
37 // Provides a module that is guaranteed to be isolated from (and non-contiguous
38 // with) any other module, by placing the module in the middle of a block of
39 // heap memory.
40 class IsolatedModule : public ModuleCache::Module {
41 public:
IsolatedModule(bool is_native=true)42 explicit IsolatedModule(bool is_native = true)
43 : is_native_(is_native), memory_region_(new char[kRegionSize]) {}
44
45 // ModuleCache::Module
GetBaseAddress() const46 uintptr_t GetBaseAddress() const override {
47 // Place the module in the middle of the region.
48 return reinterpret_cast<uintptr_t>(&memory_region_[kRegionSize / 4]);
49 }
50
GetId() const51 std::string GetId() const override { return ""; }
GetDebugBasename() const52 FilePath GetDebugBasename() const override { return FilePath(); }
GetSize() const53 size_t GetSize() const override { return kRegionSize / 2; }
IsNative() const54 bool IsNative() const override { return is_native_; }
55
56 private:
57 static const int kRegionSize = 100;
58
59 bool is_native_;
60 std::unique_ptr<char[]> memory_region_;
61 };
62
63 // Provides a fake module with configurable base address and size.
64 class FakeModule : public ModuleCache::Module {
65 public:
FakeModule(uintptr_t base_address,size_t size,bool is_native=true,OnceClosure destruction_closure=OnceClosure ())66 FakeModule(uintptr_t base_address,
67 size_t size,
68 bool is_native = true,
69 OnceClosure destruction_closure = OnceClosure())
70 : base_address_(base_address),
71 size_(size),
72 is_native_(is_native),
73 destruction_closure_runner_(std::move(destruction_closure)) {}
74
75 FakeModule(const FakeModule&) = delete;
76 FakeModule& operator=(const FakeModule&) = delete;
77
GetBaseAddress() const78 uintptr_t GetBaseAddress() const override { return base_address_; }
GetId() const79 std::string GetId() const override { return ""; }
GetDebugBasename() const80 FilePath GetDebugBasename() const override { return FilePath(); }
GetSize() const81 size_t GetSize() const override { return size_; }
IsNative() const82 bool IsNative() const override { return is_native_; }
83
84 private:
85 uintptr_t base_address_;
86 size_t size_;
87 bool is_native_;
88 ScopedClosureRunner destruction_closure_runner_;
89 };
90
91 // Utility function to add a single non-native module during test setup. Returns
92 // a pointer to the provided module.
AddNonNativeModule(ModuleCache * cache,std::unique_ptr<const ModuleCache::Module> module)93 const ModuleCache::Module* AddNonNativeModule(
94 ModuleCache* cache,
95 std::unique_ptr<const ModuleCache::Module> module) {
96 const ModuleCache::Module* module_ptr = module.get();
97 std::vector<std::unique_ptr<const ModuleCache::Module>> modules;
98 modules.push_back(std::move(module));
99 cache->UpdateNonNativeModules({}, std::move(modules));
100 return module_ptr;
101 }
102
103 #if (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_IOS) && !defined(ARCH_CPU_ARM64)) || \
104 BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_WIN)
105 #define MAYBE_TEST(TestSuite, TestName) TEST(TestSuite, TestName)
106 #else
107 #define MAYBE_TEST(TestSuite, TestName) TEST(TestSuite, DISABLED_##TestName)
108 #endif
109
MAYBE_TEST(ModuleCacheTest,GetDebugBasename)110 MAYBE_TEST(ModuleCacheTest, GetDebugBasename) {
111 ModuleCache cache;
112 const ModuleCache::Module* module =
113 cache.GetModuleForAddress(reinterpret_cast<uintptr_t>(&AFunctionForTest));
114 ASSERT_NE(nullptr, module);
115 #if BUILDFLAG(IS_ANDROID)
116 EXPECT_EQ("libbase_unittests__library",
117 // Different build configurations varyingly use .so vs. .cr.so for
118 // the module extension. Remove all the extensions in both cases.
119 module->GetDebugBasename()
120 .RemoveFinalExtension()
121 .RemoveFinalExtension()
122 .value());
123 #elif BUILDFLAG(IS_POSIX)
124 EXPECT_EQ("base_unittests", module->GetDebugBasename().value());
125 #elif BUILDFLAG(IS_WIN)
126 EXPECT_EQ(L"base_unittests.exe.pdb", module->GetDebugBasename().value());
127 #endif
128 }
129
130 // Checks that ModuleCache returns the same module instance for
131 // addresses within the module.
MAYBE_TEST(ModuleCacheTest,LookupCodeAddresses)132 MAYBE_TEST(ModuleCacheTest, LookupCodeAddresses) {
133 uintptr_t ptr1 = reinterpret_cast<uintptr_t>(&AFunctionForTest);
134 uintptr_t ptr2 = ptr1 + 1;
135 ModuleCache cache;
136 const ModuleCache::Module* module1 = cache.GetModuleForAddress(ptr1);
137 const ModuleCache::Module* module2 = cache.GetModuleForAddress(ptr2);
138 EXPECT_EQ(module1, module2);
139 EXPECT_NE(nullptr, module1);
140 EXPECT_GT(module1->GetSize(), 0u);
141 EXPECT_LE(module1->GetBaseAddress(), ptr1);
142 EXPECT_GT(module1->GetBaseAddress() + module1->GetSize(), ptr2);
143 }
144
MAYBE_TEST(ModuleCacheTest,LookupRange)145 MAYBE_TEST(ModuleCacheTest, LookupRange) {
146 ModuleCache cache;
147 auto to_inject = std::make_unique<IsolatedModule>();
148 const ModuleCache::Module* module = to_inject.get();
149 cache.AddCustomNativeModule(std::move(to_inject));
150
151 EXPECT_EQ(nullptr, cache.GetModuleForAddress(module->GetBaseAddress() - 1));
152 EXPECT_EQ(module, cache.GetModuleForAddress(module->GetBaseAddress()));
153 EXPECT_EQ(module, cache.GetModuleForAddress(module->GetBaseAddress() +
154 module->GetSize() - 1));
155 EXPECT_EQ(nullptr, cache.GetModuleForAddress(module->GetBaseAddress() +
156 module->GetSize()));
157 }
158
MAYBE_TEST(ModuleCacheTest,LookupNonNativeModule)159 MAYBE_TEST(ModuleCacheTest, LookupNonNativeModule) {
160 ModuleCache cache;
161 const ModuleCache::Module* module =
162 AddNonNativeModule(&cache, std::make_unique<IsolatedModule>(false));
163
164 EXPECT_EQ(nullptr, cache.GetModuleForAddress(module->GetBaseAddress() - 1));
165 EXPECT_EQ(module, cache.GetModuleForAddress(module->GetBaseAddress()));
166 EXPECT_EQ(module, cache.GetModuleForAddress(module->GetBaseAddress() +
167 module->GetSize() - 1));
168 EXPECT_EQ(nullptr, cache.GetModuleForAddress(module->GetBaseAddress() +
169 module->GetSize()));
170 }
171
MAYBE_TEST(ModuleCacheTest,LookupOverlaidNonNativeModule)172 MAYBE_TEST(ModuleCacheTest, LookupOverlaidNonNativeModule) {
173 ModuleCache cache;
174
175 auto native_module_to_inject = std::make_unique<IsolatedModule>();
176 const ModuleCache::Module* native_module = native_module_to_inject.get();
177 cache.AddCustomNativeModule(std::move(native_module_to_inject));
178
179 // Overlay the native module with the non-native module, starting 8 bytes into
180 // the native modules and ending 8 bytes before the end of the module.
181 const ModuleCache::Module* non_native_module = AddNonNativeModule(
182 &cache,
183 std::make_unique<FakeModule>(native_module->GetBaseAddress() + 8,
184 native_module->GetSize() - 16, false));
185
186 EXPECT_EQ(native_module,
187 cache.GetModuleForAddress(non_native_module->GetBaseAddress() - 1));
188 EXPECT_EQ(non_native_module,
189 cache.GetModuleForAddress(non_native_module->GetBaseAddress()));
190 EXPECT_EQ(non_native_module,
191 cache.GetModuleForAddress(non_native_module->GetBaseAddress() +
192 non_native_module->GetSize() - 1));
193 EXPECT_EQ(native_module,
194 cache.GetModuleForAddress(non_native_module->GetBaseAddress() +
195 non_native_module->GetSize()));
196 }
197
MAYBE_TEST(ModuleCacheTest,UpdateNonNativeModulesAdd)198 MAYBE_TEST(ModuleCacheTest, UpdateNonNativeModulesAdd) {
199 ModuleCache cache;
200 std::vector<std::unique_ptr<const ModuleCache::Module>> modules;
201 modules.push_back(std::make_unique<FakeModule>(1, 1, false));
202 const ModuleCache::Module* module = modules.back().get();
203 cache.UpdateNonNativeModules({}, std::move(modules));
204
205 EXPECT_EQ(module, cache.GetModuleForAddress(1));
206 }
207
MAYBE_TEST(ModuleCacheTest,UpdateNonNativeModulesRemove)208 MAYBE_TEST(ModuleCacheTest, UpdateNonNativeModulesRemove) {
209 ModuleCache cache;
210 std::vector<std::unique_ptr<const ModuleCache::Module>> modules;
211 modules.push_back(std::make_unique<FakeModule>(1, 1, false));
212 const ModuleCache::Module* module = modules.back().get();
213 cache.UpdateNonNativeModules({}, std::move(modules));
214 cache.UpdateNonNativeModules({module}, {});
215
216 EXPECT_EQ(nullptr, cache.GetModuleForAddress(1));
217 }
218
MAYBE_TEST(ModuleCacheTest,UpdateNonNativeModulesRemoveModuleIsNotDestroyed)219 MAYBE_TEST(ModuleCacheTest, UpdateNonNativeModulesRemoveModuleIsNotDestroyed) {
220 bool was_destroyed = false;
221 {
222 ModuleCache cache;
223 std::vector<std::unique_ptr<const ModuleCache::Module>> modules;
224 modules.push_back(std::make_unique<FakeModule>(
225 1, 1, false,
226 BindLambdaForTesting([&was_destroyed]() { was_destroyed = true; })));
227 const ModuleCache::Module* module = modules.back().get();
228 cache.UpdateNonNativeModules({}, std::move(modules));
229 cache.UpdateNonNativeModules({module}, {});
230
231 EXPECT_FALSE(was_destroyed);
232 }
233 EXPECT_TRUE(was_destroyed);
234 }
235
236 // Regression test to validate that when modules are partitioned into modules to
237 // keep and modules to remove, the modules to remove are not destroyed.
238 // https://crbug.com/1127466 case 2.
MAYBE_TEST(ModuleCacheTest,UpdateNonNativeModulesPartitioning)239 MAYBE_TEST(ModuleCacheTest, UpdateNonNativeModulesPartitioning) {
240 int destroyed_count = 0;
241 const auto record_destroyed = [&destroyed_count]() { ++destroyed_count; };
242 {
243 ModuleCache cache;
244 std::vector<std::unique_ptr<const ModuleCache::Module>> modules;
245 modules.push_back(std::make_unique<FakeModule>(
246 1, 1, false, BindLambdaForTesting(record_destroyed)));
247 const ModuleCache::Module* module1 = modules.back().get();
248 modules.push_back(std::make_unique<FakeModule>(
249 2, 1, false, BindLambdaForTesting(record_destroyed)));
250 cache.UpdateNonNativeModules({}, std::move(modules));
251 cache.UpdateNonNativeModules({module1}, {});
252
253 EXPECT_EQ(0, destroyed_count);
254 }
255 EXPECT_EQ(2, destroyed_count);
256 }
257
MAYBE_TEST(ModuleCacheTest,UpdateNonNativeModulesReplace)258 MAYBE_TEST(ModuleCacheTest, UpdateNonNativeModulesReplace) {
259 ModuleCache cache;
260 // Replace a module with another larger module at the same base address.
261 std::vector<std::unique_ptr<const ModuleCache::Module>> modules1;
262 modules1.push_back(std::make_unique<FakeModule>(1, 1, false));
263 const ModuleCache::Module* module1 = modules1.back().get();
264 std::vector<std::unique_ptr<const ModuleCache::Module>> modules2;
265 modules2.push_back(std::make_unique<FakeModule>(1, 2, false));
266 const ModuleCache::Module* module2 = modules2.back().get();
267
268 cache.UpdateNonNativeModules({}, std::move(modules1));
269 cache.UpdateNonNativeModules({module1}, std::move(modules2));
270
271 EXPECT_EQ(module2, cache.GetModuleForAddress(2));
272 }
273
MAYBE_TEST(ModuleCacheTest,UpdateNonNativeModulesMultipleRemovedModulesAtSameAddress)274 MAYBE_TEST(ModuleCacheTest,
275 UpdateNonNativeModulesMultipleRemovedModulesAtSameAddress) {
276 int destroyed_count = 0;
277 const auto record_destroyed = [&destroyed_count]() { ++destroyed_count; };
278 ModuleCache cache;
279
280 // Checks that non-native modules can be repeatedly added and removed at the
281 // same addresses, and that all are retained in the cache.
282 std::vector<std::unique_ptr<const ModuleCache::Module>> modules1;
283 modules1.push_back(std::make_unique<FakeModule>(
284 1, 1, false, BindLambdaForTesting(record_destroyed)));
285 const ModuleCache::Module* module1 = modules1.back().get();
286
287 std::vector<std::unique_ptr<const ModuleCache::Module>> modules2;
288 modules2.push_back(std::make_unique<FakeModule>(
289 1, 1, false, BindLambdaForTesting(record_destroyed)));
290 const ModuleCache::Module* module2 = modules2.back().get();
291
292 cache.UpdateNonNativeModules({}, std::move(modules1));
293 cache.UpdateNonNativeModules({module1}, std::move(modules2));
294 cache.UpdateNonNativeModules({module2}, {});
295
296 EXPECT_EQ(0, destroyed_count);
297 }
298
MAYBE_TEST(ModuleCacheTest,UpdateNonNativeModulesCorrectModulesRemoved)299 MAYBE_TEST(ModuleCacheTest, UpdateNonNativeModulesCorrectModulesRemoved) {
300 ModuleCache cache;
301
302 std::vector<std::unique_ptr<const ModuleCache::Module>> to_add;
303 for (int i = 0; i < 5; ++i) {
304 to_add.push_back(std::make_unique<FakeModule>(i + 1, 1, false));
305 }
306
307 std::vector<const ModuleCache::Module*> to_remove = {to_add[1].get(),
308 to_add[3].get()};
309
310 // Checks that the correct modules are removed when removing some but not all
311 // modules.
312 cache.UpdateNonNativeModules({}, std::move(to_add));
313 cache.UpdateNonNativeModules({to_remove}, {});
314
315 DCHECK_NE(nullptr, cache.GetModuleForAddress(1));
316 DCHECK_EQ(nullptr, cache.GetModuleForAddress(2));
317 DCHECK_NE(nullptr, cache.GetModuleForAddress(3));
318 DCHECK_EQ(nullptr, cache.GetModuleForAddress(4));
319 DCHECK_NE(nullptr, cache.GetModuleForAddress(5));
320 }
321
MAYBE_TEST(ModuleCacheTest,ModulesList)322 MAYBE_TEST(ModuleCacheTest, ModulesList) {
323 ModuleCache cache;
324 uintptr_t ptr = reinterpret_cast<uintptr_t>(&AFunctionForTest);
325 const ModuleCache::Module* native_module = cache.GetModuleForAddress(ptr);
326 const ModuleCache::Module* non_native_module =
327 AddNonNativeModule(&cache, std::make_unique<FakeModule>(1, 2, false));
328
329 EXPECT_NE(nullptr, native_module);
330 std::vector<const ModuleCache::Module*> modules = cache.GetModules();
331 ASSERT_EQ(2u, modules.size());
332 EXPECT_EQ(native_module, modules[0]);
333 EXPECT_EQ(non_native_module, modules[1]);
334 }
335
MAYBE_TEST(ModuleCacheTest,InvalidModule)336 MAYBE_TEST(ModuleCacheTest, InvalidModule) {
337 ModuleCache cache;
338 EXPECT_EQ(nullptr, cache.GetModuleForAddress(1));
339 }
340
341 // arm64 module support is not implemented.
342 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
343 (BUILDFLAG(IS_ANDROID) && !defined(ARCH_CPU_ARM64))
344 // Validates that, for the memory regions listed in /proc/self/maps, the modules
345 // found via ModuleCache are consistent with those regions' extents.
TEST(ModuleCacheTest,CheckAgainstProcMaps)346 TEST(ModuleCacheTest, CheckAgainstProcMaps) {
347 std::string proc_maps;
348 debug::ReadProcMaps(&proc_maps);
349 std::vector<debug::MappedMemoryRegion> regions;
350 ASSERT_TRUE(debug::ParseProcMaps(proc_maps, ®ions));
351
352 // Map distinct paths to lists of regions for the path in increasing memory
353 // order.
354 using RegionVector = std::vector<const debug::MappedMemoryRegion*>;
355 using PathRegionsMap = std::map<StringPiece, RegionVector>;
356 PathRegionsMap path_regions;
357 for (const debug::MappedMemoryRegion& region : regions)
358 path_regions[region.path].push_back(®ion);
359
360 const auto find_last_executable_region = [](const RegionVector& regions) {
361 const auto rloc = base::ranges::find_if(
362 base::Reversed(regions), [](const debug::MappedMemoryRegion* region) {
363 return static_cast<bool>(region->permissions &
364 debug::MappedMemoryRegion::EXECUTE);
365 });
366 return rloc == regions.rend() ? nullptr : *rloc;
367 };
368
369 int module_count = 0;
370
371 // Loop through each distinct path.
372 for (const auto& path_regions_pair : path_regions) {
373 // Regions that aren't associated with absolute paths are unlikely to be
374 // part of modules.
375 if (path_regions_pair.first.empty() || path_regions_pair.first[0] != '/')
376 continue;
377
378 const debug::MappedMemoryRegion* const last_executable_region =
379 find_last_executable_region(path_regions_pair.second);
380 // The region isn't part of a module if no executable regions are associated
381 // with the same path.
382 if (!last_executable_region)
383 continue;
384
385 // Loop through all the regions associated with the path, checking that
386 // modules created for addresses in each region have the expected extents.
387 const uintptr_t expected_base_address =
388 path_regions_pair.second.front()->start;
389 for (const auto* region : path_regions_pair.second) {
390 ModuleCache cache;
391 const ModuleCache::Module* module =
392 cache.GetModuleForAddress(region->start);
393 // Not all regions matching the prior conditions are necessarily modules;
394 // things like resources are also mmapped into memory from files. Ignore
395 // any region isn't part of a module.
396 if (!module)
397 continue;
398
399 ++module_count;
400
401 EXPECT_EQ(expected_base_address, module->GetBaseAddress());
402 // This needs an inequality comparison because the module size is computed
403 // based on the ELF section's actual extent, while the |proc_maps| region
404 // is aligned to a larger boundary.
405 EXPECT_LE(module->GetSize(),
406 last_executable_region->end - expected_base_address)
407 << "base address: " << std::hex << module->GetBaseAddress()
408 << std::endl
409 << "region start: " << std::hex << region->start << std::endl
410 << "region end: " << std::hex << region->end << std::endl;
411 }
412 }
413
414 // Linux should have at least this module and ld-linux.so. Android should have
415 // at least this module and system libraries.
416 EXPECT_GE(module_count, 2);
417 }
418 #endif
419
420 // Module provider that always return a fake module of size 1 for a given
421 // |address|.
422 class MockModuleProvider : public ModuleCache::AuxiliaryModuleProvider {
423 public:
MockModuleProvider(size_t module_size=1)424 explicit MockModuleProvider(size_t module_size = 1)
425 : module_size_(module_size) {}
426
TryCreateModuleForAddress(uintptr_t address)427 std::unique_ptr<const ModuleCache::Module> TryCreateModuleForAddress(
428 uintptr_t address) override {
429 return std::make_unique<FakeModule>(address, module_size_);
430 }
431
432 private:
433 size_t module_size_;
434 };
435
436 // Check that auxiliary provider can inject new modules when registered.
TEST(ModuleCacheTest,RegisterAuxiliaryModuleProvider)437 TEST(ModuleCacheTest, RegisterAuxiliaryModuleProvider) {
438 ModuleCache cache;
439 EXPECT_EQ(nullptr, cache.GetModuleForAddress(1));
440
441 MockModuleProvider auxiliary_provider;
442 cache.RegisterAuxiliaryModuleProvider(&auxiliary_provider);
443 auto* module = cache.GetModuleForAddress(1);
444 EXPECT_NE(nullptr, module);
445 EXPECT_EQ(1U, module->GetBaseAddress());
446 cache.UnregisterAuxiliaryModuleProvider(&auxiliary_provider);
447
448 // Even when unregistered, the module remains in the cache.
449 EXPECT_EQ(module, cache.GetModuleForAddress(1));
450 }
451
452 // Check that ModuleCache's own module creator is used preferentially over
453 // auxiliary provider if possible.
MAYBE_TEST(ModuleCacheTest,NativeModuleOverAuxiliaryModuleProvider)454 MAYBE_TEST(ModuleCacheTest, NativeModuleOverAuxiliaryModuleProvider) {
455 ModuleCache cache;
456
457 MockModuleProvider auxiliary_provider(/*module_size=*/100);
458 cache.RegisterAuxiliaryModuleProvider(&auxiliary_provider);
459
460 const ModuleCache::Module* module =
461 cache.GetModuleForAddress(reinterpret_cast<uintptr_t>(&AFunctionForTest));
462 ASSERT_NE(nullptr, module);
463
464 // The module should be a native module, which will have size greater than 100
465 // bytes.
466 EXPECT_NE(100u, module->GetSize());
467 cache.UnregisterAuxiliaryModuleProvider(&auxiliary_provider);
468 }
469
470 // Check that auxiliary provider is no longer used after being unregistered.
TEST(ModuleCacheTest,UnregisterAuxiliaryModuleProvider)471 TEST(ModuleCacheTest, UnregisterAuxiliaryModuleProvider) {
472 ModuleCache cache;
473
474 EXPECT_EQ(nullptr, cache.GetModuleForAddress(1));
475
476 MockModuleProvider auxiliary_provider;
477 cache.RegisterAuxiliaryModuleProvider(&auxiliary_provider);
478 cache.UnregisterAuxiliaryModuleProvider(&auxiliary_provider);
479
480 EXPECT_EQ(nullptr, cache.GetModuleForAddress(1));
481 }
482
483 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX)
TEST(ModuleCacheTest,TransformELFToSymbolServerFormat)484 TEST(ModuleCacheTest, TransformELFToSymbolServerFormat) {
485 // See explanation for the module_id mangling in
486 // base::TransformModuleIDToSymbolServerFormat implementation.
487 EXPECT_EQ(TransformModuleIDToSymbolServerFormat(
488 "7F0715C286F8B16C10E4AD349CDA3B9B56C7A773"),
489 "C215077FF8866CB110E4AD349CDA3B9B0");
490 }
491 #endif
492
493 } // namespace
494 } // namespace base
495