naev 0.10.4
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
131
135static void debug_backtrace_error_callback( void* data, const char* msg, int errnum )
136{
137 FrameInfo *fi = data;
138 (void) msg;
139 (void) errnum;
140 debug_backtrace_syminfo_callback( data, fi->pc, "??", 0, 0 );
141}
142
146static int debug_backtrace_full_callback( void* data, uintptr_t pc, const char* file, int line, const char* func )
147{
148 FrameInfo fi = { .data = data, .pc = pc, .file = file, .line = line, .func = func };
149 if (pc != 0 && ~pc != 0)
150 backtrace_syminfo( debug_bs, pc, debug_backtrace_syminfo_callback, debug_backtrace_error_callback, &fi );
151
152 return 0;
153}
154
158void debug_logBacktrace (void) {
159 backtrace_full( debug_bs, 1, debug_backtrace_full_callback, NULL, NULL );
160}
161
162#if HAVE_SIGACTION
163static void debug_sigHandler( int sig, siginfo_t *info, void *unused )
164#else /* HAVE_SIGACTION */
165static void debug_sigHandler( int sig )
166#endif /* HAVE_SIGACTION */
167{
168 (void) sig;
169#if HAVE_SIGACTION
170 (void) unused;
171#endif /* HAVE_SIGACTION */
172
173 LOGERR( _("Naev received %s!"),
174#if HAVE_SIGACTION
175 debug_sigCodeToStr( info->si_signo, info->si_code )
176#else /* HAVE_SIGACTION */
177 debug_sigCodeToStr( sig, 0 )
178#endif /* HAVE_SIGACTION */
179 );
180
181 debug_logBacktrace();
182 LOGERR( _("Report this to project maintainer with the backtrace.") );
183
184 /* Always exit. */
185 exit(1);
186}
187#endif /* DEBUGGING */
188
192void debug_sigInit (void)
193{
194#if DEBUGGING
195 Dl_info addr = {0};
196#if WIN32
197 dladdr( debug_sigInit, &addr ); /* Get the filename using dlfcn-win32; libbacktrace fucks this up (as of 2022-08-18). */
198#endif /* WIN32 */
199
200 debug_bs = backtrace_create_state( addr.dli_fname, /*threaded:*/ 1, NULL, NULL );
201
202 /* Set up handler. */
203#if HAVE_SIGACTION
204 const char *str = _("Unable to set up %s signal handler.");
205 struct sigaction so, sa = { .sa_handler = NULL, .sa_flags = SA_SIGINFO };
206 sa.sa_sigaction = debug_sigHandler;
207 sigemptyset(&sa.sa_mask);
208
209 sigaction(SIGSEGV, &sa, &so);
210 if (so.sa_handler == SIG_IGN)
211 DEBUG( str, "SIGSEGV" );
212 sigaction(SIGFPE, &sa, &so);
213 if (so.sa_handler == SIG_IGN)
214 DEBUG( str, "SIGFPE" );
215 sigaction(SIGABRT, &sa, &so);
216 if (so.sa_handler == SIG_IGN)
217 DEBUG( str, "SIGABRT" );
218#else /* HAVE_SIGACTION */
219 signal( SIGSEGV, debug_sigHandler );
220 signal( SIGFPE, debug_sigHandler );
221 signal( SIGABRT, debug_sigHandler );
222#endif /* HAVE_SIGACTION */
223#endif /* DEBUGGING */
224}
225
226
230void debug_sigClose (void)
231{
232#if DEBUGGING
233 signal( SIGSEGV, SIG_DFL );
234 signal( SIGFPE, SIG_DFL );
235 signal( SIGABRT, SIG_DFL );
236#endif /* DEBUGGING */
237}
238
239
244{
245}
void debug_sigInit(void)
Sets up the back-tracing signal handler.
Definition: debug.c:192
void debug_enableLeakSanitizer(void)
Does nothing. Calling this tells our debug scripts to stop tracing.
Definition: debug.c:243
void debug_sigClose(void)
Closes the back-tracing signal handler.
Definition: debug.c:230
Header file with generic functions and naev-specifics.
#define MAX(x, y)
Definition: naev.h:39
static char buf[NEWS_MAX_LENGTH]
Definition: news.c:45