脚本使用方法
- gdb_native.sh 228 adb1
- gdb_native.sh 228
- 其中228代表要调试进程的pid,必须指定;
- adb1 指定你想使用的adb工具,也可以不指定adb。
#!/bin/bash
PROJECT_ROOT=$(dirname $_)
#set -x
BIN_BIT=64 # BIN_BIT=64 when bin is 64bit version
GDB=$PROJECT_ROOT/prebuilts/gdb/linux-x86/bin/gdb
# GDB=$PROJECT_ROOT/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-gdb
# maybe gdbserver64, if process is 64bit version
GDB_SERVER=gdbserver$BIN_BIT
OUT=$PROJECT_ROOT/out/target/product/flounder
usage () {
echo -e "\033[;35m Usage : $0 228\033[0m"
echo -e "\033[;35m Usage : $0 228 adb1 \033[0m"
}
if [ -n "$2" ] ;then
aadb=$2
else
aadb=/usr/bin/adb
fi
if [ -n "$1" ] ;then
process_pid=$1
else
process_pid=0
fi
ADB=$aadb
GDB_PORT=5028
# 0: setenforce 0
$ADB root
$ADB remount
$ADB shell setenforce 0
sleep 1
if [[ $process_pid = 0 ]]; then
echo -e "\033[;31merror: Process id must designated!!!\033[0m"
usage
exit 0
else
process_name=$($ADB shell ps $process_pid | grep $process_pid | awk -F '\/' '{print $4}')
fi
TARGET_NAME=$process_name # process name
TARGET_PID=$process_pid # process id
GDB_PARAMETER=/tmp/gdbinit.`whoami`
echo -e "\033[;33m check target name($TARGET_NAME) pid($TARGET_PID) \033[0m"
# kill old gdbserver
oldgdbserver=`$ADB shell ps | grep "gdbserver" | awk '{ print $2 }'`
if [[ 0$oldgdbserver != 0 ]]; then
$ADB shell kill -9 $oldgdbserver
fi
# 1: bringup gdbserver and attach to host process for wait clent to connet me
echo "1: $ADB shell $GDB_SERVER :$GDB_PORT --attach $TARGET_PID"
$ADB shell $GDB_SERVER :$GDB_PORT --attach $TARGET_PID &
# 2: 设置端口转发
# 表示通过adb映射tcp端口1234,命令中前面的是local的端口,后面的是remote的端口。
echo "2: $ADB forward tcp:$GDB_PORT tcp:$GDB_PORT"
$ADB forward tcp:$GDB_PORT tcp:$GDB_PORT
sleep 3
# 3: 启动gdb,并传入参数
# echo "target remote:$GDB_PORT" > $GDB_PARAMETER
# echo "file $OUT/system/bin/$TARGET_NAME" >> $GDB_PARAMETER
# echo "set sysroot $OUT/symbols/" >> $GDB_PARAMETER
# echo "set dir $PROJECT_ROOT" >> $GDB_PARAMETER
echo "set solib-absolute-prefix $OUT/symbols/system/lib$BIN_BIT" > $GDB_PARAMETER
echo "set sysroot $OUT/symbols/" >> $GDB_PARAMETER
echo "set solib-search-path $OUT/symbols/system/lib$BIN_BIT/" >> $GDB_PARAMETER
echo "target extended-remote :$GDB_PORT" >> $GDB_PARAMETER
echo "3: $GDB -x $GDB_PARAMETER $OUT/symbols/system/bin/$TARGET_NAME"
$GDB -x $GDB_PARAMETER $OUT/symbols/system/bin/$TARGET_NAME
扩展功能
到这里对于刚接触gdb调试同学也有了入门的知识,对于gdb老手估计是要玩飞的节奏
但是这里要说一下,如果要调试的是framework相关的进程的native代码,可能会受到system server的watchdog的影响,1分钟没有及时响应操作就会触发watchdog而kill到system server进程,zygote也会跟着挂掉,这里有个小技巧可以用一下,就是在调试的过程中,如果需要耗时查看一些运行时状态,可以先执行
adb shell am hang
防止超时重启,查看完毕想要继续执行,就Ctrl+c终止掉am hang即可继续执行,后面就重复这个过程即可。
另外还有一种方式就是用Android Studio在线调试,把断点加在watchdog里面,配置gdb native调试。
gdb命令说明
- 显示gdb命令帮助信息
help 显示gdb命令种类
help subcommand 显示subcommand的帮助信息
apropos word 搜索与word相关的命令
- 显示调试界面
ctrl + X + A
- 设置断点
b(break) 在函数或某一行处设置断点
如果只记得部分函的前缀,可以这样:(gdb) b make_ <按TAB键>。
break filename:linenum 在源文件filename的linenum行处停住
break filename:function 在源文件filename的function函数的入口处停住
break *address 在程序运行的内存地址处停住
break break命令没有参数时,指在下一条指令处停住
break … If … 在条件成立时程序停住
break thread threadnum 定义在线程threadnum上的断点,如break BootAnimation.cpp:364 thread 28 if bartab > lim
- 设置观察点
watch expr 设置观察点,当表达式expr的值变化时,程序停住
rwatch expr 设置观察点,当表达式expr的值被读时,程序停住
awatch expr 设置观察点,当表达式expr的值被读或写时,程序停住
- 设置捕捉点
catch 设置被调试程序捕捉点,当event发生时,程序停住
catch catch [args] 捕捉一个C++捕捉到的异常
catch throw [args] 捕捉一个C++抛出的异常
catch syscall [args] 捕捉系统调用
- 维护被调试程序断点
condition N COND 修改断点号为N的停止条件为COND
ignore N COUNT 忽略断点号为N的停止条件COUNT次
clear [linenum|funname|*] 清除指定的行或函数断点
d(delete) [breakpoints][range] 清除指定的断点
disable [breakpoints] [range…] 禁用指定的断点
enable [breakpoints] [range…] 启用指定的断点
- 为停止点设定运行命令
commands N 调试程序在断点号为N的断点处停止后,执行命令
执行命令
end
- 显示被调试程序的信息
info subcommand 显示被调试程序的某些信息,可用help info查看子命令。如:
info breakpoints [n] 显示所有断点(或断点n)信息
info watchpoints [n] 显示所有观察点(或观察点n)信息
info program 查看被调试程序的执行状态
info args 打印出当前函数的参数名及其值
info locals 打印出当前函数中所有局部变量及其值
info display 查看display设置的自动显示的信息
info frame 查看栈帧信息,包括程序语言
- 运行及查看被调试信息
r(run) 运行被调试程序
c(continue) 从断点出开始继续执行直到结束或下一个断点
s(step) 单步跟踪,如果有函数调用,他会进入该函数。进入函数的前提是此函数被编译有debug信息
n(next) 单步跟踪,如果有函数调用,他不会进入该函数
p(print) 打印表达式的值
可以查看全局变量(所有文件可见)、静态全局变量(当前文件可见)、局部变量(当前Scope可见)
p file::var 查看文件file中的变量var
p func::var 查看函数file中的变量var
p start@len 查看一段连续的内存空间的值,“@”的左边是第一个内存的地址的值,“@”的右边则你你想查看内存的长度,如:
int array = (int ) malloc (len * sizeof (int));
p *array@len
p/format expr 按指定的格式显示表达式expr(format:x, d, f, c, u, o, t, a)
display[/fmt] expr 程序停下来后就会显示expr的值
undisplay/delete displaynum/disable displaynum/enable displaynum
bt 查看当前函数调用堆栈信息
f(frame) 查看当前栈层信息,包括栈的层编号、当前的函数名、函数参数值、函数所在文件及行号和函数执行到的语句
finish 运行程序,直到当前函数完成返回
u(until) 运行程序直到退出循环体
- 显示源代码
l(list) 列出具体的函数或代码行
list 显示当前行后面的源程序
list - 显示当前行前面的源程序
list + 显示当前行后面的源程序
list linenum 显示程序第linenum行的周围的源程序
list file:filenum 显示文件file中的filenum行
list file:func 显示文件file中的函数func
list funcname 显示函数名为function的函数的源程序,如list android::BootAnimation::movie
- 搜索源代码
forward-search reg 利用正则表达式前向搜索源码
search reg 利用正则表达式前向搜索源码
reverse-search reg 利用正则表达式在全部源码中进行搜索
- 设置被调试程序参数/gdb配置
set subcommand 设置gdb环境变量,可以使用help set查看set子命令。如:
set args 设置被调试程序运行参数
set directories 设置源文件搜索路径,多个使用“:”分割
set solib-search-path 设置符号表搜索路径
set environment varname [=value] 设置环境变量。如:set env USER=llj
set listsize num 设置一次显示源代码的行数
set step-mode on 打开step-mode模式,于是,在进行单步跟踪时,程序不会因为没有debug信息而不停住。这个参数有很利于查看机器码
set print address on 打开地址输出, 当程序显示函数信息时,GDB会显出函数的参数地址
set print array on 打开数组显示,打开后当数组显示时,每个元素占一行,如果不打开的话,每个元素则以逗号分隔
set print elements num 设置显示数组元素的最大个数
set print null-stop on/off 如果打开了这个选项,那么当显示字符串时,遇到结束符则停止显示
set print pretty on/off 打开后gdb显示结构体时会比较漂亮
set print sevenbit-strings 设置字符显示,是否按“/nnn”的格式显示
set print union on/off 设置显示结构体时,是否显式其内的联合体数据
show subcommand 显示调试器的信息,使用help show查看子命令。如:
show environment [varname] 查看环境变量
show listsize 显示当前listsize的设置
show directories 显示定义了的源文件搜索路径
show language 查看gdb当前的语言环境
- 其他
q(quit) 退出gdb
handle 处理信号
- shell命令
此外gdb中可以执行shell命令,使用SHELL环境变量定义的可执行程序来执行shell命令,常用的命令如下:
show environment SHELL 查看shell执行程序
make 重新编译程序,相当于shell make
cd 必变工作目录,相当于shell cd
pwd 查看当前工具目录,相当于shell pwd
-
调整程序线路
一旦使用GDB挂上被调试程序,当程序运行起来后,你可以根据自己的调试思路来动态地在GDB中更改当前被调试程序的运行线路或是其变量的值,这个强大的功能能够让你更好的调试你的程序,比如,你可以在程序的一次运行中走遍程序的所有分支。
修改变量的值
print x = 4 C/C++语法,把变量x的值修改为4
set var width=10 用gdb命令将参数width值修改为10
- 跳转执行
jump +num 当前运行点向下偏移num行开始执行
jump linenum 从当前调试文件的linenum行开始执行
jump file:linenum 从file的linenum行开始执行
-产生信号量
singal SIGNAL 发送信号SINGAL给被调试程序
-强制函数返回
return 强制函数返回,忽略未执行的语句
return result 强制函数返回结果result,忽略未执行的语句
-强制调用函数
call func 调用当前程序中的函数
print func 调用当前程序中的函数