3
votes

I have a barebone arm controller compiled with the gcc-arm-none-eabi toolchanin. I notice that asking malloc() for 132 bytes which in turn causes malloc() to call sbrk() twice, first asking for size 152 B and next asking for 4100 B before returning.

Malloc() is in this case called from sprintf(), and it seem that each formatter (%d, %f, ...) allocates its own memory. Repeted use of one identifier does not result in more allocation. It is when %f is used that the 4100 B is allocated.

4 kB is in this case not a problem. But I would like to know that it does not ask for another similar amount.

  • Should malloc allocate this much?

  • Is there anything I can set to prevent it?

  • Should I worry that malloc might ask for even more space (without reason)?

The sbrk code is not the one provided from the toolchain. malloc is wrapped so I can see what happens.

char *_cur_brk;
void *_sbrk_r(struct _reent *reent, ptrdiff_t diff)
{
    void *_old_brk = _cur_brk;
    monitor_printf("_sbrk_r called with size = %d. cur_brk = %p \t", diff, _cur_brk);
    void * ptr;
    ptr = __builtin_return_address(0);
    monitor_printf("Called from %d: %p\t", 0, ptr );
    monitor_printEOL();
    extdiff = diff;

    if ( (_cur_brk + diff ) > _HeapLimit  ) {
        sbrk_error += 1;
        errno = ENOMEM;
        monitor_printf(" failed, cur_brk=%d, HeapLimit=%d", _cur_brk, _HeapLimit);
        monitor_printEOL();
        return (void *)-1;
    }
    sbrk_error = 0;
    _cur_brk += diff;

    monitor_printf("return  %p", _old_brk);
    monitor_printEOL();
    return _old_brk;
}
void * __wrap__malloc_r( struct _reent *reent, size_t size )
{
    void * ret;
    monitor_printf("wrap_malloc_r called with size = %d.", size);
    monitor_printEOL();
    ret = __real__malloc_r( reent, size );
    return ret;
}

output of call to malloc(132)

wrap_malloc_r called with size = 132.
_sbrk_r called with size = 152. cur_brk = 0x20005f64    Called from 0: 0x801311b 
return  0x20005f64
_sbrk_r called with size = 4100. cur_brk = 0x20005ffc   Called from 0: 0x8013181 
return  0x20005ffc 
Memory allocated at 0x20005f70lling malloc(132)

thanks /johan

1
Why would you ever use malloc on a bare metal microcontroller? It doesn't make any sense - an ARM is not a PC. Read this.Lundin
@Lundin: Microcontrollers nowadays can have more memory than the original IBM PC. The link you give refers to a microcontroller where malloc indeed makes no sense, but you can't just assume that the same holds for joing here.MSalters
@joing: Is this consistent, or just on the first call? malloc might set up a 4kB page of bookkeeping on the first callMSalters
@MSalters The amount of memory isn't the reason why it doesn't make sense. The lack of a multi-process desktop operative system is.Lundin
@MSalters Another reason not to use heap-based dynamic memory allocation is fragmentation. A system which is meant to run for months and years must have predictable memory behaviour.too honest for this site

1 Answers

2
votes

"Without reason" is a bold claim, I'm sure it's not doing it just to be mean.

What you should do, however, is investigate if you can move to a more embedded-friendly standard library, one that is more careful with memory. It seems its your heap allocation code, i.e. the malloc() implementation that's the culprit.

One simple guess is that malloc() needs some data structure to keep track of the allocations, and since you're (probably, remember this is a guess!) doing the first allocation as a side-effect of vsnprintf(), it has to do that in order to track it.

On more typical desktop/server computer platforms of today, 4 KB is very little memory so no reason to not do the allocation if it's needed by the design. In the embedded space, of course, that's not true.

Personally, I have a local replacement for vsnprintf() that does zero heap allocations.