12

The cortex M3 processor startup file allows you to specify the amount of RAM dedicated to the stack and the heap. For a c++ code base, is there a general rule of thumb or perhaps some more explicit way to determine the values for the stack and heap sizes? For example, would you count the number and size of unique objects, or maybe use the compiled code size?

artless noise
  • 21,212
  • 6
  • 68
  • 105
rmaVT
  • 125
  • 1
  • 1
  • 8
  • This is more compiler-and-runtime-library specific than related to a particular CPU architecture. But most of all, it's your code that determines memory usage. Use either the MMU or a data breakpoint to catch stack overflows. – Ben Voigt Mar 12 '11 at 00:10
  • possible duplicate of [How to determine maximum stack usage?](http://stackoverflow.com/questions/389219/how-to-determine-maximum-stack-usage) – Michael Burr Mar 12 '11 at 10:53
  • @Ben: Cortex-M3 has no MMU, but it does have data access hardware breakpoint support which may help during testing, but not for deployment. – Clifford Mar 12 '11 at 21:54
  • 1
    @Michael: Given the toolchain-specific components of Clifford's answer, I don't think this is functionally a duplicate. A useful pointer nonetheless, though. – Brooks Moses Mar 14 '11 at 22:06

4 Answers4

16

The cortex M3 processor startup file allows you to specify the amount of RAM dedicated to the stack and the heap.

That is not a feature of the Cortex-M3, but rather the start-up code provided by your development toolchain. It is the way the Keil ARM-MDK default start-up files for M3 work. It is slightly unusual; more commonly you would specify a stack size, and any remaining memory after stack and static memory allocation by the linker becomes the heap; this is arguably better since you do not end up with a pool of unusable memory. You could modify that and use an alternative scheme, but you'd need to know what you are doing.

If you are using Keil ARM-MDK, the linker options --info=stack and --callgraph add information to the map file that aids stack requirement analysis. These and other techniques are described here.

If you are using an RTOS or multi-tasking kernel, each task will have its own stack. The OS may provide stack analysis tools, Keil's RTX kernel viewer shows current stack usage but not peak stack usage (so is mostly useless, and it only works correctly for tasks with default stack lengths).

If you have to implement stack checking tools yourself, the normal method is to fill the stack with a known value, and starting from the high address, inspect the value until you find the first value that is not the fill byte, this will give the likley high tide mark of the stack. You can implement code to do this, or you can manually fill the memory from the debugger, and then monitor stack usage in a debugger memory window.

Heap requirement will depend on the run-time behaviour of your code; you'll have to analyse that yourself however in ARM/Keil Realview, the MemManage Exception handler will be called when C++'s new throws an exception; I am not sure if malloc() does that or simply returns NULL. You can place a breakpoint in the exception handler or modify the handler to emit an error message to detect heap exhaustion during testing. There is also a a __heapstats() function that can be used to output heap information. It has a somewhat cumbersome interface, I wrapped it thus:

void heapinfo()
{
typedef int (*__heapprt)(void *, char const *, ...);
    __heapstats( (__heapprt)std::fprintf, stdout ) ;
}
Clifford
  • 88,407
  • 13
  • 85
  • 165
3

The compiled code size will not help as the code does not run in the stack nor the heap. Cortex-M3 devices are typically implemented on microcontrollers with built in Flash and a relatively small amount of RAM. In this configuration, the code will typically run from Flash.

The heap is used for dynamic memory allocation. Counting the number of unique objects will give you a rough estimate but you also have to account for any other elements that use dynamic memory allocation (using the new keyword in C++). Generally, dynamic memory allocation is avoided in embedded systems for the precise reason that heap size is hard to manage.

The stack will be used for variable passing, local variables, and context saving during exception handling routines. It is generally hard to get a good idea of stack usage unless you're code allocates a large block of local memory or a large objects. One technique that may help is to allocate all of the available RAM you have for the stack. Fill the stack with a known pattern (0x00 or 0xff are not the best choices since these values occur frequently), run the system for a while then examine the stack to see how much was used. Admittedly, this not a very precise nor scientific approach but still helpful in many cases.

semaj
  • 1,555
  • 1
  • 12
  • 25
1

Reducing until it crashes is a quick ad-hoc way. You can also fill the stack with a known value, say, 0xCCCC, and then monitor maximum stack usage by scanning for the 0xCCCC. It's imperfect, but much better than looking for a crash.

The rationale being, reducing stack size does not guarantee that stack overflow will munch something "visible".

Community
  • 1
  • 1
charles
  • 11
  • 1
1

The latest version of the IAR Compiler has a feature that will determine what stack size you need, based on a static analysis of your code (assuming you don't have any recursion).

The general approach, if you don't have an exact number, is to make as big as you can, and then when you start running out of memory, start trimming the stack down until your program crashes due to a stack over flow. I wish that was a joke, but that is the way it is usually done.

Mark Lakata
  • 19,989
  • 5
  • 106
  • 123