3 # Perl version of (summary.sh, loop.awk, peer.awk):
4 # Create summaries from xntpd's loop and peer statistics.
6 # Copyright (c) 1997, 1999 by Ulrich Windl <Ulrich.Windl@rz.uni-regensburg.de>
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful, but
14 # WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 # General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 my ($log_date_pattern, $statsdir, $outputdir, $skip_time_steps, $startdate,
26 $enddate, $peer_dist_limit);
28 exit run(@ARGV) unless caller;
32 if (!processOptions(\@ARGV, $opts)) {
36 $log_date_pattern = '[12]\d{3}[01]\d[0-3]\d';
37 $statsdir = $opts->{directory};
38 $outputdir = $opts->{'output-directory'};
39 $skip_time_steps = $opts->{'skip-time-steps'};
40 $startdate = $opts->{'start-date'};
41 $enddate = $opts->{'end-date'};
43 $enddate = `date -u +%Y%m%d`;
47 $peer_dist_limit = $opts->{'peer-dist-limit'};
49 # check possibly current values of options
50 die "$statsdir: no such directory" unless (-d $statsdir);
51 die "$outputdir: no such directory" unless (-d $outputdir);
52 die "$skip_time_steps: skip-time-steps must be positive"
53 unless ($skip_time_steps >= 0.0);
54 die "$startdate: invalid start date|$`|$&|$'"
55 unless ($startdate =~ m/.*$log_date_pattern$/);
56 die "$enddate: invalid end date"
57 unless ($enddate =~ m/.*$log_date_pattern$/);
59 $skip_time_steps = 0.128 if ($skip_time_steps == 0);
61 my $loop_summary="$outputdir/loop_summary";
62 my $peer_summary="$outputdir/peer_summary";
63 my $clock_summary="$outputdir/clock_summary";
64 my (@loopfiles, @peerfiles, @clockfiles);
66 print STDERR "Creating summaries from $statsdir ($startdate to $enddate)\n";
68 opendir SDIR, $statsdir or die "directory ${statsdir}: $!";
70 @loopfiles=sort grep /loop.*$log_date_pattern/, readdir SDIR;
72 @peerfiles=sort grep /peer.*$log_date_pattern/, readdir SDIR;
74 @clockfiles=sort grep /clock.*$log_date_pattern/, readdir SDIR;
77 # remove old summary files
78 for ($loop_summary, $peer_summary, $clock_summary) { unlink $_ if -f $_ };
82 $date = $_; $date =~ s/.*($log_date_pattern)$/$1/;
83 if ($date ge $startdate && $date le $enddate) {
84 do_loop($statsdir, $_, $loop_summary);
89 $date = $_; $date =~ s/.*($log_date_pattern)$/$1/;
90 if ($date ge $startdate && $date le $enddate) {
91 do_peer($statsdir, $_, $peer_summary);
96 $date = $_; $date =~ s/.*($log_date_pattern)$/$1/;
97 if ($date ge $startdate && $date le $enddate) {
98 do_clock($statsdir, $_, $clock_summary);
102 print STDERR "Creating peer summary with limit $peer_dist_limit\n";
103 peer_summary($peer_summary) if (-f $peer_summary);
108 my ($result, @rest) = @_;
109 map { $result = $_ if ($_ < $result) } @rest;
115 my ($result, @rest) = @_;
116 map { $result = $_ if ($_ > $result) } @rest;
120 # calculate mean, range, and standard deviation for offset and frequency
123 my ($directory, $fname, $out_file) = @_;
124 print "$directory/$fname\n";
125 open INPUT, "$directory/$fname" or warn "can't open $directory/$fname: $!";
126 open OUTPUT, ">>$out_file" or die "can't open $out_file: $!";
127 print OUTPUT "$fname\n";
128 my ($loop_tmax, $loop_fmax) = (-1e9, -1e9);
129 my ($loop_tmin, $loop_fmin) = (1e9, 1e9);
130 my ($loop_time_rms, $loop_freq_rms) = (0, 0);
137 chop; # strip record separator
140 #NTPv3: 50529 74356.259 -0.000112 16.1230 8
141 #NTPv3: day, sec.msec, offset, drift_comp, sys_poll
142 #NTPv4: 51333 54734.582 0.000001648 16.981964 0.000001094 0.020938 6
143 #NTPv4: day, sec.msec, offset, drift_comp, sys_error, clock_stabil, sys_poll
144 if ($Fld[2] > $skip_time_steps || $Fld[2] < -$skip_time_steps) {
145 warn "ignoring loop offset $Fld[2] (file $fname, line $.)\n";
149 ($offs, $freq) = ($Fld[2], $Fld[3]);
150 $loop_tmax = max($loop_tmax, $offs);
151 $loop_tmin = min($loop_tmin, $offs);
152 $loop_fmax = max($loop_fmax, $freq);
153 $loop_fmin = min($loop_fmin, $freq);
155 $loop_time_rms += $offs * $offs;
157 $loop_freq_rms += $freq * $freq;
160 if ($loop_count > 1) {
161 $loop_time /= $loop_count;
162 $loop_time_rms = $loop_time_rms / $loop_count - $loop_time * $loop_time;
163 if ($loop_time_rms < 0) {
164 warn "loop_time_rms: $loop_time_rms < 0";
167 $loop_time_rms = sqrt($loop_time_rms);
168 $loop_freq /= $loop_count;
169 $loop_freq_rms = $loop_freq_rms / $loop_count - $loop_freq * $loop_freq;
170 if ($loop_freq_rms < 0) {
171 warn "loop_freq_rms: $loop_freq_rms < 0";
174 $loop_freq_rms = sqrt($loop_freq_rms);
176 ("loop %d, %.0f+/-%.1f, rms %.1f, freq %.2f+/-%0.3f, var %.3f\n",
177 $loop_count, ($loop_tmax + $loop_tmin) / 2 * 1e6,
178 ($loop_tmax - $loop_tmin) / 2 * 1e6, $loop_time_rms * 1e6,
179 ($loop_fmax + $loop_fmin) / 2, ($loop_fmax - $loop_fmin) / 2,
183 warn "no valid lines in $directory/$fname";
188 # calculate mean, standard deviation, maximum offset, mean dispersion,
189 # and maximum distance for each peer
192 my ($directory, $fname, $out_file) = @_;
193 print "$directory/$fname\n";
194 open INPUT, "$directory/$fname" or warn "can't open $directory/$fname: $!";
195 open OUTPUT, ">>$out_file" or die "can't open $out_file: $!";
196 print OUTPUT "$fname\n";
197 # we toss out all distances greater than one second on the assumption the
198 # peer is in initial acquisition
199 my ($n, $MAXDISTANCE) = (0, 1.0);
213 chop; # strip record separator
216 #NTPv3: 50529 83316.249 127.127.8.1 9674 0.008628 0.00000 0.00700
217 #NTPv3: day, sec.msec, addr, status, offset, delay, dispersion
218 #NTPv4: 51333 56042.037 127.127.8.1 94f5 -0.000014657 0.000000000 0.000000000 0.000013214
219 #NTPv4: day, sec.msec, addr, status, offset, delay, dispersion, skew
221 $dist = $Fld[6] + $Fld[5] / 2;
222 next if ($dist > $MAXDISTANCE);
224 if ($offs > $skip_time_steps || $offs < -$skip_time_steps) {
225 warn "ignoring peer offset $offs (file $fname, line $.)\n";
229 for ($j = 0; $j < $n; $j++) {
230 if ($Fld[2] eq $peer_ident{$j}) {
231 $i = $j; # peer found
235 if ($i == $n) { # add new peer
236 $peer_ident{$i} = $Fld[2];
237 $peer_tmax{$i} = $peer_dist{$i} = -1e9;
238 $peer_tmin{$i} = 1e9;
239 $peer_time{$i} = $peer_time_rms{$i} = 0;
240 $peer_delay{$i} = $peer_disp{$i} = 0;
245 $peer_tmax{$i} = max($peer_tmax{$i}, $offs);
246 $peer_tmin{$i} = min($peer_tmin{$i}, $offs);
247 $peer_dist{$i} = max($peer_dist{$i}, $dist);
248 $peer_time{$i} += $offs;
249 $peer_time_rms{$i} += $offs * $offs;
250 $peer_delay{$i} += $Fld[5];
251 $peer_disp{$i} += $Fld[6];
255 " ident cnt mean rms max delay dist disp\n";
257 "==========================================================================\n";
259 for ($i = 0; $i < $n; $i++) {
260 next if $peer_count{$i} < 2;
261 $peer_time{$i} /= $peer_count{$i};
262 eval { $peer_time_rms{$i} = sqrt($peer_time_rms{$i} / $peer_count{$i} -
263 $peer_time{$i} * $peer_time{$i}); };
264 $peer_time_rms{$i} = 0, warn $@ if $@;
265 $peer_delay{$i} /= $peer_count{$i};
266 $peer_disp{$i} /= $peer_count{$i};
267 $peer_tmax{$i} = $peer_tmax{$i} - $peer_time{$i};
268 $peer_tmin{$i} = $peer_time{$i} - $peer_tmin{$i};
269 if ($peer_tmin{$i} > $peer_tmax{$i}) { # can this happen at all?
270 $peer_tmax{$i} = $peer_tmin{$i};
273 "%-15s %4d %8.3f %8.3f %8.3f %8.3f %8.3f %8.3f\n",
274 $peer_ident{$i}, $peer_count{$i}, $peer_time{$i} * 1e3,
275 $peer_time_rms{$i} * 1e3, $peer_tmax{$i} * 1e3,
276 $peer_delay{$i} * 1e3, $peer_dist{$i} * 1e3, $peer_disp{$i} * 1e3;
278 print OUTPUT sort @lines;
284 my ($directory, $fname, $out_file) = @_;
285 print "$directory/$fname\n";
286 open INPUT, "$directory/$fname";
287 open OUTPUT, ">>$out_file" or die "can't open $out_file: $!";
288 print OUTPUT "$fname\n";
297 my (%peer_ident, %peer_count, %peer_mean, %peer_var, %peer_max);
298 my (%peer_1, %peer_2, %peer_3, %peer_4);
301 open INPUT, "<$in_file" or die "can't open $in_file: $!";
305 chop; # strip record separator
307 next if ($#Fld < 7 || $Fld[0] eq 'ident');
309 for ($j = 0; $j < $n; $j++) {
310 if ($Fld[0] eq $peer_ident{$j}) {
315 if ($i == $n) { # add new peer
316 $peer_count{$i} = $peer_mean{$i} = $peer_var{$i} = 0;
318 $peer_1{$i} = $peer_2{$i} = $peer_3{$i} = $peer_4{$i} = 0;
319 $peer_ident{$i} = $Fld[0];
322 $dist = $Fld[6] - $Fld[5] / 2;
323 if ($dist < $peer_dist_limit) {
325 $peer_mean{$i} += $Fld[2];
326 $peer_var{$i} += $Fld[3] * $Fld[3];
328 $peer_max{$i} = max($peer_max{$i}, $max);
343 warn "dist exceeds limit: $dist (file $in_file, line $.)\n";
349 " host days mean rms max >1 >5 >10 >50\n";
351 "==================================================================\n";
352 for ($i = 0; $i < $n; $i++) {
353 next if ($peer_count{$i} < 2);
354 $peer_mean{$i} /= $peer_count{$i};
355 eval { $peer_var{$i} = sqrt($peer_var{$i} / $peer_count{$i} -
356 $peer_mean{$i} * $peer_mean{$i}); };
357 $peer_var{$i} = 0, warn $@ if $@;
359 "%-15s %3d %9.3f% 9.3f %9.3f %3d %3d %3d %3d\n",
360 $peer_ident{$i}, $peer_count{$i}, $peer_mean{$i}, $peer_var{$i},
361 $peer_max{$i}, $peer_1{$i}, $peer_2{$i}, $peer_3{$i}, $peer_4{$i};