Microchip PIC32和dsPIC33E上的运行时堆栈监视

时间:2013-11-04 01:38:42

标签: embedded stack-overflow microchip pic32

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

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

3 个答案:

答案 0 :(得分:1)

这是我用于带有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对我有效。

答案 1 :(得分: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 :(得分:0)

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

你有OS(即使是简单的OS)吗?如果是这样,您可以创建一个低优先级的任务来监视其他任务堆栈的保护字节(您已知的模式),如果它们没有达到预期值,它会执行某些操作。

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