From f8c96cebdab91a6e4f8e6f4eed371b47d65ce9af Mon Sep 17 00:00:00 2001
From: Vignesh C <vignesh21@gmail.com>
Date: Mon, 15 Nov 2021 09:28:47 +0530
Subject: [PATCH v13 2/2] pg_print_backtrace support for printing backtrace of
 aux procs

Enhanced pg_print_backtrace to support printing backtrace of auxiliary
processes such as bgwriter, checkpointer, wal writer, archiver, startup
process and wal receiver. It will be useful to look at the backtrace of
these processes too, for debugging purposes and to check if the process
is genuinely taking time or if it is a stuck process. Inside the code,
we could use the AuxiliaryPidGetProc() to get the PGPROC of these
processes. Note that, neither AuxiliaryPidGetProc() nor BackendPidGetProc()
can return PGPROC(as they don't have PGPROC entries at all) entries for the
syslogger and stats collector processes.
---
 doc/src/sgml/func.sgml                       | 25 ++++++++------
 src/backend/postmaster/checkpointer.c        |  4 +++
 src/backend/postmaster/interrupt.c           |  4 +++
 src/backend/postmaster/pgarch.c              |  4 +++
 src/backend/postmaster/startup.c             |  4 +++
 src/backend/postmaster/walwriter.c           |  4 +++
 src/backend/storage/ipc/procsignal.c         |  5 ++-
 src/backend/storage/ipc/signalfuncs.c        | 35 ++++++++++++++------
 src/test/regress/expected/misc_functions.out | 11 ++++++
 src/test/regress/sql/misc_functions.sql      |  9 +++++
 10 files changed, 82 insertions(+), 23 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 8a30ee2eee..daba3d4391 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25355,19 +25355,24 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para>
        <para>
         Requests to log the backtrace of the
-        <glossterm linkend="glossary-backend">backend</glossterm>
-        with the specified process ID. The backtrace will be logged at message
-        level <literal>LOG</literal>. It will appear in the server log based on
-        the log configuration set (See <xref linkend="runtime-config-logging"/>
-        for more information), but will not be sent to the client regardless of
+        <glossterm linkend="glossary-backend">backend</glossterm> or the
+        <glossterm linkend="glossary-wal-sender">WAL sender</glossterm> or the
+        <glossterm linkend="glossary-auxiliary-proc">auxiliary process</glossterm>
+        with the specified process ID. All of the
+        <glossterm linkend="glossary-auxiliary-proc">auxiliary processes</glossterm>
+        are supported except the <glossterm linkend="glossary-logger">logger</glossterm>
+        and the <glossterm linkend="glossary-stats-collector">statistics collector</glossterm>
+        as they are not connected to shared memory the function can not make requests.
+        The backtrace 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),
+        but will not be sent to the client regardless of
         <xref linkend="guc-client-min-messages"/>. A backtrace will identify
         where exactly the backend process is currently executing. This may be
         useful to developers to diagnose stuck processes and other problems.
-        This feature is not supported for the postmaster, logger, checkpointer,
-        walwriter, background writer or statistics collector process. This
-        feature will be available if PostgreSQL was built with the ability to
-        capture backtrace. If not available, the function will emit a warning
-        and return false.
+        This feature will be available if PostgreSQL was built with the
+        ability to capture backtrace. If not available, the function will emit
+        a warning and return false.
        </para></entry>
       </row>
 
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index be7366379d..83d78a0126 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -581,6 +581,10 @@ HandleCheckpointerInterrupts(void)
 		/* Normal exit from the checkpointer is here */
 		proc_exit(0);			/* done */
 	}
+
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+		ProcessPrintBacktraceInterrupt();
 }
 
 /*
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index dd9136a942..ad5716053e 100644
--- a/src/backend/postmaster/interrupt.c
+++ b/src/backend/postmaster/interrupt.c
@@ -43,6 +43,10 @@ HandleMainLoopInterrupts(void)
 
 	if (ShutdownRequestPending)
 		proc_exit(0);
+
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+		ProcessPrintBacktraceInterrupt();
 }
 
 /*
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 3b33e01d95..e6b38572bf 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -856,4 +856,8 @@ HandlePgArchInterrupts(void)
 		ConfigReloadPending = false;
 		ProcessConfigFile(PGC_SIGHUP);
 	}
+
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+		ProcessPrintBacktraceInterrupt();
 }
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index 47ec737888..91be95992e 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -200,6 +200,10 @@ HandleStartupProcInterrupts(void)
 	/* Process barrier events */
 	if (ProcSignalBarrierPending)
 		ProcessProcSignalBarrier();
+
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+		ProcessPrintBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 626fae8454..1ec5c892ac 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -306,4 +306,8 @@ HandleWalWriterInterrupts(void)
 
 		proc_exit(0);
 	}
+
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+		ProcessPrintBacktraceInterrupt();
 }
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index 80fa6d4990..9151e4cf81 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -632,9 +632,8 @@ HandlePrintBacktraceInterrupt(void)
  * backend process.
  *
  * Any backend that participates in ProcSignal signaling must arrange
- * to call this function if we see PrintBacktracePending set.
- * It is called from CHECK_FOR_INTERRUPTS(), which is enough because
- * the target process for logging of backtrace is a backend.
+ * to call this function if we see PrintBacktracePending set. It is called from
+ * CHECK_FOR_INTERRUPTS() or from process specific interrupt handlers.
  */
 void
 ProcessPrintBacktraceInterrupt(void)
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index 34f270555b..ef53f8d188 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -312,29 +312,44 @@ pg_print_backtrace(PG_FUNCTION_ARGS)
 #ifdef HAVE_BACKTRACE_SYMBOLS
 	int			pid = PG_GETARG_INT32(0);
 	PGPROC	   *proc;
+	PGPROC	   *aux_proc = NULL;
+	BackendId   backendId = InvalidBackendId;
 
 	/*
-	 * 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 CPU cycles, that it might
-	 * end on its own first and its backtrace are not logged is not a problem.
+	 * BackendPidGetProc or AuxiliaryPidGetProc 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 CPU cycles, that it might end on its own first and its backtrace are
+	 * not logged is not a problem.
 	 */
 	proc = BackendPidGetProc(pid);
 	if (proc == NULL)
 	{
-		ereport(WARNING,
-				(errmsg("PID %d is not a PostgreSQL server process", pid)));
-		PG_RETURN_BOOL(false);
+		/* See if the process with given pid is an auxiliary process. */
+		aux_proc = AuxiliaryPidGetProc(pid);
+		if (aux_proc == NULL)
+		{
+			ereport(WARNING,
+					(errmsg("PID %d is not a PostgreSQL server process", pid)));
+			PG_RETURN_BOOL(false);
+		}
 	}
 
+	/*
+	 * Only regular backends will have valid backend id, auxiliary processes
+	 * don't.
+	 */
+	if (!aux_proc)
+		backendId = proc->backendId;
+
 	/*
 	 * Send SIGUSR1 to postgres backend whose pid matches pid by
 	 * setting PROCSIG_PRINT_BACKTRACE, the backend process will print
 	 * the backtrace once the signal is received.
 	 */
-	if (!SendProcSignal(pid, PROCSIG_PRINT_BACKTRACE, proc->backendId))
+	if (!SendProcSignal(pid, PROCSIG_PRINT_BACKTRACE, backendId))
 		PG_RETURN_BOOL(true);
 	else
 		ereport(WARNING,
diff --git a/src/test/regress/expected/misc_functions.out b/src/test/regress/expected/misc_functions.out
index c75cef00e7..765a94abf5 100644
--- a/src/test/regress/expected/misc_functions.out
+++ b/src/test/regress/expected/misc_functions.out
@@ -189,6 +189,17 @@ SELECT pg_print_backtrace(pg_backend_pid());
  t
 (1 row)
 
+CREATE FUNCTION get_proc_pid(text)
+RETURNS int
+LANGUAGE SQL
+AS 'SELECT pid FROM pg_stat_activity WHERE backend_type = $1';
+SELECT pg_print_backtrace(get_proc_pid('checkpointer'));
+ pg_print_backtrace 
+--------------------
+ t
+(1 row)
+
+DROP FUNCTION get_proc_pid(text);
 CREATE ROLE regress_print_backtrace;
 SELECT has_function_privilege('regress_print_backtrace',
   'pg_print_backtrace(integer)', 'EXECUTE'); -- no
diff --git a/src/test/regress/sql/misc_functions.sql b/src/test/regress/sql/misc_functions.sql
index d63d2e2ddb..bb1d02f143 100644
--- a/src/test/regress/sql/misc_functions.sql
+++ b/src/test/regress/sql/misc_functions.sql
@@ -72,6 +72,15 @@ DROP ROLE regress_log_memory;
 
 SELECT pg_print_backtrace(pg_backend_pid());
 
+CREATE FUNCTION get_proc_pid(text)
+RETURNS int
+LANGUAGE SQL
+AS 'SELECT pid FROM pg_stat_activity WHERE backend_type = $1';
+
+SELECT pg_print_backtrace(get_proc_pid('checkpointer'));
+
+DROP FUNCTION get_proc_pid(text);
+
 CREATE ROLE regress_print_backtrace;
 
 SELECT has_function_privilege('regress_print_backtrace',
-- 
2.30.2

