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 444