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