From: torikoshia <torikoshia@oss.nttdata.com>
To: Kasahara Tatsuhito <kasahara.tatsuhito@gmail.com>
Cc: Tom Lane <tgl@sss.pgh.pa.us>
Cc: Fujii Masao <masao.fujii@oss.nttdata.com>
Cc: Georgios Kokolatos <gkokolatos@protonmail.com>
Cc: PostgreSQL-development <pgsql-hackers@postgresql.org>
Subject: Re: Get memory contexts of an arbitrary backend process
Date: Mon, 04 Jan 2021 14:34:50 +0900
Message-ID: <9a59401241e9269254e70c8e0ef1cc7a@oss.nttdata.com> (raw)
In-Reply-To: <CAP0=ZV+XcPnX+m9ROG07hs=DqwQCOLAojgvYCRogw=bFAv1+Eg@mail.gmail.com>
References: <0271f440ac77f2a4180e0e56ebd944d1@oss.nttdata.com>
<270ec80dbbebfb5c6f2b9fdd7a7858b5@oss.nttdata.com>
<160502083340.7362.4386116585349115146.pgcf@coridan.postgresql.org>
<064d510f2d33edc37dfd6b8f3b6bd30e@oss.nttdata.com>
<d42d3186-d647-9af4-7c1c-d6ffca8bff40@oss.nttdata.com>
<1685539.1606959410@sss.pgh.pa.us>
<83ff0aff04fffc95cbd1fc1637a0fccc@oss.nttdata.com>
<28e1feb71b94ac9918830bc4a0c1844f@oss.nttdata.com>
<CAP0=ZV+XcPnX+m9ROG07hs=DqwQCOLAojgvYCRogw=bFAv1+Eg@mail.gmail.com>
On Fri, Dec 25, 2020 at 6:08 PM Kasahara Tatsuhito
<kasahara.tatsuhito@gmail.com> wrote:
Thanks for reviewing and kind suggestion!
>> Attached a rewritten patch.
> Thanks for updating patch.
>
> But when I had applyed the patch to the current HEAD and did make, I
> got an error due to duplicate OIDs.
> You need to rebase the patch.
Assigned another OID.
>> Accordingly, I also slightly modified the basic design as below.
>>
>> ---
>> # Communication flow between the dumper and the requester
>> - (1) When requesting memory context dumping, the dumper changes
>> the struct on the shared memory state from 'ACCEPTABLE' to
>> 'REQUESTING'.
>> - (2) The dumper sends the signal to the dumper process and wait on
>> the latch.
>> - (3) When the dumper noticed the signal, it changes the state to
>> 'DUMPING'.
>> - (4) When the dumper completes dumping, it changes the state to
>> 'DONE' and set the latch.
>> - (5) The requestor reads the dump file and shows it to the user.
>> Finally, the requestor removes the dump file and reset the shared
>> memory state to 'ACCEPTABLE'.
>>
>> # Query cancellation
>> - When the requestor cancels dumping, e.g. signaling using ctrl-C,
>> the requestor changes the state of the shared memory to 'CANCELING'.
>> - The dumper checks the state when it tries to change the state to
>> 'DONE' at (4), and if the state is 'CANCELING', it initializes the
>> dump file and reset the shared memory state to 'ACCEPTABLE'.
>>
>> # Cleanup dump file and the shared memory
>> - In the normal case, the dumper removes the dump file and resets
>> the shared memory entry as described in (5).
>> - When something like query cancellation or process termination
>> happens on the dumper after (1) and before (3), in other words,
>> the state is 'REQUESTING', the requestor does the cleanup.
>> - When something happens on the dumper or the requestor after (3)
>> and before (4), in other words, the state is 'DUMPING', the dumper
>> does the cleanup. Specifically, if the requestor cancels the query,
>> it just changes the state to 'CANCELING' and the dumper notices it
>> and cleans up things later.
>> OTOH, when the dumper fails to dump, it cleans up the dump file and
>> reset the shared memory state.
>> - When something happens on the requestor after (4), i.e., the state
>> is 'DONE', the requestor does the cleanup.
>> - In the case of receiving SIGKILL or power failure, all dump files
>> are removed in the crash recovery process.
>> ---
> If the dumper is terminated before it dumps, the requestor will appear
> to enter an
> infinite loop because the status of mcxtdumpShmem will not change.
> The following are the steps to reproduce.
>
> - session1
> BEGIN; LOCK TABLE t;
> - session2
> SELECT * FROM t; -- wait
> - session3
> select pg_get_target_backend_memory_contexts(<pid of session2>);
> -- wait
> - session1
> select pg_terminate_backend(<pid of session2>); -- kill session2
> - session3 waits forever.
>
> Therefore, you may need to set mcxtdumpShmem->dump_status to
> MCXTDUMPSTATUS_CANCELING
> or other status before the dumper terminates.
In this case, it may be difficult for the dumper to change dump_status
because
it's waiting for latch and dump_memory_contexts() is not called yet.
Instead, it's possible for the requestor to check the existence of the
dumper
process periodically during waiting.
I added this logic to the attached patch.
> Also, although I have not been able to reproduce it, I believe that
> with the current design,
> if the requestor disappears right after the dumper dumps the memory
> information,
> the dump file will remain.
> Since the current design appears to allow only one requestor per
> instance, when the requestor
> requests a dump, it might be a good idea to delete any remaining dump
> files, if any.
Although I'm not sure when the dump file remains, deleting any remaining
dump
files seems good for safety.
I also added this idea to the attached patch.
> The following are comments on the code.
>
> + proc = BackendPidGetProc(dst_pid);
> +
> + if (proc == NULL)
> + {
> + ereport(WARNING,
> + (errmsg("PID %d is not a PostgreSQL server process",
> dst_pid)));
> +
> + return (Datum) 1;
> + }
> For now, background writer, checkpointer and WAL writer are belong to
> the auxiliary process.
> Therefore, if we specify the PIDs of these processes for
> pg_get_target_backend_memory_contexts(),
> "PID xxxx is not a PostgreSQL server process" whould be outoput.
> This confuses the user.
> How about use AuxiliaryPidGetProc() to determine these processes?
Thanks and I modified the patch to output the below message when it's an
auxiliary process.
| PID %d is not a PostgreSQL backend process but an auxiliary process.
>
> + ereport(INFO,
> + (errmsg("The request has failed and now PID %d is
> requsting dumping.",
> + mcxtdumpShmem->src_pid)));
> +
> + LWLockRelease(McxtDumpLock);
> You can release LWLock before ereport.
Modified to release the lock before ereport.
> + Assert(mcxtdumpShmem->dump_status = MCXTDUMPSTATUS_REQUESTING);
> typo?
> It might be "mcxtdumpShmem->dump_status == MCXTDUMPSTATUS_REQUESTING".
Ops, it's a serious typo. Fixed it.
> + ereport(INFO,
> + (errmsg("PID %d is looked up by another process",
> pid)));
> This message is not accurate.
> Because, in the current design, only one session can request a dump,
> so if there are multiple requests, this message will be output even if
> the
> request is for a PID that is not looked up.
Exactly. Modified the message as below.
| Only one session can dump at a time and another session is dumping
currently.
> + if (fpout == NULL)
> + {
> + ereport(LOG,
> + (errcode_for_file_access(),
> + errmsg("could not write memory context file \"%s\":
> %m",
> + dumpfile)));
> It would be better to use "dump file" instead of "memory context file".
>
> +static void
> +McxtReqKill(int code, Datum arg)
> The function McxtReqKill looks like it should have a different name.
> (Since it doesn't do any kill).
> How about McxtReqcCleanup or something similar?
Thanks for your idea and I changed the name to McxtReqCleanup.
Regards,
Attachments:
[text/x-diff] v6-0001-Add-pg_get_target_backend_memory_contexts-to-coll.patch (33.6K, ../9a59401241e9269254e70c8e0ef1cc7a@oss.nttdata.com/2-v6-0001-Add-pg_get_target_backend_memory_contexts-to-coll.patch)
download | inline diff:
From d7d99a5278c533ceae465b673b1d69f6ce51ee89 Mon Sep 17 00:00:00 2001
From: Atsushi Torikoshi <torikoshia@oss.nttdata.com>
Date: Mon, 4 Jan 2021 12:43:18 +0900
Subject: [PATCH v6] After commit 3e98c0bafb28de, we can display the usage of
the memory contexts using pg_backend_memory_contexts system view. However,
its target is limited to the process attached to the current session. This
patch introduces pg_get_target_backend_memory_contexts() and makes it
possible to collect memory contexts of the specified process.
---
src/backend/access/transam/xlog.c | 7 +
src/backend/catalog/system_views.sql | 3 +-
src/backend/postmaster/pgstat.c | 3 +
src/backend/replication/basebackup.c | 3 +
src/backend/storage/ipc/ipci.c | 2 +
src/backend/storage/ipc/procsignal.c | 4 +
src/backend/storage/lmgr/lwlocknames.txt | 1 +
src/backend/tcop/postgres.c | 5 +
src/backend/utils/adt/mcxtfuncs.c | 730 ++++++++++++++++++-
src/backend/utils/init/globals.c | 1 +
src/bin/initdb/initdb.c | 3 +-
src/bin/pg_basebackup/t/010_pg_basebackup.pl | 4 +-
src/bin/pg_rewind/filemap.c | 3 +
src/include/catalog/pg_proc.dat | 12 +-
src/include/miscadmin.h | 1 +
src/include/pgstat.h | 3 +-
src/include/storage/procsignal.h | 1 +
src/include/utils/mcxtfuncs.h | 44 ++
18 files changed, 809 insertions(+), 21 deletions(-)
create mode 100644 src/include/utils/mcxtfuncs.h
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.cindex ede93ad7fd..4cab47a61d 100644--- a/src/backend/access/transam/xlog.c+++ b/src/backend/access/transam/xlog.c@@ -73,6 +73,7 @@
#include "storage/sync.h"
#include "utils/builtins.h"
#include "utils/guc.h"
+#include "utils/mcxtfuncs.h"
#include "utils/memutils.h"
#include "utils/ps_status.h"
#include "utils/relmapper.h"
@@ -6993,6 +6994,12 @@ StartupXLOG(void)
*/
pgstat_reset_all();
+ /*+ * Reset dump files in pg_memusage, because target processes do+ * not exist any more.+ */+ RemoveMemcxtFile(0);+
/*
* If there was a backup label file, it's done its job and the info
* has now been propagated into pg_control. We must get rid of the
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sqlindex ab4603c69b..817669cd4f 100644--- a/src/backend/catalog/system_views.sql+++ b/src/backend/catalog/system_views.sql@@ -558,7 +558,8 @@ CREATE VIEW pg_backend_memory_contexts AS
SELECT * FROM pg_get_backend_memory_contexts();
REVOKE ALL ON pg_backend_memory_contexts FROM PUBLIC;
-REVOKE EXECUTE ON FUNCTION pg_get_backend_memory_contexts() FROM PUBLIC;+REVOKE EXECUTE ON FUNCTION pg_get_backend_memory_contexts FROM PUBLIC;+REVOKE EXECUTE ON FUNCTION pg_get_target_backend_memory_contexts FROM PUBLIC;
-- Statistics views
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.cindex 3f24a33ef1..8eb2d062b0 100644--- a/src/backend/postmaster/pgstat.c+++ b/src/backend/postmaster/pgstat.c@@ -4045,6 +4045,9 @@ pgstat_get_wait_ipc(WaitEventIPC w)
case WAIT_EVENT_XACT_GROUP_UPDATE:
event_name = "XactGroupUpdate";
break;
+ case WAIT_EVENT_DUMP_MEMORY_CONTEXT:+ event_name = "DumpMemoryContext";+ break;
/* no default case, so that compiler will warn */
}
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.cindex 0f54635550..c67e71d79b 100644--- a/src/backend/replication/basebackup.c+++ b/src/backend/replication/basebackup.c@@ -184,6 +184,9 @@ static const char *const excludeDirContents[] =
/* Contents zeroed on startup, see StartupSUBTRANS(). */
"pg_subtrans",
+ /* Skip memory context dump files. */+ "pg_memusage",+
/* end of list */
NULL
};
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.cindex f9bbe97b50..18a1dd5a74 100644--- a/src/backend/storage/ipc/ipci.c+++ b/src/backend/storage/ipc/ipci.c@@ -45,6 +45,7 @@
#include "storage/procsignal.h"
#include "storage/sinvaladt.h"
#include "storage/spin.h"
+#include "utils/mcxtfuncs.h"
#include "utils/snapmgr.h"
/* GUCs */
@@ -267,6 +268,7 @@ CreateSharedMemoryAndSemaphores(void)
BTreeShmemInit();
SyncScanShmemInit();
AsyncShmemInit();
+ McxtDumpShmemInit();
#ifdef EXEC_BACKEND
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.cindex 583efaecff..106e125cc2 100644--- a/src/backend/storage/ipc/procsignal.c+++ b/src/backend/storage/ipc/procsignal.c@@ -28,6 +28,7 @@
#include "storage/shmem.h"
#include "storage/sinval.h"
#include "tcop/tcopprot.h"
+#include "utils/mcxtfuncs.h"
/*
* The SIGUSR1 signal is multiplexed to support signaling multiple event
@@ -567,6 +568,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
if (CheckProcSignal(PROCSIG_BARRIER))
HandleProcSignalBarrierInterrupt();
+ if (CheckProcSignal(PROCSIG_DUMP_MEMCXT))+ HandleProcSignalDumpMemoryContext();+
if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
diff --git a/src/backend/storage/lmgr/lwlocknames.txt b/src/backend/storage/lmgr/lwlocknames.txtindex 774292fd94..6b4ff6f08b 100644--- a/src/backend/storage/lmgr/lwlocknames.txt+++ b/src/backend/storage/lmgr/lwlocknames.txt@@ -53,3 +53,4 @@ XactTruncationLock 44
# 45 was XactTruncationLock until removal of BackendRandomLock
WrapLimitsVacuumLock 46
NotifyQueueTailLock 47
+McxtDumpLock 48diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.cindex dfa0d685a8..e07ecc43f7 100644--- a/src/backend/tcop/postgres.c+++ b/src/backend/tcop/postgres.c@@ -76,6 +76,7 @@
#include "tcop/tcopprot.h"
#include "tcop/utility.h"
#include "utils/lsyscache.h"
+#include "utils/mcxtfuncs.h"
#include "utils/memutils.h"
#include "utils/ps_status.h"
#include "utils/snapmgr.h"
@@ -540,6 +541,10 @@ ProcessClientReadInterrupt(bool blocked)
/* Process notify interrupts, if any */
if (notifyInterruptPending)
ProcessNotifyInterrupt();
++ /* Process memory contexts dump interrupts, if any */+ if (ProcSignalDumpMemoryContextPending)+ ProcessDumpMemoryContextInterrupt();
}
else if (ProcDiePending)
{
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.cindex c02fa47550..c52ae2aea2 100644--- a/src/backend/utils/adt/mcxtfuncs.c+++ b/src/backend/utils/adt/mcxtfuncs.c@@ -15,30 +15,100 @@
#include "postgres.h"
+#include <sys/stat.h>+#include <unistd.h>++#include "common/logging.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "mb/pg_wchar.h"
+#include "pgstat.h"+#include "storage/ipc.h"+#include "storage/latch.h"+#include "storage/proc.h"+#include "storage/procarray.h"+#include "storage/procsignal.h"+#include "storage/shmem.h"
#include "utils/builtins.h"
+#include "utils/mcxtfuncs.h"++/* The max bytes for showing names and identifiers of MemoryContext. */+#define MEMORY_CONTEXT_DISPLAY_SIZE 1024++/* Number of columns in pg_backend_memory_contexts view */+#define PG_GET_BACKEND_MEMORY_CONTEXTS_COLS 9++/* Shared memory struct for managing the status of memory context dump. */+static mcxtdumpShmemStruct *mcxtdumpShmem = NULL;-/* ----------- * The max bytes for showing identifiers of MemoryContext.- * ----------+/*+ * McxtReqCleanup+ * Error cleanup callback for memory context requestor.
*/
-#define MEMORY_CONTEXT_IDENT_DISPLAY_SIZE 1024+static void+McxtReqCleanup(int code, Datum arg)+{+ int dst_pid = DatumGetInt32(arg);++ LWLockAcquire(McxtDumpLock, LW_EXCLUSIVE);++ if (mcxtdumpShmem->src_pid != MyProcPid)+ {+ /*+ * If the requestor is not me, simply exit.+ */+ LWLockRelease(McxtDumpLock);+ return;+ }++ if (mcxtdumpShmem->dump_status == MCXTDUMPSTATUS_REQUESTING)+ {+ /*+ * Since the dumper has not received the dump order yet, clean up+ * things by myself.+ */+ mcxtdumpShmem->dst_pid = 0;+ mcxtdumpShmem->src_pid = 0;+ mcxtdumpShmem->dump_status = MCXTDUMPSTATUS_ACCEPTABLE;+ }+ else if (mcxtdumpShmem->dump_status == MCXTDUMPSTATUS_DUMPING)+ {+ /*+ * Since the dumper has received the request already,+ * requestor just change the status and the dumper cleans up+ * things later.+ */+ mcxtdumpShmem->dump_status = MCXTDUMPSTATUS_CANCELING;+ }+ else if (mcxtdumpShmem->dump_status == MCXTDUMPSTATUS_DONE)+ {+ /*+ * Since the dumper has already finished dumping, clean up things+ * by myself.+ */+ mcxtdumpShmem->dst_pid = 0;+ mcxtdumpShmem->src_pid = 0;+ mcxtdumpShmem->dump_status = MCXTDUMPSTATUS_ACCEPTABLE;++ RemoveMemcxtFile(dst_pid);+ }+ LWLockRelease(McxtDumpLock);+}
/*
* PutMemoryContextsStatsTupleStore
* One recursion level for pg_get_backend_memory_contexts.
+ *+ * Note: When fpout is not NULL, ferror() check must be done by the caller.
*/
static void
PutMemoryContextsStatsTupleStore(Tuplestorestate *tupstore,
TupleDesc tupdesc, MemoryContext context,
- const char *parent, int level)+ const char *parent, int level, FILE *fpout)
{
-#define PG_GET_BACKEND_MEMORY_CONTEXTS_COLS 9-
Datum values[PG_GET_BACKEND_MEMORY_CONTEXTS_COLS];
bool nulls[PG_GET_BACKEND_MEMORY_CONTEXTS_COLS];
+ char clipped_ident[MEMORY_CONTEXT_DISPLAY_SIZE];
MemoryContextCounters stat;
MemoryContext child;
const char *name;
@@ -74,14 +144,12 @@ PutMemoryContextsStatsTupleStore(Tuplestorestate *tupstore,
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);+ if (idlen >= MEMORY_CONTEXT_DISPLAY_SIZE)+ idlen = pg_mbcliplen(ident, idlen, MEMORY_CONTEXT_DISPLAY_SIZE - 1);
memcpy(clipped_ident, ident, idlen);
clipped_ident[idlen] = '\0';
@@ -101,18 +169,207 @@ PutMemoryContextsStatsTupleStore(Tuplestorestate *tupstore,
values[6] = Int64GetDatum(stat.freespace);
values[7] = Int64GetDatum(stat.freechunks);
values[8] = Int64GetDatum(stat.totalspace - stat.freespace);
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);++ /*+ * Since pg_get_backend_memory_contexts() is called from local process,+ * simply put tuples.+ */+ if(fpout == NULL)+ tuplestore_putvalues(tupstore, tupdesc, values, nulls);++ /*+ * Write out the current memory context information in the form of+ * "key: value" pairs to the file specified by the requestor.+ */+ else+ {+ /*+ * Make each memory context information starts with 'D'.+ * This is checked by the requestor when reading the file.+ */+ fputc('D', fpout);++ fprintf(fpout,+ "name: %s, ident: %s, parent: %s, level: %d, total_bytes: %lu, \+ total_nblocks: %lu, free_bytes: %lu, free_chunks: %lu, used_bytes: %lu,\n",+ name,+ ident ? clipped_ident : "none",+ parent ? parent : "none", level,+ stat.totalspace,+ stat.nblocks,+ stat.freespace,+ stat.freechunks,+ stat.totalspace - stat.freespace);+ }
for (child = context->firstchild; child != NULL; child = child->nextchild)
{
PutMemoryContextsStatsTupleStore(tupstore, tupdesc,
- child, name, level + 1);+ child, name, level + 1, fpout);+ }+}++/*+ * SetupMcxtdumpShem+ * Setup shared memory struct for dumping specified PID.+ */+static void+SetupMcxtdumpShmem(int pid)+{+ /*+ * We only allow one session per target process to request memory+ * contexts dump at a time.+ * If someone uses mcxtdumpShmem, wait until it was changed to acceptable.+ */+ while (true)+ {+ LWLockAcquire(McxtDumpLock, LW_EXCLUSIVE);++ if (mcxtdumpShmem->dump_status == MCXTDUMPSTATUS_ACCEPTABLE)+ {+ mcxtdumpShmem->dump_status = MCXTDUMPSTATUS_REQUESTING;+ mcxtdumpShmem->src_pid = MyProcPid;+ mcxtdumpShmem->dst_pid = pid;++ /*+ * Dump files should not exist now, but delete any of+ * them just in case.+ *+ * Note: This is possible because only one session can+ * request memory contexts per instance.+ */+ RemoveMemcxtFile(0);++ LWLockRelease(McxtDumpLock);++ return;+ }+ else+ {+ ereport(INFO,+ (errmsg("Only one session can dump at a time and another session is dumping currently.")));++ LWLockRelease(McxtDumpLock);++ pg_usleep(5000000L);+ }+ }+}++/*+ * PutDumpedValuesOnTuplestore+ * Read specified memory context dump file and put its values+ * on the tuplestore.+ */+static void+PutDumpedValuesOnTuplestore(char *dumpfile, Tuplestorestate *tupstore,+ TupleDesc tupdesc, int pid)+{+ FILE *fpin;+ int format_id;++ if ((fpin = AllocateFile(dumpfile, "r")) == NULL)+ {+ if (errno != ENOENT)+ ereport(LOG, (errcode_for_file_access(),+ errmsg("could not open memory context dump file \"%s\": %m",+ dumpfile)));
}
++ /* Verify it's of the expected format. */+ if (fread(&format_id, 1, sizeof(format_id), fpin) != sizeof(format_id) ||+ format_id != PG_MCXT_FILE_FORMAT_ID)+ {+ ereport(WARNING,+ (errmsg("corrupted memory context dump file \"%s\"", dumpfile)));+ goto done;+ }++ /* Read dump file and put values on tuple store. */+ while (true)+ {+ Datum values[PG_GET_BACKEND_MEMORY_CONTEXTS_COLS];+ bool nulls[PG_GET_BACKEND_MEMORY_CONTEXTS_COLS];+ char name[MEMORY_CONTEXT_DISPLAY_SIZE];+ char parent[MEMORY_CONTEXT_DISPLAY_SIZE];+ char clipped_ident[MEMORY_CONTEXT_DISPLAY_SIZE];+ int level;+ Size total_bytes;+ Size total_nblocks;+ Size free_bytes;+ Size free_chunks;+ Size used_bytes;++ memset(values, 0, sizeof(values));+ memset(nulls, 0, sizeof(nulls));++ switch (fgetc(fpin))+ {+ /* 'D' A memory context information follows */+ case 'D':+ if (fscanf(fpin, "name: %1023[^,], ident: %1023[^,], parent: %1023[^,], \+ level: %d, total_bytes: %lu, total_nblocks: %lu, \+ free_bytes: %lu, free_chunks: %lu, used_bytes: %lu,\n",+ name, clipped_ident, parent, &level, &total_bytes, &total_nblocks,+ &free_bytes, &free_chunks, &used_bytes)+ != PG_GET_BACKEND_MEMORY_CONTEXTS_COLS)+ {+ ereport(WARNING,+ (errmsg("corrupted memory context dump file \"%s\"",+ dumpfile)));+ goto done;+ }++ values[0] = CStringGetTextDatum(name);++ if (strcmp(clipped_ident, "none"))+ values[1] = CStringGetTextDatum(clipped_ident);+ else+ nulls[1] = true;++ if (strcmp(parent, "none"))+ values[2] = CStringGetTextDatum(parent);+ else+ nulls[2] = true;++ values[3] = Int32GetDatum(level);+ values[4] = Int64GetDatum(total_bytes);+ values[5] = Int64GetDatum(total_nblocks);+ values[6] = Int64GetDatum(free_bytes);+ values[7] = Int64GetDatum(free_chunks);+ values[8] = Int64GetDatum(used_bytes);++ tuplestore_putvalues(tupstore, tupdesc, values, nulls);+ break;++ case 'E':+ goto done;++ default:+ ereport(WARNING,+ (errmsg("corrupted memory context dump file \"%s\"",+ dumpfile)));+ goto done;+ }+ }+done:+ FreeFile(fpin);+ unlink(dumpfile);++ LWLockAcquire(McxtDumpLock, LW_EXCLUSIVE);++ Assert(mcxtdumpShmem->src_pid == MyProcPid);++ mcxtdumpShmem->dst_pid = 0;+ mcxtdumpShmem->src_pid = 0;+ mcxtdumpShmem->dump_status = MCXTDUMPSTATUS_ACCEPTABLE;++ LWLockRelease(McxtDumpLock);
}
/*
* pg_get_backend_memory_contexts
- * SQL SRF showing backend memory context.+ * SQL SRF showing local backend memory context.
*/
Datum
pg_get_backend_memory_contexts(PG_FUNCTION_ARGS)
@@ -148,10 +405,453 @@ pg_get_backend_memory_contexts(PG_FUNCTION_ARGS)
MemoryContextSwitchTo(oldcontext);
PutMemoryContextsStatsTupleStore(tupstore, tupdesc,
- TopMemoryContext, NULL, 0);+ TopMemoryContext, "", 0, NULL);++ /* clean up and return the tuplestore */+ tuplestore_donestoring(tupstore);++ return (Datum) 0;+}++/*+ * pg_get_target_backend_memory_contexts+ * SQL SRF showing specified process's backend memory context.+ */+Datum+pg_get_target_backend_memory_contexts(PG_FUNCTION_ARGS)+{+ int dst_pid = PG_GETARG_INT32(0);++ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;+ TupleDesc tupdesc;+ Tuplestorestate *tupstore;+ MemoryContext per_query_ctx;+ MemoryContext oldcontext;+ char dumpfile[MAXPGPATH];+ PGPROC *proc;+ PgBackendStatus *beentry;++ if (dst_pid == MyProcPid)+ {+ pg_get_backend_memory_contexts(fcinfo);+ return (Datum) 0;+ }++ snprintf(dumpfile, sizeof(dumpfile), "%s/%d", PG_MEMUSAGE_DIR, dst_pid);++ /* 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);++ /*+ * Check whether the target process is PostgreSQL backend process.+ */+ proc = BackendPidGetProc(dst_pid);++ if (proc == NULL)+ {+ proc = AuxiliaryPidGetProc(dst_pid);++ if (proc == NULL)+ ereport(WARNING,+ (errmsg("PID %d is not a PostgreSQL server process.", dst_pid)));+ else+ ereport(WARNING,+ (errmsg("PID %d is not a PostgreSQL backend process but an auxiliary process.", dst_pid)));++ tuplestore_donestoring(tupstore);+ return (Datum) 1;+ }++ beentry = pgstat_fetch_stat_beentry(proc->backendId);++ if (beentry->st_backendType != B_BACKEND)+ {+ ereport(WARNING,+ (errmsg("PID %d is not a PostgreSQL backend process", dst_pid)));++ tuplestore_donestoring(tupstore);+ return (Datum) 1;+ }++ /*+ * The ENSURE stuff ensures we clean up the shared memory struct and files+ * on failure.+ */+ PG_ENSURE_ERROR_CLEANUP(McxtReqCleanup, (Datum) Int32GetDatum(dst_pid));+ {+ SetupMcxtdumpShmem(dst_pid);++ SendProcSignal(dst_pid, PROCSIG_DUMP_MEMCXT, InvalidBackendId);++ /* Wait until target process finishes dumping file. */+ for (;;)+ {+ /* Check for dump cancel request. */+ CHECK_FOR_INTERRUPTS();++ /* Must reset the latch before testing state. */+ ResetLatch(MyLatch);++ LWLockAcquire(McxtDumpLock, LW_SHARED);++ if (mcxtdumpShmem->src_pid != MyProcPid)+ {+ /*+ * It seems the dumper exited and subsequently another+ * process is requesting dumping.+ */+ LWLockRelease(McxtDumpLock);++ ereport(INFO,+ (errmsg("The request has failed and now PID %d is requsting dumping.",+ mcxtdumpShmem->src_pid)));++ tuplestore_donestoring(tupstore);++ return (Datum) 0;+ }+ else if (mcxtdumpShmem->dump_status == MCXTDUMPSTATUS_ACCEPTABLE)+ {+ /*+ * Dumper seems to have cleaned up things already because+ * of failures or cancellation.+ * Since the dumper has already removed the dump file,+ * simply exit.+ */+ LWLockRelease(McxtDumpLock);+ tuplestore_donestoring(tupstore);++ return (Datum) 0;+ }+ else if (mcxtdumpShmem->dump_status == MCXTDUMPSTATUS_CANCELING)+ {+ /*+ * Request has been canceled. Exit without dumping.+ */+ LWLockRelease(McxtDumpLock);+ tuplestore_donestoring(tupstore);++ return (Datum) 0;+ }++ else if (mcxtdumpShmem->dump_status == MCXTDUMPSTATUS_DONE)+ {+ /* Dumping has completed. */+ LWLockRelease(McxtDumpLock);+ break;+ }+ /*+ * The dumper must be in the middle of a dumping or the request+ * hasn't been reached yet.+ */+ Assert(mcxtdumpShmem->dump_status == MCXTDUMPSTATUS_REQUESTING ||+ mcxtdumpShmem->dump_status == MCXTDUMPSTATUS_DUMPING);++ /*+ * Although we have checked the target process,+ * it might have been terminated after the check.+ * Ensure it again.+ */+ proc = BackendPidGetProc(dst_pid);++ if (proc == NULL)+ {+ ereport(WARNING,+ (errmsg("PID %d seems to exit before dumping.", dst_pid)));++ /* Initialize the shared memory and exit. */+ LWLockRelease(McxtDumpLock);+ LWLockAcquire(McxtDumpLock, LW_EXCLUSIVE);++ mcxtdumpShmem->dst_pid = 0;+ mcxtdumpShmem->src_pid = 0;+ mcxtdumpShmem->dump_status = MCXTDUMPSTATUS_ACCEPTABLE;++ LWLockRelease(McxtDumpLock);++ tuplestore_donestoring(tupstore);+ return (Datum) 1;+ }+ LWLockRelease(McxtDumpLock);++ /*+ * Wait. We expect to get a latch signal back from the dumper.+ * Use a timeout to enable cancellation.+ */+ (void) WaitLatch(MyLatch,+ WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,+ 1000L, WAIT_EVENT_DUMP_MEMORY_CONTEXT);+ }+ }+ PG_END_ENSURE_ERROR_CLEANUP(McxtReqCleanup, (Datum) Int32GetDatum(dst_pid));++ /* Read values from the dump file and put them on tuplestore. */+ PutDumpedValuesOnTuplestore(dumpfile, tupstore, tupdesc, dst_pid);
/* clean up and return the tuplestore */
tuplestore_donestoring(tupstore);
return (Datum) 0;
}
++/*+ * dump_memory_contexts+ * Dump local memory contexts to a file.+ */+static void+dump_memory_contexts(void)+{+ FILE *fpout;+ char dumpfile[MAXPGPATH];+ int format_id;+ pid_t src_pid;+ PGPROC *src_proc;++ snprintf(dumpfile, sizeof(dumpfile), "%s/%d", PG_MEMUSAGE_DIR, MyProcPid);++ LWLockAcquire(McxtDumpLock, LW_EXCLUSIVE);++ if (mcxtdumpShmem->dump_status == MCXTDUMPSTATUS_ACCEPTABLE)+ {+ /*+ * The requestor canceled the request and initialized+ * the shared memory. Simply exit.+ */+ LWLockRelease(McxtDumpLock);++ return;+ }++ if (mcxtdumpShmem->dump_status == MCXTDUMPSTATUS_CANCELING)+ {+ /*+ * The requestor canceled the request.+ * Initialize the shared memory and exit.+ */+ mcxtdumpShmem->dst_pid = 0;+ mcxtdumpShmem->src_pid = 0;+ mcxtdumpShmem->dump_status = MCXTDUMPSTATUS_ACCEPTABLE;++ LWLockRelease(McxtDumpLock);++ return;+ }++ Assert(mcxtdumpShmem->dump_status == MCXTDUMPSTATUS_REQUESTING);++ mcxtdumpShmem->dump_status = MCXTDUMPSTATUS_DUMPING;+ src_pid = mcxtdumpShmem->src_pid;++ LWLockRelease(McxtDumpLock);++ fpout = AllocateFile(dumpfile, "w");++ if (fpout == NULL)+ {+ ereport(LOG,+ (errcode_for_file_access(),+ errmsg("could not write dump file \"%s\": %m",+ dumpfile)));+ FreeFile(fpout);++ LWLockAcquire(McxtDumpLock, LW_EXCLUSIVE);++ mcxtdumpShmem->dst_pid = 0;+ mcxtdumpShmem->src_pid = 0;+ mcxtdumpShmem->dump_status = MCXTDUMPSTATUS_ACCEPTABLE;++ LWLockRelease(McxtDumpLock);++ return;+ }+ format_id = PG_MCXT_FILE_FORMAT_ID;+ fwrite(&format_id, sizeof(format_id), 1, fpout);++ /* Look into each memory context from TopMemoryContext recursively. */+ PutMemoryContextsStatsTupleStore(NULL, NULL,+ TopMemoryContext, NULL, 0, fpout);++ /*+ * Make dump file ends with 'E'.+ * This is checked by the requestor later.+ */+ fputc('E', fpout);++ if (ferror(fpout))+ {+ ereport(LOG,+ (errcode_for_file_access(),+ errmsg("could not write dump file \"%s\": %m",+ dumpfile)));+ FreeFile(fpout);+ unlink(dumpfile);++ LWLockAcquire(McxtDumpLock, LW_EXCLUSIVE);++ mcxtdumpShmem->dst_pid = 0;+ mcxtdumpShmem->src_pid = 0;+ mcxtdumpShmem->dump_status = MCXTDUMPSTATUS_ACCEPTABLE;++ LWLockRelease(McxtDumpLock);++ return;+ }++ /* No more output to be done. Close file. */+ else if (FreeFile(fpout) < 0)+ {+ ereport(LOG,+ (errcode_for_file_access(),+ errmsg("could not close dump file \"%s\": %m",+ dumpfile)));+ }++ LWLockAcquire(McxtDumpLock, LW_EXCLUSIVE);++ if (mcxtdumpShmem->dump_status == MCXTDUMPSTATUS_CANCELING)+ {+ /* During dumping, the requestor canceled the request. */+ unlink(dumpfile);++ mcxtdumpShmem->dst_pid = 0;+ mcxtdumpShmem->src_pid = 0;+ mcxtdumpShmem->dump_status = MCXTDUMPSTATUS_ACCEPTABLE;++ LWLockRelease(McxtDumpLock);++ return;+ }++ /* Dumping has succeeded, notify it to the requestor. */+ mcxtdumpShmem->dump_status = MCXTDUMPSTATUS_DONE;+ LWLockRelease(McxtDumpLock);+ src_proc = BackendPidGetProc(src_pid);+ SetLatch(&(src_proc->procLatch));++ return;+}++/*+ * ProcessDumpMemoryContextInterrupt+ * The portion of memory context dump interrupt handling that runs+ * outside of the signal handler.+ */+void+ProcessDumpMemoryContextInterrupt(void)+{+ ProcSignalDumpMemoryContextPending = false;+ dump_memory_contexts();+}++/*+ * HandleProcSignalDumpMemoryContext+ * Handle receipt of an interrupt indicating a memory context dump.+ * Signal handler portion of interrupt handling.+ */+void+HandleProcSignalDumpMemoryContext(void)+{+ ProcSignalDumpMemoryContextPending = true;+}++/*+ * McxtDumpShmemInit+ * Initialize mcxtdump shared memory struct.+ */+void+McxtDumpShmemInit(void)+{+ bool found;++ mcxtdumpShmem = (mcxtdumpShmemStruct *)+ ShmemInitStruct("Memory Context Dump Data",+ sizeof(mcxtdumpShmemStruct),+ &found);+ if (!found)+ {+ mcxtdumpShmem->dst_pid = 0;+ mcxtdumpShmem->src_pid = 0;+ mcxtdumpShmem->dump_status = MCXTDUMPSTATUS_ACCEPTABLE;+ }+}++/*+ * RemoveMemcxtFile+ * Remove dump files.+ */+void+RemoveMemcxtFile(int pid)+{+ DIR *dir;+ struct dirent *dumpfile;++ if (pid == 0)+ {+ /* delete all dump files */+ dir = AllocateDir(PG_MEMUSAGE_DIR);+ while ((dumpfile = ReadDir(dir, PG_MEMUSAGE_DIR)) != NULL)+ {+ char dumpfilepath[32];++ if (strcmp(dumpfile->d_name, ".") == 0 || strcmp(dumpfile->d_name, "..") == 0)+ continue;++ sprintf(dumpfilepath, "%s/%s", PG_MEMUSAGE_DIR, dumpfile->d_name);++ ereport(DEBUG2,+ (errmsg("removing file \"%s\"", dumpfilepath)));++ if (unlink(dumpfilepath) < 0)+ {+ ereport(ERROR,+ (errcode_for_file_access(),+ errmsg("could not remove file \"%s\": %m", dumpfilepath)));+ }+ }+ FreeDir(dir);+ }+ else+ {+ /* delete specified dump file */+ char str_pid[12];+ char dumpfilepath[32];+ struct stat stat_tmp;++ pg_ltoa(pid, str_pid);+ sprintf(dumpfilepath, "%s/%s", PG_MEMUSAGE_DIR, str_pid);++ ereport(DEBUG2,+ (errmsg("removing file \"%s\"", dumpfilepath)));++ if (stat(dumpfilepath, &stat_tmp) == 0)+ {+ if (unlink(dumpfilepath) < 0)+ {+ ereport(ERROR,+ (errcode_for_file_access(),+ errmsg("could not remove file \"%s\": %m", dumpfilepath)));+ }+ }+ }+}diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.cindex 3d5d6cc033..f85326fd60 100644--- a/src/backend/utils/init/globals.c+++ b/src/backend/utils/init/globals.c@@ -33,6 +33,7 @@ volatile sig_atomic_t ProcDiePending = false;
volatile sig_atomic_t ClientConnectionLost = false;
volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
volatile sig_atomic_t ProcSignalBarrierPending = false;
+volatile sig_atomic_t ProcSignalDumpMemoryContextPending = false;
volatile uint32 InterruptHoldoffCount = 0;
volatile uint32 QueryCancelHoldoffCount = 0;
volatile uint32 CritSectionCount = 0;
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.cindex 4453aeb0f0..ed90a4a339 100644--- a/src/bin/initdb/initdb.c+++ b/src/bin/initdb/initdb.c@@ -221,7 +221,8 @@ static const char *const subdirs[] = {
"pg_xact",
"pg_logical",
"pg_logical/snapshots",
- "pg_logical/mappings"+ "pg_logical/mappings",+ "pg_memusage"
};
diff --git a/src/bin/pg_basebackup/t/010_pg_basebackup.pl b/src/bin/pg_basebackup/t/010_pg_basebackup.plindex f674a7c94e..340a80fc11 100644--- a/src/bin/pg_basebackup/t/010_pg_basebackup.pl+++ b/src/bin/pg_basebackup/t/010_pg_basebackup.pl@@ -6,7 +6,7 @@ use File::Basename qw(basename dirname);
use File::Path qw(rmtree);
use PostgresNode;
use TestLib;
-use Test::More tests => 109;+use Test::More tests => 110;
program_help_ok('pg_basebackup');
program_version_ok('pg_basebackup');
@@ -124,7 +124,7 @@ is_deeply(
# Contents of these directories should not be copied.
foreach my $dirname (
- qw(pg_dynshmem pg_notify pg_replslot pg_serial pg_snapshots pg_stat_tmp pg_subtrans)+ qw(pg_dynshmem pg_notify pg_replslot pg_serial pg_snapshots pg_stat_tmp pg_subtrans pg_memusage)
)
{
is_deeply(
diff --git a/src/bin/pg_rewind/filemap.c b/src/bin/pg_rewind/filemap.cindex 2618b4c957..33da570598 100644--- a/src/bin/pg_rewind/filemap.c+++ b/src/bin/pg_rewind/filemap.c@@ -119,6 +119,9 @@ static const char *excludeDirContents[] =
/* Contents zeroed on startup, see StartupSUBTRANS(). */
"pg_subtrans",
+ /* Skip memory context dumped files. */+ "pg_memusage",+
/* end of list */
NULL
};
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.datindex d7b55f57ea..4e8aa46e48 100644--- a/src/include/catalog/pg_proc.dat+++ b/src/include/catalog/pg_proc.dat@@ -7852,15 +7852,25 @@
# memory context of local backend
{ oid => '2282',
- descr => 'information about all memory contexts of local backend',
proname => 'pg_get_backend_memory_contexts', prorows => '100',
proretset => 't', provolatile => 'v', proparallel => 'r',
prorettype => 'record', proargtypes => '',
proallargtypes => '{text,text,text,int4,int8,int8,int8,int8,int8}',
proargmodes => '{o,o,o,o,o,o,o,o,o}',
proargnames => '{name, ident, parent, level, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes}',
+ descr => 'information about all memory contexts of local backend',
prosrc => 'pg_get_backend_memory_contexts' },
+# memory context of specified backend+{ oid => '4543',+ descr => 'information about all memory contexts of specified backend',+ proname => 'pg_get_target_backend_memory_contexts', prorows => '100', proisstrict => 'f',+ proretset => 't', provolatile => 'v', proparallel => 'r', prorettype => 'record',+ proargtypes => 'int4', proallargtypes => '{int4,text,text,text,int4,int8,int8,int8,int8,int8}',+ proargmodes => '{i,o,o,o,o,o,o,o,o,o}',+ proargnames => '{pid, name, ident, parent, level, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes}',+ prosrc => 'pg_get_target_backend_memory_contexts' },+
# non-persistent series generator
{ oid => '1066', descr => 'non-persistent series generator',
proname => 'generate_series', prorows => '1000',
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.hindex 2c71db79c0..7812fa2499 100644--- a/src/include/miscadmin.h+++ b/src/include/miscadmin.h@@ -83,6 +83,7 @@ extern PGDLLIMPORT volatile sig_atomic_t QueryCancelPending;
extern PGDLLIMPORT volatile sig_atomic_t ProcDiePending;
extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
+extern PGDLLIMPORT volatile sig_atomic_t ProcSignalDumpMemoryContextPending;
extern PGDLLIMPORT volatile sig_atomic_t ClientConnectionLost;
diff --git a/src/include/pgstat.h b/src/include/pgstat.hindex 3a7e199750..b365dd7be4 100644--- a/src/include/pgstat.h+++ b/src/include/pgstat.h@@ -964,7 +964,8 @@ typedef enum
WAIT_EVENT_REPLICATION_SLOT_DROP,
WAIT_EVENT_SAFE_SNAPSHOT,
WAIT_EVENT_SYNC_REP,
- WAIT_EVENT_XACT_GROUP_UPDATE+ WAIT_EVENT_XACT_GROUP_UPDATE,+ WAIT_EVENT_DUMP_MEMORY_CONTEXT
} WaitEventIPC;
/* ----------
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.hindex 4ae7dc33b8..d025381ae8 100644--- a/src/include/storage/procsignal.h+++ b/src/include/storage/procsignal.h@@ -34,6 +34,7 @@ typedef enum
PROCSIG_PARALLEL_MESSAGE, /* message from cooperating parallel backend */
PROCSIG_WALSND_INIT_STOPPING, /* ask walsenders to prepare for shutdown */
PROCSIG_BARRIER, /* global barrier interrupt */
+ PROCSIG_DUMP_MEMCXT, /* request dumping memory context interrupt */
/* Recovery conflict reasons */
PROCSIG_RECOVERY_CONFLICT_DATABASE,
diff --git a/src/include/utils/mcxtfuncs.h b/src/include/utils/mcxtfuncs.hnew file mode 100644index 0000000000..eff2c62cbb--- /dev/null+++ b/src/include/utils/mcxtfuncs.h@@ -0,0 +1,44 @@+/*-------------------------------------------------------------------------+ *+ * mcxtfuncs.h+ * Declarations for showing backend memory context.+ *+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group+ * Portions Copyright (c) 1994, Regents of the University of California+ *+ *+ * IDENTIFICATION+ * src/backend/utils/mcxtfuncs.h+ *+ *-------------------------------------------------------------------------+ */+#ifndef MCXT_H+#define MCXT_H++/* Directory to store dumped memory files */+#define PG_MEMUSAGE_DIR "pg_memusage"++#define PG_MCXT_FILE_FORMAT_ID 0x01B5BC9E++typedef enum McxtDumpStatus+{+ MCXTDUMPSTATUS_ACCEPTABLE, /* no one is requesting dumping */+ MCXTDUMPSTATUS_REQUESTING, /* request has been issued, but dumper has not received it yet */+ MCXTDUMPSTATUS_DUMPING, /* dumper is dumping files */+ MCXTDUMPSTATUS_DONE, /* dumper has finished dumping and the requestor is working */+ MCXTDUMPSTATUS_CANCELING /* requestor canceled the dumping */+} McxtDumpStatus;++typedef struct mcxtdumpShmemStruct+{+ pid_t dst_pid; /* pid of the signal receiver */+ pid_t src_pid; /* pid of the signal sender */+ McxtDumpStatus dump_status; /* dump status */+} mcxtdumpShmemStruct;++extern void ProcessDumpMemoryContextInterrupt(void);+extern void HandleProcSignalDumpMemoryContext(void);+extern void McxtDumpShmemInit(void);+extern void RemoveMcxtDumpFile(int);+extern void RemoveMemcxtFile(int);+#endif /* MCXT_H */--
2.18.1
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: torikoshia@oss.nttdata.com, kasahara.tatsuhito@gmail.com, tgl@sss.pgh.pa.us, masao.fujii@oss.nttdata.com, gkokolatos@protonmail.com
Subject: Re: Get memory contexts of an arbitrary backend process
In-Reply-To: <9a59401241e9269254e70c8e0ef1cc7a@oss.nttdata.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox