How Would You Improve Today's Debugging Tools?
redelvis asks: "I recently came across an article by MIT's Media Lab on 'The Debugging Scandal and What to Do About It'. It's a few years old now, but it really got me thinking about how little the debugging process has improved over the last 5,10 or even 30 years. I have developed applications using modern IDE debuggers such as Borland's JBuilder, Microsoft's Visual C++, as well as standard tools like gdb and jdb. Despite the slick graphical interfaces, nice thread stack traces and local variable browsers, I still make sure I have on hand plenty of notepads, graph paper, pens and pencils so I can try to build up a picture of what state the program is in and to help me play detective in pinpointing what is going wrong with my (or other peoples) programs. Do other developers have similar problems? Do they find modern IDEs and debuggers have shortcomings in helping track down bugs? What would make a better debugger? Why do you think so much effort been invested in areas such as advanced modelling tools but so little in improving debugging tools?"
Why do you think so much effort been invested in areas such as advanced modelling tools but so little in improving debugging tools?
Easy. As anyone who's ever tried to use software knows, nobody uses debugging tools anyway.
-JDF
Dont debug.
Eventually someone else will find the bugs and fix them for you at their own expense.
This is how the Open Source programming model works.
I don't need no instructions to know how to rock!!!!
But I'd like to be able to create 'conditional' breakpoints (i.e.: stop at this line when i==47).
Other than that, I'd like generalized hot-swapping of code and 'step back' (which I don't know if it's always factible); or at least be able to view a variable's history.
Shame that my IDE of choice (Eclipse) doesn't (AFAIK) support those.
Doesn't it make you feel good to know that our freedoms are protected by politicans, lawyers and journalists.
One of my biggest problems with people on my current project is the lack of knowledge of how to use even the most basic tools. We program in C++ on Unix and many developers find calling up dbx to be a chore and, even when they get dbx up, have trouble using it. We have many other tools, ddd, other graphical debuggers but people just don't use them, relying on printfs and couts...
main(i){(10-putchar(((25208>>3*(i+=3))&7)+(i ?i-4?100:65:10)))?main(i-4):i;}
I'm a total amateur, but I personally find that software tools can ultimately only go so far in debugging. The two most important debugging tools I'm aware of can't be solved with software: a short break from the project to clear your train of thought, and another set of eyes which might better see what you've overlooked.
----------
Something cleverAs much as everybody here likes to make fun of Visual Basic, I encourage all to have a look at VB's debugger. While there are a few improvements here and there that could be made, in general it far, far surpasses anything else that I have seen out there. (I have yet to play with VB.net--I am referring here to VB6).
Strategically embedded print statements - the time-honored way to find bugs.
That plus printing out and actually READING the code. Amazing what you can find by walking out of range of the screen and keyboard and just reading through the stuff.
I don't know if it's my greatest failing or greatest success as a developer, but I tend to be a println debugger all the way.
Recently I remember seeing some justification/defenses of this technique, I think it's sometimes called "logging based debugging".
When something goes wrong I figure out the related assumptions I'm making, and then test 'em by output to STDOUT or STDERR or a file. It avoids some of the "Heizenbug" problems debuggers can bring on (so long as dumping output can be reasonably expected to be reliable w/o bringing on crossproblems) and by thinking of your assumptions, you're tying into principles similar to "write tests first", part of XP practices.
One set of things this doesn't handle well is memory leaks and bottlenecks and looking out for runaway object creation and the like, but garden variety debuggers usually don't cover that too well either, you need special profiling tools anyway.
SO YOU'RE GOING TO DIE: The Comic for Dealing with Death
a) it is hard to make money on debuggers
b) making a good debugger is definately not easy
c) many developers prefer puzzling out the code on pen and paper then walking it in a debugger. Quite a few I have talked to believe that they make better code if they can solve the problem without resorting to debuggers. This BTW is why Linus does not like kernel debuggers.
d) most of the debuggers are good enough and as we all know from open source, a good enough solution will live forever. The existence of a debugger that mostly works gives most people a reason to spend their time elsewhere.
In my experience I have truly benefited more from programs like Purify or Insure (and now the great valgrind) than I have from a debugger.
Visual Basic supports a "watch" that allows you to break on condition such as when a variable changes at all or if a variable is set to a certain value. This is beautiful when trying to figure out how the currency value you just had disappeared. Add the watch saying when the currency = 0 and run the app so it breaks at ANY MODULE at ANY TIME.
This saves me time by not having to go into each module and watch the code to see if the value changes.
As for wanted features ( so as to not be totally off topic ) I really can't think of any that I would need per se. Since my favorite language is VB ( watch the flames poor in on that statement ) I have many tools for debugging that are perfect.
From the immediate window so I can run small snippits of code in break mode to the stack and watch windows.
The best way to learn how to write bug-free code is to NOT use a debugger at all. Every programmer should begin writing code in Notepad or some other basic, no-frills text editor. Just like everyone should learn how to type on a typewriter and not a computer. It forces you to NOT make mistakes, instead of correcting them (you don't have to "debug" a program if you don't put the bugs in in the first place!) It forces you to slow down and think about what you're doing, instead of blasting through a procedure and then run the debugger on it. If everyone programmed for at least a year without ever using a debugger, the quality of their code will improve far greater than any fancy IDE debugger will improve it.
A man's reach must exceed his grasp, or what's an erection for?
mahlen
The real problem is that the majority of software that is written is fairly lousy. People often code to moving requirements, push code in directions it was never intended to go in, and work with designs that are in desperate need of landing on the compost heap.
Honest debugging is most useful when something is being developed in the first place. Domain knowledge is what is most useful when ancient and decrepit software is being modified to do something it shouldn't.
Software applications should be thought of as prarie lands. From time to time, lightning strikes, and burns out a huge chunk, which is all replaced by newer and healthier stuff. You need talented people to make sure the new code will work well with what's already there - but the end result is that the application will be healthier if frequent refactoring is done intelligently.
Education is the silver bullet.
Debuggers aren't flashy or glamorous. I don't know anybody that thinks debuggers are cool. The tools and pieces of a development environment that are going to catch a programmers eye first are what's focused on most. You look at a nicely developed IDE that's polished and slick, and you'll be impressed. It's eye candy that sells products more than anything else.
Sadly, most -programmers- even look at a development environment's debugger as an afterthought. IMHO, it's one of the single most important tools (if not THE most important) and it's what I check out first.
Have you ever used the Gnu Visual Debugger at the GNAT Libre software developers' site? It's a multi-platform (Linux/Unix/Windows), open-source debugger with a "different view" of the world. Check it out, you may be pleasantly suprised.
1) The ability to set conditional breakpoints.
2) The ability to see not only a variable's current value, but a stack of all of its previous values.
3) The ability to select a variable's previous value and jump to the line of code that set it to that.
4) The ability to change the value of a variable at any point.
5) The ability to add/change code on the fly.
6) The ability to jump into the debugger at any point in the program, even when I hadn't planned to before running it.
7) Auto-logging of method calls and (optionally) variable values, to be started and stopped as I see fit either while stepping through code or running it.
That's all that comes to mind off the top of my head.
"You cannot simultaneously prevent and prepare for war." -- Albert Einstein
... about Debugging Tools. He felt that engineers should just learn to write software without bugs in it.
Debugging is difficult primarily because debuggers do not appear to use any kind of intuition.
Let's say I have a break point on an if statement. The debugger should automatically show me the values of any variables in the condition, as well as the truth values of the individual expressions in that condition.
A debugger should have an "intelligent" step button that steps into code that has debugging symbols, but steps over system calls. It should also automatically skip to the end of loops if desired.
Debuggers should have "sanity" checks for values. Any time a pointer is null, or an long integer is set to some really wild value (like -3492883773642) that is nowhere close to the values that it originally contained, or a string doesn't have a null terminator, the debugger should alert you with a little icon, even if you aren't actively watching that variable.
One thing that sucks about debugging is when programs behave differently in the debugging environment than they do once compiled. We had several problems related to event handling in Visual Basic only occured if the program had been compiled. In debug mode, it worked fine.
Debuggers should keep a "state" history, especially for loops or recursive functions, so you can see the entire progression of a set of variables until the point of failure. This eliminates having to write in a bunch of printf statements to get a snapshot of each loop iteration or function call.
The debugger should be integrated into the IDE so you don't have to switch back and forth. Also, it would be really nice if debuggers executed under some kind of virtual machine that allowed you to more easily freeze execution, rewind it, tinker with it as it's running, etc. Visual Basic allows you to do some of this, but certain code changes require you to restart the whole program. I'd like it if you never had to restart, and the line between "debug mode" and "design mode" were almost entirely blurred.
Just my $0.02.
bytesmythe
Hypocrisy is the resin that holds the plywood of society together.
-- Scott Meyer
Sure, we all like debuggers with conditional breakpoints and backward stepping, but the more you add to the debugger the less opportunity you will of finding the bug (unless its a 'smack in the face' bug).
The only way of truely solving bugs is to know exactly whats going on in the code (and, also as important, is knowing the language, operating system, and the things going on in the background. IE - in Java, although it appears as if you aren't dealing with pointers, you are, and you should treat all objects like a reference, cause that's what they are). If you can't study the code (cause it was written poorly), it is a terrible terrible thing, because most bugs are not just one line fixes (and even those that are require you to know exactly what all is going on around them).
"Cherry picking" (just tuning up one line and "guessing" that that's the problem) is what most 'amateurs that taught themselves coding' do, and on major/enourmously large projects, this will do nothing more than cause trouble.
The best way to beat a bug is knowledge.
Good quote, too many chars. Seriously, the slashdot 120 char limit sucks!
A debugger is going to help you find and fix the bugs that got through:
Studies have shown repeatedly that the cost of fixing each bug increases at each stage. So if you are going to invest $1000 in new development tools, is it more economical to buy tools to detect bugs when they're cheap, or when they're expensive?
The more work goes in before you get to the debugging stage, the less buggy your product will be. A well-designed system does not need as much debugging later. I know I'm idealizing, but I've seen this bear out, especially when version N > 1 comes out and it's been hacked to smithereens because of poor design choices in the previous iteration.
Probably the most amazing bit of self-fulfilling prophecy you'll ever hear on a software project is a manager saying, "We'd better start coding now, because we're going to have a lot of debugging to do."
As for catching things ahead of time, I've always put breadcrumbs in my code to spew to stderr or a Java error console class or wherever. Very easy to #ifdef out later and trivial to turn back on later.
This is not my sandwich.
You can implement reversibility on a per-thread basis (though that's harder than it sounds, in my experience). For multiple threads, it's basically impossible.
It's much easier to implement back-stepping in a processor simulator or virtual machine than it is in a native-code debugger. Unless you single-step through all the instructions, keeping snapshots of all the relevant state along the way, you can't go back.
And if you do single-step, the performance will likely be so poor that you might as well use a simulator.
-Mark
Clippy!
If you mod me down the terrorists will have won
How about instead of not using any debugging tools, you debug at write time?
For a lot of code, the compilier should be able to work out the pre and post conditions of a function, and then just check that when you call that function you don't violate them.
Also I'd like to be able to hover my mouse over a variable and see what range of values it could have. From here you can check that you never go outside the bounds of a variable, and so on.
Obviously you can never get it to work on all cases due to the halting problem, but it will work with a lot of code - and on confusing code you can manually add the special comments for the post and pre conditions - a good idea anyway for complex code.
I just need the "why" command..
=)
But I only use debuggers for two purposes.
Purpose 1. Segmentation Fault (core dumped). Uhm, now where did that happen? Whip out gdb, find the line that generated SIGSEGV, and it's usually obvious how it happened. If not, I have it print out a stack backtrace. If I really can't figure it out then, a 5 minute walk around the block and I'll have figured it out as soon as I sit back down.
When writing in high level languages, I'm finding that debuggers are wholly unnecessary. In fact, I can't remember the last time I spent more than 20 minutes trying to track down a bug. *shrug*
It'd be nice if debuggers solved my problems automatically, but I'm really finding that I don't need them. I might even go so far as saying use of debuggers encourages dependency on debuggers, which in turn discourages thinking about the program itself. Not saying that EVERYONE does this, just that some of the best work gets done remarkably well even without debuggers.
The Linux kernel, for example, was largely developed without the aid of a debugger, and the core developers seem to eschew them. Here's a good thread on why the developers don't want to include a debugger.
Purpose 2. On the other hand, debuggers are remarkably good at helping you break program code. With having almost no experience using gdb, I was able to break the license key check on Intel's C Compiler in about an hour. I was amazed at how easy it was to attach a debugger to the compiler and skip the subroutine that performed the license key check. With no debugging symbols to work with. Disassemblers rule. It took another 10 minutes to turn this into a script that could be distributed as a wrapper for icc (called xicc), so all you had to do was set CC=xicc in the Makefile.
Sure I could have used LD_PRELOAD so that time() always returned a date within the trial period, but breaking program code with a debugger is just so gosh darn fun.
Even front end tools are little help. DDD is much better than the cli but it still suffers the same performance faults as GDB and introduces some fun issues of its own. Even a 'commercial' tool such as Project Builder on Mac OS X groans to a halt with GDB running underneath and has some very odd concepts concerning when breakpoints are hit or not.
All this would be understandable if Win32 were the same but it isn't. Debugging on Win32 is a snap - just in time debugging, compile in time debugging, a debugger that works, integration with the editor and more. It is sad to say that if asked and irrespective of open / closed source issues, which OS were the better for development I would say Windows. In fact, if I'm faced with a bug in Mozilla, I'd rather fire up Windows and replicate the problem there than wade through the shit that GDB throws in my face.
Die GDB! Linux really needs a decent debugger and all the mod cons that Windows developers have benefited from in the past decade.
Recently I was given some Java code and asked to port it to C++. At the time, my knowledge of Java was pretty much at the "Where is the bathroom, please?" level. In particular, I could see where the reflection package was being used, but not how the program went from one particular bit of client code to another. I loaded the code into the debugger, started the program, set a few breakpoints at the really puzzling parts, and ran through it a few times. At that point, the student achieved enlightenment.
... and become more likely to run the darned thing and see what it really does. Sometimes, in this job, inspection is theory, and debugging and testing are experimentation.
As I've gotten more skilled at software development, I've become better able to read a bunch of code and analyze it
An MIT animal physiologist is said to have told his students, "The animal is always right. When in doubt, ask the animal." Ask the code; the code is always right. (It may be right in knowing what the bug is, but sometimes that's what you need to ask.)
P.S.: I've learned more Java since then. Running some programs through its debugger was one good way to help learn it.
Stupid job ads, weird spam, occasional insight at
If you don't have another programmer handy, try the practice of Rubber Ducking.
This is where when you are completely stuck, you take a little break. Then you come back, pull a rubber duck out of your drawer, and put it on your desk. Then you turn to it and say, "Rubber duck, here's my problem," and explain your troubles to it, just as you would to a fellow programmer.
About two thirds of the time I try this, I stop half way through and say, "Aha! That's the problem!" And even if the solution doesn't occur to me, the process of explaining the problem makes me go over it in an orderly way, so that I always think of new places to look.
I agree, Totalview is a really impressive program. When you have a bug that only manifests itself on 16 or 32 (or more) processors, print don't help as much. Stepping through code with totalview can really help figure out what is going on.
If you've ever followed the popular system computer simulators known as emulators, you've likely seen savestates. It's effectively saving all the variables of a system en bulk, to be leaded back again later so another approach might be taken. They have been very useful to emulator developers who often need to return to an exact point where a virtual machine breaks down, without having to re-run to a certain point.
It would be nice if debuggers could automate the process of saving the state of functions (and everything they immediately touch), so you could go "back in time" rather than need to restart a program in order to return to a non-bugged state. On small programs, it would also be very convenient to be able to save the state of the entire program.
Possible extentions of this idea would be a breakpoint-like auto-savestate at a certain line, and a "step back" feature to be able to rollback one line.
Of course, there would be many possible complications involved in using savestates - but I believe the potential dangers are far outweighed by the opportunities for quicker and more comprehensive testing and code-observation.
Ryan Fenton
I used to work for a company called Saber, who later changed their name to Centerline. We produced a product called Saber-C (and later Saber-C++, and the products were renamed CodeCenter and ObjectCenter).
This product was a C/C++ superdebugger. It started out as a C interpreter developed at Harvard. Now, an interpretive environment allowed a lot of neat features: You could tell whether or not variables had been initialized at reference time, you had complete stack and context information, it was pretty easy to unload and reload code in source modules (dynamic reloading). You also got dynamic type checking, which caught errors in downcasting (for instance). And everyone's favorite, array bounds checking.
With this tool debugging C code often meant: Loading the code, running your test case, and seeing where the debugger complained. The first time you ran your code through it was a humbling experience ... almost all production code has serious (but normally benign) errors.
Unfortunately the tool didn't scale well because it took time to load the interpreted code into the debugger and the heap and runtime requirements of interpreters stressed machines of the time (this was when Sun3s were common, and 3/50s couldn't even have more than 4MB memory).
I think it was 1990 when we released version 3, whose big new feature was object code debugging (it's not really that easy to have object code debugging as well as interpretive code, although we could certainly have implemented it more easily than we did). We of course maintained the ability to dynamically reload code fragments, so you could do a "make", reload the affected modules, and be on your way in a few seconds. This sure beat the several minute wait you normally had with the linker and debugger restart. But what you lost was some of the runtime checks -- including most of what made the product interesting. Still, the ability to have superdebugger capabilities on some code and still scale to large programs was very valuable.
About a year, maybe two years later we got visited by the people who eventually started Purify. They had a neat approach where they'd get most of the benefit of our tool, but in object code, by disassembling the object code, adding extra code to perform checks, and putting it back together. This would give about 80% of the functionality of our debugger but with much lower runtime costs and work even if you didn't have source code.
We ended up re-implementing this functionality such that we had a Purify competitor (and getting sued, and losing, even though Sun later patented the same technique we used).[1] Integrating it into the debugger allowed you to fine-tune the debugging capability you wanted on a module-by-module basis. This was a superb tool.
Now, what are the raw capabilities that made this stuff really neat?
The tool also provided two additional interesting features. One was called "action points". Since we had a C interpreter in the product it was no big deal to allow you to type C code into it and run it at arbitrary times. We'd allow you to attach any code you wanted to any line in the program or to just type code at the debug prompt and it would run in the current scope. This was useful for things like adding print statements without recompiling, for instance, and for adding assertions, and many other things.
The other feature was a graphical heap inspector. All this did was display structures as tables of their internal values and allow traversal of references to other structures, which would be displayed (including links between the structures). This made the shape of data a LOT easier to determine.
Several people talked about adding reverse execution. That was actually done for Java by a tools company back in 1996 (maybe 1997). It didn't always work, but it was pretty neat. The problem is that the recording code was pretty expensive and there's a limit to people's patience and what they'll spend on hardware (although I grant that hardware is a lot less of an issue today than it was even in 1997, to say nothing of 1989). Anyway this feature is a lot less interesting if your debugger can find errors at the point they occur.
Anyway, back in 1995 when I started to work with Visual-C++ on a regular basis I was appalled by what Microsoft was calling a "state of the art" debugger. Feh! It was 1985 technology with a pretty face. It really has not improved much since then either, although they do at least have a pessimistic heap allocator now. With their ability to hook in with the compiler they could do a phenomenal job with heap allocation tracking and instrumentation of code with error checks but they don't bother (because, I suppose, they make their money even without it).
Now, using a language with runtime error checking built in - such as Java, C#, or Visual Basic, seriously undermines the amount of effort you have to put into the debugger. Still, I'd like to see a lot better heap debugging features built into compilers and runtime environments.
Hope this gives people some ideas, and that they lead to tools I can use.
[1] One thing that could be done to avoid the Purify patents would be to have the compiler emit the instrumentation. For the OSS world, where you always have the source code, that is eminently practical. Furthermore you can get all the benefits of an interpreter but at higher performance. A big problem with Purify's technology, and the technology we came up with, was that you're doing a lot of heuristics during code inspection. It's easy to miss code or misinterpret code, and of course the code inspector is VERY architecture and compiler dependent. What I would like to see is a gcc/glibc/gdb combination where the compiler, libraries, and debugger all worked together. That wasn't possible back when the vendor controlled the compiler and you didn't have access to library source. Today it is, at least for the OSS world.
jim frost
jimf@frostbytes.com
It runs during installation. To activate it, click 'I do not agree' when some incomprehensible gobbledegook called a 'EULA' is shown. A EULA is a kind of bug. Unless you refuse to accept, it will bug you again and again. Don't confuse bugs with viruses, however: Microsoft's debugger can't handle viral licenses, which is why they don't like the GPL. Once activated, the powerful EULA non-acceptance debugging option leaves your system bug-free.
- undoware.ca
Yes, I could really use a debugger that runs backward for a problem I'm having recently. I wrote about it here last month:
I've been writing vc++ for a few years now, but one feature I'd REALLY like to see would be to be able to easily set conditional breakpoints on a particular instance of an object. In fact, more object awareness across the board would be nice. I'd also like to be able to be somewhere in a windows message handler, and be able to see where the message had come from. I'd like to be able to call functions from the watch window like I could in DEC FORTRAN. I'd like to see the VC++ debugger be able to enumerate over collection objects using for_each, and examine COM properties as if they were variables. I want the VB watch and immediate windows for VC++.
You can't win Darth. If you mod me down, I shall become more powerful than you could possibly imagine