做NDK开发,经常会用到ndk-gdb,但ndk-gdb的启动规则比较严格,甚至需要检查工程文件,在一些手机上往往无法启动;所以,研究了一下ndk-gdb的代码,实现了一个简单的版本,基本的原理如下:
1. 首先进入android工程目录,该目录下包含了jni, obj, bin工程相关的子目录;
2. 使用awk找到package进程对应的PID(具体实现参考后面的代码)
如果PID为0, 说明程序没有运行,需要强制启动
adb shell am start -n $package/$ActivityName
如:adb shell am start -n com.xxx.yyy/com.xxx.main.xxxx
其中,ActivityName 可以在android的工程配置文件 AndroidManefest.xml中找到;
强制启动后,再通过awk找到$package进程对应的PID;
3. 通过run-as命令在非root权限下,启动package中的gdbserver, 并且attach到$PID进程。
adb shell run-as $packagelib/gdbserver +debug-socket --attach $PID
4. 端口映射, 将本地端口5039映射到Android上的域套接字对应的端口
adb forward tcp:5039 localfilesystem:/data/data/$package/debug-socket
5. Pull调试相关的files到obj/local/armeabi目录
包括:/system/bin/app_process,/system/bin/linker, /system/lib/libc.so
6. 将GDB启动后需要进行的操作添加到obj/local/armeabi/gdb.setup文件
其中gdb.setup是在编译阶段生成的(android-ndk-r8d/build/core/setup-toolchain.mk),gdb.setup默认有两个命令:
set solib-search-path./obj/local/armeabi
directory src-dir1src-dir2 ... src-dirN
需要自己添加的操作包括:
file./obj/local/armeabi/app_process
target remove :5039
set print object on
可以根据需要,再添加其他命令,比如启动的是gdbtui,可以添加:
winheight src 25
focus cmd
7. 以gdb.setup为配置文件启动gdb
arm-linux-androideabi-gdb/gdbtui-x ./obj/local/armeabi/gdb.setup
以下是一个例子,其中Activity可以在工程配置文件AndroidManifest.xml文件中找到,改成对应的名字即可:
#!/bin/sh
#Author: Kyle(justbitguy@gmail.com)
#Date:2013-10-12
VERSION="4.0"
pid_for_package ()
{
pack_name=$1
_pid=`adb shell ps | awk -v PACKAGE="$pack_name" '
BEGIN {
FS=" "
if (PACKAGE == "") {
PACKAGE="com.google.android.apps.maps"
}
PID=0
PID_COLUMN=2
}
{
gsub("\r","",$NF)
if (NR == 1) {
for (n = 1; n <= NF; n++) {
if ($n == "PID") {
PID_COLUMN=n;
}
}
} else {
if ($NF == PACKAGE) {
PID=$PID_COLUMN
}
}
}
END {
print PID
}
'`
echo $_pid
}
#start of this script
###########################################
USAGE="usage: `basename $0` package"
if [ $# -lt 1 ]; then
echo $USAGE
exit
fi
package=$1
if [ -z $package ]; then
echo $USAGE
exit 13
fi
ARM_TYPE=armeabi
APP_OUT=./obj/local/$ARM_TYPE
APP_PROCESS=$APP_OUT/app_process
DEBUGGER=`basename $0`
DEBUGGER_PATH=`which $DEBUGGER`
DEBUGGER_DIR=`dirname $DEBUGGER_PATH`
echo "debugger dir is $DEBUGGER_DIR"
GDBSERVER_PID=`pid_for_package lib/gdbserver`
echo "gdbserver pid is $GDBSERVER_PID"
if [ "$GDBSERVER_PID" != "0" ]; then
adb shell kill -9 $GDBSERVER_PID
fi
PID=`pid_for_package $package`
if [ "$PID" -eq "0" ]; then
## COMMAND: adb_cmd shell am start -n $package/$Activity
#Starting: Intent { cmp=$package/$Activity}
## COMMAND: adb_cmd shell sleep 2
adb shell am start -n $package/$Activity
adb shell sleep 5
PID=`pid_for_package $package`
fi
echo "$packge pid is $PID"
adb forward tcp:5039 localfilesystem:/data/data/$package/debug-socket
echo adb shell run-as $package lib/gdbserver +debug-socket --attach $PID
adb shell run-as $package lib/gdbserver +debug-socket --attach $PID > ~/.gdb.log &
#pull files from devices
#####################################
adb pull /system/bin/app_process $APP_OUT/app_process
echo "Pulled app_process from device/emulator."
adb pull /system/bin/linker $APP_OUT/linker
echo "Pulled linker form device/emulator."
adb pull /system/lib/libc.so $APP_OUT/libc.so
echo "Pulled libc.so from device/emulator."
#start gdb client
GDB_PREFIX=arm-linux-androideabi-
GDBCMD=${GDB_PREFIX}gdb
GDBSETUP=$APP_OUT/gdb.setup
cp -f ./libs/$ARM_TYPE/gdb.setup $GDBSETUP -v
echo "file $APP_PROCESS" >> $GDBSETUP
echo "target remote :5039" >> $GDBSETUP
echo "set print object on" >> $GDBSETUP
GDBCLIENT=$GDBCMD
$GDBCLIENT -x $GDBSETUP