]>
Commit | Line | Data |
---|---|---|
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 | ||
18 | extern uint64_t mach_absolute_time_kernel(void); | |
19 | extern uint64_t mach_continuous_time_kernel(void); | |
20 | ||
21 | #endif | |
0a7de745 | 22 | |
39037602 A |
23 | extern char **environ; |
24 | ||
0a7de745 | 25 | static 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 | ||
30 | static mach_timebase_info_data_t tb_info; | |
31 | ||
32 | static void | |
0a7de745 A |
33 | update(uint64_t *a, uint64_t *c) |
34 | { | |
35 | mach_get_times(a, c, NULL); | |
39037602 A |
36 | } |
37 | ||
38 | T_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 | ||
71 | T_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 |
103 | T_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 |
124 | static void |
125 | update_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 |
140 | T_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 |
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) | |
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 | ||
231 | T_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 |
258 | T_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 | 285 | T_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 | |
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 |