5 #include <darwintest.h>
6 #include <darwintest_multiprocess.h>
7 #include <darwintest_utils.h>
10 #include <servers/bootstrap.h>
12 #include <sys/event.h>
14 #include <crt_externs.h>
15 #include <sys/sysctl.h>
16 #include <sys/types.h>
20 T_GLOBAL_META(T_META_NAMESPACE("xnu.kernel_mtx_perf_test"));
23 #define TEST_MTX_MAX_STATS 8
24 #define FULL_CONTENDED 0
25 #define HALF_CONTENDED 1
26 #define MAX_CONDENDED 2
29 #define TEST_MTX_LOCK_STATS 0
30 #define TEST_MTX_UNLOCK_MTX_STATS 6
33 test_from_kernel_lock_unlock_contended(void)
36 unsigned long name_size
;
37 uint64_t avg
, run
, tot
;
40 char *buff
, *buff_p
, *avg_p
, *name
, *end_name
;
42 T_LOG("Testing locking/unlocking mutex from kernel with contention.\n");
43 T_LOG("Requesting test with %d iterations\n", ITER
);
46 buff
= calloc(size
, sizeof(char));
47 T_QUIET
; T_ASSERT_NOTNULL(buff
, "Allocating buffer fo sysctl");
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");
53 T_LOG("\n%s stats :\n%s\n", __func__
, buff
);
57 for (t
= 0; t
< MAX_CONDENDED
; t
++) {
59 if (t
== FULL_CONTENDED
) {
60 type
= "FULL_CONTENDED ";
62 type
= "HALF_CONTENDED ";
65 /* first line is "STATS INNER LOOP" */
66 while (*buff_p
!= '\n') {
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
76 for (i
= 0; i
< TEST_MTX_MAX_STATS
; i
++) {
77 avg_p
= strstr(buff_p
, "avg ");
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
);
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;
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
);
98 while (*buff_p
!= '\n') {
104 while (*buff_p
!= '\n') {
109 /* next line is "STATS OUTER LOOP" */
110 while (*buff_p
!= '\n') {
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
);
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
);
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;
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
);
147 test_from_kernel_lock_unlock_uncontended(void)
150 unsigned long name_size
;
151 uint64_t avg
, run
, tot
;
154 char *buff
, *buff_p
, *avg_p
, *name
, *end_name
;
156 T_LOG("Testing locking/unlocking mutex from kernel without contention.\n");
157 T_LOG("Requesting test with %d iterations\n", ITER
);
160 buff
= calloc(size
, sizeof(char));
161 T_QUIET
; T_ASSERT_NOTNULL(buff
, "Allocating buffer fo sysctl");
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");
167 T_LOG("%s stats:\n%s\n", __func__
, buff
);
169 /* first line is "STATS INNER LOOP" */
171 while (*buff_p
!= '\n') {
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
181 for (i
= 0; i
< TEST_MTX_MAX_STATS
; i
++) {
182 avg_p
= strstr(buff_p
, "avg ");
183 T_QUIET
; T_ASSERT_NOTNULL(avg_p
, "uncontended %i average not found", i
);
184 sscanf(avg_p
, "avg %llu", &avg
);
186 name
= strstr(buff_p
, "TEST_MTX_");
187 end_name
= strstr(buff_p
, "_STATS");
188 name_size
= (unsigned long) end_name
- (unsigned long) name
- strlen("TEST_MTX_") + 1;
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
);
199 while (*buff_p
!= '\n') {
205 while (*buff_p
!= '\n') {
210 /* next line is "STATS OUTER LOOP" */
211 while (*buff_p
!= '\n') {
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
221 for (i
= 0; i
< TEST_MTX_MAX_STATS
- 2; i
++) {
222 avg_p
= strstr(buff_p
, "run time ");
223 T_QUIET
; T_ASSERT_NOTNULL(avg_p
, "uncontended %d loop run time not found", i
);
224 sscanf(avg_p
, "run time %llu", &run
);
226 avg_p
= strstr(buff_p
, "total time ");
227 T_QUIET
; T_ASSERT_NOTNULL(avg_p
, "uncontended %d loop total time not found", i
);
228 sscanf(avg_p
, "total time %llu", &tot
);
236 name
= strstr(buff_p
, "TEST_MTX_");
237 end_name
= strstr(buff_p
, "_STATS");
238 name_size
= (unsigned long) end_name
- (unsigned long) name
- strlen("TEST_MTX_") + 1;
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_")]);
246 T_PERF(name_string
, avg
/ ITER
, "ns", avg_name_string
);
249 while (*buff_p
!= '\n') {
257 #if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
259 get_freq(float val
, char scale
, int *int_val
)
264 *int_val
= (int) val
;
268 *int_val
= (int) (val
* 1000);
277 parse_freq(char* buff
, int buff_size
, const char* string_start
, int string_start_size
, char* to_parse
)
284 start
= strstr(to_parse
, string_start
);
289 if (strstr(start
, "Hz") != NULL
) {
290 sscanf(start
+ string_start_size
, "%f%cHz", &val
, &scale
);
292 if (strstr(start
, "hz") != NULL
) {
293 sscanf(start
+ string_start_size
, "%f%chz", &val
, &scale
);
299 if (!get_freq(val
, scale
, &int_val
)) {
303 snprintf(buff
, buff_size
, "%d", int_val
);
308 static bool freq_fixed
= FALSE
;
309 static char str_val_min
[10];
310 static char str_val_max
[10];
313 get_previous_freq_values(void)
317 bool min_scan
= FALSE
;
318 bool max_scan
= FALSE
;
320 memset(str_val_min
, 0, sizeof(str_val_min
));
321 memset(str_val_max
, 0, sizeof(str_val_max
));
323 fp
= popen("/usr/local/bin/xcpm limits", "r");
328 while (fgets(out_xcpm
, sizeof(out_xcpm
) - 1, fp
) != NULL
&& (!max_scan
|| !min_scan
)) {
330 max_scan
= parse_freq(str_val_max
, sizeof(str_val_max
), "Max frequency:", sizeof("Max frequency:"), out_xcpm
);
333 min_scan
= parse_freq(str_val_min
, sizeof(str_val_min
), "Min frequency:", sizeof("Min frequency:"), out_xcpm
);
339 if (!max_scan
|| !min_scan
) {
348 fix_cpu_frequency(void)
350 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
352 char *const clpcctrl_args
[] = {"/usr/local/bin/clpcctrl", "-f", "5000", NULL
};
354 T_LOG("Setting cpu frequency to %d\n", 5000);
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");
361 #else /*(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)*/
369 if (!get_previous_freq_values()) {
370 T_LOG("Impossible to parse freq values from xcpm");
375 ret
= sysctlbyname("machdep.cpu.brand_string", NULL
, &len
, NULL
, 0);
376 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "sysctlbyname machdep.cpu.brand_string");
378 buffer
= calloc(len
+ 2, sizeof(char));
379 ret
= sysctlbyname("machdep.cpu.brand_string", buffer
, &len
, NULL
, 0);
380 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "sysctlbyname machdep.cpu.brand_string");
381 buffer
[len
+ 1] = '\0';
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
);
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
);
392 char *xcpm_args
[] = {"/usr/local/bin/xcpm", "limits", str_val
, str_val
, NULL
};
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");
402 #endif /*(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)*/
406 cleanup_cpu_freq(void)
408 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
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");
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");
428 T_DECL(kernel_mtx_perf_test
,
429 "Kernel mutex performance test",
430 T_META_ASROOT(YES
), T_META_CHECK_LEAKS(NO
))
434 T_ATEND(cleanup_cpu_freq
);
436 test_from_kernel_lock_unlock_uncontended();
437 test_from_kernel_lock_unlock_contended();