]> git.saurik.com Git - apple/xnu.git/blame - tests/perf_compressor.c
xnu-4903.241.1.tar.gz
[apple/xnu.git] / tests / perf_compressor.c
CommitLineData
a39ff7e2
A
1#include <stdio.h>
2#include <signal.h>
3#include <sys/sysctl.h>
4#include <mach-o/dyld.h>
d9a64523 5#include <perfcheck_keys.h>
a39ff7e2 6
813fb2f6
A
7#ifdef T_NAMESPACE
8#undef T_NAMESPACE
9#endif
10#include <darwintest.h>
a39ff7e2 11#include <darwintest_utils.h>
813fb2f6
A
12
13T_GLOBAL_META(
14 T_META_NAMESPACE("xnu.vm.perf"),
d9a64523
A
15 T_META_CHECK_LEAKS(false),
16 T_META_TAG_PERF
813fb2f6
A
17);
18
19enum {
20 ALL_ZEROS,
21 MOSTLY_ZEROS,
22 RANDOM,
23 TYPICAL
24};
25
a39ff7e2
A
26#define CREATE_LIST(X) \
27 X(SUCCESS) \
28 X(TOO_FEW_ARGUMENTS) \
29 X(SYSCTL_VM_PAGESIZE_FAILED) \
30 X(VM_PAGESIZE_IS_ZERO) \
31 X(UNKNOWN_PAGE_TYPE) \
32 X(DISPATCH_SOURCE_CREATE_FAILED) \
33 X(INITIAL_SIGNAL_TO_PARENT_FAILED) \
34 X(SIGNAL_TO_PARENT_FAILED) \
35 X(EXIT_CODE_MAX)
36
37#define EXIT_CODES_ENUM(VAR) VAR,
38enum exit_codes_num {
39 CREATE_LIST(EXIT_CODES_ENUM)
40};
41
42#define EXIT_CODES_STRING(VAR) #VAR,
43static const char *exit_codes_str[] = {
44 CREATE_LIST(EXIT_CODES_STRING)
45};
46
47
48static pid_t pid = -1;
49static dt_stat_t r;
50static dt_stat_time_t s;
51
813fb2f6
A
52void allocate_zero_pages(char **buf, int num_pages, int vmpgsize);
53void allocate_mostly_zero_pages(char **buf, int num_pages, int vmpgsize);
54void allocate_random_pages(char **buf, int num_pages, int vmpgsize);
55void allocate_representative_pages(char **buf, int num_pages, int vmpgsize);
813fb2f6 56void run_compressor_test(int size_mb, int page_type);
a39ff7e2 57void freeze_helper_process(void);
813fb2f6
A
58
59void allocate_zero_pages(char **buf, int num_pages, int vmpgsize) {
60 int i;
61
62 for (i = 0; i < num_pages; i++) {
63 buf[i] = (char*)malloc((size_t)vmpgsize * sizeof(char));
64 memset(buf[i], 0, vmpgsize);
65 }
66}
67
68void allocate_mostly_zero_pages(char **buf, int num_pages, int vmpgsize) {
69 int i, j;
70
71 for (i = 0; i < num_pages; i++) {
72 buf[i] = (char*)malloc((size_t)vmpgsize * sizeof(char));
73 memset(buf[i], 0, vmpgsize);
74 for (j = 0; j < 40; j++) {
75 buf[i][j] = (char)(j+1);
76 }
77 }
78}
79
80void allocate_random_pages(char **buf, int num_pages, int vmpgsize) {
5ba3f43e 81 int i;
813fb2f6
A
82
83 for (i = 0; i < num_pages; i++) {
84 buf[i] = (char*)malloc((size_t)vmpgsize * sizeof(char));
5ba3f43e 85 arc4random_buf((void*)buf[i], (size_t)vmpgsize);
813fb2f6 86 }
813fb2f6
A
87}
88
89// Gives us the compression ratio we see in the typical case (~2.7)
90void allocate_representative_pages(char **buf, int num_pages, int vmpgsize) {
91 int i, j;
92 char val;
93
94 for (j = 0; j < num_pages; j++) {
95 buf[j] = (char*)malloc((size_t)vmpgsize * sizeof(char));
96 val = 0;
97 for (i = 0; i < vmpgsize; i += 16) {
98 memset(&buf[j][i], val, 16);
a39ff7e2 99 if (i < 3400 * (vmpgsize / 4096)) {
813fb2f6
A
100 val++;
101 }
102 }
103 }
104}
105
a39ff7e2
A
106void freeze_helper_process(void) {
107 int ret;
108 int64_t compressed_before, compressed_after, input_before, input_after;
109 size_t length;
110
a39ff7e2
A
111 length = sizeof(compressed_before);
112 T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.compressor_compressed_bytes", &compressed_before, &length, NULL, 0),
113 "failed to query vm.compressor_compressed_bytes");
114 length = sizeof(input_before);
115 T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.compressor_input_bytes", &input_before, &length, NULL, 0),
116 "failed to query vm.compressor_input_bytes");
117
118 T_STAT_MEASURE(s) {
119 ret = sysctlbyname("kern.memorystatus_freeze", NULL, NULL, &pid, sizeof(pid));
120 };
121
122 length = sizeof(compressed_after);
123 T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.compressor_compressed_bytes", &compressed_after, &length, NULL, 0),
124 "failed to query vm.compressor_compressed_bytes");
125 length = sizeof(input_after);
126 T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.compressor_input_bytes", &input_after, &length, NULL, 0),
127 "failed to query vm.compressor_input_bytes");
128
d9a64523 129 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.memorystatus_freeze failed");
a39ff7e2
A
130
131 dt_stat_add(r, (double)(input_after - input_before)/(double)(compressed_after - compressed_before));
132
a39ff7e2 133 ret = sysctlbyname("kern.memorystatus_thaw", NULL, NULL, &pid, sizeof(pid));
d9a64523 134 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.memorystatus_thaw failed");
a39ff7e2 135
d9a64523 136 T_QUIET; T_ASSERT_POSIX_SUCCESS(kill(pid, SIGUSR1), "failed to send SIGUSR1 to child process");
a39ff7e2
A
137}
138
139void run_compressor_test(int size_mb, int page_type) {
140 int ret;
141 char sz_str[50];
142 char pt_str[50];
143 char **launch_tool_args;
144 char testpath[PATH_MAX];
145 uint32_t testpath_buf_size;
146 dispatch_source_t ds_freeze, ds_proc;
147
148#ifndef CONFIG_FREEZE
149 T_SKIP("Task freeze not supported.");
150#endif
151
152 r = dt_stat_create("(input bytes / compressed bytes)", "compression_ratio");
153 s = dt_stat_time_create("compressor_latency");
d9a64523
A
154 // This sets the A/B failure threshold at 50% of baseline for compressor_latency
155 dt_stat_set_variable(s, kPCFailureThresholdPctVar, 50.0);
a39ff7e2
A
156
157 signal(SIGUSR1, SIG_IGN);
158 ds_freeze = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGUSR1, 0, dispatch_get_main_queue());
159 T_QUIET; T_ASSERT_NOTNULL(ds_freeze, "dispatch_source_create (ds_freeze)");
160
161 dispatch_source_set_event_handler(ds_freeze, ^{
162 if (!dt_stat_stable(s)) {
163 freeze_helper_process();
164 } else {
165 dt_stat_finalize(s);
166 dt_stat_finalize(r);
167
168 kill(pid, SIGKILL);
169 dispatch_source_cancel(ds_freeze);
170 }
171 });
172 dispatch_activate(ds_freeze);
173
174 testpath_buf_size = sizeof(testpath);
175 ret = _NSGetExecutablePath(testpath, &testpath_buf_size);
176 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "_NSGetExecutablePath");
177 T_LOG("Executable path: %s", testpath);
178
179 sprintf(sz_str, "%d", size_mb);
180 sprintf(pt_str, "%d", page_type);
181 launch_tool_args = (char *[]){
182 testpath,
183 "-n",
184 "allocate_pages",
185 "--",
186 sz_str,
187 pt_str,
188 NULL
189 };
190
191 /* Spawn the child process. Suspend after launch until the exit proc handler has been set up. */
192 ret = dt_launch_tool(&pid, launch_tool_args, true, NULL, NULL);
193 if (ret != 0) {
194 T_LOG("dt_launch tool returned %d with error code %d", ret, errno);
195 }
196 T_QUIET; T_ASSERT_POSIX_SUCCESS(pid, "dt_launch_tool");
197
198 ds_proc = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, (uintptr_t)pid, DISPATCH_PROC_EXIT, dispatch_get_main_queue());
199 T_QUIET; T_ASSERT_NOTNULL(ds_proc, "dispatch_source_create (ds_proc)");
200
201 dispatch_source_set_event_handler(ds_proc, ^{
202 int status = 0, code = 0;
203 pid_t rc = waitpid(pid, &status, 0);
204 T_QUIET; T_ASSERT_EQ(rc, pid, "waitpid");
205 code = WEXITSTATUS(status);
206
207 if (code == 0) {
208 T_END;
209 } else if (code > 0 && code < EXIT_CODE_MAX) {
210 T_ASSERT_FAIL("Child exited with %s", exit_codes_str[code]);
211 } else {
212 T_ASSERT_FAIL("Child exited with unknown exit code %d", code);
213 }
214 });
215 dispatch_activate(ds_proc);
216
d9a64523 217 T_QUIET; T_ASSERT_POSIX_SUCCESS(kill(pid, SIGCONT), "failed to send SIGCONT to child process");
a39ff7e2
A
218 dispatch_main();
219}
220
221T_HELPER_DECL(allocate_pages, "allocates pages to compress") {
222 int i, j, ret, size_mb, page_type, vmpgsize;
813fb2f6 223 size_t vmpgsize_length;
a39ff7e2
A
224 __block int num_pages;
225 __block char **buf;
226 dispatch_source_t ds_signal;
813fb2f6
A
227
228 vmpgsize_length = sizeof(vmpgsize);
a39ff7e2
A
229 ret = sysctlbyname("vm.pagesize", &vmpgsize, &vmpgsize_length, NULL, 0);
230 if (ret != 0) {
231 exit(SYSCTL_VM_PAGESIZE_FAILED);
232 }
813fb2f6 233 if (vmpgsize == 0) {
a39ff7e2
A
234 exit(VM_PAGESIZE_IS_ZERO);
235 }
236
237 if (argc < 2) {
238 exit(TOO_FEW_ARGUMENTS);
813fb2f6
A
239 }
240
a39ff7e2
A
241 size_mb = atoi(argv[0]);
242 page_type = atoi(argv[1]);
813fb2f6
A
243 num_pages = size_mb * 1024 * 1024 / vmpgsize;
244 buf = (char**)malloc(sizeof(char*) * (size_t)num_pages);
245
246 // Switch on the type of page requested
247 switch(page_type) {
248 case ALL_ZEROS:
249 allocate_zero_pages(buf, num_pages, vmpgsize);
250 break;
251 case MOSTLY_ZEROS:
252 allocate_mostly_zero_pages(buf, num_pages, vmpgsize);
253 break;
254 case RANDOM:
255 allocate_random_pages(buf, num_pages, vmpgsize);
256 break;
257 case TYPICAL:
258 allocate_representative_pages(buf, num_pages, vmpgsize);
259 break;
260 default:
a39ff7e2 261 exit(UNKNOWN_PAGE_TYPE);
813fb2f6
A
262 }
263
a39ff7e2
A
264 for (j = 0; j < num_pages; j++) {
265 i = buf[j][0];
813fb2f6 266 }
813fb2f6 267
a39ff7e2
A
268 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC), dispatch_get_main_queue(), ^{
269 /* Signal to the parent that we're done allocating and it's ok to freeze us */
270 printf("Sending initial signal to parent to begin freezing\n");
271 if (kill(getppid(), SIGUSR1) != 0) {
272 exit(INITIAL_SIGNAL_TO_PARENT_FAILED);
273 }
274 });
813fb2f6 275
a39ff7e2
A
276 signal(SIGUSR1, SIG_IGN);
277 ds_signal = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGUSR1, 0, dispatch_get_main_queue());
278 if (ds_signal == NULL) {
279 exit(DISPATCH_SOURCE_CREATE_FAILED);
280 }
813fb2f6 281
a39ff7e2
A
282 dispatch_source_set_event_handler(ds_signal, ^{
283 volatile int tmp;
813fb2f6 284
a39ff7e2
A
285 /* Make sure all the pages are accessed before trying to freeze again */
286 for (int x = 0; x < num_pages; x++) {
287 tmp = buf[x][0];
813fb2f6 288 }
a39ff7e2
A
289 if (kill(getppid(), SIGUSR1) != 0) {
290 exit(SIGNAL_TO_PARENT_FAILED);
291 }
292 });
293 dispatch_activate(ds_signal);
813fb2f6 294
a39ff7e2 295 dispatch_main();
813fb2f6
A
296}
297
298// Numbers for 10MB and above are fairly reproducible. Anything smaller shows a lot of variation.
d9a64523
A
299
300// Keeping just the 100MB version for iOSMark
301#ifndef DT_IOSMARK
813fb2f6
A
302T_DECL(compr_10MB_zero, "Compressor latencies") {
303 run_compressor_test(10, ALL_ZEROS);
304}
305
306T_DECL(compr_10MB_mostly_zero, "Compressor latencies") {
307 run_compressor_test(10, MOSTLY_ZEROS);
308}
309
310T_DECL(compr_10MB_random, "Compressor latencies") {
311 run_compressor_test(10, RANDOM);
312}
313
314T_DECL(compr_10MB_typical, "Compressor latencies") {
315 run_compressor_test(10, TYPICAL);
316}
317
318T_DECL(compr_100MB_zero, "Compressor latencies") {
319 run_compressor_test(100, ALL_ZEROS);
320}
321
322T_DECL(compr_100MB_mostly_zero, "Compressor latencies") {
323 run_compressor_test(100, MOSTLY_ZEROS);
324}
325
326T_DECL(compr_100MB_random, "Compressor latencies") {
327 run_compressor_test(100, RANDOM);
328}
d9a64523 329#endif
813fb2f6
A
330
331T_DECL(compr_100MB_typical, "Compressor latencies") {
332 run_compressor_test(100, TYPICAL);
333}
334