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