blob: 2195e0ea501e9c9f86c760964136f36cff34e17d [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/client/process_symbol_data_provider.h"
#include <inttypes.h>
#include <lib/syslog/cpp/macros.h>
#include "src/developer/debug/shared/message_loop.h"
#include "src/developer/debug/zxdb/client/frame.h"
#include "src/developer/debug/zxdb/client/memory_dump.h"
#include "src/developer/debug/zxdb/client/process.h"
#include "src/developer/debug/zxdb/client/session.h"
#include "src/developer/debug/zxdb/client/thread.h"
#include "src/developer/debug/zxdb/common/err.h"
#include "src/developer/debug/zxdb/symbols/dwarf_expr_eval.h"
#include "src/developer/debug/zxdb/symbols/loaded_module_symbols.h"
#include "src/developer/debug/zxdb/symbols/process_symbols.h"
#include "src/developer/debug/zxdb/symbols/symbol_context.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace zxdb {
namespace {
Err ProcessDestroyedErr() { return Err("Process destroyed."); }
debug::Arch ArchForProcess(const fxl::WeakPtr<Process>& process) {
if (!process)
return debug::Arch::kUnknown;
return process->session()->arch();
}
} // namespace
ProcessSymbolDataProvider::ProcessSymbolDataProvider(fxl::WeakPtr<Process> process)
: process_(std::move(process)), arch_(ArchForProcess(process_)) {}
ProcessSymbolDataProvider::~ProcessSymbolDataProvider() = default;
debug::Arch ProcessSymbolDataProvider::GetArch() { return arch_; }
void ProcessSymbolDataProvider::GetMemoryAsync(uint64_t address, uint32_t size,
GetMemoryCallback callback) {
if (!process_) {
debug::MessageLoop::Current()->PostTask(FROM_HERE, [cb = std::move(callback)]() mutable {
cb(ProcessDestroyedErr(), std::vector<uint8_t>());
});
return;
}
// Mistakes may make extremely large memory requests which can OOM the system. Prevent those.
if (size > 1024 * 1024) {
debug::MessageLoop::Current()->PostTask(
FROM_HERE, [address, size, cb = std::move(callback)]() mutable {
cb(Err(fxl::StringPrintf("Memory request for %u bytes at 0x%" PRIx64 " is too large.",
size, address)),
std::vector<uint8_t>());
});
return;
}
process_->ReadMemory(
address, size,
[address, size, cb = std::move(callback)](const Err& err, MemoryDump dump) mutable {
if (err.has_error()) {
cb(err, std::vector<uint8_t>());
return;
}
FX_DCHECK(size == 0 || dump.address() == address);
FX_DCHECK(dump.size() == size);
if (dump.blocks().size() == 1 || (dump.blocks().size() > 1 && !dump.blocks()[1].valid)) {
// Common case: came back as one block OR it read until an invalid memory boundary and the
// second block is invalid.
//
// In both these cases we can directly return the first data block. We don't have to check
// the first block's valid flag since if it's not valid it will be empty, which is what
// our API specifies.
cb(Err(), std::move(dump.blocks()[0].data));
} else {
// The debug agent doesn't guarantee that a memory dump will exist in only one block even
// if the memory is all valid. Flatten all contiguous valid regions to a single buffer.
std::vector<uint8_t> flat;
flat.reserve(dump.size());
for (const auto& block : dump.blocks()) {
if (!block.valid)
break;
flat.insert(flat.end(), block.data.begin(), block.data.end());
}
cb(Err(), std::move(flat));
}
});
}
void ProcessSymbolDataProvider::WriteMemory(uint64_t address, std::vector<uint8_t> data,
WriteCallback cb) {
if (!process_) {
debug::MessageLoop::Current()->PostTask(
FROM_HERE, [cb = std::move(cb)]() mutable { cb(ProcessDestroyedErr()); });
return;
}
process_->WriteMemory(address, std::move(data), std::move(cb));
}
std::optional<uint64_t> ProcessSymbolDataProvider::GetDebugAddressForContext(
const SymbolContext& context) const {
if (process_) {
if (auto syms = process_->GetSymbols()) {
if (auto lms = syms->GetModuleForAddress(context.load_address())) {
return lms->debug_address();
}
}
}
return std::nullopt;
}
void ProcessSymbolDataProvider::GetTLSSegment(const SymbolContext& symbol_context,
GetTLSSegmentCallback cb) {
if (!process_) {
return cb(Err("Thread-local storage requires a current process."));
}
auto syms = process_->GetSymbols();
if (!syms) {
return cb(Err("Could not load symbols for process when resolving TLS segment."));
}
auto lms = syms->GetModuleForAddress(symbol_context.load_address());
if (!lms) {
return cb(Err("Could not find current module when resolving TLS segment."));
}
process_->GetTLSHelpers([debug_address = lms->debug_address(), this_ref = RefPtrTo(this),
symbol_context,
cb = std::move(cb)](ErrOr<const Process::TLSHelpers*> helpers) mutable {
if (helpers.has_error()) {
return cb(helpers.err());
}
std::vector<uint8_t> program;
program.reserve(helpers.value()->link_map_tls_modid.size() + helpers.value()->tlsbase.size() +
1);
std::copy(helpers.value()->link_map_tls_modid.begin(),
helpers.value()->link_map_tls_modid.end(), std::back_inserter(program));
std::copy(helpers.value()->tlsbase.begin(), helpers.value()->tlsbase.end(),
std::back_inserter(program));
// This code manually creates a DwarfExprEval rather that use the AsyncDwarfExprEval (which
// makes things a little simpler) because we want to get the exact stack value (this is called
// in the context of another DwarfExprEval).
auto dwarf_eval = std::make_shared<DwarfExprEval>();
dwarf_eval->Push(DwarfStackEntry(debug_address));
dwarf_eval->Eval(this_ref, symbol_context, DwarfExpr(std::move(program)),
[dwarf_eval, cb = std::move(cb)](DwarfExprEval*, const Err& err) mutable {
// Prevent the DwarfExprEval from getting reentrantly deleted from within its
// own callback by posting a reference back to the message loop. This creates
// an owning reference to the DwarfExprEval to the message loop to force it
// to get deleted in the future when that's executed rather than from within
// this stack frame.
debug::MessageLoop::Current()->PostTask(FROM_HERE, [dwarf_eval]() {});
if (err.has_error()) {
return cb(err);
}
const char kNotPointer[] = "TLS DWARF expression did not produce a pointer.";
if (dwarf_eval->GetResultType() != DwarfExprEval::ResultType::kPointer) {
return cb(Err(kNotPointer));
}
DwarfStackEntry result = dwarf_eval->GetResult();
if (!result.TreatAsUnsigned()) {
return cb(Err(kNotPointer));
}
cb(static_cast<uint64_t>(result.unsigned_value()));
});
});
}
} // namespace zxdb