Skip to content

Commit aba4890

Browse files
committed
[lldb] [disassembler] chore: enhance VariableAnnotator to return structured data
Signed-off-by: Nikita B <[email protected]>
1 parent c491c6e commit aba4890

File tree

6 files changed

+384
-19
lines changed

6 files changed

+384
-19
lines changed

lldb/include/lldb/API/SBInstruction.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include "lldb/API/SBData.h"
1313
#include "lldb/API/SBDefines.h"
14+
#include "lldb/API/SBStructuredData.h"
1415

1516
#include <cstdio>
1617

@@ -73,6 +74,23 @@ class LLDB_API SBInstruction {
7374

7475
bool TestEmulation(lldb::SBStream &output_stream, const char *test_file);
7576

77+
/// Get variable annotations for this instruction as structured data.
78+
/// Returns an array of dictionaries, each containing:
79+
/// - "variable_name": string name of the variable
80+
/// - "location_description": string description of where variable is stored
81+
/// ("RDI", "R15", "undef", etc.)
82+
/// - "is_live": boolean indicates if variable is live at this instruction
83+
/// - "start_address": unsigned integer address where this annotation becomes
84+
/// valid
85+
/// - "end_address": unsigned integer address where this annotation becomes
86+
/// invalid
87+
/// - "register_kind": unsigned integer indicating the register numbering
88+
/// scheme
89+
/// - "decl_file": string path to the file where variable is declared
90+
/// - "decl_line": unsigned integer line number where variable is declared
91+
/// - "type_name": string type name of the variable
92+
lldb::SBStructuredData GetVariableAnnotations(lldb::SBTarget target);
93+
7694
protected:
7795
friend class SBInstructionList;
7896

lldb/include/lldb/API/SBStructuredData.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ class SBStructuredData {
153153
friend class SBBreakpointLocation;
154154
friend class SBBreakpointName;
155155
friend class SBTrace;
156+
friend class SBInstruction;
156157
friend class lldb_private::python::SWIGBridge;
157158
friend class lldb_private::lua::SWIGBridge;
158159
friend class SBCommandInterpreter;

lldb/include/lldb/Core/Disassembler.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,21 @@ class Disassembler : public std::enable_shared_from_this<Disassembler>,
566566
const Disassembler &operator=(const Disassembler &) = delete;
567567
};
568568

569+
/// Structured data for a single variable annotation
570+
struct VariableAnnotation {
571+
std::string variable_name;
572+
std::string location_description; // e.g., "r15", "undef", "const_0"
573+
lldb::addr_t start_address; // Where this annotation starts being valid
574+
lldb::addr_t end_address; // Where this annotation ends being valid
575+
bool is_live; // Whether variable is live at this instruction
576+
lldb::RegisterKind
577+
register_kind; // Register numbering scheme for location interpretation
578+
std::optional<std::string>
579+
decl_file; // Source file where variable was declared
580+
std::optional<uint32_t> decl_line; // Line number where variable was declared
581+
std::optional<std::string> type_name; // Variable's type name
582+
};
583+
569584
/// Tracks live variable annotations across instructions and produces
570585
/// per-instruction "events" like `name = RDI` or `name = <undef>`.
571586
class VariableAnnotator {
@@ -574,16 +589,39 @@ class VariableAnnotator {
574589
std::string name;
575590
/// Last printed location (empty means <undef>).
576591
std::string last_loc;
592+
/// Address range where this variable state is valid.
593+
lldb::addr_t start_address;
594+
lldb::addr_t end_address;
595+
/// Register numbering scheme for location interpretation.
596+
lldb::RegisterKind register_kind;
597+
598+
std::optional<std::string> decl_file;
599+
std::optional<uint32_t> decl_line;
600+
std::optional<std::string> type_name;
577601
};
578602

579603
// Live state from the previous instruction, keyed by Variable::GetID().
580604
llvm::DenseMap<lldb::user_id_t, VarState> Live_;
581605

606+
static constexpr const char *kUndefLocation = "undef";
607+
582608
public:
583609
/// Compute annotation strings for a single instruction and update `Live_`.
584610
/// Returns only the events that should be printed *at this instruction*.
585611
std::vector<std::string> annotate(Instruction &inst, Target &target,
586612
const lldb::ModuleSP &module_sp);
613+
614+
/// Compute structured annotation data for a single instruction and update
615+
/// `Live_`. Returns structured data for all variables relevant at this
616+
/// instruction.
617+
std::vector<VariableAnnotation>
618+
annotateStructured(Instruction &inst, Target &target,
619+
const lldb::ModuleSP &module_sp);
620+
621+
private:
622+
VariableAnnotation createAnnotation(
623+
const VarState &var_state, bool is_live,
624+
const std::optional<std::string> &location_desc = std::nullopt);
587625
};
588626

589627
} // namespace lldb_private

lldb/source/API/SBInstruction.cpp

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@
1010
#include "lldb/Utility/Instrumentation.h"
1111

1212
#include "lldb/API/SBAddress.h"
13-
#include "lldb/API/SBFrame.h"
1413
#include "lldb/API/SBFile.h"
14+
#include "lldb/API/SBFrame.h"
1515

1616
#include "lldb/API/SBStream.h"
17+
#include "lldb/API/SBStructuredData.h"
1718
#include "lldb/API/SBTarget.h"
1819
#include "lldb/Core/Disassembler.h"
1920
#include "lldb/Core/EmulateInstruction.h"
@@ -26,6 +27,7 @@
2627
#include "lldb/Utility/ArchSpec.h"
2728
#include "lldb/Utility/DataBufferHeap.h"
2829
#include "lldb/Utility/DataExtractor.h"
30+
#include "lldb/Utility/StructuredData.h"
2931

3032
#include <memory>
3133

@@ -163,7 +165,8 @@ const char *SBInstruction::GetComment(SBTarget target) {
163165
return ConstString(inst_sp->GetComment(&exe_ctx)).GetCString();
164166
}
165167

166-
lldb::InstructionControlFlowKind SBInstruction::GetControlFlowKind(lldb::SBTarget target) {
168+
lldb::InstructionControlFlowKind
169+
SBInstruction::GetControlFlowKind(lldb::SBTarget target) {
167170
LLDB_INSTRUMENT_VA(this, target);
168171

169172
lldb::InstructionSP inst_sp(GetOpaque());
@@ -347,3 +350,67 @@ bool SBInstruction::TestEmulation(lldb::SBStream &output_stream,
347350
return inst_sp->TestEmulation(output_stream.ref(), test_file);
348351
return false;
349352
}
353+
354+
lldb::SBStructuredData
355+
SBInstruction::GetVariableAnnotations(lldb::SBTarget target) {
356+
LLDB_INSTRUMENT_VA(this, target);
357+
358+
SBStructuredData result;
359+
360+
if (!m_opaque_sp || !m_opaque_sp->IsValid() || !target.IsValid()) {
361+
return result;
362+
}
363+
364+
lldb::InstructionSP inst_sp = m_opaque_sp->GetSP();
365+
lldb::TargetSP target_sp = target.GetSP();
366+
367+
if (!inst_sp || !target_sp) {
368+
return result;
369+
}
370+
371+
const Address &addr = inst_sp->GetAddress();
372+
ModuleSP module_sp = addr.GetModule();
373+
374+
if (!module_sp) {
375+
return result;
376+
}
377+
378+
VariableAnnotator annotator;
379+
std::vector<VariableAnnotation> annotations =
380+
annotator.annotateStructured(*inst_sp, *target_sp, module_sp);
381+
382+
auto array_sp = std::make_shared<StructuredData::Array>();
383+
384+
for (const auto &ann : annotations) {
385+
auto dict_sp = std::make_shared<StructuredData::Dictionary>();
386+
387+
dict_sp->AddStringItem("variable_name", ann.variable_name);
388+
dict_sp->AddStringItem("location_description", ann.location_description);
389+
dict_sp->AddBooleanItem("is_live", ann.is_live);
390+
dict_sp->AddItem(
391+
"start_address",
392+
std::make_shared<StructuredData::UnsignedInteger>(ann.start_address));
393+
dict_sp->AddItem(
394+
"end_address",
395+
std::make_shared<StructuredData::UnsignedInteger>(ann.end_address));
396+
dict_sp->AddItem(
397+
"register_kind",
398+
std::make_shared<StructuredData::UnsignedInteger>(ann.register_kind));
399+
if (ann.decl_file.has_value()) {
400+
dict_sp->AddStringItem("decl_file", *ann.decl_file);
401+
}
402+
if (ann.decl_line.has_value()) {
403+
dict_sp->AddItem(
404+
"decl_line",
405+
std::make_shared<StructuredData::UnsignedInteger>(*ann.decl_line));
406+
}
407+
if (ann.type_name.has_value()) {
408+
dict_sp->AddStringItem("type_name", *ann.type_name);
409+
}
410+
411+
array_sp->AddItem(dict_sp);
412+
}
413+
414+
result.m_impl_up->SetObjectSP(array_sp);
415+
return result;
416+
}

lldb/source/Core/Disassembler.cpp

Lines changed: 87 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -302,14 +302,39 @@ bool Disassembler::ElideMixedSourceAndDisassemblyLine(
302302
std::vector<std::string>
303303
VariableAnnotator::annotate(Instruction &inst, Target &target,
304304
const lldb::ModuleSP &module_sp) {
305+
auto structured_annotations = annotateStructured(inst, target, module_sp);
306+
305307
std::vector<std::string> events;
308+
events.reserve(structured_annotations.size());
309+
310+
for (const auto &annotation : structured_annotations) {
311+
std::string display_string;
312+
display_string =
313+
llvm::formatv(
314+
"{0} = {1}", annotation.variable_name,
315+
annotation.location_description == VariableAnnotator::kUndefLocation
316+
? llvm::formatv("<{0}>", VariableAnnotator::kUndefLocation)
317+
.str()
318+
: annotation.location_description)
319+
.str();
320+
events.push_back(display_string);
321+
}
322+
323+
return events;
324+
}
325+
326+
std::vector<VariableAnnotation>
327+
VariableAnnotator::annotateStructured(Instruction &inst, Target &target,
328+
const lldb::ModuleSP &module_sp) {
329+
std::vector<VariableAnnotation> annotations;
306330

307-
// If we lost module context, everything becomes <undef>.
331+
// If we lost module context, mark all live variables as undefined.
308332
if (!module_sp) {
309-
for (const auto &KV : Live_)
310-
events.emplace_back(llvm::formatv("{0} = <undef>", KV.second.name).str());
333+
for (const auto &KV : Live_) {
334+
annotations.push_back(createAnnotation(KV.second, false, kUndefLocation));
335+
}
311336
Live_.clear();
312-
return events;
337+
return annotations;
313338
}
314339

315340
// Resolve function/block at this *file* address.
@@ -319,10 +344,11 @@ VariableAnnotator::annotate(Instruction &inst, Target &target,
319344
if (!module_sp->ResolveSymbolContextForAddress(iaddr, mask, sc) ||
320345
!sc.function) {
321346
// No function context: everything dies here.
322-
for (const auto &KV : Live_)
323-
events.emplace_back(llvm::formatv("{0} = <undef>", KV.second.name).str());
347+
for (const auto &KV : Live_) {
348+
annotations.push_back(createAnnotation(KV.second, false, kUndefLocation));
349+
}
324350
Live_.clear();
325-
return events;
351+
return annotations;
326352
}
327353

328354
// Collect in-scope variables for this instruction into Current.
@@ -376,8 +402,35 @@ VariableAnnotator::annotate(Instruction &inst, Target &target,
376402
if (loc.empty())
377403
continue;
378404

379-
Current.try_emplace(v->GetID(),
380-
VarState{std::string(name), std::string(loc)});
405+
lldb::addr_t start_addr = inst.GetAddress().GetFileAddress();
406+
lldb::addr_t end_addr = LLDB_INVALID_ADDRESS;
407+
if (entry.file_range.has_value()) {
408+
start_addr = entry.file_range->GetBaseAddress().GetFileAddress();
409+
end_addr = start_addr + entry.file_range->GetByteSize();
410+
}
411+
412+
std::optional<std::string> decl_file;
413+
std::optional<uint32_t> decl_line;
414+
std::optional<std::string> type_name;
415+
416+
const Declaration &decl = v->GetDeclaration();
417+
if (decl.GetFile()) {
418+
decl_file = decl.GetFile().GetFilename().AsCString();
419+
if (decl.GetLine() > 0) {
420+
decl_line = decl.GetLine();
421+
}
422+
}
423+
424+
if (Type *type = v->GetType()) {
425+
if (const char *type_str = type->GetName().AsCString()) {
426+
type_name = type_str;
427+
}
428+
}
429+
430+
Current.try_emplace(
431+
v->GetID(), VarState{std::string(name), std::string(loc), start_addr,
432+
end_addr, entry.expr->GetRegisterKind(), decl_file,
433+
decl_line, type_name});
381434
}
382435

383436
// Diff Live_ → Current.
@@ -387,24 +440,41 @@ VariableAnnotator::annotate(Instruction &inst, Target &target,
387440
auto it = Live_.find(KV.first);
388441
if (it == Live_.end()) {
389442
// Newly live.
390-
events.emplace_back(
391-
llvm::formatv("{0} = {1}", KV.second.name, KV.second.last_loc).str());
443+
annotations.push_back(createAnnotation(KV.second, true));
392444
} else if (it->second.last_loc != KV.second.last_loc) {
393445
// Location changed.
394-
events.emplace_back(
395-
llvm::formatv("{0} = {1}", KV.second.name, KV.second.last_loc).str());
446+
annotations.push_back(createAnnotation(KV.second, true));
396447
}
397448
}
398449

399-
// 2) Ends: anything that was live but is not in Current becomes <undef>.
450+
// 2) Ends: anything that was live but is not in Current becomes
451+
// <kUndefLocation>.
400452
for (const auto &KV : Live_) {
401-
if (!Current.count(KV.first))
402-
events.emplace_back(llvm::formatv("{0} = <undef>", KV.second.name).str());
453+
if (!Current.count(KV.first)) {
454+
annotations.push_back(createAnnotation(KV.second, false, kUndefLocation));
455+
}
403456
}
404457

405458
// Commit new state.
406459
Live_ = std::move(Current);
407-
return events;
460+
return annotations;
461+
}
462+
463+
VariableAnnotation VariableAnnotator::createAnnotation(
464+
const VarState &var_state, bool is_live,
465+
const std::optional<std::string> &location_desc) {
466+
VariableAnnotation annotation;
467+
annotation.variable_name = var_state.name;
468+
annotation.location_description =
469+
location_desc.has_value() ? *location_desc : var_state.last_loc;
470+
annotation.start_address = var_state.start_address;
471+
annotation.end_address = var_state.end_address;
472+
annotation.is_live = is_live;
473+
annotation.register_kind = var_state.register_kind;
474+
annotation.decl_file = var_state.decl_file;
475+
annotation.decl_line = var_state.decl_line;
476+
annotation.type_name = var_state.type_name;
477+
return annotation;
408478
}
409479

410480
void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,

0 commit comments

Comments
 (0)