archer gdb macros for glib

The Archer project is working on  modernizing gdb. One aspec of this is support for scripting gdb using python. Today I landed some python macros for gdb that makes debugger integration with glib/gobject much nicer.

This is best shown by a “screencast” showing the new features. If you’ve ever debugged a glib program, read the below carefully. If not, it won’t make much sense to you, sorry.

(gdb) # Welcome to the glib macros demo
(gdb) # We support pretty printing of lists:
(gdb) l toplevel_list
296    static GtkKeyHash *gtk_window_get_key_hash        (GtkWindow   *window);
297    static void        gtk_window_free_key_hash       (GtkWindow   *window);
298    static void       gtk_window_on_composited_changed (GdkScreen *screen,
299                                 GtkWindow *window);
300
301    static GSList      *toplevel_list = NULL;
302    static guint        window_signals[LAST_SIGNAL] = { 0 };
303    static GList       *default_icon_list = NULL;
304    static gchar       *default_icon_name = NULL;
305    static guint        default_icon_serial = 0;
(gdb) p toplevel_list
$1 = 0x84d7218 = {0x842f468, 0x842f3b8, 0x842f308, 0x842f258, 0x842f1a8, 0x842f0f8,
 0x842f048, 0x8370710, 0x8370660, 0x83705b0, 0x8370500, 0x8370450, 0x83703a0, 
0x83702f0, 0x8370240, 0x8370190, 0x83700e0, 0x8370030, 0x8349088}
(gdb) # And hashtables:
(gdb) l g_quark_ht
88    G_LOCK_DEFINE_STATIC (g_dataset_global);
89    static GHashTable   *g_dataset_location_ht = NULL;
90    static GDataset     *g_dataset_cached = NULL; /* should this be
91                             threadspecific? */
92    G_LOCK_DEFINE_STATIC (g_quark_global);
93    static GHashTable   *g_quark_ht = NULL;
94    static gchar       **g_quarks = NULL;
95    static GQuark        g_quark_seq_id = 0;
96
97    /* --- functions --- */
(gdb) p g_quark_ht
$2 = 0x8220a30 = {
 [0x824db08 "draw-border"] = 0x97,
 [0x83bdc38 "<Actions>/IconViewActions/Sort by Size"] = 0x962,
 [0x3970905 "custom-success"] = 0x414,
 [0x83c0878 "<Actions>/DirViewActions/OpenFolderWindow"] = 0x8df,
 [0x2a9d6bb "tooltip-text"] = 0x107,
 [0x82c0648 "audio_%d"] = 0x50c,
 [0x83af728 "zoom_level_changed"] = 0x7e6,
 [0x83c1210 "Empty Trash"] = 0x8e6,
 [0x83584a0 "GdkPixmap"] = 0x76c,
 [0x83298d0 "soup"] = 0x61f,
 [0x82e4a58 "gio"] = 0x568,
 [0x83c2600 "<Actions>/DirViewActions/Create Link"] = 0x8f9,
 [0x835bca0 "MimeTypes"] = 0x6ed,
 [0x81c221d "NautilusIconInfo"] = 0x7bb,
 [0x2b2ff13 "gtk-file-chooser-backend"] = 0x84,
 [0x82edee8 "audio/x-shorten"] = 0x5a6,
 [0x82b9e08 "GstAllocTraceFlags"] = 0x498,
 [0x8307798 "videotestsrc"] = 0x5ee,
 [0x82cc6c0 "tcp"] = 0x529,
 [0x81a3dda "NautilusBookmarkList"] = 0x733,
 [0xc76e26 "dirname"] = 0x350,
 [0x2b1f004 "gtk-menu-bar-accel"] = 0x66,
 [0x8267da8 "GstObjectFlags"] = 0x461,
 [0x82bea88 "http://farsight.sf.net"] = 0x500,
 [0x835bbf8 "FileSystems"] = 0x6ec,
 [0x82b8bd0 "GstDebugColorFlags"] = 0x481,
 [0x81c564d "EggSMClient"] = 0x2b3,
 [0x83b6528 "band-select-ended"] = 0x84f,
 [0xc685fd "GFile"] = 0x318,
 [0x83bcb80 "row-reordered"] = 0x8ac,
 [0x8320628 "liveadder"] = 0x60f,
 [0x825c870 "drag_failed"] = 0x171,
 [0x825d8e0 "gnome_disable_sound_events"] = 0xd6,
 [0x2b4f420 "grab-focus"] = 0x129,
 [0x83c4c20 "<Actions>/DirViewActions/Self Format Volume"] = 0x91d,
 [0x2b3889d "x"] = 0x817,
 [0x2a92cc0 "y"] = 0x818,
 [0x2b2a535 "color-hash"] = 0x83,
 [0x2b3c0e6 "mask"] = 0x76f,
 [0x8343360 "<Actions>/ShellActions/Zoom Normal"] = 0x711,
 [0x83cd8b0 "<Actions>/IconViewActions/Tighter Layout"] = 0x956,
 [0x2b4f28a "gtk-pango-context"] = 0xea,
 [0x82ec370 "application/x-gzip"] = 0x58f,
---Type <return> to continue, or q <return> to quit---q
Quit
(gdb) # We also have a cool gforeach command:
(gdb) gforeach i in toplevel_list: print ((GtkWindow *)$i)
$3 = 0x842f468 [GtkWindow]
$4 = 0x842f3b8 [GtkWindow]
$5 = 0x842f308 [GtkWindow]
$6 = 0x842f258 [GtkWindow]
$7 = 0x842f1a8 [GtkWindow]
$8 = 0x842f0f8 [GtkWindow]
$9 = 0x842f048 [GtkWindow]
$10 = 0x8370710 [GtkWindow]
$11 = 0x8370660 [GtkWindow]
$12 = 0x83705b0 [GtkWindow]
$13 = 0x8370500 [GtkWindow]
$14 = 0x8370450 [GtkWindow]
$15 = 0x83703a0 [GtkWindow]
$16 = 0x83702f0 [GtkWindow]
$17 = 0x8370240 [GtkWindow]
$18 = 0x8370190 [GtkWindow]
$19 = 0x83700e0 [GtkWindow]
$20 = 0x8370030 [GtkWindow]
$21 = 0x8349088 [NautilusSpatialWindow]
(gdb) gforeach i in toplevel_list: print ((GtkWindow *)$i)->title
$22 = (gchar *) 0x0
$23 = (gchar *) 0x0
$24 = (gchar *) 0x0
$25 = (gchar *) 0x0
$26 = (gchar *) 0x0
$27 = (gchar *) 0x0
$28 = (gchar *) 0x0
$29 = (gchar *) 0x0
$30 = (gchar *) 0x0
$31 = (gchar *) 0x0
$32 = (gchar *) 0x0
$33 = (gchar *) 0x0
$34 = (gchar *) 0x0
$35 = (gchar *) 0x0
$36 = (gchar *) 0x0
$37 = (gchar *) 0x0
$38 = (gchar *) 0x0
$39 = (gchar *) 0x0
$40 = (gchar *) 0x83d0200 "alex"
(gdb) # There is also some nice GObject integration features:
(gdb) break gtk_widget_size_allocate
Breakpoint 1 at 0x29ff265: file gtkwidget.c, line 3821.
(gdb) c
Continuing.
[Thread 0xb71ffb70 (LWP 2599) exited]
Breakpoint 1, IA__gtk_widget_size_allocate (widget=0x8349088 [NautilusSpatialWindow], allocation=0xbfffe914) at gtkwidget.c:3821
3821    {
(gdb) # Notice the runtime detected type of the instance
(gdb) c
Continuing.
Breakpoint 1, IA__gtk_widget_size_allocate (widget=0x8313d38 [GtkTable], allocation=0xbfffe460) at gtkwidget.c:3821
3821    {
(gdb) c
Continuing.
Breakpoint 1, IA__gtk_widget_size_allocate (widget=0x8365c70 [GtkVBox], allocation=0xbfffe048) at gtkwidget.c:3821
3821    {
(gdb) c
Continuing.
Breakpoint 1, IA__gtk_widget_size_allocate (widget=0x8365d20 [GtkVBox], allocation=0xbfffdc08) at gtkwidget.c:3821
3821    {
(gdb) # This is gonna be a long hairy backtrace, right? NO!
(gdb) new-backtrace
#0  gtk_widget_size_allocate (widget=0x8365d20 [GtkVBox], allocation=0xbfffdc08) at gtkwidget.c:3821
#1  0x027dcb19 in gtk_box_size_allocate (widget=<value optimized out>, allocation=<value optimized out>) at gtkbox.c:500
#2  0x00a180ac in g_cclosure_marshal_VOID__BOXED (closure=0x8249ad0, return_value=0x0, n_param_values=2, param_values=
 0x8535e78, invocation_hint=0xbfffddc0, marshal_data=0x27dc850) at gmarshal.c:566
#3  <...>
#4  <...>
#5  <...>
#6  <...>
#7  <emit signal size-allocate on instance 0x8365c70 [GtkVBox]>
#8  0x029ff3da in gtk_widget_size_allocate (widget=<value optimized out>, allocation=<value optimized out>) at gtkwidget.c:3887
#9  0x0295b625 in gtk_table_size_allocate_pass2 (table=<value optimized out>) at gtktable.c:1610
#10 0x0295b625 in gtk_table_size_allocate (table=<value optimized out>) at gtktable.c:849
#11 0x00a180ac in g_cclosure_marshal_VOID__BOXED (closure=0x8249ad0, return_value=0x0, n_param_values=2, param_values=
 0x85956f0, invocation_hint=0xbfffe200, marshal_data=0x295abd0) at gmarshal.c:566
#12 <...>
#13 <...>
#14 <...>
#15 <...>
#16 <emit signal size-allocate on instance 0x8313d38 [GtkTable]>
#17 0x029ff3da in gtk_widget_size_allocate (widget=<value optimized out>, allocation=<value optimized out>) at gtkwidget.c:3887
#18 0x02a14b39 in gtk_window_size_allocate (widget=<value optimized out>, allocation=<value optimized out>) at gtkwindow.c:4941
#19 0x00a180ac in g_cclosure_marshal_VOID__BOXED (closure=0x8249ad0, return_value=0x0, n_param_values=2, param_values=
 0x859bc28, invocation_hint=0xbfffe610, marshal_data=0x2a149f0) at gmarshal.c:566
#20 <...>
#21 <...>
#22 <...>
#23 <...>
#24 <emit signal size-allocate on instance 0x8349088 [NautilusSpatialWindow]>
#25 0x029ff3da in gtk_widget_size_allocate (widget=<value optimized out>, allocation=<value optimized out>) at gtkwidget.c:3887
#26 0x02a15044 in gtk_window_move_resize (window=<value optimized out>) at gtkwindow.c:6186
#27 0x02a15044 in gtk_window_check_resize (window=<value optimized out>) at gtkwindow.c:5358
#28 0x00a17994 in g_cclosure_marshal_VOID__VOID (closure=0x8262b68, return_value=0x0, n_param_values=1, param_values=
 0x8535700, invocation_hint=0xbfffeae0, marshal_data=0x2a14b50) at gmarshal.c:77
#29 <...>
#30 <...>
#31 <...>
#32 <...>
#33 <emit signal check-resize on instance 0x8349088 [NautilusSpatialWindow]>
#34 0x028176ba in gtk_container_check_resize (container=<value optimized out>) at gtkcontainer.c:1424
#35 0x028179f2 in gtk_container_idle_sizer (data=<value optimized out>) at gtkcontainer.c:1350
#36 0x002c3588 in gdk_threads_dispatch (data=<value optimized out>) at gdk.c:506
#37 0x00929382 in g_idle_dispatch (source=0x8557f20, callback=0x8365d20, user_data=0x83548c0) at gmain.c:4065
#38 0x0092b198 in g_main_dispatch (context=<value optimized out>) at gmain.c:1960
#39 0x0092b198 in g_main_context_dispatch (context=<value optimized out>) at gmain.c:2513
#40 0x0092eac8 in g_main_context_iterate (context=0x8247f90, block=<value optimized out>, dispatch=1, self=
 0x821f018) at gmain.c:2591
#41 0x0092ef3f in g_main_loop_run (loop=0x8291758) at gmain.c:2799
#42 0x028b7129 in gtk_main () at gtkmain.c:1205
#43 0x0807e923 in g_themed_icon_append_name () at gthemedicon.c:378
#44 0x0071ab36 in __libc_start_main (main=0x807e2c0 <g_themed_icon_append_name+86084>, argc=1, ubp_av=0xbffff154, init=
 0x81a3180, fini=0x81a3170, rtld_fini=0x6efd00 <_dl_fini>, stack_end=0xbffff14c) at libc-start.c:220
#45 0x080692b1 in g_themed_icon_append_name () at gthemedicon.c:378

The above was recorded using a stock rawhide (to be Fedora 12) gdb and glib (including debuginfo packages), with just the python macros added. It also works with the gdb shipping in Fedora 11, but there is some issue there that makes the gforeach macro crash gdb. Additionally, the VTA work that landed in the GCC in Fedora 12 makes what gdb reports much more reliable wrt optimizations, which is very nice for e.g. the backtrace filtering.

GObject performance work

I spent some time last week and this week on fixing some performance issues in gobject. It started out with the patches in bug 557100, which seemed very useful. I cleaned up those patches a bit, wrote a serious performance test and did some additional optimizations.

These changes focus on speeding up creation of “simple” gobject, i.e. things that have no properties or implement any interfaces, etc. They are still important because being able to use gobject gives us lots of advantages like threadsafe refcounting, runtime type introspection, user-data, etc. Sometimes people avoid using gobjects for small things just because they are a bit more expensive than some homebrew struct, which is very sad. With these fixes we can get rid of some of that.

Another thing about gobject that has bothered me for some time is the handling of interfaces. GIO and other modern APIs are starting to use interfaces more and more, so its important that they work well. However, interfaces in gobject have a feature that most people are unaware of, namely that you can add interfaces to a class after the class/type has been initialized. This means that the list of interfaces a class implements must be protected by a lock, and this lock must be taken each time we e.g. check if an object implement an interface or cast to the interface to do a method call on it.

Additionally the interface lookup algorithm used in gobject uses a binary search on the sorted list of interfaces a class implements. Better approaches are possible, like the one used in gcj (described here) which allows constant time (O(1)) interface lookup.

In bug 594525 and 594650 I described these issues and posted patches that fix them.

I added all these patches to the gobject-performance branch in glib git, including the performance test I wrote. The performance improvements are pretty good:

  • Construction speed for simple objects more than doubled, while the construction speed for complex object is not much affected (within one percent).
  • Interface typechecks go from 52 to 95 million per second in the non-threaded case and from 12 to 95 if g_threads_init() has been called.
  • Additionally the contention for typechecks in multiple threads goes to zero as you can see in the tests does by benjamin in bug 594525.