Debugging C code on macOS
I started to write C 25 years ago now, with many different tools over the year. As many open source developers, I spent most of my life working with the GNU tools out there.
As I've been using an Apple computer over the last years, I had to adapt to this environment and learn the tricks of the trade. Here are some of my notes so a search engine can index them — and I'll be able to find them later.
I was used to `gdb` for most of years doing C. I never managed to install gdb correctly on macOS as it needs certificates, authorization, you name it, to work properly.
macOS provides a native debugger named lldb, which really looks like gdb to me — it runs in a terminal with a prompt.
I had to learn the few commands I mostly use, which are:
lldb -- myprogram -optionsto run the program with options
rto run the program
bt Nto get a backtrace of the latest N frames
f Nto select frame N
p Vto print some variable value or memory address
Those commands cover 99% of my use case with a debugger when writing C, so once I lost my old
gdb habits, I was good to go.
Debugging Memory Overflows
One of my favorite tools when writing C has always been Electric Fence (and DUMA more recently). It's a library that overrides the standard memory manipulation function (e.g.,
malloc) and instantly makes the program crash when an out of memory error is produced, rather than corrupting the heap.
Heap corruption issues are hard to debug without such tools as they can happen at any time and stay unnoticed for a while, crashing your program in a totally different location later.
There's no need to compile your program with those libraries. By using the dynamic loader, you can preload them and overload the standard C library functions.
gdb configuration has been sprinkle with my friends efence and duma, and I would activate them from
gdb easily with this configuration in
define efence set environment EF_PROTECT_BELOW 0 set environment EF_ALLOW_MALLOC_0 1 set environment LD_PRELOAD /usr/lib/libefence.so.0.0 echo Enabled Electric Fence\n end document efence Enable memory allocation debugging through Electric Fence (efence(3)). See also nofence and underfence. end define underfence set environment EF_PROTECT_BELOW 1 set environment EF_ALLOW_MALLOC_0 1 set environment LD_PRELOAD /usr/lib/libefence.so.0.0 echo Enabled Electric Fence for underflow detection\n end document underfence Enable memory allocation debugging for underflows through Electric Fence (efence(3)). See also nofence and efence. end define nofence unset environment LD_PRELOAD echo Disabled Electric Fence\n end document nofence Disable memory allocation debugging through Electric Fence (efence(3)). end define duma set environment DUMA_PROTECT_BELOW 0 set environment DYMA_ALLOW_MALLOC_0 1 set environment LD_PRELOAD /usr/lib/libduma.so echo Enabled DUMA\n end document duma Enable memory allocation debugging through DUMA (duma(3)). See also noduma and underduma. end
I've been looking for equivalent features in macOS, and after many hours of research, I found out that this feature is shipped natively with
libgmalloc. It works in the same way, and its features are documented by Apple.
~/.lldbinit file now contains the following:
command alias gm _regexp-env DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib
This command alias allows enabling
gmalloc by just typing
gm at the lldb prompt and then
run the program again to see if it crashes with
It's not a mystery that I spend a lot of time writing Python code — that's the main reason I've been doing C lately.
When playing with CPython, it can be useful to, e.g., dump the content of
PyObject structs on the heap or get the Python backtrace.
I've been using cpython-lldb for this with great success. It adds a few bells and whistles when debugging CPython or extensions inside
lldb. For example, the alias
py-bt is handy to get the Python traceback of your calls rather than a bunch of cryptic C frames.
Now, you should be ready to debug your nasty issues and memory problems on macOS efficiently!