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__ |