1 //===-- tsan_platform_mac.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.
11 //===----------------------------------------------------------------------===//
13 #include "sanitizer_common/sanitizer_platform.h"
16 #include "sanitizer_common/sanitizer_atomic.h"
17 #include "sanitizer_common/sanitizer_common.h"
18 #include "sanitizer_common/sanitizer_libc.h"
19 #include "sanitizer_common/sanitizer_posix.h"
20 #include "sanitizer_common/sanitizer_procmaps.h"
21 #include "sanitizer_common/sanitizer_stackdepot.h"
22 #include "tsan_platform.h"
24 #include "tsan_flags.h"
26 #include <mach/mach.h>
34 #include <sys/syscall.h>
36 #include <sys/types.h>
37 #include <sys/resource.h>
46 static void *SignalSafeGetOrAllocate(uptr
*dst
, uptr size
) {
47 atomic_uintptr_t
*a
= (atomic_uintptr_t
*)dst
;
48 void *val
= (void *)atomic_load_relaxed(a
);
49 atomic_signal_fence(memory_order_acquire
); // Turns the previous load into
50 // acquire wrt signals.
51 if (UNLIKELY(val
== nullptr)) {
52 val
= (void *)internal_mmap(nullptr, size
, PROT_READ
| PROT_WRITE
,
53 MAP_PRIVATE
| MAP_ANON
, -1, 0);
56 if (!atomic_compare_exchange_strong(a
, (uintptr_t *)&cmp
, (uintptr_t)val
,
57 memory_order_acq_rel
)) {
58 internal_munmap(val
, size
);
65 // On OS X, accessing TLVs via __thread or manually by using pthread_key_* is
66 // problematic, because there are several places where interceptors are called
67 // when TLVs are not accessible (early process startup, thread cleanup, ...).
68 // The following provides a "poor man's TLV" implementation, where we use the
69 // shadow memory of the pointer returned by pthread_self() to store a pointer to
70 // the ThreadState object. The main thread's ThreadState is stored separately
71 // in a static variable, because we need to access it even before the
72 // shadow memory is set up.
73 static uptr main_thread_identity
= 0;
74 ALIGNED(64) static char main_thread_state
[sizeof(ThreadState
)];
76 ThreadState
**cur_thread_location() {
77 ThreadState
**thread_identity
= (ThreadState
**)pthread_self();
78 return ((uptr
)thread_identity
== main_thread_identity
) ? nullptr
82 ThreadState
*cur_thread() {
83 ThreadState
**thr_state_loc
= cur_thread_location();
84 if (thr_state_loc
== nullptr || main_thread_identity
== 0) {
85 return (ThreadState
*)&main_thread_state
;
87 ThreadState
**fake_tls
= (ThreadState
**)MemToShadow((uptr
)thr_state_loc
);
88 ThreadState
*thr
= (ThreadState
*)SignalSafeGetOrAllocate(
89 (uptr
*)fake_tls
, sizeof(ThreadState
));
93 // TODO(kuba.brecka): This is not async-signal-safe. In particular, we call
94 // munmap first and then clear `fake_tls`; if we receive a signal in between,
95 // handler will try to access the unmapped ThreadState.
96 void cur_thread_finalize() {
97 ThreadState
**thr_state_loc
= cur_thread_location();
98 if (thr_state_loc
== nullptr) {
99 // Calling dispatch_main() or xpc_main() actually invokes pthread_exit to
100 // exit the main thread. Let's keep the main thread's ThreadState.
103 ThreadState
**fake_tls
= (ThreadState
**)MemToShadow((uptr
)thr_state_loc
);
104 internal_munmap(*fake_tls
, sizeof(ThreadState
));
109 void FlushShadowMemory() {
112 static void RegionMemUsage(uptr start
, uptr end
, uptr
*res
, uptr
*dirty
) {
113 vm_address_t address
= start
;
114 vm_address_t end_address
= end
;
115 uptr resident_pages
= 0;
116 uptr dirty_pages
= 0;
117 while (address
< end_address
) {
118 vm_size_t vm_region_size
;
119 mach_msg_type_number_t count
= VM_REGION_EXTENDED_INFO_COUNT
;
120 vm_region_extended_info_data_t vm_region_info
;
121 mach_port_t object_name
;
122 kern_return_t ret
= vm_region_64(
123 mach_task_self(), &address
, &vm_region_size
, VM_REGION_EXTENDED_INFO
,
124 (vm_region_info_t
)&vm_region_info
, &count
, &object_name
);
125 if (ret
!= KERN_SUCCESS
) break;
127 resident_pages
+= vm_region_info
.pages_resident
;
128 dirty_pages
+= vm_region_info
.pages_dirtied
;
130 address
+= vm_region_size
;
132 *res
= resident_pages
* GetPageSizeCached();
133 *dirty
= dirty_pages
* GetPageSizeCached();
136 void WriteMemoryProfile(char *buf
, uptr buf_size
, uptr nthread
, uptr nlive
) {
137 uptr shadow_res
, shadow_dirty
;
138 uptr meta_res
, meta_dirty
;
139 uptr trace_res
, trace_dirty
;
140 RegionMemUsage(ShadowBeg(), ShadowEnd(), &shadow_res
, &shadow_dirty
);
141 RegionMemUsage(MetaShadowBeg(), MetaShadowEnd(), &meta_res
, &meta_dirty
);
142 RegionMemUsage(TraceMemBeg(), TraceMemEnd(), &trace_res
, &trace_dirty
);
145 uptr low_res
, low_dirty
;
146 uptr high_res
, high_dirty
;
147 uptr heap_res
, heap_dirty
;
148 RegionMemUsage(LoAppMemBeg(), LoAppMemEnd(), &low_res
, &low_dirty
);
149 RegionMemUsage(HiAppMemBeg(), HiAppMemEnd(), &high_res
, &high_dirty
);
150 RegionMemUsage(HeapMemBeg(), HeapMemEnd(), &heap_res
, &heap_dirty
);
151 #else // !SANITIZER_GO
152 uptr app_res
, app_dirty
;
153 RegionMemUsage(AppMemBeg(), AppMemEnd(), &app_res
, &app_dirty
);
156 StackDepotStats
*stacks
= StackDepotGetStats();
157 internal_snprintf(buf
, buf_size
,
158 "shadow (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n"
159 "meta (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n"
160 "traces (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n"
162 "low app (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n"
163 "high app (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n"
164 "heap (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n"
165 #else // !SANITIZER_GO
166 "app (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n"
168 "stacks: %zd unique IDs, %zd kB allocated\n"
169 "threads: %zd total, %zd live\n"
170 "------------------------------\n",
171 ShadowBeg(), ShadowEnd(), shadow_res
/ 1024, shadow_dirty
/ 1024,
172 MetaShadowBeg(), MetaShadowEnd(), meta_res
/ 1024, meta_dirty
/ 1024,
173 TraceMemBeg(), TraceMemEnd(), trace_res
/ 1024, trace_dirty
/ 1024,
175 LoAppMemBeg(), LoAppMemEnd(), low_res
/ 1024, low_dirty
/ 1024,
176 HiAppMemBeg(), HiAppMemEnd(), high_res
/ 1024, high_dirty
/ 1024,
177 HeapMemBeg(), HeapMemEnd(), heap_res
/ 1024, heap_dirty
/ 1024,
178 #else // !SANITIZER_GO
179 AppMemBeg(), AppMemEnd(), app_res
/ 1024, app_dirty
/ 1024,
181 stacks
->n_uniq_ids
, stacks
->allocated
/ 1024,
186 void InitializeShadowMemoryPlatform() { }
188 // On OS X, GCD worker threads are created without a call to pthread_create. We
189 // need to properly register these threads with ThreadCreate and ThreadStart.
190 // These threads don't have a parent thread, as they are created "spuriously".
191 // We're using a libpthread API that notifies us about a newly created thread.
192 // The `thread == pthread_self()` check indicates this is actually a worker
193 // thread. If it's just a regular thread, this hook is called on the parent
195 typedef void (*pthread_introspection_hook_t
)(unsigned int event
,
196 pthread_t thread
, void *addr
,
198 extern "C" pthread_introspection_hook_t
pthread_introspection_hook_install(
199 pthread_introspection_hook_t hook
);
200 static const uptr PTHREAD_INTROSPECTION_THREAD_CREATE
= 1;
201 static const uptr PTHREAD_INTROSPECTION_THREAD_TERMINATE
= 3;
202 static pthread_introspection_hook_t prev_pthread_introspection_hook
;
203 static void my_pthread_introspection_hook(unsigned int event
, pthread_t thread
,
204 void *addr
, size_t size
) {
205 if (event
== PTHREAD_INTROSPECTION_THREAD_CREATE
) {
206 if (thread
== pthread_self()) {
207 // The current thread is a newly created GCD worker thread.
208 ThreadState
*thr
= cur_thread();
209 Processor
*proc
= ProcCreate();
211 ThreadState
*parent_thread_state
= nullptr; // No parent.
212 int tid
= ThreadCreate(parent_thread_state
, 0, (uptr
)thread
, true);
214 ThreadStart(thr
, tid
, GetTid(), /*workerthread*/ true);
216 } else if (event
== PTHREAD_INTROSPECTION_THREAD_TERMINATE
) {
217 if (thread
== pthread_self()) {
218 ThreadState
*thr
= cur_thread();
220 DestroyThreadState();
225 if (prev_pthread_introspection_hook
!= nullptr)
226 prev_pthread_introspection_hook(event
, thread
, addr
, size
);
230 void InitializePlatformEarly() {
231 #if defined(__aarch64__)
232 uptr max_vm
= GetMaxVirtualAddress() + 1;
233 if (max_vm
!= Mapping::kHiAppMemEnd
) {
234 Printf("ThreadSanitizer: unsupported vm address limit %p, expected %p.\n",
235 max_vm
, Mapping::kHiAppMemEnd
);
241 void InitializePlatform() {
242 DisableCoreDumperIfNecessary();
246 CHECK_EQ(main_thread_identity
, 0);
247 main_thread_identity
= (uptr
)pthread_self();
249 prev_pthread_introspection_hook
=
250 pthread_introspection_hook_install(&my_pthread_introspection_hook
);
255 void ImitateTlsWrite(ThreadState
*thr
, uptr tls_addr
, uptr tls_size
) {
256 // The pointer to the ThreadState object is stored in the shadow memory
258 uptr tls_end
= tls_addr
+ tls_size
;
259 ThreadState
**thr_state_loc
= cur_thread_location();
260 if (thr_state_loc
== nullptr) {
261 MemoryRangeImitateWrite(thr
, /*pc=*/2, tls_addr
, tls_size
);
263 uptr thr_state_start
= (uptr
)thr_state_loc
;
264 uptr thr_state_end
= thr_state_start
+ sizeof(uptr
);
265 CHECK_GE(thr_state_start
, tls_addr
);
266 CHECK_LE(thr_state_start
, tls_addr
+ tls_size
);
267 CHECK_GE(thr_state_end
, tls_addr
);
268 CHECK_LE(thr_state_end
, tls_addr
+ tls_size
);
269 MemoryRangeImitateWrite(thr
, /*pc=*/2, tls_addr
,
270 thr_state_start
- tls_addr
);
271 MemoryRangeImitateWrite(thr
, /*pc=*/2, thr_state_end
,
272 tls_end
- thr_state_end
);
278 // Note: this function runs with async signals enabled,
279 // so it must not touch any tsan state.
280 int call_pthread_cancel_with_cleanup(int(*fn
)(void *c
, void *m
,
281 void *abstime
), void *c
, void *m
, void *abstime
,
282 void(*cleanup
)(void *arg
), void *arg
) {
283 // pthread_cleanup_push/pop are hardcore macros mess.
284 // We can't intercept nor call them w/o including pthread.h.
286 pthread_cleanup_push(cleanup
, arg
);
287 res
= fn(c
, m
, abstime
);
288 pthread_cleanup_pop(0);
293 } // namespace __tsan
295 #endif // SANITIZER_MAC