xref: /freebsd/contrib/ntp/scripts/summary.in (revision 6549718b70f0e660a15685369afb4f9caf2215ce)
1#! @PATH_PERL@ -w
2# @configure_input@
3# $Id$
4# Perl version of (summary.sh, loop.awk, peer.awk):
5# Create summaries from xntpd's loop and peer statistics.
6#
7# Copyright (c) 1997, 1999 by Ulrich Windl <Ulrich.Windl@rz.uni-regensburg.de>
8#
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation; either version 2 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful, but
15# WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17# General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program; if not, write to the Free Software
21# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22package summary;
23use 5.006_000;
24use strict;
25
26my ($log_date_pattern, $statsdir, $outputdir, $skip_time_steps, $startdate,
27    $enddate, $peer_dist_limit);
28
29exit run(@ARGV) unless caller;
30
31sub run {
32    my $opts;
33    if (!processOptions(\@ARGV, $opts)) {
34        usage(1);
35    };
36
37    $log_date_pattern = '[12]\d{3}[01]\d[0-3]\d';
38    $statsdir         = $opts->{directory};
39    $outputdir        = $opts->{'output-directory'};
40    $skip_time_steps  = $opts->{'skip-time-steps'};
41    $startdate        = $opts->{'start-date'};
42    $enddate          = $opts->{'end-date'};
43    if (!$enddate){
44        $enddate = `date -u +%Y%m%d`;
45        chomp $enddate;
46        --$enddate;
47    }
48    $peer_dist_limit = $opts->{'peer-dist-limit'};
49
50    # check possibly current values of options
51    die "$statsdir: no such directory" unless (-d $statsdir);
52    die "$outputdir: no such directory" unless (-d $outputdir);
53    die "$skip_time_steps: skip-time-steps must be positive"
54        unless ($skip_time_steps >= 0.0);
55    die "$startdate: invalid start date|$`|$&|$'"
56        unless ($startdate =~ m/.*$log_date_pattern$/);
57    die "$enddate: invalid end date"
58        unless ($enddate =~ m/.*$log_date_pattern$/);
59
60    $skip_time_steps = 0.128 if ($skip_time_steps == 0);
61
62    my $loop_summary="$outputdir/loop_summary";
63    my $peer_summary="$outputdir/peer_summary";
64    my $clock_summary="$outputdir/clock_summary";
65    my (@loopfiles, @peerfiles, @clockfiles);
66
67    print STDERR "Creating summaries from $statsdir ($startdate to $enddate)\n";
68
69    opendir SDIR, $statsdir or die "directory ${statsdir}: $!";
70    rewinddir SDIR;
71    @loopfiles=sort grep /loop.*$log_date_pattern/, readdir SDIR;
72    rewinddir SDIR;
73    @peerfiles=sort grep /peer.*$log_date_pattern/, readdir SDIR;
74    rewinddir SDIR;
75    @clockfiles=sort grep /clock.*$log_date_pattern/, readdir SDIR;
76    closedir SDIR;
77
78    # remove old summary files
79    for ($loop_summary, $peer_summary, $clock_summary) { unlink $_ if -f $_ };
80
81    my $date;
82    for (@loopfiles) {
83        $date = $_; $date =~ s/.*($log_date_pattern)$/$1/;
84        if ($date ge $startdate && $date le $enddate) {
85            do_loop($statsdir, $_, $loop_summary);
86        }
87    }
88
89    for (@peerfiles) {
90        $date = $_; $date =~ s/.*($log_date_pattern)$/$1/;
91        if ($date ge $startdate && $date le $enddate) {
92            do_peer($statsdir, $_, $peer_summary);
93        }
94    }
95
96    for (@clockfiles) {
97        $date = $_; $date =~ s/.*($log_date_pattern)$/$1/;
98        if ($date ge $startdate && $date le $enddate) {
99            do_clock($statsdir, $_, $clock_summary);
100        }
101    }
102
103    print STDERR "Creating peer summary with limit $peer_dist_limit\n";
104    peer_summary($peer_summary) if (-f $peer_summary);
105}
106
107sub min
108{
109    my ($result, @rest) = @_;
110    map { $result = $_ if ($_ < $result) } @rest;
111    return($result);
112}
113
114sub max
115{
116    my ($result, @rest) = @_;
117    map { $result = $_ if ($_ > $result) } @rest;
118    return($result);
119}
120
121# calculate mean, range, and standard deviation for offset and frequency
122sub do_loop
123{
124    my ($directory, $fname, $out_file) = @_;
125    print "$directory/$fname\n";
126    open INPUT, "$directory/$fname" or warn "can't open $directory/$fname: $!";
127    open OUTPUT, ">>$out_file" or die "can't open $out_file: $!";
128    print OUTPUT "$fname\n";
129    my ($loop_tmax, $loop_fmax) = (-1e9, -1e9);
130    my ($loop_tmin, $loop_fmin) = (1e9, 1e9);
131    my ($loop_time_rms, $loop_freq_rms) = (0, 0);
132    my $loop_count = 0;
133    my $loop_time = 0;
134    my $loop_freq = 0;
135    my ($freq, $offs);
136    my @Fld;
137    while (<INPUT>) {
138	chop;	# strip record separator
139	@Fld = split;
140	next if ($#Fld < 4);
141#NTPv3: 50529 74356.259 -0.000112 16.1230 8
142#NTPv3: day, sec.msec, offset, drift_comp, sys_poll
143#NTPv4: 51333 54734.582 0.000001648 16.981964 0.000001094 0.020938 6
144#NTPv4: day, sec.msec, offset, drift_comp, sys_error, clock_stabil, sys_poll
145	if ($Fld[2] > $skip_time_steps || $Fld[2] < -$skip_time_steps) {
146	    warn "ignoring loop offset $Fld[2] (file $fname, line $.)\n";
147	    next
148	}
149	$loop_count++;
150	($offs, $freq) = ($Fld[2], $Fld[3]);
151	$loop_tmax = max($loop_tmax, $offs);
152	$loop_tmin = min($loop_tmin, $offs);
153	$loop_fmax = max($loop_fmax, $freq);
154	$loop_fmin = min($loop_fmin, $freq);
155	$loop_time += $offs;
156	$loop_time_rms += $offs * $offs;
157	$loop_freq += $freq;
158	$loop_freq_rms += $freq * $freq;
159    }
160    close INPUT;
161    if ($loop_count > 1) {
162	$loop_time /= $loop_count;
163	$loop_time_rms = $loop_time_rms / $loop_count - $loop_time * $loop_time;
164	if ($loop_time_rms < 0) {
165	    warn "loop_time_rms: $loop_time_rms < 0";
166	    $loop_time_rms = 0;
167	}
168	$loop_time_rms = sqrt($loop_time_rms);
169	$loop_freq /= $loop_count;
170	$loop_freq_rms = $loop_freq_rms / $loop_count - $loop_freq * $loop_freq;
171	if ($loop_freq_rms < 0) {
172	    warn "loop_freq_rms: $loop_freq_rms < 0";
173	    $loop_freq_rms = 0;
174	}
175	$loop_freq_rms = sqrt($loop_freq_rms);
176	printf OUTPUT
177	    ("loop %d, %.0f+/-%.1f, rms %.1f, freq %.2f+/-%0.3f, var %.3f\n",
178	     $loop_count, ($loop_tmax + $loop_tmin) / 2 * 1e6,
179	     ($loop_tmax - $loop_tmin) / 2 * 1e6, $loop_time_rms * 1e6,
180	     ($loop_fmax + $loop_fmin) / 2, ($loop_fmax - $loop_fmin) / 2,
181	     $loop_freq_rms);
182    }
183    else {
184	warn "no valid lines in $directory/$fname";
185    }
186    close OUTPUT
187}
188
189# calculate mean, standard deviation, maximum offset, mean dispersion,
190# and maximum distance for each peer
191sub do_peer
192{
193    my ($directory, $fname, $out_file) = @_;
194    print "$directory/$fname\n";
195    open INPUT, "$directory/$fname" or warn "can't open $directory/$fname: $!";
196    open OUTPUT, ">>$out_file" or die "can't open $out_file: $!";
197    print OUTPUT "$fname\n";
198# we toss out all distances greater than one second on the assumption the
199# peer is in initial acquisition
200    my ($n, $MAXDISTANCE) = (0, 1.0);
201    my %peer_time;
202    my %peer_time_rms;
203    my %peer_count;
204    my %peer_delay;
205    my %peer_disp;
206    my %peer_dist;
207    my %peer_ident;
208    my %peer_tmin;
209    my %peer_tmax;
210    my @Fld;
211    my ($i, $j);
212    my ($dist, $offs);
213    while (<INPUT>) {
214	chop;	# strip record separator
215	@Fld = split;
216	next if ($#Fld < 6);
217#NTPv3: 50529 83316.249 127.127.8.1 9674 0.008628 0.00000 0.00700
218#NTPv3: day, sec.msec, addr, status, offset, delay, dispersion
219#NTPv4: 51333 56042.037 127.127.8.1 94f5 -0.000014657 0.000000000 0.000000000 0.000013214
220#NTPv4: day, sec.msec, addr, status, offset, delay, dispersion, skew
221
222	$dist = $Fld[6] + $Fld[5] / 2;
223	next if ($dist > $MAXDISTANCE);
224	$offs = $Fld[4];
225	if ($offs > $skip_time_steps || $offs < -$skip_time_steps) {
226	    warn "ignoring peer offset $offs (file $fname, line $.)\n";
227	    next
228	}
229	$i = $n;
230	for ($j = 0; $j < $n; $j++) {
231	    if ($Fld[2] eq $peer_ident{$j}) {
232		$i = $j;		# peer found
233		last;
234	    }
235	}
236	if ($i == $n) {		# add new peer
237	    $peer_ident{$i} = $Fld[2];
238	    $peer_tmax{$i} = $peer_dist{$i} = -1e9;
239	    $peer_tmin{$i} = 1e9;
240	    $peer_time{$i} = $peer_time_rms{$i} = 0;
241	    $peer_delay{$i} = $peer_disp{$i} = 0;
242	    $peer_count{$i} = 0;
243	    $n++;
244	}
245	$peer_count{$i}++;
246	$peer_tmax{$i} = max($peer_tmax{$i}, $offs);
247	$peer_tmin{$i} = min($peer_tmin{$i}, $offs);
248	$peer_dist{$i} = max($peer_dist{$i}, $dist);
249	$peer_time{$i} += $offs;
250	$peer_time_rms{$i} += $offs * $offs;
251	$peer_delay{$i} += $Fld[5];
252	$peer_disp{$i} += $Fld[6];
253    }
254    close INPUT;
255    print OUTPUT
256"       ident     cnt     mean     rms      max     delay     dist     disp\n";
257    print OUTPUT
258"==========================================================================\n";
259    my @lines = ();
260    for ($i = 0; $i < $n; $i++) {
261	next if $peer_count{$i} < 2;
262	$peer_time{$i} /= $peer_count{$i};
263	eval { $peer_time_rms{$i} = sqrt($peer_time_rms{$i} / $peer_count{$i} -
264					 $peer_time{$i} * $peer_time{$i}); };
265	$peer_time_rms{$i} = 0, warn $@ if $@;
266	$peer_delay{$i} /= $peer_count{$i};
267	$peer_disp{$i} /= $peer_count{$i};
268	$peer_tmax{$i} = $peer_tmax{$i} - $peer_time{$i};
269	$peer_tmin{$i} = $peer_time{$i} - $peer_tmin{$i};
270	if ($peer_tmin{$i} > $peer_tmax{$i}) {	# can this happen at all?
271	    $peer_tmax{$i} = $peer_tmin{$i};
272	}
273	push @lines, sprintf
274	    "%-15s %4d %8.3f %8.3f %8.3f %8.3f %8.3f %8.3f\n",
275	    $peer_ident{$i}, $peer_count{$i}, $peer_time{$i} * 1e3,
276	    $peer_time_rms{$i} * 1e3, $peer_tmax{$i} * 1e3,
277	    $peer_delay{$i} * 1e3, $peer_dist{$i} * 1e3, $peer_disp{$i} * 1e3;
278    }
279    print OUTPUT sort @lines;
280    close OUTPUT;
281}
282
283sub do_clock
284{
285    my ($directory, $fname, $out_file) = @_;
286    print "$directory/$fname\n";
287    open INPUT, "$directory/$fname";
288    open OUTPUT, ">>$out_file" or die "can't open $out_file: $!";
289    print OUTPUT "$fname\n";
290    close INPUT;
291    close OUTPUT;
292}
293
294sub peer_summary
295{
296    my $in_file = shift;
297    my ($i, $j, $n);
298    my (%peer_ident, %peer_count, %peer_mean, %peer_var, %peer_max);
299    my (%peer_1, %peer_2, %peer_3, %peer_4);
300    my $dist;
301    my $max;
302    open INPUT, "<$in_file" or die "can't open $in_file: $!";
303    my @Fld;
304    $n = 0;
305    while (<INPUT>) {
306	chop;	# strip record separator
307	@Fld = split;
308	next if ($#Fld < 7 || $Fld[0] eq 'ident');
309	$i = $n;
310	for ($j = 0; $j < $n; $j++) {
311	    if ($Fld[0] eq $peer_ident{$j}) {
312		$i = $j;
313		last;			# peer found
314	    }
315	}
316	if ($i == $n) {			# add new peer
317	    $peer_count{$i} = $peer_mean{$i} = $peer_var{$i} = 0;
318	    $peer_max{$i} = 0;
319 	    $peer_1{$i} = $peer_2{$i} = $peer_3{$i} = $peer_4{$i} = 0;
320	    $peer_ident{$i} = $Fld[0];
321	    ++$n;
322	}
323	$dist = $Fld[6] - $Fld[5] / 2;
324	if ($dist < $peer_dist_limit) {
325	    $peer_count{$i}++;
326	    $peer_mean{$i} += $Fld[2];
327	    $peer_var{$i} += $Fld[3] * $Fld[3];
328	    $max = $Fld[4];
329	    $peer_max{$i} = max($peer_max{$i}, $max);
330	    if ($max > 1) {
331		$peer_1{$i}++;
332		if ($max > 5) {
333		    $peer_2{$i}++;
334		    if ($max > 10) {
335			$peer_3{$i}++;
336			if ($max > 50) {
337			    $peer_4{$i}++;
338			}
339		    }
340		}
341	    }
342	}
343	else {
344	    warn "dist exceeds limit: $dist (file $in_file, line $.)\n";
345	}
346    }
347    close INPUT;
348    my @lines = ();
349    print
350	"       host     days    mean       rms       max   >1  >5 >10 >50\n";
351    print
352	"==================================================================\n";
353    for ($i = 0; $i < $n; $i++) {
354	next if ($peer_count{$i} < 2);
355	$peer_mean{$i} /= $peer_count{$i};
356	eval { $peer_var{$i} = sqrt($peer_var{$i} / $peer_count{$i} -
357				    $peer_mean{$i} * $peer_mean{$i}); };
358	$peer_var{$i} = 0, warn $@ if $@;
359	push @lines, sprintf
360	    "%-15s %3d %9.3f% 9.3f %9.3f %3d %3d %3d %3d\n",
361	    $peer_ident{$i}, $peer_count{$i}, $peer_mean{$i}, $peer_var{$i},
362	    $peer_max{$i}, $peer_1{$i}, $peer_2{$i}, $peer_3{$i}, $peer_4{$i};
363    }
364    print sort @lines;
365}
366
367@summary_opts@
368
3691;
370__END__
371