hello-gdbserver:A debuggable JNI example for Android

原文地址::http://vilimpoc.org/blog/2010/09/23/hello-gdbserver-a-debuggable-jni-example-for-android/

 

 

Before I get into the rest of the post, here is a link to a .zip of the Eclipse project files if you just want to skip ahead.

The Rest Of The Post

Native software development on Android using its JNI capabilities is incredibly frustrating without proper visibility into the causes of native code errors.

Here is an example project and some background info that shows how to get gdb and gdbserver working to catch those nasty bugs. Just extract the project files into the NDK's sample directory and import the project into Eclipse.

Here were some of the search terms I tried unsuccessfully while trying find something like this:

using gdb on android
using ndk-gdb
debug jni android
hello world android native code
hello world android emulator
debugging using gdbserver android

Requirements

  • A phone running Android 2.2 (Froyo) (might work on the emulator, but I didn't focus on that)
  • The latest Android NDK (at least r4b)
  • Lots of Patience

The NDK-GDB.TXT readme file in the NDK has a good overview of the process of getting a debuggable Android application + JNI build set up, but I would have much preferred it if Google had simply included something like a hello-gdbserver test app in the provided samples. One simple app where you could induce a crash to see what would happen, like a tutorial for smarter bug-hunting. So, I made this one.

Getting Started

Here's an example of some (exaggeratedly) buggy code in the JNI shared object in jni/hello-gdbserver.c:

/*
 * Class:     com_example_hellogdbserver_HelloGdbServer
 * Method:    invokeCrash
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_example_hellogdbserver_HelloGdbServer_invokeCrash
 (JNIEnv *env, jclass clazz)
{
    int *crasher = 0x0;
    *crasher = 0xdeaddead;
}

Fugly. This will kill your app dead without so much as a thank you or goodbye. And you'll never know what hit you, because all you see is a stack trace in DDMS that says "I blew up, and here is a long list of hexadecimal numbers you'll never decode." In any non-trivial app (which is basically any app), you could spend days trying to hunt this down, which is certainly not the best use of engineer time.

I/DEBUG   (   27): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
I/DEBUG   (   27): Build fingerprint: 'generic/sdk/generic/:1.6/Donut/20842:eng/test-keys'
I/DEBUG   (   27): pid: 260, tid: 260  >>> com.example.hellogdbserver <<<
I/DEBUG   (   27): signal 11 (SIGSEGV), fault addr 00000000
I/DEBUG   (   27):  r0 0000a9d0  r1 4376e278  r2 deaddead  r3 00000000
I/DEBUG   (   27):  r4 bef506b0  r5 00000000  r6 80600ba1  r7 4104baec
I/DEBUG   (   27):  r8 bef50690  r9 4104bae4  10 4104bad4  fp 00000000
I/DEBUG   (   27):  ip 80600ba1  sp bef50680  lr ad00e438  pc 80600bae  cpsr 60000030
I/DEBUG   (   27):          #00  pc 00000bae  /data/data/com.example.hellogdbserver/lib/libhello-gdbserver.so
I/DEBUG   (   27):          #01  pc 0000e434  /system/lib/libdvm.so
I/DEBUG   (   27):          #02  pc 00040b0e  /system/lib/libdvm.so
I/DEBUG   (   27):          #03  pc 000432b6  /system/lib/libdvm.so
I/DEBUG   (   27):          #04  pc 00013198  /system/lib/libdvm.so
I/DEBUG   (   27):          #05  pc 00017be4  /system/lib/libdvm.so
I/DEBUG   (   27):          #06  pc 0001762c  /system/lib/libdvm.so
I/DEBUG   (   27):          #07  pc 000529a8  /system/lib/libdvm.so
I/DEBUG   (   27):          #08  pc 00059eda  /system/lib/libdvm.so
I/DEBUG   (   27):          #09  pc 00013198  /system/lib/libdvm.so
I/DEBUG   (   27):          #10  pc 00017be4  /system/lib/libdvm.so
I/DEBUG   (   27):          #11  pc 0001762c  /system/lib/libdvm.so
I/DEBUG   (   27):          #12  pc 0005282c  /system/lib/libdvm.so
I/DEBUG   (   27):          #13  pc 0003f790  /system/lib/libdvm.so
I/DEBUG   (   27):          #14  pc 00031caa  /system/lib/libdvm.so
I/DEBUG   (   27):          #15  pc 0002a804  /system/lib/libandroid_runtime.so
I/DEBUG   (   27):          #16  pc 0002b306  /system/lib/libandroid_runtime.so
I/DEBUG   (   27):          #17  pc 00008bf2  /system/bin/app_process
I/DEBUG   (   27):          #18  pc 0000bd60  /system/lib/libc.so
I/DEBUG   (   27):          #19  pc b000163c  /system/bin/linker
I/DEBUG   (   27): stack:
I/DEBUG   (   27):     bef50640  00000071  
I/DEBUG   (   27):     bef50644  afe0b03f  /system/lib/libc.so
I/DEBUG   (   27):     bef50648  80600ba1  /data/data/com.example.hellogdbserver/lib/libhello-gdbserver.so
I/DEBUG   (   27):     bef5064c  00000007  
I/DEBUG   (   27):     bef50650  afe3c9a0  
I/DEBUG   (   27):     bef50654  afe39dd4  /system/lib/libc.so
I/DEBUG   (   27):     bef50658  0000a000  [heap]
I/DEBUG   (   27):     bef5065c  00000028  
I/DEBUG   (   27):     bef50660  4104896a  /data/dalvik-cache/data@app@com.example.hellogdbserver.apk@classes.dex
I/DEBUG   (   27):     bef50664  00000000  
I/DEBUG   (   27):     bef50668  001c5788  [heap]
I/DEBUG   (   27):     bef5066c  001a7710  [heap]
I/DEBUG   (   27):     bef50670  001c56f0  [heap]
I/DEBUG   (   27):     bef50674  ad043199  /system/lib/libdvm.so
I/DEBUG   (   27):     bef50678  df002777  
I/DEBUG   (   27):     bef5067c  e3a070ad  
I/DEBUG   (   27): #00 bef50680  4376e278  /dev/ashmem/mspace/dalvik-heap/2 (deleted)
I/DEBUG   (   27):     bef50684  0000a9d0  [heap]
I/DEBUG   (   27):     bef50688  00000000  
I/DEBUG   (   27):     bef5068c  00000000  
I/DEBUG   (   27): #01 bef50690  41295860  /dev/ashmem/dalvik-LinearAlloc (deleted)
I/DEBUG   (   27):     bef50694  0000bc60  [heap]
I/DEBUG   (   27):     bef50698  80600ba1  /data/data/com.example.hellogdbserver/lib/libhello-gdbserver.so
I/DEBUG   (   27):     bef5069c  4376e278  /dev/ashmem/mspace/dalvik-heap/2 (deleted)
I/DEBUG   (   27):     bef506a0  00000071  
I/DEBUG   (   27):     bef506a4  0000bc60  [heap]
I/DEBUG   (   27):     bef506a8  bef506b0  [stack]
I/DEBUG   (   27):     bef506ac  ad040b11  /system/lib/libdvm.so
D/Zygote  (   29): Process 260 terminated by signal (11)
I/WindowManager(   67): WIN DEATH: Window{437ee360 com.example.hellogdbserver/com.example.hellogdbserver.HelloGdbServer paused=false}
I/ActivityManager(   67): Process com.example.hellogdbserver (pid 260) has died.

The Google guys have a page describing how to transcribe that stuff back into meaningful information, but it's long-winded, manual, and from a practical standpoint a waste of time.

The Fix

Step 1. Tell the gcc C compiler to add debug symbols to your code

In Android.mk add the following line:

LOCAL_CFLAGS := -g
Problem 1. Android Ignores You

Oddly enough, even with the debug flag set, you'll notice that the final step of ndk-build, the Install step, copies and then strips your binaries of debug information. Not very helpful, that. Just run the ndk-build script with verbose mode active, V=1, and you'll see something like this:

Install        : libhello-gdbserver.so => /libs/armeabi
mkdir -p /libs/armeabi
install -p /libhello-gdbserver.so /libs/armeabi/libhello-gdbserver.so
/arm-eabi-4.4.0/bin/arm-eabi-strip --strip-debug  /libs/armeabi/libhello-gdbserver.so

which is due to a line in the setup.mk Makefile fragment:

build/toolchains/arm-eabi-4.4.0/setup.mk:188:
cmd-strip = $(TOOLCHAIN_PREFIX)strip --strip-debug $1

That last line kills your debugging symbols.

Step 2. Ignore Android


So you have to edit it out, as a quick hack, in the install-binary.mk Makefile fragment:

build/core/install-binary.mk:29:
# $(hide) $(call cmd-strip, $(PRIVATE_DST))

A better fix would be to add a real variable check in the Makefile for debug vs. non debug builds and to act accordingly. In any case, your next builds will have full debug symbols in them. So just do a quick

$ ndk-build -B V=1


[update: 2010/09/24]

A better fix:

build/core/install-binary.mk:29:
ifneq ($(APP_OPTIM),debug)
    $(hide) $(call cmd-strip, $(PRIVATE_DST))
endif

Now when you're building, you can just specify the APP_OPTIM level as a parameter to ndk-build:

~/android-ndk-r4b/ndk-build -B V=1 APP_OPTIM=debug
~/android-ndk-r4b/ndk-build -B V=1 APP_OPTIM=release

This is also useful because APP_OPTIM can be specified in the Application.mk file.

Step 3. Rebuild Your APK

Ok, so once you have the shared object built, it's time to package it into an APK using Eclipse. This is pretty standard. Since the ndk-build has "installed" the libhello-gdbserver.so shared object to the right directory, just do a Build -> Clean, Build -> Rebuild All in Eclipse and wait the half a second it takes to regenerate the APK.

Try running the application, you should see this:

Then, still in the project's directory, call ndk-gdb.

$ pwd
/home/vilimpoc/android-ndk-r4b/samples/hello-gdbserver
$ ~/android-ndk-r4b/ndk-gdb --verbose
Fun Error Message 1
Android NDK installation path: /home/vilimpoc/android-ndk-r4b
Using default adb command: /cygdrive/c/androidDev/android-sdk-windows/tools/adb
ADB version found: Android Debug Bridge version 1.0.26
Using final ADB command: '/cygdrive/c/androidDev/android-sdk-windows/tools/adb -e'
Using auto-detected project path: .
Found package name: com.example.hellogdbserver
Found debuggable flag: true
ABIs targetted by application: armeabi
Device API Level: 8
Device CPU ABI: armeabi
Compatible device ABI: armeabi
Found device gdbserver: /data/data/com.example.hellogdbserver/lib/gdbserver: No such file or directory
ERROR: Non-debuggable application installed on the target device.
Please re-install the debuggable version !

Make sure in AndroidManifest.xml, you've got Debuggable set to true. Rebuild and reinstall.

Fun Error Message 2
$ run-as: Package 'com.example.hellogdbserver' has corrupt installation

Rebuild and reinstall.

adb uninstall com.example.hellogdbserver
adb install HelloGdbServer.apk
Fun Error Message 3

Sometimes the above message looks like the following, where the information gets mashed up wrongly into the verbose output from ndk-gdb. e.g. the string "run-as: Package 'com.example.hellogdbserver' has corrupt installation" was variable-substituted into the command to forward debug information over tcp/5039.

##### NEW COMMAND
 /cygdrive/c/androidDev/android-sdk-windows/tools/adb -e forward tcp:5039 localfilesystem:run-as: Package 'com.example.hellog
 dbserver' has corrupt installation/debug-socket##### NEW COMMAND
 /cygdrive/c/androidDev/android-sdk-windows/tools/adb -e shell run-as com.example.hellogdbserver lib/gdbserver +debug-socket
 --attach 7492
 Android Debug Bridge version 1.0.26
-d                            - directs command to the only connected USB device
 returns an error if more than one USB device is present.
 -e                            - directs command to the only running emulator.
 returns an error if more than one emulator is running.
[... snip ...]
- If it is "system" or "data", only the corresponding partition
 is updated.
 ERROR: Could not setup network redirection to gdbserver?
 Maybe using --port=<port> to use a different TCP port might help?

The solution remains the same: Rebuild APK and reinstall.

Fun Error Message 4
run-as: Package 'com.example.hellogdbserver' is unknown

Sometimes you must Clean and Rebuild your entire project in Eclipse, especially if you make a dumb mistake, like change your Java Package from "com.examples.hellogdbserver" to "com.example.hellogdbserver". Sometimes that slight change doesn't quite make it into the package, or the aapt package manager doesn't see it, and you end up with an application running under a package name that isn't what you expect, and isn't one that run-as can find. Try adb install / uninstalling a couple of times, or uninstalling via the phone's Settings -> Applications menu to get the app into a state where the phone recognizes the package.

The rule of thumb is: If adb install/uninstall can see your program by its package name, then so can gdb.

When It Works, It Looks Like This

vilimpoc@funky ~
$ ls
android-ndk-r4b  cygdrive

vilimpoc@funky ~
$ cd android-ndk-r4b/

vilimpoc@funky ~/android-ndk-r4b
$ ls
GNUmakefile  README.TXT  apps  build  docs  ndk-build  ndk-gdb  out  samples  sources

vilimpoc@funky ~/android-ndk-r4b
$ cd samples

vilimpoc@funky ~/android-ndk-r4b/samples
$ ls
bitmap-plasma  hello-exe  hello-gdbserver  hello-gl2  hello-jni  hello-neon  san-angeles  two-libs

vilimpoc@funky ~/android-ndk-r4b/samples
$ cd hello-gdbserver

vilimpoc@funky ~/android-ndk-r4b/samples/hello-gdbserver
$ ls -l
total 5
-rwx------+ 1 vilimpoc Domain Users 721 2010-09-23 16:15 AndroidManifest.xml
drwx------+ 1 vilimpoc Domain Users   0 2010-09-23 15:12 assets
drwx------+ 1 vilimpoc Domain Users   0 2010-09-23 17:24 bin
-rwx------+ 1 vilimpoc Domain Users 364 2010-09-23 15:12 default.properties
drwx------+ 1 vilimpoc Domain Users   0 2010-09-23 15:12 gen
drwx------+ 1 vilimpoc Domain Users   0 2010-09-23 16:20 jni
drwxr-xr-x+ 1 vilimpoc Domain Users   0 2010-09-23 15:23 libs
drwxr-xr-x+ 1 vilimpoc Domain Users   0 2010-09-23 15:23 obj
drwx------+ 1 vilimpoc Domain Users   0 2010-09-23 15:13 res
drwx------+ 1 vilimpoc Domain Users   0 2010-09-23 14:49 src

vilimpoc@funky ~/android-ndk-r4b/samples/hello-gdbserver
$ ~/android-ndk-r4b/ndk-gdb --verbose
Android NDK installation path: /home/vilimpoc/android-ndk-r4b
Using default adb command: /cygdrive/c/androidDev/android-sdk-windows/tools/adb
ADB version found: Android Debug Bridge version 1.0.26
Using final ADB command: '/cygdrive/c/androidDev/android-sdk-windows/tools/adb'
Using auto-detected project path: .
Found package name: com.example.hellogdbserver
Found debuggable flag: true
ABIs targetted by application: armeabi
Device API Level: 8
Device CPU ABIs: armeabi-v7a armeabi
Compatible device ABI: armeabi
Found device gdbserver: /data/data/com.example.hellogdbserver/lib/gdbserver
Using gdb setup init: /home/vilimpoc/android-ndk-r4b/samples/hello-gdbserver/libs/armeabi/gdb.setup
Using toolchain prefix: /home/vilimpoc/android-ndk-r4b/build/prebuilt/windows/arm-eabi-4.4.0/bin/arm-eabi-
Using app out directory: /home/vilimpoc/android-ndk-r4b/samples/hello-gdbserver/obj/local/armeabi
Found data directory: '/data/data/com.example.hellogdbserver'
Found first launchable activity: .HelloGdbServer
Launching activity: com.example.hellogdbserver/.HelloGdbServer
##### NEW COMMAND
/cygdrive/c/androidDev/android-sdk-windows/tools/adb shell am start -n com.example.hellogdbserver/.HelloGdbServer
Starting: Intent { cmp=com.example.hellogdbserver/.HelloGdbServer }
##### NEW COMMAND
/cygdrive/c/androidDev/android-sdk-windows/tools/adb shell sleep 1
Found running PID: 1948
Launching gdbserver.
Launched gdbserver successfully.
Setup network redirection
##### NEW COMMAND
##### NEW COMMAND
/cygdrive/c/androidDev/android-sdk-windows/tools/adb shell run-as com.example.hellogdbserver lib/gdbserver +debug-socket --a
/cygdrive/c/androidDev/android-sdk-windows/tools/adb forward tcp:5039 localfilesystem:/data/data/com.example.hellogdbserver/
debug-socket
Attached; pid = 1948
Listening on sockaddr socket debug-socket
##### NEW COMMAND
/cygdrive/c/androidDev/android-sdk-windows/tools/adb pull /system/bin/app_process C:/cygwin/home/vilimpoc/android-ndk-r4b/sa
mples/hello-gdbserver/obj/local/armeabi/app_process
5 KB/s (0 bytes in 5680.001s)
Pulled  from device/emulator.
GNU gdb 6.6
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "--host=i686-pc-cygwin --target=arm-elf-linux".
Error while mapping shared library sections:
/system/bin/linker: No such file or directory.
Error while mapping shared library sections:
libc.so: No such file or directory.
Error while mapping shared library sections:
libstdc++.so: No such file or directory.
Error while mapping shared library sections:
libm.so: No such file or directory.
Error while mapping shared library sections:
liblog.so: No such file or directory.
Error while mapping shared library sections:
libcutils.so: No such file or directory.
Error while mapping shared library sections:
libz.so: No such file or directory.
Error while mapping shared library sections:
libutils.so: No such file or directory.
Error while mapping shared library sections:
libbinder.so: No such file or directory.
Error while mapping shared library sections:
libexpat.so: No such file or directory.
Error while mapping shared library sections:
libcrypto.so: No such file or directory.
Error while mapping shared library sections:
libssl.so: No such file or directory.
Error while mapping shared library sections:
libicudata.so: No such file or directory.
Error while mapping shared library sections:
libicuuc.so: No such file or directory.
Error while mapping shared library sections:
libicui18n.so: No such file or directory.
Error while mapping shared library sections:
libsqlite.so: No such file or directory.
Error while mapping shared library sections:
libnativehelper.so: No such file or directory.
Error while mapping shared library sections:
libnetutils.so: No such file or directory.
Error while mapping shared library sections:
libEGL.so: No such file or directory.
Error while mapping shared library sections:
libwpa_client.so: No such file or directory.
Error while mapping shared library sections:
librpc.so: No such file or directory.
Error while mapping shared library sections:
libgps.so: No such file or directory.
Error while mapping shared library sections:
libhardware_legacy.so: No such file or directory.
Error while mapping shared library sections:
libpixelflinger.so: No such file or directory.
Error while mapping shared library sections:
libhardware.so: No such file or directory.
Error while mapping shared library sections:
libui.so: No such file or directory.
Error while mapping shared library sections:
libsurfaceflinger_client.so: No such file or directory.
Error while mapping shared library sections:
libcamera_client.so: No such file or directory.
Error while mapping shared library sections:
libemoji.so: No such file or directory.
Error while mapping shared library sections:
libjpeg.so: No such file or directory.
Error while mapping shared library sections:
libskia.so: No such file or directory.
Error while mapping shared library sections:
libGLESv1_CM.so: No such file or directory.
Error while mapping shared library sections:
libskiagl.so: No such file or directory.
Error while mapping shared library sections:
libdvm.so: No such file or directory.
Error while mapping shared library sections:
libGLESv2.so: No such file or directory.
Error while mapping shared library sections:
libETC1.so: No such file or directory.
Error while mapping shared library sections:
libsonivox.so: No such file or directory.
Error while mapping shared library sections:
libmedia.so: No such file or directory.
Error while mapping shared library sections:
libbluedroid.so: No such file or directory.
Error while mapping shared library sections:
libdbus.so: No such file or directory.
Error while mapping shared library sections:
libandroid_runtime.so: No such file or directory.
Error while mapping shared library sections:
libexif.so: No such file or directory.
Error while mapping shared library sections:
libdrm1.so: No such file or directory.
Error while mapping shared library sections:
libvorbisidec.so: No such file or directory.
Error while mapping shared library sections:
libopencore_common.so: No such file or directory.
Error while mapping shared library sections:
libopencore_net_support.so: No such file or directory.
Error while mapping shared library sections:
libopencore_player.so: No such file or directory.
Error while mapping shared library sections:
libomx_sharedlibrary.so: No such file or directory.
Error while mapping shared library sections:
libomx_amrenc_sharedlibrary.so: No such file or directory.
Error while mapping shared library sections:
libstagefright_amrnb_common.so: No such file or directory.
Error while mapping shared library sections:
libstagefright_avc_common.so: No such file or directory.
Error while mapping shared library sections:
libstagefright_color_conversion.so: No such file or directory.
Error while mapping shared library sections:
libstagefright.so: No such file or directory.
Error while mapping shared library sections:
libmedia_jni.so: No such file or directory.
Error while mapping shared library sections:
libstlport.so: No such file or directory.
Error while mapping shared library sections:
libwebcore.so: No such file or directory.
Error while mapping shared library sections:
gralloc.qsd8k.so: No such file or directory.
warning: Unable to find dynamic linker breakpoint function.
GDB will be unable to debug shared library initializers
and track explicitly loaded dynamic code.
warning: shared library handler failed to enable breakpoint
0xafd0ebd8 in ?? ()
(gdb) continue
Continuing.
 

Tap the "Induce Crash" button. gdb should immediately produce the following:


Program received signal SIGSEGV, Segmentation fault.
0x80300bae in Java_com_example_hellogdbserver_HelloGdbServer_invokeCrash (env=0xaa50, clazz=0x4625f0b8)
 at /home/vilimpoc/android-ndk-r4b/samples/hello-gdbserver/jni/hello-gdbserver.c:29
29              *crasher = 0xdeaddead;

(gdb)

Rejoice In No Longer Needing __android_log_print()

Hallelujah! Usable segmentation fault information. Finally, one can get rid of all those debug print statements!

Questions / Feedback / Code Fail?

File them in the comments and I'll update the above. Thanks!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值