[Lldb-commits] [lldb] r371668 - [DWARF] Evaluate DW_OP_entry_value

Vedant Kumar via lldb-commits lldb-commits at lists.llvm.org
Wed Sep 11 14:23:45 PDT 2019


Author: vedantk
Date: Wed Sep 11 14:23:45 2019
New Revision: 371668

URL: http://llvm.org/viewvc/llvm-project?rev=371668&view=rev
Log:
[DWARF] Evaluate DW_OP_entry_value

Add support for evaluating DW_OP_entry_value. This involves parsing
DW_TAG_call_site_parameter and wiring the information through to the expression
evaluator.

rdar://54496008

Differential Revision: https://reviews.llvm.org/D67376

Added:
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/param_entry_vals/
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/Makefile
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/TestBasicEntryValuesX86_64.py
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/main.cpp
Modified:
    lldb/trunk/include/lldb/Symbol/Function.h
    lldb/trunk/packages/Python/lldbsuite/test/decorators.py
    lldb/trunk/source/Expression/DWARFExpression.cpp
    lldb/trunk/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp
    lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
    lldb/trunk/source/Symbol/Function.cpp

Modified: lldb/trunk/include/lldb/Symbol/Function.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Symbol/Function.h?rev=371668&r1=371667&r2=371668&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Symbol/Function.h (original)
+++ lldb/trunk/include/lldb/Symbol/Function.h Wed Sep 11 14:23:45 2019
@@ -246,10 +246,22 @@ private:
 
 class Function;
 
+/// \class CallSiteParameter Function.h "lldb/Symbol/Function.h"
+///
+/// Represent the locations of a parameter at a call site, both in the caller
+/// and in the callee.
+struct CallSiteParameter {
+  DWARFExpression LocationInCallee;
+  DWARFExpression LocationInCaller;
+};
+
+/// A vector of \c CallSiteParameter.
+using CallSiteParameterArray = llvm::SmallVector<CallSiteParameter, 0>;
+
 /// \class CallEdge Function.h "lldb/Symbol/Function.h"
 ///
 /// Represent a call made within a Function. This can be used to find a path
-/// in the call graph between two functions.
+/// in the call graph between two functions, or to evaluate DW_OP_entry_value.
 class CallEdge {
 public:
   /// Construct a call edge using a symbol name to identify the calling
@@ -259,7 +271,8 @@ public:
   /// TODO: A symbol name may not be globally unique. To disambiguate ODR
   /// conflicts, it's necessary to determine the \c Target a call edge is
   /// associated with before resolving it.
-  CallEdge(const char *symbol_name, lldb::addr_t return_pc);
+  CallEdge(const char *symbol_name, lldb::addr_t return_pc,
+           CallSiteParameterArray parameters);
 
   CallEdge(CallEdge &&) = default;
   CallEdge &operator=(CallEdge &&) = default;
@@ -279,6 +292,9 @@ public:
   /// offset.
   lldb::addr_t GetUnresolvedReturnPCAddress() const { return return_pc; }
 
+  /// Get the call site parameters available at this call edge.
+  llvm::ArrayRef<CallSiteParameter> GetCallSiteParameters() const;
+
 private:
   void ParseSymbolFileAndResolve(ModuleList &images);
 
@@ -294,6 +310,8 @@ private:
   /// gives the return PC for the call.
   lldb::addr_t return_pc;
 
+  CallSiteParameterArray parameters;
+
   /// Whether or not an attempt was made to find the callee's definition.
   bool resolved;
 
@@ -569,6 +587,8 @@ protected:
   uint32_t
       m_prologue_byte_size; ///< Compute the prologue size once and cache it
 
+  // TODO: Use a layer of indirection to point to call edges, to save space
+  // when call info hasn't been parsed.
   bool m_call_edges_resolved = false; ///< Whether call site info has been
                                       ///  parsed.
   std::vector<CallEdge> m_call_edges; ///< Outgoing call edges.

Modified: lldb/trunk/packages/Python/lldbsuite/test/decorators.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/decorators.py?rev=371668&r1=371667&r2=371668&view=diff
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/decorators.py (original)
+++ lldb/trunk/packages/Python/lldbsuite/test/decorators.py Wed Sep 11 14:23:45 2019
@@ -641,6 +641,16 @@ def skipUnlessPlatform(oslist):
     return unittest2.skipUnless(lldbplatformutil.getPlatform() in oslist,
                                 "requires one of %s" % (", ".join(oslist)))
 
+def skipUnlessArch(arch):
+    """Decorate the item to skip tests unless running on the specified architecture."""
+
+    def arch_doesnt_match(self):
+        target_arch = self.getArchitecture()
+        if arch != target_arch:
+            return "Test only runs on " + arch + ", but target arch is " + target_arch
+        return None
+
+    return skipTestIfFn(arch_doesnt_match)
 
 def skipIfTargetAndroid(bugnumber=None, api_levels=None, archs=None):
     """Decorator to skip tests when the target is Android.
@@ -682,7 +692,7 @@ def skipUnlessHasCallSiteInfo(func):
 
         f = tempfile.NamedTemporaryFile()
         cmd = "echo 'int main() {}' | " \
-              "%s -g -glldb -O1 -S -emit-llvm -x c -o %s -" % (compiler_path, f.name)
+              "%s -g -glldb -O1 -Xclang -femit-debug-entry-values -S -emit-llvm -x c -o %s -" % (compiler_path, f.name)
         if os.popen(cmd).close() is not None:
             return "Compiler can't compile with call site info enabled"
 

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/Makefile
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/Makefile?rev=371668&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/Makefile (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/Makefile Wed Sep 11 14:23:45 2019
@@ -0,0 +1,4 @@
+LEVEL = ../../../make
+CXX_SOURCES := main.cpp
+include $(LEVEL)/Makefile.rules
+CXXFLAGS += -O1 -glldb -Xclang -femit-debug-entry-values

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/TestBasicEntryValuesX86_64.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/TestBasicEntryValuesX86_64.py?rev=371668&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/TestBasicEntryValuesX86_64.py (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/TestBasicEntryValuesX86_64.py Wed Sep 11 14:23:45 2019
@@ -0,0 +1,8 @@
+from lldbsuite.test import lldbinline
+from lldbsuite.test import decorators
+
+lldbinline.MakeInlineTest(__file__, globals(),
+        [decorators.skipUnlessDarwin,
+         decorators.skipUnlessArch('x86_64'),
+         decorators.skipUnlessHasCallSiteInfo,
+         decorators.skipIf(dwarf_version=['<', '4'])])

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/main.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/main.cpp?rev=371668&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/main.cpp (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/main.cpp Wed Sep 11 14:23:45 2019
@@ -0,0 +1,169 @@
+// Note: This test requires the SysV AMD64 ABI to be in use, and requires
+// compiler support for DWARF entry values.
+
+// Inhibit dead-arg-elim by using 'x'.
+template<typename T> __attribute__((noinline)) void use(T x) {
+  asm volatile (""
+      /* Outputs */  :
+      /* Inputs */   : "g"(x)
+      /* Clobbers */ :
+  );
+}
+
+// Destroy %rsi in the current frame.
+#define DESTROY_RSI \
+  asm volatile ("xorq %%rsi, %%rsi" \
+      /* Outputs */  : \
+      /* Inputs */   : \
+      /* Clobbers */ : "rsi" \
+  );
+
+struct S1 {
+  int field1 = 123;
+  int *field2 = &field1;
+};
+
+__attribute__((noinline))
+void func1(int &sink, int x) {
+  use(x);
+
+  // Destroy 'x' in the current frame.
+  DESTROY_RSI;
+
+  //% self.filecheck("image lookup -va $pc", "main.cpp", "-check-prefix=FUNC1-DESC")
+  // FUNC1-DESC: name = "x", type = "int", location = DW_OP_entry_value( rsi)
+
+  ++sink;
+}
+
+__attribute__((noinline))
+void func2(int &sink, int x) {
+  use(x);
+
+  // Destroy 'x' in the current frame.
+  DESTROY_RSI;
+
+  //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC2-EXPR")
+  // FUNC2-EXPR: (int) ${{.*}} = 123
+
+  ++sink;
+}
+
+__attribute__((noinline))
+void func3(int &sink, int *p) {
+  use(p);
+
+  // Destroy 'p' in the current frame.
+  DESTROY_RSI;
+
+  //% self.filecheck("expr *p", "main.cpp", "-check-prefix=FUNC3-EXPR")
+  // FUNC3-EXPR: (int) ${{.*}} = 123
+
+  ++sink;
+}
+
+__attribute__((noinline))
+void func4_amb(int &sink, int x) {
+  use(x);
+
+  // Destroy 'x' in the current frame.
+  DESTROY_RSI;
+
+  //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC4-EXPR", expect_cmd_failure=True)
+  // FUNC4-EXPR: couldn't get the value of variable x: Could not evaluate DW_OP_entry_value.
+
+  ++sink;
+}
+
+__attribute__((noinline))
+void func5_amb() {}
+
+__attribute__((noinline))
+void func6(int &sink, int x) {
+  if (sink > 0)
+    func4_amb(sink, x); /* tail (taken) */
+  else
+    func5_amb(); /* tail */
+}
+
+__attribute__((noinline))
+void func7(int &sink, int x) {
+  //% self.filecheck("bt", "main.cpp", "-check-prefix=FUNC7-BT")
+  // FUNC7-BT: func7
+  // FUNC7-BT-NEXT: [inlined] func8_inlined
+  // FUNC7-BT-NEXT: [inlined] func9_inlined
+  // FUNC7-BT-NEXT: func10
+  use(x);
+
+  // Destroy 'x' in the current frame.
+  DESTROY_RSI;
+
+  //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC7-EXPR")
+  // FUNC7-EXPR: (int) ${{.*}} = 123
+
+  ++sink;
+}
+
+__attribute__((always_inline))
+void func8_inlined(int &sink, int x) {
+  func7(sink, x);
+}
+
+__attribute__((always_inline))
+void func9_inlined(int &sink, int x) {
+  func8_inlined(sink, x);
+}
+
+__attribute__((noinline, disable_tail_calls))
+void func10(int &sink, int x) {
+  func9_inlined(sink, x);
+}
+
+__attribute__((noinline))
+void func11_tailcalled(int &sink, int x) {
+  //% self.filecheck("bt", "main.cpp", "-check-prefix=FUNC11-BT")
+  // FUNC11-BT: func11_tailcalled{{.*}}
+  // FUNC11-BT-NEXT: func12{{.*}} [artificial]
+  use(x);
+
+  // Destroy 'x' in the current frame.
+  DESTROY_RSI;
+
+  //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC11-EXPR")
+  // FUNC11-EXPR: (int) ${{.*}} = 123
+
+  ++sink;
+}
+
+__attribute__((noinline))
+void func12(int &sink, int x) {
+  func11_tailcalled(sink, x);
+}
+
+__attribute__((disable_tail_calls))
+int main() {
+  int sink = 0;
+  S1 s1;
+
+  // Test location dumping for DW_OP_entry_value.
+  func1(sink, 123);
+
+  // Test evaluation of "DW_OP_constu" in the parent frame.
+  func2(sink, 123);
+
+  // Test evaluation of "DW_OP_fbreg -24, DW_OP_deref" in the parent frame.
+  func3(sink, s1.field2);
+
+  // The sequences `main -> func4 -> func{5,6}_amb -> sink` are both plausible.
+  // Test that lldb doesn't attempt to guess which one occurred: entry value
+  // evaluation should fail.
+  func6(sink, 123);
+
+  // Test that evaluation can "see through" inlining.
+  func10(sink, 123);
+
+  // Test that evaluation can "see through" tail calls.
+  func12(sink, 123);
+
+  return 0;
+}

Modified: lldb/trunk/source/Expression/DWARFExpression.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Expression/DWARFExpression.cpp?rev=371668&r1=371667&r2=371668&view=diff
==============================================================================
--- lldb/trunk/source/Expression/DWARFExpression.cpp (original)
+++ lldb/trunk/source/Expression/DWARFExpression.cpp Wed Sep 11 14:23:45 2019
@@ -33,6 +33,7 @@
 #include "lldb/Target/RegisterContext.h"
 #include "lldb/Target/StackFrame.h"
 #include "lldb/Target/StackID.h"
+#include "lldb/Target/Target.h"
 #include "lldb/Target/Thread.h"
 
 #include "Plugins/SymbolFile/DWARF/DWARFUnit.h"
@@ -91,9 +92,27 @@ void DWARFExpression::DumpLocation(Strea
     return;
   const lldb::offset_t start_offset = offset;
   const lldb::offset_t end_offset = offset + length;
+
+  // An operation within a DWARF expression may contain a sub-expression. The
+  // motivating example for this is DW_OP_entry_value. Keep track of where each
+  // each sub-expression ends.
+  std::vector<lldb::offset_t> ends_of_subexprs;
+
+  // "Finish" (i.e. print the closing right-parens) for sub-expressions up to
+  // the specified \p op_offset.
+  auto finish_subexpressions_to = [&](const lldb::offset_t op_offset) {
+    while (!ends_of_subexprs.empty() && op_offset >= ends_of_subexprs.back()) {
+      ends_of_subexprs.pop_back();
+      s->Printf(")");
+      if (!ends_of_subexprs.empty())
+        s->Printf(" ");
+    }
+  };
+
   while (m_data.ValidOffset(offset) && offset < end_offset) {
     const lldb::offset_t op_offset = offset;
     const uint8_t op = m_data.GetU8(&offset);
+    finish_subexpressions_to(op_offset);
 
     switch (level) {
     default:
@@ -466,8 +485,16 @@ void DWARFExpression::DumpLocation(Strea
     case DW_OP_APPLE_uninit:
       s->PutCString("DW_OP_APPLE_uninit"); // 0xF0
       break;
+    case DW_OP_entry_value: {
+      uint32_t subexpr_len = m_data.GetULEB128(&offset);
+      s->PutCString("DW_OP_entry_value(");
+      ends_of_subexprs.push_back(offset + subexpr_len);
+      break;
+    }
     }
   }
+
+  finish_subexpressions_to(end_offset);
 }
 
 void DWARFExpression::SetLocationListSlide(addr_t slide) {
@@ -580,6 +607,8 @@ static bool ReadRegisterValueAsScalar(Re
   return false;
 }
 
+/// Return the length in bytes of the set of operands for \p op. No guarantees
+/// are made on the state of \p data after this call.
 static offset_t GetOpcodeDataSize(const DataExtractor &data,
                                   const lldb::offset_t data_offset,
                                   const uint8_t op) {
@@ -776,6 +805,12 @@ static offset_t GetOpcodeDataSize(const
     return offset - data_offset;
   }
 
+  case DW_OP_entry_value: // 0xa3 ULEB128 size + variable-length block
+  {
+    uint64_t subexpr_len = data.GetULEB128(&offset);
+    return (offset - data_offset) + subexpr_len;
+  }
+
   default:
     break;
   }
@@ -1071,6 +1106,216 @@ bool DWARFExpression::DumpLocationForAdd
   return false;
 }
 
+static bool Evaluate_DW_OP_entry_value(std::vector<Value> &stack,
+                                       ExecutionContext *exe_ctx,
+                                       RegisterContext *reg_ctx,
+                                       const DataExtractor &opcodes,
+                                       lldb::offset_t &opcode_offset,
+                                       Status *error_ptr, Log *log) {
+  // DW_OP_entry_value(sub-expr) describes the location a variable had upon
+  // function entry: this variable location is presumed to be optimized out at
+  // the current PC value.  The caller of the function may have call site
+  // information that describes an alternate location for the variable (e.g. a
+  // constant literal, or a spilled stack value) in the parent frame.
+  //
+  // Example (this is pseudo-code & pseudo-DWARF, but hopefully illustrative):
+  //
+  //     void child(int &sink, int x) {
+  //       ...
+  //       /* "x" gets optimized out. */
+  //
+  //       /* The location of "x" here is: DW_OP_entry_value($reg2). */
+  //       ++sink;
+  //     }
+  //
+  //     void parent() {
+  //       int sink;
+  //
+  //       /*
+  //        * The callsite information emitted here is:
+  //        *
+  //        * DW_TAG_call_site
+  //        *   DW_AT_return_pc ... (for "child(sink, 123);")
+  //        *   DW_TAG_call_site_parameter (for "sink")
+  //        *     DW_AT_location   ($reg1)
+  //        *     DW_AT_call_value ($SP - 8)
+  //        *   DW_TAG_call_site_parameter (for "x")
+  //        *     DW_AT_location   ($reg2)
+  //        *     DW_AT_call_value ($literal 123)
+  //        *
+  //        * DW_TAG_call_site
+  //        *   DW_AT_return_pc ... (for "child(sink, 456);")
+  //        *   ...
+  //        */
+  //       child(sink, 123);
+  //       child(sink, 456);
+  //     }
+  //
+  // When the program stops at "++sink" within `child`, the debugger determines
+  // the call site by analyzing the return address. Once the call site is found,
+  // the debugger determines which parameter is referenced by DW_OP_entry_value
+  // and evaluates the corresponding location for that parameter in `parent`.
+
+  // 1. Find the function which pushed the current frame onto the stack.
+  if ((!exe_ctx || !exe_ctx->HasTargetScope()) || !reg_ctx) {
+    LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no exe/reg context");
+    return false;
+  }
+
+  StackFrame *current_frame = exe_ctx->GetFramePtr();
+  Thread *thread = exe_ctx->GetThreadPtr();
+  if (!current_frame || !thread) {
+    LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no current frame/thread");
+    return false;
+  }
+
+  Target &target = exe_ctx->GetTargetRef();
+  StackFrameSP parent_frame = nullptr;
+  addr_t return_pc = LLDB_INVALID_ADDRESS;
+  uint32_t current_frame_idx = current_frame->GetFrameIndex();
+  uint32_t num_frames = thread->GetStackFrameCount();
+  for (uint32_t parent_frame_idx = current_frame_idx + 1;
+       parent_frame_idx < num_frames; ++parent_frame_idx) {
+    parent_frame = thread->GetStackFrameAtIndex(parent_frame_idx);
+    // Require a valid sequence of frames.
+    if (!parent_frame)
+      break;
+
+    // Record the first valid return address, even if this is an inlined frame,
+    // in order to look up the associated call edge in the first non-inlined
+    // parent frame.
+    if (return_pc == LLDB_INVALID_ADDRESS) {
+      return_pc = parent_frame->GetFrameCodeAddress().GetLoadAddress(&target);
+      LLDB_LOG(log,
+               "Evaluate_DW_OP_entry_value: immediate ancestor with pc = {0:x}",
+               return_pc);
+    }
+
+    // If we've found an inlined frame, skip it (these have no call site
+    // parameters).
+    if (parent_frame->IsInlined())
+      continue;
+
+    // We've found the first non-inlined parent frame.
+    break;
+  }
+  if (!parent_frame || !parent_frame->GetRegisterContext()) {
+    LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no parent frame with reg ctx");
+    return false;
+  }
+
+  Function *parent_func =
+      parent_frame->GetSymbolContext(eSymbolContextFunction).function;
+  if (!parent_func) {
+    LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no parent function");
+    return false;
+  }
+
+  // 2. Find the call edge in the parent function responsible for creating the
+  //    current activation.
+  Function *current_func =
+      current_frame->GetSymbolContext(eSymbolContextFunction).function;
+  if (!current_func) {
+    LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no current function");
+    return false;
+  }
+
+  CallEdge *call_edge = nullptr;
+  ModuleList &modlist = target.GetImages();
+  if (!parent_frame->IsArtificial()) {
+    // If the parent frame is not artificial, the current activation may be
+    // produced by an ambiguous tail call. In this case, refuse to proceed.
+    call_edge = parent_func->GetCallEdgeForReturnAddress(return_pc, target);
+    if (!call_edge) {
+      LLDB_LOG(log,
+               "Evaluate_DW_OP_entry_value: no call edge for retn-pc = {0:x} "
+               "in parent frame {1}",
+               return_pc, parent_func->GetName());
+      return false;
+    }
+    Function *callee_func = call_edge->GetCallee(modlist);
+    if (callee_func != current_func) {
+      LLDB_LOG(log, "Evaluate_DW_OP_entry_value: ambiguous call sequence, "
+                    "can't find real parent frame");
+      return false;
+    }
+  } else {
+    // The StackFrameList solver machinery has deduced that an unambiguous tail
+    // call sequence that produced the current activation.  The first edge in
+    // the parent that points to the current function must be valid.
+    for (CallEdge &edge : parent_func->GetTailCallingEdges()) {
+      if (edge.GetCallee(modlist) == current_func) {
+        call_edge = &edge;
+        break;
+      }
+    }
+  }
+  if (!call_edge) {
+    LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no unambiguous edge from parent "
+                  "to current function");
+    return false;
+  }
+
+  // 3. Attempt to locate the DW_OP_entry_value expression in the set of
+  //    available call site parameters. If found, evaluate the corresponding
+  //    parameter in the context of the parent frame.
+  const uint32_t subexpr_len = opcodes.GetULEB128(&opcode_offset);
+  const void *subexpr_data = opcodes.GetData(&opcode_offset, subexpr_len);
+  if (!subexpr_data) {
+    LLDB_LOG(log, "Evaluate_DW_OP_entry_value: subexpr could not be read");
+    return false;
+  }
+
+  const CallSiteParameter *matched_param = nullptr;
+  for (const CallSiteParameter &param : call_edge->GetCallSiteParameters()) {
+    DataExtractor param_subexpr_extractor;
+    if (!param.LocationInCallee.GetExpressionData(param_subexpr_extractor))
+      continue;
+    lldb::offset_t param_subexpr_offset = 0;
+    const void *param_subexpr_data =
+        param_subexpr_extractor.GetData(&param_subexpr_offset, subexpr_len);
+    if (!param_subexpr_data ||
+        param_subexpr_extractor.BytesLeft(param_subexpr_offset) != 0)
+      continue;
+
+    // At this point, the DW_OP_entry_value sub-expression and the callee-side
+    // expression in the call site parameter are known to have the same length.
+    // Check whether they are equal.
+    //
+    // Note that an equality check is sufficient: the contents of the
+    // DW_OP_entry_value subexpression are only used to identify the right call
+    // site parameter in the parent, and do not require any special handling.
+    if (memcmp(subexpr_data, param_subexpr_data, subexpr_len) == 0) {
+      matched_param = ¶m;
+      break;
+    }
+  }
+  if (!matched_param) {
+    LLDB_LOG(log,
+             "Evaluate_DW_OP_entry_value: no matching call site param found");
+    return false;
+  }
+
+  // TODO: Add support for DW_OP_push_object_address within a DW_OP_entry_value
+  // subexpresion whenever llvm does.
+  Value result;
+  ExecutionContext parent_exe_ctx = *exe_ctx;
+  parent_exe_ctx.SetFrameSP(parent_frame);
+  const DWARFExpression &param_expr = matched_param->LocationInCaller;
+  if (!param_expr.Evaluate(&parent_exe_ctx,
+                           parent_frame->GetRegisterContext().get(),
+                           /*loclist_base_addr=*/LLDB_INVALID_ADDRESS,
+                           /*initial_value_ptr=*/nullptr,
+                           /*object_address_ptr=*/nullptr, result, error_ptr)) {
+    LLDB_LOG(log,
+             "Evaluate_DW_OP_entry_value: call site param evaluation failed");
+    return false;
+  }
+
+  stack.push_back(result);
+  return true;
+}
+
 bool DWARFExpression::Evaluate(ExecutionContextScope *exe_scope,
                                lldb::addr_t loclist_base_load_addr,
                                const Value *initial_value_ptr,
@@ -2751,6 +2996,16 @@ bool DWARFExpression::Evaluate(
       stack.push_back(Scalar(value));
     } break;
 
+    case DW_OP_entry_value: {
+      if (!Evaluate_DW_OP_entry_value(stack, exe_ctx, reg_ctx, opcodes, offset,
+                                      error_ptr, log)) {
+        LLDB_ERRORF(error_ptr, "Could not evaluate %s.",
+                    DW_OP_value_to_name(op));
+        return false;
+      }
+      break;
+    }
+
     default:
       LLDB_LOGF(log, "Unhandled opcode %s in DWARFExpression.",
                 DW_OP_value_to_name(op));
@@ -2865,6 +3120,11 @@ static bool print_dwarf_exp_op(Stream &s
     s.Printf("%" PRIu64 " %" PRIi64, uint, sint);
     return true;
   }
+  if (opcode_class == DRC_TWOOPERANDS && opcode == DW_OP_entry_value) {
+    uint = data.GetULEB128(offset_ptr);
+    s.Printf("%" PRIu64 " ", uint);
+    return true;
+  }
   if (opcode_class != DRC_ONEOPERAND) {
     s.Printf("UNKNOWN OP %u", opcode);
     return false;
@@ -2965,6 +3225,7 @@ static bool print_dwarf_exp_op(Stream &s
   case DW_OP_regx:
   case DW_OP_GNU_addr_index:
   case DW_OP_GNU_const_index:
+  case DW_OP_entry_value:
     size = 128;
     break;
   default:

Modified: lldb/trunk/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp?rev=371668&r1=371667&r2=371668&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp (original)
+++ lldb/trunk/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp Wed Sep 11 14:23:45 2019
@@ -59,6 +59,8 @@ const char *DW_OP_value_to_name(uint32_t
 }
 
 DRC_class DW_OP_value_to_class(uint32_t val) {
+  // FIXME: If we just used llvm's DWARFExpression printer, we could delete
+  // all this code (and more in lldb's DWARFExpression.cpp).
   switch (val) {
   case 0x03:
     return DRC_ONEOPERAND;
@@ -358,6 +360,8 @@ DRC_class DW_OP_value_to_class(uint32_t
     return DRC_DWARFv3 | DRC_ONEOPERAND;
   case 0x9a:
     return DRC_DWARFv3 | DRC_ONEOPERAND;
+  case 0xa3: /* DW_OP_entry_value */
+    return DRC_TWOOPERANDS;
   case 0xf0:
     return DRC_ZEROOPERANDS; /* DW_OP_APPLE_uninit */
   case 0xe0:

Modified: lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp?rev=371668&r1=371667&r2=371668&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp (original)
+++ lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp Wed Sep 11 14:23:45 2019
@@ -8,6 +8,7 @@
 
 #include "SymbolFileDWARF.h"
 
+#include "llvm/ADT/Optional.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/Threading.h"
 
@@ -3708,9 +3709,57 @@ size_t SymbolFileDWARF::ParseVariables(c
   return vars_added;
 }
 
+/// Collect call site parameters in a DW_TAG_call_site DIE.
+static CallSiteParameterArray
+CollectCallSiteParameters(ModuleSP module, DWARFDIE call_site_die) {
+  CallSiteParameterArray parameters;
+  for (DWARFDIE child = call_site_die.GetFirstChild(); child.IsValid();
+       child = child.GetSibling()) {
+    if (child.Tag() != DW_TAG_call_site_parameter)
+      continue;
+
+    llvm::Optional<DWARFExpression> LocationInCallee = {};
+    llvm::Optional<DWARFExpression> LocationInCaller = {};
+
+    DWARFAttributes attributes;
+    const size_t num_attributes = child.GetAttributes(attributes);
+
+    // Parse the location at index \p attr_index within this call site parameter
+    // DIE, or return None on failure.
+    auto parse_simple_location =
+        [&](int attr_index) -> llvm::Optional<DWARFExpression> {
+      DWARFFormValue form_value;
+      if (!attributes.ExtractFormValueAtIndex(attr_index, form_value))
+        return {};
+      if (!DWARFFormValue::IsBlockForm(form_value.Form()))
+        return {};
+      auto data = child.GetData();
+      uint32_t block_offset = form_value.BlockData() - data.GetDataStart();
+      uint32_t block_length = form_value.Unsigned();
+      return DWARFExpression(module,
+                             DataExtractor(data, block_offset, block_length),
+                             child.GetCU());
+    };
+
+    for (size_t i = 0; i < num_attributes; ++i) {
+      dw_attr_t attr = attributes.AttributeAtIndex(i);
+      if (attr == DW_AT_location)
+        LocationInCallee = parse_simple_location(i);
+      if (attr == DW_AT_call_value)
+        LocationInCaller = parse_simple_location(i);
+    }
+
+    if (LocationInCallee && LocationInCaller) {
+      CallSiteParameter param = {*LocationInCallee, *LocationInCaller};
+      parameters.push_back(param);
+    }
+  }
+  return parameters;
+}
+
 /// Collect call graph edges present in a function DIE.
 static std::vector<lldb_private::CallEdge>
-CollectCallEdges(DWARFDIE function_die) {
+CollectCallEdges(ModuleSP module, DWARFDIE function_die) {
   // Check if the function has a supported call site-related attribute.
   // TODO: In the future it may be worthwhile to support call_all_source_calls.
   uint64_t has_call_edges =
@@ -3747,9 +3796,28 @@ CollectCallEdges(DWARFDIE function_die)
     addr_t return_pc = child.GetAttributeValueAsAddress(DW_AT_call_return_pc,
                                                         LLDB_INVALID_ADDRESS);
 
+    // Extract call site parameters.
+    CallSiteParameterArray parameters =
+        CollectCallSiteParameters(module, child);
+
     LLDB_LOG(log, "CollectCallEdges: Found call origin: {0} (retn-PC: {1:x})",
              call_origin.GetPubname(), return_pc);
-    call_edges.emplace_back(call_origin.GetMangledName(), return_pc);
+    if (log && parameters.size()) {
+      for (const CallSiteParameter &param : parameters) {
+        StreamString callee_loc_desc, caller_loc_desc;
+        param.LocationInCallee.GetDescription(&callee_loc_desc,
+                                              eDescriptionLevelBrief,
+                                              LLDB_INVALID_ADDRESS, nullptr);
+        param.LocationInCaller.GetDescription(&caller_loc_desc,
+                                              eDescriptionLevelBrief,
+                                              LLDB_INVALID_ADDRESS, nullptr);
+        LLDB_LOG(log, "CollectCallEdges: \tparam: {0} => {1}",
+                 callee_loc_desc.GetString(), caller_loc_desc.GetString());
+      }
+    }
+
+    call_edges.emplace_back(call_origin.GetMangledName(), return_pc,
+                            std::move(parameters));
   }
   return call_edges;
 }
@@ -3758,7 +3826,7 @@ std::vector<lldb_private::CallEdge>
 SymbolFileDWARF::ParseCallEdgesInFunction(UserID func_id) {
   DWARFDIE func_die = GetDIE(func_id.GetID());
   if (func_die.IsValid())
-    return CollectCallEdges(func_die);
+    return CollectCallEdges(GetObjectFile()->GetModule(), func_die);
   return {};
 }
 

Modified: lldb/trunk/source/Symbol/Function.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Symbol/Function.cpp?rev=371668&r1=371667&r2=371668&view=diff
==============================================================================
--- lldb/trunk/source/Symbol/Function.cpp (original)
+++ lldb/trunk/source/Symbol/Function.cpp Wed Sep 11 14:23:45 2019
@@ -127,11 +127,16 @@ size_t InlineFunctionInfo::MemorySize()
 }
 
 //
-CallEdge::CallEdge(const char *symbol_name, lldb::addr_t return_pc)
-    : return_pc(return_pc), resolved(false) {
+CallEdge::CallEdge(const char *symbol_name, lldb::addr_t return_pc,
+                   CallSiteParameterArray parameters)
+    : return_pc(return_pc), parameters(std::move(parameters)), resolved(false) {
   lazy_callee.symbol_name = symbol_name;
 }
 
+llvm::ArrayRef<CallSiteParameter> CallEdge::GetCallSiteParameters() const {
+  return parameters;
+}
+
 void CallEdge::ParseSymbolFileAndResolve(ModuleList &images) {
   if (resolved)
     return;




More information about the lldb-commits mailing list