/** * @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 */