File Coverage

blib/lib/Devel/MAT/Dumpfile.pm
Criterion Covered Total %
statement 257 307 83.7
branch 80 124 64.5
condition 12 24 50.0
subroutine 46 57 80.7
pod 17 18 94.4
total 412 530 77.7


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