views:

168

answers:

8

I don't like debugging in a debugger, because I think it is often below the abstraction layer of the programming language and it is often not reproducible. I favor using unit tests when possible and I think they are a good way, but it is not always that easy to implement them. Do you know about any other alternative approaches to avoid the use of a debugger?

+5  A: 

Think! :) - Seriously, though - before debuggers were commonly available, most people would troubleshoot their code by writing lots of state information to their output device. It still works.

500 - Internal Server Error
Sure that is nice, but "print" statements clutter the code and I am not aware of any reasonable policy for editing them as the code changes. One greate benefit of unit test over this approach is that the code of tests is separated from the actual program code.
Gabriel Ščerbák
@Gabriel You wouldn't leave `print` statements in the code forever. You put them there while you're hunting a bug, and take them out when you're satisfied your code is bug free. Alternatively, you employ a logging facility that you can switch off in production. Unit tests help you confirm that your code works as it should, but it's hard to trace bugs with them.
deceze
The problem for me is that often I don't know where exactly the bug happens and I add "print" statements gradually to locate the bug. After localising it I may have quite a lot of those statements at different places which gets difficult to manage, especially when the bugs are combined and removing one does not mean I can remove those statements. It is hard then to decide at what time can I safely remove "print" and where to fing all of them. Yet another thing is that once I find one bug and want to remove it, I have to deal with cluttered code, which does not help fixing the bug.
Gabriel Ščerbák
@Gabriel That's where the *thinking* part comes in. ;-) Also, using a version control system and a diff tool, you can check all changes since the last commit to find debug statements.
deceze
I *think* there should be a way to do it better:)
Gabriel Ščerbák
It still works, but why not learn how to get the debugger to do it for you, or use Aspect Oriented Programming? Just because 30 years go all people had where print statements then there is nothing better today!
hhafez
+3  A: 

Any non-trivial application should have some sort of logging/tracing facility built in to it. You'll want to be able to turn on/off various kinds of output.

It is especially helpful to log all input. It is a lot easier to reproduce issues if you can "play back" the input.

Kristopher Johnson
Yeah, and that is exactly the problem for me. Logging is pretty similar to using "print" statements in a way that those both approaches impose change on existing code or even its design. The problem with logging is to decide what do you want to see in the log. Often it is quite an effort to find relevant information in logs.
Gabriel Ščerbák
Logging is just a step above using print statements. If used properly, you can leave behind log statements that are likely to be useful in the future, but disable them by setting a higher log level. Then when you have another problem, you turn them back on. But in practice, I've rarely the log messages created for the previous problem to be useful for the current problem, so I think the gain is fairly small. I've used log4j some in Java and the main advantage I've gotten from it is that it can automatically add some extra info and save some typing.
Jay
Design your logging mechanism so that it is easily "greppable". Make sure all log statements have common format for date/time, info/warning/error/debug, subsystem name, etc.@Jay's point about logging usually being useful for fixing previous issues but not the current issue is very true. As part of my refactor-after-getting-it-to-work phase, I try to eliminate any logging statements that seem like they would be unlikely to have future value.
Kristopher Johnson
+1  A: 

Personally, I rarely use a debugger myself. I generally find that I spend too much time single-stepping and generally messing with the mechanics before I get to the part that matters.

My favorite method of debugging is to simply add print statements at strategic places. Like, if a calculation is coming up with the wrong answer, I might add print statements to dump the values that go into the calculation.

In Java I'll normally write something like

System.out.println("In function foo: bar="+bar+", plugh="+plugh);

If the problem appears to be what path the program took through the logic, I'll add statements that print "about to enter sales calc block" or "finished writing to database" or whatever.

I only resort to a debugger when I have no clue what the problem is and I can think of nothing better than to go through every single step to see where it went wrong.

Jay
The approach which I use most of the time is pretty similar to yours. However I find it pretty clumsy, especially when it comes to managing those "print" statements in different methods.
Gabriel Ščerbák
Sure. But it's less clumsy than any other method. I might add that I often precede my print statements with a comment line that reads "// debug" (or whatever the format is for comments in the given language) so I can easily find them and remove them when I'm done.
Jay
PS A pet peave of mine is when programmers leave such debugging messages behind when they move code to production, messages which were clearly intended to find a specific bug and are unlikley to have much value in finding future bugs. Then you got log files filled with junk. Then for the next debugging session they start printing a line of asterisks or equal signs around their messages so they can find them amidst all the junk. THen they leave that in, so the next guy puts five blanks lines and TWO rows of asterisks, etc.
Jay
+1  A: 

one strategy is to develop a "debug" mode, in which the application exposes several useful info, like used resources, http variables, logs, sql statements etc...

For example, I have a web app that manages the web page state thru hidden inputs, and that interacts with the database with xml messages...

when I appen a "debug=1" to the url, all hidden inputs are shown in a collapsable box, and also the executed xml-in and xml-out...

more over, in debug mode I can double-click on certain spot of the page to see the database configuration info, and there's also a shortcut to "impersonate" users, so as to test the permissions...

one of the last additions is a collapsable box that shows me the time it tok to build the whole page, and the time it took to access the database

on the database side, we process that xml and generate several sql statements. If I append an xml tag <show-sql>1</show_sql> instead of executing the query I get the sql statement... so I can "debug" the stored procedure from the sql console...

you can develop all these things in a couple of days, and I can assure you it really pays off...

to get and idea of what I'm talking about, you can have a look at the developer front end proveide by the symfony framework

http://www.symfony-project.org/book/1_0/16-Application-Management-Tools

it let's you inspect several things, like logged info, request and response contents, actions executed, etc...

in short, prepare your app to be easyble inspected...

opensas
Nice idea, but it means serious changes to the design and surely takes quite an effort to develop reasonably. Yet there are security issues regarding such an approach.
Gabriel Ščerbák
I have to say that I fully agree with your last statement, but I would rather than developing such an infrastructure think of using some reflective programming language, which will let me do much inspections "out-of-the-box". What is your opinion?
Gabriel Ščerbák
security issues - completely agree... we have all this stuff tied to development evironment only, of course...
opensas
In my case all these little tools came from our own needs, and they evolved rather randomly, to "scrath and itch" I may say. Looking backward I realize how useful they are, and next time I develop a framework I will sure take it into account... just like the guys at symfony did
opensas
"reflective programming language"... how would that be? could you expand on that idea? it might be interesting
opensas
+4  A: 
  1. Testing != Debugging != Logging
  2. Avoid using a debugger is not realistic. Eventually you have to learn certain debugging techniques to fix difficult bugs.
Lei
1. You are right and I fully agree.2. Can you give a minimalistic example?
Gabriel Ščerbák
One example is when you want to understand why a certain code path is invoked while the program inputs seem not suggesting so. A debugger can easily show you the calling stacks as well as all the condition variables on every path, without writing any code!A debugger is also a must in case you want to debug some code that is hard get the original build system to reproduce/change the binaries (e.g. standard libraries, or a 3rd party library)
Lei
One example where using a debugger is neccesary, the customer comes to you and says your application core dumped. and when you look at the traces you realise that the application quited before It got to the useful logging. And the to top it all up the customer says it's random and they don't know what sequence of events lead to it.PLease solve that without a debugger. By the way this is not a hypotehtical situation :)
hhafez
in my experience, debugging is also unavoidable to understand complex pieces of code, like peeping into a new framework...
opensas
+1  A: 

Print linestm I think is by far the most used way to debug.

You know:

  log("Starting transaction for client id %s", id )

Always make sure you use a good logging framework, otherwise your logs will become useless.

OscarRyz
Ok, most used, you have to make sure to use it right... Isn't there a better way?
Gabriel Ščerbák
You mean like voodoo programming? ;)
OscarRyz
+4  A: 

Design by contract, unit tests and these kinds of things help you ensure that a block of code, most often a function, works as it should. It's good for that, but it doesn't replace debugging.

Debugging means to find out why a block of code doesn't behave as it should. A unit test can only tell you that a block doesn't work as it should, it can't tell you why. To find out why, you have to step through the code line by line, tracing where the state differs from what you expect or where the logic branches unexpectedly. To trace these things, you need to inspect the state at any given time. You can do this with print statements, using them to get variable states or to infer logic branching. A debugger is a tool that helps you do both without changing the code.

You'll have to use either one. Even a unit test is basically a print statement that's automatically checked for correctness, just at a different level of granularity.

You seem to want to make code fix itself without looking at it. You can put tests and contracts around your code all you want, if these things tell you there's a bug in your code, eventually you'll have to look at it. If you want to inspect the code without altering it by putting in print statements, the only tool left is the debugger. Unless there's a major breakthrough, that's the state of the art.

deceze
+6  A: 

A debugger is a tool saying I want to avoid a debugger is like a carpenter saying I want to avoid a hammer. You are better of asking the question When should I avoid using the debugger?

Times when you should avoid using the debugger,

  • When you have no idea what the code is doing, understand first then try debugging otherwise your wasting your time
  • The issue is timing related (raceconditions, problems with IPC or threads), if it is a timing condition and you start single stepping, then you might not reproduce your problem.

I am sure there are other times when using a debugger is a bad idea, but my point is your question is wrong. Don't ask how do I not use the debugger but ask when should I/shouldn't I use the debugger.

my 2cents

hhafez
I am not asking about how to use a debugger, I am asking if there is a better way. I am asking if a carpenter can use something better than a two-handed saw, I am asking if maybe there is an electric saw. The problem with stepping through is for me that it has potentially linear complexity - you eventually step through the whole execution path of a buggy code. Even with "print" statements you can get logarithmic. For a debugger to be logarithmic, you would need breakpoints.
Gabriel Ščerbák
If you are using your debugger just by constantly stepping, then either you don't know where to look so you are randomly looking till you find something or you don't know how to use the advanced features in your debugger (watchpoints, conditional breakpoints, code exucation, disasembly etc..., local variable inspection). If you are forced to randomly step through code, atleast do it intelegently like by using binary search to save your time
hhafez
I understand debugging as having a system or its part of which I have assumptions how it works and a bug is when some assumption is broken, then process of finding what assumption it was and where it was and how to correct it is debugging. So when you have bug, you don't know exactly where, but you might have enough information to reproduce it. However with debugger to know where to serach for the bug or to do the binary search stepping, you must first know which execution path the code has taken, which means stepping through it all. With tests you can use test coverage tools to see the path.
Gabriel Ščerbák
You have no hope in the world finding the execution path by single stepping in any complex application (100kto 1000k loc) you need to have an idea what you are looking for and look around that using the debugger, But to use the debugger to understand your code? You are probably wasting your time
hhafez