Too long, didn't read it all.
An old technique is called "stack paining", and works (in principle) quit simple.
During initialization of your uC you fill all memory with either a static values, or the output from a quasi random generator.
And then every once in a while you have an ISR (so you have all memory to your own) and in that ISR you walk through all RAM and examine it's current content, and you can get a quite good (but not 100% reliable) result of what memory locations have been used, and you can determine the distance between the top of the heap and the bottom of the stack. Once you have a stack collision, there's a good chance you will not reach your ISR anymore, and to guard against that some "safety margin" should be respected. If stack and heap get too close, you're in trouble.
But as others have already written. uC programs tend to be small, and often constructs as malloc are not even used at all, and no heap fragmentation can occur.
With small uC's often some static buffers are declared. If your total RAM need is bigger then physical RAM, then you can declare an array of Unions, and re-use the same RAM for different buffers (but obviously not at the same time).
Memory corruption in small uC's is often caused by beginner errors, such as static text strings not being declared as static, and then the compiler copying them from Flash to RAM during initialisation. Such text strings can fill up RAM quickly in a small uC.
If you have detected memory corruption in your ISR, you can't rely on the uC still working properly. So don't return from the ISR, and don't go calling any functions (which use the stack). Some static code (stored in Flash of course) to first re-initialize a UART by writing to it's registers directly (it's registers can be corrupted, so do not rely in it being initialized) and then pushing out some bytes or a text string is still **likely** to work.