Files
llvm-project/lldb/source/API/SBFrame.cpp
Aman LaChapelle 99e4f6a05f [lldb] Add synthetic variable support to Get*VariableList. (#181501)
This patch adds a new flag to the lldb_private::StackFrame API to get variable lists: `include_synthetic_vars`.  This allows ScriptedFrame (and other future synthetic frames) to construct 'fake' variables and return them in the VariableList, so that commands like `fr v` and `SBFrame::GetVariables` can show them to the user as requested.

This patch includes all changes necessary to call the API the new way - I tried to use my best judgement on when to include synthetic variables or not and leave comments explaining the decision.

As a consequence of producing synthetic variables, this patch means that ScriptedFrame can produce Variable objects with ValueType that contains a ValueTypeExtendedMask in a high bit. This necessarily complicates some of the switch/case handling in places where we would expect to find such variables, and this patch makes best effort to address all such cases as well. From experience, they tend to show up whenever we're dealing with checking if a Variable is in a specified scope, which means we basically have to check the high bit against some user input saying "yes/no synthetic variables".
2026-04-21 13:56:47 -07:00

1249 lines
38 KiB
C++

//===-- SBFrame.cpp -------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include <algorithm>
#include <set>
#include <string>
#include "lldb/API/SBFrame.h"
#include "lldb/Utility/ValueType.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-types.h"
#include "Utils.h"
#include "lldb/Core/Address.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Expression/ExpressionVariable.h"
#include "lldb/Expression/UserExpression.h"
#include "lldb/Host/Host.h"
#include "lldb/Symbol/Block.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Symbol/Variable.h"
#include "lldb/Symbol/VariableList.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/StackFrameRecognizer.h"
#include "lldb/Target/StackID.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"
#include "lldb/Utility/ConstString.h"
#include "lldb/Utility/Instrumentation.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Stream.h"
#include "lldb/ValueObject/ValueObjectConstResult.h"
#include "lldb/ValueObject/ValueObjectRegister.h"
#include "lldb/ValueObject/ValueObjectVariable.h"
#include "lldb/API/SBAddress.h"
#include "lldb/API/SBDebugger.h"
#include "lldb/API/SBExpressionOptions.h"
#include "lldb/API/SBFormat.h"
#include "lldb/API/SBStream.h"
#include "lldb/API/SBStructuredData.h"
#include "lldb/API/SBSymbolContext.h"
#include "lldb/API/SBThread.h"
#include "lldb/API/SBValue.h"
#include "lldb/API/SBVariablesOptions.h"
#include "llvm/Support/PrettyStackTrace.h"
using namespace lldb;
using namespace lldb_private;
SBFrame::SBFrame() : m_opaque_sp(new ExecutionContextRef()) {
LLDB_INSTRUMENT_VA(this);
}
SBFrame::SBFrame(const StackFrameSP &lldb_object_sp)
: m_opaque_sp(new ExecutionContextRef(lldb_object_sp)) {
LLDB_INSTRUMENT_VA(this, lldb_object_sp);
}
SBFrame::SBFrame(const SBFrame &rhs) {
LLDB_INSTRUMENT_VA(this, rhs);
m_opaque_sp = clone(rhs.m_opaque_sp);
}
SBFrame::~SBFrame() = default;
const SBFrame &SBFrame::operator=(const SBFrame &rhs) {
LLDB_INSTRUMENT_VA(this, rhs);
if (this != &rhs)
m_opaque_sp = clone(rhs.m_opaque_sp);
return *this;
}
StackFrameSP SBFrame::GetFrameSP() const {
return (m_opaque_sp ? m_opaque_sp->GetFrameSP() : StackFrameSP());
}
void SBFrame::SetFrameSP(const StackFrameSP &lldb_object_sp) {
return m_opaque_sp->SetFrameSP(lldb_object_sp);
}
bool SBFrame::IsValid() const {
LLDB_INSTRUMENT_VA(this);
return this->operator bool();
}
SBFrame::operator bool() const {
LLDB_INSTRUMENT_VA(this);
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return false;
}
return GetFrameSP().get() != nullptr;
}
SBSymbolContext SBFrame::GetSymbolContext(uint32_t resolve_scope) const {
LLDB_INSTRUMENT_VA(this, resolve_scope);
SBSymbolContext sb_sym_ctx;
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return sb_sym_ctx;
}
SymbolContextItem scope = static_cast<SymbolContextItem>(resolve_scope);
if (StackFrame *frame = exe_ctx->GetFramePtr())
sb_sym_ctx = frame->GetSymbolContext(scope);
return sb_sym_ctx;
}
SBModule SBFrame::GetModule() const {
LLDB_INSTRUMENT_VA(this);
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return SBModule();
}
ModuleSP module_sp;
StackFrame *frame = exe_ctx->GetFramePtr();
if (!frame)
return SBModule();
SBModule sb_module;
module_sp = frame->GetSymbolContext(eSymbolContextModule).module_sp;
sb_module.SetSP(module_sp);
return sb_module;
}
SBCompileUnit SBFrame::GetCompileUnit() const {
LLDB_INSTRUMENT_VA(this);
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return SBCompileUnit();
}
if (StackFrame *frame = exe_ctx->GetFramePtr())
return SBCompileUnit(
frame->GetSymbolContext(eSymbolContextCompUnit).comp_unit);
return SBCompileUnit();
}
SBFunction SBFrame::GetFunction() const {
LLDB_INSTRUMENT_VA(this);
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return SBFunction();
}
if (StackFrame *frame = exe_ctx->GetFramePtr())
return SBFunction(frame->GetSymbolContext(eSymbolContextFunction).function);
return SBFunction();
}
SBSymbol SBFrame::GetSymbol() const {
LLDB_INSTRUMENT_VA(this);
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return SBSymbol();
}
if (StackFrame *frame = exe_ctx->GetFramePtr())
return SBSymbol(frame->GetSymbolContext(eSymbolContextSymbol).symbol);
return SBSymbol();
}
SBBlock SBFrame::GetBlock() const {
LLDB_INSTRUMENT_VA(this);
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return SBBlock();
}
if (StackFrame *frame = exe_ctx->GetFramePtr())
return SBBlock(frame->GetSymbolContext(eSymbolContextBlock).block);
return SBBlock();
}
SBBlock SBFrame::GetFrameBlock() const {
LLDB_INSTRUMENT_VA(this);
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return SBBlock();
}
if (StackFrame *frame = exe_ctx->GetFramePtr())
return SBBlock(frame->GetFrameBlock());
return SBBlock();
}
SBLineEntry SBFrame::GetLineEntry() const {
LLDB_INSTRUMENT_VA(this);
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return SBLineEntry();
}
if (StackFrame *frame = exe_ctx->GetFramePtr())
return SBLineEntry(
&frame->GetSymbolContext(eSymbolContextLineEntry).line_entry);
return SBLineEntry();
}
uint32_t SBFrame::GetFrameID() const {
LLDB_INSTRUMENT_VA(this);
constexpr uint32_t error_frame_idx = UINT32_MAX;
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return error_frame_idx;
}
if (StackFrame *frame = exe_ctx->GetFramePtr())
return frame->GetFrameIndex();
return error_frame_idx;
}
lldb::addr_t SBFrame::GetCFA() const {
LLDB_INSTRUMENT_VA(this);
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return LLDB_INVALID_ADDRESS;
}
if (StackFrame *frame = exe_ctx->GetFramePtr())
return frame->GetStackID().GetCallFrameAddressWithoutMetadata();
return LLDB_INVALID_ADDRESS;
}
addr_t SBFrame::GetPC() const {
LLDB_INSTRUMENT_VA(this);
addr_t addr = LLDB_INVALID_ADDRESS;
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return addr;
}
Target *target = exe_ctx->GetTargetPtr();
if (StackFrame *frame = exe_ctx->GetFramePtr())
return frame->GetFrameCodeAddress().GetOpcodeLoadAddress(
target, AddressClass::eCode);
return addr;
}
bool SBFrame::SetPC(addr_t new_pc) {
LLDB_INSTRUMENT_VA(this, new_pc);
constexpr bool error_ret_val = false;
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return error_ret_val;
}
if (StackFrame *frame = exe_ctx->GetFramePtr())
if (RegisterContextSP reg_ctx_sp = frame->GetRegisterContext())
return reg_ctx_sp->SetPC(new_pc);
return error_ret_val;
}
addr_t SBFrame::GetSP() const {
LLDB_INSTRUMENT_VA(this);
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return LLDB_INVALID_ADDRESS;
}
if (StackFrame *frame = exe_ctx->GetFramePtr())
if (RegisterContextSP reg_ctx_sp = frame->GetRegisterContext())
return reg_ctx_sp->GetSP();
return LLDB_INVALID_ADDRESS;
}
addr_t SBFrame::GetFP() const {
LLDB_INSTRUMENT_VA(this);
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return LLDB_INVALID_ADDRESS;
}
if (StackFrame *frame = exe_ctx->GetFramePtr())
if (RegisterContextSP reg_ctx_sp = frame->GetRegisterContext())
return reg_ctx_sp->GetFP();
return LLDB_INVALID_ADDRESS;
}
SBAddress SBFrame::GetPCAddress() const {
LLDB_INSTRUMENT_VA(this);
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return SBAddress();
}
if (StackFrame *frame = exe_ctx->GetFramePtr())
return SBAddress(frame->GetFrameCodeAddress());
return SBAddress();
}
void SBFrame::Clear() {
LLDB_INSTRUMENT_VA(this);
m_opaque_sp->Clear();
}
lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path,
lldb::DILMode mode) {
LLDB_INSTRUMENT_VA(this, var_path);
SBValue sb_value;
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return sb_value;
}
if (StackFrame *frame = exe_ctx->GetFramePtr()) {
lldb::DynamicValueType use_dynamic =
frame->CalculateTarget()->GetPreferDynamicValue();
sb_value = GetValueForVariablePath(var_path, use_dynamic, mode);
}
return sb_value;
}
lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path,
DynamicValueType use_dynamic,
lldb::DILMode mode) {
LLDB_INSTRUMENT_VA(this, var_path, use_dynamic);
SBValue sb_value;
if (var_path == nullptr || var_path[0] == '\0') {
return sb_value;
}
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return sb_value;
}
if (StackFrame *frame = exe_ctx->GetFramePtr()) {
VariableSP var_sp;
Status error;
ValueObjectSP value_sp(frame->GetValueForVariableExpressionPath(
var_path, eNoDynamicValues,
StackFrame::eExpressionPathOptionCheckPtrVsMember |
StackFrame::eExpressionPathOptionsAllowDirectIVarAccess,
var_sp, error, mode));
sb_value.SetSP(value_sp, use_dynamic);
}
return sb_value;
}
SBValue SBFrame::FindVariable(const char *name) {
LLDB_INSTRUMENT_VA(this, name);
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return SBValue();
}
if (StackFrame *frame = exe_ctx->GetFramePtr()) {
lldb::DynamicValueType use_dynamic =
frame->CalculateTarget()->GetPreferDynamicValue();
return FindVariable(name, use_dynamic);
}
return SBValue();
}
SBValue SBFrame::FindVariable(const char *name,
lldb::DynamicValueType use_dynamic) {
LLDB_INSTRUMENT_VA(this, name, use_dynamic);
VariableSP var_sp;
SBValue sb_value;
if (name == nullptr || name[0] == '\0') {
return sb_value;
}
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return sb_value;
}
if (StackFrame *frame = exe_ctx->GetFramePtr())
if (ValueObjectSP value_sp = frame->FindVariable(ConstString(name)))
sb_value.SetSP(value_sp, use_dynamic);
return sb_value;
}
SBValue SBFrame::FindValue(const char *name, ValueType value_type) {
LLDB_INSTRUMENT_VA(this, name, value_type);
SBValue value;
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return value;
}
if (StackFrame *frame = exe_ctx->GetFramePtr()) {
lldb::DynamicValueType use_dynamic =
frame->CalculateTarget()->GetPreferDynamicValue();
value = FindValue(name, value_type, use_dynamic);
}
return value;
}
SBValue SBFrame::FindValue(const char *name, ValueType value_type,
lldb::DynamicValueType use_dynamic) {
LLDB_INSTRUMENT_VA(this, name, value_type, use_dynamic);
SBValue sb_value;
if (name == nullptr || name[0] == '\0') {
return sb_value;
}
ValueObjectSP value_sp;
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return value_sp;
}
StackFrame *frame = exe_ctx->GetFramePtr();
if (!frame)
return value_sp;
VariableList variable_list;
bool include_synthetic_vars = IsSyntheticValueType(value_type);
// Switch on the value_type without the mask, but keep it in the value type so
// we can use it later when we look for variables in the list.
auto base_value_type = GetBaseValueType(value_type);
switch (base_value_type) {
case eValueTypeVariableGlobal: // global variable
case eValueTypeVariableStatic: // static variable
case eValueTypeVariableArgument: // function argument variables
case eValueTypeVariableLocal: // function local variables
case eValueTypeVariableThreadLocal: { // thread local variables
SymbolContext sc(frame->GetSymbolContext(eSymbolContextBlock));
const bool can_create = true;
const bool get_parent_variables = true;
const bool stop_if_block_is_inlined_function = true;
if (sc.block)
sc.block->AppendVariables(
can_create, get_parent_variables, stop_if_block_is_inlined_function,
[frame](Variable *v) { return v->IsInScope(frame); }, &variable_list);
// Fetch variables from the frame if we need to get
// globals/statics/synthetic variables.
if (base_value_type == eValueTypeVariableGlobal ||
base_value_type == eValueTypeVariableStatic || include_synthetic_vars) {
const bool get_file_globals = true;
VariableList *frame_vars = frame->GetVariableList(
get_file_globals, include_synthetic_vars, nullptr);
if (frame_vars)
frame_vars->AppendVariablesIfUnique(variable_list);
}
ConstString const_name(name);
VariableSP variable_sp(variable_list.FindVariable(const_name, value_type));
if (variable_sp) {
value_sp =
frame->GetValueObjectForFrameVariable(variable_sp, eNoDynamicValues);
sb_value.SetSP(value_sp, use_dynamic);
}
} break;
case eValueTypeRegister: { // stack frame register value
if (RegisterContextSP reg_ctx = frame->GetRegisterContext()) {
if (const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(name)) {
value_sp = ValueObjectRegister::Create(frame, reg_ctx, reg_info);
sb_value.SetSP(value_sp);
}
}
} break;
case eValueTypeRegisterSet: { // A collection of stack frame register
// values
if (RegisterContextSP reg_ctx = frame->GetRegisterContext()) {
const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) {
const RegisterSet *reg_set = reg_ctx->GetRegisterSet(set_idx);
if (reg_set &&
(llvm::StringRef(reg_set->name).equals_insensitive(name) ||
llvm::StringRef(reg_set->short_name).equals_insensitive(name))) {
value_sp = ValueObjectRegisterSet::Create(frame, reg_ctx, set_idx);
sb_value.SetSP(value_sp);
break;
}
}
}
} break;
case eValueTypeConstResult: { // constant result variables
ConstString const_name(name);
Target *target = exe_ctx->GetTargetPtr();
ExpressionVariableSP expr_var_sp(target->GetPersistentVariable(const_name));
if (expr_var_sp) {
value_sp = expr_var_sp->GetValueObject();
sb_value.SetSP(value_sp, use_dynamic);
}
} break;
default:
break;
}
return sb_value;
}
bool SBFrame::IsEqual(const SBFrame &that) const {
LLDB_INSTRUMENT_VA(this, that);
lldb::StackFrameSP this_sp = GetFrameSP();
lldb::StackFrameSP that_sp = that.GetFrameSP();
return (this_sp && that_sp && this_sp->GetStackID() == that_sp->GetStackID());
}
bool SBFrame::operator==(const SBFrame &rhs) const {
LLDB_INSTRUMENT_VA(this, rhs);
return IsEqual(rhs);
}
bool SBFrame::operator!=(const SBFrame &rhs) const {
LLDB_INSTRUMENT_VA(this, rhs);
return !IsEqual(rhs);
}
SBThread SBFrame::GetThread() const {
LLDB_INSTRUMENT_VA(this);
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return SBThread();
}
ThreadSP thread_sp(exe_ctx->GetThreadSP());
SBThread sb_thread(thread_sp);
return sb_thread;
}
const char *SBFrame::Disassemble() const {
LLDB_INSTRUMENT_VA(this);
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return nullptr;
}
if (auto *frame = exe_ctx->GetFramePtr())
return ConstString(frame->Disassemble()).GetCString();
return nullptr;
}
SBValueList SBFrame::GetVariables(bool arguments, bool locals, bool statics,
bool in_scope_only) {
LLDB_INSTRUMENT_VA(this, arguments, locals, statics, in_scope_only);
SBValueList value_list;
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return value_list;
}
if (StackFrame *frame = exe_ctx->GetFramePtr()) {
Target *target = exe_ctx->GetTargetPtr();
lldb::DynamicValueType use_dynamic =
frame->CalculateTarget()->GetPreferDynamicValue();
const bool include_runtime_support_values =
target->GetDisplayRuntimeSupportValues();
SBVariablesOptions options;
options.SetIncludeArguments(arguments);
options.SetIncludeLocals(locals);
options.SetIncludeStatics(statics);
options.SetInScopeOnly(in_scope_only);
options.SetIncludeRuntimeSupportValues(include_runtime_support_values);
options.SetUseDynamic(use_dynamic);
value_list = GetVariables(options);
}
return value_list;
}
lldb::SBValueList SBFrame::GetVariables(bool arguments, bool locals,
bool statics, bool in_scope_only,
lldb::DynamicValueType use_dynamic) {
LLDB_INSTRUMENT_VA(this, arguments, locals, statics, in_scope_only,
use_dynamic);
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return SBValueList();
}
Target *target = exe_ctx->GetTargetPtr();
const bool include_runtime_support_values =
target->GetDisplayRuntimeSupportValues();
SBVariablesOptions options;
options.SetIncludeArguments(arguments);
options.SetIncludeLocals(locals);
options.SetIncludeStatics(statics);
options.SetInScopeOnly(in_scope_only);
options.SetIncludeRuntimeSupportValues(include_runtime_support_values);
options.SetUseDynamic(use_dynamic);
return GetVariables(options);
}
/// Returns true if the variable is in any of the requested scopes.
static bool IsInRequestedScope(bool statics, bool arguments, bool locals,
bool synthetic, Variable &var) {
auto value_type = var.GetScope();
// Check if the variable is synthetic first.
bool is_synthetic = IsSyntheticValueType(value_type);
if (is_synthetic) {
// If the variable is synthetic but we don't want those, then it's
// automatically out of scope.
if (!synthetic)
return false;
// Get the base value type so the rest of the switch works correctly.
value_type = GetBaseValueType(value_type);
}
switch (value_type) {
case eValueTypeVariableGlobal:
case eValueTypeVariableStatic:
case eValueTypeVariableThreadLocal:
return statics;
case eValueTypeVariableArgument:
return arguments;
case eValueTypeVariableLocal:
return locals;
default:
break;
}
// The default for all other value types is is_synthetic. At this point, if
// we didn't want synthetic variables we'd have exited by now anyway, so we
// must want them. Aside from the modifiers above that should apply equally to
// synthetic and normal variables, any other synthetic variable we should
// default to showing.
return is_synthetic;
}
enum WasInterrupted { Yes, No };
/// Populates `value_list` with the variables from `frame` according to
/// `options`. This method checks whether the Debugger received an interrupt
/// before processing every variable, returning `WasInterrupted::yes` in that
/// case.
static std::pair<WasInterrupted, Status> FetchVariablesUnlessInterrupted(
const lldb::SBVariablesOptions &options, StackFrame &frame,
SBValueList &value_list, Debugger &dbg,
std::function<SBValue(ValueObjectSP, bool)> to_sbvalue) {
const bool statics = options.GetIncludeStatics();
const bool arguments = options.GetIncludeArguments();
const bool locals = options.GetIncludeLocals();
const bool synthetic = options.GetIncludeSynthetic();
const bool in_scope_only = options.GetInScopeOnly();
const bool include_runtime_support_values =
options.GetIncludeRuntimeSupportValues();
const lldb::DynamicValueType use_dynamic = options.GetUseDynamic();
Status var_error;
// Fetch all variables available and filter them later.
VariableList *variable_list = frame.GetVariableList(
/*get_file_globals=*/true, /*include_synthetic_vars=*/true, &var_error);
std::set<VariableSP> variable_set;
if (!variable_list)
return {WasInterrupted::No, std::move(var_error)};
const size_t num_variables = variable_list->GetSize();
size_t num_produced = 0;
for (const VariableSP &variable_sp : *variable_list) {
if (!variable_sp || !IsInRequestedScope(statics, arguments, locals,
synthetic, *variable_sp))
continue;
if (INTERRUPT_REQUESTED(
dbg,
"Interrupted getting frame variables with {0} of {1} "
"produced.",
num_produced, num_variables))
return {WasInterrupted::Yes, std::move(var_error)};
// Only add variables once so we don't end up with duplicates
if (variable_set.insert(variable_sp).second == false)
continue;
if (in_scope_only && !variable_sp->IsInScope(&frame))
continue;
ValueObjectSP valobj_sp(
frame.GetValueObjectForFrameVariable(variable_sp, eNoDynamicValues));
if (!include_runtime_support_values && valobj_sp != nullptr &&
valobj_sp->IsRuntimeSupportValue())
continue;
value_list.Append(to_sbvalue(valobj_sp, use_dynamic));
}
num_produced++;
return {WasInterrupted::No, std::move(var_error)};
}
/// Populates `value_list` with recognized arguments of `frame` according to
/// `options`.
static llvm::SmallVector<ValueObjectSP>
FetchRecognizedArguments(const SBVariablesOptions &options, StackFrame &frame,
SBTarget target) {
if (!options.GetIncludeRecognizedArguments(target))
return {};
RecognizedStackFrameSP recognized_frame = frame.GetRecognizedFrame();
if (!recognized_frame)
return {};
ValueObjectListSP recognized_arg_list =
recognized_frame->GetRecognizedArguments();
if (!recognized_arg_list)
return {};
return llvm::to_vector(recognized_arg_list->GetObjects());
}
SBValueList SBFrame::GetVariables(const lldb::SBVariablesOptions &options) {
LLDB_INSTRUMENT_VA(this, options);
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return SBValueList();
}
StackFrame *frame = exe_ctx->GetFramePtr();
if (!frame)
return SBValueList();
auto valobj_to_sbvalue = [](ValueObjectSP valobj, bool use_dynamic) {
SBValue value_sb;
value_sb.SetSP(valobj, use_dynamic);
return value_sb;
};
SBValueList value_list;
std::pair<WasInterrupted, Status> fetch_result =
FetchVariablesUnlessInterrupted(options, *frame, value_list,
exe_ctx->GetTargetPtr()->GetDebugger(),
valobj_to_sbvalue);
if (fetch_result.second.Fail())
value_list.SetError(std::move(fetch_result.second));
if (fetch_result.first == WasInterrupted::Yes)
return value_list;
const lldb::DynamicValueType use_dynamic = options.GetUseDynamic();
llvm::SmallVector<ValueObjectSP> args = FetchRecognizedArguments(
options, *frame, SBTarget(exe_ctx->GetTargetSP()));
for (ValueObjectSP arg : args) {
SBValue value_sb;
value_sb.SetSP(arg, use_dynamic);
value_list.Append(value_sb);
}
return value_list;
}
SBValueList SBFrame::GetRegisters() {
LLDB_INSTRUMENT_VA(this);
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return SBValueList();
}
StackFrame *frame = exe_ctx->GetFramePtr();
if (!frame)
return SBValueList();
RegisterContextSP reg_ctx(frame->GetRegisterContext());
if (!reg_ctx)
return SBValueList();
SBValueList value_list;
const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx)
value_list.Append(ValueObjectRegisterSet::Create(frame, reg_ctx, set_idx));
return value_list;
}
SBValue SBFrame::FindRegister(const char *name) {
LLDB_INSTRUMENT_VA(this, name);
ValueObjectSP value_sp;
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return SBValue();
}
StackFrame *frame = exe_ctx->GetFramePtr();
if (!frame)
return SBValue();
RegisterContextSP reg_ctx(frame->GetRegisterContext());
if (!reg_ctx)
return SBValue();
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(name);
if (!reg_info)
return SBValue();
SBValue result;
value_sp = ValueObjectRegister::Create(frame, reg_ctx, reg_info);
result.SetSP(value_sp);
return result;
}
SBError SBFrame::GetDescriptionWithFormat(const SBFormat &format,
SBStream &output) {
Stream &strm = output.ref();
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx)
return Status::FromError(exe_ctx.takeError());
SBError error;
if (!format) {
error.SetErrorString("The provided SBFormat object is invalid");
return error;
}
if (StackFrame *frame = exe_ctx->GetFramePtr();
frame && frame->DumpUsingFormat(strm, format.GetFormatEntrySP().get()))
return error;
error.SetErrorStringWithFormat(
"It was not possible to generate a frame "
"description with the given format string '%s'",
format.GetFormatEntrySP()->string.c_str());
return error;
}
bool SBFrame::GetDescription(SBStream &description) {
LLDB_INSTRUMENT_VA(this, description);
Stream &strm = description.ref();
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
strm.PutCString("Error: process is not stopped.");
return true;
}
if (StackFrame *frame = exe_ctx->GetFramePtr())
frame->DumpUsingSettingsFormat(&strm);
return true;
}
SBValue SBFrame::EvaluateExpression(const char *expr) {
LLDB_INSTRUMENT_VA(this, expr);
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return CreateProcessIsRunningExprEvalError();
}
SBExpressionOptions options;
StackFrame *frame = exe_ctx->GetFramePtr();
if (frame) {
lldb::DynamicValueType fetch_dynamic_value =
frame->CalculateTarget()->GetPreferDynamicValue();
options.SetFetchDynamicValue(fetch_dynamic_value);
}
options.SetUnwindOnError(true);
options.SetIgnoreBreakpoints(true);
Target *target = exe_ctx->GetTargetPtr();
SourceLanguage language = target->GetLanguage();
if (!language && frame)
language = frame->GetLanguage();
options.SetLanguage((SBSourceLanguageName)language.name, language.version);
return EvaluateExpression(expr, options);
}
SBValue
SBFrame::EvaluateExpression(const char *expr,
lldb::DynamicValueType fetch_dynamic_value) {
LLDB_INSTRUMENT_VA(this, expr, fetch_dynamic_value);
SBExpressionOptions options;
options.SetFetchDynamicValue(fetch_dynamic_value);
options.SetUnwindOnError(true);
options.SetIgnoreBreakpoints(true);
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return CreateProcessIsRunningExprEvalError();
}
StackFrame *frame = exe_ctx->GetFramePtr();
Target *target = exe_ctx->GetTargetPtr();
SourceLanguage language = target->GetLanguage();
if (!language && frame)
language = frame->GetLanguage();
options.SetLanguage((SBSourceLanguageName)language.name, language.version);
return EvaluateExpression(expr, options);
}
SBValue SBFrame::EvaluateExpression(const char *expr,
lldb::DynamicValueType fetch_dynamic_value,
bool unwind_on_error) {
LLDB_INSTRUMENT_VA(this, expr, fetch_dynamic_value, unwind_on_error);
SBExpressionOptions options;
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return CreateProcessIsRunningExprEvalError();
}
options.SetFetchDynamicValue(fetch_dynamic_value);
options.SetUnwindOnError(unwind_on_error);
options.SetIgnoreBreakpoints(true);
StackFrame *frame = exe_ctx->GetFramePtr();
Target *target = exe_ctx->GetTargetPtr();
SourceLanguage language = target->GetLanguage();
if (!language && frame)
language = frame->GetLanguage();
options.SetLanguage((SBSourceLanguageName)language.name, language.version);
return EvaluateExpression(expr, options);
}
lldb::SBValue SBFrame::CreateProcessIsRunningExprEvalError() {
auto error = Status::FromErrorString("can't evaluate expressions when the "
"process is running.");
ValueObjectSP expr_value_sp =
ValueObjectConstResult::Create(nullptr, std::move(error));
SBValue expr_result;
expr_result.SetSP(expr_value_sp, false);
return expr_result;
}
lldb::SBValue SBFrame::EvaluateExpression(const char *expr,
const SBExpressionOptions &options) {
LLDB_INSTRUMENT_VA(this, expr, options);
auto LogResult = [](SBValue expr_result) {
Log *expr_log = GetLog(LLDBLog::Expressions);
if (expr_result.GetError().Success())
LLDB_LOGF(expr_log,
"** [SBFrame::EvaluateExpression] Expression result is "
"%s, summary %s **",
expr_result.GetValue(), expr_result.GetSummary());
else
LLDB_LOGF(
expr_log,
"** [SBFrame::EvaluateExpression] Expression evaluation failed: "
"%s **",
expr_result.GetError().GetCString());
};
if (expr == nullptr || expr[0] == '\0') {
return SBValue();
}
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
SBValue error_result = CreateProcessIsRunningExprEvalError();
LogResult(error_result);
return error_result;
}
StackFrame *frame = exe_ctx->GetFramePtr();
if (!frame)
return SBValue();
std::unique_ptr<llvm::PrettyStackTraceFormat> stack_trace;
Target *target = exe_ctx->GetTargetPtr();
if (target->GetDisplayExpressionsInCrashlogs()) {
StreamString frame_description;
frame->DumpUsingSettingsFormat(&frame_description);
stack_trace = std::make_unique<llvm::PrettyStackTraceFormat>(
"SBFrame::EvaluateExpression (expr = \"%s\", fetch_dynamic_value "
"= %u) %s",
expr, options.GetFetchDynamicValue(), frame_description.GetData());
}
ValueObjectSP expr_value_sp;
target->EvaluateExpression(expr, frame, expr_value_sp, options.ref());
SBValue expr_result;
expr_result.SetSP(expr_value_sp, options.GetFetchDynamicValue());
LogResult(expr_result);
return expr_result;
}
SBStructuredData SBFrame::GetLanguageSpecificData() const {
LLDB_INSTRUMENT_VA(this);
SBStructuredData sb_data;
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return sb_data;
}
StackFrame *frame = exe_ctx->GetFramePtr();
if (!frame)
return sb_data;
StructuredData::ObjectSP data(frame->GetLanguageSpecificData());
sb_data.m_impl_up->SetObjectSP(data);
return sb_data;
}
bool SBFrame::IsInlined() {
LLDB_INSTRUMENT_VA(this);
return static_cast<const SBFrame *>(this)->IsInlined();
}
bool SBFrame::IsInlined() const {
LLDB_INSTRUMENT_VA(this);
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return false;
}
if (StackFrame *frame = exe_ctx->GetFramePtr())
return frame->IsInlined();
return false;
}
bool SBFrame::IsArtificial() {
LLDB_INSTRUMENT_VA(this);
return static_cast<const SBFrame *>(this)->IsArtificial();
}
bool SBFrame::IsArtificial() const {
LLDB_INSTRUMENT_VA(this);
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return false;
}
if (StackFrame *frame = exe_ctx->GetFramePtr())
return frame->IsArtificial();
return false;
}
bool SBFrame::IsSynthetic() const {
LLDB_INSTRUMENT_VA(this);
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return false;
}
if (StackFrame *frame = exe_ctx->GetFramePtr())
return frame->IsSynthetic();
return false;
}
bool SBFrame::IsHidden() const {
LLDB_INSTRUMENT_VA(this);
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return false;
}
if (StackFrame *frame = exe_ctx->GetFramePtr())
return frame->IsHidden();
return false;
}
const char *SBFrame::GetFunctionName() {
LLDB_INSTRUMENT_VA(this);
return static_cast<const SBFrame *>(this)->GetFunctionName();
}
lldb::LanguageType SBFrame::GuessLanguage() const {
LLDB_INSTRUMENT_VA(this);
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return eLanguageTypeUnknown;
}
if (StackFrame *frame = exe_ctx->GetFramePtr())
return frame->GuessLanguage().AsLanguageType();
return eLanguageTypeUnknown;
}
const char *SBFrame::GetFunctionName() const {
LLDB_INSTRUMENT_VA(this);
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return nullptr;
}
if (StackFrame *frame = exe_ctx->GetFramePtr())
return frame->GetFunctionName();
return nullptr;
}
const char *SBFrame::GetDisplayFunctionName() {
LLDB_INSTRUMENT_VA(this);
llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return nullptr;
}
if (StackFrame *frame = exe_ctx->GetFramePtr())
return frame->GetDisplayFunctionName();
return nullptr;
}