Using Debuggers

This page details using LLDB for OS development. For app development, see Debug your app instead, which explains how to use the Android Studio GUI (based on LLDB).

GDB is no longer supported or provided. If you're switching from GDB to LLDB, you should probably start by reading the LLDB Tutorial. If you're an expert GDB user, the GDB to LLDB command map is very helpful while transitioning.

Prerequisites

To use a debugger:

  • Set up the build environment with the usual envsetup.sh command.
  • Run the same lunch command you used when building. Note that the lunch item should exactly match the device you are debugging. If the lunch item doesn't match with the attached device, you'll get an error of the form: You used the wrong lunch: TARGET_PRODUCT (aosp_arm64) does not match attached device (xyzabc)
  • Connect your device to machine.

For more help with setting up your environment, see Set up environment.

Debugging a binary

To debug a binary that you built on your machine, first you'll have to copy the binary to the device and then launch the debugger. For example:

adb push test.exe /data/local/tmp/test.exe
lldbclient.py --port 5038 -r /data/local/tmp/test.exe

Debugging running apps or processes

To connect to a running app or native daemon, use lldbclient.py with a PID. For example, to debug the process with PID 1234, run this on the host:

lldbclient.py -p 1234

The script sets up port forwarding, starts the appropriate remote debugging stub on the device, starts the debugger on the host, configures it to find symbols, and connects it to the remote debugging stub.

Debugging native process startup

To debug a process as it starts, use lldbclient.py with the -r option. For example, to debug ls /bin, run this on the host:

lldbclient.py -r /system/bin/ls /bin

Then, enter continue at the debugger's prompt.

Debugging app startup

Sometimes you want to debug an app as it starts, such as when there's a crash and you want to step through code to see what happened before the crash. Attaching works in some cases, but in other cases is impossible because the app crashes before you can attach. The logwrapper approach (used for strace) doesn't always work because the app might not have permissions to open a port, and lldbserver inherits that restriction.

To debug app startup, use the developer options in Settings to instruct the app to wait for a Java debugger to attach:

  1. Go to Settings > Developer options > Select debug app and choose your app from the list, then click Wait for debugger.
  2. Start the app, either from the launcher or by using the command line to run:
    adb shell am start -a android.intent.action.MAIN -n APP_NAME/.APP_ACTIVITY
    
  3. Wait for the app to load and a dialog to appear telling you the app is waiting for a debugger.
  4. Attach lldbserver/lldbclient normally, set breakpoints, then continue the process.

To let the app run, attach a Java Debug Wire Protocol (JDWP) debugger such as Java Debugger (jdb):

adb forward tcp:12345 jdwp:XXX  # (Where XXX is the PID
of the debugged process.)
jdb -attach localhost:12345

Debugging apps or processes that crash

If you want debuggerd to suspend crashed processes so that you can attach a debugger, set the appropriate property:

  • After Android 11
    adb shell setprop debug.debuggerd.wait_for_debugger true
    
  • Android 11 and lower
    adb shell setprop debug.debuggerd.wait_for_gdb true
    
  • Android 6.0 Marshmallow and lower
    adb shell setprop debug.db.uid 999999
    

At the end of the usual crash output, debuggerd provides copy and paste instructions in logcat showing how to connect the debugger to the crashed process.

Debugging with VS Code

LLDB supports debugging platform code on Visual Studio Code. You can use the VS Code debugger frontend instead of the LLDB CLI interface to control and debug native code running on devices.

Before using VS Code for debugging, install the CodeLLDB extension.

To debug code using VS Code:

  1. Ensure that all build artifacts (such as symbols) required to run lldbclient.py or lldbclient.py are present.
  2. In VS Code, press Ctrl+Shift+P to run a command, search for Debug: Add Configuration..., then select LLDB. This opens a launch.json file and adds a new JSON object to a list.
  3. Replace the newly added debugger configuration with two comment lines - // #lldbclient-generated-begin and // #lldbclient-generated-end, so that your configuration list looks like this:
    "configurations": [
        // #lldbclient-generated-begin
        // #lldbclient-generated-end
    ]

    lldbclient.py uses these comments to detect where to write the config. If there are other items in the list, add the comment lines to the end after the other configurations.

  4. Run the following command in the terminal where you've run envsetup.sh and lunch:
    lldbclient.py --setup-forwarding vscode-lldb \
          --vscode-launch-file LAUNCH_JSON_PATH \
          ANY_OTHER_FLAGS -p pid | -n proc-name | -r ...

    lldbclient.py writes the generated config into launch.json and continues running. This is expected; don't kill the lldbclient.py program. If you omit the --vscode-launch-file the script will print the JSON snippet that you will need to copy and paste into launch.json manually.

    The -r flag must be the last flag if it is present due to how flags are parsed by the tool.

  5. Open the Run and Debug side bar - the new configuration should appear in the debugger list. Press Start Debugging (F5). The debugger should connect after 10 to 30 seconds.

    If the new configuration did not appear in the Run and Debug view, reload the window to refresh the debugger list - press Ctrl+Shift+P and type reload window.

  6. When you're done debugging, go to the terminal running lldbclient.py and press Enter to end the lldbclient.py program. Subsequent runs of the script would generate the config between the #lldbclient-generated comments and replace the old contents, you do not need to remove them manually.

To add custom properties to the generated launch config, you can use the --vscode-launch-props flag. For example:

lldbclient.py --setup-forwarding vscode-lldb \
    --vscode-launch-props \
    '{"initCommands" : ["script print(\"Hello\")"], "preLaunchTask" : "Build"}' \
    ...
The example properties would make VS Code run a task named Build before debugging and appends a new debug initialization step to the steps generated by the script. You can find an overview of available properties in the VS Code documentation and in the User Manual of the CodeLLDB extension.