File Coverage

blib/lib/Devel/MAT/Dumpfile.pm
Criterion Covered Total %
statement 248 302 82.1
branch 78 122 63.9
condition 12 24 50.0
subroutine 46 57 80.7
pod 17 18 94.4
total 401 523 76.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, 2013-2022 -- leonerd@leonerd.org.uk
5              
6             package Devel::MAT::Dumpfile 0.50;
7              
8 9     9   1896 use v5.14;
  9         31  
9 9     9   46 use warnings;
  9         18  
  9         232  
10              
11 9     9   51 use Carp;
  9         27  
  9         511  
12 9     9   5053 use IO::Handle; # ->read
  9         54901  
  9         430  
13 9     9   4144 use IO::Seekable; # ->tell
  9         5004  
  9         512  
14              
15 9     9   60 use List::Util qw( pairmap );
  9         23  
  9         465  
16              
17 9     9   5844 use Devel::MAT::SV;
  9         34  
  9         355  
18 9     9   3777 use Devel::MAT::Context;
  9         22  
  9         306  
19              
20 9     9   59 use Struct::Dumb 0.07 qw( readonly_struct );
  9         141  
  9         53  
21             readonly_struct StructType => [qw( name fields )];
22             readonly_struct StructField => [qw( name type )];
23              
24             use constant {
25 9         3215 PMAT_SVxMAGIC => 0x80,
26 9     9   774 };
  9         28  
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   85 my $self = shift;
114 42 50       249 $self->{roots}{$name} ? $self->sv_at( $self->{roots}{$name}[0] ) : undef;
115             };
116 9     9   68 no strict 'refs';
  9         38  
  9         36646  
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 24 my $class = shift;
150 7         55 my ( $path, %args ) = @_;
151              
152 7         32 my $progress = $args{progress};
153              
154 7 50       68 $progress->( "Loading file $path..." ) if $progress;
155              
156 7 50       639 open my $fh, "<", $path or croak "Cannot read $path - $!";
157 7         74 my $self = bless { fh => $fh }, $class;
158              
159 7         94 my $filelen = -s $fh;
160              
161             # Header
162 7 50       54 $self->_read(4) eq "PMAT" or croak "File magic signature not found";
163              
164 7         40 my $flags = $self->_read_u8;
165              
166 7 50       64 my $endian = ( $self->{big_endian} = $flags & 0x01 ) ? ">" : "<";
167              
168 7         44 my $u32_fmt = $self->{u32_fmt} = "L$endian";
169 7         27 my $u64_fmt = $self->{u64_fmt} = "Q$endian";
170              
171 7 50       37 @{$self}{qw( uint_len uint_fmt )} =
  7         38  
172             ( $flags & 0x02 ) ? ( 8, $u64_fmt ) : ( 4, $u32_fmt );
173              
174 7 50       34 @{$self}{qw( ptr_len ptr_fmt )} =
  7         29  
175             ( $flags & 0x04 ) ? ( 8, $u64_fmt ) : ( 4, $u32_fmt );
176              
177 7 50       39 @{$self}{qw( nv_len nv_fmt )} =
  7         21  
178             ( $flags & 0x08 ) ? ( 10, "D$endian" ) : ( 8, "d$endian" );
179              
180 7         35 $self->{ithreads} = !!( $flags & 0x10 );
181              
182 7         17 $flags &= ~0x1f;
183 7 50       43 die sprintf "Cannot read %s - unrecognised flags %x\n", $path, $flags if $flags;
184              
185 7         73 $self->{minus_1} = unpack $self->{uint_fmt}, pack $self->{uint_fmt}, -1;
186              
187 7 50       27 $self->_read_u8 == 0 or die "Cannot read $path - 'zero' header field is not zero";
188              
189 7 50       32 $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       29 ( $self->{format_minor} = $self->_read_u8 ) <= 5 or
193             die "Cannot read $path - format version minor unrecognised ($self->{format_minor})";
194 7 50       30 warnings::warnif experimental => "Support for PMAT file format v0.5 is experimental" if $self->{format_minor} == 5;
195              
196 7 50       30 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         33 $self->{perlver} = $self->_read_u32;
201              
202 7         26 my $n_types = $self->_read_u8;
203 7         45 my @sv_sizes = unpack "(a3)*", my $tmp = $self->_read( $n_types * 3 );
204 7         166 $self->{sv_sizes} = [ map [ unpack "C C C", $_ ], @sv_sizes ];
205              
206 7 50       37 if( $self->{format_minor} >= 4 ) {
207 7         26 my $n_extns = $self->_read_u8;
208 7         32 my @extn_sizes = unpack "(a3)*", my $tmp = $self->_read( $n_extns * 3 );
209 7         112 $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       37 if( $self->{format_minor} >= 2 ) {
219 7         29 my $n_ctxs = $self->_read_u8;
220 7         35 my @ctx_sizes = unpack "(a3)*", my $tmp = $self->_read( $n_ctxs * 3 );
221 7         67 $self->{ctx_sizes} = [ map [ unpack "C C C", $_ ], @ctx_sizes ];
222             }
223              
224 7         28 $self->{structtypes_by_id} = {};
225              
226             # Roots
227 7         35 foreach (qw( undef yes no )) {
228 21         72 my $addr = $self->{"${_}_at"} = $self->_read_ptr;
229 21         61 my $class = "Devel::MAT::SV::\U$_";
230 21         239 $self->{uc $_} = $class->new( $self, $addr );
231             }
232              
233 7         28 $self->{roots} = \my %roots;
234             # The three immortals
235 7         174 $roots{"sv_$_"} = [ $self->{"\U$_"}->addr, $ROOTDESC{"sv_$_"} ] for qw( undef yes no );
236              
237 7         69 foreach ( 1 .. $self->_read_u32 ) {
238 441         754 my $name = $self->_read_str;
239 441   66     1502 my $desc = $ROOTDESC{$name} // $name;
240 441 100       1783 $desc =~ m/^[+-]/ or $desc = "+$desc";
241 441         752 $roots{$name} = [ $self->_read_ptr, $desc ];
242             }
243              
244             # Stack
245 7         23 my $stacksize = $self->_read_uint;
246 7         33 $self->{stack_at} = [ map { $self->_read_ptr } 1 .. $stacksize ];
  15         57  
247              
248             # Heap
249 7         36 $self->{heap} = \my %heap;
250 7         24 $self->{protosubs_by_oproot} = \my %protosubs_by_oproot;
251 7         51 while( my $sv = $self->_read_sv ) {
252 573408         2942152 $heap{$sv->addr} = $sv;
253              
254             # Also identify the protosub of every oproot
255 573408 100 100     1449206 if( $sv->type eq "CODE" and $sv->oproot and $sv->is_clone ) {
      100        
256 951         5587 $protosubs_by_oproot{$sv->oproot} = $sv;
257             }
258              
259 573408         1342754 my $pos = $fh->IO::Seekable::tell; # fully-qualified method for 5.010
260 573408 50 33     3351401 $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         61 $self->{contexts} = \my @contexts;
266 7         66 while( my $ctx = $self->_read_ctx ) {
267 5         13 push @contexts, $ctx;
268             }
269              
270             # From here onwards newer files have mortals, older ones don't
271 7 50       49 if( my $mortalcount = $self->_read_uint ) {
272 0         0 $self->{mortals_at} = \my @mortals_at;
273 0         0 push @mortals_at, $self->_read_ptr for 1 .. $mortalcount;
274 0         0 $self->sv_at( $_ )->_set_is_mortal for @mortals_at;
275 0         0 $self->{mortal_floor} = $self->_read_uint;
276             }
277              
278 7 50       114 $self->_fixup( %args ) unless $args{no_fixup};
279              
280 7         162 return $self;
281             }
282              
283             sub structtype
284             {
285 0     0 0 0 my $self = shift;
286 0         0 my ( $id ) = @_;
287              
288 0   0     0 return $self->{structtypes_by_id}{$id} //
289             croak "Dumpfile does not define a struct type of ID=$id\n";
290             }
291              
292             sub _fixup
293             {
294 7     7   22 my $self = shift;
295 7         31 my %args = @_;
296              
297 7         19 my $progress = $args{progress};
298              
299 7         28 my $heap = $self->{heap};
300              
301 7         24 my $heap_total = scalar keys %$heap;
302              
303             # Annotate each root SV
304 7         17 foreach my $name ( keys %{ $self->{roots} } ) {
  7         157  
305 462 100       723 my $sv = $self->root( $name ) or next;
306 239         581 $sv->{rootname} = $name;
307             }
308              
309 7         36 my $count = 0;
310 7         53 while( my ( $addr ) = each %$heap ) {
311 573408 50       1516823 my $sv = $heap->{$addr} or next;
312              
313             # While dumping we weren't able to determine what ARRAYs were really
314             # PADLISTs. Now we can fix them up
315 573408 100       1333998 $sv->_fixup if $sv->can( "_fixup" );
316              
317 573408         708405 $count++;
318 573408 50 33     1927509 $progress->( sprintf "Fixing %d of %d (%.2f%%)",
319             $count, $heap_total, 100*$count / $heap_total ) if $progress and ($count % 20000) == 0;
320             }
321              
322             # Walk the SUB contexts setting their true depth
323 7 50       62 if( $self->{format_minor} >= 2 ) {
324 7         25 my %prev_depth_by_cvaddr;
325              
326 7         29 foreach my $ctx ( @{ $self->{contexts} } ) {
  7         49  
327 5 100       24 next unless $ctx->type eq "SUB";
328              
329 3         10 my $cvaddr = $ctx->{cv_at};
330 3   33     15 $ctx->_set_depth( $prev_depth_by_cvaddr{$cvaddr} // $ctx->cv->depth );
331              
332 3         9 $prev_depth_by_cvaddr{$cvaddr} = $ctx->olddepth;
333             }
334             }
335              
336 7         42 return $self;
337             }
338              
339             # Nicer interface to IO::Handle
340             sub _read
341             {
342 1569207     1569207   2056714 my $self = shift;
343 1569207         2181179 my ( $len ) = @_;
344 1569207 100       2723481 return "" if $len == 0;
345 1564796 50       2974637 defined( $self->{fh}->read( my $buf, $len ) ) or croak "Cannot read - $!";
346 1564796         11054072 return $buf;
347             }
348              
349             sub _read_u8
350             {
351 1107769     1107769   2614040 my $self = shift;
352 1107769 50       2184174 defined( $self->{fh}->read( my $buf, 1 ) ) or croak "Cannot read - $!";
353 1107769         6961999 return unpack "C", $buf;
354             }
355              
356             sub _read_u32
357             {
358 14     14   29 my $self = shift;
359 14 50       43 defined( $self->{fh}->read( my $buf, 4 ) ) or croak "Cannot read - $!";
360 14         155 return unpack $self->{u32_fmt}, $buf;
361             }
362              
363             sub _read_u64
364             {
365 0     0   0 my $self = shift;
366 0 0       0 defined( $self->{fh}->read( my $buf, 8 ) ) or croak "Cannot read - $!";
367 0         0 return unpack $self->{u64_fmt}, $buf;
368             }
369              
370             sub _read_uint
371             {
372 988031     988031   1268906 my $self = shift;
373 988031 50       1870445 defined( $self->{fh}->read( my $buf, $self->{uint_len} ) ) or croak "Cannot read - $!";
374 988031         5691048 return unpack $self->{uint_fmt}, $buf;
375             }
376              
377             sub _read_ptr
378             {
379 592887     592887   805796 my $self = shift;
380 592887 50       1107011 defined( $self->{fh}->read( my $buf, $self->{ptr_len} ) ) or croak "Cannot read - $!";
381 592887         4044059 return unpack $self->{ptr_fmt}, $buf;
382             }
383              
384             sub _read_ptrs
385             {
386 997953     997953   1461902 my $self = shift;
387 997953         1473724 my ( $n ) = @_;
388 997953 50       2060271 defined( $self->{fh}->read( my $buf, $self->{ptr_len} * $n ) ) or croak "Cannot read - $!";
389 997953         8176471 return unpack "$self->{ptr_fmt}$n", $buf;
390             }
391              
392             sub _read_nv
393             {
394 0     0   0 my $self = shift;
395 0 0       0 defined( $self->{fh}->read( my $buf, $self->{nv_len} ) ) or croak "Cannot read - $!";
396 0         0 return unpack $self->{nv_fmt}, $buf;
397             }
398              
399             sub _read_str
400             {
401 757424     757424   1030517 my $self = shift;
402 757424         1164904 my $len = $self->_read_uint;
403 757424 100       1837097 return undef if $len == $self->{minus_1};
404 545755         955942 return $self->_read($len);
405             }
406              
407             sub _read_bytesptrsstrs
408             {
409 1188697     1188697   1607084 my $self = shift;
410 1188697         1803726 my ( $nbytes, $nptrs, $nstrs ) = @_;
411              
412             return
413             ( $nbytes ? $self->_read( $nbytes ) : "" ),
414             ( $nptrs ? [ $self->_read_ptrs( $nptrs ) ] : undef ),
415 1188697 100       2582795 ( $nstrs ? [ map { $self->_read_str } 1 .. $nstrs ] : undef );
  414410 100       779788  
    100          
416             }
417              
418             sub _read_sv
419             {
420 573415     573415   838493 my $self = shift;
421              
422 573415         746897 while(1) {
423 615286         997381 my $type = $self->_read_u8;
424 615286 100       1148909 return if !$type;
425              
426 615279 50       1595842 if( $type >= 0xF1 ) {
    50          
    100          
427 0         0 die sprintf "Unrecognised META tag %02X\n", $type;
428             }
429             elsif( $type == 0xF0 ) {
430             # META_STRUCT
431 0         0 my $id = $self->_read_uint;
432 0         0 my $nfields = $self->_read_uint;
433 0         0 my $name = $self->_read_str;
434              
435 0         0 my @fields;
436             push @fields, StructField(
437             $self->_read_str,
438             $self->_read_u8,
439 0         0 ) for 1 .. $nfields;
440              
441 0         0 $self->{structtypes_by_id}{$id} = StructType(
442             $name, \@fields,
443             );
444              
445 0         0 next;
446             }
447             elsif( $type >= 0x80 ) {
448 41871         82123 my $sizes = $self->{svx_sizes}[$type - 0x80];
449              
450 41871 50 33     122415 if( $self->{format_minor} == 0 and $type == PMAT_SVxMAGIC ) {
    50          
451             # legacy magic support
452 0         0 my ( $sv_addr, $obj ) = $self->_read_ptrs(2);
453 0         0 my $type = chr $self->_read_u8;
454              
455 0         0 my $sv = $self->sv_at( $sv_addr );
456              
457             # Legacy format didn't have flags, and didn't distinguish obj from ptr
458             # However, the only objs it ever saved were refcounted ones. Lets just
459             # pretend all of them are refcounted objects.
460 0         0 $sv->more_magic( $type => 0x01, $obj, 0, 0 );
461             }
462             elsif( !$sizes ) {
463 0         0 die sprintf "Unrecognised SV extension type %02x\n", $type;
464             }
465             else {
466 41871         80307 my $sv_addr = $self->_read_ptr;
467 41871         88032 my $sv = $self->sv_at( $sv_addr );
468              
469 41871 50       224522 if( my $code = $self->can( sprintf "_read_svx_%02X", $type ) ) {
470 41871         93230 $self->$code( $sv, $self->_read_bytesptrsstrs( @$sizes ) );
471             }
472             else {
473 0         0 warn sprintf "Skipping unrecognised SVx 0x%02X\n", $type;
474 0         0 $self->_read_bytesptrsstrs( @$sizes ); # ignore
475             }
476             }
477              
478 41871         102079 next;
479             }
480              
481             # First read the "common" header
482             my $sv = Devel::MAT::SV->new( $type, $self,
483 573408         828229 $self->_read_bytesptrsstrs( @{ $self->{sv_sizes}[0] } )
  573408         1232607  
484             );
485              
486 573408 50       1246578 if( $type == 0x7F ) {
487 0         0 my $structtype = $self->structtype( $sv->structid );
488 0         0 $sv->load( $structtype->fields );
489             }
490             else {
491 573408         739666 my ( $bytes, $nptrs, $nstrs ) = @{ $self->{sv_sizes}[$type] };
  573408         1148020  
492 573408         1068641 $sv->load(
493             $self->_read_bytesptrsstrs( $bytes, $nptrs, $nstrs )
494             );
495             }
496              
497 573408         1741534 return $sv;
498             }
499             }
500              
501             sub _read_svx_80
502             {
503 41864     41864   69861 my $self = shift;
504 41864         76423 my ( $sv, $bytes, $ptrs, $strs ) = @_;
505              
506 41864         116293 my ( $type, $flags ) = unpack "A1 C", $bytes;
507              
508 41864         124324 $sv->more_magic( $type => $flags, @$ptrs );
509             }
510              
511             sub _read_svx_81
512             {
513 2     2   11 my $self = shift;
514 2         18 my ( $sv, $bytes, $ptrs, $strs ) = @_;
515              
516 2         14 $sv->_more_saved( SCALAR => $ptrs->[0] );
517             }
518              
519             sub _read_svx_82
520             {
521 1     1   9 my $self = shift;
522 1         6 my ( $sv, $bytes, $ptrs, $strs ) = @_;
523              
524 1         5 $sv->_more_saved( ARRAY => $ptrs->[0] );
525             }
526              
527             sub _read_svx_83
528             {
529 1     1   9 my $self = shift;
530 1         5 my ( $sv, $bytes, $ptrs, $strs ) = @_;
531              
532 1         5 $sv->_more_saved( HASH => $ptrs->[0] );
533             }
534              
535             sub _read_svx_84
536             {
537 1     1   5 my $self = shift;
538 1         5 my ( $av, $bytes, $ptrs, $strs ) = @_;
539              
540 1         3 my $index = unpack $self->{uint_fmt}, $bytes;
541              
542 1 50       15 $av->isa( "Devel::MAT::SV::ARRAY" ) and
543             $av->_more_saved( $index, $ptrs->[0] );
544             }
545              
546             sub _read_svx_85
547             {
548 1     1   11 my $self = shift;
549 1         8 my ( $hv, $bytes, $ptrs, $strs ) = @_;
550              
551 1 50       32 $hv->isa( "Devel::MAT::SV::HASH" ) and
552             $hv->_more_saved( $ptrs->[0], $ptrs->[1] );
553             }
554              
555             sub _read_svx_86
556             {
557 1     1   6 my $self = shift;
558 1         5 my ( $sv, $bytes, $ptrs, $strs ) = @_;
559              
560 1         7 $sv->_more_saved( CODE => $ptrs->[0] );
561             }
562              
563             sub _read_svx_87
564             {
565 0     0   0 my $self = shift;
566 0         0 my ( $sv, $bytes, $ptrs, $strs ) = @_;
567              
568 0         0 $sv->_more_annotations( $ptrs->[0], $strs->[0] );
569             }
570              
571             sub _read_svx_88
572             {
573 0     0   0 my $self = shift;
574 0         0 my ( $sv, $bytes, $ptrs, $strs ) = @_;
575              
576 0         0 my ( $serial, $line ) = unpack "($self->{uint_fmt})2", $bytes;
577 0         0 my $file = $strs->[0];
578              
579 0         0 $sv->_debugdata( $serial, $line, $file );
580             }
581              
582             sub _read_ctx
583             {
584 12     12   31 my $self = shift;
585              
586 12         48 my $type = $self->_read_u8;
587 12 100       69 return if !$type;
588              
589 5 50       15 if( $self->{format_minor} >= 2 ) {
590             my $ctx = Devel::MAT::Context->new( $type, $self,
591 5         6 $self->_read_bytesptrsstrs( @{ $self->{ctx_sizes}[0] } )
  5         18  
592             );
593              
594             $ctx->load(
595 5         12 $self->_read_bytesptrsstrs( @{ $self->{ctx_sizes}[$type] } )
  5         14  
596             );
597              
598 5         18 return $ctx;
599             }
600             else {
601 0         0 return Devel::MAT::Context->load_v0_1( $type, $self );
602             }
603             }
604              
605             =head1 METHODS
606              
607             =cut
608              
609             =head2 perlversion
610              
611             $version = $df->perlversion
612              
613             Returns the version of perl that the heap dump file was created by, as a
614             string in the form C<5.14.2>.
615              
616             =cut
617              
618             sub perlversion
619             {
620 3     3 1 6 my $self = shift;
621 3         7 my $v = $self->{perlver};
622 3         23 return join ".", $v>>24, ($v>>16) & 0xff, $v&0xffff;
623             }
624              
625             =head2 endian
626              
627             $endian = $df->endian
628              
629             Returns the endian direction of the perl that the heap dump was created by, as
630             either C or C.
631              
632             =cut
633              
634             sub endian
635             {
636 0     0 1 0 my $self = shift;
637 0 0       0 return $self->{big_endian} ? "big" : "little";
638             }
639              
640             =head2 uint_len
641              
642             $len = $df->uint_len
643              
644             Returns the length in bytes of a uint field of the perl that the heap dump was
645             created by.
646              
647             =cut
648              
649             sub uint_len
650             {
651 0     0 1 0 my $self = shift;
652 0         0 return $self->{uint_len};
653             }
654              
655             =head2 ptr_len
656              
657             $len = $df->ptr_len
658              
659             Returns the length in bytes of a pointer field of the perl that the heap dump
660             was created by.
661              
662             =cut
663              
664             sub ptr_len
665             {
666 0     0 1 0 my $self = shift;
667 0         0 return $self->{ptr_len};
668             }
669              
670             =head2 nv_len
671              
672             $len = $df->nv_len
673              
674             Returns the length in bytes of a double field of the perl that the heap dump
675             was created by.
676              
677             =cut
678              
679             sub nv_len
680             {
681 0     0 1 0 my $self = shift;
682 0         0 return $self->{nv_len};
683             }
684              
685             =head2 ithreads
686              
687             $ithreads = $df->ithreads
688              
689             Returns a boolean indicating whether ithread support was enabled in the perl
690             that the heap dump was created by.
691              
692             =cut
693              
694             sub ithreads
695             {
696 59192     59192 1 85036 my $self = shift;
697 59192         156712 return $self->{ithreads};
698             }
699              
700             =head2 roots
701              
702             %roots = $df->roots
703              
704             Returns a key/value pair list giving the names and SVs at each of the roots.
705              
706             =head2 roots_strong
707              
708             %roots = $df->roots_strong
709              
710             Returns a key/value pair list giving the names and SVs at each of the roots
711             that count as strong references.
712              
713             =head2 roots_weak
714              
715             %roots = $df->roots_weak
716              
717             Returns a key/value pair list giving the names and SVs at each of the roots
718             that count as strong references.
719              
720             =cut
721              
722             sub _roots
723             {
724 9     9   23 my $self = shift;
725             return map {
726 594         1318 my ( $root_at, $desc ) = @$_;
727 594         917 $desc => $self->sv_at( $root_at )
728 9         21 } values %{ $self->{roots} };
  9         108  
729             }
730              
731             sub roots
732             {
733 1     1 1 3 my $self = shift;
734 1     66   14 return pairmap { substr( $a, 1 ) => $b } $self->_roots;
  66         145  
735             }
736              
737             sub roots_strong
738             {
739 6     6 1 26 my $self = shift;
740 6 100   396   61 return pairmap { $a =~ m/^\+(.*)/ ? ( $1 => $b ) : () } $self->_roots;
  396         1574  
741             }
742              
743             sub roots_weak
744             {
745 2     2 1 20 my $self = shift;
746 2 100   132   14 return pairmap { $a =~ m/^\-(.*)/ ? ( $1 => $b ) : () } $self->_roots;
  132         254  
747             }
748              
749             =head2 ROOTS
750              
751             $sv = $df->ROOT
752              
753             For each of the root names given below, a method exists with that name which
754             returns the SV at that root:
755              
756             main_cv
757             defstash
758             mainstack
759             beginav
760             checkav
761             unitcheckav
762             initav
763             endav
764             strtabhv
765             envgv
766             incgv
767             statgv
768             statname
769             tmpsv
770             defgv
771             argvgv
772             argvoutgv
773             argvout_stack
774             fdpidav
775             preambleav
776             modglobalhv
777             regex_padav
778             sortstash
779             firstgv
780             secondgv
781             debstash
782             stashcache
783             isarev
784             registered_mros
785              
786             =cut
787              
788             =head2 root_descriptions
789              
790             %rootdescs = $df->root_descriptions
791              
792             Returns a key/value pair list giving the (method) name and description text of
793             each of the possible roots.
794              
795             =cut
796              
797             sub root_descriptions
798             {
799 0     0 1 0 my $self = shift;
800 0         0 my $roots = $self->{roots};
801             return map {
802 0         0 $_ => substr $roots->{$_}[1], 1
  0         0  
803             } keys %$roots;
804             }
805              
806             =head2 root_at
807              
808             $addr = $df->root_at( $name )
809              
810             Returns the SV address of the given named root.
811              
812             =cut
813              
814             sub root_at
815             {
816 462     462 1 566 my $self = shift;
817 462         643 my ( $name ) = @_;
818              
819 462 50       1653 return $self->{roots}{$name} ? $self->{roots}{$name}[0] : undef;
820             }
821              
822             =head2 root
823              
824             $sv = $df->root( $name )
825              
826             Returns the given root SV.
827              
828             =cut
829              
830             sub root
831             {
832 462     462 1 619 my $self = shift;
833 462 100       695 my $root_at = $self->root_at( @_ ) or return;
834 239         412 return $self->sv_at( $root_at );
835             }
836              
837             =head2 heap
838              
839             @svs = $df->heap
840              
841             Returns all of the heap-allocated SVs, in no particular order
842              
843             =cut
844              
845             sub heap
846             {
847 6     6 1 2049 my $self = shift;
848 6         15 return values %{ $self->{heap} };
  6         133723  
849             }
850              
851             =head2 stack
852              
853             @svs = $df->stack
854              
855             Returns all the SVs on the stack
856              
857             =cut
858              
859             sub stack
860             {
861 3     3 1 10 my $self = shift;
862              
863 3         9 return map { $self->sv_at( $_ ) } @{ $self->{stack_at} };
  6         16  
  3         14  
864             }
865              
866             =head2 contexts
867              
868             @ctxs = $df->contexts
869              
870             Returns a list of L objects representing the call context
871             stack in the dumpfile.
872              
873             =cut
874              
875             sub contexts
876             {
877 1     1 1 6 my $self = shift;
878 1         2 return @{ $self->{contexts} };
  1         4  
879             }
880              
881             =head2 sv_at
882              
883             $sv = $df->sv_at( $addr )
884              
885             Returns the SV at the given address, or C if one does not exist.
886              
887             (Note that this is unambiguous, as a Perl-level C is represented by the
888             immortal C SV).
889              
890             =cut
891              
892             sub sv_at
893             {
894 13396934     13396934 1 18038805 my $self = shift;
895 13396934         19196596 my ( $addr ) = @_;
896 13396934 100       23357785 return undef if !$addr;
897              
898 12502221 100       21289350 return $self->{UNDEF} if $addr == $self->{undef_at};
899 12500514 100       19802054 return $self->{YES} if $addr == $self->{yes_at};
900 12499418 100       19993558 return $self->{NO} if $addr == $self->{no_at};
901              
902 12499397         48163509 return $self->{heap}{$addr};
903             }
904              
905             =head1 AUTHOR
906              
907             Paul Evans
908              
909             =cut
910              
911             0x55AA;