mirror of
https://github.com/postgres/postgres.git
synced 2025-05-05 09:19:17 +03:00
Add pg_shmem_allocations view.
This tells you about allocations that have been made from the main shared memory segment. The original patch also tried to show information about dynamic shared memory allocation as well, but I decided to leave that problem for another time. Andres Freund and Robert Haas, reviewed by Michael Paquier, Marti Raudsepp, Tom Lane, Álvaro Herrera, and Kyotaro Horiguchi. Discussion: http://postgr.es/m/20140504114417.GM12715@awork2.anarazel.de
This commit is contained in:
parent
5acf6d8bb4
commit
ed10f32e37
@ -8362,6 +8362,11 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
|
|||||||
<entry>planner statistics</entry>
|
<entry>planner statistics</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><link linkend="view-pg-shmem-allocations"><structname>pg_shmem_allocations</structname></link></entry>
|
||||||
|
<entry>shared memory allocations</entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
<entry><link linkend="view-pg-stats-ext"><structname>pg_stats_ext</structname></link></entry>
|
<entry><link linkend="view-pg-stats-ext"><structname>pg_stats_ext</structname></link></entry>
|
||||||
<entry>extended planner statistics</entry>
|
<entry>extended planner statistics</entry>
|
||||||
@ -10748,6 +10753,86 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
|
|||||||
|
|
||||||
</sect1>
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="view-pg-shmem-allocations">
|
||||||
|
<title><structname>pg_shmem_allocations</structname></title>
|
||||||
|
|
||||||
|
<indexterm zone="view-pg-shmem-allocations">
|
||||||
|
<primary>pg_shmem_allocations</primary>
|
||||||
|
</indexterm>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The <structname>pg_shmem_allocations</structname> view shows allocations
|
||||||
|
made from the server's main shared memory segment. This includes both
|
||||||
|
memory allocated by <productname>postgres</productname> itself and memory
|
||||||
|
allocated by extensions using the mechanisms detailed in
|
||||||
|
<xref linkend="xfunc-shared-addin" />.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Note that this view does not include memory allocated using the dynamic
|
||||||
|
shared memory infrastructure.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<title><structname>pg_shmem_allocations</structname> Columns</title>
|
||||||
|
|
||||||
|
<tgroup cols="3">
|
||||||
|
<thead>
|
||||||
|
<row>
|
||||||
|
<entry>Name</entry>
|
||||||
|
<entry>Type</entry>
|
||||||
|
<entry>Description</entry>
|
||||||
|
</row>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<row>
|
||||||
|
<entry><structfield>name</structfield></entry>
|
||||||
|
<entry><type>text</type></entry>
|
||||||
|
<entry>The name of the shared memory allocation. NULL for unused memory
|
||||||
|
and <literal><anonymous></literal> for anonymous
|
||||||
|
allocations.</entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><structfield>off</structfield></entry>
|
||||||
|
<entry><type>bigint</type></entry>
|
||||||
|
<entry>The offset at which the allocation starts. NULL for anonymous
|
||||||
|
allocations and unused memory.</entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><structfield>size</structfield></entry>
|
||||||
|
<entry><type>bigint</type></entry>
|
||||||
|
<entry>Size of the allocation</entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><structfield>allocated_size</structfield></entry>
|
||||||
|
<entry><type>bigint</type></entry>
|
||||||
|
<entry>Size of the allocation including padding. For anonymous
|
||||||
|
allocations, no information about padding is available, so the
|
||||||
|
<literal>size</literal> and <literal>allocated_size</literal> columns
|
||||||
|
will always be equal. Padding is not meaningful for free memory, so
|
||||||
|
the columns will be equal in that case also.</entry>
|
||||||
|
</row>
|
||||||
|
</tbody>
|
||||||
|
</tgroup>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Anonymous allocations are allocations that have been made
|
||||||
|
with <literal>ShmemAlloc()</literal> directly, rather than via
|
||||||
|
<literal>ShmemInitStruct()</literal> or
|
||||||
|
<literal>ShmemInitHash()</literal>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
By default, the <structname>pg_shmem_allocations</structname> view can be
|
||||||
|
read only by superusers.
|
||||||
|
</para>
|
||||||
|
</sect1>
|
||||||
|
|
||||||
<sect1 id="view-pg-stats">
|
<sect1 id="view-pg-stats">
|
||||||
<title><structname>pg_stats</structname></title>
|
<title><structname>pg_stats</structname></title>
|
||||||
|
|
||||||
|
@ -3239,7 +3239,7 @@ CREATE FUNCTION make_array(anyelement) RETURNS anyarray
|
|||||||
</para>
|
</para>
|
||||||
</sect2>
|
</sect2>
|
||||||
|
|
||||||
<sect2>
|
<sect2 id="xfunc-shared-addin">
|
||||||
<title>Shared Memory and LWLocks</title>
|
<title>Shared Memory and LWLocks</title>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
@ -547,6 +547,12 @@ CREATE VIEW pg_config AS
|
|||||||
REVOKE ALL on pg_config FROM PUBLIC;
|
REVOKE ALL on pg_config FROM PUBLIC;
|
||||||
REVOKE EXECUTE ON FUNCTION pg_config() FROM PUBLIC;
|
REVOKE EXECUTE ON FUNCTION pg_config() FROM PUBLIC;
|
||||||
|
|
||||||
|
CREATE VIEW pg_shmem_allocations AS
|
||||||
|
SELECT * FROM pg_get_shmem_allocations();
|
||||||
|
|
||||||
|
REVOKE ALL ON pg_shmem_allocations FROM PUBLIC;
|
||||||
|
REVOKE EXECUTE ON FUNCTION pg_get_shmem_allocations() FROM PUBLIC;
|
||||||
|
|
||||||
-- Statistics views
|
-- Statistics views
|
||||||
|
|
||||||
CREATE VIEW pg_stat_all_tables AS
|
CREATE VIEW pg_stat_all_tables AS
|
||||||
|
@ -66,12 +66,16 @@
|
|||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
#include "access/transam.h"
|
#include "access/transam.h"
|
||||||
|
#include "fmgr.h"
|
||||||
|
#include "funcapi.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "storage/lwlock.h"
|
#include "storage/lwlock.h"
|
||||||
#include "storage/pg_shmem.h"
|
#include "storage/pg_shmem.h"
|
||||||
#include "storage/shmem.h"
|
#include "storage/shmem.h"
|
||||||
#include "storage/spin.h"
|
#include "storage/spin.h"
|
||||||
|
#include "utils/builtins.h"
|
||||||
|
|
||||||
|
static void *ShmemAllocRaw(Size size, Size *allocated_size);
|
||||||
|
|
||||||
/* shared memory global variables */
|
/* shared memory global variables */
|
||||||
|
|
||||||
@ -157,8 +161,9 @@ void *
|
|||||||
ShmemAlloc(Size size)
|
ShmemAlloc(Size size)
|
||||||
{
|
{
|
||||||
void *newSpace;
|
void *newSpace;
|
||||||
|
Size allocated_size;
|
||||||
|
|
||||||
newSpace = ShmemAllocNoError(size);
|
newSpace = ShmemAllocRaw(size, &allocated_size);
|
||||||
if (!newSpace)
|
if (!newSpace)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_OUT_OF_MEMORY),
|
(errcode(ERRCODE_OUT_OF_MEMORY),
|
||||||
@ -174,6 +179,20 @@ ShmemAlloc(Size size)
|
|||||||
*/
|
*/
|
||||||
void *
|
void *
|
||||||
ShmemAllocNoError(Size size)
|
ShmemAllocNoError(Size size)
|
||||||
|
{
|
||||||
|
Size allocated_size;
|
||||||
|
|
||||||
|
return ShmemAllocRaw(size, &allocated_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ShmemAllocRaw -- allocate align chunk and return allocated size
|
||||||
|
*
|
||||||
|
* Also sets *allocated_size to the number of bytes allocated, which will
|
||||||
|
* be equal to the number requested plus any padding we choose to add.
|
||||||
|
*/
|
||||||
|
static void *
|
||||||
|
ShmemAllocRaw(Size size, Size *allocated_size)
|
||||||
{
|
{
|
||||||
Size newStart;
|
Size newStart;
|
||||||
Size newFree;
|
Size newFree;
|
||||||
@ -191,6 +210,7 @@ ShmemAllocNoError(Size size)
|
|||||||
* won't be sufficient.
|
* won't be sufficient.
|
||||||
*/
|
*/
|
||||||
size = CACHELINEALIGN(size);
|
size = CACHELINEALIGN(size);
|
||||||
|
*allocated_size = size;
|
||||||
|
|
||||||
Assert(ShmemSegHdr != NULL);
|
Assert(ShmemSegHdr != NULL);
|
||||||
|
|
||||||
@ -441,8 +461,10 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Size allocated_size;
|
||||||
|
|
||||||
/* It isn't in the table yet. allocate and initialize it */
|
/* It isn't in the table yet. allocate and initialize it */
|
||||||
structPtr = ShmemAllocNoError(size);
|
structPtr = ShmemAllocRaw(size, &allocated_size);
|
||||||
if (structPtr == NULL)
|
if (structPtr == NULL)
|
||||||
{
|
{
|
||||||
/* out of memory; remove the failed ShmemIndex entry */
|
/* out of memory; remove the failed ShmemIndex entry */
|
||||||
@ -455,6 +477,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
|
|||||||
name, size)));
|
name, size)));
|
||||||
}
|
}
|
||||||
result->size = size;
|
result->size = size;
|
||||||
|
result->allocated_size = allocated_size;
|
||||||
result->location = structPtr;
|
result->location = structPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -503,3 +526,82 @@ mul_size(Size s1, Size s2)
|
|||||||
errmsg("requested shared memory size overflows size_t")));
|
errmsg("requested shared memory size overflows size_t")));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* SQL SRF showing allocated shared memory */
|
||||||
|
Datum
|
||||||
|
pg_get_shmem_allocations(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
#define PG_GET_SHMEM_SIZES_COLS 4
|
||||||
|
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
||||||
|
TupleDesc tupdesc;
|
||||||
|
Tuplestorestate *tupstore;
|
||||||
|
MemoryContext per_query_ctx;
|
||||||
|
MemoryContext oldcontext;
|
||||||
|
HASH_SEQ_STATUS hstat;
|
||||||
|
ShmemIndexEnt *ent;
|
||||||
|
Size named_allocated = 0;
|
||||||
|
Datum values[PG_GET_SHMEM_SIZES_COLS];
|
||||||
|
bool nulls[PG_GET_SHMEM_SIZES_COLS];
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
LWLockAcquire(ShmemIndexLock, LW_SHARED);
|
||||||
|
|
||||||
|
hash_seq_init(&hstat, ShmemIndex);
|
||||||
|
|
||||||
|
/* output all allocated entries */
|
||||||
|
memset(nulls, 0, sizeof(nulls));
|
||||||
|
while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
|
||||||
|
{
|
||||||
|
values[0] = CStringGetTextDatum(ent->key);
|
||||||
|
values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
|
||||||
|
values[2] = Int64GetDatum(ent->size);
|
||||||
|
values[3] = Int64GetDatum(ent->allocated_size);
|
||||||
|
named_allocated += ent->allocated_size;
|
||||||
|
|
||||||
|
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* output shared memory allocated but not counted via the shmem index */
|
||||||
|
values[0] = CStringGetTextDatum("<anonymous>");
|
||||||
|
nulls[1] = true;
|
||||||
|
values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
|
||||||
|
values[3] = values[2];
|
||||||
|
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
|
||||||
|
|
||||||
|
/* output as-of-yet unused shared memory */
|
||||||
|
nulls[0] = true;
|
||||||
|
values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
|
||||||
|
nulls[1] = false;
|
||||||
|
values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
|
||||||
|
values[3] = values[2];
|
||||||
|
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
|
||||||
|
|
||||||
|
LWLockRelease(ShmemIndexLock);
|
||||||
|
|
||||||
|
tuplestore_donestoring(tupstore);
|
||||||
|
|
||||||
|
return (Datum) 0;
|
||||||
|
}
|
||||||
|
@ -7663,6 +7663,15 @@
|
|||||||
proparallel => 'r', prorettype => 'float8', proargtypes => '',
|
proparallel => 'r', prorettype => 'float8', proargtypes => '',
|
||||||
prosrc => 'pg_notification_queue_usage' },
|
prosrc => 'pg_notification_queue_usage' },
|
||||||
|
|
||||||
|
# shared memory usage
|
||||||
|
{ oid => '8613',
|
||||||
|
descr => 'allocations from the main shared memory segment',
|
||||||
|
proname => 'pg_get_shmem_allocations', 'prorows' => 50, 'proretset' => 't',
|
||||||
|
provolatile => 'v', 'prorettype' => 'record', 'proargtypes' => '',
|
||||||
|
proallargtypes => '{text,int8,int8,int8}', proargmodes => '{o,o,o,o}',
|
||||||
|
proargnames => '{name,off,size,allocated_size}',
|
||||||
|
prosrc => 'pg_get_shmem_allocations' },
|
||||||
|
|
||||||
# non-persistent series generator
|
# non-persistent series generator
|
||||||
{ oid => '1066', descr => 'non-persistent series generator',
|
{ oid => '1066', descr => 'non-persistent series generator',
|
||||||
proname => 'generate_series', prorows => '1000',
|
proname => 'generate_series', prorows => '1000',
|
||||||
|
@ -59,7 +59,8 @@ typedef struct
|
|||||||
{
|
{
|
||||||
char key[SHMEM_INDEX_KEYSIZE]; /* string name */
|
char key[SHMEM_INDEX_KEYSIZE]; /* string name */
|
||||||
void *location; /* location in shared mem */
|
void *location; /* location in shared mem */
|
||||||
Size size; /* # bytes allocated for the structure */
|
Size size; /* # bytes requested for the structure */
|
||||||
|
Size allocated_size; /* # bytes actually allocated */
|
||||||
} ShmemIndexEnt;
|
} ShmemIndexEnt;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1721,6 +1721,11 @@ pg_shadow| SELECT pg_authid.rolname AS usename,
|
|||||||
FROM (pg_authid
|
FROM (pg_authid
|
||||||
LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid))))
|
LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid))))
|
||||||
WHERE pg_authid.rolcanlogin;
|
WHERE pg_authid.rolcanlogin;
|
||||||
|
pg_shmem_allocations| SELECT pg_get_shmem_allocations.name,
|
||||||
|
pg_get_shmem_allocations.off,
|
||||||
|
pg_get_shmem_allocations.size,
|
||||||
|
pg_get_shmem_allocations.allocated_size
|
||||||
|
FROM pg_get_shmem_allocations() pg_get_shmem_allocations(name, off, size, allocated_size);
|
||||||
pg_stat_activity| SELECT s.datid,
|
pg_stat_activity| SELECT s.datid,
|
||||||
d.datname,
|
d.datname,
|
||||||
s.pid,
|
s.pid,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user