168d75effSDimitry Andric //===-- lsan_mac.cpp ------------------------------------------------------===// 268d75effSDimitry Andric // 368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 668d75effSDimitry Andric // 768d75effSDimitry Andric //===----------------------------------------------------------------------===// 868d75effSDimitry Andric // 968d75effSDimitry Andric // This file is a part of LeakSanitizer, a memory leak checker. 1068d75effSDimitry Andric // 1168d75effSDimitry Andric // Mac-specific details. 1268d75effSDimitry Andric //===----------------------------------------------------------------------===// 1368d75effSDimitry Andric 1468d75effSDimitry Andric #include "sanitizer_common/sanitizer_platform.h" 15*81ad6265SDimitry Andric #if SANITIZER_APPLE 1668d75effSDimitry Andric 1768d75effSDimitry Andric #include "interception/interception.h" 1868d75effSDimitry Andric #include "lsan.h" 1968d75effSDimitry Andric #include "lsan_allocator.h" 2068d75effSDimitry Andric #include "lsan_thread.h" 2168d75effSDimitry Andric 2268d75effSDimitry Andric #include <pthread.h> 2368d75effSDimitry Andric 2468d75effSDimitry Andric namespace __lsan { 2568d75effSDimitry Andric // Support for the following functions from libdispatch on Mac OS: 2668d75effSDimitry Andric // dispatch_async_f() 2768d75effSDimitry Andric // dispatch_async() 2868d75effSDimitry Andric // dispatch_sync_f() 2968d75effSDimitry Andric // dispatch_sync() 3068d75effSDimitry Andric // dispatch_after_f() 3168d75effSDimitry Andric // dispatch_after() 3268d75effSDimitry Andric // dispatch_group_async_f() 3368d75effSDimitry Andric // dispatch_group_async() 3468d75effSDimitry Andric // TODO(glider): libdispatch API contains other functions that we don't support 3568d75effSDimitry Andric // yet. 3668d75effSDimitry Andric // 3768d75effSDimitry Andric // dispatch_sync() and dispatch_sync_f() are synchronous, although chances are 3868d75effSDimitry Andric // they can cause jobs to run on a thread different from the current one. 3968d75effSDimitry Andric // TODO(glider): if so, we need a test for this (otherwise we should remove 4068d75effSDimitry Andric // them). 4168d75effSDimitry Andric // 4268d75effSDimitry Andric // The following functions use dispatch_barrier_async_f() (which isn't a library 4368d75effSDimitry Andric // function but is exported) and are thus supported: 4468d75effSDimitry Andric // dispatch_source_set_cancel_handler_f() 4568d75effSDimitry Andric // dispatch_source_set_cancel_handler() 4668d75effSDimitry Andric // dispatch_source_set_event_handler_f() 4768d75effSDimitry Andric // dispatch_source_set_event_handler() 4868d75effSDimitry Andric // 4968d75effSDimitry Andric // The reference manual for Grand Central Dispatch is available at 5068d75effSDimitry Andric // http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html 5168d75effSDimitry Andric // The implementation details are at 5268d75effSDimitry Andric // http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c 5368d75effSDimitry Andric 5468d75effSDimitry Andric typedef void *dispatch_group_t; 5568d75effSDimitry Andric typedef void *dispatch_queue_t; 5668d75effSDimitry Andric typedef void *dispatch_source_t; 5768d75effSDimitry Andric typedef u64 dispatch_time_t; 5868d75effSDimitry Andric typedef void (*dispatch_function_t)(void *block); 5968d75effSDimitry Andric typedef void *(*worker_t)(void *block); 6068d75effSDimitry Andric 6168d75effSDimitry Andric // A wrapper for the ObjC blocks used to support libdispatch. 6268d75effSDimitry Andric typedef struct { 6368d75effSDimitry Andric void *block; 6468d75effSDimitry Andric dispatch_function_t func; 6568d75effSDimitry Andric u32 parent_tid; 6668d75effSDimitry Andric } lsan_block_context_t; 6768d75effSDimitry Andric 6868d75effSDimitry Andric ALWAYS_INLINE 6968d75effSDimitry Andric void lsan_register_worker_thread(int parent_tid) { 7068d75effSDimitry Andric if (GetCurrentThread() == kInvalidTid) { 71349cc55cSDimitry Andric u32 tid = ThreadCreate(parent_tid, true); 7268d75effSDimitry Andric ThreadStart(tid, GetTid()); 7368d75effSDimitry Andric SetCurrentThread(tid); 7468d75effSDimitry Andric } 7568d75effSDimitry Andric } 7668d75effSDimitry Andric 7768d75effSDimitry Andric // For use by only those functions that allocated the context via 7868d75effSDimitry Andric // alloc_lsan_context(). 7968d75effSDimitry Andric extern "C" void lsan_dispatch_call_block_and_release(void *block) { 8068d75effSDimitry Andric lsan_block_context_t *context = (lsan_block_context_t *)block; 8168d75effSDimitry Andric VReport(2, 8268d75effSDimitry Andric "lsan_dispatch_call_block_and_release(): " 8368d75effSDimitry Andric "context: %p, pthread_self: %p\n", 8468d75effSDimitry Andric block, pthread_self()); 8568d75effSDimitry Andric lsan_register_worker_thread(context->parent_tid); 8668d75effSDimitry Andric // Call the original dispatcher for the block. 8768d75effSDimitry Andric context->func(context->block); 8868d75effSDimitry Andric lsan_free(context); 8968d75effSDimitry Andric } 9068d75effSDimitry Andric 9168d75effSDimitry Andric } // namespace __lsan 9268d75effSDimitry Andric 9368d75effSDimitry Andric using namespace __lsan; 9468d75effSDimitry Andric 9568d75effSDimitry Andric // Wrap |ctxt| and |func| into an lsan_block_context_t. 9668d75effSDimitry Andric // The caller retains control of the allocated context. 9768d75effSDimitry Andric extern "C" lsan_block_context_t *alloc_lsan_context(void *ctxt, 9868d75effSDimitry Andric dispatch_function_t func) { 9968d75effSDimitry Andric GET_STACK_TRACE_THREAD; 10068d75effSDimitry Andric lsan_block_context_t *lsan_ctxt = 10168d75effSDimitry Andric (lsan_block_context_t *)lsan_malloc(sizeof(lsan_block_context_t), stack); 10268d75effSDimitry Andric lsan_ctxt->block = ctxt; 10368d75effSDimitry Andric lsan_ctxt->func = func; 10468d75effSDimitry Andric lsan_ctxt->parent_tid = GetCurrentThread(); 10568d75effSDimitry Andric return lsan_ctxt; 10668d75effSDimitry Andric } 10768d75effSDimitry Andric 10868d75effSDimitry Andric // Define interceptor for dispatch_*_f function with the three most common 10968d75effSDimitry Andric // parameters: dispatch_queue_t, context, dispatch_function_t. 11068d75effSDimitry Andric #define INTERCEPT_DISPATCH_X_F_3(dispatch_x_f) \ 11168d75effSDimitry Andric INTERCEPTOR(void, dispatch_x_f, dispatch_queue_t dq, void *ctxt, \ 11268d75effSDimitry Andric dispatch_function_t func) { \ 11368d75effSDimitry Andric lsan_block_context_t *lsan_ctxt = alloc_lsan_context(ctxt, func); \ 11468d75effSDimitry Andric return REAL(dispatch_x_f)(dq, (void *)lsan_ctxt, \ 11568d75effSDimitry Andric lsan_dispatch_call_block_and_release); \ 11668d75effSDimitry Andric } 11768d75effSDimitry Andric 11868d75effSDimitry Andric INTERCEPT_DISPATCH_X_F_3(dispatch_async_f) 11968d75effSDimitry Andric INTERCEPT_DISPATCH_X_F_3(dispatch_sync_f) 12068d75effSDimitry Andric INTERCEPT_DISPATCH_X_F_3(dispatch_barrier_async_f) 12168d75effSDimitry Andric 12268d75effSDimitry Andric INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when, dispatch_queue_t dq, 12368d75effSDimitry Andric void *ctxt, dispatch_function_t func) { 12468d75effSDimitry Andric lsan_block_context_t *lsan_ctxt = alloc_lsan_context(ctxt, func); 12568d75effSDimitry Andric return REAL(dispatch_after_f)(when, dq, (void *)lsan_ctxt, 12668d75effSDimitry Andric lsan_dispatch_call_block_and_release); 12768d75effSDimitry Andric } 12868d75effSDimitry Andric 12968d75effSDimitry Andric INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group, 13068d75effSDimitry Andric dispatch_queue_t dq, void *ctxt, dispatch_function_t func) { 13168d75effSDimitry Andric lsan_block_context_t *lsan_ctxt = alloc_lsan_context(ctxt, func); 13268d75effSDimitry Andric REAL(dispatch_group_async_f) 13368d75effSDimitry Andric (group, dq, (void *)lsan_ctxt, lsan_dispatch_call_block_and_release); 13468d75effSDimitry Andric } 13568d75effSDimitry Andric 13668d75effSDimitry Andric #if !defined(MISSING_BLOCKS_SUPPORT) 13768d75effSDimitry Andric extern "C" { 13868d75effSDimitry Andric void dispatch_async(dispatch_queue_t dq, void (^work)(void)); 13968d75effSDimitry Andric void dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq, 14068d75effSDimitry Andric void (^work)(void)); 14168d75effSDimitry Andric void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, 14268d75effSDimitry Andric void (^work)(void)); 14368d75effSDimitry Andric void dispatch_source_set_cancel_handler(dispatch_source_t ds, 14468d75effSDimitry Andric void (^work)(void)); 14568d75effSDimitry Andric void dispatch_source_set_event_handler(dispatch_source_t ds, 14668d75effSDimitry Andric void (^work)(void)); 14768d75effSDimitry Andric } 14868d75effSDimitry Andric 14968d75effSDimitry Andric #define GET_LSAN_BLOCK(work) \ 15068d75effSDimitry Andric void (^lsan_block)(void); \ 15168d75effSDimitry Andric int parent_tid = GetCurrentThread(); \ 15268d75effSDimitry Andric lsan_block = ^(void) { \ 15368d75effSDimitry Andric lsan_register_worker_thread(parent_tid); \ 15468d75effSDimitry Andric work(); \ 15568d75effSDimitry Andric } 15668d75effSDimitry Andric 15768d75effSDimitry Andric INTERCEPTOR(void, dispatch_async, dispatch_queue_t dq, void (^work)(void)) { 15868d75effSDimitry Andric GET_LSAN_BLOCK(work); 15968d75effSDimitry Andric REAL(dispatch_async)(dq, lsan_block); 16068d75effSDimitry Andric } 16168d75effSDimitry Andric 16268d75effSDimitry Andric INTERCEPTOR(void, dispatch_group_async, dispatch_group_t dg, 16368d75effSDimitry Andric dispatch_queue_t dq, void (^work)(void)) { 16468d75effSDimitry Andric GET_LSAN_BLOCK(work); 16568d75effSDimitry Andric REAL(dispatch_group_async)(dg, dq, lsan_block); 16668d75effSDimitry Andric } 16768d75effSDimitry Andric 16868d75effSDimitry Andric INTERCEPTOR(void, dispatch_after, dispatch_time_t when, dispatch_queue_t queue, 16968d75effSDimitry Andric void (^work)(void)) { 17068d75effSDimitry Andric GET_LSAN_BLOCK(work); 17168d75effSDimitry Andric REAL(dispatch_after)(when, queue, lsan_block); 17268d75effSDimitry Andric } 17368d75effSDimitry Andric 17468d75effSDimitry Andric INTERCEPTOR(void, dispatch_source_set_cancel_handler, dispatch_source_t ds, 17568d75effSDimitry Andric void (^work)(void)) { 17668d75effSDimitry Andric if (!work) { 17768d75effSDimitry Andric REAL(dispatch_source_set_cancel_handler)(ds, work); 17868d75effSDimitry Andric return; 17968d75effSDimitry Andric } 18068d75effSDimitry Andric GET_LSAN_BLOCK(work); 18168d75effSDimitry Andric REAL(dispatch_source_set_cancel_handler)(ds, lsan_block); 18268d75effSDimitry Andric } 18368d75effSDimitry Andric 18468d75effSDimitry Andric INTERCEPTOR(void, dispatch_source_set_event_handler, dispatch_source_t ds, 18568d75effSDimitry Andric void (^work)(void)) { 18668d75effSDimitry Andric GET_LSAN_BLOCK(work); 18768d75effSDimitry Andric REAL(dispatch_source_set_event_handler)(ds, lsan_block); 18868d75effSDimitry Andric } 18968d75effSDimitry Andric #endif 19068d75effSDimitry Andric 191*81ad6265SDimitry Andric #endif // SANITIZER_APPLE 192