Skip to content

ArduinoStackCrash #7824

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
1 task done
mrengineer7777 opened this issue Feb 10, 2023 · 10 comments
Closed
1 task done

ArduinoStackCrash #7824

mrengineer7777 opened this issue Feb 10, 2023 · 10 comments
Assignees
Labels
Type: For reference Common questions & problems Type: Question Only question

Comments

@mrengineer7777
Copy link
Collaborator

Related area

FreeRTOS

Hardware specification

NA

Is your feature request related to a problem?

I didn't understand what a task's stack size did or how uxTaskGetStackHighWaterMark(NULL) worked.

Describe the solution you'd like

I propose added a new example to https://github.com/espressif/arduino-esp32/tree/master/libraries/ESP32/examples/ArduinoStackSize called "ArduinoStackCrash.ino", or making a separate folder for it.

I have a working example in VSCode+PIO. Happy to submit a PR, but I need some help because I don't use Arduino IDE.

Describe alternatives you've considered

No response

Additional context

No response

I have checked existing list of Feature requests and the Contribution Guide

  • I confirm I have checked existing list of Feature requests and Contribution Guide.
@mrengineer7777 mrengineer7777 added the Type: Feature request Feature request for Arduino ESP32 label Feb 10, 2023
@mrengineer7777
Copy link
Collaborator Author

#include <Arduino.h>

/*
  This example demonstrates how a Task's stack memory decreases as functions call each other.
  Allocating local variables and malloc() seem to have no effect on the task's stack memory.
  When the stack memory runs out, the ESP32 will crash but also print a backtrace.
  This backtrace can be decoded with "esp32_exception_decoder".

  Example output:

19:49:34.091 > [     3][D][esp32-hal-cpu.c:244] setCpuFrequencyMhz(): PLL: 480 / 2 = 240 Mhz, APB: 80000000 Hz
19:49:38.812 > [  5031][I][main.cpp:27] Task1(): Stack 1744
19:49:38.813 > [  5031][I][main.cpp:35] f1(): i=1, Stack 1152 (0)
19:49:39.051 > [  5281][I][main.cpp:43] f2(): i=2, Stack 1152 (0)
19:49:39.292 > [  5531][I][main.cpp:35] f1(): i=3, Stack 1152 (0)
19:49:39.547 > [  5781][I][main.cpp:43] f2(): i=4, Stack 1120 (-32)
19:49:39.803 > [  6031][I][main.cpp:35] f1(): i=5, Stack 1056 (-64)
19:49:40.043 > [  6281][I][main.cpp:43] f2(): i=6, Stack 992 (-64)
19:49:40.299 > [  6531][I][main.cpp:35] f1(): i=7, Stack 928 (-64)
19:49:40.554 > [  6781][I][main.cpp:43] f2(): i=8, Stack 864 (-64)
19:49:40.794 > [  7031][I][main.cpp:35] f1(): i=9, Stack 800 (-64)
19:49:41.050 > [  7281][I][main.cpp:43] f2(): i=10, Stack 736 (-64)
19:49:41.306 > [  7531][I][main.cpp:35] f1(): i=11, Stack 672 (-64)
19:49:41.546 > [  7781][I][main.cpp:43] f2(): i=12, Stack 608 (-64)
19:49:41.802 > [  8031][I][main.cpp:35] f1(): i=13, Stack 544 (-64)
19:49:42.042 > [  8281][I][main.cpp:43] f2(): i=14, Stack 480 (-64)
19:49:42.298 > [  8531][I][main.cpp:35] f1(): i=15, Stack 416 (-64)
19:49:42.553 > [  8781][I][main.cpp:43] f2(): i=16, Stack 352 (-64)
19:49:42.793 > [  9031][I][main.cpp:35] f1(): i=17, Stack 288 (-64)
19:49:43.049 > [  9281][I][main.cpp:43] f2(): i=18, Stack 224 (-64)
19:49:43.305 > [  9531][I][main.cpp:35] f1(): i=19, Stack 160 (-64)
19:49:43.545 > Guru Meditation Error: Core  0 panic'ed (Unhandled debug exception). 
19:49:43.701 > Debug exception reason: Stack canary watchpoint triggered (Task1)
19:49:43.701 > Core  0 register dump:
19:49:43.701 > PC      : 0x400dbeec  PS      : 0x00060e36  A0      : 0x800dc26d  A1      : 0x3ffb90d0
=> 0x400dbeec: _printf_common at /builds/idf/crosstool-NG/.build/HOST-x86_64-w64-mingw32/xtensa-esp32-elf/src/newlib/newlib/libc/stdio/nano-vfprintf_i.c:81 (discriminator 5)
19:49:43.701 > A2      : 0x3ffb2918  A3      : 0x3ffb9140  A4      : 0x3ffb9100  A5      : 0x3ffb91f0
19:49:43.701 > A6      : 0x400df804  A7      : 0x00000000  A8      : 0x00000002  A9      : 0x00000004
=> 0x400df804: __ssputs_r at /builds/idf/crosstool-NG/.build/HOST-x86_64-w64-mingw32/xtensa-esp32-elf/src/newlib/newlib/libc/stdio/nano-vfprintf.c:179
19:49:43.701 > A10     : 0x3ffb2918  A11     : 0x3ffb91f0  A12     : 0x3ffb9159  A13     : 0x00000001
19:49:43.701 > A14     : 0x3ffb9159  A15     : 0x00000012  SAR     : 0x0000000a  EXCCAUSE: 0x00000001
19:49:43.701 > EXCVADDR: 0x00000000  LBEG    : 0x400842d9  LEND    : 0x400842e1  LCOUNT  : 0x00000027
=> 0x400842d9: esp_timer_impl_get_counter_reg at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_timer/src/esp_timer_impl_lac.c:118 (discriminator 2)
=> 0x400842e1: esp_timer_impl_get_counter_reg at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_timer/src/esp_timer_impl_lac.c:128 (discriminator 1)
19:49:43.701 >
19:49:43.701 >
19:49:43.701 > Backtrace: 0x400dbee9:0x3ffb90d0 0x400dc26a:0x3ffb9100 0x400dfc41:0x3ffb9140 0x400dd9a6:0x3ffb91f0 0x400dd9de:0x3ffb9280 0x400d1a76:0x3ffb92c0 0x400d0c1b:0x3ffb9320 0x400d0c8a:0x3ffb9360 0x400d0c26:0x3ffb93a0 0x400d0c8a:0x3ffb93e0 0x400d0c26:0x3ffb9420 0x400d0c8a:0x3ffb9460 0x400d0c26:0x3ffb94a0 0x400d0c8a:0x3ffb94e0 0x400d0c26:0x3ffb9520 0x400d0c8a:0x3ffb9560 0x400d0c26:0x3ffb95a0 0x400d0c8a:0x3ffb95e0 0x400d0c26:0x3ffb9620 0x400d0c8a:0x3ffb9660 0x400d0c26:0x3ffb96a0 0x400d0c8a:0x3ffb96e0 0x400d0c26:0x3ffb9720 0x400d0c8a:0x3ffb9760 0x400d0c26:0x3ffb97a0 0x400d0c8a:0x3ffb97e0 0x400d0cd9:0x3ffb9820
=> 0x400dbee9: _printf_common at /builds/idf/crosstool-NG/.build/HOST-x86_64-w64-mingw32/xtensa-esp32-elf/src/newlib/newlib/libc/stdio/nano-vfprintf_i.c:81 (discriminator 5)
=> 0x400dc26a: _printf_i at /builds/idf/crosstool-NG/.build/HOST-x86_64-w64-mingw32/xtensa-esp32-elf/src/newlib/newlib/libc/stdio/nano-vfprintf_i.c:237
=> 0x400dfc41: _svfprintf_r at /builds/idf/crosstool-NG/.build/HOST-x86_64-w64-mingw32/xtensa-esp32-elf/src/newlib/newlib/libc/stdio/nano-vfprintf.c:636
=> 0x400dd9a6: _vsnprintf_r at /builds/idf/crosstool-NG/.build/HOST-x86_64-w64-mingw32/xtensa-esp32-elf/src/newlib/newlib/libc/stdio/vsnprintf.c:70 (discriminator 4)
=> 0x400dd9de: vsnprintf at /builds/idf/crosstool-NG/.build/HOST-x86_64-w64-mingw32/xtensa-esp32-elf/src/newlib/newlib/libc/stdio/vsnprintf.c:40
=> 0x400d1a76: log_printf at C:/Users/UserName/.platformio/packages/framework-arduinoespressif32/cores/esp32/esp32-hal-uart.c:470
=> 0x400d0c1b: f2(int) at src/main.cpp:43
=> 0x400d0c8a: f1(int) at src/main.cpp:37
=> 0x400d0c26: f2(int) at src/main.cpp:45
=> 0x400d0c8a: f1(int) at src/main.cpp:37
=> 0x400d0c26: f2(int) at src/main.cpp:45
=> 0x400d0c8a: f1(int) at src/main.cpp:37
=> 0x400d0c26: f2(int) at src/main.cpp:45
=> 0x400d0c8a: f1(int) at src/main.cpp:37
=> 0x400d0c26: f2(int) at src/main.cpp:45
=> 0x400d0c8a: f1(int) at src/main.cpp:37
=> 0x400d0c26: f2(int) at src/main.cpp:45
=> 0x400d0c8a: f1(int) at src/main.cpp:37
=> 0x400d0c26: f2(int) at src/main.cpp:45
=> 0x400d0c8a: f1(int) at src/main.cpp:37
=> 0x400d0c26: f2(int) at src/main.cpp:45
=> 0x400d0c8a: f1(int) at src/main.cpp:37
=> 0x400d0c26: f2(int) at src/main.cpp:45
=> 0x400d0c8a: f1(int) at src/main.cpp:37
=> 0x400d0c26: f2(int) at src/main.cpp:45
=> 0x400d0c8a: f1(int) at src/main.cpp:37
=> 0x400d0cd9: Task1(void*) at src/main.cpp:29
19:49:43.701 >
19:49:43.701 >
19:49:43.701 >
19:49:43.701 >
19:49:43.701 > ELF file SHA256: 0000000000000000
19:49:43.701 >
19:49:43.701 > Rebooting...
*/

static TaskHandle_t _th1 = NULL;
unsigned int F1_Mark, F2_Mark;

//---Prototypes---
void f1(int i);
void f2(int i);
static void Task1(void*);
//---Prototypes---

void setup() {
  Serial.begin(115200);
  delay(5000); //Wait for USB to connect

  xTaskCreate(
    Task1,    //Function to call
    "Task1",  //Name displayed on crash
    2048,     //Task stack size
    NULL,     //Optional parameters
    1,        //Priority (low)
    &_th1);   //Task handle

  if(!_th1) {
    log_e("Failed to create task");
  }
}

void loop() {
  delay(1000);
}

static void Task1(void*) {
  log_i("Stack %d", uxTaskGetStackHighWaterMark(NULL));//Note log_i() increases the high water mark. printf() is worse.
  F2_Mark = uxTaskGetStackHighWaterMark(NULL);
  f1(0); //Call f1()/f2() recursively until stack crashes
}

void f1(int i) {
  i++;
  F1_Mark = uxTaskGetStackHighWaterMark(NULL);
  log_i("i=%i, Stack %d (%i)", i, F1_Mark, F1_Mark-F2_Mark);
  delay(250);
  f2(i);
}

void f2(int i) {
  i++;
  F2_Mark = uxTaskGetStackHighWaterMark(NULL);
  log_i("i=%i, Stack %u (%i)", i, F2_Mark, F2_Mark-F1_Mark);
  delay(250);
  f1(i);
}

@mrengineer7777
Copy link
Collaborator Author

platformio.ini:

[env:featheresp32]
platform = espressif32
board = featheresp32
framework = arduino
monitor_speed = 115200
check_skip_packages = yes
monitor_filters = time, colorize, esp32_exception_decoder
build_flags = -DCORE_DEBUG_LEVEL=5

@SuGlider SuGlider self-assigned this Feb 10, 2023
@SuGlider SuGlider added Type: Question Only question and removed Type: Feature request Feature request for Arduino ESP32 labels Feb 10, 2023
@SuGlider
Copy link
Collaborator

Hi @mrengineer7777

// This sets Arduino Stack Size - comment this line to use default 8K stack size
SET_LOOP_TASK_STACK_SIZE(16*1024); // 16KB

This macro will change a weak link to a function that returns the size of the stack used when starting the Arduino Task.
Therefore, it tells Arduino what should be the Stack Size of the main Arduino Task (setup/loop), in compilation time.
The default Arduino stack size is 8K, as per your example, it will also be 8K because it has not been changed by this macro.

Try runing your example with different values in the SET_LOOP_TASK_STACK_SIZE() macro.

Arduino Core source code references:
https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/Arduino.h#L191-L192
https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/main.cpp#L12-L18
https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/main.cpp#L36-L38
https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/main.cpp#L71

@mrengineer7777
Copy link
Collaborator Author

@SuGlider Yes, I saw the #define in https://github.com/espressif/arduino-esp32/blob/master/libraries/ESP32/examples/ArduinoStackSize/ArduinoStackSize.ino. I tried it and it works. It's clever.

My example is not about the Arduino loop. Perhaps the Arduino folder is not the right place for it. I was thinking Arduino because xTaskCreate() is the Arduino way of handling threads.

The example is about understanding how a Task's "stack space" is used. It specifically starts a separate task with 2048 [bytes? (documentation says words)] of stack space, then intentionally causes a crash using recursive function calls. Originally I was calling the functions from loop(), but I wanted to see how it behaved within a user defined task.

I find it interesting that every function call appears to consume 64 bytes of stack space. Given that, a stack space of 2048 should allow a max function call depth of 32.

@SuGlider
Copy link
Collaborator

I see, thank for explaining.

Is it just an experiment?
So, there is no issue, right?

@mrengineer7777
Copy link
Collaborator Author

mrengineer7777 commented Feb 10, 2023

Correct. Not an issue. It would be helpful for fellow developers to have an example that demonstrates how a Stack Task is used. I spent about 6 hours figuring that out by writing test code.

@SuGlider SuGlider added the Type: For reference Common questions & problems label Feb 10, 2023
@SuGlider
Copy link
Collaborator

OK. You may also open a discussion and/or write here your findings.
I'll mark this issue as "For Reference".

@mrengineer7777
Copy link
Collaborator Author

This is helpful: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/freertos.html#task-api.

  • Task Stack Depth parameter is in bytes
  • "If program uses thread local variables (ones specified with “__thread” keyword) then storage for them will be allocated on the task’s stack."

@mrengineer7777
Copy link
Collaborator Author

I've decided adding example code for this is overkill, so closing this issue.

@lonerzzz
Copy link
Contributor

lonerzzz commented May 13, 2023

For those who arrive here and wish to avoid playing around with where to call the SET_LOOP_TASK_STACK_SIZE, it should be called as follows:

SET_LOOP_TASK_STACK_SIZE( 16*1024 ); // 16KB

setup()
{
	// setup code here
}

loop()
{
	// loop code here:
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: For reference Common questions & problems Type: Question Only question
Projects
None yet
Development

No branches or pull requests

3 participants