mirror of
https://github.com/postgres/postgres.git
synced 2025-11-04 20:11:56 +03:00
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//5f48cb47-bf1e-05b6-7aae-3bf2cd01586d@2ndquadrant.com Discussion: https://postgr.es/m/CAMsr+YGL+yfWE=JvbUbnpWtrRZNey7hJ07+zT4bYJdVp4Szdrg@mail.gmail.com
This commit is contained in:
@@ -18,6 +18,9 @@
|
||||
#include "postgres.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#ifdef HAVE_EXECINFO_H
|
||||
#include <execinfo.h>
|
||||
#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, lengthof(buf));
|
||||
backtrace_symbols_fd(buf, nframes, fileno(stderr));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SLEEP_ON_ASSERT
|
||||
|
||||
/*
|
||||
|
||||
@@ -62,6 +62,9 @@
|
||||
#ifdef HAVE_SYSLOG
|
||||
#include <syslog.h>
|
||||
#endif
|
||||
#ifdef HAVE_EXECINFO_H
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
|
||||
#include "access/transam.h"
|
||||
#include "access/xact.h"
|
||||
@@ -167,6 +170,7 @@ static char formatted_log_time[FORMATTED_TS_LEN];
|
||||
|
||||
|
||||
static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
|
||||
static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
|
||||
static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
|
||||
static void write_console(const char *line, int len);
|
||||
static void setup_formatted_log_time(void);
|
||||
@@ -398,6 +402,32 @@ errstart(int elevel, const char *filename, int lineno,
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks whether the given funcname matches backtrace_functions; see
|
||||
* check_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') /* end of backtrace_symbol_list */
|
||||
break;
|
||||
|
||||
if (strcmp(funcname, p) == 0)
|
||||
return true;
|
||||
p += strlen(p) + 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* errfinish --- end an error-reporting cycle
|
||||
*
|
||||
@@ -424,6 +454,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 +524,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 +836,65 @@ 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;
|
||||
|
||||
initStringInfo(&errtrace);
|
||||
|
||||
#ifdef HAVE_BACKTRACE_SYMBOLS
|
||||
{
|
||||
void *buf[100];
|
||||
int nframes;
|
||||
char **strfrms;
|
||||
|
||||
nframes = backtrace(buf, lengthof(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 generation is not supported by this installation");
|
||||
#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... */
|
||||
|
||||
Reference in New Issue
Block a user