EESKILL的个人空间 https://bbs.21ic.com/?1567396 [收藏] [复制] [RSS]

日志

【找BUG】一次调试之殇

已有 382 次阅读2015-9-22 10:05 |系统分类:资源宝藏| STM32单片机, 经验分享, 单片机, 调试

最近调试一段代码,发现一个很奇怪的现象,有一个函数放在一个位置,代码的执行结果是正确的。如果放在其他地方就会出现问题,同样的一个函数,为什么会出现这样的差距呢?
看到这里,或许很多人已经猜出问题的原因了。
但是,对于当时还在调试这段代码的人来说,或许是思维进入了一种惯性模式,看不到障眼法背后的实质原因。
一直以为是,函数的位置导致了程序执行结果的偏差。
甚至当时的心情,都有些向《代码大全》中提到的,“我的代码被狗吃了”。想怀疑是编译器出问题了,但是,《代码大全》让我知道,现在 的编译器出现问题的概率是极小的。如果我遇上了,几乎相当于买彩票中几百万的大奖。
还是要回到代码本身,代码出现问题,一般不是因为其他而导致的。程序是人写的,人在这个过程中,是最有可能出错的。也是出错概率最大的。平均,代码中30%左右的错误是因为输入拼写错误导致的。
但是,当自己找了很长时间,还是没有找到原因。因为找了很长时间,当时有些想放弃找到这个问题的真正原因了。然而,在分析其他代码的时候,突然想到如果有一个地方失误了,可能导致这个问题:即,变量使用的是一个随机值。
而造成随机值的原因,就是初始化问题。
 
想到这里,将出现问题的函数后重新check了一下,发现写入时,对于某些变量可能出现没有初始化的情况,这样将造成变量的值随机。
修正后,出问题的函数,不在出现了放置位置导致的执行效果,出错的问题。
对于此,想到几点:
  1. 代码完成后,休息一下,然后check几遍,check找到bug的效率是调试的几倍;
  2. 发现代码中的漏洞,很多时候,都是我们自己引起的;
  3. 关于这几天调试程序问题,有几方面原因:
    1. 开始时,一次调试需要很长时间,这里只在意尽快调试,没有把主要精力放在,代码check上;
    2. 调试时,过于集中于代码本身,而忽视了本质问题;
    3. 本次调试时,明显分析能力下降不少,只注重了表象,没有关心实质问题;
    4. 调试的心态不对,要注意;
  4. 还是没有做好这些东西;
  5. 学会放置代码,以后再进行检查,避免进入思维惯性;
  6. 不以物喜,不以己忧,尚要努力;
附注:
调试过程中的分析记录:
  1. cnt,记录下这个变量的值,使用这个值进行测试;
  2. 同时注释掉,删除下载文件的代码,使用这段代码进行升级下载测试;
  3. cnt = 0x103,下载文件结束偏移20400;
  4. 是什么原因,导致了升级时的APP_FAULT_ISR;
  5. 同时对于升级参数中,应该有一个判断是否升级成功的标识,这样避免的那个升级不成功的时候,恢复到原来版本时,擦出原来版本。
  6. 由断点调试发现,是重启后,没有跳转到升级后的FLash地址上运行,造成了还是在原版本上跳转,但是这里的问题是,为什么原有的程序也执行失败了?
  7. 怀疑是后边升级时写入Flash的问题。
  8. 升级部分的代码,确实很令人费解,一个函数的位置,竟然影响程序的生死,但是按照语言规则,不会有这么灵异的事情;
  9. 看到f_close和f_mount,怀疑是不是跟这两个函数有关系;
  10. 测试发现,这里跟f_close,f_mount没有关系,只跟判断到HEX文件的结束标志后,要立即复位,进行程序更新,否则就会出现问题。
  11. 造成了,程序的中断向量区,似乎没有进行更新,还是跑的以前的代码段,并跳转到了以前的main函数执行,但是经过了OSStart之后,程序发生了崩溃,回到了APP_FAULT_ISR的地方;这中间的问题,具体还没有想清楚;
  1. 首先先以(a方式)每次循环中都关闭的情况,来测试下看看;测试通过;
  2. 其次使用(b方式)先打开,在循环中读取并写入内部Flash,最后再关闭文件;测试不通过;现象:
    1. 程序启动后,跳转到原来版本的入口地址处;
    2. 之后程序能够正常初始化等,但是在OSStart函数之后,执行出现异常;
    3. 跳转到APP_FAULT_ISR;
    4. 重复以上步骤。
  3. 第三次,重复(1)的步骤,进行再次验证;测试通过;
  4. 由此说明(2)的方式在程序中通不过;
  5. 关于这个情况,描述下问题:
    1. )这里需要从外部Flash中读取一个文件,并将其写入到内部Flash中,然后完成程序复位,实现程序的升级;
    2. (a)中的方式是,每次读取一定的字节数时,先打开文件进行读取,读取结束后关闭文件,然后校验读取到的数据,校验成功,写入到内部Flash中,文件读取结束后,调用升级函数实现程序复位及升级;、
    3. (b)中的方式是,先打开外部Flash文件,读取一定的字节数据,然后校验读取到的数据,校验成功,写入内部Flash,文件读取结束后,关闭外部Flash文件,调用升级函数实现程序复位及升级;
  6. 关于两种方式,第一种会引起每次循环都有打开与关闭外部Flash文件的情况,第二种只有一次打开与关闭外部Flash文件的情况,理论上(b)的执行效率更高。但是实际测试发现,复位后(a)程序跳转正常,(b)程序跳转异常;这里边的原因,尚没有找到;
  7. 现猜测是由于Flash操作的问题;
  8. 先暂定使用(a)方式,如果以后需要效率问题,这里是个可以提升的地方,但是要详细测试;
  9. (a)方式,在将upgrade_program,放在发送回复之后,出现问题;
  10. 总之,这里得出一个结论,将程序写入内部Flash后,马上调用复位函数,进行程序升级;如果以后还需要对这里进行优化,可以详细测试下这里的原因,再行决定更合适的方案;
  11. (9)不是Flash操作的问题。
  12. 发现这里只跟何时调用upgrade_program函数有关系,检测到HEX文件的结束符,之后马上进行,程序复位,将不会引起错误;这个需要分析下原因在哪里;
  13. 发现,是在更新的时候,出现的错误;由于修改升级函数,但是没有及时将其bReadBkpDR,bWriteBkpDR函数没有配对使用;

网友1:代码为什么不做lint?这种错误,工具很容易就发现了。

作者回复:谢谢呵,虽然知道有些工具,但是具体的使用,还是没有在单片机的程序试过。还是对于lint了解的少,最近因为处于代码检查需要。发现pclint确实是可以检查写给单片机的代码,也谢谢以前的提醒。


路过

鸡蛋

鲜花

握手

雷人

评论 (0 个评论)