From 2628271de4f2b185ae2eaec29927ffe12401bc60 Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Wed, 7 Feb 2024 18:22:58 +0000
Subject: [PATCH v29 2/2] Extend backtrace logging function for auxiliary
 processes

---
 doc/src/sgml/func.sgml                       | 12 ++++---
 src/backend/storage/ipc/procbacktrace.c      | 35 ++++++++++++++------
 src/backend/storage/lmgr/proc.c              |  2 ++
 src/test/regress/expected/misc_functions.out |  8 +++++
 src/test/regress/sql/misc_functions.sql      |  3 ++
 5 files changed, 45 insertions(+), 15 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index b7d7305331..b810a3dcaf 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -27196,11 +27196,13 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para>
        <para>
         Requests to log the backtrace of a backend with the specified process
-        ID. The backtraces will be logged to <systemitem>stderr</systemitem>.
-        Typically, a backtrace identifies which function a process is currently
-        executing, and is useful for developers to diagnose stuck processes.
-        This function is supported only if PostgreSQL was built with the
-        ability to capture backtraces, otherwise it will emit a warning.
+        ID. This function can send the request to backends and auxiliary
+        processes except logger. The backtraces will be logged to
+        <systemitem>stderr</systemitem>. Typically, a backtrace identifies
+        which function a process is currently executing, and is useful for
+        developers to diagnose stuck processes. This function is supported only
+        if PostgreSQL was built with the ability to capture backtraces,
+        otherwise it will emit a warning.
        </para></entry>
       </row>
 
diff --git a/src/backend/storage/ipc/procbacktrace.c b/src/backend/storage/ipc/procbacktrace.c
index bfab5783da..b85684cd19 100644
--- a/src/backend/storage/ipc/procbacktrace.c
+++ b/src/backend/storage/ipc/procbacktrace.c
@@ -92,15 +92,15 @@ LoadBacktraceFunctions(void)
 
 /*
  * pg_log_backend_backtrace
- *		Signal a backend to log its current backtrace.
+ *		Signal a backend or an auxiliary process to log its current backtrace.
  *
  * By default, only superusers are allowed to signal to log the backtrace
  * because allowing any users to issue this request at an unbounded
  * rate would cause lots of log messages on stderr and which can lead to
  * denial of service. Additional roles can be permitted with GRANT.
  *
- * On receipt of this signal, a backend emits the current backtrace to stderr
- * in the signal handler.
+ * On receipt of this signal, a backend or an auxiliary process emits the
+ * current backtrace to stderr in the signal handler.
  */
 Datum
 pg_log_backend_backtrace(PG_FUNCTION_ARGS)
@@ -108,15 +108,31 @@ pg_log_backend_backtrace(PG_FUNCTION_ARGS)
 #ifdef HAVE_BACKTRACE_SYMBOLS
 	int			pid = PG_GETARG_INT32(0);
 	PGPROC	   *proc;
+	BackendId	backendId = InvalidBackendId;
 
 	proc = BackendPidGetProc(pid);
 
 	/*
-	 * BackendPidGetProc returns NULL if the pid isn't valid.
+	 * See if the process with given pid is a backend or an auxiliary process.
 	 *
-	 * Note that the proc will also be NULL if the pid refers to an auxiliary
-	 * process or the postmaster (neither of which can be signaled via
-	 * pg_log_backend_backtrace() to get backtrace).
+	 * 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)
 	{
@@ -125,12 +141,11 @@ pg_log_backend_backtrace(PG_FUNCTION_ARGS)
 		 * if one backend terminated on its own during the run.
 		 */
 		ereport(WARNING,
-				(errmsg("PID %d is not a PostgreSQL backend process", pid)));
-
+				(errmsg("PID %d is not a PostgreSQL server process", pid)));
 		PG_RETURN_BOOL(false);
 	}
 
-	if (SendProcSignal(pid, PROCSIG_LOG_BACKTRACE, proc->backendId) < 0)
+	if (SendProcSignal(pid, PROCSIG_LOG_BACKTRACE, backendId) < 0)
 	{
 		/* Again, just a warning to allow loops */
 		ereport(WARNING,
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 73e0368c29..f61a6d0d8f 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -635,6 +635,8 @@ InitAuxiliaryProcess(void)
 	 */
 	InitLWLockAccess();
 
+	LoadBacktraceFunctions();
+
 #ifdef EXEC_BACKEND
 
 	/*
diff --git a/src/test/regress/expected/misc_functions.out b/src/test/regress/expected/misc_functions.out
index 3ff0136347..8751538c57 100644
--- a/src/test/regress/expected/misc_functions.out
+++ b/src/test/regress/expected/misc_functions.out
@@ -341,6 +341,14 @@ SELECT count(*) > 0 AS ok FROM pg_log_backend_backtrace(pg_backend_pid());
  t
 (1 row)
 
+SELECT pid AS c_pid FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer' \gset
+SELECT count(*) > 0 AS ok FROM pg_log_backend_backtrace(:c_pid);
+ ok 
+----
+ t
+(1 row)
+
 RESET client_min_messages;
 CREATE ROLE regress_log_backtrace;
 SELECT has_function_privilege('regress_log_backtrace',
diff --git a/src/test/regress/sql/misc_functions.sql b/src/test/regress/sql/misc_functions.sql
index 2a3f9bacaa..0e51974ac3 100644
--- a/src/test/regress/sql/misc_functions.sql
+++ b/src/test/regress/sql/misc_functions.sql
@@ -114,6 +114,9 @@ DROP ROLE regress_log_memory;
 -- generation support.
 SET client_min_messages = 'ERROR';
 SELECT count(*) > 0 AS ok FROM pg_log_backend_backtrace(pg_backend_pid());
+SELECT pid AS c_pid FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer' \gset
+SELECT count(*) > 0 AS ok FROM pg_log_backend_backtrace(:c_pid);
 RESET client_min_messages;
 
 CREATE ROLE regress_log_backtrace;
-- 
2.34.1

