File Coverage

blib/lib/Devel/MAT/Dumpfile.pm
Criterion Covered Total %
statement 247 297 83.1
branch 77 120 64.1
condition 12 24 50.0
subroutine 46 57 80.7
pod 17 18 94.4
total 399 516 77.3


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, 2013-2022 -- leonerd@leonerd.org.uk
5              
6             package Devel::MAT::Dumpfile 0.49;
7              
8 9     9   1231 use v5.14;
  9         26  
9 9     9   41 use warnings;
  9         10  
  9         190  
10              
11 9     9   33 use Carp;
  9         15  
  9         382  
12 9     9   5080 use IO::Handle; # ->read
  9         49249  
  9         446  
13 9     9   4364 use IO::Seekable; # ->tell
  9         4053  
  9         424  
14              
15 9     9   52 use List::Util qw( pairmap );
  9         14  
  9         429  
16              
17 9     9   4699 use Devel::MAT::SV;
  9         26  
  9         325  
18 9     9   3126 use Devel::MAT::Context;
  9         17  
  9         257  
19              
20 9     9   43 use Struct::Dumb 0.07 qw( readonly_struct );
  9         132  
  9         40  
21             readonly_struct StructType => [qw( name fields )];
22             readonly_struct StructField => [qw( name type )];
23              
24             use constant {
25 9         2664 PMAT_SVxMAGIC => 0x80,
26 9     9   653 };
  9         16  
27              
28             =head1 NAME
29              
30             C - load and analyse a heap dump file
31              
32             =head1 SYNOPSIS
33              
34             use Devel::MAT::Dumpfile;
35              
36             my $df = Devel::MAT::Dumpfile->load( "path/to/the/file.pmat" );
37              
38             TODO
39              
40             =head1 DESCRIPTION
41              
42             This module provides a class that loads a heap dump file previously written by
43             L. It provides accessor methods to obtain various
44             well-known root starting addresses, or to find arbitrary SVs by address. Each
45             SV is represented by an instance of L.
46              
47             =cut
48              
49             my @ROOTS;
50             my %ROOTDESC;
51             foreach (
52             [ sv_undef => "+the undef SV" ],
53             [ sv_yes => "+the true SV" ],
54             [ sv_no => "+the false SV" ],
55             [ main_cv => "+the main code" ],
56             [ defstash => "+the default stash" ],
57             [ mainstack => "+the main stack AV" ],
58             [ beginav => "+the BEGIN list" ],
59             [ checkav => "+the CHECK list" ],
60             [ unitcheckav => "+the UNITCHECK list" ],
61             [ initav => "+the INIT list" ],
62             [ endav => "+the END list" ],
63             [ strtab => "+the shared string table HV" ],
64             [ envgv => "-the ENV GV" ],
65             [ incgv => "+the INC GV" ],
66             [ statgv => "+the stat GV" ],
67             [ statname => "+the statname SV" ],
68             [ tmpsv => "-the temporary SV" ],
69             [ defgv => "+the default GV" ],
70             [ argvgv => "-the ARGV GV" ],
71             [ argvoutgv => "+the argvout GV" ],
72             [ argvout_stack => "+the argvout stack AV" ],
73             [ errgv => "+the *@ GV" ],
74             [ fdpidav => "+the FD-to-PID mapping AV" ],
75             [ preambleav => "+the compiler preamble AV" ],
76             [ modglobalhv => "+the module data globals HV" ],
77             [ regex_padav => "+the REGEXP pad AV" ],
78             [ sortstash => "+the sort stash" ],
79             [ firstgv => "-the *a GV" ],
80             [ secondgv => "-the *b GV" ],
81             [ debstash => "-the debugger stash" ],
82             [ stashcache => "+the stash cache" ],
83             [ isarev => "+the reverse map of \@ISA dependencies" ],
84             [ registered_mros => "+the registered MROs HV" ],
85             [ rs => "+the IRS" ],
86             [ last_in_gv => "+the last input GV" ],
87             [ ofsgv => "+the OFS GV" ],
88             [ defoutgv => "+the default output GV" ],
89             [ hintgv => "-the hints (%^H) GV" ],
90             [ patchlevel => "+the patch level" ],
91             [ apiversion => "+the API version" ],
92             [ e_script => "+the '-e' script" ],
93             [ mess_sv => "+the message SV" ],
94             [ ors_sv => "+the ORS SV" ],
95             [ encoding => "+the encoding" ],
96             [ blockhooks => "+the block hooks" ],
97             [ custom_ops => "+the custom ops HV" ],
98             [ custom_op_names => "+the custom op names HV" ],
99             [ custom_op_descs => "+the custom op descriptions HV" ],
100             map { [ $_ => "+the $_" ] } qw(
101             Latin1 UpperLatin1 AboveLatin1 NonL1NonFinalFold HasMultiCharFold
102             utf8_mark utf8_X_regular_begin utf8_X_extend utf8_toupper utf8_totitle
103             utf8_tolower utf8_tofold utf8_charname_begin utf8_charname_continue
104             utf8_idstart utf8_idcont utf8_xidstart utf8_perl_idstart utf8_perl_idcont
105             utf8_xidcont utf8_foldclosures utf8_foldable ),
106             ) {
107             my ( $name, $desc ) = @$_;
108             push @ROOTS, $name;
109             $ROOTDESC{$name} = $desc;
110              
111             # Autogenerate the accessors
112             my $code = sub {
113 42     42   83 my $self = shift;
114 42 50       251 $self->{roots}{$name} ? $self->sv_at( $self->{roots}{$name}[0] ) : undef;
115             };
116 9     9   57 no strict 'refs';
  9         14  
  9         28965  
117             *$name = $code;
118             }
119              
120 0     0   0 *ROOTS = sub { @ROOTS };
121              
122             =head1 CONSTRUCTOR
123              
124             =cut
125              
126             =head2 load
127              
128             $df = Devel::MAT::Dumpfile->load( $path, %args )
129              
130             Loads a heap dump file from the given path, and returns a new
131             C instance representing it.
132              
133             Takes the following named arguments:
134              
135             =over 8
136              
137             =item progress => CODE
138              
139             If given, should be a CODE reference to a function that will be called
140             regularly during the loading process, and given a status message to update the
141             user.
142              
143             =back
144              
145             =cut
146              
147             sub load
148             {
149 7     7 1 18 my $class = shift;
150 7         31 my ( $path, %args ) = @_;
151              
152 7         25 my $progress = $args{progress};
153              
154 7 50       45 $progress->( "Loading file $path..." ) if $progress;
155              
156 7 50       580 open my $fh, "<", $path or croak "Cannot read $path - $!";
157 7         58 my $self = bless { fh => $fh }, $class;
158              
159 7         74 my $filelen = -s $fh;
160              
161             # Header
162 7 50       44 $self->_read(4) eq "PMAT" or croak "File magic signature not found";
163              
164 7         37 my $flags = $self->_read_u8;
165              
166 7 50       51 my $endian = ( $self->{big_endian} = $flags & 0x01 ) ? ">" : "<";
167              
168 7         33 my $u32_fmt = $self->{u32_fmt} = "L$endian";
169 7         23 my $u64_fmt = $self->{u64_fmt} = "Q$endian";
170              
171 7 50       31 @{$self}{qw( uint_len uint_fmt )} =
  7         37  
172             ( $flags & 0x02 ) ? ( 8, $u64_fmt ) : ( 4, $u32_fmt );
173              
174 7 50       27 @{$self}{qw( ptr_len ptr_fmt )} =
  7         24  
175             ( $flags & 0x04 ) ? ( 8, $u64_fmt ) : ( 4, $u32_fmt );
176              
177 7 50       31 @{$self}{qw( nv_len nv_fmt )} =
  7         17  
178             ( $flags & 0x08 ) ? ( 10, "D$endian" ) : ( 8, "d$endian" );
179              
180 7         26 $self->{ithreads} = !!( $flags & 0x10 );
181              
182 7         13 $flags &= ~0x1f;
183 7 50       22 die sprintf "Cannot read %s - unrecognised flags %x\n", $path, $flags if $flags;
184              
185 7         60 $self->{minus_1} = unpack $self->{uint_fmt}, pack $self->{uint_fmt}, -1;
186              
187 7 50       23 $self->_read_u8 == 0 or die "Cannot read $path - 'zero' header field is not zero";
188              
189 7 50       21 $self->_read_u8 == 0 or die "Cannot read $path - format version major unrecognised";
190              
191             # minor version 5 is the still-experimental support for feature-class
192 7 50       22 ( $self->{format_minor} = $self->_read_u8 ) <= 5 or
193             die "Cannot read $path - format version minor unrecognised ($self->{format_minor})";
194 7 50       25 warnings::warnif experimental => "Support for PMAT file format v0.5 is experimental" if $self->{format_minor} == 5;
195              
196 7 50       26 if( $self->{format_minor} < 1 ) {
197 0         0 warn "Loading an earlier format of dumpfile - SV MAGIC annotations may be incorrect\n";
198             }
199              
200 7         29 $self->{perlver} = $self->_read_u32;
201              
202 7         18 my $n_types = $self->_read_u8;
203 7         30 my @sv_sizes = unpack "(a3)*", my $tmp = $self->_read( $n_types * 3 );
204 7         143 $self->{sv_sizes} = [ map [ unpack "C C C", $_ ], @sv_sizes ];
205              
206 7 50       30 if( $self->{format_minor} >= 4 ) {
207 7         18 my $n_extns = $self->_read_u8;
208 7         24 my @extn_sizes = unpack "(a3)*", my $tmp = $self->_read( $n_extns * 3 );
209 7         84 $self->{svx_sizes} = [ map [ unpack "C C C", $_ ], @extn_sizes ];
210             }
211             else {
212             # versions < 4 had just one, PMAT_SVxMAGIC
213             $self->{svx_sizes} = [
214 0         0 [ 2, 2, 0 ], # PMAT_SVxMAGIC
215             ];
216             }
217              
218 7 50       35 if( $self->{format_minor} >= 2 ) {
219 7         17 my $n_ctxs = $self->_read_u8;
220 7         26 my @ctx_sizes = unpack "(a3)*", my $tmp = $self->_read( $n_ctxs * 3 );
221 7         49 $self->{ctx_sizes} = [ map [ unpack "C C C", $_ ], @ctx_sizes ];
222             }
223              
224 7         18 $self->{structtypes_by_id} = {};
225              
226             # Roots
227 7         27 foreach (qw( undef yes no )) {
228 21         55 my $addr = $self->{"${_}_at"} = $self->_read_ptr;
229 21         54 my $class = "Devel::MAT::SV::\U$_";
230 21         187 $self->{uc $_} = $class->new( $self, $addr );
231             }
232              
233 7         36 $self->{roots} = \my %roots;
234             # The three immortals
235 7         148 $roots{"sv_$_"} = [ $self->{"\U$_"}->addr, $ROOTDESC{"sv_$_"} ] for qw( undef yes no );
236              
237 7         27 foreach ( 1 .. $self->_read_u32 ) {
238 441         623 my $name = $self->_read_str;
239 441   66     1115 my $desc = $ROOTDESC{$name} // $name;
240 441 100       1039 $desc =~ m/^[+-]/ or $desc = "+$desc";
241 441         639 $roots{$name} = [ $self->_read_ptr, $desc ];
242             }
243              
244             # Stack
245 7         17 my $stacksize = $self->_read_uint;
246 7         36 $self->{stack_at} = [ map { $self->_read_ptr } 1 .. $stacksize ];
  15         40  
247              
248             # Heap
249 7         23 $self->{heap} = \my %heap;
250 7         14 $self->{protosubs_by_oproot} = \my %protosubs_by_oproot;
251 7         34 while( my $sv = $self->_read_sv ) {
252 539875         2135007 $heap{$sv->addr} = $sv;
253              
254             # Also identify the protosub of every oproot
255 539875 100 100     1133032 if( $sv->type eq "CODE" and $sv->oproot and $sv->is_clone ) {
      100        
256 888         3730 $protosubs_by_oproot{$sv->oproot} = $sv;
257             }
258              
259 539875         1031055 my $pos = $fh->IO::Seekable::tell; # fully-qualified method for 5.010
260 539875 50 33     2492020 $progress->( sprintf "Loading file %d of %d bytes (%.2f%%)",
261             $pos, $filelen, 100*$pos / $filelen ) if $progress and (keys(%heap) % 5000) == 0;
262             }
263              
264             # Contexts
265 7         31 $self->{contexts} = \my @contexts;
266 7         43 while( my $ctx = $self->_read_ctx ) {
267 5         10 push @contexts, $ctx;
268             }
269              
270 7 50       62 $self->_fixup( %args ) unless $args{no_fixup};
271              
272 7         127 return $self;
273             }
274              
275             sub structtype
276             {
277 0     0 0 0 my $self = shift;
278 0         0 my ( $id ) = @_;
279              
280 0   0     0 return $self->{structtypes_by_id}{$id} //
281             croak "Dumpfile does not define a struct type of ID=$id\n";
282             }
283              
284             sub _fixup
285             {
286 7     7   19 my $self = shift;
287 7         23 my %args = @_;
288              
289 7         19 my $progress = $args{progress};
290              
291 7         17 my $heap = $self->{heap};
292              
293 7         21 my $heap_total = scalar keys %$heap;
294              
295             # Annotate each root SV
296 7         14 foreach my $name ( keys %{ $self->{roots} } ) {
  7         139  
297 462 100       572 my $sv = $self->root( $name ) or next;
298 239         526 $sv->{rootname} = $name;
299             }
300              
301 7         32 my $count = 0;
302 7         58 while( my ( $addr ) = each %$heap ) {
303 539875 50       1118514 my $sv = $heap->{$addr} or next;
304              
305             # While dumping we weren't able to determine what ARRAYs were really
306             # PADLISTs. Now we can fix them up
307 539875 100       1011475 $sv->_fixup if $sv->can( "_fixup" );
308              
309 539875         536726 $count++;
310 539875 50 33     1462943 $progress->( sprintf "Fixing %d of %d (%.2f%%)",
311             $count, $heap_total, 100*$count / $heap_total ) if $progress and ($count % 20000) == 0;
312             }
313              
314             # Walk the SUB contexts setting their true depth
315 7 50       43 if( $self->{format_minor} >= 2 ) {
316 7         18 my %prev_depth_by_cvaddr;
317              
318 7         21 foreach my $ctx ( @{ $self->{contexts} } ) {
  7         77  
319 5 100       13 next unless $ctx->type eq "SUB";
320              
321 3         4 my $cvaddr = $ctx->{cv_at};
322 3   33     12 $ctx->_set_depth( $prev_depth_by_cvaddr{$cvaddr} // $ctx->cv->depth );
323              
324 3         5 $prev_depth_by_cvaddr{$cvaddr} = $ctx->olddepth;
325             }
326             }
327              
328 7         42 return $self;
329             }
330              
331             # Nicer interface to IO::Handle
332             sub _read
333             {
334 1475492     1475492   1497810 my $self = shift;
335 1475492         1631774 my ( $len ) = @_;
336 1475492 100       2000501 return "" if $len == 0;
337 1471242 50       2206190 defined( $self->{fh}->read( my $buf, $len ) ) or croak "Cannot read - $!";
338 1471242         8226004 return $buf;
339             }
340              
341             sub _read_u8
342             {
343 1042624     1042624   2001329 my $self = shift;
344 1042624 50       1694088 defined( $self->{fh}->read( my $buf, 1 ) ) or croak "Cannot read - $!";
345 1042624         5184891 return unpack "C", $buf;
346             }
347              
348             sub _read_u32
349             {
350 14     14   22 my $self = shift;
351 14 50       36 defined( $self->{fh}->read( my $buf, 4 ) ) or croak "Cannot read - $!";
352 14         120 return unpack $self->{u32_fmt}, $buf;
353             }
354              
355             sub _read_u64
356             {
357 0     0   0 my $self = shift;
358 0 0       0 defined( $self->{fh}->read( my $buf, 8 ) ) or croak "Cannot read - $!";
359 0         0 return unpack $self->{u64_fmt}, $buf;
360             }
361              
362             sub _read_uint
363             {
364 932277     932277   941737 my $self = shift;
365 932277 50       1431524 defined( $self->{fh}->read( my $buf, $self->{uint_len} ) ) or croak "Cannot read - $!";
366 932277         4251371 return unpack $self->{uint_fmt}, $buf;
367             }
368              
369             sub _read_ptr
370             {
371 560614     560614   596992 my $self = shift;
372 560614 50       843223 defined( $self->{fh}->read( my $buf, $self->{ptr_len} ) ) or croak "Cannot read - $!";
373 560614         3023274 return unpack $self->{ptr_fmt}, $buf;
374             }
375              
376             sub _read_ptrs
377             {
378 939154     939154   1040728 my $self = shift;
379 939154         1078429 my ( $n ) = @_;
380 939154 50       1535977 defined( $self->{fh}->read( my $buf, $self->{ptr_len} * $n ) ) or croak "Cannot read - $!";
381 939154         6171316 return unpack "$self->{ptr_fmt}$n", $buf;
382             }
383              
384             sub _read_nv
385             {
386 0     0   0 my $self = shift;
387 0 0       0 defined( $self->{fh}->read( my $buf, $self->{nv_len} ) ) or croak "Cannot read - $!";
388 0         0 return unpack $self->{nv_fmt}, $buf;
389             }
390              
391             sub _read_str
392             {
393 715012     715012   777364 my $self = shift;
394 715012         871337 my $len = $self->_read_uint;
395 715012 100       1406429 return undef if $len == $self->{minus_1};
396 513478         725792 return $self->_read($len);
397             }
398              
399             sub _read_bytesptrsstrs
400             {
401 1120378     1120378   1199662 my $self = shift;
402 1120378         1343615 my ( $nbytes, $nptrs, $nstrs ) = @_;
403              
404             return
405             ( $nbytes ? $self->_read( $nbytes ) : "" ),
406             ( $nptrs ? [ $self->_read_ptrs( $nptrs ) ] : undef ),
407 1120378 100       1885457 ( $nstrs ? [ map { $self->_read_str } 1 .. $nstrs ] : undef );
  388493 100       581359  
    100          
408             }
409              
410             sub _read_sv
411             {
412 539882     539882   609221 my $self = shift;
413              
414 539882         561988 while(1) {
415 580500         759403 my $type = $self->_read_u8;
416 580500 100       855493 return if !$type;
417              
418 580493 50       1204476 if( $type >= 0xF1 ) {
    50          
    100          
419 0         0 die sprintf "Unrecognised META tag %02X\n", $type;
420             }
421             elsif( $type == 0xF0 ) {
422             # META_STRUCT
423 0         0 my $id = $self->_read_uint;
424 0         0 my $nfields = $self->_read_uint;
425 0         0 my $name = $self->_read_str;
426              
427 0         0 my @fields;
428             push @fields, StructField(
429             $self->_read_str,
430             $self->_read_u8,
431 0         0 ) for 1 .. $nfields;
432              
433 0         0 $self->{structtypes_by_id}{$id} = StructType(
434             $name, \@fields,
435             );
436              
437 0         0 next;
438             }
439             elsif( $type >= 0x80 ) {
440 40618         69901 my $sizes = $self->{svx_sizes}[$type - 0x80];
441              
442 40618 50 33     106417 if( $self->{format_minor} == 0 and $type == PMAT_SVxMAGIC ) {
    50          
443             # legacy magic support
444 0         0 my ( $sv_addr, $obj ) = $self->_read_ptrs(2);
445 0         0 my $type = chr $self->_read_u8;
446              
447 0         0 my $sv = $self->sv_at( $sv_addr );
448              
449             # Legacy format didn't have flags, and didn't distinguish obj from ptr
450             # However, the only objs it ever saved were refcounted ones. Lets just
451             # pretend all of them are refcounted objects.
452 0         0 $sv->more_magic( $type => 0x01, $obj, 0, 0 );
453             }
454             elsif( !$sizes ) {
455 0         0 die sprintf "Unrecognised SV extension type %02x\n", $type;
456             }
457             else {
458 40618         66331 my $sv_addr = $self->_read_ptr;
459 40618         80376 my $sv = $self->sv_at( $sv_addr );
460              
461 40618 50       216644 if( my $code = $self->can( sprintf "_read_svx_%02X", $type ) ) {
462 40618         80416 $self->$code( $sv, $self->_read_bytesptrsstrs( @$sizes ) );
463             }
464             else {
465 0         0 warn sprintf "Skipping unrecognised SVx 0x%02X\n", $type;
466 0         0 $self->_read_bytesptrsstrs( @$sizes ); # ignore
467             }
468             }
469              
470 40618         84286 next;
471             }
472              
473             # First read the "common" header
474             my $sv = Devel::MAT::SV->new( $type, $self,
475 539875         593525 $self->_read_bytesptrsstrs( @{ $self->{sv_sizes}[0] } )
  539875         923761  
476             );
477              
478 539875 50       963756 if( $type == 0x7F ) {
479 0         0 my $structtype = $self->structtype( $sv->structid );
480 0         0 $sv->load( $structtype->fields );
481             }
482             else {
483 539875         556275 my ( $bytes, $nptrs, $nstrs ) = @{ $self->{sv_sizes}[$type] };
  539875         845097  
484 539875         801389 $sv->load(
485             $self->_read_bytesptrsstrs( $bytes, $nptrs, $nstrs )
486             );
487             }
488              
489 539875         1305944 return $sv;
490             }
491             }
492              
493             sub _read_svx_80
494             {
495 40611     40611   57876 my $self = shift;
496 40611         60758 my ( $sv, $bytes, $ptrs, $strs ) = @_;
497              
498 40611         99965 my ( $type, $flags ) = unpack "A1 C", $bytes;
499              
500 40611         105701 $sv->more_magic( $type => $flags, @$ptrs );
501             }
502              
503             sub _read_svx_81
504             {
505 2     2   5 my $self = shift;
506 2         7 my ( $sv, $bytes, $ptrs, $strs ) = @_;
507              
508 2         11 $sv->_more_saved( SCALAR => $ptrs->[0] );
509             }
510              
511             sub _read_svx_82
512             {
513 1     1   4 my $self = shift;
514 1         3 my ( $sv, $bytes, $ptrs, $strs ) = @_;
515              
516 1         4 $sv->_more_saved( ARRAY => $ptrs->[0] );
517             }
518              
519             sub _read_svx_83
520             {
521 1     1   2 my $self = shift;
522 1         3 my ( $sv, $bytes, $ptrs, $strs ) = @_;
523              
524 1         5 $sv->_more_saved( HASH => $ptrs->[0] );
525             }
526              
527             sub _read_svx_84
528             {
529 1     1   2 my $self = shift;
530 1         3 my ( $av, $bytes, $ptrs, $strs ) = @_;
531              
532 1         4 my $index = unpack $self->{uint_fmt}, $bytes;
533              
534 1 50       11 $av->isa( "Devel::MAT::SV::ARRAY" ) and
535             $av->_more_saved( $index, $ptrs->[0] );
536             }
537              
538             sub _read_svx_85
539             {
540 1     1   3 my $self = shift;
541 1         4 my ( $hv, $bytes, $ptrs, $strs ) = @_;
542              
543 1 50       16 $hv->isa( "Devel::MAT::SV::HASH" ) and
544             $hv->_more_saved( $ptrs->[0], $ptrs->[1] );
545             }
546              
547             sub _read_svx_86
548             {
549 1     1   3 my $self = shift;
550 1         3 my ( $sv, $bytes, $ptrs, $strs ) = @_;
551              
552 1         7 $sv->_more_saved( CODE => $ptrs->[0] );
553             }
554              
555             sub _read_svx_87
556             {
557 0     0   0 my $self = shift;
558 0         0 my ( $sv, $bytes, $ptrs, $strs ) = @_;
559              
560 0         0 $sv->_more_annotations( $ptrs->[0], $strs->[0] );
561             }
562              
563             sub _read_svx_88
564             {
565 0     0   0 my $self = shift;
566 0         0 my ( $sv, $bytes, $ptrs, $strs ) = @_;
567              
568 0         0 my ( $serial, $line ) = unpack "($self->{uint_fmt})2", $bytes;
569 0         0 my $file = $strs->[0];
570              
571 0         0 $sv->_debugdata( $serial, $line, $file );
572             }
573              
574             sub _read_ctx
575             {
576 12     12   24 my $self = shift;
577              
578 12         35 my $type = $self->_read_u8;
579 12 100       54 return if !$type;
580              
581 5 50       9 if( $self->{format_minor} >= 2 ) {
582             my $ctx = Devel::MAT::Context->new( $type, $self,
583 5         7 $self->_read_bytesptrsstrs( @{ $self->{ctx_sizes}[0] } )
  5         8  
584             );
585              
586             $ctx->load(
587 5         7 $self->_read_bytesptrsstrs( @{ $self->{ctx_sizes}[$type] } )
  5         11  
588             );
589              
590 5         12 return $ctx;
591             }
592             else {
593 0         0 return Devel::MAT::Context->load_v0_1( $type, $self );
594             }
595             }
596              
597             =head1 METHODS
598              
599             =cut
600              
601             =head2 perlversion
602              
603             $version = $df->perlversion
604              
605             Returns the version of perl that the heap dump file was created by, as a
606             string in the form C<5.14.2>.
607              
608             =cut
609              
610             sub perlversion
611             {
612 3     3 1 4 my $self = shift;
613 3         4 my $v = $self->{perlver};
614 3         15 return join ".", $v>>24, ($v>>16) & 0xff, $v&0xffff;
615             }
616              
617             =head2 endian
618              
619             $endian = $df->endian
620              
621             Returns the endian direction of the perl that the heap dump was created by, as
622             either C or C.
623              
624             =cut
625              
626             sub endian
627             {
628 0     0 1 0 my $self = shift;
629 0 0       0 return $self->{big_endian} ? "big" : "little";
630             }
631              
632             =head2 uint_len
633              
634             $len = $df->uint_len
635              
636             Returns the length in bytes of a uint field of the perl that the heap dump was
637             created by.
638              
639             =cut
640              
641             sub uint_len
642             {
643 0     0 1 0 my $self = shift;
644 0         0 return $self->{uint_len};
645             }
646              
647             =head2 ptr_len
648              
649             $len = $df->ptr_len
650              
651             Returns the length in bytes of a pointer field of the perl that the heap dump
652             was created by.
653              
654             =cut
655              
656             sub ptr_len
657             {
658 0     0 1 0 my $self = shift;
659 0         0 return $self->{ptr_len};
660             }
661              
662             =head2 nv_len
663              
664             $len = $df->nv_len
665              
666             Returns the length in bytes of a double field of the perl that the heap dump
667             was created by.
668              
669             =cut
670              
671             sub nv_len
672             {
673 0     0 1 0 my $self = shift;
674 0         0 return $self->{nv_len};
675             }
676              
677             =head2 ithreads
678              
679             $ithreads = $df->ithreads
680              
681             Returns a boolean indicating whether ithread support was enabled in the perl
682             that the heap dump was created by.
683              
684             =cut
685              
686             sub ithreads
687             {
688 55375     55375 1 65116 my $self = shift;
689 55375         124716 return $self->{ithreads};
690             }
691              
692             =head2 roots
693              
694             %roots = $df->roots
695              
696             Returns a key/value pair list giving the names and SVs at each of the roots.
697              
698             =head2 roots_strong
699              
700             %roots = $df->roots_strong
701              
702             Returns a key/value pair list giving the names and SVs at each of the roots
703             that count as strong references.
704              
705             =head2 roots_weak
706              
707             %roots = $df->roots_weak
708              
709             Returns a key/value pair list giving the names and SVs at each of the roots
710             that count as strong references.
711              
712             =cut
713              
714             sub _roots
715             {
716 9     9   16 my $self = shift;
717             return map {
718 594         953 my ( $root_at, $desc ) = @$_;
719 594         739 $desc => $self->sv_at( $root_at )
720 9         21 } values %{ $self->{roots} };
  9         83  
721             }
722              
723             sub roots
724             {
725 1     1 1 11 my $self = shift;
726 1     66   12 return pairmap { substr( $a, 1 ) => $b } $self->_roots;
  66         125  
727             }
728              
729             sub roots_strong
730             {
731 6     6 1 30 my $self = shift;
732 6 100   396   53 return pairmap { $a =~ m/^\+(.*)/ ? ( $1 => $b ) : () } $self->_roots;
  396         1405  
733             }
734              
735             sub roots_weak
736             {
737 2     2 1 6 my $self = shift;
738 2 100   132   15 return pairmap { $a =~ m/^\-(.*)/ ? ( $1 => $b ) : () } $self->_roots;
  132         208  
739             }
740              
741             =head2 ROOTS
742              
743             $sv = $df->ROOT
744              
745             For each of the root names given below, a method exists with that name which
746             returns the SV at that root:
747              
748             main_cv
749             defstash
750             mainstack
751             beginav
752             checkav
753             unitcheckav
754             initav
755             endav
756             strtabhv
757             envgv
758             incgv
759             statgv
760             statname
761             tmpsv
762             defgv
763             argvgv
764             argvoutgv
765             argvout_stack
766             fdpidav
767             preambleav
768             modglobalhv
769             regex_padav
770             sortstash
771             firstgv
772             secondgv
773             debstash
774             stashcache
775             isarev
776             registered_mros
777              
778             =cut
779              
780             =head2 root_descriptions
781              
782             %rootdescs = $df->root_descriptions
783              
784             Returns a key/value pair list giving the (method) name and description text of
785             each of the possible roots.
786              
787             =cut
788              
789             sub root_descriptions
790             {
791 0     0 1 0 my $self = shift;
792 0         0 my $roots = $self->{roots};
793             return map {
794 0         0 $_ => substr $roots->{$_}[1], 1
  0         0  
795             } keys %$roots;
796             }
797              
798             =head2 root_at
799              
800             $addr = $df->root_at( $name )
801              
802             Returns the SV address of the given named root.
803              
804             =cut
805              
806             sub root_at
807             {
808 462     462 1 449 my $self = shift;
809 462         524 my ( $name ) = @_;
810              
811 462 50       1238 return $self->{roots}{$name} ? $self->{roots}{$name}[0] : undef;
812             }
813              
814             =head2 root
815              
816             $sv = $df->root( $name )
817              
818             Returns the given root SV.
819              
820             =cut
821              
822             sub root
823             {
824 462     462 1 455 my $self = shift;
825 462 100       553 my $root_at = $self->root_at( @_ ) or return;
826 239         336 return $self->sv_at( $root_at );
827             }
828              
829             =head2 heap
830              
831             @svs = $df->heap
832              
833             Returns all of the heap-allocated SVs, in no particular order
834              
835             =cut
836              
837             sub heap
838             {
839 6     6 1 1108 my $self = shift;
840 6         11 return values %{ $self->{heap} };
  6         120998  
841             }
842              
843             =head2 stack
844              
845             @svs = $df->stack
846              
847             Returns all the SVs on the stack
848              
849             =cut
850              
851             sub stack
852             {
853 3     3 1 9 my $self = shift;
854              
855 3         12 return map { $self->sv_at( $_ ) } @{ $self->{stack_at} };
  6         16  
  3         13  
856             }
857              
858             =head2 contexts
859              
860             @ctxs = $df->contexts
861              
862             Returns a list of L objects representing the call context
863             stack in the dumpfile.
864              
865             =cut
866              
867             sub contexts
868             {
869 1     1 1 6 my $self = shift;
870 1         13 return @{ $self->{contexts} };
  1         4  
871             }
872              
873             =head2 sv_at
874              
875             $sv = $df->sv_at( $addr )
876              
877             Returns the SV at the given address, or C if one does not exist.
878              
879             (Note that this is unambiguous, as a Perl-level C is represented by the
880             immortal C SV).
881              
882             =cut
883              
884             sub sv_at
885             {
886 13242552     13242552 1 14078112 my $self = shift;
887 13242552         14978955 my ( $addr ) = @_;
888 13242552 100       18012495 return undef if !$addr;
889              
890 12397921 100       16825039 return $self->{UNDEF} if $addr == $self->{undef_at};
891 12396324 100       15467535 return $self->{YES} if $addr == $self->{yes_at};
892 12395276 100       16235623 return $self->{NO} if $addr == $self->{no_at};
893              
894 12395255         32584392 return $self->{heap}{$addr};
895             }
896              
897             =head1 AUTHOR
898              
899             Paul Evans
900              
901             =cut
902              
903             0x55AA;