On Fri, 2014-08-08 at 01:16 -0700, Jeff Davis wrote: > Either way, it's better to be conservative. Attached is a version of the > patch with opt-in memory usage tracking. Child contexts inherit the > setting from their parent.
There was a problem with the previous patch: the parent was unlinked before the delete_context method was called, which meant that the parent's memory was not being decremented. Attached is a fix. It would be simpler to just reorder the operations in MemoryContextDelete, but there is a comment warning against that. So, I pass the old parent as an argument to the delete_context method. Regards, Jeff Davis
*** a/src/backend/utils/mmgr/aset.c --- b/src/backend/utils/mmgr/aset.c *************** *** 242,247 **** typedef struct AllocChunkData --- 242,249 ---- #define AllocChunkGetPointer(chk) \ ((AllocPointer)(((char *)(chk)) + ALLOC_CHUNKHDRSZ)) + static void update_allocation(MemoryContext context, int64 size); + /* * These functions implement the MemoryContext API for AllocSet contexts. */ *************** *** 250,256 **** static void AllocSetFree(MemoryContext context, void *pointer); static void *AllocSetRealloc(MemoryContext context, void *pointer, Size size); static void AllocSetInit(MemoryContext context); static void AllocSetReset(MemoryContext context); ! static void AllocSetDelete(MemoryContext context); static Size AllocSetGetChunkSpace(MemoryContext context, void *pointer); static bool AllocSetIsEmpty(MemoryContext context); static void AllocSetStats(MemoryContext context, int level); --- 252,258 ---- static void *AllocSetRealloc(MemoryContext context, void *pointer, Size size); static void AllocSetInit(MemoryContext context); static void AllocSetReset(MemoryContext context); ! static void AllocSetDelete(MemoryContext context, MemoryContext parent); static Size AllocSetGetChunkSpace(MemoryContext context, void *pointer); static bool AllocSetIsEmpty(MemoryContext context); static void AllocSetStats(MemoryContext context, int level); *************** *** 430,435 **** randomize_mem(char *ptr, size_t size) --- 432,440 ---- * minContextSize: minimum context size * initBlockSize: initial allocation block size * maxBlockSize: maximum allocation block size + * + * The flag determining whether this context tracks memory usage is inherited + * from the parent context. */ MemoryContext AllocSetContextCreate(MemoryContext parent, *************** *** 438,443 **** AllocSetContextCreate(MemoryContext parent, --- 443,468 ---- Size initBlockSize, Size maxBlockSize) { + return AllocSetContextCreateTracked( + parent, name, minContextSize, initBlockSize, maxBlockSize, + (parent == NULL) ? false : parent->track_mem); + } + + /* + * AllocSetContextCreateTracked + * Create a new AllocSet context. + * + * Implementation for AllocSetContextCreate, but also allows the caller to + * specify whether memory usage should be tracked or not. + */ + MemoryContext + AllocSetContextCreateTracked(MemoryContext parent, + const char *name, + Size minContextSize, + Size initBlockSize, + Size maxBlockSize, + bool track_mem) + { AllocSet context; /* Do the type-independent part of context creation */ *************** *** 445,451 **** AllocSetContextCreate(MemoryContext parent, sizeof(AllocSetContext), &AllocSetMethods, parent, ! name); /* * Make sure alloc parameters are reasonable, and save them. --- 470,477 ---- sizeof(AllocSetContext), &AllocSetMethods, parent, ! name, ! track_mem); /* * Make sure alloc parameters are reasonable, and save them. *************** *** 500,505 **** AllocSetContextCreate(MemoryContext parent, --- 526,534 ---- errdetail("Failed while creating memory context \"%s\".", name))); } + + update_allocation((MemoryContext) context, blksize); + block->aset = context; block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ; block->endptr = ((char *) block) + blksize; *************** *** 590,595 **** AllocSetReset(MemoryContext context) --- 619,625 ---- else { /* Normal case, release the block */ + update_allocation(context, -(block->endptr - ((char*) block))); #ifdef CLOBBER_FREED_MEMORY wipe_mem(block, block->freeptr - ((char *) block)); #endif *************** *** 611,617 **** AllocSetReset(MemoryContext context) * But note we are not responsible for deleting the context node itself. */ static void ! AllocSetDelete(MemoryContext context) { AllocSet set = (AllocSet) context; AllocBlock block = set->blocks; --- 641,647 ---- * But note we are not responsible for deleting the context node itself. */ static void ! AllocSetDelete(MemoryContext context, MemoryContext parent) { AllocSet set = (AllocSet) context; AllocBlock block = set->blocks; *************** *** 623,628 **** AllocSetDelete(MemoryContext context) --- 653,668 ---- AllocSetCheck(context); #endif + /* + * Parent is already unlinked from context, so can't use + * update_allocation(). + */ + while (parent != NULL) + { + parent->total_allocated -= context->total_allocated; + parent = parent->parent; + } + /* Make it look empty, just in case... */ MemSetAligned(set->freelist, 0, sizeof(set->freelist)); set->blocks = NULL; *************** *** 678,683 **** AllocSetAlloc(MemoryContext context, Size size) --- 718,726 ---- errmsg("out of memory"), errdetail("Failed on request of size %zu.", size))); } + + update_allocation(context, blksize); + block->aset = set; block->freeptr = block->endptr = ((char *) block) + blksize; *************** *** 873,878 **** AllocSetAlloc(MemoryContext context, Size size) --- 916,923 ---- errdetail("Failed on request of size %zu.", size))); } + update_allocation(context, blksize); + block->aset = set; block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ; block->endptr = ((char *) block) + blksize; *************** *** 976,981 **** AllocSetFree(MemoryContext context, void *pointer) --- 1021,1027 ---- set->blocks = block->next; else prevblock->next = block->next; + update_allocation(context, -(block->endptr - ((char*) block))); #ifdef CLOBBER_FREED_MEMORY wipe_mem(block, block->freeptr - ((char *) block)); #endif *************** *** 1088,1093 **** AllocSetRealloc(MemoryContext context, void *pointer, Size size) --- 1134,1140 ---- AllocBlock prevblock = NULL; Size chksize; Size blksize; + Size oldblksize; while (block != NULL) { *************** *** 1105,1110 **** AllocSetRealloc(MemoryContext context, void *pointer, Size size) --- 1152,1159 ---- /* Do the realloc */ chksize = MAXALIGN(size); blksize = chksize + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ; + oldblksize = block->endptr - ((char *)block); + block = (AllocBlock) realloc(block, blksize); if (block == NULL) { *************** *** 1114,1119 **** AllocSetRealloc(MemoryContext context, void *pointer, Size size) --- 1163,1169 ---- errmsg("out of memory"), errdetail("Failed on request of size %zu.", size))); } + update_allocation(context, blksize - oldblksize); block->freeptr = block->endptr = ((char *) block) + blksize; /* Update pointers since block has likely been moved */ *************** *** 1277,1282 **** AllocSetStats(MemoryContext context, int level) --- 1327,1359 ---- } + /* + * update_allocation + * + * Track newly-allocated or newly-freed memory (freed memory should be + * negative). + */ + static void + update_allocation(MemoryContext context, int64 size) + { + MemoryContext parent; + + if (!context->track_mem) + return; + + context->self_allocated += size; + + for (parent = context; parent != NULL; parent = parent->parent) + { + if (!parent->track_mem) + break; + + parent->total_allocated += size; + Assert(parent->self_allocated >= 0); + Assert(parent->total_allocated >= 0); + } + } + #ifdef MEMORY_CONTEXT_CHECKING /* *** a/src/backend/utils/mmgr/mcxt.c --- b/src/backend/utils/mmgr/mcxt.c *************** *** 187,192 **** MemoryContextResetChildren(MemoryContext context) --- 187,194 ---- void MemoryContextDelete(MemoryContext context) { + MemoryContext parent = context->parent; + AssertArg(MemoryContextIsValid(context)); /* We had better not be deleting TopMemoryContext ... */ Assert(context != TopMemoryContext); *************** *** 202,208 **** MemoryContextDelete(MemoryContext context) */ MemoryContextSetParent(context, NULL); ! (*context->methods->delete_context) (context); VALGRIND_DESTROY_MEMPOOL(context); pfree(context); } --- 204,211 ---- */ MemoryContextSetParent(context, NULL); ! /* pass the parent in case it's needed, however */ ! (*context->methods->delete_context) (context, parent); VALGRIND_DESTROY_MEMPOOL(context); pfree(context); } *************** *** 324,329 **** MemoryContextAllowInCriticalSection(MemoryContext context, bool allow) --- 327,349 ---- } /* + * MemoryContextGetAllocated + * + * Return memory allocated by the system to this context. If total is true, + * include child contexts. Context must have track_mem set. + */ + int64 + MemoryContextGetAllocated(MemoryContext context, bool total) + { + Assert(context->track_mem); + + if (total) + return context->total_allocated; + else + return context->self_allocated; + } + + /* * GetMemoryChunkSpace * Given a currently-allocated chunk, determine the total space * it occupies (including all memory-allocation overhead). *************** *** 546,552 **** MemoryContext MemoryContextCreate(NodeTag tag, Size size, MemoryContextMethods *methods, MemoryContext parent, ! const char *name) { MemoryContext node; Size needed = size + strlen(name) + 1; --- 566,573 ---- MemoryContextCreate(NodeTag tag, Size size, MemoryContextMethods *methods, MemoryContext parent, ! const char *name, ! bool track_mem) { MemoryContext node; Size needed = size + strlen(name) + 1; *************** *** 576,581 **** MemoryContextCreate(NodeTag tag, Size size, --- 597,605 ---- node->firstchild = NULL; node->nextchild = NULL; node->isReset = true; + node->track_mem = track_mem; + node->total_allocated = 0; + node->self_allocated = 0; node->name = ((char *) node) + size; strcpy(node->name, name); *** a/src/include/nodes/memnodes.h --- b/src/include/nodes/memnodes.h *************** *** 41,47 **** typedef struct MemoryContextMethods void *(*realloc) (MemoryContext context, void *pointer, Size size); void (*init) (MemoryContext context); void (*reset) (MemoryContext context); ! void (*delete_context) (MemoryContext context); Size (*get_chunk_space) (MemoryContext context, void *pointer); bool (*is_empty) (MemoryContext context); void (*stats) (MemoryContext context, int level); --- 41,48 ---- void *(*realloc) (MemoryContext context, void *pointer, Size size); void (*init) (MemoryContext context); void (*reset) (MemoryContext context); ! void (*delete_context) (MemoryContext context, ! MemoryContext parent); Size (*get_chunk_space) (MemoryContext context, void *pointer); bool (*is_empty) (MemoryContext context); void (*stats) (MemoryContext context, int level); *************** *** 60,65 **** typedef struct MemoryContextData --- 61,69 ---- MemoryContext nextchild; /* next child of same parent */ char *name; /* context name (just for debugging) */ bool isReset; /* T = no space alloced since last reset */ + bool track_mem; /* whether to track memory usage */ + int64 total_allocated; /* including child contexts */ + int64 self_allocated; /* not including child contexts */ #ifdef USE_ASSERT_CHECKING bool allowInCritSection; /* allow palloc in critical section */ #endif *** a/src/include/utils/memutils.h --- b/src/include/utils/memutils.h *************** *** 96,101 **** extern void MemoryContextDeleteChildren(MemoryContext context); --- 96,102 ---- extern void MemoryContextResetAndDeleteChildren(MemoryContext context); extern void MemoryContextSetParent(MemoryContext context, MemoryContext new_parent); + extern int64 MemoryContextGetAllocated(MemoryContext context, bool total); extern Size GetMemoryChunkSpace(void *pointer); extern MemoryContext GetMemoryChunkContext(void *pointer); extern MemoryContext MemoryContextGetParent(MemoryContext context); *************** *** 117,123 **** extern bool MemoryContextContains(MemoryContext context, void *pointer); extern MemoryContext MemoryContextCreate(NodeTag tag, Size size, MemoryContextMethods *methods, MemoryContext parent, ! const char *name); /* --- 118,125 ---- extern MemoryContext MemoryContextCreate(NodeTag tag, Size size, MemoryContextMethods *methods, MemoryContext parent, ! const char *name, ! bool track_mem); /* *************** *** 130,135 **** extern MemoryContext AllocSetContextCreate(MemoryContext parent, --- 132,143 ---- Size minContextSize, Size initBlockSize, Size maxBlockSize); + extern MemoryContext AllocSetContextCreateTracked(MemoryContext parent, + const char *name, + Size minContextSize, + Size initBlockSize, + Size maxBlockSize, + bool track_mem); /* * Recommended default alloc parameters, suitable for "ordinary" contexts
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers