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