Debugging tips

refdbg & valgrind

Information about valgrind, reference counting and GObject specific testing you can find here.

gdb & ddd

Printing the content of a by an interface hidden type

When testing using gdb, most of the types you encounter will be interfaces. This isn't a real problem except that if you print the content of the pointer, you won't get a lot of information. Simply casting it to the implementation type, however, will help you a lot.

For example:

Breakpoint 1, on_header_view_tree_selection_changed (selection=0x83ab218, user_data=0x83ab218) at tny-summary-view.c:321
321                             folder = tny_header_iface_get_folder (header);
(gdb) next
322                             if (G_LIKELY (folder))
(gdb) print *folder
$1 = <incomplete type>
(gdb) print *(TnyCamelFolder*)folder
$2 = {parent = {g_type_instance = {g_class = 0x8225308}, ref_count = 4, qdata = 0x0}}
(gdb)

Looking at the private data of an instance

Once in the implementation code (of for example libtinymail-camel in this case), you can look at the contents of the priv variable.

For example:

#2  0xb7254e9a in tny_folder_get_message (self=0x825fea0, header=0x83ab218) at tny-folder.c:621
621             id = tny_header_iface_get_uid (TNY_HEADER (header));
(gdb) print *priv
$12 = {loaded = 1, headers_list_type = 0, folder_changed_id = 83, folder_lock = 0x834a5a0, folder = 0x8358268, folder_name = 0x8336d18 "INBOX",
  account = 0x80c2600, folders_lock = 0x8327a58, folders = 0x81d95b0, cached_length = 920, unread_length = 0, subscribed = 1,
  has_summary_cap = 1, cached_name = 0x8336d28 "Inbox", cached_folder_type = TNY_FOLDER_TYPE_INBOX}
(gdb)

Other nasty gdb thingies

Let's go a little bit deeper. The trick here is that gdb isn't good at things like this:

void myfunction (void *ptr) {
    MyType *me = ptr;
}

If you will ask for what is inside of the "me" variable, it might not help you a lot. There are various reasons why it can't. Among them is probably code optimization. You simply look at the ptr variable and you cast it to MyType. In this example "me" is at the top of the function set like this: me = self. The difference or reason here is that "me" is the type that the developer wanted to work with whereas self is the interface type the API exposes.

static const gchar*
tny_header_get_uid (TnyHeader *self)
{
	TnyCamelHeader *me = TNY_CAMEL_HEADER (self);
	const gchar *retval;

	if (G_UNLIKELY (!me->info) || G_UNLIKELY (me->write))
		return invalid;

	retval = camel_message_info_uid ((CamelMessageInfo*)me->info);

	return retval;
}

So when we debug this method:

(gdb)
tny_header_get_uid (self=0x83ab200) at tny-header.c:438

438             if (G_UNLIKELY (!me->info) || G_UNLIKELY (me->write))
(gdb)
441             retval = camel_message_info_uid ((CamelMessageInfo*)me->info);
(gdb)
tny_header_get_uid (self=0x83ab200) at tny-header.c:438

438             if (G_UNLIKELY (!me->info) || G_UNLIKELY (me->write))
(gdb)
441             retval = camel_message_info_uid ((CamelMessageInfo*)me->info);
(gdb) print *me
No symbol "me" in current context.
(gdb) print self
$13 = (TnyHeader *) 0x83ab200
(gdb) print *self
$14 = <incomplete type>
(gdb) print *(TnyCamelHeader*)self
$15 = {parent = {g_type_instance = {g_class = 0x83a0e70}, ref_count = 2, qdata = 0x0}, info = 0x83965c8, folder = 0x825fea0, write = 0 '\0'}
(gdb)

Note that the reason why the TnyCamelHeader type doesn't have a priv pointer, is because of performance and memory reasons. Imagine 20,000 headers all needing a second priv allocation. That is really going to slowdown the application when both allocation and deallocation happen AND it will cause a huge amount of memory fragmentation AND it will need an extra pointer which is four bytes in length on most devices.

So it was decided that this type would be an opaque type without a private extra allocation. Therefore is the TnyCamelHeader type probably the most difficult to debug in the entire tinymail framework. If you understand this, you are ready to become a code-warrior of the tinymail framework. Have fun.

Oh, in case you wonder: the ref_count of two means that one reference count is caused by the as TnyList implemented instance that has become parent of the instance. And the other reference count is the as TnyHeaderView implemented instance showing the header. Because you know, on_header_view_tree_selection_changed the application will set the just-selected header instance as header of a by TnyHeaderView implemented instance: the thing that shows you the header. This should make sense, or not?

Getting the "priv" variable in gdb

For example like this:

(gdb) print g_type_instance_get_private (((TnyGtkHeaderListModel*)self)->folder, tny_camel_folder_get_type())
$15 = 136657848
(gdb) print *(TnyCamelFolderPriv*)$15
$16 = {loaded = 1, headers_list_type = 0, folder_changed_id = 132, headers_managed = 1898, folder_lock = 0x826af80, folder = 0x81f7868, 
  folder_name = 0x826afb0 "INBOX/30000", account = 0x80e58f8, store = 0x80e5a08, cached_length = 1898, unread_length = 49, unread_sync = 0, 
  local_size = 258652, subscribed = 1, has_summary_cap = 1, iter = 0x81f6800, iter_parented = 1, cached_name = 0x826afc0 "30000", 
  cached_folder_type = TNY_FOLDER_TYPE_NORMAL, remove_strat = 0x820a8b0, receive_strat = 0x820a6a0, observers = 0x8259fb0, 
  sobservers = 0x8259fc8, self = 0x8253ba8, want_changes = 1, dont_fkill = 0, parent = 0x81f3ee0}
(gdb)