Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

514 lines (475 sloc) 19.425 kb
#!/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
#
# 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.
use strict;
use Getopt::Long;
use vars qw ($help @files $verbose $debug $noplot $noextra $version $stats
$template_dir $nohtml $template_dir $gnuplot $logy);
my $tagvsn = '@PACKAGE_VERSION@';
GetOptions( "help",\$help,
"verbose",\$verbose,
"debug",\$debug,
"stats=s",\$stats,
"gnuplot=s",\$gnuplot,
"version",\$version,
"logy",\$logy,
"noplot",\$noplot,
"tdir=s",\$template_dir,
"nohtml",\$nohtml,
"noextra",\$noextra
);
&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 $category;
my $prefix ="@prefix@";
unless ($template_dir) {
if (-d "$ENV{HOME}/.tsung/templates/") {
$template_dir = "$ENV{HOME}/.tsung/templates/";
} elsif (-d "@datadir@/@TEMPLATES_SUBDIR@") {
$template_dir = "@datadir@/@TEMPLATES_SUBDIR@";
} 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;
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 $http; # true if http. add status code graphs in the HTML output
foreach my $dir ($datadir, $imgdir, $gplotdir) {
unless (-d $dir) {
print "creating subdirectory $dir \n";
mkdir "$dir" or die "can't create directory $dir";
}
}
$gnuplot = "gnuplot" unless $gnuplot;
$gnuplot .= " >> gnuplot.log 2>&1";
&parse_stats_file($stats);
&html_report() unless $nohtml;
# plot stats from file
sub plot_stats {
# 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 $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;
}
$datatype = "unknown" unless $datatype;
open(GP,">$gplotdir/graphes-$datatype.gplot") or die "can't open graphes-$datatype.gplot: $!";
select GP;
foreach my $output_ext ("ps", "png") {
if ($output_ext eq "png") {
$output_type = "png";
} elsif ($output_ext eq "ps") {
$output_type = "postscript color ";
}
# gnuplot styles and options
print "set size $thumbnail_size,$thumbnail_size\n" if ($output_ext eq "png");
print "set data style $style\n";
print "set terminal $output_type\n";
print "set grid\n";
my $d; # temporary var (title)
foreach $d (0..$#{$title}) {
# gnuplot headings
print "set output \"$imgdir/graphes-$datatype-@{$title}[$d].$output_ext\"\n";
print "set title \" @{$title}[$d]\"\n";
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];
print "show title\n";
print "set $legend_loc\n";
print "set logscale y\n" if $logy;
print "plot ";
foreach $filename (@{$files}) {
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//;
print " title \"$cur_title\"" ;
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: $!";
}
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,$stdvar,$max,$min,$meanfb,$countfb) = split(/\s+/,$values);
if ($interval) {
$rate /= $interval;
$maxval->{'rate'}->{$type} = &max($rate, $maxval->{'rate'}->{$type});
$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;
}
if ($type =~ /^cpu:/ ) {
next if $values =~ /^0/; # skip when no data is available
$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 =~ /^\w{4}packets:/) {
next if $values =~ /^0/; # skip when no data is available
$category->{$type} = "os_mon_packets";
} 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") {
$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") {
$category->{$type} = "count";
} else {
$category->{$type} = "gauge";
}
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 @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","stdvar","max_sample","min_sample");
my @colcount = ("rate","total");
my @colusers = ("simultaneous","maximum_simultaneous");
my @colasync = ("rate","total");
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 $interval) {#
my @tmp;
my $time;
($time, $data, @tmp) = split(/\s/,$data);
$data /= $interval;
$data = "$time $data @tmp";
} elsif ($key =~ /^size/) { # 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/) {
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$/) {
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 =~ /^tr_/ or $key eq "page") {
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;
}
plot_stats(\@col,"Session",undef,\@session,["sessions/sec"],$logy) unless $noplot;
plot_stats(\@colcount,"HTTP_CODE",undef,\@code,["number/sec","total"],$logy) if not $noplot and @code;
plot_stats(\@col,"Perfs",undef,\@tps,["rate","msec"],$logy) unless $noplot;
plot_stats(\@col,"Transactions",undef,\@transactions,["transactions/sec","msec"],$logy) unless $noplot;
plot_stats(\@colcount,"Match",undef,\@match,["rate","rate"],$logy) unless $noplot;
plot_stats(\@colcount,"Event",undef,\@time,["rate","msec"],$logy) unless $noplot;
plot_stats(\@colasync,"Async",undef,\@async,["rate","rate"],$logy) unless $noplot;
plot_stats(\@colcount,"Errors",undef,\@errors,["errors/sec","total"],$logy) unless $noplot;
plot_stats(\@colusers,"Users",undef,\@users,["value", "total"],$logy) unless $noplot;
plot_stats(\@colcount,"Users_Arrival",undef,\@users_rate,["number of users/sec", "total"],$logy) unless $noplot;
plot_stats(\@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,":");
plot_stats(\@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 = 'Tsung ';
my $version = $tagvsn;
my $contact = '@PACKAGE_BUGREPORT@';
my $output = 'index.html';
my $tt = Template->new({
INCLUDE_PATH => $template_dir,
PRE_CHOMP => 1,
INTERPOLATE => 1,
}) or die "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_/);
$maxval->{$type}->{$data} = &formattime($maxval->{$type}->{$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,
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,
title => $titre,
stats_subtitle => "Stats Report ",
graph_subtitle => "Graphs Report ",
contact => $contact,
conf => $xml_conf
};
$tt->process("graph.thtml", $vars, "graph.html") or die $tt->error(), "\n";
}
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",
"[--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",
"[--stats <file>] (stats file to analyse, default=tsung.log)\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 < 10) {
sprintf "%.3f sec",$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/)
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.";
exit;
}
Jump to Line
Something went wrong with that request. Please try again.