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