新闻中心

EEPW首页 > 牛人业话 > 指针就是飞刀,随时都会中招

指针就是飞刀,随时都会中招

作者:天雷君时间:2018-10-16来源:电子产品世界收藏

天气已然入秋了,迷人的夜色带着阵阵凉意降临到喧闹了一天的大地上,皎洁的月光裹挟着点点星光洒满窗台,是时中夜,寂然无声,周围的一切显得安详而又宁静,我仿佛听得见一旁熟睡的妻儿那绵长的呼吸声和带着欢快节奏的心跳声,是的,我又睡不着觉了,每当工作中遇到一个一时找不出来的时,我总会“废寝不忘食”一番。

本文引用地址:http://www.eepw.com.cn/article/201810/392924.htm

佛陀在金刚经上说,菩萨应无所住而生其心,就是说处理任何事情都不要执着,不要“住”在这个事情上,执着于事无补,就拿我来说吧,倘若不纠结在这个上,好好睡觉养足了精神,第二天休息充分的大脑或许就突然开了窍很顺利地把找出来了,像现在这样躺在床上胡思乱想,看不了代码,思绪还不时地跑飞,分明就是浪费时间嘛,休息不好,第二天状态肯定也不好,解决问题的能力肯定大不如常。从理性上来说,应该看破这种执着,放下bug,而生起睡觉的心,可是,感性的我还是执着地“住”在bug上,哎,看破和放下哪有那么容易!

1

这次遇到的bug现象再清楚不过,笔者做的一款产品,上电后代码跑着跑着就飞了,飞得我心惊肉跳,飞得我不知所措。

其实,Bug的表现非常有规律:产品上电后大约八分多钟的样子就重启复位了,一分不多,一分不少,照常理,规律性重现的bug最好捉了,但是也不尽然。因为产品代码被划分成那么多模块,每个模块都有可能出问题,这种不知道怎么操作来着就跑飞了的bug其实并不好解决。

2

据说高人们捉Bug最青睐直接分析代码,悟性高的人直捣黄龙,三下两下就能找到问题源头,悟性低的人在代码的迷雾中上下求索,最终也能拨云见雾找到真凶。而小白们捉Bug的主要手段就是调试和测试了。

笔者对着代码恍惚半天,终于意识到所谓“软件一哥”之语只是自欺欺人,于是老老实实回归测试一途。

笔者之前都是上电后就开始做各种测试,做哪些测试也只管随心,并无一定之规。按照“宁可错杀一千,不可放过一人”的原则,这些测试涉及的操作都有导致跑飞的重大嫌疑,倘若真真地都细究起来,难免有鲁迅先生《狂人日记》中觉得事事可疑,人人可怖的狂生的愚笨之嫌。于是,为了逐步缩小对bug的包围圈,笔者采用了毛主席著名的游击战策略:“敌动我不动、敌不动我动”,既然不知道究竟是什么操作造成的跑飞,那么现在什么操作也不做,我不动,看敌动否?

笔者在代码中加了一段测试程序,一旦跑飞复位,一个led灯就会闪烁一下。在测试台上拨拉几下开关,设置了产品的运行环境之后,洒家就静心屏气,正襟危坐,伴随那潺潺不息的时间之水神游天外起来。

笔者一边品茶消磨时间,一边观察着led灯的现象,时不时抬起手腕看看手机上的时间。八分钟过去了,‘不急,时间未到’,十分钟过去了,’不急,也许每次跑飞的时刻都不大一样’,十五分钟过去了,‘咦?见鬼了,就加了那么一点测试程序,代码就不跑飞了?’二十分钟过去了,洒家开始着急起来,’之前每次都跑飞的,现在怎么回事?’

“哎呦,写Bug呐!”一位相熟的同事从我身边飘过,轻松的调侃就像飞刀一样,直入我心。

笔者怔怔地看着测试台和慵懒地躺在一边的电路板,刚加上的那个测试led灯直愣愣地杵在电路板上,欲言又止。“肯定是测试条件发生了变化!”我下意识地想到,“发生了什么变化呢?”串接在电路板上的万用表默默显示着工作电流-1.6毫安,丝毫没有招摇之意,这是低功耗状态下的休眠电流,整车厂要求低于3mA,我从4mA一路做下来,减到1.6mA,‘先给自己点个赞。’我打着闲茬,随手打开了测试台的ACC开关,让离开了低功耗状态。

“我这次什么也没有操作,难道是之前进行的某个操作导致了跑飞?那就麻烦大了,鬼知道当时进行过哪些操作?”我继续分析,“又或者是操作顺序,不是单个操作引发跑飞,而是某几个操作连起来导致的?那就更麻烦了!”我痛苦地思索道。

滴答,滴答,时间无情地流逝着,我半躺在转椅上,任由思绪纷飞。电路和程序是从来都不会说谎的,我怀疑过天,怀疑过地,唯独没有怀疑过你,我盯着那个测试led灯,小声地自言自语。

Led灯像一个静静的美男子,全然不顾我的胡言乱语,默然静立。突然,就如电光火石一般,Led闪烁了一下,当它那炫美的蓝色余晖还在我眼前流连,我已经石破天惊地意识到,肯定是非休眠状态下才会跑飞的,我抬了抬手腕,看了看手机上的时间,算上上电后进入休眠状态的一分钟,加上刚才离开低功耗后运行的七分多钟,正好八分钟多!

3

按照这个思路,我重新设置了测试台,使得产品不会进入休眠状态,果不其然,每隔八分多钟led灯都会规律地闪上那么一下,守时守信,从不爽约。

那么,既然没有做任何操作,首先就可以把所有控制模块的嫌疑排除掉,剩下的就是通信模块了。这款产品有两路CAN总线通信,一路总线通信。继续调试程序,先把CAN总线通信屏蔽掉,测试发现led继续雷打不动地规律闪烁,排除CAN通信嫌疑。然后试着把通信屏蔽掉,Led终于不再闹腾了,它选择了在机器的世界中如如不动,直入三昧之境。

那么,通信程序错在哪儿了呢?这块程序大致包括时间槽调度程序、报文接收中断服务程序、报文解析程序三大块,为了进一步缩小嫌疑,我把canoe撤掉,这时LIN总线上没有了实际的LIN通信,便不会执行报文接收ISR和解析程序,如此这般,一通测试下来,这个Bug依然故我,固执而坚强,就是不肯往生极乐。问题了然了,肯定是时间槽调度程序出了问题。

这段程序说来也很简单,以20ms为时间槽长度,在每个时槽到达时发送报文头,然后根据是发送数据场还是接收数据场设置相关寄存器。听说分析程序不贴代码的都是耍流氓,我就先上为敬,把代码贴上来。

void l_ifc_tx(l_sch_table_item *sch_item)

{

      l_u32buffer_data;

      buffer_data= *(sch_item->data);

      BLIN.linflex->BDRM.R= buffer_data;  

      buffer_data= *(++sch_item->data);

      BLIN.linflex->BDRL.R= buffer_data;

      sch_item->data--;

      BLIN.linflex->BIDR.B.DFL= sch_item->datalen - 1;

      BLIN.linflex->BIDR.B.CCS= 0;

      BLIN.linflex->BIDR.B.ID= sch_item->id;  

      if(l_SEND== sch_item->mode){              

             BLIN.linflex->BIDR.B.DIR= 1;                    

      }else{

             BLIN.linflex->BIDR.B.DIR= 0;                    

      }      

      BLIN.linflex->LINCR2.B.HTRQ= 1;

}

其中,l_sch_table_item这个结构体的定义如下:

typedef struct

{

   l_ifc_handle   handle;

   l_u8           id;

   l_Resp_mode    mode;

   l_u8           datalen;

   l_u32          *data;

   l_u8           timelen;

}l_sch_table_item;

就路还家,知道问题出在哪段程序里就算找到Bug的家门了,胆大心细眼又尖的高手可能已经发现了Bug所在,没错,就是这句:

buffer_data =*(++sch_item->data);

按照C语言运算符的优先级,上述语句的执行次序是,先找到sch_item的成员变量data,然后对data执行++,最后执行*(新地址)的操作。

竟然那么随意地对指针进行了算术运算!!!这种行云流水的洒脱用在日常生活上倒是很美,用在编程上就是自讨苦吃了。看看这条语句,sch_item->data是个指针,在这里的++运算使得它每隔20ms都会累加一次,意味着1秒钟累加50次,一分钟累加3000次,八分钟累加24K次,这款产品的RAM为24KB,八分钟就累加到RAM空间之外,就是无效的地址空间了,加上程序上电开始执行第一次通信调度的时间,正好是8分多钟!

后记

嵌入式软件领域的编程宝典MISRA C的规则101明确指出,不能对指针进行算术运算,目的是为了防止指针指向无效的内存空间,言之凿凿,非常熟悉MISRA C的笔者却充耳不闻,拿豆包不当干粮,哎,不听老人言,吃亏在眼前,早干嘛去了?!



关键词: BCM bug LIN

评论


相关推荐

技术专区

关闭