调试复杂的宏定义--GDB调试技巧
2014-04-19 12:48
225 查看
C语言中的宏定义,这样给调试宏定义时,带来了很大的困难。对于开发人员来说,除了直接肉眼去看宏定义,自己来展开宏定义去确定问题,是否还有其它手段来调试宏定义吗?有着各种各样的好处和坏处,可谓让人有爱有恨。在大型的工程项目中,为了简洁,为了封装,宏的应用必不可少。但是在调试问题时,因为宏定义是被预定义处理的,所以不会有任何的编译符号和调试信息。
本文介绍两种调试宏定义的小技巧:
如头文件的插入,宏定义的展开。
如下面的代码:
第一个方法是通过gcc -E 产生预编译后的源代码,即源代码经过
4000
预编译后的结果,所有的预编译动作都已完成。
#include <stdlib.h>
#include <stdio.h>
#define MACRO1(x) (++(x))
#define MACRO2(x) (MACRO1(x)+100)
#define MACRO3(x) (MACRO2(x)+200)
int main(void)
{
int a = 0;
int b = 0;
b = MACRO3(a);
printf("%d\n", b);
return 0;
}
处只是一个示意。这里的MACRO3嵌套调用了MACRO2,MACRO1。在真正的代码中,这种用法很常见,不过这处的宏定义很简单,即使是嵌套调用也很容易看出。此
Ok,使用gcc -E test.c > test.e,得到预编译后的代码:
/*
前面是1800+行的头文件代码,此处省略
*/
int main(void)
{
int a = 0;
int b = 0;
b = (((++(a))+100)+200);
printf("%d\n", b);
return 0;
}
这里可以清晰的看到b = (((++(a))+100)+200);这个就比刚才的宏定义要清楚的多。
但是从这个例子也可以看到这个方法的局限性。
2. 得到的了一个新的代码文件。这样的话,在大型工程中,如果需要调试多个文件中的宏定义,需要我们一个一个的预编译,太麻烦了。
1. 由于预编译处理会执行所有的预处理代码,包括头文件的插入,这导致最后的代码行数太多。
下面看看第二个方法,这个方法要比第一种方法方便得多。
但是你可知道-g 也通-o一样,是分级别的。当不指定级别的时候,其level为2。为了调试宏定义,我们可以使用更高的级别-g3。我们都知道为了调试程序,需要使用-g选项,它的作用就是将调试信息加入到最后的二进制可执行文件中。
下面为我使用-g3编译上面的代码,然后进行调试:
Breakpoint 1, main () at test.c:11
11 int a = 0;
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.i686
(gdb) n
12 int b = 0;
(gdb)
14 b = MACRO3(a);
(gdb)
16 printf("%d\n", b);
(gdb) macro expand MACRO3(a)
expands to: (((++(a))+100)+200)
(gdb) macro expand MACRO3(0)
expands to: (((++(0))+100)+200)
(gdb) macro exp MACRO3(0)
expands to: (((++(0))+100)+200)
(gdb)
并且我们还可以给宏传入任何的一个值,如:在调试的过程中,可以使用macro expand/exp 来展开宏定义。从上面的调试过程中,可以直接看到宏定义展开后的结果。
(gdb) macro exp MACRO3(3)
expands to: (((++(3))+100)+200)
(gdb)
当然这个方法也有一个缺点,就是g3的调试信息会比默认的g2的调试信息要大——自然嘛,不然gdb如何知道怎样展开宏定义呢。第二个方法无疑比第一个方法要方便简单得多。我们只需要在全局的Makefile中添加新的编译参数-g3,就可以支持整个工程代码中所有的宏的调试。
本文介绍两种调试宏定义的小技巧:
如头文件的插入,宏定义的展开。
如下面的代码:
第一个方法是通过gcc -E 产生预编译后的源代码,即源代码经过
4000
预编译后的结果,所有的预编译动作都已完成。
#include <stdlib.h>
#include <stdio.h>
#define MACRO1(x) (++(x))
#define MACRO2(x) (MACRO1(x)+100)
#define MACRO3(x) (MACRO2(x)+200)
int main(void)
{
int a = 0;
int b = 0;
b = MACRO3(a);
printf("%d\n", b);
return 0;
}
处只是一个示意。这里的MACRO3嵌套调用了MACRO2,MACRO1。在真正的代码中,这种用法很常见,不过这处的宏定义很简单,即使是嵌套调用也很容易看出。此
Ok,使用gcc -E test.c > test.e,得到预编译后的代码:
/*
前面是1800+行的头文件代码,此处省略
*/
int main(void)
{
int a = 0;
int b = 0;
b = (((++(a))+100)+200);
printf("%d\n", b);
return 0;
}
这里可以清晰的看到b = (((++(a))+100)+200);这个就比刚才的宏定义要清楚的多。
但是从这个例子也可以看到这个方法的局限性。
2. 得到的了一个新的代码文件。这样的话,在大型工程中,如果需要调试多个文件中的宏定义,需要我们一个一个的预编译,太麻烦了。
1. 由于预编译处理会执行所有的预处理代码,包括头文件的插入,这导致最后的代码行数太多。
下面看看第二个方法,这个方法要比第一种方法方便得多。
但是你可知道-g 也通-o一样,是分级别的。当不指定级别的时候,其level为2。为了调试宏定义,我们可以使用更高的级别-g3。我们都知道为了调试程序,需要使用-g选项,它的作用就是将调试信息加入到最后的二进制可执行文件中。
下面为我使用-g3编译上面的代码,然后进行调试:
Breakpoint 1, main () at test.c:11
11 int a = 0;
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.i686
(gdb) n
12 int b = 0;
(gdb)
14 b = MACRO3(a);
(gdb)
16 printf("%d\n", b);
(gdb) macro expand MACRO3(a)
expands to: (((++(a))+100)+200)
(gdb) macro expand MACRO3(0)
expands to: (((++(0))+100)+200)
(gdb) macro exp MACRO3(0)
expands to: (((++(0))+100)+200)
(gdb)
并且我们还可以给宏传入任何的一个值,如:在调试的过程中,可以使用macro expand/exp 来展开宏定义。从上面的调试过程中,可以直接看到宏定义展开后的结果。
(gdb) macro exp MACRO3(3)
expands to: (((++(3))+100)+200)
(gdb)
当然这个方法也有一个缺点,就是g3的调试信息会比默认的g2的调试信息要大——自然嘛,不然gdb如何知道怎样展开宏定义呢。第二个方法无疑比第一个方法要方便简单得多。我们只需要在全局的Makefile中添加新的编译参数-g3,就可以支持整个工程代码中所有的宏的调试。
相关文章推荐
- GDB调试技巧:调试复杂的宏定义
- GDB调试技巧:调试复杂的宏定义
- GDB调试技巧:调试复杂的宏定义
- GDB调试技巧:调试复杂的宏定义
- GDB的调试技巧
- gdb调试技巧
- gdb 调试技巧
- 实用技巧:Gdbserver远程调试的具…
- gdb调试技巧
- gdb 相关调试技巧整理 收藏
- 【转贴】gdb中的信号(signal)相关调试技巧
- gdb远程调试的一些技巧
- 【转贴】gdb中的信号(signal)相关调试技巧
- GDB分析ELF文件常用的调试技巧
- GDB调试技巧
- 宏定义编写技巧__调试技巧【原创】
- gdb调试基本技巧
- GDB使用和段错误调试技巧
- gdb调试技巧(一)———— gdb 调试带参可执行
- gdb调试技巧