Leaked source code of windows server 2003
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.

626 lines
16 KiB

  1. use 5.005_64; # for (defined ref) and $#$v and our
  2. package Dumpvalue;
  3. use strict;
  4. our(%address, $stab, @stab, %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 %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 %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 (ref $v eq 'Regexp') {
  164. my $re = "$v";
  165. $re =~ s,/,\\/,g;
  166. print "$sp-> qr/$re/\n";
  167. return;
  168. }
  169. if ( UNIVERSAL::isa($v, 'HASH') ) {
  170. my @sortKeys = sort keys(%$v) ;
  171. my $more;
  172. my $tHashDepth = $#sortKeys ;
  173. $tHashDepth = $#sortKeys < $self->{hashDepth}-1 ? $#sortKeys : $self->{hashDepth}-1
  174. unless $self->{hashDepth} eq '' ;
  175. $more = "....\n" if $tHashDepth < $#sortKeys ;
  176. my $shortmore = "";
  177. $shortmore = ", ..." if $tHashDepth < $#sortKeys ;
  178. $#sortKeys = $tHashDepth ;
  179. if ($self->{compactDump} && !grep(ref $_, values %{$v})) {
  180. $short = $sp;
  181. my @keys;
  182. for (@sortKeys) {
  183. push @keys, $self->stringify($_) . " => " . $self->stringify($v->{$_});
  184. }
  185. $short .= join ', ', @keys;
  186. $short .= $shortmore;
  187. (print "$short\n"), return if length $short <= $self->{compactDump};
  188. }
  189. for my $key (@sortKeys) {
  190. return if $DB::signal and $self->{stopDbSignal};
  191. my $value = $ {$v}{$key} ;
  192. print $sp, $self->stringify($key), " => ";
  193. $self->DumpElem($value, $s);
  194. }
  195. print "$sp empty hash\n" unless @sortKeys;
  196. print "$sp$more" if defined $more ;
  197. } elsif ( UNIVERSAL::isa($v, 'ARRAY') ) {
  198. my $tArrayDepth = $#{$v} ;
  199. my $more ;
  200. $tArrayDepth = $#$v < $self->{arrayDepth}-1 ? $#$v : $self->{arrayDepth}-1
  201. unless $self->{arrayDepth} eq '' ;
  202. $more = "....\n" if $tArrayDepth < $#{$v} ;
  203. my $shortmore = "";
  204. $shortmore = " ..." if $tArrayDepth < $#{$v} ;
  205. if ($self->{compactDump} && !grep(ref $_, @{$v})) {
  206. if ($#$v >= 0) {
  207. $short = $sp . "0..$#{$v} " .
  208. join(" ",
  209. map {exists $v->[$_] ? $self->stringify($v->[$_]) : "empty"} ($[..$tArrayDepth)
  210. ) . "$shortmore";
  211. } else {
  212. $short = $sp . "empty array";
  213. }
  214. (print "$short\n"), return if length $short <= $self->{compactDump};
  215. }
  216. for my $num ($[ .. $tArrayDepth) {
  217. return if $DB::signal and $self->{stopDbSignal};
  218. print "$sp$num ";
  219. if (exists $v->[$num]) {
  220. $self->DumpElem($v->[$num], $s);
  221. } else {
  222. print "empty slot\n";
  223. }
  224. }
  225. print "$sp empty array\n" unless @$v;
  226. print "$sp$more" if defined $more ;
  227. } elsif ( UNIVERSAL::isa($v, 'SCALAR') or ref $v eq 'REF' ) {
  228. print "$sp-> ";
  229. $self->DumpElem($$v, $s);
  230. } elsif ( UNIVERSAL::isa($v, 'CODE') ) {
  231. print "$sp-> ";
  232. $self->dumpsub(0, $v);
  233. } elsif ( UNIVERSAL::isa($v, 'GLOB') ) {
  234. print "$sp-> ",$self->stringify($$v,1),"\n";
  235. if ($self->{globPrint}) {
  236. $s += 3;
  237. $self->dumpglob('', $s, "{$$v}", $$v, 1);
  238. } elsif (defined ($fileno = fileno($v))) {
  239. print( (' ' x ($s+3)) . "FileHandle({$$v}) => fileno($fileno)\n" );
  240. }
  241. } elsif (ref \$v eq 'GLOB') {
  242. if ($self->{globPrint}) {
  243. $self->dumpglob('', $s, "{$v}", $v, 1);
  244. } elsif (defined ($fileno = fileno(\$v))) {
  245. print( (' ' x $s) . "FileHandle({$v}) => fileno($fileno)\n" );
  246. }
  247. }
  248. }
  249. sub matchvar {
  250. $_[0] eq $_[1] or
  251. ($_[1] =~ /^([!~])(.)([\x00-\xff]*)/) and
  252. ($1 eq '!') ^ (eval {($_[2] . "::" . $_[0]) =~ /$2$3/});
  253. }
  254. sub compactDump {
  255. my $self = shift;
  256. $self->{compactDump} = shift if @_;
  257. $self->{compactDump} = 6*80-1
  258. if $self->{compactDump} and $self->{compactDump} < 2;
  259. $self->{compactDump};
  260. }
  261. sub veryCompact {
  262. my $self = shift;
  263. $self->{veryCompact} = shift if @_;
  264. $self->compactDump(1) if !$self->{compactDump} and $self->{veryCompact};
  265. $self->{veryCompact};
  266. }
  267. sub set_unctrl {
  268. my $self = shift;
  269. if (@_) {
  270. my $in = shift;
  271. if ($in eq 'unctrl' or $in eq 'quote') {
  272. $self->{unctrl} = $in;
  273. } else {
  274. print "Unknown value for `unctrl'.\n";
  275. }
  276. }
  277. $self->{unctrl};
  278. }
  279. sub set_quote {
  280. my $self = shift;
  281. if (@_ and $_[0] eq '"') {
  282. $self->{tick} = '"';
  283. $self->{unctrl} = 'quote';
  284. } elsif (@_ and $_[0] eq 'auto') {
  285. $self->{tick} = 'auto';
  286. $self->{unctrl} = 'quote';
  287. } elsif (@_) { # Need to set
  288. $self->{tick} = "'";
  289. $self->{unctrl} = 'unctrl';
  290. }
  291. $self->{tick};
  292. }
  293. sub dumpglob {
  294. my $self = shift;
  295. return if $DB::signal and $self->{stopDbSignal};
  296. my ($package, $off, $key, $val, $all) = @_;
  297. local(*stab) = $val;
  298. my $fileno;
  299. if (($key !~ /^_</ or $self->{dumpDBFiles}) and defined $stab) {
  300. print( (' ' x $off) . "\$", &unctrl($key), " = " );
  301. $self->DumpElem($stab, 3+$off);
  302. }
  303. if (($key !~ /^_</ or $self->{dumpDBFiles}) and @stab) {
  304. print( (' ' x $off) . "\@$key = (\n" );
  305. $self->unwrap(\@stab,3+$off) ;
  306. print( (' ' x $off) . ")\n" );
  307. }
  308. if ($key ne "main::" && $key ne "DB::" && %stab
  309. && ($self->{dumpPackages} or $key !~ /::$/)
  310. && ($key !~ /^_</ or $self->{dumpDBFiles})
  311. && !($package eq "Dumpvalue" and $key eq "stab")) {
  312. print( (' ' x $off) . "\%$key = (\n" );
  313. $self->unwrap(\%stab,3+$off) ;
  314. print( (' ' x $off) . ")\n" );
  315. }
  316. if (defined ($fileno = fileno(*stab))) {
  317. print( (' ' x $off) . "FileHandle($key) => fileno($fileno)\n" );
  318. }
  319. if ($all) {
  320. if (defined &stab) {
  321. $self->dumpsub($off, $key);
  322. }
  323. }
  324. }
  325. sub CvGV_name {
  326. my $self = shift;
  327. my $in = shift;
  328. return if $self->{skipCvGV}; # Backdoor to avoid problems if XS broken...
  329. $in = \&$in; # Hard reference...
  330. eval {require Devel::Peek; 1} or return;
  331. my $gv = Devel::Peek::CvGV($in) or return;
  332. *$gv{PACKAGE} . '::' . *$gv{NAME};
  333. }
  334. sub dumpsub {
  335. my $self = shift;
  336. my ($off,$sub) = @_;
  337. my $ini = $sub;
  338. my $s;
  339. $sub = $1 if $sub =~ /^\{\*(.*)\}$/;
  340. my $subref = defined $1 ? \&$sub : \&$ini;
  341. my $place = $DB::sub{$sub} || (($s = $subs{"$subref"}) && $DB::sub{$s})
  342. || (($s = $self->CvGV_name($subref)) && $DB::sub{$s})
  343. || ($self->{subdump} && ($s = $self->findsubs("$subref"))
  344. && $DB::sub{$s});
  345. $s = $sub unless defined $s;
  346. $place = '???' unless defined $place;
  347. print( (' ' x $off) . "&$s in $place\n" );
  348. }
  349. sub findsubs {
  350. my $self = shift;
  351. return undef unless %DB::sub;
  352. my ($addr, $name, $loc);
  353. while (($name, $loc) = each %DB::sub) {
  354. $addr = \&$name;
  355. $subs{"$addr"} = $name;
  356. }
  357. $self->{subdump} = 0;
  358. $subs{ shift() };
  359. }
  360. sub dumpvars {
  361. my $self = shift;
  362. my ($package,@vars) = @_;
  363. local(%address,$^W);
  364. my ($key,$val);
  365. $package .= "::" unless $package =~ /::$/;
  366. *stab = *main::;
  367. while ($package =~ /(\w+?::)/g) {
  368. *stab = $ {stab}{$1};
  369. }
  370. $self->{TotalStrings} = 0;
  371. $self->{Strings} = 0;
  372. $self->{CompleteTotal} = 0;
  373. while (($key,$val) = each(%stab)) {
  374. return if $DB::signal and $self->{stopDbSignal};
  375. next if @vars && !grep( matchvar($key, $_), @vars );
  376. if ($self->{usageOnly}) {
  377. $self->globUsage(\$val, $key)
  378. if ($package ne 'Dumpvalue' or $key ne 'stab')
  379. and ref(\$val) eq 'GLOB';
  380. } else {
  381. $self->dumpglob($package, 0,$key, $val);
  382. }
  383. }
  384. if ($self->{usageOnly}) {
  385. print <<EOP;
  386. String space: $self->{TotalStrings} bytes in $self->{Strings} strings.
  387. EOP
  388. $self->{CompleteTotal} += $self->{TotalStrings};
  389. print <<EOP;
  390. Grand total = $self->{CompleteTotal} bytes (1 level deep) + overhead.
  391. EOP
  392. }
  393. }
  394. sub scalarUsage {
  395. my $self = shift;
  396. my $size = length($_[0]);
  397. $self->{TotalStrings} += $size;
  398. $self->{Strings}++;
  399. $size;
  400. }
  401. sub arrayUsage { # array ref, name
  402. my $self = shift;
  403. my $size = 0;
  404. map {$size += $self->scalarUsage($_)} @{$_[0]};
  405. my $len = @{$_[0]};
  406. print "\@$_[1] = $len item", ($len > 1 ? "s" : ""), " (data: $size bytes)\n"
  407. if defined $_[1];
  408. $self->{CompleteTotal} += $size;
  409. $size;
  410. }
  411. sub hashUsage { # hash ref, name
  412. my $self = shift;
  413. my @keys = keys %{$_[0]};
  414. my @values = values %{$_[0]};
  415. my $keys = $self->arrayUsage(\@keys);
  416. my $values = $self->arrayUsage(\@values);
  417. my $len = @keys;
  418. my $total = $keys + $values;
  419. print "\%$_[1] = $len item", ($len > 1 ? "s" : ""),
  420. " (keys: $keys; values: $values; total: $total bytes)\n"
  421. if defined $_[1];
  422. $total;
  423. }
  424. sub globUsage { # glob ref, name
  425. my $self = shift;
  426. local *stab = *{$_[0]};
  427. my $total = 0;
  428. $total += $self->scalarUsage($stab) if defined $stab;
  429. $total += $self->arrayUsage(\@stab, $_[1]) if @stab;
  430. $total += $self->hashUsage(\%stab, $_[1])
  431. if %stab and $_[1] ne "main::" and $_[1] ne "DB::";
  432. #and !($package eq "Dumpvalue" and $key eq "stab"));
  433. $total;
  434. }
  435. 1;
  436. =head1 NAME
  437. Dumpvalue - provides screen dump of Perl data.
  438. =head1 SYNOPSIS
  439. use Dumpvalue;
  440. my $dumper = new Dumpvalue;
  441. $dumper->set(globPrint => 1);
  442. $dumper->dumpValue(\*::);
  443. $dumper->dumpvars('main');
  444. =head1 DESCRIPTION
  445. =head2 Creation
  446. A new dumper is created by a call
  447. $d = new Dumpvalue(option1 => value1, option2 => value2)
  448. Recognized options:
  449. =over
  450. =item C<arrayDepth>, C<hashDepth>
  451. Print only first N elements of arrays and hashes. If false, prints all the
  452. elements.
  453. =item C<compactDump>, C<veryCompact>
  454. Change style of array and hash dump. If true, short array
  455. may be printed on one line.
  456. =item C<globPrint>
  457. Whether to print contents of globs.
  458. =item C<DumpDBFiles>
  459. Dump arrays holding contents of debugged files.
  460. =item C<DumpPackages>
  461. Dump symbol tables of packages.
  462. =item C<DumpReused>
  463. Dump contents of "reused" addresses.
  464. =item C<tick>, C<HighBit>, C<printUndef>
  465. Change style of string dump. Default value of C<tick> is C<auto>, one
  466. can enable either double-quotish dump, or single-quotish by setting it
  467. to C<"> or C<'>. By default, characters with high bit set are printed
  468. I<as is>.
  469. =item C<UsageOnly>
  470. I<very> rudimentally per-package memory usage dump. If set,
  471. C<dumpvars> calculates total size of strings in variables in the package.
  472. =item unctrl
  473. Changes the style of printout of strings. Possible values are
  474. C<unctrl> and C<quote>.
  475. =item subdump
  476. Whether to try to find the subroutine name given the reference.
  477. =item bareStringify
  478. Whether to write the non-overloaded form of the stringify-overloaded objects.
  479. =item quoteHighBit
  480. Whether to print chars with high bit set in binary or "as is".
  481. =item stopDbSignal
  482. Whether to abort printing if debugger signal flag is raised.
  483. =back
  484. Later in the life of the object the methods may be queries with get()
  485. method and set() method (which accept multiple arguments).
  486. =head2 Methods
  487. =over
  488. =item dumpValue
  489. $dumper->dumpValue($value);
  490. $dumper->dumpValue([$value1, $value2]);
  491. =item dumpValues
  492. $dumper->dumpValues($value1, $value2);
  493. =item dumpvars
  494. $dumper->dumpvars('my_package');
  495. $dumper->dumpvars('my_package', 'foo', '~bar$', '!......');
  496. The optional arguments are considered as literal strings unless they
  497. start with C<~> or C<!>, in which case they are interpreted as regular
  498. expressions (possibly negated).
  499. The second example prints entries with names C<foo>, and also entries
  500. with names which ends on C<bar>, or are shorter than 5 chars.
  501. =item set_quote
  502. $d->set_quote('"');
  503. Sets C<tick> and C<unctrl> options to suitable values for printout with the
  504. given quote char. Possible values are C<auto>, C<'> and C<">.
  505. =item set_unctrl
  506. $d->set_unctrl('"');
  507. Sets C<unctrl> option with checking for an invalid argument.
  508. Possible values are C<unctrl> and C<quote>.
  509. =item compactDump
  510. $d->compactDump(1);
  511. Sets C<compactDump> option. If the value is 1, sets to a reasonable
  512. big number.
  513. =item veryCompact
  514. $d->veryCompact(1);
  515. Sets C<compactDump> and C<veryCompact> options simultaneously.
  516. =item set
  517. $d->set(option1 => value1, option2 => value2);
  518. =item get
  519. @values = $d->get('option1', 'option2');
  520. =back
  521. =cut