]> git.saurik.com Git - apple/xnu.git/blob - tests/kernel_mtx_perf.c
xnu-4903.241.1.tar.gz
[apple/xnu.git] / tests / kernel_mtx_perf.c
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
20 T_GLOBAL_META(T_META_NAMESPACE("xnu.kernel_mtx_perf_test"));
21
22 #define ITER 100000
23 #define TEST_MTX_MAX_STATS 8
24
25 #define TEST_MTX_LOCK_STATS 0
26 #define TEST_MTX_UNLOCK_MTX_STATS 6
27
28 static void
29 test_from_kernel_lock_unlock_contended(void)
30 {
31 int i, ret, name_size;
32 uint64_t avg, run, tot;
33 size_t size;
34 char iter[35];
35 char *buff, *buff_p, *avg_p, *name, *end_name;
36
37 T_LOG("Testing locking/unlocking mutex from kernel with contention.\n");
38 T_LOG("Requesting test with %d iterations\n", ITER);
39
40 size = 1000;
41 buff = calloc(size, sizeof(char));
42 T_QUIET;T_ASSERT_NOTNULL(buff, "Allocating buffer fo sysctl");
43
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");
47
48 T_LOG("%s stats:\n%s\n", __func__, buff);
49
50 /* first line is "STATS INNER LOOP" */
51 buff_p = buff;
52 while( *buff_p != '\n' ) buff_p++;
53 buff_p++;
54
55 /*
56 * Sequence of statistic lines like
57 * { samples 100000, tot 3586175 ns, avg 35 ns, max 3997 ns, min 33 ns } TEST_MTX_LOCK_STATS
58 * for all TEST_MTX_MAX_STATS statistics
59 */
60 for (i = 0; i < TEST_MTX_MAX_STATS; i++) {
61 avg_p = strstr(buff_p, "avg ");
62
63 /* contended test records statistics only for lock/unlock for now */
64 if (i == TEST_MTX_LOCK_STATS || i == TEST_MTX_UNLOCK_MTX_STATS ) {
65 T_QUIET;T_ASSERT_NOTNULL(avg_p, "contended %i average not found", i);
66 sscanf(avg_p, "avg %llu", &avg);
67
68 name = strstr(buff_p, "TEST_MTX_");
69 end_name = strstr(buff_p, "_STATS");
70 name_size = end_name - name - strlen("TEST_MTX_") + 1;
71
72 char name_string[40];
73 char avg_name_string[50];
74 char *pre_string = "contended ";
75 snprintf(name_string, name_size + strlen(pre_string), "%s%s", pre_string, &name[strlen("TEST_MTX_")]);
76 pre_string = "avg contended ";
77 snprintf(avg_name_string, name_size + strlen(pre_string), "%s%s", pre_string, &name[strlen("TEST_MTX_")]);
78 T_PERF(name_string, avg, "ns", avg_name_string);
79 }
80
81 buff_p = avg_p;
82 while( *buff_p != '\n' ) buff_p++;
83 buff_p++;
84
85 }
86
87 while( *buff_p != '\n' ) buff_p++;
88 buff_p++;
89
90 /* next line is "STATS OUTER LOOP" */
91 while( *buff_p != '\n' ) buff_p++;
92 buff_p++;
93
94 /* contended test records statistics only for lock/unlock for now */
95 avg_p = strstr(buff_p, "run time ");
96 T_QUIET;T_ASSERT_NOTNULL(avg_p, "contended %d loop run time not found", 0);
97 sscanf(avg_p, "run time %llu", &run);
98
99 avg_p = strstr(buff_p, "total time ");
100 T_QUIET;T_ASSERT_NOTNULL(avg_p, "uncontended %d loop total time not found", 0);
101 sscanf(avg_p, "total time %llu", &tot);
102
103 if (run < tot)
104 avg = run;
105 else
106 avg = tot;
107
108 name = strstr(buff_p, "TEST_MTX_");
109 end_name = strstr(buff_p, "_STATS");
110 name_size = end_name - name - strlen("TEST_MTX_") + 1;
111
112 char name_string[50];
113 char avg_name_string[60];
114 char *pre_string = "contended loop ";
115 snprintf(name_string, name_size + strlen(pre_string), "%s%s", pre_string, &name[strlen("TEST_MTX_")]);
116 pre_string = "avg time contended loop ";
117 snprintf(avg_name_string, name_size + strlen(pre_string), "%s%s", pre_string, &name[strlen("TEST_MTX_")]);
118 T_PERF(name_string, avg/ITER, "ns", avg_name_string);
119
120 free(buff);
121 }
122
123 static void
124 test_from_kernel_lock_unlock_uncontended(void)
125 {
126 int i, ret, name_size;
127 uint64_t avg, run, tot;
128 size_t size;
129 char iter[35];
130 char *buff, *buff_p, *avg_p, *name, *end_name;
131
132 T_LOG("Testing locking/unlocking mutex from kernel without contention.\n");
133 T_LOG("Requesting test with %d iterations\n", ITER);
134
135 size = 2000;
136 buff = calloc(size, sizeof(char));
137 T_QUIET;T_ASSERT_NOTNULL(buff, "Allocating buffer fo sysctl");
138
139 snprintf(iter, sizeof(iter), "%d", ITER);
140 ret = sysctlbyname("kern.test_mtx_uncontended", buff, &size, iter, sizeof(iter));
141 T_ASSERT_POSIX_SUCCESS(ret, "sysctlbyname kern.test_mtx_uncontended");
142
143 T_LOG("%s stats:\n%s\n", __func__, buff);
144
145 /* first line is "STATS INNER LOOP" */
146 buff_p = buff;
147 while( *buff_p != '\n' ) buff_p++;
148 buff_p++;
149
150 /*
151 * Sequence of statistic lines like
152 * { samples 100000, tot 3586175 ns, avg 35 ns, max 3997 ns, min 33 ns } TEST_MTX_LOCK_STATS
153 * for all TEST_MTX_MAX_STATS statistics
154 */
155 for (i = 0; i < TEST_MTX_MAX_STATS; i++) {
156 avg_p = strstr(buff_p, "avg ");
157 T_QUIET;T_ASSERT_NOTNULL(avg_p, "uncontended %i average not found", i);
158 sscanf(avg_p, "avg %llu", &avg);
159
160 name = strstr(buff_p, "TEST_MTX_");
161 end_name = strstr(buff_p, "_STATS");
162 name_size = end_name - name - strlen("TEST_MTX_") + 1;
163
164 char name_string[40];
165 char avg_name_string[50];
166 char *pre_string = "uncontended ";
167 snprintf(name_string, name_size + strlen(pre_string), "%s%s", pre_string, &name[strlen("TEST_MTX_")]);
168 pre_string = "avg time uncontended ";
169 snprintf(avg_name_string, name_size + strlen(pre_string), "%s%s", pre_string, &name[strlen("TEST_MTX_")]);
170 T_PERF(name_string, avg, "ns", avg_name_string);
171
172 buff_p = avg_p;
173 while( *buff_p != '\n' ) buff_p++;
174 buff_p++;
175 }
176
177 while( *buff_p != '\n' ) buff_p++;
178 buff_p++;
179
180 /* next line is "STATS OUTER LOOP" */
181 while( *buff_p != '\n' ) buff_p++;
182 buff_p++;
183
184 /*
185 * Sequence of statistic lines like
186 * total time 4040673 ns total run time 3981080 ns TEST_MTX_LOCK_STATS
187 * for all TEST_MTX_MAX_STATS statistics exept UNLOCK
188 */
189 for (i = 0; i < TEST_MTX_MAX_STATS - 2; i++) {
190 avg_p = strstr(buff_p, "run time ");
191 T_QUIET;T_ASSERT_NOTNULL(avg_p, "uncontended %d loop run time not found", i);
192 sscanf(avg_p, "run time %llu", &run);
193
194 avg_p = strstr(buff_p, "total time ");
195 T_QUIET;T_ASSERT_NOTNULL(avg_p, "uncontended %d loop total time not found", i);
196 sscanf(avg_p, "total time %llu", &tot);
197
198 if (run < tot)
199 avg = run;
200 else
201 avg = tot;
202
203 name = strstr(buff_p, "TEST_MTX_");
204 end_name = strstr(buff_p, "_STATS");
205 name_size = end_name - name - strlen("TEST_MTX_") + 1;
206
207 char name_string[50];
208 char avg_name_string[60];
209 char *pre_string = "uncontended loop ";
210 snprintf(name_string, name_size + strlen(pre_string), "%s%s", pre_string, &name[strlen("TEST_MTX_")]);
211 pre_string = "avg time uncontended loop ";
212 snprintf(avg_name_string, name_size + strlen(pre_string), "%s%s", pre_string, &name[strlen("TEST_MTX_")]);
213 T_PERF(name_string, avg/ITER, "ns", avg_name_string);
214
215 buff_p = avg_p;
216 while( *buff_p != '\n' ) buff_p++;
217 buff_p++;
218
219 }
220 free(buff);
221 }
222
223 extern char **environ;
224 static void
225 fix_cpu_frequency(void)
226 {
227 #if CONFIG_EMBEDDED
228 int spawn_ret, pid;
229 char *const clpcctrl_args[] = {"/usr/local/bin/clpcctrl", "-f", "5000", NULL};
230
231 T_LOG("Setting cpu frequency to %d\n", 5000);
232
233 spawn_ret = posix_spawn(&pid, clpcctrl_args[0], NULL, NULL, clpcctrl_args, environ);
234 waitpid(pid, &spawn_ret, 0);
235
236 #else /*CONFIG_EMBEDDED*/
237
238 int spawn_ret, pid;
239 int ret, nom_freq;
240 size_t len;
241 float val;
242 char scale;
243 char *buffer, *cpu_freq;
244 char str_val[10];
245
246 ret = sysctlbyname("machdep.cpu.brand_string", NULL, &len, NULL, 0);
247 T_QUIET;T_ASSERT_POSIX_SUCCESS(ret, "sysctlbyname machdep.cpu.brand_string");
248
249 buffer = malloc(len+2);
250 ret = sysctlbyname("machdep.cpu.brand_string", buffer, &len, NULL, 0);
251 T_QUIET;T_ASSERT_POSIX_SUCCESS(ret, "sysctlbyname machdep.cpu.brand_string");
252 buffer[len+1] = '\0';
253
254 cpu_freq = strstr(buffer, "CPU @ ");
255 if (cpu_freq == NULL) {
256 T_LOG("Could not fix frequency, %s field not present\n", "CPU @ ");
257 goto out;
258 }
259
260 if (strstr(cpu_freq, "Hz") != NULL) {
261 sscanf(cpu_freq, "CPU @ %f%cHz", &val, &scale);
262 } else {
263 if (strstr(cpu_freq, "hz") != NULL) {
264 sscanf(cpu_freq, "CPU @ %f%chz", &val, &scale);
265 } else {
266 T_LOG("Could not fix frequency, %s field not present\n", "Hz");
267 goto out;
268 }
269 }
270
271 switch(scale){
272 case 'M':
273 case 'm':
274 nom_freq = (int) val;
275 break;
276 case 'G':
277 case 'g':
278 nom_freq = (int) (val*1000);
279 break;
280 default:
281 T_LOG("Could not fix frequency, scale field is %c\n", scale);
282 goto out;
283 }
284
285 snprintf(str_val, 10, "%d", nom_freq);
286 T_LOG("Setting min and max cpu frequency to %d (%s)\n", nom_freq, str_val);
287 char *xcpm_args[] = {"/usr/local/bin/xcpm", "limits", str_val, str_val, NULL};
288 spawn_ret = posix_spawn(&pid, xcpm_args[0], NULL, NULL, xcpm_args, environ);
289 waitpid(pid, &spawn_ret, 0);
290
291 out:
292 free(buffer);
293 return;
294 #endif /*CONFIG_EMBEDDED*/
295 }
296
297 T_DECL(kernel_mtx_perf_test,
298 "Kernel mutex performance test",
299 T_META_ASROOT(YES), T_META_CHECK_LEAKS(NO))
300 {
301 fix_cpu_frequency();
302
303 test_from_kernel_lock_unlock_uncontended();
304 test_from_kernel_lock_unlock_contended();
305 }
306