Skip to content

Commit 912ba6d

Browse files
Add PrintRegisterOnly flag in struct DIDumpOptions and created new DWARFExpression::DumpLocationWithOptions for simplified expression printing
This patch introduces a PrintRegisterOnly flag to the DIDumpOptions struct, enabling concise rendering of DWARF expressions for disassembler annotations. Key changes: - Added DumpLocationWithOptions to DWARFExpression for flexible dumping with DIDumpOptions. - Updated DWARFExpression::print and Operation::print to respect PrintRegisterOnly, rendering registers like RDI without DW_OP_ or llvm: prefixes. - Suppressed <decoding error> output when PrintRegisterOnly is true to avoid clutter during register-only disassembly output. These changes are motivated by LLDB’s rich disassembler feature, where annotations should match user-facing register names without DWARF-level detail. Test impact: Some rich-disassembler tests that relied on DW_OP_ for validation were deprecated. Updated tests aligned with the new formatting will be added next.
1 parent b887db2 commit 912ba6d

File tree

10 files changed

+84
-380
lines changed

10 files changed

+84
-380
lines changed

lldb/include/lldb/Expression/DWARFExpression.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ class DWARFExpression {
158158
}
159159

160160
void DumpLocation(Stream *s, lldb::DescriptionLevel level, ABI *abi) const;
161+
void DumpLocationWithOptions(Stream *s, lldb::DescriptionLevel level, ABI *abi, llvm::DIDumpOptions options) const;
161162

162163
bool MatchesOperand(StackFrame &frame, const Instruction::Operand &op) const;
163164

lldb/source/Core/Disassembler.cpp

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -742,34 +742,19 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
742742
// Check if entry has a file_range, and filter on address if so.
743743
if (!entry.file_range || entry.file_range->ContainsFileAddress(
744744
(current_pc - func_load_addr) + expr_list.GetFuncFileAddress())) {
745-
745+
746746
StreamString loc_str;
747747
ABI *abi = exe_ctx->GetProcessPtr()->GetABI().get();
748-
entry.expr->DumpLocation(&loc_str, eDescriptionLevelBrief, abi);
749-
750-
std::string loc_output = loc_str.GetString().str();
751-
752-
llvm::SmallVector<llvm::StringRef, 4> parts;
753-
llvm::StringRef(loc_str.GetString()).split(parts, ", ");
748+
llvm::DIDumpOptions opts;
749+
opts.ShowAddresses = false;
750+
opts.PrintRegisterOnly = true; // <-- important: suppress DW_OP_... annotations, etc.
754751

755-
// Reconstruct the string without the decoding error chunks
756-
std::string cleaned_output;
757-
bool first = true;
758-
759-
for (const auto &part : parts) {
760-
if (part.contains("<decoding error>"))
761-
continue;
762-
763-
if (!first)
764-
cleaned_output += ", ";
765-
cleaned_output += part.str();
766-
first = false;
767-
}
752+
entry.expr->DumpLocationWithOptions(&loc_str, eDescriptionLevelBrief, abi, opts);
768753

769-
// Only keep this annotation if there is still something useful left.
770-
llvm::StringRef cleaned_ref = llvm::StringRef(cleaned_output).trim();
771-
if (!cleaned_ref.empty()) {
772-
annotations.push_back(llvm::formatv("{0} = {1}", name, cleaned_ref));
754+
// Only include if not empty
755+
llvm::StringRef loc_clean = llvm::StringRef(loc_str.GetString()).trim();
756+
if (!loc_clean.empty()) {
757+
annotations.push_back(llvm::formatv("{0} = {1}", name, loc_clean));
773758
}
774759
}
775760
}

lldb/source/Expression/DWARFExpression.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ void DWARFExpression::UpdateValue(uint64_t const_value,
6767

6868
void DWARFExpression::DumpLocation(Stream *s, lldb::DescriptionLevel level,
6969
ABI *abi) const {
70+
llvm::DIDumpOptions DumpOpts;
71+
this->DumpLocationWithOptions(s, level, abi, DumpOpts);
72+
}
73+
74+
void DWARFExpression::DumpLocationWithOptions(Stream *s, lldb::DescriptionLevel level,
75+
ABI *abi, llvm::DIDumpOptions options) const {
7076
auto *MCRegInfo = abi ? &abi->GetMCRegisterInfo() : nullptr;
7177
auto GetRegName = [&MCRegInfo](uint64_t DwarfRegNum,
7278
bool IsEH) -> llvm::StringRef {
@@ -78,10 +84,9 @@ void DWARFExpression::DumpLocation(Stream *s, lldb::DescriptionLevel level,
7884
return llvm::StringRef(RegName);
7985
return {};
8086
};
81-
llvm::DIDumpOptions DumpOpts;
82-
DumpOpts.GetNameForDWARFReg = GetRegName;
83-
llvm::DWARFExpression(m_data.GetAsLLVM(), m_data.GetAddressByteSize())
84-
.print(s->AsRawOstream(), DumpOpts, nullptr);
87+
options.GetNameForDWARFReg = GetRegName;
88+
llvm::DWARFExpression expression (m_data.GetAsLLVM(), m_data.GetAddressByteSize());
89+
expression.print(s->AsRawOstream(), options, nullptr);
8590
}
8691

8792
RegisterKind DWARFExpression::GetRegisterKind() const { return m_reg_kind; }

lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py

Lines changed: 2 additions & 258 deletions
Original file line numberDiff line numberDiff line change
@@ -2,202 +2,6 @@
22
from lldbsuite.test.decorators import *
33

44
class TestRichDisassembler(TestBase):
5-
6-
@no_debug_info_test
7-
def test_a_loop_with_local_variable(self):
8-
"""
9-
Tests that the disassembler includes basic DWARF variable annotation in output.
10-
Specifically checks that local variables in a loop are shown with DW_OP locations.
11-
Additionally, it verifies that the disassembly does not contain decoding errors.
12-
"""
13-
self.build(dictionary={
14-
'C_SOURCES': 'a_loop_with_local_variable.c',
15-
'CFLAGS_EXTRAS': '-g -O0'
16-
})
17-
exe = self.getBuildArtifact("a.out")
18-
target = self.dbg.CreateTarget(exe)
19-
self.assertTrue(target)
20-
21-
# Set a breakpoint inside main's loop
22-
src_file = lldb.SBFileSpec("test_loop_function_call.c")
23-
breakpoint = target.BreakpointCreateByName("main")
24-
self.assertGreater(breakpoint.GetNumLocations(), 0)
25-
26-
process = target.LaunchSimple(None, None, self.get_process_working_directory())
27-
self.assertTrue(process, "Failed to launch process")
28-
self.assertEqual(process.GetState(), lldb.eStateStopped)
29-
30-
frame = process.GetSelectedThread().GetSelectedFrame()
31-
disasm = frame.Disassemble()
32-
print(disasm)
33-
34-
# Check that we have DWARF annotations for variables
35-
self.assertIn("i = ", disasm)
36-
self.assertIn("DW_OP", disasm)
37-
self.assertNotIn("<decoding error>", disasm)
38-
39-
40-
@no_debug_info_test
41-
def test_b_multiple_stack_variables_O0(self):
42-
"""
43-
Tests disassembler output for b_multiple_stack_variables.c built with -O0.
44-
This test checks that multiple local variables are annotated with DWARF
45-
and that their locations are distinct. It also ensures that no decoding errors appear.
46-
"""
47-
self.build(dictionary={
48-
'C_SOURCES': 'b_multiple_stack_variables.c',
49-
'CFLAGS_EXTRAS': '-g -O0'
50-
})
51-
exe = self.getBuildArtifact("a.out")
52-
target = self.dbg.CreateTarget(exe)
53-
self.assertTrue(target)
54-
55-
# Set a breakpoint inside main's loop
56-
src_file = lldb.SBFileSpec("test_loop_function_call.c")
57-
breakpoint = target.BreakpointCreateByName("main")
58-
self.assertGreater(breakpoint.GetNumLocations(), 0)
59-
60-
process = target.LaunchSimple(None, None, self.get_process_working_directory())
61-
self.assertTrue(process, "Failed to launch process")
62-
self.assertEqual(process.GetState(), lldb.eStateStopped)
63-
64-
frame = process.GetSelectedThread().GetSelectedFrame()
65-
disasm = frame.Disassemble()
66-
print(disasm)
67-
68-
# Check that we have DWARF annotations for variables
69-
self.assertIn("a = ", disasm)
70-
self.assertIn("b = ", disasm)
71-
self.assertIn("c = ", disasm)
72-
self.assertIn("DW_OP", disasm)
73-
self.assertNotIn("<decoding error>", disasm)
74-
75-
76-
@no_debug_info_test
77-
def test_b_multiple_stack_variables_O1(self):
78-
"""
79-
Tests disassembler output for b_multiple_stack_variables.c built with -O1.
80-
Due to optimizations, some variables may be optimized out.
81-
We only check for 'c' and ensure no decoding errors appear.
82-
"""
83-
self.build(dictionary={
84-
'C_SOURCES': 'b_multiple_stack_variables.c',
85-
'CFLAGS_EXTRAS': '-g -O1'
86-
})
87-
exe = self.getBuildArtifact("a.out")
88-
target = self.dbg.CreateTarget(exe)
89-
self.assertTrue(target)
90-
91-
breakpoint = target.BreakpointCreateByName("main")
92-
self.assertGreater(breakpoint.GetNumLocations(), 0)
93-
94-
process = target.LaunchSimple(None, None, self.get_process_working_directory())
95-
self.assertTrue(process, "Failed to launch process")
96-
self.assertEqual(process.GetState(), lldb.eStateStopped)
97-
98-
frame = process.GetSelectedThread().GetSelectedFrame()
99-
disasm = frame.Disassemble()
100-
print(disasm)
101-
102-
self.assertIn("c = ", disasm)
103-
self.assertIn("DW_OP", disasm)
104-
self.assertNotIn("<decoding error>", disasm)
105-
106-
107-
@no_debug_info_test
108-
def test_c_variable_passed_to_another_function(self):
109-
"""
110-
Tests disassembler output for c_variable_passed_to_another_function.c.
111-
This test checks that a variable passed to another function is annotated
112-
with DWARF and that its location is distinct. It also ensures that no decoding errors appear.
113-
"""
114-
self.build(dictionary={
115-
'C_SOURCES': 'c_variable_passed_to_another_function.c',
116-
'CFLAGS_EXTRAS': '-g -O0'
117-
})
118-
exe = self.getBuildArtifact("a.out")
119-
target = self.dbg.CreateTarget(exe)
120-
self.assertTrue(target)
121-
122-
breakpoint = target.BreakpointCreateByName("main")
123-
self.assertGreater(breakpoint.GetNumLocations(), 0)
124-
125-
process = target.LaunchSimple(None, None, self.get_process_working_directory())
126-
self.assertTrue(process, "Failed to launch process")
127-
self.assertEqual(process.GetState(), lldb.eStateStopped)
128-
129-
frame = process.GetSelectedThread().GetSelectedFrame()
130-
disasm = frame.Disassemble()
131-
print(disasm)
132-
133-
self.assertIn("x = ", disasm)
134-
self.assertIn("DW_OP", disasm)
135-
self.assertNotIn("<decoding error>", disasm)
136-
137-
138-
@no_debug_info_test
139-
def test_c_variable_passed_to_another_function_O1(self):
140-
"""
141-
Tests disassembler output for c_variable_passed_to_another_function.c built with -O1.
142-
"""
143-
self.build(dictionary={
144-
'C_SOURCES': 'c_variable_passed_to_another_function.c',
145-
'CFLAGS_EXTRAS': '-g -O1'
146-
})
147-
exe = self.getBuildArtifact("a.out")
148-
target = self.dbg.CreateTarget(exe)
149-
self.assertTrue(target)
150-
151-
breakpoint = target.BreakpointCreateByName("main")
152-
self.assertGreater(breakpoint.GetNumLocations(), 0)
153-
154-
process = target.LaunchSimple(None, None, self.get_process_working_directory())
155-
self.assertTrue(process, "Failed to launch process")
156-
self.assertEqual(process.GetState(), lldb.eStateStopped)
157-
158-
frame = process.GetSelectedThread().GetSelectedFrame()
159-
disasm = frame.Disassemble()
160-
print(disasm)
161-
162-
self.assertIn("x = ", disasm)
163-
self.assertIn("arg = ", disasm)
164-
self.assertIn("DW_OP", disasm)
165-
self.assertNotIn("<decoding error>", disasm)
166-
167-
@no_debug_info_test
168-
def test_d_original_example(self):
169-
"""
170-
Tests disassembler output for d_original_example.c.
171-
This test checks that the disassembly includes basic DWARF variable annotations
172-
and that local variables in the main function are shown with DW_OP locations.
173-
Additionally, it verifies that the disassembly does not contain decoding errors.
174-
"""
175-
self.build(dictionary={
176-
'C_SOURCES': 'd_original_example.c',
177-
'CFLAGS_EXTRAS': '-g -O0'
178-
})
179-
exe = self.getBuildArtifact("a.out")
180-
target = self.dbg.CreateTarget(exe)
181-
self.assertTrue(target)
182-
183-
breakpoint = target.BreakpointCreateByName("main")
184-
self.assertGreater(breakpoint.GetNumLocations(), 0)
185-
186-
process = target.LaunchSimple(None, None, self.get_process_working_directory())
187-
self.assertTrue(process, "Failed to launch process")
188-
self.assertEqual(process.GetState(), lldb.eStateStopped)
189-
190-
frame = process.GetSelectedThread().GetSelectedFrame()
191-
disasm = frame.Disassemble()
192-
print(disasm)
193-
194-
self.assertIn("argc = ", disasm)
195-
self.assertIn("argv = ", disasm)
196-
self.assertIn("i = ", disasm)
197-
self.assertIn("DW_OP", disasm)
198-
self.assertNotIn("<decoding error>", disasm)
199-
200-
@no_debug_info_test
2015
def test_d_original_example_O1(self):
2026
"""
2037
Tests disassembler output for d_original_example.c built with -O1.
@@ -224,68 +28,8 @@ def test_d_original_example_O1(self):
22428
self.assertIn("argc = ", disasm)
22529
self.assertIn("argv = ", disasm)
22630
self.assertIn("i = ", disasm)
227-
self.assertIn("DW_OP_reg", disasm)
228-
self.assertIn("DW_OP_stack_value", disasm)
229-
self.assertNotIn("<decoding error>", disasm)
230-
231-
232-
@no_debug_info_test
233-
def test_e_control_flow_edge(self):
234-
"""
235-
Tests disassembler output for e_control_flow_edge.c with a focus on control flow edges.
236-
This test checks that the disassembly includes basic DWARF variable annotations
237-
and that local variables in the main function are shown with DW_OP locations.
238-
Additionally, it verifies that the disassembly does not contain decoding errors.
239-
"""
240-
self.build(dictionary={
241-
'C_SOURCES': 'e_control_flow_edge.c',
242-
'CFLAGS_EXTRAS': '-g -O0'
243-
})
244-
exe = self.getBuildArtifact("a.out")
245-
target = self.dbg.CreateTarget(exe)
246-
self.assertTrue(target)
247-
248-
breakpoint = target.BreakpointCreateByName("main")
249-
self.assertGreater(breakpoint.GetNumLocations(), 0)
250-
251-
process = target.LaunchSimple(None, None, self.get_process_working_directory())
252-
self.assertTrue(process, "Failed to launch process")
253-
self.assertEqual(process.GetState(), lldb.eStateStopped)
254-
255-
frame = process.GetSelectedThread().GetSelectedFrame()
256-
disasm = frame.Disassemble()
257-
print(disasm)
258-
259-
self.assertIn("a = ", disasm)
260-
self.assertIn("b = ", disasm)
261-
self.assertIn("DW_OP_", disasm)
262-
self.assertNotIn("<decoding error>", disasm)
263-
264-
@no_debug_info_test
265-
def test_e_control_flow_edge_O1(self):
266-
"""
267-
Tests disassembler output for e_control_flow_edge.c built with -O1.
268-
This test checks that the disassembly annotation does not contain decoding errors.
269-
"""
270-
self.build(dictionary={
271-
'C_SOURCES': 'e_control_flow_edge.c',
272-
'CFLAGS_EXTRAS': '-g -O1'
273-
})
274-
exe = self.getBuildArtifact("a.out")
275-
target = self.dbg.CreateTarget(exe)
276-
self.assertTrue(target)
277-
278-
breakpoint = target.BreakpointCreateByName("main")
279-
self.assertGreater(breakpoint.GetNumLocations(), 0)
280-
281-
process = target.LaunchSimple(None, None, self.get_process_working_directory())
282-
self.assertTrue(process, "Failed to launch process")
283-
self.assertEqual(process.GetState(), lldb.eStateStopped)
284-
285-
frame = process.GetSelectedThread().GetSelectedFrame()
286-
disasm = frame.Disassemble()
287-
print(disasm)
288-
31+
# self.assertIn("DW_OP_reg", disasm)
32+
# self.assertIn("DW_OP_stack_value", disasm)
28933
self.assertNotIn("<decoding error>", disasm)
29034

29135

lldb/test/API/functionalities/rich-disassembler/a_loop_with_local_variable.c

Lines changed: 0 additions & 8 deletions
This file was deleted.

lldb/test/API/functionalities/rich-disassembler/b_multiple_stack_variables.c

Lines changed: 0 additions & 9 deletions
This file was deleted.

lldb/test/API/functionalities/rich-disassembler/c_variable_passed_to_another_function.c

Lines changed: 0 additions & 11 deletions
This file was deleted.

lldb/test/API/functionalities/rich-disassembler/e_control_flow_edge.c

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)