blob: 5fcd846a3a2cf2b06f7df290ccc08b79e3b01b4c [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/client/frame_symbol_data_provider.h"
#include <inttypes.h>
#include "garnet/bin/zxdb/client/frame.h"
#include "garnet/bin/zxdb/client/memory_dump.h"
#include "garnet/bin/zxdb/client/process.h"
#include "garnet/bin/zxdb/client/register.h"
#include "garnet/bin/zxdb/client/register_dwarf.h"
#include "garnet/bin/zxdb/client/session.h"
#include "garnet/bin/zxdb/client/thread.h"
#include "garnet/bin/zxdb/common/err.h"
#include "garnet/lib/debug_ipc/helper/message_loop.h"
#include "lib/fxl/logging.h"
#include "lib/fxl/strings/string_printf.h"
namespace zxdb {
namespace {
Err CallFrameDestroyedErr() { return Err("Call frame destroyed."); }
} // namespace
FrameSymbolDataProvider::FrameSymbolDataProvider(Frame* frame)
: frame_(frame) {}
FrameSymbolDataProvider::~FrameSymbolDataProvider() = default;
void FrameSymbolDataProvider::DisownFrame() { frame_ = nullptr; }
std::optional<uint64_t> FrameSymbolDataProvider::GetRegister(
int dwarf_register_number) {
if (!frame_)
return std::nullopt;
if (dwarf_register_number == kRegisterIP)
return frame_->GetAddress();
// Some common registers are known without having to do a register request.
switch (GetSpecialRegisterTypeFromDWARFRegisterID(frame_->session()->arch(),
dwarf_register_number)) {
case debug_ipc::SpecialRegisterType::kIP:
return frame_->GetAddress();
case debug_ipc::SpecialRegisterType::kSP:
return frame_->GetStackPointer();
case debug_ipc::SpecialRegisterType::kBP:
return frame_->GetBasePointerRegister();
case debug_ipc::SpecialRegisterType::kNone:
break;
}
// TODO(brettw) enable synchronous access if the registers are cached.
// See GetRegisterAsync().
return std::nullopt;
}
void FrameSymbolDataProvider::GetRegisterAsync(int dwarf_register_number,
GetRegisterCallback callback) {
// TODO(brettw) registers are not available except when this frame is the
// top stack frame. Currently, there is no management of this and the frame
// doesn't get notifications when it's topmost or not, and whether the thread
// has been resumed (both things would invalidate cached registers). As
// a result, currently we do not cache register values and always do a
// full async request for each one.
//
// Additionally, some registers can be made available in non-top stack
// frames. Libunwind should be able to tell us the saved registers for older
// stack frames.
if (!frame_ || !IsTopFrame()) {
debug_ipc::MessageLoop::Current()->PostTask(
FROM_HERE, [ dwarf_register_number, cb = std::move(callback) ]() {
cb(Err(fxl::StringPrintf("Register %d unavailable.",
dwarf_register_number)),
0);
});
return;
}
// We only need the general registers.
// TODO: Other categories will need to be supported here (eg. floating point).
frame_->GetThread()->ReadRegisters(
{debug_ipc::RegisterCategory::Type::kGeneral},
[ dwarf_register_number, cb = std::move(callback) ](
const Err& err, const RegisterSet& regs) {
uint64_t value = 0;
if (err.has_error()) {
cb(err, 0);
} else if (regs.GetRegisterValueFromDWARF(dwarf_register_number,
&value)) {
cb(Err(), value); // Success.
} else {
cb(Err(fxl::StringPrintf("Register %d unavailable.",
dwarf_register_number)),
0);
}
});
}
std::optional<uint64_t> FrameSymbolDataProvider::GetFrameBase() {
if (!frame_)
return std::nullopt;
return frame_->GetBasePointer();
}
void FrameSymbolDataProvider::GetFrameBaseAsync(GetRegisterCallback cb) {
if (!frame_) {
debug_ipc::MessageLoop::Current()->PostTask(
FROM_HERE, [cb = std::move(cb)]() { cb(CallFrameDestroyedErr(), 0); });
return;
}
frame_->GetBasePointerAsync([cb = std::move(cb)](uint64_t value) {
cb(Err(), value);
});
}
void FrameSymbolDataProvider::GetMemoryAsync(uint64_t address, uint32_t size,
GetMemoryCallback callback) {
if (!frame_) {
debug_ipc::MessageLoop::Current()->PostTask(
FROM_HERE, [cb = std::move(callback)]() {
cb(CallFrameDestroyedErr(), 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;
}
frame_->GetThread()->GetProcess()->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 FrameSymbolDataProvider::WriteMemory(uint64_t address,
std::vector<uint8_t> data,
std::function<void(const Err&)> cb) {
if (!frame_) {
debug_ipc::MessageLoop::Current()->PostTask(
FROM_HERE, [cb = std::move(cb)]() { cb(CallFrameDestroyedErr()); });
return;
}
frame_->GetThread()->GetProcess()->WriteMemory(address, std::move(data),
std::move(cb));
}
bool FrameSymbolDataProvider::IsTopFrame() const {
if (!frame_)
return false;
const auto& stack = frame_->GetThread()->GetStack();
if (stack.empty())
return false;
return stack[0] == frame_;
}
} // namespace zxdb