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