168d75effSDimitry Andric //===-- tsan_debugging.cpp ------------------------------------------------===//
268d75effSDimitry Andric //
368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
668d75effSDimitry Andric //
768d75effSDimitry Andric //===----------------------------------------------------------------------===//
868d75effSDimitry Andric //
968d75effSDimitry Andric // This file is a part of ThreadSanitizer (TSan), a race detector.
1068d75effSDimitry Andric //
1168d75effSDimitry Andric // TSan debugging API implementation.
1268d75effSDimitry Andric //===----------------------------------------------------------------------===//
1368d75effSDimitry Andric #include "tsan_interface.h"
1468d75effSDimitry Andric #include "tsan_report.h"
1568d75effSDimitry Andric #include "tsan_rtl.h"
1668d75effSDimitry Andric
1768d75effSDimitry Andric #include "sanitizer_common/sanitizer_stackdepot.h"
1868d75effSDimitry Andric
1968d75effSDimitry Andric using namespace __tsan;
2068d75effSDimitry Andric
ReportTypeDescription(ReportType typ)2168d75effSDimitry Andric static const char *ReportTypeDescription(ReportType typ) {
2268d75effSDimitry Andric switch (typ) {
2368d75effSDimitry Andric case ReportTypeRace: return "data-race";
2468d75effSDimitry Andric case ReportTypeVptrRace: return "data-race-vptr";
2568d75effSDimitry Andric case ReportTypeUseAfterFree: return "heap-use-after-free";
2668d75effSDimitry Andric case ReportTypeVptrUseAfterFree: return "heap-use-after-free-vptr";
2768d75effSDimitry Andric case ReportTypeExternalRace: return "external-race";
2868d75effSDimitry Andric case ReportTypeThreadLeak: return "thread-leak";
2968d75effSDimitry Andric case ReportTypeMutexDestroyLocked: return "locked-mutex-destroy";
3068d75effSDimitry Andric case ReportTypeMutexDoubleLock: return "mutex-double-lock";
3168d75effSDimitry Andric case ReportTypeMutexInvalidAccess: return "mutex-invalid-access";
3268d75effSDimitry Andric case ReportTypeMutexBadUnlock: return "mutex-bad-unlock";
3368d75effSDimitry Andric case ReportTypeMutexBadReadLock: return "mutex-bad-read-lock";
3468d75effSDimitry Andric case ReportTypeMutexBadReadUnlock: return "mutex-bad-read-unlock";
3568d75effSDimitry Andric case ReportTypeSignalUnsafe: return "signal-unsafe-call";
3668d75effSDimitry Andric case ReportTypeErrnoInSignal: return "errno-in-signal-handler";
3768d75effSDimitry Andric case ReportTypeDeadlock: return "lock-order-inversion";
38*5f757f3fSDimitry Andric case ReportTypeMutexHeldWrongContext:
39*5f757f3fSDimitry Andric return "mutex-held-in-wrong-context";
4068d75effSDimitry Andric // No default case so compiler warns us if we miss one
4168d75effSDimitry Andric }
4268d75effSDimitry Andric UNREACHABLE("missing case");
4368d75effSDimitry Andric }
4468d75effSDimitry Andric
ReportLocationTypeDescription(ReportLocationType typ)4568d75effSDimitry Andric static const char *ReportLocationTypeDescription(ReportLocationType typ) {
4668d75effSDimitry Andric switch (typ) {
4768d75effSDimitry Andric case ReportLocationGlobal: return "global";
4868d75effSDimitry Andric case ReportLocationHeap: return "heap";
4968d75effSDimitry Andric case ReportLocationStack: return "stack";
5068d75effSDimitry Andric case ReportLocationTLS: return "tls";
5168d75effSDimitry Andric case ReportLocationFD: return "fd";
5268d75effSDimitry Andric // No default case so compiler warns us if we miss one
5368d75effSDimitry Andric }
5468d75effSDimitry Andric UNREACHABLE("missing case");
5568d75effSDimitry Andric }
5668d75effSDimitry Andric
CopyTrace(SymbolizedStack * first_frame,void ** trace,uptr trace_size)5768d75effSDimitry Andric static void CopyTrace(SymbolizedStack *first_frame, void **trace,
5868d75effSDimitry Andric uptr trace_size) {
5968d75effSDimitry Andric uptr i = 0;
6068d75effSDimitry Andric for (SymbolizedStack *frame = first_frame; frame != nullptr;
6168d75effSDimitry Andric frame = frame->next) {
6268d75effSDimitry Andric trace[i++] = (void *)frame->info.address;
6368d75effSDimitry Andric if (i >= trace_size) break;
6468d75effSDimitry Andric }
6568d75effSDimitry Andric }
6668d75effSDimitry Andric
6768d75effSDimitry Andric // Meant to be called by the debugger.
6868d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE
__tsan_get_current_report()6968d75effSDimitry Andric void *__tsan_get_current_report() {
7068d75effSDimitry Andric return const_cast<ReportDesc*>(cur_thread()->current_report);
7168d75effSDimitry Andric }
7268d75effSDimitry Andric
7368d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE
__tsan_get_report_data(void * report,const char ** description,int * count,int * stack_count,int * mop_count,int * loc_count,int * mutex_count,int * thread_count,int * unique_tid_count,void ** sleep_trace,uptr trace_size)7468d75effSDimitry Andric int __tsan_get_report_data(void *report, const char **description, int *count,
7568d75effSDimitry Andric int *stack_count, int *mop_count, int *loc_count,
7668d75effSDimitry Andric int *mutex_count, int *thread_count,
7768d75effSDimitry Andric int *unique_tid_count, void **sleep_trace,
7868d75effSDimitry Andric uptr trace_size) {
7968d75effSDimitry Andric const ReportDesc *rep = (ReportDesc *)report;
8068d75effSDimitry Andric *description = ReportTypeDescription(rep->typ);
8168d75effSDimitry Andric *count = rep->count;
8268d75effSDimitry Andric *stack_count = rep->stacks.Size();
8368d75effSDimitry Andric *mop_count = rep->mops.Size();
8468d75effSDimitry Andric *loc_count = rep->locs.Size();
8568d75effSDimitry Andric *mutex_count = rep->mutexes.Size();
8668d75effSDimitry Andric *thread_count = rep->threads.Size();
8768d75effSDimitry Andric *unique_tid_count = rep->unique_tids.Size();
8868d75effSDimitry Andric if (rep->sleep) CopyTrace(rep->sleep->frames, sleep_trace, trace_size);
8968d75effSDimitry Andric return 1;
9068d75effSDimitry Andric }
9168d75effSDimitry Andric
9268d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE
__tsan_get_report_tag(void * report,uptr * tag)9368d75effSDimitry Andric int __tsan_get_report_tag(void *report, uptr *tag) {
9468d75effSDimitry Andric const ReportDesc *rep = (ReportDesc *)report;
9568d75effSDimitry Andric *tag = rep->tag;
9668d75effSDimitry Andric return 1;
9768d75effSDimitry Andric }
9868d75effSDimitry Andric
9968d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE
__tsan_get_report_stack(void * report,uptr idx,void ** trace,uptr trace_size)10068d75effSDimitry Andric int __tsan_get_report_stack(void *report, uptr idx, void **trace,
10168d75effSDimitry Andric uptr trace_size) {
10268d75effSDimitry Andric const ReportDesc *rep = (ReportDesc *)report;
10368d75effSDimitry Andric CHECK_LT(idx, rep->stacks.Size());
10468d75effSDimitry Andric ReportStack *stack = rep->stacks[idx];
10568d75effSDimitry Andric if (stack) CopyTrace(stack->frames, trace, trace_size);
10668d75effSDimitry Andric return stack ? 1 : 0;
10768d75effSDimitry Andric }
10868d75effSDimitry Andric
10968d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE
__tsan_get_report_mop(void * report,uptr idx,int * tid,void ** addr,int * size,int * write,int * atomic,void ** trace,uptr trace_size)11068d75effSDimitry Andric int __tsan_get_report_mop(void *report, uptr idx, int *tid, void **addr,
11168d75effSDimitry Andric int *size, int *write, int *atomic, void **trace,
11268d75effSDimitry Andric uptr trace_size) {
11368d75effSDimitry Andric const ReportDesc *rep = (ReportDesc *)report;
11468d75effSDimitry Andric CHECK_LT(idx, rep->mops.Size());
11568d75effSDimitry Andric ReportMop *mop = rep->mops[idx];
11668d75effSDimitry Andric *tid = mop->tid;
11768d75effSDimitry Andric *addr = (void *)mop->addr;
11868d75effSDimitry Andric *size = mop->size;
11968d75effSDimitry Andric *write = mop->write ? 1 : 0;
12068d75effSDimitry Andric *atomic = mop->atomic ? 1 : 0;
12168d75effSDimitry Andric if (mop->stack) CopyTrace(mop->stack->frames, trace, trace_size);
12268d75effSDimitry Andric return 1;
12368d75effSDimitry Andric }
12468d75effSDimitry Andric
12568d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE
__tsan_get_report_loc(void * report,uptr idx,const char ** type,void ** addr,uptr * start,uptr * size,int * tid,int * fd,int * suppressable,void ** trace,uptr trace_size)12668d75effSDimitry Andric int __tsan_get_report_loc(void *report, uptr idx, const char **type,
12768d75effSDimitry Andric void **addr, uptr *start, uptr *size, int *tid,
12868d75effSDimitry Andric int *fd, int *suppressable, void **trace,
12968d75effSDimitry Andric uptr trace_size) {
13068d75effSDimitry Andric const ReportDesc *rep = (ReportDesc *)report;
13168d75effSDimitry Andric CHECK_LT(idx, rep->locs.Size());
13268d75effSDimitry Andric ReportLocation *loc = rep->locs[idx];
13368d75effSDimitry Andric *type = ReportLocationTypeDescription(loc->type);
13468d75effSDimitry Andric *addr = (void *)loc->global.start;
13568d75effSDimitry Andric *start = loc->heap_chunk_start;
13668d75effSDimitry Andric *size = loc->heap_chunk_size;
13768d75effSDimitry Andric *tid = loc->tid;
13868d75effSDimitry Andric *fd = loc->fd;
13968d75effSDimitry Andric *suppressable = loc->suppressable;
14068d75effSDimitry Andric if (loc->stack) CopyTrace(loc->stack->frames, trace, trace_size);
14168d75effSDimitry Andric return 1;
14268d75effSDimitry Andric }
14368d75effSDimitry Andric
14468d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE
__tsan_get_report_loc_object_type(void * report,uptr idx,const char ** object_type)14568d75effSDimitry Andric int __tsan_get_report_loc_object_type(void *report, uptr idx,
14668d75effSDimitry Andric const char **object_type) {
14768d75effSDimitry Andric const ReportDesc *rep = (ReportDesc *)report;
14868d75effSDimitry Andric CHECK_LT(idx, rep->locs.Size());
14968d75effSDimitry Andric ReportLocation *loc = rep->locs[idx];
15068d75effSDimitry Andric *object_type = GetObjectTypeFromTag(loc->external_tag);
15168d75effSDimitry Andric return 1;
15268d75effSDimitry Andric }
15368d75effSDimitry Andric
15468d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE
__tsan_get_report_mutex(void * report,uptr idx,uptr * mutex_id,void ** addr,int * destroyed,void ** trace,uptr trace_size)15568d75effSDimitry Andric int __tsan_get_report_mutex(void *report, uptr idx, uptr *mutex_id, void **addr,
15668d75effSDimitry Andric int *destroyed, void **trace, uptr trace_size) {
15768d75effSDimitry Andric const ReportDesc *rep = (ReportDesc *)report;
15868d75effSDimitry Andric CHECK_LT(idx, rep->mutexes.Size());
15968d75effSDimitry Andric ReportMutex *mutex = rep->mutexes[idx];
16068d75effSDimitry Andric *mutex_id = mutex->id;
16168d75effSDimitry Andric *addr = (void *)mutex->addr;
1620eae32dcSDimitry Andric *destroyed = false;
16368d75effSDimitry Andric if (mutex->stack) CopyTrace(mutex->stack->frames, trace, trace_size);
16468d75effSDimitry Andric return 1;
16568d75effSDimitry Andric }
16668d75effSDimitry Andric
16768d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE
__tsan_get_report_thread(void * report,uptr idx,int * tid,tid_t * os_id,int * running,const char ** name,int * parent_tid,void ** trace,uptr trace_size)16868d75effSDimitry Andric int __tsan_get_report_thread(void *report, uptr idx, int *tid, tid_t *os_id,
16968d75effSDimitry Andric int *running, const char **name, int *parent_tid,
17068d75effSDimitry Andric void **trace, uptr trace_size) {
17168d75effSDimitry Andric const ReportDesc *rep = (ReportDesc *)report;
17268d75effSDimitry Andric CHECK_LT(idx, rep->threads.Size());
17368d75effSDimitry Andric ReportThread *thread = rep->threads[idx];
17468d75effSDimitry Andric *tid = thread->id;
17568d75effSDimitry Andric *os_id = thread->os_id;
17668d75effSDimitry Andric *running = thread->running;
17768d75effSDimitry Andric *name = thread->name;
17868d75effSDimitry Andric *parent_tid = thread->parent_tid;
17968d75effSDimitry Andric if (thread->stack) CopyTrace(thread->stack->frames, trace, trace_size);
18068d75effSDimitry Andric return 1;
18168d75effSDimitry Andric }
18268d75effSDimitry Andric
18368d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE
__tsan_get_report_unique_tid(void * report,uptr idx,int * tid)18468d75effSDimitry Andric int __tsan_get_report_unique_tid(void *report, uptr idx, int *tid) {
18568d75effSDimitry Andric const ReportDesc *rep = (ReportDesc *)report;
18668d75effSDimitry Andric CHECK_LT(idx, rep->unique_tids.Size());
18768d75effSDimitry Andric *tid = rep->unique_tids[idx];
18868d75effSDimitry Andric return 1;
18968d75effSDimitry Andric }
19068d75effSDimitry Andric
19168d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE
__tsan_locate_address(uptr addr,char * name,uptr name_size,uptr * region_address_ptr,uptr * region_size_ptr)19268d75effSDimitry Andric const char *__tsan_locate_address(uptr addr, char *name, uptr name_size,
19368d75effSDimitry Andric uptr *region_address_ptr,
19468d75effSDimitry Andric uptr *region_size_ptr) {
19568d75effSDimitry Andric uptr region_address = 0;
19668d75effSDimitry Andric uptr region_size = 0;
19768d75effSDimitry Andric const char *region_kind = nullptr;
19868d75effSDimitry Andric if (name && name_size > 0) name[0] = 0;
19968d75effSDimitry Andric
200349cc55cSDimitry Andric if (IsMetaMem(reinterpret_cast<u32 *>(addr))) {
20168d75effSDimitry Andric region_kind = "meta shadow";
202349cc55cSDimitry Andric } else if (IsShadowMem(reinterpret_cast<RawShadow *>(addr))) {
20368d75effSDimitry Andric region_kind = "shadow";
20468d75effSDimitry Andric } else {
20568d75effSDimitry Andric bool is_stack = false;
20668d75effSDimitry Andric MBlock *b = 0;
20768d75effSDimitry Andric Allocator *a = allocator();
20868d75effSDimitry Andric if (a->PointerIsMine((void *)addr)) {
20968d75effSDimitry Andric void *block_begin = a->GetBlockBegin((void *)addr);
21068d75effSDimitry Andric if (block_begin) b = ctx->metamap.GetBlock((uptr)block_begin);
21168d75effSDimitry Andric }
21268d75effSDimitry Andric
21368d75effSDimitry Andric if (b != 0) {
21468d75effSDimitry Andric region_address = (uptr)allocator()->GetBlockBegin((void *)addr);
21568d75effSDimitry Andric region_size = b->siz;
21668d75effSDimitry Andric region_kind = "heap";
21768d75effSDimitry Andric } else {
21868d75effSDimitry Andric // TODO(kuba.brecka): We should not lock. This is supposed to be called
21968d75effSDimitry Andric // from within the debugger when other threads are stopped.
220349cc55cSDimitry Andric ctx->thread_registry.Lock();
22168d75effSDimitry Andric ThreadContext *tctx = IsThreadStackOrTls(addr, &is_stack);
222349cc55cSDimitry Andric ctx->thread_registry.Unlock();
22368d75effSDimitry Andric if (tctx) {
22468d75effSDimitry Andric region_kind = is_stack ? "stack" : "tls";
22568d75effSDimitry Andric } else {
22668d75effSDimitry Andric region_kind = "global";
22768d75effSDimitry Andric DataInfo info;
22868d75effSDimitry Andric if (Symbolizer::GetOrInit()->SymbolizeData(addr, &info)) {
22968d75effSDimitry Andric internal_strncpy(name, info.name, name_size);
23068d75effSDimitry Andric region_address = info.start;
23168d75effSDimitry Andric region_size = info.size;
23268d75effSDimitry Andric }
23368d75effSDimitry Andric }
23468d75effSDimitry Andric }
23568d75effSDimitry Andric }
23668d75effSDimitry Andric
23768d75effSDimitry Andric CHECK(region_kind);
23868d75effSDimitry Andric if (region_address_ptr) *region_address_ptr = region_address;
23968d75effSDimitry Andric if (region_size_ptr) *region_size_ptr = region_size;
24068d75effSDimitry Andric return region_kind;
24168d75effSDimitry Andric }
24268d75effSDimitry Andric
24368d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE
__tsan_get_alloc_stack(uptr addr,uptr * trace,uptr size,int * thread_id,tid_t * os_id)24468d75effSDimitry Andric int __tsan_get_alloc_stack(uptr addr, uptr *trace, uptr size, int *thread_id,
24568d75effSDimitry Andric tid_t *os_id) {
24668d75effSDimitry Andric MBlock *b = 0;
24768d75effSDimitry Andric Allocator *a = allocator();
24868d75effSDimitry Andric if (a->PointerIsMine((void *)addr)) {
24968d75effSDimitry Andric void *block_begin = a->GetBlockBegin((void *)addr);
25068d75effSDimitry Andric if (block_begin) b = ctx->metamap.GetBlock((uptr)block_begin);
25168d75effSDimitry Andric }
25268d75effSDimitry Andric if (b == 0) return 0;
25368d75effSDimitry Andric
25468d75effSDimitry Andric *thread_id = b->tid;
25568d75effSDimitry Andric // No locking. This is supposed to be called from within the debugger when
25668d75effSDimitry Andric // other threads are stopped.
257349cc55cSDimitry Andric ThreadContextBase *tctx = ctx->thread_registry.GetThreadLocked(b->tid);
25868d75effSDimitry Andric *os_id = tctx->os_id;
25968d75effSDimitry Andric
26068d75effSDimitry Andric StackTrace stack = StackDepotGet(b->stk);
26168d75effSDimitry Andric size = Min(size, (uptr)stack.size);
26268d75effSDimitry Andric for (uptr i = 0; i < size; i++) trace[i] = stack.trace[stack.size - i - 1];
26368d75effSDimitry Andric return size;
26468d75effSDimitry Andric }
265