#ifdef HAVE_MALLOC_CFG_H
# include "malloc_cfg.h"
#endif
#ifndef NO_FANCY_MALLOC
# ifndef SMALL_BUCKET_VIA_TABLE
# define SMALL_BUCKET_VIA_TABLE
# endif
# ifndef BUCKETS_ROOT2
# define BUCKETS_ROOT2
# endif
# ifndef IGNORE_SMALL_BAD_FREE
# define IGNORE_SMALL_BAD_FREE
# endif
#endif
#ifndef PLAIN_MALLOC /* Bulk enable features */
# ifndef PACK_MALLOC
# define PACK_MALLOC
# endif
# ifndef TWO_POT_OPTIMIZE
# define TWO_POT_OPTIMIZE
# endif
# ifndef PERL_EMERGENCY_SBRK
# define PERL_EMERGENCY_SBRK
# endif
# ifndef DEBUGGING_MSTATS
# define DEBUGGING_MSTATS
# endif
#endif
#define MIN_BUC_POW2 (sizeof(void*) > 4 ? 3 : 2) /* Allow for 4-byte arena. */
#define MIN_BUCKET (MIN_BUC_POW2 * BUCKETS_PER_POW2)
#define LOG_OF_MIN_ARENA 11
#if defined(DEBUGGING) && !defined(NO_RCHECK)
# define RCHECK
#endif
#if defined(DEBUGGING) && !defined(NO_RCHECK) && !defined(NO_MFILL) && !defined(MALLOC_FILL)
# define MALLOC_FILL
#endif
#if defined(DEBUGGING) && !defined(NO_RCHECK) && !defined(NO_FILL_CHECK) && !defined(MALLOC_FILL_CHECK)
# define MALLOC_FILL_CHECK
#endif
#if defined(RCHECK) && defined(IGNORE_SMALL_BAD_FREE)
# undef IGNORE_SMALL_BAD_FREE
#endif
#include "EXTERN.h"
#define PERL_IN_MALLOC_C
#include "perl.h"
#if defined(MULTIPLICITY)
# define croak2 Perl_croak_nocontext
#else
# define croak2 croak
#endif
#ifdef USE_ITHREADS
# define PERL_MAYBE_ALIVE PL_thr_key
#else
# define PERL_MAYBE_ALIVE 1
#endif
#ifndef MYMALLOC
# error "MYMALLOC is not defined"
#endif
#ifndef MUTEX_LOCK
# define MUTEX_LOCK(l)
#endif
#ifndef MUTEX_UNLOCK
# define MUTEX_UNLOCK(l)
#endif
#ifndef MALLOC_LOCK
# define MALLOC_LOCK MUTEX_LOCK(&PL_malloc_mutex)
#endif
#ifndef MALLOC_UNLOCK
# define MALLOC_UNLOCK MUTEX_UNLOCK(&PL_malloc_mutex)
#endif
# ifndef fatalcroak /* make depend */
# define fatalcroak(mess) (write(2, (mess), strlen(mess)), exit(2))
# endif
#ifdef DEBUGGING
# undef DEBUG_m
# define DEBUG_m(a) \
STMT_START { \
if
(PERL_MAYBE_ALIVE && PERL_GET_THX) { \
dTHX; \
if
(DEBUG_m_TEST) { \
PL_debug &= ~DEBUG_m_FLAG; \
a; \
PL_debug |= DEBUG_m_FLAG; \
} \
} \
} STMT_END
#endif
#ifdef MULTIPLICITY
# define PERL_IS_ALIVE aTHX
#else
# define PERL_IS_ALIVE TRUE
#endif
#define u_char unsigned char
#define u_int unsigned int
#define u_short unsigned short
#if defined(RCHECK) && defined(PACK_MALLOC)
# undef PACK_MALLOC
#endif
union
overhead {
union
overhead *ov_next;
#if MEM_ALIGNBYTES > 4
double
strut;
# if MEM_ALIGNBYTES > 8
char
sstrut[MEM_ALIGNBYTES];
# endif
#endif
struct
{
u_char ovu_index;
u_char ovu_magic;
#ifdef RCHECK
u_short ovu_size;
u_int ovu_rmagic;
#endif
} ovu;
#define ov_magic ovu.ovu_magic
#define ov_index ovu.ovu_index
#define ov_size ovu.ovu_size
#define ov_rmagic ovu.ovu_rmagic
};
#define MAGIC 0xff /* magic # on accounting info */
#define RMAGIC 0x55555555 /* magic # on range info */
#define RMAGIC_C 0x55 /* magic # on range info */
#ifdef RCHECK
# define RMAGIC_SZ sizeof (u_int) /* Overhead at end of bucket */
# ifdef TWO_POT_OPTIMIZE
# define MAX_SHORT_BUCKET (12 * BUCKETS_PER_POW2) /* size-1 fits in short */
# else
# define MAX_SHORT_BUCKET (13 * BUCKETS_PER_POW2)
# endif
#else
# define RMAGIC_SZ 0
#endif
#if !defined(PACK_MALLOC) && defined(BUCKETS_ROOT2)
# undef BUCKETS_ROOT2
#endif
#ifdef BUCKETS_ROOT2
# define BUCKET_TABLE_SHIFT 2
# define BUCKET_POW2_SHIFT 1
# define BUCKETS_PER_POW2 2
#else
# define BUCKET_TABLE_SHIFT MIN_BUC_POW2
# define BUCKET_POW2_SHIFT 0
# define BUCKETS_PER_POW2 1
#endif
#if !defined(MEM_ALIGNBYTES) || ((MEM_ALIGNBYTES > 4) && !defined(STRICT_ALIGNMENT))
struct
aligner {
char
c;
void
*p;
};
# define ALIGN_SMALL ((IV)((caddr_t)&(((struct aligner*)0)->p)))
#else
# define ALIGN_SMALL MEM_ALIGNBYTES
#endif
#define IF_ALIGN_8(yes,no) ((ALIGN_SMALL>4) ? (yes) : (no))
#ifdef BUCKETS_ROOT2
# define MAX_BUCKET_BY_TABLE 13
static
const
u_short buck_size[MAX_BUCKET_BY_TABLE + 1] =
{
0, 0, 0, 0, 4, 4, 8, 12, 16, 24, 32, 48, 64, 80,
};
# define BUCKET_SIZE_NO_SURPLUS(i) ((i) % 2 ? buck_size[i] : (1 << ((i) >> BUCKET_POW2_SHIFT)))
# define BUCKET_SIZE_REAL(i) ((i) <= MAX_BUCKET_BY_TABLE \
? ((
size_t
)buck_size[i]) \
: ((((
size_t
)1) << ((i) >> BUCKET_POW2_SHIFT)) \
- MEM_OVERHEAD(i) \
+ POW2_OPTIMIZE_SURPLUS(i)))
#else
# define BUCKET_SIZE_NO_SURPLUS(i) (((size_t)1) << ((i) >> BUCKET_POW2_SHIFT))
# define BUCKET_SIZE(i) (BUCKET_SIZE_NO_SURPLUS(i) + POW2_OPTIMIZE_SURPLUS(i))
# define BUCKET_SIZE_REAL(i) (BUCKET_SIZE(i) - MEM_OVERHEAD(i))
#endif
#ifdef PACK_MALLOC
# define MAX_PACKED_POW2 6
# define MAX_PACKED (MAX_PACKED_POW2 * BUCKETS_PER_POW2 + BUCKET_POW2_SHIFT)
# define MAX_POW2_ALGO ((1<<(MAX_PACKED_POW2 + 1)) - M_OVERHEAD)
# define TWOK_MASK nBIT_MASK(LOG_OF_MIN_ARENA)
# define TWOK_MASKED(x) (PTR2UV(x) & ~TWOK_MASK)
# define TWOK_SHIFT(x) (PTR2UV(x) & TWOK_MASK)
# define OV_INDEXp(block) (INT2PTR(u_char*,TWOK_MASKED(block)))
# define OV_INDEX(block) (*OV_INDEXp(block))
# define OV_MAGIC(block,bucket) (*(OV_INDEXp(block) + \
(TWOK_SHIFT(block)>> \
(bucket>>BUCKET_POW2_SHIFT)) + \
(bucket >= MIN_NEEDS_SHIFT ? 1 : 0)))
# ifdef BUCKETS_ROOT2
# define MIN_NEEDS_SHIFT (7*BUCKETS_PER_POW2 - 1) /* Shift 80 greater than chunk 64. */
# else
# define MIN_NEEDS_SHIFT (7*BUCKETS_PER_POW2) /* Shift 128 greater than chunk 32. */
# endif
# define CHUNK_SHIFT 0
#ifdef IGNORE_SMALL_BAD_FREE
#define FIRST_BUCKET_WITH_CHECK (6 * BUCKETS_PER_POW2) /* 64 */
# define N_BLKS(bucket) ( (bucket) < FIRST_BUCKET_WITH_CHECK \
? nBIT_MASK(LOG_OF_MIN_ARENA)/BUCKET_SIZE_NO_SURPLUS(bucket) \
: n_blks[bucket] )
#else
# define N_BLKS(bucket) n_blks[bucket]
#endif
static
const
u_short n_blks[LOG_OF_MIN_ARENA * BUCKETS_PER_POW2] =
{
# if BUCKETS_PER_POW2==1
0, 0,
(MIN_BUC_POW2==2 ? 384 : 0),
224, 120, 62, 31, 16, 8, 4, 2
# else
0, 0, 0, 0,
(MIN_BUC_POW2==2 ? 384 : 0), (MIN_BUC_POW2==2 ? 384 : 0),
224, 149, 120, 80, 62, 41, 31, 25, 16, 16, 8, 8, 4, 4, 2, 2
# endif
};
#ifdef IGNORE_SMALL_BAD_FREE
# define BLK_SHIFT(bucket) ( (bucket) < FIRST_BUCKET_WITH_CHECK \
? ((1<<LOG_OF_MIN_ARENA) \
- BUCKET_SIZE_NO_SURPLUS(bucket) * N_BLKS(bucket)) \
: blk_shift[bucket])
#else
# define BLK_SHIFT(bucket) blk_shift[bucket]
#endif
static
const
u_short blk_shift[LOG_OF_MIN_ARENA * BUCKETS_PER_POW2] =
{
# if BUCKETS_PER_POW2==1
0, 0,
(MIN_BUC_POW2==2 ? 512 : 0),
256, 128, 64, 64,
16*
sizeof
(
union
overhead),
8*
sizeof
(
union
overhead),
4*
sizeof
(
union
overhead),
2*
sizeof
(
union
overhead),
# else
0, 0, 0, 0,
(MIN_BUC_POW2==2 ? 512 : 0), (MIN_BUC_POW2==2 ? 512 : 0),
256, 260, 128, 128, 64, 80, 64, 48,
16*
sizeof
(
union
overhead), 16*
sizeof
(
union
overhead),
8*
sizeof
(
union
overhead), 8*
sizeof
(
union
overhead),
4*
sizeof
(
union
overhead), 4*
sizeof
(
union
overhead),
2*
sizeof
(
union
overhead), 2*
sizeof
(
union
overhead),
# endif
};
# define NEEDED_ALIGNMENT 0x800 /* 2k boundaries */
# define WANTED_ALIGNMENT 0x800 /* 2k boundaries */
#else /* !PACK_MALLOC */
# define OV_MAGIC(block,bucket) (block)->ov_magic
# define OV_INDEX(block) (block)->ov_index
# define CHUNK_SHIFT 1
# define MAX_PACKED -1
# define NEEDED_ALIGNMENT MEM_ALIGNBYTES
# define WANTED_ALIGNMENT 0x400 /* 1k boundaries */
#endif /* !PACK_MALLOC */
#define M_OVERHEAD (sizeof(union overhead) + RMAGIC_SZ) /* overhead at start+end */
#ifdef PACK_MALLOC
# define MEM_OVERHEAD(bucket) \
(bucket <= MAX_PACKED ? ((
size_t
)0) : M_OVERHEAD)
# ifdef SMALL_BUCKET_VIA_TABLE
# define START_SHIFTS_BUCKET ((MAX_PACKED_POW2 + 1) * BUCKETS_PER_POW2)
# define START_SHIFT MAX_PACKED_POW2
# ifdef BUCKETS_ROOT2 /* Chunks of size 3*2^n. */
# define SIZE_TABLE_MAX 80
# else
# define SIZE_TABLE_MAX 64
# endif
static
const
char
bucket_of[] =
{
# ifdef BUCKETS_ROOT2 /* Chunks of size 3*2^n. */
(
sizeof
(
void
*) > 4 ? 6 : 5),
6,
IF_ALIGN_8(8,7), 8,
9, 9, 10, 10,
11, 11, 11, 11,
12, 12, 12, 12,
13, 13, 13, 13,
13, 13, 13, 13
# else /* !BUCKETS_ROOT2 */
(
sizeof
(
void
*) > 4 ? 3 : 2),
3,
4, 4,
5, 5, 5, 5,
6, 6, 6, 6,
6, 6, 6, 6
# endif /* !BUCKETS_ROOT2 */
};
# else /* !SMALL_BUCKET_VIA_TABLE */
# define START_SHIFTS_BUCKET MIN_BUCKET
# define START_SHIFT (MIN_BUC_POW2 - 1)
# endif /* !SMALL_BUCKET_VIA_TABLE */
#else /* !PACK_MALLOC */
# define MEM_OVERHEAD(bucket) M_OVERHEAD
# ifdef SMALL_BUCKET_VIA_TABLE
# undef SMALL_BUCKET_VIA_TABLE
# endif
# define START_SHIFTS_BUCKET MIN_BUCKET
# define START_SHIFT (MIN_BUC_POW2 - 1)
#endif /* !PACK_MALLOC */
#ifdef TWO_POT_OPTIMIZE
# ifndef PERL_PAGESIZE
# define PERL_PAGESIZE 4096
# endif
# ifndef FIRST_BIG_POW2
# define FIRST_BIG_POW2 15 /* 32K, 16K is used too often. */
# endif
# define FIRST_BIG_BLOCK (1<<FIRST_BIG_POW2)
# define FIRST_BIG_BOUND (FIRST_BIG_BLOCK - M_OVERHEAD)
# define LAST_SMALL_BOUND ((FIRST_BIG_BLOCK>>1) - M_OVERHEAD)
# define POW2_OPTIMIZE_ADJUST(nbytes) \
((nbytes >= FIRST_BIG_BOUND) ? nbytes -= PERL_PAGESIZE : 0)
# define POW2_OPTIMIZE_SURPLUS(bucket) \
((
size_t
)((bucket >= FIRST_BIG_POW2 * BUCKETS_PER_POW2) ? PERL_PAGESIZE : 0))
#else /* !TWO_POT_OPTIMIZE */
# define POW2_OPTIMIZE_ADJUST(nbytes)
# define POW2_OPTIMIZE_SURPLUS(bucket) ((size_t)0)
#endif /* !TWO_POT_OPTIMIZE */
#define BARK_64K_LIMIT(what,nbytes,size)
#ifndef MIN_SBRK
# define MIN_SBRK 2048
#endif
#ifndef FIRST_SBRK
# define FIRST_SBRK (48*1024)
#endif
#ifndef MIN_SBRK_FRAC
# define MIN_SBRK_FRAC 3
#endif
#ifndef SBRK_ALLOW_FAILURES
# define SBRK_ALLOW_FAILURES 3
#endif
#ifndef SBRK_FAILURE_PRICE
# define SBRK_FAILURE_PRICE 50
#endif
static
void
morecore (
int
bucket);
# if defined(DEBUGGING)
static
void
botch (
const
char
*diag,
const
char
*s,
const
char
*file,
int
line);
# endif
static
void
add_to_chain (
void
*p, MEM_SIZE size, MEM_SIZE chip);
static
void
* get_from_chain (MEM_SIZE size);
static
void
* get_from_bigger_buckets(
int
bucket, MEM_SIZE size);
static
union
overhead *getpages (MEM_SIZE needed,
int
*nblksp,
int
bucket);
static
int
getpages_adjacent(MEM_SIZE require);
#ifdef I_MACH_CTHREADS
# undef MUTEX_LOCK
# define MUTEX_LOCK(m) STMT_START { if (*m) mutex_lock(*m); } STMT_END
# undef MUTEX_UNLOCK
# define MUTEX_UNLOCK(m) STMT_START { if (*m) mutex_unlock(*m); } STMT_END
#endif
#ifndef PTRSIZE
# define PTRSIZE sizeof(void*)
#endif
#ifndef BITS_IN_PTR
# define BITS_IN_PTR (8*PTRSIZE)
#endif
#define NBUCKETS (BITS_IN_PTR*BUCKETS_PER_POW2 + 1)
static
union
overhead *nextf[NBUCKETS];
#if defined(PURIFY) && !defined(USE_PERL_SBRK)
# define USE_PERL_SBRK
#endif
#ifdef USE_PERL_SBRK
# define sbrk(a) Perl_sbrk(a)
Malloc_t Perl_sbrk (
int
size);
#elif !defined(HAS_SBRK_PROTO) /* <unistd.h> usually takes care of this */
extern
Malloc_t sbrk(
int
);
#endif
#ifndef MIN_SBRK_FRAC1000 /* Backward compatibility */
# define MIN_SBRK_FRAC1000 (MIN_SBRK_FRAC * 10)
#endif
#include "malloc_ctl.h"
#ifndef NO_MALLOC_DYNAMIC_CFG
# define PERL_MALLOC_OPT_CHARS "FMfAPGdac"
# ifndef FILL_DEAD_DEFAULT
# define FILL_DEAD_DEFAULT 1
# endif
# ifndef FILL_ALIVE_DEFAULT
# define FILL_ALIVE_DEFAULT 1
# endif
# ifndef FILL_CHECK_DEFAULT
# define FILL_CHECK_DEFAULT 1
# endif
static
IV MallocCfg[MallocCfg_last] = {
FIRST_SBRK,
MIN_SBRK,
MIN_SBRK_FRAC,
SBRK_ALLOW_FAILURES,
SBRK_FAILURE_PRICE,
SBRK_ALLOW_FAILURES * SBRK_FAILURE_PRICE,
FILL_DEAD_DEFAULT,
FILL_ALIVE_DEFAULT,
FILL_CHECK_DEFAULT,
0,
0,
0,
0,
0
};
IV *MallocCfg_ptr = MallocCfg;
static
char
* MallocCfgP[MallocCfg_last] = {
0,
0,
};
char
**MallocCfgP_ptr = MallocCfgP;
# undef MIN_SBRK
# undef FIRST_SBRK
# undef MIN_SBRK_FRAC1000
# undef SBRK_ALLOW_FAILURES
# undef SBRK_FAILURE_PRICE
# define MIN_SBRK MallocCfg[MallocCfg_MIN_SBRK]
# define FIRST_SBRK MallocCfg[MallocCfg_FIRST_SBRK]
# define MIN_SBRK_FRAC1000 MallocCfg[MallocCfg_MIN_SBRK_FRAC1000]
# define SBRK_ALLOW_FAILURES MallocCfg[MallocCfg_SBRK_ALLOW_FAILURES]
# define SBRK_FAILURE_PRICE MallocCfg[MallocCfg_SBRK_FAILURE_PRICE]
# define sbrk_goodness MallocCfg[MallocCfg_sbrk_goodness]
# define emergency_buffer_size MallocCfg[MallocCfg_emergency_buffer_size]
# define emergency_buffer_last_req MallocCfg[MallocCfg_emergency_buffer_last_req]
# define FILL_DEAD MallocCfg[MallocCfg_filldead]
# define FILL_ALIVE MallocCfg[MallocCfg_fillalive]
# define FILL_CHECK_CFG MallocCfg[MallocCfg_fillcheck]
# define FILL_CHECK (FILL_DEAD && FILL_CHECK_CFG)
# define emergency_buffer MallocCfgP[MallocCfgP_emergency_buffer]
# define emergency_buffer_prepared MallocCfgP[MallocCfgP_emergency_buffer_prepared]
#else /* defined(NO_MALLOC_DYNAMIC_CFG) */
# define FILL_DEAD 1
# define FILL_ALIVE 1
# define FILL_CHECK 1
static
int
sbrk_goodness = SBRK_ALLOW_FAILURES * SBRK_FAILURE_PRICE;
# define NO_PERL_MALLOC_ENV
#endif
#ifdef DEBUGGING_MSTATS
static
u_int nmalloc[NBUCKETS];
static
u_int sbrk_slack;
static
u_int start_slack;
#else /* !( defined DEBUGGING_MSTATS ) */
# define sbrk_slack 0
#endif
static
u_int goodsbrk;
#ifdef PERL_EMERGENCY_SBRK
# ifndef BIG_SIZE
# define BIG_SIZE (1<<16) /* 64K */
# endif
# ifdef NO_MALLOC_DYNAMIC_CFG
static
MEM_SIZE emergency_buffer_size;
static
MEM_SIZE emergency_buffer_last_req;
static
char
*emergency_buffer;
static
char
*emergency_buffer_prepared;
# endif
# ifndef emergency_sbrk_croak
# define emergency_sbrk_croak croak2
# endif
static
char
*
perl_get_emergency_buffer(IV *size)
{
dTHX;
SV *sv;
char
*pv;
GV **gvp = (GV**)hv_fetchs(PL_defstash,
"^M"
, FALSE);
if
(!gvp) gvp = (GV**)hv_fetchs(PL_defstash,
"\015"
, FALSE);
if
(!gvp || !(sv = GvSV(*gvp)) || !SvPOK(sv)
|| (SvLEN(sv) < (1<<LOG_OF_MIN_ARENA) - M_OVERHEAD))
return
NULL;
pv = SvPV_nolen(sv);
if
((PTR2UV(pv) -
sizeof
(
union
overhead)) & (NEEDED_ALIGNMENT - 1)) {
PerlIO_puts(PerlIO_stderr(),
"Bad alignment of $^M!\n"
);
return
NULL;
}
SvPOK_off(sv);
SvPV_set(sv, NULL);
SvCUR_set(sv, 0);
SvLEN_set(sv, 0);
*size = malloced_size(pv) + M_OVERHEAD;
return
pv -
sizeof
(
union
overhead);
}
# define PERL_GET_EMERGENCY_BUFFER(p) perl_get_emergency_buffer(p)
# ifndef NO_MALLOC_DYNAMIC_CFG
static
char
*
get_emergency_buffer(IV *size)
{
char
*pv = emergency_buffer_prepared;
*size = MallocCfg[MallocCfg_emergency_buffer_prepared_size];
emergency_buffer_prepared = 0;
MallocCfg[MallocCfg_emergency_buffer_prepared_size] = 0;
return
pv;
}
# define GET_EMERGENCY_BUFFER(p) get_emergency_buffer(p)
# else /* NO_MALLOC_DYNAMIC_CFG */
# define GET_EMERGENCY_BUFFER(p) NULL
# endif
static
Malloc_t
emergency_sbrk(MEM_SIZE size)
{
MEM_SIZE rsize = (((size - 1)>>LOG_OF_MIN_ARENA) + 1)<<LOG_OF_MIN_ARENA;
if
(size >= BIG_SIZE
&& (!emergency_buffer_last_req ||
(size < (MEM_SIZE)emergency_buffer_last_req))) {
MALLOC_UNLOCK;
emergency_buffer_last_req = size;
emergency_sbrk_croak(
"Out of memory during \"large\" request for %"
UVuf
" bytes, total sbrk() is %"
UVuf
" bytes"
,
(UV)size, (UV)(goodsbrk + sbrk_slack));
}
if
((MEM_SIZE)emergency_buffer_size >= rsize) {
char
*old = emergency_buffer;
emergency_buffer_size -= rsize;
emergency_buffer += rsize;
return
old;
}
else
{
IV Size;
char
*pv = GET_EMERGENCY_BUFFER(&Size);
int
have = 0;
if
(emergency_buffer_size) {
add_to_chain(emergency_buffer, emergency_buffer_size, 0);
emergency_buffer_size = 0;
emergency_buffer = NULL;
have = 1;
}
if
(!pv)
pv = PERL_GET_EMERGENCY_BUFFER(&Size);
if
(!pv) {
if
(have)
goto
do_croak;
return
(
char
*)-1;
}
if
(PTR2UV(pv) & (NEEDED_ALIGNMENT - 1)) {
dTHX;
PerlIO_puts(PerlIO_stderr(),
"Bad alignment of $^M!\n"
);
return
(
char
*)-1;
}
emergency_buffer = pv;
emergency_buffer_size = Size;
}
do_croak:
MALLOC_UNLOCK;
emergency_sbrk_croak(
"Out of memory during request for %"
UVuf
" bytes, total sbrk() is %"
UVuf
" bytes"
,
(UV)size, (UV)(goodsbrk + sbrk_slack));
NOT_REACHED;
return
NULL;
}
#else /* !defined(PERL_EMERGENCY_SBRK) */
# define emergency_sbrk(size) -1
#endif /* defined PERL_EMERGENCY_SBRK */
#define MYMALLOC_WRITE2STDERR(s) PERL_UNUSED_RESULT(PerlLIO_write(PerlIO_fileno(PerlIO_stderr()),s,strlen(s)))
#ifdef DEBUGGING
#undef ASSERT
#define ASSERT(p,diag) if (!(p)) botch(diag,STRINGIFY(p),__FILE__,__LINE__);
static
void
botch(
const
char
*diag,
const
char
*s,
const
char
*file,
int
line)
{
dTHX;
if
(!(PERL_MAYBE_ALIVE && PERL_GET_THX))
goto
do_write;
else
{
if
(PerlIO_printf(PerlIO_stderr(),
"assertion botched (%s?): %s %s:%d\n"
,
diag, s, file, line) != 0) {
do_write:
MYMALLOC_WRITE2STDERR(
"assertion botched ("
);
MYMALLOC_WRITE2STDERR(diag);
MYMALLOC_WRITE2STDERR(
"?): "
);
MYMALLOC_WRITE2STDERR(s);
MYMALLOC_WRITE2STDERR(
" ("
);
MYMALLOC_WRITE2STDERR(file);
MYMALLOC_WRITE2STDERR(
":"
);
{
char
linebuf[10];
char
*s = linebuf +
sizeof
(linebuf) - 1;
int
n = line;
*s = 0;
do
{
*--s =
'0'
+ (n % 10);
}
while
(n /= 10);
MYMALLOC_WRITE2STDERR(s);
}
MYMALLOC_WRITE2STDERR(
")\n"
);
}
PerlProc_abort();
}
}
#else
#define ASSERT(p, diag)
#endif
#ifdef MALLOC_FILL
static
void
fill_pat_4bytes(unsigned
char
*s,
size_t
nbytes,
const
unsigned
char
*fill)
{
unsigned
char
*e = s + nbytes;
long
*lp;
const
long
lfill = *(
long
*)fill;
if
(PTR2UV(s) & (
sizeof
(
long
)-1)) {
int
shift =
sizeof
(
long
) - (PTR2UV(s) & (
sizeof
(
long
)-1));
unsigned
const
char
*f = fill +
sizeof
(
long
) - shift;
unsigned
char
*e1 = s + shift;
while
(s < e1)
*s++ = *f++;
}
lp = (
long
*)s;
while
((unsigned
char
*)(lp + 1) <= e)
*lp++ = lfill;
s = (unsigned
char
*)lp;
while
(s < e)
*s++ = *fill++;
}
static
const
unsigned
char
fill_feedadad[] =
{0xFE, 0xED, 0xAD, 0xAD, 0xFE, 0xED, 0xAD, 0xAD,
0xFE, 0xED, 0xAD, 0xAD, 0xFE, 0xED, 0xAD, 0xAD};
static
const
unsigned
char
fill_deadbeef[] =
{0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF,
0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF};
# define FILL_DEADBEEF(s, n) \
(
void
)(FILL_DEAD? (fill_pat_4bytes((s), (n), fill_deadbeef), 0) : 0)
# define FILL_FEEDADAD(s, n) \
(
void
)(FILL_ALIVE? (fill_pat_4bytes((s), (n), fill_feedadad), 0) : 0)
#else
# define FILL_DEADBEEF(s, n) ((void)0)
# define FILL_FEEDADAD(s, n) ((void)0)
# undef MALLOC_FILL_CHECK
#endif
#ifdef MALLOC_FILL_CHECK
static
int
cmp_pat_4bytes(unsigned
char
*s,
size_t
nbytes,
const
unsigned
char
*fill)
{
unsigned
char
*e = s + nbytes;
long
*lp;
const
long
lfill = *(
long
*)fill;
if
(PTR2UV(s) & (
sizeof
(
long
)-1)) {
int
shift =
sizeof
(
long
) - (PTR2UV(s) & (
sizeof
(
long
)-1));
unsigned
const
char
*f = fill +
sizeof
(
long
) - shift;
unsigned
char
*e1 = s + shift;
while
(s < e1)
if
(*s++ != *f++)
return
1;
}
lp = (
long
*)s;
while
((unsigned
char
*)(lp + 1) <= e)
if
(*lp++ != lfill)
return
1;
s = (unsigned
char
*)lp;
while
(s < e)
if
(*s++ != *fill++)
return
1;
return
0;
}
# define FILLCHECK_DEADBEEF(s, n) \
ASSERT(!FILL_CHECK || !cmp_pat_4bytes(s, n, fill_deadbeef), \
"free()ed/realloc()ed-away memory was overwritten"
)
#else
# define FILLCHECK_DEADBEEF(s, n) ((void)0)
#endif
STATIC
int
S_adjust_size_and_find_bucket(
size_t
*nbytes_p)
{
MEM_SIZE shiftr;
int
bucket;
size_t
nbytes;
PERL_ARGS_ASSERT_ADJUST_SIZE_AND_FIND_BUCKET;
nbytes = *nbytes_p;
#ifdef PACK_MALLOC
# ifdef SMALL_BUCKET_VIA_TABLE
if
(nbytes == 0)
bucket = MIN_BUCKET;
else
if
(nbytes <= SIZE_TABLE_MAX) {
bucket = bucket_of[(nbytes - 1) >> BUCKET_TABLE_SHIFT];
}
else
# else
if
(nbytes == 0)
nbytes = 1;
if
(nbytes <= MAX_POW2_ALGO)
goto
do_shifts;
else
# endif
#endif
{
POW2_OPTIMIZE_ADJUST(nbytes);
nbytes += M_OVERHEAD;
nbytes = (nbytes + 3) &~ 3;
#if defined(PACK_MALLOC) && !defined(SMALL_BUCKET_VIA_TABLE)
do_shifts:
#endif
shiftr = (nbytes - 1) >> START_SHIFT;
bucket = START_SHIFTS_BUCKET;
while
(shiftr >>= 1)
bucket += BUCKETS_PER_POW2;
}
*nbytes_p = nbytes;
return
bucket;
}
Malloc_t
Perl_malloc(
size_t
nbytes)
{
union
overhead *p;
int
bucket;
#if defined(DEBUGGING) || defined(RCHECK)
MEM_SIZE size = nbytes;
#endif
if
(nbytes > PTRDIFF_MAX) {
dTHX;
MYMALLOC_WRITE2STDERR(
"Memory requests are limited to PTRDIFF_MAX"
" bytes to prevent possible undefined"
" behavior"
);
return
NULL;
}
BARK_64K_LIMIT(
"Allocation"
,nbytes,nbytes);
#ifdef DEBUGGING
if
((
long
)nbytes < 0)
croak(
"%s"
,
"panic: malloc"
);
#endif
bucket = adjust_size_and_find_bucket(&nbytes);
MALLOC_LOCK;
if
(nextf[bucket] == NULL)
morecore(bucket);
if
((p = nextf[bucket]) == NULL) {
MALLOC_UNLOCK;
{
dTHX;
if
(!PL_nomemok) {
#if defined(PLAIN_MALLOC) && defined(NO_FANCY_MALLOC)
MYMALLOC_WRITE2STDERR(
"Out of memory!\n"
);
#else
char
buff[80];
char
*eb = buff +
sizeof
(buff) - 1;
char
*s = eb;
size_t
n = nbytes;
MYMALLOC_WRITE2STDERR(
"Out of memory during request for "
);
#if defined(DEBUGGING) || defined(RCHECK)
n = size;
#endif
*s = 0;
do
{
*--s =
'0'
+ (n % 10);
}
while
(n /= 10);
MYMALLOC_WRITE2STDERR(s);
MYMALLOC_WRITE2STDERR(
" bytes, total sbrk() is "
);
s = eb;
n = goodsbrk + sbrk_slack;
do
{
*--s =
'0'
+ (n % 10);
}
while
(n /= 10);
MYMALLOC_WRITE2STDERR(s);
MYMALLOC_WRITE2STDERR(
" bytes!\n"
);
#endif /* defined(PLAIN_MALLOC) && defined(NO_FANCY_MALLOC) */
my_exit(1);
}
}
return
(NULL);
}
#ifdef DEBUGGING
if
( (PTR2UV(p) & (MEM_ALIGNBYTES - 1))
|| (p && PTR2UV(p) < (1<<LOG_OF_MIN_ARENA)) ) {
dTHX;
PerlIO_printf(PerlIO_stderr(),
"Unaligned pointer in the free chain 0x%"
UVxf
"\n"
,
PTR2UV(p));
}
if
( (PTR2UV(p->ov_next) & (MEM_ALIGNBYTES - 1))
|| (p->ov_next && PTR2UV(p->ov_next) < (1<<LOG_OF_MIN_ARENA)) ) {
dTHX;
PerlIO_printf(PerlIO_stderr(),
"Unaligned \"next\" pointer in the free "
"chain 0x%"
UVxf
" at 0x%"
UVxf
"\n"
,
PTR2UV(p->ov_next), PTR2UV(p));
}
#endif
nextf[bucket] = p->ov_next;
MALLOC_UNLOCK;
DEBUG_m(PerlIO_printf(Perl_debug_log,
"%p: (%05lu) malloc %ld bytes\n"
,
(Malloc_t)(p + CHUNK_SHIFT),
(unsigned
long
)(PL_an++),
(
long
)size));
FILLCHECK_DEADBEEF((unsigned
char
*)(p + CHUNK_SHIFT),
BUCKET_SIZE_REAL(bucket) + RMAGIC_SZ);
#ifdef IGNORE_SMALL_BAD_FREE
if
(bucket >= FIRST_BUCKET_WITH_CHECK)
#endif
OV_MAGIC(p, bucket) = MAGIC;
#ifndef PACK_MALLOC
OV_INDEX(p) = bucket;
#endif
#ifdef RCHECK
p->ov_rmagic = RMAGIC;
if
(bucket <= MAX_SHORT_BUCKET) {
int
i;
nbytes = size + M_OVERHEAD;
p->ov_size = nbytes - 1;
if
((i = nbytes & (RMAGIC_SZ-1))) {
i = RMAGIC_SZ - i;
while
(i--)
((caddr_t)p + nbytes - RMAGIC_SZ)[i] = RMAGIC_C;
}
nbytes = (nbytes + RMAGIC_SZ - 1) & ~(RMAGIC_SZ - 1);
((u_int *)((caddr_t)p + nbytes))[-1] = RMAGIC;
}
FILL_FEEDADAD((unsigned
char
*)(p + CHUNK_SHIFT), size);
#endif
return
((Malloc_t)(p + CHUNK_SHIFT));
}
static
char
*last_sbrk_top;
static
char
*last_op;
static
MEM_SIZE sbrked_remains;
#ifdef DEBUGGING_MSTATS
static
int
sbrks;
#endif
struct
chunk_chain_s {
struct
chunk_chain_s *next;
MEM_SIZE size;
};
static
struct
chunk_chain_s *chunk_chain;
static
int
n_chunks;
static
char
max_bucket;
static
void
*
get_from_chain(MEM_SIZE size)
{
struct
chunk_chain_s *elt = chunk_chain, **oldp = &chunk_chain;
struct
chunk_chain_s **oldgoodp = NULL;
long
min_remain = LONG_MAX;
while
(elt) {
if
(elt->size >= size) {
long
remains = elt->size - size;
if
(remains >= 0 && remains < min_remain) {
oldgoodp = oldp;
min_remain = remains;
}
if
(remains == 0) {
break
;
}
}
oldp = &( elt->next );
elt = elt->next;
}
if
(!oldgoodp)
return
NULL;
if
(min_remain) {
void
*ret = *oldgoodp;
struct
chunk_chain_s *next = (*oldgoodp)->next;
*oldgoodp = (
struct
chunk_chain_s *)((
char
*)ret + size);
(*oldgoodp)->size = min_remain;
(*oldgoodp)->next = next;
return
ret;
}
else
{
void
*ret = *oldgoodp;
*oldgoodp = (*oldgoodp)->next;
n_chunks--;
return
ret;
}
}
static
void
add_to_chain(
void
*p, MEM_SIZE size, MEM_SIZE chip)
{
struct
chunk_chain_s *next = chunk_chain;
char
*cp = (
char
*)p;
cp += chip;
chunk_chain = (
struct
chunk_chain_s *)cp;
chunk_chain->size = size - chip;
chunk_chain->next = next;
n_chunks++;
}
static
void
*
get_from_bigger_buckets(
int
bucket, MEM_SIZE size)
{
int
price = 1;
static
int
bucketprice[NBUCKETS];
while
(bucket <= max_bucket) {
if
(nextf[bucket] && bucketprice[bucket]++ >= price) {
void
*ret = (
void
*)(nextf[bucket] - 1 + CHUNK_SHIFT);
bucketprice[bucket] = 0;
if
(((
char
*)nextf[bucket]) - M_OVERHEAD == last_op) {
last_op = NULL;
}
nextf[bucket] = nextf[bucket]->ov_next;
#ifdef DEBUGGING_MSTATS
nmalloc[bucket]--;
start_slack -= M_OVERHEAD;
#endif
add_to_chain(ret, (BUCKET_SIZE_NO_SURPLUS(bucket) +
POW2_OPTIMIZE_SURPLUS(bucket)),
size);
return
ret;
}
bucket++;
}
return
NULL;
}
static
union
overhead *
getpages(MEM_SIZE needed,
int
*nblksp,
int
bucket)
{
MEM_SIZE require = needed - sbrked_remains;
char
*cp;
union
overhead *ovp;
MEM_SIZE slack = 0;
if
(sbrk_goodness > 0) {
if
(!last_sbrk_top && require < (MEM_SIZE)FIRST_SBRK)
require = FIRST_SBRK;
else
if
(require < (MEM_SIZE)MIN_SBRK) require = MIN_SBRK;
if
(require < (Size_t)(goodsbrk * MIN_SBRK_FRAC1000 / 1000))
require = goodsbrk * MIN_SBRK_FRAC1000 / 1000;
require = ((require - 1 + MIN_SBRK) / MIN_SBRK) * MIN_SBRK;
}
else
{
require = needed;
last_sbrk_top = 0;
sbrked_remains = 0;
}
DEBUG_m(PerlIO_printf(Perl_debug_log,
"sbrk(%ld) for %ld-byte-long arena\n"
,
(
long
)require, (
long
) needed));
cp = (
char
*)sbrk(require);
#ifdef DEBUGGING_MSTATS
sbrks++;
#endif
if
(cp == last_sbrk_top) {
sbrk_goodness++;
ovp = (
union
overhead *) (cp - sbrked_remains);
last_op = cp - sbrked_remains;
sbrked_remains = require - (needed - sbrked_remains);
}
else
if
(cp == (
char
*)-1) {
ovp = (
union
overhead *)emergency_sbrk(needed);
if
(ovp == (
union
overhead *)-1)
return
0;
if
(((
char
*)ovp) > last_op) {
last_op = 0;
}
return
ovp;
}
else
{
long
add = sbrked_remains;
char
*newcp;
if
(sbrked_remains) {
add_to_chain((
void
*)(last_sbrk_top - sbrked_remains),
sbrked_remains, 0);
}
slack = 0;
if
(PTR2UV(cp) & (WANTED_ALIGNMENT - 1)) {
slack = WANTED_ALIGNMENT - (PTR2UV(cp) & (WANTED_ALIGNMENT - 1));
add += slack;
}
if
(add) {
DEBUG_m(PerlIO_printf(Perl_debug_log,
"sbrk(%ld) to fix non-continuous/off-page sbrk:\n\t%ld for alignment,\t%ld were assumed to come from the tail of the previous sbrk\n"
,
(
long
)add, (
long
) slack,
(
long
) sbrked_remains));
newcp = (
char
*)sbrk(add);
#if defined(DEBUGGING_MSTATS)
sbrks++;
sbrk_slack += add;
#endif
if
(newcp != cp + require) {
DEBUG_m(PerlIO_printf(Perl_debug_log,
"failed to fix bad sbrk()\n"
));
#ifdef PACK_MALLOC
if
(slack) {
MALLOC_UNLOCK;
fatalcroak(
"panic: Off-page sbrk\n"
);
}
#endif
if
(sbrked_remains) {
#if defined(DEBUGGING_MSTATS)
sbrk_slack += require;
#endif
require = needed;
DEBUG_m(PerlIO_printf(Perl_debug_log,
"straight sbrk(%ld)\n"
,
(
long
)require));
cp = (
char
*)sbrk(require);
#ifdef DEBUGGING_MSTATS
sbrks++;
#endif
if
(cp == (
char
*)-1)
return
0;
}
sbrk_goodness = -1;
}
else
{
cp += slack;
require += sbrked_remains;
}
}
if
(last_sbrk_top) {
sbrk_goodness -= SBRK_FAILURE_PRICE;
}
ovp = (
union
overhead *) cp;
# if NEEDED_ALIGNMENT > MEM_ALIGNBYTES
if
(PTR2UV(ovp) & (NEEDED_ALIGNMENT - 1))
fatalcroak(
"Misalignment of sbrk()\n"
);
else
# endif
if
(PTR2UV(ovp) & (MEM_ALIGNBYTES - 1)) {
DEBUG_m(PerlIO_printf(Perl_debug_log,
"fixing sbrk(): %d bytes off machine alignment\n"
,
(
int
)(PTR2UV(ovp) & (MEM_ALIGNBYTES - 1))));
ovp = INT2PTR(
union
overhead *,(PTR2UV(ovp) + MEM_ALIGNBYTES) &
(MEM_ALIGNBYTES - 1));
(*nblksp)--;
# if defined(DEBUGGING_MSTATS)
sbrk_slack += (1 << (bucket >> BUCKET_POW2_SHIFT));
# endif
}
;
sbrked_remains = require - needed;
last_op = cp;
}
#if !defined(PLAIN_MALLOC) && !defined(NO_FANCY_MALLOC)
emergency_buffer_last_req = 0;
#endif
last_sbrk_top = cp + require;
#ifdef DEBUGGING_MSTATS
goodsbrk += require;
#endif
return
ovp;
}
static
int
getpages_adjacent(MEM_SIZE require)
{
if
(require <= sbrked_remains) {
sbrked_remains -= require;
}
else
{
char
*cp;
require -= sbrked_remains;
cp = (
char
*) sbrk(require);
#ifdef DEBUGGING_MSTATS
sbrks++;
goodsbrk += require;
#endif
if
(cp == last_sbrk_top) {
sbrked_remains = 0;
last_sbrk_top = cp + require;
}
else
{
if
(cp == (
char
*)-1) {
#ifdef DEBUGGING_MSTATS
goodsbrk -= require;
#endif
return
0;
}
if
(sbrked_remains)
add_to_chain((
void
*)(last_sbrk_top - sbrked_remains),
sbrked_remains, 0);
add_to_chain((
void
*)cp, require, 0);
sbrk_goodness -= SBRK_FAILURE_PRICE;
sbrked_remains = 0;
last_sbrk_top = 0;
last_op = 0;
return
0;
}
}
return
1;
}
static
void
morecore(
int
bucket)
{
union
overhead *ovp;
int
rnu;
int
nblks;
MEM_SIZE siz, needed;
static
int
were_called = 0;
if
(nextf[bucket])
return
;
#ifndef NO_PERL_MALLOC_ENV
if
(!were_called) {
were_called = 1;
if
(!MallocCfg[MallocCfg_skip_cfg_env]) {
char
*s =
getenv
(
"PERL_MALLOC_OPT"
), *t = s;
const
char
*off;
const
char
*opts = PERL_MALLOC_OPT_CHARS;
int
changed = 0;
while
( t && t[0] && t[1] ==
'='
&& ((off =
strchr
(opts, *t))) ) {
IV val = 0;
t += 2;
while
(isDIGIT(*t))
val = 10*val + *t++ -
'0'
;
if
(!*t || *t ==
';'
) {
if
(MallocCfg[off - opts] != val)
changed = 1;
MallocCfg[off - opts] = val;
if
(*t)
t++;
}
}
if
(t && *t) {
dTHX;
MYMALLOC_WRITE2STDERR(
"Unrecognized part of PERL_MALLOC_OPT: \""
);
MYMALLOC_WRITE2STDERR(t);
MYMALLOC_WRITE2STDERR(
"\"\n"
);
}
if
(changed)
MallocCfg[MallocCfg_cfg_env_read] = 1;
}
}
#endif
if
(bucket ==
sizeof
(MEM_SIZE)*8*BUCKETS_PER_POW2) {
MALLOC_UNLOCK;
croak2(
"%s"
,
"Out of memory during ridiculously large request"
);
}
if
(bucket > max_bucket)
max_bucket = bucket;
rnu = ( (bucket <= (LOG_OF_MIN_ARENA << BUCKET_POW2_SHIFT))
? LOG_OF_MIN_ARENA
: (bucket >> BUCKET_POW2_SHIFT) );
nblks = 1 << (rnu - (bucket >> BUCKET_POW2_SHIFT));
needed = ((MEM_SIZE)1 << rnu) + POW2_OPTIMIZE_SURPLUS(bucket);
if
(nextf[rnu << BUCKET_POW2_SHIFT]) {
ovp = nextf[rnu << BUCKET_POW2_SHIFT] - 1 + CHUNK_SHIFT;
nextf[rnu << BUCKET_POW2_SHIFT]
= nextf[rnu << BUCKET_POW2_SHIFT]->ov_next;
#ifdef DEBUGGING_MSTATS
nmalloc[rnu << BUCKET_POW2_SHIFT]--;
start_slack -= M_OVERHEAD;
#endif
DEBUG_m(PerlIO_printf(Perl_debug_log,
"stealing %ld bytes from %ld arena\n"
,
(
long
) needed, (
long
) rnu << BUCKET_POW2_SHIFT));
}
else
if
(chunk_chain
&& (ovp = (
union
overhead*) get_from_chain(needed))) {
DEBUG_m(PerlIO_printf(Perl_debug_log,
"stealing %ld bytes from chain\n"
,
(
long
) needed));
}
else
if
( (ovp = (
union
overhead*)
get_from_bigger_buckets((rnu << BUCKET_POW2_SHIFT) + 1,
needed)) ) {
DEBUG_m(PerlIO_printf(Perl_debug_log,
"stealing %ld bytes from bigger buckets\n"
,
(
long
) needed));
}
else
if
(needed <= sbrked_remains) {
ovp = (
union
overhead *)(last_sbrk_top - sbrked_remains);
sbrked_remains -= needed;
last_op = (
char
*)ovp;
}
else
ovp = getpages(needed, &nblks, bucket);
if
(!ovp)
return
;
FILL_DEADBEEF((unsigned
char
*)ovp, needed);
siz = BUCKET_SIZE_NO_SURPLUS(bucket);
#ifdef PACK_MALLOC
*(u_char*)ovp = bucket;
if
(bucket <= MAX_PACKED) {
ovp = (
union
overhead *) ((
char
*)ovp + BLK_SHIFT(bucket));
nblks = N_BLKS(bucket);
# ifdef DEBUGGING_MSTATS
start_slack += BLK_SHIFT(bucket);
# endif
}
else
if
(bucket < LOG_OF_MIN_ARENA * BUCKETS_PER_POW2) {
ovp = (
union
overhead *) ((
char
*)ovp + BLK_SHIFT(bucket));
siz -=
sizeof
(
union
overhead);
}
else
ovp++;
#endif /* PACK_MALLOC */
nextf[bucket] = ovp;
#ifdef DEBUGGING_MSTATS
nmalloc[bucket] += nblks;
if
(bucket > MAX_PACKED) {
start_slack += M_OVERHEAD * nblks;
}
#endif
while
(--nblks > 0) {
ovp->ov_next = (
union
overhead *)((caddr_t)ovp + siz);
ovp = (
union
overhead *)((caddr_t)ovp + siz);
}
ovp->ov_next = (
union
overhead *)NULL;
#ifdef PACK_MALLOC
if
(bucket == 7*BUCKETS_PER_POW2) {
union
overhead *n_op = nextf[7*BUCKETS_PER_POW2]->ov_next;
nextf[7*BUCKETS_PER_POW2] =
(
union
overhead *)((caddr_t)nextf[7*BUCKETS_PER_POW2]
-
sizeof
(
union
overhead));
nextf[7*BUCKETS_PER_POW2]->ov_next = n_op;
}
#endif /* !PACK_MALLOC */
}
Free_t
Perl_mfree(Malloc_t where)
{
MEM_SIZE size;
union
overhead *ovp;
char
*cp = (
char
*)where;
#ifdef PACK_MALLOC
u_char bucket;
#endif
DEBUG_m(PerlIO_printf(Perl_debug_log,
"0x%"
UVxf
": (%05lu) free\n"
,
PTR2UV(cp), (unsigned
long
)(PL_an++)));
if
(cp == NULL)
return
;
#ifdef DEBUGGING
if
(PTR2UV(cp) & (MEM_ALIGNBYTES - 1))
croak(
"%s"
,
"wrong alignment in free()"
);
#endif
ovp = (
union
overhead *)((caddr_t)cp
-
sizeof
(
union
overhead) * CHUNK_SHIFT);
#ifdef PACK_MALLOC
bucket = OV_INDEX(ovp);
#endif
#ifdef IGNORE_SMALL_BAD_FREE
if
((bucket >= FIRST_BUCKET_WITH_CHECK)
&& (OV_MAGIC(ovp, bucket) != MAGIC))
#else
if
(OV_MAGIC(ovp, bucket) != MAGIC)
#endif
{
static
int
bad_free_warn = -1;
if
(bad_free_warn == -1) {
dTHX;
char
*pbf = PerlEnv_getenv(
"PERL_BADFREE"
);
bad_free_warn = (pbf) ? strNE(
"0"
, pbf) : 1;
}
if
(!bad_free_warn)
return
;
#ifdef RCHECK
{
dTHX;
if
(!PERL_IS_ALIVE || !PL_curcop)
Perl_ck_warner_d(aTHX_ packWARN(WARN_MALLOC),
"%s free() ignored (RMAGIC, PERL_CORE)"
,
ovp->ov_rmagic == RMAGIC - 1 ?
"Duplicate"
:
"Bad"
);
}
#else
{
dTHX;
if
(!PERL_IS_ALIVE || !PL_curcop)
Perl_ck_warner_d(aTHX_ packWARN(WARN_MALLOC),
"%s"
,
"Bad free() ignored (PERL_CORE)"
);
}
#endif
return
;
}
#ifdef RCHECK
ASSERT(ovp->ov_rmagic == RMAGIC,
"chunk's head overwrite"
);
if
(OV_INDEX(ovp) <= MAX_SHORT_BUCKET) {
int
i;
MEM_SIZE nbytes = ovp->ov_size + 1;
if
((i = nbytes & (RMAGIC_SZ-1))) {
i = RMAGIC_SZ - i;
while
(i--) {
ASSERT(((caddr_t)ovp + nbytes - RMAGIC_SZ)[i] == RMAGIC_C,
"chunk's tail overwrite"
);
}
}
nbytes = (nbytes + (RMAGIC_SZ-1)) & ~(RMAGIC_SZ-1);
ASSERT(((u_int *)((caddr_t)ovp + nbytes))[-1] == RMAGIC,
"chunk's tail overwrite"
);
FILLCHECK_DEADBEEF((unsigned
char
*)((caddr_t)ovp + nbytes),
BUCKET_SIZE(OV_INDEX(ovp)) - nbytes);
}
FILL_DEADBEEF((unsigned
char
*)(ovp+CHUNK_SHIFT),
BUCKET_SIZE_REAL(OV_INDEX(ovp)) + RMAGIC_SZ);
ovp->ov_rmagic = RMAGIC - 1;
#endif
ASSERT(OV_INDEX(ovp) < NBUCKETS,
"chunk's head overwrite"
);
size = OV_INDEX(ovp);
MALLOC_LOCK;
ovp->ov_next = nextf[size];
nextf[size] = ovp;
MALLOC_UNLOCK;
}
Malloc_t
Perl_realloc(
void
*mp,
size_t
nbytes)
{
MEM_SIZE onb;
union
overhead *ovp;
char
*res;
int
prev_bucket;
int
bucket;
int
incr;
char
*cp = (
char
*)mp;
#ifdef DEBUGGING
MEM_SIZE size = nbytes;
if
((
long
)nbytes < 0)
croak(
"%s"
,
"panic: realloc"
);
#endif
BARK_64K_LIMIT(
"Reallocation"
,nbytes,size);
if
(!cp)
return
Perl_malloc(nbytes);
ovp = (
union
overhead *)((caddr_t)cp
-
sizeof
(
union
overhead) * CHUNK_SHIFT);
bucket = OV_INDEX(ovp);
#ifdef IGNORE_SMALL_BAD_FREE
if
((bucket >= FIRST_BUCKET_WITH_CHECK)
&& (OV_MAGIC(ovp, bucket) != MAGIC))
#else
if
(OV_MAGIC(ovp, bucket) != MAGIC)
#endif
{
static
int
bad_free_warn = -1;
if
(bad_free_warn == -1) {
dTHX;
char
*pbf = PerlEnv_getenv(
"PERL_BADFREE"
);
bad_free_warn = (pbf) ? strNE(
"0"
, pbf) : 1;
}
if
(!bad_free_warn)
return
NULL;
#ifdef RCHECK
{
dTHX;
if
(!PERL_IS_ALIVE || !PL_curcop)
Perl_ck_warner_d(aTHX_ packWARN(WARN_MALLOC),
"%srealloc() %signored"
,
(ovp->ov_rmagic == RMAGIC - 1 ?
""
:
"Bad "
),
ovp->ov_rmagic == RMAGIC - 1
?
"of freed memory "
:
""
);
}
#else
{
dTHX;
if
(!PERL_IS_ALIVE || !PL_curcop)
Perl_ck_warner_d(aTHX_ packWARN(WARN_MALLOC),
"%s"
,
"Bad realloc() ignored"
);
}
#endif
return
NULL;
}
onb = BUCKET_SIZE_REAL(bucket);
if
(nbytes > onb) incr = 1;
else
{
#ifdef DO_NOT_TRY_HARDER_WHEN_SHRINKING
if
(
nbytes > ( (onb >> 1) - M_OVERHEAD )
# ifdef TWO_POT_OPTIMIZE
|| (bucket == FIRST_BIG_POW2 && nbytes >= LAST_SMALL_BOUND )
# endif
)
#else /* !DO_NOT_TRY_HARDER_WHEN_SHRINKING */
prev_bucket = ( (bucket > MAX_PACKED + 1)
? bucket - BUCKETS_PER_POW2
: bucket - 1);
if
(nbytes > BUCKET_SIZE_REAL(prev_bucket))
#endif /* !DO_NOT_TRY_HARDER_WHEN_SHRINKING */
incr = 0;
else
incr = -1;
}
#ifdef STRESS_REALLOC
goto
hard_way;
#endif
if
(incr == 0) {
inplace_label:
#ifdef RCHECK
if
(OV_INDEX(ovp) <= MAX_SHORT_BUCKET) {
int
i, nb = ovp->ov_size + 1;
if
((i = nb & (RMAGIC_SZ-1))) {
i = RMAGIC_SZ - i;
while
(i--) {
ASSERT(((caddr_t)ovp + nb - RMAGIC_SZ)[i] == RMAGIC_C,
"chunk's tail overwrite"
);
}
}
nb = (nb + (RMAGIC_SZ-1)) & ~(RMAGIC_SZ-1);
ASSERT(((u_int *)((caddr_t)ovp + nb))[-1] == RMAGIC,
"chunk's tail overwrite"
);
FILLCHECK_DEADBEEF((unsigned
char
*)((caddr_t)ovp + nb),
BUCKET_SIZE(OV_INDEX(ovp)) - nb);
if
(nbytes > ovp->ov_size + 1 - M_OVERHEAD)
FILL_FEEDADAD((unsigned
char
*)cp + ovp->ov_size + 1 - M_OVERHEAD,
nbytes - (ovp->ov_size + 1 - M_OVERHEAD));
else
FILL_DEADBEEF((unsigned
char
*)cp + nbytes,
nb - M_OVERHEAD + RMAGIC_SZ - nbytes);
nbytes += M_OVERHEAD;
ovp->ov_size = nbytes - 1;
if
((i = nbytes & (RMAGIC_SZ-1))) {
i = RMAGIC_SZ - i;
while
(i--)
((caddr_t)ovp + nbytes - RMAGIC_SZ)[i]
= RMAGIC_C;
}
nbytes = (nbytes + (RMAGIC_SZ-1)) & ~(RMAGIC_SZ - 1);
((u_int *)((caddr_t)ovp + nbytes))[-1] = RMAGIC;
}
#endif
res = cp;
DEBUG_m(PerlIO_printf(Perl_debug_log,
"0x%"
UVxf
": (%05lu) realloc %ld bytes inplace\n"
,
PTR2UV(res),(unsigned
long
)(PL_an++),
(
long
)size));
}
else
if
(incr == 1 && (cp - M_OVERHEAD == last_op)
&& (onb > (1 << LOG_OF_MIN_ARENA))) {
MEM_SIZE require, newarena = nbytes,
pow
;
int
shiftr;
POW2_OPTIMIZE_ADJUST(newarena);
newarena = newarena + M_OVERHEAD;
shiftr = (newarena - 1) >> LOG_OF_MIN_ARENA;
pow
= LOG_OF_MIN_ARENA + 1;
while
(shiftr >>= 1)
pow
++;
newarena = (1 <<
pow
) + POW2_OPTIMIZE_SURPLUS(
pow
* BUCKETS_PER_POW2);
require = newarena - onb - M_OVERHEAD;
MALLOC_LOCK;
if
(cp - M_OVERHEAD == last_op
&& getpages_adjacent(require)) {
#ifdef DEBUGGING_MSTATS
nmalloc[bucket]--;
nmalloc[
pow
* BUCKETS_PER_POW2]++;
#endif
if
(
pow
* BUCKETS_PER_POW2 > (MEM_SIZE)max_bucket)
max_bucket =
pow
* BUCKETS_PER_POW2;
*(cp - M_OVERHEAD) =
pow
* BUCKETS_PER_POW2;
MALLOC_UNLOCK;
goto
inplace_label;
}
else
{
MALLOC_UNLOCK;
goto
hard_way;
}
}
else
{
hard_way:
DEBUG_m(PerlIO_printf(Perl_debug_log,
"0x%"
UVxf
": (%05lu) realloc %ld bytes the hard way\n"
,
PTR2UV(cp),(unsigned
long
)(PL_an++),
(
long
)size));
if
((res = (
char
*)Perl_malloc(nbytes)) == NULL)
return
(NULL);
if
(cp != res)
Copy(cp, res, (MEM_SIZE)(nbytes<onb?nbytes:onb),
char
);
Perl_mfree(cp);
}
return
((Malloc_t)res);
}
Malloc_t
Perl_calloc(
size_t
elements,
size_t
size)
{
long
sz = elements * size;
Malloc_t p = Perl_malloc(sz);
if
(p) {
memset
((
void
*)p, 0, sz);
}
return
p;
}
char
*
Perl_strdup(
const
char
*s)
{
MEM_SIZE l =
strlen
(s);
char
*s1 = (
char
*)Perl_malloc(l+1);
return
(
char
*)CopyD(s, s1, (MEM_SIZE)(l+1),
char
);
}
int
Perl_putenv(
char
*a)
{
dTHX;
char
*var;
char
*val = a;
MEM_SIZE l;
char
buf[80];
while
(*val && *val !=
'='
)
val++;
if
(!*val)
return
-1;
l = val - a;
if
(l <
sizeof
(buf))
var = buf;
else
var = (
char
*)Perl_malloc(l + 1);
Copy(a, var, l,
char
);
var[l + 1] = 0;
my_setenv(var, val+1);
if
(var != buf)
Perl_mfree(var);
return
0;
}
MEM_SIZE
Perl_malloced_size(
void
*p)
{
union
overhead *
const
ovp = (
union
overhead *)
((caddr_t)p -
sizeof
(
union
overhead) * CHUNK_SHIFT);
const
int
bucket = OV_INDEX(ovp);
PERL_ARGS_ASSERT_MALLOCED_SIZE;
#ifdef RCHECK
if
(bucket <= MAX_SHORT_BUCKET) {
const
MEM_SIZE size = BUCKET_SIZE_REAL(bucket);
ovp->ov_size = size + M_OVERHEAD - 1;
*((u_int *)((caddr_t)ovp + size + M_OVERHEAD - RMAGIC_SZ)) = RMAGIC;
}
#endif
return
BUCKET_SIZE_REAL(bucket);
}
MEM_SIZE
Perl_malloc_good_size(
size_t
wanted)
{
return
BUCKET_SIZE_REAL(adjust_size_and_find_bucket(&wanted));
}
# ifdef BUCKETS_ROOT2
# define MIN_EVEN_REPORT 6
# else
# define MIN_EVEN_REPORT MIN_BUCKET
# endif
int
Perl_get_mstats(pTHX_ perl_mstats_t *buf,
int
buflen,
int
level)
{
#ifdef DEBUGGING_MSTATS
int
i, j;
union
overhead *p;
struct
chunk_chain_s* nextchain;
PERL_ARGS_ASSERT_GET_MSTATS;
buf->topbucket = buf->topbucket_ev = buf->topbucket_odd
= buf->totfree = buf->total = buf->total_chain = 0;
buf->minbucket = MIN_BUCKET;
MALLOC_LOCK;
for
(i = MIN_BUCKET ; i < NBUCKETS; i++) {
for
(j = 0, p = nextf[i]; p; p = p->ov_next, j++)
;
if
(i < buflen) {
buf->nfree[i] = j;
buf->ntotal[i] = nmalloc[i];
}
buf->totfree += j * BUCKET_SIZE_REAL(i);
buf->total += nmalloc[i] * BUCKET_SIZE_REAL(i);
if
(nmalloc[i]) {
i % 2 ? (buf->topbucket_odd = i) : (buf->topbucket_ev = i);
buf->topbucket = i;
}
}
nextchain = chunk_chain;
while
(nextchain) {
buf->total_chain += nextchain->size;
nextchain = nextchain->next;
}
buf->total_sbrk = goodsbrk + sbrk_slack;
buf->sbrks = sbrks;
buf->sbrk_good = sbrk_goodness;
buf->sbrk_slack = sbrk_slack;
buf->start_slack = start_slack;
buf->sbrked_remains = sbrked_remains;
MALLOC_UNLOCK;
buf->nbuckets = NBUCKETS;
if
(level) {
for
(i = MIN_BUCKET ; i < NBUCKETS; i++) {
if
(i >= buflen)
break
;
buf->bucket_mem_size[i] = BUCKET_SIZE_NO_SURPLUS(i);
buf->bucket_available_size[i] = BUCKET_SIZE_REAL(i);
}
}
#else /* defined DEBUGGING_MSTATS */
PerlIO_printf(Perl_error_log,
"perl not compiled with DEBUGGING_MSTATS\n"
);
#endif /* defined DEBUGGING_MSTATS */
return
0;
}
void
Perl_dump_mstats(pTHX_
const
char
*s)
{
#ifdef DEBUGGING_MSTATS
int
i;
perl_mstats_t buffer;
UV nf[NBUCKETS];
UV nt[NBUCKETS];
PERL_ARGS_ASSERT_DUMP_MSTATS;
buffer.nfree = nf;
buffer.ntotal = nt;
get_mstats(&buffer, NBUCKETS, 0);
if
(s)
PerlIO_printf(Perl_error_log,
"Memory allocation statistics %s (buckets %"
IVdf
"(%"
IVdf
")..%"
IVdf
"(%"
IVdf
")\n"
,
s,
(IV)BUCKET_SIZE_REAL(MIN_BUCKET),
(IV)BUCKET_SIZE_NO_SURPLUS(MIN_BUCKET),
(IV)BUCKET_SIZE_REAL(buffer.topbucket),
(IV)BUCKET_SIZE_NO_SURPLUS(buffer.topbucket));
PerlIO_printf(Perl_error_log,
"%8"
IVdf
" free:"
, buffer.totfree);
for
(i = MIN_EVEN_REPORT; i <= buffer.topbucket; i += BUCKETS_PER_POW2) {
PerlIO_printf(Perl_error_log,
((i < 8*BUCKETS_PER_POW2 || i == 10*BUCKETS_PER_POW2)
?
" %5"
UVuf
: ((i < 12*BUCKETS_PER_POW2) ?
" %3"
UVuf
:
" %"
UVuf)),
buffer.nfree[i]);
}
#ifdef BUCKETS_ROOT2
PerlIO_printf(Perl_error_log,
"\n\t "
);
for
(i = MIN_BUCKET + 1; i <= buffer.topbucket_odd; i += BUCKETS_PER_POW2) {
PerlIO_printf(Perl_error_log,
((i < 8*BUCKETS_PER_POW2 || i == 10*BUCKETS_PER_POW2)
?
" %5"
UVuf
: ((i < 12*BUCKETS_PER_POW2) ?
" %3"
UVuf :
" %"
UVuf)),
buffer.nfree[i]);
}
#endif
PerlIO_printf(Perl_error_log,
"\n%8"
IVdf
" used:"
,
buffer.total - buffer.totfree);
for
(i = MIN_EVEN_REPORT; i <= buffer.topbucket; i += BUCKETS_PER_POW2) {
PerlIO_printf(Perl_error_log,
((i < 8*BUCKETS_PER_POW2 || i == 10*BUCKETS_PER_POW2)
?
" %5"
IVdf
: ((i < 12*BUCKETS_PER_POW2) ?
" %3"
IVdf :
" %"
IVdf)),
buffer.ntotal[i] - buffer.nfree[i]);
}
#ifdef BUCKETS_ROOT2
PerlIO_printf(Perl_error_log,
"\n\t "
);
for
(i = MIN_BUCKET + 1; i <= buffer.topbucket_odd; i += BUCKETS_PER_POW2) {
PerlIO_printf(Perl_error_log,
((i < 8*BUCKETS_PER_POW2 || i == 10*BUCKETS_PER_POW2)
?
" %5"
IVdf
: ((i < 12*BUCKETS_PER_POW2) ?
" %3"
IVdf :
" %"
IVdf)),
buffer.ntotal[i] - buffer.nfree[i]);
}
#endif
PerlIO_printf(Perl_error_log,
"\nTotal sbrk(): %"
IVdf
"/%"
IVdf
":%"
IVdf
". Odd ends: pad+heads+chain+tail: %"
IVdf
"+%"
IVdf
"+%"
IVdf
"+%"
IVdf
".\n"
,
buffer.total_sbrk, buffer.sbrks, buffer.sbrk_good,
buffer.sbrk_slack, buffer.start_slack,
buffer.total_chain, buffer.sbrked_remains);
#else /* DEBUGGING_MSTATS */
PerlIO_printf(Perl_error_log,
"%s: perl not compiled with DEBUGGING_MSTATS\n"
,s);
#endif /* DEBUGGING_MSTATS */
}
#ifdef USE_PERL_SBRK
# if defined(PURIFY)
# define PERL_SBRK_VIA_MALLOC
# endif
# ifdef PERL_SBRK_VIA_MALLOC
# ifndef SYSTEM_ALLOC
# define SYSTEM_ALLOC(a) malloc(a)
# endif
# ifndef SYSTEM_ALLOC_ALIGNMENT
# define SYSTEM_ALLOC_ALIGNMENT MEM_ALIGNBYTES
# endif
# endif /* PERL_SBRK_VIA_MALLOC */
static
IV Perl_sbrk_oldchunk;
static
long
Perl_sbrk_oldsize;
# define PERLSBRK_32_K (1<<15)
# define PERLSBRK_64_K (1<<16)
Malloc_t
Perl_sbrk(
int
size)
{
IV got;
int
small, reqsize;
if
(!size)
return
0;
reqsize = size;
#ifdef PACK_MALLOC
size = (size + 0x7ff) & ~0x7ff;
#endif
if
(size <= Perl_sbrk_oldsize) {
got = Perl_sbrk_oldchunk;
Perl_sbrk_oldchunk += size;
Perl_sbrk_oldsize -= size;
}
else
{
if
(size >= PERLSBRK_32_K) {
small = 0;
}
else
{
size = PERLSBRK_64_K;
small = 1;
}
# if NEEDED_ALIGNMENT > SYSTEM_ALLOC_ALIGNMENT
size += NEEDED_ALIGNMENT - SYSTEM_ALLOC_ALIGNMENT;
# endif
got = (IV)SYSTEM_ALLOC(size);
# if NEEDED_ALIGNMENT > SYSTEM_ALLOC_ALIGNMENT
got = (got + NEEDED_ALIGNMENT - 1) & ~(NEEDED_ALIGNMENT - 1);
# endif
if
(small) {
Perl_sbrk_oldchunk = got + reqsize;
Perl_sbrk_oldsize = size - reqsize;
}
}
DEBUG_m(PerlIO_printf(Perl_debug_log,
"sbrk malloc size %ld (reqsize %ld), left size %ld, give addr 0x%"
UVxf
"\n"
,
size, reqsize, Perl_sbrk_oldsize, PTR2UV(got)));
return
(
void
*)got;
}
#endif /* ! defined USE_PERL_SBRK */