Tuesday, 27 July 2010

Tracking QSharedPointer leaks

Smart pointers are a great thing. When used properly, they can really help make life easier, and simpler. But things can, and do, occasionally go wrong - and that is when the hurt comes in. Qt provides a number of smart pointer classes, some might say too many, but that's a topic for a whole different discussion, one of which is QSharedPointer.

From the documentation:
"The QSharedPointer class holds a strong reference to a shared pointer
The QSharedPointer is an automatic, shared pointer in C++. It behaves
exactly like a normal pointer for normal purposes, including respect
for constness.

QSharedPointer will delete the pointer it is holding when it goes out
of scope, provided no other QSharedPointer objects are referencing it.

A QSharedPointer object can be created from a normal pointer, another
QSharedPointer object or by promoting a QWeakPointer object to a strong
reference.

Essentially, QSharedPointer works through reference counting, which means if you somehow make a mistake with cleaning up your references, the object your shared pointer refers to won't be deleted, and you've got a hard to track memory leak on your hands. This is precisely what happened to me recently at work, amongst a jungle of a few different libraries, so tracing the problem by hand was really not going to happen, so I needed a miracle, or short of that, a reliable way to track reference count changes on a QSharedPointer instance.

Reading up on QSharedPointer's internals, it became obvious that the reference counting was stored in the dpointer of each QSharedPointer instance. The dpointer is shared amongst QSharedPointer instances referring to the same pointer. So, we should be able to set a watch in gdb to break whenever the refcount changes.

First, we need to find out the address of a QSharedPointer instance, so set a breakpoint just after we first create it:


 (gdb) break main.cpp:22
 Breakpoint 1 at 0x8048806: file main.cpp, line 22.
 (gdb) r
 Starting program: /home/burchr/qsharedpointer/qsharedpointer.
 [Thread debugging using libthread_db enabled]

 Breakpoint 1, main (argc=1, argv=0xbffff444) at main.cpp:22
 22>-    QSharedPointer<MyClass> copy(initial);
 (gdb) p initial
 $1 = {<QtSharedPointer::ExternalRefCount<MyClass>> = {<QtSharedPointer::Basic<MyClass>> = { value = 0x804c438}, d = 0x804c448}, <No data fields>}

Now we have the address, we can set a watch on the QBasicAtomicInt in the dpointer, we can watch the refcount for changes:
 (gdb) watch $1.d->weakref
 Hardware watchpoint 2: $1.d->weakref


Continue debugging, and gdb will break whenever the refcount changes, telling us the old and new values, like so:
 (gdb) c
 Continuing.
 Hardware watchpoint 2: $1.d->weakref

 Old value = {_q_value = 1}
 New value = {_q_value = 2}
 0x08048953 in QBasicAtomicInt::ref (this=0x804c44c)
     at /usr/include/QtCore/qatomic_i386.h:120
 120>                 : "memory");



Much thanks to:

2 comments:

  1. Nice article to know how a QSharedpointer works. Even good to know, how to use GDB debugging on pointers.

    A silly doubt, can Valgrind tool help you in this case.

    ReplyDelete
  2. Valgrind will tell you that a leak occurred, it will tell you what object was leaked. What it won't do is to tell you which QSharedPointer(s) to it were leaked, causing it to not be deleted.

    ReplyDelete