Hi folks

I recently needed a way to get backtraces from errors in a convenient,
non-interactive and indescriminate way. The attached patch is the result.
It teaches Pg to use libunwind to self-backtrace in elog/ereport.

Anyone think this is useful/interesting/worth pursuing?

(Patch is currently against pg10, so this is a PoC only).

As written it emits a backtrace when log_error_verbosity=verbose or,
unconditionally, on PANIC. A bt can be hidden by errhidestack() but is
otherwise shown. That's ridiculously, excessively spammy, so it's not
viable for core as-is. Before playing with it too much I thought I'd ask
for ideas on if anyone thinks it's useful, and if so, how it'd work best.

My goal is to allow capture of extra diagnostic info from key locations in
production without needing to attach gdb. It's imperfect since sometimes
there's no convenient message, and other times you can't afford to set up
logging with enough detail. So this would be most useful when combined with
one of the occasionally discussed patches to allow for selective logging
verbosity on a module- or file- level. But I think it's still handy without
that.

I briefly looked into Windows too. Roughly the same approach could be used
to plug in dbghelp.dll support for Windows self-backtracing, it's just
rather uglier; see
https://jpassing.com/2008/03/12/walking-the-stack-of-the-current-thread/ .

BTW, Álvaro posted a simpler patch at
https://www.postgresql.org/message-id/20180410213203.nl645o5vj5ap66sl@alvherre.pgsql.
It uses backtrace() from glibc, and requires each site you want to bt to be
annotated explicitly. I forgot about backtrace() completely when I wrote
mine, and I prefer the control libunwind gives me anyway, but the reduced
dependency would be nice. Especially since backtrace() is in FreeBSD too.

-- 
 Craig Ringer                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services
From d9ff6bd8112b31d087d8442f25ffd430df794771 Mon Sep 17 00:00:00 2001
From: Craig Ringer <cr...@2ndquadrant.com>
Date: Wed, 20 Jun 2018 10:57:31 +0800
Subject: [PATCH v2] Support generating backtraces in logs using libunwind

---
 configure                      | 349 ++++++++++++++++++++++++++++-------------
 configure.in                   |  22 +++
 src/Makefile.global.in         |   6 +-
 src/backend/Makefile           |   4 +-
 src/backend/utils/error/elog.c | 112 +++++++++++++
 src/include/pg_config.h.in     |   6 +
 src/include/pg_config_manual.h |   2 +-
 src/include/utils/elog.h       |  12 ++
 8 files changed, 399 insertions(+), 114 deletions(-)

diff --git a/configure b/configure
index 2d5375d51c..87a7d60f3f 100755
--- a/configure
+++ b/configure
@@ -655,6 +655,8 @@ LIBOBJS
 UUID_LIBS
 LDAP_LIBS_BE
 LDAP_LIBS_FE
+LIBUNWIND_LIBS
+LIBUNWIND_CFLAGS
 PTHREAD_CFLAGS
 PTHREAD_LIBS
 PTHREAD_CC
@@ -716,12 +718,12 @@ with_perl
 with_tcl
 ICU_LIBS
 ICU_CFLAGS
-PKG_CONFIG_LIBDIR
-PKG_CONFIG_PATH
-PKG_CONFIG
 with_icu
 enable_thread_safety
 INCLUDES
+PKG_CONFIG_LIBDIR
+PKG_CONFIG_PATH
+PKG_CONFIG
 autodepend
 TAS
 GCC
@@ -846,6 +848,7 @@ with_uuid
 with_ossp_uuid
 with_libxml
 with_libxslt
+with_libunwind
 with_system_tzdata
 with_zlib
 with_gnu_ld
@@ -868,7 +871,9 @@ PKG_CONFIG_LIBDIR
 ICU_CFLAGS
 ICU_LIBS
 LDFLAGS_EX
-LDFLAGS_SL'
+LDFLAGS_SL
+LIBUNWIND_CFLAGS
+LIBUNWIND_LIBS'
 
 
 # Initialize some variables set by options.
@@ -1543,6 +1548,7 @@ Optional Packages:
   --with-ossp-uuid        obsolete spelling of --with-uuid=ossp
   --with-libxml           build with XML support
   --with-libxslt          use XSLT support when building contrib/xml2
+  --with-libunwind        use libunwind for enhanced error context stacks
   --with-system-tzdata=DIR
                           use system time zone data in DIR
   --without-zlib          do not use Zlib
@@ -1566,6 +1572,10 @@ Some influential environment variables:
   ICU_LIBS    linker flags for ICU, overriding pkg-config
   LDFLAGS_EX  extra linker flags for linking executables only
   LDFLAGS_SL  extra linker flags for linking shared libraries only
+  LIBUNWIND_CFLAGS
+              override cflags used when building with libunwind
+  LIBUNWIND_LIBS
+              override libraries linked when building with libunwind
 
 Use these variables to override the choices made by `configure' or to help
 it to find libraries and programs with nonstandard names/locations.
@@ -5359,112 +5369,8 @@ fi
 
 
 #
-# Include directories
+# Look for pkg-config
 #
-ac_save_IFS=$IFS
-IFS="${IFS}${PATH_SEPARATOR}"
-# SRCH_INC comes from the template file
-for dir in $with_includes $SRCH_INC; do
-  if test -d "$dir"; then
-    INCLUDES="$INCLUDES -I$dir"
-  else
-    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: *** Include directory $dir does not exist." >&5
-$as_echo "$as_me: WARNING: *** Include directory $dir does not exist." >&2;}
-  fi
-done
-IFS=$ac_save_IFS
-
-
-
-#
-# Library directories
-#
-ac_save_IFS=$IFS
-IFS="${IFS}${PATH_SEPARATOR}"
-# LIBRARY_DIRS comes from command line, SRCH_LIB from template file.
-for dir in $LIBRARY_DIRS $SRCH_LIB; do
-  if test -d "$dir"; then
-    LIBDIRS="$LIBDIRS -L$dir"
-  else
-    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: *** Library directory $dir does not exist." >&5
-$as_echo "$as_me: WARNING: *** Library directory $dir does not exist." >&2;}
-  fi
-done
-IFS=$ac_save_IFS
-
-#
-# Enable thread-safe client libraries
-#
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking allow thread-safe client libraries" >&5
-$as_echo_n "checking allow thread-safe client libraries... " >&6; }
-
-
-# Check whether --enable-thread-safety was given.
-if test "${enable_thread_safety+set}" = set; then :
-  enableval=$enable_thread_safety;
-  case $enableval in
-    yes)
-      :
-      ;;
-    no)
-      :
-      ;;
-    *)
-      as_fn_error $? "no argument expected for --enable-thread-safety option" "$LINENO" 5
-      ;;
-  esac
-
-else
-  enable_thread_safety=yes
-
-fi
-
-
-if test "$enable_thread_safety" = yes; then
-
-$as_echo "#define ENABLE_THREAD_SAFETY 1" >>confdefs.h
-
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_thread_safety" >&5
-$as_echo "$enable_thread_safety" >&6; }
-
-
-#
-# ICU
-#
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with ICU support" >&5
-$as_echo_n "checking whether to build with ICU support... " >&6; }
-
-
-
-# Check whether --with-icu was given.
-if test "${with_icu+set}" = set; then :
-  withval=$with_icu;
-  case $withval in
-    yes)
-
-$as_echo "#define USE_ICU 1" >>confdefs.h
-
-      ;;
-    no)
-      :
-      ;;
-    *)
-      as_fn_error $? "no argument expected for --with-icu option" "$LINENO" 5
-      ;;
-  esac
-
-else
-  with_icu=no
-
-fi
-
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_icu" >&5
-$as_echo "$with_icu" >&6; }
-
-
-if test "$with_icu" = yes; then
 
 
 
@@ -5584,8 +5490,116 @@ $as_echo "yes" >&6; }
 $as_echo "no" >&6; }
 		PKG_CONFIG=""
 	fi
+fi;
+
+#
+# Include directories
+#
+ac_save_IFS=$IFS
+IFS="${IFS}${PATH_SEPARATOR}"
+# SRCH_INC comes from the template file
+for dir in $with_includes $SRCH_INC; do
+  if test -d "$dir"; then
+    INCLUDES="$INCLUDES -I$dir"
+  else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: *** Include directory $dir does not exist." >&5
+$as_echo "$as_me: WARNING: *** Include directory $dir does not exist." >&2;}
+  fi
+done
+IFS=$ac_save_IFS
+
+
+
+#
+# Library directories
+#
+ac_save_IFS=$IFS
+IFS="${IFS}${PATH_SEPARATOR}"
+# LIBRARY_DIRS comes from command line, SRCH_LIB from template file.
+for dir in $LIBRARY_DIRS $SRCH_LIB; do
+  if test -d "$dir"; then
+    LIBDIRS="$LIBDIRS -L$dir"
+  else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: *** Library directory $dir does not exist." >&5
+$as_echo "$as_me: WARNING: *** Library directory $dir does not exist." >&2;}
+  fi
+done
+IFS=$ac_save_IFS
+
+#
+# Enable thread-safe client libraries
+#
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking allow thread-safe client libraries" >&5
+$as_echo_n "checking allow thread-safe client libraries... " >&6; }
+
+
+# Check whether --enable-thread-safety was given.
+if test "${enable_thread_safety+set}" = set; then :
+  enableval=$enable_thread_safety;
+  case $enableval in
+    yes)
+      :
+      ;;
+    no)
+      :
+      ;;
+    *)
+      as_fn_error $? "no argument expected for --enable-thread-safety option" "$LINENO" 5
+      ;;
+  esac
+
+else
+  enable_thread_safety=yes
+
 fi
 
+
+if test "$enable_thread_safety" = yes; then
+
+$as_echo "#define ENABLE_THREAD_SAFETY 1" >>confdefs.h
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_thread_safety" >&5
+$as_echo "$enable_thread_safety" >&6; }
+
+
+#
+# ICU
+#
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with ICU support" >&5
+$as_echo_n "checking whether to build with ICU support... " >&6; }
+
+
+
+# Check whether --with-icu was given.
+if test "${with_icu+set}" = set; then :
+  withval=$with_icu;
+  case $withval in
+    yes)
+
+$as_echo "#define USE_ICU 1" >>confdefs.h
+
+      ;;
+    no)
+      :
+      ;;
+    *)
+      as_fn_error $? "no argument expected for --with-icu option" "$LINENO" 5
+      ;;
+  esac
+
+else
+  with_icu=no
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_icu" >&5
+$as_echo "$with_icu" >&6; }
+
+
+if test "$with_icu" = yes; then
+
 pkg_failed=no
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for icu-uc icu-i18n" >&5
 $as_echo_n "checking for icu-uc icu-i18n... " >&6; }
@@ -6402,6 +6416,35 @@ fi
 
 
 
+#
+# libunwind
+#
+
+
+
+# Check whether --with-libunwind was given.
+if test "${with_libunwind+set}" = set; then :
+  withval=$with_libunwind;
+  case $withval in
+    yes)
+
+$as_echo "#define USE_LIBUNWIND 1" >>confdefs.h
+
+      ;;
+    no)
+      :
+      ;;
+    *)
+      as_fn_error $? "no argument expected for --with-libunwind option" "$LINENO" 5
+      ;;
+  esac
+
+else
+  with_libunwind=no
+
+fi
+
+
 
 
 
@@ -10375,6 +10418,94 @@ fi
 
 fi
 
+if test "x$with_libunwind" = xyes; then :
+
+
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libunwind" >&5
+$as_echo_n "checking for libunwind... " >&6; }
+
+if test -n "$LIBUNWIND_CFLAGS"; then
+    pkg_cv_LIBUNWIND_CFLAGS="$LIBUNWIND_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libunwind\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libunwind") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_LIBUNWIND_CFLAGS=`$PKG_CONFIG --cflags "libunwind" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$LIBUNWIND_LIBS"; then
+    pkg_cv_LIBUNWIND_LIBS="$LIBUNWIND_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libunwind\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libunwind") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_LIBUNWIND_LIBS=`$PKG_CONFIG --libs "libunwind" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+	        LIBUNWIND_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libunwind" 2>&1`
+        else
+	        LIBUNWIND_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libunwind" 2>&1`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$LIBUNWIND_PKG_ERRORS" >&5
+
+
+      as_fn_error $? "library 'libunwind' is required for enhanced error stack support, check for libunwind.pc" "$LINENO" 5
+
+elif test $pkg_failed = untried; then
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+      as_fn_error $? "library 'libunwind' is required for enhanced error stack support, check for libunwind.pc" "$LINENO" 5
+
+else
+	LIBUNWIND_CFLAGS=$pkg_cv_LIBUNWIND_CFLAGS
+	LIBUNWIND_LIBS=$pkg_cv_LIBUNWIND_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+
+fi
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: libunwind not configured" >&5
+$as_echo "$as_me: libunwind not configured" >&6;}
+fi
+
+
+
 # Note: We can test for libldap_r only after we know PTHREAD_LIBS
 if test "$with_ldap" = yes ; then
   _LIBS="$LIBS"
diff --git a/configure.in b/configure.in
index 05dce2808f..181f776fd4 100644
--- a/configure.in
+++ b/configure.in
@@ -573,6 +573,11 @@ PGAC_ARG_BOOL(enable, cassert, no, [enable assertion checks (for debugging)],
                          [Define to 1 to build with assertion checks. (--enable-cassert)])])
 
 
+#
+# Look for pkg-config
+#
+PKG_PROG_PKG_CONFIG;
+
 #
 # Include directories
 #
@@ -839,6 +844,11 @@ AC_SUBST(with_libxml)
 PGAC_ARG_BOOL(with, libxslt, no, [use XSLT support when building contrib/xml2],
               [AC_DEFINE([USE_LIBXSLT], 1, [Define to 1 to use XSLT support when building contrib/xml2. (--with-libxslt)])])
 
+#
+# libunwind
+#
+PGAC_ARG_BOOL(with, libunwind, no, [use libunwind for enhanced error context stacks],
+              [AC_DEFINE([USE_LIBUNWIND], 1, [Define to 1 to use libunwind for more error details. (--with-libunwind)])])
 
 AC_SUBST(with_libxslt)
 
@@ -1120,6 +1130,18 @@ if test "$with_libxslt" = yes ; then
   AC_CHECK_LIB(xslt, xsltCleanupGlobals, [], [AC_MSG_ERROR([library 'xslt' is required for XSLT support])])
 fi
 
+AS_IF([test "x$with_libunwind" = xyes], [
+  AC_ARG_VAR(LIBUNWIND_CFLAGS, [override cflags used when building with libunwind])
+  AC_ARG_VAR(LIBUNWIND_LIBS, [override libraries linked when building with libunwind])
+  PKG_CHECK_MODULES(LIBUNWIND, libunwind,
+    [
+    ], [
+      AC_MSG_ERROR([library 'libunwind' is required for enhanced error stack support, check for libunwind.pc])
+    ])
+], [AC_MSG_NOTICE([libunwind not configured]) ])
+AC_SUBST([LIBUNWIND_CFLAGS])
+AC_SUBST([LIBUNWIND_LIBS])
+
 # Note: We can test for libldap_r only after we know PTHREAD_LIBS
 if test "$with_ldap" = yes ; then
   _LIBS="$LIBS"
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index e8b3a519cb..23f1da398a 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -222,6 +222,9 @@ TCL_SHLIB_LD_LIBS	= @TCL_SHLIB_LD_LIBS@
 PTHREAD_CFLAGS		= @PTHREAD_CFLAGS@
 PTHREAD_LIBS		= @PTHREAD_LIBS@
 
+LIBUNWIND_CFLAGS	= @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS		= @LIBUNWIND_LIBS@
+
 
 ##########################################################################
 #
@@ -232,7 +235,7 @@ PTHREAD_LIBS		= @PTHREAD_LIBS@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
 
-override CPPFLAGS := $(ICU_CFLAGS) $(CPPFLAGS)
+override CPPFLAGS := $(ICU_CFLAGS) $(LIBUNWIND_CFLAGS) $(CPPFLAGS)
 
 ifdef PGXS
 override CPPFLAGS := -I$(includedir_server) -I$(includedir_internal) $(CPPFLAGS)
@@ -623,7 +626,6 @@ ifdef PROFILE
    LDFLAGS += $(PROFILE)
 endif
 
-
 ##########################################################################
 #
 # substitute implementations of C library routines (see src/port/)
diff --git a/src/backend/Makefile b/src/backend/Makefile
index 2640834d5f..eaab459918 100644
--- a/src/backend/Makefile
+++ b/src/backend/Makefile
@@ -39,8 +39,8 @@ OBJS = $(SUBDIROBJS) $(LOCALOBJS) $(top_builddir)/src/port/libpgport_srv.a \
        $(top_builddir)/src/common/libpgcommon_srv.a
 
 # We put libpgport and libpgcommon into OBJS, so remove it from LIBS; also add
-# libldap and ICU
-LIBS := $(filter-out -lpgport -lpgcommon, $(LIBS)) $(LDAP_LIBS_BE) $(ICU_LIBS)
+# libldap, ICU and libunwind
+LIBS := $(filter-out -lpgport -lpgcommon, $(LIBS)) $(LDAP_LIBS_BE) $(ICU_LIBS) $(LIBUNWIND_LIBS)
 
 # The backend doesn't need everything that's in LIBS, however
 LIBS := $(filter-out -lz -lreadline -ledit -ltermcap -lncurses -lcurses, $(LIBS))
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index d10a658e3d..6ac5b8457f 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -217,6 +217,20 @@ err_gettext(const char *str)
 #endif
 }
 
+static inline void 
+errsavestack(ErrorData *edata)
+{
+	/*
+	 * We need to capture the stack unwinding context now because not everybody
+	 * reaches errfinish within the ereport(...) macro, lots of call paths call
+	 * EmitErrorReport directly.
+	 */
+#if USE_LIBUNWIND
+	if (!edata->hide_stack)
+		if (unw_getcontext((unw_context_t*)&edata->unwind_context) == 0)
+			edata->unwind_context_valid = true;
+#endif
+}
 
 /*
  * errstart --- begin an error-reporting cycle
@@ -389,6 +403,7 @@ errstart(int elevel, const char *filename, int lineno,
 		edata->sqlerrcode = ERRCODE_WARNING;
 	else
 		edata->sqlerrcode = ERRCODE_SUCCESSFUL_COMPLETION;
+	errsavestack(edata);
 	/* errno is saved here so that error parameter eval can't change it */
 	edata->saved_errno = errno;
 
@@ -1096,6 +1111,24 @@ errhidecontext(bool hide_ctx)
 	return 0;					/* return value does not matter */
 }
 
+/*
+ * errhidestack --- optionally suppress STACK: field of log entry
+ *
+ * This should only be used for verbose debugging messages where the repeated
+ * inclusion of context would bloat the log volume too much.
+ */
+int
+errhidestack(bool hide_stack)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+
+	/* we don't bother incrementing recursion_depth */
+	CHECK_STACK_DEPTH();
+
+	edata->hide_stack = hide_stack;
+
+	return 0;					/* return value does not matter */
+}
 
 /*
  * errfunction --- add reporting function name to the current error
@@ -1334,6 +1367,7 @@ elog_start(const char *filename, int lineno, const char *funcname)
 	edata->filename = filename;
 	edata->lineno = lineno;
 	edata->funcname = funcname;
+	errsavestack(edata);
 	/* errno is saved now so that error parameter eval can't change it */
 	edata->saved_errno = errno;
 
@@ -2619,6 +2653,63 @@ log_line_prefix(StringInfo buf, ErrorData *edata)
 	}
 }
 
+static void
+append_unwind_backtrace(StringInfo buf, ErrorData *edata, const char *missing_str)
+{
+#if USE_LIBUNWIND
+#define MAX_FUNCNAME_LENGTH 100
+	unw_cursor_t cursor;
+
+	Assert(edata->unwind_context_valid);
+
+	if (unw_init_local(&cursor, (unw_context_t*)&edata->unwind_context) == 0)
+	{
+		int frameno = 0;
+
+		/* We don't want to see errstart or elog_start in the stack */
+		unw_step(&cursor);
+
+		while (unw_step(&cursor) > 0)
+		{
+			unw_word_t offp;
+			char frame_funcname[MAX_FUNCNAME_LENGTH];
+			int ret;
+			ret = unw_get_proc_name(&cursor, &frame_funcname[0], MAX_FUNCNAME_LENGTH, &offp);
+			if (ret == 0)
+			{
+				// StartupXLOG is 12000 bytes. So question offsets lots bigger than it,
+				// we might've failed to resolve a symbol. This helps catch things like
+				//     FRAME    8: _fini +0x1d4737
+				//
+				// but we must make an exception for _yy_ symbols, since we know the parser
+				// is huge. We mainly want to catch things like _fini.
+				bool symbol_suspect = offp > 20000 && strncmp(&frame_funcname[0], "_yy", 3) != 0;
+				appendStringInfo(buf, "\n\tFRAME %4d: %s%s +%-4ld",
+					frameno, symbol_suspect ? "<suspect> " : "",
+					frame_funcname, (long)offp);
+			}
+			else
+			{
+				unw_word_t ip, sp;
+				unw_get_reg(&cursor, UNW_REG_IP, &ip);
+				unw_get_reg(&cursor, UNW_REG_SP, &sp);
+				appendStringInfo(buf, "\n\tFRAME %4d: < ?? > ip=0x%lx sp=0x%lx", frameno, (long)ip, (long)sp);
+			}
+			frameno ++;
+			// Abbreviate stacks at known endpoints
+			if (strcmp(&frame_funcname[0], "PostmasterMain") == 0
+					|| strcmp(&frame_funcname[0], "PostgresMain") == 0
+					|| strcmp(&frame_funcname[0], "StartupProcessMain") == 0
+					|| strcmp(&frame_funcname[0], "AuxiliaryProcessMain") == 0)
+				break;
+		}
+		appendStringInfoString(buf, "\n");
+	}
+	else
+		appendStringInfoString(buf, missing_str);
+#endif
+}
+
 /*
  * append a CSV'd version of a string to a StringInfo
  * We use the PostgreSQL defaults for CSV, i.e. quote = escape = '"'
@@ -2830,6 +2921,18 @@ write_csvlog(ErrorData *edata)
 	if (application_name)
 		appendCSVLiteral(&buf, application_name);
 
+	/* Call stack if recorded */
+	if ((Log_error_verbosity >= PGERROR_VERBOSE
+		 || edata->elevel == PANIC)
+		&& !edata->hide_stack && edata->unwind_context_valid)
+	{
+		StringInfoData bt;
+		initStringInfo(&bt);
+		append_unwind_backtrace(&bt, edata, "");
+		appendCSVLiteral(&buf, bt.data);
+		pfree(bt.data);
+	}
+
 	appendStringInfoChar(&buf, '\n');
 
 	/* If in the syslogger process, try to write messages direct to file */
@@ -2949,6 +3052,15 @@ send_message_to_server_log(ErrorData *edata)
 								 edata->filename, edata->lineno);
 			}
 		}
+
+		if ((Log_error_verbosity >= PGERROR_VERBOSE
+		 	|| edata->elevel == PANIC)
+			&& !edata->hide_stack && edata->unwind_context_valid)
+		{
+			log_line_prefix(&buf, edata);
+			appendStringInfoString(&buf, _("STACK: "));
+			append_unwind_backtrace(&buf, edata, _("< no stack >"));
+		}
 	}
 
 	/*
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 79986e9241..4537644e63 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -339,6 +339,9 @@
 /* Define to 1 if you have the `xslt' library (-lxslt). */
 #undef HAVE_LIBXSLT
 
+/* Define to 1 if you have the `unwind' library (-lunwind). */
+#undef HAVE_LIBUNWIND
+
 /* Define to 1 if you have the `z' library (-lz). */
 #undef HAVE_LIBZ
 
@@ -834,6 +837,9 @@
    (--with-libxslt) */
 #undef USE_LIBXSLT
 
+/* Define to 1 to use libunwind support */
+#undef USE_LIBUNWIND
+
 /* Define to select named POSIX semaphores. */
 #undef USE_NAMED_POSIX_SEMAPHORES
 
diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h
index f3b35297d1..8612a029e9 100644
--- a/src/include/pg_config_manual.h
+++ b/src/include/pg_config_manual.h
@@ -256,7 +256,7 @@
  * You should normally use MEMORY_CONTEXT_CHECKING with USE_VALGRIND;
  * instrumentation of repalloc() is inferior without it.
  */
-/* #define USE_VALGRIND */
+#define USE_VALGRIND
 
 /*
  * Define this to cause pfree()'d memory to be cleared immediately, to
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 7bfd25a9e9..6157280252 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -16,6 +16,11 @@
 
 #include <setjmp.h>
 
+#ifdef USE_LIBUNWIND
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#endif
+
 /* Error level codes */
 #define DEBUG5		10			/* Debugging messages, in categories of
 								 * decreasing detail. */
@@ -169,6 +174,7 @@ extern int	errcontext_msg(const char *fmt,...) pg_attribute_printf(1, 2);
 
 extern int	errhidestmt(bool hide_stmt);
 extern int	errhidecontext(bool hide_ctx);
+extern int  errhidestack(bool hide_stack);
 
 extern int	errfunction(const char *funcname);
 extern int	errposition(int cursorpos);
@@ -334,6 +340,7 @@ typedef struct ErrorData
 	bool		show_funcname;	/* true to force funcname inclusion */
 	bool		hide_stmt;		/* true to prevent STATEMENT: inclusion */
 	bool		hide_ctx;		/* true to prevent CONTEXT: inclusion */
+	bool		hide_stack;		/* true to prevent STACK: inclusion */
 	const char *filename;		/* __FILE__ of ereport() call */
 	int			lineno;			/* __LINE__ of ereport() call */
 	const char *funcname;		/* __func__ of ereport() call */
@@ -358,6 +365,11 @@ typedef struct ErrorData
 
 	/* context containing associated non-constant strings */
 	struct MemoryContextData *assoc_context;
+
+#ifdef USE_LIBUNWIND
+	unw_context_t unwind_context; /* call context STACK info for libunwind */
+	bool		  unwind_context_valid;
+#endif
 } ErrorData;
 
 extern void EmitErrorReport(void);
-- 
2.14.3

Reply via email to