mirror of
https://github.com/postgres/postgres.git
synced 2025-11-24 00:23:06 +03:00
Add pg_backend_memory_contexts system view.
This view displays the usages of all the memory contexts of the server process attached to the current session. This information is useful to investigate the cause of backend-local memory bloat. This information can be also collected by calling MemoryContextStats(TopMemoryContext) via a debugger. But this technique cannot be uesd in some environments because no debugger is available there. And it outputs lots of text messages and it's not easy to analyze them. So, pg_backend_memory_contexts view allows us to access to backend-local memory contexts information more easily. Bump catalog version. Author: Atsushi Torikoshi, Fujii Masao Reviewed-by: Tatsuhito Kasahara, Andres Freund, Daniel Gustafsson, Robert Haas, Michael Paquier Discussion: https://postgr.es/m/72a656e0f71d0860161e0b3f67e4d771@oss.nttdata.com
This commit is contained in:
@@ -21,8 +21,10 @@
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "funcapi.h"
|
||||
#include "mb/pg_wchar.h"
|
||||
#include "miscadmin.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/memdebug.h"
|
||||
#include "utils/memutils.h"
|
||||
|
||||
@@ -67,6 +69,12 @@ static void MemoryContextStatsPrint(MemoryContext context, void *passthru,
|
||||
#define AssertNotInCriticalSection(context) \
|
||||
Assert(CritSectionCount == 0 || (context)->allowInCritSection)
|
||||
|
||||
/* ----------
|
||||
* The max bytes for showing identifiers of MemoryContext.
|
||||
* ----------
|
||||
*/
|
||||
#define MEMORY_CONTEXT_IDENT_DISPLAY_SIZE 1024
|
||||
|
||||
/*****************************************************************************
|
||||
* EXPORTED ROUTINES *
|
||||
*****************************************************************************/
|
||||
@@ -1220,3 +1228,133 @@ pchomp(const char *in)
|
||||
n--;
|
||||
return pnstrdup(in, n);
|
||||
}
|
||||
|
||||
/*
|
||||
* PutMemoryContextsStatsTupleStore
|
||||
* One recursion level for pg_get_backend_memory_contexts.
|
||||
*/
|
||||
static void
|
||||
PutMemoryContextsStatsTupleStore(Tuplestorestate *tupstore,
|
||||
TupleDesc tupdesc, MemoryContext context,
|
||||
const char *parent, int level)
|
||||
{
|
||||
#define PG_GET_BACKEND_MEMORY_CONTEXTS_COLS 9
|
||||
|
||||
Datum values[PG_GET_BACKEND_MEMORY_CONTEXTS_COLS];
|
||||
bool nulls[PG_GET_BACKEND_MEMORY_CONTEXTS_COLS];
|
||||
MemoryContextCounters stat;
|
||||
MemoryContext child;
|
||||
const char *name;
|
||||
const char *ident;
|
||||
|
||||
AssertArg(MemoryContextIsValid(context));
|
||||
|
||||
name = context->name;
|
||||
ident = context->ident;
|
||||
|
||||
/*
|
||||
* To be consistent with logging output, we label dynahash contexts
|
||||
* with just the hash table name as with MemoryContextStatsPrint().
|
||||
*/
|
||||
if (ident && strcmp(name, "dynahash") == 0)
|
||||
{
|
||||
name = ident;
|
||||
ident = NULL;
|
||||
}
|
||||
|
||||
/* Examine the context itself */
|
||||
memset(&stat, 0, sizeof(stat));
|
||||
(*context->methods->stats) (context, NULL, (void *) &level, &stat);
|
||||
|
||||
memset(values, 0, sizeof(values));
|
||||
memset(nulls, 0, sizeof(nulls));
|
||||
|
||||
if (name)
|
||||
values[0] = CStringGetTextDatum(name);
|
||||
else
|
||||
nulls[0] = true;
|
||||
|
||||
if (ident)
|
||||
{
|
||||
int idlen = strlen(ident);
|
||||
char clipped_ident[MEMORY_CONTEXT_IDENT_DISPLAY_SIZE];
|
||||
|
||||
/*
|
||||
* Some identifiers such as SQL query string can be very long,
|
||||
* truncate oversize identifiers.
|
||||
*/
|
||||
if (idlen >= MEMORY_CONTEXT_IDENT_DISPLAY_SIZE)
|
||||
idlen = pg_mbcliplen(ident, idlen, MEMORY_CONTEXT_IDENT_DISPLAY_SIZE - 1);
|
||||
|
||||
memcpy(clipped_ident, ident, idlen);
|
||||
clipped_ident[idlen] = '\0';
|
||||
values[1] = CStringGetTextDatum(clipped_ident);
|
||||
}
|
||||
else
|
||||
nulls[1] = true;
|
||||
|
||||
if (parent)
|
||||
values[2] = CStringGetTextDatum(parent);
|
||||
else
|
||||
nulls[2] = true;
|
||||
|
||||
values[3] = Int32GetDatum(level);
|
||||
values[4] = Int64GetDatum(stat.totalspace);
|
||||
values[5] = Int64GetDatum(stat.nblocks);
|
||||
values[6] = Int64GetDatum(stat.freespace);
|
||||
values[7] = Int64GetDatum(stat.freechunks);
|
||||
values[8] = Int64GetDatum(stat.totalspace - stat.freespace);
|
||||
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
|
||||
|
||||
for (child = context->firstchild; child != NULL; child = child->nextchild)
|
||||
{
|
||||
PutMemoryContextsStatsTupleStore(tupstore, tupdesc,
|
||||
child, name, level + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* pg_get_backend_memory_contexts
|
||||
* SQL SRF showing backend memory context.
|
||||
*/
|
||||
Datum
|
||||
pg_get_backend_memory_contexts(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
||||
TupleDesc tupdesc;
|
||||
Tuplestorestate *tupstore;
|
||||
MemoryContext per_query_ctx;
|
||||
MemoryContext oldcontext;
|
||||
|
||||
/* check to see if caller supports us returning a tuplestore */
|
||||
if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("set-valued function called in context that cannot accept a set")));
|
||||
if (!(rsinfo->allowedModes & SFRM_Materialize))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("materialize mode required, but it is not allowed in this context")));
|
||||
|
||||
/* Build a tuple descriptor for our result type */
|
||||
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
|
||||
elog(ERROR, "return type must be a row type");
|
||||
|
||||
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
|
||||
oldcontext = MemoryContextSwitchTo(per_query_ctx);
|
||||
|
||||
tupstore = tuplestore_begin_heap(true, false, work_mem);
|
||||
rsinfo->returnMode = SFRM_Materialize;
|
||||
rsinfo->setResult = tupstore;
|
||||
rsinfo->setDesc = tupdesc;
|
||||
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
|
||||
PutMemoryContextsStatsTupleStore(tupstore, tupdesc,
|
||||
TopMemoryContext, NULL, 0);
|
||||
|
||||
/* clean up and return the tuplestore */
|
||||
tuplestore_donestoring(tupstore);
|
||||
|
||||
return (Datum) 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user