tsan_report.cc revision a27512c4e115df4f260501a94b8d343f9ed955af
1311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff//===-- tsan_report.cc ----------------------------------------------------===// 2311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff// 3311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff// The LLVM Compiler Infrastructure 4311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff// 5311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff// This file is distributed under the University of Illinois Open Source 6311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff// License. See LICENSE.TXT for details. 7311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff// 8311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff//===----------------------------------------------------------------------===// 9311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff// 10311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff// This file is a part of ThreadSanitizer (TSan), a race detector. 11311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff// 12311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff//===----------------------------------------------------------------------===// 13311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff#include "tsan_report.h" 14311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff#include "tsan_platform.h" 15311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff#include "tsan_rtl.h" 16311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff#include "sanitizer_common/sanitizer_report_decorator.h" 17311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff 18311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiffnamespace __tsan { 19311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff 2028db8079f707ebdf43ce62cdfd96eb39c8f889e0openvcdiffclass Decorator: private __sanitizer::AnsiColorDecorator { 2128db8079f707ebdf43ce62cdfd96eb39c8f889e0openvcdiff public: 2228db8079f707ebdf43ce62cdfd96eb39c8f889e0openvcdiff Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { } 23311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff const char *Warning() { return Red(); } 24311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff const char *EndWarning() { return Default(); } 25311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff const char *Access() { return Blue(); } 26311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff const char *EndAccess() { return Default(); } 27311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff const char *ThreadDescription() { return Cyan(); } 28311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff const char *EndThreadDescription() { return Default(); } 29311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff const char *Location() { return Green(); } 30311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff const char *EndLocation() { return Default(); } 31311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff const char *Sleep() { return Yellow(); } 32311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff const char *EndSleep() { return Default(); } 33311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff const char *Mutex() { return Magenta(); } 34311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff const char *EndMutex() { return Default(); } 35311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff}; 36311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff 37311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiffReportDesc::ReportDesc() 38311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff : stacks(MBlockReportStack) 39311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff , mops(MBlockReportMop) 40311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff , locs(MBlockReportLoc) 41311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff , mutexes(MBlockReportMutex) 42311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff , threads(MBlockReportThread) 43311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff , sleep() 44311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff , count() { 45311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff} 46311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff 47311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiffReportMop::ReportMop() 48311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff : mset(MBlockReportMutex) { 49311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff} 50311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff 51311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiffReportDesc::~ReportDesc() { 52311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff // FIXME(dvyukov): it must be leaking a lot of memory. 53311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff} 54311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff 55311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff#ifndef TSAN_GO 56311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff 57311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiffconst int kThreadBufSize = 32; 58311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiffconst char *thread_name(char *buf, int tid) { 59311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff if (tid == 0) 60311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff return "main thread"; 61311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff internal_snprintf(buf, kThreadBufSize, "thread T%d", tid); 62311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff return buf; 63311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff} 64311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff 65311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiffstatic const char *ReportTypeString(ReportType typ) { 66311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff if (typ == ReportTypeRace) 67311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff return "data race"; 68311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff if (typ == ReportTypeVptrRace) 69311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff return "data race on vptr (ctor/dtor vs virtual call)"; 70311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff if (typ == ReportTypeUseAfterFree) 71311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff return "heap-use-after-free"; 72311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff if (typ == ReportTypeThreadLeak) 73311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff return "thread leak"; 74311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff if (typ == ReportTypeMutexDestroyLocked) 75311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff return "destroy of a locked mutex"; 76311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff if (typ == ReportTypeSignalUnsafe) 77311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff return "signal-unsafe call inside of a signal"; 78311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff if (typ == ReportTypeErrnoInSignal) 79311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff return "signal handler spoils errno"; 80311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff return ""; 81311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff} 82311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff 83311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiffvoid PrintStack(const ReportStack *ent) { 84311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff if (ent == 0) { 85311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf(" [failed to restore the stack]\n\n"); 86311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff return; 87311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff } 88311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff for (int i = 0; ent; ent = ent->next, i++) { 89311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf(" #%d %s %s:%d", i, ent->func, ent->file, ent->line); 90311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff if (ent->col) 91311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf(":%d", ent->col); 92311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff if (ent->module && ent->offset) 93311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf(" (%s+%p)\n", ent->module, (void*)ent->offset); 94311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff else 95311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf(" (%p)\n", (void*)ent->pc); 96311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff } 97311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf("\n"); 98311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff} 99311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff 100311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiffstatic void PrintMutexSet(Vector<ReportMopMutex> const& mset) { 101311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff for (uptr i = 0; i < mset.Size(); i++) { 102311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff if (i == 0) 103311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf(" (mutexes:"); 104311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff const ReportMopMutex m = mset[i]; 105311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf(" %s M%llu", m.write ? "write" : "read", m.id); 106311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf(i == mset.Size() - 1 ? ")" : ","); 107311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff } 108311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff} 109311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff 110311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiffstatic const char *MopDesc(bool first, bool write, bool atomic) { 111311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff return atomic ? (first ? (write ? "Atomic write" : "Atomic read") 112311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff : (write ? "Previous atomic write" : "Previous atomic read")) 113311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff : (first ? (write ? "Write" : "Read") 114311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff : (write ? "Previous write" : "Previous read")); 115311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff} 116311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff 117311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiffstatic void PrintMop(const ReportMop *mop, bool first) { 118311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Decorator d; 119311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff char thrbuf[kThreadBufSize]; 120311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf("%s", d.Access()); 121311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf(" %s of size %d at %p by %s", 122311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff MopDesc(first, mop->write, mop->atomic), 123311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff mop->size, (void*)mop->addr, 124311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff thread_name(thrbuf, mop->tid)); 125311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff PrintMutexSet(mop->mset); 126311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf(":\n"); 127311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf("%s", d.EndAccess()); 128311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff PrintStack(mop->stack); 129311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff} 130311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff 131311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiffstatic void PrintLocation(const ReportLocation *loc) { 132311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Decorator d; 133311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff char thrbuf[kThreadBufSize]; 134311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff bool print_stack = false; 135311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf("%s", d.Location()); 136311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff if (loc->type == ReportLocationGlobal) { 137311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf(" Location is global '%s' of size %zu at %zx (%s+%p)\n\n", 138311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff loc->name, loc->size, loc->addr, loc->module, loc->offset); 139311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff } else if (loc->type == ReportLocationHeap) { 140311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff char thrbuf[kThreadBufSize]; 141311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf(" Location is heap block of size %zu at %p allocated by %s:\n", 142311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff loc->size, loc->addr, thread_name(thrbuf, loc->tid)); 143311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff print_stack = true; 144311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff } else if (loc->type == ReportLocationStack) { 145311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf(" Location is stack of %s.\n\n", thread_name(thrbuf, loc->tid)); 146311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff } else if (loc->type == ReportLocationTLS) { 147311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf(" Location is TLS of %s.\n\n", thread_name(thrbuf, loc->tid)); 148311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff } else if (loc->type == ReportLocationFD) { 149311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf(" Location is file descriptor %d created by %s at:\n", 150311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff loc->fd, thread_name(thrbuf, loc->tid)); 151311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff print_stack = true; 152311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff } 153311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf("%s", d.EndLocation()); 154311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff if (print_stack) 155311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff PrintStack(loc->stack); 156311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff} 157311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff 158311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiffstatic void PrintMutex(const ReportMutex *rm) { 159311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Decorator d; 160311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff if (rm->destroyed) { 161311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf("%s", d.Mutex()); 162311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf(" Mutex M%llu is already destroyed.\n\n", rm->id); 163311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf("%s", d.EndMutex()); 164311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff } else { 165311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf("%s", d.Mutex()); 166311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf(" Mutex M%llu created at:\n", rm->id); 167311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf("%s", d.EndMutex()); 168311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff PrintStack(rm->stack); 169311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff } 170311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff} 171311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff 172311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiffstatic void PrintThread(const ReportThread *rt) { 173311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Decorator d; 174311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff if (rt->id == 0) // Little sense in describing the main thread. 175311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff return; 176311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf("%s", d.ThreadDescription()); 177311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf(" Thread T%d", rt->id); 178311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff if (rt->name && rt->name[0] != '\0') 179311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff Printf(" '%s'", rt->name); 180311c71486f5f6074e5ba62a7f4c5397c8700b868openvcdiff char thrbuf[kThreadBufSize]; 181 Printf(" (tid=%zu, %s) created by %s", 182 rt->pid, rt->running ? "running" : "finished", 183 thread_name(thrbuf, rt->parent_tid)); 184 if (rt->stack) 185 Printf(" at:"); 186 Printf("\n"); 187 Printf("%s", d.EndThreadDescription()); 188 PrintStack(rt->stack); 189} 190 191static void PrintSleep(const ReportStack *s) { 192 Decorator d; 193 Printf("%s", d.Sleep()); 194 Printf(" As if synchronized via sleep:\n"); 195 Printf("%s", d.EndSleep()); 196 PrintStack(s); 197} 198 199static ReportStack *ChooseSummaryStack(const ReportDesc *rep) { 200 if (rep->mops.Size()) 201 return rep->mops[0]->stack; 202 if (rep->stacks.Size()) 203 return rep->stacks[0]; 204 if (rep->mutexes.Size()) 205 return rep->mutexes[0]->stack; 206 if (rep->threads.Size()) 207 return rep->threads[0]->stack; 208 return 0; 209} 210 211ReportStack *SkipTsanInternalFrames(ReportStack *ent) { 212 while (FrameIsInternal(ent) && ent->next) 213 ent = ent->next; 214 return ent; 215} 216 217void PrintReport(const ReportDesc *rep) { 218 Decorator d; 219 Printf("==================\n"); 220 const char *rep_typ_str = ReportTypeString(rep->typ); 221 Printf("%s", d.Warning()); 222 Printf("WARNING: ThreadSanitizer: %s (pid=%d)\n", rep_typ_str, 223 (int)internal_getpid()); 224 Printf("%s", d.EndWarning()); 225 226 for (uptr i = 0; i < rep->stacks.Size(); i++) { 227 if (i) 228 Printf(" and:\n"); 229 PrintStack(rep->stacks[i]); 230 } 231 232 for (uptr i = 0; i < rep->mops.Size(); i++) 233 PrintMop(rep->mops[i], i == 0); 234 235 if (rep->sleep) 236 PrintSleep(rep->sleep); 237 238 for (uptr i = 0; i < rep->locs.Size(); i++) 239 PrintLocation(rep->locs[i]); 240 241 for (uptr i = 0; i < rep->mutexes.Size(); i++) 242 PrintMutex(rep->mutexes[i]); 243 244 for (uptr i = 0; i < rep->threads.Size(); i++) 245 PrintThread(rep->threads[i]); 246 247 if (rep->typ == ReportTypeThreadLeak && rep->count > 1) 248 Printf(" And %d more similar thread leaks.\n\n", rep->count - 1); 249 250 if (ReportStack *ent = SkipTsanInternalFrames(ChooseSummaryStack(rep))) 251 ReportErrorSummary(rep_typ_str, ent->file, ent->line, ent->func); 252 253 Printf("==================\n"); 254} 255 256#else // #ifndef TSAN_GO 257 258const int kMainThreadId = 1; 259 260void PrintStack(const ReportStack *ent) { 261 if (ent == 0) { 262 Printf(" [failed to restore the stack]\n"); 263 return; 264 } 265 for (int i = 0; ent; ent = ent->next, i++) { 266 Printf(" %s()\n %s:%d +0x%zx\n", 267 ent->func, ent->file, ent->line, (void*)ent->offset); 268 } 269} 270 271static void PrintMop(const ReportMop *mop, bool first) { 272 Printf("\n"); 273 Printf("%s by ", 274 (first ? (mop->write ? "Write" : "Read") 275 : (mop->write ? "Previous write" : "Previous read"))); 276 if (mop->tid == kMainThreadId) 277 Printf("main goroutine:\n"); 278 else 279 Printf("goroutine %d:\n", mop->tid); 280 PrintStack(mop->stack); 281} 282 283static void PrintThread(const ReportThread *rt) { 284 if (rt->id == kMainThreadId) 285 return; 286 Printf("\n"); 287 Printf("Goroutine %d (%s) created at:\n", 288 rt->id, rt->running ? "running" : "finished"); 289 PrintStack(rt->stack); 290} 291 292void PrintReport(const ReportDesc *rep) { 293 Printf("==================\n"); 294 Printf("WARNING: DATA RACE"); 295 for (uptr i = 0; i < rep->mops.Size(); i++) 296 PrintMop(rep->mops[i], i == 0); 297 for (uptr i = 0; i < rep->threads.Size(); i++) 298 PrintThread(rep->threads[i]); 299 Printf("==================\n"); 300} 301 302#endif 303 304} // namespace __tsan 305