2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
5 * Routines to generate a stack backtrace automatically when a program
17 #include <sys/types.h>
20 # include <sys/wait.h>
21 # include <sys/syscall.h>
24 #ifndef WVCRASH_USE_SIGALTSTACK
25 # define WVCRASH_USE_SIGALTSTACK 1
28 // FIXME: this file mostly only works in Linux
31 # include <execinfo.h>
35 static const char *argv0
= program_invocation_short_name
;
37 static const char *argv0
= "UNKNOWN";
40 #if WVCRASH_USE_SIGALTSTACK
41 static const size_t altstack_size
= 1048576; // wvstreams can be a pig
42 static char altstack
[altstack_size
];
45 // Reserve enough buffer for a screenful of programme.
46 static const int buffer_size
= 2048 + wvcrash_ring_buffer_size
;
48 static char desc
[buffer_size
];
50 // write a string 'str' to fd
51 static void wr(int fd
, const char *str
)
53 write(fd
, str
, strlen(str
));
57 // convert 'num' to a string and write it to fd.
58 static void wrn(int fd
, int num
)
91 // convert 'addr' to hex and write it to fd.
92 static void wra(int fd
, const void *addr
)
94 const unsigned int ptrbitsshift
= (sizeof(ptrdiff_t) << 3) - 4;
95 char digits
[] = "0123456789ABCDEF";
98 for (int shift
=ptrbitsshift
; shift
>=0; shift
-=4)
99 write(fd
, &digits
[(((ptrdiff_t)addr
)>>shift
)&0xF], 1);
103 static void wvcrash_real(int sig
, int fd
, pid_t pid
)
105 static void *trace
[64];
106 static char *signame
= strsignal(sig
);
115 wr(fd
, " dying on signal ");
124 // Write out the PID and PPID.
125 static char pid_str
[32];
126 wr(fd
, "\nProcess ID: ");
127 snprintf(pid_str
, sizeof(pid_str
), "%d", getpid());
130 wr(fd
, "\nParent's process ID: ");
131 snprintf(pid_str
, sizeof(pid_str
), "%d", getppid());
136 #if WVCRASH_USE_SIGALTSTACK
137 // Determine if this has likely been a stack overflow
138 const void *last_real_stack_frame
;
141 last_real_stack_frame
= __builtin_frame_address(0);
142 if (last_real_stack_frame
== NULL
143 || last_real_stack_frame
< &altstack
[0]
144 || last_real_stack_frame
>= &altstack
[altstack_size
])
146 last_real_stack_frame
= __builtin_frame_address(1);
147 if (last_real_stack_frame
== NULL
148 || last_real_stack_frame
< &altstack
[0]
149 || last_real_stack_frame
>= &altstack
[altstack_size
])
151 last_real_stack_frame
= __builtin_frame_address(2);
152 if (last_real_stack_frame
== NULL
153 || last_real_stack_frame
< &altstack
[0]
154 || last_real_stack_frame
>= &altstack
[altstack_size
])
156 last_real_stack_frame
= __builtin_frame_address(3);
157 if (last_real_stack_frame
== NULL
158 || last_real_stack_frame
< &altstack
[0]
159 || last_real_stack_frame
>= &altstack
[altstack_size
])
161 last_real_stack_frame
= __builtin_frame_address(4);
162 if (last_real_stack_frame
== NULL
163 || last_real_stack_frame
< &altstack
[0]
164 || last_real_stack_frame
>= &altstack
[altstack_size
])
166 last_real_stack_frame
= __builtin_frame_address(5);
167 if (last_real_stack_frame
== NULL
168 || last_real_stack_frame
< &altstack
[0]
169 || last_real_stack_frame
>= &altstack
[altstack_size
])
171 last_real_stack_frame
= NULL
;
174 if (last_real_stack_frame
!= NULL
)
176 wr(fd
, "\nLast real stack frame: ");
177 wra(fd
, last_real_stack_frame
);
178 const void *top_of_stack
= WvTaskMan::current_top_of_stack();
179 wr(fd
, "\nTop of stack: ");
180 wra(fd
, top_of_stack
);
181 size_t stack_size
= size_t(top_of_stack
) - size_t(last_real_stack_frame
);
182 wr(fd
, "\nStack size: ");
183 wrn(fd
, int(stack_size
));
184 size_t stack_size_limit
= WvTaskMan::current_stacksize_limit();
185 if (stack_size_limit
> 0)
187 wr(fd
, "\nStack size rlimit: ");
188 wrn(fd
, int(stack_size_limit
));
189 if (stack_size
> stack_size_limit
)
190 wr(fd
, " DEFINITE STACK OVERFLOW");
191 else if (stack_size
+ 16384 > stack_size_limit
)
192 wr(fd
, " PROBABLE STACK OVERFLOW");
199 // Write out the contents of the ring buffer
203 while ((ring
= wvcrash_ring_buffer_get()) != NULL
)
208 wr(fd
, "\nRing buffer:\n");
214 // Write out the assertion message, as logged by __assert*_fail(), if any.
216 const char *assert_msg
= wvcrash_read_assert();
217 if (assert_msg
&& assert_msg
[0])
219 wr(fd
, "\nAssert:\n");
224 // Write out the note, if any.
226 const char *will_msg
= wvcrash_read_will();
227 if (will_msg
&& will_msg
[0])
229 wr(fd
, "\nLast Will and Testament:\n");
235 if (WvCrashInfo::in_stream_state
!= WvCrashInfo::UNUSED
236 && WvCrashInfo::in_stream
)
238 const char *state
= NULL
;
239 switch (WvCrashInfo::in_stream_state
)
241 case WvCrashInfo::UNUSED
:
242 // Can't possibly get here.
244 case WvCrashInfo::PRE_SELECT
:
245 state
= "\nStream in pre_select: ";
247 case WvCrashInfo::POST_SELECT
:
248 state
= "\nStream in post_select: ";
250 case WvCrashInfo::EXECUTE
:
251 state
= "\nStream in execute: ";
257 static char ptr_str
[32];
258 snprintf(ptr_str
, sizeof(ptr_str
), "%p", WvCrashInfo::in_stream
);
259 ptr_str
[sizeof(ptr_str
) - 1] = '\0';
262 wr(fd
, WvCrashInfo::in_stream_id
&& WvCrashInfo::in_stream_id
[0]
263 ? WvCrashInfo::in_stream_id
: "unknown stream");
270 wr(fd
, "\nBacktrace:\n");
271 backtrace_symbols_fd(trace
,
272 backtrace(trace
, sizeof(trace
)/sizeof(trace
[0])), fd
);
276 // Wait up to 10 seconds for child to write wvcrash file in case there
277 // is limited space availible on the device; wvcrash file is more
278 // useful than core dump
280 struct timespec ts
= { 0, 100*1000*1000 };
282 for (i
=0; i
< 100; ++i
)
284 if (waitpid(pid
, NULL
, WNOHANG
) == pid
)
286 nanosleep(&ts
, NULL
);
290 // we want to create a coredump, and the kernel seems to not want to do
291 // that if we send ourselves the same signal that we're already in.
292 // Whatever... just send a different one :)
298 signal(sig
, SIG_DFL
);
303 // Hint: we can't do anything really difficult here, because the program is
304 // probably really confused. So we should try to limit this to straight
305 // kernel syscalls (ie. don't fiddle with FILE* or streams or lists, just
306 // use straight file descriptors.)
308 // We fork a subprogram to do the fancy stuff like sending email.
310 void wvcrash(int sig
)
315 signal(sig
, SIG_DFL
);
316 wr(2, "\n\nwvcrash: crashing!\n");
318 // close some fds, just in case the reason we're crashing is fd
319 // exhaustion! Otherwise we won't be able to create our pipe to a
320 // subprocess. Probably only closing two fds is possible, but the
321 // subproc could get confused if all the fds are non-close-on-exec and
322 // it needs to open a few files.
324 // Don't close fd 0, 1, or 2, however, since those might be useful to
325 // the child wvcrash script. Also, let's skip 3 and 4, in case someone
326 // uses them for something. But don't close fd numbers that are *too*
327 // big; if someone ulimits the number of fds we can use, and *that's*
328 // why we're crashing, there's no guarantee that high fd numbers are in
329 // use even if we've run out.
330 for (int count
= 5; count
< 15; count
++)
334 wvcrash_real(sig
, 2, 0); // just use stderr instead
339 wvcrash_real(sig
, 2, 0); // just use stderr instead
340 else if (pid
== 0) // child
343 dup2(fds
[0], 0); // make stdin read from pipe
344 fcntl(0, F_SETFD
, 0);
346 execlp("wvcrash", "wvcrash", NULL
);
348 // if we get here, we couldn't exec wvcrash
349 wr(2, "wvcrash: can't exec wvcrash binary "
350 "- writing to wvcrash.txt!\n");
351 execlp("dd", "dd", "of=wvcrash.txt", NULL
);
353 wr(2, "wvcrash: can't exec dd to write to wvcrash.txt!\n");
356 else if (pid
> 0) // parent
359 wvcrash_real(sig
, fds
[1], pid
);
368 static void wvcrash_setup_alt_stack()
370 #if WVCRASH_USE_SIGALTSTACK
375 ss
.ss_size
= altstack_size
;
377 if (ss
.ss_sp
== NULL
|| sigaltstack(&ss
, NULL
))
378 fprintf(stderr
, "Failed to setup sigaltstack for wvcrash: %s\n",
380 #endif //WVCRASH_USE_SIGALTSTACK
383 void wvcrash_add_signal(int sig
)
385 #if WVCRASH_USE_SIGALTSTACK
386 struct sigaction act
;
388 act
.sa_handler
= wvcrash
;
389 sigfillset(&act
.sa_mask
);
390 act
.sa_flags
= SA_ONSTACK
| SA_RESTART
;
391 act
.sa_restorer
= NULL
;
393 if (sigaction(sig
, &act
, NULL
))
394 fprintf(stderr
, "Failed to setup wvcrash handler for signal %d: %s\n",
395 sig
, strerror(errno
));
396 #else //!WVCRASH_USE_SIGALTSTACK
397 signal(sig
, wvcrash
);
398 #endif //WVCRASH_USE_SIGALTSTACK
401 // Secret symbol for initialising the will and assert buffers
402 extern void __wvcrash_init_buffers(const char *program_name
);
404 void wvcrash_setup(const char *_argv0
, const char *_desc
)
407 argv0
= basename(_argv0
);
408 __wvcrash_init_buffers(argv0
);
411 strncpy(desc
, _desc
, buffer_size
);
412 desc
[buffer_size
- 1] = '\0';
417 wvcrash_setup_alt_stack();
419 wvcrash_add_signal(SIGSEGV
);
420 wvcrash_add_signal(SIGBUS
);
421 wvcrash_add_signal(SIGABRT
);
422 wvcrash_add_signal(SIGFPE
);
423 wvcrash_add_signal(SIGILL
);
426 #elif defined(_WIN32)
430 #include <imagehlp.h>
432 inline char* last_part(char* in
)
434 int len
= strlen(in
);
438 if (*tmp
== '/' || *tmp
== '\\')
447 * Call this with a thread context to get a nice callstack. You can get a
448 * thread context either from an exception or by using this code:
450 * memset(&ctx, 0, sizeof(CONTEXT));
451 * ctx.ContextFlags = CONTEXT_FULL;
452 * GetThreadContext(hThread, &ctx);
454 int backtrace(CONTEXT
&ctx
)
456 HANDLE hProcess
= (HANDLE
)GetCurrentProcess();
457 HANDLE hThread
= (HANDLE
)GetCurrentThread();
459 SymInitialize(hProcess
, NULL
, TRUE
);
462 memset(&sf
, 0, sizeof(STACKFRAME
));
464 sf
.AddrPC
.Offset
= ctx
.Eip
;
465 sf
.AddrPC
.Mode
= AddrModeFlat
;
466 sf
.AddrFrame
.Offset
= ctx
.Ebp
;
467 sf
.AddrFrame
.Mode
= AddrModeFlat
;
468 sf
.AddrStack
.Offset
= ctx
.Esp
;
469 sf
.AddrStack
.Mode
= AddrModeFlat
;
471 fprintf(stderr
, "Generating stack trace......\n");
472 fprintf(stderr
, "%3s %16s:%-10s %32s:%3s %s\n", "Num", "Module", "Addr", "Filename", "Line", "Function Name");
474 while (StackWalk(IMAGE_FILE_MACHINE_I386
,
480 SymFunctionTableAccess
,
484 if (sf
.AddrPC
.Offset
== 0)
488 IMAGEHLP_MODULE modinfo
;
489 memset(&modinfo
, 0, sizeof(IMAGEHLP_MODULE
));
490 modinfo
.SizeOfStruct
= sizeof(IMAGEHLP_MODULE
);
491 SymGetModuleInfo(hProcess
, sf
.AddrPC
.Offset
, &modinfo
);
496 memset(buffer
, 0, sizeof(buffer
));
497 PIMAGEHLP_SYMBOL sym
= (PIMAGEHLP_SYMBOL
)buffer
;
498 sym
->SizeOfStruct
= sizeof(IMAGEHLP_SYMBOL
);
499 sym
->MaxNameLength
= sizeof(buffer
) - sizeof(IMAGEHLP_SYMBOL
) + 1;
500 SymGetSymFromAddr(hProcess
, sf
.AddrPC
.Offset
, &disp
, sym
);
502 // line numbers anyone?
504 SymSetOptions(SYMOPT_LOAD_LINES
);
506 memset(&line
, 0, sizeof(IMAGEHLP_LINE
));
507 line
.SizeOfStruct
= sizeof(IMAGEHLP_LINE
);
508 SymGetLineFromAddr(hProcess
, sf
.AddrPC
.Offset
, &disp2
, &line
);
510 // output some info now then
511 fprintf(stderr
, "%3d. %16s:0x%08X %32s:%-3d %s\n",
513 modinfo
.LoadedImageName
[0]?modinfo
.LoadedImageName
:"unknown",
514 (DWORD
)sf
.AddrPC
.Offset
,
515 (line
.FileName
&& line
.FileName
[0])?last_part(line
.FileName
):"unknown",
516 (line
.FileName
&& line
.FileName
[0])?line
.LineNumber
:0,
517 sym
->Name
[0]?sym
->Name
:"unknown");
520 SymCleanup(hProcess
);
526 static void exception_desc(FILE *file
, unsigned exception
,
527 unsigned data1
, unsigned data2
)
538 "invalid memory read from address 0x%08X",
543 "invalid memory write to address 0x%08X",
548 "invalid memory access (unknown type %d) at address 0x%08X",
556 fprintf(file
, "integer division by zero");
560 fprintf(file
, "unknown exception (data1=0x%08X, data2=0x%08X)");
565 static LONG WINAPI
ExceptionFilter( struct _EXCEPTION_POINTERS
* pExceptionPointers
)
572 unsigned more_unknown
;
576 ExceptionInfo
*info
= *(ExceptionInfo
**)pExceptionPointers
;
578 // handle a special exception. Number 3 = forced breakpoint
579 // having __asm int 3; in code will cause windows to ask if
580 // you want to debug the application nicely.
581 if (info
->exception
==0x80000003)
583 fprintf(stderr
, "Preparing to debug!\n");
584 return EXCEPTION_CONTINUE_SEARCH
;
587 fprintf(stderr
, "--------------------------------------------------------\n");
588 fprintf(stderr
, "Exception 0x%08X:\n ", info
->exception
);
589 exception_desc(stderr
, info
->exception
, info
->data1
, info
->data2
);
590 fprintf(stderr
, "\n at instruction 0x%08X in thread 0x%08X\n", info
->ip
, GetCurrentThreadId());
591 backtrace(*pExceptionPointers
->ContextRecord
);
592 fprintf(stderr
, "--------------------------------------------------------\n");
595 return EXCEPTION_EXECUTE_HANDLER
;
598 static bool have_global_exception_handler
= false;
599 void setup_console_crash()
601 if (!have_global_exception_handler
)
603 SetUnhandledExceptionFilter(ExceptionFilter
);
604 have_global_exception_handler
= true;
608 void wvcrash(int sig
) {}
609 void wvcrash_setup(const char *_argv0
, const char *_desc
) {}
613 void wvcrash(int sig
) {}
614 void wvcrash_add_signal(int sig
) {}
615 void wvcrash_setup(const char *_argv0
, const char *_desc
) {}