0

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

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

4

3 回答 3

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对我有用。

于 2013-11-18T16:23:15.247 回答
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;
}
于 2015-08-31T22:55:49.407 回答
0

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

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

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

于 2013-11-04T15:17:10.497 回答