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