Skip to content

Commit 10fddc4

Browse files
Added basic stateful variable location annotations to disassembly output
This change introduces a simple live-variable tracking system for annotated disassembly. While iterating over instructions, we now maintain an unordered_map keyed by `lldb::user_id_t` to remember each in-scope variable's last known location string. For each instruction: * If a variable is new, print `name = location` and add it to the map. * If a variable's location has changed, print the updated mapping. * If a previously tracked variable is no longer found, print `name = <undef>` and remove it. This produces concise, stateful annotations that only update when needed, reducing noise in the disassembly while still showing variable lifetimes.
1 parent 7b526fc commit 10fddc4

File tree

1 file changed

+83
-28
lines changed

1 file changed

+83
-28
lines changed

lldb/source/Core/Disassembler.cpp

Lines changed: 83 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949

5050
#include <cstdint>
5151
#include <cstring>
52+
#include <unordered_map>
5253
#include <utility>
5354

5455
#include <cassert>
@@ -394,67 +395,121 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
394395
// The goal is to give users helpful live variable hints alongside the
395396
// disassembled instruction stream, similar to how debug information
396397
// enhances source-level debugging.
397-
398+
399+
struct VarState {
400+
std::string name; // display name
401+
std::string last_loc; // last printed location (empty means <undef>)
402+
bool seen_this_inst = false;
403+
};
404+
405+
// Track live variables across instructions (keyed by stable LLDB user_id_t)
406+
std::unordered_map<lldb::user_id_t, VarState> live_vars;
407+
408+
// Stateful annotator: updates live_vars and returns only what should be printed for THIS instruction.
398409
auto annotate_variables = [&](Instruction &inst) -> std::vector<std::string> {
399-
std::vector<std::string> annotations;
410+
std::vector<std::string> events;
400411

401412
StackFrame *frame = exe_ctx.GetFramePtr();
402413
TargetSP target_sp = exe_ctx.GetTargetSP();
403-
if (!frame || !target_sp)
404-
return annotations;
414+
ProcessSP process_sp = exe_ctx.GetProcessSP();
415+
if (!frame || !target_sp || !process_sp)
416+
return events;
417+
418+
// Reset "seen" flags for this instruction
419+
for (auto &kv : live_vars)
420+
kv.second.seen_this_inst = false;
405421

406422
addr_t current_pc = inst.GetAddress().GetLoadAddress(target_sp.get());
407423
addr_t original_pc = frame->GetFrameCodeAddress().GetLoadAddress(target_sp.get());
408424

425+
// We temporarily move the frame PC so variable locations resolve at this inst
409426
if (!frame->ChangePC(current_pc))
410-
return annotations;
427+
return events;
411428

412429
VariableListSP var_list_sp = frame->GetInScopeVariableList(true);
413-
if (!var_list_sp)
414-
return annotations;
430+
if (!var_list_sp) {
431+
// No variables in scope: everything previously live becomes <undef>
432+
for (auto it = live_vars.begin(); it != live_vars.end(); ) {
433+
events.push_back(llvm::formatv("{0} = <undef>", it->second.name).str());
434+
it = live_vars.erase(it);
435+
}
436+
frame->ChangePC(original_pc);
437+
return events;
438+
}
415439

416440
SymbolContext sc = frame->GetSymbolContext(eSymbolContextFunction);
417441
addr_t func_load_addr = sc.function
418-
? sc.function->GetAddress().GetLoadAddress(target_sp.get())
419-
: LLDB_INVALID_ADDRESS;
442+
? sc.function->GetAddress().GetLoadAddress(target_sp.get())
443+
: LLDB_INVALID_ADDRESS;
420444

445+
// Walk all in-scope variables and try to resolve a location
421446
for (const VariableSP &var_sp : *var_list_sp) {
422447
if (!var_sp)
423448
continue;
424449

425-
const char *name = var_sp->GetName().AsCString();
450+
const auto var_id = var_sp->GetID(); // lldb::user_id_t – stable key
451+
const char *name_cstr = var_sp->GetName().AsCString();
452+
llvm::StringRef name = name_cstr ? name_cstr : "<anon>";
453+
426454
auto &expr_list = var_sp->LocationExpressionList();
427455
if (!expr_list.IsValid())
428456
continue;
429457

430-
if (auto entryOrErr = expr_list.GetExpressionEntryAtAddress(func_load_addr, current_pc)) {
431-
auto entry = *entryOrErr;
458+
// Try to get the expression entry for this PC
459+
auto entry_or_err = expr_list.GetExpressionEntryAtAddress(func_load_addr, current_pc);
460+
if (!entry_or_err)
461+
continue;
432462

433-
if (!entry.file_range ||
434-
entry.file_range->ContainsFileAddress(
435-
(current_pc - func_load_addr) + expr_list.GetFuncFileAddress())) {
463+
auto entry = *entry_or_err;
436464

437-
StreamString loc_str;
438-
ABI *abi = exe_ctx.GetProcessPtr()->GetABI().get();
439-
llvm::DIDumpOptions opts;
440-
opts.ShowAddresses = false;
441-
opts.PrintRegisterOnly = true;
465+
// Check range if present
466+
if (entry.file_range &&
467+
!entry.file_range->ContainsFileAddress(
468+
(current_pc - func_load_addr) + expr_list.GetFuncFileAddress()))
469+
continue;
442470

443-
entry.expr->DumpLocation(&loc_str, eDescriptionLevelBrief, abi, opts);
471+
// Render a compact location string
472+
ABI *abi = process_sp->GetABI().get();
473+
llvm::DIDumpOptions opts;
474+
opts.ShowAddresses = false;
475+
opts.PrintRegisterOnly = true;
444476

445-
llvm::StringRef loc_clean = llvm::StringRef(loc_str.GetString()).trim();
446-
if (!loc_clean.empty()) {
447-
annotations.push_back(llvm::formatv("{0} = {1}", name, loc_clean));
448-
}
477+
StreamString loc_str;
478+
entry.expr->DumpLocation(&loc_str, eDescriptionLevelBrief, abi, opts);
479+
llvm::StringRef loc_clean = llvm::StringRef(loc_str.GetString()).trim();
480+
if (loc_clean.empty())
481+
continue;
482+
483+
// Update map + decide if we print
484+
auto it = live_vars.find(var_id);
485+
if (it == live_vars.end()) {
486+
// New var → print
487+
live_vars.emplace(var_id, VarState{std::string(name), loc_clean.str(), true});
488+
events.push_back(llvm::formatv("{0} = {1}", name, loc_clean).str());
489+
} else {
490+
it->second.seen_this_inst = true;
491+
if (it->second.last_loc != loc_clean) {
492+
it->second.last_loc = loc_clean.str();
493+
events.push_back(llvm::formatv("{0} = {1}", it->second.name, loc_clean).str());
449494
}
450495
}
451496
}
452497

498+
// Anything previously live that we didn't see a location for at this inst is now <undef>
499+
for (auto it = live_vars.begin(); it != live_vars.end(); ) {
500+
if (!it->second.seen_this_inst) {
501+
events.push_back(llvm::formatv("{0} = <undef>", it->second.name).str());
502+
it = live_vars.erase(it);
503+
} else {
504+
++it;
505+
}
506+
}
507+
508+
// Restore PC
453509
frame->ChangePC(original_pc);
454-
return annotations;
510+
return events;
455511
};
456512

457-
458513
previous_symbol = nullptr;
459514
SourceLine previous_line;
460515
for (size_t i = 0; i < num_instructions_found; ++i) {
@@ -626,7 +681,7 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
626681
show_control_flow_kind, &exe_ctx, &sc, &prev_sc, nullptr,
627682
address_text_size);
628683

629-
if(enable_rich_annotations){
684+
if (enable_rich_annotations){
630685
std::vector<std::string> annotations = annotate_variables(*inst);
631686
if (!annotations.empty()) {
632687
const size_t annotation_column = 100;

0 commit comments

Comments
 (0)