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