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-exception 6 // 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 19 using namespace __tsan; 20 21 static 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 one 41 } 42 UNREACHABLE("missing case"); 43 } 44 45 static 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 one 53 } 54 UNREACHABLE("missing case"); 55 } 56 57 static 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. 68 SANITIZER_INTERFACE_ATTRIBUTE 69 void *__tsan_get_current_report() { 70 return const_cast<ReportDesc*>(cur_thread()->current_report); 71 } 72 73 SANITIZER_INTERFACE_ATTRIBUTE 74 int __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 92 SANITIZER_INTERFACE_ATTRIBUTE 93 int __tsan_get_report_tag(void *report, uptr *tag) { 94 const ReportDesc *rep = (ReportDesc *)report; 95 *tag = rep->tag; 96 return 1; 97 } 98 99 SANITIZER_INTERFACE_ATTRIBUTE 100 int __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 109 SANITIZER_INTERFACE_ATTRIBUTE 110 int __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 125 SANITIZER_INTERFACE_ATTRIBUTE 126 int __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 144 SANITIZER_INTERFACE_ATTRIBUTE 145 int __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 154 SANITIZER_INTERFACE_ATTRIBUTE 155 int __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 167 SANITIZER_INTERFACE_ATTRIBUTE 168 int __tsan_get_report_thread(void *report, uptr idx, int *tid, tid_t *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 183 SANITIZER_INTERFACE_ATTRIBUTE 184 int __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 191 SANITIZER_INTERFACE_ATTRIBUTE 192 const 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 called 219 // 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 243 SANITIZER_INTERFACE_ATTRIBUTE 244 int __tsan_get_alloc_stack(uptr addr, uptr *trace, uptr size, int *thread_id, 245 tid_t *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 when 256 // 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