File Coverage

blib/lib/Paranoid/Debug.pm
Criterion Covered Total %
statement 203 212 95.7
branch 54 68 79.4
condition 7 8 87.5
subroutine 46 47 97.8
pod 9 9 100.0
total 319 344 92.7


line stmt bran cond sub pod time code
1             # Paranoid::Debug -- Debug support for paranoid programs
2             #
3             # $Id: lib/Paranoid/Debug.pm, 2.10 2022/03/08 00:01:04 acorliss Exp $
4             #
5             # This software is free software. Similar to Perl, you can redistribute it
6             # and/or modify it under the terms of either:
7             #
8             # a) the GNU General Public License
9             # as published by the
10             # Free Software Foundation ; either version 1
11             # , or any later version
12             # , or
13             # b) the Artistic License 2.0
14             # ,
15             #
16             # subject to the following additional term: No trademark rights to
17             # "Paranoid" have been or are conveyed under any of the above licenses.
18             # However, "Paranoid" may be used fairly to describe this unmodified
19             # software, in good faith, but not as a trademark.
20             #
21             # (c) 2005 - 2020, Arthur Corliss (corliss@digitalmages.com)
22             # (tm) 2008 - 2020, Paranoid Inc. (www.paranoid.com)
23             #
24             #####################################################################
25              
26             #####################################################################
27             #
28             # Environment definitions
29             #
30             #####################################################################
31              
32             package Paranoid::Debug;
33              
34 70     70   6902 use strict;
  70         114  
  70         1634  
35 70     70   275 use warnings;
  70         104  
  70         1616  
36 70     70   275 use vars qw($VERSION @EXPORT @EXPORT_OK %EXPORT_TAGS);
  70         90  
  70         3301  
37 70     70   335 use base qw(Exporter);
  70         113  
  70         4551  
38 70     70   435 use Paranoid;
  70         118  
  70         2898  
39 70     70   367 use Carp;
  70         206  
  70         8879  
40              
41             ($VERSION) = ( q$Revision: 2.10 $ =~ /(\d+(?:\.\d+)+)/sm );
42              
43             @EXPORT = qw(PDEBUG pdebug pIn pOut subPreamble subPostamble
44             PDEBUG1 PDEBUG2 PDEBUG3 PDEBUG4 PDEBUG5 PDEBUG6 PDEBUG7 PDEBUG8);
45             @EXPORT_OK = (
46             @EXPORT,
47             qw(pderror PDPREFIX PDLEVEL1 PDLEVEL2 PDLEVEL3 PDLEVEL4 PDMAXINDENT),
48             );
49             %EXPORT_TAGS = (
50             all => [@EXPORT_OK],
51             constants => [
52             qw(PDEBUG1 PDEBUG2 PDEBUG3 PDEBUG4 PDEBUG5 PDEBUG6
53             PDEBUG7 PDEBUG8)
54             ],
55             );
56              
57 70     70   741 use constant PDLEVEL1 => 9;
  70         137  
  70         3542  
58 70     70   580 use constant PDLEVEL2 => 10;
  70         137  
  70         2983  
59 70     70   572 use constant PDLEVEL3 => 11;
  70         132  
  70         2923  
60 70     70   347 use constant PDLEVEL4 => 12;
  70         203  
  70         3158  
61              
62 70     70   363 use constant PDMAXIND => 40;
  70         110  
  70         3104  
63 70     70   342 use constant PDMAXSCALAR => 20;
  70         110  
  70         2833  
64              
65 70     70   358 use constant PDEBUG1 => 1;
  70         346  
  70         2750  
66 70     70   333 use constant PDEBUG2 => 2;
  70         104  
  70         3255  
67 70     70   369 use constant PDEBUG3 => 3;
  70         120  
  70         2959  
68 70     70   334 use constant PDEBUG4 => 4;
  70         461  
  70         2819  
69 70     70   587 use constant PDEBUG5 => 5;
  70         143  
  70         2709  
70 70     70   349 use constant PDEBUG6 => 6;
  70         159  
  70         3041  
71 70     70   356 use constant PDEBUG7 => 7;
  70         128  
  70         2899  
72 70     70   326 use constant PDEBUG8 => 8;
  70         152  
  70         2788  
73              
74 70     70   358 use constant CSF_PKG => 0;
  70         102  
  70         2525  
75 70     70   327 use constant CSF_FNM => 1;
  70         120  
  70         3078  
76 70     70   368 use constant CSF_LN => 2;
  70         126  
  70         2945  
77 70     70   366 use constant CSF_SUB => 3;
  70         131  
  70         2405  
78 70     70   435 use constant CSF_HAS => 4;
  70         158  
  70         2755  
79 70     70   375 use constant CSF_WNT => 5;
  70         121  
  70         2706  
80 70     70   368 use constant CSF_EVL => 6;
  70         118  
  70         3217  
81 70     70   343 use constant CSF_REQ => 7;
  70         115  
  70         2765  
82 70     70   338 use constant CSF_HNT => 8;
  70         183  
  70         2895  
83 70     70   366 use constant CSF_BIT => 9;
  70         106  
  70         2620  
84 70     70   529 use constant CSF_HSH => 10;
  70         129  
  70         40392  
85              
86             #####################################################################
87             #
88             # Module code follows
89             #
90             #####################################################################
91              
92             {
93             my $dlevel = 0; # Start with no indentation
94             my $pdebug = 0; # Start with debug output disabled
95             my $maxLevel = PDMAXIND; # Start with normal max indentation
96              
97             my $odefprefix = sub {
98              
99             # Old default Prefix to use with debug messages looks like:
100             #
101             # [PID - $dlevel] Subroutine:
102             #
103             my $caller = shift;
104             my $indentation = shift;
105             my $oi = $indentation;
106             my $prefix;
107              
108             # Cap indentation
109             $indentation = $maxLevel if $indentation > $maxLevel;
110              
111             # Construct the prefix
112             $prefix = ' ' x $indentation . "[$$-$oi] $caller: ";
113              
114             return $prefix;
115             };
116             my $defprefix = sub {
117              
118             # Default Prefix to use with debug messages looks like:
119             #
120             # [PID-level caller]
121             #
122             my $caller = shift;
123             my $indentation = shift;
124             my $prefix = '';
125             my $oi = $indentation;
126              
127             # Cap indentation
128             $indentation = ( $maxLevel / 3 ) if ( $indentation * 3 ) > $maxLevel;
129              
130             # Compose the prefix
131             if ( $indentation == 1 ) {
132             $prefix = '+> ';
133             } elsif ( $indentation > 1 ) {
134             $prefix = '| ' x ( $indentation - 1 ) . '+> ';
135             }
136             $prefix .= "[$$-$oi $caller] ";
137              
138             return $prefix;
139             };
140             my $pdprefix = $defprefix;
141              
142             sub PDEBUG : lvalue {
143 530721     530721 1 1775960 $pdebug;
144             }
145              
146             sub PDPREFIX : lvalue {
147 530721     530721 1 623758 $pdprefix;
148             }
149              
150             sub PDMAXINDENT : lvalue {
151 0     0 1 0 $maxLevel;
152             }
153              
154             sub pderror ($) {
155              
156             # Purpose: Print passed string to STDERR
157             # Returns: Return value from print function
158             # Usage: $rv = pderror("Foo!");
159              
160 1     1 1 2 my $msg = shift;
161              
162 1         2 $@ = $msg;
163              
164 1         33 return print STDERR "$msg\n";
165             }
166              
167             sub pdebug ($;$@) {
168              
169             # Purpose: Calls pderror() if the message level is less than or equal
170             # to the value of PDBEBUG, after prepending the string
171             # returned by the PDPREFIX routine, if defined
172             # Returns: Always returns the passed message, regardless of PDEBUG's
173             # value
174             # Usage: pdebug($message, $level);
175              
176 530721     530721 1 703535 my $msg = shift;
177 530721   100     817125 my $level = shift || 1;
178 530721         824043 my @pfargs = @_;
179 530721         686939 my $prefix = PDPREFIX;
180 530721         623756 my ( $ci, @crec, $caller, $n, $np );
181              
182 530721 50       752384 $msg = '' unless defined $msg;
183              
184             # If called with a negative level it merely means we
185             # need to go a little bit deeper in the call stack to find the
186             # true initiator of the message. This provides the mechanism for
187             # Paranoid::Log::plog to pass indirect debug messages
188 530721 100       745496 $ci = $level < 0 ? 2 : 1;
189 530721 100       771927 $level *= -1 if $level < 0;
190              
191             # Get the call stack info
192 530721         1936784 @crec = caller $ci;
193 530721 50       944746 $caller =
    100          
194             defined $crec[CSF_SUB] ? $crec[CSF_SUB]
195             : defined $crec[CSF_FNM] ? "$crec[CSF_FNM]/$crec[CSF_LN]"
196             : 'undef';
197              
198             # Filter message through sprintf if args were passed
199 530721         1986832 $n = [ $msg =~ m#(%[\w.]+)#sg ];
200 530721         874960 $np = $n = scalar @$n;
201 530721 100       865824 if ($n) {
202              
203             # Adjust n upwards if we were given more list items than
204             # we see placeholders for in the messsage string
205 455122 100       748629 $n = scalar @pfargs if @pfargs > $n;
206              
207             # Make sure the requisite number of args are translated for undefs
208 455122         691951 while ( $n > 0 ) {
209 768925         774637 $n--;
210 768925 100       1554913 $pfargs[$n] = 'undef' unless defined $pfargs[$n];
211             }
212              
213             # Consolidate extra args into the last placeholder's spot
214 455122 100       676117 if ( scalar @pfargs > $np ) {
215 155         189 $n = $np - 1;
216 155         618 @pfargs =
217             ( @pfargs[ 0 .. ( $n - 1 ) ], "@pfargs[$n .. $#pfargs]" );
218             }
219              
220             # Filter through sprintf
221             {
222 70     70   477 no warnings;
  70         122  
  70         6338  
  455122         475143  
223 455122         1381648 $msg = sprintf $msg, @pfargs;
224             }
225             }
226              
227 530721 50       880546 return $msg if $level > PDEBUG;
228              
229             # Execute the code block, if that's what it is
230 0 0       0 $prefix = &$prefix( $caller, $dlevel ) if ref($prefix) eq 'CODE';
231              
232             {
233 70     70   400 no warnings;
  70         112  
  70         32242  
  0         0  
234 0         0 pderror("$prefix$msg");
235             }
236              
237 0         0 return $msg;
238             }
239              
240             sub pIn () {
241              
242             # Purpose: Increases indentation level
243             # Returns: Always True (1)
244             # Usage: pIn();
245              
246 268     268 1 454 $dlevel++;
247              
248 268         342 return 1;
249             }
250              
251             sub pOut () {
252              
253             # Purpose: Decreases indentation level
254             # Returns: Always True (1)
255             # Usage: pOut();
256              
257 154     154 1 209 $dlevel--;
258              
259 154         293 return 1;
260             }
261              
262             my %subprotos;
263              
264             sub _protos ($$) {
265              
266             # Purpose: Converts a string prototype declaration to an array
267             # Returns: Array
268             # Usage: @proto = _protos($caller, $proto);
269              
270 394158     394158   466874 my $caller = shift;
271 394158         417906 my $proto = shift;
272 394158         469068 my ( $t, $p, @rv );
273              
274 394158 100 66     1101550 if ( defined $proto and length $proto ) {
275              
276 378300 100       662566 if ( exists $subprotos{$caller} ) {
277              
278             # Return cached values if we have them
279 375823         406331 @rv = @{ $subprotos{$caller} };
  375823         699132  
280              
281             } else {
282              
283             # Parse and generate list of argument types
284 2477         3859 $p = $proto;
285 2477         4283 while ( length $p ) {
286 4254 100       15633 if ( $p =~ /^\\/s ) {
    100          
    50          
287              
288             # argument is a reference to... something.
289 237         413 push @rv, 'ref';
290 237         851 $p =~ s/^\\(\[[^\]]+\]|.)//s;
291              
292             } elsif ( $p =~ /^([\$@%&\*pb])/s ) {
293              
294 3635         8094 $t = $1;
295 3635 0       8368 push @rv,
    50          
    100          
    100          
    50          
    100          
296             $t eq '$' ? 'scalar'
297             : $t eq 'p' ? 'private'
298             : $t eq 'b' ? 'bytes'
299             : $t eq '@' ? 'array'
300             : $t eq '%' ? 'hash'
301             : $t eq '&' ? 'code'
302             : 'glob';
303 3635         12610 $p =~ s/^.//s;
304              
305             } elsif ( $p =~ /^;/ ) {
306              
307             # Remove the optional delimiter
308 382         1094 $p =~ s/^;//s;
309              
310             } else {
311 0         0 croak
312             "unknown prototype: $proto (stopped on $p) in\n\t"
313             . join ',',
314             caller 2;
315             }
316             }
317              
318             # Cache results
319 2477         9097 $subprotos{$caller} = [@rv];
320             }
321              
322             }
323              
324 394158         757655 return @rv;
325             }
326              
327             sub _summarize (\@@) {
328              
329             # Purpose: Summarizes potentially long types of scalars
330             # Returns: Array
331             # Usage: @args = _summarize(@argt, @args);
332              
333 394158     394158   472870 my $tref = shift;
334 394158         544899 my @args = @_;
335 394158         461108 my ( @tmp, %tmp, $i, $l );
336              
337 70     70   460 no warnings;
  70         120  
  70         2196  
338 70     70   38885 use bytes;
  70         948  
  70         303  
339              
340             # Iterate over args/argt and summarize where appropriate
341 394158         692635 for ( $i = 0; $i < @args; $i++ ) {
342 689239 100 100     2174973 if ( $$tref[$i] eq 'scalar' and not ref $args[$i] ) {
    100          
    100          
    50          
    100          
343 594021 100       1055505 if ( defined $args[$i] ) {
344 473274         674211 $l = length $args[$i];
345 473274 100       805908 $args[$i] =
346             substr( $args[$i], 0, PDMAXSCALAR ) . "... ($l bytes)"
347             if $l > PDMAXSCALAR;
348 473274         725433 $args[$i] =~ s/\n/\\n/sg;
349 473274         779703 $args[$i] =~ s/[^[:print:]]/./sg;
350             }
351             } elsif ( $$tref[$i] eq 'array' ) {
352 744         2580 @tmp = splice @args, $i;
353 744         3225 $args[$i] = sprintf 'list of %d items', scalar @tmp;
354             } elsif ( $$tref[$i] eq 'hash' ) {
355 575         1955 %tmp = splice @args, $i;
356 575         3036 $args[$i] = sprintf 'hash of %d k/v pairs', scalar keys %tmp;
357             } elsif ( $$tref[$i] eq 'private' ) {
358 0 0       0 if ( defined $args[$i] ) {
359 0         0 $l = length $args[$i];
360 0         0 $args[$i] = "REDACTED ($l bytes)";
361             }
362             } elsif ( $$tref[$i] eq 'bytes' ) {
363 62 50       116 if ( defined $args[$i] ) {
364 62         69 $l = length $args[$i];
365 62         134 $args[$i] = "$l bytes";
366             }
367             }
368              
369 689239 100       1656534 $args[$i] = 'undef' unless defined $args[$i];
370             }
371              
372 394158         944799 return @args;
373             }
374              
375             sub subPreamble ($;$@) {
376              
377             # Purpose: Specialized wrapper meant specifically for use in
378             # functions and methods
379             # Returns: Output of pdebug()
380             # Usage: subPreamble(PDEBUG1, '$$@', @_);
381              
382 197024     197024 1 227755 my $level = shift;
383 197024         239157 my $proto = shift;
384 197024         362537 my @args = splice @_;
385 197024         760799 my @caller = caller(1);
386 197024         430890 my @argt = _protos $caller[CSF_SUB] . "-pre", $proto;
387 197024         278587 my ( $rv, $msg );
388              
389 70     70   22773 no warnings;
  70         136  
  70         17114  
390              
391             # Summarize the args
392 197024         326688 @args = _summarize( @argt, @args );
393              
394             # Print message
395 197024         258594 $msg = 'entering';
396 197024 100       307912 if (@args) {
397 180934         375032 $msg .= ' w/' . '(%s)' x ( scalar @args );
398             }
399 197024         340838 $rv = pdebug( $msg, ( $level * -1 ), @args );
400              
401             # Increase indentation level
402 197024         284060 $dlevel++;
403              
404 197024         481992 return $rv;
405             }
406              
407             sub subPostamble ($$@) {
408              
409             # Purpose: Specialized wrapper meant specifically for use in
410             # functions and methods
411             # Returns: Output of pdebug()
412             # Usage: subPostamble(PDEBUG1, '$', $rv);
413              
414 197134     197134 1 245803 my $level = shift;
415 197134         237684 my $proto = shift;
416 197134         324681 my @args = splice @_;
417 197134         759377 my @caller = caller(1);
418 197134         430747 my @argt = _protos $caller[CSF_SUB] . '-post', $proto;
419 197134         267941 my ( $rv, $msg );
420              
421             # Decrease indentation level
422 197134         229493 $dlevel--;
423              
424             # Summarize the args
425 197134         308842 @args = _summarize( @argt, @args );
426              
427             # Print message
428 197134         256150 $msg = 'leaving w/rv: ';
429 197134 100       298761 if (@args) {
430 197113         358623 $msg .= '(%s)' x ( scalar @args );
431             }
432 197134         348661 $rv = pdebug( $msg, ( $level * -1 ), @args );
433              
434 197134         520502 return $rv;
435             }
436              
437             }
438              
439             1;
440              
441             __END__