tsan_report.cc revision a27512c4e115df4f260501a94b8d343f9ed955af
1//===-- tsan_report.cc ----------------------------------------------------===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file is a part of ThreadSanitizer (TSan), a race detector. 11// 12//===----------------------------------------------------------------------===// 13#include "tsan_report.h" 14#include "tsan_platform.h" 15#include "tsan_rtl.h" 16#include "sanitizer_common/sanitizer_report_decorator.h" 17 18namespace __tsan { 19 20class Decorator: private __sanitizer::AnsiColorDecorator { 21 public: 22 Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { } 23 const char *Warning() { return Red(); } 24 const char *EndWarning() { return Default(); } 25 const char *Access() { return Blue(); } 26 const char *EndAccess() { return Default(); } 27 const char *ThreadDescription() { return Cyan(); } 28 const char *EndThreadDescription() { return Default(); } 29 const char *Location() { return Green(); } 30 const char *EndLocation() { return Default(); } 31 const char *Sleep() { return Yellow(); } 32 const char *EndSleep() { return Default(); } 33 const char *Mutex() { return Magenta(); } 34 const char *EndMutex() { return Default(); } 35}; 36 37ReportDesc::ReportDesc() 38 : stacks(MBlockReportStack) 39 , mops(MBlockReportMop) 40 , locs(MBlockReportLoc) 41 , mutexes(MBlockReportMutex) 42 , threads(MBlockReportThread) 43 , sleep() 44 , count() { 45} 46 47ReportMop::ReportMop() 48 : mset(MBlockReportMutex) { 49} 50 51ReportDesc::~ReportDesc() { 52 // FIXME(dvyukov): it must be leaking a lot of memory. 53} 54 55#ifndef TSAN_GO 56 57const int kThreadBufSize = 32; 58const char *thread_name(char *buf, int tid) { 59 if (tid == 0) 60 return "main thread"; 61 internal_snprintf(buf, kThreadBufSize, "thread T%d", tid); 62 return buf; 63} 64 65static const char *ReportTypeString(ReportType typ) { 66 if (typ == ReportTypeRace) 67 return "data race"; 68 if (typ == ReportTypeVptrRace) 69 return "data race on vptr (ctor/dtor vs virtual call)"; 70 if (typ == ReportTypeUseAfterFree) 71 return "heap-use-after-free"; 72 if (typ == ReportTypeThreadLeak) 73 return "thread leak"; 74 if (typ == ReportTypeMutexDestroyLocked) 75 return "destroy of a locked mutex"; 76 if (typ == ReportTypeSignalUnsafe) 77 return "signal-unsafe call inside of a signal"; 78 if (typ == ReportTypeErrnoInSignal) 79 return "signal handler spoils errno"; 80 return ""; 81} 82 83void PrintStack(const ReportStack *ent) { 84 if (ent == 0) { 85 Printf(" [failed to restore the stack]\n\n"); 86 return; 87 } 88 for (int i = 0; ent; ent = ent->next, i++) { 89 Printf(" #%d %s %s:%d", i, ent->func, ent->file, ent->line); 90 if (ent->col) 91 Printf(":%d", ent->col); 92 if (ent->module && ent->offset) 93 Printf(" (%s+%p)\n", ent->module, (void*)ent->offset); 94 else 95 Printf(" (%p)\n", (void*)ent->pc); 96 } 97 Printf("\n"); 98} 99 100static void PrintMutexSet(Vector<ReportMopMutex> const& mset) { 101 for (uptr i = 0; i < mset.Size(); i++) { 102 if (i == 0) 103 Printf(" (mutexes:"); 104 const ReportMopMutex m = mset[i]; 105 Printf(" %s M%llu", m.write ? "write" : "read", m.id); 106 Printf(i == mset.Size() - 1 ? ")" : ","); 107 } 108} 109 110static const char *MopDesc(bool first, bool write, bool atomic) { 111 return atomic ? (first ? (write ? "Atomic write" : "Atomic read") 112 : (write ? "Previous atomic write" : "Previous atomic read")) 113 : (first ? (write ? "Write" : "Read") 114 : (write ? "Previous write" : "Previous read")); 115} 116 117static void PrintMop(const ReportMop *mop, bool first) { 118 Decorator d; 119 char thrbuf[kThreadBufSize]; 120 Printf("%s", d.Access()); 121 Printf(" %s of size %d at %p by %s", 122 MopDesc(first, mop->write, mop->atomic), 123 mop->size, (void*)mop->addr, 124 thread_name(thrbuf, mop->tid)); 125 PrintMutexSet(mop->mset); 126 Printf(":\n"); 127 Printf("%s", d.EndAccess()); 128 PrintStack(mop->stack); 129} 130 131static void PrintLocation(const ReportLocation *loc) { 132 Decorator d; 133 char thrbuf[kThreadBufSize]; 134 bool print_stack = false; 135 Printf("%s", d.Location()); 136 if (loc->type == ReportLocationGlobal) { 137 Printf(" Location is global '%s' of size %zu at %zx (%s+%p)\n\n", 138 loc->name, loc->size, loc->addr, loc->module, loc->offset); 139 } else if (loc->type == ReportLocationHeap) { 140 char thrbuf[kThreadBufSize]; 141 Printf(" Location is heap block of size %zu at %p allocated by %s:\n", 142 loc->size, loc->addr, thread_name(thrbuf, loc->tid)); 143 print_stack = true; 144 } else if (loc->type == ReportLocationStack) { 145 Printf(" Location is stack of %s.\n\n", thread_name(thrbuf, loc->tid)); 146 } else if (loc->type == ReportLocationTLS) { 147 Printf(" Location is TLS of %s.\n\n", thread_name(thrbuf, loc->tid)); 148 } else if (loc->type == ReportLocationFD) { 149 Printf(" Location is file descriptor %d created by %s at:\n", 150 loc->fd, thread_name(thrbuf, loc->tid)); 151 print_stack = true; 152 } 153 Printf("%s", d.EndLocation()); 154 if (print_stack) 155 PrintStack(loc->stack); 156} 157 158static void PrintMutex(const ReportMutex *rm) { 159 Decorator d; 160 if (rm->destroyed) { 161 Printf("%s", d.Mutex()); 162 Printf(" Mutex M%llu is already destroyed.\n\n", rm->id); 163 Printf("%s", d.EndMutex()); 164 } else { 165 Printf("%s", d.Mutex()); 166 Printf(" Mutex M%llu created at:\n", rm->id); 167 Printf("%s", d.EndMutex()); 168 PrintStack(rm->stack); 169 } 170} 171 172static void PrintThread(const ReportThread *rt) { 173 Decorator d; 174 if (rt->id == 0) // Little sense in describing the main thread. 175 return; 176 Printf("%s", d.ThreadDescription()); 177 Printf(" Thread T%d", rt->id); 178 if (rt->name && rt->name[0] != '\0') 179 Printf(" '%s'", rt->name); 180 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