xref: /openbsd-src/gnu/llvm/compiler-rt/lib/lsan/lsan_mac.cpp (revision 810390e339a5425391477d5d41c78d7cab2424ac)
13cab2bb3Spatrick //===-- lsan_mac.cpp ------------------------------------------------------===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick //
93cab2bb3Spatrick // This file is a part of LeakSanitizer, a memory leak checker.
103cab2bb3Spatrick //
113cab2bb3Spatrick // Mac-specific details.
123cab2bb3Spatrick //===----------------------------------------------------------------------===//
133cab2bb3Spatrick 
143cab2bb3Spatrick #include "sanitizer_common/sanitizer_platform.h"
15*810390e3Srobert #if SANITIZER_APPLE
163cab2bb3Spatrick 
173cab2bb3Spatrick #include "interception/interception.h"
183cab2bb3Spatrick #include "lsan.h"
193cab2bb3Spatrick #include "lsan_allocator.h"
203cab2bb3Spatrick #include "lsan_thread.h"
213cab2bb3Spatrick 
223cab2bb3Spatrick #include <pthread.h>
233cab2bb3Spatrick 
243cab2bb3Spatrick namespace __lsan {
253cab2bb3Spatrick // Support for the following functions from libdispatch on Mac OS:
263cab2bb3Spatrick //   dispatch_async_f()
273cab2bb3Spatrick //   dispatch_async()
283cab2bb3Spatrick //   dispatch_sync_f()
293cab2bb3Spatrick //   dispatch_sync()
303cab2bb3Spatrick //   dispatch_after_f()
313cab2bb3Spatrick //   dispatch_after()
323cab2bb3Spatrick //   dispatch_group_async_f()
333cab2bb3Spatrick //   dispatch_group_async()
343cab2bb3Spatrick // TODO(glider): libdispatch API contains other functions that we don't support
353cab2bb3Spatrick // yet.
363cab2bb3Spatrick //
373cab2bb3Spatrick // dispatch_sync() and dispatch_sync_f() are synchronous, although chances are
383cab2bb3Spatrick // they can cause jobs to run on a thread different from the current one.
393cab2bb3Spatrick // TODO(glider): if so, we need a test for this (otherwise we should remove
403cab2bb3Spatrick // them).
413cab2bb3Spatrick //
423cab2bb3Spatrick // The following functions use dispatch_barrier_async_f() (which isn't a library
433cab2bb3Spatrick // function but is exported) and are thus supported:
443cab2bb3Spatrick //   dispatch_source_set_cancel_handler_f()
453cab2bb3Spatrick //   dispatch_source_set_cancel_handler()
463cab2bb3Spatrick //   dispatch_source_set_event_handler_f()
473cab2bb3Spatrick //   dispatch_source_set_event_handler()
483cab2bb3Spatrick //
493cab2bb3Spatrick // The reference manual for Grand Central Dispatch is available at
503cab2bb3Spatrick //   http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
513cab2bb3Spatrick // The implementation details are at
523cab2bb3Spatrick //   http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c
533cab2bb3Spatrick 
543cab2bb3Spatrick typedef void *dispatch_group_t;
553cab2bb3Spatrick typedef void *dispatch_queue_t;
563cab2bb3Spatrick typedef void *dispatch_source_t;
573cab2bb3Spatrick typedef u64 dispatch_time_t;
583cab2bb3Spatrick typedef void (*dispatch_function_t)(void *block);
593cab2bb3Spatrick typedef void *(*worker_t)(void *block);
603cab2bb3Spatrick 
613cab2bb3Spatrick // A wrapper for the ObjC blocks used to support libdispatch.
623cab2bb3Spatrick typedef struct {
633cab2bb3Spatrick   void *block;
643cab2bb3Spatrick   dispatch_function_t func;
653cab2bb3Spatrick   u32 parent_tid;
663cab2bb3Spatrick } lsan_block_context_t;
673cab2bb3Spatrick 
683cab2bb3Spatrick ALWAYS_INLINE
lsan_register_worker_thread(int parent_tid)693cab2bb3Spatrick void lsan_register_worker_thread(int parent_tid) {
703cab2bb3Spatrick   if (GetCurrentThread() == kInvalidTid) {
71*810390e3Srobert     u32 tid = ThreadCreate(parent_tid, true);
723cab2bb3Spatrick     ThreadStart(tid, GetTid());
733cab2bb3Spatrick     SetCurrentThread(tid);
743cab2bb3Spatrick   }
753cab2bb3Spatrick }
763cab2bb3Spatrick 
773cab2bb3Spatrick // For use by only those functions that allocated the context via
783cab2bb3Spatrick // alloc_lsan_context().
lsan_dispatch_call_block_and_release(void * block)793cab2bb3Spatrick extern "C" void lsan_dispatch_call_block_and_release(void *block) {
803cab2bb3Spatrick   lsan_block_context_t *context = (lsan_block_context_t *)block;
813cab2bb3Spatrick   VReport(2,
823cab2bb3Spatrick           "lsan_dispatch_call_block_and_release(): "
833cab2bb3Spatrick           "context: %p, pthread_self: %p\n",
843cab2bb3Spatrick           block, pthread_self());
853cab2bb3Spatrick   lsan_register_worker_thread(context->parent_tid);
863cab2bb3Spatrick   // Call the original dispatcher for the block.
873cab2bb3Spatrick   context->func(context->block);
883cab2bb3Spatrick   lsan_free(context);
893cab2bb3Spatrick }
903cab2bb3Spatrick 
913cab2bb3Spatrick }  // namespace __lsan
923cab2bb3Spatrick 
933cab2bb3Spatrick using namespace __lsan;
943cab2bb3Spatrick 
953cab2bb3Spatrick // Wrap |ctxt| and |func| into an lsan_block_context_t.
963cab2bb3Spatrick // The caller retains control of the allocated context.
alloc_lsan_context(void * ctxt,dispatch_function_t func)973cab2bb3Spatrick extern "C" lsan_block_context_t *alloc_lsan_context(void *ctxt,
983cab2bb3Spatrick                                                     dispatch_function_t func) {
993cab2bb3Spatrick   GET_STACK_TRACE_THREAD;
1003cab2bb3Spatrick   lsan_block_context_t *lsan_ctxt =
1013cab2bb3Spatrick       (lsan_block_context_t *)lsan_malloc(sizeof(lsan_block_context_t), stack);
1023cab2bb3Spatrick   lsan_ctxt->block = ctxt;
1033cab2bb3Spatrick   lsan_ctxt->func = func;
1043cab2bb3Spatrick   lsan_ctxt->parent_tid = GetCurrentThread();
1053cab2bb3Spatrick   return lsan_ctxt;
1063cab2bb3Spatrick }
1073cab2bb3Spatrick 
1083cab2bb3Spatrick // Define interceptor for dispatch_*_f function with the three most common
1093cab2bb3Spatrick // parameters: dispatch_queue_t, context, dispatch_function_t.
1103cab2bb3Spatrick #define INTERCEPT_DISPATCH_X_F_3(dispatch_x_f)                        \
1113cab2bb3Spatrick   INTERCEPTOR(void, dispatch_x_f, dispatch_queue_t dq, void *ctxt,    \
1123cab2bb3Spatrick               dispatch_function_t func) {                             \
1133cab2bb3Spatrick     lsan_block_context_t *lsan_ctxt = alloc_lsan_context(ctxt, func); \
1143cab2bb3Spatrick     return REAL(dispatch_x_f)(dq, (void *)lsan_ctxt,                  \
1153cab2bb3Spatrick                               lsan_dispatch_call_block_and_release);  \
1163cab2bb3Spatrick   }
1173cab2bb3Spatrick 
1183cab2bb3Spatrick INTERCEPT_DISPATCH_X_F_3(dispatch_async_f)
INTERCEPT_DISPATCH_X_F_3(dispatch_sync_f)1193cab2bb3Spatrick INTERCEPT_DISPATCH_X_F_3(dispatch_sync_f)
1203cab2bb3Spatrick INTERCEPT_DISPATCH_X_F_3(dispatch_barrier_async_f)
1213cab2bb3Spatrick 
1223cab2bb3Spatrick INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when, dispatch_queue_t dq,
1233cab2bb3Spatrick             void *ctxt, dispatch_function_t func) {
1243cab2bb3Spatrick   lsan_block_context_t *lsan_ctxt = alloc_lsan_context(ctxt, func);
1253cab2bb3Spatrick   return REAL(dispatch_after_f)(when, dq, (void *)lsan_ctxt,
1263cab2bb3Spatrick                                 lsan_dispatch_call_block_and_release);
1273cab2bb3Spatrick }
1283cab2bb3Spatrick 
INTERCEPTOR(void,dispatch_group_async_f,dispatch_group_t group,dispatch_queue_t dq,void * ctxt,dispatch_function_t func)1293cab2bb3Spatrick INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group,
1303cab2bb3Spatrick             dispatch_queue_t dq, void *ctxt, dispatch_function_t func) {
1313cab2bb3Spatrick   lsan_block_context_t *lsan_ctxt = alloc_lsan_context(ctxt, func);
1323cab2bb3Spatrick   REAL(dispatch_group_async_f)
1333cab2bb3Spatrick   (group, dq, (void *)lsan_ctxt, lsan_dispatch_call_block_and_release);
1343cab2bb3Spatrick }
1353cab2bb3Spatrick 
1363cab2bb3Spatrick #if !defined(MISSING_BLOCKS_SUPPORT)
1373cab2bb3Spatrick extern "C" {
1383cab2bb3Spatrick void dispatch_async(dispatch_queue_t dq, void (^work)(void));
1393cab2bb3Spatrick void dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq,
1403cab2bb3Spatrick                           void (^work)(void));
1413cab2bb3Spatrick void dispatch_after(dispatch_time_t when, dispatch_queue_t queue,
1423cab2bb3Spatrick                     void (^work)(void));
1433cab2bb3Spatrick void dispatch_source_set_cancel_handler(dispatch_source_t ds,
1443cab2bb3Spatrick                                         void (^work)(void));
1453cab2bb3Spatrick void dispatch_source_set_event_handler(dispatch_source_t ds,
1463cab2bb3Spatrick                                        void (^work)(void));
1473cab2bb3Spatrick }
1483cab2bb3Spatrick 
1493cab2bb3Spatrick #define GET_LSAN_BLOCK(work)                 \
1503cab2bb3Spatrick   void (^lsan_block)(void);                  \
1513cab2bb3Spatrick   int parent_tid = GetCurrentThread();       \
1523cab2bb3Spatrick   lsan_block = ^(void) {                     \
1533cab2bb3Spatrick     lsan_register_worker_thread(parent_tid); \
1543cab2bb3Spatrick     work();                                  \
1553cab2bb3Spatrick   }
1563cab2bb3Spatrick 
1573cab2bb3Spatrick INTERCEPTOR(void, dispatch_async, dispatch_queue_t dq, void (^work)(void)) {
1583cab2bb3Spatrick   GET_LSAN_BLOCK(work);
1593cab2bb3Spatrick   REAL(dispatch_async)(dq, lsan_block);
1603cab2bb3Spatrick }
1613cab2bb3Spatrick 
1623cab2bb3Spatrick INTERCEPTOR(void, dispatch_group_async, dispatch_group_t dg,
1633cab2bb3Spatrick             dispatch_queue_t dq, void (^work)(void)) {
1643cab2bb3Spatrick   GET_LSAN_BLOCK(work);
1653cab2bb3Spatrick   REAL(dispatch_group_async)(dg, dq, lsan_block);
1663cab2bb3Spatrick }
1673cab2bb3Spatrick 
1683cab2bb3Spatrick INTERCEPTOR(void, dispatch_after, dispatch_time_t when, dispatch_queue_t queue,
1693cab2bb3Spatrick             void (^work)(void)) {
1703cab2bb3Spatrick   GET_LSAN_BLOCK(work);
1713cab2bb3Spatrick   REAL(dispatch_after)(when, queue, lsan_block);
1723cab2bb3Spatrick }
1733cab2bb3Spatrick 
1743cab2bb3Spatrick INTERCEPTOR(void, dispatch_source_set_cancel_handler, dispatch_source_t ds,
1753cab2bb3Spatrick             void (^work)(void)) {
1763cab2bb3Spatrick   if (!work) {
1773cab2bb3Spatrick     REAL(dispatch_source_set_cancel_handler)(ds, work);
1783cab2bb3Spatrick     return;
1793cab2bb3Spatrick   }
1803cab2bb3Spatrick   GET_LSAN_BLOCK(work);
1813cab2bb3Spatrick   REAL(dispatch_source_set_cancel_handler)(ds, lsan_block);
1823cab2bb3Spatrick }
1833cab2bb3Spatrick 
1843cab2bb3Spatrick INTERCEPTOR(void, dispatch_source_set_event_handler, dispatch_source_t ds,
1853cab2bb3Spatrick             void (^work)(void)) {
1863cab2bb3Spatrick   GET_LSAN_BLOCK(work);
1873cab2bb3Spatrick   REAL(dispatch_source_set_event_handler)(ds, lsan_block);
1883cab2bb3Spatrick }
1893cab2bb3Spatrick #endif
1903cab2bb3Spatrick 
191*810390e3Srobert #endif  // SANITIZER_APPLE
192