DEV Community

 Carlos
Carlos

Posted on

Core dump for Rust

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
Enter fullscreen mode Exit fullscreen mode

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!")
}
Enter fullscreen mode Exit fullscreen mode

Compile config

Optional:

[profile.dev]
opt-level = 0  # Controls the --opt-level the compiler builds with
debug = true   # Controls whether the compiler passes `-g`
Enter fullscreen mode Exit fullscreen mode

Enable core dump collection.

Optional: Change the path where coredump is generated

sudo sysctl -w kernel.core_pattern=/tmp/core.%u.%p.%t
Enter fullscreen mode Exit fullscreen mode

Or check where is generated:

cat /proc/sys/kernel/core_pattern
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

or

$ ulimit -c unlimited
Enter fullscreen mode Exit fullscreen mode

Generate core

cargo run
Enter fullscreen mode Exit fullscreen mode

Debug core

rust-gdb <path-to-binary> /tmp/<core_path>
thread apply all bt full
#or
where
Enter fullscreen mode Exit fullscreen mode

Oldest comments (4)

Collapse
 
koutheir profile image
Dr. Koutheir Attouchi • Edited

unwrap() in a panic handler... ironic.

Collapse
 
koutheir profile image
Dr. Koutheir Attouchi • Edited
# Controls whether the compiler passes `-g`

rustc does not invoke the C compiler...

Collapse
 
koutheir profile image
Dr. Koutheir Attouchi

Most of fatal errors in Rust comes from a panic, unwraps that we considered safe (probably?).

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.

Collapse
 
tmccombs profile image
Thayne McCombs

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)