use strict;
package Benchmark::Harness::Handler;
use Benchmark::Harness::Constants;
use XML::Quote;
use overload;
use vars qw($VERSION); $VERSION = sprintf("%d.%02d", q$Revision: 1.1 $ =~ /(\d+)\.(\d+)/);
### ###########################################################################
# USAGE: new Benchmark::Harness::Handler(
# $parentHarness,
# modifiers_from_(...),
# package-name,
# subroutine-name)
sub new {
my ($cls, $harness, $modifiers, $pckg, $subName) = @_;
# If already defined, then we keep the original one
# ("the pen once writ . . .")
return undef if $harness->FindHandler($pckg, $subName);
my $self = bless [ $#{$harness->{EventList}}+1,
$harness,
$modifiers,
$subName,
$pckg,
undef,
0,
], $cls;
push @{$harness->{EventList}}, $self;
return $self;
}
# Attached this event handler to this subroutine in the code
# Modifiers -
# '0' : do not harness this method (even if asked to later in the parameters)
# filter, filterStart : harness, but report only each filter-th event, starting
# with the filterStart-th event. filterStart=0|undef reports
# the first event, then each filter-th one thereafter.
sub Attach {
my ($traceSubr) = @_;
my ($modifiers, $pckg, $method) = ($traceSubr->[HNDLR_MODIFIERS], $traceSubr->[HNDLR_PACKAGE], $traceSubr->[HNDLR_NAME]);
return if ( defined $modifiers && ($modifiers eq '0') ); # (0) means do not harness . . .
# Splitting handler parameters by '|' makes it easier to include them in a qw()
my ($filter, $filterStart) = (split /\s*\|\s*/, $modifiers) if defined $modifiers;
$traceSubr->[HNDLR_ORIGMETHOD] = \&{"$pckg\:\:$method"};
my $newMethod;
if ( defined $filter ) {
$filter = $filter || 1;
$filterStart = $filterStart || 1;
$traceSubr->[HNDLR_FILTER] = $filter;
$traceSubr->[HNDLR_FILTERSTART] = $filterStart;
### ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
## NEW METHOD ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
$newMethod = sub {
if ( $traceSubr->[HNDLR_FILTERSTART] ) {
goto $traceSubr->[HNDLR_ORIGMETHOD] if ( --$traceSubr->[HNDLR_FILTERSTART] );
$traceSubr->[HNDLR_FILTERSTART] = $traceSubr->[HNDLR_FILTER];
}
my @newArgs;
unless ( $Benchmark::Harness::IS_HARNESS_MODE ) {
$Benchmark::Harness::IS_HARNESS_MODE += 1;
@newArgs = $traceSubr->OnSubEntry(@_);
$traceSubr->harnessPrintReport('E',$traceSubr);
$Benchmark::Harness::IS_HARNESS_MODE -= 1;
}
if (wantarray) {
my @answer = $traceSubr->[HNDLR_ORIGMETHOD](@_);
my $newAnswer;
unless ( $Benchmark::Harness::IS_HARNESS_MODE ) {
$Benchmark::Harness::IS_HARNESS_MODE += 1;
$newAnswer = $traceSubr->OnSubExit(\@answer);
$traceSubr->harnessPrintReport('X',$traceSubr);
$Benchmark::Harness::IS_HARNESS_MODE -= 1;
}
return @answer;
} else {
my $answer;
my $newAnswer;
unless ( $Benchmark::Harness::IS_HARNESS_MODE ) {
$Benchmark::Harness::IS_HARNESS_MODE += 1;
$answer = $traceSubr->[HNDLR_ORIGMETHOD](@_);
$newAnswer = scalar $traceSubr->OnSubExit($answer);
$traceSubr->harnessPrintReport('X',$traceSubr);
$Benchmark::Harness::IS_HARNESS_MODE -= 1;
}
return $answer;
}
};
### ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
### ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
} else {
### ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
## NEW METHOD ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
$newMethod = sub {
my @newArgs;
unless ( $Benchmark::Harness::IS_HARNESS_MODE ) {
$Benchmark::Harness::IS_HARNESS_MODE += 1;
@newArgs = $traceSubr->OnSubEntry(@_);
$traceSubr->harnessPrintReport('E',$traceSubr);
$Benchmark::Harness::IS_HARNESS_MODE -= 1;
}
if (wantarray) {
my @answer = $traceSubr->[HNDLR_ORIGMETHOD](@_);
my $newAnswer;
unless ( $Benchmark::Harness::IS_HARNESS_MODE ) {
$Benchmark::Harness::IS_HARNESS_MODE += 1;
$newAnswer = $traceSubr->OnSubExit(\@answer);
$traceSubr->harnessPrintReport('X',$traceSubr);
$Benchmark::Harness::IS_HARNESS_MODE -= 1;
}
return @answer;
} else {
my $answer = $traceSubr->[HNDLR_ORIGMETHOD](@_);
my $newAnswer;
unless ( $Benchmark::Harness::IS_HARNESS_MODE ) {
$Benchmark::Harness::IS_HARNESS_MODE += 1;
$newAnswer = scalar $traceSubr->OnSubExit($answer);
$traceSubr->harnessPrintReport('X',$traceSubr);
$Benchmark::Harness::IS_HARNESS_MODE -= 1;
}
return $answer;
}
};
### ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
### ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
}
### ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
## NEW METHOD ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
no warnings; # We are redefining a method, so don't warn all that!
eval "\*$pckg\:\:$method = \$newMethod";
$traceSubr->[HNDLR_HANDLED] = 1;
}
sub Detach {
my ($traceSubr) = @_;
return unless $traceSubr->[HNDLR_HANDLED];
my ($pckg, $method, $origMethod) = ($traceSubr->[HNDLR_PACKAGE],$traceSubr->[HNDLR_NAME],$traceSubr->[HNDLR_ORIGMETHOD]);
no warnings; # We are redefining a method, so don't warn all that!
eval "\*$pckg\:\:$method = \$origMethod";
}
### ###########################################################################
sub reportTraceInfo {
my $self = shift;
$self->[HNDLR_REPORT] = [undef,{},undef,undef] unless defined $self->[HNDLR_REPORT];
my $rpt = $self->[HNDLR_REPORT];
for ( @_ ) {
my $typ = ref($_);
if ( $typ ) {
if ( $typ eq 'HASH' ) {
my $hsh = $rpt->[1];
for my $nam ( keys %$_ ) {
$hsh->{$nam} = $_->{$nam};
}
}
elsif ( $typ eq 'ARRAY' ) {
$rpt->[2] = [] unless defined $rpt->[2];
push @{$rpt->[2]}, @$_;
}
elsif ( $typ eq 'SCALAR' ) {
$rpt->[3] .= $$_;
} else {
$rpt->[3] .= $_;
}
} else {
$rpt->[0] = $_;
}
}
return $self;
}
### ###########################################################################
sub reportValueInfo {
my $self = shift;
my $val = ['V',{},undef,undef];
for ( @_ ) {
my $typ = ref($_);
if ( $typ ) {
if ( $typ eq 'HASH' ) {
my $hsh = $val->[1];
for my $nam ( keys %$_ ) {
# I figure this is the quickest way to get both
# the stringified (if overloaded) and type of
# the value in this hash-entry.
my $_val = $_->{$nam};
my $_ref = ref($_val);
if ( $_ref ) {
if ( my $stringify = overload::Method($_val,'""') ) {
$hsh->{$nam} = $stringify->($_val);
$hsh->{_t} = ref($_val);
#unless defined $hsh->{_t};
} else {
$hsh->{$nam} = $_val;
}
} else {
$hsh->{$nam} = $_val;
}
}
}
elsif ( $typ eq 'ARRAY' ) {
$val->[2] = [] unless defined $val->[2];
push @{$val->[2]}, @$_;
}
elsif ( $typ eq 'SCALAR' ) {
$val->[3] .= $$_;
}
else {
$val->[3] .= "$_"; # will stringify if overloaded
}
} else {
$val->[0] = $_;
}
}
$self->[HNDLR_REPORT] = [undef,{},[],undef] unless defined $self->[HNDLR_REPORT];
my $rpt = $self->[HNDLR_REPORT];
push @{$rpt->[2]}, $val;
return $val;
}
### ###########################################################################
### harnessPrintReport ( mode, event-handler, [ report-element ] )
sub harnessPrintReport {
my $self = shift;
return unless ref($self);
my $harness = $self->[HNDLR_HARNESS];
my $mode = shift;
my $trace = shift;
my $rpt = shift || $self->[HNDLR_REPORT];
return unless $rpt;
my $fh = $harness->{_outFH};
return unless $fh;
print $fh '<'.(defined($rpt->[0])?$rpt->[0]:'T');
print $fh " _i='$trace->[HNDLR_ID]' _m='$mode'" if $mode;
my $closeTag = '/>';
my $hsh = $rpt->[1];
map { print $fh " $_='".xml_quote($hsh->{$_})."'" if defined $hsh->{$_} } keys %$hsh;
if ( defined $rpt->[2] ) {
print $fh '>'; $closeTag = ''.(defined($rpt->[0])?$rpt->[0]:'T').'>';
for ( @{$rpt->[2]} ) {
$self->harnessPrintReport(undef, undef, $_);
}
}
if ( defined $rpt->[3] ) {
print $fh '>'; $closeTag = ''.(defined($rpt->[0])?$rpt->[0]:'T').'>';
print $fh $rpt->[3];
}
print $fh $closeTag;
$self->[HNDLR_REPORT] = undef;
}
### ###########################################################################
# USAGE: Invoked by attach()'d subroutine: see above.
# This is, presumably, overridden by the sub-harness.
sub OnSubEntry {
my $self = shift;
return @_;
}
### ###########################################################################
# USAGE: Invoked by attach()'d subroutine: see above.
# This is, presumably, overridden by the sub-harness.
sub OnSubExit {
my $self = shift;
return @_;
}
### ###########################################################################
# USAGE: Harness::Variables(list of any variable(s));
sub Variables {
my $self = ref($_[0])?shift:$Benchmark::Harness::Harness;
return unless ref($self);
return unless $self->{_outFH};
}
### ###########################################################################
# USAGE: Harness::Arguments(@_);
sub ArgumentsXXX {
my $self = shift;
return $self unless ref($self);
return $self unless $self->{_outFH};
$self->_PrintT('-Arguments', caller(1));
my $i = 1;
for ( @_ ) {
my $obj = ref($_)?$_:\$_;
my ($nm, $sz) = (ref($_), Devel::Size::total_size($_));
$nm = $i unless $nm; $i += 1;
$self->print("");
}
$self->_PrintT_();
return $self;
}
### ###########################################################################
# USAGE: Harness::NamedObject($name, $self); - where $self is a blessed reference.
sub NamedObjects {
my $self = shift;
return $self unless ref($self);
my %objects = @_;
for ( keys %objects ) {
$self->reportValueInfo(
{ 'n' => $_,
'v' => $objects{$_},
}
);
}
return $self;
}
### ###########################################################################
# USAGE: Harness::Object($obj); - where $obj is an object reference.
sub Object {
my $self = shift;
return $self unless ref($self);
my $pckg = $_[0];
my $pckgName = "$pckg";
$pckgName =~ s{=?(ARRAY|HASH|SCALAR).*$}{};
my $pckgType = $1;
$self->_PrintT("-$pckgType $pckgName", caller(1));
$self->OnObject(@_);
$self->_PrintT_();
return $self;
}
### ###########################################################################
# USAGE: Benchmark::MemoryUsage::MethodReturn( $pckg )
# Print useful information about the given object ($pckg)
sub OnObject {
my $self = shift;
my $obj = shift;
my $objName = "$obj";
$objName =~ s{=?([A-Z]+).*$}{};#s{=?(ARRAY|HASH|SCALAR|CODE).*$}{};
my $objType = $1 || '';
if ( $objType eq 'HASH' ) {
my $i = 0;
for ( keys %$obj ) {
my $obj = ref($_)?$_:\$_;
my ($nm) = ($_);
$nm = $i unless $nm; $i += 1;
$self->print("");
}
} elsif ( $objType eq 'ARRAY' ) {
my $i = 0;
for ( @$obj ) {
my ($nm) = ($i);
$i += 1;
$self->print("");
last if ( ++$i == 20 );
if ( scalar(@$objType) > 20 ) {
$self->print("");
};
}
} elsif ( $objType eq 'SCALAR' ) {
$self->print("$$obj");
} else {
$self->print("$obj");
}
return $self;
}
### ###########################################################################
# USAGE: Harness::NamedVariables('name1' => $variable1 [, 'name1' => $variable2 ])
sub NamedVariables {
my $self = ref($_[0])?shift:$Benchmark::Harness::Harness;
return $self unless ref($self);
$self->_PrintT(undef, caller(1));
my $i = 1;
while ( @_ ) {
my ($nm, $sz) = (shift, Devel::Size::total_size(shift));
$nm = $i unless $nm; $i += 1;
$self->print("");
}
$self->_PrintT_();
return $self;
}
1;