PDA

View Full Version : Bug - malloc()?



Bill Greiman
11-14-2012, 05:16 PM
malloc() crashes long before the heap is exhausted. The problem is that this version of newlib uses a 4096 byte page size.

This sketch crashes with a hard fault exception with over 4096 bytes of unallocated heap space:


#include <unistd.h>
extern long _estack;
void setup() {
Serial.begin(9600);
while (!Serial) {}
delay(2000);
Serial.print("&_estack: ");
Serial.println((int)&_estack, HEX);
for (int i = 0; ;i++) {
Serial.print("sbrk: ");
Serial.println((int)sbrk(0), HEX);
Serial.println("calling malloc");
Serial.flush();
char* p = (char*)malloc(1000);
Serial.print("malloc returned: ");
Serial.println((int)p, HEX);
Serial.flush();
if (!p) break;
}
}
void loop() {}

The output is:


&_estack: 20002000
sbrk: 1FFFF13C
calling malloc
malloc returned: 1FFFF148
sbrk: 20000000
calling malloc
malloc returned: 1FFFF538
sbrk: 20000000
calling malloc
malloc returned: 1FFFF928
sbrk: 20000000
calling malloc
malloc returned: 1FFFFD18
sbrk: 20001000
calling malloc
malloc returned: 20000108
sbrk: 20001000
calling malloc
malloc returned: 200004F8
sbrk: 20001000
calling malloc
malloc returned: 200008E8
sbrk: 20001000
calling malloc

The following change to _sbrk softens the failure:


void * _sbrk(int incr)
{
static char *heap_end = (char *)&_ebss;
char *prev = heap_end;

/* added by Bill Greiman */
if ((heap_end + incr) >= &_estack) return -1;

heap_end += incr;
return prev;
}

malloc() now return null but about 4k is not allocated.


&_estack: 20002000
sbrk: 1FFFF0A0
calling malloc
malloc returned: 1FFFF0A8
sbrk: 20000000
calling malloc
malloc returned: 1FFFF498
sbrk: 20000000
calling malloc
malloc returned: 1FFFF888
sbrk: 20000000
calling malloc
malloc returned: 1FFFFC78
sbrk: 20001000
calling malloc
malloc returned: 20000068
sbrk: 20001000
calling malloc
malloc returned: 20000458
sbrk: 20001000
calling malloc
malloc returned: 20000848
sbrk: 20001000
calling malloc
malloc returned: 0

Bill Greiman
11-14-2012, 09:12 PM
Looks like more memory is allocated before the crash. I replaced the Serial.flush() with delay(1000).

In this case almost all of memory is allocated and the result is printed.

The result is still a hard fault exception. Apparently Serial data has not been sent over the USB when Serial.flush() returns.

There still isn't an easy fix in _sbrk() because of the 4096 byte page size.

PaulStoffregen
11-15-2012, 12:08 AM
I'm planning to port the AVR malloc/realloc/free to Teensy 3.0.

Looks like this will be needed sooner rather than later!

Bill Greiman
11-15-2012, 01:40 AM
I hope the port of AVR malloc() will support schedulers and RTOSs. It needs flexibility to support calls from threads with their stack located anywhere in memory. It must also return null when appropriate.

The Due scheduler calls malloc() to allocate stack space.

I now have FreeRTOS running on Teensy 3.0 and it also relies heavily on malloc() to create threads/stacks.

ChibiOS does not need malloc() but thread stacks are in global memory below the heap. This means that String and other libraries that need malloc() will have their stack below the heap.

newlib malloc() is not inherently bad, it's just that newlib defaults are not good for most Cortex M chips.

newlib should be built with a page size of 128 byte, the SMALL_MEMORY model, for Cortex M chips.

newlib malloc() is overwhelmingly complex and messy so I wouldn't attempt to modify it.