Debugging with Valgrind

Valgrind is a collection of tools for tracking down memory-related issues, from memory leaks through to uninitialized variables. While more information on Valgrind is available on the Valgrind website, this document introduces its use specifically for debugging applications written with the Enlightenment Foundation Libraries (EFL).

Debugging a Memory Leak

In this example an Eina array is created in a callback function but it is never freed. This generates a memory leak. Begin by creating the following program:

#define EFL_EO_API_SUPPORT 1
#define EFL_BETA_API_SUPPORT 1
 
#include <Eina.h>
#include <Elementary.h>
#include <Efl_Ui.h>
 
static void
_quit(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
{
   Eina_Array *array;
   unsigned int i;
 
   // create a new array but don't free it
   // **LEAK**
   array = eina_array_new(100);
   eina_array_step_set(array, sizeof(*array), 20);
   for (i = 0; i < 20; i++) eina_array_push(array, strdup("hello"));
 
   /****To free array****/
   //while (eina_array_count(array))
   //free(eina_array_pop(array));
   //eina_array_free(array);
 
   // quit the mainloop
   efl_exit(0);
}
 
EAPI_MAIN void
efl_main(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED)
{
   Efl_Ui_Win *win;
   Efl_Ui_Box *box;
 
   // new window - new background
   win = efl_add(EFL_UI_WIN_CLASS, NULL,
                  efl_ui_win_type_set(efl_added, EFL_UI_WIN_BASIC),
                  efl_text_set(efl_added, "Hello World"),
                  efl_ui_win_autodel_set(efl_added, EINA_TRUE));
 
   // when the user clicks "close" on a window there is a request to delete
   efl_event_callback_add(win, EFL_UI_WIN_EVENT_DELETE_REQUEST, _quit, NULL);
 
   // add a box object - default is vertical.
   box = efl_add(EFL_UI_BOX_CLASS, win,
                 efl_content_set(win, efl_added),
                 // make the box horizontal
                 efl_ui_direction_set(efl_added, EFL_UI_DIR_HORIZONTAL));
 
   // add a quit button
   efl_add(EFL_UI_BUTTON_CLASS, box,
           efl_text_set(efl_added, "Leak"),
           efl_pack(box, efl_added),
           efl_event_callback_add(efl_added, EFL_UI_EVENT_CLICKED,
                                  _quit, efl_added));
}
EFL_MAIN()

Save the program as "hello.", compile it, and run it through Valgrind:

valgrind --leak-check=full --track-origins=yes ./hello
[...]
==10105== 312 (32 direct, 280 indirect) bytes in 1 blocks are definitely lost in loss record 1,362 of 1,417
==10105==    at 0x4C2CE5F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==10105==    by 0x4E4ED9C: eina_array_new (eina_array.c:280)
==10105==    by 0x109492: _quit (in /tmp/leaking)
==10105==    by 0x6D3C966: _event_callback_call (eo_base_class.c:1542)
==10105==    by 0x6D3C966: _efl_object_event_callback_legacy_call (eo_base_class.c:1615)
==10105==    by 0x6D3823D: efl_event_callback_legacy_call (eo_base_class.c:1618)
==10105==    by 0x6D3823D: efl_event_callback_legacy_call (eo_base_class.c:1618)
==10105==    by 0x6AD479A: edje_match_callback_exec_check_finals (edje_match.c:556)
==10105==    by 0x6AD479A: edje_match_callback_exec (edje_match.c:711)
==10105==    by 0x6ADBAA1: _edje_emit_cb (edje_program.c:1592)
==10105==    by 0x6ADBAA1: _edje_emit_handle (edje_program.c:1544)
==10105==    by 0x6AD62DE: _edje_message_queue_process.part.3 (edje_message_queue.c:893)
==10105==    by 0x6AD6498: _edje_message_queue_process (edje_message_queue.c:859)
==10105==    by 0x6AD6498: _edje_job (edje_message_queue.c:260)
==10105==    by 0x61A1A6A: _ecore_job_event_handler (ecore_job.c:98)
==10105==    by 0x619D300: _ecore_call_handler_cb (ecore_private.h:331)
==10105==    by 0x619D300: _ecore_event_call (ecore_events.c:629)
[...]

All the memory accesses pass through Valgrind, so it is able to produce a backtrace when an allocation is made and not freed.

NOTE: There can be other traces in the HEAD SUMMARY, but this example focuses on the memory leak within the application. As a result you may have to ignore things not relevant to that code.

This backtrace demonstrates that an allocation took place in the eina_array_new() function. This function is called from the _quit() function. You can go further up the backtrace, but remember that a callback function is called from Efl.Event so there is a good chance that the allocation is made in your specific callback function.