4 #include <darwintest.h>
6 #include <sys/kdebug.h>
7 #include <ktrace/session.h>
11 #include <stdatomic.h>
14 T_META_NAMESPACE("xnu.perf.exit"),
16 T_META_LTEPHASE(LTE_SINGLEUSER
)
19 // From osfmk/kern/sched.h
20 #define BASEPRI_FOREGROUND 47
21 #define BASEPRI_USER_INITIATED 37
22 #define BASEPRI_UTILITY 20
23 #define MAXPRI_THROTTLE 4
25 // From bsd/sys/proc_internal.h
28 #define EXIT_BINARY "perf_exit_proc"
29 #define EXIT_BINARY_PATH "./" EXIT_BINARY
31 static ktrace_session_t session
;
32 static dispatch_queue_t spawn_queue
;
33 static uint64_t *begin_ts
;
34 static dt_stat_time_t s
;
35 static bool started_tracing
= false;
37 void run_exit_test(int proc_wired_mem
, int thread_priority
, int nthreads
);
39 static void cleanup(void) {
42 dispatch_release(spawn_queue
);
43 if (started_tracing
) {
44 ktrace_end(session
, 1);
48 void run_exit_test(int proc_wired_mem
, int thread_priority
, int nthreads
) {
49 static atomic_bool ended
= false;
51 s
= dt_stat_time_create("time");
52 T_QUIET
; T_ASSERT_NOTNULL(s
, "created time statistic");
54 begin_ts
= malloc(sizeof(uint64_t) * PID_MAX
);
55 T_QUIET
; T_ASSERT_NOTNULL(begin_ts
, "created pid array");
59 session
= ktrace_session_create();
60 T_QUIET
; T_ASSERT_NOTNULL(session
, "created a trace session");
62 spawn_queue
= dispatch_queue_create("spawn_queue", NULL
);
64 ktrace_set_completion_handler(session
, ^{
65 ktrace_session_destroy(session
);
69 ktrace_set_signal_handler(session
);
70 ktrace_set_execnames_enabled(session
, KTRACE_FEATURE_ENABLED
);
72 // We are only interested in the process we launched
73 ktrace_filter_process(session
, EXIT_BINARY
);
75 ktrace_events_single(session
, (BSDDBG_CODE(DBG_BSD_EXCP_SC
, 1) | DBG_FUNC_START
), ^(ktrace_event_t e
) {
76 T_QUIET
; T_ASSERT_LE(e
->pid
, PID_MAX
, "valid pid for tracepoint");
77 begin_ts
[e
->pid
] = e
->timestamp
;
79 ktrace_events_single(session
, (BSDDBG_CODE(DBG_BSD_PROC
, BSD_PROC_EXIT
) | DBG_FUNC_END
), ^(ktrace_event_t e
) {
80 T_QUIET
; T_ASSERT_LE(e
->pid
, PID_MAX
, "valid pid for tracepoint");
82 if (begin_ts
[e
->pid
] == 0) {
85 T_QUIET
; T_ASSERT_LE(begin_ts
[e
->pid
], e
->timestamp
, "timestamps are monotonically increasing");
86 dt_stat_mach_time_add(s
, e
->timestamp
- begin_ts
[e
->pid
]);
88 if (dt_stat_stable(s
)) {
90 ktrace_end(session
, 1);
94 int ret
= ktrace_start(session
, dispatch_get_main_queue());
95 T_ASSERT_POSIX_ZERO(ret
, "starting trace");
96 started_tracing
= true;
98 // Spawn processes continuously until the test is over
99 dispatch_async(spawn_queue
, ^(void) {
100 char priority_buf
[32], nthreads_buf
[32], mem_buf
[32];
102 snprintf(priority_buf
, 32, "%d", thread_priority
);
103 snprintf(nthreads_buf
, 32, "%d", nthreads
);
104 snprintf(mem_buf
, 32, "%d", proc_wired_mem
);
106 char *args
[] = {EXIT_BINARY_PATH
, priority_buf
, nthreads_buf
, mem_buf
, NULL
};
110 int bret
= posix_spawn(&pid
, args
[0], NULL
, NULL
, args
, NULL
);
111 T_QUIET
; T_ASSERT_POSIX_ZERO(bret
, "spawned process '%s'", args
[0]);
113 bret
= waitpid(pid
, &status
, 0);
114 T_QUIET
; T_ASSERT_POSIX_SUCCESS(bret
, "waited for process %d\n", pid
);
116 if (!WIFEXITED(status
) || WEXITSTATUS(status
) != 0)
117 T_ASSERT_FAIL("child process failed to run");
119 // Avoid saturating the CPU with new processes
128 T_DECL(exit
, "exit(2) time from syscall start to end") {
129 run_exit_test(0, BASEPRI_FOREGROUND
, 0);
132 T_DECL(exit_pri_4
, "exit(2) time at priority 4 (throttled)") {
133 run_exit_test(0, MAXPRI_THROTTLE
, 0);
136 T_DECL(exit_pri_20
, "exit(2) time at priority 20 (utility)") {
137 run_exit_test(0, BASEPRI_UTILITY
, 0);
140 T_DECL(exit_pri_37
, "exit(2) time at priority 37 (user initiated)") {
141 run_exit_test(0, BASEPRI_USER_INITIATED
, 0);
144 T_DECL(exit_10_threads
, "exit(2) time with 10 threads") {
145 run_exit_test(0, BASEPRI_FOREGROUND
, 10);
148 T_DECL(exit_1mb
, "exit(2) time with 1MB of wired memory") {
149 run_exit_test(10000000, BASEPRI_FOREGROUND
, 0);
152 T_DECL(exit_10mb
, "exit(2) time with 10MB of wired memory") {
153 run_exit_test(10000000, BASEPRI_FOREGROUND
, 0);
156 T_DECL(exit_100_threads
, "exit(2) time with 100 threads", T_META_ENABLED(false), T_META_TIMEOUT(1800)) {
157 run_exit_test(0, BASEPRI_FOREGROUND
, 100);
160 T_DECL(exit_1000_threads
, "exit(2) time with 1000 threads", T_META_ENABLED(false), T_META_TIMEOUT(1800)) {
161 run_exit_test(0, BASEPRI_FOREGROUND
, 1000);
164 T_DECL(exit_100mb
, "exit(2) time with 100MB of wired memory", T_META_ENABLED(false), T_META_TIMEOUT(1800)) {
165 run_exit_test(100000000, BASEPRI_FOREGROUND
, 0);