]>
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 | ||
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 | } |