File Coverage

blib/lib/PeopleSoft/Tools.pm
Criterion Covered Total %
statement 21 204 10.2
branch 0 72 0.0
condition 0 48 0.0
subroutine 7 12 58.3
pod 3 5 60.0
total 31 341 9.0


line stmt bran cond sub pod time code
1             #
2             # $Id: Tools.pm,v 1.4 2003/06/05 16:00:58 goedicke Exp $
3             #
4             # Copyright (c) 2003 William Goedicke. All rights reserved. This program is free
5             # software; you can redistribute it and/or modify it under the same terms
6             # as Perl itself.
7              
8             =head1 NAME
9              
10             PeopleSoft::Tools - Procedural interface for working with tools, so
11             far just SQR.
12              
13             =head1 SYNOPSIS
14              
15             use PeopleSoft::Tools;
16             $new_buf = munge($sqr_prog_buf);
17             $new_buf = unmunge($munged_sqr_buf);
18             $results_in_html = profile($output_log_buf);
19              
20             =cut
21              
22 1     1   10670 use strict;
  1         3  
  1         44  
23 1     1   1088 use Time::Local;
  1         2035  
  1         66  
24 1     1   1176 use Graph;
  1         610825  
  1         49  
25 1     1   5111 use Getopt::Std;
  1         75  
  1         90  
26 1     1   8 use Data::Dumper;
  1         5  
  1         66  
27              
28             package PeopleSoft::Tools;
29 1     1   6 use Exporter;
  1         2  
  1         148  
30 1     1   6 use vars qw(@ISA @EXPORT);
  1         2  
  1         3797  
31             @ISA = qw(Exporter);
32              
33             @EXPORT = qw(munge
34             unmunge
35             profile
36             );
37             my ( $label, $max_lvl, $result_data );
38              
39             =head1 DESCRIPTION
40              
41             This module provides functions for working with various PeopleSoft
42             add-on tools, so far an SQR profiling function is provided.
43              
44             The following functions are provided (and exported) by this module:
45              
46             =cut
47              
48             # --------------------------------- munge()
49              
50             =over 3
51              
52             =item munge($buf, $debug_ltr)
53              
54             The munge function takes two parameters. First, a string buffer
55             containing a complete SQR program. Second is an optional letter
56             specifying a debug level (default is "p". It returns another buffer
57             of the original SQR with debug statements for every subroutine, select
58             and DDL.
59              
60             =back
61              
62             =cut
63              
64             #--------------------------------------------------
65             sub munge {
66 0     0 1   my ( $sqr, $letter ) = @_;
67 0 0         if ( ! defined $letter ) { $letter = "p"; }
  0            
68              
69 0           my ( %not_profiling, $rbuf );
70 0           my @sqr_lines = split "\n", $sqr;
71              
72 0           MAN: foreach ( @sqr_lines ) {
73 0           my $test=0;
74 0 0 0       if ( ( /^\s*begin\-/i or /^\s*end\-/i ) and not
      0        
      0        
75             ( /^\s*end-if\s*/i or /^\s*end-while\s*/i or /^\s*end-declare\s*/i or
76             /^\s*end-evaluate\s*/i or
77             /^\s*begin-heading\s*/i or /^\s*end-heading\s*/i or
78             /^\s*begin-setup\s*/i or /^\s*end-setup\s*/i )
79             ) {
80 0           $test = 1;
81 0           my ( $locale, $type ) = parse_stmt($_);
82              
83 0           my $start = "#debug$letter let \$BRTimeStamp = 'PFLR:' || '$label' || ':START:' || ";
84 0           $start .= "datetostr(datenow(),'YYYY-MM-DD HH24MISSNNNNNN')!PFLR\n";
85 0           $start .= "#debug$letter display \$BRTimeStamp !PFLR\n";
86              
87 0           my $end = "#debug$letter let \$BRTimeStamp = 'PFLR:' || '$label' || ':END:' || ";
88 0           $end .= "datetostr(datenow(),'YYYY-MM-DD HH24MISSNNNNNN')!PFLR\n";
89 0           $end .= "#debug$letter display \$BRTimeStamp !PFLR\n";
90              
91 0 0         if ( defined $not_profiling{$label} ) { next; }
  0            
92              
93 0 0 0       if ( $locale eq "begin" and $type eq "dml" ) {
    0 0        
    0 0        
    0 0        
      0        
      0        
94 0           $rbuf .= $start;
95 0           $rbuf .= $_ . "\n";
96             }
97             elsif ( $locale eq "end" and $type eq "dml" ) {
98 0           $rbuf .= $_ . "\n";
99 0           $rbuf .= $end;
100             }
101             elsif ( $locale eq "begin" and ( $type eq "proc" or $type eq "block" ) ) {
102 0           $rbuf .= $_ . "\n";
103 0           $rbuf .= $start;
104             }
105             elsif ( $locale eq "end" and ( $type eq "proc" or $type eq "block" ) ) {
106 0           $rbuf .= $end;
107 0           $rbuf .= $_ . "\n";
108             }
109             }
110 0 0         if ($test != 1) {
111 0           $rbuf .= $_ . "\n";
112             }
113             }
114 0           return $rbuf;
115             }
116             # --------------------------------- munge()
117              
118             =over 3
119              
120             =item unmunge($buf)
121              
122             unmunge takes a single argument of a string buffer which contains the
123             contents of a previously munged SQR. It returns an SQR with the
124             profiling statements removed.
125              
126             =back
127              
128             =cut
129              
130             #----------------------------------- unmunge()
131             sub unmunge {
132 0     0 1   my ( $mbuf ) = @_;
133 0           my ( $rbuf );
134              
135 0           my @mbuf_lines = split "\n", $mbuf;
136              
137 0           foreach ( @mbuf_lines ) {
138 0 0         if ( /\!PFLR/ ) { next; }
  0            
139 0           $rbuf .= $_ . "\n";
140             }
141 0           return $rbuf;
142             }
143             # ---------------------------------
144              
145             =over 3
146              
147             =item profile($output_log_buf);
148              
149             profile reads a buffer containing the contents of the output from a
150             munged SQR. It recurses a directed graph of the subroutines, DDL and
151             DML that were executed and returns HTML of the calling tree with times
152             called and intrinsic seconds of execution time.
153              
154             =back
155              
156             =cut
157              
158             #--------------------------------------------------
159             sub profile {
160 0     0 1   my ( $log_output ) = @_;
161 0           my @edata = split "\n", $log_output;
162 0           my $G_calls = new Graph;
163 0           my ( @call_stack );
164              
165 0           foreach ( @edata ) {
166 0           chomp;
167 0 0         if ( ! m/^PFLR/ ) {
168 0           next;
169             }
170 0           my ( $junk, $subr, $phase, $tstmp ) = split ":";
171 0           my ( $year, $mon, $day, $hour, $min, $sec, $ms ) =
172             ( $tstmp =~ /(....)-(..)-(..) (..)(..)(..)(...)/ );
173 0           my $time = Time::Local::timelocal( $sec, $min, $hour, $day, $mon, $year );
174 0           $time += $ms/1000;
175              
176             # print "$tstmp\n";
177 0 0         if ( $phase eq "START" ) {
    0          
178 0 0         if ( ! $G_calls->has_vertex($subr) ) {
179 0           $G_calls->add_vertex($subr);
180 0           $G_calls->set_attribute("Count", $subr);
181             }
182 0           my $count = $G_calls->get_attribute("Count", $subr);
183 0           $G_calls->set_attribute("Count", $subr, $count+1);
184 0 0 0       if ( $#call_stack >=0 and not $G_calls->has_edge($call_stack[-1], $subr) ) {
185 0           $G_calls->add_edge($call_stack[-1], $subr);
186             }
187 0 0         if ( $#call_stack >=0 ) {
188 0           $G_calls->set_attribute("CalcTime", $call_stack[-1], $subr, $time);
189             }
190 0           push @call_stack, $subr;
191             }
192             elsif ( $phase eq "END" ) {
193 0 0         if ( $#call_stack >0 ) {
194 0           my $start = $G_calls->get_attribute("CalcTime", $call_stack[-2], $subr);
195 0           my $duration = ( $time - $start ) +
196             $G_calls->get_attribute("Duration", $call_stack[-2], $subr);
197 0           $G_calls->set_attribute("Duration", $call_stack[-2], $subr, $duration);
198             }
199 0           pop @call_stack;
200             }
201             else {
202 0           die "Not START or END; that's bad";
203             }
204             }
205              
206 0           my $lvl=1;
207 0           foreach my $parent ( sort $G_calls->source_vertices ) {
208 0 0         if ( $G_calls->has_attribute("Done", $parent) ) {
209 0           next;
210             }
211 0           recurse( $G_calls, $parent, $lvl );
212 0           $G_calls->set_attribute("Done", $parent, 1);
213             }
214              
215 0           foreach my $vertex ( keys %{$G_calls->{'V'}} ) {
  0            
216 0           my ( $ex_dur, $in_dur );
217 0           foreach my $in_edges ( $G_calls->in_edges($vertex) ) {
218 0 0         if ( $in_edges eq $vertex ) { next; }
  0            
219 0           $ex_dur += $G_calls->get_attribute("Duration", $in_edges, $vertex);
220             # print "Ex: $ex_dur, $vertex, $in_edges\n";
221             }
222 0           foreach my $out_edges ( $G_calls->out_edges($vertex) ) {
223 0 0         if ( $out_edges eq $vertex ) { next; }
  0            
224 0           $in_dur += $G_calls->get_attribute("Duration", $vertex, $out_edges);
225             # print "In: $in_dur, $vertex, $out_edges\n";
226             }
227 0           my $fin_in = $ex_dur - $in_dur;
228 0 0         if ( $fin_in < 0 ) {
229 0           $ex_dur = $fin_in * -1;
230 0           $fin_in = 0;
231             }
232 0           $G_calls->set_attribute("Ex_Dur", $vertex, $ex_dur);
233 0           $G_calls->set_attribute("In_Dur", $vertex, $fin_in);
234             }
235              
236             # print Data::Dumper::Dumper($G_calls);
237             # exit;
238              
239 0           my $hbuf .= "\n"; \n"; \n";
240 0           for ( my $i=0;$i<$max_lvl-1;$i++) {
241 0           $hbuf .= "Function"
242             }
243 0           $hbuf .= "CountTotalIntrinsic
244 0           foreach ( split "\n", $result_data ) {
245 0           $hbuf .= "
 ";
246 0           my ( $subr, $lvl ) = split "!";
247 0           for (my $i=1;$i<$max_lvl;$i++) {
248 0 0         if ( $i == $lvl ) {
249 0           $hbuf .= "$subr ";
250             } else {
251 0           $hbuf .= " ";
252             }
253             }
254 0           $hbuf .= $G_calls->get_attribute("Count", $subr);
255 0           $hbuf .= "";
256 0           $hbuf .= sprintf("%0.3f",$G_calls->get_attribute("Ex_Dur", $subr));
257 0           $hbuf .= "";
258 0           $hbuf .= sprintf("%0.3f",$G_calls->get_attribute("In_Dur", $subr));
259 0           $hbuf .= "
260             }
261 0           $hbuf .= "
";
262              
263 0           my @junk = values( %{$G_calls->{'VertexSetParent'}} );
  0            
264 0           my $total = $G_calls->get_attribute("Ex_Dur", $junk[0]);
265            
266 0           my ( %count, %norm, %intrin );
267 0           foreach my $v ( $G_calls->vertices ) {
268 0           $count{$v} = $G_calls->get_attribute("Count", $v);
269 0           $intrin{$v} = $G_calls->get_attribute("In_Dur", $v);
270 0           $norm{$v} = $intrin{$v} / $count{$v};
271             }
272              
273 0           my $sum_buf = "\n";
274              
275 0           $sum_buf .= "\n\n"; \n"; \n"; \n";
276 0           $sum_buf .= "\n"; \n"; \n";
277 0           $sum_buf .= "
Normalized Intrinsic Duration
278              
279 0           my $i=0;
280 0           foreach my $v (sort { $norm{$b} <=> $norm{$a} } keys %norm ) {
  0            
281 0 0         if ( $i++ >= 5 ) { last; }
  0            
282 0           $sum_buf .= "
$v";
283 0           $sum_buf .= sprintf("%.2f",$norm{$v});
284 0           $sum_buf .= "
285             }
286 0           $sum_buf .= "
 
287              
288 0           $sum_buf .= "\n"; \n"; \n";
289 0           $sum_buf .= "
Intrinsic Duration
290              
291 0           my $i=0;
292 0           foreach my $v (sort { $intrin{$b} <=> $intrin{$a} } keys %intrin ) {
  0            
293 0 0         if ( $i++ >= 5 ) { last; }
  0            
294 0           $sum_buf .= "
$v";
295 0           $sum_buf .= sprintf("%.2f",$intrin{$v});
296 0           $sum_buf .= "";
297 0           my $pct = sprintf("%.2f",$intrin{$v} / $total * 100);
298 0           $sum_buf .= "$pct\%";
299 0           $sum_buf .= "
300             }
301 0           $sum_buf .= "
 
302              
303 0           $sum_buf .= "\n"; \n"; \n";
304 0           $sum_buf .= "
Counts
305              
306 0           my $i=0;
307 0           foreach my $v (sort { $count{$b} <=> $count{$a} } keys %count ) {
  0            
308 0 0         if ( $i++ >= 5 ) { last; }
  0            
309 0           $sum_buf .= "
$v$count{$v}
310             }
311 0           $sum_buf .= "
312 0           $sum_buf .= "
\n\n";
313              
314 0           $hbuf = $sum_buf . $hbuf . "\n";
315              
316 0           return $hbuf;
317             }
318             #--------------------------------------------------
319             sub recurse {
320 0     0 0   my ( $G_calls, $v, $lvl ) = @_;
321              
322 0           $result_data .= "$v!$lvl\n";
323 0 0         if ( defined $G_calls->{'Succ'}{$v} ) {
324 0           $lvl++;
325 0 0         if ( $max_lvl < $lvl ) { $max_lvl = $lvl; }
  0            
326 0           foreach my $child ( sort keys %{$G_calls->{'Succ'}{$v}} ) {
  0            
327 0           recurse( $G_calls, $child, $lvl );
328             }
329             }
330 0           $G_calls->set_attribute("Done", $v, 1);
331             }
332             #--------------------------------------------------
333             sub parse_stmt {
334             # be aware we just get $_ in this routine we use that
335 0     0 0   my ( $type, $locale );
336              
337 0 0         if ( /^\s*begin-/i ) { $locale = "begin"; }
  0 0          
338 0           elsif ( /^\s*end-/i ) { $locale = "end"; }
339 0           else { die "FATAL: I can't tell whether we're starting or ending!"; }
340              
341 0 0         if ( /^\s*${locale}-select\s*/i ) { $type = "select"; }
  0 0          
    0          
342 0           elsif ( /^\s*${locale}-sql\s*/i ) { $type = "sql"; }
343 0           elsif ( /^\s*${locale}-procedure\s*/i ) { $type = "proc"; }
344 0           else { $type = "block"; }
345              
346             # print "DBG2:$locale\t$type\t$_\n";
347              
348 0 0 0       if ( $type eq "proc" and $locale eq "begin" ) {
    0 0        
    0 0        
    0 0        
    0 0        
      0        
349 0           ( $label ) = ( /\s*$locale-procedure\s*([-\#\w]*)/i );
350             }
351             elsif ( $type eq "block" ) {
352 0           ( $label ) = ( /\s*$locale-([-\#\w]*)/i );
353             }
354             elsif ( ($type eq "select" or $type eq "sql" ) and
355             $locale eq "begin" ) {
356 0           $label .= "-dml";
357 0           $type = "dml";
358             }
359             elsif ( ($type eq "select" or $type eq "sql" ) and
360             $locale eq "end" ) {
361 0           $type = "dml";
362             }
363             elsif ( $type eq "proc" and $locale eq "end" ) {
364 0           $label =~ s/-dml//g;
365             }
366              
367 0           return( $locale, $type );
368             }
369              
370             1;