How C/C++ Debugging Works on Android

来源:互联网 发布:电动飞机杯 知乎 编辑:程序博客网 时间:2024/06/06 00:21
http://mhandroid.wordpress.com/2011/01/25/multithread-cc-debugging-on-emulators-and-rooted-devices-from-android-1-5/ 

Multithread C/C++ Debugging on Emulators and Rooted Devices from Android 1.5  原文带图 

In Android 2.2 Google announced ndk-gdb script which ease the debugging on the device. Later it turns out that it is not capable of debugging any other thread than the main thread. 

In Android 2.3 Google ships fixed setup so you are now finally able to debug multithread application. 

In fact multithread debugging is possible at least from Android 1.5. But you have to set up the environment yourself. Due to permissions limitation it is only possible on rooted devices. But it is possible on emulator as emulator is always rooted! 


Here is a small shell script which does everything for you. It will start gdbserver on the device, gdb on PC and connect them together. It will also set up paths correctly so gdb can find its unstripped libraries on PC while having stripped libraries on the device. 

Run this script from your project root directory. You have to modify APP_PACKAGE and ANDROID_TARGET variables to match your project settings. You also need to have you project compiled in debug mode. This can be done by running ndk-build NDK_DEBUG=1 or by setting android:debuggable=”true” in AndroidManifest.xml and compiling normally. 

001 
#!/bin/bash 
002 
# Martin Hejna (c) 2011 
003 
# martin.hejna@gmail.com 
004 
# CC-by 
005 

006 

007 
## Comment out for quiet run 
008 
set -x 
009 

010 
# Set those variables to match your application 
011 
[ "$APP_PACKAGE" ] || APP_PACKAGE="com.example" 
012 
[ "$ANDROID_TARGET" ] || ANDROID_TARGET="android-9" 
013 

014 
# Set installation paths of SDK and NDK 
015 
[ "$ANDROID_SDK" ] || ANDROID_SDK="/home/martin/Android/android-sdk-linux_86" 
016 
[ "$ANDROID_NDK" ] || ANDROID_NDK="/home/martin/Android/android-ndk-r5" 
017 

018 
# adb and gdb executable paths 
019 
ADB="$ANDROID_SDK"/platform-tools/adb 
020 
GDB=$ANDROID_NDK/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86/bin/arm-linux-androideabi-gdb 
021 

022 
# Check we are in application root path (the one with AndroidManifest.xml in it) 
023 
APP_ROOT=`dirname $0` 
024 
if [ ! -f $APP_ROOT/AndroidManifest.xml ] 
025 
then 
026 
    echo This script must be run from package root directory 
027 
    echo This is the directory with AndroidManifest.xml in it 
028 
    exit 1 
029 
fi 
030 

031 
# Path to unstripped versions of native .so libraries 
032 
UNSTRIPPED_SO_LIB="$APP_ROOT/obj/local/armeabi" 
033 
APP_PROCESS=$UNSTRIPPED_SO_LIB/app_process 
034 
APP_LIBC_SO=$UNSTRIPPED_SO_LIB/libc.so 
035 
if [ ! -d $UNSTRIPPED_SO_LIB ]; then 
036 
    echo Error: Directory $UNSTRIPPED_SO_LIB does not exist 
037 
    exit 1 
038 
fi 
039 

040 
# Use cgdb if available 
041 
if which cgdb > /dev/null 
042 
then GDB_CMD="cgdb -d $GDB --" 
043 
else GDB_CMD=$GDB 
044 
fi 
045 

046 
# We use same port number on pc and on device 
047 
DEBUG_PORT=5039 
048 

049 
# File with gdb setup instructions (executed when gdb starts) 
050 
[ "$GDB_SETUP" ] || GDB_SETUP="/tmp/gdb.setup" 
051 

052 

053 
# Generate setup file for gdb with appropriate paths to symbols 
054 
function gdb_setup() 
055 

056 
cat <<EOF >$GDB_SETUP 
057 
  set solib-search-path $UNSTRIPPED_SO_LIB:$ANDROID_NDK/platforms/$ANDROID_TARGET/arch-arm/usr/lib 
058 
  directory $ANDROID_NDK/platforms/$ANDROID_TARGET/arch-arm/usr/include $APP_ROOT/jni $ANDROID_NDK/sources/cxx-stl/system 
059 
  ## set solib-absolute-prefix $ANDROID_NDK/platforms/$ANDROID_TARGET/arch-arm/usr/lib 
060 

061 
  file $APP_PROCESS 
062 
  target remote :$DEBUG_PORT 
063 

064 
  # Probably not needed. Uncomment if you have problems with watchdogs 
065 
  ## set variable gDvm.nativeDebuggerActive=true 
066 
EOF 
067 

068 

069 
# Get pid of the process (first one if there are more of them) 
070 
function get_pid_on_device() 
071 

072 
    $ADB shell ps | grep "$@" | awk '{print $2; exit}' 
073 

074 

075 

076 
############################################################################# 
077 
## Main entry 
078 
############################################################################ 
079 

080 

081 
# Restart adb server. Uncomment if you have problems with adb being stuck 
082 
## $ADB kill-server 
083 

084 
# Get the app_process and libc.so from the device 
085 
# Always download and overwrite those files to be sure we have correct ones 
086 
$ADB pull /system/bin/app_process $APP_PROCESS 
087 
$ADB pull /system/lib/libc.so $APP_LIBC_SO 
088 

089 
# Set up port forwarding for gdb and gdbserver 
090 
if $ADB forward tcp:$DEBUG_PORT tcp:$DEBUG_PORT 
091 
then : 
092 
else 
093 
    echo Failed to setup tcp forwarding for port $DEBUG_PORT 
094 
    echo Is the device running? 
095 
    exit 1 
096 
fi 
097 

098 
# Get pid of the process 
099 
APP_PID=`get_pid_on_device "$APP_PACKAGE"` 
100 
if [ -z $APP_PID ] 
101 
then 
102 
    echo "$APP_PACKAGE is not running" 
103 
    exit 1 
104 
fi 
105 

106 
# Kill any prior gdbserver 
107 
GDBSERV_PID=`get_pid_on_device gdbserver` 
108 
if [ -n "$GDBSERV_PID" ] 
109 
then 
110 
    $ADB shell kill $GDBSERV_PID 
111 
    sleep 1 
112 
fi 
113 

114 
# Start gdbserver and attach it to our application 
115 
$ADB shell gdbserver :$DEBUG_PORT --attach $APP_PID & 
116 
sleep 2 
117 

118 
# Invoke gdb and feed it with setup commands to set up path to libraries 
119 
gdb_setup 
120 
$GDB_CMD -x $GDB_SETUP 
121 
rm -f $GDB_SETUP 
Have fun! 

---------------------------------------------------------------------------------------------------------- 

How C/C++ Debugging Works on Android(http://mhandroid.wordpress.com/2011/01/25/how-cc-debugging-works-on-android/) 
Posted on January 25, 2011 
How debugging of C/C++ code works on Android? Nothing special actually. Gdb itself has a feature for remote debugging. 


Note: For this article I am using Android NDK, r5. The behavior of ndk-build and ndk-gdb commands can be different in other versions. 

Basic setup 

You simply run gdbserver on the device and let it attach to some process. Gdbserver acts as a remote debugger which is commanded by gdb itself. This step is pretty easy and is done by some variant of 

gdbserver :5039 --attach 123 

where 5039 is a TCP port number to which gdb will connect and 123 is a PID of process we want to debug. 

The next step is to run gdb client and connect it to the gdbserver on the device. You need to start gdb with command 

gdb app_process 

where app_process is Android binary which you need to copy from Android device to your PC. This binary is some kind of loader and lies in /system/bin/app_process file on the device (or in the emulator). 

After you start gdb you connect it with the gdbserver by this command (ran from gdb shell) 

target remote :5039 

Because you are usually running Android device connected through USB and not normal TCP/IP network (or you are running Android emulator) you have to set up TCP port forwarding in advance. This is done by command 

adb forward tcp:5039 tcp:5039 

which you have to execute before starting gdb. This command will handle all connection requests to port 5039 on PC to the device on the same port. 

And this is all. 

Debug symbols 

You typically compile your .so library with debug symbols. But it makes good sense to strip those symbols off before uploading your binary to the Android device. Binary will be smaller (which means faster installation time of the apk package), will occupy less memory and will run faster. So you upload stripped version of your libraries to the Android device and keep unstripped versions on your PC. 

To tell gdb the path to the unstripped libraries run this command from gdb shell 

set solib-search-path obj/local/armeabi 

You can enter more paths and separate them by colon (:) character. 

There is a difference between debug symbols and code optimization. Code optimization allows changing the code flow or removing some dead-code and unused variables. While debug symbols bind machine code together with appropriate lines in the source code. Usually you build a binary with debug symbols and no code optimization for debugging and another binary without debug symbols and with some code optimization for release. But you can use debug symbols together with code optimization. In this case some variables will be missing and some lines of code will be probably jumped over but in general you can debug this way. 

Android ndk-build command always produce binaries (your libnative.so files) with debug symbols and place them in obj/local/armeabi subdirectory of your project. Then it strips off debug symbols and place stripped version in the final .apk (and thus in the device). ndk-gdb script uses this set solib-search-path command to point gdb to the unstripped binary so you can see your position in the source code. You can find the set solib-search-path command in obj/local/armeabi/gdb.setup file. 

If you have android:debuggable=”true” in your AndroidManifest.xml or if you run ndk-build with NDK_DEBUG=1 argument then ndk-build will produce binaries with no code optimization. Otherwise code optimization will be used. 

Sources 

Gdb needs to have access to your source files. Otherwise it cannot show you the source code as it is executed. You can configure directory with your source files by command 

directory jni 

You can enter more paths and separate them by space. Take a look into obj/local/armeabi/gdb.setup file for example. 

How ndk-gdb and friends works 

If you are using Android NDK, r5 and Android 2.3 device you can use ndk-gdb script which does everything for you. 

If you have android:debuggable=”true” in your AndroidManifest.xml then ndk-build will add the gdbserver into your .apk package. This gdbserver will be started by ndk-gdb and also all the other steps will be set up by this script. 

Important caveat 

Because gdbserver is attached to the already running process (as opposed to situation where process would be started by gdbserver) it can miss some code execution which take place soon after the application start. There is no easy solution to this. 

I usually write some endless while loop and then change the control variable after gdb is fully started. For example 


int i = 0 

while (!i) { 

  a++; 


After ndk-gdb starts gdb session I can set breakpoints appropriately and change value of i variable by command 

set var i=1 

and then continue in application execution by gdb command c. 

Multithread debug problem 

It is well-known problem that ndk-gdb shipped with Android NDK, r4b used on Android 2.2 device was only able to hit the breakpoint on the main thread. If you set breakpoint on any other thread gdb session will crash. 

Every time new thread is created libc will call _thread_created_hook() function (which has empty body). The purpose of this function is that gdb will set breakpoint on this function and thus will know that new thread was created. Gdb will then keep database of existing threads. Because of this gdb needs to have access to the libc.so file to figure out address of the _thread_created_hook() function. 

Android NDK, r5 contains ndk-gdb which copies the file libc.so from the device (or emulator) and stores it in obj/local/armeabi subdirectory of your project. Also gdbserver included in this ndk is compiled with libthread_db support. Both those two things are missing in Android NDK, r4b. 

Links 

Description of multithreading debug bug. 
http://code.google.com/p/android/issues/detail?id=9713 

Android NDK, r5 
http://dl.google.com/android/ndk/android-ndk-r5-linux-x86.tar.bz2 

Android ndk main page 
http://developer.android.com/sdk/ndk/index.html 


--------------------------------------------------------------------------------------------------------------- 

http://mhandroid.wordpress.com/2011/01/23/using-eclipse-for-android-cc-debugging/  原文带图 
Using Eclipse for Android C/C++ Debugging 
Posted on January 23, 2011 
Yes. You can use Eclipse for debugging of C/C++ code. I personally prefer cgdb but if you want to debug in Eclipse here is how. 


See my previous spot how to set up cgdb debugger if you think it will suit you. 

See my blog spot how to set up Eclipse for compiling and editing C/C++ code. 

0) Prerequisities 

You need Eclipse with CDT installed. 
See my previous spot how to do it. 

You also need Android ndk. Download it from http://developer.android.com/sdk/ndk/index.html and unpack it somewhere. 

1) In Eclipse open your Android project which contains C/C++ code that you want to debug. 

For this tutorial I’ve created simple MyAndroidProject. 

2) Set android:debuggable=”true”. Set android:targetSdkVersion=”9″. 

android:debuggable is a property of <application> tag in your AndroidManifest.xml. You can set it either directly in xml or in Application tab as in the screenshot. 



android:targetSdkVersion=”9″ is a property of <uses-sdk> tag in your AndroidManifest.xml. You can set it either directly in xml or in Manifest tab as in the screenshot. 



3) Run your application in debug mode and try to run ndk-gdb from console 

To run application in debug mode press debug button (green bug/spider button in toolbox). In console go to your project directory and run ndk-gdb. It should succeed. If it fails you have to resolve the problem. Running ndk-gdb does not only ensure us that we are doing everything right so far, but also creates app_process, gdb.setup and libc.so files in obj/local/armeabi/ subdirectory of our project. Those files will be needed in later steps. 

4) Create C/C++ debug configuration 

Click on combo-box like down arrow next to debug button. Pop-up menu will appear and then select Debug Configurations… 



In Debug Configurations window select C/C++ Application and press New button as advised on the right pane. 



5) Set name of the debug configuration, and fill information on Main tab. 

Select Standard Create Process Launcher by clicking on the blue Select other… link at the bottom of the window. 

In C/C++ Application entry fill the path to the app_process binary which is located in obj/local/armeabi/ subdirectory of your project. 



6) Click on Debugger tab and fill information about debugger. 

Choose gdbserver Debugger as a Debugger. 

It’s good idea to set initial breakpoint to some function but Android projects do not contain main function so fill some appropriate function name in Stop on startup field (I filled Java_com_example_map_MyAndroidProject_doSomething but you can see only Java_com_exam on the screenshot). 

Set path to GDB debugger. The debugger is distributed with the Android ndk. Its located at toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86/bin/arm-linux-androideabi-gdb. 

Set path to GDB command line. This path should point to obj/local/armeabi/gdb2.setup file inside your project. You don’t have gdb2.setup file there yet but you will create one in a while. 



7) Click on the Connection tab (so you are in Debugger->Connection section of your C/C++ debug configuration) and fill information about connecting gdb with gdbserver. 

Choose TCP as a type of connection and choose 5039 as a Port number. 



8) Finally click Apply to save all the information about your C/C++ debug configuration. 

This will save your new C/C++ debug configuration. Later, when running your application in debug mode you can choose in combo box associated with Debug button what debug configuration you want to use. Now you have two debug configurations. Android Java one which was created automatically for you when you’ve created Android project. And C/C++ one you’ve just created. 

9) Go to the obj/local/armeabi/ subdirectory of your project and copy gdb.setup file to gdb2.setup file. Remove target remote :5039 line from gdb2.setup. 

Eclipse don’t like target remote :5039 line in gdb setup file because it wants to enter this command internally (that is why you configured port 5039 in the previous step). Because the gdb.setup file is recreated by ndk scripts you have to copy it to the gdb2.setup and point Eclipse to the gdb2.setup file (we did this in step 6). 

10) Go to the directory with Android ndk and copy ndk-gdb to ndk-gdb-eclipse. Remove execution of gdb command from ndk-gdb-eclipse. 

Original content of ndk-gdb (Android NDK, r5): 

578 
if [ -n "$OPTION_EXEC" ] ; then 
579 
    cat $OPTION_EXEC >> $GDBSETUP 
580 
fi 
581 
$GDBCLIENT -x `native_path $GDBSETUP` 

Content of ndk-gdb-eclipse: 

578 
if [ -n "$OPTION_EXEC" ] ; then 
579 
    cat $OPTION_EXEC >> $GDBSETUP 
580 
fi 
581 
## $GDBCLIENT -x `native_path $GDBSETUP` 
Eclipse will run the gdb binary itself. So we have to remove the execution of gdb from ndk-gdb. To save original content it is good idea to copy the ndk-gdb to ndk-gdb-eclipse. 

11) Now you are done. Put breakpoint in Java code after the execution of System.loadLibrary() and start your application in Debug mode. 

Start your application in debug mode by clicking on Debug button. It will automatically choose Android debug mode. Later you will have to take care to choose Android Java debugging configuration by clicking on combo arrow associated with the debug button. 

The reason that breakpoint should be after System.loadLibrary() call is that our C/C++ code will be already loaded in that point and we can set breakpoints in it. 

12) When execution reach the breakpoint run ndk-gdb-eclipse from your project directory and start debugging in C/C++ debug mode. 

Go to the directory with your project and run ndk-gdb-eclipse. This will start server-part of the debugging infrastructure. Now click on the combo arrow associated with the debug button in Eclipse and choose Debug Configurations… Choose your C/C++ debug configuration and click Debug button. 

The C/C++ debug configuration will be added to recently used debug configurations so later you don’t have to walk into the Debug Configurations… window again and you can choose between Android Java debug configuration and C/C++ debug configuration by just clicking on the combo arrow associated with debug button in Eclipse. However always make sure which debug configuration you are about to execute as this is where mistakes happen very often (at least for me). 

After you’ve started C/C++ debug configuration click Resume button (or press F8). The application should resume its run and stop on C/C++ breakpoint (if you have one). 

13) Now you are debugging the C/C++ code. Have fun! 

Try to set some breakpoints in C/C++ code, etc. 

Note on running ndk-gdb-eclipse 

You have to run ndk-gdb-eclipse every time before starting C/C++ debug session. This script starts the gdbserver binary on device/emulator so gdb (run by Eclipse) can connect to it. 

I made several attempts to force Eclipse to run ndk-gdb-eclipse script itself on start of C/C++ debug session. The most logical point is to write small script which will run ndk-gdb-eclipse without parameters (or with –force parameter) and then run gdb from Android ndk toolchain with all the parameters. This script can be used as GDB debugger command in the Debugger tab. But even if this script (and ndk-gdb-eclipse inside) was run successfully, the resulting connection of gdb and gdbserver always broke apart. 

Final note and thanks! 

This tutorial is heavily inspired by Sequoyah Project native debug tutorial. See http://www.eclipse.org/sequoyah/documentation/native_debug.php 

Many thanks to you guys. 



--------------------------------------------------------------------------------------------------------------- 

http://mhandroid.wordpress.com/2011/01/23/using-cgdb-with-ndk-debug-and-cgdb-tutorial/  原文带图 
Using cgdb with ndk-debug (and cgdb tutorial) 
Posted on January 23, 2011 
Android ndk (http://developer.android.com/sdk/ndk/index.html) comes with ndk-gdb command that starts gdb debugger and connects it to Android application. 

cgdb (http://cgdb.sourceforge.net/) is superior console front-end to gdb so it seems logical to use it for debugging of Android applications. Following modification of ndk-gdb script will cause that cgdb will be invoked instead of gdb. 


Original ndk-gdb (Android NDK, r5): 

569 
# Now launch the appropriate gdb client with the right init commands 
570 

571 
GDBCLIENT=${TOOLCHAIN_PREFIX}gdb 
572 
GDBSETUP=$APP_OUT/gdb.setup 

Modified ndk-gdb (originally from Android NDK, r5) which uses cgdb instead of gdb: 

569 
# Now launch the appropriate gdb client with the right init commands 
570 

571 
GDBCLIENT="cgdb -d ${TOOLCHAIN_PREFIX}gdb --" 
572 
GDBSETUP=$APP_OUT/gdb.setup 

Now if you run ndk-gdb you will get cgdb front-end. 


Small cgdb tutorial 

In cgdb you can have focus either on source window (part with the source text) or gdb window. 
To switch from source window to gdb press i. 
To switch from gdb window to source press esc. 

Commands you can use in source window: 
arrows — scroll the text 
space — set or delete breakpoint 
o — open source file 
i — switch focus to gdb window 
- — make source window 1 line smaller 
= — make source window 1 line bigger 

Commands you can use in gdb window: 
n — next instruction. Will not dive into subfunctions 
s — step. Will dive into subfunctions 
c — continue 
b — set breakpoint 
bt — show call stack (backtrace) 
info threads — show information about running threads 
info breakpoints — show information about breakpoints 
pgup, pgdown — scroll the content of gdb window 
esc — switch focus to source windows 

Full cgdb documentation is at http://cgdb.sourceforge.net/docs/cgdb-no-split.html 
Full gdb documentation is at http://www.gnu.org/software/gdb/documentation/ 

Have fun! 



--------------------------------------------------------------------------------------------------------------- 
http://mhandroid.wordpress.com/2011/01/23/using-eclipse-for-android-cc-development/ 原文带图 
Programming in C/C++ on Android is just awesome! This tutorial shows how to setup Eclipse for using C/C++ together with Java in Android projects. 


0) Prerequisities 

You need to have Google ADT (Android Development Tools) installed. See http://developer.android.com/sdk/eclipse-adt.html how to do it. 

You also need Android ndk. Download it from http://developer.android.com/sdk/ndk/index.html and unpack it somewhere. 

1) Install CDT (C/C++ Development Tools) into Eclipse. 

Choose Help->Install New Software… from the main menu. 

Choose http://download.eclipse.org/releases/galileo as the source site. If you have another Eclipse release than Galileo choose the appropriate url. 



Click Next, Accept licences and finish the installation process. 

2) In Eclipse create Android project to which you want to add C/C++ code (if you already don’t have one). 

For this tutorial I’ve created simple MyAndroidProject. 

3) In file manager create jni/ directory in your project directory and place your C/C++ sources file here. Also put here Android.mk file which is a makefile that tells Android build-system how to build your files. 

Take a look into Android ndk docs/ANDROID-MK.html file how to create one. 

Simple example of Android.mk file: 

01 
LOCAL_PATH := $(call my-dir) 
02 

03 
include $(CLEAR_VARS) 
04 

05 
LOCAL_LDLIBS    := -llog 
06 

07 
LOCAL_MODULE    := native 
08 
LOCAL_SRC_FILES := native.c 
09 

10 
include $(BUILD_SHARED_LIBRARY) 
4)  Refresh (F5) directories in Package Explorer to see jni directory here. Open your .c/.cpp file. 

Your .c/.cpp file (native.c in my case) contains a lot of syntax errors which are not truly syntax errors. This is because Eclipse threats the project as a pure Java project. We have to convert the project into mixed Java & C/C++ project. 



5) Press Ctrl+n (or choose File->New->Other… from main menu) and select Convert to a C/C++ Project. 



This will convert your project into a mixed Java & C/C++ project rather than into pure C/C++ project (the name of the function is misleading). 

Click Next. Then choose your project and below choose Makefile project and – Other Toolchain –. Click Finish. 



After doing this Eclipse will ask you if you want to switch to C/C++ perspective. Choose Yes because otherwise you wouldn’t be able to set C/C++ build preferences. 

6) Click on your project with right button and select Properties or press Alt+Enter 

Properties windows will appear. Here you have to configure use of ndk-build instead of make all command and set proper include paths. 

7) Choose C/C++ Build and configure ndk-build as a build command 

In Builder settings fill ndk-build into Build command entry. You have to uncheck Use default build command. You also need to have ndk-build script in your PATH. 



In Behaviour setting uncheck clean (ndk-build cleans project automatically on build and does not support separate clean command) and clear all text from build (ndk-build does not accept all as a parameter. 



Click Apply to save settings. 

8) Choose C/C++ General->Paths and Symbols and configure include path 

In Includes tab choose GNU C or GNU C++ and click Add… button. Add path to include directory which is located in platforms/android-4/arch/arm/usr/include subdirectory of place where you’ve unpacked Android ndk. Include path depends on target for which you are compiling (android-4 in my case — i.e. Android 1.6). 



Finally click Apply and OK and that is all. Now you can use all Eclipse power for editing your C/C++ sources. If you click Run or Debug Eclipse will compile C/C++ code as well as Java code and run it on device/emulator. However you will not be able to debug C/C++ code.