source: trunk/third/perl/lib/dumpvar.pl @ 14545

Revision 14545, 11.7 KB checked in by ghudson, 24 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r14544, which included commits to RCS files with non-trunk default branches.
Line 
1require 5.002;                  # For (defined ref)
2package dumpvar;
3
4# Needed for PrettyPrinter only:
5
6# require 5.001;  # Well, it coredumps anyway undef DB in 5.000 (not now)
7
8# translate control chars to ^X - Randal Schwartz
9# Modifications to print types by Peter Gordon v1.0
10
11# Ilya Zakharevich -- patches after 5.001 (and some before ;-)
12
13# Won't dump symbol tables and contents of debugged files by default
14
15$winsize = 80 unless defined $winsize;
16
17
18# Defaults
19
20# $globPrint = 1;
21$printUndef = 1 unless defined $printUndef;
22$tick = "auto" unless defined $tick;
23$unctrl = 'quote' unless defined $unctrl;
24$subdump = 1;
25$dumpReused = 0 unless defined $dumpReused;
26$bareStringify = 1 unless defined $bareStringify;
27
28sub main::dumpValue {
29  local %address;
30  local $^W=0;
31  (print "undef\n"), return unless defined $_[0];
32  (print &stringify($_[0]), "\n"), return unless ref $_[0];
33  dumpvar::unwrap($_[0],0);
34}
35
36# This one is good for variable names:
37
38sub unctrl {
39        local($_) = @_;
40        local($v) ;
41
42        return \$_ if ref \$_ eq "GLOB";
43        s/([\001-\037\177])/'^'.pack('c',ord($1)^64)/eg;
44        $_;
45}
46
47sub stringify {
48        local($_,$noticks) = @_;
49        local($v) ;
50        my $tick = $tick;
51
52        return 'undef' unless defined $_ or not $printUndef;
53        return $_ . "" if ref \$_ eq 'GLOB';
54        $_ = &{'overload::StrVal'}($_)
55          if $bareStringify and ref $_
56            and %overload:: and defined &{'overload::StrVal'};
57       
58        if ($tick eq 'auto') {
59          if (/[\000-\011\013-\037\177]/) {
60            $tick = '"';
61          }else {
62            $tick = "'";
63          }
64        }
65        if ($tick eq "'") {
66          s/([\'\\])/\\$1/g;
67        } elsif ($unctrl eq 'unctrl') {
68          s/([\"\\])/\\$1/g ;
69          s/([\000-\037\177])/'^'.pack('c',ord($1)^64)/eg;
70          s/([\200-\377])/'\\0x'.sprintf('%2X',ord($1))/eg
71            if $quoteHighBit;
72        } elsif ($unctrl eq 'quote') {
73          s/([\"\\\$\@])/\\$1/g if $tick eq '"';
74          s/\033/\\e/g;
75          s/([\000-\037\177])/'\\c'.chr(ord($1)^64)/eg;
76        }
77        s/([\200-\377])/'\\'.sprintf('%3o',ord($1))/eg if $quoteHighBit;
78        ($noticks || /^\d+(\.\d*)?\Z/)
79          ? $_
80          : $tick . $_ . $tick;
81}
82
83sub ShortArray {
84  my $tArrayDepth = $#{$_[0]} ;
85  $tArrayDepth = $#{$_[0]} < $arrayDepth-1 ? $#{$_[0]} : $arrayDepth-1
86    unless  $arrayDepth eq '' ;
87  my $shortmore = "";
88  $shortmore = " ..." if $tArrayDepth < $#{$_[0]} ;
89  if (!grep(ref $_, @{$_[0]})) {
90    $short = "0..$#{$_[0]}  '" .
91      join("' '", @{$_[0]}[0..$tArrayDepth]) . "'$shortmore";
92    return $short if length $short <= $compactDump;
93  }
94  undef;
95}
96
97sub DumpElem {
98  my $short = &stringify($_[0], ref $_[0]);
99  if ($veryCompact && ref $_[0]
100      && (ref $_[0] eq 'ARRAY' and !grep(ref $_, @{$_[0]}) )) {
101    my $end = "0..$#{$v}  '" .
102      join("' '", @{$_[0]}[0..$tArrayDepth]) . "'$shortmore";
103  } elsif ($veryCompact && ref $_[0]
104      && (ref $_[0] eq 'HASH') and !grep(ref $_, values %{$_[0]})) {
105    my $end = 1;
106          $short = $sp . "0..$#{$v}  '" .
107            join("' '", @{$v}[0..$tArrayDepth]) . "'$shortmore";
108  } else {
109    print "$short\n";
110    unwrap($_[0],$_[1]);
111  }
112}
113
114sub unwrap {
115    return if $DB::signal;
116    local($v) = shift ;
117    local($s) = shift ; # extra no of spaces
118    local(%v,@v,$sp,$value,$key,@sortKeys,$more,$shortmore,$short) ;
119    local($tHashDepth,$tArrayDepth) ;
120
121    $sp = " " x $s ;
122    $s += 3 ;
123
124    # Check for reused addresses
125    if (ref $v) {
126      my $val = $v;
127      $val = &{'overload::StrVal'}($v)
128        if %overload:: and defined &{'overload::StrVal'};
129      ($address) = $val =~ /(0x[0-9a-f]+)\)$/ ;
130      if (!$dumpReused && defined $address) {
131        $address{$address}++ ;
132        if ( $address{$address} > 1 ) {
133          print "${sp}-> REUSED_ADDRESS\n" ;
134          return ;
135        }
136      }
137    } elsif (ref \$v eq 'GLOB') {
138      $address = "$v" . "";     # To avoid a bug with globs
139      $address{$address}++ ;
140      if ( $address{$address} > 1 ) {
141        print "${sp}*DUMPED_GLOB*\n" ;
142        return ;
143      }
144    }
145
146    if (ref $v eq 'Regexp') {
147      my $re = "$v";
148      $re =~ s,/,\\/,g;
149      print "$sp-> qr/$re/\n";
150      return;
151    }
152
153    if ( UNIVERSAL::isa($v, 'HASH') ) {
154        @sortKeys = sort keys(%$v) ;
155        undef $more ;
156        $tHashDepth = $#sortKeys ;
157        $tHashDepth = $#sortKeys < $hashDepth-1 ? $#sortKeys : $hashDepth-1
158          unless $hashDepth eq '' ;
159        $more = "....\n" if $tHashDepth < $#sortKeys ;
160        $shortmore = "";
161        $shortmore = ", ..." if $tHashDepth < $#sortKeys ;
162        $#sortKeys = $tHashDepth ;
163        if ($compactDump && !grep(ref $_, values %{$v})) {
164          #$short = $sp .
165          #  (join ', ',
166# Next row core dumps during require from DB on 5.000, even with map {"_"}
167          #   map {&stringify($_) . " => " . &stringify($v->{$_})}
168          #   @sortKeys) . "'$shortmore";
169          $short = $sp;
170          my @keys;
171          for (@sortKeys) {
172            push @keys, &stringify($_) . " => " . &stringify($v->{$_});
173          }
174          $short .= join ', ', @keys;
175          $short .= $shortmore;
176          (print "$short\n"), return if length $short <= $compactDump;
177        }
178        for $key (@sortKeys) {
179            return if $DB::signal;
180            $value = $ {$v}{$key} ;
181            print "$sp", &stringify($key), " => ";
182            DumpElem $value, $s;
183        }
184        print "$sp  empty hash\n" unless @sortKeys;
185        print "$sp$more" if defined $more ;
186    } elsif ( UNIVERSAL::isa($v, 'ARRAY') ) {
187        $tArrayDepth = $#{$v} ;
188        undef $more ;
189        $tArrayDepth = $#{$v} < $arrayDepth-1 ? $#{$v} : $arrayDepth-1
190          unless  $arrayDepth eq '' ;
191        $more = "....\n" if $tArrayDepth < $#{$v} ;
192        $shortmore = "";
193        $shortmore = " ..." if $tArrayDepth < $#{$v} ;
194        if ($compactDump && !grep(ref $_, @{$v})) {
195          if ($#$v >= 0) {
196            $short = $sp . "0..$#{$v}  " .
197              join(" ",
198                   map {exists $v->[$_] ? stringify $v->[$_] : "empty"} ($[..$tArrayDepth)
199                  ) . "$shortmore";
200          } else {
201            $short = $sp . "empty array";
202          }
203          (print "$short\n"), return if length $short <= $compactDump;
204        }
205        #if ($compactDump && $short = ShortArray($v)) {
206        #  print "$short\n";
207        #  return;
208        #}
209        for $num ($[ .. $tArrayDepth) {
210            return if $DB::signal;
211            print "$sp$num  ";
212            if (exists $v->[$num]) {
213                DumpElem $v->[$num], $s;
214            } else {
215                print "empty slot\n";
216            }
217        }
218        print "$sp  empty array\n" unless @$v;
219        print "$sp$more" if defined $more ; 
220    } elsif (  UNIVERSAL::isa($v, 'SCALAR') or ref $v eq 'REF' ) {
221            print "$sp-> ";
222            DumpElem $$v, $s;
223    } elsif ( UNIVERSAL::isa($v, 'CODE') ) {
224            print "$sp-> ";
225            dumpsub (0, $v);
226    } elsif ( UNIVERSAL::isa($v, 'GLOB') ) {
227      print "$sp-> ",&stringify($$v,1),"\n";
228      if ($globPrint) {
229        $s += 3;
230        dumpglob($s, "{$$v}", $$v, 1);
231      } elsif (defined ($fileno = fileno($v))) {
232        print( (' ' x ($s+3)) .  "FileHandle({$$v}) => fileno($fileno)\n" );
233      }
234    } elsif (ref \$v eq 'GLOB') {
235      if ($globPrint) {
236        dumpglob($s, "{$v}", $v, 1) if $globPrint;
237      } elsif (defined ($fileno = fileno(\$v))) {
238        print( (' ' x $s) .  "FileHandle({$v}) => fileno($fileno)\n" );
239      }
240    }
241}
242
243sub matchvar {
244  $_[0] eq $_[1] or
245    ($_[1] =~ /^([!~])(.)([\x00-\xff]*)/) and
246      ($1 eq '!') ^ (eval {($_[2] . "::" . $_[0]) =~ /$2$3/});
247}
248
249sub compactDump {
250  $compactDump = shift if @_;
251  $compactDump = 6*80-1 if $compactDump and $compactDump < 2;
252  $compactDump;
253}
254
255sub veryCompact {
256  $veryCompact = shift if @_;
257  compactDump(1) if !$compactDump and $veryCompact;
258  $veryCompact;
259}
260
261sub unctrlSet {
262  if (@_) {
263    my $in = shift;
264    if ($in eq 'unctrl' or $in eq 'quote') {
265      $unctrl = $in;
266    } else {
267      print "Unknown value for `unctrl'.\n";
268    }
269  }
270  $unctrl;
271}
272
273sub quote {
274  if (@_ and $_[0] eq '"') {
275    $tick = '"';
276    $unctrl = 'quote';
277  } elsif (@_ and $_[0] eq 'auto') {
278    $tick = 'auto';
279    $unctrl = 'quote';
280  } elsif (@_) {                # Need to set
281    $tick = "'";
282    $unctrl = 'unctrl';
283  }
284  $tick;
285}
286
287sub dumpglob {
288    return if $DB::signal;
289    my ($off,$key, $val, $all) = @_;
290    local(*entry) = $val;
291    my $fileno;
292    if (($key !~ /^_</ or $dumpDBFiles) and defined $entry) {
293      print( (' ' x $off) . "\$", &unctrl($key), " = " );
294      DumpElem $entry, 3+$off;
295    }
296    if (($key !~ /^_</ or $dumpDBFiles) and @entry) {
297      print( (' ' x $off) . "\@$key = (\n" );
298      unwrap(\@entry,3+$off) ;
299      print( (' ' x $off) .  ")\n" );
300    }
301    if ($key ne "main::" && $key ne "DB::" && %entry
302        && ($dumpPackages or $key !~ /::$/)
303        && ($key !~ /^_</ or $dumpDBFiles)
304        && !($package eq "dumpvar" and $key eq "stab")) {
305      print( (' ' x $off) . "\%$key = (\n" );
306      unwrap(\%entry,3+$off) ;
307      print( (' ' x $off) .  ")\n" );
308    }
309    if (defined ($fileno = fileno(*entry))) {
310      print( (' ' x $off) .  "FileHandle($key) => fileno($fileno)\n" );
311    }
312    if ($all) {
313      if (defined &entry) {
314        dumpsub($off, $key);
315      }
316    }
317}
318
319sub CvGV_name_or_bust {
320  my $in = shift;
321  return if $skipCvGV;          # Backdoor to avoid problems if XS broken...
322  $in = \&$in;                  # Hard reference...
323  eval {require Devel::Peek; 1} or return;
324  my $gv = Devel::Peek::CvGV($in) or return;
325  *$gv{PACKAGE} . '::' . *$gv{NAME};
326}
327
328sub dumpsub {
329    my ($off,$sub) = @_;
330    my $ini = $sub;
331    my $s;
332    $sub = $1 if $sub =~ /^\{\*(.*)\}$/;
333    my $subref = defined $1 ? \&$sub : \&$ini;
334    my $place = $DB::sub{$sub} || (($s = $subs{"$subref"}) && $DB::sub{$s})
335      || (($s = CvGV_name_or_bust($subref)) && $DB::sub{$s})
336      || ($subdump && ($s = findsubs("$subref")) && $DB::sub{$s});
337    $place = '???' unless defined $place;
338    $s = $sub unless defined $s;
339    print( (' ' x $off) .  "&$s in $place\n" );
340}
341
342sub findsubs {
343  return undef unless %DB::sub;
344  my ($addr, $name, $loc);
345  while (($name, $loc) = each %DB::sub) {
346    $addr = \&$name;
347    $subs{"$addr"} = $name;
348  }
349  $subdump = 0;
350  $subs{ shift() };
351}
352
353sub main::dumpvar {
354    my ($package,@vars) = @_;
355    local(%address,$key,$val,$^W);
356    $package .= "::" unless $package =~ /::$/;
357    *stab = *{"main::"};
358    while ($package =~ /(\w+?::)/g){
359      *stab = $ {stab}{$1};
360    }
361    local $TotalStrings = 0;
362    local $Strings = 0;
363    local $CompleteTotal = 0;
364    while (($key,$val) = each(%stab)) {
365      return if $DB::signal;
366      next if @vars && !grep( matchvar($key, $_), @vars );
367      if ($usageOnly) {
368        globUsage(\$val, $key)
369          if ($package ne 'dumpvar' or $key ne 'stab')
370             and ref(\$val) eq 'GLOB';
371      } else {
372        dumpglob(0,$key, $val);
373      }
374    }
375    if ($usageOnly) {
376      print "String space: $TotalStrings bytes in $Strings strings.\n";
377      $CompleteTotal += $TotalStrings;
378      print "Grand total = $CompleteTotal bytes (1 level deep) + overhead.\n";
379    }
380}
381
382sub scalarUsage {
383  my $size = length($_[0]);
384  $TotalStrings += $size;
385  $Strings++;
386  $size;
387}
388
389sub arrayUsage {                # array ref, name
390  my $size = 0;
391  map {$size += scalarUsage($_)} @{$_[0]};
392  my $len = @{$_[0]};
393  print "\@$_[1] = $len item", ($len > 1 ? "s" : ""),
394    " (data: $size bytes)\n"
395      if defined $_[1];
396  $CompleteTotal +=  $size;
397  $size;
398}
399
400sub hashUsage {         # hash ref, name
401  my @keys = keys %{$_[0]};
402  my @values = values %{$_[0]};
403  my $keys = arrayUsage \@keys;
404  my $values = arrayUsage \@values;
405  my $len = @keys;
406  my $total = $keys + $values;
407  print "\%$_[1] = $len item", ($len > 1 ? "s" : ""),
408    " (keys: $keys; values: $values; total: $total bytes)\n"
409      if defined $_[1];
410  $total;
411}
412
413sub globUsage {                 # glob ref, name
414  local *name = *{$_[0]};
415  $total = 0;
416  $total += scalarUsage $name if defined $name;
417  $total += arrayUsage \@name, $_[1] if @name;
418  $total += hashUsage \%name, $_[1] if %name and $_[1] ne "main::"
419    and $_[1] ne "DB::";   #and !($package eq "dumpvar" and $key eq "stab"));
420  $total;
421}
422
423sub packageUsage {
424  my ($package,@vars) = @_;
425  $package .= "::" unless $package =~ /::$/;
426  local *stab = *{"main::"};
427  while ($package =~ /(\w+?::)/g){
428    *stab = $ {stab}{$1};
429  }
430  local $TotalStrings = 0;
431  local $CompleteTotal = 0;
432  my ($key,$val);
433  while (($key,$val) = each(%stab)) {
434    next if @vars && !grep($key eq $_,@vars);
435    globUsage \$val, $key unless $package eq 'dumpvar' and $key eq 'stab';
436  }
437  print "String space: $TotalStrings.\n";
438  $CompleteTotal += $TotalStrings;
439  print "\nGrand total = $CompleteTotal bytes\n";
440}
441
4421;
443
Note: See TracBrowser for help on using the repository browser.