--- ~~Title: Debugging with GDB~~ --- # 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](https://www.gnu.org/software/gdb/documentation/), 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": ```c #define EFL_EO_API_SUPPORT 1 #define EFL_BETA_API_SUPPORT 1 #include #include #include 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: ```bash gcc -Wall -O1 -march=native -g -ggdb3 -o hello hello.c `pkg-config --cflags --libs elementary` ``` Then execute the program: ```bash ./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: ```gdb 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=, level=EINA_LOG_LEVEL_ERR, file=0x7ffff5ce0a5a "lib/eo/eo.c", fnc=0x7ffff5ce1f70 <__FUNCTION__.14772> "_efl_object_call_resolve", line=, 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=, ev=) at gdb.c:37 #7 0x00007ffff5cdeb57 in _event_callback_call (legacy_compare=0 '\000', event_info=, desc=0x7ffff6a78b80 <_EFL_LOOP_EVENT_ARGUMENTS>, pd=0x55555578bd50, obj_id=) at lib/eo/eo_base_class.c:1542 #8 _efl_object_event_callback_call (obj_id=, pd=0x55555578bd50, desc=0x7ffff6a78b80 <_EFL_LOOP_EVENT_ARGUMENTS>, event_info=) 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=) 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=) 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=, pd=) 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 (gdb) frame 6 #6 0x00005555555553d1 in efl_main (data=, ev=) 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: ```c #define EFL_EO_API_SUPPORT 1 #define EFL_BETA_API_SUPPORT 1 #include #include #include 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 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=, desc=, pd=0x555555b520c0, obj_id=) at lib/eo/eo_base_class.c:1542 #2 _efl_object_event_callback_legacy_call (obj_id=, pd=0x555555b520c0, desc=, event_info=) 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=, ed=0x555555b71800, source=0xaaaaaaaaaaaaaaab , sig=0x555555b52890 "", source_states=, signal_states=, matches=, ssp=0x555555b71800) at lib/edje/edje_match.c:556 #6 edje_match_callback_exec (ssp=ssp@entry=0x555555b71800, matches=, 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=) at lib/edje/edje_message_queue.c:260 #12 0x00007ffff683da6b in _ecore_job_event_handler (data=, type=, ev=) at lib/ecore/ecore_job.c:98 #13 0x00007ffff6839301 in _ecore_call_handler_cb (event=, type=, data=, func=) 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=, pd=) 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: * Go to frame zero, the callback function * Print the ``pi`` pointer * Print what it points to ```bash (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.