File Coverage

blib/lib/Devel/MAT/Dumper.pm
Criterion Covered Total %
statement 22 72 30.5
branch 2 34 5.8
condition 0 3 0.0
subroutine 8 12 66.6
pod n/a
total 32 121 26.4


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-2024 -- leonerd@leonerd.org.uk
5              
6             package Devel::MAT::Dumper;
7              
8 2     2   476183 use v5.10;
  2         9  
9 2     2   14 use strict;
  2         4  
  2         67  
10 2     2   10 use warnings;
  2         22  
  2         221  
11              
12             our $VERSION = '0.51';
13              
14 2     2   27 use File::Basename qw( basename );
  2         6  
  2         256  
15 2     2   16 use File::Spec;
  2         4  
  2         51  
16 2     2   1259 use POSIX;
  2         19734  
  2         14  
17              
18             require XSLoader;
19             XSLoader::load( __PACKAGE__, $VERSION );
20              
21             =head1 NAME
22              
23             C - write a heap dump file for later analysis
24              
25             =head1 SYNOPSIS
26              
27             =for highlighter language=perl
28              
29             use Devel::MAT::Dumper;
30              
31             Devel::MAT::Dumper::dump( "path/to/the/file.pmat" );
32              
33             =head1 DESCRIPTION
34              
35             This module provides the memory-dumping function that creates a heap dump file
36             which can later be read by L. It provides a single
37             function which is not exported, which writes a file to the given path.
38              
39             The dump file will contain a representation of every SV in Perl's arena,
40             providing information about pointers between them, as well as other
41             information about the state of the process at the time it was created. It
42             contains a snapshot of the process at that moment in time, which can later be
43             loaded and analysed by various tools using C.
44              
45             This module used to be part of the main L distribution but is now
46             in its own one so that it can be installed independently on servers or other
47             locations where perl processes need to inspected but analysis tools can be run
48             elsewhere.
49              
50             =cut
51              
52             =head1 IMPORT OPTIONS
53              
54             =for highlighter
55              
56             The following C options control the behaviour of the module. They may
57             primarily be useful when used in the C<-M> perl option:
58              
59             =head2 -dump_at_DIE
60              
61             Installs a handler for the special C<__DIE__> signal to write a dump file when
62             C is about to cause a fatal signal. This is more reliable at catching
63             the callstack and memory state than using an C block.
64              
65             $ perl -MDevel::MAT::Dumper=-dump_at_DIE ...
66              
67             =head2 -dump_at_WARN
68              
69             Installs a handler for the special C<__WARN__> signal to write a dump file
70             when perl prints a warning.
71              
72             $ perl -MDevel::MAT::Dumper=-dump_at_WARN ...
73              
74             It is likely useful to combine this with the C numbering feature of the
75             C<-file> argument, to ensure that later warnings don't overwrite a particular
76             file.
77              
78             =head2 -dump_at_END
79              
80             Installs an C block which writes a dump file at C time, just before
81             the interpreter exits.
82              
83             $ perl -MDevel::MAT::Dumper=-dump_at_END ...
84              
85             =head2 -dump_at_SIGQUIT
86              
87             Installs a handler for C to write a dump file if the signal is
88             received. The signal handler will remain in place and can be used several
89             times.
90              
91             $ perl -MDevel::MAT::Dumper=-dump_at_SIGQUIT ...
92              
93             Take care if you are using the C<< >> key combination on a terminal
94             to send this signal to a foreground process, because if it has Ced any
95             background workers or similar, the signal will also be delivered to those as
96             well.
97              
98             =head2 -dump_at_SIGI
99              
100             Installs a handler for the named signal (e.g. C, C) to write
101             a dump file if the signal is received. After dumping the file, the signal
102             handler is removed and the signal re-raised.
103              
104             $ perl -MDevel::MAT::Dumper=-dump_at_SIGABRT ...
105              
106             Note that C uses an "unsafe" signal handler (i.e. not deferred until
107             the next perl op), so it can capture the full context of any ongoing XS or C
108             library operations.
109              
110             =head2 -file $PATH
111              
112             Sets the name of the file which is automatically dumped; defaults to basename
113             F<$0.pmat> if not supplied.
114              
115             $ perl -MDevel::MAT::Dumper=-file,foo.pmat ...
116              
117             In the special case that C<$0> is exactly the string C<-e> or C<-E>, the
118             filename will be prefixed with C so as not to create files whose names
119             begin with a leading hyphen, as this confuses some commandline parsers.
120              
121             $ perl -MDevel::MAT::Dumper=-dump_at_END -E 'say "hello"'
122             hello
123             Dumping to perl-e.pmat because of END
124              
125             If the pattern contains C, this will be replaced by a unique serial
126             number per written file, starting from 0. This may be helpful in the case of
127             C, C or C handlers, which could be invoked multiple times.
128              
129             The file name is converted to an absolute path immediately, so if the running
130             program later calls C, it will still be generated in the directory
131             the program started from rather than the one it happens to be in at the time.
132              
133             =head2 -max_string
134              
135             Sets the maximum length of string buffer to dump from PVs; defaults to 256 if
136             not supplied. Use a negative size to dump the entire buffer of every PV
137             regardless of size.
138              
139             =head2 -eager_open
140              
141             Opens the dump file immediately at C time, instead of waiting until
142             the time it actually writes the heap dump. This may be useful if the process
143             changes user ID, or to debug problems involving too many open filehandles.
144              
145             =cut
146              
147             our $MAX_STRING = 256; # used by XS code
148              
149             my $basename = basename( $0 );
150             $basename = "perl$basename" if $basename =~ m/^-e$/i; # RT119164
151             my $dumpfile_name = File::Spec->rel2abs( "$basename.pmat" );
152              
153             my $dumpfh;
154             my $next_serial = 0;
155              
156             my $dump_at_END;
157             END {
158 2 50   2   460894 return unless $dump_at_END;
159              
160 0 0       0 if( $dumpfh ) {
161 0         0 Devel::MAT::Dumper::dumpfh( $dumpfh );
162             }
163             else {
164 0         0 ( my $file = $dumpfile_name ) =~ s/NNN/$next_serial++/e;
  0         0  
165 0         0 print STDERR "Dumping to $file because of END\n";
166 0         0 Devel::MAT::Dumper::dump( $file );
167             }
168             }
169              
170             sub import
171             {
172 2     2   21 my $pkg = shift;
173              
174 2         2 my $eager_open;
175              
176 2         11 while( @_ ) {
177 0         0 my $sym = shift;
178              
179 0 0       0 if( $sym eq "-dump_at_DIE" ) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
180 0         0 my $old_DIE = $SIG{__DIE__};
181             $SIG{__DIE__} = sub {
182 0 0   0   0 local $SIG{__DIE__} = $old_DIE if defined $old_DIE;
183 0 0 0     0 return if $^S or !defined $^S; # only catch real process-fatal errors
184              
185 0         0 ( my $file = $dumpfile_name ) =~ s/NNN/$next_serial++/e;
  0         0  
186 0         0 print STDERR "Dumping to $file because of DIE\n";
187 0         0 Devel::MAT::Dumper::dump( $file );
188 0         0 die @_;
189 0         0 };
190             }
191             elsif( $sym eq "-dump_at_WARN" ) {
192 0         0 my $old_WARN = $SIG{__WARN__};
193             $SIG{__WARN__} = sub {
194 0 0   0   0 local $SIG{__WARN__} = $old_WARN if defined $old_WARN;
195              
196 0         0 ( my $file = $dumpfile_name ) =~ s/NNN/$next_serial++/e;
  0         0  
197 0         0 print STDERR "Dumping to $file because of WARN\n";
198 0         0 Devel::MAT::Dumper::dump( $file );
199 0         0 warn @_;
200 0         0 };
201             }
202             elsif( $sym eq "-dump_at_END" ) {
203 0         0 $dump_at_END++;
204             }
205             elsif( $sym eq "-dump_at_SIGQUIT" ) {
206             $SIG{QUIT} = sub {
207 0     0   0 ( my $file = $dumpfile_name ) =~ s/NNN/$next_serial++/e;
  0         0  
208 0         0 print STDERR "Dumping to $file because of SIGQUIT\n";
209 0         0 Devel::MAT::Dumper::dump( $file );
210 0         0 };
211             }
212             elsif( $sym =~ m/^-dump_at_SIG(\S+)$/ ) {
213 0         0 my $signal = $1;
214 0 0       0 exists $SIG{$signal} or die "Unrecognised signal name SIG$signal\n";
215              
216             my $handler = sub {
217 0     0   0 ( my $file = $dumpfile_name ) =~ s/NNN/$next_serial++/e;
  0         0  
218 0         0 print STDERR "Dumping to $file because of SIG$signal\n";
219 0         0 Devel::MAT::Dumper::dump( $file );
220 0         0 undef $SIG{$signal};
221 0         0 kill $signal => $$;
222 0         0 };
223              
224 0 0       0 if( $signal eq "ABRT" ) {
225             # Install SIGABRT handler using unsafe signal so it can see
226             # inner workings of C code properly
227 0         0 my $sigaction = POSIX::SigAction->new( $handler );
228 0         0 $sigaction->safe(0);
229              
230 0         0 POSIX::sigaction( POSIX::SIGABRT, $sigaction );
231             }
232             else {
233 0         0 $SIG{$signal} = $handler;
234             }
235             }
236             elsif( $sym eq "-file" ) {
237 0         0 $dumpfile_name = File::Spec->rel2abs( shift );
238             }
239             elsif( $sym eq "-max_string" ) {
240 0         0 $MAX_STRING = shift;
241             }
242             elsif( $sym eq "-eager_open" ) {
243 0         0 $eager_open++;
244             }
245             else {
246 0         0 die "Unrecognised $pkg import symbol $sym\n";
247             }
248             }
249              
250 2 50       2235 if( $eager_open ) {
251 0 0         open $dumpfh, ">", $dumpfile_name or
252             die "Cannot open $dumpfile_name for writing - $!\n";
253             }
254             }
255              
256             =head1 FUNCTIONS
257              
258             =for highlighter language=perl
259              
260             These functions are not exported, they must be called fully-qualified.
261              
262             =head2 dump
263              
264             dump( $path );
265              
266             Writes a heap dump to the named file
267              
268             =head2 dumpfh
269              
270             dumpfh( $fh );
271              
272             Writes a heap dump to the given filehandle (which must be a plain OS-level
273             filehandle, though does not need to be a regular file, or seekable).
274              
275             =cut
276              
277             =head1 AUTHOR
278              
279             Paul Evans
280              
281             =cut
282              
283             0x55AA;