]>
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 | |
22 | ||
39037602 A |
23 | extern char **environ; |
24 | ||
25 | static const int64_t one_mil = 1000*1000; | |
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 | |
33 | update(uint64_t *a, uint64_t *c) { | |
34 | mach_get_times(a,c,NULL); | |
35 | } | |
36 | ||
37 | T_DECL(mct_monotonic, "Testing mach_continuous_time returns sane, monotonic values", | |
813fb2f6 | 38 | T_META_ALL_VALID_ARCHS(true)) |
39037602 A |
39 | { |
40 | mach_timebase_info(&tb_info); | |
5ba3f43e A |
41 | #ifdef HAS_KERNEL_TIME_TRAPS |
42 | bool kernel = false; | |
43 | #endif | |
39037602 A |
44 | |
45 | volatile uint64_t multiple_test = to_ms(mach_continuous_time()); | |
5ba3f43e A |
46 | for(int i = 0; i < 20; i++) { |
47 | uint64_t tmp; | |
48 | const char *test_type = "user"; | |
49 | #ifdef HAS_KERNEL_TIME_TRAPS | |
50 | if (kernel) { | |
51 | test_type = "kernel"; | |
52 | tmp = mach_continuous_time_kernel(); | |
53 | } else | |
54 | tmp = mach_continuous_time(); | |
55 | kernel = !kernel; | |
56 | #else | |
57 | tmp = mach_continuous_time(); | |
58 | #endif | |
59 | tmp = to_ms(tmp); | |
60 | T_ASSERT_GE(tmp, multiple_test, "mach_continuous_time (%s) must be monotonic", test_type); | |
61 | ||
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); | |
64 | ||
65 | multiple_test = tmp; | |
66 | } | |
67 | } | |
68 | ||
69 | T_DECL(mat_monotonic, "Testing mach_absolute_time returns sane, monotonic values", | |
70 | T_META_ALL_VALID_ARCHS(true)) | |
71 | { | |
72 | mach_timebase_info(&tb_info); | |
73 | #ifdef HAS_KERNEL_TIME_TRAPS | |
74 | bool kernel = false; | |
75 | #endif | |
76 | ||
77 | volatile uint64_t multiple_test = to_ms(mach_absolute_time()); | |
78 | for(int i = 0; i < 20; i++) { | |
79 | uint64_t tmp; | |
80 | const char *test_type = "user"; | |
81 | #ifdef HAS_KERNEL_TIME_TRAPS | |
82 | if (kernel) { | |
83 | test_type = "kernel"; | |
84 | tmp = mach_absolute_time_kernel(); | |
85 | } else | |
86 | tmp = mach_absolute_time(); | |
87 | kernel = !kernel; | |
88 | #endif | |
89 | tmp = mach_absolute_time(); | |
90 | tmp = to_ms(tmp); | |
91 | T_ASSERT_GE(tmp, multiple_test, "mach_absolute_time (%s) must be monotonic", test_type); | |
39037602 | 92 | |
5ba3f43e A |
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); | |
39037602 A |
95 | |
96 | multiple_test = tmp; | |
97 | } | |
98 | } | |
99 | ||
100 | T_DECL(mct_pause, "Testing mach_continuous_time and mach_absolute_time don't diverge") | |
101 | { | |
102 | mach_timebase_info(&tb_info); | |
103 | ||
104 | uint64_t abs_now; | |
105 | uint64_t cnt_now; | |
106 | int before_diff, after_diff; | |
107 | ||
108 | update(&abs_now, &cnt_now); | |
109 | before_diff = (int)(to_ms(cnt_now) - to_ms(abs_now)); | |
110 | ||
111 | sleep(1); | |
112 | ||
113 | update(&abs_now, &cnt_now); | |
114 | after_diff = (int)(to_ms(cnt_now) - to_ms(abs_now)); | |
115 | ||
116 | T_ASSERT_LE(abs(after_diff - before_diff), 1, "mach_continuous_time and mach_absolute_time should not diverge"); | |
117 | } | |
118 | ||
5ba3f43e A |
119 | #ifdef HAS_KERNEL_TIME_TRAPS |
120 | static void update_kern(uint64_t *abs, uint64_t *cont) | |
121 | { | |
122 | uint64_t abs1, abs2, cont1, cont2; | |
123 | do { | |
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)); | |
129 | *abs = abs2; | |
130 | *cont = cont2; | |
131 | } | |
132 | #endif | |
133 | ||
134 | #ifdef HAS_KERNEL_TIME_TRAPS | |
135 | T_DECL(mct_pause_kern, "Testing kernel mach_continuous_time and mach_absolute_time don't diverge") | |
136 | { | |
137 | mach_timebase_info(&tb_info); | |
138 | ||
139 | uint64_t abs_now; | |
140 | uint64_t cnt_now; | |
141 | int before_diff, after_diff; | |
142 | ||
143 | update_kern(&abs_now, &cnt_now); | |
144 | before_diff = (int)(to_ms(cnt_now) - to_ms(abs_now)); | |
145 | ||
146 | sleep(1); | |
147 | ||
148 | update_kern(&abs_now, &cnt_now); | |
149 | after_diff = (int)(to_ms(cnt_now) - to_ms(abs_now)); | |
150 | ||
151 | T_ASSERT_LE(abs(after_diff - before_diff), 1, "mach_continuous_time_kernel and mach_absolute_time_kernel should not diverge"); | |
152 | } | |
153 | #endif | |
154 | ||
39037602 A |
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."); | |
158 | #endif | |
159 | ||
160 | mach_timebase_info(&tb_info); | |
161 | ||
162 | uint64_t abs_now; | |
163 | uint64_t cnt_now; | |
164 | int before_diff, after_diff = 0; | |
165 | ||
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)); | |
169 | ||
170 | // performs: | |
171 | // pmset relative wake 5 | |
172 | // pmset sleepnow | |
173 | ||
174 | pid_t pid; | |
175 | int spawn_ret = 0; | |
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); | |
179 | ||
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); | |
182 | ||
183 | T_ASSERT_EQ(waitpid(pid, &spawn_ret, 0), pid, "waitpid failed"); | |
184 | T_ASSERT_EQ(spawn_ret, 0, "pmset relative wait 5 failed"); | |
185 | ||
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); | |
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 | // 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)); | |
196 | ||
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) { | |
200 | break; | |
201 | } | |
202 | ||
203 | sleep(1); | |
204 | T_LOG("waited %d seconds for sleep...", i+1); | |
205 | } | |
206 | ||
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); | |
210 | } | |
211 | ||
212 | time_t after_sleep = time(NULL); | |
213 | ||
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; | |
217 | ||
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); | |
220 | ||
221 | T_ASSERT_LE(abs(ct_sleep_diff - cal_sleep_diff), 2, | |
222 | "continuous time should progress at ~ same rate as calendar"); | |
223 | } | |
224 | ||
225 | T_DECL(mct_settimeofday, "Testing mach_continuous_time behavior over settimeofday"){ | |
226 | if (geteuid() != 0){ | |
227 | T_SKIP("The settimeofday() test requires root privileges to run."); | |
228 | } | |
229 | mach_timebase_info(&tb_info); | |
230 | ||
231 | struct timeval saved_tv; | |
232 | struct timezone saved_tz; | |
233 | int before, after; | |
234 | ||
235 | T_ASSERT_POSIX_ZERO(gettimeofday(&saved_tv, &saved_tz), NULL); | |
236 | ||
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; | |
241 | ||
242 | before = (int)to_ms(mach_continuous_time()); | |
243 | T_ASSERT_POSIX_ZERO(settimeofday(&forward_tv, &saved_tz), NULL); | |
244 | ||
245 | after = (int)to_ms(mach_continuous_time()); | |
246 | T_ASSERT_POSIX_ZERO(settimeofday(&saved_tv, &saved_tz), NULL); | |
247 | ||
248 | T_ASSERT_LT(abs(before - after), 1000, "mach_continuous_time should not jump more than 1s"); | |
249 | } | |
250 | ||
5ba3f43e A |
251 | #ifdef HAS_KERNEL_TIME_TRAPS |
252 | T_DECL(mct_settimeofday_kern, "Testing kernel mach_continuous_time behavior over settimeofday"){ | |
253 | if (geteuid() != 0){ | |
254 | T_SKIP("The settimeofday() test requires root privileges to run."); | |
255 | } | |
256 | mach_timebase_info(&tb_info); | |
257 | ||
258 | struct timeval saved_tv; | |
259 | struct timezone saved_tz; | |
260 | int before, after; | |
261 | ||
262 | T_ASSERT_POSIX_ZERO(gettimeofday(&saved_tv, &saved_tz), NULL); | |
263 | ||
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; | |
268 | ||
269 | before = (int)to_ms(mach_continuous_time_kernel()); | |
270 | T_ASSERT_POSIX_ZERO(settimeofday(&forward_tv, &saved_tz), NULL); | |
271 | ||
272 | after = (int)to_ms(mach_continuous_time_kernel()); | |
273 | T_ASSERT_POSIX_ZERO(settimeofday(&saved_tv, &saved_tz), NULL); | |
274 | ||
275 | T_ASSERT_LT(abs(before - after), 1000, "mach_continuous_time_kernel should not jump more than 1s"); | |
276 | } | |
277 | #endif | |
278 | ||
39037602 | 279 | T_DECL(mct_aproximate, "Testing mach_continuous_approximate_time()", |
813fb2f6 | 280 | T_META_ALL_VALID_ARCHS(true)) |
39037602 A |
281 | { |
282 | mach_timebase_info(&tb_info); | |
283 | ||
284 | uint64_t absolute = to_ns(mach_continuous_time()); | |
285 | uint64_t approximate = to_ns(mach_continuous_approximate_time()); | |
286 | ||
287 | T_EXPECT_LE(llabs((long long)absolute - (long long)approximate), (long long)(25*NSEC_PER_MSEC), NULL); | |
288 | } | |
5ba3f43e A |
289 | |
290 | T_DECL(mach_time_perf, "mach_time performance") { | |
291 | { | |
292 | dt_stat_time_t s = dt_stat_time_create("mach_absolute_time"); | |
293 | T_STAT_MEASURE_LOOP(s) { | |
294 | uint64_t t; | |
295 | t = mach_absolute_time(); | |
296 | } | |
297 | dt_stat_finalize(s); | |
298 | } | |
299 | { | |
300 | dt_stat_time_t s = dt_stat_time_create("mach_continuous_time"); | |
301 | T_STAT_MEASURE_LOOP(s) { | |
302 | uint64_t t; | |
303 | t = mach_continuous_time(); | |
304 | } | |
305 | dt_stat_finalize(s); | |
306 | } | |
307 | } | |
308 | ||
309 | T_DECL(mach_time_perf_instructions, "instructions retired for mach_time", T_META_TYPE_PERF, T_META_ASROOT(YES)) { | |
310 | { | |
311 | dt_stat_thread_instructions_t s = dt_stat_thread_instructions_create("mach_absolute_time"); | |
312 | T_STAT_MEASURE_LOOP(s) { | |
313 | uint64_t t; | |
314 | t = mach_absolute_time(); | |
315 | } | |
316 | dt_stat_finalize(s); | |
317 | } | |
318 | { | |
319 | dt_stat_thread_instructions_t s = dt_stat_thread_instructions_create("mach_continuous_time"); | |
320 | T_STAT_MEASURE_LOOP(s) { | |
321 | uint64_t t; | |
322 | t = mach_continuous_time(); | |
323 | } | |
324 | dt_stat_finalize(s); | |
325 | } | |
326 | } | |
327 | ||
328 | #ifdef HAS_KERNEL_TIME_TRAPS | |
329 | T_DECL(mach_time_perf_kern, "kernel mach_time performance") { | |
330 | { | |
331 | dt_stat_time_t s = dt_stat_time_create("mach_absolute_time_kernel"); | |
332 | T_STAT_MEASURE_LOOP(s) { | |
333 | uint64_t t; | |
334 | t = mach_absolute_time_kernel(); | |
335 | } | |
336 | dt_stat_finalize(s); | |
337 | } | |
338 | { | |
339 | dt_stat_time_t s = dt_stat_time_create("mach_continuous_time_kernel"); | |
340 | T_STAT_MEASURE_LOOP(s) { | |
341 | uint64_t t; | |
342 | t = mach_continuous_time_kernel(); | |
343 | } | |
344 | dt_stat_finalize(s); | |
345 | } | |
346 | } | |
347 | ||
348 | T_DECL(mach_time_perf_instructions_kern, "instructions retired for kernel mach_time", T_META_TYPE_PERF, T_META_ASROOT(YES)) { | |
349 | { | |
350 | dt_stat_thread_instructions_t s = dt_stat_thread_instructions_create("mach_absolute_time_kernel"); | |
351 | T_STAT_MEASURE_LOOP(s) { | |
352 | uint64_t t; | |
353 | t = mach_absolute_time_kernel(); | |
354 | } | |
355 | dt_stat_finalize(s); | |
356 | } | |
357 | { | |
358 | dt_stat_thread_instructions_t s = dt_stat_thread_instructions_create("mach_continuous_time_kernel"); | |
359 | T_STAT_MEASURE_LOOP(s) { | |
360 | uint64_t t; | |
361 | t = mach_continuous_time_kernel(); | |
362 | } | |
363 | dt_stat_finalize(s); | |
364 | } | |
365 | } | |
366 | #endif | |
367 |