Home | History | Annotate | Line # | Download | only in util
      1 /*	$NetBSD: precision.c,v 1.5 2020/05/25 20:47:37 christos Exp $	*/
      2 
      3 #include "ntp_unixtime.h"
      4 
      5 #include <stdio.h>
      6 
      7 #define	DEFAULT_SYS_PRECISION	-99
      8 
      9 int default_get_resolution();
     10 int default_get_precision();
     11 
     12 int
     13 main(
     14 	int argc,
     15 	char *argv[]
     16 	)
     17 {
     18 	printf("log2(resolution) = %d, log2(precision) = %d\n",
     19 	       default_get_resolution(),
     20 	       default_get_precision());
     21 	return 0;
     22 }
     23 
     24 /* Find the resolution of the system clock by watching how the current time
     25  * changes as we read it repeatedly.
     26  *
     27  * struct timeval is only good to 1us, which may cause problems as machines
     28  * get faster, but until then the logic goes:
     29  *
     30  * If a machine has resolution (i.e. accurate timing info) > 1us, then it will
     31  * probably use the "unused" low order bits as a counter (to force time to be
     32  * a strictly increaing variable), incrementing it each time any process
     33  * requests the time [[ or maybe time will stand still ? ]].
     34  *
     35  * SO: the logic goes:
     36  *
     37  *      IF      the difference from the last time is "small" (< MINSTEP)
     38  *      THEN    this machine is "counting" with the low order bits
     39  *      ELIF    this is not the first time round the loop
     40  *      THEN    this machine *WAS* counting, and has now stepped
     41  *      ELSE    this machine has resolution < time to read clock
     42  *
     43  * SO: if it exits on the first loop, assume "full accuracy" (1us)
     44  *     otherwise, take the log2(observered difference, rounded UP)
     45  *
     46  * MINLOOPS > 1 ensures that even if there is a STEP between the initial call
     47  * and the first loop, it doesn't stop too early.
     48  * Making it even greater allows MINSTEP to be reduced, assuming that the
     49  * chance of MINSTEP-1 other processes getting in and calling gettimeofday
     50  * between this processes's calls.
     51  * Reducing MINSTEP may be necessary as this sets an upper bound for the time
     52  * to actually call gettimeofday.
     53  */
     54 
     55 #define	DUSECS	1000000
     56 #define	HUSECS	(1024 * 1024)
     57 #define	MINSTEP	5	/* some systems increment uS on each call */
     58 /* Don't use "1" as some *other* process may read too*/
     59 /*We assume no system actually *ANSWERS* in this time*/
     60 #define MAXSTEP 20000   /* maximum clock increment (us) */
     61 #define MINLOOPS 5      /* minimum number of step samples */
     62 #define	MAXLOOPS HUSECS	/* Assume precision < .1s ! */
     63 
     64 int
     65 default_get_resolution(void)
     66 {
     67 	struct timeval tp;
     68 	struct timezone tzp;
     69 	long last;
     70 	int i;
     71 	long diff;
     72 	long val;
     73 	int minsteps = MINLOOPS;	/* need at least this many steps */
     74 
     75 	gettimeofday(&tp, &tzp);
     76 	last = tp.tv_usec;
     77 	for (i = - --minsteps; i< MAXLOOPS; i++) {
     78 		gettimeofday(&tp, &tzp);
     79 		diff = tp.tv_usec - last;
     80 		if (diff < 0) diff += DUSECS;
     81 		if (diff > MINSTEP) if (minsteps-- <= 0) break;
     82 		last = tp.tv_usec;
     83 	}
     84 
     85 	printf("resolution = %ld usec after %d loop%s\n",
     86 	       diff, i, (i==1) ? "" : "s");
     87 
     88 	diff = (diff *3)/2;
     89 	if (i >= MAXLOOPS) {
     90 		printf(
     91 			"     (Boy this machine is fast ! %d loops without a step)\n",
     92 			MAXLOOPS);
     93 		diff = 1; /* No STEP, so FAST machine */
     94 	}
     95 	if (i == 0) {
     96 		printf(
     97 			"     (The resolution is less than the time to read the clock -- Assume 1us)\n");
     98 		diff = 1; /* time to read clock >= resolution */
     99 	}
    100 	for (i=0, val=HUSECS; val>0; i--, val >>= 1) if (diff >= val) return i;
    101 	printf("     (Oh dear -- that wasn't expected ! I'll guess !)\n");
    102 	return DEFAULT_SYS_PRECISION /* Something's BUST, so lie ! */;
    103 }
    104 
    105 /* ===== Rest of this code lifted straight from xntpd/ntp_proto.c ! ===== */
    106 
    107 /*
    108  * This routine calculates the differences between successive calls to
    109  * gettimeofday(). If a difference is less than zero, the us field
    110  * has rolled over to the next second, so we add a second in us. If
    111  * the difference is greater than zero and less than MINSTEP, the
    112  * clock has been advanced by a small amount to avoid standing still.
    113  * If the clock has advanced by a greater amount, then a timer interrupt
    114  * has occurred and this amount represents the precision of the clock.
    115  * In order to guard against spurious values, which could occur if we
    116  * happen to hit a fat interrupt, we do this for MINLOOPS times and
    117  * keep the minimum value obtained.
    118  */
    119 int
    120 default_get_precision(void)
    121 {
    122 	struct timeval tp;
    123 	struct timezone tzp;
    124 #ifdef HAVE_GETCLOCK
    125 	struct timespec ts;
    126 #endif
    127 	long last;
    128 	int i;
    129 	long diff;
    130 	long val;
    131 	long usec;
    132 
    133 	usec = 0;
    134 	val = MAXSTEP;
    135 #ifdef HAVE_GETCLOCK
    136 	(void) getclock(TIMEOFDAY, &ts);
    137 	tp.tv_sec = ts.tv_sec;
    138 	tp.tv_usec = ts.tv_nsec / 1000;
    139 #else /*  not HAVE_GETCLOCK */
    140 	GETTIMEOFDAY(&tp, &tzp);
    141 #endif /* not HAVE_GETCLOCK */
    142 	last = tp.tv_usec;
    143 	for (i = 0; i < MINLOOPS && usec < HUSECS;) {
    144 #ifdef HAVE_GETCLOCK
    145 		(void) getclock(TIMEOFDAY, &ts);
    146 		tp.tv_sec = ts.tv_sec;
    147 		tp.tv_usec = ts.tv_nsec / 1000;
    148 #else /*  not HAVE_GETCLOCK */
    149 		GETTIMEOFDAY(&tp, &tzp);
    150 #endif /* not HAVE_GETCLOCK */
    151 		diff = tp.tv_usec - last;
    152 		last = tp.tv_usec;
    153 		if (diff < 0)
    154 		    diff += DUSECS;
    155 		usec += diff;
    156 		if (diff > MINSTEP) {
    157 			i++;
    158 			if (diff < val)
    159 			    val = diff;
    160 		}
    161 	}
    162 	printf("precision  = %ld usec after %d loop%s\n",
    163 	       val, i, (i == 1) ? "" : "s");
    164 	if (usec >= HUSECS) {
    165 		printf("     (Boy this machine is fast ! usec was %ld)\n",
    166 		       usec);
    167 		val = MINSTEP;	/* val <= MINSTEP; fast machine */
    168 	}
    169 	diff = HUSECS;
    170 	for (i = 0; diff > val; i--)
    171 	    diff >>= 1;
    172 	return (i);
    173 }
    174