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>
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
the server log based on the log configuration set
(See <xref linkend="runtime-config-logging"/> for more information),

View File

@ -577,6 +577,10 @@ HandleCheckpointerInterrupts(void)
/* Normal exit from the checkpointer is here */
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/procsignal.h"
#include "utils/guc.h"
#include "utils/memutils.h"
volatile sig_atomic_t ConfigReloadPending = false;
volatile sig_atomic_t ShutdownRequestPending = false;
@ -43,6 +44,10 @@ HandleMainLoopInterrupts(void)
if (ShutdownRequestPending)
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/spin.h"
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/ps_status.h"
@ -861,8 +862,9 @@ pgarch_die(int code, Datum arg)
* Interrupt handler for WAL archiver process.
*
* This is called in the loops pgarch_MainLoop and pgarch_ArchiverCopyLoop.
* It checks for barrier events and config update, but not shutdown request
* because how to handle shutdown request is different between those loops.
* It checks for barrier events, config update and request for logging of
* memory contexts, but not shutdown request because how to handle
* shutdown request is different between those loops.
*/
static void
HandlePgArchInterrupts(void)
@ -875,4 +877,8 @@ HandlePgArchInterrupts(void)
ConfigReloadPending = false;
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/standby.h"
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/timeout.h"
@ -200,6 +201,10 @@ HandleStartupProcInterrupts(void)
/* Process barrier events */
if (ProcSignalBarrierPending)
ProcessProcSignalBarrier();
/* Perform logging of memory contexts of this process */
if (LogMemoryContextPending)
ProcessLogMemoryContextInterrupt();
}

View File

@ -306,4 +306,8 @@ HandleWalWriterInterrupts(void)
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
* 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
* 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
* service. Additional roles can be permitted with GRANT.
*
* On receipt of this signal, a backend sets the flag in the signal
* handler, which causes the next CHECK_FOR_INTERRUPTS() to log the
* memory contexts.
* On receipt of this signal, a backend or an auxiliary process sets the flag
* in the signal handler, which causes the next CHECK_FOR_INTERRUPTS()
* or process-specific interrupt handler to log the memory contexts.
*/
Datum
pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
{
int pid = PG_GETARG_INT32(0);
PGPROC *proc;
BackendId backendId = InvalidBackendId;
proc = BackendPidGetProc(pid);
/*
* BackendPidGetProc returns 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 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.
* See if the process with given pid is a backend or an auxiliary process.
*
* If the given process is a backend, use its backend id in
* SendProcSignal() later to speed up the operation. Otherwise, don't do
* that because auxiliary processes (except the startup process) don't
* have a valid backend id.
*/
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)
{
@ -199,7 +213,7 @@ pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
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 */
ereport(WARNING,

View File

@ -147,6 +147,13 @@ SELECT pg_log_backend_memory_contexts(pg_backend_pid());
t
(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;
SELECT has_function_privilege('regress_log_memory',
'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(pid) FROM pg_stat_activity
WHERE backend_type = 'checkpointer';
CREATE ROLE regress_log_memory;
SELECT has_function_privilege('regress_log_memory',