From 3e913f720ba7017a4c9b5cba711a900d9083ff4e Mon Sep 17 00:00:00 2001
From: Vignesh C <vignesh21@gmail.com>
Date: Wed, 17 Nov 2021 08:40:18 +0530
Subject: [PATCH 3/4] pg_log_backtrace support for logging backtrace of aux
 procs

Enhance pg_log_backtrace to support logging the 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.
---
 doc/src/sgml/func.sgml                    | 12 ++++---
 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      |  3 +-
 src/backend/storage/ipc/signalfuncs.c     | 39 ++++++++++++++++-------
 src/test/regress/expected/backtrace.out   | 11 +++++++
 src/test/regress/expected/backtrace_1.out | 13 ++++++++
 src/test/regress/sql/backtrace.sql        |  9 ++++++
 11 files changed, 88 insertions(+), 19 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index f2a1b701e6..9692293daf 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25354,8 +25354,7 @@ SELECT collation for ('foo' COLLATE "de_DE");
         <returnvalue>boolean</returnvalue>
        </para>
        <para>
-        Requests to log the backtrace of the
-        <glossterm linkend="glossary-backend">backend</glossterm>
+        Requests to log the backtrace of the server process
         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"/>
@@ -25363,11 +25362,14 @@ SELECT collation for ('foo' COLLATE "de_DE");
         <xref linkend="guc-client-min-messages"/>. A backtrace identifies
         what function the backend process is currently executing, and how it was called.
         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 processes. This
-        feature is available if PostgreSQL was built with the ability to
+        This feature is available if PostgreSQL was built with the ability to
         capture backtraces. If the feature is not supported, the function will
         emit a warning and return false.
+        This function cannot request a backtrace from the
+        <glossterm linkend="glossary-postmaster">postmaster</glossterm>, the
+        <glossterm linkend="glossary-logger">logger</glossterm> or 
+        <glossterm linkend="glossary-stats-collector">statistics collector</glossterm>
+        process.
        </para></entry>
       </row>
 
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index be7366379d..7a25557702 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 logging backtrace */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 /*
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index dd9136a942..6298b48da3 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 logging backtrace */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 /*
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 3b33e01d95..938c08b0eb 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 logging backtrace */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index 47ec737888..41d40358bf 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 logging backtrace */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 626fae8454..8bc404d17b 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -306,4 +306,8 @@ HandleWalWriterInterrupts(void)
 
 		proc_exit(0);
 	}
+
+	/* Process logging backtrace */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index e769a7f785..af4e67e3b6 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -633,8 +633,7 @@ HandleLogBacktraceInterrupt(void)
  *
  * Any backend that participates in ProcSignal signaling must arrange
  * to call this function if we see LogBacktracePending set.
- * It is called from CHECK_FOR_INTERRUPTS(), which is enough because
- * the target process for logging of backtrace is a backend.
+ * CHECK_FOR_INTERRUPTS() or from process specific interrupt handlers.
  */
 void
 ProcessLogBacktraceInterrupt(void)
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index 0315968aa2..562ff104bb 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -311,6 +311,8 @@ pg_log_backtrace(PG_FUNCTION_ARGS)
 {
 	int			pid = PG_GETARG_INT32(0);
 	PGPROC	   *proc;
+	PGPROC	   *aux_proc = NULL;
+	BackendId   backendId = InvalidBackendId;
 
 #ifndef HAVE_BACKTRACE_SYMBOLS
 	ereport(WARNING,
@@ -320,26 +322,39 @@ pg_log_backtrace(PG_FUNCTION_ARGS)
 #endif
 
 	/*
-	 * 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 prevent a process from ending
-	 * to avoid that. But since this mechanism is usually used to debug a
-	 * backend consuming lots of CPU cycles, it's not considered a problem if
-	 * it ends on its own without first logging its backtrace.
+	 * 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
+	 * prevent a process from ending to avoid that.  But since this
+	 * mechanism is usually used to debug a backend consuming lots of CPU
+	 * cycles, it's not considered a problem if it ends without first
+	 * logging its backtrace.
 	 */
 	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);
+		}
 	}
 
 	/*
-	 * Send SIGUSR1 to postgres backend with the given pid, setting
-	 * PROCSIG_LOG_BACKTRACE, requesting the backend to log its backtrace.
+	 * Only regular backends will have valid backend id, auxiliary processes
+	 * don't.
+	 */
+	if (!aux_proc)
+		backendId = proc->backendId;
+
+	/*
+	 * Send SIGUSR1 to server process with the given pid, setting
+	 * PROCSIG_LOG_BACKTRACE, requesting the process to log its backtrace.
 	 */
-	if (!SendProcSignal(pid, PROCSIG_LOG_BACKTRACE, proc->backendId))
+	if (!SendProcSignal(pid, PROCSIG_LOG_BACKTRACE, backendId))
 		PG_RETURN_BOOL(true);
 
 	ereport(WARNING,
diff --git a/src/test/regress/expected/backtrace.out b/src/test/regress/expected/backtrace.out
index dfb044fd1d..e5d5caab62 100644
--- a/src/test/regress/expected/backtrace.out
+++ b/src/test/regress/expected/backtrace.out
@@ -12,6 +12,17 @@ SELECT pg_log_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_log_backtrace(get_proc_pid('checkpointer'));
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+DROP FUNCTION get_proc_pid(text);
 CREATE ROLE regress_log_backtrace;
 SELECT has_function_privilege('regress_log_backtrace',
   'pg_log_backtrace(integer)', 'EXECUTE'); -- no
diff --git a/src/test/regress/expected/backtrace_1.out b/src/test/regress/expected/backtrace_1.out
index f2c837965e..0d1d97343d 100644
--- a/src/test/regress/expected/backtrace_1.out
+++ b/src/test/regress/expected/backtrace_1.out
@@ -14,6 +14,19 @@ HINT:  You need to rebuild PostgreSQL using library containing backtrace_symbols
  f
 (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_log_backtrace(get_proc_pid('checkpointer'));
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+DROP FUNCTION get_proc_pid(text);
 CREATE ROLE regress_log_backtrace;
 SELECT has_function_privilege('regress_log_backtrace',
   'pg_log_backtrace(integer)', 'EXECUTE'); -- no
diff --git a/src/test/regress/sql/backtrace.sql b/src/test/regress/sql/backtrace.sql
index a8465b017f..2b0a8e3195 100644
--- a/src/test/regress/sql/backtrace.sql
+++ b/src/test/regress/sql/backtrace.sql
@@ -9,6 +9,15 @@
 
 SELECT pg_log_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_log_backtrace(get_proc_pid('checkpointer'));
+
+DROP FUNCTION get_proc_pid(text);
+
 CREATE ROLE regress_log_backtrace;
 
 SELECT has_function_privilege('regress_log_backtrace',
-- 
2.17.0

