I’ve been developing and debugging desktop and mobile applications on embedded devices over the last decade or so. The main part of this period I’ve been focused on the multimedia side of the WebKit ports using GStreamer, an area that is a mix of C (glib, GObject and GStreamer) and C++ (WebKit).
Over these years I’ve had to work on ARM embedded devices (mobile phones, set-top-boxes, Raspberry Pi using buildroot) where most of the environment aids and tools we take for granted on a regular x86 Linux desktop just aren’t available. In these situations you have to be imaginative and find your own way to get the work done and debug the issues you find in along the way.
I’ve been writing down the most interesting tricks I’ve found in this journey and I’m sharing them with you in a series of 7 blog posts, one per week. Most of them aren’t mine, and the ones I learnt in the begining of my career can even seem a bit naive, but I find them worth to share anyway. I hope you find them as useful as I do.
Breakpoints with command
You can break on a place, run some command and continue execution. Useful to get logs:
break getenv command # This disables scroll continue messages # and supresses output silent set pagination off p (char*)$r0 continue end break grl-xml-factory.c:2720 if (data != 0) command call grl_source_get_id(data->source) # $ is the last value in the history, the result of # the previous call call grl_media_set_source (send_item->media, $) call grl_media_serialize_extended (send_item->media, GRL_MEDIA_SERIALIZE_FULL) continue end
This idea can be combined with watchpoints and applied to trace reference counting in GObjects and know from which places the refcount is increased and decreased.
Force execution of an if branch
Just wait until the if chooses a branch and then jump to the other one:
6 if (i > 3) { (gdb) next 7 printf("%d > 3\n", i); (gdb) break 9 (gdb) jump 9 9 printf("%d <= 3\n", i); (gdb) next 5 <= 3
Debug glib warnings
If you get a warning message like this:
W/GLib-GObject(18414): g_object_unref: assertion `G_IS_OBJECT (object)' failed
the functions involved are: g_return_if_fail_warning()
, which calls to g_log()
. It’s good to set a breakpoint in any of the two:
break g_log
Another method is to export G_DEBUG=fatal_criticals
, which will convert all the criticals in crashes, which will stop the debugger.
Debug GObjects
If you want to inspect the contents of a GObjects that you have in a reference…
(gdb) print web_settings $1 = (WebKitWebSettings *) 0x7fffffffd020
you can dereference it…
(gdb) print *web_settings $2 = {parent_instance = {g_type_instance = {g_class = 0x18}, ref_count = 0, qdata = 0x0}, priv = 0x0}
even if it’s an untyped gpointer…
(gdb) print user_data (void *) 0x7fffffffd020 (gdb) print *((WebKitWebSettings *)(user_data)) {parent_instance = {g_type_instance = {g_class = 0x18}, ref_count = 0, qdata = 0x0}, priv = 0x0}
To find the type, you can use GType:
(gdb) call (char*)g_type_name( ((GTypeInstance*)0x70d1b038)->g_class->g_type ) $86 = 0x2d7e14 "GstOMXH264Dec-omxh264dec"
Instantiate C++ object from gdb
(gdb) call malloc(sizeof(std::string)) $1 = (void *) 0x91a6a0 (gdb) call ((std::string*)0x91a6a0)->basic_string() (gdb) call ((std::string*)0x91a6a0)->assign("Hello, World") $2 = (std::basic_string<char, std::char_traits<char>, std::allocator<char> > &) @0x91a6a0: {static npos = <optimized out>, _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, _M_p = 0x91a6f8 "Hello, World"}} (gdb) call SomeFunctionThatTakesAConstStringRef(*(const std::string*)0x91a6a0)