应用环形登录缓存解决程序调试的技术探究

时间:2022-09-24 07:17:43

应用环形登录缓存解决程序调试的技术探究

摘要:在软件开发中,查找错误是一个颇费人力物力的工作。该文提出了一种通过环形登录缓存来解决查错难的方法。文中首先给出了其数据结构,然后给出了设计思路,最后提供了一个应用结果。

关键词:查错;中断服务程序;环形登录缓存;事件

中图分类号:TP311文献标识码:A文章编号:1009-3044(2011)30-7439-02

The Debugging Method of Circular Logging Buffer

XU Jing-feng

(Nanjing Institute of Politics, Nanjing 210003, China)

Abstract: Debugging is a hard work for software development. This paper gives the method of circular logging buffer to solve the problem. At first the paper gives its data structure, then the implementation. At last it provides the outcome of an application.

Key words: debugging; interrupt service routine; circular logging buffer; event

1 问题的提出

在嵌入式软件开发中,调试阶段往往比预计所花的时间要长,人们常常希望能找一种方法能将程序中的错误快速查找出来。在调试的第一阶段,程序员通过源代码调试器可以查出一些比较明显的错误。而有些错误可能要在软件运行一段时间后才发生,比如链表崩溃了,输入/输出无法正常实施。在这种软件调试的第二阶段,要想查找出这些错误来,程序员必须要清楚地知道软件功能、目标及发生的问题。在处理过程中,很多程序员是将printf()函数放入程序中以便跟踪了解软件的运行状态。但这种做法的问题是它消耗了大量的CPU周期,会影响系统定时甚至导致某些错误不再发生。这种做法还会导致如下一些问题:

1)在很多系统中,无法从中断服务程序(ISR)中调用printf()函数。这是因为在ISR中,所有的硬件中断都不起作用了,除非是在printf()里使用的串行设备中断被激活;

2)printf()函数可以一直占用一个串行设备,使得其他操作无法发生;

3)当对软件的初始化或重启进行调试时,printf()函数可能无法获取。

2 环形登录缓存的数据结构

为解决以上问题,一种可行的解决方法是使用环形登录缓冲的技术,用它来记录各种关键软件事件且使其比错误优先级更高。这种方法对系统定时的影响很小,而且只需占用很少的一片RAM内存。该方法适用范围广泛,从最简单的只有一个main()函数的程序到具有多线程的操作系统都可使用。

环形登录缓存用一片固定的内存去保存最近的导致软件错误的事件,而此前的软件事件则会被覆盖。它记录每个事件的信息,会根据特定需求进行调节,但其数据结构通常都包括一个时间戳,一个指向字符串的指针和一个可选的32位值。其定义如下:

typedef struct {

unsigned int timeStamp;

char *message;

unsigned int associatedData;

} HistItem;

其中时间戳timeStamp是与事件时间相关的。绝大多数系统都有一个由周期性中断控制的硬件或软件计数器,当一个事件被记录下来后,计数器的值就会被读入HistItem.timeStamp中。采用timeStamp的另一个好处是它可以了解系统定时,如要花多长时间去运行一个ISR,多长时间调用一个函数。

message域是一个指向以NULL结尾字符串的指针,它可以提供位置的具体信息。当一个事件被登录时,只要指向字符串的指针通过,它也成为该事件的一部分了。没有采用复制字符或printf()函数减少了对系统定时的影响。之后,当要打印环形登录缓存时,message指针不被引用但整个字符却会被打印出来。

32位的associatedData域是用来联系与事件相关的一个局部或全局变量值的。例如ISR经常会用一个局部变量去读出中断事件寄存器中的值,而将这些值登录进环形登录缓存可以使我们深入了解硬件中断的定时。

对于有多余内存的系统,HisItem可以包含更多的诸如线程标号、文件名(__FILE__)、行号(__LINE__)的域,这样可以方便地登录大部分信息。

综上所述,环形登录缓存可以用简单的HisItem数组来实现。用一个全局整数变量跟踪环形登录缓存的开始,一旦有一个事件登录了,另一个全局整数变量马上就会跟踪上。

3 环形登录缓存的实现方法

下面介绍一下环形登录缓存的实现方法:

先用一个函数hist_init()来初始化环形登录缓存。其中设置一个参数subsystemsEnabled来激活一个或更多的子系统。例如,当只调试软件模块ABC时,就只激活ABC子系统的环形登录缓存,这样就可以把那些没用的事件去除,而只登录你感兴趣的事件。

当有一个变量与某个事件关联时可用hist_addx()函数进行登录。这时我们用subsystem参数去显示一个事件从哪个模块产生的,用msg参数指定一个指向字符串信息的指针,将data参数复制到HistItem中的associatedData域中。这样我们可以用一个函数使得一个中断在修改关键变量和数据结构前就使它不起作用,如此登录的事件就具有原子性了。接下来,hist_addx()函数检查subsystem是否可以进行登录。如果可以,则替换当前时间、msg指针,将data数据复制到histbuf[histidx]里。然后全局变量histidx累加,当环形登录缓存满了则重新清零,另一变量histstart累加。当下一个事件登录进来,它将会重写histfuf[0]中的数据。

当没有变量与一个事件相关联时,用hist_add()去登录该事件。本函数的功能仅仅是调用hist_addx()并将data设为NOVAL即可。

当系统出了问题可用hist_print()来打印环形登录缓存。当doReturn参数为真时,就可以进行打印了,这样可以深入跟踪系统错误并加以解决。要获得hist_print()可通过以下几种方式:

1)当发现错误条件后,可在源代码中写入hist_print()函数;

2)用宏ASSERT()去试一个表达式,如果这个尝试出问题了,则调用hist_print()函数;

3)在某些开发调试软件中,一个新任务可以产生执行hist_print()函数;

4)定义一些传送到输入/输出端口的特殊命令也可以让一些固件来调用hist_print()函数。

4 结束语

我们采用上述方法,对一些嵌入式软件进行调试。以下是一个hist_print()函数的输出例子:

3012:read

3015:read,waiting for data

3367:isr,RxReq

3378:isr,src:0x00bec14c

…………

通过我们的工作验证,发现这是一种较好的查找错误的方法,既方便应用,又减少了对内存的消耗。

参考文献:

[1] 潘竹生.一种新的高效嵌入式应用程序调试技术[J].电脑开发与应用,2007(6).

[2] Pressman R.软件工程:实践者的研究方法[M].7版.北京:机械工业出版社,2010.

上一篇:基于C/S程序设计课程考试系统设计 下一篇:基于Efront的《计算机基础》网络课程的建设研...