X-Git-Url: https://git.saurik.com/apple/libdispatch.git/blobdiff_plain/fe4ce08b25268ab7a007bb9eb2dd578ca5a9d5e8..517da941284910bcce6aed25a1e923708f0ed33f:/src/introspection.c?ds=inline diff --git a/src/introspection.c b/src/introspection.c new file mode 100644 index 0000000..5338f25 --- /dev/null +++ b/src/introspection.c @@ -0,0 +1,595 @@ +/* + * Copyright (c) 2012-2013 Apple Inc. All rights reserved. + * + * @APPLE_APACHE_LICENSE_HEADER_START@ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ + */ + +// Contains introspection routines that only exist in the version of the +// library with introspection support + +#if DISPATCH_INTROSPECTION + +#include "internal.h" +#include "introspection.h" +#include "introspection_private.h" + +typedef struct dispatch_introspection_thread_s { + void *dit_isa; + TAILQ_ENTRY(dispatch_introspection_thread_s) dit_list; + pthread_t thread; + dispatch_queue_t *queue; +} dispatch_introspection_thread_s; +typedef struct dispatch_introspection_thread_s *dispatch_introspection_thread_t; + +static TAILQ_HEAD(, dispatch_introspection_thread_s) + _dispatch_introspection_threads = + TAILQ_HEAD_INITIALIZER(_dispatch_introspection_threads); +static volatile OSSpinLock _dispatch_introspection_threads_lock; + +static void _dispatch_introspection_thread_remove(void *ctxt); + +static TAILQ_HEAD(, dispatch_queue_s) _dispatch_introspection_queues = + TAILQ_HEAD_INITIALIZER(_dispatch_introspection_queues); +static volatile OSSpinLock _dispatch_introspection_queues_lock; + +static ptrdiff_t _dispatch_introspection_thread_queue_offset; + +#pragma mark - +#pragma mark dispatch_introspection_init + +void +_dispatch_introspection_init(void) +{ + TAILQ_INSERT_TAIL(&_dispatch_introspection_queues, + &_dispatch_main_q, diq_list); + TAILQ_INSERT_TAIL(&_dispatch_introspection_queues, + &_dispatch_mgr_q, diq_list); +#if DISPATCH_ENABLE_PTHREAD_ROOT_QUEUES + TAILQ_INSERT_TAIL(&_dispatch_introspection_queues, + _dispatch_mgr_q.do_targetq, diq_list); +#endif + for (size_t i = 0; i < DISPATCH_ROOT_QUEUE_COUNT; i++) { + TAILQ_INSERT_TAIL(&_dispatch_introspection_queues, + &_dispatch_root_queues[i], diq_list); + } + + // Hack to determine queue TSD offset from start of pthread structure + uintptr_t thread = _dispatch_thread_self(); + thread_identifier_info_data_t tiid; + mach_msg_type_number_t cnt = THREAD_IDENTIFIER_INFO_COUNT; + kern_return_t kr = thread_info(pthread_mach_thread_np((void*)thread), + THREAD_IDENTIFIER_INFO, (thread_info_t)&tiid, &cnt); + if (!dispatch_assume_zero(kr)) { + _dispatch_introspection_thread_queue_offset = + (void*)(uintptr_t)tiid.dispatch_qaddr - (void*)thread; + } + _dispatch_thread_key_create(&dispatch_introspection_key, + _dispatch_introspection_thread_remove); + _dispatch_introspection_thread_add(); // add main thread +} + +const struct dispatch_introspection_versions_s +dispatch_introspection_versions = { + .introspection_version = 1, + .hooks_version = 1, + .hooks_size = sizeof(dispatch_introspection_hooks_s), + .queue_item_version = 1, + .queue_item_size = sizeof(dispatch_introspection_queue_item_s), + .queue_block_version = 1, + .queue_block_size = sizeof(dispatch_introspection_queue_block_s), + .queue_function_version = 1, + .queue_function_size = sizeof(dispatch_introspection_queue_function_s), + .queue_thread_version = 1, + .queue_thread_size = sizeof(dispatch_introspection_queue_thread_s), + .object_version = 1, + .object_size = sizeof(dispatch_introspection_object_s), + .queue_version = 1, + .queue_size = sizeof(dispatch_introspection_queue_s), + .source_version = 1, + .source_size = sizeof(dispatch_introspection_source_s), +}; + +#pragma mark - +#pragma mark dispatch_introspection_threads + +void +_dispatch_introspection_thread_add(void) +{ + if (_dispatch_thread_getspecific(dispatch_introspection_key)) { + return; + } + uintptr_t thread = _dispatch_thread_self(); + dispatch_introspection_thread_t dit = (void*)_dispatch_continuation_alloc(); + dit->dit_isa = (void*)0x41; + dit->thread = (void*)thread; + dit->queue = !_dispatch_introspection_thread_queue_offset ? NULL : + (void*)thread + _dispatch_introspection_thread_queue_offset; + _dispatch_thread_setspecific(dispatch_introspection_key, dit); + OSSpinLockLock(&_dispatch_introspection_threads_lock); + TAILQ_INSERT_TAIL(&_dispatch_introspection_threads, dit, dit_list); + OSSpinLockUnlock(&_dispatch_introspection_threads_lock); +} + +static void +_dispatch_introspection_thread_remove(void *ctxt) +{ + dispatch_introspection_thread_t dit = ctxt; + OSSpinLockLock(&_dispatch_introspection_threads_lock); + TAILQ_REMOVE(&_dispatch_introspection_threads, dit, dit_list); + OSSpinLockUnlock(&_dispatch_introspection_threads_lock); + _dispatch_continuation_free((void*)dit); + _dispatch_thread_setspecific(dispatch_introspection_key, NULL); +} + +#pragma mark - +#pragma mark dispatch_introspection_info + +static inline +dispatch_introspection_queue_function_s +_dispatch_introspection_continuation_get_info(dispatch_queue_t dq, + dispatch_continuation_t dc, unsigned long *type) +{ + void *ctxt = dc->dc_ctxt; + dispatch_function_t func = dc->dc_func; + pthread_t waiter = NULL; + bool apply = false; + long flags = (long)dc->do_vtable; + if (flags & DISPATCH_OBJ_SYNC_SLOW_BIT) { + waiter = dc->dc_data; + if (flags & DISPATCH_OBJ_BARRIER_BIT) { + dc = dc->dc_ctxt; + dq = dc->dc_data; + } + ctxt = dc->dc_ctxt; + func = dc->dc_func; + } + if (func == _dispatch_sync_recurse_invoke) { + dc = dc->dc_ctxt; + dq = dc->dc_data; + ctxt = dc->dc_ctxt; + func = dc->dc_func; + } else if (func == _dispatch_async_redirect_invoke) { + dq = dc->dc_data; + dc = dc->dc_other; + ctxt = dc->dc_ctxt; + func = dc->dc_func; + flags = (long)dc->do_vtable; + } else if (func == _dispatch_mach_barrier_invoke) { + dq = dq->do_targetq; + ctxt = dc->dc_data; + func = dc->dc_other; + } else if (func == _dispatch_apply_invoke || + func == _dispatch_apply_redirect_invoke) { + dispatch_apply_t da = ctxt; + if (da->da_todo) { + dc = da->da_dc; + if (func == _dispatch_apply_redirect_invoke) { + dq = dc->dc_data; + } + ctxt = dc->dc_ctxt; + func = dc->dc_func; + apply = true; + } + } + if (func == _dispatch_call_block_and_release) { + *type = dispatch_introspection_queue_item_type_block; + func = _dispatch_Block_invoke(ctxt); + } else { + *type = dispatch_introspection_queue_item_type_function; + } + dispatch_introspection_queue_function_s diqf= { + .continuation = dc, + .target_queue = dq, + .context = ctxt, + .function = func, + .group = flags & DISPATCH_OBJ_GROUP_BIT ? dc->dc_data : NULL, + .waiter = waiter, + .barrier = flags & DISPATCH_OBJ_BARRIER_BIT, + .sync = flags & DISPATCH_OBJ_SYNC_SLOW_BIT, + .apply = apply, + }; + return diqf; +} + +static inline +dispatch_introspection_object_s +_dispatch_introspection_object_get_info(dispatch_object_t dou) +{ + dispatch_introspection_object_s dio = { + .object = dou._dc, + .target_queue = dou._do->do_targetq, + .type = (void*)dou._do->do_vtable, + .kind = dx_kind(dou._do), + }; + return dio; +} + +DISPATCH_USED inline +dispatch_introspection_queue_s +dispatch_introspection_queue_get_info(dispatch_queue_t dq) +{ + bool global = (dq->do_xref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT) || + (dq->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT); + uint32_t width = dq->dq_width; + if (width > 1 && width != UINT32_MAX) width /= 2; + dispatch_introspection_queue_s diq = { + .queue = dq, + .target_queue = dq->do_targetq, + .label = dq->dq_label, + .serialnum = dq->dq_serialnum, + .width = width, + .suspend_count = dq->do_suspend_cnt / 2, + .enqueued = (dq->do_suspend_cnt & 1) && !global, + .barrier = (dq->dq_running & 1) && !global, + .draining = (dq->dq_items_head == (void*)~0ul) || + (!dq->dq_items_head && dq->dq_items_tail), + .global = global, + .main = (dq == &_dispatch_main_q), + }; + return diq; +} + +static inline +dispatch_introspection_source_s +_dispatch_introspection_source_get_info(dispatch_source_t ds) +{ + dispatch_source_refs_t dr = ds->ds_refs; + void *ctxt = dr->ds_handler_ctxt; + dispatch_function_t handler = dr->ds_handler_func; + bool handler_is_block = ds->ds_handler_is_block; + bool after = (handler == _dispatch_after_timer_callback); + if (after) { + dispatch_continuation_t dc = ctxt; + ctxt = dc->dc_ctxt; + handler = dc->dc_func; + if (handler == _dispatch_call_block_and_release) { + handler = _dispatch_Block_invoke(ctxt); + handler_is_block = 1; + } + } + dispatch_introspection_source_s dis = { + .source = ds, + .target_queue = ds->do_targetq, + .type = ds->ds_dkev ? (unsigned long)ds->ds_dkev->dk_kevent.filter : 0, + .handle = ds->ds_dkev ? (unsigned long)ds->ds_dkev->dk_kevent.ident : 0, + .context = ctxt, + .handler = handler, + .suspend_count = ds->do_suspend_cnt / 2, + .enqueued = (ds->do_suspend_cnt & 1), + .handler_is_block = handler_is_block, + .timer = ds->ds_is_timer, + .after = after, + }; + return dis; +} + +static inline +dispatch_introspection_queue_thread_s +_dispatch_introspection_thread_get_info(dispatch_introspection_thread_t dit) +{ + dispatch_introspection_queue_thread_s diqt = { + .object = (void*)dit, + .thread = dit->thread, + }; + if (dit->queue && *dit->queue) { + diqt.queue = dispatch_introspection_queue_get_info(*dit->queue); + } + return diqt; +} + +DISPATCH_USED inline +dispatch_introspection_queue_item_s +dispatch_introspection_queue_item_get_info(dispatch_queue_t dq, + dispatch_continuation_t dc) +{ + dispatch_introspection_queue_item_s diqi; + if (DISPATCH_OBJ_IS_VTABLE(dc)) { + dispatch_object_t dou = (dispatch_object_t)dc; + unsigned long type = dx_type(dou._do); + unsigned long metatype = type & _DISPATCH_META_TYPE_MASK; + if (metatype == _DISPATCH_QUEUE_TYPE && + type != DISPATCH_QUEUE_SPECIFIC_TYPE) { + diqi.type = dispatch_introspection_queue_item_type_queue; + diqi.queue = dispatch_introspection_queue_get_info(dou._dq); + } else if (metatype == _DISPATCH_SOURCE_TYPE) { + diqi.type = dispatch_introspection_queue_item_type_source; + diqi.source = _dispatch_introspection_source_get_info(dou._ds); + } else { + diqi.type = dispatch_introspection_queue_item_type_object; + diqi.object = _dispatch_introspection_object_get_info(dou._do); + } + } else { + diqi.function = _dispatch_introspection_continuation_get_info(dq, dc, + &diqi.type); + } + return diqi; +} + +#pragma mark - +#pragma mark dispatch_introspection_iterators + +DISPATCH_USED +dispatch_queue_t +dispatch_introspection_get_queues(dispatch_queue_t start, size_t count, + dispatch_introspection_queue_t queues) +{ + dispatch_queue_t next; + next = start ? start : TAILQ_FIRST(&_dispatch_introspection_queues); + while (count--) { + if (!next) { + queues->queue = NULL; + break; + } + *queues++ = dispatch_introspection_queue_get_info(next); + next = TAILQ_NEXT(next, diq_list); + } + return next; +} + +DISPATCH_USED +dispatch_continuation_t +dispatch_introspection_get_queue_threads(dispatch_continuation_t start, + size_t count, dispatch_introspection_queue_thread_t threads) +{ + dispatch_introspection_thread_t next = start ? (void*)start : + TAILQ_FIRST(&_dispatch_introspection_threads); + while (count--) { + if (!next) { + threads->object = NULL; + break; + } + *threads++ = _dispatch_introspection_thread_get_info(next); + next = TAILQ_NEXT(next, dit_list); + } + return (void*)next; +} + +DISPATCH_USED +dispatch_continuation_t +dispatch_introspection_queue_get_items(dispatch_queue_t dq, + dispatch_continuation_t start, size_t count, + dispatch_introspection_queue_item_t items) +{ + dispatch_continuation_t next = start ? start : + dq->dq_items_head == (void*)~0ul ? NULL : (void*)dq->dq_items_head; + while (count--) { + if (!next) { + items->type = dispatch_introspection_queue_item_type_none; + break; + } + *items++ = dispatch_introspection_queue_item_get_info(dq, next); + next = next->do_next; + } + return next; +} + +#pragma mark - +#pragma mark dispatch_introspection_hooks + +#define DISPATCH_INTROSPECTION_NO_HOOK ((void*)~0ul) + +dispatch_introspection_hooks_s _dispatch_introspection_hooks; +dispatch_introspection_hooks_s _dispatch_introspection_hook_callouts; +static const +dispatch_introspection_hooks_s _dispatch_introspection_hook_callouts_enabled = { + .queue_create = DISPATCH_INTROSPECTION_NO_HOOK, + .queue_dispose = DISPATCH_INTROSPECTION_NO_HOOK, + .queue_item_enqueue = DISPATCH_INTROSPECTION_NO_HOOK, + .queue_item_dequeue = DISPATCH_INTROSPECTION_NO_HOOK, +}; + +#define DISPATCH_INTROSPECTION_HOOKS_COUNT (( \ + sizeof(_dispatch_introspection_hook_callouts_enabled) - \ + sizeof(_dispatch_introspection_hook_callouts_enabled._reserved)) / \ + sizeof(dispatch_function_t)) + +#define DISPATCH_INTROSPECTION_HOOK_ENABLED(h) \ + (slowpath(_dispatch_introspection_hooks.h)) + +#define DISPATCH_INTROSPECTION_HOOK_CALLOUT(h, ...) ({ \ + typeof(_dispatch_introspection_hooks.h) _h; \ + _h = _dispatch_introspection_hooks.h; \ + if (slowpath((void*)(_h) != DISPATCH_INTROSPECTION_NO_HOOK)) { \ + _h(__VA_ARGS__); \ + } }) + +#define DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(h) \ + DISPATCH_EXPORT void _dispatch_introspection_hook_##h(void) \ + asm("_dispatch_introspection_hook_" #h); \ + void _dispatch_introspection_hook_##h(void) {} + +#define DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(h, ...)\ + dispatch_introspection_hook_##h(__VA_ARGS__) + +DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_create); +DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_destroy); +DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_item_enqueue); +DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_item_dequeue); +DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_callout_begin); +DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_callout_end); + +DISPATCH_USED +void +dispatch_introspection_hooks_install(dispatch_introspection_hooks_t hooks) +{ + dispatch_introspection_hooks_s old_hooks = _dispatch_introspection_hooks; + _dispatch_introspection_hooks = *hooks; + dispatch_function_t *e = (void*)&_dispatch_introspection_hook_callouts, + *h = (void*)&_dispatch_introspection_hooks, *oh = (void*)&old_hooks; + for (size_t i = 0; i < DISPATCH_INTROSPECTION_HOOKS_COUNT; i++) { + if (!h[i] && e[i]) { + h[i] = DISPATCH_INTROSPECTION_NO_HOOK; + } + if (oh[i] == DISPATCH_INTROSPECTION_NO_HOOK) { + oh[i] = NULL; + } + } + *hooks = old_hooks; +} + +DISPATCH_USED +void +dispatch_introspection_hook_callouts_enable( + dispatch_introspection_hooks_t enable) +{ + _dispatch_introspection_hook_callouts = enable ? *enable : + _dispatch_introspection_hook_callouts_enabled; + dispatch_function_t *e = (void*)&_dispatch_introspection_hook_callouts, + *h = (void*)&_dispatch_introspection_hooks; + for (size_t i = 0; i < DISPATCH_INTROSPECTION_HOOKS_COUNT; i++) { + if (e[i] && !h[i]) { + h[i] = DISPATCH_INTROSPECTION_NO_HOOK; + } else if (!e[i] && h[i] == DISPATCH_INTROSPECTION_NO_HOOK) { + h[i] = NULL; + } + } +} + +DISPATCH_NOINLINE +void +dispatch_introspection_hook_callout_queue_create( + dispatch_introspection_queue_t queue_info) +{ + DISPATCH_INTROSPECTION_HOOK_CALLOUT(queue_create, queue_info); +} + +DISPATCH_NOINLINE +static void +_dispatch_introspection_queue_create_hook(dispatch_queue_t dq) +{ + dispatch_introspection_queue_s diq; + diq = dispatch_introspection_queue_get_info(dq); + dispatch_introspection_hook_callout_queue_create(&diq); +} + +dispatch_queue_t +_dispatch_introspection_queue_create(dispatch_queue_t dq) +{ + OSSpinLockLock(&_dispatch_introspection_queues_lock); + TAILQ_INSERT_TAIL(&_dispatch_introspection_queues, dq, diq_list); + OSSpinLockUnlock(&_dispatch_introspection_queues_lock); + + DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(queue_create, dq); + if (DISPATCH_INTROSPECTION_HOOK_ENABLED(queue_create)) { + _dispatch_introspection_queue_create_hook(dq); + } + return dq; +} + +DISPATCH_NOINLINE +void +dispatch_introspection_hook_callout_queue_dispose( + dispatch_introspection_queue_t queue_info) +{ + DISPATCH_INTROSPECTION_HOOK_CALLOUT(queue_dispose, queue_info); +} + +DISPATCH_NOINLINE +static void +_dispatch_introspection_queue_dispose_hook(dispatch_queue_t dq) +{ + dispatch_introspection_queue_s diq; + diq = dispatch_introspection_queue_get_info(dq); + dispatch_introspection_hook_callout_queue_dispose(&diq); +} + +void +_dispatch_introspection_queue_dispose(dispatch_queue_t dq) +{ + DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(queue_destroy, dq); + if (DISPATCH_INTROSPECTION_HOOK_ENABLED(queue_dispose)) { + _dispatch_introspection_queue_dispose_hook(dq); + } + + OSSpinLockLock(&_dispatch_introspection_queues_lock); + TAILQ_REMOVE(&_dispatch_introspection_queues, dq, diq_list); + OSSpinLockUnlock(&_dispatch_introspection_queues_lock); +} + +DISPATCH_NOINLINE +void +dispatch_introspection_hook_callout_queue_item_enqueue(dispatch_queue_t queue, + dispatch_introspection_queue_item_t item) +{ + DISPATCH_INTROSPECTION_HOOK_CALLOUT(queue_item_enqueue, queue, item); +} + +DISPATCH_NOINLINE +static void +_dispatch_introspection_queue_item_enqueue_hook(dispatch_queue_t dq, + dispatch_object_t dou) +{ + dispatch_introspection_queue_item_s diqi; + diqi = dispatch_introspection_queue_item_get_info(dq, dou._dc); + dispatch_introspection_hook_callout_queue_item_enqueue(dq, &diqi); +} + +void +_dispatch_introspection_queue_item_enqueue(dispatch_queue_t dq, + dispatch_object_t dou) +{ + DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT( + queue_item_enqueue, dq, dou); + if (DISPATCH_INTROSPECTION_HOOK_ENABLED(queue_item_enqueue)) { + _dispatch_introspection_queue_item_enqueue_hook(dq, dou); + } +} + +DISPATCH_NOINLINE +void +dispatch_introspection_hook_callout_queue_item_dequeue(dispatch_queue_t queue, + dispatch_introspection_queue_item_t item) +{ + DISPATCH_INTROSPECTION_HOOK_CALLOUT(queue_item_dequeue, queue, item); +} + +DISPATCH_NOINLINE +static void +_dispatch_introspection_queue_item_dequeue_hook(dispatch_queue_t dq, + dispatch_object_t dou) +{ + dispatch_introspection_queue_item_s diqi; + diqi = dispatch_introspection_queue_item_get_info(dq, dou._dc); + dispatch_introspection_hook_callout_queue_item_enqueue(dq, &diqi); +} + +void +_dispatch_introspection_queue_item_dequeue(dispatch_queue_t dq, + dispatch_object_t dou) +{ + DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT( + queue_item_dequeue, dq, dou); + if (DISPATCH_INTROSPECTION_HOOK_ENABLED(queue_item_dequeue)) { + _dispatch_introspection_queue_item_dequeue_hook(dq, dou); + } +} + +void +_dispatch_introspection_callout_entry(void *ctxt, dispatch_function_t f) { + dispatch_queue_t dq = _dispatch_queue_get_current(); + DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT( + queue_callout_begin, dq, ctxt, f); +} + +void +_dispatch_introspection_callout_return(void *ctxt, dispatch_function_t f) { + dispatch_queue_t dq = _dispatch_queue_get_current(); + DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT( + queue_callout_end, dq, ctxt, f); +} + +#endif // DISPATCH_INTROSPECTION