代码调试跟踪与优化(一)

您所在的位置:网站首页 gdb调试go程序 代码调试跟踪与优化(一)

代码调试跟踪与优化(一)

2023-08-18 07:48| 来源: 网络整理| 查看: 265

文章目录 前言一、GDB 调试原理1.1 GDB 调试模型1.2 GDB 与被调试程序关系 二、GDB 常用调试命令2.1 断点设置命令2.2 变量堆栈查看命令2.3 执行控制命令 三、VS Code 可视化调试四、GDB 远程调试更多文章:

前言

我们在开发软件时,免不了引入一些Bug,如何快速定位并解决这些bug 呢?工程师调试跟踪解决这些bug 的过程就像是医生给病人体检治病,医生需要借助各种医疗设备检测病人的各项指标,才能诊断病情分析病因并给出治疗方案。工程师解决这些bug,也需要借助各种调试跟踪技术,通过查看当前的执行指令、内存数据、运行日志等信息,分析出产生bug 的可能原因,再给出解决方案。

这些调试跟踪技术,可以帮我们更清晰的了解代码的执行状态,快速找到执行结果与期望结果不相符的起点或原因,比如控制逻辑或业务逻辑出现漏洞、中间结果被意外更改或损坏、低效的性能瓶颈等。

一、GDB 调试原理

我们初学程序设计时,C/C++/Java 这些静态类型语言都需要先经过编译、链接、执行过程,linux 系统常用的编译链接工具是GCC。程序设计免不了出现一些非预期的Bug,要解决这些bug 首先需要我们获得当前代码足够的执行信息,GNU 也提供了GDB 调试工具,供我们查看代码的执行信息,快速定位产生bug 的代码并解决它。

一般我们在程序开发过程中,代码编辑、编译链接、代码调试这几个工具配合使用,所以很多 IDE(Intergreated Development Environment) 常将这几部分封装在一起,比如windows 平台下的Visual Studio、嵌入式开发常用的Keil MDK 等。这些IDE 工具将常用操作以菜单按钮的形式提供,让我们专注于程序开发过程,对我们隐藏了编译链接和代码调试工具的原理,本文以GDB 为例,简单介绍下代码调试原理。

1.1 GDB 调试模型

目前软件开发主要在x86 平台上进行,但我们开发的目标程序有可能在其它平台比如ARM 上运行,为了方便我们在PC 上调试ARM 主机中运行的程序,GDB 根据调试程序和被调试程序是否运行在同一台电脑中,提供了如下两种调试模型:

本地调试:调试程序和被调试程序运行在同一台电脑中。 GDB本地调试模型图示

远程调试:调试程序运行在一台电脑中,被调试程序运行在另一台电脑中。 GDB远程调试模型图示

直接跟用户交互的可视化调试程序常有两种形式:一种是在终端窗口内手动输入调试命令,以字符形式显示调试信息;另一种是在IDE 内点击菜单按钮来代替手动输入调试命令,以图形加字符的形式显示调试信息。前一种形式更方便编写脚本实现自动化调试;后一种形式不需要记忆那么多调试命令,呈现的调试信息更直观、视觉辨识度更高,能提高点调试效率。

远程调试相比本地调试,多了一个GdbServer程序,该程序和目标程序(被调试程序)都运行在目标机(比如一个ARM 主板)中。上图中的红线表示GDB与GdbServer之间通过串口线或者网络进行通讯,用于传输GDB 调试消息的通讯协议可以称为GDB Remote Serial Protocol(GDB RSP)。

GDP RSP 既然是一个通讯协议,自然有标准的报文格式和内容要求,基本的报文格式如下图所示: GDB RSP协议格式 GDP RSP 报文主要包括四个部分,固定的开始字符(’$’)和结束字符(’#’),中间的调试消息数据以及最后的校验和,我们使用GDB 调试工具并不需要了解的那么详细,这里也就不展开介绍协议报文了(若想了解更详细信息,可参阅文档:Howto: GDB Remote Serial Protocol)。

1.2 GDB 与被调试程序关系

不管是本地调试还是远程调试,GDB 调试程序都需要有两个条件:

目标程序代码包含必要的调试信息,比如文件名、函数名、变量名、行号等符号表信息,函数堆栈、寄存器等信息。这些调试信息可以在编译阶段设置编译选项添加,比如gcc 编译工具添加"-g" 选项就可以在可执行文件中添加调试信息。由于带调试信息的可执行文件较大,嵌入式开发中资源受限,软件项目常有Debug 和Release 两个版本,前者供调试跟踪,后者更精简;GDB 可以控制被调试程序的执行,可以访问被调试程序的任何指令和内存数据。比如GDB 可以启动或者接管被调试程序的运行,控制被调试程序在指定条件下停止运行,查看并修改被调试程序的变量值、参数值、执行结果、执行顺序等运行数据。

我们先使用man gdb 命令查看GDB 的简单介绍与用法: man gdb

GDB 主要有三种启动方式:

gdb program:使用GDB 开始执行被调试程序program,可通过GDB 命令控制program 的行为;gdb program core:使用GDB 同时执行被调试程序program 和core 文件(程序异常中止或退出时,保存的内存映像加调试信息文件,包含程序当前的内存、寄存器、堆栈等信息),便于定位分析程序异常中止或退出的原因;gdb attach PID (gdb -p PID):使用GDB 接管(attach)一个正在运行的被调试程序,PID 为被调试程序的process-ID(可通过pidof program 查看),可通过GDB 命令控制program 的行为。

从GDB 与被调试程序间的关系看,GDB 的三种启动方式可以分为两类:一类是由GDB 程序调用执行一个尚未运行的被调试程序program;另一类是由GDB 程序attach 接管一个正在运行的被调试程序program。

前面也谈到,GDB 进程可以控制被调试程序的执行,可以访问被调试程序的任何指令和内存数据。GDB 进程相当于是被调试程序的父进程,GDB 进程对被调试进程program 有绝对的控制权。GDB 是如何调用或者接管一个正在运行的被调试程序呢?

Linux 内核提供了一个用于进程跟踪的系统调用函数ptrace,该函数提供了一个进程(the “tracer”)监察和控制另一个进程(the “tracee”)的方法,它不仅可以监控系统调用,而且还能够检查和改变“tracee” 进程的内存和寄存器里的数据,甚至还可以拦截系统调用。GDB 进程通过系统调用函数ptrace,就可以读写被调试进程program(GDB 进程作为tracer,被调试进程作为tracee)的指令空间、数据空间、堆栈和寄存器的值,接管被调试进程program 的所有信号。这样一来,被调试进程program 的执行就被GDB 进程完全控制了,从而达到调试的目的。

我们使用man ptrace 命令查看ptrace 的简单介绍如下: GDB ptrace 函数原型与描述 我们知道了,GDB 进程借助系统调用函数ptrace 实现对被调试进程的监察和控制,也就好理解GDB 进程是如何调用或者接管被调试进程program 的了。对于尚未运行的被调试程序,启动GDB 调试进程后,该进程会创建一个子进程(linux 系统通过fork 创建子进程)并调用ptrace 函数,ptrace 函数再由第一个参数获知是启动一个尚未运行的进程(传入参数值 PTRACE_TRACEME)还是接管一个正在运行的进程(传入参数值 PTRACE_ATTACH 或 PTRACE_SEIZE)。GDB 进程启动或接管被调试进程test 的过程图示如下(接管正在运行的进程,GDB 向其发送信号SIGSTOP,正在运行的被调试进程就会暂停执行并进入TASK_STOPED状态,等待被调试): GDB 进程通过ptrace 函数启动被调试进程图示

所以,不论是调试一个新程序,还是调试一个已经处于执行中状态的服务程序,通过ptrace系统调用,最终的结果都是:gdb程序是父进程,被调试程序是子进程,子进程的所有信号都被父进程gdb来接管,并且父进程gdb可查看、修改子进程的内部信息,包括:指令空间、数据空间、堆栈、寄存器等。

二、GDB 常用调试命令

如何使用GDB 调试程序呢?GDB 提供了一系列命令,我们可以在启动GDB 进程后通过help 命令查看,GDB 支持的调试命令类别如下: GDB 命令类别 从help 命令的返回结果可以看出,GDB 主要支持12 类调试命令,其中比较常用的有断点设置breakpoints、数据查询data、文件指定与查看files、运行控制running、堆栈查询stack、状态查看status 等六大类。如果想查看某一类具体支持哪些调试命令,可以使用比如help breakpoints 形式的命令查看详情。GDB 支持的调试命令很多,本文只介绍几个最常用的调试命令。

前面已经谈到,要想使用gdb 调试程序,被调试程序需要包含调试信息,如果使用GCC 工具链编译链接程序,则需要添加-g 参数(也可以是-Og 参数)。使用前面介绍的GDB 启动命令,可以根据启动调试后的提示信息判断被调试程序是否包含调试信息:

$ gdb test ...... # 没有调试信息 Reading symbols from test...(no debugging symbols found)...done. # 包含调试信息 Reading symbols from test...done. 2.1 断点设置命令

启动GDB 调试程序后,一般先设置普通断点、观察断点、捕捉断点等,以便在后续调试过程中,让程序及时暂停在我们关注的地方,查看断点处的数据和状态信息。GDB 常用的断点设置命令如下(可通过help breakpoints 查看支持的断点命令列表,可进一步通过help break 查看某个具体命令的用法):

常用断点设置命令命令描述break location在源代码指定设置location 处设置断点,程序执行到location 处暂停执行。location 可以是行号linenum、函数名function,如果不止一个源文件,还可以在前面加上文件名,比如filename:linenum 或 filename:function。break location if cond在源代码指定位置location 处设置条件断点,程序执行到location 处判断条件condition 的真假,若条件condition 为true 则暂停执行,否则继续执行。condition 是一个布尔型表达式,location 含义跟前一条指令中的相同。watch expression在程序执行过程中,监控某个变量或表达式(也即expression)的值,当观察到该变量或表达式的值发生变化时,则程序暂停执行。catch event在程序执行过程中,监控某个事件的触发,比如程序抛出指定类型异常、某动态库被加载或卸载等,当捕捉到该事件发生时,则程序暂停执行。info breakpointsinfo break可以查看当前调试环境中存在的所有断点,包括普通断点、观察断点以及捕捉断点。每个断点都有一个唯一的编号,可根据该编号使能或清除相应的断点。                                  disable [num]禁用当前调试环境中的某个断点,如果没有指定编号num 默认禁用所有断点。enable [num]激活当前调试环境中的某个断点,如果没有指定编号num 默认激活所有断点。clear locationdelete [num]可以删除指定位置location 处或指定编号num 的所有断点,如果没有指定位置location 默认清除编号最小的断点,如果没有指定编号num 默认删除所有断点。

下面给出这几个命令的简单示例:

...... Reading symbols from test...done. (gdb) list #--> 显示带行号的源代码 1 #include 2 3 int add2(int a, int b) 4 { 5 return (a + b); 6 } 7 8 int main (int argc, char *argv[]) 9 { 10 int n = 1; (gdb) #--> 默认显示10行代码,可按Enter 健查看后续代码 11 int sum = 0; 12 13 while (n 在第13行设置普通断点 Breakpoint 1 at 0x40157f: file test.c, line 11. (gdb) break add2 #--> 在函数add2处设置普通断点 Breakpoint 2 at 0x40155a: file test.c, line 5. (gdb) tbreak 16 #--> 在第16行设置一个一次性普通断点,常用于循环中 Temporary breakpoint 3 at 0x401598: file test.c, line 16. (gdb) watch n #--> 设置变量n 为观察断点 No symbol "n" in current context. #--> 当前调试程序未运行,故当前调试环境没有符号n,需要先运行到符号n 定义后再设置其为观察断点 (gdb) run #--> 运行被调试程序到第一个断点处暂停 Starting program: D:\VSCoder\GDB\test.exe [New Thread 10816.0x42a8] [New Thread 10816.0x2b68] Thread 1 hit Breakpoint 1, main (argc=1, argv=0x1914a0) at test.c:11 11 int sum = 0; (gdb) watch n #--> 设置变量n 为观察断点 Hardware watchpoint 4: n (gdb) info break #--> 查看当前调试环境中的所有断点,Num 为唯一编号、Type 为断点类型、Disp 表示断点触发后保留还是删除,Enb 表示断点处于激活还是禁用状态,Address 显示断点在内存中的地址信息,What 显示断点所在文件与行号信息 Num Type Disp Enb Address What 1 breakpoint keep y 0x000000000040157f in main at test.c:11 breakpoint already hit 1 time 2 breakpoint keep y 0x000000000040155a in add2 at test.c:5 3 breakpoint del y 0x0000000000401598 in main at test.c:16 4 hw watchpoint keep y n (gdb) disable 1 #--> 禁用编号为1 的断点 (gdb) info break Num Type Disp Enb Address What 1 breakpoint keep n 0x000000000040157f in main at test.c:11 breakpoint already hit 1 time ...... (gdb) enable 1 #--> 激活编号为1 的断点 (gdb) info break Num Type Disp Enb Address What 1 breakpoint keep y 0x000000000040157f in main at test.c:11 breakpoint already hit 1 time ...... (gdb) clear #--> 清除编号最小的断点 Deleted breakpoint 1 (gdb) info break Num Type Disp Enb Address What 2 breakpoint keep y 0x000000000040155a in add2 at test.c:5 3 breakpoint del y 0x0000000000401598 in main at test.c:16 4 hw watchpoint keep y n (gdb) delete #--> 删除所有的断点 Delete all breakpoints? (y or n) y (gdb) info break No breakpoints or watchpoints. (gdb) 2.2 变量堆栈查看命令

在被调试程序代码中设置断点,实际上就是让被调试程序暂停在我们关注的断点位置,一般断点处也是我们怀疑程序存在bug或性能瓶颈的地方。

当被调试程序停在断点处后,我们需要查看当前调试环境的状态与数据信息,比如变量当前值、调用堆栈信息、当前寄存器值等。GDB 常用的变量或堆栈信息查询命令如下:

常用变量堆栈查询命令命令描述print expression在 GDB 调试程序的过程中,输出或者修改指定变量或者表达式的值。display exprdisplay/fmt expr在调试阶段查看某个变量或表达式的值,与print 命令的区别是,每次被调试程序暂停执行时,display 都会自动显示变量或表达式的值,print 则不会。info display可以查看当前调试环境中存在的所有display 变量或表达式,每个display 变量或表达式都有唯一的编号。undisplay num删除指定编号num 的display 变量或表达式,如果没有指定编号num 默认删除所有display 变量或表达式。                          frame spec可以查看当前调试环境中特定的栈帧信息,spec 可以是栈帧编号、栈帧地址、函数名等,如果不指定参数spec 则显示当前栈帧信息。up ndown n在当前栈帧的基础上(假设当前栈帧编号为m),查看编号为m + n 或m - n 的栈帧信息。如果不指定参数n 则默认n = 1。backtrace查看当前调试环境中所有的栈帧信息,也即从当前栈帧到main 函数的整个函数调用链信息。info argsinfo locals查看当前函数参数的值查看当前函数内局部变量的信息info frame可以查看当前栈帧中存储的详细信息,包括当前栈帧的编号及地址、当前函数及其调用者的地址、当前函数编程语言类型、当前函数参数和局部变量的地址及其值、当前栈帧寄存器值等。

下面给出这几个命令的简单示例:

...... (gdb) break 13 Breakpoint 1 at 0x401586: file test.c, line 13. (gdb) break add2 Breakpoint 2 at 0x40155a: file test.c, line 5. (gdb) print n # --> 打印变量n 的当前值 No symbol "n" in current context. # --> 当前调试程序未运行,故当前调试环境没有符号n,需要先运行到符号n 定义后再查看其当前值 (gdb) run #--> 运行被调试程序到第一个断点处暂停 Starting program: D:\VSCoder\GDB\test.exe [New Thread 10392.0x3b60] [New Thread 10392.0x1c54] Thread 1 hit Breakpoint 1, main (argc=1, argv=0x7814a0) at test.c:13 warning: Source file is more recent than executable. 13 while (n 打印变量n 的当前值 $1 = 1 (gdb) display sum # --> 保持显示变量sum 的当前值 1: sum = 0 (gdb) info display # --> 查看当前调试环境中的所有display 变量或表达式信息 Auto-display expressions now in effect: Num Enb Expression 1: y sum (gdb) undisplay # --> 删除当前调试环境中所有的display 变量或表达式 Delete all auto-display expressions? (y or n) y (gdb) print n=5 # --> 修改变量n 的当前值为5 $2 = 5 (gdb) info locals # --> 打印当前函数内的所有局部变量的当前值 n = 5 sum = 0 (gdb) frame # --> 打印当前栈帧信息 #0 main (argc=1, argv=0x9814a0) at test.c:13 13 while (n 打印当前栈帧编号减一的栈帧信息 Bottom (innermost) frame selected; you cannot go down. (gdb) continue # --> 继续执行被调试程序到下一个断点处暂停 Continuing. Thread 1 hit Breakpoint 2, add2 (a=0, b=5) at test.c:5 5 return (a + b); (gdb) backtrace # --> 打印当前调试环境中的所有栈帧信息,也即当前函数调用链信息 #0 add2 (a=0, b=5) at test.c:5 #1 0x0000000000401595 in main (argc=1, argv=0x9814a0) at test.c:15 (gdb) info args # --> 打印当前函数的所有参数值 a = 0 b = 5 (gdb) up # --> 打印当前栈帧编号加一的栈帧信息 #1 0x0000000000401595 in main (argc=1, argv=0x9814a0) at test.c:15 15 sum = add2(sum, n); (gdb) down # --> 打印当前栈帧编号减一的栈帧信息 #0 add2 (a=0, b=5) at test.c:5 5 return (a + b); (gdb) info frame # --> 打印当前栈帧的详细信息 Stack level 0, frame at 0x61fdf0: #--> 当前栈帧编号为0,地址为0x61fdf0 rip = 0x40155a in add2 (test.c:5); saved rip = 0x401595 #--> 当前函数的存储地址为0x40155a,它的调用者的存储地址为0x401595 called by frame at 0x61fe30 #-->当前函数调用者的栈帧地址为0x61fe30 source language c. #--> 当前函数使用C语言编写的 Arglist at 0x61fde0, args: a=0, b=5 #--> 当前函数的参数地址和参数值 Locals at 0x61fde0, Previous frame's sp is 0x61fdf0 #--> 当前函数内的局部变量存储地址 Saved registers: rbp at 0x61fde0, rip at 0x61fde8, #--> 当前栈帧内部存储的寄存器信息 2.3 执行控制命令

为被调试程序设置了需要特别关注的断点,也有了可以查看甚至修改当前调试环境变量值、堆栈信息、寄存器值等数据的命令,还需要能自由控制被调试程序执行顺序的命令,方便进行单步调试或断点调试。GDB 常用的执行控制命令如下:

常用执行控制命令命令描述startrun都可以用来在 GDB 调试器中启动被调试程序,start 命令执行到main 函数起始位置暂停,run 命令执行到第一个断点处暂停。next countstep count都可以控制GDB 单步执行程序,count 为执行步数缺省值为1 行。执行到函数调用代码时,next 命令不考虑函数内部代码行数,将函数调用记作一行,step 命令计算调用函数内部代码的行数。continue count控制被调试程序执行到往下数第count 个断点处暂停,也即忽略count - 1 个断点,参数count 缺省值为1,也即执行下一个断点处暂停。until location                        控制被调试程序执行到特定位置location 处暂停,如果省略参数location,则执行到循环体外暂停,若无循环体则同next 命令。

下面给出这几个命令的简单示例:

...... (gdb) break 13 Breakpoint 1 at 0x115b: file test.c, line 13. (gdb) start # --> 控制GDB 开始执行被调试程序,并在main 函数起始位置暂停 Temporary breakpoint 2 at 0x1141: file test.c, line 9. Starting program: /home/paul/Desktop/GDB/test Temporary breakpoint 2, main () at test.c:9 9 { (gdb) continue # --> 继续执行被调试程序到下一个断点处暂停 Continuing. Breakpoint 1, main () at test.c:13 13 while (n 打印当前函数内的所有局部变量的当前值 n = 1 sum = 0 (gdb) step 7 # --> 控制被调试程序继续执行7 行,计算被调函数add2 内的行数 15 sum = add2(sum, n); (gdb) info locals n = 2 sum = 1 (gdb) next 7 # --> 控制被调试程序继续执行7 行,被调函数add2 按照一行计算 16 n++; (gdb) info locals n = 4 sum = 10 (gdb) next 13 while (n 控制被调试程序执行到当前循环体外暂停 19 return 0; (gdb) info locals n = 101 sum = 5050 (gdb) quit # --> 退出GDB 调试进程 A debugging session is active. Inferior 1 [process 5867] will be killed. Quit anyway? (y or n) y

上述GDB 调试命令都可以使用缩写形式,比如使用b 代替break、使用c 代替continue、使用bt 代替backtrace 等,不用每次输入命令全称。

三、VS Code 可视化调试

使用GDB 命令来调试程序并没有那么直观,我们对可视化的图像信息更敏感些,因此很多IDE 都提供了可视化调试界面。这里使用比较轻巧的代码编辑器VS Code 搭配GCC 和GDB 工具链,实现可视化调试功能。

一般一个软件工程包含不止一个源文件,多个源文件的编译链接通常靠Makefile 控制。使用VS Code 编辑好工程代码和Makefile 文件后,可以在VS Code 启动配置文件launch.json 中配置GDB 调试器和预启动任务preLaunchTask,在任务配置文件tasks.json 中配置执行Makefile 文件中定义的make default 命令,实现对目标工程的可视化调试。

这里使用博文:VSCode+GCC+Makefile项目管理 中的示例代码,考虑到GNU工具链对Linux 系统支持更好,本文使用Ubuntu 20.04系统构建VS Code 可视化调试工程。

原博文是基于windows 系统编写的Makefile 文件和launch.json、tasks.json 配置文件,这里使用Linux 系统需要修改几个地方:

将Makefile 文件中的mingw32-make 改为make;将tasks.json 文件中的mingw32-make 改为make;将launch.json 文件中的"miDebuggerPath" 值改为 “/usr/bin/gdb”;将launch.json 文件中的"externalConsole"值改为 false(可选,设为false后使用VS Code 终端界面进行交互);

配置好VS Code 后,进入“Run and Debug” 窗口界面,先设置断点和观察表达式,变量和调用栈帧信息会自动显示在想要区域,设置好断点和观察点后,点击“Start Debugging" 按钮开始执行调试任务,界面如下: VS Code可视化调试界面 VS Code 不仅支持可视化变量、断点、堆栈信息列表显示,还支持执行GDB 命令以获得更多信息。继续执行到函数add 内的断点处,在DEBUG CONSOLE 界面执行命令“-exec info frame” 获得当前栈帧详细信息,图示如下: VS Code可视化调试控制台

四、GDB 远程调试

GDB 调试模型有本地调试和远程调试两种,前面主要基于本地调试介绍的,我们在使用服务器编译或嵌入式调试等场景,就需要在PC 端调试远程服务器或嵌入式板子上的程序了,如何进行GDB 远程调试呢?

在博文:LwIP开发调试环境搭建 中已经展示了在windows 系统上调试虚拟机qemu-vexpress-a9 内运行的程序,调试界面如下: GDB 远程调试 这里简单解释下GDB 远程调试的命令交互过程,首先执行脚本qemu-dbg.bat 启动qemu-vexpress-a9 虚拟机,主要命令如下:

# qemu-dbg.bat ...... start qemu-system-arm -M vexpress-a9 -kernel rtthread.elf -serial stdio -sd sd.bin -S -s

qemu-dbg 相比qemu 脚本主要多出来两个参数-S 和-s,这两个参数是什么意思呢?我们通过命令帮助查询如下:

C:\Users\paul>qemu-system-arm -h ...... -S freeze CPU at startup (use 'c' to start execution) ...... -s shorthand for -gdb tcp::1234 ......

从命令帮助可知,qemu-system-arm -s 参数可以让qemu-vexpress-a9 虚拟机在tcp::1234 端口启动gdbserver 服务,相当于在虚拟机内执行了“arm-none-eabi-gdbserver localhost:1234 rtthread.elf” 命令(虚拟机与宿主机共用IP 即localhost,ARM Cortex-A9 使用的编译器为arm-none-eabi-gcc,选用gdbserver 版本应与编译器架构一致)。

启动qemu-vexpress-a9 虚拟机时,通过-s 参数启动了gdbserver 服务,接下来在宿主机内执行如下命令:

$ arm-none-eabi-gdb #--> 执行ARM 架构gdb 命令 ...... (gdb) target remote localhost:1234 #--> 连接远程目标gdbserver 服务器,IP:Port 为localhost:1234 Remote debugging using localhost:1234 0x600100d0 in ?? () (gdb) file rtthread.elf #--> 从本地读取调试信息 A program is being debugged already. Are you sure you want to change the file? (y or n) y Reading symbols from rtthread.elf...done. (gdb) break main #--> 在main 函数起始位置设置断点 Breakpoint 1 at 0x60010048: file applications\main.c, line 7. (gdb) continue #--> 继续执行到下一个断点处暂停,也即main 函数起始位置 Continuing. Breakpoint 1, main () at applications\main.c:7 7 printf("hello rt-thread\n"); (gdb) backtrace #--> 查看当前调试环境所有堆栈信息 #0 main () at applications\main.c:7 (gdb) info frame #--> 查看当前堆栈详细信息 Stack level 0, frame at 0x600b9780: pc = 0x60010048 in main (applications\main.c:7); saved pc = 0x60012884 source language c. Arglist at 0x600b977c, args: Locals at 0x600b977c, Previous frame's sp is 0x600b9780 Saved registers: r11 at 0x600b9778, lr at 0x600b977c (gdb) quit #--> 退出远程调试进程 A debugging session is active. Inferior 1 [Remote target] will be detached. Quit anyway? (y or n) y Detaching from program: E:\RT_Thread\rtthread_source\qemu-vexpress-a9\rtthread.elf, Remote target Ending remote debugging. 更多文章: 《代码调试跟踪与优化(二)— 如何调试嵌入式代码?》《VSCode+GCC+Makefile+GitHub项目管理》《LwIP开发调试环境搭建》《原来gdb的底层调试原理这么简单》


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3