c语言lr分析器的设计与实现_0.stm32_trace原理及实现

TRACE原理及实现

引入

       在嵌入式开发中我们面临最大的问题就是对于程序的调试。比如通过jtag仿真、通过串口打印信息、通过硬件的led指示等等都是我们常用的调试手段,但是当我们的产品在正常运行中出现bug应该如何调试呢?或者说我们的产品出现了hardfault中断又应该如何进行分析呢?我们可以通过“栈”来进行分析。

原理

       栈:栈是一种特别的数据结构,遵循先进后出的原则。STM32的栈是向下增长型(满递减),如图:

2bb86f0fa554d6e554f02bbe83926cec.png

栈生长方向

栈的作用就是保存现场。

函数调用分析

       函数调用也会涉及到对于栈的操作。

举例:

void led_flicker(void)

{

       led_on();

       led_wait(5);

       led_off();

       led_wait(8);

}

当我们仿真进行单步执行的过程中发现在进入led_wait函数之前会执行push操作,然后退出led_wait函数之前会执行pop操作。

7b2672cbbf119d26b47b86002a359584.png

PUSH操作

05e8b7b883c5c727bc56ef44229f82c1.png

POP操作

       表示当我们进行函数调用的时候会将当前的现场进行入栈,然后函数执行完成再进行现场恢复(出栈)。

中断分析

       当我们的程序出现问题,进入不可屏蔽中断的时候,同样也会进行入栈操作,入栈的顺序是xPSR,PC,LR,R12,R3,R2,R1,R0。那么在进入不可屏蔽中断前我们判断到sp的地址,然后将栈信息打印出来,辅助我们进行程序的Debug。

f81da18bdb12d70508b28fa3427fe4b7.png

异常入栈图

trace实现

       我们以hardfault为例:

1.首先判断使用的是MSP还是PSP

2.将MSP/PSP的地址传递到C函数

3.根据当前的地址打印出堆栈信息

源码:

HardFault_Handler\

                PROC

                EXPORT  HardFault_Handler          [WEAK]

                            IMPORT  hardfault_c

                            TST LR,#4       ;判断使用的是msp还是psp

                            ITE EQ               ;

                            MRSEQ R0,MSP;使用msp

                            MRSNE R0,PSP;使用psp

;                B       .

                            B hardfault_c

                ENDP

当产生hardfault后,单片机会跳转到中断向量HardFault_Handler处执行程序.

0e4a7ff143a45c9021dbaa081c1ff531.png

TST指令:

逻辑处理指令,用于把一个寄存器的内容和另一个寄存器的内容或立即数进行按位的与运算,并根据运算结果更新CPSR中条件标志位的值。当前运算结果为1,则Z=0;当前运算结果为0,则Z=1。

LR寄存器:

在进入异常服务程序的时候,LR寄存器会更新为EXC_RETURN的值,EXC_RETURN的值位域如下图(摘自Arm Cortex-M3与Cortex-M4权威指南)。

5c4514a5b81935df5766f75ddb163632.png

所以我们通过判断bit[2]来确定使用的是MSP还是PSP。根据不同的结果来将对应的栈地址传递给R0。

ITE EQ

MRSEQ R0,MSP

MRSNE R0,PSP

如果运算结果为1,也就是PSP,Z==0,那么则将PSP读取后传递到R0

如果运算结果为0,使用的MSP,那么Z == 1,那么将MSP读取后传递到R0

汇编调用C语言,参数的传递通过R0---R3来进行。

所以C函数hardfault_c的参数就是R0的值。

hardfault_c源码

/**

* @brief:中断函数

* @param:sp_addr 堆栈的地址

* @retval:无

* @note:

*/

void hardfault_c(unsigned long * sp_addr)

{

       trace_info("hardfault",(struct TRACE_REG_T *)sp_addr);

       while(1);

}

将堆栈的地址也就是R0传递到C函数,调用打印函数:

/**

* @brief:trace实现

* @param:it_type 中断类型

* @param:addr 堆栈地址

* @retval:无

* @note:

*/

void trace_info(unsigned char * it_type,struct TRACE_REG_T * reg)

{

       trace_print("\r\nVersion:");

       trace_print(g_pver);

       trace_print("\nType:");

       trace_print(it_type);

       trace_print("\r\n");

       trace_print("R0   = 0x%08X\r\n",reg->_R0);

       trace_print("R1   = 0x%08X\r\n",reg->_R1);

       trace_print("R2   = 0x%08X\r\n",reg->_R2);

       trace_print("R3   = 0x%08X\r\n",reg->_R3);

       trace_print("R12  = 0x%08X\r\n",reg->_R12);

       trace_print("LR   = 0x%08X\r\n",reg->_LR);

       trace_print("PC   = 0x%08X\r\n",reg->_PC);

       trace_print("xPSR = 0x%08X\r\n",reg->_xPSR);

//       trace_sp_mem_data((unsigned char *)reg);//此处存在问题 待完善

}

结构体TRACE_REG_T定义:

struct TRACE_REG_T{

       unsigned long * _R0;

       unsigned long * _R1;

       unsigned long * _R2;

       unsigned long * _R3;

       unsigned long * _R12;

       unsigned long * _LR;

       unsigned long * _PC;

       unsigned long * _xPSR;

};

验证

此trace信息需要结合反汇编文件使用,反汇编文件可以在keil中通过命令设置:

C:\Keil_v5\ARM\ARMCLANG\bin\fromelf.exe –c -s –t ./Objects/traceDemo.axf -o ./debug/traceDemo.dis

83cca3d47f3e6d104f984f406d6cf16c.png

/**

* @brief:LED状态

* @param:sta true->on false->off

* @retval:无

* @note:

*/

void led_sta(uint8_t sta)

{

       volatile uint32_t * p =  (uint32_t *)0xFFFFFFFF;

       uint32_t n = 0;

       if(sta)

       {

              led_on();

              n = *p;//测试trace 人为设置死机

       }

       else

       {

              led_off();

       }

}

查看串口助手打印信息:

3b07d47bb30084a76a5897c6437dc57c.png

结合反汇编文件所有PC值。如下:

0d7dafb7e1b65b7a8d5675961957e158.png

       证明程序的出错在led_sta函数中led_on后面的地方出现错误。也就是我们对地址0xFFFFFFFF进行读取操作的地方。

当然,你可以将更多的栈内存的内容进行打印,从而进行栈回溯来还原当时出现错误的现场以及函数的调用结构。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值