blob: f2b4fd3a7624d8a232f06716d3277b0fe4bb1b2e [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 "garnet/bin/zxdb/symbols/process_symbols.h"
#include "garnet/bin/zxdb/symbols/input_location.h"
#include "garnet/bin/zxdb/symbols/line_details.h"
#include "garnet/bin/zxdb/symbols/loaded_module_symbols.h"
#include "garnet/bin/zxdb/symbols/module_symbols_impl.h"
#include "garnet/bin/zxdb/symbols/resolve_options.h"
#include "garnet/bin/zxdb/symbols/system_symbols.h"
#include "garnet/bin/zxdb/symbols/target_symbols.h"
#include "garnet/lib/debug_ipc/records.h"
namespace zxdb {
namespace {
// The vDSO doesn't have symbols and we don't want to give error messages for
// it. Ignore failures for modules that this returns true for.
bool ExpectSymbolsForName(const std::string& name) { return name != "<vDSO>"; }
} // namespace
ProcessSymbols::ProcessSymbols(Notifications* notifications,
TargetSymbols* target_symbols)
: notifications_(notifications),
target_symbols_(target_symbols),
weak_factory_(this) {}
ProcessSymbols::~ProcessSymbols() = default;
fxl::WeakPtr<const ProcessSymbols> ProcessSymbols::GetWeakPtr() const {
return weak_factory_.GetWeakPtr();
}
void ProcessSymbols::SetModules(const std::vector<debug_ipc::Module>& modules) {
// Map from load address to index into |modules| argument.
std::map<uint64_t, size_t> new_module_address_to_index;
// Find new modules. These are indices into |modules| of the added ones.
std::vector<size_t> new_module_indices;
for (size_t i = 0; i < modules.size(); i++) {
new_module_address_to_index[modules[i].base] = i;
auto found_addr = modules_.find(modules[i].base);
// Even if address is a match, the library could have been swapped.
if (found_addr == modules_.end() ||
!RefersToSameModule(modules[i], found_addr->second))
new_module_indices.push_back(i);
}
// Find deleted modules and remove them.
std::vector<ModuleMap::iterator> deleted_modules;
for (auto iter = modules_.begin(); iter != modules_.end(); ++iter) {
auto found_index = new_module_address_to_index.find(iter->second.base);
if (found_index == new_module_address_to_index.end() ||
!RefersToSameModule(modules[found_index->second], iter->second))
deleted_modules.push_back(iter);
}
// First update for deleted modules since the addresses may overlap the
// added ones.
for (auto& deleted : deleted_modules) {
notifications_->WillUnloadModuleSymbols(deleted->second.symbols.get());
modules_.erase(deleted);
}
deleted_modules.clear();
// Process the added ones.
std::vector<LoadedModuleSymbols*> added_modules;
std::vector<Err> load_errors;
for (const auto& added_index : new_module_indices) {
Err sym_load_err;
ModuleInfo* info = SaveModuleInfo(modules[added_index], &sym_load_err);
if (sym_load_err.has_error())
load_errors.push_back(std::move(sym_load_err));
else if (info->symbols->module_ref())
added_modules.push_back(info->symbols.get());
}
// Update the TargetSymbols.
target_symbols_->RemoveAllModules();
for (auto& [base, mod_info] : modules_) {
if (mod_info.symbols->module_ref()) {
target_symbols_->AddModule(fxl::RefPtr<SystemSymbols::ModuleRef>(
mod_info.symbols->module_ref()));
}
}
// Send notifications last so everything is in a consistent state.
for (auto& added_module : added_modules)
notifications_->DidLoadModuleSymbols(added_module);
for (auto& err : load_errors)
notifications_->OnSymbolLoadFailure(err);
}
void ProcessSymbols::InjectModuleForTesting(
const std::string& name, const std::string& build_id,
std::unique_ptr<LoadedModuleSymbols> mod_sym) {
LoadedModuleSymbols* loaded_ptr = mod_sym.get();
FXL_DCHECK(loaded_ptr);
ModuleInfo info;
info.name = name;
info.build_id = build_id;
info.base = loaded_ptr->load_address();
info.symbols = std::move(mod_sym);
modules_[loaded_ptr->load_address()] = std::move(info);
// Issue notifications.
target_symbols_->AddModule(
fxl::RefPtr<SystemSymbols::ModuleRef>(loaded_ptr->module_ref()));
notifications_->DidLoadModuleSymbols(loaded_ptr);
}
std::vector<ModuleSymbolStatus> ProcessSymbols::GetStatus() const {
std::vector<ModuleSymbolStatus> result;
for (const auto& [base, mod_info] : modules_) {
if (mod_info.symbols->module_ref()) {
result.push_back(mod_info.symbols->module_symbols()->GetStatus());
// ModuleSymbols doesn't know the name or base address so fill in now.
result.back().name = mod_info.name;
result.back().base = mod_info.base;
result.back().symbols = mod_info.symbols.get();
} else {
// No symbols, make an empty record.
ModuleSymbolStatus status;
status.name = mod_info.name;
status.build_id = mod_info.build_id;
status.base = mod_info.base;
status.symbols_loaded = false;
result.push_back(std::move(status));
}
}
return result;
}
std::vector<const LoadedModuleSymbols*> ProcessSymbols::GetLoadedModuleSymbols()
const {
std::vector<const LoadedModuleSymbols*> result;
result.reserve(modules_.size());
for (const auto& [base, mod_info] : modules_) {
if (mod_info.symbols->module_ref())
result.push_back(mod_info.symbols.get());
}
return result;
}
std::vector<Location> ProcessSymbols::ResolveInputLocation(
const InputLocation& input_location, const ResolveOptions& options) const {
FXL_DCHECK(input_location.type != InputLocation::Type::kNone);
// Address resolution.
if (input_location.type == InputLocation::Type::kAddress) {
if (options.symbolize) {
// Symbolize one address.
const ModuleInfo* info = InfoForAddress(input_location.address);
if (!info || !info->symbols->module_ref()) {
// Can't symbolize.
return std::vector<Location>{
Location(Location::State::kSymbolized, input_location.address)};
}
// Have the module the address.
return info->symbols->ResolveInputLocation(input_location, options);
}
// No-op conversion of address -> address.
return std::vector<Location>{
Location(Location::State::kAddress, input_location.address)};
}
// Symbol and file/line resolution both requires iterating over all modules.
std::vector<Location> result;
for (const auto& [base, mod_info] : modules_) {
if (mod_info.symbols->module_ref()) {
const LoadedModuleSymbols* loaded = mod_info.symbols.get();
for (Location& location :
loaded->ResolveInputLocation(input_location, options))
result.push_back(std::move(location));
}
}
return result;
}
LineDetails ProcessSymbols::LineDetailsForAddress(uint64_t address) const {
const ModuleInfo* info = InfoForAddress(address);
if (!info || !info->symbols->module_ref())
return LineDetails();
return info->symbols->module_symbols()->LineDetailsForAddress(
info->symbols->symbol_context(), address);
}
bool ProcessSymbols::HaveSymbolsLoadedForModuleAt(uint64_t address) const {
const ModuleInfo* info = InfoForAddress(address);
return info && info->symbols->module_ref();
}
ProcessSymbols::ModuleInfo* ProcessSymbols::SaveModuleInfo(
const debug_ipc::Module& module, Err* symbol_load_err) {
ModuleInfo info;
info.name = module.name;
info.build_id = module.build_id;
info.base = module.base;
fxl::RefPtr<SystemSymbols::ModuleRef> module_symbols;
*symbol_load_err = target_symbols_->system_symbols()->GetModule(
module.name, module.build_id, &module_symbols);
if (symbol_load_err->has_error()) {
// Error, but it may be expected.
if (!ExpectSymbolsForName(module.name))
*symbol_load_err = Err();
info.symbols = std::make_unique<LoadedModuleSymbols>(
nullptr, module.build_id, module.base);
} else {
// Success, make the LoadedModuleSymbols.
info.symbols = std::make_unique<LoadedModuleSymbols>(
std::move(module_symbols), module.build_id, module.base);
}
auto inserted_iter =
modules_
.emplace(std::piecewise_construct, std::forward_as_tuple(module.base),
std::forward_as_tuple(std::move(info)))
.first;
return &inserted_iter->second;
}
// static
bool ProcessSymbols::RefersToSameModule(const debug_ipc::Module& a,
const ModuleInfo& b) {
return a.base == b.base && a.build_id == b.build_id;
}
const ProcessSymbols::ModuleInfo* ProcessSymbols::InfoForAddress(
uint64_t address) const {
if (modules_.empty())
return nullptr;
auto found = modules_.lower_bound(address);
if (found == modules_.end() || found->first > address) {
if (found == modules_.begin())
return nullptr; // Address below first module.
// Move to previous item to get the module starting before this address.
--found;
}
return &found->second;
}
} // namespace zxdb