File Coverage

blib/lib/Devel/MAT/Dumper.pm
Criterion Covered Total %
statement 20 70 28.5
branch 2 34 5.8
condition 0 3 0.0
subroutine 7 11 63.6
pod n/a
total 29 118 24.5


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