Memory leak on task deletion with configUSE_NEWLIB_REENTRANT

n1vg wrote on Saturday, November 03, 2018:

I’ve got a problem that has me going in too many directions at once and I’m hoping someone here might be able to give me some direction.

All of my tasks save one are started once and are never terminated. The one exception is a network update downloader that of course only needs to run occasionally. It’s started, checks for updates, and ends - and it takes a chunk of heap with it, even if no explicit dynamic allocation is used in the task itself.

The leak is 1468 bytes, or 436 if stdout buffering is disabled. Assuming 8 bytes of overhead per allocation, that matches perfectly with the size of the reent structure and the default 1024 byte stdout buffer.

Presumably Newlib expects the OS to clean up after it. Or is there some way to tell it to free what it’s using?

I’m using Newlib’s own malloc for the kernel (configUSE_HEAP_SCHEME==6) - this comes from Dave Nadler by way of mcuoneclipse.com.

How does one normally deal with this? This can’t be an unusual requirement, to shut down a task without leaking memory. I’m trying to recall the reasons I switched heap schemes in the first place - I think initially I was using the CodeWarrior EWL library, which had a heap implementation that didn’t match the documentation, didn’t behave as expected, and has apparently been abandoned.

What I want is a setup that will let me use standard libraries in a thread-safe manner and either not have a separate heap for the RTOS and standard libaries, or at least have a heap that goes where the documentation says it will and plays nice with everything else. (This is all on a Cortex M4, BTW.)

What am I missing? This seems like it should be a really basic thing and I must be overlooking something obvious, but it’s Friday night and I’m already sleep-deprived and 80 hours into my work week and I’d just like to hear how everyone else deals with this.

Thanks,

Scott

rtel wrote on Saturday, November 03, 2018:

In addition - which version of FreeRTOS are you using? I think there
was something added to do with the reent struct when deleting a task
within the last year or two.

n1vg wrote on Sunday, November 04, 2018:

I’m running 10.0.1.

xz8987f wrote on Sunday, November 04, 2018:

I tried to replicate this (usually I never delete a task in my applications, so I might not have noticed things). So I created a task and killed it. It mallocs() the stack plus the TCB which includes the reentrant structure provided configUSE_NEWLIB_REENTRANT is set.
And after killing the task the IDLE task (should not be starved with killing tasks) properly frees the two memory blocks. So I don’t see for now a memory leak on my side? But I’m not calling other newlib functions, maybe the leak is caused by such functions (which ones?). What I see is that the newlib heap gets fragmented, but thats kind of expected?
If the newlib other library calls (printf()?) do allocate more memory chunks and do not free it, I don’t know how FreeRTOS would be able to deal with it?

I hope this helps,
Erich

n1vg wrote on Sunday, November 04, 2018:

Hi Erich,

I’ve been struggling to get this project to let me debug with the newlib sources so I can walk through it (as covered in my comment on your blog post) but it’s just not cooperating today.

I’ve been stepping through the first printf() call on a project that does let me see the newlib sources, but it’s not a FreeRTOS project. What I’m seeing there is that the stdio file handles get set up in findfp.c. I’m assuming that FreeRTOS is doing its thing properly and allocating and deallocating the TCB and stack and that it’s newlib that’s allocating memory, so it’s probably not strictly a FreeRTOS issue but I posted here because it looks like something that any FreeRTOS user using newlib would run into and there has to be a way to avoid this.

Scott

n1vg wrote on Sunday, November 04, 2018:

Hi Richard,

What’s the expected way for this to be dealt with? Is it an anomaly that newlib is allocating memory on its first buffered I/O call? Is there something I need to configure to avoid that? Or does FreeRTOS have exit hooks that the libc implementation would normally use to deallocate memory on shutdown?

Thanks,

Scott

n1vg wrote on Sunday, November 04, 2018:

I should also mention that I’ve got _REENT_SMALL enabled. It looks like Newlib doesn’t support global stdio streams with that option and most things are allocated on first use. I’m thinking that maybe that’s not the normal usage with FreeRTOS and that it expects the reent struct to be static and allocated as part of the TCB. I checked that my Newlib config matches Erich’s but it sounds like he doesn’t ever kill tasks so it’s possible that part wasn’t tested.

I see that prvDeleteTCB() calls _reclaim_reent() and that looks to me like it should be where it’d be freed again…

rtel wrote on Sunday, November 04, 2018:

V10.0.1 has the addition I was thinking of, as can be seen on line line 3687 of https://sourceforge.net/p/freertos/code/HEAD/tree/tags/V10.0.1/FreeRTOS/Source/tasks.c

rtel wrote on Sunday, November 04, 2018:

Personally, I just avoid using newlib, so don’t have much experience with it (as noted on the web page I linked to before). If it is allocating memory on a first call to some function or other then I presume it is best to leave the memory allocated as that is, presumably, what the library expects (some kind of singleton implementation where the memory is only allocated on the first call) - or are you seeing the memory not being freed on every task delete, so the leak increases over timer?

n1vg wrote on Monday, November 05, 2018:

(as noted on the web page I linked to before)

What web page is that? The only link you’ve posted in this thread goes to tasks.c. There is a comment around line 313 that mentions it being added by popular demand. Is that the one? Or did I miss a message? Your first post starts ‘in addition’ but I thought you were just starting by asking for clarification.

From what I can see, Newlib with _REENT_SMALL seems to have pointers to the file structures in the reent struct that are allocated on first use, so once the TCB is freed the pointers are lost. Every time the task is started the buffer is allocated anew and it runs out of memory after several iterations.

I think part of the reason I rebuilt Newlib in the first place was to reduce the per-task reent memory usage. The full size struct takes up a lot of RAM and is the main reason I’m reluctant to create any more tasks even where it would simplify other things. If you’ve got a preferred library setup that has a reliable malloc scheme that isn’t memory hungry, I’d be happy to stick with whatever is tested and proven.

Thanks,

Scott

rtel wrote on Monday, November 05, 2018:

All of my tasks save one are started once and are never terminated. The
one exception is a network update downloader that of course only needs
to run occasionally. It’s started, checks for updates, and ends - and it
takes a chunk of heap with it, even if no explicit dynamic allocation is
used in the task itself.

Is all the code yours? If not, are you sure the library you are using
is not allocating memory internally?

The leak is 1468 bytes

Sounds like the size of an Ethernet packet.

, or 436 if stdout buffering is disabled. Assuming

8 bytes of overhead per allocation, that matches perfectly with the size
of the reent structure and the default 1024 byte stdout buffer.

Ah, right.

Presumably Newlib expects the OS to clean up after it. Or is there some
way to tell it to free what it’s using?

How does one normally deal with this?

Do you have configUSE_NEWLIB_REENT define?

n1vg wrote on Monday, November 05, 2018:

Hi Richard,

For testing, I reduced the offending task to this:

static portTASK_FUNCTION(TaskUpdate, pvParameters)
{
	uint32_t f1 = (uint32_t)xPortGetFreeHeapSize();

	setvbuf(stdout, NULL, _IONBF, 0);

	printf("\rBefore: %lu After: %lu\r\n", f1, (uint32_t)xPortGetFreeHeapSize());
	update_taskhandle = NULL;
	vTaskDelete(NULL);
	return;
}

The output is:

cmd:update
Before: 9456 After: 9020
update
Before: 9020 After: 9020
update
Before: 9020 After: 8584
update
Before: 8584 After: 8148
update
Before: 8148 After: 7712

I was just working on a related project that’s not using FreeRTOS but has the same Newlib configuration and I’ve confirmed that it also takes 436 bytes after the first printf().

This doesn’t seem like a rare sort of thing to be doing and it’s not a small memory leak. What am I doing wrong?

And what’s your preferred C runtime? NXP keeps pushing Redlib but it’s not thread safe. Here’s NXP’s statement:

Redlib was always designed as a simple, Embedded C library. And at present we have no plans to make it thread safe - we have seen little or no demand for this (in the 10 years or so that we have been supplying Redlib).

Our recommendation if you want thread safe is to use Newlib (which you can switch your project to use very easily), wrap the Redlib functions yourself, or else provide equivalent functions yourself as source within your project (e.g. from other open source projects).

I think that was what got me started on this path in the first place - by NXP’s own admission, their solution isn’t safe with FreeRTOS. So what is? What does everyone else use?

Thanks,

Scott

rtel wrote on Monday, November 05, 2018:

My preference is to use the simplest library possible, then ensure
thread safety myself, as per the implementation of heap_3.c which makes
malloc() thread safe. I have found inclusion of newlib to massively
bloat the code size, especially if you can’t stop it bringing in all the
floating point support (which may not be required).

rtel wrote on Monday, November 05, 2018:

In this case could it be you are only supposed to call setvbuf() once, not each time the task is created?

n1vg wrote on Monday, November 05, 2018:

How would that work? setvbuf() is allocating memory referenced by pointers in the reent struct in the TCB - I don’t see how you could have it get them back when the task is recreated.

n1vg wrote on Monday, November 05, 2018:

When you say heap_3.c makes malloc() thread safe, do you mean it makes them safe for its own calls, or does it actually wrap the standard malloc() call so things like printf() can use it? I know that can be done with the linker but I didn’t think that was what heap_3 was doing, and I read that it doesn’t implement realloc(), which is used by at least some printf() implementations.

rtel wrote on Monday, November 05, 2018:

heap_3 just wraps the malloc calls with scheduler suspend/resume calls,
so calls to pvPortMalloc() which call malloc() cannot be interrupted.

n1vg wrote on Monday, November 05, 2018:

So that still leaves me in the position that two library functions using malloc() on their own can still interfere, right? How do you normally deal with that?

Hello to all!
I have the same issue with newlib and FreeRTOS 10.2.0. Symptoms are the same: network task which aren’t persistant and kill itself after job is done. In this task may happens printf. After every calling I have memory leak 1472 bytes. So, I’ve tried turn off any stdout printing and problem is gone. I stepping in debug mode and noticed, that printf calling _puts_r which calling __sfmoreglue().
So, have I implement some additional functions to free newlib memory on task destroy to prevent memory leak?

Yes. Or use an alternative printf version without heap usage like this one:

Note that other (very few) newlib functions might also dynamically allocate memory internally or do that by definition like strdup.