Teensy 4.1 : cannot make use of "free" memory - resulting in FW crash

Status
Not open for further replies.

tjaekel

Well-known member
I see on compile log, some "free" memory left.
I want to increase a buffer to make use of it.
But the code of my project crashes. Why?

Here, compile log when it still works:
Code:
Memory Usage on Teensy 4.1:
  FLASH: code:270504, data:88660, headers:8448   free for files:7758852
   RAM1: variables:178592, code:256712, padding:5432   free for local variables:83552
   RAM2: variables:145152  free for malloc/new:379136

Now, I change a definition, from:
Code:
#define SDRAM_SIZE_BYTES      (1024 * 32 * 4)
to:
Code:
#define SDRAM_SIZE_BYTES      (1024 * 32 * 5)
which means: increase an unsigned char buffer size from 128K to (128K + 32K) now.

The compile log is now this:
Code:
Memory Usage on Teensy 4.1:
  FLASH: code:270504, data:88660, headers:8448   free for files:7758852
   RAM1: variables:211360, code:256712, padding:5432   free for local variables:50784
   RAM2: variables:145152  free for malloc/new:379136

It makes sense:
now 50784 free (before 83552), so: 32768 bytes more now used (my exact 32K increase on buffer size).
it looks still ok, still some "free" left, no complains. All compile clean. No code changes, just a bit larger buffer.

BUT THE FW CRASHES!

I wait for UART: LUCKILY!
When I start terminal on PC - my FW crashes!
Nothing on terminal!
And I see a flashing LED: 2x with pause
The generate FW has crashed, even without any complains from linker, no code changes, nothing really modified (just a buffer size increased).

Remark:
How to recover from it?
As long as I see this flashing LED - I cannot flash the board anymore
(sure, it runs in Hardfault Handler endlessly for the rest of its life, the bootloader is dead).

Luckily, when I press the program button again - and I do NOT start my UART terminal (MCU still waiting for it),
the boot loader is alive again (due to waiting for Serial).

But it tells me this:
If you do not wait for UART and your FW crashes on startup- you do not have a chance to recover the board
(just by this 'trick' to press program button long enough until the "blinky" is flashed)

Conclusion:
Never write your FW in a way that it would NOT wait for Serial connected!
This "waiting for Serial" gives you the option to re-flash again, but when running immediately (without waiting) into issues:
your board is almost bricked.

My main concern:
Why I cannot make use of this "free" memory reported?
 
That "free" memory is where your stack is located. If your code needs more than 50K of free stack space to run, then it will crash
 
post on other thread made note of the stack.

This line accounts for known compile time data allocations {variables:178592} and what is "free for local variables:83552" as @thebigg notes here - is where running functions allocate space from the Stack:
> RAM1: variables:178592, code:256712, padding:5432 free for local variables:83552
Anything not needed for global or static variables known at compile time is left 'unused' and free for runtime use as the stack noted p#2
Dynamic allocations come from the heap which is allocated in DMAMEM/RAM2

PJRC puts the Button on the Teensy specifically to make it 'unbrickable'. It is easy to make code that quickly kills or faults the processor - "C" is great that way.

While(!Serial) is critical for seeing ALL first messages - esp CrashReport - even for running code as the Teensy is faster than the connect time to a PC.

Teensy code is not a protective OS - with 'c' code the many things can be broken - USB trashed, or other, with bad code can make it non-responsive. Often it leads to/from a missed compiler warning or erroneous code.
 
This makes a bit of sense, even it is confusing.
Thank you.

My assumption was:
"free" tells me, how much free space left in this memory, considering also the (reserved) size of the stack region.
The stack region could be a fix size region, with known total space for stack (but obviously not here).

But when I have a look at the linker script:
_estack is calculated.
So, I guess: the stack is after all the .bss, .data etc. sections, the remaining part of the DTCM.
And the size of stack is not really considered in the compile report. There is no "warning about insufficient stack size left".

Telling me "free" is 85K and increasing a buffer by 32K, still some "free" memory, results in a crash:
potentially, the stack is now outside the memory (even "free" is still there), or overlapping with some other data.
My guess: the linker script (actually the "free" reported on IDE), does not consider the stack size still needed.

It is a bit sad: I have no clue how much stack is needed. But bad is: that linker script does not have a minimum "required" size for stack.
When I add more data and I push the stack bottom towards stack top (_estack, the space left for stack) - no warning.

OK, I see now in *.lst file:
the _estack (which is top of stack), is set to 0x20040000 (correct: the end of 256K in DTCM)
Code:
	__asm__ volatile("mov sp, %0" : : "r" ((uint32_t)&_estack) : );
60001664:	4b06      	ldr	r3, [pc, #24]	; (60001680 <ResetHandler+0x2c>)
60001666:	469d      	mov	sp, r3
	__asm__ volatile("dsb":::"memory");
60001668:	f3bf 8f4f 	dsb	sy
	__asm__ volatile("isb":::"memory");
6000166c:	f3bf 8f6f 	isb	sy
	ResetHandler2();
60001670:	f7ff fec6 	bl	60001400 <ResetHandler2>
60001674:	400ac000 	.word	0x400ac000
60001678:	aaaaffff 	.word	0xaaaaffff
6000167c:	00200007 	.word	0x00200007
60001680:	20040000 	.word	0x20040000
But there is not a dedicated reserved region for the stack: the bottom of the stack is the end of unused memory.
The top of stack (_estack) is the end of DTCM - OK.

What seems to happen:
"free" tells me 82K still available, I increase buffer sizes.
But now the "bottom of stack" is moved, not so much stack space left anymore (towards _estack as "top of stack").
The stack region is shrinking (top of stack is fix, but bottom of stack is moving upwards).

Now, on startup, the "zero-fill" procedure kicks in, writes zero to all my buffers, but these buffers overlap with the bottom of the stack,
potentially the "stack in use".
The stack region is touched, overwritten, the program "must" crash.

OK, I got it know as:
"free" tells me actually how much stack is available (nobody knows and unable to predict).
There is no warning about a minimal stack size if not available anymore.

CONCLUSION:
Take the value reported as "free" mainly as: "how much stack space is left".
Do not try to make use of this "free" reported memory, if your project needs a large stack space.
Leave a "free" margin there. How much? Trial and error.

If your program starts to crash - lower buffer size in DTCM, move to somewhere else (e.g. DMAMEM),
give a bit more "free" as stack size.

Good point. Thank you.
 
Thank you.
I got it now.

"free" is actually a hint how much stack space is left ("free for local variables"),
not a hint if I could increase buffer sizes.

Teensy is not unbreakable (as I have realized).
Even writing C++ code - a large project can brick the board.
(I am not expecting such one)

BTW: when it crashes (as in my case): there is no way to see a crash report (because already before it can be sent).
After UART connects - it crashes (even for the report).

I am not expecting a protective OS, just "trusting the compile reports": 85K left, and unable to increase a buffer by 32K, still a lot of "free"...
(and surprised why so much stack would be needed on startup...)

Never mind.
Thank you.
I understand now what seems to happen.
(just a bit frustrated that "free" does not help to tweak buffer sizes, I would need a bit more data space...).
 
Most 'single memory' 'c' platforms have heap above compile data and it builds UP to the bottom of the Stack as the Stack at top of memory builds down.

Both of those are DYNAMIC - i.e. not known at compile time - so either can grow into the other and the world ends.

With the 1062 design the HEAP allocs from DMAMEM/RAM2 where it starts above compile allocs of DMAMEM - and has a known END and size. So as long as new/malloc and pointers are safely used when not returned NULL that region is "safe", it just runs at 0.25 CPU speed (with the aide of a 32KB data cache).

Where RAM1, as seen, has ITCM at '0' and builds up to DTCM that "maps" after the last used 32KB block used by ITCM and that address is 'called'/Liinker identified as 0x20000000 for 'reference' but actually reside in fast TCM addressable RAM just bytes away from ITCM.

So that ends RAM1 'compiler/linker/known' memory allocations. Indeed, everything after that to the end of that 512KB is "FREE" and where the Stack is placed.

There are posts showing ways to track Stack and Heap allocations and free space at runtime - but that isn't something the compiler can know as it would have to know what code calls what code and track all 'local variable' and stack return usage along the way, and then there is recursive code calling itself using more stack space each time based on runtime values.
 
Teensy is not unbreakable (as I have realized).
Even writing C++ code - a large project can brick the board.

As I mentioned on your other thread with dramatic claims about Teensy's memory, we generally expect you to follow the Forum Rule when making this sort of statement. Please show a test program which "can brick the board".
 
Thank you.
Great details, I appreciate (and understand).

I would prefer to have a minimal stack region size in linker script:
let me set stack region size and fire a warning towards me if not enough left.
(I have this approach in all my STM32 projects: they have reserved stack regions).

Reporting "free" and no warnings... confuses me.

Sure, I can run with "profile recording", "stack coloring" etc., just to figure out how much is really used.
It does not help really, because I have to call the functions "in the call tree" for the "worst case condition" (deepest call try, imagine, I would have variable recursion depth...).
And it crashes already before any report can be generated or displayed on UART (a real debugger would be helpful).

Anyway, I am still a bit surprised:
I use FreeRTOS and as I understand: all my RTOS threads (and almost all code is part of a thread) have their own stack region. This RTOS stack is not the system stack,
it is allocated as a buffer and assigned from there to the threads.
So, the crash happens on startup, before the RTOS scheduler is kicked off, but after the Serial is connected.

I am surprised, why 80K of stack space is needed, just during startup.
In my STM32 FW projects, I have usually just 2K of MCU stack, the RTOS stacks are allocated already independently. Even when an INT happens, and the RTOS is intercepted, an INT in "Supervisor mode",
those INT Handler do not need really so much stack.

Why I have to keep 80K "free" for stack variables (local)?
(otherwise is starts crashing). It sounds so much to me (maybe a startup function call has such a huge local variable ... - not really nice).

Is there any clue how much stack size is needed for a typical startup (before my sketch code starts using the stack)?
And I hope: my RTOS threads have their own stack, not consumed from the system stack, coming from an allocated buffer - I hope
(it is the case on STM32 projects, FreeRTOS for Teensy - if they take stack from MCU stack - it would be very bad - I have to dive into code...
Right now I am guessing: it looks like, the FreeRTOS takes the thread stack from the MCU stack - this would explain why I see such a huge stack size needed..)

I will figure out.
All OK, thank you, great "talk" and help.
 
This is the example:
https://github.com/tjaekel/Teesny_4_1

compile and run as it is after download:
all fine - UART (terminal) should be there - a welcome message and prompt, acting to user input.

Prerequisites:
it uses: QNEthernet and freertos-teensy
The TsyDMASPI should be part of the sketch.

Next:
go to file "platform.h" and change from:
Code:
#define SDRAM_SIZE_BYTES      (1024 * 32 * 4)
to
Code:
#define SDRAM_SIZE_BYTES      (1024 * 32 * 5)

Compile, flash...:
no prompt anymore, no crash report, LED blinks twice.

Try to flash again - as long as the LED blinks - no success, COM port is not there.

Close the terminal (don't run), press "program" button : now you can flash again
(luckily due to fact that it is waiting for Serial, otherwise the project code would crash immediately again and no way to flash again)
 
This is the example:
...
all fine - UART (terminal) should be there - a welcome message and prompt, acting to user input.
...

Next:
go to file "platform.h" and change from:
Code:
#define SDRAM_SIZE_BYTES      (1024 * 32 * 4)
to
Code:
#define SDRAM_SIZE_BYTES      (1024 * 32 * 5)
...

That is USB - maybe the terminal program labels it a UART - Many Arduino units with no USB may use a UART - but it is native USB on Teensy and using UART suggests a Serial port not the Teensy Device USB port.

Not ever seen that or heard of any reason to mess with whatever that does. Not aware of any 32K SDRAM component on the Teensy's 1062. The only 32KB elements are the CODE and DATA cache within the processor realm. Platform.h does not seem to be a file in the TeensyDuino distribution? Why would that be changed? Seems like something not to change.
 
I am surprised, why 80K of stack space is needed, just during startup.
In my STM32 FW projects, I have usually just 2K of MCU stack, the RTOS stacks are allocated already independently. Even when an INT happens, and the RTOS is intercepted, an INT in "Supervisor mode",
those INT Handler do not need really so much stack.

Why I have to keep 80K "free" for stack variables (local)?
(otherwise is starts crashing). It sounds so much to me (maybe a startup function call has such a huge local variable ... - not really nice).

Is there any clue how much stack size is needed for a typical startup (before my sketch code starts using the stack)?

You keep asking why the Teensy needs such a large stack at startup. You seem to be attributing the large stack usage to some typical Teensy startup function, but that is completely wrong. I would guess that it would be more appropriate to attribute that large stack usage to something in your implementation of freeRTOS which requires that much stack for startup, or more specifically, something that you are doing in the way that you are using the capabilities of freeRTOS which requires that much stack for startup. Ditch the freeRTOS & your problems will be solved [intentional over generalization ] !!

In the latest version of my TeensyMIDIPolySynth, I have a T4.1 running all MIDI interfaces (traditional 5-pin DIN IN/OUT, usbMIDI, & hostusbMIDI), as well as an 800x480 TFT touchscreen, & a T4.0 running all audio (Audio Adapter) capabilties. The T4.0 audio design includes over 600 audio objects (modulated waveform generators, mixers, filters, envelope generators, noise generators, string generators, etc.) with over 1000 interconnects. In addition, the two Teensys are intercommunicating with each other over a (real UART) serial connection (Serial4) running at 500000bps.

Code:
Here are the reported memory usage maps for each:

T4.1 MIDI/display processor:

Memory Usage on Teensy 4.1:
  FLASH: code:342304, data:92448, headers:8636   free for files:7683076
   RAM1: variables:111008, code:337160, padding:23288   free for local variables:52832
   RAM2: variables:50336  free for malloc/new:473952

T4.0 audio processor:

Memory Usage on Teensy 4.0:
  FLASH: code:189748, data:40112, headers:8728   free for files:1793028
   RAM1: variables:321472, code:173704, padding:22904   free for local variables:6208
   RAM2: variables:79296  free for malloc/new:444992


As you can see, the T4.0 audio processor has only 6208 bytes available for stack. It starts up & runs just fine in this state. I would point to this as an indication that (without your implementation of freeRTOS) there is not some unknown Teensy startup activity that requires a very large stack as you have implied.

Some things to consider:
- In your sketch, what kind of large arrays are being passed on the stack instead of being passed by reference ??
- In your sketch, what kind of large arrays are needing to be temporarily saved on the stack when the RTOS interrupts one thread to execute another ??

Something specifically in your sketch (whether it be something that freeRTOS is doing under-the-covers, or something that you are explicitly doing in your implementation) is specifically calling for that very large stack requirement. As a suggested exercise, take your base program, remove everything that depends upon freeRTOS, & run what remains to see if you still have any startup/crash problems.

Hope that helps . . .

Mark J Culross
KD5RXT
 
Reporting "free" and no warnings... confuses me.

Sure, I can run with "profile recording", "stack coloring" etc., just to figure out how much is really used.
It does not help really, because I have to call the functions "in the call tree" for the "worst case condition" (deepest call try, imagine, I would have variable recursion depth...).

You acknowledge that using these tools/approaches to manually determine the minimum stack size required would be difficult for you to execute, but yet you want the compiler (which does not necessarily need to know anything about your actual calling tree, your use of recursion, etc., but rather only about your defined functions and their individual passed parameter lists, etc.) to make this same determination ??

I would prefer to have a minimal stack region size in linker script:
let me set stack region size and fire a warning towards me if not enough left.
(I have this approach in all my STM32 projects: they have reserved stack regions).

And again, exactly how would the compiler determine "what's needed" without doing an in-depth analysis of your actual calling tree, your use of recursion, as well as your defined functions and their individual passed parameter lists, etc. ?? Setting any arbitraty stack size will only be a best guess. At runtime (especially when using a non-deterministic RTOS as you are), this arbitrary maximum could be exceeded at any time, depending (unpredictably) upon when/how real-world conditions may align.

Mark J Culross
KD5RXT
 
I am surprised, why 80K of stack space is needed, just during startup.

Here is a small program which leaves less than 3K stack space ("free for local variables").

Code:
volatile uint8_t buf1[485000];
DMAMEM volatile uint8_t buf2[480000];

void setup() {
  buf1[0] = 0;
  buf2[0] = 0;
  Serial.begin(9600);
  while (!Serial) ; // wait for serial monitor
  Serial.println("hello world");
}

void loop() {
}

Anyone can run this to see Teensy 4.1 does indeed start up and print "hello world" without 80K free stack space.

Why your huge program won't run without 80K stack space, I can not say. I'm not going to spent the time to dig through such a large program.

But the fact that this small program is able to run with less than 3K stack space pretty clearly shows the problem is not Teensy's libraries needing 80K stack space. Whatever is in need of so much stack space is very likely within your program.
 
Just in case there is any concern that sample program wrote to only 1 byte in each buffer, here is another quick test which overwrites all of the large buffer memory in both RAM1 and RAM2. Runs fine, with less than 3K for stack.

Code:
uint8_t buf1[485000];
DMAMEM uint8_t buf2[480000];

void setup() {
  Serial.begin(9600);
  while (!Serial) ;  // wait for serial monitor
  Serial.println("hello world before buffer clear");
  memset(buf1, 0x5A, sizeof(buf1));
  memset(buf2, 0xA5, sizeof(buf2));
  Serial.println("hello world after buffer clear");
}

void loop() {
}
 
If you are interested in identifying functions using huge amounts of stack space I find using the compiler flag -fstack-usage quite easy. It generates a *.su file for each compilation unit which lists the function and its used stack space:
Here for example the output generated for print.cpp
Code:
C:\toolchain\Arduino\arduino-1.8.19\hardware\teensy\avr\cores\teensy4/Print.h:62:14:virtual int Print::availableForWrite()	0	static
C:\toolchain\Arduino\arduino-1.8.19\hardware\teensy\avr\cores\teensy4/Print.h:63:15:virtual void Print::flush()	0	static
C:\toolchain\Arduino\arduino-1.8.19\hardware\teensy\avr\cores\teensy4/usb_serial.h:115:24:virtual size_t usb_serial_class::write(uint8_t)	0	static
C:\toolchain\Arduino\arduino-1.8.19\hardware\teensy\avr\cores\teensy4/Print.cpp:128:8:0(long unsigned int, uint8_t, uint8_t)	56	static
C:\toolchain\Arduino\arduino-1.8.19\hardware\teensy\avr\cores\teensy4/Print.cpp:42:8:virtual size_t Print::write(const uint8_t*, size_t)	24	static
C:\toolchain\Arduino\arduino-1.8.19\hardware\teensy\avr\cores\teensy4/Print.cpp:51:8:size_t Print::print(const String&)	64	static
C:\toolchain\Arduino\arduino-1.8.19\hardware\teensy\avr\cores\teensy4/Print.cpp:68:8:size_t Print::print(long int)	0	static
C:\toolchain\Arduino\arduino-1.8.19\hardware\teensy\avr\cores\teensy4/Print.cpp:85:8:size_t Print::println()	16	static
C:\toolchain\Arduino\arduino-1.8.19\hardware\teensy\avr\cores\teensy4/Print.cpp:93:5:int _write(int, char*, int)	4	static
C:\toolchain\Arduino\arduino-1.8.19\hardware\teensy\avr\cores\teensy4/Print.cpp:100:5:)	12	static
C:\toolchain\Arduino\arduino-1.8.19\hardware\teensy\avr\cores\teensy4/Print.cpp:114:5:)	12	static
C:\toolchain\Arduino\arduino-1.8.19\hardware\teensy\avr\cores\teensy4/Print.cpp:128:8:size_t Print::printNumber(long unsigned int, uint8_t, uint8_t)	4	static
C:\toolchain\Arduino\arduino-1.8.19\hardware\teensy\avr\cores\teensy4/Print.cpp:163:8:size_t Print::printNumber64(uint64_t, uint8_t, uint8_t)	104	static
C:\toolchain\Arduino\arduino-1.8.19\hardware\teensy\avr\cores\teensy4/Print.cpp:79:8:size_t Print::print(int64_t)	16	static
C:\toolchain\Arduino\arduino-1.8.19\hardware\teensy\avr\cores\teensy4/Print.cpp:189:8:size_t Print::printFloat(double, uint8_t)	48	static

It should be easy to do a quick script walking through all the *.su files and list them sorted by used space. This should give you a hint where all your stack space goes to. Of course this simple method doesn't take the call tree into account. If you want to do a more thorough analysis you can use -fcallgraph-info which generates *.ci files containing information about the callgraph.

Here some information about the method: https://embeddedartistry.com/blog/2...fstack-usage flag is,stack sizes for a system. Also see the paper linked therein

-Wstack-usage which generates warnings for stack frames larger than a given number is also worth looking at (see article linked above).
Using -Wstack-usage=100 only generates one warning for the T4 core which is for the 104 byte stackframe of Print:: printNumber64 (also see listing above)
 
Last edited:
In a more readable form:
Code:
Print.h:62:14:virtual 		int Print::availableForWrite()					0	static
Print.h:63:15:virtual 		void Print::flush()						0	static
usb_serial.h:115:24:virtual 	size_t usb_serial_class::write(uint8_t)				0	static
Print.cpp:128:8:0		(long unsigned int, uint8_t, uint8_t)				56	static
Print.cpp:42:8:virtual 		size_t Print::write(const uint8_t*, size_t)			24	static
Print.cpp:51:8:			size_t Print::print(const String&)				64	static
Print.cpp:68:8:			size_t Print::print(long int)					0	static
Print.cpp:85:8:			size_t Print::println()						16	static
Print.cpp:93:5:			int _write(int, char*, int)					4	static
Print.cpp:100:5:)										12	static
Print.cpp:114:5:)										12	static
Print.cpp:128:8:		size_t Print::printNumber(long unsigned int, uint8_t, uint8_t)	4	static
Print.cpp:163:8:		size_t Print::printNumber64(uint64_t, uint8_t, uint8_t)		104	static
Print.cpp:79:8:			size_t Print::print(int64_t)					16	static
Print.cpp:189:8:		size_t Print::printFloat(double, uint8_t)			48	static
 
Linux only. If you're feeling brave, add -fstack-usage and rebuild everything. Then execute this fugly oneliner
Code:
$ find . -name "*.su" -print0 | sort -t$'\t' -n -r -k2,2 --files0-from - | less
to find the low hanging fruit at the top of the list.
 
Last edited:
Got freertos installed and compiled it with -fstack-usage on 1.59-beta2 + all recent changes (I probably should package up 1.59-beta3 soon...)

Nothing seems to use more than 1K. Here's all the function -fstack-usage finds using over 200 bytes. Edited slightly for readability.

Code:
parse.c:554:18:ParseStatement        336     static
lex.c:726:15:LexGetToken             328     static
commands.c:267:6:LibOpenScript       264     static

QNEthernet/src/lwip/apps/mdns/mdns.c:1562:1:mdns_handle_question             912     static
QNEthernet/src/lwip/apps/mdns/mdns.c:1783:1:mdns_handle_response             576     static
QNEthernet/src/lwip/apps/mdns/mdns.c:1238:1:mdns_add_srv_answer              568     static
QNEthernet/src/lwip/apps/mdns/mdns.c:1181:1:mdns_add_hostv4_ptr_answer       568     static
QNEthernet/src/lwip/apps/mdns/mdns.c:1227:1:mdns_add_servicename_ptr_answer  552     static
QNEthernet/src/lwip/apps/mdns/mdns.c:1216:1:mdns_add_servicetype_ptr_answer  552     static
QNEthernet/src/lwip/apps/mdns/mdns.c:1948:1:mdns_send_probe.constprop        360     static
QNEthernet/src/lwip/apps/mdns/mdns.c:788:1:mdns_compress_domain.part.0       320     static
QNEthernet/src/lwip/apps/mdns/mdns.c:1171:1:mdns_add_a_answer                304     static
QNEthernet/src/lwip/apps/mdns/mdns.c:1261:1:mdns_add_txt_answer              296     static
QNEthernet/src/lwip/apps/mdns/mdns.c:669:1:check_host.part.0                 288     static
QNEthernet/src/lwip/apps/mdns/mdns.c:736:1:check_service.part.0              280     static

SdFat/src/FsLib/FsVolume.cpp:176:6:bool FsVolume::setVolumeLabel(const char*)                            608     static
SdFat/src/FsLib/FsVolume.cpp:95:6:bool FsVolume::getVolumeLabel(char*, size_t)                           584     static
SdFat/src/FatLib/FatDbg.cpp:184:6:bool FatPartition::dmpDirSector(print_t*, uint32_t)                    552     static
SdFat/src/SdCard/SdioTeensy.cpp:999:6:virtual bool SdioCard::writeSector(uint32_t, const uint8_t*)       536     static
SdFat/src/SdCard/SdioTeensy.cpp:846:6:virtual bool SdioCard::readSector(uint32_t, uint8_t*)              536     static
SdFat/src/FatLib/FatDbg.cpp:211:6:void FatPartition::dmpSector(print_t*, uint32_t, uint8_t)              536     static
SdFat/src/SpiDriver/SdSpiTeensy3.cpp:77:6:void SdSpiArduinoDriver::send(const uint8_t*, size_t)          528     static

cores/teensy4/eeprom.c:108:6:eeprom_write_byte     296     static

Doesn't seem to be anything recursive. Very mysterious why this (large 18523 lines) program would need over 80K stack.
 
Sorry, my eyes glaze over, with some of this stuff :D

Have not used these tools/options:
don't know if they catch things like:
Code:
void my_function(int buffer_size) {
    uint8_t my_buffer[buffer_size)
...

Or if you have a recursive function, where you have larger local variables...

Another approach we have used in past. Is code to try to see how much memory we are using.
By filling up the area between starting stack and end of area with known value, and then have calls to look at the end of memory back down until it finds something different, to give you an estimate of how far something has gone...

There is an example of it in the st7735_t3 library the uncannyEyes_async_st7789_240x240 example... There are others and threads about doing this. Note: I have not tried this out in a long time...
 
In those cases -fstack-usage will mark the entry by "dynamic" (see above linked article)

Code:
void my_function(int buffer_size)
{
   uint8_t my_buffer[buffer_size];
   Serial.println(my_buffer[0]);  // prevent optimizing my_buffer away
}

void setup(){
  my_function(100);
}

void loop(){
}
Generates:
Code:
src/main.cpp:3:6:void my_function(int)	16	dynamic
src/main.cpp:10:6:void setup()          120	dynamic,bounded
src/main.cpp:14:6:void loop() 	         0	static

It can't give you info about recursive functions. As mentioned you need to analyze the call stack and combine it with the stack frame info if you are interested in these.

More interesting is the compiler output with -Wstack-usage=100:

Code:
src/main.cpp:3:6: warning: stack usage might be unbounded [-Wstack-usage=]
    3 | void my_function(int buffer_size)
      |      ^~~~~~~~~~~
src/main.cpp: In function 'void setup()':
src/main.cpp:10:6: warning: stack usage might be 120 bytes [-Wstack-usage=]
   10 | void setup(){
      |      ^~~~~

Warning you about a potentially unbound stack frame of my_function and telling you that setups stack frame is 120 bytes (larger than the warning level 100)
 
Last edited:
Status
Not open for further replies.
Back
Top