/*------------------------------------------------------------------------- * * 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) 1996-2021, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * src/backend/utils/mmgr/mcxt.c * *------------------------------------------------------------------------- */ #include "postgres.h" #include "mb/pg_wchar.h" #include "miscadmin.h" #include "storage/proc.h" #include "storage/procarray.h" #include "storage/procsignal.h" #include "utils/fmgrprotos.h" #include "utils/memdebug.h" #include "utils/memutils.h" /***************************************************************************** * 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; /* This is a transient link to the active portal's memory context: */ MemoryContext PortalContext = NULL; static void MemoryContextCallResetCallbacks(MemoryContext context); static void MemoryContextStatsInternal(MemoryContext context, int level, bool print, int max_children, MemoryContextCounters *totals, bool print_to_stderr); static void MemoryContextStatsPrint(MemoryContext context, void *passthru, const char *stats_string, bool print_to_stderr); /* * You should not do memory allocations within a critical section, because * an out-of-memory error will be escalated to a PANIC. To enforce that * rule, the allocation functions Assert that. */ #define AssertNotInCriticalSection(context) \ Assert(CritSectionCount == 0 || (context)->allowInCritSection) /***************************************************************************** * 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). But in an EXEC_BACKEND * build, each process must do this for itself. * * In a standalone backend this must be called during backend startup. */ void MemoryContextInit(void) { AssertState(TopMemoryContext == NULL); /* * First, initialize TopMemoryContext, which is the parent of all others. */ TopMemoryContext = AllocSetContextCreate((MemoryContext) NULL, "TopMemoryContext", ALLOCSET_DEFAULT_SIZES); /* * 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! Also, allow allocations in ErrorContext within a critical * section. Otherwise a PANIC will cause an assertion failure in the error * reporting code, before printing out the real cause of the failure. * * This should be the last step in this function, as elog.c assumes memory * management works once ErrorContext is non-null. */ ErrorContext = AllocSetContextCreate(TopMemoryContext, "ErrorContext", 8 * 1024, 8 * 1024, 8 * 1024); MemoryContextAllowInCriticalSection(ErrorContext, true); } /* * MemoryContextReset * Release all space allocated within a context and delete all its * descendant contexts (but not the named context itself). */ void MemoryContextReset(MemoryContext context) { AssertArg(MemoryContextIsValid(context)); /* save a function call in common case where there are no children */ if (context->firstchild != NULL) MemoryContextDeleteChildren(context); /* save a function call if no pallocs since startup or last reset */ if (!context->isReset) MemoryContextResetOnly(context); } /* * MemoryContextResetOnly * Release all space allocated within a context. * Nothing is done to the context's descendant contexts. */ void MemoryContextResetOnly(MemoryContext context) { AssertArg(MemoryContextIsValid(context)); /* Nothing to do if no pallocs since startup or last reset */ if (!context->isReset) { MemoryContextCallResetCallbacks(context); /* * If context->ident points into the context's memory, it will become * a dangling pointer. We could prevent that by setting it to NULL * here, but that would break valid coding patterns that keep the * ident elsewhere, e.g. in a parent context. Another idea is to use * MemoryContextContains(), but we don't require ident strings to be * in separately-palloc'd chunks, so that risks false positives. So * for now we assume the programmer got it right. */ context->methods->reset(context); context->isReset = true; VALGRIND_DESTROY_MEMPOOL(context); VALGRIND_CREATE_MEMPOOL(context, 0, false); } } /* * 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) { MemoryContextResetChildren(child); MemoryContextResetOnly(child); } } /* * MemoryContextDelete * Delete a context and its descendants, and release all space * allocated therein. * * The type-specific delete routine removes all storage for the context, * but we have to recurse to handle the children. * We must also delink the context from its parent, if it has one. */ void MemoryContextDelete(MemoryContext context) { AssertArg(MemoryContextIsValid(context)); /* We had better not be deleting TopMemoryContext ... */ Assert(context != TopMemoryContext); /* And not CurrentMemoryContext, either */ Assert(context != CurrentMemoryContext); /* save a function call in common case where there are no children */ if (context->firstchild != NULL) MemoryContextDeleteChildren(context); /* * It's not entirely clear whether 'tis better to do this before or after * delinking the context; but an error in a callback will likely result in * leaking the whole context (if it's not a root context) if we do it * after, so let's do it before. */ MemoryContextCallResetCallbacks(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. */ MemoryContextSetParent(context, NULL); /* * Also reset the context's ident pointer, in case it points into the * context. This would only matter if someone tries to get stats on the * (already unlinked) context, which is unlikely, but let's be safe. */ context->ident = NULL; context->methods->delete_context(context); VALGRIND_DESTROY_MEMPOOL(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); } /* * MemoryContextRegisterResetCallback * Register a function to be called before next context reset/delete. * Such callbacks will be called in reverse order of registration. * * The caller is responsible for allocating a MemoryContextCallback struct * to hold the info about this callback request, and for filling in the * "func" and "arg" fields in the struct to show what function to call with * what argument. Typically the callback struct should be allocated within * the specified context, since that means it will automatically be freed * when no longer needed. * * There is no API for deregistering a callback once registered. If you * want it to not do anything anymore, adjust the state pointed to by its * "arg" to indicate that. */ void MemoryContextRegisterResetCallback(MemoryContext context, MemoryContextCallback *cb) { AssertArg(MemoryContextIsValid(context)); /* Push onto head so this will be called before older registrants. */ cb->next = context->reset_cbs; context->reset_cbs = cb; /* Mark the context as non-reset (it probably is already). */ context->isReset = false; } /* * MemoryContextCallResetCallbacks * Internal function to call all registered callbacks for context. */ static void MemoryContextCallResetCallbacks(MemoryContext context) { MemoryContextCallback *cb; /* * We pop each callback from the list before calling. That way, if an * error occurs inside the callback, we won't try to call it a second time * in the likely event that we reset or delete the context later. */ while ((cb = context->reset_cbs) != NULL) { context->reset_cbs = cb->next; cb->func(cb->arg); } } /* * MemoryContextSetIdentifier * Set the identifier string for a memory context. * * An identifier can be provided to help distinguish among different contexts * of the same kind in memory context stats dumps. The identifier string * must live at least as long as the context it is for; typically it is * allocated inside that context, so that it automatically goes away on * context deletion. Pass id = NULL to forget any old identifier. */ void MemoryContextSetIdentifier(MemoryContext context, const char *id) { AssertArg(MemoryContextIsValid(context)); context->ident = id; } /* * MemoryContextSetParent * Change a context to belong to a new parent (or no parent). * * We provide this as an API function because it is sometimes useful to * change a context's lifespan after creation. For example, a context * might be created underneath a transient context, filled with data, * and then reparented underneath CacheMemoryContext to make it long-lived. * In this way no special effort is needed to get rid of the context in case * a failure occurs before its contents are completely set up. * * Callers often assume that this function cannot fail, so don't put any * elog(ERROR) calls in it. * * A possible caller error is to reparent a context under itself, creating * a loop in the context graph. We assert here that context != new_parent, * but checking for multi-level loops seems more trouble than it's worth. */ void MemoryContextSetParent(MemoryContext context, MemoryContext new_parent) { AssertArg(MemoryContextIsValid(context)); AssertArg(context != new_parent); /* Fast path if it's got correct parent already */ if (new_parent == context->parent) return; /* Delink from existing parent, if any */ if (context->parent) { MemoryContext parent = context->parent; if (context->prevchild != NULL) context->prevchild->nextchild = context->nextchild; else { Assert(parent->firstchild == context); parent->firstchild = context->nextchild; } if (context->nextchild != NULL) context->nextchild->prevchild = context->prevchild; } /* And relink */ if (new_parent) { AssertArg(MemoryContextIsValid(new_parent)); context->parent = new_parent; context->prevchild = NULL; context->nextchild = new_parent->firstchild; if (new_parent->firstchild != NULL) new_parent->firstchild->prevchild = context; new_parent->firstchild = context; } else { context->parent = NULL; context->prevchild = NULL; context->nextchild = NULL; } } /* * MemoryContextAllowInCriticalSection * Allow/disallow allocations in this memory context within a critical * section. * * Normally, memory allocations are not allowed within a critical section, * because a failure would lead to PANIC. There are a few exceptions to * that, like allocations related to debugging code that is not supposed to * be enabled in production. This function can be used to exempt specific * memory contexts from the assertion in palloc(). */ void MemoryContextAllowInCriticalSection(MemoryContext context, bool allow) { AssertArg(MemoryContextIsValid(context)); context->allowInCritSection = allow; } /* * 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) { MemoryContext context = GetMemoryChunkContext(pointer); return context->methods->get_chunk_space(context, pointer); } /* * MemoryContextGetParent * Get the parent context (if any) of the specified context */ MemoryContext MemoryContextGetParent(MemoryContext context) { AssertArg(MemoryContextIsValid(context)); return context->parent; } /* * 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); } /* * Find the memory allocated to blocks for this memory context. If recurse is * true, also include children. */ Size MemoryContextMemAllocated(MemoryContext context, bool recurse) { Size total = context->mem_allocated; AssertArg(MemoryContextIsValid(context)); if (recurse) { MemoryContext child; for (child = context->firstchild; child != NULL; child = child->nextchild) total += MemoryContextMemAllocated(child, true); } return total; } /* * MemoryContextStats * Print statistics about the named context and all its descendants. * * This is just a debugging utility, so it's not very fancy. However, we do * make some effort to summarize when the output would otherwise be very long. * The statistics are sent to stderr. */ void MemoryContextStats(MemoryContext context) { /* A hard-wired limit on the number of children is usually good enough */ MemoryContextStatsDetail(context, 100, true); } /* * MemoryContextStatsDetail * * Entry point for use if you want to vary the number of child contexts shown. * * If print_to_stderr is true, print statistics about the memory contexts * with fprintf(stderr), otherwise use ereport(). */ void MemoryContextStatsDetail(MemoryContext context, int max_children, bool print_to_stderr) { MemoryContextCounters grand_totals; memset(&grand_totals, 0, sizeof(grand_totals)); MemoryContextStatsInternal(context, 0, true, max_children, &grand_totals, print_to_stderr); if (print_to_stderr) fprintf(stderr, "Grand total: %zu bytes in %zd blocks; %zu free (%zd chunks); %zu used\n", grand_totals.totalspace, grand_totals.nblocks, grand_totals.freespace, grand_totals.freechunks, grand_totals.totalspace - grand_totals.freespace); else /* * Use LOG_SERVER_ONLY to prevent the memory contexts from being sent * to the connected client. * * We don't buffer the information about all memory contexts in a * backend into StringInfo and log it as one message. Otherwise which * may require the buffer to be enlarged very much and lead to OOM * error since there can be a large number of memory contexts in a * backend. Instead, we log one message per memory context. */ ereport(LOG_SERVER_ONLY, (errhidestmt(true), errhidecontext(true), errmsg_internal("Grand total: %zu bytes in %zd blocks; %zu free (%zd chunks); %zu used", grand_totals.totalspace, grand_totals.nblocks, grand_totals.freespace, grand_totals.freechunks, grand_totals.totalspace - grand_totals.freespace))); } /* * MemoryContextStatsInternal * One recursion level for MemoryContextStats * * Print this context if print is true, but in any case accumulate counts into * *totals (if given). */ static void MemoryContextStatsInternal(MemoryContext context, int level, bool print, int max_children, MemoryContextCounters *totals, bool print_to_stderr) { MemoryContextCounters local_totals; MemoryContext child; int ichild; AssertArg(MemoryContextIsValid(context)); /* Examine the context itself */ context->methods->stats(context, print ? MemoryContextStatsPrint : NULL, (void *) &level, totals, print_to_stderr); /* * Examine children. If there are more than max_children of them, we do * not print the rest explicitly, but just summarize them. */ memset(&local_totals, 0, sizeof(local_totals)); for (child = context->firstchild, ichild = 0; child != NULL; child = child->nextchild, ichild++) { if (ichild < max_children) MemoryContextStatsInternal(child, level + 1, print, max_children, totals, print_to_stderr); else MemoryContextStatsInternal(child, level + 1, false, max_children, &local_totals, print_to_stderr); } /* Deal with excess children */ if (ichild > max_children) { if (print) { if (print_to_stderr) { int i; for (i = 0; i <= level; i++) fprintf(stderr, " "); fprintf(stderr, "%d more child contexts containing %zu total in %zd blocks; %zu free (%zd chunks); %zu used\n", ichild - max_children, local_totals.totalspace, local_totals.nblocks, local_totals.freespace, local_totals.freechunks, local_totals.totalspace - local_totals.freespace); } else ereport(LOG_SERVER_ONLY, (errhidestmt(true), errhidecontext(true), errmsg_internal("level: %d; %d more child contexts containing %zu total in %zd blocks; %zu free (%zd chunks); %zu used", level, ichild - max_children, local_totals.totalspace, local_totals.nblocks, local_totals.freespace, local_totals.freechunks, local_totals.totalspace - local_totals.freespace))); } if (totals) { totals->nblocks += local_totals.nblocks; totals->freechunks += local_totals.freechunks; totals->totalspace += local_totals.totalspace; totals->freespace += local_totals.freespace; } } } /* * MemoryContextStatsPrint * Print callback used by MemoryContextStatsInternal * * For now, the passthru pointer just points to "int level"; later we might * make that more complicated. */ static void MemoryContextStatsPrint(MemoryContext context, void *passthru, const char *stats_string, bool print_to_stderr) { int level = *(int *) passthru; const char *name = context->name; const char *ident = context->ident; char truncated_ident[110]; int i; /* * It seems preferable to label dynahash contexts with just the hash table * name. Those are already unique enough, so the "dynahash" part isn't * very helpful, and this way is more consistent with pre-v11 practice. */ if (ident && strcmp(name, "dynahash") == 0) { name = ident; ident = NULL; } truncated_ident[0] = '\0'; if (ident) { /* * Some contexts may have very long identifiers (e.g., SQL queries). * Arbitrarily truncate at 100 bytes, but be careful not to break * multibyte characters. Also, replace ASCII control characters, such * as newlines, with spaces. */ int idlen = strlen(ident); bool truncated = false; strcpy(truncated_ident, ": "); i = strlen(truncated_ident); if (idlen > 100) { idlen = pg_mbcliplen(ident, idlen, 100); truncated = true; } while (idlen-- > 0) { unsigned char c = *ident++; if (c < ' ') c = ' '; truncated_ident[i++] = c; } truncated_ident[i] = '\0'; if (truncated) strcat(truncated_ident, "..."); } if (print_to_stderr) { for (i = 0; i < level; i++) fprintf(stderr, " "); fprintf(stderr, "%s: %s%s\n", name, stats_string, truncated_ident); } else ereport(LOG_SERVER_ONLY, (errhidestmt(true), errhidecontext(true), errmsg_internal("level: %d; %s: %s%s", level, name, stats_string, truncated_ident))); } /* * 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. * * Caution: this test is reliable as long as '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. */ bool MemoryContextContains(MemoryContext context, void *pointer) { MemoryContext ptr_context; /* * NB: Can't use GetMemoryChunkContext() here - that performs assertions * that aren't acceptable here since we might be passed memory not * allocated by any memory context. * * 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 context. */ ptr_context = *(MemoryContext *) (((char *) pointer) - sizeof(void *)); return ptr_context == context; } /* * 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 memory context creation procedure goes like this: * 1. Context-type-specific routine makes some initial space allocation, * including enough space for the context header. If it fails, * it can ereport() with no damage done. * 2. Context-type-specific routine sets up all type-specific fields of * the header (those beyond MemoryContextData proper), as well as any * other management fields it needs to have a fully valid context. * Usually, failure in this step is impossible, but if it's possible * the initial space allocation should be freed before ereport'ing. * 3. Context-type-specific routine calls MemoryContextCreate() to fill in * the generic header fields and link the context into the context tree. * 4. 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. * * node: the as-yet-uninitialized common part of the context header node. * tag: NodeTag code identifying the memory context type. * methods: context-type-specific methods (usually statically allocated). * parent: parent context, or NULL if this will be a top-level context. * name: name of context (must be statically allocated). * * Context routines generally assume that MemoryContextCreate can't fail, * so this can contain Assert but not elog/ereport. */ void MemoryContextCreate(MemoryContext node, NodeTag tag, const MemoryContextMethods *methods, MemoryContext parent, const char *name) { /* Creating new memory contexts is not allowed in a critical section */ Assert(CritSectionCount == 0); /* Initialize all standard fields of memory context header */ node->type = tag; node->isReset = true; node->methods = methods; node->parent = parent; node->firstchild = NULL; node->mem_allocated = 0; node->prevchild = NULL; node->name = name; node->ident = NULL; node->reset_cbs = NULL; /* OK to link node into context tree */ if (parent) { node->nextchild = parent->firstchild; if (parent->firstchild != NULL) parent->firstchild->prevchild = node; parent->firstchild = node; /* inherit allowInCritSection flag from parent */ node->allowInCritSection = parent->allowInCritSection; } else { node->nextchild = NULL; node->allowInCritSection = false; } VALGRIND_CREATE_MEMPOOL(node, 0, false); } /* * 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 * MemoryContextAlloc(MemoryContext context, Size size) { void *ret; AssertArg(MemoryContextIsValid(context)); AssertNotInCriticalSection(context); if (!AllocSizeIsValid(size)) elog(ERROR, "invalid memory alloc request size %zu", size); context->isReset = false; ret = context->methods->alloc(context, size); if (unlikely(ret == NULL)) { MemoryContextStats(TopMemoryContext); /* * Here, and elsewhere in this module, we show the target context's * "name" but not its "ident" (if any) in user-visible error messages. * The "ident" string might contain security-sensitive data, such as * values in SQL commands. */ ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"), errdetail("Failed on request of size %zu in memory context \"%s\".", size, context->name))); } VALGRIND_MEMPOOL_ALLOC(context, ret, size); 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 * MemoryContextAllocZero(MemoryContext context, Size size) { void *ret; AssertArg(MemoryContextIsValid(context)); AssertNotInCriticalSection(context); if (!AllocSizeIsValid(size)) elog(ERROR, "invalid memory alloc request size %zu", size); context->isReset = false; ret = context->methods->alloc(context, size); if (unlikely(ret == NULL)) { MemoryContextStats(TopMemoryContext); ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"), errdetail("Failed on request of size %zu in memory context \"%s\".", size, context->name))); } VALGRIND_MEMPOOL_ALLOC(context, ret, size); MemSetAligned(ret, 0, size); 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 * MemoryContextAllocZeroAligned(MemoryContext context, Size size) { void *ret; AssertArg(MemoryContextIsValid(context)); AssertNotInCriticalSection(context); if (!AllocSizeIsValid(size)) elog(ERROR, "invalid memory alloc request size %zu", size); context->isReset = false; ret = context->methods->alloc(context, size); if (unlikely(ret == NULL)) { MemoryContextStats(TopMemoryContext); ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"), errdetail("Failed on request of size %zu in memory context \"%s\".", size, context->name))); } VALGRIND_MEMPOOL_ALLOC(context, ret, size); MemSetLoop(ret, 0, size); return ret; } /* * MemoryContextAllocExtended * Allocate space within the specified context using the given flags. */ void * MemoryContextAllocExtended(MemoryContext context, Size size, int flags) { void *ret; AssertArg(MemoryContextIsValid(context)); AssertNotInCriticalSection(context); if (((flags & MCXT_ALLOC_HUGE) != 0 && !AllocHugeSizeIsValid(size)) || ((flags & MCXT_ALLOC_HUGE) == 0 && !AllocSizeIsValid(size))) elog(ERROR, "invalid memory alloc request size %zu", size); context->isReset = false; ret = context->methods->alloc(context, size); if (unlikely(ret == NULL)) { if ((flags & MCXT_ALLOC_NO_OOM) == 0) { MemoryContextStats(TopMemoryContext); ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"), errdetail("Failed on request of size %zu in memory context \"%s\".", size, context->name))); } return NULL; } VALGRIND_MEMPOOL_ALLOC(context, ret, size); if ((flags & MCXT_ALLOC_ZERO) != 0) MemSetAligned(ret, 0, size); return ret; } /* * HandleLogMemoryContextInterrupt * Handle receipt of an interrupt indicating logging of memory * contexts. * * All the actual work is deferred to ProcessLogMemoryContextInterrupt(), * because we cannot safely emit a log message inside the signal handler. */ void HandleLogMemoryContextInterrupt(void) { InterruptPending = true; LogMemoryContextPending = true; /* latch will be set by procsignal_sigusr1_handler */ } /* * ProcessLogMemoryContextInterrupt * Perform logging of memory contexts of this backend process. * * Any backend that participates in ProcSignal signaling must arrange * to call this function if we see LogMemoryContextPending set. * It is called from CHECK_FOR_INTERRUPTS(), which is enough because * the target process for logging of memory contexts is a backend. */ void ProcessLogMemoryContextInterrupt(void) { LogMemoryContextPending = false; /* * Use LOG_SERVER_ONLY to prevent this message from being sent to the * connected client. */ ereport(LOG_SERVER_ONLY, (errhidestmt(true), errhidecontext(true), errmsg("logging memory contexts of PID %d", MyProcPid))); /* * When a backend process is consuming huge memory, logging all its memory * contexts might overrun available disk space. To prevent this, we limit * the number of child contexts to log per parent to 100. * * As with MemoryContextStats(), we suppose that practical cases where the * dump gets long will typically be huge numbers of siblings under the * same parent context; while the additional debugging value from seeing * details about individual siblings beyond 100 will not be large. */ MemoryContextStatsDetail(TopMemoryContext, 100, false); } void * palloc(Size size) { /* duplicates MemoryContextAlloc to avoid increased overhead */ void *ret; MemoryContext context = CurrentMemoryContext; AssertArg(MemoryContextIsValid(context)); AssertNotInCriticalSection(context); if (!AllocSizeIsValid(size)) elog(ERROR, "invalid memory alloc request size %zu", size); context->isReset = false; ret = context->methods->alloc(context, size); if (unlikely(ret == NULL)) { MemoryContextStats(TopMemoryContext); ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"), errdetail("Failed on request of size %zu in memory context \"%s\".", size, context->name))); } VALGRIND_MEMPOOL_ALLOC(context, ret, size); return ret; } void * palloc0(Size size) { /* duplicates MemoryContextAllocZero to avoid increased overhead */ void *ret; MemoryContext context = CurrentMemoryContext; AssertArg(MemoryContextIsValid(context)); AssertNotInCriticalSection(context); if (!AllocSizeIsValid(size)) elog(ERROR, "invalid memory alloc request size %zu", size); context->isReset = false; ret = context->methods->alloc(context, size); if (unlikely(ret == NULL)) { MemoryContextStats(TopMemoryContext); ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"), errdetail("Failed on request of size %zu in memory context \"%s\".", size, context->name))); } VALGRIND_MEMPOOL_ALLOC(context, ret, size); MemSetAligned(ret, 0, size); return ret; } void * palloc_extended(Size size, int flags) { /* duplicates MemoryContextAllocExtended to avoid increased overhead */ void *ret; MemoryContext context = CurrentMemoryContext; AssertArg(MemoryContextIsValid(context)); AssertNotInCriticalSection(context); if (((flags & MCXT_ALLOC_HUGE) != 0 && !AllocHugeSizeIsValid(size)) || ((flags & MCXT_ALLOC_HUGE) == 0 && !AllocSizeIsValid(size))) elog(ERROR, "invalid memory alloc request size %zu", size); context->isReset = false; ret = context->methods->alloc(context, size); if (unlikely(ret == NULL)) { if ((flags & MCXT_ALLOC_NO_OOM) == 0) { MemoryContextStats(TopMemoryContext); ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"), errdetail("Failed on request of size %zu in memory context \"%s\".", size, context->name))); } return NULL; } VALGRIND_MEMPOOL_ALLOC(context, ret, size); if ((flags & MCXT_ALLOC_ZERO) != 0) MemSetAligned(ret, 0, size); return ret; } /* * pfree * Release an allocated chunk. */ void pfree(void *pointer) { MemoryContext context = GetMemoryChunkContext(pointer); context->methods->free_p(context, pointer); VALGRIND_MEMPOOL_FREE(context, pointer); } /* * repalloc * Adjust the size of a previously allocated chunk. */ void * repalloc(void *pointer, Size size) { MemoryContext context = GetMemoryChunkContext(pointer); void *ret; if (!AllocSizeIsValid(size)) elog(ERROR, "invalid memory alloc request size %zu", size); AssertNotInCriticalSection(context); /* isReset must be false already */ Assert(!context->isReset); ret = context->methods->realloc(context, pointer, size); if (unlikely(ret == NULL)) { MemoryContextStats(TopMemoryContext); ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"), errdetail("Failed on request of size %zu in memory context \"%s\".", size, context->name))); } VALGRIND_MEMPOOL_CHANGE(context, pointer, ret, size); return ret; } /* * MemoryContextAllocHuge * Allocate (possibly-expansive) space within the specified context. * * See considerations in comment at MaxAllocHugeSize. */ void * MemoryContextAllocHuge(MemoryContext context, Size size) { void *ret; AssertArg(MemoryContextIsValid(context)); AssertNotInCriticalSection(context); if (!AllocHugeSizeIsValid(size)) elog(ERROR, "invalid memory alloc request size %zu", size); context->isReset = false; ret = context->methods->alloc(context, size); if (unlikely(ret == NULL)) { MemoryContextStats(TopMemoryContext); ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"), errdetail("Failed on request of size %zu in memory context \"%s\".", size, context->name))); } VALGRIND_MEMPOOL_ALLOC(context, ret, size); return ret; } /* * repalloc_huge * Adjust the size of a previously allocated chunk, permitting a large * value. The previous allocation need not have been "huge". */ void * repalloc_huge(void *pointer, Size size) { MemoryContext context = GetMemoryChunkContext(pointer); void *ret; if (!AllocHugeSizeIsValid(size)) elog(ERROR, "invalid memory alloc request size %zu", size); AssertNotInCriticalSection(context); /* isReset must be false already */ Assert(!context->isReset); ret = context->methods->realloc(context, pointer, size); if (unlikely(ret == NULL)) { MemoryContextStats(TopMemoryContext); ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"), errdetail("Failed on request of size %zu in memory context \"%s\".", size, context->name))); } VALGRIND_MEMPOOL_CHANGE(context, pointer, ret, size); 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; } char * pstrdup(const char *in) { return MemoryContextStrdup(CurrentMemoryContext, in); } /* * pnstrdup * Like pstrdup(), but append null byte to a * not-necessarily-null-terminated input string. */ char * pnstrdup(const char *in, Size len) { char *out; len = strnlen(in, len); out = palloc(len + 1); memcpy(out, in, len); out[len] = '\0'; return out; } /* * Make copy of string with all trailing newline characters removed. */ char * pchomp(const char *in) { size_t n; n = strlen(in); while (n > 0 && in[n - 1] == '\n') n--; return pnstrdup(in, n); }