| // Copyright 2018 The Fuchsia 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 "src/developer/debug/zxdb/symbols/process_symbols.h" |
| |
| #include <gtest/gtest.h> |
| |
| #include "src/developer/debug/ipc/records.h" |
| #include "src/developer/debug/zxdb/symbols/loaded_module_symbols.h" |
| #include "src/developer/debug/zxdb/symbols/module_symbols.h" |
| #include "src/developer/debug/zxdb/symbols/system_symbols.h" |
| #include "src/developer/debug/zxdb/symbols/target_symbols.h" |
| #include "src/developer/debug/zxdb/symbols/test_symbol_module.h" |
| |
| namespace zxdb { |
| |
| namespace { |
| |
| class NotificationsImpl : public ProcessSymbols::Notifications { |
| public: |
| NotificationsImpl() = default; |
| ~NotificationsImpl() = default; |
| |
| const std::vector<LoadedModuleSymbols*>& loaded() const { return loaded_; } |
| const std::vector<uint64_t>& unloaded() const { return unloaded_; } |
| int err_count() const { return err_count_; } |
| |
| void Clear() { |
| loaded_.clear(); |
| unloaded_.clear(); |
| err_count_ = 0; |
| } |
| |
| bool HaveOneUnloadFor(uint64_t addr) const { |
| if (unloaded_.size() != 1u) |
| return false; |
| return unloaded_[0] == addr; |
| } |
| bool HaveOneLoadFor(uint64_t addr, const std::string& local_file) const { |
| if (loaded_.size() != 1u) |
| return false; |
| return loaded_[0]->load_address() == addr && |
| loaded_[0]->module_symbols()->GetStatus().symbol_file == local_file; |
| } |
| |
| // Notifications implementation. |
| void DidLoadModuleSymbols(LoadedModuleSymbols* module) override { loaded_.push_back(module); } |
| void WillUnloadModuleSymbols(LoadedModuleSymbols* module) override { |
| unloaded_.push_back(module->load_address()); |
| } |
| void OnSymbolLoadFailure(const Err& err) override { err_count_++; } |
| |
| private: |
| std::vector<LoadedModuleSymbols*> loaded_; |
| |
| // Stored by load address (since the pointers are deleted). |
| std::vector<uint64_t> unloaded_; |
| |
| int err_count_ = 0; |
| }; |
| |
| } // namespace |
| |
| TEST(ProcessSymbols, SetModules_Probe) { |
| std::string test_file_name = TestSymbolModule::GetCheckedInTestFileName(); |
| std::string test_file_build_id = TestSymbolModule::kCheckedInBuildId; |
| SystemSymbols system(nullptr); |
| system.build_id_index().AddOneFile(test_file_name); |
| |
| TargetSymbols target(&system); |
| |
| NotificationsImpl notifications; |
| ProcessSymbols process(¬ifications, &target); |
| |
| // Add a new module. |
| constexpr uint64_t base1 = 0x1234567890; |
| std::vector<debug_ipc::Module> ipc; |
| debug_ipc::Module ipc_module; |
| ipc_module.base = base1; |
| ipc_module.build_id = test_file_build_id; |
| ipc_module.name = "module1.so"; |
| ipc.push_back(ipc_module); |
| process.SetModules(ipc); |
| |
| // Should have gotten one add notifications and no others. |
| ASSERT_EQ(1u, notifications.loaded().size()); |
| LoadedModuleSymbols* loaded_symbols = notifications.loaded()[0]; |
| EXPECT_EQ(base1, loaded_symbols->load_address()); |
| EXPECT_EQ(test_file_name, loaded_symbols->module_symbols()->GetStatus().symbol_file); |
| EXPECT_EQ(test_file_name, system.build_id_index().EntryForBuildID(test_file_build_id).binary); |
| EXPECT_EQ(0, notifications.err_count()); |
| } |
| |
| TEST(ProcessSymbols, SetModules) { |
| // This uses two different build IDs mapping to the same file. SystemSymbols only deals with build |
| // IDs rather than file uniqueness, so this will create two separate entries (what we need, even |
| // though we only have one symbol test file). |
| std::string fake_build_id_1 = "12345"; |
| std::string fake_build_id_2 = "67890"; |
| std::string test_file_name = TestSymbolModule::GetTestFileName(); |
| SystemSymbols system(nullptr); |
| system.build_id_index().AddBuildIDMappingForTest(fake_build_id_1, test_file_name); |
| system.build_id_index().AddBuildIDMappingForTest(fake_build_id_2, test_file_name); |
| |
| TargetSymbols target(&system); |
| |
| NotificationsImpl notifications; |
| ProcessSymbols process(¬ifications, &target); |
| |
| // Add a new module. |
| constexpr uint64_t base1 = 0x1234567890; |
| std::vector<debug_ipc::Module> ipc; |
| debug_ipc::Module ipc_module; |
| ipc_module.base = base1; |
| ipc_module.build_id = fake_build_id_1; |
| ipc_module.name = "module1.so"; |
| ipc.push_back(ipc_module); |
| process.SetModules(ipc); |
| |
| // Should have gotten one add notifications and no others. |
| ASSERT_EQ(1u, notifications.loaded().size()); |
| LoadedModuleSymbols* loaded_symbols = notifications.loaded()[0]; |
| EXPECT_EQ(base1, loaded_symbols->load_address()); |
| EXPECT_EQ(test_file_name, loaded_symbols->module_symbols()->GetStatus().symbol_file); |
| EXPECT_EQ(0, notifications.err_count()); |
| |
| // Replace with a different one at the same address. |
| notifications.Clear(); |
| ipc[0].build_id = fake_build_id_2; |
| process.SetModules(ipc); |
| EXPECT_EQ(0, notifications.err_count()); |
| |
| // Should have one unload and one load. |
| EXPECT_TRUE(notifications.HaveOneUnloadFor(base1)); |
| EXPECT_TRUE(notifications.HaveOneLoadFor(base1, test_file_name)); |
| |
| // Remove the second one and add back the first one at a different address. |
| constexpr uint64_t base2 = 0x9876543210; |
| notifications.Clear(); |
| ipc[0].build_id = fake_build_id_1; |
| ipc[0].base = base2; |
| process.SetModules(ipc); |
| EXPECT_EQ(0, notifications.err_count()); |
| |
| // Should have one unload and one load. |
| EXPECT_TRUE(notifications.HaveOneUnloadFor(base1)); |
| EXPECT_TRUE(notifications.HaveOneLoadFor(base2, test_file_name)); |
| |
| // Remove everything. |
| notifications.Clear(); |
| ipc.clear(); |
| process.SetModules(ipc); |
| EXPECT_EQ(0, notifications.err_count()); |
| |
| // Should have one unload and no load. |
| EXPECT_TRUE(notifications.HaveOneUnloadFor(base2)); |
| EXPECT_EQ(0u, notifications.loaded().size()); |
| } |
| |
| TEST(ProcessSymbols, ModuleLength) { |
| std::string fake_build_id = "12345"; |
| std::string test_file_name = TestSymbolModule::GetTestFileName(); |
| SystemSymbols system(nullptr); |
| system.build_id_index().AddBuildIDMappingForTest(fake_build_id, test_file_name); |
| |
| TargetSymbols target(&system); |
| |
| NotificationsImpl notifications; |
| ProcessSymbols process(¬ifications, &target); |
| |
| // Add a new module. |
| constexpr uint64_t kBase = 0x100000000; |
| std::vector<debug_ipc::Module> ipc; |
| debug_ipc::Module ipc_module; |
| ipc_module.base = kBase; |
| ipc_module.build_id = fake_build_id; |
| ipc_module.name = "module1.so"; |
| ipc.push_back(ipc_module); |
| process.SetModules(ipc); |
| |
| // Valid address for the module should return it. |
| EXPECT_TRUE(process.GetModuleForAddress(kBase + 1)); |
| |
| // Before the module's address shouldn't match anything. |
| EXPECT_FALSE(process.GetModuleForAddress(kBase - 1)); |
| |
| // After the module's end shouldn't match anything (this assumes the test module is less than |
| // 0x10000000 long). |
| EXPECT_FALSE(process.GetModuleForAddress(kBase + 0x10000000)); |
| } |
| |
| } // namespace zxdb |