Argh. This week I created some pretty simple parser + ISR. Of course the first implementation failed. Then I added debug print statements. Sure enough this made things worse. Seems the ISR was causing it. However removing the ISR did not resolve anything. Then I thought maybe the debug prints are a new issue and replaced everything with progmem / print(F("...")). Again uncontrolled crashes.
After two days I finally got it. Nothing at all crashed. However: even outside of ISRs cli() before Serial.print will never succeed. "Obviously" serial requires interrupts to work. So basically my debug code was causing all the issues.
One more lesson learned: combining Serial with cli() is a bad idea.
The only thing worse than debugging code making something that used to work, stop working, is when you add debugging code to solve a problem, and the presence of the debugging code makes the problem go away.
That has happened more times than I can remember to other people when I was a manager. Never happened to me though.
Every time I should have said, "volatile."
It can be other things. For example in Visual C++ (from memory) if you do a "debug build" static (or auto maybe) variables get initialized in a different way than a "release build". For example, to put something like 0xBADBAD into pointers, so you don't accidentally use them without initializing them. But this very difference can make the code behave differently.