62
62
#ifdef HAVE_SYSLOG
63
63
#include <syslog.h>
64
64
#endif
65
+ #ifdef HAVE_EXECINFO_H
66
+ #include <execinfo.h>
67
+ #endif
65
68
66
69
#include "access/transam.h"
67
70
#include "access/xact.h"
@@ -167,6 +170,7 @@ static char formatted_log_time[FORMATTED_TS_LEN];
167
170
168
171
169
172
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 );
170
174
static void set_errdata_field (MemoryContextData * cxt , char * * ptr , const char * str );
171
175
static void write_console (const char * line , int len );
172
176
static void setup_formatted_log_time (void );
@@ -398,6 +402,32 @@ errstart(int elevel, const char *filename, int lineno,
398
402
return true;
399
403
}
400
404
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
+
401
431
/*
402
432
* errfinish --- end an error-reporting cycle
403
433
*
@@ -424,6 +454,12 @@ errfinish(int dummy,...)
424
454
*/
425
455
oldcontext = MemoryContextSwitchTo (ErrorContext );
426
456
457
+ if (!edata -> backtrace &&
458
+ edata -> funcname &&
459
+ backtrace_functions &&
460
+ matches_backtrace_functions (edata -> funcname ))
461
+ set_backtrace (edata , 2 );
462
+
427
463
/*
428
464
* Call any context callback functions. Errors occurring in callback
429
465
* functions will be treated as recursive errors --- this ensures we will
@@ -488,6 +524,8 @@ errfinish(int dummy,...)
488
524
pfree (edata -> hint );
489
525
if (edata -> context )
490
526
pfree (edata -> context );
527
+ if (edata -> backtrace )
528
+ pfree (edata -> backtrace );
491
529
if (edata -> schema_name )
492
530
pfree (edata -> schema_name );
493
531
if (edata -> table_name )
@@ -798,6 +836,65 @@ errmsg(const char *fmt,...)
798
836
return 0 ; /* return value does not matter */
799
837
}
800
838
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
+ }
801
898
802
899
/*
803
900
* errmsg_internal --- add a primary error message text to the current error
@@ -1353,6 +1450,11 @@ elog_finish(int elevel, const char *fmt,...)
1353
1450
recursion_depth ++ ;
1354
1451
oldcontext = MemoryContextSwitchTo (edata -> assoc_context );
1355
1452
1453
+ if (!edata -> backtrace &&
1454
+ edata -> funcname &&
1455
+ matches_backtrace_functions (edata -> funcname ))
1456
+ set_backtrace (edata , 2 );
1457
+
1356
1458
edata -> message_id = fmt ;
1357
1459
EVALUATE_MESSAGE (edata -> domain , message , false, false);
1358
1460
@@ -1509,6 +1611,8 @@ CopyErrorData(void)
1509
1611
newedata -> hint = pstrdup (newedata -> hint );
1510
1612
if (newedata -> context )
1511
1613
newedata -> context = pstrdup (newedata -> context );
1614
+ if (newedata -> backtrace )
1615
+ newedata -> backtrace = pstrdup (newedata -> backtrace );
1512
1616
if (newedata -> schema_name )
1513
1617
newedata -> schema_name = pstrdup (newedata -> schema_name );
1514
1618
if (newedata -> table_name )
@@ -1547,6 +1651,8 @@ FreeErrorData(ErrorData *edata)
1547
1651
pfree (edata -> hint );
1548
1652
if (edata -> context )
1549
1653
pfree (edata -> context );
1654
+ if (edata -> backtrace )
1655
+ pfree (edata -> backtrace );
1550
1656
if (edata -> schema_name )
1551
1657
pfree (edata -> schema_name );
1552
1658
if (edata -> table_name )
@@ -1622,6 +1728,8 @@ ThrowErrorData(ErrorData *edata)
1622
1728
newedata -> hint = pstrdup (edata -> hint );
1623
1729
if (edata -> context )
1624
1730
newedata -> context = pstrdup (edata -> context );
1731
+ if (edata -> backtrace )
1732
+ newedata -> backtrace = pstrdup (edata -> backtrace );
1625
1733
/* assume message_id is not available */
1626
1734
if (edata -> schema_name )
1627
1735
newedata -> schema_name = pstrdup (edata -> schema_name );
@@ -1689,6 +1797,8 @@ ReThrowError(ErrorData *edata)
1689
1797
newedata -> hint = pstrdup (newedata -> hint );
1690
1798
if (newedata -> context )
1691
1799
newedata -> context = pstrdup (newedata -> context );
1800
+ if (newedata -> backtrace )
1801
+ newedata -> backtrace = pstrdup (newedata -> backtrace );
1692
1802
if (newedata -> schema_name )
1693
1803
newedata -> schema_name = pstrdup (newedata -> schema_name );
1694
1804
if (newedata -> table_name )
@@ -2914,6 +3024,13 @@ send_message_to_server_log(ErrorData *edata)
2914
3024
append_with_tabs (& buf , edata -> context );
2915
3025
appendStringInfoChar (& buf , '\n' );
2916
3026
}
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
+ }
2917
3034
if (Log_error_verbosity >= PGERROR_VERBOSE )
2918
3035
{
2919
3036
/* assume no newlines in funcname or filename... */
0 commit comments