2 #include <mach/mach_time.h>
3 #include <mach/clock_types.h>
13 #include <darwintest.h>
15 #if (defined(__arm__) || defined(__arm64__))
16 #define HAS_KERNEL_TIME_TRAPS
18 extern uint64_t mach_absolute_time_kernel(void);
19 extern uint64_t mach_continuous_time_kernel(void);
23 extern char **environ
;
25 static const int64_t one_mil
= 1000 * 1000;
27 #define to_ns(ticks) (((ticks) * tb_info.numer) / (tb_info.denom))
28 #define to_ms(ticks) (to_ns(ticks)/one_mil)
30 static mach_timebase_info_data_t tb_info
;
33 update(uint64_t *a
, uint64_t *c
)
35 mach_get_times(a
, c
, NULL
);
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))
41 mach_timebase_info(&tb_info
);
42 #ifdef HAS_KERNEL_TIME_TRAPS
46 volatile uint64_t multiple_test
= to_ms(mach_continuous_time());
47 for (int i
= 0; i
< 20; i
++) {
49 const char *test_type
= "user";
50 #ifdef HAS_KERNEL_TIME_TRAPS
53 tmp
= mach_continuous_time_kernel();
55 tmp
= mach_continuous_time();
59 tmp
= mach_continuous_time();
62 T_ASSERT_GE(tmp
, multiple_test
, "mach_continuous_time (%s) must be monotonic", test_type
);
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
);
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))
74 mach_timebase_info(&tb_info
);
75 #ifdef HAS_KERNEL_TIME_TRAPS
79 volatile uint64_t multiple_test
= to_ms(mach_absolute_time());
80 for (int i
= 0; i
< 20; i
++) {
82 const char *test_type
= "user";
83 #ifdef HAS_KERNEL_TIME_TRAPS
86 tmp
= mach_absolute_time_kernel();
88 tmp
= mach_absolute_time();
92 tmp
= mach_absolute_time();
94 T_ASSERT_GE(tmp
, multiple_test
, "mach_absolute_time (%s) must be monotonic", test_type
);
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
);
103 T_DECL(mct_pause
, "Testing mach_continuous_time and mach_absolute_time don't diverge",
104 T_META_RUN_CONCURRENTLY(true))
106 mach_timebase_info(&tb_info
);
110 int before_diff
, after_diff
;
112 update(&abs_now
, &cnt_now
);
113 before_diff
= (int)(to_ms(cnt_now
) - to_ms(abs_now
));
117 update(&abs_now
, &cnt_now
);
118 after_diff
= (int)(to_ms(cnt_now
) - to_ms(abs_now
));
120 T_ASSERT_LE(abs(after_diff
- before_diff
), 1, "mach_continuous_time and mach_absolute_time should not diverge");
123 #ifdef HAS_KERNEL_TIME_TRAPS
125 update_kern(uint64_t *abs
, uint64_t *cont
)
127 uint64_t abs1
, abs2
, cont1
, cont2
;
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
));
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))
143 mach_timebase_info(&tb_info
);
147 int before_diff
, after_diff
;
149 update_kern(&abs_now
, &cnt_now
);
150 before_diff
= (int)(to_ms(cnt_now
) - to_ms(abs_now
));
154 update_kern(&abs_now
, &cnt_now
);
155 after_diff
= (int)(to_ms(cnt_now
) - to_ms(abs_now
));
157 T_ASSERT_LE(abs(after_diff
- before_diff
), 1, "mach_continuous_time_kernel and mach_absolute_time_kernel should not diverge");
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.");
166 mach_timebase_info(&tb_info
);
170 int before_diff
, after_diff
= 0;
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
));
177 // pmset relative wake 5
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
);
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
);
189 T_ASSERT_EQ(waitpid(pid
, &spawn_ret
, 0), pid
, "waitpid failed");
190 T_ASSERT_EQ(spawn_ret
, 0, "pmset relative wait 5 failed");
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
);
195 T_ASSERT_EQ(waitpid(pid
, &spawn_ret
, 0), pid
, "waitpid failed");
196 T_ASSERT_EQ(spawn_ret
, 0, "pmset relative wait 5 failed");
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
));
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) {
210 T_LOG("waited %d seconds for sleep...", i
+ 1);
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
);
218 time_t after_sleep
= time(NULL
);
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;
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
);
227 T_ASSERT_LE(abs(ct_sleep_diff
- cal_sleep_diff
), 2,
228 "continuous time should progress at ~ same rate as calendar");
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.");
235 mach_timebase_info(&tb_info
);
237 struct timeval saved_tv
;
238 struct timezone saved_tz
;
241 T_ASSERT_POSIX_ZERO(gettimeofday(&saved_tv
, &saved_tz
), NULL
);
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;
248 before
= (int)to_ms(mach_continuous_time());
249 T_ASSERT_POSIX_ZERO(settimeofday(&forward_tv
, &saved_tz
), NULL
);
251 after
= (int)to_ms(mach_continuous_time());
252 T_ASSERT_POSIX_ZERO(settimeofday(&saved_tv
, &saved_tz
), NULL
);
254 T_ASSERT_LT(abs(before
- after
), 1000, "mach_continuous_time should not jump more than 1s");
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.");
262 mach_timebase_info(&tb_info
);
264 struct timeval saved_tv
;
265 struct timezone saved_tz
;
268 T_ASSERT_POSIX_ZERO(gettimeofday(&saved_tv
, &saved_tz
), NULL
);
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;
275 before
= (int)to_ms(mach_continuous_time_kernel());
276 T_ASSERT_POSIX_ZERO(settimeofday(&forward_tv
, &saved_tz
), NULL
);
278 after
= (int)to_ms(mach_continuous_time_kernel());
279 T_ASSERT_POSIX_ZERO(settimeofday(&saved_tv
, &saved_tz
), NULL
);
281 T_ASSERT_LT(abs(before
- after
), 1000, "mach_continuous_time_kernel should not jump more than 1s");
285 T_DECL(mct_aproximate
, "Testing mach_continuous_approximate_time()",
286 T_META_ALL_VALID_ARCHS(true), T_META_RUN_CONCURRENTLY(true))
288 mach_timebase_info(&tb_info
);
290 uint64_t absolute
= to_ns(mach_continuous_time());
291 uint64_t approximate
= to_ns(mach_continuous_approximate_time());
293 T_EXPECT_LE(llabs((long long)absolute
- (long long)approximate
), (long long)(25 * NSEC_PER_MSEC
), NULL
);
296 T_DECL(mach_time_perf
, "mach_time performance") {
298 dt_stat_time_t s
= dt_stat_time_create("mach_absolute_time");
299 T_STAT_MEASURE_LOOP(s
) {
301 t
= mach_absolute_time();
306 dt_stat_time_t s
= dt_stat_time_create("mach_continuous_time");
307 T_STAT_MEASURE_LOOP(s
) {
309 t
= mach_continuous_time();
315 T_DECL(mach_time_perf_instructions
, "instructions retired for mach_time", T_META_TYPE_PERF
, T_META_ASROOT(YES
)) {
317 dt_stat_thread_instructions_t s
= dt_stat_thread_instructions_create("mach_absolute_time");
318 T_STAT_MEASURE_LOOP(s
) {
320 t
= mach_absolute_time();
325 dt_stat_thread_instructions_t s
= dt_stat_thread_instructions_create("mach_continuous_time");
326 T_STAT_MEASURE_LOOP(s
) {
328 t
= mach_continuous_time();
334 #ifdef HAS_KERNEL_TIME_TRAPS
335 T_DECL(mach_time_perf_kern
, "kernel mach_time performance") {
337 dt_stat_time_t s
= dt_stat_time_create("mach_absolute_time_kernel");
338 T_STAT_MEASURE_LOOP(s
) {
340 t
= mach_absolute_time_kernel();
345 dt_stat_time_t s
= dt_stat_time_create("mach_continuous_time_kernel");
346 T_STAT_MEASURE_LOOP(s
) {
348 t
= mach_continuous_time_kernel();
354 T_DECL(mach_time_perf_instructions_kern
, "instructions retired for kernel mach_time", T_META_TYPE_PERF
, T_META_ASROOT(YES
)) {
356 dt_stat_thread_instructions_t s
= dt_stat_thread_instructions_create("mach_absolute_time_kernel");
357 T_STAT_MEASURE_LOOP(s
) {
359 t
= mach_absolute_time_kernel();
364 dt_stat_thread_instructions_t s
= dt_stat_thread_instructions_create("mach_continuous_time_kernel");
365 T_STAT_MEASURE_LOOP(s
) {
367 t
= mach_continuous_time_kernel();