Provide more information in ThreadSanitizer's JSON data. Move remaining TSan logic from SBThread to InstrumentationRuntime plugin.

llvm-svn: 265905
This commit is contained in:
Kuba Brecka 2016-04-10 18:57:38 +00:00
parent 8d16199b7b
commit 1aad8fb772
6 changed files with 290 additions and 61 deletions

View File

@ -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);
};

View File

@ -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)

View File

@ -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::GetStopReasonExtendedInfoAsJSON (lldb::SBStream &stream)
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::GetStopReasonExtendedBacktraces (InstrumentationRuntimeType type)
// 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::GetStopReasonExtendedBacktraces (InstrumentationRuntimeType type)
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

View File

@ -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::RetrieveReportData(ExecutionContextRef exe_ctx_ref)
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::RetrieveReportData(ExecutionContextRef exe_ctx_ref)
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::NotifyBreakpointHit(void *baton, StoppointCallbackContex
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;
}

View File

@ -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;

View File

@ -50,3 +50,9 @@ InstrumentationRuntime::IsActive()
{
return false;
}
lldb::ThreadCollectionSP
InstrumentationRuntime::GetBacktracesFromExtendedStopInfo(StructuredData::ObjectSP info)
{
return ThreadCollectionSP(new ThreadCollection());
}