嵌入式软件调试常用知识点
2018-01-02 22:53
267 查看
本博客参考王利涛老师的教程,以及自己之前学习的知识点,特此总结。主要涉及输入输出重定向和宏定义的一些用法。
标准输入流:stdin
标准输出流:stdout
标准错误输出流:stderr
程序开始运行时,系统自动打开3个标准文件:标准输入、 标准输出、标准出错输出。通常这3个文件都与终端相联系。因此,以前我们所用到的从终端输入或输出都不需要打开终端文件。系统自定义了3个文件指针stdin、stdout、stderr,分别指向终端输入、终端输出和标准出错输出(也从终端输出)。
在C语言中,提供了5种标准流,所有流均以文件的形式出现,统一了各种硬件操作接口带来的差异。
因此,我们从两方面使用它,分别是SHELL终端和程序方面:
举例: cat main.c > log 将main.c中的内容输出到Log文件中
2)重定向输入:
举例: grep main < log 将log文件内容输入到终端,同时grep查看关键字main所在位置
3) 重定向标准错误
举例: 命令 对象 1>log 2>err linux命令操作对象时,正常输出到log文件,错误输出到err文件(1可以省略)
重定向部分被写在#ifdef 和#endif ,其含义是:只有定义了符号LOCAL,才编译两条freopen函数。
这种方法适合在竞赛中使用,我们不必每次从输入终端读取特定格式数据,可以将数据写入在datain.txt文件中,使能宏定义,当最后调试完毕后,失能宏定义即可,非常方便!
举例:
大家可以看到这种写法的好处了吧,特别是当被调用函数出错时,我们可以利用这些打印信息很快的定位到出错的位置,方便快速调试。
原因是printf()函数采用行缓冲机制,减少了I/O时间与磁盘读写时间,提高了系统性能。但是,也带来了问题,就是不能实时打印信息,这里提供几种解决方案解决行缓冲带来的延迟
在printf里添加\n
刷新缓冲区:fflush(stdout);
直接将缓冲区禁掉: setvbuf(stdout,NULL,_IONBF,0);
使用错误标准流,stdout有line buffer: fprintf(stderr,…)
这里利用了一个知识点,C99标准对可变参数宏的支持:
代码:
代码:
这里我们根据PRINT_LEVEL 宏定义进行有选择的打印信息输出,能够很好的控制程序调试信息输出。
注:这里宏定义使用了接续符的概念,注意接续符’\’后面不要出现空格,否则会出现警告:
一、 输入输出重定向
在Linux中,所有对设备和文件的操作都使用文件描述符来进行。Linux中一个进程启动时,都会打开3个文件:标准输入、标准输出和标准出错处理。这三个文件分别对应文件描述符0、1、2。即:标准输入流:stdin
标准输出流:stdout
标准错误输出流:stderr
程序开始运行时,系统自动打开3个标准文件:标准输入、 标准输出、标准出错输出。通常这3个文件都与终端相联系。因此,以前我们所用到的从终端输入或输出都不需要打开终端文件。系统自定义了3个文件指针stdin、stdout、stderr,分别指向终端输入、终端输出和标准出错输出(也从终端输出)。
在C语言中,提供了5种标准流,所有流均以文件的形式出现,统一了各种硬件操作接口带来的差异。
因此,我们从两方面使用它,分别是SHELL终端和程序方面:
1.1 SHELL终端的重定向
1)重定向输出:> 覆盖方式 >> 追加方式 >! 强制覆盖方式
举例: cat main.c > log 将main.c中的内容输出到Log文件中
2)重定向输入:
<
举例: grep main < log 将log文件内容输入到终端,同时grep查看关键字main所在位置
3) 重定向标准错误
2> 标准错误以覆盖的方式写入文件 2>> 追加
举例: 命令 对象 1>log 2>err linux命令操作对象时,正常输出到log文件,错误输出到err文件(1可以省略)
1.2 C语言使用freopen函数重定向
如题,这里使用刘汝佳的算法竞赛书中的例子作为说明。#include <stdio.h> #include <string.h> #define LOCAL int main() { #ifdef LOCAL freopen("datain.txt","r",stdin); freopen("dataout.txt","w",stdout); #endif int num; scanf("%d",&num); printf("%d",num); return 0; }
重定向部分被写在#ifdef 和#endif ,其含义是:只有定义了符号LOCAL,才编译两条freopen函数。
这种方法适合在竞赛中使用,我们不必每次从输入终端读取特定格式数据,可以将数据写入在datain.txt文件中,使能宏定义,当最后调试完毕后,失能宏定义即可,非常方便!
二、 使用标准预定义宏打印属性
标准C语言预处理要求定义某些对象宏,每个预定义宏的名称一两个下划线字符开头和结尾,这些预定义宏不能被取消定义(#undef)或由编程人员重新定义。这里仅仅列举常用的几个:__LINE__ 当前程序行的行号,表示为十进制整型常量 __FILE__ 当前源文件名,表示字符串型常量 __DATE__ 转换的日历日期,表示为Mmm dd yyyy 形式的字符串常量,Mmm是由asctime产生的。 __TIME__ 转换的时间,表示"hh:mm:ss"形式的字符串型常量,是有asctime产生的。(asctime貌似是指的一个函数)
举例:
#include <stdio.h> #include <string.h> void why_me() { printf( "This function is %s\n", __func__ ); printf( "The file is %s.\n", __FILE__ ); printf( "This is line %d.\n", __LINE__ ); } int main() { printf( "The file is %s.\n", __FILE__ ); printf( "The date is %s.\n", __DATE__ ); printf( "The time is %s.\n", __TIME__ ); printf( "This is line %d.\n", __LINE__ ); printf( "This function is %s.\n", __func__ ); why_me(); return 0; }
大家可以看到这种写法的好处了吧,特别是当被调用函数出错时,我们可以利用这些打印信息很快的定位到出错的位置,方便快速调试。
三、 打印缓冲问题
有时候我们发现我们使用printf()函数打印时,并不能立即打印出我们想要的信息。而是在某个事件触发后(比如换行、缓冲区满等),才打印出我们的信息,这是为什么呢?原因是printf()函数采用行缓冲机制,减少了I/O时间与磁盘读写时间,提高了系统性能。但是,也带来了问题,就是不能实时打印信息,这里提供几种解决方案解决行缓冲带来的延迟
在printf里添加\n
刷新缓冲区:fflush(stdout);
直接将缓冲区禁掉: setvbuf(stdout,NULL,_IONBF,0);
使用错误标准流,stdout有line buffer: fprintf(stderr,…)
四、 打印开关控制
打印日志虽然能方便快速调试,但是有时也带来了问题,笔者最近也遇到了这种问题。在嵌入式对时序要求较高的场景下,打印非常影响系统的性能。 因此,我们可以使用打印开关,在DEBUG版本时,我们可以打印信息,而在Realease版本时,我们不输出调试信息。这里利用了一个知识点,C99标准对可变参数宏的支持:
__VA_ARGS__
代码:
#include <stdio.h> #include <string.h> #define __DEBUG #ifdef __DEBUG #define DEBUG(...) printf(__VA_ARGS__) #else #define DEBUG(...) #endif int main() { DEBUG("hello %s\n","paopao"); DEBUG("value %s %d\n","wsq",1000); return 0; }
五、 打印等级控制
有时,我们需要根据当前打印级别进行有选择的调试信息输出,此时就需要对打印进行等级控制。代码:
#include <stdio.h> #include <string.h> #define ERR_LEVEL 1 #define WARN_LEVEL 2 #define DEBUG_LEVEL 3 /* PRINT_LEVEL setting : 0: printf off 1: err log 2: warning conditions 3: debug_level messages */ #define PRINT_LEVEL 2 #define ERR(...) \ do{ \ if(PRINT_LEVEL>=ERR_LEVEL) \ { \ printf(__VA_ARGS__); \ } \ }while(0) #define WARN(...) \ do{ \ if(PRINT_LEVEL>=WARN_LEVEL) \ { \ printf(__VA_ARGS__); \ } \ }while(0) #define DEBUG(...) \ do{ \ if(PRINT_LEVEL>=DEBUG_LEVEL) \ { \ printf(__VA_ARGS__); \ } \ }while(0) int main() { ERR("erron log....\n"); WARN("warning log...\n"); DEBUG("debug log ...\n"); return 0; }
这里我们根据PRINT_LEVEL 宏定义进行有选择的打印信息输出,能够很好的控制程序调试信息输出。
注:这里宏定义使用了接续符的概念,注意接续符’\’后面不要出现空格,否则会出现警告:
backslash and newline separated by space
相关文章推荐
- linux 知识点整理,从虚拟机安装到常用开发软件在linux上的安装命令
- 嵌入式软件:通过串口进行调试的一些思考和实践
- 常用软件调试方法
- 嵌入式软件开发人员常用工具
- 常用嵌入式软件白盒测试工具介绍
- 嵌入式开发常用工具软件
- 软件调试笔记48 - 堆和堆检查 :堆常用调试功能
- 分析实时嵌入式系统软件调试问题
- 嵌入式软件开发 必须掌握的知识点(一)(有错还望指点^_^)
- 嵌入式软件开发 必须掌握的知识点(三)(有错还望指点^_^)
- 常用嵌入式Linux二进制调试工具
- 嵌入式开发常用工具软件集合
- 嵌入式软件开发------------------Qt之QSettings类的常用函数
- 嵌入式软件开发常用的软件包(持续更新中)
- 常用嵌入式Linux二进制调试工具(1)
- 常用嵌入式Linux二进制调试工具(1)(顶嵌开源)
- 嵌入式开发中常用商用软件的FreeWare替换
- 嵌入式软件调试方法
- 常用嵌入式Linux二进制调试工具(2)