应用调试方法

1,170 阅读5分钟

此文章为学习笔记,来源于正点原子的资料

法一:使用gdb与gdbserver

在嵌入式系统中调试时,一般在PC端运行gdb工具,源码也在PC端,源码对应的可执行文件放到开发板中运行。为此我们需要在开发板上运行gdbserver,通过网络与PC端的gdb进行通信。所以,要想在PC端通过gdb调试嵌入式程序,那么需要两件东西:gdb和gdbserver,其中gdb是运行在PC上的,gdbserver需要我们移植到开发板上。

1.1 gdb移植

一般交叉编译已经自带了 gdb 和 gdbserver,因此可以不用移植,直接使用交叉编译器自带 的即可。比如本教程所使用的 arm-linux-gnueabihf-gcc 就自带了主机使用的 arm-linux-gnueabihfgdb 和开发板所使用的 gdbserver。进入 ubuntu 中交叉编译器安装目录中的 bin 文件夹下,比如 笔者的就是/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin,此目录中就包 含了 arm-linux-gnueabihf-gdb 和 gdbserver,如图所示:

交叉编译自带的 gdb 和 gdbserver
交叉编译自带的 gdb 和 gdbserver

如果交叉编译器自带了 gdb 和 gdbserver 的话只需要将 gdbserver 拷贝到开发板根文件系统的/bin 目录下,如果交叉编译器没有自带 gdb 和 gdbserver 那就需要自己手动编译了,继续看本节即可。

1.2 使用GDB进行程序调试

1.2.1 编写一个简单的测试程序

#include <stdio.h>
#include <unistd.h>

int main(int argc,char *argv[])
{
  unsigned int times = 0;  
  while(1)
  {
     printf("runing times:%d\r\n", times);
     times++;
     sleep(1);
  }
}

使用arm-linux-gnueabihf交叉编译gdbtest.c文件,要想调试程序,那么编译时必须加上“-g”选项,这样编译处理的可执行文件才带有调试信息。编译命令为:

arm-linux-gnueabihf-gcc gdbtest.c -o gdbtest -g //编译测试程序,注意-g 选项

编译完成以后将得到的 gdbtest 可执行文件发送到开发板中

1.2.3 gdb调试程序

一切准备就绪以后就可以使用gdb进行调试了,确保ubuntu和开发板可以进行网络通信,在开发板在输入以下命令

gdbserver 192.168.1.253:2001 gdbtest //启动开发板上的 gdbserver

上述命令中192.168.1.253表示ubuntu的IP地址,2001是端口号,可以任意给一个端口号, gdbtest 是要调试的可执行文件。输入以后开发板输出信息如图所示: 开发板上gdbserver运行 接着在 ubuntu 中输入如下命令启动 gdb 调试工具:

arm-linux-gdb gdbtest

运行结果: 在上图最下面的(gdb)输入命令连接到开发板上:

target remote 192.168.1.251:2001 //连接到开发板上

其中192.68.1.251是开发板IP地址,2001就是开发板gbdsever设置的端口号。连接成功后开发板中的gdbserver就会提示连接信息。如下图: 从图中我们可以看出,ubuntu地址为192.168.1.253,连接成功之后我们就可以在ubuntu上进行代码调试了,gdb工具是一个基于命令行的调试工具,下面我们来学一下几个常用命令:

  • l命令(list) 用于列出所有的程序源码,输入“l”,结果如图所示: 从图中我们看出,输入“l”命令之后会打印出调试程序的所有源码,如果源码没有打印完就重复按下“l”命令,或者按下回车键,gdb调试工具中回车键表示重复上一个命令。

  • b命令(break):用来设置断点,也可以用缩写“b”,后面可以跟具体的函数或者行号,比如 “break main”表示在 main 函数处设置断点,“break 11”在第 11 行设置断点。输入如下命令, 在 main 函数处设置断点:

b main 或 break main

设置以后有下图: 从图可以看出,断点 1 设置到了 gdbtest.c 的第 6 行,第 6 行正好是 main 函数的起始处。

  • c命令: c 命令用于运行到断点出,输入 c 命令程序就会运行,直到下一个断点处,如图所示: 从图 可以看出,当前程序运行到第 6 行停止,因为我们前面在第 6 行放置了一个断点,因此程序运行停止。继续输入“c”命令,程序就会持续不断的运行,开发板中就会不断的打印出信息,如图所示:
  • s命令:(step)单步运行,此函数会进入函数内
  • n命令: (next)也是单步运行,但是n命令不会进入函数内部
  • p命令:(print)用来打印某个变量的值
  • q命令:退出调试
  • gdb调试多进程
    • 单独调试子进程,使用attach "PID"单独调试
    • 使用调试器选项follow-fork-mode选项,设置调试父进程还是子进程,如set follow-fork-mode child
  • gdb调试多进程:
    • info threads显示当前可调试的所有线程,gdb会为每个线程分配一个ID
    • thread ID 调试指定的目标ID的线程
    • set scheduler-locking[off|on|step] 设置被调试线程的模式,on 表示只有当前被调试的线程会继续执行,off 表示不锁定任何线程,其他线程都可以继续执行,step 表示在单步执行的时候,只有当前线程会执行

法二:使用strace命令调试

法三:修改内核打印用户段错误信息

后续补充

本文使用 mdnice 排版