diff --git a/lldb/include/lldb/Core/Disassembler.h b/lldb/include/lldb/Core/Disassembler.h index 50a5d87835844..00a04678e3202 100644 --- a/lldb/include/lldb/Core/Disassembler.h +++ b/lldb/include/lldb/Core/Disassembler.h @@ -169,7 +169,7 @@ class Instruction { virtual bool IsAuthenticated() = 0; - bool CanSetBreakpoint (); + bool CanSetBreakpoint(); virtual size_t Decode(const Disassembler &disassembler, const DataExtractor &data, @@ -282,7 +282,7 @@ std::function FetchImmOp(int64_t &imm); std::function MatchOpType(Instruction::Operand::Type type); -} +} // namespace OperandMatchers class InstructionList { public: @@ -316,20 +316,19 @@ class InstructionList { /// @param[in] ignore_calls /// It true, then fine the first branch instruction that isn't /// a function call (a branch that calls and returns to the next - /// instruction). If false, find the instruction index of any + /// instruction). If false, find the instruction index of any /// branch in the list. - /// + /// /// @param[out] found_calls - /// If non-null, this will be set to true if any calls were found in + /// If non-null, this will be set to true if any calls were found in /// extending the range. - /// + /// /// @return /// The instruction index of the first branch that is at or past - /// \a start. Returns UINT32_MAX if no matching branches are + /// \a start. Returns UINT32_MAX if no matching branches are /// found. //------------------------------------------------------------------ - uint32_t GetIndexOfNextBranchInstruction(uint32_t start, - bool ignore_calls, + uint32_t GetIndexOfNextBranchInstruction(uint32_t start, bool ignore_calls, bool *found_calls) const; uint32_t GetIndexOfInstructionAtLoadAddress(lldb::addr_t load_addr, @@ -399,6 +398,7 @@ class Disassembler : public std::enable_shared_from_this, eOptionMarkPCAddress = (1u << 3), // Mark the disassembly line the contains the PC eOptionShowControlFlowKind = (1u << 4), + eOptionRichAnnotations = (1u << 5), }; enum HexImmediateStyle { diff --git a/lldb/include/lldb/Expression/DWARFExpression.h b/lldb/include/lldb/Expression/DWARFExpression.h index 8fcc5d37b91c9..1f8464cf5cf85 100644 --- a/lldb/include/lldb/Expression/DWARFExpression.h +++ b/lldb/include/lldb/Expression/DWARFExpression.h @@ -159,7 +159,8 @@ class DWARFExpression { return data.GetByteSize() > 0; } - void DumpLocation(Stream *s, lldb::DescriptionLevel level, ABI *abi) const; + void DumpLocation(Stream *s, lldb::DescriptionLevel level, ABI *abi, + llvm::DIDumpOptions options = {}) const; bool MatchesOperand(StackFrame &frame, const Instruction::Operand &op) const; diff --git a/lldb/source/Commands/CommandObjectDisassemble.cpp b/lldb/source/Commands/CommandObjectDisassemble.cpp index 70e687e19ac6d..35315dc144669 100644 --- a/lldb/source/Commands/CommandObjectDisassemble.cpp +++ b/lldb/source/Commands/CommandObjectDisassemble.cpp @@ -154,6 +154,10 @@ Status CommandObjectDisassemble::CommandOptions::SetOptionValue( } } break; + case 'R': //< --rich + enable_rich_annotations = true; + break; + case '\x01': force = true; break; @@ -180,6 +184,7 @@ void CommandObjectDisassemble::CommandOptions::OptionParsingStarting( end_addr = LLDB_INVALID_ADDRESS; symbol_containing_addr = LLDB_INVALID_ADDRESS; raw = false; + enable_rich_annotations = false; plugin_name.clear(); Target *target = @@ -528,6 +533,9 @@ void CommandObjectDisassemble::DoExecute(Args &command, if (m_options.raw) options |= Disassembler::eOptionRawOuput; + if (m_options.enable_rich_annotations) + options |= Disassembler::eOptionRichAnnotations; + llvm::Expected> ranges = GetRangesForSelectedMode(result); if (!ranges) { diff --git a/lldb/source/Commands/CommandObjectDisassemble.h b/lldb/source/Commands/CommandObjectDisassemble.h index 4fbcd72d1c042..caaeabc15593d 100644 --- a/lldb/source/Commands/CommandObjectDisassemble.h +++ b/lldb/source/Commands/CommandObjectDisassemble.h @@ -78,6 +78,7 @@ class CommandObjectDisassemble : public CommandObjectParsed { // in SetOptionValue if anything the selects a location is set. lldb::addr_t symbol_containing_addr = 0; bool force = false; + bool enable_rich_annotations = false; }; CommandObjectDisassemble(CommandInterpreter &interpreter); diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td index acb741081cac3..15e03a2a3056b 100644 --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -361,6 +361,8 @@ let Command = "disassemble" in { Desc<"Disassemble function containing this address.">; def disassemble_options_force : Option<"force", "\\x01">, Groups<[2,3,4,5,7]>, Desc<"Force disassembly of large functions.">; + def disassemble_options_rich : Option<"rich", "R">, + Desc<"Enable rich disassembly annotations for this invocation.">; } let Command = "diagnostics dump" in { diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp index e0a7d69345706..08190d05db3c6 100644 --- a/lldb/source/Core/Disassembler.cpp +++ b/lldb/source/Core/Disassembler.cpp @@ -26,7 +26,10 @@ #include "lldb/Symbol/Function.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Symbol/VariableList.h" #include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" @@ -41,6 +44,7 @@ #include "lldb/lldb-private-enumerations.h" #include "lldb/lldb-private-interfaces.h" #include "lldb/lldb-private-types.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/Support/Compiler.h" #include "llvm/TargetParser/Triple.h" @@ -376,6 +380,147 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch, } } + // Add rich variable location annotations to the disassembly output. + // + // For each instruction, this block attempts to resolve in-scope variables + // and determine if the current PC falls within their + // DWARF location entry. If so, it prints a simplified annotation using the + // variable name and its resolved location (e.g., "var = reg; " ). + // + // Annotations are only included if the variable has a valid DWARF location + // entry, and the location string is non-empty after filtering. Decoding + // errors and DWARF opcodes are intentionally omitted to keep the output + // concise and user-friendly. + // + // The goal is to give users helpful live variable hints alongside the + // disassembled instruction stream, similar to how debug information + // enhances source-level debugging. + + struct VarState { + std::string name; //< Display name. + std::string last_loc; //< Last printed location (empty means ). + bool seen_this_inst = false; + }; + + // Track live variables across instructions (keyed by stable LLDB user_id_t. 8 + // is a good small-buffer guess. + llvm::SmallDenseMap live_vars; + + // Stateful annotator: updates live_vars and returns only what should be + // printed for THIS instruction. + auto annotate_variables = [&](Instruction &inst) -> std::vector { + std::vector events; + + StackFrame *frame = exe_ctx.GetFramePtr(); + TargetSP target_sp = exe_ctx.GetTargetSP(); + ProcessSP process_sp = exe_ctx.GetProcessSP(); + if (!frame || !target_sp || !process_sp) + return events; + + // Reset "seen" flags for this instruction. + for (auto &kv : live_vars) + kv.second.seen_this_inst = false; + + addr_t current_pc = inst.GetAddress().GetLoadAddress(target_sp.get()); + addr_t original_pc = + frame->GetFrameCodeAddress().GetLoadAddress(target_sp.get()); + + // We temporarily move the frame PC so variable locations resolve at this + // instruction. + if (!frame->ChangePC(current_pc)) + return events; + + VariableListSP var_list_sp = frame->GetInScopeVariableList(true); + if (!var_list_sp) { + // No variables in scope: everything previously live becomes . + for (auto I = live_vars.begin(), E = live_vars.end(); I != E;) { + auto Cur = I++; + events.push_back( + llvm::formatv("{0} = ", Cur->second.name).str()); + live_vars.erase(Cur); + } + frame->ChangePC(original_pc); + return events; + } + + SymbolContext sc = frame->GetSymbolContext(eSymbolContextFunction); + addr_t func_load_addr = + sc.function ? sc.function->GetAddress().GetLoadAddress(target_sp.get()) + : LLDB_INVALID_ADDRESS; + + // Walk all in-scope variables and try to resolve a location. + for (const VariableSP &var_sp : *var_list_sp) { + if (!var_sp) + continue; + + // The var_id is a lldb::user_id_t – stable key. + const auto var_id = var_sp->GetID(); + const char *name_cstr = var_sp->GetName().AsCString(); + llvm::StringRef name = name_cstr ? name_cstr : ""; + + auto &expr_list = var_sp->LocationExpressionList(); + if (!expr_list.IsValid()) + continue; + + auto entry_or_err = + expr_list.GetExpressionEntryAtAddress(func_load_addr, current_pc); + if (!entry_or_err) + continue; + + auto entry = *entry_or_err; + + // Check range if present. + if (entry.file_range && + !entry.file_range->ContainsFileAddress( + (current_pc - func_load_addr) + expr_list.GetFuncFileAddress())) + continue; + + // Render a compact location string. + ABI *abi = process_sp->GetABI().get(); + llvm::DIDumpOptions opts; + opts.ShowAddresses = false; + opts.PrintRegisterOnly = true; + + StreamString loc_str; + entry.expr->DumpLocation(&loc_str, eDescriptionLevelBrief, abi, opts); + llvm::StringRef loc_clean = llvm::StringRef(loc_str.GetString()).trim(); + if (loc_clean.empty()) + continue; + + auto insert_res = + live_vars.insert({var_id, VarState{std::string(name), loc_clean.str(), + /*seen_this_inst*/ true}}); + if (insert_res.second) { + // Newly inserted → print. + events.push_back(llvm::formatv("{0} = {1}", name, loc_clean).str()); + } else { + // Already present. + VarState &vs = insert_res.first->second; + vs.seen_this_inst = true; + if (vs.last_loc != loc_clean) { + vs.last_loc = loc_clean.str(); + events.push_back( + llvm::formatv("{0} = {1}", vs.name, loc_clean).str()); + } + } + } + + // Anything previously live that we didn't see a location for at this inst + // is now . + for (auto I = live_vars.begin(), E = live_vars.end(); I != E;) { + auto Cur = I++; + if (!Cur->second.seen_this_inst) { + events.push_back( + llvm::formatv("{0} = ", Cur->second.name).str()); + live_vars.erase(Cur); + } + } + + // Restore PC. + frame->ChangePC(original_pc); + return events; + }; + previous_symbol = nullptr; SourceLine previous_line; for (size_t i = 0; i < num_instructions_found; ++i) { @@ -540,10 +685,26 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch, const bool show_bytes = (options & eOptionShowBytes) != 0; const bool show_control_flow_kind = (options & eOptionShowControlFlowKind) != 0; - inst->Dump(&strm, max_opcode_byte_size, true, show_bytes, + + StreamString inst_line; + + inst->Dump(&inst_line, max_opcode_byte_size, true, show_bytes, show_control_flow_kind, &exe_ctx, &sc, &prev_sc, nullptr, address_text_size); + + if (options & eOptionRichAnnotations) { + std::vector annotations = annotate_variables(*inst); + if (!annotations.empty()) { + const size_t annotation_column = 100; + inst_line.FillLastLineToColumn(annotation_column, ' '); + inst_line.PutCString("; "); + inst_line.PutCString(llvm::join(annotations, ", ")); + } + } + + strm.PutCString(inst_line.GetString()); strm.EOL(); + } else { break; } @@ -724,9 +885,7 @@ bool Instruction::DumpEmulation(const ArchSpec &arch) { return false; } -bool Instruction::CanSetBreakpoint () { - return !HasDelaySlot(); -} +bool Instruction::CanSetBreakpoint() { return !HasDelaySlot(); } bool Instruction::HasDelaySlot() { // Default is false. @@ -1073,10 +1232,8 @@ void InstructionList::Append(lldb::InstructionSP &inst_sp) { m_instructions.push_back(inst_sp); } -uint32_t -InstructionList::GetIndexOfNextBranchInstruction(uint32_t start, - bool ignore_calls, - bool *found_calls) const { +uint32_t InstructionList::GetIndexOfNextBranchInstruction( + uint32_t start, bool ignore_calls, bool *found_calls) const { size_t num_instructions = m_instructions.size(); uint32_t next_branch = UINT32_MAX; diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp index ed4e4e4e341a3..df56bcf5eb43e 100644 --- a/lldb/source/Expression/DWARFExpression.cpp +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -67,7 +67,8 @@ void DWARFExpression::UpdateValue(uint64_t const_value, } void DWARFExpression::DumpLocation(Stream *s, lldb::DescriptionLevel level, - ABI *abi) const { + ABI *abi, + llvm::DIDumpOptions options) const { auto *MCRegInfo = abi ? &abi->GetMCRegisterInfo() : nullptr; auto GetRegName = [&MCRegInfo](uint64_t DwarfRegNum, bool IsEH) -> llvm::StringRef { @@ -79,10 +80,9 @@ void DWARFExpression::DumpLocation(Stream *s, lldb::DescriptionLevel level, return llvm::StringRef(RegName); return {}; }; - llvm::DIDumpOptions DumpOpts; - DumpOpts.GetNameForDWARFReg = GetRegName; + options.GetNameForDWARFReg = GetRegName; llvm::DWARFExpression E(m_data.GetAsLLVM(), m_data.GetAddressByteSize()); - llvm::printDwarfExpression(&E, s->AsRawOstream(), DumpOpts, nullptr); + llvm::printDwarfExpression(&E, s->AsRawOstream(), options, nullptr); } RegisterKind DWARFExpression::GetRegisterKind() const { return m_reg_kind; } diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp index d97a814952186..c84e99c17c72e 100644 --- a/lldb/source/Target/StackFrame.cpp +++ b/lldb/source/Target/StackFrame.cpp @@ -264,6 +264,7 @@ bool StackFrame::ChangePC(addr_t pc) { const char *StackFrame::Disassemble() { std::lock_guard guard(m_mutex); + if (!m_disassembly.Empty()) return m_disassembly.GetData(); @@ -438,10 +439,10 @@ VariableList *StackFrame::GetVariableList(bool get_file_globals, const bool get_child_variables = true; const bool can_create = true; const bool stop_if_child_block_is_inlined_function = true; - frame_block->AppendBlockVariables(can_create, get_child_variables, - stop_if_child_block_is_inlined_function, - [](Variable *v) { return true; }, - m_variable_list_sp.get()); + frame_block->AppendBlockVariables( + can_create, get_child_variables, + stop_if_child_block_is_inlined_function, + [](Variable *v) { return true; }, m_variable_list_sp.get()); } } @@ -1225,10 +1226,12 @@ StackFrame::GetValueObjectForFrameVariable(const VariableSP &variable_sp, VariableList *var_list = GetVariableList(true, nullptr); if (var_list) { // Make sure the variable is a frame variable - const uint32_t var_idx = var_list->FindIndexForVariable(variable_sp.get()); + const uint32_t var_idx = + var_list->FindIndexForVariable(variable_sp.get()); const uint32_t num_variables = var_list->GetSize(); if (var_idx < num_variables) { - valobj_sp = m_variable_list_value_objects.GetValueObjectAtIndex(var_idx); + valobj_sp = + m_variable_list_value_objects.GetValueObjectAtIndex(var_idx); if (!valobj_sp) { if (m_variable_list_value_objects.GetSize() < num_variables) m_variable_list_value_objects.Resize(num_variables); @@ -1762,11 +1765,9 @@ lldb::ValueObjectSP DoGuessValueAt(StackFrame &frame, ConstString reg, if (clobbered_reg_matcher(operands[0])) { origin_operand = &operands[1]; - } - else if (clobbered_reg_matcher(operands[1])) { + } else if (clobbered_reg_matcher(operands[1])) { origin_operand = &operands[0]; - } - else { + } else { continue; } @@ -1792,8 +1793,7 @@ lldb::ValueObjectSP DoGuessValueAt(StackFrame &frame, ConstString reg, if (!source_path) { continue; } - source_path = - GetValueForDereferincingOffset(frame, source_path, offset); + source_path = GetValueForDereferincingOffset(frame, source_path, offset); } if (source_path) { @@ -1803,7 +1803,7 @@ lldb::ValueObjectSP DoGuessValueAt(StackFrame &frame, ConstString reg, return ValueObjectSP(); } -} +} // namespace lldb::ValueObjectSP StackFrame::GuessValueForRegisterAndOffset(ConstString reg, int64_t offset) { diff --git a/lldb/test/API/functionalities/rich-disassembler/Makefile b/lldb/test/API/functionalities/rich-disassembler/Makefile new file mode 100644 index 0000000000000..be53a34f7e265 --- /dev/null +++ b/lldb/test/API/functionalities/rich-disassembler/Makefile @@ -0,0 +1 @@ +include Makefile.rules \ No newline at end of file diff --git a/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py b/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py new file mode 100644 index 0000000000000..4bad4a4f4986b --- /dev/null +++ b/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py @@ -0,0 +1,34 @@ +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +import lldb + + +class TestRichDisassembler(TestBase): + def test_d_original_example_O1(self): + """ + Tests disassembler output for d_original_example.c built with -O1, + using the CLI with --rich for enabled annotations. + """ + self.build( + dictionary={"C_SOURCES": "d_original_example.c", "CFLAGS_EXTRAS": "-g -O1"} + ) + exe = self.getBuildArtifact("a.out") + target = self.dbg.CreateTarget(exe) + self.assertTrue(target) + + bp = target.BreakpointCreateByName("main") + self.assertGreater(bp.GetNumLocations(), 0) + + process = target.LaunchSimple(None, None, self.get_process_working_directory()) + self.assertTrue(process, "Failed to launch process") + self.assertEqual(process.GetState(), lldb.eStateStopped) + + # Run the CLI command and read output from self.res + self.runCmd("disassemble --rich -f", check=True) + out = self.res.GetOutput() + print(out) + + self.assertIn("argc = ", out) + self.assertIn("argv = ", out) + self.assertIn("i = ", out) + self.assertNotIn("", out) diff --git a/lldb/test/API/functionalities/rich-disassembler/d_original_example.c b/lldb/test/API/functionalities/rich-disassembler/d_original_example.c new file mode 100644 index 0000000000000..1c864753a0220 --- /dev/null +++ b/lldb/test/API/functionalities/rich-disassembler/d_original_example.c @@ -0,0 +1,7 @@ +#include + +int main(int argc, char **argv) { + for (int i = 1; i < argc; ++i) + puts(argv[i]); + return 0; +} diff --git a/llvm/include/llvm/DebugInfo/DIContext.h b/llvm/include/llvm/DebugInfo/DIContext.h index 0347f90c236d1..e7e87bbfebf38 100644 --- a/llvm/include/llvm/DebugInfo/DIContext.h +++ b/llvm/include/llvm/DebugInfo/DIContext.h @@ -209,6 +209,7 @@ struct DIDumpOptions { bool IsEH = false; bool DumpNonSkeleton = false; bool ShowAggregateErrors = false; + bool PrintRegisterOnly = false; std::string JsonErrSummaryFile; std::function GetNameForDWARFReg; diff --git a/llvm/lib/DebugInfo/DWARF/DWARFExpressionPrinter.cpp b/llvm/lib/DebugInfo/DWARF/DWARFExpressionPrinter.cpp index ebcd4dda50488..3622ff3a886a1 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFExpressionPrinter.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFExpressionPrinter.cpp @@ -47,14 +47,16 @@ static void prettyPrintBaseTypeRef(DWARFUnit *U, raw_ostream &OS, static bool printOp(const DWARFExpression::Operation *Op, raw_ostream &OS, DIDumpOptions DumpOpts, const DWARFExpression *Expr, DWARFUnit *U) { - if (Op->isError()) { + if (Op->isError() && !DumpOpts.PrintRegisterOnly) { OS << ""; return false; } - StringRef Name = OperationEncodingString(Op->getCode()); - assert(!Name.empty() && "DW_OP has no name!"); - OS << Name; + if (!DumpOpts.PrintRegisterOnly) { + StringRef Name = OperationEncodingString(Op->getCode()); + assert(!Name.empty() && "DW_OP has no name!"); + OS << Name; + } if ((Op->getCode() >= DW_OP_breg0 && Op->getCode() <= DW_OP_breg31) || (Op->getCode() >= DW_OP_reg0 && Op->getCode() <= DW_OP_reg31) || @@ -64,48 +66,51 @@ static bool printOp(const DWARFExpression::Operation *Op, raw_ostream &OS, Op->getRawOperands())) return true; - for (unsigned Operand = 0; Operand < Op->getDescription().Op.size(); - ++Operand) { - unsigned Size = Op->getDescription().Op[Operand]; - unsigned Signed = Size & DWARFExpression::Operation::SignBit; + if (!DumpOpts.PrintRegisterOnly) { + for (unsigned Operand = 0; Operand < Op->getDescription().Op.size(); + ++Operand) { + unsigned Size = Op->getDescription().Op[Operand]; + unsigned Signed = Size & DWARFExpression::Operation::SignBit; - if (Size == DWARFExpression::Operation::SizeSubOpLEB) { - StringRef SubName = - SubOperationEncodingString(Op->getCode(), Op->getRawOperand(Operand)); - assert(!SubName.empty() && "DW_OP SubOp has no name!"); - OS << " " << SubName; - } else if (Size == DWARFExpression::Operation::BaseTypeRef && U) { - // For DW_OP_convert the operand may be 0 to indicate that conversion to - // the generic type should be done. The same holds for DW_OP_reinterpret, - // which is currently not supported. - if (Op->getCode() == DW_OP_convert && Op->getRawOperand(Operand) == 0) - OS << " 0x0"; - else - prettyPrintBaseTypeRef(U, OS, DumpOpts, Op->getRawOperands(), Operand); - } else if (Size == DWARFExpression::Operation::WasmLocationArg) { - assert(Operand == 1); - switch (Op->getRawOperand(0)) { - case 0: - case 1: - case 2: - case 3: // global as uint32 - case 4: - OS << format(" 0x%" PRIx64, Op->getRawOperand(Operand)); - break; - default: - assert(false); + if (Size == DWARFExpression::Operation::SizeSubOpLEB) { + StringRef SubName = SubOperationEncodingString( + Op->getCode(), Op->getRawOperand(Operand)); + assert(!SubName.empty() && "DW_OP SubOp has no name!"); + OS << " " << SubName; + } else if (Size == DWARFExpression::Operation::BaseTypeRef && U) { + // For DW_OP_convert the operand may be 0 to indicate that conversion to + // the generic type should be done. The same holds for + // DW_OP_reinterpret, which is currently not supported. + if (Op->getCode() == DW_OP_convert && Op->getRawOperand(Operand) == 0) + OS << " 0x0"; + else + prettyPrintBaseTypeRef(U, OS, DumpOpts, Op->getRawOperands(), + Operand); + } else if (Size == DWARFExpression::Operation::WasmLocationArg) { + assert(Operand == 1); + switch (Op->getRawOperand(0)) { + case 0: + case 1: + case 2: + case 3: // global as uint32 + case 4: + OS << format(" 0x%" PRIx64, Op->getRawOperand(Operand)); + break; + default: + assert(false); + } + } else if (Size == DWARFExpression::Operation::SizeBlock) { + uint64_t Offset = Op->getRawOperand(Operand); + for (unsigned i = 0; i < Op->getRawOperand(Operand - 1); ++i) + OS << format(" 0x%02x", + static_cast(Expr->getData()[Offset++])); + } else { + if (Signed) + OS << format(" %+" PRId64, (int64_t)Op->getRawOperand(Operand)); + else if (Op->getCode() != DW_OP_entry_value && + Op->getCode() != DW_OP_GNU_entry_value) + OS << format(" 0x%" PRIx64, Op->getRawOperand(Operand)); } - } else if (Size == DWARFExpression::Operation::SizeBlock) { - uint64_t Offset = Op->getRawOperand(Operand); - for (unsigned i = 0; i < Op->getRawOperand(Operand - 1); ++i) - OS << format(" 0x%02x", - static_cast(Expr->getData()[Offset++])); - } else { - if (Signed) - OS << format(" %+" PRId64, (int64_t)Op->getRawOperand(Operand)); - else if (Op->getCode() != DW_OP_entry_value && - Op->getCode() != DW_OP_GNU_entry_value) - OS << format(" 0x%" PRIx64, Op->getRawOperand(Operand)); } } return true; @@ -120,29 +125,30 @@ void printDwarfExpression(const DWARFExpression *E, raw_ostream &OS, for (auto &Op : *E) { DumpOpts.IsEH = IsEH; - if (!printOp(&Op, OS, DumpOpts, E, U)) { + if (!printOp(&Op, OS, DumpOpts, E, U) && !DumpOpts.PrintRegisterOnly) { uint64_t FailOffset = Op.getEndOffset(); while (FailOffset < E->getData().size()) OS << format(" %02x", static_cast(E->getData()[FailOffset++])); return; } + if (!DumpOpts.PrintRegisterOnly) { + if (Op.getCode() == DW_OP_entry_value || + Op.getCode() == DW_OP_GNU_entry_value) { + OS << "("; + EntryValExprSize = Op.getRawOperand(0); + EntryValStartOffset = Op.getEndOffset(); + continue; + } - if (Op.getCode() == DW_OP_entry_value || - Op.getCode() == DW_OP_GNU_entry_value) { - OS << "("; - EntryValExprSize = Op.getRawOperand(0); - EntryValStartOffset = Op.getEndOffset(); - continue; - } + if (EntryValExprSize) { + EntryValExprSize -= Op.getEndOffset() - EntryValStartOffset; + if (EntryValExprSize == 0) + OS << ")"; + } - if (EntryValExprSize) { - EntryValExprSize -= Op.getEndOffset() - EntryValStartOffset; - if (EntryValExprSize == 0) - OS << ")"; + if (Op.getEndOffset() < E->getData().size()) + OS << ", "; } - - if (Op.getEndOffset() < E->getData().size()) - OS << ", "; } }