blob: c695e1a96224af22c8ebe594666edc45743fc6e7 [file] [log] [blame]
// Copyright 2021 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/call_site_symbol_data_provider.h"
#include "src/developer/debug/shared/message_loop.h"
#include "src/developer/debug/shared/register_info.h"
#include "src/developer/debug/zxdb/client/process.h"
#include "src/developer/debug/zxdb/client/session.h"
#include "src/developer/debug/zxdb/expr/eval_dwarf_expr.h"
#include "src/developer/debug/zxdb/symbols/call_site.h"
#include "src/developer/debug/zxdb/symbols/call_site_parameter.h"
#include "src/developer/debug/zxdb/symbols/code_block.h"
#include "src/developer/debug/zxdb/symbols/location.h"
namespace zxdb {
CallSiteSymbolDataProvider::CallSiteSymbolDataProvider(
fxl::WeakPtr<Process> process, const Location& return_location,
fxl::RefPtr<SymbolDataProvider> frame_provider)
: ProcessSymbolDataProvider(std::move(process)),
call_site_symbol_context_(return_location.symbol_context()),
frame_provider_(std::move(frame_provider)) {
// Look up the call site definition (if any) associated with the return location.
if (const CodeBlock* block = return_location.symbol().Get()->As<CodeBlock>()) {
call_site_ =
block->GetCallSiteForReturnTo(return_location.symbol_context(), return_location.address());
}
}
CallSiteSymbolDataProvider::CallSiteSymbolDataProvider(
fxl::WeakPtr<Process> process, fxl::RefPtr<CallSite> call_site,
const SymbolContext& call_site_symbol_context, fxl::RefPtr<SymbolDataProvider> frame_provider)
: ProcessSymbolDataProvider(std::move(process)),
call_site_(std::move(call_site)),
call_site_symbol_context_(call_site_symbol_context),
frame_provider_(std::move(frame_provider)) {}
CallSiteSymbolDataProvider::~CallSiteSymbolDataProvider() = default;
fxl::RefPtr<SymbolDataProvider> CallSiteSymbolDataProvider::GetEntryDataProvider() const {
return frame_provider_->GetEntryDataProvider();
}
std::optional<containers::array_view<uint8_t>> CallSiteSymbolDataProvider::GetRegister(
debug::RegisterID id) {
// The previous frame's data provider should have all the callee-saved registers. Any additional
// registers provided by the CallSiteParameters can't always be evaluated synchronously, so we
// don't try. Therefore, anything synchronous comes from the saved registers in the caller.
fxl::RefPtr<CallSiteParameter> param = ParameterForRegister(id);
if (param && !param->value_expr().empty())
return std::nullopt; // There is a parameter. Overrides need to be evaluated asynchronously.
// No parameter, fall back to saved regular registers.
if (IsRegisterCalleeSaved(id))
return frame_provider_->GetRegister(id);
// Anything else is synchronously known to be unknown.
return containers::array_view<uint8_t>();
}
void CallSiteSymbolDataProvider::GetRegisterAsync(debug::RegisterID id, GetRegisterCallback cb) {
fxl::RefPtr<CallSiteParameter> param = ParameterForRegister(id);
if (!param || param->value_expr().empty()) {
// No CallSiteParameter. If this is a caller-saved register, we can use the ones we have.
if (IsRegisterCalleeSaved(id))
return frame_provider_->GetRegisterAsync(id, std::move(cb));
cb(Err("Call site register not available"), {});
return;
}
// Callback that handles completion of the value_expr() evaluation.
auto handle_done = [cb = std::move(cb)](DwarfExprEval& eval, const Err& err) mutable {
if (err.has_error())
return cb(err, {});
if (eval.GetResultType() == DwarfExprEval::ResultType::kData)
return cb(Err("DWARF expression produced unexpected results."), {});
DwarfStackEntry result = eval.GetResult();
if (!result.TreatAsUnsigned())
return cb(Err("DWARF expression produced unexpected results."), {});
auto result_value = result.unsigned_value();
// The register value should be at the top of the stack. We could trim the stack entry to match
// the byte width of the register, but this is expected to be used to provide data back to the
// DwarfExprEval which will pad it out again. So always pass all the bytes.
std::vector<uint8_t> bytes(sizeof(result_value));
memcpy(bytes.data(), &result_value, sizeof(result_value));
cb(Err(), std::move(bytes));
};
// Dispatch the evaluation request. In practice, many call site expression evaluations will
// complete synchronouslt because they're expressed in terms of other known registers. But the
// contract for GetRegisterAsync is that it will always complete asynchronously. As a result,
// always start execution from the message loop to prevent executing the callback from within
// the caller's stack frame.
//
// Note that we pass the frame_provider_ as the symbol data provider instead of ourselves. Call
// site parameters should not be expressed in terms of other call site parameters, so we only need
// the underlying values. And this avoids the danger of infinitely recursive definitions.
auto evaluator = fxl::MakeRefCounted<AsyncDwarfExprEval>(std::move(handle_done));
debug::MessageLoop::Current()->PostTask(
FROM_HERE,
[evaluator, provider = frame_provider_, symbol_context = call_site_symbol_context_,
expr = param->value_expr()]() { evaluator->Eval(provider, symbol_context, expr); });
}
void CallSiteSymbolDataProvider::WriteRegister(debug::RegisterID id, std::vector<uint8_t> data,
WriteCallback cb) {
// We don't support writing registers into previous stack frames.
cb(Err("Writing registers is not supported in non-topmost stack frames."));
}
std::optional<uint64_t> CallSiteSymbolDataProvider::GetFrameBase() {
return frame_provider_->GetFrameBase();
}
void CallSiteSymbolDataProvider::GetFrameBaseAsync(GetFrameBaseCallback callback) {
return frame_provider_->GetFrameBaseAsync(std::move(callback));
}
uint64_t CallSiteSymbolDataProvider::GetCanonicalFrameAddress() const {
return frame_provider_->GetCanonicalFrameAddress();
}
bool CallSiteSymbolDataProvider::IsRegisterCalleeSaved(debug::RegisterID id) {
return process() && process()->session()->arch_info().abi()->IsRegisterCalleeSaved(id);
}
fxl::RefPtr<CallSiteParameter> CallSiteSymbolDataProvider::ParameterForRegister(
debug::RegisterID id) {
if (!call_site_)
return nullptr;
// Map to the DWARF register ID referenced by the call site parameters.
const debug::RegisterInfo* info = debug::InfoForRegister(id);
if (!info || info->dwarf_id == debug::RegisterInfo::kNoDwarfId)
return nullptr;
uint32_t dwarf_id = info->dwarf_id;
// Brute-force search for a match (there are normally only a couple, and normally we only need
// one value from a call site anyway).
for (const auto& lazy_param : call_site_->parameters()) {
const CallSiteParameter* param = lazy_param.Get()->As<CallSiteParameter>();
if (param && param->location_register_num() && *param->location_register_num() == dwarf_id)
return RefPtrTo(param);
}
return nullptr;
}
} // namespace zxdb