4 #include <darwintest.h>
5 #include <darwintest_utils.h>
7 #include <sys/kdebug.h>
8 #include <ktrace/session.h>
12 #include <stdatomic.h>
15 T_META_NAMESPACE("xnu.perf"),
17 T_META_LTEPHASE(LTE_SINGLEUSER
),
21 #define TEST_TIMEOUT 3600 * (NSEC_PER_SEC)
23 #define TEST_TIMEOUT 1800 * (NSEC_PER_SEC)
25 // From bsd/sys/proc_internal.h
28 #define EXIT_BINARY "perf_exit_proc"
29 #define EXIT_BINARY_PATH "./" EXIT_BINARY
31 #define NEXT_CASE_EVENTID (0xfedcbb00)
38 static struct test_case test_cases
[] = {
47 #define TEST_CASES_COUNT (sizeof(test_cases) / sizeof(struct test_case))
49 static _Atomic
int producer_i
, consumer_i
;
51 static ktrace_session_t session
;
53 static dispatch_queue_t spawn_queue
, processing_queue
;
55 static uint64_t *begin_ts
;
56 static dt_stat_time_t s
;
57 static _Atomic
bool tracing_on
= false;
59 void run_exit_test(int proc_wired_mem
, int nthreads
);
61 static void cleanup(void) {
63 dispatch_release(spawn_queue
);
64 dispatch_release(processing_queue
);
66 ktrace_end(session
, 1);
71 create_stat(int proc_wired_mem
, int nthreads
)
73 dt_stat_time_t dst
= dt_stat_time_create("time");
74 T_ASSERT_NOTNULL(dst
, "created time statistic");
76 dt_stat_set_variable((dt_stat_t
)dst
, "proc_threads", nthreads
);
77 dt_stat_set_variable((dt_stat_t
)dst
, "proc_wired_mem", proc_wired_mem
);;
82 T_DECL(exit
, "exit(2) time from syscall start to end", T_META_TIMEOUT(TEST_TIMEOUT
)) {
83 s
= create_stat(test_cases
[consumer_i
].wired_mem
, test_cases
[consumer_i
].threads
);
85 begin_ts
= malloc(sizeof(uint64_t) * PID_MAX
);
86 T_ASSERT_NOTNULL(begin_ts
, "created pid array");
90 session
= ktrace_session_create();
91 T_ASSERT_NOTNULL(session
, "created a trace session");
93 spawn_queue
= dispatch_queue_create("com.apple.perf_exit.spawn_queue", NULL
);
94 processing_queue
= dispatch_queue_create("com.apple.perf_exit.processing_queue", NULL
);
96 ktrace_set_completion_handler(session
, ^{
97 T_ASSERT_EQ(consumer_i
, TEST_CASES_COUNT
, "ran all the test cases");
98 dispatch_sync(spawn_queue
, ^(void) {
101 ktrace_session_destroy(session
);
105 ktrace_set_signal_handler(session
);
106 ktrace_set_execnames_enabled(session
, KTRACE_FEATURE_ENABLED
);
108 // We are only interested in the processes we launched and ourselves
109 ktrace_filter_process(session
, EXIT_BINARY
);
110 ktrace_filter_process(session
, "perf_exit");
112 ktrace_events_single(session
, NEXT_CASE_EVENTID
, ^(__unused ktrace_event_t e
) {
115 if (consumer_i
>= TEST_CASES_COUNT
) {
116 ktrace_end(session
, 1);
119 s
= create_stat(test_cases
[consumer_i
].wired_mem
, test_cases
[consumer_i
].threads
);
123 ktrace_events_single(session
, (BSDDBG_CODE(DBG_BSD_EXCP_SC
, 1) | DBG_FUNC_START
), ^(ktrace_event_t e
) {
124 T_QUIET
; T_ASSERT_LE(e
->pid
, PID_MAX
, "pid %d is valid in start tracepoint", e
->pid
);
125 begin_ts
[e
->pid
] = e
->timestamp
;
128 ktrace_events_single(session
, (BSDDBG_CODE(DBG_BSD_PROC
, BSD_PROC_EXIT
) | DBG_FUNC_END
), ^(ktrace_event_t e
) {
129 T_ASSERT_LE(e
->pid
, PID_MAX
, "pid %d is valid in end tracepoint", e
->pid
);
131 if (begin_ts
[e
->pid
] == 0) {
135 T_QUIET
; T_ASSERT_LE(begin_ts
[e
->pid
], e
->timestamp
, "timestamps are monotonically increasing");
136 dt_stat_mach_time_add(s
, e
->timestamp
- begin_ts
[e
->pid
]);
139 if (dt_stat_stable(s
) && producer_i
== consumer_i
) {
140 dispatch_sync(spawn_queue
, ^(void) {
142 T_ASSERT_POSIX_ZERO(kdebug_trace(NEXT_CASE_EVENTID
, producer_i
, 0, 0, 0), "kdebug_trace returns 0");
147 int ret
= ktrace_start(session
, processing_queue
);
148 T_ASSERT_POSIX_ZERO(ret
, "starting trace");
151 // Spawn processes continuously until the test is over
153 __block
void (^spawn_process
)(void) = Block_copy(^(void) {
154 char nthreads_buf
[32], mem_buf
[32];
156 if (producer_i
>= TEST_CASES_COUNT
|| !tracing_on
) {
160 snprintf(nthreads_buf
, 32, "%d", test_cases
[producer_i
].threads
);
161 snprintf(mem_buf
, 32, "%d", test_cases
[producer_i
].wired_mem
);
163 char *args
[] = {EXIT_BINARY_PATH
, nthreads_buf
, mem_buf
, NULL
};
167 int bret
= posix_spawn(&pid
, args
[0], NULL
, NULL
, args
, NULL
);
168 T_ASSERT_POSIX_ZERO(bret
, "spawned process with pid %d (threads=%s mem=%s)", pid
, nthreads_buf
, mem_buf
);
170 bret
= waitpid(pid
, &status
, 0);
171 T_QUIET
; T_ASSERT_POSIX_SUCCESS(bret
, "waited for process %d\n", pid
);
173 if (!WIFEXITED(status
) || WEXITSTATUS(status
) != 0)
174 T_ASSERT_FAIL("child process failed to run");
176 // Avoid saturating the CPU with new processes
179 dispatch_async(spawn_queue
, spawn_process
);
182 dispatch_async(spawn_queue
, spawn_process
);
184 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, TEST_TIMEOUT
), dispatch_get_main_queue(), ^{
185 ktrace_end(session
, 0);