Native Debugging: GDB and LLDB
To diagnose complex logic errors or memory corruption in AOSP native daemons or NDK libraries, developers need interactive debuggers. Android supports both GDB (GNU Debugger) and LLDB (the LLVM debugger), though LLDB is now the standard.
Attaching a Debugger to an Android Process
Debugging on Android uses a remote debugging architecture. The debugger UI runs on your host machine (Linux/Mac/Windows), while a small debug server (gdbserver or lldb-server) runs on the Android device.
- The debug server runs on the device and uses
ptraceto attach to the target process. - The server opens a port on the device.
- ADB forwards a local port on the host machine to the device port.
- The host debugger (GDB/LLDB) connects to the local port and sends debugging commands (set breakpoint, read memory, step over) to the server via a remote serial protocol.
The gdbclient.py Wrapper
In the AOSP source tree, manually setting up the port forwarding, pushing the gdbserver, and configuring the local debugger with the correct sysroot and symbol paths is extremely tedious.
AOSP provides the gdbclient.py script to automate this entire process. Despite the name, it defaults to using LLDB on modern Android versions.
# Attach to a running process by name
source build/envsetup.sh
lunch aosp_husky-userdebug
gdbclient.py -p surfaceflinger
# Attach to a specific PID
gdbclient.py -p 1234
gdbclient.py will automatically:
- Find the correct
lldb-serverfor the device ABI. - Push it to the device and execute it.
- Setup ADB port forwarding.
- Launch the host LLDB instance, pointing it to the unstripped symbol files in your
out/directory.
LLDB for NDK Debugging
For application developers using the NDK, Android Studio integrates LLDB transparently. When you click "Debug" on an app with native code, Android Studio automatically injects lldb-server into the APK, attaches it, and provides a graphical interface over the LLDB command line.
If you need to use LLDB from the command line for an NDK app, the NDK ships with LLDB binaries in the <NDK>/toolchains/llvm/prebuilt/ directory.
The Importance of Symbol Resolution
Debuggers require symbols to translate raw memory addresses and program counters into human-readable function names, file names, and line numbers.
When Android code is compiled for release, the binaries (e.g., /system/lib64/libc.so) are stripped - debug symbols are removed to save space.
If you attach a debugger to a stripped binary without providing the unstripped version on your host machine, you will only see raw assembly instructions and hex addresses.
- AOSP: Unstripped binaries are kept in
out/target/product/<device>/symbols/.gdbclient.pyautomatically points LLDB to this directory. - NDK: Unstripped
.sofiles are kept in your project'sapp/build/intermediates/cmake/debug/obj/directory. You must configure your host LLDB to load symbols from this path using thetarget modules addandtarget modules loadcommands.