1 /*
2 * Copyright (c) 2018 Intel Corporation
3 * Copyright (c) 2023, Meta
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7 #include <sys/time.h>
8 #include <time.h>
9 #include <unistd.h>
10
11 #include <zephyr/ztest.h>
12 #include <zephyr/logging/log.h>
13
14 #define SLEEP_SECONDS 1
15 #define CLOCK_INVALID -1
16
17 LOG_MODULE_REGISTER(clock_test, LOG_LEVEL_DBG);
18
19 /* Set a particular time. In this case, the output of: `date +%s -d 2018-01-01T15:45:01Z` */
20 static const struct timespec ref_ts = {1514821501, NSEC_PER_SEC / 2U};
21
22 static const clockid_t clocks[] = {
23 CLOCK_MONOTONIC,
24 CLOCK_REALTIME,
25 };
26
27 static const bool settable[] = {
28 false,
29 true,
30 };
31
ts_to_ns(const struct timespec * ts)32 static inline int64_t ts_to_ns(const struct timespec *ts)
33 {
34 return ts->tv_sec * NSEC_PER_SEC + ts->tv_nsec;
35 }
36
tv_to_ts(const struct timeval * tv,struct timespec * ts)37 static inline void tv_to_ts(const struct timeval *tv, struct timespec *ts)
38 {
39 ts->tv_sec = tv->tv_sec;
40 ts->tv_nsec = tv->tv_usec * NSEC_PER_USEC;
41 }
42
43 #define _tp_op(_a, _b, _op) (ts_to_ns(_a) _op ts_to_ns(_b))
44
45 #define _decl_op(_type, _name, _op) \
46 static inline _type _name(const struct timespec *_a, const struct timespec *_b) \
47 { \
48 return _tp_op(_a, _b, _op); \
49 }
50
51 _decl_op(bool, tp_eq, ==); /* a == b */
52 _decl_op(bool, tp_lt, <); /* a < b */
53 _decl_op(bool, tp_gt, >); /* a > b */
54 _decl_op(bool, tp_le, <=); /* a <= b */
55 _decl_op(bool, tp_ge, >=); /* a >= b */
56 _decl_op(int64_t, tp_diff, -); /* a - b */
57
58 /* lo <= (a - b) < hi */
tp_diff_in_range_ns(const struct timespec * a,const struct timespec * b,int64_t lo,int64_t hi)59 static inline bool tp_diff_in_range_ns(const struct timespec *a, const struct timespec *b,
60 int64_t lo, int64_t hi)
61 {
62 int64_t diff = tp_diff(a, b);
63
64 return diff >= lo && diff < hi;
65 }
66
ZTEST(posix_timers,test_clock_gettime)67 ZTEST(posix_timers, test_clock_gettime)
68 {
69 struct timespec ts;
70
71 /* ensure argument validation is performed */
72 errno = 0;
73 zassert_equal(clock_gettime(CLOCK_INVALID, &ts), -1);
74 zassert_equal(errno, EINVAL);
75
76 if (false) {
77 /* undefined behaviour */
78 errno = 0;
79 zassert_equal(clock_gettime(clocks[0], NULL), -1);
80 zassert_equal(errno, EINVAL);
81 }
82
83 /* verify that we can call clock_gettime() on supported clocks */
84 ARRAY_FOR_EACH(clocks, i) {
85 ts = (struct timespec){-1, -1};
86 zassert_ok(clock_gettime(clocks[i], &ts));
87 zassert_not_equal(ts.tv_sec, -1);
88 zassert_not_equal(ts.tv_nsec, -1);
89 }
90 }
91
ZTEST(posix_timers,test_clock_settime)92 ZTEST(posix_timers, test_clock_settime)
93 {
94 int64_t diff_ns;
95 struct timespec ts = {0};
96
97 BUILD_ASSERT(ARRAY_SIZE(settable) == ARRAY_SIZE(clocks));
98
99 /* ensure argument validation is performed */
100 errno = 0;
101 zassert_equal(clock_settime(CLOCK_INVALID, &ts), -1);
102 zassert_equal(errno, EINVAL);
103
104 if (false) {
105 /* undefined behaviour */
106 errno = 0;
107 zassert_equal(clock_settime(CLOCK_REALTIME, NULL), -1);
108 zassert_equal(errno, EINVAL);
109 }
110
111 /* verify nanoseconds */
112 errno = 0;
113 ts = (struct timespec){0, NSEC_PER_SEC};
114 zassert_equal(clock_settime(CLOCK_REALTIME, &ts), -1);
115 zassert_equal(errno, EINVAL);
116 errno = 0;
117 ts = (struct timespec){0, -1};
118 zassert_equal(clock_settime(CLOCK_REALTIME, &ts), -1);
119 zassert_equal(errno, EINVAL);
120
121 ARRAY_FOR_EACH(clocks, i) {
122 if (!settable[i]) {
123 /* should fail attempting to set unsettable clocks */
124 errno = 0;
125 zassert_equal(clock_settime(clocks[i], &ts), -1);
126 zassert_equal(errno, EINVAL);
127 continue;
128 }
129
130 zassert_ok(clock_settime(clocks[i], &ref_ts));
131
132 /* read-back the time */
133 zassert_ok(clock_gettime(clocks[i], &ts));
134 /* dt should be >= 0, but definitely <= 1s */
135 diff_ns = tp_diff(&ts, &ref_ts);
136 zassert_true(diff_ns >= 0 && diff_ns <= NSEC_PER_SEC);
137 }
138 }
139
ZTEST(posix_timers,test_realtime)140 ZTEST(posix_timers, test_realtime)
141 {
142 struct timespec then, now;
143 /*
144 * For calculating cumulative moving average
145 * Note: we do not want to assert any individual samples due to scheduler noise.
146 * The CMA filters out the noise so we can make an assertion (on average).
147 * https://en.wikipedia.org/wiki/Moving_average#Cumulative_moving_average
148 */
149 int64_t cma_prev = 0;
150 int64_t cma;
151 int64_t x_i;
152 /* lower and uppoer boundary for assertion */
153 int64_t lo = CONFIG_TEST_CLOCK_RT_SLEEP_MS;
154 int64_t hi = CONFIG_TEST_CLOCK_RT_SLEEP_MS + CONFIG_TEST_CLOCK_RT_ERROR_MS;
155 /* lower and upper watermark */
156 int64_t lo_wm = INT64_MAX;
157 int64_t hi_wm = INT64_MIN;
158
159 /* Loop n times, sleeping a little bit for each */
160 (void)clock_gettime(CLOCK_REALTIME, &then);
161 for (int i = 0; i < CONFIG_TEST_CLOCK_RT_ITERATIONS; ++i) {
162
163 zassert_ok(k_usleep(USEC_PER_MSEC * CONFIG_TEST_CLOCK_RT_SLEEP_MS));
164 (void)clock_gettime(CLOCK_REALTIME, &now);
165
166 /* Make the delta milliseconds. */
167 x_i = tp_diff(&now, &then) / NSEC_PER_MSEC;
168 then = now;
169
170 if (x_i < lo_wm) {
171 /* update low watermark */
172 lo_wm = x_i;
173 }
174
175 if (x_i > hi_wm) {
176 /* update high watermark */
177 hi_wm = x_i;
178 }
179
180 /* compute cumulative running average */
181 cma = (x_i + i * cma_prev) / (i + 1);
182 cma_prev = cma;
183 }
184
185 LOG_INF("n: %d, sleep: %d, margin: %d, lo: %lld, avg: %lld, hi: %lld",
186 CONFIG_TEST_CLOCK_RT_ITERATIONS, CONFIG_TEST_CLOCK_RT_SLEEP_MS,
187 CONFIG_TEST_CLOCK_RT_ERROR_MS, lo_wm, cma, hi_wm);
188 zassert_between_inclusive(cma, lo, hi);
189 }
190
ZTEST(posix_timers,test_clock_getcpuclockid)191 ZTEST(posix_timers, test_clock_getcpuclockid)
192 {
193 int ret = 0;
194 clockid_t clock_id = CLOCK_INVALID;
195
196 ret = clock_getcpuclockid((pid_t)0, &clock_id);
197 zassert_equal(ret, 0, "POSIX clock_getcpuclock id failed");
198 zassert_equal(clock_id, CLOCK_PROCESS_CPUTIME_ID, "POSIX clock_getcpuclock id failed");
199
200 ret = clock_getcpuclockid((pid_t)2482, &clock_id);
201 zassert_equal(ret, EPERM, "POSIX clock_getcpuclock id failed");
202 }
203
ZTEST(posix_timers,test_clock_getres)204 ZTEST(posix_timers, test_clock_getres)
205 {
206 int ret;
207 struct timespec res;
208 const struct timespec one_ns = {
209 .tv_sec = 0,
210 .tv_nsec = 1,
211 };
212
213 struct arg {
214 clockid_t clock_id;
215 struct timespec *res;
216 int expect;
217 };
218
219 const struct arg args[] = {
220 /* permuting over "invalid" inputs */
221 {CLOCK_INVALID, NULL, -1},
222 {CLOCK_INVALID, &res, -1},
223 {CLOCK_REALTIME, NULL, 0},
224 {CLOCK_MONOTONIC, NULL, 0},
225 {CLOCK_PROCESS_CPUTIME_ID, NULL, 0},
226
227 /* all valid inputs */
228 {CLOCK_REALTIME, &res, 0},
229 {CLOCK_MONOTONIC, &res, 0},
230 {CLOCK_PROCESS_CPUTIME_ID, &res, 0},
231 };
232
233 ARRAY_FOR_EACH_PTR(args, arg) {
234 errno = 0;
235 res = (struct timespec){0};
236 ret = clock_getres(arg->clock_id, arg->res);
237 zassert_equal(ret, arg->expect);
238 if (ret != 0) {
239 zassert_equal(errno, EINVAL);
240 continue;
241 }
242 if (arg->res != NULL) {
243 zassert_true(tp_ge(arg->res, &one_ns));
244 }
245 }
246 }
247