【问题标题】:Runtime stack monitoring on Microchip PIC32 and dsPIC33EMicrochip PIC32 和 dsPIC33E 上的运行时堆栈监控
【发布时间】:2013-11-04 01:38:42
【问题描述】:

我需要对嵌入式硬件进行运行时堆栈分析,以确保分配了足够的堆栈空间。我了解基本理论。在启动时,您使用已知模式(例如所有 0xFF 或 0xAA)初始化堆栈,然后让您的程序运行。随着它的运行,堆栈会增长和缩小,但它永远不会恢复原始模式。在足够长的时间后,检查堆栈并找到要更改的最后一个堆栈值的地址。

我不需要便携式解决方案,只需要 dsPIC33EP512MU810 和 PIC32MX795F512 的解决方案。我的后备计划是逐渐减小堆栈大小,直到我得到堆栈溢出,但这只有在我有一个候选发布时才有效,我更喜欢在整个开发和生产过程中持续监控的解决方案。

【问题讨论】:

    标签: embedded stack-overflow microchip pic32


    【解决方案1】:

    我在 dsPICEP 系列上的“DrRobotNinja”的上述代码中遇到了一些错误,主要是使所有内容都使用 16 位变量 + 将地址增加 2 而不是 1,以通过保持字对齐来停止地址错误

    #define STACK_VAR_PAD           0x0020
    #define TOP_OF_STACK_ADDR       SPLIM
    #define STACK_GUARD_BYTE        0xA1DE
    
    void WriteStackGuardBytes(void)
    {
        static __eds__ int * ptr;
        static unsigned int start_addr;
        static unsigned int addr;
    
        //Use a variable on the stack
        int stack_var;
    
        ptr = &stack_var;
        //Casting to 32-bit first supresses complier warning
        //This chip uses a 16-bit address space
        start_addr = (unsigned long int)ptr + STACK_VAR_PAD;
    
        for (addr = start_addr; addr < TOP_OF_STACK_ADDR; addr += 2)
        {
            *((unsigned int *)(addr)) = STACK_GUARD_BYTE;
        }
    }
    
    unsigned int ReadStackGuardBytes(void)
    {
        static __eds__ int * ptr;
        static unsigned int start_addr;
        static unsigned int addr;
        static unsigned int top_addr;
    
        //Use a variable on the stack here
        int stack_var;
    
        ptr = &stack_var;
        //Casting to 32-bit first supresses complier warning
        //This chip uses a 16-bit address space
        start_addr = (unsigned long int)ptr;
        top_addr = start_addr;
    
        for (addr = start_addr; addr < TOP_OF_STACK_ADDR; addr += 2)
        {
            if (*((unsigned int *)(addr)) != STACK_GUARD_BYTE)
            {
                top_addr = addr;
            }
            else
            {
                break; //make this more robust and search for 2-3 consecutive 
            }
        }
    
        return TOP_OF_STACK_ADDR - top_addr;
    }
    

    【讨论】:

      【解决方案2】:

      这是我最终用于带有 XC16 编译器的 dsPIC33EP512MU810 的代码。通过创建一个自动变量,我可以访问堆栈顶部附近,然后增加几个字节 (STACK_VAR_PAD),并用保护字节填充堆栈的其余部分。

      #define STACK_VAR_PAD           0x0020
      #define TOP_OF_STACK_ADDR       0x4000
      #define STACK_GUARD_BYTE        0xEE
      
      void WriteStackGuardBytes(void)
      {
          static __eds__ char * ptr;
          static unsigned int start_addr;
          static unsigned int addr;
      
          //Use a variable on the stack
          char stack_var;
      
          ptr = &stack_var;
          //Casting to 32-bit first supresses complier warning
          //This chip uses a 16-bit address space
          start_addr = (unsigned long int)ptr + STACK_VAR_PAD;
      
          for (addr = start_addr; addr < TOP_OF_STACK_ADDR; addr++)
          {
              *((unsigned int *)(addr)) = STACK_GUARD_BYTE;
          }
      }
      
      unsigned int ReadStackGuardBytes(void)
      {
          static __eds__ char * ptr;
          static unsigned int start_addr;
          static unsigned int addr;
          static unsigned int top_addr;
      
          //Use a variable on the stack here
          char stack_var;
      
          ptr = &stack_var;
          //Casting to 32-bit first supresses complier warning
          //This chip uses a 16-bit address space
          start_addr = (unsigned long int)ptr;
          top_addr = start_addr;
      
          for (addr = start_addr; addr < TOP_OF_STACK_ADDR; addr++)
          {
              if (*((unsigned int *)(addr)) != STACK_GUARD_BYTE)
              {
                  top_addr = addr;
              }
          }
      
          return TOP_OF_STACK_ADDR - top_addr;
      }
      

      链接器声称堆栈在0x8000 处停止(我将设置设置为生成映射文件),但我看到0x4000 之前的零,然后是垃圾数据。我完全理解堆栈的内容是不确定的,但我也用这种方法确定我的堆栈永远不会超过0x1500 左右,因此只有检查到0x4000 对我有用。

      【讨论】:

        【解决方案3】:

        我相信您知道,等待堆栈溢出是最糟糕的解决方案。溢出可能导致任何行为,包括什么都不做(!),反转数字的符号,改变逻辑流程,以及只在上周五下雨的周二做所有这些。这一切都是随机的,你无法知道它是否发生在你身上。

        您有操作系统(即使是简单的操作系统)?如果是这样,您可以创建一个低优先级任务来监控其他任务堆栈的保护字节(您已知的模式),并在它们未达到预期值时让它执行一些操作。

        如果没有操作系统,那么您将转向静态分析,而不是动态分析。您应该能够从链接器输出中清除有关最坏情况深度的有用信息,这些信息应该为每个调用提供堆栈使用情况,并且知道调用依赖关系图。它不会告诉你一切,因为它不计算堆叠在函数调用之上的中断,但它应该让你非常接近。您取最坏情况编号,添加最坏情况中断编号,然后无论如何都要填充它以确保...

        【讨论】:

        • 因此,Microchip 有一个基于硬件的堆栈溢出异常,因此行为是明确定义的。我没有操作系统,我更喜欢编写低级事件驱动代码:低优先级任务可以轻松插入定时器中断。我已经分析了调用依赖关系,但我想验证我的分析是否正确。核心问题是我不知道如何获得对堆栈上字节的读/写访问权限。我知道自动变量被推入堆栈,但我不知道如何找到顶部或底部并初始化保护字节。
        • 链接器映射文件应该告诉您堆栈正式开始和结束的位置。但我建议查看链接器手册 (ww1.microchip.com/downloads/en/DeviceDoc/51833A.pdf) 第 11.5 节,其中进一步描述了堆栈分配。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-16
        • 1970-01-01
        • 1970-01-01
        • 2013-03-20
        • 1970-01-01
        • 2021-12-21
        相关资源
        最近更新 更多