| // 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_ipc::Arch ArchForProcess(Process* process) { | |
| if (!process) | |
| return debug_ipc::Arch::kUnknown; | |
| return process->session()->arch(); | |
| } | |
| } // namespace | |
| ProcessSymbolDataProvider::ProcessSymbolDataProvider(Process* process) | |
| : process_(process), arch_(ArchForProcess(process)) {} | |
| ProcessSymbolDataProvider::~ProcessSymbolDataProvider() = default; | |
| void ProcessSymbolDataProvider::Disown() { process_ = nullptr; } | |
| debug_ipc::Arch ProcessSymbolDataProvider::GetArch() { return arch_; } | |
| void ProcessSymbolDataProvider::GetMemoryAsync(uint64_t address, uint32_t size, | |
| GetMemoryCallback callback) { | |
| if (!process_) { | |
| debug_ipc::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_ipc::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_ipc::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)); | |
| auto dwarf_eval = std::make_shared<DwarfExprEval>(); | |
| dwarf_eval->Push(static_cast<DwarfExprEval::StackEntry>(debug_address)); | |
| dwarf_eval->Eval(this_ref, symbol_context, program, | |
| [dwarf_eval, cb = std::move(cb)](DwarfExprEval*, const Err& err) mutable { | |
| if (err.has_error()) { | |
| return cb(err); | |
| } | |
| if (dwarf_eval->GetResultType() != DwarfExprEval::ResultType::kPointer) { | |
| return cb(Err("TLS DWARF expression did not produce a pointer.")); | |
| } | |
| cb(static_cast<uint64_t>(dwarf_eval->GetResult())); | |
| }); | |
| }); | |
| } | |
| } // namespace zxdb |