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
) {
34 mach_get_times(a
,c
,NULL
);
37 T_DECL(mct_monotonic
, "Testing mach_continuous_time returns sane, monotonic values",
38 T_META_ALL_VALID_ARCHS(true))
40 mach_timebase_info(&tb_info
);
41 #ifdef HAS_KERNEL_TIME_TRAPS
45 volatile uint64_t multiple_test
= to_ms(mach_continuous_time());
46 for(int i
= 0; i
< 20; i
++) {
48 const char *test_type
= "user";
49 #ifdef HAS_KERNEL_TIME_TRAPS
52 tmp
= mach_continuous_time_kernel();
54 tmp
= mach_continuous_time();
57 tmp
= mach_continuous_time();
60 T_ASSERT_GE(tmp
, multiple_test
, "mach_continuous_time (%s) must be monotonic", test_type
);
62 // each successive call shouldn't be more than 100ms in the future
63 T_ASSERT_LE(tmp
- multiple_test
, 100ULL, "mach_continuous_time (%s) should not jump forward too fast", test_type
);
69 T_DECL(mat_monotonic
, "Testing mach_absolute_time returns sane, monotonic values",
70 T_META_ALL_VALID_ARCHS(true))
72 mach_timebase_info(&tb_info
);
73 #ifdef HAS_KERNEL_TIME_TRAPS
77 volatile uint64_t multiple_test
= to_ms(mach_absolute_time());
78 for(int i
= 0; i
< 20; i
++) {
80 const char *test_type
= "user";
81 #ifdef HAS_KERNEL_TIME_TRAPS
84 tmp
= mach_absolute_time_kernel();
86 tmp
= mach_absolute_time();
89 tmp
= mach_absolute_time();
91 T_ASSERT_GE(tmp
, multiple_test
, "mach_absolute_time (%s) must be monotonic", test_type
);
93 // each successive call shouldn't be more than 100ms in the future
94 T_ASSERT_LE(tmp
- multiple_test
, 100ULL, "mach_absolute_time (%s) should not jump forward too fast", test_type
);
100 T_DECL(mct_pause
, "Testing mach_continuous_time and mach_absolute_time don't diverge")
102 mach_timebase_info(&tb_info
);
106 int before_diff
, after_diff
;
108 update(&abs_now
, &cnt_now
);
109 before_diff
= (int)(to_ms(cnt_now
) - to_ms(abs_now
));
113 update(&abs_now
, &cnt_now
);
114 after_diff
= (int)(to_ms(cnt_now
) - to_ms(abs_now
));
116 T_ASSERT_LE(abs(after_diff
- before_diff
), 1, "mach_continuous_time and mach_absolute_time should not diverge");
119 #ifdef HAS_KERNEL_TIME_TRAPS
120 static void update_kern(uint64_t *abs
, uint64_t *cont
)
122 uint64_t abs1
, abs2
, cont1
, cont2
;
124 abs1
= mach_absolute_time_kernel();
125 cont1
= mach_continuous_time_kernel();
126 abs2
= mach_absolute_time_kernel();
127 cont2
= mach_continuous_time_kernel();
128 } while (to_ms(abs2
- abs1
) || to_ms(cont2
- cont1
));
134 #ifdef HAS_KERNEL_TIME_TRAPS
135 T_DECL(mct_pause_kern
, "Testing kernel mach_continuous_time and mach_absolute_time don't diverge")
137 mach_timebase_info(&tb_info
);
141 int before_diff
, after_diff
;
143 update_kern(&abs_now
, &cnt_now
);
144 before_diff
= (int)(to_ms(cnt_now
) - to_ms(abs_now
));
148 update_kern(&abs_now
, &cnt_now
);
149 after_diff
= (int)(to_ms(cnt_now
) - to_ms(abs_now
));
151 T_ASSERT_LE(abs(after_diff
- before_diff
), 1, "mach_continuous_time_kernel and mach_absolute_time_kernel should not diverge");
155 T_DECL(mct_sleep
, "Testing mach_continuous_time behavior over system sleep"){
156 #ifndef MCT_SLEEP_TEST
157 T_SKIP("Skipping test that sleeps the device; compile with MCT_SLEEP_TEST define to enable.");
160 mach_timebase_info(&tb_info
);
164 int before_diff
, after_diff
= 0;
166 T_LOG("Testing mach_continuous_time is ~5 seconds ahead of mach_absolute_time after 5 second sleep");
167 update(&abs_now
, &cnt_now
);
168 before_diff
= (int)(to_ms(cnt_now
) - to_ms(abs_now
));
171 // pmset relative wake 5
176 time_t before_sleep
= time(NULL
);
177 int ct_ms_before_sleep
= (int)to_ms(cnt_now
);
178 int ab_ms_before_sleep
= (int)to_ms(abs_now
);
180 char *const pmset1_args
[] = {"/usr/bin/pmset", "relative", "wake", "5", NULL
};
181 T_ASSERT_POSIX_ZERO((spawn_ret
= posix_spawn(&pid
, pmset1_args
[0], NULL
, NULL
, pmset1_args
, environ
)), NULL
);
183 T_ASSERT_EQ(waitpid(pid
, &spawn_ret
, 0), pid
, "waitpid failed");
184 T_ASSERT_EQ(spawn_ret
, 0, "pmset relative wait 5 failed");
186 char *const pmset2_args
[] = {"/usr/bin/pmset", "sleepnow", NULL
};
187 T_ASSERT_POSIX_ZERO((spawn_ret
= posix_spawn(&pid
, pmset2_args
[0], NULL
, NULL
, pmset2_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 // wait for device to sleep (up to 30 seconds)
193 for(int i
= 0; i
< 30; i
++) {
194 update(&abs_now
, &cnt_now
);
195 after_diff
= (int)(to_ms(cnt_now
) - to_ms(abs_now
));
197 // on OSX, there's enough latency between calls to MCT and MAT
198 // when the system is going down for sleep for values to diverge a few ms
199 if(abs(before_diff
- after_diff
) > 2) {
204 T_LOG("waited %d seconds for sleep...", i
+1);
207 if((after_diff
- before_diff
) < 4000) {
208 T_LOG("Device slept for less than 4 seconds, did it really sleep? (%d ms change between abs and cont)",
209 after_diff
- before_diff
);
212 time_t after_sleep
= time(NULL
);
214 int cal_sleep_diff
= (int)(double)difftime(after_sleep
, before_sleep
);
215 int ct_sleep_diff
= ((int)to_ms(cnt_now
) - ct_ms_before_sleep
)/1000;
216 int ab_sleep_diff
= ((int)to_ms(abs_now
) - ab_ms_before_sleep
)/1000;
218 T_LOG("Calendar progressed: %d sec; continuous time progressed: %d sec; absolute time progressed %d sec",
219 cal_sleep_diff
, ct_sleep_diff
, ab_sleep_diff
);
221 T_ASSERT_LE(abs(ct_sleep_diff
- cal_sleep_diff
), 2,
222 "continuous time should progress at ~ same rate as calendar");
225 T_DECL(mct_settimeofday
, "Testing mach_continuous_time behavior over settimeofday"){
227 T_SKIP("The settimeofday() test requires root privileges to run.");
229 mach_timebase_info(&tb_info
);
231 struct timeval saved_tv
;
232 struct timezone saved_tz
;
235 T_ASSERT_POSIX_ZERO(gettimeofday(&saved_tv
, &saved_tz
), NULL
);
237 struct timeval forward_tv
= saved_tv
;
238 // move time forward by two minutes, ensure mach_continuous_time keeps
239 // chugging along with mach_absolute_time
240 forward_tv
.tv_sec
+= 2*60;
242 before
= (int)to_ms(mach_continuous_time());
243 T_ASSERT_POSIX_ZERO(settimeofday(&forward_tv
, &saved_tz
), NULL
);
245 after
= (int)to_ms(mach_continuous_time());
246 T_ASSERT_POSIX_ZERO(settimeofday(&saved_tv
, &saved_tz
), NULL
);
248 T_ASSERT_LT(abs(before
- after
), 1000, "mach_continuous_time should not jump more than 1s");
251 #ifdef HAS_KERNEL_TIME_TRAPS
252 T_DECL(mct_settimeofday_kern
, "Testing kernel mach_continuous_time behavior over settimeofday"){
254 T_SKIP("The settimeofday() test requires root privileges to run.");
256 mach_timebase_info(&tb_info
);
258 struct timeval saved_tv
;
259 struct timezone saved_tz
;
262 T_ASSERT_POSIX_ZERO(gettimeofday(&saved_tv
, &saved_tz
), NULL
);
264 struct timeval forward_tv
= saved_tv
;
265 // move time forward by two minutes, ensure mach_continuous_time keeps
266 // chugging along with mach_absolute_time
267 forward_tv
.tv_sec
+= 2*60;
269 before
= (int)to_ms(mach_continuous_time_kernel());
270 T_ASSERT_POSIX_ZERO(settimeofday(&forward_tv
, &saved_tz
), NULL
);
272 after
= (int)to_ms(mach_continuous_time_kernel());
273 T_ASSERT_POSIX_ZERO(settimeofday(&saved_tv
, &saved_tz
), NULL
);
275 T_ASSERT_LT(abs(before
- after
), 1000, "mach_continuous_time_kernel should not jump more than 1s");
279 T_DECL(mct_aproximate
, "Testing mach_continuous_approximate_time()",
280 T_META_ALL_VALID_ARCHS(true))
282 mach_timebase_info(&tb_info
);
284 uint64_t absolute
= to_ns(mach_continuous_time());
285 uint64_t approximate
= to_ns(mach_continuous_approximate_time());
287 T_EXPECT_LE(llabs((long long)absolute
- (long long)approximate
), (long long)(25*NSEC_PER_MSEC
), NULL
);
290 T_DECL(mach_time_perf
, "mach_time performance") {
292 dt_stat_time_t s
= dt_stat_time_create("mach_absolute_time");
293 T_STAT_MEASURE_LOOP(s
) {
295 t
= mach_absolute_time();
300 dt_stat_time_t s
= dt_stat_time_create("mach_continuous_time");
301 T_STAT_MEASURE_LOOP(s
) {
303 t
= mach_continuous_time();
309 T_DECL(mach_time_perf_instructions
, "instructions retired for mach_time", T_META_TYPE_PERF
, T_META_ASROOT(YES
)) {
311 dt_stat_thread_instructions_t s
= dt_stat_thread_instructions_create("mach_absolute_time");
312 T_STAT_MEASURE_LOOP(s
) {
314 t
= mach_absolute_time();
319 dt_stat_thread_instructions_t s
= dt_stat_thread_instructions_create("mach_continuous_time");
320 T_STAT_MEASURE_LOOP(s
) {
322 t
= mach_continuous_time();
328 #ifdef HAS_KERNEL_TIME_TRAPS
329 T_DECL(mach_time_perf_kern
, "kernel mach_time performance") {
331 dt_stat_time_t s
= dt_stat_time_create("mach_absolute_time_kernel");
332 T_STAT_MEASURE_LOOP(s
) {
334 t
= mach_absolute_time_kernel();
339 dt_stat_time_t s
= dt_stat_time_create("mach_continuous_time_kernel");
340 T_STAT_MEASURE_LOOP(s
) {
342 t
= mach_continuous_time_kernel();
348 T_DECL(mach_time_perf_instructions_kern
, "instructions retired for kernel mach_time", T_META_TYPE_PERF
, T_META_ASROOT(YES
)) {
350 dt_stat_thread_instructions_t s
= dt_stat_thread_instructions_create("mach_absolute_time_kernel");
351 T_STAT_MEASURE_LOOP(s
) {
353 t
= mach_absolute_time_kernel();
358 dt_stat_thread_instructions_t s
= dt_stat_thread_instructions_create("mach_continuous_time_kernel");
359 T_STAT_MEASURE_LOOP(s
) {
361 t
= mach_continuous_time_kernel();