]> git.saurik.com Git - apple/xnu.git/blame - tests/kernel_mtx_perf.c
xnu-7195.81.3.tar.gz
[apple/xnu.git] / tests / kernel_mtx_perf.c
CommitLineData
d9a64523
A
1#ifdef T_NAMESPACE
2#undef T_NAMESPACE
3#endif
4
5#include <darwintest.h>
6#include <darwintest_multiprocess.h>
7#include <darwintest_utils.h>
8#include <pthread.h>
9#include <launch.h>
10#include <servers/bootstrap.h>
11#include <stdlib.h>
12#include <sys/event.h>
13#include <unistd.h>
14#include <crt_externs.h>
15#include <sys/sysctl.h>
16#include <sys/types.h>
17#include <unistd.h>
18#include <spawn.h>
19
20T_GLOBAL_META(T_META_NAMESPACE("xnu.kernel_mtx_perf_test"));
21
22#define ITER 100000
0a7de745 23#define TEST_MTX_MAX_STATS 8
cb323159
A
24#define FULL_CONTENDED 0
25#define HALF_CONTENDED 1
26#define MAX_CONDENDED 2
27
d9a64523 28
0a7de745
A
29#define TEST_MTX_LOCK_STATS 0
30#define TEST_MTX_UNLOCK_MTX_STATS 6
d9a64523
A
31
32static void
33test_from_kernel_lock_unlock_contended(void)
34{
cb323159
A
35 int i, ret;
36 unsigned long name_size;
d9a64523
A
37 uint64_t avg, run, tot;
38 size_t size;
39 char iter[35];
40 char *buff, *buff_p, *avg_p, *name, *end_name;
41
42 T_LOG("Testing locking/unlocking mutex from kernel with contention.\n");
43 T_LOG("Requesting test with %d iterations\n", ITER);
44
cb323159 45 size = 2000;
d9a64523 46 buff = calloc(size, sizeof(char));
0a7de745 47 T_QUIET; T_ASSERT_NOTNULL(buff, "Allocating buffer fo sysctl");
d9a64523
A
48
49 snprintf(iter, sizeof(iter), "%d", ITER);
50 ret = sysctlbyname("kern.test_mtx_contended", buff, &size, iter, sizeof(iter));
51 T_ASSERT_POSIX_SUCCESS(ret, "sysctlbyname kern.test_mtx_contended");
52
cb323159 53 T_LOG("\n%s stats :\n%s\n", __func__, buff);
d9a64523 54
d9a64523 55 buff_p = buff;
cb323159
A
56 int t;
57 for (t = 0; t < MAX_CONDENDED; t++) {
58 char *type;
59 if (t == FULL_CONTENDED) {
60 type = "FULL_CONTENDED ";
61 } else {
62 type = "HALF_CONTENDED ";
d9a64523
A
63 }
64
cb323159 65 /* first line is "STATS INNER LOOP" */
0a7de745
A
66 while (*buff_p != '\n') {
67 buff_p++;
68 }
d9a64523 69 buff_p++;
d9a64523 70
cb323159
A
71 /*
72 * Sequence of statistic lines like
73 * { samples 100000, tot 3586175 ns, avg 35 ns, max 3997 ns, min 33 ns } TEST_MTX_LOCK_STATS
74 * for all TEST_MTX_MAX_STATS statistics
75 */
76 for (i = 0; i < TEST_MTX_MAX_STATS; i++) {
77 avg_p = strstr(buff_p, "avg ");
78
79 /* contended test records statistics only for lock/unlock for now */
80 if (i == TEST_MTX_LOCK_STATS || i == TEST_MTX_UNLOCK_MTX_STATS) {
81 T_QUIET; T_ASSERT_NOTNULL(avg_p, "contended %i average not found", i);
82 sscanf(avg_p, "avg %llu", &avg);
83
84 name = strstr(buff_p, "TEST_MTX_");
85 end_name = strstr(buff_p, "_STATS");
86 name_size = (unsigned long) end_name - (unsigned long) name - strlen("TEST_MTX_") + 1;
87
88 char name_string[40];
89 char avg_name_string[50];
90 char *pre_string = "contended ";
91 snprintf(name_string, name_size + strlen(pre_string) + strlen(type), "%s%s%s", pre_string, type, &name[strlen("TEST_MTX_")]);
92 pre_string = "avg contended ";
93 snprintf(avg_name_string, name_size + strlen(pre_string) + strlen(type), "%s%s%s", pre_string, type, &name[strlen("TEST_MTX_")]);
94 T_PERF(name_string, avg, "ns", avg_name_string);
95 }
96
97 buff_p = avg_p;
98 while (*buff_p != '\n') {
99 buff_p++;
100 }
101 buff_p++;
102 }
103
104 while (*buff_p != '\n') {
105 buff_p++;
106 }
0a7de745 107 buff_p++;
d9a64523 108
cb323159
A
109 /* next line is "STATS OUTER LOOP" */
110 while (*buff_p != '\n') {
111 buff_p++;
112 }
0a7de745 113 buff_p++;
d9a64523 114
cb323159
A
115 /* contended test records statistics only for lock/unlock for now */
116 avg_p = strstr(buff_p, "run time ");
117 T_QUIET; T_ASSERT_NOTNULL(avg_p, "contended %d loop run time not found", 0);
118 sscanf(avg_p, "run time %llu", &run);
d9a64523 119
cb323159
A
120 avg_p = strstr(buff_p, "total time ");
121 T_QUIET; T_ASSERT_NOTNULL(avg_p, "uncontended %d loop total time not found", 0);
122 sscanf(avg_p, "total time %llu", &tot);
d9a64523 123
cb323159
A
124 if (run < tot) {
125 avg = run;
126 } else {
127 avg = tot;
128 }
d9a64523 129
cb323159
A
130 name = strstr(buff_p, "TEST_MTX_");
131 end_name = strstr(buff_p, "_STATS");
132 name_size = (unsigned long) end_name - (unsigned long) name - strlen("TEST_MTX_") + 1;
d9a64523 133
cb323159
A
134 char name_string[50];
135 char avg_name_string[60];
136 char *pre_string = "contended loop ";
137 snprintf(name_string, name_size + strlen(pre_string) + strlen(type), "%s%s%s", pre_string, type, &name[strlen("TEST_MTX_")]);
138 pre_string = "avg time contended loop ";
139 snprintf(avg_name_string, name_size + strlen(pre_string) + strlen(type), "%s%s%s", pre_string, type, &name[strlen("TEST_MTX_")]);
140 T_PERF(name_string, avg / ITER, "ns", avg_name_string);
141 }
d9a64523
A
142
143 free(buff);
144}
145
146static void
147test_from_kernel_lock_unlock_uncontended(void)
148{
cb323159
A
149 int i, ret;
150 unsigned long name_size;
d9a64523
A
151 uint64_t avg, run, tot;
152 size_t size;
153 char iter[35];
154 char *buff, *buff_p, *avg_p, *name, *end_name;
155
156 T_LOG("Testing locking/unlocking mutex from kernel without contention.\n");
157 T_LOG("Requesting test with %d iterations\n", ITER);
158
159 size = 2000;
160 buff = calloc(size, sizeof(char));
0a7de745 161 T_QUIET; T_ASSERT_NOTNULL(buff, "Allocating buffer fo sysctl");
d9a64523
A
162
163 snprintf(iter, sizeof(iter), "%d", ITER);
164 ret = sysctlbyname("kern.test_mtx_uncontended", buff, &size, iter, sizeof(iter));
165 T_ASSERT_POSIX_SUCCESS(ret, "sysctlbyname kern.test_mtx_uncontended");
166
167 T_LOG("%s stats:\n%s\n", __func__, buff);
168
169 /* first line is "STATS INNER LOOP" */
170 buff_p = buff;
0a7de745
A
171 while (*buff_p != '\n') {
172 buff_p++;
173 }
d9a64523
A
174 buff_p++;
175
176 /*
177 * Sequence of statistic lines like
178 * { samples 100000, tot 3586175 ns, avg 35 ns, max 3997 ns, min 33 ns } TEST_MTX_LOCK_STATS
179 * for all TEST_MTX_MAX_STATS statistics
180 */
181 for (i = 0; i < TEST_MTX_MAX_STATS; i++) {
182 avg_p = strstr(buff_p, "avg ");
0a7de745 183 T_QUIET; T_ASSERT_NOTNULL(avg_p, "uncontended %i average not found", i);
d9a64523
A
184 sscanf(avg_p, "avg %llu", &avg);
185
186 name = strstr(buff_p, "TEST_MTX_");
187 end_name = strstr(buff_p, "_STATS");
cb323159 188 name_size = (unsigned long) end_name - (unsigned long) name - strlen("TEST_MTX_") + 1;
d9a64523
A
189
190 char name_string[40];
191 char avg_name_string[50];
192 char *pre_string = "uncontended ";
193 snprintf(name_string, name_size + strlen(pre_string), "%s%s", pre_string, &name[strlen("TEST_MTX_")]);
194 pre_string = "avg time uncontended ";
195 snprintf(avg_name_string, name_size + strlen(pre_string), "%s%s", pre_string, &name[strlen("TEST_MTX_")]);
196 T_PERF(name_string, avg, "ns", avg_name_string);
197
198 buff_p = avg_p;
0a7de745
A
199 while (*buff_p != '\n') {
200 buff_p++;
201 }
d9a64523
A
202 buff_p++;
203 }
204
0a7de745
A
205 while (*buff_p != '\n') {
206 buff_p++;
207 }
d9a64523
A
208 buff_p++;
209
210 /* next line is "STATS OUTER LOOP" */
0a7de745
A
211 while (*buff_p != '\n') {
212 buff_p++;
213 }
d9a64523
A
214 buff_p++;
215
216 /*
217 * Sequence of statistic lines like
218 * total time 4040673 ns total run time 3981080 ns TEST_MTX_LOCK_STATS
219 * for all TEST_MTX_MAX_STATS statistics exept UNLOCK
220 */
221 for (i = 0; i < TEST_MTX_MAX_STATS - 2; i++) {
222 avg_p = strstr(buff_p, "run time ");
0a7de745 223 T_QUIET; T_ASSERT_NOTNULL(avg_p, "uncontended %d loop run time not found", i);
d9a64523
A
224 sscanf(avg_p, "run time %llu", &run);
225
226 avg_p = strstr(buff_p, "total time ");
0a7de745 227 T_QUIET; T_ASSERT_NOTNULL(avg_p, "uncontended %d loop total time not found", i);
d9a64523
A
228 sscanf(avg_p, "total time %llu", &tot);
229
0a7de745 230 if (run < tot) {
d9a64523 231 avg = run;
0a7de745 232 } else {
d9a64523 233 avg = tot;
0a7de745 234 }
d9a64523
A
235
236 name = strstr(buff_p, "TEST_MTX_");
237 end_name = strstr(buff_p, "_STATS");
cb323159 238 name_size = (unsigned long) end_name - (unsigned long) name - strlen("TEST_MTX_") + 1;
d9a64523
A
239
240 char name_string[50];
241 char avg_name_string[60];
242 char *pre_string = "uncontended loop ";
243 snprintf(name_string, name_size + strlen(pre_string), "%s%s", pre_string, &name[strlen("TEST_MTX_")]);
244 pre_string = "avg time uncontended loop ";
245 snprintf(avg_name_string, name_size + strlen(pre_string), "%s%s", pre_string, &name[strlen("TEST_MTX_")]);
0a7de745 246 T_PERF(name_string, avg / ITER, "ns", avg_name_string);
d9a64523
A
247
248 buff_p = avg_p;
0a7de745
A
249 while (*buff_p != '\n') {
250 buff_p++;
251 }
d9a64523 252 buff_p++;
d9a64523
A
253 }
254 free(buff);
255}
256
cb323159
A
257#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
258static bool
259get_freq(float val, char scale, int *int_val)
260{
261 switch (scale) {
262 case 'M':
263 case 'm':
264 *int_val = (int) val;
265 break;
266 case 'G':
267 case 'g':
268 *int_val = (int) (val * 1000);
269 break;
270 default:
271 return FALSE;
272 }
273 return TRUE;
274}
275
276static bool
277parse_freq(char* buff, int buff_size, const char* string_start, int string_start_size, char* to_parse)
278{
279 char* start;
280 float val;
281 char scale;
282 int int_val;
283
284 start = strstr(to_parse, string_start);
285 if (start == NULL) {
286 return FALSE;
287 }
288
289 if (strstr(start, "Hz") != NULL) {
290 sscanf(start + string_start_size, "%f%cHz", &val, &scale);
291 } else {
292 if (strstr(start, "hz") != NULL) {
293 sscanf(start + string_start_size, "%f%chz", &val, &scale);
294 } else {
295 return FALSE;
296 }
297 }
298
299 if (!get_freq(val, scale, &int_val)) {
300 return FALSE;
301 }
302
303 snprintf(buff, buff_size, "%d", int_val);
304
305 return TRUE;
306}
307
308static bool freq_fixed = FALSE;
309static char str_val_min[10];
310static char str_val_max[10];
311
312static bool
313get_previous_freq_values(void)
314{
315 FILE *fp;
316 char out_xcpm[1035];
317 bool min_scan = FALSE;
318 bool max_scan = FALSE;
319
320 memset(str_val_min, 0, sizeof(str_val_min));
321 memset(str_val_max, 0, sizeof(str_val_max));
322
323 fp = popen("/usr/local/bin/xcpm limits", "r");
324 if (fp == NULL) {
325 return FALSE;
326 }
327
328 while (fgets(out_xcpm, sizeof(out_xcpm) - 1, fp) != NULL && (!max_scan || !min_scan)) {
329 if (!max_scan) {
330 max_scan = parse_freq(str_val_max, sizeof(str_val_max), "Max frequency:", sizeof("Max frequency:"), out_xcpm);
331 }
332 if (!min_scan) {
333 min_scan = parse_freq(str_val_min, sizeof(str_val_min), "Min frequency:", sizeof("Min frequency:"), out_xcpm);
334 }
335 }
336
337 pclose(fp);
338
339 if (!max_scan || !min_scan) {
340 return FALSE;
341 }
342
343 return TRUE;
344}
345#endif
346
d9a64523
A
347static void
348fix_cpu_frequency(void)
349{
cb323159 350#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
d9a64523
A
351 int spawn_ret, pid;
352 char *const clpcctrl_args[] = {"/usr/local/bin/clpcctrl", "-f", "5000", NULL};
353
354 T_LOG("Setting cpu frequency to %d\n", 5000);
355
cb323159
A
356 spawn_ret = posix_spawn(&pid, clpcctrl_args[0], NULL, NULL, clpcctrl_args, *_NSGetEnviron());
357 T_QUIET; T_ASSERT_POSIX_ZERO(spawn_ret, "posix_spawn");
358 T_QUIET; T_ASSERT_EQ(waitpid(pid, &spawn_ret, 0), pid, "waitpid failed");
359 T_QUIET; T_ASSERT_EQ(spawn_ret, 0, " clpcctrl failed");
d9a64523 360
cb323159 361#else /*(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)*/
d9a64523
A
362
363 int spawn_ret, pid;
cb323159 364 int ret;
d9a64523 365 size_t len;
cb323159 366 char *buffer;
d9a64523
A
367 char str_val[10];
368
cb323159
A
369 if (!get_previous_freq_values()) {
370 T_LOG("Impossible to parse freq values from xcpm");
371 freq_fixed = FALSE;
372 return;
373 }
374
d9a64523 375 ret = sysctlbyname("machdep.cpu.brand_string", NULL, &len, NULL, 0);
0a7de745 376 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctlbyname machdep.cpu.brand_string");
d9a64523 377
cb323159 378 buffer = calloc(len + 2, sizeof(char));
d9a64523 379 ret = sysctlbyname("machdep.cpu.brand_string", buffer, &len, NULL, 0);
0a7de745
A
380 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctlbyname machdep.cpu.brand_string");
381 buffer[len + 1] = '\0';
d9a64523 382
cb323159
A
383 memset(str_val, 0, sizeof(str_val));
384 if (!parse_freq(str_val, sizeof(str_val), "CPU @", sizeof("CPU @"), buffer)) {
385 T_LOG("Impossible to parse freq values from machdep.cpu.brand_string (string was %s)", buffer);
386 freq_fixed = FALSE;
387 return;
d9a64523
A
388 }
389
cb323159
A
390 T_LOG("Previous min and max cpu frequency (%s) (%s)\n", str_val_min, str_val_max);
391 T_LOG("Setting min and max cpu frequency to (%s)\n", str_val);
d9a64523 392 char *xcpm_args[] = {"/usr/local/bin/xcpm", "limits", str_val, str_val, NULL};
cb323159
A
393 spawn_ret = posix_spawn(&pid, xcpm_args[0], NULL, NULL, xcpm_args, *_NSGetEnviron());
394 T_QUIET; T_ASSERT_POSIX_ZERO(spawn_ret, "posix_spawn");
395 T_QUIET; T_ASSERT_EQ(waitpid(pid, &spawn_ret, 0), pid, "waitpid failed");
396 T_QUIET; T_ASSERT_EQ(spawn_ret, 0, "xcpm limits failed");
397
398 freq_fixed = TRUE;
d9a64523 399
d9a64523
A
400 free(buffer);
401 return;
cb323159
A
402#endif /*(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)*/
403}
404
405static void
406cleanup_cpu_freq(void)
407{
408#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
409 int spawn_ret, pid;
410 char *const clpcctrl_args[] = {"/usr/local/bin/clpcctrl", "-d", NULL};
411 spawn_ret = posix_spawn(&pid, clpcctrl_args[0], NULL, NULL, clpcctrl_args, *_NSGetEnviron());
412 T_QUIET; T_ASSERT_POSIX_ZERO(spawn_ret, "posix_spawn");
413 T_QUIET; T_ASSERT_EQ(waitpid(pid, &spawn_ret, 0), pid, "waitpid failed");
414 T_QUIET; T_ASSERT_EQ(spawn_ret, 0, "clpcctrl failed");
415
416#else
417 if (freq_fixed) {
418 int spawn_ret, pid;
419 char *xcpm_args[] = {"/usr/local/bin/xcpm", "limits", str_val_min, str_val_max, NULL};
420 spawn_ret = posix_spawn(&pid, xcpm_args[0], NULL, NULL, xcpm_args, *_NSGetEnviron());
421 T_QUIET; T_ASSERT_POSIX_ZERO(spawn_ret, "posix_spawn");
422 T_QUIET; T_ASSERT_EQ(waitpid(pid, &spawn_ret, 0), pid, "waitpid failed");
423 T_QUIET; T_ASSERT_EQ(spawn_ret, 0, "xcpm limits failed");
424 }
425#endif
d9a64523
A
426}
427
428T_DECL(kernel_mtx_perf_test,
0a7de745
A
429 "Kernel mutex performance test",
430 T_META_ASROOT(YES), T_META_CHECK_LEAKS(NO))
d9a64523
A
431{
432 fix_cpu_frequency();
433
cb323159
A
434 T_ATEND(cleanup_cpu_freq);
435
d9a64523
A
436 test_from_kernel_lock_unlock_uncontended();
437 test_from_kernel_lock_unlock_contended();
438}