“靠,死机了”,在周遭满是噼噼啪啪的键盘声中,一声突兀的嚎叫响起。然后便是一阵手忙脚乱的各种log添加、代码查看、问题复现。这场景想想都觉得小心脏慌慌的。那么有什么办法能让我们遇到这种情况能稍微优雅一点,不至于发出杀猪般的嚎叫呢?这就是接下来要介绍的一个小方法。
实际上,在项目开发过程中,系统卡死在某个位置的情况分为两种,一种是系统访问到非法地址,一种是系统卡死在某个while循环中。
系统访问到非法地址:
系统访问到非法地址一般都会执行HardFault()硬件错误处理函数,芯片厂家都会提供相应的读取寄存器的方法,然后根据寄存器值就能知道系统在哪个位置访问到了非法地址。然而该位置不一定是导致死机的真正位置,针对该问题可查看文章嵌入式开发-死机位置在第三方库的问题原因定位
系统卡死在某个while循环中:
当系统出现内存地址非法访问时,系统通常会执行HardFault()函数以记录内存非法访问前相关的变量。那么当系统在while循环中卡死时,我们是否也可以记录系统卡死前的位置及相关变量信息呢?答案是肯定的。当系统在while循环中卡死时,系统的RAM、定时器、中断等部件仍然正常工作。因此,我们可以定义全局变量并在需要的地方将代码行号、变量记录到定义的全局变量中,以便在系统出现卡死时读取。所以,我们需要解决两个问题:一是在何处监听代码,二是在系统卡死时如何获取记录的信息。
在何处监听代码
在何处监听代码,主要是个颗粒度问题。理想情况下肯定是在所有while循环出现的地方添加,但是这明显会加重我们的开发负担。因此,我们可以考虑在每个模块的执行函数入口及出口处添加,具体实现方式如下:
DEBUG_CHK_LINE()实现方式如下:
在代码的监听处,我们主要记录文件名及行号。如果需要也可以记录需要监听的变量。
系统卡死时如何获取监听信息
鉴于系统持续记录代码执行位置及相关变量信息,我们的主要挑战在于判断系统是否陷入死循环。因此,我们可以通过设定一个定时器周期中断并累加时间,当累加时间超过预设阈值TIME_OUT时,将所记录信息打印出来。其接口实现方式如下:
为了实现时间的累加,DEBUG_CHK_LINE_TIMER_INT(TIME_OUT) 需要在定时器中断回调函数执行并在代码监听接口DEBUG_CHK_LINE()内实现累加时间的清除。
通过上述方式添加监听接口后,当系统在while循环中出现卡死现象时,系统将定期打印相关信息。我们可以通过这些信息迅速定位导致卡死的模块。若无法直接通过查看卡死函数func()来确定具体位置,可以在func()函数内部继续添加监听接口DEBUG_CHK_LINE()并复现问题。
然而,在某些情况下,系统可能会在while循环中卡死,但这并不是while循环本身代码的问题,而是由于外部模块发生了内存越界访问,从而篡改了while循环代码导致的。在这种情况下,可以结合文章嵌入式开发-死机位置在第三方库的问题原因定位的方法来定位问题。
期望本方法能帮助大家在遭遇系统卡死状况时,不必发出无助的哀嚎。