最近的项目进度比较紧张,程序修改十分仓促,导致在测试环节发现,部分机器出现工作一会儿死机的情况,其余机器长时间工作是均工作正常。
死机的情况,通常会有如下几种情况:
堆栈问题。Example:
- 栈溢出。局部变量为大数组造成的栈溢出。
- 堆溢出或内存泄漏。因为我的程序没有申请内存空间,不存在内存泄漏或者堆溢出的情况。
上面的这两种情况,我都进行了查找和排查,确定不是堆栈引起的。
- 中断向量。
我分别尝试了在每个异常中断里面增加了串口打印信息和复位。
没有发现出现死机后,窗口打印了中断向量相关的信息或者系统复位,从而确认了不是中断向量导致的问题。 - 死循环。
我逐个While(1) 进行了一遍排查,发现每个 while(1)都有退出机制,确认了不是 while(1) 导致的问题。
实在没辙了,只能在每个功能块的进入和退出,分别进行了信息的打印,来定位问题。幸好上天保佑,真的给定位到了某个功能块出的问题。然后我在有问题的功能块里面的功能函数增加进入和退出的信息打印。终于定位到了问题函数。
下面,分析下这个函数是怎么导致的死机~
源函数:
int trigger(uint16_t Timeout)
{
uint8_t cnt = 0;
while(1)
{
reg = ReadReg(0xAA, REG_TYPE_DIR);
if((reg & 0x01) || (cnt >= Timeout))
{
return OK;
}
++cnt ;
}
}
分析:trigger 函数的 while(1) 的退出条件有 2 个,分别为
- (reg & 0x01) 为真。
- (cnt >= Timeout) 为真。
只要满足其中一个就能退出 while循环。这么分析下来,是不是感觉,没有什么问题吖?
再仔细看看,Timeout 和 cnt 的数据类型。
- Timeout 的数据类型:uint16_t,表示范围 [0, 65535]
- cnt 的数据类型:uint8_t,表示范围 [0, 255]
一旦传入参数 Timeout 的值大于 255,(cnt >= Timeout) 这个条件永远为假。
reg 是 某总线上的设备的状态值,一旦设备失灵、错误或异常,(reg & 0x01) 这个条件也为假。
这样就造成了 while(1) 一直无法退出,造成死机的情况。
后来,我将 cnt 的类型修改为 uint16_t 后,一切正常。
可以说,这是一个非常低级的错误,以此立个 Flag, 坚决不再犯!
int trigger(uint16_t Timeout)
{
uint16_t cnt = 0;
while(1)
{
reg = ReadReg(0xAA, REG_TYPE_DIR);
if((reg & 0x01) || (cnt >= Timeout))
{
return OK;
}
++cnt ;
}
}