]> git.saurik.com Git - apple/xnu.git/blame - tools/tests/darwintests/mach_continuous_time.c
xnu-4570.41.2.tar.gz
[apple/xnu.git] / tools / tests / darwintests / mach_continuous_time.c
CommitLineData
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
18extern uint64_t mach_absolute_time_kernel(void);
19extern uint64_t mach_continuous_time_kernel(void);
20
21#endif
22
39037602
A
23extern char **environ;
24
25static 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
30static mach_timebase_info_data_t tb_info;
31
32static void
33update(uint64_t *a, uint64_t *c) {
34 mach_get_times(a,c,NULL);
35}
36
37T_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
69T_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
100T_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
120static 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
135T_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
155T_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
225T_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
252T_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 279T_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
290T_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
309T_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
329T_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
348T_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