Merge the 4 static variables and TRACKING into a struct state.
[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
12
9fc9ab86 13#ifdef _MSC_VER
1a36ac09 14/* "structured exception" handling is a Microsoft extension to C and C++.
15 It's *not* C++ exception handling - C++ exception handling can't capture
16 SEGVs and suchlike, whereas this can. There's no known analagous
17 functionality on other platforms. */
18# include <excpt.h>
19# define TRY_TO_CATCH_SEGV __try
20# define CAUGHT_EXCEPTION __except(EXCEPTION EXCEPTION_EXECUTE_HANDLER)
9fc9ab86 21#else
1a36ac09 22# define TRY_TO_CATCH_SEGV if(1)
23# define CAUGHT_EXCEPTION else
9fc9ab86 24#endif
25
26#ifdef __GNUC__
27# define __attribute__(x)
28#endif
29
b7621729 30#if 0 && defined(DEBUGGING)
31#define dbg_printf(x) printf x
32#else
33#define dbg_printf(x)
34#endif
98ecbbc6 35
0964064b 36#define TAG /* printf( "# %s(%d)\n", __FILE__, __LINE__ ) */
6a9ad7ec 37#define carp puts
9fc9ab86 38
30fe4f47 39/* The idea is to have a tree structure to store 1 bit per possible pointer
40 address. The lowest 16 bits are stored in a block of 8092 bytes.
41 The blocks are in a 256-way tree, indexed by the reset of the pointer.
42 This can cope with 32 and 64 bit pointers, and any address space layout,
43 without excessive memory needs. The assumption is that your CPU cache
44 works :-) (And that we're not going to bust it) */
45
9fc9ab86 46#define ALIGN_BITS ( sizeof(void*) >> 1 )
30fe4f47 47#define BYTE_BITS 3
48#define LEAF_BITS (16 - BYTE_BITS)
49#define LEAF_MASK 0x1FFF
9fc9ab86 50
65db36c0 51struct state {
52 bool regex_whine;
53 bool fm_whine;
54 bool dangle_whine;
55 bool go_yell;
56 /* My hunch (not measured) is that for most architectures pointers will
57 start with 0 bits, hence the start of this array will be hot, and the
58 end unused. So put the flags next to the hot end. */
59 void *tracking[256];
60};
61
62typedef struct state TRACKING;
9fc9ab86 63
64/*
65 Checks to see if thing is in the bitstring.
66 Returns true or false, and
67 notes thing in the segmented bitstring.
68 */
2eb93d08 69static bool
70check_new(TRACKING *tv, const void *const p) {
30fe4f47 71 unsigned int bits = 8 * sizeof(void*);
72 const size_t raw_p = PTR2nat(p);
73 /* This effectively rotates the value right by the number of low always-0
74 bits in an aligned pointer. The assmption is that most (if not all)
75 pointers are aligned, and these will be in the same chain of nodes
76 (and hence hot in the cache) but we can still deal with any unaligned
77 pointers. */
78 const size_t cooked_p
79 = (raw_p >> ALIGN_BITS) | (raw_p << (bits - BYTE_BITS));
80 const U8 this_bit = 1 << (cooked_p & 0x7);
81 U8 **leaf_p;
82 U8 *leaf;
83 unsigned int i;
65db36c0 84 void **tv_p = (void **) (tv->tracking);
30fe4f47 85
7089796d 86 assert(tv);
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 {
65db36c0 92 if (tv->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 */
110 assert(bits == 16);
111 leaf_p = (U8 **)tv_p;
112 i = (unsigned int)((cooked_p >> bits) & 0xFF);
113 if (!leaf_p[i])
114 Newxz(leaf_p[i], 1 << LEAF_BITS, U8);
115 leaf = leaf_p[i];
116
9fc9ab86 117 TAG;
30fe4f47 118
119 i = (unsigned int)((cooked_p >> BYTE_BITS) & LEAF_MASK);
120
121 if(leaf[i] & this_bit)
122 return FALSE;
123
124 leaf[i] |= this_bit;
9fc9ab86 125 return TRUE;
126}
127
e9716740 128static void
30fe4f47 129free_tracking_at(void **tv, int level)
130{
131 int i = 255;
132
133 if (--level) {
134 /* Nodes */
135 do {
136 if (tv[i]) {
137 free_tracking_at(tv[i], level);
138 Safefree(tv[i]);
139 }
140 } while (i--);
141 } else {
142 /* Leaves */
143 do {
144 if (tv[i])
145 Safefree(tv[i]);
146 } while (i--);
147 }
148}
149
150static void
e9716740 151free_tracking(TRACKING *tv)
152{
30fe4f47 153 const int top_level = (sizeof(void *) * 8 - LEAF_BITS - BYTE_BITS) / 8;
65db36c0 154 free_tracking_at((void **)tv->tracking, top_level);
30fe4f47 155 Safefree(tv);
e9716740 156}
157
265a0548 158static UV thing_size(pTHX_ const SV *const, TRACKING *);
7ccc7d88 159typedef enum {
9fc9ab86 160 OPc_NULL, /* 0 */
161 OPc_BASEOP, /* 1 */
162 OPc_UNOP, /* 2 */
163 OPc_BINOP, /* 3 */
164 OPc_LOGOP, /* 4 */
165 OPc_LISTOP, /* 5 */
166 OPc_PMOP, /* 6 */
167 OPc_SVOP, /* 7 */
168 OPc_PADOP, /* 8 */
169 OPc_PVOP, /* 9 */
170 OPc_LOOP, /* 10 */
171 OPc_COP /* 11 */
7ccc7d88 172} opclass;
173
174static opclass
9fc9ab86 175cc_opclass(const OP * const o)
7ccc7d88 176{
177 if (!o)
9fc9ab86 178 return OPc_NULL;
1a36ac09 179 TRY_TO_CATCH_SEGV {
9fc9ab86 180 if (o->op_type == 0)
181 return (o->op_flags & OPf_KIDS) ? OPc_UNOP : OPc_BASEOP;
7ccc7d88 182
9fc9ab86 183 if (o->op_type == OP_SASSIGN)
184 return ((o->op_private & OPpASSIGN_BACKWARDS) ? OPc_UNOP : OPc_BINOP);
7ccc7d88 185
9fc9ab86 186 #ifdef USE_ITHREADS
187 if (o->op_type == OP_GV || o->op_type == OP_GVSV || o->op_type == OP_AELEMFAST)
188 return OPc_PADOP;
189 #endif
7ccc7d88 190
9fc9ab86 191 if ((o->op_type == OP_TRANS)) {
192 return OPc_BASEOP;
193 }
7ccc7d88 194
9fc9ab86 195 switch (PL_opargs[o->op_type] & OA_CLASS_MASK) {
196 case OA_BASEOP: TAG;
197 return OPc_BASEOP;
198
199 case OA_UNOP: TAG;
200 return OPc_UNOP;
201
202 case OA_BINOP: TAG;
203 return OPc_BINOP;
62691e7c 204
9fc9ab86 205 case OA_LOGOP: TAG;
206 return OPc_LOGOP;
7ccc7d88 207
9fc9ab86 208 case OA_LISTOP: TAG;
209 return OPc_LISTOP;
7ccc7d88 210
9fc9ab86 211 case OA_PMOP: TAG;
212 return OPc_PMOP;
7ccc7d88 213
9fc9ab86 214 case OA_SVOP: TAG;
215 return OPc_SVOP;
7ccc7d88 216
9fc9ab86 217 case OA_PADOP: TAG;
218 return OPc_PADOP;
7ccc7d88 219
9fc9ab86 220 case OA_PVOP_OR_SVOP: TAG;
221 /*
222 * Character translations (tr///) are usually a PVOP, keeping a
223 * pointer to a table of shorts used to look up translations.
224 * Under utf8, however, a simple table isn't practical; instead,
225 * the OP is an SVOP, and the SV is a reference to a swash
226 * (i.e., an RV pointing to an HV).
227 */
228 return (o->op_private & (OPpTRANS_TO_UTF|OPpTRANS_FROM_UTF))
229 ? OPc_SVOP : OPc_PVOP;
7ccc7d88 230
9fc9ab86 231 case OA_LOOP: TAG;
232 return OPc_LOOP;
7ccc7d88 233
9fc9ab86 234 case OA_COP: TAG;
235 return OPc_COP;
7ccc7d88 236
9fc9ab86 237 case OA_BASEOP_OR_UNOP: TAG;
7ccc7d88 238 /*
9fc9ab86 239 * UNI(OP_foo) in toke.c returns token UNI or FUNC1 depending on
240 * whether parens were seen. perly.y uses OPf_SPECIAL to
241 * signal whether a BASEOP had empty parens or none.
242 * Some other UNOPs are created later, though, so the best
243 * test is OPf_KIDS, which is set in newUNOP.
7ccc7d88 244 */
9fc9ab86 245 return (o->op_flags & OPf_KIDS) ? OPc_UNOP : OPc_BASEOP;
246
247 case OA_FILESTATOP: TAG;
248 /*
249 * The file stat OPs are created via UNI(OP_foo) in toke.c but use
250 * the OPf_REF flag to distinguish between OP types instead of the
251 * usual OPf_SPECIAL flag. As usual, if OPf_KIDS is set, then we
252 * return OPc_UNOP so that walkoptree can find our children. If
253 * OPf_KIDS is not set then we check OPf_REF. Without OPf_REF set
254 * (no argument to the operator) it's an OP; with OPf_REF set it's
255 * an SVOP (and op_sv is the GV for the filehandle argument).
256 */
257 return ((o->op_flags & OPf_KIDS) ? OPc_UNOP :
258 #ifdef USE_ITHREADS
259 (o->op_flags & OPf_REF) ? OPc_PADOP : OPc_BASEOP);
260 #else
261 (o->op_flags & OPf_REF) ? OPc_SVOP : OPc_BASEOP);
262 #endif
263 case OA_LOOPEXOP: TAG;
264 /*
265 * next, last, redo, dump and goto use OPf_SPECIAL to indicate that a
266 * label was omitted (in which case it's a BASEOP) or else a term was
267 * seen. In this last case, all except goto are definitely PVOP but
268 * goto is either a PVOP (with an ordinary constant label), an UNOP
269 * with OPf_STACKED (with a non-constant non-sub) or an UNOP for
270 * OP_REFGEN (with goto &sub) in which case OPf_STACKED also seems to
271 * get set.
272 */
273 if (o->op_flags & OPf_STACKED)
274 return OPc_UNOP;
275 else if (o->op_flags & OPf_SPECIAL)
276 return OPc_BASEOP;
277 else
278 return OPc_PVOP;
279 }
280 warn("Devel::Size: Can't determine class of operator %s, assuming BASEOP\n",
281 PL_op_name[o->op_type]);
282 }
1a36ac09 283 CAUGHT_EXCEPTION { }
7ccc7d88 284 return OPc_BASEOP;
285}
286
6a9ad7ec 287
a6ea0805 288#if !defined(NV)
289#define NV double
290#endif
291
6a9ad7ec 292/* Figure out how much magic is attached to the SV and return the
293 size */
9fc9ab86 294IV magic_size(const SV * const thing, TRACKING *tv) {
6a9ad7ec 295 IV total_size = 0;
296 MAGIC *magic_pointer;
297
298 /* Is there any? */
299 if (!SvMAGIC(thing)) {
300 /* No, bail */
301 return 0;
302 }
303
304 /* Get the base magic pointer */
305 magic_pointer = SvMAGIC(thing);
306
307 /* Have we seen the magic pointer? */
9fc9ab86 308 while (magic_pointer && check_new(tv, magic_pointer)) {
6a9ad7ec 309 total_size += sizeof(MAGIC);
310
1a36ac09 311 TRY_TO_CATCH_SEGV {
9fc9ab86 312 /* Have we seen the magic vtable? */
313 if (magic_pointer->mg_virtual &&
314 check_new(tv, magic_pointer->mg_virtual)) {
315 total_size += sizeof(MGVTBL);
316 }
6a9ad7ec 317
0964064b 318 /* Get the next in the chain */
9fc9ab86 319 magic_pointer = magic_pointer->mg_moremagic;
320 }
1a36ac09 321 CAUGHT_EXCEPTION {
65db36c0 322 if (tv->dangle_whine)
9fc9ab86 323 warn( "Devel::Size: Encountered bad magic at: %p\n", magic_pointer );
324 }
6a9ad7ec 325 }
6a9ad7ec 326 return total_size;
327}
328
9fc9ab86 329UV regex_size(const REGEXP * const baseregex, TRACKING *tv) {
7ccc7d88 330 UV total_size = 0;
331
98ecbbc6 332 total_size += sizeof(REGEXP);
9fc9ab86 333#if (PERL_VERSION < 11)
6ea94d90 334 /* Note the size of the paren offset thing */
98ecbbc6 335 total_size += sizeof(I32) * baseregex->nparens * 2;
336 total_size += strlen(baseregex->precomp);
6ea94d90 337#else
338 total_size += sizeof(struct regexp);
339 total_size += sizeof(I32) * SvANY(baseregex)->nparens * 2;
340 /*total_size += strlen(SvANY(baseregex)->subbeg);*/
341#endif
65db36c0 342 if (tv->go_yell && !tv->regex_whine) {
6ea94d90 343 carp("Devel::Size: Calculated sizes for compiled regexes are incompatible, and probably always will be");
65db36c0 344 tv->regex_whine = 1;
98ecbbc6 345 }
346
7ccc7d88 347 return total_size;
348}
349
265a0548 350static UV
351op_size(pTHX_ const OP * const baseop, TRACKING *tv) {
7ccc7d88 352 UV total_size = 0;
1a36ac09 353 TRY_TO_CATCH_SEGV {
9fc9ab86 354 TAG;
355 if (check_new(tv, baseop->op_next)) {
265a0548 356 total_size += op_size(aTHX_ baseop->op_next, tv);
9fc9ab86 357 }
358 TAG;
359 switch (cc_opclass(baseop)) {
360 case OPc_BASEOP: TAG;
361 total_size += sizeof(struct op);
362 TAG;break;
363 case OPc_UNOP: TAG;
364 total_size += sizeof(struct unop);
365 if (check_new(tv, cUNOPx(baseop)->op_first)) {
265a0548 366 total_size += op_size(aTHX_ cUNOPx(baseop)->op_first, tv);
9fc9ab86 367 }
368 TAG;break;
369 case OPc_BINOP: TAG;
370 total_size += sizeof(struct binop);
371 if (check_new(tv, cBINOPx(baseop)->op_first)) {
265a0548 372 total_size += op_size(aTHX_ cBINOPx(baseop)->op_first, tv);
9fc9ab86 373 }
374 if (check_new(tv, cBINOPx(baseop)->op_last)) {
265a0548 375 total_size += op_size(aTHX_ cBINOPx(baseop)->op_last, tv);
9fc9ab86 376 }
377 TAG;break;
378 case OPc_LOGOP: TAG;
379 total_size += sizeof(struct logop);
380 if (check_new(tv, cLOGOPx(baseop)->op_first)) {
265a0548 381 total_size += op_size(aTHX_ cBINOPx(baseop)->op_first, tv);
9fc9ab86 382 }
383 if (check_new(tv, cLOGOPx(baseop)->op_other)) {
265a0548 384 total_size += op_size(aTHX_ cLOGOPx(baseop)->op_other, tv);
9fc9ab86 385 }
386 TAG;break;
387 case OPc_LISTOP: TAG;
388 total_size += sizeof(struct listop);
389 if (check_new(tv, cLISTOPx(baseop)->op_first)) {
265a0548 390 total_size += op_size(aTHX_ cLISTOPx(baseop)->op_first, tv);
9fc9ab86 391 }
392 if (check_new(tv, cLISTOPx(baseop)->op_last)) {
265a0548 393 total_size += op_size(aTHX_ cLISTOPx(baseop)->op_last, tv);
9fc9ab86 394 }
395 TAG;break;
396 case OPc_PMOP: TAG;
397 total_size += sizeof(struct pmop);
398 if (check_new(tv, cPMOPx(baseop)->op_first)) {
265a0548 399 total_size += op_size(aTHX_ cPMOPx(baseop)->op_first, tv);
9fc9ab86 400 }
401 if (check_new(tv, cPMOPx(baseop)->op_last)) {
265a0548 402 total_size += op_size(aTHX_ cPMOPx(baseop)->op_last, tv);
9fc9ab86 403 }
5a83b7cf 404#if PERL_VERSION < 9 || (PERL_VERSION == 9 && PERL_SUBVERSION < 5)
9fc9ab86 405 if (check_new(tv, cPMOPx(baseop)->op_pmreplroot)) {
265a0548 406 total_size += op_size(aTHX_ cPMOPx(baseop)->op_pmreplroot, tv);
9fc9ab86 407 }
408 if (check_new(tv, cPMOPx(baseop)->op_pmreplstart)) {
265a0548 409 total_size += op_size(aTHX_ cPMOPx(baseop)->op_pmreplstart, tv);
9fc9ab86 410 }
411 if (check_new(tv, cPMOPx(baseop)->op_pmnext)) {
265a0548 412 total_size += op_size(aTHX_ (OP *)cPMOPx(baseop)->op_pmnext, tv);
9fc9ab86 413 }
5a83b7cf 414#endif
9fc9ab86 415 /* This is defined away in perl 5.8.x, but it is in there for
416 5.6.x */
98ecbbc6 417#ifdef PM_GETRE
9fc9ab86 418 if (check_new(tv, PM_GETRE((cPMOPx(baseop))))) {
419 total_size += regex_size(PM_GETRE(cPMOPx(baseop)), tv);
420 }
98ecbbc6 421#else
9fc9ab86 422 if (check_new(tv, cPMOPx(baseop)->op_pmregexp)) {
423 total_size += regex_size(cPMOPx(baseop)->op_pmregexp, tv);
424 }
98ecbbc6 425#endif
9fc9ab86 426 TAG;break;
427 case OPc_SVOP: TAG;
428 total_size += sizeof(struct pmop);
429 if (check_new(tv, cSVOPx(baseop)->op_sv)) {
265a0548 430 total_size += thing_size(aTHX_ cSVOPx(baseop)->op_sv, tv);
9fc9ab86 431 }
432 TAG;break;
433 case OPc_PADOP: TAG;
434 total_size += sizeof(struct padop);
435 TAG;break;
436 case OPc_PVOP: TAG;
437 if (check_new(tv, cPVOPx(baseop)->op_pv)) {
438 total_size += strlen(cPVOPx(baseop)->op_pv);
439 }
440 case OPc_LOOP: TAG;
441 total_size += sizeof(struct loop);
442 if (check_new(tv, cLOOPx(baseop)->op_first)) {
265a0548 443 total_size += op_size(aTHX_ cLOOPx(baseop)->op_first, tv);
9fc9ab86 444 }
445 if (check_new(tv, cLOOPx(baseop)->op_last)) {
265a0548 446 total_size += op_size(aTHX_ cLOOPx(baseop)->op_last, tv);
9fc9ab86 447 }
448 if (check_new(tv, cLOOPx(baseop)->op_redoop)) {
265a0548 449 total_size += op_size(aTHX_ cLOOPx(baseop)->op_redoop, tv);
9fc9ab86 450 }
451 if (check_new(tv, cLOOPx(baseop)->op_nextop)) {
265a0548 452 total_size += op_size(aTHX_ cLOOPx(baseop)->op_nextop, tv);
9fc9ab86 453 }
454 if (check_new(tv, cLOOPx(baseop)->op_lastop)) {
265a0548 455 total_size += op_size(aTHX_ cLOOPx(baseop)->op_lastop, tv);
9fc9ab86 456 }
457
458 TAG;break;
459 case OPc_COP: TAG;
460 {
461 COP *basecop;
462 basecop = (COP *)baseop;
463 total_size += sizeof(struct cop);
464
465 /* Change 33656 by nicholas@mouse-mill on 2008/04/07 11:29:51
466 Eliminate cop_label from struct cop by storing a label as the first
467 entry in the hints hash. Most statements don't have labels, so this
468 will save memory. Not sure how much.
469 The check below will be incorrect fail on bleadperls
470 before 5.11 @33656, but later than 5.10, producing slightly too
471 small memory sizes on these Perls. */
b7621729 472#if (PERL_VERSION < 11)
9fc9ab86 473 if (check_new(tv, basecop->cop_label)) {
474 total_size += strlen(basecop->cop_label);
475 }
b7621729 476#endif
7ccc7d88 477#ifdef USE_ITHREADS
9fc9ab86 478 if (check_new(tv, basecop->cop_file)) {
479 total_size += strlen(basecop->cop_file);
480 }
481 if (check_new(tv, basecop->cop_stashpv)) {
482 total_size += strlen(basecop->cop_stashpv);
483 }
7ccc7d88 484#else
9fc9ab86 485 if (check_new(tv, basecop->cop_stash)) {
265a0548 486 total_size += thing_size(aTHX_ (SV *)basecop->cop_stash, tv);
9fc9ab86 487 }
488 if (check_new(tv, basecop->cop_filegv)) {
265a0548 489 total_size += thing_size(aTHX_ (SV *)basecop->cop_filegv, tv);
9fc9ab86 490 }
7ccc7d88 491#endif
492
9fc9ab86 493 }
494 TAG;break;
495 default:
496 TAG;break;
497 }
498 }
1a36ac09 499 CAUGHT_EXCEPTION {
65db36c0 500 if (tv->dangle_whine)
9fc9ab86 501 warn( "Devel::Size: Encountered dangling pointer in opcode at: %p\n", baseop );
7ccc7d88 502 }
503 return total_size;
504}
6a9ad7ec 505
24d37977 506#if PERL_VERSION > 9 || (PERL_VERSION == 9 && PERL_SUBVERSION > 2)
507# define NEW_HEAD_LAYOUT
508#endif
509
265a0548 510static UV
511thing_size(pTHX_ const SV * const orig_thing, TRACKING *tv) {
9fc9ab86 512 const SV *thing = orig_thing;
e98cedbf 513 UV total_size = sizeof(SV);
b1e5ad85 514
e98cedbf 515 switch (SvTYPE(thing)) {
516 /* Is it undef? */
9fc9ab86 517 case SVt_NULL: TAG;
518 TAG;break;
e98cedbf 519 /* Just a plain integer. This will be differently sized depending
520 on whether purify's been compiled in */
9fc9ab86 521 case SVt_IV: TAG;
24d37977 522#ifndef NEW_HEAD_LAYOUT
523# ifdef PURIFY
e98cedbf 524 total_size += sizeof(sizeof(XPVIV));
24d37977 525# else
e98cedbf 526 total_size += sizeof(IV);
24d37977 527# endif
e98cedbf 528#endif
9fc9ab86 529 TAG;break;
e98cedbf 530 /* Is it a float? Like the int, it depends on purify */
9fc9ab86 531 case SVt_NV: TAG;
e98cedbf 532#ifdef PURIFY
533 total_size += sizeof(sizeof(XPVNV));
534#else
535 total_size += sizeof(NV);
536#endif
9fc9ab86 537 TAG;break;
538#if (PERL_VERSION < 11)
e98cedbf 539 /* Is it a reference? */
9fc9ab86 540 case SVt_RV: TAG;
24d37977 541#ifndef NEW_HEAD_LAYOUT
e98cedbf 542 total_size += sizeof(XRV);
24d37977 543#endif
9fc9ab86 544 TAG;break;
6ea94d90 545#endif
e98cedbf 546 /* How about a plain string? In which case we need to add in how
547 much has been allocated */
9fc9ab86 548 case SVt_PV: TAG;
e98cedbf 549 total_size += sizeof(XPV);
b7621729 550#if (PERL_VERSION < 11)
265a0548 551 total_size += SvROK(thing) ? thing_size(aTHX_ SvRV(thing), tv) : SvLEN(thing);
b7621729 552#else
553 total_size += SvLEN(thing);
554#endif
9fc9ab86 555 TAG;break;
e98cedbf 556 /* A string with an integer part? */
9fc9ab86 557 case SVt_PVIV: TAG;
e98cedbf 558 total_size += sizeof(XPVIV);
b7621729 559#if (PERL_VERSION < 11)
265a0548 560 total_size += SvROK(thing) ? thing_size(aTHX_ SvRV(thing), tv) : SvLEN(thing);
b7621729 561#else
562 total_size += SvLEN(thing);
563#endif
0430b7f7 564 if(SvOOK(thing)) {
565 total_size += SvIVX(thing);
9fc9ab86 566 }
567 TAG;break;
c8db37d3 568 /* A scalar/string/reference with a float part? */
9fc9ab86 569 case SVt_PVNV: TAG;
e98cedbf 570 total_size += sizeof(XPVNV);
b7621729 571#if (PERL_VERSION < 11)
265a0548 572 total_size += SvROK(thing) ? thing_size(aTHX_ SvRV(thing), tv) : SvLEN(thing);
b7621729 573#else
574 total_size += SvLEN(thing);
575#endif
9fc9ab86 576 TAG;break;
577 case SVt_PVMG: TAG;
4ab42718 578 total_size += sizeof(XPVMG);
b7621729 579#if (PERL_VERSION < 11)
265a0548 580 total_size += SvROK(thing) ? thing_size(aTHX_ SvRV(thing), tv) : SvLEN(thing);
b7621729 581#else
582 total_size += SvLEN(thing);
583#endif
9fc9ab86 584 total_size += magic_size(thing, tv);
585 TAG;break;
0430b7f7 586#if PERL_VERSION <= 8
9fc9ab86 587 case SVt_PVBM: TAG;
6a9ad7ec 588 total_size += sizeof(XPVBM);
b7621729 589#if (PERL_VERSION < 11)
265a0548 590 total_size += SvROK(thing) ? thing_size(aTHX_ SvRV(thing), tv) : SvLEN(thing);
b7621729 591#else
592 total_size += SvLEN(thing);
593#endif
9fc9ab86 594 total_size += magic_size(thing, tv);
595 TAG;break;
0430b7f7 596#endif
9fc9ab86 597 case SVt_PVLV: TAG;
6a9ad7ec 598 total_size += sizeof(XPVLV);
b7621729 599#if (PERL_VERSION < 11)
265a0548 600 total_size += SvROK(thing) ? thing_size(aTHX_ SvRV(thing), tv) : SvLEN(thing);
b7621729 601#else
602 total_size += SvLEN(thing);
603#endif
9fc9ab86 604 total_size += magic_size(thing, tv);
605 TAG;break;
e98cedbf 606 /* How much space is dedicated to the array? Not counting the
607 elements in the array, mind, just the array itself */
9fc9ab86 608 case SVt_PVAV: TAG;
e98cedbf 609 total_size += sizeof(XPVAV);
610 /* Is there anything in the array? */
611 if (AvMAX(thing) != -1) {
c8db37d3 612 /* an array with 10 slots has AvMax() set to 9 - te 2007-04-22 */
613 total_size += sizeof(SV *) * (AvMAX(thing) + 1);
b7621729 614 dbg_printf(("total_size: %li AvMAX: %li av_len: $i\n", total_size, AvMAX(thing), av_len((AV*)thing)));
e98cedbf 615 }
616 /* Add in the bits on the other side of the beginning */
0430b7f7 617
b7621729 618 dbg_printf(("total_size %li, sizeof(SV *) %li, AvARRAY(thing) %li, AvALLOC(thing)%li , sizeof(ptr) %li \n",
9fc9ab86 619 total_size, sizeof(SV*), AvARRAY(thing), AvALLOC(thing), sizeof( thing )));
0430b7f7 620
621 /* under Perl 5.8.8 64bit threading, AvARRAY(thing) was a pointer while AvALLOC was 0,
b1e5ad85 622 resulting in grossly overstated sized for arrays. Technically, this shouldn't happen... */
0430b7f7 623 if (AvALLOC(thing) != 0) {
624 total_size += (sizeof(SV *) * (AvARRAY(thing) - AvALLOC(thing)));
625 }
795fc84c 626#if (PERL_VERSION < 9)
627 /* Is there something hanging off the arylen element?
628 Post 5.9.something this is stored in magic, so will be found there,
629 and Perl_av_arylen_p() takes a non-const AV*, hence compilers rightly
630 complain about AvARYLEN() passing thing to it. */
e98cedbf 631 if (AvARYLEN(thing)) {
9fc9ab86 632 if (check_new(tv, AvARYLEN(thing))) {
265a0548 633 total_size += thing_size(aTHX_ AvARYLEN(thing), tv);
6a9ad7ec 634 }
e98cedbf 635 }
795fc84c 636#endif
9fc9ab86 637 total_size += magic_size(thing, tv);
638 TAG;break;
639 case SVt_PVHV: TAG;
a6ea0805 640 /* First the base struct */
641 total_size += sizeof(XPVHV);
642 /* Now the array of buckets */
643 total_size += (sizeof(HE *) * (HvMAX(thing) + 1));
644 /* Now walk the bucket chain */
6a9ad7ec 645 if (HvARRAY(thing)) {
a6ea0805 646 HE *cur_entry;
9fc9ab86 647 UV cur_bucket = 0;
a6ea0805 648 for (cur_bucket = 0; cur_bucket <= HvMAX(thing); cur_bucket++) {
9fc9ab86 649 cur_entry = *(HvARRAY(thing) + cur_bucket);
650 while (cur_entry) {
651 total_size += sizeof(HE);
652 if (cur_entry->hent_hek) {
653 /* Hash keys can be shared. Have we seen this before? */
654 if (check_new(tv, cur_entry->hent_hek)) {
655 total_size += HEK_BASESIZE + cur_entry->hent_hek->hek_len + 2;
656 }
657 }
658 cur_entry = cur_entry->hent_next;
659 }
a6ea0805 660 }
661 }
9fc9ab86 662 total_size += magic_size(thing, tv);
663 TAG;break;
664 case SVt_PVCV: TAG;
6a9ad7ec 665 total_size += sizeof(XPVCV);
9fc9ab86 666 total_size += magic_size(thing, tv);
7ccc7d88 667
668 total_size += ((XPVIO *) SvANY(thing))->xpv_len;
9fc9ab86 669 if (check_new(tv, CvSTASH(thing))) {
265a0548 670 total_size += thing_size(aTHX_ (SV *)CvSTASH(thing), tv);
7ccc7d88 671 }
9fc9ab86 672 if (check_new(tv, SvSTASH(thing))) {
265a0548 673 total_size += thing_size(aTHX_ (SV *)SvSTASH(thing), tv);
7ccc7d88 674 }
9fc9ab86 675 if (check_new(tv, CvGV(thing))) {
265a0548 676 total_size += thing_size(aTHX_ (SV *)CvGV(thing), tv);
ebb2c5b9 677 }
9fc9ab86 678 if (check_new(tv, CvPADLIST(thing))) {
265a0548 679 total_size += thing_size(aTHX_ (SV *)CvPADLIST(thing), tv);
7ccc7d88 680 }
9fc9ab86 681 if (check_new(tv, CvOUTSIDE(thing))) {
265a0548 682 total_size += thing_size(aTHX_ (SV *)CvOUTSIDE(thing), tv);
7ccc7d88 683 }
66f50dda 684 if (CvISXSUB(thing)) {
685 SV *sv = cv_const_sv((CV *)thing);
686 if (sv) {
265a0548 687 total_size += thing_size(aTHX_ sv, tv);
66f50dda 688 }
689 } else {
87372f42 690 if (check_new(tv, CvSTART(thing))) {
265a0548 691 total_size += op_size(aTHX_ CvSTART(thing), tv);
87372f42 692 }
693 if (check_new(tv, CvROOT(thing))) {
265a0548 694 total_size += op_size(aTHX_ CvROOT(thing), tv);
87372f42 695 }
7ccc7d88 696 }
697
9fc9ab86 698 TAG;break;
699 case SVt_PVGV: TAG;
700 total_size += magic_size(thing, tv);
6a9ad7ec 701 total_size += sizeof(XPVGV);
5c2e1b12 702 total_size += GvNAMELEN(thing);
78dfb4e7 703#ifdef GvFILE
0bff12d8 704 /* Is there a file? */
705 if (GvFILE(thing)) {
9fc9ab86 706 if (check_new(tv, GvFILE(thing))) {
707 total_size += strlen(GvFILE(thing));
0bff12d8 708 }
709 }
78dfb4e7 710#endif
5c2e1b12 711 /* Is there something hanging off the glob? */
712 if (GvGP(thing)) {
9fc9ab86 713 if (check_new(tv, GvGP(thing))) {
714 total_size += sizeof(GP);
715 {
716 SV *generic_thing;
717 if ((generic_thing = (SV *)(GvGP(thing)->gp_sv))) {
265a0548 718 total_size += thing_size(aTHX_ generic_thing, tv);
9fc9ab86 719 }
720 if ((generic_thing = (SV *)(GvGP(thing)->gp_form))) {
265a0548 721 total_size += thing_size(aTHX_ generic_thing, tv);
9fc9ab86 722 }
723 if ((generic_thing = (SV *)(GvGP(thing)->gp_av))) {
265a0548 724 total_size += thing_size(aTHX_ generic_thing, tv);
9fc9ab86 725 }
726 if ((generic_thing = (SV *)(GvGP(thing)->gp_hv))) {
265a0548 727 total_size += thing_size(aTHX_ generic_thing, tv);
9fc9ab86 728 }
729 if ((generic_thing = (SV *)(GvGP(thing)->gp_egv))) {
265a0548 730 total_size += thing_size(aTHX_ generic_thing, tv);
9fc9ab86 731 }
732 if ((generic_thing = (SV *)(GvGP(thing)->gp_cv))) {
265a0548 733 total_size += thing_size(aTHX_ generic_thing, tv);
9fc9ab86 734 }
735 }
5c2e1b12 736 }
737 }
9fc9ab86 738 TAG;break;
739 case SVt_PVFM: TAG;
6a9ad7ec 740 total_size += sizeof(XPVFM);
9fc9ab86 741 total_size += magic_size(thing, tv);
7ccc7d88 742 total_size += ((XPVIO *) SvANY(thing))->xpv_len;
9fc9ab86 743 if (check_new(tv, CvPADLIST(thing))) {
265a0548 744 total_size += thing_size(aTHX_ (SV *)CvPADLIST(thing), tv);
7ccc7d88 745 }
9fc9ab86 746 if (check_new(tv, CvOUTSIDE(thing))) {
265a0548 747 total_size += thing_size(aTHX_ (SV *)CvOUTSIDE(thing), tv);
7ccc7d88 748 }
749
65db36c0 750 if (tv->go_yell && !tv->fm_whine) {
5073b933 751 carp("Devel::Size: Calculated sizes for FMs are incomplete");
65db36c0 752 tv->fm_whine = 1;
ebb2c5b9 753 }
9fc9ab86 754 TAG;break;
755 case SVt_PVIO: TAG;
6a9ad7ec 756 total_size += sizeof(XPVIO);
9fc9ab86 757 total_size += magic_size(thing, tv);
2eb93d08 758 if (check_new(tv, (SvPVX_const(thing)))) {
5073b933 759 total_size += ((XPVIO *) SvANY(thing))->xpv_cur;
ebb2c5b9 760 }
5073b933 761 /* Some embedded char pointers */
9fc9ab86 762 if (check_new(tv, ((XPVIO *) SvANY(thing))->xio_top_name)) {
5073b933 763 total_size += strlen(((XPVIO *) SvANY(thing))->xio_top_name);
764 }
9fc9ab86 765 if (check_new(tv, ((XPVIO *) SvANY(thing))->xio_fmt_name)) {
5073b933 766 total_size += strlen(((XPVIO *) SvANY(thing))->xio_fmt_name);
767 }
9fc9ab86 768 if (check_new(tv, ((XPVIO *) SvANY(thing))->xio_bottom_name)) {
5073b933 769 total_size += strlen(((XPVIO *) SvANY(thing))->xio_bottom_name);
770 }
771 /* Throw the GVs on the list to be walked if they're not-null */
772 if (((XPVIO *) SvANY(thing))->xio_top_gv) {
265a0548 773 total_size += thing_size(aTHX_ (SV *)((XPVIO *) SvANY(thing))->xio_top_gv,
9fc9ab86 774 tv);
5073b933 775 }
776 if (((XPVIO *) SvANY(thing))->xio_bottom_gv) {
265a0548 777 total_size += thing_size(aTHX_ (SV *)((XPVIO *) SvANY(thing))->xio_bottom_gv,
9fc9ab86 778 tv);
5073b933 779 }
780 if (((XPVIO *) SvANY(thing))->xio_fmt_gv) {
265a0548 781 total_size += thing_size(aTHX_ (SV *)((XPVIO *) SvANY(thing))->xio_fmt_gv,
9fc9ab86 782 tv);
5073b933 783 }
784
785 /* Only go trotting through the IO structures if they're really
786 trottable. If USE_PERLIO is defined we can do this. If
787 not... we can't, so we don't even try */
788#ifdef USE_PERLIO
789 /* Dig into xio_ifp and xio_ofp here */
9fc9ab86 790 warn("Devel::Size: Can't size up perlio layers yet\n");
5073b933 791#endif
9fc9ab86 792 TAG;break;
e98cedbf 793 default:
9fc9ab86 794 warn("Devel::Size: Unknown variable type: %d encountered\n", SvTYPE(thing) );
e98cedbf 795 }
796 return total_size;
797}
798
65db36c0 799static TRACKING *
800new_tracking(pTHX)
801{
802 SV *warn_flag;
803 TRACKING *tv;
804 Newxz(tv, 1, TRACKING);
805 tv->go_yell = TRUE;
806 if (NULL != (warn_flag = perl_get_sv("Devel::Size::warn", FALSE))) {
807 tv->dangle_whine = tv->go_yell = SvIV(warn_flag) ? TRUE : FALSE;
808 }
809 if (NULL != (warn_flag = perl_get_sv("Devel::Size::dangle", FALSE))) {
810 tv->dangle_whine = SvIV(warn_flag) ? TRUE : FALSE;
811 }
812 return tv;
813}
814
9fc9ab86 815MODULE = Devel::Size PACKAGE = Devel::Size
e98cedbf 816
fea63ffa 817PROTOTYPES: DISABLE
818
a6ea0805 819IV
820size(orig_thing)
821 SV *orig_thing
e98cedbf 822CODE:
823{
6a9ad7ec 824 SV *thing = orig_thing;
65db36c0 825 TRACKING *tv = new_tracking(aTHX);
ebb2c5b9 826
6a9ad7ec 827 /* If they passed us a reference then dereference it. This is the
828 only way we can check the sizes of arrays and hashes */
b7621729 829#if (PERL_VERSION < 11)
6a9ad7ec 830 if (SvOK(thing) && SvROK(thing)) {
831 thing = SvRV(thing);
832 }
b7621729 833#else
834 if (SvROK(thing)) {
835 thing = SvRV(thing);
836 }
837#endif
838
265a0548 839 RETVAL = thing_size(aTHX_ thing, tv);
e9716740 840 free_tracking(tv);
6a9ad7ec 841}
842OUTPUT:
843 RETVAL
844
845
846IV
847total_size(orig_thing)
848 SV *orig_thing
849CODE:
850{
851 SV *thing = orig_thing;
b7621729 852 /* Array with things we still need to do */
853 AV *pending_array;
b98fcdb9 854 IV size = 0;
65db36c0 855 TRACKING *tv = new_tracking(aTHX);
b98fcdb9 856
6a9ad7ec 857 /* Size starts at zero */
858 RETVAL = 0;
859
b7621729 860 pending_array = newAV();
861
862 /* We cannot push HV/AV directly, only the RV. So deref it
863 later (see below for "*** dereference later") and adjust here for
864 the miscalculation.
865 This is the only way we can check the sizes of arrays and hashes. */
866 if (SvROK(thing)) {
265a0548 867 RETVAL -= thing_size(aTHX_ thing, NULL);
b7621729 868 }
6a9ad7ec 869
870 /* Put it on the pending array */
871 av_push(pending_array, thing);
872
873 /* Now just yank things off the end of the array until it's done */
e96acca9 874 while (av_len(pending_array) >= 0) {
875 thing = av_pop(pending_array);
6a9ad7ec 876 /* Process it if we've not seen it */
9fc9ab86 877 if (check_new(tv, thing)) {
b7621729 878 dbg_printf(("# Found type %i at %p\n", SvTYPE(thing), thing));
e96acca9 879 /* Is it valid? */
880 if (thing) {
9fc9ab86 881 /* Yes, it is. So let's check the type */
882 switch (SvTYPE(thing)) {
883 /* fix for bug #24846 (Does not correctly recurse into references in a PVNV-type scalar) */
884 case SVt_PVNV: TAG;
885 if (SvROK(thing))
886 {
887 av_push(pending_array, SvRV(thing));
888 }
889 TAG;break;
890
891 /* this is the "*** dereference later" part - see above */
b7621729 892#if (PERL_VERSION < 11)
9fc9ab86 893 case SVt_RV: TAG;
b7621729 894#else
9fc9ab86 895 case SVt_IV: TAG;
b7621729 896#endif
897 dbg_printf(("# Found RV\n"));
898 if (SvROK(thing)) {
899 dbg_printf(("# Found RV\n"));
900 av_push(pending_array, SvRV(thing));
901 }
9fc9ab86 902 TAG;break;
903
904 case SVt_PVAV: TAG;
905 {
906 AV *tempAV = (AV *)thing;
907 SV **tempSV;
908
909 dbg_printf(("# Found type AV\n"));
910 /* Quick alias to cut down on casting */
911
912 /* Any elements? */
913 if (av_len(tempAV) != -1) {
914 IV index;
915 /* Run through them all */
916 for (index = 0; index <= av_len(tempAV); index++) {
917 /* Did we get something? */
918 if ((tempSV = av_fetch(tempAV, index, 0))) {
919 /* Was it undef? */
920 if (*tempSV != &PL_sv_undef) {
921 /* Apparently not. Save it for later */
922 av_push(pending_array, *tempSV);
923 }
924 }
925 }
926 }
927 }
928 TAG;break;
929
930 case SVt_PVHV: TAG;
931 dbg_printf(("# Found type HV\n"));
932 /* Is there anything in here? */
933 if (hv_iterinit((HV *)thing)) {
934 HE *temp_he;
935 while ((temp_he = hv_iternext((HV *)thing))) {
936 av_push(pending_array, hv_iterval((HV *)thing, temp_he));
937 }
938 }
939 TAG;break;
940
941 case SVt_PVGV: TAG;
942 dbg_printf(("# Found type GV\n"));
943 /* Run through all the pieces and push the ones with bits */
944 if (GvSV(thing)) {
945 av_push(pending_array, (SV *)GvSV(thing));
946 }
947 if (GvFORM(thing)) {
948 av_push(pending_array, (SV *)GvFORM(thing));
949 }
950 if (GvAV(thing)) {
951 av_push(pending_array, (SV *)GvAV(thing));
952 }
953 if (GvHV(thing)) {
954 av_push(pending_array, (SV *)GvHV(thing));
955 }
956 if (GvCV(thing)) {
957 av_push(pending_array, (SV *)GvCV(thing));
958 }
959 TAG;break;
960 default:
961 TAG;break;
962 }
6a9ad7ec 963 }
b98fcdb9 964
265a0548 965 size = thing_size(aTHX_ thing, tv);
b98fcdb9 966 RETVAL += size;
b7621729 967 } else {
968 /* check_new() returned false: */
969#ifdef DEVEL_SIZE_DEBUGGING
970 if (SvOK(sv)) printf("# Ignore ref copy 0x%x\n", sv);
971 else printf("# Ignore non-sv 0x%x\n", sv);
972#endif
6a9ad7ec 973 }
b7621729 974 } /* end while */
e9716740 975
976 free_tracking(tv);
6a9ad7ec 977 SvREFCNT_dec(pending_array);
e98cedbf 978}
979OUTPUT:
980 RETVAL
6a9ad7ec 981