GDB之(8)GDB-Server远程调试
Author:Once Day Date:2024年2月27日
漫漫长路,有人对你微笑过嘛…
全系列文章请查看专栏: Linux实践记录_Once-Day的博客-CSDN博客
参考文档:
- 用GDB调试程序 _CSDN博客 _陈皓
- GDB: The GNU Project Debugger (sourceware.org)
- GDB Documentation (sourceware.org)
- 【精选】VsCode + gdb + gdbserver远程调试C++程序_vscode gdbserver_cuijiecheng2018的博客-CSDN博客
- VS Code与GDB Server远程调试 - 知乎 (zhihu.com)
- VSCode+ssh+gdb远程进行嵌入式Linux调试-CSDN博客
- 通过vscode进行嵌入式linux arm开发板gdb调试_arm gdb 和vscode关联-CSDN博客
文章目录
- GDB之(8)GDB-Server远程调试
- 1. 1. 概述
- 1.1 GDB(GDB-multiarch)功能介绍
- 1.2 GDB Server的远程调试功能
- 1.3 VScode + GDB Server在嵌入式和服务器远程开发过程的应用
- 2. VScode远程调试
- 2.1 远程开发
- 2.2 远程调试
- 2.3 支持多文件编译(make)
- 2.4 gdb-server调试
- 2.5 命令行下gdb连接gdb-server调试
- 2.6 增强版远程调试(extended-remote)
- 2.7 VScode支持gdb-server调试
- 2.8 VScode支持gdb-server的attach功能
1. 1. 概述
1.1 GDB(GDB-multiarch)功能介绍
GDB,全称GNU Debugger,是一个在GNU操作系统下的强大的程序调试工具,广泛应用于Unix及类Unix系统中用以调试多种编程语言编写的程序,尤其是C和C++语言。它由Richard Stallman于1980年代创建,至今仍然是开源社区中使用最为广泛的调试器之一。GDB允许程序员看到程序执行时的内部情况,诸如变量值、程序调用栈等信息,能够在程序执行到特定点时暂停,或者在出现错误时停下来。这就好比给程序员一副透视眼镜,能够洞察程序的内部运行机制。
GDB的功能十分强大,它支持以交互和批处理模式运行,程序员可以在运行时检查程序内存,改变函数内变量的值,甚至动态地调整程序执行流程。许多程序员都曾利用GDB的这些功能解决了令人头疼的“段错误”问题,GDB就像是一个不厌其烦的侦探,帮助开发者一步步揭开程序错误背后的真相。
1.2 GDB Server的远程调试功能
GDB Server是GDB的一种运行模式,它允许开发者进行远程调试,这个功能特别适合于分布式系统、嵌入式系统或者任何难以直接在本机调试的环境。简单来说,GDB Server就像是一个中间人,它在运行目标程序的设备上作为服务运行,而开发者可以在另一台计算机上使用GDB与之通信,进行调试操作。
远程调试功能的工作原理是这样的:GDB Server在目标机(也就是运行待调试程序的机器)上启动,并等待GDB的连接。开发者在自己的工作机上运行GDB客户端,并与GDB Server建立连接,之后就可以像平时使用GDB调试本地程序一样进行操作了。这其中,GDB客户端会通过网络发送调试命令到GDB Server,后者执行这些命令,并将结果返回给客户端。这个过程对开发者来说是透明的,感觉就像直接在本机调试一样。
这样的远程调试功能带来了不少便利。首先,它使得开发者无需直接访问物理硬件就能进行调试,特别是在硬件资源有限或者不方便直接接触时,这一优点尤为突出。
比如,你可能正在开发一个嵌入式设备的程序,这个设备可能在实验室里,或者安装在不易接触的地方,使用GDB Server就可以在办公桌前舒舒服服地调试程序。其次,远程调试还能帮助团队协作,比如在分布式开发环境中,不同地点的开发者可以共享对同一设备的调试会话。
GDB Server的远程调试功能极大地拓展了GDB的使用场景,特别是在嵌入式开发和分布式系统开发中表现出了它的独特价值,不过网络延迟和稳定性可能影响调试效率。
1.3 VScode + GDB Server在嵌入式和服务器远程开发过程的应用
Visual Studio Code(VSCode)是一个轻量级但功能强大的源代码编辑器,它支持调试、语法高亮、智能代码完成、片段、代码重构以及嵌入式Git。由微软开发,它支持Windows、Linux和macOS系统,并且支持众多编程语言。VSCode通过扩展机制,可以集成各种工具和服务,这使得它不仅仅是一个编辑器,更是一个多功能的开发环境。
在嵌入式开发领域,VSCode配合GDB Server进行远程调试非常普遍。开发者通常会在自己的电脑上使用VSCode编写代码,然后通过GDB Server与嵌入式设备上的程序进行交互。这种方式使得开发者可以在拥有丰富功能和友好用户界面的编辑器中进行开发,同时也能够直接调试运行在目标硬件上的代码。
在配置好VSCode的调试环境后,开发者可以通过启动一个调试会话来连接远程的GDB Server。这通常涉及到设置一些调试配置文件,如launch.json,在这个文件中指定GDB的路径、GDB Server的远程地址和端口号等信息。配置好之后,开发者可以在VSCode中启动调试,就像在本地机器上一样设置断点、查看变量、单步执行代码等。
对于服务器远程开发来说,使用VSCode加上GDB Server的组合也非常有效。开发者可以在本地机器上使用VSCode进行代码编辑和版本控制操作,而将编译和调试过程放在远程服务器上执行。这在服务器资源远比本地机器强大,或者需要在特定的服务器环境中进行开发和测试时非常有用。
VSCode提供了一项名为“Remote Development”的功能,可以通过SSH(Secure Shell)直接连接到远程服务器,进行代码编辑和调试等操作。这样的话,即使开发者的本地机器与服务器的操作系统不同,他们也可以无缝地在VSCode中进行开发。此外,VSCode的这一功能也支持容器(如Docker)和Windows子系统,为开发者提供了多样的远程开发选项。
在实际应用中,VSCode和GDB Server的结合为开发者带来了极大的便利,无论是在嵌入式开发还是服务器远程开发中。它们一起提供了一个功能完备、操作便捷、高度可定制的开发环境,即便面临复杂的远程调试任务,也能够轻松应对。
2. VScode远程调试
2.1 远程开发
首先,需要在本地的VSCode中安装一个名为“Remote - SSH”的扩展插件。这个扩展允许VS Code通过SSH(Secure Shell,一种网络协议)连接到远程服务器,并且可以在本地VS Code界面中直接编辑远程服务器上的文件。
安装Remote - SSH
扩展后,需要配置SSH连接。打开VS Code的命令面板(通常是通过按下F1或Ctrl+Shift+P
),输入remote-SSH
,然后选择Remote-SSH: Open Configuration File
:
然后继续选择一个配置文件(如果没有,VS Code会提示你创建一个),在配置文件中输入服务器的地址、端口号、用户名等信息。配置看起来像这样:
Host my_remote_server
HostName example.com
User myusername
Port 22
其中my_remote_server
是你给这个连接起的名字,HostName
是远程服务器的IP地址或域名,User
是你在服务器上的用户名,Port
是SSH服务的端口号,默认是22。配置好之后,按下F1或Ctrl+Shift+P
打开命令面板,输入Remote
,选择Remote-SSH: Connect to Host...
:
选择刚才配置的服务器,VS Code会提示你输入密码或使用SSH密钥认证进行连接。
一旦连接成功,VS Code的界面就会变成远程服务器的工作环境。可以在远程服务器上创建文件夹,编辑C语言的源代码文件。为了编译C语言代码,需要确保远程服务器上安装了C语言的编译器,如GCC。
通过集成终端(标题栏 -> Terminal -> New Terminal),可以直接在VS Code中运行编译命令,例如gcc -o myprogram myprogram.c
来编译一个叫做myprogram.c
的源文件。
如果需要调试C语言程序,VS Code还可以安装“C/C++”扩展插件,它支持IntelliSense(智能代码完成)、代码导航、调试等功能。调试时,需要创建一个.vscode
文件夹和一个launch.json
配置文件,配置调试器参数。
整个流程总结大致如下:
- 安装VS Code和Remote - SSH扩展。
- 配置SSH连接。
- 连接到远程服务器。
- 在远程服务器上编写和编辑C语言代码。
- 安装gcc等编译工具在服务器上编译代码。
- 安装“C/C++”扩展,进行代码调试。
2.2 远程调试
我们总结一下SSH远程开发的框架图,如下:
---
title: Windows PC远程连接ubuntu开发
---
flowchart LR
pc(Windows PC)
server(腾讯云服务器ubuntu)
pc -- ssh连接 --> server
这里的远程指的就是腾讯云Ubuntu,远程开发调试就是指在腾讯云这个Ubuntu上进行本地编译和运行。
我们创建一个测试C文件,如下:
#include "stdio.h"
int main(void) {
printf("Hello, World!\n");
return 0;
}
VScode插件和Ubuntu GNU开发环境安装好了以后,直接调集VScode -> Run -> Run Without Debugging,然后跳出多个编译器配置选项,选择一个即可。
下面是运行的结果,VScode直接帮助我们搞定编译和执行过程,并直接运行。
我们可以选择已debugging
模式进行运行,再运行之前需要打上一个断点(没有断点会直接结束, 后面有配置可以修改为执行程序时自动暂停),如下所示:
下面是VScode C/C++调试界面一些主要的调试功能组件和它们的用法:
-
变量(VARIABLES):
- 这个面板显示当前停止点所在的作用域内的变量以及它们的值。
- 在程序暂停执行时,可以在这里查看和修改变量的值。
- 它通常会自动分组显示局部变量、全局变量和静态变量。
-
监视(WATCH):
- WATCH 面板允许监视特定的表达式或变量,并在每次程序暂停时更新它们的值。
- 可以通过点击“加号”(+)按钮添加你想监控的表达式,比如
i
或arr[3]
。
-
调用栈(CALL STACK):
- 显示当前线程的方法调用序列,从当前执行点回溯到程序的入口点。
- 通过点击不同的栈帧,可以在源代码中跳转到该函数的位置,查看当时的代码状态。
-
断点(BREAKPOINTS):
- 断点是调试的关键,它可以指定程序中的一个位置,程序运行到这里时会自动暂停。
- 在源代码旁边的空白区域点击即可添加或移除断点。
- BREAKPOINTS 面板列出了所有设置的断点,并可以启用、禁用或移除它们。
-
单步运行(Step Over / Step Into / Step Out),这些控制按钮允许你逐行或逐步执行程序。
- Step Over(单步跨过): 执行下一行代码,但不会进入函数体内部。
- Step Into(单步进入): 如果下一行代码是一个函数调用,将进入该函数内部。
- Step Out(单步跳出): 如果当前在一个函数内部,执行完该函数余下的部分并返回到调用它的地方。
-
继续运行(Continue / Resume):
- 点击继续运行(绿色的三角形按钮)会使得程序继续执行,直到遇到下一个断点或程序结束。
-
重新运行(Restart):
- 如果你想从头开始重新调试你的程序,可以使用重新运行功能。
- 这将重新启动调试会话,并从程序的开始处执行。
-
停止程序(Stop):
- 当你想结束当前的调试会话时,可以点击停止按钮(红色的方块按钮)。
- 这将终止正在调试的程序。
可以通过设置断点来观察代码在特定位置的行为,使用WATCH表达式来跟踪复杂逻辑中变量的变化,利用CALL STACK来理解函数调用顺序,以及使用单步执行来仔细分析代码的每一个执行步骤。
2.3 支持多文件编译(make)
在VS Code中,可以通过配置任务来运行make命令,自动编译和构建项目。这样做的好处是可以直接在编辑器内部快速编译代码,而不必切换到命令行界面。下面是如何在VS Code中配置和使用任务来支持make编译程序的步骤:
(1) 创建Tasks.json任务配置文件,文件在工作目录下.vscode/tasks.json
,如果没有,可以直接创建该文件(或者状态栏选择Terminal -> Run Task …)。
{
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: gcc build active file",
"command": "/usr/bin/gcc",
"args": [
"-fdiagnostics-color=always",
"-g",
"${file}",
"-o",
"${fileDirname}/${fileBasenameNoExtension}"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
"detail": "Task generated by Debugger."
}
],
"version": "2.0.0"
}
上面这个是VScode C/C++插件默认的配置文件,下面需要仿照增加一个make命令的配置:
{
"label": "make-build",
"type": "shell",
"command": "make", //执行make命令,可以根据实际情况补充"args"参数配置
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": [
"$gcc"
],
"presentation": {
"reveal": "always",
"panel": "shared"
}
}
在这个配置中,我们定义了一个名为 “make-build” 的任务,下面逐个解释每个属性的含义:
-
"label": "make-build"
:这是任务的标签,用于在 VSCode 中显示任务的名称。 -
"type": "shell"
:这是任务的类型,指定了任务的执行方式。在这种情况下,使用 shell 命令来执行任务。 -
"command": "make"
:这是任务要执行的命令。在这个例子中,使用 “make” 命令来构建项目。 -
"group": { "kind": "build", "isDefault": true }
:这是任务的分组属性。它指定了任务所属的分组以及是否将其设置为默认任务。在这里,将任务分组为 “build” 类型,并将其设置为默认任务。 -
"options": { "cwd": "${fileDirname}" }
:这是任务的选项属性。它指定了任务的工作目录(当前工作目录)。${fileDirname}
是一个变量,表示当前文件所在的目录。 -
"problemMatcher": [ "$gcc" ]
:这是问题匹配器属性,用于捕获任务执行过程中的错误和警告。在这个例子中,我们使用$gcc
问题匹配器,它会匹配 GCC 编译器的输出。 -
"presentation": { "reveal": "always", "panel": "shared" }
:这是任务的展示属性,用于指定任务执行时的展示方式。在这里,设置任务执行完成后总是显示输出,并将输出面板设置为共享面板。
然后增加一个Makefile文件,用于编译,如下:
CC = gcc
SRC = $(wildcard *.c)
FLAGS = -Wall -Wextra -Werror -std=c99 -g
all: $(SRC)
$(CC) $(FLAGS) -o main-test $(SRC)
然后在Terminal
里面选择Run Task
里面的make-build
:
make命令执行成功之后,便会生成对应的二进制文件,这个时候,还不会自动运行,我们还要加上Launch.json
调试配置,如下:
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "my-debug",
"type": "cppdbg",
"request": "launch",
"program": "${fileDirname}/main-test", //多文件编译时要指定二进制为目标名字
"args": [], //可选参数
"stopAtEntry": true, //这里用来控制是否在main函数(进入点)自动停止,根据情况设置
"cwd": "${fileDirname}", //执行根目录,根据实际情况指定
"environment": [],
"externalConsole": false,
"MIMode": "gdb", //指定调试器, 一般用gdb即可
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "make-build", //这里很重要, 用于执行上面的Task任务,即label名字, 执行我们的task
"miDebuggerPath": "/usr/bin/gdb" //指定gdb路径, 跨架构调试时很重要
}
]
}
这段配置文件,用于设置 C/C++ 项目的调试环境,下面是对这个文件的解释:
-
"version": "0.2.0"
:指定了配置文件的版本号。 -
"configurations": [...]
:这是一个配置项的数组,包含了多个调试配置。 -
"name": "C/C++: gcc build active file"
:这是一个调试配置的名称,用于在 Visual Studio Code 的调试面板中显示。 -
"type": "cppdbg"
:指定了调试器的类型,这里是 C/C++ 调试器。 -
"request": "launch"
:指定了调试器的请求类型,这里是启动调试。 -
"program": "${fileDirname}/${fileBasenameNoExtension}"
:指定了要调试的程序路径。${fileDirname}
表示当前文件所在的目录,${fileBasenameNoExtension}
表示当前文件的基本名称(不包含扩展名)。 -
"args": []
:指定了要传递给程序的命令行参数,这里为空数组。 -
"stopAtEntry": false
:指定了是否在程序入口处停止调试,这里设置为false
,即不停止。 -
"cwd": "${fileDirname}"
:指定了调试器的当前工作目录,这里设置为当前文件所在的目录。 -
"environment": []
:指定了要设置的环境变量,这里为空数组。 -
"externalConsole": false
:指定了是否使用外部控制台窗口进行调试,这里设置为false
,即不使用。 -
"MIMode": "gdb"
:指定了调试器的模式,这里是 GDB 调试器。 -
"setupCommands": [...]
:这是一个设置命令的数组,用于在调试会话开始前执行一些命令。这里只有一个命令,用于启用 GDB 的漂亮打印功能。 -
"preLaunchTask": "make-build"
:指定了在启动调试会话之前要运行的任务,这里是一个名为 “make-build” 的任务。 -
"miDebuggerPath": "/usr/bin/gdb"
:指定了 GDB 调试器的路径。
下面我们可以完整的通过Run
->Start debugging
来一件远程编译和调试程序,并且使用make脚本编译,如下(my-debug配置):
VScode的Task和Launch两种配置非常灵活,Task是定义任务,可以是各种shell执行,包括Gcc/Make/cmake等各类编译器或者工具,Launch则是定义调试规则,通过指定调试参数和配置,即使复杂程序,也能一键调试。
2.4 gdb-server调试
gdbserver
是一个小型的调试服务器,它可以在远程系统上运行,并允许你使用 GDB 进行远程调试。这对于在嵌入式系统或其他远程环境中调试特别有用,因为可以在一台机器上运行你的应用程序,然后在另一台机器上进行调试。
以下是一些 gdbserver
的主要功能:
-
远程调试:
gdbserver
的主要用途就是提供远程调试能力。可以在一台机器(目标机)上运行gdbserver
,然后在另一台机器(主机)上运行 GDB,并连接到gdbserver
。这样你就可以在主机上调试在目标机上运行的程序。 -
多线程支持:
gdbserver
支持多线程调试。 -
硬件和操作系统抽象:
gdbserver
提供了一个硬件和操作系统的抽象层,这使得 GDB 可以在主机上以一种统一的方式进行调试,即使目标机的硬件和操作系统与主机不同。
以下是一些 gdbserver
的命令行选项:
-
gdbserver [options] <host>:<port> <program> [args...]
:在指定的主机和端口上启动gdbserver
,并运行指定的程序。可以提供任意数量的参数来传递给程序。 -
gdbserver [options] <host>:<port> --attach <pid>
:连接到已经在运行的进程,该进程的 PID 为<pid>
。 -
gdbserver [options] --multi <host>:<port>
:在指定的主机和端口上启动gdbserver
,并等待 GDB 连接。在这种模式下,gdbserver
不会立即启动一个程序,而是等待 GDB 连接并指示它应该做什么。
gdbserver
的一些主要选项包括:
-
--debug
:启用gdbserver
的调试输出。 -
--remote-debug
:启用远程协议的调试输出。 -
--no-ack-mode
:在与 GDB 的通信中禁用确认。这可以提高性能,但可能不适用于所有连接类型。
要获得更多的 gdbserver
参数信息,可以在命令行中运行 gdbserver --help
,或者查阅 GDB 的在线文档。
2.5 命令行下gdb连接gdb-server调试
我们首先在命令行下进行测试,服务端是root用户,客户端是普通开发账号,虽然不是两台设备,但是差别并不大,关键是把流程跑通。
首先是Ubuntu下载安装gdb-server
,如下所示:
onceday->gcc-test:# sudo apt-get install gdbserver
(1) 在远程系统上启动gdbserver:
首先需要确保程序已经使用调试信息编译(通常是带有-g
选项的编译),通过file查看二进制程序信息:
onceday->gcc-test:# file main-test
main-test: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=34ba69e548cb8e292e7003b0a460015a053b0e35, for GNU/Linux 3.2.0, with debug_info, not stripped
在远程系统上,通过以下命令启动gdbserver:
gdbserver <host>:<port> <program> [program arguments...]
<host>
是远程系统的IP地址或主机名,如果只希望在本机调试,可以使用localhost
。<port>
是gdbserver监听的端口号,确保端口没有被防火墙阻挡。<program>
是你需要调试的程序的路径。[program arguments...]
是传递给程序的任何参数。
下面是示例执行:
onceday->gcc-test:# gdbserver 127.0.0.1:6000 ./main-test
Process ./main-test created; pid = 3945370
Listening on port 6000
(2) 在本地系统上启动GDB,连接到远程的gdbserver:
gdb <program>
(gdb) target remote <host>:<port>
<program>
是本地的、与远程系统上的程序相同的、带有调试信息的可执行文件。<host>
和<port>
应该与远程系统上gdbserver的设置相匹配。
示例执行如下:
ubuntu->gdb-server:$ gdb ./main-test
......
(gdb) target remote 127.0.0.1:6000
Remote debugging using 127.0.0.1:6000
Reading /lib64/ld-linux-x86-64.so.2 from remote target...
warning: File transfers from remote targets can be slow. Use "set sysroot" to access files locally instead.
Reading /lib64/ld-linux-x86-64.so.2 from remote target...
Reading symbols from target:/lib64/ld-linux-x86-64.so.2...
Reading symbols from /usr/lib/debug/.build-id/61/ef896a699bb1c2e4e231642b2e1688b2f1a61e.debug...
Reading /usr/lib/debug/.build-id/21/a2739157f98ff49d8920f47723a6a3ddb1f4d1.debug from remote target...
0x00007ffff7fe32b0 in _start () from target:/lib64/ld-linux-x86-64.so.2
(gdb)
然后服务端这边也会打印提示远程已经连接:
Remote debugging from host 127.0.0.1, port 37878
(3)开始调试:一旦连接成功,就可以使用GDB的各种命令来控制程序的执行,包括设置断点(break
)、单步执行(step
)、继续执行(continue
)、查看变量值(print
)、查看栈信息(backtrace
)等。例如,打印堆栈:
(gdb) break main
Breakpoint 1 at 0x555555555151: file test.c, line 4.
(gdb) c
Continuing.
Reading /lib/x86_64-linux-gnu/libc.so.6 from remote target...
Breakpoint 1, main () at test.c:4
4 printf("Hello, World!\n");
(gdb) bt
#0 main () at test.c:4
(4) 调试会话结束:当完成调试并想要结束会话时,可以在GDB命令行输入quit
。远程的gdbserver通常会在断开连接时自动退出,但如果需要,也可以手动停止它。
使用GDB远程调试的一个重要好处是,你无需在远程系统上安装完整的开发环境。这对于资源有限或不允许安装额外软件的环境(如生产服务器)尤为有用。此外,远程调试还允许你在本地开发机器上拥有更好的调试体验,比如使用图形化界面。
2.6 增强版远程调试(extended-remote)
在使用GDB(GNU Debugger)连接到gdbserver进行远程调试时,target extended-remote
是一个提供了额外功能的命令,允许用户在单个GDB会话中对多个程序或多个远程目标进行操作。这一功能对于那些需要频繁切换调试目标或重启远程程序的开发者来说非常有用。
在标准的target remote
模式中,GDB会话与单个远程gdbserver进程相连,在远程进程终止后,GDB会话也会结束。但是,在target extended-remote
模式中,GDB会话保持活跃状态,即使远程进程结束也不会中断。这就允许用户重启远程进程或者连接到另一个进程,而无需重启GDB。
使用target extended-remote
的步骤如下:
-
在远程系统上启动gdbserver:同之前一样,需要在远程系统上启动gdbserver,监听特定的端口。
onceday->gcc-test:# gdbserver 127.0.0.1:6000 ./main-test Process ./main-test created; pid = 3948489 Listening on port 6000
-
在本地系统上使用
target extended-remote
连接:在本地系统上启动GDB,并输入如下命令来连接到远程的gdbserver:gdb ./main-test (gdb) target extended-remote 127.0.0.1:6000
-
控制远程进程: 一旦连接成功,在
extended-remote
模式下,可以使用run
命令来启动远程程序,或者使用attach
命令连接到一个已经运行的进程。如果需要重新启动调试的程序,可以使用monitor
命令通知gdbserver重启进程。# 客户端gdb输出如下 (gdb) run The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /home/ubuntu/tdata/gdb-server/main-test Reading /lib64/ld-linux-x86-64.so.2 from remote target... Reading /lib64/ld-linux-x86-64.so.2 from remote target... Reading /usr/lib/debug/.build-id/21/a2739157f98ff49d8920f47723a6a3ddb1f4d1.debug from remote target... Reading /lib/x86_64-linux-gnu/libc.so.6 from remote target... [Inferior 1 (process 3948970) exited normally] # 服务端gdbserver输出如下 Process ./main-test created; pid = 3948970 Hello, World! Child exited with status 0
-
多重调试目标:
target extended-remote
模式下,可以使用file
命令来更改当前调试的程序,或者使用disconnect
命令断开当前连接而不退出GDB,之后可以重新连接到同一个或不同的远程gdbserver进程。也可以使用set remote exec-file
命令来指定要调试的远程程序:(gdb) set remote exec-file /path/to/remote/program
然后,可以使用
run
命令来启动远程程序: -
会话结束:当调试结束,可以使用
quit
退出GDB。如果需要,还可以在远程系统上手动停止gdbserver。
这一功能极大地提高了调试效率,尤其是在开发阶段,因为它减少了重启调试器和重新建立连接的次数。它也为开发者提供了更多的灵活性,使得在复杂的多进程或服务环境中调试变得更加简单。
总的来说,target extended-remote
是GDB强大功能的一部分,它为开发者在远程调试时提供了额外的灵活性和便利。这一命令的使用对于需要对多个目标进行调试或者需要重启远程目标进行调试的情况尤为有益。
2.7 VScode支持gdb-server调试
下面是支持 GDBextended-remote
模式的Launch.json配置:
{
"name": "my-remote-debug",
"type": "cppdbg",
"request": "launch",
"program": "./main-test",
"args": [],
"stopAtEntry": true,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"miDebuggerPath": "/usr/bin/gdb",
"miDebuggerServerAddress": "127.0.0.1:6000",
"linux": {
"MIMode": "gdb",
"miDebuggerPath": "/usr/bin/gdb"
},
"serverStarted": "Listening on port 6000",
"serverLaunchTimeout": 20000,
"filterStderr": false,
"filterStdout": false,
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": false
}
]
}
这段配置用于将GDB调试器连接到远程GDB服务器进行调试,下面是每个配置选项的含义:
"name": "my-remote-debug"
:这是调试配置的名称,可以根据需要进行更改。"type": "cppdbg"
:这指定了调试器的类型,这里是C++调试器。"request": "launch"
:这表示我们要启动一个新的调试会话。"program": "./main-test"
:这是要调试的程序的路径。在这个例子中,它指向名为main-test
的可执行文件。"args": []
:这是传递给程序的命令行参数。在这个例子中,没有传递任何参数。"stopAtEntry": true
:这表示调试器在程序入口处停止执行。"cwd": "${workspaceFolder}"
:这是调试器的当前工作目录,${workspaceFolder}
表示工作区的根目录。"environment": []
:这是要设置的环境变量列表。在这个例子中,没有设置任何环境变量。"externalConsole": false
:这表示调试器不会在外部打开一个新的控制台窗口。"MIMode": "gdb"
:这是调试器的调试接口模式,这里是GDB。"miDebuggerPath": "/usr/bin/gdb"
:这是GDB调试器的路径。"miDebuggerServerAddress": "127.0.0.1:6000"
:这是远程GDB服务器的地址和端口号。"linux": { ... }
:这是一个特定于Linux操作系统的配置块,用于指定Linux上的调试器设置。"serverStarted": "Listening on port 6000"
:这是一个调试器启动时显示的消息,表示GDB服务器已经在6000端口上监听连接。"serverLaunchTimeout": 20000
:这是等待GDB服务器启动的超时时间,以毫秒为单位。"filterStderr": false
:这表示不过滤标准错误输出。"filterStdout": false
:这表示不过滤标准输出。"setupCommands": [ ... ]
:这是一组GDB命令,用于在调试会话开始时自动执行。在这个例子中,只有一个命令用于启用GDB的漂亮打印功能。
这些配置选项一起定义了如何连接到远程GDB服务器并进行调试。通过配置这些选项,你可以根据你的需求自定义调试会话的行为。
注意:以上配置假设已经在远程机器上启动了 gdbserver
。如果需要 VSCode 启动 gdbserver
,可以再Task里面执行编译脚本,脚本里包括远程ssh连接设备并且自动执行gdbserver监听目标端口。
一般配合VScode使用,会使用gdbserver [options] --multi <host>:<port>
来启动服务端,这样可以调试不同的程序。
如果连接不上,提示需要extended-remote
,那么说明是对端gdbserver出了问题,非extended-remote
模式下,每次gdbserver都需要结束重启,然后客户端才能连接。
例如,服务端如下启动(--multi
相当于常驻后台,可以调试不同的二进制文件):
onceday->gcc-test:# gdbserver --multi 127.0.0.1:6000
Listening on port 6000
然后VScode的launch配置稍加改动,在setupCommands
里面增加几个命令,如下:
"useExtendedRemote": true,
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": false
},
{
"text": "set remote exec-file main-test", // 先设置远程运行二进制文件的地址(main-test)
"ignoreFailures": true
},
]
这里有两点需要注意:
"useExtendedRemote": true,
需要增加这个配置,因为这里是用target extended-remote
来连接,否则会导致报错。set remote exec-file
需要先运行,由于--multi
未指定可执行文件,会导致服务端直接退出。
配置完成之后,就可以不间断的重复调试了(更新二进制文件需要通过Task来完成,比如ssh上传到远程服务器里面)。
如果存在文件路径问题,那么就需要使用sourceFileMap
配置字段更改本地和远程的路径映射情况。
2.8 VScode支持gdb-server的attach功能
当使用launch
方式时,GDB本身会启动你的程序,并且立即获得对它的控制权。你可以在程序开始执行之前设置断点,然后让GDB运行程序直到达到这些断点。这种方式是在开发阶段常用的,因为它允许你从程序的起点开始调试,并且能够逐步跟踪程序的执行过程。在VSCode的launch.json
配置文件中使用launch
请求时,就是在告诉VSCode启动一个调试会话,它会通过GDB启动并控制程序。
与launch
不同,attach
方式是在程序已经在运行之后再由GDB连接到该程序。这种方式用于已经启动的进程,或是在没有调试信息的情况下运行的进程。当你使用attach
时,你需要指定一个正在运行程序的进程ID(PID),GDB会附加到这个进程上,并且可以开始调试。这种方式在解决生产环境中的问题或调试守护进程(例如服务器进程)时非常有用。
服务端启动命令不用修改,维持gdbserver --multi 127.0.0.1:6000
这样的配置就可以(可以添加额外的选项,比如debug日志信息)。
然后客户端新建一个launch.json配置文件,如下:
{
"name": "my-remote-attch",
"type": "cppdbg",
"request": "attach",
"program": "${fileDirname}/main-test",
"processId": "${command:pickProcess}",
"MIMode": "gdb",
"miDebuggerPath": "/usr/bin/gdb",
"miDebuggerServerAddress": "127.0.0.1:6000",
"useExtendedRemote": true,
"linux": {
"MIMode": "gdb",
"miDebuggerPath": "/usr/bin/gdb"
},
"filterStderr": false,
"filterStdout": false,
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": false
}
]
},
这个新增了一个processId
配置,在attach
时,需要先挑选可用的进程ID,然后选择对应进程(需要和配置里二进制文件对应上):
选择目标进程后,程序不会直接停止,而是继续执行,剩下的界面和launch
是一致的,最好attach
之前总是先打断点,避免程序无法stop住(信号未接收)。
Once Day
也信美人终作土,不堪幽梦太匆匆......
如果这篇文章为您带来了帮助或启发,不妨点个赞👍和关注,再加上一个小小的收藏⭐!
(。◕‿◕。)感谢您的阅读与支持~~~