From aba4890ee4ce4e3c37961a3997d21832f904f599 Mon Sep 17 00:00:00 2001 From: Nikita B Date: Sun, 26 Oct 2025 18:49:43 +0100 Subject: [PATCH 01/20] [lldb] [disassembler] chore: enhance VariableAnnotator to return structured data Signed-off-by: Nikita B --- lldb/include/lldb/API/SBInstruction.h | 18 ++ lldb/include/lldb/API/SBStructuredData.h | 1 + lldb/include/lldb/Core/Disassembler.h | 38 ++++ lldb/source/API/SBInstruction.cpp | 71 +++++++- lldb/source/Core/Disassembler.cpp | 104 +++++++++-- .../TestVariableAnnotationsDisassembler.py | 171 ++++++++++++++++++ 6 files changed, 384 insertions(+), 19 deletions(-) diff --git a/lldb/include/lldb/API/SBInstruction.h b/lldb/include/lldb/API/SBInstruction.h index 755e3b4a47c9b..05e7087f2e679 100644 --- a/lldb/include/lldb/API/SBInstruction.h +++ b/lldb/include/lldb/API/SBInstruction.h @@ -11,6 +11,7 @@ #include "lldb/API/SBData.h" #include "lldb/API/SBDefines.h" +#include "lldb/API/SBStructuredData.h" #include @@ -73,6 +74,23 @@ class LLDB_API SBInstruction { bool TestEmulation(lldb::SBStream &output_stream, const char *test_file); + /// Get variable annotations for this instruction as structured data. + /// Returns an array of dictionaries, each containing: + /// - "variable_name": string name of the variable + /// - "location_description": string description of where variable is stored + /// ("RDI", "R15", "undef", etc.) + /// - "is_live": boolean indicates if variable is live at this instruction + /// - "start_address": unsigned integer address where this annotation becomes + /// valid + /// - "end_address": unsigned integer address where this annotation becomes + /// invalid + /// - "register_kind": unsigned integer indicating the register numbering + /// scheme + /// - "decl_file": string path to the file where variable is declared + /// - "decl_line": unsigned integer line number where variable is declared + /// - "type_name": string type name of the variable + lldb::SBStructuredData GetVariableAnnotations(lldb::SBTarget target); + protected: friend class SBInstructionList; diff --git a/lldb/include/lldb/API/SBStructuredData.h b/lldb/include/lldb/API/SBStructuredData.h index dfd8ec0e180ce..75fb16b795a5a 100644 --- a/lldb/include/lldb/API/SBStructuredData.h +++ b/lldb/include/lldb/API/SBStructuredData.h @@ -153,6 +153,7 @@ class SBStructuredData { friend class SBBreakpointLocation; friend class SBBreakpointName; friend class SBTrace; + friend class SBInstruction; friend class lldb_private::python::SWIGBridge; friend class lldb_private::lua::SWIGBridge; friend class SBCommandInterpreter; diff --git a/lldb/include/lldb/Core/Disassembler.h b/lldb/include/lldb/Core/Disassembler.h index db186dd33d774..0539d4919c096 100644 --- a/lldb/include/lldb/Core/Disassembler.h +++ b/lldb/include/lldb/Core/Disassembler.h @@ -566,6 +566,21 @@ 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; + std::string location_description; // e.g., "r15", "undef", "const_0" + lldb::addr_t start_address; // Where this annotation starts being valid + lldb::addr_t end_address; // Where this annotation ends being valid + bool is_live; // Whether variable is live at this instruction + lldb::RegisterKind + register_kind; // Register numbering scheme for location interpretation + std::optional + decl_file; // Source file where variable was declared + std::optional decl_line; // Line number where variable was declared + std::optional type_name; // Variable's type name +}; + /// Tracks live variable annotations across instructions and produces /// per-instruction "events" like `name = RDI` or `name = `. class VariableAnnotator { @@ -574,16 +589,39 @@ class VariableAnnotator { std::string name; /// Last printed location (empty means ). std::string last_loc; + /// Address range where this variable state is valid. + lldb::addr_t start_address; + lldb::addr_t end_address; + /// Register numbering scheme for location interpretation. + lldb::RegisterKind register_kind; + + std::optional decl_file; + std::optional decl_line; + std::optional type_name; }; // Live state from the previous instruction, keyed by Variable::GetID(). llvm::DenseMap Live_; + static constexpr const char *kUndefLocation = "undef"; + public: /// Compute annotation strings for a single instruction and update `Live_`. /// Returns only the events that should be printed *at this instruction*. std::vector annotate(Instruction &inst, Target &target, const lldb::ModuleSP &module_sp); + + /// Compute structured annotation data for a single instruction and update + /// `Live_`. Returns structured data for all variables relevant at this + /// instruction. + std::vector + annotateStructured(Instruction &inst, Target &target, + const lldb::ModuleSP &module_sp); + +private: + VariableAnnotation createAnnotation( + const VarState &var_state, bool is_live, + const std::optional &location_desc = std::nullopt); }; } // namespace lldb_private diff --git a/lldb/source/API/SBInstruction.cpp b/lldb/source/API/SBInstruction.cpp index 6755089af39a4..8ce7281a99872 100644 --- a/lldb/source/API/SBInstruction.cpp +++ b/lldb/source/API/SBInstruction.cpp @@ -10,10 +10,11 @@ #include "lldb/Utility/Instrumentation.h" #include "lldb/API/SBAddress.h" -#include "lldb/API/SBFrame.h" #include "lldb/API/SBFile.h" +#include "lldb/API/SBFrame.h" #include "lldb/API/SBStream.h" +#include "lldb/API/SBStructuredData.h" #include "lldb/API/SBTarget.h" #include "lldb/Core/Disassembler.h" #include "lldb/Core/EmulateInstruction.h" @@ -26,6 +27,7 @@ #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/StructuredData.h" #include @@ -163,7 +165,8 @@ const char *SBInstruction::GetComment(SBTarget target) { return ConstString(inst_sp->GetComment(&exe_ctx)).GetCString(); } -lldb::InstructionControlFlowKind SBInstruction::GetControlFlowKind(lldb::SBTarget target) { +lldb::InstructionControlFlowKind +SBInstruction::GetControlFlowKind(lldb::SBTarget target) { LLDB_INSTRUMENT_VA(this, target); lldb::InstructionSP inst_sp(GetOpaque()); @@ -347,3 +350,67 @@ bool SBInstruction::TestEmulation(lldb::SBStream &output_stream, return inst_sp->TestEmulation(output_stream.ref(), test_file); return false; } + +lldb::SBStructuredData +SBInstruction::GetVariableAnnotations(lldb::SBTarget target) { + LLDB_INSTRUMENT_VA(this, target); + + SBStructuredData result; + + if (!m_opaque_sp || !m_opaque_sp->IsValid() || !target.IsValid()) { + return result; + } + + lldb::InstructionSP inst_sp = m_opaque_sp->GetSP(); + lldb::TargetSP target_sp = target.GetSP(); + + if (!inst_sp || !target_sp) { + return result; + } + + const Address &addr = inst_sp->GetAddress(); + ModuleSP module_sp = addr.GetModule(); + + if (!module_sp) { + return result; + } + + VariableAnnotator annotator; + std::vector annotations = + annotator.annotateStructured(*inst_sp, *target_sp, module_sp); + + auto array_sp = std::make_shared(); + + for (const auto &ann : annotations) { + auto dict_sp = std::make_shared(); + + dict_sp->AddStringItem("variable_name", ann.variable_name); + dict_sp->AddStringItem("location_description", ann.location_description); + dict_sp->AddBooleanItem("is_live", ann.is_live); + dict_sp->AddItem( + "start_address", + std::make_shared(ann.start_address)); + dict_sp->AddItem( + "end_address", + std::make_shared(ann.end_address)); + dict_sp->AddItem( + "register_kind", + std::make_shared(ann.register_kind)); + if (ann.decl_file.has_value()) { + dict_sp->AddStringItem("decl_file", *ann.decl_file); + } + if (ann.decl_line.has_value()) { + dict_sp->AddItem( + "decl_line", + std::make_shared(*ann.decl_line)); + } + if (ann.type_name.has_value()) { + dict_sp->AddStringItem("type_name", *ann.type_name); + } + + array_sp->AddItem(dict_sp); + } + + result.m_impl_up->SetObjectSP(array_sp); + return result; +} diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp index f2ed1f7395346..9786823676275 100644 --- a/lldb/source/Core/Disassembler.cpp +++ b/lldb/source/Core/Disassembler.cpp @@ -302,14 +302,39 @@ bool Disassembler::ElideMixedSourceAndDisassemblyLine( std::vector VariableAnnotator::annotate(Instruction &inst, Target &target, const lldb::ModuleSP &module_sp) { + auto structured_annotations = annotateStructured(inst, target, module_sp); + std::vector events; + events.reserve(structured_annotations.size()); + + for (const auto &annotation : structured_annotations) { + std::string display_string; + display_string = + llvm::formatv( + "{0} = {1}", annotation.variable_name, + annotation.location_description == VariableAnnotator::kUndefLocation + ? llvm::formatv("<{0}>", VariableAnnotator::kUndefLocation) + .str() + : annotation.location_description) + .str(); + events.push_back(display_string); + } + + return events; +} + +std::vector +VariableAnnotator::annotateStructured(Instruction &inst, Target &target, + const lldb::ModuleSP &module_sp) { + std::vector annotations; - // If we lost module context, everything becomes . + // If we lost module context, mark all live variables as undefined. if (!module_sp) { - for (const auto &KV : Live_) - events.emplace_back(llvm::formatv("{0} = ", KV.second.name).str()); + for (const auto &KV : Live_) { + annotations.push_back(createAnnotation(KV.second, false, kUndefLocation)); + } Live_.clear(); - return events; + return annotations; } // Resolve function/block at this *file* address. @@ -319,10 +344,11 @@ VariableAnnotator::annotate(Instruction &inst, Target &target, if (!module_sp->ResolveSymbolContextForAddress(iaddr, mask, sc) || !sc.function) { // No function context: everything dies here. - for (const auto &KV : Live_) - events.emplace_back(llvm::formatv("{0} = ", KV.second.name).str()); + for (const auto &KV : Live_) { + annotations.push_back(createAnnotation(KV.second, false, kUndefLocation)); + } Live_.clear(); - return events; + return annotations; } // Collect in-scope variables for this instruction into Current. @@ -376,8 +402,35 @@ VariableAnnotator::annotate(Instruction &inst, Target &target, if (loc.empty()) continue; - Current.try_emplace(v->GetID(), - VarState{std::string(name), std::string(loc)}); + lldb::addr_t start_addr = inst.GetAddress().GetFileAddress(); + lldb::addr_t end_addr = LLDB_INVALID_ADDRESS; + if (entry.file_range.has_value()) { + start_addr = entry.file_range->GetBaseAddress().GetFileAddress(); + end_addr = start_addr + entry.file_range->GetByteSize(); + } + + 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.try_emplace( + v->GetID(), VarState{std::string(name), std::string(loc), start_addr, + end_addr, entry.expr->GetRegisterKind(), decl_file, + decl_line, type_name}); } // Diff Live_ → Current. @@ -387,24 +440,41 @@ VariableAnnotator::annotate(Instruction &inst, Target &target, auto it = Live_.find(KV.first); if (it == Live_.end()) { // Newly live. - events.emplace_back( - llvm::formatv("{0} = {1}", KV.second.name, KV.second.last_loc).str()); + annotations.push_back(createAnnotation(KV.second, true)); } else if (it->second.last_loc != KV.second.last_loc) { // Location changed. - events.emplace_back( - llvm::formatv("{0} = {1}", KV.second.name, KV.second.last_loc).str()); + annotations.push_back(createAnnotation(KV.second, true)); } } - // 2) Ends: anything that was live but is not in Current becomes . + // 2) Ends: anything that was live but is not in Current becomes + // . for (const auto &KV : Live_) { - if (!Current.count(KV.first)) - events.emplace_back(llvm::formatv("{0} = ", KV.second.name).str()); + if (!Current.count(KV.first)) { + annotations.push_back(createAnnotation(KV.second, false, kUndefLocation)); + } } // Commit new state. Live_ = std::move(Current); - return events; + return annotations; +} + +VariableAnnotation VariableAnnotator::createAnnotation( + const VarState &var_state, bool is_live, + const std::optional &location_desc) { + VariableAnnotation annotation; + annotation.variable_name = var_state.name; + annotation.location_description = + location_desc.has_value() ? *location_desc : var_state.last_loc; + annotation.start_address = var_state.start_address; + annotation.end_address = var_state.end_address; + annotation.is_live = is_live; + annotation.register_kind = var_state.register_kind; + annotation.decl_file = var_state.decl_file; + annotation.decl_line = var_state.decl_line; + annotation.type_name = var_state.type_name; + return annotation; } void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch, diff --git a/lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py b/lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py index f107efbddddeb..b32ddfbf8cb97 100644 --- a/lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py +++ b/lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py @@ -116,3 +116,174 @@ def test_seed_reg_const_undef(self): print(out) self.assertRegex(out, r"\b(i|argc)\s*=\s*(DW_OP_reg\d+\b|R[A-Z0-9]+)") self.assertNotIn("", out) + + @no_debug_info_test + @skipIf(archs=no_match(["x86_64"])) + def test_structured_annotations_api(self): + """Test GetVariableAnnotations() API returns structured data""" + obj = self._build_obj("d_original_example.o") + target = self._create_target(obj) + + main_symbols = target.FindSymbols("main") + self.assertTrue(main_symbols.IsValid() and main_symbols.GetSize() > 0, + "Could not find 'main' symbol") + + main_symbol = main_symbols.GetContextAtIndex(0).GetSymbol() + start_addr = main_symbol.GetStartAddress() + self.assertTrue(start_addr.IsValid(), "Invalid start address for main") + + instructions = target.ReadInstructions(start_addr, 16) + self.assertGreater(instructions.GetSize(), 0, "No instructions read") + + print(f"\nTesting GetVariableAnnotations() API on {instructions.GetSize()} instructions") + + # Track what we find + found_annotations = False + found_variables = set() + + # Track variable locations to detect changes (for selective printing) + prev_locations = {} + + # Test each instruction + for i in range(instructions.GetSize()): + inst = instructions.GetInstructionAtIndex(i) + self.assertTrue(inst.IsValid(), f"Invalid instruction at index {i}") + + annotations = inst.GetVariableAnnotations(target) + + self.assertIsInstance(annotations, lldb.SBStructuredData, + "GetVariableAnnotations should return SBStructuredData") + + if annotations.GetSize() > 0: + found_annotations = True + + # Track current locations and detect changes + current_locations = {} + should_print = False + + # Validate each annotation + for j in range(annotations.GetSize()): + ann = annotations.GetItemAtIndex(j) + self.assertTrue(ann.IsValid(), + f"Invalid annotation at index {j}") + + self.assertEqual(ann.GetType(), lldb.eStructuredDataTypeDictionary, + "Each annotation should be a dictionary") + + var_name_obj = ann.GetValueForKey("variable_name") + self.assertTrue(var_name_obj.IsValid(), + "Missing 'variable_name' field") + + location_obj = ann.GetValueForKey("location_description") + self.assertTrue(location_obj.IsValid(), + "Missing 'location_description' field") + + is_live_obj = ann.GetValueForKey("is_live") + self.assertTrue(is_live_obj.IsValid(), + "Missing 'is_live' field") + + start_addr_obj = ann.GetValueForKey("start_address") + self.assertTrue(start_addr_obj.IsValid(), + "Missing 'start_address' field") + + end_addr_obj = ann.GetValueForKey("end_address") + self.assertTrue(end_addr_obj.IsValid(), + "Missing 'end_address' field") + + register_kind_obj = ann.GetValueForKey("register_kind") + self.assertTrue(register_kind_obj.IsValid(), + "Missing 'register_kind' field") + + # Extract and validate values + var_name = var_name_obj.GetStringValue(1024) + location = location_obj.GetStringValue(1024) + is_live = is_live_obj.GetBooleanValue() + start_addr = start_addr_obj.GetUnsignedIntegerValue() + end_addr = end_addr_obj.GetUnsignedIntegerValue() + register_kind = register_kind_obj.GetUnsignedIntegerValue() + + # Validate types and values + self.assertIsInstance(var_name, str, "variable_name should be string") + self.assertGreater(len(var_name), 0, "variable_name should not be empty") + + self.assertIsInstance(location, str, "location_description should be string") + self.assertGreater(len(location), 0, "location_description should not be empty") + + self.assertIsInstance(is_live, bool, "is_live should be boolean") + + self.assertIsInstance(start_addr, int, "start_address should be integer") + self.assertIsInstance(end_addr, int, "end_address should be integer") + self.assertGreater(end_addr, start_addr, + "end_address should be greater than start_address") + + self.assertIsInstance(register_kind, int, "register_kind should be integer") + + # Check for expected variables in this function + self.assertIn(var_name, ["argc", "argv", "i"], + f"Unexpected variable name: {var_name}") + + found_variables.add(var_name) + + # Track current location + current_locations[var_name] = location + + # Detect if this is a new variable or location changed + if var_name not in prev_locations or prev_locations[var_name] != location: + should_print = True + + # Check optional fields (may or may not be present) + decl_file_obj = ann.GetValueForKey("decl_file") + if decl_file_obj.IsValid(): + decl_file = decl_file_obj.GetStringValue(1024) + self.assertIsInstance(decl_file, str) + self.assertIn("d_original_example.c", decl_file, + f"Expected source file d_original_example.c in {decl_file}") + + decl_line_obj = ann.GetValueForKey("decl_line") + if decl_line_obj.IsValid(): + decl_line = decl_line_obj.GetUnsignedIntegerValue() + self.assertIsInstance(decl_line, int) + + # Validate declaration line matches the source code (according to d_original_example.c) + if var_name == "argc": + self.assertEqual(decl_line, 3, "argc should be declared on line 3") + elif var_name == "argv": + self.assertEqual(decl_line, 3, "argv should be declared on line 3") + elif var_name == "i": + self.assertEqual(decl_line, 4, "i should be declared on line 4") + + type_name_obj = ann.GetValueForKey("type_name") + if type_name_obj.IsValid(): + type_name = type_name_obj.GetStringValue(1024) + self.assertIsInstance(type_name, str) + + # Validate declaration line matches the source code (according to d_original_example.c) + if var_name == "argc": + self.assertEqual(type_name, "int", "argc should be type 'int'") + elif var_name == "argv": + self.assertEqual(type_name, "char **", "argv should be type 'char **'") + elif var_name == "i": + self.assertEqual(type_name, "int", "i should be type 'int'") + + # Only print if something happened (location changed or variable appeared/disappeared) + if should_print or len(current_locations) != len(prev_locations): + print(f"\nInstruction {i} at {inst.GetAddress()}: {annotations.GetSize()} annotations") + for var_name, location in current_locations.items(): + change_marker = " <- CHANGED" if var_name in prev_locations and prev_locations[var_name] != location else "" + new_marker = " <- NEW" if var_name not in prev_locations else "" + print(f" {var_name} = {location}{change_marker}{new_marker}") + # Check for disappeared variables + for var_name in prev_locations: + if var_name not in current_locations: + print(f" {var_name} <- GONE") + + # Update tracking + prev_locations = current_locations.copy() + + self.assertTrue(found_annotations, + "Should find at least one instruction with variable annotations") + + self.assertGreater(len(found_variables), 0, + "Should find at least one variable") + + print(f"\nTest complete. Found variables: {found_variables}") From 199a90989a389c1894f2a19b3f792dc47e489911 Mon Sep 17 00:00:00 2001 From: n2h9 <13541181+n2h9@users.noreply.github.com> Date: Tue, 28 Oct 2025 19:12:42 +0100 Subject: [PATCH 02/20] Update lldb/include/lldb/Core/Disassembler.h Co-authored-by: Jonas Devlieghere --- lldb/include/lldb/Core/Disassembler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/include/lldb/Core/Disassembler.h b/lldb/include/lldb/Core/Disassembler.h index 0539d4919c096..0f2407d72e5eb 100644 --- a/lldb/include/lldb/Core/Disassembler.h +++ b/lldb/include/lldb/Core/Disassembler.h @@ -566,7 +566,7 @@ class Disassembler : public std::enable_shared_from_this, const Disassembler &operator=(const Disassembler &) = delete; }; -/// Structured data for a single variable annotation +/// Structured data for a single variable annotation. struct VariableAnnotation { std::string variable_name; std::string location_description; // e.g., "r15", "undef", "const_0" From ea1bf46e6b5c299a4ef1f6abd415b31e08a504e3 Mon Sep 17 00:00:00 2001 From: n2h9 <13541181+n2h9@users.noreply.github.com> Date: Tue, 28 Oct 2025 19:14:58 +0100 Subject: [PATCH 03/20] Update lldb/include/lldb/Core/Disassembler.h Co-authored-by: Jonas Devlieghere --- lldb/include/lldb/Core/Disassembler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/include/lldb/Core/Disassembler.h b/lldb/include/lldb/Core/Disassembler.h index 0f2407d72e5eb..97e392008adfd 100644 --- a/lldb/include/lldb/Core/Disassembler.h +++ b/lldb/include/lldb/Core/Disassembler.h @@ -615,7 +615,7 @@ class VariableAnnotator { /// `Live_`. Returns structured data for all variables relevant at this /// instruction. std::vector - annotateStructured(Instruction &inst, Target &target, + AnnotateStructured(Instruction &inst, Target &target, const lldb::ModuleSP &module_sp); private: From 0331516bb922c2f052745a657ec62542dd3e3a77 Mon Sep 17 00:00:00 2001 From: n2h9 <13541181+n2h9@users.noreply.github.com> Date: Tue, 28 Oct 2025 19:17:12 +0100 Subject: [PATCH 04/20] Update lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py Co-authored-by: Jonas Devlieghere --- .../TestVariableAnnotationsDisassembler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py b/lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py index b32ddfbf8cb97..eccae3a3ac36c 100644 --- a/lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py +++ b/lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py @@ -137,7 +137,7 @@ def test_structured_annotations_api(self): print(f"\nTesting GetVariableAnnotations() API on {instructions.GetSize()} instructions") - # Track what we find + # Track what we find. found_annotations = False found_variables = set() From 2449b6c3bb64205313a4ef953c3b4db78e0c8c91 Mon Sep 17 00:00:00 2001 From: Nikita B Date: Thu, 30 Oct 2025 19:26:58 +0100 Subject: [PATCH 05/20] [lldb] [disassembler] chore: enhance VariableAnnotator to return structured data: remove braces around single line ifs and cycles Signed-off-by: Nikita B --- lldb/source/API/SBInstruction.cpp | 18 ++++++------------ lldb/source/Core/Disassembler.cpp | 21 +++++++-------------- 2 files changed, 13 insertions(+), 26 deletions(-) diff --git a/lldb/source/API/SBInstruction.cpp b/lldb/source/API/SBInstruction.cpp index 8ce7281a99872..f66f4b5d26f9f 100644 --- a/lldb/source/API/SBInstruction.cpp +++ b/lldb/source/API/SBInstruction.cpp @@ -357,23 +357,20 @@ SBInstruction::GetVariableAnnotations(lldb::SBTarget target) { SBStructuredData result; - if (!m_opaque_sp || !m_opaque_sp->IsValid() || !target.IsValid()) { + if (!m_opaque_sp || !m_opaque_sp->IsValid() || !target.IsValid()) return result; - } lldb::InstructionSP inst_sp = m_opaque_sp->GetSP(); lldb::TargetSP target_sp = target.GetSP(); - if (!inst_sp || !target_sp) { + if (!inst_sp || !target_sp) return result; - } const Address &addr = inst_sp->GetAddress(); ModuleSP module_sp = addr.GetModule(); - if (!module_sp) { + if (!module_sp) return result; - } VariableAnnotator annotator; std::vector annotations = @@ -396,17 +393,14 @@ SBInstruction::GetVariableAnnotations(lldb::SBTarget target) { dict_sp->AddItem( "register_kind", std::make_shared(ann.register_kind)); - if (ann.decl_file.has_value()) { + if (ann.decl_file.has_value()) dict_sp->AddStringItem("decl_file", *ann.decl_file); - } - if (ann.decl_line.has_value()) { + if (ann.decl_line.has_value()) dict_sp->AddItem( "decl_line", std::make_shared(*ann.decl_line)); - } - if (ann.type_name.has_value()) { + if (ann.type_name.has_value()) dict_sp->AddStringItem("type_name", *ann.type_name); - } array_sp->AddItem(dict_sp); } diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp index 9786823676275..140b8ce9a7d0d 100644 --- a/lldb/source/Core/Disassembler.cpp +++ b/lldb/source/Core/Disassembler.cpp @@ -330,9 +330,8 @@ VariableAnnotator::annotateStructured(Instruction &inst, Target &target, // If we lost module context, mark all live variables as undefined. if (!module_sp) { - for (const auto &KV : Live_) { + for (const auto &KV : Live_) annotations.push_back(createAnnotation(KV.second, false, kUndefLocation)); - } Live_.clear(); return annotations; } @@ -344,9 +343,8 @@ VariableAnnotator::annotateStructured(Instruction &inst, Target &target, if (!module_sp->ResolveSymbolContextForAddress(iaddr, mask, sc) || !sc.function) { // No function context: everything dies here. - for (const auto &KV : Live_) { + for (const auto &KV : Live_) annotations.push_back(createAnnotation(KV.second, false, kUndefLocation)); - } Live_.clear(); return annotations; } @@ -416,16 +414,13 @@ VariableAnnotator::annotateStructured(Instruction &inst, Target &target, const Declaration &decl = v->GetDeclaration(); if (decl.GetFile()) { decl_file = decl.GetFile().GetFilename().AsCString(); - if (decl.GetLine() > 0) { + if (decl.GetLine() > 0) decl_line = decl.GetLine(); - } } - if (Type *type = v->GetType()) { - if (const char *type_str = type->GetName().AsCString()) { + if (Type *type = v->GetType()) + if (const char *type_str = type->GetName().AsCString()) type_name = type_str; - } - } Current.try_emplace( v->GetID(), VarState{std::string(name), std::string(loc), start_addr, @@ -449,11 +444,9 @@ VariableAnnotator::annotateStructured(Instruction &inst, Target &target, // 2) Ends: anything that was live but is not in Current becomes // . - for (const auto &KV : Live_) { - if (!Current.count(KV.first)) { + for (const auto &KV : Live_) + if (!Current.count(KV.first)) annotations.push_back(createAnnotation(KV.second, false, kUndefLocation)); - } - } // Commit new state. Live_ = std::move(Current); From 839ac8e88356491999ee30e433f837e6871f2cf3 Mon Sep 17 00:00:00 2001 From: Nikita B Date: Fri, 31 Oct 2025 21:35:43 +0100 Subject: [PATCH 06/20] [lldb] [disassembler] chore: enhance VariableAnnotator to return structured data: wrap print instructions with if self.TraceOn(): Signed-off-by: Nikita B --- .../TestVariableAnnotationsDisassembler.py | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py b/lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py index eccae3a3ac36c..b32f5f821af13 100644 --- a/lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py +++ b/lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py @@ -135,7 +135,8 @@ def test_structured_annotations_api(self): instructions = target.ReadInstructions(start_addr, 16) self.assertGreater(instructions.GetSize(), 0, "No instructions read") - print(f"\nTesting GetVariableAnnotations() API on {instructions.GetSize()} instructions") + if self.TraceOn(): + print(f"\nTesting GetVariableAnnotations() API on {instructions.GetSize()} instructions") # Track what we find. found_annotations = False @@ -265,17 +266,18 @@ def test_structured_annotations_api(self): elif var_name == "i": self.assertEqual(type_name, "int", "i should be type 'int'") - # Only print if something happened (location changed or variable appeared/disappeared) - if should_print or len(current_locations) != len(prev_locations): - print(f"\nInstruction {i} at {inst.GetAddress()}: {annotations.GetSize()} annotations") - for var_name, location in current_locations.items(): - change_marker = " <- CHANGED" if var_name in prev_locations and prev_locations[var_name] != location else "" - new_marker = " <- NEW" if var_name not in prev_locations else "" - print(f" {var_name} = {location}{change_marker}{new_marker}") - # Check for disappeared variables - for var_name in prev_locations: - if var_name not in current_locations: - print(f" {var_name} <- GONE") + if self.TraceOn(): + # Only print if something happened (location changed or variable appeared/disappeared) + if should_print or len(current_locations) != len(prev_locations): + print(f"\nInstruction {i} at {inst.GetAddress()}: {annotations.GetSize()} annotations") + for var_name, location in current_locations.items(): + change_marker = " <- CHANGED" if var_name in prev_locations and prev_locations[var_name] != location else "" + new_marker = " <- NEW" if var_name not in prev_locations else "" + print(f" {var_name} = {location}{change_marker}{new_marker}") + # Check for disappeared variables + for var_name in prev_locations: + if var_name not in current_locations: + print(f" {var_name} <- GONE") # Update tracking prev_locations = current_locations.copy() @@ -286,4 +288,5 @@ def test_structured_annotations_api(self): self.assertGreater(len(found_variables), 0, "Should find at least one variable") - print(f"\nTest complete. Found variables: {found_variables}") + if self.TraceOn(): + print(f"\nTest complete. Found variables: {found_variables}") From 52780241f263ae62c370ad47b37de926dff00f88 Mon Sep 17 00:00:00 2001 From: Nikita B Date: Fri, 31 Oct 2025 21:38:17 +0100 Subject: [PATCH 07/20] [lldb] [disassembler] chore: enhance VariableAnnotator to return structured data: end comments with dots in python file Signed-off-by: Nikita B --- .../TestVariableAnnotationsDisassembler.py | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py b/lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py index b32f5f821af13..4fe49a2b44cbd 100644 --- a/lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py +++ b/lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py @@ -142,7 +142,7 @@ def test_structured_annotations_api(self): found_annotations = False found_variables = set() - # Track variable locations to detect changes (for selective printing) + # Track variable locations to detect changes (for selective printing). prev_locations = {} # Test each instruction @@ -158,11 +158,11 @@ def test_structured_annotations_api(self): if annotations.GetSize() > 0: found_annotations = True - # Track current locations and detect changes + # Track current locations and detect changes. current_locations = {} should_print = False - # Validate each annotation + # Validate each annotation. for j in range(annotations.GetSize()): ann = annotations.GetItemAtIndex(j) self.assertTrue(ann.IsValid(), @@ -195,7 +195,7 @@ def test_structured_annotations_api(self): self.assertTrue(register_kind_obj.IsValid(), "Missing 'register_kind' field") - # Extract and validate values + # Extract and validate values. var_name = var_name_obj.GetStringValue(1024) location = location_obj.GetStringValue(1024) is_live = is_live_obj.GetBooleanValue() @@ -203,7 +203,7 @@ def test_structured_annotations_api(self): end_addr = end_addr_obj.GetUnsignedIntegerValue() register_kind = register_kind_obj.GetUnsignedIntegerValue() - # Validate types and values + # Validate types and values. self.assertIsInstance(var_name, str, "variable_name should be string") self.assertGreater(len(var_name), 0, "variable_name should not be empty") @@ -219,20 +219,20 @@ def test_structured_annotations_api(self): self.assertIsInstance(register_kind, int, "register_kind should be integer") - # Check for expected variables in this function + # Check for expected variables in this function. self.assertIn(var_name, ["argc", "argv", "i"], f"Unexpected variable name: {var_name}") found_variables.add(var_name) - # Track current location + # Track current location. current_locations[var_name] = location - # Detect if this is a new variable or location changed + # Detect if this is a new variable or location changed. if var_name not in prev_locations or prev_locations[var_name] != location: should_print = True - # Check optional fields (may or may not be present) + # Check optional fields (may or may not be present). decl_file_obj = ann.GetValueForKey("decl_file") if decl_file_obj.IsValid(): decl_file = decl_file_obj.GetStringValue(1024) @@ -245,7 +245,7 @@ def test_structured_annotations_api(self): decl_line = decl_line_obj.GetUnsignedIntegerValue() self.assertIsInstance(decl_line, int) - # Validate declaration line matches the source code (according to d_original_example.c) + # Validate declaration line matches the source code (according to d_original_example.c). if var_name == "argc": self.assertEqual(decl_line, 3, "argc should be declared on line 3") elif var_name == "argv": @@ -258,7 +258,7 @@ def test_structured_annotations_api(self): type_name = type_name_obj.GetStringValue(1024) self.assertIsInstance(type_name, str) - # Validate declaration line matches the source code (according to d_original_example.c) + # Validate declaration line matches the source code (according to d_original_example.c). if var_name == "argc": self.assertEqual(type_name, "int", "argc should be type 'int'") elif var_name == "argv": @@ -267,19 +267,19 @@ def test_structured_annotations_api(self): self.assertEqual(type_name, "int", "i should be type 'int'") if self.TraceOn(): - # Only print if something happened (location changed or variable appeared/disappeared) + # Only print if something happened (location changed or variable appeared/disappeared). if should_print or len(current_locations) != len(prev_locations): print(f"\nInstruction {i} at {inst.GetAddress()}: {annotations.GetSize()} annotations") for var_name, location in current_locations.items(): change_marker = " <- CHANGED" if var_name in prev_locations and prev_locations[var_name] != location else "" new_marker = " <- NEW" if var_name not in prev_locations else "" print(f" {var_name} = {location}{change_marker}{new_marker}") - # Check for disappeared variables + # Check for disappeared variables. for var_name in prev_locations: if var_name not in current_locations: print(f" {var_name} <- GONE") - # Update tracking + # Update tracking. prev_locations = current_locations.copy() self.assertTrue(found_annotations, From 7055cc06a2d420abea1ceb35207acb6395a35bc4 Mon Sep 17 00:00:00 2001 From: Nikita B Date: Sat, 1 Nov 2025 13:09:33 +0100 Subject: [PATCH 08/20] [lldb] [disassembler] chore: enhance VariableAnnotator to return structured data: rename class method to much declaration and LLDB's coding style Signed-off-by: Nikita B --- lldb/source/API/SBInstruction.cpp | 2 +- lldb/source/Core/Disassembler.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lldb/source/API/SBInstruction.cpp b/lldb/source/API/SBInstruction.cpp index f66f4b5d26f9f..65a75354e0ed1 100644 --- a/lldb/source/API/SBInstruction.cpp +++ b/lldb/source/API/SBInstruction.cpp @@ -374,7 +374,7 @@ SBInstruction::GetVariableAnnotations(lldb::SBTarget target) { VariableAnnotator annotator; std::vector annotations = - annotator.annotateStructured(*inst_sp, *target_sp, module_sp); + annotator.AnnotateStructured(*inst_sp, *target_sp, module_sp); auto array_sp = std::make_shared(); diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp index 140b8ce9a7d0d..0396b1391b22c 100644 --- a/lldb/source/Core/Disassembler.cpp +++ b/lldb/source/Core/Disassembler.cpp @@ -302,7 +302,7 @@ bool Disassembler::ElideMixedSourceAndDisassemblyLine( std::vector VariableAnnotator::annotate(Instruction &inst, Target &target, const lldb::ModuleSP &module_sp) { - auto structured_annotations = annotateStructured(inst, target, module_sp); + auto structured_annotations = AnnotateStructured(inst, target, module_sp); std::vector events; events.reserve(structured_annotations.size()); @@ -324,7 +324,7 @@ VariableAnnotator::annotate(Instruction &inst, Target &target, } std::vector -VariableAnnotator::annotateStructured(Instruction &inst, Target &target, +VariableAnnotator::AnnotateStructured(Instruction &inst, Target &target, const lldb::ModuleSP &module_sp) { std::vector annotations; From 4cee0d76b072801450a41595451346aa9a3b7cbb Mon Sep 17 00:00:00 2001 From: Nikita B Date: Sat, 1 Nov 2025 13:22:29 +0100 Subject: [PATCH 09/20] [lldb] [disassembler] chore: enhance VariableAnnotator to return structured data: update comments to be Doxygen-style Signed-off-by: Nikita B --- lldb/include/lldb/Core/Disassembler.h | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/lldb/include/lldb/Core/Disassembler.h b/lldb/include/lldb/Core/Disassembler.h index 97e392008adfd..49df7cd006151 100644 --- a/lldb/include/lldb/Core/Disassembler.h +++ b/lldb/include/lldb/Core/Disassembler.h @@ -569,16 +569,22 @@ class Disassembler : public std::enable_shared_from_this, /// Structured data for a single variable annotation. struct VariableAnnotation { std::string variable_name; - std::string location_description; // e.g., "r15", "undef", "const_0" - lldb::addr_t start_address; // Where this annotation starts being valid - lldb::addr_t end_address; // Where this annotation ends being valid - bool is_live; // Whether variable is live at this instruction - lldb::RegisterKind - register_kind; // Register numbering scheme for location interpretation - std::optional - decl_file; // Source file where variable was declared - std::optional decl_line; // Line number where variable was declared - std::optional type_name; // Variable's type name + /// Location description (e.g., "r15", "undef", "const_0"). + std::string location_description; + /// Where this annotation starts being valid. + lldb::addr_t start_address; + /// Where this annotation ends being valid. + lldb::addr_t end_address; + /// Whether variable is live at this instruction. + bool is_live; + /// Register numbering scheme for location interpretation. + lldb::RegisterKind register_kind; + /// 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 From c955af308f31cb9639a5b36d8dec58135731e9c6 Mon Sep 17 00:00:00 2001 From: Nikita B Date: Sat, 1 Nov 2025 14:01:08 +0100 Subject: [PATCH 10/20] [lldb] [disassembler] chore: enhance VariableAnnotator to return structured data: remove VarState and keep only VariableAnnotation struct Signed-off-by: Nikita B --- lldb/include/lldb/Core/Disassembler.h | 23 +--------- lldb/source/Core/Disassembler.cpp | 63 ++++++++++++++------------- 2 files changed, 33 insertions(+), 53 deletions(-) diff --git a/lldb/include/lldb/Core/Disassembler.h b/lldb/include/lldb/Core/Disassembler.h index 49df7cd006151..08649e4521d9e 100644 --- a/lldb/include/lldb/Core/Disassembler.h +++ b/lldb/include/lldb/Core/Disassembler.h @@ -590,24 +590,8 @@ struct VariableAnnotation { /// 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; - /// Address range where this variable state is valid. - lldb::addr_t start_address; - lldb::addr_t end_address; - /// Register numbering scheme for location interpretation. - lldb::RegisterKind register_kind; - - std::optional decl_file; - std::optional decl_line; - std::optional type_name; - }; - // Live state from the previous instruction, keyed by Variable::GetID(). - llvm::DenseMap Live_; + llvm::DenseMap Live_; static constexpr const char *kUndefLocation = "undef"; @@ -623,11 +607,6 @@ class VariableAnnotator { std::vector AnnotateStructured(Instruction &inst, Target &target, const lldb::ModuleSP &module_sp); - -private: - VariableAnnotation createAnnotation( - const VarState &var_state, bool is_live, - const std::optional &location_desc = std::nullopt); }; } // namespace lldb_private diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp index 0396b1391b22c..664947437096d 100644 --- a/lldb/source/Core/Disassembler.cpp +++ b/lldb/source/Core/Disassembler.cpp @@ -330,8 +330,12 @@ VariableAnnotator::AnnotateStructured(Instruction &inst, Target &target, // If we lost module context, mark all live variables as undefined. if (!module_sp) { - for (const auto &KV : Live_) - annotations.push_back(createAnnotation(KV.second, false, kUndefLocation)); + for (const auto &KV : Live_) { + auto annotation_entity = KV.second; + annotation_entity.is_live = false; + annotation_entity.location_description = kUndefLocation; + annotations.push_back(annotation_entity); + } Live_.clear(); return annotations; } @@ -343,8 +347,12 @@ VariableAnnotator::AnnotateStructured(Instruction &inst, Target &target, if (!module_sp->ResolveSymbolContextForAddress(iaddr, mask, sc) || !sc.function) { // No function context: everything dies here. - for (const auto &KV : Live_) - annotations.push_back(createAnnotation(KV.second, false, kUndefLocation)); + for (const auto &KV : Live_) { + auto annotation_entity = KV.second; + annotation_entity.is_live = false; + annotation_entity.location_description = kUndefLocation; + annotations.push_back(annotation_entity); + } Live_.clear(); return annotations; } @@ -373,7 +381,7 @@ VariableAnnotator::AnnotateStructured(Instruction &inst, Target &target, // Prefer "register-only" output when we have an ABI. opts.PrintRegisterOnly = static_cast(abi_sp); - llvm::DenseMap Current; + llvm::DenseMap Current; for (size_t i = 0, e = var_list.GetSize(); i != e; ++i) { lldb::VariableSP v = var_list.GetVariableAtIndex(i); @@ -422,10 +430,11 @@ VariableAnnotator::AnnotateStructured(Instruction &inst, Target &target, if (const char *type_str = type->GetName().AsCString()) type_name = type_str; - Current.try_emplace( - v->GetID(), VarState{std::string(name), std::string(loc), start_addr, - end_addr, entry.expr->GetRegisterKind(), decl_file, - decl_line, type_name}); + Current.try_emplace(v->GetID(), + VariableAnnotation{std::string(name), std::string(loc), + start_addr, end_addr, true, + entry.expr->GetRegisterKind(), + decl_file, decl_line, type_name}); } // Diff Live_ → Current. @@ -435,41 +444,33 @@ VariableAnnotator::AnnotateStructured(Instruction &inst, Target &target, auto it = Live_.find(KV.first); if (it == Live_.end()) { // Newly live. - annotations.push_back(createAnnotation(KV.second, true)); - } 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. - annotations.push_back(createAnnotation(KV.second, true)); + 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 becomes // . for (const auto &KV : Live_) - if (!Current.count(KV.first)) - annotations.push_back(createAnnotation(KV.second, false, kUndefLocation)); + if (!Current.count(KV.first)) { + auto annotation_entity = KV.second; + annotation_entity.is_live = false; + annotation_entity.location_description = kUndefLocation; + annotations.push_back(annotation_entity); + } // Commit new state. Live_ = std::move(Current); return annotations; } -VariableAnnotation VariableAnnotator::createAnnotation( - const VarState &var_state, bool is_live, - const std::optional &location_desc) { - VariableAnnotation annotation; - annotation.variable_name = var_state.name; - annotation.location_description = - location_desc.has_value() ? *location_desc : var_state.last_loc; - annotation.start_address = var_state.start_address; - annotation.end_address = var_state.end_address; - annotation.is_live = is_live; - annotation.register_kind = var_state.register_kind; - annotation.decl_file = var_state.decl_file; - annotation.decl_line = var_state.decl_line; - annotation.type_name = var_state.type_name; - return annotation; -} - void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch, const ExecutionContext &exe_ctx, bool mixed_source_and_assembly, From 4d8706dc5441a4d8912e4702a259017403cdd97b Mon Sep 17 00:00:00 2001 From: Nikita B Date: Sat, 1 Nov 2025 19:17:35 +0100 Subject: [PATCH 11/20] [lldb] [disassembler] chore: enhance VariableAnnotator to return structured data: update both annotate and AnnotateStructured to use shared pointer tp module by value Signed-off-by: Nikita B --- lldb/include/lldb/Core/Disassembler.h | 8 ++++---- lldb/source/Core/Disassembler.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lldb/include/lldb/Core/Disassembler.h b/lldb/include/lldb/Core/Disassembler.h index 08649e4521d9e..8fee9575f52bc 100644 --- a/lldb/include/lldb/Core/Disassembler.h +++ b/lldb/include/lldb/Core/Disassembler.h @@ -599,14 +599,14 @@ class VariableAnnotator { /// Compute annotation strings for a single instruction and update `Live_`. /// Returns only the events that should be printed *at this instruction*. std::vector annotate(Instruction &inst, Target &target, - const lldb::ModuleSP &module_sp); + lldb::ModuleSP module_sp); /// Compute structured annotation data for a single instruction and update /// `Live_`. Returns structured data for all variables relevant at this /// instruction. - std::vector - AnnotateStructured(Instruction &inst, Target &target, - const lldb::ModuleSP &module_sp); + std::vector AnnotateStructured(Instruction &inst, + Target &target, + lldb::ModuleSP module_sp); }; } // namespace lldb_private diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp index 664947437096d..789145431adda 100644 --- a/lldb/source/Core/Disassembler.cpp +++ b/lldb/source/Core/Disassembler.cpp @@ -299,9 +299,9 @@ bool Disassembler::ElideMixedSourceAndDisassemblyLine( // The goal is to give users helpful live variable hints alongside the // disassembled instruction stream, similar to how debug information // enhances source-level debugging. -std::vector -VariableAnnotator::annotate(Instruction &inst, Target &target, - const lldb::ModuleSP &module_sp) { +std::vector VariableAnnotator::annotate(Instruction &inst, + Target &target, + lldb::ModuleSP module_sp) { auto structured_annotations = AnnotateStructured(inst, target, module_sp); std::vector events; @@ -325,7 +325,7 @@ VariableAnnotator::annotate(Instruction &inst, Target &target, std::vector VariableAnnotator::AnnotateStructured(Instruction &inst, Target &target, - const lldb::ModuleSP &module_sp) { + lldb::ModuleSP module_sp) { std::vector annotations; // If we lost module context, mark all live variables as undefined. From 573a6a1921d2a24bb1594170bc26d5604328a182 Mon Sep 17 00:00:00 2001 From: Nikita B Date: Sat, 1 Nov 2025 19:24:13 +0100 Subject: [PATCH 12/20] [lldb] [disassembler] chore: enhance VariableAnnotator to return structured data: update both annotate to Annotate class function name to follow lldb coding style Signed-off-by: Nikita B --- lldb/include/lldb/Core/Disassembler.h | 2 +- lldb/source/Core/Disassembler.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lldb/include/lldb/Core/Disassembler.h b/lldb/include/lldb/Core/Disassembler.h index 8fee9575f52bc..b88a908833207 100644 --- a/lldb/include/lldb/Core/Disassembler.h +++ b/lldb/include/lldb/Core/Disassembler.h @@ -598,7 +598,7 @@ class VariableAnnotator { public: /// Compute annotation strings for a single instruction and update `Live_`. /// Returns only the events that should be printed *at this instruction*. - std::vector annotate(Instruction &inst, Target &target, + std::vector Annotate(Instruction &inst, Target &target, lldb::ModuleSP module_sp); /// Compute structured annotation data for a single instruction and update diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp index 789145431adda..4774302dda708 100644 --- a/lldb/source/Core/Disassembler.cpp +++ b/lldb/source/Core/Disassembler.cpp @@ -299,7 +299,7 @@ bool Disassembler::ElideMixedSourceAndDisassemblyLine( // The goal is to give users helpful live variable hints alongside the // disassembled instruction stream, similar to how debug information // enhances source-level debugging. -std::vector VariableAnnotator::annotate(Instruction &inst, +std::vector VariableAnnotator::Annotate(Instruction &inst, Target &target, lldb::ModuleSP module_sp) { auto structured_annotations = AnnotateStructured(inst, target, module_sp); @@ -740,7 +740,7 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch, address_text_size); if ((options & eOptionVariableAnnotations) && target_sp) { - auto annotations = annot.annotate(*inst, *target_sp, module_sp); + auto annotations = annot.Annotate(*inst, *target_sp, module_sp); if (!annotations.empty()) { const size_t annotation_column = 100; inst_line.FillLastLineToColumn(annotation_column, ' '); From c688f7004720f48ddb290323752f6e3ab902a242 Mon Sep 17 00:00:00 2001 From: Nikita B Date: Sat, 1 Nov 2025 19:27:03 +0100 Subject: [PATCH 13/20] [lldb] [disassembler] chore: enhance VariableAnnotator to return structured data: update descriptive comment for AnnotateStructured Signed-off-by: Nikita B --- lldb/include/lldb/Core/Disassembler.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lldb/include/lldb/Core/Disassembler.h b/lldb/include/lldb/Core/Disassembler.h index b88a908833207..709bd713165e8 100644 --- a/lldb/include/lldb/Core/Disassembler.h +++ b/lldb/include/lldb/Core/Disassembler.h @@ -601,9 +601,7 @@ class VariableAnnotator { std::vector Annotate(Instruction &inst, Target &target, lldb::ModuleSP module_sp); - /// Compute structured annotation data for a single instruction and update - /// `Live_`. Returns structured data for all variables relevant at this - /// instruction. + /// Returns structured data for all variables relevant at this instruction. std::vector AnnotateStructured(Instruction &inst, Target &target, lldb::ModuleSP module_sp); From 31e91cf933c0068e214550ee91b4353bf2efc0f2 Mon Sep 17 00:00:00 2001 From: Nikita B Date: Sat, 1 Nov 2025 20:16:23 +0100 Subject: [PATCH 14/20] [lldb] [disassembler] chore: enhance VariableAnnotator to return structured data: use AddressRange instead of start_address and end_address Signed-off-by: Nikita B --- lldb/include/lldb/Core/Disassembler.h | 6 ++---- lldb/source/API/SBInstruction.cpp | 16 ++++++++++------ lldb/source/Core/Disassembler.cpp | 13 +++---------- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/lldb/include/lldb/Core/Disassembler.h b/lldb/include/lldb/Core/Disassembler.h index 709bd713165e8..8a86ae4ddb66e 100644 --- a/lldb/include/lldb/Core/Disassembler.h +++ b/lldb/include/lldb/Core/Disassembler.h @@ -571,14 +571,12 @@ struct VariableAnnotation { std::string variable_name; /// Location description (e.g., "r15", "undef", "const_0"). std::string location_description; - /// Where this annotation starts being valid. - lldb::addr_t start_address; - /// Where this annotation ends being valid. - lldb::addr_t end_address; /// 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. diff --git a/lldb/source/API/SBInstruction.cpp b/lldb/source/API/SBInstruction.cpp index 65a75354e0ed1..992bd3c3fdd5d 100644 --- a/lldb/source/API/SBInstruction.cpp +++ b/lldb/source/API/SBInstruction.cpp @@ -384,12 +384,16 @@ SBInstruction::GetVariableAnnotations(lldb::SBTarget target) { dict_sp->AddStringItem("variable_name", ann.variable_name); dict_sp->AddStringItem("location_description", ann.location_description); dict_sp->AddBooleanItem("is_live", ann.is_live); - dict_sp->AddItem( - "start_address", - std::make_shared(ann.start_address)); - dict_sp->AddItem( - "end_address", - std::make_shared(ann.end_address)); + if (ann.address_range.has_value()) { + const auto &range = *ann.address_range; + dict_sp->AddItem("start_address", + std::make_shared( + range.GetBaseAddress().GetFileAddress())); + dict_sp->AddItem( + "end_address", + std::make_shared( + range.GetBaseAddress().GetFileAddress() + range.GetByteSize())); + } dict_sp->AddItem( "register_kind", std::make_shared(ann.register_kind)); diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp index 4774302dda708..f4327e4bcce85 100644 --- a/lldb/source/Core/Disassembler.cpp +++ b/lldb/source/Core/Disassembler.cpp @@ -408,13 +408,6 @@ VariableAnnotator::AnnotateStructured(Instruction &inst, Target &target, if (loc.empty()) continue; - lldb::addr_t start_addr = inst.GetAddress().GetFileAddress(); - lldb::addr_t end_addr = LLDB_INVALID_ADDRESS; - if (entry.file_range.has_value()) { - start_addr = entry.file_range->GetBaseAddress().GetFileAddress(); - end_addr = start_addr + entry.file_range->GetByteSize(); - } - std::optional decl_file; std::optional decl_line; std::optional type_name; @@ -432,9 +425,9 @@ VariableAnnotator::AnnotateStructured(Instruction &inst, Target &target, Current.try_emplace(v->GetID(), VariableAnnotation{std::string(name), std::string(loc), - start_addr, end_addr, true, - entry.expr->GetRegisterKind(), - decl_file, decl_line, type_name}); + true, entry.expr->GetRegisterKind(), + entry.file_range, decl_file, + decl_line, type_name}); } // Diff Live_ → Current. From f805dbb7909c9a7cfe9b23cc795d8c00556405cf Mon Sep 17 00:00:00 2001 From: n2h9 <13541181+n2h9@users.noreply.github.com> Date: Thu, 6 Nov 2025 20:59:54 +0100 Subject: [PATCH 15/20] Update lldb/include/lldb/Core/Disassembler.h Co-authored-by: Jonas Devlieghere --- lldb/include/lldb/Core/Disassembler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/include/lldb/Core/Disassembler.h b/lldb/include/lldb/Core/Disassembler.h index 8a86ae4ddb66e..d24a8bf593661 100644 --- a/lldb/include/lldb/Core/Disassembler.h +++ b/lldb/include/lldb/Core/Disassembler.h @@ -589,7 +589,7 @@ struct VariableAnnotation { /// per-instruction "events" like `name = RDI` or `name = `. class VariableAnnotator { // Live state from the previous instruction, keyed by Variable::GetID(). - llvm::DenseMap Live_; + llvm::DenseMap m_live_vars; static constexpr const char *kUndefLocation = "undef"; From f03a4485bc8d2c985a39e0fb671fa57cd2d4cd61 Mon Sep 17 00:00:00 2001 From: Nikita B Date: Thu, 6 Nov 2025 21:10:45 +0100 Subject: [PATCH 16/20] [lldb] [disassembler] chore: enhance VariableAnnotator to return structured data: rename Live_ to m_live_vars to follow lldb codding style Signed-off-by: Nikita B --- lldb/source/Core/Disassembler.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp index f4327e4bcce85..43238f4c316d2 100644 --- a/lldb/source/Core/Disassembler.cpp +++ b/lldb/source/Core/Disassembler.cpp @@ -330,13 +330,13 @@ VariableAnnotator::AnnotateStructured(Instruction &inst, Target &target, // If we lost module context, mark all live variables as undefined. if (!module_sp) { - for (const auto &KV : Live_) { + for (const auto &KV : m_live_vars) { auto annotation_entity = KV.second; annotation_entity.is_live = false; annotation_entity.location_description = kUndefLocation; annotations.push_back(annotation_entity); } - Live_.clear(); + m_live_vars.clear(); return annotations; } @@ -347,13 +347,13 @@ VariableAnnotator::AnnotateStructured(Instruction &inst, Target &target, if (!module_sp->ResolveSymbolContextForAddress(iaddr, mask, sc) || !sc.function) { // No function context: everything dies here. - for (const auto &KV : Live_) { + for (const auto &KV : m_live_vars) { auto annotation_entity = KV.second; annotation_entity.is_live = false; annotation_entity.location_description = kUndefLocation; annotations.push_back(annotation_entity); } - Live_.clear(); + m_live_vars.clear(); return annotations; } @@ -430,12 +430,12 @@ VariableAnnotator::AnnotateStructured(Instruction &inst, Target &target, decl_line, type_name}); } - // Diff Live_ → Current. + // Diff m_live_vars → Current. - // 1) Starts/changes: iterate Current and compare with Live_. + // 1) Starts/changes: iterate Current and compare with m_live_vars. for (const auto &KV : Current) { - auto it = Live_.find(KV.first); - if (it == Live_.end()) { + auto it = m_live_vars.find(KV.first); + if (it == m_live_vars.end()) { // Newly live. auto annotation_entity = KV.second; annotation_entity.is_live = true; @@ -451,7 +451,7 @@ VariableAnnotator::AnnotateStructured(Instruction &inst, Target &target, // 2) Ends: anything that was live but is not in Current becomes // . - for (const auto &KV : Live_) + for (const auto &KV : m_live_vars) if (!Current.count(KV.first)) { auto annotation_entity = KV.second; annotation_entity.is_live = false; @@ -460,7 +460,7 @@ VariableAnnotator::AnnotateStructured(Instruction &inst, Target &target, } // Commit new state. - Live_ = std::move(Current); + m_live_vars = std::move(Current); return annotations; } From f10922f01bfbea332ffd58fb595fbbad3efefb7e Mon Sep 17 00:00:00 2001 From: Nikita B Date: Thu, 6 Nov 2025 21:17:15 +0100 Subject: [PATCH 17/20] [lldb] [disassembler] chore: enhance VariableAnnotator to return structured data: rename Current to current_vars to follow lldb codding style and to be consistent with m_live_vars Signed-off-by: Nikita B --- lldb/source/Core/Disassembler.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp index 43238f4c316d2..6e259326cb887 100644 --- a/lldb/source/Core/Disassembler.cpp +++ b/lldb/source/Core/Disassembler.cpp @@ -381,7 +381,7 @@ VariableAnnotator::AnnotateStructured(Instruction &inst, Target &target, // Prefer "register-only" output when we have an ABI. opts.PrintRegisterOnly = static_cast(abi_sp); - llvm::DenseMap Current; + llvm::DenseMap current_vars; for (size_t i = 0, e = var_list.GetSize(); i != e; ++i) { lldb::VariableSP v = var_list.GetVariableAtIndex(i); @@ -423,17 +423,17 @@ VariableAnnotator::AnnotateStructured(Instruction &inst, Target &target, if (const char *type_str = type->GetName().AsCString()) type_name = type_str; - Current.try_emplace(v->GetID(), + current_vars.try_emplace(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. + // Diff m_live_vars → current_vars. - // 1) Starts/changes: iterate Current and compare with m_live_vars. - for (const auto &KV : Current) { + // 1) Starts/changes: iterate current_vars and compare with m_live_vars. + for (const auto &KV : current_vars) { auto it = m_live_vars.find(KV.first); if (it == m_live_vars.end()) { // Newly live. @@ -449,10 +449,10 @@ VariableAnnotator::AnnotateStructured(Instruction &inst, Target &target, } } - // 2) Ends: anything that was live but is not in Current becomes + // 2) Ends: anything that was live but is not in current_vars becomes // . for (const auto &KV : m_live_vars) - if (!Current.count(KV.first)) { + if (!current_vars.count(KV.first)) { auto annotation_entity = KV.second; annotation_entity.is_live = false; annotation_entity.location_description = kUndefLocation; @@ -460,7 +460,7 @@ VariableAnnotator::AnnotateStructured(Instruction &inst, Target &target, } // Commit new state. - m_live_vars = std::move(Current); + m_live_vars = std::move(current_vars); return annotations; } From 35c036f3a98124275cd08d8f7a5d278708e065e4 Mon Sep 17 00:00:00 2001 From: Nikita B Date: Mon, 10 Nov 2025 20:00:32 +0100 Subject: [PATCH 18/20] [lldb] [disassembler] chore: enhance VariableAnnotator to return structured data: replace llvm::formatv with stream, formatting Signed-off-by: Nikita B --- lldb/include/lldb/Core/Disassembler.h | 1 + lldb/source/Core/Disassembler.cpp | 32 +++++++++++++++------------ 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/lldb/include/lldb/Core/Disassembler.h b/lldb/include/lldb/Core/Disassembler.h index d24a8bf593661..0e69bc4526c3b 100644 --- a/lldb/include/lldb/Core/Disassembler.h +++ b/lldb/include/lldb/Core/Disassembler.h @@ -592,6 +592,7 @@ class VariableAnnotator { llvm::DenseMap m_live_vars; static constexpr const char *kUndefLocation = "undef"; + static const std::string kUndefLocationFormatted; public: /// Compute annotation strings for a single instruction and update `Live_`. diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp index 6e259326cb887..cb9c6eb3441d6 100644 --- a/lldb/source/Core/Disassembler.cpp +++ b/lldb/source/Core/Disassembler.cpp @@ -286,6 +286,9 @@ bool Disassembler::ElideMixedSourceAndDisassemblyLine( return false; } +const std::string VariableAnnotator::kUndefLocationFormatted = + llvm::formatv("<{0}>", kUndefLocation).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 @@ -309,15 +312,16 @@ std::vector VariableAnnotator::Annotate(Instruction &inst, for (const auto &annotation : structured_annotations) { std::string display_string; - display_string = - llvm::formatv( - "{0} = {1}", annotation.variable_name, - annotation.location_description == VariableAnnotator::kUndefLocation - ? llvm::formatv("<{0}>", VariableAnnotator::kUndefLocation) - .str() - : annotation.location_description) - .str(); - events.push_back(display_string); + llvm::raw_string_ostream os(display_string); + + os << annotation.variable_name; + os << " = "; + os << (annotation.location_description == VariableAnnotator::kUndefLocation + ? VariableAnnotator::kUndefLocationFormatted + : annotation.location_description); + os.flush(); + + events.push_back(std::move(display_string)); } return events; @@ -423,11 +427,11 @@ VariableAnnotator::AnnotateStructured(Instruction &inst, Target &target, if (const char *type_str = type->GetName().AsCString()) type_name = type_str; - current_vars.try_emplace(v->GetID(), - VariableAnnotation{std::string(name), std::string(loc), - true, entry.expr->GetRegisterKind(), - entry.file_range, decl_file, - decl_line, type_name}); + current_vars.try_emplace( + 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. From 6fe62e48c8c09fc9948c5ea08f9f921c84c7756f Mon Sep 17 00:00:00 2001 From: Nikita B Date: Sat, 15 Nov 2025 14:43:55 +0100 Subject: [PATCH 19/20] [lldb] [disassembler] chore: enhance VariableAnnotator to return structured data: simplify test_structured_annotations_api Signed-off-by: Nikita B --- .../TestVariableAnnotationsDisassembler.py | 105 +----------------- 1 file changed, 6 insertions(+), 99 deletions(-) diff --git a/lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py b/lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py index 4fe49a2b44cbd..d27c4a14ca9e3 100644 --- a/lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py +++ b/lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py @@ -138,13 +138,9 @@ def test_structured_annotations_api(self): if self.TraceOn(): print(f"\nTesting GetVariableAnnotations() API on {instructions.GetSize()} instructions") - # Track what we find. - found_annotations = False + expected_vars = ["argc", "argv", "i"] found_variables = set() - # Track variable locations to detect changes (for selective printing). - prev_locations = {} - # Test each instruction for i in range(instructions.GetSize()): inst = instructions.GetInstructionAtIndex(i) @@ -155,18 +151,14 @@ def test_structured_annotations_api(self): self.assertIsInstance(annotations, lldb.SBStructuredData, "GetVariableAnnotations should return SBStructuredData") - if annotations.GetSize() > 0: - found_annotations = True - - # Track current locations and detect changes. - current_locations = {} - should_print = False + self.assertTrue(annotations.GetSize() > 0, + "GetVariableAnnotations should return non empty array") + if annotations.GetSize() > 0: # Validate each annotation. for j in range(annotations.GetSize()): ann = annotations.GetItemAtIndex(j) - self.assertTrue(ann.IsValid(), - f"Invalid annotation at index {j}") + self.assertTrue(ann.IsValid(), f"Invalid annotation at index {j}") self.assertEqual(ann.GetType(), lldb.eStructuredDataTypeDictionary, "Each annotation should be a dictionary") @@ -195,98 +187,13 @@ def test_structured_annotations_api(self): self.assertTrue(register_kind_obj.IsValid(), "Missing 'register_kind' field") - # Extract and validate values. var_name = var_name_obj.GetStringValue(1024) - location = location_obj.GetStringValue(1024) - is_live = is_live_obj.GetBooleanValue() - start_addr = start_addr_obj.GetUnsignedIntegerValue() - end_addr = end_addr_obj.GetUnsignedIntegerValue() - register_kind = register_kind_obj.GetUnsignedIntegerValue() - - # Validate types and values. - self.assertIsInstance(var_name, str, "variable_name should be string") - self.assertGreater(len(var_name), 0, "variable_name should not be empty") - - self.assertIsInstance(location, str, "location_description should be string") - self.assertGreater(len(location), 0, "location_description should not be empty") - - self.assertIsInstance(is_live, bool, "is_live should be boolean") - - self.assertIsInstance(start_addr, int, "start_address should be integer") - self.assertIsInstance(end_addr, int, "end_address should be integer") - self.assertGreater(end_addr, start_addr, - "end_address should be greater than start_address") - - self.assertIsInstance(register_kind, int, "register_kind should be integer") # Check for expected variables in this function. - self.assertIn(var_name, ["argc", "argv", "i"], + self.assertIn(var_name, expected_vars, f"Unexpected variable name: {var_name}") found_variables.add(var_name) - # Track current location. - current_locations[var_name] = location - - # Detect if this is a new variable or location changed. - if var_name not in prev_locations or prev_locations[var_name] != location: - should_print = True - - # Check optional fields (may or may not be present). - decl_file_obj = ann.GetValueForKey("decl_file") - if decl_file_obj.IsValid(): - decl_file = decl_file_obj.GetStringValue(1024) - self.assertIsInstance(decl_file, str) - self.assertIn("d_original_example.c", decl_file, - f"Expected source file d_original_example.c in {decl_file}") - - decl_line_obj = ann.GetValueForKey("decl_line") - if decl_line_obj.IsValid(): - decl_line = decl_line_obj.GetUnsignedIntegerValue() - self.assertIsInstance(decl_line, int) - - # Validate declaration line matches the source code (according to d_original_example.c). - if var_name == "argc": - self.assertEqual(decl_line, 3, "argc should be declared on line 3") - elif var_name == "argv": - self.assertEqual(decl_line, 3, "argv should be declared on line 3") - elif var_name == "i": - self.assertEqual(decl_line, 4, "i should be declared on line 4") - - type_name_obj = ann.GetValueForKey("type_name") - if type_name_obj.IsValid(): - type_name = type_name_obj.GetStringValue(1024) - self.assertIsInstance(type_name, str) - - # Validate declaration line matches the source code (according to d_original_example.c). - if var_name == "argc": - self.assertEqual(type_name, "int", "argc should be type 'int'") - elif var_name == "argv": - self.assertEqual(type_name, "char **", "argv should be type 'char **'") - elif var_name == "i": - self.assertEqual(type_name, "int", "i should be type 'int'") - - if self.TraceOn(): - # Only print if something happened (location changed or variable appeared/disappeared). - if should_print or len(current_locations) != len(prev_locations): - print(f"\nInstruction {i} at {inst.GetAddress()}: {annotations.GetSize()} annotations") - for var_name, location in current_locations.items(): - change_marker = " <- CHANGED" if var_name in prev_locations and prev_locations[var_name] != location else "" - new_marker = " <- NEW" if var_name not in prev_locations else "" - print(f" {var_name} = {location}{change_marker}{new_marker}") - # Check for disappeared variables. - for var_name in prev_locations: - if var_name not in current_locations: - print(f" {var_name} <- GONE") - - # Update tracking. - prev_locations = current_locations.copy() - - self.assertTrue(found_annotations, - "Should find at least one instruction with variable annotations") - - self.assertGreater(len(found_variables), 0, - "Should find at least one variable") - if self.TraceOn(): print(f"\nTest complete. Found variables: {found_variables}") From 651b407a5f955b21b9b514811570cb3f07df7b3e Mon Sep 17 00:00:00 2001 From: Nikita B Date: Sat, 15 Nov 2025 16:27:16 +0100 Subject: [PATCH 20/20] [lldb] [disassembler] chore: enhance VariableAnnotator to return structured data: remove target and module from Annotate methods signature Signed-off-by: Nikita B --- lldb/include/lldb/API/SBInstruction.h | 2 +- lldb/include/lldb/Core/Disassembler.h | 7 ++----- lldb/source/API/SBInstruction.cpp | 13 +++++-------- lldb/source/Core/Disassembler.cpp | 15 +++++++-------- .../TestVariableAnnotationsDisassembler.py | 2 +- 5 files changed, 16 insertions(+), 23 deletions(-) diff --git a/lldb/include/lldb/API/SBInstruction.h b/lldb/include/lldb/API/SBInstruction.h index 05e7087f2e679..c2111cd3d46af 100644 --- a/lldb/include/lldb/API/SBInstruction.h +++ b/lldb/include/lldb/API/SBInstruction.h @@ -89,7 +89,7 @@ class LLDB_API SBInstruction { /// - "decl_file": string path to the file where variable is declared /// - "decl_line": unsigned integer line number where variable is declared /// - "type_name": string type name of the variable - lldb::SBStructuredData GetVariableAnnotations(lldb::SBTarget target); + lldb::SBStructuredData GetVariableAnnotations(); protected: friend class SBInstructionList; diff --git a/lldb/include/lldb/Core/Disassembler.h b/lldb/include/lldb/Core/Disassembler.h index 0e69bc4526c3b..a37c5edd4e356 100644 --- a/lldb/include/lldb/Core/Disassembler.h +++ b/lldb/include/lldb/Core/Disassembler.h @@ -597,13 +597,10 @@ class VariableAnnotator { public: /// Compute annotation strings for a single instruction and update `Live_`. /// Returns only the events that should be printed *at this instruction*. - std::vector Annotate(Instruction &inst, Target &target, - lldb::ModuleSP module_sp); + std::vector Annotate(Instruction &inst); /// Returns structured data for all variables relevant at this instruction. - std::vector AnnotateStructured(Instruction &inst, - Target &target, - lldb::ModuleSP module_sp); + std::vector AnnotateStructured(Instruction &inst); }; } // namespace lldb_private diff --git a/lldb/source/API/SBInstruction.cpp b/lldb/source/API/SBInstruction.cpp index 992bd3c3fdd5d..ef0db7a37abb8 100644 --- a/lldb/source/API/SBInstruction.cpp +++ b/lldb/source/API/SBInstruction.cpp @@ -351,19 +351,16 @@ bool SBInstruction::TestEmulation(lldb::SBStream &output_stream, return false; } -lldb::SBStructuredData -SBInstruction::GetVariableAnnotations(lldb::SBTarget target) { - LLDB_INSTRUMENT_VA(this, target); +lldb::SBStructuredData SBInstruction::GetVariableAnnotations() { + LLDB_INSTRUMENT_VA(this); SBStructuredData result; - if (!m_opaque_sp || !m_opaque_sp->IsValid() || !target.IsValid()) + if (!m_opaque_sp || !m_opaque_sp->IsValid()) return result; lldb::InstructionSP inst_sp = m_opaque_sp->GetSP(); - lldb::TargetSP target_sp = target.GetSP(); - - if (!inst_sp || !target_sp) + if (!inst_sp) return result; const Address &addr = inst_sp->GetAddress(); @@ -374,7 +371,7 @@ SBInstruction::GetVariableAnnotations(lldb::SBTarget target) { VariableAnnotator annotator; std::vector annotations = - annotator.AnnotateStructured(*inst_sp, *target_sp, module_sp); + annotator.AnnotateStructured(*inst_sp); auto array_sp = std::make_shared(); diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp index cb9c6eb3441d6..9b3d94dfa0982 100644 --- a/lldb/source/Core/Disassembler.cpp +++ b/lldb/source/Core/Disassembler.cpp @@ -302,10 +302,8 @@ const std::string VariableAnnotator::kUndefLocationFormatted = // The goal is to give users helpful live variable hints alongside the // disassembled instruction stream, similar to how debug information // enhances source-level debugging. -std::vector VariableAnnotator::Annotate(Instruction &inst, - Target &target, - lldb::ModuleSP module_sp) { - auto structured_annotations = AnnotateStructured(inst, target, module_sp); +std::vector VariableAnnotator::Annotate(Instruction &inst) { + auto structured_annotations = AnnotateStructured(inst); std::vector events; events.reserve(structured_annotations.size()); @@ -328,10 +326,11 @@ std::vector VariableAnnotator::Annotate(Instruction &inst, } std::vector -VariableAnnotator::AnnotateStructured(Instruction &inst, Target &target, - lldb::ModuleSP module_sp) { +VariableAnnotator::AnnotateStructured(Instruction &inst) { std::vector annotations; + ModuleSP module_sp = inst.GetAddress().GetModule(); + // If we lost module context, mark all live variables as undefined. if (!module_sp) { for (const auto &KV : m_live_vars) { @@ -377,7 +376,7 @@ VariableAnnotator::AnnotateStructured(Instruction &inst, Target &target, const lldb::addr_t func_file = sc.function->GetAddress().GetFileAddress(); // ABI from Target (pretty reg names if plugin exists). Safe to be null. - lldb::ABISP abi_sp = ABI::FindPlugin(nullptr, target.GetArchitecture()); + lldb::ABISP abi_sp = ABI::FindPlugin(nullptr, module_sp->GetArchitecture()); ABI *abi = abi_sp.get(); llvm::DIDumpOptions opts; @@ -737,7 +736,7 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch, address_text_size); if ((options & eOptionVariableAnnotations) && target_sp) { - auto annotations = annot.Annotate(*inst, *target_sp, module_sp); + auto annotations = annot.Annotate(*inst); if (!annotations.empty()) { const size_t annotation_column = 100; inst_line.FillLastLineToColumn(annotation_column, ' '); diff --git a/lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py b/lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py index d27c4a14ca9e3..ea2f099b0d057 100644 --- a/lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py +++ b/lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py @@ -146,7 +146,7 @@ def test_structured_annotations_api(self): inst = instructions.GetInstructionAtIndex(i) self.assertTrue(inst.IsValid(), f"Invalid instruction at index {i}") - annotations = inst.GetVariableAnnotations(target) + annotations = inst.GetVariableAnnotations() self.assertIsInstance(annotations, lldb.SBStructuredData, "GetVariableAnnotations should return SBStructuredData")