naev 0.11.5
debug.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
4
12#include <assert.h>
13#include <signal.h>
14
15#if DEBUGGING
16#include <backtrace.h>
17
18#define __USE_GNU /* Grrr... */
19#include <dlfcn.h>
20#undef __USE_GNU
21#endif /* DEBUGGING */
22
23#include "naev.h"
26#include "debug.h"
27
28#include "log.h"
29
30#if DEBUGGING
31static struct backtrace_state *debug_bs = NULL;
32DebugFlags debug_flags;
33
41const char* debug_sigCodeToStr( int sig, int sig_code )
42{
43 if (sig == SIGFPE)
44 switch (sig_code) {
45#ifdef SI_USER
46 case SI_USER: return _("SIGFPE (raised by program)");
47#endif /* SI_USER */
48#ifdef FPE_INTDIV
49 case FPE_INTDIV: return _("SIGFPE (integer divide by zero)");
50#endif /* FPE_INTDIV */
51#ifdef FPE_INTOVF
52 case FPE_INTOVF: return _("SIGFPE (integer overflow)");
53#endif /* FPE_INTOVF */
54#ifdef FPE_FLTDIV
55 case FPE_FLTDIV: return _("SIGFPE (floating-point divide by zero)");
56#endif /* FPE_FLTDIV */
57#ifdef FPE_FLTOVF
58 case FPE_FLTOVF: return _("SIGFPE (floating-point overflow)");
59#endif /* FPE_FLTOVF */
60#ifdef FPE_FLTUND
61 case FPE_FLTUND: return _("SIGFPE (floating-point underflow)");
62#endif /* FPE_FLTUND */
63#ifdef FPE_FLTRES
64 case FPE_FLTRES: return _("SIGFPE (floating-point inexact result)");
65#endif /* FPE_FLTRES */
66#ifdef FPE_FLTINV
67 case FPE_FLTINV: return _("SIGFPE (floating-point invalid operation)");
68#endif /* FPE_FLTINV */
69#ifdef FPE_FLTSUB
70 case FPE_FLTSUB: return _("SIGFPE (subscript out of range)");
71#endif /* FPE_FLTSUB */
72 default: return _("SIGFPE");
73 }
74 else if (sig == SIGSEGV)
75 switch (sig_code) {
76#ifdef SI_USER
77 case SI_USER: return _("SIGSEGV (raised by program)");
78#endif /* SI_USER */
79#ifdef SEGV_MAPERR
80 case SEGV_MAPERR: return _("SIGSEGV (address not mapped to object)");
81#endif /* SEGV_MAPERR */
82#ifdef SEGV_ACCERR
83 case SEGV_ACCERR: return _("SIGSEGV (invalid permissions for mapped object)");
84#endif /* SEGV_ACCERR */
85 default: return _("SIGSEGV");
86 }
87 else if (sig == SIGABRT)
88 switch (sig_code) {
89#ifdef SI_USER
90 case SI_USER: return _("SIGABRT (raised by program)");
91#endif /* SI_USER */
92 default: return _("SIGABRT");
93 }
94
95 /* No suitable code found. */
96#if HAVE_STRSIGNAL
97 return strsignal(sig);
98#else /* HAVE_STRSIGNAL */
99 {
100 static char buf[128];
101 snprintf( buf, sizeof(buf), _("signal %d"), sig );
102 return buf;
103 }
104#endif /* HAVE_STRSIGNAL */
105}
106#endif /* DEBUGGING */
107
108#if DEBUGGING
109typedef struct { void* data; uintptr_t pc; const char* file; int line; const char* func; } FrameInfo;
110
114static void debug_backtrace_syminfo_callback( void* data, uintptr_t pc, const char* symname, uintptr_t symval, uintptr_t symsize )
115{
116 (void) symsize;
117 FrameInfo *fi = data;
118 Dl_info addr = {0};
119 dladdr( (void*) pc, &addr );
120 uintptr_t offset = pc - (symval ? symval : (uintptr_t)addr.dli_fbase);
121 pc -= (uintptr_t) addr.dli_fbase;
122 symname = symname ? symname : "??";
123 fi->func = fi->func ? fi->func : symname;
124 fi->file = fi->file ? fi->file : "??";
125 addr.dli_fname = addr.dli_fname ? addr.dli_fname : "??";
126 int width = snprintf( NULL, 0, "%s at %s:%u", fi->func, fi->file, fi->line );
127 int pad = MAX( 0, 80 - width );
128 LOGERR( "[%#14"PRIxPTR"] %s at %s:%u %*s| %s(%s+%#"PRIxPTR")", pc, fi->func, fi->file, fi->line, pad, "", addr.dli_fname, symval ? symname : "", offset );
129}
130
134static void debug_backtrace_error_callback( void* data, const char* msg, int errnum )
135{
136 FrameInfo *fi = data;
137 (void) msg;
138 (void) errnum;
139 debug_backtrace_syminfo_callback( data, fi->pc, "??", 0, 0 );
140}
141
145static int debug_backtrace_full_callback( void* data, uintptr_t pc, const char* file, int line, const char* func )
146{
147 FrameInfo fi = { .data = data, .pc = pc, .file = file, .line = line, .func = func };
148 if (pc != 0 && ~pc != 0)
149 backtrace_syminfo( debug_bs, pc, debug_backtrace_syminfo_callback, debug_backtrace_error_callback, &fi );
150
151 return 0;
152}
153
157void debug_logBacktrace (void) {
158 backtrace_full( debug_bs, 1, debug_backtrace_full_callback, NULL, NULL );
159}
160
161#if HAVE_SIGACTION
162static void debug_sigHandler( int sig, siginfo_t *info, void *unused )
163#else /* HAVE_SIGACTION */
164static void debug_sigHandler( int sig )
165#endif /* HAVE_SIGACTION */
166{
167 (void) sig;
168#if HAVE_SIGACTION
169 (void) unused;
170#endif /* HAVE_SIGACTION */
171
172#if HAVE_SIGACTION
173 LOGERR( _("Naev received %s!"), debug_sigCodeToStr( info->si_signo, info->si_code ) );
174#else /* HAVE_SIGACTION */
175 LOGERR( _("Naev received %s!"), debug_sigCodeToStr( sig, 0 ) );
176#endif /* HAVE_SIGACTION */
177
178 debug_logBacktrace();
179 LOGERR( _("Report this to project maintainer with the backtrace.") );
180
181 /* Always exit. */
182 exit(1);
183}
184
185#if HAVE_SIGACTION
186static void debug_sigHandlerWarn( int sig, siginfo_t *info, void *unused )
187#else /* HAVE_SIGACTION */
188static void debug_sigHandlerWarn( int sig )
189#endif /* HAVE_SIGACTION */
190{
191 (void) sig;
192#if HAVE_SIGACTION
193 (void) unused;
194#endif /* HAVE_SIGACTION */
195
196 WARN( _("Naev received %s!"),
197#if HAVE_SIGACTION
198 debug_sigCodeToStr( info->si_signo, info->si_code )
199#else /* HAVE_SIGACTION */
200 debug_sigCodeToStr( sig, 0 )
201#endif /* HAVE_SIGACTION */
202 );
203
204 debug_logBacktrace();
205}
206#endif /* DEBUGGING */
207
211void debug_sigInit (void)
212{
213#if DEBUGGING
214 Dl_info addr = {0};
215#if __WIN32__
216 dladdr( debug_sigInit, &addr ); /* Get the filename using dlfcn-win32; libbacktrace fucks this up (as of 2022-08-18). */
217#endif /* __WIN32__ */
218
219 debug_bs = backtrace_create_state( addr.dli_fname, /*threaded:*/ 1, NULL, NULL );
220
221 /* Set up handler. */
222#if HAVE_SIGACTION
223 const char *str = _("Unable to set up %s signal handler.");
224 struct sigaction so, sa = { .sa_handler = NULL, .sa_flags = SA_SIGINFO };
225 sa.sa_sigaction = debug_sigHandler;
226 sigemptyset(&sa.sa_mask);
227
228 sigaction(SIGSEGV, &sa, &so);
229 if (so.sa_handler == SIG_IGN)
230 DEBUG( str, "SIGSEGV" );
231 sigaction(SIGABRT, &sa, &so);
232 if (so.sa_handler == SIG_IGN)
233 DEBUG( str, "SIGABRT" );
234
235 sa.sa_sigaction = debug_sigHandlerWarn;
236 sigaction(SIGFPE, &sa, &so);
237 if (so.sa_handler == SIG_IGN)
238 DEBUG( str, "SIGFPE" );
239#else /* HAVE_SIGACTION */
240 signal( SIGSEGV, debug_sigHandler );
241 signal( SIGABRT, debug_sigHandler );
242 signal( SIGFPE, debug_sigHandlerWarn );
243#endif /* HAVE_SIGACTION */
244#endif /* DEBUGGING */
245}
246
250void debug_sigClose (void)
251{
252#if DEBUGGING
253 signal( SIGSEGV, SIG_DFL );
254 signal( SIGABRT, SIG_DFL );
255 signal( SIGFPE, SIG_DFL );
256#endif /* DEBUGGING */
257}
258
263{
264}
void debug_sigInit(void)
Sets up the back-tracing signal handler.
Definition debug.c:211
void debug_enableLeakSanitizer(void)
Does nothing. Calling this tells our debug scripts to stop tracing.
Definition debug.c:262
void debug_sigClose(void)
Closes the back-tracing signal handler.
Definition debug.c:250
Header file with generic functions and naev-specifics.
#define MAX(x, y)
Definition naev.h:39