New & Revolutionary Debugging Techniques?
An anonymous reader writes "It seems that people are still using print statements to debug programs (Brian Kernighan does!).
Besides the ol' traditional debugger, do you know any new debugger that has a revolutionary way to help us inspect the data? (don't answer it with ddd, or any other debugger that got fancy data display), what I mean is a new revolutionary way. I have only found one answer.
It seems that Relative Debugging is quite neat and cool."
Java Exceptions *were* a revolution in debugging. Java stack traces tell you the exact line number something went wrong, and the path taken to get there. More often than not, that's plenty of information to track down the bug and fix it. No need to load a debugger.
Javascript + Nintendo DSi = DSiCade
Seems like this form of debugging isn't too different from alpha testing. I don't see how this saves any work.
You get what's called 'glassnose syndrome' too easily.
Instead concentrate on building software in many small incremental steps so that problems are caught quickly, and on separation of design so that dependencies are rare.
If you can't find a problem, leave it and do something else.
Otherwise, print statements, yes, that's about the right level to debug at.
Ceci n'est pas une signature
Oh I do love it. My boss had 100% faith in his code claiming that he tests it so much it cant have any bugs. Running it through valgrind showed pages worth of bugs which were only accidently non-fatal.
Mouse powered Chips, Open source Processors and Lego
All you are doing is replacing human eyes with a computer at the first "filter" process. Instead of having to compare a bunch of values and look for the errors, let the machine point them out to you - grep anyone?
I see nothing reolutionary about this. You still have the DUT making "assertions" - duuuuh can you say "print?"
I haven't used a debugger in years; print statements are the only debugging tool I need.
But bear in mind that almost all of my work these days are in environments where the bugs that traditional debuggers help you find are pretty much impossible to make in the first place (Python, Java, etc.). Instead of tracing data structures through bits of memory and navigating stack frames, you just focus on the application itself. It's kind of refreshing.
I suppose this would be useful if you were writing something in a new programming language. You could port your code and run the relative debugger to make sure that both implementations acted the same. In such a situation, that would be great, but such a situation isn't the common case for me.
Good idea, but isn't unit testing + standard assertions do the same thing but in more automatic way ?
You feed some data to functions, you expect some sane pre-calculated output from them. Simple yet powerful.
And more important it's automatic. So you can integrate it into build process.
- Arwen, I'm your father, Agent Smith.
- Well, you're just Smith, but my father is Aerosmith!
I am attending a college pursuing my Software Engineering degree and a company called Mutek showed us via weblink a new problem to track software issues. They called it AppSight. It could tell you exactly at which point your program failed. It even showed all the .DLLs your program called, COM objects that were created and even system calls made by the App.
Mutek was bought out I believe and is now called Identify Software. You can see more about their technology at:
http://www.identify.com/
- Dominick
Debugging backwards in time. See the Omniscient Debugger for an implementation in Java. Instead of re-executing the program a thousand times, each time setting breakpoints and watchpoints in different places to get nearer to the root cause of the problem, this debugger completely records all key events and lets you view the complete program state at any point in time.
Donate free food here
I can't escape the suspicion that the anonymous poster is actually in some way connected to Guardsoft, but let's leave that for now...
I think it's a good idea, but I do wonder how many situations you'll be in where you already have an exisiting program that does everything you want to test against.
Having said, that, I can see how this would help with regression testing - making sure that you've not introduced any new bugs when fixing old ones. But I wonder how much it gives you above a general testing framework anyway...
Basically, you can define an aspect to capture points in your program that are of particular note, and then do debug handling at those points. Aspect oriented programming allows you to break out that debug-handling logic into seperate modules, keeping your main sourcecode nice and clean.
Aspect-oriented programming (AOP) has a lot of other uses too. I think in 5 years or so talking about AOP will be as commonplace as talking about OOP. They are orthogonal concepts.
Cheers, Me
I have some relatives who demonstrate numerous error conditions.
"If, therefore, any be unhappy, let him remember that he is unhappy by reason of himself alone."
~Epictetus
So you already have to have a program that does what your program does in order to check it.
Ever think of deploying that one?
It looks no more than a fancy variation of good old 'assert' macro, or an antecedent of unit testing. Why did this anonymous submitter find it 'revolutionary'? What does it have over current debuggers which can be attached to working process or can analyze post-mortem dump?
"Relative debugging" seems to be what people have always been doing. Dump some state and comapre it to an expected state. Most frameworks for regression tests do something like that.
The best debugging method is to have a fast build environment so that you can add one printf, rebuild, reproduce the bug, move the printf to an even better place, rebuild and reproduce, etc. The more you rely on your tools to do the work for you, the less you understand the code and the less you understand the code, the more bugs you will make in the future.
There are no shortcuts to good code.
See Formal Methods Virtual Library for more info on Formal Methods.
My guess, "real" programmers still use print statements.
-- br
Back in 1987 the FORTRAN compiler i used generated code that prited the source-code location of all failures, and that feature was old news even then.
Besides, nontrivial bugs don't result in stack traces or crashes. They result in infrequent, hard-to-spot, anomoalies in the output. No amount of Java stack traces will help you find them.
This sounds like a combination of debugging and test plans. More of a evolution and not much of that.
do you know any new debugger that has a revolutionary way to help us inspect the data?
..etc...
... on headless embedded boards), and that's usually enough to catch a 99% of whatever bugs remained. Normal debugging techniques using debuggers, or the test suite I made for that particular piece of code, takes care of the rest. My guess is, if you need anymore than that, it's probably that you lack experience.
I'm noy sure what the question is here. Any debugger will allow you to watch data. If your program is special enough that you can't use a standard debugger, you probably need to write a test suite to go with it (and well, for any reasonably sized project, you should anyway).
That's to help you find "surface" bug, i.e. to catch things like misaligned words, wrong data types, buffer overflows
For deep structural problems, like when you try to code something and you have no clue how to go about it, and the end result is just not good and never going to be, the cure is usually a total rewrite, so debuggers won't help you there. That's a problem due to bad architecture of the code.
So, I'm not sure anything else is required. FYI, when I code, I believe I have enough experience to architecture and code something relatively clean the first time, then because I've done it for many years, I sort of "instinctively" expect to find a certain amount of this or that types bugs. And usually, I can fix them without debugging because they jump at me. When they dont (and I can do it), I pull out the old "print to stdout" debugging (or LED wiggling, sound generating
"A door is what a dog is perpetually on the wrong side of" - Ogden Nash
Java Exceptions *were* a revolution in debugging.
Only if you have been living under a rock. Most languages and compilers other than C and C++ have been doing that forever. Even C and C++ allowed you to get a complete backtrace and inspect the complete program state from a core file (software bloat has made more and more people turn off that feature, however).
.NET has kickass debugging capabilities.
.NET project with serious bugs. Then again, I've never seen any serious .NET project...
You're kind of right, I've never seen any
Comparing the "state" of multiple implementations or versions of code is an old technique. You don't need a special debugger for it--you can use a regular debugger and a tiny bit of glue code. Alternatively, you can insert the debugging code using aspects (aspectj.org).
However, like many programming techniques, most real world programmers won't know about them unless they can shell out $1000 for a tool; reading a paper or book just would be too much intellectual challenge, right?
This news item seems to be a thinly veiled attempt to drum up business for that company.
On the subject of software debugging techniques, I'd like to point out visual testing, which (basically) allows you to try out method calls and fiddle with variables and examine the results (including execution history) graphically. MVT is a prototype visual testing tool for Java.
...to debug the user-defined assertions that I have to feed into the relative debugger.
I have. Visual Studio for one.
I am very small, utmostly microscopic.
There has been almost nothing new in programming environments or debuggers over the last 10-20 years.
Almost those features you see in Visual C++, Visual Studio.NET, Eclipse, NetBeans, etc. have been around in IDEs since the 1980's. Debuggers have allowed you to step forwards and backwards, see the source code, examine data structures graphically, and modify the running source code for about as long.
If anything, current commercial IDEs and debuggers still haven't caught up to the state of the art.
That sounds cool, but it isn't all that useful in practice. Debuggers that support stepping backwards usually end up keeping a lot of state around, which limits them to fairly small, well-defined problems or modules. But the problems where an experienced programmers need a debugger are just the opposite: they involve lots of code and large amounts of data.
Usually, it's best to avoid going back and forth through the code altogether; insert assertions and see which ones fail.
...unless you happen to have two pieces of software, that each function excellently alone, yet dies a horrible death together, for reasons unknown.
I've got this thing with OpenSSL, Qt and my code right now. On a one-time run, it works fine. When I put it into a program and try to loop it, it crashes on some mysterious and horrible error, sometimes on 2nd pass, sometimes 3rd, 4th pass or more.
All I'm getting from traceback logs are some wierd memory allocation errors in different places, e.g. in Qt code that *never* crashes if I replace the OpenSSL code with dummy code, but has nothing to do with each other. Or in OpenSSL itself, which I hardly think is their fault, if it was that buggy noone would use it. And only if this is put together and looped. Each taken apart, they work perfectly.
Kjella
Live today, because you never know what tomorrow brings
1) you are correct, VISUAL STUDIO has a great debugger for its products - but that isn't .NET per se. The VC++ debugger is hands down the best in the industry but...
2) gdb will do everthing VCDB does, just not as easily (ok not even remotely as easily)
3) there are guys/gals at my work who write very tight code under gcc - they rarely need a debugger anyway. Their debugging is almost all done at design time
Posts like that are just going to rile up their audiance and deserve their low score. Grow up.
Some flamebait of my own: I will try to not look down my nose at you for being a VB "programmer."
I am very small, utmostly microscopic.
The technique I've found most effective is to run many simultaneous debugging sessions in parallel. My debugger of preference is a semi-autonomous intelligent agent that seeks out defects in a random fashion. I call this type of agent a "user".
"God fights on the side with the best artillery." - Napoleon, Marshal of France - speaking truth to power
It sounds like this could be used rather effectively as a QA tool. Machine 1 running version 1.23 of the software, Machine 2 running 1.24 running on the same data and should produce the same results.
Automated regression testing etc.
For systems that don't have a console, and even for those that do, a parallel port connected to a set of LEDs can be very useful. you can run the system at full speed and monitor important events on the LEDs.
Mea navis aericumbens anguillis abundat
The problem with relative debugging is that it assumes that you have a "reference" implementation. What if you're making the reference implementation?
I think that the people making the tool have missed the point.
A better solution is to make your program generate a log of everything that happens, when an object is created, when an database connection is made etc.
And when you launch the program in debug mode everything is printed to a log file and when it crashes or a bug occurs you can just halt everything (if it hasn't crashed) and look at the log to see what it was doing.
Different levels of logging could be used. Say level 1 with the most basic logging (database connections, disk access, network access, etc), level 2 includes all level 1 plus network traffic, level 3 has all object creations, etc.
ex: logEvent(3,"DBO_Connection create");
They might not be revolutionary, but the is a few ideas
which can be just to reduce the number of bugs in a program.
1) 100% unit test coverage of your programs.
2) Statistical Debugging
3) Valgrind
4) The D programing Language
with build in support for unit testing, contracts and class Invariants.
n/t
Things I have used in debugging: A debugger, when available. No debugger, when the available debuggers have been worse than the disease (MPW, augh). The venerable printf, or its equivalent. Logs of all kinds. Whole machine state that you get from a machine through a serial port (tap foot for several hours). Nano logs written to memory (to catch race conditions). Flashing LEDs. Speaker beeps. Floppy disk track seeks. An AM radio held next to the machine. A photocopy machine (to get a permanent record of a register dump from the face of a hand-held machine). Cans of coolant spray. Heat guns. Logic analyzers (yay!).
This is just another tool, and it looks like it doesn't even work that well on the really hard problems, or even moderately complex multithreaded apps. It's more like an assert tool. Reminds me a lot of the stuff that Tandem was doing to scale availability (read Gray's Transaction Processing for a good description).
Any sufficiently advanced technology is insufficiently documented.
Don't trivialize the data logging approach to debugging.
In complex, multi-threaded systems where you are debugging timing events more often than programmer logic, data logging (aka print statements) is probably the only technique that works.
In fact, one of the first things we implement in embedded systems is a data logger that can spit out your print statements over RS232. Yes, we can single-step through code using in-circuit emulators and JTAG interfaces, however I found this rarely useful.
in a lot of higher-level languages, eg functional languages like lisp, haskell and ocaml. But not only debugging: in these languages you tend to write code that doesn't have bugs in the first place. No need for mallocs, no buffer overflows, no memory leaks. And if you're careful to write in a functional style, no "side-effect" bugs (variables that change value when you weren't expecting them to). For a language that started out in the 1950s, it's amazing how far ahead it was and still is as a development environment. This paper is a fascinating read, especially the section on Worse is better that describes why Unix/C won. And there are other languages like the ML family and Haskell. OCaml (Objective Caml, a descendant of ML) is as concise and elegant as python, but produces native-code binaries quite competitive in speed with C, and occasionally faster. I'm wondering why anyone uses C-like languages anymore.
This is going to sound like a plug, but I have nothing to do with this company or product - I just thought it was really cool.
When I was wandering through JavaOne last year, I ran across this booth by VisiComp, Inc. who sells this debugger called RetroVue. I think it's an interesting attempt at bridging the gap between live-breakpoint debugging and logging.
The main issue with debugging vs. logging is that logging provides you with a history of operations that allows you to determine the execution order and state of variables at various times of the execution, something that debuggers don't actually help you with.
RetroVue seems to instrument your Java bytecode to generate a journal file. This journal file is quite similar to a core file extended over time, by recording all operations that occurred in the program over time: every method call, every variable assignment, exception thrown, and context switch. RetroVue then allows you to play back the execution of the application.
It includes a timeline view to jump around the various execution points of the program, as well as an ongoing call-list to show the call sequence that has occurred. It also notes every context switch that the VM makes, and detects deadlocks, thus making it a great tool for multi-threaded application debugging. You can adjust the speed of the playback if you would like to watch things unfold in front of you, or you can pause it at any time and step through the various operations. Want to find out when that variable was last assigned to? Just click a button. Want to find out when that method is called? Same.
It's not free/cheap, but it seems quite useful.
-Stu
It seems to me that a lot more effort is being put into creating good unit tests to identify and prevent bugs, rather than debugging running applications. With an automated testing framework you can seriously reduce the amount of time spent on manual debugging and fixing as the bugs get identified as early as compile time, rather than run time.
thinking is better.
So, this isn't for developing or implementing a new algorithm.
However, it might be a step closer to fully automating the re-implementation of existing ones ...which is inherently a rote task to begin with.
Seeing bad movies only encourages them. Watch responsibly
The best debugging system I have ever used is in Smalltalk. Its possible to stop code at any time, and then data can be inspected and altered, new classes coded and methods re-compiled without interrupting execution. When changes have been made code can be re-started or resumed.
Features like exception handling with full stack trace in Java are great, but nothing beats the Smalltalk system of suspending execution and keeping the application 'alive', so it can be modified, inspected and resumed, when an error occurs.
Memory leaks and such are easily tracked with valgrind, although for basic logic errors you want to use the printf() and gdb methods.
Valgrind is http://valgrind.kde.org and requires that you turn off all pax protections for the binary you wish to debug.
Support my political activism on Patreon.
...is that they don't help in time-dependent situations.
For example, a program in C that uses lots of signals and semaphores could perform differently when print statements are added. This is because print statements take a (relatively) long time to execute. Print statements can affect the bug their supposed to be monitoring.
I had a situation very much like this. One process would fork and exec another, and they would send signals to each other to communicate. But there were a few small bugs that caused one of the processes to occationally miss a signal. When I added the print statements, it slowed the process down enough that it caught the signal. The only way i was able to successfully debug it was with a line-by-line trace with pencil and paper. I don't know if ddd would have helped (but I didn't know about it at the time).
This would be pretty difficult to implement efficiently since a single instruction has the potential to make massive state changes, however, even a limited implementation would be extremely useful and would provide an alternative to restarting the application.
O'Caml has a replay debugger. You can run your program in the debugger until it crashes, then step backwards through the code to see what was happening before it crashed.
Very handy, IMHO, although the O'Caml debugger sucks in other ways. (E.g. no watch conditions.)
I wrote a little jar that spings up an awt window filled with all the information about fileds and values about an java object, allows to browse to the fields and such, really handy when you need to debug something related to a datastructure. That combined with exceptions and println, i never use a debugger in java. For C, valgrind has revolutionied debugging.
Often something goes wrong with no runtime error. Those bugs are often really, really difficult to find.
21 comments, and no one has mentioned that using eXtreme Programming, there would be no need for a debugger.
Nope, looks like marketroid hype to me. Answer me this: what is the point of comparing two separate identical runs of a computer, except in the case of testing platform equivalence, in which case the output of a test set can simply be diff'd.
The key to their idea is that The user first formulates a set of assertions about key data structures, which equals traditional techniques. The reason such traditional techniques have failed and continue to fail is that those assertions are always an order of magnitude simpler than the code itself. These people forget that a program *is* a set of assumptions. Dumbing it down to "x must be > y" doesn't help with the complex flow of information.
Peace & Blessings,
bmac
And here I thought relative bebugging was something you did before you went out on a date in West Virginia
Also worth a look is AskIgor at http://www.askigor.org/
More about Delta Debugging at http://www.st.cs.uni-sb.de/dd/
- The best programmer I've met once told me that once you've dropped into the debugger, you've lost, which over time I've found to be quite true. The best debugging practice is to learn how not to use a debugger. (e.g., Are you using threads when they're not absolutely required? Say hello to debugging hell...)
- When you must debug, print statements cover 97% of the cases perfectly. They allow you to formulate a hypothesis and test it experimentally as efficiently as possible.
- Differential debugging is a nifty idea, but most of the time it'd be better to just use it with your print statements as above (e.g., print to logs and then diff them). For the one time per year (or five or ten years?) that having a true differential debugger might pay off, it's probably a loss anyway because of the cost and learning curve of the tool. (I thought about adding this to SUBTERFUGUE, but realized that no one would likely ever productively use this feature.)
- If you need another reason to avoid this tool in particular, these guys have a (software) patent on it. Blech!
--Mike"Not an actor, but he plays one on TV."
I mean, most of my relatives don't know anything about computers, much less debugging.
Those who sacrifice security to condemn liberty deserve to repeat history or something. - Benjamin Santayana
What?
---- It puts the lotion on its skin or else it gets the hose again. It does this whenever it's told.
Its all very well talking about elegance and planning in advance until you try and deal with hardware. No amount of zen contemplation of your code is going to tell you what a debugger does about how the hardware and its documentation relate.
The neatest debugging tricks I've seen so far are those logging all inputs and returns from the OS level. Since you can replay them you can rerun the app to an earlier point and investigate - in effect you can run it backwards from a bug to see how it got there.
#IFDEF DEBUG putc.. :-)
Very insightful (hint!)...
.Net code, I use NUnit and LOVE it.
Totally true... I do a lot of Unit testing...
With
In C++, I always have a static method on each object I write that is called UnitTest(); This function calls the UnitTest on all objects on which it depends.
It throws if it fails...
That way, you just call ApplicationObject.UnitTest() and it tests the whole system. (there is an issue with some repeated tests, but who cares?)
T
---- It puts the lotion on its skin or else it gets the hose again. It does this whenever it's told.
I didn't see much that addressed this, so I'm going to bring it up... firmware debugging! I always design in a serial port I can use for printf(), but when the system doesn't have much overhead and using printf() delays execution of a critical loop or changes a value in memory, the whole thing can crash.
I ran into this when putting together a system with two micros, each of which had a 16MHz processor and 32K of onboard memory. I couldn't afford their damn emulator, so I had to think of other good ways to debug. They had to communicate regularly, and using printf() caused the program to delay at critical points, causing it to hang even though nothing else was done to the code. Even putting in a trap-check was still too big of a delay. I started using bitwise operators on 4 bits of memory. Each time I executed a bit of code that I thought would give problems, I inserted an assembly command into the C code that shifted in 4 bits with a code that corresponded to the location, and set the read line high. Then I put those 4 lines to a logic analyzer and triggered on the read line, reading the bits as the program ran. If (when) it hung, then I would know where. This had the advantage of being much faster to execute than a printf(), so I could put it in anywhere, even in the middle of getting a packet.
Debugging firmware is a whole new bag, to be sure.
'Be always mindful, even when ditch-digging.' --D. T. Suzuki
To my experience, 99% of bugs can be located quickly via a combination of tracing and unit testing. Although on rare occasions it helps to be able to hop into the debugger (.NET shop using VS .NET 2k3, btw), overusing it produces bad code.
I've noticed that most developers use the debugger in the initial implementation phase to track down all of the possible issues in their code. This results in them writing very limitted unit tests, which is fine until a year from now when someone goes back to modify that routine. Yes, debugging is faster than unit testing, but unit testing is ultimately more beneficial to the longterm health of the product, so in my shop at least, we focus on the unit testing.
Where I'll admit the debugger is very useful is placing conditional watches during the testing phase AFTER initial implementation. Trace logs say this reference is being set to null in very rare and somewhat arbitrary circumstances - we simply attach the debugger to the app and wait for it to notice the change in value. Even in this case, we don't insert a breakpoint into code and step line by line through when the circumstance occurs - in VS.NET at least, you can tell the debugger to simple append the stack trace, and basic information about other threads to the log when the condition occurs, so really, we're just using the debugger as a very complicated expansion to our own tracing code.
-Steve
By far the neatest debugging possibilities I've seen is part of the Oz development suite... It can do things like: Lazy Display (your debugger won't get stuck long times on long lists, but you can unfold the whole list if you want), Thread Display, handling values still to be calculated, it even displays the evolution of any statement... Its a really powerful toy.
And, no, Java did not invent stack Traces (or Exceptions). Really.
I'm surprised to see no-one has mentioned the Fix & Continue in Apple's ProjectBuilder IDE. I use it all the time, and it's saving me weeks of debugging. You really have to try it to understand how convenient it is, but here's an attempt to describe it.
You run your program in the debugger. Let's say you try to click some button, and it doesn't do what you expected it to do. With the program still running, you change the handler code, click Fix in the debugger, and try your button again. Voila, problem solved! No need to recompile and then try to get back to the state you were in when stuff didn't work.
Those of you who haven't used languages like LISP or Smalltalk (or Forth?) wouldn't believe how convenient it is to be able to change code run-time! I often add statements like
if(this) printf(that);
on the fly. Conditional breakpoints almost lose their relevancy when you can just add whatever conditional you like in the code, and put a breakpoint there. =)
Works with many changes to the code, as long as the program counter isn't in the code block you're editing (in which case you get a warning and can retry later). Works with C/C++ and Objective-C just the same. Some things, like adding member fields to classes are not accepted, or changing the number of local variables for functions that are on the stack.
I second that. I currently have a piece of software that runs as a daemon. It silently crashes about once a week. Tell me a way of debugging it that doesn't take months, and I'll be happy. But until then, I'll have to add debugging statements and triple-check each line of code, run it again and wait another week or so. Right now I can only very vaguely tell where the crash occurs - but not what causes it. Not fun.
This is interesting, where this automates the process, similar to when Frank and Jim introduced Bounds Checker to check for corruption of the memory chain instead of checking the memory chain multiple times.
Fight Spammers!
Log4j is all you need.
For example there is Design by Contract where you specify everything twice, once in code and once in conditions. These are then compared at runtime.
There is prototyping with, e.g. a scripting lanuage, and then re-implementation in something else with offline comparison of the results. I do this all the time.
In electronics the idea is decades old: Compare the signals in a working device with those in a non-working one. Although here the devices have to be identical. That is also the main problem I see with this relative debugging: The person doing the debugging has to know where the two instances are similar in their inner workings and where not. Without support for this from the language or programming method (e.g. Design by Contract) this is extrememly hard. With such support, there is no need for separate applications.
My bottom line: Old ideas in new guise. And possibly worse in practice than many other methods.
Most ACs are not even worth the keystrokes to insult them. Be generically insulted by this and ignored otherwise.
the status codes I display using the single led output it has.
;)
The think is pretty much I/O bound with several external components, so no chip simulator does the job.
Consider yourself lucky
I believe that the old Symbolics LISP machines also dumped a stack on error. However, they also allowed you to correct the problem and keep running the program from where it left off. But that isn't the point. The point is that when Java was introduced, most of the world was running on C/C++, which are very difficult to debug. Java popularized runtime stack traces and started a revolution in the way we debug programs. So what if it's old stuff? There hasn't been anything truly "new" in CompSci since the 1960's!
Javascript + Nintendo DSi = DSiCade
Seriously, though. I've worked as a programmer for the last 15 years. Mostly, I've been fixing other people's bugs. Here's what I like to see in code that I need to fix (and generally don't see):
1) Consistency in formatting, style, variable names, design - I don't care what style you use as long as it's consistent. I prefer my own form of Hungarian Notation, where a variable's prefix indicates its scope (global, static, etc), as well as the type. If any of that information changes, I should darn well follow through to make sure I've fixed everything that depends on them. Bring on strong type checking!
2) No spaghetti code. Give me this:
instead of this:It doesn't look like it matters much yet, but try adding eight more error checks to both, and see which you can track better. The "early bailout on error" model clearly surpasses the "endless nesting" model.3) Use of descriptive variable and procedure names. Source code is not meant to be understood by the computer. This is why we have compilers, and interpreters. Source code is meant to be understood by humans. Write your code for humans, and you'll be surprised at how much faster you can grind through code. You'll only write the code once, but when you have to debug it, you'll spend eternity sifting through line after line, wondering what the hell you meant by that overused "temp" variable (temporary value? temperature? celsius? kelvin?). If you had only taken the time to spell out, "surface_temperature_C", you'd know for sure. Vowels are good for you.
4) Comment! Not every line. Not an impossible to maintain function header comment with dates and initials of everyone who's edited it. Don't fall for nor rely on that "self-documenting" code nonsense. Just one comment line every three to ten code lines. That's all. Give me an overview of what's supposed to happen in each logical block of code. Tell me what if conditions are checking for. A good rule of thumb is to sketch out your functions in comments first, then fill in the blanks.
That's all I can come up with off the top of my head, but there are certainly more...
NOTE: for the pedants who think they noticed an apparent conflict between my hungarian notation style and the "surface_temperature_C" variable: since there is no scope or type prefix on the variable, it's a local variable, and I can change it at will, knowing that it will not affect any code outside the function at hand. If it had been "m_fSurfaceTemperature_C", then I'd know it could have repercussions affecting the state of the current object. If it were "g_fSurfaceTemperature_F", then I'd know I could hose my whole program with an invalid value. And should have converted from Celsius to Fahrenheit before doing so...
How's my programming? Call 1-800-DEV-NULL
Embedded board doing solenoid control. Too lazy to read through the RS-232 output, so I programmed the controller to change the solenoid PWM to a frequency/drive that made them vibrate at the resonant frequency of the structure they were mounted to when a certain issue was encountered.
I could be all the way across the room, and suddenly there would be this nice clear tone, as my solenoids 'sang' to alert me of trouble.
---
Mod me down, you fucking twits. Go ahead. I dare you.
(I read with sigs off.)
This is valuable for finding compiler and machine problems which affect the results of important numerical packages, like NASTRAN.
That is, print statements and their equivalent when
you have no console or other device to print to.
Assuming that the software was well designed,
narrowing a bug by checking the values in critical
variables is the right thing to do. Printfs do it
just nicely.
Your worst enemy, whatever the method used, will
be the bugs that show themselves once in 100 runs
of the software, not the easily reproducible ones.
Great. Someone has reinvented assertions.
How come this is even mentioned as slashdot?
Please login to access my lawn
This is the only reason I ever use VB6, in some types of project it is a godsend!
Funny thing is that VB.NET does not have this level of debugging. (Yeah...I do know why before you all reply).
Python is a language that could, and should, support such high level fix and retry debug.
And if you thought that was boring you obviously havn't read my Journal ;-)
For a long time, I've been building algorithms and testing them against a completely different algorithm to accomplish the same task. More often than not, the tester algorithm finds errors in the testee than the other way around. That's usually because the tester is a more brute force, simple implementation (that's not necessarily efficient.)
(-1: Post disagrees with my already-settled worldview) is not a valid mod option.
Ah, I see you are a C++ coder.
The most effective debugging tool is still careful thought, coupled with
judiciously placed print statements.
-- Brian W. Kernighan, in the paper Unix for Beginners (1979)
But I think the key to debugging is not the technique used for debugging, but how one wrote the code in the first place, here again God Kernighan hits the nail in the head:
Debugging is twice as hard as writing the code in the first place. Therefore,
if you write the code as cleverly as possible, you are, by definition, not
smart enough to debug it.
-- Brian W. Kernighan
Once again, at the time of debugging, simplicity shows it's superiority to the complexity that seems to be so much in fashion this days. That is why I still prefer C to C++; rc to bash; AWK/sed to Perl; Plan 9 to Linux; Limbo to Java; 9p to NFS,...
This is the forgotten key to software design:
so simple that there are obviously no deficiencies and the other way is to make
it so complicated that there are no obvious deficiencies.
-- C.A.R. Hoare, The 1980 ACM Turing Award Lecture
Or put in another way:
The cheapest, fastest, and most reliable components are those that aren't there.
-- Gordon Bell
Back in the topic of debugging, aside from the sacred printf, the Plan 9 debugger acid is often helpful, and now you can even use it on Linux/BSD!
Plan 9 on Unix
Also the chapter on debugging in The Practice of Programming by Brian W. Kernighan and Rob Pike is very good.
Always remember:
"When in doubt, use brute force." Ken Thompson
...why not just use that? Or use the computer to modify that to any programming language you want?
How do you prove the correctness of the reference implementation?
Why not just write all software with the reference implementation, and let the computer fill in the dots. Think paint by numbers brought to coding. You draw the picture, jot some of the numbers, the computer infers the rest.
I do not moderate.
I used it in Sather and Eiffel, but the D language recently reported here has it as well.
In Sather, every class could have class invariants which were checked every time a public method was called or returned (private methods often muck about in the innards of an object so the object may be temporarily inconsistent). Every method (function) could have pre and post conditions defined (and these had nice syntax) and these where checked on calling and returning from the methods.
These checks could be turned on and off at compile time for every class.
Debugging time was often cut by an order or magnitude or more just by finding the right preconditions, postconditions and class invariants.
This did have a downside. I discovered quickly that I had to spend much more time thinking carefully about things like class invariants and pre/post conditions - but spending that time in the design phase usually paid off very well indeed.
If Java had this kind of support it would be ever so much more useful. (And the rather archaic "assert" facility just ain't the same.) I'm not a big fan of microsoft, but if they'd added this to C# I'd probably be an enthusiastic supporter now. That they didn't (and that Sun did not build this in to Java early on) leaves me wondering if either company does in fact care much about software quality.
The best system I've ever used for inspecting data in debugged programs is duel. Duel is a language specifically designed for inspecting data structures, and was implemented as a patch to gdb.
f
I used to use and love duel--debugging with duel made so many complex things trivial to do at the command line. (Sort of the way programming with regular expressions allows you to do in a single line what otherwise would be incredibly complicated.)
Unfortunately, the main author of Duel was an outspoken critic of the GPL and FSF. Even though duel itself was in the public domain, I think the author's animosity towards the FSF was probably largely reponsible for the fact that it never got integrated with gdb.
For those curious, here is a paper describing duel. You can probably still find the source code somewhere on the net.
http://research.microsoft.com/~drh/pubs/duel.pd
Assuming there's something newsworthy about it. And in this case it does look like an interesting product. However, what I *dispise* is not being told how much a product costs, and having a website plastered with "free 30 days evaluation, download now!!!"
Do they honestly expect me to invest hours of my time learning their product, only to find out that it's $5000 a seat? (And I'm making this number up, because I can't for the life of me find it on their website, or Programmer's Paradise, or on Usenet, or anywhere else.)
Another innovative testing framework that was released recently is TestNG, http://beust.com/testng
It uses Java Metadata to specify test runtime and deployment information.
Rather than using print statements, I stuff error messages in an object. The object has option of writing the errors to a database (or flat file), emailing them to a sysadmin, calling a web resource, paging people, or printing the errors to the screen. Having a tidy error handling system attached to a program can increase customer loyality and the price you charge for the product. The database itself creates a nice little auditable history of program.
When you give each error a unique id, then you can develop automated help systems with up to date info on common bugs. You can also create a system to escalate problems to the correct people.
My main fears are about errors occurring in production, well beyond the debugging stage of development.
Print statements alone depend on the end user being able to decipher what the programmer was thinking when they wrote the print statement.
I like to use assertions to debug code. Why? Under Unix, you get a nice core dump to feed back into your debugger, that shows the state of your program when it died. If you use MSVC++'s ide to run your program in debug mode, it will also stop at the assertion.
Of course, some creative tinkering with a debugger might lead to the same result too, but I find assert() more convenient. Also, common sense tells us that a multifaceted approach to debugging code is best anyway (reading it, creating test scaffolds, asserts and/or exceptions, running it through a debugger, etc.)
The internal structure of a running program is more or less a RAM database. If it is going to be a database, then why not a relational database? If so, then one could use more-or-less off-the-shelf database querying and viewing tools to inspect and compare stuff as needed for debugging. At least the learning curve will be lower if they follow typical database conventions.
It seems every language invents its own proprietary little (non-relational) database so that only that vendor can sell you tools to traverse the structures and debug them. Thus, we end up reinventing the wheel for each language. GUI systems are the same way: you need a custom browser to inspect them.
The closest thing right now is probably MS's Common Runtime Language, which uses the same structure for multiple languages. However, CRL is not relational. Also, the "schemas" are not allowed to vary per language, as far as I know. I am not proposing sharing the same schema, only the same database interface and abilities. For each language may need a different schema.
Opponents usually have 2 main complaints about such:
1. Relational databases are (allegedly) too slow.
2. They don't like relational databases, for whatever reason.
The second one is a personal preference that nobody can do much about. Can't please everybody. As far as the first, I don't know that they have to be slow in such an environment. The current crop of Oracle Clones perhaps it not suited for the suggested niche, but there are other ways to implement them. Maybe standardization results in some speed loss initially, until economies-of-scale economics finds ways to tune them, the same way that companies are coming out with Java bytecode optimizers to make Java competative with C, speedwise.
I have never seen theory that says relational MUST be slower than non-relational structures. Just because existing products are not tuned for a given use does not mean it is not possible.
Table-ized A.I.
Having read the article, this "revolution" seems little different from using masses of assert macros in your code (a practice I strongly endorse).
This seems far from being a revolutionary idea
and makes me think....
Errr....
If you've got one machine working correctly which you are checking against.... then errr...
why bother writing a second instance of the program....?
Or have I missed the point!
Linux fan and Win32 developer
I agree that this is just a fancy assert mechanism. But a lot of people are claiming that printf is the best debugger. Sometimes it't the best one can do. Other times a real debugger is the only efficient way. Many programmers that I respect, and I also, use the debugger as a sanity checker. Whenever I code up a new module and it has passed basic functionality tests I fire up my code in a debugger and do a thorough walk through. I find a good many subtle flaws this way, saving a great deal of time in the intensive testing and later QA cycles. I recommend this as a best practice to any programmer.
When your program tend to crash on random lines most of the times its because you overwrite your own memory (variables) without knowing it.
Check:
-strncpy not putting a ending '\0'.
-writing more than buffer length.
-if you pass a pointer to a fonction it must have been allocated (most of the time).
-That all your new/malloc return good values.
I rathe enjoy Codeguide's back in time debugger, because it lets you step backwards, through the statements (not the call stack).... then hot fix code and then you let it rip again. And you are always running the debugger. Lovely stuff.
"You know you want me baby!" - Crow T Robot
see this link to ACID whitepaper
also see this portion of the Inferno manual.
and this entry on citeseer.
*the* AC
In their diagram explaining relative debugging, the "reference application" PC is the one with the blue screen!
Moderator hint: a comment is neither "Flamebait" nor "Troll" if it is true.
I agree. So when will someone port Gecko to Smalltalk? I bet that things like SVG, and other such would already be there if they had been written in Smalltalk. Throw in an Objective-C/ Smalltalk bridge and now we're talking.
The code that calculated all the spreadsheet dependencies and what cells needed to be recomputed, was pretty complicated, as you might imagine.
So they had the super-optimized version running in parallel with the dumb, calulate-every-cell-every-time engine, and then they'd compare the results.
In certain cases, like this one, the technique is useful, but it's neither revolutionary nor new.
-elan
No one has mentioned UPS yet. I'm not sure you could really call it revolutionary, but it does have a few interesting features:
Like other people here I debug mostly with printfs() logged to a file for easy searching, supplemented with valgrind, memprof and occasionally UPS. They are all tools and you need to try to pick the right one for the sort of bug you think you're facing.
I found Ask Igor on Slashdot some time ago and I think that is a revolutionary debugging technique (I was going to say "as well" but after reading the advert from GuardSoft, err, the article, and some posts I am not so sure that is correct ;))
The revolution will not be televised.
Often, chip architects create reference models in a high level language, typically C/C++, while the logic designers create an implementation of the same functionality using a Hardware Description Language (HDL), typically Verilog or VHDL.
Chip verification engineers then run simulations in which data is fed into both models, and data values (internal values and/or output values) are compared at key points in the two models as the simulation progresses. The simulation is usually set up to automatically complain (WARNING/ERROR) when the two models disagree, and may or may not halt, based on the severity of the discrepancy.
It is also typical to have the HDL model dump value change events into a file whose contents can be viewed graphically in a GUI which shows the value changes with respect to time (similar to what one might see using a digital logic analyzer on real hardware).
Also, if I recall correctly, the Space Shuttles use triple redundancy with "voting", so that as long as at least two CPUs agree on output results, they are viewed as correct, and the odd man out is ignored.
For my final year dissertation I wrote an interactive profiler named rtprof. The idea is you have a remote machine calculating the profile of a process running on another machine in real time, all of which is visualised in three dimensions.
While albeit not that radical, and definitely not applicable to all environments. One of the most effective methods I have used for debugging java code is by using jython I find using jython about an order of magnitude faster for writing test cases, and debugging code than Java. Furthermore, having the ability to pop open an interpreter and try methods out whilst you program is invaluable. Eclipse added a feature (jpages I think it is called) which you can write code in, but the value of python lies in the dynamic data structures, and no compile time type checking (no class casting which is what you spend a good bit of your time doing when writing java test code)
Assertions == printf on steroids.
Or was that crack?
G, D, & R...
Mark
On the subject of descriptive variable names, I've found that having a decent method of identifier completion has helped my programming immensely. Sometimes I start to get lazy and begin to use acronyms that will undoubtibly make no sense in a few days. But if I can just type somthing like _te to get _temperature and so on, its much easier. ctrl-n is for vim, I would imagine there are similar methods in most editors.
-blue
I thought of this too. Automated debugging really is a new technique. Of course, it's still experimental and what not, but it is different.
don't debug
How about always debug.
I've been writing commercial C++ code for 7 years. I pride myself on the quality and consistency of my code. The single biggest factor that's helped me to achieve this is that the very first time I run a piece of code, I'll always step through it in the debugger (the MSVC IDE) to make sure that it's doing exactly what I *intended* it to do. You get instant feedback on how well your layout worked, if you've covered all the problem areas. It also encourages you to write code that's easily debugged.
If a function has easy to replicate data, I might even write a temporary block of code at the front of my program to pass it several different types of inputs and make sure the result is what I'm expecting. Yes, self-inflicted quality assurance.
Even if you only find a minor mistake 25% of the time, that's still a significant improvement in reliability of your code for very little effort. When you're working on a software team (~8 programmers), it pays to fix mistakes as early as possible, before you check code in and infect the codebase with what should have been easily avoided problems.
Jbuilder tells me in real time every sytax error in my code, I guess that's debugging.
It also has good refactoring support, so no need to debug my poor hand refactoring. I guess that's kinda debugging.
And it's very good at displaying my code in a way that allows me to find any bugs before running it, getters, setters, things I may have wanted to overload, UML diagrams etc... So I guess that's debugging.
Debugging without even having to run the application, and wizards to perform all the monkey work so you don't gte bugs in the first place and intergrated junit testing.
I think Eclipse has simila support.
I'm not a very experianced java programmer, but my productivity is more than 4 times that of a friend whos been programming in java for more than 6 years. I do very little runtime debugging because my code is by and large bug free thanks to the design time and code time debugging in the IDE.
Go download jbuilder trial or Eclipse with some sister project plugins (eclipse is a bit of a pain to use because it's still quite a recient product)
thank God the internet isn't a human right.
Failing that, as most of us do, the next best practice is to program defensively: anticipate where problems might occur in your code and include assertion checking and logging (yes, print statements) to illuminate those problem spots. Generally, I include debugging flags on the command line that allow me to control the level of assertion checking and logging (0=no logging, except for errors (the default), 1=log all branches, 2=log branches and variable values, 4=log everything).
This defensive debugging strategy works quite well. First, it forces the programmer to think harder about both the algorithms they are using, and their implementation. I catch about a quarter of my programming errors just in the process of adding assertions. Second, the program will tend to abort as soon as a problem is detected, rather than running on for a couple billion instructions, dumping crap into the output file or database and then either aborting mysteriously on some marginally related condition, or, worse, completing without any reported errors! Finally, when errors are detected, the debugging can usually be done simply by inspecting the soure and following actual execution from the log file.
All debugging comes down to one, fairly simple, idea: show me the program status at crucial points in the flow of control (generally at every branch and return). A few other tools are of some use under special circumstances: Purify, Electric Fence or Valgrind for detecting problems with dynamically allocated memory, or something like ddd for examining linked structures (though I prefer to just write a validation function for my data structures, see my AVL-tree code for an example). Defensive programming works because it answers the important question that usually forces you into using the debugger: what the hell just happened?!? Defensive programming gives you a way to examine program states without invoking an outside tool.
The only class of bugs that doesn't succumb well to this approach is race conditions. Unfortunately, anything that changes the timing of the program (such as stepping instruction-by-instruction in a debugger, or writting log messages out to a disk file) will change the behavior of the race condition. I'd be really interested in tools or techniques that could address this class of bugs.
I use printf() instead of print statements.
"I think this line is mostly filler"
Tarantula Web Site
The intuition of the approach is simple (this is our hypothesis): statements that are executed primarily by failed test cases are more suspicious of being faulty than those that are primarily executed by passed test cases.
So, we take the statements executed by each test case and its pass/fail status and the source code for the progam under test as input. Statements that are executed primarily by passed test cases are colored green to denote safety; statements that are executed primarily by failed test cases are colored red to denote danger; and statements that are executed by both passed and failed are colored in a yellowish hue to denote caution.
Example screenshot
We use a visualization for the code called SeeSoft that represents each line of code by a line of pixels, where the length of the line of pixels is proportionate to the length of the source code. This gives a miniature view of the code -- much like if you were to print out all of the code and post it on a wall and walk away from it. This allows the developer to see the colors of many lines of code simultaneously.
We have since extended the visualization to include an even higher-level abstraction than the SeeSoft view. This view uses TreeMaps and allows the simultaneous display of the colors of about 2 million lines of code.
Another example screenshot with the TreeMap visualization
So far, our experiments show that for programs with a single bug showing up in the test suite, this method successfully illuminates the fault about 90% of the time.
Here's some papers about this work.
Paper 1
Paper 2
Real In Circuit Emulators (ICS) were great. You could load up the symbol tables from your code, and all the code fetches (as well as all data accesses) would be stored in a hardware queue for automatic di-assembly and de-compilation. Tags from the original source were used where they matched. The great part was being able to trigger on a memory location (variable) being written, and even qualify the value written. You could say things like "run until myvar has bit 15 set" and then go backward in the execution stream to see who did it and why.
Now with on-chip memory caches and virtual memory mapping, as well as super-fast clock speeds, it would be a lot trickier to build such things today. I don't even know if anybody is doing it commercially in the PC/Windows environment, but it still is popular for embedded projects.
I still use traditional debugging methods, but I'm just a lightweight programmer. I write mainly PHP, Perl and JavaScript ..... for web applications, and our own in-house stuff. I start by writing a mock-up of the most important inner bits, maybe starting with fixed parameters, and build it up from there. My code is full of commented out echo, print and alert() statements respectively. I have also been known to use things like print $foo if $diag; {and then just changing my $diag = 1; to 0; it's quicker than finding all the extraneous print statements to comment out} and even the odd warn statement in a CGI script, using tail -f on the web server's error log.
Maybe if I was doing heavier-weight work, though, especially in compiled languages, I might need more sophisticated tools.
Je fume. Tu fumes. Nous fûmes!
Every time I hear about how "unique" the Java VM is, I can't help but think about the old ucsd Pascal and it's "p-code" interpreter.
It almost feels like being one of the old monks in the dark ages, keeping alive the memory of how things used to be...
By the taping of my glasses, something geeky this way passes
One of these would be kinda cool, but I only use ergonomic keyboards. Has any one ever seen a glowing ergonimic keyboard?
I wrote an omniscient debugger for Linux programs in 1995 (I wish I had thought of a cool name like 'omniscient', though).
The debugger works by capturing all the system calls in the inferior program. When you think about it, most of the instructions in the inferior program are deterministic -- given the same machine state on input, they produce the same machine state on output. These instructions can be replayed with no overhead.
The major interesting instructions are system calls. A system call will produce different changes in inferior state each time it is run. (Think of the results of gettimeofday(), or read()). So I wrote something like strace that hooked all the system calls and recorded all of their memory effects and return values.
So with a copy of the original program and a copy of the log file, one could replay the execution of the inferior program, on the same machine or on a *different* machine. Trace on the end-user's machine; mail the files to the developer; replay on the developer's machine!
The next step is to connect a debugger to the replay'ing program. I did this by running a debugger (gdb) in another sandbox where I filtered all of its system calls. When gdb calls ptrace(), I feed it appropriate values from the replay-inferior process. So gdb (or any other debugger) thinks it is interacting with the replay'ing process, without any changes needed to gdb.
Things I never implemented: ELF shared libraries; signals; shared memory; non-deterministic instructions such as the cpu-id instruction and hardware random number generator.
At this point the project is pretty defunct, but I may retun to it someday. It might have some value as 'prior art' in case anyone tries to patent this technique. The code is available at mec-0.3.tar.gz.
One thing that shouldn't be missed is that object-oriented exception handling, as popularized by Java, was invented in MIT Lisp Machine Lisp. This simplified and regularized error handling tremendously, to the point where today hooks into the deepest aspects of a typical implementation of Common Lisps' debugger are reduced to about a page's worth of code to deal with stack handling. I did a little work on the CLISP backend for SLIME, and this really surprised me.
In the great CONS chain of life, you can either be the CAR or be in the CDR.
Debuggers should be used for three purposes:
1. Reverse engineering.
2. Crash analysis.
3. Teaching aid.
If a programmer needs a debugger (as opposed to debugging output) to find his own mistakes while he is writing the code, he practices an unsafe programming technique. Debugger can only show a huge amount of information about what happened in a particular test scenario, so there is no hope that it will cover even a noticeably large percentage of problems, leave alone make sure that program is working as intended, or is robust. Also each and every timing-dependent problem and race condition will be missed or distorted, and the programmer will never be able to see it. Security bugs (that usually depend on the input that can not be thought of by a developer for the test cases) will be all missed. too.
Debugging output allows a programmer to see the program's work dynsmically, cover a huge amount of cases, and allows semi-automated analysis (even if it's just regexp search in vi/emacs), profilers may help with efficiency issues (though not all of them because, again, they rely on a small amount of test cases), however jumping between little boxes with values, or endlessly stepping through the lines and functions at most can produce a program coerced into giving the right answer by applying a completely wrong (and usually dangerous) algorithm.
Contrary to the popular belief, there indeed is no God.
could you elaborate a little on meaning of "m_f" and "g_f" and for what those are acronyms? I bet that "g" stands for global, but I have no idea what "m" or "f" should mean... What other variable types (apart from m_f and g_f) do you have?
.. what more?)
honestly I just want to learn from you how should I distiguish my own variables (local/global
#
#\ @ ? Colonize Mars
#
Print statements allow something that many other debugging tools don't - they can be run by a client on their own systems. The larger a system, the more potential for problems to hide for years until the right data/request exposes it.
The prints don't even have to be "all on" or "all off". You should see the amount of information coming out of some IBM software when you set the highest possible trace levels though - it's often enough that the vendor can determine the cause of an obscure problem without having the entire customer data set.
Not saying they don't use other tools, and I don't work for IBM.
-- All your bass are below two Hz
I have written a flexible printf debugging package, it can be found here. It is fully documented, but I have only used it myself. I figured I might as well release it since it is on-topic for this Slashdot post. Consider it an alpha release, because the API may change and I haven't done any testing outside my own systems.
if someone else can fix it, you are expendable. obfuscated code means you keep your job so nobody else is debugging your code, taking your money. this "maintainable code" movement is an evil idea by managers to make it possible to hire good programmers for a little while then replace them with low paid code-monkeys once the hard work is done. way to screw yourself
As for the original poster, I can only say this: "amen, brother".
Run your code under valgrind (http://valgrind.kde.org/) and watch for memory errors. If it reports an error, go fix it! If it doesn't then you can be reasonably sure memory errors are not your problem.
as described at http://www-2.cs.cmu.edu/~necula/papers.html holds a lot of promise, especially if the proof annotations were saved in the executable along with symbol table information.
excellent guesses. exactly right.
Executive Summary of my personal hungarian style:
Variables are (scope)(type)(name)(units)
scope is: g_ for globals, s_ for file statics (affects entire file, but only the current file), s_ for class statics (affects more than current class object), m_ for class members. Local variables have no scope prefix.
Types: b for boolean, c for character, d for double, e for enumerations, f for float, i for int, l for long, n for unsigned, o for misc object, p for pointer, r for reference, s for string, t for time, v for vectors/arrays/iterable sequences. Local variables have no type prefix. Scroll back to review the type if you need.
Names: all_lower_case_underscore_separators for local (unimportant) variables. UseInitialCapsForEverythingElse. Acronyms are treated as a word for the purposes of capitalization and separation, e.g. IrsForm1040A.
Units: If it is expressing a dimensioned quantity (length, volume, time, mass, etc., or combinations thereof), add units, e.g. rpm_Hz, UplinkFreq_MHz, etc. You'll never wonder whether or not that timespan measurement was in seconds or milliseconds ever again. Not for long anyway.
That's the simple version. Try a few, and see what sticks with you. Your brain might not be wired the same way as mine. This is geared towards enforcing consistency in scope and type, which are the places where assumptions tend to be broken. e.g. m_iNumerator / m_iDenominator will truncate, m_dNumerator / m_dDenominator will not (at least in C/C++, where it's useful to know the difference).
Enjoy.
How's my programming? Call 1-800-DEV-NULL
Consistency is so important to find your way around code. I've spent so long being confused by different names that eventually turn out to be referring to the same thing, and one name used to refer to several different things... Yes, this matters in comments as well as identifiers.
OTOH, I've tried a couple of standard prefixes, and come to hate them. Scope prefixes can be slightly useful; they tell you where to go to find out anything else. (Depends on the language/system.) But IME type prefixes are awkward, long-winded, and give no benefit. If you can see the variable name anywhere, then you've usually got about as much information as you need to use it. And of course encoding the type precludes you from changing it -- it only takes one or two variables which have been retyped but not renamed to put you off! But I know people feel differently about this, and I don't want to start a Holy War on what's really a minor point compared to the vast amount of really bad code being written...
Your point 2) is I think an application of a more general principle: concentrate on success. Of course, failures must be spotted and dealt with, but the main flow of the code should always be with the successful case(s). Not only does it make cases like you describe easier to handle, it also helps you to think about what the code is really doing.
I'd like to qualify 3) a little: the lengths of identifiers should generally be directly proportional to their scope. Globals (which are of course EVIL anyway) are doubly bad when you can't tell what they mean. But I've also seen code which is made completely unreadable by having short-term index variable with 10 or 20 characters. I'd be perfectly happy with a variable called 'i' if it was only used for a few lines, and the context made its value perfectly clear; though maybe 'cell' or 'line' or something might be even better. The knack with naming is to find the shortest name that's meaningful in the circumstances.
And comments are similar. For goodness' sake, DON'T put comments in to explain stuff that's obvious from the code. (This very day I've been working on code with lines like:
Instead, write comments to explain the big picture. I'm not stupid; I can usually work out what the code is doing at a low level. What I have more trouble seeing is why it's doing that, and how it fits in with the rest of the system. And explain functions/methods/procedures from the caller's point of view. If I want to see how it works, I can; but if I'm going to be calling it, I need to know what assumptions to make, and what it can do for me.
Ceterum censeo subscriptionem esse delendam.
I knew a guy with Perfect pitch, who would access the PC speaker at different frequencies in different parts of his code, and then by listening to the screeching and warlbing could find bottlenecks and such.
"Hey, there's too much B flat. My damn animation library is slowing things down again".
Scary bastard.
Debuggers that suck
Languages whose features make debuggers suck
Most languages are designed primarily to run programs fast. Features that make debuggers useful (symbols, data structure descriptions) are not available by default, and have to be grafted on afterwards with special compiler options that can sometimes change program behavior (remember when -g in gcc would turn -O off?).
Most Lisp implementations grew up in AI environments - there was a lot of experimental programming done on problems where it wasn't clear the task could be done at all. Also, machines were a lotslower back then - you might land in the debugger after a run of hours or days, and you really didn't want to start over from scratch if you could possibly avoid it.
So, Lisp implementations were (and still are) designed to "get it right, then make it fast". If you land in the debugger, you can almost always examine the whole stack, modify pretty much anything, change code and data structures, and continue the program, backing up a few stack frames if necessary.
And so, Common Lisp exceptions can be handled two different ways - handlers can run in the context where they are established (like Java and C++) or they can run in the context where the exception is created (and where a debugger/handler has the best chance of fixing things transparently). Stroustrup considered allowing this second type of error handling in C++, and rejected it because it could allow bad, non-modular coding styles, without considering the effect not having it would have on C++ debugging.
In most other languages, debugging is evil, intrusive, and slows your code down. In Lisp, the debugger is good, always there, and an extension of your basic interaction tools.
To a Lisp hacker, XML is S-expressions in drag.
And, if you can't do that, use your psychic powers or cast the yarrow stalks.
And, if that doesn't work, well, there's always print statements...
That is all.
Delta Debugging (http://www.st.cs.uni-sb.de/dd/) - has been featured on Slashdot before.
Basic idea: Run a program with input data until it crashes. Debugger will find the diff in the input data that triggered the crash and the location in the source code where the crash originated. Also works for same input and different program versions.
It's free and they have an online demo where you can upload your program with test cases and they will show you where the bug lies. (http://www.st.cs.uni-sb.de/askigor/)
Also works in Eclipse with automated testing using JUNIT.
"How much has been forgotten. "
More like, How much did most even know?
Anyway what your describing is basically the same thing that happens with hardware. Trickle-down of technology. How much of what's in our present-day hardware was originally in mainframes, and workstations? Well now we're seeing the same with software, and like the above it has to be simplified. Remember SGML? Remember the Lisp machines were dedicated in order to get usable performance out of the language, at that time. Moore's law now makes the past possible.
Something I'd quite like to see is an expansion of the varible watch feature of debuggers. What would be really useful is a set of widgets that you could quickly drag and drop to form a custom "view" of the internals of your program whatever way you liked. Of course it would have to be slightly intelligent and look at how you organised your data structures etc.
Another useful thing would be, a debugger which steps through your program and memorises its state at every point, and lets you backtrack to see where it was a few lines ago. The two of these combined would be the most powerful debugger ever, I think.
I disagree. Early bailouts make it too easy to accidentally skip necessary cleanup/rollback steps, and end up with leaked memory / file descriptors / etc. (Yes, smart pointers, I know... but not all APIs support them) "Single entry point / Single exit point" is a standard principle of structured design, and not without reason.
I don't care if it's 90,000 hectares. That lake was not my doing.
debug_print(int debug_level, str debug_msg)
Somewhere, you have a list of what the various debug levels are. It's useful to do something like
0 = off
1 = entering major functions
2 = less major functions
3 = specific breakpoints
4 = loop variables
The debug print checks a constant global variable, or if more work is required, gets set by a command line. Means you dont need to remove the statements for the final compile.
Yay me!
On Solaris, if you're using Sun's compiler/debugger, load up your program in dbx and before you enter the 'check -all' command, then run your program just like before.
Also on Solaris, you can use the "watchmalloc" library: set the LD_PRELOAD (or LD_PRELOAD_32/LD_PRELOAD_64) envval to "libwatchmalloc.so" and see what happens (see the man page for watchmalloc for details.
Then go back and try changing the error conditions and their handling for tasks 3 and 6 in both versions of your code.
Which one's much easier? And therefor more maintainable and much less error-prone for the life of the code?
While you might disagree with me, if you worked for me your code would fail in a code review if you used the single-entry/single-return for such code. (Of course, if you worked for me you wouldn't be able to go 8 nesting levels deep because I hold to code standards the limit line length to 72 chars for a lot of reasons - but the real reason is to prevent anyone from being able to go too deep in nesting code. It's really hard to write readable code that nests 11 levels deep when you have to use 4-space indentation and are limited to 72 characters per line. And if it ain't readable, it fails review....)
Those of you who have written distributed applications/code know what a bitch it can be to debug something when multiple processes are involved.
Those of you who have written multi-threaded applications know what a bitch it can be to debug something when multiple threads are involved.
Those of you who have written timing-sensitive code know what a bitch it can be to debug something that is timing-related.
Now, put all three of those into a pot and stir it around. That's what I and a co-worker have been working on the past four days.
We sent four or five debug versions of the code to the customer for them to run in their production test environment over the past several days with various information printed to the console. With the dials turned way up, the problem usually manifested after a few hours (as opposed to a day or more, when operating under normal conditions). Each time, we'd get back a multi-megabyte log file which we would pore over to see if we had found the root cause of the problem. (Yes, grep was our dear, dear companion -- we're taking it out for drinks as soon as we've verified the problem has been fixed.)
The problem was caused by a specific set of conditions -- the right things happening at the right time, in the right sequence, with a particular timing. To "trap" those conditions would require running both the client and server under a tracing debugger that recorded the time and "event" (e.g. method call, assignment, exception) of everything the system did and then allowed complex queries on the data produced. E.g. "How times per minute was update() called prior to isDead() returning true, on this instance?"
The data could perhaps be recorded using AOP. Next time we run into a scenario like this, it might be worthwhile to break out AspectJ or AspectWorkz. But analysing it will be tricky.
Recursive algorithms are always confuseing.
try {
int linux += microsoft;
} catch (Exception error) {
System.out.print("Looks like I am not going home tonight");
}
Webmaster of Infoweb
I think this can be seen as a kind of relative debugging. It can be used during algorithmic optimization as well. Of course, a version-control program such as Subversion is important when using such techniques.
I do not know about the best commercial tools in the 1980's. However, I didn't see many debuggers supporting watchpoints (stop when some data gets changed) before late 90s, and you didn't mention it explicitly, so I hope someone would tell me when did watchpoints become commercially available.
Clarification: BWK does not use debuggers.
OK, he has used them, and will on very rare occasions use them, but that's a far cry from the understated mentality expressed here.
Programmers who know their code do not need debuggers. They know where the errors are because they know their code.
Read BWK again and read him more carefully this time. And spend more time writing your own fricking code so you don't become another video arcade loser. The only programmers who finish assignments are the ones who don't need and don't use debuggers. As for anyone else - look at the deplorable state of affairs today.
People like you are wasting our time: wasting it with shitty products, puerile attitudes, suckee lack of talent, and just generally being the worst possible definition of a visitor to Slashdot.
Eat shit and die, but leave your office computer password on a post-it on the screen to minimise the clean-up after they tow your carcas out the door.
In high-level languages, you usually don't have memory-allocation or buffer-overflow problems, but quite often there are other traps. In Perl, numerous gotchas are mentioned in the manual. In Python, unexperienced developers often make shallow copies of lists when deep copies are needed. In Lisp, beginners often accidentally modify quoted lists in program sources, and they may write macros that captures variables. In Haskell, hastily-written programs may leak memory because of incorrect handling of laziness. I can't quickly think of an OCaml example, but at least it is easy to get hard-to-find typing errors during compile time if you are not careful... As for Java, I bet lots of beginners write applets that locks up randomly because they are not well aware of AWT/Swing threading issues.
All these, like memory problems in C/C++, are avoidable if the gotchas of the language is well taught and learnt --- and indeed they are mentioned on most books about the language. However if people happen to forget one of these, they will all lead to very hard-to-find bugs. So in this respect, you need self-discipline when programming with present-day languages, even high-level ones.
A problem with functional languages is that they are quite hard to learn (which also makes them interesting if you like computer science). One have to read quite a number of CS papers if he wants to use Haskell well (otherwise he will see cryptic type errors if he tries to do anything advanced, or if he did anything wrong). C is much easier in this respect, and even C++/Perl aren't that hard --- they are just complex.
the best disassembler but also a great debugger, it can
graph function flows, display pentium microcode, supports
nearly every processors on the market (including your car's
CPU.) Works nice with linux ELF binaries, etc... It is used by most antivirus researchers, crackers (who remove software protections), reverse engineers, hackers (who write exploits), etc. It runs perfect under wine without tweaking. Grab the
demo and give it a go.
Also under windows, SoftICE, is also an excellent debugger which lets you assemble in place and do many other neat things.
Under linux, people have been trying to make SoftICE look-a-like debugger, such as LinICE, etc. and gdb is quite a powerful tool and is scriptable.
If your debugging statement #ifdef'd in, it is completely removed during compile. Is your debug_print() completely optimized out?
"[Regarding the 'cloud,'] ownership was what made America different than Russia." -- Woz
run the same daemon in 20 machines, so it will crash 3 times a day, or run 20 instances on the same machine if you can
.NET users should check out the Trace and Debug classes. They are extremely useful as trace logging can be configured via an App.Config file to log to a sql database, file, etc. ASP.NET users should also check out http://yoursite/Trace.axd to get a full stacktrace or program execution of the last few visits to your site. You will never need to reproduce a bug yourself again they the full exception is already logged.
While this seems like an interesting way to debug code you have ported between systems, I have this tendency to expand existing software. Or write new software.
Where this is not terribly useful.
Besides.. have you looked at the background graphics on the left. I hardly dare to admit that I know this, but it *is* VB code. Great advert. Urgh.
Free PC version of ChipWits at http://www.breueronline.de/klaus/chipwits/
If you're working in Java, you should add assertions to your toolkit.
Amen to that; that goes for any number of development environments that have assertions or pre/postconditions as an available construct.
I use assertions anywhere that a violation of the condition would be a programming error, instead of a configuration or input error. Pass in null into the constructor? No! Pass in a negative number for the distance? No! Use this resource object after return_to_pool() has been called on it? Nuh uh!
Just having assertions be a gatekeeper for "impossible" circumstances catches a lot of those very selfsame "impossibilities".
I do a lot of concurrent programming these days, so the rules count triple. It's almost always something stupid, too, like using the ID of a constant instead of the value of the constant (in COM programming, varNull instead of Null), or forgetting to declare support for an interface. Adding assertions to other peoples' code can really track down anomalies pretty fast - someone's red/black tree algorithm had some cases where the root object (which should have no parent) got assigned, causing all sorts of subtle mayhem.
Along with this, I would highly recommend unit testing - highly. Based on the number of failed unit tests I've had over the past year, some twenty very subtle bugs would be haunting me still. Now, at least to a much higher degree than otherwise possible, I can "prove" that my objects and algorithms work, at least as advertised.
Unit tests also really help out when swapping out algorithms. I swapped a list for a much faster red/black tree (was doing thousands of ID lookups). Two unit tests failed - and both had to do with what happens if your object ID matches a blank ID. Easy to fix, but I wouldn't have had a clue.
Unit test also help out when porting. I have a fairly hefty framework in Delphi. Porting that to .NET took about a week to get working - a few days for the syntax changes, a couple extra to get green lights on the unit tests again.
Unit tests are, unfortunately, one of those things that you can agree with in principle, but figuring out how to do useful ones that don't seem like makework takes a leap. A small leap in retrospect, but a leap that can be hard to find nonetheless. Techniques for cutting down repetition, emphasis on negative unit tests (i.e. equally important to explicitly state that you can't do X), creating mock objects that record actions to compare against expectations, all are techniques that are a little hard to extrapolate from simple examples.
Assertions and unit testing might not be "debugging tools" in and of themselves, but they certainly help prevent bugs from slipping in unnoticed.
Binary geeks can count to 1,023 on their fingers
GNUTLS (www.gnutls.org) is a LGPL'd library, originally done because the OpenSSL license is uncompatible with the four freedoms of GPL and they don't want to change that. However, it is great quality, and from the developers of GPG so they know their crypto very well! I would say it's better in every way, plus it has an alternative OpenSSL-compatible-API for the switchers.
The big deal with Smalltalk (and similar systems) is that if you have an error (even one you have not prepared for with an exception handler). The system will stop with a traceback and allow any operation to be executed, any code to be compiled. Visual Studio is typical Microsoft: You can do some things in a suspended application, but not others. You can't recompile the module where the error occurred and restart, for example. Java 1.4 is part of the way there, with hot-swappable code, but you still only discover errors because of a traceback after the process has terminated.
Once someone have used a good Smalltalk-type environment in which everything is possible at any time, and with refactoring tools as standard, they usually come to realise that products such as Visual Studio are limiting and primitive.
-- Ed Avis ed@membled.com
To hype anything as 'new' and 'revolutionary' is silly, since as we all know, nothing new has been invented since 1970. But there are certainly techniques which aren't as popular as they deserve to be. One is Delta Debugging as implemented by delta (a tool to automatically produce minimal test cases) and Ask Igor.
-- Ed Avis ed@membled.com
thank you very much for this information. I must standarize my notation somehow. Most problems I have are just because I'm not aware what types should I distinguish.
I have more questions if you don't mind.
what is your convention for giving class/struct/typedef names? (I tend to use prefix class_ struct_ type_ respectively - is that enough or something more sparks to your mind?)
some hints about function names? (or just what do you prefer `foo_bar()` or `FooBar()` ? I just cannot make a legitimate decision on this topic)
do you use c++ namespaces? (I almost always start my c++ program with 'using namespace std;') If yes, then what names do you give to them?
#
#\ @ ? Colonize Mars
#
Simply release the product, and let the support group start recording the issues. Keep paying a stable of programmers, and eventually, like monkeys on typewriters, they'll hammer out version 2.0. Rinse, Repeat.
Bidirectional debugging allows you to step forward and backward one line of code at a time.
A paper published in PLDI 2000 describing this is available here. IIRC at some point they had an implementation of this built on top of gdb.
Linux Magazine issue of April 2004 covered a new debugging technique that has been used on the 2.6 kernel, called 'source code analysis'. the program used, called smatch, and all the documentation needed can be found on sourceforge here.
its main focus is the kernel, but it should be easy enough to adapt to other programs. not a debugger in the true sense of the word but it will detect a lot of bugs for you which you might otherwise have to hunt down with a debugger.
On a long enough timeline, the survival rate for everyone drops to zero.
run ulimit -c unlimited before running the daemon. gdb a.out core. If it doesn't crash in a way that dumps core, i.e. it just exits with an error code, you could run it under ltrace or strace. You'll have giant trace files if you do that, though. You could exclude some system calls from tracing, such as gettimeofday.
#define X(x,y) x##y
Peter Cordes ; e-mail: X(peter@cordes ,
Don't overdo it. The question you need to ask yourself is, "what information do I really need when _reading_ the code?" I find I am usually interested in scope, but not really in type (the question I want answered is "where the hell did they declare that?" rather than "hmm, I wonder if it is a bool or an int?"). In particular, if something has class-scope I want to know, so like the original poster I use a prefix for those. I do not use enough static or global variables for them to ever become an issue, so I do not prefix those.
Of course when you are _reading_ code it is often other peoples' code, leaving you at their mercy, prefix-wise. Typically the nastiest code will also use misleading prefixes...
what is your convention for giving class/struct/typedef names? (I tend to use prefix class_ struct_ type_ respectively - is that enough or something more sparks to your mind?)
I would say it is too much already. The difference between a class and a struct is too insignificant to worry about. I consider typedefs just a clever-looking way to obfuscate code that is otherwise perfectly clear, so as a general rule I steer away from them (whatever is wrong with just using "int" when you want an integer? I understand things like "uint32", but making integers look like structures ("csw_status_t") is just plain annoying...)
some hints about function names? (or just what do you prefer `foo_bar()` or `FooBar()` ? I just cannot make a legitimate decision on this topic)
Whichever you can type the quickest ;-) I hate those underscores, it messes up my typing, so I use BiCaps. And try to use names that makes your program look like normal prose - when you see "cfg_hw" I have no clue what it does, while "ConfigureHardware" gives me a good idea. Same goes for variable- and other names of course.
do you use c++ namespaces? (I almost always start my c++ program with 'using namespace std;') If yes, then what names do you give to them?
I do not usually use them. Whether or not that is smart I haven't figured out yet, despite 6 years intensive C++ programming (and earning the title of "C++ guru" within my company).
If I typo with an ifdef, my compiler doesnt report what _really_ went wrong. I'm not developing for mobile platforms, so I don't need the absolute greatest efficiency - hell, thats why I'm not writing in assembler. And what about languages other than the c family, that dont support precompile directives? This is a more 'language agnostic' solution.
Or, a little from column a, a little from column b
^_^
Yay me!