5990
- 收藏
- 点赞
- 分享
- 举报
嵌入式linux下使用GDB调试程序
无论是多么资深的程序员在编写的程序时,都不大可能一次性就会成功,在程序运行时,会出现许许多多意想不到的错误,一味地只是查看程序用处不大,最有效的方法通过一些手段进入到程序内部进行调试。通常在调试程序的时候如果能够得到以下一些信息,对于开发者找到错误所在是很有帮助的。
1. 程序是运行到哪个语句或者表达式就发生了错误?
2. 如果错误是在执行一个函数的时候出现的,那么是程序的哪一行包含了这个函数的调用语句,在调用该函数的时候传递的实参是什么?
3. 在程序执行到某处时,所关心的某一个变量值为多少?
4.某个表达式最终运行的结果为何值?
调试器(更准确地说应该称为符号调试器)能够完成上述目标。它是一个能够运行其他程序的应用程序,它和普通意义上的程序的唯一不同之处在于,调试器能够进入到程序源码中,允许开发者进行逐行单步运行,了解程序代码执行顺序,和每条语句执行的结果,可以在程序运行的同时,查看甚至是改变任一变量值。在程序运行出错时,它为程序开发者提供程序运行时的详细细节,从而找到出错的原因。在Linux系统中,最常用到的就是GDB(GNU Degugger)。GDB是GNU自带的调试工具。
GDB常用命令
要想使用gdb,必须在对源码进行编译的时候,使用-g编译选项开关,来通知编译器,开发者希望进行程序调试。用了-g选项后,程序在编译的时候就会包含调试信息,这些调试信息存在目标文件中,它描述了每个函数或变量的数据类型以及源码行号和可执行代码地址间对应关系,gdb正是通过这些信息使源码和机器码相关联的,它实现了源码级的调试。
为了使用gdb调试,只需要在命令行中输入gdb filename(filename是用gcc编译生成的最终可执行文件名),该语句启动与调试器的文本接口。就在上一小节中所举makefile例子来说,就是键入gdb tune1,则在屏幕上会出现
[nie@uClinux mysrc]$ gdb tune1
GNU gdb Red Hat Linux 7.x (5.0rh-15) (MI_OUT)
Copyright 2001 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
(gdb)
gdb虽然运行起来,但是可执行程序tune1并没有运行,此时在gdb提示符下直接键入run命令即可,如果可执行程序在运行的时候需要输入命令行参数,则在gdb提示符下可以这样键入命令:run command-line-arguments ,就如同是输入命令:tune1 command-line-arguments一样,启动了可执行程序的运行。
有时候,我们希望能够断点调试程序,让程序执行到代码某处时停止继续执行下去,此时可以使用命令break,该命令的格式为break place,这里place可以是程序代码的行号,某函数名,甚至可以是用break main ,让程序断点设置在代码一开始执行的地方,比如对于上面举的可执行文件名为tune1的例子,它调用了一个函数名为rtExtModeCheckInit的子函数,如果想让程序执行到该函数处停止,可以在gdb提示符下输入:break rtExtModeCheckInit,此时屏幕上出现下列信息:Breakpoint 1 at 0x8049a28: file grt_main.c, line 604.。当然,也可以使用行号设置中断位置,上面设置中断的语句可以等价为break 604,可以在屏幕上看到相同的效果。
当设置了断点后,程序会运行到断点处停下来,此时从屏幕上可以得到类似下面的信息:
Breakpoint 1, main (argc=4, argv=0xbffffb84) at grt_main.c:604
604 rtExtModeCheckInit();
(gdb)
当想将某个断点除去,可以在gdb提示符下输入命令:delete N,这里N表示第几个中断,第一个设置的中断序号为1,第二个设置的序号为2,依次类推。如果delete后不跟任何序号,在表示把设置的所有断点都删除。如果想查看目前设置断点的情况,可以使用命令info break,屏幕会显示出每一个设置的断点信息。
在gdb提示符下使用help命令,会给出有关gdb命令的一个简短描述和命令分类。
如果开发者想进入到程序内部进行单步调试,gdb提供两种命令供选择,step和next命令,两者的区别在于step执行每一条语句,如果遇到函数调用,会跳转到到该函数定义的开始行去执行,而next则不进入到函数内部,它把函数调用语句当作普通一条语句执行完成。continue命令是继续运行程序,直到遇到下一个断点或程序结束。
有时候使用者仅仅是在linux的bash提示符下输入命令gdb后,启动了gdb而已,此时,如果要加载可执行文件,需要在gdb提示符下键入命令:file filename(filename为可执行文件名),注意是可执行文件的名字而不是源文件名。
当在调试过程中,想查看一个变量值的时候,可以在gdb环境下输入命令:watch variablename ,这里的variablename是你想观察的变量名。
还有一个可以显示表达式值的命令print,其使用规则为print expressionname,其中expressionname为要显示的表达式名。
GDB具体调试实例
下面通过使用一个简单的程序使读者进一步熟悉用gdb的调试方法。
源程序名为example1.c,代码如下:
/*******************************************************
* Institute of Automation, Chinese Academy of Sciences
* File Name: example.c
* Description: introduce how to use gdb
* Author: Xueyuan Nie
* Date:
*******************************************************/
#include
static void display(int i, int *ptr);
int main(void)
{
int x = 5;
int *xptr = &x;
printf("In main():\n");
printf(" x is %d and is stored at %p.\n", x, &x);
printf(" xptr holds %p and points to %d.\n", xptr, *xptr);
display(x, xptr);
return 0;
}
void display(int z, int *zptr)
{
printf("In display():\n");
printf(" z is %d and is stored at %p.\n", z, &z);
printf(" zptr holds %p and points to %d.\n", zptr, *zptr);
}
要使用gdb调试程序,一定要在编译程序时,使用-g编译选项,以生成参数符号表( augmented symbol table),提供调试信息。
首先使用gcc –g –o example1 example.c对源代码进行编译,这样就可以使用gdb监视example1的执行细节。在bash提示符下,键入命令:gdb example1,启动了对可执行文件example1 的调试,在屏幕上会出现下面的信息:
[nie@uClinux nie]$ gdb example1
GNU gdb Red Hat Linux 7.x (5.0rh-15) (MI_OUT)
Copyright 2001 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
(gdb)
最后一行(gdb)就是进入到gdb调试中的提示符,此时可以在提示符下输入任何想键入的命令。
现在如果要进行断点调试的话,就需要显示一下要调试的源码,以便知道在哪个地方进行断点设置。在gdb下,Linux最常用文本编辑命令vi不能使用,可以使用list命令列出可执行文件的源代码的一部分,为了列出源代码的全部,只要多键入几次list命令即可。具体操作如下:
(gdb) list
1 #include
2 static void display(int i, int *ptr);
3
4 int main(void) {
5 int x = 5;
6 int *xptr = &x;
7 printf("In main():\n");
8 printf(" x is %d and is stored at %p.\n", x, &x);
9 printf(" xptr holds %p and points to %d.\n", xptr, *xptr);
10 display(x, xptr);
(gdb) list
11 return 0;
12 }
13
14 void display(int z, int *zptr) {
15 printf("In display():\n");
16 printf(" z is %d and is stored at %p.\n", z, &z);
17 printf(" zptr holds %p and points to %d.\n", zptr, *zptr);
18 }
(gdb) list
Line number 19 out of range; example1.c has 18 lines.
(gdb)
屏幕上清楚显示出了每一个语句所在的具体行号,比如现在我们想在第五行设置断点,可以在gdb提示符下输入命令:break 5,可以看到下面的显示信息:
(gdb) break 5
Breakpoint 1 at 0x8048466: file example1.c, line 5.
断点已经设置好,现在开始让程序运行起来,键入命令run,也可以键入其缩写形式r,屏幕上出现的信息如下:
(gdb) r
Starting program: /home/nie/example1
Breakpoint 1, main () at example1.c:5
5 int x = 5;
上述信息表明,gdb已经开始执行可执行程序,目前程序运行到example1.c程序中main()函数的第五行处停止,并且显示出即将要执行的第五行语句。
现在我们进行单步调试的工作,输入命令:next,它表明单步执行程序的每一条语句,当用next命令执行到函数display处时,即当屏幕出现如下所示信息时:
(gdb) next
6 int *xptr = &x;
(gdb) next
7 printf("In main():\n");
(gdb) next
In main():
8 printf(" x is %d and is stored at %p.\n", x, &x);
(gdb) next
x is 5 and is stored at 0xbffffb44.
9 printf(" xptr holds %p and points to %d.\n", xptr, *xptr);
(gdb) next
xptr holds 0xbffffb44 and points to 5.
10 display(x, xptr);
为了进入到函数display内部进行调试,输入命令step,即:
(gdb) step
display (z=5, zptr=0xbffffb44) at example1.c:15
15 printf("In display():\n");
step命令使执行进入到函数内部,此时在该函数内部,可以继续使用step命令或者是next命令进行单步执行,如果不想单步执行,而是直接将程序一次执行完毕,可以输入命令continue即可。
要退出gdb,请键入命令quit,如果程序此时仍在进行,gdb会让你确认是否真的要退出,屏幕会出现类似下面的提示信息:
(gdb) quit
The program is running. Exit anyway? (y or n)
按下'y' 即退出调试程序,如果程序本身已经运行完毕,则quit命令键入后,会直接退出gdb,而不出现任何提示信息。
当然除了使用gdb进行程序调试外,如果程序比较简短,逻辑又比较简单,此时完全可以不用gdb,采用printf语句在程序当中输出中间变量的值来调试程序,也是一个不错的调试方法。
到此为止,我们已经介绍了uClinux操作系统,GNU工具的使用,有了这些预备知识后,我们将进入到本章的重点内容了。
1. 程序是运行到哪个语句或者表达式就发生了错误?
2. 如果错误是在执行一个函数的时候出现的,那么是程序的哪一行包含了这个函数的调用语句,在调用该函数的时候传递的实参是什么?
3. 在程序执行到某处时,所关心的某一个变量值为多少?
4.某个表达式最终运行的结果为何值?
调试器(更准确地说应该称为符号调试器)能够完成上述目标。它是一个能够运行其他程序的应用程序,它和普通意义上的程序的唯一不同之处在于,调试器能够进入到程序源码中,允许开发者进行逐行单步运行,了解程序代码执行顺序,和每条语句执行的结果,可以在程序运行的同时,查看甚至是改变任一变量值。在程序运行出错时,它为程序开发者提供程序运行时的详细细节,从而找到出错的原因。在Linux系统中,最常用到的就是GDB(GNU Degugger)。GDB是GNU自带的调试工具。
GDB常用命令
要想使用gdb,必须在对源码进行编译的时候,使用-g编译选项开关,来通知编译器,开发者希望进行程序调试。用了-g选项后,程序在编译的时候就会包含调试信息,这些调试信息存在目标文件中,它描述了每个函数或变量的数据类型以及源码行号和可执行代码地址间对应关系,gdb正是通过这些信息使源码和机器码相关联的,它实现了源码级的调试。
为了使用gdb调试,只需要在命令行中输入gdb filename(filename是用gcc编译生成的最终可执行文件名),该语句启动与调试器的文本接口。就在上一小节中所举makefile例子来说,就是键入gdb tune1,则在屏幕上会出现
[nie@uClinux mysrc]$ gdb tune1
GNU gdb Red Hat Linux 7.x (5.0rh-15) (MI_OUT)
Copyright 2001 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
(gdb)
gdb虽然运行起来,但是可执行程序tune1并没有运行,此时在gdb提示符下直接键入run命令即可,如果可执行程序在运行的时候需要输入命令行参数,则在gdb提示符下可以这样键入命令:run command-line-arguments ,就如同是输入命令:tune1 command-line-arguments一样,启动了可执行程序的运行。
有时候,我们希望能够断点调试程序,让程序执行到代码某处时停止继续执行下去,此时可以使用命令break,该命令的格式为break place,这里place可以是程序代码的行号,某函数名,甚至可以是用break main ,让程序断点设置在代码一开始执行的地方,比如对于上面举的可执行文件名为tune1的例子,它调用了一个函数名为rtExtModeCheckInit的子函数,如果想让程序执行到该函数处停止,可以在gdb提示符下输入:break rtExtModeCheckInit,此时屏幕上出现下列信息:Breakpoint 1 at 0x8049a28: file grt_main.c, line 604.。当然,也可以使用行号设置中断位置,上面设置中断的语句可以等价为break 604,可以在屏幕上看到相同的效果。
当设置了断点后,程序会运行到断点处停下来,此时从屏幕上可以得到类似下面的信息:
Breakpoint 1, main (argc=4, argv=0xbffffb84) at grt_main.c:604
604 rtExtModeCheckInit();
(gdb)
当想将某个断点除去,可以在gdb提示符下输入命令:delete N,这里N表示第几个中断,第一个设置的中断序号为1,第二个设置的序号为2,依次类推。如果delete后不跟任何序号,在表示把设置的所有断点都删除。如果想查看目前设置断点的情况,可以使用命令info break,屏幕会显示出每一个设置的断点信息。
在gdb提示符下使用help命令,会给出有关gdb命令的一个简短描述和命令分类。
如果开发者想进入到程序内部进行单步调试,gdb提供两种命令供选择,step和next命令,两者的区别在于step执行每一条语句,如果遇到函数调用,会跳转到到该函数定义的开始行去执行,而next则不进入到函数内部,它把函数调用语句当作普通一条语句执行完成。continue命令是继续运行程序,直到遇到下一个断点或程序结束。
有时候使用者仅仅是在linux的bash提示符下输入命令gdb后,启动了gdb而已,此时,如果要加载可执行文件,需要在gdb提示符下键入命令:file filename(filename为可执行文件名),注意是可执行文件的名字而不是源文件名。
当在调试过程中,想查看一个变量值的时候,可以在gdb环境下输入命令:watch variablename ,这里的variablename是你想观察的变量名。
还有一个可以显示表达式值的命令print,其使用规则为print expressionname,其中expressionname为要显示的表达式名。
GDB具体调试实例
下面通过使用一个简单的程序使读者进一步熟悉用gdb的调试方法。
源程序名为example1.c,代码如下:
/*******************************************************
* Institute of Automation, Chinese Academy of Sciences
* File Name: example.c
* Description: introduce how to use gdb
* Author: Xueyuan Nie
* Date:
*******************************************************/
#include
static void display(int i, int *ptr);
int main(void)
{
int x = 5;
int *xptr = &x;
printf("In main():\n");
printf(" x is %d and is stored at %p.\n", x, &x);
printf(" xptr holds %p and points to %d.\n", xptr, *xptr);
display(x, xptr);
return 0;
}
void display(int z, int *zptr)
{
printf("In display():\n");
printf(" z is %d and is stored at %p.\n", z, &z);
printf(" zptr holds %p and points to %d.\n", zptr, *zptr);
}
要使用gdb调试程序,一定要在编译程序时,使用-g编译选项,以生成参数符号表( augmented symbol table),提供调试信息。
首先使用gcc –g –o example1 example.c对源代码进行编译,这样就可以使用gdb监视example1的执行细节。在bash提示符下,键入命令:gdb example1,启动了对可执行文件example1 的调试,在屏幕上会出现下面的信息:
[nie@uClinux nie]$ gdb example1
GNU gdb Red Hat Linux 7.x (5.0rh-15) (MI_OUT)
Copyright 2001 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
(gdb)
最后一行(gdb)就是进入到gdb调试中的提示符,此时可以在提示符下输入任何想键入的命令。
现在如果要进行断点调试的话,就需要显示一下要调试的源码,以便知道在哪个地方进行断点设置。在gdb下,Linux最常用文本编辑命令vi不能使用,可以使用list命令列出可执行文件的源代码的一部分,为了列出源代码的全部,只要多键入几次list命令即可。具体操作如下:
(gdb) list
1 #include
2 static void display(int i, int *ptr);
3
4 int main(void) {
5 int x = 5;
6 int *xptr = &x;
7 printf("In main():\n");
8 printf(" x is %d and is stored at %p.\n", x, &x);
9 printf(" xptr holds %p and points to %d.\n", xptr, *xptr);
10 display(x, xptr);
(gdb) list
11 return 0;
12 }
13
14 void display(int z, int *zptr) {
15 printf("In display():\n");
16 printf(" z is %d and is stored at %p.\n", z, &z);
17 printf(" zptr holds %p and points to %d.\n", zptr, *zptr);
18 }
(gdb) list
Line number 19 out of range; example1.c has 18 lines.
(gdb)
屏幕上清楚显示出了每一个语句所在的具体行号,比如现在我们想在第五行设置断点,可以在gdb提示符下输入命令:break 5,可以看到下面的显示信息:
(gdb) break 5
Breakpoint 1 at 0x8048466: file example1.c, line 5.
断点已经设置好,现在开始让程序运行起来,键入命令run,也可以键入其缩写形式r,屏幕上出现的信息如下:
(gdb) r
Starting program: /home/nie/example1
Breakpoint 1, main () at example1.c:5
5 int x = 5;
上述信息表明,gdb已经开始执行可执行程序,目前程序运行到example1.c程序中main()函数的第五行处停止,并且显示出即将要执行的第五行语句。
现在我们进行单步调试的工作,输入命令:next,它表明单步执行程序的每一条语句,当用next命令执行到函数display处时,即当屏幕出现如下所示信息时:
(gdb) next
6 int *xptr = &x;
(gdb) next
7 printf("In main():\n");
(gdb) next
In main():
8 printf(" x is %d and is stored at %p.\n", x, &x);
(gdb) next
x is 5 and is stored at 0xbffffb44.
9 printf(" xptr holds %p and points to %d.\n", xptr, *xptr);
(gdb) next
xptr holds 0xbffffb44 and points to 5.
10 display(x, xptr);
为了进入到函数display内部进行调试,输入命令step,即:
(gdb) step
display (z=5, zptr=0xbffffb44) at example1.c:15
15 printf("In display():\n");
step命令使执行进入到函数内部,此时在该函数内部,可以继续使用step命令或者是next命令进行单步执行,如果不想单步执行,而是直接将程序一次执行完毕,可以输入命令continue即可。
要退出gdb,请键入命令quit,如果程序此时仍在进行,gdb会让你确认是否真的要退出,屏幕会出现类似下面的提示信息:
(gdb) quit
The program is running. Exit anyway? (y or n)
按下'y' 即退出调试程序,如果程序本身已经运行完毕,则quit命令键入后,会直接退出gdb,而不出现任何提示信息。
当然除了使用gdb进行程序调试外,如果程序比较简短,逻辑又比较简单,此时完全可以不用gdb,采用printf语句在程序当中输出中间变量的值来调试程序,也是一个不错的调试方法。
到此为止,我们已经介绍了uClinux操作系统,GNU工具的使用,有了这些预备知识后,我们将进入到本章的重点内容了。
我来回答
回答4个
时间排序
认可量排序
认可0
认可0
认可0
认可0
或将文件直接拖到这里
悬赏:
E币
网盘
* 网盘链接:
* 提取码:
悬赏:
E币
Markdown 语法
- 加粗**内容**
- 斜体*内容*
- 删除线~~内容~~
- 引用> 引用内容
- 代码`代码`
- 代码块```编程语言↵代码```
- 链接[链接标题](url)
- 无序列表- 内容
- 有序列表1. 内容
- 缩进内容
- 图片![alt](url)
相关问答
-
2012-12-04 13:47:21
-
2018-07-06 16:02:29
-
342019-07-19 19:18:52
-
222019-08-20 16:57:22
-
2017-06-05 15:20:33
-
2008-08-07 19:02:21
-
2015-01-13 22:56:29
-
2015-01-26 22:36:17
-
2018-10-22 09:49:48
-
02016-06-22 23:45:46
-
2015-01-15 09:04:55
-
2020-10-14 11:01:04
-
2012-12-04 13:42:23
-
2013-11-18 14:09:30
-
2009-06-26 08:48:33
-
2013-11-19 19:13:45
-
2017-06-12 15:29:09
-
2015-03-24 11:26:06
-
02013-11-18 15:31:11
无更多相似问答 去提问
点击登录
-- 积分
-- E币
提问
—
收益
—
被采纳
—
我要提问
切换马甲
上一页
下一页
悬赏问答
-
5Hi3516CV610 如何使用SD卡升级固件
-
5cat /dev/logmpp 报错 <3>[ vi] [func]:vi_send_frame_node [line]:99 [info]:vi pic queue is full!
-
50如何获取vpss chn的图像修改后发送至vo
-
5FPGA通过Bt1120传YUV422数据过来,vi接收不到数据——3516dv500
-
50SS928 运行PQtools 拼接 推到设备里有一半画面会异常
-
53536AV100的sample_vdec输出到CVBS显示
-
10海思板子mpp怎么在vi阶段改变视频数据尺寸
-
10HI3559AV100 多摄像头同步模式
-
9海思ss928单路摄像头vio中加入opencv处理并显示
-
10EB-RV1126-BC-191板子运行自己编码的程序
举报反馈
举报类型
- 内容涉黄/赌/毒
- 内容侵权/抄袭
- 政治相关
- 涉嫌广告
- 侮辱谩骂
- 其他
详细说明
提醒
你的问题还没有最佳答案,是否结题,结题后将扣除20%的悬赏金
取消
确认
提醒
你的问题还没有最佳答案,是否结题,结题后将根据回答情况扣除相应悬赏金(1回答=1E币)
取消
确认