Having Fun with Signal Handlers
giovannimascellani.eu
giovannimascellani.eu
https://github.com/ruby/ruby/blob/5445e0435260b449decf2ac16f...
They have a bug report handler that shows a bunch of helpful information:
- the Ruby-level backtrace
- the stack of VM control frames (an internal data structure)
- the C-level backtrace (which Ruby implementation functions were running)
- which files had been required and loaded into memory
- what segments of memory are mapped and what aren't
Another thing: it's designed to work even when the program ran out of memory!
The sigaltstack(2) system call lets you preallocate a buffer that the kernel knows how to set up before it runs your SIGSEGV handler, so you can have just enough stack memory to compute and print all this debug info before the program ultimately crashes. Here's that code, again in the Ruby VM:
https://github.com/ruby/ruby/blob/5445e0435260b449decf2ac16f...
Consider the following (from memory, untested):
word_t peek(word_t addr, int success) { word_t ret; errno = 0; access(addr, F_OK); if (success) success = (errno != EFAULT); if (errno != EFAULT) ret = *addr; return ret; }
I first learnt about this trick in http://www.gcu-squad.org/2005/08/non-ce-n-est-pas-sale%21/
In English: "syscalls cannot segfault, so all that accept an address (as parameter) have to check if it is valid and return EFAULT in case of error."
(Since C code actually calls libc not system calls directly, this rationale fails on calls that do something on pointers before calling the actual kernel system call.)
Also, given the time cost of making a system call, one has to has an approach finer than "pay a system call for each byte to write" if there are many.
Anyway, TL;DR : it's not a dirty hack, it has some general validity!