#include "tsan_report.h"
#include "tsan_platform.h"
#include "tsan_rtl.h"
#include "sanitizer_common/sanitizer_file.h"
#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_report_decorator.h"
#include "sanitizer_common/sanitizer_stacktrace_printer.h"
namespace __tsan {
class Decorator: public __sanitizer::SanitizerCommonDecorator { … };
ReportDesc::ReportDesc()
: … { … }
ReportMop::ReportMop()
: … { … }
ReportDesc::~ReportDesc() { … }
#if !SANITIZER_GO
const int kThreadBufSize = …;
const char *thread_name(char *buf, Tid tid) { … }
static const char *ReportTypeString(ReportType typ, uptr tag) { … }
void PrintStack(const ReportStack *ent) { … }
static void PrintMutexSet(Vector<ReportMopMutex> const& mset) { … }
static const char *MopDesc(bool first, bool write, bool atomic) { … }
static const char *ExternalMopDesc(bool first, bool write) { … }
static void PrintMop(const ReportMop *mop, bool first) { … }
static void PrintLocation(const ReportLocation *loc) { … }
static void PrintMutexShort(const ReportMutex *rm, const char *after) { … }
static void PrintMutexShortWithAddress(const ReportMutex *rm,
const char *after) { … }
static void PrintMutex(const ReportMutex *rm) { … }
static void PrintThread(const ReportThread *rt) { … }
static void PrintSleep(const ReportStack *s) { … }
static ReportStack *ChooseSummaryStack(const ReportDesc *rep) { … }
static const SymbolizedStack *SkipTsanInternalFrames(SymbolizedStack *frames) { … }
void PrintReport(const ReportDesc *rep) { … }
#else
const Tid kMainGoroutineId = 1;
void PrintStack(const ReportStack *ent) {
if (ent == 0 || ent->frames == 0) {
Printf(" [failed to restore the stack]\n");
return;
}
SymbolizedStack *frame = ent->frames;
for (int i = 0; frame; frame = frame->next, i++) {
const AddressInfo &info = frame->info;
Printf(" %s()\n %s:%d +0x%zx\n", info.function,
StripPathPrefix(info.file, common_flags()->strip_path_prefix),
info.line, info.module_offset);
}
}
static void PrintMop(const ReportMop *mop, bool first) {
Printf("\n");
Printf("%s at %p by ",
(first ? (mop->write ? "Write" : "Read")
: (mop->write ? "Previous write" : "Previous read")),
reinterpret_cast<void *>(mop->addr));
if (mop->tid == kMainGoroutineId)
Printf("main goroutine:\n");
else
Printf("goroutine %d:\n", mop->tid);
PrintStack(mop->stack);
}
static void PrintLocation(const ReportLocation *loc) {
switch (loc->type) {
case ReportLocationHeap: {
Printf("\n");
Printf("Heap block of size %zu at %p allocated by ", loc->heap_chunk_size,
reinterpret_cast<void *>(loc->heap_chunk_start));
if (loc->tid == kMainGoroutineId)
Printf("main goroutine:\n");
else
Printf("goroutine %d:\n", loc->tid);
PrintStack(loc->stack);
break;
}
case ReportLocationGlobal: {
Printf("\n");
Printf("Global var %s of size %zu at %p declared at %s:%zu\n",
loc->global.name, loc->global.size,
reinterpret_cast<void *>(loc->global.start), loc->global.file,
loc->global.line);
break;
}
default:
break;
}
}
static void PrintThread(const ReportThread *rt) {
if (rt->id == kMainGoroutineId)
return;
Printf("\n");
Printf("Goroutine %d (%s) created at:\n",
rt->id, rt->running ? "running" : "finished");
PrintStack(rt->stack);
}
void PrintReport(const ReportDesc *rep) {
Printf("==================\n");
if (rep->typ == ReportTypeRace) {
Printf("WARNING: DATA RACE");
for (uptr i = 0; i < rep->mops.Size(); i++)
PrintMop(rep->mops[i], i == 0);
for (uptr i = 0; i < rep->locs.Size(); i++)
PrintLocation(rep->locs[i]);
for (uptr i = 0; i < rep->threads.Size(); i++)
PrintThread(rep->threads[i]);
} else if (rep->typ == ReportTypeDeadlock) {
Printf("WARNING: DEADLOCK\n");
for (uptr i = 0; i < rep->mutexes.Size(); i++) {
Printf("Goroutine %d lock mutex %u while holding mutex %u:\n", 999,
rep->mutexes[i]->id,
rep->mutexes[(i + 1) % rep->mutexes.Size()]->id);
PrintStack(rep->stacks[2*i]);
Printf("\n");
Printf("Mutex %u was previously locked here:\n",
rep->mutexes[(i + 1) % rep->mutexes.Size()]->id);
PrintStack(rep->stacks[2*i + 1]);
Printf("\n");
}
}
Printf("==================\n");
}
#endif
}