3 #include <sys/sysctl.h>
4 #include <mach-o/dyld.h>
5 #include <perfcheck_keys.h>
10 #include <darwintest.h>
11 #include <darwintest_utils.h>
14 T_META_NAMESPACE("xnu.vm.perf"),
15 T_META_CHECK_LEAKS(false),
26 #define CREATE_LIST(X) \
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) \
37 #define EXIT_CODES_ENUM(VAR) VAR,
39 CREATE_LIST(EXIT_CODES_ENUM
)
42 #define EXIT_CODES_STRING(VAR) #VAR,
43 static const char *exit_codes_str
[] = {
44 CREATE_LIST(EXIT_CODES_STRING
)
48 static pid_t pid
= -1;
50 static dt_stat_time_t s
;
52 void allocate_zero_pages(char **buf
, int num_pages
, int vmpgsize
);
53 void allocate_mostly_zero_pages(char **buf
, int num_pages
, int vmpgsize
);
54 void allocate_random_pages(char **buf
, int num_pages
, int vmpgsize
);
55 void allocate_representative_pages(char **buf
, int num_pages
, int vmpgsize
);
56 void run_compressor_test(int size_mb
, int page_type
);
57 void freeze_helper_process(void);
59 void allocate_zero_pages(char **buf
, int num_pages
, int vmpgsize
) {
62 for (i
= 0; i
< num_pages
; i
++) {
63 buf
[i
] = (char*)malloc((size_t)vmpgsize
* sizeof(char));
64 memset(buf
[i
], 0, vmpgsize
);
68 void allocate_mostly_zero_pages(char **buf
, int num_pages
, int vmpgsize
) {
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);
80 void allocate_random_pages(char **buf
, int num_pages
, int vmpgsize
) {
83 for (i
= 0; i
< num_pages
; i
++) {
84 buf
[i
] = (char*)malloc((size_t)vmpgsize
* sizeof(char));
85 arc4random_buf((void*)buf
[i
], (size_t)vmpgsize
);
89 // Gives us the compression ratio we see in the typical case (~2.7)
90 void allocate_representative_pages(char **buf
, int num_pages
, int vmpgsize
) {
94 for (j
= 0; j
< num_pages
; j
++) {
95 buf
[j
] = (char*)malloc((size_t)vmpgsize
* sizeof(char));
97 for (i
= 0; i
< vmpgsize
; i
+= 16) {
98 memset(&buf
[j
][i
], val
, 16);
99 if (i
< 3400 * (vmpgsize
/ 4096)) {
106 void freeze_helper_process(void) {
108 int64_t compressed_before
, compressed_after
, input_before
, input_after
;
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");
119 ret
= sysctlbyname("kern.memorystatus_freeze", NULL
, NULL
, &pid
, sizeof(pid
));
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");
129 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "sysctl kern.memorystatus_freeze failed");
131 dt_stat_add(r
, (double)(input_after
- input_before
)/(double)(compressed_after
- compressed_before
));
133 ret
= sysctlbyname("kern.memorystatus_thaw", NULL
, NULL
, &pid
, sizeof(pid
));
134 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "sysctl kern.memorystatus_thaw failed");
136 T_QUIET
; T_ASSERT_POSIX_SUCCESS(kill(pid
, SIGUSR1
), "failed to send SIGUSR1 to child process");
139 void run_compressor_test(int size_mb
, int page_type
) {
143 char **launch_tool_args
;
144 char testpath
[PATH_MAX
];
145 uint32_t testpath_buf_size
;
146 dispatch_source_t ds_freeze
, ds_proc
;
148 #ifndef CONFIG_FREEZE
149 T_SKIP("Task freeze not supported.");
152 r
= dt_stat_create("(input bytes / compressed bytes)", "compression_ratio");
153 s
= dt_stat_time_create("compressor_latency");
154 // This sets the A/B failure threshold at 50% of baseline for compressor_latency
155 dt_stat_set_variable(s
, kPCFailureThresholdPctVar
, 50.0);
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)");
161 dispatch_source_set_event_handler(ds_freeze
, ^{
162 if (!dt_stat_stable(s
)) {
163 freeze_helper_process();
169 dispatch_source_cancel(ds_freeze
);
172 dispatch_activate(ds_freeze
);
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
);
179 sprintf(sz_str
, "%d", size_mb
);
180 sprintf(pt_str
, "%d", page_type
);
181 launch_tool_args
= (char *[]){
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
);
194 T_LOG("dt_launch tool returned %d with error code %d", ret
, errno
);
196 T_QUIET
; T_ASSERT_POSIX_SUCCESS(pid
, "dt_launch_tool");
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)");
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
);
209 } else if (code
> 0 && code
< EXIT_CODE_MAX
) {
210 T_ASSERT_FAIL("Child exited with %s", exit_codes_str
[code
]);
212 T_ASSERT_FAIL("Child exited with unknown exit code %d", code
);
215 dispatch_activate(ds_proc
);
217 T_QUIET
; T_ASSERT_POSIX_SUCCESS(kill(pid
, SIGCONT
), "failed to send SIGCONT to child process");
221 T_HELPER_DECL(allocate_pages
, "allocates pages to compress") {
222 int i
, j
, ret
, size_mb
, page_type
, vmpgsize
;
223 size_t vmpgsize_length
;
224 __block
int num_pages
;
226 dispatch_source_t ds_signal
;
228 vmpgsize_length
= sizeof(vmpgsize
);
229 ret
= sysctlbyname("vm.pagesize", &vmpgsize
, &vmpgsize_length
, NULL
, 0);
231 exit(SYSCTL_VM_PAGESIZE_FAILED
);
234 exit(VM_PAGESIZE_IS_ZERO
);
238 exit(TOO_FEW_ARGUMENTS
);
241 size_mb
= atoi(argv
[0]);
242 page_type
= atoi(argv
[1]);
243 num_pages
= size_mb
* 1024 * 1024 / vmpgsize
;
244 buf
= (char**)malloc(sizeof(char*) * (size_t)num_pages
);
246 // Switch on the type of page requested
249 allocate_zero_pages(buf
, num_pages
, vmpgsize
);
252 allocate_mostly_zero_pages(buf
, num_pages
, vmpgsize
);
255 allocate_random_pages(buf
, num_pages
, vmpgsize
);
258 allocate_representative_pages(buf
, num_pages
, vmpgsize
);
261 exit(UNKNOWN_PAGE_TYPE
);
264 for (j
= 0; j
< num_pages
; j
++) {
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
);
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
);
282 dispatch_source_set_event_handler(ds_signal
, ^{
285 /* Make sure all the pages are accessed before trying to freeze again */
286 for (int x
= 0; x
< num_pages
; x
++) {
289 if (kill(getppid(), SIGUSR1
) != 0) {
290 exit(SIGNAL_TO_PARENT_FAILED
);
293 dispatch_activate(ds_signal
);
298 // Numbers for 10MB and above are fairly reproducible. Anything smaller shows a lot of variation.
300 // Keeping just the 100MB version for iOSMark
302 T_DECL(compr_10MB_zero
, "Compressor latencies") {
303 run_compressor_test(10, ALL_ZEROS
);
306 T_DECL(compr_10MB_mostly_zero
, "Compressor latencies") {
307 run_compressor_test(10, MOSTLY_ZEROS
);
310 T_DECL(compr_10MB_random
, "Compressor latencies") {
311 run_compressor_test(10, RANDOM
);
314 T_DECL(compr_10MB_typical
, "Compressor latencies") {
315 run_compressor_test(10, TYPICAL
);
318 T_DECL(compr_100MB_zero
, "Compressor latencies") {
319 run_compressor_test(100, ALL_ZEROS
);
322 T_DECL(compr_100MB_mostly_zero
, "Compressor latencies") {
323 run_compressor_test(100, MOSTLY_ZEROS
);
326 T_DECL(compr_100MB_random
, "Compressor latencies") {
327 run_compressor_test(100, RANDOM
);
331 T_DECL(compr_100MB_typical
, "Compressor latencies") {
332 run_compressor_test(100, TYPICAL
);