Вопрос

I need to do a run-time stack analysis on embedded hardware to ensure I've allocated enough stack space. I understand the basic theory. On boot, you initialize the stack with a known pattern (such as all 0xFF or 0xAA), then allow your program to run. As it runs the stack grows and shrinks but it never restores the original pattern. After a sufficiently long enough period, examine the stack and find the address of the last stack value to be altered.

I do not need a portable solution, just a solution for the dsPIC33EP512MU810 and PIC32MX795F512. My fallback plan is to incrementally decrease the stack size until I get a stack overflow, but that only works once I have a release candidate, I'd much prefer a solution that continually monitors throughout development and production.

Это было полезно?

Решение

I got some errors with the above code from "DrRobotNinja" on the dsPICEP series, mostly made everything use 16 bit variables + increased the address by 2 instead of 1 to stop address error's by keeping things word aligned

#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;
}

Другие советы

This is the code I wound up using for the dsPIC33EP512MU810 with the XC16 complier. By creating an automatic variable, I get access near the top of the stack, then increment a few bytes (STACK_VAR_PAD), and fill the rest of the stack with guard bytes.

#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;
}

The linker claims the stack stops at 0x8000 (I set the setting to generate a map file) but I see zeroes up to 0x4000 and then garbage data past that. I fully understand that the contents of the stack are undetermined, but I also determined with this method that my stack never grows above 0x1500 or so, thus only checking to 0x4000 works for me.

As I'm sure you know, waiting for a stack overflow is the worst possible solution. Overflows can cause any behavior at all, including doing nothing (!), reversing the sign of a number, changing logic flow, and doing all of this only on Tuesdays when it rained the previous Friday. It's all random and you have no way of knowing whether it happened to you.

Do you have a OS (even a simple one)? If so, you can create a low-priority task to monitor the guard bytes (your known pattern) of the other task's stacks and have it do something if they're not at the expected value.

If no OS, then you're moving to a static analysis, not a dynamic one. You should be able to clean useful information about worst-case depth from your linker output, which should give stack usage for each call, and knows the call dependency graph. It won't tell you everything, as it doesn't count for interrupts stacked on top of function calls, but it should get you very close. You take the worst case number, add the worst case interrupt number, then pad it anyway just to be sure...

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top