Free Ram Function?

Hi folks,

I tried searching the forum, but couldn’t find an answer to this question.

Are there any functions that would allow us to determine the amount of free SRAM while a program is running? I suspect I have a memory leak that’s causing my Artemis to spontaneously reset after a certain period of time.

Cheers,

Adam

Thanks for reaching out to us on this.

nothing readily available for WHILE the program is running, but you can read about optimizing SRAM here: https://learn.adafruit.com/memories-of- … izing-sram which may be helpful.

Best of luck!

Thanks, Russel.

The problem turned out to be a memory issue in the SparkFun u-blox library: https://github.com/sparkfun/SparkFun_u- … /issues/20

Though, I’d be curious to hear from Robin or Paul if they ever use any functions to track the heap usage with the Artemis in real-time. Back in the AVR days, there was a function you could use to do so.

Cheers,

Adam

Never worked on this in the “Arduino” world, but have done it extensively in Linux. An often and general issue is memory leaks, malloc() is happening without a corresponding free() . A quick look at the U-box shows the same. Trying to get into the details during runtime would mean a deepdive in the GCC and I am not attracted to that.

For me, it is really easy. I use my own version of malloc() and I don’t even implement a free(). That means I can always know exactly how much heap has been used. Once my systems have booted, if the heap is ever found to be decreasing, I immediately know that I have a problem. Garbage collection or memory coalescing operations during a free() are a giant source of bugs as well as timing non-determinism which can be a real problem in a real-time embedded system. By not allowing free(), all those potential problems simply vanish.

Along those same lines, my previous company developed its own embedded processor architectures. One potentially peculiar feature was that our processor architecture had no stack in either the hardware or software. That caused some heartburn for our port of the GCC compiler and significantly more complexity for our own custom assembler, but in all the products we ever shipped, we never had to track down any stack overflow issues. That was a beautiful thing.

I also wrote my own UBX protocol interface. It does not do any dynamic memory allocation. An Artemis has lots of RAM. Take advantage of that freedom!

Hi guys, I’m still lurking, have been flat out though (and will continue to be for a while). I use this code on my Artemis (core 1.2.0, untested on 2.x) to detect memory leaks. It does detect memory leaks, but some of the other functionality doesn’t work (per the comments in the code). Warts ‘n’ all below…

The subject of an issue:https://github.com/sparkfun/Arduino_Apo … -container

uint32_t prevHeapSize = 0;
uint32_t heapRAMThreshold = 1000;

bool checkMemoryLeak()
{
  char buf[maxFileLineLength];
  if(cycleCount < 3)  //Allow things to settle down
    prevHeapSize = getHeapRAM();
//      prevHeapSize = 0;
  
  if(getHeapRAM() > heapRAMThreshold && getHeapRAM() > prevHeapSize+10)
  {
    sprintf(buf,"Possible MEMORY LEAK, prevHeapSize:%lu currentHeapSize:%lu", prevHeapSize, getHeapRAM());
    Serial.print(buf);
    prevHeapSize = getHeapRAM();
    return true;
  }
  sprintf(buf,"Memory usage, prevHeapSize:%lu currentHeapSize:%lu", prevHeapSize, getHeapRAM());
   Serial.printLn(buf);
  prevHeapSize = getHeapRAM();
  return false;
}

//Adapted by Stephen Fordyce from code at https://forum.arduino.cc/index.php?topic=285436.0
#include <malloc.h>

//extern "C" char _end;
char _end;
extern "C" char *sbrk(int i);

#define RAM_START_ADDR 0x10010000 //Decimal: 268500992, per p71 of Apollo3 datasheet
#define RAM_END_ADDR 0x1005FFFF  //Decimal: 268828671, per p71 of Apollo3 datasheet
//Apollo3 has Decimal 327679 bytes of memory between RAM_START_ADDR & RAM_END_ADDR

void printRAMUsage()
{  
  char *heapend = sbrk(0);
//  register char * stack_ptr asm( "sp" );  //THIS DIDN'T WORK EITHER
  uint32_t stack_ptr = __get_MSP();  //THIS DOESN'T WORK (fix in 2 spots)
  Serial.println("****Printing Current RAM Usage****");
  Serial.print("Total RAM on board:"); Serial.println(getTotalRAM());
  Serial.println("Addresses:");
  Serial.print("  start RAM: 0x"); Serial.println(RAM_START_ADDR,HEX);
  Serial.print("   heap end: 0x"); Serial.println((int)heapend,HEX);
  Serial.print("  stack ptr: 0x"); Serial.print("(Wrong?)"); Serial.println((uint32_t)stack_ptr,HEX);
  Serial.print("    end RAM: 0x"); Serial.println(RAM_END_ADDR,HEX);
  Serial.println("Ram used (bytes): ");
  Serial.print("  static(globals):  "); Serial.println(getGlobalsRAM());
  Serial.print("  dynamic(heap/malloc): "); Serial.println(getHeapRAM());  
  Serial.print("  stack(locals):   "); Serial.print("(Wrong?)"); Serial.println(getStackRAM());
  Serial.print("Estimation free Ram(byte): "); Serial.print("(Wrong?)"); Serial.println(getFreeRAM());
}

uint32_t getTotalRAM()
{
  uint32_t totalRAM = (uint32_t)RAM_END_ADDR-(uint32_t)RAM_START_ADDR;  
  return totalRAM;
}

uint32_t getGlobalsRAM()
{
  uint32_t globalsRAM = (uint32_t)&_end - (uint32_t)RAM_START_ADDR;
  return globalsRAM;
}

uint32_t getHeapRAM()
{
  struct mallinfo mi = mallinfo();  //mallinfo() definition: https://man7.org/linux/man-pages/man3/mallinfo.3.html  
  uint32_t heapRAM = (uint32_t)mi.uordblks;
  return heapRAM;
}

uint32_t getStackRAM()
{
//  register char * stack_ptr asm( "sp" );  //THIS DIDN'T WORK EITHER
  uint32_t stack_ptr = __get_MSP();  //THIS DOESN'T WORK (fix in 2 spots)
  uint32_t stackRAM = (uint32_t)RAM_END_ADDR - stack_ptr;
  return stackRAM;  
}

uint32_t getFreeRAM()
{
  return getTotalRAM() - getGlobalsRAM() - getHeapRAM() - getStackRAM();
}

What I have found that works well for finding stack overflows in a single-stack universe like Arduino is to instruct the linker to move the stack to an area at the start of RAM, then have the global RAM follow the stack, then put the heap after the end of the globals running to the end of RAM. This arrangement makes it impossible for a stack overflow to corrupt global RAM and cause hard to track down system behavior. If the stack ever overflows, the system crashes immediately when it tries to access RAM that is simply not there. This approach allow allows your free RAM estimator to simply ignore the stack because it will have a fixed maximum size.

To get a general estimation of stack usage, you can fill the stack RAM with a sentinel value instead of 0 (like the ever popular 0xDEADBEEF) and once in a while search backwards through the stack for the first word of stack RAM that does not contain the sentinel to find the high-water mark. But that doesn’t prove that your current high-water mark will always be the absolute guaranteed high-water mark. Maybe some unusual but entirely possible combination of user interaction and interrupts will cause the stack to go deeper. It’s more of a feel-good thing than proof of anything.

@robin_hodgson and @stephenf I’m certainly digging up an old post, but I’m currently investigating another potential memory leak with the Apollo3.

Robin, you mentioned you implemented your own version of malloc() to track the heap. Is this code something you can share?

Stephen, I’m currently attempting to use your code snippets but I’m not sure if you ever managed to get everything working. Are there certain aspects of the code that are more reliable than others (e.g., stack vs. heap vs. ram)?

Cheers,

Adam

Again, I use malloc() and new() all the time, but never use free(). If you want to track your malloc operations, I recommend the following.

Malloc() typically calls the routine sbrk() to actually perform the low-level heap allocation. The standard sbrk() call has a useful side-effect: if you ask it to allocate 0 bytes, it will return a pointer to the address that will be allocated next, i.e. a pointer to the current top-of-heap. In other words:

// Find out where the top of the heap is right now:
void* topOfHeap = (void*)sbrk(0);

If you know the heap’s starting address, you can trivially calculate how much space has been allocated to this point. If sbrk(0) returns NULL, it means that your heap is absolutely full.

If you want to go deeper than just knowing where the top of the heap is, it is really easy to supply your own sbrk(). Here is an example from something I did a long time ago. You will see why sbrk(0) returns the current top of heap:

// Allocate memory from the heap.
// All allocation requests are forced to begin on a word-aligned address.
extern caddr_t sbrk(int incr)
{
	static unsigned char* endOfHeap = NULL;
	unsigned char *prev_heap;

	// Adjust incr (if necessary) such that our heap pointer always stays word aligned
	incr = ((incr+3) & ~3);
	
	if (endOfHeap == NULL) {
		// Force the initial heap pointer to be word-aligned.
		// This should be unnecessary given the instructions for how the heap is defined to the linker, but this is safe:
		endOfHeap = (unsigned char *)(((uint32_t)(&_sheap)+3) & ~3);
	}
	prev_heap = endOfHeap;
	
	// Check if this allocation request will take us past the end of the heap:
	if ((endOfHeap + incr) >= (unsigned char *)(&_eheap)) {
		// Not enough room! This allocation fails:
		return(NULL);
	}

	// If we get here, we know the allocation will succeed:
	endOfHeap += incr;
	return (caddr_t) prev_heap;
}

In this example, I got the start and end addresses of the heap created by the linker script as the symbols _sheap and _eheap. Your symbols may be different. But an even easier way to do it is to leave the linker out of the equation and declare the heap as a static array. This means that the heap will get stored in amongst the rest of your global variables , but since sbrk() is written to never go outside its bounds, it’s all perfectly safe:

#define HEAPSIZE 4096
uint8_t heap[HEAPSIZE];

Now, change the sample code above to refer to the start of the heap as &heap[0] and the end of the heap as &heap[HEAPSIZE] and you are good to go.

The beauty of supplying your own sbrk() is that your code becomes completely responsible for allocating all heap memory. No heap allocations occur without you knowing about it!