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 17 namespace __tsan { 18 19 ReportDesc::ReportDesc() 20 : stacks(MBlockReportStack) 21 , mops(MBlockReportMop) 22 , locs(MBlockReportLoc) 23 , mutexes(MBlockReportMutex) 24 , threads(MBlockReportThread) 25 , sleep() 26 , count() { 27 } 28 29 ReportMop::ReportMop() 30 : mset(MBlockReportMutex) { 31 } 32 33 ReportDesc::~ReportDesc() { 34 // FIXME(dvyukov): it must be leaking a lot of memory. 35 } 36 37 #ifndef TSAN_GO 38 39 const int kThreadBufSize = 32; 40 const 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 47 static 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 65 void 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 82 static 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 92 static 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 99 static 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 110 static 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 131 static 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 140 static 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 156 static void PrintSleep(const ReportStack *s) { 157 Printf(" As if synchronized via sleep:\n"); 158 PrintStack(s); 159 } 160 161 static 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 173 ReportStack *SkipTsanInternalFrames(ReportStack *ent) { 174 while (FrameIsInternal(ent) && ent->next) 175 ent = ent->next; 176 return ent; 177 } 178 179 void 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 216 217 void PrintStack(const ReportStack *ent) { 218 if (ent == 0) { 219 Printf(" [failed to restore the stack]\n\n"); 220 return; 221 } 222 for (int i = 0; ent; ent = ent->next, i++) { 223 Printf(" %s()\n %s:%d +0x%zx\n", 224 ent->func, ent->file, ent->line, (void*)ent->offset); 225 } 226 Printf("\n"); 227 } 228 229 static void PrintMop(const ReportMop *mop, bool first) { 230 Printf("%s by goroutine %d:\n", 231 (first ? (mop->write ? "Write" : "Read") 232 : (mop->write ? "Previous write" : "Previous read")), 233 mop->tid); 234 PrintStack(mop->stack); 235 } 236 237 static void PrintThread(const ReportThread *rt) { 238 if (rt->id == 0) // Little sense in describing the main thread. 239 return; 240 Printf("Goroutine %d (%s) created at:\n", 241 rt->id, rt->running ? "running" : "finished"); 242 PrintStack(rt->stack); 243 } 244 245 void PrintReport(const ReportDesc *rep) { 246 Printf("==================\n"); 247 Printf("WARNING: DATA RACE\n"); 248 for (uptr i = 0; i < rep->mops.Size(); i++) 249 PrintMop(rep->mops[i], i == 0); 250 for (uptr i = 0; i < rep->threads.Size(); i++) 251 PrintThread(rep->threads[i]); 252 Printf("==================\n"); 253 } 254 255 #endif 256 257 } // namespace __tsan 258