From d9ff6bd8112b31d087d8442f25ffd430df794771 Mon Sep 17 00:00:00 2001
From: Craig Ringer <craig@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

