Debugging with GDB

The GNU Debugger (GDB) is an invaluable tool for tracking down the precise cause of a crash or other error. While more information on GDB is available on the GNU website, this document introduces its use specifically for debugging applications written with the Enlightenment Foundation Libraries (EFL).

NOTE: Always begin by running the problem application and walking through the steps needed to reproduce the crash. GDB is of most use when you have a reliable reproduction for your issue.

Debugging an Incorrect Object

The code below creates three Elementary objects: win, box and an anonymous button. It displays a window with a "Quit" button, which closes the window when it is clicked. Enter the following and save it as "hello.c":

#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)
{
   // 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.
   box = efl_add(EFL_UI_BOX_CLASS, win,
                 efl_content_set(win, efl_added),
                 // set a min size as we will break layout
                 efl_gfx_size_hint_min_set(efl_added, EINA_SIZE2D(100, 50)));
 
   // add a quit button
   efl_add(EFL_UI_BUTTON_CLASS, box,
           efl_text_set(efl_added, "Quit"),
           // **ERROR**
           efl_pack(win, efl_added),
           efl_event_callback_add(efl_added, EFL_UI_EVENT_CLICKED,
                                  _quit, efl_added));
}
EFL_MAIN()

Compile with the -g flag to enable debugging symbols:

gcc -Wall -O1 -march=native -g -ggdb3 -o hello hello.c `pkg-config --cflags --libs elementary`

Then execute the program:

./hello
ERR<12993>:eo lib/eo/eo.c:605 _efl_object_call_resolve() in ../src/lib/efl/interfaces/efl_pack.eo.c:6: func 'efl_pack' (1656) could not be resolved for class 'Efl.Ui.Win'.

This error is caused by the efl_pack() function being called on an incorrect object, win. The error log explains that the efl_pack is not in the Efl.Ui.Win API, and so that the error is coming from the application and not from EFL itself.

For a more complicated application, this basic trace is not enough to track down the precise location of the error. Fortunately, EFL provides a macro for backtraces: EINA_LOG_ABORT.

A message generated by the EINA_LOG_CRI() macro can automatically call abort() once a message of a given log level is printed. This is controlled by the environment variable EINA_LOG_ABORT, and the level to be considered critical by the environment variable EINA_LOG_ABORT_LEVEL. When enabled the program will stop at the first error which meets both of the two requirements.

Run GDB over the application with EINA_LOG_ABORT_LEVEL=4 and EINA_LOG_ABORT=1 set to receive the following backtrace:

EINA_LOG_ABORT_LEVEL=4 EINA_LOG_ABORT=1 gdb hello
(gdb) run
Starting program: /home/efl/test/hello
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".
[New Thread 0x7fffebf01700 (LWP 13092)]
[New Thread 0x7fffe94d9700 (LWP 13093)]
[New Thread 0x7fffe8cd8700 (LWP 13094)]
[New Thread 0x7fffe84d7700 (LWP 13095)]
[Thread 0x7fffe84d7700 (LWP 13095) exited]
[New Thread 0x7fffe84d7700 (LWP 13096)]
ERR<13088>:eo lib/eo/eo.c:605 _efl_object_call_resolve() in ../src/lib/efl/interfaces/efl_pack.eo.c:6: func 'efl_pack' (1656) could not be resolved for class 'Efl.Ui.Win'.
 
Thread 1 "debugging" received signal SIGABRT, Aborted.
0x00007ffff3e998a0 in raise () from /usr/lib/libc.so.6
(gdb) backtrace
#0  0x00007ffff3e998a0 in raise () from /usr/lib/libc.so.6
#1  0x00007ffff3e9af09 in abort () from /usr/lib/libc.so.6
#2  0x00007ffff739ee25 in eina_log_print_unlocked (domain=<optimized out>, level=EINA_LOG_LEVEL_ERR,
    file=0x7ffff5ce0a5a "lib/eo/eo.c", fnc=0x7ffff5ce1f70 <__FUNCTION__.14772> "_efl_object_call_resolve",
    line=<optimized out>, fmt=0x7ffff5ce1410 "in %s:%d: func '%s' (%d) could not be resolved for class '%s'.",
    args=0x7fffffffdea0) at lib/eina/eina_log.c:1420
#3  0x00007ffff73a0153 in eina_log_print (domain=29, level=level@entry=EINA_LOG_LEVEL_ERR,
    file=file@entry=0x7ffff5ce0a5a "lib/eo/eo.c",
    fnc=fnc@entry=0x7ffff5ce1f70 <__FUNCTION__.14772> "_efl_object_call_resolve", line=605,
    fmt=0x7ffff5ce1410 "in %s:%d: func '%s' (%d) could not be resolved for class '%s'.") at lib/eina/eina_log.c:2259
#4  0x00007ffff5cd3fd4 in _efl_object_call_resolve (eo_id=eo_id@entry=0x40000000d32a,
    func_name=func_name@entry=0x7ffff7651b73 "efl_pack", call=call@entry=0x7fffffffe050, cache=0x7ffff7fa4b60,
    file=file@entry=0x7ffff7651b18 "../src/lib/efl/interfaces/efl_pack.eo.c", line=line@entry=6) at lib/eo/eo.c:614
#5  0x00007ffff762e80e in efl_pack (obj=0x40000000d32a, subobj=0x40000002578b) at ../src/lib/efl/interfaces/efl_pack.eo.c:6
#6  0x00005555555553d1 in efl_main (data=<optimized out>, ev=<optimized out>) at gdb.c:37
#7  0x00007ffff5cdeb57 in _event_callback_call (legacy_compare=0 '\000', event_info=<optimized out>,
    desc=0x7ffff6a78b80 <_EFL_LOOP_EVENT_ARGUMENTS>, pd=0x55555578bd50, obj_id=<optimized out>) at lib/eo/eo_base_class.c:1542
#8  _efl_object_event_callback_call (obj_id=<optimized out>, pd=0x55555578bd50,
    desc=0x7ffff6a78b80 <_EFL_LOOP_EVENT_ARGUMENTS>, event_info=<optimized out>) at lib/eo/eo_base_class.c:1603
#9  0x00007ffff5cd90fe in efl_event_callback_call (obj=0x4000000006f7,
    desc=desc@entry=0x7ffff6a78b80 <_EFL_LOOP_EVENT_ARGUMENTS>, event_info=event_info@entry=0x7fffffffe230)
    at lib/eo/eo_base_class.c:1606
#10 0x00007ffff6842c85 in _efl_loop_arguments_send (data=0x5555557a4f40, ev=<optimized out>) at lib/ecore/ecore_main.c:3151
#11 0x00007ffff684f856 in _efl_loop_future_success (ev=ev@entry=0x7fffffffe2b0, pd=pd@entry=0x5555557a8660, value=0x0)
    at lib/ecore/efl_promise.c:113
#12 0x00007ffff685080f in _efl_loop_future_propagate (obj=0x400000008316, pd=0x5555557a8660) at lib/ecore/efl_promise.c:192
#13 0x00007ffff685088b in _efl_promise_propagate (obj=0x400000007f15, pd=0x5555557a68b0) at lib/ecore/efl_promise.c:580
#14 0x00007ffff6851640 in ecore_loop_promise_fulfill (obj=<optimized out>) at lib/ecore/efl_promise.c:593
#15 0x00007ffff68416fd in _ecore_main_loop_iterate_internal (once_only=once_only@entry=0) at lib/ecore/ecore_main.c:2313
#16 0x00007ffff6841e77 in ecore_main_loop_begin () at lib/ecore/ecore_main.c:1313
#17 0x00007ffff6841eb9 in _efl_loop_begin (obj=<optimized out>, pd=<optimized out>) at lib/ecore/ecore_main.c:2889
#18 0x00007ffff683ea9e in efl_loop_begin (obj=0x4000000006f7) at lib/ecore/efl_loop.eo.c:44
#19 0x00005555555554a6 in main (argc=1, argv=0x7fffffffe4f8) at hello.c:44

The efl_pack() function is in frame five, so is called from frame six.

Go to frame six:

(gdb) frame 6
#6  0x00005555555553d1 in efl_main (data=<optimized out>, ev=<optimized out>) at gdb.c:37
37         efl_add(EFL_UI_BUTTON_CLASS, box, // win instead of box

This frame shows that the button constructor which begins on line 37 (at gdb.c:37) is responsible for the problem. Looking at the source code for the application, walk down from line 37 until you find the line in which win is used instead of box - which is line 40. This is the line which contains the error, and which must be corrected for the program to function correctly.

Debugging a Callback Function

To demonstrate the debugging of a callback function, create 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)
{
   int *pi;
 
   pi = (int *)0; // pointer to 0x0!!!
   /**SEGFAULT**/
   printf("Pi = %d\n", *pi); // segfault accessing 0x0 address
 
   // 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.
   box = efl_add(EFL_UI_BOX_CLASS, win,
                 efl_content_set(win, efl_added));
 
   // add a quit button
   efl_add(EFL_UI_BUTTON_CLASS, box,
           efl_text_set(efl_added, "Crash"),
           efl_pack(box, efl_added),
           efl_event_callback_add(efl_added, EFL_UI_EVENT_CLICKED,
                                  _quit, efl_added));
}
EFL_MAIN()

Save the program as "helloworld.c", compile it with the -g flag to include debugging symbols, and run it. The program will quit with a segmentation fault (segfault) error when the "Crash" button is clicked; in other words, during the callback function.

To debug this, load the program into GDB and run it:

gdb helloworld
(gdb) run
[...]
Thread 1 "debugging2" received signal SIGSEGV, Segmentation fault.
_quit (data=0x400000025528, event=0x7fffffffdfe0) at gdb2.c:15
15         printf("Pi = %d\n", *pi); // segfault accessing 0x0 address
(gdb) backtrace
#0  _quit (data=0x400000025528, event=0x7fffffffdfe0) at helloworld.c:15
#1  0x00007ffff5cdd967 in _event_callback_call (legacy_compare=1 '\001', event_info=<optimized out>, desc=<optimized out>,
    pd=0x555555b520c0, obj_id=<optimized out>) at lib/eo/eo_base_class.c:1542
#2  _efl_object_event_callback_legacy_call (obj_id=<optimized out>, pd=0x555555b520c0, desc=<optimized out>,
    event_info=<optimized out>) at lib/eo/eo_base_class.c:1615
#3  0x00007ffff5cd923e in efl_event_callback_legacy_call (obj=0x400000025528, desc=0x7ffff78625b0 <_EFL_UI_EVENT_CLICKED>,
    event_info=0x0) at lib/eo/eo_base_class.c:1618
#4  0x00007ffff5cd923e in efl_event_callback_legacy_call (obj=0x400000025528, desc=0x7ffff78625b0 <_EFL_UI_EVENT_CLICKED>,
    event_info=0x0) at lib/eo/eo_base_class.c:1618
#5  0x00007ffff5f8879b in edje_match_callback_exec_check_finals (prop=<optimized out>, ed=0x555555b71800,
    source=0xaaaaaaaaaaaaaaab <error: Cannot access memory at address 0xaaaaaaaaaaaaaaab>, sig=0x555555b52890 "",
    source_states=<optimized out>, signal_states=<optimized out>, matches=<optimized out>, ssp=0x555555b71800)
    at lib/edje/edje_match.c:556
#6  edje_match_callback_exec (ssp=ssp@entry=0x555555b71800, matches=<optimized out>,
    sig=sig@entry=0x555555b4263c "elm,action,click", source=source@entry=0x555555a42d90 "elm", ed=ed@entry=0x555555b52890,
    prop=prop@entry=0 '\000') at lib/edje/edje_match.c:711
#7  0x00007ffff5f8faa2 in _edje_emit_cb (prop=0 '\000', data=0x0, src=0x555555a42d90 "elm",
    sig=0x555555b4263c "elm,action,click", ed=0x555555b52890) at lib/edje/edje_program.c:1592
#8  _edje_emit_handle (ed=0x555555b52890, sig=0x555555b4263c "elm,action,click", src=0x555555a42d90 "elm", sdata=0x0,
    prop=0 '\000') at lib/edje/edje_program.c:1544
#9  0x00007ffff5f8a2df in _edje_message_queue_process () at lib/edje/edje_message_queue.c:893
#10 0x00007ffff5f8a499 in _edje_message_queue_process () at lib/edje/edje_message_queue.c:859
#11 _edje_job (data=<optimized out>) at lib/edje/edje_message_queue.c:260
#12 0x00007ffff683da6b in _ecore_job_event_handler (data=<optimized out>, type=<optimized out>, ev=<optimized out>)
    at lib/ecore/ecore_job.c:98
#13 0x00007ffff6839301 in _ecore_call_handler_cb (event=<optimized out>, type=<optimized out>, data=<optimized out>,
    func=<optimized out>) at lib/ecore/ecore_private.h:331
#14 _ecore_event_call () at lib/ecore/ecore_events.c:629
#15 0x00007ffff6841a88 in _ecore_main_loop_iterate_internal (once_only=once_only@entry=0) at lib/ecore/ecore_main.c:2438
#16 0x00007ffff6841e77 in ecore_main_loop_begin () at lib/ecore/ecore_main.c:1313
#17 0x00007ffff6841eb9 in _efl_loop_begin (obj=<optimized out>, pd=<optimized out>) at lib/ecore/ecore_main.c:2889
#18 0x00007ffff683ea9e in efl_loop_begin (obj=0x400000000494) at lib/ecore/efl_loop.eo.c:44
#19 0x000055555555549c in main (argc=1, argv=0x7fffffffe528) at helloworld.c:47

The program failed in the callback function, meaning the problem comes from the program itself and not the libraries - even if the callback is called from Efl.Event.

To look at the problem more deeply:

(gdb) frame 0
#0  _quit (data=0x400000025528, event=0x7fffffffdfe0) at gdb2.c:15
15         printf("Pi = %d\n", *pi); // segfault accessing 0x0 address
(gdb) print pi
$1 (int *) 0x0
(gdb) print *pi
Cannot access memory at address 0x0

This demonstrates the problem: address 0x0 cannot be accessed. Using these techniques, even if the callback functions are called from Efl.Event and so generate lots of traces, it is quite simple to track down these types of bug.