blob: 0084fb7595592d696df1e499745959a265344fea [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
24#include <algorithm>
25
26#include <sys/mman.h>
27#include <unistd.h>
28
29namespace __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);
109 asanThreadRegistry().SetCurrent(t);
110 }
111 // Call the original dispatcher for the block.
112 context->func(context->block);
113 asan_free(context, &stack);
114}
115
116} // namespace __asan
117
118using namespace __asan; // NOLINT
119
120// Wrap |ctxt| and |func| into an asan_block_context_t.
121// The caller retains control of the allocated context.
122extern "C"
123asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func,
124 AsanStackTrace *stack) {
125 asan_block_context_t *asan_ctxt =
126 (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack);
127 asan_ctxt->block = ctxt;
128 asan_ctxt->func = func;
129 AsanThread *curr_thread = asanThreadRegistry().GetCurrent();
130 if (FLAG_debug) {
131 // Sometimes at Chromium teardown this assertion is violated:
132 // -- a task is created via dispatch_async() on the "CFMachPort"
133 // thread while doing _dispatch_queue_drain();
134 // -- a task is created via dispatch_async_f() on the
135 // "com.apple.root.default-overcommit-priority" thread while doing
136 // _dispatch_dispose().
137 // TODO(glider): find out what's going on.
138 CHECK(curr_thread || asanThreadRegistry().IsCurrentThreadDying());
139 }
140 asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrMinusOne();
141 return asan_ctxt;
142}
143
144// TODO(glider): can we reduce code duplication by introducing a macro?
145extern "C"
146int WRAP(dispatch_async_f)(dispatch_queue_t dq,
147 void *ctxt,
148 dispatch_function_t func) {
149 GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
150 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
151 if (FLAG_v >= 2) {
152 Report("dispatch_async_f(): context: %p, pthread_self: %p\n",
153 asan_ctxt, pthread_self());
154 PRINT_CURRENT_STACK();
155 }
156 return real_dispatch_async_f(dq, (void*)asan_ctxt,
157 asan_dispatch_call_block_and_release);
158}
159
160extern "C"
161int WRAP(dispatch_sync_f)(dispatch_queue_t dq,
162 void *ctxt,
163 dispatch_function_t func) {
164 GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
165 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
166 if (FLAG_v >= 2) {
167 Report("dispatch_sync_f(): context: %p, pthread_self: %p\n",
168 asan_ctxt, pthread_self());
169 PRINT_CURRENT_STACK();
170 }
171 return real_dispatch_sync_f(dq, (void*)asan_ctxt,
172 asan_dispatch_call_block_and_release);
173}
174
175extern "C"
176int WRAP(dispatch_after_f)(dispatch_time_t when,
177 dispatch_queue_t dq,
178 void *ctxt,
179 dispatch_function_t func) {
180 GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
181 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
182 if (FLAG_v >= 2) {
183 Report("dispatch_after_f: %p\n", asan_ctxt);
184 PRINT_CURRENT_STACK();
185 }
186 return real_dispatch_after_f(when, dq, (void*)asan_ctxt,
187 asan_dispatch_call_block_and_release);
188}
189
190extern "C"
191void WRAP(dispatch_barrier_async_f)(dispatch_queue_t dq,
192 void *ctxt, dispatch_function_t func) {
193 GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
194 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
195 if (FLAG_v >= 2) {
196 Report("dispatch_barrier_async_f(): context: %p, pthread_self: %p\n",
197 asan_ctxt, pthread_self());
198 PRINT_CURRENT_STACK();
199 }
200 real_dispatch_barrier_async_f(dq, (void*)asan_ctxt,
201 asan_dispatch_call_block_and_release);
202}
203
204extern "C"
205void WRAP(dispatch_group_async_f)(dispatch_group_t group,
206 dispatch_queue_t dq,
207 void *ctxt, dispatch_function_t func) {
208 GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
209 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
210 if (FLAG_v >= 2) {
211 Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n",
212 asan_ctxt, pthread_self());
213 PRINT_CURRENT_STACK();
214 }
215 real_dispatch_group_async_f(group, dq, (void*)asan_ctxt,
216 asan_dispatch_call_block_and_release);
217}
218
219// The following stuff has been extremely helpful while looking for the
220// unhandled functions that spawned jobs on Chromium shutdown. If the verbosity
221// level is 2 or greater, we wrap pthread_workqueue_additem_np() in order to
222// find the points of worker thread creation (each of such threads may be used
223// to run several tasks, that's why this is not enough to support the whole
224// libdispatch API.
225extern "C"
226void *wrap_workitem_func(void *arg) {
227 if (FLAG_v >= 2) {
228 Report("wrap_workitem_func: %p, pthread_self: %p\n", arg, pthread_self());
229 }
230 asan_block_context_t *ctxt = (asan_block_context_t*)arg;
231 worker_t fn = (worker_t)(ctxt->func);
232 void *result = fn(ctxt->block);
233 GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
234 asan_free(arg, &stack);
235 return result;
236}
237
238extern "C"
239int WRAP(pthread_workqueue_additem_np)(pthread_workqueue_t workq,
240 void *(*workitem_func)(void *), void * workitem_arg,
241 pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp) {
242 GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
243 asan_block_context_t *asan_ctxt =
244 (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), &stack);
245 asan_ctxt->block = workitem_arg;
246 asan_ctxt->func = (dispatch_function_t)workitem_func;
247 asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrMinusOne();
248 if (FLAG_v >= 2) {
249 Report("pthread_workqueue_additem_np: %p\n", asan_ctxt);
250 PRINT_CURRENT_STACK();
251 }
252 return real_pthread_workqueue_additem_np(workq, wrap_workitem_func, asan_ctxt,
253 itemhandlep, gencountp);
254}
Kostya Serebryanyd6567c52011-12-01 21:40:52 +0000255
256#endif // __APPLE__