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