File Coverage

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


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