1*33de042dSApple OSS Distributions #ifdef T_NAMESPACE
2*33de042dSApple OSS Distributions #undef T_NAMESPACE
3*33de042dSApple OSS Distributions #endif
4*33de042dSApple OSS Distributions #include <darwintest.h>
5*33de042dSApple OSS Distributions #include <darwintest_utils.h>
6*33de042dSApple OSS Distributions
7*33de042dSApple OSS Distributions #include <sys/kdebug.h>
8*33de042dSApple OSS Distributions #include <ktrace/session.h>
9*33de042dSApple OSS Distributions #include <spawn.h>
10*33de042dSApple OSS Distributions #include <stdio.h>
11*33de042dSApple OSS Distributions #include <stdlib.h>
12*33de042dSApple OSS Distributions #include <stdatomic.h>
13*33de042dSApple OSS Distributions
14*33de042dSApple OSS Distributions T_GLOBAL_META(
15*33de042dSApple OSS Distributions T_META_NAMESPACE("xnu.perf"),
16*33de042dSApple OSS Distributions T_META_ASROOT(true),
17*33de042dSApple OSS Distributions T_META_LTEPHASE(LTE_SINGLEUSER),
18*33de042dSApple OSS Distributions T_META_TAG_PERF
19*33de042dSApple OSS Distributions );
20*33de042dSApple OSS Distributions #if TARGET_OS_WATCH
21*33de042dSApple OSS Distributions #define TEST_TIMEOUT 3600 * (NSEC_PER_SEC)
22*33de042dSApple OSS Distributions #else
23*33de042dSApple OSS Distributions #define TEST_TIMEOUT 1800 * (NSEC_PER_SEC)
24*33de042dSApple OSS Distributions #endif
25*33de042dSApple OSS Distributions // From bsd/sys/proc_internal.h
26*33de042dSApple OSS Distributions #define PID_MAX 99999
27*33de042dSApple OSS Distributions
28*33de042dSApple OSS Distributions #define EXIT_BINARY "perf_exit_proc"
29*33de042dSApple OSS Distributions #define EXIT_BINARY_PATH "./" EXIT_BINARY
30*33de042dSApple OSS Distributions
31*33de042dSApple OSS Distributions #define NEXT_CASE_EVENTID (0xfedcbb00)
32*33de042dSApple OSS Distributions
33*33de042dSApple OSS Distributions struct test_case {
34*33de042dSApple OSS Distributions int wired_mem;
35*33de042dSApple OSS Distributions int threads;
36*33de042dSApple OSS Distributions };
37*33de042dSApple OSS Distributions
38*33de042dSApple OSS Distributions static struct test_case test_cases[] = {
39*33de042dSApple OSS Distributions {0, 0},
40*33de042dSApple OSS Distributions {0, 10},
41*33de042dSApple OSS Distributions {1000000, 0},
42*33de042dSApple OSS Distributions #if !TARGET_OS_WATCH
43*33de042dSApple OSS Distributions {10000000, 0}
44*33de042dSApple OSS Distributions #endif
45*33de042dSApple OSS Distributions };
46*33de042dSApple OSS Distributions
47*33de042dSApple OSS Distributions #define TEST_CASES_COUNT (sizeof(test_cases) / sizeof(struct test_case))
48*33de042dSApple OSS Distributions
49*33de042dSApple OSS Distributions static _Atomic int producer_i, consumer_i;
50*33de042dSApple OSS Distributions
51*33de042dSApple OSS Distributions static ktrace_session_t session;
52*33de042dSApple OSS Distributions
53*33de042dSApple OSS Distributions static dispatch_queue_t spawn_queue, processing_queue;
54*33de042dSApple OSS Distributions
55*33de042dSApple OSS Distributions static uint64_t *begin_ts;
56*33de042dSApple OSS Distributions static dt_stat_time_t s;
57*33de042dSApple OSS Distributions static _Atomic bool tracing_on = false;
58*33de042dSApple OSS Distributions
59*33de042dSApple OSS Distributions void run_exit_test(int proc_wired_mem, int nthreads);
60*33de042dSApple OSS Distributions
61*33de042dSApple OSS Distributions static void
cleanup(void)62*33de042dSApple OSS Distributions cleanup(void)
63*33de042dSApple OSS Distributions {
64*33de042dSApple OSS Distributions free(begin_ts);
65*33de042dSApple OSS Distributions dispatch_release(spawn_queue);
66*33de042dSApple OSS Distributions dispatch_release(processing_queue);
67*33de042dSApple OSS Distributions if (tracing_on) {
68*33de042dSApple OSS Distributions ktrace_end(session, 1);
69*33de042dSApple OSS Distributions }
70*33de042dSApple OSS Distributions }
71*33de042dSApple OSS Distributions
72*33de042dSApple OSS Distributions static dt_stat_time_t
create_stat(int proc_wired_mem,int nthreads)73*33de042dSApple OSS Distributions create_stat(int proc_wired_mem, int nthreads)
74*33de042dSApple OSS Distributions {
75*33de042dSApple OSS Distributions dt_stat_time_t dst = dt_stat_time_create("time");
76*33de042dSApple OSS Distributions T_ASSERT_NOTNULL(dst, "created time statistic");
77*33de042dSApple OSS Distributions
78*33de042dSApple OSS Distributions dt_stat_set_variable((dt_stat_t)dst, "proc_threads", nthreads);
79*33de042dSApple OSS Distributions dt_stat_set_variable((dt_stat_t)dst, "proc_wired_mem", proc_wired_mem);
80*33de042dSApple OSS Distributions
81*33de042dSApple OSS Distributions return dst;
82*33de042dSApple OSS Distributions }
83*33de042dSApple OSS Distributions
84*33de042dSApple OSS Distributions T_DECL(exit, "exit(2) time from syscall start to end", T_META_TIMEOUT(TEST_TIMEOUT), T_META_TAG_VM_NOT_ELIGIBLE) {
85*33de042dSApple OSS Distributions s = create_stat(test_cases[consumer_i].wired_mem, test_cases[consumer_i].threads);
86*33de042dSApple OSS Distributions
87*33de042dSApple OSS Distributions begin_ts = malloc(sizeof(uint64_t) * PID_MAX);
88*33de042dSApple OSS Distributions T_ASSERT_NOTNULL(begin_ts, "created pid array");
89*33de042dSApple OSS Distributions
90*33de042dSApple OSS Distributions T_ATEND(cleanup);
91*33de042dSApple OSS Distributions
92*33de042dSApple OSS Distributions session = ktrace_session_create();
93*33de042dSApple OSS Distributions T_ASSERT_NOTNULL(session, "created a trace session");
94*33de042dSApple OSS Distributions
95*33de042dSApple OSS Distributions spawn_queue = dispatch_queue_create("com.apple.perf_exit.spawn_queue", NULL);
96*33de042dSApple OSS Distributions processing_queue = dispatch_queue_create("com.apple.perf_exit.processing_queue", NULL);
97*33de042dSApple OSS Distributions
98*33de042dSApple OSS Distributions ktrace_set_completion_handler(session, ^{
99*33de042dSApple OSS Distributions T_ASSERT_EQ(consumer_i, TEST_CASES_COUNT, "ran all the test cases");
100*33de042dSApple OSS Distributions dispatch_sync(spawn_queue, ^(void) {
101*33de042dSApple OSS Distributions tracing_on = false;
102*33de042dSApple OSS Distributions });
103*33de042dSApple OSS Distributions ktrace_session_destroy(session);
104*33de042dSApple OSS Distributions T_END;
105*33de042dSApple OSS Distributions });
106*33de042dSApple OSS Distributions
107*33de042dSApple OSS Distributions ktrace_set_signal_handler(session);
108*33de042dSApple OSS Distributions ktrace_set_execnames_enabled(session, KTRACE_FEATURE_ENABLED);
109*33de042dSApple OSS Distributions
110*33de042dSApple OSS Distributions // We are only interested in the processes we launched and ourselves
111*33de042dSApple OSS Distributions ktrace_filter_process(session, EXIT_BINARY);
112*33de042dSApple OSS Distributions ktrace_filter_process(session, "perf_exit");
113*33de042dSApple OSS Distributions
114*33de042dSApple OSS Distributions ktrace_events_single(session, NEXT_CASE_EVENTID, ^(__unused ktrace_event_t e) {
115*33de042dSApple OSS Distributions consumer_i++;
116*33de042dSApple OSS Distributions dt_stat_finalize(s);
117*33de042dSApple OSS Distributions if (consumer_i >= TEST_CASES_COUNT) {
118*33de042dSApple OSS Distributions ktrace_end(session, 1);
119*33de042dSApple OSS Distributions } else {
120*33de042dSApple OSS Distributions s = create_stat(test_cases[consumer_i].wired_mem, test_cases[consumer_i].threads);
121*33de042dSApple OSS Distributions }
122*33de042dSApple OSS Distributions });
123*33de042dSApple OSS Distributions
124*33de042dSApple OSS Distributions ktrace_events_single(session, (BSDDBG_CODE(DBG_BSD_EXCP_SC, 1) | DBG_FUNC_START), ^(ktrace_event_t e) {
125*33de042dSApple OSS Distributions T_QUIET; T_ASSERT_LE(e->pid, PID_MAX, "pid %d is valid in start tracepoint", e->pid);
126*33de042dSApple OSS Distributions begin_ts[e->pid] = e->timestamp;
127*33de042dSApple OSS Distributions });
128*33de042dSApple OSS Distributions
129*33de042dSApple OSS Distributions ktrace_events_single(session, (BSDDBG_CODE(DBG_BSD_PROC, BSD_PROC_EXIT) | DBG_FUNC_END), ^(ktrace_event_t e) {
130*33de042dSApple OSS Distributions T_ASSERT_LE(e->pid, PID_MAX, "pid %d is valid in end tracepoint", e->pid);
131*33de042dSApple OSS Distributions
132*33de042dSApple OSS Distributions if (begin_ts[e->pid] == 0) {
133*33de042dSApple OSS Distributions return;
134*33de042dSApple OSS Distributions }
135*33de042dSApple OSS Distributions
136*33de042dSApple OSS Distributions T_QUIET; T_ASSERT_LE(begin_ts[e->pid], e->timestamp, "timestamps are monotonically increasing");
137*33de042dSApple OSS Distributions dt_stat_mach_time_add(s, e->timestamp - begin_ts[e->pid]);
138*33de042dSApple OSS Distributions
139*33de042dSApple OSS Distributions
140*33de042dSApple OSS Distributions if (dt_stat_stable(s) && producer_i == consumer_i) {
141*33de042dSApple OSS Distributions dispatch_sync(spawn_queue, ^(void) {
142*33de042dSApple OSS Distributions producer_i++;
143*33de042dSApple OSS Distributions T_ASSERT_POSIX_ZERO(kdebug_trace(NEXT_CASE_EVENTID, producer_i, 0, 0, 0), "kdebug_trace returns 0");
144*33de042dSApple OSS Distributions });
145*33de042dSApple OSS Distributions }
146*33de042dSApple OSS Distributions });
147*33de042dSApple OSS Distributions
148*33de042dSApple OSS Distributions int ret = ktrace_start(session, processing_queue);
149*33de042dSApple OSS Distributions T_ASSERT_POSIX_ZERO(ret, "starting trace");
150*33de042dSApple OSS Distributions tracing_on = true;
151*33de042dSApple OSS Distributions
152*33de042dSApple OSS Distributions // Spawn processes continuously until the test is over
153*33de042dSApple OSS Distributions
154*33de042dSApple OSS Distributions __block void (^spawn_process)(void) = Block_copy(^(void) {
155*33de042dSApple OSS Distributions char nthreads_buf[32], mem_buf[32];
156*33de042dSApple OSS Distributions
157*33de042dSApple OSS Distributions if (producer_i >= TEST_CASES_COUNT || !tracing_on) {
158*33de042dSApple OSS Distributions return;
159*33de042dSApple OSS Distributions }
160*33de042dSApple OSS Distributions
161*33de042dSApple OSS Distributions snprintf(nthreads_buf, 32, "%d", test_cases[producer_i].threads);
162*33de042dSApple OSS Distributions snprintf(mem_buf, 32, "%d", test_cases[producer_i].wired_mem);
163*33de042dSApple OSS Distributions
164*33de042dSApple OSS Distributions char *args[] = {EXIT_BINARY_PATH, nthreads_buf, mem_buf, NULL};
165*33de042dSApple OSS Distributions int status;
166*33de042dSApple OSS Distributions
167*33de042dSApple OSS Distributions pid_t pid;
168*33de042dSApple OSS Distributions int bret = posix_spawn(&pid, args[0], NULL, NULL, args, NULL);
169*33de042dSApple OSS Distributions T_ASSERT_POSIX_ZERO(bret, "spawned process with pid %d (threads=%s mem=%s)", pid, nthreads_buf, mem_buf);
170*33de042dSApple OSS Distributions
171*33de042dSApple OSS Distributions bret = waitpid(pid, &status, 0);
172*33de042dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(bret, "waited for process %d\n", pid);
173*33de042dSApple OSS Distributions
174*33de042dSApple OSS Distributions if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
175*33de042dSApple OSS Distributions T_ASSERT_FAIL("child process failed to run");
176*33de042dSApple OSS Distributions }
177*33de042dSApple OSS Distributions
178*33de042dSApple OSS Distributions // Avoid saturating the CPU with new processes
179*33de042dSApple OSS Distributions usleep(1000);
180*33de042dSApple OSS Distributions
181*33de042dSApple OSS Distributions dispatch_async(spawn_queue, spawn_process);
182*33de042dSApple OSS Distributions });
183*33de042dSApple OSS Distributions
184*33de042dSApple OSS Distributions dispatch_async(spawn_queue, spawn_process);
185*33de042dSApple OSS Distributions
186*33de042dSApple OSS Distributions dispatch_after(dispatch_time(DISPATCH_TIME_NOW, TEST_TIMEOUT), dispatch_get_main_queue(), ^{
187*33de042dSApple OSS Distributions ktrace_end(session, 0);
188*33de042dSApple OSS Distributions });
189*33de042dSApple OSS Distributions
190*33de042dSApple OSS Distributions dispatch_main();
191*33de042dSApple OSS Distributions }
192