Pages: 1 [2]   Go Down
Author Topic: How much ram is being used?  (Read 26504 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Full Member
***
Karma: 0
Posts: 107
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Actually, at one point, the whole thing crashed.
Then, as I disabled things, the LCD worked but it was displaying strings in the wrong place (and I use lcd.cursorTo(1,0) before each display update)..  another time, it was displaying complete gibberish because the strings were constantly being overwritten by something..

So far, I managed to free about 350 bytes at the point the sketch starts and things appear to be behaving finally, plus I was able to re-enabled all the features after I moved 90% of my strings to PROGMEM
Logged

0
Offline Offline
Full Member
***
Karma: 0
Posts: 107
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
By nested functions I mean neither what you nor tehboii are saying.  I mean simply something like:

void A(...)
{
  some stuff
  B(...);
}

void B(...)
{
  some stuff
  C(...);
}

void C(...)
{
  some stuff
}

void loop()
{
  some stuff
  A(...);
}

loop() calls A(), which calls B(), which calls C().  Every function called adds more data to the stack, and this data isn't removed from the stack until the function returns.  By the time you've reached C() you're now four levels deep.  If you try to figure out how much stack you're using by just looking at what C() requires, you could be severely underestimating your stack usage.  If you want to assess your program's stack usage, you need to look for locations that seem like they might require the most stack space and check your free memory there.  This will probably be inside one of your most deeply nested functions, or inside a function with a lot of local variables.

The same problem results from recursion.  Every level of recursion depth adds data to the stack, so recursing too deeply will cause problems.

- Ben
Following someone else's advice, I tried to keep most functions to less than 20 lines, so you can see the whole function on-screen without scrolling.  So I tried to group certain tasks into a separate function and call them multiple times from within another.
I also got rid of some 'overhead' and freed up process time by enabling/using a 1hz 'interrupt' from my rtc.  this way most of the functions, including display refresh only happens once a second - which is more than fast enough.  otherwise the serial 'interface' was somewhat unresponsive.

Maybe the onewire functions are giving me some of the grief.. so I will probably try to reduce recursion around those functions..
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 3
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Mems, Bens, and others,

This thread has been quite helpful.  You guys are champs.  I've been developing code on top of a 3rd party's software base for the Arduino and running into problems with someone (?) writing to memory incorrectly.  Since the 3rd party code is large and rev 1.0 (and I make goofs all the time) it was not clear who was stomping on memory incorrectly (them or me).  So, after unit testing my own code elsewhere to verify it was clean and also verifying that the 3rd party's code does, indeed, work properly it seemed there was another problem coming into play.  I suspected memory size constraints but lacked simple tools to measure memory consumption on the Arduino. That's when I discovered this thread and a number of others.

I've put together a sketch that appears to give good results and a complete memory profile so the remaining memory (or stack/heap collision) can be clearly indicated.  It is in the next post....

Marc
« Last Edit: September 15, 2009, 05:57:21 am by comperem » Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 3
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Mems, Bens, and others,

Referring to the my previous post immediately above, this sketch is a straightforward memory profiler that computes the size of the .data, .bss, stack, heap, and free ram sections.  I've tested it on the Arduino Duemilanove with the ATmega328 chip in the Arduino-0016 IDE and it seems to be working properly (so far).

It is based on 'Pauls' clearly written function from the Pololu forum, get_free_memory(). (this thread already hilighted his work here: http://forum.pololu.com/viewtopic.php?f=10&t=989&view=unread#p4218http://).  It also uses the [b]check_mem()[/b] function from the Arduino playground.  I wanted to compare their results (which turned out favorably).This code has no malloc()'s and seems to predictably compute the memory sizes of each section illustrated in the [b]Memory Areas and Using malloc()[/b], [url]http://www.nongnu.org/avr-libc/user-manual/malloc.html.

Given all the disclaimers and caveats you've mentioned before, this sketch shows how adding or subtracting some number of characters to the strings in the loop() function predictably adds or subtracts that exact number of bytes from the .data section size and reduces free memory.

Thanks again for posting great explanations and pointing out the Pololu link.  Also, thanks to Paul at Pololu for his great get_free_memory() function illustrating how to get a handle on this memory issue.

You should be able to cut-and-paste and pop this code into a new Arduino sketch, download and check out the memory profile which should look something like this:
   get_free_memory() reports [1109] (bytes) which must be > 0 for no heap/stack collision
   ram   size=[2047] bytes decimal
   .data size=[734] bytes decimal
   .bss  size=[183] bytes decimal
   heap  size=[6] bytes decimal
   stack size=[16] bytes decimal
   free size=[1108] bytes decimal

Why isn't a memory profile like this included in the Arduino IDE???

Marc


Code:
/*
 * Cut-and-pasted from www.arduino.cc playground section for determining heap and stack pointer locations.
 * http://www.arduino.cc/playground/Code/AvailableMemory
 *
 * Also taken from the Pololu thread from Paul at: http://forum.pololu.com/viewtopic.php?f=10&t=989&view=unread#p4218
 *
 * Reference figure of AVR memory areas .data, .bss, heap (all growing upwards), then stack growing downward:
 * http://www.nongnu.org/avr-libc/user-manual/malloc.html
 *
 */

extern unsigned int __data_start;
extern unsigned int __data_end;
extern unsigned int __bss_start;
extern unsigned int __bss_end;
extern unsigned int __heap_start;
//extern void *__malloc_heap_start; --> apparently already declared as char*
//extern void *__malloc_margin; --> apparently already declared as a size_t
extern void *__brkval;
// RAMEND and SP seem to be available without declaration here

int16_t ramSize=0;   // total amount of ram available for partitioning
int16_t dataSize=0;  // partition size for .data section
int16_t bssSize=0;   // partition size for .bss section
int16_t heapSize=0;  // partition size for current snapshot of the heap section
int16_t stackSize=0; // partition size for current snapshot of the stack section
int16_t freeMem1=0;  // available ram calculation #1
int16_t freeMem2=0;  // available ram calculation #2


/* This function places the current value of the heap and stack pointers in the
 * variables. You can call it from any place in your code and save the data for
 * outputting or displaying later. This allows you to check at different parts of
 * your program flow.
 * The stack pointer starts at the top of RAM and grows downwards. The heap pointer
 * starts just above the static variables etc. and grows upwards. SP should always
 * be larger than HP or you'll be in big trouble! The smaller the gap, the more
 * careful you need to be. Julian Gall 6-Feb-2009.
 */
uint8_t *heapptr, *stackptr;
uint16_t diff=0;
void check_mem() {
  stackptr = (uint8_t *)malloc(4);          // use stackptr temporarily
  heapptr = stackptr;                     // save value of heap pointer
  free(stackptr);      // free up the memory again (sets stackptr to 0)
  stackptr =  (uint8_t *)(SP);           // save value of stack pointer
}


/* Stack and heap memory collision detector from: http://forum.pololu.com/viewtopic.php?f=10&t=989&view=unread#p4218
 * (found this link and good discussion from: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1213583720%3Bstart=all )
 * The idea is that you need to subtract your current stack pointer (conveniently given by the address of a local variable)
 * from a pointer to the top of the static variable memory (__bss_end). If malloc() is being used, the top of the heap
 * (__brkval) needs to be used instead. In a simple test, this function seemed to do the job, showing memory gradually
 * being used up until, with around 29 bytes free, the program started behaving erratically.
 */
//extern int __bss_end;
//extern void *__brkval;

int get_free_memory()
{
  int free_memory;

  if((int)__brkval == 0)
     free_memory = ((int)&free_memory) - ((int)&__bss_end);
  else
    free_memory = ((int)&free_memory) - ((int)__brkval);

  return free_memory;
}






void setup()                    // run once, when the sketch starts
{
  Serial.begin(57600);
}

void loop()                     // run over and over again
{
  Serial.print("\n\n--------------------------------------------");
  Serial.print("\n\nLOOP BEGIN: get_free_memory() reports [");
  Serial.print( get_free_memory() );
  Serial.print("] (bytes) which must be > 0 for no heap/stack collision");
  

  Serial.print("\n\nSP should always be larger than HP or you'll be in big trouble!");
  
  check_mem();

  Serial.print("\nheapptr=[0x"); Serial.print( (int) heapptr, HEX); Serial.print("] (growing upward, "); Serial.print( (int) heapptr, DEC); Serial.print(" decimal)");
  
  Serial.print("\nstackptr=[0x"); Serial.print( (int) stackptr, HEX); Serial.print("] (growing downward, "); Serial.print( (int) stackptr, DEC); Serial.print(" decimal)");
  
  Serial.print("\ndifference should be positive: diff=stackptr-heapptr, diff=[0x");
  diff=stackptr-heapptr;
  Serial.print( (int) diff, HEX); Serial.print("] (which is ["); Serial.print( (int) diff, DEC); Serial.print("] (bytes decimal)");
  
  
  Serial.print("\n\nLOOP END: get_free_memory() reports [");
  Serial.print( get_free_memory() );
  Serial.print("] (bytes) which must be > 0 for no heap/stack collision");
  
  
  // ---------------- Print memory profile -----------------
  Serial.print("\n\n__data_start=[0x"); Serial.print( (int) &__data_start, HEX ); Serial.print("] which is ["); Serial.print( (int) &__data_start, DEC); Serial.print("] bytes decimal");

  Serial.print("\n__data_end=[0x"); Serial.print((int) &__data_end, HEX ); Serial.print("] which is ["); Serial.print( (int) &__data_end, DEC); Serial.print("] bytes decimal");
  
  Serial.print("\n__bss_start=[0x"); Serial.print((int) & __bss_start, HEX ); Serial.print("] which is ["); Serial.print( (int) &__bss_start, DEC); Serial.print("] bytes decimal");

  Serial.print("\n__bss_end=[0x"); Serial.print( (int) &__bss_end, HEX ); Serial.print("] which is ["); Serial.print( (int) &__bss_end, DEC); Serial.print("] bytes decimal");

  Serial.print("\n__heap_start=[0x"); Serial.print( (int) &__heap_start, HEX ); Serial.print("] which is ["); Serial.print( (int) &__heap_start, DEC); Serial.print("] bytes decimal");

  Serial.print("\n__malloc_heap_start=[0x"); Serial.print( (int) __malloc_heap_start, HEX ); Serial.print("] which is ["); Serial.print( (int) __malloc_heap_start, DEC); Serial.print("] bytes decimal");

  Serial.print("\n__malloc_margin=[0x"); Serial.print( (int) &__malloc_margin, HEX ); Serial.print("] which is ["); Serial.print( (int) &__malloc_margin, DEC); Serial.print("] bytes decimal");

  Serial.print("\n__brkval=[0x"); Serial.print( (int) __brkval, HEX ); Serial.print("] which is ["); Serial.print( (int) __brkval, DEC); Serial.print("] bytes decimal");

  Serial.print("\nSP=[0x"); Serial.print( (int) SP, HEX ); Serial.print("] which is ["); Serial.print( (int) SP, DEC); Serial.print("] bytes decimal");

  Serial.print("\nRAMEND=[0x"); Serial.print( (int) RAMEND, HEX ); Serial.print("] which is ["); Serial.print( (int) RAMEND, DEC); Serial.print("] bytes decimal");

  // summaries:
  ramSize   = (int) RAMEND       - (int) &__data_start;
  dataSize  = (int) &__data_end  - (int) &__data_start;
  bssSize   = (int) &__bss_end   - (int) &__bss_start;
  heapSize  = (int) __brkval     - (int) &__heap_start;
  stackSize = (int) RAMEND       - (int) SP;
  freeMem1  = (int) SP           - (int) __brkval;
  freeMem2  = ramSize - stackSize - heapSize - bssSize - dataSize;
  Serial.print("\n--- section size summaries ---");
  Serial.print("\nram   size=["); Serial.print( ramSize, DEC ); Serial.print("] bytes decimal");
  Serial.print("\n.data size=["); Serial.print( dataSize, DEC ); Serial.print("] bytes decimal");
  Serial.print("\n.bss  size=["); Serial.print( bssSize, DEC ); Serial.print("] bytes decimal");
  Serial.print("\nheap  size=["); Serial.print( heapSize, DEC ); Serial.print("] bytes decimal");
  Serial.print("\nstack size=["); Serial.print( stackSize, DEC ); Serial.print("] bytes decimal");
  Serial.print("\nfree size1=["); Serial.print( freeMem1, DEC ); Serial.print("] bytes decimal");
  Serial.print("\nfree size2=["); Serial.print( freeMem2, DEC ); Serial.print("] bytes decimal");
  
  delay(3000);
Logged

London
Offline Offline
Tesla Member
***
Karma: 10
Posts: 6250
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I agree that reporting memory usage at compile time (from the elf file) would be very welcome, but for runtime use, having the get_memory_free function in a library would make the basic info accessable through just including the header instead of having to copy and past the code - Here is the function as a library:

Save as MemoryFree.h in a library directory called MemoryFree
Code:
// memoryFree header

#ifndef      MEMORY_FREE_H
#define MEMORY_FREE_H

#ifdef __cplusplus
extern "C" {
#endif

int freeMemory();

#ifdef  __cplusplus
}
#endif

#endif
Save as MemoryFree.c in the same library directory
Code:
/*
 * MemoryFree.c
 * returns the number of free RAM bytes
*/

#include "WProgram.h"  
#include "MemoryFree.h"

extern unsigned int __data_start;
extern unsigned int __data_end;
extern unsigned int __bss_start;
extern unsigned int __bss_end;
extern unsigned int __heap_start;
extern void *__brkval;


int freeMemory()
{
  int free_memory;

  if((int)__brkval == 0)
     free_memory = ((int)&free_memory) - ((int)&__bss_end);
  else
    free_memory = ((int)&free_memory) - ((int)__brkval);

  return free_memory;
}
Usage in a sketch:
Code:
#include <MemoryFree.h>

void setup()                    // run once, when the sketch starts
{
  Serial.begin(9600);
}

void loop()                     // run over and over again
{
  Serial.print("freeMemory() reports ");
  Serial.println( freeMemory() );
}

« Last Edit: September 16, 2009, 03:23:34 am by mem » Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 3
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

mem,

Agreed, that's great.

By the way, the code-clip in my previous post (just above) is missing the final trailing curly brace to close off the loop() function.  Just tack on a } at the end.

I'd really like to see compile-time and dynamic run-time memory profiles built into the IDE.  It would help troubleshoot lots of newbie grief (... just like mine).

Thanks men,
Marc

Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 1
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This thread has been fantastic and very helpful. I am also listing how to look at memory usage at compile time, using the .elf file. I am sure most would know, how to get this info -- the hard way, so this post is for the sake of completeness (of this thread).

My original post is at http://web.goodrobot.com/blog/2009/10/arduino-crashes-and-memory-usage/

I used a slightly different technique to look at the memory usage. I found the memory usage at the compilation time itself like this. First, find the .elf file that the gcc-avr compiler creates (under an applet directory that the arduino GUI creates). And then do
nm applet/sketch.elf | sort | less
It has the output similar to the following text (only selected extract here). This output is for sketch, that has moved text strings to flash memory using PROGMEM.
00000000 a __tmp_reg__
.....
000022f6 T loop
00002398 T setup
00002444 T main
.....
00002d82 t __stop_program
00002d84 A __data_load_start
00002d84 T _etext
00002df2 A __data_load_end
00800100 D __data_start
00800134 D crlf
.....
0080078a B timer0_millis
0080078e b timer0_fract
0080078f B __bss_end
0080078f N _end
0080078f N __heap_start
00810000 N __eeprom_end

As you would notice, the data section  (bss and data  sections included) starts from 0×800100 and ends at 0×80078f, from where the heap and stack occupy. That comes to about 1689 bytes. I am working on Arduino Duemilanove, which has ATmega328 and has 2K RAM. So we are okay there (for now atleast).

Earlier, my data section was stretching upto 0×800900 (about 0×800 bytes which is 2K RAM) and causing crash/hang. None of my code or library uses malloc (as malloc is not listed on the elf file), so my sketch won't need heap memory. In any case, we should just keep the data/bss variables well within 0×800 bytes and keep some free memory too for stack and any library usage of heap.
« Last Edit: October 27, 2009, 09:29:50 am by rsmoorthy » Logged

Belgium
Offline Offline
Newbie
*
Karma: 0
Posts: 13
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This thread was extremely helpfull, and mem's MemoryFree library above is a must-have.
It should be included in the documented arduino libraries.
Logged

0
Offline Offline
God Member
*****
Karma: 2
Posts: 596
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

MemoryFree.cpp is missing an #include line.
Without it, I can't compile the example under arduino 18.

Here's the modified code:

Code:
extern unsigned int __bss_end;
extern unsigned int __heap_start;
extern void *__brkval;


#include "MemoryFree.h";


int freeMemory() {
  int free_memory;

  if((int)__brkval == 0)
     free_memory = ((int)&free_memory) - ((int)&__bss_end);
  else
    free_memory = ((int)&free_memory) - ((int)__brkval);

  return free_memory;
}


I think this lib should be almos-standard, so I'm going to put it in the playground.
Logged

0
Offline Offline
God Member
*****
Karma: 2
Posts: 596
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Here's also an example sketch:

Code:
#include <MemoryFree.h>

// Reported free memory with str commented out:
// 1848 bytes
//
// Reported free memory with str defined:
// 1834
//
// Difference: 14 bytes (13 ascii chars + null terminator

// 14-bytes string
//char str[] = "Hallo, world!";


void setup() {
    Serial.begin(115200);
}


void loop() {
    // also uncomment this line
    // when uncommenting the str definition line
    // otherwise the unused string will be compiled out
    //Serial.println(str);
    
    Serial.print("freeMemory()=");
    Serial.println(freeMemory());
    
    delay(1000);
}
« Last Edit: July 13, 2010, 04:03:00 am by mromani » Logged

0
Offline Offline
God Member
*****
Karma: 2
Posts: 596
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

http://www.arduino.cc/playground/Code/AvailableMemory

So there's just one entry point for the "free memory" problem.
Logged

Pages: 1 [2]   Go Up
Jump to: