xref: /xnu-11215/tests/mach_continuous_time.c (revision 5c2921b0)
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(__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
update(uint64_t * a,uint64_t * c)33 update(uint64_t *a, uint64_t *c)
34 {
35 	mach_get_times(a, c, NULL);
36 }
37 
38 T_DECL(mct_monotonic, "Testing mach_continuous_time returns sane, monotonic values",
39     T_META_ALL_VALID_ARCHS(true), T_META_RUN_CONCURRENTLY(true))
40 {
41 	mach_timebase_info(&tb_info);
42 #ifdef HAS_KERNEL_TIME_TRAPS
43 	bool kernel = false;
44 #endif
45 
46 	volatile uint64_t multiple_test = to_ms(mach_continuous_time());
47 	for (int i = 0; i < 20; i++) {
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();
54 		} else {
55 			tmp = mach_continuous_time();
56 		}
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",
72     T_META_ALL_VALID_ARCHS(true), T_META_RUN_CONCURRENTLY(true))
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());
80 	for (int i = 0; i < 20; i++) {
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();
87 		} else {
88 			tmp = mach_absolute_time();
89 		}
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);
95 
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);
98 
99 		multiple_test = tmp;
100 	}
101 }
102 
103 T_DECL(mct_pause, "Testing mach_continuous_time and mach_absolute_time don't diverge",
104     T_META_RUN_CONCURRENTLY(true))
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 
123 #ifdef HAS_KERNEL_TIME_TRAPS
124 static void
update_kern(uint64_t * abs,uint64_t * cont)125 update_kern(uint64_t *abs, uint64_t *cont)
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
140 T_DECL(mct_pause_kern, "Testing kernel mach_continuous_time and mach_absolute_time don't diverge",
141     T_META_RUN_CONCURRENTLY(true))
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 
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)
199 	for (int i = 0; i < 30; i++) {
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
205 		if (abs(before_diff - after_diff) > 2) {
206 			break;
207 		}
208 
209 		sleep(1);
210 		T_LOG("waited %d seconds for sleep...", i + 1);
211 	}
212 
213 	if ((after_diff - before_diff) < 4000) {
214 		T_LOG("Device slept for less than 4 seconds, did it really sleep? (%d ms change between abs and cont)",
215 		    after_diff - before_diff);
216 	}
217 
218 	time_t after_sleep = time(NULL);
219 
220 	int cal_sleep_diff  = (int)(double)difftime(after_sleep, before_sleep);
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;
223 
224 	T_LOG("Calendar progressed: %d sec; continuous time progressed: %d sec; absolute time progressed %d sec",
225 	    cal_sleep_diff, ct_sleep_diff, ab_sleep_diff);
226 
227 	T_ASSERT_LE(abs(ct_sleep_diff - cal_sleep_diff), 2,
228 	    "continuous time should progress at ~ same rate as calendar");
229 }
230 
231 T_DECL(mct_settimeofday, "Testing mach_continuous_time behavior over settimeofday"){
232 	if (geteuid() != 0) {
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
246 	forward_tv.tv_sec += 2 * 60;
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 
257 #ifdef HAS_KERNEL_TIME_TRAPS
258 T_DECL(mct_settimeofday_kern, "Testing kernel mach_continuous_time behavior over settimeofday"){
259 	if (geteuid() != 0) {
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
273 	forward_tv.tv_sec += 2 * 60;
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 
285 T_DECL(mct_aproximate, "Testing mach_continuous_approximate_time()",
286     T_META_ALL_VALID_ARCHS(true), T_META_RUN_CONCURRENTLY(true))
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 
293 	T_EXPECT_LE(llabs((long long)absolute - (long long)approximate), (long long)(25 * NSEC_PER_MSEC), NULL);
294 }
295 
296 T_DECL(mach_time_perf, "mach_time performance") {
297 	{
298 		dt_stat_time_t s = dt_stat_time_create("mach_absolute_time");
T_STAT_MEASURE_LOOP(s)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");
T_STAT_MEASURE_LOOP(s)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");
T_STAT_MEASURE_LOOP(s)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");
T_STAT_MEASURE_LOOP(s)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");
T_STAT_MEASURE_LOOP(s)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");
T_STAT_MEASURE_LOOP(s)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");
T_STAT_MEASURE_LOOP(s)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");
T_STAT_MEASURE_LOOP(s)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
373