File Coverage

blib/lib/Devel/MAT/Tool/Show.pm
Criterion Covered Total %
statement 62 256 24.2
branch 0 134 0.0
condition 0 19 0.0
subroutine 21 43 48.8
pod 0 18 0.0
total 83 470 17.6


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2016-2022 -- leonerd@leonerd.org.uk
5              
6             package Devel::MAT::Tool::Show 0.49;
7              
8 5     5   3712 use v5.14;
  5         18  
9 5     5   26 use warnings;
  5         14  
  5         150  
10 5     5   35 use base qw( Devel::MAT::Tool );
  5         9  
  5         507  
11              
12 5     5   36 use List::Util qw( max );
  5         10  
  5         300  
13              
14 5     5   29 use constant CMD => "show";
  5         19  
  5         303  
15 5     5   28 use constant CMD_DESC => "Show information about a given SV";
  5         11  
  5         354  
16              
17 5         432 use constant CMD_OPTS => (
18             full_pv => { help => "show the full captured PV",
19             alias => "F" },
20             pad => { help => "show the first PAD of a CODE",
21             alias => "P" },
22 5     5   36 );
  5         7  
23              
24             =head1 NAME
25              
26             C - show information about a given SV
27              
28             =head1 DESCRIPTION
29              
30             This C tool provides a command that prints interesting information
31             from within an SV. Its exact output will depend on the type of SV it is
32             applied to.
33              
34             =cut
35              
36             =head1 COMMANDS
37              
38             =cut
39              
40             =head2 show
41              
42             pmat> show 0x1bbf598
43             IO() at 0x1bbf598 with refcount 2
44             blessed as IO::File
45             ifileno=2
46             ofileno=2
47              
48             Prints information about the given SV.
49              
50             =cut
51              
52 5     5   31 use constant CMD_ARGS_SV => 1;
  5         13  
  5         12107  
53              
54             my @SHOW_EXTRA;
55             sub register_extra
56             {
57 0     0 0   shift;
58 0           my ( $code ) = @_;
59 0           push @SHOW_EXTRA, $code;
60             }
61              
62             sub run
63             {
64 0     0 0   my $self = shift;
65 0           my %opts = %{ +shift };
  0            
66 0           my ( $sv ) = @_;
67              
68 0           Devel::MAT::Cmd->printf( "%s with refcount %d\n",
69             Devel::MAT::Cmd->format_sv( $sv ),
70             $sv->refcnt,
71             );
72              
73 0           my $size = $sv->size;
74 0 0         if( $size < 1024 ) {
75 0           Devel::MAT::Cmd->printf( " size %d bytes\n",
76             $size,
77             );
78             }
79             else {
80 0           Devel::MAT::Cmd->printf( " size %s (%d bytes)\n",
81             Devel::MAT::Cmd->format_bytes( $size ),
82             $size,
83             );
84             }
85              
86 0 0         if( my $stash = $sv->blessed ) {
87 0           Devel::MAT::Cmd->printf( " blessed as %s\n", $stash->stashname );
88             }
89              
90 0 0         if( my $symname = $sv->symname ) {
91 0           Devel::MAT::Cmd->printf( " named as %s\n",
92             Devel::MAT::Cmd->format_symbol( $symname )
93             );
94             }
95              
96 0           foreach my $magic ( $sv->magic ) {
97 0           my $type = $magic->type;
98 0 0         $type = "^" . chr( 0x40 + ord $type ) if ord $type < 0x20;
99              
100 0           Devel::MAT::Cmd->printf( " has %s magic",
101             Devel::MAT::Cmd->format_note( $type, 1 ),
102             );
103              
104 0 0         Devel::MAT::Cmd->printf( " with object at %s",
105             Devel::MAT::Cmd->format_sv( $magic->obj )
106             ) if $magic->obj;
107              
108 0 0         Devel::MAT::Cmd->printf( " with pointer at %s",
109             Devel::MAT::Cmd->format_sv( $magic->ptr )
110             ) if $magic->ptr;
111              
112 0 0         Devel::MAT::Cmd->printf( "\n with virtual table at 0x%x",
113             $magic->vtbl
114             ) if $magic->vtbl;
115              
116 0           Devel::MAT::Cmd->printf( "\n" );
117             }
118              
119 0 0         if( defined( my $serial = $sv->debug_serial ) ) {
120 0           Devel::MAT::Cmd->printf( " debug serial %d\n", $serial );
121              
122 0           my $file = $sv->debug_file;
123 0 0         Devel::MAT::Cmd->printf( " created at %s:%d\n", $file, $sv->debug_line )
124             if defined $file;
125             }
126              
127 0           foreach my $extra ( @SHOW_EXTRA ) {
128 0           $extra->( $sv ); # TODO: consider opts?
129             }
130              
131 0           my $type = $sv->type;
132 0           my $method = "show_$type";
133 0           $self->$method( $sv, \%opts );
134             }
135              
136             sub say_with_sv
137             {
138 0     0 0   my ( $str, @args ) = @_;
139 0           my $sv = pop @args;
140              
141 0           Devel::MAT::Cmd->printf( $str . "%s\n",
142             @args,
143             Devel::MAT::Cmd->format_sv( $sv ),
144             );
145             }
146              
147             sub show_GLOB
148             {
149 0     0 0   my $self = shift;
150 0           my ( $gv ) = @_;
151              
152 0 0         Devel::MAT::Cmd->printf( " name=%s\n", $gv->name ) if $gv->name;
153              
154 0 0         say_with_sv ' stash=', $gv->stash if $gv->stash;
155              
156 0 0         say_with_sv ' SCALAR=', $gv->scalar if $gv->scalar;
157 0 0         say_with_sv ' ARRAY=', $gv->array if $gv->array;
158 0 0         say_with_sv ' HASH=', $gv->hash if $gv->hash;
159 0 0         say_with_sv ' CODE=', $gv->code if $gv->code;
160 0 0         say_with_sv ' EGV=', $gv->egv if $gv->egv;
161 0 0         say_with_sv ' IO=', $gv->io if $gv->io;
162 0 0         say_with_sv ' FORM=', $gv->form if $gv->form;
163             }
164              
165             sub show_SCALAR
166             {
167 0     0 0   my $self = shift;
168 0           my ( $sv, $opts ) = @_;
169              
170 0 0         Devel::MAT::Cmd->printf( " UV=%s\n",
171             Devel::MAT::Cmd->format_value( $sv->uv, nv => 1 ),
172             ) if defined $sv->uv;
173 0 0         Devel::MAT::Cmd->printf( " IV=%s\n",
174             Devel::MAT::Cmd->format_value( $sv->iv, nv => 1 ),
175             ) if defined $sv->iv;
176 0 0         Devel::MAT::Cmd->printf( " NV=%s\n",
177             Devel::MAT::Cmd->format_value( $sv->nv, nv => 1 ),
178             ) if defined $sv->nv;
179              
180 0 0         if( defined( my $pv = $sv->pv ) ) {
181             Devel::MAT::Cmd->printf( " PV=%s\n",
182             Devel::MAT::Cmd->format_value( $pv, pv => 1,
183 0 0         ( $opts->{full_pv} ? ( maxlen => 0 ) : () ),
184             ),
185             );
186 0           Devel::MAT::Cmd->printf( " PVLEN %d\n", $sv->pvlen );
187             }
188             }
189              
190             sub show_BOOL
191             {
192 0     0 0   my $self = shift;
193 0           my ( $sv, $opts ) = @_;
194              
195 0 0         Devel::MAT::Cmd->printf( " BOOL=%s\n",
196             Devel::MAT::Cmd->format_value( $sv->uv ? "true" : "false" )
197             );
198             }
199              
200             sub show_REF
201             {
202 0     0 0   my $self = shift;
203 0           my ( $sv ) = @_;
204              
205 0 0         say_with_sv ' RV=', $sv->rv if $sv->rv;
206             }
207              
208             sub show_ARRAY
209             {
210 0     0 0   my $self = shift;
211 0           my ( $av ) = @_;
212              
213 0           Devel::MAT::Cmd->printf( " %d elements (use 'elems' command to show)\n",
214             $av->n_elems,
215             );
216             }
217              
218             sub show_STASH
219             {
220 0     0 0   my $self = shift;
221 0           my ( $hv ) = @_;
222              
223 0           Devel::MAT::Cmd->printf( " stashname=%s\n", $hv->stashname );
224 0           $self->show_HASH( $hv );
225             }
226              
227             sub show_HASH
228             {
229 0     0 0   my $self = shift;
230 0           my ( $hv ) = @_;
231              
232 0           Devel::MAT::Cmd->printf( " %d values (use 'values' command to show)\n",
233             $hv->n_values,
234             );
235             }
236              
237             sub show_CODE
238             {
239 0     0 0   my $self = shift;
240 0           my ( $cv, $opts ) = @_;
241              
242 0 0         $cv->hekname ? Devel::MAT::Cmd->printf( " hekname=%s\n", $cv->hekname )
243             : Devel::MAT::Cmd->printf( " no hekname\n" );
244              
245 0 0         $cv->stash ? say_with_sv( " stash=", $cv->stash )
246             : Devel::MAT::Cmd->printf( " no stash\n" );
247              
248 0 0         $cv->glob ? say_with_sv( " glob=", $cv->glob )
249             : Devel::MAT::Cmd->printf( " no glob\n" );
250              
251 0 0         $cv->location ? Devel::MAT::Cmd->printf( " location=%s\n", $cv->location )
252             : Devel::MAT::Cmd->printf( " no location\n" );
253              
254 0 0         $cv->scope ? say_with_sv( " scope=", $cv->scope )
255             : Devel::MAT::Cmd->printf( " no scope\n" );
256              
257 0 0         $cv->padlist ? say_with_sv( " padlist=", $cv->padlist )
258             : ();
259              
260 0 0         $cv->padnames_av ? say_with_sv( " padnames_av=", $cv->padnames_av )
261             : ();
262              
263 0 0         $cv->protosub ? say_with_sv( " protosub=", $cv->protosub )
264             : ();
265              
266 0           my @pads = $cv->pads;
267 0           foreach my $depth ( 0 .. $#pads ) {
268 0 0         next unless $pads[$depth];
269 0           say_with_sv( " pad[$depth]=", $pads[$depth] );
270             }
271              
272 0 0 0       if( $opts->{pad} and my $pad0 = ( $cv->pads )[0] ) {
273 0           Devel::MAT::Cmd->printf( "PAD[0]:\n" );
274 0           $self->show_PAD_contents( $pad0 );
275             }
276              
277 0 0         if( my @globs = $cv->globrefs ) {
278 0           Devel::MAT::Cmd->printf( "Referenced globs:\n " );
279 0           Devel::MAT::Cmd->printf( "%s, ", Devel::MAT::Cmd->format_sv( $_ ) ) for @globs;
280 0           Devel::MAT::Cmd->printf( "\n" );
281             }
282             }
283              
284             sub show_PAD
285             {
286 0     0 0   my $self = shift;
287 0           my ( $pad ) = @_;
288              
289 0           my $padcv = $pad->padcv;
290 0 0         $padcv ? say_with_sv( " padcv=", $padcv )
291             : Devel::MAT::Cmd->printf( " no padcv\n" );
292              
293 0           $self->show_PAD_contents( $pad );
294             }
295              
296             sub _join
297             {
298             # Like CORE::join but respects string concat operator
299 0     0     my ( $sep, @elems ) = @_;
300 0           my $ret = shift @elems;
301 0           $ret = $ret . $sep . $_ for @elems;
302 0           return $ret;
303             }
304              
305             sub show_PAD_contents
306             {
307 0     0 0   my $self = shift;
308 0           my ( $pad ) = @_;
309              
310 0           my $padcv = $pad->padcv;
311              
312 0           my @elems = $pad->elems;
313             my @padnames = map {
314 0           my $padname = $padcv->padname( $_ );
  0            
315             # is_outer is always set for is_our; it's only interesting without is_our
316 0   0       my $is_just_outer = $padname && $padname->is_outer && !$padname->is_our;
317              
318 0 0         $padname ? _join( " ",
    0          
    0          
    0          
    0          
319             ( $padname->is_state ? Devel::MAT::Cmd->format_note( "state" ) : () ),
320             ( $padname->is_our ? Devel::MAT::Cmd->format_note( "our" ) : () ),
321             ( $padname->is_field ? Devel::MAT::Cmd->format_note( "field" ) : () ),
322             Devel::MAT::Cmd->format_note( $padname->name, 1 ),
323             ( $is_just_outer ? Devel::MAT::Cmd->format_note( "*OUTER", 2 ) : () ),
324             # is_typed and is_lvalue not indicated
325             ) : undef
326             } 0 .. $#elems;
327 0           my $idxlen = length $#elems;
328 0 0         my $namelen = max map { defined $_ ? length $_ : 0 } @padnames;
  0            
329              
330 0           my %padtype;
331 0 0         if( my $gvix = $padcv->{gvix} ) {
332 0           $padtype{$_} = "GLOB" for @$gvix;
333             }
334 0 0         if( my $constix = $padcv->{constix} ) {
335 0           $padtype{$_} = "CONST" for @$constix;
336             }
337              
338 0 0         Devel::MAT::Cmd->printf( " [%*d/%-*s]=%s\n",
339             $idxlen, 0,
340             $namelen, Devel::MAT::Cmd->format_note( '@_', 1 ),
341             ( $elems[0] ? Devel::MAT::Cmd->format_sv_with_value( $elems[0] ) : "NULL" ),
342             );
343              
344 0           foreach my $padix ( 1 .. $#elems ) {
345 0           my $sv = $elems[$padix];
346 0 0         if( $padnames[$padix] ) {
347 0 0         Devel::MAT::Cmd->printf( " [%*d/%-*s]=%s\n",
348             $idxlen, $padix,
349             $namelen, $padnames[$padix],
350             ( $sv ? Devel::MAT::Cmd->format_sv_with_value( $sv ) : "NULL" ),
351             );
352             }
353             else {
354             Devel::MAT::Cmd->printf( " [%*d %-*s]=%s\n",
355             $idxlen, $padix,
356 0 0 0       $namelen, $padtype{$padix} // "",
357             ( $sv ? Devel::MAT::Cmd->format_sv_with_value( $sv ) : "NULL" ),
358             );
359             }
360             }
361             }
362              
363             # TODO: PADLIST
364              
365             sub show_PADNAMES
366             {
367 0     0 0   my $self = shift;
368 0           my ( $padnames ) = @_;
369              
370 0 0         $padnames->padcv ? say_with_sv( " padcv=", $padnames->padcv )
371             : Devel::MAT::Cmd->printf( " no padcv\n" );
372              
373 0           my @elems = $padnames->elems;
374             # Every PADNAMES element is either NULL or a SCALAR(PV)
375             # PADIX 0 is always @_
376 0           foreach my $padix ( 1 .. $#elems ) {
377 0           my $slot = $elems[$padix];
378 0 0 0       if( $slot and $slot->type eq "SCALAR" ) {
379 0           Devel::MAT::Cmd->printf( " [%d] is %s\n", $padix, Devel::MAT::Cmd->format_note( $slot->pv, 1 ) );
380             }
381             }
382             }
383              
384             sub show_IO
385             {
386 0     0 0   my $self = shift;
387 0           my ( $io ) = @_;
388              
389 0 0         Devel::MAT::Cmd->printf( " ifileno=%d\n", $io->ifileno ) if defined $io->ifileno;
390 0 0         Devel::MAT::Cmd->printf( " ofileno=%d\n", $io->ofileno ) if defined $io->ofileno;
391             }
392              
393             sub show_OBJECT
394             {
395 0     0 0   my $self = shift;
396 0           my ( $obj ) = @_;
397              
398 0           my @fields = $obj->fields;
399              
400 0           foreach my $field ( $obj->blessed->fields ) {
401 0           my $val = $obj->field( $field->fieldix );
402              
403 0           Devel::MAT::Cmd->printf( " %s=%s\n",
404             Devel::MAT::Cmd->format_note( $field->name, 1 ),
405             Devel::MAT::Cmd->format_sv_with_value( $val )
406             );
407             }
408             }
409              
410             sub show_CLASS
411             {
412 0     0 0   my $self = shift;
413 0           my ( $cls ) = @_;
414              
415 0           Devel::MAT::Cmd->printf( " is CLASS\n" );
416              
417 0 0         $cls->adjust_blocks ? say_with_sv( " adjust_blocks=", $cls->adjust_blocks )
418             : ();
419              
420 0           $self->show_STASH( $cls );
421             }
422              
423             sub show_C_STRUCT
424             {
425 0     0 0   my $self = shift;
426 0           my ( $struct ) = @_;
427              
428 0           my @fields = $struct->fields;
429              
430 0           while( @fields ) {
431 0           my $field = shift @fields;
432 0           my $val = shift @fields;
433              
434 0 0         next unless defined $val;
435              
436 0 0         if( $field->type == 0x00 ) { # PTR
    0          
437 0           Devel::MAT::Cmd->printf( " %s=%s\n",
438             $field->name,
439             Devel::MAT::Cmd->format_sv_with_value( $val )
440             );
441             }
442             elsif( $field->type == 0x01 ) { # BOOL
443 0 0         Devel::MAT::Cmd->printf( " %s=%s\n",
444             $field->name,
445             Devel::MAT::Cmd->format_value( $val ? "true" : "false" )
446             );
447             }
448             else { # various number types
449 0           Devel::MAT::Cmd->printf( " %s=%s\n",
450             $field->name,
451             Devel::MAT::Cmd->format_value( $val ),
452             );
453             }
454             }
455             }
456              
457             package # hide
458             Devel::MAT::Tool::Show::_elems;
459 5     5   50 use base qw( Devel::MAT::Tool );
  5         13  
  5         554  
460              
461 5     5   34 use List::Util qw( min );
  5         9  
  5         340  
462              
463 5     5   30 use constant CMD => "elems";
  5         15  
  5         323  
464 5     5   31 use constant CMD_DESC => "List the elements of an ARRAY SV";
  5         12  
  5         388  
465              
466             =head2 elems
467              
468             pmat> elems endav
469             [0] CODE(PP) at 0x562e93222dc8
470              
471             Prints elements of an ARRAY SV.
472              
473             Takes the following named options:
474              
475             =over 4
476              
477             =item --count, -c MAX
478              
479             Show at most this number of elements (default 50).
480              
481             =back
482              
483             Takes the following positional arguments:
484              
485             =over 4
486              
487             =item *
488              
489             Optional start index (default 0).
490              
491             =back
492              
493             =cut
494              
495 5         290 use constant CMD_OPTS => (
496             count => { help => "maximum count of elements to print",
497             type => "i",
498             alias => "c",
499             default => 50 },
500 5     5   39 );
  5         11  
501              
502 5     5   29 use constant CMD_ARGS_SV => 1;
  5         24  
  5         264  
503 5         1423 use constant CMD_ARGS => (
504             { name => "startidx", help => "starting index" },
505 5     5   27 );
  5         12  
506              
507             sub run
508             {
509 0     0     my $self = shift;
510 0           my %opts = %{ +shift };
  0            
511 0           my ( $av, $startidx ) = @_;
512              
513 0           my $type = $av->type;
514 0 0 0       if( $type eq "HASH" or $type eq "STASH" ) {
    0          
515 0           die "Cannot 'elems' of a $type - maybe you wanted 'values'?\n";
516             }
517             elsif( $type ne "ARRAY" ) {
518 0           die "Cannot 'elems' of a non-ARRAY\n";
519             }
520              
521 0   0       $startidx //= 0;
522 0           my $stopidx = min( $startidx + $opts{count}, $av->n_elems );
523              
524 0           my @rows;
525 0           foreach my $idx ( $startidx .. $stopidx-1 ) {
526 0           my $sv = $av->elem( $idx );
527 0 0         push @rows, [
528             Devel::MAT::Cmd->format_value( $idx, index => 1 ),
529             $sv ? Devel::MAT::Cmd->format_sv_with_value( $sv ) : "NULL",
530             ];
531             }
532              
533 0           Devel::MAT::Cmd->print_table( \@rows, indent => 2 );
534              
535 0           my $morecount = $av->n_elems - $stopidx;
536 0 0         Devel::MAT::Cmd->printf( " ... (%d more)\n", $morecount ) if $morecount;
537             }
538              
539             package # hide
540             Devel::MAT::Tool::Show::_values;
541 5     5   35 use base qw( Devel::MAT::Tool );
  5         18  
  5         458  
542              
543 5     5   47 use constant CMD => "values";
  5         14  
  5         269  
544 5     5   38 use constant CMD_DESC => "List the values of a HASH-like SV";
  5         16  
  5         350  
545              
546             =head2 values
547              
548             pmat> values defstash
549             {"\b"} GLOB($%*) at 0x562e93114eb8
550             {"\017"} GLOB($*) at 0x562e9315a428
551             ...
552              
553             Prints values of a HASH or STASH SV.
554              
555             Takes the following named options:
556              
557             =over 4
558              
559             =item --count, -c MAX
560              
561             Show at most this number of values (default 50).
562              
563             =back
564              
565             Takes the following positional arguments:
566              
567             =over 4
568              
569             =item *
570              
571             Optional skip count (default 0). If present, will skip over this number of
572             keys initially to show more of them.
573              
574             =back
575              
576             =cut
577              
578 5         301 use constant CMD_OPTS => (
579             count => { help => "maximum count of values to print",
580             type => "i",
581             alias => "c",
582             default => 50 },
583 5     5   33 );
  5         17  
584              
585 5     5   30 use constant CMD_ARGS_SV => 1;
  5         9  
  5         250  
586 5         1544 use constant CMD_ARGS => (
587             { name => "skipcount", help => "skip over this many keys initially" },
588 5     5   31 );
  5         25  
589              
590             sub run
591             {
592 0     0     my $self = shift;
593 0           my %opts = %{ +shift };
  0            
594 0           my ( $hv, $skipcount ) = @_;
595              
596 0           my $type = $hv->type;
597 0 0 0       if( $type eq "ARRAY" ) {
    0          
598 0           die "Cannot 'values' of a $type - maybe you wanted 'elems'?\n";
599             }
600             elsif( $type ne "HASH" and $type ne "STASH" ) {
601 0           die "Cannot 'elems' of a non-HASHlike\n";
602             }
603              
604             # TODO: control of sorting, start at, filtering
605 0           my @keys = sort $hv->keys;
606 0 0         splice @keys, 0, $skipcount if $skipcount;
607              
608             Devel::MAT::Tool::more->paginate( { pagesize => $opts{count} }, sub {
609 0     0     my ( $count ) = @_;
610 0           my @rows;
611 0           foreach my $key ( splice @keys, 0, $count ) {
612 0           my $sv = $hv->value( $key );
613 0 0         push @rows, [
614             Devel::MAT::Cmd->format_value( $key, key => 1,
615             stash => ( $type eq "STASH" ) ),
616             $sv ? Devel::MAT::Cmd->format_sv_with_value( $sv ) : "NULL",
617             ];
618             }
619              
620 0           Devel::MAT::Cmd->print_table( \@rows, indent => 2 );
621              
622 0           my $morecount = @keys;
623 0 0         Devel::MAT::Cmd->printf( " ... (%d more)\n", $morecount ) if $morecount;
624 0           return $morecount;
625 0           } );
626             }
627              
628             =head1 AUTHOR
629              
630             Paul Evans
631              
632             =cut
633              
634             0x55AA;