Skip to content

Commit 71a8a4f

Browse files
committed
Add backtrace support for error reporting
Add some support for automatically showing backtraces in certain error situations in the server. Backtraces are shown on assertion failure; also, a new setting backtrace_functions can be set to a list of C function names, and all ereport()s and elog()s from the mentioned functions will have backtraces generated. Finally, the function errbacktrace() can be manually added to an ereport() call to generate a backtrace for that call. Authors: Peter Eisentraut, Álvaro Herrera Discussion: https://postgr.es/m//[email protected] Discussion: https://postgr.es/m/CAMsr+YGL+yfWE=JvbUbnpWtrRZNey7hJ07+zT4bYJdVp4Szdrg@mail.gmail.com
1 parent 3dcffb3 commit 71a8a4f

File tree

9 files changed

+315
-2
lines changed

9 files changed

+315
-2
lines changed

configure

+59-2
Original file line numberDiff line numberDiff line change
@@ -11607,6 +11607,63 @@ if test "$ac_res" != no; then :
1160711607

1160811608
fi
1160911609

11610+
# *BSD:
11611+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing backtrace_symbols" >&5
11612+
$as_echo_n "checking for library containing backtrace_symbols... " >&6; }
11613+
if ${ac_cv_search_backtrace_symbols+:} false; then :
11614+
$as_echo_n "(cached) " >&6
11615+
else
11616+
ac_func_search_save_LIBS=$LIBS
11617+
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
11618+
/* end confdefs.h. */
11619+
11620+
/* Override any GCC internal prototype to avoid an error.
11621+
Use char because int might match the return type of a GCC
11622+
builtin and then its argument prototype would still apply. */
11623+
#ifdef __cplusplus
11624+
extern "C"
11625+
#endif
11626+
char backtrace_symbols ();
11627+
int
11628+
main ()
11629+
{
11630+
return backtrace_symbols ();
11631+
;
11632+
return 0;
11633+
}
11634+
_ACEOF
11635+
for ac_lib in '' execinfo; do
11636+
if test -z "$ac_lib"; then
11637+
ac_res="none required"
11638+
else
11639+
ac_res=-l$ac_lib
11640+
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
11641+
fi
11642+
if ac_fn_c_try_link "$LINENO"; then :
11643+
ac_cv_search_backtrace_symbols=$ac_res
11644+
fi
11645+
rm -f core conftest.err conftest.$ac_objext \
11646+
conftest$ac_exeext
11647+
if ${ac_cv_search_backtrace_symbols+:} false; then :
11648+
break
11649+
fi
11650+
done
11651+
if ${ac_cv_search_backtrace_symbols+:} false; then :
11652+
11653+
else
11654+
ac_cv_search_backtrace_symbols=no
11655+
fi
11656+
rm conftest.$ac_ext
11657+
LIBS=$ac_func_search_save_LIBS
11658+
fi
11659+
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_backtrace_symbols" >&5
11660+
$as_echo "$ac_cv_search_backtrace_symbols" >&6; }
11661+
ac_res=$ac_cv_search_backtrace_symbols
11662+
if test "$ac_res" != no; then :
11663+
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
11664+
11665+
fi
11666+
1161011667

1161111668
if test "$with_readline" = yes; then
1161211669

@@ -12705,7 +12762,7 @@ $as_echo "#define HAVE_STDBOOL_H 1" >>confdefs.h
1270512762
fi
1270612763

1270712764

12708-
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
12765+
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
1270912766
do :
1271012767
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
1271112768
ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
@@ -14935,7 +14992,7 @@ fi
1493514992
LIBS_including_readline="$LIBS"
1493614993
LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
1493714994

14938-
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
14995+
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
1493914996
do :
1494014997
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
1494114998
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"

configure.in

+4
Original file line numberDiff line numberDiff line change
@@ -1133,6 +1133,8 @@ AC_SEARCH_LIBS(sched_yield, rt)
11331133
AC_SEARCH_LIBS(gethostbyname_r, nsl)
11341134
# Cygwin:
11351135
AC_SEARCH_LIBS(shmget, cygipc)
1136+
# *BSD:
1137+
AC_SEARCH_LIBS(backtrace_symbols, execinfo)
11361138

11371139
if test "$with_readline" = yes; then
11381140
PGAC_CHECK_READLINE
@@ -1275,6 +1277,7 @@ AC_HEADER_STDBOOL
12751277
AC_CHECK_HEADERS(m4_normalize([
12761278
atomic.h
12771279
copyfile.h
1280+
execinfo.h
12781281
fp_class.h
12791282
getopt.h
12801283
ieeefp.h
@@ -1608,6 +1611,7 @@ LIBS_including_readline="$LIBS"
16081611
LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
16091612

16101613
AC_CHECK_FUNCS(m4_normalize([
1614+
backtrace_symbols
16111615
cbrt
16121616
clock_gettime
16131617
copyfile

doc/src/sgml/config.sgml

+26
Original file line numberDiff line numberDiff line change
@@ -9489,6 +9489,32 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
94899489
</listitem>
94909490
</varlistentry>
94919491

9492+
<varlistentry id="guc-backtrace-functions" xreflabel="backtrace_functions">
9493+
<term><varname>backtrace_functions</varname> (<type>string</type>)
9494+
<indexterm>
9495+
<primary><varname>backtrace_functions</varname> configuration parameter</primary>
9496+
</indexterm>
9497+
</term>
9498+
<listitem>
9499+
<para>
9500+
This parameter contains a comma-separated list of C function names.
9501+
If an error is raised and the name of the internal C function where
9502+
the error happens matches a value in the list, then a backtrace is
9503+
written to the server log together with the error message. This can
9504+
be used to debug specific areas of the source code.
9505+
</para>
9506+
9507+
<para>
9508+
Backtrace support is not available on all platforms, and the quality
9509+
of the backtraces depends on compilation options.
9510+
</para>
9511+
9512+
<para>
9513+
This parameter can only be set by superusers.
9514+
</para>
9515+
</listitem>
9516+
</varlistentry>
9517+
94929518
<varlistentry id="guc-ignore-system-indexes" xreflabel="ignore_system_indexes">
94939519
<term><varname>ignore_system_indexes</varname> (<type>boolean</type>)
94949520
<indexterm>

src/backend/utils/error/assert.c

+13
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
#include "postgres.h"
1919

2020
#include <unistd.h>
21+
#ifdef HAVE_EXECINFO_H
22+
#include <execinfo.h>
23+
#endif
2124

2225
/*
2326
* ExceptionalCondition - Handles the failure of an Assert()
@@ -42,6 +45,16 @@ ExceptionalCondition(const char *conditionName,
4245
/* Usually this shouldn't be needed, but make sure the msg went out */
4346
fflush(stderr);
4447

48+
#ifdef HAVE_BACKTRACE_SYMBOLS
49+
{
50+
void *buf[100];
51+
int nframes;
52+
53+
nframes = backtrace(buf, lengthof(buf));
54+
backtrace_symbols_fd(buf, nframes, fileno(stderr));
55+
}
56+
#endif
57+
4558
#ifdef SLEEP_ON_ASSERT
4659

4760
/*

src/backend/utils/error/elog.c

+117
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@
6262
#ifdef HAVE_SYSLOG
6363
#include <syslog.h>
6464
#endif
65+
#ifdef HAVE_EXECINFO_H
66+
#include <execinfo.h>
67+
#endif
6568

6669
#include "access/transam.h"
6770
#include "access/xact.h"
@@ -167,6 +170,7 @@ static char formatted_log_time[FORMATTED_TS_LEN];
167170

168171

169172
static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
173+
static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
170174
static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
171175
static void write_console(const char *line, int len);
172176
static void setup_formatted_log_time(void);
@@ -398,6 +402,32 @@ errstart(int elevel, const char *filename, int lineno,
398402
return true;
399403
}
400404

405+
/*
406+
* Checks whether the given funcname matches backtrace_functions; see
407+
* check_backtrace_functions.
408+
*/
409+
static bool
410+
matches_backtrace_functions(const char *funcname)
411+
{
412+
char *p;
413+
414+
if (!backtrace_symbol_list || funcname == NULL || funcname[0] == '\0')
415+
return false;
416+
417+
p = backtrace_symbol_list;
418+
for (;;)
419+
{
420+
if (*p == '\0') /* end of backtrace_symbol_list */
421+
break;
422+
423+
if (strcmp(funcname, p) == 0)
424+
return true;
425+
p += strlen(p) + 1;
426+
}
427+
428+
return false;
429+
}
430+
401431
/*
402432
* errfinish --- end an error-reporting cycle
403433
*
@@ -424,6 +454,12 @@ errfinish(int dummy,...)
424454
*/
425455
oldcontext = MemoryContextSwitchTo(ErrorContext);
426456

457+
if (!edata->backtrace &&
458+
edata->funcname &&
459+
backtrace_functions &&
460+
matches_backtrace_functions(edata->funcname))
461+
set_backtrace(edata, 2);
462+
427463
/*
428464
* Call any context callback functions. Errors occurring in callback
429465
* functions will be treated as recursive errors --- this ensures we will
@@ -488,6 +524,8 @@ errfinish(int dummy,...)
488524
pfree(edata->hint);
489525
if (edata->context)
490526
pfree(edata->context);
527+
if (edata->backtrace)
528+
pfree(edata->backtrace);
491529
if (edata->schema_name)
492530
pfree(edata->schema_name);
493531
if (edata->table_name)
@@ -798,6 +836,65 @@ errmsg(const char *fmt,...)
798836
return 0; /* return value does not matter */
799837
}
800838

839+
/*
840+
* Add a backtrace to the containing ereport() call. This is intended to be
841+
* added temporarily during debugging.
842+
*/
843+
int
844+
errbacktrace(void)
845+
{
846+
ErrorData *edata = &errordata[errordata_stack_depth];
847+
MemoryContext oldcontext;
848+
849+
Assert(false);
850+
851+
recursion_depth++;
852+
CHECK_STACK_DEPTH();
853+
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
854+
855+
set_backtrace(edata, 1);
856+
857+
MemoryContextSwitchTo(oldcontext);
858+
recursion_depth--;
859+
860+
return 0;
861+
}
862+
863+
/*
864+
* Compute backtrace data and add it to the supplied ErrorData. num_skip
865+
* specifies how many inner frames to skip. Use this to avoid showing the
866+
* internal backtrace support functions in the backtrace. This requires that
867+
* this and related functions are not inlined.
868+
*/
869+
static void
870+
set_backtrace(ErrorData *edata, int num_skip)
871+
{
872+
StringInfoData errtrace;
873+
874+
initStringInfo(&errtrace);
875+
876+
#ifdef HAVE_BACKTRACE_SYMBOLS
877+
{
878+
void *buf[100];
879+
int nframes;
880+
char **strfrms;
881+
882+
nframes = backtrace(buf, lengthof(buf));
883+
strfrms = backtrace_symbols(buf, nframes);
884+
if (strfrms == NULL)
885+
return;
886+
887+
for (int i = num_skip; i < nframes; i++)
888+
appendStringInfo(&errtrace, "\n%s", strfrms[i]);
889+
free(strfrms);
890+
}
891+
#else
892+
appendStringInfoString(&errtrace,
893+
"backtrace generation is not supported by this installation");
894+
#endif
895+
896+
edata->backtrace = errtrace.data;
897+
}
801898

802899
/*
803900
* errmsg_internal --- add a primary error message text to the current error
@@ -1353,6 +1450,11 @@ elog_finish(int elevel, const char *fmt,...)
13531450
recursion_depth++;
13541451
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
13551452

1453+
if (!edata->backtrace &&
1454+
edata->funcname &&
1455+
matches_backtrace_functions(edata->funcname))
1456+
set_backtrace(edata, 2);
1457+
13561458
edata->message_id = fmt;
13571459
EVALUATE_MESSAGE(edata->domain, message, false, false);
13581460

@@ -1509,6 +1611,8 @@ CopyErrorData(void)
15091611
newedata->hint = pstrdup(newedata->hint);
15101612
if (newedata->context)
15111613
newedata->context = pstrdup(newedata->context);
1614+
if (newedata->backtrace)
1615+
newedata->backtrace = pstrdup(newedata->backtrace);
15121616
if (newedata->schema_name)
15131617
newedata->schema_name = pstrdup(newedata->schema_name);
15141618
if (newedata->table_name)
@@ -1547,6 +1651,8 @@ FreeErrorData(ErrorData *edata)
15471651
pfree(edata->hint);
15481652
if (edata->context)
15491653
pfree(edata->context);
1654+
if (edata->backtrace)
1655+
pfree(edata->backtrace);
15501656
if (edata->schema_name)
15511657
pfree(edata->schema_name);
15521658
if (edata->table_name)
@@ -1622,6 +1728,8 @@ ThrowErrorData(ErrorData *edata)
16221728
newedata->hint = pstrdup(edata->hint);
16231729
if (edata->context)
16241730
newedata->context = pstrdup(edata->context);
1731+
if (edata->backtrace)
1732+
newedata->backtrace = pstrdup(edata->backtrace);
16251733
/* assume message_id is not available */
16261734
if (edata->schema_name)
16271735
newedata->schema_name = pstrdup(edata->schema_name);
@@ -1689,6 +1797,8 @@ ReThrowError(ErrorData *edata)
16891797
newedata->hint = pstrdup(newedata->hint);
16901798
if (newedata->context)
16911799
newedata->context = pstrdup(newedata->context);
1800+
if (newedata->backtrace)
1801+
newedata->backtrace = pstrdup(newedata->backtrace);
16921802
if (newedata->schema_name)
16931803
newedata->schema_name = pstrdup(newedata->schema_name);
16941804
if (newedata->table_name)
@@ -2914,6 +3024,13 @@ send_message_to_server_log(ErrorData *edata)
29143024
append_with_tabs(&buf, edata->context);
29153025
appendStringInfoChar(&buf, '\n');
29163026
}
3027+
if (edata->backtrace)
3028+
{
3029+
log_line_prefix(&buf, edata);
3030+
appendStringInfoString(&buf, _("BACKTRACE: "));
3031+
append_with_tabs(&buf, edata->backtrace);
3032+
appendStringInfoChar(&buf, '\n');
3033+
}
29173034
if (Log_error_verbosity >= PGERROR_VERBOSE)
29183035
{
29193036
/* assume no newlines in funcname or filename... */

0 commit comments

Comments
 (0)