1 //===-- tsan_debugging.cpp ------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file is a part of ThreadSanitizer (TSan), a race detector.
11 // TSan debugging API implementation.
12 //===----------------------------------------------------------------------===//
13 #include "tsan_interface.h"
14 #include "tsan_report.h"
17 #include "sanitizer_common/sanitizer_stackdepot.h"
19 using namespace __tsan
;
21 static const char *ReportTypeDescription(ReportType 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
42 UNREACHABLE("missing case");
45 static const char *ReportLocationTypeDescription(ReportLocationType 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
54 UNREACHABLE("missing case");
57 static void CopyTrace(SymbolizedStack
*first_frame
, void **trace
,
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;
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
);
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
,
79 const ReportDesc
*rep
= (ReportDesc
*)report
;
80 *description
= ReportTypeDescription(rep
->typ
);
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
);
92 SANITIZER_INTERFACE_ATTRIBUTE
93 int __tsan_get_report_tag(void *report
, uptr
*tag
) {
94 const ReportDesc
*rep
= (ReportDesc
*)report
;
99 SANITIZER_INTERFACE_ATTRIBUTE
100 int __tsan_get_report_stack(void *report
, uptr idx
, void **trace
,
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;
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
,
113 const ReportDesc
*rep
= (ReportDesc
*)report
;
114 CHECK_LT(idx
, rep
->mops
.Size());
115 ReportMop
*mop
= rep
->mops
[idx
];
117 *addr
= (void *)mop
->addr
;
119 *write
= mop
->write
? 1 : 0;
120 *atomic
= mop
->atomic
? 1 : 0;
121 if (mop
->stack
) CopyTrace(mop
->stack
->frames
, trace
, trace_size
);
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
,
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
;
139 *suppressable
= loc
->suppressable
;
140 if (loc
->stack
) CopyTrace(loc
->stack
->frames
, trace
, trace_size
);
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
);
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
;
163 if (mutex
->stack
) CopyTrace(mutex
->stack
->frames
, trace
, trace_size
);
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
];
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
);
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
];
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;
200 if (IsMetaMem(reinterpret_cast<u32
*>(addr
))) {
201 region_kind
= "meta shadow";
202 } else if (IsShadowMem(reinterpret_cast<RawShadow
*>(addr
))) {
203 region_kind
= "shadow";
205 bool is_stack
= false;
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
);
214 region_address
= (uptr
)allocator()->GetBlockBegin((void *)addr
);
215 region_size
= b
->siz
;
216 region_kind
= "heap";
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();
224 region_kind
= is_stack
? "stack" : "tls";
226 region_kind
= "global";
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
;
238 if (region_address_ptr
) *region_address_ptr
= region_address
;
239 if (region_size_ptr
) *region_size_ptr
= region_size
;
243 SANITIZER_INTERFACE_ATTRIBUTE
244 int __tsan_get_alloc_stack(uptr addr
, uptr
*trace
, uptr size
, int *thread_id
,
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
);
252 if (b
== 0) return 0;
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
;
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];