| // 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 "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/register.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/lib/fxl/logging.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)]() { |
| 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)]() { |
| 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) { |
| if (err.has_error()) { |
| cb(err, std::vector<uint8_t>()); |
| return; |
| } |
| |
| FXL_DCHECK(size == 0 || dump.address() == address); |
| FXL_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, |
| std::function<void(const Err&)> cb) { |
| if (!process_) { |
| debug_ipc::MessageLoop::Current()->PostTask( |
| FROM_HERE, [cb = std::move(cb)]() { cb(ProcessDestroyedErr()); }); |
| return; |
| } |
| process_->WriteMemory(address, std::move(data), std::move(cb)); |
| } |
| |
| } // namespace zxdb |