Tuesday, December 11, 2007

Memory-efficient KDE 4 debugging

The GNU Debugger (gdb) is the standard tool for debugging applications on Linux. Unfortunately starting a KDE 4 application using the gdb debugger as it comes "out of the box" takes a long time (over a minute on my laptop) and uses vast amounts of memory (> 500MB) due to the time required to load the debugging 'symbols' (class names, method names etc.). This a problem especially if you have built Qt, kdelibs and other important libraries with debugging information. If you are running on a machine with less than 1GB RAM (eg. my 512MB laptop used for development) then your system is likely to slow to a grinding halt for a while.

As I discovered talking to other KDE hackers at FOSSCamp 2007, this means that some people never use the debugger at all, relying solely on debugging messages printed by the program as it runs.

One solution to this is to only load debugging information for the code which you are interested in debugging. I wrote an email on the KDE developers list about how to do that here, which was written up more clearly by Constantin here (his blog does not appear to be syndicated on PlanetKDE, so hopefully this reaches a wider audience). One important thing to bear in mind is that you can only ask gdb to set breakpoints (ie. stop execution in) functions for which debugging information has been loaded.

Manually asking gdb to load the 3-6 libraries you need to debug a given problem can be quite a hassle. What I do is to define a few functions in my ~/.gdbinit file to load commonly used subsets of libraries. In order to debug most problems, you need the core Qt,KDE and C libraries loaded plus your application code. These all load relatively quickly and won't use too much memory, so it is usually useful to load them all together. If you need to examine the state of Qt widgets or other GUI-related things then you will need to load the QtGui library. This takes a few seconds and uses a fair amount of memory so it should be avoided otherwise.

Add the following to your ~/.gdbinit file,

def load-common-kde-libs
shar libc
shar glib
shar QtCore
shar kdecore
end

def load-gui-kde-libs
shar QtGui
shar kdeui
end

Then when debugging a KDE application, start the application with set auto-solib-add off (I put this command inside ~/.gdbinit as well, see the linked blog most and email above) and then interrupt it using Ctrl-C. Run load-common-kde-libs and then load any libraries specific to your application, usually shar <appname> will catch them. In many cases, this will be enough information to get useful backtraces (using bt) and examine the state of the application. If when you run the bt command the backtrace includes calls to functions inside the QtGui,kdeui or other libraries near the top before the calls to functions in your application's code then you will need to load those as well and then re-run bt in order to find out where in your code the problem is.

As mentioned on the KDE TechBase page, there is a script in SVN (trunk/KDE/kdesdk/scripts/kde-devel-gdb) which includes really useful gdb functions for debugging KDE applications, such as printq4string (prints the contents of QString objects) and identifyq4object (prints the class name of an object which inherits from QObject).

Other KDE debugging tricks

  • Stepping through an application which was built with compile-time optimization enabled (the default) can produce some really weird results because during compilation, the structure of the code may be altered and variables or function calls can be removed ('optimised out') to improve performance. Optimizations can be disabled by passing -DCMAKE_BUILD_TYPE=Debug to cmake when setting up the build. The resulting programs will run more slowly, depending on how much of the Qt/KDE library stack is built without optimisations (in my case, everything from Qt and up is).

  • Some applications in KDE (eg. dolphin, konsole) are single-instance, which means that there is only ever one process for that program. If you start a second copy of that program then it contacts the first, asks it to create a 'new instance' (usually this means a new window) and then immediately quits. Applications which are single-instance support the --nofork argument to prevent them from creating a new process on startup. You can find out whether an application supports this by looking at the output of appname --help-kde. If you are debugging such an application, you need to run it with the --nofork argument. In gdb you can do this by executing set args --nofork before running the program.

  • Some KDE components (eg. plasma) have their own crash handlers to trigger an automatic restart or bring up a specialized bug reporting tool (eg. amarok) in case of a crash. These custom crash handlers interfere with normal debugging, and they can be disabled by passing the --nocrashhandler argument on startup (like the above --nofork).

Over the Christmas period I hope to find time to write this up on the KDE TechBase page. Please try out the above and reply to this post with any problems/comments/queries so I can include the answers when I get around to it.

9 comments:

paines said...

Hi

You just reminded me of a time when I used to print debug messages to stderr or a file instead of now using a debugger. This especially sucked when I ported a game to a mobile device (gp2x). So I had to start my game, till it carshed, take the sd card out, mount it, read the debug log and fix the bug, and start all over again. But !!! I must say, I was more efficent with that method then now using gdb. Maybe I should go back to the old method.

Have you tried attaching gdb to your already runnig process ?

Happy coding my friend !
regards
atreyu

Anonymous said...

Zero bugs (http://www.zero-bugs.com/) is pretty nice too if you want to test an alternative to gdb...

Anonymous said...

I'm risking to get flamed to death, but you can always switch to a properly done c++ debugger - MSVC express, free as in beer.

Unknown said...

> Have you tried attaching gdb to your
> already runnig process ?

The tips in this post apply whether you start the program from within gdb or if you attach to a running process.

> Zero bugs (http://www.zero-bugs.com/)
> is pretty nice too

It would be nice to have a shiny new debugger with a clean architecture and less cruft from years of development, but it isn't libre software which puts it out of the running for recommended KDE 4 development tools.

> I'm risking to get flamed to
> death, but you can always switch
> to a properly done c++ debugger

I'd be interested to know how well MSVC's debugger copes with projects involving large shared libraries. From the information I can find on the web, both MSVC and Intel's debuggers can suffer from slow startup due to the overhead in loading symbol information from shared libraries.

It is not really surprising though, the libraries total hundreds of megabytes in size on disk.

Unknown said...
This comment has been removed by the author.
Unknown said...

It works in KDevelop too !
Just create you ~/.gdbinit as explained before ( with set auto-solib-add off ), and load the needed shared library symbols by entering commands in the GDB console of KDevelop.
Even when not loading any library symbols, gdb still has the executable symbols which may be enough.

www.sevilla-3d.com said...

Wow, there is so much effective info above!

Viagra said...

Thank you for showing us how to debug this!

laxmicynixit said...

nice information ................!
ui path training