]>
Commit | Line | Data |
---|---|---|
813fb2f6 A |
1 | #ifdef T_NAMESPACE |
2 | #undef T_NAMESPACE | |
3 | #endif | |
4 | #include <darwintest.h> | |
5 | ||
6 | #include <stdio.h> | |
7 | #include <stdlib.h> | |
8 | #include <unistd.h> | |
9 | #include <fcntl.h> | |
10 | #include <sys/sysctl.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 | void allocate_zero_pages(char **buf, int num_pages, int vmpgsize); | |
25 | void allocate_mostly_zero_pages(char **buf, int num_pages, int vmpgsize); | |
26 | void allocate_random_pages(char **buf, int num_pages, int vmpgsize); | |
27 | void allocate_representative_pages(char **buf, int num_pages, int vmpgsize); | |
28 | void allocate_pages(int size_mb, int page_type); | |
29 | void run_compressor_test(int size_mb, int page_type); | |
30 | ||
31 | void allocate_zero_pages(char **buf, int num_pages, int vmpgsize) { | |
32 | int i; | |
33 | ||
34 | for (i = 0; i < num_pages; i++) { | |
35 | buf[i] = (char*)malloc((size_t)vmpgsize * sizeof(char)); | |
36 | memset(buf[i], 0, vmpgsize); | |
37 | } | |
38 | } | |
39 | ||
40 | void allocate_mostly_zero_pages(char **buf, int num_pages, int vmpgsize) { | |
41 | int i, j; | |
42 | ||
43 | for (i = 0; i < num_pages; i++) { | |
44 | buf[i] = (char*)malloc((size_t)vmpgsize * sizeof(char)); | |
45 | memset(buf[i], 0, vmpgsize); | |
46 | for (j = 0; j < 40; j++) { | |
47 | buf[i][j] = (char)(j+1); | |
48 | } | |
49 | } | |
50 | } | |
51 | ||
52 | void allocate_random_pages(char **buf, int num_pages, int vmpgsize) { | |
53 | int fd, i; | |
54 | ||
55 | fd = open("/dev/random", O_RDONLY); | |
56 | T_QUIET; T_ASSERT_POSIX_SUCCESS(fd, "open /dev/random failed [%s]\n", strerror(errno)); | |
57 | ||
58 | for (i = 0; i < num_pages; i++) { | |
59 | buf[i] = (char*)malloc((size_t)vmpgsize * sizeof(char)); | |
60 | T_QUIET; T_ASSERT_POSIX_SUCCESS(read(fd, buf[i], (size_t)vmpgsize), | |
61 | "read from /dev/random failed [%s]\n", strerror(errno)); | |
62 | } | |
63 | close(fd); | |
64 | } | |
65 | ||
66 | // Gives us the compression ratio we see in the typical case (~2.7) | |
67 | void allocate_representative_pages(char **buf, int num_pages, int vmpgsize) { | |
68 | int i, j; | |
69 | char val; | |
70 | ||
71 | for (j = 0; j < num_pages; j++) { | |
72 | buf[j] = (char*)malloc((size_t)vmpgsize * sizeof(char)); | |
73 | val = 0; | |
74 | for (i = 0; i < vmpgsize; i += 16) { | |
75 | memset(&buf[j][i], val, 16); | |
76 | if (i < 3700 * (vmpgsize / 4096)) { | |
77 | val++; | |
78 | } | |
79 | } | |
80 | } | |
81 | } | |
82 | ||
83 | void allocate_pages(int size_mb, int page_type) { | |
84 | int num_pages = 0; | |
85 | int vmpgsize, i, j; | |
86 | char **buf; | |
87 | size_t vmpgsize_length; | |
88 | ||
89 | vmpgsize_length = sizeof(vmpgsize); | |
90 | T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.pagesize", &vmpgsize, &vmpgsize_length, NULL, 0), | |
91 | "failed to query vm.pagesize"); | |
92 | if (vmpgsize == 0) { | |
93 | T_FAIL("vm.pagesize set to zero"); | |
94 | } | |
95 | ||
96 | num_pages = size_mb * 1024 * 1024 / vmpgsize; | |
97 | buf = (char**)malloc(sizeof(char*) * (size_t)num_pages); | |
98 | ||
99 | // Switch on the type of page requested | |
100 | switch(page_type) { | |
101 | case ALL_ZEROS: | |
102 | allocate_zero_pages(buf, num_pages, vmpgsize); | |
103 | break; | |
104 | case MOSTLY_ZEROS: | |
105 | allocate_mostly_zero_pages(buf, num_pages, vmpgsize); | |
106 | break; | |
107 | case RANDOM: | |
108 | allocate_random_pages(buf, num_pages, vmpgsize); | |
109 | break; | |
110 | case TYPICAL: | |
111 | allocate_representative_pages(buf, num_pages, vmpgsize); | |
112 | break; | |
113 | default: | |
114 | T_FAIL("unknown page type"); | |
115 | break; | |
116 | } | |
117 | ||
118 | for(j = 0; j < num_pages; j++) { | |
119 | i = buf[j][1]; | |
120 | } | |
121 | } | |
122 | ||
123 | ||
124 | void run_compressor_test(int size_mb, int page_type) { | |
125 | ||
126 | #ifndef CONFIG_FREEZE | |
127 | T_SKIP("Task freeze not supported."); | |
128 | #endif | |
129 | ||
130 | dt_stat_t r = dt_stat_create("(input bytes / compressed bytes)", "compression_ratio"); | |
131 | dt_stat_time_t s = dt_stat_time_create("compressor_latency"); | |
132 | ||
133 | while (!dt_stat_stable(s)) { | |
134 | pid_t pid; | |
135 | int parent_pipe[2], child_pipe[2]; | |
136 | ||
137 | T_QUIET; T_ASSERT_POSIX_SUCCESS(pipe(parent_pipe), "pipe failed"); | |
138 | T_QUIET; T_ASSERT_POSIX_SUCCESS(pipe(child_pipe), "pipe failed"); | |
139 | ||
140 | pid = fork(); | |
141 | T_QUIET; T_ASSERT_POSIX_SUCCESS(pid, "fork failed with %d", errno); | |
142 | ||
143 | if (pid == 0) { | |
144 | int val = 1; | |
145 | ||
146 | close(child_pipe[0]); | |
147 | close(parent_pipe[1]); | |
148 | allocate_pages(size_mb, page_type); | |
149 | ||
150 | // Indicates to the parent that the child has finished allocating pages | |
151 | write(child_pipe[1], &val, sizeof(val)); | |
152 | ||
153 | // Parent is done with the freeze, ok to exit now | |
154 | read(parent_pipe[0], &val, sizeof(val)); | |
155 | if (val != 2) { | |
156 | T_FAIL("pipe read error"); | |
157 | } | |
158 | close(child_pipe[1]); | |
159 | close(parent_pipe[0]); | |
160 | exit(0); | |
161 | ||
162 | } else { | |
163 | int val, ret; | |
164 | int64_t compressed_before, compressed_after, input_before, input_after; | |
165 | dt_stat_token start_token; | |
166 | size_t length = sizeof(compressed_before); | |
167 | ||
168 | close(child_pipe[1]); | |
169 | close(parent_pipe[0]); | |
170 | ||
171 | // Wait for the child to finish allocating pages | |
172 | read(child_pipe[0], &val, sizeof(val)); | |
173 | if (val != 1) { | |
174 | T_FAIL("pipe read error"); | |
175 | } | |
176 | // Just to be extra sure that the child has finished allocating all of its pages | |
177 | usleep(100); | |
178 | ||
179 | T_LOG("attempting to freeze pid %d\n", pid); | |
180 | ||
181 | T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.compressor_compressed_bytes", &compressed_before, &length, NULL, 0), | |
182 | "failed to query vm.compressor_compressed_bytes"); | |
183 | T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.compressor_input_bytes", &input_before, &length, NULL, 0), | |
184 | "failed to query vm.compressor_input_bytes"); | |
185 | ||
186 | start_token = dt_stat_time_begin(s); | |
187 | ret = sysctlbyname("kern.memorystatus_freeze", NULL, NULL, &pid, (size_t)sizeof(int)); | |
188 | dt_stat_time_end(s, start_token); | |
189 | ||
190 | T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.compressor_compressed_bytes", &compressed_after, &length, NULL, 0), | |
191 | "failed to query vm.compressor_compressed_bytes"); | |
192 | T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.compressor_input_bytes", &input_after, &length, NULL, 0), | |
193 | "failed to query vm.compressor_input_bytes"); | |
194 | ||
195 | T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.memorystatus_freeze failed on pid %d", pid); | |
196 | ||
197 | dt_stat_add(r, (double)(input_after - input_before)/(double)(compressed_after - compressed_before)); | |
198 | ||
199 | val = 2; | |
200 | // Ok for the child to exit now | |
201 | write(parent_pipe[1], &val, sizeof(val)); | |
202 | usleep(100); | |
203 | ||
204 | close(child_pipe[0]); | |
205 | close(parent_pipe[1]); | |
206 | } | |
207 | } | |
208 | ||
209 | dt_stat_finalize(s); | |
210 | dt_stat_finalize(r); | |
211 | } | |
212 | ||
213 | // Numbers for 10MB and above are fairly reproducible. Anything smaller shows a lot of variation. | |
214 | T_DECL(compr_10MB_zero, "Compressor latencies") { | |
215 | run_compressor_test(10, ALL_ZEROS); | |
216 | } | |
217 | ||
218 | T_DECL(compr_10MB_mostly_zero, "Compressor latencies") { | |
219 | run_compressor_test(10, MOSTLY_ZEROS); | |
220 | } | |
221 | ||
222 | T_DECL(compr_10MB_random, "Compressor latencies") { | |
223 | run_compressor_test(10, RANDOM); | |
224 | } | |
225 | ||
226 | T_DECL(compr_10MB_typical, "Compressor latencies") { | |
227 | run_compressor_test(10, TYPICAL); | |
228 | } | |
229 | ||
230 | T_DECL(compr_100MB_zero, "Compressor latencies") { | |
231 | run_compressor_test(100, ALL_ZEROS); | |
232 | } | |
233 | ||
234 | T_DECL(compr_100MB_mostly_zero, "Compressor latencies") { | |
235 | run_compressor_test(100, MOSTLY_ZEROS); | |
236 | } | |
237 | ||
238 | T_DECL(compr_100MB_random, "Compressor latencies") { | |
239 | run_compressor_test(100, RANDOM); | |
240 | } | |
241 | ||
242 | T_DECL(compr_100MB_typical, "Compressor latencies") { | |
243 | run_compressor_test(100, TYPICAL); | |
244 | } | |
245 |