blob: 43e8c00ba06fffa0a1b3e96c8b433097ea593d31 [file] [log] [blame]
Kostya Serebryany1e172b42011-11-30 01:07:02 +00001//===-- asan_mac.cc -------------------------------------------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file is a part of AddressSanitizer, an address sanity checker.
11//
12// Mac-specific details.
13//===----------------------------------------------------------------------===//
14
Kostya Serebryanyd6567c52011-12-01 21:40:52 +000015#ifdef __APPLE__
Kostya Serebryany1e172b42011-11-30 01:07:02 +000016
17#include "asan_mac.h"
18
19#include "asan_internal.h"
20#include "asan_stack.h"
21#include "asan_thread.h"
22#include "asan_thread_registry.h"
23
Kostya Serebryany1e172b42011-11-30 01:07:02 +000024#include <sys/mman.h>
25#include <unistd.h>
26
Kostya Serebryany19573992011-12-02 18:52:35 +000027#include <new>
Kostya Serebryany4f24d3e2011-12-02 18:48:20 +000028
Kostya Serebryany1e172b42011-11-30 01:07:02 +000029namespace __asan {
30
31extern dispatch_async_f_f real_dispatch_async_f;
32extern dispatch_sync_f_f real_dispatch_sync_f;
33extern dispatch_after_f_f real_dispatch_after_f;
34extern dispatch_barrier_async_f_f real_dispatch_barrier_async_f;
35extern dispatch_group_async_f_f real_dispatch_group_async_f;
36extern pthread_workqueue_additem_np_f real_pthread_workqueue_additem_np;
37
38// No-op. Mac does not support static linkage anyway.
39void *AsanDoesNotSupportStaticLinkage() {
40 return NULL;
41}
42
43void *asan_mmap(void *addr, size_t length, int prot, int flags,
44 int fd, uint64_t offset) {
45 return mmap(addr, length, prot, flags, fd, offset);
46}
47
48ssize_t asan_write(int fd, const void *buf, size_t count) {
49 return write(fd, buf, count);
50}
51
52// Support for the following functions from libdispatch on Mac OS:
53// dispatch_async_f()
54// dispatch_async()
55// dispatch_sync_f()
56// dispatch_sync()
57// dispatch_after_f()
58// dispatch_after()
59// dispatch_group_async_f()
60// dispatch_group_async()
61// TODO(glider): libdispatch API contains other functions that we don't support
62// yet.
63//
64// dispatch_sync() and dispatch_sync_f() are synchronous, although chances are
65// they can cause jobs to run on a thread different from the current one.
66// TODO(glider): if so, we need a test for this (otherwise we should remove
67// them).
68//
69// The following functions use dispatch_barrier_async_f() (which isn't a library
70// function but is exported) and are thus supported:
71// dispatch_source_set_cancel_handler_f()
72// dispatch_source_set_cancel_handler()
73// dispatch_source_set_event_handler_f()
74// dispatch_source_set_event_handler()
75//
76// The reference manual for Grand Central Dispatch is available at
77// http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
78// The implementation details are at
79// http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c
80
81extern "C"
82void asan_dispatch_call_block_and_release(void *block) {
83 GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
84 asan_block_context_t *context = (asan_block_context_t*)block;
85 if (FLAG_v >= 2) {
86 Report("asan_dispatch_call_block_and_release(): "
87 "context: %p, pthread_self: %p\n",
88 block, pthread_self());
89 }
90 AsanThread *t = asanThreadRegistry().GetCurrent();
91 if (t) {
92 // We've already executed a job on this worker thread. Let's reuse the
93 // AsanThread object.
94 if (t != asanThreadRegistry().GetMain()) {
95 // Flush the statistics and update the current thread's tid.
96 asanThreadRegistry().UnregisterThread(t);
97 asanThreadRegistry().RegisterThread(t, context->parent_tid, &stack);
98 }
99 // Otherwise the worker is being executed on the main thread -- we are
100 // draining the dispatch queue.
101 // TODO(glider): any checks for that?
102 } else {
103 // It's incorrect to assert that the current thread is not dying: at least
104 // the callbacks from dispatch_sync() are sometimes called after the TSD is
105 // destroyed.
106 t = (AsanThread*)asan_malloc(sizeof(AsanThread), &stack);
107 new(t) AsanThread(context->parent_tid,
108 /*start_routine*/NULL, /*arg*/NULL, &stack);
Kostya Serebryany69eca732011-12-16 19:13:35 +0000109 t->Init();
Kostya Serebryany1e172b42011-11-30 01:07:02 +0000110 asanThreadRegistry().SetCurrent(t);
111 }
112 // Call the original dispatcher for the block.
113 context->func(context->block);
114 asan_free(context, &stack);
115}
116
117} // namespace __asan
118
119using namespace __asan; // NOLINT
120
121// Wrap |ctxt| and |func| into an asan_block_context_t.
122// The caller retains control of the allocated context.
123extern "C"
124asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func,
125 AsanStackTrace *stack) {
126 asan_block_context_t *asan_ctxt =
127 (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack);
128 asan_ctxt->block = ctxt;
129 asan_ctxt->func = func;
130 AsanThread *curr_thread = asanThreadRegistry().GetCurrent();
131 if (FLAG_debug) {
132 // Sometimes at Chromium teardown this assertion is violated:
133 // -- a task is created via dispatch_async() on the "CFMachPort"
134 // thread while doing _dispatch_queue_drain();
135 // -- a task is created via dispatch_async_f() on the
136 // "com.apple.root.default-overcommit-priority" thread while doing
137 // _dispatch_dispose().
138 // TODO(glider): find out what's going on.
139 CHECK(curr_thread || asanThreadRegistry().IsCurrentThreadDying());
140 }
141 asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrMinusOne();
142 return asan_ctxt;
143}
144
145// TODO(glider): can we reduce code duplication by introducing a macro?
146extern "C"
147int WRAP(dispatch_async_f)(dispatch_queue_t dq,
148 void *ctxt,
149 dispatch_function_t func) {
150 GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
151 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
152 if (FLAG_v >= 2) {
153 Report("dispatch_async_f(): context: %p, pthread_self: %p\n",
154 asan_ctxt, pthread_self());
155 PRINT_CURRENT_STACK();
156 }
157 return real_dispatch_async_f(dq, (void*)asan_ctxt,
158 asan_dispatch_call_block_and_release);
159}
160
161extern "C"
162int WRAP(dispatch_sync_f)(dispatch_queue_t dq,
163 void *ctxt,
164 dispatch_function_t func) {
165 GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
166 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
167 if (FLAG_v >= 2) {
168 Report("dispatch_sync_f(): context: %p, pthread_self: %p\n",
169 asan_ctxt, pthread_self());
170 PRINT_CURRENT_STACK();
171 }
172 return real_dispatch_sync_f(dq, (void*)asan_ctxt,
173 asan_dispatch_call_block_and_release);
174}
175
176extern "C"
177int WRAP(dispatch_after_f)(dispatch_time_t when,
178 dispatch_queue_t dq,
179 void *ctxt,
180 dispatch_function_t func) {
181 GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
182 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
183 if (FLAG_v >= 2) {
184 Report("dispatch_after_f: %p\n", asan_ctxt);
185 PRINT_CURRENT_STACK();
186 }
187 return real_dispatch_after_f(when, dq, (void*)asan_ctxt,
188 asan_dispatch_call_block_and_release);
189}
190
191extern "C"
192void WRAP(dispatch_barrier_async_f)(dispatch_queue_t dq,
193 void *ctxt, dispatch_function_t func) {
194 GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
195 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
196 if (FLAG_v >= 2) {
197 Report("dispatch_barrier_async_f(): context: %p, pthread_self: %p\n",
198 asan_ctxt, pthread_self());
199 PRINT_CURRENT_STACK();
200 }
201 real_dispatch_barrier_async_f(dq, (void*)asan_ctxt,
202 asan_dispatch_call_block_and_release);
203}
204
205extern "C"
206void WRAP(dispatch_group_async_f)(dispatch_group_t group,
207 dispatch_queue_t dq,
208 void *ctxt, dispatch_function_t func) {
209 GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
210 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
211 if (FLAG_v >= 2) {
212 Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n",
213 asan_ctxt, pthread_self());
214 PRINT_CURRENT_STACK();
215 }
216 real_dispatch_group_async_f(group, dq, (void*)asan_ctxt,
217 asan_dispatch_call_block_and_release);
218}
219
220// The following stuff has been extremely helpful while looking for the
221// unhandled functions that spawned jobs on Chromium shutdown. If the verbosity
222// level is 2 or greater, we wrap pthread_workqueue_additem_np() in order to
223// find the points of worker thread creation (each of such threads may be used
224// to run several tasks, that's why this is not enough to support the whole
225// libdispatch API.
226extern "C"
227void *wrap_workitem_func(void *arg) {
228 if (FLAG_v >= 2) {
229 Report("wrap_workitem_func: %p, pthread_self: %p\n", arg, pthread_self());
230 }
231 asan_block_context_t *ctxt = (asan_block_context_t*)arg;
232 worker_t fn = (worker_t)(ctxt->func);
233 void *result = fn(ctxt->block);
234 GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
235 asan_free(arg, &stack);
236 return result;
237}
238
239extern "C"
240int WRAP(pthread_workqueue_additem_np)(pthread_workqueue_t workq,
241 void *(*workitem_func)(void *), void * workitem_arg,
242 pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp) {
243 GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
244 asan_block_context_t *asan_ctxt =
245 (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), &stack);
246 asan_ctxt->block = workitem_arg;
247 asan_ctxt->func = (dispatch_function_t)workitem_func;
248 asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrMinusOne();
249 if (FLAG_v >= 2) {
250 Report("pthread_workqueue_additem_np: %p\n", asan_ctxt);
251 PRINT_CURRENT_STACK();
252 }
253 return real_pthread_workqueue_additem_np(workq, wrap_workitem_func, asan_ctxt,
254 itemhandlep, gencountp);
255}
Kostya Serebryanyd6567c52011-12-01 21:40:52 +0000256
257#endif // __APPLE__