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