]> git.saurik.com Git - apple/xnu.git/blob - tools/tests/darwintests/mach_continuous_time.c
xnu-3789.1.32.tar.gz
[apple/xnu.git] / tools / tests / darwintests / 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 extern char **environ;
16
17 static const int64_t one_mil = 1000*1000;
18
19 #define to_ns(ticks) ((ticks * tb_info.numer) / (tb_info.denom))
20 #define to_ms(ticks) (to_ns(ticks)/one_mil)
21
22 static mach_timebase_info_data_t tb_info;
23
24 static void
25 update(uint64_t *a, uint64_t *c) {
26 mach_get_times(a,c,NULL);
27 }
28
29 T_DECL(mct_monotonic, "Testing mach_continuous_time returns sane, monotonic values",
30 T_META_ALL_VALID_ARCHS(YES))
31 {
32 mach_timebase_info(&tb_info);
33
34 volatile uint64_t multiple_test = to_ms(mach_continuous_time());
35 for(int i = 0; i < 10; i++) {
36 uint64_t tmp = to_ms(mach_continuous_time());
37 T_ASSERT_GE(tmp, multiple_test, "mach_continuous_time must be monotonic");
38
39 // each successive call shouldn't be more than 50ms in the future
40 T_ASSERT_LE(tmp - multiple_test, 50ULL, "mach_continuous_time should not jump forward too fast");
41
42 multiple_test = tmp;
43 }
44 }
45
46 T_DECL(mct_pause, "Testing mach_continuous_time and mach_absolute_time don't diverge")
47 {
48 mach_timebase_info(&tb_info);
49
50 uint64_t abs_now;
51 uint64_t cnt_now;
52 int before_diff, after_diff;
53
54 update(&abs_now, &cnt_now);
55 before_diff = (int)(to_ms(cnt_now) - to_ms(abs_now));
56
57 sleep(1);
58
59 update(&abs_now, &cnt_now);
60 after_diff = (int)(to_ms(cnt_now) - to_ms(abs_now));
61
62 T_ASSERT_LE(abs(after_diff - before_diff), 1, "mach_continuous_time and mach_absolute_time should not diverge");
63 }
64
65 T_DECL(mct_sleep, "Testing mach_continuous_time behavior over system sleep"){
66 #ifndef MCT_SLEEP_TEST
67 T_SKIP("Skipping test that sleeps the device; compile with MCT_SLEEP_TEST define to enable.");
68 #endif
69
70 mach_timebase_info(&tb_info);
71
72 uint64_t abs_now;
73 uint64_t cnt_now;
74 int before_diff, after_diff = 0;
75
76 T_LOG("Testing mach_continuous_time is ~5 seconds ahead of mach_absolute_time after 5 second sleep");
77 update(&abs_now, &cnt_now);
78 before_diff = (int)(to_ms(cnt_now) - to_ms(abs_now));
79
80 // performs:
81 // pmset relative wake 5
82 // pmset sleepnow
83
84 pid_t pid;
85 int spawn_ret = 0;
86 time_t before_sleep = time(NULL);
87 int ct_ms_before_sleep = (int)to_ms(cnt_now);
88 int ab_ms_before_sleep = (int)to_ms(abs_now);
89
90 char *const pmset1_args[] = {"/usr/bin/pmset", "relative", "wake", "5", NULL};
91 T_ASSERT_POSIX_ZERO((spawn_ret = posix_spawn(&pid, pmset1_args[0], NULL, NULL, pmset1_args, environ)), NULL);
92
93 T_ASSERT_EQ(waitpid(pid, &spawn_ret, 0), pid, "waitpid failed");
94 T_ASSERT_EQ(spawn_ret, 0, "pmset relative wait 5 failed");
95
96 char *const pmset2_args[] = {"/usr/bin/pmset", "sleepnow", NULL};
97 T_ASSERT_POSIX_ZERO((spawn_ret = posix_spawn(&pid, pmset2_args[0], NULL, NULL, pmset2_args, environ)), NULL);
98
99 T_ASSERT_EQ(waitpid(pid, &spawn_ret, 0), pid, "waitpid failed");
100 T_ASSERT_EQ(spawn_ret, 0, "pmset relative wait 5 failed");
101
102 // wait for device to sleep (up to 30 seconds)
103 for(int i = 0; i < 30; i++) {
104 update(&abs_now, &cnt_now);
105 after_diff = (int)(to_ms(cnt_now) - to_ms(abs_now));
106
107 // on OSX, there's enough latency between calls to MCT and MAT
108 // when the system is going down for sleep for values to diverge a few ms
109 if(abs(before_diff - after_diff) > 2) {
110 break;
111 }
112
113 sleep(1);
114 T_LOG("waited %d seconds for sleep...", i+1);
115 }
116
117 if((after_diff - before_diff) < 4000) {
118 T_LOG("Device slept for less than 4 seconds, did it really sleep? (%d ms change between abs and cont)",
119 after_diff - before_diff);
120 }
121
122 time_t after_sleep = time(NULL);
123
124 int cal_sleep_diff = (int)(double)difftime(after_sleep, before_sleep);
125 int ct_sleep_diff = ((int)to_ms(cnt_now) - ct_ms_before_sleep)/1000;
126 int ab_sleep_diff = ((int)to_ms(abs_now) - ab_ms_before_sleep)/1000;
127
128 T_LOG("Calendar progressed: %d sec; continuous time progressed: %d sec; absolute time progressed %d sec",
129 cal_sleep_diff, ct_sleep_diff, ab_sleep_diff);
130
131 T_ASSERT_LE(abs(ct_sleep_diff - cal_sleep_diff), 2,
132 "continuous time should progress at ~ same rate as calendar");
133 }
134
135 T_DECL(mct_settimeofday, "Testing mach_continuous_time behavior over settimeofday"){
136 if (geteuid() != 0){
137 T_SKIP("The settimeofday() test requires root privileges to run.");
138 }
139 mach_timebase_info(&tb_info);
140
141 struct timeval saved_tv;
142 struct timezone saved_tz;
143 int before, after;
144
145 T_ASSERT_POSIX_ZERO(gettimeofday(&saved_tv, &saved_tz), NULL);
146
147 struct timeval forward_tv = saved_tv;
148 // move time forward by two minutes, ensure mach_continuous_time keeps
149 // chugging along with mach_absolute_time
150 forward_tv.tv_sec += 2*60;
151
152 before = (int)to_ms(mach_continuous_time());
153 T_ASSERT_POSIX_ZERO(settimeofday(&forward_tv, &saved_tz), NULL);
154
155 after = (int)to_ms(mach_continuous_time());
156 T_ASSERT_POSIX_ZERO(settimeofday(&saved_tv, &saved_tz), NULL);
157
158 T_ASSERT_LT(abs(before - after), 1000, "mach_continuous_time should not jump more than 1s");
159 }
160
161 T_DECL(mct_aproximate, "Testing mach_continuous_approximate_time()",
162 T_META_ALL_VALID_ARCHS(YES))
163 {
164 mach_timebase_info(&tb_info);
165
166 uint64_t absolute = to_ns(mach_continuous_time());
167 uint64_t approximate = to_ns(mach_continuous_approximate_time());
168
169 T_EXPECT_LE(llabs((long long)absolute - (long long)approximate), (long long)(25*NSEC_PER_MSEC), NULL);
170 }