[Lldb-commits] [lldb] [lldb] [disassembler] chore: enhance VariableAnnotator to return structured data (PR #165163)
via lldb-commits
lldb-commits at lists.llvm.org
Tue Oct 28 11:12:50 PDT 2025
https://github.com/n2h9 updated https://github.com/llvm/llvm-project/pull/165163
>From aba4890ee4ce4e3c37961a3997d21832f904f599 Mon Sep 17 00:00:00 2001
From: Nikita B <n2h9z4 at gmail.com>
Date: Sun, 26 Oct 2025 18:49:43 +0100
Subject: [PATCH 1/2] [lldb] [disassembler] chore: enhance VariableAnnotator to
return structured data
Signed-off-by: Nikita B <n2h9z4 at gmail.com>
---
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 <cstdio>
@@ -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<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,
+ 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
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 <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;
+}
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<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();
+ 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,
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("<decoding error>", 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 at users.noreply.github.com>
Date: Tue, 28 Oct 2025 19:12:42 +0100
Subject: [PATCH 2/2] Update lldb/include/lldb/Core/Disassembler.h
Co-authored-by: Jonas Devlieghere <jonas at devlieghere.com>
---
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<Disassembler>,
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"
More information about the lldb-commits
mailing list