Enhance pg_log_backend_memory_contexts() for auxiliary processes.

Previously pg_log_backend_memory_contexts() could request to
log the memory contexts of backends, but not of auxiliary processes
such as checkpointer. This commit enhances the function so that
it can also send the request to auxiliary processes. It's useful to
look at the memory contexts of those processes for debugging purpose
and better understanding of the memory usage pattern of them.

Note that pg_log_backend_memory_contexts() cannot send the request
to logger or statistics collector. Because this logging request
mechanism is based on shared memory but those processes aren't
connected to that.

Author: Bharath Rupireddy
Reviewed-by: Vignesh C, Kyotaro Horiguchi, Fujii Masao
Discussion: https://postgr.es/m/CALj2ACU1nBzpacOK2q=a65S_4+Oaz_rLTsU1Ri0gf7YUmnmhfQ@mail.gmail.com
This commit is contained in:
Fujii Masao 2022-01-11 23:19:59 +09:00
parent 85c61ba892
commit 790fbda902
9 changed files with 65 additions and 15 deletions

View File

@ -25421,7 +25421,9 @@ SELECT collation for ('foo' COLLATE "de_DE");
</para> </para>
<para> <para>
Requests to log the memory contexts of the backend with the Requests to log the memory contexts of the backend with the
specified process ID. These memory contexts will be logged at specified process ID. This function can send the request to
backends and auxiliary processes except logger and statistics
collector. These memory contexts will be logged at
<literal>LOG</literal> message level. They will appear in <literal>LOG</literal> message level. They will appear in
the server log based on the log configuration set the server log based on the log configuration set
(See <xref linkend="runtime-config-logging"/> for more information), (See <xref linkend="runtime-config-logging"/> for more information),

View File

@ -577,6 +577,10 @@ HandleCheckpointerInterrupts(void)
/* Normal exit from the checkpointer is here */ /* Normal exit from the checkpointer is here */
proc_exit(0); /* done */ proc_exit(0); /* done */
} }
/* Perform logging of memory contexts of this process */
if (LogMemoryContextPending)
ProcessLogMemoryContextInterrupt();
} }
/* /*

View File

@ -22,6 +22,7 @@
#include "storage/latch.h" #include "storage/latch.h"
#include "storage/procsignal.h" #include "storage/procsignal.h"
#include "utils/guc.h" #include "utils/guc.h"
#include "utils/memutils.h"
volatile sig_atomic_t ConfigReloadPending = false; volatile sig_atomic_t ConfigReloadPending = false;
volatile sig_atomic_t ShutdownRequestPending = false; volatile sig_atomic_t ShutdownRequestPending = false;
@ -43,6 +44,10 @@ HandleMainLoopInterrupts(void)
if (ShutdownRequestPending) if (ShutdownRequestPending)
proc_exit(0); proc_exit(0);
/* Perform logging of memory contexts of this process */
if (LogMemoryContextPending)
ProcessLogMemoryContextInterrupt();
} }
/* /*

View File

@ -50,6 +50,7 @@
#include "storage/shmem.h" #include "storage/shmem.h"
#include "storage/spin.h" #include "storage/spin.h"
#include "utils/guc.h" #include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/ps_status.h" #include "utils/ps_status.h"
@ -861,8 +862,9 @@ pgarch_die(int code, Datum arg)
* Interrupt handler for WAL archiver process. * Interrupt handler for WAL archiver process.
* *
* This is called in the loops pgarch_MainLoop and pgarch_ArchiverCopyLoop. * This is called in the loops pgarch_MainLoop and pgarch_ArchiverCopyLoop.
* It checks for barrier events and config update, but not shutdown request * It checks for barrier events, config update and request for logging of
* because how to handle shutdown request is different between those loops. * memory contexts, but not shutdown request because how to handle
* shutdown request is different between those loops.
*/ */
static void static void
HandlePgArchInterrupts(void) HandlePgArchInterrupts(void)
@ -875,4 +877,8 @@ HandlePgArchInterrupts(void)
ConfigReloadPending = false; ConfigReloadPending = false;
ProcessConfigFile(PGC_SIGHUP); ProcessConfigFile(PGC_SIGHUP);
} }
/* Perform logging of memory contexts of this process */
if (LogMemoryContextPending)
ProcessLogMemoryContextInterrupt();
} }

View File

@ -32,6 +32,7 @@
#include "storage/procsignal.h" #include "storage/procsignal.h"
#include "storage/standby.h" #include "storage/standby.h"
#include "utils/guc.h" #include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/timeout.h" #include "utils/timeout.h"
@ -200,6 +201,10 @@ HandleStartupProcInterrupts(void)
/* Process barrier events */ /* Process barrier events */
if (ProcSignalBarrierPending) if (ProcSignalBarrierPending)
ProcessProcSignalBarrier(); ProcessProcSignalBarrier();
/* Perform logging of memory contexts of this process */
if (LogMemoryContextPending)
ProcessLogMemoryContextInterrupt();
} }

View File

@ -306,4 +306,8 @@ HandleWalWriterInterrupts(void)
proc_exit(0); proc_exit(0);
} }
/* Perform logging of memory contexts of this process */
if (LogMemoryContextPending)
ProcessLogMemoryContextInterrupt();
} }

View File

@ -160,33 +160,47 @@ pg_get_backend_memory_contexts(PG_FUNCTION_ARGS)
/* /*
* pg_log_backend_memory_contexts * pg_log_backend_memory_contexts
* Signal a backend process to log its memory contexts. * Signal a backend or an auxiliary process to log its memory contexts.
* *
* By default, only superusers are allowed to signal to log the memory * By default, only superusers are allowed to signal to log the memory
* contexts because allowing any users to issue this request at an unbounded * contexts because allowing any users to issue this request at an unbounded
* rate would cause lots of log messages and which can lead to denial of * rate would cause lots of log messages and which can lead to denial of
* service. Additional roles can be permitted with GRANT. * service. Additional roles can be permitted with GRANT.
* *
* On receipt of this signal, a backend sets the flag in the signal * On receipt of this signal, a backend or an auxiliary process sets the flag
* handler, which causes the next CHECK_FOR_INTERRUPTS() to log the * in the signal handler, which causes the next CHECK_FOR_INTERRUPTS()
* memory contexts. * or process-specific interrupt handler to log the memory contexts.
*/ */
Datum Datum
pg_log_backend_memory_contexts(PG_FUNCTION_ARGS) pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
{ {
int pid = PG_GETARG_INT32(0); int pid = PG_GETARG_INT32(0);
PGPROC *proc; PGPROC *proc;
BackendId backendId = InvalidBackendId;
proc = BackendPidGetProc(pid); proc = BackendPidGetProc(pid);
/* /*
* BackendPidGetProc returns NULL if the pid isn't valid; but by the time * See if the process with given pid is a backend or an auxiliary process.
* we reach kill(), a process for which we get a valid proc here might *
* have terminated on its own. There's no way to acquire a lock on an * If the given process is a backend, use its backend id in
* arbitrary process to prevent that. But since this mechanism is usually * SendProcSignal() later to speed up the operation. Otherwise, don't do
* used to debug a backend running and consuming lots of memory, that it * that because auxiliary processes (except the startup process) don't
* might end on its own first and its memory contexts are not logged is * have a valid backend id.
* not a problem. */
if (proc != NULL)
backendId = proc->backendId;
else
proc = AuxiliaryPidGetProc(pid);
/*
* BackendPidGetProc() and AuxiliaryPidGetProc() return NULL if the pid
* isn't valid; but by the time we reach kill(), a process for which we
* get a valid proc here might have terminated on its own. There's no way
* to acquire a lock on an arbitrary process to prevent that. But since
* this mechanism is usually used to debug a backend or an auxiliary
* process running and consuming lots of memory, that it might end on its
* own first and its memory contexts are not logged is not a problem.
*/ */
if (proc == NULL) if (proc == NULL)
{ {
@ -199,7 +213,7 @@ pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(false); PG_RETURN_BOOL(false);
} }
if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, proc->backendId) < 0) if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, backendId) < 0)
{ {
/* Again, just a warning to allow loops */ /* Again, just a warning to allow loops */
ereport(WARNING, ereport(WARNING,

View File

@ -147,6 +147,13 @@ SELECT pg_log_backend_memory_contexts(pg_backend_pid());
t t
(1 row) (1 row)
SELECT pg_log_backend_memory_contexts(pid) FROM pg_stat_activity
WHERE backend_type = 'checkpointer';
pg_log_backend_memory_contexts
--------------------------------
t
(1 row)
CREATE ROLE regress_log_memory; CREATE ROLE regress_log_memory;
SELECT has_function_privilege('regress_log_memory', SELECT has_function_privilege('regress_log_memory',
'pg_log_backend_memory_contexts(integer)', 'EXECUTE'); -- no 'pg_log_backend_memory_contexts(integer)', 'EXECUTE'); -- no

View File

@ -41,6 +41,9 @@ SELECT num_nulls();
SELECT pg_log_backend_memory_contexts(pg_backend_pid()); SELECT pg_log_backend_memory_contexts(pg_backend_pid());
SELECT pg_log_backend_memory_contexts(pid) FROM pg_stat_activity
WHERE backend_type = 'checkpointer';
CREATE ROLE regress_log_memory; CREATE ROLE regress_log_memory;
SELECT has_function_privilege('regress_log_memory', SELECT has_function_privilege('regress_log_memory',