Author: kuba.brecka Date: Sun Apr 10 13:57:38 2016 New Revision: 265905 URL: http://llvm.org/viewvc/llvm-project?rev=265905&view=rev Log: Provide more information in ThreadSanitizer's JSON data. Move remaining TSan logic from SBThread to InstrumentationRuntime plugin.
Modified: lldb/trunk/include/lldb/Target/InstrumentationRuntime.h lldb/trunk/packages/Python/lldbsuite/test/functionalities/tsan/basic/TestTsanBasic.py lldb/trunk/source/API/SBThread.cpp lldb/trunk/source/Plugins/InstrumentationRuntime/ThreadSanitizer/ThreadSanitizerRuntime.cpp lldb/trunk/source/Plugins/InstrumentationRuntime/ThreadSanitizer/ThreadSanitizerRuntime.h lldb/trunk/source/Target/InstrumentationRuntime.cpp Modified: lldb/trunk/include/lldb/Target/InstrumentationRuntime.h URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Target/InstrumentationRuntime.h?rev=265905&r1=265904&r2=265905&view=diff ============================================================================== --- lldb/trunk/include/lldb/Target/InstrumentationRuntime.h (original) +++ lldb/trunk/include/lldb/Target/InstrumentationRuntime.h Sun Apr 10 13:57:38 2016 @@ -20,6 +20,7 @@ #include "lldb/lldb-private.h" #include "lldb/lldb-types.h" #include "lldb/Core/PluginInterface.h" +#include "lldb/Core/StructuredData.h" namespace lldb_private { @@ -39,6 +40,9 @@ public: virtual bool IsActive(); + + virtual lldb::ThreadCollectionSP + GetBacktracesFromExtendedStopInfo(StructuredData::ObjectSP info); }; Modified: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tsan/basic/TestTsanBasic.py URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tsan/basic/TestTsanBasic.py?rev=265905&r1=265904&r2=265905&view=diff ============================================================================== --- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tsan/basic/TestTsanBasic.py (original) +++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tsan/basic/TestTsanBasic.py Sun Apr 10 13:57:38 2016 @@ -73,7 +73,7 @@ class TsanBasicTestCase(TestBase): json_line = '\n'.join(output_lines[2:]) data = json.loads(json_line) self.assertEqual(data["instrumentation_class"], "ThreadSanitizer") - self.assertEqual(data["description"], "data-race") + self.assertEqual(data["issue_type"], "data-race") self.assertEqual(len(data["mops"]), 2) backtraces = thread.GetStopReasonExtendedBacktraces(lldb.eInstrumentationRuntimeTypeAddressSanitizer) @@ -109,7 +109,7 @@ class TsanBasicTestCase(TestBase): self.runCmd("continue") # the stop reason of the thread should be a SIGABRT. - self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + self.expect("thread list", "We should be stopped due a SIGABRT", substrs = ['stopped', 'stop reason = signal SIGABRT']) # test that we're in pthread_kill now (TSan abort the process) Modified: lldb/trunk/source/API/SBThread.cpp URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/API/SBThread.cpp?rev=265905&r1=265904&r2=265905&view=diff ============================================================================== --- lldb/trunk/source/API/SBThread.cpp (original) +++ lldb/trunk/source/API/SBThread.cpp Sun Apr 10 13:57:38 2016 @@ -34,7 +34,6 @@ #include "lldb/Target/ThreadPlanStepOut.h" #include "lldb/Target/ThreadPlanStepRange.h" #include "lldb/Target/ThreadPlanStepInRange.h" -#include "Plugins/Process/Utility/HistoryThread.h" #include "lldb/API/SBAddress.h" #include "lldb/API/SBDebugger.h" @@ -329,33 +328,6 @@ SBThread::GetStopReasonExtendedInfoAsJSO return true; } -static void -AddThreadsForPath(std::string path, ThreadCollectionSP threads, ProcessSP process_sp, StructuredData::ObjectSP info) -{ - info->GetObjectForDotSeparatedPath(path)->GetAsArray()->ForEach([process_sp, threads] (StructuredData::Object *o) -> bool { - std::vector<lldb::addr_t> pcs; - o->GetObjectForDotSeparatedPath("trace")->GetAsArray()->ForEach([&pcs] (StructuredData::Object *pc) -> bool { - pcs.push_back(pc->GetAsInteger()->GetValue()); - return true; - }); - - if (pcs.size() == 0) - return true; - - StructuredData::ObjectSP thread_id_obj = o->GetObjectForDotSeparatedPath("thread_id"); - tid_t tid = thread_id_obj ? thread_id_obj->GetIntegerValue() : 0; - uint32_t stop_id = 0; - bool stop_id_is_valid = false; - HistoryThread *history_thread = new HistoryThread(*process_sp, tid, pcs, stop_id, stop_id_is_valid); - ThreadSP new_thread_sp(history_thread); - // Save this in the Process' ExtendedThreadList so a strong pointer retains the object - process_sp->GetExtendedThreadList().AddThread(new_thread_sp); - threads->AddThread(new_thread_sp); - - return true; - }); -} - SBThreadCollection SBThread::GetStopReasonExtendedBacktraces (InstrumentationRuntimeType type) { @@ -365,10 +337,10 @@ SBThread::GetStopReasonExtendedBacktrace // We currently only support ThreadSanitizer. if (type != eInstrumentationRuntimeTypeThreadSanitizer) return threads; - + ExecutionContext exe_ctx (m_opaque_sp.get()); if (! exe_ctx.HasThreadScope()) - return SBThreadCollection(threads); + return threads; ProcessSP process_sp = exe_ctx.GetProcessSP(); @@ -377,16 +349,7 @@ SBThread::GetStopReasonExtendedBacktrace if (! info) return threads; - if (info->GetObjectForDotSeparatedPath("instrumentation_class")->GetStringValue() != "ThreadSanitizer") - return threads; - - AddThreadsForPath("stacks", threads, process_sp, info); - AddThreadsForPath("mops", threads, process_sp, info); - AddThreadsForPath("locs", threads, process_sp, info); - AddThreadsForPath("mutexes", threads, process_sp, info); - AddThreadsForPath("threads", threads, process_sp, info); - - return threads; + return process_sp->GetInstrumentationRuntime(type)->GetBacktracesFromExtendedStopInfo(info); } size_t Modified: lldb/trunk/source/Plugins/InstrumentationRuntime/ThreadSanitizer/ThreadSanitizerRuntime.cpp URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/InstrumentationRuntime/ThreadSanitizer/ThreadSanitizerRuntime.cpp?rev=265905&r1=265904&r2=265905&view=diff ============================================================================== --- lldb/trunk/source/Plugins/InstrumentationRuntime/ThreadSanitizer/ThreadSanitizerRuntime.cpp (original) +++ lldb/trunk/source/Plugins/InstrumentationRuntime/ThreadSanitizer/ThreadSanitizerRuntime.cpp Sun Apr 10 13:57:38 2016 @@ -24,9 +24,11 @@ #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Target/InstrumentationRuntimeStopInfo.h" +#include "lldb/Target/SectionLoadList.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" +#include "Plugins/Process/Utility/HistoryThread.h" using namespace lldb; using namespace lldb_private; @@ -346,7 +348,7 @@ ThreadSanitizerRuntime::RetrieveReportDa StructuredData::Dictionary *dict = new StructuredData::Dictionary(); dict->AddStringItem("instrumentation_class", "ThreadSanitizer"); - dict->AddStringItem("description", RetrieveString(main_value, process_sp, ".description")); + dict->AddStringItem("issue_type", RetrieveString(main_value, process_sp, ".description")); dict->AddIntegerItem("report_count", main_value->GetValueForExpressionPath(".report_count")->GetValueAsUnsigned(0)); dict->AddItem("sleep_trace", StructuredData::ObjectSP(CreateStackTrace(main_value, ".sleep_trace"))); @@ -412,42 +414,174 @@ ThreadSanitizerRuntime::RetrieveReportDa std::string ThreadSanitizerRuntime::FormatDescription(StructuredData::ObjectSP report) { - std::string description = report->GetAsDictionary()->GetValueForKey("description")->GetAsString()->GetValue(); + std::string description = report->GetAsDictionary()->GetValueForKey("issue_type")->GetAsString()->GetValue(); if (description == "data-race") { - return "Data race detected"; + return "Data race"; } else if (description == "data-race-vptr") { - return "Data race on C++ virtual pointer detected"; + return "Data race on C++ virtual pointer"; } else if (description == "heap-use-after-free") { - return "Use of deallocated memory detected"; + return "Use of deallocated memory"; } else if (description == "heap-use-after-free-vptr") { - return "Use of deallocated C++ virtual pointer detected"; + return "Use of deallocated C++ virtual pointer"; } else if (description == "thread-leak") { - return "Thread leak detected"; + return "Thread leak"; } else if (description == "locked-mutex-destroy") { - return "Destruction of a locked mutex detected"; + return "Destruction of a locked mutex"; } else if (description == "mutex-double-lock") { - return "Double lock of a mutex detected"; + return "Double lock of a mutex"; } else if (description == "mutex-invalid-access") { - return "Use of an invalid mutex (e.g. uninitialized or destroyed) detected"; + return "Use of an invalid mutex (e.g. uninitialized or destroyed)"; } else if (description == "mutex-bad-unlock") { - return "Unlock of an unlocked mutex (or by a wrong thread) detected"; + return "Unlock of an unlocked mutex (or by a wrong thread)"; } else if (description == "mutex-bad-read-lock") { - return "Read lock of a write locked mutex detected"; + return "Read lock of a write locked mutex"; } else if (description == "mutex-bad-read-unlock") { - return "Read unlock of a write locked mutex detected"; + return "Read unlock of a write locked mutex"; } else if (description == "signal-unsafe-call") { - return "Signal-unsafe call inside a signal handler detected"; + return "Signal-unsafe call inside a signal handler"; } else if (description == "errno-in-signal-handler") { - return "Overwrite of errno in a signal handler detected"; + return "Overwrite of errno in a signal handler"; } else if (description == "lock-order-inversion") { - return "Lock order inversion (potential deadlock) detected"; + return "Lock order inversion (potential deadlock)"; } // for unknown report codes just show the code return description; } +static std::string +Sprintf(const char *format, ...) +{ + StreamString s; + va_list args; + va_start (args, format); + s.PrintfVarArg(format, args); + va_end (args); + return s.GetString(); +} + +static std::string +GetSymbolNameFromAddress(ProcessSP process_sp, addr_t addr) +{ + lldb_private::Address so_addr; + if (! process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress(addr, so_addr)) + return ""; + + lldb_private::Symbol *symbol = so_addr.CalculateSymbolContextSymbol(); + if (! symbol) + return ""; + + std::string sym_name = symbol->GetName().GetCString(); + return sym_name; +} + +addr_t +ThreadSanitizerRuntime::GetFirstNonInternalFramePc(StructuredData::ObjectSP trace) +{ + ProcessSP process_sp = GetProcessSP(); + ModuleSP runtime_module_sp = GetRuntimeModuleSP(); + + addr_t result = 0; + trace->GetAsArray()->ForEach([process_sp, runtime_module_sp, &result] (StructuredData::Object *o) -> bool { + addr_t addr = o->GetIntegerValue(); + lldb_private::Address so_addr; + if (! process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress(addr, so_addr)) + return true; + + if (so_addr.GetModule() == runtime_module_sp) + return true; + + result = addr; + return false; + }); + + return result; +} + +std::string +ThreadSanitizerRuntime::GenerateSummary(StructuredData::ObjectSP report) +{ + ProcessSP process_sp = GetProcessSP(); + + std::string summary = report->GetAsDictionary()->GetValueForKey("description")->GetAsString()->GetValue(); + addr_t pc = 0; + if (report->GetAsDictionary()->GetValueForKey("mops")->GetAsArray()->GetSize() > 0) + pc = GetFirstNonInternalFramePc(report->GetAsDictionary()->GetValueForKey("mops")->GetAsArray()->GetItemAtIndex(0)->GetAsDictionary()->GetValueForKey("trace")); + + if (report->GetAsDictionary()->GetValueForKey("stacks")->GetAsArray()->GetSize() > 0) + pc = GetFirstNonInternalFramePc(report->GetAsDictionary()->GetValueForKey("stacks")->GetAsArray()->GetItemAtIndex(0)->GetAsDictionary()->GetValueForKey("trace")); + + if (pc != 0) { + summary = summary + " in " + GetSymbolNameFromAddress(process_sp, pc); + } + + if (report->GetAsDictionary()->GetValueForKey("locs")->GetAsArray()->GetSize() > 0) { + StructuredData::ObjectSP loc = report->GetAsDictionary()->GetValueForKey("locs")->GetAsArray()->GetItemAtIndex(0); + addr_t addr = loc->GetAsDictionary()->GetValueForKey("address")->GetAsInteger()->GetValue(); + if (addr == 0) + addr = loc->GetAsDictionary()->GetValueForKey("start")->GetAsInteger()->GetValue(); + + if (addr != 0) { + summary = summary + " at " + Sprintf("0x%llx", addr); + } else { + int fd = loc->GetAsDictionary()->GetValueForKey("file_descriptor")->GetAsInteger()->GetValue(); + if (fd != 0) { + summary = summary + " on file descriptor " + Sprintf("%d", fd); + } + } + } + + return summary; +} + +addr_t +ThreadSanitizerRuntime::GetMainRacyAddress(StructuredData::ObjectSP report) +{ + addr_t result = (addr_t)-1; + + report->GetObjectForDotSeparatedPath("mops")->GetAsArray()->ForEach([&result] (StructuredData::Object *o) -> bool { + addr_t addr = o->GetObjectForDotSeparatedPath("address")->GetIntegerValue(); + if (addr < result) result = addr; + return true; + }); + + return (result == (addr_t)-1) ? 0 : result; +} + +std::string +ThreadSanitizerRuntime::GetLocationDescription(StructuredData::ObjectSP report) +{ + std::string result = ""; + + ProcessSP process_sp = GetProcessSP(); + + if (report->GetAsDictionary()->GetValueForKey("locs")->GetAsArray()->GetSize() > 0) { + StructuredData::ObjectSP loc = report->GetAsDictionary()->GetValueForKey("locs")->GetAsArray()->GetItemAtIndex(0); + std::string type = loc->GetAsDictionary()->GetValueForKey("type")->GetStringValue(); + if (type == "global") { + addr_t addr = loc->GetAsDictionary()->GetValueForKey("address")->GetAsInteger()->GetValue(); + std::string global_name = GetSymbolNameFromAddress(process_sp, addr); + result = Sprintf("Location is a global '%s'", global_name.c_str()); + } else if (type == "heap") { + addr_t addr = loc->GetAsDictionary()->GetValueForKey("start")->GetAsInteger()->GetValue(); + long size = loc->GetAsDictionary()->GetValueForKey("size")->GetAsInteger()->GetValue(); + result = Sprintf("Location is a %ld-byte heap object at 0x%llx", size, addr); + } else if (type == "stack") { + int tid = loc->GetAsDictionary()->GetValueForKey("thread_id")->GetAsInteger()->GetValue(); + result = Sprintf("Location is stack of thread %d", tid); + } else if (type == "tls") { + int tid = loc->GetAsDictionary()->GetValueForKey("thread_id")->GetAsInteger()->GetValue(); + result = Sprintf("Location is TLS of thread %d", tid); + } else if (type == "fd") { + int fd = loc->GetAsDictionary()->GetValueForKey("file_descriptor")->GetAsInteger()->GetValue(); + result = Sprintf("Location is file descriptor %d", fd); + } + } + + return result; +} + bool ThreadSanitizerRuntime::NotifyBreakpointHit(void *baton, StoppointCallbackContext *context, user_id_t break_id, user_id_t break_loc_id) { @@ -458,17 +592,27 @@ ThreadSanitizerRuntime::NotifyBreakpoint ThreadSanitizerRuntime *const instance = static_cast<ThreadSanitizerRuntime*>(baton); StructuredData::ObjectSP report = instance->RetrieveReportData(context->exe_ctx_ref); - std::string description; + std::string stop_reason_description; if (report) { - description = instance->FormatDescription(report); + std::string issue_description = instance->FormatDescription(report); + report->GetAsDictionary()->AddStringItem("description", issue_description); + stop_reason_description = issue_description + " detected"; + report->GetAsDictionary()->AddStringItem("stop_description", stop_reason_description); + std::string summary = instance->GenerateSummary(report); + report->GetAsDictionary()->AddStringItem("summary", summary); + addr_t main_address = instance->GetMainRacyAddress(report); + report->GetAsDictionary()->AddIntegerItem("memory_address", main_address); + std::string location_description = instance->GetLocationDescription(report); + report->GetAsDictionary()->AddStringItem("location_description", location_description); } + ProcessSP process_sp = instance->GetProcessSP(); // Make sure this is the right process if (process_sp && process_sp == context->exe_ctx_ref.GetProcessSP()) { ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP(); if (thread_sp) - thread_sp->SetStopInfo(InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData(*thread_sp, description.c_str(), report)); + thread_sp->SetStopInfo(InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData(*thread_sp, stop_reason_description.c_str(), report)); StreamFileSP stream_sp (process_sp->GetTarget().GetDebugger().GetOutputFile()); if (stream_sp) @@ -536,3 +680,100 @@ ThreadSanitizerRuntime::Deactivate() } m_is_active = false; } + +static std::string +GenerateThreadName(std::string path, StructuredData::Object *o) { + std::string result = "additional information"; + + if (path == "mops") { + int size = o->GetObjectForDotSeparatedPath("size")->GetIntegerValue(); + int thread_id = o->GetObjectForDotSeparatedPath("thread_id")->GetIntegerValue(); + bool is_write = o->GetObjectForDotSeparatedPath("is_write")->GetBooleanValue(); + bool is_atomic = o->GetObjectForDotSeparatedPath("is_atomic")->GetBooleanValue(); + addr_t addr = o->GetObjectForDotSeparatedPath("address")->GetIntegerValue(); + + result = Sprintf("%s%s of size %d at 0x%llx by thread %d", is_atomic ? "atomic " : "", is_write ? "write" : "read", size, addr, thread_id); + } + + if (path == "threads") { + int thread_id = o->GetObjectForDotSeparatedPath("thread_id")->GetIntegerValue(); + int parent_thread_id = o->GetObjectForDotSeparatedPath("parent_thread_id")->GetIntegerValue(); + + result = Sprintf("thread %d created by thread %d at", thread_id, parent_thread_id); + } + + if (path == "locs") { + std::string type = o->GetAsDictionary()->GetValueForKey("type")->GetStringValue(); + int thread_id = o->GetObjectForDotSeparatedPath("thread_id")->GetIntegerValue(); + int fd = o->GetObjectForDotSeparatedPath("file_descriptor")->GetIntegerValue(); + if (type == "heap") { + result = Sprintf("Heap block allocated by thread %d at", thread_id); + } else if (type == "fd") { + result = Sprintf("File descriptor %d created by thread %t at", fd, thread_id); + } + } + + if (path == "mutexes") { + int mutex_id = o->GetObjectForDotSeparatedPath("mutex_id")->GetIntegerValue(); + + result = Sprintf("mutex M%d created at", mutex_id); + } + + if (path == "stacks") { + result = "happened at"; + } + + result[0] = toupper(result[0]); + + return result; +} + +static void +AddThreadsForPath(std::string path, ThreadCollectionSP threads, ProcessSP process_sp, StructuredData::ObjectSP info) +{ + info->GetObjectForDotSeparatedPath(path)->GetAsArray()->ForEach([process_sp, threads, path] (StructuredData::Object *o) -> bool { + std::vector<lldb::addr_t> pcs; + o->GetObjectForDotSeparatedPath("trace")->GetAsArray()->ForEach([&pcs] (StructuredData::Object *pc) -> bool { + pcs.push_back(pc->GetAsInteger()->GetValue()); + return true; + }); + + if (pcs.size() == 0) + return true; + + StructuredData::ObjectSP thread_id_obj = o->GetObjectForDotSeparatedPath("thread_id"); + tid_t tid = thread_id_obj ? thread_id_obj->GetIntegerValue() : 0; + + uint32_t stop_id = 0; + bool stop_id_is_valid = false; + HistoryThread *history_thread = new HistoryThread(*process_sp, tid, pcs, stop_id, stop_id_is_valid); + ThreadSP new_thread_sp(history_thread); + new_thread_sp->SetName(GenerateThreadName(path, o).c_str()); + + // Save this in the Process' ExtendedThreadList so a strong pointer retains the object + process_sp->GetExtendedThreadList().AddThread(new_thread_sp); + threads->AddThread(new_thread_sp); + + return true; + }); +} + +lldb::ThreadCollectionSP +ThreadSanitizerRuntime::GetBacktracesFromExtendedStopInfo(StructuredData::ObjectSP info) +{ + ThreadCollectionSP threads; + threads.reset(new ThreadCollection()); + + if (info->GetObjectForDotSeparatedPath("instrumentation_class")->GetStringValue() != "ThreadSanitizer") + return threads; + + ProcessSP process_sp = GetProcessSP(); + + AddThreadsForPath("stacks", threads, process_sp, info); + AddThreadsForPath("mops", threads, process_sp, info); + AddThreadsForPath("locs", threads, process_sp, info); + AddThreadsForPath("mutexes", threads, process_sp, info); + AddThreadsForPath("threads", threads, process_sp, info); + + return threads; +} Modified: lldb/trunk/source/Plugins/InstrumentationRuntime/ThreadSanitizer/ThreadSanitizerRuntime.h URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/InstrumentationRuntime/ThreadSanitizer/ThreadSanitizerRuntime.h?rev=265905&r1=265904&r2=265905&view=diff ============================================================================== --- lldb/trunk/source/Plugins/InstrumentationRuntime/ThreadSanitizer/ThreadSanitizerRuntime.h (original) +++ lldb/trunk/source/Plugins/InstrumentationRuntime/ThreadSanitizer/ThreadSanitizerRuntime.h Sun Apr 10 13:57:38 2016 @@ -63,6 +63,9 @@ public: bool IsActive() override; + lldb::ThreadCollectionSP + GetBacktracesFromExtendedStopInfo(StructuredData::ObjectSP info) override; + private: ThreadSanitizerRuntime(const lldb::ProcessSP &process_sp); @@ -93,6 +96,18 @@ private: std::string FormatDescription(StructuredData::ObjectSP report); + std::string + GenerateSummary(StructuredData::ObjectSP report); + + lldb::addr_t + GetMainRacyAddress(StructuredData::ObjectSP report); + + std::string + GetLocationDescription(StructuredData::ObjectSP report); + + lldb::addr_t + GetFirstNonInternalFramePc(StructuredData::ObjectSP trace); + bool m_is_active; lldb::ModuleWP m_runtime_module_wp; lldb::ProcessWP m_process_wp; Modified: lldb/trunk/source/Target/InstrumentationRuntime.cpp URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Target/InstrumentationRuntime.cpp?rev=265905&r1=265904&r2=265905&view=diff ============================================================================== --- lldb/trunk/source/Target/InstrumentationRuntime.cpp (original) +++ lldb/trunk/source/Target/InstrumentationRuntime.cpp Sun Apr 10 13:57:38 2016 @@ -50,3 +50,9 @@ InstrumentationRuntime::IsActive() { return false; } + +lldb::ThreadCollectionSP +InstrumentationRuntime::GetBacktracesFromExtendedStopInfo(StructuredData::ObjectSP info) +{ + return ThreadCollectionSP(new ThreadCollection()); +} _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits