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 static const bool settable[] = {
27 	false,
28 	true,
29 };
30 
ts_to_ns(const struct timespec * ts)31 static inline int64_t ts_to_ns(const struct timespec *ts)
32 {
33 	return ts->tv_sec * NSEC_PER_SEC + ts->tv_nsec;
34 }
35 
tv_to_ts(const struct timeval * tv,struct timespec * ts)36 static inline void tv_to_ts(const struct timeval *tv, struct timespec *ts)
37 {
38 	ts->tv_sec = tv->tv_sec;
39 	ts->tv_nsec = tv->tv_usec * NSEC_PER_USEC;
40 }
41 
42 #define _tp_op(_a, _b, _op) (ts_to_ns(_a) _op ts_to_ns(_b))
43 
44 #define _decl_op(_type, _name, _op)                                                                \
45 	static inline _type _name(const struct timespec *_a, const struct timespec *_b)            \
46 	{                                                                                          \
47 		return _tp_op(_a, _b, _op);                                                        \
48 	}
49 
50 _decl_op(bool, tp_eq, ==);     /* a == b */
51 _decl_op(bool, tp_lt, <);      /* a < b */
52 _decl_op(bool, tp_gt, >);      /* a > b */
53 _decl_op(bool, tp_le, <=);     /* a <= b */
54 _decl_op(bool, tp_ge, >=);     /* a >= b */
55 _decl_op(int64_t, tp_diff, -); /* a - b */
56 
57 /* lo <= (a - b) < hi */
tp_diff_in_range_ns(const struct timespec * a,const struct timespec * b,int64_t lo,int64_t hi)58 static inline bool tp_diff_in_range_ns(const struct timespec *a, const struct timespec *b,
59 				       int64_t lo, int64_t hi)
60 {
61 	int64_t diff = tp_diff(a, b);
62 
63 	return diff >= lo && diff < hi;
64 }
65 
ZTEST(clock,test_clock_gettime)66 ZTEST(clock, test_clock_gettime)
67 {
68 	struct timespec ts;
69 
70 	/* ensure argument validation is performed */
71 	errno = 0;
72 	zassert_equal(clock_gettime(CLOCK_INVALID, &ts), -1);
73 	zassert_equal(errno, EINVAL);
74 
75 	if (false) {
76 		/* undefined behaviour */
77 		errno = 0;
78 		zassert_equal(clock_gettime(clocks[0], NULL), -1);
79 		zassert_equal(errno, EINVAL);
80 	}
81 
82 	/* verify that we can call clock_gettime() on supported clocks */
83 	ARRAY_FOR_EACH(clocks, i)
84 	{
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(clock,test_gettimeofday)92 ZTEST(clock, test_gettimeofday)
93 {
94 	struct timeval tv;
95 	struct timespec ts;
96 	struct timespec rts;
97 
98 	if (false) {
99 		/* undefined behaviour */
100 		errno = 0;
101 		zassert_equal(gettimeofday(NULL, NULL), -1);
102 		zassert_equal(errno, EINVAL);
103 	}
104 
105 	/* Validate gettimeofday API */
106 	zassert_ok(gettimeofday(&tv, NULL));
107 	zassert_ok(clock_gettime(CLOCK_REALTIME, &rts));
108 
109 	/* TESTPOINT: Check if time obtained from
110 	 * gettimeofday is same or more than obtained
111 	 * from clock_gettime
112 	 */
113 	tv_to_ts(&tv, &ts);
114 	zassert_true(tp_ge(&rts, &ts));
115 }
116 
ZTEST(clock,test_clock_settime)117 ZTEST(clock, test_clock_settime)
118 {
119 	int64_t diff_ns;
120 	struct timespec ts = {0};
121 
122 	BUILD_ASSERT(ARRAY_SIZE(settable) == ARRAY_SIZE(clocks));
123 
124 	/* ensure argument validation is performed */
125 	errno = 0;
126 	zassert_equal(clock_settime(CLOCK_INVALID, &ts), -1);
127 	zassert_equal(errno, EINVAL);
128 
129 	if (false) {
130 		/* undefined behaviour */
131 		errno = 0;
132 		zassert_equal(clock_settime(CLOCK_REALTIME, NULL), -1);
133 		zassert_equal(errno, EINVAL);
134 	}
135 
136 	/* verify nanoseconds */
137 	errno = 0;
138 	ts = (struct timespec){0, NSEC_PER_SEC};
139 	zassert_equal(clock_settime(CLOCK_REALTIME, &ts), -1);
140 	zassert_equal(errno, EINVAL);
141 	errno = 0;
142 	ts = (struct timespec){0, -1};
143 	zassert_equal(clock_settime(CLOCK_REALTIME, &ts), -1);
144 	zassert_equal(errno, EINVAL);
145 
146 	ARRAY_FOR_EACH(clocks, i)
147 	{
148 		if (!settable[i]) {
149 			/* should fail attempting to set unsettable clocks */
150 			errno = 0;
151 			zassert_equal(clock_settime(clocks[i], &ts), -1);
152 			zassert_equal(errno, EINVAL);
153 			continue;
154 		}
155 
156 		zassert_ok(clock_settime(clocks[i], &ref_ts));
157 
158 		/* read-back the time */
159 		zassert_ok(clock_gettime(clocks[i], &ts));
160 		/* dt should be >= 0, but definitely <= 1s */
161 		diff_ns = tp_diff(&ts, &ref_ts);
162 		zassert_true(diff_ns >= 0 && diff_ns <= NSEC_PER_SEC);
163 	}
164 }
165 
ZTEST(clock,test_realtime)166 ZTEST(clock, test_realtime)
167 {
168 	struct timespec then, now;
169 	/*
170 	 * For calculating cumulative moving average
171 	 * Note: we do not want to assert any individual samples due to scheduler noise.
172 	 * The CMA filters out the noise so we can make an assertion (on average).
173 	 * https://en.wikipedia.org/wiki/Moving_average#Cumulative_moving_average
174 	 */
175 	int64_t cma_prev = 0;
176 	int64_t cma;
177 	int64_t x_i;
178 	/* lower and uppoer boundary for assertion */
179 	int64_t lo = CONFIG_TEST_CLOCK_RT_SLEEP_MS;
180 	int64_t hi = CONFIG_TEST_CLOCK_RT_SLEEP_MS + CONFIG_TEST_CLOCK_RT_ERROR_MS;
181 	/* lower and upper watermark */
182 	int64_t lo_wm = INT64_MAX;
183 	int64_t hi_wm = INT64_MIN;
184 
185 	/* Loop n times, sleeping a little bit for each */
186 	(void)clock_gettime(CLOCK_REALTIME, &then);
187 	for (int i = 0; i < CONFIG_TEST_CLOCK_RT_ITERATIONS; ++i) {
188 
189 		zassert_ok(k_usleep(USEC_PER_MSEC * CONFIG_TEST_CLOCK_RT_SLEEP_MS));
190 		(void)clock_gettime(CLOCK_REALTIME, &now);
191 
192 		/* Make the delta milliseconds. */
193 		x_i = tp_diff(&now, &then) / NSEC_PER_MSEC;
194 		then = now;
195 
196 		if (x_i < lo_wm) {
197 			/* update low watermark */
198 			lo_wm = x_i;
199 		}
200 
201 		if (x_i > hi_wm) {
202 			/* update high watermark */
203 			hi_wm = x_i;
204 		}
205 
206 		/* compute cumulative running average */
207 		cma = (x_i + i * cma_prev) / (i + 1);
208 		cma_prev = cma;
209 	}
210 
211 	LOG_INF("n: %d, sleep: %d, margin: %d, lo: %lld, avg: %lld, hi: %lld",
212 		CONFIG_TEST_CLOCK_RT_ITERATIONS, CONFIG_TEST_CLOCK_RT_SLEEP_MS,
213 		CONFIG_TEST_CLOCK_RT_ERROR_MS, lo_wm, cma, hi_wm);
214 	zassert_between_inclusive(cma, lo, hi);
215 }
216 
ZTEST(clock,test_clock_getcpuclockid)217 ZTEST(clock, test_clock_getcpuclockid)
218 {
219 	int ret = 0;
220 	clockid_t clock_id = CLOCK_INVALID;
221 
222 	ret = clock_getcpuclockid((pid_t)0, &clock_id);
223 	zassert_equal(ret, 0, "POSIX clock_getcpuclock id failed");
224 	zassert_equal(clock_id, CLOCK_PROCESS_CPUTIME_ID, "POSIX clock_getcpuclock id failed");
225 
226 	ret = clock_getcpuclockid((pid_t)2482, &clock_id);
227 	zassert_equal(ret, EPERM, "POSIX clock_getcpuclock id failed");
228 }
229 
230 ZTEST_SUITE(clock, NULL, NULL, NULL, NULL, NULL);
231