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