Skip to content

Commit 3d19b02

Browse files
Added --rich option for disassembler annotations and updated SBFrame path**
* Added a new `--rich` (`-R`) command-line option to `CommandObjectDisassemble` to enable rich disassembly annotations for the current invocation. * Plumbed a new `enable_rich_annotations` flag through: * `Disassembler::Disassemble` overloads * `Disassembler::PrintInstructions` * `Instruction::Dump` * `StackFrame::Disassemble` * Updated `StackFrame::Disassemble` to take an optional `bool enable_rich_annotations` (default `false`) so the SB API can request annotated output without CLI involvement. * Ensured annotations are only added when `enable_rich_annotations` is `true`; preserved caching for the non-rich path. * Modified `Options.td` to define the new `--rich` option. * Added/updated API test `TestRichDisassembler.py` to run `disassemble --rich -f` and check annotated output. * Kept default behavior unchanged so existing scripts and IDE integrations are unaffected.
1 parent 79c0a9e commit 3d19b02

File tree

8 files changed

+55
-32
lines changed

8 files changed

+55
-32
lines changed

lldb/include/lldb/Core/Disassembler.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,8 @@ class Instruction {
159159
const SymbolContext *sym_ctx,
160160
const SymbolContext *prev_sym_ctx,
161161
const FormatEntity::Entry *disassembly_addr_format,
162-
size_t max_address_text_size);
162+
size_t max_address_text_size,
163+
bool enable_rich_annotations = false);
163164

164165
virtual bool DoesBranch() = 0;
165166

@@ -443,10 +444,10 @@ class Disassembler : public std::enable_shared_from_this<Disassembler>,
443444
const ExecutionContext &exe_ctx, const Address &start,
444445
Limit limit, bool mixed_source_and_assembly,
445446
uint32_t num_mixed_context_lines, uint32_t options,
446-
Stream &strm);
447+
Stream &strm, bool enable_rich_annotations = false);
447448

448449
static bool Disassemble(Debugger &debugger, const ArchSpec &arch,
449-
StackFrame &frame, Stream &strm);
450+
StackFrame &frame, Stream &strm, bool enable_rich_annotations = false);
450451

451452
// Constructors and Destructors
452453
Disassembler(const ArchSpec &arch, const char *flavor);
@@ -456,7 +457,7 @@ class Disassembler : public std::enable_shared_from_this<Disassembler>,
456457
const ExecutionContext &exe_ctx,
457458
bool mixed_source_and_assembly,
458459
uint32_t num_mixed_context_lines, uint32_t options,
459-
Stream &strm);
460+
Stream &strm, bool enable_rich_annotations = false);
460461

461462
size_t ParseInstructions(Target &target, Address address, Limit limit,
462463
Stream *error_strm_ptr,

lldb/include/lldb/Target/StackFrame.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,8 @@ class StackFrame : public ExecutionContextScope,
321321
///
322322
/// \return
323323
/// C string with the assembly instructions for this function.
324-
const char *Disassemble();
324+
const char *Disassemble(bool enable_rich_annotations = false);
325+
325326

326327
/// Print a description of this frame using the provided frame format.
327328
///

lldb/source/Commands/CommandObjectDisassemble.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,10 @@ Status CommandObjectDisassemble::CommandOptions::SetOptionValue(
154154
}
155155
} break;
156156

157+
case 'R': // --rich
158+
enable_rich_annotations = true;
159+
break;
160+
157161
case '\x01':
158162
force = true;
159163
break;
@@ -180,6 +184,7 @@ void CommandObjectDisassemble::CommandOptions::OptionParsingStarting(
180184
end_addr = LLDB_INVALID_ADDRESS;
181185
symbol_containing_addr = LLDB_INVALID_ADDRESS;
182186
raw = false;
187+
enable_rich_annotations = false;
183188
plugin_name.clear();
184189

185190
Target *target =
@@ -550,7 +555,7 @@ void CommandObjectDisassemble::DoExecute(Args &command,
550555
cpu_string, features_string, m_exe_ctx, cur_range.GetBaseAddress(),
551556
limit, m_options.show_mixed,
552557
m_options.show_mixed ? m_options.num_lines_context : 0, options,
553-
result.GetOutputStream())) {
558+
result.GetOutputStream(), /*enable_rich_annotations=*/m_options.enable_rich_annotations)) {
554559
result.SetStatus(eReturnStatusSuccessFinishResult);
555560
} else {
556561
if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS) {

lldb/source/Commands/CommandObjectDisassemble.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ class CommandObjectDisassemble : public CommandObjectParsed {
7878
// in SetOptionValue if anything the selects a location is set.
7979
lldb::addr_t symbol_containing_addr = 0;
8080
bool force = false;
81+
bool enable_rich_annotations = false;
8182
};
8283

8384
CommandObjectDisassemble(CommandInterpreter &interpreter);

lldb/source/Commands/Options.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,8 @@ let Command = "disassemble" in {
361361
Desc<"Disassemble function containing this address.">;
362362
def disassemble_options_force : Option<"force", "\\x01">, Groups<[2,3,4,5,7]>,
363363
Desc<"Force disassembly of large functions.">;
364+
def disassemble_options_rich : Option<"rich", "R">,
365+
Desc<"Enable rich disassembly annotations for this invocation.">;
364366
}
365367

366368
let Command = "diagnostics dump" in {

lldb/source/Core/Disassembler.cpp

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,8 @@ bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch,
175175
const Address &address, Limit limit,
176176
bool mixed_source_and_assembly,
177177
uint32_t num_mixed_context_lines,
178-
uint32_t options, Stream &strm) {
178+
uint32_t options, Stream &strm,
179+
bool enable_rich_annotations) {
179180
if (!exe_ctx.GetTargetPtr())
180181
return false;
181182

@@ -191,8 +192,9 @@ bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch,
191192
return false;
192193

193194
disasm_sp->PrintInstructions(debugger, arch, exe_ctx,
194-
mixed_source_and_assembly,
195-
num_mixed_context_lines, options, strm);
195+
mixed_source_and_assembly,
196+
num_mixed_context_lines, options, strm,
197+
/*enable_rich_annotations=*/enable_rich_annotations);
196198
return true;
197199
}
198200

@@ -287,7 +289,8 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
287289
const ExecutionContext &exe_ctx,
288290
bool mixed_source_and_assembly,
289291
uint32_t num_mixed_context_lines,
290-
uint32_t options, Stream &strm) {
292+
uint32_t options, Stream &strm,
293+
bool enable_rich_annotations) {
291294
// We got some things disassembled...
292295
size_t num_instructions_found = GetInstructionList().GetSize();
293296

@@ -545,7 +548,8 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
545548
(options & eOptionShowControlFlowKind) != 0;
546549
inst->Dump(&strm, max_opcode_byte_size, true, show_bytes,
547550
show_control_flow_kind, &exe_ctx, &sc, &prev_sc, nullptr,
548-
address_text_size);
551+
address_text_size,
552+
enable_rich_annotations);
549553
strm.EOL();
550554
} else {
551555
break;
@@ -554,7 +558,8 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
554558
}
555559

556560
bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch,
557-
StackFrame &frame, Stream &strm) {
561+
StackFrame &frame, Stream &strm,
562+
bool enable_rich_annotations) {
558563
constexpr const char *plugin_name = nullptr;
559564
constexpr const char *flavor = nullptr;
560565
constexpr const char *cpu = nullptr;
@@ -641,7 +646,8 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
641646
const SymbolContext *sym_ctx,
642647
const SymbolContext *prev_sym_ctx,
643648
const FormatEntity::Entry *disassembly_addr_format,
644-
size_t max_address_text_size) {
649+
size_t max_address_text_size,
650+
bool enable_rich_annotations) {
645651
size_t opcode_column_width = 7;
646652
const size_t operand_column_width = 25;
647653

@@ -801,7 +807,7 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
801807
frame->ChangePC(original_pc);
802808
};
803809

804-
if (exe_ctx && exe_ctx->GetFramePtr()) {
810+
if (enable_rich_annotations && exe_ctx && exe_ctx->GetFramePtr()) {
805811
annotate_variables();
806812
}
807813

lldb/source/Target/StackFrame.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -262,15 +262,22 @@ bool StackFrame::ChangePC(addr_t pc) {
262262
return true;
263263
}
264264

265-
const char *StackFrame::Disassemble() {
265+
const char *StackFrame::Disassemble(bool enable_rich_annotations) {
266266
std::lock_guard<std::recursive_mutex> guard(m_mutex);
267-
if (!m_disassembly.Empty())
268-
return m_disassembly.GetData();
267+
268+
// Keep the existing cache only for the plain (non-rich) path.
269+
if (!enable_rich_annotations) {
270+
if (!m_disassembly.Empty())
271+
return m_disassembly.GetData();
272+
}
269273

270274
ExecutionContext exe_ctx(shared_from_this());
271275
if (Target *target = exe_ctx.GetTargetPtr()) {
272-
Disassembler::Disassemble(target->GetDebugger(), target->GetArchitecture(),
273-
*this, m_disassembly);
276+
Disassembler::Disassemble(target->GetDebugger(),
277+
target->GetArchitecture(),
278+
*this,
279+
m_disassembly,
280+
/*enable_rich_annotations=*/enable_rich_annotations);
274281
}
275282

276283
return m_disassembly.Empty() ? nullptr : m_disassembly.GetData();
Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
from lldbsuite.test.lldbtest import *
22
from lldbsuite.test.decorators import *
3-
3+
import lldb
44

55
class TestRichDisassembler(TestBase):
66
def test_d_original_example_O1(self):
77
"""
8-
Tests disassembler output for d_original_example.c built with -O1.
8+
Tests disassembler output for d_original_example.c built with -O1,
9+
using the CLI with --rich to enable annotations.
910
"""
1011
self.build(
1112
dictionary={"C_SOURCES": "d_original_example.c", "CFLAGS_EXTRAS": "-g -O1"}
@@ -14,20 +15,19 @@ def test_d_original_example_O1(self):
1415
target = self.dbg.CreateTarget(exe)
1516
self.assertTrue(target)
1617

17-
breakpoint = target.BreakpointCreateByName("main")
18-
self.assertGreater(breakpoint.GetNumLocations(), 0)
18+
bp = target.BreakpointCreateByName("main")
19+
self.assertGreater(bp.GetNumLocations(), 0)
1920

2021
process = target.LaunchSimple(None, None, self.get_process_working_directory())
2122
self.assertTrue(process, "Failed to launch process")
2223
self.assertEqual(process.GetState(), lldb.eStateStopped)
2324

24-
frame = process.GetSelectedThread().GetSelectedFrame()
25-
disasm = frame.Disassemble()
26-
print(disasm)
25+
# Run the CLI command and read output from self.res
26+
self.runCmd("disassemble --rich -f", check=True)
27+
out = self.res.GetOutput()
28+
print(out)
2729

28-
self.assertIn("argc = ", disasm)
29-
self.assertIn("argv = ", disasm)
30-
self.assertIn("i = ", disasm)
31-
# self.assertIn("DW_OP_reg", disasm)
32-
# self.assertIn("DW_OP_stack_value", disasm)
33-
self.assertNotIn("<decoding error>", disasm)
30+
self.assertIn("argc = ", out)
31+
self.assertIn("argv = ", out)
32+
self.assertIn("i = ", out)
33+
self.assertNotIn("<decoding error>", out)

0 commit comments

Comments
 (0)