Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

453 lines (415 sloc) 16.246 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 $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"; # 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;
die "$gnuplot is not executable !" unless -x $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 right 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");
}
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) = split(/\s+/,$values);
if ($interval) {
$rate /= $interval;
$maxval->{'rate'}->{$type} = &max($rate, $maxval->{'rate'}->{$type});
$maxval->{'mean'}->{$type} = &max($mean, $maxval->{'mean'}->{$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 =~ /match/) {
$match = 1;
$category->{$type} = "match";
} elsif ($type ne "users") {
$category->{$type} = "count";
}
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 $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");
foreach $key (keys %{$data}) {
$key =~ s/\'//g;
open (TYPE, "> $datadir/$key.txt") or die "$!";
foreach my $data (@{$data->{$key}}) {
if (($key !~ /^users$/ 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";
}
print TYPE $data ."\n";
}
if ($key eq "session") {
push @session, "$key.txt";
} elsif ($key =~ /^size/) {
push @size, "$key.txt";
} elsif ($key =~ /^users$/) {
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 =~ /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(\@colcount,"Errors",undef,\@errors,["errors/sec","total"],$logy) unless $noplot;
plot_stats(\@colusers,"Users",undef,\@users,["connected users", "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 $size ("size_rcv", "size_sent") {
if ($maxval->{rate}->{$size}) {
$maxval->{rate}->{$size} = sprintf("%.2f",$maxval->{rate}->{$size}*8/1024);
$maxval->{mean}->{$size} = sprintf("%.2f",$maxval->{mean}->{$size}/(1024*1024));
} 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,
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 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.