File Coverage

blib/lib/Benchmark/Harness.pm
Criterion Covered Total %
statement 18 168 10.7
branch 0 80 0.0
condition 0 17 0.0
subroutine 6 19 31.5
pod 0 12 0.0
total 24 296 8.1


line stmt bran cond sub pod time code
1 1     1   5 use strict;
  1         2  
  1         43  
2             package Benchmark::Harness;
3 1     1   595 use Benchmark::Harness::Constants;
  1         2  
  1         95  
4              
5 1     1   6 use vars qw($VERSION $VERSION $IS_HARNESS_MODE);
  1         2  
  1         138  
6             $VERSION = '1.11';
7             $VERSION = sprintf("%d.%02d", q$Revision: 1.3 $ =~ /(\d+)\.(\d+)/);
8             $IS_HARNESS_MODE = 0; ## PREVENT INTER-MODAL RECURSION
9              
10             =pod
11              
12             =head1 Benchmark::Harness
13              
14             =head2 WARNING!
15              
16             B
17             security/privacy risk to your application and the host computer it is running on.>
18              
19             See L, below
20              
21             B
22             IN YOUR PERL APPLICATION FOR ANY REASON!>>
23              
24             =head2 SYNOPSIS
25              
26             Benchmark::Harness will invoke subroutines at specific, parametizable
27             points during the execution of your Perl program.
28             These subroutines may be standard C tracing routines, or routines composed by you.
29             The setup involves just a one line addition to your test or driver program,
30             and is easily parameterized and turned on or off from the outside.
31              
32             To activate Benchmark::Harness on your program, add to your test or driver program the following:
33              
34             use Benchmark::Harness;
35             Benchmark::Harness:new(userPsw, 'MyHarness(reportFilename, ...)', @parameters );
36              
37             C is the required user authentication to make Benchmark::Harness work.
38             After authentication, new() loads your specified sub-harness (e.g., 'C')
39             and executes the C method on it, giving it the parameters specified in parantheses here.
40             C specifies how to report the results from your harness,
41             and C<@parameter> is a list of 'module::sub' strings, each of which specifies
42             a point in your target program to be monitored.
43              
44             =over 4
45              
46             =item userPsw
47              
48             The first parameter must be the userid and password (in the form "userid:password").
49             There is no default for this, and until you make an adjustment in the Authenticate()
50             subroutine of Benchmark::Harness, the Benchmark::Harness will not function.
51              
52             The base class will handle basic authentication in a standard manner for you, and you may override this
53             functionality by coding your own Authenticate() subroutine in your sub-harness.
54              
55             =item 'MyHarness'
56              
57             The second parameter causes your harness module to be loaded (you do not need to
58             'use' it to have it effective). See the documentation for C
59             for how you would write your sub-harness.
60              
61             Each sub-harness will be handed an array consisting of the parameters given in this new()
62             statement (as in the "(userPsw,...)" illustrated above).
63              
64             =item reportFilename
65              
66             Filename specifies the disposition (or not) of the output report.
67             Note that this is given to the sub-harness to handle as it pleases;
68             the base class Benchmark::Harness will handle it in the following manner:
69              
70             =over 8
71              
72             =item2 '1'
73              
74             The harness report is written to a temporary file. You can get the string contained
75             in this file with the Benchmark::Harness::old() method. The temporary file is then deleted.
76              
77             =item2 '0'
78              
79             This is a convenient way to turn the harness off. Since it can be done by parameterization
80             from the outside, it is especially adaptable to external toggling of the harness.
81             If '0' is specified, no action is performed by Benchmark::Harness or by your sub-harness.
82              
83             =item2 a file name
84              
85             If not '1' or '0', then this parameter is interpreted as a filename into which the report
86             is written. C will now return this filename rather than the content
87             of the file. The report file will not be deleted by C.
88              
89             =back
90              
91             =back
92              
93             =head3 Parameters
94              
95             Benchmark::Harness handles subsequent parameters by calling SetupHandler() with them.
96             Your sub-harness may perform specialized operations with these parameters;
97             Benchmark::Harness's default behavior is as follows.
98              
99             Each parameter after the filename specifies a sub() in your target program.
100             Methods in your sub-harness are called at the entry, exit, or both of the
101             Cs specified here.
102             These are strings; that is, you name the module and C in a string, not by a CODE reference.
103              
104             my @parms = qw(-MyProgram::start +MyProgram::finish MyProgram::run)
105             new Benchmark::Harness(userPsw, 'Benchmark::MyHarness(reportFilename)', @parms);
106              
107             Each parameter is preceded by a special character to specify the type of
108             monitoring to be performed on that sub().
109              
110             =over 4
111              
112             =item '-'
113              
114             Your sub-harness is called at the entry of the target sub(), with @_ equal
115             to the input parameters of that sub().
116              
117             =item '+'
118              
119             Your sub-harness is called when the sub() exits, with @_ or $_[0] (depending on wantarray)
120             equal to the return value of that sub().
121              
122             =item none
123              
124             Performs both '-' and '+'.
125              
126             =back
127              
128             You may select subroutines from your target module by some simple wildcards
129             (which are actually Perl regular expressions). Thus,
130              
131             new Benchmark::Harness(qw(user:psw Trace(1) -TestServer::M.* TestServer::Loop) )
132              
133             traces the entry of every subroutine in C whose name begins with an 'M',
134             and the entry and exit of the subroutine C.
135              
136             =head2 Example
137              
138             use Benchmark::Harness;
139             my @traceParameters = qw(Trace(1) -TestServer::M.* TestServer::Loop);
140             my $traceHarness = new Benchmark::Harness(userPsw, @traceParameters);
141              
142             TestServer::new(5,10,15,3,4); # Fire the module under test,
143              
144             my $result = $traceHarness->old(); # and here's our result (ref to a string).
145              
146             See C and C for examples
147             of how to build your own harness operations.
148              
149             =head2 More generalization
150              
151             Use the following construction to generalize your harness cababilites even more.
152             It is especially adaptable to supplying harness parameters in an XML attribute
153             (as an xsd:list type, which is a space delimited string).
154              
155             my @harnessParameters = split /\s/, $myParameterString;
156             if ( @harnessParameters ) {
157             eval "use Benchmark::Harness";
158             my $harness = Benchmark::Harness::new(\@harnessParameters);
159             }
160              
161             =cut
162              
163 1     1   899 use FileHandle;
  1         13532  
  1         7  
164 1     1   522943 use Devel::Peek; # thanks to Nate and Tye on perlmonks.org . . .
  1         1116  
  1         12  
165              
166             ## ###############################################
167             ## Authenticate user:psw given as first parameter
168             sub Authenticate {
169 0     0 0   my ($self, $givenAuthentication) = @_;
170              
171             # NOTE: You must code the required user/psw in the form "userId:password".
172 0           my $Authentication = undef;
173 0 0         return undef unless defined $Authentication;
174 0           my ($rUserId, $rPassword) = split /\:/,$Authentication;
175 0           my ($gUserId, $gPassword) = split /\:/,$givenAuthentication;
176 0   0       return ($rUserId eq $gUserId) && ($rPassword eq $gPassword);
177             }
178              
179             ## #######################################################################
180             ## Create a new harness based on the given sub-class of Benchmark::Harness
181             sub new {
182             # Ok, one of these days Glenn will figure out how to manage Perl static/function/methods/subs/variance . . .
183             # It just doesn't look like this is that day - gdw.2004-01-13
184 0     0 0   my $context = $_[0];
185 0 0         my $class = ($context =~ m/^Benchmark\:\:Harness/ ? shift : 'Benchmark::Harness');
186              
187 0           my $self = bless {
188             '_startTime' => time()
189             ,'_latestTime' => ''
190             ,'_latestPackage' => ''
191             ,'_latestFilename' => ''
192             ,'_latestLine' => ''
193             }, $class;
194 0           my $authentication = shift;
195              
196 0           my ($harnessClass, $harnessParameters) = ($_[0] =~ m/^([^(]+)(?:\(([^)]*)\))?$/);
197 0 0         $harnessClass = $_[0] unless $harnessClass; shift;
  0            
198              
199 0           my @harnessClasses = split /\|/,$harnessClass;
200 0           bless $self, 'Benchmark::Harness::'.$harnessClasses[0];
201 0 0         eval 'use '.ref($self); die $@ if $@;
  0            
202 0           my @harnessParameters = split /\|/, $harnessParameters;
203 0 0         return $self unless $self->Authenticate($authentication); # pretend we're working, but we're not.
204              
205 0           $self->Initialize(@harnessParameters);
206 0           $self->GenerateEvents(@_);
207              
208             # Now generate the harness attachment wrappers . . .
209 0           map {$_->Attach($self)} @{$self->{EventList}};
  0            
  0            
210              
211             # We're ready to go, print the report header.
212 0           $self->harnessPrintReportHeader();
213 0           $self->{_latestTime} = $self->{_startTime};
214              
215 0           return $self;
216             }
217              
218             sub old {
219 0 0   0 0   my $self = ref($_[0])?shift:$Benchmark::Harness::Harness;
220 0 0         return unless ref($self);
221 0 0         $self->close if $self->{_outFH};
222              
223 0 0         if ( $self->{_isTemp} ) {
224 0 0         open TMP, "<$self->{_outFilename}" or die "Can't open Harness file '$self->{_outFilename}': $!";
225 0           my $value= join '',; close TMP;
  0            
226 0           unlink $self->{_outFilename}; # would be unlinked by Apache::TempFile.
227 0           delete $self->{_outFilename};
228 0           return \$value;
229             } else {
230 0           return $self->{_outFilename};
231             }
232             }
233              
234             # Overridable by event handler
235             sub harnessPrintReportHeader {
236 0     0 0   my ($self) = @_;
237 0           my $fh = $self->{_outFH};
238 0           my $tm = localtime;
239 0           my $tagName = ref($self); $tagName =~ s{^.*::([^:]+)$}{$1};# $tagName =~ s/::/:/g;
  0            
240 0           my $version = $self->VERSION;
241 0           print $fh "<$tagName ".$self->xmlHeaders." n='$0' v='$version' V='$VERSION' tm='$tm' pid='$$' userid='$<,$>' os='$^O'>";
242 0   0       map {
243 0           my $modifiers = $_->[HNDLR_MODIFIERS] || '';
244 0           print $fh ""
245             }
246 0           @{$self->{EventList}};
247             }
248              
249             # Overridable by event harness
250             sub harnessPrintReportFooter {
251 0     0 0   my $fh = $_[0]->{_outFH};
252 0           my $tagName = ref($_[0]); $tagName =~ s{^.*::([^:]+)$}{$1}; #$tagName =~ s/::/:/g;
  0            
253 0           print $fh "";
254             }
255              
256             # Generic $harness->print
257             sub print {
258 0 0   0 0   my $self = ref($_[0])?shift:$Benchmark::Harness::Harness;
259 0 0         return unless ref($self);
260 0           my $fh = $self->{_outFH};
261 0 0         return unless $fh;
262 0           print $fh $_[0];
263 0           return $self;
264             }
265              
266             sub close {
267 0 0   0 0   my $self = ref($_[0])?shift:$Benchmark::Harness::Harness;
268 0 0         return unless ref($self);
269 0           my $fh = $self->{_outFH};
270 0 0         return unless $fh;
271 0           $self->harnessPrintReportFooter();
272 0           close $fh;
273 0           delete $self->{_outFH};
274              
275 0 0         map { $_->Detach() if defined $_ } @{$self->{EventList}};
  0            
  0            
276 0           delete $self->{EventList};
277 0           return $self;
278             }
279              
280             DESTROY {
281 0     0     $_[0]->close();
282             }
283              
284             ### ###########################################################################
285             ### FindHandler(newHandler) -
286             sub FindHandler {
287 0     0 0   my ($self, $pckg, $subName) = @_;
288 0           for ( @{$self->{EventList}} ) {
  0            
289 0 0 0       if ( $_->[HNDLR_NAME] eq $subName
290             && $_->[HNDLR_PACKAGE] eq $pckg
291             ) {
292 0           return $_;
293             }
294             }
295 0           return undef;
296             }
297              
298             ### ###########################################################################
299             sub harnessPrintReport {
300 0 0   0 0   my $self = ref($_[0])?shift:$Benchmark::Harness::Harness;
301 0 0         return unless ref($self);
302 0           my ($mode,$trace) = @_;
303              
304 0           my $rpt = $self->{report};
305 0 0         return unless $rpt;
306              
307 0           my $fh = $self->{_outFH};
308 0 0         return unless $fh;
309              
310 0 0         print $fh '<'.(defined($rpt->[0])?$rpt->[0]:'T')." _i='$trace->{id}' _m='$mode'";
311 0           my $closeTag = '/>';
312              
313 0           my $hsh = $rpt->[1];
314 0           map { print $fh " $_='$hsh->{$_}'" } keys %$hsh;
  0            
315              
316 0 0         if ( defined $rpt->[2] ) {
317 0 0         print $fh '>'; $closeTag = '[0])?$rpt->[0]:'T').'>';
  0            
318 0           for ( @{$rpt->[2]} ) {
  0            
319              
320             }
321             }
322              
323 0 0         if ( defined $rpt->[3] ) {
324 0 0         print $fh '>'; $closeTag = '[0])?$rpt->[0]:'T').'>';
  0            
325 0           print $fh $rpt->[3];
326             }
327              
328 0           print $fh $closeTag;
329 0           $self->{report} = undef;
330             }
331              
332             ### ###########################################################################
333             sub xmlHeaders {
334             #?? '' ??
335             #?? '' ??
336 0     0 0   my $pckg = ref($_[0]);
337 0           $pckg =~ s{Benchmark\:\:Harness\:\:}{};
338 0           $pckg =~ s{::}{/}g;
339             #my $schema = "http://schemas.GlennWood.us/Benchmark/$pckg";
340             #my $hdr = " xmlns='$schema'";
341 0           my $hdr .= " xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'";
342 0           $hdr .= " xsi:noNamespaceSchemaLocation='http://schemas.benchmark-harness.org/$pckg.xsd'";#" xsi:schemaLocation='$schema\nhttp://schemas.benchmark-harness.org/$pckg.xsd'";
343 0           return $hdr;
344             }
345              
346             ### ###########################################################################
347             ### ###########################################################################
348             # HERE WE SET UP THE DEFAULT BASE METHODS FOR CERTAIN STATISTICS
349             sub Initialize { # stub - this is normally set up in event handler
350 0     0 0   my $self = shift;
351              
352 0           for ( @_ ) {
353 0 0         m/^0?$/ && do { return $self; }; # '0' shuts everything off. next; };
  0            
354 0 0         m/^\|\d/ && do {
355 0           $self->{_isHotpipe} = 1;
356 0           $_ =~ s/^\|//;
357             }; # then fall through to tempfile open
358 0 0         m/^\d+$/ && do {
359 0 0         $self->{_outFilename} = (($^O eq 'MSWin32')?$ENV{TEMP}:'/tmp').'/harness.'.$$.'.xml';
360 0 0         $self->{_outFH} = new FileHandle(">$self->{_outFilename}")
361             or die "Can't open Harness file '$self->{_outFilename}': $!";
362 0           $self->{_isTemp} = 1;
363 0 0         $self->{_outFH}->autoflush(1) if ( $self->{_isHotpipe} );;
364 0           next;
365             };
366 0 0         m/^\|./ && do {
367 0           $self->{_isHotpipe} = 1;
368 0           $_ =~ s/^\|//;
369             }; # then fall through to filename open
370 0 0         m/^./ && do {
371 0           $self->{_outFilename} = $_;
372 0 0         $self->{_outFH} = new FileHandle(">$self->{_outFilename}")
373             or die "Can't open Harness file '$self->{_outFilename}': $!";
374 0           $self->{_isTemp} = 0;
375 0 0         $self->{_outFH}->autoflush(1) if ( $self->{_isHotpipe} );;
376 0           next;
377             };
378             }
379 0           return $self;
380             }
381              
382             ### ###########################################################################
383             ### ###########################################################################
384             #
385             sub GenerateEvents {
386 0     0 0   my $self = shift;
387 0           $self->{EventList} = [];
388 0           my $handler = ref($self); $handler =~ s{(\:\:[\w\d]+)$}{::Handler$1};
  0            
389              
390 0           for ( @_ ) {
391 0           my ($modifiers, $pckg, $method) = (m/^(?:\(([^)]*)\))?(.*)::([^:]+)$/);
392 0 0         eval "require $pckg"; die $@ if $@;
  0            
393 0 0         if ( $method !~ m/[\.\?\*\[\(]/ ) {
394 0           $handler->new($self, $modifiers, $pckg, $method);
395             } else {
396             # thanks to Nate on perlmonks.org . . .
397 1     1   4360 no strict;
  1         4  
  1         470  
398 0           local *stash;
399 0           *stash = *{ "${pckg}::" };
  0            
400 0           local $rgx = qr($method);
401 0           for (keys %stash)
402             {
403 0           my $glob = Devel::Peek::CvGV(\&{$stash{$_}});
  0            
404 0           $handler->new($self, $modifiers, $pckg, $_)
405 0 0 0       if ( defined &{ $stash{$_} }
      0        
      0        
406             and $glob eq "\*$pckg\:\:$_"
407             and m/$rgx/
408             and !m/(import|export|AUTOLOAD)/ );
409             }
410             }
411             }
412 0           return 1;
413             }
414             1;
415              
416             __END__