]> git.saurik.com Git - apple/xnu.git/blob - tests/mach_continuous_time.c
xnu-6153.141.1.tar.gz
[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), T_META_RUN_CONCURRENTLY(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), T_META_RUN_CONCURRENTLY(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 T_META_RUN_CONCURRENTLY(true))
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
123 #ifdef HAS_KERNEL_TIME_TRAPS
124 static void
125 update_kern(uint64_t *abs, uint64_t *cont)
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
140 T_DECL(mct_pause_kern, "Testing kernel mach_continuous_time and mach_absolute_time don't diverge",
141 T_META_RUN_CONCURRENTLY(true))
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
161 T_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)
199 for (int i = 0; i < 30; i++) {
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
205 if (abs(before_diff - after_diff) > 2) {
206 break;
207 }
208
209 sleep(1);
210 T_LOG("waited %d seconds for sleep...", i + 1);
211 }
212
213 if ((after_diff - before_diff) < 4000) {
214 T_LOG("Device slept for less than 4 seconds, did it really sleep? (%d ms change between abs and cont)",
215 after_diff - before_diff);
216 }
217
218 time_t after_sleep = time(NULL);
219
220 int cal_sleep_diff = (int)(double)difftime(after_sleep, before_sleep);
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;
223
224 T_LOG("Calendar progressed: %d sec; continuous time progressed: %d sec; absolute time progressed %d sec",
225 cal_sleep_diff, ct_sleep_diff, ab_sleep_diff);
226
227 T_ASSERT_LE(abs(ct_sleep_diff - cal_sleep_diff), 2,
228 "continuous time should progress at ~ same rate as calendar");
229 }
230
231 T_DECL(mct_settimeofday, "Testing mach_continuous_time behavior over settimeofday"){
232 if (geteuid() != 0) {
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
246 forward_tv.tv_sec += 2 * 60;
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
257 #ifdef HAS_KERNEL_TIME_TRAPS
258 T_DECL(mct_settimeofday_kern, "Testing kernel mach_continuous_time behavior over settimeofday"){
259 if (geteuid() != 0) {
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
273 forward_tv.tv_sec += 2 * 60;
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
285 T_DECL(mct_aproximate, "Testing mach_continuous_approximate_time()",
286 T_META_ALL_VALID_ARCHS(true), T_META_RUN_CONCURRENTLY(true))
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
293 T_EXPECT_LE(llabs((long long)absolute - (long long)approximate), (long long)(25 * NSEC_PER_MSEC), NULL);
294 }
295
296 T_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
315 T_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
335 T_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
354 T_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