Working(ish) on demand demo using statis data
[p5sagit/Devel-Size.git] / Size.xs
CommitLineData
fd495cc6 1/* -*- mode: C -*- */
2
c07e8ef8 3#undef NDEBUG /* XXX */
4#include <assert.h>
5
265a0548 6#define PERL_NO_GET_CONTEXT
7
e98cedbf 8#include "EXTERN.h"
9#include "perl.h"
10#include "XSUB.h"
2eb93d08 11#include "ppport.h"
e98cedbf 12
87372f42 13/* Not yet in ppport.h */
14#ifndef CvISXSUB
15# define CvISXSUB(cv) (CvXSUB(cv) ? TRUE : FALSE)
16#endif
0e1f978f 17#ifndef SvRV_const
18# define SvRV_const(rv) SvRV(rv)
19#endif
95dc1714 20#ifndef SvOOK_offset
21# define SvOOK_offset(sv, len) STMT_START { len = SvIVX(sv); } STMT_END
22#endif
924d9c4e 23#ifndef SvIsCOW
24# define SvIsCOW(sv) ((SvFLAGS(sv) & (SVf_FAKE | SVf_READONLY)) == \
25 (SVf_FAKE | SVf_READONLY))
26#endif
27#ifndef SvIsCOW_shared_hash
28# define SvIsCOW_shared_hash(sv) (SvIsCOW(sv) && SvLEN(sv) == 0)
29#endif
30#ifndef SvSHARED_HEK_FROM_PV
31# define SvSHARED_HEK_FROM_PV(pvx) \
32 ((struct hek*)(pvx - STRUCT_OFFSET(struct hek, hek_key)))
33#endif
87372f42 34
177ebd37 35#if PERL_VERSION < 6
36# define PL_opargs opargs
37# define PL_op_name op_name
38#endif
39
9fc9ab86 40#ifdef _MSC_VER
1a36ac09 41/* "structured exception" handling is a Microsoft extension to C and C++.
42 It's *not* C++ exception handling - C++ exception handling can't capture
43 SEGVs and suchlike, whereas this can. There's no known analagous
44 functionality on other platforms. */
45# include <excpt.h>
46# define TRY_TO_CATCH_SEGV __try
1c35d132 47# define CAUGHT_EXCEPTION __except(EXCEPTION_EXECUTE_HANDLER)
9fc9ab86 48#else
1a36ac09 49# define TRY_TO_CATCH_SEGV if(1)
50# define CAUGHT_EXCEPTION else
9fc9ab86 51#endif
52
53#ifdef __GNUC__
54# define __attribute__(x)
55#endif
56
b7621729 57#if 0 && defined(DEBUGGING)
58#define dbg_printf(x) printf x
59#else
60#define dbg_printf(x)
61#endif
98ecbbc6 62
0964064b 63#define TAG /* printf( "# %s(%d)\n", __FILE__, __LINE__ ) */
6a9ad7ec 64#define carp puts
9fc9ab86 65
30fe4f47 66/* The idea is to have a tree structure to store 1 bit per possible pointer
67 address. The lowest 16 bits are stored in a block of 8092 bytes.
68 The blocks are in a 256-way tree, indexed by the reset of the pointer.
69 This can cope with 32 and 64 bit pointers, and any address space layout,
70 without excessive memory needs. The assumption is that your CPU cache
71 works :-) (And that we're not going to bust it) */
72
30fe4f47 73#define BYTE_BITS 3
74#define LEAF_BITS (16 - BYTE_BITS)
75#define LEAF_MASK 0x1FFF
9fc9ab86 76
c07e8ef8 77typedef struct npath_node_st npath_node_t;
78struct npath_node_st {
79 npath_node_t *prev;
80 const void *id;
81 U8 type;
82 U8 flags;
83 UV seqn;
84 U16 depth;
85};
86
65db36c0 87struct state {
eee00145 88 UV total_size;
65db36c0 89 bool regex_whine;
90 bool fm_whine;
91 bool dangle_whine;
92 bool go_yell;
93 /* My hunch (not measured) is that for most architectures pointers will
94 start with 0 bits, hence the start of this array will be hot, and the
95 end unused. So put the flags next to the hot end. */
96 void *tracking[256];
c07e8ef8 97 /* callback hooks and data */
98 int (*add_attr_cb)(struct state *st, npath_node_t *npath_node, UV attr_type, const char *name, UV value);
99 void (*free_state_cb)(struct state *st);
100 UV seqn;
101 void *state_cb_data; /* free'd by free_state() after free_state_cb() call */
5e486cae 102 /* this stuff wil be moved to state_cb_data later */
103 FILE *node_stream;
65db36c0 104};
105
c07e8ef8 106#define ADD_SIZE(st, leafname, bytes) (NPathAddSizeCb(st, leafname, bytes) (st)->total_size += (bytes))
107
108#define PATH_TRACKING
109#ifdef PATH_TRACKING
110
111#define NPathAddSizeCb(st, name, bytes) (st->add_attr_cb && st->add_attr_cb(st, NP-1, 0, (name), (bytes))),
112#define pPATH npath_node_t *NPathArg
113
012b5f33 114/* A subtle point here is that dNPathNodes and NPathPushNode leaves NP pointing
115 * to the next unused slot (though with prev already filled in)
c07e8ef8 116 * whereas NPathLink leaves NP unchanged, it just fills in the slot NP points
117 * to and passes that NP value to the function being called.
118 */
119#define dNPathNodes(nodes, prev_np) \
120 npath_node_t name_path_nodes[nodes+1]; /* +1 for NPathLink */ \
121 npath_node_t *NP = &name_path_nodes[0]; \
122 NP->seqn = 0; \
123 NP->type = 0; \
124 NP->id = "?0?"; /* DEBUG */ \
125 NP->prev = prev_np
012b5f33 126#define NPathPushNode(nodeid, nodetype) \
c07e8ef8 127 NP->id = nodeid; \
128 NP->type = nodetype; \
012b5f33 129 if(0)fprintf(stderr,"NPathPushNode (%p <-) %p <- [%d %s]\n", NP->prev, NP, nodetype,(char*)nodeid);\
c07e8ef8 130 NP++; \
131 NP->id="?+?"; /* DEBUG */ \
132 NP->seqn = 0; \
133 NP->prev = (NP-1)
012b5f33 134#define NPathSetNode(nodeid, nodetype) \
135 (NP-1)->id = nodeid; \
136 (NP-1)->type = nodetype; \
137 if(0)fprintf(stderr,"NPathSetNode (%p <-) %p <- [%d %s]\n", (NP-1)->prev, (NP-1), nodetype,(char*)nodeid);\
138 (NP-1)->seqn = 0;
c07e8ef8 139
140/* dNPathUseParent points NP directly the the parents' name_path_nodes array
141 * So the function can only safely call ADD_*() but not NPathLink, unless the
142 * caller has spare nodes in its name_path_nodes.
143 */
144#define dNPathUseParent(prev_np) npath_node_t *NP = (((prev_np+1)->prev = prev_np), prev_np+1)
145
146#define NPtype_NAME 0x01
147#define NPtype_LINK 0x02
148#define NPtype_SV 0x03
149#define NPtype_MAGIC 0x04
150#define NPtype_OP 0x05
151
fc6614ee 152#define NPathLink(nodeid) ((NP->id = nodeid), (NP->type = NPtype_LINK), (NP->seqn = 0), NP)
c07e8ef8 153#define NPathOpLink (NPathArg)
154#define ADD_ATTR(st, attr_type, attr_name, attr_value) (st->add_attr_cb && st->add_attr_cb(st, NP-1, attr_type, attr_name, attr_value))
155
156#else
157
158#define NPathAddSizeCb(st, name, bytes)
159#define pPATH void *npath_dummy /* XXX ideally remove */
160#define dNPathNodes(nodes, prev_np) dNOOP
161#define NPathLink(nodeid, nodetype) NULL
162#define NPathOpLink NULL
163#define ADD_ATTR(st, attr_type, attr_name, attr_value) NOOP
164
165#endif /* PATH_TRACKING */
166
167
168
169
170#ifdef PATH_TRACKING
171
172static const char *svtypenames[SVt_LAST] = {
173#if PERL_VERSION < 9
174 "NULL", "IV", "NV", "RV", "PV", "PVIV", "PVNV", "PVMG", "PVBM", "PVLV", "PVAV", "PVHV", "PVCV", "PVGV", "PVFM", "PVIO",
175#elif PERL_VERSION == 10 && PERL_SUBVERSION == 0
176 "NULL", "BIND", "IV", "NV", "RV", "PV", "PVIV", "PVNV", "PVMG", "PVGV", "PVLV", "PVAV", "PVHV", "PVCV", "PVFM", "PVIO",
177#elif PERL_VERSION == 10 && PERL_SUBVERSION == 1
178 "NULL", "BIND", "IV", "NV", "RV", "PV", "PVIV", "PVNV", "PVMG", "PVGV", "PVLV", "PVAV", "PVHV", "PVCV", "PVFM", "PVIO",
179#elif PERL_VERSION < 13
180 "NULL", "BIND", "IV", "NV", "PV", "PVIV", "PVNV", "PVMG", "REGEXP", "PVGV", "PVLV", "PVAV", "PVHV", "PVCV", "PVFM", "PVIO",
181#else
182 "NULL", "BIND", "IV", "NV", "PV", "PVIV", "PVNV", "PVMG", "REGEXP", "PVGV", "PVLV", "PVAV", "PVHV", "PVCV", "PVFM", "PVIO",
183#endif
184};
185
186int
5e486cae 187np_print_node_name(FILE *fp, npath_node_t *npath_node)
c07e8ef8 188{
189 char buf[1024]; /* XXX */
190
191 switch (npath_node->type) {
192 case NPtype_SV: { /* id is pointer to the SV sv_size was called on */
193 const SV *sv = (SV*)npath_node->id;
194 int type = SvTYPE(sv);
195 char *typename = (type == SVt_IV && SvROK(sv)) ? "RV" : svtypenames[type];
5e486cae 196 fprintf(fp, "SV(%s)", typename);
c07e8ef8 197 switch(type) { /* add some useful details */
5e486cae 198 case SVt_PVAV: fprintf(fp, " fill=%d/%ld", av_len((AV*)sv), AvMAX((AV*)sv)); break;
199 case SVt_PVHV: fprintf(fp, " fill=%ld/%ld", HvFILL((HV*)sv), HvMAX((HV*)sv)); break;
c07e8ef8 200 }
201 break;
202 }
203 case NPtype_OP: { /* id is pointer to the OP op_size was called on */
204 const OP *op = (OP*)npath_node->id;
5e486cae 205 fprintf(fp, "OP(%s)", OP_NAME(op));
c07e8ef8 206 break;
207 }
208 case NPtype_MAGIC: { /* id is pointer to the MAGIC struct */
209 MAGIC *magic_pointer = (MAGIC*)npath_node->id;
210 /* XXX it would be nice if we could reuse mg_names.c [sigh] */
5e486cae 211 fprintf(fp, "MAGIC(%c)", magic_pointer->mg_type ? magic_pointer->mg_type : '0');
c07e8ef8 212 break;
213 }
214 case NPtype_LINK:
5e486cae 215 fprintf(fp, "%s->", npath_node->id);
c07e8ef8 216 break;
217 case NPtype_NAME:
5e486cae 218 fprintf(fp, "%s", npath_node->id);
c07e8ef8 219 break;
220 default: /* assume id is a string pointer */
5e486cae 221 fprintf(fp, "UNKNOWN(%d,%p)", npath_node->type, npath_node->id);
c07e8ef8 222 break;
223 }
224 return 0;
225}
226
227void
5e486cae 228np_dump_indent(int depth) {
c07e8ef8 229 while (depth-- > 0)
230 fprintf(stderr, ": ");
231}
232
233int
fc6614ee 234np_walk_new_nodes(struct state *st,
235 npath_node_t *npath_node,
236 npath_node_t *npath_node_deeper,
237 int (*cb)(struct state *st, npath_node_t *npath_node, npath_node_t *npath_node_deeper))
c07e8ef8 238{
239 if (npath_node->seqn) /* node already output */
fc6614ee 240 return 0;
c07e8ef8 241
242 if (npath_node->prev) {
fc6614ee 243 np_walk_new_nodes(st, npath_node->prev, npath_node, cb); /* recurse */
c07e8ef8 244 npath_node->depth = npath_node->prev->depth + 1;
245 }
246 else npath_node->depth = 0;
247 npath_node->seqn = ++st->seqn;
248
fc6614ee 249 if (cb) {
250 if (cb(st, npath_node, npath_node_deeper)) {
251 /* ignore this node */
252 assert(npath_node->prev);
253 assert(npath_node->depth);
254 assert(npath_node_deeper);
255 npath_node->depth--;
256 npath_node->seqn = --st->seqn;
257 npath_node_deeper->prev = npath_node->prev;
258 }
259 }
c07e8ef8 260
fc6614ee 261 return 0;
262}
263
264int
265np_dump_formatted_node(struct state *st, npath_node_t *npath_node, npath_node_t *npath_node_deeper) {
266 if (0 && npath_node->type == NPtype_LINK)
267 return 1;
268 np_dump_indent(npath_node->depth);
269 np_print_node_name(stderr, npath_node);
270 fprintf(stderr, "\t\t[#%ld @%u] ", npath_node->seqn, npath_node->depth);
271 fprintf(stderr, "\n");
272 return 0;
c07e8ef8 273}
274
275int
5e486cae 276np_dump_node_path_info(struct state *st, npath_node_t *npath_node, UV attr_type, const char *attr_name, UV attr_value)
c07e8ef8 277{
278 if (!attr_type && !attr_value)
5e486cae 279 return 0; /* ignore zero sized leaf items */
fc6614ee 280 np_walk_new_nodes(st, npath_node, NULL, np_dump_formatted_node);
5e486cae 281 np_dump_indent(npath_node->depth+1);
c07e8ef8 282 if (attr_type) {
283 fprintf(stderr, "~NAMED('%s') %lu", attr_name, attr_value);
284 }
285 else {
286 fprintf(stderr, "+%ld ", attr_value);
287 fprintf(stderr, "%s ", attr_name);
288 fprintf(stderr, "=%ld ", attr_value+st->total_size);
289 }
290 fprintf(stderr, "\n");
291 return 0;
292}
293
5e486cae 294int
fc6614ee 295np_stream_formatted_node(struct state *st, npath_node_t *npath_node, npath_node_t *npath_node_deeper) {
5e486cae 296 fprintf(st->node_stream, "N %lu %u ", npath_node->seqn,
fc6614ee 297 (unsigned)npath_node->depth
5e486cae 298 );
299 np_print_node_name(st->node_stream, npath_node);
300 fprintf(st->node_stream, "\n");
301 return 0;
302}
303
304int
305np_stream_node_path_info(struct state *st, npath_node_t *npath_node, UV attr_type, const char *attr_name, UV attr_value)
306{
307 if (!attr_type && !attr_value)
308 return 0; /* ignore zero sized leaf items */
fc6614ee 309 np_walk_new_nodes(st, npath_node, NULL, np_stream_formatted_node);
5e486cae 310 if (attr_type) {
311 fprintf(st->node_stream, "A %lu ", npath_node->seqn); /* Attribute name and value */
312 }
313 else {
314 fprintf(st->node_stream, "L %lu ", npath_node->seqn); /* Leaf name and memory size */
315 }
316 fprintf(st->node_stream, "%lu %s\n", attr_value, attr_name);
317 return 0;
318}
319
c07e8ef8 320#endif /* PATH_TRACKING */
321
322
9fc9ab86 323/*
324 Checks to see if thing is in the bitstring.
325 Returns true or false, and
326 notes thing in the segmented bitstring.
327 */
2eb93d08 328static bool
a4efdff3 329check_new(struct state *st, const void *const p) {
30fe4f47 330 unsigned int bits = 8 * sizeof(void*);
331 const size_t raw_p = PTR2nat(p);
332 /* This effectively rotates the value right by the number of low always-0
333 bits in an aligned pointer. The assmption is that most (if not all)
334 pointers are aligned, and these will be in the same chain of nodes
335 (and hence hot in the cache) but we can still deal with any unaligned
336 pointers. */
337 const size_t cooked_p
f404ed48 338 = (raw_p >> ALIGN_BITS) | (raw_p << (bits - ALIGN_BITS));
30fe4f47 339 const U8 this_bit = 1 << (cooked_p & 0x7);
340 U8 **leaf_p;
341 U8 *leaf;
342 unsigned int i;
302077b6 343 void **tv_p = (void **) (st->tracking);
30fe4f47 344
302077b6 345 if (NULL == p) return FALSE;
1a36ac09 346 TRY_TO_CATCH_SEGV {
2eb93d08 347 const char c = *(const char *)p;
9fc9ab86 348 }
1a36ac09 349 CAUGHT_EXCEPTION {
a4efdff3 350 if (st->dangle_whine)
9fc9ab86 351 warn( "Devel::Size: Encountered invalid pointer: %p\n", p );
352 return FALSE;
353 }
9fc9ab86 354 TAG;
30fe4f47 355
356 bits -= 8;
357 /* bits now 24 (32 bit pointers) or 56 (64 bit pointers) */
358
359 /* First level is always present. */
360 do {
361 i = (unsigned int)((cooked_p >> bits) & 0xFF);
362 if (!tv_p[i])
363 Newxz(tv_p[i], 256, void *);
364 tv_p = (void **)(tv_p[i]);
365 bits -= 8;
366 } while (bits > LEAF_BITS + BYTE_BITS);
367 /* bits now 16 always */
5f04c81d 368#if !defined(MULTIPLICITY) || PERL_VERSION > 8 || (PERL_VERSION == 8 && PERL_SUBVERSION > 8)
369 /* 5.8.8 and early have an assert() macro that uses Perl_croak, hence needs
370 a my_perl under multiplicity */
30fe4f47 371 assert(bits == 16);
5f04c81d 372#endif
30fe4f47 373 leaf_p = (U8 **)tv_p;
374 i = (unsigned int)((cooked_p >> bits) & 0xFF);
375 if (!leaf_p[i])
376 Newxz(leaf_p[i], 1 << LEAF_BITS, U8);
377 leaf = leaf_p[i];
378
9fc9ab86 379 TAG;
30fe4f47 380
381 i = (unsigned int)((cooked_p >> BYTE_BITS) & LEAF_MASK);
382
383 if(leaf[i] & this_bit)
384 return FALSE;
385
386 leaf[i] |= this_bit;
9fc9ab86 387 return TRUE;
388}
389
e9716740 390static void
30fe4f47 391free_tracking_at(void **tv, int level)
392{
393 int i = 255;
394
395 if (--level) {
396 /* Nodes */
397 do {
398 if (tv[i]) {
384ebd3f 399 free_tracking_at((void **) tv[i], level);
30fe4f47 400 Safefree(tv[i]);
401 }
402 } while (i--);
403 } else {
404 /* Leaves */
405 do {
406 if (tv[i])
407 Safefree(tv[i]);
408 } while (i--);
409 }
410}
411
412static void
a4efdff3 413free_state(struct state *st)
e9716740 414{
30fe4f47 415 const int top_level = (sizeof(void *) * 8 - LEAF_BITS - BYTE_BITS) / 8;
c07e8ef8 416 if (st->free_state_cb)
417 st->free_state_cb(st);
418 if (st->state_cb_data)
419 Safefree(st->state_cb_data);
a4efdff3 420 free_tracking_at((void **)st->tracking, top_level);
421 Safefree(st);
e9716740 422}
423
f3cf7e20 424/* For now, this is somewhat a compatibility bodge until the plan comes
425 together for fine grained recursion control. total_size() would recurse into
426 hash and array members, whereas sv_size() would not. However, sv_size() is
427 called with CvSTASH() of a CV, which means that if it (also) starts to
428 recurse fully, then the size of any CV now becomes the size of the entire
429 symbol table reachable from it, and potentially the entire symbol table, if
430 any subroutine makes a reference to a global (such as %SIG). The historical
431 implementation of total_size() didn't report "everything", and changing the
432 only available size to "everything" doesn't feel at all useful. */
433
434#define NO_RECURSION 0
435#define SOME_RECURSION 1
436#define TOTAL_SIZE_RECURSION 2
437
c07e8ef8 438static void sv_size(pTHX_ struct state *, pPATH, const SV *const, const int recurse);
db519f11 439
7ccc7d88 440typedef enum {
9fc9ab86 441 OPc_NULL, /* 0 */
442 OPc_BASEOP, /* 1 */
443 OPc_UNOP, /* 2 */
444 OPc_BINOP, /* 3 */
445 OPc_LOGOP, /* 4 */
446 OPc_LISTOP, /* 5 */
447 OPc_PMOP, /* 6 */
448 OPc_SVOP, /* 7 */
449 OPc_PADOP, /* 8 */
450 OPc_PVOP, /* 9 */
451 OPc_LOOP, /* 10 */
452 OPc_COP /* 11 */
177ebd37 453#ifdef OA_CONDOP
454 , OPc_CONDOP /* 12 */
455#endif
456#ifdef OA_GVOP
457 , OPc_GVOP /* 13 */
458#endif
459
7ccc7d88 460} opclass;
461
462static opclass
9fc9ab86 463cc_opclass(const OP * const o)
7ccc7d88 464{
465 if (!o)
9fc9ab86 466 return OPc_NULL;
1a36ac09 467 TRY_TO_CATCH_SEGV {
9fc9ab86 468 if (o->op_type == 0)
469 return (o->op_flags & OPf_KIDS) ? OPc_UNOP : OPc_BASEOP;
7ccc7d88 470
9fc9ab86 471 if (o->op_type == OP_SASSIGN)
472 return ((o->op_private & OPpASSIGN_BACKWARDS) ? OPc_UNOP : OPc_BINOP);
7ccc7d88 473
9fc9ab86 474 #ifdef USE_ITHREADS
475 if (o->op_type == OP_GV || o->op_type == OP_GVSV || o->op_type == OP_AELEMFAST)
476 return OPc_PADOP;
477 #endif
7ccc7d88 478
9fc9ab86 479 if ((o->op_type == OP_TRANS)) {
480 return OPc_BASEOP;
481 }
7ccc7d88 482
9fc9ab86 483 switch (PL_opargs[o->op_type] & OA_CLASS_MASK) {
484 case OA_BASEOP: TAG;
485 return OPc_BASEOP;
486
487 case OA_UNOP: TAG;
488 return OPc_UNOP;
489
490 case OA_BINOP: TAG;
491 return OPc_BINOP;
62691e7c 492
9fc9ab86 493 case OA_LOGOP: TAG;
494 return OPc_LOGOP;
7ccc7d88 495
9fc9ab86 496 case OA_LISTOP: TAG;
497 return OPc_LISTOP;
7ccc7d88 498
9fc9ab86 499 case OA_PMOP: TAG;
500 return OPc_PMOP;
7ccc7d88 501
9fc9ab86 502 case OA_SVOP: TAG;
503 return OPc_SVOP;
7ccc7d88 504
177ebd37 505#ifdef OA_PADOP
9fc9ab86 506 case OA_PADOP: TAG;
507 return OPc_PADOP;
177ebd37 508#endif
509
510#ifdef OA_GVOP
511 case OA_GVOP: TAG;
512 return OPc_GVOP;
513#endif
7ccc7d88 514
177ebd37 515#ifdef OA_PVOP_OR_SVOP
9fc9ab86 516 case OA_PVOP_OR_SVOP: TAG;
517 /*
518 * Character translations (tr///) are usually a PVOP, keeping a
519 * pointer to a table of shorts used to look up translations.
520 * Under utf8, however, a simple table isn't practical; instead,
521 * the OP is an SVOP, and the SV is a reference to a swash
522 * (i.e., an RV pointing to an HV).
523 */
524 return (o->op_private & (OPpTRANS_TO_UTF|OPpTRANS_FROM_UTF))
525 ? OPc_SVOP : OPc_PVOP;
177ebd37 526#endif
7ccc7d88 527
9fc9ab86 528 case OA_LOOP: TAG;
529 return OPc_LOOP;
7ccc7d88 530
9fc9ab86 531 case OA_COP: TAG;
532 return OPc_COP;
7ccc7d88 533
9fc9ab86 534 case OA_BASEOP_OR_UNOP: TAG;
7ccc7d88 535 /*
9fc9ab86 536 * UNI(OP_foo) in toke.c returns token UNI or FUNC1 depending on
537 * whether parens were seen. perly.y uses OPf_SPECIAL to
538 * signal whether a BASEOP had empty parens or none.
539 * Some other UNOPs are created later, though, so the best
540 * test is OPf_KIDS, which is set in newUNOP.
7ccc7d88 541 */
9fc9ab86 542 return (o->op_flags & OPf_KIDS) ? OPc_UNOP : OPc_BASEOP;
543
544 case OA_FILESTATOP: TAG;
545 /*
546 * The file stat OPs are created via UNI(OP_foo) in toke.c but use
547 * the OPf_REF flag to distinguish between OP types instead of the
548 * usual OPf_SPECIAL flag. As usual, if OPf_KIDS is set, then we
549 * return OPc_UNOP so that walkoptree can find our children. If
550 * OPf_KIDS is not set then we check OPf_REF. Without OPf_REF set
551 * (no argument to the operator) it's an OP; with OPf_REF set it's
552 * an SVOP (and op_sv is the GV for the filehandle argument).
553 */
554 return ((o->op_flags & OPf_KIDS) ? OPc_UNOP :
555 #ifdef USE_ITHREADS
556 (o->op_flags & OPf_REF) ? OPc_PADOP : OPc_BASEOP);
557 #else
558 (o->op_flags & OPf_REF) ? OPc_SVOP : OPc_BASEOP);
559 #endif
560 case OA_LOOPEXOP: TAG;
561 /*
562 * next, last, redo, dump and goto use OPf_SPECIAL to indicate that a
563 * label was omitted (in which case it's a BASEOP) or else a term was
564 * seen. In this last case, all except goto are definitely PVOP but
565 * goto is either a PVOP (with an ordinary constant label), an UNOP
566 * with OPf_STACKED (with a non-constant non-sub) or an UNOP for
567 * OP_REFGEN (with goto &sub) in which case OPf_STACKED also seems to
568 * get set.
569 */
570 if (o->op_flags & OPf_STACKED)
571 return OPc_UNOP;
572 else if (o->op_flags & OPf_SPECIAL)
573 return OPc_BASEOP;
574 else
575 return OPc_PVOP;
177ebd37 576
577#ifdef OA_CONDOP
578 case OA_CONDOP: TAG;
579 return OPc_CONDOP;
580#endif
9fc9ab86 581 }
582 warn("Devel::Size: Can't determine class of operator %s, assuming BASEOP\n",
583 PL_op_name[o->op_type]);
584 }
1a36ac09 585 CAUGHT_EXCEPTION { }
7ccc7d88 586 return OPc_BASEOP;
587}
588
6a9ad7ec 589/* Figure out how much magic is attached to the SV and return the
590 size */
eee00145 591static void
c07e8ef8 592magic_size(pTHX_ const SV * const thing, struct state *st, pPATH) {
593 dNPathNodes(1, NPathArg);
980c6576 594 MAGIC *magic_pointer = SvMAGIC(thing);
6a9ad7ec 595
012b5f33 596 /* push a dummy node for NPathSetNode to update inside the while loop */
597 NPathPushNode("dummy", NPtype_NAME);
598
980c6576 599 /* Have we seen the magic pointer? (NULL has always been seen before) */
e5c69bdd 600 while (check_new(st, magic_pointer)) {
c07e8ef8 601
012b5f33 602 NPathSetNode(magic_pointer, NPtype_MAGIC);
c07e8ef8 603
604 ADD_SIZE(st, "mg", sizeof(MAGIC));
9847261d 605 /* magic vtables aren't freed when magic is freed, so don't count them.
606 (They are static structures. Anything that assumes otherwise is buggy.)
607 */
608
6a9ad7ec 609
1a36ac09 610 TRY_TO_CATCH_SEGV {
fc6614ee 611 sv_size(aTHX_ st, NPathLink("mg_obj"), magic_pointer->mg_obj, TOTAL_SIZE_RECURSION);
d1888d0b 612 if (magic_pointer->mg_len == HEf_SVKEY) {
fc6614ee 613 sv_size(aTHX_ st, NPathLink("mg_ptr"), (SV *)magic_pointer->mg_ptr, TOTAL_SIZE_RECURSION);
d1888d0b 614 }
615#if defined(PERL_MAGIC_utf8) && defined (PERL_MAGIC_UTF8_CACHESIZE)
616 else if (magic_pointer->mg_type == PERL_MAGIC_utf8) {
617 if (check_new(st, magic_pointer->mg_ptr)) {
c07e8ef8 618 ADD_SIZE(st, "PERL_MAGIC_utf8", PERL_MAGIC_UTF8_CACHESIZE * 2 * sizeof(STRLEN));
d1888d0b 619 }
620 }
621#endif
622 else if (magic_pointer->mg_len > 0) {
623 if (check_new(st, magic_pointer->mg_ptr)) {
c07e8ef8 624 ADD_SIZE(st, "mg_len", magic_pointer->mg_len);
d1888d0b 625 }
626 }
6a9ad7ec 627
0964064b 628 /* Get the next in the chain */
9fc9ab86 629 magic_pointer = magic_pointer->mg_moremagic;
630 }
1a36ac09 631 CAUGHT_EXCEPTION {
a4efdff3 632 if (st->dangle_whine)
9fc9ab86 633 warn( "Devel::Size: Encountered bad magic at: %p\n", magic_pointer );
634 }
6a9ad7ec 635 }
6a9ad7ec 636}
637
eee00145 638static void
c07e8ef8 639check_new_and_strlen(struct state *st, const char *const p, pPATH) {
640 dNPathNodes(1, NPathArg->prev);
641 if(check_new(st, p)) {
012b5f33 642 NPathPushNode(NPathArg->id, NPtype_NAME);
c07e8ef8 643 ADD_SIZE(st, NPathArg->id, 1 + strlen(p));
644 }
99684fd4 645}
646
647static void
c07e8ef8 648regex_size(const REGEXP * const baseregex, struct state *st, pPATH) {
649 dNPathNodes(1, NPathArg);
c1bfd7da 650 if(!check_new(st, baseregex))
651 return;
012b5f33 652 NPathPushNode("regex_size", NPtype_NAME);
c07e8ef8 653 ADD_SIZE(st, "REGEXP", sizeof(REGEXP));
9fc9ab86 654#if (PERL_VERSION < 11)
6ea94d90 655 /* Note the size of the paren offset thing */
c07e8ef8 656 ADD_SIZE(st, "nparens", sizeof(I32) * baseregex->nparens * 2);
657 ADD_SIZE(st, "precomp", strlen(baseregex->precomp));
6ea94d90 658#else
c07e8ef8 659 ADD_SIZE(st, "regexp", sizeof(struct regexp));
660 ADD_SIZE(st, "nparens", sizeof(I32) * SvANY(baseregex)->nparens * 2);
661 /*ADD_SIZE(st, strlen(SvANY(baseregex)->subbeg));*/
6ea94d90 662#endif
a4efdff3 663 if (st->go_yell && !st->regex_whine) {
6ea94d90 664 carp("Devel::Size: Calculated sizes for compiled regexes are incompatible, and probably always will be");
a4efdff3 665 st->regex_whine = 1;
98ecbbc6 666 }
7ccc7d88 667}
668
eee00145 669static void
c07e8ef8 670op_size(pTHX_ const OP * const baseop, struct state *st, pPATH)
1e5a8ad2 671{
c07e8ef8 672 /* op_size recurses to follow the chain of opcodes.
673 * For the 'path' we don't want the chain to be 'nested' in the path so we
674 * use ->prev in dNPathNodes.
675 */
676 dNPathUseParent(NPathArg);
677
1e5a8ad2 678 TRY_TO_CATCH_SEGV {
679 TAG;
680 if(!check_new(st, baseop))
681 return;
682 TAG;
c07e8ef8 683 op_size(aTHX_ baseop->op_next, st, NPathOpLink);
1e5a8ad2 684 TAG;
685 switch (cc_opclass(baseop)) {
686 case OPc_BASEOP: TAG;
c07e8ef8 687 ADD_SIZE(st, "op", sizeof(struct op));
1e5a8ad2 688 TAG;break;
689 case OPc_UNOP: TAG;
c07e8ef8 690 ADD_SIZE(st, "unop", sizeof(struct unop));
691 op_size(aTHX_ ((UNOP *)baseop)->op_first, st, NPathOpLink);
1e5a8ad2 692 TAG;break;
693 case OPc_BINOP: TAG;
c07e8ef8 694 ADD_SIZE(st, "binop", sizeof(struct binop));
695 op_size(aTHX_ ((BINOP *)baseop)->op_first, st, NPathOpLink);
696 op_size(aTHX_ ((BINOP *)baseop)->op_last, st, NPathOpLink);
1e5a8ad2 697 TAG;break;
698 case OPc_LOGOP: TAG;
c07e8ef8 699 ADD_SIZE(st, "logop", sizeof(struct logop));
700 op_size(aTHX_ ((BINOP *)baseop)->op_first, st, NPathOpLink);
701 op_size(aTHX_ ((LOGOP *)baseop)->op_other, st, NPathOpLink);
1e5a8ad2 702 TAG;break;
177ebd37 703#ifdef OA_CONDOP
704 case OPc_CONDOP: TAG;
c07e8ef8 705 ADD_SIZE(st, "condop", sizeof(struct condop));
706 op_size(aTHX_ ((BINOP *)baseop)->op_first, st, NPathOpLink);
707 op_size(aTHX_ ((CONDOP *)baseop)->op_true, st, NPathOpLink);
708 op_size(aTHX_ ((CONDOP *)baseop)->op_false, st, NPathOpLink);
177ebd37 709 TAG;break;
710#endif
1e5a8ad2 711 case OPc_LISTOP: TAG;
c07e8ef8 712 ADD_SIZE(st, "listop", sizeof(struct listop));
713 op_size(aTHX_ ((LISTOP *)baseop)->op_first, st, NPathOpLink);
714 op_size(aTHX_ ((LISTOP *)baseop)->op_last, st, NPathOpLink);
1e5a8ad2 715 TAG;break;
716 case OPc_PMOP: TAG;
c07e8ef8 717 ADD_SIZE(st, "pmop", sizeof(struct pmop));
718 op_size(aTHX_ ((PMOP *)baseop)->op_first, st, NPathOpLink);
719 op_size(aTHX_ ((PMOP *)baseop)->op_last, st, NPathOpLink);
5a83b7cf 720#if PERL_VERSION < 9 || (PERL_VERSION == 9 && PERL_SUBVERSION < 5)
c07e8ef8 721 op_size(aTHX_ ((PMOP *)baseop)->op_pmreplroot, st, NPathOpLink);
722 op_size(aTHX_ ((PMOP *)baseop)->op_pmreplstart, st, NPathOpLink);
5a83b7cf 723#endif
c1bfd7da 724 /* This is defined away in perl 5.8.x, but it is in there for
725 5.6.x */
98ecbbc6 726#ifdef PM_GETRE
fc6614ee 727 regex_size(PM_GETRE((PMOP *)baseop), st, NPathLink("PM_GETRE"));
98ecbbc6 728#else
fc6614ee 729 regex_size(((PMOP *)baseop)->op_pmregexp, st, NPathLink("op_pmregexp"));
98ecbbc6 730#endif
c1bfd7da 731 TAG;break;
81f1c018 732 case OPc_SVOP: TAG;
c07e8ef8 733 ADD_SIZE(st, "svop", sizeof(struct svop));
574d9fd9 734 if (!(baseop->op_type == OP_AELEMFAST
735 && baseop->op_flags & OPf_SPECIAL)) {
736 /* not an OP_PADAV replacement */
fc6614ee 737 sv_size(aTHX_ st, NPathLink("SVOP"), ((SVOP *)baseop)->op_sv, SOME_RECURSION);
574d9fd9 738 }
81f1c018 739 TAG;break;
177ebd37 740#ifdef OA_PADOP
9fc9ab86 741 case OPc_PADOP: TAG;
c07e8ef8 742 ADD_SIZE(st, "padop", sizeof(struct padop));
99684fd4 743 TAG;break;
177ebd37 744#endif
745#ifdef OA_GVOP
746 case OPc_GVOP: TAG;
c07e8ef8 747 ADD_SIZE(st, "gvop", sizeof(struct gvop));
fc6614ee 748 sv_size(aTHX_ st, NPathLink("GVOP"), ((GVOP *)baseop)->op_gv, SOME_RECURSION);
177ebd37 749 TAG;break;
750#endif
99684fd4 751 case OPc_PVOP: TAG;
fc6614ee 752 check_new_and_strlen(st, ((PVOP *)baseop)->op_pv, NPathLink("op_pv"));
219b7d34 753 TAG;break;
1e5a8ad2 754 case OPc_LOOP: TAG;
c07e8ef8 755 ADD_SIZE(st, "loop", sizeof(struct loop));
756 op_size(aTHX_ ((LOOP *)baseop)->op_first, st, NPathOpLink);
757 op_size(aTHX_ ((LOOP *)baseop)->op_last, st, NPathOpLink);
758 op_size(aTHX_ ((LOOP *)baseop)->op_redoop, st, NPathOpLink);
759 op_size(aTHX_ ((LOOP *)baseop)->op_nextop, st, NPathOpLink);
760 op_size(aTHX_ ((LOOP *)baseop)->op_lastop, st, NPathOpLink);
1e5a8ad2 761 TAG;break;
762 case OPc_COP: TAG;
9fc9ab86 763 {
764 COP *basecop;
765 basecop = (COP *)baseop;
c07e8ef8 766 ADD_SIZE(st, "cop", sizeof(struct cop));
9fc9ab86 767
768 /* Change 33656 by nicholas@mouse-mill on 2008/04/07 11:29:51
769 Eliminate cop_label from struct cop by storing a label as the first
770 entry in the hints hash. Most statements don't have labels, so this
771 will save memory. Not sure how much.
772 The check below will be incorrect fail on bleadperls
773 before 5.11 @33656, but later than 5.10, producing slightly too
774 small memory sizes on these Perls. */
b7621729 775#if (PERL_VERSION < 11)
fc6614ee 776 check_new_and_strlen(st, basecop->cop_label, NPathLink("cop_label"));
b7621729 777#endif
7ccc7d88 778#ifdef USE_ITHREADS
fc6614ee 779 check_new_and_strlen(st, basecop->cop_file, NPathLink("cop_file"));
780 check_new_and_strlen(st, basecop->cop_stashpv, NPathLink("cop_stashpv"));
7ccc7d88 781#else
fc6614ee 782 sv_size(aTHX_ st, NPathLink("cop_stash"), (SV *)basecop->cop_stash, SOME_RECURSION);
783 sv_size(aTHX_ st, NPathLink("cop_filegv"), (SV *)basecop->cop_filegv, SOME_RECURSION);
7ccc7d88 784#endif
785
9fc9ab86 786 }
787 TAG;break;
788 default:
789 TAG;break;
790 }
791 }
1a36ac09 792 CAUGHT_EXCEPTION {
a4efdff3 793 if (st->dangle_whine)
9fc9ab86 794 warn( "Devel::Size: Encountered dangling pointer in opcode at: %p\n", baseop );
7ccc7d88 795 }
7ccc7d88 796}
6a9ad7ec 797
3d18ea10 798static void
c07e8ef8 799hek_size(pTHX_ struct state *st, HEK *hek, U32 shared, pPATH)
3d18ea10 800{
fc6614ee 801 dNPathNodes(1, NPathArg);
802
3d18ea10 803 /* Hash keys can be shared. Have we seen this before? */
804 if (!check_new(st, hek))
805 return;
fc6614ee 806 NPathPushNode("hek", NPtype_NAME);
c07e8ef8 807 ADD_SIZE(st, "hek_len", HEK_BASESIZE + hek->hek_len
3d18ea10 808#if PERL_VERSION < 8
809 + 1 /* No hash key flags prior to 5.8.0 */
810#else
811 + 2
812#endif
c07e8ef8 813 );
3d18ea10 814 if (shared) {
815#if PERL_VERSION < 10
c07e8ef8 816 ADD_SIZE(st, "he", sizeof(struct he));
3d18ea10 817#else
c07e8ef8 818 ADD_SIZE(st, "shared_he", STRUCT_OFFSET(struct shared_he, shared_he_hek));
3d18ea10 819#endif
820 }
821}
822
823
b6558d1d 824#if PERL_VERSION < 8 || PERL_SUBVERSION < 9
825# define SVt_LAST 16
24d37977 826#endif
827
f73dcfce 828#ifdef PURIFY
829# define MAYBE_PURIFY(normal, pure) (pure)
830# define MAYBE_OFFSET(struct_name, member) 0
831#else
832# define MAYBE_PURIFY(normal, pure) (normal)
833# define MAYBE_OFFSET(struct_name, member) STRUCT_OFFSET(struct_name, member)
834#endif
835
b6558d1d 836const U8 body_sizes[SVt_LAST] = {
837#if PERL_VERSION < 9
f73dcfce 838 0, /* SVt_NULL */
839 MAYBE_PURIFY(sizeof(IV), sizeof(XPVIV)), /* SVt_IV */
840 MAYBE_PURIFY(sizeof(NV), sizeof(XPVNV)), /* SVt_NV */
841 sizeof(XRV), /* SVt_RV */
842 sizeof(XPV), /* SVt_PV */
843 sizeof(XPVIV), /* SVt_PVIV */
844 sizeof(XPVNV), /* SVt_PVNV */
845 sizeof(XPVMG), /* SVt_PVMG */
846 sizeof(XPVBM), /* SVt_PVBM */
847 sizeof(XPVLV), /* SVt_PVLV */
848 sizeof(XPVAV), /* SVt_PVAV */
849 sizeof(XPVHV), /* SVt_PVHV */
850 sizeof(XPVCV), /* SVt_PVCV */
851 sizeof(XPVGV), /* SVt_PVGV */
852 sizeof(XPVFM), /* SVt_PVFM */
853 sizeof(XPVIO) /* SVt_PVIO */
b6558d1d 854#elif PERL_VERSION == 10 && PERL_SUBVERSION == 0
f73dcfce 855 0, /* SVt_NULL */
856 0, /* SVt_BIND */
857 0, /* SVt_IV */
858 MAYBE_PURIFY(sizeof(NV), sizeof(XPVNV)), /* SVt_NV */
859 0, /* SVt_RV */
860 MAYBE_PURIFY(sizeof(xpv_allocated), sizeof(XPV)), /* SVt_PV */
861 MAYBE_PURIFY(sizeof(xpviv_allocated), sizeof(XPVIV)),/* SVt_PVIV */
862 sizeof(XPVNV), /* SVt_PVNV */
863 sizeof(XPVMG), /* SVt_PVMG */
864 sizeof(XPVGV), /* SVt_PVGV */
865 sizeof(XPVLV), /* SVt_PVLV */
866 MAYBE_PURIFY(sizeof(xpvav_allocated), sizeof(XPVAV)),/* SVt_PVAV */
867 MAYBE_PURIFY(sizeof(xpvhv_allocated), sizeof(XPVHV)),/* SVt_PVHV */
868 MAYBE_PURIFY(sizeof(xpvcv_allocated), sizeof(XPVCV)),/* SVt_PVCV */
869 MAYBE_PURIFY(sizeof(xpvfm_allocated), sizeof(XPVFM)),/* SVt_PVFM */
870 sizeof(XPVIO), /* SVt_PVIO */
b6558d1d 871#elif PERL_VERSION == 10 && PERL_SUBVERSION == 1
f73dcfce 872 0, /* SVt_NULL */
873 0, /* SVt_BIND */
874 0, /* SVt_IV */
875 MAYBE_PURIFY(sizeof(NV), sizeof(XPVNV)), /* SVt_NV */
876 0, /* SVt_RV */
877 sizeof(XPV) - MAYBE_OFFSET(XPV, xpv_cur), /* SVt_PV */
878 sizeof(XPVIV) - MAYBE_OFFSET(XPV, xpv_cur), /* SVt_PVIV */
879 sizeof(XPVNV), /* SVt_PVNV */
880 sizeof(XPVMG), /* SVt_PVMG */
881 sizeof(XPVGV), /* SVt_PVGV */
882 sizeof(XPVLV), /* SVt_PVLV */
883 sizeof(XPVAV) - MAYBE_OFFSET(XPVAV, xav_fill), /* SVt_PVAV */
884 sizeof(XPVHV) - MAYBE_OFFSET(XPVHV, xhv_fill), /* SVt_PVHV */
885 sizeof(XPVCV) - MAYBE_OFFSET(XPVCV, xpv_cur), /* SVt_PVCV */
886 sizeof(XPVFM) - MAYBE_OFFSET(XPVFM, xpv_cur), /* SVt_PVFM */
887 sizeof(XPVIO) /* SVt_PVIO */
b6558d1d 888#elif PERL_VERSION < 13
f73dcfce 889 0, /* SVt_NULL */
890 0, /* SVt_BIND */
891 0, /* SVt_IV */
892 MAYBE_PURIFY(sizeof(NV), sizeof(XPVNV)), /* SVt_NV */
893 sizeof(XPV) - MAYBE_OFFSET(XPV, xpv_cur), /* SVt_PV */
894 sizeof(XPVIV) - MAYBE_OFFSET(XPV, xpv_cur), /* SVt_PVIV */
895 sizeof(XPVNV), /* SVt_PVNV */
896 sizeof(XPVMG), /* SVt_PVMG */
897 sizeof(regexp) - MAYBE_OFFSET(regexp, xpv_cur), /* SVt_REGEXP */
898 sizeof(XPVGV), /* SVt_PVGV */
899 sizeof(XPVLV), /* SVt_PVLV */
900 sizeof(XPVAV) - MAYBE_OFFSET(XPVAV, xav_fill), /* SVt_PVAV */
901 sizeof(XPVHV) - MAYBE_OFFSET(XPVHV, xhv_fill), /* SVt_PVHV */
902 sizeof(XPVCV) - MAYBE_OFFSET(XPVCV, xpv_cur), /* SVt_PVCV */
903 sizeof(XPVFM) - MAYBE_OFFSET(XPVFM, xpv_cur), /* SVt_PVFM */
904 sizeof(XPVIO) /* SVt_PVIO */
b6558d1d 905#else
f73dcfce 906 0, /* SVt_NULL */
907 0, /* SVt_BIND */
908 0, /* SVt_IV */
909 MAYBE_PURIFY(sizeof(NV), sizeof(XPVNV)), /* SVt_NV */
910 sizeof(XPV) - MAYBE_OFFSET(XPV, xpv_cur), /* SVt_PV */
911 sizeof(XPVIV) - MAYBE_OFFSET(XPV, xpv_cur), /* SVt_PVIV */
912 sizeof(XPVNV) - MAYBE_OFFSET(XPV, xpv_cur), /* SVt_PVNV */
913 sizeof(XPVMG), /* SVt_PVMG */
914 sizeof(regexp), /* SVt_REGEXP */
915 sizeof(XPVGV), /* SVt_PVGV */
916 sizeof(XPVLV), /* SVt_PVLV */
917 sizeof(XPVAV), /* SVt_PVAV */
918 sizeof(XPVHV), /* SVt_PVHV */
919 sizeof(XPVCV), /* SVt_PVCV */
920 sizeof(XPVFM), /* SVt_PVFM */
921 sizeof(XPVIO) /* SVt_PVIO */
b6558d1d 922#endif
923};
924
c07e8ef8 925
926static void
927padlist_size(pTHX_ struct state *const st, pPATH, PADLIST *padlist,
928 const int recurse)
929{
930 dNPathUseParent(NPathArg);
931 /* based on Perl_do_dump_pad() */
932 const AV *pad_name;
933 SV **pname;
934 I32 ix;
935
936 if (!padlist) {
937 return;
938 }
939 pad_name = MUTABLE_AV(*av_fetch(MUTABLE_AV(padlist), 0, FALSE));
940 pname = AvARRAY(pad_name);
941
942 for (ix = 1; ix <= AvFILLp(pad_name); ix++) {
943 const SV *namesv = pname[ix];
944 if (namesv && namesv == &PL_sv_undef) {
945 namesv = NULL;
946 }
947 if (namesv) {
948 if (SvFAKE(namesv))
949 ADD_ATTR(st, 1, SvPVX_const(namesv), ix);
950 else
951 ADD_ATTR(st, 1, SvPVX_const(namesv), ix);
952 }
953 else {
954 ADD_ATTR(st, 1, "SVs_PADTMP", ix);
955 }
956
957 }
958 sv_size(aTHX_ st, NPathArg, (SV*)padlist, recurse);
959}
960
961
a5c6bdd7 962static void
c07e8ef8 963sv_size(pTHX_ struct state *const st, pPATH, const SV * const orig_thing,
f3cf7e20 964 const int recurse) {
9fc9ab86 965 const SV *thing = orig_thing;
c07e8ef8 966 dNPathNodes(3, NPathArg);
b6558d1d 967 U32 type;
eee00145 968
c07e8ef8 969 if(!check_new(st, orig_thing))
a5c6bdd7 970 return;
81f1c018 971
b6558d1d 972 type = SvTYPE(thing);
973 if (type > SVt_LAST) {
974 warn("Devel::Size: Unknown variable type: %d encountered\n", type);
a5c6bdd7 975 return;
b6558d1d 976 }
012b5f33 977 NPathPushNode(thing, NPtype_SV);
c07e8ef8 978 ADD_SIZE(st, "sv", sizeof(SV) + body_sizes[type]);
b1e5ad85 979
b6558d1d 980 if (type >= SVt_PVMG) {
fc6614ee 981 magic_size(aTHX_ thing, st, NPathLink("MG"));
696b99e2 982 }
983
b6558d1d 984 switch (type) {
985#if (PERL_VERSION < 11)
e98cedbf 986 /* Is it a reference? */
9fc9ab86 987 case SVt_RV: TAG;
b6558d1d 988#else
989 case SVt_IV: TAG;
24d37977 990#endif
81f1c018 991 if(recurse && SvROK(thing))
fc6614ee 992 sv_size(aTHX_ st, NPathLink("RV"), SvRV_const(thing), recurse);
9fc9ab86 993 TAG;break;
267703fd 994
9fc9ab86 995 case SVt_PVAV: TAG;
e98cedbf 996 /* Is there anything in the array? */
997 if (AvMAX(thing) != -1) {
c8db37d3 998 /* an array with 10 slots has AvMax() set to 9 - te 2007-04-22 */
c07e8ef8 999 ADD_SIZE(st, "av_max", sizeof(SV *) * (AvMAX(thing) + 1));
eee00145 1000 dbg_printf(("total_size: %li AvMAX: %li av_len: $i\n", st->total_size, AvMAX(thing), av_len((AV*)thing)));
6c5ddc0d 1001
1002 if (recurse >= TOTAL_SIZE_RECURSION) {
1003 SSize_t i = AvFILLp(thing) + 1;
1004
1005 while (i--)
fc6614ee 1006 sv_size(aTHX_ st, NPathLink("AVelem"), AvARRAY(thing)[i], recurse);
6c5ddc0d 1007 }
e98cedbf 1008 }
1009 /* Add in the bits on the other side of the beginning */
0430b7f7 1010
b7621729 1011 dbg_printf(("total_size %li, sizeof(SV *) %li, AvARRAY(thing) %li, AvALLOC(thing)%li , sizeof(ptr) %li \n",
c07e8ef8 1012 st->total_size, sizeof(SV*), AvARRAY(thing), AvALLOC(thing), sizeof( thing )));
0430b7f7 1013
1014 /* under Perl 5.8.8 64bit threading, AvARRAY(thing) was a pointer while AvALLOC was 0,
b1e5ad85 1015 resulting in grossly overstated sized for arrays. Technically, this shouldn't happen... */
0430b7f7 1016 if (AvALLOC(thing) != 0) {
c07e8ef8 1017 ADD_SIZE(st, "AvALLOC", (sizeof(SV *) * (AvARRAY(thing) - AvALLOC(thing))));
0430b7f7 1018 }
795fc84c 1019#if (PERL_VERSION < 9)
1020 /* Is there something hanging off the arylen element?
1021 Post 5.9.something this is stored in magic, so will be found there,
1022 and Perl_av_arylen_p() takes a non-const AV*, hence compilers rightly
1023 complain about AvARYLEN() passing thing to it. */
fc6614ee 1024 sv_size(aTHX_ st, NPathLink("ARYLEN"), AvARYLEN(thing), recurse);
795fc84c 1025#endif
9fc9ab86 1026 TAG;break;
1027 case SVt_PVHV: TAG;
a6ea0805 1028 /* Now the array of buckets */
c07e8ef8 1029 ADD_SIZE(st, "hv_max", (sizeof(HE *) * (HvMAX(thing) + 1)));
1030 if (HvENAME(thing)) {
1031 ADD_ATTR(st, 1, HvENAME(thing), 0);
1032 }
a6ea0805 1033 /* Now walk the bucket chain */
6a9ad7ec 1034 if (HvARRAY(thing)) {
a6ea0805 1035 HE *cur_entry;
9fc9ab86 1036 UV cur_bucket = 0;
a6ea0805 1037 for (cur_bucket = 0; cur_bucket <= HvMAX(thing); cur_bucket++) {
9fc9ab86 1038 cur_entry = *(HvARRAY(thing) + cur_bucket);
1039 while (cur_entry) {
c07e8ef8 1040 ADD_SIZE(st, "he", sizeof(HE));
fc6614ee 1041 hek_size(aTHX_ st, cur_entry->hent_hek, HvSHAREKEYS(thing), NPathLink("hent_hek"));
5e486cae 1042 if (recurse >= TOTAL_SIZE_RECURSION) {
2c631ee0 1043/* I've seen a PL_strtab HeVAL == 0xC and 0x40C etc
5e486cae 1044 * just running perl -Mblib -Mstrict -MDevel::Size=:all -MCarp -e 'warn perl_size()'
1045 * but it seemed like a corruption - it would change come and go with irrelevant code changes.
1046 * so we protect against that here, but I'd like to know the cause.
1047 */
2c631ee0 1048if (PTR2UV(HeVAL(cur_entry)) > 0xFFF)
fc6614ee 1049 sv_size(aTHX_ st, NPathLink("HeVAL"), HeVAL(cur_entry), recurse);
5e486cae 1050 }
9fc9ab86 1051 cur_entry = cur_entry->hent_next;
1052 }
a6ea0805 1053 }
1054 }
78037efb 1055#ifdef HvAUX
1056 if (SvOOK(thing)) {
1057 /* This direct access is arguably "naughty": */
1058 struct mro_meta *meta = HvAUX(thing)->xhv_mro_meta;
b3a37f1a 1059#if PERL_VERSION > 13 || PERL_SUBVERSION > 8
1060 /* As is this: */
1061 I32 count = HvAUX(thing)->xhv_name_count;
1062
1063 if (count) {
1064 HEK **names = HvAUX(thing)->xhv_name_u.xhvnameu_names;
1065 if (count < 0)
1066 count = -count;
1067 while (--count)
fc6614ee 1068 hek_size(aTHX_ st, names[count], 1, NPathLink("HvAUXelem"));
b3a37f1a 1069 }
1070 else
1071#endif
1072 {
fc6614ee 1073 hek_size(aTHX_ st, HvNAME_HEK(thing), 1, NPathLink("HvNAME_HEK"));
b3a37f1a 1074 }
1075
c07e8ef8 1076 ADD_SIZE(st, "xpvhv_aux", sizeof(struct xpvhv_aux));
78037efb 1077 if (meta) {
c07e8ef8 1078 ADD_SIZE(st, "mro_meta", sizeof(struct mro_meta));
fc6614ee 1079 sv_size(aTHX_ st, NPathLink("mro_nextmethod"), (SV *)meta->mro_nextmethod, TOTAL_SIZE_RECURSION);
78037efb 1080#if PERL_VERSION > 10 || (PERL_VERSION == 10 && PERL_SUBVERSION > 0)
fc6614ee 1081 sv_size(aTHX_ st, NPathLink("isa"), (SV *)meta->isa, TOTAL_SIZE_RECURSION);
78037efb 1082#endif
1083#if PERL_VERSION > 10
fc6614ee 1084 sv_size(aTHX_ st, NPathLink("mro_linear_all"), (SV *)meta->mro_linear_all, TOTAL_SIZE_RECURSION);
1085 sv_size(aTHX_ st, NPathLink("mro_linear_current"), meta->mro_linear_current, TOTAL_SIZE_RECURSION);
78037efb 1086#else
fc6614ee 1087 sv_size(aTHX_ st, NPathLink("mro_linear_dfs"), (SV *)meta->mro_linear_dfs, TOTAL_SIZE_RECURSION);
1088 sv_size(aTHX_ st, NPathLink("mro_linear_c3"), (SV *)meta->mro_linear_c3, TOTAL_SIZE_RECURSION);
78037efb 1089#endif
1090 }
1091 }
1092#else
fc6614ee 1093 check_new_and_strlen(st, HvNAME_get(thing), NPathLink("HvNAME"));
78037efb 1094#endif
9fc9ab86 1095 TAG;break;
267703fd 1096
1097
1098 case SVt_PVFM: TAG;
fc6614ee 1099 padlist_size(aTHX_ st, NPathLink("CvPADLIST"), CvPADLIST(thing), SOME_RECURSION);
1100 sv_size(aTHX_ st, NPathLink("CvOUTSIDE"), (SV *)CvOUTSIDE(thing), recurse);
267703fd 1101
1102 if (st->go_yell && !st->fm_whine) {
1103 carp("Devel::Size: Calculated sizes for FMs are incomplete");
1104 st->fm_whine = 1;
1105 }
1106 goto freescalar;
1107
9fc9ab86 1108 case SVt_PVCV: TAG;
fc6614ee 1109 sv_size(aTHX_ st, NPathLink("CvSTASH"), (SV *)CvSTASH(thing), SOME_RECURSION);
1110 sv_size(aTHX_ st, NPathLink("SvSTASH"), (SV *)SvSTASH(thing), SOME_RECURSION);
1111 sv_size(aTHX_ st, NPathLink("CvGV"), (SV *)CvGV(thing), SOME_RECURSION);
1112 padlist_size(aTHX_ st, NPathLink("CvPADLIST"), CvPADLIST(thing), SOME_RECURSION);
1113 sv_size(aTHX_ st, NPathLink("CvOUTSIDE"), (SV *)CvOUTSIDE(thing), recurse);
66f50dda 1114 if (CvISXSUB(thing)) {
fc6614ee 1115 sv_size(aTHX_ st, NPathLink("cv_const_sv"), cv_const_sv((CV *)thing), recurse);
66f50dda 1116 } else {
fc6614ee 1117 op_size(aTHX_ CvSTART(thing), st, NPathLink("CvSTART"));
1118 op_size(aTHX_ CvROOT(thing), st, NPathLink("CvROOT"));
7ccc7d88 1119 }
267703fd 1120 goto freescalar;
1121
1122 case SVt_PVIO: TAG;
267703fd 1123 /* Some embedded char pointers */
fc6614ee 1124 check_new_and_strlen(st, ((XPVIO *) SvANY(thing))->xio_top_name, NPathLink("xio_top_name"));
1125 check_new_and_strlen(st, ((XPVIO *) SvANY(thing))->xio_fmt_name, NPathLink("xio_fmt_name"));
1126 check_new_and_strlen(st, ((XPVIO *) SvANY(thing))->xio_bottom_name, NPathLink("xio_bottom_name"));
267703fd 1127 /* Throw the GVs on the list to be walked if they're not-null */
fc6614ee 1128 sv_size(aTHX_ st, NPathLink("xio_top_gv"), (SV *)((XPVIO *) SvANY(thing))->xio_top_gv, recurse);
1129 sv_size(aTHX_ st, NPathLink("xio_bottom_gv"), (SV *)((XPVIO *) SvANY(thing))->xio_bottom_gv, recurse);
1130 sv_size(aTHX_ st, NPathLink("xio_fmt_gv"), (SV *)((XPVIO *) SvANY(thing))->xio_fmt_gv, recurse);
267703fd 1131
1132 /* Only go trotting through the IO structures if they're really
1133 trottable. If USE_PERLIO is defined we can do this. If
1134 not... we can't, so we don't even try */
1135#ifdef USE_PERLIO
1136 /* Dig into xio_ifp and xio_ofp here */
1137 warn("Devel::Size: Can't size up perlio layers yet\n");
1138#endif
1139 goto freescalar;
1140
267703fd 1141 case SVt_PVLV: TAG;
267703fd 1142#if (PERL_VERSION < 9)
1143 goto freescalar;
267703fd 1144#endif
7ccc7d88 1145
9fc9ab86 1146 case SVt_PVGV: TAG;
4a3d023d 1147 if(isGV_with_GP(thing)) {
638a265a 1148#ifdef GvNAME_HEK
fc6614ee 1149 hek_size(aTHX_ st, GvNAME_HEK(thing), 1, NPathLink("GvNAME_HEK"));
638a265a 1150#else
c07e8ef8 1151 ADD_SIZE(st, "GvNAMELEN", GvNAMELEN(thing));
638a265a 1152#endif
c07e8ef8 1153 ADD_ATTR(st, 1, GvNAME_get(thing), 0);
15588e9c 1154#ifdef GvFILE_HEK
fc6614ee 1155 hek_size(aTHX_ st, GvFILE_HEK(thing), 1, NPathLink("GvFILE_HEK"));
15588e9c 1156#elif defined(GvFILE)
2b217e71 1157# if !defined(USE_ITHREADS) || (PERL_VERSION > 8 || (PERL_VERSION == 8 && PERL_SUBVERSION > 8))
1158 /* With itreads, before 5.8.9, this can end up pointing to freed memory
1159 if the GV was created in an eval, as GvFILE() points to CopFILE(),
1160 and the relevant COP has been freed on scope cleanup after the eval.
1161 5.8.9 adds a binary compatible fudge that catches the vast majority
1162 of cases. 5.9.something added a proper fix, by converting the GP to
1163 use a shared hash key (porperly reference counted), instead of a
1164 char * (owned by who knows? possibly no-one now) */
fc6614ee 1165 check_new_and_strlen(st, GvFILE(thing), NPathLink("GvFILE"));
2b217e71 1166# endif
78dfb4e7 1167#endif
4a3d023d 1168 /* Is there something hanging off the glob? */
1169 if (check_new(st, GvGP(thing))) {
c07e8ef8 1170 ADD_SIZE(st, "GP", sizeof(GP));
fc6614ee 1171 sv_size(aTHX_ st, NPathLink("gp_sv"), (SV *)(GvGP(thing)->gp_sv), recurse);
1172 sv_size(aTHX_ st, NPathLink("gp_form"), (SV *)(GvGP(thing)->gp_form), recurse);
1173 sv_size(aTHX_ st, NPathLink("gp_av"), (SV *)(GvGP(thing)->gp_av), recurse);
1174 sv_size(aTHX_ st, NPathLink("gp_hv"), (SV *)(GvGP(thing)->gp_hv), recurse);
1175 sv_size(aTHX_ st, NPathLink("gp_egv"), (SV *)(GvGP(thing)->gp_egv), recurse);
1176 sv_size(aTHX_ st, NPathLink("gp_cv"), (SV *)(GvGP(thing)->gp_cv), recurse);
4a3d023d 1177 }
267703fd 1178#if (PERL_VERSION >= 9)
1179 TAG; break;
1180#endif
5c2e1b12 1181 }
b6558d1d 1182#if PERL_VERSION <= 8
1183 case SVt_PVBM: TAG;
1184#endif
267703fd 1185 case SVt_PVMG: TAG;
267703fd 1186 case SVt_PVNV: TAG;
267703fd 1187 case SVt_PVIV: TAG;
267703fd 1188 case SVt_PV: TAG;
267703fd 1189 freescalar:
1190 if(recurse && SvROK(thing))
fc6614ee 1191 sv_size(aTHX_ st, NPathLink("RV"), SvRV_const(thing), recurse);
924d9c4e 1192 else if (SvIsCOW_shared_hash(thing))
fc6614ee 1193 hek_size(aTHX_ st, SvSHARED_HEK_FROM_PV(SvPVX(thing)), 1, NPathLink("SvSHARED_HEK_FROM_PV"));
267703fd 1194 else
c07e8ef8 1195 ADD_SIZE(st, "SvLEN", SvLEN(thing));
267703fd 1196
1197 if(SvOOK(thing)) {
95dc1714 1198 STRLEN len;
1199 SvOOK_offset(thing, len);
c07e8ef8 1200 ADD_SIZE(st, "SvOOK", len);
ebb2c5b9 1201 }
9fc9ab86 1202 TAG;break;
5073b933 1203
e98cedbf 1204 }
a5c6bdd7 1205 return;
e98cedbf 1206}
1207
a4efdff3 1208static struct state *
1209new_state(pTHX)
65db36c0 1210{
1211 SV *warn_flag;
a4efdff3 1212 struct state *st;
d9b022a1 1213
a4efdff3 1214 Newxz(st, 1, struct state);
1215 st->go_yell = TRUE;
65db36c0 1216 if (NULL != (warn_flag = perl_get_sv("Devel::Size::warn", FALSE))) {
a4efdff3 1217 st->dangle_whine = st->go_yell = SvIV(warn_flag) ? TRUE : FALSE;
65db36c0 1218 }
1219 if (NULL != (warn_flag = perl_get_sv("Devel::Size::dangle", FALSE))) {
a4efdff3 1220 st->dangle_whine = SvIV(warn_flag) ? TRUE : FALSE;
65db36c0 1221 }
a52ceccd 1222 check_new(st, &PL_sv_undef);
1223 check_new(st, &PL_sv_no);
1224 check_new(st, &PL_sv_yes);
6389ea67 1225#if PERL_VERSION > 8 || (PERL_VERSION == 8 && PERL_SUBVERSION > 0)
1226 check_new(st, &PL_sv_placeholder);
1227#endif
33f2f60c 1228#ifdef PATH_TRACKING
5e486cae 1229if (getenv("M"))
1230st->node_stream = stdout;
1231 if (st->node_stream)
1232 st->add_attr_cb = np_stream_node_path_info;
1233 else
1234 st->add_attr_cb = np_dump_node_path_info;
33f2f60c 1235#endif
a4efdff3 1236 return st;
65db36c0 1237}
1238
9fc9ab86 1239MODULE = Devel::Size PACKAGE = Devel::Size
e98cedbf 1240
fea63ffa 1241PROTOTYPES: DISABLE
1242
eee00145 1243UV
a6ea0805 1244size(orig_thing)
1245 SV *orig_thing
13683e3a 1246ALIAS:
1247 total_size = TOTAL_SIZE_RECURSION
e98cedbf 1248CODE:
1249{
6a9ad7ec 1250 SV *thing = orig_thing;
a4efdff3 1251 struct state *st = new_state(aTHX);
ebb2c5b9 1252
6a9ad7ec 1253 /* If they passed us a reference then dereference it. This is the
1254 only way we can check the sizes of arrays and hashes */
b7621729 1255 if (SvROK(thing)) {
1256 thing = SvRV(thing);
1257 }
33f2f60c 1258
c07e8ef8 1259 sv_size(aTHX_ st, NULL, thing, ix);
eee00145 1260 RETVAL = st->total_size;
a4efdff3 1261 free_state(st);
6a9ad7ec 1262}
1263OUTPUT:
1264 RETVAL
33f2f60c 1265
1266UV
1267perl_size()
1268CODE:
1269{
1270 dNPathNodes(1, NULL);
1271 struct state *st = new_state(aTHX);
012b5f33 1272 NPathPushNode("perl_size", NPtype_NAME); /* provide a root node */
33f2f60c 1273
1274 /* start with PL_defstash to get everything reachable from \%main::
1275 * this seems to include PL_defgv, PL_incgv etc but I've listed them anyway
1276 */
fc6614ee 1277 sv_size(aTHX_ st, NPathLink("PL_defstash"), (SV*)PL_defstash, TOTAL_SIZE_RECURSION);
1278 sv_size(aTHX_ st, NPathLink("PL_defgv"), (SV*)PL_defgv, TOTAL_SIZE_RECURSION);
1279 sv_size(aTHX_ st, NPathLink("PL_incgv"), (SV*)PL_incgv, TOTAL_SIZE_RECURSION);
1280 sv_size(aTHX_ st, NPathLink("PL_rs"), (SV*)PL_rs, TOTAL_SIZE_RECURSION);
1281 sv_size(aTHX_ st, NPathLink("PL_fdpid"), (SV*)PL_fdpid, TOTAL_SIZE_RECURSION);
1282 sv_size(aTHX_ st, NPathLink("PL_modglobal"), (SV*)PL_modglobal, TOTAL_SIZE_RECURSION);
1283 sv_size(aTHX_ st, NPathLink("PL_errors"), (SV*)PL_errors, TOTAL_SIZE_RECURSION);
1284 sv_size(aTHX_ st, NPathLink("PL_stashcache"), (SV*)PL_stashcache, TOTAL_SIZE_RECURSION);
1285 sv_size(aTHX_ st, NPathLink("PL_patchlevel"), (SV*)PL_patchlevel, TOTAL_SIZE_RECURSION);
1286 sv_size(aTHX_ st, NPathLink("PL_apiversion"), (SV*)PL_apiversion, TOTAL_SIZE_RECURSION);
1287 sv_size(aTHX_ st, NPathLink("PL_registered_mros"), (SV*)PL_registered_mros, TOTAL_SIZE_RECURSION);
33f2f60c 1288#ifdef USE_ITHREADS
fc6614ee 1289 sv_size(aTHX_ st, NPathLink("PL_regex_padav"), (SV*)PL_regex_padav, TOTAL_SIZE_RECURSION);
33f2f60c 1290#endif
1291 /* TODO PL_pidstatus */
1292 /* TODO PL_stashpad */
1293
1294 /* in theory we shouldn't have any elements in PL_strtab that haven't been seen yet */
fc6614ee 1295 sv_size(aTHX_ st, NPathLink("PL_strtab"), (SV*)PL_strtab, TOTAL_SIZE_RECURSION);
33f2f60c 1296
1297 /* TODO stacks: cur, main, tmps, mark, scope, save */
1298 /* TODO unused space in arenas */
1299 /* TODO unused space in malloc, for whichever mallocs support it */
5e486cae 1300 /* TODO threads? */
33f2f60c 1301 /* TODO anything missed? */
1302
1303 RETVAL = st->total_size;
1304 free_state(st);
1305}
1306OUTPUT:
1307 RETVAL