首页 > 其他分享 >gdb+gdbserver远程调试

gdb+gdbserver远程调试

时间:2023-06-12 09:02:39浏览次数:51  
标签:core gdbserver gdb linux arm 调试

gdb+gdbserver远程调试

 

在开发机器上:

  • 安装 VS Code Insiders, 在 2019年05月, 只有这个版本支持 Remote - Development 插件
  • 安装 VSCode 扩展 “Remote - Development”, 方法是左下角 齿轮图标 -> 扩展, 直接搜索商店
  • 安装兼容 OpenSSH 的 SSH 客户端, 对于 Windows 7, 安装 Git for Windows 即可, Windows 10 的选择更多

在执行机器上:

  • 安装 gdb, 对于 CentOS 是 yum install gdb
  • 安装 gdbserver, 对于 CentOS 是 yum install gdb-gdbserver
  • 启动 ssh 服务端, 一般都有

使用 VSCode 远程访问代码

以前的办法是通过 sftp, samba 等协议, 把 Linux 环境的文件映射到 Windows 做成一个虚拟盘, 这类工具有很多, 可以选 RaiDrive 等等, 现在 VSCode 的官方扩展 “Remote - Development” 可以直接走 ssh 协议访问远程文件, 就不必映射虚拟盘了

安装好上述扩展, 首先改设置, 左下角 齿轮图标 -> 设置, 搜 “remote.SSH”, 勾选 remote.SSH.showLoginTerminal (或者直接在 settings.json 里添加一行 "remote.SSH.showLoginTerminal": true 也可)

完后就可以连接到远程机器了, 在 VSCode 主界面 ctrl+shift+p 选 Remote.SSH: Connect to host, 输入 root@<ip>, 如果是密码认证, 之后留意终端窗口, 在里面输入密码

首次连接会多次要求输入密码, 成功后界面左下角会有 SSH: <ip> 的已连接状态, 同时这个 VSCode 也变成了该远程连接的专属实例, 然后就可以从侧边栏打开项目路径了, 打开后如图:

图示通过 SSH 连接后的远程 VSCode 项目

之后根据项目类型, 可以安装一些基本的扩展, 如:

  • C/C++ IntelliSense, debugging, and code browsing
  • autoconf
  • Bookmarks

等等, 注意有些扩展是要安装在 SSH 的目标机器上, 安装时会提示你 install on SSH: xxxx, 而主题类扩展和一部分功能类扩展是安装在本地机器上

如果 Remote-SSH 连接到远程机器是密码认证的, 频繁使用时每次都要输密码, 很麻烦, 这时用公钥认证更好, 这里参考 Windows 下 VS Code Remote-Development(ssh) 插件的安装和配置 基本思路就是典型的 SSH 密钥方式登录: 生成一对公钥私钥, Linux 执行环境安装公钥, VSCode 使用私钥, 如下:

首先生成密钥:

# 生成一对密钥, 在哪个机器生成都行
ssh-keygen -t rsa

# 公钥需要放到 Linux 执行环境里
# 假设生成的公钥是 "vscode_rsa.pub"
cat /root/.ssh/vscode_rsa.pub >> /root/.ssh/authorized_keys
chmod 644 /root/.ssh/authorized_keys

# 私钥需要留在 VSCode 一端 Windows 机器里
# 假设路径是 "D:/.ssh/vscode_rsa"

然后在 VSCode 主界面 ctrl+shift+p 选 Remote.SSH: Open Configuration File, 输入以下内容:

Host dev-56144               <- 连接标识, 随便写
    HostName 172.16.xx.xxx           <- 远程机器 IP
    User root
    IdentityFile D:/.ssh/vscode_rsa  <- 私钥的本地完整路径

配置好后, 在 Remote.SSH: Connect to host 时就会看到这个连接配置了

使用 VSCode 和 gdbserver 远程调试 C 代码

在远程 Linux 机器上, 运行 gdbserver localhost:<port> <program> <args>, 比如

gdbserver localhost:2333 /path/to/myprogram myarg1 myarg2 myarg3

这里假设远程 Linux 机器 IP 是 172.16.56.144, gdbserver 的端口设为 2333, 在 VSCode 机器上, 进入菜单 调试 -> 添加配置, 会生成一个配置文件 SSH之后的项目根目录/.vscode/launch.json, 将其修改如下:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "gdb Remote Launch",
            "type": "cppdbg",
            "request": "launch",
            "program": "/path/to/myprogram",
            "args": ["myarg1", "myarg2", "myarg3"],
            "stopAtEntry": true,
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb",
            "miDebuggerPath": "gdb",
            "miDebuggerArgs": "gdb",
            "linux": {
                "MIMode": "gdb",
                "miDebuggerPath": "/usr/bin/gdb",
                "miDebuggerServerAddress": "172.16.56.144:2333",
            },
            "logging": {
                "moduleLoad": false,
                "engineLogging": false,
                "trace": false
            },
            "setupCommands": [
                {
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ],
            "cwd": "${workspaceFolder}",
        }
    ]
}

几个注意的地方:

  • 应该是 "request": "launch", 不是 “attach”, 此后也并不需要记录进程ID
  • 需要填对 "miDebuggerServerAddress": "172.16.56.144:2333", 有这个设置才会开启 gdb 远程调试
  • "engineLogging": true 可以看到 gdb 自身的详细消息
  • 必须是 "externalConsole": false 否则报错
  • /path/to/myprogram 应该是在 gdbserver 和 launch.json 里都要填一次

之后就可以正常使用调试功能了, 添加断点, 监视等等, 如图:

图示远程 gdb 调试

在 VSCode 的调试控制台输入变量名, 就可以直接打印出来, 想使用默认的调试器命令要在前面加 -exec, 如 -exec p/x args

附 gdbserver usage

gdbserver  --help
Usage:    gdbserver [OPTIONS] COMM PROG [ARGS ...]
    gdbserver [OPTIONS] --attach COMM PID
    gdbserver [OPTIONS] --multi COMM

COMM may either be a tty device (for serial debugging), or
HOST:PORT to listen for a TCP connection.

Options:
  --debug               Enable general debugging output.
  --remote-debug        Enable remote protocol debugging output.
  --version             Display version information and exit.
  --wrapper WRAPPER --  Run WRAPPER to start new programs.
  --once                Exit after the first connection has closed.
Report bugs to "<http://www.gnu.org/software/gdb/bugs/>".

 

1.编译时加-g

2.gdbserver + gdb 远程调试
prebuilts/toolchains/mips-gcc720-glibc226/mips-linux-gnu/libc/mfp64/usr/bin/gdbserver 
把它拷贝到target

3.target
gdbserver 宿主机 IP:端口号 要调试的可执行程序
#./gdbserver 192.168.0.240:50555 ./cloud_sound_dance

4.host
mips-linux-gnu-gdb 要调试的可执行程序
#mips-linux-gnu-gdb tmp_files/cloud_sound_dance

target remote 目标板 IP:端口号
(gdb) target remote 192.168.0.174:50555

5.设置断点,运行:
(gdb) b App.cpp:308
Breakpoint 1 at 0x454834: file ../App.cpp, line 308.
(gdb) r
The "remote" target does not support "run".  Try "help target" or "continue".
(gdb) continue
Continuing.

(gdb) info breakpoints

 

命令

命令缩写

命令说明

list

l

显示多行源代码

break

b

设置断点,程序运行到断点的位置会停下来

info

i

描述程序的状态

run

r

开始运行程序

display

disp

跟踪查看某个变量,每次停下来都显示它的值

step

s

执行下一条语句,如果该语句为函数调用,则进入函数执行其中的第一条语句

next

n

执行下一条语句,如果该语句为函数调用,不会进入函数内部执行(即不会一步步地调试函数内部语句)

print

p

打印内部变量值

continue

c

继续程序的运行,直到遇到下一个断点

set var name=v

 

设置变量的值

start

st

开始执行程序,在main函数的第一条语句前面停下来

file

 

装入需要调试的程序

kill

k

终止正在调试的程序

watch

 

监视变量值的变化

backtrace

bt

产看函数调用信息(堆栈)

frame

f

查看栈帧

quit

q

退出GDB环境

 

对于软件开发来说,调试程序是比不可少的。对于开发PC软件通常系统已经继承了调试工具(比如Linux系统的GDB),或者IDE直接支持对程序的调试。而对于开发嵌入式软件来说调试的手段比较有限,很多开发者仅有的调试手段依然是最原始的打印(我也是其中之一)。当然除了打印调试之外还有通过gdb+gdbserver来调试,gdbserver在目标系统中运行,gdb则在宿主机上运行。

一、源码下载

对于嵌入式软件开发调试工具没有现成的,且嵌入式系统比较繁杂,gdbserver需要根据目标系统单独编译。gdb的源码包下载地址为:http://ftp.gnu.org/gnu/gdb/。目前最新的版本为8.0但由于8.0加入了C++11,而我的目标系统的交叉工具链不支持C++11,故下载7.12版本。

二、编译arm-linux-gdb

Linux系统本身已经自带gdb工具,但无法用在嵌入式调试中,需要单独编译arm-linux-gdb
  
1.解压源码包

$ tar zxvf gdb-7.12.tar.gz 
$ cd gdb-7.12/

2.生成Makefile

$ ./configure --target=arm-linux --prefix=$PWD/__install

–target:指定目标平台。–prefix:指定安装路径。
 
3.编译

$ make

4.安装

$ make install

执行此命令后会在当前目录下生成文件夹__install/里面包含可执行文件、头文件、动态库文件等,如下图所示:

这里写图片描述
目前只用到bin/目录下的可执行文件arm-linux-gdb,执行下面命令:

$ sudo cp __install/bin/arm-linux-gdb /usr/bin/

将生成的arm-linux-gdb文件拷贝到系统/usr/bin/目录下,这样便可以在任何地方很方便的调用。

三、编译gdbserver

1.生成Makefile

$ cd gdb/gdbserver/
$ ./configure --target=arm-linux --host=arm-linux-gnueabi

–host:指定交叉工具链,arm-linux-gnueabi为我的目标系统的交叉工具链。
  
2.编译

$ make

编译gdbserver不需要执行make install命令,make之后在当前目录下会生成可执行程序gdbserver,将其拷贝到目标系统中。

四、可能出现的问题

1.GDB7.6 Remote ‘g’ packet reply is too long错误

在gdb调试过程中可能出现如下图所示错误:
这里写图片描述

解决方案:

修改gdb/remote.c文件,屏蔽process_g_packet函数中的下列两行:

 if (buf_len > 2 * rsa->sizeof_g_packet)
     error (_(“Remote ‘g’ packet reply is too long: %s”), rs->buf);

在其后添加:

 if (buf_len > 2 * rsa->sizeof_g_packet) {
      rsa->sizeof_g_packet = buf_len ;
      for (i = 0; i < gdbarch_num_regs (gdbarch); i++)
      {
         if (rsa->regs[i].pnum == -1)
         continue;

         if (rsa->regs[i].offset >= rsa->sizeof_g_packet)
         rsa->regs[i].in_g_packet = 0;
         else
         rsa->regs[i].in_g_packet = 1;
      }
   }

2.arm-linux-gdb加载目标系统库出错

arm-linux-gdb在调试的时候会加载目标系统的库文件,如果出错时便无法调试,如下图所示:
这里写图片描述

解决方案

可通过指令[set solib-search-path+库文件路径]来手动加载目标系统库文件,如下为我的设置:

(gdb) set solib-search-path /home/zxd/MerriiLinux_qa1_qe1/boot/config/gcc-linaro/arm-linux-gnueabi/libc/lib/

五、调试

下面给出调试用的示例代码:

test.c

#include <stdio.h>

int main(void)
{
	int i;
	for (i=0; i<5; i++) {
		printf("Hello World:%d\n", i);
	}
	
	printf("Good bye!\n");

	return 0;
}

1.编译用于调试的程序

$ arm-linux-gnueabi-gcc -g test.c -o test

为了能够gdb调试,在编译程序的时候必须加-g选项,将生成的可执行文件test拷贝到目标板中。

2.环境配置

宿主机IP:192.168.0.139
目标板IP:192.168.0.138

3.目标板上运行gdbserver

# cd /
# gdbserver 192.168.0.139:6666 /test 

其中 192.168.0.139 是宿主机的地址,6666是调试端口,helloword是需要调试的可执行程序。gdbserver启动之后打印出下面内容:
这里写图片描述

4.宿主机上运行arm-linux-gdb

$ arm-linux-gdb test

arm-linux-gdb启动之后会有如下打印:
这里写图片描述
其中最后一行(gdb) 表示arm-linux-gdb在等待输入指令,现在需要输入指令来连接gdbserver,如下所示:

(gdb) target remote 192.168.0.138:6666

输入上述指令若成功连接目标板会打印出下图所示:
这里写图片描述

宿主机会打印出如下图所示:
这里写图片描述

5.指定库文件路径

由上图可知arm-linux-gdb加载目标系统库文件成功,如果失败需要手动加载,下面给出对于我的目标板的指令:

(gdb) set solib-search-path /home/zxd/MerriiLinux_qa1_qe1/boot/config/gcc-linaro/arm-linux-gnueabi/libc/lib/

6.调试
  下面给出调试结果,具体调试方法跟PC机上gdb调试类似,只是在设置断点后,gdb通过run/r指令让程序运行。而对于嵌入式系统来说程序已经通过gdbserver运行了,arm-linux-gdb通过continue/c让程序继续运行,下面为调试结果:
这里写图片描述

其中左侧的为目标开发板通过串口终端打印出来的,右边窗口为通过ssh连接宿主机的终端。

 

segmentation-fault段错误一直是一个很难解决的问题,尤其是当代码量很大的时候寻找起来更是如大海捞针一般,本文将介绍通过gdb+gdbserver来找到出错的位置。

segmentation-fault段错误有时候每次都能重现出来,这种问题相对来说比较好调试可以直接在线调试。有的问题比较难重现,甚至可能运行好几天才有可能重现,这种问题一般是通过分析segmentation-fault段错误产生的core文件。

下面给出本文用的示例代码:
test.c

#include <stdio.h>  
#include <string.h>

int main()  
{  
	char* p;

	memcpy(p, "hello", 5);

    return 0;  
} 

用gdb调试代码时必须加上-g选项,如果没有-g,你将看不见程序的函数名、变量名,所代替的全是运行时的内存地址。先对示例代码交叉编译到目标机上:

arm-linux-gnueabi-gcc -g test.c -o test

将生成的可执行程序test拷贝到目标机上便可以开始本次试验了。

一、直接在线调试

1.在目标机上启动gdbserver

$ gdbserver 192.168.0.139:6666 /test 

这里写图片描述

2.在宿主机上启动gdb

$ arm-linux-gdb test

这里写图片描述

3.连接目标机

(gdb) target remote 192.168.0.138:6666

若连接成功会有下面提示:
这里写图片描述

在目标机上也有连接成功提示:
这里写图片描述

4.调试

执行指令 c(continue)让程序开始执行并出错,若错误简单可直接显示出错误的具体位置:
这里写图片描述

可以通过指令bt来查看当前的堆栈信息:
这里写图片描述

若程序比较复杂,出错时没有显示出具体的出错信息,可通过where指令来查看出错的位置:
这里写图片描述

二、抓取core文件

1.系统设置

Linux系统默认core文件的大小限制为0,即产生segmentation-fault段错误时不会生成core文件,可以通过ulimit -c指令来查看系统限制。可以通过ulimit -c filesize命令来修改系统对core文件大小的限制,但如果core文件超过限制大小(filesize的单位为kbyte)将会被裁剪,最终生成一个不完整的core文件。在调试此core文件的时候,gdb会提示错误。所以一般情况下不会限制core文件的大小,指令如下:

$ ulimit -c unlimited

2.产生core文件

当运行程序出现segmentation-fault段错误时,如果产生core文件,会在终端有如下打印:
这里写图片描述

在当前目录下也会生成core文件:
这里写图片描述

3.调试core文件

core文件产生之后将其拷贝到宿主机,不需要连接目标机便可调试。运行下列指令启动gdb:

$ arm-linux-gdb test core

这里写图片描述

由于本文采用的示例比较简单,gdb已经列出了出错的具体位置,敲击回车,gdb列出了具体出错的代码:
这里写图片描述

可以通过指令bt来查看堆栈信息:
这里写图片描述

可以通过where指令来定位出错的位置:
这里写图片描述

 

=============== End

 

标签:core,gdbserver,gdb,linux,arm,调试
From: https://www.cnblogs.com/lsgxeva/p/17474031.html

相关文章

  • NTSD(Command Line)调试DMP格式文件
    前言:之前介绍了利用VS2005进行Dump文件的调试,功能非常强大。但VS2005是一个大程序,本文将讨论利用NTSD的CommandLine 实现Dump 文件的调试。1、  载入DMP格式文件利用CMD打开命令行窗口,切换到NTSD所在目录。利用命令载入DMP文件:ntsd–zdumpfileName–ysymbolPath–srcpath......
  • phptrace 是一个用于跟踪 PHP 应用程序性能的工具,可以帮助开发者快速发现性能瓶颈和调
    phptrace是一个用于跟踪PHP应用程序性能的工具,可以帮助开发者快速发现性能瓶颈和调试PHP应用程序。以下是一个使用phptrace的简单案例:1.安装phptrace可以通过以下命令安装phptrace:sudoapt-getinstallphp7.0-devgitclonehttps://github.com/Qihoo360/phptrace.......
  • gdb常用命令
    1.gdb[GDB调试教程:1小时玩转Linuxgdb命令]sudodnfdebuginfo-installglibc-2.32-2.fc33.x86_64编译时,gcc-gtest.c-otest,因为在调试二进制文件的时候,我们需要在二进制文件中加入调试信息,而调试信息是怎么添加的呢?它是在编译的时候,通过-g选项添加的,即类似于gcc-gtest......
  • gdb
     当程序出现“段错误(核心已转储)”的错误时,这通常意味着你的程序访问了无效的内存地址,导致程序崩溃。你可以使用gdb调试器来查找在程序的哪一行代码导致了这个错误。下面是使用gdb调试器查找错误的一般步骤:  在编译你的程序时,使用-g选项生成调试信息。这样gdb就......
  • 调试技巧之调用堆栈
    在计算机科学中,Callstack是指存放某个程序的正在运行的函数的信息的栈。Callstack由stackframes组成,每个stackframe对应于一个未完成运行的函数。在当今流行的计算机体系架构中,大部分计算机的参数传递,局部变量的分配和释放都是通过操纵程序栈来实现的。栈用来传递函数参数,存......
  • 使用dbghelp获取调用堆栈--release下的调试方法学
    当软件作为release模式被发布给用户时,当程序崩溃时我们很难去查找原因。常见的手法是输出LOG文件,根据LOG文件分析程序崩溃时的运行情况。我们可以通过SEH来捕获程序错误,然后输出一些有用的信息作为我们分析错误的资料。一般我们需要输出的信息包括:系统信息、CPU寄存器信息、堆栈......
  • dlv调试实践
    dlv调试实践案例一:通过dlvdebug进行源码调试(1.)新建main.go,并执行gomodinitpackagemainimport("fmt")funcmain(){nums:=make([]int,5)fori:=0;i<len(nums);i++{nums[i]=i*i}fmt.Println(nums)}(2.)执行dlvd......
  • GDB调试
    在编译的时候要加上“-g”选项才可以调试,例如:gcc-g-oprogmain.cmstore.c 参考链接:GDB官方手册:Top(DebuggingwithGDB)(sourceware.org)......
  • Python代码调试之异常回溯
    当发生异常时,Python会回溯异常,给出大量的提示,可能会给程序员的定位和纠错带来一定的困难,这时可以使用sys模块的exc_info()函数来回溯最近一次异常。sys.exc_info()的返回值tuple是一个三元组(type, value, traceback),其中:type——异常的类型value——异常的信息或者参数tr......
  • vc6,windows 7 x64 调试 (IS2120@BG57IV3)
    //z2012-12-2416:31:[email protected]在windows7x64上调试vc6console程序时,按了shift+f5,程序并不结束。解决方法:替换这个文件TLLOC.dllwww.dr-hoiby.com/TLLOC.dll//z2012-12-2416:31:[email protected]......