#!/usr/bin/perl -w require 5.003; use strict; # plots traces labeled with label_results # see also: label_results, plot_traces if (`pwd` =~ 'ReportGen') { use lib '..'; } use FileHandle; #use HTML::Entities; use ReportGen::Opts; use ReportGen::Globs; use ReportGen::ObjDbase; use ReportGen::ObjManip; use ReportGen::RepFormat; # Configuration my %Opts = ( trace_plotter => undef(), trace_plotter_opts => undef(), load_balancer => undef(), ); my %SavedOpts = %Opts; my @BaseTypes = qw( hit miss ims.sc200 ims.sc304 redired_req rep_to_redir head post put abort cachable uncachable fill basic ims reload rep ); # globals my @Labels; &init(); exit(&main()); sub main { if ($Opts{load_balancer}) { @BaseTypes = grep { !/^(hit|miss|fill)$/ } @BaseTypes; } foreach my $label (@Labels) { makeReport($label); } return 0; } sub makeReport { my $label = &curLabel(shift); &clearObjects(); my $stats = &ReadDbase(&FullInFName($label, "clt.All.lx")); &importObjects($stats); my $facts = &ReadDbase(&FullInFName($label, "facts.lx"), 'may fail'); &importObjects($facts) if $facts; &checkObjects(); my $fname = &FullOutFName($label, 'index', '.html'); my $hname = label2hname($label); &openReport($fname, $hname); &reportAggrStats(); &reportTraces(); #&reportDistrib(); &closeReport(); warn("$0: local report URL is file:$fname\n"); &curLabel(undef()); } sub reportAggrStats { &openSection('Executive summary'); &reportExSummary(); &closeSection(); &openSection('Engineer summary'); print($OFile &substNamesInHTML('

[log.count||"Unknown number of"] logs were used to generate this report.

')); &reportTput(); &reportHits() unless $Opts{load_balancer}; &reportChb(); &reportTimes(); &reportWait(); &reportStreams(); &reportConns(); &reportSizes(); &reportClasses(); &reportErrors(); # print some warnings if needed my @wobjs = (&getWarnObjs()); if (grep { $_->{warn_txt} } @wobjs) { my $hasDiff = 0; print($OFile "

Potential problems:\n

    \n"); foreach my $obj (@wobjs) { next unless $obj->{warn_txt}; printf($OFile "\t
  1. %s\n", $obj->{warn_key}, $obj->{warn_txt}); $hasDiff ||= $obj->{warn_txt} =~ /\bdiffers\b/; } print($OFile "
\n"); print($OFile "\n") if $hasDiff; } &closeSection(); } sub reportExSummary { my $html = <
Throughput:[rep.rate]rep/sec
Response time:[rep.rptm.mean]msec
- misses:[miss.rptm.mean]msec
- hits:[hit.rptm.mean]msec
Hit Ratio:[hit.ratio.obj]%
Errors:[err_xact.ratio]%
Duration:[duration/3600]hour

Phases: [name]

HTML if ($Opts{load_balancer}) { $html =~ s|^.*misses:.*$||m; $html =~ s|^.*hits:.*$||m; $html =~ s|^.*Hit Ratio:.*$||m; } print($OFile &substNamesInHTML($html)); } sub reportTput { my $html = <
TABLE if (&objVal('_have_icp_stats')) { $html .= < TABLE } $html .= <

TABLE print($OFile &substNamesInHTML($html)); } sub reportHits { my $html = <
Load Count
(xact/sec)
Volume
(Mbit/sec)
Offered: [req.rate] [req.bw/$Mbit]
Measured: [rep.rate] [rep.bw/$Mbit]
ICP reqs: [icp.req.rate] [icp.req.bw/$Mbit]
ICP reps: [icp.rep.rate] [icp.rep.bw/$Mbit]
TABLE if (&objVal('_have_icp_stats')) { $html .= < TABLE } $html .= <

TABLE print($OFile &substNamesInHTML($html)); } sub reportChb { my $html = <
Hit Ratios DHR
(%)
BHR
(%)
Offered: [offered.hit.ratio.obj] [offered.hit.ratio.byte]
Measured: [hit.ratio.obj] [hit.ratio.byte]
ICP: [icp.hit.ratio.obj] [icp.hit.ratio.byte]
Cachability Ratios Count
(%)
Volume
(%)
Measured: [cachable.ratio.obj] [cachable.ratio.byte]

TABLE print($OFile &substNamesInHTML($html)); } sub reportTimes { my @types = ( 'byte', @BaseTypes ); my @measures = qw( min median mean max ); push @types, map { "icp.$_" } qw ( hit miss rep ) if &objVal('_have_icp_stats'); print $OFile <
TABLE my $html = ''; foreach my $type (@types) { $html .= "\n"; foreach my $meas (@measures) { if ( $type eq 'byte' && $meas ne 'mean' ){ $html .= "\t"; } else { $html .= "\t\n"; } } $html .= "\n"; } print($OFile &substNamesInHTML($html)); print $OFile <

TABLE } sub reportWait { my $html = <
Response Times Response Time (msec)
Min Median Mean Max
$type [$type.rptm.$meas]
Wait Queue requests
Enqueued:[wait.started]
Dequeued:[wait.finished]
Average length:[wait.level.mean]

TABLE print($OFile &substNamesInHTML($html)); } sub reportStreams { &reportStreamRates(); &reportStreamTotals(); } sub reportStreamRates { my $html = <
TABLE my @types = @BaseTypes; push @types, map { "icp.$_" } qw ( hit miss rep ) if &objVal('_have_icp_stats'); for my $type (@types) { $html .= " \n"; } $html .= '
Stream Rates Count
(rep/sec)
Volume
(Mbit/sec)
$type [$type.rate] [$type.bw/$Mbit]

'; print($OFile &substNamesInHTML($html)); } sub reportStreamTotals { my $html = <
TABLE my @types = @BaseTypes; push @types, map { "icp.$_" } qw ( hit miss rep ) if &objVal('_have_icp_stats'); for my $type (@types) { $html .= " \n"; } $html .= '
Stream Totals Count
(rep*106)
Volume
(GByte)
$type [$type.size.count/1e6] [$type.size.sum/$GByte]

'; print($OFile &substNamesInHTML($html)); } sub reportConns { my $html = <
Connection Length Min Mean Max
Use (xact/conn) [conn.use.min] [conn.use.mean] [conn.use.max]
Life time (msec) [conn.ttl.min] [conn.ttl.mean] [conn.ttl.max]

TABLE print($OFile &substNamesInHTML($html)); } sub reportSizes { my @types = @BaseTypes; my @measures = qw( min median mean max ); push @types, map { "icp.$_" } qw ( hit miss rep ) if &objVal('_have_icp_stats'); print $OFile <
TABLE my $html = ''; foreach my $type (@types) { $html .= "\n"; foreach my $meas (@measures) { $html .= "\t\n"; } $html .= "\n"; } print($OFile &substNamesInHTML($html)); print $OFile <

TABLE } sub reportClasses { my @types = @BaseTypes; my @measures = qw( obj byte ); print $OFile <
Object Sizes Size (KB)
Min Median Mean Max
$type[$type.size.$meas/1024]
TABLE my $html = ''; foreach my $type (@types) { $html .= "\n"; foreach my $meas (@measures) { $html .= "\t\n"; } $html .= "\n"; } print($OFile &substNamesInHTML($html)); print $OFile <

TABLE } sub reportErrors { if (&objVal('_have_icp_stats')) { print($OFile &substNamesInHTML( "

ICP timeouts: [icp.timeout.count]". " ([icp.timeout.ratio]% of all ICP requests)

\n" )); } # print errors table my $tbl = &getTable('errors.tbl'); if (defined $tbl) { my $lines = $tbl->{lines}; if ($lines && @{$lines}) { print($OFile &substNamesInHTML( "

Errors ([err_xact.ratio]% of all transactions):\n

"));

			foreach my $line (@{$lines}) {
				print($OFile $line);
			}
			print($OFile "
\n"); } } } sub checkObjects { my $diffp; $diffp = 2; if (&exprDiffer('rep.rate', 'req.rate', \$diffp)) { &setWarn('rep.rate', 'suspecious reply rate', "Reply rate ([rep.rate]/sec) differs from the request rate ([req.rate]/sec) by [$diffp]%."); } if ($Opts{load_balancer}) { if (&objVal('_have_cache_stats')) { &setWarn('rep.rate', 'LB cached data', 'The report was configured for a no-caching load balancing test, but logs indicate presence of [hit.size.count] hits ([hit.ratio.obj]% DHR).'); } } else { $diffp = 3; if (&exprDiffer('hit.ratio.obj', 'offered.hit.ratio.obj', \$diffp)) { &setWarn('hit.ratio.obj', 'suspecious DHR', "Measured document hit ratio ([hit.ratio.obj]%) differs from the offered DHR ([offered.hit.ratio.obj]%) by [$diffp]%."); } $diffp = 5; if (&exprDiffer('hit.ratio.byte', 'offered.hit.ratio.byte', \$diffp)) { &setWarn('hit.ratio.byte', 'suspecious BHR', "Measured byte hit ratio ([hit.ratio.byte]%) differs from the offered BHR ([offered.hit.ratio.byte]%) by [$diffp]%."); } $diffp = 10; if (&exprDiffer('hit.rptm.mean', 'miss.rptm.mean', \$diffp) && $diffp > 0) { &setWarn('hit.rptm.mean', 'hit rptm too high', "Hit response time ([hit.rptm.mean]s msec) is [$diffp]% higher than miss response time ([miss.rptm.mean] msec)."); } } # $diffp = 0.01; # my $wr = '100*wait.started/xact.started'; # if (&computeExpr("$wr > $diffp")) { # &setWarn('wait.started', 'too many queued requests', # "Quite a few ([$wr]% > $diffp%) requests # got queued waiting for resources."); # } $diffp = 0.1; if (&checkObj('err_xact.ratio') && &objVal('err_xact.ratio') > $diffp) { &setWarn('err_xact.ratio', 'too many errors', "Error ratio ([err_xact.ratio]%) exceeds [$diffp]%."); } my $minDur = $Opts{load_balancer} ? (1*3600) : (4*3600); # secs if (checkObj('duration') && &objVal('duration') < $minDur) { &setWarn('duration', 'too small sample', "Experiment duration ([duration/60] minutes) is less than [$minDur/60] minutes and may be too short."); } } sub reportTraces { &openSection('Traces'); my @rptmTrace = $Opts{load_balancer} ? qw( rep.rptm.mean ) : qw( miss.rptm.mean rep.rptm.mean hit.rptm.mean ); &traceSmth('rates', [qw( req.rate rep.rate )]); &traceSmth('rptms', [@rptmTrace]); &traceSmth('byte_rptms', [qw( byte.rptm.mean )]); &traceSmth('ratios', [qw( cachable.ratio.obj offered.hit.ratio.obj hit.ratio.obj )]) unless $Opts{load_balancer}; &traceSmth('conn_rates', [qw( conn.open.rate conn.estb.rate )]); &traceSmth('xact_lvl', [qw( xact.level.mean wait.level.mean )]); &traceSmth('conn_lvl', [qw( conn.estb.level.mean conn.open.level.mean )]); &traceSmth('errors',[qw( err_xact.ratio err_xact.count )]); &traceSmth('many', [qw( req.rate rep.rate rep.rptm.mean conn.open.rate conn.estb.rate)]); if (&objVal('_have_icp_stats')) { &traceSmth('icp_rates', [ map { "icp.$_" } qw( req.rate rep.rate )]); &traceSmth('icp_rptms', [ map { "icp.$_" } qw( miss.rptm.mean rep.rptm.mean hit.rptm.mean )]); &traceSmth('icp_touts', [ map { "icp.$_" } qw( timeout.count )]); } &closeSection(); } sub traceSmth { my ($name, $objects, $options) = @_; $options = $Opts{trace_plotter_opts} unless defined $options; $options = '' unless defined $options; $options .= ' --plot_size 1,0.85' unless index('--plot_size ', $options) >= 0; my $fname = "$name.png"; my $label = &curLabel(); my $fullFName = &FullOutFName($label, $fname); &System(sprintf("%s %s --out_name %s %s", $Opts{trace_plotter}, $options, $fullFName, join(' ', map { "$label:$_" } @{$objects}))); printf($OFile '

', $fname); } sub init { die(&usage()) unless &ParseOpts(\%Opts, \@Labels, @ARGV) && @Labels; $Opts{load_balancer} = defined $Opts{load_balancer} && $Opts{load_balancer} =~ /^(yes|1)$/; } sub usage { return "usage: $0 [options]
Object Class Contribution (%)
Count Volume
$type[$type.ratio.$meas]