Tuesday, June 25, 2013

13 Linux Debuggers for C++ Reviewed

http://www.drdobbs.com/testing/13-linux-debuggers-for-c-reviewed/240156817



Have you compared debuggers lately? Until recently, I'd been programming using only one debugger — the one supplied by my compiler vendor. Suddenly, with a new job programming on Linux, I find the range of choices in debuggers is dizzying. Wikipedia lists 18 GUI front ends for GDB alone. This article is the result of my effort to choose a debugger with a good GUI front end for my first UNIX/Linux job in several years.

Scope

There are many tools that provide functionality related to debugging. Examples include memory-leak detectors, memory-corruption detectors, profilers, and gdb enhancers such as undoDB. And with multithreaded programming going mainstream, there are single-purpose tools to help diagnose deadlock and race conditions. Those tools, while valuable, are not part of this review — I'm solely focusing on debuggers, and specifically, the user-friendliness of the interfaces. The focus on the user experience is intentional and specific: I find that the vast majority of the time in the debugger involves two principal activities — stepping through code and examining variables. In the next section, I show a screen capture of each product and I comment on how well it provides these functions in addition to how well it enables me to move and dock tabs so that I can maximize my screen space.
In subsequent sections, I rate the products on other common features, such as setting breakpoints, customizing variable display, and so on. I also explain my thinking on what makes good implementations of some of these features now that I've seen them implemented across these 13 products, some standalone and some embedded in IDEs.

Products

Affinic Technology's Affinic 0.5.3 (recently updated to version 1.0) is a commercial GUI wrapper for GDB. It can be downloaded and tried out for free. A commercial license costs $49. Versions for Linux and Windows (via Cygwin) are available. Affinic uses single-button hotkeys and buttons for stepping through the target program. Its variable display is usable but minimal. Its tabbed docking windows would be good if they remembered their position from one debugging session to the next. The "variable display tooltip" is bare bones: Hovering the cursor over a variable displays as much of its value as will fit in the single status line at the bottom of the main window.
Afficnic
Figure 1: The Affinic debugger.
Code::Blocks 12.11 is a free, open-source product that uses a plugin model to add various capabilities, which I'll touch on later. It offers tabbed docking windows, but they won't dock together into user-selected groups. Its variable display understands STL vectors, but not STL strings. Its tooltips show variable values for only the simplest data types, and its program console window isn't part of its docking system.
Code::Blocks
Figure 2: Code::Blocks.
Codelite 5.1 is an open-source C++ IDE that runs on Windows, Linux, Mac OS X, and FreeBSD among other platforms. It has tabbed docking windows, but they resist efforts to form user-selected groups. Also, the product doesn't understand nested data structures like an array of vectors, and its Quick Debug mode doesn't remember breakpoints from one debug session to the next.
Codelite 5.1
Figure 3: Codelite 5.1.
DDD 3.3.12 is the GNU project's standard GUI front end to gdb and its other language debuggers. In many ways, it is a minimalist front end. It has single-button hotkeys for stepping through debugged program execution, but it lacks a toolbar with buttons for those commands. It displays nested data structures on a single line, which makes complex data items hard to understand. It sometimes gets stuck starting up, with an hourglass cursor that never changes back to normal.
Gnu DDD
Figure 4: Gnu DDD.
Eclipse is the Java IDE that, through continued development and its extensive ecosystem, has grown into an environment for development in many languages, including C and C++. It has resizable and detachable tabbed docking windows, excellent support for variable display, and an adequate code declaration/definition navigator. Its single button hotkeys and toolbar buttons for stepping through the debugged program are good. It also remembers breakpoints across debug sessions, but to find out how many times a breakpoint has been ignored, you have to issue the "info break" command in the gdb console window. (Note that while this example shows Eclipse using GDB, it supports many other compilers and debuggers. For example, cross-platform C/C++ development in Eclipse on Linux and Windows is also an option. —Ed.)
Eclipse (Juno release) front-ending gdb
Figure 5: Eclipse (Juno release) front-ending gdb.
GNU Emacs: As a long-time Emacs user, I wanted to like GNU emacs version 23.3.1's GDB mode. I like many of its features, but it's starting to show some age. It has multiple windows, some with tabs (to switch to other windows), but it doesn't support rearranging them. I like its display of single-level STL containers as arrays, but for the nested container: std::vector> it displays only the unhelpful {...}.
GNU Emacs' GDB mode
Figure 6: GNU Emacs' GDB mode.
KDevelop 4.3. is an open-source IDE for Linux, Solaris, FreeBSD, Max OS X, and other UNIX flavors. It has single-button hotkeys, but lacks toolbar buttons for stepping through the debugged program. Its simple variable and single-level STL container support is good, but displays that unhelpful abbreviation {...} for nested STL containers (see Figure 7). It does possess impressive menu of commands to navigate program symbols.
The KDevelop debugger
Figure 7: The KDevelop debugger.
Nemiver 0.9.1 "is an on-going effort to write a standalone graphical debugger that integrates well in the GNOME desktop environment," according to its creators. Predictably, it's open-source. It strives to maximize user friendliness in the debugger interface. It has single-button hotkeys and a toolbar with buttons for stepping through the debugged program. It pops up a convenient window for simple variables and arrays, but has no support for STL containers.
Nemiver
Figure 8: Nemiver.


Oracle's NetBeans 7.3, like Eclipse, started out as a Java IDE and added extensive capabilities for C and C++ programming. Like Eclipse, NetBeans is open-source and actively developed. The IDE offers single-button hotkeys and a toolbar with buttons for stepping through the debugged program. Its resizable detachable tabbed docking windows are excellent. NetBeans remembers breakpoints from one session to the next. Unfortunately, its tooltips for variables display STL container values on unformatted lines, but its Variables window has good navigation controls.
Oracle NetBeans
Figure 9: Oracle NetBeans.
Digia Qt Creator 2.4.1 has single-button hotkeys and buttons for stepping through the target program. Its variable value tooltips sometimes appear as black text on a black background, but moving the mouse pointer over the solid black tooltip corrects this. Its variable display works well for nested data structures, and it has a complete menu of commands to navigate program symbols.

Digia Qt Creator
Figure 10: Digia Qt Creator.
SlickEdit 17.0.3.0 is a commercial programming editor with some excellent user interface features. Single-seat licenses start at $299. SlickEdit was recently reviewed in Dr. Dobb's . Its program-stepping toolbar is the best I've seen — each button's tooltip not only tells you what the button does, but also how to do the same action with the keyboard. It has the docking windows I like, but its Watch window doesn't group with its other windows. It remembers breakpoints across debug sessions, but when multiple variable watch windows are open, it is painfully slow to start up (with my test program). SlickEdit's tech support tells me that problem will be fixed in the next release.
SlickEdit
Figure 11: SlickEdit.

Rogue Wave's TotalView 8.11.0-2 is a high-powered product with the best support for debugging multithreaded and multiprocessor code of the products listed here. It's pure debugging prowess is far ahead of mainstream products, including gdb. It's a commercial product, with single-seat licenses starting at $400. As to the UI, it has the single-keypress hotkeys and toolbar buttons I like. I hear from Rogue Wave that they'll add tabbed docking windows sometime in the next year, but for now, TotalView does not have them. While the UI has room for improvement, the product's superior engineering is apparent everywhere you look.
Rogue Wave TotalView
Figure 12: Rogue Wave TotalView.

Zerobugs 1.22.139 enables debugging for C, C++, and D. It sports a Gtkmm-based GUI, and includes a Python scripting framework. It usually requires you to build it from source. It wouldn't build for me but fortunately they offer a binary installer for 64 bit Ubuntu 11.10 so I tested it there. It doesn't understand STL containers or strings, but it does remember your breakpoints across debug session.
The Zerobugs UI
Figure 13: The Zerobugs UI.

This list is by no means complete. I could have included other lesser-known and/or more specialized debuggers and GDB interfaces, but I believe this list presents the best selection of debugging tools commonly used by C and C++ developers for non-embedded work.

Rating Basic Features

This section describes the most important qualities I use to pick a debugger for everyday use, focusing on the operations I do many times in every debug session. They include things like stepping through the program, interrupting program execution, and inspecting variable values. The debugger should make these important operations as easy as possible. Let's examine these in a bit more detail.
When stepping through a program, the four actions we typically perform most frequently are stepping over a function call, stepping into a function, returning from a function, and resuming program execution. Because they're so common, and because I prefer the keyboard over the mouse, my ideal debugger will have a single-button hotkey for each of these actions. The best hotkeys require only a single button press, such as F6 or "s." Combinations like Control-n are acceptable, but navigating through a menu by pressing something like Alt-R-n many times in every debug session is inconvenient. If you prefer the mouse, your ideal debugger will have a one-click toolbar button for each of these commands, such as the one in SlickEdit (Figure 11).
The most common ways to interrupt program execution are the breakpoint on a line, the conditional breakpoint (usually based on a variable value expression), and the breakpoint with a hit (sometimes called "ignore") count. Two other important ways to interrupt a running program are the watchpoint, which interrupts the program when a variable changes, and the catchpoint, which interrupts the program when it throws an exception. Three more useful features are abilities to temporarily disable a breakpoint, to find out how many times a breakpoint with an ignore count has been ignored, and to automatically restore breakpoints from a previous session. My ratings increase with the range of program interruption options.
There are five standard kinds of windows for inspecting variable values — tooltip, watch, locals, autos, and quick-watch. The quickest of these is the tooltip, which is a temporary window that displays a variable's value when the mouse hovers over the variable. It disappears when you press the escape key or move the mouse away. Good tooltip windows have data structure navigation controls as good as those in any of the other variable inspection window. And the best ones don't waste every other line of screen real estate with something like "(Struct)" or the variable type.
In a variable inspection window, you should be able to navigate complex variables. It's also the most convenient place to call an arbitrary function, so the window should support that use. A common example of that is a function that dumps the state of a complex or nested variable to the console or a file.
By default, every debugger will display a variable the way its programmer laid it out. For example, inspecting the values of a linked list could require you to manually navigate links until you reach the node you're interested in.
The debugger should automatically display variables in the standard library (such as STL containers) as if they were an array — one element per line. Other variables that internally manage memory, such as std::strings have a similar problem, because they're complex and nested; so by default, finding the data they contain requires even more manual navigation than linked lists do. When displaying variable values in any window (such as the tooltip, the watch window, or the local variable list), it's important that the debugger show the data that the programmer stored there, rather than any nested containing structure. This is a nonissue for most variables, but for more complex data structures like those mentioned here, it's very important.
When you've temporarily interrupted program execution, it's essential to be able to navigate up and down the call stack and see the program state. Usually, you're looking at the local variables for each stack frame. This is usually done with a clickable call stack window.


Everybody has "favorite" information they use while debugging. My favorites include variable values and the call stack. And when looking at a program statement, the ability to see as much of its surrounding context as possible can really aid productivity. For these reasons, a GUI and resizable and detachable tabbed docking windows are valuable.
In Table 1, I rate these features using a scale from 1 to 5, with 5 being best. Like all such assessments, they are subjective (but I expect that where I gave low rankings to features, most developers would agree those products need improvement to be comparable to the other listed products).

Product
Stepping
keyboard
Stepping
mouse
Breakpoints
Tooltips
Variable
Inspection
Views
Navigate
Call Stack
Tab Docking
Windows
Totals
Affinic
5
5
4
2
3
5
3
27
Code::Blocks
5
5
4
2
2
5
3
26
Codelite
4
5
4
1
2
5
3
24
DDD
5
5
4
2
2
5
2
25
Eclipse
5
5
4
4
4
5
5
32
Emacs
3
5
2
1
3
5
2
21
KDevelop
5
5
4
3
2
5
2
26
Nemiver
5
5
4
2
2
5
2
25
Netbeans
5
5
5
2
5
5
5
32
QTCreator
5
2
4
3
5
5
2
26
SlickEdit
5
5
5
3
3
5
4
30
Totalview
5
5
4
1
5
5
1
26
Zerobugs
5
3
5
1
3
5
1
23
Table 1: Ratings of basic features, where 5 is best.

Less Used Features

These features I discuss here support operations we perform less often, so while important in choosing a debugger, they are somewhat less important — to me, at least — than the features in the previous section.
There are two frequent actions I used as indicators for this section: toggle breakpoint and run to line. One button hotkeys are ideal for these, but two buttons combinations work well enough here.
Sometimes you step over a function call and then, having seen its return value, you wish you'd stepped in. For that situation, the ability to set the next statement to be executed back one or more lines by moving the instruction pointer saves the trouble of rerunning the program. Moving the instruction pointer forward to skip a program statement is another use of set-next-statement, though less common.
At other times, you might like to know how the program (or a function in it) would act if a variable had a different value. The ability to change the value of a variable can save you from having to edit, recompile, and perform the sometimes complex steps to duplicate your program's state. Every variable window should support this use.
Some programs implement data structures complex enough that the debugger's default variable value display isn't very helpful. Examples include hand-crafted linked lists and tagged unions. Certainly, you can click-click-click your way through a custom linked list, or look at a union's tag field to see which union member is active, but that extra effort can break the concentration you need to diagnose a really tough bug. For frequently used data, some programmers (including me) find it worthwhile to write a bit of custom code to do that work automatically. Of course, your debugger must support this kind of customization. These customizations must be in separate files, so changes don't have to be merged as new versions of the debugger are released. Also, these customizations are designed for data structures the debuggers couldn't possibly know about.
I don't often use the ability to load a core dump, but it's invaluable for those (thankfully) rare crashes that only seem to happen at customer sites.
One non-debugging feature I like in a debugger is the ability to click on a symbol and have the file and line with the declaration or definition appear. Ideally, the debugger should present you with a choice of declaration or definition.
Customizing the keyboard bindings is something I typically do not do, but some of my coworkers adamantly insist that this is an important feature. There are a few capabilities that, together, make this ability fully featured. In order of importance, they are binding debugger commands to new keys, importing/exporting a complete set of bindings, and emulating other debuggers' key bindings.
Table 2 summarizes how the various products scored on all of these less used features.
(5 is best)
Toggle
breakpoint
Run to
line
Set Next
Statement
Change
Variable Value
Custom
Variable Display
Load
Core Dump
Jump to
Declaration/
Definition
Easy build
and debug
Customize
Keyboard
Totals
Affinic
4
3
1
2
1
1
3
1
1
17
Code::Blocks
5
5
5
1
3
1
2
5
1
28
Codelite
5
5
5
1
4
5
5
5
1
36
DDD
4
1
1
1
1
5
3
1
1
18
Eclipse
5
5
5
5
5
5
4
4
4
42
Emacs
4
1
1
3
1
1
2
3
2
18
KDBG
5
5
5
1
1
5
1
1
3
27
KDevelop
4
5
5
1
3
5
1
1
3
28
Nemiver
5
5
5
4
1
5
1
1
1
28
Netbeans
5
5
1
5
5
5
5
5
5
41
QTCreator
5
5
5
5
1
4
5
5
4
39
SlickEdit
5
5
5
5
5
5
5
5
5
45
Totalview
5
5
5
5
5
5
1
1
1
33
Zerobugs
3
1
1
5
2
5
2
1
3
23
Table 2: Ratings of lesser-used features (5 is best).

Conclusion

None of the debuggers I tested was perfect. Not surprisingly, every product had some unexpected weakness that many of the others didn't share. However, most of the debuggers supported the bulk of the features I look for. The only debuggers with resizable and detachable tabbed docking windows are Eclipse, Netbeans, and SlickEdit. All are fine choices. For now, I'm using Eclipse. Its engineering isn't as good as TotalView's, but for my uses, it makes the best overall compromise between UI and engineering — it has both docking windows and good variable display.
TotalView gets a special mention for its excellent multithreaded and multiprocessor support, and when it gets tabbed docking windows, it'll be a top contender. Its multithread support is so far ahead of the field that if that were my principal need, I'd look no further.
I hope that if you're using other products, this comparison will provide you incentive to experiment and possibly find a tool that better serves your specific needs.

Test Platforms

At home, I tested on Canonical Ltd. Ubuntu 12.04 64-bit on VMware. At wor,k I tested on Ubuntu 10.04 64 bit on a quad-core AMD system. The Zerobugs binary is available for Ubuntu 11.10, so that's where I tested it.
Ubuntu 10.04 installs gdb version 7.1-1ubuntu2, which is sometimes extremely slow. Fortunately, recent versions correct that. I used gdb 7.5.1.
I installed the STL pretty printers for gdb. They require Python support, so if you compile gdb from source, you'll have to install python-dev and run gdb's pre-build configure script with the --with-python switch.

No comments:

Post a Comment

Post a Comment