From d9ff6bd8112b31d087d8442f25ffd430df794771 Mon Sep 17 00:00:00 2001 From: Craig Ringer 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 ? " " : "", + 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 +#ifdef USE_LIBUNWIND +#define UNW_LOCAL_ONLY +#include +#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