diff options
Diffstat (limited to 'source4/lib/appweb/ejs-2.0/mpr/mprAlloc.c')
-rw-r--r-- | source4/lib/appweb/ejs-2.0/mpr/mprAlloc.c | 1775 |
1 files changed, 1775 insertions, 0 deletions
diff --git a/source4/lib/appweb/ejs-2.0/mpr/mprAlloc.c b/source4/lib/appweb/ejs-2.0/mpr/mprAlloc.c new file mode 100644 index 0000000000..6fcaa63a6c --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/mprAlloc.c @@ -0,0 +1,1775 @@ +/** + * @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 + */ |