File Coverage

blib/lib/Devel/MAT/Dumper/Helper.pm
Criterion Covered Total %
statement 6 21 28.5
branch 0 4 0.0
condition n/a
subroutine 2 5 40.0
pod 3 3 100.0
total 11 33 33.3


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2019 -- leonerd@leonerd.org.uk
5              
6             package Devel::MAT::Dumper::Helper;
7              
8 1     1   1032 use strict;
  1         1  
  1         26  
9 1     1   5 use warnings;
  1         2  
  1         247  
10              
11             our $VERSION = '0.45';
12              
13             =head1 NAME
14              
15             C - give XS modules extensions for memory dumping
16              
17             =head1 SYNOPSIS
18              
19             In F
20              
21             if( eval { require Devel::MAT::Dumper::Helper } ) {
22             Devel::MAT::Dumper::Helper->extend_module_build( $build );
23             }
24              
25             In your module's XS source:
26              
27             #ifdef HAVE_DMD_HELPER
28             # WANT_DMD_API_044
29             # include "DMD_helper.h"
30             #endif
31              
32             ...
33              
34             #ifdef HAVE_DMD_HELPER
35             static int dumpstruct(pTHX_ const SV *sv)
36             {
37             int ret = 0;
38              
39             ret += DMD_ANNOTATE_SV(sv, another_sv,
40             "the description of this field");
41             ...
42              
43             return ret;
44             }
45              
46             static int dumpmagic(pTHX_ const SV *sv, MAGIC *mg)
47             {
48             int ret = 0;
49              
50             ret += DMD_ANNOTATE_SV(sv, another_sv,
51             "the description of this field");
52             ...
53              
54             return ret;
55             }
56             #endif
57              
58             ...
59              
60             BOOT:
61             #ifdef HAVE_DMD_HELPER
62             DMD_SET_PACKAGE_HELPER("My::Package", dumpstruct);
63             DMD_SET_MAGIC_HELPER(&vtbl, dumpmagic);
64             #endif
65              
66             =head1 DESCRIPTION
67              
68             This module provides a build-time helper to assist in writing XS modules that
69             can provide extra information to a L heap dump file when dumping
70             data structures relating to that module.
71              
72             Following the example in the L section above, the C
73             function is called whenever L finds an SV blessed into the
74             given package, and the C function is called whenever
75             L finds an SV with extension magic matching the given
76             magic virtual table pointer. These functions may then inspect the module's
77             state from the SV or MAGIC pointers, and invoke the C macro
78             to provide extra annotations into the heap dump file about how this SV is
79             related to another one.
80              
81             The C macro is required before C<#include>ing the file, so
82             as to enable the API structure described here. Without that, an earlier
83             version of the module is provided instead, which will eventually be removed in
84             some later version.
85              
86             Under this code structure, a module will cleanly build, install and run just
87             fine if L is not available at build time, so it is
88             not necessary to list that as a C or C
89             requirement.
90              
91             Additionally, the way the inserted code is structured does not cause the XS
92             module to load C itself, so there is no runtime dependency
93             either, even if the support was made available. The newly inserted code is
94             only invoked if both C and this XS module are actually
95             loaded.
96              
97             Note that this entire mechanism is currently experimental.
98              
99             =cut
100              
101             my $DMD_helper_h = do {
102             local $/;
103             readline DATA;
104             };
105              
106             =head1 FUNCTIONS
107              
108             =cut
109              
110             =head2 write_DMD_helper_h
111              
112             Devel::MAT::Dumper::Helper->write_DMD_helper_h
113              
114             Writes the L file to the current working directory. To cause the
115             compiler to actually find this file, see L.
116              
117             =cut
118              
119             sub write_DMD_helper_h
120             {
121 0     0 1   shift;
122              
123 0 0         open my $out, ">", "DMD_helper.h" or
124             die "Cannot open DMD_helper.h for writing - $!\n";
125              
126 0           $out->print( $DMD_helper_h );
127             }
128              
129             =head2 extra_compiler_flags
130              
131             @flags = Devel::MAT::Dumper::Helper->extra_compiler_flags
132              
133             Returns a list of extra flags that the build scripts should add to the
134             compiler invocation. This enables the C compiler to find the F
135             file, and also defines a symbol C which the XS code can then
136             use in C<#ifdef> guards:
137              
138             #ifdef HAVE_DMD_HELPER
139             ...
140             #endif
141              
142             =cut
143              
144             sub extra_compiler_flags
145             {
146 0     0 1   shift;
147 0           return "-DHAVE_DMD_HELPER",
148             "-I.";
149             }
150              
151             =head2 extend_module_build
152              
153             Devel::MAT::Dumper::Helper->extend_module_build( $build )
154              
155             A convenient shortcut for performing all the tasks necessary to make a
156             L-based distribution use the helper.
157              
158             =cut
159              
160             sub extend_module_build
161             {
162 0     0 1   my $self = shift;
163 0           my ( $build ) = @_;
164              
165 0 0         eval { $self->write_DMD_helper_h } or do {
  0            
166 0           warn $@;
167 0           return;
168             };
169              
170             # preserve existing flags
171 0           my @flags = @{ $build->extra_compiler_flags };
  0            
172 0           push @flags, $self->extra_compiler_flags;
173              
174 0           $build->extra_compiler_flags( @flags );
175             }
176              
177             =head1 XS MACROS
178              
179             The header file provides the following macros, which may be used by the XS
180             module.
181              
182             =head2 DMD_SET_PACKAGE_HELPER
183              
184             typedef int DMD_Helper(pTHX_ DMDContext *ctx, const SV *sv);
185              
186             DMD_SET_PACKAGE_HELPER(char *packagename, DMD_Helper *helper);
187              
188             This macro should be called from the C section of the XS module to
189             associate a helper function with a named package. Whenever an instance of an
190             object blessed into that package is encountered by the dumper, the helper
191             function will be called to provide extra information about it.
192              
193             When invoked, the helper function is passed a pointer to the blessed SV
194             directly - remember this will be the underlying object storage and not the
195             C that the Perl code uses to refer to it. It should return an integer that
196             is the sum total of the return values of all the calls to C
197             that it made, or 0 if it did not make any.
198              
199             The I pointer to the helper function points at an opaque structure
200             internal to the C module. Helper functions are not
201             expected to interact with it, except to pass it on any C
202             calls it may make.
203              
204             =head2 DMD_SET_MAGIC_HELPER
205              
206             typedef int DMD_MagicHelper(pTHX_ DMDContext *ctx, const SV *sv, MAGIC *mg);
207              
208             DMD_SET_MAGIC_HELPER(MGVTBL *vtbl, DMD_MagicHelper *helper);
209              
210             This macro should be called from the C section of the XS module to
211             associate a helper function with a given magic virtual method table. Whenever
212             an SV with that kind of magic is encountered by the dumper, the helper
213             function will be called to provide extra information about it.
214              
215             When invoked, the helper function is passed a pointer to the magical SV as
216             well as the specific C instance responsible for this call. It should
217             return an integer that is the sum total of the return values of all the calls
218             to C that it made, or 0 if it did not make any.
219              
220             The I pointer to the helper function points at an opaque structure
221             internal to the C module. Helper functions are not
222             expected to interact with it, except to pass it on any C
223             calls it may make.
224              
225             =head2 DMD_ADD_ROOT
226              
227             DMD_ADD_ROOT(SV *sv, const char *name);
228              
229             This macro should be called from the C section of the XS module to add
230             another root SV pointer to be added to the root SVs table. This is useful for
231             annotating static SV pointers or other storage that can refer to SVs or memory
232             structures within the module, but which would not be discovered by a normal
233             heap walk.
234              
235             The I argument is also used as the description string within the
236             C UI. It should begin with either a C<+> or C<-> character to
237             annotate that the root contains a strong or weak reference, respectively.
238              
239             =head2 DMD_ANNOTATE_SV
240              
241             DMD_ANNOTATE_SV(const SV *referrer, const SV *referrant, const char *label);
242              
243             This macro should be called by a helper function, in order to provide extra
244             information about the SV it has encountered. The macro notes that a pointer
245             exists from the SV given by I, pointing at the SV given by
246             I, described by the given string label.
247              
248             Each call to this macro returns an integer, which the helper function must
249             accumulate the total of, and return that number to the caller.
250              
251             Not that it is not necessary that either the referrer nor the referrant
252             actually are the SV that the helper function encountered. Arbitrary
253             annotations between SVs are permitted. Additionally, it is permitted that
254             the SV addresses do not in fact point at Perl SVs, but instead point to
255             arbitarary data structures, which should be written about using
256             C.
257              
258             =head2 DMD_DUMP_STRUCT
259              
260             typedef struct {
261             const char *name;
262             enum {
263             DMD_FIELD_PTR,
264             DMD_FIELD_BOOL,
265             DMD_FIELD_U8,
266             DMD_FIELD_U32,
267             DMD_FIELD_UINT,
268             } type;
269              
270             void *ptr; /* for type=PTR */
271             bool b; /* for type=BOOL */
272             long n; /* for the remaining numerical types */
273             } DMDNamedField;
274              
275             DMD_DUMP_STRUCT(DMDContext *ctx, const char *name, void *addr, size_t size,
276             size_t nfields, const DMDNamedField fields[]);
277              
278             This macro should be called by a helper function, in order to provide extra
279             information about a memory structure that is not a Perl SV. By using this
280             macro, the module can write information into the dumpfile about the memory
281             structure types and values that it operates on, allowing the C
282             tooling to operate on it - such as by following pointers and finding or
283             identifying the contents.
284              
285             The code invoked by this macro at runtime actually does B separate tasks,
286             which are closely related. The first time a call is made for any particular
287             string value in I, the function will write metadata information into the
288             dumpfile which gives the name and type of each of the fields. Every call,
289             including this first one, will write the values of the fields associated with
290             a single instance of the structure, by reusing the information provided to the
291             first call.
292              
293             The I argument must be the value given to the helper function. I
294             gives the pointer address of the structure itself. I should give its
295             total size in bytes (often C is sufficient here).
296              
297             The I, I, and I parameters between them are used both
298             by the initial metadata call, and for every structure instance. I gives
299             a unique name to this type of structure - it should be composed of the base
300             name of the XS module, and a local name within the module, separated by C.
301             I gives the number of individual field instances given in the
302             I array, which itself provides a label name, a type, and an actual
303             value.
304              
305             The first two fields of the C structure give its name and type,
306             and one subsequent field should be set to give the value for it. Which field
307             to use depends on the type.
308              
309             Note that it is very important, once a structure name has been seen the first
310             time, that every subsequent call for the same must have exactly the same count
311             of fields, and the types of each of them. The values of the fields, as well as
312             the size of the structure overall, are recorded for every call, but the typing
313             information is stored only once on that first call. It is best to ensure that
314             the module source contains only a single instance of this macro for a given
315             structure name, thus ensuring the type information will always be consistent.
316              
317             =head1 HANDLING C-LEVEL STRUCTURES
318              
319             For example, given a C struct definition such as:
320              
321             struct MyData {
322             SV *buf;
323             int state;
324              
325             AV *more_stuff;
326             };
327              
328             A call to provide this to the dumpfile could look like:
329              
330             struct MyData *dat = ...;
331              
332             DMD_DUMP_STRUCT(ctx, "Module::Name/MyData", dat, sizeof(struct MyData),
333             3, ((const DMDNamedField []){
334             {"the buf SV", DMD_FIELD_PTR, .ptr = dat->buf},
335             {"the state", DMD_FIELD_UINT, .n = dat->state},
336             {"the more_stuff AV", DMD_FIELD_PTR, .ptr = dat->more_stuff},
337             })
338             );
339              
340             Conventionally, names of unique fields all begin C<"the ...">. Fields that
341             point to other Perl SVs should explain what kind of SV they point to, so any
342             discrepencies can be observed in the tooling later on.
343              
344             A call to this macro alone is likely not enough to fully link the information
345             in the dumpfile, however. It is unlikely that any pointer value that the
346             dumper itself will encounter would point to this data structure - if so, Perl
347             would not know how to deal with it. It's likely that the module would use some
348             technique such as storing a pointer in the UV field of a blessed SCALAR SV, as
349             a way to retain it. In that typical example, a helper function should be
350             attached to the package name that SV would be blessed into. When the dumper
351             encounters that blessed SV it will invoke the helper function, which can then
352             call C and also use C to provide a linkage
353             between the blessed SV containing the UV value, and this structure.
354              
355             static int dumppackage_mydata(pTHX_ DMDContext *ctx, const SV *sv)
356             {
357             int ret = 0;
358              
359             struct MyData *dat = NUM2PTR(struct MyData *, SvUV((SV *)sv));
360             DMD_DUMP_STRUCT(...);
361              
362             ret += DMD_ANNOTATE_SV(sv, (SV *)dat, "the MyData structure");
363              
364             return ret;
365             }
366              
367             BOOT:
368              
369             There is no ordering requirement between these two - the annotation linking
370             the pointers can be made before, or after, the structure itself has been
371             written. In fact, there are no ordering constraints at all; feel free to write
372             the data structures and annotations in whatever order is most natural to the
373             dumper code,
374              
375             =cut
376              
377             =head1 AUTHOR
378              
379             Paul Evans
380              
381             =cut
382              
383             0x55AA;
384              
385             __DATA__