| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /*------------------------------------------------------------------------- |
| * |
| * mcxt.c |
| * POSTGRES memory context management code. |
| * |
| * This module handles context management operations that are independent |
| * of the particular kind of context being operated on. It calls |
| * context-type-specific operations via the function pointers in a |
| * context's MemoryContextMethods struct. |
| * |
| * |
| * Portions Copyright (c) 2007-2008, Greenplum inc |
| * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group |
| * Portions Copyright (c) 1994, Regents of the University of California |
| * |
| * |
| * IDENTIFICATION |
| * $PostgreSQL: pgsql/src/backend/utils/mmgr/mcxt.c,v 1.58 2006/07/14 14:52:25 momjian Exp $ |
| * |
| *------------------------------------------------------------------------- |
| */ |
| |
| #include "postgres.h" |
| |
| #include "miscadmin.h" /* MyProcPid */ |
| #include "utils/memutils.h" |
| #include "utils/memaccounting.h" |
| |
| #include "utils/debugbreak.h" |
| |
| #include "cdb/cdbvars.h" /* gp_process_memory_cutoff_bytes */ |
| #include "inttypes.h" |
| |
| #ifdef HAVE_STDINT_H |
| #include <stdint.h> /* SIZE_MAX (C99) */ |
| #endif |
| #ifndef SIZE_MAX |
| #define SIZE_MAX ((Size)0-(Size)1) /* for Solaris */ |
| #endif |
| |
| #ifdef CDB_PALLOC_CALLER_ID |
| #define CDB_MCXT_WHERE(context) (context)->callerFile, (context)->callerLine |
| #else |
| #define CDB_MCXT_WHERE(context) __FILE__, __LINE__ |
| #endif |
| |
| #if defined(CDB_PALLOC_TAGS) && !defined(CDB_PALLOC_CALLER_ID) |
| #error "If CDB_PALLOC_TAGS is defined, CDB_PALLOC_CALLER_ID must be defined too" |
| #endif |
| |
| /* Maximum allowed length of the name of a context including the parent names prepended */ |
| #define MAX_CONTEXT_NAME_SIZE 200 |
| |
| /***************************************************************************** |
| * GLOBAL MEMORY * |
| *****************************************************************************/ |
| |
| /* |
| * CurrentMemoryContext |
| * Default memory context for allocations. |
| */ |
| MemoryContext CurrentMemoryContext = NULL; |
| |
| /* |
| * Standard top-level contexts. For a description of the purpose of each |
| * of these contexts, refer to src/backend/utils/mmgr/README |
| */ |
| MemoryContext TopMemoryContext = NULL; |
| MemoryContext ErrorContext = NULL; |
| MemoryContext PostmasterContext = NULL; |
| MemoryContext CacheMemoryContext = NULL; |
| MemoryContext MessageContext = NULL; |
| MemoryContext TopTransactionContext = NULL; |
| MemoryContext CurTransactionContext = NULL; |
| MemoryContext MemoryAccountMemoryContext = NULL; |
| |
| /* These two are transient links to contexts owned by other objects: */ |
| MemoryContext QueryContext = NULL; |
| MemoryContext PortalContext = NULL; |
| |
| |
| /***************************************************************************** |
| * EXPORTED ROUTINES * |
| *****************************************************************************/ |
| |
| |
| /* |
| * MemoryContextInit |
| * Start up the memory-context subsystem. |
| * |
| * This must be called before creating contexts or allocating memory in |
| * contexts. TopMemoryContext and ErrorContext are initialized here; |
| * other contexts must be created afterwards. |
| * |
| * In normal multi-backend operation, this is called once during |
| * postmaster startup, and not at all by individual backend startup |
| * (since the backends inherit an already-initialized context subsystem |
| * by virtue of being forked off the postmaster). |
| * |
| * In a standalone backend this must be called during backend startup. |
| */ |
| void |
| MemoryContextInit(void) |
| { |
| AssertState(TopMemoryContext == NULL); |
| AssertState(CurrentMemoryContext == NULL); |
| AssertState(MemoryAccountMemoryContext == NULL); |
| |
| /* |
| * Initialize TopMemoryContext as an AllocSetContext with slow growth rate |
| * --- we don't really expect much to be allocated in it. |
| * |
| * (There is special-case code in MemoryContextCreate() for this call.) |
| */ |
| TopMemoryContext = AllocSetContextCreate((MemoryContext) NULL, |
| "TopMemoryContext", |
| 0, |
| 8 * 1024, |
| 8 * 1024); |
| |
| /* |
| * Not having any other place to point CurrentMemoryContext, make it point |
| * to TopMemoryContext. Caller should change this soon! |
| */ |
| CurrentMemoryContext = TopMemoryContext; |
| |
| /* |
| * Initialize ErrorContext as an AllocSetContext with slow growth rate --- |
| * we don't really expect much to be allocated in it. More to the point, |
| * require it to contain at least 8K at all times. This is the only case |
| * where retained memory in a context is *essential* --- we want to be |
| * sure ErrorContext still has some memory even if we've run out |
| * elsewhere! |
| */ |
| ErrorContext = AllocSetContextCreate(TopMemoryContext, |
| "ErrorContext", |
| 8 * 1024, |
| 8 * 1024, |
| 8 * 1024); |
| |
| MemoryAccounting_Reset(); |
| } |
| |
| /* |
| * MemoryContextReset |
| * Release all space allocated within a context and its descendants, |
| * but don't delete the contexts themselves. |
| * |
| * The type-specific reset routine handles the context itself, but we |
| * have to do the recursion for the children. |
| */ |
| void |
| MemoryContextReset(MemoryContext context) |
| { |
| AssertArg(MemoryContextIsValid(context)); |
| |
| /* save a function call in common case where there are no children */ |
| if (context->firstchild != NULL) |
| MemoryContextResetChildren(context); |
| |
| (*context->methods.reset) (context); |
| } |
| |
| /* |
| * MemoryContextResetChildren |
| * Release all space allocated within a context's descendants, |
| * but don't delete the contexts themselves. The named context |
| * itself is not touched. |
| */ |
| void |
| MemoryContextResetChildren(MemoryContext context) |
| { |
| MemoryContext child; |
| |
| AssertArg(MemoryContextIsValid(context)); |
| |
| for (child = context->firstchild; child != NULL; child = child->nextchild) |
| MemoryContextReset(child); |
| } |
| |
| /* |
| * MemoryContextDelete |
| * Delete a context and its descendants, and release all space |
| * allocated therein. |
| * |
| * The type-specific delete routine removes all subsidiary storage |
| * for the context, but we have to delete the context node itself, |
| * as well as recurse to get the children. We must also delink the |
| * node from its parent, if it has one. |
| */ |
| void |
| MemoryContextDeleteImpl(MemoryContext context, const char* sfile, const char *func, int sline) |
| { |
| AssertArg(MemoryContextIsValid(context)); |
| /* We had better not be deleting TopMemoryContext ... */ |
| Assert(context != TopMemoryContext); |
| /* And not CurrentMemoryContext, either */ |
| Assert(context != CurrentMemoryContext); |
| |
| #ifdef CDB_PALLOC_CALLER_ID |
| context->callerFile = sfile; |
| context->callerLine = sline; |
| #endif |
| |
| MemoryContextDeleteChildren(context); |
| |
| /* |
| * We delink the context from its parent before deleting it, so that if |
| * there's an error we won't have deleted/busted contexts still attached |
| * to the context tree. Better a leak than a crash. |
| */ |
| if (context->parent) |
| { |
| MemoryContext parent = context->parent; |
| |
| if (context == parent->firstchild) |
| parent->firstchild = context->nextchild; |
| else |
| { |
| MemoryContext child; |
| |
| for (child = parent->firstchild; child; child = child->nextchild) |
| { |
| if (context == child->nextchild) |
| { |
| child->nextchild = context->nextchild; |
| break; |
| } |
| } |
| } |
| } |
| (*context->methods.delete_context)(context); |
| pfree(context); |
| } |
| |
| /* |
| * MemoryContextDeleteChildren |
| * Delete all the descendants of the named context and release all |
| * space allocated therein. The named context itself is not touched. |
| */ |
| void |
| MemoryContextDeleteChildren(MemoryContext context) |
| { |
| AssertArg(MemoryContextIsValid(context)); |
| |
| /* |
| * MemoryContextDelete will delink the child from me, so just iterate as |
| * long as there is a child. |
| */ |
| while (context->firstchild != NULL) |
| MemoryContextDelete(context->firstchild); |
| } |
| |
| /* |
| * MemoryContextResetAndDeleteChildren |
| * Release all space allocated within a context and delete all |
| * its descendants. |
| * |
| * This is a common combination case where we want to preserve the |
| * specific context but get rid of absolutely everything under it. |
| */ |
| void |
| MemoryContextResetAndDeleteChildren(MemoryContext context) |
| { |
| AssertArg(MemoryContextIsValid(context)); |
| |
| MemoryContextDeleteChildren(context); |
| (*context->methods.reset) (context); |
| } |
| |
| /* |
| * GetMemoryChunkSpace |
| * Given a currently-allocated chunk, determine the total space |
| * it occupies (including all memory-allocation overhead). |
| * |
| * This is useful for measuring the total space occupied by a set of |
| * allocated chunks. |
| */ |
| Size |
| GetMemoryChunkSpace(void *pointer) |
| { |
| StandardChunkHeader *header; |
| |
| /* |
| * Try to detect bogus pointers handed to us, poorly though we can. |
| * Presumably, a pointer that isn't MAXALIGNED isn't pointing at an |
| * allocated chunk. |
| */ |
| Assert(pointer != NULL); |
| Assert(pointer == (void *) MAXALIGN(pointer)); |
| |
| /* |
| * OK, it's probably safe to look at the chunk header. |
| */ |
| header = (StandardChunkHeader *) |
| ((char *) pointer - STANDARDCHUNKHEADERSIZE); |
| |
| AssertArg(MemoryContextIsValid(header->sharedHeader->context)); |
| |
| return (*header->sharedHeader->context->methods.get_chunk_space) (header->sharedHeader->context, |
| pointer); |
| } |
| |
| /* |
| * GetMemoryChunkContext |
| * Given a currently-allocated chunk, determine the context |
| * it belongs to. |
| */ |
| MemoryContext |
| GetMemoryChunkContext(void *pointer) |
| { |
| StandardChunkHeader *header; |
| |
| /* |
| * Try to detect bogus pointers handed to us, poorly though we can. |
| * Presumably, a pointer that isn't MAXALIGNED isn't pointing at an |
| * allocated chunk. |
| */ |
| Assert(pointer != NULL); |
| Assert(pointer == (void *) MAXALIGN(pointer)); |
| |
| /* |
| * OK, it's probably safe to look at the chunk header. |
| */ |
| header = (StandardChunkHeader *) |
| ((char *) pointer - STANDARDCHUNKHEADERSIZE); |
| |
| AssertArg(MemoryContextIsValid(header->sharedHeader->context)); |
| |
| return header->sharedHeader->context; |
| } |
| |
| /* |
| * MemoryContextIsEmpty |
| * Is a memory context empty of any allocated space? |
| */ |
| bool |
| MemoryContextIsEmpty(MemoryContext context) |
| { |
| AssertArg(MemoryContextIsValid(context)); |
| |
| /* |
| * For now, we consider a memory context nonempty if it has any children; |
| * perhaps this should be changed later. |
| */ |
| if (context->firstchild != NULL) |
| return false; |
| /* Otherwise use the type-specific inquiry */ |
| return (*context->methods.is_empty) (context); |
| } |
| |
| |
| /* |
| * MemoryContextNoteAlloc |
| * Update lifetime cumulative statistics upon allocation from host mem mgr. |
| * |
| * Called by the context-type-specific memory manager upon successfully |
| * obtaining a block of size 'nbytes' from its lower-level source (e.g. malloc). |
| */ |
| void |
| MemoryContextNoteAlloc(MemoryContext context, Size nbytes) |
| { |
| Size held; |
| |
| AssertArg(MemoryContextIsValid(context)); |
| |
| for (;;) |
| { |
| Assert(context->allBytesAlloc >= context->allBytesFreed); |
| Assert(context->allBytesAlloc - context->allBytesFreed < SIZE_MAX - nbytes); |
| |
| context->allBytesAlloc += nbytes; |
| |
| held = (Size)(context->allBytesAlloc - context->allBytesFreed); |
| if (context->maxBytesHeld < held) |
| context->maxBytesHeld = held; |
| |
| if (!context->parent) |
| break; |
| context = context->parent; |
| } |
| } /* MemoryContextNoteAlloc */ |
| |
| /* |
| * MemoryContextNoteFree |
| * Update lifetime cumulative statistics upon free to host memory manager. |
| * |
| * Called by the context-type-specific memory manager upon relinquishing a |
| * block of size 'nbytes' back to its lower-level source (e.g. free()). |
| */ |
| void |
| MemoryContextNoteFree(MemoryContext context, Size nbytes) |
| { |
| Size held; |
| |
| AssertArg(MemoryContextIsValid(context)); |
| |
| while (context) |
| { |
| Assert(context->allBytesAlloc >= context->allBytesFreed + nbytes); |
| Assert(context->allBytesFreed + nbytes >= context->allBytesFreed); |
| |
| context->allBytesFreed += nbytes; |
| |
| held = (Size)(context->allBytesAlloc - context->allBytesFreed); |
| if (context->localMinHeld > held) |
| context->localMinHeld = held; |
| |
| context = context->parent; |
| } |
| } /* MemoryContextNoteFree */ |
| |
| /* |
| * MemoryContextError |
| * Report failure of a memory context operation. Does not return. |
| */ |
| void |
| MemoryContextError(int errorcode, MemoryContext context, |
| const char *sfile, int sline, |
| const char *fmt, ...) |
| { |
| va_list args; |
| char buf[200]; |
| |
| /* |
| * Don't use elog, as we might have a malloc problem. |
| * Also, don't use write_log, as this method might be |
| * called from syslogger, which does not support |
| * write_log calls |
| */ |
| write_stderr("Logging memory usage for memory context error"); |
| |
| MemoryAccounting_SaveToLog(); |
| MemoryContextStats(TopMemoryContext); |
| |
| if(coredump_on_memerror) |
| { |
| /* |
| * Turn memory context into a SIGSEGV, so will generate |
| * a core dump. |
| * |
| * XXX What is the right way of doing this? |
| */ |
| *(int *) NULL = errorcode; |
| } |
| |
| if(errorcode != ERRCODE_OUT_OF_MEMORY) |
| { |
| Assert(!"Memory context error!"); |
| } |
| |
| /* Format caller's message. */ |
| va_start(args, fmt); |
| vsnprintf(buf, sizeof(buf)-32, fmt, args); |
| va_end(args); |
| |
| /* |
| * This might fail if we run out of memory at the system level |
| * (i.e., malloc returned null), and the system is running so |
| * low in memory that ereport cannot format its parameter. |
| * However, we already dumped our usage information using |
| * write_stderr, so we are gonna take a chance by calling ereport. |
| * If we fail, we at least have OOM message in the log. If we succeed, |
| * we will also have the detail error code and location of the error. |
| * Note, ereport should switch to ErrorContext which should have |
| * some preallocated memory to handle this message. Therefore, |
| * our chance of success is quite high |
| */ |
| ereport(ERROR, (errcode(errorcode), |
| errmsg("%s (context '%s') (%s:%d)", |
| buf, |
| context->name, |
| sfile ? sfile : "", |
| sline) |
| )); |
| |
| /* not reached */ |
| abort(); |
| } /* MemoryContextError */ |
| |
| |
| /* |
| * MemoryContextGetCurrentSpace |
| * Return the number of bytes currently occupied by the memory context. |
| * |
| * This is the amount of space obtained from the lower-level source of the |
| * memory (e.g. malloc) and not yet released back to that source. Includes |
| * overhead and free space held and managed within this context by the |
| * context-type-specific memory manager. |
| */ |
| Size |
| MemoryContextGetCurrentSpace(MemoryContext context) |
| { |
| AssertArg(MemoryContextIsValid(context)); |
| Assert(context->allBytesAlloc >= context->allBytesFreed); |
| Assert(context->allBytesAlloc - context->allBytesFreed < SIZE_MAX); |
| |
| return (Size)(context->allBytesAlloc - context->allBytesFreed); |
| } /* MemoryContextGetCurrentSpace */ |
| |
| /* |
| * MemoryContextGetPeakSpace |
| * Return the peak number of bytes occupied by the memory context. |
| * |
| * This is the maximum value reached by MemoryContextGetCurrentSpace() since |
| * the context was created, or since reset by MemoryContextSetPeakSpace(). |
| */ |
| Size |
| MemoryContextGetPeakSpace(MemoryContext context) |
| { |
| AssertArg(MemoryContextIsValid(context)); |
| return context->maxBytesHeld; |
| } /* MemoryContextGetPeakSpace */ |
| |
| /* |
| * MemoryContextSetPeakSpace |
| * Resets the peak space statistic to the space currently occupied or |
| * the specified value, whichever is greater. Returns the former peak |
| * space value. |
| * |
| * Can be used to observe local maximum usage over an interval and then to |
| * restore the overall maximum. |
| */ |
| Size |
| MemoryContextSetPeakSpace(MemoryContext context, Size nbytes) |
| { |
| Size held; |
| Size oldpeak; |
| |
| AssertArg(MemoryContextIsValid(context)); |
| Assert(context->allBytesAlloc >= context->allBytesFreed); |
| Assert(context->allBytesAlloc - context->allBytesFreed < SIZE_MAX); |
| |
| oldpeak = context->maxBytesHeld; |
| |
| held = (Size)(context->allBytesAlloc - context->allBytesFreed); |
| context->maxBytesHeld = Max(held, nbytes); |
| |
| return oldpeak; |
| } /* MemoryContextSetPeakSpace */ |
| |
| |
| /* |
| * MemoryContextName |
| * Format the name of the memory context into the caller's buffer. |
| * |
| * Returns ptr to the name string within the supplied buffer. (The string |
| * is built at the tail of the buffer from right to left.) |
| */ |
| char * |
| MemoryContextName(MemoryContext context, MemoryContext relativeTo, |
| char *buf, int bufsize) |
| { |
| MemoryContext ctx; |
| char *cbp = buf + bufsize - 1; |
| |
| AssertArg(MemoryContextIsValid(context)); |
| |
| if (bufsize <= 0) |
| return buf; |
| |
| for (ctx = context; ctx && ctx != relativeTo; ctx = ctx->parent) |
| { |
| const char *name = ctx->name ? ctx->name : ""; |
| int len = strlen(name); |
| |
| if (cbp - buf < len + 1) |
| { |
| len = Min(3, cbp - buf); |
| cbp -= len; |
| memcpy(cbp, "...", len); |
| break; |
| } |
| if (ctx != context) |
| *--cbp = '/'; |
| cbp -= len; |
| memcpy(cbp, name, len); |
| } |
| |
| if (buf < cbp) |
| { |
| if (!ctx) |
| *--cbp = '/'; |
| else if (ctx == context) |
| *--cbp = '.'; |
| } |
| |
| buf[bufsize-1] = '\0'; |
| return cbp; |
| } /* MemoryContextName */ |
| |
| /* |
| * MemoryContext_LogContextStats |
| * Logs memory consumption details of a given context. |
| * |
| * Parameters: |
| * siblingCount: number of sibling context of this context in the memory context tree |
| * allAllocated: total bytes allocated in this context |
| * allFreed: total bytes freed in this context |
| * curAvailable: bytes that are allocated in blocks but are not used in any chunks |
| * contextName: name of the context |
| */ |
| static void |
| MemoryContext_LogContextStats(uint64 siblingCount, uint64 allAllocated, |
| uint64 allFreed, uint64 curAvailable, const char *contextName) |
| { |
| write_stderr("context: %" PRIu64 ", %" PRIu64 ", %" PRIu64 ", %" PRIu64 ", %" PRIu64 ", %s\n", \ |
| siblingCount, (allAllocated - allFreed), curAvailable, \ |
| allAllocated, allFreed, contextName); |
| } |
| |
| |
| /* |
| * MemoryContextStats_recur |
| * Print statistics about the named context and all its descendants. |
| * |
| * This is just a debugging utility, so it's not fancy. The statistics |
| * are merely sent to stderr. |
| * |
| * Parameters: |
| * topContext: the top of the sub-tree where we start our processing |
| * rootContext: the root context of the entire tree that can be used |
| * to generate a bread crumb like context name |
| * |
| * topContexName: the name of the top context |
| * nameBuffer: a buffer to format the name of any future context |
| * nameBufferSize: size of the nameBuffer |
| * nBlocksTop: number of blocks in the top context |
| * nChunksTop: number of chunks in the top context |
| * |
| * currentAvailableTop: free space across all blocks in the top context |
| * |
| * allAllocatedTop: total bytes allocated in the top context, including |
| * blocks that are already dropped |
| * |
| * allFreedTop: total bytes that were freed in the top context |
| * maxHeldTop: maximum bytes held in the top context |
| */ |
| static void |
| MemoryContextStats_recur(MemoryContext topContext, MemoryContext rootContext, |
| char *topContextName, char *nameBuffer, int nameBufferSize, |
| uint64 nBlocksTop, uint64 nChunksTop, |
| uint64 currentAvailableTop, uint64 allAllocatedTop, |
| uint64 allFreedTop, uint64 maxHeldTop) |
| { |
| MemoryContext child; |
| char* name; |
| |
| AssertArg(MemoryContextIsValid(topContext)); |
| |
| uint64 nBlocks = 0; |
| uint64 nChunks = 0; |
| uint64 currentAvailable = 0; |
| uint64 allAllocated = 0; |
| uint64 allFreed = 0; |
| uint64 maxHeld = 0; |
| |
| /* |
| * The top context is always supposed to have children contexts. Therefore, it is not |
| * collapse-able with other siblings. So, the siblingCount is set to 1. |
| */ |
| MemoryContext_LogContextStats(1 /* siblingCount */, allAllocatedTop, allFreedTop, currentAvailableTop, topContextName); |
| |
| uint64 cumBlocks = 0; |
| uint64 cumChunks = 0; |
| uint64 cumCurAvailable = 0; |
| uint64 cumAllAllocated = 0; |
| uint64 cumAllFreed = 0; |
| uint64 cumMaxHeld = 0; |
| |
| char prevChildName[MAX_CONTEXT_NAME_SIZE] = ""; |
| |
| uint64 siblingCount = 0; |
| |
| for (child = topContext->firstchild; child != NULL; child = child->nextchild) |
| { |
| /* Get name and ancestry of this MemoryContext */ |
| name = MemoryContextName(child, rootContext, nameBuffer, nameBufferSize); |
| |
| (*child->methods.stats)(child, &nBlocks, &nChunks, ¤tAvailable, &allAllocated, &allFreed, &maxHeld); |
| |
| if (child->firstchild == NULL) |
| { |
| /* To qualify for sibling collapsing the context must not have any child context */ |
| |
| if (strcmp(name, prevChildName) == 0) |
| { |
| cumBlocks += nBlocks; |
| cumChunks += nChunks; |
| cumCurAvailable += currentAvailable; |
| cumAllAllocated += allAllocated; |
| cumAllFreed += allFreed; |
| cumMaxHeld = Max(cumMaxHeld, maxHeld); |
| |
| siblingCount++; |
| } |
| else |
| { |
| if (siblingCount != 0) |
| { |
| /* |
| * Output the previous cumulative stat, and start a new run. Note: don't just |
| * pass the new one to MemoryContextStats_recur, as the new one might be the |
| * start of another run of duplicate contexts |
| */ |
| |
| MemoryContext_LogContextStats(siblingCount, cumAllAllocated, cumAllFreed, cumCurAvailable, prevChildName); |
| } |
| |
| cumBlocks = nBlocks; |
| cumChunks = nChunks; |
| cumCurAvailable = currentAvailable; |
| cumAllAllocated = allAllocated; |
| cumAllFreed = allFreed; |
| cumMaxHeld = maxHeld; |
| |
| /* Move new name into previous name */ |
| strncpy(prevChildName, name, MAX_CONTEXT_NAME_SIZE - 1); |
| |
| /* The current one is the sole sibling */ |
| siblingCount = 1; |
| } |
| } |
| else |
| { |
| /* Does not qualify for sibling collapsing as the context has child context */ |
| |
| if (siblingCount != 0) |
| { |
| /* |
| * We have previously collapsed (one or more siblings with empty children) context |
| * stats that we want to print here. Output the previous cumulative stat. |
| */ |
| |
| MemoryContext_LogContextStats(siblingCount, cumAllAllocated, cumAllFreed, cumCurAvailable, prevChildName); |
| } |
| |
| MemoryContextStats_recur(child, rootContext, name, nameBuffer, nameBufferSize, nBlocks, |
| nChunks, currentAvailable, allAllocated, allFreed, maxHeld); |
| |
| /* |
| * We just traversed a child node, so we need to make sure we don't carry over |
| * any child name from previous matching siblings. So, we reset prevChildName, |
| * and all cumulative stats |
| */ |
| prevChildName[0] = '\0'; |
| |
| cumBlocks = 0; |
| cumChunks = 0; |
| cumCurAvailable = 0; |
| cumAllAllocated = 0; |
| cumAllFreed = 0; |
| cumMaxHeld = 0; |
| |
| /* |
| * The current one doesn't qualify for collapsing, and we already |
| * printed it and its children by calling MemoryContextStats_recur |
| */ |
| siblingCount = 0; |
| } |
| } |
| |
| if (siblingCount != 0) |
| { |
| /* Output any unprinted cumulative stats */ |
| |
| MemoryContext_LogContextStats(siblingCount, cumAllAllocated, cumAllFreed, cumCurAvailable, prevChildName); |
| } |
| } |
| |
| /* |
| * MemoryContextStats |
| * Prints the usage details of a context. |
| * |
| * Parameters: |
| * context: the context of interest. |
| */ |
| void |
| MemoryContextStats(MemoryContext context) |
| { |
| char* name; |
| char namebuf[MAX_CONTEXT_NAME_SIZE]; |
| |
| AssertArg(MemoryContextIsValid(context)); |
| |
| name = MemoryContextName(context, NULL, namebuf, sizeof(namebuf)); |
| write_stderr("pid %d: Memory statistics for %s/\n", MyProcPid, name); |
| write_stderr("context: occurrences_count, currently_allocated, currently_available, total_allocated, total_freed, name\n"); |
| |
| uint64 nBlocks = 0; |
| uint64 nChunks = 0; |
| uint64 currentAvailable = 0; |
| uint64 allAllocated = 0; |
| uint64 allFreed = 0; |
| uint64 maxHeld = 0; |
| int namebufsize = sizeof(namebuf); |
| |
| /* Get the root context's stat and pass it to the MemoryContextStats_recur for printing */ |
| (*context->methods.stats)(context, &nBlocks, &nChunks, ¤tAvailable, &allAllocated, &allFreed, &maxHeld); |
| name = MemoryContextName(context, context, namebuf, namebufsize); |
| |
| MemoryContextStats_recur(context, context, name, namebuf, namebufsize, nBlocks, nChunks, |
| currentAvailable, allAllocated, allFreed, maxHeld); |
| } |
| |
| |
| /* |
| * MemoryContextCheck |
| * Check all chunks in the named context. |
| * |
| * This is just a debugging utility, so it's not fancy. |
| */ |
| #ifdef MEMORY_CONTEXT_CHECKING |
| void |
| MemoryContextCheck(MemoryContext context) |
| { |
| MemoryContext child; |
| |
| AssertArg(MemoryContextIsValid(context)); |
| |
| (*context->methods.check) (context); |
| for (child = context->firstchild; child != NULL; child = child->nextchild) |
| MemoryContextCheck(child); |
| } |
| #endif |
| |
| /* |
| * MemoryContextContains |
| * Detect whether an allocated chunk of memory belongs to a given |
| * context or not. |
| * |
| * Note: this test assumes that the pointer was allocated using palloc. |
| * If unsure, please use the generic version (MemoryContextContainsGenericAllocation). |
| * |
| * Caution: this test is reliable as long as the 'pointer' does point to |
| * a chunk of memory allocated from *some* context. If 'pointer' points |
| * at memory obtained in some other way, there is a small chance of a |
| * false-positive result since the bits right before it might look like |
| * a valid chunk header by chance. In the latter case (when the memory |
| * was not palloc'ed), we are more likely to crash. Please use the generic |
| * version of this method if you have any doubt that the tested memory |
| * region may not be palloc'ed. |
| */ |
| bool |
| MemoryContextContains(MemoryContext context, void *pointer) |
| { |
| StandardChunkHeader *header; |
| |
| /* |
| * Try to detect bogus pointers handed to us, poorly though we can. |
| * Presumably, a pointer that isn't MAXALIGNED isn't pointing at an |
| * allocated chunk. |
| */ |
| if (pointer == NULL || pointer != (void *) MAXALIGN(pointer)) |
| { |
| return false; |
| } |
| |
| /* |
| * OK, it's probably safe to look at the chunk header. |
| */ |
| header = (StandardChunkHeader *) |
| ((char *) pointer - STANDARDCHUNKHEADERSIZE); |
| |
| if (header->sharedHeader == NULL || (void*)header->sharedHeader != (void *) MAXALIGN(header->sharedHeader) || !AllocSizeIsValid(header->size)) |
| { |
| return false; |
| } |
| |
| SharedChunkHeader *sharedHeader = (SharedChunkHeader *)header->sharedHeader; |
| |
| /* |
| * If the context link doesn't match then we certainly have a non-member |
| * chunk. Also check for a reasonable-looking size as extra guard against |
| * being fooled by bogus pointers. |
| */ |
| if (sharedHeader->context == context) |
| { |
| return true; |
| } |
| return false; |
| } |
| |
| /* |
| * MemoryContextContainsGenericAllocation |
| * Detects whether a generic (may or may not be allocated by |
| * palloc) chunk of memory belongs to a given context or not. |
| * Note, the "generic" means it will be ready to |
| * handle chunks not allocated using palloc. |
| * |
| * Caution: this test has the same problem as MemoryContextContains |
| * where it can falsely detect a chunk belonging to a context, |
| * while it does not. In addition, it can also falsely conclude |
| * that a chunk does *not* belong to a context, while in reality |
| * it does. The latter weakness stems from its versatility to |
| * handle non-palloc'ed chunks. |
| */ |
| bool |
| MemoryContextContainsGenericAllocation(MemoryContext context, void *pointer) |
| { |
| StandardChunkHeader *header; |
| |
| /* |
| * Try to detect bogus pointers handed to us, poorly though we can. |
| * Presumably, a pointer that isn't MAXALIGNED isn't pointing at an |
| * allocated chunk. |
| */ |
| if (pointer == NULL || pointer != (void *) MAXALIGN(pointer)) |
| { |
| return false; |
| } |
| |
| /* |
| * OK, it's probably safe to look at the chunk header. |
| */ |
| header = (StandardChunkHeader *) |
| ((char *) pointer - STANDARDCHUNKHEADERSIZE); |
| |
| AllocSet set = (AllocSet)context; |
| |
| if (header->sharedHeader == set->sharedHeaderList || |
| (set->sharedHeaderList != NULL && set->sharedHeaderList->next == header->sharedHeader) || |
| (set->sharedHeaderList != NULL && set->sharedHeaderList->next != NULL && set->sharedHeaderList->next->next == header->sharedHeader)) |
| { |
| /* |
| * At this point we know that one of the sharedHeader pointers of the |
| * provided context (AllocSet) is the same as the sharedHeader |
| * pointer of the provided chunk. Therefore, the chunk should |
| * belong to the AllocSet (with a false positive chance coming |
| * from some third party allocated memory region having the |
| * same value as the sharedHeaderList pointer address |
| */ |
| return true; |
| } |
| |
| /* |
| * We might falsely conclude that the chunk does not belong |
| * to the context, if we fail to match the chunk's sharedHeader |
| * pointer with one of the leading sharedHeader pointers in the |
| * context's sharedHeaderList. |
| */ |
| return false; |
| } |
| |
| /*-------------------- |
| * MemoryContextCreate |
| * Context-type-independent part of context creation. |
| * |
| * This is only intended to be called by context-type-specific |
| * context creation routines, not by the unwashed masses. |
| * |
| * The context creation procedure is a little bit tricky because |
| * we want to be sure that we don't leave the context tree invalid |
| * in case of failure (such as insufficient memory to allocate the |
| * context node itself). The procedure goes like this: |
| * 1. Context-type-specific routine first calls MemoryContextCreate(), |
| * passing the appropriate tag/size/methods values (the methods |
| * pointer will ordinarily point to statically allocated data). |
| * The parent and name parameters usually come from the caller. |
| * 2. MemoryContextCreate() attempts to allocate the context node, |
| * plus space for the name. If this fails we can ereport() with no |
| * damage done. |
| * 3. We fill in all of the type-independent MemoryContext fields. |
| * 4. We call the type-specific init routine (using the methods pointer). |
| * The init routine is required to make the node minimally valid |
| * with zero chance of failure --- it can't allocate more memory, |
| * for example. |
| * 5. Now we have a minimally valid node that can behave correctly |
| * when told to reset or delete itself. We link the node to its |
| * parent (if any), making the node part of the context tree. |
| * 6. We return to the context-type-specific routine, which finishes |
| * up type-specific initialization. This routine can now do things |
| * that might fail (like allocate more memory), so long as it's |
| * sure the node is left in a state that delete will handle. |
| * |
| * This protocol doesn't prevent us from leaking memory if step 6 fails |
| * during creation of a top-level context, since there's no parent link |
| * in that case. However, if you run out of memory while you're building |
| * a top-level context, you might as well go home anyway... |
| * |
| * Normally, the context node and the name are allocated from |
| * TopMemoryContext (NOT from the parent context, since the node must |
| * survive resets of its parent context!). However, this routine is itself |
| * used to create TopMemoryContext! If we see that TopMemoryContext is NULL, |
| * we assume we are creating TopMemoryContext and use malloc() to allocate |
| * the node. |
| * |
| * Note that the name field of a MemoryContext does not point to |
| * separately-allocated storage, so it should not be freed at context |
| * deletion. |
| *-------------------- |
| */ |
| MemoryContext |
| MemoryContextCreate(NodeTag tag, Size size, |
| MemoryContextMethods *methods, |
| MemoryContext parent, |
| const char *name) |
| { |
| MemoryContext node; |
| Size needed = size + strlen(name) + 1; |
| |
| /* Get space for node and name */ |
| if (TopMemoryContext != NULL) |
| { |
| /* Normal case: allocate the node in TopMemoryContext */ |
| node = (MemoryContext) MemoryContextAlloc(TopMemoryContext, |
| needed); |
| } |
| else |
| { |
| /* Special case for startup: use good ol' malloc */ |
| node = (MemoryContext) malloc(needed); |
| } |
| if(!node) |
| { |
| ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), |
| errmsg("Failed to create memory context: out of memory") |
| )); |
| } |
| |
| /* Initialize the node as best we can */ |
| MemSet(node, 0, size); |
| node->type = tag; |
| node->methods = *methods; |
| node->parent = parent; |
| node->firstchild = NULL; |
| node->nextchild = NULL; |
| node->name = ((char *) node) + size; |
| strcpy(node->name, name); |
| |
| /* Type-specific routine finishes any other essential initialization */ |
| (*node->methods.init) (node); |
| |
| /* OK to link node to parent (if any) */ |
| if (parent) |
| { |
| node->nextchild = parent->firstchild; |
| parent->firstchild = node; |
| } |
| |
| /* Return to type-specific creation routine to finish up */ |
| return node; |
| } |
| |
| /* |
| * MemoryContextAlloc |
| * Allocate space within the specified context. |
| * |
| * This could be turned into a macro, but we'd have to import |
| * nodes/memnodes.h into postgres.h which seems a bad idea. |
| */ |
| void * |
| MemoryContextAllocImpl(MemoryContext context, Size size, const char* sfile, const char *sfunc, int sline) |
| { |
| void *ret; |
| |
| #ifdef PGTRACE_ENABLED |
| StandardChunkHeader *header; |
| #endif |
| AssertArg(MemoryContextIsValid(context)); |
| |
| #ifdef CDB_PALLOC_CALLER_ID |
| context->callerFile = sfile; |
| context->callerLine = sline; |
| #endif |
| |
| if (!AllocSizeIsValid(size)) |
| MemoryContextError(ERRCODE_INTERNAL_ERROR, |
| context, CDB_MCXT_WHERE(context), |
| "invalid memory alloc request size %lu", |
| (unsigned long)size); |
| |
| ret = (*context->methods.alloc) (context, size); |
| #ifdef PGTRACE_ENABLED |
| header = (StandardChunkHeader *) |
| ((char *) ret - STANDARDCHUNKHEADERSIZE); |
| PG_TRACE5(memctxt__alloc, size, header->size, 0, 0, (long) context->name); |
| #endif |
| |
| return ret; |
| } |
| |
| /* |
| * MemoryContextAllocZero |
| * Like MemoryContextAlloc, but clears allocated memory |
| * |
| * We could just call MemoryContextAlloc then clear the memory, but this |
| * is a very common combination, so we provide the combined operation. |
| */ |
| void * |
| MemoryContextAllocZeroImpl(MemoryContext context, Size size, const char* sfile, const char *sfunc, int sline) |
| { |
| void *ret; |
| |
| #ifdef PGTRACE_ENABLED |
| StandardChunkHeader *header; |
| #endif |
| AssertArg(MemoryContextIsValid(context)); |
| |
| #ifdef CDB_PALLOC_CALLER_ID |
| context->callerFile = sfile; |
| context->callerLine = sline; |
| #endif |
| |
| if (!AllocSizeIsValid(size)) |
| MemoryContextError(ERRCODE_INTERNAL_ERROR, |
| context, CDB_MCXT_WHERE(context), |
| "invalid memory alloc request size %lu", |
| (unsigned long)size); |
| |
| ret = (*context->methods.alloc) (context, size); |
| |
| MemSetAligned(ret, 0, size); |
| |
| #ifdef PGTRACE_ENABLED |
| header = (StandardChunkHeader *) |
| ((char *) ret - STANDARDCHUNKHEADERSIZE); |
| PG_TRACE5(memctxt__alloc, size, header->size, 0, 0, (long) context->name); |
| #endif |
| |
| return ret; |
| } |
| |
| /* |
| * MemoryContextAllocZeroAligned |
| * MemoryContextAllocZero where length is suitable for MemSetLoop |
| * |
| * This might seem overly specialized, but it's not because newNode() |
| * is so often called with compile-time-constant sizes. |
| */ |
| void * |
| MemoryContextAllocZeroAlignedImpl(MemoryContext context, Size size, const char* sfile, const char *sfunc, int sline) |
| { |
| void *ret; |
| |
| #ifdef PGTRACE_ENABLED |
| StandardChunkHeader *header; |
| #endif |
| |
| AssertArg(MemoryContextIsValid(context)); |
| |
| #ifdef CDB_PALLOC_CALLER_ID |
| context->callerFile = sfile; |
| context->callerLine = sline; |
| #endif |
| |
| if (!AllocSizeIsValid(size)) |
| MemoryContextError(ERRCODE_INTERNAL_ERROR, |
| context, CDB_MCXT_WHERE(context), |
| "invalid memory alloc request size %lu", |
| (unsigned long)size); |
| |
| ret = (*context->methods.alloc) (context, size); |
| |
| MemSetLoop(ret, 0, size); |
| |
| #ifdef PGTRACE_ENABLED |
| header = (StandardChunkHeader *) |
| ((char *) ret - STANDARDCHUNKHEADERSIZE); |
| PG_TRACE5(memctxt__alloc, size, header->size, 0, 0, (long) context->name); |
| #endif |
| |
| return ret; |
| } |
| |
| /* |
| * pfree |
| * Release an allocated chunk. |
| */ |
| void |
| MemoryContextFreeImpl(void *pointer, const char *sfile, const char *sfunc, int sline) |
| { |
| /* |
| * Try to detect bogus pointers handed to us, poorly though we can. |
| * Presumably, a pointer that isn't MAXALIGNED isn't pointing at an |
| * allocated chunk. |
| */ |
| Assert(pointer != NULL); |
| Assert(pointer == (void *) MAXALIGN(pointer)); |
| |
| /* |
| * OK, it's probably safe to look at the chunk header. |
| */ |
| StandardChunkHeader* header = (StandardChunkHeader *) |
| ((char *) pointer - STANDARDCHUNKHEADERSIZE); |
| |
| AssertArg(MemoryContextIsValid(header->sharedHeader->context)); |
| |
| #ifdef PGTRACE_ENABLED |
| PG_TRACE5(memctxt__free, 0, 0, |
| #ifdef MEMORY_CONTEXT_CHECKING |
| header->requested_size, header->size, |
| #else |
| 0, header->size, |
| #endif |
| (long) header->sharedHeader->context->name); |
| #endif |
| |
| #ifdef CDB_PALLOC_CALLER_ID |
| header->sharedHeader->context->callerFile = sfile; |
| header->sharedHeader->context->callerLine = sline; |
| #endif |
| |
| if (header->sharedHeader->context->methods.free_p) |
| (*header->sharedHeader->context->methods.free_p) (header->sharedHeader->context, pointer); |
| else |
| Assert(header); /* this assert never fails. Just here so we can set breakpoint in debugger. */ |
| } |
| |
| /* |
| * repalloc |
| * Adjust the size of a previously allocated chunk. |
| */ |
| void * |
| MemoryContextReallocImpl(void *pointer, Size size, const char *sfile, const char *sfunc, int sline) |
| { |
| StandardChunkHeader *header; |
| void *ret; |
| |
| #ifdef PGTRACE_ENABLED |
| long old_reqsize; |
| long old_size; |
| #endif |
| |
| /* |
| * Try to detect bogus pointers handed to us, poorly though we can. |
| * Presumably, a pointer that isn't MAXALIGNED isn't pointing at an |
| * allocated chunk. |
| */ |
| Assert(pointer != NULL); |
| Assert(pointer == (void *) MAXALIGN(pointer)); |
| |
| /* |
| * OK, it's probably safe to look at the chunk header. |
| */ |
| header = (StandardChunkHeader *) |
| ((char *) pointer - STANDARDCHUNKHEADERSIZE); |
| |
| AssertArg(MemoryContextIsValid(header->sharedHeader->context)); |
| |
| #ifdef PGTRACE_ENABLED |
| #ifdef MEMORY_CONTEXT_CHECKING |
| old_reqsize = header->requested_size; |
| #else |
| old_reqsize = 0; |
| #endif |
| old_size = header->size; |
| #endif |
| |
| #ifdef CDB_PALLOC_CALLER_ID |
| header->sharedHeader->context->callerFile = sfile; |
| header->sharedHeader->context->callerLine = sline; |
| #endif |
| |
| if (!AllocSizeIsValid(size)) |
| MemoryContextError(ERRCODE_INTERNAL_ERROR, |
| header->sharedHeader->context, CDB_MCXT_WHERE(header->sharedHeader->context), |
| "invalid memory alloc request size %lu", |
| (unsigned long)size); |
| |
| ret = (*header->sharedHeader->context->methods.realloc) (header->sharedHeader->context, pointer, size); |
| |
| #ifdef PGTRACE_ENABLED |
| header = (StandardChunkHeader *) |
| ((char *) ret - STANDARDCHUNKHEADERSIZE); |
| PG_TRACE5(memctxt__realloc, size, header->size, old_reqsize, old_size, (long) header->sharedHeader->context->name); |
| #endif |
| |
| return ret; |
| } |
| |
| |
| /* |
| * MemoryContextStrdup |
| * Like strdup(), but allocate from the specified context |
| */ |
| char * |
| MemoryContextStrdup(MemoryContext context, const char *string) |
| { |
| char *nstr; |
| Size len = strlen(string) + 1; |
| |
| nstr = (char *) MemoryContextAlloc(context, len); |
| |
| memcpy(nstr, string, len); |
| |
| return nstr; |
| } |
| |
| |
| /* |
| * floor_log2_Size |
| */ |
| int floor_log2_Size(Size sz) |
| { |
| return floor_log2_Size_inline(sz); |
| } |
| |
| /* |
| * ceil_log2_Size |
| */ |
| int ceil_log2_Size(Size sz) |
| { |
| return ceil_log2_Size_inline(sz); |
| } |
| |
| /* |
| * pnstrdup |
| * Like pstrdup(), but append null byte to a |
| * not-necessarily-null-terminated input string. |
| */ |
| char * |
| pnstrdup(const char *in, Size len) |
| { |
| char *out = palloc(len + 1); |
| |
| memcpy(out, in, len); |
| out[len] = '\0'; |
| return out; |
| } |
| |
| |
| #if defined(WIN32) || defined(__CYGWIN__) |
| /* |
| * Memory support routines for libpgport on Win32 |
| * |
| * Win32 can't load a library that DLLIMPORTs a variable |
| * if the link object files also DLLIMPORT the same variable. |
| * For this reason, libpgport can't reference CurrentMemoryContext |
| * in the palloc macro calls. |
| * |
| * To fix this, we create several functions here that allow us to |
| * manage memory without doing the inline in libpgport. |
| */ |
| void * |
| pgport_palloc(Size sz) |
| { |
| return palloc(sz); |
| } |
| |
| |
| char * |
| pgport_pstrdup(const char *str) |
| { |
| return pstrdup(str); |
| } |
| |
| |
| /* Doesn't reference a DLLIMPORT variable, but here for completeness. */ |
| void |
| pgport_pfree(void *pointer) |
| { |
| pfree(pointer); |
| } |
| |
| #endif |