123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488 |
- // Copyright 2018 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include <iomanip>
- #include <map>
- #include <memory>
- #include <utility>
- #include <vector>
- #include "base/callback.h"
- #include "base/callback_helpers.h"
- #include "base/profiler/module_cache.h"
- #include "base/strings/string_piece.h"
- #include "base/test/bind.h"
- #include "build/build_config.h"
- #include "testing/gtest/include/gtest/gtest.h"
- #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
- #include "base/debug/proc_maps_linux.h"
- #endif
- namespace base {
- namespace {
- int AFunctionForTest() {
- return 42;
- }
- // Provides a module that is guaranteed to be isolated from (and non-contiguous
- // with) any other module, by placing the module in the middle of a block of
- // heap memory.
- class IsolatedModule : public ModuleCache::Module {
- public:
- explicit IsolatedModule(bool is_native = true)
- : is_native_(is_native), memory_region_(new char[kRegionSize]) {}
- // ModuleCache::Module
- uintptr_t GetBaseAddress() const override {
- // Place the module in the middle of the region.
- return reinterpret_cast<uintptr_t>(&memory_region_[kRegionSize / 4]);
- }
- std::string GetId() const override { return ""; }
- FilePath GetDebugBasename() const override { return FilePath(); }
- size_t GetSize() const override { return kRegionSize / 2; }
- bool IsNative() const override { return is_native_; }
- private:
- static const int kRegionSize = 100;
- bool is_native_;
- std::unique_ptr<char[]> memory_region_;
- };
- // Provides a fake module with configurable base address and size.
- class FakeModule : public ModuleCache::Module {
- public:
- FakeModule(uintptr_t base_address,
- size_t size,
- bool is_native = true,
- OnceClosure destruction_closure = OnceClosure())
- : base_address_(base_address),
- size_(size),
- is_native_(is_native),
- destruction_closure_runner_(std::move(destruction_closure)) {}
- FakeModule(const FakeModule&) = delete;
- FakeModule& operator=(const FakeModule&) = delete;
- uintptr_t GetBaseAddress() const override { return base_address_; }
- std::string GetId() const override { return ""; }
- FilePath GetDebugBasename() const override { return FilePath(); }
- size_t GetSize() const override { return size_; }
- bool IsNative() const override { return is_native_; }
- private:
- uintptr_t base_address_;
- size_t size_;
- bool is_native_;
- ScopedClosureRunner destruction_closure_runner_;
- };
- // Utility function to add a single non-native module during test setup. Returns
- // a pointer to the provided module.
- const ModuleCache::Module* AddNonNativeModule(
- ModuleCache* cache,
- std::unique_ptr<const ModuleCache::Module> module) {
- const ModuleCache::Module* module_ptr = module.get();
- std::vector<std::unique_ptr<const ModuleCache::Module>> modules;
- modules.push_back(std::move(module));
- cache->UpdateNonNativeModules({}, std::move(modules));
- return module_ptr;
- }
- #if (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_IOS) && !defined(ARCH_CPU_ARM64)) || \
- (BUILDFLAG(IS_FUCHSIA) && !defined(ARCH_CPU_ARM64)) || BUILDFLAG(IS_WIN)
- #define MAYBE_TEST(TestSuite, TestName) TEST(TestSuite, TestName)
- #else
- #define MAYBE_TEST(TestSuite, TestName) TEST(TestSuite, DISABLED_##TestName)
- #endif
- MAYBE_TEST(ModuleCacheTest, GetDebugBasename) {
- ModuleCache cache;
- const ModuleCache::Module* module =
- cache.GetModuleForAddress(reinterpret_cast<uintptr_t>(&AFunctionForTest));
- ASSERT_NE(nullptr, module);
- #if BUILDFLAG(IS_ANDROID)
- EXPECT_EQ("libbase_unittests__library",
- // Different build configurations varyingly use .so vs. .cr.so for
- // the module extension. Remove all the extensions in both cases.
- module->GetDebugBasename()
- .RemoveFinalExtension()
- .RemoveFinalExtension()
- .value());
- #elif BUILDFLAG(IS_POSIX)
- EXPECT_EQ("base_unittests", module->GetDebugBasename().value());
- #elif BUILDFLAG(IS_WIN)
- EXPECT_EQ(L"base_unittests.exe.pdb", module->GetDebugBasename().value());
- #endif
- }
- // Checks that ModuleCache returns the same module instance for
- // addresses within the module.
- MAYBE_TEST(ModuleCacheTest, LookupCodeAddresses) {
- uintptr_t ptr1 = reinterpret_cast<uintptr_t>(&AFunctionForTest);
- uintptr_t ptr2 = ptr1 + 1;
- ModuleCache cache;
- const ModuleCache::Module* module1 = cache.GetModuleForAddress(ptr1);
- const ModuleCache::Module* module2 = cache.GetModuleForAddress(ptr2);
- EXPECT_EQ(module1, module2);
- EXPECT_NE(nullptr, module1);
- EXPECT_GT(module1->GetSize(), 0u);
- EXPECT_LE(module1->GetBaseAddress(), ptr1);
- EXPECT_GT(module1->GetBaseAddress() + module1->GetSize(), ptr2);
- }
- MAYBE_TEST(ModuleCacheTest, LookupRange) {
- ModuleCache cache;
- auto to_inject = std::make_unique<IsolatedModule>();
- const ModuleCache::Module* module = to_inject.get();
- cache.AddCustomNativeModule(std::move(to_inject));
- EXPECT_EQ(nullptr, cache.GetModuleForAddress(module->GetBaseAddress() - 1));
- EXPECT_EQ(module, cache.GetModuleForAddress(module->GetBaseAddress()));
- EXPECT_EQ(module, cache.GetModuleForAddress(module->GetBaseAddress() +
- module->GetSize() - 1));
- EXPECT_EQ(nullptr, cache.GetModuleForAddress(module->GetBaseAddress() +
- module->GetSize()));
- }
- MAYBE_TEST(ModuleCacheTest, LookupNonNativeModule) {
- ModuleCache cache;
- const ModuleCache::Module* module =
- AddNonNativeModule(&cache, std::make_unique<IsolatedModule>(false));
- EXPECT_EQ(nullptr, cache.GetModuleForAddress(module->GetBaseAddress() - 1));
- EXPECT_EQ(module, cache.GetModuleForAddress(module->GetBaseAddress()));
- EXPECT_EQ(module, cache.GetModuleForAddress(module->GetBaseAddress() +
- module->GetSize() - 1));
- EXPECT_EQ(nullptr, cache.GetModuleForAddress(module->GetBaseAddress() +
- module->GetSize()));
- }
- MAYBE_TEST(ModuleCacheTest, LookupOverlaidNonNativeModule) {
- ModuleCache cache;
- auto native_module_to_inject = std::make_unique<IsolatedModule>();
- const ModuleCache::Module* native_module = native_module_to_inject.get();
- cache.AddCustomNativeModule(std::move(native_module_to_inject));
- // Overlay the native module with the non-native module, starting 8 bytes into
- // the native modules and ending 8 bytes before the end of the module.
- const ModuleCache::Module* non_native_module = AddNonNativeModule(
- &cache,
- std::make_unique<FakeModule>(native_module->GetBaseAddress() + 8,
- native_module->GetSize() - 16, false));
- EXPECT_EQ(native_module,
- cache.GetModuleForAddress(non_native_module->GetBaseAddress() - 1));
- EXPECT_EQ(non_native_module,
- cache.GetModuleForAddress(non_native_module->GetBaseAddress()));
- EXPECT_EQ(non_native_module,
- cache.GetModuleForAddress(non_native_module->GetBaseAddress() +
- non_native_module->GetSize() - 1));
- EXPECT_EQ(native_module,
- cache.GetModuleForAddress(non_native_module->GetBaseAddress() +
- non_native_module->GetSize()));
- }
- MAYBE_TEST(ModuleCacheTest, UpdateNonNativeModulesAdd) {
- ModuleCache cache;
- std::vector<std::unique_ptr<const ModuleCache::Module>> modules;
- modules.push_back(std::make_unique<FakeModule>(1, 1, false));
- const ModuleCache::Module* module = modules.back().get();
- cache.UpdateNonNativeModules({}, std::move(modules));
- EXPECT_EQ(module, cache.GetModuleForAddress(1));
- }
- MAYBE_TEST(ModuleCacheTest, UpdateNonNativeModulesRemove) {
- ModuleCache cache;
- std::vector<std::unique_ptr<const ModuleCache::Module>> modules;
- modules.push_back(std::make_unique<FakeModule>(1, 1, false));
- const ModuleCache::Module* module = modules.back().get();
- cache.UpdateNonNativeModules({}, std::move(modules));
- cache.UpdateNonNativeModules({module}, {});
- EXPECT_EQ(nullptr, cache.GetModuleForAddress(1));
- }
- MAYBE_TEST(ModuleCacheTest, UpdateNonNativeModulesRemoveModuleIsNotDestroyed) {
- bool was_destroyed = false;
- {
- ModuleCache cache;
- std::vector<std::unique_ptr<const ModuleCache::Module>> modules;
- modules.push_back(std::make_unique<FakeModule>(
- 1, 1, false,
- BindLambdaForTesting([&was_destroyed]() { was_destroyed = true; })));
- const ModuleCache::Module* module = modules.back().get();
- cache.UpdateNonNativeModules({}, std::move(modules));
- cache.UpdateNonNativeModules({module}, {});
- EXPECT_FALSE(was_destroyed);
- }
- EXPECT_TRUE(was_destroyed);
- }
- // Regression test to validate that when modules are partitioned into modules to
- // keep and modules to remove, the modules to remove are not destroyed.
- // https://crbug.com/1127466 case 2.
- MAYBE_TEST(ModuleCacheTest, UpdateNonNativeModulesPartitioning) {
- int destroyed_count = 0;
- const auto record_destroyed = [&destroyed_count]() { ++destroyed_count; };
- {
- ModuleCache cache;
- std::vector<std::unique_ptr<const ModuleCache::Module>> modules;
- modules.push_back(std::make_unique<FakeModule>(
- 1, 1, false, BindLambdaForTesting(record_destroyed)));
- const ModuleCache::Module* module1 = modules.back().get();
- modules.push_back(std::make_unique<FakeModule>(
- 2, 1, false, BindLambdaForTesting(record_destroyed)));
- cache.UpdateNonNativeModules({}, std::move(modules));
- cache.UpdateNonNativeModules({module1}, {});
- EXPECT_EQ(0, destroyed_count);
- }
- EXPECT_EQ(2, destroyed_count);
- }
- MAYBE_TEST(ModuleCacheTest, UpdateNonNativeModulesReplace) {
- ModuleCache cache;
- // Replace a module with another larger module at the same base address.
- std::vector<std::unique_ptr<const ModuleCache::Module>> modules1;
- modules1.push_back(std::make_unique<FakeModule>(1, 1, false));
- const ModuleCache::Module* module1 = modules1.back().get();
- std::vector<std::unique_ptr<const ModuleCache::Module>> modules2;
- modules2.push_back(std::make_unique<FakeModule>(1, 2, false));
- const ModuleCache::Module* module2 = modules2.back().get();
- cache.UpdateNonNativeModules({}, std::move(modules1));
- cache.UpdateNonNativeModules({module1}, std::move(modules2));
- EXPECT_EQ(module2, cache.GetModuleForAddress(2));
- }
- MAYBE_TEST(ModuleCacheTest,
- UpdateNonNativeModulesMultipleRemovedModulesAtSameAddress) {
- int destroyed_count = 0;
- const auto record_destroyed = [&destroyed_count]() { ++destroyed_count; };
- ModuleCache cache;
- // Checks that non-native modules can be repeatedly added and removed at the
- // same addresses, and that all are retained in the cache.
- std::vector<std::unique_ptr<const ModuleCache::Module>> modules1;
- modules1.push_back(std::make_unique<FakeModule>(
- 1, 1, false, BindLambdaForTesting(record_destroyed)));
- const ModuleCache::Module* module1 = modules1.back().get();
- std::vector<std::unique_ptr<const ModuleCache::Module>> modules2;
- modules2.push_back(std::make_unique<FakeModule>(
- 1, 1, false, BindLambdaForTesting(record_destroyed)));
- const ModuleCache::Module* module2 = modules2.back().get();
- cache.UpdateNonNativeModules({}, std::move(modules1));
- cache.UpdateNonNativeModules({module1}, std::move(modules2));
- cache.UpdateNonNativeModules({module2}, {});
- EXPECT_EQ(0, destroyed_count);
- }
- MAYBE_TEST(ModuleCacheTest, UpdateNonNativeModulesCorrectModulesRemoved) {
- ModuleCache cache;
- std::vector<std::unique_ptr<const ModuleCache::Module>> to_add;
- for (int i = 0; i < 5; ++i) {
- to_add.push_back(std::make_unique<FakeModule>(i + 1, 1, false));
- }
- std::vector<const ModuleCache::Module*> to_remove = {to_add[1].get(),
- to_add[3].get()};
- // Checks that the correct modules are removed when removing some but not all
- // modules.
- cache.UpdateNonNativeModules({}, std::move(to_add));
- cache.UpdateNonNativeModules({to_remove}, {});
- DCHECK_NE(nullptr, cache.GetModuleForAddress(1));
- DCHECK_EQ(nullptr, cache.GetModuleForAddress(2));
- DCHECK_NE(nullptr, cache.GetModuleForAddress(3));
- DCHECK_EQ(nullptr, cache.GetModuleForAddress(4));
- DCHECK_NE(nullptr, cache.GetModuleForAddress(5));
- }
- MAYBE_TEST(ModuleCacheTest, ModulesList) {
- ModuleCache cache;
- uintptr_t ptr = reinterpret_cast<uintptr_t>(&AFunctionForTest);
- const ModuleCache::Module* native_module = cache.GetModuleForAddress(ptr);
- const ModuleCache::Module* non_native_module =
- AddNonNativeModule(&cache, std::make_unique<FakeModule>(1, 2, false));
- EXPECT_NE(nullptr, native_module);
- std::vector<const ModuleCache::Module*> modules = cache.GetModules();
- ASSERT_EQ(2u, modules.size());
- EXPECT_EQ(native_module, modules[0]);
- EXPECT_EQ(non_native_module, modules[1]);
- }
- MAYBE_TEST(ModuleCacheTest, InvalidModule) {
- ModuleCache cache;
- EXPECT_EQ(nullptr, cache.GetModuleForAddress(1));
- }
- // arm64 module support is not implemented.
- #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
- (BUILDFLAG(IS_ANDROID) && !defined(ARCH_CPU_ARM64))
- // Validates that, for the memory regions listed in /proc/self/maps, the modules
- // found via ModuleCache are consistent with those regions' extents.
- TEST(ModuleCacheTest, CheckAgainstProcMaps) {
- std::string proc_maps;
- debug::ReadProcMaps(&proc_maps);
- std::vector<debug::MappedMemoryRegion> regions;
- ASSERT_TRUE(debug::ParseProcMaps(proc_maps, ®ions));
- // Map distinct paths to lists of regions for the path in increasing memory
- // order.
- using RegionVector = std::vector<const debug::MappedMemoryRegion*>;
- using PathRegionsMap = std::map<StringPiece, RegionVector>;
- PathRegionsMap path_regions;
- for (const debug::MappedMemoryRegion& region : regions)
- path_regions[region.path].push_back(®ion);
- const auto find_last_executable_region = [](const RegionVector& regions) {
- const auto rloc = std::find_if(
- regions.rbegin(), regions.rend(),
- [](const debug::MappedMemoryRegion* region) {
- return static_cast<bool>(region->permissions &
- debug::MappedMemoryRegion::EXECUTE);
- });
- return rloc == regions.rend() ? nullptr : *rloc;
- };
- int module_count = 0;
- // Loop through each distinct path.
- for (const auto& path_regions_pair : path_regions) {
- // Regions that aren't associated with absolute paths are unlikely to be
- // part of modules.
- if (path_regions_pair.first.empty() || path_regions_pair.first[0] != '/')
- continue;
- const debug::MappedMemoryRegion* const last_executable_region =
- find_last_executable_region(path_regions_pair.second);
- // The region isn't part of a module if no executable regions are associated
- // with the same path.
- if (!last_executable_region)
- continue;
- // Loop through all the regions associated with the path, checking that
- // modules created for addresses in each region have the expected extents.
- const uintptr_t expected_base_address =
- path_regions_pair.second.front()->start;
- for (const auto* region : path_regions_pair.second) {
- ModuleCache cache;
- const ModuleCache::Module* module =
- cache.GetModuleForAddress(region->start);
- // Not all regions matching the prior conditions are necessarily modules;
- // things like resources are also mmapped into memory from files. Ignore
- // any region isn't part of a module.
- if (!module)
- continue;
- ++module_count;
- EXPECT_EQ(expected_base_address, module->GetBaseAddress());
- // This needs an inequality comparison because the module size is computed
- // based on the ELF section's actual extent, while the |proc_maps| region
- // is aligned to a larger boundary.
- EXPECT_LE(module->GetSize(),
- last_executable_region->end - expected_base_address)
- << "base address: " << std::hex << module->GetBaseAddress()
- << std::endl
- << "region start: " << std::hex << region->start << std::endl
- << "region end: " << std::hex << region->end << std::endl;
- }
- }
- // Linux should have at least this module and ld-linux.so. Android should have
- // at least this module and system libraries.
- EXPECT_GE(module_count, 2);
- }
- #endif
- // Module provider that always return a fake module of size 1 for a given
- // |address|.
- class MockModuleProvider : public ModuleCache::AuxiliaryModuleProvider {
- public:
- explicit MockModuleProvider(size_t module_size = 1)
- : module_size_(module_size) {}
- std::unique_ptr<const ModuleCache::Module> TryCreateModuleForAddress(
- uintptr_t address) override {
- return std::make_unique<FakeModule>(address, module_size_);
- }
- private:
- size_t module_size_;
- };
- // Check that auxiliary provider can inject new modules when registered.
- TEST(ModuleCacheTest, RegisterAuxiliaryModuleProvider) {
- ModuleCache cache;
- EXPECT_EQ(nullptr, cache.GetModuleForAddress(1));
- MockModuleProvider auxiliary_provider;
- cache.RegisterAuxiliaryModuleProvider(&auxiliary_provider);
- auto* module = cache.GetModuleForAddress(1);
- EXPECT_NE(nullptr, module);
- EXPECT_EQ(1U, module->GetBaseAddress());
- cache.UnregisterAuxiliaryModuleProvider(&auxiliary_provider);
- // Even when unregistered, the module remains in the cache.
- EXPECT_EQ(module, cache.GetModuleForAddress(1));
- }
- // Check that ModuleCache's own module creator is used preferentially over
- // auxiliary provider if possible.
- MAYBE_TEST(ModuleCacheTest, NativeModuleOverAuxiliaryModuleProvider) {
- ModuleCache cache;
- MockModuleProvider auxiliary_provider(/*module_size=*/100);
- cache.RegisterAuxiliaryModuleProvider(&auxiliary_provider);
- const ModuleCache::Module* module =
- cache.GetModuleForAddress(reinterpret_cast<uintptr_t>(&AFunctionForTest));
- ASSERT_NE(nullptr, module);
- // The module should be a native module, which will have size greater than 100
- // bytes.
- EXPECT_NE(100u, module->GetSize());
- cache.UnregisterAuxiliaryModuleProvider(&auxiliary_provider);
- }
- // Check that auxiliary provider is no longer used after being unregistered.
- TEST(ModuleCacheTest, UnregisterAuxiliaryModuleProvider) {
- ModuleCache cache;
- EXPECT_EQ(nullptr, cache.GetModuleForAddress(1));
- MockModuleProvider auxiliary_provider;
- cache.RegisterAuxiliaryModuleProvider(&auxiliary_provider);
- cache.UnregisterAuxiliaryModuleProvider(&auxiliary_provider);
- EXPECT_EQ(nullptr, cache.GetModuleForAddress(1));
- }
- #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
- TEST(ModuleCacheTest, TransformELFModuleIDToBreakpadFormat) {
- // See explanation for the module_id mangling in
- // base::TransformModuleIDToBreakpadFormat implementation.
- EXPECT_EQ(TransformModuleIDToBreakpadFormat(
- "7F0715C286F8B16C10E4AD349CDA3B9B56C7A773"),
- "C215077FF8866CB110E4AD349CDA3B9B0");
- }
- #endif
- } // namespace
- } // namespace base
|