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