Add -DPURIFY sizes to body_sizes[]
[p5sagit/Devel-Size.git] / Size.xs
CommitLineData
265a0548 1#define PERL_NO_GET_CONTEXT
2
e98cedbf 3#include "EXTERN.h"
4#include "perl.h"
5#include "XSUB.h"
2eb93d08 6#include "ppport.h"
e98cedbf 7
87372f42 8/* Not yet in ppport.h */
9#ifndef CvISXSUB
10# define CvISXSUB(cv) (CvXSUB(cv) ? TRUE : FALSE)
11#endif
0e1f978f 12#ifndef SvRV_const
13# define SvRV_const(rv) SvRV(rv)
14#endif
87372f42 15
9fc9ab86 16#ifdef _MSC_VER
1a36ac09 17/* "structured exception" handling is a Microsoft extension to C and C++.
18 It's *not* C++ exception handling - C++ exception handling can't capture
19 SEGVs and suchlike, whereas this can. There's no known analagous
20 functionality on other platforms. */
21# include <excpt.h>
22# define TRY_TO_CATCH_SEGV __try
23# define CAUGHT_EXCEPTION __except(EXCEPTION EXCEPTION_EXECUTE_HANDLER)
9fc9ab86 24#else
1a36ac09 25# define TRY_TO_CATCH_SEGV if(1)
26# define CAUGHT_EXCEPTION else
9fc9ab86 27#endif
28
29#ifdef __GNUC__
30# define __attribute__(x)
31#endif
32
b7621729 33#if 0 && defined(DEBUGGING)
34#define dbg_printf(x) printf x
35#else
36#define dbg_printf(x)
37#endif
98ecbbc6 38
0964064b 39#define TAG /* printf( "# %s(%d)\n", __FILE__, __LINE__ ) */
6a9ad7ec 40#define carp puts
9fc9ab86 41
30fe4f47 42/* The idea is to have a tree structure to store 1 bit per possible pointer
43 address. The lowest 16 bits are stored in a block of 8092 bytes.
44 The blocks are in a 256-way tree, indexed by the reset of the pointer.
45 This can cope with 32 and 64 bit pointers, and any address space layout,
46 without excessive memory needs. The assumption is that your CPU cache
47 works :-) (And that we're not going to bust it) */
48
30fe4f47 49#define BYTE_BITS 3
50#define LEAF_BITS (16 - BYTE_BITS)
51#define LEAF_MASK 0x1FFF
9fc9ab86 52
65db36c0 53struct state {
eee00145 54 UV total_size;
65db36c0 55 bool regex_whine;
56 bool fm_whine;
57 bool dangle_whine;
58 bool go_yell;
59 /* My hunch (not measured) is that for most architectures pointers will
60 start with 0 bits, hence the start of this array will be hot, and the
61 end unused. So put the flags next to the hot end. */
62 void *tracking[256];
63};
64
9fc9ab86 65/*
66 Checks to see if thing is in the bitstring.
67 Returns true or false, and
68 notes thing in the segmented bitstring.
69 */
2eb93d08 70static bool
a4efdff3 71check_new(struct state *st, const void *const p) {
30fe4f47 72 unsigned int bits = 8 * sizeof(void*);
73 const size_t raw_p = PTR2nat(p);
74 /* This effectively rotates the value right by the number of low always-0
75 bits in an aligned pointer. The assmption is that most (if not all)
76 pointers are aligned, and these will be in the same chain of nodes
77 (and hence hot in the cache) but we can still deal with any unaligned
78 pointers. */
79 const size_t cooked_p
f404ed48 80 = (raw_p >> ALIGN_BITS) | (raw_p << (bits - ALIGN_BITS));
30fe4f47 81 const U8 this_bit = 1 << (cooked_p & 0x7);
82 U8 **leaf_p;
83 U8 *leaf;
84 unsigned int i;
302077b6 85 void **tv_p = (void **) (st->tracking);
30fe4f47 86
302077b6 87 if (NULL == p) return FALSE;
1a36ac09 88 TRY_TO_CATCH_SEGV {
2eb93d08 89 const char c = *(const char *)p;
9fc9ab86 90 }
1a36ac09 91 CAUGHT_EXCEPTION {
a4efdff3 92 if (st->dangle_whine)
9fc9ab86 93 warn( "Devel::Size: Encountered invalid pointer: %p\n", p );
94 return FALSE;
95 }
9fc9ab86 96 TAG;
30fe4f47 97
98 bits -= 8;
99 /* bits now 24 (32 bit pointers) or 56 (64 bit pointers) */
100
101 /* First level is always present. */
102 do {
103 i = (unsigned int)((cooked_p >> bits) & 0xFF);
104 if (!tv_p[i])
105 Newxz(tv_p[i], 256, void *);
106 tv_p = (void **)(tv_p[i]);
107 bits -= 8;
108 } while (bits > LEAF_BITS + BYTE_BITS);
109 /* bits now 16 always */
5f04c81d 110#if !defined(MULTIPLICITY) || PERL_VERSION > 8 || (PERL_VERSION == 8 && PERL_SUBVERSION > 8)
111 /* 5.8.8 and early have an assert() macro that uses Perl_croak, hence needs
112 a my_perl under multiplicity */
30fe4f47 113 assert(bits == 16);
5f04c81d 114#endif
30fe4f47 115 leaf_p = (U8 **)tv_p;
116 i = (unsigned int)((cooked_p >> bits) & 0xFF);
117 if (!leaf_p[i])
118 Newxz(leaf_p[i], 1 << LEAF_BITS, U8);
119 leaf = leaf_p[i];
120
9fc9ab86 121 TAG;
30fe4f47 122
123 i = (unsigned int)((cooked_p >> BYTE_BITS) & LEAF_MASK);
124
125 if(leaf[i] & this_bit)
126 return FALSE;
127
128 leaf[i] |= this_bit;
9fc9ab86 129 return TRUE;
130}
131
e9716740 132static void
30fe4f47 133free_tracking_at(void **tv, int level)
134{
135 int i = 255;
136
137 if (--level) {
138 /* Nodes */
139 do {
140 if (tv[i]) {
141 free_tracking_at(tv[i], level);
142 Safefree(tv[i]);
143 }
144 } while (i--);
145 } else {
146 /* Leaves */
147 do {
148 if (tv[i])
149 Safefree(tv[i]);
150 } while (i--);
151 }
152}
153
154static void
a4efdff3 155free_state(struct state *st)
e9716740 156{
30fe4f47 157 const int top_level = (sizeof(void *) * 8 - LEAF_BITS - BYTE_BITS) / 8;
a4efdff3 158 free_tracking_at((void **)st->tracking, top_level);
159 Safefree(st);
e9716740 160}
161
f3cf7e20 162/* For now, this is somewhat a compatibility bodge until the plan comes
163 together for fine grained recursion control. total_size() would recurse into
164 hash and array members, whereas sv_size() would not. However, sv_size() is
165 called with CvSTASH() of a CV, which means that if it (also) starts to
166 recurse fully, then the size of any CV now becomes the size of the entire
167 symbol table reachable from it, and potentially the entire symbol table, if
168 any subroutine makes a reference to a global (such as %SIG). The historical
169 implementation of total_size() didn't report "everything", and changing the
170 only available size to "everything" doesn't feel at all useful. */
171
172#define NO_RECURSION 0
173#define SOME_RECURSION 1
174#define TOTAL_SIZE_RECURSION 2
175
176static bool sv_size(pTHX_ struct state *, const SV *const, const int recurse);
db519f11 177
7ccc7d88 178typedef enum {
9fc9ab86 179 OPc_NULL, /* 0 */
180 OPc_BASEOP, /* 1 */
181 OPc_UNOP, /* 2 */
182 OPc_BINOP, /* 3 */
183 OPc_LOGOP, /* 4 */
184 OPc_LISTOP, /* 5 */
185 OPc_PMOP, /* 6 */
186 OPc_SVOP, /* 7 */
187 OPc_PADOP, /* 8 */
188 OPc_PVOP, /* 9 */
189 OPc_LOOP, /* 10 */
190 OPc_COP /* 11 */
7ccc7d88 191} opclass;
192
193static opclass
9fc9ab86 194cc_opclass(const OP * const o)
7ccc7d88 195{
196 if (!o)
9fc9ab86 197 return OPc_NULL;
1a36ac09 198 TRY_TO_CATCH_SEGV {
9fc9ab86 199 if (o->op_type == 0)
200 return (o->op_flags & OPf_KIDS) ? OPc_UNOP : OPc_BASEOP;
7ccc7d88 201
9fc9ab86 202 if (o->op_type == OP_SASSIGN)
203 return ((o->op_private & OPpASSIGN_BACKWARDS) ? OPc_UNOP : OPc_BINOP);
7ccc7d88 204
9fc9ab86 205 #ifdef USE_ITHREADS
206 if (o->op_type == OP_GV || o->op_type == OP_GVSV || o->op_type == OP_AELEMFAST)
207 return OPc_PADOP;
208 #endif
7ccc7d88 209
9fc9ab86 210 if ((o->op_type == OP_TRANS)) {
211 return OPc_BASEOP;
212 }
7ccc7d88 213
9fc9ab86 214 switch (PL_opargs[o->op_type] & OA_CLASS_MASK) {
215 case OA_BASEOP: TAG;
216 return OPc_BASEOP;
217
218 case OA_UNOP: TAG;
219 return OPc_UNOP;
220
221 case OA_BINOP: TAG;
222 return OPc_BINOP;
62691e7c 223
9fc9ab86 224 case OA_LOGOP: TAG;
225 return OPc_LOGOP;
7ccc7d88 226
9fc9ab86 227 case OA_LISTOP: TAG;
228 return OPc_LISTOP;
7ccc7d88 229
9fc9ab86 230 case OA_PMOP: TAG;
231 return OPc_PMOP;
7ccc7d88 232
9fc9ab86 233 case OA_SVOP: TAG;
234 return OPc_SVOP;
7ccc7d88 235
9fc9ab86 236 case OA_PADOP: TAG;
237 return OPc_PADOP;
7ccc7d88 238
9fc9ab86 239 case OA_PVOP_OR_SVOP: TAG;
240 /*
241 * Character translations (tr///) are usually a PVOP, keeping a
242 * pointer to a table of shorts used to look up translations.
243 * Under utf8, however, a simple table isn't practical; instead,
244 * the OP is an SVOP, and the SV is a reference to a swash
245 * (i.e., an RV pointing to an HV).
246 */
247 return (o->op_private & (OPpTRANS_TO_UTF|OPpTRANS_FROM_UTF))
248 ? OPc_SVOP : OPc_PVOP;
7ccc7d88 249
9fc9ab86 250 case OA_LOOP: TAG;
251 return OPc_LOOP;
7ccc7d88 252
9fc9ab86 253 case OA_COP: TAG;
254 return OPc_COP;
7ccc7d88 255
9fc9ab86 256 case OA_BASEOP_OR_UNOP: TAG;
7ccc7d88 257 /*
9fc9ab86 258 * UNI(OP_foo) in toke.c returns token UNI or FUNC1 depending on
259 * whether parens were seen. perly.y uses OPf_SPECIAL to
260 * signal whether a BASEOP had empty parens or none.
261 * Some other UNOPs are created later, though, so the best
262 * test is OPf_KIDS, which is set in newUNOP.
7ccc7d88 263 */
9fc9ab86 264 return (o->op_flags & OPf_KIDS) ? OPc_UNOP : OPc_BASEOP;
265
266 case OA_FILESTATOP: TAG;
267 /*
268 * The file stat OPs are created via UNI(OP_foo) in toke.c but use
269 * the OPf_REF flag to distinguish between OP types instead of the
270 * usual OPf_SPECIAL flag. As usual, if OPf_KIDS is set, then we
271 * return OPc_UNOP so that walkoptree can find our children. If
272 * OPf_KIDS is not set then we check OPf_REF. Without OPf_REF set
273 * (no argument to the operator) it's an OP; with OPf_REF set it's
274 * an SVOP (and op_sv is the GV for the filehandle argument).
275 */
276 return ((o->op_flags & OPf_KIDS) ? OPc_UNOP :
277 #ifdef USE_ITHREADS
278 (o->op_flags & OPf_REF) ? OPc_PADOP : OPc_BASEOP);
279 #else
280 (o->op_flags & OPf_REF) ? OPc_SVOP : OPc_BASEOP);
281 #endif
282 case OA_LOOPEXOP: TAG;
283 /*
284 * next, last, redo, dump and goto use OPf_SPECIAL to indicate that a
285 * label was omitted (in which case it's a BASEOP) or else a term was
286 * seen. In this last case, all except goto are definitely PVOP but
287 * goto is either a PVOP (with an ordinary constant label), an UNOP
288 * with OPf_STACKED (with a non-constant non-sub) or an UNOP for
289 * OP_REFGEN (with goto &sub) in which case OPf_STACKED also seems to
290 * get set.
291 */
292 if (o->op_flags & OPf_STACKED)
293 return OPc_UNOP;
294 else if (o->op_flags & OPf_SPECIAL)
295 return OPc_BASEOP;
296 else
297 return OPc_PVOP;
298 }
299 warn("Devel::Size: Can't determine class of operator %s, assuming BASEOP\n",
300 PL_op_name[o->op_type]);
301 }
1a36ac09 302 CAUGHT_EXCEPTION { }
7ccc7d88 303 return OPc_BASEOP;
304}
305
6a9ad7ec 306
a6ea0805 307#if !defined(NV)
308#define NV double
309#endif
310
6a9ad7ec 311/* Figure out how much magic is attached to the SV and return the
312 size */
eee00145 313static void
b7130948 314magic_size(pTHX_ const SV * const thing, struct state *st) {
980c6576 315 MAGIC *magic_pointer = SvMAGIC(thing);
6a9ad7ec 316
980c6576 317 /* Have we seen the magic pointer? (NULL has always been seen before) */
e5c69bdd 318 while (check_new(st, magic_pointer)) {
eee00145 319 st->total_size += sizeof(MAGIC);
6a9ad7ec 320
1a36ac09 321 TRY_TO_CATCH_SEGV {
9fc9ab86 322 /* Have we seen the magic vtable? */
e5c69bdd 323 if (check_new(st, magic_pointer->mg_virtual)) {
eee00145 324 st->total_size += sizeof(MGVTBL);
9fc9ab86 325 }
b7130948 326 sv_size(aTHX_ st, magic_pointer->mg_obj, TOTAL_SIZE_RECURSION);
d1888d0b 327 if (magic_pointer->mg_len == HEf_SVKEY) {
328 sv_size(aTHX_ st, (SV *)magic_pointer->mg_ptr, TOTAL_SIZE_RECURSION);
329 }
330#if defined(PERL_MAGIC_utf8) && defined (PERL_MAGIC_UTF8_CACHESIZE)
331 else if (magic_pointer->mg_type == PERL_MAGIC_utf8) {
332 if (check_new(st, magic_pointer->mg_ptr)) {
333 st->total_size += PERL_MAGIC_UTF8_CACHESIZE * 2 * sizeof(STRLEN);
334 }
335 }
336#endif
337 else if (magic_pointer->mg_len > 0) {
338 if (check_new(st, magic_pointer->mg_ptr)) {
339 st->total_size += magic_pointer->mg_len;
340 }
341 }
6a9ad7ec 342
0964064b 343 /* Get the next in the chain */
9fc9ab86 344 magic_pointer = magic_pointer->mg_moremagic;
345 }
1a36ac09 346 CAUGHT_EXCEPTION {
a4efdff3 347 if (st->dangle_whine)
9fc9ab86 348 warn( "Devel::Size: Encountered bad magic at: %p\n", magic_pointer );
349 }
6a9ad7ec 350 }
6a9ad7ec 351}
352
eee00145 353static void
99684fd4 354check_new_and_strlen(struct state *st, const char *const p) {
355 if(check_new(st, p))
6ec51ae0 356 st->total_size += 1 + strlen(p);
99684fd4 357}
358
359static void
eee00145 360regex_size(const REGEXP * const baseregex, struct state *st) {
c1bfd7da 361 if(!check_new(st, baseregex))
362 return;
eee00145 363 st->total_size += sizeof(REGEXP);
9fc9ab86 364#if (PERL_VERSION < 11)
6ea94d90 365 /* Note the size of the paren offset thing */
eee00145 366 st->total_size += sizeof(I32) * baseregex->nparens * 2;
367 st->total_size += strlen(baseregex->precomp);
6ea94d90 368#else
eee00145 369 st->total_size += sizeof(struct regexp);
370 st->total_size += sizeof(I32) * SvANY(baseregex)->nparens * 2;
371 /*st->total_size += strlen(SvANY(baseregex)->subbeg);*/
6ea94d90 372#endif
a4efdff3 373 if (st->go_yell && !st->regex_whine) {
6ea94d90 374 carp("Devel::Size: Calculated sizes for compiled regexes are incompatible, and probably always will be");
a4efdff3 375 st->regex_whine = 1;
98ecbbc6 376 }
7ccc7d88 377}
378
eee00145 379static void
1e5a8ad2 380op_size(pTHX_ const OP * const baseop, struct state *st)
381{
382 TRY_TO_CATCH_SEGV {
383 TAG;
384 if(!check_new(st, baseop))
385 return;
386 TAG;
387 op_size(aTHX_ baseop->op_next, st);
388 TAG;
389 switch (cc_opclass(baseop)) {
390 case OPc_BASEOP: TAG;
391 st->total_size += sizeof(struct op);
392 TAG;break;
393 case OPc_UNOP: TAG;
394 st->total_size += sizeof(struct unop);
395 op_size(aTHX_ cUNOPx(baseop)->op_first, st);
396 TAG;break;
397 case OPc_BINOP: TAG;
398 st->total_size += sizeof(struct binop);
399 op_size(aTHX_ cBINOPx(baseop)->op_first, st);
400 op_size(aTHX_ cBINOPx(baseop)->op_last, st);
401 TAG;break;
402 case OPc_LOGOP: TAG;
403 st->total_size += sizeof(struct logop);
404 op_size(aTHX_ cBINOPx(baseop)->op_first, st);
405 op_size(aTHX_ cLOGOPx(baseop)->op_other, st);
406 TAG;break;
407 case OPc_LISTOP: TAG;
408 st->total_size += sizeof(struct listop);
409 op_size(aTHX_ cLISTOPx(baseop)->op_first, st);
410 op_size(aTHX_ cLISTOPx(baseop)->op_last, st);
411 TAG;break;
412 case OPc_PMOP: TAG;
413 st->total_size += sizeof(struct pmop);
414 op_size(aTHX_ cPMOPx(baseop)->op_first, st);
415 op_size(aTHX_ cPMOPx(baseop)->op_last, st);
5a83b7cf 416#if PERL_VERSION < 9 || (PERL_VERSION == 9 && PERL_SUBVERSION < 5)
1e5a8ad2 417 op_size(aTHX_ cPMOPx(baseop)->op_pmreplroot, st);
418 op_size(aTHX_ cPMOPx(baseop)->op_pmreplstart, st);
419 op_size(aTHX_ (OP *)cPMOPx(baseop)->op_pmnext, st);
5a83b7cf 420#endif
c1bfd7da 421 /* This is defined away in perl 5.8.x, but it is in there for
422 5.6.x */
98ecbbc6 423#ifdef PM_GETRE
c1bfd7da 424 regex_size(PM_GETRE(cPMOPx(baseop)), st);
98ecbbc6 425#else
c1bfd7da 426 regex_size(cPMOPx(baseop)->op_pmregexp, st);
98ecbbc6 427#endif
c1bfd7da 428 TAG;break;
81f1c018 429 case OPc_SVOP: TAG;
430 st->total_size += sizeof(struct pmop);
574d9fd9 431 if (!(baseop->op_type == OP_AELEMFAST
432 && baseop->op_flags & OPf_SPECIAL)) {
433 /* not an OP_PADAV replacement */
f3cf7e20 434 sv_size(aTHX_ st, cSVOPx(baseop)->op_sv, SOME_RECURSION);
574d9fd9 435 }
81f1c018 436 TAG;break;
9fc9ab86 437 case OPc_PADOP: TAG;
eee00145 438 st->total_size += sizeof(struct padop);
99684fd4 439 TAG;break;
440 case OPc_PVOP: TAG;
441 check_new_and_strlen(st, cPVOPx(baseop)->op_pv);
219b7d34 442 TAG;break;
1e5a8ad2 443 case OPc_LOOP: TAG;
444 st->total_size += sizeof(struct loop);
445 op_size(aTHX_ cLOOPx(baseop)->op_first, st);
446 op_size(aTHX_ cLOOPx(baseop)->op_last, st);
447 op_size(aTHX_ cLOOPx(baseop)->op_redoop, st);
448 op_size(aTHX_ cLOOPx(baseop)->op_nextop, st);
449 op_size(aTHX_ cLOOPx(baseop)->op_lastop, st);
450 TAG;break;
451 case OPc_COP: TAG;
9fc9ab86 452 {
453 COP *basecop;
454 basecop = (COP *)baseop;
eee00145 455 st->total_size += sizeof(struct cop);
9fc9ab86 456
457 /* Change 33656 by nicholas@mouse-mill on 2008/04/07 11:29:51
458 Eliminate cop_label from struct cop by storing a label as the first
459 entry in the hints hash. Most statements don't have labels, so this
460 will save memory. Not sure how much.
461 The check below will be incorrect fail on bleadperls
462 before 5.11 @33656, but later than 5.10, producing slightly too
463 small memory sizes on these Perls. */
b7621729 464#if (PERL_VERSION < 11)
99684fd4 465 check_new_and_strlen(st, basecop->cop_label);
b7621729 466#endif
7ccc7d88 467#ifdef USE_ITHREADS
99684fd4 468 check_new_and_strlen(st, basecop->cop_file);
469 check_new_and_strlen(st, basecop->cop_stashpv);
7ccc7d88 470#else
f3cf7e20 471 sv_size(aTHX_ st, (SV *)basecop->cop_stash, SOME_RECURSION);
472 sv_size(aTHX_ st, (SV *)basecop->cop_filegv, SOME_RECURSION);
7ccc7d88 473#endif
474
9fc9ab86 475 }
476 TAG;break;
477 default:
478 TAG;break;
479 }
480 }
1a36ac09 481 CAUGHT_EXCEPTION {
a4efdff3 482 if (st->dangle_whine)
9fc9ab86 483 warn( "Devel::Size: Encountered dangling pointer in opcode at: %p\n", baseop );
7ccc7d88 484 }
7ccc7d88 485}
6a9ad7ec 486
b6558d1d 487#if PERL_VERSION < 8 || PERL_SUBVERSION < 9
488# define SVt_LAST 16
24d37977 489#endif
490
f73dcfce 491#ifdef PURIFY
492# define MAYBE_PURIFY(normal, pure) (pure)
493# define MAYBE_OFFSET(struct_name, member) 0
494#else
495# define MAYBE_PURIFY(normal, pure) (normal)
496# define MAYBE_OFFSET(struct_name, member) STRUCT_OFFSET(struct_name, member)
497#endif
498
b6558d1d 499const U8 body_sizes[SVt_LAST] = {
500#if PERL_VERSION < 9
f73dcfce 501 0, /* SVt_NULL */
502 MAYBE_PURIFY(sizeof(IV), sizeof(XPVIV)), /* SVt_IV */
503 MAYBE_PURIFY(sizeof(NV), sizeof(XPVNV)), /* SVt_NV */
504 sizeof(XRV), /* SVt_RV */
505 sizeof(XPV), /* SVt_PV */
506 sizeof(XPVIV), /* SVt_PVIV */
507 sizeof(XPVNV), /* SVt_PVNV */
508 sizeof(XPVMG), /* SVt_PVMG */
509 sizeof(XPVBM), /* SVt_PVBM */
510 sizeof(XPVLV), /* SVt_PVLV */
511 sizeof(XPVAV), /* SVt_PVAV */
512 sizeof(XPVHV), /* SVt_PVHV */
513 sizeof(XPVCV), /* SVt_PVCV */
514 sizeof(XPVGV), /* SVt_PVGV */
515 sizeof(XPVFM), /* SVt_PVFM */
516 sizeof(XPVIO) /* SVt_PVIO */
b6558d1d 517#elif PERL_VERSION == 10 && PERL_SUBVERSION == 0
f73dcfce 518 0, /* SVt_NULL */
519 0, /* SVt_BIND */
520 0, /* SVt_IV */
521 MAYBE_PURIFY(sizeof(NV), sizeof(XPVNV)), /* SVt_NV */
522 0, /* SVt_RV */
523 MAYBE_PURIFY(sizeof(xpv_allocated), sizeof(XPV)), /* SVt_PV */
524 MAYBE_PURIFY(sizeof(xpviv_allocated), sizeof(XPVIV)),/* SVt_PVIV */
525 sizeof(XPVNV), /* SVt_PVNV */
526 sizeof(XPVMG), /* SVt_PVMG */
527 sizeof(XPVGV), /* SVt_PVGV */
528 sizeof(XPVLV), /* SVt_PVLV */
529 MAYBE_PURIFY(sizeof(xpvav_allocated), sizeof(XPVAV)),/* SVt_PVAV */
530 MAYBE_PURIFY(sizeof(xpvhv_allocated), sizeof(XPVHV)),/* SVt_PVHV */
531 MAYBE_PURIFY(sizeof(xpvcv_allocated), sizeof(XPVCV)),/* SVt_PVCV */
532 MAYBE_PURIFY(sizeof(xpvfm_allocated), sizeof(XPVFM)),/* SVt_PVFM */
533 sizeof(XPVIO), /* SVt_PVIO */
b6558d1d 534#elif PERL_VERSION == 10 && PERL_SUBVERSION == 1
f73dcfce 535 0, /* SVt_NULL */
536 0, /* SVt_BIND */
537 0, /* SVt_IV */
538 MAYBE_PURIFY(sizeof(NV), sizeof(XPVNV)), /* SVt_NV */
539 0, /* SVt_RV */
540 sizeof(XPV) - MAYBE_OFFSET(XPV, xpv_cur), /* SVt_PV */
541 sizeof(XPVIV) - MAYBE_OFFSET(XPV, xpv_cur), /* SVt_PVIV */
542 sizeof(XPVNV), /* SVt_PVNV */
543 sizeof(XPVMG), /* SVt_PVMG */
544 sizeof(XPVGV), /* SVt_PVGV */
545 sizeof(XPVLV), /* SVt_PVLV */
546 sizeof(XPVAV) - MAYBE_OFFSET(XPVAV, xav_fill), /* SVt_PVAV */
547 sizeof(XPVHV) - MAYBE_OFFSET(XPVHV, xhv_fill), /* SVt_PVHV */
548 sizeof(XPVCV) - MAYBE_OFFSET(XPVCV, xpv_cur), /* SVt_PVCV */
549 sizeof(XPVFM) - MAYBE_OFFSET(XPVFM, xpv_cur), /* SVt_PVFM */
550 sizeof(XPVIO) /* SVt_PVIO */
b6558d1d 551#elif PERL_VERSION < 13
f73dcfce 552 0, /* SVt_NULL */
553 0, /* SVt_BIND */
554 0, /* SVt_IV */
555 MAYBE_PURIFY(sizeof(NV), sizeof(XPVNV)), /* SVt_NV */
556 sizeof(XPV) - MAYBE_OFFSET(XPV, xpv_cur), /* SVt_PV */
557 sizeof(XPVIV) - MAYBE_OFFSET(XPV, xpv_cur), /* SVt_PVIV */
558 sizeof(XPVNV), /* SVt_PVNV */
559 sizeof(XPVMG), /* SVt_PVMG */
560 sizeof(regexp) - MAYBE_OFFSET(regexp, xpv_cur), /* SVt_REGEXP */
561 sizeof(XPVGV), /* SVt_PVGV */
562 sizeof(XPVLV), /* SVt_PVLV */
563 sizeof(XPVAV) - MAYBE_OFFSET(XPVAV, xav_fill), /* SVt_PVAV */
564 sizeof(XPVHV) - MAYBE_OFFSET(XPVHV, xhv_fill), /* SVt_PVHV */
565 sizeof(XPVCV) - MAYBE_OFFSET(XPVCV, xpv_cur), /* SVt_PVCV */
566 sizeof(XPVFM) - MAYBE_OFFSET(XPVFM, xpv_cur), /* SVt_PVFM */
567 sizeof(XPVIO) /* SVt_PVIO */
b6558d1d 568#else
f73dcfce 569 0, /* SVt_NULL */
570 0, /* SVt_BIND */
571 0, /* SVt_IV */
572 MAYBE_PURIFY(sizeof(NV), sizeof(XPVNV)), /* SVt_NV */
573 sizeof(XPV) - MAYBE_OFFSET(XPV, xpv_cur), /* SVt_PV */
574 sizeof(XPVIV) - MAYBE_OFFSET(XPV, xpv_cur), /* SVt_PVIV */
575 sizeof(XPVNV) - MAYBE_OFFSET(XPV, xpv_cur), /* SVt_PVNV */
576 sizeof(XPVMG), /* SVt_PVMG */
577 sizeof(regexp), /* SVt_REGEXP */
578 sizeof(XPVGV), /* SVt_PVGV */
579 sizeof(XPVLV), /* SVt_PVLV */
580 sizeof(XPVAV), /* SVt_PVAV */
581 sizeof(XPVHV), /* SVt_PVHV */
582 sizeof(XPVCV), /* SVt_PVCV */
583 sizeof(XPVFM), /* SVt_PVFM */
584 sizeof(XPVIO) /* SVt_PVIO */
b6558d1d 585#endif
586};
587
81f1c018 588static bool
db519f11 589sv_size(pTHX_ struct state *const st, const SV * const orig_thing,
f3cf7e20 590 const int recurse) {
9fc9ab86 591 const SV *thing = orig_thing;
b6558d1d 592 U32 type;
eee00145 593
81f1c018 594 if(!check_new(st, thing))
595 return FALSE;
596
b6558d1d 597 type = SvTYPE(thing);
598 if (type > SVt_LAST) {
599 warn("Devel::Size: Unknown variable type: %d encountered\n", type);
600 return TRUE;
601 }
602 st->total_size += sizeof(SV) + body_sizes[type];
b1e5ad85 603
b6558d1d 604 if (type >= SVt_PVMG) {
696b99e2 605 magic_size(aTHX_ thing, st);
606 }
607
b6558d1d 608 switch (type) {
609#if (PERL_VERSION < 11)
e98cedbf 610 /* Is it a reference? */
9fc9ab86 611 case SVt_RV: TAG;
b6558d1d 612#else
613 case SVt_IV: TAG;
24d37977 614#endif
81f1c018 615 if(recurse && SvROK(thing))
f3cf7e20 616 sv_size(aTHX_ st, SvRV_const(thing), recurse);
9fc9ab86 617 TAG;break;
267703fd 618
9fc9ab86 619 case SVt_PVAV: TAG;
e98cedbf 620 /* Is there anything in the array? */
621 if (AvMAX(thing) != -1) {
c8db37d3 622 /* an array with 10 slots has AvMax() set to 9 - te 2007-04-22 */
eee00145 623 st->total_size += sizeof(SV *) * (AvMAX(thing) + 1);
624 dbg_printf(("total_size: %li AvMAX: %li av_len: $i\n", st->total_size, AvMAX(thing), av_len((AV*)thing)));
6c5ddc0d 625
626 if (recurse >= TOTAL_SIZE_RECURSION) {
627 SSize_t i = AvFILLp(thing) + 1;
628
629 while (i--)
630 sv_size(aTHX_ st, AvARRAY(thing)[i], recurse);
631 }
e98cedbf 632 }
633 /* Add in the bits on the other side of the beginning */
0430b7f7 634
b7621729 635 dbg_printf(("total_size %li, sizeof(SV *) %li, AvARRAY(thing) %li, AvALLOC(thing)%li , sizeof(ptr) %li \n",
eee00145 636 st->total_size, sizeof(SV*), AvARRAY(thing), AvALLOC(thing), sizeof( thing )));
0430b7f7 637
638 /* under Perl 5.8.8 64bit threading, AvARRAY(thing) was a pointer while AvALLOC was 0,
b1e5ad85 639 resulting in grossly overstated sized for arrays. Technically, this shouldn't happen... */
0430b7f7 640 if (AvALLOC(thing) != 0) {
eee00145 641 st->total_size += (sizeof(SV *) * (AvARRAY(thing) - AvALLOC(thing)));
0430b7f7 642 }
795fc84c 643#if (PERL_VERSION < 9)
644 /* Is there something hanging off the arylen element?
645 Post 5.9.something this is stored in magic, so will be found there,
646 and Perl_av_arylen_p() takes a non-const AV*, hence compilers rightly
647 complain about AvARYLEN() passing thing to it. */
f3cf7e20 648 sv_size(aTHX_ st, AvARYLEN(thing), recurse);
795fc84c 649#endif
9fc9ab86 650 TAG;break;
651 case SVt_PVHV: TAG;
a6ea0805 652 /* Now the array of buckets */
eee00145 653 st->total_size += (sizeof(HE *) * (HvMAX(thing) + 1));
a6ea0805 654 /* Now walk the bucket chain */
6a9ad7ec 655 if (HvARRAY(thing)) {
a6ea0805 656 HE *cur_entry;
9fc9ab86 657 UV cur_bucket = 0;
a6ea0805 658 for (cur_bucket = 0; cur_bucket <= HvMAX(thing); cur_bucket++) {
9fc9ab86 659 cur_entry = *(HvARRAY(thing) + cur_bucket);
660 while (cur_entry) {
eee00145 661 st->total_size += sizeof(HE);
9fc9ab86 662 if (cur_entry->hent_hek) {
663 /* Hash keys can be shared. Have we seen this before? */
a4efdff3 664 if (check_new(st, cur_entry->hent_hek)) {
eee00145 665 st->total_size += HEK_BASESIZE + cur_entry->hent_hek->hek_len + 2;
9fc9ab86 666 }
667 }
f3cf7e20 668 if (recurse >= TOTAL_SIZE_RECURSION)
669 sv_size(aTHX_ st, HeVAL(cur_entry), recurse);
9fc9ab86 670 cur_entry = cur_entry->hent_next;
671 }
a6ea0805 672 }
673 }
9fc9ab86 674 TAG;break;
267703fd 675
676
677 case SVt_PVFM: TAG;
267703fd 678 sv_size(aTHX_ st, (SV *)CvPADLIST(thing), SOME_RECURSION);
679 sv_size(aTHX_ st, (SV *)CvOUTSIDE(thing), recurse);
680
681 if (st->go_yell && !st->fm_whine) {
682 carp("Devel::Size: Calculated sizes for FMs are incomplete");
683 st->fm_whine = 1;
684 }
685 goto freescalar;
686
9fc9ab86 687 case SVt_PVCV: TAG;
f3cf7e20 688 sv_size(aTHX_ st, (SV *)CvSTASH(thing), SOME_RECURSION);
689 sv_size(aTHX_ st, (SV *)SvSTASH(thing), SOME_RECURSION);
690 sv_size(aTHX_ st, (SV *)CvGV(thing), SOME_RECURSION);
6c5ddc0d 691 sv_size(aTHX_ st, (SV *)CvPADLIST(thing), SOME_RECURSION);
f3cf7e20 692 sv_size(aTHX_ st, (SV *)CvOUTSIDE(thing), recurse);
66f50dda 693 if (CvISXSUB(thing)) {
f3cf7e20 694 sv_size(aTHX_ st, cv_const_sv((CV *)thing), recurse);
66f50dda 695 } else {
1e5a8ad2 696 op_size(aTHX_ CvSTART(thing), st);
697 op_size(aTHX_ CvROOT(thing), st);
7ccc7d88 698 }
267703fd 699 goto freescalar;
700
701 case SVt_PVIO: TAG;
267703fd 702 /* Some embedded char pointers */
703 check_new_and_strlen(st, ((XPVIO *) SvANY(thing))->xio_top_name);
704 check_new_and_strlen(st, ((XPVIO *) SvANY(thing))->xio_fmt_name);
705 check_new_and_strlen(st, ((XPVIO *) SvANY(thing))->xio_bottom_name);
706 /* Throw the GVs on the list to be walked if they're not-null */
707 sv_size(aTHX_ st, (SV *)((XPVIO *) SvANY(thing))->xio_top_gv, recurse);
708 sv_size(aTHX_ st, (SV *)((XPVIO *) SvANY(thing))->xio_bottom_gv, recurse);
709 sv_size(aTHX_ st, (SV *)((XPVIO *) SvANY(thing))->xio_fmt_gv, recurse);
710
711 /* Only go trotting through the IO structures if they're really
712 trottable. If USE_PERLIO is defined we can do this. If
713 not... we can't, so we don't even try */
714#ifdef USE_PERLIO
715 /* Dig into xio_ifp and xio_ofp here */
716 warn("Devel::Size: Can't size up perlio layers yet\n");
717#endif
718 goto freescalar;
719
267703fd 720 case SVt_PVLV: TAG;
267703fd 721#if (PERL_VERSION < 9)
722 goto freescalar;
267703fd 723#endif
7ccc7d88 724
9fc9ab86 725 case SVt_PVGV: TAG;
4a3d023d 726 if(isGV_with_GP(thing)) {
727 st->total_size += GvNAMELEN(thing);
78dfb4e7 728#ifdef GvFILE
2b217e71 729# if !defined(USE_ITHREADS) || (PERL_VERSION > 8 || (PERL_VERSION == 8 && PERL_SUBVERSION > 8))
730 /* With itreads, before 5.8.9, this can end up pointing to freed memory
731 if the GV was created in an eval, as GvFILE() points to CopFILE(),
732 and the relevant COP has been freed on scope cleanup after the eval.
733 5.8.9 adds a binary compatible fudge that catches the vast majority
734 of cases. 5.9.something added a proper fix, by converting the GP to
735 use a shared hash key (porperly reference counted), instead of a
736 char * (owned by who knows? possibly no-one now) */
4a3d023d 737 check_new_and_strlen(st, GvFILE(thing));
2b217e71 738# endif
78dfb4e7 739#endif
4a3d023d 740 /* Is there something hanging off the glob? */
741 if (check_new(st, GvGP(thing))) {
742 st->total_size += sizeof(GP);
f3cf7e20 743 sv_size(aTHX_ st, (SV *)(GvGP(thing)->gp_sv), recurse);
744 sv_size(aTHX_ st, (SV *)(GvGP(thing)->gp_form), recurse);
745 sv_size(aTHX_ st, (SV *)(GvGP(thing)->gp_av), recurse);
746 sv_size(aTHX_ st, (SV *)(GvGP(thing)->gp_hv), recurse);
747 sv_size(aTHX_ st, (SV *)(GvGP(thing)->gp_egv), recurse);
748 sv_size(aTHX_ st, (SV *)(GvGP(thing)->gp_cv), recurse);
4a3d023d 749 }
267703fd 750#if (PERL_VERSION >= 9)
751 TAG; break;
752#endif
5c2e1b12 753 }
b6558d1d 754#if PERL_VERSION <= 8
755 case SVt_PVBM: TAG;
756#endif
267703fd 757 case SVt_PVMG: TAG;
267703fd 758 case SVt_PVNV: TAG;
267703fd 759 case SVt_PVIV: TAG;
267703fd 760 case SVt_PV: TAG;
267703fd 761 freescalar:
762 if(recurse && SvROK(thing))
763 sv_size(aTHX_ st, SvRV_const(thing), recurse);
764 else
765 st->total_size += SvLEN(thing);
766
767 if(SvOOK(thing)) {
768 st->total_size += SvIVX(thing);
ebb2c5b9 769 }
9fc9ab86 770 TAG;break;
5073b933 771
e98cedbf 772 }
81f1c018 773 return TRUE;
e98cedbf 774}
775
ec404c23 776/* Frustratingly, the vtables aren't const in perl.h
777 gcc is happy enough to have non-const initialisers in a static array.
778 VC seems not to be. (Is it actually treating the file as C++?)
779 So do the maximally portable thing, unless we know it's gcc, in which case
780 we can do the more space efficient version. */
781
782#if __GNUC__
d9b022a1 783void *vtables[] = {
784#include "vtables.inc"
785 NULL
786};
ec404c23 787#endif
d9b022a1 788
a4efdff3 789static struct state *
790new_state(pTHX)
65db36c0 791{
792 SV *warn_flag;
a4efdff3 793 struct state *st;
ec404c23 794#if __GNUC__
d9b022a1 795 void **vt_p = vtables;
ec404c23 796#endif
d9b022a1 797
a4efdff3 798 Newxz(st, 1, struct state);
799 st->go_yell = TRUE;
65db36c0 800 if (NULL != (warn_flag = perl_get_sv("Devel::Size::warn", FALSE))) {
a4efdff3 801 st->dangle_whine = st->go_yell = SvIV(warn_flag) ? TRUE : FALSE;
65db36c0 802 }
803 if (NULL != (warn_flag = perl_get_sv("Devel::Size::dangle", FALSE))) {
a4efdff3 804 st->dangle_whine = SvIV(warn_flag) ? TRUE : FALSE;
65db36c0 805 }
a52ceccd 806 check_new(st, &PL_sv_undef);
807 check_new(st, &PL_sv_no);
808 check_new(st, &PL_sv_yes);
ec404c23 809#if __GNUC__
d9b022a1 810 while(*vt_p)
811 check_new(st, *vt_p++);
ec404c23 812#else
813#include "vtables.inc"
814#endif
a4efdff3 815 return st;
65db36c0 816}
817
9fc9ab86 818MODULE = Devel::Size PACKAGE = Devel::Size
e98cedbf 819
fea63ffa 820PROTOTYPES: DISABLE
821
eee00145 822UV
a6ea0805 823size(orig_thing)
824 SV *orig_thing
13683e3a 825ALIAS:
826 total_size = TOTAL_SIZE_RECURSION
e98cedbf 827CODE:
828{
6a9ad7ec 829 SV *thing = orig_thing;
a4efdff3 830 struct state *st = new_state(aTHX);
ebb2c5b9 831
6a9ad7ec 832 /* If they passed us a reference then dereference it. This is the
833 only way we can check the sizes of arrays and hashes */
b7621729 834 if (SvROK(thing)) {
835 thing = SvRV(thing);
836 }
b7621729 837
13683e3a 838 sv_size(aTHX_ st, thing, ix);
eee00145 839 RETVAL = st->total_size;
a4efdff3 840 free_state(st);
6a9ad7ec 841}
842OUTPUT:
843 RETVAL