]> git.saurik.com Git - apple/xnu.git/blob - tests/mach_continuous_time.c
xnu-4903.241.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 mach_get_times(a,c,NULL);
35 }
36
37 T_DECL(mct_monotonic, "Testing mach_continuous_time returns sane, monotonic values",
38 T_META_ALL_VALID_ARCHS(true))
39 {
40 mach_timebase_info(&tb_info);
41 #ifdef HAS_KERNEL_TIME_TRAPS
42 bool kernel = false;
43 #endif
44
45 volatile uint64_t multiple_test = to_ms(mach_continuous_time());
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);
92
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);
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
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
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
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
279 T_DECL(mct_aproximate, "Testing mach_continuous_approximate_time()",
280 T_META_ALL_VALID_ARCHS(true))
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 }
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