]> git.saurik.com Git - apple/xnu.git/blame - tests/mach_continuous_time.c
xnu-6153.81.5.tar.gz
[apple/xnu.git] / tests / mach_continuous_time.c
CommitLineData
39037602
A
1#include <mach/mach.h>
2#include <mach/mach_time.h>
3#include <mach/clock_types.h>
4#include <sys/time.h>
5#include <spawn.h>
6#include <sys/wait.h>
7#include <stdio.h>
8#include <unistd.h>
9#include <stdlib.h>
10#include <time.h>
11#include <errno.h>
12
13#include <darwintest.h>
14
5ba3f43e
A
15#if (defined(__arm__) || defined(__arm64__))
16#define HAS_KERNEL_TIME_TRAPS
17
18extern uint64_t mach_absolute_time_kernel(void);
19extern uint64_t mach_continuous_time_kernel(void);
20
21#endif
0a7de745 22
39037602
A
23extern char **environ;
24
0a7de745 25static const int64_t one_mil = 1000 * 1000;
39037602 26
5ba3f43e 27#define to_ns(ticks) (((ticks) * tb_info.numer) / (tb_info.denom))
39037602
A
28#define to_ms(ticks) (to_ns(ticks)/one_mil)
29
30static mach_timebase_info_data_t tb_info;
31
32static void
0a7de745
A
33update(uint64_t *a, uint64_t *c)
34{
35 mach_get_times(a, c, NULL);
39037602
A
36}
37
38T_DECL(mct_monotonic, "Testing mach_continuous_time returns sane, monotonic values",
cb323159 39 T_META_ALL_VALID_ARCHS(true), T_META_RUN_CONCURRENTLY(true))
39037602
A
40{
41 mach_timebase_info(&tb_info);
5ba3f43e
A
42#ifdef HAS_KERNEL_TIME_TRAPS
43 bool kernel = false;
44#endif
39037602
A
45
46 volatile uint64_t multiple_test = to_ms(mach_continuous_time());
0a7de745 47 for (int i = 0; i < 20; i++) {
5ba3f43e
A
48 uint64_t tmp;
49 const char *test_type = "user";
50#ifdef HAS_KERNEL_TIME_TRAPS
51 if (kernel) {
52 test_type = "kernel";
53 tmp = mach_continuous_time_kernel();
0a7de745 54 } else {
5ba3f43e 55 tmp = mach_continuous_time();
0a7de745 56 }
5ba3f43e
A
57 kernel = !kernel;
58#else
59 tmp = mach_continuous_time();
60#endif
61 tmp = to_ms(tmp);
62 T_ASSERT_GE(tmp, multiple_test, "mach_continuous_time (%s) must be monotonic", test_type);
63
64 // each successive call shouldn't be more than 100ms in the future
65 T_ASSERT_LE(tmp - multiple_test, 100ULL, "mach_continuous_time (%s) should not jump forward too fast", test_type);
66
67 multiple_test = tmp;
68 }
69}
70
71T_DECL(mat_monotonic, "Testing mach_absolute_time returns sane, monotonic values",
cb323159 72 T_META_ALL_VALID_ARCHS(true), T_META_RUN_CONCURRENTLY(true))
5ba3f43e
A
73{
74 mach_timebase_info(&tb_info);
75#ifdef HAS_KERNEL_TIME_TRAPS
76 bool kernel = false;
77#endif
78
79 volatile uint64_t multiple_test = to_ms(mach_absolute_time());
0a7de745 80 for (int i = 0; i < 20; i++) {
5ba3f43e
A
81 uint64_t tmp;
82 const char *test_type = "user";
83#ifdef HAS_KERNEL_TIME_TRAPS
84 if (kernel) {
85 test_type = "kernel";
86 tmp = mach_absolute_time_kernel();
0a7de745 87 } else {
5ba3f43e 88 tmp = mach_absolute_time();
0a7de745 89 }
5ba3f43e
A
90 kernel = !kernel;
91#endif
92 tmp = mach_absolute_time();
93 tmp = to_ms(tmp);
94 T_ASSERT_GE(tmp, multiple_test, "mach_absolute_time (%s) must be monotonic", test_type);
39037602 95
5ba3f43e
A
96 // each successive call shouldn't be more than 100ms in the future
97 T_ASSERT_LE(tmp - multiple_test, 100ULL, "mach_absolute_time (%s) should not jump forward too fast", test_type);
39037602
A
98
99 multiple_test = tmp;
100 }
101}
102
cb323159
A
103T_DECL(mct_pause, "Testing mach_continuous_time and mach_absolute_time don't diverge",
104 T_META_RUN_CONCURRENTLY(true))
39037602
A
105{
106 mach_timebase_info(&tb_info);
107
108 uint64_t abs_now;
109 uint64_t cnt_now;
110 int before_diff, after_diff;
111
112 update(&abs_now, &cnt_now);
113 before_diff = (int)(to_ms(cnt_now) - to_ms(abs_now));
114
115 sleep(1);
116
117 update(&abs_now, &cnt_now);
118 after_diff = (int)(to_ms(cnt_now) - to_ms(abs_now));
119
120 T_ASSERT_LE(abs(after_diff - before_diff), 1, "mach_continuous_time and mach_absolute_time should not diverge");
121}
122
5ba3f43e 123#ifdef HAS_KERNEL_TIME_TRAPS
0a7de745
A
124static void
125update_kern(uint64_t *abs, uint64_t *cont)
5ba3f43e
A
126{
127 uint64_t abs1, abs2, cont1, cont2;
128 do {
129 abs1 = mach_absolute_time_kernel();
130 cont1 = mach_continuous_time_kernel();
131 abs2 = mach_absolute_time_kernel();
132 cont2 = mach_continuous_time_kernel();
133 } while (to_ms(abs2 - abs1) || to_ms(cont2 - cont1));
134 *abs = abs2;
135 *cont = cont2;
136}
137#endif
138
139#ifdef HAS_KERNEL_TIME_TRAPS
cb323159
A
140T_DECL(mct_pause_kern, "Testing kernel mach_continuous_time and mach_absolute_time don't diverge",
141 T_META_RUN_CONCURRENTLY(true))
5ba3f43e
A
142{
143 mach_timebase_info(&tb_info);
144
145 uint64_t abs_now;
146 uint64_t cnt_now;
147 int before_diff, after_diff;
148
149 update_kern(&abs_now, &cnt_now);
150 before_diff = (int)(to_ms(cnt_now) - to_ms(abs_now));
151
152 sleep(1);
153
154 update_kern(&abs_now, &cnt_now);
155 after_diff = (int)(to_ms(cnt_now) - to_ms(abs_now));
156
157 T_ASSERT_LE(abs(after_diff - before_diff), 1, "mach_continuous_time_kernel and mach_absolute_time_kernel should not diverge");
158}
159#endif
160
39037602
A
161T_DECL(mct_sleep, "Testing mach_continuous_time behavior over system sleep"){
162#ifndef MCT_SLEEP_TEST
163 T_SKIP("Skipping test that sleeps the device; compile with MCT_SLEEP_TEST define to enable.");
164#endif
165
166 mach_timebase_info(&tb_info);
167
168 uint64_t abs_now;
169 uint64_t cnt_now;
170 int before_diff, after_diff = 0;
171
172 T_LOG("Testing mach_continuous_time is ~5 seconds ahead of mach_absolute_time after 5 second sleep");
173 update(&abs_now, &cnt_now);
174 before_diff = (int)(to_ms(cnt_now) - to_ms(abs_now));
175
176 // performs:
177 // pmset relative wake 5
178 // pmset sleepnow
179
180 pid_t pid;
181 int spawn_ret = 0;
182 time_t before_sleep = time(NULL);
183 int ct_ms_before_sleep = (int)to_ms(cnt_now);
184 int ab_ms_before_sleep = (int)to_ms(abs_now);
185
186 char *const pmset1_args[] = {"/usr/bin/pmset", "relative", "wake", "5", NULL};
187 T_ASSERT_POSIX_ZERO((spawn_ret = posix_spawn(&pid, pmset1_args[0], NULL, NULL, pmset1_args, environ)), NULL);
188
189 T_ASSERT_EQ(waitpid(pid, &spawn_ret, 0), pid, "waitpid failed");
190 T_ASSERT_EQ(spawn_ret, 0, "pmset relative wait 5 failed");
191
192 char *const pmset2_args[] = {"/usr/bin/pmset", "sleepnow", NULL};
193 T_ASSERT_POSIX_ZERO((spawn_ret = posix_spawn(&pid, pmset2_args[0], NULL, NULL, pmset2_args, environ)), NULL);
194
195 T_ASSERT_EQ(waitpid(pid, &spawn_ret, 0), pid, "waitpid failed");
196 T_ASSERT_EQ(spawn_ret, 0, "pmset relative wait 5 failed");
197
198 // wait for device to sleep (up to 30 seconds)
0a7de745 199 for (int i = 0; i < 30; i++) {
39037602
A
200 update(&abs_now, &cnt_now);
201 after_diff = (int)(to_ms(cnt_now) - to_ms(abs_now));
202
203 // on OSX, there's enough latency between calls to MCT and MAT
204 // when the system is going down for sleep for values to diverge a few ms
0a7de745 205 if (abs(before_diff - after_diff) > 2) {
39037602
A
206 break;
207 }
208
209 sleep(1);
0a7de745 210 T_LOG("waited %d seconds for sleep...", i + 1);
39037602
A
211 }
212
0a7de745 213 if ((after_diff - before_diff) < 4000) {
39037602 214 T_LOG("Device slept for less than 4 seconds, did it really sleep? (%d ms change between abs and cont)",
0a7de745 215 after_diff - before_diff);
39037602
A
216 }
217
218 time_t after_sleep = time(NULL);
219
220 int cal_sleep_diff = (int)(double)difftime(after_sleep, before_sleep);
0a7de745
A
221 int ct_sleep_diff = ((int)to_ms(cnt_now) - ct_ms_before_sleep) / 1000;
222 int ab_sleep_diff = ((int)to_ms(abs_now) - ab_ms_before_sleep) / 1000;
39037602
A
223
224 T_LOG("Calendar progressed: %d sec; continuous time progressed: %d sec; absolute time progressed %d sec",
0a7de745 225 cal_sleep_diff, ct_sleep_diff, ab_sleep_diff);
39037602
A
226
227 T_ASSERT_LE(abs(ct_sleep_diff - cal_sleep_diff), 2,
0a7de745 228 "continuous time should progress at ~ same rate as calendar");
39037602
A
229}
230
231T_DECL(mct_settimeofday, "Testing mach_continuous_time behavior over settimeofday"){
0a7de745 232 if (geteuid() != 0) {
39037602
A
233 T_SKIP("The settimeofday() test requires root privileges to run.");
234 }
235 mach_timebase_info(&tb_info);
236
237 struct timeval saved_tv;
238 struct timezone saved_tz;
239 int before, after;
240
241 T_ASSERT_POSIX_ZERO(gettimeofday(&saved_tv, &saved_tz), NULL);
242
243 struct timeval forward_tv = saved_tv;
244 // move time forward by two minutes, ensure mach_continuous_time keeps
245 // chugging along with mach_absolute_time
0a7de745 246 forward_tv.tv_sec += 2 * 60;
39037602
A
247
248 before = (int)to_ms(mach_continuous_time());
249 T_ASSERT_POSIX_ZERO(settimeofday(&forward_tv, &saved_tz), NULL);
250
251 after = (int)to_ms(mach_continuous_time());
252 T_ASSERT_POSIX_ZERO(settimeofday(&saved_tv, &saved_tz), NULL);
253
254 T_ASSERT_LT(abs(before - after), 1000, "mach_continuous_time should not jump more than 1s");
255}
256
5ba3f43e
A
257#ifdef HAS_KERNEL_TIME_TRAPS
258T_DECL(mct_settimeofday_kern, "Testing kernel mach_continuous_time behavior over settimeofday"){
0a7de745 259 if (geteuid() != 0) {
5ba3f43e
A
260 T_SKIP("The settimeofday() test requires root privileges to run.");
261 }
262 mach_timebase_info(&tb_info);
263
264 struct timeval saved_tv;
265 struct timezone saved_tz;
266 int before, after;
267
268 T_ASSERT_POSIX_ZERO(gettimeofday(&saved_tv, &saved_tz), NULL);
269
270 struct timeval forward_tv = saved_tv;
271 // move time forward by two minutes, ensure mach_continuous_time keeps
272 // chugging along with mach_absolute_time
0a7de745 273 forward_tv.tv_sec += 2 * 60;
5ba3f43e
A
274
275 before = (int)to_ms(mach_continuous_time_kernel());
276 T_ASSERT_POSIX_ZERO(settimeofday(&forward_tv, &saved_tz), NULL);
277
278 after = (int)to_ms(mach_continuous_time_kernel());
279 T_ASSERT_POSIX_ZERO(settimeofday(&saved_tv, &saved_tz), NULL);
280
281 T_ASSERT_LT(abs(before - after), 1000, "mach_continuous_time_kernel should not jump more than 1s");
282}
283#endif
284
39037602 285T_DECL(mct_aproximate, "Testing mach_continuous_approximate_time()",
cb323159 286 T_META_ALL_VALID_ARCHS(true), T_META_RUN_CONCURRENTLY(true))
39037602
A
287{
288 mach_timebase_info(&tb_info);
289
290 uint64_t absolute = to_ns(mach_continuous_time());
291 uint64_t approximate = to_ns(mach_continuous_approximate_time());
292
0a7de745 293 T_EXPECT_LE(llabs((long long)absolute - (long long)approximate), (long long)(25 * NSEC_PER_MSEC), NULL);
39037602 294}
5ba3f43e
A
295
296T_DECL(mach_time_perf, "mach_time performance") {
297 {
298 dt_stat_time_t s = dt_stat_time_create("mach_absolute_time");
299 T_STAT_MEASURE_LOOP(s) {
300 uint64_t t;
301 t = mach_absolute_time();
302 }
303 dt_stat_finalize(s);
304 }
305 {
306 dt_stat_time_t s = dt_stat_time_create("mach_continuous_time");
307 T_STAT_MEASURE_LOOP(s) {
308 uint64_t t;
309 t = mach_continuous_time();
310 }
311 dt_stat_finalize(s);
312 }
313}
314
315T_DECL(mach_time_perf_instructions, "instructions retired for mach_time", T_META_TYPE_PERF, T_META_ASROOT(YES)) {
316 {
317 dt_stat_thread_instructions_t s = dt_stat_thread_instructions_create("mach_absolute_time");
318 T_STAT_MEASURE_LOOP(s) {
319 uint64_t t;
320 t = mach_absolute_time();
321 }
322 dt_stat_finalize(s);
323 }
324 {
325 dt_stat_thread_instructions_t s = dt_stat_thread_instructions_create("mach_continuous_time");
326 T_STAT_MEASURE_LOOP(s) {
327 uint64_t t;
328 t = mach_continuous_time();
329 }
330 dt_stat_finalize(s);
331 }
332}
333
334#ifdef HAS_KERNEL_TIME_TRAPS
335T_DECL(mach_time_perf_kern, "kernel mach_time performance") {
336 {
337 dt_stat_time_t s = dt_stat_time_create("mach_absolute_time_kernel");
338 T_STAT_MEASURE_LOOP(s) {
339 uint64_t t;
340 t = mach_absolute_time_kernel();
341 }
342 dt_stat_finalize(s);
343 }
344 {
345 dt_stat_time_t s = dt_stat_time_create("mach_continuous_time_kernel");
346 T_STAT_MEASURE_LOOP(s) {
347 uint64_t t;
348 t = mach_continuous_time_kernel();
349 }
350 dt_stat_finalize(s);
351 }
352}
353
354T_DECL(mach_time_perf_instructions_kern, "instructions retired for kernel mach_time", T_META_TYPE_PERF, T_META_ASROOT(YES)) {
355 {
356 dt_stat_thread_instructions_t s = dt_stat_thread_instructions_create("mach_absolute_time_kernel");
357 T_STAT_MEASURE_LOOP(s) {
358 uint64_t t;
359 t = mach_absolute_time_kernel();
360 }
361 dt_stat_finalize(s);
362 }
363 {
364 dt_stat_thread_instructions_t s = dt_stat_thread_instructions_create("mach_continuous_time_kernel");
365 T_STAT_MEASURE_LOOP(s) {
366 uint64_t t;
367 t = mach_continuous_time_kernel();
368 }
369 dt_stat_finalize(s);
370 }
371}
372#endif