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