tsan_report.cc revision 9d95475f8adb1e08e45484feb03570ae2be2d0f3
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 17namespace __tsan { 18 19ReportDesc::ReportDesc() 20 : stacks(MBlockReportStack) 21 , mops(MBlockReportMop) 22 , locs(MBlockReportLoc) 23 , mutexes(MBlockReportMutex) 24 , threads(MBlockReportThread) 25 , sleep() 26 , count() { 27} 28 29ReportMop::ReportMop() 30 : mset(MBlockReportMutex) { 31} 32 33ReportDesc::~ReportDesc() { 34 // FIXME(dvyukov): it must be leaking a lot of memory. 35} 36 37#ifndef TSAN_GO 38 39const int kThreadBufSize = 32; 40const char *thread_name(char *buf, int tid) { 41 if (tid == 0) 42 return "main thread"; 43 internal_snprintf(buf, kThreadBufSize, "thread T%d", tid); 44 return buf; 45} 46 47static const char *ReportTypeString(ReportType typ) { 48 if (typ == ReportTypeRace) 49 return "data race"; 50 if (typ == ReportTypeVptrRace) 51 return "data race on vptr (ctor/dtor vs virtual call)"; 52 if (typ == ReportTypeUseAfterFree) 53 return "heap-use-after-free"; 54 if (typ == ReportTypeThreadLeak) 55 return "thread leak"; 56 if (typ == ReportTypeMutexDestroyLocked) 57 return "destroy of a locked mutex"; 58 if (typ == ReportTypeSignalUnsafe) 59 return "signal-unsafe call inside of a signal"; 60 if (typ == ReportTypeErrnoInSignal) 61 return "signal handler spoils errno"; 62 return ""; 63} 64 65void PrintStack(const ReportStack *ent) { 66 if (ent == 0) { 67 Printf(" [failed to restore the stack]\n\n"); 68 return; 69 } 70 for (int i = 0; ent; ent = ent->next, i++) { 71 Printf(" #%d %s %s:%d", i, ent->func, ent->file, ent->line); 72 if (ent->col) 73 Printf(":%d", ent->col); 74 if (ent->module && ent->offset) 75 Printf(" (%s+%p)\n", ent->module, (void*)ent->offset); 76 else 77 Printf(" (%p)\n", (void*)ent->pc); 78 } 79 Printf("\n"); 80} 81 82static void PrintMutexSet(Vector<ReportMopMutex> const& mset) { 83 for (uptr i = 0; i < mset.Size(); i++) { 84 if (i == 0) 85 Printf(" (mutexes:"); 86 const ReportMopMutex m = mset[i]; 87 Printf(" %s M%llu", m.write ? "write" : "read", m.id); 88 Printf(i == mset.Size() - 1 ? ")" : ","); 89 } 90} 91 92static const char *MopDesc(bool first, bool write, bool atomic) { 93 return atomic ? (first ? (write ? "Atomic write" : "Atomic read") 94 : (write ? "Previous atomic write" : "Previous atomic read")) 95 : (first ? (write ? "Write" : "Read") 96 : (write ? "Previous write" : "Previous read")); 97} 98 99static void PrintMop(const ReportMop *mop, bool first) { 100 char thrbuf[kThreadBufSize]; 101 Printf(" %s of size %d at %p by %s", 102 MopDesc(first, mop->write, mop->atomic), 103 mop->size, (void*)mop->addr, 104 thread_name(thrbuf, mop->tid)); 105 PrintMutexSet(mop->mset); 106 Printf(":\n"); 107 PrintStack(mop->stack); 108} 109 110static void PrintLocation(const ReportLocation *loc) { 111 char thrbuf[kThreadBufSize]; 112 if (loc->type == ReportLocationGlobal) { 113 Printf(" Location is global '%s' of size %zu at %zx (%s+%p)\n\n", 114 loc->name, loc->size, loc->addr, loc->module, loc->offset); 115 } else if (loc->type == ReportLocationHeap) { 116 char thrbuf[kThreadBufSize]; 117 Printf(" Location is heap block of size %zu at %p allocated by %s:\n", 118 loc->size, loc->addr, thread_name(thrbuf, loc->tid)); 119 PrintStack(loc->stack); 120 } else if (loc->type == ReportLocationStack) { 121 Printf(" Location is stack of %s.\n\n", thread_name(thrbuf, loc->tid)); 122 } else if (loc->type == ReportLocationTLS) { 123 Printf(" Location is TLS of %s.\n\n", thread_name(thrbuf, loc->tid)); 124 } else if (loc->type == ReportLocationFD) { 125 Printf(" Location is file descriptor %d created by %s at:\n", 126 loc->fd, thread_name(thrbuf, loc->tid)); 127 PrintStack(loc->stack); 128 } 129} 130 131static void PrintMutex(const ReportMutex *rm) { 132 if (rm->destroyed) { 133 Printf(" Mutex M%llu is already destroyed.\n\n", rm->id); 134 } else { 135 Printf(" Mutex M%llu created at:\n", rm->id); 136 PrintStack(rm->stack); 137 } 138} 139 140static void PrintThread(const ReportThread *rt) { 141 if (rt->id == 0) // Little sense in describing the main thread. 142 return; 143 Printf(" Thread T%d", rt->id); 144 if (rt->name && rt->name[0] != '\0') 145 Printf(" '%s'", rt->name); 146 char thrbuf[kThreadBufSize]; 147 Printf(" (tid=%zu, %s) created by %s", 148 rt->pid, rt->running ? "running" : "finished", 149 thread_name(thrbuf, rt->parent_tid)); 150 if (rt->stack) 151 Printf(" at:"); 152 Printf("\n"); 153 PrintStack(rt->stack); 154} 155 156static void PrintSleep(const ReportStack *s) { 157 Printf(" As if synchronized via sleep:\n"); 158 PrintStack(s); 159} 160 161static ReportStack *ChooseSummaryStack(const ReportDesc *rep) { 162 if (rep->mops.Size()) 163 return rep->mops[0]->stack; 164 if (rep->stacks.Size()) 165 return rep->stacks[0]; 166 if (rep->mutexes.Size()) 167 return rep->mutexes[0]->stack; 168 if (rep->threads.Size()) 169 return rep->threads[0]->stack; 170 return 0; 171} 172 173ReportStack *SkipTsanInternalFrames(ReportStack *ent) { 174 while (FrameIsInternal(ent) && ent->next) 175 ent = ent->next; 176 return ent; 177} 178 179void PrintReport(const ReportDesc *rep) { 180 Printf("==================\n"); 181 const char *rep_typ_str = ReportTypeString(rep->typ); 182 Printf("WARNING: ThreadSanitizer: %s (pid=%d)\n", rep_typ_str, 183 (int)internal_getpid()); 184 185 for (uptr i = 0; i < rep->stacks.Size(); i++) { 186 if (i) 187 Printf(" and:\n"); 188 PrintStack(rep->stacks[i]); 189 } 190 191 for (uptr i = 0; i < rep->mops.Size(); i++) 192 PrintMop(rep->mops[i], i == 0); 193 194 if (rep->sleep) 195 PrintSleep(rep->sleep); 196 197 for (uptr i = 0; i < rep->locs.Size(); i++) 198 PrintLocation(rep->locs[i]); 199 200 for (uptr i = 0; i < rep->mutexes.Size(); i++) 201 PrintMutex(rep->mutexes[i]); 202 203 for (uptr i = 0; i < rep->threads.Size(); i++) 204 PrintThread(rep->threads[i]); 205 206 if (rep->typ == ReportTypeThreadLeak && rep->count > 1) 207 Printf(" And %d more similar thread leaks.\n\n", rep->count - 1); 208 209 if (ReportStack *ent = SkipTsanInternalFrames(ChooseSummaryStack(rep))) 210 ReportErrorSummary(rep_typ_str, ent->file, ent->line, ent->func); 211 212 Printf("==================\n"); 213} 214 215#else // #ifndef TSAN_GO 216 217const int kMainThreadId = 1; 218 219void PrintStack(const ReportStack *ent) { 220 if (ent == 0) { 221 Printf(" [failed to restore the stack]\n"); 222 return; 223 } 224 for (int i = 0; ent; ent = ent->next, i++) { 225 Printf(" %s()\n %s:%d +0x%zx\n", 226 ent->func, ent->file, ent->line, (void*)ent->offset); 227 } 228} 229 230static void PrintMop(const ReportMop *mop, bool first) { 231 Printf("\n"); 232 Printf("%s by ", 233 (first ? (mop->write ? "Write" : "Read") 234 : (mop->write ? "Previous write" : "Previous read"))); 235 if (mop->tid == kMainThreadId) 236 Printf("main goroutine:\n"); 237 else 238 Printf("goroutine %d:\n", mop->tid); 239 PrintStack(mop->stack); 240} 241 242static void PrintThread(const ReportThread *rt) { 243 if (rt->id == kMainThreadId) 244 return; 245 Printf("\n"); 246 Printf("Goroutine %d (%s) created at:\n", 247 rt->id, rt->running ? "running" : "finished"); 248 PrintStack(rt->stack); 249} 250 251void PrintReport(const ReportDesc *rep) { 252 Printf("==================\n"); 253 Printf("WARNING: DATA RACE"); 254 for (uptr i = 0; i < rep->mops.Size(); i++) 255 PrintMop(rep->mops[i], i == 0); 256 for (uptr i = 0; i < rep->threads.Size(); i++) 257 PrintThread(rep->threads[i]); 258 Printf("==================\n"); 259} 260 261#endif 262 263} // namespace __tsan 264