Skip to content

Commit 99e4f6a

Browse files
authored
[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".
1 parent e7b1037 commit 99e4f6a

13 files changed

Lines changed: 217 additions & 56 deletions

File tree

lldb/include/lldb/Target/BorrowedStackFrame.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,12 @@ class BorrowedStackFrame : public StackFrame {
7878
lldb::RegisterContextSP GetRegisterContext() override;
7979

8080
VariableList *GetVariableList(bool get_file_globals,
81+
bool include_synthetic_vars,
8182
Status *error_ptr) override;
8283

8384
lldb::VariableListSP
8485
GetInScopeVariableList(bool get_file_globals,
86+
bool include_synthetic_vars = true,
8587
bool must_have_valid_location = false) override;
8688

8789
lldb::ValueObjectSP GetValueForVariableExpressionPath(

lldb/include/lldb/Target/StackFrame.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,12 @@ class StackFrame : public ExecutionContextScope,
264264
/// that are visible to the entire compilation unit (e.g. file
265265
/// static in C, globals that are homed in this CU).
266266
///
267+
/// \param[in] include_synthetic_vars
268+
/// Whether to also include synthetic variables from other
269+
/// sources. For example, synthetic frames can produce
270+
/// variables that aren't strictly 'variables', but can still
271+
/// be displayed with their values.
272+
///
267273
/// \param [out] error_ptr
268274
/// If there is an error in the debug information that prevents variables
269275
/// from being fetched. \see SymbolFile::GetFrameVariableError() for full
@@ -272,6 +278,7 @@ class StackFrame : public ExecutionContextScope,
272278
/// \return
273279
/// A pointer to a list of variables.
274280
virtual VariableList *GetVariableList(bool get_file_globals,
281+
bool include_synthetic_vars,
275282
Status *error_ptr);
276283

277284
/// Retrieve the list of variables that are in scope at this StackFrame's
@@ -286,13 +293,22 @@ class StackFrame : public ExecutionContextScope,
286293
/// that are visible to the entire compilation unit (e.g. file
287294
/// static in C, globals that are homed in this CU).
288295
///
296+
/// \param[in] include_synthetic_vars
297+
/// Whether to also include synthetic variables from other
298+
/// sources. For example, synthetic frames can produce
299+
/// variables that aren't strictly 'variables', but can still
300+
/// be displayed with their values. Defaults to `true` because
301+
/// we are assuming that if a user's context has synthetic variables,
302+
/// they want them shown.
303+
///
289304
/// \param[in] must_have_valid_location
290305
/// Whether to filter variables whose location is not available at this
291306
/// StackFrame's pc.
292307
/// \return
293308
/// A pointer to a list of variables.
294309
virtual lldb::VariableListSP
295310
GetInScopeVariableList(bool get_file_globals,
311+
bool include_synthetic_vars = true,
296312
bool must_have_valid_location = false);
297313

298314
/// Create a ValueObject for a variable name / pathname, possibly including

lldb/source/API/SBFrame.cpp

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
#include "lldb/API/SBFrame.h"
1414

15+
#include "lldb/Utility/ValueType.h"
16+
#include "lldb/lldb-enumerations.h"
1517
#include "lldb/lldb-types.h"
1618

1719
#include "Utils.h"
@@ -500,7 +502,11 @@ SBValue SBFrame::FindValue(const char *name, ValueType value_type,
500502

501503
VariableList variable_list;
502504

503-
switch (value_type) {
505+
bool include_synthetic_vars = IsSyntheticValueType(value_type);
506+
// Switch on the value_type without the mask, but keep it in the value type so
507+
// we can use it later when we look for variables in the list.
508+
auto base_value_type = GetBaseValueType(value_type);
509+
switch (base_value_type) {
504510
case eValueTypeVariableGlobal: // global variable
505511
case eValueTypeVariableStatic: // static variable
506512
case eValueTypeVariableArgument: // function argument variables
@@ -516,14 +522,17 @@ SBValue SBFrame::FindValue(const char *name, ValueType value_type,
516522
sc.block->AppendVariables(
517523
can_create, get_parent_variables, stop_if_block_is_inlined_function,
518524
[frame](Variable *v) { return v->IsInScope(frame); }, &variable_list);
519-
if (value_type == eValueTypeVariableGlobal ||
520-
value_type == eValueTypeVariableStatic) {
525+
// Fetch variables from the frame if we need to get
526+
// globals/statics/synthetic variables.
527+
if (base_value_type == eValueTypeVariableGlobal ||
528+
base_value_type == eValueTypeVariableStatic || include_synthetic_vars) {
521529
const bool get_file_globals = true;
522-
VariableList *frame_vars =
523-
frame->GetVariableList(get_file_globals, nullptr);
530+
VariableList *frame_vars = frame->GetVariableList(
531+
get_file_globals, include_synthetic_vars, nullptr);
524532
if (frame_vars)
525533
frame_vars->AppendVariablesIfUnique(variable_list);
526534
}
535+
527536
ConstString const_name(name);
528537
VariableSP variable_sp(variable_list.FindVariable(const_name, value_type));
529538
if (variable_sp) {
@@ -688,8 +697,21 @@ lldb::SBValueList SBFrame::GetVariables(bool arguments, bool locals,
688697

689698
/// Returns true if the variable is in any of the requested scopes.
690699
static bool IsInRequestedScope(bool statics, bool arguments, bool locals,
691-
Variable &var) {
692-
switch (var.GetScope()) {
700+
bool synthetic, Variable &var) {
701+
auto value_type = var.GetScope();
702+
// Check if the variable is synthetic first.
703+
bool is_synthetic = IsSyntheticValueType(value_type);
704+
if (is_synthetic) {
705+
// If the variable is synthetic but we don't want those, then it's
706+
// automatically out of scope.
707+
if (!synthetic)
708+
return false;
709+
710+
// Get the base value type so the rest of the switch works correctly.
711+
value_type = GetBaseValueType(value_type);
712+
}
713+
714+
switch (value_type) {
693715
case eValueTypeVariableGlobal:
694716
case eValueTypeVariableStatic:
695717
case eValueTypeVariableThreadLocal:
@@ -704,7 +726,13 @@ static bool IsInRequestedScope(bool statics, bool arguments, bool locals,
704726
default:
705727
break;
706728
}
707-
return false;
729+
730+
// The default for all other value types is is_synthetic. At this point, if
731+
// we didn't want synthetic variables we'd have exited by now anyway, so we
732+
// must want them. Aside from the modifiers above that should apply equally to
733+
// synthetic and normal variables, any other synthetic variable we should
734+
// default to showing.
735+
return is_synthetic;
708736
}
709737

710738
enum WasInterrupted { Yes, No };
@@ -720,13 +748,16 @@ static std::pair<WasInterrupted, Status> FetchVariablesUnlessInterrupted(
720748
const bool statics = options.GetIncludeStatics();
721749
const bool arguments = options.GetIncludeArguments();
722750
const bool locals = options.GetIncludeLocals();
751+
const bool synthetic = options.GetIncludeSynthetic();
723752
const bool in_scope_only = options.GetInScopeOnly();
724753
const bool include_runtime_support_values =
725754
options.GetIncludeRuntimeSupportValues();
726755
const lldb::DynamicValueType use_dynamic = options.GetUseDynamic();
727756

728757
Status var_error;
729-
VariableList *variable_list = frame.GetVariableList(true, &var_error);
758+
// Fetch all variables available and filter them later.
759+
VariableList *variable_list = frame.GetVariableList(
760+
/*get_file_globals=*/true, /*include_synthetic_vars=*/true, &var_error);
730761

731762
std::set<VariableSP> variable_set;
732763

@@ -735,8 +766,8 @@ static std::pair<WasInterrupted, Status> FetchVariablesUnlessInterrupted(
735766
const size_t num_variables = variable_list->GetSize();
736767
size_t num_produced = 0;
737768
for (const VariableSP &variable_sp : *variable_list) {
738-
if (!variable_sp ||
739-
!IsInRequestedScope(statics, arguments, locals, *variable_sp))
769+
if (!variable_sp || !IsInRequestedScope(statics, arguments, locals,
770+
synthetic, *variable_sp))
740771
continue;
741772

742773
if (INTERRUPT_REQUESTED(

lldb/source/Commands/CommandObjectFrame.cpp

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@
2929
#include "lldb/Target/Target.h"
3030
#include "lldb/Target/Thread.h"
3131
#include "lldb/Utility/Args.h"
32+
#include "lldb/Utility/ValueType.h"
3233
#include "lldb/ValueObject/ValueObject.h"
34+
#include "lldb/lldb-enumerations.h"
3335

3436
#include <memory>
3537
#include <optional>
@@ -337,9 +339,9 @@ class CommandObjectFrameSelect : public CommandObjectParsed {
337339
// The request went past the stack, so handle that case:
338340
const uint32_t num_frames = thread->GetStackFrameCount();
339341
if (static_cast<int32_t>(num_frames - frame_idx) >
340-
*m_options.relative_frame_offset)
341-
frame_idx += *m_options.relative_frame_offset;
342-
else {
342+
*m_options.relative_frame_offset) {
343+
frame_idx += *m_options.relative_frame_offset;
344+
} else {
343345
if (frame_idx == num_frames - 1) {
344346
// If we are already at the top of the stack, just warn and don't
345347
// reset the frame.
@@ -439,17 +441,23 @@ may even involve JITing and running code in the target program.)");
439441
if (!var_sp)
440442
return llvm::StringRef();
441443

442-
switch (var_sp->GetScope()) {
444+
auto vt = var_sp->GetScope();
445+
bool is_synthetic = IsSyntheticValueType(vt);
446+
// Clear the bit so the rest works correctly.
447+
if (is_synthetic)
448+
vt = GetBaseValueType(vt);
449+
450+
switch (vt) {
443451
case eValueTypeVariableGlobal:
444-
return "GLOBAL: ";
452+
return is_synthetic ? "(synthetic) GLOBAL: " : "GLOBAL: ";
445453
case eValueTypeVariableStatic:
446-
return "STATIC: ";
454+
return is_synthetic ? "(synthetic) STATIC: " : "STATIC: ";
447455
case eValueTypeVariableArgument:
448-
return "ARG: ";
456+
return is_synthetic ? "(synthetic) ARG: " : "ARG: ";
449457
case eValueTypeVariableLocal:
450-
return "LOCAL: ";
458+
return is_synthetic ? "(synthetic) LOCAL: " : "LOCAL: ";
451459
case eValueTypeVariableThreadLocal:
452-
return "THREAD: ";
460+
return is_synthetic ? "(synthetic) THREAD: " : "THREAD: ";
453461
default:
454462
break;
455463
}
@@ -459,6 +467,14 @@ may even involve JITing and running code in the target program.)");
459467

460468
/// Returns true if `scope` matches any of the options in `m_option_variable`.
461469
bool ScopeRequested(lldb::ValueType scope) {
470+
// If it's a synthetic variable, check if we want to show those first.
471+
bool is_synthetic = IsSyntheticValueType(scope);
472+
if (is_synthetic) {
473+
if (!m_option_variable.show_synthetic)
474+
return false;
475+
476+
scope = GetBaseValueType(scope);
477+
}
462478
switch (scope) {
463479
case eValueTypeVariableGlobal:
464480
case eValueTypeVariableStatic:
@@ -474,7 +490,10 @@ may even involve JITing and running code in the target program.)");
474490
case eValueTypeVariableThreadLocal:
475491
case eValueTypeVTable:
476492
case eValueTypeVTableEntry:
477-
return false;
493+
// The default for all other value types is is_synthetic. Aside from the
494+
// modifiers above that should apply equally to synthetic and normal
495+
// variables, any other synthetic variable we should default to showing.
496+
return is_synthetic;
478497
}
479498
llvm_unreachable("Unexpected scope value");
480499
}
@@ -521,7 +540,8 @@ may even involve JITing and running code in the target program.)");
521540

522541
Status error;
523542
VariableList *variable_list =
524-
frame->GetVariableList(m_option_variable.show_globals, &error);
543+
frame->GetVariableList(m_option_variable.show_globals,
544+
m_option_variable.show_synthetic, &error);
525545

526546
if (error.Fail() && (!variable_list || variable_list->GetSize() == 0)) {
527547
result.AppendError(error.AsCString());

lldb/source/Core/IOHandlerCursesGUI.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5934,7 +5934,9 @@ class FrameVariablesWindowDelegate : public ValueObjectListDelegate {
59345934
if (m_frame_block != frame_block) {
59355935
m_frame_block = frame_block;
59365936

5937-
VariableList *locals = frame->GetVariableList(true, nullptr);
5937+
VariableList *locals = frame->GetVariableList(
5938+
/*get_file_globals=*/true, /*include_synthetic_vars=*/true,
5939+
nullptr);
59385940
if (locals) {
59395941
const DynamicValueType use_dynamic = eDynamicDontRunTarget;
59405942
for (const VariableSP &local_sp : *locals) {

lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -876,8 +876,11 @@ void ClangExpressionDeclMap::LookUpLldbClass(NameSearchContext &context) {
876876
// creates decls for function templates by attaching them to the TU instead
877877
// of a class context. So we can actually have template methods scoped
878878
// outside of a class. Once we fix that, we can remove this code-path.
879-
880-
VariableList *vars = frame->GetVariableList(false, nullptr);
879+
// Additionally, we exclude synthetic variables from here. Clang-based
880+
// languages are unlikely candidates for synthetic variables anyway, and
881+
// especially in this case, we're looking for something specific to C++.
882+
VariableList *vars = frame->GetVariableList(
883+
/*get_file_globals=*/false, /*include_synthetic_vars=*/false, nullptr);
881884

882885
lldb::VariableSP this_var = vars->FindVariable(ConstString("this"));
883886

@@ -963,7 +966,11 @@ void ClangExpressionDeclMap::LookUpLldbObjCClass(NameSearchContext &context) {
963966
// In that case, just look up the "self" variable in the current scope
964967
// and use its type.
965968

966-
VariableList *vars = frame->GetVariableList(false, nullptr);
969+
// We exclude synthetic variables from here. Like above, it's highly unlikely
970+
// we care about synthetic variables here, and indeed this code is looking for
971+
// an obj-C specific construct.
972+
VariableList *vars = frame->GetVariableList(
973+
/*get_file_globals=*/false, /*include_synthetic_vars=*/false, nullptr);
967974

968975
lldb::VariableSP self_var = vars->FindVariable(ConstString("self"));
969976

lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include "lldb/Core/Debugger.h"
1414
#include "lldb/Core/Module.h"
1515
#include "lldb/Core/ModuleList.h"
16+
#include "lldb/Expression/DWARFExpressionList.h"
17+
#include "lldb/Host/FileSystem.h"
1618
#include "lldb/Interpreter/Interfaces/ScriptedFrameInterface.h"
1719
#include "lldb/Interpreter/Interfaces/ScriptedInterface.h"
1820
#include "lldb/Interpreter/Interfaces/ScriptedThreadInterface.h"
@@ -28,8 +30,14 @@
2830
#include "lldb/Utility/LLDBLog.h"
2931
#include "lldb/Utility/Log.h"
3032
#include "lldb/Utility/StructuredData.h"
33+
#include "lldb/Utility/ValueType.h"
3134
#include "lldb/ValueObject/ValueObject.h"
3235
#include "lldb/ValueObject/ValueObjectList.h"
36+
#include "lldb/lldb-enumerations.h"
37+
#include "lldb/lldb-forward.h"
38+
#include "llvm/Support/ErrorHandling.h"
39+
40+
#include <memory>
3341

3442
using namespace lldb;
3543
using namespace lldb_private;
@@ -269,19 +277,22 @@ lldb::RegisterContextSP ScriptedFrame::GetRegisterContext() {
269277
}
270278

271279
VariableList *ScriptedFrame::GetVariableList(bool get_file_globals,
280+
bool include_synthetic_vars,
272281
Status *error_ptr) {
273-
PopulateVariableListFromInterface();
282+
PopulateVariableListFromInterface(include_synthetic_vars);
274283
return m_variable_list_sp.get();
275284
}
276285

277286
lldb::VariableListSP
278287
ScriptedFrame::GetInScopeVariableList(bool get_file_globals,
288+
bool include_synthetic_vars,
279289
bool must_have_valid_location) {
280-
PopulateVariableListFromInterface();
290+
PopulateVariableListFromInterface(include_synthetic_vars);
281291
return m_variable_list_sp;
282292
}
283293

284-
void ScriptedFrame::PopulateVariableListFromInterface() {
294+
void ScriptedFrame::PopulateVariableListFromInterface(
295+
bool include_synthetic_vars) {
285296
// Fetch values from the interface.
286297
ValueObjectListSP value_list_sp = GetInterface()->GetVariables();
287298
if (!value_list_sp)
@@ -295,12 +306,28 @@ void ScriptedFrame::PopulateVariableListFromInterface() {
295306
continue;
296307

297308
VariableSP var = v->GetVariable();
298-
// TODO: We could in theory ask the scripted frame to *produce* a
299-
// variable for this value object.
300-
if (!var)
301-
continue;
309+
if (!var && include_synthetic_vars) {
310+
// Construct the value type as an synthetic verison of what the value type
311+
// is. That'll allow the user to tell the scope and the 'synthetic-ness'
312+
// of the variable.
313+
lldb::ValueType vt = GetSyntheticValueType(v->GetValueType());
314+
315+
// Just make up a variable - the frame variable dumper just passes it
316+
// back in to GetValueObjectForFrameVariable, so we really just need to
317+
// make sure the name and type are correct. We create IDs based on
318+
// value_list_sp in order to make sure they're unique.
319+
var = std::make_shared<lldb_private::Variable>(
320+
(lldb::user_id_t)value_list_sp->GetSize() + i,
321+
v->GetName().GetCString(), v->GetName().GetCString(), nullptr, vt,
322+
/*owner_scope=*/nullptr,
323+
/*scope_range=*/Variable::RangeList{},
324+
/*decl=*/nullptr, DWARFExpressionList{}, /*external=*/false,
325+
/*artificial=*/true, /*location_is_constant_data=*/false);
326+
}
302327

303-
m_variable_list_sp->AddVariable(var);
328+
// Only append the variable if we have one (had already, or just created).
329+
if (var)
330+
m_variable_list_sp->AddVariable(var);
304331
}
305332
}
306333

@@ -315,6 +342,15 @@ lldb::ValueObjectSP ScriptedFrame::GetValueObjectForFrameVariable(
315342
variable_sp->GetName().AsCString(nullptr));
316343
}
317344

345+
lldb::ValueObjectSP ScriptedFrame::FindVariable(ConstString name) {
346+
// Fetch values from the interface.
347+
ValueObjectListSP values = m_scripted_frame_interface_sp->GetVariables();
348+
if (!values)
349+
return {};
350+
351+
return values->FindValueObjectByValueName(name.AsCString(nullptr));
352+
}
353+
318354
lldb::ValueObjectSP ScriptedFrame::GetValueForVariableExpressionPath(
319355
llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
320356
uint32_t options, lldb::VariableSP &var_sp, Status &error,

0 commit comments

Comments
 (0)