Debug NetworkManager with GDB
I'd like to debug NetworkManager by stepping through it using GDB. I assume I'll have to compile NetworkManager from source to get debug symbols since file /usr/bin/NetworkManager
indicates that the binary is stripped, having no debug symbols.
I'm on Arch Linux and I can see that systemd starts NetworkManager. Preferably, I'd be able to debug a NetworkManager process started by systemd.
1 answer
The following users marked this post as Works for me:
User | Comment | Date |
---|---|---|
Matthias Braun | (no comment) | Dec 12, 2023 at 22:59 |
Note that if you want to debug the currently installed version of NetworkManager, you don't have to compile NetworkManager yourself. Instead, make GDB download symbol files via debuginfod
. You can skip to section "Debug NetworkManager with GDB" of this answer to see how.
Clone, configure, and compile
If you still want to compile NetworkManager yourself (e.g., to edit its code), here are the steps.
Clone NetworkManager's repository:
git clone https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git --depth 1
Before configuring (with autogen.sh
) and compiling, make sure you have the required packages installed. I was missing gtk-doc
, gobject-introspection
, and ppp
which caused errors:
./autogen.sh: line 25: gtkdocize: command not found
configure: error: introspection enabled but can't be used
configure: error: "couldn't find pppd.h. pppd development headers are required."
You'll probably need base-devel
installed as well:
sudo pacman -S base-devel gtk-doc gobject-introspection ppp
Although NetworkManager will run with root privileges, it needs to be installed in a path entirely owned by root. If you put your self-compiled NetworkManager in a directory like /home/me/nm_install_dir
, even if you chown root:root /home/me/nm_install_dir/
, it will start but won't assign IP addresses and will print errors like these:
Cannot create parents for '/home/me/nm_install_dir/var/lib/NetworkManager': Read-only file system
secret-key: failure to persist secret key in "/home/me/nm_install_dir/var/lib/NetworkManager/secret_key" (failed to create file /home/me/nm_install_dir/var/lib/NetworkManager/secret_key.KG6J61: Read-only file system) (use non-persistent key)
config: device-state: write #3 (/home/me/nm_install_dir/var/run/NetworkManager/devices/3) failed: Failed to create file “/home/me/nm_install_dir/var/run/NetworkManager/devices/3.C2ZI61”: Read-only file system
Thus, I used a directory in the root directory for putting NetworkManager's binary, configuration, and runtime files:
./autogen.sh --prefix=/nm_install_dir
Then, compile and install NetworkManager with:
make -j 8 && sudo make install
If you encounter issues while configuring or compiling, see NetworkManager's CONTRIBUTING.md which also links to required packages.
Increase logging
It can be useful to have NetworkManager produce more detailed log messages; create a file /nm_install_dir/etc/NetworkManager/NetworkManager.conf
containing this:
[logging]
domains=ALL:TRACE
Run self-compiled NetworkManager
Stop your currently running instance of NetworkManager with systemctl stop NetworkManager
and (optionally) remove current IP addresses from your interfaces with ip addr flush up
.
On Arch Linux, systemd starts NetworkManager as defined in the unit file /usr/lib/systemd/system/NetworkManager.service
. In that file, I changed the line
ExecStart=/usr/bin/NetworkManager --no-daemon
to
ExecStart=/nm_install_dir/sbin/NetworkManager --no-daemon
and ran systemctl daemon-reload && systemctl start NetworkManager
.
There are other ways to do that, as described here.
Make sure you're indeed running the self-compiled version of NetworkManager with systemctl status NetworkManager
. Check the logs with journalctl --since=-5m -u NetworkManager
to see if there are any warnings or errors.
Debug NetworkManager with GDB
If you want to debug NetworkManager without it being started by systemd, run this as root:
# Stop the systemd-started NetworkManager
systemctl stop NetworkManager
# Change NetworkManager's executable path if you didn't compile it yourself
DEBUGINFOD_URLS="https://debuginfod.archlinux.org/" gdb /nm_install_dir/sbin/NetworkManager
The environment variable DEBUGINFOD_URLS
enables GDB to download debugging symbols on demand using debuginfod
. We set the variable here explicitly since per default, user root doesn't have that variable set.
Whether or not you have compiled NetworkManager yourself, you probably still want to have debuginfod
activated since it also fetches the debugging symbols of NetworkManager's dependencies (like libglib and libgobject) which you don't get when compiling NetworkManager.
Start NetworkManager with systemd and debug with GDB
Alternatively, we can attach GDB to the NetworkManager process once systemd has started it. Prerequisite for this is GDB having the capability to attach to a running process, discussed here:
sudo setcap CAP_SYS_PTRACE=+eip /usr/bin/gdb
Make sure NetworkManager is not running with systemctl stop NetworkManager
.
Save the following script, adapted from here, as gdbwait
:
#!/bin/sh
process_name="$1"
breakpoint_location="$2"
echo "Waiting for $process_name to start"
pid=$(pgrep -o "$process_name")
while [ "$pid" = "" ]; do
pid=$(pgrep -o "$process_name")
done
# Per default, user root doesn't have the environment variable DEBUGINFOD_URLS → Set it to download symbols as needed
DEBUGINFOD_URLS="https://debuginfod.archlinux.org/" gdb -ex "break $breakpoint_location" -ex continue -p "$pid"
To avoid GDB's prompt "Enable debuginfod for this session?", you can echo "set debuginfod enabled on" >> /root/.gdbinit
.
Then, run ./gdbwait NetworkManager yourDebugLocation
as root and systemctl start NetworkManager
afterwards. GDB needs root privileges, otherwise GDB won't be able to debug the process and will print errors:
warning: "target:/nm_install_dir/sbin/NetworkManager": could not open as an executable file: Operation not permitted.
warning: `target:/nm_install_dir/sbin/NetworkManager': can't open to read symbols: Operation not permitted.
warning: opening /proc/self/mem file failed: Permission denied (13)
Mind that gdbwait
won't be able to break at early functions like main
since detecting the process and attaching to it takes longer than for main to have started.
But this for example should work (using TUI here):
./gdbwait NetworkManager nm_utils_ipv6_addr_set_stable_privacy_with_host_id
0 comment threads