xref: /netbsd-src/tests/lib/libc/sys/t_clock_gettime.c (revision a41503c83d90220b1d787eaa3473b843a1878d0c)
1*a41503c8Sriastradh /* $NetBSD: t_clock_gettime.c,v 1.6 2023/07/09 19:19:40 riastradh Exp $ */
22a39986dSjruoho 
32a39986dSjruoho /*-
42a39986dSjruoho  * Copyright (c) 2008 The NetBSD Foundation, Inc.
52a39986dSjruoho  * All rights reserved.
62a39986dSjruoho  *
72a39986dSjruoho  * This code is derived from software contributed to The NetBSD Foundation
82a39986dSjruoho  * by Frank Kardel.
92a39986dSjruoho  *
102a39986dSjruoho  * Redistribution and use in source and binary forms, with or without
112a39986dSjruoho  * modification, are permitted provided that the following conditions
122a39986dSjruoho  * are met:
132a39986dSjruoho  * 1. Redistributions of source code must retain the above copyright
142a39986dSjruoho  *    notice, this list of conditions and the following disclaimer.
152a39986dSjruoho  * 2. Redistributions in binary form must reproduce the above copyright
162a39986dSjruoho  *    notice, this list of conditions and the following disclaimer in the
172a39986dSjruoho  *    documentation and/or other materials provided with the distribution.
182a39986dSjruoho  *
192a39986dSjruoho  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
202a39986dSjruoho  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
212a39986dSjruoho  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
222a39986dSjruoho  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
232a39986dSjruoho  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
242a39986dSjruoho  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
252a39986dSjruoho  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
262a39986dSjruoho  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
272a39986dSjruoho  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
282a39986dSjruoho  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
292a39986dSjruoho  * POSSIBILITY OF SUCH DAMAGE.
302a39986dSjruoho  */
312a39986dSjruoho 
322a39986dSjruoho /*-
332a39986dSjruoho  * Copyright (c) 2006 Frank Kardel
342a39986dSjruoho  * All rights reserved.
352a39986dSjruoho  *
362a39986dSjruoho  * Redistribution and use in source and binary forms, with or without
372a39986dSjruoho  * modification, are permitted provided that the following conditions
382a39986dSjruoho  * are met:
392a39986dSjruoho  * 1. Redistributions of source code must retain the above copyright
402a39986dSjruoho  *    notice, this list of conditions and the following disclaimer.
412a39986dSjruoho  * 2. Redistributions in binary form must reproduce the above copyright
422a39986dSjruoho  *    notice, this list of conditions and the following disclaimer in the
432a39986dSjruoho  *    documentation and/or other materials provided with the distribution.
442a39986dSjruoho  *
452a39986dSjruoho  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
462a39986dSjruoho  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
472a39986dSjruoho  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
482a39986dSjruoho  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
492a39986dSjruoho  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
502a39986dSjruoho  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
512a39986dSjruoho  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
522a39986dSjruoho  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
532a39986dSjruoho  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
542a39986dSjruoho  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
552a39986dSjruoho  * POSSIBILITY OF SUCH DAMAGE.
562a39986dSjruoho  */
572a39986dSjruoho 
582a39986dSjruoho #include <sys/cdefs.h>
592a39986dSjruoho __COPYRIGHT("@(#) Copyright (c) 2008\
602a39986dSjruoho  The NetBSD Foundation, inc. All rights reserved.");
61*a41503c8Sriastradh __RCSID("$NetBSD: t_clock_gettime.c,v 1.6 2023/07/09 19:19:40 riastradh Exp $");
622a39986dSjruoho 
632a39986dSjruoho #include <sys/param.h>
642a39986dSjruoho 
653fe2e072Sriastradh #include <sys/ioctl.h>
663fe2e072Sriastradh #include <sys/sysctl.h>
672a39986dSjruoho 
682a39986dSjruoho #include <atf-c.h>
692a39986dSjruoho #include <errno.h>
703fe2e072Sriastradh #include <fcntl.h>
71bf323d9fSchristos #include <limits.h>
72bf323d9fSchristos #include <stdint.h>
733fe2e072Sriastradh #include <stdio.h>
742a39986dSjruoho #include <stdlib.h>
752a39986dSjruoho #include <string.h>
762a39986dSjruoho #include <time.h>
772a39986dSjruoho #include <unistd.h>
782a39986dSjruoho 
79c54cb811Schristos #include "h_macros.h"
802a39986dSjruoho 
812a39986dSjruoho #define MINPOSDIFF	15000000	/* 15 ms for now */
822a39986dSjruoho #define TIMEOUT		5
832a39986dSjruoho 
842a39986dSjruoho #define TC_HARDWARE	"kern.timecounter.hardware"
852a39986dSjruoho #define TC_CHOICE	"kern.timecounter.choice"
862a39986dSjruoho 
872a39986dSjruoho static void
check_timecounter(void)882a39986dSjruoho check_timecounter(void)
892a39986dSjruoho {
902a39986dSjruoho 	struct timespec tsa, tsb, tsl, res;
912a39986dSjruoho 	long long mindiff = INTMAX_MAX;
922a39986dSjruoho 	time_t endlimit;
932a39986dSjruoho 
942a39986dSjruoho #define CL(x) \
952a39986dSjruoho 	do { \
962a39986dSjruoho 		if ((x) != -1) \
972a39986dSjruoho 			break; \
982a39986dSjruoho 		atf_tc_fail_nonfatal("%s: %s", #x, strerror(errno)); \
992a39986dSjruoho 		return; \
1002a39986dSjruoho 	} while (0)
1012a39986dSjruoho 
1022a39986dSjruoho 	CL(clock_gettime(CLOCK_REALTIME, &tsa));
1032a39986dSjruoho 	tsl = tsa;
1042a39986dSjruoho 
1052a39986dSjruoho 	CL(time(&endlimit));
1062a39986dSjruoho 	endlimit += TIMEOUT + 1;
1072a39986dSjruoho 
1082a39986dSjruoho 	while ((time_t)tsa.tv_sec < endlimit) {
1092a39986dSjruoho 		long long diff;
1102a39986dSjruoho 
1112a39986dSjruoho 		CL(clock_gettime(CLOCK_REALTIME, &tsb));
1122a39986dSjruoho 		diff = 1000000000LL * (tsb.tv_sec - tsa.tv_sec)
1132a39986dSjruoho 		    + tsb.tv_nsec - tsa.tv_nsec;
1142a39986dSjruoho 
1152a39986dSjruoho 		if (diff > 0 && mindiff > diff)
1162a39986dSjruoho 			mindiff = diff;
1172a39986dSjruoho 
1182a39986dSjruoho 		if (diff < 0 || diff > MINPOSDIFF) {
1192a39986dSjruoho 			long long elapsed;
1202a39986dSjruoho 			(void)printf("%stime TSA: 0x%jx.%08jx, TSB: 0x%jx.%08jx, "
1212a39986dSjruoho 			    "diff = %lld nsec, ", (diff < 0) ? "BAD " : "",
1222a39986dSjruoho 			    (uintmax_t)tsa.tv_sec, (uintmax_t)tsa.tv_nsec,
1232a39986dSjruoho 			    (uintmax_t)tsb.tv_sec, (uintmax_t)tsb.tv_nsec, diff);
1242a39986dSjruoho 
1252a39986dSjruoho 			elapsed = 1000000000LL * (tsb.tv_sec - tsl.tv_sec)
1262a39986dSjruoho 			    + tsb.tv_nsec - tsl.tv_nsec;
1272a39986dSjruoho 
1282a39986dSjruoho 
1292a39986dSjruoho 			(void)printf("%lld nsec\n", elapsed);
1302a39986dSjruoho 			tsl = tsb;
1312a39986dSjruoho 
1322a39986dSjruoho 			ATF_CHECK(diff >= 0);
1332a39986dSjruoho 			if (diff < 0)
1342a39986dSjruoho 				return;
1352a39986dSjruoho 		}
1362a39986dSjruoho 
1372a39986dSjruoho 		tsa.tv_sec = tsb.tv_sec;
1382a39986dSjruoho 		tsa.tv_nsec = tsb.tv_nsec;
1392a39986dSjruoho 	}
1402a39986dSjruoho 
1412a39986dSjruoho 	if (clock_getres(CLOCK_REALTIME, &res) == 0) {
1422a39986dSjruoho 		long long r = res.tv_sec * 1000000000 + res.tv_nsec;
1432a39986dSjruoho 
1442a39986dSjruoho 		(void)printf("Claimed resolution: %lld nsec (%f Hz) or "
1452a39986dSjruoho 		    "better\n", r, 1.0 / r * 1e9);
1462a39986dSjruoho 		(void)printf("Observed minimum non zero delta: %lld "
1472a39986dSjruoho 		    "nsec\n", mindiff);
1482a39986dSjruoho 	}
1492a39986dSjruoho 
1502a39986dSjruoho #undef CL
1512a39986dSjruoho }
1522a39986dSjruoho 
1532a39986dSjruoho ATF_TC(clock_gettime_real);
ATF_TC_HEAD(clock_gettime_real,tc)1542a39986dSjruoho ATF_TC_HEAD(clock_gettime_real, tc)
1552a39986dSjruoho {
1562a39986dSjruoho 	atf_tc_set_md_var(tc, "require.user", "root");
1572a39986dSjruoho 	atf_tc_set_md_var(tc, "descr",
1582a39986dSjruoho 	    "Checks the monotonicity of the CLOCK_REALTIME implementation");
1592a39986dSjruoho 	atf_tc_set_md_var(tc, "timeout", "300");
1602a39986dSjruoho }
1612a39986dSjruoho 
ATF_TC_BODY(clock_gettime_real,tc)1622a39986dSjruoho ATF_TC_BODY(clock_gettime_real, tc)
1632a39986dSjruoho {
1642a39986dSjruoho 	char name[128], cbuf[512], ctrbuf[10240];
1652a39986dSjruoho 	size_t cbufsiz = sizeof(cbuf);
1662a39986dSjruoho 	size_t ctrbufsiz = sizeof(ctrbuf);
1672a39986dSjruoho 	const char *p;
1682a39986dSjruoho 	char *save;
1692a39986dSjruoho 	int quality, n;
1702a39986dSjruoho 
1712a39986dSjruoho 	if (sysctlbyname(TC_HARDWARE, cbuf, &cbufsiz, NULL, 0) != 0) {
1722a39986dSjruoho 		(void)printf("\nChecking legacy time implementation "
1732a39986dSjruoho 		    "for %d seconds\n", TIMEOUT);
1742a39986dSjruoho 		check_timecounter();
1752a39986dSjruoho 		return;
1762a39986dSjruoho 		/* NOTREACHED */
1772a39986dSjruoho 	}
1782a39986dSjruoho 	(void)printf("%s = %s\n", TC_HARDWARE, cbuf);
1792a39986dSjruoho 	REQUIRE_LIBC(save = strdup(cbuf), NULL);
1802a39986dSjruoho 
1812a39986dSjruoho 	RL(sysctlbyname(TC_CHOICE, ctrbuf, &ctrbufsiz, NULL, 0));
1822a39986dSjruoho 	(void)printf("%s = %s\n", TC_CHOICE, ctrbuf);
1832a39986dSjruoho 
1842a39986dSjruoho 	for (p = ctrbuf, n = 0; sscanf(p, "%127[^(](q=%d, f=%*u Hz)%*[ ]%n",
1852a39986dSjruoho 	    name, &quality, &n) == 2; p += n) {
1862a39986dSjruoho 		struct timespec ts;
1872a39986dSjruoho 		int ret;
1882a39986dSjruoho 
1892a39986dSjruoho 		if (quality < 0)
1902a39986dSjruoho 			continue;
1912a39986dSjruoho 
1922a39986dSjruoho 		(void)printf("\nChecking %s for %d seconds\n", name, TIMEOUT);
1932a39986dSjruoho 		CHECK_LIBC(ret = sysctlbyname(TC_HARDWARE, NULL, 0,
1942a39986dSjruoho 		    name, strlen(name)), -1);
1952a39986dSjruoho 		if (ret == -1)
1962a39986dSjruoho 			continue;
1972a39986dSjruoho 
1982a39986dSjruoho 		/* wait a bit to select new counter in clockinterrupt */
1992a39986dSjruoho 		ts.tv_sec = 0;
2002a39986dSjruoho 		ts.tv_nsec = 100000000;
2012a39986dSjruoho 		(void)nanosleep(&ts, NULL);
2022a39986dSjruoho 
2032a39986dSjruoho 		check_timecounter();
2042a39986dSjruoho 	}
2052a39986dSjruoho 
2062a39986dSjruoho 	RL(sysctlbyname(TC_HARDWARE, NULL, 0, save, strlen(save)));
2072a39986dSjruoho }
2082a39986dSjruoho 
2093fe2e072Sriastradh static void
waste_user_time(void)2103fe2e072Sriastradh waste_user_time(void)
2113fe2e072Sriastradh {
2123fe2e072Sriastradh 	static char buf[4*4096];
2133fe2e072Sriastradh 
2143fe2e072Sriastradh 	arc4random_buf(buf, sizeof(buf));
2153fe2e072Sriastradh }
2163fe2e072Sriastradh 
2173fe2e072Sriastradh static void __unused
waste_system_time(void)2183fe2e072Sriastradh waste_system_time(void)
2193fe2e072Sriastradh {
2203fe2e072Sriastradh 	static char buf[4*4096];
2213fe2e072Sriastradh 	int fd[2];
2223fe2e072Sriastradh 	int i, n;
2233fe2e072Sriastradh 
2243fe2e072Sriastradh 	RL(pipe2(fd, O_NONBLOCK));
2253fe2e072Sriastradh 	RL(n = ioctl(fd[1], FIONSPACE));
226*a41503c8Sriastradh 	n = MIN((unsigned)MAX(0, n), sizeof(buf));
2273fe2e072Sriastradh 	for (i = 0; i < 16; i++) {
2283fe2e072Sriastradh 		RL(write(fd[1], buf, n));
2293fe2e072Sriastradh 		RL(read(fd[0], buf, n));
2303fe2e072Sriastradh 	}
2313fe2e072Sriastradh 	RL(close(fd[0]));
2323fe2e072Sriastradh 	RL(close(fd[1]));
2333fe2e072Sriastradh }
2343fe2e072Sriastradh 
2353fe2e072Sriastradh static void
check_monotonicity(const char * clockname,clockid_t clockid,void (* waste_time)(void))2363fe2e072Sriastradh check_monotonicity(const char *clockname, clockid_t clockid,
2373fe2e072Sriastradh     void (*waste_time)(void))
2383fe2e072Sriastradh {
2393fe2e072Sriastradh 	static const struct timespec maxtime = {5, 0};
2403fe2e072Sriastradh 	struct timespec mono_t0, t0, mono_d;
2413fe2e072Sriastradh 
2423fe2e072Sriastradh 	RL(clock_gettime(CLOCK_MONOTONIC, &mono_t0));
2433fe2e072Sriastradh 	RL(clock_gettime(clockid, &t0));
2443fe2e072Sriastradh 
2453fe2e072Sriastradh 	do {
2463fe2e072Sriastradh 		struct timespec t1, mono_t1;
2473fe2e072Sriastradh 
2483fe2e072Sriastradh 		(*waste_time)();
2493fe2e072Sriastradh 
2503fe2e072Sriastradh 		RL(clock_gettime(clockid, &t1));
2513fe2e072Sriastradh 		ATF_CHECK_MSG(timespeccmp(&t0, &t1, <=),
2523fe2e072Sriastradh 		    "clock %s=0x%jx went backwards t0=%jd.%09ld t1=%jd.%09ld",
2533fe2e072Sriastradh 		    clockname, (uintmax_t)clockid,
2543fe2e072Sriastradh 		    (intmax_t)t0.tv_sec, t0.tv_nsec,
2553fe2e072Sriastradh 		    (intmax_t)t1.tv_sec, t1.tv_nsec);
2563fe2e072Sriastradh 
2573fe2e072Sriastradh 		t0 = t1;
2583fe2e072Sriastradh 
2593fe2e072Sriastradh 		RL(clock_gettime(CLOCK_MONOTONIC, &mono_t1));
2603fe2e072Sriastradh 		timespecsub(&mono_t1, &mono_t0, &mono_d);
2613fe2e072Sriastradh 	} while (timespeccmp(&mono_d, &maxtime, <));
2623fe2e072Sriastradh }
2633fe2e072Sriastradh 
2643fe2e072Sriastradh ATF_TC(clock_gettime_process_cputime_is_monotonic);
ATF_TC_HEAD(clock_gettime_process_cputime_is_monotonic,tc)2653fe2e072Sriastradh ATF_TC_HEAD(clock_gettime_process_cputime_is_monotonic, tc)
2663fe2e072Sriastradh {
2673fe2e072Sriastradh 	atf_tc_set_md_var(tc, "descr",
2683fe2e072Sriastradh 	    "Checks that CLOCK_PROCESS_CPUTIME_ID is monotonic");
2693fe2e072Sriastradh }
ATF_TC_BODY(clock_gettime_process_cputime_is_monotonic,tc)2703fe2e072Sriastradh ATF_TC_BODY(clock_gettime_process_cputime_is_monotonic, tc)
2713fe2e072Sriastradh {
2723fe2e072Sriastradh 	check_monotonicity("CLOCK_PROCESS_CPUTIME_ID",
2733fe2e072Sriastradh 	    CLOCK_PROCESS_CPUTIME_ID, &waste_user_time);
2743fe2e072Sriastradh }
2753fe2e072Sriastradh 
2763fe2e072Sriastradh ATF_TC(clock_gettime_thread_cputime_is_monotonic);
ATF_TC_HEAD(clock_gettime_thread_cputime_is_monotonic,tc)2773fe2e072Sriastradh ATF_TC_HEAD(clock_gettime_thread_cputime_is_monotonic, tc)
2783fe2e072Sriastradh {
2793fe2e072Sriastradh 	atf_tc_set_md_var(tc, "descr",
2803fe2e072Sriastradh 	    "Checks that CLOCK_THREAD_CPUTIME_ID is monotonic");
2813fe2e072Sriastradh }
ATF_TC_BODY(clock_gettime_thread_cputime_is_monotonic,tc)2823fe2e072Sriastradh ATF_TC_BODY(clock_gettime_thread_cputime_is_monotonic, tc)
2833fe2e072Sriastradh {
2843fe2e072Sriastradh 	check_monotonicity("CLOCK_THREAD_CPUTIME_ID",
2853fe2e072Sriastradh 	    CLOCK_THREAD_CPUTIME_ID, &waste_user_time);
2863fe2e072Sriastradh }
2873fe2e072Sriastradh 
ATF_TP_ADD_TCS(tp)2882a39986dSjruoho ATF_TP_ADD_TCS(tp)
2892a39986dSjruoho {
2902a39986dSjruoho 
2912a39986dSjruoho 	ATF_TP_ADD_TC(tp, clock_gettime_real);
2923fe2e072Sriastradh 	ATF_TP_ADD_TC(tp, clock_gettime_process_cputime_is_monotonic);
2933fe2e072Sriastradh 	ATF_TP_ADD_TC(tp, clock_gettime_thread_cputime_is_monotonic);
2942a39986dSjruoho 
2952a39986dSjruoho 	return atf_no_error();
2962a39986dSjruoho }
297