Core Dumps
A core dump is a file that captures the memory of a running process at a specific point in time, typically when the process crashes or encounters a serious error. It contains a snapshot of the process’s memory, including the call stack, heap, and other relevant information. A core dump is useful for debugging and analyzing the state of a program at the moment of failure. By loading it into a debugger, you can retrace exactly where and why your program crashed.
One of the first things I do when I configure my development environment is to make sure core dumps are enabled. The process is different on each OS, so I will cover the most common ones: Linux, Windows and MacOS.
Example of a program that generates a core dump:
1 | int main() { |
Compile it with clang
, for example:
1 | clang -o crash crash.c |
Linux
Howto
This is the easiest one, but it depends on the distributions. For me, on Debian Sid, it was as easy as adding
ulimit -c unlimited
to my .bashrc
file. This command allows core dumps to be generated without any size limit.
Apart from that, I could set the core dump pattern by modifying the /usr/lib/sysctl.d/10-coredump-debian.conf
file
to contain the following:
1 | kernel.core_pattern=/tmp/core.%p |
After running the executable, you can inspect the core dump with gdb
:
1 | gdb <executable> /path/to/core.<PID> |
Or using lldb
:
1 | lldb <executable> -c /path/to/core.<PID> |
If your distribution works differently, you can check where your system picks up the core dump pattern by running:
1 | sudo sysctl --system |
In my case it was obvious because one of the lines printed was * Applying /usr/lib/sysctl.d/10-coredump-debian.conf ….
Easy steps
- Set the core dump pattern in
/usr/lib/sysctl.d/10-coredump-debian.conf
tokernel.core_pattern=/tmp/core.%p
. - Set
ulimit -c unlimited
in your.bashrc
file. - Source your
.bashrc
file or restart your terminal.
MacOS
Set a pattern
Core dumps are by default written into the /cores
directory. If you want to keep it like that, you should make sure the
directory is writable by user running the program:
1 | sudo chmod 1777 /cores |
What this does:
- Everybody can create, rename, or modify files in it (because of th 777).
- Only the file owner can delete or rename their own files (because of the 1 - sticky bit).
If you want to change the location of the core dumps, you can do that by specifying a different path in the sysctl
command:
1 | sudo sysctl -w kern.corefile=/tmp/core.%P |
This will write the core dumps into the /tmp
directory, with the name core.<PID>
, where <PID>
is the process ID
of the process that crashed. Sadly, this is not consistent across reboots.
You can check the currently used pattern with sysctl kern.corefile
.
Set kernel state to allow core dumps
To enable core dumps, set the kern.coredump
kernel state to 1. This setting is persistent across reboots.
1 | sudo sysctl -w kern.coredump=1 |
You can check the current value with sysctl kern.coredump
.
Set the maximum size of core dumps
Similar to Linux, you need to let the system know that you’re ok with core dumps filling up your disk.
1 | ulimit -c unlimited |
I personally set this in my .bashrc
file, so that it stays somewhat consistent.
Sign the executable
This is the most annoying part, because on MacOS only signed executables can generate core dumps. Fortunately, it’s
not hard to do so. Suppose your executable is called crash
, you can sign it with the following commands:
1 | /usr/libexec/PlistBuddy -c "Add :com.apple.security.get-task-allow bool true" crash.entitlements |
The first command creates a file called crash.entitlements
that contains the necessary entitlements for the executable.
It’s not a big deal, just an XML file.
1 |
|
The second command signs the executable with the entitlements file. The -s -
option means that you’re using an ad-hoc
signature, which is sufficient for this purpose. The -f
option forces the signing, even if the executable is already
signed.
After that, running ./crash
will generate a core dump.
Loading the core dump
To load the core dump, you can use lldb
:
1 | lldb <executable> -c /path/to/core.<PID> |
Inspect the backtrace with bt
or see the list of threads with thread list
. You can also use thread select <N>
to switch to a different thread.
Bonus: disabling SIP
System Integrity Protection is a security feature that’s very useful for the regular user, but it can get annoying when developing or debugging. Disable it at your own risk. In my case, it slowed down the startup time of my built executables considerably, so I disabled it.
- Enter mac recovery mode: hold power until prompted for options.
- From utilities choose the terminal.
- Disable system integrity protection:
csrutil disable
.
Windows
Surprisingly convenient, it can mostly be done with from the Registry Editor
- Open
regedit.exe
. - Navigate to
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps
. If the keyLocalDumps
doesn’t exist, create it. - Set the
DumpFolder
value (typeREG_EXPAND_SZ
) to%LOCALAPPDATA%\Temp
. This is the path where dumps will be written. You may need to create this folder and grant permissions. - Set the
DumpCount
value (typeDWORD
) to10
. This is the maximum number of dumps that will be kept. - Set the
DumpType
value (typeDWORD
) to2
. This will create a mini dump. The options are:0
: Custom dump (legacy)1
: Mini dump2
: Full dump
Here’s a reg file that does all of this.
A mini-dump contains thread call stacks, registers, module list, and optionally selected memory regions, while a full dump captures the entire process address space—every heap, buffer, and allocated block. Which one you choose depends on your use case, but I rarely experience crashes, so a full dump is “better safe than sorry” for me. Take it with a grain of salt, though, as full dumps can be quite large.
From a developer console, I run cl crash.c
to compile the crash.exe
executable. Running it produces a 132K file
crash.exe.2092.dmp
in %LOCALAPPDATA%\Temp
. You can open it with WinDbgX.exe -z .\crash.exe.2092.dmp
and inspect
the backtrace with k
or kP
.