naev 0.12.6
debug.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
4
10
12#include <inttypes.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"
25
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:
47 return _( "SIGFPE (raised by program)" );
48#endif /* SI_USER */
49#ifdef FPE_INTDIV
50 case FPE_INTDIV:
51 return _( "SIGFPE (integer divide by zero)" );
52#endif /* FPE_INTDIV */
53#ifdef FPE_INTOVF
54 case FPE_INTOVF:
55 return _( "SIGFPE (integer overflow)" );
56#endif /* FPE_INTOVF */
57#ifdef FPE_FLTDIV
58 case FPE_FLTDIV:
59 return _( "SIGFPE (floating-point divide by zero)" );
60#endif /* FPE_FLTDIV */
61#ifdef FPE_FLTOVF
62 case FPE_FLTOVF:
63 return _( "SIGFPE (floating-point overflow)" );
64#endif /* FPE_FLTOVF */
65#ifdef FPE_FLTUND
66 case FPE_FLTUND:
67 return _( "SIGFPE (floating-point underflow)" );
68#endif /* FPE_FLTUND */
69#ifdef FPE_FLTRES
70 case FPE_FLTRES:
71 return _( "SIGFPE (floating-point inexact result)" );
72#endif /* FPE_FLTRES */
73#ifdef FPE_FLTINV
74 case FPE_FLTINV:
75 return _( "SIGFPE (floating-point invalid operation)" );
76#endif /* FPE_FLTINV */
77#ifdef FPE_FLTSUB
78 case FPE_FLTSUB:
79 return _( "SIGFPE (subscript out of range)" );
80#endif /* FPE_FLTSUB */
81 default:
82 return _( "SIGFPE" );
83 }
84 else if (sig == SIGSEGV)
85 switch (sig_code) {
86#ifdef SI_USER
87 case SI_USER:
88 return _( "SIGSEGV (raised by program)" );
89#endif /* SI_USER */
90#ifdef SEGV_MAPERR
91 case SEGV_MAPERR:
92 return _( "SIGSEGV (address not mapped to object)" );
93#endif /* SEGV_MAPERR */
94#ifdef SEGV_ACCERR
95 case SEGV_ACCERR:
96 return _( "SIGSEGV (invalid permissions for mapped object)" );
97#endif /* SEGV_ACCERR */
98 default:
99 return _( "SIGSEGV" );
100 }
101 else if (sig == SIGABRT)
102 switch (sig_code) {
103#ifdef SI_USER
104 case SI_USER:
105 return _( "SIGABRT (raised by program)" );
106#endif /* SI_USER */
107 default:
108 return _( "SIGABRT" );
109 }
110
111 /* No suitable code found. */
112#if HAVE_STRSIGNAL
113 return strsignal( sig );
114#else /* HAVE_STRSIGNAL */
115 {
116 static char buf[128];
117 snprintf( buf, sizeof( buf ), _( "signal %d" ), sig );
118 return buf;
119 }
120#endif /* HAVE_STRSIGNAL */
121}
122#endif /* DEBUGGING */
123
124#if DEBUGGING
125typedef struct {
126 void *data;
127 uintptr_t pc;
128 const char *file;
129 int line;
130 const char *func;
131} FrameInfo;
132
137static void debug_backtrace_syminfo_callback( void *data, uintptr_t pc,
138 const char *symname,
139 uintptr_t symval,
140 uintptr_t symsize )
141{
142 (void)symsize;
143 FrameInfo *fi = data;
144 Dl_info addr = { 0 };
145 dladdr( (void *)pc, &addr );
146 uintptr_t offset = pc - ( symval ? symval : (uintptr_t)addr.dli_fbase );
147 pc -= (uintptr_t)addr.dli_fbase;
148 symname = symname ? symname : "??";
149 fi->func = fi->func ? fi->func : symname;
150 fi->file = fi->file ? fi->file : "??";
151 addr.dli_fname = addr.dli_fname ? addr.dli_fname : "??";
152 int width = snprintf( NULL, 0, "%s at %s:%u", fi->func, fi->file, fi->line );
153 int pad = MAX( 0, 80 - width );
154 LOGERR( "[%#14" PRIxPTR "] %s at %s:%u %*s| %s(%s+%#" PRIxPTR ")", pc,
155 fi->func, fi->file, fi->line, pad, "", addr.dli_fname,
156 symval ? symname : "", offset );
157}
158
163static void debug_backtrace_error_callback( void *data, const char *msg,
164 int errnum )
165{
166 FrameInfo *fi = data;
167 (void)msg;
168 (void)errnum;
169 debug_backtrace_syminfo_callback( data, fi->pc, "??", 0, 0 );
170}
171
175static int debug_backtrace_full_callback( void *data, uintptr_t pc,
176 const char *file, int line,
177 const char *func )
178{
179 FrameInfo fi = {
180 .data = data, .pc = pc, .file = file, .line = line, .func = func };
181 if (pc != 0 && ~pc != 0)
182 backtrace_syminfo( debug_bs, pc, debug_backtrace_syminfo_callback,
183 debug_backtrace_error_callback, &fi );
184
185 return 0;
186}
187
191static void debug_backtrace_full_error_callback( void *data, const char *msg,
192 int errnum )
193{
194 (void)data;
195 (void)msg;
196 (void)errnum;
197}
198
202void debug_logBacktrace( void )
203{
204 backtrace_full( debug_bs, 1, debug_backtrace_full_callback,
205 debug_backtrace_full_error_callback, NULL );
206}
207
208#if HAVE_SIGACTION
209static void debug_sigHandler( int sig, siginfo_t *info, void *unused )
210#else /* HAVE_SIGACTION */
211static void debug_sigHandler( int sig )
212#endif /* HAVE_SIGACTION */
213{
214 (void)sig;
215#if HAVE_SIGACTION
216 (void)unused;
217#endif /* HAVE_SIGACTION */
218
219#if HAVE_SIGACTION
220 LOGERR( _( "Naev received %s!" ),
221 debug_sigCodeToStr( info->si_signo, info->si_code ) );
222#else /* HAVE_SIGACTION */
223 LOGERR( _( "Naev received %s!" ), debug_sigCodeToStr( sig, 0 ) );
224#endif /* HAVE_SIGACTION */
225
226 debug_logBacktrace();
227 LOGERR( _( "Report this to project maintainer with the backtrace." ) );
228
229 /* Always exit. */
230 exit( 1 );
231}
232
233#if HAVE_SIGACTION
234static void debug_sigHandlerWarn( int sig, siginfo_t *info, void *unused )
235#else /* HAVE_SIGACTION */
236static void debug_sigHandlerWarn( int sig )
237#endif /* HAVE_SIGACTION */
238{
239 (void)sig;
240#if HAVE_SIGACTION
241 (void)unused;
242#endif /* HAVE_SIGACTION */
243
244 WARN( _( "Naev received %s!" ),
245#if HAVE_SIGACTION
246 debug_sigCodeToStr( info->si_signo, info->si_code )
247#else /* HAVE_SIGACTION */
248 debug_sigCodeToStr( sig, 0 )
249#endif /* HAVE_SIGACTION */
250 );
251
252 debug_logBacktrace();
253}
254#endif /* DEBUGGING */
255
259void debug_sigInit( void )
260{
261#if DEBUGGING
262 Dl_info addr = { 0 };
263#if __WIN32__
264 dladdr( debug_sigInit,
265 &addr ); /* Get the filename using dlfcn-win32; libbacktrace fucks
266 this up (as of 2022-08-18). */
267#endif /* __WIN32__ */
268
269 debug_bs =
270 backtrace_create_state( addr.dli_fname, /*threaded:*/ 1, NULL, NULL );
271
272 /* Set up handler. */
273#if HAVE_SIGACTION
274 const char *str = _( "Unable to set up %s signal handler." );
275 struct sigaction so, sa = { .sa_handler = NULL, .sa_flags = SA_SIGINFO };
276 sa.sa_sigaction = debug_sigHandler;
277 sigemptyset( &sa.sa_mask );
278
279 sigaction( SIGSEGV, &sa, &so );
280 if (so.sa_handler == SIG_IGN)
281 DEBUG( str, "SIGSEGV" );
282 sigaction( SIGABRT, &sa, &so );
283 if (so.sa_handler == SIG_IGN)
284 DEBUG( str, "SIGABRT" );
285
286 sa.sa_sigaction = debug_sigHandlerWarn;
287 sigaction( SIGFPE, &sa, &so );
288 if (so.sa_handler == SIG_IGN)
289 DEBUG( str, "SIGFPE" );
290#else /* HAVE_SIGACTION */
291 signal( SIGSEGV, debug_sigHandler );
292 signal( SIGABRT, debug_sigHandler );
293 signal( SIGFPE, debug_sigHandlerWarn );
294#endif /* HAVE_SIGACTION */
295#endif /* DEBUGGING */
296}
297
301void debug_sigClose( void )
302{
303#if DEBUGGING
304 signal( SIGSEGV, SIG_DFL );
305 signal( SIGABRT, SIG_DFL );
306 signal( SIGFPE, SIG_DFL );
307#endif /* DEBUGGING */
308}
309
314{
315}
void debug_sigInit(void)
Sets up the back-tracing signal handler.
Definition debug.c:259
void debug_enableLeakSanitizer(void)
Does nothing. Calling this tells our debug scripts to stop tracing.
Definition debug.c:313
void debug_sigClose(void)
Closes the back-tracing signal handler.
Definition debug.c:301
Header file with generic functions and naev-specifics.
#define MAX(x, y)
Definition naev.h:37