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