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.
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.
Figure 10: Digia Qt Creator.
SlickEdit 126.96.36.199 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.
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.
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.
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.