mirror of
https://github.com/samba-team/samba.git
synced 2025-12-16 00:23:52 +03:00
1776 lines
35 KiB
C
1776 lines
35 KiB
C
/**
|
|
* @file mprAlloc.c
|
|
* @brief Memory Allocation
|
|
* @overview
|
|
*/
|
|
|
|
/*
|
|
* @copy default
|
|
*
|
|
* Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved.
|
|
*
|
|
* This software is distributed under commercial and open source licenses.
|
|
* You may use the GPL open source license described below or you may acquire
|
|
* a commercial license from Mbedthis Software. You agree to be fully bound
|
|
* by the terms of either license. Consult the LICENSE.TXT distributed with
|
|
* this software for full details.
|
|
*
|
|
* This software is open source; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version. See the GNU General Public License for more
|
|
* details at: http://www.mbedthis.com/downloads/gplLicense.html
|
|
*
|
|
* This program is distributed WITHOUT ANY WARRANTY; without even the
|
|
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
*
|
|
* This GPL license does NOT permit incorporating this software into
|
|
* proprietary programs. If you are unable to comply with the GPL, you must
|
|
* acquire a commercial license to use this software. Commercial licenses
|
|
* for this software and support services are available from Mbedthis
|
|
* Software at http://www.mbedthis.com
|
|
*
|
|
* @end
|
|
*/
|
|
|
|
/********************************* Includes ***********************************/
|
|
|
|
#define UNSAFE_FUNCTIONS_OK 1
|
|
|
|
#include "mpr.h"
|
|
|
|
/******************************* Local Defines ********************************/
|
|
/*
|
|
* Set to 1 to disable slab based allocations
|
|
*/
|
|
#define NO_SLAB 0
|
|
|
|
/*
|
|
* Validation mode is quite slow
|
|
*/
|
|
#define VALIDATE_ALLOC 0
|
|
#if VALIDATE_ALLOC
|
|
#define VALIDATE_BLOCK(ptr) mprValidateBlock(ptr)
|
|
#else
|
|
#define VALIDATE_BLOCK(ptr)
|
|
#endif
|
|
|
|
/*
|
|
* Align on 4 bytes if squeeze, Otherwize on 16 bytes.
|
|
*/
|
|
#define HDR_SIZE MPR_BLK_HDR_SIZE
|
|
|
|
#define APP_MAGIC 0xa571cb80
|
|
#define ALLOC_MAGIC 0xe814ecc0
|
|
|
|
/*
|
|
* This must be at least one word to ensure that the smallest allocation is
|
|
* 4 bytes. Slab allocations need at least one word to store their next ptr.
|
|
*/
|
|
#define ALLOC_ALIGN(x) (((x)+3)&~3)
|
|
#define GET_HDR(ptr) ((MprBlk*) (((char*) (ptr)) - HDR_SIZE))
|
|
#define GET_PTR(bp) ((void*) (((char*) (bp)) + HDR_SIZE))
|
|
#define VALID_HDR(bp) (((bp)->flags & ~0x3F) == ALLOC_MAGIC)
|
|
#define VALID_BLK(ptr) (VALID_HDR(GET_HDR(ptr)))
|
|
|
|
/*
|
|
* In production releases, mprAssert will compile out (not be included)
|
|
* but CHECK_HDR will remain even in production builds.
|
|
*/
|
|
#define CHECK_HDR(bp) \
|
|
if (1) { if (! VALID_HDR(bp)) { mprAllocAbort(); } } else
|
|
|
|
/*
|
|
* Chunk the slabs into 32 byte increments.
|
|
* This allows for allocations up to 512 bytes via slabs and maximizes
|
|
* sharing of slab allocations.
|
|
*
|
|
* Index map:
|
|
* 0 == 32 bytes
|
|
* 1 == 64 bytes
|
|
* 2 == 96 bytes
|
|
* 3 == 128 bytes
|
|
* 4 == 160 bytes
|
|
* 5 == 192 bytes
|
|
* 6 == 224 bytes
|
|
* 7 == 256 bytes
|
|
* 8 == 288 bytes
|
|
* 9 == 320 bytes
|
|
* 10 == 352 bytes
|
|
* 11 == 384 bytes
|
|
* 12 == 416 bytes
|
|
* 13 == 448 bytes
|
|
* 14 == 480 bytes
|
|
* 15 == 512 bytes
|
|
*/
|
|
#define SLAB_ALIGN(size) ((size + 31) & ~31)
|
|
#define GET_SLAB(size) (size >> 6)
|
|
|
|
/*
|
|
* Block flags
|
|
*/
|
|
#define ALLOC_FLAGS_FREE 0x1 /* Block is free */
|
|
#define ALLOC_FLAGS_FREEING 0x2 /* Block is being freed */
|
|
#define ALLOC_FLAGS_SLAB_BLOCK 0x4 /* Block was allocated from slab */
|
|
#define ALLOC_FLAGS_REQUIRED 0x8 /* Block is required by alloc */
|
|
#define ALLOC_FLAGS_KEEP 0x10 /* Keep block - don't mprFree */
|
|
#define ALLOC_FLAGS_DONT_OS_FREE 0x20 /* Don't return mem to O/S */
|
|
#define ALLOC_FLAGS_IS_SLAB 0x40 /* Block is a slab */
|
|
|
|
#if BLD_DEBUG && !BREW
|
|
/*
|
|
* Set this address to break when this address is allocated or freed. This is
|
|
* a block address (not a user ptr).
|
|
*/
|
|
static MprBlk *stopAlloc;
|
|
#endif
|
|
|
|
#if !BREW
|
|
static MprCtx rootCtx; /* Root context if none supplied */
|
|
#endif
|
|
|
|
/***************************** Forward Declarations ***************************/
|
|
|
|
static int mprAllocException(MPR_LOC_DEC(ptr, loc), uint size, bool granted);
|
|
static void slabFree(MprBlk *bp);
|
|
static int growSlab(MPR_LOC_DEC(ctx, loc), MprSlab *slab, uint size, uint inc);
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Put first in file so it is easy to locate in a debugger
|
|
*/
|
|
|
|
void mprBreakpoint(const char *loc, const char *msg)
|
|
{
|
|
}
|
|
|
|
/******************************************************************************/
|
|
#if (WIN || BREW_SIMULATOR) && BLD_DEBUG
|
|
|
|
int crtReportHook(int type, char *msg, int *retval)
|
|
{
|
|
printf("%s\n", msg);
|
|
*retval = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
#endif
|
|
/******************************************************************************/
|
|
/*
|
|
* Initialize the memory subsystem
|
|
*/
|
|
|
|
MprApp *mprAllocInit(MprAllocCback cback)
|
|
{
|
|
MprAllocStats *stats;
|
|
MprApp *app;
|
|
MprSlab *slab;
|
|
MprBlk *bp, *sp;
|
|
int i;
|
|
|
|
bp = malloc(sizeof(MprApp) + HDR_SIZE);
|
|
mprAssert(bp);
|
|
if (bp == 0) {
|
|
if (cback) {
|
|
(*cback)(0, sizeof(MprApp), 0, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
memset(bp, 0, sizeof(MprApp) + HDR_SIZE);
|
|
|
|
bp->parent = bp;
|
|
bp->size = sizeof(MprApp);
|
|
bp->flags = ALLOC_MAGIC;
|
|
bp->next = bp->prev = bp;
|
|
|
|
#if BLD_FEATURE_ALLOC_LEAK_TRACK
|
|
bp->location = MPR_LOC;
|
|
#endif
|
|
|
|
app = (MprApp*) GET_PTR(bp);
|
|
app->magic = APP_MAGIC;
|
|
|
|
app->alloc.cback = cback;
|
|
app->stackStart = (void*) &app;
|
|
|
|
bp->app = app;
|
|
|
|
app->alloc.slabs = mprAllocZeroedBlock(MPR_LOC_PASS(app, MPR_LOC),
|
|
sizeof(MprSlab) * MPR_MAX_SLAB);
|
|
if (app->alloc.slabs == 0) {
|
|
mprFree(app);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* The slab control structures must not be freed. Set keep to safeguard
|
|
* against accidents.
|
|
*/
|
|
sp = GET_HDR(app->alloc.slabs);
|
|
sp->flags |= ALLOC_FLAGS_KEEP;
|
|
|
|
for (i = 0; i < MPR_MAX_SLAB; i++) {
|
|
/*
|
|
* This is overriden by requestors calling slabAlloc
|
|
*/
|
|
slab = &app->alloc.slabs[i];
|
|
slab->preAllocateIncr = MPR_SLAB_DEFAULT_INC;
|
|
}
|
|
|
|
/*
|
|
* Keep aggregated stats even in production code
|
|
*/
|
|
stats = &app->alloc.stats;
|
|
stats->bytesAllocated += sizeof(MprApp);
|
|
if (stats->bytesAllocated > stats->peakAllocated) {
|
|
stats->peakAllocated = stats->bytesAllocated;
|
|
}
|
|
stats->allocCount++;
|
|
|
|
#if !BREW
|
|
rootCtx = app;
|
|
#endif
|
|
#if (WIN || BREW_SIMULATOR) && BLD_DEBUG
|
|
_CrtSetReportHook(crtReportHook);
|
|
#endif
|
|
return app;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Terminate the alloc module
|
|
*/
|
|
|
|
void mprAllocTerm(MprApp *app)
|
|
{
|
|
MprSlab *slabs;
|
|
MprBlk *appBlk, *slabBlk;
|
|
|
|
/*
|
|
* Must do a carefully ordered cleanup. Need to free all children blocks
|
|
* before freeing the slab memory. Save a local pointer to the slabs.
|
|
*/
|
|
slabs = app->alloc.slabs;
|
|
|
|
/*
|
|
* Free the app and all children. Set DONT_OS_FREE to prevent free() being
|
|
* called on app itself. We need that so we can free the slabs below.
|
|
*/
|
|
appBlk = GET_HDR(app);
|
|
appBlk->flags |= ALLOC_FLAGS_DONT_OS_FREE;
|
|
mprFree(app);
|
|
|
|
/*
|
|
* Slabs are initially marked don't free. We must preserve them while all
|
|
* other blocks are freed. Then we clear the don't free flag and free.
|
|
* Now we don't have an app structure which is used by mprFree. We must
|
|
* fake it.
|
|
*/
|
|
slabBlk = GET_HDR(slabs);
|
|
slabBlk->flags &= ~ALLOC_FLAGS_KEEP;
|
|
mprFree(slabs);
|
|
|
|
/*
|
|
* Now we can finally free the memory for the app structure
|
|
*/
|
|
free(appBlk);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Allocate a block
|
|
*/
|
|
|
|
void *mprAllocBlock(MPR_LOC_DEC(ctx, loc), uint size)
|
|
{
|
|
MprAllocStats *stats;
|
|
MprBlk *bp, *parent;
|
|
MprApp *app;
|
|
int diff;
|
|
|
|
mprAssert(size > 0);
|
|
|
|
if (ctx == 0) {
|
|
#if BREW
|
|
mprAssert(ctx);
|
|
return 0;
|
|
#else
|
|
ctx = rootCtx;
|
|
#endif
|
|
}
|
|
if (size == 0) {
|
|
size = 1;
|
|
}
|
|
|
|
mprAssert(VALID_BLK(ctx));
|
|
parent = GET_HDR(ctx);
|
|
mprAssert(VALID_HDR(parent));
|
|
|
|
CHECK_HDR(parent);
|
|
|
|
size = ALLOC_ALIGN(size);
|
|
|
|
app = parent->app;
|
|
|
|
stats = &app->alloc.stats;
|
|
|
|
mprLock(app->allocLock);
|
|
|
|
stats->bytesAllocated += size + HDR_SIZE;
|
|
if (stats->bytesAllocated > stats->peakAllocated) {
|
|
stats->peakAllocated = stats->bytesAllocated;
|
|
}
|
|
|
|
/*
|
|
* Prevent allocation if over the maximum
|
|
*/
|
|
if (stats->maxMemory && stats->bytesAllocated > stats->maxMemory) {
|
|
stats->bytesAllocated -= (size + HDR_SIZE);
|
|
mprUnlock(app->allocLock);
|
|
if (mprAllocException(MPR_LOC_PASS(ctx, loc), size, 0) < 0) {
|
|
return 0;
|
|
}
|
|
mprLock(app->allocLock);
|
|
}
|
|
|
|
if ((bp = malloc(size + HDR_SIZE)) == 0) {
|
|
mprAssert(bp);
|
|
stats->errors++;
|
|
mprUnlock(app->allocLock);
|
|
mprAllocException(MPR_LOC_PASS(ctx, loc), size, 0);
|
|
return 0;
|
|
}
|
|
|
|
#if BLD_DEBUG
|
|
memset(bp, 0xf7, size + HDR_SIZE);
|
|
#endif
|
|
|
|
#if BLD_DEBUG && !BREW
|
|
if (bp == stopAlloc) {
|
|
mprBreakpoint(MPR_LOC, "breakOnAddr");
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Warn if allocation puts us over the red line
|
|
*/
|
|
if (stats->redLine && stats->bytesAllocated > stats->redLine) {
|
|
mprUnlock(app->allocLock);
|
|
if (mprAllocException(MPR_LOC_PASS(ctx, loc), size, 1) < 0) {
|
|
return 0;
|
|
}
|
|
mprLock(app->allocLock);
|
|
}
|
|
|
|
bp->size = size;
|
|
bp->flags = ALLOC_MAGIC;
|
|
bp->destructor = 0;
|
|
|
|
bp->parent = parent;
|
|
|
|
if (parent->children == 0) {
|
|
parent->children = bp;
|
|
bp->next = bp->prev = bp;
|
|
|
|
} else {
|
|
/*
|
|
* Append to the end of the list. Preserve alloc order
|
|
*/
|
|
bp->next = parent->children;
|
|
bp->prev = parent->children->prev;
|
|
parent->children->prev->next = bp;
|
|
parent->children->prev = bp;
|
|
}
|
|
|
|
bp->children = 0;
|
|
|
|
#if BLD_FEATURE_ALLOC_LEAK_TRACK
|
|
bp->location = loc;
|
|
#endif
|
|
|
|
bp->app = parent->app;
|
|
|
|
VALIDATE_BLOCK(GET_PTR(bp));
|
|
|
|
stats->allocCount++;
|
|
|
|
/*
|
|
* Monitor stack usage
|
|
*/
|
|
diff = (int) bp->app->stackStart - (int) &stats;
|
|
if (diff < 0) {
|
|
app->maxStack -= diff;
|
|
app->stackStart = (void*) &stats;
|
|
diff = 0;
|
|
}
|
|
|
|
if ((uint) diff > app->maxStack) {
|
|
app->maxStack = diff;
|
|
}
|
|
mprUnlock(app->allocLock);
|
|
|
|
return GET_PTR(bp);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Allocate and zero a block
|
|
*/
|
|
|
|
void *mprAllocZeroedBlock(MPR_LOC_DEC(ctx, loc), uint size)
|
|
{
|
|
void *newBlock;
|
|
|
|
MprBlk *bp;
|
|
|
|
bp = GET_HDR(ctx);
|
|
mprAssert(VALID_BLK(ctx));
|
|
|
|
newBlock = mprAllocBlock(MPR_LOC_PASS(ctx, loc), size);
|
|
if (newBlock) {
|
|
memset(newBlock, 0, size);
|
|
}
|
|
return newBlock;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Free a block of memory. Free all children recursively.
|
|
*/
|
|
|
|
int mprFree(void *ptr)
|
|
{
|
|
MprAllocStats *stats;
|
|
MprBlk *bp, *parent, *cp, *firstChild, *prev;
|
|
MprApp *app;
|
|
|
|
if (ptr == 0) {
|
|
return 0;
|
|
}
|
|
|
|
mprAssert(VALID_BLK(ptr));
|
|
VALIDATE_BLOCK(ptr);
|
|
|
|
bp = GET_HDR(ptr);
|
|
|
|
#if BLD_DEBUG && !BREW
|
|
if (bp == stopAlloc) {
|
|
mprBreakpoint(MPR_LOC, "breakOnAddr");
|
|
}
|
|
#endif
|
|
|
|
mprAssert(bp);
|
|
mprAssert(VALID_HDR(bp));
|
|
|
|
CHECK_HDR(bp);
|
|
|
|
/*
|
|
* Test if already freed
|
|
*/
|
|
mprAssert(! (bp->flags & ALLOC_FLAGS_FREE));
|
|
if (bp->flags & ALLOC_FLAGS_FREE) {
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Return if recursive freeing or this is a permanent block
|
|
*/
|
|
app = bp->app;
|
|
mprLock(app->allocLock);
|
|
if (bp->flags & (ALLOC_FLAGS_FREEING | ALLOC_FLAGS_KEEP)) {
|
|
mprUnlock(app->allocLock);
|
|
return 0;
|
|
}
|
|
bp->flags |= ALLOC_FLAGS_FREEING;
|
|
|
|
|
|
/*
|
|
* Call any destructors
|
|
*/
|
|
if (bp->destructor) {
|
|
mprUnlock(app->allocLock);
|
|
if ((bp->destructor)(ptr) < 0) {
|
|
return -1;
|
|
}
|
|
mprLock(app->allocLock);
|
|
bp->destructor = 0;
|
|
}
|
|
|
|
/*
|
|
* Free the children. Free in reverse order so firstChild is preserved
|
|
* during the list scan as an end of list marker.
|
|
*/
|
|
if ((firstChild = bp->children) != 0) {
|
|
cp = firstChild->prev;
|
|
while (cp != firstChild) {
|
|
|
|
mprAssert(VALID_HDR(cp));
|
|
VALIDATE_BLOCK(GET_PTR(cp));
|
|
|
|
prev = cp->prev;
|
|
|
|
/*
|
|
* FUTURE - OPT. Make this inline
|
|
*/
|
|
mprFree(GET_PTR(cp));
|
|
|
|
cp = prev;
|
|
}
|
|
|
|
mprFree(GET_PTR(firstChild));
|
|
|
|
/*
|
|
* Just for clarity
|
|
*/
|
|
bp->children = 0;
|
|
}
|
|
|
|
parent = bp->parent;
|
|
|
|
mprAssert(VALID_HDR(parent));
|
|
|
|
/*
|
|
* Unlink from the parent
|
|
*/
|
|
if (parent->children == bp) {
|
|
if (bp->next == bp) {
|
|
parent->children = 0;
|
|
} else {
|
|
parent->children = bp->next;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Remove from the sibling chain
|
|
*/
|
|
bp->prev->next = bp->next;
|
|
bp->next->prev = bp->prev;
|
|
|
|
bp->flags |= ALLOC_FLAGS_FREE;
|
|
|
|
/*
|
|
* Release the memory. If from a slab, return to the slab. Otherwise,
|
|
* return to the O/S.
|
|
*/
|
|
if (bp->flags & ALLOC_FLAGS_SLAB_BLOCK) {
|
|
slabFree(bp);
|
|
|
|
} else {
|
|
mprAssert(bp);
|
|
|
|
/*
|
|
* Update the stats
|
|
*/
|
|
stats = &bp->app->alloc.stats;
|
|
stats->bytesAllocated -= (bp->size + HDR_SIZE);
|
|
mprAssert(stats->bytesAllocated >= 0);
|
|
|
|
stats->allocCount--;
|
|
mprAssert(stats->allocCount >= 0);
|
|
|
|
#if BLD_DEBUG && !BREW
|
|
if (bp == stopAlloc) {
|
|
mprBreakpoint(MPR_LOC, "breakOnAddr");
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Return to the O/S
|
|
*/
|
|
if (! (bp->flags & ALLOC_FLAGS_DONT_OS_FREE)) {
|
|
free(bp);
|
|
}
|
|
}
|
|
/* OPT */
|
|
if (app != ptr) {
|
|
mprUnlock(app->allocLock);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Rallocate a block
|
|
*/
|
|
|
|
void *mprReallocBlock(MPR_LOC_DEC(ctx, loc), void *ptr, uint size)
|
|
{
|
|
MprBlk *bp, *newbp, *firstChild, *cp;
|
|
MprApp *app;
|
|
void *newPtr;
|
|
|
|
mprAssert(VALID_BLK(ctx));
|
|
mprAssert(size > 0);
|
|
|
|
if (ptr == 0) {
|
|
return mprAllocBlock(MPR_LOC_PASS(ctx, loc), size);
|
|
}
|
|
|
|
mprAssert(VALID_BLK(ptr));
|
|
bp = GET_HDR(ptr);
|
|
mprAssert(bp);
|
|
mprAssert(VALID_HDR(bp));
|
|
|
|
CHECK_HDR(bp);
|
|
|
|
if (size < bp->size) {
|
|
return ptr;
|
|
}
|
|
|
|
newPtr = mprAllocBlock(MPR_LOC_PASS(ctx, loc), size);
|
|
if (newPtr == 0) {
|
|
bp->flags &= ~ALLOC_FLAGS_FREE;
|
|
free(bp);
|
|
return 0;
|
|
}
|
|
|
|
newbp = GET_HDR(newPtr);
|
|
mprAssert(newbp->size >= size);
|
|
memcpy((char*) newbp + HDR_SIZE, (char*) bp + HDR_SIZE, bp->size);
|
|
mprAssert(newbp->size >= size);
|
|
|
|
/*
|
|
* Fix the next / prev pointers
|
|
*/
|
|
app = bp->app;
|
|
mprLock(app->allocLock);
|
|
newbp->next->prev = newbp;
|
|
newbp->prev->next = newbp;
|
|
|
|
/*
|
|
* Need to fix the parent pointer of all children
|
|
*/
|
|
if ((firstChild = newbp->children) != 0) {
|
|
cp = firstChild;
|
|
do {
|
|
cp->parent = newbp;
|
|
cp = cp->next;
|
|
} while (cp != firstChild);
|
|
}
|
|
|
|
/*
|
|
* May need to set the children pointer of our parent
|
|
*/
|
|
if (newbp->parent->children == bp) {
|
|
newbp->parent->children = newbp;
|
|
}
|
|
|
|
/*
|
|
* Free the original block
|
|
*/
|
|
mprFree(ptr);
|
|
|
|
mprUnlock(app->allocLock);
|
|
|
|
return GET_PTR(newbp);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Allocate a block from a slab
|
|
*/
|
|
|
|
void *mprSlabAllocBlock(MPR_LOC_DEC(ctx, loc), uint size, uint inc)
|
|
{
|
|
|
|
#if NO_SLAB
|
|
return mprAllocBlock(MPR_LOC_PASS(ctx, loc), size);
|
|
#else
|
|
|
|
MprBlk *parent, *bp;
|
|
MprSlabBlock *sb;
|
|
MprApp *app;
|
|
MprSlab *slab;
|
|
int slabIndex;
|
|
|
|
if (ctx == 0) {
|
|
mprAssert(ctx);
|
|
return 0;
|
|
}
|
|
|
|
mprAssert(size > 0);
|
|
mprAssert(VALID_BLK(ctx));
|
|
|
|
parent = GET_HDR(ctx);
|
|
mprAssert(VALID_HDR(parent));
|
|
|
|
CHECK_HDR(parent);
|
|
|
|
size = SLAB_ALIGN(size);
|
|
|
|
app = parent->app;
|
|
mprAssert(app);
|
|
|
|
slabIndex = GET_SLAB(size);
|
|
|
|
if (slabIndex < 0 || slabIndex >= MPR_MAX_SLAB) {
|
|
return mprAllocBlock(MPR_LOC_PASS(ctx, loc), size);
|
|
}
|
|
|
|
/*
|
|
* Dequeue a block from the slab. "sb" will point to the user data
|
|
* portion of the block (i.e. after the MprBlk header). Slabs must be
|
|
* allocated off the "slabs" context to ensure they don't get freed
|
|
* until after all other blocks are freed.
|
|
*/
|
|
mprLock(app->allocLock);
|
|
slab = &app->alloc.slabs[slabIndex];
|
|
if ((sb = slab->next) == 0) {
|
|
if (growSlab(MPR_LOC_ARGS(parent->app->alloc.slabs),
|
|
slab, size, inc) < 0) {
|
|
mprUnlock(app->allocLock);
|
|
return 0;
|
|
}
|
|
sb = slab->next;
|
|
}
|
|
mprAssert(sb);
|
|
|
|
/*
|
|
* Dequeue the block
|
|
*/
|
|
slab->next = sb->next;
|
|
|
|
#if BLD_FEATURE_ALLOC_STATS
|
|
{
|
|
MprSlabStats *slabStats;
|
|
/*
|
|
* Update the slab stats
|
|
*/
|
|
slabStats = &slab->stats;
|
|
slabStats->totalAllocCount++;
|
|
slabStats->freeCount--;
|
|
slabStats->allocCount++;
|
|
if (slabStats->allocCount > slabStats->peakAllocCount) {
|
|
slabStats->peakAllocCount = slabStats->allocCount;
|
|
}
|
|
}
|
|
#endif /* BLD_FEATURE_ALLOC_STATS */
|
|
|
|
bp = GET_HDR(sb);
|
|
|
|
#if BLD_DEBUG && !BREW
|
|
if (bp == stopAlloc) {
|
|
mprBreakpoint(MPR_LOC, "breakOnAddr");
|
|
}
|
|
#endif
|
|
|
|
bp->size = size;
|
|
bp->flags = ALLOC_MAGIC | ALLOC_FLAGS_SLAB_BLOCK;
|
|
bp->destructor = 0;
|
|
|
|
bp->parent = parent;
|
|
|
|
if (parent->children == 0) {
|
|
parent->children = bp;
|
|
bp->next = bp->prev = bp;
|
|
|
|
} else {
|
|
/*
|
|
* Append to the end of the list. Preserve alloc order
|
|
*/
|
|
bp->next = parent->children;
|
|
bp->prev = parent->children->prev;
|
|
parent->children->prev->next = bp;
|
|
parent->children->prev = bp;
|
|
}
|
|
|
|
bp->children = 0;
|
|
|
|
bp->app = app;
|
|
|
|
#if BLD_FEATURE_ALLOC_LEAK_TRACK
|
|
bp->location = loc;
|
|
#endif
|
|
mprUnlock(app->allocLock);
|
|
|
|
return GET_PTR(bp);
|
|
#endif
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Return a block back to its slab
|
|
*/
|
|
|
|
static void slabFree(MprBlk *bp)
|
|
{
|
|
MprSlab *slab;
|
|
MprApp *app;
|
|
void *ptr;
|
|
int slabIndex;
|
|
|
|
mprAssert(VALID_HDR(bp));
|
|
|
|
slabIndex = GET_SLAB(bp->size);
|
|
mprAssert(0 <= slabIndex && slabIndex < MPR_MAX_SLAB);
|
|
|
|
if (0 <= slabIndex && slabIndex < MPR_MAX_SLAB) {
|
|
mprLock(bp->app->allocLock);
|
|
slab = &bp->app->alloc.slabs[slabIndex];
|
|
app = bp->app;
|
|
|
|
#if BLD_DEBUG
|
|
memset(bp, 0xfc, bp->size + HDR_SIZE);
|
|
#endif
|
|
|
|
ptr = GET_PTR(bp);
|
|
((MprSlabBlock*) ptr)->next = slab->next;
|
|
slab->next = ((MprSlabBlock*) ptr);
|
|
|
|
#if BLD_FEATURE_ALLOC_STATS
|
|
{
|
|
MprSlabStats *slabStats;
|
|
slabStats = &slab->stats;
|
|
|
|
slabStats->freeCount++;
|
|
slabStats->allocCount--;
|
|
|
|
if (slabStats->freeCount >= slabStats->peakFreeCount) {
|
|
slabStats->peakFreeCount = slabStats->freeCount;
|
|
}
|
|
}
|
|
#endif
|
|
mprUnlock(app->allocLock);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Grow the slab and return the next free block
|
|
* Must be called locked.
|
|
*/
|
|
|
|
static int growSlab(MPR_LOC_DEC(ctx, loc), MprSlab *slab, uint size, uint inc)
|
|
{
|
|
MprBlk *bp;
|
|
MprSlabBlock *sb;
|
|
int i, chunkSize, len;
|
|
|
|
mprAssert(VALID_BLK(ctx));
|
|
mprAssert(slab);
|
|
mprAssert(size > 0);
|
|
|
|
/*
|
|
* Take the maximum requested by anyone
|
|
*/
|
|
slab->preAllocateIncr = max(slab->preAllocateIncr, inc);
|
|
|
|
/*
|
|
* We allocate an array of blocks each of user "size" bytes.
|
|
*/
|
|
chunkSize = HDR_SIZE + size;
|
|
len = chunkSize * slab->preAllocateIncr;
|
|
bp = mprAllocBlock(MPR_LOC_PASS(ctx, loc), len);
|
|
|
|
#if BLD_DEBUG
|
|
memset(bp, 0xf1, len);
|
|
#endif
|
|
|
|
if (bp == 0) {
|
|
mprAssert(0);
|
|
return MPR_ERR_MEMORY;
|
|
}
|
|
bp->flags |= ALLOC_FLAGS_IS_SLAB;
|
|
|
|
/*
|
|
* We store the slab information in the user data portion
|
|
*/
|
|
sb = (MprSlabBlock*) GET_PTR(bp);
|
|
|
|
|
|
sb = (MprSlabBlock*) ((char*) sb + len - chunkSize);
|
|
for (i = slab->preAllocateIncr - 1; i >= 0; i--) {
|
|
sb->next = slab->next;
|
|
slab->next = sb;
|
|
sb = (MprSlabBlock*) ((char*) sb - chunkSize);
|
|
}
|
|
|
|
#if BLD_FEATURE_ALLOC_STATS
|
|
{
|
|
MprSlabStats *stats;
|
|
stats = &slab->stats;
|
|
stats->freeCount += slab->preAllocateIncr;
|
|
if (stats->freeCount > stats->peakFreeCount) {
|
|
stats->peakFreeCount = stats->freeCount;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Set the pre-allocate amount
|
|
*/
|
|
|
|
int mprSetSlabPreAllocate(MprCtx ctx, int slabIndex, int preAllocateIncr)
|
|
{
|
|
MprApp *app;
|
|
MprSlab *slab;
|
|
|
|
mprAssert(VALID_BLK(ctx));
|
|
mprAssert(0 <= slabIndex && slabIndex < MPR_MAX_SLAB);
|
|
mprAssert(preAllocateIncr > 0);
|
|
|
|
if (0 <= slabIndex && slabIndex < MPR_MAX_SLAB) {
|
|
app = mprGetApp(ctx);
|
|
slab = &app->alloc.slabs[slabIndex];
|
|
slab->preAllocateIncr = preAllocateIncr;
|
|
} else {
|
|
return MPR_ERR_BAD_ARGS;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
void *mprSlabAllocZeroedBlock(MPR_LOC_DEC(ctx, loc), uint size, uint inc)
|
|
{
|
|
void *newBlock;
|
|
|
|
mprAssert(VALID_BLK(ctx));
|
|
mprAssert(size > 0);
|
|
|
|
newBlock = mprSlabAllocBlock(MPR_LOC_PASS(ctx, loc), size, inc);
|
|
if (newBlock) {
|
|
memset(newBlock, 0, size);
|
|
}
|
|
return newBlock;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Internal strdup function. Will use the slab allocator for small strings
|
|
*/
|
|
|
|
char *mprStrdupInternal(MPR_LOC_DEC(ctx, loc), const char *str)
|
|
{
|
|
char *newp;
|
|
int len;
|
|
|
|
mprAssert(VALID_BLK(ctx));
|
|
|
|
if (str == 0) {
|
|
str = "";
|
|
}
|
|
|
|
len = strlen(str) + 1;
|
|
|
|
if (len < MPR_SLAB_STR_MAX) {
|
|
newp = mprSlabAllocBlock(MPR_LOC_PASS(ctx, loc), MPR_SLAB_STR_MAX,
|
|
MPR_SLAB_STR_INC);
|
|
} else {
|
|
newp = mprAllocBlock(MPR_LOC_PASS(ctx, loc), len);
|
|
}
|
|
|
|
if (newp) {
|
|
memcpy(newp, str, len);
|
|
}
|
|
|
|
return newp;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Internal strndup function. Will use the slab allocator for small strings
|
|
*/
|
|
|
|
char *mprStrndupInternal(MPR_LOC_DEC(ctx, loc), const char *str, uint size)
|
|
{
|
|
char *newp;
|
|
uint len;
|
|
|
|
mprAssert(VALID_BLK(ctx));
|
|
|
|
if (str == 0) {
|
|
str = "";
|
|
}
|
|
len = strlen(str) + 1;
|
|
len = min(len, size);
|
|
|
|
if (len < MPR_SLAB_STR_MAX) {
|
|
newp = mprSlabAllocBlock(MPR_LOC_PASS(ctx, loc), MPR_SLAB_STR_MAX,
|
|
MPR_SLAB_STR_INC);
|
|
} else {
|
|
newp = mprAllocBlock(MPR_LOC_PASS(ctx, loc), len);
|
|
}
|
|
|
|
if (newp) {
|
|
memcpy(newp, str, len);
|
|
}
|
|
|
|
return newp;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Internal memcpy function. Will use the slab allocator for small strings
|
|
*/
|
|
|
|
void *mprMemdupInternal(MPR_LOC_DEC(ctx, loc), const void *ptr, uint size)
|
|
{
|
|
char *newp;
|
|
|
|
mprAssert(VALID_BLK(ctx));
|
|
|
|
if (size < MPR_SLAB_STR_MAX) {
|
|
newp = mprSlabAllocBlock(MPR_LOC_PASS(ctx, loc), MPR_SLAB_STR_MAX,
|
|
MPR_SLAB_STR_INC);
|
|
} else {
|
|
newp = mprAllocBlock(MPR_LOC_PASS(ctx, loc), size);
|
|
}
|
|
|
|
if (newp) {
|
|
memcpy(newp, ptr, size);
|
|
}
|
|
|
|
return newp;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Steal a block from one context and insert in another
|
|
*/
|
|
|
|
int mprStealAllocBlock(MPR_LOC_DEC(ctx, loc), const void *ptr)
|
|
{
|
|
MprBlk *bp, *parent;
|
|
|
|
if (ptr == 0) {
|
|
return 0;
|
|
}
|
|
|
|
mprAssert(VALID_BLK(ctx));
|
|
mprAssert(VALID_BLK(ptr));
|
|
|
|
bp = GET_HDR(ptr);
|
|
|
|
#if BLD_DEBUG && !BREW
|
|
if (bp == stopAlloc) {
|
|
mprBreakpoint(MPR_LOC, "breakOnAddr");
|
|
}
|
|
#endif
|
|
|
|
mprAssert(bp);
|
|
mprAssert(VALID_HDR(bp));
|
|
mprAssert(ptr != mprGetAllocParent(ptr));
|
|
|
|
CHECK_HDR(bp);
|
|
|
|
mprAssert(bp->prev);
|
|
mprAssert(bp->prev->next);
|
|
mprAssert(bp->next);
|
|
mprAssert(bp->next->prev);
|
|
|
|
parent = bp->parent;
|
|
mprAssert(VALID_HDR(parent));
|
|
|
|
mprLock(bp->app->allocLock);
|
|
if (parent->children == bp) {
|
|
if (bp->next == bp) {
|
|
parent->children = 0;
|
|
} else {
|
|
parent->children = bp->next;
|
|
}
|
|
}
|
|
|
|
bp->prev->next = bp->next;
|
|
bp->next->prev = bp->prev;
|
|
|
|
parent = GET_HDR(ctx);
|
|
mprAssert(VALID_HDR(parent));
|
|
bp->parent = parent;
|
|
|
|
if (parent->children == 0) {
|
|
parent->children = bp;
|
|
bp->next = bp->prev = bp;
|
|
|
|
} else {
|
|
bp->next = parent->children;
|
|
bp->prev = parent->children->prev;
|
|
parent->children->prev->next = bp;
|
|
parent->children->prev = bp;
|
|
}
|
|
|
|
#if BLD_FEATURE_ALLOC_LEAK_TRACK
|
|
bp->location = loc;
|
|
#endif
|
|
|
|
VALIDATE_BLOCK(GET_PTR(bp));
|
|
|
|
mprUnlock(bp->app->allocLock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
void mprSetRequiredAlloc(MprCtx ptr, bool recurse)
|
|
{
|
|
MprBlk *bp, *firstChild, *cp;
|
|
|
|
bp = GET_HDR(ptr);
|
|
|
|
bp->flags |= ALLOC_FLAGS_REQUIRED;
|
|
|
|
if (recurse && (firstChild = bp->children) != 0) {
|
|
cp = firstChild;
|
|
do {
|
|
mprSetRequiredAlloc(GET_PTR(cp), recurse);
|
|
cp = cp->next;
|
|
} while (cp != firstChild);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Monitor stack usage. Return true if the stack has grown
|
|
*/
|
|
|
|
int mprStackCheck(MprCtx ptr)
|
|
{
|
|
MprApp *app;
|
|
int size;
|
|
|
|
mprAssert(VALID_BLK(ptr));
|
|
|
|
app = mprGetApp(ptr);
|
|
|
|
size = (int) app->stackStart - (int) &app;
|
|
if (size < 0) {
|
|
app->maxStack -= size;
|
|
app->stackStart = (void*) &app;
|
|
size = 0;
|
|
}
|
|
if ((uint) size > app->maxStack) {
|
|
app->maxStack = size;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Return the stack size
|
|
*/
|
|
|
|
int mprStackSize(MprCtx ptr)
|
|
{
|
|
MprApp *app;
|
|
|
|
mprAssert(VALID_BLK(ptr));
|
|
|
|
app = mprGetApp(ptr);
|
|
return app->maxStack;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
static int mprAllocException(MPR_LOC_DEC(ctx, loc), uint size, bool granted)
|
|
{
|
|
MprApp *app;
|
|
MprAlloc *alloc;
|
|
int rc;
|
|
|
|
mprAssert(VALID_BLK(ctx));
|
|
|
|
app = mprGetApp(ctx);
|
|
alloc = &app->alloc;
|
|
|
|
if (alloc->cback == 0) {
|
|
return 0;
|
|
}
|
|
|
|
mprLock(app->allocLock);
|
|
if (alloc->inAllocException == 0) {
|
|
alloc->inAllocException = 1;
|
|
mprUnlock(app->allocLock);
|
|
|
|
rc = (alloc->cback)(app, size, alloc->stats.bytesAllocated, granted);
|
|
|
|
mprLock(app->allocLock);
|
|
app->alloc.inAllocException = 0;
|
|
mprUnlock(app->allocLock);
|
|
|
|
return rc;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
void mprSetAllocLimits(MprApp *app, uint redLine, uint maxMemory)
|
|
{
|
|
app->alloc.stats.redLine = redLine;
|
|
app->alloc.stats.maxMemory = maxMemory;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
MprAllocCback mprSetAllocCallback(MprApp *app, MprAllocCback cback)
|
|
{
|
|
MprAllocCback old;
|
|
|
|
mprAssert(app);
|
|
mprAssert(VALID_BLK(app));
|
|
|
|
old = app->alloc.cback;
|
|
app->alloc.cback = cback;
|
|
return old;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
uint mprGetAllocBlockSize(MprCtx ptr)
|
|
{
|
|
MprBlk *bp;
|
|
|
|
mprAssert(VALID_BLK(ptr));
|
|
|
|
if (ptr == 0) {
|
|
return 0;
|
|
}
|
|
|
|
bp = GET_HDR(ptr);
|
|
mprAssert(VALID_HDR(bp));
|
|
|
|
CHECK_HDR(bp);
|
|
|
|
return bp->size;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Return the total block count used by a block including all children
|
|
*/
|
|
|
|
uint mprGetAllocBlockCount(MprCtx ptr)
|
|
{
|
|
MprBlk *bp, *firstChild, *cp;
|
|
uint count;
|
|
|
|
mprAssert(VALID_BLK(ptr));
|
|
|
|
if (ptr == 0) {
|
|
return 0;
|
|
}
|
|
|
|
bp = GET_HDR(ptr);
|
|
mprAssert(VALID_HDR(bp));
|
|
|
|
/*
|
|
* Add one for itself
|
|
*/
|
|
count = 1;
|
|
if ((firstChild = bp->children) != 0) {
|
|
cp = firstChild;
|
|
do {
|
|
count += mprGetAllocBlockCount(GET_PTR(cp));
|
|
cp = cp->next;
|
|
} while (cp != firstChild);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Return the total of all memory allocated including slabs
|
|
*/
|
|
|
|
uint mprGetAllocBlockMemory(MprCtx ptr)
|
|
{
|
|
MprBlk *bp, *firstChild, *cp;
|
|
uint count;
|
|
|
|
mprAssert(VALID_BLK(ptr));
|
|
|
|
if (ptr == 0) {
|
|
return 0;
|
|
}
|
|
|
|
bp = GET_HDR(ptr);
|
|
mprAssert(VALID_HDR(bp));
|
|
|
|
count = bp->size + HDR_SIZE;
|
|
if ((firstChild = bp->children) != 0) {
|
|
cp = firstChild;
|
|
do {
|
|
count += mprGetAllocBlockMemory(GET_PTR(cp));
|
|
cp = cp->next;
|
|
} while (cp != firstChild);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
#if BLD_FEATURE_ALLOC_LEAK_TRACK
|
|
|
|
const char *mprGetAllocLocation(MprCtx ptr)
|
|
{
|
|
MprBlk *bp;
|
|
|
|
if (ptr == 0) {
|
|
return 0;
|
|
}
|
|
mprAssert(VALID_BLK(ptr));
|
|
|
|
bp = GET_HDR(ptr);
|
|
mprAssert(VALID_HDR(bp));
|
|
return bp->location;
|
|
}
|
|
|
|
#endif
|
|
/******************************************************************************/
|
|
|
|
void *mprGetAllocParent(MprCtx ptr)
|
|
{
|
|
MprBlk *bp;
|
|
|
|
mprAssert(VALID_BLK(ptr));
|
|
|
|
if (ptr == 0) {
|
|
return 0;
|
|
}
|
|
|
|
bp = GET_HDR(ptr);
|
|
mprAssert(VALID_HDR(bp));
|
|
|
|
CHECK_HDR(bp);
|
|
|
|
return GET_PTR(bp->parent);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
MprAllocStats *mprGetAllocStats(MprApp *app)
|
|
{
|
|
mprAssert(VALID_BLK(app));
|
|
|
|
return &app->alloc.stats;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
#if BLD_FEATURE_ALLOC_STATS
|
|
|
|
MprSlabStats *mprGetSlabAllocStats(MprApp *app, int slabIndex)
|
|
{
|
|
MprSlab *slab;
|
|
|
|
mprAssert(VALID_BLK(app));
|
|
|
|
if (0 <= slabIndex && slabIndex < MPR_MAX_SLAB) {
|
|
slab = &app->alloc.slabs[slabIndex];
|
|
return &slab->stats;
|
|
}
|
|
|
|
mprAssert(0 <= slabIndex && slabIndex < MPR_MAX_SLAB);
|
|
return 0;
|
|
}
|
|
|
|
#endif /* BLD_FEATURE_ALLOC_STATS */
|
|
/******************************************************************************/
|
|
#if BLD_DEBUG
|
|
|
|
int mprPrintAllocBlocks(MprCtx ptr, int indent)
|
|
{
|
|
MprBlk *bp, *firstChild, *cp;
|
|
const char *location;
|
|
int subTotal, size, indentSpaces, code;
|
|
|
|
subTotal = 0;
|
|
|
|
bp = GET_HDR(ptr);
|
|
|
|
if (! (bp->flags & ALLOC_FLAGS_REQUIRED)) {
|
|
size = bp->size + HDR_SIZE;
|
|
|
|
/*
|
|
* Take one level off because we don't trace app
|
|
*/
|
|
indentSpaces = indent;
|
|
|
|
if (bp->flags & ALLOC_FLAGS_REQUIRED) {
|
|
code = 'R';
|
|
} else if (bp->flags & ALLOC_FLAGS_IS_SLAB) {
|
|
code = 'S';
|
|
} else {
|
|
code = ' ';
|
|
}
|
|
|
|
#if BLD_FEATURE_ALLOC_LEAK_TRACK
|
|
location = bp->location;
|
|
#else
|
|
location = "";
|
|
#endif
|
|
mprLog(bp->app, 0,
|
|
"%c %.*s %-16s %.*s size %5d has %3d deps, total %6d", code,
|
|
indentSpaces, " ",
|
|
mprGetBaseName(location),
|
|
8 - indent, " ",
|
|
size,
|
|
mprGetAllocBlockCount(GET_PTR(bp)),
|
|
mprGetAllocBlockMemory(GET_PTR(bp))
|
|
/* (uint) bp */
|
|
);
|
|
|
|
subTotal += size;
|
|
}
|
|
|
|
if ((firstChild = bp->children) != 0) {
|
|
cp = firstChild;
|
|
do {
|
|
subTotal += mprPrintAllocBlocks(GET_PTR(cp), indent + 2);
|
|
cp = cp->next;
|
|
} while (cp != firstChild);
|
|
}
|
|
|
|
return subTotal;
|
|
}
|
|
|
|
#endif
|
|
/******************************************************************************/
|
|
#if BLD_FEATURE_ALLOC_STATS
|
|
/*
|
|
* Print a memory allocation report that includes a list of allocated blocks
|
|
* and a statistics summary
|
|
*/
|
|
|
|
void mprPrintAllocReport(MprApp *app, bool printBlocks, const char *msg)
|
|
{
|
|
MprSlabStats *stats;
|
|
uint total;
|
|
int i, size;
|
|
|
|
mprAssert(VALID_BLK(app));
|
|
|
|
if (msg) {
|
|
mprLog(app, 0, " ");
|
|
mprLog(app, 0, "%s", msg);
|
|
}
|
|
|
|
#if BLD_DEBUG
|
|
/*
|
|
* Do block stats
|
|
*/
|
|
if (printBlocks) {
|
|
int sum;
|
|
mprLog(app, 0, " ");
|
|
sum = mprPrintAllocBlocks(app, 0);
|
|
if (sum) {
|
|
mprLog(app, 0, " Sum of blocks %d", sum);
|
|
} else {
|
|
mprLog(app, 0, " None");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Do Slab stats
|
|
*/
|
|
mprLog(app, 0, " ");
|
|
mprLog(app, 0, "MPR Slab Memory Stats");
|
|
mprLog(app, 0, " ");
|
|
|
|
mprLog(app, 0,
|
|
" Index Size Total Allocated Free PeakAlloc PeakFree TotalAlloc");
|
|
|
|
total = 0;
|
|
for (i = 0; i < MPR_MAX_SLAB; i++) {
|
|
stats = &app->alloc.slabs[i].stats;
|
|
size = 1 << (i + 5);
|
|
if (stats->totalAllocCount > 0) {
|
|
mprLog(app, 0, " %2d %6d %8d %9d %6d %9d %8d %10d",
|
|
i, size, size * (stats->allocCount + stats->freeCount),
|
|
stats->allocCount, stats->freeCount,
|
|
stats->peakAllocCount, stats->peakFreeCount,
|
|
stats->totalAllocCount);
|
|
total += size * (stats->allocCount + stats->freeCount);
|
|
}
|
|
}
|
|
mprLog(app, 0, " ");
|
|
mprLog(app, 0, "MPR Total Allocated Slab RAM: %10d", total);
|
|
mprLog(app, 0, "MPR Total Allocated RAM: %10d",
|
|
mprGetAllocatedMemory(app));
|
|
mprLog(app, 0, "MPR Peak Allocated RAM: %10d",
|
|
mprGetPeakAllocatedMemory(app));
|
|
mprLog(app, 0, " ");
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Return the total memory allocated.
|
|
*/
|
|
|
|
uint mprGetAllocatedMemory(MprCtx ctx)
|
|
{
|
|
MprApp *app;
|
|
|
|
app = mprGetApp(ctx);
|
|
|
|
return app->alloc.stats.bytesAllocated;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Return the peak memory allocated.
|
|
*/
|
|
|
|
uint mprGetPeakAllocatedMemory(MprCtx ctx)
|
|
{
|
|
MprApp *app;
|
|
|
|
app = mprGetApp(ctx);
|
|
|
|
return app->alloc.stats.peakAllocated;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Return memory in the MPR slab. This excludes the EJS slabs
|
|
*/
|
|
|
|
uint mprGetAllocatedSlabMemory(MprCtx ctx)
|
|
{
|
|
MprApp *app;
|
|
MprSlabStats *stats;
|
|
uint total;
|
|
int i, size;
|
|
|
|
app = mprGetApp(ctx);
|
|
|
|
total = 0;
|
|
for (i = 0; i < MPR_MAX_SLAB; i++) {
|
|
stats = &app->alloc.slabs[i].stats;
|
|
size = 1 << (i + 5);
|
|
if (stats->totalAllocCount > 0) {
|
|
total += size * (stats->allocCount + stats->freeCount);
|
|
}
|
|
}
|
|
return total;
|
|
}
|
|
|
|
#endif /* BLD_FEATURE_ALLOC_STATS */
|
|
/******************************************************************************/
|
|
|
|
MprDestructor mprSetDestructor(MprCtx ptr, MprDestructor destructor)
|
|
{
|
|
MprDestructor old;
|
|
MprBlk *bp;
|
|
|
|
mprAssert(VALID_BLK(ptr));
|
|
|
|
if (ptr == 0) {
|
|
return 0;
|
|
}
|
|
|
|
bp = GET_HDR(ptr);
|
|
|
|
mprAssert(bp);
|
|
mprAssert(VALID_HDR(bp));
|
|
mprAssert(ptr != mprGetAllocParent(ptr));
|
|
|
|
CHECK_HDR(bp);
|
|
|
|
old = bp->destructor;
|
|
bp->destructor = destructor;
|
|
|
|
return old;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
int mprIsAllocBlockValid(MprCtx ptr)
|
|
{
|
|
MprBlk *bp;
|
|
|
|
bp = GET_HDR(ptr);
|
|
return (bp && VALID_HDR(bp));
|
|
}
|
|
|
|
/******************************************************************************/
|
|
#if VALIDATE_ALLOC
|
|
/*
|
|
* Exhaustive validation of the block and its children. Does not go recursive
|
|
* as it would be too slow.
|
|
*/
|
|
|
|
int mprValidateBlock(MprCtx ptr)
|
|
{
|
|
MprBlk *bp, *parent, *cp, *firstChild;
|
|
int count;
|
|
|
|
mprAssert(ptr);
|
|
mprAssert(VALID_BLK(ptr));
|
|
|
|
bp = GET_HDR(ptr);
|
|
|
|
mprAssert(bp);
|
|
mprAssert(VALID_HDR(bp));
|
|
mprAssert(VALID_HDR(bp->parent));
|
|
|
|
if (ptr != bp->app) {
|
|
mprAssert(bp != bp->parent);
|
|
}
|
|
mprAssert(! (bp->flags & ALLOC_FLAGS_FREE));
|
|
mprAssert(! (bp->flags & ALLOC_FLAGS_FREEING));
|
|
|
|
/*
|
|
*
|
|
*/
|
|
count = 0;
|
|
parent = bp->parent;
|
|
|
|
if ((firstChild = bp->children) != 0) {
|
|
cp = firstChild;
|
|
mprAssert((int) cp != 0xfeefee);
|
|
do {
|
|
mprAssert(bp->next->prev == bp);
|
|
mprAssert(bp->prev->next == bp);
|
|
mprAssert(bp->prev->parent == parent);
|
|
mprAssert(bp->next->parent == parent);
|
|
|
|
count++;
|
|
cp = cp->next;
|
|
|
|
if (bp->next == bp) {
|
|
mprAssert(bp->prev == bp);
|
|
if (ptr != bp->app) {
|
|
mprAssert(parent->children == bp);
|
|
}
|
|
}
|
|
if (bp->prev == bp) {
|
|
mprAssert(bp->next == bp);
|
|
if (ptr != bp->app) {
|
|
mprAssert(parent->children == bp);
|
|
}
|
|
}
|
|
} while (cp != firstChild);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
/******************************************************************************/
|
|
/*
|
|
* Validate a block and all children
|
|
*/
|
|
|
|
int mprValidateAllocTree(MprCtx ptr)
|
|
{
|
|
#if VALIDATE_ALLOC
|
|
MprBlk *bp, *cp, *firstChild;
|
|
|
|
mprAssert(ptr);
|
|
mprAssert(VALID_BLK(ptr));
|
|
|
|
bp = GET_HDR(ptr);
|
|
|
|
mprValidateBlock(GET_PTR(bp));
|
|
|
|
if ((firstChild = bp->children) != 0) {
|
|
cp = firstChild;
|
|
do {
|
|
mprValidateAllocTree(GET_PTR(cp));
|
|
cp = cp->next;
|
|
} while (cp != firstChild);
|
|
}
|
|
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
#if UNUSED && FUTURE
|
|
/*
|
|
* Exhaustive validation of the block and its children. Does not go recursive
|
|
* as it would be too slow.
|
|
*/
|
|
|
|
int mprValidateSlabs(MprApp *app)
|
|
{
|
|
MprSlab *slab;
|
|
MprSlabStats *slabStats;
|
|
MprSlabBlock *sp;
|
|
int count, i;
|
|
|
|
for (i = 0; i < MPR_MAX_SLAB; i++) {
|
|
slab = &app->alloc.slabs[i];
|
|
slabStats = &slab->stats;
|
|
|
|
count = 0;
|
|
for (sp = slab->next; sp; sp = sp->next) {
|
|
count++;
|
|
}
|
|
mprAssert(count == (int) slabStats->freeCount);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
/******************************************************************************/
|
|
|
|
void mprAllocAbort()
|
|
{
|
|
#if BREW
|
|
printf("Bad block header");
|
|
#else
|
|
exit(255);
|
|
#endif
|
|
}
|
|
|
|
/******************************************************************************/
|
|
#undef mprGetApp
|
|
/*
|
|
* Get the root parent from any block (which is the MprApp structure)
|
|
*/
|
|
|
|
MprApp *mprGetApp(MprCtx ptr)
|
|
{
|
|
MprBlk *bp;
|
|
|
|
mprAssert(ptr);
|
|
|
|
bp = GET_HDR(ptr);
|
|
mprAssert(VALID_HDR(bp));
|
|
|
|
CHECK_HDR(bp);
|
|
|
|
mprAssert(bp->app->magic == APP_MAGIC);
|
|
|
|
return bp->app;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
int mprGetAllocErrors(MprCtx ctx)
|
|
{
|
|
MprApp *app;
|
|
|
|
app = mprGetApp(ctx);
|
|
return app->alloc.stats.errors;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
void mprClearAllocErrors(MprCtx ctx)
|
|
{
|
|
MprApp *app;
|
|
|
|
app = mprGetApp(ctx);
|
|
app->alloc.stats.errors = 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Local variables:
|
|
* tab-width: 4
|
|
* c-basic-offset: 4
|
|
* End:
|
|
* vim:tw=78
|
|
* vim600: sw=4 ts=4 fdm=marker
|
|
* vim<600: sw=4 ts=4
|
|
*/
|