Sauraen

Monitoring memory use

15 posts in this topic

Hi TK.,

Since my project MIDIbox Quad Genesis uses dynamic memory allocation in C, I need to know at all times what percentage of the heap is in use. The umm_info function in umm_malloc.c gives this information, but it takes a couple hundred milliseconds to iterate through the heap (and send a ton of debug information to the terminal), and this causes the synth to freeze for this amount of time which is unacceptable.

So instead I modified umm_malloc and added a variable, umm_numusedblocks, which gets updated every time umm_malloc, umm_free, and umm_realloc are called. It just keeps track of the changes in the number of blocks allocated, it doesn't traverse the whole heap. (The additional overhead is a couple of adds and 4 bytes of memory.)

After some debugging, this system works in my synth. I created some test cases to make sure the block count increases and decreases correctly in the cases of malloc and free, and also in the cases of realloc to a larger and a smaller size.

In the process of working on this, I discovered that heap blocks are actually 12 bytes, not 8 bytes like the comments say. This is because (the size of the block is defined in umm_malloc.c at line 564) the union 'header' is 4 bytes and the union 'body' ends up being 8 bytes because portBYTE_ALIGNMENT is 8 bytes. Now, obviously this system is working for all MIOS32 projects, but it doesn't look quite right to me. In fact it looks like someone changed the size of data from 4 (or maybe sizeof(umm_ptr) or whatever) to portBYTE_ALIGNMENT, thinking this would cause all addresses returned by malloc to be aligned to 8 bytes. There's three reasons this isn't true:

  • Obviously, this makes the blocks 12 bytes, which means every other one starts on an 8-byte boundary; so on average half the returned addresses won't be 8-byte-aligned.
  • Even if this wasn't the case, the block has 4 bytes of header data, so if the block was aligned to 8 bytes, the returned pointer would be aligned to 4 bytes.
  • Besides all that, there's no guarantee the heap itself is aligned to 8 bytes. In fact when I first defined umm_numusedblocks, I made it unsigned short int (i.e. u16), because this is how all the blocks are addressed in this library. It turns out that this caused the heap itself to get aligned to a 2-byte boundary, which caused mallocs to fail and the synth to go up in flames. Now it's an unsigned int (i.e. u32), so the heap is aligned to 4 bytes, but I can't guarantee it'll be aligned to 8 bytes.

Moreover, I don't know of anything in ARM32 which would need to be aligned to anything larger than 4 bytes. And in fact, we know there isn't, because the current system isn't aligning half of its contents!

Therefore I tried changing data[portBYTE_ALIGNMENT] to data[4], and it seems to work fine. My synth runs fine, and in fact uses 0.1% less memory this way due to the smaller block size. :) And I also compiled MIDIbox SEQ V4 with this modified umm_malloc.c, and it starts up and appears to run just fine.

So I would like to commit my modifications, so that I can continue using this code in my synth (and maybe other people can benefit from the RAM usage tracking too). But I wanted to get your approval before modifying a critical part of MIOS32 for everybody. Would you like me to commit it somewhere else and you can test it first? Or do you just want me to commit it normally, and you'll test it in place, and if there's an issue you can roll back the files with SVN?

Thanks,
Sauraen

Share this post


Link to post
Share on other sites

Since then, I copied and modified the heap code from umm_malloc to set up a second heap in the 64k of previously-unused RAM (see this topic).

Both heaps work correctly with a block size of 8 and with their respective allocation counter variables. This new heap's code is in modules/vgm/vgm_heap2.c . The new heap will seamlessly start allocating data in the normal heap when it runs out of space.

I would suggest this code be usable for other projects, but there's a major drawback of this second heap: the RAM it's stored in is inaccessible to DMA operations. Which means that you have to know whether any memory you allocate at any given time will be filled via DMA (e.g. reads from SD card, etc.). So for now this second heap will only be accessible to MIDIbox Quad Genesis.

Share this post


Link to post
Share on other sites

Hi Sauaen,

Interesting find!

Yes, please commit the changes, so that I can try them at my side.
The change should be documented in http://svnmios.midibox.org/filedetails.php?repname=svn.mios32&path=%2Ftrunk%2FFreeRTOS%2FREADME_1st.txt so that it can be reproduced on FreeRTOS updates.

While I'm writing this, I noticed that we are using a 3 years old FreeRTOS version.

I will update soon. Very likely the malloc handling has been improved meanwhile, so that the change becomes obsolete. But please commit anyhow so that in case of issues we are able to switch back.

Best Regards, Thorsten.

 

 

Share this post


Link to post
Share on other sites

It's committed, have fun! I made brief notes of the changes in the file you requested, plus there's a decent amount of comments on the changes in umm_malloc itself.

Share this post


Link to post
Share on other sites

Update: I migrated to the latest FreeRTOS V9.0.0

Instead of umm_malloc we are using heap_4 now - seems to work fine for my apps, please doublecheck at your side

Best Regards, Thorsten.

Share this post


Link to post
Share on other sites

Thanks for letting me know. I haven't been actively working on this project recently, but when I get back into it I will see what changes I need to make. I may just end up staying with the modified umm_malloc.

Share this post


Link to post
Share on other sites

I finally got around to converting my code for MIDIbox Quad Genesis / VGM module to use the up-to-date heap_4, which was not hard because it already has a system for monitoring the memory usage. However, now when I tried to build (after "make clean" of course) I'm getting the linker error "undefined reference to '_sbrk'", which the internet says has to do with some memory management libraries not having been compiled. My SVN is all synced and up-to-date, and midibox_seq_v4 compiles fine. I tried editing my Makefile to look more like the midibox_seq_v4 one, and there wasn't much to change (I of course left out modules which shouldn't matter), though I tried adding in the "freertos_utils" module but that didn't help. I also tried getting rid of the part of my synth's code which directly interfaces with the heap stuff, and that didn't fix it either. Don't know what else to do--my MIDIbox Quad Genesis project is just a src folder and a Makefile, so there's not much place for there to be something wrong.

Share this post


Link to post
Share on other sites

Two things to point out which might lead to solutions:

When I compile anything (even midibox_seq_v4, which succeeds), I get a new warning along the way "warning: stack usage computation not supported for this target". Thought I'd mention it since it's memory-related, but that's stack and not heap so it probably isn't relevant.

The other thing I could think of is that MIDIbox Quad Genesis uses some of the STM32F4's hardware timers, so MIOS32_DONT_USE_TIMER is set in mios32_config.h. In case one of these timers is being used by the OS, maybe that's what's going on. (It's Timers 2, 3, and 5, and of course it worked fine with the old FreeRTOS, so unless it uses new timers there should be no issue.) Worth mentioning, commenting out MIOS32_DONT_USE_TIMER results in a "multiple definition of TIM3_IRQHandler" as expected, but it also does not get rid of the "undefined reference to '_sbrk'" error.

Disabling my entire secondary heap (in the 64 kB of non-DMA RAM) and all calls from MIDIbox Quad Genesis / VGM Module code to the OS's memory management stuff also did not solve the problem.

Just checked again that SVN is up-to-date, and all my stuff is committed, so you should be able to go into apps/synthesizers/midibox_quad_genesis and try making it if you want to see.

Edited by Sauraen

Share this post


Link to post
Share on other sites

Found the problem: it's realloc. I created a copy of the C template and it compiled fine. I added the lines

u8* a = malloc(16);
a = realloc(a, 32);

to app.c in APP_Init() and it fails with the "undefined reference to '_sbrk'" linker error.

I use realloc a couple times in the VGM module, and it's also used by my vgm_heap2 implementation of the secondary heap in that extra 64k of RAM, in so far as that heap provides its own realloc function to go with its own malloc and free, and it calls the OS's realloc function if the pointer you passed was in the main heap rather than the secondary heap.

While I could get rid of these all without too much trouble, I'm not sure why a major embedded platform like MIDIbox would choose a memory management scheme (heap_4) which does not support realloc, especially if it used to support this (with umm_heap). I could write an extension to heap_4 which provides realloc--it's not a terribly complex system (it's simpler than umm_malloc)--but for the sake of quality control it might be better to use a real-world-proven solution (and I don't exactly know how to tell the linker that whatever "pvPortRealloc" I write should be called whenever "realloc" is called).

Share this post


Link to post
Share on other sites

I wrote a pvPortRealloc function for heap_4.c and linked it up the way the other OS memory management functions are, and now the synth compiles and runs fine. But it's barely using the realloc functionality as is, so I'm going to write a little interactive test program for the synth and see if I can stress-test it and find any bugs. Once I do I'll commit it and you can give it a try. Of course it won't affect any current projects, because none of them use realloc, but of course it is important to have correct for the future.

Share this post


Link to post
Share on other sites

I tested my pvPortRealloc, and after fixing the bugs and testing what I believe to be one instance of every case of resizing a block, I committed the implementation. It changed heap_4.c, portable.h (to provide a definition), and freertos_heap.cpp (to make realloc point to this function). I documented these changes as you requested in README_1st.txt. It should not have any effect on any existing projects, as (evidently) none of them use realloc, and I didn't change any other code in heap_4.c (besides adding an additional printed parameter to your vPortMallocDebugInfo function). MIDIbox SEQ V4 compiles and appears to run fine on this new version. Feel free to test my implementation as you see fit.

Edited by Sauraen

Share this post


Link to post
Share on other sites

For a few days I've been unable to build using MSYS. I get a compile error of "no rule to make target" with reference to /MemMang/umm_malloc.h

Even if I go to a previous subversion the error persists. Any ideas?

Best,
Andy

Share this post


Link to post
Share on other sites

Clearly I need to learn Tortoise better, I can check out to a previous working rev and my builds work again. Still, there's something not right with the current one.

Share this post


Link to post
Share on other sites

There is no umm_malloc.c or umm_malloc.h anymore. My best guess is that some Makefile somewhere said "compile everything in this folder" and since umm_malloc was deleted from SVN at the server side but the server won't delete local files which are unversioned, your old copy of umm_malloc is still hanging around and it's trying to compile that.

Which project are you trying to build?

Share this post


Link to post
Share on other sites

Hi, thanks for the tip!

I did a fresh checkout and everything compiles okay now.

Thanks again,
Andy

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now