Home | History | Annotate | Line # | Download | only in lsan
      1  1.1  kamil //===-- lsan_mac.cc -------------------------------------------------------===//
      2  1.1  kamil //
      3  1.1  kamil //                     The LLVM Compiler Infrastructure
      4  1.1  kamil //
      5  1.1  kamil // This file is distributed under the University of Illinois Open Source
      6  1.1  kamil // License. See LICENSE.TXT for details.
      7  1.1  kamil //
      8  1.1  kamil //===----------------------------------------------------------------------===//
      9  1.1  kamil //
     10  1.1  kamil // This file is a part of LeakSanitizer, a memory leak checker.
     11  1.1  kamil //
     12  1.1  kamil // Mac-specific details.
     13  1.1  kamil //===----------------------------------------------------------------------===//
     14  1.1  kamil 
     15  1.1  kamil #include "sanitizer_common/sanitizer_platform.h"
     16  1.1  kamil #if SANITIZER_MAC
     17  1.1  kamil 
     18  1.1  kamil #include "interception/interception.h"
     19  1.1  kamil #include "lsan.h"
     20  1.1  kamil #include "lsan_allocator.h"
     21  1.1  kamil #include "lsan_thread.h"
     22  1.1  kamil 
     23  1.1  kamil #include <pthread.h>
     24  1.1  kamil 
     25  1.1  kamil namespace __lsan {
     26  1.1  kamil // Support for the following functions from libdispatch on Mac OS:
     27  1.1  kamil //   dispatch_async_f()
     28  1.1  kamil //   dispatch_async()
     29  1.1  kamil //   dispatch_sync_f()
     30  1.1  kamil //   dispatch_sync()
     31  1.1  kamil //   dispatch_after_f()
     32  1.1  kamil //   dispatch_after()
     33  1.1  kamil //   dispatch_group_async_f()
     34  1.1  kamil //   dispatch_group_async()
     35  1.1  kamil // TODO(glider): libdispatch API contains other functions that we don't support
     36  1.1  kamil // yet.
     37  1.1  kamil //
     38  1.1  kamil // dispatch_sync() and dispatch_sync_f() are synchronous, although chances are
     39  1.1  kamil // they can cause jobs to run on a thread different from the current one.
     40  1.1  kamil // TODO(glider): if so, we need a test for this (otherwise we should remove
     41  1.1  kamil // them).
     42  1.1  kamil //
     43  1.1  kamil // The following functions use dispatch_barrier_async_f() (which isn't a library
     44  1.1  kamil // function but is exported) and are thus supported:
     45  1.1  kamil //   dispatch_source_set_cancel_handler_f()
     46  1.1  kamil //   dispatch_source_set_cancel_handler()
     47  1.1  kamil //   dispatch_source_set_event_handler_f()
     48  1.1  kamil //   dispatch_source_set_event_handler()
     49  1.1  kamil //
     50  1.1  kamil // The reference manual for Grand Central Dispatch is available at
     51  1.1  kamil //   http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
     52  1.1  kamil // The implementation details are at
     53  1.1  kamil //   http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c
     54  1.1  kamil 
     55  1.1  kamil typedef void *dispatch_group_t;
     56  1.1  kamil typedef void *dispatch_queue_t;
     57  1.1  kamil typedef void *dispatch_source_t;
     58  1.1  kamil typedef u64 dispatch_time_t;
     59  1.1  kamil typedef void (*dispatch_function_t)(void *block);
     60  1.1  kamil typedef void *(*worker_t)(void *block);
     61  1.1  kamil 
     62  1.1  kamil // A wrapper for the ObjC blocks used to support libdispatch.
     63  1.1  kamil typedef struct {
     64  1.1  kamil   void *block;
     65  1.1  kamil   dispatch_function_t func;
     66  1.1  kamil   u32 parent_tid;
     67  1.1  kamil } lsan_block_context_t;
     68  1.1  kamil 
     69  1.1  kamil ALWAYS_INLINE
     70  1.1  kamil void lsan_register_worker_thread(int parent_tid) {
     71  1.1  kamil   if (GetCurrentThread() == kInvalidTid) {
     72  1.1  kamil     u32 tid = ThreadCreate(parent_tid, 0, true);
     73  1.1  kamil     ThreadStart(tid, GetTid());
     74  1.1  kamil     SetCurrentThread(tid);
     75  1.1  kamil   }
     76  1.1  kamil }
     77  1.1  kamil 
     78  1.1  kamil // For use by only those functions that allocated the context via
     79  1.1  kamil // alloc_lsan_context().
     80  1.1  kamil extern "C" void lsan_dispatch_call_block_and_release(void *block) {
     81  1.1  kamil   lsan_block_context_t *context = (lsan_block_context_t *)block;
     82  1.1  kamil   VReport(2,
     83  1.1  kamil           "lsan_dispatch_call_block_and_release(): "
     84  1.1  kamil           "context: %p, pthread_self: %p\n",
     85  1.1  kamil           block, pthread_self());
     86  1.1  kamil   lsan_register_worker_thread(context->parent_tid);
     87  1.1  kamil   // Call the original dispatcher for the block.
     88  1.1  kamil   context->func(context->block);
     89  1.1  kamil   lsan_free(context);
     90  1.1  kamil }
     91  1.1  kamil 
     92  1.1  kamil }  // namespace __lsan
     93  1.1  kamil 
     94  1.1  kamil using namespace __lsan;  // NOLINT
     95  1.1  kamil 
     96  1.1  kamil // Wrap |ctxt| and |func| into an lsan_block_context_t.
     97  1.1  kamil // The caller retains control of the allocated context.
     98  1.1  kamil extern "C" lsan_block_context_t *alloc_lsan_context(void *ctxt,
     99  1.1  kamil                                                     dispatch_function_t func) {
    100  1.1  kamil   GET_STACK_TRACE_THREAD;
    101  1.1  kamil   lsan_block_context_t *lsan_ctxt =
    102  1.1  kamil       (lsan_block_context_t *)lsan_malloc(sizeof(lsan_block_context_t), stack);
    103  1.1  kamil   lsan_ctxt->block = ctxt;
    104  1.1  kamil   lsan_ctxt->func = func;
    105  1.1  kamil   lsan_ctxt->parent_tid = GetCurrentThread();
    106  1.1  kamil   return lsan_ctxt;
    107  1.1  kamil }
    108  1.1  kamil 
    109  1.1  kamil // Define interceptor for dispatch_*_f function with the three most common
    110  1.1  kamil // parameters: dispatch_queue_t, context, dispatch_function_t.
    111  1.1  kamil #define INTERCEPT_DISPATCH_X_F_3(dispatch_x_f)                        \
    112  1.1  kamil   INTERCEPTOR(void, dispatch_x_f, dispatch_queue_t dq, void *ctxt,    \
    113  1.1  kamil               dispatch_function_t func) {                             \
    114  1.1  kamil     lsan_block_context_t *lsan_ctxt = alloc_lsan_context(ctxt, func); \
    115  1.1  kamil     return REAL(dispatch_x_f)(dq, (void *)lsan_ctxt,                  \
    116  1.1  kamil                               lsan_dispatch_call_block_and_release);  \
    117  1.1  kamil   }
    118  1.1  kamil 
    119  1.1  kamil INTERCEPT_DISPATCH_X_F_3(dispatch_async_f)
    120  1.1  kamil INTERCEPT_DISPATCH_X_F_3(dispatch_sync_f)
    121  1.1  kamil INTERCEPT_DISPATCH_X_F_3(dispatch_barrier_async_f)
    122  1.1  kamil 
    123  1.1  kamil INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when, dispatch_queue_t dq,
    124  1.1  kamil             void *ctxt, dispatch_function_t func) {
    125  1.1  kamil   lsan_block_context_t *lsan_ctxt = alloc_lsan_context(ctxt, func);
    126  1.1  kamil   return REAL(dispatch_after_f)(when, dq, (void *)lsan_ctxt,
    127  1.1  kamil                                 lsan_dispatch_call_block_and_release);
    128  1.1  kamil }
    129  1.1  kamil 
    130  1.1  kamil INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group,
    131  1.1  kamil             dispatch_queue_t dq, void *ctxt, dispatch_function_t func) {
    132  1.1  kamil   lsan_block_context_t *lsan_ctxt = alloc_lsan_context(ctxt, func);
    133  1.1  kamil   REAL(dispatch_group_async_f)
    134  1.1  kamil   (group, dq, (void *)lsan_ctxt, lsan_dispatch_call_block_and_release);
    135  1.1  kamil }
    136  1.1  kamil 
    137  1.1  kamil #if !defined(MISSING_BLOCKS_SUPPORT)
    138  1.1  kamil extern "C" {
    139  1.1  kamil void dispatch_async(dispatch_queue_t dq, void (^work)(void));
    140  1.1  kamil void dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq,
    141  1.1  kamil                           void (^work)(void));
    142  1.1  kamil void dispatch_after(dispatch_time_t when, dispatch_queue_t queue,
    143  1.1  kamil                     void (^work)(void));
    144  1.1  kamil void dispatch_source_set_cancel_handler(dispatch_source_t ds,
    145  1.1  kamil                                         void (^work)(void));
    146  1.1  kamil void dispatch_source_set_event_handler(dispatch_source_t ds,
    147  1.1  kamil                                        void (^work)(void));
    148  1.1  kamil }
    149  1.1  kamil 
    150  1.1  kamil #define GET_LSAN_BLOCK(work)                 \
    151  1.1  kamil   void (^lsan_block)(void);                  \
    152  1.1  kamil   int parent_tid = GetCurrentThread();       \
    153  1.1  kamil   lsan_block = ^(void) {                     \
    154  1.1  kamil     lsan_register_worker_thread(parent_tid); \
    155  1.1  kamil     work();                                  \
    156  1.1  kamil   }
    157  1.1  kamil 
    158  1.1  kamil INTERCEPTOR(void, dispatch_async, dispatch_queue_t dq, void (^work)(void)) {
    159  1.1  kamil   GET_LSAN_BLOCK(work);
    160  1.1  kamil   REAL(dispatch_async)(dq, lsan_block);
    161  1.1  kamil }
    162  1.1  kamil 
    163  1.1  kamil INTERCEPTOR(void, dispatch_group_async, dispatch_group_t dg,
    164  1.1  kamil             dispatch_queue_t dq, void (^work)(void)) {
    165  1.1  kamil   GET_LSAN_BLOCK(work);
    166  1.1  kamil   REAL(dispatch_group_async)(dg, dq, lsan_block);
    167  1.1  kamil }
    168  1.1  kamil 
    169  1.1  kamil INTERCEPTOR(void, dispatch_after, dispatch_time_t when, dispatch_queue_t queue,
    170  1.1  kamil             void (^work)(void)) {
    171  1.1  kamil   GET_LSAN_BLOCK(work);
    172  1.1  kamil   REAL(dispatch_after)(when, queue, lsan_block);
    173  1.1  kamil }
    174  1.1  kamil 
    175  1.1  kamil INTERCEPTOR(void, dispatch_source_set_cancel_handler, dispatch_source_t ds,
    176  1.1  kamil             void (^work)(void)) {
    177  1.1  kamil   if (!work) {
    178  1.1  kamil     REAL(dispatch_source_set_cancel_handler)(ds, work);
    179  1.1  kamil     return;
    180  1.1  kamil   }
    181  1.1  kamil   GET_LSAN_BLOCK(work);
    182  1.1  kamil   REAL(dispatch_source_set_cancel_handler)(ds, lsan_block);
    183  1.1  kamil }
    184  1.1  kamil 
    185  1.1  kamil INTERCEPTOR(void, dispatch_source_set_event_handler, dispatch_source_t ds,
    186  1.1  kamil             void (^work)(void)) {
    187  1.1  kamil   GET_LSAN_BLOCK(work);
    188  1.1  kamil   REAL(dispatch_source_set_event_handler)(ds, lsan_block);
    189  1.1  kamil }
    190  1.1  kamil #endif
    191  1.1  kamil 
    192  1.1  kamil #endif  // SANITIZER_MAC
    193