On Fri, Dec 25, 2020 at 6:08 PM Kasahara Tatsuhito <kasahara.tatsuh...@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,
From d7d99a5278c533ceae465b673b1d69f6ce51ee89 Mon Sep 17 00:00:00 2001
From: Atsushi Torikoshi <torikos...@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.c
index 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.sql
index 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.c
index 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.c
index 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.c
index 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.c
index 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.txt
index 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					48
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 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.c
index 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.c
index 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.c
index 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.pl
index 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.c
index 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.dat
index 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.h
index 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.h
index 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.h
index 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.h
new file mode 100644
index 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 via email to