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