summary.in revision 290001
1#! @PATH_PERL@ -w
2# $Id$
3# Perl version of (summary.sh, loop.awk, peer.awk):
4# Create summaries from xntpd's loop and peer statistics.
5#
6# Copyright (c) 1997, 1999 by Ulrich Windl <Ulrich.Windl@rz.uni-regensburg.de>
7#
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.
12#
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.
17#
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.
21package summary;
22use 5.006_000;
23use strict;
24
25my ($log_date_pattern, $statsdir, $outputdir, $skip_time_steps, $startdate,
26    $enddate, $peer_dist_limit);
27
28exit run(@ARGV) unless caller;
29
30sub run {
31    my $opts;
32    if (!processOptions(\@ARGV, $opts)) {
33        usage(1);
34    };
35
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'};
42    if (!$enddate){
43        $enddate = `date -u +%Y%m%d`; 
44        chomp $enddate;
45        --$enddate;
46    }
47    $peer_dist_limit = $opts->{'peer-dist-limit'};
48
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$/);
58
59    $skip_time_steps = 0.128 if ($skip_time_steps == 0);
60
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);
65
66    print STDERR "Creating summaries from $statsdir ($startdate to $enddate)\n";
67
68    opendir SDIR, $statsdir or die "directory ${statsdir}: $!";
69    rewinddir SDIR;
70    @loopfiles=sort grep /loop.*$log_date_pattern/, readdir SDIR;
71    rewinddir SDIR;
72    @peerfiles=sort grep /peer.*$log_date_pattern/, readdir SDIR;
73    rewinddir SDIR;
74    @clockfiles=sort grep /clock.*$log_date_pattern/, readdir SDIR;
75    closedir SDIR;
76
77    # remove old summary files
78    for ($loop_summary, $peer_summary, $clock_summary) { unlink $_ if -f $_ };
79
80    my $date;
81    for (@loopfiles) {
82        $date = $_; $date =~ s/.*($log_date_pattern)$/$1/;
83        if ($date ge $startdate && $date le $enddate) {
84            do_loop($statsdir, $_, $loop_summary);
85        }
86    }
87
88    for (@peerfiles) {
89        $date = $_; $date =~ s/.*($log_date_pattern)$/$1/;
90        if ($date ge $startdate && $date le $enddate) {
91            do_peer($statsdir, $_, $peer_summary);
92        }
93    }
94
95    for (@clockfiles) {
96        $date = $_; $date =~ s/.*($log_date_pattern)$/$1/;
97        if ($date ge $startdate && $date le $enddate) {
98            do_clock($statsdir, $_, $clock_summary);
99        }
100    }
101
102    print STDERR "Creating peer summary with limit $peer_dist_limit\n";
103    peer_summary($peer_summary) if (-f $peer_summary);
104}
105
106sub min
107{
108    my ($result, @rest) = @_;
109    map { $result = $_ if ($_ < $result) } @rest;
110    return($result);
111}
112
113sub max
114{
115    my ($result, @rest) = @_;
116    map { $result = $_ if ($_ > $result) } @rest;
117    return($result);
118}
119
120# calculate mean, range, and standard deviation for offset and frequency
121sub do_loop
122{
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);
131    my $loop_count = 0;
132    my $loop_time = 0;
133    my $loop_freq = 0;
134    my ($freq, $offs);
135    my @Fld;
136    while (<INPUT>) {
137	chop;	# strip record separator
138	@Fld = split;
139	next if ($#Fld < 4);
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";
146	    next
147	}
148	$loop_count++;
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);
154	$loop_time += $offs;
155	$loop_time_rms += $offs * $offs;
156	$loop_freq += $freq;
157	$loop_freq_rms += $freq * $freq;
158    }
159    close INPUT;
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";
165	    $loop_time_rms = 0;
166	}
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";
172	    $loop_freq_rms = 0;
173	}
174	$loop_freq_rms = sqrt($loop_freq_rms);
175	printf OUTPUT
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,
180	     $loop_freq_rms);
181    }
182    else {
183	warn "no valid lines in $directory/$fname";
184    }
185    close OUTPUT
186}
187
188# calculate mean, standard deviation, maximum offset, mean dispersion,
189# and maximum distance for each peer
190sub do_peer
191{
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);
200    my %peer_time;
201    my %peer_time_rms;
202    my %peer_count;
203    my %peer_delay;
204    my %peer_disp;
205    my %peer_dist;
206    my %peer_ident;
207    my %peer_tmin;
208    my %peer_tmax;
209    my @Fld;
210    my ($i, $j);
211    my ($dist, $offs);
212    while (<INPUT>) {
213	chop;	# strip record separator
214	@Fld = split;
215	next if ($#Fld < 6);
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
220
221	$dist = $Fld[6] + $Fld[5] / 2;
222	next if ($dist > $MAXDISTANCE);
223	$offs = $Fld[4];
224	if ($offs > $skip_time_steps || $offs < -$skip_time_steps) {
225	    warn "ignoring peer offset $offs (file $fname, line $.)\n";
226	    next
227	}
228	$i = $n;
229	for ($j = 0; $j < $n; $j++) {
230	    if ($Fld[2] eq $peer_ident{$j}) {
231		$i = $j;		# peer found
232		last;
233	    }
234	}
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;
241	    $peer_count{$i} = 0;
242	    $n++;
243	}
244	$peer_count{$i}++;
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];
252    }
253    close INPUT;
254    print OUTPUT
255"       ident     cnt     mean     rms      max     delay     dist     disp\n";
256    print OUTPUT
257"==========================================================================\n";
258    my @lines = ();
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};
271	}
272	push @lines, sprintf
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;
277    }
278    print OUTPUT sort @lines;
279    close OUTPUT;
280}
281
282sub do_clock
283{
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";
289    close INPUT;
290    close OUTPUT;
291}
292
293sub peer_summary
294{
295    my $in_file = shift;
296    my ($i, $j, $n);
297    my (%peer_ident, %peer_count, %peer_mean, %peer_var, %peer_max);
298    my (%peer_1, %peer_2, %peer_3, %peer_4);
299    my $dist;
300    my $max;
301    open INPUT, "<$in_file" or die "can't open $in_file: $!";
302    my @Fld;
303    $n = 0;
304    while (<INPUT>) {
305	chop;	# strip record separator
306	@Fld = split;
307	next if ($#Fld < 7 || $Fld[0] eq 'ident');
308	$i = $n;
309	for ($j = 0; $j < $n; $j++) {
310	    if ($Fld[0] eq $peer_ident{$j}) {
311		$i = $j;
312		last;			# peer found
313	    }
314	}
315	if ($i == $n) {			# add new peer
316	    $peer_count{$i} = $peer_mean{$i} = $peer_var{$i} = 0;
317	    $peer_max{$i} = 0;
318 	    $peer_1{$i} = $peer_2{$i} = $peer_3{$i} = $peer_4{$i} = 0;
319	    $peer_ident{$i} = $Fld[0];
320	    ++$n;
321	}
322	$dist = $Fld[6] - $Fld[5] / 2;
323	if ($dist < $peer_dist_limit) {
324	    $peer_count{$i}++;
325	    $peer_mean{$i} += $Fld[2];
326	    $peer_var{$i} += $Fld[3] * $Fld[3];
327	    $max = $Fld[4];
328	    $peer_max{$i} = max($peer_max{$i}, $max);
329	    if ($max > 1) {
330		$peer_1{$i}++;
331		if ($max > 5) {
332		    $peer_2{$i}++;
333		    if ($max > 10) {
334			$peer_3{$i}++;
335			if ($max > 50) {
336			    $peer_4{$i}++;
337			}
338		    }
339		}
340	    }
341	}
342	else {
343	    warn "dist exceeds limit: $dist (file $in_file, line $.)\n";
344	}
345    }
346    close INPUT;
347    my @lines = ();
348    print
349	"       host     days    mean       rms       max   >1  >5 >10 >50\n";
350    print
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 $@;
358	push @lines, sprintf
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};
362    }
363    print sort @lines;
364}
365
366@summary_opts@
367
3681;
369__END__
370