/contrib/ntp/scripts/summary.in

https://bitbucket.org/freebsd/freebsd-head/ · Autoconf · 373 lines · 316 code · 22 blank · 35 comment · 52 complexity · f112a8973810d3641853195aaf9712e1 MD5 · raw file

  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.
  21. require 5.003; # "never tested with any other version of Perl"
  22. use strict;
  23. use Getopt::Long;
  24. my $log_date_pattern = '[12]\d{3}[01]\d[0-3]\d';
  25. my $statsdir = "/var/log/ntp"; # directory with input files
  26. my $outputdir = "/tmp"; # directory for output files
  27. my $skip_time_steps = 3600.0; # ignore time offsets larger that this
  28. my $startdate = "19700101"; # first data file to use (YYYYMMDD)
  29. my $enddate=`date -u +%Y%m%d`; chomp $enddate; --$enddate;
  30. my $peer_dist_limit = 400.0;
  31. my %options = ("directory|input-directory=s" => \$statsdir,
  32. "output-directory=s" => \$outputdir,
  33. "skip-time-steps:f" => \$skip_time_steps,
  34. "start-date=s" => \$startdate,
  35. "end-date=s" => \$enddate,
  36. "peer-dist-limit=f" => \$peer_dist_limit);
  37. if ( !GetOptions(%options) )
  38. {
  39. print STDERR "valid options for $0 are:\n";
  40. my $opt;
  41. foreach $opt (sort(keys %options)) {
  42. print STDERR "\t--$opt\t(default is ";
  43. if ( ref($options{$opt}) eq "ARRAY" ) {
  44. print STDERR join(", ", map { "'$_'" } @{$options{$opt}});
  45. } else {
  46. print STDERR "'${$options{$opt}}'";
  47. }
  48. print STDERR ")\n";
  49. }
  50. print STDERR "\n";
  51. die;
  52. }
  53. # check possibly current values of options
  54. die "$statsdir: no such directory" unless (-d $statsdir);
  55. die "$outputdir: no such directory" unless (-d $outputdir);
  56. die "$skip_time_steps: skip-time-steps must be positive"
  57. unless ($skip_time_steps >= 0.0);
  58. die "$startdate: invalid start date|$`|$&|$'"
  59. unless ($startdate =~ m/.*$log_date_pattern$/);
  60. die "$enddate: invalid end date"
  61. unless ($enddate =~ m/.*$log_date_pattern$/);
  62. $skip_time_steps = 0.128 if ($skip_time_steps == 0);
  63. sub min
  64. {
  65. my ($result, @rest) = @_;
  66. map { $result = $_ if ($_ < $result) } @rest;
  67. return($result);
  68. }
  69. sub max
  70. {
  71. my ($result, @rest) = @_;
  72. map { $result = $_ if ($_ > $result) } @rest;
  73. return($result);
  74. }
  75. # calculate mean, range, and standard deviation for offset and frequency
  76. sub do_loop
  77. {
  78. my ($directory, $fname, $out_file) = @_;
  79. print "$directory/$fname\n";
  80. open INPUT, "$directory/$fname" or warn "can't open $directory/$fname: $!";
  81. open OUTPUT, ">>$out_file" or die "can't open $out_file: $!";
  82. print OUTPUT "$fname\n";
  83. my ($loop_tmax, $loop_fmax) = (-1e9, -1e9);
  84. my ($loop_tmin, $loop_fmin) = (1e9, 1e9);
  85. my ($loop_time_rms, $loop_freq_rms) = (0, 0);
  86. my $loop_count = 0;
  87. my $loop_time = 0;
  88. my $loop_freq = 0;
  89. my ($freq, $offs);
  90. my @Fld;
  91. while (<INPUT>) {
  92. chop; # strip record separator
  93. @Fld = split;
  94. next if ($#Fld < 4);
  95. #NTPv3: 50529 74356.259 -0.000112 16.1230 8
  96. #NTPv3: day, sec.msec, offset, drift_comp, sys_poll
  97. #NTPv4: 51333 54734.582 0.000001648 16.981964 0.000001094 0.020938 6
  98. #NTPv4: day, sec.msec, offset, drift_comp, sys_error, clock_stabil, sys_poll
  99. if ($Fld[2] > $skip_time_steps || $Fld[2] < -$skip_time_steps) {
  100. warn "ignoring loop offset $Fld[2] (file $fname, line $.)\n";
  101. next
  102. }
  103. $loop_count++;
  104. ($offs, $freq) = ($Fld[2], $Fld[3]);
  105. $loop_tmax = max($loop_tmax, $offs);
  106. $loop_tmin = min($loop_tmin, $offs);
  107. $loop_fmax = max($loop_fmax, $freq);
  108. $loop_fmin = min($loop_fmin, $freq);
  109. $loop_time += $offs;
  110. $loop_time_rms += $offs * $offs;
  111. $loop_freq += $freq;
  112. $loop_freq_rms += $freq * $freq;
  113. }
  114. close INPUT;
  115. if ($loop_count > 1) {
  116. $loop_time /= $loop_count;
  117. $loop_time_rms = $loop_time_rms / $loop_count - $loop_time * $loop_time;
  118. if ($loop_time_rms < 0) {
  119. warn "loop_time_rms: $loop_time_rms < 0";
  120. $loop_time_rms = 0;
  121. }
  122. $loop_time_rms = sqrt($loop_time_rms);
  123. $loop_freq /= $loop_count;
  124. $loop_freq_rms = $loop_freq_rms / $loop_count - $loop_freq * $loop_freq;
  125. if ($loop_freq_rms < 0) {
  126. warn "loop_freq_rms: $loop_freq_rms < 0";
  127. $loop_freq_rms = 0;
  128. }
  129. $loop_freq_rms = sqrt($loop_freq_rms);
  130. printf OUTPUT
  131. ("loop %d, %.0f+/-%.1f, rms %.1f, freq %.2f+/-%0.3f, var %.3f\n",
  132. $loop_count, ($loop_tmax + $loop_tmin) / 2 * 1e6,
  133. ($loop_tmax - $loop_tmin) / 2 * 1e6, $loop_time_rms * 1e6,
  134. ($loop_fmax + $loop_fmin) / 2, ($loop_fmax - $loop_fmin) / 2,
  135. $loop_freq_rms);
  136. }
  137. else {
  138. warn "no valid lines in $directory/$fname";
  139. }
  140. close OUTPUT
  141. }
  142. # calculate mean, standard deviation, maximum offset, mean dispersion,
  143. # and maximum distance for each peer
  144. sub do_peer
  145. {
  146. my ($directory, $fname, $out_file) = @_;
  147. print "$directory/$fname\n";
  148. open INPUT, "$directory/$fname" or warn "can't open $directory/$fname: $!";
  149. open OUTPUT, ">>$out_file" or die "can't open $out_file: $!";
  150. print OUTPUT "$fname\n";
  151. # we toss out all distances greater than one second on the assumption the
  152. # peer is in initial acquisition
  153. my ($n, $MAXDISTANCE) = (0, 1.0);
  154. my %peer_time;
  155. my %peer_time_rms;
  156. my %peer_count;
  157. my %peer_delay;
  158. my %peer_disp;
  159. my %peer_dist;
  160. my %peer_ident;
  161. my %peer_tmin;
  162. my %peer_tmax;
  163. my @Fld;
  164. my ($i, $j);
  165. my ($dist, $offs);
  166. while (<INPUT>) {
  167. chop; # strip record separator
  168. @Fld = split;
  169. next if ($#Fld < 6);
  170. #NTPv3: 50529 83316.249 127.127.8.1 9674 0.008628 0.00000 0.00700
  171. #NTPv3: day, sec.msec, addr, status, offset, delay, dispersion
  172. #NTPv4: 51333 56042.037 127.127.8.1 94f5 -0.000014657 0.000000000 0.000000000 0.000013214
  173. #NTPv4: day, sec.msec, addr, status, offset, delay, dispersion, skew
  174. $dist = $Fld[6] + $Fld[5] / 2;
  175. next if ($dist > $MAXDISTANCE);
  176. $offs = $Fld[4];
  177. if ($offs > $skip_time_steps || $offs < -$skip_time_steps) {
  178. warn "ignoring peer offset $offs (file $fname, line $.)\n";
  179. next
  180. }
  181. $i = $n;
  182. for ($j = 0; $j < $n; $j++) {
  183. if ($Fld[2] eq $peer_ident{$j}) {
  184. $i = $j; # peer found
  185. last;
  186. }
  187. }
  188. if ($i == $n) { # add new peer
  189. $peer_ident{$i} = $Fld[2];
  190. $peer_tmax{$i} = $peer_dist{$i} = -1e9;
  191. $peer_tmin{$i} = 1e9;
  192. $peer_time{$i} = $peer_time_rms{$i} = 0;
  193. $peer_delay{$i} = $peer_disp{$i} = 0;
  194. $peer_count{$i} = 0;
  195. $n++;
  196. }
  197. $peer_count{$i}++;
  198. $peer_tmax{$i} = max($peer_tmax{$i}, $offs);
  199. $peer_tmin{$i} = min($peer_tmin{$i}, $offs);
  200. $peer_dist{$i} = max($peer_dist{$i}, $dist);
  201. $peer_time{$i} += $offs;
  202. $peer_time_rms{$i} += $offs * $offs;
  203. $peer_delay{$i} += $Fld[5];
  204. $peer_disp{$i} += $Fld[6];
  205. }
  206. close INPUT;
  207. print OUTPUT
  208. " ident cnt mean rms max delay dist disp\n";
  209. print OUTPUT
  210. "==========================================================================\n";
  211. my @lines = ();
  212. for ($i = 0; $i < $n; $i++) {
  213. next if $peer_count{$i} < 2;
  214. $peer_time{$i} /= $peer_count{$i};
  215. eval { $peer_time_rms{$i} = sqrt($peer_time_rms{$i} / $peer_count{$i} -
  216. $peer_time{$i} * $peer_time{$i}); };
  217. $peer_time_rms{$i} = 0, warn $@ if $@;
  218. $peer_delay{$i} /= $peer_count{$i};
  219. $peer_disp{$i} /= $peer_count{$i};
  220. $peer_tmax{$i} = $peer_tmax{$i} - $peer_time{$i};
  221. $peer_tmin{$i} = $peer_time{$i} - $peer_tmin{$i};
  222. if ($peer_tmin{$i} > $peer_tmax{$i}) { # can this happen at all?
  223. $peer_tmax{$i} = $peer_tmin{$i};
  224. }
  225. push @lines, sprintf
  226. "%-15s %4d %8.3f %8.3f %8.3f %8.3f %8.3f %8.3f\n",
  227. $peer_ident{$i}, $peer_count{$i}, $peer_time{$i} * 1e3,
  228. $peer_time_rms{$i} * 1e3, $peer_tmax{$i} * 1e3,
  229. $peer_delay{$i} * 1e3, $peer_dist{$i} * 1e3, $peer_disp{$i} * 1e3;
  230. }
  231. print OUTPUT sort @lines;
  232. close OUTPUT;
  233. }
  234. sub do_clock
  235. {
  236. my ($directory, $fname, $out_file) = @_;
  237. print "$directory/$fname\n";
  238. open INPUT, "$directory/$fname";
  239. open OUTPUT, ">>$out_file" or die "can't open $out_file: $!";
  240. print OUTPUT "$fname\n";
  241. close INPUT;
  242. close OUTPUT;
  243. }
  244. sub peer_summary
  245. {
  246. my $in_file = shift;
  247. my ($i, $j, $n);
  248. my (%peer_ident, %peer_count, %peer_mean, %peer_var, %peer_max);
  249. my (%peer_1, %peer_2, %peer_3, %peer_4);
  250. my $dist;
  251. my $max;
  252. open INPUT, "<$in_file" or die "can't open $in_file: $!";
  253. my @Fld;
  254. $n = 0;
  255. while (<INPUT>) {
  256. chop; # strip record separator
  257. @Fld = split;
  258. next if ($#Fld < 7 || $Fld[0] eq 'ident');
  259. $i = $n;
  260. for ($j = 0; $j < $n; $j++) {
  261. if ($Fld[0] eq $peer_ident{$j}) {
  262. $i = $j;
  263. last; # peer found
  264. }
  265. }
  266. if ($i == $n) { # add new peer
  267. $peer_count{$i} = $peer_mean{$i} = $peer_var{$i} = 0;
  268. $peer_max{$i} = 0;
  269. $peer_1{$i} = $peer_2{$i} = $peer_3{$i} = $peer_4{$i} = 0;
  270. $peer_ident{$i} = $Fld[0];
  271. ++$n;
  272. }
  273. $dist = $Fld[6] - $Fld[5] / 2;
  274. if ($dist < $peer_dist_limit) {
  275. $peer_count{$i}++;
  276. $peer_mean{$i} += $Fld[2];
  277. $peer_var{$i} += $Fld[3] * $Fld[3];
  278. $max = $Fld[4];
  279. $peer_max{$i} = max($peer_max{$i}, $max);
  280. if ($max > 1) {
  281. $peer_1{$i}++;
  282. if ($max > 5) {
  283. $peer_2{$i}++;
  284. if ($max > 10) {
  285. $peer_3{$i}++;
  286. if ($max > 50) {
  287. $peer_4{$i}++;
  288. }
  289. }
  290. }
  291. }
  292. }
  293. else {
  294. warn "dist exceeds limit: $dist (file $in_file, line $.)\n";
  295. }
  296. }
  297. close INPUT;
  298. my @lines = ();
  299. print
  300. " host days mean rms max >1 >5 >10 >50\n";
  301. print
  302. "==================================================================\n";
  303. for ($i = 0; $i < $n; $i++) {
  304. next if ($peer_count{$i} < 2);
  305. $peer_mean{$i} /= $peer_count{$i};
  306. eval { $peer_var{$i} = sqrt($peer_var{$i} / $peer_count{$i} -
  307. $peer_mean{$i} * $peer_mean{$i}); };
  308. $peer_var{$i} = 0, warn $@ if $@;
  309. push @lines, sprintf
  310. "%-15s %3d %9.3f% 9.3f %9.3f %3d %3d %3d %3d\n",
  311. $peer_ident{$i}, $peer_count{$i}, $peer_mean{$i}, $peer_var{$i},
  312. $peer_max{$i}, $peer_1{$i}, $peer_2{$i}, $peer_3{$i}, $peer_4{$i};
  313. }
  314. print sort @lines;
  315. }
  316. my $loop_summary="$outputdir/loop_summary";
  317. my $peer_summary="$outputdir/peer_summary";
  318. my $clock_summary="$outputdir/clock_summary";
  319. my (@loopfiles, @peerfiles, @clockfiles);
  320. print STDERR "Creating summaries from $statsdir ($startdate to $enddate)\n";
  321. opendir SDIR, $statsdir or die "directory ${statsdir}: $!";
  322. rewinddir SDIR;
  323. @loopfiles=sort grep /loop.*$log_date_pattern/, readdir SDIR;
  324. rewinddir SDIR;
  325. @peerfiles=sort grep /peer.*$log_date_pattern/, readdir SDIR;
  326. rewinddir SDIR;
  327. @clockfiles=sort grep /clock.*$log_date_pattern/, readdir SDIR;
  328. closedir SDIR;
  329. # remove old summary files
  330. map { unlink $_ if -f $_ } ($loop_summary, $peer_summary, $clock_summary);
  331. my $date;
  332. map {
  333. $date = $_; $date =~ s/.*($log_date_pattern)$/$1/;
  334. if ($date ge $startdate && $date le $enddate) {
  335. do_loop $statsdir, $_, $loop_summary;
  336. }
  337. } @loopfiles;
  338. map {
  339. $date = $_; $date =~ s/.*($log_date_pattern)$/$1/;
  340. if ($date ge $startdate && $date le $enddate) {
  341. do_peer $statsdir, $_, $peer_summary;
  342. }
  343. } @peerfiles;
  344. map {
  345. $date = $_; $date =~ s/.*($log_date_pattern)$/$1/;
  346. if ($date ge $startdate && $date le $enddate) {
  347. do_clock $statsdir, $_, $clock_summary;
  348. }
  349. } @clockfiles;
  350. print STDERR "Creating peer summary with limit $peer_dist_limit\n";
  351. peer_summary $peer_summary if (-f $peer_summary);