gdb是GNU开发的针对Linux/Unix环境下程序的调试工具。为了节约目标系统的资源,gdb通常采用gdb+gdbserver的方式进行调试。
在Android GDB调试场景下,gdb运行在PC端,gdbserver运行在Android系统中。在实际的调试过程中,PC端的gdb参照调试符号文件向gdbserver发出命令,gdbserver就会向运行程序发出信号,从而实现对Android系统运行程序的跟踪与控制:
gdb
gdb运行在PC端,通常位于Android源码中的prebuilt目录下。不同的项目,不同的平台,gdb的位置可能不同,但是我们可以在进入prebuilt目录下通过find命令来查找:find ./ -name “gdb“,常见路径列举如下:
prebuilts/gdb/linux-x86/bin/gdb
prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin/aarch64-linux-android-gdb
gdbserver
gdbserver运行在Android系统中,默认已经安装在eng或者userdebug的软件版本中;根据被调试程序的运行位数(32位/64位)的不同,gdbserver需要选择相匹配的版本gdbserver/gdbserver64。
注意:如果手机中找不到gdbserver,我们可以将源码prebuilt目录下的gdbserver安装到手机中
带有调试符号信息的文件
要调试C/C++程序,必须要有调试符号信息。按照Android源码的编译规则,调试符号信息通常位于在目录out/target/product/[PRODUCT_NAME]/sysmbols下;为了能够在调试过程中获取到更多的调试信息,建议对源码进行全编,并在调试目标的Android.mk中关闭编译优化:
LOCAL_CFLAGS += -O0 -g // -O0:优化级别为0, -g:打开调试
以mediaserver进程为例;
adb shell gdbserver :8888 --attach 1232
注意:
a. 使用gdbserver,还是使用gdbserver64,需要依据进程的位数来决定;进程的位数可以通过file命令查看:
adb shell file system/bin/mediaserver
b. 1232为进程id,可以ps命令查看;
adb shell ps -ef | grep mediaserver
c. 如果不绑定进程id,则gdbserver会重新创建一个新的进程:
adb shell gdbserver :8888 system/bin/mediaserver
创建的mediaserver相比于系统的启动方法,会丢失一些group,selinux context信息。因此如果系统已经启动了调试的进程,可以直接绑定到指定PID上;否则才可以考虑通过gdbserver创建新的进程;
adb forward tcp:8888 tcp:8888
prebuilts/gdb/linux-x86/bin/gdb out/target/product/[PRODUCT_NAME]/symbols/system/bin/mediaserver
(gdb) set solib-absolute-prefix out/target/product/[PRODUCT_NAME]/symbols/
(gdb) set solib-search-path out/target/product/[PRODUCT_NAME]/symbols/
(gdb)target remote :8888
(gdb) info thread: 打印当前进程所有的thread信息
(gdb) info breakpoints [n] 显示所有断点(或断点n)信息
(gdb) info watchpoints [n] 显示所有观察点(或观察点n)信息
(gdb) info program 查看被调试程序的执行状态
(gdb) info args 打印出当前函数的参数名及其值
(gdb) info locals 打印出当前函数中所有局部变量及其值
(gdb) info display 查看display设置的自动显示的信息
(gdb) info frame 查看栈帧信息,包括程序语言
(gdb) print a 打印变量a的值
(gdb) print &a 打印变量a的地址
(gdb) thread: 打印当前gdbserver所跟踪的thread信息
(gdb) thread [Id]: 切换到指定Id的thread
(gdb) bt full 显示当前的堆栈信息
(gdb) ctrl + x + a (full) : 相当强大的功能,将会在当前窗口创建一个小的窗口用于显示源码
(gdb) up/down: 依据调用堆栈显示上一级/下一级调用的源码
(gdb) c 运行程序直至断点/观察点处停住
(gdb) n 单步执行
(gdb) quit 退出执行
(gdb) b(reak) filename:linenumber 运行到文件指定行停住;不需要指定文件的路径
(gdb) b(reak) filename:function 运行到文件指定函数入口停住;不需要指定文件的路径
(gdb) watch *(long *)0x7777777 观察long类型变量,当变量变化时,程序停住
(gdb) rwatch *(long *)0x7777777 观察long类型变量,当变量的值被读时,程序停住
(gdb) awatch *(long *)0x7777777 设置观察点,当变量的值被读或写时,程序停住
(gdb) print x = 4 C/C++语法,把变量x的值修改为4
(gdb) jump +num 当前运行点向下偏移num行开始执行
(gdb) jump linenum 从当前调试文件的linenum行开始执行
(gdb) jump file:linenum 从file的linenum行开始执行
(gdb) singal SIGNAL 发送信号SINGAL给被调试程序
(gdb) return 强制函数返回,忽略未执行的语句
(gdb) return result 强制函数返回结果result,忽略未执行的语句
(gdb) call func 调用当前程序中的函数