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