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
25 #define TEST_MTX_LOCK_STATS 0
26 #define TEST_MTX_UNLOCK_MTX_STATS 6
29 test_from_kernel_lock_unlock_contended(void)
31 int i
, ret
, name_size
;
32 uint64_t avg
, run
, tot
;
35 char *buff
, *buff_p
, *avg_p
, *name
, *end_name
;
37 T_LOG("Testing locking/unlocking mutex from kernel with contention.\n");
38 T_LOG("Requesting test with %d iterations\n", ITER
);
41 buff
= calloc(size
, sizeof(char));
42 T_QUIET
; T_ASSERT_NOTNULL(buff
, "Allocating buffer fo sysctl");
44 snprintf(iter
, sizeof(iter
), "%d", ITER
);
45 ret
= sysctlbyname("kern.test_mtx_contended", buff
, &size
, iter
, sizeof(iter
));
46 T_ASSERT_POSIX_SUCCESS(ret
, "sysctlbyname kern.test_mtx_contended");
48 T_LOG("%s stats:\n%s\n", __func__
, buff
);
50 /* first line is "STATS INNER LOOP" */
52 while (*buff_p
!= '\n') {
58 * Sequence of statistic lines like
59 * { samples 100000, tot 3586175 ns, avg 35 ns, max 3997 ns, min 33 ns } TEST_MTX_LOCK_STATS
60 * for all TEST_MTX_MAX_STATS statistics
62 for (i
= 0; i
< TEST_MTX_MAX_STATS
; i
++) {
63 avg_p
= strstr(buff_p
, "avg ");
65 /* contended test records statistics only for lock/unlock for now */
66 if (i
== TEST_MTX_LOCK_STATS
|| i
== TEST_MTX_UNLOCK_MTX_STATS
) {
67 T_QUIET
; T_ASSERT_NOTNULL(avg_p
, "contended %i average not found", i
);
68 sscanf(avg_p
, "avg %llu", &avg
);
70 name
= strstr(buff_p
, "TEST_MTX_");
71 end_name
= strstr(buff_p
, "_STATS");
72 name_size
= end_name
- name
- strlen("TEST_MTX_") + 1;
75 char avg_name_string
[50];
76 char *pre_string
= "contended ";
77 snprintf(name_string
, name_size
+ strlen(pre_string
), "%s%s", pre_string
, &name
[strlen("TEST_MTX_")]);
78 pre_string
= "avg contended ";
79 snprintf(avg_name_string
, name_size
+ strlen(pre_string
), "%s%s", pre_string
, &name
[strlen("TEST_MTX_")]);
80 T_PERF(name_string
, avg
, "ns", avg_name_string
);
84 while (*buff_p
!= '\n') {
90 while (*buff_p
!= '\n') {
95 /* next line is "STATS OUTER LOOP" */
96 while (*buff_p
!= '\n') {
101 /* contended test records statistics only for lock/unlock for now */
102 avg_p
= strstr(buff_p
, "run time ");
103 T_QUIET
; T_ASSERT_NOTNULL(avg_p
, "contended %d loop run time not found", 0);
104 sscanf(avg_p
, "run time %llu", &run
);
106 avg_p
= strstr(buff_p
, "total time ");
107 T_QUIET
; T_ASSERT_NOTNULL(avg_p
, "uncontended %d loop total time not found", 0);
108 sscanf(avg_p
, "total time %llu", &tot
);
116 name
= strstr(buff_p
, "TEST_MTX_");
117 end_name
= strstr(buff_p
, "_STATS");
118 name_size
= end_name
- name
- strlen("TEST_MTX_") + 1;
120 char name_string
[50];
121 char avg_name_string
[60];
122 char *pre_string
= "contended loop ";
123 snprintf(name_string
, name_size
+ strlen(pre_string
), "%s%s", pre_string
, &name
[strlen("TEST_MTX_")]);
124 pre_string
= "avg time contended loop ";
125 snprintf(avg_name_string
, name_size
+ strlen(pre_string
), "%s%s", pre_string
, &name
[strlen("TEST_MTX_")]);
126 T_PERF(name_string
, avg
/ ITER
, "ns", avg_name_string
);
132 test_from_kernel_lock_unlock_uncontended(void)
134 int i
, ret
, name_size
;
135 uint64_t avg
, run
, tot
;
138 char *buff
, *buff_p
, *avg_p
, *name
, *end_name
;
140 T_LOG("Testing locking/unlocking mutex from kernel without contention.\n");
141 T_LOG("Requesting test with %d iterations\n", ITER
);
144 buff
= calloc(size
, sizeof(char));
145 T_QUIET
; T_ASSERT_NOTNULL(buff
, "Allocating buffer fo sysctl");
147 snprintf(iter
, sizeof(iter
), "%d", ITER
);
148 ret
= sysctlbyname("kern.test_mtx_uncontended", buff
, &size
, iter
, sizeof(iter
));
149 T_ASSERT_POSIX_SUCCESS(ret
, "sysctlbyname kern.test_mtx_uncontended");
151 T_LOG("%s stats:\n%s\n", __func__
, buff
);
153 /* first line is "STATS INNER LOOP" */
155 while (*buff_p
!= '\n') {
161 * Sequence of statistic lines like
162 * { samples 100000, tot 3586175 ns, avg 35 ns, max 3997 ns, min 33 ns } TEST_MTX_LOCK_STATS
163 * for all TEST_MTX_MAX_STATS statistics
165 for (i
= 0; i
< TEST_MTX_MAX_STATS
; i
++) {
166 avg_p
= strstr(buff_p
, "avg ");
167 T_QUIET
; T_ASSERT_NOTNULL(avg_p
, "uncontended %i average not found", i
);
168 sscanf(avg_p
, "avg %llu", &avg
);
170 name
= strstr(buff_p
, "TEST_MTX_");
171 end_name
= strstr(buff_p
, "_STATS");
172 name_size
= end_name
- name
- strlen("TEST_MTX_") + 1;
174 char name_string
[40];
175 char avg_name_string
[50];
176 char *pre_string
= "uncontended ";
177 snprintf(name_string
, name_size
+ strlen(pre_string
), "%s%s", pre_string
, &name
[strlen("TEST_MTX_")]);
178 pre_string
= "avg time uncontended ";
179 snprintf(avg_name_string
, name_size
+ strlen(pre_string
), "%s%s", pre_string
, &name
[strlen("TEST_MTX_")]);
180 T_PERF(name_string
, avg
, "ns", avg_name_string
);
183 while (*buff_p
!= '\n') {
189 while (*buff_p
!= '\n') {
194 /* next line is "STATS OUTER LOOP" */
195 while (*buff_p
!= '\n') {
201 * Sequence of statistic lines like
202 * total time 4040673 ns total run time 3981080 ns TEST_MTX_LOCK_STATS
203 * for all TEST_MTX_MAX_STATS statistics exept UNLOCK
205 for (i
= 0; i
< TEST_MTX_MAX_STATS
- 2; i
++) {
206 avg_p
= strstr(buff_p
, "run time ");
207 T_QUIET
; T_ASSERT_NOTNULL(avg_p
, "uncontended %d loop run time not found", i
);
208 sscanf(avg_p
, "run time %llu", &run
);
210 avg_p
= strstr(buff_p
, "total time ");
211 T_QUIET
; T_ASSERT_NOTNULL(avg_p
, "uncontended %d loop total time not found", i
);
212 sscanf(avg_p
, "total time %llu", &tot
);
220 name
= strstr(buff_p
, "TEST_MTX_");
221 end_name
= strstr(buff_p
, "_STATS");
222 name_size
= end_name
- name
- strlen("TEST_MTX_") + 1;
224 char name_string
[50];
225 char avg_name_string
[60];
226 char *pre_string
= "uncontended loop ";
227 snprintf(name_string
, name_size
+ strlen(pre_string
), "%s%s", pre_string
, &name
[strlen("TEST_MTX_")]);
228 pre_string
= "avg time uncontended loop ";
229 snprintf(avg_name_string
, name_size
+ strlen(pre_string
), "%s%s", pre_string
, &name
[strlen("TEST_MTX_")]);
230 T_PERF(name_string
, avg
/ ITER
, "ns", avg_name_string
);
233 while (*buff_p
!= '\n') {
241 extern char **environ
;
243 fix_cpu_frequency(void)
247 char *const clpcctrl_args
[] = {"/usr/local/bin/clpcctrl", "-f", "5000", NULL
};
249 T_LOG("Setting cpu frequency to %d\n", 5000);
251 spawn_ret
= posix_spawn(&pid
, clpcctrl_args
[0], NULL
, NULL
, clpcctrl_args
, environ
);
252 waitpid(pid
, &spawn_ret
, 0);
254 #else /*CONFIG_EMBEDDED*/
261 char *buffer
, *cpu_freq
;
264 ret
= sysctlbyname("machdep.cpu.brand_string", NULL
, &len
, NULL
, 0);
265 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "sysctlbyname machdep.cpu.brand_string");
267 buffer
= malloc(len
+ 2);
268 ret
= sysctlbyname("machdep.cpu.brand_string", buffer
, &len
, NULL
, 0);
269 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "sysctlbyname machdep.cpu.brand_string");
270 buffer
[len
+ 1] = '\0';
272 cpu_freq
= strstr(buffer
, "CPU @ ");
273 if (cpu_freq
== NULL
) {
274 T_LOG("Could not fix frequency, %s field not present\n", "CPU @ ");
278 if (strstr(cpu_freq
, "Hz") != NULL
) {
279 sscanf(cpu_freq
, "CPU @ %f%cHz", &val
, &scale
);
281 if (strstr(cpu_freq
, "hz") != NULL
) {
282 sscanf(cpu_freq
, "CPU @ %f%chz", &val
, &scale
);
284 T_LOG("Could not fix frequency, %s field not present\n", "Hz");
292 nom_freq
= (int) val
;
296 nom_freq
= (int) (val
* 1000);
299 T_LOG("Could not fix frequency, scale field is %c\n", scale
);
303 snprintf(str_val
, 10, "%d", nom_freq
);
304 T_LOG("Setting min and max cpu frequency to %d (%s)\n", nom_freq
, str_val
);
305 char *xcpm_args
[] = {"/usr/local/bin/xcpm", "limits", str_val
, str_val
, NULL
};
306 spawn_ret
= posix_spawn(&pid
, xcpm_args
[0], NULL
, NULL
, xcpm_args
, environ
);
307 waitpid(pid
, &spawn_ret
, 0);
312 #endif /*CONFIG_EMBEDDED*/
315 T_DECL(kernel_mtx_perf_test
,
316 "Kernel mutex performance test",
317 T_META_ASROOT(YES
), T_META_CHECK_LEAKS(NO
))
321 test_from_kernel_lock_unlock_uncontended();
322 test_from_kernel_lock_unlock_contended();