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