blob: 75f2f0e5f4062560eafb924e6ad4abb10f0d9104 [file] [log] [blame]
// 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(&notifications, &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(&notifications, &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(&notifications, &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