From 7d0365815d838ba7ff926f6b9f5a8faf4590e052 Mon Sep 17 00:00:00 2001 From: Nikita B Date: Mon, 24 Nov 2025 21:02:52 +0100 Subject: [PATCH 1/5] [lldb] [disassembler] chore: enhance VariableAnnotator to return structured data: introduce VariableAnnotator::AnnotateStructured method Signed-off-by: Nikita B --- lldb/include/lldb/Core/Disassembler.h | 30 ++++++-- lldb/source/Core/Disassembler.cpp | 100 ++++++++++++++++++++------ 2 files changed, 102 insertions(+), 28 deletions(-) diff --git a/lldb/include/lldb/Core/Disassembler.h b/lldb/include/lldb/Core/Disassembler.h index 5de314109b0cc0..a7922d4a4f7034 100644 --- a/lldb/include/lldb/Core/Disassembler.h +++ b/lldb/include/lldb/Core/Disassembler.h @@ -570,24 +570,40 @@ class Disassembler : public std::enable_shared_from_this, const Disassembler &operator=(const Disassembler &) = delete; }; +/// Structured data for a single variable annotation. +struct VariableAnnotation { + std::string variable_name; + /// Location description (e.g., "r15", "undef", "const_0"). + std::string location_description; + /// Whether variable is live at this instruction. + bool is_live; + /// Register numbering scheme for location interpretation. + lldb::RegisterKind register_kind; + /// Where this annotation is valid. + std::optional address_range; + /// Source file where variable was declared. + std::optional decl_file; + /// Line number where variable was declared. + std::optional decl_line; + /// Variable's type name. + std::optional type_name; +}; + /// Tracks live variable annotations across instructions and produces /// per-instruction "events" like `name = RDI` or `name = `. class VariableAnnotator { - struct VarState { - /// Display name. - std::string name; - /// Last printed location (empty means ). - std::string last_loc; - }; // Live state from the previous instruction, keyed by Variable::GetID(). - llvm::DenseMap m_live_vars; + llvm::DenseMap m_live_vars; public: /// Compute annotation strings for a single instruction and update /// `m_live_vars`. Returns only the events that should be printed *at this /// instruction*. std::vector Annotate(Instruction &inst); + + /// Returns structured data for all variables relevant at this instruction. + std::vector AnnotateStructured(Instruction &inst); }; } // namespace lldb_private diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp index f2eb887986bfb9..a2ebbe63cc9c60 100644 --- a/lldb/source/Core/Disassembler.cpp +++ b/lldb/source/Core/Disassembler.cpp @@ -286,6 +286,10 @@ bool Disassembler::ElideMixedSourceAndDisassemblyLine( return false; } +static constexpr const char *UndefLocation = "undef"; +static const std::string UndefLocationFormatted = + llvm::formatv("<{0}>", UndefLocation).str(); + // 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 @@ -300,16 +304,41 @@ bool Disassembler::ElideMixedSourceAndDisassemblyLine( // disassembled instruction stream, similar to how debug information // enhances source-level debugging. std::vector VariableAnnotator::Annotate(Instruction &inst) { + auto structured_annotations = AnnotateStructured(inst); + std::vector events; + events.reserve(structured_annotations.size()); + + for (const auto &annotation : structured_annotations) { + auto location = (annotation.location_description == UndefLocation + ? UndefLocationFormatted + : annotation.location_description); + + auto display_string = + llvm::formatv("{0} = {1}", annotation.variable_name, location).str(); + + events.push_back(std::move(display_string)); + } + + return events; +} + +std::vector +VariableAnnotator::AnnotateStructured(Instruction &inst) { + std::vector annotations; auto module_sp = inst.GetAddress().GetModule(); - // If we lost module context, everything becomes . + // If we lost module context, mark all live variables as UndefLocation. if (!module_sp) { - for (const auto &KV : m_live_vars) - events.emplace_back(llvm::formatv("{0} = ", KV.second.name).str()); + for (const auto &KV : m_live_vars) { + auto annotation_entity = KV.second; + annotation_entity.is_live = false; + annotation_entity.location_description = UndefLocation; + annotations.push_back(annotation_entity); + } m_live_vars.clear(); - return events; + return annotations; } // Resolve function/block at this *file* address. @@ -319,10 +348,14 @@ std::vector VariableAnnotator::Annotate(Instruction &inst) { if (!module_sp->ResolveSymbolContextForAddress(iaddr, mask, sc) || !sc.function) { // No function context: everything dies here. - for (const auto &KV : m_live_vars) - events.emplace_back(llvm::formatv("{0} = ", KV.second.name).str()); + for (const auto &KV : m_live_vars) { + auto annotation_entity = KV.second; + annotation_entity.is_live = false; + annotation_entity.location_description = UndefLocation; + annotations.push_back(annotation_entity); + } m_live_vars.clear(); - return events; + return annotations; } // Collect in-scope variables for this instruction into current_vars. @@ -349,7 +382,7 @@ std::vector VariableAnnotator::Annotate(Instruction &inst) { // Prefer "register-only" output when we have an ABI. opts.PrintRegisterOnly = static_cast(abi_sp); - llvm::DenseMap current_vars; + llvm::DenseMap current_vars; for (size_t i = 0, e = var_list.GetSize(); i != e; ++i) { lldb::VariableSP v = var_list.GetVariableAtIndex(i); @@ -376,8 +409,26 @@ std::vector VariableAnnotator::Annotate(Instruction &inst) { if (loc.empty()) continue; - current_vars.try_emplace(v->GetID(), - VarState{std::string(name), std::string(loc)}); + std::optional decl_file; + std::optional decl_line; + std::optional type_name; + + const Declaration &decl = v->GetDeclaration(); + if (decl.GetFile()) { + decl_file = decl.GetFile().GetFilename().AsCString(); + if (decl.GetLine() > 0) + decl_line = decl.GetLine(); + } + + if (Type *type = v->GetType()) + if (const char *type_str = type->GetName().AsCString()) + type_name = type_str; + + current_vars.try_emplace( + f v->GetID(), + VariableAnnotation{std::string(name), std::string(loc), true, + entry.expr->GetRegisterKind(), entry.file_range, + decl_file, decl_line, type_name}); } // Diff m_live_vars → current_vars. @@ -387,24 +438,31 @@ std::vector VariableAnnotator::Annotate(Instruction &inst) { auto it = m_live_vars.find(KV.first); if (it == m_live_vars.end()) { // Newly live. - events.emplace_back( - llvm::formatv("{0} = {1}", KV.second.name, KV.second.last_loc).str()); - } else if (it->second.last_loc != KV.second.last_loc) { + auto annotation_entity = KV.second; + annotation_entity.is_live = true; + annotations.push_back(annotation_entity); + } else if (it->second.location_description != + KV.second.location_description) { // Location changed. - events.emplace_back( - llvm::formatv("{0} = {1}", KV.second.name, KV.second.last_loc).str()); + auto annotation_entity = KV.second; + annotation_entity.is_live = true; + annotations.push_back(annotation_entity); } } - // 2) Ends: anything that was live but is not in current_vars becomes . - for (const auto &KV : m_live_vars) { - if (!current_vars.count(KV.first)) - events.emplace_back(llvm::formatv("{0} = ", KV.second.name).str()); - } + // 2) Ends: anything that was live but is not in current_vars becomes + // UndefLocation. + for (const auto &KV : m_live_vars) + if (!current_vars.count(KV.first)) { + auto annotation_entity = KV.second; + annotation_entity.is_live = false; + annotation_entity.location_description = UndefLocation; + annotations.push_back(annotation_entity); + } // Commit new state. m_live_vars = std::move(current_vars); - return events; + return annotations; } void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch, From 7e7ae0509108ac5c9337aaf18236b72a81d4dac9 Mon Sep 17 00:00:00 2001 From: Nikita B Date: Mon, 24 Nov 2025 21:24:03 +0100 Subject: [PATCH 2/5] [lldb] [disassembler] chore: enhance VariableAnnotator to return structured data: introduce VariableAnnotator::AnnotateStructured method: fix typo Signed-off-by: Nikita B --- lldb/source/Core/Disassembler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp index a2ebbe63cc9c60..c695aa5d696b25 100644 --- a/lldb/source/Core/Disassembler.cpp +++ b/lldb/source/Core/Disassembler.cpp @@ -425,7 +425,7 @@ VariableAnnotator::AnnotateStructured(Instruction &inst) { type_name = type_str; current_vars.try_emplace( - f v->GetID(), + v->GetID(), VariableAnnotation{std::string(name), std::string(loc), true, entry.expr->GetRegisterKind(), entry.file_range, decl_file, decl_line, type_name}); From 4b601f8d52aaa82d94096280e15d3d3bfe037610 Mon Sep 17 00:00:00 2001 From: n2h9 <13541181+n2h9@users.noreply.github.com> Date: Wed, 26 Nov 2025 21:51:27 +0100 Subject: [PATCH 3/5] Update lldb/source/Core/Disassembler.cpp Co-authored-by: Jonas Devlieghere --- lldb/source/Core/Disassembler.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp index c695aa5d696b25..0edeeb9d2eac4d 100644 --- a/lldb/source/Core/Disassembler.cpp +++ b/lldb/source/Core/Disassembler.cpp @@ -286,9 +286,8 @@ bool Disassembler::ElideMixedSourceAndDisassemblyLine( return false; } -static constexpr const char *UndefLocation = "undef"; -static const std::string UndefLocationFormatted = - llvm::formatv("<{0}>", UndefLocation).str(); +static constexpr const llvm::StringLiteral kUndefLocation = "undef"; +static contexpr const llvm::StringLiteral kUndefLocationFormatted = ""; // For each instruction, this block attempts to resolve in-scope variables // and determine if the current PC falls within their From 3654a864c24d45d74ac4d393b541a265e6f3a955 Mon Sep 17 00:00:00 2001 From: Nikita B Date: Wed, 26 Nov 2025 22:34:20 +0100 Subject: [PATCH 4/5] [lldb] [disassembler] chore: enhance VariableAnnotator to return structured data: introduce VariableAnnotator::AnnotateStructured method: replace auto with actual type where type is not obvious; add const modifier to local variables which are read only Signed-off-by: Nikita B --- lldb/source/Core/Disassembler.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp index 0edeeb9d2eac4d..985a0ce56e8c29 100644 --- a/lldb/source/Core/Disassembler.cpp +++ b/lldb/source/Core/Disassembler.cpp @@ -287,7 +287,7 @@ bool Disassembler::ElideMixedSourceAndDisassemblyLine( } static constexpr const llvm::StringLiteral kUndefLocation = "undef"; -static contexpr const llvm::StringLiteral kUndefLocationFormatted = ""; +static constexpr const llvm::StringLiteral kUndefLocationFormatted = ""; // For each instruction, this block attempts to resolve in-scope variables // and determine if the current PC falls within their @@ -303,17 +303,19 @@ static contexpr const llvm::StringLiteral kUndefLocationFormatted = ""; // disassembled instruction stream, similar to how debug information // enhances source-level debugging. std::vector VariableAnnotator::Annotate(Instruction &inst) { - auto structured_annotations = AnnotateStructured(inst); + std::vector structured_annotations = + AnnotateStructured(inst); std::vector events; events.reserve(structured_annotations.size()); for (const auto &annotation : structured_annotations) { - auto location = (annotation.location_description == UndefLocation - ? UndefLocationFormatted - : annotation.location_description); + const llvm::StringRef location = + (annotation.location_description == kUndefLocation + ? llvm::StringRef(kUndefLocationFormatted) + : llvm::StringRef(annotation.location_description)); - auto display_string = + const auto display_string = llvm::formatv("{0} = {1}", annotation.variable_name, location).str(); events.push_back(std::move(display_string)); @@ -331,9 +333,9 @@ VariableAnnotator::AnnotateStructured(Instruction &inst) { // If we lost module context, mark all live variables as UndefLocation. if (!module_sp) { for (const auto &KV : m_live_vars) { - auto annotation_entity = KV.second; + VariableAnnotation annotation_entity = KV.second; annotation_entity.is_live = false; - annotation_entity.location_description = UndefLocation; + annotation_entity.location_description = kUndefLocation; annotations.push_back(annotation_entity); } m_live_vars.clear(); @@ -348,9 +350,9 @@ VariableAnnotator::AnnotateStructured(Instruction &inst) { !sc.function) { // No function context: everything dies here. for (const auto &KV : m_live_vars) { - auto annotation_entity = KV.second; + VariableAnnotation annotation_entity = KV.second; annotation_entity.is_live = false; - annotation_entity.location_description = UndefLocation; + annotation_entity.location_description = kUndefLocation; annotations.push_back(annotation_entity); } m_live_vars.clear(); @@ -437,13 +439,13 @@ VariableAnnotator::AnnotateStructured(Instruction &inst) { auto it = m_live_vars.find(KV.first); if (it == m_live_vars.end()) { // Newly live. - auto annotation_entity = KV.second; + VariableAnnotation annotation_entity = KV.second; annotation_entity.is_live = true; annotations.push_back(annotation_entity); } else if (it->second.location_description != KV.second.location_description) { // Location changed. - auto annotation_entity = KV.second; + VariableAnnotation annotation_entity = KV.second; annotation_entity.is_live = true; annotations.push_back(annotation_entity); } @@ -455,7 +457,7 @@ VariableAnnotator::AnnotateStructured(Instruction &inst) { if (!current_vars.count(KV.first)) { auto annotation_entity = KV.second; annotation_entity.is_live = false; - annotation_entity.location_description = UndefLocation; + annotation_entity.location_description = kUndefLocation; annotations.push_back(annotation_entity); } From 110c041b63a4a06bfe6e7ed20b177455798a66f3 Mon Sep 17 00:00:00 2001 From: Nikita B Date: Wed, 26 Nov 2025 22:36:02 +0100 Subject: [PATCH 5/5] [lldb] [disassembler] chore: enhance VariableAnnotator to return structured data: introduce VariableAnnotator::AnnotateStructured method: add std::move when push back annotation entity into vector Signed-off-by: Nikita B --- lldb/source/Core/Disassembler.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp index 985a0ce56e8c29..117c13a1233cd9 100644 --- a/lldb/source/Core/Disassembler.cpp +++ b/lldb/source/Core/Disassembler.cpp @@ -336,7 +336,7 @@ VariableAnnotator::AnnotateStructured(Instruction &inst) { VariableAnnotation annotation_entity = KV.second; annotation_entity.is_live = false; annotation_entity.location_description = kUndefLocation; - annotations.push_back(annotation_entity); + annotations.push_back(std::move(annotation_entity)); } m_live_vars.clear(); return annotations; @@ -353,7 +353,7 @@ VariableAnnotator::AnnotateStructured(Instruction &inst) { VariableAnnotation annotation_entity = KV.second; annotation_entity.is_live = false; annotation_entity.location_description = kUndefLocation; - annotations.push_back(annotation_entity); + annotations.push_back(std::move(annotation_entity)); } m_live_vars.clear(); return annotations; @@ -441,13 +441,13 @@ VariableAnnotator::AnnotateStructured(Instruction &inst) { // Newly live. VariableAnnotation annotation_entity = KV.second; annotation_entity.is_live = true; - annotations.push_back(annotation_entity); + annotations.push_back(std::move(annotation_entity)); } else if (it->second.location_description != KV.second.location_description) { // Location changed. VariableAnnotation annotation_entity = KV.second; annotation_entity.is_live = true; - annotations.push_back(annotation_entity); + annotations.push_back(std::move(annotation_entity)); } } @@ -458,7 +458,7 @@ VariableAnnotator::AnnotateStructured(Instruction &inst) { auto annotation_entity = KV.second; annotation_entity.is_live = false; annotation_entity.location_description = kUndefLocation; - annotations.push_back(annotation_entity); + annotations.push_back(std::move(annotation_entity)); } // Commit new state.