20

I would like to know how much RAM I am using in my project, as far as I can tell, there's no way to actually work that out (other than going through and calculating it myself). I have got to a stage in a rather large project where I have determined that I am running out of RAM.

I have determined this because I can add a section and then all hell breaks loose somewhere else in my code for no apparent reason. If I #ifndef something else out, it works again. There is nothing programatically wrong with the new code.

I suspected for a while that I was getting to the end of available RAM. I don't think I'm using too much stack (although it's possible), what is the best way to determine how much RAM I am actually using?

Going through and trying to work it out, I have problems when I get to enums and structs; how much memory do they cost?

first edit: ALSO, I have edited my sketch so much since starting, these are not the actual results I initially got, but they are what I am getting now.

  text     data     bss     dec     hex filename
 17554      844     449   18847    499f HA15_20140317w.cpp.elf
 16316      694     409   17419    440b HA15_20140317w.cpp.elf
 17346      790     426   18562    4882 HA15_20140317w.cpp.elf

The first line (with text 17554) was not working, after much editing, the second line (with text 16316) is working as it should.

edit: the third line has everything working, serial reading, my new functions, etc. I essentially removed some global variables and duplicate code. I mention this because (as suspected) it's not about this code per sae, it has to be about the RAM usage. Which brings me back to the original question, "how to best measure it" I'm still checking out some answers, thanks.

How do I actually interpret the above information?

So far my understanding is:

`TEXT` is program instruction memory
`DATA` is variables (unitialised?) in program memory
`BSS`  is variables occupying RAM

since BSS is considerably less than 1024 bytes, why does the second work, but the first doesn't? If it's DATA+BSS then both occupy more than 1024.

re-edit: I edited the question to include the code, but now I've removed it because it really had nothing to do with the problem (other than maybe poor coding practices, variable declarations and the like). You can review the code by looking back through the edits if you really want to see it. I wanted to get back to the question at hand, which was more based around: How to measure RAM usage.

Madivad
  • 1,372
  • 8
  • 26

5 Answers5

15

You can use the functions provided AVRGCC: Monitoring Stack Usage

The function was intended to check the stack usage but what it reports is the actual RAM that has never been used (during execution). It does so by "painting" (filling) the RAM with a known value (0xC5), and then checking the RAM area counting how many bytes have still the same initial value.
The report will show the RAM that has not been used (minimum free RAM) and therefor you can calculate the max RAM that has been used (Total RAM - reported RAM).

There are two functions:

  • StackPaint is executed automatically during initialization and "paints" the RAM with the value 0xC5 (can be changed if needed).

  • StackCount can be called at any point to count the RAM that hasn't been used.

Here is an example of usage. Doesn't do much but is intended to show how to use the functions.

// -----------------------------------------------------------------------------
extern uint8_t _end;
extern uint8_t __stack;

void StackPaint(void) __attribute__ ((naked)) __attribute__ ((section (".init1")));

void StackPaint(void)
{
#if 0
    uint8_t *p = &_end;

    while(p <= &__stack)
    {
        *p = 0xc5;
        p++;
    }
#else
    __asm volatile ("    ldi r30,lo8(_end)\n"
                    "    ldi r31,hi8(_end)\n"
                    "    ldi r24,lo8(0xc5)\n" /* STACK_CANARY = 0xc5 */
                    "    ldi r25,hi8(__stack)\n"
                    "    rjmp .cmp\n"
                    ".loop:\n"
                    "    st Z+,r24\n"
                    ".cmp:\n"
                    "    cpi r30,lo8(__stack)\n"
                    "    cpc r31,r25\n"
                    "    brlo .loop\n"
                    "    breq .loop"::);
#endif
} 


uint16_t StackCount(void)
{
    const uint8_t *p = &_end;
    uint16_t       c = 0;

    while(*p == 0xc5 && p <= &__stack)
    {
        p++;
        c++;
    }

    return c;
} 

// -----------------------------------------------------------------------------

void setup() {

Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
Serial.println(StackCount(), DEC);  // calls StackCount() to report the unused RAM
delay(1000);
}
alexan_e
  • 1,860
  • 1
  • 13
  • 16
10

The main issues you can have with memory usage at runtime are:

  • no available memory in the heap for dynamic allocations (malloc or new)
  • no room left on the stack when calling a function

Both are actually the same as the AVR SRAM (2K on Arduino) is used for both (in addition to static data which size never changes during program execution).

Generally, dynamic memory allocation is seldom used on MCUs, only a few libraries typically use it (one of them is String class, which you mentioned you don't use, and that's a good point).

The stack and the heap can be seen in the picture below (courtesy of Adafruit): enter image description here

Hence, the most expected issue comes from stack overflow (i.e. when the stack grows towards the heap and overflows on it, and then -if the heap was not used at all- overflows on the static data zone of the SRAM. At that time, you have a high risk of either:

  • data corruption (i.e. the stack ovewrites heap or static data), giving you ununderstandable behavior
  • stack corruption (i.e. the heap or static data overwrites stack content), generally leading to a crash

In order to know the amount of memory that's left between the top of the heap and the top of the stack (actually, we might call it the bottom if we represent both the heap and the stack on the same image as depicted below), you can use the following function:

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

In the code above, __brkval points to the top of the heap but is 0 when the heap has not been used, in which case we use &__heap_start which points to __heap_start, the first variable that marks the bottom of the heap; &v points of course to the top of the stack (this is the last variable pushed on the stack), hence the formula above returns the amount of memory available for the stack (or the heap if you use it) to grow.

You can use this function in various locations of your code to try and find out where this size is getting dramatically reduced.

Of course, if ever you see this function return a negative number then it is too late: you have already overflown the stack!

jfpoilpret
  • 9,162
  • 7
  • 38
  • 54
7

When you figure out how to locate the generated .elf file in your temporary directory, you can execute the command below to dump a SRAM usage, where project.elf is to be replaced with the generated .elf file. The advantage of this output is the ability to inspect how your SRAM is used. Do all the variables need to be global, are they really all required?

avr-objdump -S -j .bss project.elf

project.elf:     file format elf32-avr


Disassembly of section .bss:

00800060 <__bss_start>:
        ...

00800070 <measurementReady>:
        ...

00800071 <cycles>:
        ...

00800073 <measurement>:
  800073:       00 00 00 00                                         ....

00800077 <measurementStart>:
  800077:       00 00 00 00                                         ....

0080007b <timerOverflows>:
  80007b:       00 00 00 00

Notice that this doesn't show stack or dynamic memory use as Ignacio Vazquez-Abrams noted in the comments below.

Additionally a avr-objdump -S -j .data project.elf can be checked, but none of my programs output anything with that so I can't tell for sure if it is useful. It supposed to list 'initialized (non-zero) data'.

jippie
  • 2,911
  • 14
  • 23
3

I suspected for a while that I was getting to the end of available RAM. I don't think I'm using too much stack (although it's possible), what is the best way to determine how much RAM I am actually using?

It would be best to use a combination of manual estimation and by using the sizeof operator. If all your declarations are static, then this should give you an accurate picture.

If you are using dynamic allocations, then you may run into a problem once you start deallocating the memory. This is due to memory fragmentation on the heap.

Going through and trying to work it out, I have problems when I get to enums and structs; how much memory do they cost?

An enum takes as much space as an int. So, if you have a set of 10 elements in a enum declaration, that would be 10*sizeof(int). Also, every variable that uses an enumeration is simply an int.

For structures, it would be easiest to use sizeof to find out. Structures occupy a (minimum) space equal to sum of its members. If the compiler does structure alignment, then it may be more, however this is unlikely in the case of avr-gcc.

asheeshr
  • 3,847
  • 3
  • 26
  • 61
1

There is a program called Arduino Builder that provides a neat visualization of the amount of flash, SRAM and EEPROM your program is using.

Arduino Builder

The Arduino builder makes up part of the CodeBlocks Arduino IDE solution. It can be used as either a standalone program or through the CodeBlocks Arduino IDE.

Unfortunately Arduino Builder is a little old but it should work for most programs and most Arduinos, such as the Uno.

sa_leinad
  • 3,218
  • 2
  • 23
  • 51