Intro
From Wikipedia:
In computing, a core dump,[a] memory dump, crash dump, system dump, or ABEND dump[1] consists of the recorded state of the working memory of a computer program at a specific time, generally when the program has crashed or otherwise terminated abnormally.
In GNU/Linux core dump generation is handle by the kernel. And it can pipe the generation to a user-space program like systemd-coredump
or apport
. You can see if is handle by an external program looking into:
cat /proc/sys/kernel/core_pattern
|/usr/share/apport/apport %p %s %c
From core(5)
The default action of certain signals is to cause a process to
terminate and produce a core dump file, a file containing an
image of the process's memory at the time of termination. This
image can be used in a debugger (e.g., gdb(1)) to inspect the
state of the program at the time that it terminated. A list of
the signals which cause a process to dump core can be found in
signal(7).
Signals that generate a core dump.
- SIGABRT Abort signal from abort(3)
- SIGBUS Bus error (bad memory access)
- SIGFPE Floating-point exception
- SIGILL Illegal Instruction
- SIGSEGV Invalid memory reference See the full documentation for a complete list.
Generate a core dump on Rust panics.
While rust panics itself help with the great information they provide. Sometimes it is really useful debug using a core dump specially if your binary has debug symbols plus the gdb wrappers provided by rust-gdb
.
Instrument panic handler to generate coredump.
Most of fatal errors in Rust comes from a panic, unwraps
that we considered safe (probably?).
Based in a really helpful gist I found:
https://gist.github.com/epilys/a6caba03cb02cfd2880fd80755cd08b8
First modify how the panic is handled by printing the panic trace as usual, but then send a signal that generates a core to our own process. In the example gist, the signal is SIGQUIT
.
pub fn panic_handler_generate_coredump() {
let default_panic = std::panic::take_hook();
std::panic::set_hook(Box::new(move |panic_info| {
default_panic(panic_info);
let pid = std::process::id();
use libc::kill;
use libc::SIGQUIT;
use std::convert::TryInto;
unsafe { kill(pid.try_into().unwrap(), SIGQUIT) };
}));
}
fn main(){
panic_handler_generate_coredump();
panic!("don't Panic!")
}
Compile config
Optional:
[profile.dev]
opt-level = 0 # Controls the --opt-level the compiler builds with
debug = true # Controls whether the compiler passes `-g`
Enable core dump collection.
Optional: Change the path where coredump is generated
sudo sysctl -w kernel.core_pattern=/tmp/core.%u.%p.%t
Or check where is generated:
cat /proc/sys/kernel/core_pattern
Modify core file size:
From : https://community.perforce.com/s/article/2979
For most distributions the core file size limitation is set to 0 to produce no core files at all. To find out the limit on the system in question, issue the command:
$ ulimit -c
If it is set to 0 then no core files are produced. To allow core files to be produced, set a core file size that is not 0:
$ ulimit -c 100000000000
or
$ ulimit -c unlimited
Generate core
cargo run
Debug core
rust-gdb <path-to-binary> /tmp/<core_path>
thread apply all bt full
#or
where
Top comments (4)
If that is the case, then why talk about all signals that can kill a process, e.g.,
SIGABRT
,SIGBUS
,SIGFPE
,SIGILL
,SIGSEGV
, etc?Most non-trivial Rust applications execute unsafe code (written in Rust or otherwise) that can cause the signals above to be sent to the process, and that can happen at some tricky hardly-predictable moments, where one does not get a nice call stack trace like the one produced in a panic, and that is when a core dump is the most useful.
unwrap()
in a panic handler... ironic.rustc
does not invoke the C compiler...Why use kill instead of the abort function (which sends SIGABRT to the calling process), or raise (which sends a signal to the calling process)