最近我们的嵌入式项目上出现了一个应用程序没有机会给watchdog汇报,导致watchdog跳起来重启系统的问题。这个也就是我们常说的上层应用程序异常没法及时喂狗,导致狗发飙了。
因为我们的系统业务比较复杂,很难定位到底哪个代码出问题了。初步根据经验和现象,个人认为应该是代码进入了某个死循环了。但是要找到这个代码就比较困难了,因为这个问题只发生在客户现场,而且我们很难使用过多的debug手段。
所以第一个问题就是要想办法找到这个死循环点:
- 如何脚本监控watchdog多久没有收到report, 当时间比较长时,比如150秒,我们给应用程序使用strace去抓取进程的系统调用情况,结果 没有任何系统调用,抓到的log是空的。所以死循环一定是发生了,而且发生的地方应该没有使用系统API。
- 如果脚本监控watchdog多久没有收到report, 当时间比较长时,比如150秒,我们给应用程序发送一个“kill -11 pid”的命令,让它自己进入exception case,收集这个进程当前的堆栈信息。
我们通过方法2就可以通过堆栈找到loop到底在哪个里了。当然这个150可以根据情况,去调整,目的就是发生异常时,我们可以获得堆栈就可以了。
这样我们定位到了如下一个函数里面:
xyc(uint8_t bComp)
{
for (char bStage = 3; bStage >= 0; bStage--)
{
if (bComp%4))
return bStage + 1;
}
return 0;
}
初看这个函数怎么可能死循环呀!!!最多循环4次就结束了呀。 对呀,bStage是个char行的它的范围就是-128~+127。 那就是说char是个signed char了,怎么loop结束不了呢?(问题的根源是这里的char是unsiged, 0-1就变成了255了,就这样无线循环开了)
这样想想,char有三个类型: char, unsiged char 和 signed char。那char到底是signed还是unsigned? 这是个问题。查了相关的C的规范,大概的结果就是这个事情是有编译器来定的。对于我们的编译器就是GCC了,而且GCC还有option 去控制char到底用什么:
-funsigned-char / -fno-signed-char --> char 就是unsiged char
-fsigned-char / -fno-unsigned-char --> char 就是 signed char
所以我们要去测试我们的GCC缺省情况下char到底是是unsigned还是signed,下面的代码可以帮助我们确定GCC的缺省行为,如果a和b相等,那char就是signed char; 如果a和c相等,那char就是unsigned char:
#include <stdio.h>
int main()
{
char a=-1;
signed char b=-1;
unsigned char c=-1;
printf("define the char a=-1, signed char b=-1, unsigned char c=-1");
printf("a=%d,b=%d,c=%d",a,b,c);
return 0;
}
下面的表格是我的测试结果, 这个也说明了实践是最靠谱的。我们可以根据我们的项目情况和开发人员说清楚char到底是什么实际的类型。或者明确的用GCC的option指明char到底是哪个char。
Bin file | Testing log | Comments |
./newarm_default_char | define the char a=-1, signed char b=-1, unsigned char c=-1 a=255,b=-1,c=255 | toolchain A, the “char” is matched to “unsigned char”. |
./newarm_gcc-fsigned-char | define the char a=-1, signed char b=-1, unsigned char c=-1 a=-1,b=-1,c=255 | toolchain A, add gcc option “-fsigned-char”, the “char” is matched to “signed char”. |
./cortexa8_default_char | define the char a=-1, signed char b=-1, unsigned char c=-1 a=255,b=-1,c=255 | toolchain B, the “char” is matched to “unsigned char”. |