Ah, yes, very clever how you find this stuff
However, this isn't exactly my Newlib sourcecode. I looked through Cube IDE disassembly of mine, when looking to fix the stupid empty mutex stubs. The ST lib was not "weak" so they could not be replaced in the obvious way (see other threads on weakening the whole libc.a using objopy) and it had calls to dummy mutex functions, around the entry and exit. It could be the same sourcecode apart from that though.
It seems to work fine. I am running malloc/free fairly randomly, at ~10Hz, with a 1k and a 48k block, within a heap area of about 56k (so I reckon fragmentation is impossible) while running the whole box, loads of RTOS tasks, comms, TLS "concurrently" to two peers (TLS uses the 48k block for its private heap which uses another heap code) and there is no leakage.
Well, the sourcecode is probably the 1990 Newlib (the source
was found for the printf, now replaced with one which doesn't need mutex protection) so they've had 33 years to fix the bugs
I think mutex protection on malloc/free cannot be avoided if you want it thread-safe (multiple RTOS tasks using it). My box doesn't use the heap in any way which could ever fragment, FWIW.
Try allocating 12 bytes or 20 bytes and see what happens.
This is really strange!
Just 4 bytes between the 20 byte blocks, and they are all the same. So it looks like the previous data was just garbage.
The yellow bit is just the remainder of the heap area which could not be allocated for a 20 byte block.
The "S" is the stack area, which (as traditionally) starts after the top of the heap. The top 8k (2001E000-FFFF) of the RAM is the 8k stack. That one is used only for interrupts.
This is the 12-byte case:
int x=1;
while (true)
{
osDelay(100);
char *buf = malloc(12);
if ( buf==NULL)
{
printf("malloc failed, x=%d",x);
x++;
}
else
{
printf("malloc good, x=%d",x);
x++;
// fill the block with 0x77
for (int a=0; a<12; a++)
{
buf[a]=0x77;
}
//free(buf);
}
}
With a 16384 block, I see 4 bytes of (probably) garbage and 0x00004009
BTW does anyone know what exactly _sbrk is supposed to do? I am finding conflicting stuff on the net. It is a simple function which takes in a value called incr (which it turns out is
not the size of the requested block, but mine seems to get called with a sequence of power of 2 values like 4096 8192 etc) and has access to labels representing the end of BSS and the base of stack i.e. the extent of the RAM available for the heap. And it is
not getting called on every malloc.
Another test, a repeated 50000 byte malloc/free, calls _sbrk just on the 1st malloc, with an incr value of Decimal 53248 / Hex:0xd000. However, this is probably right since the source
https://github.com/devkitPro/newlib/blob/master/newlib/libc/stdlib/_mallocr.c describes how padding is used to avoid calling _sbrk too often. Therefore the heap code must be using that 1st call to _sbrk to obtain the heap area extents and then it stores them locally. But that would make it non thread safe - although all RTOS tasks would be sharing the same area so maybe it's OK.
My current _sbrk
// This is used by malloc().
caddr_t _sbrk(int incr)
{
// These are defined in the linkfile
extern char end asm("_end");
extern char top asm("_top");
static char *heap_end;
char *prev_heap_end;
// This sets heap_end to end of BSS, on the first call to _sbrk
if (heap_end == 0)
heap_end = &end;
prev_heap_end = heap_end;
// top = top of RAM minus size of stack
if ( (heap_end + incr) > &top )
{
// write(1, "Heap and stack collision\n", 25);
// abort();
errno = ENOMEM;
return (caddr_t) -1;
}
heap_end += incr;
return (caddr_t) prev_heap_end;
}
Am I right that the line
if (heap_end == 0)
should be
if (heap_end == NULL)