Introduction
- Do you have (server) processes that sometimes crash for mysterious reasons?
- Can you not figure out why?
- Do they not print any error messages to their log files upon crashing?
- Are debuggers complicated, scary things that you'd rather avoid?
crash-watch
to the rescue! This little program will monitor a specified process and wait until it crashes. It will then print useful information such as its exit status, what signal caused it to abort, and its backtrace.
Installation with RubyGems
Run:
gem install crash-watch
You must also have GDB installed. macOS already has it by default. If you're on Linux, try one of these:
apt-get install gdb
yum install gdb
Sample usage
$ crash-watch <PID>
Monitoring PID <PID>...
(...some time later, <PID> exits...)
Process exited.
Exit code: 0
Backtrace:
Thread 1 (process 95205):
#0 0x00007fff87ea1db0 in _exit ()
No symbol table info available.
#1 0x000000010002a260 in ruby_stop ()
No symbol table info available.
#2 0x0000000100031a54 in ruby_run ()
No symbol table info available.
#3 0x00000001000009e4 in main ()
No symbol table info available.
While monitoring the process, you may interrupt crash-watch
by pressing Ctrl-C. crash-watch
will then detach from the process, which will then continue normally. You may re-attach crash-watch
later.
Consult crash-watch --help
for more usage options.
Dumping live backtrace
Instead of waiting until a process crashes, you can also dump a live backtrace of a process. crash-watch
will immediately exit after dumping the backtrace, letting the process continue as normally.
$ crash-watch --dump <PID>
Current thread (1) backtrace:
#0 0x00007fff81fd9464 in read ()
No symbol table info available.
#1 0x0000000100060d3e in ?? ()
No symbol table info available.
Goodie: GDB controller
I've written a small library for controlling gdb, which crash-watch
uses internally. With CrashWatch::GdbController you can send arbitrary commands to gdb and also get its response.
Instantiate with:
require 'crash_watch/gdb_controller'
gdb = CrashWatch::GdbController.new
This will spawn a new GDB process. Use #execute
to execute arbitrary GDB commands. Whatever the command prints to stdout and stderr will be available in the result string.
gdb.execute("bt") # => backtrace string
gdb.execute("p 1 + 2") # => "$1 = 3\n"
Call #close
when you no longer need it.
gdb.close