]> git.saurik.com Git - apple/xnu.git/blame - tests/perf_compressor.c
xnu-4903.270.47.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
0a7de745 17 );
813fb2f6
A
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
0a7de745 47#define SYSCTL_FREEZE_TO_MEMORY "kern.memorystatus_freeze_to_memory=1"
a39ff7e2
A
48
49static pid_t pid = -1;
50static dt_stat_t r;
51static dt_stat_time_t s;
52
813fb2f6
A
53void allocate_zero_pages(char **buf, int num_pages, int vmpgsize);
54void allocate_mostly_zero_pages(char **buf, int num_pages, int vmpgsize);
55void allocate_random_pages(char **buf, int num_pages, int vmpgsize);
56void allocate_representative_pages(char **buf, int num_pages, int vmpgsize);
813fb2f6 57void run_compressor_test(int size_mb, int page_type);
a39ff7e2 58void freeze_helper_process(void);
0a7de745 59void cleanup(void);
813fb2f6 60
0a7de745
A
61void
62allocate_zero_pages(char **buf, int num_pages, int vmpgsize)
63{
813fb2f6
A
64 int i;
65
66 for (i = 0; i < num_pages; i++) {
67 buf[i] = (char*)malloc((size_t)vmpgsize * sizeof(char));
68 memset(buf[i], 0, vmpgsize);
69 }
70}
71
0a7de745
A
72void
73allocate_mostly_zero_pages(char **buf, int num_pages, int vmpgsize)
74{
813fb2f6
A
75 int i, j;
76
77 for (i = 0; i < num_pages; i++) {
78 buf[i] = (char*)malloc((size_t)vmpgsize * sizeof(char));
79 memset(buf[i], 0, vmpgsize);
80 for (j = 0; j < 40; j++) {
0a7de745 81 buf[i][j] = (char)(j + 1);
813fb2f6
A
82 }
83 }
84}
85
0a7de745
A
86void
87allocate_random_pages(char **buf, int num_pages, int vmpgsize)
88{
5ba3f43e 89 int i;
813fb2f6
A
90
91 for (i = 0; i < num_pages; i++) {
92 buf[i] = (char*)malloc((size_t)vmpgsize * sizeof(char));
5ba3f43e 93 arc4random_buf((void*)buf[i], (size_t)vmpgsize);
813fb2f6 94 }
813fb2f6
A
95}
96
97// Gives us the compression ratio we see in the typical case (~2.7)
0a7de745
A
98void
99allocate_representative_pages(char **buf, int num_pages, int vmpgsize)
100{
813fb2f6
A
101 int i, j;
102 char val;
103
104 for (j = 0; j < num_pages; j++) {
105 buf[j] = (char*)malloc((size_t)vmpgsize * sizeof(char));
106 val = 0;
107 for (i = 0; i < vmpgsize; i += 16) {
108 memset(&buf[j][i], val, 16);
a39ff7e2 109 if (i < 3400 * (vmpgsize / 4096)) {
813fb2f6
A
110 val++;
111 }
112 }
113 }
114}
115
0a7de745
A
116void
117freeze_helper_process(void)
118{
119 int ret, freeze_enabled;
a39ff7e2
A
120 int64_t compressed_before, compressed_after, input_before, input_after;
121 size_t length;
0a7de745 122 int errno_sysctl_freeze;
a39ff7e2 123
a39ff7e2
A
124 length = sizeof(compressed_before);
125 T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.compressor_compressed_bytes", &compressed_before, &length, NULL, 0),
0a7de745 126 "failed to query vm.compressor_compressed_bytes");
a39ff7e2
A
127 length = sizeof(input_before);
128 T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.compressor_input_bytes", &input_before, &length, NULL, 0),
0a7de745 129 "failed to query vm.compressor_input_bytes");
a39ff7e2
A
130
131 T_STAT_MEASURE(s) {
132 ret = sysctlbyname("kern.memorystatus_freeze", NULL, NULL, &pid, sizeof(pid));
0a7de745 133 errno_sysctl_freeze = errno;
a39ff7e2
A
134 };
135
136 length = sizeof(compressed_after);
137 T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.compressor_compressed_bytes", &compressed_after, &length, NULL, 0),
0a7de745 138 "failed to query vm.compressor_compressed_bytes");
a39ff7e2
A
139 length = sizeof(input_after);
140 T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.compressor_input_bytes", &input_after, &length, NULL, 0),
0a7de745
A
141 "failed to query vm.compressor_input_bytes");
142
143 length = sizeof(freeze_enabled);
144 T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.freeze_enabled", &freeze_enabled, &length, NULL, 0),
145 "failed to query vm.freeze_enabled");
146 if (freeze_enabled) {
147 errno = errno_sysctl_freeze;
148 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.memorystatus_freeze failed");
149 } else {
150 /* If freezer is disabled, skip the test. This can happen due to disk space shortage. */
151 T_LOG("Freeze has been disabled. Terminating early.");
152 T_END;
153 }
a39ff7e2 154
0a7de745 155 dt_stat_add(r, (double)(input_after - input_before) / (double)(compressed_after - compressed_before));
a39ff7e2 156
a39ff7e2 157 ret = sysctlbyname("kern.memorystatus_thaw", NULL, NULL, &pid, sizeof(pid));
d9a64523 158 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.memorystatus_thaw failed");
a39ff7e2 159
d9a64523 160 T_QUIET; T_ASSERT_POSIX_SUCCESS(kill(pid, SIGUSR1), "failed to send SIGUSR1 to child process");
a39ff7e2
A
161}
162
0a7de745
A
163void
164cleanup(void)
165{
166 int status = 0;
167
168 /* No helper process. */
169 if (pid == -1) {
170 return;
171 }
172 /* Kill the helper process. */
173 kill(pid, SIGKILL);
174}
175
176void
177run_compressor_test(int size_mb, int page_type)
178{
a39ff7e2
A
179 int ret;
180 char sz_str[50];
181 char pt_str[50];
182 char **launch_tool_args;
183 char testpath[PATH_MAX];
184 uint32_t testpath_buf_size;
185 dispatch_source_t ds_freeze, ds_proc;
0a7de745
A
186 int freeze_enabled;
187 size_t length;
a39ff7e2 188
0a7de745
A
189 length = sizeof(freeze_enabled);
190 T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.freeze_enabled", &freeze_enabled, &length, NULL, 0),
191 "failed to query vm.freeze_enabled");
192 if (!freeze_enabled) {
193 /* If freezer is disabled, skip the test. This can happen due to disk space shortage. */
194 T_SKIP("Freeze has been disabled. Skipping test.");
195 }
196
197 T_ATEND(cleanup);
a39ff7e2
A
198
199 r = dt_stat_create("(input bytes / compressed bytes)", "compression_ratio");
200 s = dt_stat_time_create("compressor_latency");
d9a64523
A
201 // This sets the A/B failure threshold at 50% of baseline for compressor_latency
202 dt_stat_set_variable(s, kPCFailureThresholdPctVar, 50.0);
a39ff7e2
A
203
204 signal(SIGUSR1, SIG_IGN);
205 ds_freeze = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGUSR1, 0, dispatch_get_main_queue());
206 T_QUIET; T_ASSERT_NOTNULL(ds_freeze, "dispatch_source_create (ds_freeze)");
207
208 dispatch_source_set_event_handler(ds_freeze, ^{
209 if (!dt_stat_stable(s)) {
0a7de745 210 freeze_helper_process();
a39ff7e2 211 } else {
0a7de745
A
212 dt_stat_finalize(s);
213 dt_stat_finalize(r);
a39ff7e2 214
0a7de745
A
215 kill(pid, SIGKILL);
216 dispatch_source_cancel(ds_freeze);
a39ff7e2
A
217 }
218 });
219 dispatch_activate(ds_freeze);
220
221 testpath_buf_size = sizeof(testpath);
222 ret = _NSGetExecutablePath(testpath, &testpath_buf_size);
223 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "_NSGetExecutablePath");
224 T_LOG("Executable path: %s", testpath);
225
226 sprintf(sz_str, "%d", size_mb);
227 sprintf(pt_str, "%d", page_type);
228 launch_tool_args = (char *[]){
229 testpath,
230 "-n",
231 "allocate_pages",
232 "--",
233 sz_str,
234 pt_str,
235 NULL
236 };
237
238 /* Spawn the child process. Suspend after launch until the exit proc handler has been set up. */
239 ret = dt_launch_tool(&pid, launch_tool_args, true, NULL, NULL);
240 if (ret != 0) {
241 T_LOG("dt_launch tool returned %d with error code %d", ret, errno);
242 }
243 T_QUIET; T_ASSERT_POSIX_SUCCESS(pid, "dt_launch_tool");
244
245 ds_proc = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, (uintptr_t)pid, DISPATCH_PROC_EXIT, dispatch_get_main_queue());
246 T_QUIET; T_ASSERT_NOTNULL(ds_proc, "dispatch_source_create (ds_proc)");
247
248 dispatch_source_set_event_handler(ds_proc, ^{
249 int status = 0, code = 0;
250 pid_t rc = waitpid(pid, &status, 0);
251 T_QUIET; T_ASSERT_EQ(rc, pid, "waitpid");
252 code = WEXITSTATUS(status);
253
254 if (code == 0) {
0a7de745 255 T_END;
a39ff7e2 256 } else if (code > 0 && code < EXIT_CODE_MAX) {
0a7de745 257 T_ASSERT_FAIL("Child exited with %s", exit_codes_str[code]);
a39ff7e2 258 } else {
0a7de745 259 T_ASSERT_FAIL("Child exited with unknown exit code %d", code);
a39ff7e2
A
260 }
261 });
262 dispatch_activate(ds_proc);
263
d9a64523 264 T_QUIET; T_ASSERT_POSIX_SUCCESS(kill(pid, SIGCONT), "failed to send SIGCONT to child process");
a39ff7e2
A
265 dispatch_main();
266}
267
268T_HELPER_DECL(allocate_pages, "allocates pages to compress") {
269 int i, j, ret, size_mb, page_type, vmpgsize;
813fb2f6 270 size_t vmpgsize_length;
a39ff7e2
A
271 __block int num_pages;
272 __block char **buf;
273 dispatch_source_t ds_signal;
813fb2f6
A
274
275 vmpgsize_length = sizeof(vmpgsize);
a39ff7e2
A
276 ret = sysctlbyname("vm.pagesize", &vmpgsize, &vmpgsize_length, NULL, 0);
277 if (ret != 0) {
278 exit(SYSCTL_VM_PAGESIZE_FAILED);
279 }
813fb2f6 280 if (vmpgsize == 0) {
a39ff7e2
A
281 exit(VM_PAGESIZE_IS_ZERO);
282 }
283
284 if (argc < 2) {
285 exit(TOO_FEW_ARGUMENTS);
813fb2f6
A
286 }
287
a39ff7e2
A
288 size_mb = atoi(argv[0]);
289 page_type = atoi(argv[1]);
813fb2f6
A
290 num_pages = size_mb * 1024 * 1024 / vmpgsize;
291 buf = (char**)malloc(sizeof(char*) * (size_t)num_pages);
292
293 // Switch on the type of page requested
0a7de745
A
294 switch (page_type) {
295 case ALL_ZEROS:
296 allocate_zero_pages(buf, num_pages, vmpgsize);
297 break;
298 case MOSTLY_ZEROS:
299 allocate_mostly_zero_pages(buf, num_pages, vmpgsize);
300 break;
301 case RANDOM:
302 allocate_random_pages(buf, num_pages, vmpgsize);
303 break;
304 case TYPICAL:
305 allocate_representative_pages(buf, num_pages, vmpgsize);
306 break;
307 default:
308 exit(UNKNOWN_PAGE_TYPE);
813fb2f6
A
309 }
310
a39ff7e2
A
311 for (j = 0; j < num_pages; j++) {
312 i = buf[j][0];
813fb2f6 313 }
813fb2f6 314
a39ff7e2
A
315 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC), dispatch_get_main_queue(), ^{
316 /* Signal to the parent that we're done allocating and it's ok to freeze us */
0a7de745 317 printf("[%d] Sending initial signal to parent to begin freezing\n", getpid());
a39ff7e2 318 if (kill(getppid(), SIGUSR1) != 0) {
0a7de745 319 exit(INITIAL_SIGNAL_TO_PARENT_FAILED);
a39ff7e2
A
320 }
321 });
813fb2f6 322
a39ff7e2
A
323 signal(SIGUSR1, SIG_IGN);
324 ds_signal = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGUSR1, 0, dispatch_get_main_queue());
325 if (ds_signal == NULL) {
326 exit(DISPATCH_SOURCE_CREATE_FAILED);
327 }
813fb2f6 328
a39ff7e2
A
329 dispatch_source_set_event_handler(ds_signal, ^{
330 volatile int tmp;
813fb2f6 331
a39ff7e2
A
332 /* Make sure all the pages are accessed before trying to freeze again */
333 for (int x = 0; x < num_pages; x++) {
0a7de745 334 tmp = buf[x][0];
813fb2f6 335 }
a39ff7e2 336 if (kill(getppid(), SIGUSR1) != 0) {
0a7de745 337 exit(SIGNAL_TO_PARENT_FAILED);
a39ff7e2
A
338 }
339 });
340 dispatch_activate(ds_signal);
813fb2f6 341
a39ff7e2 342 dispatch_main();
813fb2f6
A
343}
344
345// Numbers for 10MB and above are fairly reproducible. Anything smaller shows a lot of variation.
d9a64523
A
346
347// Keeping just the 100MB version for iOSMark
348#ifndef DT_IOSMARK
0a7de745
A
349T_DECL(compr_10MB_zero,
350 "Compression latency for 10MB - zero pages",
351 T_META_SYSCTL_INT(SYSCTL_FREEZE_TO_MEMORY)) {
813fb2f6
A
352 run_compressor_test(10, ALL_ZEROS);
353}
354
0a7de745
A
355T_DECL(compr_10MB_mostly_zero,
356 "Compression latency for 10MB - mostly zero pages",
357 T_META_SYSCTL_INT(SYSCTL_FREEZE_TO_MEMORY)) {
813fb2f6
A
358 run_compressor_test(10, MOSTLY_ZEROS);
359}
360
0a7de745
A
361T_DECL(compr_10MB_random,
362 "Compression latency for 10MB - random pages",
363 T_META_SYSCTL_INT(SYSCTL_FREEZE_TO_MEMORY)) {
813fb2f6
A
364 run_compressor_test(10, RANDOM);
365}
366
0a7de745
A
367T_DECL(compr_10MB_typical,
368 "Compression latency for 10MB - typical pages",
369 T_META_SYSCTL_INT(SYSCTL_FREEZE_TO_MEMORY)) {
813fb2f6
A
370 run_compressor_test(10, TYPICAL);
371}
372
0a7de745
A
373T_DECL(compr_100MB_zero,
374 "Compression latency for 100MB - zero pages",
375 T_META_SYSCTL_INT(SYSCTL_FREEZE_TO_MEMORY)) {
813fb2f6
A
376 run_compressor_test(100, ALL_ZEROS);
377}
378
0a7de745
A
379T_DECL(compr_100MB_mostly_zero,
380 "Compression latency for 100MB - mostly zero pages",
381 T_META_SYSCTL_INT(SYSCTL_FREEZE_TO_MEMORY)) {
813fb2f6
A
382 run_compressor_test(100, MOSTLY_ZEROS);
383}
384
0a7de745
A
385T_DECL(compr_100MB_random,
386 "Compression latency for 100MB - random pages",
387 T_META_SYSCTL_INT(SYSCTL_FREEZE_TO_MEMORY)) {
813fb2f6
A
388 run_compressor_test(100, RANDOM);
389}
d9a64523 390#endif
813fb2f6 391
0a7de745
A
392T_DECL(compr_100MB_typical,
393 "Compression latency for 100MB - typical pages",
394 T_META_SYSCTL_INT(SYSCTL_FREEZE_TO_MEMORY)) {
813fb2f6
A
395 run_compressor_test(100, TYPICAL);
396}