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