Android Emulator Development, Anbox, & Waydroid

Debugging Custom AVD System Images: Advanced Troubleshooting for Boot Loops and Runtime Errors

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction: The Intricacies of Custom AVD System Image Debugging

Developing custom Android Virtual Device (AVD) system images offers unparalleled flexibility for testing, security research, and specialized deployments. However, the complexity of the Android Open Source Project (AOSP) build system and the myriad dependencies within a custom image often lead to obscure boot loops, runtime errors, and stability issues. This guide delves into advanced troubleshooting techniques, empowering developers to diagnose and resolve these challenging problems effectively.

Understanding Common Failure Modes

Before diving into debugging tools, it’s essential to recognize the typical manifestations of issues in custom AVD images:

  • Boot Loops: The emulator starts, shows the Android logo, but never reaches the home screen, restarting repeatedly. This often points to critical system service failures, incorrect filesystem configurations, or kernel panics.
  • System Crashes/ANRs: The system becomes unresponsive, or core Android processes (e.g., system_server, zygote) crash, leading to a non-functional or extremely unstable environment.
  • Application Runtime Errors: Custom pre-installed apps or even core system apps crash unexpectedly, often due to missing libraries, incorrect permissions, or incompatible ABIs.
  • Performance Degradation: The AVD boots but runs extremely slowly, indicating resource contention or inefficient system processes.

Prerequisites for Effective Debugging

A robust debugging workflow requires specific tools and environment setup:

  • AOSP Build Environment: Access to the full AOSP source tree and a working build setup is crucial for modifying and rebuilding components.
  • ADB (Android Debug Bridge): The primary tool for interacting with the running or partially booting AVD.
  • Fastboot: For flashing new kernel, ramdisk, or system images when adb is unavailable.
  • Android Emulator with qemu-system-x86_64 (or aarch64): Understanding emulator command-line options is vital.
  • Symbol Files: Essential for native debugging to map crash addresses back to source code.

Debugging Boot Loops: A Step-by-Step Approach

Boot loops are perhaps the most frustrating issue. The key is to gather as much information as possible from the earliest boot stages.

1. Initial Log Collection via ADB

Even if the system doesn’t fully boot, adb logcat might capture critical early messages:

# Start the emulator with a writable system and potentially verbose loggingemulator -avd my_custom_avd -writable-system -qemu -append "androidboot.console=ttyS0 console=ttyS0" -serial stdio# In a separate terminal, try to connect adbadb wait-for-deviceadb logcat -b all -d > bootloop_log.txt

Examine bootloop_log.txt for recurring error patterns, “FATAL EXCEPTION”, “debuggerd”, or “Process terminated” messages from critical services like system_server, installd, or zygote.

2. Analyzing Kernel and Init Process Logs

If adb isn’t responsive, or logs are sparse, the issue might be lower-level. Kernel panics or init failures are common culprits.

  • Kernel Debugging with dmesg (if accessible): If you can get adb shell briefly, dmesg can show kernel messages. Often, however, the kernel crashes before adb initializes.
  • Serial Console Output: Configure the emulator to output serial console data, which includes kernel boot messages and init logs. This is often the most reliable way to catch early boot failures.
# Start emulator with serial output to a file or stdoutemulator -avd my_custom_avd -qemu -serial file:/tmp/emu_serial.log# Or to stdout for direct inspectionemulator -avd my_custom_avd -qemu -serial stdio

Look for messages like “Kernel panic – not syncing”, “init: critical service ‘service_name’ died”, or errors related to mounting filesystems (fs_mgr).

3. Modifying Init.rc for Diagnostics

The init.rc script and its associated .rc files define how Android initializes. Temporarily modifying these can help isolate issues.

  • Disabling Services: Comment out or remove service entries one by one in your custom init.rc (or init.${hardware}.rc equivalent) to find which service is causing the boot loop. Rebuild the ramdisk.img after changes.
  • Increasing Log Verbosity: Add loglevel options or modify service commands to print more output.
  • Launching a Shell on Boot: For severe issues preventing any boot, you can try to force a shell prompt:
# In your init.rc, locate the 'on early-init' or 'on init' section.# Add or modify a service to run a service debug-shell /system/bin/sh    class core    console    oneshot    user root    group root    disabled # Keep disabled, enable via 'setprop service.debug-shell 1' or during kernel boot args# Or even force the system to halt and give a shell immediately after kernel boots (advanced, use with caution)# Append to kernel command line: init=/system/bin/sh

Then, rebuild your ramdisk.img and flash it. If you get a shell, you can manually inspect directories, mount points, and try to start services one by one.

Debugging Runtime Errors and Application Crashes

Once the AVD boots, runtime issues become the focus.

1. Advanced Logcat Filtering

Beyond basic logcat, targeted filtering is crucial:

# Filter by process ID (PID) or tagadb logcat --pid=<PID_OF_CRASHING_APP>adb logcat -s PackageManager:V ActivityManager:V MyAppTag:D *:S# View kernel messages onlyadb logcat -b kernel

2. Utilizing dumpsys for System State

dumpsys provides diagnostic output for all system services. It’s invaluable for understanding the state of critical Android components.

# Dump information for a specific service, e.g., activity manageradb shell dumpsys activity# Get package manager information (e.g., about your installed apps)adb shell dumpsys package <your.package.name># View memory usageadb shell dumpsys meminfo

Look for discrepancies in expected service states, incorrect permissions, or missing component registrations.

3. Comprehensive Data Collection with bugreport

For persistent or complex issues, bugreport captures a snapshot of the entire system state, including logs, dumpsys output, and various system properties.

adb bugreport > bugreport.zip

The resulting .zip file contains extensive data for offline analysis, particularly useful when collaborating or reporting issues upstream.

4. Native Code Debugging with GDB/LLDB

For crashes in C/C++ native code (JNI, custom daemons, HAL implementations), gdbserver or lldb-server are indispensable.

  1. Push the debugger server to the device:
  2. adb push $ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/lib64/clang/9.0.8/lib/linux/arm64/lldb-server /data/local/tmp/lldb-serveradb shell chmod 755 /data/local/tmp/lldb-server
  3. Start the target application or process under lldb-server:
  4. # For an application:adb shell run-as <your.package.name> /data/local/tmp/lldb-server platform --listen "*:5039" -- <command_to_start_your_app># For a running process (attach):adb shell /data/local/tmp/lldb-server platform --listen "*:5039" --attach <PID>
  5. Forward the port:
  6. adb forward tcp:5039 tcp:5039
  7. Connect from your host debugger:
  8. # Example using lldblldb(lldb) platform select remote-android(lldb) platform connect connect://localhost:5039(lldb) target create <path_to_your_native_binary_on_host>(lldb) b <function_name_or_file:line>(lldb) c

    Ensure you have the correct unstripped binaries and symbol files on your host machine to get meaningful stack traces.

    Advanced Techniques and Best Practices

    Kernel Debugging with KGDB (Remote GDB for Kernel)

    For deep-seated kernel issues, kgdb allows debugging the kernel itself. This requires specific kernel build configurations (e.g., CONFIG_KGDB, CONFIG_KGDB_SERIAL_CONSOLE) and a serial connection (virtual or physical) to a host running GDB.

    Example kernel command line parameters for KGDB (passed via emulator’s -qemu -append):

    kgdboc=ttyS0,115200 kgdbwait

    Then, connect GDB on the host to the serial port (e.g., /dev/ttyUSB0 or a named pipe created by QEMU).

    Analyzing bootchart for Performance

    bootchart helps visualize the boot process, identifying bottlenecks, excessive I/O, or CPU contention. Enable it in your init.rc (or via kernel command line) and collect the generated charts for analysis.

    Filesystem and SELinux Policy Verification

    • Filesystem Mounts: Verify all expected partitions (e.g., /system, /vendor, /data) are correctly mounted and accessible with appropriate permissions (adb shell mount).
    • SELinux Permissive Mode: Temporarily set SELinux to permissive mode during debugging (adb shell setenforce 0) to rule out policy violations as the root cause of access denied errors. Remember to re-enable enforcing mode for production.
    • AOSP Build Artifacts: Always double-check that you are flashing the correct system.img, ramdisk.img, and kernel (or boot.img) for your target AVD configuration. Mismatched images are a frequent source of issues.

    Conclusion

    Debugging custom AVD system images demands a methodical approach and a deep understanding of the Android boot process and runtime environment. By leveraging a combination of adb diagnostics, serial console output, init.rc modifications, and native debugging tools like lldb-server, developers can effectively pinpoint and resolve even the most elusive boot loops and runtime errors. Consistent log analysis, incremental changes, and thorough testing are paramount to maintaining a stable and functional custom AVD image.

    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 →
Google AdSense Inline Placement - Content Footer banner