嵌入式技术
1. 调试相关的宏
在Linux使用gcc编译程序的时候,对于调试的语句还具有一些特殊的语法。gcc编译的过程中,会生成一些宏,可以使用这些宏分别打印当前源文件的信息,主要内容是当前的文件、当前运行的函数和当前的程序行。
具体宏如下:
__FILE__ 当前程序源文件 (char*) __FUNCTION__ 当前运行的函数 (char*) __LINE__ 当前的函数行 (int)这些宏不是程序代码定义的,而是有编译器产生的。这些信息都是在编译器处理文件的时候动态产生的。
#include2. # 字符串化操作符int main(void) { printf("file: %s ", __FILE__); printf("function: %s ", __FUNCTION__); printf("line: %d ", __LINE__); return 0; }
#include执行结果:#define DPRINT(expr) printf(" %s = %d ", #expr, expr); int main(void) { int x = 3; int y = 5; DPRINT(x / y); DPRINT(x + y); DPRINT(x * y); return 0; }
deng@itcast:~/tmp$ gcc test.c deng@itcast:~/tmp$ ./a.out#expr表示根据宏中的参数(即表达式的内容),生成一个字符串。该过程同样是有编译器产生的,编译器在编译源文件的时候,如果遇到了类似的宏,会自动根据程序中表达式的内容,生成一个字符串的宏。x / y = 0 x + y = 8 x * y = 15
//打印字符 #define debugc(expr) printf("由于#expr本质上市一个表示字符串的宏,因此在程序中也可以不适用%s打印它的内容,而是可以将其直接与其它的字符串连接。因此,上述宏可以等价以下形式:%s = %c ", #expr, expr) //打印浮点数 #define debugf(expr) printf(" %s = %f ", #expr, expr) //按照16进制打印整数 #define debugx(expr) printf(" %s = 0X%x ", #expr, expr);
//打印字符 #define debugc(expr) printf("总结#expr = %c ", expr) //打印浮点数 #define debugf(expr) printf(" #expr = %f ", expr) //按照16进制打印整数 #define debugx(expr) printf(" #expr = 0X%x ", expr);
#include上述程序中,test(x)宏被定义为test##x, 他表示test字符串和x字符串的连接。 在程序的调试语句中,##常用的方式如下:#define test(x) test##x void test1(int a) { printf("test1 a = %d ", a); } void test2(char *s) { printf("test2 s = %s ", s); } int main(void) { test(1)(100); test(2)("hello world"); return 0; }
#define DEBUG(fmt, args...) printf(fmt, ##args)替换的方式是将参数的两个部分以##连接。##表示连接变量代表前面的参数列表。使用这种形式可以将宏的参数传递给一个参数。args…是宏的参数,表示可变的参数列表,使用##args将其传给printf函数。
#define DEBUG(fmt, args...) { printf("file:%s function: %s line: %d ", __FILE__, __FUNCTION__, __LINE__); printf(fmt, ##args); }程序示例:
#include总结#define DEBUG(fmt, args...) { printf("file:%s function: %s line: %d ", __FILE__, __FUNCTION__, __LINE__); printf(fmt, ##args); } int main(void) { int a = 100; int b = 200; char *s = "hello world"; DEBUG("a = %d b = %d ", a, b); DEBUG("a = %x b = %x ", a, b); DEBUG("s = %s ", s); return 0; }
#define DEBUG(fmt, args...) printf("file:%s function: %s line: %d "fmt, __FILE__, __FUNCTION__, __LINE__, ##args)程序示例:
#include总结#define DEBUG(fmt, args...) printf("file:%s function: %s line: %d "fmt, __FILE__, __FUNCTION__, __LINE__, ##args) int main(void) { int a = 100; int b = 200; char *s = "hello world"; DEBUG("a = %d b = %d ", a, b); DEBUG("a = %x b = %x ", a, b); DEBUG("s = %s ", s); return 0; }
[debug] debug_level=XXX_MODULE解析配置文件使用标准的字符串操作库函数就可以获取XXX_MODULE这个数值。
int show_debug(int level) { if (level == XXX_MODULE) { #define DEBUG(fmt, args...) printf("file:%s function: %s line: %d "fmt, __FILE__, __FUNCTION__, __LINE__, ##args) } else if (...) { .... } }7. 条件编译调试语句
#ifdef USE_DEBUG #define DEBUG(fmt, args...) printf("file:%s function: %s line: %d "fmt, __FILE__, __FUNCTION__, __LINE__, ##args) #else #define DEBUG(fmt, args...) #endif如果USE_DEBUG被定义,那么有调试信息,否则DEBUG就为空。
#define USE_DEBUG #undef USE_DEBUG定义条件编译的方式使用一个带有值的宏。
#if USE_DEBUG #define DEBUG(fmt, args...) printf("file:%s function: %s line: %d "fmt, __FILE__, __FUNCTION__, __LINE__, ##args) #else #define DEBUG(fmt, args...) #endif可以使用如下方式进行条件编译。
#ifndef USE_DEBUG #define USE_DEBUG 0 #endif8. 使用do…while的宏定义
#define HELLO(str) do { printf("hello: %s ", str); }while(0)程序示例:
int cond = 1; if (cond) HELLO("true"); else HELLO("false");9. 代码剖析
#include编译的时候加入-pg选项。#define T 100000 void call_one() { int count = T * 1000; while(count--); } void call_two() { int count = T * 50; while(count--); } void call_three() { int count = T * 20; while(count--); } int main(void) { int time = 10; while(time--) { call_one(); call_two(); call_three(); } return 0; }
deng@itcast:~/tmp$ gcc -pg test.c -o test执行完成后,在当前文件中生成了一个gmon.out文件。
deng@itcast:~/tmp$ ./test deng@itcast:~/tmp$ ls gmon.out test test.c deng@itcast:~/tmp$使用gprof剖析主程序。
deng@itcast:~/tmp$ gprof test Flat profile: Each sample counts as 0.01 seconds. % cumulative self self total time seconds seconds calls ms/call ms/call name 95.64 1.61 1.61 10 160.68 160.68 call_one 3.63 1.67 0.06 10 6.10 6.10 call_two 2.42 1.71 0.04 10 4.07 4.07 call_three
其中主要的信息有两个,一个是每个函数执行的时间占程序总时间的百分比,另外一个就是函数被调用的次数。通过这些信息,可以优化核心程序的实现方式来提高效率。
当然这个剖析程序由于它自身特性有一些限制,比较适用于运行时间比较长的程序,因为统计的时间是基于间隔计数这种机制,所以还需要考虑函数执行的相对时间,如果程序执行时间过短,那得到的信息是没有任何参考意义的。
将上述程序时间缩短。
#include剖析结果如下。#define T 100 void call_one() { int count = T * 1000; while(count--); } void call_two() { int count = T * 50; while(count--); } void call_three() { int count = T * 20; while(count--); } int main(void) { int time = 10; while(time--) { call_one(); call_two(); call_three(); } return 0; }
deng@itcast:~/tmp$ gcc -pg test.c -o test deng@itcast:~/tmp$ ./test deng@itcast:~/tmp$ gprof test Flat profile: Each sample counts as 0.01 seconds. no time accumulated % cumulative self self total time seconds seconds calls Ts/call Ts/call name 0.00 0.00 0.00 10 0.00 0.00 call_one 0.00 0.00 0.00 10 0.00 0.00 call_three 0.00 0.00 0.00 10 0.00 0.00 call_two因此该剖析程序对于越复杂、执行时间越长的函数也适用。
#include剖析结果如下。#define T 100 void call_one() { int count = T * 1000; while(count--); } void call_two() { int count = T * 100000; while(count--); } void call_three() { int count = T * 20; while(count--); } int main(void) { int time = 10; while(time--) { call_one(); call_two(); call_three(); } return 0; }
deng@itcast:~/tmp$ gcc -pg test.c -o test deng@itcast:~/tmp$ ./test deng@itcast:~/tmp$ gprof test Flat profile: Each sample counts as 0.01 seconds. % cumulative self self total time seconds seconds calls ms/call ms/call name 101.69 0.15 0.15 10 15.25 15.25 call_two 0.00 0.15 0.00 10 0.00 0.00 call_one 0.00 0.15 0.00 10 0.00 0.00 call_three总结
审核编辑:汤梓红
全部0条评论
快来发表一下你的评论吧 !