File Coverage

blib/lib/Devel/DumpTrace.pm
Criterion Covered Total %
statement 378 591 63.9
branch 131 254 51.5
condition 45 104 43.2
subroutine 61 75 81.3
pod 0 19 0.0
total 615 1043 58.9


line stmt bran cond sub pod time code
1             package Devel::DumpTrace;
2             ## no critic (NoStrict,StringyEval)
3              
4 16     16   55311 use 5.008000;
  16         92  
5 16     16   5895 use Hash::SafeKeys;
  16         11561  
  16         810  
6 16     16   4905 use PadWalker;
  16         6625  
  16         560  
7 16     16   79 use Scalar::Util 1.14;
  16         334  
  16         764  
8 16     16   4982 use Text::Shorten;
  16         30  
  16         572  
9 16     16   4958 use Devel::DumpTrace::CachedDisplayedArray;
  16         48  
  16         381  
10 16     16   4627 use Devel::DumpTrace::CachedDisplayedHash;
  16         28  
  16         349  
11 16     16   6732 use IO::Handle;
  16         73046  
  16         610  
12 16     16   8784 use File::Temp;
  16         184654  
  16         836  
13 16     16   94 use Carp;
  16         27  
  16         626  
14 16     16   73 use Fcntl qw(:flock :seek);
  16         40  
  16         1863  
15 16     16   76 use strict;
  16         26  
  16         264  
16 16     16   55 use warnings;
  16         29  
  16         2136  
17              
18             our $VERSION = '0.27';
19              
20             my $Time_HiRes_avail;
21             my $color_avail;
22              
23             BEGIN {
24             # process environment before Devel::DumpTrace::Const is compiled
25 16 50   16   80 if (defined $ENV{DUMPTRACE}) {
26 0 0       0 my $kv_splitter = $ENV{DUMPTRACE}=~/;/ ? ';' : ',';
27 0         0 foreach my $kv (split $kv_splitter, $ENV{DUMPTRACE}) {
28 0         0 my ($k,$v) = split /=/, $kv, 2;
29 0         0 $ENV{"DUMPTRACE_$k"} = $v;
30             }
31             }
32              
33 16   50 16   897 $Time_HiRes_avail = eval 'use Time::HiRes qw(time);1' || 0;
  16         6245  
  16         16871  
  16         51  
34 16   50 16   683 $color_avail = eval
  16         7234  
  16         97723  
  16         898  
35             'use Term::ANSIColor;$Term::ANSIColor::VERSION>=3.00' || 0;
36              
37             # idea from Devel::GlobalDestruction 0.13
38             # replace $_GLOBAL_DESTRUCTION used in earlier versions
39 16 50       79 if (defined ${^GLOBAL_PHASE}) {
40 16 0   0   1014 eval 'sub __inGD(){${^GLOBAL_PHASE}eq q{DESTRUCT}&&__END()};1';
  0         0  
41             } else {
42 0         0 require B;
43 0         0 eval 'sub __inGD(){${B::main_cv()}==0&&__END();};1';
44             }
45             }
46 16     16   5597 use Devel::DumpTrace::Const;
  16         31  
  16         12451  
47              
48             our $ARRAY_ELEM_SEPARATOR = ',';
49             our $HASH_ENTRY_SEPARATOR = ';';
50             our $HASH_PAIR_SEPARATOR = '=>';
51             our $XEVAL_SEPARATOR = ':';
52             our $SEPARATOR = "-------------------------------------------\n";
53              
54             my $pid = $$;
55             our $DUMPTRACE_FH;
56             our $DUMPTRACE_COLOR;
57             our $SMART_ABBREV = 1;
58             our $DB_ARGS_DEPTH = 3;
59             our %EXCLUDE_PKG = ();
60             our %INCLUDE_PKG = ('main' => 1);
61             our @EXCLUDE_PATTERN = ('^Devel::DumpTrace', '^Text::Shorten');
62             our @INCLUDE_PATTERN = ();
63             our (%DEFERRED, $PAD_MY, $PAD_OUR, $TRACE);
64             our $_THREADS = 0;
65             our $_INIT = 0;
66              
67             my (@matches, %sources);
68             my @_INC = @lib::ORIG_INC ? @lib::ORIG_INC : @INC;
69              
70             # these variables are always qualified into the 'main' package,
71             # regardless of the current package
72             my %ALWAYS_MAIN = (ENV => 1, INC => 1, ARGV => 1, ARGVOUT => 1,
73             SIG => 1, STDIN => 1, STDOUT => 1, STDERR => 1,);
74              
75             # used by _qquote below
76             my %esc = ("\a" => '\a', "\b" => '\b', "\t" => '\t', "\n" => '\n',
77             "\f" => '\f', "\r" => '\r', "\e" => '\e',);
78              
79             # use PPI by default, if available
80             $Devel::DumpTrace::NO_PPI
81             || $ENV{DUMPTRACE_NOPPI}
82 14     14   7768 || eval 'use Devel::DumpTrace::PPI;1';
  0         0  
  0         0  
83              
84             {
85             *Devel::Trace::TRACE = \$TRACE;
86             tie $TRACE, 'Devel::DumpTrace::VerboseLevel';
87             if (defined $ENV{DUMPTRACE_LEVEL}) {
88             $TRACE = $ENV{DUMPTRACE_LEVEL};
89             } else {
90             $TRACE = 'default';
91             }
92              
93             *DB::DB = \&DB__DB unless defined &DB::DB;
94              
95             if (defined $ENV{DUMPTRACE_FH}) {
96             if (uc $ENV{DUMPTRACE_FH} eq 'STDOUT') {
97             $DUMPTRACE_FH = *STDOUT;
98             } elsif (uc $ENV{DUMPTRACE_FH} eq 'TTY') {
99             my $tty = $^O eq 'MSWin32' ? 'CON' : '/dev/tty';
100             unless (open $DUMPTRACE_FH, '>>', $tty) {
101             warn "Failed to open tty as requsted by ",
102             "DUMPTRACE_FH=$ENV{DUMPTRACE_FH}. Failover to STDERR\n";
103             $DUMPTRACE_FH = *STDERR;
104             }
105             } else {
106             ## no critic (BriefOpen)
107             unless (open $DUMPTRACE_FH, '>', $ENV{DUMPTRACE_FH}) {
108             die "Can't use $ENV{DUMPTRACE_FH} as trace output file: $!\n",
109             "Devel::DumpTrace module is quitting.\n";
110             }
111             }
112             } else {
113             $DUMPTRACE_FH = *STDERR;
114             }
115             $DUMPTRACE_FH->autoflush(1);
116             $DUMPTRACE_COLOR = $ENV{DUMPTRACE_COLOR} || '';
117             if ($DUMPTRACE_COLOR) {
118             if ($color_avail) {
119             if ($DUMPTRACE_COLOR =~ /^\d+$/) {
120             my $bg = $DUMPTRACE_COLOR >> 4;
121             my $fg = $DUMPTRACE_COLOR & 7;
122             my $bold = ($DUMPTRACE_COLOR & 8) != 0;
123             my @c = ("black","red","green","yellow",
124             "blue","magenta","cyan","white");
125             $DUMPTRACE_COLOR = ($bold ? "bold " : "") . $c[$fg]
126             . ($bg ? " on_" . $c[$bg & 7] : "");
127             }
128             $DUMPTRACE_COLOR = Term::ANSIColor::color($DUMPTRACE_COLOR);
129             our $DUMPTRACE_RESET = Term::ANSIColor::color('reset');
130             } else {
131             carp "DUMPTRACE_COLOR spec ignored: ",
132             "Term::ANSIColor not available";
133             $DUMPTRACE_COLOR = '';
134             }
135             } else {
136             $DUMPTRACE_COLOR = "";
137             }
138             $SMART_ABBREV = 0 if $ENV{DUMPTRACE_DUMB_ABBREV};
139             }
140              
141             sub import {
142 32     32   232 my ($class, @args) = @_;
143              
144 32         82 push @EXCLUDE_PATTERN, map '^' . substr($_,1) . '$', grep { /^-/ } @args;
  11         42  
145 32         63 push @INCLUDE_PATTERN, map '^' . substr($_,1) . '$', grep { /^\+/ }@args;
  11         29  
146             # these packages will be included/excluded at CHECK time, after
147             # all packages have been loaded
148              
149             push @EXCLUDE_PATTERN,
150 32   50     187 map { '^' . $_ . '$' } split /,/, $ENV{DUMPTRACE_EXCLPKG} || '';
  0         0  
151             push @INCLUDE_PATTERN,
152 32   50     156 map { '^' . $_ . '$' } split /,/, $ENV{DUMPTRACE_INCLPKG} || '';
  0         0  
153              
154 32         58 @args = grep { /^[^+-]/ } @args;
  11         48  
155 32 100       78 if (grep { $_ eq ':test' } @args) {
  11         37  
156              
157             # :test
158             # import some low level routines to the calling
159             # namespace for testing.
160              
161 11         21 @args = grep { $_ ne ':test' } @args;
  11         26  
162 16     16   100 no strict 'refs';
  16         27  
  16         5333  
163 11         24 my $p = caller;
164 11         27 foreach my $name (qw(save_pads evaluate_and_display_line dump_scalar
165             hash_repr array_repr handle_deferred_output
166             evaluate save_previous_regex_matches)) {
167 88         137 *{$p . '::' . $name} = *$name;
  88         312  
168             }
169 11         24 *{$p . '::substitute'} = *perform_variable_substitutions;
  11         39  
170 11         22 *{$p . '::xsubstitute'} = *perform_extended_variable_substitutions;
  11         48  
171             }
172 32 50       84 if (@args > 0) {
173 0         0 $TRACE = join ',', @args;
174             }
175 32         1539 return;
176             }
177              
178             our $ZZ = 0;
179              
180             sub DB__DB {
181 0 0   0 0 0 return if __inGD();
182 0 0       0 return unless $Devel::DumpTrace::TRACE;
183              
184 0         0 my ($p, $f, $l) = caller();
185 0         0 my (undef, undef, undef, $sub) = caller(1);
186 0   0     0 $sub ||= '__top__';
187 0         0 $sub =~ s/::$/::__top__/;
188              
189 0 0       0 if ($DB::single < 2) {
190 0 0       0 return if _exclude_pkg($f,$p,$l);
191 0 0       0 return if _display_style() == DISPLAY_NONE;
192             }
193              
194 0         0 handle_deferred_output($sub, $f);
195 0         0 my $code = get_source($f,$l);
196              
197 0         0 save_pads(1);
198 0         0 save_previous_regex_matches();
199 0         0 evaluate_and_display_line($code, $p, $f, $l, $sub);
200 0         0 return;
201             }
202              
203             sub get_source {
204 0     0 0 0 my ($file, $line) = @_;
205 16     16   95 no strict 'refs';
  16         30  
  16         8083  
206              
207 0 0       0 if (!defined $sources{$file}) {
208             # die "source not available for $file ...\n";
209 0         0 my $source_key = "::_<" . $file;
210 0         0 eval {
211 0         0 $sources{$file} = [ @{$source_key} ]
  0         0  
212             };
213 0 0       0 if ($@) {
214             # this happens when we are poking around the symbol table?
215             # are we corrupting the source file data somehow?
216              
217 0         0 $sources{$file} = [
218             ("SOURCE NOT AVAILABLE FOR file $file: $@") x 999
219             ];
220 0 0       0 if (open my $grrrrr, '<', $file) {
221 0         0 $sources{$file} = [ "", <$grrrrr> ];
222 0         0 warn "Source for \"$file\" not loaded ",
223             "automatically at debugger level ...\n";
224 0         0 close $grrrrr;
225             }
226             }
227             }
228 0         0 return $sources{$file}->[$line];
229             }
230              
231             sub _exclude_pkg {
232 0     0   0 my($file,$pkg,$line) = @_;
233              
234 0 0 0     0 return 0 if $INCLUDE_PKG{$pkg} || $INCLUDE_PKG{$file};
235 0         0 foreach (@INCLUDE_PATTERN) {
236 0 0       0 if ($pkg =~ $_) {
237 0         0 $INCLUDE_PKG{$pkg} = 1;
238 0         0 return 0;
239             }
240             }
241              
242 0 0 0     0 return 1 if $EXCLUDE_PKG{$pkg} || $EXCLUDE_PKG{$file};
243 0         0 foreach (@EXCLUDE_PATTERN) {
244 0 0       0 if ($pkg =~ $_) {
245 0 0       0 return $EXCLUDE_PKG{$pkg}=1 if $pkg =~ $_;
246             }
247             }
248 0 0       0 return 0 if _package_style() > DISPLAY_NONE;
249              
250             # exclude files from @_INC when _package_style() is 0
251 0         0 foreach my $inc (@_INC) {
252 0 0 0     0 if (index($inc,"/") >= 0 && index($file,$inc) == 0) {
253 0         0 return $EXCLUDE_PKG{$file} = $EXCLUDE_PKG{$pkg} = 1;
254             }
255             }
256 0         0 $INCLUDE_PKG{$pkg} = 1;
257              
258 0         0 return 0;
259             }
260              
261             # map $TRACE variable to a display style
262             sub _display_style_old {
263 16 100   16   91 return DISPLAY_TERSE if $TRACE eq 'default'; # 5.8.8 bug?
264 12         69 return (DISPLAY_TERSE,
265             DISPLAY_TERSE,
266             DISPLAY_TERSE,
267             DISPLAY_TERSE,
268             DISPLAY_GABBY,
269             DISPLAY_GABBY,
270             DISPLAY_GABBY,
271             DISPLAY_GABBY,
272             DISPLAY_GABBY,
273             DISPLAY_GABBY)[$TRACE % 10];
274             }
275             sub _display_style_new {
276 0     0   0 return (DISPLAY_TERSE,
277             DISPLAY_TERSE,
278             DISPLAY_TERSE,
279             DISPLAY_TERSE,
280             DISPLAY_TERSE,
281             DISPLAY_GABBY,
282             DISPLAY_GABBY,
283             DISPLAY_GABBY,
284             DISPLAY_GABBY,
285             DISPLAY_GABBY)[$TRACE % 10];
286             }
287              
288             # map $TRACE variable to an abbreviation style
289             sub _abbrev_style_old {
290 1368     1368   2257 return (ABBREV_SMART,
291             ABBREV_SMART,
292             ABBREV_MILD_SM,
293             ABBREV_NONE,
294             ABBREV_MILD_SM,
295             ABBREV_NONE,
296             ABBREV_NONE,
297             ABBREV_NONE,
298             ABBREV_NONE,
299             ABBREV_NONE,)[$TRACE % 10]
300             }
301             sub _abbrev_style_new {
302 0     0   0 return (ABBREV_SMART,
303             ABBREV_SMART,
304             ABBREV_STRONG,
305             ABBREV_MILD_SM,
306             ABBREV_MILD,
307             ABBREV_NONE,
308             ABBREV_SMART,
309             ABBREV_STRONG,
310             ABBREV_MILD_SM,
311             ABBREV_NONE,)[$TRACE % 10]
312             }
313              
314             BEGIN {
315 16     16   81 *_display_style = *_display_style_old;
316 16         20348 *_abbrev_style = *_abbrev_style_old;
317             }
318              
319             sub _package_style {
320 0     0   0 return $TRACE >= 100;
321             }
322              
323             sub save_pads {
324 11   50 11 0 2665 my $n = shift || 0;
325 11         33 my $target_depth = current_depth() - $n - 1;
326              
327 11 50       32 if ($target_depth < 0) {
328 0         0 Carp::cluck "save_pads: request for negative frame ",
329             current_depth(), " $target_depth $n at ";
330 0         0 return;
331             }
332 11 50       31 if ($n < 0) {
333 0         0 Carp::cluck "save_pads: request for shallow frame ",
334             current_depth(), " $target_depth $n at ";
335 0         0 return;
336             }
337              
338             eval {
339 11         176 $PAD_MY = PadWalker::peek_my($n + 1);
340 11         81 $PAD_OUR = PadWalker::peek_our($n + 1);
341 11         46 1;
342 11 50       19 } or do {
343 0         0 Carp::confess("$@ from PadWalker: \$n=$n is too large.\n",
344             "Target depth was $target_depth\n");
345             };
346              
347             # add extra data to the pads so that they can be refreshed
348             # at an arbitrary point in the future
349 11         28 $PAD_MY->{__DEPTH__} = $PAD_OUR->{__DEPTH__} = current_depth() - $n - 1;
350              
351 11         28 return;
352             }
353              
354             sub current_depth {
355 22     22 0 36 my $n = 0;
356 22         88 $n++ while caller($n);
357 22         71 return $n-1;
358             }
359              
360             sub refresh_pads {
361 0 0   0 0 0 return if __inGD();
362 0         0 my $current = current_depth();
363 0         0 my $target = $PAD_MY->{__DEPTH__};
364 0 0       0 if ($current >= $target) {
365 0         0 save_pads($current - $target);
366             }
367             # $current < $target
368 0         0 return;
369             }
370              
371             our $last_dumptrace = '';
372             my @dt_prefix = (" ", ### not used
373             "> ", ### not used
374             ">> ", # to display current file/line/sub
375             ">>> ", # raw statement
376             ">>>> ", # with var substitution, before execution
377             ">>>>> ", # with var substitution after execution
378             " \t ", ### not used
379             "> \t ", ### not used
380             ">> \t ", ### not used
381             ">>> \t ", # raw statetment
382             ">>>> \t ", # with var substitution, before execution
383             ">>>>>\t ", # with var substitution after execution
384             "");
385              
386             sub dumptrace {
387 0     0 0 0 my ($n, $tab, @output) = @_;
388 0         0 my $dt = join ('', @output);
389 0         0 my $out = $dt_prefix[$n+6*!!$tab] . $dt;
390 0 0 0     0 if ($last_dumptrace && $dt eq $last_dumptrace) {
391             # duplicate
392 0         0 return;
393             }
394              
395 0         0 $last_dumptrace = $dt;
396 0 0       0 if ($DUMPTRACE_COLOR) {
397 0         0 our $DUMPTRACE_RESET;
398 0         0 $out = join $/, map( $DUMPTRACE_COLOR . $_ . $DUMPTRACE_RESET,
399             split($/,$out)), "";
400             }
401 0 0       0 our $LOCKOBJ && lock(my $lock = \$LOCKOBJ);
402 0         0 print {$DUMPTRACE_FH} $out;
  0         0  
403             }
404              
405             sub evaluate_and_display_line {
406 0     0 0 0 my ($code, $p, $f, $l, $sub) = @_;
407 0         0 my $style = _display_style();
408              
409 0 0       0 if ($style > DISPLAY_TERSE) {
410 0         0 separate();
411 0         0 dumptrace(2,0, current_position_string($f,$l,$sub), "\n");
412 0         0 dumptrace(3,1,$code);
413             }
414              
415             # look for assignment operator.
416 0   0     0 $DEFERRED{"$sub : $f"} ||= [];
417 0 0 0     0 if ($code =~ m{[-+*/&|^.%]?=[^=>]}
418             || $code =~ m{[\b*&|/<>]{2}=\b} ) {
419              
420 0         0 my ($expr1, $op, $expr2) = ($`, $&, $'); # ');
421              
422 0 0       0 if ($style < DISPLAY_GABBY) {
423 0         0 $expr2 = perform_extended_variable_substitutions($op . $expr2, $p);
424             } else {
425 0         0 $expr2 = perform_variable_substitutions($op . $expr2, $p);
426             }
427              
428 0         0 push @{$DEFERRED{"$sub : $f"}},
  0         0  
429             { PACKAGE => $p,
430             MY_PAD => $PAD_MY,
431             OUR_PAD => $PAD_OUR,
432             SUB => $sub,
433             FILE => $f,
434             LINE => $l,
435             DISPLAY_FILE_AND_LINE => $style <= DISPLAY_TERSE,
436             EXPRESSION => [ $expr1, $expr2 ]
437             };
438              
439 0 0       0 if ("$expr1$expr2" ne $code) {
440 0 0       0 if ($style >= DISPLAY_GABBY) {
441 0         0 dumptrace(4,1,$expr1,$expr2);
442             }
443             }
444 0         0 return;
445             } else {
446 0         0 push @{$DEFERRED{"$sub : $f"}}, undef;
  0         0  
447             }
448              
449 0         0 my $xcode;
450              
451             # if this is a simple lexical declaration and NOT an assignment,
452             # then don't perform variable substitution:
453             # my $k;
454             # my ($a,$b,@c);
455             # our $ZZZ;
456              
457 0 0 0     0 if ($code =~ /^ \s* (my|our) \s*
      0        
458             [\$@%*\(] /x # lexical declaration
459             && $code =~ / (?
460             \s* (\# .* )? $/x # single statement, single line
461             && $code !~ /=/) { # NOT an assignment
462              
463 0         0 $xcode = $code;
464              
465             } else {
466              
467 0         0 $xcode = perform_variable_substitutions($code, $p);
468              
469             }
470              
471 0 0       0 if ($style >= DISPLAY_GABBY) {
    0          
472 0 0       0 if ($xcode ne $code) {
473 0         0 dumptrace(4,1,$xcode);
474             }
475             } elsif ($style == DISPLAY_TERSE) {
476 0         0 dumptrace(4,0, current_position_string($f,$l,$sub),
477             "\t $xcode");
478             }
479 0         0 return;
480             }
481              
482             sub separate {
483 0     0 0 0 our $SEPARATOR_USED;
484 0 0       0 $SEPARATOR_USED++ && dumptrace(-1, 0, $SEPARATOR);
485 0         0 return;
486             }
487              
488             # a guard against deep recursion in dump_scalar subroutine
489             my %_dump_scalar_seen = ();
490              
491             sub _reset_dump {
492 193     193   256 %_dump_scalar_seen = ();
493             }
494              
495             sub dump_scalar {
496 1228     1228 0 2471 my $scalar = $_[0];
497             # was my $scalar = shift and was my ($scalar) = @_;
498             # but they caused "Modification of a read-only value attempted"
499             # error with Perl 5.8.8
500              
501 1228 100       3030 return 'undef' if !defined $scalar;
502 1159 100       1444 if (ref $scalar) {
503 42 100       122 if ($_dump_scalar_seen{$scalar}) {
504 1         4 return "... $scalar (prev. referenced)";
505             }
506 41         80 $_dump_scalar_seen{$scalar}++;
507 41         42 my $z;
508 41 100       139 if (Scalar::Util::reftype($scalar) eq 'ARRAY') {
    100          
    50          
509 22         42 $z = '[' . array_repr($scalar) . ']';
510             } elsif (Scalar::Util::reftype($scalar) eq 'HASH') {
511 11         25 $z = '{' . hash_repr($scalar) . '}';
512             } elsif (Scalar::Util::reftype($scalar) eq 'GLOB') {
513 0         0 $z = $scalar;
514             } else {
515 8         14 $z = "$scalar";
516             }
517 41         99 delete $_dump_scalar_seen{$scalar};
518 41         154 return $z;
519             }
520 1117 100       1908 if (Scalar::Util::looks_like_number($scalar)) {
521 464         762 $scalar =~ s/^\s+//;
522 464         579 $scalar =~ s/\s+$//;
523 464         540 return _abbreviate_scalar($scalar);
524             }
525 653 100       1056 if (ref \$scalar eq 'GLOB') {
526 4         15 return $scalar;
527             }
528 649         819 my $qq = _qquote($scalar);
529 649 100       1006 if ($qq ne $scalar) {
530 1         3 return _abbreviate_scalar(qq("$qq"));
531             }
532 648         1112 return _abbreviate_scalar(qq('$scalar'));
533             }
534              
535             sub _abbreviate_scalar {
536 1113     1113   1440 my ($value) = @_;
537 1113 50       1286 if (_abbrev_style() >= ABBREV_NONE) {
538 1113         2881 return $value;
539             }
540 0 0       0 if (_abbrev_style() > ABBREV_STRONG) {
541             # mild abbreviation: no token longer than 80 chars
542 0         0 return Text::Shorten::shorten_scalar($value, 80);
543             } else {
544             # strong abbreviation: no token longer than 20 chars
545 0         0 return Text::Shorten::shorten_scalar($value, 20);
546             }
547             }
548              
549             # shamelessly lifted from Data::Dumper::qquote
550             #
551             # converts a string of arbitrary characters to an ASCII string that
552             # produces the original string under double quote interpolation
553             sub _qquote {
554 649     649   883 local($_) = shift;
555 649         906 s/([\\\"\@\$])/\\$1/g;
556 16     16   8107 my $bytes; { use bytes; $bytes = length }
  16         186  
  16         135  
  649         594  
  649         609  
  649         672  
557 649 50       903 ($bytes > length) && s/([^\x00-\x7f])/'\x{'.sprintf("%x",ord($1)).'}'/ge;
  0         0  
558 649 100       1422 /[^ !"\#\$%&'()*+,\-.\/0-9:;<=>?\@A-Z[\\\]^_`a-z{|}~]/ || return $_;
559              
560 1   50     6 my $high = shift || '';
561 1         6 s/([\a\b\t\n\f\r\e])/$esc{$1}/g;
562              
563 1         2 if (ord('^')==94) {
564             # no need for 3 digits in escape for these
565 1         10 s/([\0-\037])(?!\d)/'\\'.sprintf('%o',ord($1))/eg;
  0         0  
566 1         2 s/([\0-\037\177])/'\\'.sprintf('%03o',ord($1))/eg;
  0         0  
567             # all but last branch below not supported --BEHAVIOR SUBJECT TO CHANGE--
568 1 50       8 if ($high eq 'iso8859') {
    50          
    50          
569 0         0 s/([\200-\240])/'\\'.sprintf('%o',ord($1))/eg;
  0         0  
570             } elsif ($high eq 'utf8') {
571             # use utf8;
572             # $str =~ s/([^\040-\176])/sprintf "\\x{%04x}", ord($1)/ge;
573             } elsif ($high eq '8bit') {
574             # leave it as it is
575             } else {
576 1         3 s/([\200-\377])/'\\'.sprintf('%03o',ord($1))/eg;
  0         0  
577 1         2 s/([^\040-\176])/sprintf "\\x{%04x}", ord($1)/ge;
  0         0  
578             }
579             } else { # ebcdic
580             s{([^ !"\#\$%&'()*+,\-.\/0-9:;<=>?\@A-Z[\\\]^_`a-z{|}~])(?!\d)}
581             {my $v = ord($1); '\\'.sprintf(($v <= 037 ? '%o' : '%03o'), $v)}eg;
582             s{([^ !"\#\$%&'()*+,\-.\/0-9:;<=>?\@A-Z[\\\]^_`a-z{|}~])}
583 1         3 {'\\'.sprintf('%03o',ord($1))}eg;
584             }
585             return $_;
586             }
587 82     82 0 884  
588             sub hash_repr {
589 82 100       145 my ($hashref, @keys) = @_;
590 81 50       128  
591 81 100 66     292 return '' if !defined $hashref;
592             @keys = () unless $SMART_ABBREV;
593 81 0       130 my $ref = ref $hashref && ref $hashref ne 'HASH'
    50          
594             ? ref($hashref) . ': ' : '';
595 81         262 my $maxlen = _abbrev_style() < ABBREV_NONE
596             ? _abbrev_style() > ABBREV_STRONG ? 79 : 19 : -1;
597 81         147 my $cache_key = join ':',
598             $maxlen, $HASH_ENTRY_SEPARATOR, $HASH_PAIR_SEPARATOR;
599             my $hash;
600              
601             # When the hash table gets large, tie it to
602             # Devel::DumpTrace::CachedDisplayedHash and
603             # see if we can avoid some expensive calls
604 81 50 50     264 # to Text::Shorten::shorten_hash .
605              
606 0         0 if ((Scalar::Util::reftype($hashref)||'') ne 'HASH') {
607             # this can happen with globs, e.g., $$glob->{attribute} = value;
608             return "$hashref";
609 81 50 33     311 }
    50 66        
      66        
      33        
610              
611             if (@keys == 0 &&
612 0         0 Devel::DumpTrace::CachedDisplayedHash->is($hashref)) {
  0         0  
613 0 0       0  
614             my $result = (tied %{$hashref})->get_cache($cache_key);
615 0         0 if (defined $result) {
  0         0  
  0         0  
  0         0  
616             return $ref . join $HASH_ENTRY_SEPARATOR,
617 0         0 map { join $HASH_PAIR_SEPARATOR, @{$_} } _condsort(@{$result});
  0         0  
618 81         279 }
619             $hash = (tied %{$hashref})->{PHASH};
620             } elsif (!tied(%{$hashref})
621             && @keys == 0
622             && !__hashref_is_symbol_table($hashref)
623              
624             # use safekeys in case this DB hook is inside an `each` iterator
625 0         0 && 100 < scalar safekeys %$hashref) {
626 0         0  
  0         0  
627 0         0 my $cdh = tie %{$hashref}, 'Devel::DumpTrace::CachedDisplayedHash',
628             %{$hashref};
629             $hash = $cdh->{PHASH};
630 81         861 } else {
631 81         254 # Hash::SafeKeys::safekeys will not reset an active `each` iterator
  430         905  
632             my $it = Hash::SafeKeys::save_iterator_state($hashref);
633 81         233 $hash = { map { dump_scalar($_) => dump_scalar($hashref->{$_}) }
634             keys %$hashref };
635             Hash::SafeKeys::restore_iterator_state($hashref,$it);
636 81         113 }
637 81 50       110  
638 0         0 my @r;
639             if (_abbrev_style() < ABBREV_NONE) {
640 0         0 local $Text::Shortern::HASHREPR_SORTKEYS
641             = $Devel::DumpTrace::HASHREPR_SORT;
642             @r = Text::Shorten::shorten_hash(
643             $hash, $maxlen,
644             $HASH_ENTRY_SEPARATOR,
645             $HASH_PAIR_SEPARATOR, @keys );
646 81         185 } else {
647 81         189 # safekeys does not reset an active `each` iterator (RT#77673)
  430         650  
648 81         155 my $it = Hash::SafeKeys::save_iterator_state($hash);
649             @r = map { [ $_ => $hash->{$_} ] } _condsort(keys %$hash);
650             Hash::SafeKeys::restore_iterator_state($hash,$it);
651 81 50 33     268 }
652 0         0  
  0         0  
653             if (@keys == 0 && Devel::DumpTrace::CachedDisplayedHash->is($hashref)) {
654             (tied %{$hashref})->store_cache($cache_key, \@r);
655 81 50       134 }
656 0         0  
657 0         0 if (!defined $HASH_PAIR_SEPARATOR) {
658             Carp::cluck("setting \$HASH_PAIR_SEPARATOR definition ...");
659             $HASH_PAIR_SEPARATOR = " =======> ";
660             }
661 81         118  
662 430 50       393 return $ref . join ($HASH_ENTRY_SEPARATOR,
  860         1800  
  430         442  
663             map { join ($HASH_PAIR_SEPARATOR,
664             map{defined($_)?$_:'undef'}@{$_}) } @r );
665             }
666              
667 81 100   81   249 # sort an array iff $Devel::DumpTrace::HASHREPR_SORT is set.
668             sub _condsort {
669             $Devel::DumpTrace::HASHREPR_SORT ? sort @_ : @_;
670             }
671              
672             sub __hashref_is_symbol_table {
673             # if we pass a reference to a symbol table in repr_hash,
674             # we don't want to tie it to a D::DT::CachedDisplayedHash.
675             #
676             # Don't know if this is the best method or if it is
677 16     16   14425 # perfectly reliable, but it is getting there ...
  16         33  
  16         18646  
678 79     79   127  
679 79         207 use B;
680 79   33     573 my ($hashref) = @_;
681             my $sv = B::svref_2object($hashref);
682             return ref($sv) eq 'B::HV' && $sv->NAME;
683             }
684 94     94 0 1252  
685             sub array_repr {
686 94 100       170 my ($arrayref, @keys) = @_;
687 93 50       156  
688 93 100 66     338 return '' if !defined $arrayref;
689             @keys = () unless $SMART_ABBREV;
690 93 0       156 my $ref = ref $arrayref && ref $arrayref ne 'ARRAY'
    50          
691             ? ref($arrayref) . ': ' : '';
692 93         223 my $maxlen = _abbrev_style() < ABBREV_NONE
693 93         110 ? _abbrev_style() > ABBREV_STRONG ? 79 : 19 : -1;
694             my $cache_key = join ':', $maxlen, $ARRAY_ELEM_SEPARATOR;
695             my $array;
696              
697             # When the array gets large, tie it to
698             # Devel::DumpTrace::CachedDisplayedArray and
699             # see if we can avoid some expensive calls
700 93 50 33     390 # to Text::Shorten::shorten_array .
    50 33        
      33        
701              
702             if (@keys == 0
703 0         0 && Devel::DumpTrace::CachedDisplayedArray->is($arrayref)) {
  0         0  
704 0 0       0  
705 0         0 my $result = (tied @{$arrayref})->get_cache($cache_key);
706             if (defined $result) {
707 0         0 return $ref . join $ARRAY_ELEM_SEPARATOR, @$result;
  0         0  
708 93         250 }
  93         184  
709             $array = (tied @{$arrayref})->{PARRAY};
710 0         0 } elsif (@keys==0 && !tied(@{$arrayref}) && 100 < scalar @{$arrayref}) {
711 0         0 eval {
  0         0  
712 0         0 tie @{$arrayref}, 'Devel::DumpTrace::CachedDisplayedArray',
  0         0  
713 0 0       0 @{$arrayref};
714 0         0 $array = (tied @{$arrayref})->{PARRAY};
  0         0  
  0         0  
715             } or do {
716             $array = [ map { dump_scalar($_) } @{$arrayref} ];
717 93         106 };
  278         413  
  93         137  
718             } else {
719             $array = [ map { dump_scalar($_) } @{$arrayref} ];
720 93         160 }
721 93 50       153  
722 0         0 my @r;
723             if ($maxlen > 0) {
724             @r = Text::Shorten::shorten_array(
725 93         96 $array, $maxlen, $ARRAY_ELEM_SEPARATOR, @keys);
  93         188  
726             } else {
727 93 50 33     321 @r = @{$array};
728             }
729 0         0 if (@keys == 0
  0         0  
730             && Devel::DumpTrace::CachedDisplayedArray->is($arrayref)) {
731 93         550 (tied @{$arrayref})->store_cache($cache_key, \@r);
732             }
733             return $ref . join $ARRAY_ELEM_SEPARATOR, @r;
734             }
735 16     16 0 57  
736 0         0 sub handle_ALL_deferred_output {
737 0         0 foreach my $context (keys %DEFERRED) {
738             my ($sub, $file) = split / : /, $context, 2;
739 16 50       58 handle_deferred_output($sub, $file);
740 16         34 }
741             separate() if _display_style() > DISPLAY_TERSE;
742             return;
743             }
744 0     0 0 0  
745 0         0 sub handle_deferred_output {
  0         0  
746 0         0 my ($sub, $file) = @_;
747             my $deferred = pop @{$DEFERRED{"$sub : $file"}};
748 0 0       0 delete $DEFERRED{"$sub : $file"};
749              
750 0         0 if (defined $deferred) {
  0         0  
751 0         0  
752 0         0 my ($expr1, $expr2) = @{$deferred->{EXPRESSION}};
753 0         0 my $deferred_pkg = $deferred->{PACKAGE};
754 0         0 $PAD_MY = $deferred->{MY_PAD};
755 0         0 $PAD_OUR = $deferred->{OUR_PAD};
756 0         0 refresh_pads();
757 0         0 $PAD_MY->{__STALE__} = $deferred->{MY_PAD};
758 0 0       0 $PAD_OUR->{__STALE__} = $deferred->{OUR_PAD};
759 0         0 my ($line);
760 0         0 if ($deferred->{DISPLAY_FILE_AND_LINE}) {
761             $file = $deferred->{FILE};
762 0         0 $line = $deferred->{LINE};
763 0 0       0 }
764             my $output = $expr2;
765 0         0 if (defined($line)) {
766             $output = current_position_string($file,$line,$deferred->{SUB})
767             . "\t" .
768             perform_extended_variable_substitutions($expr1, $deferred_pkg)
769 0         0 . $output;
770             } else {
771             $output = "\t "
772             . perform_variable_substitutions($expr1, $deferred_pkg)
773 0         0 . $output;
774             }
775 0         0 dumptrace(5,0,$output);
776             }
777             return;
778             }
779 107     107 0 20227  
780 107         661 sub perform_variable_substitutions {
781             my ($xcode, $pkg) = @_;
782             $xcode =~ s{ ([\$\@\%])\s* # sigil
783             ([\w:]+) # package (optional) and var name
784             (\s*->)? # optional dereference op
785 122   100     658 (\s*[\[\{])? # optional subscript
      100        
786             }{
787             evaluate($1,$2,$3||'',$4||'',$pkg)
788 107         284 }gex;
789              
790             return $xcode;
791             }
792              
793 0     0 0 0 my %output_count;
794 0         0 sub current_position_string {
795             my ($file, $line, $sub) = @_;
796             if (OUTPUT_COUNT) {
797             my $cnt = ++$output_count{$file}{$line};
798 0         0 $line .= "\[$cnt\]";
799             }
800             if (OUTPUT_TIME) {
801             if ($Time_HiRes_avail) {
802             $file = sprintf "%.3f:%s", Time::HiRes::time()-$^T, $file;
803             } else {
804             $file = sprintf "t=%d:%s", time-$^T, $file;
805 0         0 }
806 0   0     0 }
807             if (OUTPUT_SUB) {
808             $sub ||= '__top__';
809             # $file already probably contains package information.
810 0         0 # Keeping it in $sub is _usually_ redundant and makes the
811             # line too long.
812 0         0 $sub =~ s/.*:://;
813              
814             if (OUTPUT_PID) {
815             my $p = $$;
816             if ($_THREADS) {
817             $p .= eval { "-t" . threads->tid() }; warn $@ if $@;
818             }
819 0         0 return "$p:$file:$line:[$sub]:";
820             } else {
821             return "$file:$line:[$sub]:";
822             }
823             } elsif (OUTPUT_PID) {
824             my $p = $$;
825             if ($_THREADS) {
826             $p .= eval { "-t" . threads->tid() }; warn $@ if $@;
827             }
828             return "$p:$file:$line:";
829             } else {
830             return "$file:$line:";
831             }
832             }
833 59     59 0 13827  
834 59         376 sub perform_extended_variable_substitutions {
835             my ($xcode, $pkg) = @_;
836             $xcode =~ s{ ([\$\@\%])\s* # sigil
837             ([\w:]*\w)(?!:) # var name, may incl. pkg, ends in alphanum
838 70   100     444 (\s*->)? # optional dereference op
      100        
839             (\s*[\[\{])? # optional subscript
840             }{ $1 . $2 . $XEVAL_SEPARATOR
841 59         144 . evaluate($1,$2,$3||'',$4||'',$pkg)
842             }gex;
843             return $xcode;
844             }
845 8     8 0 18  
846 8         12 sub get_DB_args {
847 8         21 my $depth = 1 + shift;
848 8 50       19 my @z;
849             for (my $i=$depth; $i<=$depth; $i++) {
850 8         85 if ($i>=0) {
851 8 50       35 package DB;
852             my @y = caller($depth);
853             return if @y==0;
854             }
855              
856             # best efforts here. Sometimes this assignment gives a
857 8     1   590 # "Bizarre copy of ARRAY in aassign" error message
  1     1   9  
  1     1   2  
  1     1   45  
  1     1   8  
  1     1   3  
  1     1   31  
  1     1   9  
  1         3  
  1         31  
  1         7  
  1         3  
  1         33  
  1         8  
  1         87  
  1         39  
  1         8  
  1         3  
  1         31  
  1         8  
  1         2  
  1         59  
  1         8  
  1         2  
  1         31  
858             # (when $depth is too deep and @DB::args is not defined?).
859 8         43 eval 'no warnings q/internal/; @z = @DB::args';
860             }
861             return @z;
862             }
863              
864 193     193 0 812 # McCabe score: 49
865             sub evaluate {
866 193         246 my ($sigil, $varname, $deref_op, $index_op, $pkg, @keys) = @_;
867 193         357 # return unless defined($sigil) && $sigil ne '';
868             my $v;
869 16     16   107 _reset_dump();
  16         29  
  16         6691  
870              
871 193   100     486 no strict 'refs';
872 193   100     391  
873 193         282 $deref_op ||= '';
874             $index_op ||= '';
875 193 100 100     669 $index_op =~ s/^\s+//;
876 10         14  
877             if ($ALWAYS_MAIN{$varname} || $varname =~ /^\d+$/) {
878 193         328 $pkg = 'main';
879 193 100 100     574 }
880 15         17 $pkg .= '::';
881             if ($varname =~ /::/ || $pkg eq '::') {
882             $pkg = '';
883 193 100       285 }
884 42         56  
885 42         127 if ($deref_op) {
886             my $sigvar = "\$$varname";
887 42 100       131 (my $pkgvar = $sigvar) =~ s/\$/\$$pkg/;
    100          
    50          
    50          
888 14         21  
889             if (defined $PAD_MY->{$sigvar}) {
890 14         20 $v = $PAD_MY->{$sigvar};
891             } elsif (defined $PAD_OUR->{$sigvar}) {
892 0         0 $v = $PAD_OUR->{$sigvar};
893             } elsif (defined $PAD_MY->{__STALE__}{$sigvar}) {
894 0         0 $v = $PAD_MY->{__STALE__}{$sigvar};
895             } elsif (defined $PAD_OUR->{__STALE__}{$sigvar}) {
896 14         628 $v = $PAD_OUR->{__STALE__}{$sigvar};
897             } else {
898 42 100       83 $v = eval "\\$pkgvar";
899 12         14 }
  12         22  
900             if ($index_op eq '[') {
901 30 100       49 return '[' . array_repr(${$v}, @keys) . ']->[';
902 12         14 }
  12         26  
903             if ($index_op eq '{') {
904             return '{' . hash_repr(${$v}, @keys) . '}->{';
905 18         16 }
  18         45  
906 18 50 33     64  
    100          
    100          
907 0         0 my $reftype = Scalar::Util::reftype(${$v});
908             if (!defined($reftype) || $reftype eq '') {
909 6         7 return '(' . dump_scalar($v) . ')->';
  6         19  
910             } elsif ($reftype eq 'HASH') {
911 6         6 return '{' . hash_repr(${$v}, @keys) . '}->';
  6         10  
912             } elsif ($reftype eq 'ARRAY') {
913 6         12 return '[' . array_repr(${$v}, @keys) . ']->';
914             } else {
915             return '(' . dump_scalar($v) . ')->';
916             }
917 151 100       256 }
918 25         35  
919 25         77 if ($index_op eq '{') {
920 25 100       65 my $sigvar = "\%$varname";
    100          
921 7         12 (my $pkgvar = $sigvar) =~ s/\%/\%$pkg/;
922             if (defined($PAD_MY->{$sigvar})) {
923 7         17 $v = $PAD_MY->{$sigvar};
924             } elsif (defined($PAD_OUR->{$sigvar})) {
925 11         491 $v = $PAD_OUR->{$sigvar};
926             } else {
927 25         69 $v = eval "\\$pkgvar";
928             }
929 126 100       176 return '(' . hash_repr($v, @keys) . '){';
930 21         43 }
931 21         82 if ($sigil eq '@') {
932             my $sigvar = "\@$varname";
933 21 100       81 (my $pkgvar = $sigvar) =~ s/\@/\@$pkg/;
    100          
    100          
934              
935             if ($varname eq '_') {
936             # calling caller (1) with arg, (2) in list context,
937 2         6 # (3) from DB package will populate @DB::args, which is
938 16     16   96 # what we really want.
  16         30  
  16         10979  
939 2         21 my $depth = $DB_ARGS_DEPTH;
940 0         0 no warnings 'uninitialized';
941             while ((caller $depth)[CALLER_SUB] =~ /^\(eval/) {
942 2         10 $depth++;
943             }
944 8         16 $v = [ get_DB_args($depth) ];
945             } elsif (defined($PAD_MY->{$sigvar})) {
946 4         9 $v = $PAD_MY->{$sigvar};
947             } elsif (defined($PAD_OUR->{$sigvar})) {
948 7         10 $v = $PAD_OUR->{$sigvar};
949 7         306 } else {
950             eval {
951 7 50       26 $v = eval "\\" . $pkgvar;
952 0         0 };
  0         0  
953             if (!defined $v) {
954 0         0 print {$DUMPTRACE_FH} "Devel::DumpTrace: ",
955             "Couldn't find $sigvar/$pkgvar in any appropriate scope.\n";
956             $v = [];
957 21 100       55 }
958 8         18 }
959             if ($index_op eq '[') {
960 13         34 return '(' . array_repr($v, @keys) . ')[';
961             }
962 105 100       184 return '(' . array_repr($v, @keys) . ')';
963 19         31 }
964 19         74 if ($sigil eq '%') {
965 19 100       70 my $sigvar = "\%$varname";
    100          
966 5         11 (my $pkgvar = $sigvar) =~ s/\%/\%$pkg/;
967             if (defined($PAD_MY->{$sigvar})) {
968 5         9 $v = $PAD_MY->{$sigvar};
969             } elsif (defined($PAD_OUR->{$sigvar})) {
970 9         395 $v = $PAD_OUR->{$sigvar};
971             } else {
972 19         52 $v = eval "\\$pkgvar";
973             }
974 86 50       131 return '(' . hash_repr($v) . ')';
975 86 100       217 }
    100          
976 23         41 if ($sigil eq '$') {
977 23         99 if ($index_op eq '[') {
978 23 100       78 my $sigvar = "\@$varname";
    100          
    100          
979 6         12 (my $pkgvar = $sigvar) =~ s/\@/\@$pkg/;
980 6         14 if ($varname eq '_') {
981             my $depth = $DB_ARGS_DEPTH;
982 6         14 $v = [ get_DB_args($depth) ];
983             } elsif (defined($PAD_MY->{$sigvar})) {
984 4         7 $v = $PAD_MY->{$sigvar};
985             } elsif (defined($PAD_OUR->{$sigvar})) {
986 7         10 $v = $PAD_OUR->{$sigvar};
  7         320  
987 7 50       28 } else {
988 0         0 eval { $v = eval "\\$pkgvar" };
  0         0  
989             if (!defined $v) {
990 0         0 print {$DUMPTRACE_FH} "Devel::DumpTrace: ",
991             "Couldn't find $sigvar/$pkgvar in any appropriate scope.\n";
992             $v = [];
993 23         58 }
994             }
995             return '(' . array_repr($v, @keys) . ')[';
996             } elsif ($varname =~ /^\d+$/) {
997 7         10 # special regex match var $1,$2,...
998 7         13 # they were loaded into @matches in save_previous_regex_matches()
999             $v = $matches[$varname];
1000             return dump_scalar($v);
1001 56         95 } else {
1002 56 100       78  
1003 2         3 my $sigvar = "\$$varname";
1004             if ($varname eq '_') {
1005 56         182 $pkg = 'main::';
1006             }
1007 56 100       150 (my $pkgvar = $sigvar) =~ s/\$/\$$pkg/;
    100          
1008 24         28  
  24         41  
1009             if (defined($PAD_MY->{$sigvar})) {
1010 12         16 $v = ${$PAD_MY->{$sigvar}};
  12         19  
1011             } elsif (defined($PAD_OUR->{$sigvar})) {
1012 20         860 $v = ${$PAD_OUR->{$sigvar}};
1013             } else {
1014 56         132 $v = eval "$pkgvar";
1015             }
1016             return dump_scalar($v);
1017             }
1018 0         0 }
1019              
1020             Carp::confess 'No interpolation done for input: ',
1021             "
1022             "index:$index_op ; pkg:$pkg>\n"
1023             }
1024 2     2 0 861  
1025             sub save_previous_regex_matches {
1026             @matches = ($0,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,
1027             $11,$12,$13,$14,$15,$16,$17,$18,$19,$20,
1028             $21,$22,$23,$24,$25,$26,$27,$28,$29,$30,);
1029              
1030             # XXX - if someone needs more than $30, submit a feature request
1031             # (http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Devel-DumpTrace,
1032             # or email to bug-Devel-DumpTrace@rt.cpan.org)
1033 2         3 # and I'll figure something out ...
1034              
1035             return;
1036             }
1037              
1038             # RT#76864
1039             {
1040             # Devel::DumpTrace is typically loaded before any other module
1041             # (from the -d:DumpTrace switch). Running this thread-specific
1042 16     16   105 # code in a CHECK block gives the traced program a chance to
  16         24  
  16         1535  
1043             # load threads later.
1044 15 50   15   729763 no warnings 'void';
1045 0         0 CHECK {
1046 0         0 if ($INC{'threads.pm'}) {
1047 0         0 $_THREADS = 1;
1048 0         0 require threads::shared;
1049             our $LOCKOBJ = 1; # to synchronize access to output stream
1050             threads::shared::share(\$LOCKOBJ);
1051             }
1052             };
1053             }
1054 16     16   130  
  16         32  
  16         3730  
1055 16     0   139 sub __END {
        16      
1056 16         78 no warnings 'redefine';
1057 16         130 *DB::DB = sub { };
1058 16 50 33     120 *__inGD = sub () { 1 };
1059 16         139 untie $TRACE;
1060             handle_ALL_deferred_output() unless $_THREADS && threads->tid();
1061             1;
1062 16     16   12042 }
1063              
1064             END { &__END; }
1065              
1066              
1067             ##################################################################
1068             # Devel::DumpTrace::VerboseLevel: tie class for $Devel::DumpTrace::TRACE.
1069             #
1070             # This class allows us to say, for example,
1071             #
1072             # $TRACE = 'verbose'
1073             #
1074             # and have the keyword 'verbose' translated into the value "5".
1075             #
1076 16     16   78  
1077 16         34 sub Devel::DumpTrace::VerboseLevel::TIESCALAR {
1078 16         56 my ($pkg) = @_;
1079             my $scalar;
1080             return bless \$scalar, $pkg;
1081             }
1082 1388     1388   1547  
1083 1388         1280 sub Devel::DumpTrace::VerboseLevel::FETCH {
  1388         3296  
1084             my $self = shift;
1085             return ${$self};
1086             }
1087 36     36   4577  
1088             sub Devel::DumpTrace::VerboseLevel::STORE {
1089             my ($self, $value) = @_;
1090 36 50       99  
1091             #Carp::cluck $self,"->STORE($value) called !\n";
1092 36         46 return if !defined $value;
  36         126  
1093 36         109  
1094 36         107 my $old = ${$self};
1095 36         75 my ($style, $package) = split /,/, $value;
1096 16     16   95 $style =~ s/^\s+//;
  16         28  
  16         3589  
1097             $style =~ s/\s+$//;
1098 36   66     228 no warnings 'uninitialized';
1099 36 50       201 $style = {verbose=>5, normal=>3, default=>3,
1100 0         0 quiet=>1, on=>3, off=>'00'}->{lc $style} || $style;
1101 0         0 if ($style !~ /^\d+$/) {
1102             carp "Unrecognized debugging level $style\n";
1103 36         50 $style = 3;
  36         54  
1104 36 100       71 }
1105 2         4 ${$self} = $style;
1106 2         3 if (defined $package) {
1107 2 50       8 $package =~ s/^\s+//;
1108 2         3 $package =~ s/\s+$//;
  2         3  
1109             if ($package) {
1110             ${$self} += 100;
1111 36         77 }
1112             }
1113             return $old;
1114             }
1115              
1116             1;
1117              
1118             __END__