Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

600 lines
15 KiB

  1. require 5.005; # For (defined ref) and $#$v
  2. package Dumpvalue;
  3. use strict;
  4. use vars qw(%address *stab %subs);
  5. # translate control chars to ^X - Randal Schwartz
  6. # Modifications to print types by Peter Gordon v1.0
  7. # Ilya Zakharevich -- patches after 5.001 (and some before ;-)
  8. # Won't dump symbol tables and contents of debugged files by default
  9. # (IZ) changes for objectification:
  10. # c) quote() renamed to method set_quote();
  11. # d) unctrlSet() renamed to method set_unctrl();
  12. # f) Compiles with `use strict', but in two places no strict refs is needed:
  13. # maybe more problems are waiting...
  14. my %defaults = (
  15. globPrint => 0,
  16. printUndef => 1,
  17. tick => "auto",
  18. unctrl => 'quote',
  19. subdump => 1,
  20. dumpReused => 0,
  21. bareStringify => 1,
  22. hashDepth => '',
  23. arrayDepth => '',
  24. dumpDBFiles => '',
  25. dumpPackages => '',
  26. quoteHighBit => '',
  27. usageOnly => '',
  28. compactDump => '',
  29. veryCompact => '',
  30. stopDbSignal => '',
  31. );
  32. sub new {
  33. my $class = shift;
  34. my %opt = (%defaults, @_);
  35. bless \%opt, $class;
  36. }
  37. sub set {
  38. my $self = shift;
  39. my %opt = @_;
  40. @$self{keys %opt} = values %opt;
  41. }
  42. sub get {
  43. my $self = shift;
  44. wantarray ? @$self{@_} : $$self{pop @_};
  45. }
  46. sub dumpValue {
  47. my $self = shift;
  48. die "usage: \$dumper->dumpValue(value)" unless @_ == 1;
  49. local %address;
  50. local $^W=0;
  51. (print "undef\n"), return unless defined $_[0];
  52. (print $self->stringify($_[0]), "\n"), return unless ref $_[0];
  53. $self->unwrap($_[0],0);
  54. }
  55. sub dumpValues {
  56. my $self = shift;
  57. local %address;
  58. local $^W=0;
  59. (print "undef\n"), return unless defined $_[0];
  60. $self->unwrap(\@_,0);
  61. }
  62. # This one is good for variable names:
  63. sub unctrl {
  64. local($_) = @_;
  65. return \$_ if ref \$_ eq "GLOB";
  66. s/([\001-\037\177])/'^'.pack('c',ord($1)^64)/eg;
  67. $_;
  68. }
  69. sub stringify {
  70. my $self = shift;
  71. local $_ = shift;
  72. my $noticks = shift;
  73. my $tick = $self->{tick};
  74. return 'undef' unless defined $_ or not $self->{printUndef};
  75. return $_ . "" if ref \$_ eq 'GLOB';
  76. { no strict 'refs';
  77. $_ = &{'overload::StrVal'}($_)
  78. if $self->{bareStringify} and ref $_
  79. and defined %overload:: and defined &{'overload::StrVal'};
  80. }
  81. if ($tick eq 'auto') {
  82. if (/[\000-\011\013-\037\177]/) {
  83. $tick = '"';
  84. } else {
  85. $tick = "'";
  86. }
  87. }
  88. if ($tick eq "'") {
  89. s/([\'\\])/\\$1/g;
  90. } elsif ($self->{unctrl} eq 'unctrl') {
  91. s/([\"\\])/\\$1/g ;
  92. s/([\000-\037\177])/'^'.pack('c',ord($1)^64)/eg;
  93. s/([\200-\377])/'\\0x'.sprintf('%2X',ord($1))/eg
  94. if $self->{quoteHighBit};
  95. } elsif ($self->{unctrl} eq 'quote') {
  96. s/([\"\\\$\@])/\\$1/g if $tick eq '"';
  97. s/\033/\\e/g;
  98. s/([\000-\037\177])/'\\c'.chr(ord($1)^64)/eg;
  99. }
  100. s/([\200-\377])/'\\'.sprintf('%3o',ord($1))/eg if $self->{quoteHighBit};
  101. ($noticks || /^\d+(\.\d*)?\Z/)
  102. ? $_
  103. : $tick . $_ . $tick;
  104. }
  105. sub DumpElem {
  106. my ($self, $v) = (shift, shift);
  107. my $short = $self->stringify($v, ref $v);
  108. my $shortmore = '';
  109. if ($self->{veryCompact} && ref $v
  110. && (ref $v eq 'ARRAY' and !grep(ref $_, @$v) )) {
  111. my $depth = $#$v;
  112. ($shortmore, $depth) = (' ...', $self->{arrayDepth} - 1)
  113. if $self->{arrayDepth} and $depth >= $self->{arrayDepth};
  114. my @a = map $self->stringify($_), @$v[0..$depth];
  115. print "0..$#{$v} @a$shortmore\n";
  116. } elsif ($self->{veryCompact} && ref $v
  117. && (ref $v eq 'HASH') and !grep(ref $_, values %$v)) {
  118. my @a = sort keys %$v;
  119. my $depth = $#a;
  120. ($shortmore, $depth) = (' ...', $self->{hashDepth} - 1)
  121. if $self->{hashDepth} and $depth >= $self->{hashDepth};
  122. my @b = map {$self->stringify($_) . " => " . $self->stringify($$v{$_})}
  123. @a[0..$depth];
  124. local $" = ', ';
  125. print "@b$shortmore\n";
  126. } else {
  127. print "$short\n";
  128. $self->unwrap($v,shift);
  129. }
  130. }
  131. sub unwrap {
  132. my $self = shift;
  133. return if $DB::signal and $self->{stopDbSignal};
  134. my ($v) = shift ;
  135. my ($s) = shift ; # extra no of spaces
  136. my $sp;
  137. my (%v,@v,$address,$short,$fileno);
  138. $sp = " " x $s ;
  139. $s += 3 ;
  140. # Check for reused addresses
  141. if (ref $v) {
  142. my $val = $v;
  143. { no strict 'refs';
  144. $val = &{'overload::StrVal'}($v)
  145. if defined %overload:: and defined &{'overload::StrVal'};
  146. }
  147. ($address) = $val =~ /(0x[0-9a-f]+)\)$/ ;
  148. if (!$self->{dumpReused} && defined $address) {
  149. $address{$address}++ ;
  150. if ( $address{$address} > 1 ) {
  151. print "${sp}-> REUSED_ADDRESS\n" ;
  152. return ;
  153. }
  154. }
  155. } elsif (ref \$v eq 'GLOB') {
  156. $address = "$v" . ""; # To avoid a bug with globs
  157. $address{$address}++ ;
  158. if ( $address{$address} > 1 ) {
  159. print "${sp}*DUMPED_GLOB*\n" ;
  160. return ;
  161. }
  162. }
  163. if ( UNIVERSAL::isa($v, 'HASH') ) {
  164. my @sortKeys = sort keys(%$v) ;
  165. my $more;
  166. my $tHashDepth = $#sortKeys ;
  167. $tHashDepth = $#sortKeys < $self->{hashDepth}-1 ? $#sortKeys : $self->{hashDepth}-1
  168. unless $self->{hashDepth} eq '' ;
  169. $more = "....\n" if $tHashDepth < $#sortKeys ;
  170. my $shortmore = "";
  171. $shortmore = ", ..." if $tHashDepth < $#sortKeys ;
  172. $#sortKeys = $tHashDepth ;
  173. if ($self->{compactDump} && !grep(ref $_, values %{$v})) {
  174. $short = $sp;
  175. my @keys;
  176. for (@sortKeys) {
  177. push @keys, $self->stringify($_) . " => " . $self->stringify($v->{$_});
  178. }
  179. $short .= join ', ', @keys;
  180. $short .= $shortmore;
  181. (print "$short\n"), return if length $short <= $self->{compactDump};
  182. }
  183. for my $key (@sortKeys) {
  184. return if $DB::signal and $self->{stopDbSignal};
  185. my $value = $ {$v}{$key} ;
  186. print $sp, $self->stringify($key), " => ";
  187. $self->DumpElem($value, $s);
  188. }
  189. print "$sp empty hash\n" unless @sortKeys;
  190. print "$sp$more" if defined $more ;
  191. } elsif ( UNIVERSAL::isa($v, 'ARRAY') ) {
  192. my $tArrayDepth = $#{$v} ;
  193. my $more ;
  194. $tArrayDepth = $#$v < $self->{arrayDepth}-1 ? $#$v : $self->{arrayDepth}-1
  195. unless $self->{arrayDepth} eq '' ;
  196. $more = "....\n" if $tArrayDepth < $#{$v} ;
  197. my $shortmore = "";
  198. $shortmore = " ..." if $tArrayDepth < $#{$v} ;
  199. if ($self->{compactDump} && !grep(ref $_, @{$v})) {
  200. if ($#$v >= 0) {
  201. $short = $sp . "0..$#{$v} " .
  202. join(" ",
  203. map {$self->stringify($_)} @{$v}[0..$tArrayDepth])
  204. . "$shortmore";
  205. } else {
  206. $short = $sp . "empty array";
  207. }
  208. (print "$short\n"), return if length $short <= $self->{compactDump};
  209. }
  210. for my $num ($[ .. $tArrayDepth) {
  211. return if $DB::signal and $self->{stopDbSignal};
  212. print "$sp$num ";
  213. $self->DumpElem($v->[$num], $s);
  214. }
  215. print "$sp empty array\n" unless @$v;
  216. print "$sp$more" if defined $more ;
  217. } elsif ( UNIVERSAL::isa($v, 'SCALAR') or ref $v eq 'REF' ) {
  218. print "$sp-> ";
  219. $self->DumpElem($$v, $s);
  220. } elsif ( UNIVERSAL::isa($v, 'CODE') ) {
  221. print "$sp-> ";
  222. $self->dumpsub(0, $v);
  223. } elsif ( UNIVERSAL::isa($v, 'GLOB') ) {
  224. print "$sp-> ",$self->stringify($$v,1),"\n";
  225. if ($self->{globPrint}) {
  226. $s += 3;
  227. $self->dumpglob('', $s, "{$$v}", $$v, 1);
  228. } elsif (defined ($fileno = fileno($v))) {
  229. print( (' ' x ($s+3)) . "FileHandle({$$v}) => fileno($fileno)\n" );
  230. }
  231. } elsif (ref \$v eq 'GLOB') {
  232. if ($self->{globPrint}) {
  233. $self->dumpglob('', $s, "{$v}", $v, 1);
  234. } elsif (defined ($fileno = fileno(\$v))) {
  235. print( (' ' x $s) . "FileHandle({$v}) => fileno($fileno)\n" );
  236. }
  237. }
  238. }
  239. sub matchvar {
  240. $_[0] eq $_[1] or
  241. ($_[1] =~ /^([!~])(.)([\x00-\xff]*)/) and
  242. ($1 eq '!') ^ (eval {($_[2] . "::" . $_[0]) =~ /$2$3/});
  243. }
  244. sub compactDump {
  245. my $self = shift;
  246. $self->{compactDump} = shift if @_;
  247. $self->{compactDump} = 6*80-1
  248. if $self->{compactDump} and $self->{compactDump} < 2;
  249. $self->{compactDump};
  250. }
  251. sub veryCompact {
  252. my $self = shift;
  253. $self->{veryCompact} = shift if @_;
  254. $self->compactDump(1) if !$self->{compactDump} and $self->{veryCompact};
  255. $self->{veryCompact};
  256. }
  257. sub set_unctrl {
  258. my $self = shift;
  259. if (@_) {
  260. my $in = shift;
  261. if ($in eq 'unctrl' or $in eq 'quote') {
  262. $self->{unctrl} = $in;
  263. } else {
  264. print "Unknown value for `unctrl'.\n";
  265. }
  266. }
  267. $self->{unctrl};
  268. }
  269. sub set_quote {
  270. my $self = shift;
  271. if (@_ and $_[0] eq '"') {
  272. $self->{tick} = '"';
  273. $self->{unctrl} = 'quote';
  274. } elsif (@_ and $_[0] eq 'auto') {
  275. $self->{tick} = 'auto';
  276. $self->{unctrl} = 'quote';
  277. } elsif (@_) { # Need to set
  278. $self->{tick} = "'";
  279. $self->{unctrl} = 'unctrl';
  280. }
  281. $self->{tick};
  282. }
  283. sub dumpglob {
  284. my $self = shift;
  285. return if $DB::signal and $self->{stopDbSignal};
  286. my ($package, $off, $key, $val, $all) = @_;
  287. local(*stab) = $val;
  288. my $fileno;
  289. if (($key !~ /^_</ or $self->{dumpDBFiles}) and defined $stab) {
  290. print( (' ' x $off) . "\$", &unctrl($key), " = " );
  291. $self->DumpElem($stab, 3+$off);
  292. }
  293. if (($key !~ /^_</ or $self->{dumpDBFiles}) and defined @stab) {
  294. print( (' ' x $off) . "\@$key = (\n" );
  295. $self->unwrap(\@stab,3+$off) ;
  296. print( (' ' x $off) . ")\n" );
  297. }
  298. if ($key ne "main::" && $key ne "DB::" && defined %stab
  299. && ($self->{dumpPackages} or $key !~ /::$/)
  300. && ($key !~ /^_</ or $self->{dumpDBFiles})
  301. && !($package eq "Dumpvalue" and $key eq "stab")) {
  302. print( (' ' x $off) . "\%$key = (\n" );
  303. $self->unwrap(\%stab,3+$off) ;
  304. print( (' ' x $off) . ")\n" );
  305. }
  306. if (defined ($fileno = fileno(*stab))) {
  307. print( (' ' x $off) . "FileHandle($key) => fileno($fileno)\n" );
  308. }
  309. if ($all) {
  310. if (defined &stab) {
  311. $self->dumpsub($off, $key);
  312. }
  313. }
  314. }
  315. sub dumpsub {
  316. my $self = shift;
  317. my ($off,$sub) = @_;
  318. $sub = $1 if $sub =~ /^\{\*(.*)\}$/;
  319. my $subref = \&$sub;
  320. my $place = $DB::sub{$sub} || (($sub = $subs{"$subref"}) && $DB::sub{$sub})
  321. || ($self->{subdump} && ($sub = $self->findsubs("$subref"))
  322. && $DB::sub{$sub});
  323. $place = '???' unless defined $place;
  324. print( (' ' x $off) . "&$sub in $place\n" );
  325. }
  326. sub findsubs {
  327. my $self = shift;
  328. return undef unless defined %DB::sub;
  329. my ($addr, $name, $loc);
  330. while (($name, $loc) = each %DB::sub) {
  331. $addr = \&$name;
  332. $subs{"$addr"} = $name;
  333. }
  334. $self->{subdump} = 0;
  335. $subs{ shift() };
  336. }
  337. sub dumpvars {
  338. my $self = shift;
  339. my ($package,@vars) = @_;
  340. local(%address,$^W);
  341. my ($key,$val);
  342. $package .= "::" unless $package =~ /::$/;
  343. *stab = *main::;
  344. while ($package =~ /(\w+?::)/g) {
  345. *stab = $ {stab}{$1};
  346. }
  347. $self->{TotalStrings} = 0;
  348. $self->{Strings} = 0;
  349. $self->{CompleteTotal} = 0;
  350. while (($key,$val) = each(%stab)) {
  351. return if $DB::signal and $self->{stopDbSignal};
  352. next if @vars && !grep( matchvar($key, $_), @vars );
  353. if ($self->{usageOnly}) {
  354. $self->globUsage(\$val, $key)
  355. unless $package eq 'Dumpvalue' and $key eq 'stab';
  356. } else {
  357. $self->dumpglob($package, 0,$key, $val);
  358. }
  359. }
  360. if ($self->{usageOnly}) {
  361. print <<EOP;
  362. String space: $self->{TotalStrings} bytes in $self->{Strings} strings.
  363. EOP
  364. $self->{CompleteTotal} += $self->{TotalStrings};
  365. print <<EOP;
  366. Grand total = $self->{CompleteTotal} bytes (1 level deep) + overhead.
  367. EOP
  368. }
  369. }
  370. sub scalarUsage {
  371. my $self = shift;
  372. my $size = length($_[0]);
  373. $self->{TotalStrings} += $size;
  374. $self->{Strings}++;
  375. $size;
  376. }
  377. sub arrayUsage { # array ref, name
  378. my $self = shift;
  379. my $size = 0;
  380. map {$size += $self->scalarUsage($_)} @{$_[0]};
  381. my $len = @{$_[0]};
  382. print "\@$_[1] = $len item", ($len > 1 ? "s" : ""), " (data: $size bytes)\n"
  383. if defined $_[1];
  384. $self->{CompleteTotal} += $size;
  385. $size;
  386. }
  387. sub hashUsage { # hash ref, name
  388. my $self = shift;
  389. my @keys = keys %{$_[0]};
  390. my @values = values %{$_[0]};
  391. my $keys = $self->arrayUsage(\@keys);
  392. my $values = $self->arrayUsage(\@values);
  393. my $len = @keys;
  394. my $total = $keys + $values;
  395. print "\%$_[1] = $len item", ($len > 1 ? "s" : ""),
  396. " (keys: $keys; values: $values; total: $total bytes)\n"
  397. if defined $_[1];
  398. $total;
  399. }
  400. sub globUsage { # glob ref, name
  401. my $self = shift;
  402. local *stab = *{$_[0]};
  403. my $total = 0;
  404. $total += $self->scalarUsage($stab) if defined $stab;
  405. $total += $self->arrayUsage(\@stab, $_[1]) if defined @stab;
  406. $total += $self->hashUsage(\%stab, $_[1])
  407. if defined %stab and $_[1] ne "main::" and $_[1] ne "DB::";
  408. #and !($package eq "Dumpvalue" and $key eq "stab"));
  409. $total;
  410. }
  411. 1;
  412. =head1 NAME
  413. Dumpvalue - provides screen dump of Perl data.
  414. =head1 SYNOPSYS
  415. use Dumpvalue;
  416. my $dumper = new Dumpvalue;
  417. $dumper->set(globPrint => 1);
  418. $dumper->dumpValue(\*::);
  419. $dumper->dumpvars('main');
  420. =head1 DESCRIPTION
  421. =head2 Creation
  422. A new dumper is created by a call
  423. $d = new Dumpvalue(option1 => value1, option2 => value2)
  424. Recognized options:
  425. =over
  426. =item C<arrayDepth>, C<hashDepth>
  427. Print only first N elements of arrays and hashes. If false, prints all the
  428. elements.
  429. =item C<compactDump>, C<veryCompact>
  430. Change style of array and hash dump. If true, short array
  431. may be printed on one line.
  432. =item C<globPrint>
  433. Whether to print contents of globs.
  434. =item C<DumpDBFiles>
  435. Dump arrays holding contents of debugged files.
  436. =item C<DumpPackages>
  437. Dump symbol tables of packages.
  438. =item C<DumpReused>
  439. Dump contents of "reused" addresses.
  440. =item C<tick>, C<HighBit>, C<printUndef>
  441. Change style of string dump. Default value of C<tick> is C<auto>, one
  442. can enable either double-quotish dump, or single-quotish by setting it
  443. to C<"> or C<'>. By default, characters with high bit set are printed
  444. I<as is>.
  445. =item C<UsageOnly>
  446. I<very> rudimentally per-package memory usage dump. If set,
  447. C<dumpvars> calculates total size of strings in variables in the package.
  448. =item unctrl
  449. Changes the style of printout of strings. Possible values are
  450. C<unctrl> and C<quote>.
  451. =item subdump
  452. Whether to try to find the subroutine name given the reference.
  453. =item bareStringify
  454. Whether to write the non-overloaded form of the stringify-overloaded objects.
  455. =item quoteHighBit
  456. Whether to print chars with high bit set in binary or "as is".
  457. =item stopDbSignal
  458. Whether to abort printing if debugger signal flag is raised.
  459. =back
  460. Later in the life of the object the methods may be queries with get()
  461. method and set() method (which accept multiple arguments).
  462. =head2 Methods
  463. =over
  464. =item dumpValue
  465. $dumper->dumpValue($value);
  466. $dumper->dumpValue([$value1, $value2]);
  467. =item dumpValues
  468. $dumper->dumpValues($value1, $value2);
  469. =item dumpvars
  470. $dumper->dumpvars('my_package');
  471. $dumper->dumpvars('my_package', 'foo', '~bar$', '!......');
  472. The optional arguments are considered as literal strings unless they
  473. start with C<~> or C<!>, in which case they are interpreted as regular
  474. expressions (possibly negated).
  475. The second example prints entries with names C<foo>, and also entries
  476. with names which ends on C<bar>, or are shorter than 5 chars.
  477. =item set_quote
  478. $d->set_quote('"');
  479. Sets C<tick> and C<unctrl> options to suitable values for printout with the
  480. given quote char. Possible values are C<auto>, C<'> and C<">.
  481. =item set_unctrl
  482. $d->set_unctrl('"');
  483. Sets C<unctrl> option with checking for an invalid argument.
  484. Possible values are C<unctrl> and C<quote>.
  485. =item compactDump
  486. $d->compactDump(1);
  487. Sets C<compactDump> option. If the value is 1, sets to a reasonable
  488. big number.
  489. =item veryCompact
  490. $d->veryCompact(1);
  491. Sets C<compactDump> and C<veryCompact> options simultaneously.
  492. =item set
  493. $d->set(option1 => value1, option2 => value2);
  494. =item get
  495. @values = $d->get('option1', 'option2');
  496. =back
  497. =cut