Alexey Samsonov | 6fbecdd | 2012-07-17 09:39:59 +0000 | [diff] [blame] | 1 | //===-- tsan_rtl_report.cc ------------------------------------------------===// |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 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 | |
Alexey Samsonov | c0d78c1 | 2012-06-04 13:27:49 +0000 | [diff] [blame] | 14 | #include "sanitizer_common/sanitizer_libc.h" |
Alexey Samsonov | 47b1634 | 2012-06-07 09:50:16 +0000 | [diff] [blame] | 15 | #include "sanitizer_common/sanitizer_placement_new.h" |
Dmitry Vyukov | ff35f1d | 2012-08-30 13:02:30 +0000 | [diff] [blame] | 16 | #include "sanitizer_common/sanitizer_stackdepot.h" |
Dmitry Vyukov | ad9da37 | 2012-12-06 12:16:15 +0000 | [diff] [blame] | 17 | #include "sanitizer_common/sanitizer_common.h" |
Dmitry Vyukov | 793e761 | 2013-01-29 14:20:12 +0000 | [diff] [blame] | 18 | #include "sanitizer_common/sanitizer_stacktrace.h" |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 19 | #include "tsan_platform.h" |
| 20 | #include "tsan_rtl.h" |
| 21 | #include "tsan_suppressions.h" |
| 22 | #include "tsan_symbolize.h" |
| 23 | #include "tsan_report.h" |
| 24 | #include "tsan_sync.h" |
| 25 | #include "tsan_mman.h" |
| 26 | #include "tsan_flags.h" |
Dmitry Vyukov | c2234cd | 2012-12-18 06:57:34 +0000 | [diff] [blame] | 27 | #include "tsan_fd.h" |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 28 | |
Alexey Samsonov | 591616d | 2012-09-11 09:44:48 +0000 | [diff] [blame] | 29 | namespace __tsan { |
Alexey Samsonov | 15a7761 | 2012-06-06 15:22:20 +0000 | [diff] [blame] | 30 | |
Dmitry Vyukov | ad9da37 | 2012-12-06 12:16:15 +0000 | [diff] [blame] | 31 | using namespace __sanitizer; // NOLINT |
| 32 | |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 33 | static ReportStack *SymbolizeStack(StackTrace trace); |
Dmitry Vyukov | 793e761 | 2013-01-29 14:20:12 +0000 | [diff] [blame] | 34 | |
Alexey Samsonov | 591616d | 2012-09-11 09:44:48 +0000 | [diff] [blame] | 35 | void TsanCheckFailed(const char *file, int line, const char *cond, |
| 36 | u64 v1, u64 v2) { |
Stephen Hines | 2d1fdb2 | 2014-05-28 23:58:16 -0700 | [diff] [blame] | 37 | // There is high probability that interceptors will check-fail as well, |
| 38 | // on the other hand there is no sense in processing interceptors |
| 39 | // since we are going to die soon. |
| 40 | ScopedIgnoreInterceptors ignore; |
Alexey Samsonov | b1fe302 | 2012-11-02 12:17:51 +0000 | [diff] [blame] | 41 | Printf("FATAL: ThreadSanitizer CHECK failed: " |
| 42 | "%s:%d \"%s\" (0x%zx, 0x%zx)\n", |
| 43 | file, line, cond, (uptr)v1, (uptr)v2); |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 44 | PrintCurrentStackSlow(StackTrace::GetCurrentPc()); |
Alexey Samsonov | 15a7761 | 2012-06-06 15:22:20 +0000 | [diff] [blame] | 45 | Die(); |
| 46 | } |
| 47 | |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 48 | // Can be overriden by an application/test to intercept reports. |
Dmitry Vyukov | 87dbdf5 | 2012-07-25 14:30:51 +0000 | [diff] [blame] | 49 | #ifdef TSAN_EXTERNAL_HOOKS |
| 50 | bool OnReport(const ReportDesc *rep, bool suppressed); |
| 51 | #else |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 52 | SANITIZER_WEAK_CXX_DEFAULT_IMPL |
| 53 | bool OnReport(const ReportDesc *rep, bool suppressed) { |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 54 | (void)rep; |
| 55 | return suppressed; |
| 56 | } |
Dmitry Vyukov | 87dbdf5 | 2012-07-25 14:30:51 +0000 | [diff] [blame] | 57 | #endif |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 58 | |
Stephen Hines | 86277eb | 2015-03-23 12:06:32 -0700 | [diff] [blame] | 59 | static void StackStripMain(SymbolizedStack *frames) { |
| 60 | SymbolizedStack *last_frame = nullptr; |
| 61 | SymbolizedStack *last_frame2 = nullptr; |
| 62 | for (SymbolizedStack *cur = frames; cur; cur = cur->next) { |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 63 | last_frame2 = last_frame; |
Stephen Hines | 86277eb | 2015-03-23 12:06:32 -0700 | [diff] [blame] | 64 | last_frame = cur; |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 65 | } |
| 66 | |
| 67 | if (last_frame2 == 0) |
| 68 | return; |
Stephen Hines | 86277eb | 2015-03-23 12:06:32 -0700 | [diff] [blame] | 69 | #ifndef SANITIZER_GO |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 70 | const char *last = last_frame->info.function; |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 71 | const char *last2 = last_frame2->info.function; |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 72 | // Strip frame above 'main' |
| 73 | if (last2 && 0 == internal_strcmp(last2, "main")) { |
Stephen Hines | 86277eb | 2015-03-23 12:06:32 -0700 | [diff] [blame] | 74 | last_frame->ClearAll(); |
| 75 | last_frame2->next = nullptr; |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 76 | // Strip our internal thread start routine. |
| 77 | } else if (last && 0 == internal_strcmp(last, "__tsan_thread_start_func")) { |
Stephen Hines | 86277eb | 2015-03-23 12:06:32 -0700 | [diff] [blame] | 78 | last_frame->ClearAll(); |
| 79 | last_frame2->next = nullptr; |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 80 | // Strip global ctors init. |
| 81 | } else if (last && 0 == internal_strcmp(last, "__do_global_ctors_aux")) { |
Stephen Hines | 86277eb | 2015-03-23 12:06:32 -0700 | [diff] [blame] | 82 | last_frame->ClearAll(); |
| 83 | last_frame2->next = nullptr; |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 84 | // If both are 0, then we probably just failed to symbolize. |
| 85 | } else if (last || last2) { |
| 86 | // Ensure that we recovered stack completely. Trimmed stack |
| 87 | // can actually happen if we do not instrument some code, |
Dmitry Vyukov | 0ab628c | 2012-09-06 15:18:14 +0000 | [diff] [blame] | 88 | // so it's only a debug print. However we must try hard to not miss it |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 89 | // due to our fault. |
Stephen Hines | 86277eb | 2015-03-23 12:06:32 -0700 | [diff] [blame] | 90 | DPrintf("Bottom stack frame is missed\n"); |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 91 | } |
Dmitry Vyukov | cb3a6b8 | 2012-07-06 20:23:59 +0000 | [diff] [blame] | 92 | #else |
Dmitry Vyukov | 1dc5f39 | 2013-06-06 13:31:35 +0000 | [diff] [blame] | 93 | // The last frame always point into runtime (gosched0, goexit0, runtime.main). |
Stephen Hines | 86277eb | 2015-03-23 12:06:32 -0700 | [diff] [blame] | 94 | last_frame->ClearAll(); |
| 95 | last_frame2->next = nullptr; |
Dmitry Vyukov | c510a2f | 2012-07-06 14:54:25 +0000 | [diff] [blame] | 96 | #endif |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 97 | } |
| 98 | |
Stephen Hines | 2d1fdb2 | 2014-05-28 23:58:16 -0700 | [diff] [blame] | 99 | ReportStack *SymbolizeStackId(u32 stack_id) { |
| 100 | if (stack_id == 0) |
| 101 | return 0; |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 102 | StackTrace stack = StackDepotGet(stack_id); |
| 103 | if (stack.trace == nullptr) |
| 104 | return nullptr; |
| 105 | return SymbolizeStack(stack); |
Stephen Hines | 2d1fdb2 | 2014-05-28 23:58:16 -0700 | [diff] [blame] | 106 | } |
| 107 | |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 108 | static ReportStack *SymbolizeStack(StackTrace trace) { |
| 109 | if (trace.size == 0) |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 110 | return 0; |
Stephen Hines | 86277eb | 2015-03-23 12:06:32 -0700 | [diff] [blame] | 111 | SymbolizedStack *top = nullptr; |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 112 | for (uptr si = 0; si < trace.size; si++) { |
| 113 | const uptr pc = trace.trace[si]; |
Dmitry Vyukov | e7718bc | 2013-06-17 19:57:03 +0000 | [diff] [blame] | 114 | uptr pc1 = pc; |
Stephen Hines | 86277eb | 2015-03-23 12:06:32 -0700 | [diff] [blame] | 115 | // We obtain the return address, but we're interested in the previous |
| 116 | // instruction. |
| 117 | if ((pc & kExternalPCBit) == 0) |
| 118 | pc1 = StackTrace::GetPreviousInstructionPc(pc); |
| 119 | SymbolizedStack *ent = SymbolizeCode(pc1); |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 120 | CHECK_NE(ent, 0); |
Stephen Hines | 86277eb | 2015-03-23 12:06:32 -0700 | [diff] [blame] | 121 | SymbolizedStack *last = ent; |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 122 | while (last->next) { |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 123 | last->info.address = pc; // restore original pc for report |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 124 | last = last->next; |
| 125 | } |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 126 | last->info.address = pc; // restore original pc for report |
Stephen Hines | 86277eb | 2015-03-23 12:06:32 -0700 | [diff] [blame] | 127 | last->next = top; |
| 128 | top = ent; |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 129 | } |
Stephen Hines | 86277eb | 2015-03-23 12:06:32 -0700 | [diff] [blame] | 130 | StackStripMain(top); |
| 131 | |
| 132 | ReportStack *stack = ReportStack::New(); |
| 133 | stack->frames = top; |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 134 | return stack; |
| 135 | } |
| 136 | |
| 137 | ScopedReport::ScopedReport(ReportType typ) { |
Stephen Hines | 2d1fdb2 | 2014-05-28 23:58:16 -0700 | [diff] [blame] | 138 | ctx->thread_registry->CheckLocked(); |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 139 | void *mem = internal_alloc(MBlockReport, sizeof(ReportDesc)); |
| 140 | rep_ = new(mem) ReportDesc; |
| 141 | rep_->typ = typ; |
Stephen Hines | 2d1fdb2 | 2014-05-28 23:58:16 -0700 | [diff] [blame] | 142 | ctx->report_mtx.Lock(); |
Alexey Samsonov | 7ed46ff | 2013-04-05 07:30:29 +0000 | [diff] [blame] | 143 | CommonSanitizerReportMutex.Lock(); |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 144 | } |
| 145 | |
| 146 | ScopedReport::~ScopedReport() { |
Alexey Samsonov | 7ed46ff | 2013-04-05 07:30:29 +0000 | [diff] [blame] | 147 | CommonSanitizerReportMutex.Unlock(); |
Stephen Hines | 2d1fdb2 | 2014-05-28 23:58:16 -0700 | [diff] [blame] | 148 | ctx->report_mtx.Unlock(); |
Dmitry Vyukov | aecf2e5 | 2012-12-04 15:46:05 +0000 | [diff] [blame] | 149 | DestroyAndFree(rep_); |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 150 | } |
| 151 | |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 152 | void ScopedReport::AddStack(StackTrace stack, bool suppressable) { |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 153 | ReportStack **rs = rep_->stacks.PushBack(); |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 154 | *rs = SymbolizeStack(stack); |
Stephen Hines | 6a211c5 | 2014-07-21 00:49:56 -0700 | [diff] [blame] | 155 | (*rs)->suppressable = suppressable; |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 156 | } |
| 157 | |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 158 | void ScopedReport::AddMemoryAccess(uptr addr, Shadow s, StackTrace stack, |
| 159 | const MutexSet *mset) { |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 160 | void *mem = internal_alloc(MBlockReportMop, sizeof(ReportMop)); |
| 161 | ReportMop *mop = new(mem) ReportMop; |
| 162 | rep_->mops.PushBack(mop); |
| 163 | mop->tid = s.tid(); |
| 164 | mop->addr = addr + s.addr0(); |
| 165 | mop->size = s.size(); |
Dmitry Vyukov | 334553e | 2013-02-01 09:42:06 +0000 | [diff] [blame] | 166 | mop->write = s.IsWrite(); |
Dmitry Vyukov | 0a07b35 | 2013-02-01 11:10:53 +0000 | [diff] [blame] | 167 | mop->atomic = s.IsAtomic(); |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 168 | mop->stack = SymbolizeStack(stack); |
Stephen Hines | 6a211c5 | 2014-07-21 00:49:56 -0700 | [diff] [blame] | 169 | if (mop->stack) |
| 170 | mop->stack->suppressable = true; |
Dmitry Vyukov | ad9da37 | 2012-12-06 12:16:15 +0000 | [diff] [blame] | 171 | for (uptr i = 0; i < mset->Size(); i++) { |
| 172 | MutexSet::Desc d = mset->Get(i); |
Stephen Hines | 2d1fdb2 | 2014-05-28 23:58:16 -0700 | [diff] [blame] | 173 | u64 mid = this->AddMutex(d.id); |
| 174 | ReportMopMutex mtx = {mid, d.write}; |
| 175 | mop->mset.PushBack(mtx); |
Dmitry Vyukov | ad9da37 | 2012-12-06 12:16:15 +0000 | [diff] [blame] | 176 | } |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 177 | } |
| 178 | |
Stephen Hines | 2d1fdb2 | 2014-05-28 23:58:16 -0700 | [diff] [blame] | 179 | void ScopedReport::AddUniqueTid(int unique_tid) { |
| 180 | rep_->unique_tids.PushBack(unique_tid); |
| 181 | } |
| 182 | |
Stephen Hines | 6a211c5 | 2014-07-21 00:49:56 -0700 | [diff] [blame] | 183 | void ScopedReport::AddThread(const ThreadContext *tctx, bool suppressable) { |
Dmitry Vyukov | ff35f1d | 2012-08-30 13:02:30 +0000 | [diff] [blame] | 184 | for (uptr i = 0; i < rep_->threads.Size(); i++) { |
Alexey Samsonov | 2bbd8be | 2013-03-15 13:48:44 +0000 | [diff] [blame] | 185 | if ((u32)rep_->threads[i]->id == tctx->tid) |
Dmitry Vyukov | ff35f1d | 2012-08-30 13:02:30 +0000 | [diff] [blame] | 186 | return; |
| 187 | } |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 188 | void *mem = internal_alloc(MBlockReportThread, sizeof(ReportThread)); |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 189 | ReportThread *rt = new(mem) ReportThread; |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 190 | rep_->threads.PushBack(rt); |
| 191 | rt->id = tctx->tid; |
Dmitry Vyukov | 7dccf3f | 2012-10-02 11:52:05 +0000 | [diff] [blame] | 192 | rt->pid = tctx->os_id; |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 193 | rt->running = (tctx->status == ThreadStatusRunning); |
Stephen Hines | 2d1fdb2 | 2014-05-28 23:58:16 -0700 | [diff] [blame] | 194 | rt->name = internal_strdup(tctx->name); |
Alexey Samsonov | 2bbd8be | 2013-03-15 13:48:44 +0000 | [diff] [blame] | 195 | rt->parent_tid = tctx->parent_tid; |
Dmitry Vyukov | 2c5284e | 2013-03-18 09:02:27 +0000 | [diff] [blame] | 196 | rt->stack = 0; |
Stephen Hines | 2d1fdb2 | 2014-05-28 23:58:16 -0700 | [diff] [blame] | 197 | rt->stack = SymbolizeStackId(tctx->creation_stack_id); |
Stephen Hines | 6a211c5 | 2014-07-21 00:49:56 -0700 | [diff] [blame] | 198 | if (rt->stack) |
| 199 | rt->stack->suppressable = suppressable; |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 200 | } |
| 201 | |
Stephen Hines | 86277eb | 2015-03-23 12:06:32 -0700 | [diff] [blame] | 202 | #ifndef SANITIZER_GO |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 203 | static bool FindThreadByUidLockedCallback(ThreadContextBase *tctx, void *arg) { |
| 204 | int unique_id = *(int *)arg; |
| 205 | return tctx->unique_id == (u32)unique_id; |
| 206 | } |
| 207 | |
Alexey Samsonov | df2ca17 | 2013-03-18 07:02:08 +0000 | [diff] [blame] | 208 | static ThreadContext *FindThreadByUidLocked(int unique_id) { |
Alexey Samsonov | 2bbd8be | 2013-03-15 13:48:44 +0000 | [diff] [blame] | 209 | ctx->thread_registry->CheckLocked(); |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 210 | return static_cast<ThreadContext *>( |
| 211 | ctx->thread_registry->FindThreadContextLocked( |
| 212 | FindThreadByUidLockedCallback, &unique_id)); |
Dmitry Vyukov | ff35f1d | 2012-08-30 13:02:30 +0000 | [diff] [blame] | 213 | } |
Dmitry Vyukov | fb917e9 | 2013-01-14 10:00:03 +0000 | [diff] [blame] | 214 | |
Dmitry Vyukov | f51c386 | 2013-03-18 19:47:36 +0000 | [diff] [blame] | 215 | static ThreadContext *FindThreadByTidLocked(int tid) { |
Dmitry Vyukov | f51c386 | 2013-03-18 19:47:36 +0000 | [diff] [blame] | 216 | ctx->thread_registry->CheckLocked(); |
| 217 | return static_cast<ThreadContext*>( |
| 218 | ctx->thread_registry->GetThreadLocked(tid)); |
| 219 | } |
| 220 | |
Alexey Samsonov | df2ca17 | 2013-03-18 07:02:08 +0000 | [diff] [blame] | 221 | static bool IsInStackOrTls(ThreadContextBase *tctx_base, void *arg) { |
| 222 | uptr addr = (uptr)arg; |
| 223 | ThreadContext *tctx = static_cast<ThreadContext*>(tctx_base); |
| 224 | if (tctx->status != ThreadStatusRunning) |
| 225 | return false; |
| 226 | ThreadState *thr = tctx->thr; |
| 227 | CHECK(thr); |
| 228 | return ((addr >= thr->stk_addr && addr < thr->stk_addr + thr->stk_size) || |
| 229 | (addr >= thr->tls_addr && addr < thr->tls_addr + thr->tls_size)); |
| 230 | } |
| 231 | |
Dmitry Vyukov | fb917e9 | 2013-01-14 10:00:03 +0000 | [diff] [blame] | 232 | ThreadContext *IsThreadStackOrTls(uptr addr, bool *is_stack) { |
Alexey Samsonov | 2bbd8be | 2013-03-15 13:48:44 +0000 | [diff] [blame] | 233 | ctx->thread_registry->CheckLocked(); |
Alexey Samsonov | df2ca17 | 2013-03-18 07:02:08 +0000 | [diff] [blame] | 234 | ThreadContext *tctx = static_cast<ThreadContext*>( |
| 235 | ctx->thread_registry->FindThreadContextLocked(IsInStackOrTls, |
| 236 | (void*)addr)); |
| 237 | if (!tctx) |
| 238 | return 0; |
| 239 | ThreadState *thr = tctx->thr; |
| 240 | CHECK(thr); |
| 241 | *is_stack = (addr >= thr->stk_addr && addr < thr->stk_addr + thr->stk_size); |
| 242 | return tctx; |
Dmitry Vyukov | fb917e9 | 2013-01-14 10:00:03 +0000 | [diff] [blame] | 243 | } |
Dmitry Vyukov | 0ab628c | 2012-09-06 15:18:14 +0000 | [diff] [blame] | 244 | #endif |
Dmitry Vyukov | ff35f1d | 2012-08-30 13:02:30 +0000 | [diff] [blame] | 245 | |
Stephen Hines | 6a211c5 | 2014-07-21 00:49:56 -0700 | [diff] [blame] | 246 | void ScopedReport::AddThread(int unique_tid, bool suppressable) { |
Stephen Hines | 86277eb | 2015-03-23 12:06:32 -0700 | [diff] [blame] | 247 | #ifndef SANITIZER_GO |
| 248 | if (const ThreadContext *tctx = FindThreadByUidLocked(unique_tid)) |
| 249 | AddThread(tctx, suppressable); |
Stephen Hines | 2d1fdb2 | 2014-05-28 23:58:16 -0700 | [diff] [blame] | 250 | #endif |
| 251 | } |
| 252 | |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 253 | void ScopedReport::AddMutex(const SyncVar *s) { |
Dmitry Vyukov | ad9da37 | 2012-12-06 12:16:15 +0000 | [diff] [blame] | 254 | for (uptr i = 0; i < rep_->mutexes.Size(); i++) { |
| 255 | if (rep_->mutexes[i]->id == s->uid) |
| 256 | return; |
| 257 | } |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 258 | void *mem = internal_alloc(MBlockReportMutex, sizeof(ReportMutex)); |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 259 | ReportMutex *rm = new(mem) ReportMutex; |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 260 | rep_->mutexes.PushBack(rm); |
Dmitry Vyukov | ad9da37 | 2012-12-06 12:16:15 +0000 | [diff] [blame] | 261 | rm->id = s->uid; |
Stephen Hines | 2d1fdb2 | 2014-05-28 23:58:16 -0700 | [diff] [blame] | 262 | rm->addr = s->addr; |
Dmitry Vyukov | ad9da37 | 2012-12-06 12:16:15 +0000 | [diff] [blame] | 263 | rm->destroyed = false; |
Stephen Hines | 2d1fdb2 | 2014-05-28 23:58:16 -0700 | [diff] [blame] | 264 | rm->stack = SymbolizeStackId(s->creation_stack_id); |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 265 | } |
| 266 | |
Stephen Hines | 2d1fdb2 | 2014-05-28 23:58:16 -0700 | [diff] [blame] | 267 | u64 ScopedReport::AddMutex(u64 id) { |
| 268 | u64 uid = 0; |
| 269 | u64 mid = id; |
| 270 | uptr addr = SyncVar::SplitId(id, &uid); |
Stephen Hines | 6a211c5 | 2014-07-21 00:49:56 -0700 | [diff] [blame] | 271 | SyncVar *s = ctx->metamap.GetIfExistsAndLock(addr); |
Stephen Hines | 2d1fdb2 | 2014-05-28 23:58:16 -0700 | [diff] [blame] | 272 | // Check that the mutex is still alive. |
| 273 | // Another mutex can be created at the same address, |
| 274 | // so check uid as well. |
| 275 | if (s && s->CheckId(uid)) { |
| 276 | mid = s->uid; |
| 277 | AddMutex(s); |
| 278 | } else { |
| 279 | AddDeadMutex(id); |
| 280 | } |
| 281 | if (s) |
Stephen Hines | 6a211c5 | 2014-07-21 00:49:56 -0700 | [diff] [blame] | 282 | s->mtx.Unlock(); |
Stephen Hines | 2d1fdb2 | 2014-05-28 23:58:16 -0700 | [diff] [blame] | 283 | return mid; |
| 284 | } |
| 285 | |
| 286 | void ScopedReport::AddDeadMutex(u64 id) { |
Dmitry Vyukov | ad9da37 | 2012-12-06 12:16:15 +0000 | [diff] [blame] | 287 | for (uptr i = 0; i < rep_->mutexes.Size(); i++) { |
| 288 | if (rep_->mutexes[i]->id == id) |
| 289 | return; |
| 290 | } |
| 291 | void *mem = internal_alloc(MBlockReportMutex, sizeof(ReportMutex)); |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 292 | ReportMutex *rm = new(mem) ReportMutex; |
Dmitry Vyukov | ad9da37 | 2012-12-06 12:16:15 +0000 | [diff] [blame] | 293 | rep_->mutexes.PushBack(rm); |
| 294 | rm->id = id; |
Stephen Hines | 2d1fdb2 | 2014-05-28 23:58:16 -0700 | [diff] [blame] | 295 | rm->addr = 0; |
Dmitry Vyukov | ad9da37 | 2012-12-06 12:16:15 +0000 | [diff] [blame] | 296 | rm->destroyed = true; |
| 297 | rm->stack = 0; |
| 298 | } |
| 299 | |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 300 | void ScopedReport::AddLocation(uptr addr, uptr size) { |
Dmitry Vyukov | ff35f1d | 2012-08-30 13:02:30 +0000 | [diff] [blame] | 301 | if (addr == 0) |
| 302 | return; |
Stephen Hines | 86277eb | 2015-03-23 12:06:32 -0700 | [diff] [blame] | 303 | #ifndef SANITIZER_GO |
Dmitry Vyukov | c2234cd | 2012-12-18 06:57:34 +0000 | [diff] [blame] | 304 | int fd = -1; |
| 305 | int creat_tid = -1; |
| 306 | u32 creat_stack = 0; |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 307 | if (FdLocation(addr, &fd, &creat_tid, &creat_stack)) { |
| 308 | ReportLocation *loc = ReportLocation::New(ReportLocationFD); |
Dmitry Vyukov | c2234cd | 2012-12-18 06:57:34 +0000 | [diff] [blame] | 309 | loc->fd = fd; |
| 310 | loc->tid = creat_tid; |
Stephen Hines | 2d1fdb2 | 2014-05-28 23:58:16 -0700 | [diff] [blame] | 311 | loc->stack = SymbolizeStackId(creat_stack); |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 312 | rep_->locs.PushBack(loc); |
Alexey Samsonov | df2ca17 | 2013-03-18 07:02:08 +0000 | [diff] [blame] | 313 | ThreadContext *tctx = FindThreadByUidLocked(creat_tid); |
Dmitry Vyukov | c2234cd | 2012-12-18 06:57:34 +0000 | [diff] [blame] | 314 | if (tctx) |
| 315 | AddThread(tctx); |
| 316 | return; |
| 317 | } |
Dmitry Vyukov | 0304941 | 2013-04-24 09:20:25 +0000 | [diff] [blame] | 318 | MBlock *b = 0; |
Stephen Hines | 6a211c5 | 2014-07-21 00:49:56 -0700 | [diff] [blame] | 319 | Allocator *a = allocator(); |
| 320 | if (a->PointerIsMine((void*)addr)) { |
| 321 | void *block_begin = a->GetBlockBegin((void*)addr); |
| 322 | if (block_begin) |
| 323 | b = ctx->metamap.GetBlock((uptr)block_begin); |
| 324 | } |
| 325 | if (b != 0) { |
| 326 | ThreadContext *tctx = FindThreadByTidLocked(b->tid); |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 327 | ReportLocation *loc = ReportLocation::New(ReportLocationHeap); |
| 328 | loc->heap_chunk_start = (uptr)allocator()->GetBlockBegin((void *)addr); |
| 329 | loc->heap_chunk_size = b->siz; |
Stephen Hines | 6a211c5 | 2014-07-21 00:49:56 -0700 | [diff] [blame] | 330 | loc->tid = tctx ? tctx->tid : b->tid; |
Stephen Hines | 6a211c5 | 2014-07-21 00:49:56 -0700 | [diff] [blame] | 331 | loc->stack = SymbolizeStackId(b->stk); |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 332 | rep_->locs.PushBack(loc); |
Dmitry Vyukov | ff35f1d | 2012-08-30 13:02:30 +0000 | [diff] [blame] | 333 | if (tctx) |
| 334 | AddThread(tctx); |
| 335 | return; |
| 336 | } |
Dmitry Vyukov | fb917e9 | 2013-01-14 10:00:03 +0000 | [diff] [blame] | 337 | bool is_stack = false; |
| 338 | if (ThreadContext *tctx = IsThreadStackOrTls(addr, &is_stack)) { |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 339 | ReportLocation *loc = |
| 340 | ReportLocation::New(is_stack ? ReportLocationStack : ReportLocationTLS); |
Dmitry Vyukov | fb917e9 | 2013-01-14 10:00:03 +0000 | [diff] [blame] | 341 | loc->tid = tctx->tid; |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 342 | rep_->locs.PushBack(loc); |
Dmitry Vyukov | fb917e9 | 2013-01-14 10:00:03 +0000 | [diff] [blame] | 343 | AddThread(tctx); |
| 344 | } |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 345 | if (ReportLocation *loc = SymbolizeData(addr)) { |
Stephen Hines | 6a211c5 | 2014-07-21 00:49:56 -0700 | [diff] [blame] | 346 | loc->suppressable = true; |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 347 | rep_->locs.PushBack(loc); |
Dmitry Vyukov | ff35f1d | 2012-08-30 13:02:30 +0000 | [diff] [blame] | 348 | return; |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 349 | } |
Dmitry Vyukov | fb917e9 | 2013-01-14 10:00:03 +0000 | [diff] [blame] | 350 | #endif |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 351 | } |
| 352 | |
Stephen Hines | 86277eb | 2015-03-23 12:06:32 -0700 | [diff] [blame] | 353 | #ifndef SANITIZER_GO |
Dmitry Vyukov | 8485311 | 2012-08-31 17:27:49 +0000 | [diff] [blame] | 354 | void ScopedReport::AddSleep(u32 stack_id) { |
Stephen Hines | 2d1fdb2 | 2014-05-28 23:58:16 -0700 | [diff] [blame] | 355 | rep_->sleep = SymbolizeStackId(stack_id); |
Dmitry Vyukov | 8485311 | 2012-08-31 17:27:49 +0000 | [diff] [blame] | 356 | } |
Dmitry Vyukov | 0ab628c | 2012-09-06 15:18:14 +0000 | [diff] [blame] | 357 | #endif |
Dmitry Vyukov | 8485311 | 2012-08-31 17:27:49 +0000 | [diff] [blame] | 358 | |
Dmitry Vyukov | 4536cb1 | 2013-03-21 16:55:17 +0000 | [diff] [blame] | 359 | void ScopedReport::SetCount(int count) { |
| 360 | rep_->count = count; |
| 361 | } |
| 362 | |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 363 | const ReportDesc *ScopedReport::GetReport() const { |
| 364 | return rep_; |
| 365 | } |
| 366 | |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 367 | void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk, |
| 368 | MutexSet *mset) { |
Dmitry Vyukov | ad9da37 | 2012-12-06 12:16:15 +0000 | [diff] [blame] | 369 | // This function restores stack trace and mutex set for the thread/epoch. |
| 370 | // It does so by getting stack trace and mutex set at the beginning of |
| 371 | // trace part, and then replaying the trace till the given epoch. |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 372 | Trace* trace = ThreadTrace(tid); |
| 373 | ReadLock l(&trace->mtx); |
Dmitry Vyukov | 0415ac0 | 2012-12-04 12:19:53 +0000 | [diff] [blame] | 374 | const int partidx = (epoch / kTracePartSize) % TraceParts(); |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 375 | TraceHeader* hdr = &trace->headers[partidx]; |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 376 | if (epoch < hdr->epoch0 || epoch >= hdr->epoch0 + kTracePartSize) |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 377 | return; |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 378 | CHECK_EQ(RoundDown(epoch, kTracePartSize), hdr->epoch0); |
Dmitry Vyukov | ad9da37 | 2012-12-06 12:16:15 +0000 | [diff] [blame] | 379 | const u64 epoch0 = RoundDown(epoch, TraceSize()); |
Dmitry Vyukov | d698edc | 2012-11-28 12:19:50 +0000 | [diff] [blame] | 380 | const u64 eend = epoch % TraceSize(); |
Dmitry Vyukov | 0415ac0 | 2012-12-04 12:19:53 +0000 | [diff] [blame] | 381 | const u64 ebegin = RoundDown(eend, kTracePartSize); |
Alexey Samsonov | e954101 | 2012-06-06 13:11:29 +0000 | [diff] [blame] | 382 | DPrintf("#%d: RestoreStack epoch=%zu ebegin=%zu eend=%zu partidx=%d\n", |
| 383 | tid, (uptr)epoch, (uptr)ebegin, (uptr)eend, partidx); |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 384 | Vector<uptr> stack(MBlockReportStack); |
| 385 | stack.Resize(hdr->stack0.size + 64); |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 386 | for (uptr i = 0; i < hdr->stack0.size; i++) { |
| 387 | stack[i] = hdr->stack0.trace[i]; |
Stephen Hines | 86277eb | 2015-03-23 12:06:32 -0700 | [diff] [blame] | 388 | DPrintf2(" #%02zu: pc=%zx\n", i, stack[i]); |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 389 | } |
Dmitry Vyukov | ad9da37 | 2012-12-06 12:16:15 +0000 | [diff] [blame] | 390 | if (mset) |
| 391 | *mset = hdr->mset0; |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 392 | uptr pos = hdr->stack0.size; |
Dmitry Vyukov | 385542a | 2012-11-28 10:35:31 +0000 | [diff] [blame] | 393 | Event *events = (Event*)GetThreadTrace(tid); |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 394 | for (uptr i = ebegin; i <= eend; i++) { |
Dmitry Vyukov | 385542a | 2012-11-28 10:35:31 +0000 | [diff] [blame] | 395 | Event ev = events[i]; |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 396 | EventType typ = (EventType)(ev >> 61); |
Dmitry Vyukov | ad9da37 | 2012-12-06 12:16:15 +0000 | [diff] [blame] | 397 | uptr pc = (uptr)(ev & ((1ull << 61) - 1)); |
Alexey Samsonov | e954101 | 2012-06-06 13:11:29 +0000 | [diff] [blame] | 398 | DPrintf2(" %zu typ=%d pc=%zx\n", i, typ, pc); |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 399 | if (typ == EventTypeMop) { |
| 400 | stack[pos] = pc; |
| 401 | } else if (typ == EventTypeFuncEnter) { |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 402 | if (stack.Size() < pos + 2) |
| 403 | stack.Resize(pos + 2); |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 404 | stack[pos++] = pc; |
| 405 | } else if (typ == EventTypeFuncExit) { |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 406 | if (pos > 0) |
| 407 | pos--; |
| 408 | } |
Dmitry Vyukov | ad9da37 | 2012-12-06 12:16:15 +0000 | [diff] [blame] | 409 | if (mset) { |
| 410 | if (typ == EventTypeLock) { |
| 411 | mset->Add(pc, true, epoch0 + i); |
| 412 | } else if (typ == EventTypeUnlock) { |
| 413 | mset->Del(pc, true); |
| 414 | } else if (typ == EventTypeRLock) { |
| 415 | mset->Add(pc, false, epoch0 + i); |
| 416 | } else if (typ == EventTypeRUnlock) { |
| 417 | mset->Del(pc, false); |
| 418 | } |
| 419 | } |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 420 | for (uptr j = 0; j <= pos; j++) |
Alexey Samsonov | e954101 | 2012-06-06 13:11:29 +0000 | [diff] [blame] | 421 | DPrintf2(" #%zu: %zx\n", j, stack[j]); |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 422 | } |
| 423 | if (pos == 0 && stack[0] == 0) |
| 424 | return; |
| 425 | pos++; |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 426 | stk->Init(&stack[0], pos); |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 427 | } |
| 428 | |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 429 | static bool HandleRacyStacks(ThreadState *thr, VarSizeStackTrace traces[2], |
| 430 | uptr addr_min, uptr addr_max) { |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 431 | bool equal_stack = false; |
Dmitry Vyukov | e0c4561 | 2013-06-11 11:44:43 +0000 | [diff] [blame] | 432 | RacyStacks hash; |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 433 | bool equal_address = false; |
| 434 | RacyAddress ra0 = {addr_min, addr_max}; |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 435 | { |
| 436 | ReadLock lock(&ctx->racy_mtx); |
| 437 | if (flags()->suppress_equal_stacks) { |
| 438 | hash.hash[0] = md5_hash(traces[0].trace, traces[0].size * sizeof(uptr)); |
| 439 | hash.hash[1] = md5_hash(traces[1].trace, traces[1].size * sizeof(uptr)); |
| 440 | for (uptr i = 0; i < ctx->racy_stacks.Size(); i++) { |
| 441 | if (hash == ctx->racy_stacks[i]) { |
| 442 | VPrintf(2, |
| 443 | "ThreadSanitizer: suppressing report as doubled (stack)\n"); |
| 444 | equal_stack = true; |
| 445 | break; |
| 446 | } |
| 447 | } |
| 448 | } |
| 449 | if (flags()->suppress_equal_addresses) { |
| 450 | for (uptr i = 0; i < ctx->racy_addresses.Size(); i++) { |
| 451 | RacyAddress ra2 = ctx->racy_addresses[i]; |
| 452 | uptr maxbeg = max(ra0.addr_min, ra2.addr_min); |
| 453 | uptr minend = min(ra0.addr_max, ra2.addr_max); |
| 454 | if (maxbeg < minend) { |
| 455 | VPrintf(2, "ThreadSanitizer: suppressing report as doubled (addr)\n"); |
| 456 | equal_address = true; |
| 457 | break; |
| 458 | } |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 459 | } |
| 460 | } |
| 461 | } |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 462 | if (!equal_stack && !equal_address) |
| 463 | return false; |
| 464 | if (!equal_stack) { |
| 465 | Lock lock(&ctx->racy_mtx); |
| 466 | ctx->racy_stacks.PushBack(hash); |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 467 | } |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 468 | if (!equal_address) { |
| 469 | Lock lock(&ctx->racy_mtx); |
| 470 | ctx->racy_addresses.PushBack(ra0); |
| 471 | } |
| 472 | return true; |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 473 | } |
| 474 | |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 475 | static void AddRacyStacks(ThreadState *thr, VarSizeStackTrace traces[2], |
| 476 | uptr addr_min, uptr addr_max) { |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 477 | Lock lock(&ctx->racy_mtx); |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 478 | if (flags()->suppress_equal_stacks) { |
Dmitry Vyukov | e0c4561 | 2013-06-11 11:44:43 +0000 | [diff] [blame] | 479 | RacyStacks hash; |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 480 | hash.hash[0] = md5_hash(traces[0].trace, traces[0].size * sizeof(uptr)); |
| 481 | hash.hash[1] = md5_hash(traces[1].trace, traces[1].size * sizeof(uptr)); |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 482 | ctx->racy_stacks.PushBack(hash); |
| 483 | } |
| 484 | if (flags()->suppress_equal_addresses) { |
| 485 | RacyAddress ra0 = {addr_min, addr_max}; |
| 486 | ctx->racy_addresses.PushBack(ra0); |
| 487 | } |
| 488 | } |
| 489 | |
Stephen Hines | 6a211c5 | 2014-07-21 00:49:56 -0700 | [diff] [blame] | 490 | bool OutputReport(ThreadState *thr, const ScopedReport &srep) { |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 491 | if (!flags()->report_bugs) |
| 492 | return false; |
| 493 | atomic_store_relaxed(&ctx->last_symbolize_time_ns, NanoTime()); |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 494 | const ReportDesc *rep = srep.GetReport(); |
Dmitry Vyukov | f754eb5 | 2013-03-27 17:59:57 +0000 | [diff] [blame] | 495 | Suppression *supp = 0; |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 496 | uptr pc_or_addr = 0; |
| 497 | for (uptr i = 0; pc_or_addr == 0 && i < rep->mops.Size(); i++) |
| 498 | pc_or_addr = IsSuppressed(rep->typ, rep->mops[i]->stack, &supp); |
| 499 | for (uptr i = 0; pc_or_addr == 0 && i < rep->stacks.Size(); i++) |
| 500 | pc_or_addr = IsSuppressed(rep->typ, rep->stacks[i], &supp); |
| 501 | for (uptr i = 0; pc_or_addr == 0 && i < rep->threads.Size(); i++) |
| 502 | pc_or_addr = IsSuppressed(rep->typ, rep->threads[i]->stack, &supp); |
| 503 | for (uptr i = 0; pc_or_addr == 0 && i < rep->locs.Size(); i++) |
| 504 | pc_or_addr = IsSuppressed(rep->typ, rep->locs[i], &supp); |
| 505 | if (pc_or_addr != 0) { |
| 506 | Lock lock(&ctx->fired_suppressions_mtx); |
| 507 | FiredSuppression s = {srep.GetReport()->typ, pc_or_addr, supp}; |
Alexey Samsonov | 0a05e5f | 2013-06-14 11:18:58 +0000 | [diff] [blame] | 508 | ctx->fired_suppressions.push_back(s); |
Dmitry Vyukov | 158c6ac | 2012-10-05 15:51:32 +0000 | [diff] [blame] | 509 | } |
Stephen Hines | 6a211c5 | 2014-07-21 00:49:56 -0700 | [diff] [blame] | 510 | { |
| 511 | bool old_is_freeing = thr->is_freeing; |
| 512 | thr->is_freeing = false; |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 513 | bool suppressed = OnReport(rep, pc_or_addr != 0); |
Stephen Hines | 6a211c5 | 2014-07-21 00:49:56 -0700 | [diff] [blame] | 514 | thr->is_freeing = old_is_freeing; |
| 515 | if (suppressed) |
| 516 | return false; |
| 517 | } |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 518 | PrintReport(rep); |
Dmitry Vyukov | dbac0a4 | 2013-08-13 15:33:00 +0000 | [diff] [blame] | 519 | ctx->nreported++; |
| 520 | if (flags()->halt_on_error) |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 521 | Die(); |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 522 | return true; |
| 523 | } |
| 524 | |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 525 | bool IsFiredSuppression(Context *ctx, ReportType type, StackTrace trace) { |
| 526 | ReadLock lock(&ctx->fired_suppressions_mtx); |
Alexey Samsonov | 0a05e5f | 2013-06-14 11:18:58 +0000 | [diff] [blame] | 527 | for (uptr k = 0; k < ctx->fired_suppressions.size(); k++) { |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 528 | if (ctx->fired_suppressions[k].type != type) |
Dmitry Vyukov | 158c6ac | 2012-10-05 15:51:32 +0000 | [diff] [blame] | 529 | continue; |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 530 | for (uptr j = 0; j < trace.size; j++) { |
Dmitry Vyukov | f754eb5 | 2013-03-27 17:59:57 +0000 | [diff] [blame] | 531 | FiredSuppression *s = &ctx->fired_suppressions[k]; |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 532 | if (trace.trace[j] == s->pc_or_addr) { |
Dmitry Vyukov | f754eb5 | 2013-03-27 17:59:57 +0000 | [diff] [blame] | 533 | if (s->supp) |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 534 | atomic_fetch_add(&s->supp->hit_count, 1, memory_order_relaxed); |
Dmitry Vyukov | 158c6ac | 2012-10-05 15:51:32 +0000 | [diff] [blame] | 535 | return true; |
Dmitry Vyukov | f754eb5 | 2013-03-27 17:59:57 +0000 | [diff] [blame] | 536 | } |
Dmitry Vyukov | 158c6ac | 2012-10-05 15:51:32 +0000 | [diff] [blame] | 537 | } |
| 538 | } |
| 539 | return false; |
| 540 | } |
| 541 | |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 542 | static bool IsFiredSuppression(Context *ctx, ReportType type, uptr addr) { |
| 543 | ReadLock lock(&ctx->fired_suppressions_mtx); |
Alexey Samsonov | 0a05e5f | 2013-06-14 11:18:58 +0000 | [diff] [blame] | 544 | for (uptr k = 0; k < ctx->fired_suppressions.size(); k++) { |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 545 | if (ctx->fired_suppressions[k].type != type) |
Dmitry Vyukov | 3996833 | 2013-06-10 15:38:44 +0000 | [diff] [blame] | 546 | continue; |
| 547 | FiredSuppression *s = &ctx->fired_suppressions[k]; |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 548 | if (addr == s->pc_or_addr) { |
Dmitry Vyukov | 3996833 | 2013-06-10 15:38:44 +0000 | [diff] [blame] | 549 | if (s->supp) |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 550 | atomic_fetch_add(&s->supp->hit_count, 1, memory_order_relaxed); |
Dmitry Vyukov | 3996833 | 2013-06-10 15:38:44 +0000 | [diff] [blame] | 551 | return true; |
| 552 | } |
| 553 | } |
| 554 | return false; |
| 555 | } |
| 556 | |
Dmitry Vyukov | 3285866 | 2013-02-01 14:41:58 +0000 | [diff] [blame] | 557 | static bool RaceBetweenAtomicAndFree(ThreadState *thr) { |
| 558 | Shadow s0(thr->racy_state[0]); |
| 559 | Shadow s1(thr->racy_state[1]); |
| 560 | CHECK(!(s0.IsAtomic() && s1.IsAtomic())); |
| 561 | if (!s0.IsAtomic() && !s1.IsAtomic()) |
| 562 | return true; |
| 563 | if (s0.IsAtomic() && s1.IsFreed()) |
| 564 | return true; |
| 565 | if (s1.IsAtomic() && thr->is_freeing) |
| 566 | return true; |
| 567 | return false; |
| 568 | } |
| 569 | |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 570 | void ReportRace(ThreadState *thr) { |
Stephen Hines | 6a211c5 | 2014-07-21 00:49:56 -0700 | [diff] [blame] | 571 | CheckNoLocks(thr); |
| 572 | |
Stephen Hines | 2d1fdb2 | 2014-05-28 23:58:16 -0700 | [diff] [blame] | 573 | // Symbolizer makes lots of intercepted calls. If we try to process them, |
| 574 | // at best it will cause deadlocks on internal mutexes. |
| 575 | ScopedIgnoreInterceptors ignore; |
| 576 | |
Dmitry Vyukov | 8a32677 | 2012-11-07 16:14:12 +0000 | [diff] [blame] | 577 | if (!flags()->report_bugs) |
| 578 | return; |
Dmitry Vyukov | 3285866 | 2013-02-01 14:41:58 +0000 | [diff] [blame] | 579 | if (!flags()->report_atomic_races && !RaceBetweenAtomicAndFree(thr)) |
| 580 | return; |
| 581 | |
Dmitry Vyukov | 069ce82 | 2012-05-17 14:17:51 +0000 | [diff] [blame] | 582 | bool freed = false; |
| 583 | { |
| 584 | Shadow s(thr->racy_state[1]); |
| 585 | freed = s.GetFreedAndReset(); |
| 586 | thr->racy_state[1] = s.raw(); |
| 587 | } |
| 588 | |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 589 | uptr addr = ShadowToMem((uptr)thr->racy_shadow_addr); |
| 590 | uptr addr_min = 0; |
| 591 | uptr addr_max = 0; |
| 592 | { |
| 593 | uptr a0 = addr + Shadow(thr->racy_state[0]).addr0(); |
| 594 | uptr a1 = addr + Shadow(thr->racy_state[1]).addr0(); |
| 595 | uptr e0 = a0 + Shadow(thr->racy_state[0]).size(); |
| 596 | uptr e1 = a1 + Shadow(thr->racy_state[1]).size(); |
| 597 | addr_min = min(a0, a1); |
| 598 | addr_max = max(e0, e1); |
| 599 | if (IsExpectedReport(addr_min, addr_max - addr_min)) |
| 600 | return; |
| 601 | } |
| 602 | |
Dmitry Vyukov | 0dc47b6 | 2013-03-21 15:37:39 +0000 | [diff] [blame] | 603 | ReportType typ = ReportTypeRace; |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 604 | if (thr->is_vptr_access && freed) |
| 605 | typ = ReportTypeVptrUseAfterFree; |
| 606 | else if (thr->is_vptr_access) |
Dmitry Vyukov | 0dc47b6 | 2013-03-21 15:37:39 +0000 | [diff] [blame] | 607 | typ = ReportTypeVptrRace; |
| 608 | else if (freed) |
| 609 | typ = ReportTypeUseAfterFree; |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 610 | |
| 611 | if (IsFiredSuppression(ctx, typ, addr)) |
Dmitry Vyukov | 3996833 | 2013-06-10 15:38:44 +0000 | [diff] [blame] | 612 | return; |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 613 | |
Dmitry Vyukov | 069ce82 | 2012-05-17 14:17:51 +0000 | [diff] [blame] | 614 | const uptr kMop = 2; |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 615 | VarSizeStackTrace traces[kMop]; |
Dmitry Vyukov | 385542a | 2012-11-28 10:35:31 +0000 | [diff] [blame] | 616 | const uptr toppc = TraceTopPC(thr); |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 617 | ObtainCurrentStack(thr, toppc, &traces[0]); |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 618 | if (IsFiredSuppression(ctx, typ, traces[0])) |
Dmitry Vyukov | 158c6ac | 2012-10-05 15:51:32 +0000 | [diff] [blame] | 619 | return; |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 620 | |
| 621 | // MutexSet is too large to live on stack. |
| 622 | Vector<u64> mset_buffer(MBlockScopedBuf); |
| 623 | mset_buffer.Resize(sizeof(MutexSet) / sizeof(u64) + 1); |
| 624 | MutexSet *mset2 = new(&mset_buffer[0]) MutexSet(); |
| 625 | |
Dmitry Vyukov | 158c6ac | 2012-10-05 15:51:32 +0000 | [diff] [blame] | 626 | Shadow s2(thr->racy_state[1]); |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 627 | RestoreStack(s2.tid(), s2.epoch(), &traces[1], mset2); |
| 628 | if (IsFiredSuppression(ctx, typ, traces[1])) |
Dmitry Vyukov | 3996833 | 2013-06-10 15:38:44 +0000 | [diff] [blame] | 629 | return; |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 630 | |
| 631 | if (HandleRacyStacks(thr, traces, addr_min, addr_max)) |
| 632 | return; |
| 633 | |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 634 | ThreadRegistryLock l0(ctx->thread_registry); |
| 635 | ScopedReport rep(typ); |
Dmitry Vyukov | 069ce82 | 2012-05-17 14:17:51 +0000 | [diff] [blame] | 636 | for (uptr i = 0; i < kMop; i++) { |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 637 | Shadow s(thr->racy_state[i]); |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 638 | rep.AddMemoryAccess(addr, s, traces[i], i == 0 ? &thr->mset : mset2); |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 639 | } |
| 640 | |
Dmitry Vyukov | 069ce82 | 2012-05-17 14:17:51 +0000 | [diff] [blame] | 641 | for (uptr i = 0; i < kMop; i++) { |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 642 | FastState s(thr->racy_state[i]); |
Alexey Samsonov | 2bbd8be | 2013-03-15 13:48:44 +0000 | [diff] [blame] | 643 | ThreadContext *tctx = static_cast<ThreadContext*>( |
| 644 | ctx->thread_registry->GetThreadLocked(s.tid())); |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 645 | if (s.epoch() < tctx->epoch0 || s.epoch() > tctx->epoch1) |
| 646 | continue; |
| 647 | rep.AddThread(tctx); |
| 648 | } |
| 649 | |
Dmitry Vyukov | ff35f1d | 2012-08-30 13:02:30 +0000 | [diff] [blame] | 650 | rep.AddLocation(addr_min, addr_max - addr_min); |
| 651 | |
Stephen Hines | 86277eb | 2015-03-23 12:06:32 -0700 | [diff] [blame] | 652 | #ifndef SANITIZER_GO |
Dmitry Vyukov | 8485311 | 2012-08-31 17:27:49 +0000 | [diff] [blame] | 653 | { // NOLINT |
| 654 | Shadow s(thr->racy_state[1]); |
| 655 | if (s.epoch() <= thr->last_sleep_clock.get(s.tid())) |
| 656 | rep.AddSleep(thr->last_sleep_stack_id); |
| 657 | } |
| 658 | #endif |
| 659 | |
Stephen Hines | 6a211c5 | 2014-07-21 00:49:56 -0700 | [diff] [blame] | 660 | if (!OutputReport(thr, rep)) |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 661 | return; |
| 662 | |
| 663 | AddRacyStacks(thr, traces, addr_min, addr_max); |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 664 | } |
| 665 | |
Dmitry Vyukov | 1da1056 | 2012-09-01 12:13:18 +0000 | [diff] [blame] | 666 | void PrintCurrentStack(ThreadState *thr, uptr pc) { |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 667 | VarSizeStackTrace trace; |
| 668 | ObtainCurrentStack(thr, pc, &trace); |
Dmitry Vyukov | 1da1056 | 2012-09-01 12:13:18 +0000 | [diff] [blame] | 669 | PrintStack(SymbolizeStack(trace)); |
| 670 | } |
| 671 | |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 672 | void PrintCurrentStackSlow(uptr pc) { |
Stephen Hines | 86277eb | 2015-03-23 12:06:32 -0700 | [diff] [blame] | 673 | #ifndef SANITIZER_GO |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 674 | BufferedStackTrace *ptrace = |
| 675 | new(internal_alloc(MBlockStackTrace, sizeof(BufferedStackTrace))) |
| 676 | BufferedStackTrace(); |
| 677 | ptrace->Unwind(kStackTraceMax, pc, 0, 0, 0, 0, false); |
Dmitry Vyukov | 924047f | 2013-04-30 11:57:32 +0000 | [diff] [blame] | 678 | for (uptr i = 0; i < ptrace->size / 2; i++) { |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 679 | uptr tmp = ptrace->trace_buffer[i]; |
| 680 | ptrace->trace_buffer[i] = ptrace->trace_buffer[ptrace->size - i - 1]; |
| 681 | ptrace->trace_buffer[ptrace->size - i - 1] = tmp; |
Dmitry Vyukov | 924047f | 2013-04-30 11:57:32 +0000 | [diff] [blame] | 682 | } |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 683 | PrintStack(SymbolizeStack(*ptrace)); |
Dmitry Vyukov | 793e761 | 2013-01-29 14:20:12 +0000 | [diff] [blame] | 684 | #endif |
| 685 | } |
| 686 | |
Kostya Serebryany | 7ac4148 | 2012-05-10 13:48:04 +0000 | [diff] [blame] | 687 | } // namespace __tsan |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 688 | |
| 689 | using namespace __tsan; |
| 690 | |
| 691 | extern "C" { |
| 692 | SANITIZER_INTERFACE_ATTRIBUTE |
| 693 | void __sanitizer_print_stack_trace() { |
| 694 | PrintCurrentStackSlow(StackTrace::GetCurrentPc()); |
| 695 | } |
| 696 | } // extern "C" |