[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".
This commit is contained in:
@@ -78,10 +78,12 @@ public:
|
||||
lldb::RegisterContextSP GetRegisterContext() override;
|
||||
|
||||
VariableList *GetVariableList(bool get_file_globals,
|
||||
bool include_synthetic_vars,
|
||||
Status *error_ptr) override;
|
||||
|
||||
lldb::VariableListSP
|
||||
GetInScopeVariableList(bool get_file_globals,
|
||||
bool include_synthetic_vars = true,
|
||||
bool must_have_valid_location = false) override;
|
||||
|
||||
lldb::ValueObjectSP GetValueForVariableExpressionPath(
|
||||
|
||||
@@ -264,6 +264,12 @@ public:
|
||||
/// that are visible to the entire compilation unit (e.g. file
|
||||
/// static in C, globals that are homed in this CU).
|
||||
///
|
||||
/// \param[in] include_synthetic_vars
|
||||
/// Whether to also include synthetic variables from other
|
||||
/// sources. For example, synthetic frames can produce
|
||||
/// variables that aren't strictly 'variables', but can still
|
||||
/// be displayed with their values.
|
||||
///
|
||||
/// \param [out] error_ptr
|
||||
/// If there is an error in the debug information that prevents variables
|
||||
/// from being fetched. \see SymbolFile::GetFrameVariableError() for full
|
||||
@@ -272,6 +278,7 @@ public:
|
||||
/// \return
|
||||
/// A pointer to a list of variables.
|
||||
virtual VariableList *GetVariableList(bool get_file_globals,
|
||||
bool include_synthetic_vars,
|
||||
Status *error_ptr);
|
||||
|
||||
/// Retrieve the list of variables that are in scope at this StackFrame's
|
||||
@@ -286,6 +293,14 @@ public:
|
||||
/// that are visible to the entire compilation unit (e.g. file
|
||||
/// static in C, globals that are homed in this CU).
|
||||
///
|
||||
/// \param[in] include_synthetic_vars
|
||||
/// Whether to also include synthetic variables from other
|
||||
/// sources. For example, synthetic frames can produce
|
||||
/// variables that aren't strictly 'variables', but can still
|
||||
/// be displayed with their values. Defaults to `true` because
|
||||
/// we are assuming that if a user's context has synthetic variables,
|
||||
/// they want them shown.
|
||||
///
|
||||
/// \param[in] must_have_valid_location
|
||||
/// Whether to filter variables whose location is not available at this
|
||||
/// StackFrame's pc.
|
||||
@@ -293,6 +308,7 @@ public:
|
||||
/// A pointer to a list of variables.
|
||||
virtual lldb::VariableListSP
|
||||
GetInScopeVariableList(bool get_file_globals,
|
||||
bool include_synthetic_vars = true,
|
||||
bool must_have_valid_location = false);
|
||||
|
||||
/// Create a ValueObject for a variable name / pathname, possibly including
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
|
||||
#include "lldb/API/SBFrame.h"
|
||||
|
||||
#include "lldb/Utility/ValueType.h"
|
||||
#include "lldb/lldb-enumerations.h"
|
||||
#include "lldb/lldb-types.h"
|
||||
|
||||
#include "Utils.h"
|
||||
@@ -500,7 +502,11 @@ SBValue SBFrame::FindValue(const char *name, ValueType value_type,
|
||||
|
||||
VariableList variable_list;
|
||||
|
||||
switch (value_type) {
|
||||
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
|
||||
@@ -516,14 +522,17 @@ SBValue SBFrame::FindValue(const char *name, ValueType value_type,
|
||||
sc.block->AppendVariables(
|
||||
can_create, get_parent_variables, stop_if_block_is_inlined_function,
|
||||
[frame](Variable *v) { return v->IsInScope(frame); }, &variable_list);
|
||||
if (value_type == eValueTypeVariableGlobal ||
|
||||
value_type == eValueTypeVariableStatic) {
|
||||
// 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, nullptr);
|
||||
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) {
|
||||
@@ -688,8 +697,21 @@ lldb::SBValueList SBFrame::GetVariables(bool arguments, bool locals,
|
||||
|
||||
/// Returns true if the variable is in any of the requested scopes.
|
||||
static bool IsInRequestedScope(bool statics, bool arguments, bool locals,
|
||||
Variable &var) {
|
||||
switch (var.GetScope()) {
|
||||
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:
|
||||
@@ -704,7 +726,13 @@ static bool IsInRequestedScope(bool statics, bool arguments, bool locals,
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
|
||||
// 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 };
|
||||
@@ -720,13 +748,16 @@ static std::pair<WasInterrupted, Status> FetchVariablesUnlessInterrupted(
|
||||
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;
|
||||
VariableList *variable_list = frame.GetVariableList(true, &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;
|
||||
|
||||
@@ -735,8 +766,8 @@ static std::pair<WasInterrupted, Status> FetchVariablesUnlessInterrupted(
|
||||
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, *variable_sp))
|
||||
if (!variable_sp || !IsInRequestedScope(statics, arguments, locals,
|
||||
synthetic, *variable_sp))
|
||||
continue;
|
||||
|
||||
if (INTERRUPT_REQUESTED(
|
||||
|
||||
@@ -29,7 +29,9 @@
|
||||
#include "lldb/Target/Target.h"
|
||||
#include "lldb/Target/Thread.h"
|
||||
#include "lldb/Utility/Args.h"
|
||||
#include "lldb/Utility/ValueType.h"
|
||||
#include "lldb/ValueObject/ValueObject.h"
|
||||
#include "lldb/lldb-enumerations.h"
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
@@ -337,9 +339,9 @@ protected:
|
||||
// The request went past the stack, so handle that case:
|
||||
const uint32_t num_frames = thread->GetStackFrameCount();
|
||||
if (static_cast<int32_t>(num_frames - frame_idx) >
|
||||
*m_options.relative_frame_offset)
|
||||
frame_idx += *m_options.relative_frame_offset;
|
||||
else {
|
||||
*m_options.relative_frame_offset) {
|
||||
frame_idx += *m_options.relative_frame_offset;
|
||||
} else {
|
||||
if (frame_idx == num_frames - 1) {
|
||||
// If we are already at the top of the stack, just warn and don't
|
||||
// reset the frame.
|
||||
@@ -439,17 +441,23 @@ protected:
|
||||
if (!var_sp)
|
||||
return llvm::StringRef();
|
||||
|
||||
switch (var_sp->GetScope()) {
|
||||
auto vt = var_sp->GetScope();
|
||||
bool is_synthetic = IsSyntheticValueType(vt);
|
||||
// Clear the bit so the rest works correctly.
|
||||
if (is_synthetic)
|
||||
vt = GetBaseValueType(vt);
|
||||
|
||||
switch (vt) {
|
||||
case eValueTypeVariableGlobal:
|
||||
return "GLOBAL: ";
|
||||
return is_synthetic ? "(synthetic) GLOBAL: " : "GLOBAL: ";
|
||||
case eValueTypeVariableStatic:
|
||||
return "STATIC: ";
|
||||
return is_synthetic ? "(synthetic) STATIC: " : "STATIC: ";
|
||||
case eValueTypeVariableArgument:
|
||||
return "ARG: ";
|
||||
return is_synthetic ? "(synthetic) ARG: " : "ARG: ";
|
||||
case eValueTypeVariableLocal:
|
||||
return "LOCAL: ";
|
||||
return is_synthetic ? "(synthetic) LOCAL: " : "LOCAL: ";
|
||||
case eValueTypeVariableThreadLocal:
|
||||
return "THREAD: ";
|
||||
return is_synthetic ? "(synthetic) THREAD: " : "THREAD: ";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -459,6 +467,14 @@ protected:
|
||||
|
||||
/// Returns true if `scope` matches any of the options in `m_option_variable`.
|
||||
bool ScopeRequested(lldb::ValueType scope) {
|
||||
// If it's a synthetic variable, check if we want to show those first.
|
||||
bool is_synthetic = IsSyntheticValueType(scope);
|
||||
if (is_synthetic) {
|
||||
if (!m_option_variable.show_synthetic)
|
||||
return false;
|
||||
|
||||
scope = GetBaseValueType(scope);
|
||||
}
|
||||
switch (scope) {
|
||||
case eValueTypeVariableGlobal:
|
||||
case eValueTypeVariableStatic:
|
||||
@@ -474,7 +490,10 @@ protected:
|
||||
case eValueTypeVariableThreadLocal:
|
||||
case eValueTypeVTable:
|
||||
case eValueTypeVTableEntry:
|
||||
return false;
|
||||
// The default for all other value types is is_synthetic. 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;
|
||||
}
|
||||
llvm_unreachable("Unexpected scope value");
|
||||
}
|
||||
@@ -521,7 +540,8 @@ protected:
|
||||
|
||||
Status error;
|
||||
VariableList *variable_list =
|
||||
frame->GetVariableList(m_option_variable.show_globals, &error);
|
||||
frame->GetVariableList(m_option_variable.show_globals,
|
||||
m_option_variable.show_synthetic, &error);
|
||||
|
||||
if (error.Fail() && (!variable_list || variable_list->GetSize() == 0)) {
|
||||
result.AppendError(error.AsCString());
|
||||
|
||||
@@ -5934,7 +5934,9 @@ public:
|
||||
if (m_frame_block != frame_block) {
|
||||
m_frame_block = frame_block;
|
||||
|
||||
VariableList *locals = frame->GetVariableList(true, nullptr);
|
||||
VariableList *locals = frame->GetVariableList(
|
||||
/*get_file_globals=*/true, /*include_synthetic_vars=*/true,
|
||||
nullptr);
|
||||
if (locals) {
|
||||
const DynamicValueType use_dynamic = eDynamicDontRunTarget;
|
||||
for (const VariableSP &local_sp : *locals) {
|
||||
|
||||
@@ -876,8 +876,11 @@ void ClangExpressionDeclMap::LookUpLldbClass(NameSearchContext &context) {
|
||||
// creates decls for function templates by attaching them to the TU instead
|
||||
// of a class context. So we can actually have template methods scoped
|
||||
// outside of a class. Once we fix that, we can remove this code-path.
|
||||
|
||||
VariableList *vars = frame->GetVariableList(false, nullptr);
|
||||
// Additionally, we exclude synthetic variables from here. Clang-based
|
||||
// languages are unlikely candidates for synthetic variables anyway, and
|
||||
// especially in this case, we're looking for something specific to C++.
|
||||
VariableList *vars = frame->GetVariableList(
|
||||
/*get_file_globals=*/false, /*include_synthetic_vars=*/false, nullptr);
|
||||
|
||||
lldb::VariableSP this_var = vars->FindVariable(ConstString("this"));
|
||||
|
||||
@@ -963,7 +966,11 @@ void ClangExpressionDeclMap::LookUpLldbObjCClass(NameSearchContext &context) {
|
||||
// In that case, just look up the "self" variable in the current scope
|
||||
// and use its type.
|
||||
|
||||
VariableList *vars = frame->GetVariableList(false, nullptr);
|
||||
// We exclude synthetic variables from here. Like above, it's highly unlikely
|
||||
// we care about synthetic variables here, and indeed this code is looking for
|
||||
// an obj-C specific construct.
|
||||
VariableList *vars = frame->GetVariableList(
|
||||
/*get_file_globals=*/false, /*include_synthetic_vars=*/false, nullptr);
|
||||
|
||||
lldb::VariableSP self_var = vars->FindVariable(ConstString("self"));
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#include "lldb/Core/Debugger.h"
|
||||
#include "lldb/Core/Module.h"
|
||||
#include "lldb/Core/ModuleList.h"
|
||||
#include "lldb/Expression/DWARFExpressionList.h"
|
||||
#include "lldb/Host/FileSystem.h"
|
||||
#include "lldb/Interpreter/Interfaces/ScriptedFrameInterface.h"
|
||||
#include "lldb/Interpreter/Interfaces/ScriptedInterface.h"
|
||||
#include "lldb/Interpreter/Interfaces/ScriptedThreadInterface.h"
|
||||
@@ -28,8 +30,14 @@
|
||||
#include "lldb/Utility/LLDBLog.h"
|
||||
#include "lldb/Utility/Log.h"
|
||||
#include "lldb/Utility/StructuredData.h"
|
||||
#include "lldb/Utility/ValueType.h"
|
||||
#include "lldb/ValueObject/ValueObject.h"
|
||||
#include "lldb/ValueObject/ValueObjectList.h"
|
||||
#include "lldb/lldb-enumerations.h"
|
||||
#include "lldb/lldb-forward.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
using namespace lldb;
|
||||
using namespace lldb_private;
|
||||
@@ -269,19 +277,22 @@ lldb::RegisterContextSP ScriptedFrame::GetRegisterContext() {
|
||||
}
|
||||
|
||||
VariableList *ScriptedFrame::GetVariableList(bool get_file_globals,
|
||||
bool include_synthetic_vars,
|
||||
Status *error_ptr) {
|
||||
PopulateVariableListFromInterface();
|
||||
PopulateVariableListFromInterface(include_synthetic_vars);
|
||||
return m_variable_list_sp.get();
|
||||
}
|
||||
|
||||
lldb::VariableListSP
|
||||
ScriptedFrame::GetInScopeVariableList(bool get_file_globals,
|
||||
bool include_synthetic_vars,
|
||||
bool must_have_valid_location) {
|
||||
PopulateVariableListFromInterface();
|
||||
PopulateVariableListFromInterface(include_synthetic_vars);
|
||||
return m_variable_list_sp;
|
||||
}
|
||||
|
||||
void ScriptedFrame::PopulateVariableListFromInterface() {
|
||||
void ScriptedFrame::PopulateVariableListFromInterface(
|
||||
bool include_synthetic_vars) {
|
||||
// Fetch values from the interface.
|
||||
ValueObjectListSP value_list_sp = GetInterface()->GetVariables();
|
||||
if (!value_list_sp)
|
||||
@@ -295,12 +306,28 @@ void ScriptedFrame::PopulateVariableListFromInterface() {
|
||||
continue;
|
||||
|
||||
VariableSP var = v->GetVariable();
|
||||
// TODO: We could in theory ask the scripted frame to *produce* a
|
||||
// variable for this value object.
|
||||
if (!var)
|
||||
continue;
|
||||
if (!var && include_synthetic_vars) {
|
||||
// Construct the value type as an synthetic verison of what the value type
|
||||
// is. That'll allow the user to tell the scope and the 'synthetic-ness'
|
||||
// of the variable.
|
||||
lldb::ValueType vt = GetSyntheticValueType(v->GetValueType());
|
||||
|
||||
m_variable_list_sp->AddVariable(var);
|
||||
// Just make up a variable - the frame variable dumper just passes it
|
||||
// back in to GetValueObjectForFrameVariable, so we really just need to
|
||||
// make sure the name and type are correct. We create IDs based on
|
||||
// value_list_sp in order to make sure they're unique.
|
||||
var = std::make_shared<lldb_private::Variable>(
|
||||
(lldb::user_id_t)value_list_sp->GetSize() + i,
|
||||
v->GetName().GetCString(), v->GetName().GetCString(), nullptr, vt,
|
||||
/*owner_scope=*/nullptr,
|
||||
/*scope_range=*/Variable::RangeList{},
|
||||
/*decl=*/nullptr, DWARFExpressionList{}, /*external=*/false,
|
||||
/*artificial=*/true, /*location_is_constant_data=*/false);
|
||||
}
|
||||
|
||||
// Only append the variable if we have one (had already, or just created).
|
||||
if (var)
|
||||
m_variable_list_sp->AddVariable(var);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,6 +342,15 @@ lldb::ValueObjectSP ScriptedFrame::GetValueObjectForFrameVariable(
|
||||
variable_sp->GetName().AsCString(nullptr));
|
||||
}
|
||||
|
||||
lldb::ValueObjectSP ScriptedFrame::FindVariable(ConstString name) {
|
||||
// Fetch values from the interface.
|
||||
ValueObjectListSP values = m_scripted_frame_interface_sp->GetVariables();
|
||||
if (!values)
|
||||
return {};
|
||||
|
||||
return values->FindValueObjectByValueName(name.AsCString(nullptr));
|
||||
}
|
||||
|
||||
lldb::ValueObjectSP ScriptedFrame::GetValueForVariableExpressionPath(
|
||||
llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
|
||||
uint32_t options, lldb::VariableSP &var_sp, Status &error,
|
||||
|
||||
@@ -64,16 +64,19 @@ public:
|
||||
lldb::RegisterContextSP GetRegisterContext() override;
|
||||
|
||||
VariableList *GetVariableList(bool get_file_globals,
|
||||
bool include_synthetic_vars,
|
||||
lldb_private::Status *error_ptr) override;
|
||||
|
||||
lldb::VariableListSP
|
||||
GetInScopeVariableList(bool get_file_globals,
|
||||
GetInScopeVariableList(bool get_file_globals, bool include_synthetic_vars,
|
||||
bool must_have_valid_location = false) override;
|
||||
|
||||
lldb::ValueObjectSP
|
||||
GetValueObjectForFrameVariable(const lldb::VariableSP &variable_sp,
|
||||
lldb::DynamicValueType use_dynamic) override;
|
||||
|
||||
lldb::ValueObjectSP FindVariable(ConstString name) override;
|
||||
|
||||
lldb::ValueObjectSP GetValueForVariableExpressionPath(
|
||||
llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
|
||||
uint32_t options, lldb::VariableSP &var_sp, Status &error,
|
||||
@@ -91,10 +94,12 @@ private:
|
||||
CreateRegisterContext(ScriptedFrameInterface &interface, Thread &thread,
|
||||
lldb::user_id_t frame_id);
|
||||
|
||||
// Populate m_variable_list_sp from the scripted frame interface. Right now
|
||||
// this doesn't take any options because the implementation can't really do
|
||||
// anything with those options anyway, so there's no point.
|
||||
void PopulateVariableListFromInterface();
|
||||
// Populate m_variable_list_sp from the scripted frame interface. The boolean
|
||||
// controls if we should try to fabricate Variable objects for each of the
|
||||
// ValueObjects that we have. This defaults to 'true' because this is a
|
||||
// scripted frame, so kind of the whole point is to provide synthetic
|
||||
// variables to the user.
|
||||
void PopulateVariableListFromInterface(bool include_synthetic_vars = true);
|
||||
|
||||
ScriptedFrame(const ScriptedFrame &) = delete;
|
||||
const ScriptedFrame &operator=(const ScriptedFrame &) = delete;
|
||||
|
||||
@@ -590,9 +590,10 @@ static void PrivateAutoComplete(
|
||||
} else {
|
||||
if (frame) {
|
||||
const bool get_file_globals = true;
|
||||
const bool include_synthetic_vars = true;
|
||||
|
||||
VariableList *variable_list = frame->GetVariableList(get_file_globals,
|
||||
nullptr);
|
||||
VariableList *variable_list = frame->GetVariableList(
|
||||
get_file_globals, include_synthetic_vars, nullptr);
|
||||
|
||||
if (variable_list) {
|
||||
for (const VariableSP &var_sp : *variable_list)
|
||||
@@ -686,9 +687,10 @@ static void PrivateAutoComplete(
|
||||
} else if (frame) {
|
||||
// We haven't found our variable yet
|
||||
const bool get_file_globals = true;
|
||||
const bool include_synthetic_vars = true;
|
||||
|
||||
VariableList *variable_list =
|
||||
frame->GetVariableList(get_file_globals, nullptr);
|
||||
VariableList *variable_list = frame->GetVariableList(
|
||||
get_file_globals, include_synthetic_vars, nullptr);
|
||||
|
||||
if (!variable_list)
|
||||
break;
|
||||
|
||||
@@ -86,15 +86,18 @@ RegisterContextSP BorrowedStackFrame::GetRegisterContext() {
|
||||
}
|
||||
|
||||
VariableList *BorrowedStackFrame::GetVariableList(bool get_file_globals,
|
||||
bool include_synthetic_vars,
|
||||
Status *error_ptr) {
|
||||
return m_borrowed_frame_sp->GetVariableList(get_file_globals, error_ptr);
|
||||
return m_borrowed_frame_sp->GetVariableList(
|
||||
get_file_globals, include_synthetic_vars, error_ptr);
|
||||
}
|
||||
|
||||
VariableListSP
|
||||
BorrowedStackFrame::GetInScopeVariableList(bool get_file_globals,
|
||||
bool include_synthetic_vars,
|
||||
bool must_have_valid_location) {
|
||||
return m_borrowed_frame_sp->GetInScopeVariableList(get_file_globals,
|
||||
must_have_valid_location);
|
||||
return m_borrowed_frame_sp->GetInScopeVariableList(
|
||||
get_file_globals, include_synthetic_vars, must_have_valid_location);
|
||||
}
|
||||
|
||||
ValueObjectSP BorrowedStackFrame::GetValueForVariableExpressionPath(
|
||||
|
||||
@@ -439,7 +439,11 @@ StackFrame::GetSymbolContext(SymbolContextItem resolve_scope) {
|
||||
}
|
||||
|
||||
VariableList *StackFrame::GetVariableList(bool get_file_globals,
|
||||
bool include_synthetic_vars,
|
||||
Status *error_ptr) {
|
||||
// We don't have 'synthetic variables' in the base stack frame.
|
||||
(void)include_synthetic_vars;
|
||||
|
||||
std::lock_guard<std::recursive_mutex> guard(m_mutex);
|
||||
if (m_flags.IsClear(RESOLVED_VARIABLES)) {
|
||||
m_flags.Set(RESOLVED_VARIABLES);
|
||||
@@ -490,7 +494,11 @@ VariableList *StackFrame::GetVariableList(bool get_file_globals,
|
||||
|
||||
VariableListSP
|
||||
StackFrame::GetInScopeVariableList(bool get_file_globals,
|
||||
bool include_synthetic_vars,
|
||||
bool must_have_valid_location) {
|
||||
// We don't have synthetic variables in the base stack frame.
|
||||
(void)include_synthetic_vars;
|
||||
|
||||
std::lock_guard<std::recursive_mutex> guard(m_mutex);
|
||||
// We can't fetch variable information for a history stack frame.
|
||||
if (IsHistorical())
|
||||
@@ -1227,7 +1235,8 @@ StackFrame::GetValueObjectForFrameVariable(const VariableSP &variable_sp,
|
||||
if (IsHistorical()) {
|
||||
return valobj_sp;
|
||||
}
|
||||
VariableList *var_list = GetVariableList(true, nullptr);
|
||||
VariableList *var_list = GetVariableList(
|
||||
/*get_file_globals=*/true, /*include_synthetic_vars=*/true, nullptr);
|
||||
if (var_list) {
|
||||
// Make sure the variable is a frame variable
|
||||
const uint32_t var_idx =
|
||||
@@ -1848,7 +1857,12 @@ lldb::ValueObjectSP StackFrame::GuessValueForRegisterAndOffset(ConstString reg,
|
||||
}
|
||||
|
||||
const bool get_file_globals = false;
|
||||
VariableList *variables = GetVariableList(get_file_globals, nullptr);
|
||||
// Keep this as 'false' here because if we're inspecting a register, it's
|
||||
// HIGHLY unlikely that we have an synthetic variable. Indeed, since we're not
|
||||
// in a synthetic frame, it's probably actually impossible here.
|
||||
const bool include_synthetic_vars = false;
|
||||
VariableList *variables =
|
||||
GetVariableList(get_file_globals, include_synthetic_vars, nullptr);
|
||||
|
||||
if (!variables) {
|
||||
return ValueObjectSP();
|
||||
|
||||
@@ -819,19 +819,38 @@ class ScriptedFrameProviderTestCase(TestBase):
|
||||
# Check that we can get variables from this frame.
|
||||
frame0 = thread.GetFrameAtIndex(0)
|
||||
self.assertIsNotNone(frame0)
|
||||
# Get every variable visible at this point
|
||||
variables = frame0.GetVariables(True, True, True, False)
|
||||
|
||||
# Ensure that we can get synthetic variables with `SetIncludeSynthetic`.
|
||||
options = lldb.SBVariablesOptions()
|
||||
options.SetIncludeSynthetic(True)
|
||||
variables = frame0.GetVariables(options)
|
||||
self.assertTrue(variables.IsValid())
|
||||
self.assertEqual(variables.GetSize(), 1)
|
||||
self.assertTrue(variables.GetValueAtIndex(0).name == "_handler_one")
|
||||
|
||||
# Check the `frame variable` command(s) handle synthetic variables the
|
||||
# way we expect by printing them.
|
||||
self.expect("frame var", substrs=["variable_in_main", "_handler_one"])
|
||||
|
||||
# Then, try and run it without synthetic variables and ensure we don't
|
||||
# get any, but we still get the others.
|
||||
interp = self.dbg.GetCommandInterpreter()
|
||||
command_result = lldb.SBCommandReturnObject()
|
||||
result = interp.HandleCommand("frame var -e", command_result)
|
||||
self.assertEqual(
|
||||
result, lldb.eReturnStatusSuccessFinishResult, "frame var -e didn't succeed"
|
||||
)
|
||||
output = command_result.GetOutput()
|
||||
self.assertIn("variable_in_main", output, "Didn't find a regular variable")
|
||||
self.assertNotIn("_handler_one", output, "Found an synthetic variable")
|
||||
|
||||
# Check that we can get values from paths. `_handler_one` is a special
|
||||
# value we provide through only our expression handler in the frame
|
||||
# implementation.
|
||||
# implementation. We can't evaluate expressions on the special value
|
||||
# just because the test implementation doesn't handle it, and we
|
||||
# delegate all expression handling to the implementation.
|
||||
one = frame0.GetValueForVariablePath("_handler_one")
|
||||
self.assertEqual(one.unsigned, 1)
|
||||
var = frame0.GetValueForVariablePath("variable_in_main")
|
||||
# The names won't necessarily match, but the values should (the frame renames the SBValue)
|
||||
self.assertEqual(var.unsigned, variables.GetValueAtIndex(0).unsigned)
|
||||
# Ensure I can still access and do arithmetic on regular variables.
|
||||
varp1 = frame0.GetValueForVariablePath("variable_in_main + 1")
|
||||
self.assertEqual(varp1.unsigned, 124)
|
||||
|
||||
@@ -1259,4 +1278,4 @@ class ScriptedFrameProviderTestCase(TestBase):
|
||||
frame1 = thread.GetFrameAtIndex(1)
|
||||
|
||||
self.assertEqual(frame0.GetFunctionName(), "compute_fibonacci")
|
||||
self.assertEqual(frame1.GetFunctionName(), "process_data")
|
||||
self.assertEqual(frame1.GetFunctionName(), "process_data")
|
||||
|
||||
@@ -559,6 +559,10 @@ class ValueProvidingFrame(ScriptedFrame):
|
||||
""""""
|
||||
out = lldb.SBValueList()
|
||||
out.Append(self.variable)
|
||||
# Produce a fake value to be displayed.
|
||||
out.Append(
|
||||
self.variable.CreateValueFromExpression("_handler_one", "(uint32_t)1")
|
||||
)
|
||||
return out
|
||||
|
||||
def get_value_for_variable_expression(self, expr, options, error: lldb.SBError):
|
||||
|
||||
Reference in New Issue
Block a user