blob: df328e04382efdecf7b8defd216aa6f6bba9b09e [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/frame_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 CallFrameDestroyedErr() { return Err("Call frame destroyed."); }
} // namespace
FrameSymbolDataProvider::FrameSymbolDataProvider(Frame* frame)
: ProcessSymbolDataProvider(frame->GetThread()->GetProcess()),
frame_(frame) {}
FrameSymbolDataProvider::~FrameSymbolDataProvider() = default;
void FrameSymbolDataProvider::Disown() {
frame_ = nullptr;
bool FrameSymbolDataProvider::GetRegister(debug_ipc::RegisterID id,
std::optional<uint64_t>* value) {
FXL_DCHECK(id != debug_ipc::RegisterID::kUnknown);
*value = std::nullopt;
if (!frame_)
return true; // Synchronously know we don't have the value.
// Some common registers are known without having to do a register request.
switch (debug_ipc::GetSpecialRegisterType(id)) {
case debug_ipc::SpecialRegisterType::kIP:
*value = frame_->GetAddress();
return true;
case debug_ipc::SpecialRegisterType::kSP:
*value = frame_->GetStackPointer();
return true;
case debug_ipc::SpecialRegisterType::kNone:
// TODO(brettw) enable synchronous access if the registers are cached.
// See GetRegisterAsync().
return false;
void FrameSymbolDataProvider::GetRegisterAsync(debug_ipc::RegisterID id,
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_ || !IsInTopPhysicalFrame()) {
FROM_HERE, [id, cb = std::move(callback)]() {
cb(Err(fxl::StringPrintf("Register %s unavailable.",
// We only need the general registers.
// TODO: Other categories will need to be supported here (eg. floating point).
[id, cb = std::move(callback)](const Err& err, const RegisterSet& regs) {
if (err.has_error()) {
cb(err, 0);
} else if (auto reg = regs[id]) {
cb(Err(), reg->GetValue()); // Success.
} else {
cb(Err(fxl::StringPrintf("Register %s unavailable.",
std::optional<uint64_t> FrameSymbolDataProvider::GetFrameBase() {
if (!frame_)
return std::nullopt;
return frame_->GetBasePointer();
void FrameSymbolDataProvider::GetFrameBaseAsync(GetRegisterCallback cb) {
if (!frame_) {
FROM_HERE, [cb = std::move(cb)]() { cb(CallFrameDestroyedErr(), 0); });
[cb = std::move(cb)](uint64_t value) { cb(Err(), value); });
bool FrameSymbolDataProvider::IsInTopPhysicalFrame() const {
if (!frame_)
return false;
const Stack& stack = frame_->GetThread()->GetStack();
if (stack.empty())
return false;
// Search for the first physical frame, and return true if it or anything
// above it matches the current frame.
for (size_t i = 0; i < stack.size(); i++) {
if (stack[i] == frame_)
return true;
if (!stack[i]->IsInline())
return false;
} // namespace zxdb