1 #include <darwintest.h>
2 #include <darwintest_utils.h>
3 #include <dispatch/dispatch.h>
5 #include <ktrace/session.h>
6 #include <ktrace/private.h>
7 #include <kperf/kperf.h>
8 #include <mach/clock_types.h>
9 #include <mach/dyld_kernel.h>
10 #include <mach/host_info.h>
11 #include <mach/mach.h>
12 #include <mach/mach_init.h>
13 #include <mach/task.h>
14 #include <os/assumes.h>
16 #include <sys/kdebug.h>
17 #include <sys/kdebug_signpost.h>
18 #include <sys/sysctl.h>
21 #include "ktrace_helpers.h"
22 #include "test_utils.h"
25 T_META_NAMESPACE("xnu.ktrace"),
28 #define KDBG_TEST_MACROS 1
29 #define KDBG_TEST_OLD_TIMES 2
30 #define KDBG_TEST_FUTURE_TIMES 3
31 #define KDBG_TEST_IOP_SYNC_FLUSH 4
34 assert_kdebug_test(unsigned int flavor
)
37 int mib
[] = { CTL_KERN
, KERN_KDEBUG
, KERN_KDTEST
};
38 T_ASSERT_POSIX_SUCCESS(sysctl(mib
, sizeof(mib
) / sizeof(mib
[0]), NULL
,
39 &size
, NULL
, 0), "KERN_KDTEST sysctl");
42 #pragma mark kdebug syscalls
44 #define TRACE_DEBUGID (0xfedfed00U)
46 T_DECL(kdebug_trace_syscall
, "test that kdebug_trace(2) emits correct events")
48 start_controlling_ktrace();
50 ktrace_session_t s
= ktrace_session_create();
51 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_NOTNULL(s
, "created session");
53 ktrace_events_class(s
, DBG_MACH
, ^(__unused
struct trace_point
*tp
){});
55 __block
int events_seen
= 0;
56 ktrace_events_single(s
, TRACE_DEBUGID
, ^void (struct trace_point
*tp
) {
58 T_PASS("saw traced event");
60 if (ktrace_is_kernel_64_bit(s
)) {
61 T_EXPECT_EQ(tp
->arg1
, UINT64_C(0xfeedfacefeedface),
62 "argument 1 of traced event is correct");
64 T_EXPECT_EQ(tp
->arg1
, UINT64_C(0xfeedface),
65 "argument 1 of traced event is correct");
67 T_EXPECT_EQ(tp
->arg2
, 2ULL, "argument 2 of traced event is correct");
68 T_EXPECT_EQ(tp
->arg3
, 3ULL, "argument 3 of traced event is correct");
69 T_EXPECT_EQ(tp
->arg4
, 4ULL, "argument 4 of traced event is correct");
74 ktrace_set_completion_handler(s
, ^{
75 T_EXPECT_GE(events_seen
, 1, NULL
);
76 ktrace_session_destroy(s
);
80 ktrace_filter_pid(s
, getpid());
82 T_ASSERT_POSIX_ZERO(ktrace_start(s
, dispatch_get_main_queue()), NULL
);
83 T_ASSERT_POSIX_SUCCESS(kdebug_trace(TRACE_DEBUGID
, 0xfeedfacefeedface, 2,
90 #define SIGNPOST_SINGLE_CODE (0x10U)
91 #define SIGNPOST_PAIRED_CODE (0x20U)
93 T_DECL(kdebug_signpost_syscall
,
94 "test that kdebug_signpost(2) emits correct events")
96 start_controlling_ktrace();
98 ktrace_session_t s
= ktrace_session_create();
99 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_NOTNULL(s
, "created session");
101 __block
int single_seen
= 0;
102 __block
int paired_seen
= 0;
104 /* make sure to get enough events for the KDBUFWAIT to trigger */
105 // ktrace_events_class(s, DBG_MACH, ^(__unused struct trace_point *tp){});
106 ktrace_events_single(s
,
107 APPSDBG_CODE(DBG_APP_SIGNPOST
, SIGNPOST_SINGLE_CODE
),
108 ^(struct trace_point
*tp
) {
110 T_PASS("single signpost is traced");
112 T_EXPECT_EQ(tp
->arg1
, 1ULL, "argument 1 of single signpost is correct");
113 T_EXPECT_EQ(tp
->arg2
, 2ULL, "argument 2 of single signpost is correct");
114 T_EXPECT_EQ(tp
->arg3
, 3ULL, "argument 3 of single signpost is correct");
115 T_EXPECT_EQ(tp
->arg4
, 4ULL, "argument 4 of single signpost is correct");
118 ktrace_events_single_paired(s
,
119 APPSDBG_CODE(DBG_APP_SIGNPOST
, SIGNPOST_PAIRED_CODE
),
120 ^(struct trace_point
*start
, struct trace_point
*end
) {
122 T_PASS("paired signposts are traced");
124 T_EXPECT_EQ(start
->arg1
, 5ULL, "argument 1 of start signpost is correct");
125 T_EXPECT_EQ(start
->arg2
, 6ULL, "argument 2 of start signpost is correct");
126 T_EXPECT_EQ(start
->arg3
, 7ULL, "argument 3 of start signpost is correct");
127 T_EXPECT_EQ(start
->arg4
, 8ULL, "argument 4 of start signpost is correct");
129 T_EXPECT_EQ(end
->arg1
, 9ULL, "argument 1 of end signpost is correct");
130 T_EXPECT_EQ(end
->arg2
, 10ULL, "argument 2 of end signpost is correct");
131 T_EXPECT_EQ(end
->arg3
, 11ULL, "argument 3 of end signpost is correct");
132 T_EXPECT_EQ(end
->arg4
, 12ULL, "argument 4 of end signpost is correct");
134 T_EXPECT_EQ(single_seen
, 1, "signposts are traced in the correct order");
139 ktrace_set_completion_handler(s
, ^(void) {
140 T_QUIET
; T_EXPECT_NE(single_seen
, 0,
141 "did not see single tracepoint before timeout");
142 T_QUIET
; T_EXPECT_NE(paired_seen
, 0,
143 "did not see single tracepoint before timeout");
144 ktrace_session_destroy(s
);
148 ktrace_filter_pid(s
, getpid());
150 T_ASSERT_POSIX_ZERO(ktrace_start(s
, dispatch_get_main_queue()),
153 #pragma clang diagnostic push
154 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
155 T_EXPECT_POSIX_SUCCESS(kdebug_signpost(SIGNPOST_SINGLE_CODE
, 1, 2, 3, 4),
156 "emitted single signpost");
157 T_EXPECT_POSIX_SUCCESS(
158 kdebug_signpost_start(SIGNPOST_PAIRED_CODE
, 5, 6, 7, 8),
159 "emitted start signpost");
160 T_EXPECT_POSIX_SUCCESS(
161 kdebug_signpost_end(SIGNPOST_PAIRED_CODE
, 9, 10, 11, 12),
162 "emitted end signpost");
163 #pragma clang diagnostic pop
169 T_DECL(syscall_tracing
,
170 "ensure that syscall arguments are traced propertly")
172 ktrace_session_t s
= ktrace_session_create();
173 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_NOTNULL(s
, "created session");
175 __block
bool seen
= 0;
177 ktrace_filter_pid(s
, getpid());
179 static const int telemetry_syscall_no
= 451;
180 static const uint64_t arg1
= 0xfeedfacefeedface;
182 ktrace_events_single(s
, BSDDBG_CODE(DBG_BSD_EXCP_SC
, telemetry_syscall_no
),
183 ^(struct trace_point
*evt
){
184 if (KDBG_EXTRACT_CODE(evt
->debugid
) != telemetry_syscall_no
|| seen
) {
189 if (ktrace_is_kernel_64_bit(s
)) {
190 T_EXPECT_EQ(evt
->arg1
, arg1
,
191 "argument 1 of syscall event is correct");
193 T_EXPECT_EQ(evt
->arg1
, (uint64_t)(uint32_t)(arg1
),
194 "argument 1 of syscall event is correct");
200 ktrace_set_completion_handler(s
, ^{
202 "should have seen a syscall event for kevent_id(2)");
203 ktrace_session_destroy(s
);
207 int error
= ktrace_start(s
, dispatch_get_main_queue());
208 T_ASSERT_POSIX_ZERO(error
, "started tracing");
211 * telemetry(2) has a 64-bit argument that will definitely be traced, and
212 * is unlikely to be used elsewhere by this process.
214 extern int __telemetry(uint64_t cmd
, uint64_t deadline
, uint64_t interval
,
215 uint64_t leeway
, uint64_t arg4
, uint64_t arg5
);
216 (void)__telemetry(arg1
, 0, 0, 0, 0, 0);
218 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, 5 * NSEC_PER_SEC
),
219 dispatch_get_main_queue(), ^{
220 T_LOG("ending test due to timeout");
227 #pragma mark kdebug behaviors
229 #define WRAPPING_EVENTS_COUNT (150000)
230 #define TRACE_ITERATIONS (5000)
231 #define WRAPPING_EVENTS_THRESHOLD (100)
234 "ensure that wrapping traces lost events and no events prior to the wrap",
235 T_META_CHECK_LEAKS(false))
238 int wait_wrapping_secs
= (WRAPPING_EVENTS_COUNT
/ TRACE_ITERATIONS
) + 5;
239 int current_secs
= wait_wrapping_secs
;
241 start_controlling_ktrace();
243 /* use sysctls manually to bypass libktrace assumptions */
245 int mib
[4] = { CTL_KERN
, KERN_KDEBUG
};
246 mib
[2] = KERN_KDSETBUF
; mib
[3] = WRAPPING_EVENTS_COUNT
;
247 T_ASSERT_POSIX_SUCCESS(sysctl(mib
, 4, NULL
, 0, NULL
, 0), "KERN_KDSETBUF");
249 mib
[2] = KERN_KDSETUP
; mib
[3] = 0;
251 T_ASSERT_POSIX_SUCCESS(sysctl(mib
, 3, NULL
, &needed
, NULL
, 0),
254 mib
[2] = KERN_KDENABLE
; mib
[3] = 1;
255 T_ASSERT_POSIX_SUCCESS(sysctl(mib
, 4, NULL
, 0, NULL
, 0), "KERN_KDENABLE");
257 /* wrapping is on by default */
259 /* wait until wrapped */
260 T_LOG("waiting for trace to wrap");
261 mib
[2] = KERN_KDGETBUF
;
262 needed
= sizeof(buf_info
);
265 for (int i
= 0; i
< TRACE_ITERATIONS
; i
++) {
267 T_ASSERT_POSIX_SUCCESS(kdebug_trace(0xfefe0000, 0, 0, 0, 0), NULL
);
270 T_ASSERT_POSIX_SUCCESS(sysctl(mib
, 3, &buf_info
, &needed
, NULL
, 0),
272 } while (!(buf_info
.flags
& KDBG_WRAPPED
) && --current_secs
> 0);
274 T_ASSERT_TRUE(buf_info
.flags
& KDBG_WRAPPED
,
275 "trace wrapped (after %d seconds within %d second timeout)",
276 wait_wrapping_secs
- current_secs
, wait_wrapping_secs
);
278 ktrace_session_t s
= ktrace_session_create();
279 T_QUIET
; T_ASSERT_NOTNULL(s
, NULL
);
280 T_QUIET
; T_ASSERT_POSIX_ZERO(ktrace_set_use_existing(s
), NULL
);
282 __block
int events
= 0;
284 ktrace_events_all(s
, ^(struct trace_point
*tp
) {
286 T_EXPECT_EQ(tp
->debugid
, (unsigned int)TRACE_LOST_EVENTS
,
287 "first event's debugid 0x%08x (%s) should be TRACE_LOST_EVENTS",
289 ktrace_name_for_eventid(s
, tp
->debugid
& KDBG_EVENTID_MASK
));
292 T_EXPECT_NE(tp
->debugid
, (unsigned int)TRACE_LOST_EVENTS
,
293 "event debugid 0x%08x (%s) should not be TRACE_LOST_EVENTS",
295 ktrace_name_for_eventid(s
, tp
->debugid
& KDBG_EVENTID_MASK
));
299 if (events
> WRAPPING_EVENTS_THRESHOLD
) {
304 ktrace_set_completion_handler(s
, ^{
305 ktrace_session_destroy(s
);
309 T_ASSERT_POSIX_ZERO(ktrace_start(s
, dispatch_get_main_queue()),
315 T_DECL(reject_old_events
,
316 "ensure that kdebug rejects events from before tracing began",
317 T_META_CHECK_LEAKS(false))
319 __block
uint64_t event_horizon_ts
;
321 start_controlling_ktrace();
323 ktrace_session_t s
= ktrace_session_create();
324 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_NOTNULL(s
, "created session");
326 __block
int events
= 0;
327 ktrace_events_single(s
, KDBG_EVENTID(DBG_BSD
, DBG_BSD_KDEBUG_TEST
, 1),
328 ^(struct trace_point
*tp
) {
330 T_EXPECT_GT(tp
->timestamp
, event_horizon_ts
,
331 "events in trace should be from after tracing began");
334 ktrace_set_completion_handler(s
, ^{
335 T_EXPECT_EQ(events
, 2, "should see only two events");
336 ktrace_session_destroy(s
);
340 event_horizon_ts
= mach_absolute_time();
342 T_ASSERT_POSIX_ZERO(ktrace_start(s
, dispatch_get_main_queue()), NULL
);
343 /* first, try an old event at the beginning of trace */
344 assert_kdebug_test(KDBG_TEST_OLD_TIMES
);
345 /* after a good event has been traced, old events should be rejected */
346 assert_kdebug_test(KDBG_TEST_OLD_TIMES
);
352 #define ORDERING_TIMEOUT_SEC 5
354 T_DECL(ascending_time_order
,
355 "ensure that kdebug events are in ascending order based on time",
356 T_META_CHECK_LEAKS(false))
358 __block
uint64_t prev_ts
= 0;
359 __block
uint32_t prev_debugid
= 0;
360 __block
unsigned int prev_cpu
= 0;
361 __block
bool in_order
= true;
363 start_controlling_ktrace();
365 ktrace_session_t s
= ktrace_session_create();
366 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_NOTNULL(s
, "created session");
368 ktrace_events_all(s
, ^(struct trace_point
*tp
) {
369 if (tp
->timestamp
< prev_ts
) {
371 T_LOG("%" PRIu64
": %#" PRIx32
" (cpu %d)",
372 prev_ts
, prev_debugid
, prev_cpu
);
373 T_LOG("%" PRIu64
": %#" PRIx32
" (cpu %d)",
374 tp
->timestamp
, tp
->debugid
, tp
->cpuid
);
379 ktrace_set_completion_handler(s
, ^{
380 ktrace_session_destroy(s
);
381 T_EXPECT_TRUE(in_order
, "event timestamps were in-order");
385 T_ASSERT_POSIX_ZERO(ktrace_start(s
, dispatch_get_main_queue()),
388 /* try to inject old timestamps into trace */
389 assert_kdebug_test(KDBG_TEST_OLD_TIMES
);
391 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, ORDERING_TIMEOUT_SEC
* NSEC_PER_SEC
),
392 dispatch_get_main_queue(), ^{
393 T_LOG("ending test after timeout");
400 #pragma mark dyld tracing
402 __attribute__((aligned(8)))
403 static const char map_uuid
[16] = "map UUID";
405 __attribute__((aligned(8)))
406 static const char unmap_uuid
[16] = "unmap UUID";
408 __attribute__((aligned(8)))
409 static const char sc_uuid
[16] = "shared UUID";
411 static fsid_t map_fsid
= { .val
= { 42, 43 } };
412 static fsid_t unmap_fsid
= { .val
= { 44, 45 } };
413 static fsid_t sc_fsid
= { .val
= { 46, 47 } };
415 static fsobj_id_t map_fsobjid
= { .fid_objno
= 42, .fid_generation
= 43 };
416 static fsobj_id_t unmap_fsobjid
= { .fid_objno
= 44, .fid_generation
= 45 };
417 static fsobj_id_t sc_fsobjid
= { .fid_objno
= 46, .fid_generation
= 47 };
419 #define MAP_LOAD_ADDR 0xabadcafe
420 #define UNMAP_LOAD_ADDR 0xfeedface
421 #define SC_LOAD_ADDR 0xfedfaced
425 expect_dyld_image_info(struct trace_point
*tp
, const uint64_t *exp_uuid
,
426 uint64_t exp_load_addr
, fsid_t
*exp_fsid
, fsobj_id_t
*exp_fsobjid
,
429 #if defined(__LP64__) || defined(__arm64__)
435 uuid
[0] = (uint64_t)tp
->arg1
;
436 uuid
[1] = (uint64_t)tp
->arg2
;
437 load_addr
= (uint64_t)tp
->arg3
;
438 fsid
.val
[0] = (int32_t)(tp
->arg4
& UINT32_MAX
);
439 fsid
.val
[1] = (int32_t)((uint64_t)tp
->arg4
>> 32);
441 T_QUIET
; T_EXPECT_EQ(uuid
[0], exp_uuid
[0], NULL
);
442 T_QUIET
; T_EXPECT_EQ(uuid
[1], exp_uuid
[1], NULL
);
443 T_QUIET
; T_EXPECT_EQ(load_addr
, exp_load_addr
, NULL
);
444 T_QUIET
; T_EXPECT_EQ(fsid
.val
[0], exp_fsid
->val
[0], NULL
);
445 T_QUIET
; T_EXPECT_EQ(fsid
.val
[1], exp_fsid
->val
[1], NULL
);
446 } else if (order
== 1) {
449 fsobjid
.fid_objno
= (uint32_t)(tp
->arg1
& UINT32_MAX
);
450 fsobjid
.fid_generation
= (uint32_t)((uint64_t)tp
->arg1
>> 32);
452 T_QUIET
; T_EXPECT_EQ(fsobjid
.fid_objno
, exp_fsobjid
->fid_objno
, NULL
);
453 T_QUIET
; T_EXPECT_EQ(fsobjid
.fid_generation
,
454 exp_fsobjid
->fid_generation
, NULL
);
456 T_ASSERT_FAIL("unrecognized order of events %d", order
);
458 #else /* defined(__LP64__) */
462 uuid
[0] = (uint32_t)tp
->arg1
;
463 uuid
[1] = (uint32_t)tp
->arg2
;
464 uuid
[2] = (uint32_t)tp
->arg3
;
465 uuid
[3] = (uint32_t)tp
->arg4
;
467 T_QUIET
; T_EXPECT_EQ(uuid
[0], (uint32_t)exp_uuid
[0], NULL
);
468 T_QUIET
; T_EXPECT_EQ(uuid
[1], (uint32_t)(exp_uuid
[0] >> 32), NULL
);
469 T_QUIET
; T_EXPECT_EQ(uuid
[2], (uint32_t)exp_uuid
[1], NULL
);
470 T_QUIET
; T_EXPECT_EQ(uuid
[3], (uint32_t)(exp_uuid
[1] >> 32), NULL
);
471 } else if (order
== 1) {
476 load_addr
= (uint32_t)tp
->arg1
;
477 fsid
.val
[0] = (int32_t)tp
->arg2
;
478 fsid
.val
[1] = (int32_t)tp
->arg3
;
479 fsobjid
.fid_objno
= (uint32_t)tp
->arg4
;
481 T_QUIET
; T_EXPECT_EQ(load_addr
, (uint32_t)exp_load_addr
, NULL
);
482 T_QUIET
; T_EXPECT_EQ(fsid
.val
[0], exp_fsid
->val
[0], NULL
);
483 T_QUIET
; T_EXPECT_EQ(fsid
.val
[1], exp_fsid
->val
[1], NULL
);
484 T_QUIET
; T_EXPECT_EQ(fsobjid
.fid_objno
, exp_fsobjid
->fid_objno
, NULL
);
485 } else if (order
== 2) {
488 fsobjid
.fid_generation
= tp
->arg1
;
490 T_QUIET
; T_EXPECT_EQ(fsobjid
.fid_generation
,
491 exp_fsobjid
->fid_generation
, NULL
);
493 T_ASSERT_FAIL("unrecognized order of events %d", order
);
495 #endif /* defined(__LP64__) */
498 #if defined(__LP64__) || defined(__arm64__)
499 #define DYLD_CODE_OFFSET (0)
500 #define DYLD_EVENTS (2)
502 #define DYLD_CODE_OFFSET (2)
503 #define DYLD_EVENTS (3)
507 expect_dyld_events(ktrace_session_t s
, const char *name
, uint32_t base_code
,
508 const char *exp_uuid
, uint64_t exp_load_addr
, fsid_t
*exp_fsid
,
509 fsobj_id_t
*exp_fsobjid
, uint8_t *saw_events
)
511 for (int i
= 0; i
< DYLD_EVENTS
; i
++) {
512 ktrace_events_single(s
, KDBG_EVENTID(DBG_DYLD
, DBG_DYLD_UUID
,
513 base_code
+ DYLD_CODE_OFFSET
+ (unsigned int)i
),
514 ^(struct trace_point
*tp
) {
515 T_LOG("checking %s event %c", name
, 'A' + i
);
516 expect_dyld_image_info(tp
, (const void *)exp_uuid
, exp_load_addr
,
517 exp_fsid
, exp_fsobjid
, i
);
518 *saw_events
|= (1U << i
);
523 T_DECL(dyld_events
, "test that dyld registering libraries emits events")
525 dyld_kernel_image_info_t info
;
528 * Use pointers instead of __block variables in order to use these variables
529 * in the completion block below _and_ pass pointers to them to the
530 * expect_dyld_events function.
532 uint8_t saw_events
[3] = { 0 };
533 uint8_t *saw_mapping
= &(saw_events
[0]);
534 uint8_t *saw_unmapping
= &(saw_events
[1]);
535 uint8_t *saw_shared_cache
= &(saw_events
[2]);
537 start_controlling_ktrace();
539 ktrace_session_t s
= ktrace_session_create();
540 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_NOTNULL(s
, "created session");
543 T_ASSERT_POSIX_ZERO(ktrace_filter_pid(s
, getpid()),
544 "filtered to current process");
546 expect_dyld_events(s
, "mapping", DBG_DYLD_UUID_MAP_A
, map_uuid
,
547 MAP_LOAD_ADDR
, &map_fsid
, &map_fsobjid
, saw_mapping
);
548 expect_dyld_events(s
, "unmapping", DBG_DYLD_UUID_UNMAP_A
, unmap_uuid
,
549 UNMAP_LOAD_ADDR
, &unmap_fsid
, &unmap_fsobjid
, saw_unmapping
);
550 expect_dyld_events(s
, "shared cache", DBG_DYLD_UUID_SHARED_CACHE_A
,
551 sc_uuid
, SC_LOAD_ADDR
, &sc_fsid
, &sc_fsobjid
, saw_shared_cache
);
553 ktrace_set_completion_handler(s
, ^{
554 ktrace_session_destroy(s
);
556 T_EXPECT_EQ(__builtin_popcount(*saw_mapping
), DYLD_EVENTS
, NULL
);
557 T_EXPECT_EQ(__builtin_popcount(*saw_unmapping
), DYLD_EVENTS
, NULL
);
558 T_EXPECT_EQ(__builtin_popcount(*saw_shared_cache
), DYLD_EVENTS
, NULL
);
562 T_ASSERT_POSIX_ZERO(ktrace_start(s
, dispatch_get_main_queue()), NULL
);
564 info
.load_addr
= MAP_LOAD_ADDR
;
565 memcpy(info
.uuid
, map_uuid
, sizeof(info
.uuid
));
566 info
.fsid
= map_fsid
;
567 info
.fsobjid
= map_fsobjid
;
568 T_EXPECT_MACH_SUCCESS(task_register_dyld_image_infos(mach_task_self(),
569 &info
, 1), "registered dyld image info");
571 info
.load_addr
= UNMAP_LOAD_ADDR
;
572 memcpy(info
.uuid
, unmap_uuid
, sizeof(info
.uuid
));
573 info
.fsid
= unmap_fsid
;
574 info
.fsobjid
= unmap_fsobjid
;
575 T_EXPECT_MACH_SUCCESS(task_unregister_dyld_image_infos(mach_task_self(),
576 &info
, 1), "unregistered dyld image info");
578 info
.load_addr
= SC_LOAD_ADDR
;
579 memcpy(info
.uuid
, sc_uuid
, sizeof(info
.uuid
));
581 info
.fsobjid
= sc_fsobjid
;
582 T_EXPECT_MACH_SUCCESS(task_register_dyld_shared_cache_image_info(
583 mach_task_self(), info
, FALSE
, FALSE
),
584 "registered dyld shared cache image info");
591 #pragma mark kdebug kernel macros
593 #define EXP_KERNEL_EVENTS 5U
595 static const uint32_t dev_evts
[EXP_KERNEL_EVENTS
] = {
596 BSDDBG_CODE(DBG_BSD_KDEBUG_TEST
, 0),
597 BSDDBG_CODE(DBG_BSD_KDEBUG_TEST
, 1),
598 BSDDBG_CODE(DBG_BSD_KDEBUG_TEST
, 2),
599 BSDDBG_CODE(DBG_BSD_KDEBUG_TEST
, 3),
600 BSDDBG_CODE(DBG_BSD_KDEBUG_TEST
, 4),
603 static const uint32_t rel_evts
[EXP_KERNEL_EVENTS
] = {
604 BSDDBG_CODE(DBG_BSD_KDEBUG_TEST
, 5),
605 BSDDBG_CODE(DBG_BSD_KDEBUG_TEST
, 6),
606 BSDDBG_CODE(DBG_BSD_KDEBUG_TEST
, 7),
607 BSDDBG_CODE(DBG_BSD_KDEBUG_TEST
, 8),
608 BSDDBG_CODE(DBG_BSD_KDEBUG_TEST
, 9),
611 static const uint32_t filt_evts
[EXP_KERNEL_EVENTS
] = {
612 BSDDBG_CODE(DBG_BSD_KDEBUG_TEST
, 10),
613 BSDDBG_CODE(DBG_BSD_KDEBUG_TEST
, 11),
614 BSDDBG_CODE(DBG_BSD_KDEBUG_TEST
, 12),
615 BSDDBG_CODE(DBG_BSD_KDEBUG_TEST
, 13),
616 BSDDBG_CODE(DBG_BSD_KDEBUG_TEST
, 14),
619 static const uint32_t noprocfilt_evts
[EXP_KERNEL_EVENTS
] = {
620 BSDDBG_CODE(DBG_BSD_KDEBUG_TEST
, 15),
621 BSDDBG_CODE(DBG_BSD_KDEBUG_TEST
, 16),
622 BSDDBG_CODE(DBG_BSD_KDEBUG_TEST
, 17),
623 BSDDBG_CODE(DBG_BSD_KDEBUG_TEST
, 18),
624 BSDDBG_CODE(DBG_BSD_KDEBUG_TEST
, 19),
628 expect_event(struct trace_point
*tp
, const char *name
, unsigned int *events
,
629 const uint32_t *event_ids
, size_t event_ids_len
)
631 unsigned int event_idx
= *events
;
632 bool event_found
= false;
634 for (i
= 0; i
< event_ids_len
; i
++) {
635 if (event_ids
[i
] == (tp
->debugid
& KDBG_EVENTID_MASK
)) {
636 T_LOG("found %s event 0x%x", name
, tp
->debugid
);
646 for (i
= 0; i
< event_idx
; i
++) {
647 T_QUIET
; T_EXPECT_EQ(((uint64_t *)&tp
->arg1
)[i
], (uint64_t)i
+ 1,
651 T_QUIET
; T_EXPECT_EQ(((uint64_t *)&tp
->arg1
)[i
], (uint64_t)0, NULL
);
656 expect_release_event(struct trace_point
*tp
, unsigned int *events
)
658 expect_event(tp
, "release", events
, rel_evts
,
659 sizeof(rel_evts
) / sizeof(rel_evts
[0]));
663 expect_development_event(struct trace_point
*tp
, unsigned int *events
)
665 expect_event(tp
, "dev", events
, dev_evts
, sizeof(dev_evts
) / sizeof(dev_evts
[0]));
669 expect_filtered_event(struct trace_point
*tp
, unsigned int *events
)
671 expect_event(tp
, "filtered", events
, filt_evts
,
672 sizeof(filt_evts
) / sizeof(filt_evts
[0]));
676 expect_noprocfilt_event(struct trace_point
*tp
, unsigned int *events
)
678 expect_event(tp
, "noprocfilt", events
, noprocfilt_evts
,
679 sizeof(noprocfilt_evts
) / sizeof(noprocfilt_evts
[0]));
683 expect_kdbg_test_events(ktrace_session_t s
, bool use_all_callback
,
684 void (^cb
)(unsigned int dev_seen
, unsigned int rel_seen
,
685 unsigned int filt_seen
, unsigned int noprocfilt_seen
))
687 __block
unsigned int dev_seen
= 0;
688 __block
unsigned int rel_seen
= 0;
689 __block
unsigned int filt_seen
= 0;
690 __block
unsigned int noprocfilt_seen
= 0;
692 void (^evtcb
)(struct trace_point
*tp
) = ^(struct trace_point
*tp
) {
693 expect_development_event(tp
, &dev_seen
);
694 expect_release_event(tp
, &rel_seen
);
695 expect_filtered_event(tp
, &filt_seen
);
696 expect_noprocfilt_event(tp
, &noprocfilt_seen
);
699 if (use_all_callback
) {
700 ktrace_events_all(s
, evtcb
);
702 ktrace_events_range(s
, KDBG_EVENTID(DBG_BSD
, DBG_BSD_KDEBUG_TEST
, 0),
703 KDBG_EVENTID(DBG_BSD
+ 1, 0, 0), evtcb
);
706 ktrace_set_completion_handler(s
, ^{
707 ktrace_session_destroy(s
);
708 cb(dev_seen
, rel_seen
, filt_seen
, noprocfilt_seen
);
712 T_ASSERT_POSIX_ZERO(ktrace_start(s
, dispatch_get_main_queue()), NULL
);
713 assert_kdebug_test(KDBG_TEST_MACROS
);
718 T_DECL(kernel_events
, "ensure kernel macros work")
720 start_controlling_ktrace();
722 ktrace_session_t s
= ktrace_session_create();
723 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_NOTNULL(s
, "created session");
725 T_QUIET
; T_ASSERT_POSIX_ZERO(ktrace_filter_pid(s
, getpid()),
726 "filtered events to current process");
728 expect_kdbg_test_events(s
, false,
729 ^(unsigned int dev_seen
, unsigned int rel_seen
,
730 unsigned int filt_seen
, unsigned int noprocfilt_seen
) {
732 * Development-only events are only filtered if running on an embedded
735 unsigned int dev_exp
;
736 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
737 dev_exp
= is_development_kernel() ? EXP_KERNEL_EVENTS
: 0U;
739 dev_exp
= EXP_KERNEL_EVENTS
;
742 T_EXPECT_EQ(rel_seen
, EXP_KERNEL_EVENTS
,
743 "release and development events seen");
744 T_EXPECT_EQ(dev_seen
, dev_exp
, "development-only events %sseen",
745 dev_exp
? "" : "not ");
746 T_EXPECT_EQ(filt_seen
, dev_exp
, "filter-only events seen");
747 T_EXPECT_EQ(noprocfilt_seen
, EXP_KERNEL_EVENTS
,
748 "process filter-agnostic events seen");
754 T_DECL(kernel_events_filtered
, "ensure that the filtered kernel macros work")
756 start_controlling_ktrace();
758 ktrace_session_t s
= ktrace_session_create();
759 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_NOTNULL(s
, "created session");
761 T_QUIET
; T_ASSERT_POSIX_ZERO(ktrace_filter_pid(s
, getpid()),
762 "filtered events to current process");
764 expect_kdbg_test_events(s
, true,
765 ^(unsigned int dev_seen
, unsigned int rel_seen
,
766 unsigned int filt_seen
, unsigned int noprocfilt_seen
) {
767 T_EXPECT_EQ(rel_seen
, EXP_KERNEL_EVENTS
, NULL
);
768 #if defined(__arm__) || defined(__arm64__)
769 T_EXPECT_EQ(dev_seen
, is_development_kernel() ? EXP_KERNEL_EVENTS
: 0U,
772 T_EXPECT_EQ(dev_seen
, EXP_KERNEL_EVENTS
,
773 "development-only events seen");
774 #endif /* defined(__arm__) || defined(__arm64__) */
775 T_EXPECT_EQ(filt_seen
, 0U, "no filter-only events seen");
776 T_EXPECT_EQ(noprocfilt_seen
, EXP_KERNEL_EVENTS
,
777 "process filter-agnostic events seen");
783 T_DECL(kernel_events_noprocfilt
,
784 "ensure that the no process filter kernel macros work")
786 start_controlling_ktrace();
788 ktrace_session_t s
= ktrace_session_create();
789 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_NOTNULL(s
, "created session");
792 * Only allow launchd events through.
794 T_ASSERT_POSIX_ZERO(ktrace_filter_pid(s
, 1), "filtered events to launchd");
795 for (size_t i
= 0; i
< sizeof(noprocfilt_evts
) / sizeof(noprocfilt_evts
[0]); i
++) {
797 T_ASSERT_POSIX_ZERO(ktrace_ignore_process_filter_for_event(s
,
799 "ignored process filter for noprocfilt event");
802 expect_kdbg_test_events(s
, false,
803 ^(unsigned int dev_seen
, unsigned int rel_seen
,
804 unsigned int filt_seen
, unsigned int noprocfilt_seen
) {
805 T_EXPECT_EQ(rel_seen
, 0U, "release and development events not seen");
806 T_EXPECT_EQ(dev_seen
, 0U, "development-only events not seen");
807 T_EXPECT_EQ(filt_seen
, 0U, "filter-only events not seen");
809 T_EXPECT_EQ(noprocfilt_seen
, EXP_KERNEL_EVENTS
,
810 "process filter-agnostic events seen");
816 static volatile bool continue_abuse
= true;
818 #define STRESS_DEBUGID (0xfeedfac0)
819 #define ABUSE_SECS (2)
820 #define TIMER_NS (100 * NSEC_PER_USEC)
822 * Use the quantum as the gap threshold.
824 #define GAP_THRESHOLD_NS (10 * NSEC_PER_MSEC)
827 kdebug_abuser_thread(void *ctx
)
829 unsigned int id
= (unsigned int)ctx
;
831 while (continue_abuse
) {
832 kdebug_trace(STRESS_DEBUGID
, id
, i
, 0, 0);
839 T_DECL(stress
, "emit events on all but one CPU with a small buffer",
840 T_META_CHECK_LEAKS(false))
842 start_controlling_ktrace();
845 ktrace_session_t s
= ktrace_session_create();
846 T_WITH_ERRNO
; T_QUIET
; T_ASSERT_NOTNULL(s
, "ktrace_session_create");
848 /* Let's not waste any time with pleasantries. */
849 ktrace_set_uuid_map_enabled(s
, KTRACE_FEATURE_DISABLED
);
852 ktrace_events_all(s
, ^(__unused
struct trace_point
*tp
) {});
853 ktrace_set_vnode_paths_enabled(s
, KTRACE_FEATURE_ENABLED
);
854 (void)atexit_b(^{ kperf_reset(); });
855 (void)kperf_action_count_set(1);
856 (void)kperf_timer_count_set(1);
857 int kperror
= kperf_timer_period_set(0, kperf_ns_to_ticks(TIMER_NS
));
858 T_QUIET
; T_ASSERT_POSIX_SUCCESS(kperror
, "kperf_timer_period_set %llu ns",
860 kperror
= kperf_timer_action_set(0, 1);
861 T_QUIET
; T_ASSERT_POSIX_SUCCESS(kperror
, "kperf_timer_action_set");
862 kperror
= kperf_action_samplers_set(1, KPERF_SAMPLER_TINFO
|
863 KPERF_SAMPLER_TH_SNAPSHOT
| KPERF_SAMPLER_KSTACK
|
864 KPERF_SAMPLER_USTACK
| KPERF_SAMPLER_MEMINFO
|
865 KPERF_SAMPLER_TINFO_SCHED
| KPERF_SAMPLER_TH_DISPATCH
|
866 KPERF_SAMPLER_TK_SNAPSHOT
| KPERF_SAMPLER_SYS_MEM
|
867 KPERF_SAMPLER_TH_INSTRS_CYCLES
);
868 T_QUIET
; T_ASSERT_POSIX_SUCCESS(kperror
, "kperf_action_samplers_set");
871 /* The coup-de-grace. */
872 ktrace_set_buffer_size(s
, 10);
874 char filepath_arr
[MAXPATHLEN
] = "";
875 strlcpy(filepath_arr
, dt_tmpdir(), sizeof(filepath_arr
));
876 strlcat(filepath_arr
, "/stress.ktrace", sizeof(filepath_arr
));
877 char *filepath
= filepath_arr
;
880 size_t ncpus_size
= sizeof(ncpus
);
881 int ret
= sysctlbyname("hw.logicalcpu_max", &ncpus
, &ncpus_size
, NULL
, 0);
882 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "sysctlbyname(\"hw.logicalcpu_max\"");
883 T_QUIET
; T_ASSERT_GT(ncpus
, 0, "realistic number of CPUs");
885 pthread_t
*threads
= calloc((unsigned int)ncpus
- 1, sizeof(pthread_t
));
886 T_WITH_ERRNO
; T_QUIET
; T_ASSERT_NOTNULL(threads
, "calloc(%d threads)",
889 ktrace_set_completion_handler(s
, ^{
891 ktrace_session_destroy(s
);
893 T_LOG("trace ended, searching for gaps");
895 ktrace_session_t sread
= ktrace_session_create();
896 T_WITH_ERRNO
; T_QUIET
; T_ASSERT_NOTNULL(sread
, "ktrace_session_create");
898 int error
= ktrace_set_file(sread
, filepath
);
899 T_QUIET
; T_ASSERT_POSIX_ZERO(error
, "ktrace_set_file %s", filepath
);
901 ktrace_file_t f
= ktrace_file_open(filepath
, false);
902 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_NOTNULL(f
, "ktrace_file_open %s",
904 uint64_t first_timestamp
= 0;
905 error
= ktrace_file_earliest_timestamp(f
, &first_timestamp
);
906 T_QUIET
; T_ASSERT_POSIX_ZERO(error
, "ktrace_file_earliest_timestamp");
908 uint64_t last_timestamp
= 0;
909 (void)ktrace_file_latest_timestamp(f
, &last_timestamp
);
911 __block
uint64_t prev_timestamp
= 0;
912 __block
uint64_t nevents
= 0;
913 ktrace_events_all(sread
, ^(struct trace_point
*tp
) {
915 uint64_t delta_ns
= 0;
916 T_QUIET
; T_EXPECT_GE(tp
->timestamp
, prev_timestamp
,
917 "timestamps are monotonically increasing");
918 int converror
= ktrace_convert_timestamp_to_nanoseconds(sread
,
919 tp
->timestamp
- prev_timestamp
, &delta_ns
);
920 T_QUIET
; T_ASSERT_POSIX_ZERO(converror
, "convert timestamp to ns");
921 if (prev_timestamp
&& delta_ns
> GAP_THRESHOLD_NS
) {
923 T_LOG("gap: %gs at %llu - %llu on %d: %s (%#08x)",
924 (double)delta_ns
/ 1e9
, prev_timestamp
,
925 tp
->timestamp
, tp
->cpuid
, tp
->debugname
, tp
->debugid
);
927 T_LOG("gap: %gs at %llu - %llu on %d: %#x",
928 (double)delta_ns
/ 1e9
, prev_timestamp
,
929 tp
->timestamp
, tp
->cpuid
, tp
->debugid
);
933 * These gaps are ok -- they appear after CPUs are brought back
936 #define INTERRUPT (0x1050000)
937 #define PERF_CPU_IDLE (0x27001000)
938 #define INTC_HANDLER (0x5000004)
939 #define DECR_TRAP (0x1090000)
940 uint32_t eventid
= tp
->debugid
& KDBG_EVENTID_MASK
;
941 if (eventid
!= INTERRUPT
&& eventid
!= PERF_CPU_IDLE
&&
942 eventid
!= INTC_HANDLER
&& eventid
!= DECR_TRAP
) {
943 unsigned int lost_events
= TRACE_LOST_EVENTS
;
944 T_QUIET
; T_EXPECT_EQ(tp
->debugid
, lost_events
,
945 "gaps should end with lost events");
949 prev_timestamp
= tp
->timestamp
;
951 ktrace_events_single(sread
, TRACE_LOST_EVENTS
, ^(struct trace_point
*tp
){
952 T_LOG("lost: %llu on %d (%llu)", tp
->timestamp
, tp
->cpuid
, tp
->arg1
);
955 __block
uint64_t last_write
= 0;
956 ktrace_events_single_paired(sread
, TRACE_WRITING_EVENTS
,
957 ^(struct trace_point
*start
, struct trace_point
*end
) {
959 int converror
= ktrace_convert_timestamp_to_nanoseconds(sread
,
960 start
->timestamp
- last_write
, &delta_ns
);
961 T_QUIET
; T_ASSERT_POSIX_ZERO(converror
, "convert timestamp to ns");
964 converror
= ktrace_convert_timestamp_to_nanoseconds(sread
,
965 end
->timestamp
- start
->timestamp
, &dur_ns
);
966 T_QUIET
; T_ASSERT_POSIX_ZERO(converror
, "convert timestamp to ns");
968 T_LOG("write: %llu (+%gs): %gus on %d: %llu events", start
->timestamp
,
969 (double)delta_ns
/ 1e9
, (double)dur_ns
/ 1e3
, end
->cpuid
, end
->arg1
);
970 last_write
= end
->timestamp
;
972 ktrace_set_completion_handler(sread
, ^{
973 uint64_t duration_ns
= 0;
974 if (last_timestamp
) {
975 int converror
= ktrace_convert_timestamp_to_nanoseconds(sread
,
976 last_timestamp
- first_timestamp
, &duration_ns
);
977 T_QUIET
; T_ASSERT_POSIX_ZERO(converror
,
978 "convert timestamp to ns");
979 T_LOG("file was %gs long, %llu events: %g events/msec/cpu",
980 (double)duration_ns
/ 1e9
, nevents
,
981 (double)nevents
/ ((double)duration_ns
/ 1e6
) / ncpus
);
983 (void)unlink(filepath
);
984 ktrace_session_destroy(sread
);
988 int starterror
= ktrace_start(sread
, dispatch_get_main_queue());
989 T_QUIET
; T_ASSERT_POSIX_ZERO(starterror
, "ktrace_start read session");
994 /* Just kidding... for now. */
996 kperror
= kperf_sample_set(1);
997 T_ASSERT_POSIX_SUCCESS(kperror
,
998 "started kperf timer sampling every %llu ns", TIMER_NS
);
1001 for (int i
= 0; i
< (ncpus
- 1); i
++) {
1002 int error
= pthread_create(&threads
[i
], NULL
, kdebug_abuser_thread
,
1003 (void *)(uintptr_t)i
);
1004 T_QUIET
; T_ASSERT_POSIX_ZERO(error
,
1005 "pthread_create abuser thread %d", i
);
1008 int error
= ktrace_start_writing_file(s
, filepath
,
1009 ktrace_compression_none
, NULL
, NULL
);
1010 T_ASSERT_POSIX_ZERO(error
, "started writing ktrace to %s", filepath
);
1014 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, ABUSE_SECS
* NSEC_PER_SEC
),
1015 dispatch_get_main_queue(), ^{
1016 T_LOG("ending trace");
1019 continue_abuse
= false;
1020 for (int i
= 0; i
< (ncpus
- 1); i
++) {
1021 int joinerror
= pthread_join(threads
[i
], NULL
);
1022 T_QUIET
; T_EXPECT_POSIX_ZERO(joinerror
, "pthread_join thread %d",
1030 #define ROUND_TRIP_PERIOD UINT64_C(10 * 1000)
1031 #define ROUND_TRIPS_THRESHOLD UINT64_C(25)
1032 #define ROUND_TRIPS_TIMEOUT_SECS (2 * 60)
1033 #define COLLECTION_INTERVAL_MS 100
1036 * Test a sustained tracing session, involving multiple round-trips to the
1039 * Trace all events, and every `ROUND_TRIP_PERIOD` events, emit an event that's
1040 * unlikely to be emitted elsewhere. Look for this event, too, and make sure we
1041 * see as many of them as we emitted.
1043 * After seeing `ROUND_TRIPS_THRESHOLD` of the unlikely events, end tracing.
1044 * In the failure mode, we won't see any of these, so set a timeout of
1045 * `ROUND_TRIPS_TIMEOUT_SECS` to prevent hanging, waiting for events that we'll
1049 "test sustained tracing with multiple round-trips through the kernel")
1051 start_controlling_ktrace();
1053 ktrace_session_t s
= ktrace_session_create();
1054 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_NOTNULL(s
, "created session");
1057 * Set a small buffer and collection interval to increase the number of
1060 ktrace_set_buffer_size(s
, 50);
1061 ktrace_set_collection_interval(s
, COLLECTION_INTERVAL_MS
);
1063 __block
uint64_t events
= 0;
1064 __block
uint64_t emitted
= 0;
1065 __block
uint64_t seen
= 0;
1066 ktrace_events_all(s
, ^(__unused
struct trace_point
*tp
) {
1068 if (events
% ROUND_TRIP_PERIOD
== 0) {
1069 T_LOG("emitting round-trip event %" PRIu64
, emitted
);
1070 kdebug_trace(TRACE_DEBUGID
, events
, 0, 0, 0);
1075 ktrace_events_single(s
, TRACE_DEBUGID
, ^(__unused
struct trace_point
*tp
) {
1076 T_LOG("saw round-trip event after %" PRIu64
" events", events
);
1078 if (seen
>= ROUND_TRIPS_THRESHOLD
) {
1079 T_LOG("ending trace after seeing %" PRIu64
" events, "
1080 "emitting %" PRIu64
, seen
, emitted
);
1085 ktrace_set_completion_handler(s
, ^{
1086 T_EXPECT_GE(emitted
, ROUND_TRIPS_THRESHOLD
,
1087 "emitted %" PRIu64
" round-trip events", emitted
);
1088 T_EXPECT_GE(seen
, ROUND_TRIPS_THRESHOLD
,
1089 "saw %" PRIu64
" round-trip events", seen
);
1090 ktrace_session_destroy(s
);
1094 int error
= ktrace_start(s
, dispatch_get_main_queue());
1095 T_ASSERT_POSIX_ZERO(error
, "started tracing");
1097 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
,
1098 ROUND_TRIPS_TIMEOUT_SECS
* NSEC_PER_SEC
), dispatch_get_main_queue(),
1100 T_LOG("ending trace after %d seconds", ROUND_TRIPS_TIMEOUT_SECS
);
1107 #define HEARTBEAT_INTERVAL_SECS 2
1108 #define HEARTBEAT_COUNT 20
1111 * Ensure we see events periodically, checking for recent events on a
1114 T_DECL(event_coverage
, "ensure events appear up to the end of tracing")
1116 start_controlling_ktrace();
1118 ktrace_session_t s
= ktrace_session_create();
1119 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_NOTNULL(s
, "created session");
1121 __block
uint64_t current_timestamp
= 0;
1122 __block
uint64_t events
= 0;
1123 ktrace_events_all(s
, ^(struct trace_point
*tp
) {
1124 current_timestamp
= tp
->timestamp
;
1128 ktrace_set_buffer_size(s
, 20);
1129 ktrace_set_collection_interval(s
, COLLECTION_INTERVAL_MS
);
1131 __block
uint64_t last_timestamp
= 0;
1132 __block
uint64_t last_events
= 0;
1133 __block
unsigned int heartbeats
= 0;
1135 ktrace_set_completion_handler(s
, ^{
1136 ktrace_session_destroy(s
);
1137 T_QUIET
; T_EXPECT_GT(events
, 0ULL, "should have seen some events");
1141 dispatch_source_t timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
,
1142 0, 0, dispatch_get_main_queue());
1143 dispatch_source_set_timer(timer
, dispatch_time(DISPATCH_TIME_NOW
,
1144 HEARTBEAT_INTERVAL_SECS
* NSEC_PER_SEC
),
1145 HEARTBEAT_INTERVAL_SECS
* NSEC_PER_SEC
, 0);
1146 dispatch_source_set_cancel_handler(timer
, ^{
1147 dispatch_release(timer
);
1150 dispatch_source_set_event_handler(timer
, ^{
1153 T_LOG("heartbeat %u at time %lld, seen %" PRIu64
" events, "
1154 "current event time %lld", heartbeats
, mach_absolute_time(),
1155 events
, current_timestamp
);
1157 if (current_timestamp
> 0) {
1158 T_EXPECT_GT(current_timestamp
, last_timestamp
,
1159 "event timestamps should be increasing");
1160 T_QUIET
; T_EXPECT_GT(events
, last_events
,
1161 "number of events should be increasing");
1164 last_timestamp
= current_timestamp
;
1165 last_events
= events
;
1167 if (heartbeats
>= HEARTBEAT_COUNT
) {
1168 T_LOG("ending trace after %u heartbeats", HEARTBEAT_COUNT
);
1173 int error
= ktrace_start(s
, dispatch_get_main_queue());
1174 T_ASSERT_POSIX_ZERO(error
, "started tracing");
1176 dispatch_activate(timer
);
1182 set_nevents(unsigned int nevents
)
1185 T_ASSERT_POSIX_SUCCESS(sysctl(
1186 (int[]){ CTL_KERN
, KERN_KDEBUG
, KERN_KDSETBUF
, (int)nevents
}, 4,
1187 NULL
, 0, NULL
, 0), "set kdebug buffer size");
1190 T_ASSERT_POSIX_SUCCESS(sysctl(
1191 (int[]){ CTL_KERN
, KERN_KDEBUG
, KERN_KDSETUP
, (int)nevents
}, 4,
1192 NULL
, 0, NULL
, 0), "setup kdebug buffers");
1194 kbufinfo_t bufinfo
= { 0 };
1196 T_ASSERT_POSIX_SUCCESS(sysctl(
1197 (int[]){ CTL_KERN
, KERN_KDEBUG
, KERN_KDGETBUF
}, 3,
1198 &bufinfo
, &(size_t){ sizeof(bufinfo
) }, NULL
, 0),
1199 "get kdebug buffer size");
1202 T_ASSERT_POSIX_SUCCESS(sysctl(
1203 (int[]){ CTL_KERN
, KERN_KDEBUG
, KERN_KDREMOVE
}, 3,
1205 "remove kdebug buffers");
1207 return (unsigned int)bufinfo
.nkdbufs
;
1210 T_DECL(set_buffer_size
, "ensure large buffer sizes can be set")
1212 start_controlling_ktrace();
1214 uint64_t memsize
= 0;
1215 T_QUIET
; T_ASSERT_POSIX_SUCCESS(sysctlbyname("hw.memsize", &memsize
,
1216 &(size_t){ sizeof(memsize
) }, NULL
, 0), "get memory size");
1219 * Try to allocate up to one-eighth of available memory towards
1222 uint64_t maxevents_u64
= memsize
/ 8 / sizeof(kd_buf
);
1223 if (maxevents_u64
> UINT32_MAX
) {
1224 maxevents_u64
= UINT32_MAX
;
1226 unsigned int maxevents
= (unsigned int)maxevents_u64
;
1228 unsigned int minevents
= set_nevents(0);
1229 T_ASSERT_GT(minevents
, 0, "saw non-zero minimum event count of %u",
1232 unsigned int step
= ((maxevents
- minevents
- 1) / 4);
1233 T_ASSERT_GT(step
, 0, "stepping by %u events", step
);
1235 for (unsigned int i
= minevents
+ step
; i
< maxevents
; i
+= step
) {
1236 unsigned int actualevents
= set_nevents(i
);
1237 T_ASSERT_GE(actualevents
, i
- minevents
,
1238 "%u events in kernel when %u requested", actualevents
, i
);
1243 donothing(__unused
void *arg
)
1248 T_DECL(long_names
, "ensure long command names are reported")
1250 start_controlling_ktrace();
1252 char longname
[] = "thisisaverylongprocessname!";
1253 char *longname_ptr
= longname
;
1254 static_assert(sizeof(longname
) > 16,
1255 "the name should be longer than MAXCOMLEN");
1257 int ret
= sysctlbyname("kern.procname", NULL
, NULL
, longname
,
1259 T_ASSERT_POSIX_SUCCESS(ret
,
1260 "use sysctl kern.procname to lengthen the name");
1262 ktrace_session_t ktsess
= ktrace_session_create();
1265 * 32-bit kernels can only trace 16 bytes of the string in their event
1268 if (!ktrace_is_kernel_64_bit(ktsess
)) {
1269 longname
[16] = '\0';
1272 ktrace_filter_pid(ktsess
, getpid());
1274 __block
bool saw_newthread
= false;
1275 ktrace_events_single(ktsess
, TRACE_STRING_NEWTHREAD
,
1276 ^(struct trace_point
*tp
) {
1277 if (ktrace_get_pid_for_thread(ktsess
, tp
->threadid
) ==
1279 saw_newthread
= true;
1281 char argname
[32] = {};
1282 strncat(argname
, (char *)&tp
->arg1
, sizeof(tp
->arg1
));
1283 strncat(argname
, (char *)&tp
->arg2
, sizeof(tp
->arg2
));
1284 strncat(argname
, (char *)&tp
->arg3
, sizeof(tp
->arg3
));
1285 strncat(argname
, (char *)&tp
->arg4
, sizeof(tp
->arg4
));
1287 T_EXPECT_EQ_STR((char *)argname
, longname_ptr
,
1288 "process name of new thread should be long");
1290 ktrace_end(ktsess
, 1);
1294 ktrace_set_completion_handler(ktsess
, ^{
1295 ktrace_session_destroy(ktsess
);
1296 T_EXPECT_TRUE(saw_newthread
,
1297 "should have seen the new thread");
1301 int error
= ktrace_start(ktsess
, dispatch_get_main_queue());
1302 T_ASSERT_POSIX_ZERO(error
, "started tracing");
1304 pthread_t thread
= NULL
;
1305 error
= pthread_create(&thread
, NULL
, donothing
, NULL
);
1306 T_ASSERT_POSIX_ZERO(error
, "create new thread");
1308 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, 5 * NSEC_PER_SEC
),
1309 dispatch_get_main_queue(), ^{
1310 ktrace_end(ktsess
, 0);
1313 error
= pthread_join(thread
, NULL
);
1314 T_ASSERT_POSIX_ZERO(error
, "join to thread");
1319 T_DECL(continuous_time
, "make sure continuous time status can be queried",
1320 T_META_RUN_CONCURRENTLY(true))
1322 bool cont_time
= kdebug_using_continuous_time();
1323 T_ASSERT_FALSE(cont_time
, "should not be using continuous time yet");
1326 static const uint32_t frame_eventid
= KDBG_EVENTID(DBG_BSD
,
1327 DBG_BSD_KDEBUG_TEST
, 1);
1329 static ktrace_session_t
1330 future_events_session(void)
1332 ktrace_session_t ktsess
= ktrace_session_create();
1333 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_NOTNULL(ktsess
, "failed to create session");
1335 ktrace_events_single(ktsess
, KDBG_EVENTID(DBG_BSD
, DBG_BSD_KDEBUG_TEST
, 0),
1336 ^(struct trace_point
*tp __unused
) {
1337 T_FAIL("saw future test event from IOP");
1339 ktrace_events_single(ktsess
, frame_eventid
, ^(struct trace_point
*tp
) {
1340 if (tp
->debugid
& DBG_FUNC_START
) {
1341 T_LOG("saw start event");
1343 T_LOG("saw event traced after trying to trace future event, ending");
1344 ktrace_end(ktsess
, 1);
1348 ktrace_set_collection_interval(ktsess
, 100);
1352 T_DECL(future_iop_events
,
1353 "make sure IOPs cannot trace events in the future while live tracing")
1355 start_controlling_ktrace();
1356 ktrace_session_t ktsess
= future_events_session();
1357 ktrace_set_completion_handler(ktsess
, ^{
1358 ktrace_session_destroy(ktsess
);
1362 T_ASSERT_POSIX_ZERO(ktrace_start(ktsess
, dispatch_get_main_queue()),
1364 kdebug_trace(frame_eventid
| DBG_FUNC_START
, 0, 0, 0, 0);
1365 assert_kdebug_test(KDBG_TEST_FUTURE_TIMES
);
1366 kdebug_trace(frame_eventid
| DBG_FUNC_END
, 0, 0, 0, 0);
1368 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, 5 * NSEC_PER_SEC
),
1369 dispatch_get_main_queue(), ^{
1370 T_FAIL("ending tracing after timeout");
1371 ktrace_end(ktsess
, 0);
1377 T_DECL(future_iop_events_disabled
,
1378 "make sure IOPs cannot trace events in the future after disabling tracing")
1380 start_controlling_ktrace();
1381 ktrace_session_t ktsess
= future_events_session();
1382 T_ASSERT_POSIX_ZERO(ktrace_configure(ktsess
), "configure tracing");
1384 kdebug_trace(frame_eventid
| DBG_FUNC_START
, 0, 0, 0, 0);
1385 assert_kdebug_test(KDBG_TEST_FUTURE_TIMES
);
1386 kdebug_trace(frame_eventid
| DBG_FUNC_END
, 0, 0, 0, 0);
1388 T_ASSERT_POSIX_ZERO(ktrace_disable_configured(ktsess
),
1390 ktrace_session_destroy(ktsess
);
1392 ktsess
= future_events_session();
1394 T_ASSERT_POSIX_ZERO(ktrace_set_use_existing(ktsess
), "use existing trace");
1395 ktrace_set_completion_handler(ktsess
, ^{
1396 ktrace_session_destroy(ktsess
);
1400 T_ASSERT_POSIX_ZERO(ktrace_start(ktsess
, dispatch_get_main_queue()),
1401 "start tracing existing session");
1406 T_DECL(iop_events_disable
,
1407 "make sure IOP events are flushed before disabling trace")
1409 start_controlling_ktrace();
1410 ktrace_session_t ktsess
= future_events_session();
1412 assert_kdebug_test(KDBG_TEST_IOP_SYNC_FLUSH
);
1413 T_ASSERT_POSIX_ZERO(ktrace_configure(ktsess
), "configure tracing");
1415 kdebug_trace(frame_eventid
| DBG_FUNC_START
, 0, 0, 0, 0);
1417 T_ASSERT_POSIX_ZERO(ktrace_disable_configured(ktsess
),
1419 ktrace_session_destroy(ktsess
);
1421 ktsess
= ktrace_session_create();
1422 T_QUIET
; T_WITH_ERRNO
;
1423 T_ASSERT_NOTNULL(ktsess
, "create session");
1425 ktrace_events_single(ktsess
,
1426 KDBG_EVENTID(DBG_BSD
, DBG_BSD_KDEBUG_TEST
, 0xff),
1427 ^(struct trace_point
*tp __unused
) {
1428 T_PASS("saw IOP event from sync flush");
1432 T_ASSERT_POSIX_ZERO(ktrace_set_use_existing(ktsess
), "use existing trace");
1433 ktrace_set_completion_handler(ktsess
, ^{
1434 ktrace_session_destroy(ktsess
);
1438 T_ASSERT_POSIX_ZERO(ktrace_start(ktsess
, dispatch_get_main_queue()),
1439 "start tracing existing session");
1444 T_DECL(lookup_long_paths
,
1445 "lookup long path names")
1447 start_controlling_ktrace();
1449 int ret
= chdir("/tmp");
1450 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "chdir to /tmp");
1451 const char *dir
= "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/";
1454 ret
= mkdir(dir
, S_IRUSR
| S_IWUSR
| S_IXUSR
);
1455 if (ret
>= 0 || errno
!= EEXIST
) {
1456 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "mkdir of %d nested directory",
1460 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "chdir to %d nested directory", i
);
1463 ktrace_session_t s
= ktrace_session_create();
1464 ktrace_set_collection_interval(s
, 250);
1465 ktrace_filter_pid(s
, getpid());
1466 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_NOTNULL(s
, "created session");
1467 ktrace_events_single(s
, VFS_LOOKUP
, ^(struct trace_point
*tp __unused
){});
1468 ktrace_set_vnode_paths_enabled(s
, KTRACE_FEATURE_ENABLED
);
1470 dispatch_queue_t q
= dispatch_queue_create("com.apple.kdebug-test", 0);
1472 ktrace_set_completion_handler(s
, ^{
1473 dispatch_release(q
);
1477 int error
= ktrace_start(s
, q
);
1478 T_ASSERT_POSIX_ZERO(error
, "started tracing");
1480 int fd
= open("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", O_RDWR
| O_CREAT
);
1481 T_ASSERT_POSIX_SUCCESS(fd
, "opened file at %d directories deep", i
);
1485 T_LOG("ending tracing");
1490 #pragma mark - boot tracing
1492 static const char *expected_subsystems
[] = {
1493 "tunables", "locks_early", "kprintf", "pmap_steal", "vm_kernel",
1494 "kmem", "kmem_alloc", "zalloc",
1495 /* "percpu", only has a startup phase on Intel */
1496 "locks", "codesigning", "oslog", "early_boot",
1498 #define EXPECTED_SUBSYSTEMS_LEN \
1499 (sizeof(expected_subsystems) / sizeof(expected_subsystems[0]))
1501 T_DECL(early_boot_tracing
, "ensure early boot strings are present",
1502 T_META_BOOTARGS_SET("trace=100000"))
1504 T_ATEND(reset_ktrace
);
1507 ktrace_session_t s
= ktrace_session_create();
1508 ktrace_set_collection_interval(s
, 250);
1509 int error
= ktrace_set_use_existing(s
);
1510 T_ASSERT_POSIX_ZERO(error
, "use existing trace buffer");
1512 #if defined(__x86_64__)
1513 #define FIRST_EVENT_STRING "i386_init"
1514 #else /* defined(__x86_64__) */
1515 #define FIRST_EVENT_STRING "kernel_startup_bootstrap"
1516 #endif /* !defined(__x86_64__) */
1518 __block
bool seen_event
= false;
1519 __block
unsigned int cur_subsystem
= 0;
1520 ktrace_events_single(s
, TRACE_INFO_STRING
, ^(struct trace_point
*tp
) {
1521 char early_str
[33] = "";
1522 size_t argsize
= ktrace_is_kernel_64_bit(s
) ? 8 : 4;
1523 memcpy(early_str
, &tp
->arg1
, argsize
);
1524 memcpy(early_str
+ argsize
, &tp
->arg2
, argsize
);
1525 memcpy(early_str
+ argsize
* 2, &tp
->arg3
, argsize
);
1526 memcpy(early_str
+ argsize
* 3, &tp
->arg4
, argsize
);
1529 T_LOG("found first string event with args: "
1530 "0x%" PRIx64
", 0x%" PRIx64
", 0x%" PRIx64
", 0x%" PRIx64
,
1531 tp
->arg1
, tp
->arg2
, tp
->arg3
, tp
->arg4
);
1532 char expect_str
[33] = FIRST_EVENT_STRING
;
1533 if (!ktrace_is_kernel_64_bit(s
)) {
1534 // Only the first 16 bytes of the string will be traced.
1535 expect_str
[16] = '\0';
1538 T_EXPECT_EQ_STR(early_str
, expect_str
,
1539 "first event in boot trace should be the bootstrap message");
1543 if (strcmp(early_str
, expected_subsystems
[cur_subsystem
]) == 0) {
1544 T_LOG("found log for subsystem %s",
1545 expected_subsystems
[cur_subsystem
]);
1549 if (cur_subsystem
== EXPECTED_SUBSYSTEMS_LEN
) {
1550 T_LOG("ending after seeing all expected logs");
1555 ktrace_set_completion_handler(s
, ^{
1556 T_EXPECT_TRUE(seen_event
, "should see an early boot string event");
1557 T_EXPECT_TRUE(cur_subsystem
== EXPECTED_SUBSYSTEMS_LEN
,
1558 "should see logs from all subsystems");
1559 if (cur_subsystem
!= EXPECTED_SUBSYSTEMS_LEN
) {
1560 T_LOG("missing log for %s", expected_subsystems
[cur_subsystem
]);
1565 error
= ktrace_start(s
, dispatch_get_main_queue());
1566 T_ASSERT_POSIX_ZERO(error
, "started tracing");
1573 T_DECL(typefilter_boot_arg
, "ensure typefilter is set up correctly at boot",
1574 T_META_BOOTARGS_SET("trace=100000 trace_typefilter=S0x0c00,C0xfe"))
1576 T_ATEND(reset_ktrace
);
1579 ktrace_config_t config
= ktrace_config_create_current();
1580 T_QUIET
; T_WITH_ERRNO
;
1581 T_ASSERT_NOTNULL(config
, "create config from current system");
1584 T_LOG("ktrace configuration:");
1585 ktrace_config_print_description(config
, stdout
);
1587 uint8_t *typefilt
= ktrace_config_kdebug_get_typefilter(config
);
1588 T_ASSERT_NOTNULL(typefilt
, "typefilter is active");
1589 T_EXPECT_TRUE(typefilt
[0x0c00 / 8],
1590 "specified subclass is set in typefilter");
1591 T_MAYFAIL
; // rdar://63625062 (UTD converts commas in boot-args to spaces)
1592 T_EXPECT_TRUE(typefilt
[0xfeed / 8],
1593 "specified class is set in typefilter");
1595 ktrace_config_destroy(config
);
1598 #pragma mark - events present
1600 static int recvd_sigchild
= 0;
1604 if (sig
!= SIGCHLD
) {
1605 T_ASSERT_FAIL("unexpected signal: %d", sig
);
1610 T_DECL(instrs_and_cycles_on_proc_exit
,
1611 "instructions and cycles should be traced on thread exit",
1612 T_META_REQUIRES_SYSCTL_EQ("kern.monotonic.supported", 1))
1615 start_controlling_ktrace();
1617 struct rusage_info_v4
*rusage
= calloc(1, sizeof(*rusage
));
1618 char *args
[] = { "ls", "-l", NULL
, };
1620 dispatch_queue_t q
= dispatch_queue_create("com.apple.kdebug-test",
1621 DISPATCH_QUEUE_SERIAL
);
1622 T_QUIET
; T_ASSERT_POSIX_SUCCESS(signal(SIGCHLD
, sighandler
),
1623 "register signal handler");
1625 ktrace_session_t s
= ktrace_session_create();
1626 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_NOTNULL(s
, "ktrace_session_create");
1629 __block
bool seen_event
= false;
1630 __block
uint64_t proc_instrs
= 0;
1631 __block
uint64_t proc_cycles
= 0;
1632 __block
uint64_t proc_sys_time
= 0;
1633 __block
uint64_t proc_usr_time
= 0;
1634 error
= ktrace_events_single(s
, DBG_MT_INSTRS_CYCLES_PROC_EXIT
,
1635 ^(ktrace_event_t tp
){
1636 if (tp
->pid
== pid
) {
1638 proc_instrs
= tp
->arg1
;
1639 proc_cycles
= tp
->arg2
;
1640 proc_sys_time
= tp
->arg3
;
1641 proc_usr_time
= tp
->arg4
;
1645 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_POSIX_ZERO(error
, "trace single event");
1646 ktrace_set_completion_handler(s
, ^{
1647 // TODO Check for equality once rdar://61948669 is fixed.
1648 T_ASSERT_GE(proc_instrs
, rusage
->ri_instructions
,
1649 "trace event instrs are >= to rusage instrs");
1650 T_ASSERT_GE(proc_cycles
, rusage
->ri_cycles
,
1651 "trace event cycles are >= to rusage cycles");
1652 T_ASSERT_GE(proc_sys_time
, rusage
->ri_system_time
,
1653 "trace event sys time is >= rusage sys time");
1654 T_ASSERT_GE(proc_usr_time
, rusage
->ri_user_time
,
1655 "trace event usr time >= rusage usr time");
1656 T_EXPECT_TRUE(seen_event
, "should see the proc exit trace event");
1659 ktrace_session_destroy(s
);
1660 dispatch_release(q
);
1663 error
= ktrace_start(s
, q
);
1664 T_ASSERT_POSIX_ZERO(error
, "start tracing");
1667 extern char **environ
;
1668 status
= posix_spawnp(&pid
, args
[0], NULL
, NULL
, args
, environ
);
1669 T_QUIET
; T_ASSERT_POSIX_SUCCESS(status
, "spawn process");
1671 while (!recvd_sigchild
) {
1674 error
= proc_pid_rusage(pid
, RUSAGE_INFO_V4
, (rusage_info_t
)rusage
);
1675 T_QUIET
; T_ASSERT_POSIX_ZERO(error
, "rusage");
1676 error
= waitpid(pid
, &status
, 0);
1677 T_QUIET
; T_ASSERT_POSIX_SUCCESS(error
, "waitpid");
1682 #define NO_OF_THREADS 2
1684 struct thread_counters_info
{
1685 uint64_t counts
[2]; //cycles and/or instrs
1689 typedef struct thread_counters_info
*tc_info_t
;
1692 get_thread_counters(void* ptr
)
1694 extern int thread_selfcounts(int type
, void *buf
, size_t nbytes
);
1695 extern uint64_t __thread_selfusage(void);
1696 extern uint64_t __thread_selfid(void);
1697 tc_info_t tc_info
= (tc_info_t
) ptr
;
1698 tc_info
->thread_id
= __thread_selfid();
1699 // Just to increase the instr, cycle count
1700 T_LOG("printing %llu\n", tc_info
->thread_id
);
1701 tc_info
->cpu_time
= __thread_selfusage();
1702 thread_selfcounts(1, tc_info
->counts
, sizeof(tc_info
->counts
));
1706 T_DECL(instrs_and_cycles_on_thread_exit
,
1707 "instructions and cycles should be traced on thread exit",
1708 T_META_REQUIRES_SYSCTL_EQ("kern.monotonic.supported", 1))
1711 start_controlling_ktrace();
1714 pthread_t
*threads
= calloc((unsigned int)(NO_OF_THREADS
),
1716 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_NOTNULL(threads
, "calloc(%d threads)",
1718 tc_info_t tc_infos
= calloc((unsigned int) (NO_OF_THREADS
),
1719 sizeof(struct thread_counters_info
));
1720 T_WITH_ERRNO
; T_QUIET
; T_ASSERT_NOTNULL(tc_infos
,
1721 "calloc(%d thread counters)", NO_OF_THREADS
);
1723 ktrace_session_t s
= ktrace_session_create();
1724 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_NOTNULL(s
, "ktrace_session_create");
1725 ktrace_filter_pid(s
, getpid());
1727 __block
int nevents
= 0;
1728 error
= ktrace_events_single(s
, DBG_MT_INSTRS_CYCLES_THR_EXIT
,
1729 ^(ktrace_event_t tp
) {
1730 for (int i
= 0; i
< NO_OF_THREADS
; i
++) {
1731 if (tp
->threadid
== tc_infos
[i
].thread_id
) {
1733 uint64_t cpu_time
= tp
->arg3
+ tp
->arg4
;
1735 * as we are getting counts before thread exit,
1736 * the counts at thread exit should be greater than
1739 T_ASSERT_GE(tp
->arg1
, tc_infos
[i
].counts
[0],
1740 "trace event instrs are >= to thread's instrs");
1741 T_ASSERT_GE(tp
->arg2
, tc_infos
[i
].counts
[1],
1742 "trace event cycles are >= to thread's cycles");
1743 T_ASSERT_GE(cpu_time
, tc_infos
[i
].cpu_time
,
1744 "trace event cpu time is >= thread's cpu time");
1746 if (nevents
== NO_OF_THREADS
) {
1751 T_QUIET
; T_ASSERT_POSIX_ZERO(error
, "trace single event");
1752 ktrace_set_completion_handler(s
, ^{
1753 T_EXPECT_EQ(NO_OF_THREADS
, nevents
, "seen %d thread exit trace events",
1756 ktrace_session_destroy(s
);
1759 error
= ktrace_start(s
, dispatch_get_main_queue());
1760 T_ASSERT_POSIX_ZERO(error
, "start tracing");
1762 for (int i
= 0; i
< NO_OF_THREADS
; i
++) {
1763 error
= pthread_create(&threads
[i
], NULL
, get_thread_counters
,
1764 (void *)&tc_infos
[i
]);
1765 T_QUIET
; T_ASSERT_POSIX_ZERO(error
, "pthread_create thread %d", i
);
1768 for (int i
= 0; i
< NO_OF_THREADS
; i
++) {
1769 error
= pthread_join(threads
[i
], NULL
);
1770 T_QUIET
; T_EXPECT_POSIX_ZERO(error
, "pthread_join thread %d", i
);