Printing backtrace of postgres processes

Started by vignesh Cabout 5 years ago133 messages
#1vignesh C
vignesh21@gmail.com
1 attachment(s)

Hi,

I would like to propose getting the callstack of the postgres process
by connecting to the server. This helps us in diagnosing the problems
from a customer environment in case of hung process or in case of long
running process.
The idea here is to implement & expose pg_print_callstack function,
internally what this function does is, the connected backend will send
SIGUSR1 signal by setting PMSIGNAL_BACKTRACE_EMIT to the postmaster
process. Postmaster process will send a SIGUSR1 signal to the process
by setting PROCSIG_BACKTRACE_PRINT if the process has access to
ProcSignal. As syslogger process & Stats process don't have access to
ProcSignal, multiplexing with SIGUSR1 is not possible for these
processes, hence SIGUSR2 signal will be sent for these processes. Once
the process receives this signal it will log the backtrace of the
process.
Attached is a WIP patch for the same.
Thoughts?

Regards,
Vignesh
EnterpriseDB: http://www.enterprisedb.com

Attachments:

0001-Print-backtrace-of-postgres-process.patchtext/x-patch; charset=US-ASCII; name=0001-Print-backtrace-of-postgres-process.patchDownload
From c1006110bdeac2135d1c8e9220f65d50cd49ab63 Mon Sep 17 00:00:00 2001
From: Vignesh C <vignesh21@gmail.com>
Date: Sun, 22 Nov 2020 05:58:24 +0530
Subject: [PATCH] Print backtrace of postgres process that are part of this
 instance.

The idea here is to implement & expose pg_print_callstack function, internally
what this function does is, the connected backend will send SIGUSR1 signal by
setting PMSIGNAL_BACKTRACE_EMIT to the postmaster process. Postmaster process
will send SIGUSR1 signal to process by setting PROCSIG_BACKTRACE_PRINT if the
process that have access to ProcSignal. As syslogger process & Stats process
don't have access to ProcSignal, multiplexing with SIGUSR1 is not possible
for these processes, hence SIGUSR2 signal will be sent for these process.
Once the process receives this signal it will log the backtrace of the process.
---
 src/backend/postmaster/pgstat.c       | 19 ++++++++++++++++++-
 src/backend/postmaster/postmaster.c   | 20 ++++++++++++++++++++
 src/backend/postmaster/syslogger.c    | 16 +++++++++++++++-
 src/backend/storage/ipc/procsignal.c  | 28 ++++++++++++++++++++++++++++
 src/backend/storage/ipc/signalfuncs.c | 22 ++++++++++++++++++++++
 src/backend/tcop/postgres.c           | 31 +++++++++++++++++++++++++++++++
 src/include/catalog/pg_proc.dat       |  6 +++++-
 src/include/storage/pmsignal.h        |  2 ++
 src/include/storage/procsignal.h      |  2 ++
 src/include/tcop/tcopprot.h           |  2 ++
 10 files changed, 145 insertions(+), 3 deletions(-)

diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index e76e627..bd38264 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -62,6 +62,7 @@
 #include "storage/pg_shmem.h"
 #include "storage/procsignal.h"
 #include "storage/sinvaladt.h"
+#include "tcop/tcopprot.h"
 #include "utils/ascii.h"
 #include "utils/guc.h"
 #include "utils/memutils.h"
@@ -372,6 +373,8 @@ static void pgstat_recv_checksum_failure(PgStat_MsgChecksumFailure *msg, int len
 static void pgstat_recv_replslot(PgStat_MsgReplSlot *msg, int len);
 static void pgstat_recv_tempfile(PgStat_MsgTempFile *msg, int len);
 
+static void sigUsr2Handler(SIGNAL_ARGS);
+
 /* ------------------------------------------------------------
  * Public functions called from postmaster follow
  * ------------------------------------------------------------
@@ -4666,7 +4669,7 @@ PgstatCollectorMain(int argc, char *argv[])
 	pqsignal(SIGALRM, SIG_IGN);
 	pqsignal(SIGPIPE, SIG_IGN);
 	pqsignal(SIGUSR1, SIG_IGN);
-	pqsignal(SIGUSR2, SIG_IGN);
+	pqsignal(SIGUSR2, sigUsr2Handler);
 	/* Reset some signals that are accepted by postmaster but not here */
 	pqsignal(SIGCHLD, SIG_DFL);
 	PG_SETMASK(&UnBlockSig);
@@ -7242,3 +7245,17 @@ pgstat_count_slru_truncate(int slru_idx)
 {
 	slru_entry(slru_idx)->m_truncate += 1;
 }
+
+/*
+ * sigUsr2Handler
+ *
+ * handle SIGUSR2 signal to print call stack of pgstat process.
+ */
+static void
+sigUsr2Handler(SIGNAL_ARGS)
+{
+	int			save_errno = errno;
+
+	LogBackTrace();
+	errno = save_errno;
+}
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index b7799ed..dd7c930 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -5149,6 +5149,26 @@ sigusr1_handler(SIGNAL_ARGS)
 		StartWorkerNeeded = true;
 	}
 
+	/* Process backtrace emit signal. */
+	if (CheckPostmasterSignal(PMSIGNAL_BACKTRACE_EMIT))
+	{
+		EmitProcSignalPrintCallStack();
+
+		/*
+		 * Pgstat process & syslogger process do not have access to ProcSignal,
+		 * multiplexing with SIGUSR1 is not possible for these processes so send
+		 * SIGUSR2 signal for them as multiplexing with SIGUSR1 is not possible.
+		 */
+		if (PgStatPID)
+			kill(PgStatPID, SIGUSR2);
+
+		if (SysLoggerPID)
+			kill(SysLoggerPID, SIGUSR2);
+
+		/* Print call stack for postmaster process. */
+		LogBackTrace();
+	}
+
 	/*
 	 * RECOVERY_STARTED and BEGIN_HOT_STANDBY signals are ignored in
 	 * unexpected states. If the startup process quickly starts up, completes
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index faa82ec..5f83ee3 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -145,6 +145,7 @@ static void logfile_rotate(bool time_based_rotation, int size_rotation_for);
 static char *logfile_getname(pg_time_t timestamp, const char *suffix);
 static void set_next_rotation_time(void);
 static void sigUsr1Handler(SIGNAL_ARGS);
+static void sigUsr2Handler(SIGNAL_ARGS);
 static void update_metainfo_datafile(void);
 
 
@@ -246,7 +247,7 @@ SysLoggerMain(int argc, char *argv[])
 	pqsignal(SIGALRM, SIG_IGN);
 	pqsignal(SIGPIPE, SIG_IGN);
 	pqsignal(SIGUSR1, sigUsr1Handler);	/* request log rotation */
-	pqsignal(SIGUSR2, SIG_IGN);
+	pqsignal(SIGUSR2, sigUsr2Handler);
 
 	/*
 	 * Reset some signals that are accepted by postmaster but not here
@@ -1563,3 +1564,16 @@ sigUsr1Handler(SIGNAL_ARGS)
 
 	errno = save_errno;
 }
+
+/*
+ * sigUsr2Handler - handle SIGUSR2 signal to print call stack.
+ */
+static void
+sigUsr2Handler(SIGNAL_ARGS)
+{
+	int			save_errno = errno;
+
+	LogBackTrace();
+
+	errno = save_errno;
+}
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index ffe67ac..f1d72e0 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -302,6 +302,31 @@ SendProcSignal(pid_t pid, ProcSignalReason reason, BackendId backendId)
 }
 
 /*
+ * EmitProcSignalPrintCallStack
+ *
+ * Send SIGUSR1 to all postgres backends by setting PROCSIG_BACKTRACE_PRINT, the
+ * postgres processes will print the backtrace once the signal is received.
+ */
+void
+EmitProcSignalPrintCallStack(void)
+{
+	for (int i = NumProcSignalSlots - 1; i >= 0; i--)
+	{
+		volatile ProcSignalSlot *slot = &ProcSignal->psh_slot[i];
+		pid_t		pid = slot->pss_pid;
+
+		if (pid != 0)
+		{
+			/* see SendProcSignal for details */
+			slot->pss_signalFlags[PROCSIG_BACKTRACE_PRINT] = true;
+
+			/* Signal SIGUSR1 to the process, so that they print backtrace. */
+			kill(pid, SIGUSR1);
+		}
+	}
+}
+
+/*
  * EmitProcSignalBarrier
  *		Send a signal to every Postgres process
  *
@@ -585,6 +610,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
 
+	if (CheckProcSignal(PROCSIG_BACKTRACE_PRINT))
+		LogBackTrace();
+
 	SetLatch(MyLatch);
 
 	latch_sigusr1_handler();
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index d822e82..3c3eb0d 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -215,3 +215,25 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_print_callstack - print callstack of process that are part of this
+ * instance.
+ *
+ * Permission checking for this function is managed through the normal
+ * GRANT system.
+ */
+Datum
+pg_print_callstack(PG_FUNCTION_ARGS)
+{
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	SendPostmasterSignal(PMSIGNAL_BACKTRACE_EMIT);
+#else
+	{
+		ereport(WARNING,
+				(errmsg("backtrace generation is not supported by this installation")));
+		PG_RETURN_BOOL(false);
+	}
+#endif
+	PG_RETURN_BOOL(true);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 7c5f7c7..1079a65 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -19,6 +19,7 @@
 
 #include "postgres.h"
 
+#include <execinfo.h>
 #include <fcntl.h>
 #include <limits.h>
 #include <signal.h>
@@ -2887,6 +2888,36 @@ FloatExceptionHandler(SIGNAL_ARGS)
 }
 
 /*
+ * LogBackTrace
+ *
+ * Get the backtrace and log the backtrace to log file.
+ */
+void
+LogBackTrace(void)
+{
+	int			save_errno = errno;
+
+	void	   *buf[100];
+	int			nframes;
+	char	  **strfrms;
+	StringInfoData errtrace;
+
+	nframes = backtrace(buf, lengthof(buf));
+	strfrms = backtrace_symbols(buf, nframes);
+	if (strfrms == NULL)
+		return;
+
+	initStringInfo(&errtrace);
+	for (int i = 0; i < nframes; i++)
+		appendStringInfo(&errtrace, "\n%s", strfrms[i]);
+	free(strfrms);
+
+	elog(LOG, "current backtrace:%s", errtrace.data);
+
+	errno = save_errno;
+}
+
+/*
  * RecoveryConflictInterrupt: out-of-line portion of recovery conflict
  * handling following receipt of SIGUSR1. Designed to be similar to die()
  * and StatementCancelHandler(). Called only by a normal user backend
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index c01da4b..8614d94 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11005,4 +11005,8 @@
   proname => 'is_normalized', prorettype => 'bool', proargtypes => 'text text',
   prosrc => 'unicode_is_normalized' },
 
-]
+{ oid => '4388', descr => 'print callstack of process that are part of this instance',
+  proname => 'pg_print_callstack', provolatile => 'v', prorettype => 'bool',
+  proargtypes => '', prosrc => 'pg_print_callstack' },
+
+]
\ No newline at end of file
diff --git a/src/include/storage/pmsignal.h b/src/include/storage/pmsignal.h
index 56c5ec4..cda2995 100644
--- a/src/include/storage/pmsignal.h
+++ b/src/include/storage/pmsignal.h
@@ -42,6 +42,8 @@ typedef enum
 	PMSIGNAL_START_WALRECEIVER, /* start a walreceiver */
 	PMSIGNAL_ADVANCE_STATE_MACHINE, /* advance postmaster's state machine */
 
+	PMSIGNAL_BACKTRACE_EMIT,	/* send PROCSIG_BACKTRACE_PRINT to all backend */
+
 	NUM_PMSIGNALS				/* Must be last value of enum! */
 } PMSignalReason;
 
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index 5cb3969..c8bbeae 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -43,6 +43,8 @@ typedef enum
 	PROCSIG_RECOVERY_CONFLICT_BUFFERPIN,
 	PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK,
 
+	PROCSIG_BACKTRACE_PRINT,	/* ask backend to print the current backtrace */
+
 	NUM_PROCSIGNALS				/* Must be last! */
 } ProcSignalReason;
 
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index bd30607..1188639 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -71,6 +71,8 @@ extern void RecoveryConflictInterrupt(ProcSignalReason reason); /* called from S
 extern void ProcessClientReadInterrupt(bool blocked);
 extern void ProcessClientWriteInterrupt(bool blocked);
 
+extern void LogBackTrace(void); /* Called from EmitProcSignalPrintCallStack */
+extern void EmitProcSignalPrintCallStack(void);
 extern void process_postgres_switches(int argc, char *argv[],
 									  GucContext ctx, const char **dbname);
 extern void PostgresMain(int argc, char *argv[],
-- 
1.8.3.1

#2Tom Lane
tgl@sss.pgh.pa.us
In reply to: vignesh C (#1)
Re: Printing backtrace of postgres processes

vignesh C <vignesh21@gmail.com> writes:

The idea here is to implement & expose pg_print_callstack function,
internally what this function does is, the connected backend will send
SIGUSR1 signal by setting PMSIGNAL_BACKTRACE_EMIT to the postmaster
process. Postmaster process will send a SIGUSR1 signal to the process
by setting PROCSIG_BACKTRACE_PRINT if the process has access to
ProcSignal. As syslogger process & Stats process don't have access to
ProcSignal, multiplexing with SIGUSR1 is not possible for these
processes, hence SIGUSR2 signal will be sent for these processes. Once
the process receives this signal it will log the backtrace of the
process.

Surely this is *utterly* unsafe. You can't do that sort of stuff in
a signal handler.

It might be all right to set a flag that would cause the next
CHECK_FOR_INTERRUPTS to print a backtrace, but I'm not sure
how useful that really is.

The proposed postmaster.c addition seems quite useless, as there
is exactly one stack trace it could ever log.

I would like to see some discussion of the security implications
of such a feature, as well. ("There aren't any" is the wrong
answer.)

regards, tom lane

#3vignesh C
vignesh21@gmail.com
In reply to: Tom Lane (#2)
Re: Printing backtrace of postgres processes

On Sun, Nov 22, 2020 at 11:55 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:

vignesh C <vignesh21@gmail.com> writes:

The idea here is to implement & expose pg_print_callstack function,
internally what this function does is, the connected backend will send
SIGUSR1 signal by setting PMSIGNAL_BACKTRACE_EMIT to the postmaster
process. Postmaster process will send a SIGUSR1 signal to the process
by setting PROCSIG_BACKTRACE_PRINT if the process has access to
ProcSignal. As syslogger process & Stats process don't have access to
ProcSignal, multiplexing with SIGUSR1 is not possible for these
processes, hence SIGUSR2 signal will be sent for these processes. Once
the process receives this signal it will log the backtrace of the
process.

Surely this is *utterly* unsafe. You can't do that sort of stuff in
a signal handler.

It might be all right to set a flag that would cause the next
CHECK_FOR_INTERRUPTS to print a backtrace, but I'm not sure
how useful that really is.

The proposed postmaster.c addition seems quite useless, as there
is exactly one stack trace it could ever log.

I would like to see some discussion of the security implications
of such a feature, as well. ("There aren't any" is the wrong
answer.)

Hi Hackers,

Any thoughts on the security implication for this feature.

Regards,
Vignesh
EnterpriseDB: http://www.enterprisedb.com

#4Craig Ringer
craig.ringer@enterprisedb.com
In reply to: Tom Lane (#2)
Re: Printing backtrace of postgres processes

Surely this is *utterly* unsafe. You can't do that sort of stuff in
a signal handler.

Not safely, anyway. The signal handler could be called in the middle
of a malloc(), a pfree(), or all sorts of other exciting
circumstances. It'd have to be extremely careful to use only local
resources on the stack and I don't see how that's feasible here.

It'll work - most of the time. But it could explode messily and
excitingly just when you actually need it to work properly, which is
rarely what you want from features clearly intended for production
debugging.

(It'd be interesting to add some test infrastructure that helps us
fire signal handlers at awkward times in a controlled manner, so we
could predictably test signal handler re-entrancy and concurrency
behaviour.)

It might be all right to set a flag that would cause the next
CHECK_FOR_INTERRUPTS to print a backtrace, but I'm not sure
how useful that really is.

I find that when I most often want a backtrace of a running, live
backend, it's because the backend is doing something that isn't
passing a CHECK_FOR_INTERRUPTS() so it's not responding to signals. So
it wouldn't help if a backend is waiting on an LWLock, busy in a
blocking call to some loaded library, a blocking syscall, etc. But
there are enough other times I want live backtraces, and I'm not the
only one whose needs matter.

It'd be kinda handy when collecting samples of some backend's activity
when it's taking an excessively long time doing something
indeterminate. I generally use a gdb script for that because
unfortunately the Linux trace tool situation is so hopeless that I
can't rely on perf or systemtap being present, working, and
understanding the same command line arguments across various distros
and versions, so something built-in would be convenient. That
convenience would probably be counterbalanced by the hassle of
extracting the results from the log files unless it's possible to use
a function in one backend to query the stack in another instead of
just writing it to the log.

So a weak +1 from me, assuming printing stacks from
CHECK_FOR_INTERRUPTS() to the log. We already have most of the
infrastructure for that so the change is trivial, and we already trust
anyone who can read the log, so it seems like a pretty low-cost,
low-risk change.

Somewhat more interested favour if the results can be obtained from a
function or view from another backend, but then the security
implications get a bit more exciting if we let non-superusers do it.

You may recall that I wanted to do something similar a while ago in
order to request MemoryContextStats() without needing to attach gdb
and invoke a function manually using ptrace(). Also changes to support
reading TCP socket state for a process. So I find this sort of thing
useful in general.

If we're querying one backend from another we could read its stack
with ptrace() and unwind it with libunwind within the requesting
backend, which would be a whole lot safer to execute and would work
fine even when blocked in syscalls or synchronous library calls. See
the eu-stack command from elfutils. If the target backend had shared
libraries loaded that the requested backend didn't, libunwind could
load the debuginfo for us if available. The downsides would be that
many system lockdowns disable ptrace() for non-root even for
same-user-owned processes, and that we'd need to depend on libunwind
(or other platform equivalents) so it'd probably be a contrib. In
which case you have to wonder if it's that much better than running
`system("eu-stack $pid")` in plperl or a trivial custom C extension
function.

I would like to see some discussion of the security implications
of such a feature, as well. ("There aren't any" is the wrong
answer.)

If the stack only goes to the log, I actually don't think there are
significant security implications beyond those we already have with
our existing backtrace printing features. We already trust anyone who
can read the log almost completely, and we can already emit stacks to
the log. But I'd still want it to be gated superuser-only, or a role
that's GRANTable by superuser only by default, since it exposes
arbitrary internals of the server.

"same user id" matching is not sufficient. A less-privileged session
user might be calling into SECURITY DEFINER code, code from a
privileged view, or sensitive C library code. Checking for the
effective user is no better because the effective user might be less
privileged at the moment the bt is requested, but the state up-stack
might reveal sensitive information from a more privileged user.

#5Tom Lane
tgl@sss.pgh.pa.us
In reply to: Craig Ringer (#4)
Re: Printing backtrace of postgres processes

Craig Ringer <craig.ringer@enterprisedb.com> writes:

I would like to see some discussion of the security implications
of such a feature, as well. ("There aren't any" is the wrong
answer.)

If the stack only goes to the log, I actually don't think there are
significant security implications beyond those we already have with
our existing backtrace printing features. We already trust anyone who
can read the log almost completely, and we can already emit stacks to
the log. But I'd still want it to be gated superuser-only, or a role
that's GRANTable by superuser only by default, since it exposes
arbitrary internals of the server.

The concerns that I had were that the patch as submitted provides a
mechanism that causes ALL processes in the system to dump backtraces,
not a targeted request; and that it allows any user to issue such
requests at an unbounded rate. That seems like a really easy route
to denial of service. There's also a question of whether you'd even
get intelligible results from dozens of processes simultaneously
dumping many-line messages to the same place. (This might work out
all right if you're using our syslogger, but it probably would not
with any other logging technology.)

I'd feel better about it if the mechanism had you specify exactly
one target process, and were restricted to a superuser requestor.

I'm not excited about adding on frammishes like letting one process
extract another's stack trace. I think that just adds more points
of failure, which is a bad thing in a feature that you're only going
to care about when things are a bit pear-shaped already.

regards, tom lane

#6Craig Ringer
craig.ringer@enterprisedb.com
In reply to: Tom Lane (#5)
Re: Printing backtrace of postgres processes

On Tue, 1 Dec 2020 at 07:04, Tom Lane <tgl@sss.pgh.pa.us> wrote:

I'd feel better about it if the mechanism had you specify exactly
one target process, and were restricted to a superuser requestor.

Er, rather. I actually assumed the former was the case already, not
having looked closely yet.

#7Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#2)
Re: Printing backtrace of postgres processes

Hi,

On 2020-11-22 01:25:08 -0500, Tom Lane wrote:

Surely this is *utterly* unsafe. You can't do that sort of stuff in
a signal handler.

That's of course true for the current implementation - but I don't think
it's a fundamental constraint. With a bit of care backtrace() and
backtrace_symbols() itself can be signal safe:

backtrace() and backtrace_symbols_fd() don't call malloc() explicitly, but they are part of libgcc, which gets loaded dynamically when first
used. Dynamic loading usually triggers a call to malloc(3). If you need certain calls to these two functions to not allocate memory (in signal
handlers, for example), you need to make sure libgcc is loaded beforehand.

It should be quite doable to emit such backtraces directly to stderr,
instead of using appendStringInfoString()/elog(). Or even use a static
buffer.

It does have quite some appeal to be able to debug production workloads
where queries can't be cancelled etc. And knowing that backtraces
reliably work in case of SIGQUIT etc is also nice...

I would like to see some discussion of the security implications
of such a feature, as well. ("There aren't any" is the wrong
answer.)

+1

Greetings,

Andres Freund

#8Andres Freund
andres@anarazel.de
In reply to: Craig Ringer (#4)
Re: Printing backtrace of postgres processes

Hi,

On 2020-11-30 13:35:46 +0800, Craig Ringer wrote:

I find that when I most often want a backtrace of a running, live
backend, it's because the backend is doing something that isn't
passing a CHECK_FOR_INTERRUPTS() so it's not responding to signals. So
it wouldn't help if a backend is waiting on an LWLock, busy in a
blocking call to some loaded library, a blocking syscall, etc. But
there are enough other times I want live backtraces, and I'm not the
only one whose needs matter.

Random thought: Wonder if it could be worth adding a conditionally
compiled mode where we track what the longest time between two
CHECK_FOR_INTERRUPTS() calls is (with some extra logic for client
IO).

Obviously the regression tests don't tend to hit the worst cases of
CFR() less code, but even if they did, we currently wouldn't know from
running the regression tests.

Greetings,

Andres Freund

#9Craig Ringer
craig.ringer@enterprisedb.com
In reply to: Andres Freund (#8)
Re: Printing backtrace of postgres processes

On Tue, 1 Dec 2020 at 11:31, Andres Freund <andres@anarazel.de> wrote:

Hi,

On 2020-11-30 13:35:46 +0800, Craig Ringer wrote:

I find that when I most often want a backtrace of a running, live
backend, it's because the backend is doing something that isn't
passing a CHECK_FOR_INTERRUPTS() so it's not responding to signals. So
it wouldn't help if a backend is waiting on an LWLock, busy in a
blocking call to some loaded library, a blocking syscall, etc. But
there are enough other times I want live backtraces, and I'm not the
only one whose needs matter.

Random thought: Wonder if it could be worth adding a conditionally
compiled mode where we track what the longest time between two
CHECK_FOR_INTERRUPTS() calls is (with some extra logic for client
IO).

Obviously the regression tests don't tend to hit the worst cases of
CFR() less code, but even if they did, we currently wouldn't know from
running the regression tests.

We can probably determine that just as well with a perf or systemtap
run on an --enable-dtrace build. Just tag CHECK_FOR_INTERRUPTS() with
a SDT marker then record the timings.

It might be convenient to have it built-in I guess, but if we tag the
site and do the timing/tracing externally we don't have to bother
about conditional compilation and special builds.

#10Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#7)
Re: Printing backtrace of postgres processes

Andres Freund <andres@anarazel.de> writes:

It should be quite doable to emit such backtraces directly to stderr,
instead of using appendStringInfoString()/elog().

No, please no.

(1) On lots of logging setups (think syslog), anything that goes to
stderr is just going to wind up in the bit bucket. I realize that
we have that issue already for memory context dumps on OOM errors,
but that doesn't make it a good thing.

(2) You couldn't really write "to stderr", only to fileno(stderr),
creating issues about interleaving of the output with regular stderr
output. For instance it's quite likely that the backtrace would
appear before stderr output that had actually been emitted earlier,
which'd be tremendously confusing.

(3) This isn't going to do anything good for my concerns about interleaved
output from different processes, either.

regards, tom lane

#11vignesh C
vignesh21@gmail.com
In reply to: Tom Lane (#10)
Re: Printing backtrace of postgres processes

On Tue, Dec 1, 2020 at 9:31 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:

Andres Freund <andres@anarazel.de> writes:

It should be quite doable to emit such backtraces directly to stderr,
instead of using appendStringInfoString()/elog().

No, please no.

(1) On lots of logging setups (think syslog), anything that goes to
stderr is just going to wind up in the bit bucket. I realize that
we have that issue already for memory context dumps on OOM errors,
but that doesn't make it a good thing.

(2) You couldn't really write "to stderr", only to fileno(stderr),
creating issues about interleaving of the output with regular stderr
output. For instance it's quite likely that the backtrace would
appear before stderr output that had actually been emitted earlier,
which'd be tremendously confusing.

(3) This isn't going to do anything good for my concerns about interleaved
output from different processes, either.

I felt if we are not agreeing on logging on the stderr, even using
static buffer we might not be able to log as
send_message_to_server_log calls appendStringInfo. I felt that doing
it from CHECK_FOR_INTERRUPTS may be better.

Regards,
Vignesh
EnterpriseDB: http://www.enterprisedb.com

#12vignesh C
vignesh21@gmail.com
In reply to: vignesh C (#11)
1 attachment(s)
Re: Printing backtrace of postgres processes

On Tue, Dec 1, 2020 at 2:15 PM vignesh C <vignesh21@gmail.com> wrote:

On Tue, Dec 1, 2020 at 9:31 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:

Andres Freund <andres@anarazel.de> writes:

It should be quite doable to emit such backtraces directly to stderr,
instead of using appendStringInfoString()/elog().

No, please no.

(1) On lots of logging setups (think syslog), anything that goes to
stderr is just going to wind up in the bit bucket. I realize that
we have that issue already for memory context dumps on OOM errors,
but that doesn't make it a good thing.

(2) You couldn't really write "to stderr", only to fileno(stderr),
creating issues about interleaving of the output with regular stderr
output. For instance it's quite likely that the backtrace would
appear before stderr output that had actually been emitted earlier,
which'd be tremendously confusing.

(3) This isn't going to do anything good for my concerns about interleaved
output from different processes, either.

I felt if we are not agreeing on logging on the stderr, even using
static buffer we might not be able to log as
send_message_to_server_log calls appendStringInfo. I felt that doing
it from CHECK_FOR_INTERRUPTS may be better.

I have implemented printing of backtrace based on handling it in
CHECK_FOR_INTERRUPTS. This patch also includes the change to allow
getting backtrace of any particular process based on the suggestions.
Attached patch has the implementation for the same.
Thoughts?

Regards,
Vignesh
EnterpriseDB: http://www.enterprisedb.com

Attachments:

0001-Print-backtrace-of-postgres-process-that-are-part-of.patchtext/x-patch; charset=US-ASCII; name=0001-Print-backtrace-of-postgres-process-that-are-part-of.patchDownload
From ffcd85f310dcedbe21f78ed4a7f7b281dc4d6ad8 Mon Sep 17 00:00:00 2001
From: Vignesh C <vignesh21@gmail.com>
Date: Sun, 22 Nov 2020 05:58:24 +0530
Subject: [PATCH] Print backtrace of postgres process that are part of this
 instance.

The idea here is to implement & expose pg_print_callstack function, internally
what this function does is, the connected backend will send SIGUSR1 signal by
setting PMSIGNAL_BACKTRACE_EMIT to the postmaster process. Postmaster process
will send SIGUSR1 signal to process by setting PROCSIG_BACKTRACE_PRINT if the
process that have access to ProcSignal. As syslogger process & Stats process
don't have access to ProcSignal, multiplexing with SIGUSR1 is not possible
for these processes, hence SIGUSR2 signal will be sent for these process.
Once the process receives this signal it will log the backtrace of the process.
---
 src/backend/postmaster/autovacuum.c   |  4 ++
 src/backend/postmaster/checkpointer.c |  5 +++
 src/backend/postmaster/interrupt.c    |  5 +++
 src/backend/postmaster/pgstat.c       | 34 ++++++++++++++++-
 src/backend/postmaster/postmaster.c   | 29 ++++++++++++++
 src/backend/postmaster/syslogger.c    | 30 ++++++++++++++-
 src/backend/storage/ipc/procsignal.c  | 50 ++++++++++++++++++++++++
 src/backend/storage/ipc/signalfuncs.c | 67 ++++++++++++++++++++++++++++++++
 src/backend/tcop/postgres.c           | 38 ++++++++++++++++++
 src/backend/utils/init/globals.c      |  1 +
 src/bin/pg_ctl/t/005_backtrace.pl     | 72 +++++++++++++++++++++++++++++++++++
 src/include/catalog/pg_proc.dat       |  9 ++++-
 src/include/miscadmin.h               |  2 +
 src/include/storage/pmsignal.h        |  2 +
 src/include/storage/procsignal.h      |  4 ++
 src/include/tcop/tcopprot.h           |  1 +
 16 files changed, 350 insertions(+), 3 deletions(-)
 create mode 100644 src/bin/pg_ctl/t/005_backtrace.pl

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index aa5b97f..597f14b 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -832,6 +832,10 @@ HandleAutoVacLauncherInterrupts(void)
 	if (ProcSignalBarrierPending)
 		ProcessProcSignalBarrier();
 
+	/* Process printing back trace */
+	if (PrintBacktracePending)
+		LogBackTrace();
+
 	/* Process sinval catchup interrupts that happened while sleeping */
 	ProcessCatchupInterrupt();
 }
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 429c801..5f9501b 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -57,6 +57,7 @@
 #include "storage/shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
+#include "tcop/tcopprot.h"
 #include "utils/guc.h"
 #include "utils/memutils.h"
 #include "utils/resowner.h"
@@ -547,6 +548,10 @@ HandleCheckpointerInterrupts(void)
 	if (ProcSignalBarrierPending)
 		ProcessProcSignalBarrier();
 
+	/* Process printing back trace */
+	if (PrintBacktracePending)
+		LogBackTrace();
+
 	if (ConfigReloadPending)
 	{
 		ConfigReloadPending = false;
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index ee7dbf9..d341d6f 100644
--- a/src/backend/postmaster/interrupt.c
+++ b/src/backend/postmaster/interrupt.c
@@ -21,6 +21,7 @@
 #include "storage/ipc.h"
 #include "storage/latch.h"
 #include "storage/procsignal.h"
+#include "tcop/tcopprot.h"
 #include "utils/guc.h"
 
 volatile sig_atomic_t ConfigReloadPending = false;
@@ -41,6 +42,10 @@ HandleMainLoopInterrupts(void)
 		ProcessConfigFile(PGC_SIGHUP);
 	}
 
+	/* Process printing back trace */
+	if (PrintBacktracePending)
+		LogBackTrace();
+
 	if (ShutdownRequestPending)
 		proc_exit(0);
 }
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 7c75a25..a4ecf52 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -63,6 +63,7 @@
 #include "storage/pg_shmem.h"
 #include "storage/procsignal.h"
 #include "storage/sinvaladt.h"
+#include "tcop/tcopprot.h"
 #include "utils/ascii.h"
 #include "utils/guc.h"
 #include "utils/memutils.h"
@@ -381,6 +382,8 @@ static void pgstat_recv_checksum_failure(PgStat_MsgChecksumFailure *msg, int len
 static void pgstat_recv_replslot(PgStat_MsgReplSlot *msg, int len);
 static void pgstat_recv_tempfile(PgStat_MsgTempFile *msg, int len);
 
+static void sigUsr2Handler(SIGNAL_ARGS);
+
 /* ------------------------------------------------------------
  * Public functions called from postmaster follow
  * ------------------------------------------------------------
@@ -4703,7 +4706,7 @@ PgstatCollectorMain(int argc, char *argv[])
 	pqsignal(SIGALRM, SIG_IGN);
 	pqsignal(SIGPIPE, SIG_IGN);
 	pqsignal(SIGUSR1, SIG_IGN);
-	pqsignal(SIGUSR2, SIG_IGN);
+	pqsignal(SIGUSR2, sigUsr2Handler);
 	/* Reset some signals that are accepted by postmaster but not here */
 	pqsignal(SIGCHLD, SIG_DFL);
 	PG_SETMASK(&UnBlockSig);
@@ -4763,6 +4766,10 @@ PgstatCollectorMain(int argc, char *argv[])
 				ProcessConfigFile(PGC_SIGHUP);
 			}
 
+			/* Process printing back trace */
+			if (PrintBacktracePending)
+				LogBackTrace();
+
 			/*
 			 * Write the stats file(s) if a new request has arrived that is
 			 * not satisfied by existing file(s).
@@ -7283,3 +7290,28 @@ pgstat_count_slru_truncate(int slru_idx)
 {
 	slru_entry(slru_idx)->m_truncate += 1;
 }
+
+/*
+ * sigUsr2Handler
+ *
+ * handle SIGUSR2 signal to print call stack of pgstat process.
+ */
+static void
+sigUsr2Handler(SIGNAL_ARGS)
+{
+	int			save_errno = errno;
+
+	/*
+	 * Don't joggle the elbow of proc_exit
+	 */
+	if (!proc_exit_inprogress)
+	{
+		InterruptPending = true;
+		PrintBacktracePending = true;
+	}
+
+	/* If we're still here, waken anything waiting on the process latch */
+	SetLatch(MyLatch);
+
+	errno = save_errno;
+}
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 5d09822..156906b 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -5134,6 +5134,31 @@ ExitPostmaster(int status)
 	proc_exit(status);
 }
 
+bool
+HandlePrintCallStack(int bt_pid)
+{
+	bool found = EmitProcSignalPrintCallStack(bt_pid);
+
+	/*
+	 * Pgstat process & syslogger process do not have access to ProcSignal,
+	 * multiplexing with SIGUSR1 is not possible for these processes so send
+	 * SIGUSR2 signal for them as multiplexing with SIGUSR1 is not possible.
+	 */
+	if (PgStatPID && (!bt_pid || (!found && bt_pid == PgStatPID)))
+	{
+		found = true;
+		kill(PgStatPID, SIGUSR2);
+	}
+
+	if (SysLoggerPID && (!bt_pid || (!found && bt_pid == SysLoggerPID)))
+	{
+		found = true;
+		kill(SysLoggerPID, SIGUSR2);
+	}
+
+	return found;
+}
+
 /*
  * sigusr1_handler - handle signal conditions from child processes
  */
@@ -5157,6 +5182,10 @@ sigusr1_handler(SIGNAL_ARGS)
 		StartWorkerNeeded = true;
 	}
 
+	/* Process backtrace emit signal. */
+	if (CheckPostmasterSignal(PMSIGNAL_BACKTRACE_EMIT))
+		HandlePrintCallStack(0);
+
 	/*
 	 * RECOVERY_STARTED and BEGIN_HOT_STANDBY signals are ignored in
 	 * unexpected states. If the startup process quickly starts up, completes
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index faa82ec..b42eff1 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -145,6 +145,7 @@ static void logfile_rotate(bool time_based_rotation, int size_rotation_for);
 static char *logfile_getname(pg_time_t timestamp, const char *suffix);
 static void set_next_rotation_time(void);
 static void sigUsr1Handler(SIGNAL_ARGS);
+static void sigUsr2Handler(SIGNAL_ARGS);
 static void update_metainfo_datafile(void);
 
 
@@ -246,7 +247,7 @@ SysLoggerMain(int argc, char *argv[])
 	pqsignal(SIGALRM, SIG_IGN);
 	pqsignal(SIGPIPE, SIG_IGN);
 	pqsignal(SIGUSR1, sigUsr1Handler);	/* request log rotation */
-	pqsignal(SIGUSR2, SIG_IGN);
+	pqsignal(SIGUSR2, sigUsr2Handler);
 
 	/*
 	 * Reset some signals that are accepted by postmaster but not here
@@ -320,6 +321,10 @@ SysLoggerMain(int argc, char *argv[])
 		/* Clear any already-pending wakeups */
 		ResetLatch(MyLatch);
 
+		/* Process printing back trace */
+		if (PrintBacktracePending)
+			LogBackTrace();
+
 		/*
 		 * Process any requests or signals received recently.
 		 */
@@ -1563,3 +1568,26 @@ sigUsr1Handler(SIGNAL_ARGS)
 
 	errno = save_errno;
 }
+
+/*
+ * sigUsr2Handler - handle SIGUSR2 signal to print call stack.
+ */
+static void
+sigUsr2Handler(SIGNAL_ARGS)
+{
+	int			save_errno = errno;
+
+	/*
+	 * Don't joggle the elbow of proc_exit
+	 */
+	if (!proc_exit_inprogress)
+	{
+		InterruptPending = true;
+		PrintBacktracePending = true;
+	}
+
+	/* If we're still here, waken anything waiting on the process latch */
+	SetLatch(MyLatch);
+
+	errno = save_errno;
+}
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index ffe67ac..b4fe978 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -302,6 +302,38 @@ SendProcSignal(pid_t pid, ProcSignalReason reason, BackendId backendId)
 }
 
 /*
+ * EmitProcSignalPrintCallStack
+ *
+ * Send SIGUSR1 to all postgres backends by setting PROCSIG_BACKTRACE_PRINT, the
+ * postgres processes will print the backtrace once the signal is received.
+ */
+bool
+EmitProcSignalPrintCallStack(int bt_pid)
+{
+	bool found = false;
+	for (int i = NumProcSignalSlots - 1; i >= 0; i--)
+	{
+		volatile ProcSignalSlot *slot = &ProcSignal->psh_slot[i];
+		pid_t		pid = slot->pss_pid;
+
+		if (pid != 0 && (pid == bt_pid || bt_pid == 0))
+		{
+			found = true;
+
+			/* see SendProcSignal for details */
+			slot->pss_signalFlags[PROCSIG_BACKTRACE_PRINT] = true;
+
+			/* Signal SIGUSR1 to the process, so that they print backtrace. */
+			kill(pid, SIGUSR1);
+			if (bt_pid != 0)
+				return found;
+		}
+	}
+
+	return found;
+}
+
+/*
  * EmitProcSignalBarrier
  *		Send a signal to every Postgres process
  *
@@ -441,6 +473,21 @@ HandleProcSignalBarrierInterrupt(void)
 }
 
 /*
+ * Handle receipt of an print backtrace.
+ *
+ * Note: this is called within a signal handler!  All we can do is set
+ * a flag that will cause the next CHECK_FOR_INTERRUPTS() to invoke
+ * LogBackTrace().
+ */
+static void
+HandlePrintBacktraceInterrupt(void)
+{
+	InterruptPending = true;
+	PrintBacktracePending = true;
+	/* latch will be set by procsignal_sigusr1_handler */
+}
+
+/*
  * Perform global barrier related interrupt checking.
  *
  * Any backend that participates in ProcSignal signaling must arrange to
@@ -585,6 +632,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
 
+	if (CheckProcSignal(PROCSIG_BACKTRACE_PRINT))
+		HandlePrintBacktraceInterrupt();
+
 	SetLatch(MyLatch);
 
 	latch_sigusr1_handler();
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index d822e82..7810936 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -22,6 +22,7 @@
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
+#include "tcop/tcopprot.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 
@@ -215,3 +216,69 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_print_callstack - print callstack of process that are part of this
+ * instance.
+ *
+ * Permission checking for this function is managed through the normal
+ * GRANT system.
+ */
+Datum
+pg_print_callstack(PG_FUNCTION_ARGS)
+{
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser to print backtraces")));
+
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	SendPostmasterSignal(PMSIGNAL_BACKTRACE_EMIT);
+#else
+	{
+		ereport(WARNING,
+				(errmsg("backtrace generation is not supported by this installation")));
+		PG_RETURN_BOOL(false);
+	}
+#endif
+	PG_RETURN_BOOL(true);
+}
+
+/*
+ * pg_print_callstack - print callstack of process that are part of this
+ * instance.
+ *
+ * Permission checking for this function is managed through the normal
+ * GRANT system.
+ */
+Datum
+pg_print_callstack_pid(PG_FUNCTION_ARGS)
+{
+	int	bt_pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser to print backtraces")));
+
+	if (bt_pid <= 0 || bt_pid > INT_MAX)
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("value should between 1 and %d",
+						INT_MAX)));
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	{
+		if(HandlePrintCallStack(bt_pid))
+			PG_RETURN_BOOL(true);
+
+		ereport(WARNING,
+				(errmsg("specified pid is not part of this instance")));
+		PG_RETURN_BOOL(false);
+	}
+#else
+	{
+		ereport(WARNING,
+				(errmsg("backtrace generation is not supported by this installation")));
+		PG_RETURN_BOOL(false);
+	}
+#endif
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 3679799..5ac2c51 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -19,6 +19,7 @@
 
 #include "postgres.h"
 
+#include <execinfo.h>
 #include <fcntl.h>
 #include <limits.h>
 #include <signal.h>
@@ -2887,6 +2888,39 @@ FloatExceptionHandler(SIGNAL_ARGS)
 }
 
 /*
+ * LogBackTrace
+ *
+ * Get the backtrace and log the backtrace to log file.
+ */
+void
+LogBackTrace(void)
+{
+	int			save_errno = errno;
+
+	void	   *buf[100];
+	int			nframes;
+	char	  **strfrms;
+	StringInfoData errtrace;
+
+	/* OK to process messages.  Reset the flag saying there are more to do. */
+	PrintBacktracePending = false;
+
+	nframes = backtrace(buf, lengthof(buf));
+	strfrms = backtrace_symbols(buf, nframes);
+	if (strfrms == NULL)
+		return;
+
+	initStringInfo(&errtrace);
+	for (int i = 0; i < nframes; i++)
+		appendStringInfo(&errtrace, "\n%s", strfrms[i]);
+	free(strfrms);
+
+	elog(LOG, "current backtrace:%s", errtrace.data);
+
+	errno = save_errno;
+}
+
+/*
  * RecoveryConflictInterrupt: out-of-line portion of recovery conflict
  * handling following receipt of SIGUSR1. Designed to be similar to die()
  * and StatementCancelHandler(). Called only by a normal user backend
@@ -3214,6 +3248,10 @@ ProcessInterrupts(void)
 
 	if (ParallelMessagePending)
 		HandleParallelMessages();
+
+	/* Process printing back trace */
+	if (PrintBacktracePending)
+		LogBackTrace();
 }
 
 
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 6ab8216..ea3cca2 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -36,6 +36,7 @@ volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
+volatile sig_atomic_t PrintBacktracePending = false;
 
 int			MyProcPid;
 pg_time_t	MyStartTime;
diff --git a/src/bin/pg_ctl/t/005_backtrace.pl b/src/bin/pg_ctl/t/005_backtrace.pl
new file mode 100644
index 0000000..12d2535
--- /dev/null
+++ b/src/bin/pg_ctl/t/005_backtrace.pl
@@ -0,0 +1,72 @@
+use strict;
+use warnings;
+
+use PostgresNode;
+use TestLib;
+use Test::More tests => 2;
+use Time::HiRes qw(usleep);
+
+# Set up node with logging collector
+my $node = get_new_node('primary');
+$node->init();
+$node->append_conf(
+	'postgresql.conf', qq(
+logging_collector = on
+lc_messages = 'C'
+));
+
+$node->start();
+
+# Verify that log output gets to the file
+$node->psql('postgres', 'select pg_print_callstack()');
+
+# might need to retry if logging collector process is slow...
+my $max_attempts = 180 * 10;
+
+my $current_logfiles;
+for (my $attempts = 0; $attempts < $max_attempts; $attempts++)
+{
+	eval {
+		$current_logfiles = slurp_file($node->data_dir . '/current_logfiles');
+	};
+	last unless $@;
+	usleep(100_000);
+}
+die $@ if $@;
+
+note "current_logfiles = $current_logfiles";
+
+like(
+	$current_logfiles,
+	qr|^stderr log/postgresql-.*log$|,
+	'current_logfiles is sane');
+
+my $lfname = $current_logfiles;
+$lfname =~ s/^stderr //;
+chomp $lfname;
+
+my $first_logfile;
+my $bt_occurence_count;
+
+# Verify that the backtraces of the processes are logged into logfile.
+for (my $attempts = 0; $attempts < $max_attempts; $attempts++)
+{
+	$first_logfile = $node->data_dir . '/' . $lfname;
+	chomp $first_logfile;
+	print "file is $first_logfile";
+	open my $fh, '<', $first_logfile or die "Could not open '$first_logfile' $!";
+	while (my $line = <$fh>) 
+	{
+    		chomp $line;
+	    	if ($line =~ m/current backtrace/)
+		{
+        		$bt_occurence_count++;
+		}
+	}
+	last if $bt_occurence_count == 8;
+	usleep(100_000);
+}
+
+is($bt_occurence_count, 8, 'found expected back trace in the log file');
+
+$node->stop();
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index fc2202b..8ef1425 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11010,4 +11010,11 @@
   proname => 'is_normalized', prorettype => 'bool', proargtypes => 'text text',
   prosrc => 'unicode_is_normalized' },
 
-]
+{ oid => '4388', descr => 'print callstack of process that are part of this instance',
+  proname => 'pg_print_callstack', provolatile => 'v', prorettype => 'bool',
+  proargtypes => '', prosrc => 'pg_print_callstack' },
+{ oid => '4389', descr => 'print callstack of process that are part of this instance',
+  proname => 'pg_print_callstack', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_print_callstack_pid' },
+
+]
\ No newline at end of file
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 72e3352..f07b72a 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -91,6 +91,8 @@ extern PGDLLIMPORT volatile uint32 InterruptHoldoffCount;
 extern PGDLLIMPORT volatile uint32 QueryCancelHoldoffCount;
 extern PGDLLIMPORT volatile uint32 CritSectionCount;
 
+extern PGDLLIMPORT volatile sig_atomic_t PrintBacktracePending;
+
 /* in tcop/postgres.c */
 extern void ProcessInterrupts(void);
 
diff --git a/src/include/storage/pmsignal.h b/src/include/storage/pmsignal.h
index 56c5ec4..cda2995 100644
--- a/src/include/storage/pmsignal.h
+++ b/src/include/storage/pmsignal.h
@@ -42,6 +42,8 @@ typedef enum
 	PMSIGNAL_START_WALRECEIVER, /* start a walreceiver */
 	PMSIGNAL_ADVANCE_STATE_MACHINE, /* advance postmaster's state machine */
 
+	PMSIGNAL_BACKTRACE_EMIT,	/* send PROCSIG_BACKTRACE_PRINT to all backend */
+
 	NUM_PMSIGNALS				/* Must be last value of enum! */
 } PMSignalReason;
 
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index 5cb3969..44f42fd 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -43,6 +43,8 @@ typedef enum
 	PROCSIG_RECOVERY_CONFLICT_BUFFERPIN,
 	PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK,
 
+	PROCSIG_BACKTRACE_PRINT,	/* ask backend to print the current backtrace */
+
 	NUM_PROCSIGNALS				/* Must be last! */
 } ProcSignalReason;
 
@@ -71,5 +73,7 @@ extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
 
 extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
+extern bool EmitProcSignalPrintCallStack(int bt_pid);
+extern bool HandlePrintCallStack(int bt_pid);
 
 #endif							/* PROCSIGNAL_H */
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index bd30607..a2b30d1 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -71,6 +71,7 @@ extern void RecoveryConflictInterrupt(ProcSignalReason reason); /* called from S
 extern void ProcessClientReadInterrupt(bool blocked);
 extern void ProcessClientWriteInterrupt(bool blocked);
 
+extern void LogBackTrace(void); /* Called from EmitProcSignalPrintCallStack */
 extern void process_postgres_switches(int argc, char *argv[],
 									  GucContext ctx, const char **dbname);
 extern void PostgresMain(int argc, char *argv[],
-- 
1.8.3.1

#13Peter Eisentraut
peter.eisentraut@enterprisedb.com
In reply to: vignesh C (#12)
Re: Printing backtrace of postgres processes

On 2020-12-08 10:38, vignesh C wrote:

I have implemented printing of backtrace based on handling it in
CHECK_FOR_INTERRUPTS. This patch also includes the change to allow
getting backtrace of any particular process based on the suggestions.
Attached patch has the implementation for the same.
Thoughts?

Are we willing to use up a signal for this?

#14Andres Freund
andres@anarazel.de
In reply to: Peter Eisentraut (#13)
Re: Printing backtrace of postgres processes

On 2021-01-15 09:53:05 +0100, Peter Eisentraut wrote:

On 2020-12-08 10:38, vignesh C wrote:

I have implemented printing of backtrace based on handling it in
CHECK_FOR_INTERRUPTS. This patch also includes the change to allow
getting backtrace of any particular process based on the suggestions.
Attached patch has the implementation for the same.
Thoughts?

Are we willing to use up a signal for this?

Why is a full signal needed? Seems the procsignal infrastructure should
suffice?

#15vignesh C
vignesh21@gmail.com
In reply to: Andres Freund (#14)
Re: Printing backtrace of postgres processes

On Sat, Jan 16, 2021 at 1:40 AM Andres Freund <andres@anarazel.de> wrote:

On 2021-01-15 09:53:05 +0100, Peter Eisentraut wrote:

On 2020-12-08 10:38, vignesh C wrote:

I have implemented printing of backtrace based on handling it in
CHECK_FOR_INTERRUPTS. This patch also includes the change to allow
getting backtrace of any particular process based on the suggestions.
Attached patch has the implementation for the same.
Thoughts?

Are we willing to use up a signal for this?

Why is a full signal needed? Seems the procsignal infrastructure should
suffice?

Most of the processes have access to ProcSignal, for these processes
printing of callstack signal was handled by using ProcSignal. Pgstat
process & syslogger process do not have access to ProcSignal,
multiplexing with SIGUSR1 is not possible for these processes. So I
handled the printing of callstack for pgstat process & syslogger using
the SIGUSR2 signal.
This is because shared memory is detached before pgstat & syslogger
process is started by using the below:
/* Drop our connection to postmaster's shared memory, as well */
dsm_detach_all();
PGSharedMemoryDetach();

Regards,
Vignesh
EnterpriseDB: http://www.enterprisedb.com

#16Andres Freund
andres@anarazel.de
In reply to: vignesh C (#15)
Re: Printing backtrace of postgres processes

Hi,

On Sat, Jan 16, 2021, at 09:34, vignesh C wrote:

On Sat, Jan 16, 2021 at 1:40 AM Andres Freund <andres@anarazel.de> wrote:

On 2021-01-15 09:53:05 +0100, Peter Eisentraut wrote:

On 2020-12-08 10:38, vignesh C wrote:

I have implemented printing of backtrace based on handling it in
CHECK_FOR_INTERRUPTS. This patch also includes the change to allow
getting backtrace of any particular process based on the suggestions.
Attached patch has the implementation for the same.
Thoughts?

Are we willing to use up a signal for this?

Why is a full signal needed? Seems the procsignal infrastructure should
suffice?

Most of the processes have access to ProcSignal, for these processes
printing of callstack signal was handled by using ProcSignal. Pgstat
process & syslogger process do not have access to ProcSignal,
multiplexing with SIGUSR1 is not possible for these processes. So I
handled the printing of callstack for pgstat process & syslogger using
the SIGUSR2 signal.
This is because shared memory is detached before pgstat & syslogger
process is started by using the below:
/* Drop our connection to postmaster's shared memory, as well */
dsm_detach_all();
PGSharedMemoryDetach();

Sure. But why is it important enough to support those that we are willing to dedicate a signal to the task? Their backtraces aren't often interesting, so I think we should just ignore them here.

Andres

#17Tom Lane
tgl@sss.pgh.pa.us
In reply to: vignesh C (#15)
Re: Printing backtrace of postgres processes

vignesh C <vignesh21@gmail.com> writes:

On Sat, Jan 16, 2021 at 1:40 AM Andres Freund <andres@anarazel.de> wrote:

Why is a full signal needed? Seems the procsignal infrastructure should
suffice?

Most of the processes have access to ProcSignal, for these processes
printing of callstack signal was handled by using ProcSignal. Pgstat
process & syslogger process do not have access to ProcSignal,
multiplexing with SIGUSR1 is not possible for these processes. So I
handled the printing of callstack for pgstat process & syslogger using
the SIGUSR2 signal.

I'd argue that backtraces for those processes aren't really essential,
and indeed that trying to make the syslogger report its own backtrace
is damn dangerous.

(Personally, I think this whole patch fails the safety-vs-usefulness
tradeoff, but I expect I'll get shouted down.)

regards, tom lane

#18vignesh C
vignesh21@gmail.com
In reply to: Andres Freund (#16)
Re: Printing backtrace of postgres processes

On Sat, Jan 16, 2021 at 11:10 PM Andres Freund <andres@anarazel.de> wrote:

Hi,

On Sat, Jan 16, 2021, at 09:34, vignesh C wrote:

On Sat, Jan 16, 2021 at 1:40 AM Andres Freund <andres@anarazel.de> wrote:

On 2021-01-15 09:53:05 +0100, Peter Eisentraut wrote:

On 2020-12-08 10:38, vignesh C wrote:

I have implemented printing of backtrace based on handling it in
CHECK_FOR_INTERRUPTS. This patch also includes the change to allow
getting backtrace of any particular process based on the suggestions.
Attached patch has the implementation for the same.
Thoughts?

Are we willing to use up a signal for this?

Why is a full signal needed? Seems the procsignal infrastructure should
suffice?

Most of the processes have access to ProcSignal, for these processes
printing of callstack signal was handled by using ProcSignal. Pgstat
process & syslogger process do not have access to ProcSignal,
multiplexing with SIGUSR1 is not possible for these processes. So I
handled the printing of callstack for pgstat process & syslogger using
the SIGUSR2 signal.
This is because shared memory is detached before pgstat & syslogger
process is started by using the below:
/* Drop our connection to postmaster's shared memory, as well */
dsm_detach_all();
PGSharedMemoryDetach();

Sure. But why is it important enough to support those that we are willing to dedicate a signal to the task? Their backtraces aren't often interesting, so I think we should just ignore them here.

Thanks for your comments Andres, I will ignore it for the processes
which do not have access to ProcSignal. I will make the changes and
post a patch for this soon.

Regards,
Vignesh
EnterpriseDB: http://www.enterprisedb.com

#19Craig Ringer
craig.ringer@enterprisedb.com
In reply to: vignesh C (#18)
Re: Printing backtrace of postgres processes

On Mon, 18 Jan 2021 at 00:56, vignesh C <vignesh21@gmail.com> wrote:

Thanks for your comments Andres, I will ignore it for the processes
which do not have access to ProcSignal. I will make the changes and
post a patch for this soon.

I think that's sensible.

I've had variable results with glibc's backtrace(), especially on older
platforms and/or with external debuginfo, but it's much better than
nothing. It's often not feasible to get someone to install gdb and run
commands on their production systems - they can be isolated and firewalled
or hobbled by painful change policies. Something basic built-in to
postgres, even if basic, is likely to come in very handy.

#20vignesh C
vignesh21@gmail.com
In reply to: vignesh C (#18)
1 attachment(s)
Re: Printing backtrace of postgres processes

On Sun, Jan 17, 2021 at 10:26 PM vignesh C <vignesh21@gmail.com> wrote:

On Sat, Jan 16, 2021 at 11:10 PM Andres Freund <andres@anarazel.de> wrote:

Hi,

On Sat, Jan 16, 2021, at 09:34, vignesh C wrote:

On Sat, Jan 16, 2021 at 1:40 AM Andres Freund <andres@anarazel.de> wrote:

On 2021-01-15 09:53:05 +0100, Peter Eisentraut wrote:

On 2020-12-08 10:38, vignesh C wrote:

I have implemented printing of backtrace based on handling it in
CHECK_FOR_INTERRUPTS. This patch also includes the change to allow
getting backtrace of any particular process based on the suggestions.
Attached patch has the implementation for the same.
Thoughts?

Are we willing to use up a signal for this?

Why is a full signal needed? Seems the procsignal infrastructure should
suffice?

Most of the processes have access to ProcSignal, for these processes
printing of callstack signal was handled by using ProcSignal. Pgstat
process & syslogger process do not have access to ProcSignal,
multiplexing with SIGUSR1 is not possible for these processes. So I
handled the printing of callstack for pgstat process & syslogger using
the SIGUSR2 signal.
This is because shared memory is detached before pgstat & syslogger
process is started by using the below:
/* Drop our connection to postmaster's shared memory, as well */
dsm_detach_all();
PGSharedMemoryDetach();

Sure. But why is it important enough to support those that we are willing to dedicate a signal to the task? Their backtraces aren't often interesting, so I think we should just ignore them here.

Thanks for your comments Andres, I will ignore it for the processes
which do not have access to ProcSignal. I will make the changes and
post a patch for this soon.

The attached patch has the fix for this.

Regards,
Vignesh
EnterpriseDB: http://www.enterprisedb.com

Attachments:

v2-0001-Print-backtrace-of-postgres-process-that-are-part.patchtext/x-patch; charset=US-ASCII; name=v2-0001-Print-backtrace-of-postgres-process-that-are-part.patchDownload
From e173fac2bef56b165cac96398f8b38e91233c59f Mon Sep 17 00:00:00 2001
From: Vignesh C <vignesh.c@enterprisedb.com>
Date: Tue, 19 Jan 2021 16:44:37 +0530
Subject: [PATCH v2] Print backtrace of postgres process that are part of this 
 instance.

The idea here is to implement & expose pg_print_callstack function, internally
what this function does is, the connected backend will send SIGUSR1 signal by
setting PMSIGNAL_BACKTRACE_EMIT to the postmaster process. Postmaster process
will send SIGUSR1 signal to process by setting PROCSIG_BACKTRACE_PRINT. Once
the process receives this signal it will log the backtrace of the process. As
syslogger process & Stats process don't have access to ProcSignal, it was
discussed and concluded to skip these processes.
---
 doc/src/sgml/func.sgml                | 23 +++++++++++
 src/backend/postmaster/autovacuum.c   |  4 ++
 src/backend/postmaster/checkpointer.c |  5 +++
 src/backend/postmaster/interrupt.c    |  5 +++
 src/backend/postmaster/postmaster.c   | 26 ++++++++++++
 src/backend/storage/ipc/procsignal.c  | 50 +++++++++++++++++++++++
 src/backend/storage/ipc/signalfuncs.c | 74 +++++++++++++++++++++++++++++++++++
 src/backend/tcop/postgres.c           | 38 ++++++++++++++++++
 src/backend/utils/init/globals.c      |  1 +
 src/bin/pg_ctl/t/005_backtrace.pl     | 73 ++++++++++++++++++++++++++++++++++
 src/include/catalog/pg_proc.dat       |  8 ++++
 src/include/miscadmin.h               |  2 +
 src/include/storage/pmsignal.h        |  2 +
 src/include/storage/procsignal.h      |  5 +++
 src/include/tcop/tcopprot.h           |  1 +
 15 files changed, 317 insertions(+)
 create mode 100644 src/bin/pg_ctl/t/005_backtrace.pl

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index fd0370a..8bc2b46 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -24668,6 +24668,29 @@ SELECT collation for ('foo' COLLATE "de_DE");
         however only superusers can terminate superuser backends.
        </para></entry>
       </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_print_callstack</primary>
+        </indexterm>
+        <function>pg_print_callstack</function> ( [<parameter>pid</parameter> <type>integer</type>] )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Signals the server processes to print the callstack.
+        <literal> pg_print_callstack</literal> without an argument prints the
+        callstack of all the server processes in this instance. To request
+        callstack of a particular process from this instance, the corresponding
+        process id should be passed as an argument. Callstack will be printed
+        based on the log configuration set. See
+        <xref linkend="runtime-config-logging"/> for more information. This
+        feature is not supported for stats collector and syslogger processes.
+        Only superusers can use <literal>pg_print_callstack </literal> to print
+        the callstack.
+       </para></entry>
+      </row>
+
      </tbody>
     </tgroup>
    </table>
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 47e60ca..aa87eda 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -836,6 +836,10 @@ HandleAutoVacLauncherInterrupts(void)
 	if (ProcSignalBarrierPending)
 		ProcessProcSignalBarrier();
 
+	/* Process printing back trace */
+	if (PrintBacktracePending)
+		LogBackTrace();
+
 	/* Process sinval catchup interrupts that happened while sleeping */
 	ProcessCatchupInterrupt();
 }
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 54a818b..6655de3 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -57,6 +57,7 @@
 #include "storage/shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
+#include "tcop/tcopprot.h"
 #include "utils/guc.h"
 #include "utils/memutils.h"
 #include "utils/resowner.h"
@@ -547,6 +548,10 @@ HandleCheckpointerInterrupts(void)
 	if (ProcSignalBarrierPending)
 		ProcessProcSignalBarrier();
 
+	/* Process printing back trace */
+	if (PrintBacktracePending)
+		LogBackTrace();
+
 	if (ConfigReloadPending)
 	{
 		ConfigReloadPending = false;
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index dd9136a..a6397ab 100644
--- a/src/backend/postmaster/interrupt.c
+++ b/src/backend/postmaster/interrupt.c
@@ -21,6 +21,7 @@
 #include "storage/ipc.h"
 #include "storage/latch.h"
 #include "storage/procsignal.h"
+#include "tcop/tcopprot.h"
 #include "utils/guc.h"
 
 volatile sig_atomic_t ConfigReloadPending = false;
@@ -41,6 +42,10 @@ HandleMainLoopInterrupts(void)
 		ProcessConfigFile(PGC_SIGHUP);
 	}
 
+	/* Process printing back trace */
+	if (PrintBacktracePending)
+		LogBackTrace();
+
 	if (ShutdownRequestPending)
 		proc_exit(0);
 }
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 7de27ee..32d3bec 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -5116,6 +5116,28 @@ ExitPostmaster(int status)
 	proc_exit(status);
 }
 
+bool
+HandlePrintCallStack(int bt_pid)
+{
+	return EmitProcSignalPrintCallStack(bt_pid);
+}
+
+/*
+ * IsCallStackSupportedPid -- check if callstack printing for this process is
+ * supported
+ *
+ * Call stack not supported for stats collector and syslogger process.
+ */
+bool
+IsCallStackSupportedPid(int pid)
+{
+	if ((PgStatPID != 0 && pid == PgStatPID) ||
+		(SysLoggerPID != 0 && pid == SysLoggerPID))
+		return false;
+
+	return true;
+}
+
 /*
  * sigusr1_handler - handle signal conditions from child processes
  */
@@ -5132,6 +5154,10 @@ sigusr1_handler(SIGNAL_ARGS)
 	PG_SETMASK(&BlockSig);
 #endif
 
+	/* Process backtrace emit signal. */
+	if (CheckPostmasterSignal(PMSIGNAL_BACKTRACE_EMIT))
+		HandlePrintCallStack(0);
+
 	/*
 	 * RECOVERY_STARTED and BEGIN_HOT_STANDBY signals are ignored in
 	 * unexpected states. If the startup process quickly starts up, completes
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index 583efae..61ff8f5 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -302,6 +302,38 @@ SendProcSignal(pid_t pid, ProcSignalReason reason, BackendId backendId)
 }
 
 /*
+ * EmitProcSignalPrintCallStack
+ *
+ * Send SIGUSR1 to all postgres backends by setting PROCSIG_BACKTRACE_PRINT, the
+ * postgres processes will print the backtrace once the signal is received.
+ */
+bool
+EmitProcSignalPrintCallStack(int bt_pid)
+{
+	bool found = false;
+	for (int i = NumProcSignalSlots - 1; i >= 0; i--)
+	{
+		volatile ProcSignalSlot *slot = &ProcSignal->psh_slot[i];
+		pid_t		pid = slot->pss_pid;
+
+		if (pid != 0 && (pid == bt_pid || bt_pid == 0))
+		{
+			found = true;
+
+			/* see SendProcSignal for details */
+			slot->pss_signalFlags[PROCSIG_BACKTRACE_PRINT] = true;
+
+			/* Signal SIGUSR1 to the process, so that they print backtrace. */
+			kill(pid, SIGUSR1);
+			if (bt_pid != 0)
+				return found;
+		}
+	}
+
+	return found;
+}
+
+/*
  * EmitProcSignalBarrier
  *		Send a signal to every Postgres process
  *
@@ -441,6 +473,21 @@ HandleProcSignalBarrierInterrupt(void)
 }
 
 /*
+ * Handle receipt of an print backtrace.
+ *
+ * Note: this is called within a signal handler!  All we can do is set
+ * a flag that will cause the next CHECK_FOR_INTERRUPTS() to invoke
+ * LogBackTrace().
+ */
+static void
+HandlePrintBacktraceInterrupt(void)
+{
+	InterruptPending = true;
+	PrintBacktracePending = true;
+	/* latch will be set by procsignal_sigusr1_handler */
+}
+
+/*
  * Perform global barrier related interrupt checking.
  *
  * Any backend that participates in ProcSignal signaling must arrange to
@@ -585,6 +632,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
 
+	if (CheckProcSignal(PROCSIG_BACKTRACE_PRINT))
+		HandlePrintBacktraceInterrupt();
+
 	SetLatch(MyLatch);
 
 	latch_sigusr1_handler();
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index 69fe23a..abd5b1c 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -22,6 +22,7 @@
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
+#include "tcop/tcopprot.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 
@@ -215,3 +216,76 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_print_callstack - print callstack of process that are part of this
+ * instance.
+ *
+ * Permission checking for this function is managed through the normal
+ * GRANT system.
+ */
+Datum
+pg_print_callstack(PG_FUNCTION_ARGS)
+{
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser to print backtraces")));
+
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	SendPostmasterSignal(PMSIGNAL_BACKTRACE_EMIT);
+#else
+	{
+		ereport(WARNING,
+				(errmsg("backtrace generation is not supported by this installation")));
+		PG_RETURN_BOOL(false);
+	}
+#endif
+	PG_RETURN_BOOL(true);
+}
+
+/*
+ * pg_print_callstack - print callstack of process that are part of this
+ * instance.
+ *
+ * Permission checking for this function is managed through the normal
+ * GRANT system.
+ */
+Datum
+pg_print_callstack_pid(PG_FUNCTION_ARGS)
+{
+	int	bt_pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser to print backtraces")));
+
+	if (bt_pid <= 0 || bt_pid > INT_MAX)
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("value should between 1 and %d",
+						INT_MAX)));
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	{
+		if (!IsCallStackSupportedPid(bt_pid))
+		{
+			ereport(WARNING,
+				(errmsg("backtrace generation is not supported for stats collector/syslogger process")));
+			PG_RETURN_BOOL(false);
+		}
+
+		if(HandlePrintCallStack(bt_pid))
+			PG_RETURN_BOOL(true);
+
+		ereport(WARNING,
+				(errmsg("specified pid is not part of this instance")));
+		PG_RETURN_BOOL(false);
+	}
+#else
+	{
+		ereport(WARNING,
+				(errmsg("backtrace generation is not supported by this installation")));
+		PG_RETURN_BOOL(false);
+	}
+#endif
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 8dab9fd..977600c 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -19,6 +19,7 @@
 
 #include "postgres.h"
 
+#include <execinfo.h>
 #include <fcntl.h>
 #include <limits.h>
 #include <signal.h>
@@ -2921,6 +2922,39 @@ FloatExceptionHandler(SIGNAL_ARGS)
 }
 
 /*
+ * LogBackTrace
+ *
+ * Get the backtrace and log the backtrace to log file.
+ */
+void
+LogBackTrace(void)
+{
+	int			save_errno = errno;
+
+	void	   *buf[100];
+	int			nframes;
+	char	  **strfrms;
+	StringInfoData errtrace;
+
+	/* OK to process messages.  Reset the flag saying there are more to do. */
+	PrintBacktracePending = false;
+
+	nframes = backtrace(buf, lengthof(buf));
+	strfrms = backtrace_symbols(buf, nframes);
+	if (strfrms == NULL)
+		return;
+
+	initStringInfo(&errtrace);
+	for (int i = 0; i < nframes; i++)
+		appendStringInfo(&errtrace, "\n%s", strfrms[i]);
+	free(strfrms);
+
+	elog(LOG, "current backtrace:%s", errtrace.data);
+
+	errno = save_errno;
+}
+
+/*
  * RecoveryConflictInterrupt: out-of-line portion of recovery conflict
  * handling following receipt of SIGUSR1. Designed to be similar to die()
  * and StatementCancelHandler(). Called only by a normal user backend
@@ -3274,6 +3308,10 @@ ProcessInterrupts(void)
 
 	if (ParallelMessagePending)
 		HandleParallelMessages();
+
+	/* Process printing back trace */
+	if (PrintBacktracePending)
+		LogBackTrace();
 }
 
 
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index ea28769..0cac8d2 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -37,6 +37,7 @@ volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
+volatile sig_atomic_t PrintBacktracePending = false;
 
 int			MyProcPid;
 pg_time_t	MyStartTime;
diff --git a/src/bin/pg_ctl/t/005_backtrace.pl b/src/bin/pg_ctl/t/005_backtrace.pl
new file mode 100644
index 0000000..2346aa1
--- /dev/null
+++ b/src/bin/pg_ctl/t/005_backtrace.pl
@@ -0,0 +1,73 @@
+use strict;
+use warnings;
+
+use PostgresNode;
+use TestLib;
+use Test::More tests => 2;
+use Time::HiRes qw(usleep);
+
+# Set up node with logging collector
+my $node = get_new_node('primary');
+$node->init();
+$node->append_conf(
+	'postgresql.conf', qq(
+logging_collector = on
+lc_messages = 'C'
+));
+
+$node->start();
+
+# Verify that log output gets to the file
+$node->psql('postgres', 'select pg_print_callstack()');
+
+# might need to retry if logging collector process is slow...
+my $max_attempts = 180 * 10;
+
+my $current_logfiles;
+for (my $attempts = 0; $attempts < $max_attempts; $attempts++)
+{
+	eval {
+		$current_logfiles = slurp_file($node->data_dir . '/current_logfiles');
+	};
+	last unless $@;
+	usleep(100_000);
+}
+die $@ if $@;
+
+note "current_logfiles = $current_logfiles";
+
+like(
+	$current_logfiles,
+	qr|^stderr log/postgresql-.*log$|,
+	'current_logfiles is sane');
+
+my $lfname = $current_logfiles;
+$lfname =~ s/^stderr //;
+chomp $lfname;
+
+my $first_logfile;
+my $bt_occurence_count;
+
+# Verify that the backtraces of the processes are logged into logfile.
+for (my $attempts = 0; $attempts < $max_attempts; $attempts++)
+{
+	$first_logfile = $node->data_dir . '/' . $lfname;
+	chomp $first_logfile;
+	print "file is $first_logfile";
+	open my $fh, '<', $first_logfile
+	  or die "Could not open '$first_logfile' $!";
+	while (my $line = <$fh>)
+	{
+		chomp $line;
+		if ($line =~ m/current backtrace/)
+		{
+			$bt_occurence_count++;
+		}
+	}
+	last if $bt_occurence_count == 6;
+	usleep(100_000);
+}
+
+is($bt_occurence_count, 6, 'found expected back trace in the log file');
+
+$node->stop();
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index dd64c3b..9fa651b 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11369,4 +11369,12 @@
   proname => 'is_normalized', prorettype => 'bool', proargtypes => 'text text',
   prosrc => 'unicode_is_normalized' },
 
+# function to get the callstack of server process
+{ oid => '8908', descr => 'print callstack of process that are part of this instance',
+  proname => 'pg_print_callstack', provolatile => 'v', prorettype => 'bool',
+  proargtypes => '', prosrc => 'pg_print_callstack' },
+{ oid => '8909', descr => 'print callstack of process that are part of this instance',
+  proname => 'pg_print_callstack', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_print_callstack_pid' },
+
 ]
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 1bdc97e..0d00ff9 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -92,6 +92,8 @@ extern PGDLLIMPORT volatile uint32 InterruptHoldoffCount;
 extern PGDLLIMPORT volatile uint32 QueryCancelHoldoffCount;
 extern PGDLLIMPORT volatile uint32 CritSectionCount;
 
+extern PGDLLIMPORT volatile sig_atomic_t PrintBacktracePending;
+
 /* in tcop/postgres.c */
 extern void ProcessInterrupts(void);
 
diff --git a/src/include/storage/pmsignal.h b/src/include/storage/pmsignal.h
index dbbed18..5d66c79 100644
--- a/src/include/storage/pmsignal.h
+++ b/src/include/storage/pmsignal.h
@@ -42,6 +42,8 @@ typedef enum
 	PMSIGNAL_START_WALRECEIVER, /* start a walreceiver */
 	PMSIGNAL_ADVANCE_STATE_MACHINE, /* advance postmaster's state machine */
 
+	PMSIGNAL_BACKTRACE_EMIT,	/* send PROCSIG_BACKTRACE_PRINT to all backend */
+
 	NUM_PMSIGNALS				/* Must be last value of enum! */
 } PMSignalReason;
 
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index 4ae7dc3..80cb281 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -43,6 +43,8 @@ typedef enum
 	PROCSIG_RECOVERY_CONFLICT_BUFFERPIN,
 	PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK,
 
+	PROCSIG_BACKTRACE_PRINT,	/* ask backend to print the current backtrace */
+
 	NUM_PROCSIGNALS				/* Must be last! */
 } ProcSignalReason;
 
@@ -71,5 +73,8 @@ extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
 
 extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
+extern bool EmitProcSignalPrintCallStack(int bt_pid);
+extern bool HandlePrintCallStack(int bt_pid);
+extern bool IsCallStackSupportedPid(int pid);
 
 #endif							/* PROCSIGNAL_H */
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index e547210..0f8b74d 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -71,6 +71,7 @@ extern void RecoveryConflictInterrupt(ProcSignalReason reason); /* called from S
 extern void ProcessClientReadInterrupt(bool blocked);
 extern void ProcessClientWriteInterrupt(bool blocked);
 
+extern void LogBackTrace(void); /* Called from EmitProcSignalPrintCallStack */
 extern void process_postgres_switches(int argc, char *argv[],
 									  GucContext ctx, const char **dbname);
 extern void PostgresMain(int argc, char *argv[],
-- 
1.8.3.1

#21Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#17)
Re: Printing backtrace of postgres processes

On Sat, Jan 16, 2021 at 3:21 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

I'd argue that backtraces for those processes aren't really essential,
and indeed that trying to make the syslogger report its own backtrace
is damn dangerous.

I agree. Ideally I'd like to be able to use the same mechanism
everywhere and include those processes too, but surely regular
backends and parallel workers are going to be the things that come up
most often.

(Personally, I think this whole patch fails the safety-vs-usefulness
tradeoff, but I expect I'll get shouted down.)

You and I are frequently on opposite sides of these kinds of
questions, but I think this is a closer call than many cases. I'm
convinced that it's useful, but I'm not sure whether it's safe. On the
usefulness side, backtraces are often the only way to troubleshoot
problems that occur on production systems. I wish we had better
logging and tracing tools instead of having to ask for this sort of
thing, but we don't. EDB support today frequently asks customers to
attach gdb and take a backtrace that way, and that has risks which
this implementation does not: for example, suppose you were unlucky
enough to attach during a spinlock protected critical section, and
suppose you didn't continue the stopped process before the 60 second
timeout expired and some other process caused a PANIC. Even if this
implementation were to end up emitting a backtrace with a spinlock
held, it would remove the risk of leaving the process stopped while
holding a critical lock, and would in that sense be safer. However, as
soon as you make something like this accessible via an SQL callable
function, some people are going to start spamming it. And, as soon as
they do that, any risks inherent in the implementation are multiplied.
If it carries an 0.01% chance of crashing the system, we'll have
people taking production systems down with this all the time. At that
point I wouldn't want the feature, even if the gdb approach had the
same risk (which I don't think it does).

What do you see as the main safety risks here?

--
Robert Haas
EDB: http://www.enterprisedb.com

#22Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#21)
Re: Printing backtrace of postgres processes

Robert Haas <robertmhaas@gmail.com> writes:

On Sat, Jan 16, 2021 at 3:21 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

(Personally, I think this whole patch fails the safety-vs-usefulness
tradeoff, but I expect I'll get shouted down.)

What do you see as the main safety risks here?

The thing that is scaring me the most is the "broadcast" aspect.
For starters, I think that that is going to be useless in the
field because of the likelihood that different backends' stack
traces will get interleaved in whatever log file the traces are
going to. Also, having hundreds of processes spitting dozens of
lines to the same place at the same time is going to expose any
little weaknesses in your logging arrangements, such as rsyslog's
tendency to drop messages under load.

I think it's got security hazards as well. If we restricted the
feature to cause a trace of only one process at a time, and required
that process to be logged in as the same database user as the
requestor (or at least someone with the privs of that user, such
as a superuser), that'd be less of an issue.

Beyond that, well, maybe it's all right. In theory anyplace that
there's a CHECK_FOR_INTERRUPTS should be okay to call elog from;
but it's completely untested whether we can do that and then
continue, as opposed to aborting the transaction or whole session.
I share your estimate that there'll be small-fraction-of-a-percent
hazards that could still add up to dangerous instability if people
go wild with this.

regards, tom lane

#23Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#22)
Re: Printing backtrace of postgres processes

On Tue, Jan 19, 2021 at 12:50 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

The thing that is scaring me the most is the "broadcast" aspect.
For starters, I think that that is going to be useless in the
field because of the likelihood that different backends' stack
traces will get interleaved in whatever log file the traces are
going to. Also, having hundreds of processes spitting dozens of
lines to the same place at the same time is going to expose any
little weaknesses in your logging arrangements, such as rsyslog's
tendency to drop messages under load.

+1. I don't think broadcast is a good idea.

I think it's got security hazards as well. If we restricted the
feature to cause a trace of only one process at a time, and required
that process to be logged in as the same database user as the
requestor (or at least someone with the privs of that user, such
as a superuser), that'd be less of an issue.

I am not sure I see a security hazard here. I think the checks for
this should have the same structure as pg_terminate_backend() and
pg_cancel_backend(); whatever is required there should be required
here, too, but not more, unless we have a real clear reason for such
an inconsistency.

Beyond that, well, maybe it's all right. In theory anyplace that
there's a CHECK_FOR_INTERRUPTS should be okay to call elog from;
but it's completely untested whether we can do that and then
continue, as opposed to aborting the transaction or whole session.

I guess that's a theoretical risk but it doesn't seem very likely.
And, if we do have such a problem, I think that'd probably be a case
of bad code that we would want to fix either way.

I share your estimate that there'll be small-fraction-of-a-percent
hazards that could still add up to dangerous instability if people
go wild with this.

Right. I was more concerned about whether we could, for example, crash
while inside the function that generates the backtrace, on some
platforms or in some scenarios. That would be super-sad. I assume that
the people who wrote the code tried to make sure that didn't happen
but I don't really know what's reasonable to expect.

--
Robert Haas
EDB: http://www.enterprisedb.com

#24Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#23)
Re: Printing backtrace of postgres processes

Robert Haas <robertmhaas@gmail.com> writes:

On Tue, Jan 19, 2021 at 12:50 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

I think it's got security hazards as well. If we restricted the
feature to cause a trace of only one process at a time, and required
that process to be logged in as the same database user as the
requestor (or at least someone with the privs of that user, such
as a superuser), that'd be less of an issue.

I am not sure I see a security hazard here. I think the checks for
this should have the same structure as pg_terminate_backend() and
pg_cancel_backend(); whatever is required there should be required
here, too, but not more, unless we have a real clear reason for such
an inconsistency.

Yeah, agreed. The "broadcast" option seems inconsistent with doing
things that way, but I don't have a problem with being able to send
a trace signal to the same processes you could terminate.

I share your estimate that there'll be small-fraction-of-a-percent
hazards that could still add up to dangerous instability if people
go wild with this.

Right. I was more concerned about whether we could, for example, crash
while inside the function that generates the backtrace, on some
platforms or in some scenarios. That would be super-sad. I assume that
the people who wrote the code tried to make sure that didn't happen
but I don't really know what's reasonable to expect.

Recursion is scary, but it should (I think) not be possible if this
is driven off CHECK_FOR_INTERRUPTS. There will certainly be none
of those in libbacktrace.

One point here is that it might be a good idea to suppress elog.c's
calls to functions in the error context stack. As we saw in another
connection recently, allowing that to happen makes for a *very*
large increase in the footprint of code that you are expecting to
work at any random CHECK_FOR_INTERRUPTS call site.

BTW, it also looks like the patch is doing nothing to prevent the
backtrace from being sent to the connected client. I'm not sure
what I think about whether it'd be okay from a security standpoint
to do that on the connection that requested the trace, but I sure
as heck don't want it to happen on connections that didn't. Also,
whatever you think about security concerns, it seems like there'd be
pretty substantial reentrancy hazards if the interrupt occurs
anywhere in code dealing with normal client communication.

Maybe, given all of these things, we should forget using elog at
all and just emit the trace with fprintf(stderr). That seems like
it would decrease the odds of trouble by about an order of magnitude.
It would make it more painful to collect the trace in some logging
setups, of course.

regards, tom lane

#25vignesh C
vignesh21@gmail.com
In reply to: Tom Lane (#24)
Re: Printing backtrace of postgres processes

On Wed, Jan 20, 2021 at 2:52 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:

Robert Haas <robertmhaas@gmail.com> writes:

On Tue, Jan 19, 2021 at 12:50 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

I think it's got security hazards as well. If we restricted the
feature to cause a trace of only one process at a time, and required
that process to be logged in as the same database user as the
requestor (or at least someone with the privs of that user, such
as a superuser), that'd be less of an issue.

I am not sure I see a security hazard here. I think the checks for
this should have the same structure as pg_terminate_backend() and
pg_cancel_backend(); whatever is required there should be required
here, too, but not more, unless we have a real clear reason for such
an inconsistency.

Yeah, agreed. The "broadcast" option seems inconsistent with doing
things that way, but I don't have a problem with being able to send
a trace signal to the same processes you could terminate.

The current patch supports both getting the trace for all the
processes of that instance and getting the trace for a particular
process, I'm understanding the concern here with broadcasting to all
the connected backends, I will remove the functionality to get trace
for all the processes. And I will also change the trace of a single
process in such a way that the user can get the trace of only the
processes which that user could terminate.

I share your estimate that there'll be small-fraction-of-a-percent
hazards that could still add up to dangerous instability if people
go wild with this.

Right. I was more concerned about whether we could, for example, crash
while inside the function that generates the backtrace, on some
platforms or in some scenarios. That would be super-sad. I assume that
the people who wrote the code tried to make sure that didn't happen
but I don't really know what's reasonable to expect.

Recursion is scary, but it should (I think) not be possible if this
is driven off CHECK_FOR_INTERRUPTS. There will certainly be none
of those in libbacktrace.

One point here is that it might be a good idea to suppress elog.c's
calls to functions in the error context stack. As we saw in another
connection recently, allowing that to happen makes for a *very*
large increase in the footprint of code that you are expecting to
work at any random CHECK_FOR_INTERRUPTS call site.

BTW, it also looks like the patch is doing nothing to prevent the
backtrace from being sent to the connected client. I'm not sure
what I think about whether it'd be okay from a security standpoint
to do that on the connection that requested the trace, but I sure
as heck don't want it to happen on connections that didn't. Also,
whatever you think about security concerns, it seems like there'd be
pretty substantial reentrancy hazards if the interrupt occurs
anywhere in code dealing with normal client communication.

Maybe, given all of these things, we should forget using elog at
all and just emit the trace with fprintf(stderr). That seems like
it would decrease the odds of trouble by about an order of magnitude.
It would make it more painful to collect the trace in some logging
setups, of course.

I would prefer if the trace appears in the log file rather on the
console, as you rightly pointed out it will be difficult to collect
the trace of fprint(stderr). Let me know if I misunderstood. I think
you are concerned about the problem where elog logs the trace to the
client also. Can we use LOG_SERVER_ONLY log level that would prevent
it from logging to the client. That way we can keep the elog
implementation itself.

Thoughts?

Regards,
Vignesh
EnterpriseDB: http://www.enterprisedb.com

#26Craig Ringer
craig.ringer@enterprisedb.com
In reply to: Robert Haas (#21)
Re: Printing backtrace of postgres processes

On Wed, 20 Jan 2021 at 01:31, Robert Haas <robertmhaas@gmail.com> wrote:

On Sat, Jan 16, 2021 at 3:21 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

I'd argue that backtraces for those processes aren't really essential,
and indeed that trying to make the syslogger report its own backtrace
is damn dangerous.

I agree. Ideally I'd like to be able to use the same mechanism
everywhere and include those processes too, but surely regular
backends and parallel workers are going to be the things that come up
most often.

(Personally, I think this whole patch fails the safety-vs-usefulness
tradeoff, but I expect I'll get shouted down.)

You and I are frequently on opposite sides of these kinds of
questions, but I think this is a closer call than many cases. I'm
convinced that it's useful, but I'm not sure whether it's safe. On the
usefulness side, backtraces are often the only way to troubleshoot
problems that occur on production systems. I wish we had better
logging and tracing tools instead of having to ask for this sort of
thing, but we don't.

Agreed.

In theory we should be able to do this sort of thing using external trace
and diagnostic tools like perf, systemtap, etc. In practice, these tools
tend to be quite version-sensitive and hard to get right without multiple
rounds of back-and-forth to deal with specifics of the site's setup,
installed debuginfo or lack thereof, specific tool versions, etc.

It's quite common to have to fall back on attaching gdb with a breakpoint
on a function in the export symbol table (so it works w/o debuginfo),
saving a core, and then analysing the core on a separate system on which
debuginfo is available for all the loaded modules. It's a major pain.

The ability to get a basic bt from within Pg is strongly desirable. IIRC
gdb's basic unwinder works without external debuginfo, if not especially
well. libunwind produces much better results, but that didn't pass the
extra-dependency bar when backtracing support was introduced to core
postgres.

On a side note, to help get better diagnostics I've also been meaning to
look into building --enable-debug with -ggdb3 so we can embed macro info,
and using dwz to deduplicate+compress the debuginfo so we can encourage
people to install it by default on production. I also want to start
exporting pointers to all the important data symbols for diagnostic use,
even if we do so in a separate ELF section just for debug use.

#27Craig Ringer
craig.ringer@enterprisedb.com
In reply to: Tom Lane (#24)
Re: Printing backtrace of postgres processes

On Wed, 20 Jan 2021 at 05:23, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Recursion is scary, but it should (I think) not be possible if this
is driven off CHECK_FOR_INTERRUPTS. There will certainly be none
of those in libbacktrace.

We can also hold interrupts for the call, and it might be wise to do so.

One point here is that it might be a good idea to suppress elog.c's

calls to functions in the error context stack. As we saw in another
connection recently, allowing that to happen makes for a *very*
large increase in the footprint of code that you are expecting to
work at any random CHECK_FOR_INTERRUPTS call site.

I strongly agree. Treat it as errhidecontext().

BTW, it also looks like the patch is doing nothing to prevent the

backtrace from being sent to the connected client. I'm not sure
what I think about whether it'd be okay from a security standpoint
to do that on the connection that requested the trace, but I sure
as heck don't want it to happen on connections that didn't.

I don't see a good reason to send a bt to a client. Even though these
backtraces won't be analysing debuginfo and populating args, locals, etc,
it should still just go to the server log.

Maybe, given all of these things, we should forget using elog at
all and just emit the trace with fprintf(stderr).

That causes quite a lot of pain with MemoryContextStats() already as it's
frequently difficult to actually locate the output given the variations
that exist in customer logging configurations. Sometimes stderr goes to a
separate file or to journald. It's also much harder to locate the desired
output since there's no log_line_prefix. I have a WIP patch floating around
somewhere that tries to teach MemoryContextStats to write to the ereport
channel when not called during an actual out-of-memory situation for that
reason; an early version is somewhere in the archives.

This is one of those "ok in development, painful in production" situations.

So I'm not a big fan of pushing it to stderr, though I'd rather have that
than not have the ability at all.

#28Tom Lane
tgl@sss.pgh.pa.us
In reply to: Craig Ringer (#27)
Re: Printing backtrace of postgres processes

Craig Ringer <craig.ringer@enterprisedb.com> writes:

On Wed, 20 Jan 2021 at 05:23, Tom Lane <tgl@sss.pgh.pa.us> wrote:

BTW, it also looks like the patch is doing nothing to prevent the
backtrace from being sent to the connected client.

I don't see a good reason to send a bt to a client. Even though these
backtraces won't be analysing debuginfo and populating args, locals, etc,
it should still just go to the server log.

Yeah. That's easier than I was thinking, we just need to
s/LOG/LOG_SERVER_ONLY/.

Maybe, given all of these things, we should forget using elog at
all and just emit the trace with fprintf(stderr).

That causes quite a lot of pain with MemoryContextStats() already

True. Given the changes discussed in the last couple messages, I don't
see any really killer reasons why we can't ship the trace through elog.
We can always try that first, and back off to fprintf if we do find
reasons why it's too unstable.

regards, tom lane

#29Craig Ringer
craig.ringer@enterprisedb.com
In reply to: Tom Lane (#28)
Re: Printing backtrace of postgres processes

On Thu, 21 Jan 2021 at 09:56, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Craig Ringer <craig.ringer@enterprisedb.com> writes:

On Wed, 20 Jan 2021 at 05:23, Tom Lane <tgl@sss.pgh.pa.us> wrote:

BTW, it also looks like the patch is doing nothing to prevent the
backtrace from being sent to the connected client.

I don't see a good reason to send a bt to a client. Even though these
backtraces won't be analysing debuginfo and populating args, locals, etc,
it should still just go to the server log.

Yeah. That's easier than I was thinking, we just need to
s/LOG/LOG_SERVER_ONLY/.

Maybe, given all of these things, we should forget using elog at
all and just emit the trace with fprintf(stderr).

That causes quite a lot of pain with MemoryContextStats() already

True. Given the changes discussed in the last couple messages, I don't
see any really killer reasons why we can't ship the trace through elog.
We can always try that first, and back off to fprintf if we do find
reasons why it's too unstable.

Yep, works for me.

Thanks for being open to considering this.

I know lots of this stuff can seem like a pointless sidetrack because the
utility of it is not obvious on dev systems or when you're doing your own
hands-on expert support on systems you own and operate yourself. These
sorts of things really only start to make sense when you're touching many
different postgres systems "in the wild" - such as commercial support
services, helping people on -general, -bugs or stackoverflow, etc.

I really appreciate your help with it.

#30Robert Haas
robertmhaas@gmail.com
In reply to: Craig Ringer (#29)
Re: Printing backtrace of postgres processes

On Wed, Jan 20, 2021 at 9:24 PM Craig Ringer
<craig.ringer@enterprisedb.com> wrote:

I know lots of this stuff can seem like a pointless sidetrack because the utility of it is not obvious on dev systems or when you're doing your own hands-on expert support on systems you own and operate yourself. These sorts of things really only start to make sense when you're touching many different postgres systems "in the wild" - such as commercial support services, helping people on -general, -bugs or stackoverflow, etc.

I really appreciate your help with it.

Big +1 for all that from me, too.

--
Robert Haas
EDB: http://www.enterprisedb.com

#31vignesh C
vignesh21@gmail.com
In reply to: Tom Lane (#28)
1 attachment(s)
Re: Printing backtrace of postgres processes

On Thu, Jan 21, 2021 at 7:26 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:

Craig Ringer <craig.ringer@enterprisedb.com> writes:

On Wed, 20 Jan 2021 at 05:23, Tom Lane <tgl@sss.pgh.pa.us> wrote:

BTW, it also looks like the patch is doing nothing to prevent the
backtrace from being sent to the connected client.

I don't see a good reason to send a bt to a client. Even though these
backtraces won't be analysing debuginfo and populating args, locals, etc,
it should still just go to the server log.

Yeah. That's easier than I was thinking, we just need to
s/LOG/LOG_SERVER_ONLY/.

Maybe, given all of these things, we should forget using elog at
all and just emit the trace with fprintf(stderr).

That causes quite a lot of pain with MemoryContextStats() already

True. Given the changes discussed in the last couple messages, I don't
see any really killer reasons why we can't ship the trace through elog.
We can always try that first, and back off to fprintf if we do find
reasons why it's too unstable.

Thanks all of them for the suggestions. Attached v3 patch which has
fixes based on the suggestions. It includes the following fixes: 1)
Removal of support to get callstack of all postgres process, user can
get only one process callstack. 2) Update the documentation. 3) Added
necessary checks for pg_print_callstack similar to
pg_terminate_backend. 4) Changed LOG to LOG_SERVER_ONLY.
Thoughts?

Regards,
Vignesh

Attachments:

v3-0001-Print-backtrace-of-postgres-process-that-are-part.patchtext/x-patch; charset=US-ASCII; name=v3-0001-Print-backtrace-of-postgres-process-that-are-part.patchDownload
From dd51938225881be14cce2ba0e80ae9019a3f2bfc Mon Sep 17 00:00:00 2001
From: Vignesh C <vignesh.c@enterprisedb.com>
Date: Wed, 27 Jan 2021 18:20:13 +0530
Subject: [PATCH v3] Print backtrace of postgres process that are part of this
 instance.

The idea here is to implement & expose pg_print_callstack function, internally
what this function does is, the connected backend will send SIGUSR1 signal by
setting PMSIGNAL_BACKTRACE_EMIT to the postmaster process. Once the process
receives this signal it will log the backtrace of the process.
---
 doc/src/sgml/func.sgml                | 75 +++++++++++++++++++++++++++++++++++
 src/backend/postmaster/autovacuum.c   |  4 ++
 src/backend/postmaster/checkpointer.c |  5 +++
 src/backend/postmaster/interrupt.c    |  5 +++
 src/backend/storage/ipc/procsignal.c  | 33 +++++++++++++++
 src/backend/storage/ipc/signalfuncs.c | 59 ++++++++++++++++++++++++++-
 src/backend/tcop/postgres.c           | 38 ++++++++++++++++++
 src/backend/utils/init/globals.c      |  1 +
 src/bin/pg_ctl/t/005_backtrace.pl     | 73 ++++++++++++++++++++++++++++++++++
 src/include/catalog/pg_proc.dat       |  5 +++
 src/include/miscadmin.h               |  2 +
 src/include/storage/pmsignal.h        |  2 +
 src/include/storage/procsignal.h      |  3 ++
 src/include/tcop/tcopprot.h           |  1 +
 14 files changed, 305 insertions(+), 1 deletion(-)
 create mode 100644 src/bin/pg_ctl/t/005_backtrace.pl

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index aa99665..4ff6e7f 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -24709,6 +24709,25 @@ SELECT collation for ('foo' COLLATE "de_DE");
         however only superusers can terminate superuser backends.
        </para></entry>
       </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_print_callstack</primary>
+        </indexterm>
+        <function>pg_print_callstack</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Prints the callstack whose backend process has the specified process ID.
+        Callstack will be printed based on the log configuration set. See
+        <xref linkend="runtime-config-logging"/> for more information.  This is
+        allowed if the calling role is a member of the role whose backend
+        callstack is being printed or the calling role has been granted
+        <literal>pg_print_callstack</literal>, however only superusers can
+        print callstack of superuser backends.
+       </para></entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
@@ -24728,6 +24747,62 @@ SELECT collation for ('foo' COLLATE "de_DE");
     <structname>pg_stat_activity</structname> view.
    </para>
 
+   <para>
+    <function>pg_print_callstack</function> can be used to print callstack of
+    a backend porcess. For example:
+<programlisting>
+postgres=# select pg_print_callstack(pg_backend_pid());
+ pg_print_callstack
+--------------------
+ t
+(1 row)
+
+The callstack will be logged in the log file. For example:
+2021-01-27 11:33:50.247 IST [111735] LOG:  current backtrace:
+        postgres: postgresdba postgres [local] SELECT(LogBackTrace+0x33) [0x9501cd]
+        postgres: postgresdba postgres [local] SELECT(ProcessInterrupts+0x774) [0x950bac]
+        postgres: postgresdba postgres [local] SELECT() [0x761ee8]
+        postgres: postgresdba postgres [local] SELECT() [0x71bc39]
+        postgres: postgresdba postgres [local] SELECT() [0x71e3df]
+        postgres: postgresdba postgres [local] SELECT(standard_ExecutorRun+0x1d6) [0x71c25d]
+        postgres: postgresdba postgres [local] SELECT(ExecutorRun+0x55) [0x71c085]
+        postgres: postgresdba postgres [local] SELECT() [0x953f3d]
+        postgres: postgresdba postgres [local] SELECT(PortalRun+0x262) [0x953bf6]
+        postgres: postgresdba postgres [local] SELECT() [0x94dafa]
+        postgres: postgresdba postgres [local] SELECT(PostgresMain+0x7d7) [0x951dea]
+        postgres: postgresdba postgres [local] SELECT() [0x896a7b]
+        postgres: postgresdba postgres [local] SELECT() [0x896401]
+        postgres: postgresdba postgres [local] SELECT() [0x8929c5]
+        postgres: postgresdba postgres [local] SELECT(PostmasterMain+0x116b) [0x89229c]
+        postgres: postgresdba postgres [local] SELECT() [0x795efa]
+        /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f6c12697505]
+        postgres: postgresdba postgres [local] SELECT() [0x484229]
+
+</programlisting>
+    You can get the file name and line number by using:
+<programlisting>
+1)  "info line *address" from gdb on postgres executable. For example:
+gdb ./postgres
+GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-115.el7
+Copyright (C) 2013 Free Software Foundation, Inc.
+License GPLv3+: GNU GPL version 3 or later <literal>&lt;</literal>http://gnu.org/licenses/gpl.html<literal>&gt;</literal>
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
+and "show warranty" for details.
+This GDB was configured as "x86_64-redhat-linux-gnu".
+For bug reporting instructions, please see:
+<literal>&lt;</literal>http://www.gnu.org/software/gdb/bugs/<literal>&gt;</literal>...
+Reading symbols from /home/vignesh/postgres/postgres/inst/bin/postgres...done.
+(gdb) info line *0x71c25d
+Line 378 of "execMain.c" starts at address 0x71c25d <literal>&lt;</literal>standard_ExecutorRun+470<literal>&gt;</literal> and ends at 0x71c263 <literal>&lt;</literal>standard_ExecutorRun+476<literal>&gt;</literal>.
+OR 
+2) Using "addr2line -e postgres address", For exmple:
+addr2line -e ./postgres 0x71c25d
+/home/vignesh/postgres/postgres/src/backend/executor/execMain.c:378
+</programlisting>
+</para>
+
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 47e60ca..aa87eda 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -836,6 +836,10 @@ HandleAutoVacLauncherInterrupts(void)
 	if (ProcSignalBarrierPending)
 		ProcessProcSignalBarrier();
 
+	/* Process printing back trace */
+	if (PrintBacktracePending)
+		LogBackTrace();
+
 	/* Process sinval catchup interrupts that happened while sleeping */
 	ProcessCatchupInterrupt();
 }
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 54a818b..6655de3 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -57,6 +57,7 @@
 #include "storage/shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
+#include "tcop/tcopprot.h"
 #include "utils/guc.h"
 #include "utils/memutils.h"
 #include "utils/resowner.h"
@@ -547,6 +548,10 @@ HandleCheckpointerInterrupts(void)
 	if (ProcSignalBarrierPending)
 		ProcessProcSignalBarrier();
 
+	/* Process printing back trace */
+	if (PrintBacktracePending)
+		LogBackTrace();
+
 	if (ConfigReloadPending)
 	{
 		ConfigReloadPending = false;
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index dd9136a..a6397ab 100644
--- a/src/backend/postmaster/interrupt.c
+++ b/src/backend/postmaster/interrupt.c
@@ -21,6 +21,7 @@
 #include "storage/ipc.h"
 #include "storage/latch.h"
 #include "storage/procsignal.h"
+#include "tcop/tcopprot.h"
 #include "utils/guc.h"
 
 volatile sig_atomic_t ConfigReloadPending = false;
@@ -41,6 +42,10 @@ HandleMainLoopInterrupts(void)
 		ProcessConfigFile(PGC_SIGHUP);
 	}
 
+	/* Process printing back trace */
+	if (PrintBacktracePending)
+		LogBackTrace();
+
 	if (ShutdownRequestPending)
 		proc_exit(0);
 }
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index c43cdd6..b0479aa 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -308,6 +308,21 @@ SendProcSignal(pid_t pid, ProcSignalReason reason, BackendId backendId)
 }
 
 /*
+ * EmitProcSignalPrintCallStack
+ *
+ * Send SIGUSR1 to postgres backend whose pid matches bt_pid by setting
+ * PROCSIG_BACKTRACE_PRINT, the postgres processes will print the backtrace once
+ * the signal is received.
+ */
+bool
+EmitProcSignalPrintCallStack(int bt_pid)
+{
+	int			ret = SendProcSignal(bt_pid, PROCSIG_BACKTRACE_PRINT, InvalidBackendId);
+
+	return (ret == 0);
+}
+
+/*
  * EmitProcSignalBarrier
  *		Send a signal to every Postgres process
  *
@@ -453,6 +468,21 @@ HandleProcSignalBarrierInterrupt(void)
 }
 
 /*
+ * Handle receipt of an print backtrace.
+ *
+ * Note: this is called within a signal handler!  All we can do is set
+ * a flag that will cause the next CHECK_FOR_INTERRUPTS() to invoke
+ * LogBackTrace().
+ */
+static void
+HandlePrintBacktraceInterrupt(void)
+{
+	InterruptPending = true;
+	PrintBacktracePending = true;
+	/* latch will be set by procsignal_sigusr1_handler */
+}
+
+/*
  * Perform global barrier related interrupt checking.
  *
  * Any backend that participates in ProcSignal signaling must arrange to
@@ -686,6 +716,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
 
+	if (CheckProcSignal(PROCSIG_BACKTRACE_PRINT))
+		HandlePrintBacktraceInterrupt();
+
 	SetLatch(MyLatch);
 
 	latch_sigusr1_handler();
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index 69fe23a..7b0e1bc 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -22,6 +22,7 @@
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
+#include "tcop/tcopprot.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 
@@ -44,8 +45,9 @@
 #define SIGNAL_BACKEND_ERROR 1
 #define SIGNAL_BACKEND_NOPERMISSION 2
 #define SIGNAL_BACKEND_NOSUPERUSER 3
+
 static int
-pg_signal_backend(int pid, int sig)
+check_valid_pid(int pid)
 {
 	PGPROC	   *proc = BackendPidGetProc(pid);
 
@@ -77,6 +79,17 @@ pg_signal_backend(int pid, int sig)
 		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_SIGNAL_BACKENDID))
 		return SIGNAL_BACKEND_NOPERMISSION;
 
+	return 0;
+}
+
+static int
+pg_signal_backend(int pid, int sig)
+{
+	int			ret = check_valid_pid(pid);
+
+	if (ret)
+		return ret;
+
 	/*
 	 * Can the process we just validated above end, followed by the pid being
 	 * recycled for a new process, before reaching here?  Then we'd be trying
@@ -215,3 +228,47 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_print_callstack - print callstack of backend process.
+ *
+ * Permission checking for this function is managed through the normal
+ * GRANT system.
+ */
+Datum
+pg_print_callstack(PG_FUNCTION_ARGS)
+{
+	int			bt_pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
+
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	{
+		int			r = check_valid_pid(bt_pid);
+
+		if (r == SIGNAL_BACKEND_NOSUPERUSER)
+			ereport(ERROR,
+					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+					 errmsg("must be a superuser to print backtrace of superuser query process")));
+
+		if (r == SIGNAL_BACKEND_NOPERMISSION)
+			ereport(ERROR,
+					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+					 errmsg("must be a member of the role whose query process's backtrace is being logged or member of pg_signal_backend")));
+
+		if (r)
+			PG_RETURN_BOOL(false);
+
+		if (EmitProcSignalPrintCallStack(bt_pid))
+			PG_RETURN_BOOL(true);
+
+		ereport(WARNING,
+				(errmsg("failed to send signal to postmaster: %m")));
+		PG_RETURN_BOOL(false);
+	}
+#else
+	{
+		ereport(WARNING,
+				(errmsg("backtrace generation is not supported by this installation")));
+		PG_RETURN_BOOL(false);
+	}
+#endif
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index cb5a961..a6f0ad3 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -19,6 +19,7 @@
 
 #include "postgres.h"
 
+#include <execinfo.h>
 #include <fcntl.h>
 #include <limits.h>
 #include <signal.h>
@@ -2921,6 +2922,39 @@ FloatExceptionHandler(SIGNAL_ARGS)
 }
 
 /*
+ * LogBackTrace
+ *
+ * Get the backtrace and log the backtrace to log file.
+ */
+void
+LogBackTrace(void)
+{
+	int			save_errno = errno;
+
+	void	   *buf[100];
+	int			nframes;
+	char	  **strfrms;
+	StringInfoData errtrace;
+
+	/* OK to process messages.  Reset the flag saying there are more to do. */
+	PrintBacktracePending = false;
+
+	nframes = backtrace(buf, lengthof(buf));
+	strfrms = backtrace_symbols(buf, nframes);
+	if (strfrms == NULL)
+		return;
+
+	initStringInfo(&errtrace);
+	for (int i = 0; i < nframes; i++)
+		appendStringInfo(&errtrace, "\n%s", strfrms[i]);
+	free(strfrms);
+
+	elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
+
+	errno = save_errno;
+}
+
+/*
  * RecoveryConflictInterrupt: out-of-line portion of recovery conflict
  * handling following receipt of SIGUSR1. Designed to be similar to die()
  * and StatementCancelHandler(). Called only by a normal user backend
@@ -3274,6 +3308,10 @@ ProcessInterrupts(void)
 
 	if (ParallelMessagePending)
 		HandleParallelMessages();
+
+	/* Process printing back trace */
+	if (PrintBacktracePending)
+		LogBackTrace();
 }
 
 
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index ea28769..0cac8d2 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -37,6 +37,7 @@ volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
+volatile sig_atomic_t PrintBacktracePending = false;
 
 int			MyProcPid;
 pg_time_t	MyStartTime;
diff --git a/src/bin/pg_ctl/t/005_backtrace.pl b/src/bin/pg_ctl/t/005_backtrace.pl
new file mode 100644
index 0000000..46e77d9
--- /dev/null
+++ b/src/bin/pg_ctl/t/005_backtrace.pl
@@ -0,0 +1,73 @@
+use strict;
+use warnings;
+
+use PostgresNode;
+use TestLib;
+use Test::More tests => 2;
+use Time::HiRes qw(usleep);
+
+# Set up node with logging collector
+my $node = get_new_node('primary');
+$node->init();
+$node->append_conf(
+	'postgresql.conf', qq(
+logging_collector = on
+lc_messages = 'C'
+));
+
+$node->start();
+
+# Verify that log output gets to the file
+$node->psql('postgres', 'select pg_print_callstack(pg_backend_pid())');
+
+# might need to retry if logging collector process is slow...
+my $max_attempts = 180 * 10;
+
+my $current_logfiles;
+for (my $attempts = 0; $attempts < $max_attempts; $attempts++)
+{
+	eval {
+		$current_logfiles = slurp_file($node->data_dir . '/current_logfiles');
+	};
+	last unless $@;
+	usleep(100_000);
+}
+die $@ if $@;
+
+note "current_logfiles = $current_logfiles";
+
+like(
+	$current_logfiles,
+	qr|^stderr log/postgresql-.*log$|,
+	'current_logfiles is sane');
+
+my $lfname = $current_logfiles;
+$lfname =~ s/^stderr //;
+chomp $lfname;
+
+my $first_logfile;
+my $bt_occurence_count;
+
+# Verify that the backtraces of the processes are logged into logfile.
+for (my $attempts = 0; $attempts < $max_attempts; $attempts++)
+{
+	$first_logfile = $node->data_dir . '/' . $lfname;
+	chomp $first_logfile;
+	print "file is $first_logfile";
+	open my $fh, '<', $first_logfile
+	  or die "Could not open '$first_logfile' $!";
+	while (my $line = <$fh>)
+	{
+		chomp $line;
+		if ($line =~ m/current backtrace/)
+		{
+			$bt_occurence_count++;
+		}
+	}
+	last if $bt_occurence_count == 1;
+	usleep(100_000);
+}
+
+is($bt_occurence_count, 1, 'found expected back trace in the log file');
+
+$node->stop();
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index b5f52d4..4c5ec4e 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11375,4 +11375,9 @@
   proname => 'is_normalized', prorettype => 'bool', proargtypes => 'text text',
   prosrc => 'unicode_is_normalized' },
 
+# function to get the callstack of server process
+{ oid => '6105', descr => 'print callstack of process',
+  proname => 'pg_print_callstack', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_print_callstack' },
+
 ]
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 1bdc97e..0d00ff9 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -92,6 +92,8 @@ extern PGDLLIMPORT volatile uint32 InterruptHoldoffCount;
 extern PGDLLIMPORT volatile uint32 QueryCancelHoldoffCount;
 extern PGDLLIMPORT volatile uint32 CritSectionCount;
 
+extern PGDLLIMPORT volatile sig_atomic_t PrintBacktracePending;
+
 /* in tcop/postgres.c */
 extern void ProcessInterrupts(void);
 
diff --git a/src/include/storage/pmsignal.h b/src/include/storage/pmsignal.h
index dbbed18..803f663 100644
--- a/src/include/storage/pmsignal.h
+++ b/src/include/storage/pmsignal.h
@@ -42,6 +42,8 @@ typedef enum
 	PMSIGNAL_START_WALRECEIVER, /* start a walreceiver */
 	PMSIGNAL_ADVANCE_STATE_MACHINE, /* advance postmaster's state machine */
 
+	PMSIGNAL_BACKTRACE_EMIT,	/* send PROCSIG_BACKTRACE_PRINT to backend */
+
 	NUM_PMSIGNALS				/* Must be last value of enum! */
 } PMSignalReason;
 
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index 4ae7dc3..3348494 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -43,6 +43,8 @@ typedef enum
 	PROCSIG_RECOVERY_CONFLICT_BUFFERPIN,
 	PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK,
 
+	PROCSIG_BACKTRACE_PRINT,	/* ask backend to print the current backtrace */
+
 	NUM_PROCSIGNALS				/* Must be last! */
 } ProcSignalReason;
 
@@ -71,5 +73,6 @@ extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
 
 extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
+extern bool EmitProcSignalPrintCallStack(int bt_pid);
 
 #endif							/* PROCSIGNAL_H */
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index e547210..0f8b74d 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -71,6 +71,7 @@ extern void RecoveryConflictInterrupt(ProcSignalReason reason); /* called from S
 extern void ProcessClientReadInterrupt(bool blocked);
 extern void ProcessClientWriteInterrupt(bool blocked);
 
+extern void LogBackTrace(void); /* Called from EmitProcSignalPrintCallStack */
 extern void process_postgres_switches(int argc, char *argv[],
 									  GucContext ctx, const char **dbname);
 extern void PostgresMain(int argc, char *argv[],
-- 
1.8.3.1

#32Andres Freund
andres@anarazel.de
In reply to: vignesh C (#31)
Re: Printing backtrace of postgres processes

Hi,

On 2021-01-27 19:05:16 +0530, vignesh C wrote:

/*
+ * LogBackTrace
+ *
+ * Get the backtrace and log the backtrace to log file.
+ */
+void
+LogBackTrace(void)
+{
+	int			save_errno = errno;
+
+	void	   *buf[100];
+	int			nframes;
+	char	  **strfrms;
+	StringInfoData errtrace;
+
+	/* OK to process messages.  Reset the flag saying there are more to do. */
+	PrintBacktracePending = false;

ISTM that it'd be better to do this in the caller, allowing this
function to be used outside of signal triggered backtraces.

+extern void LogBackTrace(void); /* Called from EmitProcSignalPrintCallStack */

I don't think this comment is correct anymore?

Greetings,

Andres Freund

#33vignesh C
vignesh21@gmail.com
In reply to: Andres Freund (#32)
1 attachment(s)
Re: Printing backtrace of postgres processes

On Wed, Jan 27, 2021 at 10:40 PM Andres Freund <andres@anarazel.de> wrote:

Hi,

On 2021-01-27 19:05:16 +0530, vignesh C wrote:

/*
+ * LogBackTrace
+ *
+ * Get the backtrace and log the backtrace to log file.
+ */
+void
+LogBackTrace(void)
+{
+     int                     save_errno = errno;
+
+     void       *buf[100];
+     int                     nframes;
+     char      **strfrms;
+     StringInfoData errtrace;
+
+     /* OK to process messages.  Reset the flag saying there are more to do. */
+     PrintBacktracePending = false;

ISTM that it'd be better to do this in the caller, allowing this
function to be used outside of signal triggered backtraces.

+extern void LogBackTrace(void); /* Called from EmitProcSignalPrintCallStack */

I don't think this comment is correct anymore?

Thanks for the comments, I have fixed and attached an updated patch
with the fixes for the same.
Thoughts?

Regards,
Vignesh

Attachments:

v4-0001-Print-backtrace-of-postgres-process-that-are-part.patchtext/x-patch; charset=US-ASCII; name=v4-0001-Print-backtrace-of-postgres-process-that-are-part.patchDownload
From f1d592cd02379974c8875a950f82fd4df13b7837 Mon Sep 17 00:00:00 2001
From: Vignesh C <vignesh.c@enterprisedb.com>
Date: Wed, 27 Jan 2021 18:20:13 +0530
Subject: [PATCH v4] Print backtrace of postgres process that are part of this
 instance.

The idea here is to implement & expose pg_print_callstack function, internally
what this function does is, the connected backend will send SIGUSR1 signal by
setting PMSIGNAL_BACKTRACE_EMIT to the postmaster process. Once the process
receives this signal it will log the backtrace of the process.
---
 doc/src/sgml/func.sgml                | 75 +++++++++++++++++++++++++++++++++++
 src/backend/postmaster/autovacuum.c   |  7 ++++
 src/backend/postmaster/checkpointer.c |  8 ++++
 src/backend/postmaster/interrupt.c    |  8 ++++
 src/backend/storage/ipc/procsignal.c  | 33 +++++++++++++++
 src/backend/storage/ipc/signalfuncs.c | 59 ++++++++++++++++++++++++++-
 src/backend/tcop/postgres.c           | 38 ++++++++++++++++++
 src/backend/utils/init/globals.c      |  1 +
 src/bin/pg_ctl/t/005_backtrace.pl     | 73 ++++++++++++++++++++++++++++++++++
 src/include/catalog/pg_proc.dat       |  5 +++
 src/include/miscadmin.h               |  2 +
 src/include/storage/pmsignal.h        |  2 +
 src/include/storage/procsignal.h      |  3 ++
 src/include/tcop/tcopprot.h           |  1 +
 14 files changed, 314 insertions(+), 1 deletion(-)
 create mode 100644 src/bin/pg_ctl/t/005_backtrace.pl

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index aa99665..4ff6e7f 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -24709,6 +24709,25 @@ SELECT collation for ('foo' COLLATE "de_DE");
         however only superusers can terminate superuser backends.
        </para></entry>
       </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_print_callstack</primary>
+        </indexterm>
+        <function>pg_print_callstack</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Prints the callstack whose backend process has the specified process ID.
+        Callstack will be printed based on the log configuration set. See
+        <xref linkend="runtime-config-logging"/> for more information.  This is
+        allowed if the calling role is a member of the role whose backend
+        callstack is being printed or the calling role has been granted
+        <literal>pg_print_callstack</literal>, however only superusers can
+        print callstack of superuser backends.
+       </para></entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
@@ -24728,6 +24747,62 @@ SELECT collation for ('foo' COLLATE "de_DE");
     <structname>pg_stat_activity</structname> view.
    </para>
 
+   <para>
+    <function>pg_print_callstack</function> can be used to print callstack of
+    a backend porcess. For example:
+<programlisting>
+postgres=# select pg_print_callstack(pg_backend_pid());
+ pg_print_callstack
+--------------------
+ t
+(1 row)
+
+The callstack will be logged in the log file. For example:
+2021-01-27 11:33:50.247 IST [111735] LOG:  current backtrace:
+        postgres: postgresdba postgres [local] SELECT(LogBackTrace+0x33) [0x9501cd]
+        postgres: postgresdba postgres [local] SELECT(ProcessInterrupts+0x774) [0x950bac]
+        postgres: postgresdba postgres [local] SELECT() [0x761ee8]
+        postgres: postgresdba postgres [local] SELECT() [0x71bc39]
+        postgres: postgresdba postgres [local] SELECT() [0x71e3df]
+        postgres: postgresdba postgres [local] SELECT(standard_ExecutorRun+0x1d6) [0x71c25d]
+        postgres: postgresdba postgres [local] SELECT(ExecutorRun+0x55) [0x71c085]
+        postgres: postgresdba postgres [local] SELECT() [0x953f3d]
+        postgres: postgresdba postgres [local] SELECT(PortalRun+0x262) [0x953bf6]
+        postgres: postgresdba postgres [local] SELECT() [0x94dafa]
+        postgres: postgresdba postgres [local] SELECT(PostgresMain+0x7d7) [0x951dea]
+        postgres: postgresdba postgres [local] SELECT() [0x896a7b]
+        postgres: postgresdba postgres [local] SELECT() [0x896401]
+        postgres: postgresdba postgres [local] SELECT() [0x8929c5]
+        postgres: postgresdba postgres [local] SELECT(PostmasterMain+0x116b) [0x89229c]
+        postgres: postgresdba postgres [local] SELECT() [0x795efa]
+        /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f6c12697505]
+        postgres: postgresdba postgres [local] SELECT() [0x484229]
+
+</programlisting>
+    You can get the file name and line number by using:
+<programlisting>
+1)  "info line *address" from gdb on postgres executable. For example:
+gdb ./postgres
+GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-115.el7
+Copyright (C) 2013 Free Software Foundation, Inc.
+License GPLv3+: GNU GPL version 3 or later <literal>&lt;</literal>http://gnu.org/licenses/gpl.html<literal>&gt;</literal>
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
+and "show warranty" for details.
+This GDB was configured as "x86_64-redhat-linux-gnu".
+For bug reporting instructions, please see:
+<literal>&lt;</literal>http://www.gnu.org/software/gdb/bugs/<literal>&gt;</literal>...
+Reading symbols from /home/vignesh/postgres/postgres/inst/bin/postgres...done.
+(gdb) info line *0x71c25d
+Line 378 of "execMain.c" starts at address 0x71c25d <literal>&lt;</literal>standard_ExecutorRun+470<literal>&gt;</literal> and ends at 0x71c263 <literal>&lt;</literal>standard_ExecutorRun+476<literal>&gt;</literal>.
+OR 
+2) Using "addr2line -e postgres address", For exmple:
+addr2line -e ./postgres 0x71c25d
+/home/vignesh/postgres/postgres/src/backend/executor/execMain.c:378
+</programlisting>
+</para>
+
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 47e60ca..0f27f78 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -836,6 +836,13 @@ HandleAutoVacLauncherInterrupts(void)
 	if (ProcSignalBarrierPending)
 		ProcessProcSignalBarrier();
 
+	/* Process printing back trace */
+	if (PrintBacktracePending)
+	{
+		PrintBacktracePending = false;
+		LogBackTrace();
+	}
+
 	/* Process sinval catchup interrupts that happened while sleeping */
 	ProcessCatchupInterrupt();
 }
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 54a818b..d26bf16 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -57,6 +57,7 @@
 #include "storage/shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
+#include "tcop/tcopprot.h"
 #include "utils/guc.h"
 #include "utils/memutils.h"
 #include "utils/resowner.h"
@@ -547,6 +548,13 @@ HandleCheckpointerInterrupts(void)
 	if (ProcSignalBarrierPending)
 		ProcessProcSignalBarrier();
 
+	/* Process printing back trace */
+	if (PrintBacktracePending)
+	{
+		PrintBacktracePending = false;
+		LogBackTrace();
+	}
+
 	if (ConfigReloadPending)
 	{
 		ConfigReloadPending = false;
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index dd9136a..8e1029b 100644
--- a/src/backend/postmaster/interrupt.c
+++ b/src/backend/postmaster/interrupt.c
@@ -21,6 +21,7 @@
 #include "storage/ipc.h"
 #include "storage/latch.h"
 #include "storage/procsignal.h"
+#include "tcop/tcopprot.h"
 #include "utils/guc.h"
 
 volatile sig_atomic_t ConfigReloadPending = false;
@@ -41,6 +42,13 @@ HandleMainLoopInterrupts(void)
 		ProcessConfigFile(PGC_SIGHUP);
 	}
 
+	/* Process printing back trace */
+	if (PrintBacktracePending)
+	{
+		PrintBacktracePending = false;
+		LogBackTrace();
+	}
+
 	if (ShutdownRequestPending)
 		proc_exit(0);
 }
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index c43cdd6..b0479aa 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -308,6 +308,21 @@ SendProcSignal(pid_t pid, ProcSignalReason reason, BackendId backendId)
 }
 
 /*
+ * EmitProcSignalPrintCallStack
+ *
+ * Send SIGUSR1 to postgres backend whose pid matches bt_pid by setting
+ * PROCSIG_BACKTRACE_PRINT, the postgres processes will print the backtrace once
+ * the signal is received.
+ */
+bool
+EmitProcSignalPrintCallStack(int bt_pid)
+{
+	int			ret = SendProcSignal(bt_pid, PROCSIG_BACKTRACE_PRINT, InvalidBackendId);
+
+	return (ret == 0);
+}
+
+/*
  * EmitProcSignalBarrier
  *		Send a signal to every Postgres process
  *
@@ -453,6 +468,21 @@ HandleProcSignalBarrierInterrupt(void)
 }
 
 /*
+ * Handle receipt of an print backtrace.
+ *
+ * Note: this is called within a signal handler!  All we can do is set
+ * a flag that will cause the next CHECK_FOR_INTERRUPTS() to invoke
+ * LogBackTrace().
+ */
+static void
+HandlePrintBacktraceInterrupt(void)
+{
+	InterruptPending = true;
+	PrintBacktracePending = true;
+	/* latch will be set by procsignal_sigusr1_handler */
+}
+
+/*
  * Perform global barrier related interrupt checking.
  *
  * Any backend that participates in ProcSignal signaling must arrange to
@@ -686,6 +716,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
 
+	if (CheckProcSignal(PROCSIG_BACKTRACE_PRINT))
+		HandlePrintBacktraceInterrupt();
+
 	SetLatch(MyLatch);
 
 	latch_sigusr1_handler();
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index 69fe23a..7b0e1bc 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -22,6 +22,7 @@
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
+#include "tcop/tcopprot.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 
@@ -44,8 +45,9 @@
 #define SIGNAL_BACKEND_ERROR 1
 #define SIGNAL_BACKEND_NOPERMISSION 2
 #define SIGNAL_BACKEND_NOSUPERUSER 3
+
 static int
-pg_signal_backend(int pid, int sig)
+check_valid_pid(int pid)
 {
 	PGPROC	   *proc = BackendPidGetProc(pid);
 
@@ -77,6 +79,17 @@ pg_signal_backend(int pid, int sig)
 		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_SIGNAL_BACKENDID))
 		return SIGNAL_BACKEND_NOPERMISSION;
 
+	return 0;
+}
+
+static int
+pg_signal_backend(int pid, int sig)
+{
+	int			ret = check_valid_pid(pid);
+
+	if (ret)
+		return ret;
+
 	/*
 	 * Can the process we just validated above end, followed by the pid being
 	 * recycled for a new process, before reaching here?  Then we'd be trying
@@ -215,3 +228,47 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_print_callstack - print callstack of backend process.
+ *
+ * Permission checking for this function is managed through the normal
+ * GRANT system.
+ */
+Datum
+pg_print_callstack(PG_FUNCTION_ARGS)
+{
+	int			bt_pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
+
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	{
+		int			r = check_valid_pid(bt_pid);
+
+		if (r == SIGNAL_BACKEND_NOSUPERUSER)
+			ereport(ERROR,
+					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+					 errmsg("must be a superuser to print backtrace of superuser query process")));
+
+		if (r == SIGNAL_BACKEND_NOPERMISSION)
+			ereport(ERROR,
+					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+					 errmsg("must be a member of the role whose query process's backtrace is being logged or member of pg_signal_backend")));
+
+		if (r)
+			PG_RETURN_BOOL(false);
+
+		if (EmitProcSignalPrintCallStack(bt_pid))
+			PG_RETURN_BOOL(true);
+
+		ereport(WARNING,
+				(errmsg("failed to send signal to postmaster: %m")));
+		PG_RETURN_BOOL(false);
+	}
+#else
+	{
+		ereport(WARNING,
+				(errmsg("backtrace generation is not supported by this installation")));
+		PG_RETURN_BOOL(false);
+	}
+#endif
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index cb5a961..7aefde3 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -19,6 +19,7 @@
 
 #include "postgres.h"
 
+#include <execinfo.h>
 #include <fcntl.h>
 #include <limits.h>
 #include <signal.h>
@@ -2921,6 +2922,36 @@ FloatExceptionHandler(SIGNAL_ARGS)
 }
 
 /*
+ * LogBackTrace
+ *
+ * Get the backtrace and log the backtrace to log file.
+ */
+void
+LogBackTrace(void)
+{
+	int			save_errno = errno;
+
+	void	   *buf[100];
+	int			nframes;
+	char	  **strfrms;
+	StringInfoData errtrace;
+
+	nframes = backtrace(buf, lengthof(buf));
+	strfrms = backtrace_symbols(buf, nframes);
+	if (strfrms == NULL)
+		return;
+
+	initStringInfo(&errtrace);
+	for (int i = 0; i < nframes; i++)
+		appendStringInfo(&errtrace, "\n%s", strfrms[i]);
+	free(strfrms);
+
+	elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
+
+	errno = save_errno;
+}
+
+/*
  * RecoveryConflictInterrupt: out-of-line portion of recovery conflict
  * handling following receipt of SIGUSR1. Designed to be similar to die()
  * and StatementCancelHandler(). Called only by a normal user backend
@@ -3274,6 +3305,13 @@ ProcessInterrupts(void)
 
 	if (ParallelMessagePending)
 		HandleParallelMessages();
+
+	/* Process printing back trace */
+	if (PrintBacktracePending)
+	{
+		PrintBacktracePending = false;
+		LogBackTrace();
+	}
 }
 
 
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index ea28769..0cac8d2 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -37,6 +37,7 @@ volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
+volatile sig_atomic_t PrintBacktracePending = false;
 
 int			MyProcPid;
 pg_time_t	MyStartTime;
diff --git a/src/bin/pg_ctl/t/005_backtrace.pl b/src/bin/pg_ctl/t/005_backtrace.pl
new file mode 100644
index 0000000..46e77d9
--- /dev/null
+++ b/src/bin/pg_ctl/t/005_backtrace.pl
@@ -0,0 +1,73 @@
+use strict;
+use warnings;
+
+use PostgresNode;
+use TestLib;
+use Test::More tests => 2;
+use Time::HiRes qw(usleep);
+
+# Set up node with logging collector
+my $node = get_new_node('primary');
+$node->init();
+$node->append_conf(
+	'postgresql.conf', qq(
+logging_collector = on
+lc_messages = 'C'
+));
+
+$node->start();
+
+# Verify that log output gets to the file
+$node->psql('postgres', 'select pg_print_callstack(pg_backend_pid())');
+
+# might need to retry if logging collector process is slow...
+my $max_attempts = 180 * 10;
+
+my $current_logfiles;
+for (my $attempts = 0; $attempts < $max_attempts; $attempts++)
+{
+	eval {
+		$current_logfiles = slurp_file($node->data_dir . '/current_logfiles');
+	};
+	last unless $@;
+	usleep(100_000);
+}
+die $@ if $@;
+
+note "current_logfiles = $current_logfiles";
+
+like(
+	$current_logfiles,
+	qr|^stderr log/postgresql-.*log$|,
+	'current_logfiles is sane');
+
+my $lfname = $current_logfiles;
+$lfname =~ s/^stderr //;
+chomp $lfname;
+
+my $first_logfile;
+my $bt_occurence_count;
+
+# Verify that the backtraces of the processes are logged into logfile.
+for (my $attempts = 0; $attempts < $max_attempts; $attempts++)
+{
+	$first_logfile = $node->data_dir . '/' . $lfname;
+	chomp $first_logfile;
+	print "file is $first_logfile";
+	open my $fh, '<', $first_logfile
+	  or die "Could not open '$first_logfile' $!";
+	while (my $line = <$fh>)
+	{
+		chomp $line;
+		if ($line =~ m/current backtrace/)
+		{
+			$bt_occurence_count++;
+		}
+	}
+	last if $bt_occurence_count == 1;
+	usleep(100_000);
+}
+
+is($bt_occurence_count, 1, 'found expected back trace in the log file');
+
+$node->stop();
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index b5f52d4..4c5ec4e 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11375,4 +11375,9 @@
   proname => 'is_normalized', prorettype => 'bool', proargtypes => 'text text',
   prosrc => 'unicode_is_normalized' },
 
+# function to get the callstack of server process
+{ oid => '6105', descr => 'print callstack of process',
+  proname => 'pg_print_callstack', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_print_callstack' },
+
 ]
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 1bdc97e..0d00ff9 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -92,6 +92,8 @@ extern PGDLLIMPORT volatile uint32 InterruptHoldoffCount;
 extern PGDLLIMPORT volatile uint32 QueryCancelHoldoffCount;
 extern PGDLLIMPORT volatile uint32 CritSectionCount;
 
+extern PGDLLIMPORT volatile sig_atomic_t PrintBacktracePending;
+
 /* in tcop/postgres.c */
 extern void ProcessInterrupts(void);
 
diff --git a/src/include/storage/pmsignal.h b/src/include/storage/pmsignal.h
index dbbed18..803f663 100644
--- a/src/include/storage/pmsignal.h
+++ b/src/include/storage/pmsignal.h
@@ -42,6 +42,8 @@ typedef enum
 	PMSIGNAL_START_WALRECEIVER, /* start a walreceiver */
 	PMSIGNAL_ADVANCE_STATE_MACHINE, /* advance postmaster's state machine */
 
+	PMSIGNAL_BACKTRACE_EMIT,	/* send PROCSIG_BACKTRACE_PRINT to backend */
+
 	NUM_PMSIGNALS				/* Must be last value of enum! */
 } PMSignalReason;
 
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index 4ae7dc3..3348494 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -43,6 +43,8 @@ typedef enum
 	PROCSIG_RECOVERY_CONFLICT_BUFFERPIN,
 	PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK,
 
+	PROCSIG_BACKTRACE_PRINT,	/* ask backend to print the current backtrace */
+
 	NUM_PROCSIGNALS				/* Must be last! */
 } ProcSignalReason;
 
@@ -71,5 +73,6 @@ extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
 
 extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
+extern bool EmitProcSignalPrintCallStack(int bt_pid);
 
 #endif							/* PROCSIGNAL_H */
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index e547210..7d640e9 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -71,6 +71,7 @@ extern void RecoveryConflictInterrupt(ProcSignalReason reason); /* called from S
 extern void ProcessClientReadInterrupt(bool blocked);
 extern void ProcessClientWriteInterrupt(bool blocked);
 
+extern void LogBackTrace(void); /* log the backtrace to log file */
 extern void process_postgres_switches(int argc, char *argv[],
 									  GucContext ctx, const char **dbname);
 extern void PostgresMain(int argc, char *argv[],
-- 
1.8.3.1

#34Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: vignesh C (#33)
Re: Printing backtrace of postgres processes

On Thu, Jan 28, 2021 at 5:22 PM vignesh C <vignesh21@gmail.com> wrote:

Thanks for the comments, I have fixed and attached an updated patch
with the fixes for the same.
Thoughts?

Thanks for the patch. Here are few comments:

1) I think it's return SIGNAL_BACKEND_SUCCESS; instead of return 0; in
check_valid_pid?

2) How about following in pg_signal_backend for more readability
+    if (ret != SIGNAL_BACKEND_SUCCESS)
+        return ret;
instead of
+    if (ret)
+        return ret;

3) How about validate_backend_pid or some better name instead of
check_valid_pid?

4) How about following
+                     errmsg("must be a superuser to print backtrace
of backend process")));
instead of
+                     errmsg("must be a superuser to print backtrace
of superuser query process")));

5) How about following
errmsg("must be a member of the role whose backed
process's backtrace is being printed or member of
pg_signal_backend")));
instead of
+ errmsg("must be a member of the role whose
backtrace is being logged or member of pg_signal_backend")));

6) I'm not sure whether "backtrace" or "call stack" is a generic term
from the user/developer perspective. In the patch, the function name
and documentation says callstack(I think it is "call stack" actually),
but the error/warning messages says backtrace. IMHO, having
"backtrace" everywhere in the patch, even the function name changed to
pg_print_backtrace, looks better and consistent. Thoughts?

7) How about following in pg_print_callstack?
{
int bt_pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
bool result = false;

if (r == SIGNAL_BACKEND_SUCCESS)
{
if (EmitProcSignalPrintCallStack(bt_pid))
result = true;
else
ereport(WARNING,
(errmsg("failed to send signal to postmaster: %m")));
}

PG_RETURN_BOOL(result);
}

8) How about following
+                (errmsg("backtrace generation is not supported by
this PostgresSQL installation")));
instead of
+                (errmsg("backtrace generation is not supported by
this installation")));

9) Typo - it's "example" +2) Using "addr2line -e postgres address", For exmple:

10) How about
+ * Handle print backtrace signal
instead of
+ * Handle receipt of an print backtrace.
11) Isn't below in documentation specific to Linux platform. What
happens if GDB is not there on the platform?
+<programlisting>
+1)  "info line *address" from gdb on postgres executable. For example:
+gdb ./postgres
+GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-115.el7

12) +The callstack will be logged in the log file. What happens if the
server is started without a log file , ./pg_ctl -D data start? Where
will the backtrace go?

13) Not sure, if it's an overkill, but how about pg_print_callstack
returning a warning/notice along with true, which just says, "See
<<<full log file name along with log directory>>>". Thoughts?

With Regards,
Bharath Rupireddy.
EnterpriseDB: http://www.enterprisedb.com

#35vignesh C
vignesh21@gmail.com
In reply to: Bharath Rupireddy (#34)
1 attachment(s)
Re: Printing backtrace of postgres processes

Thanks Bharath for your review comments. Please find my comments inline below.

On Thu, Jan 28, 2021 at 7:40 PM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Thu, Jan 28, 2021 at 5:22 PM vignesh C <vignesh21@gmail.com> wrote:

Thanks for the comments, I have fixed and attached an updated patch
with the fixes for the same.
Thoughts?

Thanks for the patch. Here are few comments:

1) I think it's return SIGNAL_BACKEND_SUCCESS; instead of return 0; in
check_valid_pid?

I did not want to use SIGNAL_BACKEND_SUCCESS as we have not yet
signalled the backend process at this time. I have added
BACKEND_VALIDATION_SUCCESS macro and used it here for better
readability.

2) How about following in pg_signal_backend for more readability
+    if (ret != SIGNAL_BACKEND_SUCCESS)
+        return ret;
instead of
+    if (ret)
+        return ret;

Modified it to ret != BACKEND_VALIDATION_SUCCESS

3) How about validate_backend_pid or some better name instead of
check_valid_pid?

Modified it to validate_backend_pid

4) How about following
+                     errmsg("must be a superuser to print backtrace
of backend process")));
instead of
+                     errmsg("must be a superuser to print backtrace
of superuser query process")));

Here the message should include superuser, we cannot remove it. Non
super user can log non super user provided if user has permissions for
it.

5) How about following
errmsg("must be a member of the role whose backed
process's backtrace is being printed or member of
pg_signal_backend")));
instead of
+ errmsg("must be a member of the role whose
backtrace is being logged or member of pg_signal_backend")));

Modified it.

6) I'm not sure whether "backtrace" or "call stack" is a generic term
from the user/developer perspective. In the patch, the function name
and documentation says callstack(I think it is "call stack" actually),
but the error/warning messages says backtrace. IMHO, having
"backtrace" everywhere in the patch, even the function name changed to
pg_print_backtrace, looks better and consistent. Thoughts?

Modified it to pg_print_backtrace.

7) How about following in pg_print_callstack?
{
int bt_pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
bool result = false;

if (r == SIGNAL_BACKEND_SUCCESS)
{
if (EmitProcSignalPrintCallStack(bt_pid))
result = true;
else
ereport(WARNING,
(errmsg("failed to send signal to postmaster: %m")));
}

PG_RETURN_BOOL(result);
}

Modified similarly with slight change.

8) How about following
+                (errmsg("backtrace generation is not supported by
this PostgresSQL installation")));
instead of
+                (errmsg("backtrace generation is not supported by
this installation")));

I used the existing message to maintain consistency with
set_backtrace. I feel we can keep it the same.

9) Typo - it's "example" +2) Using "addr2line -e postgres address", For exmple:

Modified it.

10) How about
+ * Handle print backtrace signal
instead of
+ * Handle receipt of an print backtrace.

I used the existing message to maintain consistency similar to
HandleProcSignalBarrierInterrupt. I feel we can keep it the same.

11) Isn't below in documentation specific to Linux platform. What
happens if GDB is not there on the platform?
+<programlisting>
+1)  "info line *address" from gdb on postgres executable. For example:
+gdb ./postgres
+GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-115.el7

I have made changes "You can get the file name and line number by
using gdb/addr2line in linux platforms, as a prerequisite users must
ensure gdb/addr2line is already installed".

User will get an error like this in windows:
select pg_print_backtrace(pg_backend_pid());
WARNING: backtrace generation is not supported by this installation
pg_print_callstack
--------------------
f
(1 row)

The backtrace will not be logged in case of windows, it will throw a
warning "backtrace generation is not supported by this installation"
Thoughts?

12) +The callstack will be logged in the log file. What happens if the
server is started without a log file , ./pg_ctl -D data start? Where
will the backtrace go?

Updated to: The backtrace will be logged to the log file if logging is
enabled, if logging is disabled backtrace will be logged to the
console where the postmaster was started.

13) Not sure, if it's an overkill, but how about pg_print_callstack
returning a warning/notice along with true, which just says, "See
<<<full log file name along with log directory>>>". Thoughts?

As you rightly pointed out it will be an overkill, I feel the existing
is easily understandable.

Attached v5 patch has the fixes for the same.
Thoughts?

Regards,
Vignesh

Attachments:

v5-0001-Print-backtrace-of-postgres-process-that-are-part.patchtext/x-patch; charset=US-ASCII; name=v5-0001-Print-backtrace-of-postgres-process-that-are-part.patchDownload
From 13564e153f4154d90a0b53e2b77c3e8d2df97544 Mon Sep 17 00:00:00 2001
From: Vignesh C <vignesh.c@enterprisedb.com>
Date: Wed, 27 Jan 2021 18:20:13 +0530
Subject: [PATCH v5] Print backtrace of postgres process that are part of this
 instance.

The idea here is to implement & expose pg_print_backtrace function, internally
what this function does is, the connected backend will send SIGUSR1 signal by
setting PMSIGNAL_BACKTRACE_EMIT to the postmaster process. Once the process
receives this signal it will log the backtrace of the process.
---
 doc/src/sgml/func.sgml                | 79 +++++++++++++++++++++++++++++++++++
 src/backend/postmaster/autovacuum.c   |  7 ++++
 src/backend/postmaster/checkpointer.c |  8 ++++
 src/backend/postmaster/interrupt.c    |  8 ++++
 src/backend/storage/ipc/procsignal.c  | 18 ++++++++
 src/backend/storage/ipc/signalfuncs.c | 67 ++++++++++++++++++++++++++++-
 src/backend/tcop/postgres.c           | 38 +++++++++++++++++
 src/backend/utils/init/globals.c      |  1 +
 src/bin/pg_ctl/t/005_backtrace.pl     | 73 ++++++++++++++++++++++++++++++++
 src/include/catalog/pg_proc.dat       |  5 +++
 src/include/miscadmin.h               |  2 +
 src/include/storage/pmsignal.h        |  2 +
 src/include/storage/procsignal.h      |  2 +
 src/include/tcop/tcopprot.h           |  1 +
 14 files changed, 310 insertions(+), 1 deletion(-)
 create mode 100644 src/bin/pg_ctl/t/005_backtrace.pl

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 4342c8e..fd96155 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -24720,6 +24720,25 @@ SELECT collation for ('foo' COLLATE "de_DE");
         however only superusers can terminate superuser backends.
        </para></entry>
       </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_print_backtrace</primary>
+        </indexterm>
+        <function>pg_print_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Prints the backtrace whose backend process has the specified process ID.
+        Backtrace will be printed based on the log configuration set. See
+        <xref linkend="runtime-config-logging"/> for more information.  This is
+        allowed if the calling role is a member of the role whose backend
+        backtrace is being printed or the calling role has been granted
+        <literal>pg_print_backtrace</literal>, however only superusers can
+        print backtrace of superuser backends.
+       </para></entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
@@ -24739,6 +24758,66 @@ SELECT collation for ('foo' COLLATE "de_DE");
     <structname>pg_stat_activity</structname> view.
    </para>
 
+   <para>
+    <function>pg_print_backtrace</function> can be used to print backtrace of
+    a backend porcess. For example:
+<programlisting>
+postgres=# select pg_print_backtrace(pg_backend_pid());
+ pg_print_backtrace
+--------------------
+ t
+(1 row)
+
+The backtrace will be logged to the log file if logging is enabled, if logging
+is disabled backtrace will be logged to the console where the postmaster was
+started. For example:
+2021-01-27 11:33:50.247 IST [111735] LOG:  current backtrace:
+        postgres: postgresdba postgres [local] SELECT(LogBackTrace+0x33) [0x9501cd]
+        postgres: postgresdba postgres [local] SELECT(ProcessInterrupts+0x774) [0x950bac]
+        postgres: postgresdba postgres [local] SELECT() [0x761ee8]
+        postgres: postgresdba postgres [local] SELECT() [0x71bc39]
+        postgres: postgresdba postgres [local] SELECT() [0x71e3df]
+        postgres: postgresdba postgres [local] SELECT(standard_ExecutorRun+0x1d6) [0x71c25d]
+        postgres: postgresdba postgres [local] SELECT(ExecutorRun+0x55) [0x71c085]
+        postgres: postgresdba postgres [local] SELECT() [0x953f3d]
+        postgres: postgresdba postgres [local] SELECT(PortalRun+0x262) [0x953bf6]
+        postgres: postgresdba postgres [local] SELECT() [0x94dafa]
+        postgres: postgresdba postgres [local] SELECT(PostgresMain+0x7d7) [0x951dea]
+        postgres: postgresdba postgres [local] SELECT() [0x896a7b]
+        postgres: postgresdba postgres [local] SELECT() [0x896401]
+        postgres: postgresdba postgres [local] SELECT() [0x8929c5]
+        postgres: postgresdba postgres [local] SELECT(PostmasterMain+0x116b) [0x89229c]
+        postgres: postgresdba postgres [local] SELECT() [0x795efa]
+        /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f6c12697505]
+        postgres: postgresdba postgres [local] SELECT() [0x484229]
+
+</programlisting>
+    You can get the file name and line number by using gdb/addr2line in
+    linux platforms, as a prerequisite users must ensure gdb/addr2line is
+    already installed:
+<programlisting>
+1)  "info line *address" from gdb on postgres executable. For example:
+gdb ./postgres
+GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-115.el7
+Copyright (C) 2013 Free Software Foundation, Inc.
+License GPLv3+: GNU GPL version 3 or later <literal>&lt;</literal>http://gnu.org/licenses/gpl.html<literal>&gt;</literal>
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
+and "show warranty" for details.
+This GDB was configured as "x86_64-redhat-linux-gnu".
+For bug reporting instructions, please see:
+<literal>&lt;</literal>http://www.gnu.org/software/gdb/bugs/<literal>&gt;</literal>...
+Reading symbols from /home/postgresdba/inst/bin/postgres...done.
+(gdb) info line *0x71c25d
+Line 378 of "execMain.c" starts at address 0x71c25d <literal>&lt;</literal>standard_ExecutorRun+470<literal>&gt;</literal> and ends at 0x71c263 <literal>&lt;</literal>standard_ExecutorRun+476<literal>&gt;</literal>.
+OR 
+2) Using "addr2line -e postgres address", For example:
+addr2line -e ./postgres 0x71c25d
+/home/postgresdba/src/backend/executor/execMain.c:378
+</programlisting>
+</para>
+
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 47e60ca..0f27f78 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -836,6 +836,13 @@ HandleAutoVacLauncherInterrupts(void)
 	if (ProcSignalBarrierPending)
 		ProcessProcSignalBarrier();
 
+	/* Process printing back trace */
+	if (PrintBacktracePending)
+	{
+		PrintBacktracePending = false;
+		LogBackTrace();
+	}
+
 	/* Process sinval catchup interrupts that happened while sleeping */
 	ProcessCatchupInterrupt();
 }
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 54a818b..d26bf16 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -57,6 +57,7 @@
 #include "storage/shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
+#include "tcop/tcopprot.h"
 #include "utils/guc.h"
 #include "utils/memutils.h"
 #include "utils/resowner.h"
@@ -547,6 +548,13 @@ HandleCheckpointerInterrupts(void)
 	if (ProcSignalBarrierPending)
 		ProcessProcSignalBarrier();
 
+	/* Process printing back trace */
+	if (PrintBacktracePending)
+	{
+		PrintBacktracePending = false;
+		LogBackTrace();
+	}
+
 	if (ConfigReloadPending)
 	{
 		ConfigReloadPending = false;
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index dd9136a..8e1029b 100644
--- a/src/backend/postmaster/interrupt.c
+++ b/src/backend/postmaster/interrupt.c
@@ -21,6 +21,7 @@
 #include "storage/ipc.h"
 #include "storage/latch.h"
 #include "storage/procsignal.h"
+#include "tcop/tcopprot.h"
 #include "utils/guc.h"
 
 volatile sig_atomic_t ConfigReloadPending = false;
@@ -41,6 +42,13 @@ HandleMainLoopInterrupts(void)
 		ProcessConfigFile(PGC_SIGHUP);
 	}
 
+	/* Process printing back trace */
+	if (PrintBacktracePending)
+	{
+		PrintBacktracePending = false;
+		LogBackTrace();
+	}
+
 	if (ShutdownRequestPending)
 		proc_exit(0);
 }
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index c43cdd6..f6c451c 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -453,6 +453,21 @@ HandleProcSignalBarrierInterrupt(void)
 }
 
 /*
+ * Handle receipt of an interrupt indicating a print backtrace.
+ *
+ * Note: this is called within a signal handler!  All we can do is set
+ * a flag that will cause the next CHECK_FOR_INTERRUPTS() to invoke
+ * LogBackTrace().
+ */
+static void
+HandlePrintBacktraceInterrupt(void)
+{
+	InterruptPending = true;
+	PrintBacktracePending = true;
+	/* latch will be set by procsignal_sigusr1_handler */
+}
+
+/*
  * Perform global barrier related interrupt checking.
  *
  * Any backend that participates in ProcSignal signaling must arrange to
@@ -686,6 +701,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
 
+	if (CheckProcSignal(PROCSIG_BACKTRACE_PRINT))
+		HandlePrintBacktraceInterrupt();
+
 	SetLatch(MyLatch);
 
 	latch_sigusr1_handler();
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index 69fe23a..03faca0 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -22,6 +22,7 @@
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
+#include "tcop/tcopprot.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 
@@ -41,11 +42,13 @@
  * the caller.
  */
 #define SIGNAL_BACKEND_SUCCESS 0
+#define BACKEND_VALIDATION_SUCCESS 0
 #define SIGNAL_BACKEND_ERROR 1
 #define SIGNAL_BACKEND_NOPERMISSION 2
 #define SIGNAL_BACKEND_NOSUPERUSER 3
+
 static int
-pg_signal_backend(int pid, int sig)
+validate_backend_pid(int pid)
 {
 	PGPROC	   *proc = BackendPidGetProc(pid);
 
@@ -77,6 +80,17 @@ pg_signal_backend(int pid, int sig)
 		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_SIGNAL_BACKENDID))
 		return SIGNAL_BACKEND_NOPERMISSION;
 
+	return BACKEND_VALIDATION_SUCCESS;
+}
+
+static int
+pg_signal_backend(int pid, int sig)
+{
+	int			ret = validate_backend_pid(pid);
+
+	if (ret != BACKEND_VALIDATION_SUCCESS)
+		return ret;
+
 	/*
 	 * Can the process we just validated above end, followed by the pid being
 	 * recycled for a new process, before reaching here?  Then we'd be trying
@@ -215,3 +229,54 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_print_backtrace - print backtrace of backend process.
+ *
+ * Permission checking for this function is managed through the normal
+ * GRANT system.
+ */
+Datum
+pg_print_backtrace(PG_FUNCTION_ARGS)
+{
+	int			bt_pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
+
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	{
+		bool 		result = false;
+		int			r = validate_backend_pid(bt_pid);
+
+		if (r == SIGNAL_BACKEND_NOSUPERUSER)
+			ereport(ERROR,
+					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+					 errmsg("must be a superuser to print backtrace of superuser process")));
+
+		if (r == SIGNAL_BACKEND_NOPERMISSION)
+			ereport(ERROR,
+					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+					 errmsg("must be a member of the role whose backtrace is being logged or member of pg_signal_backend")));
+
+		if (r == BACKEND_VALIDATION_SUCCESS)
+		{
+			/*
+			 * Send SIGUSR1 to postgres backend whose pid matches bt_pid by
+			 * setting PROCSIG_BACKTRACE_PRINT, the backend process will print
+			 * the backtrace once the signal is received.
+			 */
+			if (!SendProcSignal(bt_pid, PROCSIG_BACKTRACE_PRINT, InvalidBackendId))
+				PG_RETURN_BOOL(true);
+			else
+				ereport(WARNING,
+						(errmsg("failed to send signal to postmaster: %m")));
+		}
+
+		PG_RETURN_BOOL(result);
+	}
+#else
+	{
+		ereport(WARNING,
+				(errmsg("backtrace generation is not supported by this installation")));
+		PG_RETURN_BOOL(false);
+	}
+#endif
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index cb5a961..7aefde3 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -19,6 +19,7 @@
 
 #include "postgres.h"
 
+#include <execinfo.h>
 #include <fcntl.h>
 #include <limits.h>
 #include <signal.h>
@@ -2921,6 +2922,36 @@ FloatExceptionHandler(SIGNAL_ARGS)
 }
 
 /*
+ * LogBackTrace
+ *
+ * Get the backtrace and log the backtrace to log file.
+ */
+void
+LogBackTrace(void)
+{
+	int			save_errno = errno;
+
+	void	   *buf[100];
+	int			nframes;
+	char	  **strfrms;
+	StringInfoData errtrace;
+
+	nframes = backtrace(buf, lengthof(buf));
+	strfrms = backtrace_symbols(buf, nframes);
+	if (strfrms == NULL)
+		return;
+
+	initStringInfo(&errtrace);
+	for (int i = 0; i < nframes; i++)
+		appendStringInfo(&errtrace, "\n%s", strfrms[i]);
+	free(strfrms);
+
+	elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
+
+	errno = save_errno;
+}
+
+/*
  * RecoveryConflictInterrupt: out-of-line portion of recovery conflict
  * handling following receipt of SIGUSR1. Designed to be similar to die()
  * and StatementCancelHandler(). Called only by a normal user backend
@@ -3274,6 +3305,13 @@ ProcessInterrupts(void)
 
 	if (ParallelMessagePending)
 		HandleParallelMessages();
+
+	/* Process printing back trace */
+	if (PrintBacktracePending)
+	{
+		PrintBacktracePending = false;
+		LogBackTrace();
+	}
 }
 
 
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index a5976ad..e2b5ad5 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -37,6 +37,7 @@ volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
+volatile sig_atomic_t PrintBacktracePending = false;
 
 int			MyProcPid;
 pg_time_t	MyStartTime;
diff --git a/src/bin/pg_ctl/t/005_backtrace.pl b/src/bin/pg_ctl/t/005_backtrace.pl
new file mode 100644
index 0000000..cc5022d
--- /dev/null
+++ b/src/bin/pg_ctl/t/005_backtrace.pl
@@ -0,0 +1,73 @@
+use strict;
+use warnings;
+
+use PostgresNode;
+use TestLib;
+use Test::More tests => 2;
+use Time::HiRes qw(usleep);
+
+# Set up node with logging collector
+my $node = get_new_node('primary');
+$node->init();
+$node->append_conf(
+	'postgresql.conf', qq(
+logging_collector = on
+lc_messages = 'C'
+));
+
+$node->start();
+
+# Verify that log output gets to the file
+$node->psql('postgres', 'select pg_print_backtrace(pg_backend_pid())');
+
+# might need to retry if logging collector process is slow...
+my $max_attempts = 180 * 10;
+
+my $current_logfiles;
+for (my $attempts = 0; $attempts < $max_attempts; $attempts++)
+{
+	eval {
+		$current_logfiles = slurp_file($node->data_dir . '/current_logfiles');
+	};
+	last unless $@;
+	usleep(100_000);
+}
+die $@ if $@;
+
+note "current_logfiles = $current_logfiles";
+
+like(
+	$current_logfiles,
+	qr|^stderr log/postgresql-.*log$|,
+	'current_logfiles is sane');
+
+my $lfname = $current_logfiles;
+$lfname =~ s/^stderr //;
+chomp $lfname;
+
+my $first_logfile;
+my $bt_occurence_count;
+
+# Verify that the backtraces of the processes are logged into logfile.
+for (my $attempts = 0; $attempts < $max_attempts; $attempts++)
+{
+	$first_logfile = $node->data_dir . '/' . $lfname;
+	chomp $first_logfile;
+	print "file is $first_logfile";
+	open my $fh, '<', $first_logfile
+	  or die "Could not open '$first_logfile' $!";
+	while (my $line = <$fh>)
+	{
+		chomp $line;
+		if ($line =~ m/current backtrace/)
+		{
+			$bt_occurence_count++;
+		}
+	}
+	last if $bt_occurence_count == 1;
+	usleep(100_000);
+}
+
+is($bt_occurence_count, 1, 'found expected back trace in the log file');
+
+$node->stop();
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index b5f52d4..7fb5ce0 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11375,4 +11375,9 @@
   proname => 'is_normalized', prorettype => 'bool', proargtypes => 'text text',
   prosrc => 'unicode_is_normalized' },
 
+# function to get the backtrace of server process
+{ oid => '6105', descr => 'print backtrace of process',
+  proname => 'pg_print_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_print_backtrace' },
+
 ]
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 1bdc97e..0d00ff9 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -92,6 +92,8 @@ extern PGDLLIMPORT volatile uint32 InterruptHoldoffCount;
 extern PGDLLIMPORT volatile uint32 QueryCancelHoldoffCount;
 extern PGDLLIMPORT volatile uint32 CritSectionCount;
 
+extern PGDLLIMPORT volatile sig_atomic_t PrintBacktracePending;
+
 /* in tcop/postgres.c */
 extern void ProcessInterrupts(void);
 
diff --git a/src/include/storage/pmsignal.h b/src/include/storage/pmsignal.h
index dbbed18..803f663 100644
--- a/src/include/storage/pmsignal.h
+++ b/src/include/storage/pmsignal.h
@@ -42,6 +42,8 @@ typedef enum
 	PMSIGNAL_START_WALRECEIVER, /* start a walreceiver */
 	PMSIGNAL_ADVANCE_STATE_MACHINE, /* advance postmaster's state machine */
 
+	PMSIGNAL_BACKTRACE_EMIT,	/* send PROCSIG_BACKTRACE_PRINT to backend */
+
 	NUM_PMSIGNALS				/* Must be last value of enum! */
 } PMSignalReason;
 
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index 4ae7dc3..f31b97a 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -43,6 +43,8 @@ typedef enum
 	PROCSIG_RECOVERY_CONFLICT_BUFFERPIN,
 	PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK,
 
+	PROCSIG_BACKTRACE_PRINT,	/* ask backend to print the current backtrace */
+
 	NUM_PROCSIGNALS				/* Must be last! */
 } ProcSignalReason;
 
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index e547210..7d640e9 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -71,6 +71,7 @@ extern void RecoveryConflictInterrupt(ProcSignalReason reason); /* called from S
 extern void ProcessClientReadInterrupt(bool blocked);
 extern void ProcessClientWriteInterrupt(bool blocked);
 
+extern void LogBackTrace(void); /* log the backtrace to log file */
 extern void process_postgres_switches(int argc, char *argv[],
 									  GucContext ctx, const char **dbname);
 extern void PostgresMain(int argc, char *argv[],
-- 
1.8.3.1

#36Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: vignesh C (#35)
Re: Printing backtrace of postgres processes

On Fri, Jan 29, 2021 at 7:10 PM vignesh C <vignesh21@gmail.com> wrote:

4) How about following
+                     errmsg("must be a superuser to print backtrace
of backend process")));
instead of
+                     errmsg("must be a superuser to print backtrace
of superuser query process")));

Here the message should include superuser, we cannot remove it. Non
super user can log non super user provided if user has permissions for
it.

5) How about following
errmsg("must be a member of the role whose backed
process's backtrace is being printed or member of
pg_signal_backend")));
instead of
+ errmsg("must be a member of the role whose
backtrace is being logged or member of pg_signal_backend")));

Modified it.

Maybe I'm confused here to understand the difference between
SIGNAL_BACKEND_NOSUPERUSER and SIGNAL_BACKEND_NOPERMISSION macros and
corresponding error messages. Some clarification/use case to know in
which scenarios we hit those error messages might help me. Did we try
to add test cases that show up these error messages for
pg_print_backtrace? If not, can we add?

Attached v5 patch has the fixes for the same.
Thoughts?

Thanks. Here are some comments on v5 patch:

1) typo - it's "process" not "porcess" + a backend porcess. For example:

2) select * from pg_print_backtrace(NULL);
postgres=# select proname, proisstrict from pg_proc where proname =
'pg_print_backtrace';
proname | proisstrict
--------------------+-------------
pg_print_backtrace | t

See the documentation:
"proisstrict bool

Function returns null if any call argument is null. In that case the
function won't actually be called at all. Functions that are not
“strict” must be prepared to handle null inputs."
So below PG_ARGISNUL check is not needed, you can remove that, because
pg_print_backtrace will not get called with null input.
int bt_pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);

3) Can we just set results = true instead of PG_RETURN_BOOL(true); so
that it will be returned from PG_RETURN_BOOL(result); just for
consistency?
if (!SendProcSignal(bt_pid, PROCSIG_BACKTRACE_PRINT,
InvalidBackendId))
PG_RETURN_BOOL(true);
else
ereport(WARNING,
(errmsg("failed to send signal to postmaster: %m")));
}

PG_RETURN_BOOL(result);

4) Below is what happens if I request for a backtrace of the
postmaster process? 1388210 is pid of postmaster.
postgres=# select * from pg_print_backtrace(1388210);
WARNING: PID 1388210 is not a PostgreSQL server process
pg_print_backtrace
--------------------
f

Does it make sense to have a postmaster's backtrace? If yes, can we
have something like below in sigusr1_handler()?
if (CheckPostmasterSignal(PMSIGNAL_BACKTRACE_EMIT))
{
LogBackTrace();
}

5) Can we have PROCSIG_PRINT_BACKTRACE instead of
PROCSIG_BACKTRACE_PRINT, just for readability and consistency with
function name?

6) I think it's not the postmaster that prints backtrace of the
requested backend and we don't send SIGUSR1 with
PMSIGNAL_BACKTRACE_EMIT to postmaster, I think it's the backends, upon
receiving SIGUSR1 with PMSIGNAL_BACKTRACE_EMIT signal, log their own
backtrace. Am I missing anything here? If I'm correct, we need to
change the below description and other places wherever we refer to
this description.

The idea here is to implement & expose pg_print_backtrace function, internally
what this function does is, the connected backend will send SIGUSR1 signal by
setting PMSIGNAL_BACKTRACE_EMIT to the postmaster process. Once the process
receives this signal it will log the backtrace of the process.

7) Can we get the parallel worker's backtrace? IIUC it's possible.

8) What happens if a user runs pg_print_backtrace() on Windows or
MacOS or some other platform? Do you want to say something about other
platforms where gdb/addr2line doesn't exist?
+</programlisting>
+    You can get the file name and line number by using gdb/addr2line in
+    linux platforms, as a prerequisite users must ensure gdb/addr2line is
+    already installed:
+<programlisting>

9) Can't we reuse set_backtrace with just adding a flag to
set_backtrace(ErrorData *edata, int num_skip, bool
is_print_backtrace_function), making it a non-static function and call
set_backtrace(NULL, 0, true)?

void
set_backtrace(ErrorData *edata, int num_skip, bool is_print_backtrace_function)
{
StringInfoData errtrace;
-------
-------
if (is_print_backtrace_function)
elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
else
edata->backtrace = errtrace.data;
}

I think it will be good if we do this, because we can avoid duplicate
code in set_backtrace and LogBackTrace.

10) I think it's "pg_signal_backend" instead of "pg_print_backtrace"?
+        backtrace is being printed or the calling role has been granted
+        <literal>pg_print_backtrace</literal>, however only superusers can

11) In the documentation added, isn't it good if we talk a bit about
in which scenarios users can use this function? For instance,
something like "Use pg_print_backtrace to know exactly where it's
currently waiting when a backend process hangs."?

With Regards,
Bharath Rupireddy.
EnterpriseDB: http://www.enterprisedb.com

#37Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: Bharath Rupireddy (#36)
Re: Printing backtrace of postgres processes

On Mon, Feb 1, 2021 at 6:14 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Fri, Jan 29, 2021 at 7:10 PM vignesh C <vignesh21@gmail.com> wrote:

4) How about following
+                     errmsg("must be a superuser to print backtrace
of backend process")));
instead of
+                     errmsg("must be a superuser to print backtrace
of superuser query process")));

Here the message should include superuser, we cannot remove it. Non
super user can log non super user provided if user has permissions for
it.

5) How about following
errmsg("must be a member of the role whose backed
process's backtrace is being printed or member of
pg_signal_backend")));
instead of
+ errmsg("must be a member of the role whose
backtrace is being logged or member of pg_signal_backend")));

Modified it.

Maybe I'm confused here to understand the difference between
SIGNAL_BACKEND_NOSUPERUSER and SIGNAL_BACKEND_NOPERMISSION macros and
corresponding error messages. Some clarification/use case to know in
which scenarios we hit those error messages might help me. Did we try
to add test cases that show up these error messages for
pg_print_backtrace? If not, can we add?

Are these superuser and permission checks enough from a security
standpoint that we don't expose some sensitive information to the
user? Although I'm not sure, say from the backtrace printed and
attached to GDB, can users see the passwords or other sensitive
information from the system that they aren't supposed to see?

I'm sure this point would have been discussed upthread.

With Regards,
Bharath Rupireddy.
EnterpriseDB: http://www.enterprisedb.com

#38Kyotaro Horiguchi
horikyota.ntt@gmail.com
In reply to: vignesh C (#35)
Re: Printing backtrace of postgres processes

At Fri, 29 Jan 2021 19:10:24 +0530, vignesh C <vignesh21@gmail.com> wrote in

Attached v5 patch has the fixes for the same.

PMSIGNAL_BACKTRACE_EMIT is not used anywhere?
(the commit message seems stale.)

+++ b/src/bin/pg_ctl/t/005_backtrace.pl

pg_ctl doesn't (or no longer?) seem relevant to this patch.

+	eval {
+		$current_logfiles = slurp_file($node->data_dir . '/current_logfiles');
+	};
+	last unless $@;

Is there any reason not just to do "while (! -e <filenmae)) { usleep(); }" ?

+logging_collector = on

I don't see a reason for turning on logging collector here.

+gdb ./postgres
+GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-115.el7
+Copyright (C) 2013 Free Software Foundation, Inc.
+License GPLv3+: GNU GPL version 3 or later <literal>&lt;</literal>http://gnu.org/licenses/gpl.html<literal>&gt;</literal>
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
+and "show warranty" for details.
+This GDB was configured as "x86_64-redhat-linux-gnu".
+For bug reporting instructions, please see:
+<literal>&lt;</literal>http://www.gnu.org/software/gdb/bugs/<literal>&gt;</literal>...
+Reading symbols from /home/postgresdba/inst/bin/postgres...done.

Almost all of the banner lines seem to be useless here.

#define SIGNAL_BACKEND_SUCCESS 0
+#define BACKEND_VALIDATION_SUCCESS 0
#define SIGNAL_BACKEND_ERROR 1
#define SIGNAL_BACKEND_NOPERMISSION 2
#define SIGNAL_BACKEND_NOSUPERUSER 3

Even though I can share the feeling that SIGNAL_BACKEND_SUCCESS is a
bit odd to represent "sending signal is allowed", I feel that it's
better using the existing symbol than using the new symbol.

+validate_backend_pid(int pid)

The function needs a comment. The name is somewhat
confusing. check_privilege_to_send_singal()?

Maybe the return value of the function should be changed to an enum,
and its callers should use switch-case to handle the value.

+			if (!SendProcSignal(bt_pid, PROCSIG_BACKTRACE_PRINT, InvalidBackendId))
+				PG_RETURN_BOOL(true);
+			else
+				ereport(WARNING,
+						(errmsg("failed to send signal to postmaster: %m")));
+		}
+
+		PG_RETURN_BOOL(result);

The variable "result" seems useless.

+	elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
+
+	errno = save_errno;
+}

You need to release the resouces held by the errtrace. And the
errtrace is a bit pointless. Why isn't it "backtrace"?

regards.

--
Kyotaro Horiguchi
NTT Open Source Software Center

#39Dilip Kumar
dilipbalaut@gmail.com
In reply to: vignesh C (#35)
Re: Printing backtrace of postgres processes

On Fri, Jan 29, 2021 at 7:10 PM vignesh C <vignesh21@gmail.com> wrote:

Thanks Bharath for your review comments. Please find my comments inline below.

On Thu, Jan 28, 2021 at 7:40 PM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Thu, Jan 28, 2021 at 5:22 PM vignesh C <vignesh21@gmail.com> wrote:

Thanks for the comments, I have fixed and attached an updated patch
with the fixes for the same.
Thoughts?

Thanks for the patch. Here are few comments:

1) I think it's return SIGNAL_BACKEND_SUCCESS; instead of return 0; in
check_valid_pid?

I did not want to use SIGNAL_BACKEND_SUCCESS as we have not yet
signalled the backend process at this time. I have added
BACKEND_VALIDATION_SUCCESS macro and used it here for better
readability.

2) How about following in pg_signal_backend for more readability
+    if (ret != SIGNAL_BACKEND_SUCCESS)
+        return ret;
instead of
+    if (ret)
+        return ret;

Modified it to ret != BACKEND_VALIDATION_SUCCESS

3) How about validate_backend_pid or some better name instead of
check_valid_pid?

Modified it to validate_backend_pid

4) How about following
+                     errmsg("must be a superuser to print backtrace
of backend process")));
instead of
+                     errmsg("must be a superuser to print backtrace
of superuser query process")));

Here the message should include superuser, we cannot remove it. Non
super user can log non super user provided if user has permissions for
it.

5) How about following
errmsg("must be a member of the role whose backed
process's backtrace is being printed or member of
pg_signal_backend")));
instead of
+ errmsg("must be a member of the role whose
backtrace is being logged or member of pg_signal_backend")));

Modified it.

6) I'm not sure whether "backtrace" or "call stack" is a generic term
from the user/developer perspective. In the patch, the function name
and documentation says callstack(I think it is "call stack" actually),
but the error/warning messages says backtrace. IMHO, having
"backtrace" everywhere in the patch, even the function name changed to
pg_print_backtrace, looks better and consistent. Thoughts?

Modified it to pg_print_backtrace.

7) How about following in pg_print_callstack?
{
int bt_pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
bool result = false;

if (r == SIGNAL_BACKEND_SUCCESS)
{
if (EmitProcSignalPrintCallStack(bt_pid))
result = true;
else
ereport(WARNING,
(errmsg("failed to send signal to postmaster: %m")));
}

PG_RETURN_BOOL(result);
}

Modified similarly with slight change.

8) How about following
+                (errmsg("backtrace generation is not supported by
this PostgresSQL installation")));
instead of
+                (errmsg("backtrace generation is not supported by
this installation")));

I used the existing message to maintain consistency with
set_backtrace. I feel we can keep it the same.

9) Typo - it's "example" +2) Using "addr2line -e postgres address", For exmple:

Modified it.

10) How about
+ * Handle print backtrace signal
instead of
+ * Handle receipt of an print backtrace.

I used the existing message to maintain consistency similar to
HandleProcSignalBarrierInterrupt. I feel we can keep it the same.

11) Isn't below in documentation specific to Linux platform. What
happens if GDB is not there on the platform?
+<programlisting>
+1)  "info line *address" from gdb on postgres executable. For example:
+gdb ./postgres
+GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-115.el7

I have made changes "You can get the file name and line number by
using gdb/addr2line in linux platforms, as a prerequisite users must
ensure gdb/addr2line is already installed".

User will get an error like this in windows:
select pg_print_backtrace(pg_backend_pid());
WARNING: backtrace generation is not supported by this installation
pg_print_callstack
--------------------
f
(1 row)

The backtrace will not be logged in case of windows, it will throw a
warning "backtrace generation is not supported by this installation"
Thoughts?

12) +The callstack will be logged in the log file. What happens if the
server is started without a log file , ./pg_ctl -D data start? Where
will the backtrace go?

Updated to: The backtrace will be logged to the log file if logging is
enabled, if logging is disabled backtrace will be logged to the
console where the postmaster was started.

13) Not sure, if it's an overkill, but how about pg_print_callstack
returning a warning/notice along with true, which just says, "See
<<<full log file name along with log directory>>>". Thoughts?

As you rightly pointed out it will be an overkill, I feel the existing
is easily understandable.

Attached v5 patch has the fixes for the same.
Thoughts?

1.
+ elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
+
+ errno = save_errno;

Can you add some comments that why we have chosen LOG_SERVER_ONLY?

2.
+pg_print_backtrace(PG_FUNCTION_ARGS)
+{
+ int bt_pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
+

The variable name bt_pid is a bit odd, can we just use pid?

3.
+Datum
+pg_print_backtrace(PG_FUNCTION_ARGS)
+{
+ int bt_pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
+
+#ifdef HAVE_BACKTRACE_SYMBOLS
+ {
+ bool result = false;
...
+
+ PG_RETURN_BOOL(result);
+ }
+#else
+ {
+ ereport(WARNING,
+ (errmsg("backtrace generation is not supported by this installation")));
+ PG_RETURN_BOOL(false);
+ }
+#endif

The result is just initialized to false and it is never updated?

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#40vignesh C
vignesh21@gmail.com
In reply to: Bharath Rupireddy (#36)
1 attachment(s)
Re: Printing backtrace of postgres processes

Thanks Bharath for your comments.

On Mon, Feb 1, 2021 at 6:14 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Fri, Jan 29, 2021 at 7:10 PM vignesh C <vignesh21@gmail.com> wrote:

4) How about following
+                     errmsg("must be a superuser to print backtrace
of backend process")));
instead of
+                     errmsg("must be a superuser to print backtrace
of superuser query process")));

Here the message should include superuser, we cannot remove it. Non
super user can log non super user provided if user has permissions for
it.

5) How about following
errmsg("must be a member of the role whose backed
process's backtrace is being printed or member of
pg_signal_backend")));
instead of
+ errmsg("must be a member of the role whose
backtrace is being logged or member of pg_signal_backend")));

Modified it.

Maybe I'm confused here to understand the difference between
SIGNAL_BACKEND_NOSUPERUSER and SIGNAL_BACKEND_NOPERMISSION macros and
corresponding error messages. Some clarification/use case to know in
which scenarios we hit those error messages might help me. Did we try
to add test cases that show up these error messages for
pg_print_backtrace? If not, can we add?

I have tested this manually:

I have tested it manually, Here is the test I did:
Create 2 users:
create user test password 'test@123';
create user test1 password 'test@123';

Test1: Test print backtrace of a different user's session:
./psql -d postgres -U test
psql (14devel)
Type "help" for help.
postgres=> select pg_backend_pid();
pg_backend_pid
----------------
30142
(1 row)
------------------------------------------
./psql -d postgres -U test1
psql (14devel)
Type "help" for help.
postgres=> select pg_print_backtrace(30142);
ERROR: must be a member of the role whose backtrace is being logged
or member of pg_signal_backend

The above will be successful after:
grant pg_signal_backend to test1;

Test1: Test for non super user trying to print backtrace of a super
user's session:
./psql -d postgres
psql (14devel)
Type "help" for help.
postgres=# select pg_backend_pid();
pg_backend_pid
----------------
30211
(1 row)

./psql -d postgres -U test1
psql (14devel)
Type "help" for help.
postgres=> select pg_print_backtrace(30211);
ERROR: must be a superuser to print backtrace of superuser process
I have not added any tests for this as we required 2 active sessions
and I did not see any existing framework for this.
This test should help in relating the behavior.

Attached v5 patch has the fixes for the same.
Thoughts?

Thanks. Here are some comments on v5 patch:

1) typo - it's "process" not "porcess" + a backend porcess. For example:

Modified.

2) select * from pg_print_backtrace(NULL);
postgres=# select proname, proisstrict from pg_proc where proname =
'pg_print_backtrace';
proname | proisstrict
--------------------+-------------
pg_print_backtrace | t

See the documentation:
"proisstrict bool

Function returns null if any call argument is null. In that case the
function won't actually be called at all. Functions that are not
“strict” must be prepared to handle null inputs."
So below PG_ARGISNUL check is not needed, you can remove that, because
pg_print_backtrace will not get called with null input.
int bt_pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);

Modified.

3) Can we just set results = true instead of PG_RETURN_BOOL(true); so
that it will be returned from PG_RETURN_BOOL(result); just for
consistency?
if (!SendProcSignal(bt_pid, PROCSIG_BACKTRACE_PRINT,
InvalidBackendId))
PG_RETURN_BOOL(true);
else
ereport(WARNING,
(errmsg("failed to send signal to postmaster: %m")));
}

PG_RETURN_BOOL(result);

I felt existing is better as it will reduce one instruction of setting
first and then returning. There are only 2 places from where we
return. I felt we could directly return true or false.

4) Below is what happens if I request for a backtrace of the
postmaster process? 1388210 is pid of postmaster.
postgres=# select * from pg_print_backtrace(1388210);
WARNING: PID 1388210 is not a PostgreSQL server process
pg_print_backtrace
--------------------
f

Does it make sense to have a postmaster's backtrace? If yes, can we
have something like below in sigusr1_handler()?
if (CheckPostmasterSignal(PMSIGNAL_BACKTRACE_EMIT))
{
LogBackTrace();
}

We had a discussion about this in [1]https:://www.postgresql.org/message-id/1778088.1606026308%40sss.pgh.pa.us Attached v6 patch with the fixes. Thoughts? earlier and felt including this
is not very useful.

5) Can we have PROCSIG_PRINT_BACKTRACE instead of
PROCSIG_BACKTRACE_PRINT, just for readability and consistency with
function name?

PROCSIG_PRINT_BACKTRACE is better, I have modified it.

6) I think it's not the postmaster that prints backtrace of the
requested backend and we don't send SIGUSR1 with
PMSIGNAL_BACKTRACE_EMIT to postmaster, I think it's the backends, upon
receiving SIGUSR1 with PMSIGNAL_BACKTRACE_EMIT signal, log their own
backtrace. Am I missing anything here? If I'm correct, we need to
change the below description and other places wherever we refer to
this description.

The idea here is to implement & expose pg_print_backtrace function, internally
what this function does is, the connected backend will send SIGUSR1 signal by
setting PMSIGNAL_BACKTRACE_EMIT to the postmaster process. Once the process
receives this signal it will log the backtrace of the process.

Modified.

7) Can we get the parallel worker's backtrace? IIUC it's possible.

Yes we can get the parallel worker's backtrace. As this design is
driven based on CHECK_FOR_INTERRUPTS, it will be printed when
CHECK_FOR_INTERRUPTS is called.

8) What happens if a user runs pg_print_backtrace() on Windows or
MacOS or some other platform? Do you want to say something about other
platforms where gdb/addr2line doesn't exist?
+</programlisting>
+    You can get the file name and line number by using gdb/addr2line in
+    linux platforms, as a prerequisite users must ensure gdb/addr2line is
+    already installed:
+<programlisting>

I have added the following:
This feature will be available, if the installer was generated on a
platform which had backtrace capturing capability. If not available it
will return false by throwing the following warning "WARNING:
backtrace generation is not supported by this installation"

9) Can't we reuse set_backtrace with just adding a flag to
set_backtrace(ErrorData *edata, int num_skip, bool
is_print_backtrace_function), making it a non-static function and call
set_backtrace(NULL, 0, true)?

void
set_backtrace(ErrorData *edata, int num_skip, bool is_print_backtrace_function)
{
StringInfoData errtrace;
-------
-------
if (is_print_backtrace_function)
elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
else
edata->backtrace = errtrace.data;
}

I think it will be good if we do this, because we can avoid duplicate
code in set_backtrace and LogBackTrace.

Yes it is better, I have modified it to use the same function.

10) I think it's "pg_signal_backend" instead of "pg_print_backtrace"?
+        backtrace is being printed or the calling role has been granted
+        <literal>pg_print_backtrace</literal>, however only superusers can

Modified.

11) In the documentation added, isn't it good if we talk a bit about
in which scenarios users can use this function? For instance,
something like "Use pg_print_backtrace to know exactly where it's
currently waiting when a backend process hangs."?

Modified to include more details.

[1]: https:://www.postgresql.org/message-id/1778088.1606026308%40sss.pgh.pa.us Attached v6 patch with the fixes. Thoughts?
Attached v6 patch with the fixes.
Thoughts?

Regards,
Vignesh

Attachments:

v6-0001-Print-backtrace-of-specified-postgres-process.patchtext/x-patch; charset=US-ASCII; name=v6-0001-Print-backtrace-of-specified-postgres-process.patchDownload
From 9c67eb101bbbdebf28b8bbb8a8a186f0b1c69669 Mon Sep 17 00:00:00 2001
From: Vignesh C <vignesh.c@enterprisedb.com>
Date: Wed, 27 Jan 2021 18:20:13 +0530
Subject: [PATCH v6] Print backtrace of specified postgres process.

The idea here is to implement & expose pg_print_backtrace function, internally
what this function does is, the connected backend will send SIGUSR1 signal by
setting PROCSIG_PRINT_BACKTRACE to postgres backend whose pid matches the
specified process id. Once the backend process receives this signal it will
print the backtrace of the process to the log file based on the logging
configuration, if logging is disabled backtrace will be printed to the
console where postmaster was started.
---
 doc/src/sgml/func.sgml                             |  77 ++++++++++++
 src/backend/postmaster/autovacuum.c                |   7 ++
 src/backend/postmaster/checkpointer.c              |   8 ++
 src/backend/postmaster/interrupt.c                 |   8 ++
 src/backend/storage/ipc/procsignal.c               |  18 +++
 src/backend/storage/ipc/signalfuncs.c              | 134 +++++++++++++++++----
 src/backend/tcop/postgres.c                        |   7 ++
 src/backend/utils/error/elog.c                     |  20 ++-
 src/backend/utils/init/globals.c                   |   1 +
 src/include/catalog/pg_proc.dat                    |   5 +
 src/include/miscadmin.h                            |   2 +
 src/include/storage/procsignal.h                   |   2 +
 src/include/utils/elog.h                           |   2 +
 .../test_misc/t/002_print_backtrace_validation.pl  |  73 +++++++++++
 14 files changed, 334 insertions(+), 30 deletions(-)
 create mode 100644 src/test/modules/test_misc/t/002_print_backtrace_validation.pl

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index f30eaa3..750a0ba 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -24739,6 +24739,33 @@ SELECT collation for ('foo' COLLATE "de_DE");
         however only superusers can terminate superuser backends.
        </para></entry>
       </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_print_backtrace</primary>
+        </indexterm>
+        <function>pg_print_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Prints the backtrace whose backend process has the specified process ID.
+        Backtrace will be printed based on the log configuration set. See
+        <xref linkend="runtime-config-logging"/> for more information. This
+        will help in identifying where exactly the backend process is currently
+        executing. This information will be useful in reporting the hangs and
+        also helps the developers in diagonising the problems. This is
+        allowed if the calling role is a member of the role whose backend
+        backtrace is being printed or the calling role has been granted
+        <literal>pg_signal_backend</literal>, however only superusers can
+        print backtrace of superuser backends. This feature is not supported
+        for postmaster, logging and statistics process. This feature will be
+        available, if the installer was generated on a platform which had
+        backtrace capturing capability. If not available it will return false
+        by throwing the following warning "WARNING:  backtrace generation is
+        not supported by this installation".
+       </para></entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
@@ -24758,6 +24785,56 @@ SELECT collation for ('foo' COLLATE "de_DE");
     <structname>pg_stat_activity</structname> view.
    </para>
 
+   <para>
+    <function>pg_print_backtrace</function> can be used to print backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_print_backtrace(pg_backend_pid());
+ pg_print_backtrace
+--------------------
+ t
+(1 row)
+
+The backtrace will be logged to the log file if logging is enabled, if logging
+is disabled backtrace will be logged to the console where the postmaster was
+started. For example:
+2021-01-27 11:33:50.247 IST [111735] LOG:  current backtrace:
+        postgres: postgresdba postgres [local] SELECT(set_backtrace+0x38) [0xae06c5]
+        postgres: postgresdba postgres [local] SELECT(ProcessInterrupts+0x788) [0x950c34]
+        postgres: postgresdba postgres [local] SELECT() [0x761e89]
+        postgres: postgresdba postgres [local] SELECT() [0x71bbda]
+        postgres: postgresdba postgres [local] SELECT() [0x71e380]
+        postgres: postgresdba postgres [local] SELECT(standard_ExecutorRun+0x1d6) [0x71c1fe]
+        postgres: postgresdba postgres [local] SELECT(ExecutorRun+0x55) [0x71c026]
+        postgres: postgresdba postgres [local] SELECT() [0x953fc5]
+        postgres: postgresdba postgres [local] SELECT(PortalRun+0x262) [0x953c7e]
+        postgres: postgresdba postgres [local] SELECT() [0x94db78]
+        postgres: postgresdba postgres [local] SELECT(PostgresMain+0x7d7) [0x951e72]
+        postgres: postgresdba postgres [local] SELECT() [0x896b2f]
+        postgres: postgresdba postgres [local] SELECT() [0x8964b5]
+        postgres: postgresdba postgres [local] SELECT() [0x892a79]
+        postgres: postgresdba postgres [local] SELECT(PostmasterMain+0x116b) [0x892350]
+        postgres: postgresdba postgres [local] SELECT() [0x795f72]
+        /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2107bbd505]
+        postgres: postgresdba postgres [local] SELECT() [0x4842a9]
+
+</programlisting>
+    You can get the file name and line number by using gdb/addr2line in
+    linux platforms, as a prerequisite users must ensure gdb/addr2line is
+    already installed:
+<programlisting>
+1)  "info line *address" from gdb on postgres executable. For example:
+gdb ./postgres
+(gdb) info line *0x71c25d
+Line 378 of "execMain.c" starts at address 0x71c25d <literal>&lt;</literal>standard_ExecutorRun+470<literal>&gt;</literal> and ends at 0x71c263 <literal>&lt;</literal>standard_ExecutorRun+476<literal>&gt;</literal>.
+OR 
+2) Using "addr2line -e postgres address", For example:
+addr2line -e ./postgres 0x71c25d
+/home/postgresdba/src/backend/executor/execMain.c:378
+</programlisting>
+</para>
+
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 47e60ca..3f72929 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -836,6 +836,13 @@ HandleAutoVacLauncherInterrupts(void)
 	if (ProcSignalBarrierPending)
 		ProcessProcSignalBarrier();
 
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+	{
+		PrintBacktracePending = false;
+		set_backtrace(NULL, 0);
+	}
+
 	/* Process sinval catchup interrupts that happened while sleeping */
 	ProcessCatchupInterrupt();
 }
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 54a818b..5fae328 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -57,6 +57,7 @@
 #include "storage/shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
+#include "tcop/tcopprot.h"
 #include "utils/guc.h"
 #include "utils/memutils.h"
 #include "utils/resowner.h"
@@ -547,6 +548,13 @@ HandleCheckpointerInterrupts(void)
 	if (ProcSignalBarrierPending)
 		ProcessProcSignalBarrier();
 
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+	{
+		PrintBacktracePending = false;
+		set_backtrace(NULL, 0);
+	}
+
 	if (ConfigReloadPending)
 	{
 		ConfigReloadPending = false;
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index dd9136a..59aff6c 100644
--- a/src/backend/postmaster/interrupt.c
+++ b/src/backend/postmaster/interrupt.c
@@ -21,6 +21,7 @@
 #include "storage/ipc.h"
 #include "storage/latch.h"
 #include "storage/procsignal.h"
+#include "tcop/tcopprot.h"
 #include "utils/guc.h"
 
 volatile sig_atomic_t ConfigReloadPending = false;
@@ -41,6 +42,13 @@ HandleMainLoopInterrupts(void)
 		ProcessConfigFile(PGC_SIGHUP);
 	}
 
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+	{
+		PrintBacktracePending = false;
+		set_backtrace(NULL, 0);
+	}
+
 	if (ShutdownRequestPending)
 		proc_exit(0);
 }
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index c43cdd6..f2f0fe7 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -453,6 +453,21 @@ HandleProcSignalBarrierInterrupt(void)
 }
 
 /*
+ * Handle receipt of an interrupt indicating a print backtrace.
+ *
+ * Note: this is called within a signal handler!  All we can do is set
+ * a flag that will cause the next CHECK_FOR_INTERRUPTS to invoke
+ * set_backtrace function which will log the backtrace.
+ */
+static void
+HandlePrintBacktraceInterrupt(void)
+{
+	InterruptPending = true;
+	PrintBacktracePending = true;
+	/* latch will be set by procsignal_sigusr1_handler */
+}
+
+/*
  * Perform global barrier related interrupt checking.
  *
  * Any backend that participates in ProcSignal signaling must arrange to
@@ -686,6 +701,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
 
+	if (CheckProcSignal(PROCSIG_PRINT_BACKTRACE))
+		HandlePrintBacktraceInterrupt();
+
 	SetLatch(MyLatch);
 
 	latch_sigusr1_handler();
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index 69fe23a..c29f765 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -22,6 +22,7 @@
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
+#include "tcop/tcopprot.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 
@@ -40,12 +41,22 @@
  * be emitted. For permission errors, doing that is the responsibility of
  * the caller.
  */
-#define SIGNAL_BACKEND_SUCCESS 0
-#define SIGNAL_BACKEND_ERROR 1
-#define SIGNAL_BACKEND_NOPERMISSION 2
-#define SIGNAL_BACKEND_NOSUPERUSER 3
-static int
-pg_signal_backend(int pid, int sig)
+typedef enum SignalBackendStatus
+{
+	SIGNAL_BACKEND_SUCCESS = 0,
+	SIGNAL_BACKEND_ERROR,
+	SIGNAL_BACKEND_NOPERMISSION,
+	SIGNAL_BACKEND_NOSUPERUSER
+}			SignalBackendStatus;
+
+/*
+ * check_privilege_to_send_singal
+ *
+ * Check if the specified pid is a valid backend process and the user has
+ * permissions to signal to the process.
+ */
+static SignalBackendStatus
+check_privilege_to_send_singal(int pid)
 {
 	PGPROC	   *proc = BackendPidGetProc(pid);
 
@@ -77,6 +88,17 @@ pg_signal_backend(int pid, int sig)
 		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_SIGNAL_BACKENDID))
 		return SIGNAL_BACKEND_NOPERMISSION;
 
+	return SIGNAL_BACKEND_SUCCESS;
+}
+
+static SignalBackendStatus
+pg_signal_backend(int pid, int sig)
+{
+	SignalBackendStatus ret = check_privilege_to_send_singal(pid);
+
+	if (ret != SIGNAL_BACKEND_SUCCESS)
+		return ret;
+
 	/*
 	 * Can the process we just validated above end, followed by the pid being
 	 * recycled for a new process, before reaching here?  Then we'd be trying
@@ -110,19 +132,23 @@ pg_signal_backend(int pid, int sig)
 Datum
 pg_cancel_backend(PG_FUNCTION_ARGS)
 {
-	int			r = pg_signal_backend(PG_GETARG_INT32(0), SIGINT);
+	SignalBackendStatus r = pg_signal_backend(PG_GETARG_INT32(0), SIGINT);
 
-	if (r == SIGNAL_BACKEND_NOSUPERUSER)
-		ereport(ERROR,
-				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 errmsg("must be a superuser to cancel superuser query")));
+	switch (r)
+	{
+		case SIGNAL_BACKEND_NOSUPERUSER:
+			ereport(ERROR,
+					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+					 errmsg("must be a superuser to cancel superuser query")));
 
-	if (r == SIGNAL_BACKEND_NOPERMISSION)
-		ereport(ERROR,
-				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 errmsg("must be a member of the role whose query is being canceled or member of pg_signal_backend")));
+		case SIGNAL_BACKEND_NOPERMISSION:
+			ereport(ERROR,
+					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+					 errmsg("must be a member of the role whose query is being canceled or member of pg_signal_backend")));
 
-	PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
+		default:
+			PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
+	}
 }
 
 /*
@@ -134,19 +160,23 @@ pg_cancel_backend(PG_FUNCTION_ARGS)
 Datum
 pg_terminate_backend(PG_FUNCTION_ARGS)
 {
-	int			r = pg_signal_backend(PG_GETARG_INT32(0), SIGTERM);
+	SignalBackendStatus r = pg_signal_backend(PG_GETARG_INT32(0), SIGTERM);
 
-	if (r == SIGNAL_BACKEND_NOSUPERUSER)
-		ereport(ERROR,
-				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 errmsg("must be a superuser to terminate superuser process")));
+	switch (r)
+	{
+		case SIGNAL_BACKEND_NOSUPERUSER:
+			ereport(ERROR,
+					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+					 errmsg("must be a superuser to terminate superuser process")));
 
-	if (r == SIGNAL_BACKEND_NOPERMISSION)
-		ereport(ERROR,
-				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 errmsg("must be a member of the role whose process is being terminated or member of pg_signal_backend")));
+		case SIGNAL_BACKEND_NOPERMISSION:
+			ereport(ERROR,
+					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+					 errmsg("must be a member of the role whose process is being terminated or member of pg_signal_backend")));
 
-	PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
+		default:
+			PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
+	}
 }
 
 /*
@@ -215,3 +245,55 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_print_backtrace - print backtrace of backend process.
+ *
+ * Permission checking for this function is managed through the normal
+ * GRANT system.
+ */
+Datum
+pg_print_backtrace(PG_FUNCTION_ARGS)
+{
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	int			pid = PG_GETARG_INT32(0);
+	SignalBackendStatus r = check_privilege_to_send_singal(pid);
+
+	switch (r)
+	{
+		case SIGNAL_BACKEND_NOSUPERUSER:
+			ereport(ERROR,
+					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+					 errmsg("must be a superuser to print backtrace of superuser process")));
+
+		case SIGNAL_BACKEND_NOPERMISSION:
+			ereport(ERROR,
+					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+					 errmsg("must be a member of the role whose backtrace is being logged or member of pg_signal_backend")));
+
+		case SIGNAL_BACKEND_SUCCESS:
+
+			/*
+			 * 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, InvalidBackendId))
+				PG_RETURN_BOOL(true);
+			else
+			{
+				ereport(WARNING,
+						(errmsg("failed to send signal to postmaster: %m")));
+				break;			/* Return false */
+			}
+
+		default:
+			break;				/* Not a supported process, return false */
+	}
+#else
+	ereport(WARNING,
+			(errmsg("backtrace generation is not supported by this installation")));
+#endif
+
+	PG_RETURN_BOOL(false);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index cb5a961..8f2c4f7 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3274,6 +3274,13 @@ ProcessInterrupts(void)
 
 	if (ParallelMessagePending)
 		HandleParallelMessages();
+
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+	{
+		PrintBacktracePending = false;
+		set_backtrace(NULL, 0);
+	}
 }
 
 
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 80c2672..a98a048 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -172,7 +172,6 @@ static char formatted_log_time[FORMATTED_TS_LEN];
 
 
 static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
-static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
 static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
 static void write_console(const char *line, int len);
 static void setup_formatted_log_time(void);
@@ -955,9 +954,10 @@ errbacktrace(void)
  * Compute backtrace data and add it to the supplied ErrorData.  num_skip
  * specifies how many inner frames to skip.  Use this to avoid showing the
  * internal backtrace support functions in the backtrace.  This requires that
- * this and related functions are not inlined.
+ * this and related functions are not inlined. If edata pointer is valid
+ * backtrace information will set in edata.
  */
-static void
+void
 set_backtrace(ErrorData *edata, int num_skip)
 {
 	StringInfoData errtrace;
@@ -984,7 +984,19 @@ set_backtrace(ErrorData *edata, int num_skip)
 						   "backtrace generation is not supported by this installation");
 #endif
 
-	edata->backtrace = errtrace.data;
+	if (edata)
+		edata->backtrace = errtrace.data;
+	else
+	{
+		/*
+		 * LOG_SERVER_ONLY is used intentionally to make sure this information
+		 * is not sent to client based on client_min_messages. We don't want
+		 * to mess up a different session as pg_print_backtrace will be
+		 * sending SIGNAL to a different backend.
+		 */
+		elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
+		pfree(errtrace.data);
+	}
 }
 
 /*
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index a5976ad..e2b5ad5 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -37,6 +37,7 @@ volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
+volatile sig_atomic_t PrintBacktracePending = false;
 
 int			MyProcPid;
 pg_time_t	MyStartTime;
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index f817406..9668786 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11379,4 +11379,9 @@
   proname => 'is_normalized', prorettype => 'bool', proargtypes => 'text text',
   prosrc => 'unicode_is_normalized' },
 
+# function to get the backtrace of server process
+{ oid => '6105', descr => 'print backtrace of process',
+  proname => 'pg_print_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_print_backtrace' },
+
 ]
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 1bdc97e..0d00ff9 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -92,6 +92,8 @@ extern PGDLLIMPORT volatile uint32 InterruptHoldoffCount;
 extern PGDLLIMPORT volatile uint32 QueryCancelHoldoffCount;
 extern PGDLLIMPORT volatile uint32 CritSectionCount;
 
+extern PGDLLIMPORT volatile sig_atomic_t PrintBacktracePending;
+
 /* in tcop/postgres.c */
 extern void ProcessInterrupts(void);
 
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index 4ae7dc3..1c7532c 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -43,6 +43,8 @@ typedef enum
 	PROCSIG_RECOVERY_CONFLICT_BUFFERPIN,
 	PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK,
 
+	PROCSIG_PRINT_BACKTRACE,	/* ask backend to print the current backtrace */
+
 	NUM_PROCSIGNALS				/* Must be last! */
 } ProcSignalReason;
 
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 3c0e576..5fa108b 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -450,4 +450,6 @@ extern void set_syslog_parameters(const char *ident, int facility);
  */
 extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
 
+extern pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
+
 #endif							/* ELOG_H */
diff --git a/src/test/modules/test_misc/t/002_print_backtrace_validation.pl b/src/test/modules/test_misc/t/002_print_backtrace_validation.pl
new file mode 100644
index 0000000..00b2cae
--- /dev/null
+++ b/src/test/modules/test_misc/t/002_print_backtrace_validation.pl
@@ -0,0 +1,73 @@
+use strict;
+use warnings;
+
+use PostgresNode;
+use TestLib;
+use Test::More tests => 2;
+use Time::HiRes qw(usleep);
+
+# Set up node with logging collector
+my $node = get_new_node('primary');
+$node->init();
+$node->append_conf(
+	'postgresql.conf', qq(
+logging_collector = on
+lc_messages = 'C'
+));
+
+$node->start();
+
+# Verify that log output gets to the file
+$node->psql('postgres', 'select pg_print_backtrace(pg_backend_pid())');
+
+# might need to retry if logging collector process is slow...
+my $max_attempts = 180 * 10;
+
+my $current_logfiles;
+for (my $attempts = 0; $attempts < $max_attempts; $attempts++)
+{
+	eval {
+		$current_logfiles = slurp_file($node->data_dir . '/current_logfiles');
+	};
+	last unless $@;
+	usleep(100_000);
+}
+die $@ if $@;
+
+note "current_logfiles = $current_logfiles";
+
+like(
+	$current_logfiles,
+	qr|^stderr log/postgresql-.*log$|,
+	'current_logfiles is sane');
+
+my $lfname = $current_logfiles;
+$lfname =~ s/^stderr //;
+chomp $lfname;
+
+my $first_logfile;
+my $bt_occurence_count;
+
+# Verify that the backtraces of the processes are logged into logfile.
+for (my $attempts = 0; $attempts < $max_attempts; $attempts++)
+{
+	$first_logfile = $node->data_dir . '/' . $lfname;
+	chomp $first_logfile;
+	print "file is $first_logfile";
+	open my $fh, '<', $first_logfile
+	  or die "Could not open '$first_logfile' $!";
+	while (my $line = <$fh>)
+	{
+		chomp $line;
+		if ($line =~ m/current backtrace/)
+		{
+			$bt_occurence_count++;
+		}
+	}
+	last if $bt_occurence_count == 1;
+	usleep(100_000);
+}
+
+is($bt_occurence_count, 1, 'found expected backtrace in the log file');
+
+$node->stop();
-- 
1.8.3.1

#41vignesh C
vignesh21@gmail.com
In reply to: Bharath Rupireddy (#37)
Re: Printing backtrace of postgres processes

On Mon, Feb 1, 2021 at 11:04 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Mon, Feb 1, 2021 at 6:14 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Fri, Jan 29, 2021 at 7:10 PM vignesh C <vignesh21@gmail.com> wrote:

4) How about following
+                     errmsg("must be a superuser to print backtrace
of backend process")));
instead of
+                     errmsg("must be a superuser to print backtrace
of superuser query process")));

Here the message should include superuser, we cannot remove it. Non
super user can log non super user provided if user has permissions for
it.

5) How about following
errmsg("must be a member of the role whose backed
process's backtrace is being printed or member of
pg_signal_backend")));
instead of
+ errmsg("must be a member of the role whose
backtrace is being logged or member of pg_signal_backend")));

Modified it.

Maybe I'm confused here to understand the difference between
SIGNAL_BACKEND_NOSUPERUSER and SIGNAL_BACKEND_NOPERMISSION macros and
corresponding error messages. Some clarification/use case to know in
which scenarios we hit those error messages might help me. Did we try
to add test cases that show up these error messages for
pg_print_backtrace? If not, can we add?

Are these superuser and permission checks enough from a security
standpoint that we don't expose some sensitive information to the
user? Although I'm not sure, say from the backtrace printed and
attached to GDB, can users see the passwords or other sensitive
information from the system that they aren't supposed to see?

I'm sure this point would have been discussed upthread.

This will just print the backtrace of the current backend. Users
cannot get password information from this. This backtrace will be sent
from customer side to the customer support. Developers will use gdb to
check the file and line number using the addresses. We are suggesting
to use gdb to get the file and line number from the address. Users
can attach gdb to the process even now without this feature, I think
that behavior will be the same as is. That will not be impacted
because of this feature. It was discussed to keep the checks similar
to pg_terminate_backend as discussed in [1]/messages/by-id/CA+TgmoZ8XeQVCCqfq826kAr804a1ZnYy46FnQB9r2n_iOofh8Q@mail.gmail.com.
[1]: /messages/by-id/CA+TgmoZ8XeQVCCqfq826kAr804a1ZnYy46FnQB9r2n_iOofh8Q@mail.gmail.com

Regards,
Vignesh

#42Tom Lane
tgl@sss.pgh.pa.us
In reply to: vignesh C (#41)
Re: Printing backtrace of postgres processes

vignesh C <vignesh21@gmail.com> writes:

On Mon, Feb 1, 2021 at 11:04 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

Are these superuser and permission checks enough from a security
standpoint that we don't expose some sensitive information to the
user?

This will just print the backtrace of the current backend. Users
cannot get password information from this.

Really?

A backtrace normally exposes the text of the current query, for
instance, which could contain very sensitive data (passwords in ALTER
USER, customer credit card numbers in ordinary data, etc etc). We
don't allow the postmaster log to be seen by any but very privileged
users; it's not sane to think that this data is any less
security-critical than the postmaster log.

This point is entirely separate from the question of whether
triggering stack traces at inopportune moments could cause system
malfunctions, but that question is also not to be ignored.

TBH, I'm leaning to the position that this should be superuser
only. I do NOT agree with the idea that ordinary users should
be able to trigger it, even against backends theoretically
belonging to their own userid. (Do I need to point out that
some levels of the call stack might be from security-definer
functions with more privilege than the session's nominal user?)

regards, tom lane

#43vignesh C
vignesh21@gmail.com
In reply to: Tom Lane (#42)
Re: Printing backtrace of postgres processes

On Wed, Feb 3, 2021 at 1:00 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

vignesh C <vignesh21@gmail.com> writes:

On Mon, Feb 1, 2021 at 11:04 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

Are these superuser and permission checks enough from a security
standpoint that we don't expose some sensitive information to the
user?

This will just print the backtrace of the current backend. Users
cannot get password information from this.

Really?

A backtrace normally exposes the text of the current query, for
instance, which could contain very sensitive data (passwords in ALTER
USER, customer credit card numbers in ordinary data, etc etc). We
don't allow the postmaster log to be seen by any but very privileged
users; it's not sane to think that this data is any less
security-critical than the postmaster log.

This point is entirely separate from the question of whether
triggering stack traces at inopportune moments could cause system
malfunctions, but that question is also not to be ignored.

TBH, I'm leaning to the position that this should be superuser
only. I do NOT agree with the idea that ordinary users should
be able to trigger it, even against backends theoretically
belonging to their own userid. (Do I need to point out that
some levels of the call stack might be from security-definer
functions with more privilege than the session's nominal user?)

I had seen that the log that will be logged will be something like:
postgres: test postgres [local]
idle(ProcessClientReadInterrupt+0x3a) [0x9500ec]
postgres: test postgres [local] idle(secure_read+0x183) [0x787f43]
postgres: test postgres [local] idle() [0x7919de]
postgres: test postgres [local] idle(pq_getbyte+0x32) [0x791a8e]
postgres: test postgres [local] idle() [0x94fc16]
postgres: test postgres [local] idle() [0x950099]
postgres: test postgres [local] idle(PostgresMain+0x6d5) [0x954bd5]
postgres: test postgres [local] idle() [0x898a09]
postgres: test postgres [local] idle() [0x89838f]
postgres: test postgres [local] idle() [0x894953]
postgres: test postgres [local] idle(PostmasterMain+0x116b) [0x89422a]
postgres: test postgres [local] idle() [0x79725b]
/lib64/libc.so.6(__libc_start_main+0xf5) [0x7f6e68d75555]
postgres: test postgres [local] idle() [0x484249]
I was not sure if we would be able to get any secure information from
this. I did not notice the function arguments being printed. I felt
the function name, offset and the return address will be logged. I
might be missing something here.
Thoughts?

Regards,
Vignesh

#44Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: vignesh C (#43)
Re: Printing backtrace of postgres processes

On Wed, Feb 3, 2021 at 1:49 PM vignesh C <vignesh21@gmail.com> wrote:

On Wed, Feb 3, 2021 at 1:00 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

vignesh C <vignesh21@gmail.com> writes:

On Mon, Feb 1, 2021 at 11:04 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

Are these superuser and permission checks enough from a security
standpoint that we don't expose some sensitive information to the
user?

This will just print the backtrace of the current backend. Users
cannot get password information from this.

Really?

A backtrace normally exposes the text of the current query, for
instance, which could contain very sensitive data (passwords in ALTER
USER, customer credit card numbers in ordinary data, etc etc). We
don't allow the postmaster log to be seen by any but very privileged
users; it's not sane to think that this data is any less
security-critical than the postmaster log.

This point is entirely separate from the question of whether
triggering stack traces at inopportune moments could cause system
malfunctions, but that question is also not to be ignored.

TBH, I'm leaning to the position that this should be superuser
only. I do NOT agree with the idea that ordinary users should
be able to trigger it, even against backends theoretically
belonging to their own userid. (Do I need to point out that
some levels of the call stack might be from security-definer
functions with more privilege than the session's nominal user?)

I had seen that the log that will be logged will be something like:
postgres: test postgres [local]
idle(ProcessClientReadInterrupt+0x3a) [0x9500ec]
postgres: test postgres [local] idle(secure_read+0x183) [0x787f43]
postgres: test postgres [local] idle() [0x7919de]
postgres: test postgres [local] idle(pq_getbyte+0x32) [0x791a8e]
postgres: test postgres [local] idle() [0x94fc16]
postgres: test postgres [local] idle() [0x950099]
postgres: test postgres [local] idle(PostgresMain+0x6d5) [0x954bd5]
postgres: test postgres [local] idle() [0x898a09]
postgres: test postgres [local] idle() [0x89838f]
postgres: test postgres [local] idle() [0x894953]
postgres: test postgres [local] idle(PostmasterMain+0x116b) [0x89422a]
postgres: test postgres [local] idle() [0x79725b]
/lib64/libc.so.6(__libc_start_main+0xf5) [0x7f6e68d75555]
postgres: test postgres [local] idle() [0x484249]
I was not sure if we would be able to get any secure information from
this. I did not notice the function arguments being printed. I felt
the function name, offset and the return address will be logged. I
might be missing something here.
Thoughts?

First of all, we need to see if the output of pg_print_backtrace shows
up function parameter addresses or only function start addresses along
with line and file information when attached to gdb. In either case,
IMO, it will be easy for experienced hackers(I'm not one though) to
calculate and fetch the query string or other function parameters or
the variables inside the functions from the stack by looking at the
code (which is available openly, of course).

Say, if a backend is in a long running scan or insert operation, then
pg_print_backtrace is issued from another session, the
exec_simple_query function shows up query_string. Below is captured
from attached gdb though, I'm not sure whether the logged backtrace
will have function address or the function parameters addresses, I
think we can check that by having a long running query which
frequently checks interrupts and issue pg_print_backtrace from another
session to that backend. Now, attach gdb to the backend in which the
query is running, then take bt, see if the logged backtrace and the
gdb bt have the same or closer addresses.

#13 0x00005644f4320729 in exec_simple_query (
query_string=0x5644f6771bf0 "select pg_backend_pid();") at postgres.c:1240
#14 0x00005644f4324ff4 in PostgresMain (argc=1, argv=0x7ffd819bd5e0,
dbname=0x5644f679d2b8 "postgres", username=0x5644f679d298 "bharath")
at postgres.c:4394
#15 0x00005644f4256f9d in BackendRun (port=0x5644f67935c0) at postmaster.c:4484
#16 0x00005644f4256856 in BackendStartup (port=0x5644f67935c0) at
postmaster.c:4206
#17 0x00005644f4252a11 in ServerLoop () at postmaster.c:1730
#18 0x00005644f42521aa in PostmasterMain (argc=3, argv=0x5644f676b1f0)
at postmaster.c:1402
#19 0x00005644f4148789 in main (argc=3, argv=0x5644f676b1f0) at main.c:209

As suggested by Tom, I'm okay if this function is callable only by the
superusers. In that case, the superusers can fetch the backtrace and
send it for further analysis in case of any hangs or issues.

Others may have better thoughts.

With Regards,
Bharath Rupireddy.
EnterpriseDB: http://www.enterprisedb.com

#45torikoshia
torikoshia@oss.nttdata.com
In reply to: vignesh C (#40)
Re: Printing backtrace of postgres processes

Hi,

I also think this feature would be useful when supporting
environments that lack debugger or debug symbols.
I think such environments are not rare.

+        <xref linkend="runtime-config-logging"/> for more information. 
This
+        will help in identifying where exactly the backend process is 
currently
+        executing.

When I read this, I expected a backtrace would be generated at
the moment when it receives the signal, but actually it just
sets a flag that causes the next CHECK_FOR_INTERRUPTS to print
a backtrace.

How about explaining the timing of the backtrace generation?

+        print backtrace of superuser backends. This feature is not 
supported
+        for postmaster, logging and statistics process.

Since the current patch use BackendPidGetProc(), it does not
support this feature not only postmaster, logging, and
statistics but also checkpointer, background writer, and
walwriter.

And when I specify pid of these PostgreSQL processes, it
says "PID xxxx is not a PostgreSQL server process".

I think it may confuse users, so it might be worth
changing messages for those PostgreSQL processes.
AuxiliaryPidGetProc() may help to do it.

diff --git a/src/backend/postmaster/checkpointer.c 
b/src/backend/postmaster/checkpointer.c
index 54a818b..5fae328 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -57,6 +57,7 @@
  #include "storage/shmem.h"
  #include "storage/smgr.h"
  #include "storage/spin.h"
+#include "tcop/tcopprot.h"
  #include "utils/guc.h"
  #include "utils/memutils.h"
  #include "utils/resowner.h"
@@ -547,6 +548,13 @@ HandleCheckpointerInterrupts(void)
         if (ProcSignalBarrierPending)
                 ProcessProcSignalBarrier();
+       /* Process printing backtrace */
+       if (PrintBacktracePending)
+       {
+               PrintBacktracePending = false;
+               set_backtrace(NULL, 0);
+       }
+

Although it implements backtrace for checkpointer, when
I specified pid of checkpointer it was refused from
BackendPidGetProc().

Regards,

--
Atsushi Torikoshi
NTT DATA CORPORATION

#46Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: torikoshia (#45)
Re: Printing backtrace of postgres processes

On Mon, Mar 1, 2021 at 10:43 AM torikoshia <torikoshia@oss.nttdata.com> wrote:

Since the current patch use BackendPidGetProc(), it does not
support this feature not only postmaster, logging, and
statistics but also checkpointer, background writer, and
walwriter.

And when I specify pid of these PostgreSQL processes, it
says "PID xxxx is not a PostgreSQL server process".

I think it may confuse users, so it might be worth
changing messages for those PostgreSQL processes.
AuxiliaryPidGetProc() may help to do it.

Exactly this was the doubt I got when I initially reviewed this patch.
And I felt it should be discussed in a separate thread, you may want
to update your thoughts there [1]/messages/by-id/CALj2ACW7Rr-R7mBcBQiXWPp=JV5chajjTdudLiF5YcpW-BmHhg@mail.gmail.com.

[1]: /messages/by-id/CALj2ACW7Rr-R7mBcBQiXWPp=JV5chajjTdudLiF5YcpW-BmHhg@mail.gmail.com

With Regards,
Bharath Rupireddy.
EnterpriseDB: http://www.enterprisedb.com

#47torikoshia
torikoshia@oss.nttdata.com
In reply to: Bharath Rupireddy (#46)
Re: Printing backtrace of postgres processes

On 2021-03-04 21:55, Bharath Rupireddy wrote:

On Mon, Mar 1, 2021 at 10:43 AM torikoshia <torikoshia@oss.nttdata.com>
wrote:

Since the current patch use BackendPidGetProc(), it does not
support this feature not only postmaster, logging, and
statistics but also checkpointer, background writer, and
walwriter.

And when I specify pid of these PostgreSQL processes, it
says "PID xxxx is not a PostgreSQL server process".

I think it may confuse users, so it might be worth
changing messages for those PostgreSQL processes.
AuxiliaryPidGetProc() may help to do it.

Exactly this was the doubt I got when I initially reviewed this patch.
And I felt it should be discussed in a separate thread, you may want
to update your thoughts there [1].

[1] -
/messages/by-id/CALj2ACW7Rr-R7mBcBQiXWPp=JV5chajjTdudLiF5YcpW-BmHhg@mail.gmail.com

Thanks!
I'm going to join the discussion there.

Regards,

--
Atsushi Torikoshi
NTT DATA CORPORATION

#48vignesh C
vignesh21@gmail.com
In reply to: Bharath Rupireddy (#44)
1 attachment(s)
Re: Printing backtrace of postgres processes

On Wed, Feb 3, 2021 at 3:24 PM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Wed, Feb 3, 2021 at 1:49 PM vignesh C <vignesh21@gmail.com> wrote:

On Wed, Feb 3, 2021 at 1:00 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

vignesh C <vignesh21@gmail.com> writes:

On Mon, Feb 1, 2021 at 11:04 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

Are these superuser and permission checks enough from a security
standpoint that we don't expose some sensitive information to the
user?

This will just print the backtrace of the current backend. Users
cannot get password information from this.

Really?

A backtrace normally exposes the text of the current query, for
instance, which could contain very sensitive data (passwords in ALTER
USER, customer credit card numbers in ordinary data, etc etc). We
don't allow the postmaster log to be seen by any but very privileged
users; it's not sane to think that this data is any less
security-critical than the postmaster log.

This point is entirely separate from the question of whether
triggering stack traces at inopportune moments could cause system
malfunctions, but that question is also not to be ignored.

TBH, I'm leaning to the position that this should be superuser
only. I do NOT agree with the idea that ordinary users should
be able to trigger it, even against backends theoretically
belonging to their own userid. (Do I need to point out that
some levels of the call stack might be from security-definer
functions with more privilege than the session's nominal user?)

I had seen that the log that will be logged will be something like:
postgres: test postgres [local]
idle(ProcessClientReadInterrupt+0x3a) [0x9500ec]
postgres: test postgres [local] idle(secure_read+0x183) [0x787f43]
postgres: test postgres [local] idle() [0x7919de]
postgres: test postgres [local] idle(pq_getbyte+0x32) [0x791a8e]
postgres: test postgres [local] idle() [0x94fc16]
postgres: test postgres [local] idle() [0x950099]
postgres: test postgres [local] idle(PostgresMain+0x6d5) [0x954bd5]
postgres: test postgres [local] idle() [0x898a09]
postgres: test postgres [local] idle() [0x89838f]
postgres: test postgres [local] idle() [0x894953]
postgres: test postgres [local] idle(PostmasterMain+0x116b) [0x89422a]
postgres: test postgres [local] idle() [0x79725b]
/lib64/libc.so.6(__libc_start_main+0xf5) [0x7f6e68d75555]
postgres: test postgres [local] idle() [0x484249]
I was not sure if we would be able to get any secure information from
this. I did not notice the function arguments being printed. I felt
the function name, offset and the return address will be logged. I
might be missing something here.
Thoughts?

First of all, we need to see if the output of pg_print_backtrace shows
up function parameter addresses or only function start addresses along
with line and file information when attached to gdb. In either case,
IMO, it will be easy for experienced hackers(I'm not one though) to
calculate and fetch the query string or other function parameters or
the variables inside the functions from the stack by looking at the
code (which is available openly, of course).

Say, if a backend is in a long running scan or insert operation, then
pg_print_backtrace is issued from another session, the
exec_simple_query function shows up query_string. Below is captured
from attached gdb though, I'm not sure whether the logged backtrace
will have function address or the function parameters addresses, I
think we can check that by having a long running query which
frequently checks interrupts and issue pg_print_backtrace from another
session to that backend. Now, attach gdb to the backend in which the
query is running, then take bt, see if the logged backtrace and the
gdb bt have the same or closer addresses.

#13 0x00005644f4320729 in exec_simple_query (
query_string=0x5644f6771bf0 "select pg_backend_pid();") at postgres.c:1240
#14 0x00005644f4324ff4 in PostgresMain (argc=1, argv=0x7ffd819bd5e0,
dbname=0x5644f679d2b8 "postgres", username=0x5644f679d298 "bharath")
at postgres.c:4394
#15 0x00005644f4256f9d in BackendRun (port=0x5644f67935c0) at postmaster.c:4484
#16 0x00005644f4256856 in BackendStartup (port=0x5644f67935c0) at
postmaster.c:4206
#17 0x00005644f4252a11 in ServerLoop () at postmaster.c:1730
#18 0x00005644f42521aa in PostmasterMain (argc=3, argv=0x5644f676b1f0)
at postmaster.c:1402
#19 0x00005644f4148789 in main (argc=3, argv=0x5644f676b1f0) at main.c:209

As suggested by Tom, I'm okay if this function is callable only by the
superusers. In that case, the superusers can fetch the backtrace and
send it for further analysis in case of any hangs or issues.

Others may have better thoughts.

I would like to clarify a bit to avoid confusion here: Currently when
there is a long running query or hang in the server, one of our
customer support members will go for a screen share with the customer.
If gdb is not installed we tell the customer to install gdb. Then we
tell the customer to attach the backend process and execute the
command. We tell the customer to share this to the customer support
team and later the development team checks if this is an issue or a
long running query and provides a workaround or explains what needs to
be done next.
This feature reduces a lot of these processes. Whenever there is an
issue and if the user/customer is not sure if it is a hang or long
running query. User can execute pg_print_backtrace, after this is
executed, the backtrace will be logged to the log file something like
below:
postgres: test postgres [local]
idle(ProcessClientReadInterrupt+0x3a) [0x9500ec]
postgres: test postgres [local] idle(secure_read+0x183) [0x787f43]
postgres: test postgres [local] idle() [0x7919de]
postgres: test postgres [local] idle(pq_getbyte+0x32) [0x791a8e]
postgres: test postgres [local] idle() [0x94fc16]
postgres: test postgres [local] idle() [0x950099]
postgres: test postgres [local] idle(PostgresMain+0x6d5) [0x954bd5]
postgres: test postgres [local] idle() [0x898a09]
postgres: test postgres [local] idle() [0x89838f]
postgres: test postgres [local] idle() [0x894953]
postgres: test postgres [local] idle(PostmasterMain+0x116b) [0x89422a]
postgres: test postgres [local] idle() [0x79725b]
/lib64/libc.so.6(__libc_start_main+0xf5) [0x7f6e68d75555]
postgres: test postgres [local] idle() [0x484249]

The above log contents (not the complete log file) will be shared by
the customer/user along with the query/configuration/statistics, etc
to the support team. I have mentioned a few steps in the documentation
how to get the file/line from the backtrace logged using
gdb/addr2line. Here this will be done in the developer environment,
not in the actual customer environment which has the sensitive data.
We don't attach to the running process in gdb. Developers will use the
same binary that was released to the customer(not in the customer
environment) to get the file/line number. Users will be able to
simulate a backtrace which includes file and line number. I felt users
cannot get the sensitive information from here. This information will
help the developer to analyze and suggest what is the next action that
customers need to take.
I felt that there was a slight misunderstanding in where gdb is
executed, it is not in the customer environment but in the developer
environment. From the customer environment we will only get the logs
of stack trace as mentioned above.
I have changed it so that this feature is supported only for superuser users.
Thoughts?

Regards,
Vignesh

Attachments:

v7-0001-Print-backtrace-of-specified-postgres-process.patchtext/x-patch; charset=US-ASCII; name=v7-0001-Print-backtrace-of-specified-postgres-process.patchDownload
From 65b6105e17ac64f2d1773b3d8680b88531e79717 Mon Sep 17 00:00:00 2001
From: vignesh <vignesh21@gmail.com>
Date: Wed, 5 May 2021 20:31:38 +0530
Subject: [PATCH v7] Print backtrace of specified postgres process.

The idea here is to implement & expose pg_print_backtrace function, internally
what this function does is, the connected backend will send SIGUSR1 signal by
setting PROCSIG_PRINT_BACKTRACE to postgres backend whose pid matches the
specified process id. Once the backend process receives this signal it will
print the backtrace of the process to the log file based on the logging
configuration, if logging is disabled backtrace will be printed to the
console where postmaster was started.
---
 doc/src/sgml/func.sgml                        | 77 +++++++++++++++++++
 src/backend/postmaster/autovacuum.c           |  7 ++
 src/backend/postmaster/interrupt.c            |  8 ++
 src/backend/storage/ipc/procsignal.c          | 18 +++++
 src/backend/storage/ipc/signalfuncs.c         | 46 +++++++++++
 src/backend/tcop/postgres.c                   |  9 +++
 src/backend/utils/error/elog.c                | 20 ++++-
 src/backend/utils/init/globals.c              |  1 +
 src/include/catalog/pg_proc.dat               |  5 ++
 src/include/miscadmin.h                       |  2 +
 src/include/storage/procsignal.h              |  2 +
 src/include/utils/elog.h                      |  2 +
 .../t/002_print_backtrace_validation.pl       | 73 ++++++++++++++++++
 13 files changed, 266 insertions(+), 4 deletions(-)
 create mode 100644 src/test/modules/test_misc/t/002_print_backtrace_validation.pl

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 5ae8abff0c..c196e19335 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -24978,6 +24978,33 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_print_backtrace</primary>
+        </indexterm>
+        <function>pg_print_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Prints the backtrace whose backend process has the specified process ID.
+        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"/>.  This
+        will help in identifying where exactly the backend process is currently
+        executing. This information will be useful in reporting the hangs and
+        also helps the developers in diagonising the problems. This feature is
+        not supported for postmaster, logging and statistics process. This
+        feature will be available, if the installer was generated on a platform
+        which had backtrace capturing capability. If not available it will
+        return false by throwing the following warning "WARNING:  backtrace
+        generation is not supported by this installation". Only superusers can
+        request to log backtrace of a backend process.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -25108,6 +25135,56 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_print_backtrace</function> can be used to print backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_print_backtrace(pg_backend_pid());
+ pg_print_backtrace
+--------------------
+ t
+(1 row)
+
+The backtrace will be logged to the log file if logging is enabled, if logging
+is disabled backtrace will be logged to the console where the postmaster was
+started. For example:
+2021-01-27 11:33:50.247 IST [111735] LOG:  current backtrace:
+        postgres: postgresdba postgres [local] SELECT(set_backtrace+0x38) [0xae06c5]
+        postgres: postgresdba postgres [local] SELECT(ProcessInterrupts+0x788) [0x950c34]
+        postgres: postgresdba postgres [local] SELECT() [0x761e89]
+        postgres: postgresdba postgres [local] SELECT() [0x71bbda]
+        postgres: postgresdba postgres [local] SELECT() [0x71e380]
+        postgres: postgresdba postgres [local] SELECT(standard_ExecutorRun+0x1d6) [0x71c1fe]
+        postgres: postgresdba postgres [local] SELECT(ExecutorRun+0x55) [0x71c026]
+        postgres: postgresdba postgres [local] SELECT() [0x953fc5]
+        postgres: postgresdba postgres [local] SELECT(PortalRun+0x262) [0x953c7e]
+        postgres: postgresdba postgres [local] SELECT() [0x94db78]
+        postgres: postgresdba postgres [local] SELECT(PostgresMain+0x7d7) [0x951e72]
+        postgres: postgresdba postgres [local] SELECT() [0x896b2f]
+        postgres: postgresdba postgres [local] SELECT() [0x8964b5]
+        postgres: postgresdba postgres [local] SELECT() [0x892a79]
+        postgres: postgresdba postgres [local] SELECT(PostmasterMain+0x116b) [0x892350]
+        postgres: postgresdba postgres [local] SELECT() [0x795f72]
+        /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2107bbd505]
+        postgres: postgresdba postgres [local] SELECT() [0x4842a9]
+
+</programlisting>
+    You can get the file name and line number by using gdb/addr2line in
+    linux platforms, as a prerequisite users must ensure gdb/addr2line is
+    already installed:
+<programlisting>
+1)  "info line *address" from gdb on postgres executable. For example:
+gdb ./postgres
+(gdb) info line *0x71c25d
+Line 378 of "execMain.c" starts at address 0x71c25d <literal>&lt;</literal>standard_ExecutorRun+470<literal>&gt;</literal> and ends at 0x71c263 <literal>&lt;</literal>standard_ExecutorRun+476<literal>&gt;</literal>.
+OR 
+2) Using "addr2line -e postgres address", For example:
+addr2line -e ./postgres 0x71c25d
+/home/postgresdba/src/backend/executor/execMain.c:378
+</programlisting>
+</para>
+
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index d516df0ac5..739180b8d9 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -837,6 +837,13 @@ HandleAutoVacLauncherInterrupts(void)
 	if (ProcSignalBarrierPending)
 		ProcessProcSignalBarrier();
 
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+	{
+		PrintBacktracePending = false;
+		set_backtrace(NULL, 0);
+	}
+
 	/* Process sinval catchup interrupts that happened while sleeping */
 	ProcessCatchupInterrupt();
 }
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index dd9136a942..59aff6ca02 100644
--- a/src/backend/postmaster/interrupt.c
+++ b/src/backend/postmaster/interrupt.c
@@ -21,6 +21,7 @@
 #include "storage/ipc.h"
 #include "storage/latch.h"
 #include "storage/procsignal.h"
+#include "tcop/tcopprot.h"
 #include "utils/guc.h"
 
 volatile sig_atomic_t ConfigReloadPending = false;
@@ -41,6 +42,13 @@ HandleMainLoopInterrupts(void)
 		ProcessConfigFile(PGC_SIGHUP);
 	}
 
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+	{
+		PrintBacktracePending = false;
+		set_backtrace(NULL, 0);
+	}
+
 	if (ShutdownRequestPending)
 		proc_exit(0);
 }
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index eac6895141..7cea2a42c5 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -441,6 +441,21 @@ HandleProcSignalBarrierInterrupt(void)
 	/* latch will be set by procsignal_sigusr1_handler */
 }
 
+/*
+ * Handle receipt of an interrupt indicating a print backtrace.
+ *
+ * Note: this is called within a signal handler!  All we can do is set
+ * a flag that will cause the next CHECK_FOR_INTERRUPTS to invoke
+ * set_backtrace function which will log the backtrace.
+ */
+static void
+HandlePrintBacktraceInterrupt(void)
+{
+	InterruptPending = true;
+	PrintBacktracePending = true;
+	/* latch will be set by procsignal_sigusr1_handler */
+}
+
 /*
  * Perform global barrier related interrupt checking.
  *
@@ -679,6 +694,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
 
+	if (CheckProcSignal(PROCSIG_PRINT_BACKTRACE))
+		HandlePrintBacktraceInterrupt();
+
 	SetLatch(MyLatch);
 
 	errno = save_errno;
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index 0337b00226..9af41731e3 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -23,6 +23,7 @@
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
+#include "tcop/tcopprot.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 
@@ -331,3 +332,48 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_print_backtrace - print backtrace of backend process.
+ *
+ * Only superusers can print backtrace.
+ */
+Datum
+pg_print_backtrace(PG_FUNCTION_ARGS)
+{
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	int			pid = PG_GETARG_INT32(0);
+	PGPROC	   *proc;
+
+	/* Only superusers can print back trace. */
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be a superuser to print backtrace")));
+
+	/* BackendPidGetProc returns NULL if the pid isn't valid. */
+	proc = BackendPidGetProc(pid);
+	if (proc == NULL)
+	{
+		ereport(WARNING,
+				(errmsg("PID %d is not a PostgreSQL server process", pid)));
+		PG_RETURN_BOOL(false);
+	}
+
+	/*
+	 * 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, InvalidBackendId))
+		PG_RETURN_BOOL(true);
+	else
+		ereport(WARNING,
+				(errmsg("could not send signal to process %d: %m", pid))); /* return false below */
+#else
+	ereport(WARNING,
+			(errmsg("backtrace generation is not supported by this installation")));
+#endif
+
+	PG_RETURN_BOOL(false);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 2d6d145ecc..d3d0de28ab 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3356,6 +3356,15 @@ ProcessInterrupts(void)
 
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+	{
+		PrintBacktracePending = false;
+		ereport(LOG,
+			(errmsg("logging backtrace of PID %d", MyProcPid)));
+		set_backtrace(NULL, 0);
+	}
 }
 
 
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 65019989cf..45687eb16b 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -172,7 +172,6 @@ static char formatted_log_time[FORMATTED_TS_LEN];
 
 
 static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
-static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
 static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
 static void write_console(const char *line, int len);
 static void setup_formatted_log_time(void);
@@ -949,9 +948,10 @@ errbacktrace(void)
  * Compute backtrace data and add it to the supplied ErrorData.  num_skip
  * specifies how many inner frames to skip.  Use this to avoid showing the
  * internal backtrace support functions in the backtrace.  This requires that
- * this and related functions are not inlined.
+ * this and related functions are not inlined. If edata pointer is valid
+ * backtrace information will set in edata.
  */
-static void
+void
 set_backtrace(ErrorData *edata, int num_skip)
 {
 	StringInfoData errtrace;
@@ -978,7 +978,19 @@ set_backtrace(ErrorData *edata, int num_skip)
 						   "backtrace generation is not supported by this installation");
 #endif
 
-	edata->backtrace = errtrace.data;
+	if (edata)
+		edata->backtrace = errtrace.data;
+	else
+	{
+		/*
+		 * LOG_SERVER_ONLY is used intentionally to make sure this information
+		 * is not sent to client based on client_min_messages. We don't want
+		 * to mess up a different session as pg_print_backtrace will be
+		 * sending SIGNAL to a different backend.
+		 */
+		elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
+		pfree(errtrace.data);
+	}
 }
 
 /*
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 381d9e548d..bd869ebf8c 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -39,6 +39,7 @@ volatile sig_atomic_t LogMemoryContextPending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
+volatile sig_atomic_t PrintBacktracePending = false;
 
 int			MyProcPid;
 pg_time_t	MyStartTime;
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 91f0ea2212..251a7f35e1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11611,4 +11611,9 @@
   proname => 'brin_minmax_multi_summary_send', provolatile => 's', prorettype => 'bytea',
   proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_send' },
 
+# function to get the backtrace of server process
+{ oid => '6105', descr => 'print backtrace of process',
+  proname => 'pg_print_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_print_backtrace' },
+
 ]
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 95202d37af..34b0909848 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -94,6 +94,8 @@ extern PGDLLIMPORT volatile uint32 InterruptHoldoffCount;
 extern PGDLLIMPORT volatile uint32 QueryCancelHoldoffCount;
 extern PGDLLIMPORT volatile uint32 CritSectionCount;
 
+extern PGDLLIMPORT volatile sig_atomic_t PrintBacktracePending;
+
 /* in tcop/postgres.c */
 extern void ProcessInterrupts(void);
 
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index eec186be2e..089b15993b 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -44,6 +44,8 @@ typedef enum
 	PROCSIG_RECOVERY_CONFLICT_BUFFERPIN,
 	PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK,
 
+	PROCSIG_PRINT_BACKTRACE,	/* ask backend to print the current backtrace */
+
 	NUM_PROCSIGNALS				/* Must be last! */
 } ProcSignalReason;
 
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index f53607e12e..c63d25716a 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -453,4 +453,6 @@ extern void set_syslog_parameters(const char *ident, int facility);
  */
 extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
 
+extern pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
+
 #endif							/* ELOG_H */
diff --git a/src/test/modules/test_misc/t/002_print_backtrace_validation.pl b/src/test/modules/test_misc/t/002_print_backtrace_validation.pl
new file mode 100644
index 0000000000..00b2cae14e
--- /dev/null
+++ b/src/test/modules/test_misc/t/002_print_backtrace_validation.pl
@@ -0,0 +1,73 @@
+use strict;
+use warnings;
+
+use PostgresNode;
+use TestLib;
+use Test::More tests => 2;
+use Time::HiRes qw(usleep);
+
+# Set up node with logging collector
+my $node = get_new_node('primary');
+$node->init();
+$node->append_conf(
+	'postgresql.conf', qq(
+logging_collector = on
+lc_messages = 'C'
+));
+
+$node->start();
+
+# Verify that log output gets to the file
+$node->psql('postgres', 'select pg_print_backtrace(pg_backend_pid())');
+
+# might need to retry if logging collector process is slow...
+my $max_attempts = 180 * 10;
+
+my $current_logfiles;
+for (my $attempts = 0; $attempts < $max_attempts; $attempts++)
+{
+	eval {
+		$current_logfiles = slurp_file($node->data_dir . '/current_logfiles');
+	};
+	last unless $@;
+	usleep(100_000);
+}
+die $@ if $@;
+
+note "current_logfiles = $current_logfiles";
+
+like(
+	$current_logfiles,
+	qr|^stderr log/postgresql-.*log$|,
+	'current_logfiles is sane');
+
+my $lfname = $current_logfiles;
+$lfname =~ s/^stderr //;
+chomp $lfname;
+
+my $first_logfile;
+my $bt_occurence_count;
+
+# Verify that the backtraces of the processes are logged into logfile.
+for (my $attempts = 0; $attempts < $max_attempts; $attempts++)
+{
+	$first_logfile = $node->data_dir . '/' . $lfname;
+	chomp $first_logfile;
+	print "file is $first_logfile";
+	open my $fh, '<', $first_logfile
+	  or die "Could not open '$first_logfile' $!";
+	while (my $line = <$fh>)
+	{
+		chomp $line;
+		if ($line =~ m/current backtrace/)
+		{
+			$bt_occurence_count++;
+		}
+	}
+	last if $bt_occurence_count == 1;
+	usleep(100_000);
+}
+
+is($bt_occurence_count, 1, 'found expected backtrace in the log file');
+
+$node->stop();
-- 
2.25.1

#49Justin Pryzby
pryzby@telsasoft.com
In reply to: vignesh C (#48)
Re: Printing backtrace of postgres processes

Here's a cleaned-up copy of the doc text.

Send a request to the backend with the specified process ID to log its backtrace.
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
<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, or statistics collector process. This
feature will be available if PostgreSQL was built
with the ability to capture backtracee. If not available, the function will
return false and show a WARNING.
Only superusers can request backends to log their backtrace.

- * this and related functions are not inlined.
+ * this and related functions are not inlined. If edata pointer is valid
+ * backtrace information will set in edata.

will *be* set

--
Justin

#50vignesh C
vignesh21@gmail.com
In reply to: Justin Pryzby (#49)
1 attachment(s)
Re: Printing backtrace of postgres processes

On Thu, May 6, 2021 at 7:43 AM Justin Pryzby <pryzby@telsasoft.com> wrote:

Here's a cleaned-up copy of the doc text.

Send a request to the backend with the specified process ID to log its backtrace.
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
<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, or statistics collector process. This
feature will be available if PostgreSQL was built
with the ability to capture backtracee. If not available, the function will
return false and show a WARNING.
Only superusers can request backends to log their backtrace.

Thanks for rephrasing, I have modified to include checkpointer,
walwriter and background writer process also.

- * this and related functions are not inlined.
+ * this and related functions are not inlined. If edata pointer is valid
+ * backtrace information will set in edata.

will *be* set

Modified.

Thanks for the comments, Attached v8 patch has the fixes for the same.

Regards,
Vignesh

Attachments:

v8-0001-Print-backtrace-of-specified-postgres-process.patchtext/x-patch; charset=US-ASCII; name=v8-0001-Print-backtrace-of-specified-postgres-process.patchDownload
From ace69a3f22d0776a6f7fb4e793035354946558e8 Mon Sep 17 00:00:00 2001
From: vignesh <vignesh21@gmail.com>
Date: Wed, 5 May 2021 20:31:38 +0530
Subject: [PATCH v8] Print backtrace of specified postgres process.

The idea here is to implement & expose pg_print_backtrace function, internally
what this function does is, the connected backend will send SIGUSR1 signal by
setting PROCSIG_PRINT_BACKTRACE to postgres backend whose pid matches the
specified process id. Once the backend process receives this signal it will
print the backtrace of the process to the log file based on the logging
configuration, if logging is disabled backtrace will be printed to the
console where postmaster was started.
---
 doc/src/sgml/func.sgml                        | 76 +++++++++++++++++++
 src/backend/postmaster/autovacuum.c           |  7 ++
 src/backend/postmaster/interrupt.c            |  8 ++
 src/backend/storage/ipc/procsignal.c          | 18 +++++
 src/backend/storage/ipc/signalfuncs.c         | 46 +++++++++++
 src/backend/tcop/postgres.c                   |  9 +++
 src/backend/utils/error/elog.c                | 20 ++++-
 src/backend/utils/init/globals.c              |  1 +
 src/include/catalog/pg_proc.dat               |  5 ++
 src/include/miscadmin.h                       |  2 +
 src/include/storage/procsignal.h              |  2 +
 src/include/utils/elog.h                      |  2 +
 .../t/002_print_backtrace_validation.pl       | 73 ++++++++++++++++++
 13 files changed, 265 insertions(+), 4 deletions(-)
 create mode 100644 src/test/modules/test_misc/t/002_print_backtrace_validation.pl

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 0b5571460d..1b76935ce4 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -24974,6 +24974,32 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_print_backtrace</primary>
+        </indexterm>
+        <function>pg_print_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Send a request to the backend with the specified process ID to log its
+        backtrace. 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
+        <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 return false
+        and show a WARNING. Only superusers can request backends to log their
+        backtrace.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -25069,6 +25095,56 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_print_backtrace</function> can be used to print backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_print_backtrace(pg_backend_pid());
+ pg_print_backtrace
+--------------------
+ t
+(1 row)
+
+The backtrace will be logged to the log file if logging is enabled, if logging
+is disabled backtrace will be logged to the console where the postmaster was
+started. For example:
+2021-01-27 11:33:50.247 IST [111735] LOG:  current backtrace:
+        postgres: postgresdba postgres [local] SELECT(set_backtrace+0x38) [0xae06c5]
+        postgres: postgresdba postgres [local] SELECT(ProcessInterrupts+0x788) [0x950c34]
+        postgres: postgresdba postgres [local] SELECT() [0x761e89]
+        postgres: postgresdba postgres [local] SELECT() [0x71bbda]
+        postgres: postgresdba postgres [local] SELECT() [0x71e380]
+        postgres: postgresdba postgres [local] SELECT(standard_ExecutorRun+0x1d6) [0x71c1fe]
+        postgres: postgresdba postgres [local] SELECT(ExecutorRun+0x55) [0x71c026]
+        postgres: postgresdba postgres [local] SELECT() [0x953fc5]
+        postgres: postgresdba postgres [local] SELECT(PortalRun+0x262) [0x953c7e]
+        postgres: postgresdba postgres [local] SELECT() [0x94db78]
+        postgres: postgresdba postgres [local] SELECT(PostgresMain+0x7d7) [0x951e72]
+        postgres: postgresdba postgres [local] SELECT() [0x896b2f]
+        postgres: postgresdba postgres [local] SELECT() [0x8964b5]
+        postgres: postgresdba postgres [local] SELECT() [0x892a79]
+        postgres: postgresdba postgres [local] SELECT(PostmasterMain+0x116b) [0x892350]
+        postgres: postgresdba postgres [local] SELECT() [0x795f72]
+        /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2107bbd505]
+        postgres: postgresdba postgres [local] SELECT() [0x4842a9]
+
+</programlisting>
+    You can get the file name and line number by using gdb/addr2line in
+    linux platforms, as a prerequisite users must ensure gdb/addr2line is
+    already installed:
+<programlisting>
+1)  "info line *address" from gdb on postgres executable. For example:
+gdb ./postgres
+(gdb) info line *0x71c25d
+Line 378 of "execMain.c" starts at address 0x71c25d <literal>&lt;</literal>standard_ExecutorRun+470<literal>&gt;</literal> and ends at 0x71c263 <literal>&lt;</literal>standard_ExecutorRun+476<literal>&gt;</literal>.
+OR 
+2) Using "addr2line -e postgres address", For example:
+addr2line -e ./postgres 0x71c25d
+/home/postgresdba/src/backend/executor/execMain.c:378
+</programlisting>
+</para>
+
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index d516df0ac5..739180b8d9 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -837,6 +837,13 @@ HandleAutoVacLauncherInterrupts(void)
 	if (ProcSignalBarrierPending)
 		ProcessProcSignalBarrier();
 
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+	{
+		PrintBacktracePending = false;
+		set_backtrace(NULL, 0);
+	}
+
 	/* Process sinval catchup interrupts that happened while sleeping */
 	ProcessCatchupInterrupt();
 }
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index dd9136a942..59aff6ca02 100644
--- a/src/backend/postmaster/interrupt.c
+++ b/src/backend/postmaster/interrupt.c
@@ -21,6 +21,7 @@
 #include "storage/ipc.h"
 #include "storage/latch.h"
 #include "storage/procsignal.h"
+#include "tcop/tcopprot.h"
 #include "utils/guc.h"
 
 volatile sig_atomic_t ConfigReloadPending = false;
@@ -41,6 +42,13 @@ HandleMainLoopInterrupts(void)
 		ProcessConfigFile(PGC_SIGHUP);
 	}
 
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+	{
+		PrintBacktracePending = false;
+		set_backtrace(NULL, 0);
+	}
+
 	if (ShutdownRequestPending)
 		proc_exit(0);
 }
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index eac6895141..7cea2a42c5 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -441,6 +441,21 @@ HandleProcSignalBarrierInterrupt(void)
 	/* latch will be set by procsignal_sigusr1_handler */
 }
 
+/*
+ * Handle receipt of an interrupt indicating a print backtrace.
+ *
+ * Note: this is called within a signal handler!  All we can do is set
+ * a flag that will cause the next CHECK_FOR_INTERRUPTS to invoke
+ * set_backtrace function which will log the backtrace.
+ */
+static void
+HandlePrintBacktraceInterrupt(void)
+{
+	InterruptPending = true;
+	PrintBacktracePending = true;
+	/* latch will be set by procsignal_sigusr1_handler */
+}
+
 /*
  * Perform global barrier related interrupt checking.
  *
@@ -679,6 +694,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
 
+	if (CheckProcSignal(PROCSIG_PRINT_BACKTRACE))
+		HandlePrintBacktraceInterrupt();
+
 	SetLatch(MyLatch);
 
 	errno = save_errno;
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index 0337b00226..9af41731e3 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -23,6 +23,7 @@
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
+#include "tcop/tcopprot.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 
@@ -331,3 +332,48 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_print_backtrace - print backtrace of backend process.
+ *
+ * Only superusers can print backtrace.
+ */
+Datum
+pg_print_backtrace(PG_FUNCTION_ARGS)
+{
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	int			pid = PG_GETARG_INT32(0);
+	PGPROC	   *proc;
+
+	/* Only superusers can print back trace. */
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be a superuser to print backtrace")));
+
+	/* BackendPidGetProc returns NULL if the pid isn't valid. */
+	proc = BackendPidGetProc(pid);
+	if (proc == NULL)
+	{
+		ereport(WARNING,
+				(errmsg("PID %d is not a PostgreSQL server process", pid)));
+		PG_RETURN_BOOL(false);
+	}
+
+	/*
+	 * 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, InvalidBackendId))
+		PG_RETURN_BOOL(true);
+	else
+		ereport(WARNING,
+				(errmsg("could not send signal to process %d: %m", pid))); /* return false below */
+#else
+	ereport(WARNING,
+			(errmsg("backtrace generation is not supported by this installation")));
+#endif
+
+	PG_RETURN_BOOL(false);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 2d6d145ecc..d3d0de28ab 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3356,6 +3356,15 @@ ProcessInterrupts(void)
 
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+	{
+		PrintBacktracePending = false;
+		ereport(LOG,
+			(errmsg("logging backtrace of PID %d", MyProcPid)));
+		set_backtrace(NULL, 0);
+	}
 }
 
 
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 65019989cf..a3724eda7f 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -172,7 +172,6 @@ static char formatted_log_time[FORMATTED_TS_LEN];
 
 
 static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
-static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
 static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
 static void write_console(const char *line, int len);
 static void setup_formatted_log_time(void);
@@ -949,9 +948,10 @@ errbacktrace(void)
  * Compute backtrace data and add it to the supplied ErrorData.  num_skip
  * specifies how many inner frames to skip.  Use this to avoid showing the
  * internal backtrace support functions in the backtrace.  This requires that
- * this and related functions are not inlined.
+ * this and related functions are not inlined. If edata pointer is valid
+ * backtrace information will be set in edata.
  */
-static void
+void
 set_backtrace(ErrorData *edata, int num_skip)
 {
 	StringInfoData errtrace;
@@ -978,7 +978,19 @@ set_backtrace(ErrorData *edata, int num_skip)
 						   "backtrace generation is not supported by this installation");
 #endif
 
-	edata->backtrace = errtrace.data;
+	if (edata)
+		edata->backtrace = errtrace.data;
+	else
+	{
+		/*
+		 * LOG_SERVER_ONLY is used intentionally to make sure this information
+		 * is not sent to client based on client_min_messages. We don't want
+		 * to mess up a different session as pg_print_backtrace will be
+		 * sending SIGNAL to a different backend.
+		 */
+		elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
+		pfree(errtrace.data);
+	}
 }
 
 /*
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 381d9e548d..bd869ebf8c 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -39,6 +39,7 @@ volatile sig_atomic_t LogMemoryContextPending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
+volatile sig_atomic_t PrintBacktracePending = false;
 
 int			MyProcPid;
 pg_time_t	MyStartTime;
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 91f0ea2212..251a7f35e1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11611,4 +11611,9 @@
   proname => 'brin_minmax_multi_summary_send', provolatile => 's', prorettype => 'bytea',
   proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_send' },
 
+# function to get the backtrace of server process
+{ oid => '6105', descr => 'print backtrace of process',
+  proname => 'pg_print_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_print_backtrace' },
+
 ]
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 95202d37af..34b0909848 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -94,6 +94,8 @@ extern PGDLLIMPORT volatile uint32 InterruptHoldoffCount;
 extern PGDLLIMPORT volatile uint32 QueryCancelHoldoffCount;
 extern PGDLLIMPORT volatile uint32 CritSectionCount;
 
+extern PGDLLIMPORT volatile sig_atomic_t PrintBacktracePending;
+
 /* in tcop/postgres.c */
 extern void ProcessInterrupts(void);
 
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index eec186be2e..089b15993b 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -44,6 +44,8 @@ typedef enum
 	PROCSIG_RECOVERY_CONFLICT_BUFFERPIN,
 	PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK,
 
+	PROCSIG_PRINT_BACKTRACE,	/* ask backend to print the current backtrace */
+
 	NUM_PROCSIGNALS				/* Must be last! */
 } ProcSignalReason;
 
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index f53607e12e..c63d25716a 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -453,4 +453,6 @@ extern void set_syslog_parameters(const char *ident, int facility);
  */
 extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
 
+extern pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
+
 #endif							/* ELOG_H */
diff --git a/src/test/modules/test_misc/t/002_print_backtrace_validation.pl b/src/test/modules/test_misc/t/002_print_backtrace_validation.pl
new file mode 100644
index 0000000000..00b2cae14e
--- /dev/null
+++ b/src/test/modules/test_misc/t/002_print_backtrace_validation.pl
@@ -0,0 +1,73 @@
+use strict;
+use warnings;
+
+use PostgresNode;
+use TestLib;
+use Test::More tests => 2;
+use Time::HiRes qw(usleep);
+
+# Set up node with logging collector
+my $node = get_new_node('primary');
+$node->init();
+$node->append_conf(
+	'postgresql.conf', qq(
+logging_collector = on
+lc_messages = 'C'
+));
+
+$node->start();
+
+# Verify that log output gets to the file
+$node->psql('postgres', 'select pg_print_backtrace(pg_backend_pid())');
+
+# might need to retry if logging collector process is slow...
+my $max_attempts = 180 * 10;
+
+my $current_logfiles;
+for (my $attempts = 0; $attempts < $max_attempts; $attempts++)
+{
+	eval {
+		$current_logfiles = slurp_file($node->data_dir . '/current_logfiles');
+	};
+	last unless $@;
+	usleep(100_000);
+}
+die $@ if $@;
+
+note "current_logfiles = $current_logfiles";
+
+like(
+	$current_logfiles,
+	qr|^stderr log/postgresql-.*log$|,
+	'current_logfiles is sane');
+
+my $lfname = $current_logfiles;
+$lfname =~ s/^stderr //;
+chomp $lfname;
+
+my $first_logfile;
+my $bt_occurence_count;
+
+# Verify that the backtraces of the processes are logged into logfile.
+for (my $attempts = 0; $attempts < $max_attempts; $attempts++)
+{
+	$first_logfile = $node->data_dir . '/' . $lfname;
+	chomp $first_logfile;
+	print "file is $first_logfile";
+	open my $fh, '<', $first_logfile
+	  or die "Could not open '$first_logfile' $!";
+	while (my $line = <$fh>)
+	{
+		chomp $line;
+		if ($line =~ m/current backtrace/)
+		{
+			$bt_occurence_count++;
+		}
+	}
+	last if $bt_occurence_count == 1;
+	usleep(100_000);
+}
+
+is($bt_occurence_count, 1, 'found expected backtrace in the log file');
+
+$node->stop();
-- 
2.25.1

#51Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#42)
Re: Printing backtrace of postgres processes

On Wed, Feb 3, 2021 at 2:30 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:

A backtrace normally exposes the text of the current query, for
instance, which could contain very sensitive data (passwords in ALTER
USER, customer credit card numbers in ordinary data, etc etc). We
don't allow the postmaster log to be seen by any but very privileged
users; it's not sane to think that this data is any less
security-critical than the postmaster log.

I agree. Vignesh points out downthread that it's just printing out
memory addresses and not necessarily anything too directly usable, but
the memory addresses themselves are potentially security-sensitive. If
that were not a thing, there would not be such a thing as ASLR.

This point is entirely separate from the question of whether
triggering stack traces at inopportune moments could cause system
malfunctions, but that question is also not to be ignored.

That worries me too, although I have a hard time saying exactly why.
If we call an OS-provided function called backtrace() and it does
something other than generate a backtrace - e.g. makes the process seg
fault, or mucks about with the values of global variables - isn't that
just a bug in the OS? Do we have particular reasons to believe that
such bugs are common? My own skepticism here is mostly based on how
inconsistent debuggers are about being able to tell you anything
useful, which makes me think that in a binary compiled with any
optimization, the ability of backtrace() to do something consistently
useful is also questionable. But that's a separate question from
whether it's likely to cause any active harm.

TBH, I'm leaning to the position that this should be superuser
only. I do NOT agree with the idea that ordinary users should
be able to trigger it, even against backends theoretically
belonging to their own userid. (Do I need to point out that
some levels of the call stack might be from security-definer
functions with more privilege than the session's nominal user?)

I agree that ordinary users shouldn't be able to trigger it, but I
think it should be restricted to some predefined role, new or
existing, rather than to superuser. I see no reason not to let
individual users decide what risks they want to take.

--
Robert Haas
EDB: http://www.enterprisedb.com

#52Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#51)
Re: Printing backtrace of postgres processes

Robert Haas <robertmhaas@gmail.com> writes:

On Wed, Feb 3, 2021 at 2:30 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:

TBH, I'm leaning to the position that this should be superuser
only.

I agree that ordinary users shouldn't be able to trigger it, but I
think it should be restricted to some predefined role, new or
existing, rather than to superuser. I see no reason not to let
individual users decide what risks they want to take.

If we think it's worth having a predefined role for, OK. However,
I don't like the future I see us heading towards where there are
hundreds of random predefined roles. Is there an existing role
that it'd be reasonable to attach this ability to?

regards, tom lane

#53Andres Freund
andres@anarazel.de
In reply to: Robert Haas (#51)
Re: Printing backtrace of postgres processes

Hi,

On 2021-05-06 14:38:51 -0400, Robert Haas wrote:

On Wed, Feb 3, 2021 at 2:30 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:

This point is entirely separate from the question of whether
triggering stack traces at inopportune moments could cause system
malfunctions, but that question is also not to be ignored.

That worries me too, although I have a hard time saying exactly why.
If we call an OS-provided function called backtrace() and it does
something other than generate a backtrace - e.g. makes the process seg
fault, or mucks about with the values of global variables - isn't that
just a bug in the OS? Do we have particular reasons to believe that
such bugs are common? My own skepticism here is mostly based on how
inconsistent debuggers are about being able to tell you anything
useful, which makes me think that in a binary compiled with any
optimization, the ability of backtrace() to do something consistently
useful is also questionable. But that's a separate question from
whether it's likely to cause any active harm.

I think that ship kind of has sailed with

commit 71a8a4f6e36547bb060dbcc961ea9b57420f7190
Author: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: 2019-11-08 15:44:20 -0300

Add backtrace support for error reporting

we allow generating backtraces in all kind of places, including
e.g. some inside critical sections via backtrace_functions. I don't
think also doing so during interrupt processing is a meaningful increase
in exposed surface area?

Greetings,

Andres Freund

#54Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#52)
Re: Printing backtrace of postgres processes

Hi,

On 2021-05-06 14:56:09 -0400, Tom Lane wrote:

Robert Haas <robertmhaas@gmail.com> writes:

On Wed, Feb 3, 2021 at 2:30 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:

TBH, I'm leaning to the position that this should be superuser
only.

I agree that ordinary users shouldn't be able to trigger it, but I
think it should be restricted to some predefined role, new or
existing, rather than to superuser. I see no reason not to let
individual users decide what risks they want to take.

If we think it's worth having a predefined role for, OK. However,
I don't like the future I see us heading towards where there are
hundreds of random predefined roles. Is there an existing role
that it'd be reasonable to attach this ability to?

It does seem like it'd be good to group it in with something
else. There's nothing fitting 100% though.

postgres[1475723][1]=# SELECT rolname FROM pg_roles WHERE rolname LIKE 'pg_%' ORDER BY rolname;
┌───────────────────────────┐
│ rolname │
├───────────────────────────┤
│ pg_database_owner │
│ pg_execute_server_program │
│ pg_monitor │
│ pg_read_all_data │
│ pg_read_all_settings │
│ pg_read_all_stats │
│ pg_read_server_files │
│ pg_signal_backend │
│ pg_stat_scan_tables │
│ pg_write_all_data │
│ pg_write_server_files │
└───────────────────────────┘
(11 rows)

We could fit it into pg_monitor, but it's probably a bit more impactful
than most things in there? But I'd go for it anyway, I think.

Greetings,

Andres Freund

#55Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#53)
Re: Printing backtrace of postgres processes

Andres Freund <andres@anarazel.de> writes:

On 2021-05-06 14:38:51 -0400, Robert Haas wrote:

On Wed, Feb 3, 2021 at 2:30 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:

This point is entirely separate from the question of whether
triggering stack traces at inopportune moments could cause system
malfunctions, but that question is also not to be ignored.

I think that ship kind of has sailed with

commit 71a8a4f6e36547bb060dbcc961ea9b57420f7190
Author: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: 2019-11-08 15:44:20 -0300
Add backtrace support for error reporting

The fact that we have a scarily large surface area for that to
cause problems is not a great argument for making the surface
area even larger. Also, I don't think v13 has been out long
enough for us to have full confidence that the backtrace behavior
doesn't cause any problems already.

we allow generating backtraces in all kind of places, including
e.g. some inside critical sections via backtrace_functions.

If there's an elog call inside a critical section, that seems
like a problem already. Are you sure that there are any such?

regards, tom lane

#56Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#54)
Re: Printing backtrace of postgres processes

Andres Freund <andres@anarazel.de> writes:

On 2021-05-06 14:56:09 -0400, Tom Lane wrote:

If we think it's worth having a predefined role for, OK. However,
I don't like the future I see us heading towards where there are
hundreds of random predefined roles. Is there an existing role
that it'd be reasonable to attach this ability to?

It does seem like it'd be good to group it in with something
else. There's nothing fitting 100% though.

I'd probably vote for pg_read_all_data, considering that much of
the concern about this has to do with the possibility of exposure
of sensitive data. I'm not quite sure what the security expectations
are for pg_monitor.

regards, tom lane

#57Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#56)
Re: Printing backtrace of postgres processes

Hi,

On 2021-05-06 15:31:02 -0400, Tom Lane wrote:

I'd probably vote for pg_read_all_data, considering that much of
the concern about this has to do with the possibility of exposure
of sensitive data. I'm not quite sure what the security expectations
are for pg_monitor.

I was wondering the same, but looking at the docs of pg_read_all_data
that doesn't seem like a great fit:

<row>
<entry>pg_read_all_data</entry>
<entry>Read all data (tables, views, sequences), as if having SELECT
rights on those objects, and USAGE rights on all schemas, even without
having it explicitly. This role does not have the role attribute
<literal>BYPASSRLS</literal> set. If RLS is being used, an administrator
may wish to set <literal>BYPASSRLS</literal> on roles which this role is
GRANTed to.</entry>
</row>

It's mostly useful to be able to run pg_dump without superuser
permissions.

Contrast that to pg_monitor:

<row>
<entry>pg_monitor</entry>
<entry>Read/execute various monitoring views and functions.
This role is a member of <literal>pg_read_all_settings</literal>,
<literal>pg_read_all_stats</literal> and
<literal>pg_stat_scan_tables</literal>.</entry>
</row>
...
<para>
The <literal>pg_monitor</literal>, <literal>pg_read_all_settings</literal>,
<literal>pg_read_all_stats</literal> and <literal>pg_stat_scan_tables</literal>
roles are intended to allow administrators to easily configure a role for the
purpose of monitoring the database server. They grant a set of common privileges
allowing the role to read various useful configuration settings, statistics and
other system information normally restricted to superusers.
</para>

"normally restricted to superusers" seems to fit pretty well?

I think if we follow your argument, pg_read_server_files would be a bit
better fit than pg_read_all_data? But it seems "too powerful" to me.
<row>
<entry>pg_read_server_files</entry>
<entry>Allow reading files from any location the database can access on the server with COPY and
other file-access functions.</entry>
</row>

Greetings,

Andres Freund

#58Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#55)
Re: Printing backtrace of postgres processes

Hi,

On 2021-05-06 15:22:02 -0400, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

we allow generating backtraces in all kind of places, including
e.g. some inside critical sections via backtrace_functions.

If there's an elog call inside a critical section, that seems
like a problem already. Are you sure that there are any such?

There's several, yes. In xlog.c there's quite a few that are gated by
wal_debug being enabled. But also a few without that,
e.g. XLogFileInit() logging
elog(DEBUG1, "creating and filling new WAL file");
and XLogFileInit() can be called within a critical section.

Greetings,

Andres Freund

#59Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#56)
Re: Printing backtrace of postgres processes

On Thu, May 6, 2021 at 3:31 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

Andres Freund <andres@anarazel.de> writes:

On 2021-05-06 14:56:09 -0400, Tom Lane wrote:

If we think it's worth having a predefined role for, OK. However,
I don't like the future I see us heading towards where there are
hundreds of random predefined roles. Is there an existing role
that it'd be reasonable to attach this ability to?

It does seem like it'd be good to group it in with something
else. There's nothing fitting 100% though.

I'd probably vote for pg_read_all_data, considering that much of
the concern about this has to do with the possibility of exposure
of sensitive data. I'm not quite sure what the security expectations
are for pg_monitor.

Maybe we should have a role that is specifically for server debugging
type things. This kind of overlaps with Mark Dilger's proposal to try
to allow SET for security-sensitive GUCs to be delegated via
predefined roles. The exact way to divide that up is open to question,
but it wouldn't seem crazy to me if the same role controlled the
ability to do this plus the ability to set the GUCs
backtrace_functions, debug_invalidate_system_caches_always,
wal_consistency_checking, and maybe a few other things.

--
Robert Haas
EDB: http://www.enterprisedb.com

#60vignesh C
vignesh21@gmail.com
In reply to: Robert Haas (#59)
Re: Printing backtrace of postgres processes

On Wed, May 12, 2021 at 2:27 AM Robert Haas <robertmhaas@gmail.com> wrote:

On Thu, May 6, 2021 at 3:31 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

Andres Freund <andres@anarazel.de> writes:

On 2021-05-06 14:56:09 -0400, Tom Lane wrote:

If we think it's worth having a predefined role for, OK. However,
I don't like the future I see us heading towards where there are
hundreds of random predefined roles. Is there an existing role
that it'd be reasonable to attach this ability to?

It does seem like it'd be good to group it in with something
else. There's nothing fitting 100% though.

I'd probably vote for pg_read_all_data, considering that much of
the concern about this has to do with the possibility of exposure
of sensitive data. I'm not quite sure what the security expectations
are for pg_monitor.

Maybe we should have a role that is specifically for server debugging
type things. This kind of overlaps with Mark Dilger's proposal to try
to allow SET for security-sensitive GUCs to be delegated via
predefined roles. The exact way to divide that up is open to question,
but it wouldn't seem crazy to me if the same role controlled the
ability to do this plus the ability to set the GUCs
backtrace_functions, debug_invalidate_system_caches_always,
wal_consistency_checking, and maybe a few other things.

+1 for the idea of having a new role for this. Currently I have
implemented this feature to be supported only for the superuser. If we
are ok with having a new role to handle debugging features, I will
make a 002 patch to handle this.

Regards,
Vignesh

#61Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: vignesh C (#60)
Re: Printing backtrace of postgres processes

On Tue, Jul 13, 2021 at 9:03 PM vignesh C <vignesh21@gmail.com> wrote:

On Wed, May 12, 2021 at 2:27 AM Robert Haas <robertmhaas@gmail.com> wrote:

Maybe we should have a role that is specifically for server debugging
type things. This kind of overlaps with Mark Dilger's proposal to try
to allow SET for security-sensitive GUCs to be delegated via
predefined roles. The exact way to divide that up is open to question,
but it wouldn't seem crazy to me if the same role controlled the
ability to do this plus the ability to set the GUCs
backtrace_functions, debug_invalidate_system_caches_always,
wal_consistency_checking, and maybe a few other things.

+1 for the idea of having a new role for this. Currently I have
implemented this feature to be supported only for the superuser. If we
are ok with having a new role to handle debugging features, I will
make a 002 patch to handle this.

I see that there are a good number of user functions that are
accessible only by superuser (I searched for "if (!superuser())" in
the code base). I agree with the intention to not overload the
superuser anymore and have a few other special roles to delegate the
existing superuser-only functions to them. In that case, are we going
to revisit and reassign all the existing superuser-only functions?

Regards,
Bharath Rupireddy.

#62vignesh C
vignesh21@gmail.com
In reply to: Bharath Rupireddy (#61)
Re: Printing backtrace of postgres processes

On Wed, Jul 21, 2021 at 3:52 PM Bharath Rupireddy <
bharath.rupireddyforpostgres@gmail.com> wrote:

On Tue, Jul 13, 2021 at 9:03 PM vignesh C <vignesh21@gmail.com> wrote:

On Wed, May 12, 2021 at 2:27 AM Robert Haas <robertmhaas@gmail.com>

wrote:

Maybe we should have a role that is specifically for server debugging
type things. This kind of overlaps with Mark Dilger's proposal to try
to allow SET for security-sensitive GUCs to be delegated via
predefined roles. The exact way to divide that up is open to question,
but it wouldn't seem crazy to me if the same role controlled the
ability to do this plus the ability to set the GUCs
backtrace_functions, debug_invalidate_system_caches_always,
wal_consistency_checking, and maybe a few other things.

+1 for the idea of having a new role for this. Currently I have
implemented this feature to be supported only for the superuser. If we
are ok with having a new role to handle debugging features, I will
make a 002 patch to handle this.

I see that there are a good number of user functions that are
accessible only by superuser (I searched for "if (!superuser())" in
the code base). I agree with the intention to not overload the
superuser anymore and have a few other special roles to delegate the
existing superuser-only functions to them. In that case, are we going
to revisit and reassign all the existing superuser-only functions?

As Robert pointed out, this idea is based on Mark Dilger's proposal. Mark
Dilger is already handling many of them at [1]https://commitfest.postgresql.org/33/3223/. I'm proposing this patch
only for server debugging functionalities based on Robert's suggestions at
[2]: /messages/by-id/CA+TgmoZz=K1bQRp0Ug=6uMGFWg-6kaxdHe6VSWaxq0U-YkppYQ@mail.gmail.com
[1]: https://commitfest.postgresql.org/33/3223/
[2]: /messages/by-id/CA+TgmoZz=K1bQRp0Ug=6uMGFWg-6kaxdHe6VSWaxq0U-YkppYQ@mail.gmail.com
/messages/by-id/CA+TgmoZz=K1bQRp0Ug=6uMGFWg-6kaxdHe6VSWaxq0U-YkppYQ@mail.gmail.com

Regards,
Vignesh

#63vignesh C
vignesh21@gmail.com
In reply to: vignesh C (#62)
1 attachment(s)
Re: Printing backtrace of postgres processes

On Wed, Jul 21, 2021 at 10:56 PM vignesh C <vignesh21@gmail.com> wrote:

On Wed, Jul 21, 2021 at 3:52 PM Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> wrote:

On Tue, Jul 13, 2021 at 9:03 PM vignesh C <vignesh21@gmail.com> wrote:

On Wed, May 12, 2021 at 2:27 AM Robert Haas <robertmhaas@gmail.com> wrote:

Maybe we should have a role that is specifically for server debugging
type things. This kind of overlaps with Mark Dilger's proposal to try
to allow SET for security-sensitive GUCs to be delegated via
predefined roles. The exact way to divide that up is open to question,
but it wouldn't seem crazy to me if the same role controlled the
ability to do this plus the ability to set the GUCs
backtrace_functions, debug_invalidate_system_caches_always,
wal_consistency_checking, and maybe a few other things.

+1 for the idea of having a new role for this. Currently I have
implemented this feature to be supported only for the superuser. If we
are ok with having a new role to handle debugging features, I will
make a 002 patch to handle this.

I see that there are a good number of user functions that are
accessible only by superuser (I searched for "if (!superuser())" in
the code base). I agree with the intention to not overload the
superuser anymore and have a few other special roles to delegate the
existing superuser-only functions to them. In that case, are we going
to revisit and reassign all the existing superuser-only functions?

As Robert pointed out, this idea is based on Mark Dilger's proposal. Mark Dilger is already handling many of them at [1]. I'm proposing this patch only for server debugging functionalities based on Robert's suggestions at [2].
[1] - https://commitfest.postgresql.org/33/3223/
[2] - /messages/by-id/CA+TgmoZz=K1bQRp0Ug=6uMGFWg-6kaxdHe6VSWaxq0U-YkppYQ@mail.gmail.com

The previous patch was failing because of the recent test changes made
by commit 201a76183e2 which unified new and get_new_node, attached
patch has the changes to handle the changes accordingly.

Regards,
Vignesh

Attachments:

v8-0001-Print-backtrace-of-specified-postgres-process.patchapplication/x-patch; name=v8-0001-Print-backtrace-of-specified-postgres-process.patchDownload
From e99c97e9a8356976e556fc0cc061d6c98d6b58da Mon Sep 17 00:00:00 2001
From: Vigneshwaran C <vignesh21@gmail.com>
Date: Thu, 26 Aug 2021 17:41:02 +0530
Subject: [PATCH v8] Print backtrace of specified postgres process.

The idea here is to implement & expose pg_print_backtrace function, internally
what this function does is, the connected backend will send SIGUSR1 signal by
setting PROCSIG_PRINT_BACKTRACE to postgres backend whose pid matches the
specified process id. Once the backend process receives this signal it will
print the backtrace of the process to the log file based on the logging
configuration, if logging is disabled backtrace will be printed to the
console where postmaster was started.
---
 doc/src/sgml/func.sgml                        | 76 +++++++++++++++++++
 src/backend/postmaster/autovacuum.c           |  7 ++
 src/backend/postmaster/interrupt.c            |  8 ++
 src/backend/storage/ipc/procsignal.c          | 18 +++++
 src/backend/storage/ipc/signalfuncs.c         | 46 +++++++++++
 src/backend/tcop/postgres.c                   |  9 +++
 src/backend/utils/error/elog.c                | 20 ++++-
 src/backend/utils/init/globals.c              |  1 +
 src/include/catalog/pg_proc.dat               |  5 ++
 src/include/miscadmin.h                       |  2 +
 src/include/storage/procsignal.h              |  2 +
 src/include/utils/elog.h                      |  2 +
 .../t/002_print_backtrace_validation.pl       | 73 ++++++++++++++++++
 13 files changed, 265 insertions(+), 4 deletions(-)
 create mode 100644 src/test/modules/test_misc/t/002_print_backtrace_validation.pl

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 78812b2dbe..de7d341ee0 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -24832,6 +24832,32 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_print_backtrace</primary>
+        </indexterm>
+        <function>pg_print_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Send a request to the backend with the specified process ID to log its
+        backtrace. 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
+        <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 return false
+        and show a WARNING. Only superusers can request backends to log their
+        backtrace.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -25394,6 +25420,56 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_print_backtrace</function> can be used to print backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_print_backtrace(pg_backend_pid());
+ pg_print_backtrace
+--------------------
+ t
+(1 row)
+
+The backtrace will be logged to the log file if logging is enabled, if logging
+is disabled backtrace will be logged to the console where the postmaster was
+started. For example:
+2021-01-27 11:33:50.247 IST [111735] LOG:  current backtrace:
+        postgres: postgresdba postgres [local] SELECT(set_backtrace+0x38) [0xae06c5]
+        postgres: postgresdba postgres [local] SELECT(ProcessInterrupts+0x788) [0x950c34]
+        postgres: postgresdba postgres [local] SELECT() [0x761e89]
+        postgres: postgresdba postgres [local] SELECT() [0x71bbda]
+        postgres: postgresdba postgres [local] SELECT() [0x71e380]
+        postgres: postgresdba postgres [local] SELECT(standard_ExecutorRun+0x1d6) [0x71c1fe]
+        postgres: postgresdba postgres [local] SELECT(ExecutorRun+0x55) [0x71c026]
+        postgres: postgresdba postgres [local] SELECT() [0x953fc5]
+        postgres: postgresdba postgres [local] SELECT(PortalRun+0x262) [0x953c7e]
+        postgres: postgresdba postgres [local] SELECT() [0x94db78]
+        postgres: postgresdba postgres [local] SELECT(PostgresMain+0x7d7) [0x951e72]
+        postgres: postgresdba postgres [local] SELECT() [0x896b2f]
+        postgres: postgresdba postgres [local] SELECT() [0x8964b5]
+        postgres: postgresdba postgres [local] SELECT() [0x892a79]
+        postgres: postgresdba postgres [local] SELECT(PostmasterMain+0x116b) [0x892350]
+        postgres: postgresdba postgres [local] SELECT() [0x795f72]
+        /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2107bbd505]
+        postgres: postgresdba postgres [local] SELECT() [0x4842a9]
+
+</programlisting>
+    You can get the file name and line number by using gdb/addr2line in
+    linux platforms, as a prerequisite users must ensure gdb/addr2line is
+    already installed:
+<programlisting>
+1)  "info line *address" from gdb on postgres executable. For example:
+gdb ./postgres
+(gdb) info line *0x71c25d
+Line 378 of "execMain.c" starts at address 0x71c25d <literal>&lt;</literal>standard_ExecutorRun+470<literal>&gt;</literal> and ends at 0x71c263 <literal>&lt;</literal>standard_ExecutorRun+476<literal>&gt;</literal>.
+OR 
+2) Using "addr2line -e postgres address", For example:
+addr2line -e ./postgres 0x71c25d
+/home/postgresdba/src/backend/executor/execMain.c:378
+</programlisting>
+</para>
+
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 3b3df8fa8c..ce43459708 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -836,6 +836,13 @@ HandleAutoVacLauncherInterrupts(void)
 	if (ProcSignalBarrierPending)
 		ProcessProcSignalBarrier();
 
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+	{
+		PrintBacktracePending = false;
+		set_backtrace(NULL, 0);
+	}
+
 	/* Process sinval catchup interrupts that happened while sleeping */
 	ProcessCatchupInterrupt();
 }
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index dd9136a942..59aff6ca02 100644
--- a/src/backend/postmaster/interrupt.c
+++ b/src/backend/postmaster/interrupt.c
@@ -21,6 +21,7 @@
 #include "storage/ipc.h"
 #include "storage/latch.h"
 #include "storage/procsignal.h"
+#include "tcop/tcopprot.h"
 #include "utils/guc.h"
 
 volatile sig_atomic_t ConfigReloadPending = false;
@@ -41,6 +42,13 @@ HandleMainLoopInterrupts(void)
 		ProcessConfigFile(PGC_SIGHUP);
 	}
 
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+	{
+		PrintBacktracePending = false;
+		set_backtrace(NULL, 0);
+	}
+
 	if (ShutdownRequestPending)
 		proc_exit(0);
 }
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index defb75aa26..a2863e7e8b 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -441,6 +441,21 @@ HandleProcSignalBarrierInterrupt(void)
 	/* latch will be set by procsignal_sigusr1_handler */
 }
 
+/*
+ * Handle receipt of an interrupt indicating a print backtrace.
+ *
+ * Note: this is called within a signal handler!  All we can do is set
+ * a flag that will cause the next CHECK_FOR_INTERRUPTS to invoke
+ * set_backtrace function which will log the backtrace.
+ */
+static void
+HandlePrintBacktraceInterrupt(void)
+{
+	InterruptPending = true;
+	PrintBacktracePending = true;
+	/* latch will be set by procsignal_sigusr1_handler */
+}
+
 /*
  * Perform global barrier related interrupt checking.
  *
@@ -679,6 +694,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
 
+	if (CheckProcSignal(PROCSIG_PRINT_BACKTRACE))
+		HandlePrintBacktraceInterrupt();
+
 	SetLatch(MyLatch);
 
 	errno = save_errno;
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index de69d60e79..12df551502 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -23,6 +23,7 @@
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
+#include "tcop/tcopprot.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 
@@ -298,3 +299,48 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_print_backtrace - print backtrace of backend process.
+ *
+ * Only superusers can print backtrace.
+ */
+Datum
+pg_print_backtrace(PG_FUNCTION_ARGS)
+{
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	int			pid = PG_GETARG_INT32(0);
+	PGPROC	   *proc;
+
+	/* Only superusers can print back trace. */
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be a superuser to print backtrace")));
+
+	/* BackendPidGetProc returns NULL if the pid isn't valid. */
+	proc = BackendPidGetProc(pid);
+	if (proc == NULL)
+	{
+		ereport(WARNING,
+				(errmsg("PID %d is not a PostgreSQL server process", pid)));
+		PG_RETURN_BOOL(false);
+	}
+
+	/*
+	 * 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, InvalidBackendId))
+		PG_RETURN_BOOL(true);
+	else
+		ereport(WARNING,
+				(errmsg("could not send signal to process %d: %m", pid))); /* return false below */
+#else
+	ereport(WARNING,
+			(errmsg("backtrace generation is not supported by this installation")));
+#endif
+
+	PG_RETURN_BOOL(false);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 58b5960e27..f35c42629e 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3366,6 +3366,15 @@ ProcessInterrupts(void)
 
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+	{
+		PrintBacktracePending = false;
+		ereport(LOG,
+			(errmsg("logging backtrace of PID %d", MyProcPid)));
+		set_backtrace(NULL, 0);
+	}
 }
 
 
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index a3e1c59a82..046ce3920b 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -172,7 +172,6 @@ static char formatted_log_time[FORMATTED_TS_LEN];
 
 
 static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
-static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
 static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
 static void write_console(const char *line, int len);
 static void setup_formatted_log_time(void);
@@ -949,9 +948,10 @@ errbacktrace(void)
  * Compute backtrace data and add it to the supplied ErrorData.  num_skip
  * specifies how many inner frames to skip.  Use this to avoid showing the
  * internal backtrace support functions in the backtrace.  This requires that
- * this and related functions are not inlined.
+ * this and related functions are not inlined. If edata pointer is valid
+ * backtrace information will be set in edata.
  */
-static void
+void
 set_backtrace(ErrorData *edata, int num_skip)
 {
 	StringInfoData errtrace;
@@ -978,7 +978,19 @@ set_backtrace(ErrorData *edata, int num_skip)
 						   "backtrace generation is not supported by this installation");
 #endif
 
-	edata->backtrace = errtrace.data;
+	if (edata)
+		edata->backtrace = errtrace.data;
+	else
+	{
+		/*
+		 * LOG_SERVER_ONLY is used intentionally to make sure this information
+		 * is not sent to client based on client_min_messages. We don't want
+		 * to mess up a different session as pg_print_backtrace will be
+		 * sending SIGNAL to a different backend.
+		 */
+		elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
+		pfree(errtrace.data);
+	}
 }
 
 /*
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 381d9e548d..bd869ebf8c 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -39,6 +39,7 @@ volatile sig_atomic_t LogMemoryContextPending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
+volatile sig_atomic_t PrintBacktracePending = false;
 
 int			MyProcPid;
 pg_time_t	MyStartTime;
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index b603700ed9..1bc679a0b5 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11689,4 +11689,9 @@
   prorettype => 'bytea', proargtypes => 'pg_brin_minmax_multi_summary',
   prosrc => 'brin_minmax_multi_summary_send' },
 
+# function to get the backtrace of server process
+{ oid => '6105', descr => 'print backtrace of process',
+  proname => 'pg_print_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_print_backtrace' },
+
 ]
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 2e2e9a364a..011127b6c9 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -103,6 +103,8 @@ extern PGDLLIMPORT volatile uint32 InterruptHoldoffCount;
 extern PGDLLIMPORT volatile uint32 QueryCancelHoldoffCount;
 extern PGDLLIMPORT volatile uint32 CritSectionCount;
 
+extern PGDLLIMPORT volatile sig_atomic_t PrintBacktracePending;
+
 /* in tcop/postgres.c */
 extern void ProcessInterrupts(void);
 
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index eec186be2e..089b15993b 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -44,6 +44,8 @@ typedef enum
 	PROCSIG_RECOVERY_CONFLICT_BUFFERPIN,
 	PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK,
 
+	PROCSIG_PRINT_BACKTRACE,	/* ask backend to print the current backtrace */
+
 	NUM_PROCSIGNALS				/* Must be last! */
 } ProcSignalReason;
 
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index f53607e12e..c63d25716a 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -453,4 +453,6 @@ extern void set_syslog_parameters(const char *ident, int facility);
  */
 extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
 
+extern pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
+
 #endif							/* ELOG_H */
diff --git a/src/test/modules/test_misc/t/002_print_backtrace_validation.pl b/src/test/modules/test_misc/t/002_print_backtrace_validation.pl
new file mode 100644
index 0000000000..5a5b0dc564
--- /dev/null
+++ b/src/test/modules/test_misc/t/002_print_backtrace_validation.pl
@@ -0,0 +1,73 @@
+use strict;
+use warnings;
+
+use PostgresNode;
+use TestLib;
+use Test::More tests => 2;
+use Time::HiRes qw(usleep);
+
+# Set up node with logging collector
+my $node = PostgresNode->new('primary');
+$node->init();
+$node->append_conf(
+	'postgresql.conf', qq(
+logging_collector = on
+lc_messages = 'C'
+));
+
+$node->start();
+
+# Verify that log output gets to the file
+$node->psql('postgres', 'select pg_print_backtrace(pg_backend_pid())');
+
+# might need to retry if logging collector process is slow...
+my $max_attempts = 180 * 10;
+
+my $current_logfiles;
+for (my $attempts = 0; $attempts < $max_attempts; $attempts++)
+{
+	eval {
+		$current_logfiles = slurp_file($node->data_dir . '/current_logfiles');
+	};
+	last unless $@;
+	usleep(100_000);
+}
+die $@ if $@;
+
+note "current_logfiles = $current_logfiles";
+
+like(
+	$current_logfiles,
+	qr|^stderr log/postgresql-.*log$|,
+	'current_logfiles is sane');
+
+my $lfname = $current_logfiles;
+$lfname =~ s/^stderr //;
+chomp $lfname;
+
+my $first_logfile;
+my $bt_occurence_count;
+
+# Verify that the backtraces of the processes are logged into logfile.
+for (my $attempts = 0; $attempts < $max_attempts; $attempts++)
+{
+	$first_logfile = $node->data_dir . '/' . $lfname;
+	chomp $first_logfile;
+	print "file is $first_logfile";
+	open my $fh, '<', $first_logfile
+	  or die "Could not open '$first_logfile' $!";
+	while (my $line = <$fh>)
+	{
+		chomp $line;
+		if ($line =~ m/current backtrace/)
+		{
+			$bt_occurence_count++;
+		}
+	}
+	last if $bt_occurence_count == 1;
+	usleep(100_000);
+}
+
+is($bt_occurence_count, 1, 'found expected backtrace in the log file');
+
+$node->stop();
-- 
2.30.2

#64Daniel Gustafsson
daniel@yesql.se
In reply to: vignesh C (#63)
Re: Printing backtrace of postgres processes

On 26 Aug 2021, at 16:56, vignesh C <vignesh21@gmail.com> wrote:

The previous patch was failing because of the recent test changes made
by commit 201a76183e2 which unified new and get_new_node, attached
patch has the changes to handle the changes accordingly.

This patch now fails because of the test changes made by commit b3b4d8e68a,
please submit a rebase.

--
Daniel Gustafsson https://vmware.com/

#65vignesh C
vignesh21@gmail.com
In reply to: Daniel Gustafsson (#64)
1 attachment(s)
Re: Printing backtrace of postgres processes

On Thu, Nov 4, 2021 at 4:06 PM Daniel Gustafsson <daniel@yesql.se> wrote:

On 26 Aug 2021, at 16:56, vignesh C <vignesh21@gmail.com> wrote:

The previous patch was failing because of the recent test changes made
by commit 201a76183e2 which unified new and get_new_node, attached
patch has the changes to handle the changes accordingly.

This patch now fails because of the test changes made by commit b3b4d8e68a,
please submit a rebase.

Thanks for reporting this, the attached v9 patch has the changes for the same.

Regards,
Vignesh

Attachments:

v9-0001-Print-backtrace-of-specified-postgres-process.patchtext/x-patch; charset=US-ASCII; name=v9-0001-Print-backtrace-of-specified-postgres-process.patchDownload
From 68a694c147a530ae2e964a126b6f36152ca14cd0 Mon Sep 17 00:00:00 2001
From: Vigneshwaran C <vignesh21@gmail.com>
Date: Tue, 9 Nov 2021 16:28:03 +0530
Subject: [PATCH v9] Print backtrace of specified postgres process.

The idea here is to implement & expose pg_print_backtrace function, internally
what this function does is, the connected backend will send SIGUSR1 signal by
setting PROCSIG_PRINT_BACKTRACE to postgres backend whose pid matches the
specified process id. Once the backend process receives this signal it will
print the backtrace of the process to the log file based on the logging
configuration, if logging is disabled backtrace will be printed to the
console where postmaster was started.
---
 doc/src/sgml/func.sgml                        | 81 +++++++++++++++++++
 src/backend/postmaster/autovacuum.c           |  4 +
 src/backend/storage/ipc/procsignal.c          | 36 +++++++++
 src/backend/storage/ipc/signalfuncs.c         | 46 +++++++++++
 src/backend/tcop/postgres.c                   |  4 +
 src/backend/utils/error/elog.c                | 20 ++++-
 src/backend/utils/init/globals.c              |  1 +
 src/include/catalog/pg_proc.dat               |  5 ++
 src/include/miscadmin.h                       |  1 +
 src/include/storage/procsignal.h              |  3 +-
 src/include/utils/elog.h                      |  2 +
 .../t/002_print_backtrace_validation.pl       | 73 +++++++++++++++++
 12 files changed, 271 insertions(+), 5 deletions(-)
 create mode 100644 src/test/modules/test_misc/t/002_print_backtrace_validation.pl

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 24447c0017..c9e6e470f3 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25345,6 +25345,37 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_print_backtrace</primary>
+        </indexterm>
+        <function>pg_print_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Send a request to the backend with the specified process ID to log its
+        backtrace. 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
+        <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 return false
+        and a warning is issued, for example:
+<screen>
+WARNING:  backtrace generation is not supported by this installation
+ pg_print_backtrace 
+--------------------
+ f
+</screen>
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -25458,6 +25489,56 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_print_backtrace</function> can be used to print backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_print_backtrace(pg_backend_pid());
+ pg_print_backtrace
+--------------------
+ t
+(1 row)
+
+The backtrace will be logged to the log file if logging is enabled, if logging
+is disabled backtrace will be logged to the console where the postmaster was
+started. For example:
+2021-01-27 11:33:50.247 IST [111735] LOG:  current backtrace:
+        postgres: postgresdba postgres [local] SELECT(set_backtrace+0x38) [0xae06c5]
+        postgres: postgresdba postgres [local] SELECT(ProcessInterrupts+0x788) [0x950c34]
+        postgres: postgresdba postgres [local] SELECT() [0x761e89]
+        postgres: postgresdba postgres [local] SELECT() [0x71bbda]
+        postgres: postgresdba postgres [local] SELECT() [0x71e380]
+        postgres: postgresdba postgres [local] SELECT(standard_ExecutorRun+0x1d6) [0x71c1fe]
+        postgres: postgresdba postgres [local] SELECT(ExecutorRun+0x55) [0x71c026]
+        postgres: postgresdba postgres [local] SELECT() [0x953fc5]
+        postgres: postgresdba postgres [local] SELECT(PortalRun+0x262) [0x953c7e]
+        postgres: postgresdba postgres [local] SELECT() [0x94db78]
+        postgres: postgresdba postgres [local] SELECT(PostgresMain+0x7d7) [0x951e72]
+        postgres: postgresdba postgres [local] SELECT() [0x896b2f]
+        postgres: postgresdba postgres [local] SELECT() [0x8964b5]
+        postgres: postgresdba postgres [local] SELECT() [0x892a79]
+        postgres: postgresdba postgres [local] SELECT(PostmasterMain+0x116b) [0x892350]
+        postgres: postgresdba postgres [local] SELECT() [0x795f72]
+        /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2107bbd505]
+        postgres: postgresdba postgres [local] SELECT() [0x4842a9]
+
+</programlisting>
+    You can get the file name and line number by using gdb/addr2line in
+    linux platforms, as a prerequisite users must ensure gdb/addr2line is
+    already installed:
+<programlisting>
+1)  "info line *address" from gdb on postgres executable. For example:
+gdb ./postgres
+(gdb) info line *0x71c25d
+Line 378 of "execMain.c" starts at address 0x71c25d <literal>&lt;</literal>standard_ExecutorRun+470<literal>&gt;</literal> and ends at 0x71c263 <literal>&lt;</literal>standard_ExecutorRun+476<literal>&gt;</literal>.
+OR 
+2) Using "addr2line -e postgres address", For example:
+addr2line -e ./postgres 0x71c25d
+/home/postgresdba/src/backend/executor/execMain.c:378
+</programlisting>
+</para>
+
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 96332320a7..f1da744d0f 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -840,6 +840,10 @@ HandleAutoVacLauncherInterrupts(void)
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
 
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+		ProcessPrintBacktraceInterrupt();
+
 	/* Process sinval catchup interrupts that happened while sleeping */
 	ProcessCatchupInterrupt();
 }
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index defb75aa26..68bd17c9de 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -612,6 +612,39 @@ ProcessBarrierPlaceholder(void)
 	return true;
 }
 
+/*
+ * Handle receipt of an interrupt indicating a print backtrace.
+ *
+ * Note: this is called within a signal handler!  All we can do is set
+ * a flag that will cause the next CHECK_FOR_INTERRUPTS to invoke
+ * set_backtrace function which will log the backtrace.
+ */
+static void
+HandlePrintBacktraceInterrupt(void)
+{
+	InterruptPending = true;
+	PrintBacktracePending = true;
+	/* latch will be set by procsignal_sigusr1_handler */
+}
+
+/*
+ * ProcessPrintBacktraceInterrupt
+ * 		Perform logging of backtrace of this 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.
+ */
+void
+ProcessPrintBacktraceInterrupt(void)
+{
+	PrintBacktracePending = false;
+	ereport(LOG,
+			(errmsg("logging backtrace of PID %d", MyProcPid)));
+	set_backtrace(NULL, 0);
+}
+
 /*
  * CheckProcSignal - check to see if a particular reason has been
  * signaled, and clear the signal flag.  Should be called after receiving
@@ -661,6 +694,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_LOG_MEMORY_CONTEXT))
 		HandleLogMemoryContextInterrupt();
 
+	if (CheckProcSignal(PROCSIG_PRINT_BACKTRACE))
+		HandlePrintBacktraceInterrupt();
+
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
 
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index de69d60e79..86024ca315 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -23,6 +23,7 @@
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
+#include "tcop/tcopprot.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 
@@ -298,3 +299,48 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_print_backtrace - print backtrace of backend process.
+ *
+ * Only superusers can print backtrace.
+ */
+Datum
+pg_print_backtrace(PG_FUNCTION_ARGS)
+{
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	int			pid = PG_GETARG_INT32(0);
+	PGPROC	   *proc;
+
+	/* Only superusers can print back trace. */
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be a superuser to print backtrace")));
+
+	/* BackendPidGetProc returns NULL if the pid isn't valid. */
+	proc = BackendPidGetProc(pid);
+	if (proc == NULL)
+	{
+		ereport(WARNING,
+				(errmsg("PID %d is not a PostgreSQL server process", pid)));
+		PG_RETURN_BOOL(false);
+	}
+
+	/*
+	 * 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))
+		PG_RETURN_BOOL(true);
+	else
+		ereport(WARNING,
+				(errmsg("could not send signal to process %d: %m", pid))); /* return false below */
+#else
+	ereport(WARNING,
+			(errmsg("backtrace generation is not supported by this installation")));
+#endif
+
+	PG_RETURN_BOOL(false);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 0775abe35d..624b3f8453 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3366,6 +3366,10 @@ ProcessInterrupts(void)
 
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+		ProcessPrintBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index f33729513a..05fe1bb5e7 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -172,7 +172,6 @@ static char formatted_log_time[FORMATTED_TS_LEN];
 
 
 static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
-static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
 static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
 static void write_console(const char *line, int len);
 static void setup_formatted_log_time(void);
@@ -949,9 +948,10 @@ errbacktrace(void)
  * Compute backtrace data and add it to the supplied ErrorData.  num_skip
  * specifies how many inner frames to skip.  Use this to avoid showing the
  * internal backtrace support functions in the backtrace.  This requires that
- * this and related functions are not inlined.
+ * this and related functions are not inlined. If edata pointer is valid
+ * backtrace information will be set in edata.
  */
-static void
+void
 set_backtrace(ErrorData *edata, int num_skip)
 {
 	StringInfoData errtrace;
@@ -978,7 +978,19 @@ set_backtrace(ErrorData *edata, int num_skip)
 						   "backtrace generation is not supported by this installation");
 #endif
 
-	edata->backtrace = errtrace.data;
+	if (edata)
+		edata->backtrace = errtrace.data;
+	else
+	{
+		/*
+		 * LOG_SERVER_ONLY is used intentionally to make sure this information
+		 * is not sent to client based on client_min_messages. We don't want
+		 * to mess up a different session as pg_print_backtrace will be
+		 * sending SIGNAL to a different backend.
+		 */
+		elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
+		pfree(errtrace.data);
+	}
 }
 
 /*
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 381d9e548d..e49f5331a0 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -36,6 +36,7 @@ volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t IdleSessionTimeoutPending = false;
 volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile sig_atomic_t LogMemoryContextPending = false;
+volatile sig_atomic_t PrintBacktracePending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index d068d6532e..47872558b8 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11689,4 +11689,9 @@
   prorettype => 'bytea', proargtypes => 'pg_brin_minmax_multi_summary',
   prosrc => 'brin_minmax_multi_summary_send' },
 
+# function to get the backtrace of server process
+{ oid => '6105', descr => 'print backtrace of process',
+  proname => 'pg_print_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_print_backtrace' },
+
 ]
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 90a3016065..0da18326f5 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -94,6 +94,7 @@ extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
 extern PGDLLIMPORT volatile sig_atomic_t LogMemoryContextPending;
+extern PGDLLIMPORT volatile sig_atomic_t PrintBacktracePending;
 
 extern PGDLLIMPORT volatile sig_atomic_t CheckClientConnectionPending;
 extern PGDLLIMPORT volatile sig_atomic_t ClientConnectionLost;
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index eec186be2e..731c4fa970 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -35,6 +35,7 @@ typedef enum
 	PROCSIG_WALSND_INIT_STOPPING,	/* ask walsenders to prepare for shutdown  */
 	PROCSIG_BARRIER,			/* global barrier interrupt  */
 	PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
+	PROCSIG_PRINT_BACKTRACE,	/* ask backend to print the current backtrace */
 
 	/* Recovery conflict reasons */
 	PROCSIG_RECOVERY_CONFLICT_DATABASE,
@@ -70,7 +71,7 @@ extern int	SendProcSignal(pid_t pid, ProcSignalReason reason,
 extern uint64 EmitProcSignalBarrier(ProcSignalBarrierType type);
 extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
-
+extern void ProcessPrintBacktraceInterrupt(void);
 extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
 
 #endif							/* PROCSIGNAL_H */
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index f53607e12e..c63d25716a 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -453,4 +453,6 @@ extern void set_syslog_parameters(const char *ident, int facility);
  */
 extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
 
+extern pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
+
 #endif							/* ELOG_H */
diff --git a/src/test/modules/test_misc/t/002_print_backtrace_validation.pl b/src/test/modules/test_misc/t/002_print_backtrace_validation.pl
new file mode 100644
index 0000000000..65d00e7515
--- /dev/null
+++ b/src/test/modules/test_misc/t/002_print_backtrace_validation.pl
@@ -0,0 +1,73 @@
+use strict;
+use warnings;
+
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More tests => 2;
+use Time::HiRes qw(usleep);
+
+# Set up node with logging collector
+my $node = PostgreSQL::Test::Cluster->new('primary');
+$node->init();
+$node->append_conf(
+	'postgresql.conf', qq(
+logging_collector = on
+lc_messages = 'C'
+));
+
+$node->start();
+
+# Verify that log output gets to the file
+$node->psql('postgres', 'select pg_print_backtrace(pg_backend_pid())');
+
+# might need to retry if logging collector process is slow...
+my $max_attempts = 180 * 10;
+
+my $current_logfiles;
+for (my $attempts = 0; $attempts < $max_attempts; $attempts++)
+{
+	eval {
+		$current_logfiles = slurp_file($node->data_dir . '/current_logfiles');
+	};
+	last unless $@;
+	usleep(100_000);
+}
+die $@ if $@;
+
+note "current_logfiles = $current_logfiles";
+
+like(
+	$current_logfiles,
+	qr|^stderr log/postgresql-.*log$|,
+	'current_logfiles is sane');
+
+my $lfname = $current_logfiles;
+$lfname =~ s/^stderr //;
+chomp $lfname;
+
+my $first_logfile;
+my $bt_occurence_count;
+
+# Verify that the backtraces of the processes are logged into logfile.
+for (my $attempts = 0; $attempts < $max_attempts; $attempts++)
+{
+	$first_logfile = $node->data_dir . '/' . $lfname;
+	chomp $first_logfile;
+	print "file is $first_logfile";
+	open my $fh, '<', $first_logfile
+	  or die "Could not open '$first_logfile' $!";
+	while (my $line = <$fh>)
+	{
+		chomp $line;
+		if ($line =~ m/current backtrace/)
+		{
+			$bt_occurence_count++;
+		}
+	}
+	last if $bt_occurence_count == 1;
+	usleep(100_000);
+}
+
+is($bt_occurence_count, 1, 'found expected backtrace in the log file');
+
+$node->stop();
-- 
2.30.2

#66Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: vignesh C (#65)
Re: Printing backtrace of postgres processes

On Tue, Nov 9, 2021 at 4:45 PM vignesh C <vignesh21@gmail.com> wrote:

Thanks for reporting this, the attached v9 patch has the changes for the same.

Thanks for the v9 patch. I have some comments:

1) I think we are moving away from if (!superuser()) checks, see the
commit [1]commit f0b051e322d530a340e62f2ae16d99acdbcb3d05 Author: Jeff Davis <jdavis@postgresql.org> Date: Tue Oct 26 13:13:52 2021 -0700. The goal is to let the GRANT-REVOKE system deal with who
is supposed to run these system functions. Since pg_print_backtrace
also writes the info to server logs,

2) I think we need to have LOG_SERVER_ONLY instead of LOG to avoid
bakctrace being sent to the connected client. This will be good from
security perspective as well since we don't send backtrace over the
wire to the client.
+ PrintBacktracePending = false;
+ ereport(LOG,
+ (errmsg("logging backtrace of PID %d", MyProcPid)));
for pg_log_backend_memory_contexts:
+               /*
+                * Use LOG_SERVER_ONLY to prevent the memory contexts
from being sent
+                * to the connected client.
+                *
+                * We don't buffer the information about all memory
contexts in a
+                * backend into StringInfo and log it as one message.
Otherwise which
+                * may require the buffer to be enlarged very much and
lead to OOM
+                * error since there can be a large number of memory
contexts in a
+                * backend. Instead, we log one message per memory context.
+                */
+               ereport(LOG_SERVER_ONLY,

3) I think we need to extend this function to the auxiliary processes
too, because users might be interested to see what these processes are
doing and where they are currently stuck via their backtraces, see the
proposal for pg_log_backend_memory_contexts at [2]/messages/by-id/CALj2ACU1nBzpacOK2q=a65S_4+Oaz_rLTsU1Ri0gf7YUmnmhfQ@mail.gmail.com. I think you need
to add below code in couple of other places such as
HandleCheckpointerInterrupts, HandleMainLoopInterrupts,
HandlePgArchInterrupts, HandleStartupProcInterrupts,
HandleWalWriterInterrupts.

+ /* Process printing backtrace */
+ if (PrintBacktracePending)
+ ProcessPrintBacktraceInterrupt();

[1]: commit f0b051e322d530a340e62f2ae16d99acdbcb3d05 Author: Jeff Davis <jdavis@postgresql.org> Date: Tue Oct 26 13:13:52 2021 -0700
Author: Jeff Davis <jdavis@postgresql.org>
Date: Tue Oct 26 13:13:52 2021 -0700

Allow GRANT on pg_log_backend_memory_contexts().

Remove superuser check, allowing any user granted permissions on
pg_log_backend_memory_contexts() to log the memory contexts of any
backend.

Note that this could allow a privileged non-superuser to log the
memory contexts of a superuser backend, but as discussed, that does
not seem to be a problem.

Reviewed-by: Nathan Bossart, Bharath Rupireddy, Michael Paquier,
Kyotaro Horiguchi, Andres Freund
Discussion:
/messages/by-id/e5cf6684d17c8d1ef4904ae248605ccd6da03e72.camel@j-davis.com

[2]: /messages/by-id/CALj2ACU1nBzpacOK2q=a65S_4+Oaz_rLTsU1Ri0gf7YUmnmhfQ@mail.gmail.com

Regards,
Bharath Rupireddy.

#67vignesh C
vignesh21@gmail.com
In reply to: Bharath Rupireddy (#66)
2 attachment(s)
Re: Printing backtrace of postgres processes

On Wed, Nov 10, 2021 at 12:17 PM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Tue, Nov 9, 2021 at 4:45 PM vignesh C <vignesh21@gmail.com> wrote:

Thanks for reporting this, the attached v9 patch has the changes for the same.

Thanks for the v9 patch. I have some comments:

1) I think we are moving away from if (!superuser()) checks, see the
commit [1]. The goal is to let the GRANT-REVOKE system deal with who
is supposed to run these system functions. Since pg_print_backtrace
also writes the info to server logs,

Modified

2) I think we need to have LOG_SERVER_ONLY instead of LOG to avoid
bakctrace being sent to the connected client. This will be good from
security perspective as well since we don't send backtrace over the
wire to the client.
+ PrintBacktracePending = false;
+ ereport(LOG,
+ (errmsg("logging backtrace of PID %d", MyProcPid)));
for pg_log_backend_memory_contexts:
+               /*
+                * Use LOG_SERVER_ONLY to prevent the memory contexts
from being sent
+                * to the connected client.
+                *
+                * We don't buffer the information about all memory
contexts in a
+                * backend into StringInfo and log it as one message.
Otherwise which
+                * may require the buffer to be enlarged very much and
lead to OOM
+                * error since there can be a large number of memory
contexts in a
+                * backend. Instead, we log one message per memory context.
+                */
+               ereport(LOG_SERVER_ONLY,

Modified

3) I think we need to extend this function to the auxiliary processes
too, because users might be interested to see what these processes are
doing and where they are currently stuck via their backtraces, see the
proposal for pg_log_backend_memory_contexts at [2]. I think you need
to add below code in couple of other places such as
HandleCheckpointerInterrupts, HandleMainLoopInterrupts,
HandlePgArchInterrupts, HandleStartupProcInterrupts,
HandleWalWriterInterrupts.

+ /* Process printing backtrace */
+ if (PrintBacktracePending)
+ ProcessPrintBacktraceInterrupt();

Created 0002 patch to handle this.

Thanks for the comments, the attached v10 patch has the fixes for the same.

Regards,
Vignesh

Attachments:

v10-0001-Print-backtrace-of-specified-postgres-process.patchtext/x-patch; charset=US-ASCII; name=v10-0001-Print-backtrace-of-specified-postgres-process.patchDownload
From b8f4a48f3f8c95f15f596b4ad77f7a90d3ea376d Mon Sep 17 00:00:00 2001
From: Vigneshwaran C <vignesh21@gmail.com>
Date: Tue, 9 Nov 2021 16:28:03 +0530
Subject: [PATCH v10 1/2] Print backtrace of specified postgres process.

The idea here is to implement & expose pg_print_backtrace function, internally
what this function does is, the connected backend will send SIGUSR1 signal by
setting PROCSIG_PRINT_BACKTRACE to postgres backend whose pid matches the
specified process id. Once the backend process receives this signal it will
print the backtrace of the process to the log file based on the logging
configuration, if logging is disabled backtrace will be printed to the
console where postmaster was started.
---
 doc/src/sgml/func.sgml                        | 81 +++++++++++++++++++
 src/backend/catalog/system_functions.sql      |  2 +
 src/backend/postmaster/autovacuum.c           |  4 +
 src/backend/storage/ipc/procsignal.c          | 41 ++++++++++
 src/backend/storage/ipc/signalfuncs.c         | 48 +++++++++++
 src/backend/tcop/postgres.c                   |  4 +
 src/backend/utils/error/elog.c                | 20 ++++-
 src/backend/utils/init/globals.c              |  1 +
 src/include/catalog/pg_proc.dat               |  5 ++
 src/include/miscadmin.h                       |  1 +
 src/include/storage/procsignal.h              |  3 +-
 src/include/utils/elog.h                      |  2 +
 .../t/002_print_backtrace_validation.pl       | 73 +++++++++++++++++
 src/test/regress/expected/misc_functions.out  | 42 ++++++++++
 src/test/regress/sql/misc_functions.sql       | 31 +++++++
 15 files changed, 353 insertions(+), 5 deletions(-)
 create mode 100644 src/test/modules/test_misc/t/002_print_backtrace_validation.pl

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 24447c0017..c9e6e470f3 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25345,6 +25345,37 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_print_backtrace</primary>
+        </indexterm>
+        <function>pg_print_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Send a request to the backend with the specified process ID to log its
+        backtrace. 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
+        <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 return false
+        and a warning is issued, for example:
+<screen>
+WARNING:  backtrace generation is not supported by this installation
+ pg_print_backtrace 
+--------------------
+ f
+</screen>
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -25458,6 +25489,56 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_print_backtrace</function> can be used to print backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_print_backtrace(pg_backend_pid());
+ pg_print_backtrace
+--------------------
+ t
+(1 row)
+
+The backtrace will be logged to the log file if logging is enabled, if logging
+is disabled backtrace will be logged to the console where the postmaster was
+started. For example:
+2021-01-27 11:33:50.247 IST [111735] LOG:  current backtrace:
+        postgres: postgresdba postgres [local] SELECT(set_backtrace+0x38) [0xae06c5]
+        postgres: postgresdba postgres [local] SELECT(ProcessInterrupts+0x788) [0x950c34]
+        postgres: postgresdba postgres [local] SELECT() [0x761e89]
+        postgres: postgresdba postgres [local] SELECT() [0x71bbda]
+        postgres: postgresdba postgres [local] SELECT() [0x71e380]
+        postgres: postgresdba postgres [local] SELECT(standard_ExecutorRun+0x1d6) [0x71c1fe]
+        postgres: postgresdba postgres [local] SELECT(ExecutorRun+0x55) [0x71c026]
+        postgres: postgresdba postgres [local] SELECT() [0x953fc5]
+        postgres: postgresdba postgres [local] SELECT(PortalRun+0x262) [0x953c7e]
+        postgres: postgresdba postgres [local] SELECT() [0x94db78]
+        postgres: postgresdba postgres [local] SELECT(PostgresMain+0x7d7) [0x951e72]
+        postgres: postgresdba postgres [local] SELECT() [0x896b2f]
+        postgres: postgresdba postgres [local] SELECT() [0x8964b5]
+        postgres: postgresdba postgres [local] SELECT() [0x892a79]
+        postgres: postgresdba postgres [local] SELECT(PostmasterMain+0x116b) [0x892350]
+        postgres: postgresdba postgres [local] SELECT() [0x795f72]
+        /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2107bbd505]
+        postgres: postgresdba postgres [local] SELECT() [0x4842a9]
+
+</programlisting>
+    You can get the file name and line number by using gdb/addr2line in
+    linux platforms, as a prerequisite users must ensure gdb/addr2line is
+    already installed:
+<programlisting>
+1)  "info line *address" from gdb on postgres executable. For example:
+gdb ./postgres
+(gdb) info line *0x71c25d
+Line 378 of "execMain.c" starts at address 0x71c25d <literal>&lt;</literal>standard_ExecutorRun+470<literal>&gt;</literal> and ends at 0x71c263 <literal>&lt;</literal>standard_ExecutorRun+476<literal>&gt;</literal>.
+OR 
+2) Using "addr2line -e postgres address", For example:
+addr2line -e ./postgres 0x71c25d
+/home/postgresdba/src/backend/executor/execMain.c:378
+</programlisting>
+</para>
+
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 54c93b16c4..072c5952fa 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -701,6 +701,8 @@ REVOKE EXECUTE ON FUNCTION pg_ls_dir(text,boolean,boolean) FROM public;
 
 REVOKE EXECUTE ON FUNCTION pg_log_backend_memory_contexts(integer) FROM PUBLIC;
 
+REVOKE EXECUTE ON FUNCTION pg_print_backtrace(integer) FROM PUBLIC;
+
 --
 -- We also set up some things as accessible to standard roles.
 --
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 96332320a7..f1da744d0f 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -840,6 +840,10 @@ HandleAutoVacLauncherInterrupts(void)
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
 
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+		ProcessPrintBacktraceInterrupt();
+
 	/* Process sinval catchup interrupts that happened while sleeping */
 	ProcessCatchupInterrupt();
 }
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index 6e69398cdd..b26b2d4c0b 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -612,6 +612,44 @@ ProcessBarrierPlaceholder(void)
 	return true;
 }
 
+/*
+ * Handle receipt of an interrupt indicating a print backtrace.
+ *
+ * Note: this is called within a signal handler!  All we can do is set
+ * a flag that will cause the next CHECK_FOR_INTERRUPTS to invoke
+ * set_backtrace function which will log the backtrace.
+ */
+static void
+HandlePrintBacktraceInterrupt(void)
+{
+	InterruptPending = true;
+	PrintBacktracePending = true;
+	/* latch will be set by procsignal_sigusr1_handler */
+}
+
+/*
+ * ProcessPrintBacktraceInterrupt
+ * 		Perform logging of backtrace of this 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.
+ */
+void
+ProcessPrintBacktraceInterrupt(void)
+{
+	PrintBacktracePending = false;
+
+	/*
+	 * Use LOG_SERVER_ONLY to prevent this message from being sent to the
+	 * connected client.
+	 */
+	ereport(LOG_SERVER_ONLY,
+			(errmsg("logging backtrace of PID %d", MyProcPid)));
+	set_backtrace(NULL, 0);
+}
+
 /*
  * CheckProcSignal - check to see if a particular reason has been
  * signaled, and clear the signal flag.  Should be called after receiving
@@ -661,6 +699,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_LOG_MEMORY_CONTEXT))
 		HandleLogMemoryContextInterrupt();
 
+	if (CheckProcSignal(PROCSIG_PRINT_BACKTRACE))
+		HandlePrintBacktraceInterrupt();
+
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
 
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index de69d60e79..df942d6015 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -23,6 +23,7 @@
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
+#include "tcop/tcopprot.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 
@@ -298,3 +299,50 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_print_backtrace - print backtrace of backend process.
+ *
+ * By default, only superusers can print backtrace. Additional roles can be
+ * permitted with GRANT.
+ */
+Datum
+pg_print_backtrace(PG_FUNCTION_ARGS)
+{
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	int			pid = PG_GETARG_INT32(0);
+	PGPROC	   *proc;
+
+	/*
+	 * 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.
+	 */
+	proc = BackendPidGetProc(pid);
+	if (proc == NULL)
+	{
+		ereport(WARNING,
+				(errmsg("PID %d is not a PostgreSQL server process", pid)));
+		PG_RETURN_BOOL(false);
+	}
+
+	/*
+	 * 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))
+		PG_RETURN_BOOL(true);
+	else
+		ereport(WARNING,
+				(errmsg("could not send signal to process %d: %m", pid))); /* return false below */
+#else
+	ereport(WARNING,
+			(errmsg("backtrace generation is not supported by this installation")));
+#endif
+
+	PG_RETURN_BOOL(false);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 0775abe35d..624b3f8453 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3366,6 +3366,10 @@ ProcessInterrupts(void)
 
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+		ProcessPrintBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index f33729513a..05fe1bb5e7 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -172,7 +172,6 @@ static char formatted_log_time[FORMATTED_TS_LEN];
 
 
 static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
-static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
 static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
 static void write_console(const char *line, int len);
 static void setup_formatted_log_time(void);
@@ -949,9 +948,10 @@ errbacktrace(void)
  * Compute backtrace data and add it to the supplied ErrorData.  num_skip
  * specifies how many inner frames to skip.  Use this to avoid showing the
  * internal backtrace support functions in the backtrace.  This requires that
- * this and related functions are not inlined.
+ * this and related functions are not inlined. If edata pointer is valid
+ * backtrace information will be set in edata.
  */
-static void
+void
 set_backtrace(ErrorData *edata, int num_skip)
 {
 	StringInfoData errtrace;
@@ -978,7 +978,19 @@ set_backtrace(ErrorData *edata, int num_skip)
 						   "backtrace generation is not supported by this installation");
 #endif
 
-	edata->backtrace = errtrace.data;
+	if (edata)
+		edata->backtrace = errtrace.data;
+	else
+	{
+		/*
+		 * LOG_SERVER_ONLY is used intentionally to make sure this information
+		 * is not sent to client based on client_min_messages. We don't want
+		 * to mess up a different session as pg_print_backtrace will be
+		 * sending SIGNAL to a different backend.
+		 */
+		elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
+		pfree(errtrace.data);
+	}
 }
 
 /*
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 381d9e548d..e49f5331a0 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -36,6 +36,7 @@ volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t IdleSessionTimeoutPending = false;
 volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile sig_atomic_t LogMemoryContextPending = false;
+volatile sig_atomic_t PrintBacktracePending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index d068d6532e..47872558b8 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11689,4 +11689,9 @@
   prorettype => 'bytea', proargtypes => 'pg_brin_minmax_multi_summary',
   prosrc => 'brin_minmax_multi_summary_send' },
 
+# function to get the backtrace of server process
+{ oid => '6105', descr => 'print backtrace of process',
+  proname => 'pg_print_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_print_backtrace' },
+
 ]
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 90a3016065..0da18326f5 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -94,6 +94,7 @@ extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
 extern PGDLLIMPORT volatile sig_atomic_t LogMemoryContextPending;
+extern PGDLLIMPORT volatile sig_atomic_t PrintBacktracePending;
 
 extern PGDLLIMPORT volatile sig_atomic_t CheckClientConnectionPending;
 extern PGDLLIMPORT volatile sig_atomic_t ClientConnectionLost;
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index eec186be2e..731c4fa970 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -35,6 +35,7 @@ typedef enum
 	PROCSIG_WALSND_INIT_STOPPING,	/* ask walsenders to prepare for shutdown  */
 	PROCSIG_BARRIER,			/* global barrier interrupt  */
 	PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
+	PROCSIG_PRINT_BACKTRACE,	/* ask backend to print the current backtrace */
 
 	/* Recovery conflict reasons */
 	PROCSIG_RECOVERY_CONFLICT_DATABASE,
@@ -70,7 +71,7 @@ extern int	SendProcSignal(pid_t pid, ProcSignalReason reason,
 extern uint64 EmitProcSignalBarrier(ProcSignalBarrierType type);
 extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
-
+extern void ProcessPrintBacktraceInterrupt(void);
 extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
 
 #endif							/* PROCSIGNAL_H */
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index f53607e12e..c63d25716a 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -453,4 +453,6 @@ extern void set_syslog_parameters(const char *ident, int facility);
  */
 extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
 
+extern pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
+
 #endif							/* ELOG_H */
diff --git a/src/test/modules/test_misc/t/002_print_backtrace_validation.pl b/src/test/modules/test_misc/t/002_print_backtrace_validation.pl
new file mode 100644
index 0000000000..65d00e7515
--- /dev/null
+++ b/src/test/modules/test_misc/t/002_print_backtrace_validation.pl
@@ -0,0 +1,73 @@
+use strict;
+use warnings;
+
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More tests => 2;
+use Time::HiRes qw(usleep);
+
+# Set up node with logging collector
+my $node = PostgreSQL::Test::Cluster->new('primary');
+$node->init();
+$node->append_conf(
+	'postgresql.conf', qq(
+logging_collector = on
+lc_messages = 'C'
+));
+
+$node->start();
+
+# Verify that log output gets to the file
+$node->psql('postgres', 'select pg_print_backtrace(pg_backend_pid())');
+
+# might need to retry if logging collector process is slow...
+my $max_attempts = 180 * 10;
+
+my $current_logfiles;
+for (my $attempts = 0; $attempts < $max_attempts; $attempts++)
+{
+	eval {
+		$current_logfiles = slurp_file($node->data_dir . '/current_logfiles');
+	};
+	last unless $@;
+	usleep(100_000);
+}
+die $@ if $@;
+
+note "current_logfiles = $current_logfiles";
+
+like(
+	$current_logfiles,
+	qr|^stderr log/postgresql-.*log$|,
+	'current_logfiles is sane');
+
+my $lfname = $current_logfiles;
+$lfname =~ s/^stderr //;
+chomp $lfname;
+
+my $first_logfile;
+my $bt_occurence_count;
+
+# Verify that the backtraces of the processes are logged into logfile.
+for (my $attempts = 0; $attempts < $max_attempts; $attempts++)
+{
+	$first_logfile = $node->data_dir . '/' . $lfname;
+	chomp $first_logfile;
+	print "file is $first_logfile";
+	open my $fh, '<', $first_logfile
+	  or die "Could not open '$first_logfile' $!";
+	while (my $line = <$fh>)
+	{
+		chomp $line;
+		if ($line =~ m/current backtrace/)
+		{
+			$bt_occurence_count++;
+		}
+	}
+	last if $bt_occurence_count == 1;
+	usleep(100_000);
+}
+
+is($bt_occurence_count, 1, 'found expected backtrace in the log file');
+
+$node->stop();
diff --git a/src/test/regress/expected/misc_functions.out b/src/test/regress/expected/misc_functions.out
index 71d316cad3..c75cef00e7 100644
--- a/src/test/regress/expected/misc_functions.out
+++ b/src/test/regress/expected/misc_functions.out
@@ -176,6 +176,48 @@ REVOKE EXECUTE ON FUNCTION pg_log_backend_memory_contexts(integer)
   FROM regress_log_memory;
 DROP ROLE regress_log_memory;
 --
+-- pg_print_backtrace()
+--
+-- Backtrace are logged and they are not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_print_backtrace(pg_backend_pid());
+ pg_print_backtrace 
+--------------------
+ t
+(1 row)
+
+CREATE ROLE regress_print_backtrace;
+SELECT has_function_privilege('regress_print_backtrace',
+  'pg_print_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_print_backtrace(integer)
+  TO regress_print_backtrace;
+SELECT has_function_privilege('regress_print_backtrace',
+  'pg_print_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_print_backtrace;
+SELECT pg_print_backtrace(pg_backend_pid());
+ pg_print_backtrace 
+--------------------
+ t
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_print_backtrace(integer)
+  FROM regress_print_backtrace;
+DROP ROLE regress_print_backtrace;
+--
 -- Test some built-in SRFs
 --
 -- The outputs of these are variable, so we can't just print their results
diff --git a/src/test/regress/sql/misc_functions.sql b/src/test/regress/sql/misc_functions.sql
index 8c23874b3f..d63d2e2ddb 100644
--- a/src/test/regress/sql/misc_functions.sql
+++ b/src/test/regress/sql/misc_functions.sql
@@ -61,6 +61,37 @@ REVOKE EXECUTE ON FUNCTION pg_log_backend_memory_contexts(integer)
 
 DROP ROLE regress_log_memory;
 
+--
+-- pg_print_backtrace()
+--
+-- Backtrace are logged and they are not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+
+SELECT pg_print_backtrace(pg_backend_pid());
+
+CREATE ROLE regress_print_backtrace;
+
+SELECT has_function_privilege('regress_print_backtrace',
+  'pg_print_backtrace(integer)', 'EXECUTE'); -- no
+
+GRANT EXECUTE ON FUNCTION pg_print_backtrace(integer)
+  TO regress_print_backtrace;
+
+SELECT has_function_privilege('regress_print_backtrace',
+  'pg_print_backtrace(integer)', 'EXECUTE'); -- yes
+
+SET ROLE regress_print_backtrace;
+SELECT pg_print_backtrace(pg_backend_pid());
+RESET ROLE;
+
+REVOKE EXECUTE ON FUNCTION pg_print_backtrace(integer)
+  FROM regress_print_backtrace;
+
+DROP ROLE regress_print_backtrace;
+
 --
 -- Test some built-in SRFs
 --
-- 
2.30.2

v10-0002-pg_print_backtrace-support-for-printing-backtrac.patchtext/x-patch; charset=US-ASCII; name=v10-0002-pg_print_backtrace-support-for-printing-backtrac.patchDownload
From 96972c00ae3ee2110d176f3ae2eb425949d0db6d Mon Sep 17 00:00:00 2001
From: Vigneshwaran C <vignesh21@gmail.com>
Date: Thu, 11 Nov 2021 11:50:34 +0530
Subject: [PATCH v10 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                       |  9 +++--
 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/signalfuncs.c        | 35 ++++++++++++++------
 src/test/regress/expected/misc_functions.out | 11 ++++++
 src/test/regress/sql/misc_functions.sql      |  9 +++++
 9 files changed, 69 insertions(+), 15 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index c9e6e470f3..2ce236cf7b 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25362,11 +25362,10 @@ SELECT collation for ('foo' COLLATE "de_DE");
         <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 return false
-        and a warning is issued, for example:
+        This feature is not supported for the postmaster, logger 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 return false and a warning is issued, for example:
 <screen>
 WARNING:  backtrace generation is not supported by this installation
  pg_print_backtrace 
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 74a7d7c4d0..1c6410a410 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -715,4 +715,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/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index df942d6015..93108448a5 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

#68Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: vignesh C (#67)
Re: Printing backtrace of postgres processes

On Thu, Nov 11, 2021 at 12:14 PM vignesh C <vignesh21@gmail.com> wrote:

Thanks for the comments, the attached v10 patch has the fixes for the same.

Thanks for the patches. Here are some comments:

1) In the docs, let's have the similar description of
pg_log_backend_memory_contexts for pg_print_backtrace, just for the
continuity in the users readability.

2) I don't know how the <screen> part looks like in the Server
Signaling Functions table. I think here you can just say, it will emit
a warning and return false if not supported by the installation. And
you can give the <screen> part after the description where you are
showing a sample backtrace.

+        capture backtrace. If not available, the function will return false
+        and a warning is issued, for example:
+<screen>
+WARNING:  backtrace generation is not supported by this installation
+ pg_print_backtrace
+--------------------
+ f
+</screen>
+       </para></entry>
+      </row>

3) Replace '!' with '.'.
+ * Note: this is called within a signal handler! All we can do is set

4) It is not only the next CFI but also the process specific interrupt
handlers (in your 0002 patch) right?
+ * a flag that will cause the next CHECK_FOR_INTERRUPTS to invoke

5) I think you need to update CATALOG_VERSION_NO, mostly the committer
will take care of it but just in case.

6) Be consistent with casing "Verify" and "Might"
+# Verify that log output gets to the file
+# might need to retry if logging collector process is slow...

Regards,
Bharath Rupireddy.

#69Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: Bharath Rupireddy (#68)
Re: Printing backtrace of postgres processes

On Fri, Nov 12, 2021 at 5:15 PM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Thu, Nov 11, 2021 at 12:14 PM vignesh C <vignesh21@gmail.com> wrote:

Thanks for the comments, the attached v10 patch has the fixes for the same.

Thanks for the patches. Here are some comments:

1) In the docs, let's have the similar description of
pg_log_backend_memory_contexts for pg_print_backtrace, just for the
continuity in the users readability.

2) I don't know how the <screen> part looks like in the Server
Signaling Functions table. I think here you can just say, it will emit
a warning and return false if not supported by the installation. And
you can give the <screen> part after the description where you are
showing a sample backtrace.

+        capture backtrace. If not available, the function will return false
+        and a warning is issued, for example:
+<screen>
+WARNING:  backtrace generation is not supported by this installation
+ pg_print_backtrace
+--------------------
+ f
+</screen>
+       </para></entry>
+      </row>

3) Replace '!' with '.'.
+ * Note: this is called within a signal handler! All we can do is set

4) It is not only the next CFI but also the process specific interrupt
handlers (in your 0002 patch) right?
+ * a flag that will cause the next CHECK_FOR_INTERRUPTS to invoke

5) I think you need to update CATALOG_VERSION_NO, mostly the committer
will take care of it but just in case.

6) Be consistent with casing "Verify" and "Might"
+# Verify that log output gets to the file
+# might need to retry if logging collector process is slow...

Few more comments:

7) Do we need TAP tests for this function? I think it is sufficient to
test the function in misc_functions.sql, please remove
002_print_backtrace_validation.pl. Note that we don't do similar TAP
testing for pg_log_backend_memory_contexts as well.

8) I hope you have manually tested the pg_print_backtrace for the
startup process and other auxiliary processes.

9) I think we can have a similar description (to the patch [1]/messages/by-id/CALj2ACU1nBzpacOK2q=a65S_4+Oaz_rLTsU1Ri0gf7YUmnmhfQ@mail.gmail.com). with
links to each process definition in the glossary so that it will be
easier for the users to follow the links and know what each process
is. Especially, I didn't like the 0002 mentioned about the
BackendPidGetProc or AuxiliaryPidGetProc as these are internal to the
server and the users don't care about them.

- * 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.
  */

Here's what I have written in the other patch [1]/messages/by-id/CALj2ACU1nBzpacOK2q=a65S_4+Oaz_rLTsU1Ri0gf7YUmnmhfQ@mail.gmail.com, if okay please use this:

+        Requests to log memory contexts of the backend 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.
+        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),

I have no further comments on v10.

[1]: /messages/by-id/CALj2ACU1nBzpacOK2q=a65S_4+Oaz_rLTsU1Ri0gf7YUmnmhfQ@mail.gmail.com

Regards,
Bharath Rupireddy.

#70vignesh C
vignesh21@gmail.com
In reply to: Bharath Rupireddy (#68)
2 attachment(s)
Re: Printing backtrace of postgres processes

On Fri, Nov 12, 2021 at 5:15 PM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Thu, Nov 11, 2021 at 12:14 PM vignesh C <vignesh21@gmail.com> wrote:

Thanks for the comments, the attached v10 patch has the fixes for the same.

Thanks for the patches. Here are some comments:

1) In the docs, let's have the similar description of
pg_log_backend_memory_contexts for pg_print_backtrace, just for the
continuity in the users readability.

I have kept some contents of the description similar. There is some
additional information to explain more about the functionality. I felt
that will help the user to understand more about the feature.

2) I don't know how the <screen> part looks like in the Server
Signaling Functions table. I think here you can just say, it will emit
a warning and return false if not supported by the installation. And
you can give the <screen> part after the description where you are
showing a sample backtrace.

+        capture backtrace. If not available, the function will return false
+        and a warning is issued, for example:
+<screen>
+WARNING:  backtrace generation is not supported by this installation
+ pg_print_backtrace
+--------------------
+ f
+</screen>
+       </para></entry>
+      </row>

Modified

3) Replace '!' with '.'.
+ * Note: this is called within a signal handler! All we can do is set

I have changed it similar to HandleLogMemoryContextInterrupt

4) It is not only the next CFI but also the process specific interrupt
handlers (in your 0002 patch) right?
+ * a flag that will cause the next CHECK_FOR_INTERRUPTS to invoke

Modified

5) I think you need to update CATALOG_VERSION_NO, mostly the committer
will take care of it but just in case.

Modified

6) Be consistent with casing "Verify" and "Might"
+# Verify that log output gets to the file
+# might need to retry if logging collector process is slow...

Modified

The attached v11 patch has the changes for the same.

Regards,
Vignesh

Attachments:

v11-0001-Print-backtrace-of-specified-postgres-process.patchtext/x-patch; charset=US-ASCII; name=v11-0001-Print-backtrace-of-specified-postgres-process.patchDownload
From c29bafb470a367bd4f790f6f50edc3e8002f46c7 Mon Sep 17 00:00:00 2001
From: Vignesh C <vignesh21@gmail.com>
Date: Tue, 9 Nov 2021 16:28:03 +0530
Subject: [PATCH v11 1/2] Print backtrace of specified postgres process.

The idea here is to implement & expose pg_print_backtrace function, internally
what this function does is, the connected backend will send SIGUSR1 signal by
setting PROCSIG_PRINT_BACKTRACE to postgres backend whose pid matches the
specified process id. Once the backend process receives this signal it will
print the backtrace of the process to the log file based on the logging
configuration, if logging is disabled backtrace will be printed to the
console where postmaster was started.
---
 doc/src/sgml/func.sgml                        | 82 +++++++++++++++++++
 src/backend/catalog/system_functions.sql      |  2 +
 src/backend/postmaster/autovacuum.c           |  4 +
 src/backend/storage/ipc/procsignal.c          | 41 ++++++++++
 src/backend/storage/ipc/signalfuncs.c         | 48 +++++++++++
 src/backend/tcop/postgres.c                   |  4 +
 src/backend/utils/error/elog.c                | 20 ++++-
 src/backend/utils/init/globals.c              |  1 +
 src/include/catalog/catversion.h              |  2 +-
 src/include/catalog/pg_proc.dat               |  5 ++
 src/include/miscadmin.h                       |  1 +
 src/include/storage/procsignal.h              |  3 +-
 src/include/utils/elog.h                      |  2 +
 .../t/002_print_backtrace_validation.pl       | 73 +++++++++++++++++
 src/test/regress/expected/misc_functions.out  | 42 ++++++++++
 src/test/regress/sql/misc_functions.sql       | 31 +++++++
 16 files changed, 355 insertions(+), 6 deletions(-)
 create mode 100644 src/test/modules/test_misc/t/002_print_backtrace_validation.pl

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 24447c0017..620e6ee54a 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25345,6 +25345,31 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_print_backtrace</primary>
+        </indexterm>
+        <function>pg_print_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Requests to log the backtrace of the backend 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
+        <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.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -25458,6 +25483,63 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_print_backtrace</function> can be used to log the backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_print_backtrace(pg_backend_pid());
+ pg_print_backtrace
+--------------------
+ t
+(1 row)
+</programlisting>
+The backtrace will be logged to the log file if logging is enabled, if logging
+is disabled backtrace will be logged to the console where the postmaster was
+started. For example:
+<screen>
+2021-01-27 11:33:50.247 IST [111735] LOG:  current backtrace:
+        postgres: postgresdba postgres [local] SELECT(set_backtrace+0x38) [0xae06c5]
+        postgres: postgresdba postgres [local] SELECT(ProcessInterrupts+0x788) [0x950c34]
+        postgres: postgresdba postgres [local] SELECT() [0x761e89]
+        postgres: postgresdba postgres [local] SELECT() [0x71bbda]
+        postgres: postgresdba postgres [local] SELECT() [0x71e380]
+        postgres: postgresdba postgres [local] SELECT(standard_ExecutorRun+0x1d6) [0x71c1fe]
+        postgres: postgresdba postgres [local] SELECT(ExecutorRun+0x55) [0x71c026]
+        postgres: postgresdba postgres [local] SELECT() [0x953fc5]
+        postgres: postgresdba postgres [local] SELECT(PortalRun+0x262) [0x953c7e]
+        postgres: postgresdba postgres [local] SELECT() [0x94db78]
+        postgres: postgresdba postgres [local] SELECT(PostgresMain+0x7d7) [0x951e72]
+        postgres: postgresdba postgres [local] SELECT() [0x896b2f]
+        postgres: postgresdba postgres [local] SELECT() [0x8964b5]
+        postgres: postgresdba postgres [local] SELECT() [0x892a79]
+        postgres: postgresdba postgres [local] SELECT(PostmasterMain+0x116b) [0x892350]
+        postgres: postgresdba postgres [local] SELECT() [0x795f72]
+        /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2107bbd505]
+        postgres: postgresdba postgres [local] SELECT() [0x4842a9]
+</screen>
+    You can get the file name and line number by using gdb/addr2line in
+    linux platforms, as a prerequisite users must ensure gdb/addr2line is
+    already installed:
+<programlisting>
+1)  "info line *address" from gdb on postgres executable. For example:
+gdb ./postgres
+(gdb) info line *0x71c25d
+Line 378 of "execMain.c" starts at address 0x71c25d <literal>&lt;</literal>standard_ExecutorRun+470<literal>&gt;</literal> and ends at 0x71c263 <literal>&lt;</literal>standard_ExecutorRun+476<literal>&gt;</literal>.
+OR 
+2) Using "addr2line -e postgres address", For example:
+addr2line -e ./postgres 0x71c25d
+/home/postgresdba/src/backend/executor/execMain.c:378
+</programlisting>
+    If PostgreSQL was not built with the ability to capture backtrace. The
+    function will emit a warning and return false, for example:
+<screen>
+WARNING:  backtrace generation is not supported by this installation
+ pg_print_backtrace 
+--------------------
+ f
+</screen>
+   </para>
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 54c93b16c4..072c5952fa 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -701,6 +701,8 @@ REVOKE EXECUTE ON FUNCTION pg_ls_dir(text,boolean,boolean) FROM public;
 
 REVOKE EXECUTE ON FUNCTION pg_log_backend_memory_contexts(integer) FROM PUBLIC;
 
+REVOKE EXECUTE ON FUNCTION pg_print_backtrace(integer) FROM PUBLIC;
+
 --
 -- We also set up some things as accessible to standard roles.
 --
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 96332320a7..f1da744d0f 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -840,6 +840,10 @@ HandleAutoVacLauncherInterrupts(void)
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
 
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+		ProcessPrintBacktraceInterrupt();
+
 	/* Process sinval catchup interrupts that happened while sleeping */
 	ProcessCatchupInterrupt();
 }
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index 6e69398cdd..80fa6d4990 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -612,6 +612,44 @@ ProcessBarrierPlaceholder(void)
 	return true;
 }
 
+/*
+ * HandlePrintBacktraceInterrupt - Handle receipt of an interrupt indicating a
+ * print backtrace.
+ *
+ * All the actual work is deferred to ProcessPrintBacktraceInterrupt(),
+ * because we cannot safely emit a log message inside the signal handler.
+ */
+static void
+HandlePrintBacktraceInterrupt(void)
+{
+	InterruptPending = true;
+	PrintBacktracePending = true;
+	/* latch will be set by procsignal_sigusr1_handler */
+}
+
+/*
+ * ProcessPrintBacktraceInterrupt - Perform logging of backtrace of this
+ * 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.
+ */
+void
+ProcessPrintBacktraceInterrupt(void)
+{
+	PrintBacktracePending = false;
+
+	/*
+	 * Use LOG_SERVER_ONLY to prevent this message from being sent to the
+	 * connected client.
+	 */
+	ereport(LOG_SERVER_ONLY,
+			(errmsg("logging backtrace of PID %d", MyProcPid)));
+	set_backtrace(NULL, 0);
+}
+
 /*
  * CheckProcSignal - check to see if a particular reason has been
  * signaled, and clear the signal flag.  Should be called after receiving
@@ -661,6 +699,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_LOG_MEMORY_CONTEXT))
 		HandleLogMemoryContextInterrupt();
 
+	if (CheckProcSignal(PROCSIG_PRINT_BACKTRACE))
+		HandlePrintBacktraceInterrupt();
+
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
 
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index de69d60e79..df942d6015 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -23,6 +23,7 @@
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
+#include "tcop/tcopprot.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 
@@ -298,3 +299,50 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_print_backtrace - print backtrace of backend process.
+ *
+ * By default, only superusers can print backtrace. Additional roles can be
+ * permitted with GRANT.
+ */
+Datum
+pg_print_backtrace(PG_FUNCTION_ARGS)
+{
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	int			pid = PG_GETARG_INT32(0);
+	PGPROC	   *proc;
+
+	/*
+	 * 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.
+	 */
+	proc = BackendPidGetProc(pid);
+	if (proc == NULL)
+	{
+		ereport(WARNING,
+				(errmsg("PID %d is not a PostgreSQL server process", pid)));
+		PG_RETURN_BOOL(false);
+	}
+
+	/*
+	 * 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))
+		PG_RETURN_BOOL(true);
+	else
+		ereport(WARNING,
+				(errmsg("could not send signal to process %d: %m", pid))); /* return false below */
+#else
+	ereport(WARNING,
+			(errmsg("backtrace generation is not supported by this installation")));
+#endif
+
+	PG_RETURN_BOOL(false);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 0775abe35d..624b3f8453 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3366,6 +3366,10 @@ ProcessInterrupts(void)
 
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+		ProcessPrintBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index f33729513a..05fe1bb5e7 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -172,7 +172,6 @@ static char formatted_log_time[FORMATTED_TS_LEN];
 
 
 static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
-static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
 static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
 static void write_console(const char *line, int len);
 static void setup_formatted_log_time(void);
@@ -949,9 +948,10 @@ errbacktrace(void)
  * Compute backtrace data and add it to the supplied ErrorData.  num_skip
  * specifies how many inner frames to skip.  Use this to avoid showing the
  * internal backtrace support functions in the backtrace.  This requires that
- * this and related functions are not inlined.
+ * this and related functions are not inlined. If edata pointer is valid
+ * backtrace information will be set in edata.
  */
-static void
+void
 set_backtrace(ErrorData *edata, int num_skip)
 {
 	StringInfoData errtrace;
@@ -978,7 +978,19 @@ set_backtrace(ErrorData *edata, int num_skip)
 						   "backtrace generation is not supported by this installation");
 #endif
 
-	edata->backtrace = errtrace.data;
+	if (edata)
+		edata->backtrace = errtrace.data;
+	else
+	{
+		/*
+		 * LOG_SERVER_ONLY is used intentionally to make sure this information
+		 * is not sent to client based on client_min_messages. We don't want
+		 * to mess up a different session as pg_print_backtrace will be
+		 * sending SIGNAL to a different backend.
+		 */
+		elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
+		pfree(errtrace.data);
+	}
 }
 
 /*
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 381d9e548d..e49f5331a0 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -36,6 +36,7 @@ volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t IdleSessionTimeoutPending = false;
 volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile sig_atomic_t LogMemoryContextPending = false;
+volatile sig_atomic_t PrintBacktracePending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 49e8e59129..9f3e7b7503 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	202111091
+#define CATALOG_VERSION_NO	202111141
 
 #endif
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index d068d6532e..47872558b8 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11689,4 +11689,9 @@
   prorettype => 'bytea', proargtypes => 'pg_brin_minmax_multi_summary',
   prosrc => 'brin_minmax_multi_summary_send' },
 
+# function to get the backtrace of server process
+{ oid => '6105', descr => 'print backtrace of process',
+  proname => 'pg_print_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_print_backtrace' },
+
 ]
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 90a3016065..0da18326f5 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -94,6 +94,7 @@ extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
 extern PGDLLIMPORT volatile sig_atomic_t LogMemoryContextPending;
+extern PGDLLIMPORT volatile sig_atomic_t PrintBacktracePending;
 
 extern PGDLLIMPORT volatile sig_atomic_t CheckClientConnectionPending;
 extern PGDLLIMPORT volatile sig_atomic_t ClientConnectionLost;
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index eec186be2e..731c4fa970 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -35,6 +35,7 @@ typedef enum
 	PROCSIG_WALSND_INIT_STOPPING,	/* ask walsenders to prepare for shutdown  */
 	PROCSIG_BARRIER,			/* global barrier interrupt  */
 	PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
+	PROCSIG_PRINT_BACKTRACE,	/* ask backend to print the current backtrace */
 
 	/* Recovery conflict reasons */
 	PROCSIG_RECOVERY_CONFLICT_DATABASE,
@@ -70,7 +71,7 @@ extern int	SendProcSignal(pid_t pid, ProcSignalReason reason,
 extern uint64 EmitProcSignalBarrier(ProcSignalBarrierType type);
 extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
-
+extern void ProcessPrintBacktraceInterrupt(void);
 extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
 
 #endif							/* PROCSIGNAL_H */
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index f53607e12e..c63d25716a 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -453,4 +453,6 @@ extern void set_syslog_parameters(const char *ident, int facility);
  */
 extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
 
+extern pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
+
 #endif							/* ELOG_H */
diff --git a/src/test/modules/test_misc/t/002_print_backtrace_validation.pl b/src/test/modules/test_misc/t/002_print_backtrace_validation.pl
new file mode 100644
index 0000000000..4655b13a95
--- /dev/null
+++ b/src/test/modules/test_misc/t/002_print_backtrace_validation.pl
@@ -0,0 +1,73 @@
+use strict;
+use warnings;
+
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More tests => 2;
+use Time::HiRes qw(usleep);
+
+# Set up node with logging collector
+my $node = PostgreSQL::Test::Cluster->new('primary');
+$node->init();
+$node->append_conf(
+	'postgresql.conf', qq(
+logging_collector = on
+lc_messages = 'C'
+));
+
+$node->start();
+
+# Verify that log output gets to the file
+$node->psql('postgres', 'select pg_print_backtrace(pg_backend_pid())');
+
+# Might need to retry if logging collector process is slow...
+my $max_attempts = 180 * 10;
+
+my $current_logfiles;
+for (my $attempts = 0; $attempts < $max_attempts; $attempts++)
+{
+	eval {
+		$current_logfiles = slurp_file($node->data_dir . '/current_logfiles');
+	};
+	last unless $@;
+	usleep(100_000);
+}
+die $@ if $@;
+
+note "current_logfiles = $current_logfiles";
+
+like(
+	$current_logfiles,
+	qr|^stderr log/postgresql-.*log$|,
+	'current_logfiles is sane');
+
+my $lfname = $current_logfiles;
+$lfname =~ s/^stderr //;
+chomp $lfname;
+
+my $first_logfile;
+my $bt_occurence_count;
+
+# Verify that the backtraces of the processes are logged into logfile.
+for (my $attempts = 0; $attempts < $max_attempts; $attempts++)
+{
+	$first_logfile = $node->data_dir . '/' . $lfname;
+	chomp $first_logfile;
+	print "file is $first_logfile";
+	open my $fh, '<', $first_logfile
+	  or die "Could not open '$first_logfile' $!";
+	while (my $line = <$fh>)
+	{
+		chomp $line;
+		if ($line =~ m/current backtrace/)
+		{
+			$bt_occurence_count++;
+		}
+	}
+	last if $bt_occurence_count == 1;
+	usleep(100_000);
+}
+
+is($bt_occurence_count, 1, 'found expected backtrace in the log file');
+
+$node->stop();
diff --git a/src/test/regress/expected/misc_functions.out b/src/test/regress/expected/misc_functions.out
index 71d316cad3..c75cef00e7 100644
--- a/src/test/regress/expected/misc_functions.out
+++ b/src/test/regress/expected/misc_functions.out
@@ -176,6 +176,48 @@ REVOKE EXECUTE ON FUNCTION pg_log_backend_memory_contexts(integer)
   FROM regress_log_memory;
 DROP ROLE regress_log_memory;
 --
+-- pg_print_backtrace()
+--
+-- Backtrace are logged and they are not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_print_backtrace(pg_backend_pid());
+ pg_print_backtrace 
+--------------------
+ t
+(1 row)
+
+CREATE ROLE regress_print_backtrace;
+SELECT has_function_privilege('regress_print_backtrace',
+  'pg_print_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_print_backtrace(integer)
+  TO regress_print_backtrace;
+SELECT has_function_privilege('regress_print_backtrace',
+  'pg_print_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_print_backtrace;
+SELECT pg_print_backtrace(pg_backend_pid());
+ pg_print_backtrace 
+--------------------
+ t
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_print_backtrace(integer)
+  FROM regress_print_backtrace;
+DROP ROLE regress_print_backtrace;
+--
 -- Test some built-in SRFs
 --
 -- The outputs of these are variable, so we can't just print their results
diff --git a/src/test/regress/sql/misc_functions.sql b/src/test/regress/sql/misc_functions.sql
index 8c23874b3f..d63d2e2ddb 100644
--- a/src/test/regress/sql/misc_functions.sql
+++ b/src/test/regress/sql/misc_functions.sql
@@ -61,6 +61,37 @@ REVOKE EXECUTE ON FUNCTION pg_log_backend_memory_contexts(integer)
 
 DROP ROLE regress_log_memory;
 
+--
+-- pg_print_backtrace()
+--
+-- Backtrace are logged and they are not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+
+SELECT pg_print_backtrace(pg_backend_pid());
+
+CREATE ROLE regress_print_backtrace;
+
+SELECT has_function_privilege('regress_print_backtrace',
+  'pg_print_backtrace(integer)', 'EXECUTE'); -- no
+
+GRANT EXECUTE ON FUNCTION pg_print_backtrace(integer)
+  TO regress_print_backtrace;
+
+SELECT has_function_privilege('regress_print_backtrace',
+  'pg_print_backtrace(integer)', 'EXECUTE'); -- yes
+
+SET ROLE regress_print_backtrace;
+SELECT pg_print_backtrace(pg_backend_pid());
+RESET ROLE;
+
+REVOKE EXECUTE ON FUNCTION pg_print_backtrace(integer)
+  FROM regress_print_backtrace;
+
+DROP ROLE regress_print_backtrace;
+
 --
 -- Test some built-in SRFs
 --
-- 
2.30.2

v11-0002-pg_print_backtrace-support-for-printing-backtrac.patchtext/x-patch; charset=US-ASCII; name=v11-0002-pg_print_backtrace-support-for-printing-backtrac.patchDownload
From 49a62caab6741dd5d5f73ed3d273051b44c3145b Mon Sep 17 00:00:00 2001
From: Vignesh C <vignesh21@gmail.com>
Date: Sun, 14 Nov 2021 20:01:45 +0530
Subject: [PATCH v11 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         |  6 ++--
 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, 83 insertions(+), 23 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 620e6ee54a..b38e24cdb1 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25354,19 +25354,24 @@ SELECT collation for ('foo' COLLATE "de_DE");
         <returnvalue>boolean</returnvalue>
        </para>
        <para>
-        Requests to log the backtrace of the backend 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
+        Requests to log the backtrace of the backend 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..7cc43e86c2 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -632,9 +632,9 @@ 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, which is
+ * enough because the target process for logging of backtrace is a backend.
  */
 void
 ProcessPrintBacktraceInterrupt(void)
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index df942d6015..e59a010279 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

#71vignesh C
vignesh21@gmail.com
In reply to: Bharath Rupireddy (#69)
Re: Printing backtrace of postgres processes

On Fri, Nov 12, 2021 at 6:11 PM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Fri, Nov 12, 2021 at 5:15 PM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Thu, Nov 11, 2021 at 12:14 PM vignesh C <vignesh21@gmail.com> wrote:

Thanks for the comments, the attached v10 patch has the fixes for the same.

Thanks for the patches. Here are some comments:

1) In the docs, let's have the similar description of
pg_log_backend_memory_contexts for pg_print_backtrace, just for the
continuity in the users readability.

2) I don't know how the <screen> part looks like in the Server
Signaling Functions table. I think here you can just say, it will emit
a warning and return false if not supported by the installation. And
you can give the <screen> part after the description where you are
showing a sample backtrace.

+        capture backtrace. If not available, the function will return false
+        and a warning is issued, for example:
+<screen>
+WARNING:  backtrace generation is not supported by this installation
+ pg_print_backtrace
+--------------------
+ f
+</screen>
+       </para></entry>
+      </row>

3) Replace '!' with '.'.
+ * Note: this is called within a signal handler! All we can do is set

4) It is not only the next CFI but also the process specific interrupt
handlers (in your 0002 patch) right?
+ * a flag that will cause the next CHECK_FOR_INTERRUPTS to invoke

5) I think you need to update CATALOG_VERSION_NO, mostly the committer
will take care of it but just in case.

6) Be consistent with casing "Verify" and "Might"
+# Verify that log output gets to the file
+# might need to retry if logging collector process is slow...

Few more comments:

7) Do we need TAP tests for this function? I think it is sufficient to
test the function in misc_functions.sql, please remove
002_print_backtrace_validation.pl. Note that we don't do similar TAP
testing for pg_log_backend_memory_contexts as well.

I felt let's keep this test case, all the other tests just check if it
returns true or false, it does not checks for the contents in the
logfile. This is the only test which checks the logfile.

8) I hope you have manually tested the pg_print_backtrace for the
startup process and other auxiliary processes.

Yes, I have checked them manually.

9) I think we can have a similar description (to the patch [1]). with
links to each process definition in the glossary so that it will be
easier for the users to follow the links and know what each process
is. Especially, I didn't like the 0002 mentioned about the
BackendPidGetProc or AuxiliaryPidGetProc as these are internal to the
server and the users don't care about them.

- * 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.
*/

Here's what I have written in the other patch [1], if okay please use this:

+        Requests to log memory contexts of the backend 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.
+        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),

I had mentioned BackendPidGetProc or AuxiliaryPidGetProc as comments
in the function, but I have not changed that. I have changed the
documentation similar to your patch.

Thanks for the comments, v11 patch attached at [1]/messages/by-id/CALDaNm3BYGOG3-PQvYbWkB=G3h1KYJ8CO8UYbzfECH4DYGMGqA@mail.gmail.com has the changes for the same.
[1]: /messages/by-id/CALDaNm3BYGOG3-PQvYbWkB=G3h1KYJ8CO8UYbzfECH4DYGMGqA@mail.gmail.com

Regards,
Vignesh

#72Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: vignesh C (#71)
Re: Printing backtrace of postgres processes

On Sun, Nov 14, 2021 at 8:49 PM vignesh C <vignesh21@gmail.com> wrote:

7) Do we need TAP tests for this function? I think it is sufficient to
test the function in misc_functions.sql, please remove
002_print_backtrace_validation.pl. Note that we don't do similar TAP
testing for pg_log_backend_memory_contexts as well.

I felt let's keep this test case, all the other tests just check if it
returns true or false, it does not checks for the contents in the
logfile. This is the only test which checks the logfile.

I still don't agree to have test cases within a new file
002_print_backtrace_validation.pl. I feel this test case doesn't add
value because the code coverage is done by .sql test cases and .pl
just ensures the backtrace appears in the server logs. I don't think
we ever need a new file for this purpose. If this is the case, then
there are other functions like pg_log_backend_memory_contexts or
pg_log_query_plan (in progress thread) might add the same test files
for the same reasons which make the TAP tests i.e. "make check-world"
to take longer times. Moreover, pg_log_backend_memory_contexts has
been committed without having a TAP test case.

I think we can remove it.

Few more comments on v11:
1) I think we can improve here by adding a link to "backend" as well,
I will modify it in the other thread.
+        Requests to log the backtrace of the backend or the
+        <glossterm linkend="glossary-wal-sender">WAL sender</glossterm> or
Something like:
+        Requests to log the backtrace of the <glossterm
linkend="glossary-backend">backend</glossterm> or the
+        <glossterm linkend="glossary-wal-sender">WAL sender</glossterm> or
2) I think "which is enough because the target process for logging of
backtrace is a backend" isn't valid anymore with 0002, righit? Please
remove it.
+ * to call this function if we see PrintBacktracePending set. It is called from
+ * CHECK_FOR_INTERRUPTS() or from process specific interrupt handlers, which is
+ * enough because the target process for logging of backtrace is a backend.

Thanks for the comments, v11 patch attached at [1] has the changes for the same.

The v11 patches mostly look good to me except the above comments.

Regards,
Bharath Rupireddy.

#73vignesh C
vignesh21@gmail.com
In reply to: Bharath Rupireddy (#72)
2 attachment(s)
Re: Printing backtrace of postgres processes

On Mon, Nov 15, 2021 at 7:37 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Sun, Nov 14, 2021 at 8:49 PM vignesh C <vignesh21@gmail.com> wrote:

7) Do we need TAP tests for this function? I think it is sufficient to
test the function in misc_functions.sql, please remove
002_print_backtrace_validation.pl. Note that we don't do similar TAP
testing for pg_log_backend_memory_contexts as well.

I felt let's keep this test case, all the other tests just check if it
returns true or false, it does not checks for the contents in the
logfile. This is the only test which checks the logfile.

I still don't agree to have test cases within a new file
002_print_backtrace_validation.pl. I feel this test case doesn't add
value because the code coverage is done by .sql test cases and .pl
just ensures the backtrace appears in the server logs. I don't think
we ever need a new file for this purpose. If this is the case, then
there are other functions like pg_log_backend_memory_contexts or
pg_log_query_plan (in progress thread) might add the same test files
for the same reasons which make the TAP tests i.e. "make check-world"
to take longer times. Moreover, pg_log_backend_memory_contexts has
been committed without having a TAP test case.

I think we can remove it.

Removed

Few more comments on v11:
1) I think we can improve here by adding a link to "backend" as well,
I will modify it in the other thread.
+        Requests to log the backtrace of the backend or the
+        <glossterm linkend="glossary-wal-sender">WAL sender</glossterm> or
Something like:
+        Requests to log the backtrace of the <glossterm
linkend="glossary-backend">backend</glossterm> or the
+        <glossterm linkend="glossary-wal-sender">WAL sender</glossterm> or

Modified

2) I think "which is enough because the target process for logging of
backtrace is a backend" isn't valid anymore with 0002, righit? Please
remove it.
+ * to call this function if we see PrintBacktracePending set. It is called from
+ * CHECK_FOR_INTERRUPTS() or from process specific interrupt handlers, which is
+ * enough because the target process for logging of backtrace is a backend.

Thanks for the comments, v11 patch attached at [1] has the changes for the same.

Modified

Thanks for the comments, the attached v12 patch has the changes for the same.

Regards,
Vignesh

Attachments:

v12-0001-Print-backtrace-of-specified-postgres-process.patchtext/x-patch; charset=US-ASCII; name=v12-0001-Print-backtrace-of-specified-postgres-process.patchDownload
From c34d0114833219ef7b1b2d4283522ce9d4e6ffe3 Mon Sep 17 00:00:00 2001
From: Vigneshwaran C <vignesh21@gmail.com>
Date: Tue, 9 Nov 2021 16:28:03 +0530
Subject: [PATCH v12 1/2] Print backtrace of specified postgres process.

The idea here is to implement & expose pg_print_backtrace function, internally
what this function does is, the connected backend will send SIGUSR1 signal by
setting PROCSIG_PRINT_BACKTRACE to postgres backend whose pid matches the
specified process id. Once the backend process receives this signal it will
print the backtrace of the process to the log file based on the logging
configuration, if logging is disabled backtrace will be printed to the
console where postmaster was started.
---
 doc/src/sgml/func.sgml                       | 83 ++++++++++++++++++++
 src/backend/catalog/system_functions.sql     |  2 +
 src/backend/postmaster/autovacuum.c          |  4 +
 src/backend/storage/ipc/procsignal.c         | 41 ++++++++++
 src/backend/storage/ipc/signalfuncs.c        | 48 +++++++++++
 src/backend/tcop/postgres.c                  |  4 +
 src/backend/utils/error/elog.c               | 20 ++++-
 src/backend/utils/init/globals.c             |  1 +
 src/include/catalog/catversion.h             |  2 +-
 src/include/catalog/pg_proc.dat              |  5 ++
 src/include/miscadmin.h                      |  1 +
 src/include/storage/procsignal.h             |  3 +-
 src/include/utils/elog.h                     |  2 +
 src/test/regress/expected/misc_functions.out | 42 ++++++++++
 src/test/regress/sql/misc_functions.sql      | 31 ++++++++
 15 files changed, 283 insertions(+), 6 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 24447c0017..8d147825eb 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25345,6 +25345,32 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_print_backtrace</primary>
+        </indexterm>
+        <function>pg_print_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </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
+        <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.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -25458,6 +25484,63 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_print_backtrace</function> can be used to log the backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_print_backtrace(pg_backend_pid());
+ pg_print_backtrace
+--------------------
+ t
+(1 row)
+</programlisting>
+The backtrace will be logged to the log file if logging is enabled, if logging
+is disabled backtrace will be logged to the console where the postmaster was
+started. For example:
+<screen>
+2021-01-27 11:33:50.247 IST [111735] LOG:  current backtrace:
+        postgres: postgresdba postgres [local] SELECT(set_backtrace+0x38) [0xae06c5]
+        postgres: postgresdba postgres [local] SELECT(ProcessInterrupts+0x788) [0x950c34]
+        postgres: postgresdba postgres [local] SELECT() [0x761e89]
+        postgres: postgresdba postgres [local] SELECT() [0x71bbda]
+        postgres: postgresdba postgres [local] SELECT() [0x71e380]
+        postgres: postgresdba postgres [local] SELECT(standard_ExecutorRun+0x1d6) [0x71c1fe]
+        postgres: postgresdba postgres [local] SELECT(ExecutorRun+0x55) [0x71c026]
+        postgres: postgresdba postgres [local] SELECT() [0x953fc5]
+        postgres: postgresdba postgres [local] SELECT(PortalRun+0x262) [0x953c7e]
+        postgres: postgresdba postgres [local] SELECT() [0x94db78]
+        postgres: postgresdba postgres [local] SELECT(PostgresMain+0x7d7) [0x951e72]
+        postgres: postgresdba postgres [local] SELECT() [0x896b2f]
+        postgres: postgresdba postgres [local] SELECT() [0x8964b5]
+        postgres: postgresdba postgres [local] SELECT() [0x892a79]
+        postgres: postgresdba postgres [local] SELECT(PostmasterMain+0x116b) [0x892350]
+        postgres: postgresdba postgres [local] SELECT() [0x795f72]
+        /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2107bbd505]
+        postgres: postgresdba postgres [local] SELECT() [0x4842a9]
+</screen>
+    You can get the file name and line number by using gdb/addr2line in
+    linux platforms, as a prerequisite users must ensure gdb/addr2line is
+    already installed:
+<programlisting>
+1)  "info line *address" from gdb on postgres executable. For example:
+gdb ./postgres
+(gdb) info line *0x71c25d
+Line 378 of "execMain.c" starts at address 0x71c25d <literal>&lt;</literal>standard_ExecutorRun+470<literal>&gt;</literal> and ends at 0x71c263 <literal>&lt;</literal>standard_ExecutorRun+476<literal>&gt;</literal>.
+OR 
+2) Using "addr2line -e postgres address", For example:
+addr2line -e ./postgres 0x71c25d
+/home/postgresdba/src/backend/executor/execMain.c:378
+</programlisting>
+    If PostgreSQL was not built with the ability to capture backtrace. The
+    function will emit a warning and return false, for example:
+<screen>
+WARNING:  backtrace generation is not supported by this installation
+ pg_print_backtrace 
+--------------------
+ f
+</screen>
+   </para>
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 54c93b16c4..072c5952fa 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -701,6 +701,8 @@ REVOKE EXECUTE ON FUNCTION pg_ls_dir(text,boolean,boolean) FROM public;
 
 REVOKE EXECUTE ON FUNCTION pg_log_backend_memory_contexts(integer) FROM PUBLIC;
 
+REVOKE EXECUTE ON FUNCTION pg_print_backtrace(integer) FROM PUBLIC;
+
 --
 -- We also set up some things as accessible to standard roles.
 --
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 96332320a7..f1da744d0f 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -840,6 +840,10 @@ HandleAutoVacLauncherInterrupts(void)
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
 
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+		ProcessPrintBacktraceInterrupt();
+
 	/* Process sinval catchup interrupts that happened while sleeping */
 	ProcessCatchupInterrupt();
 }
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index 6e69398cdd..80fa6d4990 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -612,6 +612,44 @@ ProcessBarrierPlaceholder(void)
 	return true;
 }
 
+/*
+ * HandlePrintBacktraceInterrupt - Handle receipt of an interrupt indicating a
+ * print backtrace.
+ *
+ * All the actual work is deferred to ProcessPrintBacktraceInterrupt(),
+ * because we cannot safely emit a log message inside the signal handler.
+ */
+static void
+HandlePrintBacktraceInterrupt(void)
+{
+	InterruptPending = true;
+	PrintBacktracePending = true;
+	/* latch will be set by procsignal_sigusr1_handler */
+}
+
+/*
+ * ProcessPrintBacktraceInterrupt - Perform logging of backtrace of this
+ * 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.
+ */
+void
+ProcessPrintBacktraceInterrupt(void)
+{
+	PrintBacktracePending = false;
+
+	/*
+	 * Use LOG_SERVER_ONLY to prevent this message from being sent to the
+	 * connected client.
+	 */
+	ereport(LOG_SERVER_ONLY,
+			(errmsg("logging backtrace of PID %d", MyProcPid)));
+	set_backtrace(NULL, 0);
+}
+
 /*
  * CheckProcSignal - check to see if a particular reason has been
  * signaled, and clear the signal flag.  Should be called after receiving
@@ -661,6 +699,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_LOG_MEMORY_CONTEXT))
 		HandleLogMemoryContextInterrupt();
 
+	if (CheckProcSignal(PROCSIG_PRINT_BACKTRACE))
+		HandlePrintBacktraceInterrupt();
+
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
 
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index de69d60e79..df942d6015 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -23,6 +23,7 @@
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
+#include "tcop/tcopprot.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 
@@ -298,3 +299,50 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_print_backtrace - print backtrace of backend process.
+ *
+ * By default, only superusers can print backtrace. Additional roles can be
+ * permitted with GRANT.
+ */
+Datum
+pg_print_backtrace(PG_FUNCTION_ARGS)
+{
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	int			pid = PG_GETARG_INT32(0);
+	PGPROC	   *proc;
+
+	/*
+	 * 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.
+	 */
+	proc = BackendPidGetProc(pid);
+	if (proc == NULL)
+	{
+		ereport(WARNING,
+				(errmsg("PID %d is not a PostgreSQL server process", pid)));
+		PG_RETURN_BOOL(false);
+	}
+
+	/*
+	 * 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))
+		PG_RETURN_BOOL(true);
+	else
+		ereport(WARNING,
+				(errmsg("could not send signal to process %d: %m", pid))); /* return false below */
+#else
+	ereport(WARNING,
+			(errmsg("backtrace generation is not supported by this installation")));
+#endif
+
+	PG_RETURN_BOOL(false);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 0775abe35d..624b3f8453 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3366,6 +3366,10 @@ ProcessInterrupts(void)
 
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+		ProcessPrintBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index f33729513a..05fe1bb5e7 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -172,7 +172,6 @@ static char formatted_log_time[FORMATTED_TS_LEN];
 
 
 static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
-static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
 static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
 static void write_console(const char *line, int len);
 static void setup_formatted_log_time(void);
@@ -949,9 +948,10 @@ errbacktrace(void)
  * Compute backtrace data and add it to the supplied ErrorData.  num_skip
  * specifies how many inner frames to skip.  Use this to avoid showing the
  * internal backtrace support functions in the backtrace.  This requires that
- * this and related functions are not inlined.
+ * this and related functions are not inlined. If edata pointer is valid
+ * backtrace information will be set in edata.
  */
-static void
+void
 set_backtrace(ErrorData *edata, int num_skip)
 {
 	StringInfoData errtrace;
@@ -978,7 +978,19 @@ set_backtrace(ErrorData *edata, int num_skip)
 						   "backtrace generation is not supported by this installation");
 #endif
 
-	edata->backtrace = errtrace.data;
+	if (edata)
+		edata->backtrace = errtrace.data;
+	else
+	{
+		/*
+		 * LOG_SERVER_ONLY is used intentionally to make sure this information
+		 * is not sent to client based on client_min_messages. We don't want
+		 * to mess up a different session as pg_print_backtrace will be
+		 * sending SIGNAL to a different backend.
+		 */
+		elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
+		pfree(errtrace.data);
+	}
 }
 
 /*
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 381d9e548d..e49f5331a0 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -36,6 +36,7 @@ volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t IdleSessionTimeoutPending = false;
 volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile sig_atomic_t LogMemoryContextPending = false;
+volatile sig_atomic_t PrintBacktracePending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 49e8e59129..9f3e7b7503 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	202111091
+#define CATALOG_VERSION_NO	202111141
 
 #endif
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index d068d6532e..47872558b8 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11689,4 +11689,9 @@
   prorettype => 'bytea', proargtypes => 'pg_brin_minmax_multi_summary',
   prosrc => 'brin_minmax_multi_summary_send' },
 
+# function to get the backtrace of server process
+{ oid => '6105', descr => 'print backtrace of process',
+  proname => 'pg_print_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_print_backtrace' },
+
 ]
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 90a3016065..0da18326f5 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -94,6 +94,7 @@ extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
 extern PGDLLIMPORT volatile sig_atomic_t LogMemoryContextPending;
+extern PGDLLIMPORT volatile sig_atomic_t PrintBacktracePending;
 
 extern PGDLLIMPORT volatile sig_atomic_t CheckClientConnectionPending;
 extern PGDLLIMPORT volatile sig_atomic_t ClientConnectionLost;
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index eec186be2e..731c4fa970 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -35,6 +35,7 @@ typedef enum
 	PROCSIG_WALSND_INIT_STOPPING,	/* ask walsenders to prepare for shutdown  */
 	PROCSIG_BARRIER,			/* global barrier interrupt  */
 	PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
+	PROCSIG_PRINT_BACKTRACE,	/* ask backend to print the current backtrace */
 
 	/* Recovery conflict reasons */
 	PROCSIG_RECOVERY_CONFLICT_DATABASE,
@@ -70,7 +71,7 @@ extern int	SendProcSignal(pid_t pid, ProcSignalReason reason,
 extern uint64 EmitProcSignalBarrier(ProcSignalBarrierType type);
 extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
-
+extern void ProcessPrintBacktraceInterrupt(void);
 extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
 
 #endif							/* PROCSIGNAL_H */
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index f53607e12e..c63d25716a 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -453,4 +453,6 @@ extern void set_syslog_parameters(const char *ident, int facility);
  */
 extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
 
+extern pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
+
 #endif							/* ELOG_H */
diff --git a/src/test/regress/expected/misc_functions.out b/src/test/regress/expected/misc_functions.out
index 71d316cad3..c75cef00e7 100644
--- a/src/test/regress/expected/misc_functions.out
+++ b/src/test/regress/expected/misc_functions.out
@@ -176,6 +176,48 @@ REVOKE EXECUTE ON FUNCTION pg_log_backend_memory_contexts(integer)
   FROM regress_log_memory;
 DROP ROLE regress_log_memory;
 --
+-- pg_print_backtrace()
+--
+-- Backtrace are logged and they are not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_print_backtrace(pg_backend_pid());
+ pg_print_backtrace 
+--------------------
+ t
+(1 row)
+
+CREATE ROLE regress_print_backtrace;
+SELECT has_function_privilege('regress_print_backtrace',
+  'pg_print_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_print_backtrace(integer)
+  TO regress_print_backtrace;
+SELECT has_function_privilege('regress_print_backtrace',
+  'pg_print_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_print_backtrace;
+SELECT pg_print_backtrace(pg_backend_pid());
+ pg_print_backtrace 
+--------------------
+ t
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_print_backtrace(integer)
+  FROM regress_print_backtrace;
+DROP ROLE regress_print_backtrace;
+--
 -- Test some built-in SRFs
 --
 -- The outputs of these are variable, so we can't just print their results
diff --git a/src/test/regress/sql/misc_functions.sql b/src/test/regress/sql/misc_functions.sql
index 8c23874b3f..d63d2e2ddb 100644
--- a/src/test/regress/sql/misc_functions.sql
+++ b/src/test/regress/sql/misc_functions.sql
@@ -61,6 +61,37 @@ REVOKE EXECUTE ON FUNCTION pg_log_backend_memory_contexts(integer)
 
 DROP ROLE regress_log_memory;
 
+--
+-- pg_print_backtrace()
+--
+-- Backtrace are logged and they are not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+
+SELECT pg_print_backtrace(pg_backend_pid());
+
+CREATE ROLE regress_print_backtrace;
+
+SELECT has_function_privilege('regress_print_backtrace',
+  'pg_print_backtrace(integer)', 'EXECUTE'); -- no
+
+GRANT EXECUTE ON FUNCTION pg_print_backtrace(integer)
+  TO regress_print_backtrace;
+
+SELECT has_function_privilege('regress_print_backtrace',
+  'pg_print_backtrace(integer)', 'EXECUTE'); -- yes
+
+SET ROLE regress_print_backtrace;
+SELECT pg_print_backtrace(pg_backend_pid());
+RESET ROLE;
+
+REVOKE EXECUTE ON FUNCTION pg_print_backtrace(integer)
+  FROM regress_print_backtrace;
+
+DROP ROLE regress_print_backtrace;
+
 --
 -- Test some built-in SRFs
 --
-- 
2.30.2

v12-0002-pg_print_backtrace-support-for-printing-backtrac.patchtext/x-patch; charset=US-ASCII; name=v12-0002-pg_print_backtrace-support-for-printing-backtrac.patchDownload
From dda8560aa4034dee9e5dde7177ad265f58b3feb7 Mon Sep 17 00:00:00 2001
From: Vigneshwaran C <vignesh21@gmail.com>
Date: Mon, 15 Nov 2021 09:28:47 +0530
Subject: [PATCH v12 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 8d147825eb..8098246897 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 df942d6015..e59a010279 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

#74Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: vignesh C (#73)
Re: Printing backtrace of postgres processes

On Mon, Nov 15, 2021 at 10:34 AM vignesh C <vignesh21@gmail.com> wrote:

2) I think "which is enough because the target process for logging of
backtrace is a backend" isn't valid anymore with 0002, righit? Please
remove it.
+ * to call this function if we see PrintBacktracePending set. It is called from
+ * CHECK_FOR_INTERRUPTS() or from process specific interrupt handlers, which is
+ * enough because the target process for logging of backtrace is a backend.

Thanks for the comments, v11 patch attached at [1] has the changes for the same.

Modified

I don't see the above change in v12. Am I missing something? I still
see the comment "It is called from CHECK_FOR_INTERRUPTS(), which is
enough because the target process for logging of backtrace is a
backend.".

+ * ProcessPrintBacktraceInterrupt - Perform logging of backtrace of this
+ * 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.
+ */
+void
+ProcessPrintBacktraceInterrupt(void)

Regards,
Bharath Rupireddy.

#75Dilip Kumar
dilipbalaut@gmail.com
In reply to: vignesh C (#73)
Re: Printing backtrace of postgres processes

On Mon, Nov 15, 2021 at 10:34 AM vignesh C <vignesh21@gmail.com> wrote:

Thanks for the comments, the attached v12 patch has the changes for the same.

I have reviewed this patch and have some comments on v12-0001,

1.
+        This feature is not supported for the postmaster, logger, checkpointer,
+        walwriter, background writer or statistics collector process. This

Comment says it is not supported for postmaster, logger, checkpointer
etc, but I just tried and it is working for checkpointer and walwriter
processes, can you explain in comments why do we not want to support
for these processes? or the comment is old and now we are supporting
for some of these processes.

2.
postgres[64154]=# select pg_print_backtrace(64136);
WARNING: 01000: PID 64136 is not a PostgreSQL server process
LOCATION: pg_print_backtrace, signalfuncs.c:335
pg_print_backtrace
--------------------
f

For postmaster I am getting this WARNING "PID 64136 is not a
PostgreSQL server process", even if we don't want to support this
process I don't think this message is good.

3.
I was looking into the patch, I tried to print the backtrace using GDB
as well as using this function, I have complied in debug mode, I can
see the backtrace printed
by GDB is more detailed than printed by this API, I understand we can
find out the function name from address, but can't we print the
detailed call stack with all function names at least when debug
symbols are available?

Using GDB

#0 0x00007fa26c527e93 in __epoll_wait_nocancel () from
#1 0x0000000000947a61 in WaitEventSetWaitBlock (set=0x2
#2 0x00000000009478f9 in WaitEventSetWait (set=0x2f0111
#3 0x00000000007a6cef in secure_read (port=0x2f26800, p
#4 0x00000000007b0bd6 in pq_recvbuf () at pqcomm.c:957
#5 0x00000000007b0c86 in pq_getbyte () at pqcomm.c:1000
#6 0x0000000000978c13 in SocketBackend (inBuf=0x7ffea99
#7 0x0000000000978e37 in ReadCommand (inBuf=0x7ffea9937
#8 0x000000000097dca5 in PostgresMain (dbname=0x2f2ef40
....

Using pg_print_backtrace
postgres: dilipkumar postgres [local]
SELECT(set_backtrace+0x38) [0xb118ff]
postgres: dilipkumar postgres [local]
SELECT(ProcessPrintBacktraceInterrupt+0x5b) [0x94fe42]
postgres: dilipkumar postgres [local]
SELECT(ProcessInterrupts+0x7d9) [0x97cb2a]
postgres: dilipkumar postgres [local] SELECT() [0x78143c]
postgres: dilipkumar postgres [local] SELECT() [0x736731]
postgres: dilipkumar postgres [local] SELECT() [0x738f5f]
postgres: dilipkumar postgres [local]
SELECT(standard_ExecutorRun+0x1d6) [0x736d94]
postgres: dilipkumar postgres [local] SELECT(ExecutorRun+0x55)
[0x736bbc]
postgres: dilipkumar postgres [local] SELECT() [0x97ff0c]
postgres: dilipkumar postgres [local] SELECT(PortalRun+0x268) [0x97fbbf]
postgres: dilipkumar postgres [local] SELECT() [0x9798dc]
postgres: dilipkumar postgres [local]
SELECT(PostgresMain+0x6ca) [0x97dda7]

4.
+WARNING:  backtrace generation is not supported by this installation
+ pg_print_backtrace
+--------------------
+ f

Should we give some hints on how to enable that?

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#76Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: Dilip Kumar (#75)
Re: Printing backtrace of postgres processes

On Mon, Nov 15, 2021 at 11:37 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Mon, Nov 15, 2021 at 10:34 AM vignesh C <vignesh21@gmail.com> wrote:

Thanks for the comments, the attached v12 patch has the changes for the same.

I have reviewed this patch and have some comments on v12-0001,

1.
+        This feature is not supported for the postmaster, logger, checkpointer,
+        walwriter, background writer or statistics collector process. This

Comment says it is not supported for postmaster, logger, checkpointer
etc, but I just tried and it is working for checkpointer and walwriter
processes, can you explain in comments why do we not want to support
for these processes? or the comment is old and now we are supporting
for some of these processes.

Please see the v12-0002 which will have the description modified.

2.
postgres[64154]=# select pg_print_backtrace(64136);
WARNING: 01000: PID 64136 is not a PostgreSQL server process
LOCATION: pg_print_backtrace, signalfuncs.c:335
pg_print_backtrace
--------------------
f

For postmaster I am getting this WARNING "PID 64136 is not a
PostgreSQL server process", even if we don't want to support this
process I don't think this message is good.

This is a generic message that is coming from pg_signal_backend, not
related to Vignesh's patch. I agree with you that emitting a "not
postgres server process" for the postmaster process which is the main
"postgres process" doesn't sound sensible. Please see there's already
a thread [1]/messages/by-id/CALj2ACW7Rr-R7mBcBQiXWPp=JV5chajjTdudLiF5YcpW-BmHhg@mail.gmail.com and see the v1 patch [2]/messages/by-id/CALj2ACUGxedgYk-5nO8D2EJV2YHXnoycp_oqYAxDXTODhWkmkg@mail.gmail.com for changing this message.
Please let me know if you want me to revive that stalled thread?

[1]: /messages/by-id/CALj2ACW7Rr-R7mBcBQiXWPp=JV5chajjTdudLiF5YcpW-BmHhg@mail.gmail.com
[2]: /messages/by-id/CALj2ACUGxedgYk-5nO8D2EJV2YHXnoycp_oqYAxDXTODhWkmkg@mail.gmail.com

Regards,
Bharath Rupireddy.

#77Dilip Kumar
dilipbalaut@gmail.com
In reply to: Bharath Rupireddy (#76)
Re: Printing backtrace of postgres processes

On Mon, Nov 15, 2021 at 11:53 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Mon, Nov 15, 2021 at 11:37 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Mon, Nov 15, 2021 at 10:34 AM vignesh C <vignesh21@gmail.com> wrote:

Thanks for the comments, the attached v12 patch has the changes for the same.

I have reviewed this patch and have some comments on v12-0001,

1.
+        This feature is not supported for the postmaster, logger, checkpointer,
+        walwriter, background writer or statistics collector process. This

Comment says it is not supported for postmaster, logger, checkpointer
etc, but I just tried and it is working for checkpointer and walwriter
processes, can you explain in comments why do we not want to support
for these processes? or the comment is old and now we are supporting
for some of these processes.

Please see the v12-0002 which will have the description modified.

Okay, now I see that.

2.
postgres[64154]=# select pg_print_backtrace(64136);
WARNING: 01000: PID 64136 is not a PostgreSQL server process
LOCATION: pg_print_backtrace, signalfuncs.c:335
pg_print_backtrace
--------------------
f

For postmaster I am getting this WARNING "PID 64136 is not a
PostgreSQL server process", even if we don't want to support this
process I don't think this message is good.

This is a generic message that is coming from pg_signal_backend, not
related to Vignesh's patch. I agree with you that emitting a "not
postgres server process" for the postmaster process which is the main
"postgres process" doesn't sound sensible. Please see there's already
a thread [1] and see the v1 patch [2] for changing this message.
Please let me know if you want me to revive that stalled thread?

[1] /messages/by-id/CALj2ACW7Rr-R7mBcBQiXWPp=JV5chajjTdudLiF5YcpW-BmHhg@mail.gmail.com
[2] /messages/by-id/CALj2ACUGxedgYk-5nO8D2EJV2YHXnoycp_oqYAxDXTODhWkmkg@mail.gmail.com

Hmm, yeah I think I like the idea posted in [1], however, I could not
open the link [2]

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#78vignesh C
vignesh21@gmail.com
In reply to: Bharath Rupireddy (#74)
Re: Printing backtrace of postgres processes

On Mon, Nov 15, 2021 at 11:00 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Mon, Nov 15, 2021 at 10:34 AM vignesh C <vignesh21@gmail.com> wrote:

2) I think "which is enough because the target process for logging of
backtrace is a backend" isn't valid anymore with 0002, righit? Please
remove it.
+ * to call this function if we see PrintBacktracePending set. It is called from
+ * CHECK_FOR_INTERRUPTS() or from process specific interrupt handlers, which is
+ * enough because the target process for logging of backtrace is a backend.

Thanks for the comments, v11 patch attached at [1] has the changes for the same.

Modified

I don't see the above change in v12. Am I missing something? I still
see the comment "It is called from CHECK_FOR_INTERRUPTS(), which is
enough because the target process for logging of backtrace is a
backend.".

This change is present in the 0002
(v12-0002-pg_print_backtrace-support-for-printing-backtrac.patch)
patch.

Regards,
Vignesh

#79Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: vignesh C (#78)
Re: Printing backtrace of postgres processes

On Mon, Nov 15, 2021 at 12:13 PM vignesh C <vignesh21@gmail.com> wrote:

On Mon, Nov 15, 2021 at 11:00 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Mon, Nov 15, 2021 at 10:34 AM vignesh C <vignesh21@gmail.com> wrote:

2) I think "which is enough because the target process for logging of
backtrace is a backend" isn't valid anymore with 0002, righit? Please
remove it.
+ * to call this function if we see PrintBacktracePending set. It is called from
+ * CHECK_FOR_INTERRUPTS() or from process specific interrupt handlers, which is
+ * enough because the target process for logging of backtrace is a backend.

Thanks for the comments, v11 patch attached at [1] has the changes for the same.

Modified

I don't see the above change in v12. Am I missing something? I still
see the comment "It is called from CHECK_FOR_INTERRUPTS(), which is
enough because the target process for logging of backtrace is a
backend.".

This change is present in the 0002
(v12-0002-pg_print_backtrace-support-for-printing-backtrac.patch)
patch.

Thanks. Yes, it was removed in 0002. I missed it.

Regards,
Bharath Rupireddy.

#80Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: Dilip Kumar (#77)
Re: Printing backtrace of postgres processes

On Mon, Nov 15, 2021 at 12:08 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:

2.
postgres[64154]=# select pg_print_backtrace(64136);
WARNING: 01000: PID 64136 is not a PostgreSQL server process
LOCATION: pg_print_backtrace, signalfuncs.c:335
pg_print_backtrace
--------------------
f

For postmaster I am getting this WARNING "PID 64136 is not a
PostgreSQL server process", even if we don't want to support this
process I don't think this message is good.

This is a generic message that is coming from pg_signal_backend, not
related to Vignesh's patch. I agree with you that emitting a "not
postgres server process" for the postmaster process which is the main
"postgres process" doesn't sound sensible. Please see there's already
a thread [1] and see the v1 patch [2] for changing this message.
Please let me know if you want me to revive that stalled thread?

[1] /messages/by-id/CALj2ACW7Rr-R7mBcBQiXWPp=JV5chajjTdudLiF5YcpW-BmHhg@mail.gmail.com
[2] /messages/by-id/CALj2ACUGxedgYk-5nO8D2EJV2YHXnoycp_oqYAxDXTODhWkmkg@mail.gmail.com

Hmm, yeah I think I like the idea posted in [1], however, I could not
open the link [2]

Thanks, I posted an updated v3 patch at [1]/messages/by-id/CALj2ACWS2bJRW-bSvcoL4FvS=kbQ8SSWXi=9RFUt7uqZvTQWWw@mail.gmail.com. Please review it there.

[1]: /messages/by-id/CALj2ACWS2bJRW-bSvcoL4FvS=kbQ8SSWXi=9RFUt7uqZvTQWWw@mail.gmail.com

Regards,
Bharath Rupireddy.

#81vignesh C
vignesh21@gmail.com
In reply to: Dilip Kumar (#75)
2 attachment(s)
Re: Printing backtrace of postgres processes

On Mon, Nov 15, 2021 at 11:37 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Mon, Nov 15, 2021 at 10:34 AM vignesh C <vignesh21@gmail.com> wrote:

Thanks for the comments, the attached v12 patch has the changes for the same.

I have reviewed this patch and have some comments on v12-0001,

1.
+        This feature is not supported for the postmaster, logger, checkpointer,
+        walwriter, background writer or statistics collector process. This

Comment says it is not supported for postmaster, logger, checkpointer
etc, but I just tried and it is working for checkpointer and walwriter
processes, can you explain in comments why do we not want to support
for these processes? or the comment is old and now we are supporting
for some of these processes.

2.
postgres[64154]=# select pg_print_backtrace(64136);
WARNING: 01000: PID 64136 is not a PostgreSQL server process
LOCATION: pg_print_backtrace, signalfuncs.c:335
pg_print_backtrace
--------------------
f

For postmaster I am getting this WARNING "PID 64136 is not a
PostgreSQL server process", even if we don't want to support this
process I don't think this message is good.

3.
I was looking into the patch, I tried to print the backtrace using GDB
as well as using this function, I have complied in debug mode, I can
see the backtrace printed
by GDB is more detailed than printed by this API, I understand we can
find out the function name from address, but can't we print the
detailed call stack with all function names at least when debug
symbols are available?

Using GDB

#0 0x00007fa26c527e93 in __epoll_wait_nocancel () from
#1 0x0000000000947a61 in WaitEventSetWaitBlock (set=0x2
#2 0x00000000009478f9 in WaitEventSetWait (set=0x2f0111
#3 0x00000000007a6cef in secure_read (port=0x2f26800, p
#4 0x00000000007b0bd6 in pq_recvbuf () at pqcomm.c:957
#5 0x00000000007b0c86 in pq_getbyte () at pqcomm.c:1000
#6 0x0000000000978c13 in SocketBackend (inBuf=0x7ffea99
#7 0x0000000000978e37 in ReadCommand (inBuf=0x7ffea9937
#8 0x000000000097dca5 in PostgresMain (dbname=0x2f2ef40
....

Using pg_print_backtrace
postgres: dilipkumar postgres [local]
SELECT(set_backtrace+0x38) [0xb118ff]
postgres: dilipkumar postgres [local]
SELECT(ProcessPrintBacktraceInterrupt+0x5b) [0x94fe42]
postgres: dilipkumar postgres [local]
SELECT(ProcessInterrupts+0x7d9) [0x97cb2a]
postgres: dilipkumar postgres [local] SELECT() [0x78143c]
postgres: dilipkumar postgres [local] SELECT() [0x736731]
postgres: dilipkumar postgres [local] SELECT() [0x738f5f]
postgres: dilipkumar postgres [local]
SELECT(standard_ExecutorRun+0x1d6) [0x736d94]
postgres: dilipkumar postgres [local] SELECT(ExecutorRun+0x55)
[0x736bbc]
postgres: dilipkumar postgres [local] SELECT() [0x97ff0c]
postgres: dilipkumar postgres [local] SELECT(PortalRun+0x268) [0x97fbbf]
postgres: dilipkumar postgres [local] SELECT() [0x9798dc]
postgres: dilipkumar postgres [local]
SELECT(PostgresMain+0x6ca) [0x97dda7]

I did not find any API's with such an implementation. We have used
backtrace and backtrace_symbols to print the address. Same thing is
used to add error backtrace and print backtrace for assert failure,
see errbacktrace and ExceptionalCondition. I felt this is ok.

4.
+WARNING:  backtrace generation is not supported by this installation
+ pg_print_backtrace
+--------------------
+ f

Should we give some hints on how to enable that?

Modified to include a hint message.
The Attached v13 patch has the fix for it.

Regards,
Vignesh

Attachments:

v13-0002-pg_print_backtrace-support-for-printing-backtrac.patchtext/x-patch; charset=US-ASCII; name=v13-0002-pg_print_backtrace-support-for-printing-backtrac.patchDownload
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

v13-0001-Print-backtrace-of-specified-postgres-process.patchtext/x-patch; charset=US-ASCII; name=v13-0001-Print-backtrace-of-specified-postgres-process.patchDownload
From 89f36d323c04c45dde450e78deeb76a726e146b5 Mon Sep 17 00:00:00 2001
From: Vignesh C <vignesh21@gmail.com>
Date: Tue, 9 Nov 2021 16:28:03 +0530
Subject: [PATCH v13 1/2] Print backtrace of specified postgres process.

The idea here is to implement & expose pg_print_backtrace function, internally
what this function does is, the connected backend will send SIGUSR1 signal by
setting PROCSIG_PRINT_BACKTRACE to postgres backend whose pid matches the
specified process id. Once the backend process receives this signal it will
print the backtrace of the process to the log file based on the logging
configuration, if logging is disabled backtrace will be printed to the
console where postmaster was started.
---
 doc/src/sgml/func.sgml                       | 85 ++++++++++++++++++++
 src/backend/catalog/system_functions.sql     |  2 +
 src/backend/postmaster/autovacuum.c          |  4 +
 src/backend/storage/ipc/procsignal.c         | 41 ++++++++++
 src/backend/storage/ipc/signalfuncs.c        | 49 +++++++++++
 src/backend/tcop/postgres.c                  |  4 +
 src/backend/utils/error/elog.c               | 20 ++++-
 src/backend/utils/init/globals.c             |  1 +
 src/include/catalog/catversion.h             |  2 +-
 src/include/catalog/pg_proc.dat              |  5 ++
 src/include/miscadmin.h                      |  1 +
 src/include/storage/procsignal.h             |  3 +-
 src/include/utils/elog.h                     |  2 +
 src/test/regress/expected/misc_functions.out | 42 ++++++++++
 src/test/regress/sql/misc_functions.sql      | 31 +++++++
 15 files changed, 286 insertions(+), 6 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 24447c0017..8a30ee2eee 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25345,6 +25345,32 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_print_backtrace</primary>
+        </indexterm>
+        <function>pg_print_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </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
+        <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.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -25458,6 +25484,65 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_print_backtrace</function> can be used to log the backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_print_backtrace(pg_backend_pid());
+ pg_print_backtrace
+--------------------
+ t
+(1 row)
+</programlisting>
+The backtrace will be logged to the log file if logging is enabled, if logging
+is disabled backtrace will be logged to the console where the postmaster was
+started. For example:
+<screen>
+2021-01-27 11:33:50.247 IST [111735] LOG:  current backtrace:
+        postgres: postgresdba postgres [local] SELECT(set_backtrace+0x38) [0xae06c5]
+        postgres: postgresdba postgres [local] SELECT(ProcessInterrupts+0x788) [0x950c34]
+        postgres: postgresdba postgres [local] SELECT() [0x761e89]
+        postgres: postgresdba postgres [local] SELECT() [0x71bbda]
+        postgres: postgresdba postgres [local] SELECT() [0x71e380]
+        postgres: postgresdba postgres [local] SELECT(standard_ExecutorRun+0x1d6) [0x71c1fe]
+        postgres: postgresdba postgres [local] SELECT(ExecutorRun+0x55) [0x71c026]
+        postgres: postgresdba postgres [local] SELECT() [0x953fc5]
+        postgres: postgresdba postgres [local] SELECT(PortalRun+0x262) [0x953c7e]
+        postgres: postgresdba postgres [local] SELECT() [0x94db78]
+        postgres: postgresdba postgres [local] SELECT(PostgresMain+0x7d7) [0x951e72]
+        postgres: postgresdba postgres [local] SELECT() [0x896b2f]
+        postgres: postgresdba postgres [local] SELECT() [0x8964b5]
+        postgres: postgresdba postgres [local] SELECT() [0x892a79]
+        postgres: postgresdba postgres [local] SELECT(PostmasterMain+0x116b) [0x892350]
+        postgres: postgresdba postgres [local] SELECT() [0x795f72]
+        /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2107bbd505]
+        postgres: postgresdba postgres [local] SELECT() [0x4842a9]
+</screen>
+    You can get the file name and line number by using gdb/addr2line in
+    linux platforms, as a prerequisite users must ensure gdb/addr2line is
+    already installed:
+<programlisting>
+1)  "info line *address" from gdb on postgres executable. For example:
+gdb ./postgres
+(gdb) info line *0x71c25d
+Line 378 of "execMain.c" starts at address 0x71c25d <literal>&lt;</literal>standard_ExecutorRun+470<literal>&gt;</literal> and ends at 0x71c263 <literal>&lt;</literal>standard_ExecutorRun+476<literal>&gt;</literal>.
+OR 
+2) Using "addr2line -e postgres address", For example:
+addr2line -e ./postgres 0x71c25d
+/home/postgresdba/src/backend/executor/execMain.c:378
+</programlisting>
+    If PostgreSQL was not built with the ability to capture backtrace. The
+    function will emit a warning and return false, for example:
+<screen>
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using library containing backtrace_symbols.
+ pg_print_backtrace 
+--------------------
+ f
+(1 row)
+</screen>
+   </para>
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 54c93b16c4..072c5952fa 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -701,6 +701,8 @@ REVOKE EXECUTE ON FUNCTION pg_ls_dir(text,boolean,boolean) FROM public;
 
 REVOKE EXECUTE ON FUNCTION pg_log_backend_memory_contexts(integer) FROM PUBLIC;
 
+REVOKE EXECUTE ON FUNCTION pg_print_backtrace(integer) FROM PUBLIC;
+
 --
 -- We also set up some things as accessible to standard roles.
 --
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 96332320a7..f1da744d0f 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -840,6 +840,10 @@ HandleAutoVacLauncherInterrupts(void)
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
 
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+		ProcessPrintBacktraceInterrupt();
+
 	/* Process sinval catchup interrupts that happened while sleeping */
 	ProcessCatchupInterrupt();
 }
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index 6e69398cdd..80fa6d4990 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -612,6 +612,44 @@ ProcessBarrierPlaceholder(void)
 	return true;
 }
 
+/*
+ * HandlePrintBacktraceInterrupt - Handle receipt of an interrupt indicating a
+ * print backtrace.
+ *
+ * All the actual work is deferred to ProcessPrintBacktraceInterrupt(),
+ * because we cannot safely emit a log message inside the signal handler.
+ */
+static void
+HandlePrintBacktraceInterrupt(void)
+{
+	InterruptPending = true;
+	PrintBacktracePending = true;
+	/* latch will be set by procsignal_sigusr1_handler */
+}
+
+/*
+ * ProcessPrintBacktraceInterrupt - Perform logging of backtrace of this
+ * 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.
+ */
+void
+ProcessPrintBacktraceInterrupt(void)
+{
+	PrintBacktracePending = false;
+
+	/*
+	 * Use LOG_SERVER_ONLY to prevent this message from being sent to the
+	 * connected client.
+	 */
+	ereport(LOG_SERVER_ONLY,
+			(errmsg("logging backtrace of PID %d", MyProcPid)));
+	set_backtrace(NULL, 0);
+}
+
 /*
  * CheckProcSignal - check to see if a particular reason has been
  * signaled, and clear the signal flag.  Should be called after receiving
@@ -661,6 +699,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_LOG_MEMORY_CONTEXT))
 		HandleLogMemoryContextInterrupt();
 
+	if (CheckProcSignal(PROCSIG_PRINT_BACKTRACE))
+		HandlePrintBacktraceInterrupt();
+
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
 
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index de69d60e79..34f270555b 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -23,6 +23,7 @@
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
+#include "tcop/tcopprot.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 
@@ -298,3 +299,51 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_print_backtrace - print backtrace of backend process.
+ *
+ * By default, only superusers can print backtrace. Additional roles can be
+ * permitted with GRANT.
+ */
+Datum
+pg_print_backtrace(PG_FUNCTION_ARGS)
+{
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	int			pid = PG_GETARG_INT32(0);
+	PGPROC	   *proc;
+
+	/*
+	 * 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.
+	 */
+	proc = BackendPidGetProc(pid);
+	if (proc == NULL)
+	{
+		ereport(WARNING,
+				(errmsg("PID %d is not a PostgreSQL server process", pid)));
+		PG_RETURN_BOOL(false);
+	}
+
+	/*
+	 * 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))
+		PG_RETURN_BOOL(true);
+	else
+		ereport(WARNING,
+				(errmsg("could not send signal to process %d: %m", pid))); /* return false below */
+#else
+	ereport(WARNING,
+			errmsg("backtrace generation is not supported by this installation"),
+			errhint("You need to rebuild PostgreSQL using library containing backtrace_symbols."));
+#endif
+
+	PG_RETURN_BOOL(false);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 0775abe35d..624b3f8453 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3366,6 +3366,10 @@ ProcessInterrupts(void)
 
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Process printing backtrace */
+	if (PrintBacktracePending)
+		ProcessPrintBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index f33729513a..05fe1bb5e7 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -172,7 +172,6 @@ static char formatted_log_time[FORMATTED_TS_LEN];
 
 
 static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
-static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
 static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
 static void write_console(const char *line, int len);
 static void setup_formatted_log_time(void);
@@ -949,9 +948,10 @@ errbacktrace(void)
  * Compute backtrace data and add it to the supplied ErrorData.  num_skip
  * specifies how many inner frames to skip.  Use this to avoid showing the
  * internal backtrace support functions in the backtrace.  This requires that
- * this and related functions are not inlined.
+ * this and related functions are not inlined. If edata pointer is valid
+ * backtrace information will be set in edata.
  */
-static void
+void
 set_backtrace(ErrorData *edata, int num_skip)
 {
 	StringInfoData errtrace;
@@ -978,7 +978,19 @@ set_backtrace(ErrorData *edata, int num_skip)
 						   "backtrace generation is not supported by this installation");
 #endif
 
-	edata->backtrace = errtrace.data;
+	if (edata)
+		edata->backtrace = errtrace.data;
+	else
+	{
+		/*
+		 * LOG_SERVER_ONLY is used intentionally to make sure this information
+		 * is not sent to client based on client_min_messages. We don't want
+		 * to mess up a different session as pg_print_backtrace will be
+		 * sending SIGNAL to a different backend.
+		 */
+		elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
+		pfree(errtrace.data);
+	}
 }
 
 /*
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 381d9e548d..e49f5331a0 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -36,6 +36,7 @@ volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t IdleSessionTimeoutPending = false;
 volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile sig_atomic_t LogMemoryContextPending = false;
+volatile sig_atomic_t PrintBacktracePending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 49e8e59129..9f3e7b7503 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	202111091
+#define CATALOG_VERSION_NO	202111141
 
 #endif
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index d068d6532e..47872558b8 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11689,4 +11689,9 @@
   prorettype => 'bytea', proargtypes => 'pg_brin_minmax_multi_summary',
   prosrc => 'brin_minmax_multi_summary_send' },
 
+# function to get the backtrace of server process
+{ oid => '6105', descr => 'print backtrace of process',
+  proname => 'pg_print_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_print_backtrace' },
+
 ]
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 90a3016065..0da18326f5 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -94,6 +94,7 @@ extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
 extern PGDLLIMPORT volatile sig_atomic_t LogMemoryContextPending;
+extern PGDLLIMPORT volatile sig_atomic_t PrintBacktracePending;
 
 extern PGDLLIMPORT volatile sig_atomic_t CheckClientConnectionPending;
 extern PGDLLIMPORT volatile sig_atomic_t ClientConnectionLost;
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index eec186be2e..731c4fa970 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -35,6 +35,7 @@ typedef enum
 	PROCSIG_WALSND_INIT_STOPPING,	/* ask walsenders to prepare for shutdown  */
 	PROCSIG_BARRIER,			/* global barrier interrupt  */
 	PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
+	PROCSIG_PRINT_BACKTRACE,	/* ask backend to print the current backtrace */
 
 	/* Recovery conflict reasons */
 	PROCSIG_RECOVERY_CONFLICT_DATABASE,
@@ -70,7 +71,7 @@ extern int	SendProcSignal(pid_t pid, ProcSignalReason reason,
 extern uint64 EmitProcSignalBarrier(ProcSignalBarrierType type);
 extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
-
+extern void ProcessPrintBacktraceInterrupt(void);
 extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
 
 #endif							/* PROCSIGNAL_H */
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index f53607e12e..c63d25716a 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -453,4 +453,6 @@ extern void set_syslog_parameters(const char *ident, int facility);
  */
 extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
 
+extern pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
+
 #endif							/* ELOG_H */
diff --git a/src/test/regress/expected/misc_functions.out b/src/test/regress/expected/misc_functions.out
index 71d316cad3..c75cef00e7 100644
--- a/src/test/regress/expected/misc_functions.out
+++ b/src/test/regress/expected/misc_functions.out
@@ -176,6 +176,48 @@ REVOKE EXECUTE ON FUNCTION pg_log_backend_memory_contexts(integer)
   FROM regress_log_memory;
 DROP ROLE regress_log_memory;
 --
+-- pg_print_backtrace()
+--
+-- Backtrace are logged and they are not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_print_backtrace(pg_backend_pid());
+ pg_print_backtrace 
+--------------------
+ t
+(1 row)
+
+CREATE ROLE regress_print_backtrace;
+SELECT has_function_privilege('regress_print_backtrace',
+  'pg_print_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_print_backtrace(integer)
+  TO regress_print_backtrace;
+SELECT has_function_privilege('regress_print_backtrace',
+  'pg_print_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_print_backtrace;
+SELECT pg_print_backtrace(pg_backend_pid());
+ pg_print_backtrace 
+--------------------
+ t
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_print_backtrace(integer)
+  FROM regress_print_backtrace;
+DROP ROLE regress_print_backtrace;
+--
 -- Test some built-in SRFs
 --
 -- The outputs of these are variable, so we can't just print their results
diff --git a/src/test/regress/sql/misc_functions.sql b/src/test/regress/sql/misc_functions.sql
index 8c23874b3f..d63d2e2ddb 100644
--- a/src/test/regress/sql/misc_functions.sql
+++ b/src/test/regress/sql/misc_functions.sql
@@ -61,6 +61,37 @@ REVOKE EXECUTE ON FUNCTION pg_log_backend_memory_contexts(integer)
 
 DROP ROLE regress_log_memory;
 
+--
+-- pg_print_backtrace()
+--
+-- Backtrace are logged and they are not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+
+SELECT pg_print_backtrace(pg_backend_pid());
+
+CREATE ROLE regress_print_backtrace;
+
+SELECT has_function_privilege('regress_print_backtrace',
+  'pg_print_backtrace(integer)', 'EXECUTE'); -- no
+
+GRANT EXECUTE ON FUNCTION pg_print_backtrace(integer)
+  TO regress_print_backtrace;
+
+SELECT has_function_privilege('regress_print_backtrace',
+  'pg_print_backtrace(integer)', 'EXECUTE'); -- yes
+
+SET ROLE regress_print_backtrace;
+SELECT pg_print_backtrace(pg_backend_pid());
+RESET ROLE;
+
+REVOKE EXECUTE ON FUNCTION pg_print_backtrace(integer)
+  FROM regress_print_backtrace;
+
+DROP ROLE regress_print_backtrace;
+
 --
 -- Test some built-in SRFs
 --
-- 
2.30.2

#82Justin Pryzby
pryzby@telsasoft.com
In reply to: vignesh C (#81)
Re: Printing backtrace of postgres processes

On Mon, Nov 15, 2021 at 09:12:49PM +0530, vignesh C wrote:

The idea here is to implement & expose pg_print_backtrace function, internally

This patch is closely related to this one
https://commitfest.postgresql.org/35/3142/
| Logging plan of the currently running query

I suggest to review that patch and make sure there's nothing you could borrow.

My only comment for now is that maybe the function name should be
pg_log_backtrace() rather than pg_print_backtrace(), since it doesn't actually
"print" the backtrace, but rather request the other backend to log its
backtrace.

Did you see that the windows build failed ?
https://ci.appveyor.com/project/postgresql-cfbot/postgresql/build/1.0.153557

I think you'll need to create an "alternate" output like
src/test/regress/expected/misc_functions_1.out

It's possible that's best done by creating a totally new .sql and .out files
added to src/test/regress/parallel_schedule - because otherwise, you'd have to
duplicate the existing 300 lines of misc_fuctions.out.

--
Justin

#83Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: Justin Pryzby (#82)
Re: Printing backtrace of postgres processes

On Tue, Nov 16, 2021 at 1:12 AM Justin Pryzby <pryzby@telsasoft.com> wrote:

On Mon, Nov 15, 2021 at 09:12:49PM +0530, vignesh C wrote:

The idea here is to implement & expose pg_print_backtrace function, internally

This patch is closely related to this one
https://commitfest.postgresql.org/35/3142/
| Logging plan of the currently running query

I suggest to review that patch and make sure there's nothing you could borrow.

My only comment for now is that maybe the function name should be
pg_log_backtrace() rather than pg_print_backtrace(), since it doesn't actually
"print" the backtrace, but rather request the other backend to log its
backtrace.

+1 for pg_log_backtrace().

Regards,
Bharath Rupireddy.

#84vignesh C
vignesh21@gmail.com
In reply to: Justin Pryzby (#82)
2 attachment(s)
Re: Printing backtrace of postgres processes

On Tue, Nov 16, 2021 at 1:12 AM Justin Pryzby <pryzby@telsasoft.com> wrote:

On Mon, Nov 15, 2021 at 09:12:49PM +0530, vignesh C wrote:

The idea here is to implement & expose pg_print_backtrace function, internally

This patch is closely related to this one
https://commitfest.postgresql.org/35/3142/
| Logging plan of the currently running query

I suggest to review that patch and make sure there's nothing you could borrow.

I had a look and felt the attached patch is in similar lines

My only comment for now is that maybe the function name should be
pg_log_backtrace() rather than pg_print_backtrace(), since it doesn't actually
"print" the backtrace, but rather request the other backend to log its
backtrace.

Modified

Did you see that the windows build failed ?
https://ci.appveyor.com/project/postgresql-cfbot/postgresql/build/1.0.153557
I think you'll need to create an "alternate" output like
src/test/regress/expected/misc_functions_1.out

It's possible that's best done by creating a totally new .sql and .out files
added to src/test/regress/parallel_schedule - because otherwise, you'd have to
duplicate the existing 300 lines of misc_fuctions.out.

Modified

Attached v14 patch has the fixes for the same.

Regards,
Vignesh

Attachments:

v14-0001-Add-function-to-log-the-backtrace-of-the-specifi.patchtext/x-patch; charset=US-ASCII; name=v14-0001-Add-function-to-log-the-backtrace-of-the-specifi.patchDownload
From e267fb1772c05ad8f622d9dae0e474bcd8b25da6 Mon Sep 17 00:00:00 2001
From: Vignesh C <vignesh21@gmail.com>
Date: Tue, 9 Nov 2021 16:28:03 +0530
Subject: [PATCH v14 1/2] Add function to log the backtrace of the specified
 backend process.

This commit adds pg_log_backtrace() function that requests to log
the backtrace of the specified backend process.

Only superusers are allowed to request to log the backtrace
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.

On receipt of the request, at the next CHECK_FOR_INTERRUPTS(),
the target backend logs its backtrace at LOG_SERVER_ONLY level,
so that the backtrace will appear in the server log but not
be sent to the client.

Bump catalog version.
---
 doc/src/sgml/func.sgml                    | 85 +++++++++++++++++++++++
 src/backend/catalog/system_functions.sql  |  2 +
 src/backend/postmaster/autovacuum.c       |  4 ++
 src/backend/storage/ipc/procsignal.c      | 41 +++++++++++
 src/backend/storage/ipc/signalfuncs.c     | 49 +++++++++++++
 src/backend/tcop/postgres.c               |  4 ++
 src/backend/utils/error/elog.c            | 20 ++++--
 src/backend/utils/init/globals.c          |  1 +
 src/include/catalog/catversion.h          |  2 +-
 src/include/catalog/pg_proc.dat           |  5 ++
 src/include/miscadmin.h                   |  1 +
 src/include/storage/procsignal.h          |  3 +-
 src/include/utils/elog.h                  |  2 +
 src/test/regress/expected/backtrace.out   | 42 +++++++++++
 src/test/regress/expected/backtrace_1.out | 46 ++++++++++++
 src/test/regress/parallel_schedule        |  2 +-
 src/test/regress/sql/backtrace.sql        | 30 ++++++++
 17 files changed, 332 insertions(+), 7 deletions(-)
 create mode 100644 src/test/regress/expected/backtrace.out
 create mode 100644 src/test/regress/expected/backtrace_1.out
 create mode 100644 src/test/regress/sql/backtrace.sql

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 24447c0017..a88583edfd 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25345,6 +25345,32 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_log_backtrace</primary>
+        </indexterm>
+        <function>pg_log_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </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
+        <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.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -25458,6 +25484,65 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_log_backtrace</function> can be used to log the backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace
+------------------
+ t
+(1 row)
+</programlisting>
+The backtrace will be logged to the log file if logging is enabled, if logging
+is disabled backtrace will be logged to the console where the postmaster was
+started. For example:
+<screen>
+2021-01-27 11:33:50.247 IST [111735] LOG:  current backtrace:
+        postgres: postgresdba postgres [local] SELECT(set_backtrace+0x38) [0xae06c5]
+        postgres: postgresdba postgres [local] SELECT(ProcessInterrupts+0x788) [0x950c34]
+        postgres: postgresdba postgres [local] SELECT() [0x761e89]
+        postgres: postgresdba postgres [local] SELECT() [0x71bbda]
+        postgres: postgresdba postgres [local] SELECT() [0x71e380]
+        postgres: postgresdba postgres [local] SELECT(standard_ExecutorRun+0x1d6) [0x71c1fe]
+        postgres: postgresdba postgres [local] SELECT(ExecutorRun+0x55) [0x71c026]
+        postgres: postgresdba postgres [local] SELECT() [0x953fc5]
+        postgres: postgresdba postgres [local] SELECT(PortalRun+0x262) [0x953c7e]
+        postgres: postgresdba postgres [local] SELECT() [0x94db78]
+        postgres: postgresdba postgres [local] SELECT(PostgresMain+0x7d7) [0x951e72]
+        postgres: postgresdba postgres [local] SELECT() [0x896b2f]
+        postgres: postgresdba postgres [local] SELECT() [0x8964b5]
+        postgres: postgresdba postgres [local] SELECT() [0x892a79]
+        postgres: postgresdba postgres [local] SELECT(PostmasterMain+0x116b) [0x892350]
+        postgres: postgresdba postgres [local] SELECT() [0x795f72]
+        /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2107bbd505]
+        postgres: postgresdba postgres [local] SELECT() [0x4842a9]
+</screen>
+    You can get the file name and line number by using gdb/addr2line in
+    linux platforms, as a prerequisite users must ensure gdb/addr2line is
+    already installed:
+<programlisting>
+1)  "info line *address" from gdb on postgres executable. For example:
+gdb ./postgres
+(gdb) info line *0x71c25d
+Line 378 of "execMain.c" starts at address 0x71c25d <literal>&lt;</literal>standard_ExecutorRun+470<literal>&gt;</literal> and ends at 0x71c263 <literal>&lt;</literal>standard_ExecutorRun+476<literal>&gt;</literal>.
+OR 
+2) Using "addr2line -e postgres address", For example:
+addr2line -e ./postgres 0x71c25d
+/home/postgresdba/src/backend/executor/execMain.c:378
+</programlisting>
+    If PostgreSQL was not built with the ability to capture backtrace. The
+    function will emit a warning and return false, for example:
+<screen>
+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)
+</screen>
+   </para>
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 54c93b16c4..6e598f9bdb 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -701,6 +701,8 @@ REVOKE EXECUTE ON FUNCTION pg_ls_dir(text,boolean,boolean) FROM public;
 
 REVOKE EXECUTE ON FUNCTION pg_log_backend_memory_contexts(integer) FROM PUBLIC;
 
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer) FROM PUBLIC;
+
 --
 -- We also set up some things as accessible to standard roles.
 --
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 96332320a7..90111135ff 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -840,6 +840,10 @@ HandleAutoVacLauncherInterrupts(void)
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
 
+	/* Process logging backtrace */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
+
 	/* Process sinval catchup interrupts that happened while sleeping */
 	ProcessCatchupInterrupt();
 }
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index 6e69398cdd..3dac4a8f4b 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -612,6 +612,44 @@ ProcessBarrierPlaceholder(void)
 	return true;
 }
 
+/*
+ * HandleLogBacktraceInterrupt - Handle receipt of an interrupt indicating
+ * logging of backtrace.
+ *
+ * All the actual work is deferred to ProcessLogBacktraceInterrupt(),
+ * because we cannot safely emit a log message inside the signal handler.
+ */
+static void
+HandleLogBacktraceInterrupt(void)
+{
+	InterruptPending = true;
+	LogBacktracePending = true;
+	/* latch will be set by procsignal_sigusr1_handler */
+}
+
+/*
+ * ProcessLogBacktraceInterrupt - Perform logging of backtrace of this
+ * backend process.
+ *
+ * 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.
+ */
+void
+ProcessLogBacktraceInterrupt(void)
+{
+	LogBacktracePending = false;
+
+	/*
+	 * Use LOG_SERVER_ONLY to prevent this message from being sent to the
+	 * connected client.
+	 */
+	ereport(LOG_SERVER_ONLY,
+			(errmsg("logging backtrace of PID %d", MyProcPid)));
+	set_backtrace(NULL, 0);
+}
+
 /*
  * CheckProcSignal - check to see if a particular reason has been
  * signaled, and clear the signal flag.  Should be called after receiving
@@ -661,6 +699,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_LOG_MEMORY_CONTEXT))
 		HandleLogMemoryContextInterrupt();
 
+	if (CheckProcSignal(PROCSIG_LOG_BACKTRACE))
+		HandleLogBacktraceInterrupt();
+
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
 
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index de69d60e79..d042feefee 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -23,6 +23,7 @@
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
+#include "tcop/tcopprot.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 
@@ -298,3 +299,51 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_log_backtrace - log backtrace of backend process.
+ *
+ * By default, only superusers can log backtrace. Additional roles can be
+ * permitted with GRANT.
+ */
+Datum
+pg_log_backtrace(PG_FUNCTION_ARGS)
+{
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	int			pid = PG_GETARG_INT32(0);
+	PGPROC	   *proc;
+
+	/*
+	 * 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.
+	 */
+	proc = BackendPidGetProc(pid);
+	if (proc == NULL)
+	{
+		ereport(WARNING,
+				(errmsg("PID %d is not a PostgreSQL server process", pid)));
+		PG_RETURN_BOOL(false);
+	}
+
+	/*
+	 * Send SIGUSR1 to postgres backend whose pid matches pid by
+	 * setting PROCSIG_LOG_BACKTRACE, the backend process will log
+	 * the backtrace once the signal is received.
+	 */
+	if (!SendProcSignal(pid, PROCSIG_LOG_BACKTRACE, proc->backendId))
+		PG_RETURN_BOOL(true);
+	else
+		ereport(WARNING,
+				(errmsg("could not send signal to process %d: %m", pid))); /* return false below */
+#else
+	ereport(WARNING,
+			errmsg("backtrace generation is not supported by this installation"),
+			errhint("You need to rebuild PostgreSQL using library containing backtrace_symbols."));
+#endif
+
+	PG_RETURN_BOOL(false);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 0775abe35d..3a2b670d23 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3366,6 +3366,10 @@ ProcessInterrupts(void)
 
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Process logging backtrace */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index f33729513a..6166965a39 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -172,7 +172,6 @@ static char formatted_log_time[FORMATTED_TS_LEN];
 
 
 static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
-static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
 static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
 static void write_console(const char *line, int len);
 static void setup_formatted_log_time(void);
@@ -949,9 +948,10 @@ errbacktrace(void)
  * Compute backtrace data and add it to the supplied ErrorData.  num_skip
  * specifies how many inner frames to skip.  Use this to avoid showing the
  * internal backtrace support functions in the backtrace.  This requires that
- * this and related functions are not inlined.
+ * this and related functions are not inlined. If edata pointer is valid
+ * backtrace information will be set in edata.
  */
-static void
+void
 set_backtrace(ErrorData *edata, int num_skip)
 {
 	StringInfoData errtrace;
@@ -978,7 +978,19 @@ set_backtrace(ErrorData *edata, int num_skip)
 						   "backtrace generation is not supported by this installation");
 #endif
 
-	edata->backtrace = errtrace.data;
+	if (edata)
+		edata->backtrace = errtrace.data;
+	else
+	{
+		/*
+		 * LOG_SERVER_ONLY is used intentionally to make sure this information
+		 * is not sent to client based on client_min_messages. We don't want
+		 * to mess up a different session as pg_log_backtrace will be
+		 * sending SIGNAL to a different backend.
+		 */
+		elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
+		pfree(errtrace.data);
+	}
 }
 
 /*
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 381d9e548d..66570a902c 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -36,6 +36,7 @@ volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t IdleSessionTimeoutPending = false;
 volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile sig_atomic_t LogMemoryContextPending = false;
+volatile sig_atomic_t LogBacktracePending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 49e8e59129..9f3e7b7503 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	202111091
+#define CATALOG_VERSION_NO	202111141
 
 #endif
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index d068d6532e..91551bb2ee 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11689,4 +11689,9 @@
   prorettype => 'bytea', proargtypes => 'pg_brin_minmax_multi_summary',
   prosrc => 'brin_minmax_multi_summary_send' },
 
+# function to get the backtrace of server process
+{ oid => '6105', descr => 'log backtrace of process',
+  proname => 'pg_log_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_log_backtrace' },
+
 ]
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 90a3016065..8a7f5fba54 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -94,6 +94,7 @@ extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
 extern PGDLLIMPORT volatile sig_atomic_t LogMemoryContextPending;
+extern PGDLLIMPORT volatile sig_atomic_t LogBacktracePending;
 
 extern PGDLLIMPORT volatile sig_atomic_t CheckClientConnectionPending;
 extern PGDLLIMPORT volatile sig_atomic_t ClientConnectionLost;
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index eec186be2e..e14763820f 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -35,6 +35,7 @@ typedef enum
 	PROCSIG_WALSND_INIT_STOPPING,	/* ask walsenders to prepare for shutdown  */
 	PROCSIG_BARRIER,			/* global barrier interrupt  */
 	PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
+	PROCSIG_LOG_BACKTRACE,		/* ask backend to log the current backtrace */
 
 	/* Recovery conflict reasons */
 	PROCSIG_RECOVERY_CONFLICT_DATABASE,
@@ -70,7 +71,7 @@ extern int	SendProcSignal(pid_t pid, ProcSignalReason reason,
 extern uint64 EmitProcSignalBarrier(ProcSignalBarrierType type);
 extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
-
+extern void ProcessLogBacktraceInterrupt(void);
 extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
 
 #endif							/* PROCSIGNAL_H */
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index f53607e12e..c63d25716a 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -453,4 +453,6 @@ extern void set_syslog_parameters(const char *ident, int facility);
  */
 extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
 
+extern pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
+
 #endif							/* ELOG_H */
diff --git a/src/test/regress/expected/backtrace.out b/src/test/regress/expected/backtrace.out
new file mode 100644
index 0000000000..88ee383d4d
--- /dev/null
+++ b/src/test/regress/expected/backtrace.out
@@ -0,0 +1,42 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtrace are logged and they are not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/expected/backtrace_1.out b/src/test/regress/expected/backtrace_1.out
new file mode 100644
index 0000000000..62beef8f51
--- /dev/null
+++ b/src/test/regress/expected/backtrace_1.out
@@ -0,0 +1,46 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtrace are logged and they are not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+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)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+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)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 017e962fed..ca8b2a6d8e 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -86,7 +86,7 @@ test: brin_bloom brin_multi
 # ----------
 # Another group of parallel tests
 # ----------
-test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort
+test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort backtrace
 
 # rules cannot run concurrently with any test that creates
 # a view or rule in the public schema
diff --git a/src/test/regress/sql/backtrace.sql b/src/test/regress/sql/backtrace.sql
new file mode 100644
index 0000000000..2688bc7330
--- /dev/null
+++ b/src/test/regress/sql/backtrace.sql
@@ -0,0 +1,30 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtrace are logged and they are not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+
+SELECT pg_log_backtrace(pg_backend_pid());
+
+CREATE ROLE regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+RESET ROLE;
+
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+
+DROP ROLE regress_log_backtrace;
-- 
2.30.2

v14-0002-pg_log_backtrace-support-for-logging-backtrace-o.patchtext/x-patch; charset=US-ASCII; name=v14-0002-pg_log_backtrace-support-for-logging-backtrace-o.patchDownload
From fea4198a289d20d4ec1cb0523d8ef91c5d298ce7 Mon Sep 17 00:00:00 2001
From: Vignesh C <vignesh21@gmail.com>
Date: Wed, 17 Nov 2021 08:40:18 +0530
Subject: [PATCH v14 2/2] pg_log_backtrace support for logging backtrace of aux
 procs

Enhanced pg_log_backtrace to support logging 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
---
 doc/src/sgml/func.sgml                    | 26 ++++++++++-------
 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     | 35 ++++++++++++++++-------
 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, 95 insertions(+), 22 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index a88583edfd..7e245711da 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25355,19 +25355,25 @@ 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. This function cannot request
+        <glossterm linkend="glossary-postmaster">postmaster process</glossterm> or
+        <glossterm linkend="glossary-logger">logger</glossterm> or 
+        <glossterm linkend="glossary-stats-collector">statistics collector</glossterm>
+        (all other <glossterm linkend="glossary-auxiliary-proc">auxiliary processes</glossterm>
+        it can) for the backtrace.  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..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 3dac4a8f4b..e70bfce0bd 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 d042feefee..21441c64b8 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -312,29 +312,44 @@ pg_log_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_LOG_BACKTRACE, the backend process will log
 	 * the backtrace once the signal is received.
 	 */
-	if (!SendProcSignal(pid, PROCSIG_LOG_BACKTRACE, proc->backendId))
+	if (!SendProcSignal(pid, PROCSIG_LOG_BACKTRACE, backendId))
 		PG_RETURN_BOOL(true);
 	else
 		ereport(WARNING,
diff --git a/src/test/regress/expected/backtrace.out b/src/test/regress/expected/backtrace.out
index 88ee383d4d..52aec7088c 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 62beef8f51..cfe51e1329 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 2688bc7330..11f52c7d29 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.30.2

#85Justin Pryzby
pryzby@telsasoft.com
In reply to: vignesh C (#84)
4 attachment(s)
Re: Printing backtrace of postgres processes

On Wed, Nov 17, 2021 at 08:12:44PM +0530, vignesh C wrote:

Attached v14 patch has the fixes for the same.

Thanks for updating the patch.

I cleaned up the docs and comments. I think this could be nearly "Ready".

If you like the changes in my "fixup" patch (0002 and 0004), you should be able
to apply my 0002 on top of your 0001. I'm sure it'll cause some conflicts with
your 2nd patch, though...

This doesn't bump the catversion, since that would cause the patch to fail in
cfbot every time another commit updates catversion.

Your 0001 patch allows printing backtraces of autovacuum, but the doc says it's
only for "backends". Should the change to autovacuum.c be moved to the 2nd
patch ? Or, why is autovacuum so special that it should be handled in the
first patch ?

--
Justin

Attachments:

0001-Add-function-to-log-the-backtrace-of-the-specified-b.patchtext/x-diff; charset=us-asciiDownload
From 0829c99951194df3e101e1c648a05bb5651ef090 Mon Sep 17 00:00:00 2001
From: Vignesh C <vignesh21@gmail.com>
Date: Tue, 9 Nov 2021 16:28:03 +0530
Subject: [PATCH 1/4] Add function to log the backtrace of the specified
 backend process.

This commit adds pg_log_backtrace() function that requests to log
the backtrace of the specified backend process.

Only superusers are allowed to request to log the backtrace
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.

On receipt of the request, at the next CHECK_FOR_INTERRUPTS(),
the target backend logs its backtrace at LOG_SERVER_ONLY level,
so that the backtrace will appear in the server log but not
be sent to the client.

Bump catalog version.
---
 doc/src/sgml/func.sgml                    | 85 +++++++++++++++++++++++
 src/backend/catalog/system_functions.sql  |  2 +
 src/backend/postmaster/autovacuum.c       |  4 ++
 src/backend/storage/ipc/procsignal.c      | 41 +++++++++++
 src/backend/storage/ipc/signalfuncs.c     | 49 +++++++++++++
 src/backend/tcop/postgres.c               |  4 ++
 src/backend/utils/error/elog.c            | 20 ++++--
 src/backend/utils/init/globals.c          |  1 +
 src/include/catalog/pg_proc.dat           |  5 ++
 src/include/miscadmin.h                   |  1 +
 src/include/storage/procsignal.h          |  3 +-
 src/include/utils/elog.h                  |  2 +
 src/test/regress/expected/backtrace.out   | 42 +++++++++++
 src/test/regress/expected/backtrace_1.out | 46 ++++++++++++
 src/test/regress/parallel_schedule        |  2 +-
 src/test/regress/sql/backtrace.sql        | 30 ++++++++
 16 files changed, 331 insertions(+), 6 deletions(-)
 create mode 100644 src/test/regress/expected/backtrace.out
 create mode 100644 src/test/regress/expected/backtrace_1.out
 create mode 100644 src/test/regress/sql/backtrace.sql

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 24447c0017..a88583edfd 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25345,6 +25345,32 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_log_backtrace</primary>
+        </indexterm>
+        <function>pg_log_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </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
+        <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.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -25458,6 +25484,65 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_log_backtrace</function> can be used to log the backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace
+------------------
+ t
+(1 row)
+</programlisting>
+The backtrace will be logged to the log file if logging is enabled, if logging
+is disabled backtrace will be logged to the console where the postmaster was
+started. For example:
+<screen>
+2021-01-27 11:33:50.247 IST [111735] LOG:  current backtrace:
+        postgres: postgresdba postgres [local] SELECT(set_backtrace+0x38) [0xae06c5]
+        postgres: postgresdba postgres [local] SELECT(ProcessInterrupts+0x788) [0x950c34]
+        postgres: postgresdba postgres [local] SELECT() [0x761e89]
+        postgres: postgresdba postgres [local] SELECT() [0x71bbda]
+        postgres: postgresdba postgres [local] SELECT() [0x71e380]
+        postgres: postgresdba postgres [local] SELECT(standard_ExecutorRun+0x1d6) [0x71c1fe]
+        postgres: postgresdba postgres [local] SELECT(ExecutorRun+0x55) [0x71c026]
+        postgres: postgresdba postgres [local] SELECT() [0x953fc5]
+        postgres: postgresdba postgres [local] SELECT(PortalRun+0x262) [0x953c7e]
+        postgres: postgresdba postgres [local] SELECT() [0x94db78]
+        postgres: postgresdba postgres [local] SELECT(PostgresMain+0x7d7) [0x951e72]
+        postgres: postgresdba postgres [local] SELECT() [0x896b2f]
+        postgres: postgresdba postgres [local] SELECT() [0x8964b5]
+        postgres: postgresdba postgres [local] SELECT() [0x892a79]
+        postgres: postgresdba postgres [local] SELECT(PostmasterMain+0x116b) [0x892350]
+        postgres: postgresdba postgres [local] SELECT() [0x795f72]
+        /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2107bbd505]
+        postgres: postgresdba postgres [local] SELECT() [0x4842a9]
+</screen>
+    You can get the file name and line number by using gdb/addr2line in
+    linux platforms, as a prerequisite users must ensure gdb/addr2line is
+    already installed:
+<programlisting>
+1)  "info line *address" from gdb on postgres executable. For example:
+gdb ./postgres
+(gdb) info line *0x71c25d
+Line 378 of "execMain.c" starts at address 0x71c25d <literal>&lt;</literal>standard_ExecutorRun+470<literal>&gt;</literal> and ends at 0x71c263 <literal>&lt;</literal>standard_ExecutorRun+476<literal>&gt;</literal>.
+OR 
+2) Using "addr2line -e postgres address", For example:
+addr2line -e ./postgres 0x71c25d
+/home/postgresdba/src/backend/executor/execMain.c:378
+</programlisting>
+    If PostgreSQL was not built with the ability to capture backtrace. The
+    function will emit a warning and return false, for example:
+<screen>
+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)
+</screen>
+   </para>
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 54c93b16c4..6e598f9bdb 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -701,6 +701,8 @@ REVOKE EXECUTE ON FUNCTION pg_ls_dir(text,boolean,boolean) FROM public;
 
 REVOKE EXECUTE ON FUNCTION pg_log_backend_memory_contexts(integer) FROM PUBLIC;
 
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer) FROM PUBLIC;
+
 --
 -- We also set up some things as accessible to standard roles.
 --
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 96332320a7..90111135ff 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -840,6 +840,10 @@ HandleAutoVacLauncherInterrupts(void)
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
 
+	/* Process logging backtrace */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
+
 	/* Process sinval catchup interrupts that happened while sleeping */
 	ProcessCatchupInterrupt();
 }
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index 6e69398cdd..3dac4a8f4b 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -612,6 +612,44 @@ ProcessBarrierPlaceholder(void)
 	return true;
 }
 
+/*
+ * HandleLogBacktraceInterrupt - Handle receipt of an interrupt indicating
+ * logging of backtrace.
+ *
+ * All the actual work is deferred to ProcessLogBacktraceInterrupt(),
+ * because we cannot safely emit a log message inside the signal handler.
+ */
+static void
+HandleLogBacktraceInterrupt(void)
+{
+	InterruptPending = true;
+	LogBacktracePending = true;
+	/* latch will be set by procsignal_sigusr1_handler */
+}
+
+/*
+ * ProcessLogBacktraceInterrupt - Perform logging of backtrace of this
+ * backend process.
+ *
+ * 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.
+ */
+void
+ProcessLogBacktraceInterrupt(void)
+{
+	LogBacktracePending = false;
+
+	/*
+	 * Use LOG_SERVER_ONLY to prevent this message from being sent to the
+	 * connected client.
+	 */
+	ereport(LOG_SERVER_ONLY,
+			(errmsg("logging backtrace of PID %d", MyProcPid)));
+	set_backtrace(NULL, 0);
+}
+
 /*
  * CheckProcSignal - check to see if a particular reason has been
  * signaled, and clear the signal flag.  Should be called after receiving
@@ -661,6 +699,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_LOG_MEMORY_CONTEXT))
 		HandleLogMemoryContextInterrupt();
 
+	if (CheckProcSignal(PROCSIG_LOG_BACKTRACE))
+		HandleLogBacktraceInterrupt();
+
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
 
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index de69d60e79..d042feefee 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -23,6 +23,7 @@
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
+#include "tcop/tcopprot.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 
@@ -298,3 +299,51 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_log_backtrace - log backtrace of backend process.
+ *
+ * By default, only superusers can log backtrace. Additional roles can be
+ * permitted with GRANT.
+ */
+Datum
+pg_log_backtrace(PG_FUNCTION_ARGS)
+{
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	int			pid = PG_GETARG_INT32(0);
+	PGPROC	   *proc;
+
+	/*
+	 * 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.
+	 */
+	proc = BackendPidGetProc(pid);
+	if (proc == NULL)
+	{
+		ereport(WARNING,
+				(errmsg("PID %d is not a PostgreSQL server process", pid)));
+		PG_RETURN_BOOL(false);
+	}
+
+	/*
+	 * Send SIGUSR1 to postgres backend whose pid matches pid by
+	 * setting PROCSIG_LOG_BACKTRACE, the backend process will log
+	 * the backtrace once the signal is received.
+	 */
+	if (!SendProcSignal(pid, PROCSIG_LOG_BACKTRACE, proc->backendId))
+		PG_RETURN_BOOL(true);
+	else
+		ereport(WARNING,
+				(errmsg("could not send signal to process %d: %m", pid))); /* return false below */
+#else
+	ereport(WARNING,
+			errmsg("backtrace generation is not supported by this installation"),
+			errhint("You need to rebuild PostgreSQL using library containing backtrace_symbols."));
+#endif
+
+	PG_RETURN_BOOL(false);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 0775abe35d..3a2b670d23 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3366,6 +3366,10 @@ ProcessInterrupts(void)
 
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Process logging backtrace */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index f33729513a..6166965a39 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -172,7 +172,6 @@ static char formatted_log_time[FORMATTED_TS_LEN];
 
 
 static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
-static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
 static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
 static void write_console(const char *line, int len);
 static void setup_formatted_log_time(void);
@@ -949,9 +948,10 @@ errbacktrace(void)
  * Compute backtrace data and add it to the supplied ErrorData.  num_skip
  * specifies how many inner frames to skip.  Use this to avoid showing the
  * internal backtrace support functions in the backtrace.  This requires that
- * this and related functions are not inlined.
+ * this and related functions are not inlined. If edata pointer is valid
+ * backtrace information will be set in edata.
  */
-static void
+void
 set_backtrace(ErrorData *edata, int num_skip)
 {
 	StringInfoData errtrace;
@@ -978,7 +978,19 @@ set_backtrace(ErrorData *edata, int num_skip)
 						   "backtrace generation is not supported by this installation");
 #endif
 
-	edata->backtrace = errtrace.data;
+	if (edata)
+		edata->backtrace = errtrace.data;
+	else
+	{
+		/*
+		 * LOG_SERVER_ONLY is used intentionally to make sure this information
+		 * is not sent to client based on client_min_messages. We don't want
+		 * to mess up a different session as pg_log_backtrace will be
+		 * sending SIGNAL to a different backend.
+		 */
+		elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
+		pfree(errtrace.data);
+	}
 }
 
 /*
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 381d9e548d..66570a902c 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -36,6 +36,7 @@ volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t IdleSessionTimeoutPending = false;
 volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile sig_atomic_t LogMemoryContextPending = false;
+volatile sig_atomic_t LogBacktracePending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 6412f369f1..2537edca12 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11693,4 +11693,9 @@
   prorettype => 'bytea', proargtypes => 'pg_brin_minmax_multi_summary',
   prosrc => 'brin_minmax_multi_summary_send' },
 
+# function to get the backtrace of server process
+{ oid => '6105', descr => 'log backtrace of process',
+  proname => 'pg_log_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_log_backtrace' },
+
 ]
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 90a3016065..8a7f5fba54 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -94,6 +94,7 @@ extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
 extern PGDLLIMPORT volatile sig_atomic_t LogMemoryContextPending;
+extern PGDLLIMPORT volatile sig_atomic_t LogBacktracePending;
 
 extern PGDLLIMPORT volatile sig_atomic_t CheckClientConnectionPending;
 extern PGDLLIMPORT volatile sig_atomic_t ClientConnectionLost;
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index eec186be2e..e14763820f 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -35,6 +35,7 @@ typedef enum
 	PROCSIG_WALSND_INIT_STOPPING,	/* ask walsenders to prepare for shutdown  */
 	PROCSIG_BARRIER,			/* global barrier interrupt  */
 	PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
+	PROCSIG_LOG_BACKTRACE,		/* ask backend to log the current backtrace */
 
 	/* Recovery conflict reasons */
 	PROCSIG_RECOVERY_CONFLICT_DATABASE,
@@ -70,7 +71,7 @@ extern int	SendProcSignal(pid_t pid, ProcSignalReason reason,
 extern uint64 EmitProcSignalBarrier(ProcSignalBarrierType type);
 extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
-
+extern void ProcessLogBacktraceInterrupt(void);
 extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
 
 #endif							/* PROCSIGNAL_H */
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index f53607e12e..c63d25716a 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -453,4 +453,6 @@ extern void set_syslog_parameters(const char *ident, int facility);
  */
 extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
 
+extern pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
+
 #endif							/* ELOG_H */
diff --git a/src/test/regress/expected/backtrace.out b/src/test/regress/expected/backtrace.out
new file mode 100644
index 0000000000..88ee383d4d
--- /dev/null
+++ b/src/test/regress/expected/backtrace.out
@@ -0,0 +1,42 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtrace are logged and they are not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/expected/backtrace_1.out b/src/test/regress/expected/backtrace_1.out
new file mode 100644
index 0000000000..62beef8f51
--- /dev/null
+++ b/src/test/regress/expected/backtrace_1.out
@@ -0,0 +1,46 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtrace are logged and they are not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+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)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+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)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 017e962fed..ca8b2a6d8e 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -86,7 +86,7 @@ test: brin_bloom brin_multi
 # ----------
 # Another group of parallel tests
 # ----------
-test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort
+test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort backtrace
 
 # rules cannot run concurrently with any test that creates
 # a view or rule in the public schema
diff --git a/src/test/regress/sql/backtrace.sql b/src/test/regress/sql/backtrace.sql
new file mode 100644
index 0000000000..2688bc7330
--- /dev/null
+++ b/src/test/regress/sql/backtrace.sql
@@ -0,0 +1,30 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtrace are logged and they are not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+
+SELECT pg_log_backtrace(pg_backend_pid());
+
+CREATE ROLE regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+RESET ROLE;
+
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+
+DROP ROLE regress_log_backtrace;
-- 
2.17.0

0002-f.patchtext/x-diff; charset=us-asciiDownload
From 26ab02e0038d89f2d3716cda4bea1195b9b8c1ea Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Wed, 17 Nov 2021 21:49:23 -0600
Subject: [PATCH 2/4] f!

---
 doc/src/sgml/func.sgml                    | 27 +++++++++----------
 src/backend/postmaster/autovacuum.c       |  2 +-
 src/backend/storage/ipc/procsignal.c      |  4 +--
 src/backend/storage/ipc/signalfuncs.c     | 33 +++++++++++------------
 src/backend/tcop/postgres.c               |  2 +-
 src/backend/utils/error/elog.c            |  9 +++----
 src/include/catalog/pg_proc.dat           |  2 +-
 src/test/regress/expected/backtrace.out   |  2 +-
 src/test/regress/expected/backtrace_1.out |  2 +-
 src/test/regress/sql/backtrace.sql        |  2 +-
 10 files changed, 41 insertions(+), 44 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index a88583edfd..f2a1b701e6 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25360,14 +25360,14 @@ SELECT collation for ('foo' COLLATE "de_DE");
         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
-        <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.
+        <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 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.
+        walwriter, background writer or statistics collector processes. 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.
        </para></entry>
       </row>
 
@@ -25494,9 +25494,8 @@ postgres=# select pg_log_backtrace(pg_backend_pid());
  t
 (1 row)
 </programlisting>
-The backtrace will be logged to the log file if logging is enabled, if logging
-is disabled backtrace will be logged to the console where the postmaster was
-started. For example:
+The backtrace will be logged as specified by the logging configuration.
+For example:
 <screen>
 2021-01-27 11:33:50.247 IST [111735] LOG:  current backtrace:
         postgres: postgresdba postgres [local] SELECT(set_backtrace+0x38) [0xae06c5]
@@ -25518,9 +25517,9 @@ started. For example:
         /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2107bbd505]
         postgres: postgresdba postgres [local] SELECT() [0x4842a9]
 </screen>
-    You can get the file name and line number by using gdb/addr2line in
-    linux platforms, as a prerequisite users must ensure gdb/addr2line is
-    already installed:
+    You can get the file name and line number from the logged details by using gdb/addr2line in
+    linux platforms (users must ensure gdb/addr2line is
+    already installed).
 <programlisting>
 1)  "info line *address" from gdb on postgres executable. For example:
 gdb ./postgres
@@ -25535,7 +25534,7 @@ addr2line -e ./postgres 0x71c25d
     function will emit a warning and return false, for example:
 <screen>
 WARNING:  backtrace generation is not supported by this installation
-HINT:  You need to rebuild PostgreSQL using library containing backtrace_symbols.
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
  pg_log_backtrace 
 ------------------
  f
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 90111135ff..ca74937603 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -840,7 +840,7 @@ HandleAutoVacLauncherInterrupts(void)
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
 
-	/* Process logging backtrace */
+	/* Process a request to log our backtrace */
 	if (LogBacktracePending)
 		ProcessLogBacktraceInterrupt();
 
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index 3dac4a8f4b..e769a7f785 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -613,8 +613,8 @@ ProcessBarrierPlaceholder(void)
 }
 
 /*
- * HandleLogBacktraceInterrupt - Handle receipt of an interrupt indicating
- * logging of backtrace.
+ * HandleLogBacktraceInterrupt - Handle receipt of an interrupt requesting to
+ * log a backtrace.
  *
  * All the actual work is deferred to ProcessLogBacktraceInterrupt(),
  * because we cannot safely emit a log message inside the signal handler.
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index d042feefee..0315968aa2 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -303,23 +303,29 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 /*
  * pg_log_backtrace - log backtrace of backend process.
  *
- * By default, only superusers can log backtrace. Additional roles can be
+ * By default, only superusers can log a backtrace. Additional roles can be
  * permitted with GRANT.
  */
 Datum
 pg_log_backtrace(PG_FUNCTION_ARGS)
 {
-#ifdef HAVE_BACKTRACE_SYMBOLS
 	int			pid = PG_GETARG_INT32(0);
 	PGPROC	   *proc;
 
+#ifndef HAVE_BACKTRACE_SYMBOLS
+	ereport(WARNING,
+			errmsg("backtrace generation is not supported by this installation"),
+			errhint("You need to rebuild PostgreSQL using a library containing backtrace_symbols."));
+	PG_RETURN_BOOL(false);
+#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 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.
+	 * 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.
 	 */
 	proc = BackendPidGetProc(pid);
 	if (proc == NULL)
@@ -330,20 +336,13 @@ pg_log_backtrace(PG_FUNCTION_ARGS)
 	}
 
 	/*
-	 * Send SIGUSR1 to postgres backend whose pid matches pid by
-	 * setting PROCSIG_LOG_BACKTRACE, the backend process will log
-	 * the backtrace once the signal is received.
+	 * Send SIGUSR1 to postgres backend with the given pid, setting
+	 * PROCSIG_LOG_BACKTRACE, requesting the backend to log its backtrace.
 	 */
 	if (!SendProcSignal(pid, PROCSIG_LOG_BACKTRACE, proc->backendId))
 		PG_RETURN_BOOL(true);
-	else
-		ereport(WARNING,
-				(errmsg("could not send signal to process %d: %m", pid))); /* return false below */
-#else
-	ereport(WARNING,
-			errmsg("backtrace generation is not supported by this installation"),
-			errhint("You need to rebuild PostgreSQL using library containing backtrace_symbols."));
-#endif
 
+	ereport(WARNING,
+			(errmsg("could not send signal to process %d: %m", pid)));
 	PG_RETURN_BOOL(false);
 }
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 3a2b670d23..cb3d218449 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3367,7 +3367,7 @@ ProcessInterrupts(void)
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
 
-	/* Process logging backtrace */
+	/* Process a request to log our backtrace */
 	if (LogBacktracePending)
 		ProcessLogBacktraceInterrupt();
 }
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 6166965a39..dce0e2b155 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -948,7 +948,7 @@ errbacktrace(void)
  * Compute backtrace data and add it to the supplied ErrorData.  num_skip
  * specifies how many inner frames to skip.  Use this to avoid showing the
  * internal backtrace support functions in the backtrace.  This requires that
- * this and related functions are not inlined. If edata pointer is valid
+ * this and related functions are not inlined. If the edata pointer is valid,
  * backtrace information will be set in edata.
  */
 void
@@ -983,10 +983,9 @@ set_backtrace(ErrorData *edata, int num_skip)
 	else
 	{
 		/*
-		 * LOG_SERVER_ONLY is used intentionally to make sure this information
-		 * is not sent to client based on client_min_messages. We don't want
-		 * to mess up a different session as pg_log_backtrace will be
-		 * sending SIGNAL to a different backend.
+		 * LOG_SERVER_ONLY is used to make sure the backtrace is never
+		 * sent to client. We want to avoid messing up the other client
+		 * session.
 		 */
 		elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
 		pfree(errtrace.data);
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 2537edca12..498ee4e869 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11694,7 +11694,7 @@
   prosrc => 'brin_minmax_multi_summary_send' },
 
 # function to get the backtrace of server process
-{ oid => '6105', descr => 'log backtrace of process',
+{ oid => '6105', descr => 'log backtrace of server process',
   proname => 'pg_log_backtrace', provolatile => 'v', prorettype => 'bool',
   proargtypes => 'int4', prosrc => 'pg_log_backtrace' },
 
diff --git a/src/test/regress/expected/backtrace.out b/src/test/regress/expected/backtrace.out
index 88ee383d4d..dfb044fd1d 100644
--- a/src/test/regress/expected/backtrace.out
+++ b/src/test/regress/expected/backtrace.out
@@ -1,7 +1,7 @@
 --
 -- pg_log_backtrace()
 --
--- Backtrace are logged and they are not returned to the function.
+-- Backtraces are logged and not returned to the function.
 -- Furthermore, their contents can vary depending on the timing. However,
 -- we can at least verify that the code doesn't fail, and that the
 -- permissions are set properly.
diff --git a/src/test/regress/expected/backtrace_1.out b/src/test/regress/expected/backtrace_1.out
index 62beef8f51..f2c837965e 100644
--- a/src/test/regress/expected/backtrace_1.out
+++ b/src/test/regress/expected/backtrace_1.out
@@ -1,7 +1,7 @@
 --
 -- pg_log_backtrace()
 --
--- Backtrace are logged and they are not returned to the function.
+-- Backtraces are logged and not returned to the function.
 -- Furthermore, their contents can vary depending on the timing. However,
 -- we can at least verify that the code doesn't fail, and that the
 -- permissions are set properly.
diff --git a/src/test/regress/sql/backtrace.sql b/src/test/regress/sql/backtrace.sql
index 2688bc7330..a8465b017f 100644
--- a/src/test/regress/sql/backtrace.sql
+++ b/src/test/regress/sql/backtrace.sql
@@ -1,7 +1,7 @@
 --
 -- pg_log_backtrace()
 --
--- Backtrace are logged and they are not returned to the function.
+-- Backtraces are logged and not returned to the function.
 -- Furthermore, their contents can vary depending on the timing. However,
 -- we can at least verify that the code doesn't fail, and that the
 -- permissions are set properly.
-- 
2.17.0

0003-pg_log_backtrace-support-for-logging-backtrace-of-au.patchtext/x-diff; charset=us-asciiDownload
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

0004-f.patchtext/x-diff; charset=us-asciiDownload
From 326e640abc40f890f68248d7a3e0a9499e0d157c Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Wed, 17 Nov 2021 22:03:53 -0600
Subject: [PATCH 4/4] f!

---
 src/backend/postmaster/checkpointer.c | 2 +-
 src/backend/postmaster/interrupt.c    | 2 +-
 src/backend/postmaster/pgarch.c       | 2 +-
 src/backend/postmaster/startup.c      | 2 +-
 src/backend/postmaster/walwriter.c    | 2 +-
 src/backend/storage/ipc/signalfuncs.c | 2 +-
 6 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 7a25557702..31fb8abae4 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -582,7 +582,7 @@ HandleCheckpointerInterrupts(void)
 		proc_exit(0);			/* done */
 	}
 
-	/* Process logging backtrace */
+	/* Process a request to log our backtrace */
 	if (LogBacktracePending)
 		ProcessLogBacktraceInterrupt();
 }
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index 6298b48da3..2a75f89fc6 100644
--- a/src/backend/postmaster/interrupt.c
+++ b/src/backend/postmaster/interrupt.c
@@ -44,7 +44,7 @@ HandleMainLoopInterrupts(void)
 	if (ShutdownRequestPending)
 		proc_exit(0);
 
-	/* Process logging backtrace */
+	/* Process a request to log our backtrace */
 	if (LogBacktracePending)
 		ProcessLogBacktraceInterrupt();
 }
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 938c08b0eb..b24a8f0264 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -857,7 +857,7 @@ HandlePgArchInterrupts(void)
 		ProcessConfigFile(PGC_SIGHUP);
 	}
 
-	/* Process logging backtrace */
+	/* Process a request to log our backtrace */
 	if (LogBacktracePending)
 		ProcessLogBacktraceInterrupt();
 }
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index 41d40358bf..f920b73a4b 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -201,7 +201,7 @@ HandleStartupProcInterrupts(void)
 	if (ProcSignalBarrierPending)
 		ProcessProcSignalBarrier();
 
-	/* Process logging backtrace */
+	/* Process a request to log our backtrace */
 	if (LogBacktracePending)
 		ProcessLogBacktraceInterrupt();
 }
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 8bc404d17b..4ac1383e07 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -307,7 +307,7 @@ HandleWalWriterInterrupts(void)
 		proc_exit(0);
 	}
 
-	/* Process logging backtrace */
+	/* Process a request to log our backtrace */
 	if (LogBacktracePending)
 		ProcessLogBacktraceInterrupt();
 }
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index 562ff104bb..6bf1de27e2 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -344,7 +344,7 @@ pg_log_backtrace(PG_FUNCTION_ARGS)
 	}
 
 	/*
-	 * Only regular backends will have valid backend id, auxiliary processes
+	 * Only regular backends will have a valid backend id; auxiliary processes
 	 * don't.
 	 */
 	if (!aux_proc)
-- 
2.17.0

#86vignesh C
vignesh21@gmail.com
In reply to: Justin Pryzby (#85)
1 attachment(s)
Re: Printing backtrace of postgres processes

On Thu, Nov 18, 2021 at 9:52 PM Justin Pryzby <pryzby@telsasoft.com> wrote:

On Wed, Nov 17, 2021 at 08:12:44PM +0530, vignesh C wrote:

Attached v14 patch has the fixes for the same.

Thanks for updating the patch.

I cleaned up the docs and comments. I think this could be nearly "Ready".

If you like the changes in my "fixup" patch (0002 and 0004), you should be able
to apply my 0002 on top of your 0001. I'm sure it'll cause some conflicts with
your 2nd patch, though...

I have slightly modified and taken the changes. I have not taken a few
of the changes to keep it similar to pg_log_backend_memory_contexts.

This doesn't bump the catversion, since that would cause the patch to fail in
cfbot every time another commit updates catversion.

I have removed it, since it there in commit message it is ok.

Your 0001 patch allows printing backtraces of autovacuum, but the doc says it's
only for "backends". Should the change to autovacuum.c be moved to the 2nd
patch ? Or, why is autovacuum so special that it should be handled in the
first patch ?

I had separated the patches so that it is easier for review, I have
merged the changes as few rounds of review is done for the patch. Now
since the patch is merged, this change is handled.
The Attached v15 patch has the fixes for the same.

Regards,
Vignesh

Attachments:

v15-0001-Add-function-to-log-the-backtrace-of-the-specifi.patchtext/x-patch; charset=US-ASCII; name=v15-0001-Add-function-to-log-the-backtrace-of-the-specifi.patchDownload
From 66f7b2b5a42b3cbe347d227a64488cdcc025da8a Mon Sep 17 00:00:00 2001
From: Vignesh C <vignesh21@gmail.com>
Date: Tue, 9 Nov 2021 16:28:03 +0530
Subject: [PATCH v15] Add function to log the backtrace of the specified
 backend process.

This commit adds pg_log_backtrace() function that requests to log
the backtrace of the specified backend process.

Only superusers are allowed to request to log the backtrace
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.

On receipt of the request, at the next CHECK_FOR_INTERRUPTS(),
the target backend logs its backtrace at LOG_SERVER_ONLY level,
so that the backtrace will appear in the server log but not
be sent to the client.

Bump catalog version.
---
 doc/src/sgml/func.sgml                    | 80 +++++++++++++++++++++++
 src/backend/catalog/system_functions.sql  |  2 +
 src/backend/postmaster/autovacuum.c       |  4 ++
 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      | 40 ++++++++++++
 src/backend/storage/ipc/signalfuncs.c     | 66 +++++++++++++++++++
 src/backend/tcop/postgres.c               |  4 ++
 src/backend/utils/error/elog.c            | 19 ++++--
 src/backend/utils/init/globals.c          |  1 +
 src/include/catalog/pg_proc.dat           |  5 ++
 src/include/miscadmin.h                   |  1 +
 src/include/storage/procsignal.h          |  3 +-
 src/include/utils/elog.h                  |  2 +
 src/test/regress/expected/backtrace.out   | 53 +++++++++++++++
 src/test/regress/expected/backtrace_1.out | 59 +++++++++++++++++
 src/test/regress/parallel_schedule        |  2 +-
 src/test/regress/sql/backtrace.sql        | 39 +++++++++++
 21 files changed, 394 insertions(+), 6 deletions(-)
 create mode 100644 src/test/regress/expected/backtrace.out
 create mode 100644 src/test/regress/expected/backtrace_1.out
 create mode 100644 src/test/regress/sql/backtrace.sql

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 24447c0017..24a2b920dc 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25345,6 +25345,38 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_log_backtrace</primary>
+        </indexterm>
+        <function>pg_log_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Requests to log the backtrace of the
+        <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. This function cannot request
+        <glossterm linkend="glossary-postmaster">postmaster process</glossterm> or
+        <glossterm linkend="glossary-logger">logger</glossterm> or 
+        <glossterm linkend="glossary-stats-collector">statistics collector</glossterm>
+        (all other <glossterm linkend="glossary-auxiliary-proc">auxiliary processes</glossterm>
+        it can) for the backtrace. The backtrace will be logged at
+        <literal>LOG</literal> message level. 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
+        <xref linkend="guc-client-min-messages"/>. A backtrace identifies
+        which function the backend process is currently executing. This may be
+        useful to developers to diagnose stuck processes and other problems.
+        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.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -25458,6 +25490,54 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_log_backtrace</function> can be used to log the backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace
+------------------
+ t
+(1 row)
+</programlisting>
+The backtrace will be logged as specified by the logging configuration.
+For example:
+<screen>
+2021-01-27 11:33:50.247 IST [111735] LOG:  current backtrace:
+        postgres: postgresdba postgres [local] SELECT(set_backtrace+0x38) [0xae06c5]
+        postgres: postgresdba postgres [local] SELECT(ProcessInterrupts+0x788) [0x950c34]
+        postgres: postgresdba postgres [local] SELECT() [0x761e89]
+        postgres: postgresdba postgres [local] SELECT() [0x71bbda]
+        postgres: postgresdba postgres [local] SELECT() [0x71e380]
+        postgres: postgresdba postgres [local] SELECT(standard_ExecutorRun+0x1d6) [0x71c1fe]
+        postgres: postgresdba postgres [local] SELECT(ExecutorRun+0x55) [0x71c026]
+        postgres: postgresdba postgres [local] SELECT() [0x953fc5]
+        postgres: postgresdba postgres [local] SELECT(PortalRun+0x262) [0x953c7e]
+        postgres: postgresdba postgres [local] SELECT() [0x94db78]
+        postgres: postgresdba postgres [local] SELECT(PostgresMain+0x7d7) [0x951e72]
+        postgres: postgresdba postgres [local] SELECT() [0x896b2f]
+        postgres: postgresdba postgres [local] SELECT() [0x8964b5]
+        postgres: postgresdba postgres [local] SELECT() [0x892a79]
+        postgres: postgresdba postgres [local] SELECT(PostmasterMain+0x116b) [0x892350]
+        postgres: postgresdba postgres [local] SELECT() [0x795f72]
+        /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2107bbd505]
+        postgres: postgresdba postgres [local] SELECT() [0x4842a9]
+</screen>
+    You can get the file name and line number from the logged details by using
+    gdb/addr2line in linux platforms (users must ensure gdb/addr2line is
+    already installed).
+<programlisting>
+1)  "info line *address" from gdb on postgres executable. For example:
+gdb ./postgres
+(gdb) info line *0x71c25d
+Line 378 of "execMain.c" starts at address 0x71c25d <literal>&lt;</literal>standard_ExecutorRun+470<literal>&gt;</literal> and ends at 0x71c263 <literal>&lt;</literal>standard_ExecutorRun+476<literal>&gt;</literal>.
+OR 
+2) Using "addr2line -e postgres address", For example:
+addr2line -e ./postgres 0x71c25d
+/home/postgresdba/src/backend/executor/execMain.c:378
+</programlisting>
+   </para>
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 54c93b16c4..6e598f9bdb 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -701,6 +701,8 @@ REVOKE EXECUTE ON FUNCTION pg_ls_dir(text,boolean,boolean) FROM public;
 
 REVOKE EXECUTE ON FUNCTION pg_log_backend_memory_contexts(integer) FROM PUBLIC;
 
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer) FROM PUBLIC;
+
 --
 -- We also set up some things as accessible to standard roles.
 --
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 96332320a7..d640a44a81 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -840,6 +840,10 @@ HandleAutoVacLauncherInterrupts(void)
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
 
+	/* Process a request to log backtrace */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
+
 	/* Process sinval catchup interrupts that happened while sleeping */
 	ProcessCatchupInterrupt();
 }
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index be7366379d..69b288b6fd 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 a request to log backtrace */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 /*
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index dd9136a942..594ad66ced 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 a request to log backtrace */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 /*
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 3b33e01d95..9a237b678d 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 a request to log backtrace */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index 47ec737888..eac66a1939 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 a request to log backtrace */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 626fae8454..1e330efe50 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -306,4 +306,8 @@ HandleWalWriterInterrupts(void)
 
 		proc_exit(0);
 	}
+
+	/* Process a request to log backtrace */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index 6e69398cdd..af4e67e3b6 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -612,6 +612,43 @@ ProcessBarrierPlaceholder(void)
 	return true;
 }
 
+/*
+ * HandleLogBacktraceInterrupt - Handle receipt of an interrupt requesting to
+ * log a backtrace.
+ *
+ * All the actual work is deferred to ProcessLogBacktraceInterrupt(),
+ * because we cannot safely emit a log message inside the signal handler.
+ */
+static void
+HandleLogBacktraceInterrupt(void)
+{
+	InterruptPending = true;
+	LogBacktracePending = true;
+	/* latch will be set by procsignal_sigusr1_handler */
+}
+
+/*
+ * ProcessLogBacktraceInterrupt - Perform logging of backtrace of this
+ * backend process.
+ *
+ * Any backend that participates in ProcSignal signaling must arrange
+ * to call this function if we see LogBacktracePending set.
+ * CHECK_FOR_INTERRUPTS() or from process specific interrupt handlers.
+ */
+void
+ProcessLogBacktraceInterrupt(void)
+{
+	LogBacktracePending = false;
+
+	/*
+	 * Use LOG_SERVER_ONLY to prevent this message from being sent to the
+	 * connected client.
+	 */
+	ereport(LOG_SERVER_ONLY,
+			(errmsg("logging backtrace of PID %d", MyProcPid)));
+	set_backtrace(NULL, 0);
+}
+
 /*
  * CheckProcSignal - check to see if a particular reason has been
  * signaled, and clear the signal flag.  Should be called after receiving
@@ -661,6 +698,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_LOG_MEMORY_CONTEXT))
 		HandleLogMemoryContextInterrupt();
 
+	if (CheckProcSignal(PROCSIG_LOG_BACKTRACE))
+		HandleLogBacktraceInterrupt();
+
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
 
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index de69d60e79..287a205356 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -23,6 +23,7 @@
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
+#include "tcop/tcopprot.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 
@@ -298,3 +299,68 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_log_backtrace - log backtrace of backend process.
+ *
+ * By default, only superusers can log a backtrace. Additional roles can be
+ * permitted with GRANT.
+ */
+Datum
+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,
+			errmsg("backtrace generation is not supported by this installation"),
+			errhint("You need to rebuild PostgreSQL using a library containing backtrace_symbols."));
+	PG_RETURN_BOOL(false);
+#endif
+
+	/*
+	 * 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 consuming lots of CPU
+	 * cycles, that it might end on its own first and its backtrace not logged
+	 * is not a problem.
+	 */
+	proc = BackendPidGetProc(pid);
+	if (proc == NULL)
+	{
+		/* 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 a 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_LOG_BACKTRACE, the backend process will log
+	 * the backtrace once the signal is received.
+	 */
+	if (SendProcSignal(pid, PROCSIG_LOG_BACKTRACE, backendId))
+	{
+		ereport(WARNING,
+				(errmsg("could not send signal to process %d: %m", pid)));
+		PG_RETURN_BOOL(false);
+	}
+
+	PG_RETURN_BOOL(true);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 0775abe35d..cce2e56c80 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3366,6 +3366,10 @@ ProcessInterrupts(void)
 
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Process a request to log backtrace */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index f33729513a..dce0e2b155 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -172,7 +172,6 @@ static char formatted_log_time[FORMATTED_TS_LEN];
 
 
 static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
-static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
 static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
 static void write_console(const char *line, int len);
 static void setup_formatted_log_time(void);
@@ -949,9 +948,10 @@ errbacktrace(void)
  * Compute backtrace data and add it to the supplied ErrorData.  num_skip
  * specifies how many inner frames to skip.  Use this to avoid showing the
  * internal backtrace support functions in the backtrace.  This requires that
- * this and related functions are not inlined.
+ * this and related functions are not inlined. If the edata pointer is valid,
+ * backtrace information will be set in edata.
  */
-static void
+void
 set_backtrace(ErrorData *edata, int num_skip)
 {
 	StringInfoData errtrace;
@@ -978,7 +978,18 @@ set_backtrace(ErrorData *edata, int num_skip)
 						   "backtrace generation is not supported by this installation");
 #endif
 
-	edata->backtrace = errtrace.data;
+	if (edata)
+		edata->backtrace = errtrace.data;
+	else
+	{
+		/*
+		 * LOG_SERVER_ONLY is used to make sure the backtrace is never
+		 * sent to client. We want to avoid messing up the other client
+		 * session.
+		 */
+		elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
+		pfree(errtrace.data);
+	}
 }
 
 /*
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 381d9e548d..66570a902c 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -36,6 +36,7 @@ volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t IdleSessionTimeoutPending = false;
 volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile sig_atomic_t LogMemoryContextPending = false;
+volatile sig_atomic_t LogBacktracePending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 6412f369f1..498ee4e869 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11693,4 +11693,9 @@
   prorettype => 'bytea', proargtypes => 'pg_brin_minmax_multi_summary',
   prosrc => 'brin_minmax_multi_summary_send' },
 
+# function to get the backtrace of server process
+{ oid => '6105', descr => 'log backtrace of server process',
+  proname => 'pg_log_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_log_backtrace' },
+
 ]
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 90a3016065..8a7f5fba54 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -94,6 +94,7 @@ extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
 extern PGDLLIMPORT volatile sig_atomic_t LogMemoryContextPending;
+extern PGDLLIMPORT volatile sig_atomic_t LogBacktracePending;
 
 extern PGDLLIMPORT volatile sig_atomic_t CheckClientConnectionPending;
 extern PGDLLIMPORT volatile sig_atomic_t ClientConnectionLost;
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index eec186be2e..e14763820f 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -35,6 +35,7 @@ typedef enum
 	PROCSIG_WALSND_INIT_STOPPING,	/* ask walsenders to prepare for shutdown  */
 	PROCSIG_BARRIER,			/* global barrier interrupt  */
 	PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
+	PROCSIG_LOG_BACKTRACE,		/* ask backend to log the current backtrace */
 
 	/* Recovery conflict reasons */
 	PROCSIG_RECOVERY_CONFLICT_DATABASE,
@@ -70,7 +71,7 @@ extern int	SendProcSignal(pid_t pid, ProcSignalReason reason,
 extern uint64 EmitProcSignalBarrier(ProcSignalBarrierType type);
 extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
-
+extern void ProcessLogBacktraceInterrupt(void);
 extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
 
 #endif							/* PROCSIGNAL_H */
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index f53607e12e..c63d25716a 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -453,4 +453,6 @@ extern void set_syslog_parameters(const char *ident, int facility);
  */
 extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
 
+extern pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
+
 #endif							/* ELOG_H */
diff --git a/src/test/regress/expected/backtrace.out b/src/test/regress/expected/backtrace.out
new file mode 100644
index 0000000000..e5d5caab62
--- /dev/null
+++ b/src/test/regress/expected/backtrace.out
@@ -0,0 +1,53 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ 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
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/expected/backtrace_1.out b/src/test/regress/expected/backtrace_1.out
new file mode 100644
index 0000000000..c70472c5cf
--- /dev/null
+++ b/src/test/regress/expected/backtrace_1.out
@@ -0,0 +1,59 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ 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 a 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
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 017e962fed..ca8b2a6d8e 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -86,7 +86,7 @@ test: brin_bloom brin_multi
 # ----------
 # Another group of parallel tests
 # ----------
-test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort
+test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort backtrace
 
 # rules cannot run concurrently with any test that creates
 # a view or rule in the public schema
diff --git a/src/test/regress/sql/backtrace.sql b/src/test/regress/sql/backtrace.sql
new file mode 100644
index 0000000000..2b0a8e3195
--- /dev/null
+++ b/src/test/regress/sql/backtrace.sql
@@ -0,0 +1,39 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+
+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',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+RESET ROLE;
+
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+
+DROP ROLE regress_log_backtrace;
-- 
2.30.2

#87Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: vignesh C (#86)
Re: Printing backtrace of postgres processes

On Fri, Nov 19, 2021 at 4:07 PM vignesh C <vignesh21@gmail.com> wrote:

The Attached v15 patch has the fixes for the same.

Thanks. The v15 patch LGTM and the cf bot is happy hence marking it as RfC.

Regards,
Bharath Rupireddy.

#88Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: Bharath Rupireddy (#87)
1 attachment(s)
Re: Printing backtrace of postgres processes

On Sat, Nov 20, 2021 at 11:50 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Fri, Nov 19, 2021 at 4:07 PM vignesh C <vignesh21@gmail.com> wrote:

The Attached v15 patch has the fixes for the same.

Thanks. The v15 patch LGTM and the cf bot is happy hence marking it as RfC.

The patch was not applying because of the recent commit [1]commit 790fbda902093c71ae47bff1414799cd716abb80 Author: Fujii Masao <fujii@postgresql.org> Date: Tue Jan 11 23:19:59 2022 +0900. I took
this opportunity and tried a bunch of things without modifying the
core logic of the pg_log_backtrace feature that Vignesh has worked on.

I did following - moved the duplicate code to a new function
CheckPostgresProcessId which can be used by pg_log_backtrace,
pg_log_memory_contexts, pg_signal_backend and pg_log_query_plan ([2]/messages/by-id/20220114063846.7jygvulyu6g23kdv@jrouhaud),
modified the code comments, docs and tests to be more in sync with the
commit [1]commit 790fbda902093c71ae47bff1414799cd716abb80 Author: Fujii Masao <fujii@postgresql.org> Date: Tue Jan 11 23:19:59 2022 +0900, moved two of ProcessLogBacktraceInterrupt calls (archiver
and wal writer) to their respective interrupt handlers. Here's the v16
version that I've come up with.

[1]: commit 790fbda902093c71ae47bff1414799cd716abb80 Author: Fujii Masao <fujii@postgresql.org> Date: Tue Jan 11 23:19:59 2022 +0900
commit 790fbda902093c71ae47bff1414799cd716abb80
Author: Fujii Masao <fujii@postgresql.org>
Date: Tue Jan 11 23:19:59 2022 +0900

Enhance pg_log_backend_memory_contexts() for auxiliary processes.

[2]: /messages/by-id/20220114063846.7jygvulyu6g23kdv@jrouhaud

Regards,
Bharath Rupireddy.

Attachments:

v16-0001-Add-function-to-log-the-backtrace-of-the-specifi.patchapplication/octet-stream; name=v16-0001-Add-function-to-log-the-backtrace-of-the-specifi.patchDownload
From e933c9ae6feca336fe96b600a20cd90b9d264aa2 Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Fri, 14 Jan 2022 10:28:31 +0000
Subject: [PATCH v16] Add function to log the backtrace of the specified
 postgres process.

This commit adds pg_log_backtrace() function that requests to log
the backtrace of the specified backend or auxiliary process except
logger and statistic collector.

Only superusers are allowed to request to log the backtrace
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.

On receipt of the request, at the next CHECK_FOR_INTERRUPTS(),
the target backend logs its backtrace at LOG_SERVER_ONLY level,
so that the backtrace will appear in the server log but not
be sent to the client.

Bump catalog version.

Authors: Vignesh C, Bharath Rupireddy
---
 doc/src/sgml/func.sgml                    | 72 +++++++++++++++++++++
 src/backend/catalog/system_functions.sql  |  2 +
 src/backend/postmaster/autovacuum.c       |  4 ++
 src/backend/postmaster/checkpointer.c     |  4 ++
 src/backend/postmaster/interrupt.c        |  4 ++
 src/backend/postmaster/pgarch.c           | 10 ++-
 src/backend/postmaster/startup.c          |  4 ++
 src/backend/postmaster/walwriter.c        |  4 ++
 src/backend/storage/ipc/procarray.c       | 63 ++++++++++++++++++
 src/backend/storage/ipc/procsignal.c      | 40 ++++++++++++
 src/backend/storage/ipc/signalfuncs.c     | 78 +++++++++++++++++------
 src/backend/tcop/postgres.c               |  4 ++
 src/backend/utils/adt/mcxtfuncs.c         | 39 +++---------
 src/backend/utils/error/elog.c            | 19 ++++--
 src/backend/utils/init/globals.c          |  1 +
 src/include/catalog/pg_proc.dat           |  5 ++
 src/include/miscadmin.h                   |  1 +
 src/include/storage/procarray.h           |  1 +
 src/include/storage/procsignal.h          |  3 +-
 src/include/utils/elog.h                  |  2 +
 src/test/regress/expected/backtrace.out   | 49 ++++++++++++++
 src/test/regress/expected/backtrace_1.out | 55 ++++++++++++++++
 src/test/regress/parallel_schedule        |  2 +-
 src/test/regress/sql/backtrace.sql        | 33 ++++++++++
 24 files changed, 439 insertions(+), 60 deletions(-)
 create mode 100644 src/test/regress/expected/backtrace.out
 create mode 100644 src/test/regress/expected/backtrace_1.out
 create mode 100644 src/test/regress/sql/backtrace.sql

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 391d01bcf3..c979fa1fcc 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25326,6 +25326,30 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_log_backtrace</primary>
+        </indexterm>
+        <function>pg_log_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Requests to log the backtrace of the backend with the
+        specified process ID.  This function can send the request to
+        backends and auxiliary processes except logger and statistics
+        collector.  These backtraces 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 identifies
+        which function a process is currently executing and it may be useful
+        for developers to diagnose stuck processes and other problems. This
+        function is supported only if PostgreSQL was built with the ability to
+        capture backtraces, otherwise it will emit a warning.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -25545,6 +25569,54 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_log_backtrace</function> can be used to log the backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace
+------------------
+ t
+(1 row)
+</programlisting>
+The backtrace will be logged as specified by the logging configuration.
+For example:
+<screen>
+2021-01-27 11:33:50.247 IST [111735] LOG:  current backtrace:
+        postgres: postgresdba postgres [local] SELECT(set_backtrace+0x38) [0xae06c5]
+        postgres: postgresdba postgres [local] SELECT(ProcessInterrupts+0x788) [0x950c34]
+        postgres: postgresdba postgres [local] SELECT() [0x761e89]
+        postgres: postgresdba postgres [local] SELECT() [0x71bbda]
+        postgres: postgresdba postgres [local] SELECT() [0x71e380]
+        postgres: postgresdba postgres [local] SELECT(standard_ExecutorRun+0x1d6) [0x71c1fe]
+        postgres: postgresdba postgres [local] SELECT(ExecutorRun+0x55) [0x71c026]
+        postgres: postgresdba postgres [local] SELECT() [0x953fc5]
+        postgres: postgresdba postgres [local] SELECT(PortalRun+0x262) [0x953c7e]
+        postgres: postgresdba postgres [local] SELECT() [0x94db78]
+        postgres: postgresdba postgres [local] SELECT(PostgresMain+0x7d7) [0x951e72]
+        postgres: postgresdba postgres [local] SELECT() [0x896b2f]
+        postgres: postgresdba postgres [local] SELECT() [0x8964b5]
+        postgres: postgresdba postgres [local] SELECT() [0x892a79]
+        postgres: postgresdba postgres [local] SELECT(PostmasterMain+0x116b) [0x892350]
+        postgres: postgresdba postgres [local] SELECT() [0x795f72]
+        /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2107bbd505]
+        postgres: postgresdba postgres [local] SELECT() [0x4842a9]
+</screen>
+    You can get the file name and line number from the logged details by using
+    gdb/addr2line in linux platforms (users must ensure gdb/addr2line is
+    already installed).
+<programlisting>
+1)  "info line *address" from gdb on postgres executable. For example:
+gdb ./postgres
+(gdb) info line *0x71c25d
+Line 378 of "execMain.c" starts at address 0x71c25d <literal>&lt;</literal>standard_ExecutorRun+470<literal>&gt;</literal> and ends at 0x71c263 <literal>&lt;</literal>standard_ExecutorRun+476<literal>&gt;</literal>.
+OR 
+2) Using "addr2line -e postgres address", For example:
+addr2line -e ./postgres 0x71c25d
+/home/postgresdba/src/backend/executor/execMain.c:378
+</programlisting>
+   </para>
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index fd1421788e..a9e5ebce15 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -711,6 +711,8 @@ REVOKE EXECUTE ON FUNCTION pg_ls_logicalmapdir() FROM PUBLIC;
 
 REVOKE EXECUTE ON FUNCTION pg_ls_replslotdir(text) FROM PUBLIC;
 
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer) FROM PUBLIC;
+
 --
 -- We also set up some things as accessible to standard roles.
 --
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 681ef91b81..a572412f2a 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -841,6 +841,10 @@ HandleAutoVacLauncherInterrupts(void)
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
 
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
+
 	/* Process sinval catchup interrupts that happened while sleeping */
 	ProcessCatchupInterrupt();
 }
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 23f691cd47..013b16ca82 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -581,6 +581,10 @@ HandleCheckpointerInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 /*
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index 3f412dad2e..84b1de4967 100644
--- a/src/backend/postmaster/interrupt.c
+++ b/src/backend/postmaster/interrupt.c
@@ -48,6 +48,10 @@ HandleMainLoopInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 /*
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 1121e4fb29..d146719188 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -862,9 +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, config update and request for logging of
- * memory contexts, but not shutdown request because how to handle
- * shutdown request is different between those loops.
+ * It checks for barrier events, config update, request for logging of
+ * memory contexts and backtrace, but not shutdown request because how to
+ * handle shutdown request is different between those loops.
  */
 static void
 HandlePgArchInterrupts(void)
@@ -881,4 +881,8 @@ HandlePgArchInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index 9bae16bfc7..76425cf079 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -205,6 +205,10 @@ HandleStartupProcInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 102fa2a089..52d5a82ecb 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -310,4 +310,8 @@ HandleWalWriterInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 3be6040289..c773351b66 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -3135,6 +3135,69 @@ HaveVirtualXIDsDelayingChkpt(VirtualTransactionId *vxids, int nvxids)
 	return result;
 }
 
+
+/*
+ * CheckPostgresProcessId -- check if the process with given pid is a backend
+ * or an auxiliary process.
+ *
+
+ */
+bool
+CheckPostgresProcessId(int pid, bool chk_auxiliary_proc, PGPROC **proc,
+					   BackendId *backendId)
+{
+	PGPROC	   *proc_l;
+
+	/*
+	 * Get backend id from PGPROC for a backend. Since auxiliary processes
+	 * (except the startup process) don't have a valid backend id, return
+	 * InvalidBackendId.
+	 */
+	if (backendId)
+		*backendId = InvalidBackendId;
+
+	/* See if the process with given pid is a backend */
+	proc_l = BackendPidGetProc(pid);
+
+	if (proc_l && backendId)
+		*backendId = proc_l->backendId;
+	else if (chk_auxiliary_proc)
+	{
+		/* See if the process with given pid is an auxiliary process */
+		proc_l = AuxiliaryPidGetProc(pid);
+	}
+
+	if (proc)
+		*proc = proc_l;
+
+	/*
+	 * 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_l == NULL)
+	{
+		/*
+		 * This is just a warning so a loop-through-resultset will not abort
+		 * if one backend terminated on its own during the run.
+		 */
+		if (chk_auxiliary_proc)
+			ereport(WARNING,
+					(errmsg("PID %d is not a PostgreSQL server process", pid)));
+		else
+			ereport(WARNING,
+					(errmsg("PID %d is not a PostgreSQL backend process", pid)));
+
+		return false;
+	}
+
+	return true;
+}
+
 /*
  * BackendPidGetProc -- get a backend's PGPROC given its PID
  *
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index f1c8ff8f9e..862eaa5f0d 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -612,6 +612,43 @@ ProcessBarrierPlaceholder(void)
 	return true;
 }
 
+/*
+ * HandleLogBacktraceInterrupt - Handle receipt of an interrupt requesting to
+ * log a backtrace.
+ *
+ * All the actual work is deferred to ProcessLogBacktraceInterrupt(),
+ * because we cannot safely emit a log message inside the signal handler.
+ */
+static void
+HandleLogBacktraceInterrupt(void)
+{
+	InterruptPending = true;
+	LogBacktracePending = true;
+	/* latch will be set by procsignal_sigusr1_handler */
+}
+
+/*
+ * ProcessLogBacktraceInterrupt - Perform logging of backtrace of this
+ * backend process.
+ *
+ * Any backend that participates in ProcSignal signaling must arrange
+ * to call this function if we see LogBacktracePending set.
+ * CHECK_FOR_INTERRUPTS() or from process specific interrupt handlers.
+ */
+void
+ProcessLogBacktraceInterrupt(void)
+{
+	LogBacktracePending = false;
+
+	/*
+	 * Use LOG_SERVER_ONLY to prevent this message from being sent to the
+	 * connected client.
+	 */
+	ereport(LOG_SERVER_ONLY,
+			(errmsg("logging backtrace of PID %d", MyProcPid)));
+	set_backtrace(NULL, 0);
+}
+
 /*
  * CheckProcSignal - check to see if a particular reason has been
  * signaled, and clear the signal flag.  Should be called after receiving
@@ -661,6 +698,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_LOG_MEMORY_CONTEXT))
 		HandleLogMemoryContextInterrupt();
 
+	if (CheckProcSignal(PROCSIG_LOG_BACKTRACE))
+		HandleLogBacktraceInterrupt();
+
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
 
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index 6e310b14eb..95fa3e11c7 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -48,31 +48,16 @@
 static int
 pg_signal_backend(int pid, int sig)
 {
-	PGPROC	   *proc = BackendPidGetProc(pid);
+	PGPROC  *proc;
+	bool	result;
+
+	result = CheckPostgresProcessId(pid, false, &proc, NULL);
 
 	/*
-	 * 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 so far all the callers of
-	 * this mechanism involve some request for ending the process anyway, that
-	 * it might end on its own first is not a problem.
-	 *
-	 * Note that proc will also be NULL if the pid refers to an auxiliary
-	 * process or the postmaster (neither of which can be signaled via
-	 * pg_signal_backend()).
+	 * CheckPostgresProcessId has logged a WARNING, just return from here.
 	 */
-	if (proc == NULL)
-	{
-		/*
-		 * This is just a warning so a loop-through-resultset will not abort
-		 * if one backend terminated on its own during the run.
-		 */
-		ereport(WARNING,
-				(errmsg("PID %d is not a PostgreSQL backend process", pid)));
-
+	if (!result)
 		return SIGNAL_BACKEND_ERROR;
-	}
 
 	/* Only allow superusers to signal superuser-owned backends. */
 	if (superuser_arg(proc->roleId) && !superuser())
@@ -303,3 +288,54 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_log_backtrace
+ *		Signal a backend or an auxiliary process to log its 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 and which can lead to denial of
+ * service. Additional roles can be permitted with GRANT.
+ *
+ * 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 backtrace.
+ */
+Datum
+pg_log_backtrace(PG_FUNCTION_ARGS)
+{
+	int			pid = PG_GETARG_INT32(0);
+	BackendId   backendId;
+	bool		result;
+
+#ifndef HAVE_BACKTRACE_SYMBOLS
+	ereport(WARNING,
+			errmsg("backtrace generation is not supported by this installation"),
+			errhint("You need to rebuild PostgreSQL using a library containing backtrace_symbols."));
+	PG_RETURN_BOOL(false);
+#endif
+
+	result = CheckPostgresProcessId(pid, true, NULL, &backendId);
+
+	/*
+	 * CheckPostgresProcessId has logged a WARNING, just return from here.
+	 */
+	if (!result)
+		PG_RETURN_BOOL(false);
+
+	/*
+	 * If the given process is a backend, its backend id from PGPROC is used in
+	 * SendProcSignal() later to speed up the operation. Otherwise,
+	 * InvalidBackendId is used because auxiliary processes (except the startup
+	 * process) don't have a valid backend id.
+	 */
+	if (SendProcSignal(pid, PROCSIG_LOG_BACKTRACE, backendId))
+	{
+		ereport(WARNING,
+				(errmsg("could not send signal to process %d: %m", pid)));
+		PG_RETURN_BOOL(false);
+	}
+
+	PG_RETURN_BOOL(true);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index fda2e9360e..49d7f4a765 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3367,6 +3367,10 @@ ProcessInterrupts(void)
 
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index 28cb9d3ff1..1507e63d8f 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -175,44 +175,23 @@ Datum
 pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
 {
 	int			pid = PG_GETARG_INT32(0);
-	PGPROC	   *proc;
 	BackendId	backendId = InvalidBackendId;
+	bool		result;
 
-	proc = BackendPidGetProc(pid);
+	result = CheckPostgresProcessId(pid, true, NULL, &backendId);
 
 	/*
-	 * 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.
+	 * CheckPostgresProcessId has logged a WARNING, just return from here.
 	 */
-	if (proc != NULL)
-		backendId = proc->backendId;
-	else
-		proc = AuxiliaryPidGetProc(pid);
+	if (!result)
+		PG_RETURN_BOOL(false);
 
 	/*
-	 * 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 the given process is a backend, its backend id from PGPROC is used in
+	 * SendProcSignal() later to speed up the operation. Otherwise,
+	 * InvalidBackendId is used because auxiliary processes (except the startup
+	 * process) don't have a valid backend id.
 	 */
-	if (proc == NULL)
-	{
-		/*
-		 * This is just a warning so a loop-through-resultset will not abort
-		 * if one backend terminated on its own during the run.
-		 */
-		ereport(WARNING,
-				(errmsg("PID %d is not a PostgreSQL server process", pid)));
-		PG_RETURN_BOOL(false);
-	}
-
 	if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, backendId) < 0)
 	{
 		/* Again, just a warning to allow loops */
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 4db41ba564..7de200da0e 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -172,7 +172,6 @@ static char formatted_log_time[FORMATTED_TS_LEN];
 
 
 static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
-static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
 static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
 static void write_console(const char *line, int len);
 static const char *process_log_prefix_padding(const char *p, int *padding);
@@ -944,9 +943,10 @@ errbacktrace(void)
  * Compute backtrace data and add it to the supplied ErrorData.  num_skip
  * specifies how many inner frames to skip.  Use this to avoid showing the
  * internal backtrace support functions in the backtrace.  This requires that
- * this and related functions are not inlined.
+ * this and related functions are not inlined. If the edata pointer is valid,
+ * backtrace information will be set in edata.
  */
-static void
+void
 set_backtrace(ErrorData *edata, int num_skip)
 {
 	StringInfoData errtrace;
@@ -973,7 +973,18 @@ set_backtrace(ErrorData *edata, int num_skip)
 						   "backtrace generation is not supported by this installation");
 #endif
 
-	edata->backtrace = errtrace.data;
+	if (edata)
+		edata->backtrace = errtrace.data;
+	else
+	{
+		/*
+		 * LOG_SERVER_ONLY is used to make sure the backtrace is never
+		 * sent to client. We want to avoid messing up the other client
+		 * session.
+		 */
+		elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
+		pfree(errtrace.data);
+	}
 }
 
 /*
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index c26a1a73df..3ef4888e93 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -36,6 +36,7 @@ volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t IdleSessionTimeoutPending = false;
 volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile sig_atomic_t LogMemoryContextPending = false;
+volatile sig_atomic_t LogBacktracePending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index d6bf1f3274..19c5586c0e 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11715,4 +11715,9 @@
   prorettype => 'bytea', proargtypes => 'pg_brin_minmax_multi_summary',
   prosrc => 'brin_minmax_multi_summary_send' },
 
+# function to get the backtrace of server process
+{ oid => '6105', descr => 'log backtrace of server process',
+  proname => 'pg_log_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_log_backtrace' },
+
 ]
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 02276d3edd..b94287fad0 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -94,6 +94,7 @@ extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
 extern PGDLLIMPORT volatile sig_atomic_t LogMemoryContextPending;
+extern PGDLLIMPORT volatile sig_atomic_t LogBacktracePending;
 
 extern PGDLLIMPORT volatile sig_atomic_t CheckClientConnectionPending;
 extern PGDLLIMPORT volatile sig_atomic_t ClientConnectionLost;
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index e03692053e..e9a17373b5 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -66,6 +66,7 @@ extern PGPROC *BackendPidGetProc(int pid);
 extern PGPROC *BackendPidGetProcWithLock(int pid);
 extern int	BackendXidGetPid(TransactionId xid);
 extern bool IsBackendPid(int pid);
+extern bool CheckPostgresProcessId(int pid, bool chk_auxiliary_proc, PGPROC **proc, BackendId *backendId);
 
 extern VirtualTransactionId *GetCurrentVirtualXIDs(TransactionId limitXmin,
 												   bool excludeXmin0, bool allDbs, int excludeVacuum,
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index a121e65066..3185ff11da 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -35,6 +35,7 @@ typedef enum
 	PROCSIG_WALSND_INIT_STOPPING,	/* ask walsenders to prepare for shutdown  */
 	PROCSIG_BARRIER,			/* global barrier interrupt  */
 	PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
+	PROCSIG_LOG_BACKTRACE,		/* ask backend to log the current backtrace */
 
 	/* Recovery conflict reasons */
 	PROCSIG_RECOVERY_CONFLICT_DATABASE,
@@ -70,7 +71,7 @@ extern int	SendProcSignal(pid_t pid, ProcSignalReason reason,
 extern uint64 EmitProcSignalBarrier(ProcSignalBarrierType type);
 extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
-
+extern void ProcessLogBacktraceInterrupt(void);
 extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
 
 #endif							/* PROCSIGNAL_H */
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 5bc38663cb..dde51bf7bf 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -465,4 +465,6 @@ extern void set_syslog_parameters(const char *ident, int facility);
  */
 extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
 
+extern pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
+
 #endif							/* ELOG_H */
diff --git a/src/test/regress/expected/backtrace.out b/src/test/regress/expected/backtrace.out
new file mode 100644
index 0000000000..2184a99483
--- /dev/null
+++ b/src/test/regress/expected/backtrace.out
@@ -0,0 +1,49 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/expected/backtrace_1.out b/src/test/regress/expected/backtrace_1.out
new file mode 100644
index 0000000000..fe9523f89d
--- /dev/null
+++ b/src/test/regress/expected/backtrace_1.out
@@ -0,0 +1,55 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 5b0c73d7e3..ac58cceac3 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -89,7 +89,7 @@ test: brin_bloom brin_multi
 # ----------
 # Another group of parallel tests
 # ----------
-test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort
+test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort backtrace
 
 # rules cannot run concurrently with any test that creates
 # a view or rule in the public schema
diff --git a/src/test/regress/sql/backtrace.sql b/src/test/regress/sql/backtrace.sql
new file mode 100644
index 0000000000..d74b1016ae
--- /dev/null
+++ b/src/test/regress/sql/backtrace.sql
@@ -0,0 +1,33 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+
+SELECT pg_log_backtrace(pg_backend_pid());
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+
+CREATE ROLE regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+RESET ROLE;
+
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+
+DROP ROLE regress_log_backtrace;
-- 
2.25.1

#89torikoshia
torikoshia@oss.nttdata.com
In reply to: Bharath Rupireddy (#88)
Re: Printing backtrace of postgres processes

On 2022-01-14 19:48, Bharath Rupireddy wrote:

On Sat, Nov 20, 2021 at 11:50 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Fri, Nov 19, 2021 at 4:07 PM vignesh C <vignesh21@gmail.com> wrote:

The Attached v15 patch has the fixes for the same.

Thanks. The v15 patch LGTM and the cf bot is happy hence marking it as
RfC.

The patch was not applying because of the recent commit [1]. I took
this opportunity and tried a bunch of things without modifying the
core logic of the pg_log_backtrace feature that Vignesh has worked on.

I did following - moved the duplicate code to a new function
CheckPostgresProcessId which can be used by pg_log_backtrace,
pg_log_memory_contexts, pg_signal_backend and pg_log_query_plan ([2]),

Thanks for refactoring!
I'm going to use it for pg_log_query_plan after this patch is merged.

modified the code comments, docs and tests to be more in sync with the
commit [1], moved two of ProcessLogBacktraceInterrupt calls (archiver
and wal writer) to their respective interrupt handlers. Here's the v16
version that I've come up with.

I have some minor comments.

+</screen>
+    You can get the file name and line number from the logged details 
by using
+    gdb/addr2line in linux platforms (users must ensure gdb/addr2line 
is
+    already installed).
+<programlisting>
+1)  "info line *address" from gdb on postgres executable. For example:
+gdb ./postgres
+(gdb) info line *0x71c25d
+Line 378 of "execMain.c" starts at address 0x71c25d 
<literal>&lt;</literal>standard_ExecutorRun+470<literal>&gt;</literal> 
and ends at 0x71c263     
<literal>&lt;</literal>standard_ExecutorRun+476<literal>&gt;</literal>.
+OR
+2) Using "addr2line -e postgres address", For example:
+addr2line -e ./postgres 0x71c25d
+/home/postgresdba/src/backend/executor/execMain.c:378
+</programlisting>
+   </para>
+

Isn't it better to remove line 1) and 2) from <programlisting>?
I just glanced at the existing sgml, but <programlisting> seems to
contain only codes.

+ * CheckPostgresProcessId -- check if the process with given pid is a 
backend
+ * or an auxiliary process.
+ *
+
+ */

Isn't the 4th line needless?

BTW, when I saw the name of this function, I thought it just checks if
the specified pid is PostgreSQL process or not.
Since it returns the pointer to the PGPROC or BackendId of the PID, it
might be kind to write comments about it.

--
Regards,

--
Atsushi Torikoshi
NTT DATA CORPORATION

#90Fujii Masao
masao.fujii@oss.nttdata.com
In reply to: torikoshia (#89)
Re: Printing backtrace of postgres processes

On 2022/01/24 16:35, torikoshia wrote:

On 2022-01-14 19:48, Bharath Rupireddy wrote:

On Sat, Nov 20, 2021 at 11:50 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Fri, Nov 19, 2021 at 4:07 PM vignesh C <vignesh21@gmail.com> wrote:

The Attached v15 patch has the fixes for the same.

Thanks. The v15 patch LGTM and the cf bot is happy hence marking it as RfC.

The patch was not applying because of the recent commit [1]. I took
this opportunity and tried a bunch of things without modifying the
core logic of the pg_log_backtrace feature that Vignesh has worked on.

I have one question about this feature. When the PID of auxiliary process like archiver is specified, probably the function always reports the same result, doesn't it? Because, for example, the archiver always logs its backtrace in HandlePgArchInterrupts().

Regards,

--
Fujii Masao
Advanced Computing Technology Center
Research and Development Headquarters
NTT DATA CORPORATION

#91vignesh C
vignesh21@gmail.com
In reply to: Fujii Masao (#90)
Re: Printing backtrace of postgres processes

On Mon, Jan 24, 2022 at 10:06 PM Fujii Masao
<masao.fujii@oss.nttdata.com> wrote:

On 2022/01/24 16:35, torikoshia wrote:

On 2022-01-14 19:48, Bharath Rupireddy wrote:

On Sat, Nov 20, 2021 at 11:50 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Fri, Nov 19, 2021 at 4:07 PM vignesh C <vignesh21@gmail.com> wrote:

The Attached v15 patch has the fixes for the same.

Thanks. The v15 patch LGTM and the cf bot is happy hence marking it as RfC.

The patch was not applying because of the recent commit [1]. I took
this opportunity and tried a bunch of things without modifying the
core logic of the pg_log_backtrace feature that Vignesh has worked on.

I have one question about this feature. When the PID of auxiliary process like archiver is specified, probably the function always reports the same result, doesn't it? Because, for example, the archiver always logs its backtrace in HandlePgArchInterrupts().

It can be either from HandlePgArchInterrupts in pgarch_MainLoop or
from HandlePgArchInterrupts in pgarch_ArchiverCopyLoop, since the
archiver functionality is mainly in these functions.

Regards,
Vignesh

#92vignesh C
vignesh21@gmail.com
In reply to: torikoshia (#89)
1 attachment(s)
Re: Printing backtrace of postgres processes

On Mon, Jan 24, 2022 at 1:05 PM torikoshia <torikoshia@oss.nttdata.com> wrote:

On 2022-01-14 19:48, Bharath Rupireddy wrote:

On Sat, Nov 20, 2021 at 11:50 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Fri, Nov 19, 2021 at 4:07 PM vignesh C <vignesh21@gmail.com> wrote:

The Attached v15 patch has the fixes for the same.

Thanks. The v15 patch LGTM and the cf bot is happy hence marking it as
RfC.

The patch was not applying because of the recent commit [1]. I took
this opportunity and tried a bunch of things without modifying the
core logic of the pg_log_backtrace feature that Vignesh has worked on.

I did following - moved the duplicate code to a new function
CheckPostgresProcessId which can be used by pg_log_backtrace,
pg_log_memory_contexts, pg_signal_backend and pg_log_query_plan ([2]),

Thanks for refactoring!
I'm going to use it for pg_log_query_plan after this patch is merged.

modified the code comments, docs and tests to be more in sync with the
commit [1], moved two of ProcessLogBacktraceInterrupt calls (archiver
and wal writer) to their respective interrupt handlers. Here's the v16
version that I've come up with.

I have some minor comments.

+</screen>
+    You can get the file name and line number from the logged details
by using
+    gdb/addr2line in linux platforms (users must ensure gdb/addr2line
is
+    already installed).
+<programlisting>
+1)  "info line *address" from gdb on postgres executable. For example:
+gdb ./postgres
+(gdb) info line *0x71c25d
+Line 378 of "execMain.c" starts at address 0x71c25d
<literal>&lt;</literal>standard_ExecutorRun+470<literal>&gt;</literal>
and ends at 0x71c263
<literal>&lt;</literal>standard_ExecutorRun+476<literal>&gt;</literal>.
+OR
+2) Using "addr2line -e postgres address", For example:
+addr2line -e ./postgres 0x71c25d
+/home/postgresdba/src/backend/executor/execMain.c:378
+</programlisting>
+   </para>
+

Isn't it better to remove line 1) and 2) from <programlisting>?
I just glanced at the existing sgml, but <programlisting> seems to
contain only codes.

Modified

+ * CheckPostgresProcessId -- check if the process with given pid is a
backend
+ * or an auxiliary process.
+ *
+
+ */

Isn't the 4th line needless?

Modified

BTW, when I saw the name of this function, I thought it just checks if
the specified pid is PostgreSQL process or not.
Since it returns the pointer to the PGPROC or BackendId of the PID, it
might be kind to write comments about it.

Modified

Thanks for the comments, attached v17 patch has the fix for the same.

Regards,
Vignesh

Attachments:

v17-0001-Add-function-to-log-the-backtrace-of-the-specifi.patchtext/x-patch; charset=US-ASCII; name=v17-0001-Add-function-to-log-the-backtrace-of-the-specifi.patchDownload
From 5859e2c34f73206cf48a40d9e429b02f923b7081 Mon Sep 17 00:00:00 2001
From: Vigneshwaran C <vignesh21@gmail.com>
Date: Tue, 25 Jan 2022 08:21:22 +0530
Subject: [PATCH v17] Add function to log the backtrace of the specified
 postgres process.

This commit adds pg_log_backtrace() function that requests to log
the backtrace of the specified backend or auxiliary process except
logger and statistic collector.

Only superusers are allowed to request to log the backtrace
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.

On receipt of the request, at the next CHECK_FOR_INTERRUPTS(),
the target backend logs its backtrace at LOG_SERVER_ONLY level,
so that the backtrace will appear in the server log but not
be sent to the client.

Bump catalog version.

Authors: Vignesh C, Bharath Rupireddy
---
 doc/src/sgml/func.sgml                    | 62 +++++++++++++++++++
 src/backend/catalog/system_functions.sql  |  2 +
 src/backend/postmaster/autovacuum.c       |  4 ++
 src/backend/postmaster/checkpointer.c     |  4 ++
 src/backend/postmaster/interrupt.c        |  4 ++
 src/backend/postmaster/pgarch.c           | 10 +++-
 src/backend/postmaster/startup.c          |  4 ++
 src/backend/postmaster/walwriter.c        |  4 ++
 src/backend/storage/ipc/procarray.c       | 56 +++++++++++++++++
 src/backend/storage/ipc/procsignal.c      | 40 +++++++++++++
 src/backend/storage/ipc/signalfuncs.c     | 73 ++++++++++++++++-------
 src/backend/tcop/postgres.c               |  4 ++
 src/backend/utils/adt/mcxtfuncs.c         | 40 +++----------
 src/backend/utils/error/elog.c            | 19 ++++--
 src/backend/utils/init/globals.c          |  1 +
 src/include/catalog/pg_proc.dat           |  5 ++
 src/include/miscadmin.h                   |  1 +
 src/include/storage/procarray.h           |  2 +
 src/include/storage/procsignal.h          |  3 +-
 src/include/utils/elog.h                  |  2 +
 src/test/regress/expected/backtrace.out   | 49 +++++++++++++++
 src/test/regress/expected/backtrace_1.out | 55 +++++++++++++++++
 src/test/regress/parallel_schedule        |  2 +-
 src/test/regress/sql/backtrace.sql        | 33 ++++++++++
 24 files changed, 414 insertions(+), 65 deletions(-)
 create mode 100644 src/test/regress/expected/backtrace.out
 create mode 100644 src/test/regress/expected/backtrace_1.out
 create mode 100644 src/test/regress/sql/backtrace.sql

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 0ee6974f1c..855ccc8902 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25318,6 +25318,30 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_log_backtrace</primary>
+        </indexterm>
+        <function>pg_log_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Requests to log the backtrace of the backend with the
+        specified process ID.  This function can send the request to
+        backends and auxiliary processes except logger and statistics
+        collector.  These backtraces 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 identifies
+        which function a process is currently executing and it may be useful
+        for developers to diagnose stuck processes and other problems. This
+        function is supported only if PostgreSQL was built with the ability to
+        capture backtraces, otherwise it will emit a warning.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -25537,6 +25561,44 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_log_backtrace</function> can be used to log the backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace
+------------------
+ t
+(1 row)
+</programlisting>
+The backtrace will be logged as specified by the logging configuration.
+For example:
+<screen>
+2021-01-27 11:33:50.247 IST [111735] LOG:  current backtrace:
+        postgres: postgresdba postgres [local] SELECT(set_backtrace+0x38) [0xae06c5]
+        postgres: postgresdba postgres [local] SELECT(ProcessInterrupts+0x788) [0x950c34]
+        postgres: postgresdba postgres [local] SELECT() [0x761e89]
+        postgres: postgresdba postgres [local] SELECT() [0x71bbda]
+        postgres: postgresdba postgres [local] SELECT() [0x71e380]
+        postgres: postgresdba postgres [local] SELECT(standard_ExecutorRun+0x1d6) [0x71c1fe]
+        postgres: postgresdba postgres [local] SELECT(ExecutorRun+0x55) [0x71c026]
+        postgres: postgresdba postgres [local] SELECT() [0x953fc5]
+        postgres: postgresdba postgres [local] SELECT(PortalRun+0x262) [0x953c7e]
+        postgres: postgresdba postgres [local] SELECT() [0x94db78]
+        postgres: postgresdba postgres [local] SELECT(PostgresMain+0x7d7) [0x951e72]
+        postgres: postgresdba postgres [local] SELECT() [0x896b2f]
+        postgres: postgresdba postgres [local] SELECT() [0x8964b5]
+        postgres: postgresdba postgres [local] SELECT() [0x892a79]
+        postgres: postgresdba postgres [local] SELECT(PostmasterMain+0x116b) [0x892350]
+        postgres: postgresdba postgres [local] SELECT() [0x795f72]
+        /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2107bbd505]
+        postgres: postgresdba postgres [local] SELECT() [0x4842a9]
+</screen>
+    You can get the file name and line number from the logged details by using
+    gdb/addr2line in linux platforms (users must ensure gdb/addr2line is
+    already installed).
+   </para>
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index fd1421788e..a9e5ebce15 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -711,6 +711,8 @@ REVOKE EXECUTE ON FUNCTION pg_ls_logicalmapdir() FROM PUBLIC;
 
 REVOKE EXECUTE ON FUNCTION pg_ls_replslotdir(text) FROM PUBLIC;
 
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer) FROM PUBLIC;
+
 --
 -- We also set up some things as accessible to standard roles.
 --
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 681ef91b81..a572412f2a 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -841,6 +841,10 @@ HandleAutoVacLauncherInterrupts(void)
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
 
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
+
 	/* Process sinval catchup interrupts that happened while sleeping */
 	ProcessCatchupInterrupt();
 }
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 23f691cd47..013b16ca82 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -581,6 +581,10 @@ HandleCheckpointerInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 /*
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index 3f412dad2e..84b1de4967 100644
--- a/src/backend/postmaster/interrupt.c
+++ b/src/backend/postmaster/interrupt.c
@@ -48,6 +48,10 @@ HandleMainLoopInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 /*
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 1121e4fb29..d146719188 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -862,9 +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, config update and request for logging of
- * memory contexts, but not shutdown request because how to handle
- * shutdown request is different between those loops.
+ * It checks for barrier events, config update, request for logging of
+ * memory contexts and backtrace, but not shutdown request because how to
+ * handle shutdown request is different between those loops.
  */
 static void
 HandlePgArchInterrupts(void)
@@ -881,4 +881,8 @@ HandlePgArchInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index 9bae16bfc7..76425cf079 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -205,6 +205,10 @@ HandleStartupProcInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 102fa2a089..52d5a82ecb 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -310,4 +310,8 @@ HandleWalWriterInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 3be6040289..1a3ca15a79 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -3135,6 +3135,62 @@ HaveVirtualXIDsDelayingChkpt(VirtualTransactionId *vxids, int nvxids)
 	return result;
 }
 
+/*
+ * CheckPostgresProcessId -- check if the process with given pid is a backend
+ * or an auxiliary process and return its PGPROC.
+ *
+ * Returns NULL if not found.
+ */
+PGPROC *
+CheckPostgresProcessId(int pid, bool chk_auxiliary_proc, BackendId *backendId)
+{
+	PGPROC	   *result;
+
+	/*
+	 * Get backend id from PGPROC for a backend. Since auxiliary processes
+	 * (except the startup process) don't have a valid backend id, return
+	 * InvalidBackendId.
+	 */
+	if (backendId)
+		*backendId = InvalidBackendId;
+
+	/* See if the process with given pid is a backend */
+	result = BackendPidGetProc(pid);
+
+	if (result && backendId)
+		*backendId = result->backendId;
+	else if (chk_auxiliary_proc)
+	{
+		/* See if the process with given pid is an auxiliary process */
+		result = 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 (result == NULL)
+	{
+		/*
+		 * This is just a warning so a loop-through-resultset will not abort
+		 * if one backend terminated on its own during the run.
+		 */
+		if (chk_auxiliary_proc)
+			ereport(WARNING,
+					(errmsg("PID %d is not a PostgreSQL server process", pid)));
+		else
+			ereport(WARNING,
+					(errmsg("PID %d is not a PostgreSQL backend process", pid)));
+	}
+
+	return result;
+}
+
 /*
  * BackendPidGetProc -- get a backend's PGPROC given its PID
  *
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index f1c8ff8f9e..862eaa5f0d 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -612,6 +612,43 @@ ProcessBarrierPlaceholder(void)
 	return true;
 }
 
+/*
+ * HandleLogBacktraceInterrupt - Handle receipt of an interrupt requesting to
+ * log a backtrace.
+ *
+ * All the actual work is deferred to ProcessLogBacktraceInterrupt(),
+ * because we cannot safely emit a log message inside the signal handler.
+ */
+static void
+HandleLogBacktraceInterrupt(void)
+{
+	InterruptPending = true;
+	LogBacktracePending = true;
+	/* latch will be set by procsignal_sigusr1_handler */
+}
+
+/*
+ * ProcessLogBacktraceInterrupt - Perform logging of backtrace of this
+ * backend process.
+ *
+ * Any backend that participates in ProcSignal signaling must arrange
+ * to call this function if we see LogBacktracePending set.
+ * CHECK_FOR_INTERRUPTS() or from process specific interrupt handlers.
+ */
+void
+ProcessLogBacktraceInterrupt(void)
+{
+	LogBacktracePending = false;
+
+	/*
+	 * Use LOG_SERVER_ONLY to prevent this message from being sent to the
+	 * connected client.
+	 */
+	ereport(LOG_SERVER_ONLY,
+			(errmsg("logging backtrace of PID %d", MyProcPid)));
+	set_backtrace(NULL, 0);
+}
+
 /*
  * CheckProcSignal - check to see if a particular reason has been
  * signaled, and clear the signal flag.  Should be called after receiving
@@ -661,6 +698,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_LOG_MEMORY_CONTEXT))
 		HandleLogMemoryContextInterrupt();
 
+	if (CheckProcSignal(PROCSIG_LOG_BACKTRACE))
+		HandleLogBacktraceInterrupt();
+
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
 
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index 6e310b14eb..16d39467ea 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -48,31 +48,12 @@
 static int
 pg_signal_backend(int pid, int sig)
 {
-	PGPROC	   *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 so far all the callers of
-	 * this mechanism involve some request for ending the process anyway, that
-	 * it might end on its own first is not a problem.
-	 *
-	 * Note that proc will also be NULL if the pid refers to an auxiliary
-	 * process or the postmaster (neither of which can be signaled via
-	 * pg_signal_backend()).
-	 */
-	if (proc == NULL)
-	{
-		/*
-		 * This is just a warning so a loop-through-resultset will not abort
-		 * if one backend terminated on its own during the run.
-		 */
-		ereport(WARNING,
-				(errmsg("PID %d is not a PostgreSQL backend process", pid)));
+	PGPROC  *proc;
 
+	/* Users can only signal valid backend or an auxiliary process. */
+	proc = CheckPostgresProcessId(pid, false, NULL);
+	if (!proc)
 		return SIGNAL_BACKEND_ERROR;
-	}
 
 	/* Only allow superusers to signal superuser-owned backends. */
 	if (superuser_arg(proc->roleId) && !superuser())
@@ -303,3 +284,49 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_log_backtrace
+ *		Signal a backend or an auxiliary process to log its 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 and which can lead to denial of
+ * service. Additional roles can be permitted with GRANT.
+ *
+ * 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 backtrace.
+ */
+Datum
+pg_log_backtrace(PG_FUNCTION_ARGS)
+{
+	int			pid = PG_GETARG_INT32(0);
+	BackendId   backendId;
+
+#ifndef HAVE_BACKTRACE_SYMBOLS
+	ereport(WARNING,
+			errmsg("backtrace generation is not supported by this installation"),
+			errhint("You need to rebuild PostgreSQL using a library containing backtrace_symbols."));
+	PG_RETURN_BOOL(false);
+#endif
+
+	/* Get the process id of the backend or an auxiliary process */
+	if (!CheckPostgresProcessId(pid, true, &backendId))
+		PG_RETURN_BOOL(false);
+
+	/*
+	 * If the given process is a backend, its backend id from PGPROC is used in
+	 * SendProcSignal() later to speed up the operation. Otherwise,
+	 * InvalidBackendId is used because auxiliary processes (except the startup
+	 * process) don't have a valid backend id.
+	 */
+	if (SendProcSignal(pid, PROCSIG_LOG_BACKTRACE, backendId))
+	{
+		ereport(WARNING,
+				(errmsg("could not send signal to process %d: %m", pid)));
+		PG_RETURN_BOOL(false);
+	}
+
+	PG_RETURN_BOOL(true);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index fda2e9360e..49d7f4a765 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3367,6 +3367,10 @@ ProcessInterrupts(void)
 
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index 28cb9d3ff1..eb46770a6d 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -175,44 +175,18 @@ Datum
 pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
 {
 	int			pid = PG_GETARG_INT32(0);
-	PGPROC	   *proc;
 	BackendId	backendId = InvalidBackendId;
 
-	proc = BackendPidGetProc(pid);
-
-	/*
-	 * 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);
+	/* Get the process id of the backend or an auxiliary process */
+	if (!CheckPostgresProcessId(pid, true, &backendId))
+		PG_RETURN_BOOL(false);
 
 	/*
-	 * 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 the given process is a backend, its backend id from PGPROC is used in
+	 * SendProcSignal() later to speed up the operation. Otherwise,
+	 * InvalidBackendId is used because auxiliary processes (except the startup
+	 * process) don't have a valid backend id.
 	 */
-	if (proc == NULL)
-	{
-		/*
-		 * This is just a warning so a loop-through-resultset will not abort
-		 * if one backend terminated on its own during the run.
-		 */
-		ereport(WARNING,
-				(errmsg("PID %d is not a PostgreSQL server process", pid)));
-		PG_RETURN_BOOL(false);
-	}
-
 	if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, backendId) < 0)
 	{
 		/* Again, just a warning to allow loops */
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 7402696986..522a525741 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -172,7 +172,6 @@ static char formatted_log_time[FORMATTED_TS_LEN];
 
 
 static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
-static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
 static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
 static void write_console(const char *line, int len);
 static const char *process_log_prefix_padding(const char *p, int *padding);
@@ -944,9 +943,10 @@ errbacktrace(void)
  * Compute backtrace data and add it to the supplied ErrorData.  num_skip
  * specifies how many inner frames to skip.  Use this to avoid showing the
  * internal backtrace support functions in the backtrace.  This requires that
- * this and related functions are not inlined.
+ * this and related functions are not inlined. If the edata pointer is valid,
+ * backtrace information will be set in edata.
  */
-static void
+void
 set_backtrace(ErrorData *edata, int num_skip)
 {
 	StringInfoData errtrace;
@@ -973,7 +973,18 @@ set_backtrace(ErrorData *edata, int num_skip)
 						   "backtrace generation is not supported by this installation");
 #endif
 
-	edata->backtrace = errtrace.data;
+	if (edata)
+		edata->backtrace = errtrace.data;
+	else
+	{
+		/*
+		 * LOG_SERVER_ONLY is used to make sure the backtrace is never
+		 * sent to client. We want to avoid messing up the other client
+		 * session.
+		 */
+		elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
+		pfree(errtrace.data);
+	}
 }
 
 /*
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index c26a1a73df..3ef4888e93 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -36,6 +36,7 @@ volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t IdleSessionTimeoutPending = false;
 volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile sig_atomic_t LogMemoryContextPending = false;
+volatile sig_atomic_t LogBacktracePending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 0859dc81ca..ac9d854785 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11731,4 +11731,9 @@
   prorettype => 'bytea', proargtypes => 'pg_brin_minmax_multi_summary',
   prosrc => 'brin_minmax_multi_summary_send' },
 
+# function to get the backtrace of server process
+{ oid => '6105', descr => 'log backtrace of server process',
+  proname => 'pg_log_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_log_backtrace' },
+
 ]
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 02276d3edd..b94287fad0 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -94,6 +94,7 @@ extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
 extern PGDLLIMPORT volatile sig_atomic_t LogMemoryContextPending;
+extern PGDLLIMPORT volatile sig_atomic_t LogBacktracePending;
 
 extern PGDLLIMPORT volatile sig_atomic_t CheckClientConnectionPending;
 extern PGDLLIMPORT volatile sig_atomic_t ClientConnectionLost;
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index e03692053e..df3d3f1b32 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -66,6 +66,8 @@ extern PGPROC *BackendPidGetProc(int pid);
 extern PGPROC *BackendPidGetProcWithLock(int pid);
 extern int	BackendXidGetPid(TransactionId xid);
 extern bool IsBackendPid(int pid);
+extern PGPROC *CheckPostgresProcessId(int pid, bool chk_auxiliary_proc,
+									  BackendId *backendId);
 
 extern VirtualTransactionId *GetCurrentVirtualXIDs(TransactionId limitXmin,
 												   bool excludeXmin0, bool allDbs, int excludeVacuum,
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index a121e65066..3185ff11da 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -35,6 +35,7 @@ typedef enum
 	PROCSIG_WALSND_INIT_STOPPING,	/* ask walsenders to prepare for shutdown  */
 	PROCSIG_BARRIER,			/* global barrier interrupt  */
 	PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
+	PROCSIG_LOG_BACKTRACE,		/* ask backend to log the current backtrace */
 
 	/* Recovery conflict reasons */
 	PROCSIG_RECOVERY_CONFLICT_DATABASE,
@@ -70,7 +71,7 @@ extern int	SendProcSignal(pid_t pid, ProcSignalReason reason,
 extern uint64 EmitProcSignalBarrier(ProcSignalBarrierType type);
 extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
-
+extern void ProcessLogBacktraceInterrupt(void);
 extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
 
 #endif							/* PROCSIGNAL_H */
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 3eb8de3966..074f258674 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -467,4 +467,6 @@ extern void set_syslog_parameters(const char *ident, int facility);
  */
 extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
 
+extern pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
+
 #endif							/* ELOG_H */
diff --git a/src/test/regress/expected/backtrace.out b/src/test/regress/expected/backtrace.out
new file mode 100644
index 0000000000..2184a99483
--- /dev/null
+++ b/src/test/regress/expected/backtrace.out
@@ -0,0 +1,49 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/expected/backtrace_1.out b/src/test/regress/expected/backtrace_1.out
new file mode 100644
index 0000000000..fe9523f89d
--- /dev/null
+++ b/src/test/regress/expected/backtrace_1.out
@@ -0,0 +1,55 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 861c30a73a..7b44124821 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -89,7 +89,7 @@ test: brin_bloom brin_multi
 # ----------
 # Another group of parallel tests
 # ----------
-test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role
+test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role backtrace
 
 # rules cannot run concurrently with any test that creates
 # a view or rule in the public schema
diff --git a/src/test/regress/sql/backtrace.sql b/src/test/regress/sql/backtrace.sql
new file mode 100644
index 0000000000..d74b1016ae
--- /dev/null
+++ b/src/test/regress/sql/backtrace.sql
@@ -0,0 +1,33 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+
+SELECT pg_log_backtrace(pg_backend_pid());
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+
+CREATE ROLE regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+RESET ROLE;
+
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+
+DROP ROLE regress_log_backtrace;
-- 
2.32.0

#93Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: vignesh C (#92)
Re: Printing backtrace of postgres processes

On Tue, Jan 25, 2022 at 12:00 PM vignesh C <vignesh21@gmail.com> wrote:

Thanks for the comments, attached v17 patch has the fix for the same.

Have a minor comment on v17:

can we modify the elog(LOG, to new style ereport(LOG, ?
+ elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);

/*----------
* New-style error reporting API: to be used in this way:
* ereport(ERROR,
* errcode(ERRCODE_UNDEFINED_CURSOR),
* errmsg("portal \"%s\" not found", stmt->portalname),
* ... other errxxx() fields as needed ...);
*

Regards,
Bharath Rupireddy.

#94vignesh C
vignesh21@gmail.com
In reply to: Bharath Rupireddy (#93)
1 attachment(s)
Re: Printing backtrace of postgres processes

On Wed, Jan 26, 2022 at 11:07 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Tue, Jan 25, 2022 at 12:00 PM vignesh C <vignesh21@gmail.com> wrote:

Thanks for the comments, attached v17 patch has the fix for the same.

Have a minor comment on v17:

can we modify the elog(LOG, to new style ereport(LOG, ?
+ elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);

/*----------
* New-style error reporting API: to be used in this way:
* ereport(ERROR,
* errcode(ERRCODE_UNDEFINED_CURSOR),
* errmsg("portal \"%s\" not found", stmt->portalname),
* ... other errxxx() fields as needed ...);
*

Attached v18 patch has the changes for the same.

Regards,
Vignesh

Attachments:

v18-0001-Add-function-to-log-the-backtrace-of-the-specifi.patchtext/x-patch; charset=US-ASCII; name=v18-0001-Add-function-to-log-the-backtrace-of-the-specifi.patchDownload
From 3599e6940e16eb164737a8428af4b8ba5ef6bf59 Mon Sep 17 00:00:00 2001
From: Vigneshwaran C <vignesh21@gmail.com>
Date: Tue, 25 Jan 2022 08:21:22 +0530
Subject: [PATCH v18] Add function to log the backtrace of the specified
 postgres process.

This commit adds pg_log_backtrace() function that requests to log
the backtrace of the specified backend or auxiliary process except
logger and statistic collector.

Only superusers are allowed to request to log the backtrace
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.

On receipt of the request, at the next CHECK_FOR_INTERRUPTS(),
the target backend logs its backtrace at LOG_SERVER_ONLY level,
so that the backtrace will appear in the server log but not
be sent to the client.

Bump catalog version.

Authors: Vignesh C, Bharath Rupireddy
---
 doc/src/sgml/func.sgml                    | 62 +++++++++++++++++++
 src/backend/catalog/system_functions.sql  |  2 +
 src/backend/postmaster/autovacuum.c       |  4 ++
 src/backend/postmaster/checkpointer.c     |  4 ++
 src/backend/postmaster/interrupt.c        |  4 ++
 src/backend/postmaster/pgarch.c           | 10 +++-
 src/backend/postmaster/startup.c          |  4 ++
 src/backend/postmaster/walwriter.c        |  4 ++
 src/backend/storage/ipc/procarray.c       | 56 +++++++++++++++++
 src/backend/storage/ipc/procsignal.c      | 42 +++++++++++++
 src/backend/storage/ipc/signalfuncs.c     | 73 ++++++++++++++++-------
 src/backend/tcop/postgres.c               |  4 ++
 src/backend/utils/adt/mcxtfuncs.c         | 40 +++----------
 src/backend/utils/error/elog.c            | 19 ++++--
 src/backend/utils/init/globals.c          |  1 +
 src/include/catalog/pg_proc.dat           |  5 ++
 src/include/miscadmin.h                   |  1 +
 src/include/storage/procarray.h           |  2 +
 src/include/storage/procsignal.h          |  3 +-
 src/include/utils/elog.h                  |  2 +
 src/test/regress/expected/backtrace.out   | 49 +++++++++++++++
 src/test/regress/expected/backtrace_1.out | 55 +++++++++++++++++
 src/test/regress/parallel_schedule        |  2 +-
 src/test/regress/sql/backtrace.sql        | 33 ++++++++++
 24 files changed, 416 insertions(+), 65 deletions(-)
 create mode 100644 src/test/regress/expected/backtrace.out
 create mode 100644 src/test/regress/expected/backtrace_1.out
 create mode 100644 src/test/regress/sql/backtrace.sql

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 0ee6974f1c..855ccc8902 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25318,6 +25318,30 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_log_backtrace</primary>
+        </indexterm>
+        <function>pg_log_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Requests to log the backtrace of the backend with the
+        specified process ID.  This function can send the request to
+        backends and auxiliary processes except logger and statistics
+        collector.  These backtraces 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 identifies
+        which function a process is currently executing and it may be useful
+        for developers to diagnose stuck processes and other problems. This
+        function is supported only if PostgreSQL was built with the ability to
+        capture backtraces, otherwise it will emit a warning.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -25537,6 +25561,44 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_log_backtrace</function> can be used to log the backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace
+------------------
+ t
+(1 row)
+</programlisting>
+The backtrace will be logged as specified by the logging configuration.
+For example:
+<screen>
+2021-01-27 11:33:50.247 IST [111735] LOG:  current backtrace:
+        postgres: postgresdba postgres [local] SELECT(set_backtrace+0x38) [0xae06c5]
+        postgres: postgresdba postgres [local] SELECT(ProcessInterrupts+0x788) [0x950c34]
+        postgres: postgresdba postgres [local] SELECT() [0x761e89]
+        postgres: postgresdba postgres [local] SELECT() [0x71bbda]
+        postgres: postgresdba postgres [local] SELECT() [0x71e380]
+        postgres: postgresdba postgres [local] SELECT(standard_ExecutorRun+0x1d6) [0x71c1fe]
+        postgres: postgresdba postgres [local] SELECT(ExecutorRun+0x55) [0x71c026]
+        postgres: postgresdba postgres [local] SELECT() [0x953fc5]
+        postgres: postgresdba postgres [local] SELECT(PortalRun+0x262) [0x953c7e]
+        postgres: postgresdba postgres [local] SELECT() [0x94db78]
+        postgres: postgresdba postgres [local] SELECT(PostgresMain+0x7d7) [0x951e72]
+        postgres: postgresdba postgres [local] SELECT() [0x896b2f]
+        postgres: postgresdba postgres [local] SELECT() [0x8964b5]
+        postgres: postgresdba postgres [local] SELECT() [0x892a79]
+        postgres: postgresdba postgres [local] SELECT(PostmasterMain+0x116b) [0x892350]
+        postgres: postgresdba postgres [local] SELECT() [0x795f72]
+        /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2107bbd505]
+        postgres: postgresdba postgres [local] SELECT() [0x4842a9]
+</screen>
+    You can get the file name and line number from the logged details by using
+    gdb/addr2line in linux platforms (users must ensure gdb/addr2line is
+    already installed).
+   </para>
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index fd1421788e..a9e5ebce15 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -711,6 +711,8 @@ REVOKE EXECUTE ON FUNCTION pg_ls_logicalmapdir() FROM PUBLIC;
 
 REVOKE EXECUTE ON FUNCTION pg_ls_replslotdir(text) FROM PUBLIC;
 
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer) FROM PUBLIC;
+
 --
 -- We also set up some things as accessible to standard roles.
 --
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 681ef91b81..a572412f2a 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -841,6 +841,10 @@ HandleAutoVacLauncherInterrupts(void)
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
 
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
+
 	/* Process sinval catchup interrupts that happened while sleeping */
 	ProcessCatchupInterrupt();
 }
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 23f691cd47..013b16ca82 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -581,6 +581,10 @@ HandleCheckpointerInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 /*
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index 3f412dad2e..84b1de4967 100644
--- a/src/backend/postmaster/interrupt.c
+++ b/src/backend/postmaster/interrupt.c
@@ -48,6 +48,10 @@ HandleMainLoopInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 /*
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 1121e4fb29..d146719188 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -862,9 +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, config update and request for logging of
- * memory contexts, but not shutdown request because how to handle
- * shutdown request is different between those loops.
+ * It checks for barrier events, config update, request for logging of
+ * memory contexts and backtrace, but not shutdown request because how to
+ * handle shutdown request is different between those loops.
  */
 static void
 HandlePgArchInterrupts(void)
@@ -881,4 +881,8 @@ HandlePgArchInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index 9bae16bfc7..76425cf079 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -205,6 +205,10 @@ HandleStartupProcInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 102fa2a089..52d5a82ecb 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -310,4 +310,8 @@ HandleWalWriterInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 3be6040289..1a3ca15a79 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -3135,6 +3135,62 @@ HaveVirtualXIDsDelayingChkpt(VirtualTransactionId *vxids, int nvxids)
 	return result;
 }
 
+/*
+ * CheckPostgresProcessId -- check if the process with given pid is a backend
+ * or an auxiliary process and return its PGPROC.
+ *
+ * Returns NULL if not found.
+ */
+PGPROC *
+CheckPostgresProcessId(int pid, bool chk_auxiliary_proc, BackendId *backendId)
+{
+	PGPROC	   *result;
+
+	/*
+	 * Get backend id from PGPROC for a backend. Since auxiliary processes
+	 * (except the startup process) don't have a valid backend id, return
+	 * InvalidBackendId.
+	 */
+	if (backendId)
+		*backendId = InvalidBackendId;
+
+	/* See if the process with given pid is a backend */
+	result = BackendPidGetProc(pid);
+
+	if (result && backendId)
+		*backendId = result->backendId;
+	else if (chk_auxiliary_proc)
+	{
+		/* See if the process with given pid is an auxiliary process */
+		result = 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 (result == NULL)
+	{
+		/*
+		 * This is just a warning so a loop-through-resultset will not abort
+		 * if one backend terminated on its own during the run.
+		 */
+		if (chk_auxiliary_proc)
+			ereport(WARNING,
+					(errmsg("PID %d is not a PostgreSQL server process", pid)));
+		else
+			ereport(WARNING,
+					(errmsg("PID %d is not a PostgreSQL backend process", pid)));
+	}
+
+	return result;
+}
+
 /*
  * BackendPidGetProc -- get a backend's PGPROC given its PID
  *
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index f1c8ff8f9e..a6e00feddb 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -612,6 +612,45 @@ ProcessBarrierPlaceholder(void)
 	return true;
 }
 
+/*
+ * HandleLogBacktraceInterrupt - Handle receipt of an interrupt requesting to
+ * log a backtrace.
+ *
+ * All the actual work is deferred to ProcessLogBacktraceInterrupt(),
+ * because we cannot safely emit a log message inside the signal handler.
+ */
+static void
+HandleLogBacktraceInterrupt(void)
+{
+	InterruptPending = true;
+	LogBacktracePending = true;
+	/* latch will be set by procsignal_sigusr1_handler */
+}
+
+/*
+ * ProcessLogBacktraceInterrupt - Perform logging of backtrace of this
+ * backend process.
+ *
+ * Any backend that participates in ProcSignal signaling must arrange
+ * to call this function if we see LogBacktracePending set.
+ * CHECK_FOR_INTERRUPTS() or from process specific interrupt handlers.
+ */
+void
+ProcessLogBacktraceInterrupt(void)
+{
+	LogBacktracePending = false;
+
+	/*
+	 * Use LOG_SERVER_ONLY to prevent this message from being sent to the
+	 * connected client.
+	 */
+	ereport(LOG_SERVER_ONLY,
+			errhidestmt(true),
+			errhidecontext(true),
+			errmsg_internal("logging backtrace of PID %d", MyProcPid));
+	set_backtrace(NULL, 0);
+}
+
 /*
  * CheckProcSignal - check to see if a particular reason has been
  * signaled, and clear the signal flag.  Should be called after receiving
@@ -661,6 +700,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_LOG_MEMORY_CONTEXT))
 		HandleLogMemoryContextInterrupt();
 
+	if (CheckProcSignal(PROCSIG_LOG_BACKTRACE))
+		HandleLogBacktraceInterrupt();
+
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
 
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index 6e310b14eb..16d39467ea 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -48,31 +48,12 @@
 static int
 pg_signal_backend(int pid, int sig)
 {
-	PGPROC	   *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 so far all the callers of
-	 * this mechanism involve some request for ending the process anyway, that
-	 * it might end on its own first is not a problem.
-	 *
-	 * Note that proc will also be NULL if the pid refers to an auxiliary
-	 * process or the postmaster (neither of which can be signaled via
-	 * pg_signal_backend()).
-	 */
-	if (proc == NULL)
-	{
-		/*
-		 * This is just a warning so a loop-through-resultset will not abort
-		 * if one backend terminated on its own during the run.
-		 */
-		ereport(WARNING,
-				(errmsg("PID %d is not a PostgreSQL backend process", pid)));
+	PGPROC  *proc;
 
+	/* Users can only signal valid backend or an auxiliary process. */
+	proc = CheckPostgresProcessId(pid, false, NULL);
+	if (!proc)
 		return SIGNAL_BACKEND_ERROR;
-	}
 
 	/* Only allow superusers to signal superuser-owned backends. */
 	if (superuser_arg(proc->roleId) && !superuser())
@@ -303,3 +284,49 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_log_backtrace
+ *		Signal a backend or an auxiliary process to log its 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 and which can lead to denial of
+ * service. Additional roles can be permitted with GRANT.
+ *
+ * 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 backtrace.
+ */
+Datum
+pg_log_backtrace(PG_FUNCTION_ARGS)
+{
+	int			pid = PG_GETARG_INT32(0);
+	BackendId   backendId;
+
+#ifndef HAVE_BACKTRACE_SYMBOLS
+	ereport(WARNING,
+			errmsg("backtrace generation is not supported by this installation"),
+			errhint("You need to rebuild PostgreSQL using a library containing backtrace_symbols."));
+	PG_RETURN_BOOL(false);
+#endif
+
+	/* Get the process id of the backend or an auxiliary process */
+	if (!CheckPostgresProcessId(pid, true, &backendId))
+		PG_RETURN_BOOL(false);
+
+	/*
+	 * If the given process is a backend, its backend id from PGPROC is used in
+	 * SendProcSignal() later to speed up the operation. Otherwise,
+	 * InvalidBackendId is used because auxiliary processes (except the startup
+	 * process) don't have a valid backend id.
+	 */
+	if (SendProcSignal(pid, PROCSIG_LOG_BACKTRACE, backendId))
+	{
+		ereport(WARNING,
+				(errmsg("could not send signal to process %d: %m", pid)));
+		PG_RETURN_BOOL(false);
+	}
+
+	PG_RETURN_BOOL(true);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index fda2e9360e..49d7f4a765 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3367,6 +3367,10 @@ ProcessInterrupts(void)
 
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index 28cb9d3ff1..eb46770a6d 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -175,44 +175,18 @@ Datum
 pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
 {
 	int			pid = PG_GETARG_INT32(0);
-	PGPROC	   *proc;
 	BackendId	backendId = InvalidBackendId;
 
-	proc = BackendPidGetProc(pid);
-
-	/*
-	 * 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);
+	/* Get the process id of the backend or an auxiliary process */
+	if (!CheckPostgresProcessId(pid, true, &backendId))
+		PG_RETURN_BOOL(false);
 
 	/*
-	 * 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 the given process is a backend, its backend id from PGPROC is used in
+	 * SendProcSignal() later to speed up the operation. Otherwise,
+	 * InvalidBackendId is used because auxiliary processes (except the startup
+	 * process) don't have a valid backend id.
 	 */
-	if (proc == NULL)
-	{
-		/*
-		 * This is just a warning so a loop-through-resultset will not abort
-		 * if one backend terminated on its own during the run.
-		 */
-		ereport(WARNING,
-				(errmsg("PID %d is not a PostgreSQL server process", pid)));
-		PG_RETURN_BOOL(false);
-	}
-
 	if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, backendId) < 0)
 	{
 		/* Again, just a warning to allow loops */
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 7402696986..522a525741 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -172,7 +172,6 @@ static char formatted_log_time[FORMATTED_TS_LEN];
 
 
 static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
-static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
 static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
 static void write_console(const char *line, int len);
 static const char *process_log_prefix_padding(const char *p, int *padding);
@@ -944,9 +943,10 @@ errbacktrace(void)
  * Compute backtrace data and add it to the supplied ErrorData.  num_skip
  * specifies how many inner frames to skip.  Use this to avoid showing the
  * internal backtrace support functions in the backtrace.  This requires that
- * this and related functions are not inlined.
+ * this and related functions are not inlined. If the edata pointer is valid,
+ * backtrace information will be set in edata.
  */
-static void
+void
 set_backtrace(ErrorData *edata, int num_skip)
 {
 	StringInfoData errtrace;
@@ -973,7 +973,18 @@ set_backtrace(ErrorData *edata, int num_skip)
 						   "backtrace generation is not supported by this installation");
 #endif
 
-	edata->backtrace = errtrace.data;
+	if (edata)
+		edata->backtrace = errtrace.data;
+	else
+	{
+		/*
+		 * LOG_SERVER_ONLY is used to make sure the backtrace is never
+		 * sent to client. We want to avoid messing up the other client
+		 * session.
+		 */
+		elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
+		pfree(errtrace.data);
+	}
 }
 
 /*
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index c26a1a73df..3ef4888e93 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -36,6 +36,7 @@ volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t IdleSessionTimeoutPending = false;
 volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile sig_atomic_t LogMemoryContextPending = false;
+volatile sig_atomic_t LogBacktracePending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 0859dc81ca..ac9d854785 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11731,4 +11731,9 @@
   prorettype => 'bytea', proargtypes => 'pg_brin_minmax_multi_summary',
   prosrc => 'brin_minmax_multi_summary_send' },
 
+# function to get the backtrace of server process
+{ oid => '6105', descr => 'log backtrace of server process',
+  proname => 'pg_log_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_log_backtrace' },
+
 ]
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 02276d3edd..b94287fad0 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -94,6 +94,7 @@ extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
 extern PGDLLIMPORT volatile sig_atomic_t LogMemoryContextPending;
+extern PGDLLIMPORT volatile sig_atomic_t LogBacktracePending;
 
 extern PGDLLIMPORT volatile sig_atomic_t CheckClientConnectionPending;
 extern PGDLLIMPORT volatile sig_atomic_t ClientConnectionLost;
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index e03692053e..df3d3f1b32 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -66,6 +66,8 @@ extern PGPROC *BackendPidGetProc(int pid);
 extern PGPROC *BackendPidGetProcWithLock(int pid);
 extern int	BackendXidGetPid(TransactionId xid);
 extern bool IsBackendPid(int pid);
+extern PGPROC *CheckPostgresProcessId(int pid, bool chk_auxiliary_proc,
+									  BackendId *backendId);
 
 extern VirtualTransactionId *GetCurrentVirtualXIDs(TransactionId limitXmin,
 												   bool excludeXmin0, bool allDbs, int excludeVacuum,
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index a121e65066..3185ff11da 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -35,6 +35,7 @@ typedef enum
 	PROCSIG_WALSND_INIT_STOPPING,	/* ask walsenders to prepare for shutdown  */
 	PROCSIG_BARRIER,			/* global barrier interrupt  */
 	PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
+	PROCSIG_LOG_BACKTRACE,		/* ask backend to log the current backtrace */
 
 	/* Recovery conflict reasons */
 	PROCSIG_RECOVERY_CONFLICT_DATABASE,
@@ -70,7 +71,7 @@ extern int	SendProcSignal(pid_t pid, ProcSignalReason reason,
 extern uint64 EmitProcSignalBarrier(ProcSignalBarrierType type);
 extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
-
+extern void ProcessLogBacktraceInterrupt(void);
 extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
 
 #endif							/* PROCSIGNAL_H */
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 3eb8de3966..074f258674 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -467,4 +467,6 @@ extern void set_syslog_parameters(const char *ident, int facility);
  */
 extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
 
+extern pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
+
 #endif							/* ELOG_H */
diff --git a/src/test/regress/expected/backtrace.out b/src/test/regress/expected/backtrace.out
new file mode 100644
index 0000000000..2184a99483
--- /dev/null
+++ b/src/test/regress/expected/backtrace.out
@@ -0,0 +1,49 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/expected/backtrace_1.out b/src/test/regress/expected/backtrace_1.out
new file mode 100644
index 0000000000..fe9523f89d
--- /dev/null
+++ b/src/test/regress/expected/backtrace_1.out
@@ -0,0 +1,55 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 861c30a73a..7b44124821 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -89,7 +89,7 @@ test: brin_bloom brin_multi
 # ----------
 # Another group of parallel tests
 # ----------
-test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role
+test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role backtrace
 
 # rules cannot run concurrently with any test that creates
 # a view or rule in the public schema
diff --git a/src/test/regress/sql/backtrace.sql b/src/test/regress/sql/backtrace.sql
new file mode 100644
index 0000000000..d74b1016ae
--- /dev/null
+++ b/src/test/regress/sql/backtrace.sql
@@ -0,0 +1,33 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+
+SELECT pg_log_backtrace(pg_backend_pid());
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+
+CREATE ROLE regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+RESET ROLE;
+
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+
+DROP ROLE regress_log_backtrace;
-- 
2.32.0

#95Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: vignesh C (#94)
Re: Printing backtrace of postgres processes

On Thu, Jan 27, 2022 at 10:45 AM vignesh C <vignesh21@gmail.com> wrote:

On Wed, Jan 26, 2022 at 11:07 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Tue, Jan 25, 2022 at 12:00 PM vignesh C <vignesh21@gmail.com> wrote:

Thanks for the comments, attached v17 patch has the fix for the same.

Have a minor comment on v17:

can we modify the elog(LOG, to new style ereport(LOG, ?
+ elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);

/*----------
* New-style error reporting API: to be used in this way:
* ereport(ERROR,
* errcode(ERRCODE_UNDEFINED_CURSOR),
* errmsg("portal \"%s\" not found", stmt->portalname),
* ... other errxxx() fields as needed ...);
*

Attached v18 patch has the changes for the same.

Thanks. The v18 patch LGTM. I'm not sure if the CF bot failure is
related to the patch - https://cirrus-ci.com/task/5633364051886080

Regards,
Bharath Rupireddy.

#96vignesh C
vignesh21@gmail.com
In reply to: Bharath Rupireddy (#95)
1 attachment(s)
Re: Printing backtrace of postgres processes

On Fri, Jan 28, 2022 at 1:54 PM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Thu, Jan 27, 2022 at 10:45 AM vignesh C <vignesh21@gmail.com> wrote:

On Wed, Jan 26, 2022 at 11:07 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Tue, Jan 25, 2022 at 12:00 PM vignesh C <vignesh21@gmail.com> wrote:

Thanks for the comments, attached v17 patch has the fix for the same.

Have a minor comment on v17:

can we modify the elog(LOG, to new style ereport(LOG, ?
+ elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);

/*----------
* New-style error reporting API: to be used in this way:
* ereport(ERROR,
* errcode(ERRCODE_UNDEFINED_CURSOR),
* errmsg("portal \"%s\" not found", stmt->portalname),
* ... other errxxx() fields as needed ...);
*

Attached v18 patch has the changes for the same.

Thanks. The v18 patch LGTM. I'm not sure if the CF bot failure is
related to the patch - https://cirrus-ci.com/task/5633364051886080

I felt this test failure is not related to this change. Let's wait and
see the results of the next run. Also I noticed that this test seems
to have failed many times in the buildfarm too recently:
https://buildfarm.postgresql.org/cgi-bin/show_failures.pl?max_days=60&amp;branch=HEAD&amp;member=&amp;stage=recoveryCheck&amp;filter=Submit

Regards,
Vignesh

Attachments:

v18-0001-Add-function-to-log-the-backtrace-of-the-specifi.patchtext/x-patch; charset=US-ASCII; name=v18-0001-Add-function-to-log-the-backtrace-of-the-specifi.patchDownload
From 3599e6940e16eb164737a8428af4b8ba5ef6bf59 Mon Sep 17 00:00:00 2001
From: Vigneshwaran C <vignesh21@gmail.com>
Date: Tue, 25 Jan 2022 08:21:22 +0530
Subject: [PATCH v18] Add function to log the backtrace of the specified
 postgres process.

This commit adds pg_log_backtrace() function that requests to log
the backtrace of the specified backend or auxiliary process except
logger and statistic collector.

Only superusers are allowed to request to log the backtrace
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.

On receipt of the request, at the next CHECK_FOR_INTERRUPTS(),
the target backend logs its backtrace at LOG_SERVER_ONLY level,
so that the backtrace will appear in the server log but not
be sent to the client.

Bump catalog version.

Authors: Vignesh C, Bharath Rupireddy
---
 doc/src/sgml/func.sgml                    | 62 +++++++++++++++++++
 src/backend/catalog/system_functions.sql  |  2 +
 src/backend/postmaster/autovacuum.c       |  4 ++
 src/backend/postmaster/checkpointer.c     |  4 ++
 src/backend/postmaster/interrupt.c        |  4 ++
 src/backend/postmaster/pgarch.c           | 10 +++-
 src/backend/postmaster/startup.c          |  4 ++
 src/backend/postmaster/walwriter.c        |  4 ++
 src/backend/storage/ipc/procarray.c       | 56 +++++++++++++++++
 src/backend/storage/ipc/procsignal.c      | 42 +++++++++++++
 src/backend/storage/ipc/signalfuncs.c     | 73 ++++++++++++++++-------
 src/backend/tcop/postgres.c               |  4 ++
 src/backend/utils/adt/mcxtfuncs.c         | 40 +++----------
 src/backend/utils/error/elog.c            | 19 ++++--
 src/backend/utils/init/globals.c          |  1 +
 src/include/catalog/pg_proc.dat           |  5 ++
 src/include/miscadmin.h                   |  1 +
 src/include/storage/procarray.h           |  2 +
 src/include/storage/procsignal.h          |  3 +-
 src/include/utils/elog.h                  |  2 +
 src/test/regress/expected/backtrace.out   | 49 +++++++++++++++
 src/test/regress/expected/backtrace_1.out | 55 +++++++++++++++++
 src/test/regress/parallel_schedule        |  2 +-
 src/test/regress/sql/backtrace.sql        | 33 ++++++++++
 24 files changed, 416 insertions(+), 65 deletions(-)
 create mode 100644 src/test/regress/expected/backtrace.out
 create mode 100644 src/test/regress/expected/backtrace_1.out
 create mode 100644 src/test/regress/sql/backtrace.sql

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 0ee6974f1c..855ccc8902 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25318,6 +25318,30 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_log_backtrace</primary>
+        </indexterm>
+        <function>pg_log_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Requests to log the backtrace of the backend with the
+        specified process ID.  This function can send the request to
+        backends and auxiliary processes except logger and statistics
+        collector.  These backtraces 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 identifies
+        which function a process is currently executing and it may be useful
+        for developers to diagnose stuck processes and other problems. This
+        function is supported only if PostgreSQL was built with the ability to
+        capture backtraces, otherwise it will emit a warning.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -25537,6 +25561,44 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_log_backtrace</function> can be used to log the backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace
+------------------
+ t
+(1 row)
+</programlisting>
+The backtrace will be logged as specified by the logging configuration.
+For example:
+<screen>
+2021-01-27 11:33:50.247 IST [111735] LOG:  current backtrace:
+        postgres: postgresdba postgres [local] SELECT(set_backtrace+0x38) [0xae06c5]
+        postgres: postgresdba postgres [local] SELECT(ProcessInterrupts+0x788) [0x950c34]
+        postgres: postgresdba postgres [local] SELECT() [0x761e89]
+        postgres: postgresdba postgres [local] SELECT() [0x71bbda]
+        postgres: postgresdba postgres [local] SELECT() [0x71e380]
+        postgres: postgresdba postgres [local] SELECT(standard_ExecutorRun+0x1d6) [0x71c1fe]
+        postgres: postgresdba postgres [local] SELECT(ExecutorRun+0x55) [0x71c026]
+        postgres: postgresdba postgres [local] SELECT() [0x953fc5]
+        postgres: postgresdba postgres [local] SELECT(PortalRun+0x262) [0x953c7e]
+        postgres: postgresdba postgres [local] SELECT() [0x94db78]
+        postgres: postgresdba postgres [local] SELECT(PostgresMain+0x7d7) [0x951e72]
+        postgres: postgresdba postgres [local] SELECT() [0x896b2f]
+        postgres: postgresdba postgres [local] SELECT() [0x8964b5]
+        postgres: postgresdba postgres [local] SELECT() [0x892a79]
+        postgres: postgresdba postgres [local] SELECT(PostmasterMain+0x116b) [0x892350]
+        postgres: postgresdba postgres [local] SELECT() [0x795f72]
+        /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2107bbd505]
+        postgres: postgresdba postgres [local] SELECT() [0x4842a9]
+</screen>
+    You can get the file name and line number from the logged details by using
+    gdb/addr2line in linux platforms (users must ensure gdb/addr2line is
+    already installed).
+   </para>
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index fd1421788e..a9e5ebce15 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -711,6 +711,8 @@ REVOKE EXECUTE ON FUNCTION pg_ls_logicalmapdir() FROM PUBLIC;
 
 REVOKE EXECUTE ON FUNCTION pg_ls_replslotdir(text) FROM PUBLIC;
 
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer) FROM PUBLIC;
+
 --
 -- We also set up some things as accessible to standard roles.
 --
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 681ef91b81..a572412f2a 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -841,6 +841,10 @@ HandleAutoVacLauncherInterrupts(void)
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
 
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
+
 	/* Process sinval catchup interrupts that happened while sleeping */
 	ProcessCatchupInterrupt();
 }
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 23f691cd47..013b16ca82 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -581,6 +581,10 @@ HandleCheckpointerInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 /*
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index 3f412dad2e..84b1de4967 100644
--- a/src/backend/postmaster/interrupt.c
+++ b/src/backend/postmaster/interrupt.c
@@ -48,6 +48,10 @@ HandleMainLoopInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 /*
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 1121e4fb29..d146719188 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -862,9 +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, config update and request for logging of
- * memory contexts, but not shutdown request because how to handle
- * shutdown request is different between those loops.
+ * It checks for barrier events, config update, request for logging of
+ * memory contexts and backtrace, but not shutdown request because how to
+ * handle shutdown request is different between those loops.
  */
 static void
 HandlePgArchInterrupts(void)
@@ -881,4 +881,8 @@ HandlePgArchInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index 9bae16bfc7..76425cf079 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -205,6 +205,10 @@ HandleStartupProcInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 102fa2a089..52d5a82ecb 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -310,4 +310,8 @@ HandleWalWriterInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 3be6040289..1a3ca15a79 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -3135,6 +3135,62 @@ HaveVirtualXIDsDelayingChkpt(VirtualTransactionId *vxids, int nvxids)
 	return result;
 }
 
+/*
+ * CheckPostgresProcessId -- check if the process with given pid is a backend
+ * or an auxiliary process and return its PGPROC.
+ *
+ * Returns NULL if not found.
+ */
+PGPROC *
+CheckPostgresProcessId(int pid, bool chk_auxiliary_proc, BackendId *backendId)
+{
+	PGPROC	   *result;
+
+	/*
+	 * Get backend id from PGPROC for a backend. Since auxiliary processes
+	 * (except the startup process) don't have a valid backend id, return
+	 * InvalidBackendId.
+	 */
+	if (backendId)
+		*backendId = InvalidBackendId;
+
+	/* See if the process with given pid is a backend */
+	result = BackendPidGetProc(pid);
+
+	if (result && backendId)
+		*backendId = result->backendId;
+	else if (chk_auxiliary_proc)
+	{
+		/* See if the process with given pid is an auxiliary process */
+		result = 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 (result == NULL)
+	{
+		/*
+		 * This is just a warning so a loop-through-resultset will not abort
+		 * if one backend terminated on its own during the run.
+		 */
+		if (chk_auxiliary_proc)
+			ereport(WARNING,
+					(errmsg("PID %d is not a PostgreSQL server process", pid)));
+		else
+			ereport(WARNING,
+					(errmsg("PID %d is not a PostgreSQL backend process", pid)));
+	}
+
+	return result;
+}
+
 /*
  * BackendPidGetProc -- get a backend's PGPROC given its PID
  *
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index f1c8ff8f9e..a6e00feddb 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -612,6 +612,45 @@ ProcessBarrierPlaceholder(void)
 	return true;
 }
 
+/*
+ * HandleLogBacktraceInterrupt - Handle receipt of an interrupt requesting to
+ * log a backtrace.
+ *
+ * All the actual work is deferred to ProcessLogBacktraceInterrupt(),
+ * because we cannot safely emit a log message inside the signal handler.
+ */
+static void
+HandleLogBacktraceInterrupt(void)
+{
+	InterruptPending = true;
+	LogBacktracePending = true;
+	/* latch will be set by procsignal_sigusr1_handler */
+}
+
+/*
+ * ProcessLogBacktraceInterrupt - Perform logging of backtrace of this
+ * backend process.
+ *
+ * Any backend that participates in ProcSignal signaling must arrange
+ * to call this function if we see LogBacktracePending set.
+ * CHECK_FOR_INTERRUPTS() or from process specific interrupt handlers.
+ */
+void
+ProcessLogBacktraceInterrupt(void)
+{
+	LogBacktracePending = false;
+
+	/*
+	 * Use LOG_SERVER_ONLY to prevent this message from being sent to the
+	 * connected client.
+	 */
+	ereport(LOG_SERVER_ONLY,
+			errhidestmt(true),
+			errhidecontext(true),
+			errmsg_internal("logging backtrace of PID %d", MyProcPid));
+	set_backtrace(NULL, 0);
+}
+
 /*
  * CheckProcSignal - check to see if a particular reason has been
  * signaled, and clear the signal flag.  Should be called after receiving
@@ -661,6 +700,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_LOG_MEMORY_CONTEXT))
 		HandleLogMemoryContextInterrupt();
 
+	if (CheckProcSignal(PROCSIG_LOG_BACKTRACE))
+		HandleLogBacktraceInterrupt();
+
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
 
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index 6e310b14eb..16d39467ea 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -48,31 +48,12 @@
 static int
 pg_signal_backend(int pid, int sig)
 {
-	PGPROC	   *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 so far all the callers of
-	 * this mechanism involve some request for ending the process anyway, that
-	 * it might end on its own first is not a problem.
-	 *
-	 * Note that proc will also be NULL if the pid refers to an auxiliary
-	 * process or the postmaster (neither of which can be signaled via
-	 * pg_signal_backend()).
-	 */
-	if (proc == NULL)
-	{
-		/*
-		 * This is just a warning so a loop-through-resultset will not abort
-		 * if one backend terminated on its own during the run.
-		 */
-		ereport(WARNING,
-				(errmsg("PID %d is not a PostgreSQL backend process", pid)));
+	PGPROC  *proc;
 
+	/* Users can only signal valid backend or an auxiliary process. */
+	proc = CheckPostgresProcessId(pid, false, NULL);
+	if (!proc)
 		return SIGNAL_BACKEND_ERROR;
-	}
 
 	/* Only allow superusers to signal superuser-owned backends. */
 	if (superuser_arg(proc->roleId) && !superuser())
@@ -303,3 +284,49 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_log_backtrace
+ *		Signal a backend or an auxiliary process to log its 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 and which can lead to denial of
+ * service. Additional roles can be permitted with GRANT.
+ *
+ * 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 backtrace.
+ */
+Datum
+pg_log_backtrace(PG_FUNCTION_ARGS)
+{
+	int			pid = PG_GETARG_INT32(0);
+	BackendId   backendId;
+
+#ifndef HAVE_BACKTRACE_SYMBOLS
+	ereport(WARNING,
+			errmsg("backtrace generation is not supported by this installation"),
+			errhint("You need to rebuild PostgreSQL using a library containing backtrace_symbols."));
+	PG_RETURN_BOOL(false);
+#endif
+
+	/* Get the process id of the backend or an auxiliary process */
+	if (!CheckPostgresProcessId(pid, true, &backendId))
+		PG_RETURN_BOOL(false);
+
+	/*
+	 * If the given process is a backend, its backend id from PGPROC is used in
+	 * SendProcSignal() later to speed up the operation. Otherwise,
+	 * InvalidBackendId is used because auxiliary processes (except the startup
+	 * process) don't have a valid backend id.
+	 */
+	if (SendProcSignal(pid, PROCSIG_LOG_BACKTRACE, backendId))
+	{
+		ereport(WARNING,
+				(errmsg("could not send signal to process %d: %m", pid)));
+		PG_RETURN_BOOL(false);
+	}
+
+	PG_RETURN_BOOL(true);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index fda2e9360e..49d7f4a765 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3367,6 +3367,10 @@ ProcessInterrupts(void)
 
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index 28cb9d3ff1..eb46770a6d 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -175,44 +175,18 @@ Datum
 pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
 {
 	int			pid = PG_GETARG_INT32(0);
-	PGPROC	   *proc;
 	BackendId	backendId = InvalidBackendId;
 
-	proc = BackendPidGetProc(pid);
-
-	/*
-	 * 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);
+	/* Get the process id of the backend or an auxiliary process */
+	if (!CheckPostgresProcessId(pid, true, &backendId))
+		PG_RETURN_BOOL(false);
 
 	/*
-	 * 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 the given process is a backend, its backend id from PGPROC is used in
+	 * SendProcSignal() later to speed up the operation. Otherwise,
+	 * InvalidBackendId is used because auxiliary processes (except the startup
+	 * process) don't have a valid backend id.
 	 */
-	if (proc == NULL)
-	{
-		/*
-		 * This is just a warning so a loop-through-resultset will not abort
-		 * if one backend terminated on its own during the run.
-		 */
-		ereport(WARNING,
-				(errmsg("PID %d is not a PostgreSQL server process", pid)));
-		PG_RETURN_BOOL(false);
-	}
-
 	if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, backendId) < 0)
 	{
 		/* Again, just a warning to allow loops */
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 7402696986..522a525741 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -172,7 +172,6 @@ static char formatted_log_time[FORMATTED_TS_LEN];
 
 
 static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
-static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
 static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
 static void write_console(const char *line, int len);
 static const char *process_log_prefix_padding(const char *p, int *padding);
@@ -944,9 +943,10 @@ errbacktrace(void)
  * Compute backtrace data and add it to the supplied ErrorData.  num_skip
  * specifies how many inner frames to skip.  Use this to avoid showing the
  * internal backtrace support functions in the backtrace.  This requires that
- * this and related functions are not inlined.
+ * this and related functions are not inlined. If the edata pointer is valid,
+ * backtrace information will be set in edata.
  */
-static void
+void
 set_backtrace(ErrorData *edata, int num_skip)
 {
 	StringInfoData errtrace;
@@ -973,7 +973,18 @@ set_backtrace(ErrorData *edata, int num_skip)
 						   "backtrace generation is not supported by this installation");
 #endif
 
-	edata->backtrace = errtrace.data;
+	if (edata)
+		edata->backtrace = errtrace.data;
+	else
+	{
+		/*
+		 * LOG_SERVER_ONLY is used to make sure the backtrace is never
+		 * sent to client. We want to avoid messing up the other client
+		 * session.
+		 */
+		elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
+		pfree(errtrace.data);
+	}
 }
 
 /*
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index c26a1a73df..3ef4888e93 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -36,6 +36,7 @@ volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t IdleSessionTimeoutPending = false;
 volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile sig_atomic_t LogMemoryContextPending = false;
+volatile sig_atomic_t LogBacktracePending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 0859dc81ca..ac9d854785 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11731,4 +11731,9 @@
   prorettype => 'bytea', proargtypes => 'pg_brin_minmax_multi_summary',
   prosrc => 'brin_minmax_multi_summary_send' },
 
+# function to get the backtrace of server process
+{ oid => '6105', descr => 'log backtrace of server process',
+  proname => 'pg_log_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_log_backtrace' },
+
 ]
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 02276d3edd..b94287fad0 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -94,6 +94,7 @@ extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
 extern PGDLLIMPORT volatile sig_atomic_t LogMemoryContextPending;
+extern PGDLLIMPORT volatile sig_atomic_t LogBacktracePending;
 
 extern PGDLLIMPORT volatile sig_atomic_t CheckClientConnectionPending;
 extern PGDLLIMPORT volatile sig_atomic_t ClientConnectionLost;
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index e03692053e..df3d3f1b32 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -66,6 +66,8 @@ extern PGPROC *BackendPidGetProc(int pid);
 extern PGPROC *BackendPidGetProcWithLock(int pid);
 extern int	BackendXidGetPid(TransactionId xid);
 extern bool IsBackendPid(int pid);
+extern PGPROC *CheckPostgresProcessId(int pid, bool chk_auxiliary_proc,
+									  BackendId *backendId);
 
 extern VirtualTransactionId *GetCurrentVirtualXIDs(TransactionId limitXmin,
 												   bool excludeXmin0, bool allDbs, int excludeVacuum,
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index a121e65066..3185ff11da 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -35,6 +35,7 @@ typedef enum
 	PROCSIG_WALSND_INIT_STOPPING,	/* ask walsenders to prepare for shutdown  */
 	PROCSIG_BARRIER,			/* global barrier interrupt  */
 	PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
+	PROCSIG_LOG_BACKTRACE,		/* ask backend to log the current backtrace */
 
 	/* Recovery conflict reasons */
 	PROCSIG_RECOVERY_CONFLICT_DATABASE,
@@ -70,7 +71,7 @@ extern int	SendProcSignal(pid_t pid, ProcSignalReason reason,
 extern uint64 EmitProcSignalBarrier(ProcSignalBarrierType type);
 extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
-
+extern void ProcessLogBacktraceInterrupt(void);
 extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
 
 #endif							/* PROCSIGNAL_H */
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 3eb8de3966..074f258674 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -467,4 +467,6 @@ extern void set_syslog_parameters(const char *ident, int facility);
  */
 extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
 
+extern pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
+
 #endif							/* ELOG_H */
diff --git a/src/test/regress/expected/backtrace.out b/src/test/regress/expected/backtrace.out
new file mode 100644
index 0000000000..2184a99483
--- /dev/null
+++ b/src/test/regress/expected/backtrace.out
@@ -0,0 +1,49 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/expected/backtrace_1.out b/src/test/regress/expected/backtrace_1.out
new file mode 100644
index 0000000000..fe9523f89d
--- /dev/null
+++ b/src/test/regress/expected/backtrace_1.out
@@ -0,0 +1,55 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 861c30a73a..7b44124821 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -89,7 +89,7 @@ test: brin_bloom brin_multi
 # ----------
 # Another group of parallel tests
 # ----------
-test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role
+test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role backtrace
 
 # rules cannot run concurrently with any test that creates
 # a view or rule in the public schema
diff --git a/src/test/regress/sql/backtrace.sql b/src/test/regress/sql/backtrace.sql
new file mode 100644
index 0000000000..d74b1016ae
--- /dev/null
+++ b/src/test/regress/sql/backtrace.sql
@@ -0,0 +1,33 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+
+SELECT pg_log_backtrace(pg_backend_pid());
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+
+CREATE ROLE regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+RESET ROLE;
+
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+
+DROP ROLE regress_log_backtrace;
-- 
2.32.0

#97vignesh C
vignesh21@gmail.com
In reply to: vignesh C (#96)
Re: Printing backtrace of postgres processes

On Sat, Jan 29, 2022 at 8:06 AM vignesh C <vignesh21@gmail.com> wrote:

On Fri, Jan 28, 2022 at 1:54 PM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Thu, Jan 27, 2022 at 10:45 AM vignesh C <vignesh21@gmail.com> wrote:

On Wed, Jan 26, 2022 at 11:07 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Tue, Jan 25, 2022 at 12:00 PM vignesh C <vignesh21@gmail.com> wrote:

Thanks for the comments, attached v17 patch has the fix for the same.

Have a minor comment on v17:

can we modify the elog(LOG, to new style ereport(LOG, ?
+ elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);

/*----------
* New-style error reporting API: to be used in this way:
* ereport(ERROR,
* errcode(ERRCODE_UNDEFINED_CURSOR),
* errmsg("portal \"%s\" not found", stmt->portalname),
* ... other errxxx() fields as needed ...);
*

Attached v18 patch has the changes for the same.

Thanks. The v18 patch LGTM. I'm not sure if the CF bot failure is
related to the patch - https://cirrus-ci.com/task/5633364051886080

I felt this test failure is not related to this change. Let's wait and
see the results of the next run. Also I noticed that this test seems
to have failed many times in the buildfarm too recently:
https://buildfarm.postgresql.org/cgi-bin/show_failures.pl?max_days=60&amp;branch=HEAD&amp;member=&amp;stage=recoveryCheck&amp;filter=Submit

CFBot has passed in the last run.

Regards,
Vignesh

#98Justin Pryzby
pryzby@telsasoft.com
In reply to: vignesh C (#96)
2 attachment(s)
Re: Printing backtrace of postgres processes

rebased to appease cfbot.

+ couple of little fixes as 0002.

--
Justin

Attachments:

0001-Add-function-to-log-the-backtrace-of-the-specified-p.patchtext/x-diff; charset=us-asciiDownload
From 4be93f2bab460682a0f5af9e1e3f4970709b3517 Mon Sep 17 00:00:00 2001
From: Vigneshwaran C <vignesh21@gmail.com>
Date: Tue, 25 Jan 2022 08:21:22 +0530
Subject: [PATCH 1/2] Add function to log the backtrace of the specified
 postgres process.

ci-os-only: html

This commit adds pg_log_backtrace() function that requests to log
the backtrace of the specified backend or auxiliary process except
logger and statistic collector.

Only superusers are allowed to request to log the backtrace
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.

On receipt of the request, at the next CHECK_FOR_INTERRUPTS(),
the target backend logs its backtrace at LOG_SERVER_ONLY level,
so that the backtrace will appear in the server log but not
be sent to the client.

Bump catalog version.

Authors: Vignesh C, Bharath Rupireddy
---
 doc/src/sgml/func.sgml                    | 62 +++++++++++++++++++
 src/backend/catalog/system_functions.sql  |  2 +
 src/backend/postmaster/autovacuum.c       |  4 ++
 src/backend/postmaster/checkpointer.c     |  4 ++
 src/backend/postmaster/interrupt.c        |  4 ++
 src/backend/postmaster/pgarch.c           | 10 +++-
 src/backend/postmaster/startup.c          |  4 ++
 src/backend/postmaster/walwriter.c        |  4 ++
 src/backend/storage/ipc/procarray.c       | 56 +++++++++++++++++
 src/backend/storage/ipc/procsignal.c      | 42 +++++++++++++
 src/backend/storage/ipc/signalfuncs.c     | 73 ++++++++++++++++-------
 src/backend/tcop/postgres.c               |  4 ++
 src/backend/utils/adt/mcxtfuncs.c         | 40 +++----------
 src/backend/utils/error/elog.c            | 19 ++++--
 src/backend/utils/init/globals.c          |  1 +
 src/include/catalog/pg_proc.dat           |  5 ++
 src/include/miscadmin.h                   |  1 +
 src/include/storage/procarray.h           |  2 +
 src/include/storage/procsignal.h          |  3 +-
 src/include/utils/elog.h                  |  2 +
 src/test/regress/expected/backtrace.out   | 49 +++++++++++++++
 src/test/regress/expected/backtrace_1.out | 55 +++++++++++++++++
 src/test/regress/parallel_schedule        |  2 +-
 src/test/regress/sql/backtrace.sql        | 33 ++++++++++
 24 files changed, 416 insertions(+), 65 deletions(-)
 create mode 100644 src/test/regress/expected/backtrace.out
 create mode 100644 src/test/regress/expected/backtrace_1.out
 create mode 100644 src/test/regress/sql/backtrace.sql

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index b727c3423aa..9f321fb019a 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25355,6 +25355,30 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_log_backtrace</primary>
+        </indexterm>
+        <function>pg_log_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Requests to log the backtrace of the backend with the
+        specified process ID.  This function can send the request to
+        backends and auxiliary processes except logger and statistics
+        collector.  These backtraces 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 identifies
+        which function a process is currently executing and it may be useful
+        for developers to diagnose stuck processes and other problems. This
+        function is supported only if PostgreSQL was built with the ability to
+        capture backtraces, otherwise it will emit a warning.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -25574,6 +25598,44 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_log_backtrace</function> can be used to log the backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace
+------------------
+ t
+(1 row)
+</programlisting>
+The backtrace will be logged as specified by the logging configuration.
+For example:
+<screen>
+2021-01-27 11:33:50.247 IST [111735] LOG:  current backtrace:
+        postgres: postgresdba postgres [local] SELECT(set_backtrace+0x38) [0xae06c5]
+        postgres: postgresdba postgres [local] SELECT(ProcessInterrupts+0x788) [0x950c34]
+        postgres: postgresdba postgres [local] SELECT() [0x761e89]
+        postgres: postgresdba postgres [local] SELECT() [0x71bbda]
+        postgres: postgresdba postgres [local] SELECT() [0x71e380]
+        postgres: postgresdba postgres [local] SELECT(standard_ExecutorRun+0x1d6) [0x71c1fe]
+        postgres: postgresdba postgres [local] SELECT(ExecutorRun+0x55) [0x71c026]
+        postgres: postgresdba postgres [local] SELECT() [0x953fc5]
+        postgres: postgresdba postgres [local] SELECT(PortalRun+0x262) [0x953c7e]
+        postgres: postgresdba postgres [local] SELECT() [0x94db78]
+        postgres: postgresdba postgres [local] SELECT(PostgresMain+0x7d7) [0x951e72]
+        postgres: postgresdba postgres [local] SELECT() [0x896b2f]
+        postgres: postgresdba postgres [local] SELECT() [0x8964b5]
+        postgres: postgresdba postgres [local] SELECT() [0x892a79]
+        postgres: postgresdba postgres [local] SELECT(PostmasterMain+0x116b) [0x892350]
+        postgres: postgresdba postgres [local] SELECT() [0x795f72]
+        /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2107bbd505]
+        postgres: postgresdba postgres [local] SELECT() [0x4842a9]
+</screen>
+    You can get the file name and line number from the logged details by using
+    gdb/addr2line in linux platforms (users must ensure gdb/addr2line is
+    already installed).
+   </para>
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 08a14e3a7f9..d53b3f76782 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -710,6 +710,8 @@ REVOKE EXECUTE ON FUNCTION pg_ls_logicalmapdir() FROM PUBLIC;
 
 REVOKE EXECUTE ON FUNCTION pg_ls_replslotdir(text) FROM PUBLIC;
 
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer) FROM PUBLIC;
+
 --
 -- We also set up some things as accessible to standard roles.
 --
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 681ef91b81e..a572412f2a4 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -841,6 +841,10 @@ HandleAutoVacLauncherInterrupts(void)
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
 
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
+
 	/* Process sinval catchup interrupts that happened while sleeping */
 	ProcessCatchupInterrupt();
 }
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 4488e3a4435..e9af4ca74ef 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -582,6 +582,10 @@ HandleCheckpointerInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 /*
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index 3f412dad2e6..84b1de49679 100644
--- a/src/backend/postmaster/interrupt.c
+++ b/src/backend/postmaster/interrupt.c
@@ -48,6 +48,10 @@ HandleMainLoopInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 /*
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index d916ed39a8c..0726072e327 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -765,9 +765,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, config update and request for logging of
- * memory contexts, but not shutdown request because how to handle
- * shutdown request is different between those loops.
+ * It checks for barrier events, config update, request for logging of
+ * memory contexts and backtrace, but not shutdown request because how to
+ * handle shutdown request is different between those loops.
  */
 static void
 HandlePgArchInterrupts(void)
@@ -779,6 +779,10 @@ HandlePgArchInterrupts(void)
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
 
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
+
 	if (ConfigReloadPending)
 	{
 		char	   *archiveLib = pstrdup(XLogArchiveLibrary);
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index 29cf8f18e1a..013d8fe68f5 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -206,6 +206,10 @@ HandleStartupProcInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 102fa2a089f..52d5a82ecb1 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -310,4 +310,8 @@ HandleWalWriterInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 13d192ec2b4..d5b75e0db4d 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -3149,6 +3149,62 @@ HaveVirtualXIDsDelayingChkpt(VirtualTransactionId *vxids, int nvxids)
 	return result;
 }
 
+/*
+ * CheckPostgresProcessId -- check if the process with given pid is a backend
+ * or an auxiliary process and return its PGPROC.
+ *
+ * Returns NULL if not found.
+ */
+PGPROC *
+CheckPostgresProcessId(int pid, bool chk_auxiliary_proc, BackendId *backendId)
+{
+	PGPROC	   *result;
+
+	/*
+	 * Get backend id from PGPROC for a backend. Since auxiliary processes
+	 * (except the startup process) don't have a valid backend id, return
+	 * InvalidBackendId.
+	 */
+	if (backendId)
+		*backendId = InvalidBackendId;
+
+	/* See if the process with given pid is a backend */
+	result = BackendPidGetProc(pid);
+
+	if (result && backendId)
+		*backendId = result->backendId;
+	else if (chk_auxiliary_proc)
+	{
+		/* See if the process with given pid is an auxiliary process */
+		result = 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 (result == NULL)
+	{
+		/*
+		 * This is just a warning so a loop-through-resultset will not abort
+		 * if one backend terminated on its own during the run.
+		 */
+		if (chk_auxiliary_proc)
+			ereport(WARNING,
+					(errmsg("PID %d is not a PostgreSQL server process", pid)));
+		else
+			ereport(WARNING,
+					(errmsg("PID %d is not a PostgreSQL backend process", pid)));
+	}
+
+	return result;
+}
+
 /*
  * BackendPidGetProc -- get a backend's PGPROC given its PID
  *
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index f41563a0a48..56fa7231cac 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -603,6 +603,45 @@ ResetProcSignalBarrierBits(uint32 flags)
 	InterruptPending = true;
 }
 
+/*
+ * HandleLogBacktraceInterrupt - Handle receipt of an interrupt requesting to
+ * log a backtrace.
+ *
+ * All the actual work is deferred to ProcessLogBacktraceInterrupt(),
+ * because we cannot safely emit a log message inside the signal handler.
+ */
+static void
+HandleLogBacktraceInterrupt(void)
+{
+	InterruptPending = true;
+	LogBacktracePending = true;
+	/* latch will be set by procsignal_sigusr1_handler */
+}
+
+/*
+ * ProcessLogBacktraceInterrupt - Perform logging of backtrace of this
+ * backend process.
+ *
+ * Any backend that participates in ProcSignal signaling must arrange
+ * to call this function if we see LogBacktracePending set.
+ * CHECK_FOR_INTERRUPTS() or from process specific interrupt handlers.
+ */
+void
+ProcessLogBacktraceInterrupt(void)
+{
+	LogBacktracePending = false;
+
+	/*
+	 * Use LOG_SERVER_ONLY to prevent this message from being sent to the
+	 * connected client.
+	 */
+	ereport(LOG_SERVER_ONLY,
+			errhidestmt(true),
+			errhidecontext(true),
+			errmsg_internal("logging backtrace of PID %d", MyProcPid));
+	set_backtrace(NULL, 0);
+}
+
 /*
  * CheckProcSignal - check to see if a particular reason has been
  * signaled, and clear the signal flag.  Should be called after receiving
@@ -652,6 +691,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_LOG_MEMORY_CONTEXT))
 		HandleLogMemoryContextInterrupt();
 
+	if (CheckProcSignal(PROCSIG_LOG_BACKTRACE))
+		HandleLogBacktraceInterrupt();
+
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
 
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index 6e310b14ebd..16d39467ea0 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -48,31 +48,12 @@
 static int
 pg_signal_backend(int pid, int sig)
 {
-	PGPROC	   *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 so far all the callers of
-	 * this mechanism involve some request for ending the process anyway, that
-	 * it might end on its own first is not a problem.
-	 *
-	 * Note that proc will also be NULL if the pid refers to an auxiliary
-	 * process or the postmaster (neither of which can be signaled via
-	 * pg_signal_backend()).
-	 */
-	if (proc == NULL)
-	{
-		/*
-		 * This is just a warning so a loop-through-resultset will not abort
-		 * if one backend terminated on its own during the run.
-		 */
-		ereport(WARNING,
-				(errmsg("PID %d is not a PostgreSQL backend process", pid)));
+	PGPROC  *proc;
 
+	/* Users can only signal valid backend or an auxiliary process. */
+	proc = CheckPostgresProcessId(pid, false, NULL);
+	if (!proc)
 		return SIGNAL_BACKEND_ERROR;
-	}
 
 	/* Only allow superusers to signal superuser-owned backends. */
 	if (superuser_arg(proc->roleId) && !superuser())
@@ -303,3 +284,49 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_log_backtrace
+ *		Signal a backend or an auxiliary process to log its 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 and which can lead to denial of
+ * service. Additional roles can be permitted with GRANT.
+ *
+ * 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 backtrace.
+ */
+Datum
+pg_log_backtrace(PG_FUNCTION_ARGS)
+{
+	int			pid = PG_GETARG_INT32(0);
+	BackendId   backendId;
+
+#ifndef HAVE_BACKTRACE_SYMBOLS
+	ereport(WARNING,
+			errmsg("backtrace generation is not supported by this installation"),
+			errhint("You need to rebuild PostgreSQL using a library containing backtrace_symbols."));
+	PG_RETURN_BOOL(false);
+#endif
+
+	/* Get the process id of the backend or an auxiliary process */
+	if (!CheckPostgresProcessId(pid, true, &backendId))
+		PG_RETURN_BOOL(false);
+
+	/*
+	 * If the given process is a backend, its backend id from PGPROC is used in
+	 * SendProcSignal() later to speed up the operation. Otherwise,
+	 * InvalidBackendId is used because auxiliary processes (except the startup
+	 * process) don't have a valid backend id.
+	 */
+	if (SendProcSignal(pid, PROCSIG_LOG_BACKTRACE, backendId))
+	{
+		ereport(WARNING,
+				(errmsg("could not send signal to process %d: %m", pid)));
+		PG_RETURN_BOOL(false);
+	}
+
+	PG_RETURN_BOOL(true);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index d7e39aed64b..1488192d05e 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3398,6 +3398,10 @@ ProcessInterrupts(void)
 
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index bb7cc940249..b8d70a37313 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -145,44 +145,18 @@ Datum
 pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
 {
 	int			pid = PG_GETARG_INT32(0);
-	PGPROC	   *proc;
 	BackendId	backendId = InvalidBackendId;
 
-	proc = BackendPidGetProc(pid);
-
-	/*
-	 * 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);
+	/* Get the process id of the backend or an auxiliary process */
+	if (!CheckPostgresProcessId(pid, true, &backendId))
+		PG_RETURN_BOOL(false);
 
 	/*
-	 * 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 the given process is a backend, its backend id from PGPROC is used in
+	 * SendProcSignal() later to speed up the operation. Otherwise,
+	 * InvalidBackendId is used because auxiliary processes (except the startup
+	 * process) don't have a valid backend id.
 	 */
-	if (proc == NULL)
-	{
-		/*
-		 * This is just a warning so a loop-through-resultset will not abort
-		 * if one backend terminated on its own during the run.
-		 */
-		ereport(WARNING,
-				(errmsg("PID %d is not a PostgreSQL server process", pid)));
-		PG_RETURN_BOOL(false);
-	}
-
 	if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, backendId) < 0)
 	{
 		/* Again, just a warning to allow loops */
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 7402696986b..522a525741c 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -172,7 +172,6 @@ static char formatted_log_time[FORMATTED_TS_LEN];
 
 
 static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
-static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
 static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
 static void write_console(const char *line, int len);
 static const char *process_log_prefix_padding(const char *p, int *padding);
@@ -944,9 +943,10 @@ errbacktrace(void)
  * Compute backtrace data and add it to the supplied ErrorData.  num_skip
  * specifies how many inner frames to skip.  Use this to avoid showing the
  * internal backtrace support functions in the backtrace.  This requires that
- * this and related functions are not inlined.
+ * this and related functions are not inlined. If the edata pointer is valid,
+ * backtrace information will be set in edata.
  */
-static void
+void
 set_backtrace(ErrorData *edata, int num_skip)
 {
 	StringInfoData errtrace;
@@ -973,7 +973,18 @@ set_backtrace(ErrorData *edata, int num_skip)
 						   "backtrace generation is not supported by this installation");
 #endif
 
-	edata->backtrace = errtrace.data;
+	if (edata)
+		edata->backtrace = errtrace.data;
+	else
+	{
+		/*
+		 * LOG_SERVER_ONLY is used to make sure the backtrace is never
+		 * sent to client. We want to avoid messing up the other client
+		 * session.
+		 */
+		elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
+		pfree(errtrace.data);
+	}
 }
 
 /*
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 3419c099b28..2dd2cc3dc67 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -36,6 +36,7 @@ volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t IdleSessionTimeoutPending = false;
 volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile sig_atomic_t LogMemoryContextPending = false;
+volatile sig_atomic_t LogBacktracePending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 6fa7897580d..bda1f41dc71 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11765,4 +11765,9 @@
   prorettype => 'bytea', proargtypes => 'pg_brin_minmax_multi_summary',
   prosrc => 'brin_minmax_multi_summary_send' },
 
+# function to get the backtrace of server process
+{ oid => '6105', descr => 'log backtrace of server process',
+  proname => 'pg_log_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_log_backtrace' },
+
 ]
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 0abc3ad5405..2309874d47f 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -94,6 +94,7 @@ extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
 extern PGDLLIMPORT volatile sig_atomic_t LogMemoryContextPending;
+extern PGDLLIMPORT volatile sig_atomic_t LogBacktracePending;
 
 extern PGDLLIMPORT volatile sig_atomic_t CheckClientConnectionPending;
 extern PGDLLIMPORT volatile sig_atomic_t ClientConnectionLost;
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index e03692053ee..df3d3f1b32e 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -66,6 +66,8 @@ extern PGPROC *BackendPidGetProc(int pid);
 extern PGPROC *BackendPidGetProcWithLock(int pid);
 extern int	BackendXidGetPid(TransactionId xid);
 extern bool IsBackendPid(int pid);
+extern PGPROC *CheckPostgresProcessId(int pid, bool chk_auxiliary_proc,
+									  BackendId *backendId);
 
 extern VirtualTransactionId *GetCurrentVirtualXIDs(TransactionId limitXmin,
 												   bool excludeXmin0, bool allDbs, int excludeVacuum,
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index ee636900f33..af3954d5644 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -35,6 +35,7 @@ typedef enum
 	PROCSIG_WALSND_INIT_STOPPING,	/* ask walsenders to prepare for shutdown  */
 	PROCSIG_BARRIER,			/* global barrier interrupt  */
 	PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
+	PROCSIG_LOG_BACKTRACE,		/* ask backend to log the current backtrace */
 
 	/* Recovery conflict reasons */
 	PROCSIG_RECOVERY_CONFLICT_DATABASE,
@@ -65,7 +66,7 @@ extern int	SendProcSignal(pid_t pid, ProcSignalReason reason,
 extern uint64 EmitProcSignalBarrier(ProcSignalBarrierType type);
 extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
-
+extern void ProcessLogBacktraceInterrupt(void);
 extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
 
 #endif							/* PROCSIGNAL_H */
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 3eb8de39661..074f258674d 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -467,4 +467,6 @@ extern void set_syslog_parameters(const char *ident, int facility);
  */
 extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
 
+extern pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
+
 #endif							/* ELOG_H */
diff --git a/src/test/regress/expected/backtrace.out b/src/test/regress/expected/backtrace.out
new file mode 100644
index 00000000000..2184a99483a
--- /dev/null
+++ b/src/test/regress/expected/backtrace.out
@@ -0,0 +1,49 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/expected/backtrace_1.out b/src/test/regress/expected/backtrace_1.out
new file mode 100644
index 00000000000..fe9523f89d6
--- /dev/null
+++ b/src/test/regress/expected/backtrace_1.out
@@ -0,0 +1,55 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 6d8f524ae9e..d8c647968a3 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -86,7 +86,7 @@ test: brin_bloom brin_multi
 # psql depends on create_am
 # amutils depends on geometry, create_index_spgist, hash_index, brin
 # ----------
-test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role
+test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role backtrace
 
 # collate.*.utf8 tests cannot be run in parallel with each other
 test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8
diff --git a/src/test/regress/sql/backtrace.sql b/src/test/regress/sql/backtrace.sql
new file mode 100644
index 00000000000..d74b1016ae2
--- /dev/null
+++ b/src/test/regress/sql/backtrace.sql
@@ -0,0 +1,33 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+
+SELECT pg_log_backtrace(pg_backend_pid());
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+
+CREATE ROLE regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+RESET ROLE;
+
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+
+DROP ROLE regress_log_backtrace;
-- 
2.17.1

0002-f-minor-fixen.patchtext/x-diff; charset=us-asciiDownload
From cc5c6f68ca4622d7e4fd294924a99ea866b31833 Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Wed, 9 Mar 2022 08:54:06 -0600
Subject: [PATCH 2/2] f!minor fixen

---
 doc/src/sgml/func.sgml                |  6 +++---
 src/backend/postmaster/pgarch.c       |  2 +-
 src/backend/storage/ipc/procarray.c   |  6 +++---
 src/backend/storage/ipc/signalfuncs.c | 10 +++++-----
 src/backend/utils/adt/mcxtfuncs.c     |  8 ++++----
 5 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 9f321fb019a..b33e69181e9 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25366,8 +25366,8 @@ SELECT collation for ('foo' COLLATE "de_DE");
        <para>
         Requests to log the backtrace of the backend with the
         specified process ID.  This function can send the request to
-        backends and auxiliary processes except logger and statistics
-        collector.  These backtraces will be logged at <literal>LOG</literal>
+        backends and auxiliary processes except the logger and statistics
+        collector.  The backtraces 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
@@ -25631,7 +25631,7 @@ For example:
         /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2107bbd505]
         postgres: postgresdba postgres [local] SELECT() [0x4842a9]
 </screen>
-    You can get the file name and line number from the logged details by using
+    You can obtain the file name and line number from the logged details by using
     gdb/addr2line in linux platforms (users must ensure gdb/addr2line is
     already installed).
    </para>
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 0726072e327..af55e39cc15 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -766,7 +766,7 @@ pgarch_die(int code, Datum arg)
  *
  * This is called in the loops pgarch_MainLoop and pgarch_ArchiverCopyLoop.
  * It checks for barrier events, config update, request for logging of
- * memory contexts and backtrace, but not shutdown request because how to
+ * memory contexts and backtraces, but not shutdown request because how to
  * handle shutdown request is different between those loops.
  */
 static void
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index d5b75e0db4d..3ad7dd4a782 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -3186,7 +3186,7 @@ CheckPostgresProcessId(int pid, bool chk_auxiliary_proc, BackendId *backendId)
 	 * 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.
+	 * own first without logging the requested info is not a problem.
 	 */
 	if (result == NULL)
 	{
@@ -3196,10 +3196,10 @@ CheckPostgresProcessId(int pid, bool chk_auxiliary_proc, BackendId *backendId)
 		 */
 		if (chk_auxiliary_proc)
 			ereport(WARNING,
-					(errmsg("PID %d is not a PostgreSQL server process", pid)));
+					errmsg("PID %d is not a PostgreSQL server process", pid));
 		else
 			ereport(WARNING,
-					(errmsg("PID %d is not a PostgreSQL backend process", pid)));
+					errmsg("PID %d is not a PostgreSQL backend process", pid));
 	}
 
 	return result;
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index 16d39467ea0..727158098e3 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -290,9 +290,9 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
  *		Signal a backend or an auxiliary process to log its 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 and which can lead to denial of
- * service. Additional roles can be permitted with GRANT.
+ * because allowing any user to issue this request at an unbounded
+ * rate would cause lots of log messages which can lead to denial of service.
+ * Additional roles can be permitted with GRANT.
  *
  * 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()
@@ -311,7 +311,7 @@ pg_log_backtrace(PG_FUNCTION_ARGS)
 	PG_RETURN_BOOL(false);
 #endif
 
-	/* Get the process id of the backend or an auxiliary process */
+	/* Get the process id of the backend or auxiliary process */
 	if (!CheckPostgresProcessId(pid, true, &backendId))
 		PG_RETURN_BOOL(false);
 
@@ -324,7 +324,7 @@ pg_log_backtrace(PG_FUNCTION_ARGS)
 	if (SendProcSignal(pid, PROCSIG_LOG_BACKTRACE, backendId))
 	{
 		ereport(WARNING,
-				(errmsg("could not send signal to process %d: %m", pid)));
+				errmsg("could not send signal to process %d: %m", pid));
 		PG_RETURN_BOOL(false);
 	}
 
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index b8d70a37313..f338781695d 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -133,9 +133,9 @@ pg_get_backend_memory_contexts(PG_FUNCTION_ARGS)
  *		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.
+ * contexts because allowing any user to issue this request at an unbounded
+ * rate would cause lots of log messages which can lead to denial of service.
+ * Additional roles can be permitted with GRANT.
  *
  * 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()
@@ -147,7 +147,7 @@ pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
 	int			pid = PG_GETARG_INT32(0);
 	BackendId	backendId = InvalidBackendId;
 
-	/* Get the process id of the backend or an auxiliary process */
+	/* Get the process id of the backend or auxiliary process */
 	if (!CheckPostgresProcessId(pid, true, &backendId))
 		PG_RETURN_BOOL(false);
 
-- 
2.17.1

#99vignesh C
vignesh21@gmail.com
In reply to: Justin Pryzby (#98)
1 attachment(s)
Re: Printing backtrace of postgres processes

On Wed, Mar 9, 2022 at 9:26 PM Justin Pryzby <pryzby@telsasoft.com> wrote:

rebased to appease cfbot.

+ couple of little fixes as 0002.

Thanks for rebasing and fixing a few issues. I have taken all your
changes except for mcxtfuncs changes as those changes were not done as
part of this patch. Attached v19 patch which has the changes for the
same.

Regards,
Vignesh

Attachments:

v19-0001-Add-function-to-log-the-backtrace-of-the-specifi.patchtext/x-patch; charset=US-ASCII; name=v19-0001-Add-function-to-log-the-backtrace-of-the-specifi.patchDownload
From baff4bd9331eff3d3d021724847c2fb89d4bcdcf Mon Sep 17 00:00:00 2001
From: Vigneshwaran C <vignesh21@gmail.com>
Date: Tue, 25 Jan 2022 08:21:22 +0530
Subject: [PATCH v19] Add function to log the backtrace of the specified
 postgres process.

This commit adds pg_log_backtrace() function that requests to log
the backtrace of the specified backend or auxiliary process except
logger and statistic collector.

Only superusers are allowed to request to log the backtrace
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.

On receipt of the request, at the next CHECK_FOR_INTERRUPTS(),
the target backend logs its backtrace at LOG_SERVER_ONLY level,
so that the backtrace will appear in the server log but not
be sent to the client.

Bump catalog version.

Authors: Vignesh C, Bharath Rupireddy, Justin Pryzby
Reviewers: Bharath Rupireddy, Justin Pryzby, Fujii Masao, Atsushi Torikoshi, Dilip Kumar, Robert Haas, Andres Freund, Tom lane, Craig Ringer
Discussion: https://www.postgresql.org/message-id/CALDaNm3ZzmFS-=r7oDUzj7y7BgQv+N06Kqyft6C3xZDoKnk_6w@mail.gmail.com
---
 doc/src/sgml/func.sgml                    | 62 +++++++++++++++++++
 src/backend/catalog/system_functions.sql  |  2 +
 src/backend/postmaster/autovacuum.c       |  4 ++
 src/backend/postmaster/checkpointer.c     |  4 ++
 src/backend/postmaster/interrupt.c        |  4 ++
 src/backend/postmaster/pgarch.c           | 10 +++-
 src/backend/postmaster/startup.c          |  4 ++
 src/backend/postmaster/walwriter.c        |  4 ++
 src/backend/storage/ipc/procarray.c       | 56 +++++++++++++++++
 src/backend/storage/ipc/procsignal.c      | 42 +++++++++++++
 src/backend/storage/ipc/signalfuncs.c     | 73 ++++++++++++++++-------
 src/backend/tcop/postgres.c               |  4 ++
 src/backend/utils/adt/mcxtfuncs.c         | 40 +++----------
 src/backend/utils/error/elog.c            | 19 ++++--
 src/backend/utils/init/globals.c          |  1 +
 src/include/catalog/pg_proc.dat           |  5 ++
 src/include/miscadmin.h                   |  1 +
 src/include/storage/procarray.h           |  2 +
 src/include/storage/procsignal.h          |  3 +-
 src/include/utils/elog.h                  |  2 +
 src/test/regress/expected/backtrace.out   | 49 +++++++++++++++
 src/test/regress/expected/backtrace_1.out | 55 +++++++++++++++++
 src/test/regress/parallel_schedule        |  2 +-
 src/test/regress/sql/backtrace.sql        | 33 ++++++++++
 24 files changed, 416 insertions(+), 65 deletions(-)
 create mode 100644 src/test/regress/expected/backtrace.out
 create mode 100644 src/test/regress/expected/backtrace_1.out
 create mode 100644 src/test/regress/sql/backtrace.sql

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 8a802fb225..4171208719 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25355,6 +25355,30 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_log_backtrace</primary>
+        </indexterm>
+        <function>pg_log_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Requests to log the backtrace of the backend with the
+        specified process ID.  This function can send the request to
+        backends and auxiliary processes except the logger and statistics
+        collector.  The backtraces 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 identifies
+        which function a process is currently executing and it may be useful
+        for developers to diagnose stuck processes and other problems. This
+        function is supported only if PostgreSQL was built with the ability to
+        capture backtraces, otherwise it will emit a warning.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -25574,6 +25598,44 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_log_backtrace</function> can be used to log the backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace
+------------------
+ t
+(1 row)
+</programlisting>
+The backtrace will be logged as specified by the logging configuration.
+For example:
+<screen>
+2021-01-27 11:33:50.247 IST [111735] LOG:  current backtrace:
+        postgres: postgresdba postgres [local] SELECT(set_backtrace+0x38) [0xae06c5]
+        postgres: postgresdba postgres [local] SELECT(ProcessInterrupts+0x788) [0x950c34]
+        postgres: postgresdba postgres [local] SELECT() [0x761e89]
+        postgres: postgresdba postgres [local] SELECT() [0x71bbda]
+        postgres: postgresdba postgres [local] SELECT() [0x71e380]
+        postgres: postgresdba postgres [local] SELECT(standard_ExecutorRun+0x1d6) [0x71c1fe]
+        postgres: postgresdba postgres [local] SELECT(ExecutorRun+0x55) [0x71c026]
+        postgres: postgresdba postgres [local] SELECT() [0x953fc5]
+        postgres: postgresdba postgres [local] SELECT(PortalRun+0x262) [0x953c7e]
+        postgres: postgresdba postgres [local] SELECT() [0x94db78]
+        postgres: postgresdba postgres [local] SELECT(PostgresMain+0x7d7) [0x951e72]
+        postgres: postgresdba postgres [local] SELECT() [0x896b2f]
+        postgres: postgresdba postgres [local] SELECT() [0x8964b5]
+        postgres: postgresdba postgres [local] SELECT() [0x892a79]
+        postgres: postgresdba postgres [local] SELECT(PostmasterMain+0x116b) [0x892350]
+        postgres: postgresdba postgres [local] SELECT() [0x795f72]
+        /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2107bbd505]
+        postgres: postgresdba postgres [local] SELECT() [0x4842a9]
+</screen>
+    You can obtain the file name and line number from the logged details by using
+    gdb/addr2line in linux platforms (users must ensure gdb/addr2line is
+    already installed).
+   </para>
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 81bac6f581..eab437988a 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -709,6 +709,8 @@ REVOKE EXECUTE ON FUNCTION pg_ls_logicalmapdir() FROM PUBLIC;
 
 REVOKE EXECUTE ON FUNCTION pg_ls_replslotdir(text) FROM PUBLIC;
 
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer) FROM PUBLIC;
+
 --
 -- We also set up some things as accessible to standard roles.
 --
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 681ef91b81..a572412f2a 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -841,6 +841,10 @@ HandleAutoVacLauncherInterrupts(void)
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
 
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
+
 	/* Process sinval catchup interrupts that happened while sleeping */
 	ProcessCatchupInterrupt();
 }
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 4488e3a443..e9af4ca74e 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -582,6 +582,10 @@ HandleCheckpointerInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 /*
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index 3f412dad2e..84b1de4967 100644
--- a/src/backend/postmaster/interrupt.c
+++ b/src/backend/postmaster/interrupt.c
@@ -48,6 +48,10 @@ HandleMainLoopInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 /*
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index d916ed39a8..af55e39cc1 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -765,9 +765,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, config update and request for logging of
- * memory contexts, but not shutdown request because how to handle
- * shutdown request is different between those loops.
+ * It checks for barrier events, config update, request for logging of
+ * memory contexts and backtraces, but not shutdown request because how to
+ * handle shutdown request is different between those loops.
  */
 static void
 HandlePgArchInterrupts(void)
@@ -779,6 +779,10 @@ HandlePgArchInterrupts(void)
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
 
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
+
 	if (ConfigReloadPending)
 	{
 		char	   *archiveLib = pstrdup(XLogArchiveLibrary);
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index 29cf8f18e1..013d8fe68f 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -206,6 +206,10 @@ HandleStartupProcInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 102fa2a089..52d5a82ecb 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -310,4 +310,8 @@ HandleWalWriterInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 13d192ec2b..3ad7dd4a78 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -3149,6 +3149,62 @@ HaveVirtualXIDsDelayingChkpt(VirtualTransactionId *vxids, int nvxids)
 	return result;
 }
 
+/*
+ * CheckPostgresProcessId -- check if the process with given pid is a backend
+ * or an auxiliary process and return its PGPROC.
+ *
+ * Returns NULL if not found.
+ */
+PGPROC *
+CheckPostgresProcessId(int pid, bool chk_auxiliary_proc, BackendId *backendId)
+{
+	PGPROC	   *result;
+
+	/*
+	 * Get backend id from PGPROC for a backend. Since auxiliary processes
+	 * (except the startup process) don't have a valid backend id, return
+	 * InvalidBackendId.
+	 */
+	if (backendId)
+		*backendId = InvalidBackendId;
+
+	/* See if the process with given pid is a backend */
+	result = BackendPidGetProc(pid);
+
+	if (result && backendId)
+		*backendId = result->backendId;
+	else if (chk_auxiliary_proc)
+	{
+		/* See if the process with given pid is an auxiliary process */
+		result = 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 without logging the requested info is not a problem.
+	 */
+	if (result == NULL)
+	{
+		/*
+		 * This is just a warning so a loop-through-resultset will not abort
+		 * if one backend terminated on its own during the run.
+		 */
+		if (chk_auxiliary_proc)
+			ereport(WARNING,
+					errmsg("PID %d is not a PostgreSQL server process", pid));
+		else
+			ereport(WARNING,
+					errmsg("PID %d is not a PostgreSQL backend process", pid));
+	}
+
+	return result;
+}
+
 /*
  * BackendPidGetProc -- get a backend's PGPROC given its PID
  *
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index f41563a0a4..56fa7231ca 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -603,6 +603,45 @@ ResetProcSignalBarrierBits(uint32 flags)
 	InterruptPending = true;
 }
 
+/*
+ * HandleLogBacktraceInterrupt - Handle receipt of an interrupt requesting to
+ * log a backtrace.
+ *
+ * All the actual work is deferred to ProcessLogBacktraceInterrupt(),
+ * because we cannot safely emit a log message inside the signal handler.
+ */
+static void
+HandleLogBacktraceInterrupt(void)
+{
+	InterruptPending = true;
+	LogBacktracePending = true;
+	/* latch will be set by procsignal_sigusr1_handler */
+}
+
+/*
+ * ProcessLogBacktraceInterrupt - Perform logging of backtrace of this
+ * backend process.
+ *
+ * Any backend that participates in ProcSignal signaling must arrange
+ * to call this function if we see LogBacktracePending set.
+ * CHECK_FOR_INTERRUPTS() or from process specific interrupt handlers.
+ */
+void
+ProcessLogBacktraceInterrupt(void)
+{
+	LogBacktracePending = false;
+
+	/*
+	 * Use LOG_SERVER_ONLY to prevent this message from being sent to the
+	 * connected client.
+	 */
+	ereport(LOG_SERVER_ONLY,
+			errhidestmt(true),
+			errhidecontext(true),
+			errmsg_internal("logging backtrace of PID %d", MyProcPid));
+	set_backtrace(NULL, 0);
+}
+
 /*
  * CheckProcSignal - check to see if a particular reason has been
  * signaled, and clear the signal flag.  Should be called after receiving
@@ -652,6 +691,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_LOG_MEMORY_CONTEXT))
 		HandleLogMemoryContextInterrupt();
 
+	if (CheckProcSignal(PROCSIG_LOG_BACKTRACE))
+		HandleLogBacktraceInterrupt();
+
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
 
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index 6e310b14eb..727158098e 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -48,31 +48,12 @@
 static int
 pg_signal_backend(int pid, int sig)
 {
-	PGPROC	   *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 so far all the callers of
-	 * this mechanism involve some request for ending the process anyway, that
-	 * it might end on its own first is not a problem.
-	 *
-	 * Note that proc will also be NULL if the pid refers to an auxiliary
-	 * process or the postmaster (neither of which can be signaled via
-	 * pg_signal_backend()).
-	 */
-	if (proc == NULL)
-	{
-		/*
-		 * This is just a warning so a loop-through-resultset will not abort
-		 * if one backend terminated on its own during the run.
-		 */
-		ereport(WARNING,
-				(errmsg("PID %d is not a PostgreSQL backend process", pid)));
+	PGPROC  *proc;
 
+	/* Users can only signal valid backend or an auxiliary process. */
+	proc = CheckPostgresProcessId(pid, false, NULL);
+	if (!proc)
 		return SIGNAL_BACKEND_ERROR;
-	}
 
 	/* Only allow superusers to signal superuser-owned backends. */
 	if (superuser_arg(proc->roleId) && !superuser())
@@ -303,3 +284,49 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_log_backtrace
+ *		Signal a backend or an auxiliary process to log its backtrace.
+ *
+ * By default, only superusers are allowed to signal to log the backtrace
+ * because allowing any user to issue this request at an unbounded
+ * rate would cause lots of log messages which can lead to denial of service.
+ * Additional roles can be permitted with GRANT.
+ *
+ * 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 backtrace.
+ */
+Datum
+pg_log_backtrace(PG_FUNCTION_ARGS)
+{
+	int			pid = PG_GETARG_INT32(0);
+	BackendId   backendId;
+
+#ifndef HAVE_BACKTRACE_SYMBOLS
+	ereport(WARNING,
+			errmsg("backtrace generation is not supported by this installation"),
+			errhint("You need to rebuild PostgreSQL using a library containing backtrace_symbols."));
+	PG_RETURN_BOOL(false);
+#endif
+
+	/* Get the process id of the backend or auxiliary process */
+	if (!CheckPostgresProcessId(pid, true, &backendId))
+		PG_RETURN_BOOL(false);
+
+	/*
+	 * If the given process is a backend, its backend id from PGPROC is used in
+	 * SendProcSignal() later to speed up the operation. Otherwise,
+	 * InvalidBackendId is used because auxiliary processes (except the startup
+	 * process) don't have a valid backend id.
+	 */
+	if (SendProcSignal(pid, PROCSIG_LOG_BACKTRACE, backendId))
+	{
+		ereport(WARNING,
+				errmsg("could not send signal to process %d: %m", pid));
+		PG_RETURN_BOOL(false);
+	}
+
+	PG_RETURN_BOOL(true);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index ba2fcfeb4a..2b1d1610a7 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3380,6 +3380,10 @@ ProcessInterrupts(void)
 
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index bb7cc94024..b8d70a3731 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -145,44 +145,18 @@ Datum
 pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
 {
 	int			pid = PG_GETARG_INT32(0);
-	PGPROC	   *proc;
 	BackendId	backendId = InvalidBackendId;
 
-	proc = BackendPidGetProc(pid);
-
-	/*
-	 * 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);
+	/* Get the process id of the backend or an auxiliary process */
+	if (!CheckPostgresProcessId(pid, true, &backendId))
+		PG_RETURN_BOOL(false);
 
 	/*
-	 * 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 the given process is a backend, its backend id from PGPROC is used in
+	 * SendProcSignal() later to speed up the operation. Otherwise,
+	 * InvalidBackendId is used because auxiliary processes (except the startup
+	 * process) don't have a valid backend id.
 	 */
-	if (proc == NULL)
-	{
-		/*
-		 * This is just a warning so a loop-through-resultset will not abort
-		 * if one backend terminated on its own during the run.
-		 */
-		ereport(WARNING,
-				(errmsg("PID %d is not a PostgreSQL server process", pid)));
-		PG_RETURN_BOOL(false);
-	}
-
 	if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, backendId) < 0)
 	{
 		/* Again, just a warning to allow loops */
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 7402696986..522a525741 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -172,7 +172,6 @@ static char formatted_log_time[FORMATTED_TS_LEN];
 
 
 static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
-static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
 static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
 static void write_console(const char *line, int len);
 static const char *process_log_prefix_padding(const char *p, int *padding);
@@ -944,9 +943,10 @@ errbacktrace(void)
  * Compute backtrace data and add it to the supplied ErrorData.  num_skip
  * specifies how many inner frames to skip.  Use this to avoid showing the
  * internal backtrace support functions in the backtrace.  This requires that
- * this and related functions are not inlined.
+ * this and related functions are not inlined. If the edata pointer is valid,
+ * backtrace information will be set in edata.
  */
-static void
+void
 set_backtrace(ErrorData *edata, int num_skip)
 {
 	StringInfoData errtrace;
@@ -973,7 +973,18 @@ set_backtrace(ErrorData *edata, int num_skip)
 						   "backtrace generation is not supported by this installation");
 #endif
 
-	edata->backtrace = errtrace.data;
+	if (edata)
+		edata->backtrace = errtrace.data;
+	else
+	{
+		/*
+		 * LOG_SERVER_ONLY is used to make sure the backtrace is never
+		 * sent to client. We want to avoid messing up the other client
+		 * session.
+		 */
+		elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
+		pfree(errtrace.data);
+	}
 }
 
 /*
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 3419c099b2..2dd2cc3dc6 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -36,6 +36,7 @@ volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t IdleSessionTimeoutPending = false;
 volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile sig_atomic_t LogMemoryContextPending = false;
+volatile sig_atomic_t LogBacktracePending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index d8e8715ed1..7750ffd3d1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11747,4 +11747,9 @@
   prorettype => 'bytea', proargtypes => 'pg_brin_minmax_multi_summary',
   prosrc => 'brin_minmax_multi_summary_send' },
 
+# function to get the backtrace of server process
+{ oid => '6105', descr => 'log backtrace of server process',
+  proname => 'pg_log_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_log_backtrace' },
+
 ]
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 0abc3ad540..2309874d47 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -94,6 +94,7 @@ extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
 extern PGDLLIMPORT volatile sig_atomic_t LogMemoryContextPending;
+extern PGDLLIMPORT volatile sig_atomic_t LogBacktracePending;
 
 extern PGDLLIMPORT volatile sig_atomic_t CheckClientConnectionPending;
 extern PGDLLIMPORT volatile sig_atomic_t ClientConnectionLost;
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index e03692053e..df3d3f1b32 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -66,6 +66,8 @@ extern PGPROC *BackendPidGetProc(int pid);
 extern PGPROC *BackendPidGetProcWithLock(int pid);
 extern int	BackendXidGetPid(TransactionId xid);
 extern bool IsBackendPid(int pid);
+extern PGPROC *CheckPostgresProcessId(int pid, bool chk_auxiliary_proc,
+									  BackendId *backendId);
 
 extern VirtualTransactionId *GetCurrentVirtualXIDs(TransactionId limitXmin,
 												   bool excludeXmin0, bool allDbs, int excludeVacuum,
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index ee636900f3..af3954d564 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -35,6 +35,7 @@ typedef enum
 	PROCSIG_WALSND_INIT_STOPPING,	/* ask walsenders to prepare for shutdown  */
 	PROCSIG_BARRIER,			/* global barrier interrupt  */
 	PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
+	PROCSIG_LOG_BACKTRACE,		/* ask backend to log the current backtrace */
 
 	/* Recovery conflict reasons */
 	PROCSIG_RECOVERY_CONFLICT_DATABASE,
@@ -65,7 +66,7 @@ extern int	SendProcSignal(pid_t pid, ProcSignalReason reason,
 extern uint64 EmitProcSignalBarrier(ProcSignalBarrierType type);
 extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
-
+extern void ProcessLogBacktraceInterrupt(void);
 extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
 
 #endif							/* PROCSIGNAL_H */
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 3eb8de3966..074f258674 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -467,4 +467,6 @@ extern void set_syslog_parameters(const char *ident, int facility);
  */
 extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
 
+extern pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
+
 #endif							/* ELOG_H */
diff --git a/src/test/regress/expected/backtrace.out b/src/test/regress/expected/backtrace.out
new file mode 100644
index 0000000000..2184a99483
--- /dev/null
+++ b/src/test/regress/expected/backtrace.out
@@ -0,0 +1,49 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/expected/backtrace_1.out b/src/test/regress/expected/backtrace_1.out
new file mode 100644
index 0000000000..fe9523f89d
--- /dev/null
+++ b/src/test/regress/expected/backtrace_1.out
@@ -0,0 +1,55 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 6d8f524ae9..d8c647968a 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -86,7 +86,7 @@ test: brin_bloom brin_multi
 # psql depends on create_am
 # amutils depends on geometry, create_index_spgist, hash_index, brin
 # ----------
-test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role
+test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role backtrace
 
 # collate.*.utf8 tests cannot be run in parallel with each other
 test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8
diff --git a/src/test/regress/sql/backtrace.sql b/src/test/regress/sql/backtrace.sql
new file mode 100644
index 0000000000..d74b1016ae
--- /dev/null
+++ b/src/test/regress/sql/backtrace.sql
@@ -0,0 +1,33 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+
+SELECT pg_log_backtrace(pg_backend_pid());
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+
+CREATE ROLE regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+RESET ROLE;
+
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+
+DROP ROLE regress_log_backtrace;
-- 
2.32.0

#100Greg Stark
stark@mit.edu
In reply to: vignesh C (#99)
Re: Printing backtrace of postgres processes

Sadly the cfbot is showing a patch conflict again. It's just a trivial
conflict in the regression test schedule so I'm not going to update
the status but it would be good to rebase it so we continue to get
cfbot testing.

#101Justin Pryzby
pryzby@telsasoft.com
In reply to: Greg Stark (#100)
1 attachment(s)
Re: Printing backtrace of postgres processes

On Wed, Mar 30, 2022 at 11:53:52AM -0400, Greg Stark wrote:

Sadly the cfbot is showing a patch conflict again. It's just a trivial
conflict in the regression test schedule so I'm not going to update
the status but it would be good to rebase it so we continue to get
cfbot testing.

Done. No changes.

Attachments:

0001-Add-function-to-log-the-backtrace-of-the-specified-p.patchtext/x-diff; charset=us-asciiDownload
From b984dacb4bf2794705a85555da49fa68ac9d9a6f Mon Sep 17 00:00:00 2001
From: Vigneshwaran C <vignesh21@gmail.com>
Date: Tue, 25 Jan 2022 08:21:22 +0530
Subject: [PATCH] Add function to log the backtrace of the specified postgres
 process.

This commit adds pg_log_backtrace() function that requests to log
the backtrace of the specified backend or auxiliary process except
logger and statistic collector.

Only superusers are allowed to request to log the backtrace
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.

On receipt of the request, at the next CHECK_FOR_INTERRUPTS(),
the target backend logs its backtrace at LOG_SERVER_ONLY level,
so that the backtrace will appear in the server log but not
be sent to the client.

Bump catalog version.

Authors: Vignesh C, Bharath Rupireddy, Justin Pryzby
Reviewers: Bharath Rupireddy, Justin Pryzby, Fujii Masao, Atsushi Torikoshi, Dilip Kumar, Robert Haas, Andres Freund, Tom lane, Craig Ringer
Discussion: https://www.postgresql.org/message-id/CALDaNm3ZzmFS-=r7oDUzj7y7BgQv+N06Kqyft6C3xZDoKnk_6w@mail.gmail.com
---
 doc/src/sgml/func.sgml                    | 62 +++++++++++++++++++
 src/backend/catalog/system_functions.sql  |  2 +
 src/backend/postmaster/autovacuum.c       |  4 ++
 src/backend/postmaster/checkpointer.c     |  4 ++
 src/backend/postmaster/interrupt.c        |  4 ++
 src/backend/postmaster/pgarch.c           | 10 +++-
 src/backend/postmaster/startup.c          |  4 ++
 src/backend/postmaster/walwriter.c        |  4 ++
 src/backend/storage/ipc/procarray.c       | 56 +++++++++++++++++
 src/backend/storage/ipc/procsignal.c      | 42 +++++++++++++
 src/backend/storage/ipc/signalfuncs.c     | 73 ++++++++++++++++-------
 src/backend/tcop/postgres.c               |  4 ++
 src/backend/utils/adt/mcxtfuncs.c         | 40 +++----------
 src/backend/utils/error/elog.c            | 19 ++++--
 src/backend/utils/init/globals.c          |  1 +
 src/include/catalog/pg_proc.dat           |  5 ++
 src/include/miscadmin.h                   |  1 +
 src/include/storage/procarray.h           |  2 +
 src/include/storage/procsignal.h          |  3 +-
 src/include/utils/elog.h                  |  2 +
 src/test/regress/expected/backtrace.out   | 49 +++++++++++++++
 src/test/regress/expected/backtrace_1.out | 55 +++++++++++++++++
 src/test/regress/parallel_schedule        |  2 +-
 src/test/regress/sql/backtrace.sql        | 33 ++++++++++
 24 files changed, 416 insertions(+), 65 deletions(-)
 create mode 100644 src/test/regress/expected/backtrace.out
 create mode 100644 src/test/regress/expected/backtrace_1.out
 create mode 100644 src/test/regress/sql/backtrace.sql

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 524c6b98547..4d187b3a6d8 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25394,6 +25394,30 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_log_backtrace</primary>
+        </indexterm>
+        <function>pg_log_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Requests to log the backtrace of the backend with the
+        specified process ID.  This function can send the request to
+        backends and auxiliary processes except the logger and statistics
+        collector.  The backtraces 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 identifies
+        which function a process is currently executing and it may be useful
+        for developers to diagnose stuck processes and other problems. This
+        function is supported only if PostgreSQL was built with the ability to
+        capture backtraces, otherwise it will emit a warning.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -25614,6 +25638,44 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_log_backtrace</function> can be used to log the backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace
+------------------
+ t
+(1 row)
+</programlisting>
+The backtrace will be logged as specified by the logging configuration.
+For example:
+<screen>
+2021-01-27 11:33:50.247 IST [111735] LOG:  current backtrace:
+        postgres: postgresdba postgres [local] SELECT(set_backtrace+0x38) [0xae06c5]
+        postgres: postgresdba postgres [local] SELECT(ProcessInterrupts+0x788) [0x950c34]
+        postgres: postgresdba postgres [local] SELECT() [0x761e89]
+        postgres: postgresdba postgres [local] SELECT() [0x71bbda]
+        postgres: postgresdba postgres [local] SELECT() [0x71e380]
+        postgres: postgresdba postgres [local] SELECT(standard_ExecutorRun+0x1d6) [0x71c1fe]
+        postgres: postgresdba postgres [local] SELECT(ExecutorRun+0x55) [0x71c026]
+        postgres: postgresdba postgres [local] SELECT() [0x953fc5]
+        postgres: postgresdba postgres [local] SELECT(PortalRun+0x262) [0x953c7e]
+        postgres: postgresdba postgres [local] SELECT() [0x94db78]
+        postgres: postgresdba postgres [local] SELECT(PostgresMain+0x7d7) [0x951e72]
+        postgres: postgresdba postgres [local] SELECT() [0x896b2f]
+        postgres: postgresdba postgres [local] SELECT() [0x8964b5]
+        postgres: postgresdba postgres [local] SELECT() [0x892a79]
+        postgres: postgresdba postgres [local] SELECT(PostmasterMain+0x116b) [0x892350]
+        postgres: postgresdba postgres [local] SELECT() [0x795f72]
+        /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2107bbd505]
+        postgres: postgresdba postgres [local] SELECT() [0x4842a9]
+</screen>
+    You can obtain the file name and line number from the logged details by using
+    gdb/addr2line in linux platforms (users must ensure gdb/addr2line is
+    already installed).
+   </para>
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 08a14e3a7f9..d53b3f76782 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -710,6 +710,8 @@ REVOKE EXECUTE ON FUNCTION pg_ls_logicalmapdir() FROM PUBLIC;
 
 REVOKE EXECUTE ON FUNCTION pg_ls_replslotdir(text) FROM PUBLIC;
 
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer) FROM PUBLIC;
+
 --
 -- We also set up some things as accessible to standard roles.
 --
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index e0f2eefd769..34aced13f16 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -841,6 +841,10 @@ HandleAutoVacLauncherInterrupts(void)
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
 
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
+
 	/* Process sinval catchup interrupts that happened while sleeping */
 	ProcessCatchupInterrupt();
 }
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index a59c3cf0201..9f1d163bec3 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -585,6 +585,10 @@ HandleCheckpointerInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 /*
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index 3f412dad2e6..84b1de49679 100644
--- a/src/backend/postmaster/interrupt.c
+++ b/src/backend/postmaster/interrupt.c
@@ -48,6 +48,10 @@ HandleMainLoopInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 /*
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index d916ed39a8c..af55e39cc15 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -765,9 +765,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, config update and request for logging of
- * memory contexts, but not shutdown request because how to handle
- * shutdown request is different between those loops.
+ * It checks for barrier events, config update, request for logging of
+ * memory contexts and backtraces, but not shutdown request because how to
+ * handle shutdown request is different between those loops.
  */
 static void
 HandlePgArchInterrupts(void)
@@ -779,6 +779,10 @@ HandlePgArchInterrupts(void)
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
 
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
+
 	if (ConfigReloadPending)
 	{
 		char	   *archiveLib = pstrdup(XLogArchiveLibrary);
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index 29cf8f18e1a..013d8fe68f5 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -206,6 +206,10 @@ HandleStartupProcInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 102fa2a089f..52d5a82ecb1 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -310,4 +310,8 @@ HandleWalWriterInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 735763cc242..e58eabb3c1b 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -3161,6 +3161,62 @@ HaveVirtualXIDsDelayingChkpt(VirtualTransactionId *vxids, int nvxids, int type)
 	return result;
 }
 
+/*
+ * CheckPostgresProcessId -- check if the process with given pid is a backend
+ * or an auxiliary process and return its PGPROC.
+ *
+ * Returns NULL if not found.
+ */
+PGPROC *
+CheckPostgresProcessId(int pid, bool chk_auxiliary_proc, BackendId *backendId)
+{
+	PGPROC	   *result;
+
+	/*
+	 * Get backend id from PGPROC for a backend. Since auxiliary processes
+	 * (except the startup process) don't have a valid backend id, return
+	 * InvalidBackendId.
+	 */
+	if (backendId)
+		*backendId = InvalidBackendId;
+
+	/* See if the process with given pid is a backend */
+	result = BackendPidGetProc(pid);
+
+	if (result && backendId)
+		*backendId = result->backendId;
+	else if (chk_auxiliary_proc)
+	{
+		/* See if the process with given pid is an auxiliary process */
+		result = 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 without logging the requested info is not a problem.
+	 */
+	if (result == NULL)
+	{
+		/*
+		 * This is just a warning so a loop-through-resultset will not abort
+		 * if one backend terminated on its own during the run.
+		 */
+		if (chk_auxiliary_proc)
+			ereport(WARNING,
+					errmsg("PID %d is not a PostgreSQL server process", pid));
+		else
+			ereport(WARNING,
+					errmsg("PID %d is not a PostgreSQL backend process", pid));
+	}
+
+	return result;
+}
+
 /*
  * BackendPidGetProc -- get a backend's PGPROC given its PID
  *
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index f41563a0a48..56fa7231cac 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -603,6 +603,45 @@ ResetProcSignalBarrierBits(uint32 flags)
 	InterruptPending = true;
 }
 
+/*
+ * HandleLogBacktraceInterrupt - Handle receipt of an interrupt requesting to
+ * log a backtrace.
+ *
+ * All the actual work is deferred to ProcessLogBacktraceInterrupt(),
+ * because we cannot safely emit a log message inside the signal handler.
+ */
+static void
+HandleLogBacktraceInterrupt(void)
+{
+	InterruptPending = true;
+	LogBacktracePending = true;
+	/* latch will be set by procsignal_sigusr1_handler */
+}
+
+/*
+ * ProcessLogBacktraceInterrupt - Perform logging of backtrace of this
+ * backend process.
+ *
+ * Any backend that participates in ProcSignal signaling must arrange
+ * to call this function if we see LogBacktracePending set.
+ * CHECK_FOR_INTERRUPTS() or from process specific interrupt handlers.
+ */
+void
+ProcessLogBacktraceInterrupt(void)
+{
+	LogBacktracePending = false;
+
+	/*
+	 * Use LOG_SERVER_ONLY to prevent this message from being sent to the
+	 * connected client.
+	 */
+	ereport(LOG_SERVER_ONLY,
+			errhidestmt(true),
+			errhidecontext(true),
+			errmsg_internal("logging backtrace of PID %d", MyProcPid));
+	set_backtrace(NULL, 0);
+}
+
 /*
  * CheckProcSignal - check to see if a particular reason has been
  * signaled, and clear the signal flag.  Should be called after receiving
@@ -652,6 +691,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_LOG_MEMORY_CONTEXT))
 		HandleLogMemoryContextInterrupt();
 
+	if (CheckProcSignal(PROCSIG_LOG_BACKTRACE))
+		HandleLogBacktraceInterrupt();
+
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
 
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index 6e310b14ebd..727158098e3 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -48,31 +48,12 @@
 static int
 pg_signal_backend(int pid, int sig)
 {
-	PGPROC	   *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 so far all the callers of
-	 * this mechanism involve some request for ending the process anyway, that
-	 * it might end on its own first is not a problem.
-	 *
-	 * Note that proc will also be NULL if the pid refers to an auxiliary
-	 * process or the postmaster (neither of which can be signaled via
-	 * pg_signal_backend()).
-	 */
-	if (proc == NULL)
-	{
-		/*
-		 * This is just a warning so a loop-through-resultset will not abort
-		 * if one backend terminated on its own during the run.
-		 */
-		ereport(WARNING,
-				(errmsg("PID %d is not a PostgreSQL backend process", pid)));
+	PGPROC  *proc;
 
+	/* Users can only signal valid backend or an auxiliary process. */
+	proc = CheckPostgresProcessId(pid, false, NULL);
+	if (!proc)
 		return SIGNAL_BACKEND_ERROR;
-	}
 
 	/* Only allow superusers to signal superuser-owned backends. */
 	if (superuser_arg(proc->roleId) && !superuser())
@@ -303,3 +284,49 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_log_backtrace
+ *		Signal a backend or an auxiliary process to log its backtrace.
+ *
+ * By default, only superusers are allowed to signal to log the backtrace
+ * because allowing any user to issue this request at an unbounded
+ * rate would cause lots of log messages which can lead to denial of service.
+ * Additional roles can be permitted with GRANT.
+ *
+ * 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 backtrace.
+ */
+Datum
+pg_log_backtrace(PG_FUNCTION_ARGS)
+{
+	int			pid = PG_GETARG_INT32(0);
+	BackendId   backendId;
+
+#ifndef HAVE_BACKTRACE_SYMBOLS
+	ereport(WARNING,
+			errmsg("backtrace generation is not supported by this installation"),
+			errhint("You need to rebuild PostgreSQL using a library containing backtrace_symbols."));
+	PG_RETURN_BOOL(false);
+#endif
+
+	/* Get the process id of the backend or auxiliary process */
+	if (!CheckPostgresProcessId(pid, true, &backendId))
+		PG_RETURN_BOOL(false);
+
+	/*
+	 * If the given process is a backend, its backend id from PGPROC is used in
+	 * SendProcSignal() later to speed up the operation. Otherwise,
+	 * InvalidBackendId is used because auxiliary processes (except the startup
+	 * process) don't have a valid backend id.
+	 */
+	if (SendProcSignal(pid, PROCSIG_LOG_BACKTRACE, backendId))
+	{
+		ereport(WARNING,
+				errmsg("could not send signal to process %d: %m", pid));
+		PG_RETURN_BOOL(false);
+	}
+
+	PG_RETURN_BOOL(true);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index ba2fcfeb4af..2b1d1610a73 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3380,6 +3380,10 @@ ProcessInterrupts(void)
 
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index bb7cc940249..b8d70a37313 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -145,44 +145,18 @@ Datum
 pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
 {
 	int			pid = PG_GETARG_INT32(0);
-	PGPROC	   *proc;
 	BackendId	backendId = InvalidBackendId;
 
-	proc = BackendPidGetProc(pid);
-
-	/*
-	 * 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);
+	/* Get the process id of the backend or an auxiliary process */
+	if (!CheckPostgresProcessId(pid, true, &backendId))
+		PG_RETURN_BOOL(false);
 
 	/*
-	 * 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 the given process is a backend, its backend id from PGPROC is used in
+	 * SendProcSignal() later to speed up the operation. Otherwise,
+	 * InvalidBackendId is used because auxiliary processes (except the startup
+	 * process) don't have a valid backend id.
 	 */
-	if (proc == NULL)
-	{
-		/*
-		 * This is just a warning so a loop-through-resultset will not abort
-		 * if one backend terminated on its own during the run.
-		 */
-		ereport(WARNING,
-				(errmsg("PID %d is not a PostgreSQL server process", pid)));
-		PG_RETURN_BOOL(false);
-	}
-
 	if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, backendId) < 0)
 	{
 		/* Again, just a warning to allow loops */
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 8826d3dc6f4..b2ddbd79a74 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -172,7 +172,6 @@ static char formatted_log_time[FORMATTED_TS_LEN];
 
 
 static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
-static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
 static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
 static void write_console(const char *line, int len);
 static const char *process_log_prefix_padding(const char *p, int *padding);
@@ -949,9 +948,10 @@ errbacktrace(void)
  * Compute backtrace data and add it to the supplied ErrorData.  num_skip
  * specifies how many inner frames to skip.  Use this to avoid showing the
  * internal backtrace support functions in the backtrace.  This requires that
- * this and related functions are not inlined.
+ * this and related functions are not inlined. If the edata pointer is valid,
+ * backtrace information will be set in edata.
  */
-static void
+void
 set_backtrace(ErrorData *edata, int num_skip)
 {
 	StringInfoData errtrace;
@@ -978,7 +978,18 @@ set_backtrace(ErrorData *edata, int num_skip)
 						   "backtrace generation is not supported by this installation");
 #endif
 
-	edata->backtrace = errtrace.data;
+	if (edata)
+		edata->backtrace = errtrace.data;
+	else
+	{
+		/*
+		 * LOG_SERVER_ONLY is used to make sure the backtrace is never
+		 * sent to client. We want to avoid messing up the other client
+		 * session.
+		 */
+		elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
+		pfree(errtrace.data);
+	}
 }
 
 /*
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 3419c099b28..2dd2cc3dc67 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -36,6 +36,7 @@ volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t IdleSessionTimeoutPending = false;
 volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile sig_atomic_t LogMemoryContextPending = false;
+volatile sig_atomic_t LogBacktracePending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index f6da84dbd3f..3b13bcce50c 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11861,4 +11861,9 @@
   prorettype => 'bytea', proargtypes => 'pg_brin_minmax_multi_summary',
   prosrc => 'brin_minmax_multi_summary_send' },
 
+# function to get the backtrace of server process
+{ oid => '6105', descr => 'log backtrace of server process',
+  proname => 'pg_log_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_log_backtrace' },
+
 ]
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 0abc3ad5405..2309874d47f 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -94,6 +94,7 @@ extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
 extern PGDLLIMPORT volatile sig_atomic_t LogMemoryContextPending;
+extern PGDLLIMPORT volatile sig_atomic_t LogBacktracePending;
 
 extern PGDLLIMPORT volatile sig_atomic_t CheckClientConnectionPending;
 extern PGDLLIMPORT volatile sig_atomic_t ClientConnectionLost;
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index 1b2cfac5ad0..8c881caa819 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -67,6 +67,8 @@ extern PGPROC *BackendPidGetProc(int pid);
 extern PGPROC *BackendPidGetProcWithLock(int pid);
 extern int	BackendXidGetPid(TransactionId xid);
 extern bool IsBackendPid(int pid);
+extern PGPROC *CheckPostgresProcessId(int pid, bool chk_auxiliary_proc,
+									  BackendId *backendId);
 
 extern VirtualTransactionId *GetCurrentVirtualXIDs(TransactionId limitXmin,
 												   bool excludeXmin0, bool allDbs, int excludeVacuum,
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index ee636900f33..af3954d5644 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -35,6 +35,7 @@ typedef enum
 	PROCSIG_WALSND_INIT_STOPPING,	/* ask walsenders to prepare for shutdown  */
 	PROCSIG_BARRIER,			/* global barrier interrupt  */
 	PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
+	PROCSIG_LOG_BACKTRACE,		/* ask backend to log the current backtrace */
 
 	/* Recovery conflict reasons */
 	PROCSIG_RECOVERY_CONFLICT_DATABASE,
@@ -65,7 +66,7 @@ extern int	SendProcSignal(pid_t pid, ProcSignalReason reason,
 extern uint64 EmitProcSignalBarrier(ProcSignalBarrierType type);
 extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
-
+extern void ProcessLogBacktraceInterrupt(void);
 extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
 
 #endif							/* PROCSIGNAL_H */
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 3eb8de39661..074f258674d 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -467,4 +467,6 @@ extern void set_syslog_parameters(const char *ident, int facility);
  */
 extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
 
+extern pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
+
 #endif							/* ELOG_H */
diff --git a/src/test/regress/expected/backtrace.out b/src/test/regress/expected/backtrace.out
new file mode 100644
index 00000000000..2184a99483a
--- /dev/null
+++ b/src/test/regress/expected/backtrace.out
@@ -0,0 +1,49 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/expected/backtrace_1.out b/src/test/regress/expected/backtrace_1.out
new file mode 100644
index 00000000000..fe9523f89d6
--- /dev/null
+++ b/src/test/regress/expected/backtrace_1.out
@@ -0,0 +1,55 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index ef810151253..34fdebe1942 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -86,7 +86,7 @@ test: brin_bloom brin_multi
 # psql depends on create_am
 # amutils depends on geometry, create_index_spgist, hash_index, brin
 # ----------
-test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role
+test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role backtrace
 
 # collate.*.utf8 tests cannot be run in parallel with each other
 test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8
diff --git a/src/test/regress/sql/backtrace.sql b/src/test/regress/sql/backtrace.sql
new file mode 100644
index 00000000000..d74b1016ae2
--- /dev/null
+++ b/src/test/regress/sql/backtrace.sql
@@ -0,0 +1,33 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+
+SELECT pg_log_backtrace(pg_backend_pid());
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+
+CREATE ROLE regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+RESET ROLE;
+
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+
+DROP ROLE regress_log_backtrace;
-- 
2.17.1

#102Robert Haas
robertmhaas@gmail.com
In reply to: Justin Pryzby (#101)
Re: Printing backtrace of postgres processes

On Wed, Mar 30, 2022 at 12:03 PM Justin Pryzby <pryzby@telsasoft.com> wrote:

On Wed, Mar 30, 2022 at 11:53:52AM -0400, Greg Stark wrote:

Sadly the cfbot is showing a patch conflict again. It's just a trivial
conflict in the regression test schedule so I'm not going to update
the status but it would be good to rebase it so we continue to get
cfbot testing.

Done. No changes.

+ if (chk_auxiliary_proc)
+ ereport(WARNING,
+ errmsg("PID %d is not a PostgreSQL server process", pid));
+ else
+ ereport(WARNING,
+ errmsg("PID %d is not a PostgreSQL backend process", pid));

This doesn't look right to me. I don't think that PostgreSQL server
processes are one kind of thing and PostgreSQL backend processes are
another kind of thing. I think they're two terms that are pretty
nearly interchangeable, or maybe at best you want to argue that
backend processes are some subset of server processes. I don't see
this sort of thing adding any clarity.

-static void
+void
set_backtrace(ErrorData *edata, int num_skip)
{
StringInfoData errtrace;
@@ -978,7 +978,18 @@ set_backtrace(ErrorData *edata, int num_skip)
"backtrace generation is not supported by this installation");
#endif

- edata->backtrace = errtrace.data;
+ if (edata)
+ edata->backtrace = errtrace.data;
+ else
+ {
+ /*
+ * LOG_SERVER_ONLY is used to make sure the backtrace is never
+ * sent to client. We want to avoid messing up the other client
+ * session.
+ */
+ elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
+ pfree(errtrace.data);
+ }
 }

This looks like a grotty hack.

- PGPROC    *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 so far all the callers of
- * this mechanism involve some request for ending the process anyway, that
- * it might end on its own first is not a problem.
- *
- * Note that proc will also be NULL if the pid refers to an auxiliary
- * process or the postmaster (neither of which can be signaled via
- * pg_signal_backend()).
- */
- if (proc == NULL)
- {
- /*
- * This is just a warning so a loop-through-resultset will not abort
- * if one backend terminated on its own during the run.
- */
- ereport(WARNING,
- (errmsg("PID %d is not a PostgreSQL backend process", pid)));
+ PGPROC  *proc;
+ /* Users can only signal valid backend or an auxiliary process. */
+ proc = CheckPostgresProcessId(pid, false, NULL);
+ if (!proc)
  return SIGNAL_BACKEND_ERROR;
- }

Incidentally changing the behavior of pg_signal_backend() doesn't seem
like a great idea. We can do that as a separate commit, after
considering whether documentation changes are needed. But it's not
something that should get folded into a commit on another topic.

+ /*
+ * 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 without logging the requested info is not a problem.
+ */

This comment made a lot more sense where it used to be than it does
where it is now. I think more work is required here than just cutting
and pasting.

--
Robert Haas
EDB: http://www.enterprisedb.com

#103vignesh C
vignesh21@gmail.com
In reply to: Robert Haas (#102)
1 attachment(s)
Re: Printing backtrace of postgres processes

On Tue, Apr 5, 2022 at 9:18 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Wed, Mar 30, 2022 at 12:03 PM Justin Pryzby <pryzby@telsasoft.com> wrote:

On Wed, Mar 30, 2022 at 11:53:52AM -0400, Greg Stark wrote:

Sadly the cfbot is showing a patch conflict again. It's just a trivial
conflict in the regression test schedule so I'm not going to update
the status but it would be good to rebase it so we continue to get
cfbot testing.

Done. No changes.

+ if (chk_auxiliary_proc)
+ ereport(WARNING,
+ errmsg("PID %d is not a PostgreSQL server process", pid));
+ else
+ ereport(WARNING,
+ errmsg("PID %d is not a PostgreSQL backend process", pid));

This doesn't look right to me. I don't think that PostgreSQL server
processes are one kind of thing and PostgreSQL backend processes are
another kind of thing. I think they're two terms that are pretty
nearly interchangeable, or maybe at best you want to argue that
backend processes are some subset of server processes. I don't see
this sort of thing adding any clarity.

I have changed it to "PID %d is not a PostgreSQL server process" which
is similar to the existing warning message in
pg_log_backend_memory_contexts.

-static void
+void
set_backtrace(ErrorData *edata, int num_skip)
{
StringInfoData errtrace;
@@ -978,7 +978,18 @@ set_backtrace(ErrorData *edata, int num_skip)
"backtrace generation is not supported by this installation");
#endif

- edata->backtrace = errtrace.data;
+ if (edata)
+ edata->backtrace = errtrace.data;
+ else
+ {
+ /*
+ * LOG_SERVER_ONLY is used to make sure the backtrace is never
+ * sent to client. We want to avoid messing up the other client
+ * session.
+ */
+ elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
+ pfree(errtrace.data);
+ }
}

This looks like a grotty hack.

I have changed it so that the backtrace is set and returned to the
caller. The caller will take care of logging or setting it to the
error data context. Thoughts?

- PGPROC    *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 so far all the callers of
- * this mechanism involve some request for ending the process anyway, that
- * it might end on its own first is not a problem.
- *
- * Note that proc will also be NULL if the pid refers to an auxiliary
- * process or the postmaster (neither of which can be signaled via
- * pg_signal_backend()).
- */
- if (proc == NULL)
- {
- /*
- * This is just a warning so a loop-through-resultset will not abort
- * if one backend terminated on its own during the run.
- */
- ereport(WARNING,
- (errmsg("PID %d is not a PostgreSQL backend process", pid)));
+ PGPROC  *proc;
+ /* Users can only signal valid backend or an auxiliary process. */
+ proc = CheckPostgresProcessId(pid, false, NULL);
+ if (!proc)
return SIGNAL_BACKEND_ERROR;
- }

Incidentally changing the behavior of pg_signal_backend() doesn't seem
like a great idea. We can do that as a separate commit, after
considering whether documentation changes are needed. But it's not
something that should get folded into a commit on another topic.

Agreed. I have kept the logic of pg_signal_backend as it is.
pg_log_backtrace and pg_log_backend_memory_contexts use the same
functionality to check and send signal. I have added a new function
"CheckProcSendDebugSignal" which has the common code implementation
and will be called by both pg_log_backtrace and
pg_log_backend_memory_contexts.

+ /*
+ * 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 without logging the requested info is not a problem.
+ */

This comment made a lot more sense where it used to be than it does
where it is now. I think more work is required here than just cutting
and pasting.

This function was called from pg_signal_backend earlier, this logic is
now moved to CheckProcSendDebugSignal which will be called only by
pg_log_backtrace and pg_log_backend_memory_contexts. This looks
appropriate in CheckProcSendDebugSignal with slight change to specify
it can consume lots of memory or a long running process.
Thoughts?

Attached v20 patch has the changes for the same.

Regards,
Vignesh

Attachments:

v20-0001-Add-function-to-log-the-backtrace-of-the-specifi.patchtext/x-patch; charset=US-ASCII; name=v20-0001-Add-function-to-log-the-backtrace-of-the-specifi.patchDownload
From cc3847b30e0b61218aebdf68e837d366a82ebad1 Mon Sep 17 00:00:00 2001
From: Vigneshwaran C <vignesh21@gmail.com>
Date: Wed, 6 Apr 2022 10:45:56 +0530
Subject: [PATCH v20] Add function to log the backtrace of the specified
 postgres  process.

This commit adds pg_log_backtrace() function that requests to log
the backtrace of the specified backend or auxiliary process except
logger and statistic collector.

Only superusers are allowed to request to log the backtrace
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.

On receipt of the request, at the next CHECK_FOR_INTERRUPTS(),
the target backend logs its backtrace at LOG_SERVER_ONLY level,
so that the backtrace will appear in the server log but not
be sent to the client.

Bump catalog version.

Authors: Vignesh C, Bharath Rupireddy, Justin Pryzby
Reviewers: Bharath Rupireddy, Justin Pryzby, Fujii Masao, Atsushi Torikoshi, Dilip Kumar, Robert Haas, Andres Freund, Tom lane, Craig Ringer
Discussion: https://www.postgresql.org/message-id/CALDaNm3ZzmFS-=r7oDUzj7y7BgQv+N06Kqyft6C3xZDoKnk_6w@mail.gmail.com
---
 doc/src/sgml/func.sgml                    | 62 +++++++++++++++++++++++
 src/backend/catalog/system_functions.sql  |  2 +
 src/backend/postmaster/autovacuum.c       |  4 ++
 src/backend/postmaster/checkpointer.c     |  4 ++
 src/backend/postmaster/interrupt.c        |  4 ++
 src/backend/postmaster/pgarch.c           | 10 ++--
 src/backend/postmaster/startup.c          |  4 ++
 src/backend/postmaster/walwriter.c        |  4 ++
 src/backend/storage/ipc/procarray.c       | 59 +++++++++++++++++++++
 src/backend/storage/ipc/procsignal.c      | 56 ++++++++++++++++++++
 src/backend/storage/ipc/signalfuncs.c     | 30 +++++++++++
 src/backend/tcop/postgres.c               |  4 ++
 src/backend/utils/adt/mcxtfuncs.c         | 49 ++----------------
 src/backend/utils/error/elog.c            | 21 ++++----
 src/backend/utils/init/globals.c          |  1 +
 src/include/catalog/pg_proc.dat           |  5 ++
 src/include/miscadmin.h                   |  1 +
 src/include/storage/procarray.h           |  1 +
 src/include/storage/procsignal.h          |  3 +-
 src/include/utils/elog.h                  |  2 +
 src/test/regress/expected/backtrace.out   | 49 ++++++++++++++++++
 src/test/regress/expected/backtrace_1.out | 55 ++++++++++++++++++++
 src/test/regress/parallel_schedule        |  2 +-
 src/test/regress/sql/backtrace.sql        | 33 ++++++++++++
 24 files changed, 403 insertions(+), 62 deletions(-)
 create mode 100644 src/test/regress/expected/backtrace.out
 create mode 100644 src/test/regress/expected/backtrace_1.out
 create mode 100644 src/test/regress/sql/backtrace.sql

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 4001cb2bda..98b0aec71f 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25365,6 +25365,30 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_log_backtrace</primary>
+        </indexterm>
+        <function>pg_log_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Requests to log the backtrace of the backend with the
+        specified process ID.  This function can send the request to
+        backends and auxiliary processes except the logger and statistics
+        collector.  The backtraces 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 identifies
+        which function a process is currently executing and it may be useful
+        for developers to diagnose stuck processes and other problems. This
+        function is supported only if PostgreSQL was built with the ability to
+        capture backtraces, otherwise it will emit a warning.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -25585,6 +25609,44 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_log_backtrace</function> can be used to log the backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace
+------------------
+ t
+(1 row)
+</programlisting>
+The backtrace will be logged as specified by the logging configuration.
+For example:
+<screen>
+2021-01-27 11:33:50.247 IST [111735] LOG:  current backtrace:
+        postgres: postgresdba postgres [local] SELECT(set_backtrace+0x38) [0xae06c5]
+        postgres: postgresdba postgres [local] SELECT(ProcessInterrupts+0x788) [0x950c34]
+        postgres: postgresdba postgres [local] SELECT() [0x761e89]
+        postgres: postgresdba postgres [local] SELECT() [0x71bbda]
+        postgres: postgresdba postgres [local] SELECT() [0x71e380]
+        postgres: postgresdba postgres [local] SELECT(standard_ExecutorRun+0x1d6) [0x71c1fe]
+        postgres: postgresdba postgres [local] SELECT(ExecutorRun+0x55) [0x71c026]
+        postgres: postgresdba postgres [local] SELECT() [0x953fc5]
+        postgres: postgresdba postgres [local] SELECT(PortalRun+0x262) [0x953c7e]
+        postgres: postgresdba postgres [local] SELECT() [0x94db78]
+        postgres: postgresdba postgres [local] SELECT(PostgresMain+0x7d7) [0x951e72]
+        postgres: postgresdba postgres [local] SELECT() [0x896b2f]
+        postgres: postgresdba postgres [local] SELECT() [0x8964b5]
+        postgres: postgresdba postgres [local] SELECT() [0x892a79]
+        postgres: postgresdba postgres [local] SELECT(PostmasterMain+0x116b) [0x892350]
+        postgres: postgresdba postgres [local] SELECT() [0x795f72]
+        /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2107bbd505]
+        postgres: postgresdba postgres [local] SELECT() [0x4842a9]
+</screen>
+    You can obtain the file name and line number from the logged details by using
+    gdb/addr2line in linux platforms (users must ensure gdb/addr2line is
+    already installed).
+   </para>
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 81bac6f581..eab437988a 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -709,6 +709,8 @@ REVOKE EXECUTE ON FUNCTION pg_ls_logicalmapdir() FROM PUBLIC;
 
 REVOKE EXECUTE ON FUNCTION pg_ls_replslotdir(text) FROM PUBLIC;
 
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer) FROM PUBLIC;
+
 --
 -- We also set up some things as accessible to standard roles.
 --
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 681ef91b81..a572412f2a 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -841,6 +841,10 @@ HandleAutoVacLauncherInterrupts(void)
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
 
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
+
 	/* Process sinval catchup interrupts that happened while sleeping */
 	ProcessCatchupInterrupt();
 }
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index a59c3cf020..9f1d163bec 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -585,6 +585,10 @@ HandleCheckpointerInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 /*
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index 3f412dad2e..84b1de4967 100644
--- a/src/backend/postmaster/interrupt.c
+++ b/src/backend/postmaster/interrupt.c
@@ -48,6 +48,10 @@ HandleMainLoopInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 /*
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index d916ed39a8..af55e39cc1 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -765,9 +765,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, config update and request for logging of
- * memory contexts, but not shutdown request because how to handle
- * shutdown request is different between those loops.
+ * It checks for barrier events, config update, request for logging of
+ * memory contexts and backtraces, but not shutdown request because how to
+ * handle shutdown request is different between those loops.
  */
 static void
 HandlePgArchInterrupts(void)
@@ -779,6 +779,10 @@ HandlePgArchInterrupts(void)
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
 
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
+
 	if (ConfigReloadPending)
 	{
 		char	   *archiveLib = pstrdup(XLogArchiveLibrary);
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index 29cf8f18e1..013d8fe68f 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -206,6 +206,10 @@ HandleStartupProcInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 102fa2a089..52d5a82ecb 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -310,4 +310,8 @@ HandleWalWriterInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 735763cc24..a8cc210975 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -3161,6 +3161,65 @@ HaveVirtualXIDsDelayingChkpt(VirtualTransactionId *vxids, int nvxids, int type)
 	return result;
 }
 
+/*
+ * CheckProcSendDebugSignal -- check if the process with given pid is a backend
+ * or an auxiliary process and send the specified DEBUG signal.
+ *
+ * Returns true if sending the signal was successful, false otherwise
+ */
+bool
+CheckProcSendDebugSignal(int pid, ProcSignalReason signo)
+{
+	PGPROC	   *proc;
+	BackendId	backendId = InvalidBackendId;
+
+	proc = BackendPidGetProc(pid);
+
+	/*
+	 * 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 or a long running process,
+	 * that it might end on its own first and its memory contexts are not
+	 * logged or backtrace not logged is not a problem.
+	 */
+	if (proc == NULL)
+	{
+		/*
+		 * This is just a warning so a loop-through-resultset will not abort
+		 * if one backend terminated on its own during the run.
+		 */
+		ereport(WARNING,
+				(errmsg("PID %d is not a PostgreSQL server process", pid)));
+		return false;
+	}
+
+	if (SendProcSignal(pid, signo, backendId) < 0)
+	{
+		/* Again, just a warning to allow loops */
+		ereport(WARNING,
+				(errmsg("could not send signal to process %d: %m", pid)));
+		return false;
+	}
+
+	return true;
+}
+
 /*
  * BackendPidGetProc -- get a backend's PGPROC given its PID
  *
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index f41563a0a4..5a54a1480d 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -603,6 +603,59 @@ ResetProcSignalBarrierBits(uint32 flags)
 	InterruptPending = true;
 }
 
+/*
+ * HandleLogBacktraceInterrupt - Handle receipt of an interrupt requesting to
+ * log a backtrace.
+ *
+ * All the actual work is deferred to ProcessLogBacktraceInterrupt(),
+ * because we cannot safely emit a log message inside the signal handler.
+ */
+static void
+HandleLogBacktraceInterrupt(void)
+{
+	InterruptPending = true;
+	LogBacktracePending = true;
+	/* latch will be set by procsignal_sigusr1_handler */
+}
+
+/*
+ * ProcessLogBacktraceInterrupt - Perform logging of backtrace of this
+ * backend process.
+ *
+ * Any backend that participates in ProcSignal signaling must arrange
+ * to call this function if we see LogBacktracePending set.
+ * CHECK_FOR_INTERRUPTS() or from process specific interrupt handlers.
+ */
+void
+ProcessLogBacktraceInterrupt(void)
+{
+	StringInfo errtrace = makeStringInfo();
+
+	LogBacktracePending = false;
+
+	/*
+	 * Use LOG_SERVER_ONLY to prevent this message from being sent to the
+	 * connected client.
+	 */
+	ereport(LOG_SERVER_ONLY,
+			errhidestmt(true),
+			errhidecontext(true),
+			errmsg_internal("logging backtrace of PID %d", MyProcPid));
+
+	errtrace->data = set_backtrace(0);
+	if (!errtrace->data)
+		return;
+
+	/*
+	 * LOG_SERVER_ONLY is used to make sure the backtrace is never
+	 * sent to client. We want to avoid messing up the other client
+	 * session.
+	 */
+	elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace->data);
+	pfree(errtrace->data);
+	pfree(errtrace);
+}
+
 /*
  * CheckProcSignal - check to see if a particular reason has been
  * signaled, and clear the signal flag.  Should be called after receiving
@@ -652,6 +705,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_LOG_MEMORY_CONTEXT))
 		HandleLogMemoryContextInterrupt();
 
+	if (CheckProcSignal(PROCSIG_LOG_BACKTRACE))
+		HandleLogBacktraceInterrupt();
+
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
 
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index 6e310b14eb..1ed7ce7218 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -303,3 +303,33 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_log_backtrace
+ *		Signal a backend or an auxiliary process to log its backtrace.
+ *
+ * By default, only superusers are allowed to signal to log the backtrace
+ * because allowing any user to issue this request at an unbounded
+ * rate would cause lots of log messages which can lead to denial of service.
+ * Additional roles can be permitted with GRANT.
+ *
+ * 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 backtrace.
+ */
+Datum
+pg_log_backtrace(PG_FUNCTION_ARGS)
+{
+	int			pid = PG_GETARG_INT32(0);
+	bool		result;
+
+#ifndef HAVE_BACKTRACE_SYMBOLS
+	ereport(WARNING,
+			errmsg("backtrace generation is not supported by this installation"),
+			errhint("You need to rebuild PostgreSQL using a library containing backtrace_symbols."));
+	PG_RETURN_BOOL(false);
+#endif
+
+	result = CheckProcSendDebugSignal(pid, PROCSIG_LOG_BACKTRACE);
+	PG_RETURN_BOOL(result);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index ba2fcfeb4a..2b1d1610a7 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3380,6 +3380,10 @@ ProcessInterrupts(void)
 
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index bb7cc94024..6dffe63943 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -145,51 +145,8 @@ Datum
 pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
 {
 	int			pid = PG_GETARG_INT32(0);
-	PGPROC	   *proc;
-	BackendId	backendId = InvalidBackendId;
+	bool		result;
 
-	proc = BackendPidGetProc(pid);
-
-	/*
-	 * 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)
-	{
-		/*
-		 * This is just a warning so a loop-through-resultset will not abort
-		 * if one backend terminated on its own during the run.
-		 */
-		ereport(WARNING,
-				(errmsg("PID %d is not a PostgreSQL server process", pid)));
-		PG_RETURN_BOOL(false);
-	}
-
-	if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, backendId) < 0)
-	{
-		/* Again, just a warning to allow loops */
-		ereport(WARNING,
-				(errmsg("could not send signal to process %d: %m", pid)));
-		PG_RETURN_BOOL(false);
-	}
-
-	PG_RETURN_BOOL(true);
+	result = CheckProcSendDebugSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT);
+	PG_RETURN_BOOL(result);
 }
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 7402696986..97ec2a5065 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -172,7 +172,6 @@ static char formatted_log_time[FORMATTED_TS_LEN];
 
 
 static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
-static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
 static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
 static void write_console(const char *line, int len);
 static const char *process_log_prefix_padding(const char *p, int *padding);
@@ -546,7 +545,7 @@ errfinish(const char *filename, int lineno, const char *funcname)
 		edata->funcname &&
 		backtrace_functions &&
 		matches_backtrace_functions(edata->funcname))
-		set_backtrace(edata, 2);
+		edata->backtrace = set_backtrace(2);
 
 	/*
 	 * Call any context callback functions.  Errors occurring in callback
@@ -932,7 +931,7 @@ errbacktrace(void)
 	CHECK_STACK_DEPTH();
 	oldcontext = MemoryContextSwitchTo(edata->assoc_context);
 
-	set_backtrace(edata, 1);
+	edata->backtrace = set_backtrace(1);
 
 	MemoryContextSwitchTo(oldcontext);
 	recursion_depth--;
@@ -941,13 +940,13 @@ errbacktrace(void)
 }
 
 /*
- * Compute backtrace data and add it to the supplied ErrorData.  num_skip
- * specifies how many inner frames to skip.  Use this to avoid showing the
- * internal backtrace support functions in the backtrace.  This requires that
- * this and related functions are not inlined.
+ * Compute backtrace data and return it.  num_skip specifies how many inner
+ * frames to skip.  Use this to avoid showing the internal backtrace support
+ * functions in the backtrace.  This requires that this and related functions
+ * are not inlined.
  */
-static void
-set_backtrace(ErrorData *edata, int num_skip)
+char *
+set_backtrace(int num_skip)
 {
 	StringInfoData errtrace;
 
@@ -962,7 +961,7 @@ set_backtrace(ErrorData *edata, int num_skip)
 		nframes = backtrace(buf, lengthof(buf));
 		strfrms = backtrace_symbols(buf, nframes);
 		if (strfrms == NULL)
-			return;
+			return NULL;
 
 		for (int i = num_skip; i < nframes; i++)
 			appendStringInfo(&errtrace, "\n%s", strfrms[i]);
@@ -973,7 +972,7 @@ set_backtrace(ErrorData *edata, int num_skip)
 						   "backtrace generation is not supported by this installation");
 #endif
 
-	edata->backtrace = errtrace.data;
+	return errtrace.data;
 }
 
 /*
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 3419c099b2..2dd2cc3dc6 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -36,6 +36,7 @@ volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t IdleSessionTimeoutPending = false;
 volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile sig_atomic_t LogMemoryContextPending = false;
+volatile sig_atomic_t LogBacktracePending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 25304430f4..f32662b0f8 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11839,4 +11839,9 @@
   prorettype => 'bytea', proargtypes => 'pg_brin_minmax_multi_summary',
   prosrc => 'brin_minmax_multi_summary_send' },
 
+# function to get the backtrace of server process
+{ oid => '6105', descr => 'log backtrace of server process',
+  proname => 'pg_log_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_log_backtrace' },
+
 ]
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 0abc3ad540..2309874d47 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -94,6 +94,7 @@ extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
 extern PGDLLIMPORT volatile sig_atomic_t LogMemoryContextPending;
+extern PGDLLIMPORT volatile sig_atomic_t LogBacktracePending;
 
 extern PGDLLIMPORT volatile sig_atomic_t CheckClientConnectionPending;
 extern PGDLLIMPORT volatile sig_atomic_t ClientConnectionLost;
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index 1b2cfac5ad..a60b6f82f5 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -67,6 +67,7 @@ extern PGPROC *BackendPidGetProc(int pid);
 extern PGPROC *BackendPidGetProcWithLock(int pid);
 extern int	BackendXidGetPid(TransactionId xid);
 extern bool IsBackendPid(int pid);
+extern bool CheckProcSendDebugSignal(int pid, ProcSignalReason signo);
 
 extern VirtualTransactionId *GetCurrentVirtualXIDs(TransactionId limitXmin,
 												   bool excludeXmin0, bool allDbs, int excludeVacuum,
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index ee636900f3..af3954d564 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -35,6 +35,7 @@ typedef enum
 	PROCSIG_WALSND_INIT_STOPPING,	/* ask walsenders to prepare for shutdown  */
 	PROCSIG_BARRIER,			/* global barrier interrupt  */
 	PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
+	PROCSIG_LOG_BACKTRACE,		/* ask backend to log the current backtrace */
 
 	/* Recovery conflict reasons */
 	PROCSIG_RECOVERY_CONFLICT_DATABASE,
@@ -65,7 +66,7 @@ extern int	SendProcSignal(pid_t pid, ProcSignalReason reason,
 extern uint64 EmitProcSignalBarrier(ProcSignalBarrierType type);
 extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
-
+extern void ProcessLogBacktraceInterrupt(void);
 extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
 
 #endif							/* PROCSIGNAL_H */
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 3eb8de3966..9a7d20903c 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -467,4 +467,6 @@ extern void set_syslog_parameters(const char *ident, int facility);
  */
 extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
 
+extern pg_noinline char *set_backtrace(int num_skip);
+
 #endif							/* ELOG_H */
diff --git a/src/test/regress/expected/backtrace.out b/src/test/regress/expected/backtrace.out
new file mode 100644
index 0000000000..2184a99483
--- /dev/null
+++ b/src/test/regress/expected/backtrace.out
@@ -0,0 +1,49 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/expected/backtrace_1.out b/src/test/regress/expected/backtrace_1.out
new file mode 100644
index 0000000000..fe9523f89d
--- /dev/null
+++ b/src/test/regress/expected/backtrace_1.out
@@ -0,0 +1,55 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 5030d19c03..489f543a83 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -86,7 +86,7 @@ test: brin_bloom brin_multi
 # psql depends on create_am
 # amutils depends on geometry, create_index_spgist, hash_index, brin
 # ----------
-test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role
+test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role backtrace
 
 # collate.*.utf8 tests cannot be run in parallel with each other
 test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8
diff --git a/src/test/regress/sql/backtrace.sql b/src/test/regress/sql/backtrace.sql
new file mode 100644
index 0000000000..d74b1016ae
--- /dev/null
+++ b/src/test/regress/sql/backtrace.sql
@@ -0,0 +1,33 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+
+SELECT pg_log_backtrace(pg_backend_pid());
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+
+CREATE ROLE regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+RESET ROLE;
+
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+
+DROP ROLE regress_log_backtrace;
-- 
2.32.0

#104vignesh C
vignesh21@gmail.com
In reply to: vignesh C (#103)
1 attachment(s)
Re: Printing backtrace of postgres processes

On Wed, Apr 6, 2022 at 12:29 PM vignesh C <vignesh21@gmail.com> wrote:

On Tue, Apr 5, 2022 at 9:18 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Wed, Mar 30, 2022 at 12:03 PM Justin Pryzby <pryzby@telsasoft.com> wrote:

On Wed, Mar 30, 2022 at 11:53:52AM -0400, Greg Stark wrote:

Sadly the cfbot is showing a patch conflict again. It's just a trivial
conflict in the regression test schedule so I'm not going to update
the status but it would be good to rebase it so we continue to get
cfbot testing.

Done. No changes.

+ if (chk_auxiliary_proc)
+ ereport(WARNING,
+ errmsg("PID %d is not a PostgreSQL server process", pid));
+ else
+ ereport(WARNING,
+ errmsg("PID %d is not a PostgreSQL backend process", pid));

This doesn't look right to me. I don't think that PostgreSQL server
processes are one kind of thing and PostgreSQL backend processes are
another kind of thing. I think they're two terms that are pretty
nearly interchangeable, or maybe at best you want to argue that
backend processes are some subset of server processes. I don't see
this sort of thing adding any clarity.

I have changed it to "PID %d is not a PostgreSQL server process" which
is similar to the existing warning message in
pg_log_backend_memory_contexts.

-static void
+void
set_backtrace(ErrorData *edata, int num_skip)
{
StringInfoData errtrace;
@@ -978,7 +978,18 @@ set_backtrace(ErrorData *edata, int num_skip)
"backtrace generation is not supported by this installation");
#endif

- edata->backtrace = errtrace.data;
+ if (edata)
+ edata->backtrace = errtrace.data;
+ else
+ {
+ /*
+ * LOG_SERVER_ONLY is used to make sure the backtrace is never
+ * sent to client. We want to avoid messing up the other client
+ * session.
+ */
+ elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace.data);
+ pfree(errtrace.data);
+ }
}

This looks like a grotty hack.

I have changed it so that the backtrace is set and returned to the
caller. The caller will take care of logging or setting it to the
error data context. Thoughts?

- PGPROC    *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 so far all the callers of
- * this mechanism involve some request for ending the process anyway, that
- * it might end on its own first is not a problem.
- *
- * Note that proc will also be NULL if the pid refers to an auxiliary
- * process or the postmaster (neither of which can be signaled via
- * pg_signal_backend()).
- */
- if (proc == NULL)
- {
- /*
- * This is just a warning so a loop-through-resultset will not abort
- * if one backend terminated on its own during the run.
- */
- ereport(WARNING,
- (errmsg("PID %d is not a PostgreSQL backend process", pid)));
+ PGPROC  *proc;
+ /* Users can only signal valid backend or an auxiliary process. */
+ proc = CheckPostgresProcessId(pid, false, NULL);
+ if (!proc)
return SIGNAL_BACKEND_ERROR;
- }

Incidentally changing the behavior of pg_signal_backend() doesn't seem
like a great idea. We can do that as a separate commit, after
considering whether documentation changes are needed. But it's not
something that should get folded into a commit on another topic.

Agreed. I have kept the logic of pg_signal_backend as it is.
pg_log_backtrace and pg_log_backend_memory_contexts use the same
functionality to check and send signal. I have added a new function
"CheckProcSendDebugSignal" which has the common code implementation
and will be called by both pg_log_backtrace and
pg_log_backend_memory_contexts.

+ /*
+ * 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 without logging the requested info is not a problem.
+ */

This comment made a lot more sense where it used to be than it does
where it is now. I think more work is required here than just cutting
and pasting.

This function was called from pg_signal_backend earlier, this logic is
now moved to CheckProcSendDebugSignal which will be called only by
pg_log_backtrace and pg_log_backend_memory_contexts. This looks
appropriate in CheckProcSendDebugSignal with slight change to specify
it can consume lots of memory or a long running process.
Thoughts?

Attached v20 patch has the changes for the same.

The patch was not applying on top of HEAD. Attached patch is a rebased
version on top of HEAD.

Regards,
Vignesh

Attachments:

v20-0001-Add-function-to-log-the-backtrace-of-the-specifi.patchtext/x-patch; charset=US-ASCII; name=v20-0001-Add-function-to-log-the-backtrace-of-the-specifi.patchDownload
From b2617dbb4c18e0279c65c0d32fc181b9809e65e8 Mon Sep 17 00:00:00 2001
From: Vigneshwaran C <vignesh21@gmail.com>
Date: Thu, 14 Apr 2022 10:31:50 +0530
Subject: [PATCH v20] Add function to log the backtrace of the specified 
 postgres  process.

This commit adds pg_log_backtrace() function that requests to log
the backtrace of the specified backend or auxiliary process except
logger and statistic collector.

Only superusers are allowed to request to log the backtrace
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.

On receipt of the request, at the next CHECK_FOR_INTERRUPTS(),
the target backend logs its backtrace at LOG_SERVER_ONLY level,
so that the backtrace will appear in the server log but not
be sent to the client.

Bump catalog version.

Authors: Vignesh C, Bharath Rupireddy, Justin Pryzby
Reviewers: Bharath Rupireddy, Justin Pryzby, Fujii Masao, Atsushi Torikoshi, Dilip Kumar, Robert Haas, Andres Freund, Tom lane, Craig Ringer
Discussion: https://www.postgresql.org/message-id/CALDaNm3ZzmFS-=r7oDUzj7y7BgQv+N06Kqyft6C3xZDoKnk_6w@mail.gmail.com
---
 doc/src/sgml/func.sgml                    | 62 +++++++++++++++++++++++
 src/backend/catalog/system_functions.sql  |  2 +
 src/backend/postmaster/autovacuum.c       |  4 ++
 src/backend/postmaster/checkpointer.c     |  4 ++
 src/backend/postmaster/interrupt.c        |  4 ++
 src/backend/postmaster/pgarch.c           | 10 ++--
 src/backend/postmaster/startup.c          |  4 ++
 src/backend/postmaster/walwriter.c        |  4 ++
 src/backend/storage/ipc/procarray.c       | 59 +++++++++++++++++++++
 src/backend/storage/ipc/procsignal.c      | 56 ++++++++++++++++++++
 src/backend/storage/ipc/signalfuncs.c     | 30 +++++++++++
 src/backend/tcop/postgres.c               |  4 ++
 src/backend/utils/adt/mcxtfuncs.c         | 49 ++----------------
 src/backend/utils/error/elog.c            | 21 ++++----
 src/backend/utils/init/globals.c          |  1 +
 src/include/catalog/pg_proc.dat           |  5 ++
 src/include/miscadmin.h                   |  1 +
 src/include/storage/procarray.h           |  1 +
 src/include/storage/procsignal.h          |  3 +-
 src/include/utils/elog.h                  |  2 +
 src/test/regress/expected/backtrace.out   | 49 ++++++++++++++++++
 src/test/regress/expected/backtrace_1.out | 55 ++++++++++++++++++++
 src/test/regress/parallel_schedule        |  2 +-
 src/test/regress/sql/backtrace.sql        | 33 ++++++++++++
 24 files changed, 403 insertions(+), 62 deletions(-)
 create mode 100644 src/test/regress/expected/backtrace.out
 create mode 100644 src/test/regress/expected/backtrace_1.out
 create mode 100644 src/test/regress/sql/backtrace.sql

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 93ba39eff1..9dbe381628 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25372,6 +25372,30 @@ SELECT has_function_privilege('joeuser', 'myfunc(int, text)', 'execute');
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_log_backtrace</primary>
+        </indexterm>
+        <function>pg_log_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Requests to log the backtrace of the backend with the
+        specified process ID.  This function can send the request to
+        backends and auxiliary processes except the logger and statistics
+        collector.  The backtraces 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 identifies
+        which function a process is currently executing and it may be useful
+        for developers to diagnose stuck processes and other problems. This
+        function is supported only if PostgreSQL was built with the ability to
+        capture backtraces, otherwise it will emit a warning.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -28141,6 +28165,44 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_log_backtrace</function> can be used to log the backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace
+------------------
+ t
+(1 row)
+</programlisting>
+The backtrace will be logged as specified by the logging configuration.
+For example:
+<screen>
+2021-01-27 11:33:50.247 IST [111735] LOG:  current backtrace:
+        postgres: postgresdba postgres [local] SELECT(set_backtrace+0x38) [0xae06c5]
+        postgres: postgresdba postgres [local] SELECT(ProcessInterrupts+0x788) [0x950c34]
+        postgres: postgresdba postgres [local] SELECT() [0x761e89]
+        postgres: postgresdba postgres [local] SELECT() [0x71bbda]
+        postgres: postgresdba postgres [local] SELECT() [0x71e380]
+        postgres: postgresdba postgres [local] SELECT(standard_ExecutorRun+0x1d6) [0x71c1fe]
+        postgres: postgresdba postgres [local] SELECT(ExecutorRun+0x55) [0x71c026]
+        postgres: postgresdba postgres [local] SELECT() [0x953fc5]
+        postgres: postgresdba postgres [local] SELECT(PortalRun+0x262) [0x953c7e]
+        postgres: postgresdba postgres [local] SELECT() [0x94db78]
+        postgres: postgresdba postgres [local] SELECT(PostgresMain+0x7d7) [0x951e72]
+        postgres: postgresdba postgres [local] SELECT() [0x896b2f]
+        postgres: postgresdba postgres [local] SELECT() [0x8964b5]
+        postgres: postgresdba postgres [local] SELECT() [0x892a79]
+        postgres: postgresdba postgres [local] SELECT(PostmasterMain+0x116b) [0x892350]
+        postgres: postgresdba postgres [local] SELECT() [0x795f72]
+        /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2107bbd505]
+        postgres: postgresdba postgres [local] SELECT() [0x4842a9]
+</screen>
+    You can obtain the file name and line number from the logged details by using
+    gdb/addr2line in linux platforms (users must ensure gdb/addr2line is
+    already installed).
+   </para>
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 73da687d5d..9fbc990b9b 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -709,6 +709,8 @@ REVOKE EXECUTE ON FUNCTION pg_ls_logicalmapdir() FROM PUBLIC;
 
 REVOKE EXECUTE ON FUNCTION pg_ls_replslotdir(text) FROM PUBLIC;
 
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer) FROM PUBLIC;
+
 --
 -- We also set up some things as accessible to standard roles.
 --
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index f36c40e852..f82eba5245 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -835,6 +835,10 @@ HandleAutoVacLauncherInterrupts(void)
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
 
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
+
 	/* Process sinval catchup interrupts that happened while sleeping */
 	ProcessCatchupInterrupt();
 }
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index c937c39f50..d0d85347b4 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -591,6 +591,10 @@ HandleCheckpointerInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 /*
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index 1aed2e2e99..f934d3b485 100644
--- a/src/backend/postmaster/interrupt.c
+++ b/src/backend/postmaster/interrupt.c
@@ -48,6 +48,10 @@ HandleMainLoopInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 /*
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 0c8ca29f73..e487c18e3f 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -765,9 +765,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, config update and request for logging of
- * memory contexts, but not shutdown request because how to handle
- * shutdown request is different between those loops.
+ * It checks for barrier events, config update, request for logging of
+ * memory contexts and backtraces, but not shutdown request because how to
+ * handle shutdown request is different between those loops.
  */
 static void
 HandlePgArchInterrupts(void)
@@ -779,6 +779,10 @@ HandlePgArchInterrupts(void)
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
 
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
+
 	if (ConfigReloadPending)
 	{
 		char	   *archiveLib = pstrdup(XLogArchiveLibrary);
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index 29cf8f18e1..013d8fe68f 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -206,6 +206,10 @@ HandleStartupProcInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 77aebb244c..fb5a830313 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -309,4 +309,8 @@ HandleWalWriterInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index f6e98aae29..8167cb7cb6 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -3151,6 +3151,65 @@ HaveVirtualXIDsDelayingChkpt(VirtualTransactionId *vxids, int nvxids, int type)
 	return result;
 }
 
+/*
+ * CheckProcSendDebugSignal -- check if the process with given pid is a backend
+ * or an auxiliary process and send the specified DEBUG signal.
+ *
+ * Returns true if sending the signal was successful, false otherwise
+ */
+bool
+CheckProcSendDebugSignal(int pid, ProcSignalReason signo)
+{
+	PGPROC	   *proc;
+	BackendId	backendId = InvalidBackendId;
+
+	proc = BackendPidGetProc(pid);
+
+	/*
+	 * 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 or a long running process,
+	 * that it might end on its own first and its memory contexts are not
+	 * logged or backtrace not logged is not a problem.
+	 */
+	if (proc == NULL)
+	{
+		/*
+		 * This is just a warning so a loop-through-resultset will not abort
+		 * if one backend terminated on its own during the run.
+		 */
+		ereport(WARNING,
+				(errmsg("PID %d is not a PostgreSQL server process", pid)));
+		return false;
+	}
+
+	if (SendProcSignal(pid, signo, backendId) < 0)
+	{
+		/* Again, just a warning to allow loops */
+		ereport(WARNING,
+				(errmsg("could not send signal to process %d: %m", pid)));
+		return false;
+	}
+
+	return true;
+}
+
 /*
  * BackendPidGetProc -- get a backend's PGPROC given its PID
  *
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index 00d66902d8..ce3f12465e 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -594,6 +594,59 @@ ResetProcSignalBarrierBits(uint32 flags)
 	InterruptPending = true;
 }
 
+/*
+ * HandleLogBacktraceInterrupt - Handle receipt of an interrupt requesting to
+ * log a backtrace.
+ *
+ * All the actual work is deferred to ProcessLogBacktraceInterrupt(),
+ * because we cannot safely emit a log message inside the signal handler.
+ */
+static void
+HandleLogBacktraceInterrupt(void)
+{
+	InterruptPending = true;
+	LogBacktracePending = true;
+	/* latch will be set by procsignal_sigusr1_handler */
+}
+
+/*
+ * ProcessLogBacktraceInterrupt - Perform logging of backtrace of this
+ * backend process.
+ *
+ * Any backend that participates in ProcSignal signaling must arrange
+ * to call this function if we see LogBacktracePending set.
+ * CHECK_FOR_INTERRUPTS() or from process specific interrupt handlers.
+ */
+void
+ProcessLogBacktraceInterrupt(void)
+{
+	StringInfo errtrace = makeStringInfo();
+
+	LogBacktracePending = false;
+
+	/*
+	 * Use LOG_SERVER_ONLY to prevent this message from being sent to the
+	 * connected client.
+	 */
+	ereport(LOG_SERVER_ONLY,
+			errhidestmt(true),
+			errhidecontext(true),
+			errmsg_internal("logging backtrace of PID %d", MyProcPid));
+
+	errtrace->data = set_backtrace(0);
+	if (!errtrace->data)
+		return;
+
+	/*
+	 * LOG_SERVER_ONLY is used to make sure the backtrace is never
+	 * sent to client. We want to avoid messing up the other client
+	 * session.
+	 */
+	elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace->data);
+	pfree(errtrace->data);
+	pfree(errtrace);
+}
+
 /*
  * CheckProcSignal - check to see if a particular reason has been
  * signaled, and clear the signal flag.  Should be called after receiving
@@ -643,6 +696,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_LOG_MEMORY_CONTEXT))
 		HandleLogMemoryContextInterrupt();
 
+	if (CheckProcSignal(PROCSIG_LOG_BACKTRACE))
+		HandleLogBacktraceInterrupt();
+
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
 
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index 6e310b14eb..1ed7ce7218 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -303,3 +303,33 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_log_backtrace
+ *		Signal a backend or an auxiliary process to log its backtrace.
+ *
+ * By default, only superusers are allowed to signal to log the backtrace
+ * because allowing any user to issue this request at an unbounded
+ * rate would cause lots of log messages which can lead to denial of service.
+ * Additional roles can be permitted with GRANT.
+ *
+ * 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 backtrace.
+ */
+Datum
+pg_log_backtrace(PG_FUNCTION_ARGS)
+{
+	int			pid = PG_GETARG_INT32(0);
+	bool		result;
+
+#ifndef HAVE_BACKTRACE_SYMBOLS
+	ereport(WARNING,
+			errmsg("backtrace generation is not supported by this installation"),
+			errhint("You need to rebuild PostgreSQL using a library containing backtrace_symbols."));
+	PG_RETURN_BOOL(false);
+#endif
+
+	result = CheckProcSendDebugSignal(pid, PROCSIG_LOG_BACKTRACE);
+	PG_RETURN_BOOL(result);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 304cce135a..727c23d5a1 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3387,6 +3387,10 @@ ProcessInterrupts(void)
 
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index bb7cc94024..6dffe63943 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -145,51 +145,8 @@ Datum
 pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
 {
 	int			pid = PG_GETARG_INT32(0);
-	PGPROC	   *proc;
-	BackendId	backendId = InvalidBackendId;
+	bool		result;
 
-	proc = BackendPidGetProc(pid);
-
-	/*
-	 * 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)
-	{
-		/*
-		 * This is just a warning so a loop-through-resultset will not abort
-		 * if one backend terminated on its own during the run.
-		 */
-		ereport(WARNING,
-				(errmsg("PID %d is not a PostgreSQL server process", pid)));
-		PG_RETURN_BOOL(false);
-	}
-
-	if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, backendId) < 0)
-	{
-		/* Again, just a warning to allow loops */
-		ereport(WARNING,
-				(errmsg("could not send signal to process %d: %m", pid)));
-		PG_RETURN_BOOL(false);
-	}
-
-	PG_RETURN_BOOL(true);
+	result = CheckProcSendDebugSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT);
+	PG_RETURN_BOOL(result);
 }
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 55ee5423af..699fc3ebc2 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -172,7 +172,6 @@ static char formatted_log_time[FORMATTED_TS_LEN];
 
 
 static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
-static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
 static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
 static void write_console(const char *line, int len);
 static const char *process_log_prefix_padding(const char *p, int *padding);
@@ -546,7 +545,7 @@ errfinish(const char *filename, int lineno, const char *funcname)
 		edata->funcname &&
 		backtrace_functions &&
 		matches_backtrace_functions(edata->funcname))
-		set_backtrace(edata, 2);
+		edata->backtrace = set_backtrace(2);
 
 	/*
 	 * Call any context callback functions.  Errors occurring in callback
@@ -932,7 +931,7 @@ errbacktrace(void)
 	CHECK_STACK_DEPTH();
 	oldcontext = MemoryContextSwitchTo(edata->assoc_context);
 
-	set_backtrace(edata, 1);
+	edata->backtrace = set_backtrace(1);
 
 	MemoryContextSwitchTo(oldcontext);
 	recursion_depth--;
@@ -941,13 +940,13 @@ errbacktrace(void)
 }
 
 /*
- * Compute backtrace data and add it to the supplied ErrorData.  num_skip
- * specifies how many inner frames to skip.  Use this to avoid showing the
- * internal backtrace support functions in the backtrace.  This requires that
- * this and related functions are not inlined.
+ * Compute backtrace data and return it.  num_skip specifies how many inner
+ * frames to skip.  Use this to avoid showing the internal backtrace support
+ * functions in the backtrace.  This requires that this and related functions
+ * are not inlined.
  */
-static void
-set_backtrace(ErrorData *edata, int num_skip)
+char *
+set_backtrace(int num_skip)
 {
 	StringInfoData errtrace;
 
@@ -962,7 +961,7 @@ set_backtrace(ErrorData *edata, int num_skip)
 		nframes = backtrace(buf, lengthof(buf));
 		strfrms = backtrace_symbols(buf, nframes);
 		if (strfrms == NULL)
-			return;
+			return NULL;
 
 		for (int i = num_skip; i < nframes; i++)
 			appendStringInfo(&errtrace, "\n%s", strfrms[i]);
@@ -973,7 +972,7 @@ set_backtrace(ErrorData *edata, int num_skip)
 						   "backtrace generation is not supported by this installation");
 #endif
 
-	edata->backtrace = errtrace.data;
+	return errtrace.data;
 }
 
 /*
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 1a5d29ac9b..606f0cb1ba 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -36,6 +36,7 @@ volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t IdleSessionTimeoutPending = false;
 volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile sig_atomic_t LogMemoryContextPending = false;
+volatile sig_atomic_t LogBacktracePending = false;
 volatile sig_atomic_t IdleStatsUpdateTimeoutPending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 6d378ff785..7612be1bfd 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11882,4 +11882,9 @@
   prorettype => 'bytea', proargtypes => 'pg_brin_minmax_multi_summary',
   prosrc => 'brin_minmax_multi_summary_send' },
 
+# function to get the backtrace of server process
+{ oid => '6105', descr => 'log backtrace of server process',
+  proname => 'pg_log_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_log_backtrace' },
+
 ]
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 53fd168d93..67076aac83 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -94,6 +94,7 @@ extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
 extern PGDLLIMPORT volatile sig_atomic_t LogMemoryContextPending;
+extern PGDLLIMPORT volatile sig_atomic_t LogBacktracePending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleStatsUpdateTimeoutPending;
 
 extern PGDLLIMPORT volatile sig_atomic_t CheckClientConnectionPending;
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index 1b2cfac5ad..a60b6f82f5 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -67,6 +67,7 @@ extern PGPROC *BackendPidGetProc(int pid);
 extern PGPROC *BackendPidGetProcWithLock(int pid);
 extern int	BackendXidGetPid(TransactionId xid);
 extern bool IsBackendPid(int pid);
+extern bool CheckProcSendDebugSignal(int pid, ProcSignalReason signo);
 
 extern VirtualTransactionId *GetCurrentVirtualXIDs(TransactionId limitXmin,
 												   bool excludeXmin0, bool allDbs, int excludeVacuum,
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index ee636900f3..af3954d564 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -35,6 +35,7 @@ typedef enum
 	PROCSIG_WALSND_INIT_STOPPING,	/* ask walsenders to prepare for shutdown  */
 	PROCSIG_BARRIER,			/* global barrier interrupt  */
 	PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
+	PROCSIG_LOG_BACKTRACE,		/* ask backend to log the current backtrace */
 
 	/* Recovery conflict reasons */
 	PROCSIG_RECOVERY_CONFLICT_DATABASE,
@@ -65,7 +66,7 @@ extern int	SendProcSignal(pid_t pid, ProcSignalReason reason,
 extern uint64 EmitProcSignalBarrier(ProcSignalBarrierType type);
 extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
-
+extern void ProcessLogBacktraceInterrupt(void);
 extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
 
 #endif							/* PROCSIGNAL_H */
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index f5c6cd904d..9bcdcb2947 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -467,4 +467,6 @@ extern void set_syslog_parameters(const char *ident, int facility);
  */
 extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
 
+extern pg_noinline char *set_backtrace(int num_skip);
+
 #endif							/* ELOG_H */
diff --git a/src/test/regress/expected/backtrace.out b/src/test/regress/expected/backtrace.out
new file mode 100644
index 0000000000..2184a99483
--- /dev/null
+++ b/src/test/regress/expected/backtrace.out
@@ -0,0 +1,49 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/expected/backtrace_1.out b/src/test/regress/expected/backtrace_1.out
new file mode 100644
index 0000000000..fe9523f89d
--- /dev/null
+++ b/src/test/regress/expected/backtrace_1.out
@@ -0,0 +1,55 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 103e11483d..b4df53cb42 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -86,7 +86,7 @@ test: brin_bloom brin_multi
 # psql depends on create_am
 # amutils depends on geometry, create_index_spgist, hash_index, brin
 # ----------
-test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role
+test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role backtrace
 
 # collate.*.utf8 tests cannot be run in parallel with each other
 test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8
diff --git a/src/test/regress/sql/backtrace.sql b/src/test/regress/sql/backtrace.sql
new file mode 100644
index 0000000000..d74b1016ae
--- /dev/null
+++ b/src/test/regress/sql/backtrace.sql
@@ -0,0 +1,33 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+
+SELECT pg_log_backtrace(pg_backend_pid());
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+
+CREATE ROLE regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+RESET ROLE;
+
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+
+DROP ROLE regress_log_backtrace;
-- 
2.32.0

#105Kyotaro Horiguchi
horikyota.ntt@gmail.com
In reply to: vignesh C (#104)
Re: Printing backtrace of postgres processes

At Thu, 14 Apr 2022 10:33:50 +0530, vignesh C <vignesh21@gmail.com> wrote in

On Wed, Apr 6, 2022 at 12:29 PM vignesh C <vignesh21@gmail.com> wrote:

On Tue, Apr 5, 2022 at 9:18 PM Robert Haas <robertmhaas@gmail.com> wrote:

This looks like a grotty hack.

I have changed it so that the backtrace is set and returned to the
caller. The caller will take care of logging or setting it to the
error data context. Thoughts?

The point here I think is that the StringInfo is useless here in the
first place. Addition to that, it is outside the use of StringInfo
hammering in a string pointer into it.

By the way, as Andres suggested very early in this thread, backrace()
can be called from signal handlers with some preparation. So we can
run backtrace() in HandleLogBacktraceInterrupt() and backtrace_symbols
in ProceeLogBacktraceInterrupt(). This make this a bit complex, but
the outcome should be more informative.

Incidentally changing the behavior of pg_signal_backend() doesn't seem
like a great idea. We can do that as a separate commit, after
considering whether documentation changes are needed. But it's not
something that should get folded into a commit on another topic.

Agreed. I have kept the logic of pg_signal_backend as it is.
pg_log_backtrace and pg_log_backend_memory_contexts use the same
functionality to check and send signal. I have added a new function
"CheckProcSendDebugSignal" which has the common code implementation
and will be called by both pg_log_backtrace and
pg_log_backend_memory_contexts.

The name looks like too specific than what it actually does, and
rather doesn't manifest what it does.
SendProcSignalBackendOrAuxproc() might be more eescriptive.

Or we can provide BackendOrAuxiliaryPidGetProc(int pid, BackendId &backendid).

+ /*
+ * 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 without logging the requested info is not a problem.
+ */

This comment made a lot more sense where it used to be than it does
where it is now. I think more work is required here than just cutting
and pasting.

This function was called from pg_signal_backend earlier, this logic is
now moved to CheckProcSendDebugSignal which will be called only by
pg_log_backtrace and pg_log_backend_memory_contexts. This looks
appropriate in CheckProcSendDebugSignal with slight change to specify
it can consume lots of memory or a long running process.
Thoughts?

For example. do you see the following part correct as well for
pg_log_backtrace()?

+ * 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

regards.

--
Kyotaro Horiguchi
NTT Open Source Software Center

#106vignesh C
vignesh21@gmail.com
In reply to: Kyotaro Horiguchi (#105)
1 attachment(s)
Re: Printing backtrace of postgres processes

On Fri, Apr 15, 2022 at 11:49 AM Kyotaro Horiguchi
<horikyota.ntt@gmail.com> wrote:

At Thu, 14 Apr 2022 10:33:50 +0530, vignesh C <vignesh21@gmail.com> wrote in

On Wed, Apr 6, 2022 at 12:29 PM vignesh C <vignesh21@gmail.com> wrote:

On Tue, Apr 5, 2022 at 9:18 PM Robert Haas <robertmhaas@gmail.com> wrote:

This looks like a grotty hack.

I have changed it so that the backtrace is set and returned to the
caller. The caller will take care of logging or setting it to the
error data context. Thoughts?

The point here I think is that the StringInfo is useless here in the
first place. Addition to that, it is outside the use of StringInfo
hammering in a string pointer into it.

Modified

By the way, as Andres suggested very early in this thread, backrace()
can be called from signal handlers with some preparation. So we can
run backtrace() in HandleLogBacktraceInterrupt() and backtrace_symbols
in ProceeLogBacktraceInterrupt(). This make this a bit complex, but
the outcome should be more informative.

Using that approach we could only send the trace to stderr, when we
are trying to log it to the logs it might result in deadlock as the
signal handler can be called in between malloc lock. We dropped that
approach to avoid issues pointed in [1]/messages/by-id/1361750.1606795285@sss.pgh.pa.us.

Incidentally changing the behavior of pg_signal_backend() doesn't seem
like a great idea. We can do that as a separate commit, after
considering whether documentation changes are needed. But it's not
something that should get folded into a commit on another topic.

Agreed. I have kept the logic of pg_signal_backend as it is.
pg_log_backtrace and pg_log_backend_memory_contexts use the same
functionality to check and send signal. I have added a new function
"CheckProcSendDebugSignal" which has the common code implementation
and will be called by both pg_log_backtrace and
pg_log_backend_memory_contexts.

The name looks like too specific than what it actually does, and
rather doesn't manifest what it does.
SendProcSignalBackendOrAuxproc() might be more eescriptive.

Or we can provide BackendOrAuxiliaryPidGetProc(int pid, BackendId &backendid).

Modified it to SendProcSignalBackendOrAuxproc

+ /*
+ * 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 without logging the requested info is not a problem.
+ */

This comment made a lot more sense where it used to be than it does
where it is now. I think more work is required here than just cutting
and pasting.

This function was called from pg_signal_backend earlier, this logic is
now moved to CheckProcSendDebugSignal which will be called only by
pg_log_backtrace and pg_log_backend_memory_contexts. This looks
appropriate in CheckProcSendDebugSignal with slight change to specify
it can consume lots of memory or a long running process.
Thoughts?

For example. do you see the following part correct as well for
pg_log_backtrace()?

">

+ * 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

I felt it was ok since I mentioned it as "consuming lots of memory or
a long running process", let me know if you want to change it to
something else, I will change it.

[1]: /messages/by-id/1361750.1606795285@sss.pgh.pa.us

The attached v21 patch has the changes for the same.

Regards,
Vignesh

Attachments:

v21-0001-Add-function-to-log-the-backtrace-of-the-specifi.patchtext/x-patch; charset=US-ASCII; name=v21-0001-Add-function-to-log-the-backtrace-of-the-specifi.patchDownload
From f7c29250f137844c36e5164ff457028a893fadd7 Mon Sep 17 00:00:00 2001
From: Vigneshwaran C <vignesh21@gmail.com>
Date: Thu, 14 Apr 2022 10:31:50 +0530
Subject: [PATCH v21] Add function to log the backtrace of the specified
 postgres process.

This commit adds pg_log_backtrace() function that requests to log
the backtrace of the specified backend or auxiliary process except
logger and statistic collector.

Only superusers are allowed to request to log the backtrace
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.

On receipt of the request, at the next CHECK_FOR_INTERRUPTS(),
the target backend logs its backtrace at LOG_SERVER_ONLY level,
so that the backtrace will appear in the server log but not
be sent to the client.

Bump catalog version.

Authors: Vignesh C, Bharath Rupireddy, Justin Pryzby
Reviewers: Bharath Rupireddy, Justin Pryzby, Fujii Masao, Atsushi Torikoshi, Dilip Kumar, Robert Haas, Andres Freund, Tom lane, Craig Ringer
Discussion: https://www.postgresql.org/message-id/CALDaNm3ZzmFS-=r7oDUzj7y7BgQv+N06Kqyft6C3xZDoKnk_6w@mail.gmail.com
---
 doc/src/sgml/func.sgml                    | 62 +++++++++++++++++++++++
 src/backend/catalog/system_functions.sql  |  2 +
 src/backend/postmaster/autovacuum.c       |  4 ++
 src/backend/postmaster/checkpointer.c     |  4 ++
 src/backend/postmaster/interrupt.c        |  4 ++
 src/backend/postmaster/pgarch.c           | 10 ++--
 src/backend/postmaster/startup.c          |  4 ++
 src/backend/postmaster/walwriter.c        |  4 ++
 src/backend/storage/ipc/procarray.c       | 59 +++++++++++++++++++++
 src/backend/storage/ipc/procsignal.c      | 49 ++++++++++++++++++
 src/backend/storage/ipc/signalfuncs.c     | 30 +++++++++++
 src/backend/tcop/postgres.c               |  4 ++
 src/backend/utils/adt/mcxtfuncs.c         | 49 ++----------------
 src/backend/utils/error/elog.c            | 21 ++++----
 src/backend/utils/init/globals.c          |  1 +
 src/include/catalog/pg_proc.dat           |  5 ++
 src/include/miscadmin.h                   |  1 +
 src/include/storage/procarray.h           |  1 +
 src/include/storage/procsignal.h          |  3 +-
 src/include/utils/elog.h                  |  2 +
 src/test/regress/expected/backtrace.out   | 49 ++++++++++++++++++
 src/test/regress/expected/backtrace_1.out | 55 ++++++++++++++++++++
 src/test/regress/parallel_schedule        |  2 +-
 src/test/regress/sql/backtrace.sql        | 33 ++++++++++++
 24 files changed, 396 insertions(+), 62 deletions(-)
 create mode 100644 src/test/regress/expected/backtrace.out
 create mode 100644 src/test/regress/expected/backtrace_1.out
 create mode 100644 src/test/regress/sql/backtrace.sql

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 489184a4f0..b94cd1c610 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25372,6 +25372,30 @@ SELECT has_function_privilege('joeuser', 'myfunc(int, text)', 'execute');
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_log_backtrace</primary>
+        </indexterm>
+        <function>pg_log_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Requests to log the backtrace of the backend with the
+        specified process ID.  This function can send the request to
+        backends and auxiliary processes except the logger and statistics
+        collector.  The backtraces 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 identifies
+        which function a process is currently executing and it may be useful
+        for developers to diagnose stuck processes and other problems. This
+        function is supported only if PostgreSQL was built with the ability to
+        capture backtraces, otherwise it will emit a warning.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -28141,6 +28165,44 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_log_backtrace</function> can be used to log the backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace
+------------------
+ t
+(1 row)
+</programlisting>
+The backtrace will be logged as specified by the logging configuration.
+For example:
+<screen>
+2021-01-27 11:33:50.247 IST [111735] LOG:  current backtrace:
+        postgres: postgresdba postgres [local] SELECT(set_backtrace+0x38) [0xae06c5]
+        postgres: postgresdba postgres [local] SELECT(ProcessInterrupts+0x788) [0x950c34]
+        postgres: postgresdba postgres [local] SELECT() [0x761e89]
+        postgres: postgresdba postgres [local] SELECT() [0x71bbda]
+        postgres: postgresdba postgres [local] SELECT() [0x71e380]
+        postgres: postgresdba postgres [local] SELECT(standard_ExecutorRun+0x1d6) [0x71c1fe]
+        postgres: postgresdba postgres [local] SELECT(ExecutorRun+0x55) [0x71c026]
+        postgres: postgresdba postgres [local] SELECT() [0x953fc5]
+        postgres: postgresdba postgres [local] SELECT(PortalRun+0x262) [0x953c7e]
+        postgres: postgresdba postgres [local] SELECT() [0x94db78]
+        postgres: postgresdba postgres [local] SELECT(PostgresMain+0x7d7) [0x951e72]
+        postgres: postgresdba postgres [local] SELECT() [0x896b2f]
+        postgres: postgresdba postgres [local] SELECT() [0x8964b5]
+        postgres: postgresdba postgres [local] SELECT() [0x892a79]
+        postgres: postgresdba postgres [local] SELECT(PostmasterMain+0x116b) [0x892350]
+        postgres: postgresdba postgres [local] SELECT() [0x795f72]
+        /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2107bbd505]
+        postgres: postgresdba postgres [local] SELECT() [0x4842a9]
+</screen>
+    You can obtain the file name and line number from the logged details by using
+    gdb/addr2line in linux platforms (users must ensure gdb/addr2line is
+    already installed).
+   </para>
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 73da687d5d..9fbc990b9b 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -709,6 +709,8 @@ REVOKE EXECUTE ON FUNCTION pg_ls_logicalmapdir() FROM PUBLIC;
 
 REVOKE EXECUTE ON FUNCTION pg_ls_replslotdir(text) FROM PUBLIC;
 
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer) FROM PUBLIC;
+
 --
 -- We also set up some things as accessible to standard roles.
 --
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index f36c40e852..f82eba5245 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -835,6 +835,10 @@ HandleAutoVacLauncherInterrupts(void)
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
 
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
+
 	/* Process sinval catchup interrupts that happened while sleeping */
 	ProcessCatchupInterrupt();
 }
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index c937c39f50..d0d85347b4 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -591,6 +591,10 @@ HandleCheckpointerInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 /*
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index 1aed2e2e99..f934d3b485 100644
--- a/src/backend/postmaster/interrupt.c
+++ b/src/backend/postmaster/interrupt.c
@@ -48,6 +48,10 @@ HandleMainLoopInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 /*
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 0c8ca29f73..e487c18e3f 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -765,9 +765,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, config update and request for logging of
- * memory contexts, but not shutdown request because how to handle
- * shutdown request is different between those loops.
+ * It checks for barrier events, config update, request for logging of
+ * memory contexts and backtraces, but not shutdown request because how to
+ * handle shutdown request is different between those loops.
  */
 static void
 HandlePgArchInterrupts(void)
@@ -779,6 +779,10 @@ HandlePgArchInterrupts(void)
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
 
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
+
 	if (ConfigReloadPending)
 	{
 		char	   *archiveLib = pstrdup(XLogArchiveLibrary);
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index 29cf8f18e1..013d8fe68f 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -206,6 +206,10 @@ HandleStartupProcInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 77aebb244c..fb5a830313 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -309,4 +309,8 @@ HandleWalWriterInterrupts(void)
 	/* Perform logging of memory contexts of this process */
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 25c310f675..e7bc065f6d 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -3162,6 +3162,65 @@ HaveVirtualXIDsDelayingChkpt(VirtualTransactionId *vxids, int nvxids, int type)
 	return result;
 }
 
+/*
+ * SendProcSignalBackendOrAuxproc -- check if the process with given pid is a
+ * backend or an auxiliary process and send the specified DEBUG signal.
+ *
+ * Returns true if sending the signal was successful, false otherwise
+ */
+bool
+SendProcSignalBackendOrAuxproc(int pid, ProcSignalReason signo)
+{
+	PGPROC	   *proc;
+	BackendId	backendId = InvalidBackendId;
+
+	proc = BackendPidGetProc(pid);
+
+	/*
+	 * 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 or a long running process,
+	 * that it might end on its own first and its memory contexts are not
+	 * logged or backtrace not logged is not a problem.
+	 */
+	if (proc == NULL)
+	{
+		/*
+		 * This is just a warning so a loop-through-resultset will not abort
+		 * if one backend terminated on its own during the run.
+		 */
+		ereport(WARNING,
+				(errmsg("PID %d is not a PostgreSQL server process", pid)));
+		return false;
+	}
+
+	if (SendProcSignal(pid, signo, backendId) < 0)
+	{
+		/* Again, just a warning to allow loops */
+		ereport(WARNING,
+				(errmsg("could not send signal to process %d: %m", pid)));
+		return false;
+	}
+
+	return true;
+}
+
 /*
  * BackendPidGetProc -- get a backend's PGPROC given its PID
  *
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index 00d66902d8..7be457be16 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -594,6 +594,52 @@ ResetProcSignalBarrierBits(uint32 flags)
 	InterruptPending = true;
 }
 
+/*
+ * HandleLogBacktraceInterrupt - Handle receipt of an interrupt requesting to
+ * log a backtrace.
+ *
+ * All the actual work is deferred to ProcessLogBacktraceInterrupt(),
+ * because we cannot safely emit a log message inside the signal handler.
+ */
+static void
+HandleLogBacktraceInterrupt(void)
+{
+	InterruptPending = true;
+	LogBacktracePending = true;
+	/* latch will be set by procsignal_sigusr1_handler */
+}
+
+/*
+ * ProcessLogBacktraceInterrupt - Perform logging of backtrace of this
+ * backend process.
+ *
+ * Any backend that participates in ProcSignal signaling must arrange
+ * to call this function if we see LogBacktracePending set.
+ * CHECK_FOR_INTERRUPTS() or from process specific interrupt handlers.
+ */
+void
+ProcessLogBacktraceInterrupt(void)
+{
+	char	   *errtrace;
+
+	LogBacktracePending = false;
+
+	errtrace = set_backtrace(0);
+	if (!errtrace)
+		return;
+
+	/*
+	 * LOG_SERVER_ONLY is used to make sure the backtrace is never sent to
+	 * client. We want to avoid messing up the other client session.
+	 */
+	ereport(LOG_SERVER_ONLY,
+			errhidestmt(true),
+			errhidecontext(true),
+			errmsg_internal("logging backtrace of PID %d", MyProcPid));
+	elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace);
+	pfree(errtrace);
+}
+
 /*
  * CheckProcSignal - check to see if a particular reason has been
  * signaled, and clear the signal flag.  Should be called after receiving
@@ -643,6 +689,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_LOG_MEMORY_CONTEXT))
 		HandleLogMemoryContextInterrupt();
 
+	if (CheckProcSignal(PROCSIG_LOG_BACKTRACE))
+		HandleLogBacktraceInterrupt();
+
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
 
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index 6e310b14eb..557dabdfdf 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -303,3 +303,33 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_log_backtrace
+ *		Signal a backend or an auxiliary process to log its backtrace.
+ *
+ * By default, only superusers are allowed to signal to log the backtrace
+ * because allowing any user to issue this request at an unbounded
+ * rate would cause lots of log messages which can lead to denial of service.
+ * Additional roles can be permitted with GRANT.
+ *
+ * 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 backtrace.
+ */
+Datum
+pg_log_backtrace(PG_FUNCTION_ARGS)
+{
+	int			pid = PG_GETARG_INT32(0);
+	bool		result;
+
+#ifndef HAVE_BACKTRACE_SYMBOLS
+	ereport(WARNING,
+			errmsg("backtrace generation is not supported by this installation"),
+			errhint("You need to rebuild PostgreSQL using a library containing backtrace_symbols."));
+	PG_RETURN_BOOL(false);
+#endif
+
+	result = SendProcSignalBackendOrAuxproc(pid, PROCSIG_LOG_BACKTRACE);
+	PG_RETURN_BOOL(result);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 304cce135a..727c23d5a1 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3387,6 +3387,10 @@ ProcessInterrupts(void)
 
 	if (LogMemoryContextPending)
 		ProcessLogMemoryContextInterrupt();
+
+	/* Perform logging of backtrace of this process */
+	if (LogBacktracePending)
+		ProcessLogBacktraceInterrupt();
 }
 
 
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index bb7cc94024..49e2bb933d 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -145,51 +145,8 @@ Datum
 pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
 {
 	int			pid = PG_GETARG_INT32(0);
-	PGPROC	   *proc;
-	BackendId	backendId = InvalidBackendId;
+	bool		result;
 
-	proc = BackendPidGetProc(pid);
-
-	/*
-	 * 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)
-	{
-		/*
-		 * This is just a warning so a loop-through-resultset will not abort
-		 * if one backend terminated on its own during the run.
-		 */
-		ereport(WARNING,
-				(errmsg("PID %d is not a PostgreSQL server process", pid)));
-		PG_RETURN_BOOL(false);
-	}
-
-	if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, backendId) < 0)
-	{
-		/* Again, just a warning to allow loops */
-		ereport(WARNING,
-				(errmsg("could not send signal to process %d: %m", pid)));
-		PG_RETURN_BOOL(false);
-	}
-
-	PG_RETURN_BOOL(true);
+	result = SendProcSignalBackendOrAuxproc(pid, PROCSIG_LOG_MEMORY_CONTEXT);
+	PG_RETURN_BOOL(result);
 }
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 55ee5423af..699fc3ebc2 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -172,7 +172,6 @@ static char formatted_log_time[FORMATTED_TS_LEN];
 
 
 static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
-static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
 static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
 static void write_console(const char *line, int len);
 static const char *process_log_prefix_padding(const char *p, int *padding);
@@ -546,7 +545,7 @@ errfinish(const char *filename, int lineno, const char *funcname)
 		edata->funcname &&
 		backtrace_functions &&
 		matches_backtrace_functions(edata->funcname))
-		set_backtrace(edata, 2);
+		edata->backtrace = set_backtrace(2);
 
 	/*
 	 * Call any context callback functions.  Errors occurring in callback
@@ -932,7 +931,7 @@ errbacktrace(void)
 	CHECK_STACK_DEPTH();
 	oldcontext = MemoryContextSwitchTo(edata->assoc_context);
 
-	set_backtrace(edata, 1);
+	edata->backtrace = set_backtrace(1);
 
 	MemoryContextSwitchTo(oldcontext);
 	recursion_depth--;
@@ -941,13 +940,13 @@ errbacktrace(void)
 }
 
 /*
- * Compute backtrace data and add it to the supplied ErrorData.  num_skip
- * specifies how many inner frames to skip.  Use this to avoid showing the
- * internal backtrace support functions in the backtrace.  This requires that
- * this and related functions are not inlined.
+ * Compute backtrace data and return it.  num_skip specifies how many inner
+ * frames to skip.  Use this to avoid showing the internal backtrace support
+ * functions in the backtrace.  This requires that this and related functions
+ * are not inlined.
  */
-static void
-set_backtrace(ErrorData *edata, int num_skip)
+char *
+set_backtrace(int num_skip)
 {
 	StringInfoData errtrace;
 
@@ -962,7 +961,7 @@ set_backtrace(ErrorData *edata, int num_skip)
 		nframes = backtrace(buf, lengthof(buf));
 		strfrms = backtrace_symbols(buf, nframes);
 		if (strfrms == NULL)
-			return;
+			return NULL;
 
 		for (int i = num_skip; i < nframes; i++)
 			appendStringInfo(&errtrace, "\n%s", strfrms[i]);
@@ -973,7 +972,7 @@ set_backtrace(ErrorData *edata, int num_skip)
 						   "backtrace generation is not supported by this installation");
 #endif
 
-	edata->backtrace = errtrace.data;
+	return errtrace.data;
 }
 
 /*
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 1a5d29ac9b..606f0cb1ba 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -36,6 +36,7 @@ volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t IdleSessionTimeoutPending = false;
 volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile sig_atomic_t LogMemoryContextPending = false;
+volatile sig_atomic_t LogBacktracePending = false;
 volatile sig_atomic_t IdleStatsUpdateTimeoutPending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 6d378ff785..7612be1bfd 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11882,4 +11882,9 @@
   prorettype => 'bytea', proargtypes => 'pg_brin_minmax_multi_summary',
   prosrc => 'brin_minmax_multi_summary_send' },
 
+# function to get the backtrace of server process
+{ oid => '6105', descr => 'log backtrace of server process',
+  proname => 'pg_log_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_log_backtrace' },
+
 ]
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 53fd168d93..67076aac83 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -94,6 +94,7 @@ extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
 extern PGDLLIMPORT volatile sig_atomic_t LogMemoryContextPending;
+extern PGDLLIMPORT volatile sig_atomic_t LogBacktracePending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleStatsUpdateTimeoutPending;
 
 extern PGDLLIMPORT volatile sig_atomic_t CheckClientConnectionPending;
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index 1b2cfac5ad..b34e1aaebd 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -67,6 +67,7 @@ extern PGPROC *BackendPidGetProc(int pid);
 extern PGPROC *BackendPidGetProcWithLock(int pid);
 extern int	BackendXidGetPid(TransactionId xid);
 extern bool IsBackendPid(int pid);
+extern bool SendProcSignalBackendOrAuxproc(int pid, ProcSignalReason signo);
 
 extern VirtualTransactionId *GetCurrentVirtualXIDs(TransactionId limitXmin,
 												   bool excludeXmin0, bool allDbs, int excludeVacuum,
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index ee636900f3..af3954d564 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -35,6 +35,7 @@ typedef enum
 	PROCSIG_WALSND_INIT_STOPPING,	/* ask walsenders to prepare for shutdown  */
 	PROCSIG_BARRIER,			/* global barrier interrupt  */
 	PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
+	PROCSIG_LOG_BACKTRACE,		/* ask backend to log the current backtrace */
 
 	/* Recovery conflict reasons */
 	PROCSIG_RECOVERY_CONFLICT_DATABASE,
@@ -65,7 +66,7 @@ extern int	SendProcSignal(pid_t pid, ProcSignalReason reason,
 extern uint64 EmitProcSignalBarrier(ProcSignalBarrierType type);
 extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
-
+extern void ProcessLogBacktraceInterrupt(void);
 extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
 
 #endif							/* PROCSIGNAL_H */
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index f5c6cd904d..9bcdcb2947 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -467,4 +467,6 @@ extern void set_syslog_parameters(const char *ident, int facility);
  */
 extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
 
+extern pg_noinline char *set_backtrace(int num_skip);
+
 #endif							/* ELOG_H */
diff --git a/src/test/regress/expected/backtrace.out b/src/test/regress/expected/backtrace.out
new file mode 100644
index 0000000000..2184a99483
--- /dev/null
+++ b/src/test/regress/expected/backtrace.out
@@ -0,0 +1,49 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/expected/backtrace_1.out b/src/test/regress/expected/backtrace_1.out
new file mode 100644
index 0000000000..fe9523f89d
--- /dev/null
+++ b/src/test/regress/expected/backtrace_1.out
@@ -0,0 +1,55 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 103e11483d..b4df53cb42 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -86,7 +86,7 @@ test: brin_bloom brin_multi
 # psql depends on create_am
 # amutils depends on geometry, create_index_spgist, hash_index, brin
 # ----------
-test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role
+test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role backtrace
 
 # collate.*.utf8 tests cannot be run in parallel with each other
 test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8
diff --git a/src/test/regress/sql/backtrace.sql b/src/test/regress/sql/backtrace.sql
new file mode 100644
index 0000000000..d74b1016ae
--- /dev/null
+++ b/src/test/regress/sql/backtrace.sql
@@ -0,0 +1,33 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+
+SELECT pg_log_backtrace(pg_backend_pid());
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+
+CREATE ROLE regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+RESET ROLE;
+
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+
+DROP ROLE regress_log_backtrace;
-- 
2.32.0

#107Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: vignesh C (#106)
Re: Printing backtrace of postgres processes

On Mon, Apr 18, 2022 at 9:10 AM vignesh C <vignesh21@gmail.com> wrote:

The attached v21 patch has the changes for the same.

Thanks for the patch. Here are some comments:

1. I think there's a fundamental problem with this patch, that is, it
prints the backtrace when the interrupt is processed but not when
interrupt is received. This way, the backends/aux processes will
always have backtraces in their main loops or some processing loops
wherever CHECK_FOR_INTERRUPTS() is called [1]2022-11-10 09:55:44.691 UTC [1346443] LOG: logging backtrace of PID 1346443 2022-11-10 09:55:44.691 UTC [1346443] LOG: current backtrace: postgres: checkpointer (set_backtrace+0x46) [0x5640df9849c6] postgres: checkpointer (ProcessLogBacktraceInterrupt+0x16) [0x5640df7fd326] postgres: checkpointer (CheckpointerMain+0x1a3) [0x5640df77f553] postgres: checkpointer (AuxiliaryProcessMain+0xc9) [0x5640df77d5e9] postgres: checkpointer (+0x436a9a) [0x5640df783a9a] postgres: checkpointer (PostmasterMain+0xd57) [0x5640df7877e7] postgres: checkpointer (main+0x20f) [0x5640df4a6f1f] /lib/x86_64-linux-gnu/libc.so.6(+0x29d90) [0x7f4b9fe9dd90] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80) [0x7f4b9fe9de40] postgres: checkpointer (_start+0x25) [0x5640df4a7265] 2022-11-10 09:56:05.032 UTC [1346448] LOG: logging backtrace of PID 1346448 2022-11-10 09:56:05.032 UTC [1346448] LOG: current backtrace: postgres: logical replication launcher (set_backtrace+0x46) [0x5640df9849c6] postgres: logical replication launcher (ProcessLogBacktraceInterrupt+0x16) [0x5640df7fd326] postgres: logical replication launcher (ApplyLauncherMain+0x11b) [0x5640df7a253b] postgres: logical replication launcher (StartBackgroundWorker+0x220) [0x5640df77e270] postgres: logical replication launcher (+0x4369f4) [0x5640df7839f4] postgres: logical replication launcher (+0x43771d) [0x5640df78471d] /lib/x86_64-linux-gnu/libc.so.6(+0x42520) [0x7f4b9feb6520] /lib/x86_64-linux-gnu/libc.so.6(__select+0xbd) [0x7f4b9ff8f74d] postgres: logical replication launcher (+0x43894d) [0x5640df78594d] postgres: logical replication launcher (PostmasterMain+0xcb5) [0x5640df787745] postgres: logical replication launcher (main+0x20f) [0x5640df4a6f1f] /lib/x86_64-linux-gnu/libc.so.6(+0x29d90) [0x7f4b9fe9dd90] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80) [0x7f4b9fe9de40] postgres: logical replication launcher (_start+0x25) [0x5640df4a7265]. What we need is
something more useful. What I think is the better way is to capture
the backtrace, call set_backtrace(), when the interrupt arrives and
then if we don't want to print it in the interrupt handler, perhaps
save it and print it in the next CFI() cycle. See some realistic and
useful backtrace [2]2022-11-10 10:25:20.021 UTC [1351953] LOG: current backtrace: postgres: ubuntu postgres [local] INSERT(set_backtrace+0x46) [0x55c60ae069b6] postgres: ubuntu postgres [local] INSERT(procsignal_sigusr1_handler+0x1d8) [0x55c60ac7f4f8] /lib/x86_64-linux-gnu/libc.so.6(+0x42520) [0x7f75a395c520] /lib/x86_64-linux-gnu/libc.so.6(+0x91104) [0x7f75a39ab104] /lib/x86_64-linux-gnu/libc.so.6(+0x9ccf8) [0x7f75a39b6cf8] postgres: ubuntu postgres [local] INSERT(PGSemaphoreLock+0x22) [0x55c60abfb1f2] postgres: ubuntu postgres [local] INSERT(LWLockAcquire+0x174) [0x55c60ac8f384] postgres: ubuntu postgres [local] INSERT(+0x1fb778) [0x55c60a9ca778] postgres: ubuntu postgres [local] INSERT(+0x1fb8d3) [0x55c60a9ca8d3] postgres: ubuntu postgres [local] INSERT(XLogInsertRecord+0x4e4) [0x55c60a9cb0c4] postgres: ubuntu postgres [local] INSERT(XLogInsert+0xcf) [0x55c60a9d167f] postgres: ubuntu postgres [local] INSERT(heap_insert+0x2ca) [0x55c60a97168a] postgres: ubuntu postgres [local] INSERT(+0x1ab14a) [0x55c60a97a14a] postgres: ubuntu postgres [local] INSERT(+0x33dcab) [0x55c60ab0ccab] postgres: ubuntu postgres [local] INSERT(+0x33f10d) [0x55c60ab0e10d] postgres: ubuntu postgres [local] INSERT(standard_ExecutorRun+0x102) [0x55c60aadfdb2] postgres: ubuntu postgres [local] INSERT(+0x4d48d4) [0x55c60aca38d4] postgres: ubuntu postgres [local] INSERT(PortalRun+0x27d) [0x55c60aca47ed] postgres: ubuntu postgres [local] INSERT(+0x4d180f) [0x55c60aca080f] postgres: ubuntu postgres [local] INSERT(PostgresMain+0x1eb4) [0x55c60aca2b94] postgres: ubuntu postgres [local] INSERT(+0x4396f8) [0x55c60ac086f8] postgres: ubuntu postgres [local] INSERT(PostmasterMain+0xcb5) [0x55c60ac09745] postgres: ubuntu postgres [local] INSERT(main+0x20f) [0x55c60a928f1f] /lib/x86_64-linux-gnu/libc.so.6(+0x29d90) [0x7f75a3943d90] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80) [0x7f75a3943e40] postgres: ubuntu postgres [local] INSERT(_start+0x25) [0x55c60a929265] 2022-11-10 10:25:20.021 UTC [1351953] STATEMENT: insert into foo select * from generate_series(1, 1000000000); 2022-11-10 10:25:20.521 UTC [1351953] LOG: logging backtrace of PID 1351953 2022-11-10 10:25:20.521 UTC [1351953] LOG: current backtrace: postgres: ubuntu postgres [local] INSERT(set_backtrace+0x46) [0x55c60ae069b6] postgres: ubuntu postgres [local] INSERT(procsignal_sigusr1_handler+0x1d8) [0x55c60ac7f4f8] /lib/x86_64-linux-gnu/libc.so.6(+0x42520) [0x7f75a395c520] postgres: ubuntu postgres [local] INSERT(XLogInsertRecord+0x976) [0x55c60a9cb556] postgres: ubuntu postgres [local] INSERT(XLogInsert+0xcf) [0x55c60a9d167f] postgres: ubuntu postgres [local] INSERT(heap_insert+0x2ca) [0x55c60a97168a] postgres: ubuntu postgres [local] INSERT(+0x1ab14a) [0x55c60a97a14a] postgres: ubuntu postgres [local] INSERT(+0x33dcab) [0x55c60ab0ccab] postgres: ubuntu postgres [local] INSERT(+0x33f10d) [0x55c60ab0e10d] postgres: ubuntu postgres [local] INSERT(standard_ExecutorRun+0x102) [0x55c60aadfdb2] postgres: ubuntu postgres [local] INSERT(+0x4d48d4) [0x55c60aca38d4] postgres: ubuntu postgres [local] INSERT(PortalRun+0x27d) [0x55c60aca47ed] postgres: ubuntu postgres [local] INSERT(+0x4d180f) [0x55c60aca080f] postgres: ubuntu postgres [local] INSERT(PostgresMain+0x1eb4) [0x55c60aca2b94] postgres: ubuntu postgres [local] INSERT(+0x4396f8) [0x55c60ac086f8] postgres: ubuntu postgres [local] INSERT(PostmasterMain+0xcb5) [0x55c60ac09745] postgres: ubuntu postgres [local] INSERT(main+0x20f) [0x55c60a928f1f] /lib/x86_64-linux-gnu/libc.so.6(+0x29d90) [0x7f75a3943d90] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80) [0x7f75a3943e40] postgres: ubuntu postgres [local] INSERT(_start+0x25) [0x55c60a929265] 2022-11-10 10:25:20.521 UTC [1351953] STATEMENT: insert into foo select * from generate_series(1, 1000000000); 2022-11-10 10:25:21.050 UTC [1351953] LOG: logging backtrace of PID 1351953 2022-11-10 10:25:21.050 UTC [1351953] LOG: current backtrace: postgres: ubuntu postgres [local] INSERT(set_backtrace+0x46) [0x55c60ae069b6] postgres: ubuntu postgres [local] INSERT(procsignal_sigusr1_handler+0x1d8) [0x55c60ac7f4f8] /lib/x86_64-linux-gnu/libc.so.6(+0x42520) [0x7f75a395c520] /lib/x86_64-linux-gnu/libc.so.6(fdatasync+0x17) [0x7f75a3a35af7] postgres: ubuntu postgres [local] INSERT(+0x1fb384) [0x55c60a9ca384] postgres: ubuntu postgres [local] INSERT(+0x1fb7ef) [0x55c60a9ca7ef] postgres: ubuntu postgres [local] INSERT(+0x1fb8d3) [0x55c60a9ca8d3] postgres: ubuntu postgres [local] INSERT(XLogInsertRecord+0x4e4) [0x55c60a9cb0c4] postgres: ubuntu postgres [local] INSERT(XLogInsert+0xcf) [0x55c60a9d167f] postgres: ubuntu postgres [local] INSERT(heap_insert+0x2ca) [0x55c60a97168a] postgres: ubuntu postgres [local] INSERT(+0x1ab14a) [0x55c60a97a14a] postgres: ubuntu postgres [local] INSERT(+0x33dcab) [0x55c60ab0ccab] postgres: ubuntu postgres [local] INSERT(+0x33f10d) [0x55c60ab0e10d] postgres: ubuntu postgres [local] INSERT(standard_ExecutorRun+0x102) [0x55c60aadfdb2] postgres: ubuntu postgres [local] INSERT(+0x4d48d4) [0x55c60aca38d4] postgres: ubuntu postgres [local] INSERT(PortalRun+0x27d) [0x55c60aca47ed] postgres: ubuntu postgres [local] INSERT(+0x4d180f) [0x55c60aca080f] postgres: ubuntu postgres [local] INSERT(PostgresMain+0x1eb4) [0x55c60aca2b94] postgres: ubuntu postgres [local] INSERT(+0x4396f8) [0x55c60ac086f8] postgres: ubuntu postgres [local] INSERT(PostmasterMain+0xcb5) [0x55c60ac09745] postgres: ubuntu postgres [local] INSERT(main+0x20f) [0x55c60a928f1f] /lib/x86_64-linux-gnu/libc.so.6(+0x29d90) [0x7f75a3943d90] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80) [0x7f75a3943e40] postgres: ubuntu postgres [local] INSERT(_start+0x25) [0x55c60a929265] when I emitted the backtrace in the interrupt
handler.

2.
+        specified process ID.  This function can send the request to
+        backends and auxiliary processes except the logger and statistics
+        collector.  The backtraces will be logged at <literal>LOG</literal>
There's no statistics collector process any more. Please remove it.
Also remove 'the' before 'logger' to be in sync with
pg_log_backend_memory_contexts docs.

3.
+ configuration set (See <xref linkend="runtime-config-logging"/> for
Lowercase 'see' to be in sync with pg_log_backend_memory_contexts docs.

4.
+ You can obtain the file name and line number from the logged
details by using
How about 'One can obtain'?

5.
+postgres=# select pg_log_backtrace(pg_backend_pid());
+For example:
+<screen>
+2021-01-27 11:33:50.247 IST [111735] LOG:  current backtrace:
A more realistic example would be better here, say walwriter or
checkpointer or some other process backtrace will be good instead of a
backend logging its pg_log_backtrace()'s call stack?

6.
Don't we need the backtrace of walreceiver? I think it'll be good to
have in ProcessWalRcvInterrupts().

7.
+            errmsg_internal("logging backtrace of PID %d", MyProcPid));
+    elog(LOG_SERVER_ONLY, "current backtrace:%s", errtrace);
Can we get rid of "current backtrace:%s" and have something similar to
ProcessLogMemoryContextInterrupt() like below?

errmsg("logging backtrace of PID %d", MyProcPid)));
errmsg("%s", errtrace);

[1]: 2022-11-10 09:55:44.691 UTC [1346443] LOG: logging backtrace of PID 1346443 2022-11-10 09:55:44.691 UTC [1346443] LOG: current backtrace: postgres: checkpointer (set_backtrace+0x46) [0x5640df9849c6] postgres: checkpointer (ProcessLogBacktraceInterrupt+0x16) [0x5640df7fd326] postgres: checkpointer (CheckpointerMain+0x1a3) [0x5640df77f553] postgres: checkpointer (AuxiliaryProcessMain+0xc9) [0x5640df77d5e9] postgres: checkpointer (+0x436a9a) [0x5640df783a9a] postgres: checkpointer (PostmasterMain+0xd57) [0x5640df7877e7] postgres: checkpointer (main+0x20f) [0x5640df4a6f1f] /lib/x86_64-linux-gnu/libc.so.6(+0x29d90) [0x7f4b9fe9dd90] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80) [0x7f4b9fe9de40] postgres: checkpointer (_start+0x25) [0x5640df4a7265] 2022-11-10 09:56:05.032 UTC [1346448] LOG: logging backtrace of PID 1346448 2022-11-10 09:56:05.032 UTC [1346448] LOG: current backtrace: postgres: logical replication launcher (set_backtrace+0x46) [0x5640df9849c6] postgres: logical replication launcher (ProcessLogBacktraceInterrupt+0x16) [0x5640df7fd326] postgres: logical replication launcher (ApplyLauncherMain+0x11b) [0x5640df7a253b] postgres: logical replication launcher (StartBackgroundWorker+0x220) [0x5640df77e270] postgres: logical replication launcher (+0x4369f4) [0x5640df7839f4] postgres: logical replication launcher (+0x43771d) [0x5640df78471d] /lib/x86_64-linux-gnu/libc.so.6(+0x42520) [0x7f4b9feb6520] /lib/x86_64-linux-gnu/libc.so.6(__select+0xbd) [0x7f4b9ff8f74d] postgres: logical replication launcher (+0x43894d) [0x5640df78594d] postgres: logical replication launcher (PostmasterMain+0xcb5) [0x5640df787745] postgres: logical replication launcher (main+0x20f) [0x5640df4a6f1f] /lib/x86_64-linux-gnu/libc.so.6(+0x29d90) [0x7f4b9fe9dd90] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80) [0x7f4b9fe9de40] postgres: logical replication launcher (_start+0x25) [0x5640df4a7265]
2022-11-10 09:55:44.691 UTC [1346443] LOG: logging backtrace of PID 1346443
2022-11-10 09:55:44.691 UTC [1346443] LOG: current backtrace:
postgres: checkpointer (set_backtrace+0x46) [0x5640df9849c6]
postgres: checkpointer (ProcessLogBacktraceInterrupt+0x16)
[0x5640df7fd326]
postgres: checkpointer (CheckpointerMain+0x1a3) [0x5640df77f553]
postgres: checkpointer (AuxiliaryProcessMain+0xc9) [0x5640df77d5e9]
postgres: checkpointer (+0x436a9a) [0x5640df783a9a]
postgres: checkpointer (PostmasterMain+0xd57) [0x5640df7877e7]
postgres: checkpointer (main+0x20f) [0x5640df4a6f1f]
/lib/x86_64-linux-gnu/libc.so.6(+0x29d90) [0x7f4b9fe9dd90]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80) [0x7f4b9fe9de40]
postgres: checkpointer (_start+0x25) [0x5640df4a7265]
2022-11-10 09:56:05.032 UTC [1346448] LOG: logging backtrace of PID 1346448
2022-11-10 09:56:05.032 UTC [1346448] LOG: current backtrace:
postgres: logical replication launcher (set_backtrace+0x46)
[0x5640df9849c6]
postgres: logical replication launcher
(ProcessLogBacktraceInterrupt+0x16) [0x5640df7fd326]
postgres: logical replication launcher
(ApplyLauncherMain+0x11b) [0x5640df7a253b]
postgres: logical replication launcher
(StartBackgroundWorker+0x220) [0x5640df77e270]
postgres: logical replication launcher (+0x4369f4) [0x5640df7839f4]
postgres: logical replication launcher (+0x43771d) [0x5640df78471d]
/lib/x86_64-linux-gnu/libc.so.6(+0x42520) [0x7f4b9feb6520]
/lib/x86_64-linux-gnu/libc.so.6(__select+0xbd) [0x7f4b9ff8f74d]
postgres: logical replication launcher (+0x43894d) [0x5640df78594d]
postgres: logical replication launcher (PostmasterMain+0xcb5)
[0x5640df787745]
postgres: logical replication launcher (main+0x20f) [0x5640df4a6f1f]
/lib/x86_64-linux-gnu/libc.so.6(+0x29d90) [0x7f4b9fe9dd90]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80) [0x7f4b9fe9de40]
postgres: logical replication launcher (_start+0x25) [0x5640df4a7265]

[2]: 2022-11-10 10:25:20.021 UTC [1351953] LOG: current backtrace: postgres: ubuntu postgres [local] INSERT(set_backtrace+0x46) [0x55c60ae069b6] postgres: ubuntu postgres [local] INSERT(procsignal_sigusr1_handler+0x1d8) [0x55c60ac7f4f8] /lib/x86_64-linux-gnu/libc.so.6(+0x42520) [0x7f75a395c520] /lib/x86_64-linux-gnu/libc.so.6(+0x91104) [0x7f75a39ab104] /lib/x86_64-linux-gnu/libc.so.6(+0x9ccf8) [0x7f75a39b6cf8] postgres: ubuntu postgres [local] INSERT(PGSemaphoreLock+0x22) [0x55c60abfb1f2] postgres: ubuntu postgres [local] INSERT(LWLockAcquire+0x174) [0x55c60ac8f384] postgres: ubuntu postgres [local] INSERT(+0x1fb778) [0x55c60a9ca778] postgres: ubuntu postgres [local] INSERT(+0x1fb8d3) [0x55c60a9ca8d3] postgres: ubuntu postgres [local] INSERT(XLogInsertRecord+0x4e4) [0x55c60a9cb0c4] postgres: ubuntu postgres [local] INSERT(XLogInsert+0xcf) [0x55c60a9d167f] postgres: ubuntu postgres [local] INSERT(heap_insert+0x2ca) [0x55c60a97168a] postgres: ubuntu postgres [local] INSERT(+0x1ab14a) [0x55c60a97a14a] postgres: ubuntu postgres [local] INSERT(+0x33dcab) [0x55c60ab0ccab] postgres: ubuntu postgres [local] INSERT(+0x33f10d) [0x55c60ab0e10d] postgres: ubuntu postgres [local] INSERT(standard_ExecutorRun+0x102) [0x55c60aadfdb2] postgres: ubuntu postgres [local] INSERT(+0x4d48d4) [0x55c60aca38d4] postgres: ubuntu postgres [local] INSERT(PortalRun+0x27d) [0x55c60aca47ed] postgres: ubuntu postgres [local] INSERT(+0x4d180f) [0x55c60aca080f] postgres: ubuntu postgres [local] INSERT(PostgresMain+0x1eb4) [0x55c60aca2b94] postgres: ubuntu postgres [local] INSERT(+0x4396f8) [0x55c60ac086f8] postgres: ubuntu postgres [local] INSERT(PostmasterMain+0xcb5) [0x55c60ac09745] postgres: ubuntu postgres [local] INSERT(main+0x20f) [0x55c60a928f1f] /lib/x86_64-linux-gnu/libc.so.6(+0x29d90) [0x7f75a3943d90] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80) [0x7f75a3943e40] postgres: ubuntu postgres [local] INSERT(_start+0x25) [0x55c60a929265] 2022-11-10 10:25:20.021 UTC [1351953] STATEMENT: insert into foo select * from generate_series(1, 1000000000); 2022-11-10 10:25:20.521 UTC [1351953] LOG: logging backtrace of PID 1351953 2022-11-10 10:25:20.521 UTC [1351953] LOG: current backtrace: postgres: ubuntu postgres [local] INSERT(set_backtrace+0x46) [0x55c60ae069b6] postgres: ubuntu postgres [local] INSERT(procsignal_sigusr1_handler+0x1d8) [0x55c60ac7f4f8] /lib/x86_64-linux-gnu/libc.so.6(+0x42520) [0x7f75a395c520] postgres: ubuntu postgres [local] INSERT(XLogInsertRecord+0x976) [0x55c60a9cb556] postgres: ubuntu postgres [local] INSERT(XLogInsert+0xcf) [0x55c60a9d167f] postgres: ubuntu postgres [local] INSERT(heap_insert+0x2ca) [0x55c60a97168a] postgres: ubuntu postgres [local] INSERT(+0x1ab14a) [0x55c60a97a14a] postgres: ubuntu postgres [local] INSERT(+0x33dcab) [0x55c60ab0ccab] postgres: ubuntu postgres [local] INSERT(+0x33f10d) [0x55c60ab0e10d] postgres: ubuntu postgres [local] INSERT(standard_ExecutorRun+0x102) [0x55c60aadfdb2] postgres: ubuntu postgres [local] INSERT(+0x4d48d4) [0x55c60aca38d4] postgres: ubuntu postgres [local] INSERT(PortalRun+0x27d) [0x55c60aca47ed] postgres: ubuntu postgres [local] INSERT(+0x4d180f) [0x55c60aca080f] postgres: ubuntu postgres [local] INSERT(PostgresMain+0x1eb4) [0x55c60aca2b94] postgres: ubuntu postgres [local] INSERT(+0x4396f8) [0x55c60ac086f8] postgres: ubuntu postgres [local] INSERT(PostmasterMain+0xcb5) [0x55c60ac09745] postgres: ubuntu postgres [local] INSERT(main+0x20f) [0x55c60a928f1f] /lib/x86_64-linux-gnu/libc.so.6(+0x29d90) [0x7f75a3943d90] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80) [0x7f75a3943e40] postgres: ubuntu postgres [local] INSERT(_start+0x25) [0x55c60a929265] 2022-11-10 10:25:20.521 UTC [1351953] STATEMENT: insert into foo select * from generate_series(1, 1000000000); 2022-11-10 10:25:21.050 UTC [1351953] LOG: logging backtrace of PID 1351953 2022-11-10 10:25:21.050 UTC [1351953] LOG: current backtrace: postgres: ubuntu postgres [local] INSERT(set_backtrace+0x46) [0x55c60ae069b6] postgres: ubuntu postgres [local] INSERT(procsignal_sigusr1_handler+0x1d8) [0x55c60ac7f4f8] /lib/x86_64-linux-gnu/libc.so.6(+0x42520) [0x7f75a395c520] /lib/x86_64-linux-gnu/libc.so.6(fdatasync+0x17) [0x7f75a3a35af7] postgres: ubuntu postgres [local] INSERT(+0x1fb384) [0x55c60a9ca384] postgres: ubuntu postgres [local] INSERT(+0x1fb7ef) [0x55c60a9ca7ef] postgres: ubuntu postgres [local] INSERT(+0x1fb8d3) [0x55c60a9ca8d3] postgres: ubuntu postgres [local] INSERT(XLogInsertRecord+0x4e4) [0x55c60a9cb0c4] postgres: ubuntu postgres [local] INSERT(XLogInsert+0xcf) [0x55c60a9d167f] postgres: ubuntu postgres [local] INSERT(heap_insert+0x2ca) [0x55c60a97168a] postgres: ubuntu postgres [local] INSERT(+0x1ab14a) [0x55c60a97a14a] postgres: ubuntu postgres [local] INSERT(+0x33dcab) [0x55c60ab0ccab] postgres: ubuntu postgres [local] INSERT(+0x33f10d) [0x55c60ab0e10d] postgres: ubuntu postgres [local] INSERT(standard_ExecutorRun+0x102) [0x55c60aadfdb2] postgres: ubuntu postgres [local] INSERT(+0x4d48d4) [0x55c60aca38d4] postgres: ubuntu postgres [local] INSERT(PortalRun+0x27d) [0x55c60aca47ed] postgres: ubuntu postgres [local] INSERT(+0x4d180f) [0x55c60aca080f] postgres: ubuntu postgres [local] INSERT(PostgresMain+0x1eb4) [0x55c60aca2b94] postgres: ubuntu postgres [local] INSERT(+0x4396f8) [0x55c60ac086f8] postgres: ubuntu postgres [local] INSERT(PostmasterMain+0xcb5) [0x55c60ac09745] postgres: ubuntu postgres [local] INSERT(main+0x20f) [0x55c60a928f1f] /lib/x86_64-linux-gnu/libc.so.6(+0x29d90) [0x7f75a3943d90] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80) [0x7f75a3943e40] postgres: ubuntu postgres [local] INSERT(_start+0x25) [0x55c60a929265]
2022-11-10 10:25:20.021 UTC [1351953] LOG: current backtrace:
postgres: ubuntu postgres [local] INSERT(set_backtrace+0x46)
[0x55c60ae069b6]
postgres: ubuntu postgres [local]
INSERT(procsignal_sigusr1_handler+0x1d8) [0x55c60ac7f4f8]
/lib/x86_64-linux-gnu/libc.so.6(+0x42520) [0x7f75a395c520]
/lib/x86_64-linux-gnu/libc.so.6(+0x91104) [0x7f75a39ab104]
/lib/x86_64-linux-gnu/libc.so.6(+0x9ccf8) [0x7f75a39b6cf8]
postgres: ubuntu postgres [local] INSERT(PGSemaphoreLock+0x22)
[0x55c60abfb1f2]
postgres: ubuntu postgres [local] INSERT(LWLockAcquire+0x174)
[0x55c60ac8f384]
postgres: ubuntu postgres [local] INSERT(+0x1fb778) [0x55c60a9ca778]
postgres: ubuntu postgres [local] INSERT(+0x1fb8d3) [0x55c60a9ca8d3]
postgres: ubuntu postgres [local]
INSERT(XLogInsertRecord+0x4e4) [0x55c60a9cb0c4]
postgres: ubuntu postgres [local] INSERT(XLogInsert+0xcf)
[0x55c60a9d167f]
postgres: ubuntu postgres [local] INSERT(heap_insert+0x2ca)
[0x55c60a97168a]
postgres: ubuntu postgres [local] INSERT(+0x1ab14a) [0x55c60a97a14a]
postgres: ubuntu postgres [local] INSERT(+0x33dcab) [0x55c60ab0ccab]
postgres: ubuntu postgres [local] INSERT(+0x33f10d) [0x55c60ab0e10d]
postgres: ubuntu postgres [local]
INSERT(standard_ExecutorRun+0x102) [0x55c60aadfdb2]
postgres: ubuntu postgres [local] INSERT(+0x4d48d4) [0x55c60aca38d4]
postgres: ubuntu postgres [local] INSERT(PortalRun+0x27d)
[0x55c60aca47ed]
postgres: ubuntu postgres [local] INSERT(+0x4d180f) [0x55c60aca080f]
postgres: ubuntu postgres [local] INSERT(PostgresMain+0x1eb4)
[0x55c60aca2b94]
postgres: ubuntu postgres [local] INSERT(+0x4396f8) [0x55c60ac086f8]
postgres: ubuntu postgres [local] INSERT(PostmasterMain+0xcb5)
[0x55c60ac09745]
postgres: ubuntu postgres [local] INSERT(main+0x20f) [0x55c60a928f1f]
/lib/x86_64-linux-gnu/libc.so.6(+0x29d90) [0x7f75a3943d90]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80) [0x7f75a3943e40]
postgres: ubuntu postgres [local] INSERT(_start+0x25) [0x55c60a929265]
2022-11-10 10:25:20.021 UTC [1351953] STATEMENT: insert into foo
select * from generate_series(1, 1000000000);
2022-11-10 10:25:20.521 UTC [1351953] LOG: logging backtrace of PID 1351953
2022-11-10 10:25:20.521 UTC [1351953] LOG: current backtrace:
postgres: ubuntu postgres [local] INSERT(set_backtrace+0x46)
[0x55c60ae069b6]
postgres: ubuntu postgres [local]
INSERT(procsignal_sigusr1_handler+0x1d8) [0x55c60ac7f4f8]
/lib/x86_64-linux-gnu/libc.so.6(+0x42520) [0x7f75a395c520]
postgres: ubuntu postgres [local]
INSERT(XLogInsertRecord+0x976) [0x55c60a9cb556]
postgres: ubuntu postgres [local] INSERT(XLogInsert+0xcf)
[0x55c60a9d167f]
postgres: ubuntu postgres [local] INSERT(heap_insert+0x2ca)
[0x55c60a97168a]
postgres: ubuntu postgres [local] INSERT(+0x1ab14a) [0x55c60a97a14a]
postgres: ubuntu postgres [local] INSERT(+0x33dcab) [0x55c60ab0ccab]
postgres: ubuntu postgres [local] INSERT(+0x33f10d) [0x55c60ab0e10d]
postgres: ubuntu postgres [local]
INSERT(standard_ExecutorRun+0x102) [0x55c60aadfdb2]
postgres: ubuntu postgres [local] INSERT(+0x4d48d4) [0x55c60aca38d4]
postgres: ubuntu postgres [local] INSERT(PortalRun+0x27d)
[0x55c60aca47ed]
postgres: ubuntu postgres [local] INSERT(+0x4d180f) [0x55c60aca080f]
postgres: ubuntu postgres [local] INSERT(PostgresMain+0x1eb4)
[0x55c60aca2b94]
postgres: ubuntu postgres [local] INSERT(+0x4396f8) [0x55c60ac086f8]
postgres: ubuntu postgres [local] INSERT(PostmasterMain+0xcb5)
[0x55c60ac09745]
postgres: ubuntu postgres [local] INSERT(main+0x20f) [0x55c60a928f1f]
/lib/x86_64-linux-gnu/libc.so.6(+0x29d90) [0x7f75a3943d90]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80) [0x7f75a3943e40]
postgres: ubuntu postgres [local] INSERT(_start+0x25) [0x55c60a929265]
2022-11-10 10:25:20.521 UTC [1351953] STATEMENT: insert into foo
select * from generate_series(1, 1000000000);
2022-11-10 10:25:21.050 UTC [1351953] LOG: logging backtrace of PID 1351953
2022-11-10 10:25:21.050 UTC [1351953] LOG: current backtrace:
postgres: ubuntu postgres [local] INSERT(set_backtrace+0x46)
[0x55c60ae069b6]
postgres: ubuntu postgres [local]
INSERT(procsignal_sigusr1_handler+0x1d8) [0x55c60ac7f4f8]
/lib/x86_64-linux-gnu/libc.so.6(+0x42520) [0x7f75a395c520]
/lib/x86_64-linux-gnu/libc.so.6(fdatasync+0x17) [0x7f75a3a35af7]
postgres: ubuntu postgres [local] INSERT(+0x1fb384) [0x55c60a9ca384]
postgres: ubuntu postgres [local] INSERT(+0x1fb7ef) [0x55c60a9ca7ef]
postgres: ubuntu postgres [local] INSERT(+0x1fb8d3) [0x55c60a9ca8d3]
postgres: ubuntu postgres [local]
INSERT(XLogInsertRecord+0x4e4) [0x55c60a9cb0c4]
postgres: ubuntu postgres [local] INSERT(XLogInsert+0xcf)
[0x55c60a9d167f]
postgres: ubuntu postgres [local] INSERT(heap_insert+0x2ca)
[0x55c60a97168a]
postgres: ubuntu postgres [local] INSERT(+0x1ab14a) [0x55c60a97a14a]
postgres: ubuntu postgres [local] INSERT(+0x33dcab) [0x55c60ab0ccab]
postgres: ubuntu postgres [local] INSERT(+0x33f10d) [0x55c60ab0e10d]
postgres: ubuntu postgres [local]
INSERT(standard_ExecutorRun+0x102) [0x55c60aadfdb2]
postgres: ubuntu postgres [local] INSERT(+0x4d48d4) [0x55c60aca38d4]
postgres: ubuntu postgres [local] INSERT(PortalRun+0x27d)
[0x55c60aca47ed]
postgres: ubuntu postgres [local] INSERT(+0x4d180f) [0x55c60aca080f]
postgres: ubuntu postgres [local] INSERT(PostgresMain+0x1eb4)
[0x55c60aca2b94]
postgres: ubuntu postgres [local] INSERT(+0x4396f8) [0x55c60ac086f8]
postgres: ubuntu postgres [local] INSERT(PostmasterMain+0xcb5)
[0x55c60ac09745]
postgres: ubuntu postgres [local] INSERT(main+0x20f) [0x55c60a928f1f]
/lib/x86_64-linux-gnu/libc.so.6(+0x29d90) [0x7f75a3943d90]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80) [0x7f75a3943e40]
postgres: ubuntu postgres [local] INSERT(_start+0x25) [0x55c60a929265]

--
Bharath Rupireddy
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com

#108Kyotaro Horiguchi
horikyota.ntt@gmail.com
In reply to: Bharath Rupireddy (#107)
Re: Printing backtrace of postgres processes

At Thu, 10 Nov 2022 15:56:35 +0530, Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> wrote in

On Mon, Apr 18, 2022 at 9:10 AM vignesh C <vignesh21@gmail.com> wrote:

The attached v21 patch has the changes for the same.

Thanks for the patch. Here are some comments:

1. I think there's a fundamental problem with this patch, that is, it
prints the backtrace when the interrupt is processed but not when
interrupt is received. This way, the backends/aux processes will

Yeah, but the obstacle was backtrace(3) itself. Andres pointed [1]/messages/by-id/20201201032649.aekv5b5dicvmovf4@alap3.anarazel.de | > Surely this is *utterly* unsafe. You can't do that sort of stuff in | > a signal handler. | | That's of course true for the current implementation - but I don't think | it's a fundamental constraint. With a bit of care backtrace() and | backtrace_symbols() itself can be signal safe:
that that may be doable with some care (and I agree to the opinion).
AFAIS no discussions followed and things have been to the current
shape since then.

[1]: /messages/by-id/20201201032649.aekv5b5dicvmovf4@alap3.anarazel.de | > Surely this is *utterly* unsafe. You can't do that sort of stuff in | > a signal handler. | | That's of course true for the current implementation - but I don't think | it's a fundamental constraint. With a bit of care backtrace() and | backtrace_symbols() itself can be signal safe:
| > Surely this is *utterly* unsafe. You can't do that sort of stuff in
| > a signal handler.
|
| That's of course true for the current implementation - but I don't think
| it's a fundamental constraint. With a bit of care backtrace() and
| backtrace_symbols() itself can be signal safe:

man 3 backtrace

* backtrace() and backtrace_symbols_fd() don't call malloc() explic‐
itly, but they are part of libgcc, which gets loaded dynamically
when first used. Dynamic loading usually triggers a call to mal‐
loc(3). If you need certain calls to these two functions to not
allocate memory (in signal handlers, for example), you need to make
sure libgcc is loaded beforehand.

regards.

--
Kyotaro Horiguchi
NTT Open Source Software Center

#109Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: Kyotaro Horiguchi (#108)
Re: Printing backtrace of postgres processes

On Fri, Nov 11, 2022 at 7:59 AM Kyotaro Horiguchi
<horikyota.ntt@gmail.com> wrote:

At Thu, 10 Nov 2022 15:56:35 +0530, Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> wrote in

On Mon, Apr 18, 2022 at 9:10 AM vignesh C <vignesh21@gmail.com> wrote:

The attached v21 patch has the changes for the same.

Thanks for the patch. Here are some comments:

1. I think there's a fundamental problem with this patch, that is, it
prints the backtrace when the interrupt is processed but not when
interrupt is received. This way, the backends/aux processes will

Yeah, but the obstacle was backtrace(3) itself. Andres pointed [1]
that that may be doable with some care (and I agree to the opinion).
AFAIS no discussions followed and things have been to the current
shape since then.

[1] /messages/by-id/20201201032649.aekv5b5dicvmovf4@alap3.anarazel.de
| > Surely this is *utterly* unsafe. You can't do that sort of stuff in
| > a signal handler.
|
| That's of course true for the current implementation - but I don't think
| it's a fundamental constraint. With a bit of care backtrace() and
| backtrace_symbols() itself can be signal safe:

man 3 backtrace

* backtrace() and backtrace_symbols_fd() don't call malloc() explic‐
itly, but they are part of libgcc, which gets loaded dynamically
when first used. Dynamic loading usually triggers a call to mal‐
loc(3). If you need certain calls to these two functions to not
allocate memory (in signal handlers, for example), you need to make
sure libgcc is loaded beforehand.

I missed that part. Thanks for pointing it out. The
backtrace_symbols() seems to be returning a malloc'ed array [1]https://linux.die.net/man/3/backtrace_symbols,
meaning it can't be used in signal handlers (if used, it can cause
deadlocks as per [2]https://stackoverflow.com/questions/40049751/malloc-inside-linux-signal-handler-cause-deadlock) and existing set_backtrace() is using it.
Therefore, we need to either change set_backtrace() to use
backtrace_symbols_fd() instead of backtrace_symobls() or introduce
another function for the purpose of this feature. If done that, then
we can think of preloading of libgcc which makes backtrace(),
backtrace_symobols_fd() safe to use in signal handlers.

Looks like we're not loading libgcc explicitly now into any of
postgres processes, please correct me if I'm wrong here. If we're not
loading it right now, is it acceptable to load libgcc into every
postgres process for the sake of this feature?

[1]: https://linux.die.net/man/3/backtrace_symbols
[2]: https://stackoverflow.com/questions/40049751/malloc-inside-linux-signal-handler-cause-deadlock

--
Bharath Rupireddy
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com

#110Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: Bharath Rupireddy (#109)
3 attachment(s)
Re: Printing backtrace of postgres processes

On Fri, Nov 11, 2022 at 6:04 PM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Fri, Nov 11, 2022 at 7:59 AM Kyotaro Horiguchi
<horikyota.ntt@gmail.com> wrote:

At Thu, 10 Nov 2022 15:56:35 +0530, Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> wrote in

On Mon, Apr 18, 2022 at 9:10 AM vignesh C <vignesh21@gmail.com> wrote:

The attached v21 patch has the changes for the same.

Thanks for the patch. Here are some comments:

1. I think there's a fundamental problem with this patch, that is, it
prints the backtrace when the interrupt is processed but not when
interrupt is received. This way, the backends/aux processes will

Yeah, but the obstacle was backtrace(3) itself. Andres pointed [1]
that that may be doable with some care (and I agree to the opinion).
AFAIS no discussions followed and things have been to the current
shape since then.

[1] /messages/by-id/20201201032649.aekv5b5dicvmovf4@alap3.anarazel.de
| > Surely this is *utterly* unsafe. You can't do that sort of stuff in
| > a signal handler.
|
| That's of course true for the current implementation - but I don't think
| it's a fundamental constraint. With a bit of care backtrace() and
| backtrace_symbols() itself can be signal safe:

man 3 backtrace

* backtrace() and backtrace_symbols_fd() don't call malloc() explic‐
itly, but they are part of libgcc, which gets loaded dynamically
when first used. Dynamic loading usually triggers a call to mal‐
loc(3). If you need certain calls to these two functions to not
allocate memory (in signal handlers, for example), you need to make
sure libgcc is loaded beforehand.

I missed that part. Thanks for pointing it out. The
backtrace_symbols() seems to be returning a malloc'ed array [1],
meaning it can't be used in signal handlers (if used, it can cause
deadlocks as per [2]) and existing set_backtrace() is using it.
Therefore, we need to either change set_backtrace() to use
backtrace_symbols_fd() instead of backtrace_symobls() or introduce
another function for the purpose of this feature. If done that, then
we can think of preloading of libgcc which makes backtrace(),
backtrace_symobols_fd() safe to use in signal handlers.

Looks like we're not loading libgcc explicitly now into any of
postgres processes, please correct me if I'm wrong here. If we're not
loading it right now, is it acceptable to load libgcc into every
postgres process for the sake of this feature?

[1] https://linux.die.net/man/3/backtrace_symbols
[2] https://stackoverflow.com/questions/40049751/malloc-inside-linux-signal-handler-cause-deadlock

I took a stab at it after discussing with Vignesh off-list. Here's
what I've done:

1. Make backtrace functions signal safe i.e. ensure libgcc is loaded,
just in case if it's not, by calling backtrace() function during the
early life of a process after SIGUSR1 signal handler and other signal
handlers are established.
2. Emit backtrace to stderr within the signal handler itself to keep
things simple so that we don't need to allocate dynamic memory inside
the signal handler.
3. Split the patch into 3 for ease of review, 0001 does preparatory
stuff, 0002 adds the function, 0003 adds the docs and tests.

I'm attaching the v23 patch set for further review.

--
Bharath Rupireddy
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com

Attachments:

v22-0001-Move-sending-multiplexed-SIGUSR1-signal-code-to-.patchapplication/octet-stream; name=v22-0001-Move-sending-multiplexed-SIGUSR1-signal-code-to-.patchDownload
From b2ff8f344528fc871863dccdb9eda8624e66c29d Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Tue, 29 Nov 2022 09:59:56 +0000
Subject: [PATCH v22] Move sending multiplexed-SIGUSR1 signal code to a
 function

Add a new function hosting the common code for sending
multiplexed-SIGUSR1 signal to a backend process. This function
will also be used as-is by an upcoming commit reducing the code
duplication.
---
 src/backend/storage/ipc/procarray.c | 60 +++++++++++++++++++++++++++++
 src/backend/utils/adt/mcxtfuncs.c   | 49 ++---------------------
 src/include/storage/procarray.h     |  1 +
 3 files changed, 64 insertions(+), 46 deletions(-)

diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 06918759e7..58ad140466 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -3140,6 +3140,66 @@ HaveVirtualXIDsDelayingChkpt(VirtualTransactionId *vxids, int nvxids, int type)
 	return result;
 }
 
+/*
+ * SendProcSignalBackendOrAuxproc -- check if the process with given pid is a
+ * backend or an auxiliary process and send it the SIGUSR1 signal for a given
+ * reason.
+ *
+ * Returns true if sending the signal was successful, false otherwise.
+ */
+bool
+SendProcSignalBackendOrAuxproc(int pid, ProcSignalReason reason)
+{
+	PGPROC	   *proc;
+	BackendId	backendId = InvalidBackendId;
+
+	proc = BackendPidGetProc(pid);
+
+	/*
+	 * 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 or a long running process,
+	 * that it might end on its own first and its memory contexts are not
+	 * logged or backtrace not logged is not a problem.
+	 */
+	if (proc == NULL)
+	{
+		/*
+		 * This is just a warning so a loop-through-resultset will not abort
+		 * if one backend terminated on its own during the run.
+		 */
+		ereport(WARNING,
+				(errmsg("PID %d is not a PostgreSQL server process", pid)));
+		return false;
+	}
+
+	if (SendProcSignal(pid, reason, backendId) < 0)
+	{
+		/* Again, just a warning to allow loops */
+		ereport(WARNING,
+				(errmsg("could not send signal to process %d: %m", pid)));
+		return false;
+	}
+
+	return true;
+}
+
 /*
  * BackendPidGetProc -- get a backend's PGPROC given its PID
  *
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index 4add219553..e3b4f9943d 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -145,51 +145,8 @@ Datum
 pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
 {
 	int			pid = PG_GETARG_INT32(0);
-	PGPROC	   *proc;
-	BackendId	backendId = InvalidBackendId;
+	bool		result;
 
-	proc = BackendPidGetProc(pid);
-
-	/*
-	 * 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)
-	{
-		/*
-		 * This is just a warning so a loop-through-resultset will not abort
-		 * if one backend terminated on its own during the run.
-		 */
-		ereport(WARNING,
-				(errmsg("PID %d is not a PostgreSQL server process", pid)));
-		PG_RETURN_BOOL(false);
-	}
-
-	if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, backendId) < 0)
-	{
-		/* Again, just a warning to allow loops */
-		ereport(WARNING,
-				(errmsg("could not send signal to process %d: %m", pid)));
-		PG_RETURN_BOOL(false);
-	}
-
-	PG_RETURN_BOOL(true);
+	result = SendProcSignalBackendOrAuxproc(pid, PROCSIG_LOG_MEMORY_CONTEXT);
+	PG_RETURN_BOOL(result);
 }
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index 9761f5374c..134b905074 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -67,6 +67,7 @@ extern PGPROC *BackendPidGetProc(int pid);
 extern PGPROC *BackendPidGetProcWithLock(int pid);
 extern int	BackendXidGetPid(TransactionId xid);
 extern bool IsBackendPid(int pid);
+extern bool SendProcSignalBackendOrAuxproc(int pid, ProcSignalReason reason);
 
 extern VirtualTransactionId *GetCurrentVirtualXIDs(TransactionId limitXmin,
 												   bool excludeXmin0, bool allDbs, int excludeVacuum,
-- 
2.34.1

v22-0002-Add-function-to-log-the-backtrace-of-the-specifi.patchapplication/octet-stream; name=v22-0002-Add-function-to-log-the-backtrace-of-the-specifi.patchDownload
From b1c557adfae2fa3f798425f10d85e8da30168068 Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Wed, 30 Nov 2022 05:35:47 +0000
Subject: [PATCH v22] Add function to log the backtrace of the specified
 postgres process

This commit adds pg_log_backtrace() function that requests to log
the backtrace to console i.e. stderr of the specified backend or
auxiliary process except logger and statistic collector.

Only superusers are allowed to request to log the backtrace which
is safe from a security standpoint because the backtrace might
contain internal details.

Bump catalog version.
---
 contrib/pg_prewarm/autoprewarm.c         |  2 +
 src/backend/catalog/system_functions.sql |  2 +
 src/backend/postmaster/autovacuum.c      |  4 ++
 src/backend/postmaster/bgworker.c        |  2 +
 src/backend/postmaster/bgwriter.c        |  2 +
 src/backend/postmaster/checkpointer.c    |  2 +
 src/backend/postmaster/pgarch.c          |  2 +
 src/backend/postmaster/startup.c         |  2 +
 src/backend/postmaster/walwriter.c       |  2 +
 src/backend/replication/walreceiver.c    |  2 +
 src/backend/replication/walsender.c      |  2 +
 src/backend/storage/ipc/procsignal.c     | 87 ++++++++++++++++++++++++
 src/backend/storage/ipc/signalfuncs.c    | 26 +++++++
 src/backend/tcop/postgres.c              |  2 +
 src/include/catalog/pg_proc.dat          |  5 ++
 src/include/storage/procsignal.h         |  2 +
 16 files changed, 146 insertions(+)

diff --git a/contrib/pg_prewarm/autoprewarm.c b/contrib/pg_prewarm/autoprewarm.c
index d02a6a1ba0..17911351af 100644
--- a/contrib/pg_prewarm/autoprewarm.c
+++ b/contrib/pg_prewarm/autoprewarm.c
@@ -177,6 +177,8 @@ autoprewarm_main(Datum main_arg)
 	pqsignal(SIGUSR1, procsignal_sigusr1_handler);
 	BackgroundWorkerUnblockSignals();
 
+	LoadBacktraceFunctions();
+
 	/* Create (if necessary) and attach to our shared memory area. */
 	if (apw_init_shmem())
 		first_time = false;
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 52517a6531..8bcfcda02f 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -739,6 +739,8 @@ REVOKE EXECUTE ON FUNCTION pg_ls_logicalmapdir() FROM PUBLIC;
 
 REVOKE EXECUTE ON FUNCTION pg_ls_replslotdir(text) FROM PUBLIC;
 
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer) FROM PUBLIC;
+
 --
 -- We also set up some things as accessible to standard roles.
 --
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 601834d4b4..95bc9f4934 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -463,6 +463,8 @@ AutoVacLauncherMain(int argc, char *argv[])
 	pqsignal(SIGFPE, FloatExceptionHandler);
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * Create a per-backend PGPROC struct in shared memory, except in the
 	 * EXEC_BACKEND case where this was done in SubPostmasterMain. We must do
@@ -1541,6 +1543,8 @@ AutoVacWorkerMain(int argc, char *argv[])
 	pqsignal(SIGFPE, FloatExceptionHandler);
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * Create a per-backend PGPROC struct in shared memory, except in the
 	 * EXEC_BACKEND case where this was done in SubPostmasterMain. We must do
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 0d72de24b0..69b58afb81 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -788,6 +788,8 @@ StartBackgroundWorker(void)
 	pqsignal(SIGUSR2, SIG_IGN);
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * If an exception is encountered, processing resumes here.
 	 *
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index 91e6f6ea18..76e74a44c3 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -112,6 +112,8 @@ BackgroundWriterMain(void)
 	 */
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * We just started, assume there has been either a shutdown or
 	 * end-of-recovery snapshot.
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 5fc076fc14..dfa2d3f58d 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -207,6 +207,8 @@ CheckpointerMain(void)
 	 */
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * Initialize so that first time-driven event happens at the correct time.
 	 */
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index fffb6a599c..d307a842ef 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -229,6 +229,8 @@ PgArchiverMain(void)
 	/* Unblock signals (they were blocked when the postmaster forked us) */
 	PG_SETMASK(&UnBlockSig);
 
+	LoadBacktraceFunctions();
+
 	/* We shouldn't be launched unnecessarily. */
 	Assert(XLogArchivingActive());
 
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index f99186eab7..cddc545a38 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -261,6 +261,8 @@ StartupProcessMain(void)
 	 */
 	PG_SETMASK(&UnBlockSig);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * Do what we came for.
 	 */
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index beb46dcb55..0b7404d836 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -115,6 +115,8 @@ WalWriterMain(void)
 	 */
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * Create a memory context that we will do all our work in.  We do this so
 	 * that we can reset the context during error recovery and thereby avoid
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index ad383dbcaa..a75fba13b0 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -288,6 +288,8 @@ WalReceiverMain(void)
 	/* Reset some signals that are accepted by postmaster but not here */
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/* Load the libpq-specific functions */
 	load_file("libpqwalreceiver", false);
 	if (WalReceiverFunctions == NULL)
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index c11bb3716f..98facff929 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -3245,6 +3245,8 @@ WalSndSignals(void)
 
 	/* Reset some signals that are accepted by postmaster but not here */
 	pqsignal(SIGCHLD, SIG_DFL);
+
+	LoadBacktraceFunctions();
 }
 
 /* Report shared-memory space needed by WalSndShmemInit */
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index 7767657f27..59b986cfc8 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -16,6 +16,9 @@
 
 #include <signal.h>
 #include <unistd.h>
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
 
 #include "access/parallel.h"
 #include "port/pg_bitutils.h"
@@ -96,6 +99,10 @@ typedef struct
 #define BARRIER_CLEAR_BIT(flags, type) \
 	((flags) &= ~(((uint32) 1) << (uint32) (type)))
 
+#ifdef HAVE_BACKTRACE_SYMBOLS
+static bool	backtrace_functions_loaded = false;
+#endif
+
 static ProcSignalHeader *ProcSignal = NULL;
 static ProcSignalSlot *MyProcSignalSlot = NULL;
 
@@ -608,6 +615,76 @@ ResetProcSignalBarrierBits(uint32 flags)
 	InterruptPending = true;
 }
 
+/*
+ * HandleLogBacktraceInterrupt - Handle receipt of an interrupt requesting to
+ * log a backtrace.
+ *
+ * We capture the backtrace within this signal handler and emit to stderr. Note
+ * that we ensured the backtrace-related functions are signal-safe, see
+ * LoadBacktraceFunctions() for more details.
+ *
+ * Emitting backtrace to stderr as opposed to writing to server log has an
+ * advantage - we don't need to allocate any dynamic memory while capturing
+ * backtrace which makes the signal handler safe.
+ */
+static void
+HandleLogBacktraceInterrupt(void)
+{
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	void	   *buf[100];
+	int			nframes;
+
+	/* Quickly exit if backtrace-related functions aren't loaded. */
+	if (!backtrace_functions_loaded)
+		return;
+
+	nframes = backtrace(buf, lengthof(buf));
+
+	write_stderr("logging current backtrace of process with PID %d:\n",
+				 MyProcPid);
+	backtrace_symbols_fd(buf, nframes, fileno(stderr));
+#endif
+}
+
+/*
+ * LoadBacktraceFunctions - call a backtrace-related function to ensure the
+ * shared library implementing them is loaded beforehand.
+ *
+ * Any backtrace-related functions when called for the first time dynamically
+ * loads the shared library, which usually triggers a call to malloc, making
+ * them unsafe to use in signal handlers.
+ *
+ * This functions is an attempt to make backtrace-related functions signal
+ * safe.
+ *
+ * NOTE: This function is supposed to be called in the early life of a process,
+ * preferably after SIGUSR1 handler is setup and before the backtrace-related
+ * functions are used in signal handlers. It is not supposed to be called from
+ * within a signal handler.
+ */
+void
+LoadBacktraceFunctions(void)
+{
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	void	   *buf[100];
+
+	/*
+	 * XXX: It is a bit of overkill to check if the shared library implementing
+	 * backtrace-related functions is loaded already. Instead, we go ahead and
+	 * call one function.
+	 */
+
+	/*
+	 * It is enough to call any one backtrace-related function to ensure that
+	 * the corresponding shared library is dynamically loaded if not done
+	 * already.
+	 */
+	backtrace(buf, lengthof(buf));
+
+	backtrace_functions_loaded = true;
+#endif
+}
+
 /*
  * CheckProcSignal - check to see if a particular reason has been
  * signaled, and clear the signal flag.  Should be called after receiving
@@ -657,6 +734,16 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_LOG_MEMORY_CONTEXT))
 		HandleLogMemoryContextInterrupt();
 
+	/*
+	 * XXX: Since the log backtrace signal handler itself does the required
+	 * job, returning without setting the latch may be a good idea here.
+	 * However, it is better not to deviate from the tradition of a signal
+	 * handler setting the latch to wake up the processes that are waiting on
+	 * it.
+	 */
+	if (CheckProcSignal(PROCSIG_LOG_BACKTRACE))
+		HandleLogBacktraceInterrupt();
+
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
 
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index 6e310b14eb..5bb68be9f6 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -303,3 +303,29 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_log_backtrace - signal a backend or an auxiliary process to log its
+ * current backtrace to stderr.
+ *
+ * By default, only superusers are allowed to request to log the backtrace
+ * which is safe from a security standpoint because the backtrace might contain
+ * internal details. However, a superuser can grant the execute permission to
+ * anyone, if it wishes.
+ */
+Datum
+pg_log_backtrace(PG_FUNCTION_ARGS)
+{
+	int			pid = PG_GETARG_INT32(0);
+	bool		result;
+
+#ifndef HAVE_BACKTRACE_SYMBOLS
+	ereport(WARNING,
+			errmsg("backtrace generation is not supported by this installation"),
+			errhint("You need to rebuild PostgreSQL using a library containing backtrace_symbols."));
+	PG_RETURN_BOOL(false);
+#endif
+
+	result = SendProcSignalBackendOrAuxproc(pid, PROCSIG_LOG_BACKTRACE);
+	PG_RETURN_BOOL(result);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 3082093d1e..4bc1621733 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -4109,6 +4109,8 @@ PostgresMain(const char *dbname, const char *username)
 		 */
 		pqsignal(SIGCHLD, SIG_DFL); /* system() requires this on some
 									 * platforms */
+
+		LoadBacktraceFunctions();
 	}
 
 	/* Early initialization */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index f9301b2627..04c8b011f8 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11854,4 +11854,9 @@
   prorettype => 'bytea', proargtypes => 'pg_brin_minmax_multi_summary',
   prosrc => 'brin_minmax_multi_summary_send' },
 
+# function to get the backtrace of server process
+{ oid => '6105', descr => 'log backtrace of server process',
+  proname => 'pg_log_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_log_backtrace' },
+
 ]
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index ee636900f3..0ee63f7faf 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -35,6 +35,7 @@ typedef enum
 	PROCSIG_WALSND_INIT_STOPPING,	/* ask walsenders to prepare for shutdown  */
 	PROCSIG_BARRIER,			/* global barrier interrupt  */
 	PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
+	PROCSIG_LOG_BACKTRACE,		/* ask backend to log the current backtrace */
 
 	/* Recovery conflict reasons */
 	PROCSIG_RECOVERY_CONFLICT_DATABASE,
@@ -67,5 +68,6 @@ extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
 
 extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
+extern void LoadBacktraceFunctions(void);
 
 #endif							/* PROCSIGNAL_H */
-- 
2.34.1

v22-0003-Add-documentation-and-tests-for-pg_log_backtrace.patchapplication/octet-stream; name=v22-0003-Add-documentation-and-tests-for-pg_log_backtrace.patchDownload
From a9b65bfd1f55467fb597df33c0952680473d9f87 Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Wed, 30 Nov 2022 05:43:06 +0000
Subject: [PATCH v22] Add documentation and tests for pg_log_backtrace()

---
 doc/src/sgml/func.sgml                    | 76 +++++++++++++++++++++++
 src/test/regress/expected/backtrace.out   | 49 +++++++++++++++
 src/test/regress/expected/backtrace_1.out | 55 ++++++++++++++++
 src/test/regress/parallel_schedule        |  2 +-
 src/test/regress/sql/backtrace.sql        | 33 ++++++++++
 5 files changed, 214 insertions(+), 1 deletion(-)
 create mode 100644 src/test/regress/expected/backtrace.out
 create mode 100644 src/test/regress/expected/backtrace_1.out
 create mode 100644 src/test/regress/sql/backtrace.sql

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 22204f16c2..f5baae952c 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25482,6 +25482,27 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_log_backtrace</primary>
+        </indexterm>
+        <function>pg_log_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Requests to log the backtrace of the backend with the specified
+        process ID. This function can send the request to backends and
+        auxiliary processes except the logger and statistics collector.
+        The backtraces will be logged to <systemitem>stderr</systemitem>.
+        Typically, a backtrace identifies which function a process is
+        currently executing and it is useful for developers to diagnose
+        stuck processes and other problems. This function is supported
+        only if PostgreSQL was built with the ability to capture backtraces,
+        otherwise it will emit a warning.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -25702,6 +25723,61 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_log_backtrace</function> can be used to log the backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace
+------------------
+ t
+(1 row)
+</programlisting>
+The backtrace will be logged as specified by the logging configuration.
+For example:
+<screen>
+logging current backtrace of process with PID 3499242:
+postgres: ubuntu postgres [local] INSERT(+0x61a355)[0x5559b94de355]
+postgres: ubuntu postgres [local] INSERT(procsignal_sigusr1_handler+0x9e)[0x5559b94de4ef]
+/lib/x86_64-linux-gnu/libc.so.6(+0x42520)[0x7f7e9c0e8520]
+postgres: ubuntu postgres [local] INSERT(+0x868004)[0x5559b972c004]
+postgres: ubuntu postgres [local] INSERT(pfree+0x1c)[0x5559b972e445]
+postgres: ubuntu postgres [local] INSERT(heap_free_minimal_tuple+0x1c)[0x5559b8fa7b48]
+postgres: ubuntu postgres [local] INSERT(+0x8826d6)[0x5559b97466d6]
+postgres: ubuntu postgres [local] INSERT(+0x881126)[0x5559b9745126]
+postgres: ubuntu postgres [local] INSERT(tuplestore_putvalues+0x83)[0x5559b9744ed5]
+postgres: ubuntu postgres [local] INSERT(ExecMakeTableFunctionResult+0x68b)[0x5559b9284295]
+postgres: ubuntu postgres [local] INSERT(+0x3dd14a)[0x5559b92a114a]
+postgres: ubuntu postgres [local] INSERT(+0x3c1a5c)[0x5559b9285a5c]
+postgres: ubuntu postgres [local] INSERT(ExecScan+0x77)[0x5559b9285ad5]
+postgres: ubuntu postgres [local] INSERT(+0x3dd4f4)[0x5559b92a14f4]
+postgres: ubuntu postgres [local] INSERT(+0x3bd485)[0x5559b9281485]
+postgres: ubuntu postgres [local] INSERT(+0x3f96f0)[0x5559b92bd6f0]
+postgres: ubuntu postgres [local] INSERT(+0x3ff25e)[0x5559b92c325e]
+postgres: ubuntu postgres [local] INSERT(+0x3bd485)[0x5559b9281485]
+postgres: ubuntu postgres [local] INSERT(+0x3b07a4)[0x5559b92747a4]
+postgres: ubuntu postgres [local] INSERT(+0x3b3594)[0x5559b9277594]
+postgres: ubuntu postgres [local] INSERT(standard_ExecutorRun+0x1f4)[0x5559b9274e8c]
+postgres: ubuntu postgres [local] INSERT(ExecutorRun+0x5d)[0x5559b9274c95]
+postgres: ubuntu postgres [local] INSERT(+0x64ee3a)[0x5559b9512e3a]
+postgres: ubuntu postgres [local] INSERT(+0x6509c9)[0x5559b95149c9]
+postgres: ubuntu postgres [local] INSERT(PortalRun+0x378)[0x5559b9513ec0]
+postgres: ubuntu postgres [local] INSERT(+0x648c56)[0x5559b950cc56]
+postgres: ubuntu postgres [local] INSERT(PostgresMain+0x80c)[0x5559b9511c46]
+postgres: ubuntu postgres [local] INSERT(+0x576647)[0x5559b943a647]
+postgres: ubuntu postgres [local] INSERT(+0x575ed3)[0x5559b9439ed3]
+postgres: ubuntu postgres [local] INSERT(+0x572388)[0x5559b9436388]
+postgres: ubuntu postgres [local] INSERT(PostmasterMain+0x14b3)[0x5559b9435ae5]
+postgres: ubuntu postgres [local] INSERT(+0x43b563)[0x5559b92ff563]
+/lib/x86_64-linux-gnu/libc.so.6(+0x29d90)[0x7f7e9c0cfd90]
+/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80)[0x7f7e9c0cfe40]
+postgres: ubuntu postgres [local] INSERT(_start+0x25)[0x5559b8f8f005]
+</screen>
+    One can obtain the file name and line number from the logged details by using
+    gdb/addr2line in linux platforms (users must ensure gdb/addr2line is
+    already installed).
+   </para>
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/test/regress/expected/backtrace.out b/src/test/regress/expected/backtrace.out
new file mode 100644
index 0000000000..5341a6adfe
--- /dev/null
+++ b/src/test/regress/expected/backtrace.out
@@ -0,0 +1,49 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged to stderr and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/expected/backtrace_1.out b/src/test/regress/expected/backtrace_1.out
new file mode 100644
index 0000000000..899f330224
--- /dev/null
+++ b/src/test/regress/expected/backtrace_1.out
@@ -0,0 +1,55 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged to stderr and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 9a139f1e24..c0f388300c 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -86,7 +86,7 @@ test: brin_bloom brin_multi
 # psql depends on create_am
 # amutils depends on geometry, create_index_spgist, hash_index, brin
 # ----------
-test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role
+test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role backtrace
 
 # collate.*.utf8 tests cannot be run in parallel with each other
 test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8
diff --git a/src/test/regress/sql/backtrace.sql b/src/test/regress/sql/backtrace.sql
new file mode 100644
index 0000000000..674e41c998
--- /dev/null
+++ b/src/test/regress/sql/backtrace.sql
@@ -0,0 +1,33 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged to stderr and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+
+SELECT pg_log_backtrace(pg_backend_pid());
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+
+CREATE ROLE regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+RESET ROLE;
+
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+
+DROP ROLE regress_log_backtrace;
-- 
2.34.1

#111Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: Bharath Rupireddy (#110)
3 attachment(s)
Re: Printing backtrace of postgres processes

On Wed, Nov 30, 2022 at 11:43 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

I'm attaching the v22 patch set for further review.

Needed a rebase due to 216a784829c2c5f03ab0c43e009126cbb819e9b2.
Attaching v23 patch set for further review.

--
Bharath Rupireddy
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com

Attachments:

v23-0001-Move-sending-multiplexed-SIGUSR1-signal-code-to-.patchapplication/x-patch; name=v23-0001-Move-sending-multiplexed-SIGUSR1-signal-code-to-.patchDownload
From ee5c26f0d4e2e211166250857ea42d30a3666709 Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Wed, 11 Jan 2023 13:32:13 +0000
Subject: [PATCH v23] Move sending multiplexed-SIGUSR1 signal code to a
 function

Add a new function hosting the common code for sending
multiplexed-SIGUSR1 signal to a backend process. This function
will also be used as-is by an upcoming commit reducing the code
duplication.
---
 src/backend/storage/ipc/procarray.c | 60 +++++++++++++++++++++++++++++
 src/backend/utils/adt/mcxtfuncs.c   | 49 ++---------------------
 src/include/storage/procarray.h     |  1 +
 3 files changed, 64 insertions(+), 46 deletions(-)

diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 4340bf9641..5681f0d3b0 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -3151,6 +3151,66 @@ HaveVirtualXIDsDelayingChkpt(VirtualTransactionId *vxids, int nvxids, int type)
 	return result;
 }
 
+/*
+ * SendProcSignalBackendOrAuxproc -- check if the process with given pid is a
+ * backend or an auxiliary process and send it the SIGUSR1 signal for a given
+ * reason.
+ *
+ * Returns true if sending the signal was successful, false otherwise.
+ */
+bool
+SendProcSignalBackendOrAuxproc(int pid, ProcSignalReason reason)
+{
+	PGPROC	   *proc;
+	BackendId	backendId = InvalidBackendId;
+
+	proc = BackendPidGetProc(pid);
+
+	/*
+	 * 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 or a long running process,
+	 * that it might end on its own first and its memory contexts are not
+	 * logged or backtrace not logged is not a problem.
+	 */
+	if (proc == NULL)
+	{
+		/*
+		 * This is just a warning so a loop-through-resultset will not abort
+		 * if one backend terminated on its own during the run.
+		 */
+		ereport(WARNING,
+				(errmsg("PID %d is not a PostgreSQL server process", pid)));
+		return false;
+	}
+
+	if (SendProcSignal(pid, reason, backendId) < 0)
+	{
+		/* Again, just a warning to allow loops */
+		ereport(WARNING,
+				(errmsg("could not send signal to process %d: %m", pid)));
+		return false;
+	}
+
+	return true;
+}
+
 /*
  * BackendPidGetProc -- get a backend's PGPROC given its PID
  *
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index 92ca5b2f72..7b17afc2ff 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -145,51 +145,8 @@ Datum
 pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
 {
 	int			pid = PG_GETARG_INT32(0);
-	PGPROC	   *proc;
-	BackendId	backendId = InvalidBackendId;
+	bool		result;
 
-	proc = BackendPidGetProc(pid);
-
-	/*
-	 * 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)
-	{
-		/*
-		 * This is just a warning so a loop-through-resultset will not abort
-		 * if one backend terminated on its own during the run.
-		 */
-		ereport(WARNING,
-				(errmsg("PID %d is not a PostgreSQL server process", pid)));
-		PG_RETURN_BOOL(false);
-	}
-
-	if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, backendId) < 0)
-	{
-		/* Again, just a warning to allow loops */
-		ereport(WARNING,
-				(errmsg("could not send signal to process %d: %m", pid)));
-		PG_RETURN_BOOL(false);
-	}
-
-	PG_RETURN_BOOL(true);
+	result = SendProcSignalBackendOrAuxproc(pid, PROCSIG_LOG_MEMORY_CONTEXT);
+	PG_RETURN_BOOL(result);
 }
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index d8cae3ce1c..2b470ddadf 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -68,6 +68,7 @@ extern PGPROC *BackendPidGetProc(int pid);
 extern PGPROC *BackendPidGetProcWithLock(int pid);
 extern int	BackendXidGetPid(TransactionId xid);
 extern bool IsBackendPid(int pid);
+extern bool SendProcSignalBackendOrAuxproc(int pid, ProcSignalReason reason);
 
 extern VirtualTransactionId *GetCurrentVirtualXIDs(TransactionId limitXmin,
 												   bool excludeXmin0, bool allDbs, int excludeVacuum,
-- 
2.34.1

v23-0002-Add-function-to-log-the-backtrace-of-the-specifi.patchapplication/x-patch; name=v23-0002-Add-function-to-log-the-backtrace-of-the-specifi.patchDownload
From 4962f6c9d889d94d4bac49cd94e04fb8562a6f74 Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Wed, 11 Jan 2023 13:34:02 +0000
Subject: [PATCH v23] Add function to log the backtrace of the specified
 postgres process

This commit adds pg_log_backtrace() function that requests to log
the backtrace to console i.e. stderr of the specified backend or
auxiliary process except logger and statistic collector.

Only superusers are allowed to request to log the backtrace which
is safe from a security standpoint because the backtrace might
contain internal details.

Bump catalog version.
---
 contrib/pg_prewarm/autoprewarm.c         |  2 +
 src/backend/catalog/system_functions.sql |  2 +
 src/backend/postmaster/autovacuum.c      |  4 ++
 src/backend/postmaster/bgworker.c        |  2 +
 src/backend/postmaster/bgwriter.c        |  2 +
 src/backend/postmaster/checkpointer.c    |  2 +
 src/backend/postmaster/pgarch.c          |  2 +
 src/backend/postmaster/startup.c         |  2 +
 src/backend/postmaster/walwriter.c       |  2 +
 src/backend/replication/walreceiver.c    |  2 +
 src/backend/replication/walsender.c      |  2 +
 src/backend/storage/ipc/procsignal.c     | 87 ++++++++++++++++++++++++
 src/backend/storage/ipc/signalfuncs.c    | 26 +++++++
 src/backend/tcop/postgres.c              |  2 +
 src/include/catalog/pg_proc.dat          |  5 ++
 src/include/storage/procsignal.h         |  2 +
 16 files changed, 146 insertions(+)

diff --git a/contrib/pg_prewarm/autoprewarm.c b/contrib/pg_prewarm/autoprewarm.c
index 93835449c0..636333bda1 100644
--- a/contrib/pg_prewarm/autoprewarm.c
+++ b/contrib/pg_prewarm/autoprewarm.c
@@ -177,6 +177,8 @@ autoprewarm_main(Datum main_arg)
 	pqsignal(SIGUSR1, procsignal_sigusr1_handler);
 	BackgroundWorkerUnblockSignals();
 
+	LoadBacktraceFunctions();
+
 	/* Create (if necessary) and attach to our shared memory area. */
 	if (apw_init_shmem())
 		first_time = false;
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 83ca893444..67fe4501f3 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -746,6 +746,8 @@ REVOKE EXECUTE ON FUNCTION pg_ls_logicalmapdir() FROM PUBLIC;
 
 REVOKE EXECUTE ON FUNCTION pg_ls_replslotdir(text) FROM PUBLIC;
 
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer) FROM PUBLIC;
+
 --
 -- We also set up some things as accessible to standard roles.
 --
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index f5ea381c53..428770262f 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -463,6 +463,8 @@ AutoVacLauncherMain(int argc, char *argv[])
 	pqsignal(SIGFPE, FloatExceptionHandler);
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * Create a per-backend PGPROC struct in shared memory, except in the
 	 * EXEC_BACKEND case where this was done in SubPostmasterMain. We must do
@@ -1541,6 +1543,8 @@ AutoVacWorkerMain(int argc, char *argv[])
 	pqsignal(SIGFPE, FloatExceptionHandler);
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * Create a per-backend PGPROC struct in shared memory, except in the
 	 * EXEC_BACKEND case where this was done in SubPostmasterMain. We must do
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index e7a4a7136a..ad84ba263b 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -791,6 +791,8 @@ StartBackgroundWorker(void)
 	pqsignal(SIGUSR2, SIG_IGN);
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * If an exception is encountered, processing resumes here.
 	 *
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index 69667f0eb4..20dc6c49ff 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -112,6 +112,8 @@ BackgroundWriterMain(void)
 	 */
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * We just started, assume there has been either a shutdown or
 	 * end-of-recovery snapshot.
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index de0bbbfa79..1d7e7b3e6c 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -207,6 +207,8 @@ CheckpointerMain(void)
 	 */
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * Initialize so that first time-driven event happens at the correct time.
 	 */
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 8ecdb9ca23..2e2f186a15 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -229,6 +229,8 @@ PgArchiverMain(void)
 	/* Unblock signals (they were blocked when the postmaster forked us) */
 	PG_SETMASK(&UnBlockSig);
 
+	LoadBacktraceFunctions();
+
 	/* We shouldn't be launched unnecessarily. */
 	Assert(XLogArchivingActive());
 
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index 8786186898..1c16ae62eb 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -261,6 +261,8 @@ StartupProcessMain(void)
 	 */
 	PG_SETMASK(&UnBlockSig);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * Do what we came for.
 	 */
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 3113e8fbdd..e444a58b51 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -115,6 +115,8 @@ WalWriterMain(void)
 	 */
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * Create a memory context that we will do all our work in.  We do this so
 	 * that we can reset the context during error recovery and thereby avoid
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 3876c0188d..55cc235875 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -288,6 +288,8 @@ WalReceiverMain(void)
 	/* Reset some signals that are accepted by postmaster but not here */
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/* Load the libpq-specific functions */
 	load_file("libpqwalreceiver", false);
 	if (WalReceiverFunctions == NULL)
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 015ae2995d..c914b43f1c 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -3245,6 +3245,8 @@ WalSndSignals(void)
 
 	/* Reset some signals that are accepted by postmaster but not here */
 	pqsignal(SIGCHLD, SIG_DFL);
+
+	LoadBacktraceFunctions();
 }
 
 /* Report shared-memory space needed by WalSndShmemInit */
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index 395b2cf690..37e3553fad 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -16,6 +16,9 @@
 
 #include <signal.h>
 #include <unistd.h>
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
 
 #include "access/parallel.h"
 #include "port/pg_bitutils.h"
@@ -97,6 +100,10 @@ typedef struct
 #define BARRIER_CLEAR_BIT(flags, type) \
 	((flags) &= ~(((uint32) 1) << (uint32) (type)))
 
+#ifdef HAVE_BACKTRACE_SYMBOLS
+static bool	backtrace_functions_loaded = false;
+#endif
+
 static ProcSignalHeader *ProcSignal = NULL;
 static ProcSignalSlot *MyProcSignalSlot = NULL;
 
@@ -609,6 +616,76 @@ ResetProcSignalBarrierBits(uint32 flags)
 	InterruptPending = true;
 }
 
+/*
+ * HandleLogBacktraceInterrupt - Handle receipt of an interrupt requesting to
+ * log a backtrace.
+ *
+ * We capture the backtrace within this signal handler and emit to stderr. Note
+ * that we ensured the backtrace-related functions are signal-safe, see
+ * LoadBacktraceFunctions() for more details.
+ *
+ * Emitting backtrace to stderr as opposed to writing to server log has an
+ * advantage - we don't need to allocate any dynamic memory while capturing
+ * backtrace which makes the signal handler safe.
+ */
+static void
+HandleLogBacktraceInterrupt(void)
+{
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	void	   *buf[100];
+	int			nframes;
+
+	/* Quickly exit if backtrace-related functions aren't loaded. */
+	if (!backtrace_functions_loaded)
+		return;
+
+	nframes = backtrace(buf, lengthof(buf));
+
+	write_stderr("logging current backtrace of process with PID %d:\n",
+				 MyProcPid);
+	backtrace_symbols_fd(buf, nframes, fileno(stderr));
+#endif
+}
+
+/*
+ * LoadBacktraceFunctions - call a backtrace-related function to ensure the
+ * shared library implementing them is loaded beforehand.
+ *
+ * Any backtrace-related functions when called for the first time dynamically
+ * loads the shared library, which usually triggers a call to malloc, making
+ * them unsafe to use in signal handlers.
+ *
+ * This functions is an attempt to make backtrace-related functions signal
+ * safe.
+ *
+ * NOTE: This function is supposed to be called in the early life of a process,
+ * preferably after SIGUSR1 handler is setup and before the backtrace-related
+ * functions are used in signal handlers. It is not supposed to be called from
+ * within a signal handler.
+ */
+void
+LoadBacktraceFunctions(void)
+{
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	void	   *buf[100];
+
+	/*
+	 * XXX: It is a bit of overkill to check if the shared library implementing
+	 * backtrace-related functions is loaded already. Instead, we go ahead and
+	 * call one function.
+	 */
+
+	/*
+	 * It is enough to call any one backtrace-related function to ensure that
+	 * the corresponding shared library is dynamically loaded if not done
+	 * already.
+	 */
+	backtrace(buf, lengthof(buf));
+
+	backtrace_functions_loaded = true;
+#endif
+}
+
 /*
  * CheckProcSignal - check to see if a particular reason has been
  * signaled, and clear the signal flag.  Should be called after receiving
@@ -661,6 +738,16 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_PARALLEL_APPLY_MESSAGE))
 		HandleParallelApplyMessageInterrupt();
 
+	/*
+	 * XXX: Since the log backtrace signal handler itself does the required
+	 * job, returning without setting the latch may be a good idea here.
+	 * However, it is better not to deviate from the tradition of a signal
+	 * handler setting the latch to wake up the processes that are waiting on
+	 * it.
+	 */
+	if (CheckProcSignal(PROCSIG_LOG_BACKTRACE))
+		HandleLogBacktraceInterrupt();
+
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
 
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index bc93ab5b52..fbe37cee9d 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -303,3 +303,29 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_log_backtrace - signal a backend or an auxiliary process to log its
+ * current backtrace to stderr.
+ *
+ * By default, only superusers are allowed to request to log the backtrace
+ * which is safe from a security standpoint because the backtrace might contain
+ * internal details. However, a superuser can grant the execute permission to
+ * anyone, if it wishes.
+ */
+Datum
+pg_log_backtrace(PG_FUNCTION_ARGS)
+{
+	int			pid = PG_GETARG_INT32(0);
+	bool		result;
+
+#ifndef HAVE_BACKTRACE_SYMBOLS
+	ereport(WARNING,
+			errmsg("backtrace generation is not supported by this installation"),
+			errhint("You need to rebuild PostgreSQL using a library containing backtrace_symbols."));
+	PG_RETURN_BOOL(false);
+#endif
+
+	result = SendProcSignalBackendOrAuxproc(pid, PROCSIG_LOG_BACKTRACE);
+	PG_RETURN_BOOL(result);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 224ab290af..cb58ba4d0a 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -4124,6 +4124,8 @@ PostgresMain(const char *dbname, const char *username)
 		 */
 		pqsignal(SIGCHLD, SIG_DFL); /* system() requires this on some
 									 * platforms */
+
+		LoadBacktraceFunctions();
 	}
 
 	/* Early initialization */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 3810de7b22..5d5f9d361a 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11891,4 +11891,9 @@
   prorettype => 'bytea', proargtypes => 'pg_brin_minmax_multi_summary',
   prosrc => 'brin_minmax_multi_summary_send' },
 
+# function to get the backtrace of server process
+{ oid => '6105', descr => 'log backtrace of server process',
+  proname => 'pg_log_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_log_backtrace' },
+
 ]
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index 905af2231b..2782a9cd2c 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -36,6 +36,7 @@ typedef enum
 	PROCSIG_BARRIER,			/* global barrier interrupt  */
 	PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
 	PROCSIG_PARALLEL_APPLY_MESSAGE, /* Message from parallel apply workers */
+	PROCSIG_LOG_BACKTRACE,          /* ask backend to log the current backtrace */
 
 	/* Recovery conflict reasons */
 	PROCSIG_RECOVERY_CONFLICT_DATABASE,
@@ -68,5 +69,6 @@ extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
 
 extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
+extern void LoadBacktraceFunctions(void);
 
 #endif							/* PROCSIGNAL_H */
-- 
2.34.1

v23-0003-Add-documentation-and-tests-for-pg_log_backtrace.patchapplication/x-patch; name=v23-0003-Add-documentation-and-tests-for-pg_log_backtrace.patchDownload
From bb65cc7b8e817209ace4aa4a26ca454ab81966b9 Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Wed, 11 Jan 2023 13:35:38 +0000
Subject: [PATCH v23] Add documentation and tests for pg_log_backtrace()

---
 doc/src/sgml/func.sgml                    | 76 +++++++++++++++++++++++
 src/test/regress/expected/backtrace.out   | 49 +++++++++++++++
 src/test/regress/expected/backtrace_1.out | 55 ++++++++++++++++
 src/test/regress/parallel_schedule        |  2 +-
 src/test/regress/sql/backtrace.sql        | 33 ++++++++++
 5 files changed, 214 insertions(+), 1 deletion(-)
 create mode 100644 src/test/regress/expected/backtrace.out
 create mode 100644 src/test/regress/expected/backtrace_1.out
 create mode 100644 src/test/regress/sql/backtrace.sql

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index b8dac9ef46..703457ca7a 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25651,6 +25651,27 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_log_backtrace</primary>
+        </indexterm>
+        <function>pg_log_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Requests to log the backtrace of the backend with the specified
+        process ID. This function can send the request to backends and
+        auxiliary processes except the logger and statistics collector.
+        The backtraces will be logged to <systemitem>stderr</systemitem>.
+        Typically, a backtrace identifies which function a process is
+        currently executing and it is useful for developers to diagnose
+        stuck processes and other problems. This function is supported
+        only if PostgreSQL was built with the ability to capture backtraces,
+        otherwise it will emit a warning.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -25871,6 +25892,61 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_log_backtrace</function> can be used to log the backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace
+------------------
+ t
+(1 row)
+</programlisting>
+The backtrace will be logged as specified by the logging configuration.
+For example:
+<screen>
+logging current backtrace of process with PID 3499242:
+postgres: ubuntu postgres [local] INSERT(+0x61a355)[0x5559b94de355]
+postgres: ubuntu postgres [local] INSERT(procsignal_sigusr1_handler+0x9e)[0x5559b94de4ef]
+/lib/x86_64-linux-gnu/libc.so.6(+0x42520)[0x7f7e9c0e8520]
+postgres: ubuntu postgres [local] INSERT(+0x868004)[0x5559b972c004]
+postgres: ubuntu postgres [local] INSERT(pfree+0x1c)[0x5559b972e445]
+postgres: ubuntu postgres [local] INSERT(heap_free_minimal_tuple+0x1c)[0x5559b8fa7b48]
+postgres: ubuntu postgres [local] INSERT(+0x8826d6)[0x5559b97466d6]
+postgres: ubuntu postgres [local] INSERT(+0x881126)[0x5559b9745126]
+postgres: ubuntu postgres [local] INSERT(tuplestore_putvalues+0x83)[0x5559b9744ed5]
+postgres: ubuntu postgres [local] INSERT(ExecMakeTableFunctionResult+0x68b)[0x5559b9284295]
+postgres: ubuntu postgres [local] INSERT(+0x3dd14a)[0x5559b92a114a]
+postgres: ubuntu postgres [local] INSERT(+0x3c1a5c)[0x5559b9285a5c]
+postgres: ubuntu postgres [local] INSERT(ExecScan+0x77)[0x5559b9285ad5]
+postgres: ubuntu postgres [local] INSERT(+0x3dd4f4)[0x5559b92a14f4]
+postgres: ubuntu postgres [local] INSERT(+0x3bd485)[0x5559b9281485]
+postgres: ubuntu postgres [local] INSERT(+0x3f96f0)[0x5559b92bd6f0]
+postgres: ubuntu postgres [local] INSERT(+0x3ff25e)[0x5559b92c325e]
+postgres: ubuntu postgres [local] INSERT(+0x3bd485)[0x5559b9281485]
+postgres: ubuntu postgres [local] INSERT(+0x3b07a4)[0x5559b92747a4]
+postgres: ubuntu postgres [local] INSERT(+0x3b3594)[0x5559b9277594]
+postgres: ubuntu postgres [local] INSERT(standard_ExecutorRun+0x1f4)[0x5559b9274e8c]
+postgres: ubuntu postgres [local] INSERT(ExecutorRun+0x5d)[0x5559b9274c95]
+postgres: ubuntu postgres [local] INSERT(+0x64ee3a)[0x5559b9512e3a]
+postgres: ubuntu postgres [local] INSERT(+0x6509c9)[0x5559b95149c9]
+postgres: ubuntu postgres [local] INSERT(PortalRun+0x378)[0x5559b9513ec0]
+postgres: ubuntu postgres [local] INSERT(+0x648c56)[0x5559b950cc56]
+postgres: ubuntu postgres [local] INSERT(PostgresMain+0x80c)[0x5559b9511c46]
+postgres: ubuntu postgres [local] INSERT(+0x576647)[0x5559b943a647]
+postgres: ubuntu postgres [local] INSERT(+0x575ed3)[0x5559b9439ed3]
+postgres: ubuntu postgres [local] INSERT(+0x572388)[0x5559b9436388]
+postgres: ubuntu postgres [local] INSERT(PostmasterMain+0x14b3)[0x5559b9435ae5]
+postgres: ubuntu postgres [local] INSERT(+0x43b563)[0x5559b92ff563]
+/lib/x86_64-linux-gnu/libc.so.6(+0x29d90)[0x7f7e9c0cfd90]
+/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80)[0x7f7e9c0cfe40]
+postgres: ubuntu postgres [local] INSERT(_start+0x25)[0x5559b8f8f005]
+</screen>
+    One can obtain the file name and line number from the logged details by using
+    gdb/addr2line in linux platforms (users must ensure gdb/addr2line is
+    already installed).
+   </para>
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/test/regress/expected/backtrace.out b/src/test/regress/expected/backtrace.out
new file mode 100644
index 0000000000..5341a6adfe
--- /dev/null
+++ b/src/test/regress/expected/backtrace.out
@@ -0,0 +1,49 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged to stderr and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/expected/backtrace_1.out b/src/test/regress/expected/backtrace_1.out
new file mode 100644
index 0000000000..899f330224
--- /dev/null
+++ b/src/test/regress/expected/backtrace_1.out
@@ -0,0 +1,55 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged to stderr and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index a930dfe48c..ab27bdcc0f 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -83,7 +83,7 @@ test: brin_bloom brin_multi
 # psql depends on create_am
 # amutils depends on geometry, create_index_spgist, hash_index, brin
 # ----------
-test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role
+test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role backtrace
 
 # collate.*.utf8 tests cannot be run in parallel with each other
 test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 collate.windows.win1252
diff --git a/src/test/regress/sql/backtrace.sql b/src/test/regress/sql/backtrace.sql
new file mode 100644
index 0000000000..674e41c998
--- /dev/null
+++ b/src/test/regress/sql/backtrace.sql
@@ -0,0 +1,33 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged to stderr and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+
+SELECT pg_log_backtrace(pg_backend_pid());
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+
+CREATE ROLE regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+RESET ROLE;
+
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+
+DROP ROLE regress_log_backtrace;
-- 
2.34.1

#112Daniel Gustafsson
daniel@yesql.se
In reply to: Bharath Rupireddy (#111)
Re: Printing backtrace of postgres processes

On 11 Jan 2023, at 15:44, Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> wrote:

On Wed, Nov 30, 2022 at 11:43 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

I'm attaching the v22 patch set for further review.

Needed a rebase due to 216a784829c2c5f03ab0c43e009126cbb819e9b2.
Attaching v23 patch set for further review.

This thread has stalled for well over 6 months with the patch going from CF to
CF. From skimming the thread it seems that a lot of the details have been
ironed out with most (all?) objections addressed. Is any committer interested
in picking this up? If not we should probably mark it returned with feedback.

--
Daniel Gustafsson

#113Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: Daniel Gustafsson (#112)
2 attachment(s)
Re: Printing backtrace of postgres processes

On Thu, Jul 20, 2023 at 8:22 PM Daniel Gustafsson <daniel@yesql.se> wrote:

On 11 Jan 2023, at 15:44, Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> wrote:

On Wed, Nov 30, 2022 at 11:43 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

I'm attaching the v22 patch set for further review.

Needed a rebase due to 216a784829c2c5f03ab0c43e009126cbb819e9b2.
Attaching v23 patch set for further review.

This thread has stalled for well over 6 months with the patch going from CF to
CF. From skimming the thread it seems that a lot of the details have been
ironed out with most (all?) objections addressed. Is any committer interested
in picking this up? If not we should probably mark it returned with feedback.

Rebase needed due to function oid clash. Picked the new OID with the
help of src/include/catalog/unused_oids. PSA v24 patch set.

--
Bharath Rupireddy
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com

Attachments:

v24-0002-Add-function-to-log-the-backtrace-of-specified-p.patchapplication/x-patch; name=v24-0002-Add-function-to-log-the-backtrace-of-specified-p.patchDownload
From 5be41719cdfa5bb5cff1cce88a50ddd11c4675dc Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Sat, 4 Nov 2023 18:11:08 +0000
Subject: [PATCH v24] Add function to log the backtrace of specified postgres
 process

This commit adds pg_log_backtrace() function that requests to log
the backtrace to console i.e. stderr of the specified backend or
auxiliary process except logger and statistic collector.

Only superusers are allowed to request to log the backtrace which
is safe from a security standpoint because the backtrace might
contain internal details.

Bump catalog version.
---
 contrib/pg_prewarm/autoprewarm.c          |  2 +
 doc/src/sgml/func.sgml                    | 76 ++++++++++++++++++++
 src/backend/catalog/system_functions.sql  |  2 +
 src/backend/postmaster/autovacuum.c       |  4 ++
 src/backend/postmaster/bgworker.c         |  2 +
 src/backend/postmaster/bgwriter.c         |  2 +
 src/backend/postmaster/checkpointer.c     |  2 +
 src/backend/postmaster/pgarch.c           |  2 +
 src/backend/postmaster/startup.c          |  2 +
 src/backend/postmaster/walwriter.c        |  2 +
 src/backend/replication/walreceiver.c     |  2 +
 src/backend/replication/walsender.c       |  2 +
 src/backend/storage/ipc/procsignal.c      | 87 +++++++++++++++++++++++
 src/backend/storage/ipc/signalfuncs.c     | 26 +++++++
 src/backend/tcop/postgres.c               |  2 +
 src/include/catalog/pg_proc.dat           |  5 ++
 src/include/storage/procsignal.h          |  2 +
 src/test/regress/expected/backtrace.out   | 49 +++++++++++++
 src/test/regress/expected/backtrace_1.out | 55 ++++++++++++++
 src/test/regress/parallel_schedule        |  2 +-
 src/test/regress/sql/backtrace.sql        | 33 +++++++++
 21 files changed, 360 insertions(+), 1 deletion(-)
 create mode 100644 src/test/regress/expected/backtrace.out
 create mode 100644 src/test/regress/expected/backtrace_1.out
 create mode 100644 src/test/regress/sql/backtrace.sql

diff --git a/contrib/pg_prewarm/autoprewarm.c b/contrib/pg_prewarm/autoprewarm.c
index d0efc9e524..bf6556285e 100644
--- a/contrib/pg_prewarm/autoprewarm.c
+++ b/contrib/pg_prewarm/autoprewarm.c
@@ -177,6 +177,8 @@ autoprewarm_main(Datum main_arg)
 	pqsignal(SIGUSR1, procsignal_sigusr1_handler);
 	BackgroundWorkerUnblockSignals();
 
+	LoadBacktraceFunctions();
+
 	/* Create (if necessary) and attach to our shared memory area. */
 	if (apw_init_shmem())
 		first_time = false;
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index a6fcac0824..915f98ba49 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25649,6 +25649,27 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_log_backtrace</primary>
+        </indexterm>
+        <function>pg_log_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Requests to log the backtrace of the backend with the specified
+        process ID. This function can send the request to backends and
+        auxiliary processes except the logger and statistics collector.
+        The backtraces will be logged to <systemitem>stderr</systemitem>.
+        Typically, a backtrace identifies which function a process is
+        currently executing and it is useful for developers to diagnose
+        stuck processes and other problems. This function is supported
+        only if PostgreSQL was built with the ability to capture backtraces,
+        otherwise it will emit a warning.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -26706,6 +26727,61 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_log_backtrace</function> can be used to log the backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace
+------------------
+ t
+(1 row)
+</programlisting>
+The backtrace will be logged as specified by the logging configuration.
+For example:
+<screen>
+logging current backtrace of process with PID 3499242:
+postgres: ubuntu postgres [local] INSERT(+0x61a355)[0x5559b94de355]
+postgres: ubuntu postgres [local] INSERT(procsignal_sigusr1_handler+0x9e)[0x5559b94de4ef]
+/lib/x86_64-linux-gnu/libc.so.6(+0x42520)[0x7f7e9c0e8520]
+postgres: ubuntu postgres [local] INSERT(+0x868004)[0x5559b972c004]
+postgres: ubuntu postgres [local] INSERT(pfree+0x1c)[0x5559b972e445]
+postgres: ubuntu postgres [local] INSERT(heap_free_minimal_tuple+0x1c)[0x5559b8fa7b48]
+postgres: ubuntu postgres [local] INSERT(+0x8826d6)[0x5559b97466d6]
+postgres: ubuntu postgres [local] INSERT(+0x881126)[0x5559b9745126]
+postgres: ubuntu postgres [local] INSERT(tuplestore_putvalues+0x83)[0x5559b9744ed5]
+postgres: ubuntu postgres [local] INSERT(ExecMakeTableFunctionResult+0x68b)[0x5559b9284295]
+postgres: ubuntu postgres [local] INSERT(+0x3dd14a)[0x5559b92a114a]
+postgres: ubuntu postgres [local] INSERT(+0x3c1a5c)[0x5559b9285a5c]
+postgres: ubuntu postgres [local] INSERT(ExecScan+0x77)[0x5559b9285ad5]
+postgres: ubuntu postgres [local] INSERT(+0x3dd4f4)[0x5559b92a14f4]
+postgres: ubuntu postgres [local] INSERT(+0x3bd485)[0x5559b9281485]
+postgres: ubuntu postgres [local] INSERT(+0x3f96f0)[0x5559b92bd6f0]
+postgres: ubuntu postgres [local] INSERT(+0x3ff25e)[0x5559b92c325e]
+postgres: ubuntu postgres [local] INSERT(+0x3bd485)[0x5559b9281485]
+postgres: ubuntu postgres [local] INSERT(+0x3b07a4)[0x5559b92747a4]
+postgres: ubuntu postgres [local] INSERT(+0x3b3594)[0x5559b9277594]
+postgres: ubuntu postgres [local] INSERT(standard_ExecutorRun+0x1f4)[0x5559b9274e8c]
+postgres: ubuntu postgres [local] INSERT(ExecutorRun+0x5d)[0x5559b9274c95]
+postgres: ubuntu postgres [local] INSERT(+0x64ee3a)[0x5559b9512e3a]
+postgres: ubuntu postgres [local] INSERT(+0x6509c9)[0x5559b95149c9]
+postgres: ubuntu postgres [local] INSERT(PortalRun+0x378)[0x5559b9513ec0]
+postgres: ubuntu postgres [local] INSERT(+0x648c56)[0x5559b950cc56]
+postgres: ubuntu postgres [local] INSERT(PostgresMain+0x80c)[0x5559b9511c46]
+postgres: ubuntu postgres [local] INSERT(+0x576647)[0x5559b943a647]
+postgres: ubuntu postgres [local] INSERT(+0x575ed3)[0x5559b9439ed3]
+postgres: ubuntu postgres [local] INSERT(+0x572388)[0x5559b9436388]
+postgres: ubuntu postgres [local] INSERT(PostmasterMain+0x14b3)[0x5559b9435ae5]
+postgres: ubuntu postgres [local] INSERT(+0x43b563)[0x5559b92ff563]
+/lib/x86_64-linux-gnu/libc.so.6(+0x29d90)[0x7f7e9c0cfd90]
+/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80)[0x7f7e9c0cfe40]
+postgres: ubuntu postgres [local] INSERT(_start+0x25)[0x5559b8f8f005]
+</screen>
+    One can obtain the file name and line number from the logged details by using
+    gdb/addr2line in linux platforms (users must ensure gdb/addr2line is
+    already installed).
+   </para>
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 35d738d576..dfa8f19fe1 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -742,6 +742,8 @@ REVOKE EXECUTE ON FUNCTION pg_ls_logicalmapdir() FROM PUBLIC;
 
 REVOKE EXECUTE ON FUNCTION pg_ls_replslotdir(text) FROM PUBLIC;
 
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer) FROM PUBLIC;
+
 --
 -- We also set up some things as accessible to standard roles.
 --
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 3a6f24a023..64b01c9f65 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -475,6 +475,8 @@ AutoVacLauncherMain(int argc, char *argv[])
 	pqsignal(SIGFPE, FloatExceptionHandler);
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * Create a per-backend PGPROC struct in shared memory, except in the
 	 * EXEC_BACKEND case where this was done in SubPostmasterMain. We must do
@@ -1547,6 +1549,8 @@ AutoVacWorkerMain(int argc, char *argv[])
 	pqsignal(SIGFPE, FloatExceptionHandler);
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * Create a per-backend PGPROC struct in shared memory, except in the
 	 * EXEC_BACKEND case where this was done in SubPostmasterMain. We must do
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 48a9924527..906bb28c20 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -794,6 +794,8 @@ BackgroundWorkerMain(void)
 	pqsignal(SIGUSR2, SIG_IGN);
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * If an exception is encountered, processing resumes here.
 	 *
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index f2e4f23d9f..29cb27e3f7 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -112,6 +112,8 @@ BackgroundWriterMain(void)
 	 */
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * We just started, assume there has been either a shutdown or
 	 * end-of-recovery snapshot.
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index a3c1aba24e..b027bae7ce 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -198,6 +198,8 @@ CheckpointerMain(void)
 	 */
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * Initialize so that first time-driven event happens at the correct time.
 	 */
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 46af349564..bc542e2f21 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -232,6 +232,8 @@ PgArchiverMain(void)
 	/* Unblock signals (they were blocked when the postmaster forked us) */
 	sigprocmask(SIG_SETMASK, &UnBlockSig, NULL);
 
+	LoadBacktraceFunctions();
+
 	/* We shouldn't be launched unnecessarily. */
 	Assert(XLogArchivingActive());
 
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index 0e7de26bc2..3ea64ed449 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -259,6 +259,8 @@ StartupProcessMain(void)
 	pqsignal(SIGUSR1, procsignal_sigusr1_handler);
 	pqsignal(SIGUSR2, StartupProcTriggerHandler);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * Reset some signals that are accepted by postmaster but not here
 	 */
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 266fbc2339..6982d05647 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -115,6 +115,8 @@ WalWriterMain(void)
 	 */
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * Create a memory context that we will do all our work in.  We do this so
 	 * that we can reset the context during error recovery and thereby avoid
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index a3128874b2..949c768c33 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -287,6 +287,8 @@ WalReceiverMain(void)
 	/* Reset some signals that are accepted by postmaster but not here */
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/* Load the libpq-specific functions */
 	load_file("libpqwalreceiver", false);
 	if (WalReceiverFunctions == NULL)
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index e250b0567e..33e81cf02b 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -3270,6 +3270,8 @@ WalSndSignals(void)
 
 	/* Reset some signals that are accepted by postmaster but not here */
 	pqsignal(SIGCHLD, SIG_DFL);
+
+	LoadBacktraceFunctions();
 }
 
 /* Report shared-memory space needed by WalSndShmemInit */
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index b7427906de..e2b3b4009e 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -16,6 +16,9 @@
 
 #include <signal.h>
 #include <unistd.h>
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
 
 #include "access/parallel.h"
 #include "port/pg_bitutils.h"
@@ -97,6 +100,10 @@ typedef struct
 #define BARRIER_CLEAR_BIT(flags, type) \
 	((flags) &= ~(((uint32) 1) << (uint32) (type)))
 
+#ifdef HAVE_BACKTRACE_SYMBOLS
+static bool backtrace_functions_loaded = false;
+#endif
+
 static ProcSignalHeader *ProcSignal = NULL;
 static ProcSignalSlot *MyProcSignalSlot = NULL;
 
@@ -609,6 +616,76 @@ ResetProcSignalBarrierBits(uint32 flags)
 	InterruptPending = true;
 }
 
+/*
+ * HandleLogBacktraceInterrupt - Handle receipt of an interrupt requesting to
+ * log a backtrace.
+ *
+ * We capture the backtrace within this signal handler and emit to stderr. Note
+ * that we ensured the backtrace-related functions are signal-safe, see
+ * LoadBacktraceFunctions() for more details.
+ *
+ * Emitting backtrace to stderr as opposed to writing to server log has an
+ * advantage - we don't need to allocate any dynamic memory while capturing
+ * backtrace which makes the signal handler safe.
+ */
+static void
+HandleLogBacktraceInterrupt(void)
+{
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	void	   *buf[100];
+	int			nframes;
+
+	/* Quickly exit if backtrace-related functions aren't loaded. */
+	if (!backtrace_functions_loaded)
+		return;
+
+	nframes = backtrace(buf, lengthof(buf));
+
+	write_stderr("logging current backtrace of process with PID %d:\n",
+				 MyProcPid);
+	backtrace_symbols_fd(buf, nframes, fileno(stderr));
+#endif
+}
+
+/*
+ * LoadBacktraceFunctions - call a backtrace-related function to ensure the
+ * shared library implementing them is loaded beforehand.
+ *
+ * Any backtrace-related functions when called for the first time dynamically
+ * loads the shared library, which usually triggers a call to malloc, making
+ * them unsafe to use in signal handlers.
+ *
+ * This functions is an attempt to make backtrace-related functions signal
+ * safe.
+ *
+ * NOTE: This function is supposed to be called in the early life of a process,
+ * preferably after SIGUSR1 handler is setup and before the backtrace-related
+ * functions are used in signal handlers. It is not supposed to be called from
+ * within a signal handler.
+ */
+void
+LoadBacktraceFunctions(void)
+{
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	void	   *buf[100];
+
+	/*
+	 * XXX: It is a bit of overkill to check if the shared library
+	 * implementing backtrace-related functions is loaded already. Instead, we
+	 * go ahead and call one function.
+	 */
+
+	/*
+	 * It is enough to call any one backtrace-related function to ensure that
+	 * the corresponding shared library is dynamically loaded if not done
+	 * already.
+	 */
+	backtrace(buf, lengthof(buf));
+
+	backtrace_functions_loaded = true;
+#endif
+}
+
 /*
  * CheckProcSignal - check to see if a particular reason has been
  * signaled, and clear the signal flag.  Should be called after receiving
@@ -661,6 +738,16 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_PARALLEL_APPLY_MESSAGE))
 		HandleParallelApplyMessageInterrupt();
 
+	/*
+	 * XXX: Since the log backtrace signal handler itself does the required
+	 * job, returning without setting the latch may be a good idea here.
+	 * However, it is better not to deviate from the tradition of a signal
+	 * handler setting the latch to wake up the processes that are waiting on
+	 * it.
+	 */
+	if (CheckProcSignal(PROCSIG_LOG_BACKTRACE))
+		HandleLogBacktraceInterrupt();
+
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
 		HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
 
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index 94ae553b94..e24f04bbcb 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -311,3 +311,29 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_log_backtrace - signal a backend or an auxiliary process to log its
+ * current backtrace to stderr.
+ *
+ * By default, only superusers are allowed to request to log the backtrace
+ * which is safe from a security standpoint because the backtrace might contain
+ * internal details. However, a superuser can grant the execute permission to
+ * anyone, if it wishes.
+ */
+Datum
+pg_log_backtrace(PG_FUNCTION_ARGS)
+{
+	int			pid = PG_GETARG_INT32(0);
+	bool		result;
+
+#ifndef HAVE_BACKTRACE_SYMBOLS
+	ereport(WARNING,
+			errmsg("backtrace generation is not supported by this installation"),
+			errhint("You need to rebuild PostgreSQL using a library containing backtrace_symbols."));
+	PG_RETURN_BOOL(false);
+#endif
+
+	result = SendProcSignalBackendOrAuxproc(pid, PROCSIG_LOG_BACKTRACE);
+	PG_RETURN_BOOL(result);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 6a070b5d8c..c64fdb95d9 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -4189,6 +4189,8 @@ PostgresMain(const char *dbname, const char *username)
 		 */
 		pqsignal(SIGCHLD, SIG_DFL); /* system() requires this on some
 									 * platforms */
+
+		LoadBacktraceFunctions();
 	}
 
 	/* Early initialization */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 091f7e343c..e76c85a531 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -12083,4 +12083,9 @@
   proname => 'any_value_transfn', prorettype => 'anyelement',
   proargtypes => 'anyelement anyelement', prosrc => 'any_value_transfn' },
 
+# function to get the backtrace of server process
+{ oid => '9661', descr => 'log backtrace of server process',
+  proname => 'pg_log_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_log_backtrace' },
+
 ]
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index 548519117a..9e517395f6 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -36,6 +36,7 @@ typedef enum
 	PROCSIG_BARRIER,			/* global barrier interrupt  */
 	PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
 	PROCSIG_PARALLEL_APPLY_MESSAGE, /* Message from parallel apply workers */
+	PROCSIG_LOG_BACKTRACE,		/* ask backend to log the current backtrace */
 
 	/* Recovery conflict reasons */
 	PROCSIG_RECOVERY_CONFLICT_FIRST,
@@ -71,5 +72,6 @@ extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
 
 extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
+extern void LoadBacktraceFunctions(void);
 
 #endif							/* PROCSIGNAL_H */
diff --git a/src/test/regress/expected/backtrace.out b/src/test/regress/expected/backtrace.out
new file mode 100644
index 0000000000..5341a6adfe
--- /dev/null
+++ b/src/test/regress/expected/backtrace.out
@@ -0,0 +1,49 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged to stderr and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/expected/backtrace_1.out b/src/test/regress/expected/backtrace_1.out
new file mode 100644
index 0000000000..899f330224
--- /dev/null
+++ b/src/test/regress/expected/backtrace_1.out
@@ -0,0 +1,55 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged to stderr and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index f0987ff537..32c286dd0c 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -78,7 +78,7 @@ test: brin_bloom brin_multi
 # psql depends on create_am
 # amutils depends on geometry, create_index_spgist, hash_index, brin
 # ----------
-test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role
+test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role backtrace
 
 # collate.*.utf8 tests cannot be run in parallel with each other
 test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 collate.windows.win1252
diff --git a/src/test/regress/sql/backtrace.sql b/src/test/regress/sql/backtrace.sql
new file mode 100644
index 0000000000..674e41c998
--- /dev/null
+++ b/src/test/regress/sql/backtrace.sql
@@ -0,0 +1,33 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged to stderr and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+
+SELECT pg_log_backtrace(pg_backend_pid());
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+
+CREATE ROLE regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+RESET ROLE;
+
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+
+DROP ROLE regress_log_backtrace;
-- 
2.34.1

v24-0001-Move-sending-multiplexed-SIGUSR1-signal-code-to-.patchapplication/x-patch; name=v24-0001-Move-sending-multiplexed-SIGUSR1-signal-code-to-.patchDownload
From 9673b537cc1797f86aac69613eb50b6fc99256f8 Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Sat, 4 Nov 2023 18:06:36 +0000
Subject: [PATCH v24] Move sending multiplexed-SIGUSR1 signal code to a
 function

Add a new function hosting the common code for sending
multiplexed-SIGUSR1 signal to a backend process. This function
will also be used as-is by an upcoming commit reducing the code
duplication.
---
 src/backend/storage/ipc/procarray.c | 60 +++++++++++++++++++++++++++++
 src/backend/utils/adt/mcxtfuncs.c   | 49 ++---------------------
 src/include/storage/procarray.h     |  1 +
 3 files changed, 64 insertions(+), 46 deletions(-)

diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 4ca2789d10..9fe1158a0c 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -3092,6 +3092,66 @@ HaveVirtualXIDsDelayingChkpt(VirtualTransactionId *vxids, int nvxids, int type)
 	return result;
 }
 
+/*
+ * SendProcSignalBackendOrAuxproc -- check if the process with given pid is a
+ * backend or an auxiliary process and send it the SIGUSR1 signal for a given
+ * reason.
+ *
+ * Returns true if sending the signal was successful, false otherwise.
+ */
+bool
+SendProcSignalBackendOrAuxproc(int pid, ProcSignalReason reason)
+{
+	PGPROC	   *proc;
+	BackendId	backendId = InvalidBackendId;
+
+	proc = BackendPidGetProc(pid);
+
+	/*
+	 * 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 or a long running process,
+	 * that it might end on its own first and its memory contexts are not
+	 * logged or backtrace not logged is not a problem.
+	 */
+	if (proc == NULL)
+	{
+		/*
+		 * This is just a warning so a loop-through-resultset will not abort
+		 * if one backend terminated on its own during the run.
+		 */
+		ereport(WARNING,
+				(errmsg("PID %d is not a PostgreSQL server process", pid)));
+		return false;
+	}
+
+	if (SendProcSignal(pid, reason, backendId) < 0)
+	{
+		/* Again, just a warning to allow loops */
+		ereport(WARNING,
+				(errmsg("could not send signal to process %d: %m", pid)));
+		return false;
+	}
+
+	return true;
+}
+
 /*
  * BackendPidGetProc -- get a backend's PGPROC given its PID
  *
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index 92ca5b2f72..7b17afc2ff 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -145,51 +145,8 @@ Datum
 pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
 {
 	int			pid = PG_GETARG_INT32(0);
-	PGPROC	   *proc;
-	BackendId	backendId = InvalidBackendId;
+	bool		result;
 
-	proc = BackendPidGetProc(pid);
-
-	/*
-	 * 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)
-	{
-		/*
-		 * This is just a warning so a loop-through-resultset will not abort
-		 * if one backend terminated on its own during the run.
-		 */
-		ereport(WARNING,
-				(errmsg("PID %d is not a PostgreSQL server process", pid)));
-		PG_RETURN_BOOL(false);
-	}
-
-	if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, backendId) < 0)
-	{
-		/* Again, just a warning to allow loops */
-		ereport(WARNING,
-				(errmsg("could not send signal to process %d: %m", pid)));
-		PG_RETURN_BOOL(false);
-	}
-
-	PG_RETURN_BOOL(true);
+	result = SendProcSignalBackendOrAuxproc(pid, PROCSIG_LOG_MEMORY_CONTEXT);
+	PG_RETURN_BOOL(result);
 }
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index d8cae3ce1c..2b470ddadf 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -68,6 +68,7 @@ extern PGPROC *BackendPidGetProc(int pid);
 extern PGPROC *BackendPidGetProcWithLock(int pid);
 extern int	BackendXidGetPid(TransactionId xid);
 extern bool IsBackendPid(int pid);
+extern bool SendProcSignalBackendOrAuxproc(int pid, ProcSignalReason reason);
 
 extern VirtualTransactionId *GetCurrentVirtualXIDs(TransactionId limitXmin,
 												   bool excludeXmin0, bool allDbs, int excludeVacuum,
-- 
2.34.1

#114Maciek Sakrejda
m.sakrejda@gmail.com
In reply to: Bharath Rupireddy (#113)
Re: Printing backtrace of postgres processes

The following review has been posted through the commitfest application:
make installcheck-world: tested, failed
Implements feature: tested, passed
Spec compliant: not tested
Documentation: tested, passed

I'm not sure if this actually still needs review, but it's marked as such in the CF app, so I'm reviewing it in the hopes of moving it along.

The feature works as documented. The docs say "This function is supported only if PostgreSQL was built with the ability to capture backtraces, otherwise it will emit a warning." I'm not sure what building with the ability to capture backtraces is, but it worked with no special config on my machine. I don't have much C experience, so I don't know if this is something that should have more context in a README somewhere, or if it's likely someone who's interested in this will already know what to do. The code looks fine to me.

I tried running make installcheck-world, but it failed on 17 tests. However, master also fails here on 17 tests. A normal make check-world passes on both branches. I assume I'm doing something wrong and would appreciate any pointers [0]My regression.diffs has errors like.

Based on my review and Daniel's comment above, I'm marking this as Ready for Committer.

Thanks,
Maciek

[0]: My regression.diffs has errors like

```
diff -U3 /home/maciek/code/aux/postgres/src/test/regress/expected/copyselect.out /home/maciek/code/aux/postgres/src/test/regress/results/copyselect.out
--- /home/maciek/code/aux/postgres/src/test/regress/expected/copyselect.out     2023-01-02 12:21:10.792646101 -0800
+++ /home/maciek/code/aux/postgres/src/test/regress/results/copyselect.out      2024-01-14 15:04:07.513887866 -0800
@@ -131,11 +131,6 @@
 2
  ?column? 
 ----------
-        3
-(1 row)
-
- ?column? 
-----------
         4
 (1 row)
```

and

```
diff -U3 /home/maciek/code/aux/postgres/src/test/regress/expected/create_table.out /home/maciek/code/aux/postgres/src/test/regress/results/create_table.out
--- /home/maciek/code/aux/postgres/src/test/regress/expected/create_table.out   2023-10-02 22:14:02.583377845 -0700
+++ /home/maciek/code/aux/postgres/src/test/regress/results/create_table.out    2024-01-14 15:04:09.037890710 -0800
@@ -854,8 +854,6 @@
  b      | integer |           | not null | 1       | plain    |              | 
 Partition of: parted FOR VALUES IN ('b')
 Partition constraint: ((a IS NOT NULL) AND (a = 'b'::text))
-Not-null constraints:
-    "part_b_b_not_null" NOT NULL "b" (local, inherited)

-- Both partition bound and partition key in describe output
\d+ part_c
```

I'm on Ubuntu 22.04 with Postgres 11, 12, 13, and 16 installed from PGDG.

The new status of this patch is: Ready for Committer

#115vignesh C
vignesh21@gmail.com
In reply to: Bharath Rupireddy (#113)
2 attachment(s)
Re: Printing backtrace of postgres processes

On Sun, 5 Nov 2023 at 01:49, Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Thu, Jul 20, 2023 at 8:22 PM Daniel Gustafsson <daniel@yesql.se> wrote:

On 11 Jan 2023, at 15:44, Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> wrote:

On Wed, Nov 30, 2022 at 11:43 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

I'm attaching the v22 patch set for further review.

Needed a rebase due to 216a784829c2c5f03ab0c43e009126cbb819e9b2.
Attaching v23 patch set for further review.

This thread has stalled for well over 6 months with the patch going from CF to
CF. From skimming the thread it seems that a lot of the details have been
ironed out with most (all?) objections addressed. Is any committer interested
in picking this up? If not we should probably mark it returned with feedback.

Rebase needed due to function oid clash. Picked the new OID with the
help of src/include/catalog/unused_oids. PSA v24 patch set.

Rebase needed due to changes in parallel_schedule. PSA v25 patch set.

Regards,
Vignesh

Attachments:

v25-0001-Move-sending-multiplexed-SIGUSR1-signal-code-to-.patchtext/x-patch; charset=US-ASCII; name=v25-0001-Move-sending-multiplexed-SIGUSR1-signal-code-to-.patchDownload
From 32fa1d898b2599b836a2265f746acf230ffb358e Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Sat, 4 Nov 2023 18:06:36 +0000
Subject: [PATCH v25 1/2] Move sending multiplexed-SIGUSR1 signal code to a
 function

Add a new function hosting the common code for sending
multiplexed-SIGUSR1 signal to a backend process. This function
will also be used as-is by an upcoming commit reducing the code
duplication.
---
 src/backend/storage/ipc/procarray.c | 60 +++++++++++++++++++++++++++++
 src/backend/utils/adt/mcxtfuncs.c   | 49 ++---------------------
 src/include/storage/procarray.h     |  1 +
 3 files changed, 64 insertions(+), 46 deletions(-)

diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index ee2d7f8585..f6465529e7 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -3097,6 +3097,66 @@ HaveVirtualXIDsDelayingChkpt(VirtualTransactionId *vxids, int nvxids, int type)
 	return result;
 }
 
+/*
+ * SendProcSignalBackendOrAuxproc -- check if the process with given pid is a
+ * backend or an auxiliary process and send it the SIGUSR1 signal for a given
+ * reason.
+ *
+ * Returns true if sending the signal was successful, false otherwise.
+ */
+bool
+SendProcSignalBackendOrAuxproc(int pid, ProcSignalReason reason)
+{
+	PGPROC	   *proc;
+	BackendId	backendId = InvalidBackendId;
+
+	proc = BackendPidGetProc(pid);
+
+	/*
+	 * 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 or a long running process,
+	 * that it might end on its own first and its memory contexts are not
+	 * logged or backtrace not logged is not a problem.
+	 */
+	if (proc == NULL)
+	{
+		/*
+		 * This is just a warning so a loop-through-resultset will not abort
+		 * if one backend terminated on its own during the run.
+		 */
+		ereport(WARNING,
+				(errmsg("PID %d is not a PostgreSQL server process", pid)));
+		return false;
+	}
+
+	if (SendProcSignal(pid, reason, backendId) < 0)
+	{
+		/* Again, just a warning to allow loops */
+		ereport(WARNING,
+				(errmsg("could not send signal to process %d: %m", pid)));
+		return false;
+	}
+
+	return true;
+}
+
 /*
  * BackendPidGetProc -- get a backend's PGPROC given its PID
  *
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index 4708d73f5f..f7b4f8dac1 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -145,51 +145,8 @@ Datum
 pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
 {
 	int			pid = PG_GETARG_INT32(0);
-	PGPROC	   *proc;
-	BackendId	backendId = InvalidBackendId;
+	bool		result;
 
-	proc = BackendPidGetProc(pid);
-
-	/*
-	 * 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)
-	{
-		/*
-		 * This is just a warning so a loop-through-resultset will not abort
-		 * if one backend terminated on its own during the run.
-		 */
-		ereport(WARNING,
-				(errmsg("PID %d is not a PostgreSQL server process", pid)));
-		PG_RETURN_BOOL(false);
-	}
-
-	if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, backendId) < 0)
-	{
-		/* Again, just a warning to allow loops */
-		ereport(WARNING,
-				(errmsg("could not send signal to process %d: %m", pid)));
-		PG_RETURN_BOOL(false);
-	}
-
-	PG_RETURN_BOOL(true);
+	result = SendProcSignalBackendOrAuxproc(pid, PROCSIG_LOG_MEMORY_CONTEXT);
+	PG_RETURN_BOOL(result);
 }
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index f3eba9b764..4ac8e2c60c 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -68,6 +68,7 @@ extern PGPROC *BackendPidGetProc(int pid);
 extern PGPROC *BackendPidGetProcWithLock(int pid);
 extern int	BackendXidGetPid(TransactionId xid);
 extern bool IsBackendPid(int pid);
+extern bool SendProcSignalBackendOrAuxproc(int pid, ProcSignalReason reason);
 
 extern VirtualTransactionId *GetCurrentVirtualXIDs(TransactionId limitXmin,
 												   bool excludeXmin0, bool allDbs, int excludeVacuum,
-- 
2.34.1

v25-0002-Add-function-to-log-the-backtrace-of-specified-p.patchtext/x-patch; charset=US-ASCII; name=v25-0002-Add-function-to-log-the-backtrace-of-specified-p.patchDownload
From b850dde3476cc6643b1cf2ca04d48124f23fd1bd Mon Sep 17 00:00:00 2001
From: Vignesh C <vignesh21@gmail.com>
Date: Fri, 26 Jan 2024 07:33:55 +0530
Subject: [PATCH v25 2/2] Add function to log the backtrace of specified
 postgres process

This commit adds pg_log_backtrace() function that requests to log
the backtrace to console i.e. stderr of the specified backend or
auxiliary process except logger and statistic collector.

Only superusers are allowed to request to log the backtrace which
is safe from a security standpoint because the backtrace might
contain internal details.

Bump catalog version.
---
 contrib/pg_prewarm/autoprewarm.c          |  2 +
 doc/src/sgml/func.sgml                    | 76 ++++++++++++++++++++
 src/backend/catalog/system_functions.sql  |  2 +
 src/backend/postmaster/autovacuum.c       |  4 ++
 src/backend/postmaster/bgworker.c         |  2 +
 src/backend/postmaster/bgwriter.c         |  2 +
 src/backend/postmaster/checkpointer.c     |  2 +
 src/backend/postmaster/pgarch.c           |  2 +
 src/backend/postmaster/startup.c          |  2 +
 src/backend/postmaster/walwriter.c        |  2 +
 src/backend/replication/walreceiver.c     |  2 +
 src/backend/replication/walsender.c       |  2 +
 src/backend/storage/ipc/procsignal.c      | 87 +++++++++++++++++++++++
 src/backend/storage/ipc/signalfuncs.c     | 26 +++++++
 src/backend/tcop/postgres.c               |  2 +
 src/include/catalog/pg_proc.dat           |  5 ++
 src/include/storage/procsignal.h          |  2 +
 src/test/regress/expected/backtrace.out   | 49 +++++++++++++
 src/test/regress/expected/backtrace_1.out | 55 ++++++++++++++
 src/test/regress/parallel_schedule        |  2 +-
 src/test/regress/sql/backtrace.sql        | 33 +++++++++
 21 files changed, 360 insertions(+), 1 deletion(-)
 create mode 100644 src/test/regress/expected/backtrace.out
 create mode 100644 src/test/regress/expected/backtrace_1.out
 create mode 100644 src/test/regress/sql/backtrace.sql

diff --git a/contrib/pg_prewarm/autoprewarm.c b/contrib/pg_prewarm/autoprewarm.c
index 06ee21d496..94e04b78cc 100644
--- a/contrib/pg_prewarm/autoprewarm.c
+++ b/contrib/pg_prewarm/autoprewarm.c
@@ -160,6 +160,8 @@ autoprewarm_main(Datum main_arg)
 	pqsignal(SIGUSR1, procsignal_sigusr1_handler);
 	BackgroundWorkerUnblockSignals();
 
+	LoadBacktraceFunctions();
+
 	/* Create (if necessary) and attach to our shared memory area. */
 	if (apw_init_shmem())
 		first_time = false;
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 6788ba8ef4..5bd014adc2 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25707,6 +25707,27 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_log_backtrace</primary>
+        </indexterm>
+        <function>pg_log_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Requests to log the backtrace of the backend with the specified
+        process ID. This function can send the request to backends and
+        auxiliary processes except the logger and statistics collector.
+        The backtraces will be logged to <systemitem>stderr</systemitem>.
+        Typically, a backtrace identifies which function a process is
+        currently executing and it is useful for developers to diagnose
+        stuck processes and other problems. This function is supported
+        only if PostgreSQL was built with the ability to capture backtraces,
+        otherwise it will emit a warning.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -27300,6 +27321,61 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_log_backtrace</function> can be used to log the backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace
+------------------
+ t
+(1 row)
+</programlisting>
+The backtrace will be logged as specified by the logging configuration.
+For example:
+<screen>
+logging current backtrace of process with PID 3499242:
+postgres: ubuntu postgres [local] INSERT(+0x61a355)[0x5559b94de355]
+postgres: ubuntu postgres [local] INSERT(procsignal_sigusr1_handler+0x9e)[0x5559b94de4ef]
+/lib/x86_64-linux-gnu/libc.so.6(+0x42520)[0x7f7e9c0e8520]
+postgres: ubuntu postgres [local] INSERT(+0x868004)[0x5559b972c004]
+postgres: ubuntu postgres [local] INSERT(pfree+0x1c)[0x5559b972e445]
+postgres: ubuntu postgres [local] INSERT(heap_free_minimal_tuple+0x1c)[0x5559b8fa7b48]
+postgres: ubuntu postgres [local] INSERT(+0x8826d6)[0x5559b97466d6]
+postgres: ubuntu postgres [local] INSERT(+0x881126)[0x5559b9745126]
+postgres: ubuntu postgres [local] INSERT(tuplestore_putvalues+0x83)[0x5559b9744ed5]
+postgres: ubuntu postgres [local] INSERT(ExecMakeTableFunctionResult+0x68b)[0x5559b9284295]
+postgres: ubuntu postgres [local] INSERT(+0x3dd14a)[0x5559b92a114a]
+postgres: ubuntu postgres [local] INSERT(+0x3c1a5c)[0x5559b9285a5c]
+postgres: ubuntu postgres [local] INSERT(ExecScan+0x77)[0x5559b9285ad5]
+postgres: ubuntu postgres [local] INSERT(+0x3dd4f4)[0x5559b92a14f4]
+postgres: ubuntu postgres [local] INSERT(+0x3bd485)[0x5559b9281485]
+postgres: ubuntu postgres [local] INSERT(+0x3f96f0)[0x5559b92bd6f0]
+postgres: ubuntu postgres [local] INSERT(+0x3ff25e)[0x5559b92c325e]
+postgres: ubuntu postgres [local] INSERT(+0x3bd485)[0x5559b9281485]
+postgres: ubuntu postgres [local] INSERT(+0x3b07a4)[0x5559b92747a4]
+postgres: ubuntu postgres [local] INSERT(+0x3b3594)[0x5559b9277594]
+postgres: ubuntu postgres [local] INSERT(standard_ExecutorRun+0x1f4)[0x5559b9274e8c]
+postgres: ubuntu postgres [local] INSERT(ExecutorRun+0x5d)[0x5559b9274c95]
+postgres: ubuntu postgres [local] INSERT(+0x64ee3a)[0x5559b9512e3a]
+postgres: ubuntu postgres [local] INSERT(+0x6509c9)[0x5559b95149c9]
+postgres: ubuntu postgres [local] INSERT(PortalRun+0x378)[0x5559b9513ec0]
+postgres: ubuntu postgres [local] INSERT(+0x648c56)[0x5559b950cc56]
+postgres: ubuntu postgres [local] INSERT(PostgresMain+0x80c)[0x5559b9511c46]
+postgres: ubuntu postgres [local] INSERT(+0x576647)[0x5559b943a647]
+postgres: ubuntu postgres [local] INSERT(+0x575ed3)[0x5559b9439ed3]
+postgres: ubuntu postgres [local] INSERT(+0x572388)[0x5559b9436388]
+postgres: ubuntu postgres [local] INSERT(PostmasterMain+0x14b3)[0x5559b9435ae5]
+postgres: ubuntu postgres [local] INSERT(+0x43b563)[0x5559b92ff563]
+/lib/x86_64-linux-gnu/libc.so.6(+0x29d90)[0x7f7e9c0cfd90]
+/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80)[0x7f7e9c0cfe40]
+postgres: ubuntu postgres [local] INSERT(_start+0x25)[0x5559b8f8f005]
+</screen>
+    One can obtain the file name and line number from the logged details by using
+    gdb/addr2line in linux platforms (users must ensure gdb/addr2line is
+    already installed).
+   </para>
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 346cfb98a0..3d8ea385fb 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -757,6 +757,8 @@ REVOKE EXECUTE ON FUNCTION pg_ls_logicalmapdir() FROM PUBLIC;
 
 REVOKE EXECUTE ON FUNCTION pg_ls_replslotdir(text) FROM PUBLIC;
 
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer) FROM PUBLIC;
+
 --
 -- We also set up some things as accessible to standard roles.
 --
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 2c3099f76f..b404049b15 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -479,6 +479,8 @@ AutoVacLauncherMain(int argc, char *argv[])
 
 	SetProcessingMode(NormalProcessing);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * Create a memory context that we will do all our work in.  We do this so
 	 * that we can reset the context during error recovery and thereby avoid
@@ -1534,6 +1536,8 @@ AutoVacWorkerMain(int argc, char *argv[])
 	/* Early initialization */
 	BaseInit();
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * If an exception is encountered, processing resumes here.
 	 *
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 67f92c24db..3b989990fb 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -772,6 +772,8 @@ BackgroundWorkerMain(void)
 	pqsignal(SIGUSR2, SIG_IGN);
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * If an exception is encountered, processing resumes here.
 	 *
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index d7d6cc0cd7..4f6f62b404 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -112,6 +112,8 @@ BackgroundWriterMain(void)
 	 */
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * We just started, assume there has been either a shutdown or
 	 * end-of-recovery snapshot.
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 5e949fc885..26d1367909 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -198,6 +198,8 @@ CheckpointerMain(void)
 	 */
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * Initialize so that first time-driven event happens at the correct time.
 	 */
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 67693b0580..9fa476974a 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -232,6 +232,8 @@ PgArchiverMain(void)
 	/* Unblock signals (they were blocked when the postmaster forked us) */
 	sigprocmask(SIG_SETMASK, &UnBlockSig, NULL);
 
+	LoadBacktraceFunctions();
+
 	/* We shouldn't be launched unnecessarily. */
 	Assert(XLogArchivingActive());
 
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index d53c37d062..87bf59ce9e 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -258,6 +258,8 @@ StartupProcessMain(void)
 	pqsignal(SIGUSR1, procsignal_sigusr1_handler);
 	pqsignal(SIGUSR2, StartupProcTriggerHandler);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * Reset some signals that are accepted by postmaster but not here
 	 */
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 75c9f8707b..5b2e643c94 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -112,6 +112,8 @@ WalWriterMain(void)
 	 */
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * Create a memory context that we will do all our work in.  We do this so
 	 * that we can reset the context during error recovery and thereby avoid
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 728059518e..623b162244 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -287,6 +287,8 @@ WalReceiverMain(void)
 	/* Reset some signals that are accepted by postmaster but not here */
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/* Load the libpq-specific functions */
 	load_file("libpqwalreceiver", false);
 	if (WalReceiverFunctions == NULL)
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index aa80f3de20..33cec58db8 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -3430,6 +3430,8 @@ WalSndSignals(void)
 
 	/* Reset some signals that are accepted by postmaster but not here */
 	pqsignal(SIGCHLD, SIG_DFL);
+
+	LoadBacktraceFunctions();
 }
 
 /* Report shared-memory space needed by WalSndShmemInit */
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index e84619e5a5..5ca9218a46 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -16,6 +16,9 @@
 
 #include <signal.h>
 #include <unistd.h>
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
 
 #include "access/parallel.h"
 #include "port/pg_bitutils.h"
@@ -97,6 +100,10 @@ typedef struct
 #define BARRIER_CLEAR_BIT(flags, type) \
 	((flags) &= ~(((uint32) 1) << (uint32) (type)))
 
+#ifdef HAVE_BACKTRACE_SYMBOLS
+static bool backtrace_functions_loaded = false;
+#endif
+
 static ProcSignalHeader *ProcSignal = NULL;
 static ProcSignalSlot *MyProcSignalSlot = NULL;
 
@@ -609,6 +616,76 @@ ResetProcSignalBarrierBits(uint32 flags)
 	InterruptPending = true;
 }
 
+/*
+ * HandleLogBacktraceInterrupt - Handle receipt of an interrupt requesting to
+ * log a backtrace.
+ *
+ * We capture the backtrace within this signal handler and emit to stderr. Note
+ * that we ensured the backtrace-related functions are signal-safe, see
+ * LoadBacktraceFunctions() for more details.
+ *
+ * Emitting backtrace to stderr as opposed to writing to server log has an
+ * advantage - we don't need to allocate any dynamic memory while capturing
+ * backtrace which makes the signal handler safe.
+ */
+static void
+HandleLogBacktraceInterrupt(void)
+{
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	void	   *buf[100];
+	int			nframes;
+
+	/* Quickly exit if backtrace-related functions aren't loaded. */
+	if (!backtrace_functions_loaded)
+		return;
+
+	nframes = backtrace(buf, lengthof(buf));
+
+	write_stderr("logging current backtrace of process with PID %d:\n",
+				 MyProcPid);
+	backtrace_symbols_fd(buf, nframes, fileno(stderr));
+#endif
+}
+
+/*
+ * LoadBacktraceFunctions - call a backtrace-related function to ensure the
+ * shared library implementing them is loaded beforehand.
+ *
+ * Any backtrace-related functions when called for the first time dynamically
+ * loads the shared library, which usually triggers a call to malloc, making
+ * them unsafe to use in signal handlers.
+ *
+ * This functions is an attempt to make backtrace-related functions signal
+ * safe.
+ *
+ * NOTE: This function is supposed to be called in the early life of a process,
+ * preferably after SIGUSR1 handler is setup and before the backtrace-related
+ * functions are used in signal handlers. It is not supposed to be called from
+ * within a signal handler.
+ */
+void
+LoadBacktraceFunctions(void)
+{
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	void	   *buf[100];
+
+	/*
+	 * XXX: It is a bit of overkill to check if the shared library
+	 * implementing backtrace-related functions is loaded already. Instead, we
+	 * go ahead and call one function.
+	 */
+
+	/*
+	 * It is enough to call any one backtrace-related function to ensure that
+	 * the corresponding shared library is dynamically loaded if not done
+	 * already.
+	 */
+	backtrace(buf, lengthof(buf));
+
+	backtrace_functions_loaded = true;
+#endif
+}
+
 /*
  * CheckProcSignal - check to see if a particular reason has been
  * signaled, and clear the signal flag.  Should be called after receiving
@@ -661,6 +738,16 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_PARALLEL_APPLY_MESSAGE))
 		HandleParallelApplyMessageInterrupt();
 
+	/*
+	 * XXX: Since the log backtrace signal handler itself does the required
+	 * job, returning without setting the latch may be a good idea here.
+	 * However, it is better not to deviate from the tradition of a signal
+	 * handler setting the latch to wake up the processes that are waiting on
+	 * it.
+	 */
+	if (CheckProcSignal(PROCSIG_LOG_BACKTRACE))
+		HandleLogBacktraceInterrupt();
+
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
 		HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
 
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index 81d1a59659..836b36e5bb 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -316,3 +316,29 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_log_backtrace - signal a backend or an auxiliary process to log its
+ * current backtrace to stderr.
+ *
+ * By default, only superusers are allowed to request to log the backtrace
+ * which is safe from a security standpoint because the backtrace might contain
+ * internal details. However, a superuser can grant the execute permission to
+ * anyone, if it wishes.
+ */
+Datum
+pg_log_backtrace(PG_FUNCTION_ARGS)
+{
+	int			pid = PG_GETARG_INT32(0);
+	bool		result;
+
+#ifndef HAVE_BACKTRACE_SYMBOLS
+	ereport(WARNING,
+			errmsg("backtrace generation is not supported by this installation"),
+			errhint("You need to rebuild PostgreSQL using a library containing backtrace_symbols."));
+	PG_RETURN_BOOL(false);
+#endif
+
+	result = SendProcSignalBackendOrAuxproc(pid, PROCSIG_LOG_BACKTRACE);
+	PG_RETURN_BOOL(result);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 1a34bd3715..e316987db5 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -4189,6 +4189,8 @@ PostgresMain(const char *dbname, const char *username)
 		 */
 		pqsignal(SIGCHLD, SIG_DFL); /* system() requires this on some
 									 * platforms */
+
+		LoadBacktraceFunctions();
 	}
 
 	/* Early initialization */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 29af4ce65d..b13e808bcb 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -12170,4 +12170,9 @@
   proargtypes => 'int2',
   prosrc => 'gist_stratnum_identity' },
 
+# function to get the backtrace of server process
+{ oid => '9661', descr => 'log backtrace of server process',
+  proname => 'pg_log_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_log_backtrace' },
+
 ]
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index 52dcb4c2ad..76033ccafc 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -36,6 +36,7 @@ typedef enum
 	PROCSIG_BARRIER,			/* global barrier interrupt  */
 	PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
 	PROCSIG_PARALLEL_APPLY_MESSAGE, /* Message from parallel apply workers */
+	PROCSIG_LOG_BACKTRACE,		/* ask backend to log the current backtrace */
 
 	/* Recovery conflict reasons */
 	PROCSIG_RECOVERY_CONFLICT_FIRST,
@@ -71,5 +72,6 @@ extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
 
 extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
+extern void LoadBacktraceFunctions(void);
 
 #endif							/* PROCSIGNAL_H */
diff --git a/src/test/regress/expected/backtrace.out b/src/test/regress/expected/backtrace.out
new file mode 100644
index 0000000000..5341a6adfe
--- /dev/null
+++ b/src/test/regress/expected/backtrace.out
@@ -0,0 +1,49 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged to stderr and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/expected/backtrace_1.out b/src/test/regress/expected/backtrace_1.out
new file mode 100644
index 0000000000..899f330224
--- /dev/null
+++ b/src/test/regress/expected/backtrace_1.out
@@ -0,0 +1,55 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged to stderr and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 1d8a414eea..9ad90d725b 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -78,7 +78,7 @@ test: brin_bloom brin_multi
 # psql depends on create_am
 # amutils depends on geometry, create_index_spgist, hash_index, brin
 # ----------
-test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role without_overlaps
+test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role without_overlaps backtrace
 
 # collate.*.utf8 tests cannot be run in parallel with each other
 test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 collate.windows.win1252
diff --git a/src/test/regress/sql/backtrace.sql b/src/test/regress/sql/backtrace.sql
new file mode 100644
index 0000000000..674e41c998
--- /dev/null
+++ b/src/test/regress/sql/backtrace.sql
@@ -0,0 +1,33 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged to stderr and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+
+SELECT pg_log_backtrace(pg_backend_pid());
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+
+CREATE ROLE regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+RESET ROLE;
+
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+
+DROP ROLE regress_log_backtrace;
-- 
2.34.1

#116Alvaro Herrera
alvherre@alvh.no-ip.org
In reply to: vignesh C (#94)
Re: Printing backtrace of postgres processes

On 2022-Jan-27, vignesh C wrote:

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 0ee6974f1c..855ccc8902 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
+    You can get the file name and line number from the logged details by using
+    gdb/addr2line in linux platforms (users must ensure gdb/addr2line is
+    already installed).

This doesn't read great. I mean, what is gdb/addr2line? I think you
mean either GDB or addr2line; this could be clearer.

diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 7402696986..522a525741 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -944,9 +943,10 @@ errbacktrace(void)
* Compute backtrace data and add it to the supplied ErrorData.  num_skip
* specifies how many inner frames to skip.  Use this to avoid showing the
* internal backtrace support functions in the backtrace.  This requires that
- * this and related functions are not inlined.
+ * this and related functions are not inlined. If the edata pointer is valid,
+ * backtrace information will be set in edata.
*/
-static void
+void
set_backtrace(ErrorData *edata, int num_skip)
{
StringInfoData errtrace;

This seems like a terrible API choice, and the comment change is no
good. I suggest you need to create a function that deals only with a
StringInfo, maybe
append_backtrace(StringInfo buf, int num_skip)
which is used by set_backtrace to print the backtrace in
edata->backtrace, and a new function log_backtrace() that does the
ereport(LOG_SERVER_ONLY) thing.

--
Álvaro Herrera PostgreSQL Developer — https://www.EnterpriseDB.com/
"Before you were born your parents weren't as boring as they are now. They
got that way paying your bills, cleaning up your room and listening to you
tell them how idealistic you are." -- Charles J. Sykes' advice to teenagers

#117Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: Alvaro Herrera (#116)
2 attachment(s)
Re: Printing backtrace of postgres processes

On Fri, Jan 26, 2024 at 4:11 PM Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:

Thanks for reviewing.

+    You can get the file name and line number from the logged details by using
+    gdb/addr2line in linux platforms (users must ensure gdb/addr2line is
+    already installed).

This doesn't read great. I mean, what is gdb/addr2line? I think you
mean either GDB or addr2line; this could be clearer.

Wrapped them in <productname> tag and reworded the comment a bit.

* internal backtrace support functions in the backtrace.  This requires that
- * this and related functions are not inlined.
+ * this and related functions are not inlined. If the edata pointer is valid,
+ * backtrace information will be set in edata.
*/
-static void
+void
set_backtrace(ErrorData *edata, int num_skip)
{
StringInfoData errtrace;

This seems like a terrible API choice, and the comment change is no
good. I suggest you need to create a function that deals only with a
StringInfo, maybe
append_backtrace(StringInfo buf, int num_skip)
which is used by set_backtrace to print the backtrace in
edata->backtrace, and a new function log_backtrace() that does the
ereport(LOG_SERVER_ONLY) thing.

You probably were looking at v21, the above change isn't there in
versions after that. Can you please review the latest version v26
attached here?

We might want this patch extended to the newly added walsummarizer
process which I'll do so in the next version.

--
Bharath Rupireddy
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com

Attachments:

v26-0001-Move-sending-multiplexed-SIGUSR1-signal-code-to-.patchapplication/x-patch; name=v26-0001-Move-sending-multiplexed-SIGUSR1-signal-code-to-.patchDownload
From c00bc969b7e55dff92cdd5b7fef355dc2bc98f8f Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Fri, 26 Jan 2024 15:51:02 +0000
Subject: [PATCH v26] Move sending multiplexed-SIGUSR1 signal code to a
 function

Add a new function hosting the common code for sending
multiplexed-SIGUSR1 signal to a backend process. This function
will also be used as-is by an upcoming commit reducing the code
duplication.
---
 src/backend/storage/ipc/procarray.c | 60 +++++++++++++++++++++++++++++
 src/backend/utils/adt/mcxtfuncs.c   | 49 ++---------------------
 src/include/storage/procarray.h     |  1 +
 3 files changed, 64 insertions(+), 46 deletions(-)

diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index ee2d7f8585..f6465529e7 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -3097,6 +3097,66 @@ HaveVirtualXIDsDelayingChkpt(VirtualTransactionId *vxids, int nvxids, int type)
 	return result;
 }
 
+/*
+ * SendProcSignalBackendOrAuxproc -- check if the process with given pid is a
+ * backend or an auxiliary process and send it the SIGUSR1 signal for a given
+ * reason.
+ *
+ * Returns true if sending the signal was successful, false otherwise.
+ */
+bool
+SendProcSignalBackendOrAuxproc(int pid, ProcSignalReason reason)
+{
+	PGPROC	   *proc;
+	BackendId	backendId = InvalidBackendId;
+
+	proc = BackendPidGetProc(pid);
+
+	/*
+	 * 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 or a long running process,
+	 * that it might end on its own first and its memory contexts are not
+	 * logged or backtrace not logged is not a problem.
+	 */
+	if (proc == NULL)
+	{
+		/*
+		 * This is just a warning so a loop-through-resultset will not abort
+		 * if one backend terminated on its own during the run.
+		 */
+		ereport(WARNING,
+				(errmsg("PID %d is not a PostgreSQL server process", pid)));
+		return false;
+	}
+
+	if (SendProcSignal(pid, reason, backendId) < 0)
+	{
+		/* Again, just a warning to allow loops */
+		ereport(WARNING,
+				(errmsg("could not send signal to process %d: %m", pid)));
+		return false;
+	}
+
+	return true;
+}
+
 /*
  * BackendPidGetProc -- get a backend's PGPROC given its PID
  *
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index 4708d73f5f..f7b4f8dac1 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -145,51 +145,8 @@ Datum
 pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
 {
 	int			pid = PG_GETARG_INT32(0);
-	PGPROC	   *proc;
-	BackendId	backendId = InvalidBackendId;
+	bool		result;
 
-	proc = BackendPidGetProc(pid);
-
-	/*
-	 * 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)
-	{
-		/*
-		 * This is just a warning so a loop-through-resultset will not abort
-		 * if one backend terminated on its own during the run.
-		 */
-		ereport(WARNING,
-				(errmsg("PID %d is not a PostgreSQL server process", pid)));
-		PG_RETURN_BOOL(false);
-	}
-
-	if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, backendId) < 0)
-	{
-		/* Again, just a warning to allow loops */
-		ereport(WARNING,
-				(errmsg("could not send signal to process %d: %m", pid)));
-		PG_RETURN_BOOL(false);
-	}
-
-	PG_RETURN_BOOL(true);
+	result = SendProcSignalBackendOrAuxproc(pid, PROCSIG_LOG_MEMORY_CONTEXT);
+	PG_RETURN_BOOL(result);
 }
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index f3eba9b764..4ac8e2c60c 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -68,6 +68,7 @@ extern PGPROC *BackendPidGetProc(int pid);
 extern PGPROC *BackendPidGetProcWithLock(int pid);
 extern int	BackendXidGetPid(TransactionId xid);
 extern bool IsBackendPid(int pid);
+extern bool SendProcSignalBackendOrAuxproc(int pid, ProcSignalReason reason);
 
 extern VirtualTransactionId *GetCurrentVirtualXIDs(TransactionId limitXmin,
 												   bool excludeXmin0, bool allDbs, int excludeVacuum,
-- 
2.34.1

v26-0002-Add-function-to-log-the-backtrace-of-specified-p.patchapplication/x-patch; name=v26-0002-Add-function-to-log-the-backtrace-of-specified-p.patchDownload
From c895756097eee8157d3a0f2c95fac616e92bab40 Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Fri, 26 Jan 2024 17:25:41 +0000
Subject: [PATCH v26] Add function to log the backtrace of specified postgres
 process

This commit adds pg_log_backtrace() function that requests to log
the backtrace to console i.e. stderr of the specified backend or
auxiliary process except logger and statistic collector.

Only superusers are allowed to request to log the backtrace which
is safe from a security standpoint because the backtrace might
contain internal details.

Bump catalog version.
---
 contrib/pg_prewarm/autoprewarm.c          |  2 +
 doc/src/sgml/func.sgml                    | 76 +++++++++++++++++++++++
 src/backend/catalog/system_functions.sql  |  2 +
 src/backend/postmaster/autovacuum.c       |  4 ++
 src/backend/postmaster/bgworker.c         |  2 +
 src/backend/postmaster/bgwriter.c         |  2 +
 src/backend/postmaster/checkpointer.c     |  2 +
 src/backend/postmaster/pgarch.c           |  2 +
 src/backend/postmaster/startup.c          |  2 +
 src/backend/postmaster/walwriter.c        |  2 +
 src/backend/replication/walreceiver.c     |  2 +
 src/backend/replication/walsender.c       |  2 +
 src/backend/storage/ipc/procsignal.c      | 74 ++++++++++++++++++++++
 src/backend/storage/ipc/signalfuncs.c     | 26 ++++++++
 src/backend/tcop/postgres.c               |  2 +
 src/include/catalog/pg_proc.dat           |  5 ++
 src/include/storage/procsignal.h          |  2 +
 src/test/regress/expected/backtrace.out   | 49 +++++++++++++++
 src/test/regress/expected/backtrace_1.out | 55 ++++++++++++++++
 src/test/regress/parallel_schedule        |  2 +-
 src/test/regress/sql/backtrace.sql        | 33 ++++++++++
 21 files changed, 347 insertions(+), 1 deletion(-)
 create mode 100644 src/test/regress/expected/backtrace.out
 create mode 100644 src/test/regress/expected/backtrace_1.out
 create mode 100644 src/test/regress/sql/backtrace.sql

diff --git a/contrib/pg_prewarm/autoprewarm.c b/contrib/pg_prewarm/autoprewarm.c
index 06ee21d496..94e04b78cc 100644
--- a/contrib/pg_prewarm/autoprewarm.c
+++ b/contrib/pg_prewarm/autoprewarm.c
@@ -160,6 +160,8 @@ autoprewarm_main(Datum main_arg)
 	pqsignal(SIGUSR1, procsignal_sigusr1_handler);
 	BackgroundWorkerUnblockSignals();
 
+	LoadBacktraceFunctions();
+
 	/* Create (if necessary) and attach to our shared memory area. */
 	if (apw_init_shmem())
 		first_time = false;
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 6788ba8ef4..b9dbf49813 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25707,6 +25707,27 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_log_backtrace</primary>
+        </indexterm>
+        <function>pg_log_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Requests to log the backtrace of the backend with the specified
+        process ID. This function can send the request to backends and
+        auxiliary processes except the logger and statistics collector.
+        The backtraces will be logged to <systemitem>stderr</systemitem>.
+        Typically, a backtrace identifies which function a process is
+        currently executing and it is useful for developers to diagnose
+        stuck processes and other problems. This function is supported
+        only if PostgreSQL was built with the ability to capture backtraces,
+        otherwise it will emit a warning.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -27300,6 +27321,61 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_log_backtrace</function> can be used to log the backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace
+------------------
+ t
+(1 row)
+</programlisting>
+The backtrace will be logged as specified by the logging configuration.
+For example:
+<screen>
+logging current backtrace of process with PID 3499242:
+postgres: ubuntu postgres [local] INSERT(+0x61a355)[0x5559b94de355]
+postgres: ubuntu postgres [local] INSERT(procsignal_sigusr1_handler+0x9e)[0x5559b94de4ef]
+/lib/x86_64-linux-gnu/libc.so.6(+0x42520)[0x7f7e9c0e8520]
+postgres: ubuntu postgres [local] INSERT(+0x868004)[0x5559b972c004]
+postgres: ubuntu postgres [local] INSERT(pfree+0x1c)[0x5559b972e445]
+postgres: ubuntu postgres [local] INSERT(heap_free_minimal_tuple+0x1c)[0x5559b8fa7b48]
+postgres: ubuntu postgres [local] INSERT(+0x8826d6)[0x5559b97466d6]
+postgres: ubuntu postgres [local] INSERT(+0x881126)[0x5559b9745126]
+postgres: ubuntu postgres [local] INSERT(tuplestore_putvalues+0x83)[0x5559b9744ed5]
+postgres: ubuntu postgres [local] INSERT(ExecMakeTableFunctionResult+0x68b)[0x5559b9284295]
+postgres: ubuntu postgres [local] INSERT(+0x3dd14a)[0x5559b92a114a]
+postgres: ubuntu postgres [local] INSERT(+0x3c1a5c)[0x5559b9285a5c]
+postgres: ubuntu postgres [local] INSERT(ExecScan+0x77)[0x5559b9285ad5]
+postgres: ubuntu postgres [local] INSERT(+0x3dd4f4)[0x5559b92a14f4]
+postgres: ubuntu postgres [local] INSERT(+0x3bd485)[0x5559b9281485]
+postgres: ubuntu postgres [local] INSERT(+0x3f96f0)[0x5559b92bd6f0]
+postgres: ubuntu postgres [local] INSERT(+0x3ff25e)[0x5559b92c325e]
+postgres: ubuntu postgres [local] INSERT(+0x3bd485)[0x5559b9281485]
+postgres: ubuntu postgres [local] INSERT(+0x3b07a4)[0x5559b92747a4]
+postgres: ubuntu postgres [local] INSERT(+0x3b3594)[0x5559b9277594]
+postgres: ubuntu postgres [local] INSERT(standard_ExecutorRun+0x1f4)[0x5559b9274e8c]
+postgres: ubuntu postgres [local] INSERT(ExecutorRun+0x5d)[0x5559b9274c95]
+postgres: ubuntu postgres [local] INSERT(+0x64ee3a)[0x5559b9512e3a]
+postgres: ubuntu postgres [local] INSERT(+0x6509c9)[0x5559b95149c9]
+postgres: ubuntu postgres [local] INSERT(PortalRun+0x378)[0x5559b9513ec0]
+postgres: ubuntu postgres [local] INSERT(+0x648c56)[0x5559b950cc56]
+postgres: ubuntu postgres [local] INSERT(PostgresMain+0x80c)[0x5559b9511c46]
+postgres: ubuntu postgres [local] INSERT(+0x576647)[0x5559b943a647]
+postgres: ubuntu postgres [local] INSERT(+0x575ed3)[0x5559b9439ed3]
+postgres: ubuntu postgres [local] INSERT(+0x572388)[0x5559b9436388]
+postgres: ubuntu postgres [local] INSERT(PostmasterMain+0x14b3)[0x5559b9435ae5]
+postgres: ubuntu postgres [local] INSERT(+0x43b563)[0x5559b92ff563]
+/lib/x86_64-linux-gnu/libc.so.6(+0x29d90)[0x7f7e9c0cfd90]
+/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80)[0x7f7e9c0cfe40]
+postgres: ubuntu postgres [local] INSERT(_start+0x25)[0x5559b8f8f005]
+</screen>
+    One can obtain the file name and line number from the logged details by
+    using <productname>GDB</productname> or
+    <productname>addr2line</productname> in most Linux systems.
+   </para>
+
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 346cfb98a0..3d8ea385fb 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -757,6 +757,8 @@ REVOKE EXECUTE ON FUNCTION pg_ls_logicalmapdir() FROM PUBLIC;
 
 REVOKE EXECUTE ON FUNCTION pg_ls_replslotdir(text) FROM PUBLIC;
 
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer) FROM PUBLIC;
+
 --
 -- We also set up some things as accessible to standard roles.
 --
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 2c3099f76f..b404049b15 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -479,6 +479,8 @@ AutoVacLauncherMain(int argc, char *argv[])
 
 	SetProcessingMode(NormalProcessing);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * Create a memory context that we will do all our work in.  We do this so
 	 * that we can reset the context during error recovery and thereby avoid
@@ -1534,6 +1536,8 @@ AutoVacWorkerMain(int argc, char *argv[])
 	/* Early initialization */
 	BaseInit();
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * If an exception is encountered, processing resumes here.
 	 *
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 67f92c24db..3b989990fb 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -772,6 +772,8 @@ BackgroundWorkerMain(void)
 	pqsignal(SIGUSR2, SIG_IGN);
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * If an exception is encountered, processing resumes here.
 	 *
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index d7d6cc0cd7..4f6f62b404 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -112,6 +112,8 @@ BackgroundWriterMain(void)
 	 */
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * We just started, assume there has been either a shutdown or
 	 * end-of-recovery snapshot.
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 5e949fc885..26d1367909 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -198,6 +198,8 @@ CheckpointerMain(void)
 	 */
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * Initialize so that first time-driven event happens at the correct time.
 	 */
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 67693b0580..9fa476974a 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -232,6 +232,8 @@ PgArchiverMain(void)
 	/* Unblock signals (they were blocked when the postmaster forked us) */
 	sigprocmask(SIG_SETMASK, &UnBlockSig, NULL);
 
+	LoadBacktraceFunctions();
+
 	/* We shouldn't be launched unnecessarily. */
 	Assert(XLogArchivingActive());
 
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index d53c37d062..87bf59ce9e 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -258,6 +258,8 @@ StartupProcessMain(void)
 	pqsignal(SIGUSR1, procsignal_sigusr1_handler);
 	pqsignal(SIGUSR2, StartupProcTriggerHandler);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * Reset some signals that are accepted by postmaster but not here
 	 */
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 75c9f8707b..5b2e643c94 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -112,6 +112,8 @@ WalWriterMain(void)
 	 */
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/*
 	 * Create a memory context that we will do all our work in.  We do this so
 	 * that we can reset the context during error recovery and thereby avoid
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 728059518e..623b162244 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -287,6 +287,8 @@ WalReceiverMain(void)
 	/* Reset some signals that are accepted by postmaster but not here */
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	LoadBacktraceFunctions();
+
 	/* Load the libpq-specific functions */
 	load_file("libpqwalreceiver", false);
 	if (WalReceiverFunctions == NULL)
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index aa80f3de20..33cec58db8 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -3430,6 +3430,8 @@ WalSndSignals(void)
 
 	/* Reset some signals that are accepted by postmaster but not here */
 	pqsignal(SIGCHLD, SIG_DFL);
+
+	LoadBacktraceFunctions();
 }
 
 /* Report shared-memory space needed by WalSndShmemInit */
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index e84619e5a5..32f1e27750 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -16,6 +16,9 @@
 
 #include <signal.h>
 #include <unistd.h>
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
 
 #include "access/parallel.h"
 #include "port/pg_bitutils.h"
@@ -97,6 +100,10 @@ typedef struct
 #define BARRIER_CLEAR_BIT(flags, type) \
 	((flags) &= ~(((uint32) 1) << (uint32) (type)))
 
+#ifdef HAVE_BACKTRACE_SYMBOLS
+static bool backtrace_functions_loaded = false;
+#endif
+
 static ProcSignalHeader *ProcSignal = NULL;
 static ProcSignalSlot *MyProcSignalSlot = NULL;
 
@@ -609,6 +616,70 @@ ResetProcSignalBarrierBits(uint32 flags)
 	InterruptPending = true;
 }
 
+/*
+ * HandleLogBacktraceInterrupt - Handle receipt of an interrupt requesting to
+ * log a backtrace.
+ *
+ * We capture the backtrace within this signal handler and emit to stderr. Note
+ * that we ensured the backtrace-related functions are signal-safe, see
+ * LoadBacktraceFunctions() for more details.
+ *
+ * Emitting backtrace to stderr as opposed to writing to server log has an
+ * advantage - we don't need to allocate any dynamic memory while capturing
+ * backtrace which makes the signal handler safe.
+ */
+static void
+HandleLogBacktraceInterrupt(void)
+{
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	void	   *buf[100];
+	int			nframes;
+
+	/* Quickly exit if backtrace-related functions aren't loaded. */
+	if (!backtrace_functions_loaded)
+		return;
+
+	nframes = backtrace(buf, lengthof(buf));
+
+	write_stderr("logging current backtrace of process with PID %d:\n",
+				 MyProcPid);
+	backtrace_symbols_fd(buf, nframes, fileno(stderr));
+#endif
+}
+
+/*
+ * LoadBacktraceFunctions - call a backtrace-related function to ensure the
+ * shared library implementing them is loaded beforehand.
+ *
+ * Any backtrace-related functions when called for the first time dynamically
+ * loads the shared library, which usually triggers a call to malloc, making
+ * them unsafe to use in signal handlers.
+ *
+ * This functions is an attempt to make backtrace-related functions signal
+ * safe.
+ *
+ * NOTE: This function is supposed to be called in the early life of a process,
+ * preferably after SIGUSR1 handler is setup and before the backtrace-related
+ * functions are used in signal handlers. It is not supposed to be called from
+ * within a signal handler.
+ */
+void
+LoadBacktraceFunctions(void)
+{
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	void	   *buf[100];
+
+	/*
+	 * It is enough to call any one backtrace-related function to ensure that
+	 * the corresponding shared library is dynamically loaded if not done
+	 * already.
+	 */
+	backtrace(buf, lengthof(buf));
+
+	backtrace_functions_loaded = true;
+#endif
+}
+
 /*
  * CheckProcSignal - check to see if a particular reason has been
  * signaled, and clear the signal flag.  Should be called after receiving
@@ -661,6 +732,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_PARALLEL_APPLY_MESSAGE))
 		HandleParallelApplyMessageInterrupt();
 
+	if (CheckProcSignal(PROCSIG_LOG_BACKTRACE))
+		HandleLogBacktraceInterrupt();
+
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
 		HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
 
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index 81d1a59659..836b36e5bb 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -316,3 +316,29 @@ pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
 	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
 	PG_RETURN_BOOL(true);
 }
+
+/*
+ * pg_log_backtrace - signal a backend or an auxiliary process to log its
+ * current backtrace to stderr.
+ *
+ * By default, only superusers are allowed to request to log the backtrace
+ * which is safe from a security standpoint because the backtrace might contain
+ * internal details. However, a superuser can grant the execute permission to
+ * anyone, if it wishes.
+ */
+Datum
+pg_log_backtrace(PG_FUNCTION_ARGS)
+{
+	int			pid = PG_GETARG_INT32(0);
+	bool		result;
+
+#ifndef HAVE_BACKTRACE_SYMBOLS
+	ereport(WARNING,
+			errmsg("backtrace generation is not supported by this installation"),
+			errhint("You need to rebuild PostgreSQL using a library containing backtrace_symbols."));
+	PG_RETURN_BOOL(false);
+#endif
+
+	result = SendProcSignalBackendOrAuxproc(pid, PROCSIG_LOG_BACKTRACE);
+	PG_RETURN_BOOL(result);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 1a34bd3715..e316987db5 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -4189,6 +4189,8 @@ PostgresMain(const char *dbname, const char *username)
 		 */
 		pqsignal(SIGCHLD, SIG_DFL); /* system() requires this on some
 									 * platforms */
+
+		LoadBacktraceFunctions();
 	}
 
 	/* Early initialization */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 29af4ce65d..b13e808bcb 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -12170,4 +12170,9 @@
   proargtypes => 'int2',
   prosrc => 'gist_stratnum_identity' },
 
+# function to get the backtrace of server process
+{ oid => '9661', descr => 'log backtrace of server process',
+  proname => 'pg_log_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_log_backtrace' },
+
 ]
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index 52dcb4c2ad..76033ccafc 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -36,6 +36,7 @@ typedef enum
 	PROCSIG_BARRIER,			/* global barrier interrupt  */
 	PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
 	PROCSIG_PARALLEL_APPLY_MESSAGE, /* Message from parallel apply workers */
+	PROCSIG_LOG_BACKTRACE,		/* ask backend to log the current backtrace */
 
 	/* Recovery conflict reasons */
 	PROCSIG_RECOVERY_CONFLICT_FIRST,
@@ -71,5 +72,6 @@ extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
 
 extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
+extern void LoadBacktraceFunctions(void);
 
 #endif							/* PROCSIGNAL_H */
diff --git a/src/test/regress/expected/backtrace.out b/src/test/regress/expected/backtrace.out
new file mode 100644
index 0000000000..5341a6adfe
--- /dev/null
+++ b/src/test/regress/expected/backtrace.out
@@ -0,0 +1,49 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged to stderr and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/expected/backtrace_1.out b/src/test/regress/expected/backtrace_1.out
new file mode 100644
index 0000000000..899f330224
--- /dev/null
+++ b/src/test/regress/expected/backtrace_1.out
@@ -0,0 +1,55 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged to stderr and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+WARNING:  backtrace generation is not supported by this installation
+HINT:  You need to rebuild PostgreSQL using a library containing backtrace_symbols.
+ pg_log_backtrace 
+------------------
+ f
+(1 row)
+
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 1d8a414eea..9ad90d725b 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -78,7 +78,7 @@ test: brin_bloom brin_multi
 # psql depends on create_am
 # amutils depends on geometry, create_index_spgist, hash_index, brin
 # ----------
-test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role without_overlaps
+test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role without_overlaps backtrace
 
 # collate.*.utf8 tests cannot be run in parallel with each other
 test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 collate.windows.win1252
diff --git a/src/test/regress/sql/backtrace.sql b/src/test/regress/sql/backtrace.sql
new file mode 100644
index 0000000000..674e41c998
--- /dev/null
+++ b/src/test/regress/sql/backtrace.sql
@@ -0,0 +1,33 @@
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged to stderr and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can at least verify that the code doesn't fail, and that the
+-- permissions are set properly.
+--
+
+SELECT pg_log_backtrace(pg_backend_pid());
+
+SELECT pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type = 'checkpointer';
+
+CREATE ROLE regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+
+SET ROLE regress_log_backtrace;
+SELECT pg_log_backtrace(pg_backend_pid());
+RESET ROLE;
+
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+
+DROP ROLE regress_log_backtrace;
-- 
2.34.1

#118Michael Paquier
michael@paquier.xyz
In reply to: Bharath Rupireddy (#117)
Re: Printing backtrace of postgres processes

On Fri, Jan 26, 2024 at 11:58:00PM +0530, Bharath Rupireddy wrote:

You probably were looking at v21, the above change isn't there in
versions after that. Can you please review the latest version v26
attached here?

We might want this patch extended to the newly added walsummarizer
process which I'll do so in the next version.

--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
+bool
+SendProcSignalBackendOrAuxproc(int pid, ProcSignalReason reason)
+{

Looking at 0001. This API is a thin wrapper of SendProcSignal(), that
just checks that we have actually a process before using it.
Shouldn't it be in procsignal.c.

Now looking at 0002, this new routine is used in one place. Seeing
that we have something similar in pgstatfuncs.c, wouldn't it be
better, instead of englobing SendProcSignal(), to have one routine
that's able to return a PID for a PostgreSQL process?

All the backtrace-related handling is stored in procsignal.c, could it
be cleaner to move the internals into a separate, new file, like
procbacktrace.c or something similar?

LoadBacktraceFunctions() is one more thing we need to set up in all
auxiliary processes. That's a bit sad to require that in all these
places, and we may forget to add it. Could we put more efforts in
centralizing these initializations? The auxiliary processes are one
portion of the problem, and getting stack traces for backend processes
would be useful on its own. Another suggestion that I'd propose to
simplify the patch would be to focus solely on backends for now, and
do something for auxiliary process later on. If you do that, the
strange layer with BackendOrAuxproc() is not a problem anymore, as it
would be left out for now.

+<screen>
+logging current backtrace of process with PID 3499242:
+postgres: ubuntu postgres [local] INSERT(+0x61a355)[0x5559b94de355]
+postgres: ubuntu postgres [local] INSERT(procsignal_sigusr1_handler+0x9e)[0x5559b94de4ef]

This is IMO too much details for the documentation, and could be
confusing depending on how the code evolves in the future. I'd
suggest to keep it minimal, cutting that to a few lines. I don't see
a need to mention ubuntu, either.
--
Michael

#119Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: Michael Paquier (#118)
1 attachment(s)
Re: Printing backtrace of postgres processes

On Tue, Feb 6, 2024 at 4:21 PM Michael Paquier <michael@paquier.xyz> wrote:

--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
+bool
+SendProcSignalBackendOrAuxproc(int pid, ProcSignalReason reason)
+{

Looking at 0001. This API is a thin wrapper of SendProcSignal(), that
just checks that we have actually a process before using it.
Shouldn't it be in procsignal.c.

Now looking at 0002, this new routine is used in one place. Seeing
that we have something similar in pgstatfuncs.c, wouldn't it be
better, instead of englobing SendProcSignal(), to have one routine
that's able to return a PID for a PostgreSQL process?

I liked the idea of going ahead with logging backtraces for only
backends for now, so a separate wrapper like this isn't needed.

All the backtrace-related handling is stored in procsignal.c, could it
be cleaner to move the internals into a separate, new file, like
procbacktrace.c or something similar?

+1. Moved all the code to a new file.

LoadBacktraceFunctions() is one more thing we need to set up in all
auxiliary processes. That's a bit sad to require that in all these
places, and we may forget to add it. Could we put more efforts in
centralizing these initializations?

If we were to do it for only backends (including bg workers)
InitProcess() is the better place. If we were to do it for both
backends and auxiliary processes, BaseInit() is best.

The auxiliary processes are one
portion of the problem, and getting stack traces for backend processes
would be useful on its own. Another suggestion that I'd propose to
simplify the patch would be to focus solely on backends for now, and
do something for auxiliary process later on. If you do that, the
strange layer with BackendOrAuxproc() is not a problem anymore, as it
would be left out for now.

+1 to keep it simple for now; that is, log backtraces of only backends
leaving auxiliary processes aside.

+<screen>
+logging current backtrace of process with PID 3499242:
+postgres: ubuntu postgres [local] INSERT(+0x61a355)[0x5559b94de355]
+postgres: ubuntu postgres [local] INSERT(procsignal_sigusr1_handler+0x9e)[0x5559b94de4ef]

This is IMO too much details for the documentation, and could be
confusing depending on how the code evolves in the future. I'd
suggest to keep it minimal, cutting that to a few lines. I don't see
a need to mention ubuntu, either.

Well, that 'ubuntu' is the default username there, I've changed it now
and kept the output short.

I've simplified the tests, now we don't need two separate output files
for tests. Please see the attached v27 patch.

--
Bharath Rupireddy
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com

Attachments:

v27-0001-Add-function-to-log-backtrace-of-a-backend.patchapplication/x-patch; name=v27-0001-Add-function-to-log-backtrace-of-a-backend.patchDownload
From e44351d85f6415c0649c18e00dc92368be0bc1d6 Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Wed, 7 Feb 2024 07:46:05 +0000
Subject: [PATCH v27] Add function to log backtrace of a backend

---
 doc/src/sgml/func.sgml                       |  53 +++++++
 src/backend/catalog/system_functions.sql     |   2 +
 src/backend/storage/ipc/Makefile             |   1 +
 src/backend/storage/ipc/meson.build          |   1 +
 src/backend/storage/ipc/procbacktrace.c      | 143 +++++++++++++++++++
 src/backend/storage/ipc/procsignal.c         |   4 +
 src/backend/storage/lmgr/proc.c              |   3 +
 src/include/catalog/pg_proc.dat              |   4 +
 src/include/storage/procbacktrace.h          |  20 +++
 src/include/storage/procsignal.h             |   1 +
 src/test/regress/expected/misc_functions.out |  51 +++++++
 src/test/regress/sql/misc_functions.sql      |  41 ++++++
 12 files changed, 324 insertions(+)
 create mode 100644 src/backend/storage/ipc/procbacktrace.c
 create mode 100644 src/include/storage/procbacktrace.h

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 6788ba8ef4..4d5a31b113 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -27186,6 +27186,24 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_log_backtrace</primary>
+        </indexterm>
+        <function>pg_log_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </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.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -27300,6 +27318,41 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_log_backtrace</function> can be used to log the backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace
+------------------
+ t
+(1 row)
+</programlisting>
+The backtrace will be logged as specified by the logging configuration.
+For example:
+<screen>
+logging current backtrace of process with PID 174542:
+postgres: myuser mydb [local] ALTER TABLE(HandleLogBacktraceInterrupt+0x3c)[0x55fb151249ce]
+postgres: myuser postgres [local] ALTER TABLE(procsignal_sigusr1_handler+0x9e)[0x55fb15125cbc]
+/lib/x86_64-linux-gnu/libc.so.6(+0x42520)[0x7fd572842520]
+/lib/x86_64-linux-gnu/libc.so.6(fsync+0x17)[0x7fd57291b887]
+postgres: myuser mydb [local] ALTER TABLE(pg_fsync_no_writethrough+0x2f)[0x55fb1510d18b]
+postgres: myuser mydb [local] ALTER TABLE(pg_fsync+0xe0)[0x55fb1510d146]
+postgres: myuser mydb [local] ALTER TABLE(FileSync+0xb2)[0x55fb1510fad6]
+postgres: myuser mydb [local] ALTER TABLE(mdimmedsync+0x9e)[0x55fb1515040a]
+postgres: myuser mydb [local] ALTER TABLE(smgrimmedsync+0x2b)[0x55fb1515218d]
+postgres: myuser mydb [local] ALTER TABLE(+0x183d09)[0x55fb14c4ed09]
+postgres: myuser mydb [local] ALTER TABLE(+0x18243a)[0x55fb14c4d43a]
+postgres: myuser mydb [local] ALTER TABLE(btbuild+0x112)[0x55fb14c4ce69]
+postgres: myuser mydb [local] ALTER TABLE(index_build+0x331)[0x55fb14cef750]
+postgres: myuser mydb [local] ALTER TABLE(index_create+0x1212)[0x55fb14cec1ea]
+postgres: myuser mydb [local] ALTER TABLE(DefineIndex+0x1a73)[0x55fb14df8f7e]
+...
+</screen>
+    One can obtain the file name and line number from the logged details by
+    using <productname>GDB</productname> or
+    <productname>addr2line</productname> in most Linux systems.
+   </para>
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 346cfb98a0..3d8ea385fb 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -757,6 +757,8 @@ REVOKE EXECUTE ON FUNCTION pg_ls_logicalmapdir() FROM PUBLIC;
 
 REVOKE EXECUTE ON FUNCTION pg_ls_replslotdir(text) FROM PUBLIC;
 
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer) FROM PUBLIC;
+
 --
 -- We also set up some things as accessible to standard roles.
 --
diff --git a/src/backend/storage/ipc/Makefile b/src/backend/storage/ipc/Makefile
index d8a1653eb6..4647916ea6 100644
--- a/src/backend/storage/ipc/Makefile
+++ b/src/backend/storage/ipc/Makefile
@@ -18,6 +18,7 @@ OBJS = \
 	latch.o \
 	pmsignal.o \
 	procarray.o \
+	procbacktrace.o \
 	procsignal.o \
 	shm_mq.o \
 	shm_toc.o \
diff --git a/src/backend/storage/ipc/meson.build b/src/backend/storage/ipc/meson.build
index 5a936171f7..7b38cdd3f0 100644
--- a/src/backend/storage/ipc/meson.build
+++ b/src/backend/storage/ipc/meson.build
@@ -10,6 +10,7 @@ backend_sources += files(
   'latch.c',
   'pmsignal.c',
   'procarray.c',
+  'procbacktrace.c',
   'procsignal.c',
   'shm_mq.c',
   'shm_toc.c',
diff --git a/src/backend/storage/ipc/procbacktrace.c b/src/backend/storage/ipc/procbacktrace.c
new file mode 100644
index 0000000000..ffb93756c9
--- /dev/null
+++ b/src/backend/storage/ipc/procbacktrace.c
@@ -0,0 +1,143 @@
+/*-------------------------------------------------------------------------
+ *
+ * procbacktrace.c
+ *	  Backtrace-related routines
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/storage/ipc/procbacktrace.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
+
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "storage/procbacktrace.h"
+#include "storage/procsignal.h"
+#include "utils/fmgrprotos.h"
+
+static volatile sig_atomic_t backtrace_functions_loaded = false;
+
+/*
+ * Handle receipt of an interrupt indicating logging of current backtrace.
+ *
+ * We capture the backtrace within this signal handler itself in a safe manner.
+ * It is ensured that no memory allocations happen here. This function emits
+ * backtrace to stderr as writing to server log allocates memory, making the
+ * signal halder unsafe. Also, the shared library implementing the
+ * backtrace-related functions is preloaded to avoid memory allocations upon
+ * first-time loading, see LoadBacktraceFunctions.
+ */
+void
+HandleLogBacktraceInterrupt(void)
+{
+	void	   *buf[100];
+	int			nframes;
+
+#ifndef HAVE_BACKTRACE_SYMBOLS
+	return;
+#endif
+
+	if (!backtrace_functions_loaded)
+		return;
+
+	nframes = backtrace(buf, lengthof(buf));
+
+	write_stderr("logging current backtrace of process with PID %d:\n",
+				 MyProcPid);
+	backtrace_symbols_fd(buf, nframes, fileno(stderr));
+}
+
+/*
+ * Load backtrace shared library.
+ *
+ * Any backtrace-related functions when called for the first time dynamically
+ * loads the shared library, which usually triggers a call to malloc, making
+ * them unsafe to use in signal handlers. This function makes backtrace-related
+ * functions signal-safe.
+ *
+ * Note that this function is supposed to be called in the early life of a
+ * process, preferably after signal handlers are setup; but not from within a
+ * signal handler.
+ */
+void
+LoadBacktraceFunctions(void)
+{
+	void	   *buf[2];
+
+#ifndef HAVE_BACKTRACE_SYMBOLS
+	return;
+#endif
+
+	if (backtrace_functions_loaded)
+		return;
+
+	/*
+	 * It is enough to call any one backtrace-related function to ensure that
+	 * the corresponding shared library is dynamically loaded. We just load
+	 * two most recent function calls, as we don't use the backtrace anyway.
+	 */
+	backtrace(buf, lengthof(buf));
+
+	backtrace_functions_loaded = true;
+}
+
+/*
+ * Signal a backend to log its current backtrace.
+ */
+Datum
+pg_log_backtrace(PG_FUNCTION_ARGS)
+{
+	int			pid = PG_GETARG_INT32(0);
+	PGPROC	   *proc;
+
+#ifndef HAVE_BACKTRACE_SYMBOLS
+	ereport(WARNING,
+			errmsg("backtrace generation is not supported by this installation"),
+			errhint("You need to rebuild PostgreSQL using a library containing backtrace_symbols."));
+	PG_RETURN_BOOL(false);
+#endif
+
+	proc = BackendPidGetProc(pid);
+
+	/*
+	 * BackendPidGetProc returns NULL if the pid isn't valid.
+	 *
+	 * 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_backtrace() to get backtrace).
+	 *
+	 * XXX: It might be worth implementing it for auxiliary processes.
+	 */
+	if (proc == NULL)
+	{
+		/*
+		 * This is just a warning so a loop-through-resultset will not abort
+		 * if one backend terminated on its own during the run.
+		 */
+		ereport(WARNING,
+				(errmsg("PID %d is not a PostgreSQL backend process", pid)));
+
+		PG_RETURN_BOOL(false);
+	}
+
+	if (SendProcSignal(pid, PROCSIG_LOG_BACKTRACE, proc->backendId) < 0)
+	{
+		/* Again, just a warning to allow loops */
+		ereport(WARNING,
+				(errmsg("could not send signal to process %d: %m", pid)));
+		PG_RETURN_BOOL(false);
+	}
+
+	PG_RETURN_BOOL(true);
+}
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index e84619e5a5..3f1ce745af 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -28,6 +28,7 @@
 #include "storage/ipc.h"
 #include "storage/latch.h"
 #include "storage/proc.h"
+#include "storage/procbacktrace.h"
 #include "storage/shmem.h"
 #include "storage/smgr.h"
 #include "storage/sinval.h"
@@ -658,6 +659,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_LOG_MEMORY_CONTEXT))
 		HandleLogMemoryContextInterrupt();
 
+	if (CheckProcSignal(PROCSIG_LOG_BACKTRACE))
+		HandleLogBacktraceInterrupt();
+
 	if (CheckProcSignal(PROCSIG_PARALLEL_APPLY_MESSAGE))
 		HandleParallelApplyMessageInterrupt();
 
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index e5977548fe..73e0368c29 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -48,6 +48,7 @@
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
+#include "storage/procbacktrace.h"
 #include "storage/procsignal.h"
 #include "storage/spin.h"
 #include "storage/standby.h"
@@ -462,6 +463,8 @@ InitProcess(void)
 	InitLWLockAccess();
 	InitDeadLockChecking();
 
+	LoadBacktraceFunctions();
+
 #ifdef EXEC_BACKEND
 
 	/*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 29af4ce65d..17b95f3f35 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -12170,4 +12170,8 @@
   proargtypes => 'int2',
   prosrc => 'gist_stratnum_identity' },
 
+# function to get the backtrace of server process
+{ oid => '9661', descr => 'log backtrace of server process',
+  proname => 'pg_log_backtrace', provolatile => 'v', prorettype => 'bool',
+  proargtypes => 'int4', prosrc => 'pg_log_backtrace' },
 ]
diff --git a/src/include/storage/procbacktrace.h b/src/include/storage/procbacktrace.h
new file mode 100644
index 0000000000..884bdb34ad
--- /dev/null
+++ b/src/include/storage/procbacktrace.h
@@ -0,0 +1,20 @@
+/*-------------------------------------------------------------------------
+ *
+ * procsignal.h
+ *	  Backtrace-related routines
+ *
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/storage/procbacktrace.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PROCBACKTRACE_H
+#define PROCBACKTRACE_H
+
+extern void HandleLogBacktraceInterrupt(void);
+extern void LoadBacktraceFunctions(void);
+
+#endif							/* PROCBACKTRACE_H */
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index 52dcb4c2ad..a387da8379 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -36,6 +36,7 @@ typedef enum
 	PROCSIG_BARRIER,			/* global barrier interrupt  */
 	PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
 	PROCSIG_PARALLEL_APPLY_MESSAGE, /* Message from parallel apply workers */
+	PROCSIG_LOG_BACKTRACE,		/* ask backend to log the current backtrace */
 
 	/* Recovery conflict reasons */
 	PROCSIG_RECOVERY_CONFLICT_FIRST,
diff --git a/src/test/regress/expected/misc_functions.out b/src/test/regress/expected/misc_functions.out
index 7c15477104..92328a4f21 100644
--- a/src/test/regress/expected/misc_functions.out
+++ b/src/test/regress/expected/misc_functions.out
@@ -326,6 +326,57 @@ REVOKE EXECUTE ON FUNCTION pg_log_backend_memory_contexts(integer)
   FROM regress_log_memory;
 DROP ROLE regress_log_memory;
 --
+-- pg_log_backtrace()
+--
+-- Backtraces are logged to stderr and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can verify that the code doesn't fail, and that the permissions are
+-- set properly.
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+-- Suppress warnings that depend on dynamic content i.e. PID or backtrace
+-- generation support.
+SET client_min_messages = 'ERROR';
+SET ROLE regress_log_backtrace;
+-- Backtrace is logged only for backends
+SELECT pg_log_backtrace(pg_backend_pid());
+ pg_log_backtrace 
+------------------
+ t
+(1 row)
+
+RESET ROLE;
+-- Backtrace is not logged for auxiliary processes
+SELECT backend_type, pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type IN ('checkpointer', 'walwriter', 'background writer')
+    ORDER BY backend_type;
+   backend_type    | pg_log_backtrace 
+-------------------+------------------
+ background writer | f
+ checkpointer      | f
+ walwriter         | f
+(3 rows)
+
+RESET client_min_messages;
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
+--
 -- Test some built-in SRFs
 --
 -- The outputs of these are variable, so we can't just print their results
diff --git a/src/test/regress/sql/misc_functions.sql b/src/test/regress/sql/misc_functions.sql
index 851dad90f4..9931c384a6 100644
--- a/src/test/regress/sql/misc_functions.sql
+++ b/src/test/regress/sql/misc_functions.sql
@@ -102,6 +102,47 @@ REVOKE EXECUTE ON FUNCTION pg_log_backend_memory_contexts(integer)
 
 DROP ROLE regress_log_memory;
 
+--
+-- pg_log_backtrace()
+--
+-- Backtraces are logged to stderr and not returned to the function.
+-- Furthermore, their contents can vary depending on the timing. However,
+-- we can verify that the code doesn't fail, and that the permissions are
+-- set properly.
+CREATE ROLE regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- no
+
+GRANT EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  TO regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backtrace(integer)', 'EXECUTE'); -- yes
+
+-- Suppress warnings that depend on dynamic content i.e. PID or backtrace
+-- generation support.
+SET client_min_messages = 'ERROR';
+
+SET ROLE regress_log_backtrace;
+
+-- Backtrace is logged only for backends
+SELECT pg_log_backtrace(pg_backend_pid());
+
+RESET ROLE;
+
+-- Backtrace is not logged for auxiliary processes
+SELECT backend_type, pg_log_backtrace(pid) FROM pg_stat_activity
+  WHERE backend_type IN ('checkpointer', 'walwriter', 'background writer')
+    ORDER BY backend_type;
+
+RESET client_min_messages;
+
+REVOKE EXECUTE ON FUNCTION pg_log_backtrace(integer)
+  FROM regress_log_backtrace;
+
+DROP ROLE regress_log_backtrace;
+
 --
 -- Test some built-in SRFs
 --
-- 
2.34.1

#120Michael Paquier
michael@paquier.xyz
In reply to: Bharath Rupireddy (#119)
Re: Printing backtrace of postgres processes

On Wed, Feb 07, 2024 at 02:04:39PM +0530, Bharath Rupireddy wrote:

Well, that 'ubuntu' is the default username there, I've changed it now
and kept the output short.

I would keep it just at two or three lines, with a "For example, with
lines like":

I've simplified the tests, now we don't need two separate output files
for tests. Please see the attached v27 patch.

+ proname => 'pg_log_backtrace', provolatile => 'v', prorettype => 'bool',

Hmm. Would it be better to be in line with memory contexts logging
and use pg_log_backend_backtrace()? One thing I was wondering is that
there may be a good point in splitting the backtrace support into two
functions (backends and auxiliary processes) that could be split with
two execution ACLs across different roles.

+ PROCSIG_LOG_BACKTRACE, /* ask backend to log the current backtrace */

Incorrect order.

+-- Backtrace is not logged for auxiliary processes

Not sure there's a point in keeping that in the tests for now.

+ * XXX: It might be worth implementing it for auxiliary processes.

Same, I would remove that.

+static volatile sig_atomic_t backtrace_functions_loaded = false;

Hmm, so you need that because of the fact that it would be called in a
signal as backtrace(3) says:
"If you need certain calls to these two functions to not allocate
memory (in signal handlers, for example), you need to make sure libgcc
is loaded beforehand".

True that it is not interesting to only log something when having a
CFI, this needs to be dynamic to get a precise state of things.
--
Michael

#121Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: Michael Paquier (#120)
2 attachment(s)
Re: Printing backtrace of postgres processes

On Wed, Feb 7, 2024 at 2:57 PM Michael Paquier <michael@paquier.xyz> wrote:

On Wed, Feb 07, 2024 at 02:04:39PM +0530, Bharath Rupireddy wrote:

Well, that 'ubuntu' is the default username there, I've changed it now
and kept the output short.

I would keep it just at two or three lines, with a "For example, with
lines like":

Done.

I've simplified the tests, now we don't need two separate output files
for tests. Please see the attached v27 patch.

+ proname => 'pg_log_backtrace', provolatile => 'v', prorettype => 'bool',

Hmm. Would it be better to be in line with memory contexts logging
and use pg_log_backend_backtrace()?

+1.

One thing I was wondering is that
there may be a good point in splitting the backtrace support into two
functions (backends and auxiliary processes) that could be split with
two execution ACLs across different roles.

-1 for that unless we have any requests. I mean, backtrace is a common
thing for all postgres processes, why different controls are needed?
I'd go with what pg_log_backend_memory_contexts does - it supports
both backends and auxiliary processes.

+ PROCSIG_LOG_BACKTRACE, /* ask backend to log the current backtrace */

Incorrect order.

PROCSIG_XXX aren't alphabetically ordered, no?

+-- Backtrace is not logged for auxiliary processes

Not sure there's a point in keeping that in the tests for now.

+ * XXX: It might be worth implementing it for auxiliary processes.

Same, I would remove that.

Done.

+static volatile sig_atomic_t backtrace_functions_loaded = false;

Hmm, so you need that because of the fact that it would be called in a
signal as backtrace(3) says:
"If you need certain calls to these two functions to not allocate
memory (in signal handlers, for example), you need to make sure libgcc
is loaded beforehand".

True that it is not interesting to only log something when having a
CFI, this needs to be dynamic to get a precise state of things.

Right.

I've also fixed some test failures. Please see the attached v28 patch
set. 0002 extends pg_log_backend_backtrace to auxiliary processes,
just like pg_log_backend_memory_contexts (not focused on PID
de-duplication code yet).

--
Bharath Rupireddy
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com

Attachments:

v28-0002-Extend-backtrace-logging-function-to-auxiliary-p.patchapplication/octet-stream; name=v28-0002-Extend-backtrace-logging-function-to-auxiliary-p.patchDownload
From 3cf748bb3dd891ae0a478698cfcef93b95a3eed0 Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Wed, 7 Feb 2024 14:39:14 +0000
Subject: [PATCH v28 2/2] Extend backtrace logging function to auxiliary
 processes

---
 doc/src/sgml/func.sgml                       | 12 ++++---
 src/backend/storage/ipc/procbacktrace.c      | 35 ++++++++++++++------
 src/test/regress/expected/misc_functions.out |  8 +++++
 src/test/regress/sql/misc_functions.sql      |  3 ++
 4 files changed, 43 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/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

v28-0001-Add-function-to-log-backtrace-of-a-backend.patchapplication/octet-stream; name=v28-0001-Add-function-to-log-backtrace-of-a-backend.patchDownload
From 74c77805ae1f76a192e98ab88feb3947f3712e98 Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Wed, 7 Feb 2024 14:26:00 +0000
Subject: [PATCH v28 1/2] Add function to log backtrace of a backend

---
 doc/src/sgml/func.sgml                       |  45 ++++++
 src/backend/catalog/system_functions.sql     |   2 +
 src/backend/storage/ipc/Makefile             |   1 +
 src/backend/storage/ipc/meson.build          |   1 +
 src/backend/storage/ipc/procbacktrace.c      | 149 +++++++++++++++++++
 src/backend/storage/ipc/procsignal.c         |   4 +
 src/backend/storage/lmgr/proc.c              |   3 +
 src/include/catalog/pg_proc.dat              |   6 +
 src/include/storage/procbacktrace.h          |  20 +++
 src/include/storage/procsignal.h             |   1 +
 src/test/regress/expected/misc_functions.out |  49 ++++++
 src/test/regress/sql/misc_functions.sql      |  40 +++++
 12 files changed, 321 insertions(+)
 create mode 100644 src/backend/storage/ipc/procbacktrace.c
 create mode 100644 src/include/storage/procbacktrace.h

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 6788ba8ef4..b7d7305331 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -27186,6 +27186,24 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_log_backend_backtrace</primary>
+        </indexterm>
+        <function>pg_log_backend_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </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.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -27300,6 +27318,33 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_log_backend_backtrace</function> can be used to log the backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_log_backend_backtrace(pg_backend_pid());
+ pg_log_backend_backtrace
+------------------
+ t
+(1 row)
+</programlisting>
+The backtrace will be logged as specified by the logging configuration.
+For example:
+<screen>
+logging current backtrace of process with PID 174542:
+postgres: myuser mydb [local] ALTER TABLE(HandleLogBacktraceInterrupt+0x3c)[0x55fb151249ce]
+postgres: myuser postgres [local] ALTER TABLE(procsignal_sigusr1_handler+0x9e)[0x55fb15125cbc]
+/lib/x86_64-linux-gnu/libc.so.6(+0x42520)[0x7fd572842520]
+/lib/x86_64-linux-gnu/libc.so.6(fsync+0x17)[0x7fd57291b887]
+postgres: myuser mydb [local] ALTER TABLE(pg_fsync_no_writethrough+0x2f)[0x55fb1510d18b]
+postgres: myuser mydb [local] ALTER TABLE(pg_fsync+0xe0)[0x55fb1510d146]
+postgres: myuser mydb [local] ALTER TABLE(FileSync+0xb2)[0x55fb1510fad6]
+...
+</screen>
+    One can obtain the file name and line number from the logged details by
+    using <productname>GDB</productname> or
+    <productname>addr2line</productname> in most Linux systems.
+   </para>
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 346cfb98a0..f4c6334bb9 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -751,6 +751,8 @@ REVOKE EXECUTE ON FUNCTION pg_ls_dir(text,boolean,boolean) FROM public;
 
 REVOKE EXECUTE ON FUNCTION pg_log_backend_memory_contexts(integer) FROM PUBLIC;
 
+REVOKE EXECUTE ON FUNCTION pg_log_backend_backtrace(integer) FROM PUBLIC;
+
 REVOKE EXECUTE ON FUNCTION pg_ls_logicalsnapdir() FROM PUBLIC;
 
 REVOKE EXECUTE ON FUNCTION pg_ls_logicalmapdir() FROM PUBLIC;
diff --git a/src/backend/storage/ipc/Makefile b/src/backend/storage/ipc/Makefile
index d8a1653eb6..4647916ea6 100644
--- a/src/backend/storage/ipc/Makefile
+++ b/src/backend/storage/ipc/Makefile
@@ -18,6 +18,7 @@ OBJS = \
 	latch.o \
 	pmsignal.o \
 	procarray.o \
+	procbacktrace.o \
 	procsignal.o \
 	shm_mq.o \
 	shm_toc.o \
diff --git a/src/backend/storage/ipc/meson.build b/src/backend/storage/ipc/meson.build
index 5a936171f7..7b38cdd3f0 100644
--- a/src/backend/storage/ipc/meson.build
+++ b/src/backend/storage/ipc/meson.build
@@ -10,6 +10,7 @@ backend_sources += files(
   'latch.c',
   'pmsignal.c',
   'procarray.c',
+  'procbacktrace.c',
   'procsignal.c',
   'shm_mq.c',
   'shm_toc.c',
diff --git a/src/backend/storage/ipc/procbacktrace.c b/src/backend/storage/ipc/procbacktrace.c
new file mode 100644
index 0000000000..bfab5783da
--- /dev/null
+++ b/src/backend/storage/ipc/procbacktrace.c
@@ -0,0 +1,149 @@
+/*-------------------------------------------------------------------------
+ *
+ * procbacktrace.c
+ *	  Backtrace-related routines
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/storage/ipc/procbacktrace.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
+
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "storage/procbacktrace.h"
+#include "storage/procsignal.h"
+#include "utils/fmgrprotos.h"
+
+#ifdef HAVE_EXECINFO_H
+static volatile sig_atomic_t backtrace_functions_loaded = false;
+#endif
+
+/*
+ * Handle receipt of an interrupt indicating logging of current backtrace.
+ *
+ * We capture the backtrace within this signal handler itself in a safe manner.
+ * It is ensured that no memory allocations happen here. This function emits
+ * backtrace to stderr as writing to server log allocates memory, making the
+ * signal halder unsafe. Also, the shared library implementing the
+ * backtrace-related functions is preloaded to avoid memory allocations upon
+ * first-time loading, see LoadBacktraceFunctions.
+ */
+void
+HandleLogBacktraceInterrupt(void)
+{
+#ifdef HAVE_EXECINFO_H
+	void	   *buf[100];
+	int			nframes;
+
+	if (!backtrace_functions_loaded)
+		return;
+
+	nframes = backtrace(buf, lengthof(buf));
+
+	write_stderr("logging current backtrace of process with PID %d:\n",
+				 MyProcPid);
+	backtrace_symbols_fd(buf, nframes, fileno(stderr));
+#endif
+}
+
+/*
+ * Load backtrace shared library.
+ *
+ * Any backtrace-related functions when called for the first time dynamically
+ * loads the shared library, which usually triggers a call to malloc, making
+ * them unsafe to use in signal handlers. This function makes backtrace-related
+ * functions signal-safe.
+ *
+ * Note that this function is supposed to be called in the early life of a
+ * process, preferably after signal handlers are setup; but not from within a
+ * signal handler.
+ */
+void
+LoadBacktraceFunctions(void)
+{
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	void	   *buf[2];
+
+	if (backtrace_functions_loaded)
+		return;
+
+	/*
+	 * It is enough to call any one backtrace-related function to ensure that
+	 * the corresponding shared library is dynamically loaded. We just load
+	 * two most recent function calls, as we don't use the backtrace anyway.
+	 */
+	backtrace(buf, lengthof(buf));
+
+	backtrace_functions_loaded = true;
+#endif
+}
+
+/*
+ * pg_log_backend_backtrace
+ *		Signal a backend 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.
+ */
+Datum
+pg_log_backend_backtrace(PG_FUNCTION_ARGS)
+{
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	int			pid = PG_GETARG_INT32(0);
+	PGPROC	   *proc;
+
+	proc = BackendPidGetProc(pid);
+
+	/*
+	 * BackendPidGetProc returns NULL if the pid isn't valid.
+	 *
+	 * 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 (proc == NULL)
+	{
+		/*
+		 * This is just a warning so a loop-through-resultset will not abort
+		 * if one backend terminated on its own during the run.
+		 */
+		ereport(WARNING,
+				(errmsg("PID %d is not a PostgreSQL backend process", pid)));
+
+		PG_RETURN_BOOL(false);
+	}
+
+	if (SendProcSignal(pid, PROCSIG_LOG_BACKTRACE, proc->backendId) < 0)
+	{
+		/* Again, just a warning to allow loops */
+		ereport(WARNING,
+				(errmsg("could not send signal to process %d: %m", pid)));
+		PG_RETURN_BOOL(false);
+	}
+
+	PG_RETURN_BOOL(true);
+#else
+	ereport(WARNING,
+			errmsg("backtrace generation is not supported by this installation"),
+			errhint("You need to build PostgreSQL with library containing backtrace_symbols."));
+
+	PG_RETURN_BOOL(false);
+#endif
+}
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index e84619e5a5..3f1ce745af 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -28,6 +28,7 @@
 #include "storage/ipc.h"
 #include "storage/latch.h"
 #include "storage/proc.h"
+#include "storage/procbacktrace.h"
 #include "storage/shmem.h"
 #include "storage/smgr.h"
 #include "storage/sinval.h"
@@ -658,6 +659,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_LOG_MEMORY_CONTEXT))
 		HandleLogMemoryContextInterrupt();
 
+	if (CheckProcSignal(PROCSIG_LOG_BACKTRACE))
+		HandleLogBacktraceInterrupt();
+
 	if (CheckProcSignal(PROCSIG_PARALLEL_APPLY_MESSAGE))
 		HandleParallelApplyMessageInterrupt();
 
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index e5977548fe..73e0368c29 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -48,6 +48,7 @@
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
+#include "storage/procbacktrace.h"
 #include "storage/procsignal.h"
 #include "storage/spin.h"
 #include "storage/standby.h"
@@ -462,6 +463,8 @@ InitProcess(void)
 	InitLWLockAccess();
 	InitDeadLockChecking();
 
+	LoadBacktraceFunctions();
+
 #ifdef EXEC_BACKEND
 
 	/*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 29af4ce65d..cee43a8304 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8274,6 +8274,12 @@
   prorettype => 'bool', proargtypes => 'int4',
   prosrc => 'pg_log_backend_memory_contexts' },
 
+# logging backtrace of the specified backend
+{ oid => '9661', descr => 'log backtrace of the specified backend',
+  proname => 'pg_log_backend_backtrace', provolatile => 'v',
+  prorettype => 'bool', proargtypes => 'int4',
+  prosrc => 'pg_log_backend_backtrace' },
+
 # non-persistent series generator
 { oid => '1066', descr => 'non-persistent series generator',
   proname => 'generate_series', prorows => '1000',
diff --git a/src/include/storage/procbacktrace.h b/src/include/storage/procbacktrace.h
new file mode 100644
index 0000000000..884bdb34ad
--- /dev/null
+++ b/src/include/storage/procbacktrace.h
@@ -0,0 +1,20 @@
+/*-------------------------------------------------------------------------
+ *
+ * procsignal.h
+ *	  Backtrace-related routines
+ *
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/storage/procbacktrace.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PROCBACKTRACE_H
+#define PROCBACKTRACE_H
+
+extern void HandleLogBacktraceInterrupt(void);
+extern void LoadBacktraceFunctions(void);
+
+#endif							/* PROCBACKTRACE_H */
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index 52dcb4c2ad..5772b335d6 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -35,6 +35,7 @@ typedef enum
 	PROCSIG_WALSND_INIT_STOPPING,	/* ask walsenders to prepare for shutdown  */
 	PROCSIG_BARRIER,			/* global barrier interrupt  */
 	PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
+	PROCSIG_LOG_BACKTRACE,		/* ask backend to log the current backtrace */
 	PROCSIG_PARALLEL_APPLY_MESSAGE, /* Message from parallel apply workers */
 
 	/* Recovery conflict reasons */
diff --git a/src/test/regress/expected/misc_functions.out b/src/test/regress/expected/misc_functions.out
index 7c15477104..3ff0136347 100644
--- a/src/test/regress/expected/misc_functions.out
+++ b/src/test/regress/expected/misc_functions.out
@@ -326,6 +326,55 @@ REVOKE EXECUTE ON FUNCTION pg_log_backend_memory_contexts(integer)
   FROM regress_log_memory;
 DROP ROLE regress_log_memory;
 --
+-- pg_log_backend_backtrace()
+--
+-- Backtraces are logged to stderr and not returned to the function.
+-- Furthermore, the output of this function can vary depending on backtrace
+-- generation support. However, we can at least verify that the code doesn't
+-- fail, and that the permissions are set properly.
+-- Suppress warnings that depend on dynamic output i.e. PID or backtrace
+-- generation support.
+SET client_min_messages = 'ERROR';
+SELECT count(*) > 0 AS ok FROM pg_log_backend_backtrace(pg_backend_pid());
+ ok 
+----
+ t
+(1 row)
+
+RESET client_min_messages;
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backend_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backend_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backend_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+-- Suppress warnings that depend on dynamic output i.e. PID or backtrace
+-- generation support.
+SET client_min_messages = 'ERROR';
+SELECT count(*) > 0 AS ok FROM pg_log_backend_backtrace(pg_backend_pid());
+ ok 
+----
+ t
+(1 row)
+
+RESET client_min_messages;
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backend_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
+--
 -- Test some built-in SRFs
 --
 -- The outputs of these are variable, so we can't just print their results
diff --git a/src/test/regress/sql/misc_functions.sql b/src/test/regress/sql/misc_functions.sql
index 851dad90f4..2a3f9bacaa 100644
--- a/src/test/regress/sql/misc_functions.sql
+++ b/src/test/regress/sql/misc_functions.sql
@@ -102,6 +102,46 @@ REVOKE EXECUTE ON FUNCTION pg_log_backend_memory_contexts(integer)
 
 DROP ROLE regress_log_memory;
 
+--
+-- pg_log_backend_backtrace()
+--
+-- Backtraces are logged to stderr and not returned to the function.
+-- Furthermore, the output of this function can vary depending on backtrace
+-- generation support. However, we can at least verify that the code doesn't
+-- fail, and that the permissions are set properly.
+
+-- Suppress warnings that depend on dynamic output i.e. PID or backtrace
+-- generation support.
+SET client_min_messages = 'ERROR';
+SELECT count(*) > 0 AS ok FROM pg_log_backend_backtrace(pg_backend_pid());
+RESET client_min_messages;
+
+CREATE ROLE regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backend_backtrace(integer)', 'EXECUTE'); -- no
+
+GRANT EXECUTE ON FUNCTION pg_log_backend_backtrace(integer)
+  TO regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backend_backtrace(integer)', 'EXECUTE'); -- yes
+
+SET ROLE regress_log_backtrace;
+
+-- Suppress warnings that depend on dynamic output i.e. PID or backtrace
+-- generation support.
+SET client_min_messages = 'ERROR';
+SELECT count(*) > 0 AS ok FROM pg_log_backend_backtrace(pg_backend_pid());
+RESET client_min_messages;
+
+RESET ROLE;
+
+REVOKE EXECUTE ON FUNCTION pg_log_backend_backtrace(integer)
+  FROM regress_log_backtrace;
+
+DROP ROLE regress_log_backtrace;
+
 --
 -- Test some built-in SRFs
 --
-- 
2.34.1

#122Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: Bharath Rupireddy (#121)
2 attachment(s)
Re: Printing backtrace of postgres processes

On Wed, Feb 7, 2024 at 9:00 PM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:

On Wed, Feb 7, 2024 at 2:57 PM Michael Paquier <michael@paquier.xyz> wrote:

On Wed, Feb 07, 2024 at 02:04:39PM +0530, Bharath Rupireddy wrote:

Well, that 'ubuntu' is the default username there, I've changed it now
and kept the output short.

I would keep it just at two or three lines, with a "For example, with
lines like":

Done.

I've simplified the tests, now we don't need two separate output files
for tests. Please see the attached v27 patch.

+ proname => 'pg_log_backtrace', provolatile => 'v', prorettype => 'bool',

Hmm. Would it be better to be in line with memory contexts logging
and use pg_log_backend_backtrace()?

+1.

One thing I was wondering is that
there may be a good point in splitting the backtrace support into two
functions (backends and auxiliary processes) that could be split with
two execution ACLs across different roles.

-1 for that unless we have any requests. I mean, backtrace is a common
thing for all postgres processes, why different controls are needed?
I'd go with what pg_log_backend_memory_contexts does - it supports
both backends and auxiliary processes.

+ PROCSIG_LOG_BACKTRACE, /* ask backend to log the current backtrace */

Incorrect order.

PROCSIG_XXX aren't alphabetically ordered, no?

+-- Backtrace is not logged for auxiliary processes

Not sure there's a point in keeping that in the tests for now.

+ * XXX: It might be worth implementing it for auxiliary processes.

Same, I would remove that.

Done.

+static volatile sig_atomic_t backtrace_functions_loaded = false;

Hmm, so you need that because of the fact that it would be called in a
signal as backtrace(3) says:
"If you need certain calls to these two functions to not allocate
memory (in signal handlers, for example), you need to make sure libgcc
is loaded beforehand".

True that it is not interesting to only log something when having a
CFI, this needs to be dynamic to get a precise state of things.

Right.

I've also fixed some test failures. Please see the attached v28 patch
set. 0002 extends pg_log_backend_backtrace to auxiliary processes,
just like pg_log_backend_memory_contexts (not focused on PID
de-duplication code yet).

I've missed adding LoadBacktraceFunctions() in InitAuxiliaryProcess
for 0002 patch. Please find the attached v29 patch set. Sorry for the
noise.

--
Bharath Rupireddy
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com

Attachments:

v29-0001-Add-function-to-log-backtrace-of-a-backend.patchapplication/octet-stream; name=v29-0001-Add-function-to-log-backtrace-of-a-backend.patchDownload
From b9fe5485726915c963882d1d99eb582023a9bf55 Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Wed, 7 Feb 2024 18:22:10 +0000
Subject: [PATCH v29 1/2] Add function to log backtrace of a backend

---
 doc/src/sgml/func.sgml                       |  45 ++++++
 src/backend/catalog/system_functions.sql     |   2 +
 src/backend/storage/ipc/Makefile             |   1 +
 src/backend/storage/ipc/meson.build          |   1 +
 src/backend/storage/ipc/procbacktrace.c      | 149 +++++++++++++++++++
 src/backend/storage/ipc/procsignal.c         |   4 +
 src/backend/storage/lmgr/proc.c              |   3 +
 src/include/catalog/pg_proc.dat              |   6 +
 src/include/storage/procbacktrace.h          |  20 +++
 src/include/storage/procsignal.h             |   1 +
 src/test/regress/expected/misc_functions.out |  49 ++++++
 src/test/regress/sql/misc_functions.sql      |  40 +++++
 12 files changed, 321 insertions(+)
 create mode 100644 src/backend/storage/ipc/procbacktrace.c
 create mode 100644 src/include/storage/procbacktrace.h

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 6788ba8ef4..b7d7305331 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -27186,6 +27186,24 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_log_backend_backtrace</primary>
+        </indexterm>
+        <function>pg_log_backend_backtrace</function> ( <parameter>pid</parameter> <type>integer</type> )
+        <returnvalue>boolean</returnvalue>
+       </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.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -27300,6 +27318,33 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
     because it may generate a large number of log messages.
    </para>
 
+   <para>
+    <function>pg_log_backend_backtrace</function> can be used to log the backtrace of
+    a backend process. For example:
+<programlisting>
+postgres=# select pg_log_backend_backtrace(pg_backend_pid());
+ pg_log_backend_backtrace
+------------------
+ t
+(1 row)
+</programlisting>
+The backtrace will be logged as specified by the logging configuration.
+For example:
+<screen>
+logging current backtrace of process with PID 174542:
+postgres: myuser mydb [local] ALTER TABLE(HandleLogBacktraceInterrupt+0x3c)[0x55fb151249ce]
+postgres: myuser postgres [local] ALTER TABLE(procsignal_sigusr1_handler+0x9e)[0x55fb15125cbc]
+/lib/x86_64-linux-gnu/libc.so.6(+0x42520)[0x7fd572842520]
+/lib/x86_64-linux-gnu/libc.so.6(fsync+0x17)[0x7fd57291b887]
+postgres: myuser mydb [local] ALTER TABLE(pg_fsync_no_writethrough+0x2f)[0x55fb1510d18b]
+postgres: myuser mydb [local] ALTER TABLE(pg_fsync+0xe0)[0x55fb1510d146]
+postgres: myuser mydb [local] ALTER TABLE(FileSync+0xb2)[0x55fb1510fad6]
+...
+</screen>
+    One can obtain the file name and line number from the logged details by
+    using <productname>GDB</productname> or
+    <productname>addr2line</productname> in most Linux systems.
+   </para>
   </sect2>
 
   <sect2 id="functions-admin-backup">
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 346cfb98a0..f4c6334bb9 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -751,6 +751,8 @@ REVOKE EXECUTE ON FUNCTION pg_ls_dir(text,boolean,boolean) FROM public;
 
 REVOKE EXECUTE ON FUNCTION pg_log_backend_memory_contexts(integer) FROM PUBLIC;
 
+REVOKE EXECUTE ON FUNCTION pg_log_backend_backtrace(integer) FROM PUBLIC;
+
 REVOKE EXECUTE ON FUNCTION pg_ls_logicalsnapdir() FROM PUBLIC;
 
 REVOKE EXECUTE ON FUNCTION pg_ls_logicalmapdir() FROM PUBLIC;
diff --git a/src/backend/storage/ipc/Makefile b/src/backend/storage/ipc/Makefile
index d8a1653eb6..4647916ea6 100644
--- a/src/backend/storage/ipc/Makefile
+++ b/src/backend/storage/ipc/Makefile
@@ -18,6 +18,7 @@ OBJS = \
 	latch.o \
 	pmsignal.o \
 	procarray.o \
+	procbacktrace.o \
 	procsignal.o \
 	shm_mq.o \
 	shm_toc.o \
diff --git a/src/backend/storage/ipc/meson.build b/src/backend/storage/ipc/meson.build
index 5a936171f7..7b38cdd3f0 100644
--- a/src/backend/storage/ipc/meson.build
+++ b/src/backend/storage/ipc/meson.build
@@ -10,6 +10,7 @@ backend_sources += files(
   'latch.c',
   'pmsignal.c',
   'procarray.c',
+  'procbacktrace.c',
   'procsignal.c',
   'shm_mq.c',
   'shm_toc.c',
diff --git a/src/backend/storage/ipc/procbacktrace.c b/src/backend/storage/ipc/procbacktrace.c
new file mode 100644
index 0000000000..bfab5783da
--- /dev/null
+++ b/src/backend/storage/ipc/procbacktrace.c
@@ -0,0 +1,149 @@
+/*-------------------------------------------------------------------------
+ *
+ * procbacktrace.c
+ *	  Backtrace-related routines
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/storage/ipc/procbacktrace.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
+
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "storage/procbacktrace.h"
+#include "storage/procsignal.h"
+#include "utils/fmgrprotos.h"
+
+#ifdef HAVE_EXECINFO_H
+static volatile sig_atomic_t backtrace_functions_loaded = false;
+#endif
+
+/*
+ * Handle receipt of an interrupt indicating logging of current backtrace.
+ *
+ * We capture the backtrace within this signal handler itself in a safe manner.
+ * It is ensured that no memory allocations happen here. This function emits
+ * backtrace to stderr as writing to server log allocates memory, making the
+ * signal halder unsafe. Also, the shared library implementing the
+ * backtrace-related functions is preloaded to avoid memory allocations upon
+ * first-time loading, see LoadBacktraceFunctions.
+ */
+void
+HandleLogBacktraceInterrupt(void)
+{
+#ifdef HAVE_EXECINFO_H
+	void	   *buf[100];
+	int			nframes;
+
+	if (!backtrace_functions_loaded)
+		return;
+
+	nframes = backtrace(buf, lengthof(buf));
+
+	write_stderr("logging current backtrace of process with PID %d:\n",
+				 MyProcPid);
+	backtrace_symbols_fd(buf, nframes, fileno(stderr));
+#endif
+}
+
+/*
+ * Load backtrace shared library.
+ *
+ * Any backtrace-related functions when called for the first time dynamically
+ * loads the shared library, which usually triggers a call to malloc, making
+ * them unsafe to use in signal handlers. This function makes backtrace-related
+ * functions signal-safe.
+ *
+ * Note that this function is supposed to be called in the early life of a
+ * process, preferably after signal handlers are setup; but not from within a
+ * signal handler.
+ */
+void
+LoadBacktraceFunctions(void)
+{
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	void	   *buf[2];
+
+	if (backtrace_functions_loaded)
+		return;
+
+	/*
+	 * It is enough to call any one backtrace-related function to ensure that
+	 * the corresponding shared library is dynamically loaded. We just load
+	 * two most recent function calls, as we don't use the backtrace anyway.
+	 */
+	backtrace(buf, lengthof(buf));
+
+	backtrace_functions_loaded = true;
+#endif
+}
+
+/*
+ * pg_log_backend_backtrace
+ *		Signal a backend 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.
+ */
+Datum
+pg_log_backend_backtrace(PG_FUNCTION_ARGS)
+{
+#ifdef HAVE_BACKTRACE_SYMBOLS
+	int			pid = PG_GETARG_INT32(0);
+	PGPROC	   *proc;
+
+	proc = BackendPidGetProc(pid);
+
+	/*
+	 * BackendPidGetProc returns NULL if the pid isn't valid.
+	 *
+	 * 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 (proc == NULL)
+	{
+		/*
+		 * This is just a warning so a loop-through-resultset will not abort
+		 * if one backend terminated on its own during the run.
+		 */
+		ereport(WARNING,
+				(errmsg("PID %d is not a PostgreSQL backend process", pid)));
+
+		PG_RETURN_BOOL(false);
+	}
+
+	if (SendProcSignal(pid, PROCSIG_LOG_BACKTRACE, proc->backendId) < 0)
+	{
+		/* Again, just a warning to allow loops */
+		ereport(WARNING,
+				(errmsg("could not send signal to process %d: %m", pid)));
+		PG_RETURN_BOOL(false);
+	}
+
+	PG_RETURN_BOOL(true);
+#else
+	ereport(WARNING,
+			errmsg("backtrace generation is not supported by this installation"),
+			errhint("You need to build PostgreSQL with library containing backtrace_symbols."));
+
+	PG_RETURN_BOOL(false);
+#endif
+}
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index e84619e5a5..3f1ce745af 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -28,6 +28,7 @@
 #include "storage/ipc.h"
 #include "storage/latch.h"
 #include "storage/proc.h"
+#include "storage/procbacktrace.h"
 #include "storage/shmem.h"
 #include "storage/smgr.h"
 #include "storage/sinval.h"
@@ -658,6 +659,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_LOG_MEMORY_CONTEXT))
 		HandleLogMemoryContextInterrupt();
 
+	if (CheckProcSignal(PROCSIG_LOG_BACKTRACE))
+		HandleLogBacktraceInterrupt();
+
 	if (CheckProcSignal(PROCSIG_PARALLEL_APPLY_MESSAGE))
 		HandleParallelApplyMessageInterrupt();
 
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index e5977548fe..73e0368c29 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -48,6 +48,7 @@
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
+#include "storage/procbacktrace.h"
 #include "storage/procsignal.h"
 #include "storage/spin.h"
 #include "storage/standby.h"
@@ -462,6 +463,8 @@ InitProcess(void)
 	InitLWLockAccess();
 	InitDeadLockChecking();
 
+	LoadBacktraceFunctions();
+
 #ifdef EXEC_BACKEND
 
 	/*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 29af4ce65d..cee43a8304 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8274,6 +8274,12 @@
   prorettype => 'bool', proargtypes => 'int4',
   prosrc => 'pg_log_backend_memory_contexts' },
 
+# logging backtrace of the specified backend
+{ oid => '9661', descr => 'log backtrace of the specified backend',
+  proname => 'pg_log_backend_backtrace', provolatile => 'v',
+  prorettype => 'bool', proargtypes => 'int4',
+  prosrc => 'pg_log_backend_backtrace' },
+
 # non-persistent series generator
 { oid => '1066', descr => 'non-persistent series generator',
   proname => 'generate_series', prorows => '1000',
diff --git a/src/include/storage/procbacktrace.h b/src/include/storage/procbacktrace.h
new file mode 100644
index 0000000000..884bdb34ad
--- /dev/null
+++ b/src/include/storage/procbacktrace.h
@@ -0,0 +1,20 @@
+/*-------------------------------------------------------------------------
+ *
+ * procsignal.h
+ *	  Backtrace-related routines
+ *
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/storage/procbacktrace.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PROCBACKTRACE_H
+#define PROCBACKTRACE_H
+
+extern void HandleLogBacktraceInterrupt(void);
+extern void LoadBacktraceFunctions(void);
+
+#endif							/* PROCBACKTRACE_H */
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index 52dcb4c2ad..5772b335d6 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -35,6 +35,7 @@ typedef enum
 	PROCSIG_WALSND_INIT_STOPPING,	/* ask walsenders to prepare for shutdown  */
 	PROCSIG_BARRIER,			/* global barrier interrupt  */
 	PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
+	PROCSIG_LOG_BACKTRACE,		/* ask backend to log the current backtrace */
 	PROCSIG_PARALLEL_APPLY_MESSAGE, /* Message from parallel apply workers */
 
 	/* Recovery conflict reasons */
diff --git a/src/test/regress/expected/misc_functions.out b/src/test/regress/expected/misc_functions.out
index 7c15477104..3ff0136347 100644
--- a/src/test/regress/expected/misc_functions.out
+++ b/src/test/regress/expected/misc_functions.out
@@ -326,6 +326,55 @@ REVOKE EXECUTE ON FUNCTION pg_log_backend_memory_contexts(integer)
   FROM regress_log_memory;
 DROP ROLE regress_log_memory;
 --
+-- pg_log_backend_backtrace()
+--
+-- Backtraces are logged to stderr and not returned to the function.
+-- Furthermore, the output of this function can vary depending on backtrace
+-- generation support. However, we can at least verify that the code doesn't
+-- fail, and that the permissions are set properly.
+-- Suppress warnings that depend on dynamic output i.e. PID or backtrace
+-- generation support.
+SET client_min_messages = 'ERROR';
+SELECT count(*) > 0 AS ok FROM pg_log_backend_backtrace(pg_backend_pid());
+ ok 
+----
+ t
+(1 row)
+
+RESET client_min_messages;
+CREATE ROLE regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backend_backtrace(integer)', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT EXECUTE ON FUNCTION pg_log_backend_backtrace(integer)
+  TO regress_log_backtrace;
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backend_backtrace(integer)', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SET ROLE regress_log_backtrace;
+-- Suppress warnings that depend on dynamic output i.e. PID or backtrace
+-- generation support.
+SET client_min_messages = 'ERROR';
+SELECT count(*) > 0 AS ok FROM pg_log_backend_backtrace(pg_backend_pid());
+ ok 
+----
+ t
+(1 row)
+
+RESET client_min_messages;
+RESET ROLE;
+REVOKE EXECUTE ON FUNCTION pg_log_backend_backtrace(integer)
+  FROM regress_log_backtrace;
+DROP ROLE regress_log_backtrace;
+--
 -- Test some built-in SRFs
 --
 -- The outputs of these are variable, so we can't just print their results
diff --git a/src/test/regress/sql/misc_functions.sql b/src/test/regress/sql/misc_functions.sql
index 851dad90f4..2a3f9bacaa 100644
--- a/src/test/regress/sql/misc_functions.sql
+++ b/src/test/regress/sql/misc_functions.sql
@@ -102,6 +102,46 @@ REVOKE EXECUTE ON FUNCTION pg_log_backend_memory_contexts(integer)
 
 DROP ROLE regress_log_memory;
 
+--
+-- pg_log_backend_backtrace()
+--
+-- Backtraces are logged to stderr and not returned to the function.
+-- Furthermore, the output of this function can vary depending on backtrace
+-- generation support. However, we can at least verify that the code doesn't
+-- fail, and that the permissions are set properly.
+
+-- Suppress warnings that depend on dynamic output i.e. PID or backtrace
+-- generation support.
+SET client_min_messages = 'ERROR';
+SELECT count(*) > 0 AS ok FROM pg_log_backend_backtrace(pg_backend_pid());
+RESET client_min_messages;
+
+CREATE ROLE regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backend_backtrace(integer)', 'EXECUTE'); -- no
+
+GRANT EXECUTE ON FUNCTION pg_log_backend_backtrace(integer)
+  TO regress_log_backtrace;
+
+SELECT has_function_privilege('regress_log_backtrace',
+  'pg_log_backend_backtrace(integer)', 'EXECUTE'); -- yes
+
+SET ROLE regress_log_backtrace;
+
+-- Suppress warnings that depend on dynamic output i.e. PID or backtrace
+-- generation support.
+SET client_min_messages = 'ERROR';
+SELECT count(*) > 0 AS ok FROM pg_log_backend_backtrace(pg_backend_pid());
+RESET client_min_messages;
+
+RESET ROLE;
+
+REVOKE EXECUTE ON FUNCTION pg_log_backend_backtrace(integer)
+  FROM regress_log_backtrace;
+
+DROP ROLE regress_log_backtrace;
+
 --
 -- Test some built-in SRFs
 --
-- 
2.34.1

v29-0002-Extend-backtrace-logging-function-for-auxiliary-.patchapplication/octet-stream; name=v29-0002-Extend-backtrace-logging-function-for-auxiliary-.patchDownload
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

#123Michael Paquier
michael@paquier.xyz
In reply to: Bharath Rupireddy (#122)
Re: Printing backtrace of postgres processes

On Thu, Feb 08, 2024 at 12:30:00AM +0530, Bharath Rupireddy wrote:

I've missed adding LoadBacktraceFunctions() in InitAuxiliaryProcess
for 0002 patch. Please find the attached v29 patch set. Sorry for the
noise.

I've been torturing the patch with \watch and loops calling the
function while doing a sequential scan of pg_stat_activity, and that
was stable while doing a pgbench and an installcheck-world in
parallel, with some infinite loops and some spinlocks I should not
have taken.

+ if (backtrace_functions_loaded)
+ return;

I was really wondering about this point, particularly regarding the
fact that this would load libgcc for all the backends when they start,
unconditionally. One thing could be to hide that behind a postmaster
GUC disabled by default, but then we'd come back to using gdb on a
live server, which is no fun on a customer environment because of the
extra dependencies, which may not, or just cannot, be installed. So
yeah, I think that I'm OK about that.

+ * procsignal.h
+ * Backtrace-related routines

This one is incorrect.

In HandleLogBacktraceInterrupt(), we don't use backtrace_symbols() and
rely on backtrace_symbols_fd() to avoid doing malloc() in the signal
handler as mentioned in [1]/messages/by-id/CALj2ACUNZVB0cQovvKBd53-upsMur8j-5_K=-fg86uAa+WYEWg@mail.gmail.com -- Michael back in 2022. Perhaps the part about the
fact that we don't use backtrace_symbols() should be mentioned
explicitely in a comment rather than silently implied? That's
a very important point.

Echoing with upthread, and we've been more lax with superuser checks
and assignment of custom roles in recent years, I agree with the
approach of the patch to make that superuser by default. Then users
can force their own policy as they want with an EXECUTE ACL on the SQL
function.

As a whole, I'm pretty excited about being able to rely on that
without the need to use gdb to get a live trace. Does anybody have
objections and/or comments, particularly about the superuser and the
load part at backend startup? This thread has been going on for so
long that it would be good to let 1 week for folks to react before
doing anything. See v29 for references.

[1]: /messages/by-id/CALj2ACUNZVB0cQovvKBd53-upsMur8j-5_K=-fg86uAa+WYEWg@mail.gmail.com -- Michael
--
Michael

#124Michael Paquier
michael@paquier.xyz
In reply to: Michael Paquier (#123)
Re: Printing backtrace of postgres processes

On Thu, Feb 08, 2024 at 12:25:18PM +0900, Michael Paquier wrote:

In HandleLogBacktraceInterrupt(), we don't use backtrace_symbols() and
rely on backtrace_symbols_fd() to avoid doing malloc() in the signal
handler as mentioned in [1] back in 2022. Perhaps the part about the
fact that we don't use backtrace_symbols() should be mentioned
explicitely in a comment rather than silently implied? That's
a very important point.

This has been itching me, so I have spent more time reading about
that, and while browsing signal(7) and signal-safety(7), I've first
noticed that this is not safe in the patch: 
+   write_stderr("logging current backtrace of process with PID %d:\n",
+                MyProcPid);

Note that there's a write_stderr_signal_safe().

Anyway, I've been digging around the signal-safety of backtrace(3)
(even looking a bit at some GCC code, brrr), and I am under the
impression that backtrace() is just by nature not safe and also
dangerous in signal handlers. One example of issue I've found:
https://github.com/gperftools/gperftools/issues/838

This looks like enough ground to me to reject the patch.
--
Michael

#125Alvaro Herrera
alvherre@alvh.no-ip.org
In reply to: Michael Paquier (#124)
Re: Printing backtrace of postgres processes

On 2024-Feb-09, Michael Paquier wrote:

Anyway, I've been digging around the signal-safety of backtrace(3)
(even looking a bit at some GCC code, brrr), and I am under the
impression that backtrace() is just by nature not safe and also
dangerous in signal handlers. One example of issue I've found:
https://github.com/gperftools/gperftools/issues/838

This looks like enough ground to me to reject the patch.

Hmm, but the backtrace() manpage says

• backtrace() and backtrace_symbols_fd() don't call malloc() explic‐
itly, but they are part of libgcc, which gets loaded dynamically
when first used. Dynamic loading usually triggers a call to mal‐
loc(3). If you need certain calls to these two functions to not
allocate memory (in signal handlers, for example), you need to make
sure libgcc is loaded beforehand.

and the patch ensures that libgcc is loaded by calling a dummy
backtrace() at the start of the process.

--
Álvaro Herrera 48°01'N 7°57'E — https://www.EnterpriseDB.com/
"All rings of power are equal,
But some rings of power are more equal than others."
(George Orwell's The Lord of the Rings)

#126Ashutosh Bapat
ashutosh.bapat.oss@gmail.com
In reply to: Alvaro Herrera (#125)
Re: Printing backtrace of postgres processes

On Fri, Feb 9, 2024 at 2:18 PM Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:

On 2024-Feb-09, Michael Paquier wrote:

Anyway, I've been digging around the signal-safety of backtrace(3)
(even looking a bit at some GCC code, brrr), and I am under the
impression that backtrace() is just by nature not safe and also
dangerous in signal handlers. One example of issue I've found:
https://github.com/gperftools/gperftools/issues/838

This looks like enough ground to me to reject the patch.

Hmm, but the backtrace() manpage says

• backtrace() and backtrace_symbols_fd() don't call malloc() explic‐
itly, but they are part of libgcc, which gets loaded dynamically
when first used. Dynamic loading usually triggers a call to mal‐
loc(3). If you need certain calls to these two functions to not
allocate memory (in signal handlers, for example), you need to make
sure libgcc is loaded beforehand.

and the patch ensures that libgcc is loaded by calling a dummy
backtrace() at the start of the process.

We defer actual action triggered by a signal till CHECK_FOR_INTERRUPTS
is called. I understand that we can't do that here since we want to
capture the backtrace at that moment and can't wait till next CFI. But
printing the backend can surely wait till next CFI right?

--
Best Wishes,
Ashutosh Bapat

#127Michael Paquier
michael@paquier.xyz
In reply to: Ashutosh Bapat (#126)
Re: Printing backtrace of postgres processes

On Fri, Feb 09, 2024 at 02:27:26PM +0530, Ashutosh Bapat wrote:

On Fri, Feb 9, 2024 at 2:18 PM Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:

Hmm, but the backtrace() manpage says

• backtrace() and backtrace_symbols_fd() don't call malloc() explic‐
itly, but they are part of libgcc, which gets loaded dynamically
when first used. Dynamic loading usually triggers a call to mal‐
loc(3). If you need certain calls to these two functions to not
allocate memory (in signal handlers, for example), you need to make
sure libgcc is loaded beforehand.

and the patch ensures that libgcc is loaded by calling a dummy
backtrace() at the start of the process.

FWIW, anything I am reading about the matter freaks me out, including
the dlopen() part in all the backends:
https://www.gnu.org/software/libc/manual/html_node/Backtraces.html

So I really question whether it is a good idea to assume if this will
always be safe depending on the version of libgcc dealt with,
increasing the impact area. Perhaps that's worrying too much, but it
looks like one of these things where we'd better be really careful.

We defer actual action triggered by a signal till CHECK_FOR_INTERRUPTS
is called. I understand that we can't do that here since we want to
capture the backtrace at that moment and can't wait till next CFI. But
printing the backend can surely wait till next CFI right?

Delaying the call of backtrace() to happen during a CFI() would be
safe, yes, and writing data to stderr would not really be an issue as
at least the data would be sent somewhere. That's less useful, but
we do that for memory contexts.
--
Michael

#128Ashutosh Bapat
ashutosh.bapat.oss@gmail.com
In reply to: Michael Paquier (#127)
Re: Printing backtrace of postgres processes

On Sat, Feb 10, 2024 at 5:36 AM Michael Paquier <michael@paquier.xyz> wrote:

On Fri, Feb 09, 2024 at 02:27:26PM +0530, Ashutosh Bapat wrote:

On Fri, Feb 9, 2024 at 2:18 PM Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:

Hmm, but the backtrace() manpage says

• backtrace() and backtrace_symbols_fd() don't call malloc() explic‐
itly, but they are part of libgcc, which gets loaded dynamically
when first used. Dynamic loading usually triggers a call to mal‐
loc(3). If you need certain calls to these two functions to not
allocate memory (in signal handlers, for example), you need to make
sure libgcc is loaded beforehand.

and the patch ensures that libgcc is loaded by calling a dummy
backtrace() at the start of the process.

FWIW, anything I am reading about the matter freaks me out, including
the dlopen() part in all the backends:
https://www.gnu.org/software/libc/manual/html_node/Backtraces.html

So I really question whether it is a good idea to assume if this will
always be safe depending on the version of libgcc dealt with,
increasing the impact area. Perhaps that's worrying too much, but it
looks like one of these things where we'd better be really careful.

I agree. We don't want a call to backtrace printing mechanism to make
things worse.

We defer actual action triggered by a signal till CHECK_FOR_INTERRUPTS
is called. I understand that we can't do that here since we want to
capture the backtrace at that moment and can't wait till next CFI. But
printing the backend can surely wait till next CFI right?

Delaying the call of backtrace() to happen during a CFI() would be
safe, yes, and writing data to stderr would not really be an issue as
at least the data would be sent somewhere. That's less useful, but
we do that for memory contexts.

Memory contexts do not change more or less till next CFI, but stack
traces do. So I am not sure whether it is desirable to wait to capture
backtrace till next CFI. Given that the user can not time a call to
pg_log_backend() exactly, so whether it captures the backtrace exactly
at when interrupt happens or at the next CFI may not matter much in
practice.

--
Best Wishes,
Ashutosh Bapat

#129Ashutosh Bapat
ashutosh.bapat.oss@gmail.com
In reply to: Ashutosh Bapat (#128)
Re: Printing backtrace of postgres processes

On Mon, Feb 12, 2024 at 6:52 AM Ashutosh Bapat
<ashutosh.bapat.oss@gmail.com> wrote:

We defer actual action triggered by a signal till CHECK_FOR_INTERRUPTS
is called. I understand that we can't do that here since we want to
capture the backtrace at that moment and can't wait till next CFI. But
printing the backend can surely wait till next CFI right?

Delaying the call of backtrace() to happen during a CFI() would be
safe, yes, and writing data to stderr would not really be an issue as
at least the data would be sent somewhere. That's less useful, but
we do that for memory contexts.

Memory contexts do not change more or less till next CFI, but stack
traces do. So I am not sure whether it is desirable to wait to capture
backtrace till next CFI. Given that the user can not time a call to
pg_log_backend() exactly, so whether it captures the backtrace exactly
at when interrupt happens or at the next CFI may not matter much in
practice.

Thinking more about this I have following thoughts/questions:

1. Whether getting a backtrace at CFI is good enough?
Backtrace is required to know what a process is doing when it's stuck
or is behaviour unexpected etc. PostgreSQL code has CFIs sprinkled in
almost all the tight loops. Whether those places are enough to cover
most of the cases that the user of this feature would care about?

2. tools like gdb, strace can be used to get the stack trace of any
process, so do we really need this tool?
Most of the OSes provide such tools but may be there are useful in
kubernetes like environment, I am not sure.

3. tools like gdb and strace are able to capture stack trace at any
point during execution. Can we use the same mechanism instead of
relying on CFI?

4. tools like gdb and strace can capture more than just stack trace
e.g. variable values, values of registers etc. Are we planning to add
those facilities as well? OR whether this feature will be useful
without those facilities?

May the feature be more useful if it can provide PostgreSQL specific
details which an external tool can not.

--
Best Wishes,
Ashutosh Bapat

#130Christoph Berg
myon@debian.org
In reply to: Michael Paquier (#127)
Re: Printing backtrace of postgres processes

Re: Michael Paquier

• backtrace() and backtrace_symbols_fd() don't call malloc() explic‐
itly, but they are part of libgcc, which gets loaded dynamically
when first used. Dynamic loading usually triggers a call to mal‐
loc(3). If you need certain calls to these two functions to not
allocate memory (in signal handlers, for example), you need to make
sure libgcc is loaded beforehand.

and the patch ensures that libgcc is loaded by calling a dummy
backtrace() at the start of the process.

FWIW, anything I am reading about the matter freaks me out, including
the dlopen() part in all the backends:
https://www.gnu.org/software/libc/manual/html_node/Backtraces.html

I'd be concerned about the cost of doing that as part of the startup
of every single backend process. Shouldn't this rather be done within
the postmaster so it's automatically inherited by forked backends?
(EXEC_BACKEND systems probably don't have libgcc I guess.)

Christoph

#131Michael Paquier
michael@paquier.xyz
In reply to: Christoph Berg (#130)
Re: Printing backtrace of postgres processes

On Fri, Feb 23, 2024 at 04:39:47PM +0100, Christoph Berg wrote:

I'd be concerned about the cost of doing that as part of the startup
of every single backend process. Shouldn't this rather be done within
the postmaster so it's automatically inherited by forked backends?
(EXEC_BACKEND systems probably don't have libgcc I guess.)

Something like this can be measured with a bunch of concurrent
connections attempting connections and a very high rate, like pgbench
with an empty script and -C, for local connections.
--
Michael

#132Christoph Berg
myon@debian.org
In reply to: Michael Paquier (#131)
Re: Printing backtrace of postgres processes

Re: Michael Paquier

Something like this can be measured with a bunch of concurrent
connections attempting connections and a very high rate, like pgbench
with an empty script and -C, for local connections.

I tried that now. Mind that I'm not a benchmarking expert, and there's
been quite some jitter in the results, but I think there's a clear
trend.

Current head without and with the v28 patchset.
Command line:
pgbench -n -C -c 20 -j 20 -f empty.sql -T 30 --progress=2
empty.sql just contains a ";"
model name: 13th Gen Intel(R) Core(TM) i7-13700H

head:
tps = 2211.289863 (including reconnection times)
tps = 2113.907588 (including reconnection times)
tps = 2200.406877 (including reconnection times)
average: 2175

v28:
tps = 1873.472459 (including reconnection times)
tps = 2068.094383 (including reconnection times)
tps = 2196.890897 (including reconnection times)
average: 2046

2046 / 2175 = 0.941

Even if we regard the 1873 as an outlier, I've seen many vanilla runs
with 22xx tps, and not a single v28 run with 22xx tps. Other numbers I
collected suggested a cost of at least 3% for the feature.

Christoph

#133Michael Paquier
michael@paquier.xyz
In reply to: Christoph Berg (#132)
Re: Printing backtrace of postgres processes

On Mon, Feb 26, 2024 at 04:05:05PM +0100, Christoph Berg wrote:

I tried that now. Mind that I'm not a benchmarking expert, and there's
been quite some jitter in the results, but I think there's a clear
trend.

Even if we regard the 1873 as an outlier, I've seen many vanilla runs
with 22xx tps, and not a single v28 run with 22xx tps. Other numbers I
collected suggested a cost of at least 3% for the feature.

Thanks for the numbers. Yes, that's annoying and I suspect could be
noticeable for a lot of users..
--
Michael