Stack Overflow in Setup()

I have a pretty large number of objects being initialized in the setup() function and it seems like I’ve hit some type of limit that I’m not sure how to approach.
I’m observing the crash in ardens as well as Felipe’s emulator.

I have plenty of mem available:

arduino-cli compile --fqbn "arduboy-homemade:avr:arduboy" --optimize-for-debug  --output-dir .
Sketch uses 20680 bytes (72%) of program storage space. Maximum is 28672 bytes.
Global variables use 2292 bytes (89%) of dynamic memory, leaving 268 bytes for local variables. Maximum is 2560 bytes.

Ardens stack trace:
Capture
Breakpoint asm:
Capture

I have a branch where the latest commit causes the runtime error with the binaries committed (can build from scratch on linux using ./setup.sh to install everything assuming have the arduino-cli, then just make)

Simply adding: uint8_t cursor; as a private member to one of my classes causes this issue.
This is a bit odd since the stack trace is showing the error occurs during what I believe is a poll to the timer.

Another thing I noticed is playing arround with pulling out writes to some of my member vars in the functions called in setup seem to get passed the error. Like puling out:
world = WorldEngine(&arduboy, &state, &engine, &menu2);
from the setup() function solves this runtime issue for example. Digging in there a little bit I have a feeling this is a red herring error being caused by me corrupting memory or something of the like. Any tips on how to approach debugging this would be appreciated.

EDIT:
I’m digging around in ardens some more an I noticed that the stack under the CPU dataspace shrinks as I add more variables, to the point of it rolling over and overflowing, so It looks like this is a real overflow issue. That leaves me wondering why this is occurring though. I’ve also noticed that the stack usage doesn’t change at all during gameplay. It looks like I need to reduce the global vars I’m using to free-up some space.

Without looking too deeply into it, I’m willing to bet the issue is that you’re using up too much RAM and there’s too little left over for the stack.

Adding a member variable to a class will increase the size of all instances of that class. If some of those instances are global variables or local variables that are ending up on the stack then that’s an action that consumes more RAM.

Removing all writes to a local variable will cause the compiler to remove that variable entirely, and if that variable was being stored on the stack then that’s an action that will consume less RAM by causing the stack to shrink.

I would presume that’s because the stack is allocated a fixed amount of space into which it is allowed to grow and what you’re seeing is that allocated space rather than what the stack is actually using.

Bear in mind that if there’s no recursive functions in the code (or at least none that aren’t tail-recursive) then the maximum stack size can be known at compile time.


If you’re looking for savings, I’d suggest trying to move the player’s ‘stored’ creatures onto the FX chip in a ‘save’ area.

You may also want to consider looking for ways to squash values down into bit fields if you don’t need a full 8 bits for them.
E.g. if you had three values that could only go up to 32 (5 bits), you could squash three of them into a single uint16_t rather than using one uint8_t for each of the three.

You may also want to try storing fewer pointers in favour of passing a reference to the relevant function as and when it’s needed.

E.g. instead of having an Arduboy2 * member in WorldEngine, instead just have an Arduboy2 & as a parameter to whatever function needs access to an Arduboy2 object, e.g. void drawMap(Arduboy2 &).


Some things that may or may not help (but would be a good idea to do anyway for the sake of best practice):

It may not make a difference, but I’d also advise using a member initialiser list instead of using the body of a constructor to initialise the class members. E.g. instead of X(int x) { this->x = x; } do X(int x) : x { x } {}.

And if your default constructor is empty, don’t explicitly add a constructor, instead mark it as defaulted. E.g. WorldEngine() = default;. Again, I’m not 100% sure that this will make a difference to code size, but it may do.


This will help with progmem rather than RAM, but instead of:

Do:

if(this->cursorIndex < 4)
	this->queueAction(ActionType::ATTACK, this->cursorIndex);

That’ll get you a single function call in a single branch instead of having four function calls in four branch targets.

A similar technique can be used elsewhere. E.g. instead of:

You could have:

namespace TypeNames
{
	const char spirit[] PROGMEM = "SPIRIT";
	const char water[] PROGMEM = "WATER";
	// ...
}

const char * typeNames[] PROGMEM
{
	TypeNames::spirit,
	TypeNames::water,
	// ...
};

const char * getTypeName(Type type)
{
	const uint8_t index { static_cast<uint8_t>(type) };
	const auto pointer { pgm_read_pointer(&typeNames[index]) };

	return static_cast<const char *>(pointer);
}

const __FlashStringHelper * getPrintableTypeName(Type type)
{
	return reinterpret_cast<const __FlashStringHelper *>(getTypeName(type));
}

void printType(Type type, Arduboy2 & arduboy)
{
	arduboy.print(getPrintableTypeName(type));
}

Which, despite using more source code should actually use less progmem because the amount saved by having fewer calls to print should be greater than the space consumed by the array.

I’ve been meaning to rework all the pointers to trim them out. I did some cleanup and got back 119 bytes so that’s enough room to work with at least. There’s a lot of half finished things or things that are scheduled for deletion so guess I need to move that up in priority.

It’s a little weird to me since I’ve remember seeing games that use almost all the available dynamic mem, wondering how the stack fit for those titles.

Also I completely forgot about initializer lists lol. I write too much go for work

The call stack shows heavy RAM usage – the bracketed numbers are the size of the stack frame for each call. If you tally up each stack frame, you’ll find the total size is 268 bytes, which is precisely how much stack space you have available according to the build output, so at this exact point you’ve used up the entire stack.

Reserving 174 bytes on the stack during setup seems excessive. I haven’t looked into it in detail but I would suspect that lines like this are the culprit:

world = WorldEngine(&arduboy, &state, &engine, &menu2);

This constructs a temporary WorldEngine object on the stack before copying it to world. One way to avoid this is to use an initialization method:

world.init(&arduboy, &state, &engine, &menu2);
1 Like

Thanks this is great info to know going forward

Assuming you aren’t misremembering, the most likely answer is that they weren’t calling many functions or weren’t creating large local variables.

I’ve yet to understand why the compiler can’t or won’t optimise this into just destroying world and then reconstructing it. Fortunately that’s something that can be done manually…

Another option that doesn’t require having to redo the constructor:

// Destroy the world
world.~WorldEngine();
// Reconstruct it in-place
new (&world) WorldEngine(&arduboy, &state, &engine, &menu2);

Odds are the explicit destructor call will be optimised away, but better safe than sorry.

(Though I’m wondering why this is happening in setup rather than at the point world is actually defined…)

If this is something that’s going to need to happen a lot then it’s possible to write a template function that will do this for any type.

1 Like

Programming humor

1 Like

I thought new was a no-no on this platform, along with any dynamic allocation?

This is ‘placement new’. No dynamic allocation involved.
It initialises an object directly into the designated storage area (which must have the correct size and alignment).

E.g.

// A buffer with the correct size and alignment
alignas(T) unsigned char buffer[sizeof(T)];

// Construct the new object directly into the buffer
T * pointer = new (&buffer[0]) T();

// Destroy the object (pointer points to buffer)
pointer->~T();

(Incidentally, Arduino didn’t always support placement new. It’s partly my fault that it got added.)

2 Likes

I have a question about this, on the overflow the two matched up, but watching the game rung the stack use doesn’t seem to change, or match up
Capture

The stack use (“249/376” in the above) represents the maximum stack usage at any point in execution history, not the current stack usage (this is usually what you’d care more about because it tells you how many more globals you might be able to fit before you run into trouble). I can see that this is confusing from the way it’s indicated – I’ll make an adjustment to that.

1 Like