| 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
|
|
1018
|
use strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
25
|
|
|
9
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
254
|
|
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
our $VERSION = '0.46'; |
|
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
|
|
|
|
|
|
|
# define WANT_DMD_API_044 |
|
29
|
|
|
|
|
|
|
# include "DMD_helper.h" |
|
30
|
|
|
|
|
|
|
#endif |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
... |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
#ifdef HAVE_DMD_HELPER |
|
35
|
|
|
|
|
|
|
static int dumpstruct(pTHX_ DMDContext *ctx, 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_ DMDContext *ctx, 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__ |