Dynamic Memory Allocation

I am using LVGL for a project and have become confused at how it uses dynamic memory allocation. My expectation would have been that if I allocate all my objects at the beginning, my memory usage should remain consistent and LVGL should not be allocating and freeing memory while the application is running. However, that does not seem to be the case. I updated to LVGL v7.9.0 yesterday and am using the demo widgets application from lv_examples. The code runs, I can swipe from screen to screen and everything appears to work fine until randomly some time later while trying to navigate it will freeze.

#0  vApplicationMallocFailedHook () at src/main.c:227
#1  0x600769d6 in pvPortMalloc (xWantedSize=1168) at freertos_kernel/portable/MemMang/heap_4.c:253
#2  0x6005f4fe in lv_mem_alloc (size=1152) at ./lvgl/src/lv_misc/lv_mem.c:193
#3  0x600a3182 in lv_mem_realloc (data_p=0x20036844 <ucHeap+49520>, new_size=1152) at ./lvgl/src/lv_misc/lv_mem.c:321
#4  0x6005f70e in _lv_mem_buf_get (size=1152) at ./lvgl/src/lv_misc/lv_mem.c:531
#5  0x600a9f48 in draw_shadow (coords=0x20033444 <ucHeap+36208>, clip=0x2002b290 <ucHeap+3004>, dsc=0x2002b210 <ucHeap+2876>) at ./lvgl/src/lv_draw/lv_draw_rect.c:616
#6  0x600a8d84 in lv_draw_rect (coords=0x20033444 <ucHeap+36208>, clip=0x2002b290 <ucHeap+3004>, dsc=0x2002b210 <ucHeap+2876>) at ./lvgl/src/lv_draw/lv_draw_rect.c:107
#7  0x6005b832 in lv_led_design (led=0x20033434 <ucHeap+36192>, clip_area=0x2002b290 <ucHeap+3004>, mode=0 '\000') at ./lvgl/src/lv_widgets/lv_led.c:220
#8  0x60095398 in lv_refr_obj (obj=0x20033434 <ucHeap+36192>, mask_ori_p=0x2002b2c0 <ucHeap+3052>) at ./lvgl/src/lv_core/lv_refr.c:699
#9  0x6009544e in lv_refr_obj (obj=0x20033294 <ucHeap+35776>, mask_ori_p=0x2002b300 <ucHeap+3116>) at ./lvgl/src/lv_core/lv_refr.c:739
#10 0x6009544e in lv_refr_obj (obj=0x2002e6bc <ucHeap+16360>, mask_ori_p=0x2002b340 <ucHeap+3180>) at ./lvgl/src/lv_core/lv_refr.c:739
#11 0x6009544e in lv_refr_obj (obj=0x2002e60c <ucHeap+16184>, mask_ori_p=0x2002b380 <ucHeap+3244>) at ./lvgl/src/lv_core/lv_refr.c:739
#12 0x6009544e in lv_refr_obj (obj=0x2002df74 <ucHeap+14496>, mask_ori_p=0x2002b3c0 <ucHeap+3308>) at ./lvgl/src/lv_core/lv_refr.c:739
#13 0x6009544e in lv_refr_obj (obj=0x2002dea4 <ucHeap+14288>, mask_ori_p=0x2002b400 <ucHeap+3372>) at ./lvgl/src/lv_core/lv_refr.c:739
#14 0x6009544e in lv_refr_obj (obj=0x2002de0c <ucHeap+14136>, mask_ori_p=0x2002b4d0 <ucHeap+3580>) at ./lvgl/src/lv_core/lv_refr.c:739
#15 0x6004a64c in lv_refr_obj_and_children (top_p=0x2002dd84 <ucHeap+14000>, mask_p=0x2002b4d0 <ucHeap+3580>) at ./lvgl/src/lv_core/lv_refr.c:656
#16 0x6004a5a4 in lv_refr_area_part (area_p=0x2002da3e <ucHeap+13162>) at ./lvgl/src/lv_core/lv_refr.c:572
#17 0x6004a378 in lv_refr_area (area_p=0x2002da3e <ucHeap+13162>) at ./lvgl/src/lv_core/lv_refr.c:482
#18 0x6004a152 in lv_refr_areas () at ./lvgl/src/lv_core/lv_refr.c:404
#19 0x60049d52 in _lv_disp_refr_task (task=0x2002db7c <ucHeap+13480>) at ./lvgl/src/lv_core/lv_refr.c:203
#20 0x6005efba in lv_task_exec (task=0x2002db7c <ucHeap+13480>) at ./lvgl/src/lv_misc/lv_task.c:409
#21 0x6005ec38 in lv_task_handler () at ./lvgl/src/lv_misc/lv_task.c:142
#22 0x60082584 in AppTask (param=0x0) at src/main.c:56
#23 0x60076cc8 in pxPortInitialiseStack (pxTopOfStack=0x0, pxCode=0xa5a5a5a5, pvParameters=0x60082585 <AppTask+28>) at freertos_kernel/portable/GCC/ARM_CM7/r0p1/port.c:210

As you can see from the stack trace, its trying to realloc memory while updating a screen and fails. I was really not expecting this from an embedded GUI. I thought maybe it was just this new LED widget, but after some poking around it looks like everything is doing this. Am I missing something here? Typically we would want to have confidence in the maximum memory usage of the system but with this I don’t see how that is possible.

System Info
MCU: NXP RT1021
Compiler: ARM GCC
LVGL: v7.9.0

Cheers,
Chris

I would need to know the exact screen size you are testing with to investigate this on a simulator, but here is my general knowledge of how things work.

_lv_mem_buf_get is generally used to support rendering fancy effects like shadows. If you are rendering a new screen where shadows are larger/the object is bigger, there may be a single reallocation call to grow the preallocated buffer to the required size. However, LVGL should not continue allocating more and more memory with a static set of objects being redrawn. If lv_demo_widgets is doing that, there might be a bug somewhere.

Is there something else which frequently allocates/frees dynamic memory in your system?

The screen size allocated is 320x240 and we defined LCD_VIRTUAL_BUF_SIZE = (LCD_WIDTH * LCD_HEIGHT / 10). I did some memory tracing with FreeRTOS. It seems that total is memory is only allocated once every iteration of each screen has been rendered. For example, I go to the text input screen, memory is stable, I open the keyboard, and memory drops temporarily. Then if I go to the dials screen and scroll down, its the same thing. Once I have exercised everything in the UI, memory consumption appears to be stable. I had disabled the LEDs in this test, so I can’t speak for that widget specifically, but based on my high watermark, the 1152 bytes it tried to allocate could have failed.