Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1i8oFy-0006Ct-D2 for pgsql-hackers@arkaria.postgresql.org; Fri, 13 Sep 2019 16:17:10 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.89) (envelope-from ) id 1i8oFw-0005hz-VT for pgsql-hackers@arkaria.postgresql.org; Fri, 13 Sep 2019 16:17:08 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1i8oFw-0005hq-H4 for pgsql-hackers@lists.postgresql.org; Fri, 13 Sep 2019 16:17:08 +0000 Received: from mail-qk1-x744.google.com ([2607:f8b0:4864:20::744]) by magus.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1i8oFs-0004kk-Rb for pgsql-hackers@postgresql.org; Fri, 13 Sep 2019 16:17:08 +0000 Received: by mail-qk1-x744.google.com with SMTP id u186so4971111qkc.5 for ; Fri, 13 Sep 2019 09:17:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=2ndquadrant-com.20150623.gappssmtp.com; s=20150623; h=date:from:to:cc:subject:message-id:mime-version:content-disposition :content-transfer-encoding:in-reply-to:user-agent; bh=NlsSsc0GFzweJSfP4skFxKiYL6+wZOpXWQ1/FR9qoVw=; b=CoLN1SVtrj8cO+mWfuRfZ+4ZH46Z2jofawF6mE+tlVwDlnZyyxEp/yxgjxnaNyb3/9 5MysSctZK237YOxtUu6Ibm7FeSsYaUV5CwqxV+f7/Xe7Pp7KEiOyx5ENJnUTqbsVzaP8 oMumwqKdrU+WaxJrrEecUKJ0VinKZ6Q0GgzTNVhju4SUNGdXwpk/BxWlmw95vOMIStig gBPNmNzqaKsdsjZdvLASmjHuVga3vk83lNLLt8/cXevzT4bu2RqPWYg3WGKWMdD7r12R 41kMNi/Z/ZeMHBHgL7HnXQXZXsFPqd9GsdSqcnVnCof4pMRl2bs5SZdpZbzHMDd7lloD hZXg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:mime-version :content-disposition:content-transfer-encoding:in-reply-to :user-agent; bh=NlsSsc0GFzweJSfP4skFxKiYL6+wZOpXWQ1/FR9qoVw=; b=X9He96QwsJ9JpfTFFbGJ9vudCAkit7fd6vDgPFX+swkzSDnFqGsW5P1KX65rnZ9Aan 1Wxg65h+A38bxaWCvoeJKLhX4XRQuFiaOH3vnCDM+ZMQI56QiK3hShRqwAOxsP7GwHSN YawCrsjkr4jAhfecesxKl+kzAwny40ocSJJw5fxg40b23KvikdZfFywW/Y1LBTcKU81Y +/4k7pfUjuhKzedUOjFJplkIsekna5Nvmk3qUDYhxQzWeF8YVggh4YpjtbRfqugbA465 OzEQvb9B87geErWxBWM7hhosRpPIYjmDSKkYgHhYzQ4fMsJbnOxheoQU+wAOQbPeQRk6 Q3Mw== X-Gm-Message-State: APjAAAVRdWCekSq8PLgDsbs3e6T8lCkmzCkMTtacelxSpCJRdSWGHuZl USLWTWEGI49hqzIbnyMDYsd1Z0ENIf4= X-Google-Smtp-Source: APXvYqw/ext1qNJsUkeoZT3mFIVnQIPnRoN/iyakz9LwWY3Q+4ib7TZISL5LsXqvH4qja7Rn+SaUgQ== X-Received: by 2002:a37:8701:: with SMTP id j1mr46231562qkd.262.1568390080318; Fri, 13 Sep 2019 08:54:40 -0700 (PDT) Received: from nimloth.alvh.no-ip.org ([191.125.99.73]) by smtp.gmail.com with ESMTPSA id h68sm13543780qkd.35.2019.09.13.08.54.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 13 Sep 2019 08:54:39 -0700 (PDT) Received: by nimloth.alvh.no-ip.org (Postfix, from userid 1000) id 5BA67120269; Fri, 13 Sep 2019 12:54:32 -0300 (-03) Date: Fri, 13 Sep 2019 12:54:32 -0300 From: Alvaro Herrera To: Peter Eisentraut Cc: pgsql-hackers , Dmitry Dolgov <9erthalion6@gmail.com>, Thomas Munro Subject: Re: errbacktrace Message-ID: <20190913155432.GA1200@alvherre.pgsql> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="wac7ysb48OaltWcw" Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: User-Agent: Mutt/1.9.4 (2018-02-28) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk --wac7ysb48OaltWcw Content-Type: text/plain; charset=iso-8859-1 Content-Disposition: inline Content-Transfer-Encoding: 8bit On 2019-Aug-20, Peter Eisentraut wrote: > The memory management of that seems too complicated. The "extra" > mechanism of the check/assign hooks only supports one level of malloc. > Using a List seems impossible. I don't know if you can safely do a > malloc-ed array of malloc-ed strings either. Here's an idea -- have the check/assign hooks create a different representation, which is a single guc_malloc'ed chunk that is made up of every function name listed in the GUC, separated by \0. That can be scanned at error time comparing the function name with each piece. -- Álvaro Herrera https://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services --wac7ysb48OaltWcw Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v5-0001-Add-backtrace-support.patch" From 7341310cd75722fbfe7cb7996516b0b50d83bb1f Mon Sep 17 00:00:00 2001 From: Alvaro Herrera Date: Fri, 13 Sep 2019 12:51:13 -0300 Subject: [PATCH v5] Add backtrace support Add some support for automatically showing backtraces in certain error situations in the server. Backtraces are shown on assertion failure. A new setting backtrace_functions can be set to a list of C function names, and all ereport()s and elog()s from the functions will have backtraces generated. Finally, the function errbacktrace() can be manually added to an ereport() call to generate a backtrace for that call. Discussion: https://www.postgresql.org/message-id/flat/5f48cb47-bf1e-05b6-7aae-3bf2cd01586d@2ndquadrant.com --- configure | 61 +++++++++++++++- configure.in | 4 ++ doc/src/sgml/config.sgml | 26 +++++++ src/backend/utils/error/assert.c | 13 ++++ src/backend/utils/error/elog.c | 117 +++++++++++++++++++++++++++++++ src/backend/utils/misc/guc.c | 67 ++++++++++++++++++ src/include/pg_config.h.in | 6 ++ src/include/utils/elog.h | 3 + src/include/utils/guc.h | 2 + 9 files changed, 297 insertions(+), 2 deletions(-) diff --git a/configure b/configure index b3c92764be..900b83dc4e 100755 --- a/configure +++ b/configure @@ -11606,6 +11606,63 @@ if test "$ac_res" != no; then : fi +# *BSD: +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing backtrace_symbols" >&5 +$as_echo_n "checking for library containing backtrace_symbols... " >&6; } +if ${ac_cv_search_backtrace_symbols+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char backtrace_symbols (); +int +main () +{ +return backtrace_symbols (); + ; + return 0; +} +_ACEOF +for ac_lib in '' execinfo; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_backtrace_symbols=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_backtrace_symbols+:} false; then : + break +fi +done +if ${ac_cv_search_backtrace_symbols+:} false; then : + +else + ac_cv_search_backtrace_symbols=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_backtrace_symbols" >&5 +$as_echo "$ac_cv_search_backtrace_symbols" >&6; } +ac_res=$ac_cv_search_backtrace_symbols +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + if test "$with_readline" = yes; then @@ -12704,7 +12761,7 @@ $as_echo "#define HAVE_STDBOOL_H 1" >>confdefs.h fi -for ac_header in atomic.h copyfile.h fp_class.h getopt.h ieeefp.h ifaddrs.h langinfo.h mbarrier.h poll.h sys/epoll.h sys/ipc.h sys/prctl.h sys/procctl.h sys/pstat.h sys/resource.h sys/select.h sys/sem.h sys/shm.h sys/sockio.h sys/tas.h sys/un.h termios.h ucred.h utime.h wchar.h wctype.h +for ac_header in atomic.h copyfile.h execinfo.h fp_class.h getopt.h ieeefp.h ifaddrs.h langinfo.h mbarrier.h poll.h sys/epoll.h sys/ipc.h sys/prctl.h sys/procctl.h sys/pstat.h sys/resource.h sys/select.h sys/sem.h sys/shm.h sys/sockio.h sys/tas.h sys/un.h termios.h ucred.h utime.h wchar.h wctype.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" @@ -15087,7 +15144,7 @@ fi LIBS_including_readline="$LIBS" LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'` -for ac_func in cbrt clock_gettime copyfile fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memset_s memmove poll posix_fallocate ppoll pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open strchrnul strsignal symlink sync_file_range uselocale utime utimes wcstombs_l +for ac_func in backtrace_symbols cbrt clock_gettime copyfile fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memset_s memmove poll posix_fallocate ppoll pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open strchrnul strsignal symlink sync_file_range uselocale utime utimes wcstombs_l do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" diff --git a/configure.in b/configure.in index 0d16c1a971..15f9fffacb 100644 --- a/configure.in +++ b/configure.in @@ -1130,6 +1130,8 @@ AC_SEARCH_LIBS(sched_yield, rt) AC_SEARCH_LIBS(gethostbyname_r, nsl) # Cygwin: AC_SEARCH_LIBS(shmget, cygipc) +# *BSD: +AC_SEARCH_LIBS(backtrace_symbols, execinfo) if test "$with_readline" = yes; then PGAC_CHECK_READLINE @@ -1272,6 +1274,7 @@ AC_HEADER_STDBOOL AC_CHECK_HEADERS(m4_normalize([ atomic.h copyfile.h + execinfo.h fp_class.h getopt.h ieeefp.h @@ -1586,6 +1589,7 @@ LIBS_including_readline="$LIBS" LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'` AC_CHECK_FUNCS(m4_normalize([ + backtrace_symbols cbrt clock_gettime copyfile diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index 7f9ce8fcba..78b0f79d79 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -9299,6 +9299,32 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir' + + backtrace_functions (string) + + backtrace_functions configuration parameter + + + + + This parameter contains a comma-separated list of C function names. + If an error is raised and the name of the internal C function where + the error happens matches a value in the list, then a backtrace is + written to the server log together with the error message. This can + be used to debug specific areas of the source code. + + + + Backtrace support is not available on all platforms, and the quality + of the backtraces depends on compilation options. + + + + This parameter can only be set by superusers. + + + + ignore_system_indexes (boolean) diff --git a/src/backend/utils/error/assert.c b/src/backend/utils/error/assert.c index 2050b4355d..14707fc296 100644 --- a/src/backend/utils/error/assert.c +++ b/src/backend/utils/error/assert.c @@ -18,6 +18,9 @@ #include "postgres.h" #include +#ifdef HAVE_EXECINFO_H +#include +#endif /* * ExceptionalCondition - Handles the failure of an Assert() @@ -42,6 +45,16 @@ ExceptionalCondition(const char *conditionName, /* Usually this shouldn't be needed, but make sure the msg went out */ fflush(stderr); +#ifdef HAVE_BACKTRACE_SYMBOLS + { + void *buf[100]; + int nframes; + + nframes = backtrace(buf, sizeof(buf)); + backtrace_symbols_fd(buf, nframes, fileno(stderr)); + } +#endif + #ifdef SLEEP_ON_ASSERT /* diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index 8b4720ef3a..715b140ebb 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -62,6 +62,9 @@ #ifdef HAVE_SYSLOG #include #endif +#ifdef HAVE_EXECINFO_H +#include +#endif #include "access/transam.h" #include "access/xact.h" @@ -166,6 +169,7 @@ static char formatted_log_time[FORMATTED_TS_LEN]; } while (0) +static pg_noinline void set_backtrace(ErrorData *edata, int num_skip); static const char *err_gettext(const char *str) pg_attribute_format_arg(1); static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str); static void write_console(const char *line, int len); @@ -398,6 +402,31 @@ errstart(int elevel, const char *filename, int lineno, return true; } +/* + * Checks whether the given funcname matches backtrace_functions. + */ +static bool +matches_backtrace_functions(const char *funcname) +{ + char *p; + + if (!backtrace_symbol_list || funcname == NULL || funcname[0] == '\0') + return false; + + p = backtrace_symbol_list; + for (;;) + { + if (*p == '\0') + break; + + if (strcmp(funcname, p) == 0) + return true; + p += strlen(p) + 1; + } + + return false; +} + /* * errfinish --- end an error-reporting cycle * @@ -424,6 +453,12 @@ errfinish(int dummy,...) */ oldcontext = MemoryContextSwitchTo(ErrorContext); + if (!edata->backtrace && + edata->funcname && + backtrace_functions && + matches_backtrace_functions(edata->funcname)) + set_backtrace(edata, 2); + /* * Call any context callback functions. Errors occurring in callback * functions will be treated as recursive errors --- this ensures we will @@ -488,6 +523,8 @@ errfinish(int dummy,...) pfree(edata->hint); if (edata->context) pfree(edata->context); + if (edata->backtrace) + pfree(edata->backtrace); if (edata->schema_name) pfree(edata->schema_name); if (edata->table_name) @@ -798,6 +835,66 @@ errmsg(const char *fmt,...) return 0; /* return value does not matter */ } +/* + * Add a backtrace to the containing ereport() call. This is intended to be + * added temporarily during debugging. + */ +int +errbacktrace(void) +{ + ErrorData *edata = &errordata[errordata_stack_depth]; + MemoryContext oldcontext; + + Assert(false); + + recursion_depth++; + CHECK_STACK_DEPTH(); + oldcontext = MemoryContextSwitchTo(edata->assoc_context); + + set_backtrace(edata, 1); + + MemoryContextSwitchTo(oldcontext); + recursion_depth--; + + return 0; +} + +/* + * 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. + */ +static void +set_backtrace(ErrorData *edata, int num_skip) +{ + StringInfoData errtrace; + + fprintf(stderr, "set_backtrace called\n"); + + initStringInfo(&errtrace); + +#ifdef HAVE_BACKTRACE_SYMBOLS + { + void *buf[100]; + int nframes; + char **strfrms; + + nframes = backtrace(buf, sizeof(buf)); + strfrms = backtrace_symbols(buf, nframes); + if (strfrms == NULL) + return; + + for (int i = num_skip; i < nframes; i++) + appendStringInfo(&errtrace, "\n%s", strfrms[i]); + free(strfrms); + } +#else + appendStringInfoString(&errtrace, "backtrace not supported"); +#endif + + edata->backtrace = errtrace.data; +} /* * errmsg_internal --- add a primary error message text to the current error @@ -1353,6 +1450,11 @@ elog_finish(int elevel, const char *fmt,...) recursion_depth++; oldcontext = MemoryContextSwitchTo(edata->assoc_context); + if (!edata->backtrace && + edata->funcname && + matches_backtrace_functions(edata->funcname)) + set_backtrace(edata, 2); + edata->message_id = fmt; EVALUATE_MESSAGE(edata->domain, message, false, false); @@ -1509,6 +1611,8 @@ CopyErrorData(void) newedata->hint = pstrdup(newedata->hint); if (newedata->context) newedata->context = pstrdup(newedata->context); + if (newedata->backtrace) + newedata->backtrace = pstrdup(newedata->backtrace); if (newedata->schema_name) newedata->schema_name = pstrdup(newedata->schema_name); if (newedata->table_name) @@ -1547,6 +1651,8 @@ FreeErrorData(ErrorData *edata) pfree(edata->hint); if (edata->context) pfree(edata->context); + if (edata->backtrace) + pfree(edata->backtrace); if (edata->schema_name) pfree(edata->schema_name); if (edata->table_name) @@ -1622,6 +1728,8 @@ ThrowErrorData(ErrorData *edata) newedata->hint = pstrdup(edata->hint); if (edata->context) newedata->context = pstrdup(edata->context); + if (edata->backtrace) + newedata->backtrace = pstrdup(edata->backtrace); /* assume message_id is not available */ if (edata->schema_name) newedata->schema_name = pstrdup(edata->schema_name); @@ -1689,6 +1797,8 @@ ReThrowError(ErrorData *edata) newedata->hint = pstrdup(newedata->hint); if (newedata->context) newedata->context = pstrdup(newedata->context); + if (newedata->backtrace) + newedata->backtrace = pstrdup(newedata->backtrace); if (newedata->schema_name) newedata->schema_name = pstrdup(newedata->schema_name); if (newedata->table_name) @@ -2914,6 +3024,13 @@ send_message_to_server_log(ErrorData *edata) append_with_tabs(&buf, edata->context); appendStringInfoChar(&buf, '\n'); } + if (edata->backtrace) + { + log_line_prefix(&buf, edata); + appendStringInfoString(&buf, _("BACKTRACE: ")); + append_with_tabs(&buf, edata->backtrace); + appendStringInfoChar(&buf, '\n'); + } if (Log_error_verbosity >= PGERROR_VERBOSE) { /* assume no newlines in funcname or filename... */ diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 90ffd89339..687434b7fa 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -201,6 +201,8 @@ static bool check_cluster_name(char **newval, void **extra, GucSource source); static const char *show_unix_socket_permissions(void); static const char *show_log_file_mode(void); static const char *show_data_directory_mode(void); +static bool check_backtrace_functions(char **newval, void **extra, GucSource source); +static void assign_backtrace_functions(const char *newval, void *extra); static bool check_recovery_target_timeline(char **newval, void **extra, GucSource source); static void assign_recovery_target_timeline(const char *newval, void *extra); static bool check_recovery_target(char **newval, void **extra, GucSource source); @@ -513,6 +515,8 @@ int log_min_duration_statement = -1; int log_temp_files = -1; double log_xact_sample_rate = 0; int trace_recovery_messages = LOG; +char *backtrace_functions; +char *backtrace_symbol_list; int temp_file_limit = -1; @@ -4199,6 +4203,17 @@ static struct config_string ConfigureNamesString[] = NULL, NULL, NULL }, + { + {"backtrace_functions", PGC_SUSET, DEVELOPER_OPTIONS, + gettext_noop("Log backtrace for errors in these functions."), + NULL, + GUC_NOT_IN_SAMPLE + }, + &backtrace_functions, + "", + check_backtrace_functions, assign_backtrace_functions, NULL + }, + /* End-of-list marker */ { {NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL, NULL @@ -11433,6 +11448,58 @@ show_data_directory_mode(void) return buf; } +static bool +check_backtrace_functions(char **newval, void **extra, GucSource source) +{ + int newvallen = strlen(*newval); + char *someval; + int validlen; + int i; + int j; + + newvallen = strlen(*newval); + validlen = strspn(*newval, + ",_ \n\t" + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789"); + if (validlen != newvallen) + { + GUC_check_errdetail("invalid character"); + return false; + } + + if (*newval[0] == '\0') + { + *extra = NULL; + return true; + } + + someval = guc_malloc(ERROR, newvallen + 1 + 1); + for (i = 0, j = 0; i < newvallen; i++) + { + if ((*newval)[i] == ',') + someval[j++] = '\0'; /* next item */ + else if ((*newval)[i] == ' ' || (*newval)[i] == '\n') + ; /* ignore */ + else + someval[j++] = (*newval)[i]; + } + + /* two \0s end the setting */ + someval[j] = '\0'; + someval[j + 1] = '\0'; + *extra = someval; + + return true; +} + +static void +assign_backtrace_functions(const char *newval, void *extra) +{ + backtrace_symbol_list = (char *) extra; +} + static bool check_recovery_target_timeline(char **newval, void **extra, GucSource source) { diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index c6014e83fa..4d6a306849 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -96,6 +96,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_ATOMIC_H +/* Define to 1 if you have the `backtrace_symbols' function. */ +#undef HAVE_BACKTRACE_SYMBOLS + /* Define to 1 if you have the `BIO_get_data' function. */ #undef HAVE_BIO_GET_DATA @@ -198,6 +201,9 @@ /* Define to 1 if you have the `explicit_bzero' function. */ #undef HAVE_EXPLICIT_BZERO +/* Define to 1 if you have the header file. */ +#undef HAVE_EXECINFO_H + /* Define to 1 if you have the `fdatasync' function. */ #undef HAVE_FDATASYNC diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h index ba0b7f6f79..6a315414e2 100644 --- a/src/include/utils/elog.h +++ b/src/include/utils/elog.h @@ -189,6 +189,8 @@ 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 errbacktrace(void); + extern int errfunction(const char *funcname); extern int errposition(int cursorpos); @@ -362,6 +364,7 @@ typedef struct ErrorData char *detail_log; /* detail error message for server log only */ char *hint; /* hint message */ char *context; /* context message */ + char *backtrace; /* backtrace */ const char *message_id; /* primary message's id (original string) */ char *schema_name; /* name of schema */ char *table_name; /* name of table */ diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index 6791e0cbc2..1e1876d320 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -254,6 +254,8 @@ extern PGDLLIMPORT int client_min_messages; extern int log_min_duration_statement; extern int log_temp_files; extern double log_xact_sample_rate; +extern char *backtrace_functions; +extern char *backtrace_symbol_list; extern int temp_file_limit; -- 2.17.1 --wac7ysb48OaltWcw--