Skip to content

[LLDB] Add framework for Data Inspection Language (DIL) work. #115666

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Nov 14, 2024

Conversation

cmtice
Copy link
Contributor

@cmtice cmtice commented Nov 10, 2024

Add the framework code for hooking up and calling the Data Inspection Language (DIL) implementation, as an alternate implementation for the 'frame variable' command. For now, this is an opt-in option, via a target setting 'target.experimental.use-DIL'. See
https://discourse.llvm.org/t/rfc-data-inspection-language/69893 for more information about this project.

This PR does not actually call any of the DIL code; instead the piece that will eventually call the DIL code (StackFrame::DILEvaluateVariableExpression) calls back into the original 'frame variable' implementation.

Add the framework code for hooking up and calling the Data Inspection Language
(DIL) implementation, as an alternate implementation for the 'frame
variable' command. For now, this is an opt-in option, via a target setting
'target.experimental.use-DIL'.  See
https://discourse.llvm.org/t/rfc-data-inspection-language/69893 for more
information about this project.

This PR does not actually call any of the DIL code; instead the piece that will
eventually call the DIL code (StackFrame::DILEvaluateVariableExpression) calls
back into the original 'frame variable' implementation.
@cmtice cmtice requested a review from JDevlieghere as a code owner November 10, 2024 18:17
@llvmbot llvmbot added the lldb label Nov 10, 2024
@cmtice cmtice requested a review from labath November 10, 2024 18:17
@llvmbot
Copy link
Member

llvmbot commented Nov 10, 2024

@llvm/pr-subscribers-lldb

Author: None (cmtice)

Changes

Add the framework code for hooking up and calling the Data Inspection Language (DIL) implementation, as an alternate implementation for the 'frame variable' command. For now, this is an opt-in option, via a target setting 'target.experimental.use-DIL'. See
https://discourse.llvm.org/t/rfc-data-inspection-language/69893 for more information about this project.

This PR does not actually call any of the DIL code; instead the piece that will eventually call the DIL code (StackFrame::DILEvaluateVariableExpression) calls back into the original 'frame variable' implementation.


Full diff: https://github.com/llvm/llvm-project/pull/115666.diff

10 Files Affected:

  • (modified) lldb/include/lldb/Target/StackFrame.h (+29)
  • (modified) lldb/include/lldb/Target/Target.h (+4)
  • (modified) lldb/source/API/SBFrame.cpp (+17-5)
  • (modified) lldb/source/Commands/CommandObjectFrame.cpp (+15-4)
  • (modified) lldb/source/Commands/CommandObjectWatchpoint.cpp (+11-3)
  • (modified) lldb/source/Expression/UserExpression.cpp (+22-7)
  • (modified) lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.cpp (+13-2)
  • (modified) lldb/source/Target/StackFrame.cpp (+9)
  • (modified) lldb/source/Target/Target.cpp (+21)
  • (modified) lldb/source/Target/TargetProperties.td (+3)
diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h
index 3881137583b941..03128447b5d496 100644
--- a/lldb/include/lldb/Target/StackFrame.h
+++ b/lldb/include/lldb/Target/StackFrame.h
@@ -308,6 +308,35 @@ class StackFrame : public ExecutionContextScope,
       llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
       uint32_t options, lldb::VariableSP &var_sp, Status &error);
 
+  /// Create a ValueObject for a variable name / pathname, possibly including
+  /// simple dereference/child selection syntax.
+  ///
+  /// \param[in] var_expr
+  ///     The string specifying a variable to base the VariableObject off
+  ///     of.
+  ///
+  /// \param[in] use_dynamic
+  ///     Whether the correct dynamic type of an object pointer should be
+  ///     determined before creating the object, or if the static type is
+  ///     sufficient.  One of the DynamicValueType enumerated values.
+  ///
+  /// \param[in] options
+  ///     An unsigned integer of flags, values from
+  ///     StackFrame::ExpressionPathOption
+  ///     enum.
+  /// \param[in] var_sp
+  ///     A VariableSP that will be set to the variable described in the
+  ///     var_expr path.
+  ///
+  /// \param[in] error
+  ///     Record any errors encountered while evaluating var_expr.
+  ///
+  /// \return
+  ///     A shared pointer to the ValueObject described by var_expr.
+  lldb::ValueObjectSP DILEvaluateVariableExpression(
+      llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
+      uint32_t options, lldb::VariableSP &var_sp, Status &error);
+
   /// Determine whether this StackFrame has debug information available or not.
   ///
   /// \return
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index cab21c29a7486f..b5becfb0e4fe17 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -252,6 +252,10 @@ class TargetProperties : public Properties {
 
   bool GetInjectLocalVariables(ExecutionContext *exe_ctx) const;
 
+  bool GetUseDIL(ExecutionContext *exe_ctx) const;
+
+  void SetUseDIL(ExecutionContext *exe_ctx, bool b);
+
   void SetRequireHardwareBreakpoints(bool b);
 
   bool GetRequireHardwareBreakpoints() const;
diff --git a/lldb/source/API/SBFrame.cpp b/lldb/source/API/SBFrame.cpp
index dc41e80b457d7d..4e3e47b7a5f60b 100644
--- a/lldb/source/API/SBFrame.cpp
+++ b/lldb/source/API/SBFrame.cpp
@@ -472,6 +472,7 @@ lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path,
   StackFrame *frame = nullptr;
   Target *target = exe_ctx.GetTargetPtr();
   Process *process = exe_ctx.GetProcessPtr();
+  bool use_DIL = target->GetUseDIL(&exe_ctx);
   if (target && process) {
     Process::StopLocker stop_locker;
     if (stop_locker.TryLock(&process->GetRunLock())) {
@@ -479,11 +480,22 @@ lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path,
       if (frame) {
         VariableSP var_sp;
         Status error;
-        ValueObjectSP value_sp(frame->GetValueForVariableExpressionPath(
-            var_path, eNoDynamicValues,
-            StackFrame::eExpressionPathOptionCheckPtrVsMember |
-                StackFrame::eExpressionPathOptionsAllowDirectIVarAccess,
-            var_sp, error));
+        ValueObjectSP value_sp;
+        if (use_DIL) {
+          // Use DIL parser/evaluator.
+          value_sp = frame->DILEvaluateVariableExpression(
+              var_path, eNoDynamicValues,
+              StackFrame::eExpressionPathOptionCheckPtrVsMember |
+                  StackFrame::eExpressionPathOptionsAllowDirectIVarAccess,
+              var_sp, error);
+        } else {
+          // Use original frame function.
+          value_sp = frame->GetValueForVariableExpressionPath(
+              var_path, eNoDynamicValues,
+              StackFrame::eExpressionPathOptionCheckPtrVsMember |
+                  StackFrame::eExpressionPathOptionsAllowDirectIVarAccess,
+              var_sp, error);
+        }
         sb_value.SetSP(value_sp, use_dynamic);
       }
     }
diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp
index a5709b36f52ee2..7e0651657482bb 100644
--- a/lldb/source/Commands/CommandObjectFrame.cpp
+++ b/lldb/source/Commands/CommandObjectFrame.cpp
@@ -517,7 +517,10 @@ may even involve JITing and running code in the target program.)");
       result.AppendError(error.AsCString());
 
     }
+    VariableSP var_sp;
     ValueObjectSP valobj_sp;
+    TargetSP target_sp = frame->CalculateTarget();
+    bool use_DIL = target_sp->GetUseDIL(&m_exe_ctx);
 
     TypeSummaryImplSP summary_format_sp;
     if (!m_option_variable.summary.IsCurrentValueEmpty())
@@ -600,9 +603,17 @@ may even involve JITing and running code in the target program.)");
                 StackFrame::eExpressionPathOptionsAllowDirectIVarAccess |
                 StackFrame::eExpressionPathOptionsInspectAnonymousUnions;
             lldb::VariableSP var_sp;
-            valobj_sp = frame->GetValueForVariableExpressionPath(
-                entry.ref(), m_varobj_options.use_dynamic, expr_path_options,
-                var_sp, error);
+            if (use_DIL) {
+              // Use the DIL parser/evaluator.
+              valobj_sp = frame->DILEvaluateVariableExpression(
+                  entry.ref(), m_varobj_options.use_dynamic, expr_path_options,
+                  var_sp, error);
+            } else {
+              // Original 'frame variable' execution path
+              valobj_sp = frame->GetValueForVariableExpressionPath(
+                  entry.ref(), m_varobj_options.use_dynamic, expr_path_options,
+                  var_sp, error);
+            }
             if (valobj_sp) {
               std::string scope_string;
               if (m_option_variable.show_scope)
@@ -641,7 +652,7 @@ may even involve JITing and running code in the target program.)");
         const size_t num_variables = variable_list->GetSize();
         if (num_variables > 0) {
           for (size_t i = 0; i < num_variables; i++) {
-            VariableSP var_sp = variable_list->GetVariableAtIndex(i);
+            var_sp = variable_list->GetVariableAtIndex(i);
             if (!ScopeRequested(var_sp->GetScope()))
                 continue;
             std::string scope_string;
diff --git a/lldb/source/Commands/CommandObjectWatchpoint.cpp b/lldb/source/Commands/CommandObjectWatchpoint.cpp
index 766d650a2ca070..6eaefcef4398f4 100644
--- a/lldb/source/Commands/CommandObjectWatchpoint.cpp
+++ b/lldb/source/Commands/CommandObjectWatchpoint.cpp
@@ -806,6 +806,7 @@ corresponding to the byte size of the data type.");
   void DoExecute(Args &command, CommandReturnObject &result) override {
     Target &target = GetTarget();
     StackFrame *frame = m_exe_ctx.GetFramePtr();
+    bool use_DIL = target.GetUseDIL(&m_exe_ctx);
 
     // If no argument is present, issue an error message.  There's no way to
     // set a watchpoint.
@@ -840,9 +841,16 @@ corresponding to the byte size of the data type.");
     uint32_t expr_path_options =
         StackFrame::eExpressionPathOptionCheckPtrVsMember |
         StackFrame::eExpressionPathOptionsAllowDirectIVarAccess;
-    valobj_sp = frame->GetValueForVariableExpressionPath(
-        command.GetArgumentAtIndex(0), eNoDynamicValues, expr_path_options,
-        var_sp, error);
+    if (use_DIL)
+      // Use the DIL parser/evaluator.
+      valobj_sp = frame->DILEvaluateVariableExpression(
+          command.GetArgumentAtIndex(0), eNoDynamicValues, expr_path_options,
+          var_sp, error);
+    else
+      // Use the original frame function.
+      valobj_sp = frame->GetValueForVariableExpressionPath(
+          command.GetArgumentAtIndex(0), eNoDynamicValues, expr_path_options,
+          var_sp, error);
 
     if (!valobj_sp) {
       // Not in the frame; let's check the globals.
diff --git a/lldb/source/Expression/UserExpression.cpp b/lldb/source/Expression/UserExpression.cpp
index ed3734cbb943f6..bcfc25ff40b537 100644
--- a/lldb/source/Expression/UserExpression.cpp
+++ b/lldb/source/Expression/UserExpression.cpp
@@ -110,13 +110,28 @@ lldb::ValueObjectSP UserExpression::GetObjectPointerValueObject(
   lldb::VariableSP var_sp;
   lldb::ValueObjectSP valobj_sp;
 
-  return frame_sp->GetValueForVariableExpressionPath(
-      object_name, lldb::eNoDynamicValues,
-      StackFrame::eExpressionPathOptionCheckPtrVsMember |
-          StackFrame::eExpressionPathOptionsNoFragileObjcIvar |
-          StackFrame::eExpressionPathOptionsNoSyntheticChildren |
-          StackFrame::eExpressionPathOptionsNoSyntheticArrayRange,
-      var_sp, err);
+  lldb::TargetSP target_sp = frame_sp->CalculateTarget();
+  ExecutionContext exe_ctx;
+  frame_sp->CalculateExecutionContext(exe_ctx);
+  bool use_DIL = target_sp->GetUseDIL(&exe_ctx);
+
+  if (use_DIL) {
+    return frame_sp->DILEvaluateVariableExpression(
+        object_name, lldb::eNoDynamicValues,
+        StackFrame::eExpressionPathOptionCheckPtrVsMember |
+            StackFrame::eExpressionPathOptionsNoFragileObjcIvar |
+            StackFrame::eExpressionPathOptionsNoSyntheticChildren |
+            StackFrame::eExpressionPathOptionsNoSyntheticArrayRange,
+        var_sp, err);
+  } else {
+    return frame_sp->GetValueForVariableExpressionPath(
+        object_name, lldb::eNoDynamicValues,
+        StackFrame::eExpressionPathOptionCheckPtrVsMember |
+            StackFrame::eExpressionPathOptionsNoFragileObjcIvar |
+            StackFrame::eExpressionPathOptionsNoSyntheticChildren |
+            StackFrame::eExpressionPathOptionsNoSyntheticArrayRange,
+        var_sp, err);
+  }
 }
 
 lldb::addr_t UserExpression::GetObjectPointer(lldb::StackFrameSP frame_sp,
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.cpp
index e53e930665a602..270aa1481994c8 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.cpp
@@ -56,8 +56,19 @@ DWARFASTParser::ParseChildArrayInfo(const DWARFDIE &parent_die,
                 if (auto frame = exe_ctx->GetFrameSP()) {
                   Status error;
                   lldb::VariableSP var_sp;
-                  auto valobj_sp = frame->GetValueForVariableExpressionPath(
-                      var_die.GetName(), eNoDynamicValues, 0, var_sp, error);
+                  lldb::TargetSP target_sp = frame->CalculateTarget();
+                  bool use_DIL = target_sp->GetUseDIL(
+                      (lldb_private::ExecutionContext *)exe_ctx);
+                  lldb::ValueObjectSP valobj_sp;
+                  if (use_DIL) {
+                    // Use the DIL parser/evaluator.
+                    valobj_sp = frame->DILEvaluateVariableExpression(
+                        var_die.GetName(), eNoDynamicValues, 0, var_sp, error);
+                  } else {
+                    // Use the original frame function.
+                    valobj_sp = frame->GetValueForVariableExpressionPath(
+                        var_die.GetName(), eNoDynamicValues, 0, var_sp, error);
+                  }
                   if (valobj_sp) {
                     num_elements = valobj_sp->GetValueAsUnsigned(0);
                     break;
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index 3761b867452c99..83d8cbb440ad9d 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -505,6 +505,15 @@ StackFrame::GetInScopeVariableList(bool get_file_globals,
   return var_list_sp;
 }
 
+ValueObjectSP StackFrame::DILEvaluateVariableExpression(
+    llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
+    uint32_t options, lldb::VariableSP &var_sp, Status &error) {
+  // This is a place-holder for the calls into the DIL parser and
+  // evaluator.  For now, just call the "real" frame variable implementation.
+  return GetValueForVariableExpressionPath(var_expr, use_dynamic, options,
+                                           var_sp, error);
+}
+
 ValueObjectSP StackFrame::GetValueForVariableExpressionPath(
     llvm::StringRef var_expr, DynamicValueType use_dynamic, uint32_t options,
     VariableSP &var_sp, Status &error) {
diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index 242d2eaec2a15a..57ecbbc29bce64 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -4385,6 +4385,27 @@ bool TargetProperties::GetInjectLocalVariables(
       .value_or(true);
 }
 
+bool TargetProperties::GetUseDIL(ExecutionContext *exe_ctx) const {
+  const Property *exp_property =
+      m_collection_sp->GetPropertyAtIndex(ePropertyExperimental, exe_ctx);
+  OptionValueProperties *exp_values =
+      exp_property->GetValue()->GetAsProperties();
+  if (exp_values)
+    return exp_values->GetPropertyAtIndexAs<bool>(ePropertyUseDIL, exe_ctx)
+        .value_or(false);
+  else
+    return true;
+}
+
+void TargetProperties::SetUseDIL(ExecutionContext *exe_ctx, bool b) {
+  const Property *exp_property =
+      m_collection_sp->GetPropertyAtIndex(ePropertyExperimental, exe_ctx);
+  OptionValueProperties *exp_values =
+      exp_property->GetValue()->GetAsProperties();
+  if (exp_values)
+    exp_values->SetPropertyAtIndex(ePropertyUseDIL, true, exe_ctx);
+}
+
 ArchSpec TargetProperties::GetDefaultArchitecture() const {
   const uint32_t idx = ePropertyDefaultArch;
   return GetPropertyAtIndexAs<ArchSpec>(idx, {});
diff --git a/lldb/source/Target/TargetProperties.td b/lldb/source/Target/TargetProperties.td
index 00ad8dd2a9f7f9..8978a4a143b6f7 100644
--- a/lldb/source/Target/TargetProperties.td
+++ b/lldb/source/Target/TargetProperties.td
@@ -4,6 +4,9 @@ let Definition = "target_experimental" in {
   def InjectLocalVars : Property<"inject-local-vars", "Boolean">,
     Global, DefaultTrue,
     Desc<"If true, inject local variables explicitly into the expression text. This will fix symbol resolution when there are name collisions between ivars and local variables. But it can make expressions run much more slowly.">;
+  def UseDIL : Property<"use-DIL", "Boolean">,
+    Global, DefaultFalse,
+    Desc<"If true, use the alternative DIL implementation for frame variable evaluation.">;
 }
 
 let Definition = "target" in {

@jimingham
Copy link
Collaborator

jimingham commented Nov 12, 2024

IIUC, the goal is that In the end, DIL will win and there will only be one VariableExpressionPath interface in lldb. So it seems inefficient for the interim stage to introduce another function and switch between them with UseDIL all over the code. Why not leave GetValueForVariableExpressionPath for now, and move the if (UseDIL()) into that function. They have the same signature, and you'll always be able to find the target from the ValueObject when you are in GetValueForVariableExpressionPath, so that should be possible.

That will make the diff much smaller, and in the end mean that pulling the switch to use only the DIL will be less disruptive.

var_die.GetName(), eNoDynamicValues, 0, var_sp, error);
lldb::TargetSP target_sp = frame->CalculateTarget();
bool use_DIL = target_sp->GetUseDIL(
(lldb_private::ExecutionContext *)exe_ctx);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this cast wouldn't be required if you changed GetExperimentalPropertyValue to take a const execution context (all of the functions below it already take a const argument)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code & cast are gone now.

@labath
Copy link
Collaborator

labath commented Nov 12, 2024

+1 to what Jim said. You could keep GetValueForVariableExpressionPath as a single entry point, but then implement it as something like if(use_dil) LegacyGetValueForVariableExpressionPath() else DILGetValueForVariableExpressionPath()

    Remove redundant calculations of 'use_DIL'. Make
    StackFrame::GetValueForVariableExpressionPath the gating function
    that dispatches to either DILGetValueForVariableExpressionPath or
    LegacyGetValueForVariableExpressionPath.
@cmtice
Copy link
Contributor Author

cmtice commented Nov 13, 2024

I've updated the code as Jim and Pavel suggested. :-)

Comment on lines 307 to 336
lldb::ValueObjectSP LegacyGetValueForVariableExpressionPath(
llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
uint32_t options, lldb::VariableSP &var_sp, Status &error);

/// Create a ValueObject for a variable name / pathname, possibly including
/// simple dereference/child selection syntax.
///
/// \param[in] var_expr
/// The string specifying a variable to base the VariableObject off
/// of.
///
/// \param[in] use_dynamic
/// Whether the correct dynamic type of an object pointer should be
/// determined before creating the object, or if the static type is
/// sufficient. One of the DynamicValueType enumerated values.
///
/// \param[in] options
/// An unsigned integer of flags, values from
/// StackFrame::ExpressionPathOption
/// enum.
/// \param[in] var_sp
/// A VariableSP that will be set to the variable described in the
/// var_expr path.
///
/// \param[in] error
/// Record any errors encountered while evaluating var_expr.
///
/// \return
/// A shared pointer to the ValueObject described by var_expr.
lldb::ValueObjectSP DILGetValueForVariableExpressionPath(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can these be private (they should only be called from the dispatcher function, right)?

(You can attach the doxygen comment to the public function, there's no need to repeat it everywhere)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

if (use_DIL)
return DILGetValueForVariableExpressionPath(var_expr, use_dynamic, options,
var_sp, error);
else
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://llvm.org/docs/CodingStandards.html#don-t-use-else-after-a-return

(I also think the comments in this method don't tell us anything that's not immediately obvious from the code. I'd just remove them.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines 512 to 515
lldb::TargetSP target_sp = CalculateTarget();
ExecutionContext exe_ctx;
CalculateExecutionContext(exe_ctx);
bool use_DIL = target_sp->GetUseDIL(&exe_ctx);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
lldb::TargetSP target_sp = CalculateTarget();
ExecutionContext exe_ctx;
CalculateExecutionContext(exe_ctx);
bool use_DIL = target_sp->GetUseDIL(&exe_ctx);
ExecutionContext exe_ctx;
CalculateExecutionContext(exe_ctx);
bool use_DIL = exe_ctx.GetTargetRef().GetUseDIL(&exe_ctx);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

return exp_values->GetPropertyAtIndexAs<bool>(ePropertyUseDIL, exe_ctx)
.value_or(false);
else
return true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be false as well? Otherwise DIL will be enabled if exp_values is not available (not sure when this happens though).

- Make LegacyGetValueForVariableExpressionPath and
  DILGetValueForVariableExpressionPath private.
- Remove redundant or useless comments.
- Clean up definition of use_DIL slightly.
@cmtice cmtice merged commit 310351d into llvm:main Nov 14, 2024
7 checks passed
@cmtice cmtice deleted the dil-framework branch December 11, 2024 21:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants