3 * @brief Memory Allocation
10 * Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved.
12 * This software is distributed under commercial and open source licenses.
13 * You may use the GPL open source license described below or you may acquire
14 * a commercial license from Mbedthis Software. You agree to be fully bound
15 * by the terms of either license. Consult the LICENSE.TXT distributed with
16 * this software for full details.
18 * This software is open source; you can redistribute it and/or modify it
19 * under the terms of the GNU General Public License as published by the
20 * Free Software Foundation; either version 2 of the License, or (at your
21 * option) any later version. See the GNU General Public License for more
22 * details at: http://www.mbedthis.com/downloads/gplLicense.html
24 * This program is distributed WITHOUT ANY WARRANTY; without even the
25 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
27 * This GPL license does NOT permit incorporating this software into
28 * proprietary programs. If you are unable to comply with the GPL, you must
29 * acquire a commercial license to use this software. Commercial licenses
30 * for this software and support services are available from Mbedthis
31 * Software at http://www.mbedthis.com
36 /********************************* Includes ***********************************/
38 #define UNSAFE_FUNCTIONS_OK 1
42 /******************************* Local Defines ********************************/
44 * Set to 1 to disable slab based allocations
49 * Validation mode is quite slow
51 #define VALIDATE_ALLOC 0
53 #define VALIDATE_BLOCK(ptr) mprValidateBlock(ptr)
55 #define VALIDATE_BLOCK(ptr)
59 * Align on 4 bytes if squeeze, Otherwize on 16 bytes.
61 #define HDR_SIZE MPR_BLK_HDR_SIZE
63 #define APP_MAGIC 0xa571cb80
64 #define ALLOC_MAGIC 0xe814ecc0
67 * This must be at least one word to ensure that the smallest allocation is
68 * 4 bytes. Slab allocations need at least one word to store their next ptr.
70 #define ALLOC_ALIGN(x) (((x)+3)&~3)
71 #define GET_HDR(ptr) ((MprBlk*) (((char*) (ptr)) - HDR_SIZE))
72 #define GET_PTR(bp) ((void*) (((char*) (bp)) + HDR_SIZE))
73 #define VALID_HDR(bp) (((bp)->flags & ~0x3F) == ALLOC_MAGIC)
74 #define VALID_BLK(ptr) (VALID_HDR(GET_HDR(ptr)))
77 * In production releases, mprAssert will compile out (not be included)
78 * but CHECK_HDR will remain even in production builds.
80 #define CHECK_HDR(bp) \
81 if (1) { if (! VALID_HDR(bp)) { mprAllocAbort(); } } else
84 * Chunk the slabs into 32 byte increments.
85 * This allows for allocations up to 512 bytes via slabs and maximizes
86 * sharing of slab allocations.
106 #define SLAB_ALIGN(size) ((size + 31) & ~31)
107 #define GET_SLAB(size) (size >> 6)
112 #define ALLOC_FLAGS_FREE 0x1 /* Block is free */
113 #define ALLOC_FLAGS_FREEING 0x2 /* Block is being freed */
114 #define ALLOC_FLAGS_SLAB_BLOCK 0x4 /* Block was allocated from slab */
115 #define ALLOC_FLAGS_REQUIRED 0x8 /* Block is required by alloc */
116 #define ALLOC_FLAGS_KEEP 0x10 /* Keep block - don't mprFree */
117 #define ALLOC_FLAGS_DONT_OS_FREE 0x20 /* Don't return mem to O/S */
118 #define ALLOC_FLAGS_IS_SLAB 0x40 /* Block is a slab */
120 #if BLD_DEBUG && !BREW
122 * Set this address to break when this address is allocated or freed. This is
123 * a block address (not a user ptr).
125 static MprBlk *stopAlloc;
129 static MprCtx rootCtx; /* Root context if none supplied */
132 /***************************** Forward Declarations ***************************/
134 static int mprAllocException(MPR_LOC_DEC(ptr, loc), uint size, bool granted);
135 static void slabFree(MprBlk *bp);
136 static int growSlab(MPR_LOC_DEC(ctx, loc), MprSlab *slab, uint size, uint inc);
138 /******************************************************************************/
140 * Put first in file so it is easy to locate in a debugger
143 void mprBreakpoint(const char *loc, const char *msg)
147 /******************************************************************************/
148 #if (WIN || BREW_SIMULATOR) && BLD_DEBUG
150 int crtReportHook(int type, char *msg, int *retval)
158 /******************************************************************************/
160 * Initialize the memory subsystem
163 MprApp *mprAllocInit(MprAllocCback cback)
165 MprAllocStats *stats;
171 bp = malloc(sizeof(MprApp) + HDR_SIZE);
175 (*cback)(0, sizeof(MprApp), 0, 0);
179 memset(bp, 0, sizeof(MprApp) + HDR_SIZE);
182 bp->size = sizeof(MprApp);
183 bp->flags = ALLOC_MAGIC;
184 bp->next = bp->prev = bp;
186 #if BLD_FEATURE_ALLOC_LEAK_TRACK
187 bp->location = MPR_LOC;
190 app = (MprApp*) GET_PTR(bp);
191 app->magic = APP_MAGIC;
193 app->alloc.cback = cback;
194 app->stackStart = (void*) &app;
198 app->alloc.slabs = mprAllocZeroedBlock(MPR_LOC_PASS(app, MPR_LOC),
199 sizeof(MprSlab) * MPR_MAX_SLAB);
200 if (app->alloc.slabs == 0) {
206 * The slab control structures must not be freed. Set keep to safeguard
209 sp = GET_HDR(app->alloc.slabs);
210 sp->flags |= ALLOC_FLAGS_KEEP;
212 for (i = 0; i < MPR_MAX_SLAB; i++) {
214 * This is overriden by requestors calling slabAlloc
216 slab = &app->alloc.slabs[i];
217 slab->preAllocateIncr = MPR_SLAB_DEFAULT_INC;
221 * Keep aggregated stats even in production code
223 stats = &app->alloc.stats;
224 stats->bytesAllocated += sizeof(MprApp);
225 if (stats->bytesAllocated > stats->peakAllocated) {
226 stats->peakAllocated = stats->bytesAllocated;
233 #if (WIN || BREW_SIMULATOR) && BLD_DEBUG
234 _CrtSetReportHook(crtReportHook);
239 /******************************************************************************/
241 * Terminate the alloc module
244 void mprAllocTerm(MprApp *app)
247 MprBlk *appBlk, *slabBlk;
250 * Must do a carefully ordered cleanup. Need to free all children blocks
251 * before freeing the slab memory. Save a local pointer to the slabs.
253 slabs = app->alloc.slabs;
256 * Free the app and all children. Set DONT_OS_FREE to prevent free() being
257 * called on app itself. We need that so we can free the slabs below.
259 appBlk = GET_HDR(app);
260 appBlk->flags |= ALLOC_FLAGS_DONT_OS_FREE;
264 * Slabs are initially marked don't free. We must preserve them while all
265 * other blocks are freed. Then we clear the don't free flag and free.
266 * Now we don't have an app structure which is used by mprFree. We must
269 slabBlk = GET_HDR(slabs);
270 slabBlk->flags &= ~ALLOC_FLAGS_KEEP;
274 * Now we can finally free the memory for the app structure
279 /******************************************************************************/
284 void *mprAllocBlock(MPR_LOC_DEC(ctx, loc), uint size)
286 MprAllocStats *stats;
305 mprAssert(VALID_BLK(ctx));
306 parent = GET_HDR(ctx);
307 mprAssert(VALID_HDR(parent));
311 size = ALLOC_ALIGN(size);
315 stats = &app->alloc.stats;
317 mprLock(app->allocLock);
319 stats->bytesAllocated += size + HDR_SIZE;
320 if (stats->bytesAllocated > stats->peakAllocated) {
321 stats->peakAllocated = stats->bytesAllocated;
325 * Prevent allocation if over the maximum
327 if (stats->maxMemory && stats->bytesAllocated > stats->maxMemory) {
328 stats->bytesAllocated -= (size + HDR_SIZE);
329 mprUnlock(app->allocLock);
330 if (mprAllocException(MPR_LOC_PASS(ctx, loc), size, 0) < 0) {
333 mprLock(app->allocLock);
336 if ((bp = malloc(size + HDR_SIZE)) == 0) {
339 mprUnlock(app->allocLock);
340 mprAllocException(MPR_LOC_PASS(ctx, loc), size, 0);
345 memset(bp, 0xf7, size + HDR_SIZE);
348 #if BLD_DEBUG && !BREW
349 if (bp == stopAlloc) {
350 mprBreakpoint(MPR_LOC, "breakOnAddr");
355 * Warn if allocation puts us over the red line
357 if (stats->redLine && stats->bytesAllocated > stats->redLine) {
358 mprUnlock(app->allocLock);
359 if (mprAllocException(MPR_LOC_PASS(ctx, loc), size, 1) < 0) {
362 mprLock(app->allocLock);
366 bp->flags = ALLOC_MAGIC;
371 if (parent->children == 0) {
372 parent->children = bp;
373 bp->next = bp->prev = bp;
377 * Append to the end of the list. Preserve alloc order
379 bp->next = parent->children;
380 bp->prev = parent->children->prev;
381 parent->children->prev->next = bp;
382 parent->children->prev = bp;
387 #if BLD_FEATURE_ALLOC_LEAK_TRACK
391 bp->app = parent->app;
393 VALIDATE_BLOCK(GET_PTR(bp));
398 * Monitor stack usage
400 diff = (int) bp->app->stackStart - (int) &stats;
402 app->maxStack -= diff;
403 app->stackStart = (void*) &stats;
407 if ((uint) diff > app->maxStack) {
408 app->maxStack = diff;
410 mprUnlock(app->allocLock);
415 /******************************************************************************/
417 * Allocate and zero a block
420 void *mprAllocZeroedBlock(MPR_LOC_DEC(ctx, loc), uint size)
427 mprAssert(VALID_BLK(ctx));
429 newBlock = mprAllocBlock(MPR_LOC_PASS(ctx, loc), size);
431 memset(newBlock, 0, size);
436 /******************************************************************************/
438 * Free a block of memory. Free all children recursively.
441 int mprFree(void *ptr)
443 MprAllocStats *stats;
444 MprBlk *bp, *parent, *cp, *firstChild, *prev;
451 mprAssert(VALID_BLK(ptr));
456 #if BLD_DEBUG && !BREW
457 if (bp == stopAlloc) {
458 mprBreakpoint(MPR_LOC, "breakOnAddr");
463 mprAssert(VALID_HDR(bp));
468 * Test if already freed
470 mprAssert(! (bp->flags & ALLOC_FLAGS_FREE));
471 if (bp->flags & ALLOC_FLAGS_FREE) {
476 * Return if recursive freeing or this is a permanent block
479 mprLock(app->allocLock);
480 if (bp->flags & (ALLOC_FLAGS_FREEING | ALLOC_FLAGS_KEEP)) {
481 mprUnlock(app->allocLock);
484 bp->flags |= ALLOC_FLAGS_FREEING;
488 * Call any destructors
490 if (bp->destructor) {
491 mprUnlock(app->allocLock);
492 if ((bp->destructor)(ptr) < 0) {
495 mprLock(app->allocLock);
500 * Free the children. Free in reverse order so firstChild is preserved
501 * during the list scan as an end of list marker.
503 if ((firstChild = bp->children) != 0) {
504 cp = firstChild->prev;
505 while (cp != firstChild) {
507 mprAssert(VALID_HDR(cp));
508 VALIDATE_BLOCK(GET_PTR(cp));
513 * FUTURE - OPT. Make this inline
515 mprFree(GET_PTR(cp));
520 mprFree(GET_PTR(firstChild));
530 mprAssert(VALID_HDR(parent));
533 * Unlink from the parent
535 if (parent->children == bp) {
536 if (bp->next == bp) {
537 parent->children = 0;
539 parent->children = bp->next;
544 * Remove from the sibling chain
546 bp->prev->next = bp->next;
547 bp->next->prev = bp->prev;
549 bp->flags |= ALLOC_FLAGS_FREE;
552 * Release the memory. If from a slab, return to the slab. Otherwise,
555 if (bp->flags & ALLOC_FLAGS_SLAB_BLOCK) {
564 stats = &bp->app->alloc.stats;
565 stats->bytesAllocated -= (bp->size + HDR_SIZE);
566 mprAssert(stats->bytesAllocated >= 0);
569 mprAssert(stats->allocCount >= 0);
571 #if BLD_DEBUG && !BREW
572 if (bp == stopAlloc) {
573 mprBreakpoint(MPR_LOC, "breakOnAddr");
580 if (! (bp->flags & ALLOC_FLAGS_DONT_OS_FREE)) {
586 mprUnlock(app->allocLock);
592 /******************************************************************************/
597 void *mprReallocBlock(MPR_LOC_DEC(ctx, loc), void *ptr, uint size)
599 MprBlk *bp, *newbp, *firstChild, *cp;
603 mprAssert(VALID_BLK(ctx));
607 return mprAllocBlock(MPR_LOC_PASS(ctx, loc), size);
610 mprAssert(VALID_BLK(ptr));
613 mprAssert(VALID_HDR(bp));
617 if (size < bp->size) {
621 newPtr = mprAllocBlock(MPR_LOC_PASS(ctx, loc), size);
623 bp->flags &= ~ALLOC_FLAGS_FREE;
628 newbp = GET_HDR(newPtr);
629 mprAssert(newbp->size >= size);
630 memcpy((char*) newbp + HDR_SIZE, (char*) bp + HDR_SIZE, bp->size);
631 mprAssert(newbp->size >= size);
634 * Fix the next / prev pointers
637 mprLock(app->allocLock);
638 newbp->next->prev = newbp;
639 newbp->prev->next = newbp;
642 * Need to fix the parent pointer of all children
644 if ((firstChild = newbp->children) != 0) {
649 } while (cp != firstChild);
653 * May need to set the children pointer of our parent
655 if (newbp->parent->children == bp) {
656 newbp->parent->children = newbp;
660 * Free the original block
664 mprUnlock(app->allocLock);
666 return GET_PTR(newbp);
669 /******************************************************************************/
671 * Allocate a block from a slab
674 void *mprSlabAllocBlock(MPR_LOC_DEC(ctx, loc), uint size, uint inc)
678 return mprAllocBlock(MPR_LOC_PASS(ctx, loc), size);
693 mprAssert(VALID_BLK(ctx));
695 parent = GET_HDR(ctx);
696 mprAssert(VALID_HDR(parent));
700 size = SLAB_ALIGN(size);
705 slabIndex = GET_SLAB(size);
707 if (slabIndex < 0 || slabIndex >= MPR_MAX_SLAB) {
708 return mprAllocBlock(MPR_LOC_PASS(ctx, loc), size);
712 * Dequeue a block from the slab. "sb" will point to the user data
713 * portion of the block (i.e. after the MprBlk header). Slabs must be
714 * allocated off the "slabs" context to ensure they don't get freed
715 * until after all other blocks are freed.
717 mprLock(app->allocLock);
718 slab = &app->alloc.slabs[slabIndex];
719 if ((sb = slab->next) == 0) {
720 if (growSlab(MPR_LOC_ARGS(parent->app->alloc.slabs),
721 slab, size, inc) < 0) {
722 mprUnlock(app->allocLock);
732 slab->next = sb->next;
734 #if BLD_FEATURE_ALLOC_STATS
736 MprSlabStats *slabStats;
738 * Update the slab stats
740 slabStats = &slab->stats;
741 slabStats->totalAllocCount++;
742 slabStats->freeCount--;
743 slabStats->allocCount++;
744 if (slabStats->allocCount > slabStats->peakAllocCount) {
745 slabStats->peakAllocCount = slabStats->allocCount;
748 #endif /* BLD_FEATURE_ALLOC_STATS */
752 #if BLD_DEBUG && !BREW
753 if (bp == stopAlloc) {
754 mprBreakpoint(MPR_LOC, "breakOnAddr");
759 bp->flags = ALLOC_MAGIC | ALLOC_FLAGS_SLAB_BLOCK;
764 if (parent->children == 0) {
765 parent->children = bp;
766 bp->next = bp->prev = bp;
770 * Append to the end of the list. Preserve alloc order
772 bp->next = parent->children;
773 bp->prev = parent->children->prev;
774 parent->children->prev->next = bp;
775 parent->children->prev = bp;
782 #if BLD_FEATURE_ALLOC_LEAK_TRACK
785 mprUnlock(app->allocLock);
791 /******************************************************************************/
793 * Return a block back to its slab
796 static void slabFree(MprBlk *bp)
803 mprAssert(VALID_HDR(bp));
805 slabIndex = GET_SLAB(bp->size);
806 mprAssert(0 <= slabIndex && slabIndex < MPR_MAX_SLAB);
808 if (0 <= slabIndex && slabIndex < MPR_MAX_SLAB) {
809 mprLock(bp->app->allocLock);
810 slab = &bp->app->alloc.slabs[slabIndex];
814 memset(bp, 0xfc, bp->size + HDR_SIZE);
818 ((MprSlabBlock*) ptr)->next = slab->next;
819 slab->next = ((MprSlabBlock*) ptr);
821 #if BLD_FEATURE_ALLOC_STATS
823 MprSlabStats *slabStats;
824 slabStats = &slab->stats;
826 slabStats->freeCount++;
827 slabStats->allocCount--;
829 if (slabStats->freeCount >= slabStats->peakFreeCount) {
830 slabStats->peakFreeCount = slabStats->freeCount;
834 mprUnlock(app->allocLock);
838 /******************************************************************************/
840 * Grow the slab and return the next free block
841 * Must be called locked.
844 static int growSlab(MPR_LOC_DEC(ctx, loc), MprSlab *slab, uint size, uint inc)
848 int i, chunkSize, len;
850 mprAssert(VALID_BLK(ctx));
855 * Take the maximum requested by anyone
857 slab->preAllocateIncr = max(slab->preAllocateIncr, inc);
860 * We allocate an array of blocks each of user "size" bytes.
862 chunkSize = HDR_SIZE + size;
863 len = chunkSize * slab->preAllocateIncr;
864 bp = mprAllocBlock(MPR_LOC_PASS(ctx, loc), len);
867 memset(bp, 0xf1, len);
872 return MPR_ERR_MEMORY;
874 bp->flags |= ALLOC_FLAGS_IS_SLAB;
877 * We store the slab information in the user data portion
879 sb = (MprSlabBlock*) GET_PTR(bp);
882 sb = (MprSlabBlock*) ((char*) sb + len - chunkSize);
883 for (i = slab->preAllocateIncr - 1; i >= 0; i--) {
884 sb->next = slab->next;
886 sb = (MprSlabBlock*) ((char*) sb - chunkSize);
889 #if BLD_FEATURE_ALLOC_STATS
892 stats = &slab->stats;
893 stats->freeCount += slab->preAllocateIncr;
894 if (stats->freeCount > stats->peakFreeCount) {
895 stats->peakFreeCount = stats->freeCount;
903 /******************************************************************************/
905 * Set the pre-allocate amount
908 int mprSetSlabPreAllocate(MprCtx ctx, int slabIndex, int preAllocateIncr)
913 mprAssert(VALID_BLK(ctx));
914 mprAssert(0 <= slabIndex && slabIndex < MPR_MAX_SLAB);
915 mprAssert(preAllocateIncr > 0);
917 if (0 <= slabIndex && slabIndex < MPR_MAX_SLAB) {
918 app = mprGetApp(ctx);
919 slab = &app->alloc.slabs[slabIndex];
920 slab->preAllocateIncr = preAllocateIncr;
922 return MPR_ERR_BAD_ARGS;
927 /******************************************************************************/
929 void *mprSlabAllocZeroedBlock(MPR_LOC_DEC(ctx, loc), uint size, uint inc)
933 mprAssert(VALID_BLK(ctx));
936 newBlock = mprSlabAllocBlock(MPR_LOC_PASS(ctx, loc), size, inc);
938 memset(newBlock, 0, size);
943 /******************************************************************************/
945 * Internal strdup function. Will use the slab allocator for small strings
948 char *mprStrdupInternal(MPR_LOC_DEC(ctx, loc), const char *str)
953 mprAssert(VALID_BLK(ctx));
959 len = strlen(str) + 1;
961 if (len < MPR_SLAB_STR_MAX) {
962 newp = mprSlabAllocBlock(MPR_LOC_PASS(ctx, loc), MPR_SLAB_STR_MAX,
965 newp = mprAllocBlock(MPR_LOC_PASS(ctx, loc), len);
969 memcpy(newp, str, len);
975 /******************************************************************************/
977 * Internal strndup function. Will use the slab allocator for small strings
980 char *mprStrndupInternal(MPR_LOC_DEC(ctx, loc), const char *str, uint size)
985 mprAssert(VALID_BLK(ctx));
990 len = strlen(str) + 1;
991 len = min(len, size);
993 if (len < MPR_SLAB_STR_MAX) {
994 newp = mprSlabAllocBlock(MPR_LOC_PASS(ctx, loc), MPR_SLAB_STR_MAX,
997 newp = mprAllocBlock(MPR_LOC_PASS(ctx, loc), len);
1001 memcpy(newp, str, len);
1007 /******************************************************************************/
1009 * Internal memcpy function. Will use the slab allocator for small strings
1012 void *mprMemdupInternal(MPR_LOC_DEC(ctx, loc), const void *ptr, uint size)
1016 mprAssert(VALID_BLK(ctx));
1018 if (size < MPR_SLAB_STR_MAX) {
1019 newp = mprSlabAllocBlock(MPR_LOC_PASS(ctx, loc), MPR_SLAB_STR_MAX,
1022 newp = mprAllocBlock(MPR_LOC_PASS(ctx, loc), size);
1026 memcpy(newp, ptr, size);
1032 /******************************************************************************/
1034 * Steal a block from one context and insert in another
1037 int mprStealAllocBlock(MPR_LOC_DEC(ctx, loc), const void *ptr)
1039 MprBlk *bp, *parent;
1045 mprAssert(VALID_BLK(ctx));
1046 mprAssert(VALID_BLK(ptr));
1050 #if BLD_DEBUG && !BREW
1051 if (bp == stopAlloc) {
1052 mprBreakpoint(MPR_LOC, "breakOnAddr");
1057 mprAssert(VALID_HDR(bp));
1058 mprAssert(ptr != mprGetAllocParent(ptr));
1062 mprAssert(bp->prev);
1063 mprAssert(bp->prev->next);
1064 mprAssert(bp->next);
1065 mprAssert(bp->next->prev);
1067 parent = bp->parent;
1068 mprAssert(VALID_HDR(parent));
1070 mprLock(bp->app->allocLock);
1071 if (parent->children == bp) {
1072 if (bp->next == bp) {
1073 parent->children = 0;
1075 parent->children = bp->next;
1079 bp->prev->next = bp->next;
1080 bp->next->prev = bp->prev;
1082 parent = GET_HDR(ctx);
1083 mprAssert(VALID_HDR(parent));
1084 bp->parent = parent;
1086 if (parent->children == 0) {
1087 parent->children = bp;
1088 bp->next = bp->prev = bp;
1091 bp->next = parent->children;
1092 bp->prev = parent->children->prev;
1093 parent->children->prev->next = bp;
1094 parent->children->prev = bp;
1097 #if BLD_FEATURE_ALLOC_LEAK_TRACK
1101 VALIDATE_BLOCK(GET_PTR(bp));
1103 mprUnlock(bp->app->allocLock);
1108 /******************************************************************************/
1110 void mprSetRequiredAlloc(MprCtx ptr, bool recurse)
1112 MprBlk *bp, *firstChild, *cp;
1116 bp->flags |= ALLOC_FLAGS_REQUIRED;
1118 if (recurse && (firstChild = bp->children) != 0) {
1121 mprSetRequiredAlloc(GET_PTR(cp), recurse);
1123 } while (cp != firstChild);
1127 /******************************************************************************/
1129 * Monitor stack usage. Return true if the stack has grown
1132 int mprStackCheck(MprCtx ptr)
1137 mprAssert(VALID_BLK(ptr));
1139 app = mprGetApp(ptr);
1141 size = (int) app->stackStart - (int) &app;
1143 app->maxStack -= size;
1144 app->stackStart = (void*) &app;
1147 if ((uint) size > app->maxStack) {
1148 app->maxStack = size;
1154 /******************************************************************************/
1156 * Return the stack size
1159 int mprStackSize(MprCtx ptr)
1163 mprAssert(VALID_BLK(ptr));
1165 app = mprGetApp(ptr);
1166 return app->maxStack;
1169 /******************************************************************************/
1171 static int mprAllocException(MPR_LOC_DEC(ctx, loc), uint size, bool granted)
1177 mprAssert(VALID_BLK(ctx));
1179 app = mprGetApp(ctx);
1180 alloc = &app->alloc;
1182 if (alloc->cback == 0) {
1186 mprLock(app->allocLock);
1187 if (alloc->inAllocException == 0) {
1188 alloc->inAllocException = 1;
1189 mprUnlock(app->allocLock);
1191 rc = (alloc->cback)(app, size, alloc->stats.bytesAllocated, granted);
1193 mprLock(app->allocLock);
1194 app->alloc.inAllocException = 0;
1195 mprUnlock(app->allocLock);
1202 /******************************************************************************/
1204 void mprSetAllocLimits(MprApp *app, uint redLine, uint maxMemory)
1206 app->alloc.stats.redLine = redLine;
1207 app->alloc.stats.maxMemory = maxMemory;
1210 /******************************************************************************/
1212 MprAllocCback mprSetAllocCallback(MprApp *app, MprAllocCback cback)
1217 mprAssert(VALID_BLK(app));
1219 old = app->alloc.cback;
1220 app->alloc.cback = cback;
1224 /******************************************************************************/
1226 uint mprGetAllocBlockSize(MprCtx ptr)
1230 mprAssert(VALID_BLK(ptr));
1237 mprAssert(VALID_HDR(bp));
1244 /******************************************************************************/
1246 * Return the total block count used by a block including all children
1249 uint mprGetAllocBlockCount(MprCtx ptr)
1251 MprBlk *bp, *firstChild, *cp;
1254 mprAssert(VALID_BLK(ptr));
1261 mprAssert(VALID_HDR(bp));
1264 * Add one for itself
1267 if ((firstChild = bp->children) != 0) {
1270 count += mprGetAllocBlockCount(GET_PTR(cp));
1272 } while (cp != firstChild);
1277 /******************************************************************************/
1279 * Return the total of all memory allocated including slabs
1282 uint mprGetAllocBlockMemory(MprCtx ptr)
1284 MprBlk *bp, *firstChild, *cp;
1287 mprAssert(VALID_BLK(ptr));
1294 mprAssert(VALID_HDR(bp));
1296 count = bp->size + HDR_SIZE;
1297 if ((firstChild = bp->children) != 0) {
1300 count += mprGetAllocBlockMemory(GET_PTR(cp));
1302 } while (cp != firstChild);
1307 /******************************************************************************/
1308 #if BLD_FEATURE_ALLOC_LEAK_TRACK
1310 const char *mprGetAllocLocation(MprCtx ptr)
1317 mprAssert(VALID_BLK(ptr));
1320 mprAssert(VALID_HDR(bp));
1321 return bp->location;
1325 /******************************************************************************/
1327 void *mprGetAllocParent(MprCtx ptr)
1331 mprAssert(VALID_BLK(ptr));
1338 mprAssert(VALID_HDR(bp));
1342 return GET_PTR(bp->parent);
1345 /******************************************************************************/
1347 MprAllocStats *mprGetAllocStats(MprApp *app)
1349 mprAssert(VALID_BLK(app));
1351 return &app->alloc.stats;
1354 /******************************************************************************/
1355 #if BLD_FEATURE_ALLOC_STATS
1357 MprSlabStats *mprGetSlabAllocStats(MprApp *app, int slabIndex)
1361 mprAssert(VALID_BLK(app));
1363 if (0 <= slabIndex && slabIndex < MPR_MAX_SLAB) {
1364 slab = &app->alloc.slabs[slabIndex];
1365 return &slab->stats;
1368 mprAssert(0 <= slabIndex && slabIndex < MPR_MAX_SLAB);
1372 #endif /* BLD_FEATURE_ALLOC_STATS */
1373 /******************************************************************************/
1376 int mprPrintAllocBlocks(MprCtx ptr, int indent)
1378 MprBlk *bp, *firstChild, *cp;
1379 const char *location;
1380 int subTotal, size, indentSpaces, code;
1386 if (! (bp->flags & ALLOC_FLAGS_REQUIRED)) {
1387 size = bp->size + HDR_SIZE;
1390 * Take one level off because we don't trace app
1392 indentSpaces = indent;
1394 if (bp->flags & ALLOC_FLAGS_REQUIRED) {
1396 } else if (bp->flags & ALLOC_FLAGS_IS_SLAB) {
1402 #if BLD_FEATURE_ALLOC_LEAK_TRACK
1403 location = bp->location;
1408 "%c %.*s %-16s %.*s size %5d has %3d deps, total %6d", code,
1410 mprGetBaseName(location),
1413 mprGetAllocBlockCount(GET_PTR(bp)),
1414 mprGetAllocBlockMemory(GET_PTR(bp))
1421 if ((firstChild = bp->children) != 0) {
1424 subTotal += mprPrintAllocBlocks(GET_PTR(cp), indent + 2);
1426 } while (cp != firstChild);
1433 /******************************************************************************/
1434 #if BLD_FEATURE_ALLOC_STATS
1436 * Print a memory allocation report that includes a list of allocated blocks
1437 * and a statistics summary
1440 void mprPrintAllocReport(MprApp *app, bool printBlocks, const char *msg)
1442 MprSlabStats *stats;
1446 mprAssert(VALID_BLK(app));
1449 mprLog(app, 0, " ");
1450 mprLog(app, 0, "%s", msg);
1459 mprLog(app, 0, " ");
1460 sum = mprPrintAllocBlocks(app, 0);
1462 mprLog(app, 0, " Sum of blocks %d", sum);
1464 mprLog(app, 0, " None");
1472 mprLog(app, 0, " ");
1473 mprLog(app, 0, "MPR Slab Memory Stats");
1474 mprLog(app, 0, " ");
1477 " Index Size Total Allocated Free PeakAlloc PeakFree TotalAlloc");
1480 for (i = 0; i < MPR_MAX_SLAB; i++) {
1481 stats = &app->alloc.slabs[i].stats;
1482 size = 1 << (i + 5);
1483 if (stats->totalAllocCount > 0) {
1484 mprLog(app, 0, " %2d %6d %8d %9d %6d %9d %8d %10d",
1485 i, size, size * (stats->allocCount + stats->freeCount),
1486 stats->allocCount, stats->freeCount,
1487 stats->peakAllocCount, stats->peakFreeCount,
1488 stats->totalAllocCount);
1489 total += size * (stats->allocCount + stats->freeCount);
1492 mprLog(app, 0, " ");
1493 mprLog(app, 0, "MPR Total Allocated Slab RAM: %10d", total);
1494 mprLog(app, 0, "MPR Total Allocated RAM: %10d",
1495 mprGetAllocatedMemory(app));
1496 mprLog(app, 0, "MPR Peak Allocated RAM: %10d",
1497 mprGetPeakAllocatedMemory(app));
1498 mprLog(app, 0, " ");
1501 /******************************************************************************/
1503 * Return the total memory allocated.
1506 uint mprGetAllocatedMemory(MprCtx ctx)
1510 app = mprGetApp(ctx);
1512 return app->alloc.stats.bytesAllocated;
1515 /******************************************************************************/
1517 * Return the peak memory allocated.
1520 uint mprGetPeakAllocatedMemory(MprCtx ctx)
1524 app = mprGetApp(ctx);
1526 return app->alloc.stats.peakAllocated;
1529 /******************************************************************************/
1531 * Return memory in the MPR slab. This excludes the EJS slabs
1534 uint mprGetAllocatedSlabMemory(MprCtx ctx)
1537 MprSlabStats *stats;
1541 app = mprGetApp(ctx);
1544 for (i = 0; i < MPR_MAX_SLAB; i++) {
1545 stats = &app->alloc.slabs[i].stats;
1546 size = 1 << (i + 5);
1547 if (stats->totalAllocCount > 0) {
1548 total += size * (stats->allocCount + stats->freeCount);
1554 #endif /* BLD_FEATURE_ALLOC_STATS */
1555 /******************************************************************************/
1557 MprDestructor mprSetDestructor(MprCtx ptr, MprDestructor destructor)
1562 mprAssert(VALID_BLK(ptr));
1571 mprAssert(VALID_HDR(bp));
1572 mprAssert(ptr != mprGetAllocParent(ptr));
1576 old = bp->destructor;
1577 bp->destructor = destructor;
1582 /******************************************************************************/
1584 int mprIsAllocBlockValid(MprCtx ptr)
1589 return (bp && VALID_HDR(bp));
1592 /******************************************************************************/
1595 * Exhaustive validation of the block and its children. Does not go recursive
1596 * as it would be too slow.
1599 int mprValidateBlock(MprCtx ptr)
1601 MprBlk *bp, *parent, *cp, *firstChild;
1605 mprAssert(VALID_BLK(ptr));
1610 mprAssert(VALID_HDR(bp));
1611 mprAssert(VALID_HDR(bp->parent));
1613 if (ptr != bp->app) {
1614 mprAssert(bp != bp->parent);
1616 mprAssert(! (bp->flags & ALLOC_FLAGS_FREE));
1617 mprAssert(! (bp->flags & ALLOC_FLAGS_FREEING));
1623 parent = bp->parent;
1625 if ((firstChild = bp->children) != 0) {
1627 mprAssert((int) cp != 0xfeefee);
1629 mprAssert(bp->next->prev == bp);
1630 mprAssert(bp->prev->next == bp);
1631 mprAssert(bp->prev->parent == parent);
1632 mprAssert(bp->next->parent == parent);
1637 if (bp->next == bp) {
1638 mprAssert(bp->prev == bp);
1639 if (ptr != bp->app) {
1640 mprAssert(parent->children == bp);
1643 if (bp->prev == bp) {
1644 mprAssert(bp->next == bp);
1645 if (ptr != bp->app) {
1646 mprAssert(parent->children == bp);
1649 } while (cp != firstChild);
1656 /******************************************************************************/
1658 * Validate a block and all children
1661 int mprValidateAllocTree(MprCtx ptr)
1664 MprBlk *bp, *cp, *firstChild;
1667 mprAssert(VALID_BLK(ptr));
1671 mprValidateBlock(GET_PTR(bp));
1673 if ((firstChild = bp->children) != 0) {
1676 mprValidateAllocTree(GET_PTR(cp));
1678 } while (cp != firstChild);
1685 /******************************************************************************/
1686 #if UNUSED && FUTURE
1688 * Exhaustive validation of the block and its children. Does not go recursive
1689 * as it would be too slow.
1692 int mprValidateSlabs(MprApp *app)
1695 MprSlabStats *slabStats;
1699 for (i = 0; i < MPR_MAX_SLAB; i++) {
1700 slab = &app->alloc.slabs[i];
1701 slabStats = &slab->stats;
1704 for (sp = slab->next; sp; sp = sp->next) {
1707 mprAssert(count == (int) slabStats->freeCount);
1713 /******************************************************************************/
1715 void mprAllocAbort()
1718 printf("Bad block header");
1724 /******************************************************************************/
1727 * Get the root parent from any block (which is the MprApp structure)
1730 MprApp *mprGetApp(MprCtx ptr)
1737 mprAssert(VALID_HDR(bp));
1741 mprAssert(bp->app->magic == APP_MAGIC);
1746 /******************************************************************************/
1748 int mprGetAllocErrors(MprCtx ctx)
1752 app = mprGetApp(ctx);
1753 return app->alloc.stats.errors;
1756 /******************************************************************************/
1758 void mprClearAllocErrors(MprCtx ctx)
1762 app = mprGetApp(ctx);
1763 app->alloc.stats.errors = 0;
1766 /******************************************************************************/
1773 * vim600: sw=4 ts=4 fdm=marker
1774 * vim<600: sw=4 ts=4