1. 背景
- 为什么会弄这玩意?因为要弄这个热更工具,gdb是绕不过的坎。为什么要弄这个工具,因为我们一直以来客户端世界里都没有lua(Miniserver的不算,那就是跑在客户端的服务器,通过网络层和客户端通信),用ulua之类的大家都得学,多个东西多个鬼,能少则少。
- 理论上,ndk提供了gdb的封装脚本ndk-gdb,理论上能方便无缝地像在linux server一样远程连gdbserver。但因为gdb所在环境和手机环境的多样性,这个脚本基本跑不通。而且这个脚本不稳定,也在随版本号升级,你可以想象把脚本过一遍,还原出该设的环境变量,一升级,日了狗了,又不行了,还得再过一遍。×#&%¥!,还是把里面的东西扒出来,直接用gdb+gdbserver。但即使是直接用gdb+gdbserver,各个命令在不同环境的表现还是有差异,例如原来能用的adb shell的后台命令更新后就一直占着前台。
2. 调试
以下脚本均为bash脚本,在git bash下执行。gdb调试android的动态库,本质和gdb远程调试Linux程序一样:远端启动gdbserver attach上要调试的应用,同时开放监听端口,然后gdb远程连上去调试。所以其涉及远端(Android)和本机两个系统。
2.1. 准备环境变量
1 | export PACKAGE_NAME=com.test.test |
就这几个,因人而异,自己改,都很简单,不多做解释了,下面的命令会用到。
我用的是模拟器,规避权限问题,所以用的是x86版本的。
2.2. 准备远端环境
用Debug版本的动态库构建App,安装App,App本身是不是debug版本无所谓(App的这个debug版本是给java调试用的,如果是在没有root的真机上,需要开App的Debug,用run-as切到app对应userid,才能attach)。其实用Release版本的动态库也可以调,只是所有调试信息都没了,只能用public的符号或绝对地址下断点,想看参数得直接用栈指针,像下面这样。Ok,这么说,读者应该知道怎么灵活选择了,你想调的那个库是Debug版就行了。
1 | b __android_log_print |
2.3. 准备本机环境
- 得保证能连上模拟器,有adb。
- 本机环境我用的是Windows + git bash,如果你用其他环境,既然你都用了,我相信你能处理好。
- 建个空目录,在里面启动git bash,执行以下脚本,而且每次App内动态库有变化,都要执行一次。
1 | # 把端口映射打开,adb负责把本机的5039端口,映射到远端Android的5039端口 |
2.4. 先封一些函数,方便使用
- 启动app前,上次调试环境可能还在,下面是清理的gdbserver的函数
1 | function kill_gdbserver(){ |
- 启动app。启动前清理可能的gdbserver和可能的残留app,启动命令自己按需修改
1 | function start_app(){ |
- 计算各个库的代码段加载地址,因为gdb只能从本地环境加载符号。理论上每次启动,都需要计算一次,但是只要App没变化,动态库在App的内存空间中的地址不变,所以,在App第一次启动的时候做一次就行。计算后会加载库的命令给准备好,追加到gdb.setup中。
1 | function map_app(){ |
- 在远端启动gdbserver,attach app
1 | function attach_app(){ |
2.5. 调
建议开三个终端(git bash),全都定义好变量和封装的函数。
一个跑
1 | start_app |
一个接着跑
1 | attach_app |
最后一个跑
1 | map_app #这条命令比较慢,仅第一次启动的时候跑就好了 |
然后,读者就可以放飞自我了。