vsk created this revision.
vsk added reviewers: aprantl, friss, jasonmolenda, jingham.
Herald added subscribers: llvm-commits, hiraditya.
Herald added a project: LLVM.
Add support for evaluating DW_OP_entry_value. This involves:
- Teaching clang to emit debug entry values when the debugger tuning is set to
lldb.
- Parsing DW_TAG_call_site_parameter.
- Updating the expression evaluator.
rdar://54496008
https://reviews.llvm.org/D67376
Files:
lldb/include/lldb/Symbol/Function.h
lldb/packages/Python/lldbsuite/test/decorators.py
lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values/Makefile
lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values/TestEntryValsDumpLocationDescriptions1.py
lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values/main.cpp
lldb/packages/Python/lldbsuite/test/lldbtest.py
lldb/source/Expression/DWARFExpression.cpp
lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp
lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
lldb/source/Symbol/Function.cpp
lldb/source/Target/StackFrameList.cpp
llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
Index: llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
===================================================================
--- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -790,9 +790,9 @@
CU.constructCallSiteEntryDIE(ScopeDIE, CalleeSP, IsTail, PCAddr,
PCOffset, CallReg);
- // For now only GDB supports call site parameter debug info.
+ // For now only GDB and LLDB support call site parameter debug info.
if (Asm->TM.Options.EnableDebugEntryValues &&
- tuneForGDB()) {
+ (tuneForGDB() || tuneForLLDB())) {
ParamSet Params;
// Try to interpret values of call site parameters.
collectCallSiteParameters(&MI, Params);
Index: lldb/source/Target/StackFrameList.cpp
===================================================================
--- lldb/source/Target/StackFrameList.cpp
+++ lldb/source/Target/StackFrameList.cpp
@@ -250,26 +250,19 @@
begin.GetDisplayName(), end.GetDisplayName(), return_pc);
// Find a non-tail calling edge with the correct return PC.
- auto first_level_edges = begin.GetCallEdges();
if (log)
- for (const CallEdge &edge : first_level_edges)
+ for (const CallEdge &edge : begin.GetCallEdges())
LLDB_LOG(log, "FindInterveningFrames: found call with retn-PC = {0:x}",
edge.GetReturnPCAddress(begin, target));
- auto first_edge_it = std::lower_bound(
- first_level_edges.begin(), first_level_edges.end(), return_pc,
- [&](const CallEdge &edge, addr_t target_pc) {
- return edge.GetReturnPCAddress(begin, target) < target_pc;
- });
- if (first_edge_it == first_level_edges.end() ||
- first_edge_it->GetReturnPCAddress(begin, target) != return_pc) {
+ CallEdge *first_edge = begin.GetCallEdgeForReturnAddress(return_pc, target);
+ if (!first_edge) {
LLDB_LOG(log, "No call edge outgoing from {0} with retn-PC == {1:x}",
begin.GetDisplayName(), return_pc);
return;
}
- CallEdge &first_edge = const_cast<CallEdge &>(*first_edge_it);
// The first callee may not be resolved, or there may be nothing to fill in.
- Function *first_callee = first_edge.GetCallee(images);
+ Function *first_callee = first_edge->GetCallee(images);
if (!first_callee) {
LLDB_LOG(log, "Could not resolve callee");
return;
Index: lldb/source/Symbol/Function.cpp
===================================================================
--- lldb/source/Symbol/Function.cpp
+++ lldb/source/Symbol/Function.cpp
@@ -127,11 +127,18 @@
}
//
-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 {
+ if (parameters)
+ return *parameters.get();
+ return {};
+}
+
void CallEdge::ParseSymbolFileAndResolve(ModuleList &images) {
if (resolved)
return;
@@ -276,6 +283,20 @@
});
}
+CallEdge *Function::GetCallEdgeForReturnAddress(addr_t return_pc,
+ Target &target) {
+ auto edges = GetCallEdges();
+ auto edge_it =
+ std::lower_bound(edges.begin(), edges.end(), return_pc,
+ [&](const CallEdge &edge, addr_t pc) {
+ return edge.GetReturnPCAddress(*this, target) < pc;
+ });
+ if (edge_it == edges.end() ||
+ edge_it->GetReturnPCAddress(*this, target) != return_pc)
+ return nullptr;
+ return &const_cast<CallEdge &>(*edge_it);
+}
+
Block &Function::GetBlock(bool can_create) {
if (!m_block.BlockInfoHasBeenParsed() && can_create) {
ModuleSP module_sp = CalculateSymbolContextModule();
Index: lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -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,63 @@
return vars_added;
}
+/// Collect call site parameters in a TAG_call_site DIE.
+static CallSiteParameterArray
+CollectCallSiteParameters(ModuleSP module, DWARFDIE call_site_die) {
+ CallSiteParameterArray parameters = nullptr;
+ 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);
+ switch (attr) {
+ case DW_AT_location:
+ LocationInCallee = parse_simple_location(i);
+ break;
+ case DW_AT_call_value:
+ LocationInCaller = parse_simple_location(i);
+ break;
+ }
+ }
+
+ if (LocationInCallee && LocationInCaller) {
+ if (!parameters)
+ parameters.reset(new std::vector<CallSiteParameter>);
+ 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 =
@@ -3749,9 +3804,28 @@
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) {
+ for (const CallSiteParameter ¶m : *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;
}
@@ -3760,7 +3834,7 @@
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 {};
}
Index: lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp
+++ lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp
@@ -59,6 +59,7 @@
}
DRC_class DW_OP_value_to_class(uint32_t val) {
+ // FIXME: This switch should be simplified by using DW_OP_* names.
switch (val) {
case 0x03:
return DRC_ONEOPERAND;
@@ -358,6 +359,8 @@
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:
Index: lldb/source/Expression/DWARFExpression.cpp
===================================================================
--- lldb/source/Expression/DWARFExpression.cpp
+++ lldb/source/Expression/DWARFExpression.cpp
@@ -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 @@
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 @@
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 @@
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 @@
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,146 @@
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) {
+ // 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;
+ }
+
+ StackFrameSP parent_frame = nullptr;
+ 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);
+ // Ignore inlined frames: these have no call site parameters.
+ if (parent_frame && !parent_frame->IsInlined())
+ 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.
+ Target &target = exe_ctx->GetTargetRef();
+ ModuleList &modlist = target.GetImages();
+
+ 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;
+ 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.
+ addr_t return_pc =
+ parent_frame->GetFrameCodeAddress().GetLoadAddress(&target);
+ 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}",
+ return_pc);
+ 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 {
+ 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 ¶m : 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(¶m_subexpr_offset, subexpr_len);
+ if (!param_subexpr_data ||
+ param_subexpr_extractor.BytesLeft(param_subexpr_offset) != 0)
+ continue;
+
+ // At this point, the expression described by the DW_OP_entry_value and the
+ // expression in the call site parameter are known to have the same length.
+ // Check whether they are equal.
+ 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;
+ }
+
+ Value result;
+ ExecutionContext parent_exe_ctx = *exe_ctx;
+ parent_exe_ctx.SetFrameSP(parent_frame);
+ const DWARFExpression ¶m_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,
@@ -2494,6 +2669,7 @@
// address has been dynamically determined by an earlier step during user
// expression evaluation.
case DW_OP_push_object_address:
+ // TODO: Reject DW_OP_push_object_address within entry value exprs.
if (object_address_ptr)
stack.push_back(*object_address_ptr);
else {
@@ -2674,6 +2850,15 @@
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_LOGF(log, "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));
@@ -2788,6 +2973,11 @@
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;
@@ -2888,6 +3078,7 @@
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:
Index: lldb/packages/Python/lldbsuite/test/lldbtest.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/lldbtest.py
+++ lldb/packages/Python/lldbsuite/test/lldbtest.py
@@ -2207,12 +2207,16 @@
self,
command,
check_file,
- filecheck_options = ''):
+ filecheck_options = '',
+ expect_cmd_failure = False):
# Run the command.
self.runCmd(
command,
+ check=(not expect_cmd_failure),
msg="FileCheck'ing result of `{0}`".format(command))
+ self.assertTrue((not expect_cmd_failure) == self.res.Succeeded())
+
# Get the error text if there was an error, and the regular text if not.
output = self.res.GetOutput() if self.res.Succeeded() \
else self.res.GetError()
Index: lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values/main.cpp
===================================================================
--- /dev/null
+++ lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values/main.cpp
@@ -0,0 +1,125 @@
+//===-- main.cpp ------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// 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 */ :
+ );
+}
+
+struct S1 {
+ int field1 = 123;
+ int *field2 = &field1;
+};
+
+volatile int volatile_int;
+
+__attribute__((noinline))
+void func1(int &sink, int x) {
+ use(x);
+
+ // Destroy 'x' in the current frame.
+ asm volatile ("xorq %%rsi, %%rsi"
+ /* Outputs */ :
+ /* Inputs */ :
+ /* Clobbers */ : "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.
+ asm volatile ("xorq %%rsi, %%rsi"
+ /* Outputs */ :
+ /* Inputs */ :
+ /* Clobbers */ : "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.
+ asm volatile ("xorq %%rsi, %%rsi"
+ /* Outputs */ :
+ /* Inputs */ :
+ /* Clobbers */ : "rsi"
+ );
+
+ //% self.filecheck("expr *p", "main.cpp", "-check-prefix=FUNC3-EXPR")
+ // FUNC3-EXPR: (int) ${{.*}} = 123
+
+ ++sink;
+}
+
+void __attribute__((noinline)) func4_amb(int &sink, int x) {
+ use(x);
+
+ // Destroy 'x' in the current frame.
+ asm volatile ("xorq %%rsi, %%rsi"
+ /* Outputs */ :
+ /* Inputs */ :
+ /* Clobbers */ : "rsi"
+ );
+
+ //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC4-EXPR", expect_cmd_failure=True)
+ // FUNC4-EXPR: couldn't get the value of x: extracting data from value failed
+
+ ++sink;
+
+}
+
+void __attribute__((noinline)) func5_amb() {}
+
+void __attribute__((noinline)) func6(int &sink, int x) {
+ if (sink > 0)
+ func4_amb(sink, x); /* tail (taken) */
+ else
+ func5_amb(); /* tail */
+}
+
+__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);
+
+ return 0;
+}
Index: lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values/TestEntryValsDumpLocationDescriptions1.py
===================================================================
--- /dev/null
+++ lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values/TestEntryValsDumpLocationDescriptions1.py
@@ -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'])])
Index: lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values/Makefile
===================================================================
--- /dev/null
+++ lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values/Makefile
@@ -0,0 +1,4 @@
+LEVEL = ../../../make
+CXX_SOURCES := main.cpp
+include $(LEVEL)/Makefile.rules
+CXXFLAGS += -g -O1 -glldb -Xclang -femit-debug-entry-values
Index: lldb/packages/Python/lldbsuite/test/decorators.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/decorators.py
+++ lldb/packages/Python/lldbsuite/test/decorators.py
@@ -641,6 +641,16 @@
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 @@
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"
Index: lldb/include/lldb/Symbol/Function.h
===================================================================
--- lldb/include/lldb/Symbol/Function.h
+++ lldb/include/lldb/Symbol/Function.h
@@ -246,10 +246,21 @@
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;
+};
+
+using CallSiteParameterArray = std::unique_ptr<std::vector<CallSiteParameter>>;
+
/// \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 +270,8 @@
/// 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 +291,9 @@
/// 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 +309,10 @@
/// gives the return PC for the call.
lldb::addr_t return_pc;
+ // TODO: Use PointerIntPair to store the parameter array and the resolved
+ // flag in a single word.
+ CallSiteParameterArray parameters;
+
/// Whether or not an attempt was made to find the callee's definition.
bool resolved;
@@ -402,6 +421,11 @@
/// return None.
llvm::MutableArrayRef<CallEdge> GetTailCallingEdges();
+ /// Get the outgoing call edge from this function which has the given return
+ /// address \p return_pc, or return nullptr. Note that this will not return a
+ /// tail-calling edge.
+ CallEdge *GetCallEdgeForReturnAddress(lldb::addr_t return_pc, Target &target);
+
/// Get accessor for the block list.
///
/// \return
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits