使用ndk-gdb调试android native程序
《使用ndk-gdb调试android native程序》
作者: 游蓝海
文章链接: http://blog.csdn.net/you_lan_hai/article/details/50993437
转载请注明出处
虽然Eclipse可以调试android native程序,但是Eclipse启动和监测的线程太多(感觉都上百个了),导致数据同步很慢,还没走几步就卡崩了。
网上也有很多人向google反馈过调试太慢的问题,但是google给的回复是,他们没觉得native调试太慢,是被Eclipse搞慢的,他们建议使用ndk-gdb进行调试。
我以前没用过gdb调试,感觉很低效,比起Xcode的调试功能,android真是太落后了,没有合适的native调试工具。无奈代码在ios上运行正确,在android上就崩了,该面对的还是要面对。遂作此文,记录下调试过程。
我使用的操作系统是OSX,Windows系统上的操作类似,但需要安装Cygwin。
1.配置目标程序
- C++代码必须使用
ndk-build
编译,传入参数NDK_DEBUG=1
。编译完成后,会在lib目录下生成gdbserver,供后续调试使用。 - 设置AndroidManifest.xml,在application项下面设置
android:debuggable="true"
<application
android:debuggable="true">
...
</application>
注意:是application项下面,别写到其他地方去了,我就被坑过。
3. 打包生成apk,然后安装到手机上。
2.启动ndk-gdb
- 在手机上启动上一步骤安装的程序,准备让ndk-gdb挂接。注意:手机需要root,否则ndk-gdb可能没有相关权限启动gdbserver。
- 进入工程目录,执行
$NDK_PATH/ndk-gdb
。ndk-gdb会分析当前目录下的AndroidManifest.xml文件,提取包名,并且判断是否是debuggable模式,然后自动连接包名对应的程序上。
你的工程可能还需要提供参数NDK_MODULE_PATH=your_module_path
。
如果启动失败,设置上参数--verbose
,可以看到更详细的log。
如果log显示ERROR: The device does not support the application's targetted CPU ABIs!
,这是ndk-gdb脚本的bug,提取COMPAT_ABI
失败了,换成python脚本$NDK_PATH/ndk-gdb.py
重新执行。 - 如果启动成功,程序会被中断,在控制台上就可以输入gdb的命令了。
- ndk-gdb的部分参数说明
参数 | 说明 |
---|---|
--verbose |
开启verbose模式,可以看到ndk-gdb输出的详细调试信息。如果gdb总是启动失败,可以加上该参数,查看错误的原因。 |
--force |
强制结束掉已经存在的调试连接。 |
--nowait |
app直接启动,不用等待调试器。默认情况下,app启动之后,会等待调试器挂接(attach)。该参数可与–delay配合使用。 |
--delay=<secs> |
延迟若干秒之后,再挂接调试器。如果希望程序完全运行起来之后再挂接调试器,可以设置上--nowait 参数,并且--delay 设置上希望延迟的时间。 |
--start |
重新启动app用于调试。默认情况下,gdb会挂接到已经启动了的app上。但是,直接挂接已经启动了的app可能会失败。 |
--launch=<name> |
跟--start 功能相似。不同之处在于,--launch 可以指定Android Activity。如果你的app存在多个Activity,该参数就比较合适。某些情况下,--start 会获取到错误的Activity名称,也会导致启动失败。 |
3.使用gdb调试命令
ndk-gdb启动成功后,可能会频繁的出现SIG33中断,这个中断不是异常,使用下面的命令忽略掉就可以了。
(gdb) handle SIG33 nostop
(gdb) handle SIG33 noprint
某些情况下,在启动的时候,会一直报段错误(SIGSEGV),并且没有调用堆栈。此时可以一直使用命令c
跳过,直到执行正常。
常用命令说明
命令名称 | 命令 | 说明 |
---|---|---|
查看调用堆栈 | backtrace | 简写bt。如果程序中断或者崩溃,可以使用该命令查看当前的函数调用堆栈。然后结合frame参数,定位到对应的层次中。 |
跳转到堆栈 | frame frame-number | 简写f。如果想查看某层堆栈的变量,可以用该命令跳转到相应的层。 |
设置断点 | break file:line | file为文件名称,可以不含路径。line为行号 |
列出断点 | info breakpoints | 列出所有的断点,每个断点对应一个编号。可以使用delete命令删除编号对应的断点。 |
删除断点 | delete breakpoint-number | breakpoint-number为断点编号。 |
单步进入 | step | 简写s。逐个命令执行。 |
单步执行 | next | 简写n。逐行执行。 |
跳出当前函数 | finish | |
继续执行 | continue | 简写c。程序恢复执行,直到遇到下个断点或者异常。 |
查看变量 | print variable | 简写p。打印变量值、地址、寄存器等,也可以执行函数,变量赋值等。 |
设置变量 | set var name=value |
更多gdb用法,可以参考文章尾部的链接[2]
参考链接:
- nkd-gdb官网:http://developer.android.com/ndk/guides/ndk-gdb.html
- gdb常用命令:http://elinux.org/GDB