Introduction
This article talks about how to debug C++ code using the NDK debug tool, and give some advice about C++ programming on android. If the application is written in Java, programmers can use Eclipse to debug the code, set break points, and trace step by step. When written in C++, how do we debug? By now, for android version above 2.2, NDK provides an NDK debug tool, which runs on Linux, and can be used to debug the source code.
The debug environment may vary with platforms. The steps given here are based on the environment of my host, which is Windows XP, Android-ndk-r6b, and Cgywin is used to build C++ code for android. How to install android-ndk-r6b and cgywin are out of the scope of this article. You can refer to other materials.
The example code to be debugged is a list view holder which is given in the previous article. Suppose you have already prepared the environment, and the C++ code can be built for android successfully.
Changes to the project
- Change AndroidManifest.xml to enable gdbserver.
Based on document of NDK, in order to debug the native code, AndroidManifest.xml should add a flag android:debuggable="true"
, as follows.
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:debuggable="true" >
<activity
android:label="@string/app_name"
android:name=".DebugActivity" >
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
- Recompile the project with debug flag “NDK_DEBUG=1”.
- Run the project from eclipse.
Debug with ndk-debug
Switch to Cgywin console
- Run ndk-debug as follows:
There may be many warnings. But need not worry, they do not affect the debug process.
- Type list command, you can see the source file.
- use b command to set the break points.
- use c command to continue to run.
- The mobile may respond slowly, please wait until the mobile can operate normally.
If the mobile has no response for a long time, you can type CTRL+C to exit ndk-debug and repeat the above steps.
- Click button, then the source will be broken at the break point set before.
In the Cygwin console, when watching the variable value, you may see “value optimized out”.
In this case, you should add –Oo flags to the Android.mk file, rebuild the project, and restart.
LOCAL_CFLAGS += -Wno-write-strings -O0 -DENV_ANDROID
LOCAL_CPPFLAGS += -Wno-write-strings -O0 -fexceptions -DENV_ANDROID
LOCAL_LDFLAGS += -Wno-write-strings -O0 -DENV_ANDROID
Debug on the simulator may be less efficient with a slow response. The better choice is to buy a cheaper mobile or palm, and debug using the real device. Debugging C++ code on android is not an easy work, whether you are familiar with GDB or not. I think migrating mature code from another platform to android is a good choice, or debugging on another platform using more convenient tools other than on android directly.
Debug on Win32
CLE also supports Windows and Linux, therefore we can debug program logic on Windows. Let's discuss this in deeper. To debug on Windows, there is lots of work to do, not technology, but the work to setup stubs for android classes. The work has not started yet, so this article only gives the steps to illustrate how we can debug Android c++ code on Windows. You can also write stubs yourself following the examples in this article. The examples are also included in the download package. The result of the C++ code is a share library, on Windows platform, and it can only be built into a dynamic library. So the first step is to create a loader to init environment and load it.
Before starting, CLE for Win32 should be installed from http://www.srplab.com/data/starcore_win32.1.5.1.exe.
Dynamic library loader
The loader init CLE environment imports the wrapandroid service description file SRPWrapAndroidEngine.xml and creates a service for the share library. The loader can be created with Java, Python, lua, C#, C++, or other languages supported by CLE. Here we create it using C++. The code is shown below.
- Init the CLE environment.
class ClassOfBasicSRPInterface *BasicSRPInterface;
VS_CORESIMPLECONTEXT Context;
BasicSRPInterface = VSCore_InitSimpleEx(&Context,0,0,NULL,0,NULL);
if( BasicSRPInterface == NULL ){
printf("init starcore fail\n");
return -1;
}
{
FILE *hFile;
int FileSize;
char *xmlBufer;
hFile = fopen("SRPWrapAndroidEngine.xml","rt");
fseek(hFile,0,SEEK_END);
FileSize = ftell(hFile);
fseek(hFile,0,SEEK_SET);
xmlBufer = (char *)malloc(FileSize+1);
FileSize = fread(xmlBufer,1,FileSize,hFile);
fclose(hFile);
xmlBufer[FileSize] = 0;
if( BasicSRPInterface ->ImportServiceFromXmlBuf(xmlBufer,false) == VS_FALSE ){
printf("parse [SRPWrapAndroidEngine.xml] failed\n");
return -1;
}
free(xmlBufer);
}
BasicSRPInterface -> CreateService( "","wrapandroid", NULL, "123",5,0,0,0,0,0 );
SRPInterface = BasicSRPInterface ->GetSRPInterface("wrapandroid","root","123");
SRPInterface ->CheckPassword(VS_FALSE);
- Create root activity.
The Share library needs a pre-created activity. The following is used to create it. And we write stub code for the getCurrent
function of the activity. Then this function can be called from the share library.
Here is the stub function of getCurrent
:
void *RootActivity;
static void *ActivityClass_getCurrent(void *Object){
return RootActivity;
}
The code for creating the root activity:
void *ActivityClass = SRPInterface -> GetObjectEx(NULL,"ActivityClass");
void *AtomicActivityClass = SRPInterface ->ObjectToAtomic(ActivityClass);
void *AtomicActivityClassFunction_getCurrent = SRPInterface ->CreateAtomicFunctionSimple(
AtomicActivityClass,"getCurrent","VS_OBJPTR getCurrent();",NULL,NULL,VS_FALSE,VS_FALSE);
SRPInterface -> SetAtomicFunction(AtomicActivityClassFunction_getCurrent,(void *)ActivityClass_getCurrent);
RootActivity = SRPInterface ->MallocObjectL(&VSOBJID_ActivityClass,0,NULL);
- Load the share library.
Loading the share library is simple. We call the CLE interface DoFile
function to do this, more like the script interface.
if( SRPInterface ->DoFile("","../libcode.dll",NULL, NULL, VS_FALSE) == VS_FALSE ){
printf("load library file\n");
return -1;
}
When compiling, starlib_vcm.lib should be added to the project, as shown below:
Add header files to the project:
Set break points on code.cpp, and Run.
Create stubs for android classes
We create stubs for Android classes in order to debug or for other goals. It will enable us to debug android C++ codes on the Windows platform. How do we create a stub? It is simple, like this:
Define function:
static void *ActivityClass_getCurrent(void *Object){
…
}
Create the stub function:
void *AtomicActivityClassFunction_getCurrent =
SRPInterface ->CreateAtomicFunctionSimple(AtomicActivityClass,
"getCurrent","VS_OBJPTR getCurrent();",NULL,NULL,VS_FALSE,VS_FALSE);
and assign the address to the stub function:
SRPInterface -> SetAtomicFunction(AtomicActivityClassFunction_getCurrent,(void *)ActivityClass_getCurrent);
If you create stubs of all functions of android classes, your application can be migrated to Windows or other platforms.