Introduction
Debugging native code within custom Android Open Source Project (AOSP) ROMs running on emulators presents unique challenges compared to standard app development. When working deep within the Android framework, system services, or custom HALs, traditional Android Studio debugging might fall short. This guide provides a practical, expert-level walkthrough on setting up a robust debugging environment using GDB (or LLDB) to inspect native processes directly on an Android emulator flashed with your custom AOSP build.
Prerequisites for Native Debugging
Before diving into the debugging process, ensure you have the following components and expertise:
- AOSP Source Code: A complete synchronized AOSP source tree.
- Build Environment: A Linux machine configured for AOSP compilation (Ubuntu 18.04/20.04 recommended).
- Android SDK/Platform Tools: Essential for
adbandemulatorcommands. - GNU Debugger (GDB) or LLDB: Installed on your host machine. LLDB is often preferred for newer AOSP versions due to its tighter integration with Clang/LLVM.
- Familiarity with Shell: Command-line proficiency is crucial for `adb` and debugger interactions.
Building a Custom AOSP ROM for Emulator Debugging
The first step involves building an AOSP ROM specifically configured for emulation and debugging. The `userdebug` build variant is critical as it includes debugging symbols and enables root access via adb, which is necessary for pushing debugging tools and attaching debuggers.
1. Set Up Your Build Environment
Navigate to your AOSP source directory:
cd /path/to/your/aosp/source
2. Configure the Build Target
For emulator builds, you’ll typically select a target like `aosp_emu-userdebug` (for x86) or `aosp_x86_64-userdebug` (for 64-bit x86 emulators). This example will use `aosp_x86_64-userdebug`.
source build/envsetup.shlunch aosp_x86_64-userdebug
The `userdebug` variant provides root access with adb and includes debugging symbols, which are vital for effective native debugging.
3. Compile AOSP
Start the compilation process. Using `make -jX` where X is 1.5-2 times your CPU cores significantly speeds up the build.
make -j$(nproc --all)
This process can take several hours depending on your hardware. Once complete, your build artifacts will be located in the `out/target/product/generic_x86_64` directory (or similar, based on your `lunch` target).
Launching the Emulator with Your Custom ROM
With the custom ROM built, you can now launch it using the Android Emulator. Ensure your `emulator` binary is in your PATH or specify its full path.
emulator -kernel /path/to/your/aosp/source/prebuilts/qemu-kernel/x86_64/kernel-qemu-x86_64 -system /path/to/your/aosp/source/out/target/product/generic_x86_64/system.img -data /path/to/your/aosp/source/out/target/product/generic_x86_64/userdata.img -ramdisk /path/to/your/aosp/source/out/target/product/generic_x86_64/ramdisk.img -debug-init -show-kernel
Note: Adjust paths to `kernel-qemu`, `system.img`, `userdata.img`, and `ramdisk.img` according to your AOSP version and `lunch` target. The `-debug-init` and `-show-kernel` flags are useful for initial boot diagnostics.
Wait for the emulator to fully boot. Verify `adb` connectivity:
adb devices
You should see your emulator listed. If not, troubleshoot `adb` connectivity before proceeding.
Preparing for Native Debugging on the Emulator
To debug native code, you need a debugger server running on the emulator. AOSP builds typically include `lldb-server` in `userdebug` variants, but `gdbserver` can also be pushed if preferred.
1. Push the Debugger Server
If you used a `userdebug` build, `lldb-server` is usually present at `/system/bin/lldb-server`. If not, or if you prefer `gdbserver`, you can push it from your AOSP build output.
# For LLDB:adb shell "cp /system/bin/lldb-server /data/local/tmp/"# For GDB (if lldb-server is not suitable or missing):# Locate gdbserver in your AOSP output (e.g., prebuilts/tools/linux-x86_64/gdb/gdbserver)# adb push /path/to/your/aosp/prebuilts/tools/linux-x86_64/gdb/gdbserver /data/local/tmp/# adb shell "chmod 777 /data/local/tmp/gdbserver"
Using `lldb-server` is generally recommended for modern AOSP versions.
2. Identify the Target Process
Decide which native process you want to debug. This could be a system service (e.g., `mediaserver`), a daemon, or a native library loaded by an app. Get its PID:
adb shell ps -Ao PID,CMD | grep mediaserver
Let’s assume the `mediaserver` PID is `1234` for this example.
3. Set Up Port Forwarding
The debugger client on your host machine needs to connect to the debugger server on the emulator. Use `adb` to forward a local port to a remote port on the emulator.
adb forward tcp:5039 tcp:5039
Here, port `5039` on your host is forwarded to port `5039` on the emulator.
4. Launch the Debugger Server on the Emulator
Now, launch `lldb-server` on the emulator, instructing it to attach to the target process and listen on the forwarded port.
adb shell "/data/local/tmp/lldb-server platform --listen *:5039 --attach 1234"
Replace `1234` with the actual PID of your target process. The `platform` argument ensures it uses the correct AOSP-specific platform debugging features.
Debugging with LLDB on the Host
With the server running, you can now connect your host-side LLDB client.
1. Prepare LLDB Client
Open a new terminal on your host machine and launch `lldb`.
lldb
2. Connect to the Remote Server
Tell LLDB to connect to the remote `lldb-server` running on your emulator via the forwarded port.
(lldb) platform select remote-android(lldb) target create --remote-platform /path/to/your/aosp/out/target/product/generic_x86_64/symbols/system/bin/mediaserver(lldb) gdb-remote 5039
Replace `/path/to/your/aosp/out/target/product/generic_x86_64/symbols/system/bin/mediaserver` with the actual path to the unstripped binary (with debug symbols) corresponding to the process you are debugging. This path is crucial for LLDB to load symbols correctly.
3. Load Additional Symbols (if necessary)
If your target process uses dynamically loaded libraries, you might need to manually add their symbols. For example, if `mediaserver` uses `libmedia.so`:
(lldb) add-symbol-file /path/to/your/aosp/out/target/product/generic_x86_64/symbols/system/lib64/libmedia.so
You can find the loaded libraries and their addresses using `adb shell cat /proc/<PID>/maps` and then load symbols for those you are interested in.
4. Set Breakpoints and Inspect
Now you can set breakpoints, step through code, and inspect variables just like in any other debugging session.
- Set a breakpoint: `(lldb) b ANativeFunctionInMediaServer`
- Continue execution: `(lldb) c`
- Step over: `(lldb) n`
- Step into: `(lldb) s`
- Print a variable: `(lldb) p myLocalVariable`
- View backtrace: `(lldb) bt`
Trigger the action on the emulator that calls the function where you’ve set a breakpoint. The LLDB client on your host should hit the breakpoint.
Advanced Debugging Considerations
- Crash Dumps: For hard-to-catch crashes, analyze crash dumps. Enable `coredump` generation on userdebug builds or use `tombstones` located at `/data/tombstones`.
- `strace`/`ltrace`: For system call tracing or library call tracing, `adb shell strace -p ` or `adb shell ltrace -p ` can be invaluable for understanding runtime behavior. These tools require root and are usually present in `userdebug` builds.
- Source Code View: Configure your LLDB client to locate your source files so you can see the source code alongside the assembly during debugging. Use `settings set target.source-map /remote/path /local/path`.
Conclusion
Debugging native code in custom AOSP ROMs on an Android emulator is a powerful technique for low-level system development and bug hunting. By correctly building a `userdebug` AOSP image, setting up port forwarding, and leveraging `lldb-server` with the host-side `lldb` client, developers gain deep insights into the runtime behavior of native Android components. This expert-level approach ensures you can effectively troubleshoot and optimize even the most intricate parts of your custom Android system.
Android Mobile Specs & Compare Directory
Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!
Compare Devices Specs →