1*68d75effSDimitry Andric //===-- tsan_debugging.cpp ------------------------------------------------===// 2*68d75effSDimitry Andric // 3*68d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*68d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*68d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*68d75effSDimitry Andric // 7*68d75effSDimitry Andric //===----------------------------------------------------------------------===// 8*68d75effSDimitry Andric // 9*68d75effSDimitry Andric // This file is a part of ThreadSanitizer (TSan), a race detector. 10*68d75effSDimitry Andric // 11*68d75effSDimitry Andric // TSan debugging API implementation. 12*68d75effSDimitry Andric //===----------------------------------------------------------------------===// 13*68d75effSDimitry Andric #include "tsan_interface.h" 14*68d75effSDimitry Andric #include "tsan_report.h" 15*68d75effSDimitry Andric #include "tsan_rtl.h" 16*68d75effSDimitry Andric 17*68d75effSDimitry Andric #include "sanitizer_common/sanitizer_stackdepot.h" 18*68d75effSDimitry Andric 19*68d75effSDimitry Andric using namespace __tsan; 20*68d75effSDimitry Andric 21*68d75effSDimitry Andric static const char *ReportTypeDescription(ReportType typ) { 22*68d75effSDimitry Andric switch (typ) { 23*68d75effSDimitry Andric case ReportTypeRace: return "data-race"; 24*68d75effSDimitry Andric case ReportTypeVptrRace: return "data-race-vptr"; 25*68d75effSDimitry Andric case ReportTypeUseAfterFree: return "heap-use-after-free"; 26*68d75effSDimitry Andric case ReportTypeVptrUseAfterFree: return "heap-use-after-free-vptr"; 27*68d75effSDimitry Andric case ReportTypeExternalRace: return "external-race"; 28*68d75effSDimitry Andric case ReportTypeThreadLeak: return "thread-leak"; 29*68d75effSDimitry Andric case ReportTypeMutexDestroyLocked: return "locked-mutex-destroy"; 30*68d75effSDimitry Andric case ReportTypeMutexDoubleLock: return "mutex-double-lock"; 31*68d75effSDimitry Andric case ReportTypeMutexInvalidAccess: return "mutex-invalid-access"; 32*68d75effSDimitry Andric case ReportTypeMutexBadUnlock: return "mutex-bad-unlock"; 33*68d75effSDimitry Andric case ReportTypeMutexBadReadLock: return "mutex-bad-read-lock"; 34*68d75effSDimitry Andric case ReportTypeMutexBadReadUnlock: return "mutex-bad-read-unlock"; 35*68d75effSDimitry Andric case ReportTypeSignalUnsafe: return "signal-unsafe-call"; 36*68d75effSDimitry Andric case ReportTypeErrnoInSignal: return "errno-in-signal-handler"; 37*68d75effSDimitry Andric case ReportTypeDeadlock: return "lock-order-inversion"; 38*68d75effSDimitry Andric // No default case so compiler warns us if we miss one 39*68d75effSDimitry Andric } 40*68d75effSDimitry Andric UNREACHABLE("missing case"); 41*68d75effSDimitry Andric } 42*68d75effSDimitry Andric 43*68d75effSDimitry Andric static const char *ReportLocationTypeDescription(ReportLocationType typ) { 44*68d75effSDimitry Andric switch (typ) { 45*68d75effSDimitry Andric case ReportLocationGlobal: return "global"; 46*68d75effSDimitry Andric case ReportLocationHeap: return "heap"; 47*68d75effSDimitry Andric case ReportLocationStack: return "stack"; 48*68d75effSDimitry Andric case ReportLocationTLS: return "tls"; 49*68d75effSDimitry Andric case ReportLocationFD: return "fd"; 50*68d75effSDimitry Andric // No default case so compiler warns us if we miss one 51*68d75effSDimitry Andric } 52*68d75effSDimitry Andric UNREACHABLE("missing case"); 53*68d75effSDimitry Andric } 54*68d75effSDimitry Andric 55*68d75effSDimitry Andric static void CopyTrace(SymbolizedStack *first_frame, void **trace, 56*68d75effSDimitry Andric uptr trace_size) { 57*68d75effSDimitry Andric uptr i = 0; 58*68d75effSDimitry Andric for (SymbolizedStack *frame = first_frame; frame != nullptr; 59*68d75effSDimitry Andric frame = frame->next) { 60*68d75effSDimitry Andric trace[i++] = (void *)frame->info.address; 61*68d75effSDimitry Andric if (i >= trace_size) break; 62*68d75effSDimitry Andric } 63*68d75effSDimitry Andric } 64*68d75effSDimitry Andric 65*68d75effSDimitry Andric // Meant to be called by the debugger. 66*68d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE 67*68d75effSDimitry Andric void *__tsan_get_current_report() { 68*68d75effSDimitry Andric return const_cast<ReportDesc*>(cur_thread()->current_report); 69*68d75effSDimitry Andric } 70*68d75effSDimitry Andric 71*68d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE 72*68d75effSDimitry Andric int __tsan_get_report_data(void *report, const char **description, int *count, 73*68d75effSDimitry Andric int *stack_count, int *mop_count, int *loc_count, 74*68d75effSDimitry Andric int *mutex_count, int *thread_count, 75*68d75effSDimitry Andric int *unique_tid_count, void **sleep_trace, 76*68d75effSDimitry Andric uptr trace_size) { 77*68d75effSDimitry Andric const ReportDesc *rep = (ReportDesc *)report; 78*68d75effSDimitry Andric *description = ReportTypeDescription(rep->typ); 79*68d75effSDimitry Andric *count = rep->count; 80*68d75effSDimitry Andric *stack_count = rep->stacks.Size(); 81*68d75effSDimitry Andric *mop_count = rep->mops.Size(); 82*68d75effSDimitry Andric *loc_count = rep->locs.Size(); 83*68d75effSDimitry Andric *mutex_count = rep->mutexes.Size(); 84*68d75effSDimitry Andric *thread_count = rep->threads.Size(); 85*68d75effSDimitry Andric *unique_tid_count = rep->unique_tids.Size(); 86*68d75effSDimitry Andric if (rep->sleep) CopyTrace(rep->sleep->frames, sleep_trace, trace_size); 87*68d75effSDimitry Andric return 1; 88*68d75effSDimitry Andric } 89*68d75effSDimitry Andric 90*68d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE 91*68d75effSDimitry Andric int __tsan_get_report_tag(void *report, uptr *tag) { 92*68d75effSDimitry Andric const ReportDesc *rep = (ReportDesc *)report; 93*68d75effSDimitry Andric *tag = rep->tag; 94*68d75effSDimitry Andric return 1; 95*68d75effSDimitry Andric } 96*68d75effSDimitry Andric 97*68d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE 98*68d75effSDimitry Andric int __tsan_get_report_stack(void *report, uptr idx, void **trace, 99*68d75effSDimitry Andric uptr trace_size) { 100*68d75effSDimitry Andric const ReportDesc *rep = (ReportDesc *)report; 101*68d75effSDimitry Andric CHECK_LT(idx, rep->stacks.Size()); 102*68d75effSDimitry Andric ReportStack *stack = rep->stacks[idx]; 103*68d75effSDimitry Andric if (stack) CopyTrace(stack->frames, trace, trace_size); 104*68d75effSDimitry Andric return stack ? 1 : 0; 105*68d75effSDimitry Andric } 106*68d75effSDimitry Andric 107*68d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE 108*68d75effSDimitry Andric int __tsan_get_report_mop(void *report, uptr idx, int *tid, void **addr, 109*68d75effSDimitry Andric int *size, int *write, int *atomic, void **trace, 110*68d75effSDimitry Andric uptr trace_size) { 111*68d75effSDimitry Andric const ReportDesc *rep = (ReportDesc *)report; 112*68d75effSDimitry Andric CHECK_LT(idx, rep->mops.Size()); 113*68d75effSDimitry Andric ReportMop *mop = rep->mops[idx]; 114*68d75effSDimitry Andric *tid = mop->tid; 115*68d75effSDimitry Andric *addr = (void *)mop->addr; 116*68d75effSDimitry Andric *size = mop->size; 117*68d75effSDimitry Andric *write = mop->write ? 1 : 0; 118*68d75effSDimitry Andric *atomic = mop->atomic ? 1 : 0; 119*68d75effSDimitry Andric if (mop->stack) CopyTrace(mop->stack->frames, trace, trace_size); 120*68d75effSDimitry Andric return 1; 121*68d75effSDimitry Andric } 122*68d75effSDimitry Andric 123*68d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE 124*68d75effSDimitry Andric int __tsan_get_report_loc(void *report, uptr idx, const char **type, 125*68d75effSDimitry Andric void **addr, uptr *start, uptr *size, int *tid, 126*68d75effSDimitry Andric int *fd, int *suppressable, void **trace, 127*68d75effSDimitry Andric uptr trace_size) { 128*68d75effSDimitry Andric const ReportDesc *rep = (ReportDesc *)report; 129*68d75effSDimitry Andric CHECK_LT(idx, rep->locs.Size()); 130*68d75effSDimitry Andric ReportLocation *loc = rep->locs[idx]; 131*68d75effSDimitry Andric *type = ReportLocationTypeDescription(loc->type); 132*68d75effSDimitry Andric *addr = (void *)loc->global.start; 133*68d75effSDimitry Andric *start = loc->heap_chunk_start; 134*68d75effSDimitry Andric *size = loc->heap_chunk_size; 135*68d75effSDimitry Andric *tid = loc->tid; 136*68d75effSDimitry Andric *fd = loc->fd; 137*68d75effSDimitry Andric *suppressable = loc->suppressable; 138*68d75effSDimitry Andric if (loc->stack) CopyTrace(loc->stack->frames, trace, trace_size); 139*68d75effSDimitry Andric return 1; 140*68d75effSDimitry Andric } 141*68d75effSDimitry Andric 142*68d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE 143*68d75effSDimitry Andric int __tsan_get_report_loc_object_type(void *report, uptr idx, 144*68d75effSDimitry Andric const char **object_type) { 145*68d75effSDimitry Andric const ReportDesc *rep = (ReportDesc *)report; 146*68d75effSDimitry Andric CHECK_LT(idx, rep->locs.Size()); 147*68d75effSDimitry Andric ReportLocation *loc = rep->locs[idx]; 148*68d75effSDimitry Andric *object_type = GetObjectTypeFromTag(loc->external_tag); 149*68d75effSDimitry Andric return 1; 150*68d75effSDimitry Andric } 151*68d75effSDimitry Andric 152*68d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE 153*68d75effSDimitry Andric int __tsan_get_report_mutex(void *report, uptr idx, uptr *mutex_id, void **addr, 154*68d75effSDimitry Andric int *destroyed, void **trace, uptr trace_size) { 155*68d75effSDimitry Andric const ReportDesc *rep = (ReportDesc *)report; 156*68d75effSDimitry Andric CHECK_LT(idx, rep->mutexes.Size()); 157*68d75effSDimitry Andric ReportMutex *mutex = rep->mutexes[idx]; 158*68d75effSDimitry Andric *mutex_id = mutex->id; 159*68d75effSDimitry Andric *addr = (void *)mutex->addr; 160*68d75effSDimitry Andric *destroyed = mutex->destroyed; 161*68d75effSDimitry Andric if (mutex->stack) CopyTrace(mutex->stack->frames, trace, trace_size); 162*68d75effSDimitry Andric return 1; 163*68d75effSDimitry Andric } 164*68d75effSDimitry Andric 165*68d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE 166*68d75effSDimitry Andric int __tsan_get_report_thread(void *report, uptr idx, int *tid, tid_t *os_id, 167*68d75effSDimitry Andric int *running, const char **name, int *parent_tid, 168*68d75effSDimitry Andric void **trace, uptr trace_size) { 169*68d75effSDimitry Andric const ReportDesc *rep = (ReportDesc *)report; 170*68d75effSDimitry Andric CHECK_LT(idx, rep->threads.Size()); 171*68d75effSDimitry Andric ReportThread *thread = rep->threads[idx]; 172*68d75effSDimitry Andric *tid = thread->id; 173*68d75effSDimitry Andric *os_id = thread->os_id; 174*68d75effSDimitry Andric *running = thread->running; 175*68d75effSDimitry Andric *name = thread->name; 176*68d75effSDimitry Andric *parent_tid = thread->parent_tid; 177*68d75effSDimitry Andric if (thread->stack) CopyTrace(thread->stack->frames, trace, trace_size); 178*68d75effSDimitry Andric return 1; 179*68d75effSDimitry Andric } 180*68d75effSDimitry Andric 181*68d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE 182*68d75effSDimitry Andric int __tsan_get_report_unique_tid(void *report, uptr idx, int *tid) { 183*68d75effSDimitry Andric const ReportDesc *rep = (ReportDesc *)report; 184*68d75effSDimitry Andric CHECK_LT(idx, rep->unique_tids.Size()); 185*68d75effSDimitry Andric *tid = rep->unique_tids[idx]; 186*68d75effSDimitry Andric return 1; 187*68d75effSDimitry Andric } 188*68d75effSDimitry Andric 189*68d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE 190*68d75effSDimitry Andric const char *__tsan_locate_address(uptr addr, char *name, uptr name_size, 191*68d75effSDimitry Andric uptr *region_address_ptr, 192*68d75effSDimitry Andric uptr *region_size_ptr) { 193*68d75effSDimitry Andric uptr region_address = 0; 194*68d75effSDimitry Andric uptr region_size = 0; 195*68d75effSDimitry Andric const char *region_kind = nullptr; 196*68d75effSDimitry Andric if (name && name_size > 0) name[0] = 0; 197*68d75effSDimitry Andric 198*68d75effSDimitry Andric if (IsMetaMem(addr)) { 199*68d75effSDimitry Andric region_kind = "meta shadow"; 200*68d75effSDimitry Andric } else if (IsShadowMem(addr)) { 201*68d75effSDimitry Andric region_kind = "shadow"; 202*68d75effSDimitry Andric } else { 203*68d75effSDimitry Andric bool is_stack = false; 204*68d75effSDimitry Andric MBlock *b = 0; 205*68d75effSDimitry Andric Allocator *a = allocator(); 206*68d75effSDimitry Andric if (a->PointerIsMine((void *)addr)) { 207*68d75effSDimitry Andric void *block_begin = a->GetBlockBegin((void *)addr); 208*68d75effSDimitry Andric if (block_begin) b = ctx->metamap.GetBlock((uptr)block_begin); 209*68d75effSDimitry Andric } 210*68d75effSDimitry Andric 211*68d75effSDimitry Andric if (b != 0) { 212*68d75effSDimitry Andric region_address = (uptr)allocator()->GetBlockBegin((void *)addr); 213*68d75effSDimitry Andric region_size = b->siz; 214*68d75effSDimitry Andric region_kind = "heap"; 215*68d75effSDimitry Andric } else { 216*68d75effSDimitry Andric // TODO(kuba.brecka): We should not lock. This is supposed to be called 217*68d75effSDimitry Andric // from within the debugger when other threads are stopped. 218*68d75effSDimitry Andric ctx->thread_registry->Lock(); 219*68d75effSDimitry Andric ThreadContext *tctx = IsThreadStackOrTls(addr, &is_stack); 220*68d75effSDimitry Andric ctx->thread_registry->Unlock(); 221*68d75effSDimitry Andric if (tctx) { 222*68d75effSDimitry Andric region_kind = is_stack ? "stack" : "tls"; 223*68d75effSDimitry Andric } else { 224*68d75effSDimitry Andric region_kind = "global"; 225*68d75effSDimitry Andric DataInfo info; 226*68d75effSDimitry Andric if (Symbolizer::GetOrInit()->SymbolizeData(addr, &info)) { 227*68d75effSDimitry Andric internal_strncpy(name, info.name, name_size); 228*68d75effSDimitry Andric region_address = info.start; 229*68d75effSDimitry Andric region_size = info.size; 230*68d75effSDimitry Andric } 231*68d75effSDimitry Andric } 232*68d75effSDimitry Andric } 233*68d75effSDimitry Andric } 234*68d75effSDimitry Andric 235*68d75effSDimitry Andric CHECK(region_kind); 236*68d75effSDimitry Andric if (region_address_ptr) *region_address_ptr = region_address; 237*68d75effSDimitry Andric if (region_size_ptr) *region_size_ptr = region_size; 238*68d75effSDimitry Andric return region_kind; 239*68d75effSDimitry Andric } 240*68d75effSDimitry Andric 241*68d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE 242*68d75effSDimitry Andric int __tsan_get_alloc_stack(uptr addr, uptr *trace, uptr size, int *thread_id, 243*68d75effSDimitry Andric tid_t *os_id) { 244*68d75effSDimitry Andric MBlock *b = 0; 245*68d75effSDimitry Andric Allocator *a = allocator(); 246*68d75effSDimitry Andric if (a->PointerIsMine((void *)addr)) { 247*68d75effSDimitry Andric void *block_begin = a->GetBlockBegin((void *)addr); 248*68d75effSDimitry Andric if (block_begin) b = ctx->metamap.GetBlock((uptr)block_begin); 249*68d75effSDimitry Andric } 250*68d75effSDimitry Andric if (b == 0) return 0; 251*68d75effSDimitry Andric 252*68d75effSDimitry Andric *thread_id = b->tid; 253*68d75effSDimitry Andric // No locking. This is supposed to be called from within the debugger when 254*68d75effSDimitry Andric // other threads are stopped. 255*68d75effSDimitry Andric ThreadContextBase *tctx = ctx->thread_registry->GetThreadLocked(b->tid); 256*68d75effSDimitry Andric *os_id = tctx->os_id; 257*68d75effSDimitry Andric 258*68d75effSDimitry Andric StackTrace stack = StackDepotGet(b->stk); 259*68d75effSDimitry Andric size = Min(size, (uptr)stack.size); 260*68d75effSDimitry Andric for (uptr i = 0; i < size; i++) trace[i] = stack.trace[stack.size - i - 1]; 261*68d75effSDimitry Andric return size; 262*68d75effSDimitry Andric } 263