Created
May 12, 2019 05:01
-
-
Save praveenperera/6ad456936350bc6f43450265ea4a1cb0 to your computer and use it in GitHub Desktop.
tsung_stats.pl
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/perl -w | |
# -*- Mode: CPerl -*- | |
# | |
# This code was developped by IDEALX (http://IDEALX.org/) and | |
# contributors (their names can be found in the CONTRIBUTORS file). | |
# Copyright (C) 2000-2004 IDEALX | |
# Copyright (C) 2005-2011 Nicolas Niclausse | |
# | |
# This program is free software; you can redistribute it and/or modify | |
# it under the terms of the GNU General Public License as published by | |
# the Free Software Foundation; either version 2 of the License, or | |
# (at your option) any later version. | |
# | |
# This program is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
# GNU General Public License for more details. | |
# | |
# You should have received a copy of the GNU General Public License | |
# along with this program; if not, write to the Free Software | |
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. | |
# Version: $Id$ | |
# purpose: quick and dirty ugly hack to compute stats and plots graph | |
# given a (set of) log file(s) from the tsung tool. | |
# dygraphs fonctionnality added by Bearstech (http://bearstech.com) | |
# using dygraphs JavaScript Visualization Library (http://dygraphs.com) | |
use strict; | |
use Getopt::Long; | |
use vars qw ($help @files $dygraph $verbose $debug $noplot $noextra $version $stats | |
$template_dir $nohtml $template_dir $gnuplot $logy $rotate_xtics | |
$imgfmt $newgnuplot $report_title ); | |
use File::Spec::Functions qw(rel2abs); | |
use File::Basename; | |
use File::Copy; | |
my $tagvsn = '1.7.0'; | |
GetOptions( "help",\$help, | |
"verbose",\$verbose, | |
"debug",\$debug, | |
"stats=s",\$stats, | |
"gnuplot=s",\$gnuplot, | |
"version",\$version, | |
"logy",\$logy, | |
"dygraph",\$dygraph, | |
"noplot",\$noplot, | |
"tdir=s",\$template_dir, | |
"nohtml",\$nohtml, | |
"rotate-xtics",\$rotate_xtics, | |
"noextra",\$noextra, | |
"img_format=s",\$imgfmt, | |
"title=s",\$report_title | |
); | |
&usage if $help or $Getopt::Long::error; | |
&version if $version; | |
my $extra = not $noextra; | |
my $match =0; | |
my $async =0; | |
my $errors =0; | |
my $maxval; | |
my $os_mon_other; | |
my $category; | |
my $CPU_MAX = 3200; # cpu usage should never be higher than 3200% (32 cores at 100%) | |
my $prefix ="/usr"; | |
unless ($template_dir) { | |
if (-d (dirname($0) . "/templates/")) { | |
$template_dir = dirname($0)."/templates/"; | |
} elsif (-d "$ENV{HOME}/.tsung/templates/") { | |
$template_dir = "$ENV{HOME}/.tsung/templates/"; | |
} elsif (-d "${prefix}/share/tsung/templates") { | |
$template_dir = "${prefix}/share/tsung/templates"; | |
} elsif (-d "/usr/share/tsung/templates") { | |
$template_dir = "/usr/share/tsung/templates"; | |
} elsif (-d "/usr/local/share/tsung/templates") { | |
$template_dir = "/usr/local/share/tsung/templates"; | |
} else { | |
warn "Can't find template directory !"; | |
} | |
} | |
$stats = "tsung.log" unless $stats; | |
die "The stats file ($stats) does not exist, abort !" unless -e $stats; | |
$report_title = "Tsung" unless $report_title; | |
$imgfmt = "png" unless $imgfmt; | |
my %imgfmt_list = ("png" => 1, "svg" => 1, "pdf" => 1, "ps" => 1); # hash of all the format we support to make search more efficient | |
die "Image format \"$imgfmt\" not supported" unless $imgfmt_list{"$imgfmt"}; | |
my $datadir = "data"; # all data files are created in this subdirectory | |
my $imgdir = "images"; # all data files are created in this subdirectory | |
my $gplotdir = "gnuplot_scripts"; # all data files are created in this subdirectory | |
my $csvdir = "csv_data"; # all data files are created in this subdirectory | |
my $http; # true if http. add status code graphs in the HTML output | |
my $bosh; # true if bosh. add bosh specific graphs in the HTML output | |
my @dydirs = ($csvdir); | |
my @gnuplotdirs=($gplotdir, $imgdir); | |
my @dirs = ($datadir); | |
push @dirs, @dydirs if $dygraph; | |
push @dirs, @gnuplotdirs if not $dygraph; | |
foreach my $dir (@dirs) { | |
unless (-d $dir) { | |
print "creating subdirectory $dir \n"; | |
mkdir "$dir" or die "can't create directory $dir"; | |
} | |
} | |
$gnuplot = "gnuplot" unless $gnuplot; | |
my $newgnuplot = is_newgnuplot($gnuplot); | |
$gnuplot .= " >> gnuplot.log 2>&1"; | |
&parse_stats_file($stats); | |
&html_report() unless $nohtml; | |
# returns 1 (=true) if gnuplot doesn't support the "set terminal png size x,y" specifications | |
sub is_newgnuplot { | |
# args | |
my $gnuplot = shift; | |
my $version = `$gnuplot -V`; | |
if ($version =~ m /gnuplot (\d+).(\d+) patchlevel (\d+)/) { | |
my ($major, $minor,$patchlevel) = ($1,$2,$3); | |
return 2 if $major >= 5; | |
return 0 if $major < 3; | |
return 0 if $minor <2; # does it work with gnuplot 4.1 or 4.0 ? we assume no. | |
return 1; | |
} else { | |
return 0; | |
} | |
} | |
sub gnuplot_has_cairo { | |
return (system("$gnuplot -e 'set term pngcairo' 2> /dev/null ") == 0) | |
} | |
# plot stats from file with gnuplot | |
sub plot_stats { | |
# args: | |
my $names = shift; # arrayref contaning data names | |
my $titles = shift; # arrayref contaning data titles | |
my $datatype = shift; # type of data (added in filenames) | |
my $timestamp = shift; | |
my $files = shift; # arrayref contaning data files | |
my $ylabel = shift; | |
my $logy = shift; # if true use logarithmic scale for Y axis | |
# local var | |
my $style = "linespoint"; # TODO: can be override in option | |
my $legend_loc = "key left top"; # TODO: can be override in option | |
my $output_type; | |
my $filename; # temporary var | |
my $thumbnail_size = 0.5; | |
if (scalar @{$files} == 0 ) { | |
warn "No data for $datatype\n"; | |
return; | |
} | |
my $has_pngcairo = &gnuplot_has_cairo(); | |
print STDERR "Using pngcairo support in gnuplot\n" if $verbose and $has_pngcairo; | |
$datatype = "unknown" unless $datatype; | |
open(GP,">$gplotdir/graphes-$datatype.gplot") or die "can't open graphes-$datatype.gplot: $!"; | |
select GP; | |
foreach my $output_fmt ($imgfmt, "tn.png") { | |
my $output_ext; | |
my $output_type; | |
if (($output_fmt eq "png") || ($output_fmt eq "tn.png")) { | |
$output_ext = "png"; | |
if ($has_pngcairo) { | |
$output_type = "pngcairo "; | |
} else { | |
$output_type = "png "; | |
} | |
} elsif ($output_fmt eq "ps") { | |
$output_type = "postscript color "; | |
$output_ext = "ps"; | |
} elsif ($output_fmt eq "pdf") { | |
$output_type = "pdf color "; | |
$output_ext = "pdf"; | |
} elsif ($output_fmt eq "svg") { | |
$output_type = "svg "; | |
$output_ext = "svg"; | |
} | |
# gnuplot styles and options | |
print "set size $thumbnail_size,$thumbnail_size\n" if (($output_fmt eq "tn.png") and !$newgnuplot); | |
print "set xtics rotate\n" if (($output_ext eq "png") and ($rotate_xtics)); | |
print "set style data $style\n"; | |
if (($output_fmt eq "tn.png") and $newgnuplot and ($output_type !~ /pngcairo/)) { | |
print "set terminal $output_type tiny size 320,240\n"; | |
} elsif (($output_fmt eq "tn.png") and $newgnuplot) { | |
print "set terminal $output_type size 320,240 font 'Verdana,7'\n"; | |
} elsif ((($output_ext eq "svg") or (($output_ext eq "png")) ) and $newgnuplot) { | |
print "set terminal $output_type size 1024,768 \n"; | |
} else { | |
print "set terminal $output_type\n"; | |
} | |
print "set grid\n"; | |
# remove border on top and right and set color to gray | |
print "set style line 11 lc rgb '#808080' lt 1\n"; | |
print "set border 3 back ls 11\n"; | |
print "set tics nomirror\n"; | |
print "set style line 1 lc rgb '#8b1a0e' pt 1 ps 1 lt 2 lw 2\n"; # --- red | |
print "set style line 2 lc rgb '#5e9c36' pt 6 ps 1 lt 2 lw 2\n"; # --- green | |
my $d; # temporary var (title) | |
foreach $d (0..$#{$names}) { | |
# gnuplot headings | |
if ($output_fmt eq "tn.png") { | |
print "set output \"$imgdir/graphes-$datatype-@{$names}[$d]_$output_fmt\"\n"; | |
} else { | |
print "set output \"$imgdir/graphes-$datatype-@{$names}[$d].$output_ext\"\n"; | |
} | |
print "set title \" @{$titles}[$d]\"\n"; | |
if ($timestamp) { | |
print "set xlabel \"unit = $timestamp sec \"\n"; | |
} else { | |
print "set xlabel \"time (sec of the running test)\"\n"; | |
} | |
print "set ylabel \"".$ylabel->[$d]."\"\n" if $ylabel->[$d]; | |
print "show title\n"; | |
print "set $legend_loc\n"; | |
print "set logscale y\n" if $logy; | |
print "plot "; | |
my $lineindex=0; | |
foreach $filename (@{$files}) { | |
$lineindex++; | |
print " \"$datadir/$filename\" using "; | |
# if $timestamp isn't defined, use the first column as timestamp | |
if ($timestamp) { | |
print $d+1 ; | |
} else { | |
print " 1:" .($d+2); | |
} | |
my $cur_title = $filename; | |
$cur_title =~ s/\.txt$//; | |
$cur_title =~ s/:os_mon//; | |
$cur_title =~ s/(_|@)/\\$1/g if ($newgnuplot > 1); | |
print " title '$cur_title' ls $lineindex" ; | |
print "," unless ($filename eq @{$files}[$#{$files}]); # unless last occurence | |
} | |
print "\n"; # plot done | |
} | |
} | |
close GP; | |
system("$gnuplot $gplotdir/graphes-$datatype.gplot") and warn "Error while running gnuplot: $!"; | |
} | |
# plot stats from file with dygraph | |
sub plot_stats_dygraph { | |
# args: | |
my $title = shift; # arrayref contaning data titles | |
my $datatype = shift; # type of data (added in filenames) | |
my $timestamp = shift; | |
my $files = shift; # arrayref contaning data files | |
my $ylabel = shift; | |
my $logy = shift; # if true use logarithmic scale for Y axis | |
# local var | |
my $filename; # temporary var | |
my $thumbnail_size = 0.5; | |
my $row; | |
my @rowdata; | |
my %info; | |
my $cur_title; #tmp var | |
my @fields; | |
my $time; | |
my $value; | |
my $count = 0; | |
if (scalar @{$files} == 0 ) { | |
warn "No data for $datatype\n"; | |
return; | |
} | |
$datatype = "unknown" unless $datatype; | |
my $d; # temporary var (title) | |
foreach $d (0..$#{$title}) { | |
#loop on all the kind of graph needed for a subject (transaction, user ...) | |
$info{'path'} = "$csvdir/graphes-$datatype-@{$title}[$d].csv"; | |
@rowdata = (); | |
#if ($timestamp) { | |
# print "set xlabel \"unit = $timestamp sec \"\n"; | |
#} else { | |
# print "set xlabel \"unit = sec \"\n"; | |
#} | |
#print "set ylabel \"".$ylabel->[$d]."\"\n" if $ylabel->[$d]; | |
foreach $filename (@{$files}) { | |
$cur_title = $filename; | |
$cur_title =~ s/\.txt$//; | |
$cur_title =~ s/:os_mon//; | |
open FILE, "$datadir/$filename"; | |
#we go throught the file in order to complete row | |
$count = 1; | |
$rowdata[0] = ["time"] if not defined $rowdata[0]; | |
push @{$rowdata[0]}, $cur_title; | |
#print "go for $cur_title\n"; | |
while (<FILE>) { | |
@fields = split(/ /); | |
($time, $value) = @fields[0,$d+1] if defined @fields[0,$d+1]; | |
chomp($value); | |
chomp($time); | |
#print "$time : $value \n"; | |
die if not defined $time ; | |
$rowdata[$count] = [$time] if not defined $rowdata[$count]; # new line | |
while (1) { #look for right time | |
my $actual = @{$rowdata[$count]}[0]; | |
chomp($actual); | |
die unless defined $actual; | |
if ($time == $actual) { | |
push @{$rowdata[$count]}, $value ; | |
$count++; | |
last; | |
} elsif ($time > $actual) { | |
push @{$rowdata[$count]}, "" ; | |
$count++; | |
} else { | |
last; | |
} | |
} | |
} | |
close FILE; #FIXME : ugly open and close multiple time | |
} | |
open CSV, ">$csvdir/graphes-$datatype-@{$title}[$d].csv" or die $!; | |
foreach $row (@rowdata) { | |
print CSV (join(",", @{$row})." \n"); | |
} | |
close CSV; | |
} | |
} | |
sub max { | |
my $value = shift; | |
my $oldvalue= shift; | |
return $value unless $oldvalue; | |
return $value if $oldvalue < $value; | |
return $oldvalue; | |
} | |
sub min { | |
my $value = shift; | |
my $oldvalue= shift; | |
return $value unless $oldvalue; | |
return $value if $oldvalue > $value; | |
return $oldvalue; | |
} | |
sub parse_stats_file { | |
my $file = shift; | |
my $data; | |
my $timestamp; | |
my $first_timestamp =0; | |
my $first_interval =0; | |
my $interval; | |
open (FILE,"<$file") or die "Can't open $file $!"; | |
while (<FILE>) { | |
if (/^stats: (\S+)\s+(.*)$/) { | |
my $type = $1; | |
my $values = $2; | |
$type =~ s/page_resptime/page/g; | |
$type =~ s/response_time/request/g; | |
# handle new format of ts_os_mon : reformat as old one | |
if ($type =~ m/os_mon/) { | |
$type =~ s/\{(\S+)\,\"(\S+)\"\}/$1:$2/g; | |
} else { | |
$type =~ s/\{(\S+)\,\"(\S+)\"\}/$1:os_mon\@$2/g; | |
} | |
my ($rate,$mean,$stddev,$max,$min,$meanfb,$countfb) = split(/\s+/,$values); | |
if ($type =~ /^cpu:/ ) { | |
next if $values =~ /^0/; # skip when no data is available | |
next if $mean > $CPU_MAX; # skip bad value | |
$category->{$type} = "os_mon_cpu"; | |
} elsif ($type =~ /^freemem:/) { | |
next if $values =~ /^0/; # skip when no data is available | |
$category->{$type} = "os_mon_free"; | |
} elsif ($type =~ /^load:/) { | |
next if $values =~ /^0/; # skip when no data is available | |
$category->{$type} = "os_mon_load"; | |
} elsif ($type =~ /^\w{4}packets:/) { | |
next if $values =~ /^0/; # skip when no data is available | |
$category->{$type} = "os_mon_packets"; | |
} elsif ($type =~ /^(\w+):os_mon/) { | |
next if $values =~ /^0/; # skip when no data is available | |
$category->{$type} = "os_mon_other"; | |
my $osname = $1; | |
push @{$os_mon_other}, $osname unless (grep {/^$osname$/ } @{$os_mon_other}) ; | |
} elsif ($type =~ /^\d+$/) { | |
$category->{$type} = "http_status"; | |
} elsif ($type eq "request" or $type eq "page" or $type eq "session" or $type eq "connect" or $type eq "async_rcv") { | |
$category->{$type} = "stats"; | |
} elsif ($type =~ /^tr_/ or $type eq "page" or $type eq "xmpp_msg_latency") { | |
$category->{$type} = "transaction"; | |
} elsif ($type =~ "^size") { | |
$category->{$type} = "network"; | |
} elsif ($type =~ /^error/) { | |
$category->{$type} = "error"; | |
} elsif ($type =~ /^bidi/ or $type eq "request_noack") { | |
$async = 1; | |
$category->{$type} = "count"; | |
} elsif ($type =~ /match/) { | |
$match = 1; | |
$category->{$type} = "match"; | |
} elsif ($type ne "users" and $type ne "connected" and $type =~ /^job_/) { | |
$category->{$type} = "count"; | |
} else { | |
$category->{$type} = "gauge"; | |
} | |
if ($interval) { | |
$rate /= $interval; | |
$maxval->{'rate'}->{$type} = &max($rate, $maxval->{'rate'}->{$type}); | |
$maxval->{'rate_total'}->{$type} = 0 if not defined $maxval->{'rate_total'}->{$type}; | |
$maxval->{'rate_count'}->{$type} = 0 if not defined $maxval->{'rate_count'}->{$type}; | |
$maxval->{'rate_total'}->{$type} = $maxval->{'rate_total'}->{$type} + $rate; | |
$maxval->{'rate_count'}->{$type} = $maxval->{'rate_count'}->{$type} + 1; | |
$maxval->{'maxmean'}->{$type} = &max($mean, $maxval->{'maxmean'}->{$type}); | |
$maxval->{'mean'}->{$type} = $meanfb; | |
$maxval->{'count'}->{$type} = &max($countfb, $maxval->{'count'}->{$type}); | |
$maxval->{'minmean'}->{$type} = &min($mean, $maxval->{'minmean'}->{$type}) if $rate; | |
} | |
push @{$data->{$type}}, $timestamp . " ". $values; | |
} elsif (/^\# stats:\s+dump at\s+(\d+)/) { | |
$first_timestamp= $1 unless $first_timestamp; | |
$interval = ($timestamp) ? $timestamp : 0; # keep previous value | |
$timestamp = $1 - $first_timestamp; | |
$interval = $timestamp-$interval; | |
$first_interval= $interval if $interval and not $first_interval; | |
} | |
} | |
close FILE; | |
if ($nohtml) { | |
foreach my $key (sort keys %{$maxval->{'rate'}}) { | |
if ($key =~ /\d+/ or $key =~ /^size/) { | |
printf "Total $key = %7.2f\n", $maxval->{'mean'}->{$key}; | |
} else { | |
printf "Mean $key (max sample) = %7.2f\n", $maxval->{'mean'}->{$key}; | |
} | |
printf "Rate $key (max sample) = %7.2f\n",$maxval->{'rate'}->{$key}; | |
} | |
} | |
my @time; | |
my @errors; | |
my @tps; | |
my @bosh_tps; | |
my @code; | |
my %extra_info = (); | |
my @session; | |
my @connect; | |
my @size; | |
my @match; | |
my @users; | |
my @users_rate; | |
my @transactions; | |
my @async; | |
my $key; | |
if ($interval != $first_interval) { | |
print "warn, last interval ($interval) not equal to the first, use the first one ($first_interval)\n"; | |
$interval=$first_interval; | |
} | |
my @col = ("rate","mean","stddev","max_sample","min_sample"); #session, perfs et transaction | |
my @colcount = ("rate","total"); #http_code match, event, errirn users_arrivaln size | |
my @colusers = ("simultaneous","maximum_simultaneous"); #users | |
my @colasync = ("rate","total"); #async | |
foreach $key (keys %{$data}) { | |
$key =~ s/\'//g; | |
open (TYPE, "> $datadir/$key.txt") or die "$!"; | |
foreach my $data (@{$data->{$key}}) { | |
if (($key !~ /^users$/ and $key !~ /^connected$/ and $key !~ /^size_/ and $key !~ /^job_/) and $interval) { # | |
my @tmp; | |
my $time; | |
($time, $data, @tmp) = split(/\s/,$data); | |
$data /= $interval; | |
$data = "$time $data @tmp"; | |
} elsif ($key =~ /^size/ and $interval) { # bits instead of bytes | |
my ($time, @tmp) = split(/\s/,$data); | |
@tmp = map {$_*8/(1024*$interval) } @tmp; # kb/s instead of Bytes/s | |
$data = "$time @tmp"; | |
} elsif ($key =~ /^connected/ or $key =~ /^job_/) { | |
my ($time,$rate, $cur) = split(/\s/,$data); | |
$data = "$time $cur $rate"; | |
} | |
print TYPE $data ."\n"; | |
} | |
if ($key eq "session") { | |
push @session, "$key.txt"; | |
} elsif ($key =~ /^size/) { | |
push @size, "$key.txt"; | |
} elsif ($key =~ /^users$/ or $key =~ /^connected$/ or $key =~ /^job_/) { | |
push @users, "$key.txt"; | |
} elsif ($key =~ /users/) { | |
push @users_rate, "$key.txt"; | |
} elsif ($key =~ /match/) { | |
push @match, "$key.txt"; | |
} elsif ($key =~ /^error/) { | |
push @errors, "$key.txt"; | |
} elsif ($key=~ /^bidi/ or $key eq "request_noack") { | |
push @async, "$key.txt"; | |
} elsif ($key =~ /request$/ or $key eq "connect" or $key eq "async_rcv") { | |
push @tps, "$key.txt"; | |
} elsif ($key eq "bosh_http_conn" or $key eq "bosh_http_req") { | |
$bosh = 1; | |
push @bosh_tps, "$key.txt"; | |
} elsif ($key =~ /^tr_/ or $key eq "page" or $key eq "xmpp_msg_latency") { | |
push @transactions, "$key.txt"; | |
} elsif ($key =~ /^\d+$/) { | |
$http = 1; | |
push @code, "$key.txt"; | |
} elsif ($key =~ /^(\S+)?:\S+?@\S+$/) { | |
my $key_short_name = $1; | |
push(@{$extra_info{$key_short_name}}, "$key.txt"); | |
} else { | |
push @time, "$key.txt"; | |
} | |
close TYPE; | |
} | |
if (not $dygraph) { | |
plot_stats(\@col,\@col,"Session",undef,\@session,["sessions/sec"],$logy) unless $noplot; | |
plot_stats(\@colcount,["HTTP Code Response rate","total"], "HTTP_CODE",undef,\@code,["number/sec","total"],$logy) if not $noplot and @code; | |
plot_stats(\@colcount,\@colcount,"Bosh",undef,\@bosh_tps,["rate"],$logy) unless $noplot; | |
plot_stats(\@col,["Request and tcp/udp connection rate","Mean request (and connection) duration","stddev","max_sample","min_sample"],"Perfs",undef,\@tps,["Requests rate (r/sec)","Requests duration (msec)"],$logy) unless $noplot; | |
plot_stats(\@col,["Transaction and page rate", "Mean transaction and page duration","stddev","max_sample","min_sample"],"Transactions",undef,\@transactions,["transactions/sec","Transaction duration (msec)"],$logy) unless $noplot; | |
plot_stats(\@colcount,\@colcount,"Match",undef,\@match,["rate","rate"],$logy) unless $noplot; | |
plot_stats(\@colcount,\@colcount,"Event",undef,\@time,["rate","msec"],$logy) unless $noplot; | |
plot_stats(\@colasync,\@colasync,"Async",undef,\@async,["rate","rate"],$logy) unless $noplot; | |
plot_stats(\@colcount,\@colcount,"Errors",undef,\@errors,["errors/sec","total"],$logy) unless $noplot; | |
plot_stats(\@colusers,["Simultaneous users and opened TCP/UDP connections","maximum_simultaneous"],"Users",undef,\@users,["value", "total"],$logy) unless $noplot; | |
plot_stats(\@colcount,["Users arrival/departure rate", "Total users"],"Users_Arrival",undef,\@users_rate,["number of users/sec", "total"],$logy) unless $noplot; | |
plot_stats(\@colcount,["Network throughput","Total send/receive bytes"],"Size",undef,\@size,["Kbits/sec","total Kbits"],$logy) unless $noplot; | |
} else { | |
plot_stats_dygraph(\@col,"Session",undef,\@session,["sessions/sec"],$logy) unless $noplot; | |
plot_stats_dygraph(\@colcount,"HTTP_CODE",undef,\@code,["number/sec","total"],$logy) if not $noplot and @code; | |
plot_stats_dygraph(\@colcount,"Bosh",undef,\@bosh_tps,["rate"],$logy) if not $noplot and @bosh_tps; | |
plot_stats_dygraph(\@col,"Perfs",undef,\@tps,["rate","msec"],$logy) unless $noplot; | |
plot_stats_dygraph(\@col,"Transactions",undef,\@transactions,["transactions/sec","msec"],$logy) unless $noplot; | |
plot_stats_dygraph(\@colcount,"Match",undef,\@match,["rate","rate"],$logy) unless $noplot; | |
plot_stats_dygraph(\@colcount,"Event",undef,\@time,["rate","msec"],$logy) unless $noplot; | |
plot_stats_dygraph(\@colasync,"Async",undef,\@async,["rate","rate"],$logy) unless $noplot; | |
plot_stats_dygraph(\@colcount,"Errors",undef,\@errors,["errors/sec","total"],$logy) unless $noplot; | |
plot_stats_dygraph(\@colusers,"Users",undef,\@users,["value", "total"],$logy) unless $noplot; | |
plot_stats_dygraph(\@colcount,"Users_Arrival",undef,\@users_rate,["number of users/sec", "total"],$logy) unless $noplot; | |
plot_stats_dygraph(\@colcount,"Size",undef,\@size,["Kbits/sec","total Kbits"],$logy) unless $noplot; | |
} | |
# Generate graphes for extra indicators (os_mon for example) | |
if (not $noplot and $extra and (scalar keys %extra_info) != 0 ) { | |
print STDOUT "Generation os_mon graphs\n" if $verbose; | |
foreach my $key (sort keys %extra_info) { | |
my $pos = index($key,":"); | |
if (not $dygraph) { | |
plot_stats(\@col,\@col, $key, undef, \@{$extra_info{$key}}, [$key],$logy); | |
} else { | |
plot_stats_dygraph(\@col, $key, undef, \@{$extra_info{$key}}, [$key],$logy); | |
} | |
} | |
} | |
$extra=0 if (scalar keys %extra_info == 0 ); # no extra information available | |
$errors=1 unless (scalar @errors == 0 ); # no extra information available | |
} | |
sub html_report { | |
require Template; | |
my $titre = $report_title; | |
my $version = $tagvsn; | |
my $contact = '[email protected]'; | |
my $output = 'index.html'; | |
my $tt = Template->new({ | |
INCLUDE_PATH => $template_dir, | |
PRE_CHOMP => 1, | |
INTERPOLATE => 1, | |
}) or die "Template error " . Template->error(); | |
my $xml_conf; | |
opendir (DIR, ".") or warn "can't open directory ."; | |
while (my $file = readdir (DIR) ) { | |
if ($file =~ /.xml$/) { | |
$xml_conf= $file; | |
} | |
} | |
foreach my $type ("mean", "maxmean", "minmean") { | |
foreach my $data (keys % {$maxval->{$type}} ) { | |
next if ($data =~ m/^size/); | |
if ($data =~ m/os_mon/) { | |
$maxval->{$type}->{$data} = sprintf "%.2f",$maxval->{$type}->{$data}; | |
next; | |
} | |
next if not ($data eq "session" or $data eq "connect" or $data eq "request" or $data eq "page" or $data =~ m/^tr_/ or $data eq "xmpp_msg_latency"); | |
$maxval->{$type}->{$data} = &formattime($maxval->{$type}->{$data}); | |
} | |
} | |
foreach my $data (keys % {$maxval->{'rate_count'}} ) { | |
next if ($data =~ m/^size/); | |
$maxval->{'rate_mean'}->{$data} = sprintf "%.2f",$maxval->{'rate_total'}->{$data} / $maxval->{'rate_count'}->{$data}; | |
} | |
foreach my $size ("size_rcv", "size_sent") { | |
if ($maxval->{rate}->{$size}) { | |
$maxval->{rate}->{$size} = &formatsize($maxval->{rate}->{$size}*8,"bits"); | |
$maxval->{maxmean}->{$size} = &formatsize($maxval->{maxmean}->{$size},"B"); | |
} else { | |
warn "$size is equal to 0 !\n"; | |
} | |
} | |
my $vars = | |
{ | |
version => $version, | |
os_mon => $extra, | |
errors => $errors, | |
title => $titre, | |
subtitle => "Stats Report", | |
http => $http, | |
stats_subtitle => "Stats Report ", | |
graph_subtitle => "Graphs Report ", | |
contact => $contact, | |
data => $maxval, | |
cat_data => $category, | |
conf => $xml_conf | |
}; | |
$tt->process("report.thtml", $vars, "report.html") or die $tt->error(), "\n"; | |
$vars = | |
{ | |
version => $version, | |
os_mon => $extra, | |
errors => $errors, | |
http => $http, | |
match => $match, | |
async => $async, | |
bosh => $bosh, | |
title => $titre, | |
subtitle => "Graphs Report", | |
stats_subtitle => "Stats Report ", | |
graph_subtitle => "Graphs Report ", | |
os_mon_other=> $os_mon_other, | |
contact => $contact, | |
conf => $xml_conf, | |
ext => $imgfmt | |
}; | |
mkdir "style" unless -d "./style"; | |
copy (($template_dir . "/style/bootstrap.min.js"), "./style/") or die "copy failed : $!"; | |
copy (($template_dir . "/style/bootstrap.min.css"), "./style/") or die "copy failed : $!"; | |
copy (($template_dir . "/style/dashboard.css"), "./style/") or die "copy failed : $!"; | |
copy (($template_dir . "/style/docs.min.css"), "./style/") or die "copy failed : $!"; | |
copy (($template_dir . "/style/jquery.min.js"), "./style/") or die "copy failed : $!"; | |
if (not $dygraph) { | |
$tt->process("graph.thtml", $vars, "graph.html") or die $tt->error(), "\n"; | |
} else { | |
$tt->process("graph_dy.thtml", $vars, "graph.html") or die $tt->error(), "\n"; | |
copy (($template_dir . "/style/dygraph-combined.js"), "./style/") or die "copy failed : $!"; | |
} | |
} | |
sub usage { | |
print "this script is part of tsung version $tagvsn, | |
Copyright (C) 2001-2004 IDEALX (http://IDEALX.org/)\n\n"; | |
print "tsung comes with ABSOLUTELY NO WARRANTY; This is free software, and | |
ou are welcome to redistribute it under certain conditions | |
type `tsung_stats.pl --version` for details.\n\n"; | |
print "Usage: $0 [<options>]\n","Available options:\n\t", | |
"[--help] (this help text)\n\t", | |
"[--verbose] (print all messages)\n\t", | |
"[--debug] (print receive without send messages)\n\t", | |
"[--dygraph] use dygraphs (http://dygraphs.com) to render graphs\n\t", | |
"[--noplot] (don't make graphics)\n\t", | |
"[--gnuplot <command>] (path to the gnuplot binary)\n\t", | |
"[--nohtml] (don't create HTML reports)\n\t", | |
"[--logy] (logarithmic scale for Y axis)\n\t", | |
"[--tdir <template_dir>] (Path to the HTML tsung templates)\n\t", | |
"[--noextra (don't generate graphics from extra data (os monitor, etc)\n\t", | |
"[--rotate-xtics (rotate legend of x axes)\n\t", | |
"[--stats <file>] (stats file to analyse, default=tsung.log)\n\t", | |
"[--img_format <format>] (output format for images, default=png | |
available format: ps, svg, png, pdf)\n\t", | |
"[--title <title>] (report title, default=Tsung)\n\t"; | |
exit; | |
} | |
sub affiche() { | |
my $name = shift; | |
my $value = shift; | |
return sprintf "#%7s = %.3f",$name,$value; | |
} | |
sub formattime { | |
my $value = shift; | |
return unless $value; | |
$value /=1000; | |
if ($value < 0.001) { | |
sprintf "%.3f msec",$value*1000; | |
} elsif ($value < 0.1) { | |
sprintf "%.2f msec",$value*1000; | |
} elsif ($value < 60) { | |
sprintf "%.2f sec",$value; | |
} elsif ($value < 3600) { | |
my $mn= int($value / 60); | |
$value = $value-($mn*60); | |
sprintf "%dmn %dsec",$mn, $value; | |
} elsif ($value > 3600) { | |
my $h = int($value / 3600); | |
my $mn= int(($value -$h*3600) / 60); | |
$value = $value-($h*3600+$mn*60); | |
sprintf "%d h %dmn %dsec",$h,$mn, $value; | |
} else { | |
sprintf "%.1f sec",$value; | |
} | |
} | |
sub formatsize { | |
my $value = shift; | |
my $unit = shift; | |
return unless $value; | |
if ($value < 1024) { | |
sprintf "%d $unit",$value; | |
} elsif ($value < 1024*1024) { | |
sprintf "%.2f K$unit",$value/1024; | |
} elsif ($value < 1024*1024*1024) { | |
sprintf "%.2f M$unit",$value/(1024*1024); | |
} else { | |
sprintf "%.2f G$unit",$value/(1024*1024*1024); | |
} | |
} | |
sub version { | |
print "this script is part of Tsung version $tagvsn | |
Written by Nicolas Niclausse and Jean François Lecomte | |
Copyright (C) 2001-2004 IDEALX (http://IDEALX.org/) | |
Copyright (C) 2004-2011 Nicolas Niclausse | |
This program is free software; you can redistribute it and/or | |
modify it under the terms of the GNU General Public License | |
as published by the Free Software Foundation; either version 2 | |
of the License, or (at your option) any later version. | |
This program is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU General Public License for more details. | |
You should have received a copy of the GNU General Public License | |
along with this program (see COPYING); if not, write to the | |
Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
Boston, MA 02111-1307, USA.\n"; | |
exit; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment