#!/usr/bin/perl -w
require 5.003;
use strict;
# plots traces labeled with label_results
# see also: label_results, report_results
if (`pwd` =~ 'ReportGen') {
use lib '..';
}
use ReportGen::Opts;
use ReportGen::Globs;
use ReportGen::ObjDbase;
use ReportGen::ObjManip;
require ReportGen::Plot;
require ReportGen::Line;
# Configuration
my %Opts = (
out_name => undef(),
plot_title => undef(),
plot_size => undef(),
plotter_terminal => undef(),
plotter => undef(),
plotter_opts => undef(),
);
my %SavedOpts = %Opts;
# globals
my @ObjTags = (); # objects to plot, label:name,name ...
my @Objs = (); # objects to plot, {label,name,fname,pos} tuples
my @ObjLabels = ();
my @ObjTypes = ();
my $Stats = {}; # aggregate stats
my $Plot;
my $XScale = 1;
&init();
exit(&main());
sub main {
foreach my $label (@ObjLabels) {
$Stats->{$label} = &ReadDbase(&FullInFName($label, 'clt.All.lx'));
}
&scanObjs();
$Plot = new ReportGen::Plot(join(' ', $0, @ARGV));
&setXLabel();
&setYLabels();
&setTitle();
&setOther();
&addLines();
&setTerminal($Opts{plotter_terminal});
&setOutput($Opts{out_name});
&plot();
return 0;
}
sub scanObjs {
my %labels = ();
my %types = ();
foreach my $tag (@ObjTags) {
my ($label, $names) = ($tag =~ /^(\S+):(.*)$/);
die(&usage()) unless $names;
&curLabel($label);
&addObjects();
foreach my $name (split(/,/, $names)) {
my $fname = &FullInFName($label, 'clt.Traces', '.lx');
my $type = &ObjName2Type($name);
die("$0: unknown object `$name'\n") unless $type;
my $pos = &getObjPos($fname, $name);
warn("$0: `$fname' has no object named `$name', skipping\n"), next
unless defined $pos;
my $obj = {
label => $label,
name => $name,
type => $type,
pos => $pos,
fname => $fname,
};
push @Objs, $obj;
push @ObjLabels, $label unless exists $labels{$label};
push @ObjTypes, $type unless exists $types{$type};
$labels{$label} = $obj;
$types{$type} = $obj;
}
&curLabel(undef());
}
}
sub setXLabel {
my $max_dur = -1;
foreach my $label (@ObjLabels) {
my $dur = $Stats->{$label}->{duration}->{body};
$max_dur = $dur if defined $dur && $max_dur < $dur;
}
if ($max_dur > 2*60*60) {
$XScale = 60*60;
$Plot->xlabel_qw('time (hours)');
} else {
$XScale = 60;
$Plot->xlabel_qw('time (minutes)');
}
}
sub setYLabels {
if ( @ObjTypes > 2 ){
$Plot->ylabel();
$Plot->y2label();
$Plot->noytics();
$Plot->noy2tics();
} else { # @ObjTypes <= 2
$Plot->ylabel_qw($ObjTypes[0]) unless !@ObjTypes;
if (@ObjTypes > 1) {
$Plot->y2label_qw($ObjTypes[1]);
$Plot->y2tics();
$Plot->ytics('nomirror');
}
}
}
sub setTitle {
my $title = $Opts{plot_title};
if (!defined $title) {
my @onames = ();
# XXX: use importObjects instead and use human names everywhere
if (@ObjLabels == 1) {
my $label = $ObjLabels[0];
my $facts = &ReadDbase(&FullInFName($label, 'facts.lx'), 'may_fail');
&importObjects($facts, $label) if $facts;
my $hname = &label2hname($label);
@onames = map { $_->{name} } @Objs;
$title = "$hname: ";
} else {
@onames = @ObjTags;
$title = '';
}
$title = &fmtTitle($title, " vs. time", @onames);
}
$Plot->title($title);
}
sub setOther {
$Plot->range('y', 0);
$Plot->data('style lines');
$Plot->grid();
$Plot->timestamp();
$Plot->size($Opts{plot_size}) if defined $Opts{plot_size};
}
sub addLines {
for my $obj (@Objs) {
my $line = new ReportGen::Line;
my $ypos = $obj->{'pos'};
$ypos = "\$$ypos" unless $ypos =~ /\$/;
$line->data($obj->{fname});
$line->using("\$1/$XScale", $ypos);
if (@ObjLabels == 1) {
$line->title($obj->{name});
} else {
$line->title($obj->{label}. ':'. $obj->{name});
}
$line->axes('x1y2') if
$obj->{type} ne $ObjTypes[0] && @ObjTypes <= 2;
$Plot->line($line);
}
}
sub setTerminal {
my $term = shift;
$Plot->terminal($term) if $term;
}
sub setOutput {
my $fname = shift;
$Plot->output($fname) if defined $fname;
}
sub plot {
if (my $fname = $Plot->ofile()) {
warn("$0: plotting: `$fname'\n");
}
$Plot->plot();
}
sub getObjPos {
my ($fname, $name) = @_;
open(IF, "<$fname") or die("$0: cannot open `$fname': $!\n");
my $firstLine = <IF>;
close(IF);
my @names = split(/\s+/, $firstLine);
die("$0: `$fname' has unknown file format\n")
unless @names && $names[0] =~ s/^#//;
my $pos = &findName($name, \@names);
return $pos if defined $pos;
#die("$0: `$fname' has no object named `$name'\n");
return undef();
}
sub findName {
my ($name, $names) = @_;
my $pos = 1;
foreach my $n (@{$names}) {
return $pos if $n eq $name;
$pos++;
}
# raw object is not found, see if the object is derived
my $expr = &objExpr($name);
return undef() unless defined $expr;
$expr =~ s/([_a-zA-Z][\w\.]*)/&findNameAndFmt($1, $names)/eg;
return undef() if $expr =~ /@@@/;
return $expr;
}
sub fmtTitle {
my ($prefix, $suffix, @names) = @_;
my $extraLen = length($prefix) + length($suffix);
my $title = '';
while (@names && $extraLen + length($title) + length($names[0]) <= 75) {
$title .= ' & ' if length($title);
$title .= shift @names;
}
return "${prefix}${title}${suffix}";
}
sub findNameAndFmt {
my ($name, $names) = @_;
my $pos = &findName($name, $names);
return '@@@' unless defined $pos;
return sprintf('($%d)', $pos) if $pos =~ /^\d/;
return "($pos)";
}
sub init {
die(&usage()) unless &ParseOpts(\%Opts, \@ObjTags, @ARGV) && @ObjTags;
die("plotter must be specified\n" . &usage()) unless $Opts{plotter};
$ReportGen::Plot::Plotter = $Opts{plotter};
$ReportGen::Plot::Plotter .= ' ' . $Opts{plotter_opts}
if $Opts{plotter_opts};
}
sub usage {
return "usage: $0 [options] <label:object[,object...]> ...\n" . &Opts2Str(\%SavedOpts);
}
syntax highlighted by Code2HTML, v. 0.9.1