Monday, March 11, 2013

Android native code debugging with Eclipse

OK, this has been covered in numerous blogs and forum posts, but I will post this tutorial for multiple reasons. The first reason is that this will be a reminder for me, and the second one is the fact that all tutorials cover the steps for debugging android application projects that have native part; but what about debugging android library project that has native part? What if you have a situation like this: you have an android library project that has native part and you have pure java-based android application project which uses the library project and you have bug in your native part. How to debug this? Well, you can always add native support to your application project and then make the debugging, but why doing that, if there is an easier way to solve this?

For this tutorial I will assume that you have the latest version of Eclipse, Android SDK and Android NDK installed. In my case, eclipse is at version Juno, Android SDK is at revision 21, and I'm using NDK r8d. Of course, this tutorial should work with earlier versions of NDK and SDK, but I haven't tested that.

OK, let's start:
First, you need to adapt the ndk-gdb script which is located in Android NDK dir. Create a copy of that script; let's call it ndk-gdb-eclipse. Open the copied file in your favorite editor and find the following snippet:


    PACKAGE_NAME=`run_awk_manifest_script extract-package-name.awk`
    log "Found package name: $PACKAGE_NAME"
    if [ $? != 0 -o "$PACKAGE_NAME" = "<none>" ] ; then
        echo "ERROR: Could not extract package name from $PROJECT/$MANIFEST."
        echo "       Please check that the file is well-formed!"
        exit 1
    fi

Now, edit this so it will look like this:

if [[ -z "$PACKAGE_NAME" ]]; then
    PACKAGE_NAME=`run_awk_manifest_script extract-package-name.awk`
    log "Found package name: $PACKAGE_NAME"
    if [ $? != 0 -o "$PACKAGE_NAME" = "<none>" ] ; then
        echo "ERROR: Could not extract package name from $PROJECT/$MANIFEST."
        echo "       Please check that the file is well-formed!"
        exit 1
    fi
else
    log "Using predefined package: $PACKAGE_NAME"
fi

OK, what does that do? This code actually checks if the variable PACKAGE_NAME is set, and if it is not set, it sets it to the name of the package for Android project in current directory. This works fine if you are trying to debug an application, but if you try to debug Android library project, the found package name will be wrong (the name of the package in the library does not need to be the same as the name of the package of application that uses the library).

OK, don't close the text editor yet, there is a still one little change that needs to be done. Scroll to the end of the script. The last line should be as this:
$GDBCLIENT -x `native_path $GDBSETUP`
Comment-out this line by prefixing it with #. The changed line should look like this:

#$GDBCLIENT -x `native_path $GDBSETUP` 
 OK, now save the file - the hard hacker-style work is done. Actually not yet, we will still write a little helper bash script which is actually not needed, but vastly eases the later usage. So, open your editor and put the following code inside it:

 #!/bin/bash
 # activate remote port to pipe forwarding
 PACKAGE_NAME=$1

 if [[ -z "$PACKAGE_NAME" ]]; then
  echo "No package name defined :-("
 else
  export PACKAGE_NAME
  ndk-gdb-eclipse --force --verbose
 fi

Save the file as "android-debug" and make it executable with chmod +x android-debug.

Next, you should build your native code with ndk-build NDK_DEBUG=1, which will enable native debugging. The easiest way to ensure that your native code is always built with debugging support is to make eclipse do that for you. Right click the project that contains native code, choose properties, click the C/C++ Build tab, uncheck "Use default build command" and add NDK_DEBUG=1 to build command (see the picture).

Example of C/C++ build settings for android library project with native code
OK, now connect your phone or start android emulator and start the application project which will use the library project you are about to debug. You can start the application by either selecting "Run" or "Debug". If you select "Run", you will be able to debug only the native part, and if you select "Debug", you will be able to debug both the native and the java part of your project.

Just to be sure that everything will work, add android:debuggable="true" to application tag in your AndroidManifest.xml. In my case, this is required only for debugging of java part of library project, but not for native debug.

OK, the application is now started on your phone, but before being able to debug the native part, you still have to perform some steps. First, open your terminal and navigate to root folder of library project that contains the native code. Run the following command:
android-debug test.application.packagename
So, "android-debug" is the script that you have created earlier, and test.application.packagename is the name of the package of application that uses the library project (not the package of library project). For example, if your library project is in package com.test.library and your application is in package net.cool.application, you should run "android-debug net.cool.application".
The script will then run the gdbserver on phone that will attach to the process of application you started. Now, all you have to do is connect your eclipse to that gdbserver and debugging can start. Warning, on some phones, deploying of gdbserver may not succeed. Unfortunately, on such phones, debugging the native code is not possible.

To connect your eclipse to the gdbserver on the phone do the following: open the Debug Configurations dialog in Eclipse (little arrow next to the button for starting the debug, and choosing "Debug Configurations..." option.

Create new C/C++ Remote Application launch configuration as seen on the following screenshot:


If you don't have C/C++ Remote Application launch configuration option, then you should install C/C++ hardware debug support from CDT update site.
In C/C++ application field enter the full path to app_process file that will be found in libraries ./obj/local/armeabi folder. If you can't find that file, that means that android-debug script has failed.

Next, click the debugger tab and enter the full path to arm-linux-androideabi-gdb file that is found in android-ndk-dir/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86/bin/ folder (I'm not sure where this is found in windows or mac, but it should be similar) to the GDB debugger field and into the GDB command file put the full path to the gdb.setup file that was created by ndk-build script (it should be found next to .so file in libs/armeabi folder). See the next screenshot for example:


Finally, open the Connection tab and set the port number to 5039.


And that is all. Click the Debug button and start putting breakpoints to your native code. When phone hits them, the execution will be suspended and you will be able to examine variables, do steps and everything else you are used to do when debugging C/C++ code with eclipse CDT.

I hope this wasn't too complicated...