Dynamic stack checking & new Pokitto Crash Screen

In programming many good things has been invented out of laziness or if something is annoying enough. I was fed up with getting stack overflow situations out of the blue, and decided to make a stack checking function for the run time. In MicroPython, I statically allocate as much ram as possible for the MP heap memory, taking into account that the stack needs ram too. In MP implementation the C language function call chains can be over 20 levels deep, so it needs quite much ram for stack (in each function call many of the 32-bit registers are pushed to stack first, and also e.g. local variables need stack).

The problem is that there is no control in Pokitto (or in many other MCU devices) for the stack usage. If the stack gets too big it just overwrites statically allocated data (BSS) or heap. The stack in Pokitto starts in a high memory location and the pointer decreases when more data is pushed to the stack. The static data and heap is allocated from low addresses upwards. When these collide, ram is overwritten and anything can happen(!).

Many times, after updating PokittoLib in my env, have been literally wasting hours for searching various problems in Python when the reason was actually a stack overflow (!).

To fix this I am now frequently checking the stack overflow in MP code. It is done by quickly comparing the stack pointer address to the end of the BSS chunk. That do not yet take dynamic heap allocation into account as it is rarely used in code. When the stack pointer is decreasing near to the BSS chunk end, I am showing the Pokitto Crash Screen as follows. So the problem is immediately visible on HW.

The cryptic hex sequence (Guru Meditation number :wink: ) is the size of the stack (“7ec”) and the address of the stack pointer (“10007814”). Pokitto Crash Screen can be re-used for other messages and data also. I am using C64UIGfx font to “print” Pokitto image and the frame on screen.

5 Likes

I love it. Because this error occurs mostly during dev, message could be shorter. “Restart” etc. is unnecessary for devs and unlikely to occur with released apps

1 Like

Laziness is a virtue.


@Hanski do you have a code snippet to show us?

(I really enjoy seeing code snippets.)


@jonne I’m half and half about the message shortening.

On the one hand I agree that stack overflows shouldn’t occur in a well written program and that all a developer would need is stack size and the stack address, but on the other hand I don’t like assuming that an error won’t ever occur in production.

Perhaps it should be selectable with a compiler flag?
e.g. STACK_OVERFLOW_PRINT_FRIENDLY (or STACK_OVERFLOW_PRINT_MINIMAL, depending on what should be considered the ‘default’).


Musing to myself:

It’s a shame that there isn’t an interrupt that fires when the stack overflows like there is on some systems.

1 Like

I am not saying it won’t happen in production. But to use 45 bytes for a message that shouldn’t occur if program was tested prior to release is in my books excessive! :smiling_imp:

Otherwise I think its - yet another - great contribution from @Hanski .

I will give him a badge for this!

2 Likes

Fair enough.

Hopefully if any non-programmers ran into such a message in production they’d post a topic about it anyway.

It’s certainly deserved.

And here we go. I will give the badges like this from now on. So people see them!!

3 Likes

Cool, thanks!

And I actually like that error message. Lets keep it like it is!

Could you provide demo code for showing it :grinning:?

Yes, the same message can be used for other crash errors also. Feel free to change the layout or texts if needed. I will make a pull request with a demo code soon.

Below are the code snippets. I have added this to the PokittoDisplay.cpp file. I keep it C-functions, so I can call it directly from the MicroPython library. A Display class method wrapper can be added if needed.

extern "C" void CheckStack();
extern char _ebss[];  // In map file
extern char _vStackTop[];  // In map file
// Check the stack size and show a crash screen if the stack is too big.
void CheckStack() {
    #ifndef POK_SIM
    int currStackTop;
    const int freeStackThreshold = 400;
    if ((int)&currStackTop - (int)_ebss < freeStackThreshold) {

        // Create info string: "<stack size>:<current stack pointer>", e.g. "12345ABC:12345ABC"
        const int infoStringLen = 8+1+8;
        static char infoString[infoStringLen+1];
        memset(infoString,0,infoStringLen+1);
        const int stackSize = (int)_vStackTop - (int)&currStackTop;
        const int tmpStrLen = 8;
        static char tmpStr[tmpStrLen+1];
        memset(tmpStr,0,tmpStrLen+1);
        char* subStr = itoa_hex(stackSize, tmpStr, tmpStrLen); // keep ending null
        strcat(infoString, subStr); // Add stack size as hex string
        strcat(infoString, ":");
        subStr = itoa_hex((int)&currStackTop, tmpStr, tmpStrLen); // keep ending null
        strcat(infoString, subStr); // Add stack pointer address as hex string

        // Draw the crash screen and wait forever. Use static string to avoid more stack usage (!)
        static const char* texLine1 = "OOOPS! PLEASE,";
        static const char* texLine2 = "RESTART POKITTO OR";
        static const char* texLine3 = "RELOAD SOFTWARE.";
        static const char* texLine4 = "STACK TOO BIG!";
        ShowCrashScreenAndWait(texLine1, texLine2, texLine3, texLine4, infoString);
    }
    #endif
}
// Draw the crash screen and wait forever
void ShowCrashScreenAndWait( const char* texLine1, const char* texLine2, const char* texLine3, const char* texLine4, const char* texLine5 ) {

    // draw screen red
    lcdFillSurface(COLOR_RED);

    // Draw text
    Display::directcolor = COLOR_WHITE;
    Display::invisiblecolor = COLOR_RED;
    Display::directbgcolor = COLOR_RED; 
    Display::directtextrotated = false;
    Display::adjustCharStep = 0;
    Display::adjustLineStep = 0;
    Display::setFont(fntC64UIGfx);  // A special font set that contains UI gfx in lower case letters.
    Display::fixedWidthFont = true; // Needed for the non-proportional C64 font (default value=false)
    Display::enableDirectPrinting(true);

    // Draw texts
    int  yOffsetInPixels = 60;
    Display::set_cursor(0, 9 + yOffsetInPixels);
    Display::print("     ");    Display::println(texLine1);
    Display::print("     ");    Display::println(texLine2);
    Display::print("     ");    Display::println(texLine3);
    Display::println();
    Display::print("     *");   Display::println(texLine4);
    Display::print("     *");   Display::println(texLine5);

    // Pokitto image and frame
    Display::set_cursor(0, 0 + yOffsetInPixels);
    Display::println("    abbbbbbbbbbbbbbbbbbbbbc");
    Display::print  ("ijkl|"); Display::println(26*8, Display::cursorY,"|");
    Display::print  ("mnop|"); Display::println(26*8, Display::cursorY,"|");
    Display::print  ("qrst|"); Display::println(26*8, Display::cursorY,"|");
    Display::print  ("uvwx|"); Display::println(26*8, Display::cursorY,"|");
    Display::print  ("    |"); Display::println(26*8, Display::cursorY,"|");
    Display::print  ("    |"); Display::println(26*8, Display::cursorY,"|");
    Display::println("    dbbbbbbbbbbbbbbbbbbbbbe");

    // loop forever
    while(1){;}
 }

edit: Changed those static strings to const to make sure they stay in rom(!)

1 Like

Changed the layout a bit. There is now more space for text and I think it looks more cartoonish this way :slight_smile:

I just barely managed not to make also some animations on Pokitto image as just staying in a while loop is so boring. Well, I have other coding waiting in the queue…

3 Likes

I have made a pull request of the Stack overflow check and Pokitto Crash Screen functions. There is a test program which makes deeper and deeper recursive function calls until the stack is near to overflow and the Crash Screen is shown. I also added four “hard edge” graphics chars to C64UIgfx font. I had to sacrifice ‘{’ and '}'chars because of that. I assume they are not used with that font anywhere (?).

The CheckStack() function itself is very light so it can be checked often in the code.

@jonne For some unknown reason the newer version of EmBitz I am using is reordering the targets in *.epb file. The only real change is that I added TestCrashScreen target.

1 Like

Ok. I am back in Finland tomorrow and will check it out

1 Like