tsan_rtl_report.cc revision ff35f1d82b4f145b3477ef27a7a2e7b63c486988
16fbecdd97512bd7d9ccef130e99650d446b50444Alexey Samsonov//===-- tsan_rtl_report.cc ------------------------------------------------===// 27ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany// 37ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany// The LLVM Compiler Infrastructure 47ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany// 57ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany// This file is distributed under the University of Illinois Open Source 67ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany// License. See LICENSE.TXT for details. 77ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany// 87ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany//===----------------------------------------------------------------------===// 97ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany// 107ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany// This file is a part of ThreadSanitizer (TSan), a race detector. 117ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany// 127ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany//===----------------------------------------------------------------------===// 137ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 14c0d78c1de1f2607c874020d27b72cf989c5ce092Alexey Samsonov#include "sanitizer_common/sanitizer_libc.h" 1547b1634df012507799eb39aa17d4022d748ba67bAlexey Samsonov#include "sanitizer_common/sanitizer_placement_new.h" 16ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov#include "sanitizer_common/sanitizer_stackdepot.h" 177ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany#include "tsan_platform.h" 187ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany#include "tsan_rtl.h" 197ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany#include "tsan_suppressions.h" 207ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany#include "tsan_symbolize.h" 217ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany#include "tsan_report.h" 227ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany#include "tsan_sync.h" 237ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany#include "tsan_mman.h" 247ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany#include "tsan_flags.h" 257ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 2615a77612e0a89c1df444a2034e531c8968d0cedfAlexey Samsonovnamespace __sanitizer { 2715a77612e0a89c1df444a2034e531c8968d0cedfAlexey Samsonovusing namespace __tsan; 2815a77612e0a89c1df444a2034e531c8968d0cedfAlexey Samsonov 2915a77612e0a89c1df444a2034e531c8968d0cedfAlexey Samsonovvoid CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2) { 3015a77612e0a89c1df444a2034e531c8968d0cedfAlexey Samsonov ScopedInRtl in_rtl; 315164ad434eccbfa9ad8097cf25146626313643f9Alexey Samsonov TsanPrintf("FATAL: ThreadSanitizer CHECK failed: " 325164ad434eccbfa9ad8097cf25146626313643f9Alexey Samsonov "%s:%d \"%s\" (0x%zx, 0x%zx)\n", 3315a77612e0a89c1df444a2034e531c8968d0cedfAlexey Samsonov file, line, cond, (uptr)v1, (uptr)v2); 3415a77612e0a89c1df444a2034e531c8968d0cedfAlexey Samsonov Die(); 3515a77612e0a89c1df444a2034e531c8968d0cedfAlexey Samsonov} 3615a77612e0a89c1df444a2034e531c8968d0cedfAlexey Samsonov 3715a77612e0a89c1df444a2034e531c8968d0cedfAlexey Samsonov} // namespace __sanitizer 3815a77612e0a89c1df444a2034e531c8968d0cedfAlexey Samsonov 397ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryanynamespace __tsan { 407ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 417ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany// Can be overriden by an application/test to intercept reports. 4287dbdf5fd6cb9f1b90a0a97b7675bd8cad8a0264Dmitry Vyukov#ifdef TSAN_EXTERNAL_HOOKS 4387dbdf5fd6cb9f1b90a0a97b7675bd8cad8a0264Dmitry Vyukovbool OnReport(const ReportDesc *rep, bool suppressed); 4487dbdf5fd6cb9f1b90a0a97b7675bd8cad8a0264Dmitry Vyukov#else 457ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryanybool WEAK OnReport(const ReportDesc *rep, bool suppressed) { 467ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany (void)rep; 477ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany return suppressed; 487ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany} 4987dbdf5fd6cb9f1b90a0a97b7675bd8cad8a0264Dmitry Vyukov#endif 507ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 517ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryanystatic void StackStripMain(ReportStack *stack) { 527ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany ReportStack *last_frame = 0; 537ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany ReportStack *last_frame2 = 0; 54f54c0e3321e2381fca3f02faefaaa6639d59c7cfDmitry Vyukov const char *prefix = "__interceptor_"; 557ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany uptr prefix_len = internal_strlen(prefix); 567ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany const char *path_prefix = flags()->strip_path_prefix; 577ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany uptr path_prefix_len = internal_strlen(path_prefix); 5887dbdf5fd6cb9f1b90a0a97b7675bd8cad8a0264Dmitry Vyukov char *pos; 597ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany for (ReportStack *ent = stack; ent; ent = ent->next) { 60d51a1a10cba87be50e9ada9fa21337c387edb237Dmitry Vyukov if (ent->func && 0 == internal_strncmp(ent->func, prefix, prefix_len)) 617ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany ent->func += prefix_len; 6287dbdf5fd6cb9f1b90a0a97b7675bd8cad8a0264Dmitry Vyukov if (ent->file && (pos = internal_strstr(ent->file, path_prefix))) 6387dbdf5fd6cb9f1b90a0a97b7675bd8cad8a0264Dmitry Vyukov ent->file = pos + path_prefix_len; 647ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany if (ent->file && ent->file[0] == '.' && ent->file[1] == '/') 657ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany ent->file += 2; 667ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany last_frame2 = last_frame; 677ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany last_frame = ent; 687ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } 697ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 707ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany if (last_frame2 == 0) 717ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany return; 727ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany const char *last = last_frame->func; 73cb3a6b82ae406613f8870519d2acda1ee1c8f2b5Dmitry Vyukov#ifndef TSAN_GO 747ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany const char *last2 = last_frame2->func; 757ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany // Strip frame above 'main' 767ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany if (last2 && 0 == internal_strcmp(last2, "main")) { 777ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany last_frame2->next = 0; 787ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany // Strip our internal thread start routine. 797ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } else if (last && 0 == internal_strcmp(last, "__tsan_thread_start_func")) { 807ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany last_frame2->next = 0; 817ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany // Strip global ctors init. 827ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } else if (last && 0 == internal_strcmp(last, "__do_global_ctors_aux")) { 837ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany last_frame2->next = 0; 847ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany // If both are 0, then we probably just failed to symbolize. 857ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } else if (last || last2) { 867ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany // Ensure that we recovered stack completely. Trimmed stack 877ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany // can actually happen if we do not instrument some code, 887ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany // so it's only a DCHECK. However we must try hard to not miss it 897ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany // due to our fault. 90e954101f6602ac181a2c3accfbbad0ae51b0bf7cAlexey Samsonov TsanPrintf("Bottom stack frame of stack %zx is missed\n", stack->pc); 917ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } 92cb3a6b82ae406613f8870519d2acda1ee1c8f2b5Dmitry Vyukov#else 93cb3a6b82ae406613f8870519d2acda1ee1c8f2b5Dmitry Vyukov if (last && 0 == internal_strcmp(last, "schedunlock")) 94cb3a6b82ae406613f8870519d2acda1ee1c8f2b5Dmitry Vyukov last_frame2->next = 0; 95c510a2f264a22ff60333fc48e5fa12d41cefba3cDmitry Vyukov#endif 967ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany} 977ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 987ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryanystatic ReportStack *SymbolizeStack(const StackTrace& trace) { 997ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany if (trace.IsEmpty()) 1007ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany return 0; 1017ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany ReportStack *stack = 0; 1027ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany for (uptr si = 0; si < trace.Size(); si++) { 1037ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany // We obtain the return address, that is, address of the next instruction, 1047ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany // so offset it by 1 byte. 1057ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany bool is_last = (si == trace.Size() - 1); 1067ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany ReportStack *ent = SymbolizeCode(trace.Get(si) - !is_last); 1077ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany CHECK_NE(ent, 0); 1087ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany ReportStack *last = ent; 1097ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany while (last->next) { 1107ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany last->pc += !is_last; 1117ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany last = last->next; 1127ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } 1137ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany last->pc += !is_last; 1147ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany last->next = stack; 1157ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany stack = ent; 1167ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } 1177ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany StackStripMain(stack); 1187ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany return stack; 1197ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany} 1207ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 1217ac41484ea322e0ea5774df681660269f5dc321eKostya SerebryanyScopedReport::ScopedReport(ReportType typ) { 1227ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany ctx_ = CTX(); 1237ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany void *mem = internal_alloc(MBlockReport, sizeof(ReportDesc)); 1247ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany rep_ = new(mem) ReportDesc; 1257ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany rep_->typ = typ; 1267ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany ctx_->report_mtx.Lock(); 1277ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany} 1287ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 1297ac41484ea322e0ea5774df681660269f5dc321eKostya SerebryanyScopedReport::~ScopedReport() { 1307ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany ctx_->report_mtx.Unlock(); 1317ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany rep_->~ReportDesc(); 1327ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany internal_free(rep_); 1337ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany} 1347ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 1357ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryanyvoid ScopedReport::AddStack(const StackTrace *stack) { 1367ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany ReportStack **rs = rep_->stacks.PushBack(); 1377ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany *rs = SymbolizeStack(*stack); 1387ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany} 1397ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 1407ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryanyvoid ScopedReport::AddMemoryAccess(uptr addr, Shadow s, 1417ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany const StackTrace *stack) { 1427ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany void *mem = internal_alloc(MBlockReportMop, sizeof(ReportMop)); 1437ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany ReportMop *mop = new(mem) ReportMop; 1447ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany rep_->mops.PushBack(mop); 1457ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany mop->tid = s.tid(); 1467ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany mop->addr = addr + s.addr0(); 1477ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany mop->size = s.size(); 1487ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany mop->write = s.is_write(); 1497ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany mop->nmutex = 0; 1507ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany mop->stack = SymbolizeStack(*stack); 1517ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany} 1527ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 1537ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryanyvoid ScopedReport::AddThread(const ThreadContext *tctx) { 154ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov for (uptr i = 0; i < rep_->threads.Size(); i++) { 155ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov if (rep_->threads[i]->id == tctx->tid) 156ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov return; 157ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov } 1587ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany void *mem = internal_alloc(MBlockReportThread, sizeof(ReportThread)); 1597ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany ReportThread *rt = new(mem) ReportThread(); 1607ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany rep_->threads.PushBack(rt); 1617ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany rt->id = tctx->tid; 1627ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany rt->running = (tctx->status == ThreadStatusRunning); 1637ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany rt->stack = SymbolizeStack(tctx->creation_stack); 1647ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany} 1657ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 166ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukovstatic ThreadContext *FindThread(int unique_id) { 167ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov CTX()->thread_mtx.CheckLocked(); 168ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov for (unsigned i = 0; i < kMaxTid; i++) { 169ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov ThreadContext *tctx = CTX()->threads[i]; 170ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov if (tctx && tctx->unique_id == unique_id) { 171ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov return tctx; 172ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov } 173ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov } 174ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov return 0; 175ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov} 176ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov 1777ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryanyvoid ScopedReport::AddMutex(const SyncVar *s) { 1787ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany void *mem = internal_alloc(MBlockReportMutex, sizeof(ReportMutex)); 1797ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany ReportMutex *rm = new(mem) ReportMutex(); 1807ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany rep_->mutexes.PushBack(rm); 1817ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany rm->id = 42; 1827ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany rm->stack = SymbolizeStack(s->creation_stack); 1837ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany} 1847ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 1857ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryanyvoid ScopedReport::AddLocation(uptr addr, uptr size) { 186ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov if (addr == 0) 187ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov return; 188ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov#ifndef TSAN_GO 189ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov if (allocator()->PointerIsMine((void*)addr)) { 190ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov MBlock *b = user_mblock(0, (void*)addr); 191ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov ThreadContext *tctx = FindThread(b->alloc_tid); 192ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov void *mem = internal_alloc(MBlockReportLoc, sizeof(ReportLocation)); 193ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov ReportLocation *loc = new(mem) ReportLocation(); 194ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov rep_->locs.PushBack(loc); 195ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov loc->type = ReportLocationHeap; 196ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov loc->addr = (uptr)allocator()->GetBlockBegin((void*)addr); 197ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov loc->size = b->size; 198ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov loc->tid = tctx ? tctx->tid : b->alloc_tid; 199ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov loc->name = 0; 200ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov loc->file = 0; 201ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov loc->line = 0; 202ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov loc->stack = 0; 203ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov uptr ssz = 0; 204ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov const uptr *stack = StackDepotGet(b->alloc_stack_id, &ssz); 205ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov if (stack) { 206ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov StackTrace trace; 207ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov trace.Init(stack, ssz); 208ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov loc->stack = SymbolizeStack(trace); 209ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov } 210ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov if (tctx) 211ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov AddThread(tctx); 212ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov return; 213ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov } 214ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov#endif 2157ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany ReportStack *symb = SymbolizeData(addr); 2167ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany if (symb) { 2177ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany void *mem = internal_alloc(MBlockReportLoc, sizeof(ReportLocation)); 2187ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany ReportLocation *loc = new(mem) ReportLocation(); 2197ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany rep_->locs.PushBack(loc); 2207ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany loc->type = ReportLocationGlobal; 2217ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany loc->addr = addr; 2227ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany loc->size = size; 2237ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany loc->tid = 0; 2247ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany loc->name = symb->func; 2257ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany loc->file = symb->file; 2267ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany loc->line = symb->line; 2277ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany loc->stack = 0; 2287ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany internal_free(symb); 229ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov return; 2307ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } 2317ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany} 2327ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 2337ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryanyconst ReportDesc *ScopedReport::GetReport() const { 2347ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany return rep_; 2357ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany} 2367ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 237332c62b52b3603be872b28bd3ea5e739aa28cd05Dmitry Vyukovvoid RestoreStack(int tid, const u64 epoch, StackTrace *stk) { 2387ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany ThreadContext *tctx = CTX()->threads[tid]; 2397ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany if (tctx == 0) 2407ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany return; 2417ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany Trace* trace = 0; 2427ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany if (tctx->status == ThreadStatusRunning) { 2437ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany CHECK(tctx->thr); 2447ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany trace = &tctx->thr->trace; 2457ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } else if (tctx->status == ThreadStatusFinished 2467ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany || tctx->status == ThreadStatusDead) { 2479d2ffc2ee08216f8fad9b1bd267d1f112e0d2f01Dmitry Vyukov if (tctx->dead_info == 0) 2489d2ffc2ee08216f8fad9b1bd267d1f112e0d2f01Dmitry Vyukov return; 2499d2ffc2ee08216f8fad9b1bd267d1f112e0d2f01Dmitry Vyukov trace = &tctx->dead_info->trace; 2507ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } else { 2517ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany return; 2527ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } 2537ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany Lock l(&trace->mtx); 2547ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany const int partidx = (epoch / (kTraceSize / kTraceParts)) % kTraceParts; 2557ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany TraceHeader* hdr = &trace->headers[partidx]; 2567ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany if (epoch < hdr->epoch0) 2577ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany return; 2587ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany const u64 eend = epoch % kTraceSize; 2597ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany const u64 ebegin = eend / kTracePartSize * kTracePartSize; 260e954101f6602ac181a2c3accfbbad0ae51b0bf7cAlexey Samsonov DPrintf("#%d: RestoreStack epoch=%zu ebegin=%zu eend=%zu partidx=%d\n", 261e954101f6602ac181a2c3accfbbad0ae51b0bf7cAlexey Samsonov tid, (uptr)epoch, (uptr)ebegin, (uptr)eend, partidx); 26214c8bd7250742749e44e306c02a56cf47ad1db82Alexey Samsonov InternalScopedBuffer<uptr> stack(1024); // FIXME: de-hardcode 1024 2637ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany for (uptr i = 0; i < hdr->stack0.Size(); i++) { 2647ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany stack[i] = hdr->stack0.Get(i); 265e954101f6602ac181a2c3accfbbad0ae51b0bf7cAlexey Samsonov DPrintf2(" #%02lu: pc=%zx\n", i, stack[i]); 2667ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } 2677ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany uptr pos = hdr->stack0.Size(); 2687ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany for (uptr i = ebegin; i <= eend; i++) { 2697ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany Event ev = trace->events[i]; 2707ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany EventType typ = (EventType)(ev >> 61); 2717ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany uptr pc = (uptr)(ev & 0xffffffffffffull); 272e954101f6602ac181a2c3accfbbad0ae51b0bf7cAlexey Samsonov DPrintf2(" %zu typ=%d pc=%zx\n", i, typ, pc); 2737ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany if (typ == EventTypeMop) { 2747ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany stack[pos] = pc; 2757ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } else if (typ == EventTypeFuncEnter) { 2767ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany stack[pos++] = pc; 2777ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } else if (typ == EventTypeFuncExit) { 2787ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany // Since we have full stacks, this should never happen. 2797ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany DCHECK_GT(pos, 0); 2807ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany if (pos > 0) 2817ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany pos--; 2827ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } 2837ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany for (uptr j = 0; j <= pos; j++) 284e954101f6602ac181a2c3accfbbad0ae51b0bf7cAlexey Samsonov DPrintf2(" #%zu: %zx\n", j, stack[j]); 2857ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } 2867ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany if (pos == 0 && stack[0] == 0) 2877ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany return; 2887ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany pos++; 2897ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany stk->Init(stack, pos); 2907ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany} 2917ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 2927ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryanystatic bool HandleRacyStacks(ThreadState *thr, const StackTrace (&traces)[2], 2937ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany uptr addr_min, uptr addr_max) { 2947ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany Context *ctx = CTX(); 2957ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany bool equal_stack = false; 2967ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany RacyStacks hash = {}; 2977ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany if (flags()->suppress_equal_stacks) { 2987ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany hash.hash[0] = md5_hash(traces[0].Begin(), traces[0].Size() * sizeof(uptr)); 2997ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany hash.hash[1] = md5_hash(traces[1].Begin(), traces[1].Size() * sizeof(uptr)); 3007ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany for (uptr i = 0; i < ctx->racy_stacks.Size(); i++) { 3017ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany if (hash == ctx->racy_stacks[i]) { 3027ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany DPrintf("ThreadSanitizer: suppressing report as doubled (stack)\n"); 3037ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany equal_stack = true; 3047ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany break; 3057ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } 3067ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } 3077ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } 3087ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany bool equal_address = false; 3097ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany RacyAddress ra0 = {addr_min, addr_max}; 3107ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany if (flags()->suppress_equal_addresses) { 3117ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany for (uptr i = 0; i < ctx->racy_addresses.Size(); i++) { 3127ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany RacyAddress ra2 = ctx->racy_addresses[i]; 3137ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany uptr maxbeg = max(ra0.addr_min, ra2.addr_min); 3147ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany uptr minend = min(ra0.addr_max, ra2.addr_max); 3157ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany if (maxbeg < minend) { 3167ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany DPrintf("ThreadSanitizer: suppressing report as doubled (addr)\n"); 3177ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany equal_address = true; 3187ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany break; 3197ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } 3207ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } 3217ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } 3227ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany if (equal_stack || equal_address) { 3237ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany if (!equal_stack) 3247ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany ctx->racy_stacks.PushBack(hash); 3257ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany if (!equal_address) 3267ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany ctx->racy_addresses.PushBack(ra0); 3277ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany return true; 3287ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } 3297ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany return false; 3307ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany} 3317ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 3327ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryanystatic void AddRacyStacks(ThreadState *thr, const StackTrace (&traces)[2], 3337ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany uptr addr_min, uptr addr_max) { 3347ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany Context *ctx = CTX(); 3357ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany if (flags()->suppress_equal_stacks) { 3367ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany RacyStacks hash; 3377ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany hash.hash[0] = md5_hash(traces[0].Begin(), traces[0].Size() * sizeof(uptr)); 3387ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany hash.hash[1] = md5_hash(traces[1].Begin(), traces[1].Size() * sizeof(uptr)); 3397ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany ctx->racy_stacks.PushBack(hash); 3407ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } 3417ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany if (flags()->suppress_equal_addresses) { 3427ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany RacyAddress ra0 = {addr_min, addr_max}; 3437ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany ctx->racy_addresses.PushBack(ra0); 3447ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } 3457ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany} 3467ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 347f5820e74ad31eb8352049c880f8d58e286a9b713Dmitry Vyukovbool OutputReport(const ScopedReport &srep, const ReportStack *suppress_stack) { 3487ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany const ReportDesc *rep = srep.GetReport(); 3497ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany bool suppressed = IsSuppressed(rep->typ, suppress_stack); 3507ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany suppressed = OnReport(rep, suppressed); 3517ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany if (suppressed) 3527ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany return false; 3537ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany PrintReport(rep); 3547ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany CTX()->nreported++; 3557ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany return true; 3567ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany} 3577ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 3587ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryanyvoid ReportRace(ThreadState *thr) { 3597ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany ScopedInRtl in_rtl; 360069ce828e3057819ee34426496ea7080f7cc52f0Dmitry Vyukov 361069ce828e3057819ee34426496ea7080f7cc52f0Dmitry Vyukov bool freed = false; 362069ce828e3057819ee34426496ea7080f7cc52f0Dmitry Vyukov { 363069ce828e3057819ee34426496ea7080f7cc52f0Dmitry Vyukov Shadow s(thr->racy_state[1]); 364069ce828e3057819ee34426496ea7080f7cc52f0Dmitry Vyukov freed = s.GetFreedAndReset(); 365069ce828e3057819ee34426496ea7080f7cc52f0Dmitry Vyukov thr->racy_state[1] = s.raw(); 366069ce828e3057819ee34426496ea7080f7cc52f0Dmitry Vyukov } 367069ce828e3057819ee34426496ea7080f7cc52f0Dmitry Vyukov 3687ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany uptr addr = ShadowToMem((uptr)thr->racy_shadow_addr); 3697ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany uptr addr_min = 0; 3707ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany uptr addr_max = 0; 3717ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany { 3727ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany uptr a0 = addr + Shadow(thr->racy_state[0]).addr0(); 3737ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany uptr a1 = addr + Shadow(thr->racy_state[1]).addr0(); 3747ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany uptr e0 = a0 + Shadow(thr->racy_state[0]).size(); 3757ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany uptr e1 = a1 + Shadow(thr->racy_state[1]).size(); 3767ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany addr_min = min(a0, a1); 3777ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany addr_max = max(e0, e1); 3787ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany if (IsExpectedReport(addr_min, addr_max - addr_min)) 3797ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany return; 3807ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } 3817ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 3827ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany Context *ctx = CTX(); 3837ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany Lock l0(&ctx->thread_mtx); 3847ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 385069ce828e3057819ee34426496ea7080f7cc52f0Dmitry Vyukov ScopedReport rep(freed ? ReportTypeUseAfterFree : ReportTypeRace); 386069ce828e3057819ee34426496ea7080f7cc52f0Dmitry Vyukov const uptr kMop = 2; 387069ce828e3057819ee34426496ea7080f7cc52f0Dmitry Vyukov StackTrace traces[kMop]; 388069ce828e3057819ee34426496ea7080f7cc52f0Dmitry Vyukov for (uptr i = 0; i < kMop; i++) { 3897ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany Shadow s(thr->racy_state[i]); 3907ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany RestoreStack(s.tid(), s.epoch(), &traces[i]); 3917ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } 3927ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 3937ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany if (HandleRacyStacks(thr, traces, addr_min, addr_max)) 3947ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany return; 3957ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 396069ce828e3057819ee34426496ea7080f7cc52f0Dmitry Vyukov for (uptr i = 0; i < kMop; i++) { 3977ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany Shadow s(thr->racy_state[i]); 3987ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany rep.AddMemoryAccess(addr, s, &traces[i]); 3997ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } 4007ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 4017ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany // Ensure that we have at least something for the current thread. 402ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov DCHECK_EQ(traces[0].IsEmpty(), false); 4037ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 404069ce828e3057819ee34426496ea7080f7cc52f0Dmitry Vyukov for (uptr i = 0; i < kMop; i++) { 4057ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany FastState s(thr->racy_state[i]); 4067ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany ThreadContext *tctx = ctx->threads[s.tid()]; 4077ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany if (s.epoch() < tctx->epoch0 || s.epoch() > tctx->epoch1) 4087ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany continue; 4097ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany rep.AddThread(tctx); 4107ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany } 4117ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 412ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov rep.AddLocation(addr_min, addr_max - addr_min); 413ff35f1d82b4f145b3477ef27a7a2e7b63c486988Dmitry Vyukov 4147ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany if (!OutputReport(rep, rep.GetReport()->mops[0]->stack)) 4157ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany return; 4167ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 4177ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany AddRacyStacks(thr, traces, addr_min, addr_max); 4187ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany} 4197ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany 4207ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany} // namespace __tsan 421