Index: lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc =================================================================== --- lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc +++ lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc @@ -39,7 +39,7 @@ RenderFrame(&frame_desc, common_flags()->stack_trace_format, frame_num++, cur->info, common_flags()->symbolize_vs_style, common_flags()->strip_path_prefix); - Printf("%s\n", frame_desc.data()); + Printf(" %s\n", frame_desc.data()); if (dedup_frames-- > 0) { if (dedup_token.length()) dedup_token.append("--"); Index: lib/sanitizer_common/sanitizer_stacktrace_printer.cc =================================================================== --- lib/sanitizer_common/sanitizer_stacktrace_printer.cc +++ lib/sanitizer_common/sanitizer_stacktrace_printer.cc @@ -24,7 +24,7 @@ return function; } -static const char kDefaultFormat[] = " #%n %p %F %L"; +static const char kDefaultFormat[] = "#%n %p %F %L"; void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no, const AddressInfo &info, bool vs_style, Index: lib/tsan/rtl/tsan_flags.cc =================================================================== --- lib/tsan/rtl/tsan_flags.cc +++ lib/tsan/rtl/tsan_flags.cc @@ -65,7 +65,7 @@ cf.detect_deadlocks = true; } cf.print_suppressions = false; - cf.stack_trace_format = " #%n %f %S %M"; + cf.stack_trace_format = "#%n %f %S %M"; cf.exitcode = 66; cf.intercept_tls_get_addr = true; OverrideCommonFlags(cf); Index: lib/tsan/rtl/tsan_report.h =================================================================== --- lib/tsan/rtl/tsan_report.h +++ lib/tsan/rtl/tsan_report.h @@ -40,6 +40,7 @@ struct ReportStack { SymbolizedStack *frames; bool suppressable; + SymbolizedStack *responsible_frame; static ReportStack *New(); private: @@ -127,7 +128,7 @@ // Format and output the report to the console/log. No additional logic. void PrintReport(const ReportDesc *rep); -void PrintStack(const ReportStack *stack); +void PrintStack(const ReportStack *ent, bool mark_frames = false); } // namespace __tsan Index: lib/tsan/rtl/tsan_report.cc =================================================================== --- lib/tsan/rtl/tsan_report.cc +++ lib/tsan/rtl/tsan_report.cc @@ -19,7 +19,8 @@ namespace __tsan { -ReportStack::ReportStack() : frames(nullptr), suppressable(false) {} +ReportStack::ReportStack() + : frames(nullptr), suppressable(false), responsible_frame(0) {} ReportStack *ReportStack::New() { void *mem = internal_alloc(MBlockReportStack, sizeof(ReportStack)); @@ -121,7 +122,7 @@ static const char *const kInterposedFunctionPrefix = "__interceptor_"; #endif -void PrintStack(const ReportStack *ent) { +void PrintStack(const ReportStack *ent, bool mark_frames) { if (ent == 0 || ent->frames == 0) { Printf(" [failed to restore the stack]\n\n"); return; @@ -132,7 +133,9 @@ RenderFrame(&res, common_flags()->stack_trace_format, i, frame->info, common_flags()->symbolize_vs_style, common_flags()->strip_path_prefix, kInterposedFunctionPrefix); - Printf("%s\n", res.data()); + const char *star_or_space = + (mark_frames && frame == ent->responsible_frame) ? "*" : " "; + Printf(" %s %s\n", star_or_space, res.data()); } Printf("\n"); } @@ -159,7 +162,7 @@ : (write ? "Previous mutating" : "Previous read-only"); } -static void PrintMop(const ReportMop *mop, bool first) { +static void PrintMop(const ReportMop *mop, bool mark_frames, bool first) { Decorator d; char thrbuf[kThreadBufSize]; Printf("%s", d.Access()); @@ -176,7 +179,7 @@ PrintMutexSet(mop->mset); Printf(":\n"); Printf("%s", d.EndAccess()); - PrintStack(mop->stack); + PrintStack(mop->stack, mark_frames); } static void PrintLocation(const ReportLocation *loc) { @@ -306,10 +309,17 @@ return false; } -static SymbolizedStack *SkipTsanInternalFrames(SymbolizedStack *frames) { - while (FrameIsInternal(frames) && frames->next) - frames = frames->next; - return frames; +static bool FillResponsibleFrame(ReportStack *stack, ReportType typ) { + if (stack) { + SymbolizedStack *frame = stack->frames; + while (FrameIsInternal(frame) && frame->next) frame = frame->next; + if (typ == ReportTypeExternalRace && frame->next) frame = frame->next; + if (stack) { + stack->responsible_frame = frame; + return stack->responsible_frame != stack->frames; + } + } + return false; } void PrintReport(const ReportDesc *rep) { @@ -321,6 +331,16 @@ (int)internal_getpid()); Printf("%s", d.EndWarning()); + bool should_mark_frame = false; + for (uptr i = 0; i < rep->mops.Size(); i++) + should_mark_frame |= FillResponsibleFrame(rep->mops[i]->stack, rep->typ); + for (uptr i = 0; i < rep->stacks.Size(); i++) + should_mark_frame |= FillResponsibleFrame(rep->stacks[i], rep->typ); + for (uptr i = 0; i < rep->threads.Size(); i++) + should_mark_frame |= FillResponsibleFrame(rep->threads[i]->stack, rep->typ); + for (uptr i = 0; i < rep->locs.Size(); i++) + should_mark_frame |= FillResponsibleFrame(rep->locs[i]->stack, rep->typ); + if (rep->typ == ReportTypeDeadlock) { char thrbuf[kThreadBufSize]; Printf(" Cycle in lock order graph: "); @@ -355,12 +375,16 @@ for (uptr i = 0; i < rep->stacks.Size(); i++) { if (i) Printf(" and:\n"); - PrintStack(rep->stacks[i]); + PrintStack(rep->stacks[i], should_mark_frame); } } for (uptr i = 0; i < rep->mops.Size(); i++) - PrintMop(rep->mops[i], i == 0); + PrintMop(rep->mops[i], should_mark_frame, i == 0); + + if (should_mark_frame) { + Printf(" Issue is caused by frames marked with \"*\".\n\n"); + } if (rep->sleep) PrintSleep(rep->sleep); @@ -380,7 +404,8 @@ Printf(" And %d more similar thread leaks.\n\n", rep->count - 1); if (ReportStack *stack = ChooseSummaryStack(rep)) { - if (SymbolizedStack *frame = SkipTsanInternalFrames(stack->frames)) + SymbolizedStack *frame = stack->responsible_frame ?: stack->frames; + if (frame) ReportErrorSummary(rep_typ_str, frame->info); } @@ -393,7 +418,7 @@ const int kMainThreadId = 1; -void PrintStack(const ReportStack *ent) { +void PrintStack(const ReportStack *ent, bool mark_frames) { if (ent == 0 || ent->frames == 0) { Printf(" [failed to restore the stack]\n"); return; Index: test/tsan/Darwin/external.cc =================================================================== --- test/tsan/Darwin/external.cc +++ test/tsan/Darwin/external.cc @@ -94,13 +94,25 @@ #else // defined(SHARED_LIB) +void UserCodeRead(MyObjectRef ref) { + ObjectRead(ref); +} + +void UserCodeWrite(MyObjectRef ref, long val) { + ObjectWrite(ref, val); +} + +void UserCodeWriteAnother(MyObjectRef ref, long val) { + ObjectWriteAnother(ref, val); +} + int main(int argc, char *argv[]) { InitializeLibrary(); { MyObjectRef ref = ObjectCreate(); - std::thread t1([ref]{ ObjectRead(ref); }); - std::thread t2([ref]{ ObjectRead(ref); }); + std::thread t1([ref]{ UserCodeRead(ref); }); + std::thread t2([ref]{ UserCodeRead(ref); }); t1.join(); t2.join(); } @@ -109,11 +121,11 @@ fprintf(stderr, "RR test done\n"); // CHECK: RR test done - + { MyObjectRef ref = ObjectCreate(); - std::thread t1([ref]{ ObjectRead(ref); }); - std::thread t2([ref]{ ObjectWrite(ref, 66); }); + std::thread t1([ref]{ UserCodeRead(ref); }); + std::thread t2([ref]{ UserCodeWrite(ref, 66); }); t1.join(); t2.join(); } @@ -127,19 +139,20 @@ // TEST3: WARNING: ThreadSanitizer: race on a library object // TEST3: {{Mutating|read-only}} access of object MyLibrary::MyObject at - // TEST3: {{ObjectWrite|ObjectRead}} + // TEST3: * #1 {{UserCodeWrite|UserCodeRead}} // TEST3: Previous {{mutating|read-only}} access of object MyLibrary::MyObject at - // TEST3: {{ObjectWrite|ObjectRead}} + // TEST3: * #1 {{UserCodeWrite|UserCodeRead}} + // TEST3: Issue is caused by frames marked with "*". // TEST3: Location is MyLibrary::MyObject object of size 16 at // TEST3: {{ObjectCreate}} - + fprintf(stderr, "RW test done\n"); // CHECK: RW test done - + { MyObjectRef ref = ObjectCreate(); - std::thread t1([ref]{ ObjectWrite(ref, 76); }); - std::thread t2([ref]{ ObjectWriteAnother(ref, 77); }); + std::thread t1([ref]{ UserCodeWrite(ref, 76); }); + std::thread t2([ref]{ UserCodeWriteAnother(ref, 77); }); t1.join(); t2.join(); } @@ -150,12 +163,13 @@ // TEST3: WARNING: ThreadSanitizer: race on a library object // TEST3: Mutating access of object MyLibrary::MyObject at - // TEST3: {{ObjectWrite|ObjectWriteAnother}} + // TEST3: * #1 {{UserCodeWrite|UserCodeWriteAnother}} // TEST3: Previous mutating access of object MyLibrary::MyObject at - // TEST3: {{ObjectWrite|ObjectWriteAnother}} + // TEST3: * #1 {{UserCodeWrite|UserCodeWriteAnother}} + // TEST3: Issue is caused by frames marked with "*". // TEST3: Location is MyLibrary::MyObject object of size 16 at // TEST3: {{ObjectCreate}} - + fprintf(stderr, "WW test done\n"); // CHECK: WW test done } Index: test/tsan/free_race.c =================================================================== --- test/tsan/free_race.c +++ test/tsan/free_race.c @@ -37,11 +37,12 @@ // CHECK-NOZUPP: WARNING: ThreadSanitizer: heap-use-after-free // CHECK-NOZUPP: Write of size 4 at {{.*}} by main thread{{.*}}: -// CHECK-NOZUPP: #0 Thread2 +// CHECK-NOZUPP: * #0 Thread2 // CHECK-NOZUPP: #1 main // CHECK-NOZUPP: Previous write of size 8 at {{.*}} by thread T1{{.*}}: // CHECK-NOZUPP: #0 free -// CHECK-NOZUPP: #{{(1|2)}} Thread1 +// CHECK-NOZUPP: * #{{(1|2)}} Thread1 +// CHECK-NOZUPP: Issue is caused by frames marked with "*". // CHECK-NOZUPP: SUMMARY: ThreadSanitizer: heap-use-after-free{{.*}}Thread2 // CHECK-SUPP: ThreadSanitizer: Matched 1 suppressions // CHECK-SUPP: 1 race:^Thread2$ Index: test/tsan/memcmp_race.cc =================================================================== --- test/tsan/memcmp_race.cc +++ test/tsan/memcmp_race.cc @@ -36,7 +36,8 @@ // CHECK: WARNING: ThreadSanitizer: data race // CHECK: Write of size 1 at [[ADDR]] by thread T2: // CHECK: #0 {{(memcpy|memmove)}} -// CHECK: #1 Thread2 +// CHECK: * #1 Thread2 // CHECK: Previous read of size 1 at [[ADDR]] by thread T1: // CHECK: #0 memcmp -// CHECK: #1 Thread1 +// CHECK: * #1 Thread1 +// CHECK: Issue is caused by frames marked with "*". Index: test/tsan/memcpy_race.cc =================================================================== --- test/tsan/memcpy_race.cc +++ test/tsan/memcpy_race.cc @@ -35,7 +35,8 @@ // CHECK: WARNING: ThreadSanitizer: data race // CHECK: Write of size 1 at [[ADDR]] by thread T2: // CHECK: #0 {{(memcpy|memmove)}} -// CHECK: #1 Thread2 +// CHECK: * #1 Thread2 // CHECK: Previous write of size 1 at [[ADDR]] by thread T1: // CHECK: #0 {{(memcpy|memmove)}} -// CHECK: #1 Thread1 +// CHECK: * #1 Thread1 +// CHECK: Issue is caused by frames marked with "*". Index: test/tsan/mutex_destroy_locked.cc =================================================================== --- test/tsan/mutex_destroy_locked.cc +++ test/tsan/mutex_destroy_locked.cc @@ -12,10 +12,11 @@ // CHECK: WARNING: ThreadSanitizer: destroy of a locked mutex // CHECK: #0 pthread_mutex_destroy -// CHECK: #1 main +// CHECK: * #1 main // CHECK: and: // CHECK: #0 pthread_mutex_lock -// CHECK: #1 main +// CHECK: * #1 main +// CHECK: Issue is caused by frames marked with "*". // CHECK: Mutex {{.*}} created at: // CHECK: #0 pthread_mutex_init // CHECK: #1 main