2 * Copyright (c) 2012-2013 Apple Inc. All rights reserved.
4 * @APPLE_APACHE_LICENSE_HEADER_START@
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 * @APPLE_APACHE_LICENSE_HEADER_END@
21 // Contains introspection routines that only exist in the version of the
22 // library with introspection support
24 #if DISPATCH_INTROSPECTION
27 #include "dispatch/introspection.h"
28 #include "introspection_private.h"
30 typedef struct dispatch_introspection_thread_s
{
32 TAILQ_ENTRY(dispatch_introspection_thread_s
) dit_list
;
34 dispatch_queue_t
*queue
;
35 } dispatch_introspection_thread_s
;
36 typedef struct dispatch_introspection_thread_s
*dispatch_introspection_thread_t
;
38 static TAILQ_HEAD(, dispatch_introspection_thread_s
)
39 _dispatch_introspection_threads
=
40 TAILQ_HEAD_INITIALIZER(_dispatch_introspection_threads
);
41 static volatile OSSpinLock _dispatch_introspection_threads_lock
;
43 static void _dispatch_introspection_thread_remove(void *ctxt
);
45 static TAILQ_HEAD(, dispatch_queue_s
) _dispatch_introspection_queues
=
46 TAILQ_HEAD_INITIALIZER(_dispatch_introspection_queues
);
47 static volatile OSSpinLock _dispatch_introspection_queues_lock
;
49 static ptrdiff_t _dispatch_introspection_thread_queue_offset
;
52 #pragma mark dispatch_introspection_init
55 _dispatch_introspection_init(void)
57 TAILQ_INSERT_TAIL(&_dispatch_introspection_queues
,
58 &_dispatch_main_q
, diq_list
);
59 TAILQ_INSERT_TAIL(&_dispatch_introspection_queues
,
60 &_dispatch_mgr_q
, diq_list
);
61 #if DISPATCH_ENABLE_PTHREAD_ROOT_QUEUES
62 TAILQ_INSERT_TAIL(&_dispatch_introspection_queues
,
63 _dispatch_mgr_q
.do_targetq
, diq_list
);
65 for (size_t i
= 0; i
< DISPATCH_ROOT_QUEUE_COUNT
; i
++) {
66 TAILQ_INSERT_TAIL(&_dispatch_introspection_queues
,
67 &_dispatch_root_queues
[i
], diq_list
);
70 // Hack to determine queue TSD offset from start of pthread structure
71 uintptr_t thread
= _dispatch_thread_self();
72 thread_identifier_info_data_t tiid
;
73 mach_msg_type_number_t cnt
= THREAD_IDENTIFIER_INFO_COUNT
;
74 kern_return_t kr
= thread_info(pthread_mach_thread_np((void*)thread
),
75 THREAD_IDENTIFIER_INFO
, (thread_info_t
)&tiid
, &cnt
);
76 if (!dispatch_assume_zero(kr
)) {
77 _dispatch_introspection_thread_queue_offset
=
78 (void*)(uintptr_t)tiid
.dispatch_qaddr
- (void*)thread
;
80 _dispatch_thread_key_create(&dispatch_introspection_key
,
81 _dispatch_introspection_thread_remove
);
82 _dispatch_introspection_thread_add(); // add main thread
85 const struct dispatch_introspection_versions_s
86 dispatch_introspection_versions
= {
87 .introspection_version
= 1,
89 .hooks_size
= sizeof(dispatch_introspection_hooks_s
),
90 .queue_item_version
= 1,
91 .queue_item_size
= sizeof(dispatch_introspection_queue_item_s
),
92 .queue_block_version
= 1,
93 .queue_block_size
= sizeof(dispatch_introspection_queue_block_s
),
94 .queue_function_version
= 1,
95 .queue_function_size
= sizeof(dispatch_introspection_queue_function_s
),
96 .queue_thread_version
= 1,
97 .queue_thread_size
= sizeof(dispatch_introspection_queue_thread_s
),
99 .object_size
= sizeof(dispatch_introspection_object_s
),
101 .queue_size
= sizeof(dispatch_introspection_queue_s
),
103 .source_size
= sizeof(dispatch_introspection_source_s
),
107 #pragma mark dispatch_introspection_threads
110 _dispatch_introspection_thread_add(void)
112 if (_dispatch_thread_getspecific(dispatch_introspection_key
)) {
115 uintptr_t thread
= _dispatch_thread_self();
116 dispatch_introspection_thread_t dit
= (void*)_dispatch_continuation_alloc();
117 dit
->dit_isa
= (void*)0x41;
118 dit
->thread
= (void*)thread
;
119 dit
->queue
= !_dispatch_introspection_thread_queue_offset
? NULL
:
120 (void*)thread
+ _dispatch_introspection_thread_queue_offset
;
121 _dispatch_thread_setspecific(dispatch_introspection_key
, dit
);
122 OSSpinLockLock(&_dispatch_introspection_threads_lock
);
123 TAILQ_INSERT_TAIL(&_dispatch_introspection_threads
, dit
, dit_list
);
124 OSSpinLockUnlock(&_dispatch_introspection_threads_lock
);
128 _dispatch_introspection_thread_remove(void *ctxt
)
130 dispatch_introspection_thread_t dit
= ctxt
;
131 OSSpinLockLock(&_dispatch_introspection_threads_lock
);
132 TAILQ_REMOVE(&_dispatch_introspection_threads
, dit
, dit_list
);
133 OSSpinLockUnlock(&_dispatch_introspection_threads_lock
);
134 _dispatch_continuation_free((void*)dit
);
135 _dispatch_thread_setspecific(dispatch_introspection_key
, NULL
);
139 #pragma mark dispatch_introspection_info
142 dispatch_introspection_queue_function_s
143 _dispatch_introspection_continuation_get_info(dispatch_queue_t dq
,
144 dispatch_continuation_t dc
, unsigned long *type
)
146 void *ctxt
= dc
->dc_ctxt
;
147 dispatch_function_t func
= dc
->dc_func
;
148 pthread_t waiter
= NULL
;
150 long flags
= (long)dc
->do_vtable
;
151 if (flags
& DISPATCH_OBJ_SYNC_SLOW_BIT
) {
152 waiter
= pthread_from_mach_thread_np((mach_port_t
)dc
->dc_data
);
153 if (flags
& DISPATCH_OBJ_BARRIER_BIT
) {
160 if (func
== _dispatch_sync_recurse_invoke
) {
165 } else if (func
== _dispatch_async_redirect_invoke
) {
170 flags
= (long)dc
->do_vtable
;
171 } else if (func
== _dispatch_mach_barrier_invoke
) {
175 } else if (func
== _dispatch_apply_invoke
||
176 func
== _dispatch_apply_redirect_invoke
) {
177 dispatch_apply_t da
= ctxt
;
180 if (func
== _dispatch_apply_redirect_invoke
) {
188 if (func
== _dispatch_call_block_and_release
) {
189 *type
= dispatch_introspection_queue_item_type_block
;
190 func
= _dispatch_Block_invoke(ctxt
);
192 *type
= dispatch_introspection_queue_item_type_function
;
194 dispatch_introspection_queue_function_s diqf
= {
199 .group
= flags
& DISPATCH_OBJ_GROUP_BIT
? dc
->dc_data
: NULL
,
201 .barrier
= flags
& DISPATCH_OBJ_BARRIER_BIT
,
202 .sync
= flags
& DISPATCH_OBJ_SYNC_SLOW_BIT
,
209 dispatch_introspection_object_s
210 _dispatch_introspection_object_get_info(dispatch_object_t dou
)
212 dispatch_introspection_object_s dio
= {
214 .target_queue
= dou
._do
->do_targetq
,
215 .type
= (void*)dou
._do
->do_vtable
,
216 .kind
= dx_kind(dou
._do
),
222 dispatch_introspection_queue_s
223 dispatch_introspection_queue_get_info(dispatch_queue_t dq
)
225 bool global
= (dq
->do_xref_cnt
== DISPATCH_OBJECT_GLOBAL_REFCNT
) ||
226 (dq
->do_ref_cnt
== DISPATCH_OBJECT_GLOBAL_REFCNT
);
227 uint16_t width
= dq
->dq_width
;
228 if (width
> 1 && width
!= DISPATCH_QUEUE_WIDTH_MAX
) width
/= 2;
229 dispatch_introspection_queue_s diq
= {
231 .target_queue
= dq
->do_targetq
,
232 .label
= dq
->dq_label
,
233 .serialnum
= dq
->dq_serialnum
,
235 .suspend_count
= dq
->do_suspend_cnt
/ 2,
236 .enqueued
= (dq
->do_suspend_cnt
& 1) && !global
,
237 .barrier
= (dq
->dq_running
& 1) && !global
,
238 .draining
= (dq
->dq_items_head
== (void*)~0ul) ||
239 (!dq
->dq_items_head
&& dq
->dq_items_tail
),
241 .main
= (dq
== &_dispatch_main_q
),
247 dispatch_introspection_source_s
248 _dispatch_introspection_source_get_info(dispatch_source_t ds
)
250 dispatch_source_refs_t dr
= ds
->ds_refs
;
251 dispatch_continuation_t dc
= dr
->ds_handler
[DS_EVENT_HANDLER
];
253 dispatch_function_t handler
= NULL
;
254 bool hdlr_is_block
= false;
257 handler
= dc
->dc_func
;
258 hdlr_is_block
= ((long)dc
->do_vtable
& DISPATCH_OBJ_BLOCK_RELEASE_BIT
);
260 bool after
= (handler
== _dispatch_after_timer_callback
);
261 if (after
&& !(ds
->ds_atomic_flags
& DSF_CANCELED
)) {
264 handler
= dc
->dc_func
;
265 hdlr_is_block
= (handler
== _dispatch_call_block_and_release
);
267 handler
= _dispatch_Block_invoke(ctxt
);
270 dispatch_introspection_source_s dis
= {
272 .target_queue
= ds
->do_targetq
,
273 .type
= ds
->ds_dkev
? (unsigned long)ds
->ds_dkev
->dk_kevent
.filter
: 0,
274 .handle
= ds
->ds_dkev
? (unsigned long)ds
->ds_dkev
->dk_kevent
.ident
: 0,
277 .suspend_count
= ds
->do_suspend_cnt
/ 2,
278 .enqueued
= (ds
->do_suspend_cnt
& 1),
279 .handler_is_block
= hdlr_is_block
,
280 .timer
= ds
->ds_is_timer
,
287 dispatch_introspection_queue_thread_s
288 _dispatch_introspection_thread_get_info(dispatch_introspection_thread_t dit
)
290 dispatch_introspection_queue_thread_s diqt
= {
291 .object
= (void*)dit
,
292 .thread
= dit
->thread
,
294 if (dit
->queue
&& *dit
->queue
) {
295 diqt
.queue
= dispatch_introspection_queue_get_info(*dit
->queue
);
301 dispatch_introspection_queue_item_s
302 dispatch_introspection_queue_item_get_info(dispatch_queue_t dq
,
303 dispatch_continuation_t dc
)
305 dispatch_introspection_queue_item_s diqi
;
306 if (DISPATCH_OBJ_IS_VTABLE(dc
)) {
307 dispatch_object_t dou
= (dispatch_object_t
)dc
;
308 unsigned long type
= dx_type(dou
._do
);
309 unsigned long metatype
= type
& _DISPATCH_META_TYPE_MASK
;
310 if (metatype
== _DISPATCH_QUEUE_TYPE
&&
311 type
!= DISPATCH_QUEUE_SPECIFIC_TYPE
) {
312 diqi
.type
= dispatch_introspection_queue_item_type_queue
;
313 diqi
.queue
= dispatch_introspection_queue_get_info(dou
._dq
);
314 } else if (metatype
== _DISPATCH_SOURCE_TYPE
&&
315 type
!= DISPATCH_MACH_CHANNEL_TYPE
) {
316 diqi
.type
= dispatch_introspection_queue_item_type_source
;
317 diqi
.source
= _dispatch_introspection_source_get_info(dou
._ds
);
319 diqi
.type
= dispatch_introspection_queue_item_type_object
;
320 diqi
.object
= _dispatch_introspection_object_get_info(dou
._do
);
323 diqi
.function
= _dispatch_introspection_continuation_get_info(dq
, dc
,
330 #pragma mark dispatch_introspection_iterators
334 dispatch_introspection_get_queues(dispatch_queue_t start
, size_t count
,
335 dispatch_introspection_queue_t queues
)
337 dispatch_queue_t next
;
338 next
= start
? start
: TAILQ_FIRST(&_dispatch_introspection_queues
);
341 queues
->queue
= NULL
;
344 *queues
++ = dispatch_introspection_queue_get_info(next
);
345 next
= TAILQ_NEXT(next
, diq_list
);
351 dispatch_continuation_t
352 dispatch_introspection_get_queue_threads(dispatch_continuation_t start
,
353 size_t count
, dispatch_introspection_queue_thread_t threads
)
355 dispatch_introspection_thread_t next
= start
? (void*)start
:
356 TAILQ_FIRST(&_dispatch_introspection_threads
);
359 threads
->object
= NULL
;
362 *threads
++ = _dispatch_introspection_thread_get_info(next
);
363 next
= TAILQ_NEXT(next
, dit_list
);
369 dispatch_continuation_t
370 dispatch_introspection_queue_get_items(dispatch_queue_t dq
,
371 dispatch_continuation_t start
, size_t count
,
372 dispatch_introspection_queue_item_t items
)
374 dispatch_continuation_t next
= start
? start
:
375 dq
->dq_items_head
== (void*)~0ul ? NULL
: (void*)dq
->dq_items_head
;
378 items
->type
= dispatch_introspection_queue_item_type_none
;
381 *items
++ = dispatch_introspection_queue_item_get_info(dq
, next
);
382 next
= next
->do_next
;
388 #pragma mark dispatch_introspection_hooks
390 #define DISPATCH_INTROSPECTION_NO_HOOK ((void*)~0ul)
392 dispatch_introspection_hooks_s _dispatch_introspection_hooks
;
393 dispatch_introspection_hooks_s _dispatch_introspection_hook_callouts
;
395 dispatch_introspection_hooks_s _dispatch_introspection_hook_callouts_enabled
= {
396 .queue_create
= DISPATCH_INTROSPECTION_NO_HOOK
,
397 .queue_dispose
= DISPATCH_INTROSPECTION_NO_HOOK
,
398 .queue_item_enqueue
= DISPATCH_INTROSPECTION_NO_HOOK
,
399 .queue_item_dequeue
= DISPATCH_INTROSPECTION_NO_HOOK
,
400 .queue_item_complete
= DISPATCH_INTROSPECTION_NO_HOOK
,
403 #define DISPATCH_INTROSPECTION_HOOKS_COUNT (( \
404 sizeof(_dispatch_introspection_hook_callouts_enabled) - \
405 sizeof(_dispatch_introspection_hook_callouts_enabled._reserved)) / \
406 sizeof(dispatch_function_t))
408 #define DISPATCH_INTROSPECTION_HOOK_ENABLED(h) \
409 (slowpath(_dispatch_introspection_hooks.h))
411 #define DISPATCH_INTROSPECTION_HOOK_CALLOUT(h, ...) ({ \
412 typeof(_dispatch_introspection_hooks.h) _h; \
413 _h = _dispatch_introspection_hooks.h; \
414 if (slowpath((void*)(_h) != DISPATCH_INTROSPECTION_NO_HOOK)) { \
418 #define DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(h) \
419 DISPATCH_EXPORT void _dispatch_introspection_hook_##h(void) \
420 asm("_dispatch_introspection_hook_" #h); \
421 void _dispatch_introspection_hook_##h(void) {}
423 #define DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(h, ...)\
424 dispatch_introspection_hook_##h(__VA_ARGS__)
426 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_create
);
427 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_destroy
);
428 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_item_enqueue
);
429 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_item_dequeue
);
430 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_item_complete
);
431 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_callout_begin
);
432 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_callout_end
);
436 dispatch_introspection_hooks_install(dispatch_introspection_hooks_t hooks
)
438 dispatch_introspection_hooks_s old_hooks
= _dispatch_introspection_hooks
;
439 _dispatch_introspection_hooks
= *hooks
;
440 dispatch_function_t
*e
= (void*)&_dispatch_introspection_hook_callouts
,
441 *h
= (void*)&_dispatch_introspection_hooks
, *oh
= (void*)&old_hooks
;
442 for (size_t i
= 0; i
< DISPATCH_INTROSPECTION_HOOKS_COUNT
; i
++) {
444 h
[i
] = DISPATCH_INTROSPECTION_NO_HOOK
;
446 if (oh
[i
] == DISPATCH_INTROSPECTION_NO_HOOK
) {
455 dispatch_introspection_hook_callouts_enable(
456 dispatch_introspection_hooks_t enable
)
458 _dispatch_introspection_hook_callouts
= enable
? *enable
:
459 _dispatch_introspection_hook_callouts_enabled
;
460 dispatch_function_t
*e
= (void*)&_dispatch_introspection_hook_callouts
,
461 *h
= (void*)&_dispatch_introspection_hooks
;
462 for (size_t i
= 0; i
< DISPATCH_INTROSPECTION_HOOKS_COUNT
; i
++) {
464 h
[i
] = DISPATCH_INTROSPECTION_NO_HOOK
;
465 } else if (!e
[i
] && h
[i
] == DISPATCH_INTROSPECTION_NO_HOOK
) {
473 dispatch_introspection_hook_callout_queue_create(
474 dispatch_introspection_queue_t queue_info
)
476 DISPATCH_INTROSPECTION_HOOK_CALLOUT(queue_create
, queue_info
);
481 _dispatch_introspection_queue_create_hook(dispatch_queue_t dq
)
483 dispatch_introspection_queue_s diq
;
484 diq
= dispatch_introspection_queue_get_info(dq
);
485 dispatch_introspection_hook_callout_queue_create(&diq
);
489 _dispatch_introspection_queue_create(dispatch_queue_t dq
)
491 OSSpinLockLock(&_dispatch_introspection_queues_lock
);
492 TAILQ_INSERT_TAIL(&_dispatch_introspection_queues
, dq
, diq_list
);
493 OSSpinLockUnlock(&_dispatch_introspection_queues_lock
);
495 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(queue_create
, dq
);
496 if (DISPATCH_INTROSPECTION_HOOK_ENABLED(queue_create
)) {
497 _dispatch_introspection_queue_create_hook(dq
);
504 dispatch_introspection_hook_callout_queue_dispose(
505 dispatch_introspection_queue_t queue_info
)
507 DISPATCH_INTROSPECTION_HOOK_CALLOUT(queue_dispose
, queue_info
);
512 _dispatch_introspection_queue_dispose_hook(dispatch_queue_t dq
)
514 dispatch_introspection_queue_s diq
;
515 diq
= dispatch_introspection_queue_get_info(dq
);
516 dispatch_introspection_hook_callout_queue_dispose(&diq
);
520 _dispatch_introspection_queue_dispose(dispatch_queue_t dq
)
522 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(queue_destroy
, dq
);
523 if (DISPATCH_INTROSPECTION_HOOK_ENABLED(queue_dispose
)) {
524 _dispatch_introspection_queue_dispose_hook(dq
);
527 OSSpinLockLock(&_dispatch_introspection_queues_lock
);
528 TAILQ_REMOVE(&_dispatch_introspection_queues
, dq
, diq_list
);
529 OSSpinLockUnlock(&_dispatch_introspection_queues_lock
);
534 dispatch_introspection_hook_callout_queue_item_enqueue(dispatch_queue_t queue
,
535 dispatch_introspection_queue_item_t item
)
537 DISPATCH_INTROSPECTION_HOOK_CALLOUT(queue_item_enqueue
, queue
, item
);
542 _dispatch_introspection_queue_item_enqueue_hook(dispatch_queue_t dq
,
543 dispatch_object_t dou
)
545 dispatch_introspection_queue_item_s diqi
;
546 diqi
= dispatch_introspection_queue_item_get_info(dq
, dou
._dc
);
547 dispatch_introspection_hook_callout_queue_item_enqueue(dq
, &diqi
);
551 _dispatch_introspection_queue_item_enqueue(dispatch_queue_t dq
,
552 dispatch_object_t dou
)
554 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(
555 queue_item_enqueue
, dq
, dou
);
556 if (DISPATCH_INTROSPECTION_HOOK_ENABLED(queue_item_enqueue
)) {
557 _dispatch_introspection_queue_item_enqueue_hook(dq
, dou
);
563 dispatch_introspection_hook_callout_queue_item_dequeue(dispatch_queue_t queue
,
564 dispatch_introspection_queue_item_t item
)
566 DISPATCH_INTROSPECTION_HOOK_CALLOUT(queue_item_dequeue
, queue
, item
);
571 _dispatch_introspection_queue_item_dequeue_hook(dispatch_queue_t dq
,
572 dispatch_object_t dou
)
574 dispatch_introspection_queue_item_s diqi
;
575 diqi
= dispatch_introspection_queue_item_get_info(dq
, dou
._dc
);
576 dispatch_introspection_hook_callout_queue_item_dequeue(dq
, &diqi
);
580 _dispatch_introspection_queue_item_dequeue(dispatch_queue_t dq
,
581 dispatch_object_t dou
)
583 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(
584 queue_item_dequeue
, dq
, dou
);
585 if (DISPATCH_INTROSPECTION_HOOK_ENABLED(queue_item_dequeue
)) {
586 _dispatch_introspection_queue_item_dequeue_hook(dq
, dou
);
592 dispatch_introspection_hook_callout_queue_item_complete(
593 dispatch_continuation_t object
)
595 DISPATCH_INTROSPECTION_HOOK_CALLOUT(queue_item_complete
, object
);
600 _dispatch_introspection_queue_item_complete_hook(dispatch_object_t dou
)
602 dispatch_introspection_hook_callout_queue_item_complete(dou
._dc
);
606 _dispatch_introspection_queue_item_complete(dispatch_object_t dou
)
608 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(queue_item_complete
, dou
);
609 if (DISPATCH_INTROSPECTION_HOOK_ENABLED(queue_item_complete
)) {
610 _dispatch_introspection_queue_item_complete_hook(dou
);
615 _dispatch_introspection_callout_entry(void *ctxt
, dispatch_function_t f
) {
616 dispatch_queue_t dq
= _dispatch_queue_get_current();
617 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(
618 queue_callout_begin
, dq
, ctxt
, f
);
622 _dispatch_introspection_callout_return(void *ctxt
, dispatch_function_t f
) {
623 dispatch_queue_t dq
= _dispatch_queue_get_current();
624 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(
625 queue_callout_end
, dq
, ctxt
, f
);
628 #endif // DISPATCH_INTROSPECTION