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 "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
= 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 uint32_t width
= dq
->dq_width
;
228 if (width
> 1 && width
!= UINT32_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 void *ctxt
= dr
->ds_handler_ctxt
;
252 dispatch_function_t handler
= dr
->ds_handler_func
;
253 bool handler_is_block
= ds
->ds_handler_is_block
;
254 bool after
= (handler
== _dispatch_after_timer_callback
);
255 if (after
&& !(ds
->ds_atomic_flags
& DSF_CANCELED
)) {
256 dispatch_continuation_t dc
= ctxt
;
258 handler
= dc
->dc_func
;
259 if (handler
== _dispatch_call_block_and_release
) {
260 handler
= _dispatch_Block_invoke(ctxt
);
261 handler_is_block
= 1;
264 dispatch_introspection_source_s dis
= {
266 .target_queue
= ds
->do_targetq
,
267 .type
= ds
->ds_dkev
? (unsigned long)ds
->ds_dkev
->dk_kevent
.filter
: 0,
268 .handle
= ds
->ds_dkev
? (unsigned long)ds
->ds_dkev
->dk_kevent
.ident
: 0,
271 .suspend_count
= ds
->do_suspend_cnt
/ 2,
272 .enqueued
= (ds
->do_suspend_cnt
& 1),
273 .handler_is_block
= handler_is_block
,
274 .timer
= ds
->ds_is_timer
,
281 dispatch_introspection_queue_thread_s
282 _dispatch_introspection_thread_get_info(dispatch_introspection_thread_t dit
)
284 dispatch_introspection_queue_thread_s diqt
= {
285 .object
= (void*)dit
,
286 .thread
= dit
->thread
,
288 if (dit
->queue
&& *dit
->queue
) {
289 diqt
.queue
= dispatch_introspection_queue_get_info(*dit
->queue
);
295 dispatch_introspection_queue_item_s
296 dispatch_introspection_queue_item_get_info(dispatch_queue_t dq
,
297 dispatch_continuation_t dc
)
299 dispatch_introspection_queue_item_s diqi
;
300 if (DISPATCH_OBJ_IS_VTABLE(dc
)) {
301 dispatch_object_t dou
= (dispatch_object_t
)dc
;
302 unsigned long type
= dx_type(dou
._do
);
303 unsigned long metatype
= type
& _DISPATCH_META_TYPE_MASK
;
304 if (metatype
== _DISPATCH_QUEUE_TYPE
&&
305 type
!= DISPATCH_QUEUE_SPECIFIC_TYPE
) {
306 diqi
.type
= dispatch_introspection_queue_item_type_queue
;
307 diqi
.queue
= dispatch_introspection_queue_get_info(dou
._dq
);
308 } else if (metatype
== _DISPATCH_SOURCE_TYPE
) {
309 diqi
.type
= dispatch_introspection_queue_item_type_source
;
310 diqi
.source
= _dispatch_introspection_source_get_info(dou
._ds
);
312 diqi
.type
= dispatch_introspection_queue_item_type_object
;
313 diqi
.object
= _dispatch_introspection_object_get_info(dou
._do
);
316 diqi
.function
= _dispatch_introspection_continuation_get_info(dq
, dc
,
323 #pragma mark dispatch_introspection_iterators
327 dispatch_introspection_get_queues(dispatch_queue_t start
, size_t count
,
328 dispatch_introspection_queue_t queues
)
330 dispatch_queue_t next
;
331 next
= start
? start
: TAILQ_FIRST(&_dispatch_introspection_queues
);
334 queues
->queue
= NULL
;
337 *queues
++ = dispatch_introspection_queue_get_info(next
);
338 next
= TAILQ_NEXT(next
, diq_list
);
344 dispatch_continuation_t
345 dispatch_introspection_get_queue_threads(dispatch_continuation_t start
,
346 size_t count
, dispatch_introspection_queue_thread_t threads
)
348 dispatch_introspection_thread_t next
= start
? (void*)start
:
349 TAILQ_FIRST(&_dispatch_introspection_threads
);
352 threads
->object
= NULL
;
355 *threads
++ = _dispatch_introspection_thread_get_info(next
);
356 next
= TAILQ_NEXT(next
, dit_list
);
362 dispatch_continuation_t
363 dispatch_introspection_queue_get_items(dispatch_queue_t dq
,
364 dispatch_continuation_t start
, size_t count
,
365 dispatch_introspection_queue_item_t items
)
367 dispatch_continuation_t next
= start
? start
:
368 dq
->dq_items_head
== (void*)~0ul ? NULL
: (void*)dq
->dq_items_head
;
371 items
->type
= dispatch_introspection_queue_item_type_none
;
374 *items
++ = dispatch_introspection_queue_item_get_info(dq
, next
);
375 next
= next
->do_next
;
381 #pragma mark dispatch_introspection_hooks
383 #define DISPATCH_INTROSPECTION_NO_HOOK ((void*)~0ul)
385 dispatch_introspection_hooks_s _dispatch_introspection_hooks
;
386 dispatch_introspection_hooks_s _dispatch_introspection_hook_callouts
;
388 dispatch_introspection_hooks_s _dispatch_introspection_hook_callouts_enabled
= {
389 .queue_create
= DISPATCH_INTROSPECTION_NO_HOOK
,
390 .queue_dispose
= DISPATCH_INTROSPECTION_NO_HOOK
,
391 .queue_item_enqueue
= DISPATCH_INTROSPECTION_NO_HOOK
,
392 .queue_item_dequeue
= DISPATCH_INTROSPECTION_NO_HOOK
,
393 .queue_item_complete
= DISPATCH_INTROSPECTION_NO_HOOK
,
396 #define DISPATCH_INTROSPECTION_HOOKS_COUNT (( \
397 sizeof(_dispatch_introspection_hook_callouts_enabled) - \
398 sizeof(_dispatch_introspection_hook_callouts_enabled._reserved)) / \
399 sizeof(dispatch_function_t))
401 #define DISPATCH_INTROSPECTION_HOOK_ENABLED(h) \
402 (slowpath(_dispatch_introspection_hooks.h))
404 #define DISPATCH_INTROSPECTION_HOOK_CALLOUT(h, ...) ({ \
405 typeof(_dispatch_introspection_hooks.h) _h; \
406 _h = _dispatch_introspection_hooks.h; \
407 if (slowpath((void*)(_h) != DISPATCH_INTROSPECTION_NO_HOOK)) { \
411 #define DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(h) \
412 DISPATCH_EXPORT void _dispatch_introspection_hook_##h(void) \
413 asm("_dispatch_introspection_hook_" #h); \
414 void _dispatch_introspection_hook_##h(void) {}
416 #define DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(h, ...)\
417 dispatch_introspection_hook_##h(__VA_ARGS__)
419 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_create
);
420 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_destroy
);
421 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_item_enqueue
);
422 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_item_dequeue
);
423 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_item_complete
);
424 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_callout_begin
);
425 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_callout_end
);
429 dispatch_introspection_hooks_install(dispatch_introspection_hooks_t hooks
)
431 dispatch_introspection_hooks_s old_hooks
= _dispatch_introspection_hooks
;
432 _dispatch_introspection_hooks
= *hooks
;
433 dispatch_function_t
*e
= (void*)&_dispatch_introspection_hook_callouts
,
434 *h
= (void*)&_dispatch_introspection_hooks
, *oh
= (void*)&old_hooks
;
435 for (size_t i
= 0; i
< DISPATCH_INTROSPECTION_HOOKS_COUNT
; i
++) {
437 h
[i
] = DISPATCH_INTROSPECTION_NO_HOOK
;
439 if (oh
[i
] == DISPATCH_INTROSPECTION_NO_HOOK
) {
448 dispatch_introspection_hook_callouts_enable(
449 dispatch_introspection_hooks_t enable
)
451 _dispatch_introspection_hook_callouts
= enable
? *enable
:
452 _dispatch_introspection_hook_callouts_enabled
;
453 dispatch_function_t
*e
= (void*)&_dispatch_introspection_hook_callouts
,
454 *h
= (void*)&_dispatch_introspection_hooks
;
455 for (size_t i
= 0; i
< DISPATCH_INTROSPECTION_HOOKS_COUNT
; i
++) {
457 h
[i
] = DISPATCH_INTROSPECTION_NO_HOOK
;
458 } else if (!e
[i
] && h
[i
] == DISPATCH_INTROSPECTION_NO_HOOK
) {
466 dispatch_introspection_hook_callout_queue_create(
467 dispatch_introspection_queue_t queue_info
)
469 DISPATCH_INTROSPECTION_HOOK_CALLOUT(queue_create
, queue_info
);
474 _dispatch_introspection_queue_create_hook(dispatch_queue_t dq
)
476 dispatch_introspection_queue_s diq
;
477 diq
= dispatch_introspection_queue_get_info(dq
);
478 dispatch_introspection_hook_callout_queue_create(&diq
);
482 _dispatch_introspection_queue_create(dispatch_queue_t dq
)
484 OSSpinLockLock(&_dispatch_introspection_queues_lock
);
485 TAILQ_INSERT_TAIL(&_dispatch_introspection_queues
, dq
, diq_list
);
486 OSSpinLockUnlock(&_dispatch_introspection_queues_lock
);
488 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(queue_create
, dq
);
489 if (DISPATCH_INTROSPECTION_HOOK_ENABLED(queue_create
)) {
490 _dispatch_introspection_queue_create_hook(dq
);
497 dispatch_introspection_hook_callout_queue_dispose(
498 dispatch_introspection_queue_t queue_info
)
500 DISPATCH_INTROSPECTION_HOOK_CALLOUT(queue_dispose
, queue_info
);
505 _dispatch_introspection_queue_dispose_hook(dispatch_queue_t dq
)
507 dispatch_introspection_queue_s diq
;
508 diq
= dispatch_introspection_queue_get_info(dq
);
509 dispatch_introspection_hook_callout_queue_dispose(&diq
);
513 _dispatch_introspection_queue_dispose(dispatch_queue_t dq
)
515 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(queue_destroy
, dq
);
516 if (DISPATCH_INTROSPECTION_HOOK_ENABLED(queue_dispose
)) {
517 _dispatch_introspection_queue_dispose_hook(dq
);
520 OSSpinLockLock(&_dispatch_introspection_queues_lock
);
521 TAILQ_REMOVE(&_dispatch_introspection_queues
, dq
, diq_list
);
522 OSSpinLockUnlock(&_dispatch_introspection_queues_lock
);
527 dispatch_introspection_hook_callout_queue_item_enqueue(dispatch_queue_t queue
,
528 dispatch_introspection_queue_item_t item
)
530 DISPATCH_INTROSPECTION_HOOK_CALLOUT(queue_item_enqueue
, queue
, item
);
535 _dispatch_introspection_queue_item_enqueue_hook(dispatch_queue_t dq
,
536 dispatch_object_t dou
)
538 dispatch_introspection_queue_item_s diqi
;
539 diqi
= dispatch_introspection_queue_item_get_info(dq
, dou
._dc
);
540 dispatch_introspection_hook_callout_queue_item_enqueue(dq
, &diqi
);
544 _dispatch_introspection_queue_item_enqueue(dispatch_queue_t dq
,
545 dispatch_object_t dou
)
547 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(
548 queue_item_enqueue
, dq
, dou
);
549 if (DISPATCH_INTROSPECTION_HOOK_ENABLED(queue_item_enqueue
)) {
550 _dispatch_introspection_queue_item_enqueue_hook(dq
, dou
);
556 dispatch_introspection_hook_callout_queue_item_dequeue(dispatch_queue_t queue
,
557 dispatch_introspection_queue_item_t item
)
559 DISPATCH_INTROSPECTION_HOOK_CALLOUT(queue_item_dequeue
, queue
, item
);
564 _dispatch_introspection_queue_item_dequeue_hook(dispatch_queue_t dq
,
565 dispatch_object_t dou
)
567 dispatch_introspection_queue_item_s diqi
;
568 diqi
= dispatch_introspection_queue_item_get_info(dq
, dou
._dc
);
569 dispatch_introspection_hook_callout_queue_item_dequeue(dq
, &diqi
);
573 _dispatch_introspection_queue_item_dequeue(dispatch_queue_t dq
,
574 dispatch_object_t dou
)
576 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(
577 queue_item_dequeue
, dq
, dou
);
578 if (DISPATCH_INTROSPECTION_HOOK_ENABLED(queue_item_dequeue
)) {
579 _dispatch_introspection_queue_item_dequeue_hook(dq
, dou
);
585 dispatch_introspection_hook_callout_queue_item_complete(
586 dispatch_continuation_t object
)
588 DISPATCH_INTROSPECTION_HOOK_CALLOUT(queue_item_complete
, object
);
593 _dispatch_introspection_queue_item_complete_hook(dispatch_object_t dou
)
595 dispatch_introspection_hook_callout_queue_item_complete(dou
._dc
);
599 _dispatch_introspection_queue_item_complete(dispatch_object_t dou
)
601 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(queue_item_complete
, dou
);
602 if (DISPATCH_INTROSPECTION_HOOK_ENABLED(queue_item_complete
)) {
603 _dispatch_introspection_queue_item_complete_hook(dou
);
608 _dispatch_introspection_callout_entry(void *ctxt
, dispatch_function_t f
) {
609 dispatch_queue_t dq
= _dispatch_queue_get_current();
610 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(
611 queue_callout_begin
, dq
, ctxt
, f
);
615 _dispatch_introspection_callout_return(void *ctxt
, dispatch_function_t f
) {
616 dispatch_queue_t dq
= _dispatch_queue_get_current();
617 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(
618 queue_callout_end
, dq
, ctxt
, f
);
621 #endif // DISPATCH_INTROSPECTION