1 //===-- tsan_debugging.cc -------------------------------------------------===//
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
6 //===----------------------------------------------------------------------===//
8 // This file is a part of ThreadSanitizer (TSan), a race detector.
10 // TSan debugging API implementation.
11 //===----------------------------------------------------------------------===//
12 #include "tsan_interface.h"
13 #include "tsan_report.h"
16 #include "sanitizer_common/sanitizer_stackdepot.h"
18 using namespace __tsan
;
20 static const char *ReportTypeDescription(ReportType typ
) {
21 if (typ
== ReportTypeRace
) return "data-race";
22 if (typ
== ReportTypeVptrRace
) return "data-race-vptr";
23 if (typ
== ReportTypeUseAfterFree
) return "heap-use-after-free";
24 if (typ
== ReportTypeVptrUseAfterFree
) return "heap-use-after-free-vptr";
25 if (typ
== ReportTypeExternalRace
) return "external-race";
26 if (typ
== ReportTypeThreadLeak
) return "thread-leak";
27 if (typ
== ReportTypeMutexDestroyLocked
) return "locked-mutex-destroy";
28 if (typ
== ReportTypeMutexDoubleLock
) return "mutex-double-lock";
29 if (typ
== ReportTypeMutexInvalidAccess
) return "mutex-invalid-access";
30 if (typ
== ReportTypeMutexBadUnlock
) return "mutex-bad-unlock";
31 if (typ
== ReportTypeMutexBadReadLock
) return "mutex-bad-read-lock";
32 if (typ
== ReportTypeMutexBadReadUnlock
) return "mutex-bad-read-unlock";
33 if (typ
== ReportTypeSignalUnsafe
) return "signal-unsafe-call";
34 if (typ
== ReportTypeErrnoInSignal
) return "errno-in-signal-handler";
35 if (typ
== ReportTypeDeadlock
) return "lock-order-inversion";
39 static const char *ReportLocationTypeDescription(ReportLocationType typ
) {
40 if (typ
== ReportLocationGlobal
) return "global";
41 if (typ
== ReportLocationHeap
) return "heap";
42 if (typ
== ReportLocationStack
) return "stack";
43 if (typ
== ReportLocationTLS
) return "tls";
44 if (typ
== ReportLocationFD
) return "fd";
48 static void CopyTrace(SymbolizedStack
*first_frame
, void **trace
,
51 for (SymbolizedStack
*frame
= first_frame
; frame
!= nullptr;
52 frame
= frame
->next
) {
53 trace
[i
++] = (void *)frame
->info
.address
;
54 if (i
>= trace_size
) break;
58 // Meant to be called by the debugger.
59 SANITIZER_INTERFACE_ATTRIBUTE
60 void *__tsan_get_current_report() {
61 return const_cast<ReportDesc
*>(cur_thread()->current_report
);
64 SANITIZER_INTERFACE_ATTRIBUTE
65 int __tsan_get_report_data(void *report
, const char **description
, int *count
,
66 int *stack_count
, int *mop_count
, int *loc_count
,
67 int *mutex_count
, int *thread_count
,
68 int *unique_tid_count
, void **sleep_trace
,
70 const ReportDesc
*rep
= (ReportDesc
*)report
;
71 *description
= ReportTypeDescription(rep
->typ
);
73 *stack_count
= rep
->stacks
.Size();
74 *mop_count
= rep
->mops
.Size();
75 *loc_count
= rep
->locs
.Size();
76 *mutex_count
= rep
->mutexes
.Size();
77 *thread_count
= rep
->threads
.Size();
78 *unique_tid_count
= rep
->unique_tids
.Size();
79 if (rep
->sleep
) CopyTrace(rep
->sleep
->frames
, sleep_trace
, trace_size
);
83 SANITIZER_INTERFACE_ATTRIBUTE
84 int __tsan_get_report_tag(void *report
, uptr
*tag
) {
85 const ReportDesc
*rep
= (ReportDesc
*)report
;
90 SANITIZER_INTERFACE_ATTRIBUTE
91 int __tsan_get_report_stack(void *report
, uptr idx
, void **trace
,
93 const ReportDesc
*rep
= (ReportDesc
*)report
;
94 CHECK_LT(idx
, rep
->stacks
.Size());
95 ReportStack
*stack
= rep
->stacks
[idx
];
96 if (stack
) CopyTrace(stack
->frames
, trace
, trace_size
);
100 SANITIZER_INTERFACE_ATTRIBUTE
101 int __tsan_get_report_mop(void *report
, uptr idx
, int *tid
, void **addr
,
102 int *size
, int *write
, int *atomic
, void **trace
,
104 const ReportDesc
*rep
= (ReportDesc
*)report
;
105 CHECK_LT(idx
, rep
->mops
.Size());
106 ReportMop
*mop
= rep
->mops
[idx
];
108 *addr
= (void *)mop
->addr
;
110 *write
= mop
->write
? 1 : 0;
111 *atomic
= mop
->atomic
? 1 : 0;
112 if (mop
->stack
) CopyTrace(mop
->stack
->frames
, trace
, trace_size
);
116 SANITIZER_INTERFACE_ATTRIBUTE
117 int __tsan_get_report_loc(void *report
, uptr idx
, const char **type
,
118 void **addr
, uptr
*start
, uptr
*size
, int *tid
,
119 int *fd
, int *suppressable
, void **trace
,
121 const ReportDesc
*rep
= (ReportDesc
*)report
;
122 CHECK_LT(idx
, rep
->locs
.Size());
123 ReportLocation
*loc
= rep
->locs
[idx
];
124 *type
= ReportLocationTypeDescription(loc
->type
);
125 *addr
= (void *)loc
->global
.start
;
126 *start
= loc
->heap_chunk_start
;
127 *size
= loc
->heap_chunk_size
;
130 *suppressable
= loc
->suppressable
;
131 if (loc
->stack
) CopyTrace(loc
->stack
->frames
, trace
, trace_size
);
135 SANITIZER_INTERFACE_ATTRIBUTE
136 int __tsan_get_report_loc_object_type(void *report
, uptr idx
,
137 const char **object_type
) {
138 const ReportDesc
*rep
= (ReportDesc
*)report
;
139 CHECK_LT(idx
, rep
->locs
.Size());
140 ReportLocation
*loc
= rep
->locs
[idx
];
141 *object_type
= GetObjectTypeFromTag(loc
->external_tag
);
145 SANITIZER_INTERFACE_ATTRIBUTE
146 int __tsan_get_report_mutex(void *report
, uptr idx
, uptr
*mutex_id
, void **addr
,
147 int *destroyed
, void **trace
, uptr trace_size
) {
148 const ReportDesc
*rep
= (ReportDesc
*)report
;
149 CHECK_LT(idx
, rep
->mutexes
.Size());
150 ReportMutex
*mutex
= rep
->mutexes
[idx
];
151 *mutex_id
= mutex
->id
;
152 *addr
= (void *)mutex
->addr
;
153 *destroyed
= mutex
->destroyed
;
154 if (mutex
->stack
) CopyTrace(mutex
->stack
->frames
, trace
, trace_size
);
158 SANITIZER_INTERFACE_ATTRIBUTE
159 int __tsan_get_report_thread(void *report
, uptr idx
, int *tid
, tid_t
*os_id
,
160 int *running
, const char **name
, int *parent_tid
,
161 void **trace
, uptr trace_size
) {
162 const ReportDesc
*rep
= (ReportDesc
*)report
;
163 CHECK_LT(idx
, rep
->threads
.Size());
164 ReportThread
*thread
= rep
->threads
[idx
];
166 *os_id
= thread
->os_id
;
167 *running
= thread
->running
;
168 *name
= thread
->name
;
169 *parent_tid
= thread
->parent_tid
;
170 if (thread
->stack
) CopyTrace(thread
->stack
->frames
, trace
, trace_size
);
174 SANITIZER_INTERFACE_ATTRIBUTE
175 int __tsan_get_report_unique_tid(void *report
, uptr idx
, int *tid
) {
176 const ReportDesc
*rep
= (ReportDesc
*)report
;
177 CHECK_LT(idx
, rep
->unique_tids
.Size());
178 *tid
= rep
->unique_tids
[idx
];
182 SANITIZER_INTERFACE_ATTRIBUTE
183 const char *__tsan_locate_address(uptr addr
, char *name
, uptr name_size
,
184 uptr
*region_address_ptr
,
185 uptr
*region_size_ptr
) {
186 uptr region_address
= 0;
187 uptr region_size
= 0;
188 const char *region_kind
= nullptr;
189 if (name
&& name_size
> 0) name
[0] = 0;
191 if (IsMetaMem(addr
)) {
192 region_kind
= "meta shadow";
193 } else if (IsShadowMem(addr
)) {
194 region_kind
= "shadow";
196 bool is_stack
= false;
198 Allocator
*a
= allocator();
199 if (a
->PointerIsMine((void *)addr
)) {
200 void *block_begin
= a
->GetBlockBegin((void *)addr
);
201 if (block_begin
) b
= ctx
->metamap
.GetBlock((uptr
)block_begin
);
205 region_address
= (uptr
)allocator()->GetBlockBegin((void *)addr
);
206 region_size
= b
->siz
;
207 region_kind
= "heap";
209 // TODO(kuba.brecka): We should not lock. This is supposed to be called
210 // from within the debugger when other threads are stopped.
211 ctx
->thread_registry
->Lock();
212 ThreadContext
*tctx
= IsThreadStackOrTls(addr
, &is_stack
);
213 ctx
->thread_registry
->Unlock();
215 region_kind
= is_stack
? "stack" : "tls";
217 region_kind
= "global";
219 if (Symbolizer::GetOrInit()->SymbolizeData(addr
, &info
)) {
220 internal_strncpy(name
, info
.name
, name_size
);
221 region_address
= info
.start
;
222 region_size
= info
.size
;
229 if (region_address_ptr
) *region_address_ptr
= region_address
;
230 if (region_size_ptr
) *region_size_ptr
= region_size
;
234 SANITIZER_INTERFACE_ATTRIBUTE
235 int __tsan_get_alloc_stack(uptr addr
, uptr
*trace
, uptr size
, int *thread_id
,
238 Allocator
*a
= allocator();
239 if (a
->PointerIsMine((void *)addr
)) {
240 void *block_begin
= a
->GetBlockBegin((void *)addr
);
241 if (block_begin
) b
= ctx
->metamap
.GetBlock((uptr
)block_begin
);
243 if (b
== 0) return 0;
246 // No locking. This is supposed to be called from within the debugger when
247 // other threads are stopped.
248 ThreadContextBase
*tctx
= ctx
->thread_registry
->GetThreadLocked(b
->tid
);
249 *os_id
= tctx
->os_id
;
251 StackTrace stack
= StackDepotGet(b
->stk
);
252 size
= Min(size
, (uptr
)stack
.size
);
253 for (uptr i
= 0; i
< size
; i
++) trace
[i
] = stack
.trace
[stack
.size
- i
- 1];