-
Notifications
You must be signed in to change notification settings - Fork 15.1k
[lldb] [disassembler] chore: enhance VariableAnnotator to return structured data #165163
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
aba4890
199a909
ea1bf46
0331516
2449b6c
839ac8e
5278024
7055cc0
13afa00
4cee0d7
c955af3
4d8706d
573a6a1
c688f70
31e91cf
f805dbb
f03a448
f10922f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -566,6 +566,21 @@ class Disassembler : public std::enable_shared_from_this<Disassembler>, | |
| 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<std::string> | ||
| decl_file; // Source file where variable was declared | ||
| std::optional<uint32_t> decl_line; // Line number where variable was declared | ||
|
||
| std::optional<std::string> type_name; // Variable's type name | ||
| }; | ||
|
|
||
| /// Tracks live variable annotations across instructions and produces | ||
| /// per-instruction "events" like `name = RDI` or `name = <undef>`. | ||
| class VariableAnnotator { | ||
|
|
@@ -574,16 +589,39 @@ class VariableAnnotator { | |
| std::string name; | ||
| /// Last printed location (empty means <undef>). | ||
| 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<std::string> decl_file; | ||
| std::optional<uint32_t> decl_line; | ||
| std::optional<std::string> type_name; | ||
|
||
| }; | ||
|
|
||
| // Live state from the previous instruction, keyed by Variable::GetID(). | ||
| llvm::DenseMap<lldb::user_id_t, VarState> 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<std::string> 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<VariableAnnotation> | ||
| annotateStructured(Instruction &inst, Target &target, | ||
n2h9 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| const lldb::ModuleSP &module_sp); | ||
|
||
|
|
||
| private: | ||
| VariableAnnotation createAnnotation( | ||
| const VarState &var_state, bool is_live, | ||
| const std::optional<std::string> &location_desc = std::nullopt); | ||
| }; | ||
|
|
||
| } // namespace lldb_private | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 <memory> | ||
|
|
||
|
|
@@ -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<VariableAnnotation> annotations = | ||
| annotator.annotateStructured(*inst_sp, *target_sp, module_sp); | ||
|
|
||
| auto array_sp = std::make_shared<StructuredData::Array>(); | ||
|
|
||
| for (const auto &ann : annotations) { | ||
| auto dict_sp = std::make_shared<StructuredData::Dictionary>(); | ||
|
|
||
| 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<StructuredData::UnsignedInteger>(ann.start_address)); | ||
| dict_sp->AddItem( | ||
| "end_address", | ||
| std::make_shared<StructuredData::UnsignedInteger>(ann.end_address)); | ||
| dict_sp->AddItem( | ||
| "register_kind", | ||
| std::make_shared<StructuredData::UnsignedInteger>(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<StructuredData::UnsignedInteger>(*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; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -302,14 +302,39 @@ bool Disassembler::ElideMixedSourceAndDisassemblyLine( | |
| std::vector<std::string> | ||
| VariableAnnotator::annotate(Instruction &inst, Target &target, | ||
| const lldb::ModuleSP &module_sp) { | ||
| auto structured_annotations = annotateStructured(inst, target, module_sp); | ||
|
|
||
| std::vector<std::string> 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(); | ||
|
Comment on lines
+312
to
+319
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems like it could benefit from writing this as a stream: |
||
| events.push_back(display_string); | ||
| } | ||
|
|
||
| return events; | ||
| } | ||
|
|
||
| std::vector<VariableAnnotation> | ||
| VariableAnnotator::annotateStructured(Instruction &inst, Target &target, | ||
|
||
| const lldb::ModuleSP &module_sp) { | ||
| std::vector<VariableAnnotation> annotations; | ||
|
|
||
| // If we lost module context, everything becomes <undef>. | ||
| // 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} = <undef>", 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} = <undef>", 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<std::string> decl_file; | ||
| std::optional<uint32_t> decl_line; | ||
| std::optional<std::string> 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 <undef>. | ||
| // 2) Ends: anything that was live but is not in Current becomes | ||
| // <kUndefLocation>. | ||
| for (const auto &KV : Live_) { | ||
| if (!Current.count(KV.first)) | ||
| events.emplace_back(llvm::formatv("{0} = <undef>", 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<std::string> &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, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.