5 * Refactor this to split out D:M code from Devel::Size code.
6 * Start migrating Devel::Size's Size.xs towards the new code.
10 #undef NDEBUG /* XXX */
13 #define PERL_NO_GET_CONTEXT
19 #define DPPP_PL_parser_NO_DUMMY
20 #define NEED_PL_parser
23 #include "refcounted_he.h"
25 /* Not yet in ppport.h */
27 # define CvISXSUB(cv) (CvXSUB(cv) ? TRUE : FALSE)
30 # define SvRV_const(rv) SvRV(rv)
33 # define SvOOK_offset(sv, len) STMT_START { len = SvIVX(sv); } STMT_END
36 # define SvIsCOW(sv) ((SvFLAGS(sv) & (SVf_FAKE | SVf_READONLY)) == \
37 (SVf_FAKE | SVf_READONLY))
39 #ifndef SvIsCOW_shared_hash
40 # define SvIsCOW_shared_hash(sv) (SvIsCOW(sv) && SvLEN(sv) == 0)
42 #ifndef SvSHARED_HEK_FROM_PV
43 # define SvSHARED_HEK_FROM_PV(pvx) \
44 ((struct hek*)(pvx - STRUCT_OFFSET(struct hek, hek_key)))
47 #define MUTABLE_AV(p) ((AV*)p)
50 #define MUTABLE_SV(p) ((SV*)p)
54 # define PL_opargs opargs
55 # define PL_op_name op_name
59 /* "structured exception" handling is a Microsoft extension to C and C++.
60 It's *not* C++ exception handling - C++ exception handling can't capture
61 SEGVs and suchlike, whereas this can. There's no known analagous
62 functionality on other platforms. */
64 # define TRY_TO_CATCH_SEGV __try
65 # define CAUGHT_EXCEPTION __except(EXCEPTION_EXECUTE_HANDLER)
67 # define TRY_TO_CATCH_SEGV if(1)
68 # define CAUGHT_EXCEPTION else
72 # define __attribute__(x)
75 #if 0 && defined(DEBUGGING)
76 #define dbg_printf(x) printf x
81 #define TAG /* printf( "# %s(%d)\n", __FILE__, __LINE__ ) */
84 /* The idea is to have a tree structure to store 1 bit per possible pointer
85 address. The lowest 16 bits are stored in a block of 8092 bytes.
86 The blocks are in a 256-way tree, indexed by the reset of the pointer.
87 This can cope with 32 and 64 bit pointers, and any address space layout,
88 without excessive memory needs. The assumption is that your CPU cache
89 works :-) (And that we're not going to bust it) */
92 #define LEAF_BITS (16 - BYTE_BITS)
93 #define LEAF_MASK 0x1FFF
95 typedef struct npath_node_st npath_node_t;
96 struct npath_node_st {
111 /* My hunch (not measured) is that for most architectures pointers will
112 start with 0 bits, hence the start of this array will be hot, and the
113 end unused. So put the flags next to the hot end. */
116 int min_recurse_threshold;
117 /* callback hooks and data */
118 void (*add_attr_cb)(pTHX_ struct state *st, npath_node_t *npath_node, UV attr_type, const char *name, UV value);
119 void (*free_state_cb)(pTHX_ struct state *st);
120 void *state_cb_data; /* free'd by free_state() after free_state_cb() call */
121 /* this stuff wil be moved to state_cb_data later */
123 FILE *node_stream_fh;
124 char *node_stream_name;
127 #define ADD_SIZE(st, leafname, bytes) \
129 NPathAddSizeCb(st, leafname, bytes); \
130 (st)->total_size += (bytes); \
134 #define PATH_TRACKING
137 #define pPATH npath_node_t *NPathArg
139 /* A subtle point here is that dNPathNodes and NPathPushNode leave NP pointing
140 * to the next unused slot (though with prev already filled in)
141 * whereas NPathLink leaves NP unchanged, it just fills in the slot NP points
142 * to and passes that NP value to the function being called.
143 * seqn==0 indicates the node is new (hasn't been output yet)
145 #define dNPathNodes(nodes, prev_np) \
146 npath_node_t name_path_nodes[nodes+1]; /* +1 for NPathLink */ \
147 npath_node_t *NP = &name_path_nodes[0]; \
148 NP->seqn = NP->type = 0; NP->id = Nullch; /* safety/debug */ \
150 #define NPathPushNode(nodeid, nodetype) \
152 NP->type = nodetype; \
154 if(0)fprintf(stderr,"NPathPushNode (%p <-) %p <- [%d %s]\n", NP->prev, NP, nodetype,(char*)nodeid);\
156 NP->id = Nullch; /* safety/debug */ \
159 #define NPathSetNode(nodeid, nodetype) \
160 (NP-1)->id = nodeid; \
161 (NP-1)->type = nodetype; \
162 if(0)fprintf(stderr,"NPathSetNode (%p <-) %p <- [%d %s]\n", (NP-1)->prev, (NP-1), nodetype,(char*)nodeid);\
164 #define NPathPopNode \
167 /* dNPathUseParent points NP directly the the parents' name_path_nodes array
168 * So the function can only safely call ADD_*() but not NPathLink, unless the
169 * caller has spare nodes in its name_path_nodes.
171 #define dNPathUseParent(prev_np) npath_node_t *NP = (((prev_np+1)->prev = prev_np), prev_np+1)
173 #define NPtype_NAME 0x01
174 #define NPtype_LINK 0x02
175 #define NPtype_SV 0x03
176 #define NPtype_MAGIC 0x04
177 #define NPtype_OP 0x05
179 /* XXX these should probably be generalized into flag bits */
180 #define NPattr_LEAFSIZE 0x00
181 #define NPattr_NAME 0x01
182 #define NPattr_PADFAKE 0x02
183 #define NPattr_PADNAME 0x03
184 #define NPattr_PADTMP 0x04
185 #define NPattr_NOTE 0x05
187 #define _ADD_ATTR_NP(st, attr_type, attr_name, attr_value, np) \
189 if (st->add_attr_cb) { \
190 st->add_attr_cb(aTHX_ st, np, attr_type, attr_name, attr_value); \
194 #define ADD_ATTR(st, attr_type, attr_name, attr_value) \
195 _ADD_ATTR_NP(st, attr_type, attr_name, attr_value, NP-1)
197 #define ADD_LINK_ATTR(st, attr_type, attr_name, attr_value) \
199 if (st->add_attr_cb) assert(NP->seqn); \
200 _ADD_ATTR_NP(st, attr_type, attr_name, attr_value, NP); \
203 #define _NPathLink(np, nid, ntype) (((np)->id=nid), ((np)->type=ntype), ((np)->seqn=0))
204 #define NPathLink(nid) (_NPathLink(NP, nid, NPtype_LINK), NP)
205 /* add a link and a name node to the path - a special case for op_size */
206 #define NPathLinkAndNode(nid, nid2) (_NPathLink(NP, nid, NPtype_LINK), _NPathLink(NP+1, nid2, NPtype_NAME), ((NP+1)->prev=NP), (NP+1))
207 #define NPathOpLink (NPathArg)
208 #define NPathAddSizeCb(st, name, bytes) \
210 if (st->add_attr_cb) { \
211 st->add_attr_cb(aTHX_ st, NP-1, NPattr_LEAFSIZE, (name), (bytes)); \
217 #define NPathAddSizeCb(st, name, bytes)
218 #define pPATH void *npath_dummy /* XXX ideally remove */
219 #define dNPathNodes(nodes, prev_np) dNOOP
220 #define NPathLink(nodeid, nodetype) NULL
221 #define NPathOpLink NULL
222 #define ADD_ATTR(st, attr_type, attr_name, attr_value) NOOP
224 #endif /* PATH_TRACKING */
231 static const char *svtypenames[SVt_LAST] = {
233 "NULL", "IV", "NV", "RV", "PV", "PVIV", "PVNV", "PVMG", "PVBM", "PVLV", "PVAV", "PVHV", "PVCV", "PVGV", "PVFM", "PVIO",
234 #elif PERL_VERSION == 10 && PERL_SUBVERSION == 0
235 "NULL", "BIND", "IV", "NV", "RV", "PV", "PVIV", "PVNV", "PVMG", "PVGV", "PVLV", "PVAV", "PVHV", "PVCV", "PVFM", "PVIO",
236 #elif PERL_VERSION == 10 && PERL_SUBVERSION == 1
237 "NULL", "BIND", "IV", "NV", "RV", "PV", "PVIV", "PVNV", "PVMG", "PVGV", "PVLV", "PVAV", "PVHV", "PVCV", "PVFM", "PVIO",
238 #elif PERL_VERSION < 13
239 "NULL", "BIND", "IV", "NV", "PV", "PVIV", "PVNV", "PVMG", "REGEXP", "PVGV", "PVLV", "PVAV", "PVHV", "PVCV", "PVFM", "PVIO",
241 "NULL", "BIND", "IV", "NV", "PV", "PVIV", "PVNV", "PVMG", "REGEXP", "PVGV", "PVLV", "PVAV", "PVHV", "PVCV", "PVFM", "PVIO",
246 gettimeofday_nv(void)
248 #ifdef HAS_GETTIMEOFDAY
250 gettimeofday(&when, (struct timezone *) 0);
251 return when.tv_sec + (when.tv_usec / 1000000.0);
255 (*u2time)(aTHX_ &time_of_day);
256 return time_of_day[0] + (time_of_day[1] / 1000000.0);
264 np_print_node_name(pTHX_ FILE *fp, npath_node_t *npath_node)
266 switch (npath_node->type) {
267 case NPtype_SV: { /* id is pointer to the SV sv_size was called on */
268 const SV *sv = (SV*)npath_node->id;
269 int type = SvTYPE(sv);
270 const char *typename = (type == SVt_IV && SvROK(sv)) ? "RV" : svtypenames[type];
271 fprintf(fp, "SV(%s)", typename);
272 switch(type) { /* add some useful details */
273 case SVt_PVAV: fprintf(fp, " fill=%d/%ld", av_len((AV*)sv), AvMAX((AV*)sv)); break;
274 case SVt_PVHV: fprintf(fp, " fill=%ld/%ld", HvFILL((HV*)sv), HvMAX((HV*)sv)); break;
278 case NPtype_OP: { /* id is pointer to the OP op_size was called on */
279 const OP *op = (OP*)npath_node->id;
280 fprintf(fp, "OP(%s)", OP_NAME(op));
283 case NPtype_MAGIC: { /* id is pointer to the MAGIC struct */
284 MAGIC *magic_pointer = (MAGIC*)npath_node->id;
285 /* XXX it would be nice if we could reuse mg_names.c [sigh] */
286 fprintf(fp, "MAGIC(%c)", magic_pointer->mg_type ? magic_pointer->mg_type : '0');
290 fprintf(fp, "%s", (const char *)npath_node->id);
293 fprintf(fp, "%s", (const char *)npath_node->id);
295 default: /* assume id is a string pointer */
296 fprintf(fp, "UNKNOWN(%d,%p)", npath_node->type, npath_node->id);
303 np_dump_indent(int depth) {
305 fprintf(stderr, ": ");
309 np_walk_new_nodes(pTHX_ struct state *st,
310 npath_node_t *npath_node,
311 npath_node_t *npath_node_deeper,
312 int (*cb)(pTHX_ struct state *st, npath_node_t *npath_node, npath_node_t *npath_node_deeper))
314 if (npath_node->seqn) /* node already output */
317 if (npath_node->prev) {
318 np_walk_new_nodes(aTHX_ st, npath_node->prev, npath_node, cb); /* recurse */
319 npath_node->depth = npath_node->prev->depth + 1;
321 else npath_node->depth = 0;
322 npath_node->seqn = ++st->seqn;
325 if (cb(aTHX_ st, npath_node, npath_node_deeper)) {
326 /* ignore this node */
327 assert(npath_node->prev);
328 assert(npath_node->depth);
329 assert(npath_node_deeper);
331 npath_node->seqn = --st->seqn;
332 npath_node_deeper->prev = npath_node->prev;
340 np_dump_formatted_node(pTHX_ struct state *st, npath_node_t *npath_node, npath_node_t *npath_node_deeper) {
342 PERL_UNUSED_ARG(npath_node_deeper);
343 if (0 && npath_node->type == NPtype_LINK)
345 np_dump_indent(npath_node->depth);
346 np_print_node_name(aTHX_ stderr, npath_node);
347 if (npath_node->type == NPtype_LINK)
348 fprintf(stderr, "->"); /* cosmetic */
349 fprintf(stderr, "\t\t[#%ld @%u] ", npath_node->seqn, npath_node->depth);
350 fprintf(stderr, "\n");
355 np_dump_node_path_info(pTHX_ struct state *st, npath_node_t *npath_node, UV attr_type, const char *attr_name, UV attr_value)
357 if (attr_type == NPattr_LEAFSIZE && !attr_value)
358 return; /* ignore zero sized leaf items */
359 np_walk_new_nodes(aTHX_ st, npath_node, NULL, np_dump_formatted_node);
360 np_dump_indent(npath_node->depth+1);
362 case NPattr_LEAFSIZE:
363 fprintf(stderr, "+%ld %s =%ld", attr_value, attr_name, attr_value+st->total_size);
366 fprintf(stderr, "~NAMED('%s') %lu", attr_name, attr_value);
369 fprintf(stderr, "~note %s %lu", attr_name, attr_value);
374 fprintf(stderr, "~pad%lu %s %lu", attr_type, attr_name, attr_value);
377 fprintf(stderr, "~??? %s %lu", attr_name, attr_value);
380 fprintf(stderr, "\n");
384 np_stream_formatted_node(pTHX_ struct state *st, npath_node_t *npath_node, npath_node_t *npath_node_deeper) {
385 PERL_UNUSED_ARG(npath_node_deeper);
386 fprintf(st->node_stream_fh, "-%u %lu %u ",
387 npath_node->type, npath_node->seqn, (unsigned)npath_node->depth
389 np_print_node_name(aTHX_ st->node_stream_fh, npath_node);
390 fprintf(st->node_stream_fh, "\n");
395 np_stream_node_path_info(pTHX_ struct state *st, npath_node_t *npath_node, UV attr_type, const char *attr_name, UV attr_value)
397 if (!attr_type && !attr_value)
398 return; /* ignore zero sized leaf items */
399 np_walk_new_nodes(aTHX_ st, npath_node, NULL, np_stream_formatted_node);
400 if (attr_type) { /* Attribute type, name and value */
401 fprintf(st->node_stream_fh, "%lu %lu ", attr_type, npath_node->seqn);
403 else { /* Leaf name and memory size */
404 fprintf(st->node_stream_fh, "L %lu ", npath_node->seqn);
406 fprintf(st->node_stream_fh, "%lu %s\n", attr_value, attr_name);
410 #endif /* PATH_TRACKING */
414 Checks to see if thing is in the bitstring.
415 Returns true or false, and
416 notes thing in the segmented bitstring.
419 check_new(struct state *st, const void *const p) {
420 unsigned int bits = 8 * sizeof(void*);
421 const size_t raw_p = PTR2nat(p);
422 /* This effectively rotates the value right by the number of low always-0
423 bits in an aligned pointer. The assmption is that most (if not all)
424 pointers are aligned, and these will be in the same chain of nodes
425 (and hence hot in the cache) but we can still deal with any unaligned
427 const size_t cooked_p
428 = (raw_p >> ALIGN_BITS) | (raw_p << (bits - ALIGN_BITS));
429 const U8 this_bit = 1 << (cooked_p & 0x7);
433 void **tv_p = (void **) (st->tracking);
435 if (NULL == p) return FALSE;
437 const char c = *(const char *)p;
441 if (st->dangle_whine)
442 warn( "Devel::Size: Encountered invalid pointer: %p\n", p );
448 /* bits now 24 (32 bit pointers) or 56 (64 bit pointers) */
450 /* First level is always present. */
452 i = (unsigned int)((cooked_p >> bits) & 0xFF);
454 Newxz(tv_p[i], 256, void *);
455 tv_p = (void **)(tv_p[i]);
457 } while (bits > LEAF_BITS + BYTE_BITS);
458 /* bits now 16 always */
459 #if !defined(MULTIPLICITY) || PERL_VERSION > 8 || (PERL_VERSION == 8 && PERL_SUBVERSION > 8)
460 /* 5.8.8 and early have an assert() macro that uses Perl_croak, hence needs
461 a my_perl under multiplicity */
464 leaf_p = (U8 **)tv_p;
465 i = (unsigned int)((cooked_p >> bits) & 0xFF);
467 Newxz(leaf_p[i], 1 << LEAF_BITS, U8);
472 i = (unsigned int)((cooked_p >> BYTE_BITS) & LEAF_MASK);
474 if(leaf[i] & this_bit)
482 free_tracking_at(void **tv, int level)
490 free_tracking_at((void **) tv[i], level);
504 free_state(pTHX_ struct state *st)
506 const int top_level = (sizeof(void *) * 8 - LEAF_BITS - BYTE_BITS) / 8;
507 if (st->free_state_cb)
508 st->free_state_cb(aTHX_ st);
509 if (st->state_cb_data)
510 Safefree(st->state_cb_data);
511 free_tracking_at((void **)st->tracking, top_level);
515 /* For now, this is somewhat a compatibility bodge until the plan comes
516 together for fine grained recursion control. total_size() would recurse into
517 hash and array members, whereas sv_size() would not. However, sv_size() is
518 called with CvSTASH() of a CV, which means that if it (also) starts to
519 recurse fully, then the size of any CV now becomes the size of the entire
520 symbol table reachable from it, and potentially the entire symbol table, if
521 any subroutine makes a reference to a global (such as %SIG). The historical
522 implementation of total_size() didn't report "everything", and changing the
523 only available size to "everything" doesn't feel at all useful. */
525 #define NO_RECURSION 0
526 #define SOME_RECURSION 1
527 #define TOTAL_SIZE_RECURSION 2
529 static bool sv_size(pTHX_ struct state *, pPATH, const SV *const, const int recurse);
545 , OPc_CONDOP /* 12 */
554 cc_opclass(const OP * const o)
560 return (o->op_flags & OPf_KIDS) ? OPc_UNOP : OPc_BASEOP;
562 if (o->op_type == OP_SASSIGN)
563 return ((o->op_private & OPpASSIGN_BACKWARDS) ? OPc_UNOP : OPc_BINOP);
566 if (o->op_type == OP_GV || o->op_type == OP_GVSV || o->op_type == OP_AELEMFAST)
570 if ((o->op_type == OP_TRANS)) {
574 switch (PL_opargs[o->op_type] & OA_CLASS_MASK) {
606 #ifdef OA_PVOP_OR_SVOP
607 case OA_PVOP_OR_SVOP: TAG;
609 * Character translations (tr///) are usually a PVOP, keeping a
610 * pointer to a table of shorts used to look up translations.
611 * Under utf8, however, a simple table isn't practical; instead,
612 * the OP is an SVOP, and the SV is a reference to a swash
613 * (i.e., an RV pointing to an HV).
615 return (o->op_private & (OPpTRANS_TO_UTF|OPpTRANS_FROM_UTF))
616 ? OPc_SVOP : OPc_PVOP;
625 case OA_BASEOP_OR_UNOP: TAG;
627 * UNI(OP_foo) in toke.c returns token UNI or FUNC1 depending on
628 * whether parens were seen. perly.y uses OPf_SPECIAL to
629 * signal whether a BASEOP had empty parens or none.
630 * Some other UNOPs are created later, though, so the best
631 * test is OPf_KIDS, which is set in newUNOP.
633 return (o->op_flags & OPf_KIDS) ? OPc_UNOP : OPc_BASEOP;
635 case OA_FILESTATOP: TAG;
637 * The file stat OPs are created via UNI(OP_foo) in toke.c but use
638 * the OPf_REF flag to distinguish between OP types instead of the
639 * usual OPf_SPECIAL flag. As usual, if OPf_KIDS is set, then we
640 * return OPc_UNOP so that walkoptree can find our children. If
641 * OPf_KIDS is not set then we check OPf_REF. Without OPf_REF set
642 * (no argument to the operator) it's an OP; with OPf_REF set it's
643 * an SVOP (and op_sv is the GV for the filehandle argument).
645 return ((o->op_flags & OPf_KIDS) ? OPc_UNOP :
647 (o->op_flags & OPf_REF) ? OPc_PADOP : OPc_BASEOP);
649 (o->op_flags & OPf_REF) ? OPc_SVOP : OPc_BASEOP);
651 case OA_LOOPEXOP: TAG;
653 * next, last, redo, dump and goto use OPf_SPECIAL to indicate that a
654 * label was omitted (in which case it's a BASEOP) or else a term was
655 * seen. In this last case, all except goto are definitely PVOP but
656 * goto is either a PVOP (with an ordinary constant label), an UNOP
657 * with OPf_STACKED (with a non-constant non-sub) or an UNOP for
658 * OP_REFGEN (with goto &sub) in which case OPf_STACKED also seems to
661 if (o->op_flags & OPf_STACKED)
663 else if (o->op_flags & OPf_SPECIAL)
673 warn("Devel::Size: Can't determine class of operator %s, assuming BASEOP\n",
674 PL_op_name[o->op_type]);
680 /* Figure out how much magic is attached to the SV and return the
683 magic_size(pTHX_ const SV * const thing, struct state *st, pPATH) {
684 dNPathNodes(1, NPathArg);
685 MAGIC *magic_pointer = SvMAGIC(thing); /* caller ensures thing is SvMAGICAL */
687 /* push a dummy node for NPathSetNode to update inside the while loop */
688 NPathPushNode("dummy", NPtype_NAME);
690 /* Have we seen the magic pointer? (NULL has always been seen before) */
691 while (check_new(st, magic_pointer)) {
693 NPathSetNode(magic_pointer, NPtype_MAGIC);
695 ADD_SIZE(st, "mg", sizeof(MAGIC));
696 /* magic vtables aren't freed when magic is freed, so don't count them.
697 (They are static structures. Anything that assumes otherwise is buggy.)
702 /* XXX only chase mg_obj if mg->mg_flags & MGf_REFCOUNTED ? */
703 sv_size(aTHX_ st, NPathLink("mg_obj"), magic_pointer->mg_obj, TOTAL_SIZE_RECURSION);
704 if (magic_pointer->mg_len == HEf_SVKEY) {
705 sv_size(aTHX_ st, NPathLink("mg_ptr"), (SV *)magic_pointer->mg_ptr, TOTAL_SIZE_RECURSION);
707 #if defined(PERL_MAGIC_utf8) && defined (PERL_MAGIC_UTF8_CACHESIZE)
708 else if (magic_pointer->mg_type == PERL_MAGIC_utf8) {
709 if (check_new(st, magic_pointer->mg_ptr)) {
710 ADD_SIZE(st, "PERL_MAGIC_utf8", PERL_MAGIC_UTF8_CACHESIZE * 2 * sizeof(STRLEN));
714 /* XXX also handle mg->mg_type == PERL_MAGIC_utf8 ? */
715 else if (magic_pointer->mg_len > 0) {
716 if(0)do_magic_dump(0, Perl_debug_log, magic_pointer, 0, 0, FALSE, 0);
717 if (check_new(st, magic_pointer->mg_ptr)) {
718 ADD_SIZE(st, "mg_len", magic_pointer->mg_len);
722 /* Get the next in the chain */
723 magic_pointer = magic_pointer->mg_moremagic;
726 if (st->dangle_whine)
727 warn( "Devel::Size: Encountered bad magic at: %p\n", magic_pointer );
732 #define check_new_and_strlen(st, p, ppath) S_check_new_and_strlen(aTHX_ st, p, ppath)
734 S_check_new_and_strlen(pTHX_ struct state *st, const char *const p, pPATH) {
735 dNPathNodes(1, NPathArg->prev);
736 if(check_new(st, p)) {
737 NPathPushNode(NPathArg->id, NPtype_NAME);
738 ADD_SIZE(st, NPathArg->id, 1 + strlen(p));
743 regex_size(pTHX_ const REGEXP * const baseregex, struct state *st, pPATH) {
744 dNPathNodes(1, NPathArg);
745 if(!check_new(st, baseregex))
747 NPathPushNode("regex_size", NPtype_NAME);
748 ADD_SIZE(st, "REGEXP", sizeof(REGEXP));
749 #if (PERL_VERSION < 11)
750 /* Note the size of the paren offset thing */
751 ADD_SIZE(st, "nparens", sizeof(I32) * baseregex->nparens * 2);
752 ADD_SIZE(st, "precomp", strlen(baseregex->precomp));
754 ADD_SIZE(st, "regexp", sizeof(struct regexp));
755 ADD_SIZE(st, "nparens", sizeof(I32) * SvANY(baseregex)->nparens * 2);
756 /*ADD_SIZE(st, strlen(SvANY(baseregex)->subbeg));*/
758 if (st->go_yell && !st->regex_whine) {
759 carp("Devel::Size: Calculated sizes for compiled regexes are incompatible, and probably always will be");
765 hek_size(pTHX_ struct state *st, HEK *hek, U32 shared, pPATH)
767 dNPathNodes(1, NPathArg);
769 /* Hash keys can be shared. Have we seen this before? */
770 if (!check_new(st, hek))
772 NPathPushNode("hek", NPtype_NAME);
773 ADD_SIZE(st, "hek_len", HEK_BASESIZE + hek->hek_len
775 + 1 /* No hash key flags prior to 5.8.0 */
781 #if PERL_VERSION < 10
782 ADD_SIZE(st, "he", sizeof(struct he));
784 ADD_SIZE(st, "shared_he", STRUCT_OFFSET(struct shared_he, shared_he_hek));
790 #if (PERL_BCDVERSION >= 0x5009004)
792 refcounted_he_size(pTHX_ struct state *st, struct refcounted_he *he, pPATH)
794 dNPathNodes(1, NPathArg);
795 if (!check_new(st, he))
797 NPathPushNode("refcounted_he_size", NPtype_NAME);
798 ADD_SIZE(st, "refcounted_he", sizeof(struct refcounted_he));
801 ADD_SIZE(st, "refcounted_he_data", NPtype_NAME);
803 hek_size(aTHX_ st, he->refcounted_he_hek, 0, NPathLink("refcounted_he_hek"));
806 if (he->refcounted_he_next)
807 refcounted_he_size(aTHX_ st, he->refcounted_he_next, NPathLink("refcounted_he_next"));
811 static void op_size_class(pTHX_ const OP * const baseop, opclass op_class, bool skip_op_struct, struct state *st, pPATH);
814 op_size(pTHX_ const OP * const baseop, struct state *st, pPATH)
816 op_size_class(aTHX_ baseop, cc_opclass(baseop), 0, st, NPathArg);
820 op_size_class(pTHX_ const OP * const baseop, opclass op_class, bool skip_op_struct, struct state *st, pPATH)
822 /* op_size recurses to follow the chain of opcodes. For the node path we
823 * don't want the chain to be 'nested' in the path so we use dNPathUseParent().
824 * Also, to avoid a link-to-a-link the caller should use NPathLinkAndNode()
825 * instead of NPathLink().
827 dNPathUseParent(NPathArg);
831 if(!check_new(st, baseop))
834 op_size(aTHX_ baseop->op_next, st, NPathOpLink);
836 madprop_size(aTHX_ st, NPathOpLink, baseop->op_madprop);
840 case OPc_BASEOP: TAG;
842 ADD_SIZE(st, "op", sizeof(struct op));
846 ADD_SIZE(st, "unop", sizeof(struct unop));
847 op_size(aTHX_ ((UNOP *)baseop)->op_first, st, NPathOpLink);
851 ADD_SIZE(st, "binop", sizeof(struct binop));
852 op_size(aTHX_ ((BINOP *)baseop)->op_first, st, NPathOpLink);
853 op_size(aTHX_ ((BINOP *)baseop)->op_last, st, NPathOpLink);
857 ADD_SIZE(st, "logop", sizeof(struct logop));
858 op_size(aTHX_ ((BINOP *)baseop)->op_first, st, NPathOpLink);
859 op_size(aTHX_ ((LOGOP *)baseop)->op_other, st, NPathOpLink);
862 case OPc_CONDOP: TAG;
864 ADD_SIZE(st, "condop", sizeof(struct condop));
865 op_size(aTHX_ ((BINOP *)baseop)->op_first, st, NPathOpLink);
866 op_size(aTHX_ ((CONDOP *)baseop)->op_true, st, NPathOpLink);
867 op_size(aTHX_ ((CONDOP *)baseop)->op_false, st, NPathOpLink);
870 case OPc_LISTOP: TAG;
872 ADD_SIZE(st, "listop", sizeof(struct listop));
873 op_size(aTHX_ ((LISTOP *)baseop)->op_first, st, NPathOpLink);
874 op_size(aTHX_ ((LISTOP *)baseop)->op_last, st, NPathOpLink);
878 ADD_SIZE(st, "pmop", sizeof(struct pmop));
879 op_size(aTHX_ ((PMOP *)baseop)->op_first, st, NPathOpLink);
880 op_size(aTHX_ ((PMOP *)baseop)->op_last, st, NPathOpLink);
881 #if PERL_VERSION < 9 || (PERL_VERSION == 9 && PERL_SUBVERSION < 5)
882 op_size(aTHX_ ((PMOP *)baseop)->op_pmreplroot, st, NPathOpLink);
883 op_size(aTHX_ ((PMOP *)baseop)->op_pmreplstart, st, NPathOpLink);
885 /* This is defined away in perl 5.8.x, but it is in there for
888 regex_size(aTHX_ PM_GETRE((PMOP *)baseop), st, NPathLink("PM_GETRE"));
890 regex_size(aTHX_ ((PMOP *)baseop)->op_pmregexp, st, NPathLink("op_pmregexp"));
895 ADD_SIZE(st, "svop", sizeof(struct svop));
896 if (!(baseop->op_type == OP_AELEMFAST
897 && baseop->op_flags & OPf_SPECIAL)) {
898 /* not an OP_PADAV replacement */
899 sv_size(aTHX_ st, NPathLink("SVOP"), ((SVOP *)baseop)->op_sv, SOME_RECURSION);
905 ADD_SIZE(st, "padop", sizeof(struct padop));
911 ADD_SIZE(st, "gvop", sizeof(struct gvop));
912 sv_size(aTHX_ st, NPathLink("GVOP"), ((GVOP *)baseop)->op_gv, SOME_RECURSION);
916 check_new_and_strlen(st, ((PVOP *)baseop)->op_pv, NPathLink("op_pv"));
920 ADD_SIZE(st, "loop", sizeof(struct loop));
921 op_size(aTHX_ ((LOOP *)baseop)->op_first, st, NPathOpLink);
922 op_size(aTHX_ ((LOOP *)baseop)->op_last, st, NPathOpLink);
923 op_size(aTHX_ ((LOOP *)baseop)->op_redoop, st, NPathOpLink);
924 op_size(aTHX_ ((LOOP *)baseop)->op_nextop, st, NPathOpLink);
925 op_size(aTHX_ ((LOOP *)baseop)->op_lastop, st, NPathOpLink);
930 basecop = (COP *)baseop;
932 ADD_SIZE(st, "cop", sizeof(struct cop));
934 /* Change 33656 by nicholas@mouse-mill on 2008/04/07 11:29:51
935 Eliminate cop_label from struct cop by storing a label as the first
936 entry in the hints hash. Most statements don't have labels, so this
937 will save memory. Not sure how much.
938 The check below will be incorrect fail on bleadperls
939 before 5.11 @33656, but later than 5.10, producing slightly too
940 small memory sizes on these Perls. */
941 #if (PERL_VERSION < 11)
942 check_new_and_strlen(st, basecop->cop_label, NPathLink("cop_label"));
945 check_new_and_strlen(st, basecop->cop_file, NPathLink("cop_file"));
946 /*check_new_and_strlen(st, basecop->cop_stashpv, NPathLink("cop_stashpv")); XXX */
948 if (SvREFCNT(basecop->cop_stash) == 1) /* XXX hack? */
949 sv_size(aTHX_ st, NPathLink("cop_stash"), (SV *)basecop->cop_stash, SOME_RECURSION);
950 sv_size(aTHX_ st, NPathLink("cop_filegv"), (SV *)basecop->cop_filegv, SOME_RECURSION);
953 #if (PERL_BCDVERSION >= 0x5009004)
954 # if (PERL_BCDVERSION < 0x5013007)
955 # define COPHH struct refcounted_he
957 # ifndef CopHINTHASH_get
958 # define CopHINTHASH_get(c) ((COPHH*)((c)->cop_hints_hash))
960 refcounted_he_size(aTHX_ st, CopHINTHASH_get(basecop), NPathLink("cop_hints_hash"));
969 if (st->dangle_whine)
970 warn( "Devel::Size: Encountered dangling pointer in opcode at: %p\n", baseop );
974 #if PERL_VERSION < 8 || PERL_SUBVERSION < 9 /* XXX plain || seems like a bug */
979 # define MAYBE_PURIFY(normal, pure) (pure)
980 # define MAYBE_OFFSET(struct_name, member) 0
982 # define MAYBE_PURIFY(normal, pure) (normal)
983 # define MAYBE_OFFSET(struct_name, member) STRUCT_OFFSET(struct_name, member)
986 const U8 body_sizes[SVt_LAST] = {
989 MAYBE_PURIFY(sizeof(IV), sizeof(XPVIV)), /* SVt_IV */
990 MAYBE_PURIFY(sizeof(NV), sizeof(XPVNV)), /* SVt_NV */
991 sizeof(XRV), /* SVt_RV */
992 sizeof(XPV), /* SVt_PV */
993 sizeof(XPVIV), /* SVt_PVIV */
994 sizeof(XPVNV), /* SVt_PVNV */
995 sizeof(XPVMG), /* SVt_PVMG */
996 sizeof(XPVBM), /* SVt_PVBM */
997 sizeof(XPVLV), /* SVt_PVLV */
998 sizeof(XPVAV), /* SVt_PVAV */
999 sizeof(XPVHV), /* SVt_PVHV */
1000 sizeof(XPVCV), /* SVt_PVCV */
1001 sizeof(XPVGV), /* SVt_PVGV */
1002 sizeof(XPVFM), /* SVt_PVFM */
1003 sizeof(XPVIO) /* SVt_PVIO */
1004 #elif PERL_VERSION == 10 && PERL_SUBVERSION == 0
1008 MAYBE_PURIFY(sizeof(NV), sizeof(XPVNV)), /* SVt_NV */
1010 MAYBE_PURIFY(sizeof(xpv_allocated), sizeof(XPV)), /* SVt_PV */
1011 MAYBE_PURIFY(sizeof(xpviv_allocated), sizeof(XPVIV)),/* SVt_PVIV */
1012 sizeof(XPVNV), /* SVt_PVNV */
1013 sizeof(XPVMG), /* SVt_PVMG */
1014 sizeof(XPVGV), /* SVt_PVGV */
1015 sizeof(XPVLV), /* SVt_PVLV */
1016 MAYBE_PURIFY(sizeof(xpvav_allocated), sizeof(XPVAV)),/* SVt_PVAV */
1017 MAYBE_PURIFY(sizeof(xpvhv_allocated), sizeof(XPVHV)),/* SVt_PVHV */
1018 MAYBE_PURIFY(sizeof(xpvcv_allocated), sizeof(XPVCV)),/* SVt_PVCV */
1019 MAYBE_PURIFY(sizeof(xpvfm_allocated), sizeof(XPVFM)),/* SVt_PVFM */
1020 sizeof(XPVIO), /* SVt_PVIO */
1021 #elif PERL_VERSION == 10 && PERL_SUBVERSION == 1
1025 MAYBE_PURIFY(sizeof(NV), sizeof(XPVNV)), /* SVt_NV */
1027 sizeof(XPV) - MAYBE_OFFSET(XPV, xpv_cur), /* SVt_PV */
1028 sizeof(XPVIV) - MAYBE_OFFSET(XPV, xpv_cur), /* SVt_PVIV */
1029 sizeof(XPVNV), /* SVt_PVNV */
1030 sizeof(XPVMG), /* SVt_PVMG */
1031 sizeof(XPVGV), /* SVt_PVGV */
1032 sizeof(XPVLV), /* SVt_PVLV */
1033 sizeof(XPVAV) - MAYBE_OFFSET(XPVAV, xav_fill), /* SVt_PVAV */
1034 sizeof(XPVHV) - MAYBE_OFFSET(XPVHV, xhv_fill), /* SVt_PVHV */
1035 sizeof(XPVCV) - MAYBE_OFFSET(XPVCV, xpv_cur), /* SVt_PVCV */
1036 sizeof(XPVFM) - MAYBE_OFFSET(XPVFM, xpv_cur), /* SVt_PVFM */
1037 sizeof(XPVIO) /* SVt_PVIO */
1038 #elif PERL_VERSION < 13
1042 MAYBE_PURIFY(sizeof(NV), sizeof(XPVNV)), /* SVt_NV */
1043 sizeof(XPV) - MAYBE_OFFSET(XPV, xpv_cur), /* SVt_PV */
1044 sizeof(XPVIV) - MAYBE_OFFSET(XPV, xpv_cur), /* SVt_PVIV */
1045 sizeof(XPVNV), /* SVt_PVNV */
1046 sizeof(XPVMG), /* SVt_PVMG */
1047 sizeof(regexp) - MAYBE_OFFSET(regexp, xpv_cur), /* SVt_REGEXP */
1048 sizeof(XPVGV), /* SVt_PVGV */
1049 sizeof(XPVLV), /* SVt_PVLV */
1050 sizeof(XPVAV) - MAYBE_OFFSET(XPVAV, xav_fill), /* SVt_PVAV */
1051 sizeof(XPVHV) - MAYBE_OFFSET(XPVHV, xhv_fill), /* SVt_PVHV */
1052 sizeof(XPVCV) - MAYBE_OFFSET(XPVCV, xpv_cur), /* SVt_PVCV */
1053 sizeof(XPVFM) - MAYBE_OFFSET(XPVFM, xpv_cur), /* SVt_PVFM */
1054 sizeof(XPVIO) /* SVt_PVIO */
1059 MAYBE_PURIFY(sizeof(NV), sizeof(XPVNV)), /* SVt_NV */
1060 sizeof(XPV) - MAYBE_OFFSET(XPV, xpv_cur), /* SVt_PV */
1061 sizeof(XPVIV) - MAYBE_OFFSET(XPV, xpv_cur), /* SVt_PVIV */
1062 sizeof(XPVNV) - MAYBE_OFFSET(XPV, xpv_cur), /* SVt_PVNV */
1063 sizeof(XPVMG), /* SVt_PVMG */
1064 sizeof(regexp), /* SVt_REGEXP */
1065 sizeof(XPVGV), /* SVt_PVGV */
1066 sizeof(XPVLV), /* SVt_PVLV */
1067 sizeof(XPVAV), /* SVt_PVAV */
1068 sizeof(XPVHV), /* SVt_PVHV */
1069 sizeof(XPVCV), /* SVt_PVCV */
1070 sizeof(XPVFM), /* SVt_PVFM */
1071 sizeof(XPVIO) /* SVt_PVIO */
1076 /* based on Perl_do_dump_pad() - wraps sv_size and adds ADD_ATTR calls for the pad names */
1078 padlist_size(pTHX_ struct state *const st, pPATH, PADLIST *padlist,
1081 dNPathUseParent(NPathArg);
1088 if( 0 && !check_new(st, padlist))
1091 pad_name = MUTABLE_AV(*av_fetch(MUTABLE_AV(padlist), 0, FALSE));
1092 pname = AvARRAY(pad_name);
1094 for (ix = 1; ix <= AvFILLp(pad_name); ix++) {
1095 const SV *namesv = pname[ix];
1096 if (namesv && namesv == &PL_sv_undef) {
1100 /* SvFAKE: On a pad name SV, that slot in the frame AV is a REFCNT'ed reference to a lexical from "outside" */
1102 ADD_ATTR(st, NPattr_PADFAKE, SvPVX_const(namesv), ix);
1104 ADD_ATTR(st, NPattr_PADNAME, SvPVX_const(namesv), ix);
1107 ADD_ATTR(st, NPattr_PADTMP, "SVs_PADTMP", ix);
1111 sv_size(aTHX_ st, NPathArg, (SV*)padlist, recurse);
1116 sv_size(pTHX_ struct state *const st, pPATH, const SV * const orig_thing,
1117 const int recurse) {
1118 const SV *thing = orig_thing;
1119 dNPathNodes(3, NPathArg);
1122 if(!check_new(st, orig_thing))
1125 type = SvTYPE(thing);
1126 if (type > SVt_LAST) {
1127 warn("Devel::Size: Unknown variable type: %d encountered\n", type);
1130 NPathPushNode(thing, NPtype_SV);
1131 ADD_SIZE(st, "sv_head", sizeof(SV));
1132 ADD_SIZE(st, "sv_body", body_sizes[type]);
1135 #if (PERL_VERSION < 11)
1136 /* Is it a reference? */
1141 if(recurse && SvROK(thing)) /* XXX maybe don't follow weakrefs */
1142 sv_size(aTHX_ st, (SvWEAKREF(thing) ? NPathLink("weakRV") : NPathLink("RV")), SvRV_const(thing), recurse);
1146 /* Is there anything in the array? */
1147 if (AvMAX(thing) != -1) {
1148 /* an array with 10 slots has AvMax() set to 9 - te 2007-04-22 */
1149 ADD_SIZE(st, "av_max", sizeof(SV *) * (AvMAX(thing) + 1));
1150 ADD_ATTR(st, NPattr_NOTE, "av_len", av_len((AV*)thing));
1151 dbg_printf(("total_size: %li AvMAX: %li av_len: $i\n", st->total_size, AvMAX(thing), av_len((AV*)thing)));
1153 if (recurse >= st->min_recurse_threshold) {
1154 SSize_t i = AvFILLp(thing) + 1;
1157 if (sv_size(aTHX_ st, NPathLink("AVelem"), AvARRAY(thing)[i], recurse))
1158 ADD_LINK_ATTR(st, NPattr_NOTE, "i", i);
1162 /* Add in the bits on the other side of the beginning */
1164 dbg_printf(("total_size %li, sizeof(SV *) %li, AvARRAY(thing) %li, AvALLOC(thing)%li , sizeof(ptr) %li \n",
1165 st->total_size, sizeof(SV*), AvARRAY(thing), AvALLOC(thing), sizeof( thing )));
1167 /* under Perl 5.8.8 64bit threading, AvARRAY(thing) was a pointer while AvALLOC was 0,
1168 resulting in grossly overstated sized for arrays. Technically, this shouldn't happen... */
1169 if (AvALLOC(thing) != 0) {
1170 ADD_SIZE(st, "AvALLOC", (sizeof(SV *) * (AvARRAY(thing) - AvALLOC(thing))));
1172 #if (PERL_VERSION < 9)
1173 /* Is there something hanging off the arylen element?
1174 Post 5.9.something this is stored in magic, so will be found there,
1175 and Perl_av_arylen_p() takes a non-const AV*, hence compilers rightly
1176 complain about AvARYLEN() passing thing to it. */
1177 sv_size(aTHX_ st, NPathLink("ARYLEN"), AvARYLEN(thing), recurse);
1182 /* Now the array of buckets */
1184 if (HvENAME(thing)) { ADD_ATTR(st, NPattr_NAME, HvENAME(thing), 0); }
1186 if (HvNAME(thing)) { ADD_ATTR(st, NPattr_NAME, HvNAME(thing), 0); }
1188 ADD_SIZE(st, "hv_max", (sizeof(HE *) * (HvMAX(thing) + 1)));
1189 /* Now walk the bucket chain */
1190 if (HvARRAY(thing)) {
1194 for (cur_bucket = 0; cur_bucket <= HvMAX(thing); cur_bucket++) {
1195 cur_entry = *(HvARRAY(thing) + cur_bucket);
1197 NPathPushNode("he", NPtype_LINK);
1198 NPathPushNode("he+hek", NPtype_NAME);
1199 ADD_SIZE(st, "he", sizeof(HE));
1200 hek_size(aTHX_ st, cur_entry->hent_hek, HvSHAREKEYS(thing), NPathLink("hent_hek"));
1201 if (recurse >= st->min_recurse_threshold) {
1202 if (orig_thing == (SV*)PL_strtab) {
1203 /* For PL_strtab the HeVAL is used as a refcnt */
1204 ADD_SIZE(st, "shared_hek", HeKLEN(cur_entry));
1207 /* I've seen a PL_strtab HeVAL == 0xC and 0x40C etc
1208 * just running perl -Mblib -Mstrict -MDevel::Size=:all -MCarp -e 'warn perl_size()'
1209 * but it seemed like a corruption - it would change come and go with irrelevant code changes.
1210 * so we protect against that here, but I'd like to know the cause.
1212 if (PTR2UV(HeVAL(cur_entry)) > 0xFFF)
1213 sv_size(aTHX_ st, NPathLink("HeVAL"), HeVAL(cur_entry), recurse);
1214 else warn("skipped suspect HeVAL %p", HeVAL(cur_entry));
1217 cur_entry = cur_entry->hent_next;
1221 } /* bucket chain */
1226 /* This direct access is arguably "naughty": */
1227 struct mro_meta *meta = HvAUX(thing)->xhv_mro_meta;
1228 #if PERL_VERSION > 13 || PERL_SUBVERSION > 8 /* XXX plain || seems like a bug */
1230 I32 count = HvAUX(thing)->xhv_name_count;
1233 HEK **names = HvAUX(thing)->xhv_name_u.xhvnameu_names;
1237 hek_size(aTHX_ st, names[count], 1, NPathLink("HvAUXelem"));
1242 hek_size(aTHX_ st, HvNAME_HEK(thing), 1, NPathLink("HvNAME_HEK"));
1245 ADD_SIZE(st, "xpvhv_aux", sizeof(struct xpvhv_aux));
1247 ADD_SIZE(st, "mro_meta", sizeof(struct mro_meta));
1248 sv_size(aTHX_ st, NPathLink("mro_nextmethod"), (SV *)meta->mro_nextmethod, TOTAL_SIZE_RECURSION);
1249 #if PERL_VERSION > 10 || (PERL_VERSION == 10 && PERL_SUBVERSION > 0)
1250 sv_size(aTHX_ st, NPathLink("isa"), (SV *)meta->isa, TOTAL_SIZE_RECURSION);
1252 #if PERL_VERSION > 10
1253 sv_size(aTHX_ st, NPathLink("mro_linear_all"), (SV *)meta->mro_linear_all, TOTAL_SIZE_RECURSION);
1254 sv_size(aTHX_ st, NPathLink("mro_linear_current"), meta->mro_linear_current, TOTAL_SIZE_RECURSION);
1256 sv_size(aTHX_ st, NPathLink("mro_linear_dfs"), (SV *)meta->mro_linear_dfs, TOTAL_SIZE_RECURSION);
1257 sv_size(aTHX_ st, NPathLink("mro_linear_c3"), (SV *)meta->mro_linear_c3, TOTAL_SIZE_RECURSION);
1262 check_new_and_strlen(st, HvNAME_get(thing), NPathLink("HvNAME"));
1268 padlist_size(aTHX_ st, NPathLink("CvPADLIST"), CvPADLIST(thing), recurse);
1269 sv_size(aTHX_ st, NPathLink("CvOUTSIDE"), (SV *)CvOUTSIDE(thing), SOME_RECURSION);
1271 if (st->go_yell && !st->fm_whine) {
1272 carp("Devel::Size: Calculated sizes for FMs are incomplete");
1278 /* not CvSTASH, per https://rt.cpan.org/Ticket/Display.html?id=79366 */
1279 ADD_ATTR(st, NPattr_NAME, CvGV(thing) ? GvNAME(CvGV(thing)) : "UNDEFINED", 0);
1280 sv_size(aTHX_ st, NPathLink("CvGV"), (SV *)CvGV(thing), SOME_RECURSION);
1281 padlist_size(aTHX_ st, NPathLink("CvPADLIST"), CvPADLIST(thing), recurse);
1282 sv_size(aTHX_ st, NPathLink("CvOUTSIDE"), (SV *)CvOUTSIDE(thing), SOME_RECURSION);
1283 if (CvISXSUB(thing)) {
1284 sv_size(aTHX_ st, NPathLink("cv_const_sv"), cv_const_sv((CV *)thing), recurse);
1286 if(1)op_size(aTHX_ CvSTART(thing), st, NPathLinkAndNode("CvSTART", "OPs")); /* XXX ? */
1287 op_size(aTHX_ CvROOT(thing), st, NPathLinkAndNode("CvROOT", "OPs"));
1292 /* Some embedded char pointers */
1293 check_new_and_strlen(st, ((XPVIO *) SvANY(thing))->xio_top_name, NPathLink("xio_top_name"));
1294 check_new_and_strlen(st, ((XPVIO *) SvANY(thing))->xio_fmt_name, NPathLink("xio_fmt_name"));
1295 check_new_and_strlen(st, ((XPVIO *) SvANY(thing))->xio_bottom_name, NPathLink("xio_bottom_name"));
1296 /* Throw the GVs on the list to be walked if they're not-null */
1297 sv_size(aTHX_ st, NPathLink("xio_top_gv"), (SV *)((XPVIO *) SvANY(thing))->xio_top_gv, recurse);
1298 sv_size(aTHX_ st, NPathLink("xio_bottom_gv"), (SV *)((XPVIO *) SvANY(thing))->xio_bottom_gv, recurse);
1299 sv_size(aTHX_ st, NPathLink("xio_fmt_gv"), (SV *)((XPVIO *) SvANY(thing))->xio_fmt_gv, recurse);
1301 /* Only go trotting through the IO structures if they're really
1302 trottable. If USE_PERLIO is defined we can do this. If
1303 not... we can't, so we don't even try */
1305 /* Dig into xio_ifp and xio_ofp here */
1306 warn("Devel::Size: Can't size up perlio layers yet\n");
1311 #if (PERL_VERSION < 9)
1316 if(isGV_with_GP(thing)) {
1318 hek_size(aTHX_ st, GvNAME_HEK(thing), 1, NPathLink("GvNAME_HEK"));
1320 ADD_SIZE(st, "GvNAMELEN", GvNAMELEN(thing));
1322 ADD_ATTR(st, NPattr_NAME, GvNAME_get(thing), 0);
1324 hek_size(aTHX_ st, GvFILE_HEK(thing), 1, NPathLink("GvFILE_HEK"));
1325 #elif defined(GvFILE)
1326 /* XXX this coredumped for me in t/recurse.t with a non-threaded 5.8.9
1327 * so I've changed the condition to be more restricive
1328 *# if !defined(USE_ITHREADS) || (PERL_VERSION > 8 || (PERL_VERSION == 8 && PERL_SUBVERSION > 8))
1330 # if (PERL_VERSION > 8 || (PERL_VERSION == 8 && PERL_SUBVERSION > 9))
1331 /* With itreads, before 5.8.9, this can end up pointing to freed memory
1332 if the GV was created in an eval, as GvFILE() points to CopFILE(),
1333 and the relevant COP has been freed on scope cleanup after the eval.
1334 5.8.9 adds a binary compatible fudge that catches the vast majority
1335 of cases. 5.9.something added a proper fix, by converting the GP to
1336 use a shared hash key (porperly reference counted), instead of a
1337 char * (owned by who knows? possibly no-one now) */
1338 check_new_and_strlen(st, GvFILE(thing), NPathLink("GvFILE"));
1341 /* Is there something hanging off the glob? */
1342 if (check_new(st, GvGP(thing))) {
1343 ADD_SIZE(st, "GP", sizeof(GP));
1344 sv_size(aTHX_ st, NPathLink("gp_sv"), (SV *)(GvGP(thing)->gp_sv), recurse);
1345 sv_size(aTHX_ st, NPathLink("gp_av"), (SV *)(GvGP(thing)->gp_av), recurse);
1346 sv_size(aTHX_ st, NPathLink("gp_hv"), (SV *)(GvGP(thing)->gp_hv), recurse);
1347 sv_size(aTHX_ st, NPathLink("gp_cv"), (SV *)(GvGP(thing)->gp_cv), recurse);
1348 sv_size(aTHX_ st, NPathLink("gp_egv"), (SV *)(GvGP(thing)->gp_egv), recurse);
1349 sv_size(aTHX_ st, NPathLink("gp_form"), (SV *)(GvGP(thing)->gp_form), recurse);
1351 #if (PERL_VERSION >= 9)
1355 #if PERL_VERSION <= 8
1363 if(recurse && SvROK(thing))
1364 sv_size(aTHX_ st, NPathLink("RV"), SvRV_const(thing), recurse);
1365 else if (SvIsCOW_shared_hash(thing))
1366 hek_size(aTHX_ st, SvSHARED_HEK_FROM_PV(SvPVX(thing)), 1, NPathLink("SvSHARED_HEK_FROM_PV"));
1368 ADD_SIZE(st, "SvLEN", SvLEN(thing));
1372 SvOOK_offset(thing, len);
1373 ADD_SIZE(st, "SvOOK", len);
1379 if (type >= SVt_PVMG) {
1380 if (SvMAGICAL(thing))
1381 magic_size(aTHX_ thing, st, NPathLink("MG"));
1382 /* SVpad_OUR shares same flag bit as SVpbm_VALID and others */
1383 if (type == SVt_PVGV && SvPAD_OUR(thing) && SvOURSTASH(thing))
1384 sv_size(aTHX_ st, NPathLink("SvOURSTASH"), (SV *)SvOURSTASH(thing), SOME_RECURSION);
1386 sv_size(aTHX_ st, NPathLink("SvSTASH"), (SV *)SvSTASH(thing), SOME_RECURSION);
1393 free_memnode_state(pTHX_ struct state *st)
1395 /* PERL_UNUSED_ARG(aTHX); fails for non-threaded perl */
1396 if (st->node_stream_fh && st->node_stream_name && *st->node_stream_name) {
1397 fprintf(st->node_stream_fh, "E %d %f %s\n",
1398 getpid(), gettimeofday_nv()-st->start_time_nv, "unnamed");
1399 if (*st->node_stream_name == '|') {
1400 if (pclose(st->node_stream_fh))
1401 warn("%s exited with an error status\n", st->node_stream_name);
1404 if (fclose(st->node_stream_fh))
1405 warn("Error closing %s: %s\n", st->node_stream_name, strerror(errno));
1410 static struct state *
1416 Newxz(st, 1, struct state);
1418 st->min_recurse_threshold = TOTAL_SIZE_RECURSION;
1419 if (NULL != (warn_flag = get_sv("Devel::Size::warn", FALSE))) {
1420 st->dangle_whine = st->go_yell = SvIV(warn_flag) ? TRUE : FALSE;
1422 if (NULL != (warn_flag = get_sv("Devel::Size::dangle", FALSE))) {
1423 st->dangle_whine = SvIV(warn_flag) ? TRUE : FALSE;
1425 st->start_time_nv = gettimeofday_nv();
1426 check_new(st, &PL_sv_undef);
1427 check_new(st, &PL_sv_no);
1428 check_new(st, &PL_sv_yes);
1429 #if PERL_VERSION > 8 || (PERL_VERSION == 8 && PERL_SUBVERSION > 0)
1430 check_new(st, &PL_sv_placeholder);
1433 #ifdef PATH_TRACKING
1434 /* XXX quick hack */
1435 st->node_stream_name = getenv("SIZEME");
1436 if (st->node_stream_name) {
1437 if (*st->node_stream_name) {
1438 if (*st->node_stream_name == '|')
1439 st->node_stream_fh = popen(st->node_stream_name+1, "w");
1441 st->node_stream_fh = fopen(st->node_stream_name, "wb");
1442 if (!st->node_stream_fh)
1443 croak("Can't open '%s' for writing: %s", st->node_stream_name, strerror(errno));
1444 if(0)setlinebuf(st->node_stream_fh); /* XXX temporary for debugging */
1445 st->add_attr_cb = np_stream_node_path_info;
1446 fprintf(st->node_stream_fh, "S %d %f %s\n",
1447 getpid(), st->start_time_nv, "unnamed");
1450 st->add_attr_cb = np_dump_node_path_info;
1452 st->free_state_cb = free_memnode_state;
1458 /* XXX based on S_visit() in sv.c */
1460 unseen_sv_size(pTHX_ struct state *st, pPATH)
1464 dNPathNodes(1, NPathArg);
1466 NPathPushNode("unseen", NPtype_NAME);
1468 /* by this point we should have visited all the SVs
1469 * so now we'll run through all the SVs via the arenas
1470 * in order to find any that we've missed for some reason.
1471 * Once the rest of the code is finding ALL the SVs then any
1472 * found here will be leaks.
1474 for (sva = PL_sv_arenaroot; sva; sva = MUTABLE_SV(SvANY(sva))) {
1475 const SV * const svend = &sva[SvREFCNT(sva)];
1477 for (sv = sva + 1; sv < svend; ++sv) {
1478 if (SvTYPE(sv) != (svtype)SVTYPEMASK && SvREFCNT(sv)) {
1479 sv_size(aTHX_ st, NPathLink("arena"), sv, TOTAL_SIZE_RECURSION);
1481 else if (check_new(st, sv)) { /* sanity check */
1483 warn("unseen_sv_size encountered freed SV unexpectedly"); /* XXX warn uses an SV, I think */
1491 madprop_size(pTHX_ struct state *const st, pPath, MADPROP *prop)
1493 dPathNodes(2, NPathArg);
1494 if (!check_new(st, prop))
1496 NPathPushNode("madprop_size", NPtype_NAME);
1497 ADD_SIZE(st, "MADPROP", sizeof(MADPROP));
1499 NPathPushNode("val");
1500 ADD_SIZE(st, "val", prop->mad_val);
1502 madprop_size(aTHX_ st, NPathLink("mad_next"), prop->mad_next);
1506 #if (PERL_BCDVERSION >= 0x5009005)
1508 parser_size(pTHX_ struct state *const st, pPATH, yy_parser *parser)
1510 dNPathNodes(2, NPathArg);
1511 if (!check_new(st, parser))
1513 NPathPushNode("parser_size", NPtype_NAME);
1514 ADD_SIZE(st, "yy_parser", sizeof(yy_parser));
1516 NPathPushNode("stack", NPtype_NAME);
1518 /*warn("total: %u", parser->stack_size); */
1519 /*warn("foo: %u", parser->ps - parser->stack); */
1520 ADD_SIZE(st, "stack_frames", parser->stack_size * sizeof(yy_stack_frame));
1521 for (ps = parser->stack; ps <= parser->ps; ps++) {
1522 #if (PERL_BCDVERSION >= 0x5011001) /* roughly */
1523 if (sv_size(aTHX_ st, NPathLink("compcv"), (SV*)ps->compcv, TOTAL_SIZE_RECURSION))
1524 ADD_LINK_ATTR(st, NPattr_NOTE, "i", ps - parser->ps);
1525 #else /* prior to perl 8c63ea58 Dec 8 2009 */
1526 if (sv_size(aTHX_ st, NPathLink("comppad"), (SV*)ps->comppad, TOTAL_SIZE_RECURSION))
1527 ADD_LINK_ATTR(st, NPattr_NOTE, "i", ps - parser->ps);
1532 sv_size(aTHX_ st, NPathLink("lex_repl"), (SV*)parser->lex_repl, TOTAL_SIZE_RECURSION);
1533 sv_size(aTHX_ st, NPathLink("lex_stuff"), (SV*)parser->lex_stuff, TOTAL_SIZE_RECURSION);
1534 sv_size(aTHX_ st, NPathLink("linestr"), (SV*)parser->linestr, TOTAL_SIZE_RECURSION);
1535 sv_size(aTHX_ st, NPathLink("in_my_stash"), (SV*)parser->in_my_stash, TOTAL_SIZE_RECURSION);
1536 /*sv_size(aTHX_ st, NPathLink("rsfp"), parser->rsfp, TOTAL_SIZE_RECURSION); */
1537 sv_size(aTHX_ st, NPathLink("rsfp_filters"), (SV*)parser->rsfp_filters, TOTAL_SIZE_RECURSION);
1539 sv_size(aTHX_ st, NPathLink("endwhite"), parser->endwhite, TOTAL_SIZE_RECURSION);
1540 sv_size(aTHX_ st, NPathLink("nextwhite"), parser->nextwhite, TOTAL_SIZE_RECURSION);
1541 sv_size(aTHX_ st, NPathLink("skipwhite"), parser->skipwhite, TOTAL_SIZE_RECURSION);
1542 sv_size(aTHX_ st, NPathLink("thisclose"), parser->thisclose, TOTAL_SIZE_RECURSION);
1543 madprop_size(aTHX_ st, NPathLink("thismad"), parser->thismad);
1544 sv_size(aTHX_ st, NPathLink("thisopen"), parser->thisopen, TOTAL_SIZE_RECURSION);
1545 sv_size(aTHX_ st, NPathLink("thisstuff"), parser->thisstuff, TOTAL_SIZE_RECURSION);
1546 sv_size(aTHX_ st, NPathLink("thistoken"), parser->thistoken, TOTAL_SIZE_RECURSION);
1547 sv_size(aTHX_ st, NPathLink("thiswhite"), parser->thiswhite, TOTAL_SIZE_RECURSION);
1549 op_size_class(aTHX_ (OP*)parser->saved_curcop, OPc_COP, 0,
1550 st, NPathLink("saved_curcop"));
1552 if (parser->old_parser)
1553 parser_size(aTHX_ st, NPathLink("old_parser"), parser->old_parser);
1558 perl_size(pTHX_ struct state *const st, pPATH)
1560 dNPathNodes(3, NPathArg);
1562 /* if(!check_new(st, interp)) return; */
1563 NPathPushNode("perl", NPtype_NAME);
1564 #if defined(MULTIPLICITY)
1565 ADD_SIZE(st, "PerlInterpreter", sizeof(PerlInterpreter));
1571 * unknown <== = O/S Heap size - perl - free_malloc_space
1573 /* start with PL_defstash to get everything reachable from \%main:: */
1574 sv_size(aTHX_ st, NPathLink("PL_defstash"), (SV*)PL_defstash, TOTAL_SIZE_RECURSION);
1576 NPathPushNode("others", NPtype_NAME); /* group these (typically much smaller) items */
1577 sv_size(aTHX_ st, NPathLink("PL_defgv"), (SV*)PL_defgv, TOTAL_SIZE_RECURSION);
1578 sv_size(aTHX_ st, NPathLink("PL_incgv"), (SV*)PL_incgv, TOTAL_SIZE_RECURSION);
1579 sv_size(aTHX_ st, NPathLink("PL_rs"), (SV*)PL_rs, TOTAL_SIZE_RECURSION);
1580 sv_size(aTHX_ st, NPathLink("PL_fdpid"), (SV*)PL_fdpid, TOTAL_SIZE_RECURSION);
1581 sv_size(aTHX_ st, NPathLink("PL_modglobal"), (SV*)PL_modglobal, TOTAL_SIZE_RECURSION);
1582 sv_size(aTHX_ st, NPathLink("PL_errors"), (SV*)PL_errors, TOTAL_SIZE_RECURSION);
1583 sv_size(aTHX_ st, NPathLink("PL_stashcache"), (SV*)PL_stashcache, TOTAL_SIZE_RECURSION);
1584 sv_size(aTHX_ st, NPathLink("PL_patchlevel"), (SV*)PL_patchlevel, TOTAL_SIZE_RECURSION);
1585 #ifdef PL_apiversion
1586 sv_size(aTHX_ st, NPathLink("PL_apiversion"), (SV*)PL_apiversion, TOTAL_SIZE_RECURSION);
1588 #ifdef PL_registered_mros
1589 sv_size(aTHX_ st, NPathLink("PL_registered_mros"), (SV*)PL_registered_mros, TOTAL_SIZE_RECURSION);
1592 sv_size(aTHX_ st, NPathLink("PL_regex_padav"), (SV*)PL_regex_padav, TOTAL_SIZE_RECURSION);
1594 sv_size(aTHX_ st, NPathLink("PL_warnhook"), (SV*)PL_warnhook, TOTAL_SIZE_RECURSION);
1595 sv_size(aTHX_ st, NPathLink("PL_diehook"), (SV*)PL_diehook, TOTAL_SIZE_RECURSION);
1596 sv_size(aTHX_ st, NPathLink("PL_endav"), (SV*)PL_endav, TOTAL_SIZE_RECURSION);
1597 sv_size(aTHX_ st, NPathLink("PL_main_cv"), (SV*)PL_main_cv, TOTAL_SIZE_RECURSION);
1598 sv_size(aTHX_ st, NPathLink("PL_main_root"), (SV*)PL_main_root, TOTAL_SIZE_RECURSION);
1599 sv_size(aTHX_ st, NPathLink("PL_main_start"), (SV*)PL_main_start, TOTAL_SIZE_RECURSION);
1600 sv_size(aTHX_ st, NPathLink("PL_envgv"), (SV*)PL_envgv, TOTAL_SIZE_RECURSION);
1601 sv_size(aTHX_ st, NPathLink("PL_hintgv"), (SV*)PL_hintgv, TOTAL_SIZE_RECURSION);
1602 sv_size(aTHX_ st, NPathLink("PL_e_script"), (SV*)PL_e_script, TOTAL_SIZE_RECURSION);
1603 sv_size(aTHX_ st, NPathLink("PL_encoding"), (SV*)PL_encoding, TOTAL_SIZE_RECURSION);
1605 sv_size(aTHX_ st, NPathLink("PL_ofsgv"), (SV*)PL_ofsgv, TOTAL_SIZE_RECURSION);
1607 sv_size(aTHX_ st, NPathLink("PL_argvout_stack"), (SV*)PL_argvout_stack, TOTAL_SIZE_RECURSION);
1608 sv_size(aTHX_ st, NPathLink("PL_beginav"), (SV*)PL_beginav, TOTAL_SIZE_RECURSION);
1609 sv_size(aTHX_ st, NPathLink("PL_beginav_save"), (SV*)PL_beginav_save, TOTAL_SIZE_RECURSION);
1610 sv_size(aTHX_ st, NPathLink("PL_checkav_save"), (SV*)PL_checkav_save, TOTAL_SIZE_RECURSION);
1611 #ifdef PL_unitcheckav
1612 sv_size(aTHX_ st, NPathLink("PL_unitcheckav"), (SV*)PL_unitcheckav, TOTAL_SIZE_RECURSION);
1614 #ifdef PL_unitcheckav_save
1615 sv_size(aTHX_ st, NPathLink("PL_unitcheckav_save"), (SV*)PL_unitcheckav_save, TOTAL_SIZE_RECURSION);
1617 sv_size(aTHX_ st, NPathLink("PL_endav"), (SV*)PL_endav, TOTAL_SIZE_RECURSION);
1618 sv_size(aTHX_ st, NPathLink("PL_checkav"), (SV*)PL_checkav, TOTAL_SIZE_RECURSION);
1619 sv_size(aTHX_ st, NPathLink("PL_initav"), (SV*)PL_initav, TOTAL_SIZE_RECURSION);
1621 sv_size(aTHX_ st, NPathLink("PL_isarev"), (SV*)PL_isarev, TOTAL_SIZE_RECURSION);
1623 sv_size(aTHX_ st, NPathLink("PL_fdpid"), (SV*)PL_fdpid, TOTAL_SIZE_RECURSION);
1624 sv_size(aTHX_ st, NPathLink("PL_preambleav"), (SV*)PL_preambleav, TOTAL_SIZE_RECURSION);
1625 sv_size(aTHX_ st, NPathLink("PL_ors_sv"), (SV*)PL_ors_sv, TOTAL_SIZE_RECURSION);
1626 sv_size(aTHX_ st, NPathLink("PL_modglobal"), (SV*)PL_modglobal, TOTAL_SIZE_RECURSION);
1627 sv_size(aTHX_ st, NPathLink("PL_custom_op_names"), (SV*)PL_custom_op_names, TOTAL_SIZE_RECURSION);
1628 sv_size(aTHX_ st, NPathLink("PL_custom_op_descs"), (SV*)PL_custom_op_descs, TOTAL_SIZE_RECURSION);
1629 #ifdef PL_custom_ops
1630 sv_size(aTHX_ st, NPathLink("PL_custom_ops"), (SV*)PL_custom_ops, TOTAL_SIZE_RECURSION);
1632 sv_size(aTHX_ st, NPathLink("PL_compcv"), (SV*)PL_compcv, TOTAL_SIZE_RECURSION);
1633 sv_size(aTHX_ st, NPathLink("PL_DBcv"), (SV*)PL_DBcv, TOTAL_SIZE_RECURSION);
1634 #ifdef PERL_USES_PL_PIDSTATUS
1635 sv_size(aTHX_ st, NPathLink("PL_pidstatus"), (SV*)PL_pidstatus, TOTAL_SIZE_RECURSION);
1637 sv_size(aTHX_ st, NPathLink("PL_subname"), (SV*)PL_subname, TOTAL_SIZE_RECURSION);
1638 #ifdef USE_LOCALE_NUMERIC
1639 sv_size(aTHX_ st, NPathLink("PL_numeric_radix_sv"), (SV*)PL_numeric_radix_sv, TOTAL_SIZE_RECURSION);
1640 check_new_and_strlen(st, PL_numeric_name, NPathLink("PL_numeric_name"));
1642 #ifdef USE_LOCALE_COLLATE
1643 check_new_and_strlen(st, PL_collation_name, NPathLink("PL_collation_name"));
1645 check_new_and_strlen(st, PL_origfilename, NPathLink("PL_origfilename"));
1646 check_new_and_strlen(st, PL_inplace, NPathLink("PL_inplace"));
1647 check_new_and_strlen(st, PL_osname, NPathLink("PL_osname"));
1648 if (PL_op_mask && check_new(st, PL_op_mask))
1649 ADD_SIZE(st, "PL_op_mask", PL_maxo);
1650 if (PL_exitlistlen && check_new(st, PL_exitlist))
1651 ADD_SIZE(st, "PL_exitlist", (PL_exitlistlen * sizeof(PerlExitListEntry *))
1652 + (PL_exitlistlen * sizeof(PerlExitListEntry)));
1653 #ifdef PERL_IMPLICIT_CONTEXT
1654 if (PL_my_cxt_size && check_new(st, PL_my_cxt_list)) {
1655 ADD_SIZE(st, "PL_my_cxt_list", (PL_my_cxt_size * sizeof(void *)));
1656 #ifdef PERL_GLOBAL_STRUCT_PRIVATE
1657 ADD_SIZE(st, "PL_my_cxt_keys", (PL_my_cxt_size * sizeof(char *)));
1661 /* TODO PL_stashpad */
1662 op_size_class(aTHX_ (OP*)&PL_compiling, OPc_COP, 1, st, NPathLink("PL_compiling"));
1663 op_size_class(aTHX_ (OP*)PL_curcopdb, OPc_COP, 0, st, NPathLink("PL_curcopdb"));
1665 #if (PERL_BCDVERSION >= 0x5009005)
1666 parser_size(aTHX_ st, NPathLink("PL_parser"), PL_parser);
1669 /* TODO stacks: cur, main, tmps, mark, scope, save */
1670 /* TODO PL_exitlist */
1671 /* TODO PL_reentrant_buffers etc */
1673 /* TODO PerlIO? PL_known_layers PL_def_layerlist PL_perlio_fd_refcnt etc */
1675 /* TODO anything missed? */
1677 /* --- by this point we should have seen all reachable SVs --- */
1679 /* in theory we shouldn't have any elements in PL_strtab that haven't been seen yet */
1680 sv_size(aTHX_ st, NPathLink("PL_strtab-unseen"), (SV*)PL_strtab, TOTAL_SIZE_RECURSION);
1682 /* unused space in sv head arenas */
1686 # define SvARENA_CHAIN(sv) SvANY(sv) /* XXX breaks encapsulation*/
1687 while ((p = MUTABLE_SV(SvARENA_CHAIN(p)))) {
1688 if (!check_new(st, p)) /* sanity check */
1689 warn("Free'd SV head unexpectedly already seen");
1692 NPathPushNode("unused_sv_heads", NPtype_NAME);
1693 ADD_SIZE(st, "sv", free_heads * sizeof(SV));
1696 /* XXX iterate over bodies_by_type and crawl the free chains for each */
1698 /* iterate over all SVs to find any we've not accounted for yet */
1699 /* once the code above is visiting all SVs, any found here have been leaked */
1700 unseen_sv_size(aTHX_ st, NPathLink("unaccounted"));
1704 MODULE = Devel::SizeMe PACKAGE = Devel::SizeMe
1712 total_size = TOTAL_SIZE_RECURSION
1715 SV *thing = orig_thing;
1716 struct state *st = new_state(aTHX);
1718 /* If they passed us a reference then dereference it. This is the
1719 only way we can check the sizes of arrays and hashes */
1721 thing = SvRV(thing);
1724 sv_size(aTHX_ st, NULL, thing, ix);
1725 RETVAL = st->total_size;
1726 free_state(aTHX_ st);
1735 /* just the current perl interpreter */
1736 struct state *st = new_state(aTHX);
1737 st->min_recurse_threshold = NO_RECURSION; /* so always recurse */
1738 perl_size(aTHX_ st, NULL);
1739 RETVAL = st->total_size;
1740 free_state(aTHX_ st);
1749 /* the current perl interpreter plus malloc, in the context of total heap size */
1750 # ifdef _MALLOC_MALLOC_H_ /* OSX. Now sure where else mstats is available */
1754 /* some systems have the SVID2/XPG mallinfo structure and function */
1755 struct mstats ms = mstats(); /* mstats() first */
1757 struct state *st = new_state(aTHX);
1758 dNPathNodes(1, NULL);
1759 NPathPushNode("heap", NPtype_NAME);
1761 st->min_recurse_threshold = NO_RECURSION; /* so always recurse */
1763 perl_size(aTHX_ st, NPathLink("perl_interp"));
1765 NPathSetNode("free_malloc_space", NPtype_NAME);
1766 ADD_SIZE(st, "bytes_free", ms.bytes_free);
1767 ADD_ATTR(st, NPattr_NOTE, "bytes_total", ms.bytes_total);
1768 ADD_ATTR(st, NPattr_NOTE, "bytes_used", ms.bytes_used);
1769 ADD_ATTR(st, NPattr_NOTE, "chunks_used", ms.chunks_used);
1770 ADD_ATTR(st, NPattr_NOTE, "chunks_free", ms.chunks_free);
1771 /* TODO get heap size from OS and add a node: unknown = heapsize - perl - ms.bytes_free */
1772 /* for now we use bytes_total as an approximation */
1773 NPathSetNode("unknown", NPtype_NAME);
1774 ADD_SIZE(st, "unknown", ms.bytes_total - st->total_size);
1779 RETVAL = st->total_size;
1780 free_state(aTHX_ st);