| // 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 |