1、任务异常的一般表现:
i) 指令异常:系统打印program异常或instruction access异常。
ii) 访问非法地址异常,串口打印data access异常。
iii) 中断处理中产生的异常。
data access
Exception current instruction address:0x00187d4c
Machine Status Register:0x00009030
Data Access Register: 0x8003435c
Condition Register:0x48000080
Data storage interrupt Register:0x0000000b
Task:0xc844f0 "XXX"
2、可能的原因:
i) 堆栈写越界,主要是数组写越界,导致前面声明的变量(因为堆栈是从下往上增长的)或者函数的参数或者函数返回的地址被改写为无效值。
ii) 堆栈溢出,堆栈声明过小,而函数又声明了大数组,超出堆栈的容量。
iii) 内存改写,这是最通常出现的原因。包括,指针没有初始化,导致访问随机地址;访问空指针;内存操作范围越界,例如在使用memcpy/memset等函数使用的长度超过所分配,导致改写了其他的指针。因此在定位异常问题过程中,可以通过内存管理先查看一下先前是否有内存写越界的记录,但是内存写越界只有在释放该内存区时才能检查到,如果该内存没有被释放,则即使写越界也是不知道的。
iv) 系统调用不当,在中断回调中,使用printf,semTake之类可能引起阻塞的操作;printf等使用不当导致内存改写,这些函数在中断回调函数中是严格禁止的。
v) 增量编译引起的问题,Tornado和workbench增量编译有时会出现问题,导致古怪的异常,重新全量编译后,可能个会解决问题。
vi) 多任务抢占引起的问题,当多个任务共同访问一个或者多个变量时,如果互斥不当,可能会产生同步或互斥的问题,比较典型的是:任务A和B都使用一个变量指针P;而A和B的优先级不同(即存在任务抢占),则他们在释放或者修改这个变量指针的时候,可能会出现随机的异常访问问题。
3、处理策略:
i) 如果有理由怀疑是增量编译引起的,则首先尝试全量编译一下工程。
ii) 分析异常出现时的调用栈信息,以data access异常为例:
如果在系统出现异常时接了串口,则可以直接看到类似以下列信息;如果出现异常时没有接串口,则定位问题时可首先接上串口或者Tornado,然后使用“ti”命令也可以看到如下的异常信息:
data access
Exception current instruction address:0x00187d4c
通过这个地址通常可以通过符号表知道异常出现时系统正在运行哪个函数。
Machine Status Register:0x00009030
Data Access Register: 0x8003435c
Condition Register:0x48000080
Data storage interrupt Register:0x0000000b
Task:0xc844f0 "XXX"
通过这个参数,可以知道是哪个任务出现的异常。
4、具体定位提示:
i) 可以使用“checkStack”命令看是否产生了堆栈溢出,如果是XXX任务的堆栈溢出,则在checkStack的打印中,XXX的堆栈状态会显示成“overflow”。
ii) 可以使用tt命令查看任务调用栈,通过分析调用栈的每一层函数,尝试找原因,参照上面的可能原因部分。
iii) 分析函数调用序列中所访问的全局变量(尤其是指针和数组),看是否存在任务抢占而导致全局指针被修改或者访问无效指针的问题。
5、重现问题:
如果通过上述步骤无法直接找到原因,那么恭喜你,你已经遇到恐怖的随机内存改写问题,这类问题通常难以重现,而且通过分析单个任务的运行轨迹很难找到原因,这类问题很可能的情况是:虽然任务A在运行函数FuncA时产生的异常,但真正的内存改写代码很可能是在任务X或函数FuncX 中,解决这类问题的关键是如何重现问题,一旦问题可以比较容易的重现,那么可以说问题已经解决了一半。
重现问题的办法:
i) 回忆先前的操作步骤,一步一步地重做一遍,看问题是否可以重现。
ii) 如果是任务A产生的异常,则人工制造一些条件,使任务A不断的运行,以重现问题,通过脚本或者打桩,不断地发一些消息触发任务A的运行,这是最常用的方法,如果A是周期运行,则缩短周期,加快运行。
iii) 逆向分析,通过分析代码,可能怀疑一些代码有问题,这时候,可以针对性的创造各种人为条件来重现问题,例如怀疑某个定时器可能存在重复进入和重复删除,则可以人为修改代码流程,使之进入定时器重复进入/重复删除流程,或者用脚本不断地创建和删除定时器,以加快问题出现的频率。
iv) 手动调整任务优先级,分析任务抢占相关的问题,在基本确定任务异常可能和任务抢占相关的情况下,可以尝试以各种组合,将几个相关的任务优先级调整成相同的优先级(这样,这几个任务就不会再产生任务抢占了)。在此基础上尝试重现问题;如果在优先级相同的情况下异常不再出现,则说明这几个任务之间所共享的变量存在互斥问题;否则说明该问题很可能和互斥无关,或者根据需要把某些优先级调高,某些调低,以重现任务抢占的问题。
6、解决问题:
i) 如果找到问题所在,则直接修改问题。
ii) 如果通过多日的重现和定位,都无法找到真正的原因,那么没有办法的办法就是:直接修改代码,把怀疑有问题的地方全部改掉,然后再测试看问题是否解决。我曾经遇到一个任务异常的问题,如果跑1个测试脚本,则平均跑7-8小时可以出现一次,跑7个测试脚本,则平均跑半小时就会出现一次,定位了一个多月找不到真正原因,花了很多时间用来想办法重现异常,写脚本。后来没有办法只好把怀疑的地方全部改掉,之后跑7个脚本共48小时没有出现异常,最后解决问题,之后反过来推导root cause,最后找到原因。