深入解析:windows+Ubuntu24+Arm+gdb+gdbserver+VsCode 嵌入式开发一键可视化调试


前言

首先描述下需求,产品是arm架构,需要在linux系统上交叉编译,日常办公又需要windows上各种软件。

目前的软件开发流程是:windows上编辑代码–>虚拟机交叉编译–>sftp放入目标设备–>调试查看打印–>windows上编辑代码,过程繁琐且低效率

目的是简化流程,实现像使用VS开发win应用一样可以直接启动调试并查看变量、堆栈信息等

核心实现方式是gdb+gdbserver,只是借助vscode简化操作,且能可视化调试

整体实现方式分以下几个部分:

1.gdb和gdbserver构建并连接
2.windws上vscode远程连接ubuntu
3.vscode配置launch和task

开发机:windows11 192.168.3.80 用于代码编辑
虚拟机:ubuntu24 192.168.3.87 用于交叉编译、连接gdb
目标机:rk3568 192.168.3.193 待开发设备

一、gdb和gdbserver构建连接

gdb
linux上的命令行调试工具,能够打断点、查看变量、运行栈信息,极大提高开发效率,且能调试程序崩溃时的core文件。功能强大,但是是命令行界面,对新手较不友好
gdbserver
由于gdb功能强大,因此在内存和存储空间有限的目标设备上,直接运行完整的gdb可能很困难,况且gdb的断点调试等需要结合源码。gdbserver应运而生,体积小、资源占用少,将其运行在目标机上,再在开发机或服务器上运行,可以实现类似gdb直接运行在目标机上的效果,且能实现跨平台调试,比如目标机是arm架构,开发机是x86架构
对于交叉编译的程序,需要先构建对应架构的gdb和gbdserver,并保证二者的版本一致,或者如果有sdk,可以直接使用buildroot提供的gdb及gdbserver

1 编译gdb及gdbserver

由于我使用的sdk提供的已经编译好的gdb及gbdserver,此处仅使用我之前编译过的gdb举例说明。

官网下载gdb源码:一般保持与gcc版本一致或者高于gcc版本都可以,我这里下载的是gdb-11.2.tar.gz,下载到了opt下,之前使用的交叉编译器是arm-linux-gnueabihf-gcc

下载后,查看README发现里面有提到gdb和gdbserver可以同时编译,但是测试发现无法同时编译,不知为何,而且我们编译后得到的gdb需要运行在虚拟机ubuntu上,而gbdserver需要运行在目标机arm上,因此分开编译。

编译时可以直接在/gdb-11.2下进行,或在/gdb-11.2下新建文件夹进行,推荐在/gdb-11.2下新建文件夹进行,这样可以分别编译应用于不同架构的gdb,无论使用哪种方式,需要执行的configure都是/gdb-11.2下的。

先在/opt下建立gdbtest和gdbstest,分别存放编译得到的gdb及gdbserver

编译gdb:

cd /opt
mkdir gdbtest
tar -xzvf /opt/gdb-11.2.tar.gz
cd gdb-11.2
mkdir gdbarm
cd gdbarm
../configure --target=arm-linux-gnueabihf --prefix=/opt/gdbtest

–prefix是编译得到gdb的存放路径
–build是当前编译gdb的系统的系统架构,会自己检测
–host是gdb要运行于的系统机构,缺省会与–build一致
–target是gdb要调试的目标程序的架构,缺省会与–host一致

等待configure完成后会在当前目录下生成makefile等,再执行

make -j4 && make install

如果configure及make过程中出现错误,会报错,注意终端打印。

编译gdbserver:

gdbserver的编译基本流程与gdb类似,只是需要在gdbserver下,且由于gdbserver运行在arm上,configure时需要指定host为arm-linux-gnueabihf。因为上一步已完成gdb的编译,所以不用再重新解压和编译gdb,需要注意configure仍然是顶层目录/gdb-11.2下的。

cd /opt
mkdir gdbstest
cd gdb-11.2/gdbserver
mkdir gdbsarm
cd gdbsarm
#指定交叉编译链
export CC=arm-linux-gnueabihf-gcc
../../configure --host=arm-linux-gnueabihf --prefix=/opt/gdbstest --disable-gdb
#等待配置完成后
make all-gdbserver
make install

都编译完成后,在/opt/gdbtest/bin 及 /opt/gdbstest/bin 下分别查看:
在这里插入图片描述
在这里插入图片描述

同时查看gbdserver的文件格式:
在这里插入图片描述
编译成功,将gdbserver放到目标机上,准备下一步

应用程序的交叉编译不必多说,注意编译时加上-g选项

2 gdb及gdbserver 使用

在得到gdb及gdbserver使用后,先脱离vscode进行连接验证。

gbdserver与gdb的连接可以使用串口,也可以使用网络,网络方式时还需要启用一个端口,因为需要传文件到目标机,所以本文使用网络方式

在目标机上运行gdbserver,gdbserver有3种模式,分别是:

标准模式gdbserver :2345 hello [arg1] (arg1为程序运行时需要传入的参数,可不带)
附加模式gdbserver --attach :2345 pid (pid为运行的进程号)
多进程模式gdbserver --multi :2345

为了方便直接使用第三种,关联的进程号可在gdb侧进行设置

串口方式的启动:gdbserver /dev/ttyS0 /path/to/my_program

gdbserver设置好之后,在虚拟机上启动gdb,进入gdb模式

根据gdbserver启动方式的不同略有区分:

#连接标准模式或附加模式时: (gdb) target remote 192.168.3.193:2345
#连接多进程模式时:(gdb) target extended-remote 192.168.3.193:2345

出现Remote debugging using 192.168.3.193:2345类似打印时说明连接成功
在这里插入图片描述
连接成功以后可以输入quit退出了,开始进行vscode的配置

二、windows上vscode远程连接ubuntu

由于日常办公无法脱离windows,而代码的交叉编译与调试又必须在ubuntu/linux上,因此一种较为方便的方式就是使用vsocde的扩展remote ssh远程连接ubuntu后,实现在windows上通过vscode对Ubuntu上的代码进行开发调试,同时又能方便切换到其他windows办公软件,如果有条件能直接在ubuntu上进行办公或代码开发,可跳过此步骤直接进入三。

1 初次连接

首先本机安装vscode openssh vscode中的remote ssh扩展等

  • 打开vscode ctrl+shift+p 输入ssh,选择连接到主机
    在这里插入图片描述

  • 配置ssh主机
    在这里插入图片描述

  • 选择config文件,如果没生成可在对应路径手动配置
    在这里插入图片描述

  • 新建一个节点,Host是为这个连接起名,后续可以使用test87代替[email protected],如果不是默认端口可以自行添加Port参数
    在这里插入图片描述

  • 配置完成后保存,再次ctrl+shift+P 输入ssh,连接到主机,选择刚刚建立的节点
    在这里插入图片描述

  • 选择linux
    在这里插入图片描述

  • 输入密码,等待在远程服务器上安装环境
    在这里插入图片描述

  • 连接成功后可以在左侧看到连接状态
    在这里插入图片描述

2 配置免密连接

上述连接成功后,每次重新打开vscode后仍然需要重新输入密码,需要配置ssh key来实现免密登录

windows 控制台下输入ssh-keygen -f C:\Users\用户名\.ssh\id_ed25519_test87,一直按回车。
-f 参数是指定生成的密钥存放路径以及文件名,放到与上述config文件同目录下
(如果提示没有命令需要先安装openssh)
在这里插入图片描述

在C:\User\用户名.ssh下会生成一组密钥文件
在这里插入图片描述

记事本打开.pub文件,复制里面的内容粘贴到虚拟机/home/用户名/.ssh/authorized_keys中,如果没有此文件或目录,先手动自己创建

打开vscode,找到刚才编辑过的config文件,test87连接节点下新建一个参数IdentityFile ,值即为带路径的密钥文件名IdentityFile ~/.ssh/id_ed25519_test87
在这里插入图片描述

此时在控制台直接输入ssh test87,就可以直接免密码登录虚拟机了

另外在配置过程中碰到了两个问题,可以参考

1.虚拟机未开启密钥登录

如果还是需要输入密码,可能是虚拟机上未开启密钥登录,修改sshd_config,sudo vi /etc/ssh/sshd_config 将下图两处放开
放开这两行配置

重启ssh服务

sudo systemctl restart sshd #重启ssh服务

2.authoried_keys权限问题

开启ssh服务后尝试还是不行,查找问题,发现是authoried_keys文件的问题,在虚拟机.ssh/目录下ll查看:
在这里插入图片描述

发现authorized_keys的权限和所有者、所属组都不对,权限设置为600,所属组设置为用户所在的组

cd ~/.ssh
sudo chown vmuser:vmuser ~/.ssh/authorized_keys
sudo chmod 600 authorized_keys

上述两个问题修改后,windows上再次尝试ssh test87,成功免密登录

vscode打开的工作文件夹,可以将项目源码直接放在ubuntu上,也可以使用windows本地文件挂载到虚拟机方法同步到ubuntu上,这样实际修改的还是windows本地的文件,由于本人项目使用了svn进行管理,所以使用的后者。

三、vscode配置launch和task

经过前两步后,已经具备调试基础,本节主要使用vscode的task.json,launch.json来配置自动化操作流程,实现一键调试。

vscode成功连接ubuntu后,打开ubuntu上的工作文件夹,左侧文件列表里.vscode下会有launch.json和task.json两个文件,如果没有,可以手动新建。

在这里插入图片描述

launch.json主要是调试配置
task.json主要是自动化任务配置

1.launch.json设置

launch.json主要有两种配置模式,根据其中的"request"来区分,二者分别针对于不同的应用场景,其配置略有区分:

**“request”: “launch”**为启动模式,目标机上的待调试应用程序由虚拟机的gdb负责启动

**“request”: “attach”**为附加模式,适用于目标机上的应用程序已经启动,或者不便于通过gdb单独启动的情况

以进程hello为例

launch模式

{
"version": "0.2.0",
"configurations": [
{
"name": "hello_launch",  // 为该项调试起名,配置完成后可以在debug界面下拉选择此项
// "preLaunchTask": "hello", // 预启动任务,配合task使用,后续详细说明
"useExtendedRemote": true, // 与gdbserver的多进程模式配合
"type": "cppdbg",
"request": "launch", // 调试模式
"cwd": "${workspaceFolder}",
"args": ["6", "1572912"], // 程序启动时传入的参数,没有则不填
"program": "${workspaceFolder}/bin/hello", // 要调试的应用程序在虚拟机ubuntu上的位置
"MIMode": "gdb",
"miDebuggerPath": "/opt/ok3568/host/bin/aarch64-buildroot-linux-gnu-gdb", // 编译gdb后得到的gdb程序路径
"miDebuggerServerAddress": "192.168.3.193:2345",// 目标机ip地址与端口,与gdbserver开启的端口号需一致
"logging": {
"trace": true,
"traceResponse": true,
"engineLogging": true
}
}
]
}

attach模式

{
"version": "0.2.0",
"configurations": [
{
"name": "hello_attach",
"useExtendedRemote": true,
"type": "cppdbg",
"request": "attach",// 模式改为attach,且cwd和args参数不能存在
"program": "${workspaceFolder}/bin/hello",
"processId": "3773",//目标机上应用程序的进程号
// "processId": "${command:pickProcess}",
// "processId": "${input:selectProcess}",
"MIMode": "gdb",
"miDebuggerPath": "/opt/ok3568/host/bin/aarch64-buildroot-linux-gnu-gdb",
"miDebuggerServerAddress": "192.168.3.193:2345",
"logging": {
"trace": true,
"traceResponse": true,
"engineLogging": true
}
}
]
}

与lanunch模式相比,attach模式下cwd参数和args参数需要删除,且多了processId的参数,该参数为要调试的应用程序在目标机上的进程号,比如"processId": "3773"。除这种模式外,还有另外两种模式可以选择,分别是:

"processId": "${command:pickProcess}",这种设置下,F5启动调试后会先让你选择某个进程,按照文档说明,这里选择的应该是目标机上的某个进程才对,但是实测发现,提供选择的总是虚拟机上的某个进程,不清楚怎么回事,有懂得小伙伴也可以指教下。

"processId": "${input:selectProcess}",这种设置需要配合三个扩展进行使用,分别是Command VariableShell CommandTasks Shell Input,注意需要安装在虚拟机服务器端,安装后,就能在launch和task两个json中使用一些扩展命令。比如这里设置为${input:selectProcess}后,在launch中新增inputs块,与configurations块并列,id是为该项操作任意命名,在input:后使用,type设置为command表示使用shell命令,args表示执行的具体命令内容,这里我的命令是自动获取目标机上名为hello进程的进程号并返回,它将返回目标机上的进程号并提供给proessId,就相当于上文中的3773,这样就不用再去到目标机上ps获取进程号了。

"inputs": [
{
"id": "selectProcess",
"type": "command",
"command": "shellCommand.execute",
"args": {
"command": "sshpass -p 123456 ssh -o StrictHostKeyChecking=no [email protected] 'ps -eo pid,stat,comm | grep hello| grep -v defunct | head -1 | awk \"{print \\$1}\"'",
"useFirstResult": true
}
}
]

完整的launch.json如下所示:

{
"version": "0.2.0",
"configurations": [
{
"name": "hello_launch",
//"preLaunchTask": "hello",//先行任务,配合task使用,后续详细说明
"useExtendedRemote": true,
"type": "cppdbg",
"request": "launch",
"cwd": "${workspaceFolder}",
"args": ["6", "1572912"],
"program": "${workspaceFolder}/bin/hello",
"MIMode": "gdb",
"miDebuggerPath": "/opt/ok3568/host/bin/aarch64-buildroot-linux-gnu-gdb",
"miDebuggerServerAddress": "192.168.3.193:2345",
"logging": {
"trace": true,
"traceResponse": true,
"engineLogging": true
}
},
{
"name": "hello_attach",
"useExtendedRemote": true,
"type": "cppdbg",
"request": "attach",
"program": "${workspaceFolder}/bin/hello",
"processId": "3773",
// "processId": "${input:selectProcess}",
// "processId": "${command:pickProcess}",
"MIMode": "gdb",
"miDebuggerPath": "/opt/ok3568/host/bin/aarch64-buildroot-linux-gnu-gdb",
"miDebuggerServerAddress": "192.168.3.193:2345",
"logging": {
"trace": true,
"traceResponse": true,
"engineLogging": true
}
}
],
"inputs": [
{
"id": "selectProcess",
"type": "command",
"command": "shellCommand.execute",
"args": {
"command": "sshpass -p 123456 ssh -o StrictHostKeyChecking=no [email protected] 'ps -eo pid,stat,comm | grep hello| grep -v defunct | head -1 | awk \"{print \\$1}\"'",
"useFirstResult": true
}
}
]
}

配置完成后,ctrl+shift+D进入调试界面,下拉箭头选择刚才配置的name,点击绿色三角
在这里插入图片描述

以launch为例,不出意外的话会成功连接上,可以在左侧查看变量值,代码中可直接添加断点调试,大功告成!
在这里插入图片描述

2.task.json设置

解决gdb可视化调试后,现在还剩一个步骤较为繁琐,那就是应用程序源码需要随时修改,修改后交叉编译,并且每次编译完成后都需要通过sftp手动传入,并在目标机终端上运行,还是不够方便。

核心思想是使用vscode的task.json配合sshpass、ssh等命令实现虚拟机交叉编译–>sftp放入目标设备这一步骤的自动化操作。

直接看task.json配置文件

{
"version": "2.0.0",
"tasks": [
{
"label": "make_hello",
"type": "shell",
"command": "make",
"args": ["debug"],
"options": {
"cwd": "${workspaceFolder}/syncomm"
},
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": ["$gcc"],
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "shared"
}
},
{
"label": "kill_hello",
"type": "shell",
"command": "sshpass",
"args": [
"-p",
"123456",
"ssh",
"[email protected]",
"killall -9 hello 2>/dev/null || true"
],
"presentation": {
"reveal": "silent"
},
"problemMatcher": []
},
{
"label": "up_hello",
"type": "shell",
"command": "sshpass",
"args": [
"-p",
"123456",
"scp",
"${workspaceFolder}/bin/hello", // 本地编译输出的程序路径
"[email protected]:/opt/" // 目标板上的路径
],
"presentation": {
"reveal": "silent"
},
"problemMatcher": []
},
{
"label": "hello",
"dependsOn": [
"make_hello",
"kill_hello",
"up_hello"
],
"dependsOrder": "sequence", // 确保任务按顺序一个接一个执行
"group": "build",
"problemMatcher": []
}
]
}

在task.json中我创建了四个任务块,使用label来区分,他们的作用分别是:

make_hello:重新交叉编译hello程序
kill_hello:杀死目标机上正在运行的hello
up_hello:上传新编译后的hello到目标机
hello:按照设定顺序执行make_hello、kill_hello、up_hello三个任务

配置完成后,按F6,输入hello,选择刚才建立的hello任务,回车执行,会在终端打印执行过程和执行结果
在这里插入图片描述

task配置完成后,在launch.json中放开"preLaunchTask": "hello",这一行配置,直接按F5,即可一键完成交叉编译、上传文件、gdb启动程序并进入调试。

总结

以上就是嵌入式开发过程中一键启动可视化调试的全部流程,有问题欢迎探讨。

posted on 2025-12-15 20:38  ljbguanli  阅读(0)  评论(0)    收藏  举报