How to watch for out-of-memory errors?

PhilK

Member
Hi,

If the stack or malloc heap in Teensy 4.x grows too large for the space available, how will I know? I'm starting to suspect out-of-memory errors in some strange crashes I'm seeing. Is there some facility or function I can use within my sketch to constantly monitor free memory?

Thanks!
 
Last edited:
I believe maybe @FrankB may have library to help show memory usage and the like...

I do have an example sketch in the ST7735 library (uncannyEyes_asyc_st7789_240x240... which has some stuff I was doing back then to see if I am running out of stack space.

Again T4.x code, that has a function to call at the start of setup(DumpMemoryInfo) and one that I use in loop to see how far a stack has gone.... EstimateStackUsage...
Code:
// from the linker
//  extern unsigned long _stextload;
extern unsigned long _stext;
extern unsigned long _etext;
//  extern unsigned long _sdataload;
extern unsigned long _sdata;
extern unsigned long _edata;
extern unsigned long _sbss;
extern unsigned long _ebss;
//  extern unsigned long _flexram_bank_config;
extern unsigned long _estack;

void DumpMemoryInfo() {
#if defined(__IMXRT1062__) && defined(DEBUG_MEMORY)
  uint32_t flexram_config = IOMUXC_GPR_GPR17;
  Serial.printf("IOMUXC_GPR_GPR17:%x IOMUXC_GPR_GPR16:%x IOMUXC_GPR_GPR14:%x\n",
                flexram_config, IOMUXC_GPR_GPR16, IOMUXC_GPR_GPR14);
  Serial.printf("Initial Stack pointer: %x\n", &_estack);
  uint32_t dtcm_size = 0;
  uint32_t itcm_size = 0;
  for (; flexram_config; flexram_config >>= 2) {
    if ((flexram_config & 0x3) == 0x2) dtcm_size += 32768;
    else if ((flexram_config & 0x3) == 0x3) itcm_size += 32768;
  }
  Serial.printf("ITCM allocated: %u  DTCM allocated: %u\n", itcm_size, dtcm_size);
  Serial.printf("ITCM init range: %x - %x Count: %u\n", &_stext, &_etext, (uint32_t)&_etext - (uint32_t)&_stext);
  Serial.printf("DTCM init range: %x - %x Count: %u\n", &_sdata, &_edata, (uint32_t)&_edata - (uint32_t)&_sdata);
  Serial.printf("DTCM cleared range: %x - %x Count: %u\n", &_sbss, &_ebss, (uint32_t)&_ebss - (uint32_t)&_sbss);
  Serial.printf("Now fill rest of DTCM with known pattern(%x - %x\n", (&_ebss + 1), (&itcm_size - 10)); Serial.flush(); //
  // Guess of where it is safe to fill memory... Maybe address of last variable we have defined - some slop...
  for (uint32_t *pfill = (&_ebss + 32); pfill < (&itcm_size - 10); pfill++) {
    *pfill = 0x01020304;  // some random value
  }
#endif
}
void EstimateStackUsage() {
#if defined(__IMXRT1062__) && defined(DEBUG_MEMORY)
  uint32_t *pmem = (&_ebss + 32);
  while (*pmem == 0x01020304) pmem++;
  Serial.printf("Estimated max stack usage: %d\n", (uint32_t)&_estack - (uint32_t)pmem);
#endif
}

As for figuring out Heap usage, I will sometimes call the _sbrk function with asking for 0 increment, which will return the current start of the heap.
This is in startup.c.
Code:
#include <errno.h>

// from the linker script
extern unsigned long _heap_start;
extern unsigned long _heap_end;

char *__brkval = (char *)&_heap_start;

void * _sbrk(int incr)
{
        char *prev = __brkval;
        if (incr != 0) {
                if (prev + incr > (char *)&_heap_end) {
                        errno = ENOMEM;
                        return (void *)-1;
                }
                __brkval = prev + incr;
        }
        return prev;
}

Again this will NOT show you how much space has been freed up internally in your memory allocations.
But sometimes I will add debug code that does something like:

Serial.print((uint32_t)_sbrk(0), HEX);

Or go a little more advanced like in my estimate code above, maybe save it and only print again if it changes, and can estimate how close you are to running out of memory. By comparing it to where the heap will end... As it will always be the same spot:
From Linker scripts:
_heap_end = ORIGIN(RAM) + LENGTH(RAM);
as:
RAM (rwx): ORIGIN = 0x20200000, LENGTH = 512K
 
I believe maybe @FrankB may have library to help show memory usage and the like...

I do have an example sketch in the ST7735 library (uncannyEyes_asyc_st7789_240x240... which has some stuff I was doing back then to see if I am running out of stack space.

Again T4.x code, that has a function to call at the start of setup(DumpMemoryInfo) and one that I use in loop to see how far a stack has gone.... EstimateStackUsage...
Code:
// from the linker
//  extern unsigned long _stextload;
extern unsigned long _stext;
extern unsigned long _etext;
//  extern unsigned long _sdataload;
extern unsigned long _sdata;
extern unsigned long _edata;
extern unsigned long _sbss;
extern unsigned long _ebss;
//  extern unsigned long _flexram_bank_config;
extern unsigned long _estack;

void DumpMemoryInfo() {
#if defined(__IMXRT1062__) && defined(DEBUG_MEMORY)
  uint32_t flexram_config = IOMUXC_GPR_GPR17;
  Serial.printf("IOMUXC_GPR_GPR17:%x IOMUXC_GPR_GPR16:%x IOMUXC_GPR_GPR14:%x\n",
                flexram_config, IOMUXC_GPR_GPR16, IOMUXC_GPR_GPR14);
  Serial.printf("Initial Stack pointer: %x\n", &_estack);
  uint32_t dtcm_size = 0;
  uint32_t itcm_size = 0;
  for (; flexram_config; flexram_config >>= 2) {
    if ((flexram_config & 0x3) == 0x2) dtcm_size += 32768;
    else if ((flexram_config & 0x3) == 0x3) itcm_size += 32768;
  }
  Serial.printf("ITCM allocated: %u  DTCM allocated: %u\n", itcm_size, dtcm_size);
  Serial.printf("ITCM init range: %x - %x Count: %u\n", &_stext, &_etext, (uint32_t)&_etext - (uint32_t)&_stext);
  Serial.printf("DTCM init range: %x - %x Count: %u\n", &_sdata, &_edata, (uint32_t)&_edata - (uint32_t)&_sdata);
  Serial.printf("DTCM cleared range: %x - %x Count: %u\n", &_sbss, &_ebss, (uint32_t)&_ebss - (uint32_t)&_sbss);
  Serial.printf("Now fill rest of DTCM with known pattern(%x - %x\n", (&_ebss + 1), (&itcm_size - 10)); Serial.flush(); //
  // Guess of where it is safe to fill memory... Maybe address of last variable we have defined - some slop...
  for (uint32_t *pfill = (&_ebss + 32); pfill < (&itcm_size - 10); pfill++) {
    *pfill = 0x01020304;  // some random value
  }
#endif
}
void EstimateStackUsage() {
#if defined(__IMXRT1062__) && defined(DEBUG_MEMORY)
  uint32_t *pmem = (&_ebss + 32);
  while (*pmem == 0x01020304) pmem++;
  Serial.printf("Estimated max stack usage: %d\n", (uint32_t)&_estack - (uint32_t)pmem);
#endif
}

As for figuring out Heap usage, I will sometimes call the _sbrk function with asking for 0 increment, which will return the current start of the heap.
This is in startup.c.
Code:
#include <errno.h>

// from the linker script
extern unsigned long _heap_start;
extern unsigned long _heap_end;

char *__brkval = (char *)&_heap_start;

void * _sbrk(int incr)
{
        char *prev = __brkval;
        if (incr != 0) {
                if (prev + incr > (char *)&_heap_end) {
                        errno = ENOMEM;
                        return (void *)-1;
                }
                __brkval = prev + incr;
        }
        return prev;
}

Again this will NOT show you how much space has been freed up internally in your memory allocations.
But sometimes I will add debug code that does something like:

Serial.print((uint32_t)_sbrk(0), HEX);

Or go a little more advanced like in my estimate code above, maybe save it and only print again if it changes, and can estimate how close you are to running out of memory. By comparing it to where the heap will end... As it will always be the same spot:
From Linker scripts:
_heap_end = ORIGIN(RAM) + LENGTH(RAM);
as:
RAM (rwx): ORIGIN = 0x20200000, LENGTH = 512K

This is an older thread, but it addressed exactly what I was searching for today: how to get an idea of the headspace (footspace?) for the stack.
I suspect I have code that has eaten up too much RAM so that there's not enough free stack space anymore. My T4 application crashes, crashreport does not give useful hints on what's wrong where.
So I tried the code above, but it fails compiling, it says:

C:\Users\HP\OneDrive\Documents\Arduino\AJD_Teensy.ino:545:22: error: conflicting declaration 'long unsigned int _ebss'
545 | extern unsigned long _ebss;
| ^~~~~
In file included from C:\Users\HP\OneDrive\Documents\Arduino\AJD_Teensy.ino:2:
C:\Users\HP\AppData\Local\Arduino15\packages\teensy\hardware\avr\1.58.1\libraries\SdFat\src/FreeStack.h:56:16: note: previous declaration as 'uint8_t _ebss'
56 | extern uint8_t _ebss;
| ^~~~~

How to get around this?
 
Solved: I had a leftover #include <FreeStack.h> in my project file...
And yes I did suffer from stack overflow, mainly because I had a huge array declared inside my main loop(). So once loop() executed, that huge array ended up living on the stack, so that any call from within loop() was an attempt to push more onto the stack than what can fit in there...

(A true debugger option for Teensy4, integrated in Arduino would have saved me 2 days to find my bug. My J-Link Plus is within hand reach, but...).
 
Back
Top