forked from OSchip/llvm-project
tsan: refactor suppressions machinery
The refactoring makes suppressions more flexible and allow to suppress based on arbitrary number of stacks. In particular it fixes: https://code.google.com/p/thread-sanitizer/issues/detail?id=64 "Make it possible to suppress deadlock reports by any stack (not just first)" llvm-svn: 209757
This commit is contained in:
parent
303934ba49
commit
a43e98cc74
|
@ -1704,8 +1704,8 @@ static void CallUserSignalHandler(ThreadState *thr, bool sync, bool sigact,
|
||||||
ThreadRegistryLock l(ctx->thread_registry);
|
ThreadRegistryLock l(ctx->thread_registry);
|
||||||
ScopedReport rep(ReportTypeErrnoInSignal);
|
ScopedReport rep(ReportTypeErrnoInSignal);
|
||||||
if (!IsFiredSuppression(ctx, rep, stack)) {
|
if (!IsFiredSuppression(ctx, rep, stack)) {
|
||||||
rep.AddStack(&stack);
|
rep.AddStack(&stack, true);
|
||||||
OutputReport(ctx, rep, rep.GetReport()->stacks[0]);
|
OutputReport(ctx, rep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
errno = saved_errno;
|
errno = saved_errno;
|
||||||
|
|
|
@ -95,8 +95,8 @@ static void SignalUnsafeCall(ThreadState *thr, uptr pc) {
|
||||||
ThreadRegistryLock l(ctx->thread_registry);
|
ThreadRegistryLock l(ctx->thread_registry);
|
||||||
ScopedReport rep(ReportTypeSignalUnsafe);
|
ScopedReport rep(ReportTypeSignalUnsafe);
|
||||||
if (!IsFiredSuppression(ctx, rep, stack)) {
|
if (!IsFiredSuppression(ctx, rep, stack)) {
|
||||||
rep.AddStack(&stack);
|
rep.AddStack(&stack, true);
|
||||||
OutputReport(ctx, rep, rep.GetReport()->stacks[0]);
|
OutputReport(ctx, rep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ struct ReportStack {
|
||||||
char *file;
|
char *file;
|
||||||
int line;
|
int line;
|
||||||
int col;
|
int col;
|
||||||
|
bool suppressable;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ReportMopMutex {
|
struct ReportMopMutex {
|
||||||
|
@ -80,6 +81,7 @@ struct ReportLocation {
|
||||||
char *name;
|
char *name;
|
||||||
char *file;
|
char *file;
|
||||||
int line;
|
int line;
|
||||||
|
bool suppressable;
|
||||||
ReportStack *stack;
|
ReportStack *stack;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -576,11 +576,11 @@ class ScopedReport {
|
||||||
explicit ScopedReport(ReportType typ);
|
explicit ScopedReport(ReportType typ);
|
||||||
~ScopedReport();
|
~ScopedReport();
|
||||||
|
|
||||||
void AddStack(const StackTrace *stack);
|
|
||||||
void AddMemoryAccess(uptr addr, Shadow s, const StackTrace *stack,
|
void AddMemoryAccess(uptr addr, Shadow s, const StackTrace *stack,
|
||||||
const MutexSet *mset);
|
const MutexSet *mset);
|
||||||
void AddThread(const ThreadContext *tctx);
|
void AddStack(const StackTrace *stack, bool suppressable = false);
|
||||||
void AddThread(int unique_tid);
|
void AddThread(const ThreadContext *tctx, bool suppressable = false);
|
||||||
|
void AddThread(int unique_tid, bool suppressable = false);
|
||||||
void AddUniqueTid(int unique_tid);
|
void AddUniqueTid(int unique_tid);
|
||||||
void AddMutex(const SyncVar *s);
|
void AddMutex(const SyncVar *s);
|
||||||
u64 AddMutex(u64 id);
|
u64 AddMutex(u64 id);
|
||||||
|
@ -628,11 +628,7 @@ void ForkParentAfter(ThreadState *thr, uptr pc);
|
||||||
void ForkChildAfter(ThreadState *thr, uptr pc);
|
void ForkChildAfter(ThreadState *thr, uptr pc);
|
||||||
|
|
||||||
void ReportRace(ThreadState *thr);
|
void ReportRace(ThreadState *thr);
|
||||||
bool OutputReport(Context *ctx,
|
bool OutputReport(Context *ctx, const ScopedReport &srep);
|
||||||
const ScopedReport &srep,
|
|
||||||
const ReportStack *suppress_stack1,
|
|
||||||
const ReportStack *suppress_stack2 = 0,
|
|
||||||
const ReportLocation *suppress_loc = 0);
|
|
||||||
bool IsFiredSuppression(Context *ctx,
|
bool IsFiredSuppression(Context *ctx,
|
||||||
const ScopedReport &srep,
|
const ScopedReport &srep,
|
||||||
const StackTrace &trace);
|
const StackTrace &trace);
|
||||||
|
|
|
@ -57,9 +57,9 @@ static void ReportMutexMisuse(ThreadState *thr, uptr pc, ReportType typ,
|
||||||
rep.AddMutex(mid);
|
rep.AddMutex(mid);
|
||||||
StackTrace trace;
|
StackTrace trace;
|
||||||
trace.ObtainCurrent(thr, pc);
|
trace.ObtainCurrent(thr, pc);
|
||||||
rep.AddStack(&trace);
|
rep.AddStack(&trace, true);
|
||||||
rep.AddLocation(addr, 1);
|
rep.AddLocation(addr, 1);
|
||||||
OutputReport(ctx, rep, rep.GetReport()->stacks[0]);
|
OutputReport(ctx, rep);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MutexCreate(ThreadState *thr, uptr pc, uptr addr,
|
void MutexCreate(ThreadState *thr, uptr pc, uptr addr,
|
||||||
|
@ -113,9 +113,9 @@ void MutexDestroy(ThreadState *thr, uptr pc, uptr addr) {
|
||||||
rep.AddStack(&trace);
|
rep.AddStack(&trace);
|
||||||
FastState last(s->last_lock);
|
FastState last(s->last_lock);
|
||||||
RestoreStack(last.tid(), last.epoch(), &trace, 0);
|
RestoreStack(last.tid(), last.epoch(), &trace, 0);
|
||||||
rep.AddStack(&trace);
|
rep.AddStack(&trace, true);
|
||||||
rep.AddLocation(s->addr, 1);
|
rep.AddLocation(s->addr, 1);
|
||||||
OutputReport(ctx, rep, rep.GetReport()->stacks[0]);
|
OutputReport(ctx, rep);
|
||||||
}
|
}
|
||||||
thr->mset.Remove(s->GetId());
|
thr->mset.Remove(s->GetId());
|
||||||
DestroyAndFree(s);
|
DestroyAndFree(s);
|
||||||
|
@ -462,12 +462,10 @@ void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r) {
|
||||||
// but we should still produce some stack trace in the report.
|
// but we should still produce some stack trace in the report.
|
||||||
stacks[i].Init(&dummy_pc, 1);
|
stacks[i].Init(&dummy_pc, 1);
|
||||||
}
|
}
|
||||||
rep.AddStack(&stacks[i]);
|
rep.AddStack(&stacks[i], true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// FIXME: use all stacks for suppressions, not just the second stack of the
|
OutputReport(ctx, rep);
|
||||||
// first edge.
|
|
||||||
OutputReport(ctx, rep, rep.GetReport()->stacks[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace __tsan
|
} // namespace __tsan
|
||||||
|
|
|
@ -162,9 +162,10 @@ ScopedReport::~ScopedReport() {
|
||||||
DestroyAndFree(rep_);
|
DestroyAndFree(rep_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScopedReport::AddStack(const StackTrace *stack) {
|
void ScopedReport::AddStack(const StackTrace *stack, bool suppressable) {
|
||||||
ReportStack **rs = rep_->stacks.PushBack();
|
ReportStack **rs = rep_->stacks.PushBack();
|
||||||
*rs = SymbolizeStack(*stack);
|
*rs = SymbolizeStack(*stack);
|
||||||
|
(*rs)->suppressable = suppressable;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScopedReport::AddMemoryAccess(uptr addr, Shadow s,
|
void ScopedReport::AddMemoryAccess(uptr addr, Shadow s,
|
||||||
|
@ -178,6 +179,7 @@ void ScopedReport::AddMemoryAccess(uptr addr, Shadow s,
|
||||||
mop->write = s.IsWrite();
|
mop->write = s.IsWrite();
|
||||||
mop->atomic = s.IsAtomic();
|
mop->atomic = s.IsAtomic();
|
||||||
mop->stack = SymbolizeStack(*stack);
|
mop->stack = SymbolizeStack(*stack);
|
||||||
|
mop->stack->suppressable = true;
|
||||||
for (uptr i = 0; i < mset->Size(); i++) {
|
for (uptr i = 0; i < mset->Size(); i++) {
|
||||||
MutexSet::Desc d = mset->Get(i);
|
MutexSet::Desc d = mset->Get(i);
|
||||||
u64 mid = this->AddMutex(d.id);
|
u64 mid = this->AddMutex(d.id);
|
||||||
|
@ -190,7 +192,7 @@ void ScopedReport::AddUniqueTid(int unique_tid) {
|
||||||
rep_->unique_tids.PushBack(unique_tid);
|
rep_->unique_tids.PushBack(unique_tid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScopedReport::AddThread(const ThreadContext *tctx) {
|
void ScopedReport::AddThread(const ThreadContext *tctx, bool suppressable) {
|
||||||
for (uptr i = 0; i < rep_->threads.Size(); i++) {
|
for (uptr i = 0; i < rep_->threads.Size(); i++) {
|
||||||
if ((u32)rep_->threads[i]->id == tctx->tid)
|
if ((u32)rep_->threads[i]->id == tctx->tid)
|
||||||
return;
|
return;
|
||||||
|
@ -205,6 +207,8 @@ void ScopedReport::AddThread(const ThreadContext *tctx) {
|
||||||
rt->parent_tid = tctx->parent_tid;
|
rt->parent_tid = tctx->parent_tid;
|
||||||
rt->stack = 0;
|
rt->stack = 0;
|
||||||
rt->stack = SymbolizeStackId(tctx->creation_stack_id);
|
rt->stack = SymbolizeStackId(tctx->creation_stack_id);
|
||||||
|
if (rt->stack)
|
||||||
|
rt->stack->suppressable = suppressable;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef TSAN_GO
|
#ifndef TSAN_GO
|
||||||
|
@ -251,9 +255,9 @@ ThreadContext *IsThreadStackOrTls(uptr addr, bool *is_stack) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void ScopedReport::AddThread(int unique_tid) {
|
void ScopedReport::AddThread(int unique_tid, bool suppressable) {
|
||||||
#ifndef TSAN_GO
|
#ifndef TSAN_GO
|
||||||
AddThread(FindThreadByUidLocked(unique_tid));
|
AddThread(FindThreadByUidLocked(unique_tid), suppressable);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,6 +360,7 @@ void ScopedReport::AddLocation(uptr addr, uptr size) {
|
||||||
}
|
}
|
||||||
ReportLocation *loc = SymbolizeData(addr);
|
ReportLocation *loc = SymbolizeData(addr);
|
||||||
if (loc) {
|
if (loc) {
|
||||||
|
loc->suppressable = true;
|
||||||
rep_->locs.PushBack(loc);
|
rep_->locs.PushBack(loc);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -495,19 +500,19 @@ static void AddRacyStacks(ThreadState *thr, const StackTrace (&traces)[2],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OutputReport(Context *ctx,
|
bool OutputReport(Context *ctx, const ScopedReport &srep) {
|
||||||
const ScopedReport &srep,
|
|
||||||
const ReportStack *suppress_stack1,
|
|
||||||
const ReportStack *suppress_stack2,
|
|
||||||
const ReportLocation *suppress_loc) {
|
|
||||||
atomic_store(&ctx->last_symbolize_time_ns, NanoTime(), memory_order_relaxed);
|
atomic_store(&ctx->last_symbolize_time_ns, NanoTime(), memory_order_relaxed);
|
||||||
const ReportDesc *rep = srep.GetReport();
|
const ReportDesc *rep = srep.GetReport();
|
||||||
Suppression *supp = 0;
|
Suppression *supp = 0;
|
||||||
uptr suppress_pc = IsSuppressed(rep->typ, suppress_stack1, &supp);
|
uptr suppress_pc = 0;
|
||||||
if (suppress_pc == 0)
|
for (uptr i = 0; suppress_pc == 0 && i < rep->mops.Size(); i++)
|
||||||
suppress_pc = IsSuppressed(rep->typ, suppress_stack2, &supp);
|
suppress_pc = IsSuppressed(rep->typ, rep->mops[i]->stack, &supp);
|
||||||
if (suppress_pc == 0)
|
for (uptr i = 0; suppress_pc == 0 && i < rep->stacks.Size(); i++)
|
||||||
suppress_pc = IsSuppressed(rep->typ, suppress_loc, &supp);
|
suppress_pc = IsSuppressed(rep->typ, rep->stacks[i], &supp);
|
||||||
|
for (uptr i = 0; suppress_pc == 0 && i < rep->threads.Size(); i++)
|
||||||
|
suppress_pc = IsSuppressed(rep->typ, rep->threads[i]->stack, &supp);
|
||||||
|
for (uptr i = 0; suppress_pc == 0 && i < rep->locs.Size(); i++)
|
||||||
|
suppress_pc = IsSuppressed(rep->typ, rep->locs[i], &supp);
|
||||||
if (suppress_pc != 0) {
|
if (suppress_pc != 0) {
|
||||||
FiredSuppression s = {srep.GetReport()->typ, suppress_pc, supp};
|
FiredSuppression s = {srep.GetReport()->typ, suppress_pc, supp};
|
||||||
ctx->fired_suppressions.push_back(s);
|
ctx->fired_suppressions.push_back(s);
|
||||||
|
@ -695,11 +700,7 @@ void ReportRace(ThreadState *thr) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ReportLocation *suppress_loc = rep.GetReport()->locs.Size() ?
|
if (!OutputReport(ctx, rep))
|
||||||
rep.GetReport()->locs[0] : 0;
|
|
||||||
if (!OutputReport(ctx, rep, rep.GetReport()->mops[0]->stack,
|
|
||||||
rep.GetReport()->mops[1]->stack,
|
|
||||||
suppress_loc))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
AddRacyStacks(thr, traces, addr_min, addr_max);
|
AddRacyStacks(thr, traces, addr_min, addr_max);
|
||||||
|
|
|
@ -205,9 +205,9 @@ void ThreadFinalize(ThreadState *thr) {
|
||||||
MaybeReportThreadLeak, &leaks);
|
MaybeReportThreadLeak, &leaks);
|
||||||
for (uptr i = 0; i < leaks.Size(); i++) {
|
for (uptr i = 0; i < leaks.Size(); i++) {
|
||||||
ScopedReport rep(ReportTypeThreadLeak);
|
ScopedReport rep(ReportTypeThreadLeak);
|
||||||
rep.AddThread(leaks[i].tctx);
|
rep.AddThread(leaks[i].tctx, true);
|
||||||
rep.SetCount(leaks[i].count);
|
rep.SetCount(leaks[i].count);
|
||||||
OutputReport(ctx, rep, rep.GetReport()->threads[0]->stack);
|
OutputReport(ctx, rep);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,7 +123,8 @@ SuppressionType conv(ReportType typ) {
|
||||||
|
|
||||||
uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) {
|
uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) {
|
||||||
CHECK(g_ctx);
|
CHECK(g_ctx);
|
||||||
if (!g_ctx->SuppressionCount() || stack == 0) return 0;
|
if (!g_ctx->SuppressionCount() || stack == 0 || !stack->suppressable)
|
||||||
|
return 0;
|
||||||
SuppressionType stype = conv(typ);
|
SuppressionType stype = conv(typ);
|
||||||
if (stype == SuppressionNone)
|
if (stype == SuppressionNone)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -144,7 +145,7 @@ uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) {
|
||||||
uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp) {
|
uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp) {
|
||||||
CHECK(g_ctx);
|
CHECK(g_ctx);
|
||||||
if (!g_ctx->SuppressionCount() || loc == 0 ||
|
if (!g_ctx->SuppressionCount() || loc == 0 ||
|
||||||
loc->type != ReportLocationGlobal)
|
loc->type != ReportLocationGlobal || !loc->suppressable)
|
||||||
return 0;
|
return 0;
|
||||||
SuppressionType stype = conv(typ);
|
SuppressionType stype = conv(typ);
|
||||||
if (stype == SuppressionNone)
|
if (stype == SuppressionNone)
|
||||||
|
|
Loading…
Reference in New Issue