gdb远程调试
gdb远程调试
gdb远程调试功能允许你在一台机器上运行你的程序,而在另外一台机器上使用gdb或者使用IDE来进行调试。可以是在移动平台运行程序,在pc上进行调试。甚至是在docker里面运行程序,在主机上调试。配合IDE,你能够像是在本地运行程序一样的去调试。只要你运行程序的机器上有gdbserver,并且能通过网络连接你的调试的机器,你就可以使用这种方式进行远程调试。
为了方便,这里约定后文中把运行你的程序的机器称为远程机器,把你用来调试的机器称为本地机器。
gdbserver
让远程调试变为可能的工具就是gdbserver。在远程调试中,远程机器上通过gdbserver启动被调试程序,本地机器上启动gdb。gdb通过网络连接gdbserver,控制gdbserver进行调试。
环境依赖
远程机器
只要有gdbserver即可。可以在终端中执行gdbserver --version
来检测gdbserver是否存在。一般linux系统都默认安装有gdbserver,如果没有安装,则需要自行安装。您的远程机器是否支持gdbserver以及怎么安装gdbserver需要查找相关文档。
本地机器
本地机器需要有gdb。注意,本地机器要使用的gdb应该是跟远程机器匹配的。例如,你在系统为ubuntu20.04的PC机上远程调试android设备上运行的程序,你需要的gdb是跟android匹配的gdb,而不是调试ubuntu20.04上的程序使用的gdb。通常在交叉编译工具链中你可以找到对应的gdb工具,本文实验使用的gdb在${ANDROID_NDK}/prebuilt/linux-x86_64/bin/gdb
下面,其中ANDROID_NDK
是安卓交叉编译工具链的路径。
如果你想在IDE中以图形界面的方式调试程序,你需要安装IDE软件。本文会讲述vs code和clion两款IDE工具的配置流程。
网络连接
gdb和gdbserver之间是通过网络来通信的,远程机器和本地机器之前需要有网络连接。本文实验环境是通过adb端口映射的方式来实现网络连接的。
编译工具
你需要相应的编译工具来编译你的程序,这里不再赘述。需要强调的是,你应当编译debug版来调试,编译出来的二进制程序需要包含调试信息。
基本流程
-
将被调试程序上传到远程机器上,通过gdbserver在远程机器上启动程序。gdbserver的使用方式为
gdbserver :port program args
,其中port是端口号,program是你的程序,args是需要传给你的程序的参数。如果你的程序需要设置环境变量,你需要在这之前设置号。端口号是用来跟本地机器上的gdb通信的端口号,可以任意指定。然后gdbserver就会启动,等待本地机器的gdb来连接。
下面是一个示例:
export LD_LIBRARY_PATH=$(pwd)/libs gdbserver :9090 dms --video='./path/of/video/file.mp4'
-
本地机器上启动gdb(注意是跟远程机器环境匹配的gdb,前文环境依赖中亦有说明),然后连接远程gdbserver,就可以像在本地调试程序一样的调试了。gdb调试程序的方式可以查阅相关文档。
下面是一个示例
gdb dms //启动gdb,然后就会进入gdb工具 target remote ip:9090 //在gdb工具中输入这行命令,连接远程机器上的gdbserver set sysroot ./path/of/solib //设置本地机器上的动态库的副本的路径 ... //开启你愉快的调试旅程吧~
在上面这个示例中,第1行启动gdb。这里在gdb后面加上了你的程序,这里的程序是你在本地机器上保留的副本。注意要和远程机器上跑的程序保持一致,否则可能会出错。其实,这里在gdb后面也可以不加上你的程序,直接gdb就可以启动。不过,不在gdb后面加上你的程序的话,gdb会通过与远程的gdbserver通信将你的程序中包含的符号通过网络传输过来,这可能会增加等待的时间。加上你的程序在本地的副本,gdb会从本地副本程序中读取符号,这会减少等待。
第2行是在gdb中输入的命令,它表示通过
ip:9090
这个地址连接远程的gdbserver。这里的9090
是你在第1步启动gdbserver时设置的端口号,ip
是远程机器的ip。本文实验中是用adb forward tcp:9090 tcp:9090
来将本地机器的9090端口映射到远程机器的9090端口,因此输入的命令是target remote localhost:9090
。如果你是通过网络连接远程机器,这里的ip填入远程机器的ip即可。第3行
set sysroot
命令设置本第依赖库的副本的路径。跟第1行中类似,你也可以执行这行命令,但这样gdb会将依赖库中的符号从远程通过网络传输过来,这可能会增加你的等待。然后你就可以开始使用gdb调试了。具体的怎么使用gdb调试,可以参考gdb的文档。
结合IDE
使用IDE来进行调试,体验会比在命令行中直接使用gdb好得多。接下来将会以vs code和clion为例讲述如何配置IDE来实现远程调试。
vs code
vs code通过launch.json文件来配置远程调试。下面直接以一个实际的例子来讲解launch.json配置文件。
{
"version": "0.2.0",
"configurations": [
{
"name": "(gdb on remote) 启动",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/hycan_android29/bin/hycan_front",
"args": [
"--flagfile=./dms.flag"
],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/build/hycan_android29/bin",
"environment": [
{
"name": "LD_LIBRARY_PATH",
"value": "${workspaceFolder}/build/hycan_android29/lib/"
}
],
"externalConsole": true,
"MIMode": "gdb",
"setupCommands": [
{
"description": "为 gdb 启用整齐打印",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "将反汇编风格设置为 Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
},
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"miDebuggerPath": "/home/mini/Downloads/toolchain/android-ndk-r21e/prebuilt/linux-x86_64/bin/gdb",
"miDebuggerServerAddress": "0.0.0.0:9090"
}
]
}
"program"
: 配置本地机器上的可执行文件副本
"args"
: 程序运行时的参数
"cwd"
: 当前目录
"environment"
: 配置环境变量,例如LD_LIBRARY_PATH可以在这里配置
"miDebuggerPath"
: gdb的路径,注意要使用跟远程机器上的环境配套的gdb
"miDebuggerServerAddress"
: 远程机器的ip和端口号,跟上面命令行使用gdb类似
这些就足够了。在远程机器上启动gdbserver,如上面所说。然后在vs code里面启动调试(F5快捷键或点击界面上的调试按钮)就可以了,打断点、单步调试,等等。
clion
创建一个远程调试
Run菜单下点击Edit Configurations,点击左上角的加号,新建一个Remote Debug.
Name
输入框,输入一个合适的名字
debug
输入框选择要使用的gdb,注意要用跟远程机器环境匹配的gdb
"target remote" args
输入框输入远程机器的ip和端口号,同上文。
Symbol file
选择本地机器上保存的待调试程序的副本,因为在linux上符号跟可执行文件是一起的。
Sysroot
设置依赖库的路径,避免gdb从远程机器传输符号。
然后远程机器启动gdbserver,clion界面上点击调试就可以开始调试了。