Historical debugging - debugging

I have to debug a very big program, which takes around 10 minutes until it reaches the most important debugging state. I just want to modify some values in this part of the program, but sometimes I would like to go back and modify them again, like travelling to the past. As far as I know it is called historical debugging. Reading some info it seems it has been implemented in Visual Studio 2010. But I only use Eclipse or Xcode or vi :)
I wonder if know some other software with these capabilites.
By the way, I ask about your opinion, do you think it will be possible, once I reach this state of my program, to modify some small part of the code, after the breakpoint, and compile it again (supposing it does not affect to the past execution and code) so I can test it without recompiling?
Thanks

Also discussed here
Mostly only available for interpreted languages like Java (ODB). And I am not sure if you can continue from some point of execution with changed data.
Have you tried to set watch point in Eclipse that would break as soon as a variable reach a specific value? This means your code executes normally until it breaks your data and execution stops so that you can see how you code got to this point.

Related

CICS/COBOL Abend ASRA in debugger only

I have an issue I don't seem to find a solution for.
One of the transactions gives ABEND ASRA when used in debug mode.
When I compile the Cobol program without debug option and run the program, it works fine.
The error looks like this one (quite exactly like this), only I am using Cobol V4:
http://www-01.ibm.com/support/docview.wss?uid=swg1PM96501
Now the question would be: why is it abending in debugger and not without debugger?
I am using the CICS debugger (DTCN transaction), the program starts normally, I can do steps with F2 and all this, then at some location is abends.
Please note that it is extremely difficult to say where it abends as the program is really big.
This happens only to this program at the moment, others are running fine with debugger. I placed a breakpoint before my modifications, the abend occurs in some other area.
Another weird thing is that this Abend is not consistent, If I do a big portion of the code with small steps (F2 and small breakpoints), sometimes it executes without abend until the end.
Due to the nature of the issue, I can not post much information.
I was hoping you encountered similar issues and you can tell me where to look for.
Thank you!
The issue was solved by deleting my debug tool profile form the system and then login to the debugger (DTCN) again so it creates a new profile (the profile was 3 files: TOOLTEMP.PDTOOLS.{userid}.DBGTOOL.* ). After this the issue was gone. I asked the guys how this happened, they told me this was because I had modified the program between 2 debugging sessions without closing CICS. This was a disfunction that can be avoided by closing CICS while we compile programs used in it (not sure about why exactly.... neither are they).
Hope this helps if you face a similar issue with DTCN debugging.

Better Breakpoints for Multi-Client Development

My development system uses different clients for development and testing which I assume is a common practice. Unfortunately this introduces a rather annoying convenience issue when it comes to debugging. While breakpoints placed on the development system will stick to their code and move as lines are inserted or deleted, this is rather obviously not the case for breakpoints placed on the same code in another client.
Since the system has no knowledge of exactly how rows were changed between two versions, breakpoints placed in the testing client will remain at a particular line in the program. Any change to the code will therefore break the breakpoints. To resolve this I have to: open another program or screen then return to the program to refresh the code (where's the refresh button SAP?), find where the breakpoints have been moved to and remove them one-by-one (where's the batch remove breakpoints button SAP?) and then set new breakpoints at usually the exact same location.
This problem is becoming so frequent in my work that I sometimes spend more time moving breakpoints than I spend on the actual development. In some cases I just gave up and started coding in user breakpoints since those will at least remain in place. However, these come with their own drawbacks as they can't be removed in the debugger, making them useless when you are forced to stop at every breakpoint in a thousand-record loop.
My actual question is now whether there's a better approach or best practice when it comes to debugging in this scenario. I'm relatively new to ABAP programming so I hope that more experienced developers have alternatives or tricks that they use to speed this process up. Is there some better way to go about debugging and breaking code in a secondary client?
You could try creating a checkpoint group in transaction SAAB, and code the break-points to the checkpoint group.
Syntax
BREAK-POINT ID zyour_new_checkpoint_group.
This has the advantage that you can activate it for a set time, or a set of users etc. However, I'm not sure that if you get stuck in a 1000-line loop that you will be able to just deactivate it & skip over the break-point.
It may be worthwhile to check first if you can deactivate the checkpoint group on the fly while the program is running before using this in anger.
The practice of having a development client and test client makes sense for client dependent objects, e.g. customizing. It ensures a reasonably stable environment for development testing. But it makes no sense for programs and other development objects since they are client independent. However, it is still important that all your client dependent development objects (e.g. standard texts and SapScripts) originate from the development client so it is best to create all your objects there. But once you have done that and are on to testing and debugging there is no technical reason to not just change your program in the test client.
It might take some effort to convince the people responsible for development procedures of this practice since there always is a chance that objects get created in the wrong client which could lead to a mess when you want to release them. But with the scenario you describe in your question you should be able to plead your case.

General strategy for finding the cause of random freezes?

I have a application which randomly freezes, including the IDE and it's driving me mad. That makes me wonder:
What's a good general strategy for finding the cause of random freezes?
If you are wanting to check from outside of a running app then I would potentially use the sysinternals.com toolset from Mark Russonivich, the perfmon tool allows you to trace file / registry access and check the trace for delays - and what is being accessed at that time. It will show the DLL call stack at that time with the right symbols can is useful for debugging problems external to an application that are causing delays. (I've used it to find out that an I/O filter associated to a security suite was the reason an application was piccking up a number of 1.5sec delays.)
If you're lucky, you can run your code in a debugger until it freezes, then stop the debugger to find the offending line of code. But if it were that easy, you probably wouldn't be asking for advice. :-)
Two strategies that can be used together are to "divide and conquer" and "leave bread crumbs."
Divide and conquer: Comment out increasingly larger portions of your code. If it still freezes, you've reduced the amount of code that might be responsible for causing the freeze. Caveat: eventually you'll comment out some code and the program will not freeze. This doesn't mean that last bit of code is necessarily responsible for the freeze; it's just somehow involved. Put it back and comment out something else.
Leave bread crumbs: Make your program tell you where it is and what it's doing as it executes. Display a message, add to a log file, make a sound, or send a packet over the network. Is the execution path as you expected? What was the last thing it was doing before it froze? Again, be aware that the last message may have come from a different thread than the one responsible for freezing the program, but as you get closer to the cause you'll adjust what and where the code logs.
You're probably doing things in the UI thread when you shouldn't be.
I would install the UserDump tool, and follow these instructions for generating a user dump of the application....
Once you have the user dump, you can use WinDbg, or cdb to inspect the threads, stacks, and locks, etc.
Often I find hangs are caused by locked mutexes or things like that.
The good general strategy is, run the program until it hangs. Then attach a debugger to it and see what's going on. In a GUI program, you're most interested in what the UI thread is doing.
You say the application hangs the IDE. This isn't supposed to happen, and I imagine it means the program is putting so much strain on the OS (perhaps CPU load or memory) that the whole system is struggling.
Try running it until it hangs, going back to the IDE, and clicking the Stop button. You may have to be really patient. If the IDE is really permanently stuck, then you'll have to give more details about your situation to get useful help.

Why is debugging better in an IDE? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 years ago.
Improve this question
I've been a software developer for over twenty years, programming in C, Perl, SQL, Java, PHP, JavaScript, and recently Python. I've never had a problem I could not debug using some careful thought, and well-placed debugging print statements.
I respect that many people say that my techniques are primitive, and using a real debugger in an IDE is much better. Yet from my observation, IDE users don't appear to debug faster or more successfully than I can, using my stone knives and bear skins. I'm sincerely open to learning the right tools, I've just never been shown a compelling advantage to using visual debuggers.
Moreover, I have never read a tutorial or book that showed how to debug effectively using an IDE, beyond the basics of how to set breakpoints and display the contents of variables.
What am I missing? What makes IDE debugging tools so much more effective than thoughtful use of diagnostic print statements?
Can you suggest resources (tutorials, books, screencasts) that show the finer techniques of IDE debugging?
Sweet answers! Thanks much to everyone for taking the time. Very illuminating. I voted up many, and voted none down.
Some notable points:
Debuggers can help me do ad hoc inspection or alteration of variables, code, or any other aspect of the runtime environment, whereas manual debugging requires me to stop, edit, and re-execute the application (possibly requiring recompilation).
Debuggers can attach to a running process or use a crash dump, whereas with manual debugging, "steps to reproduce" a defect are necessary.
Debuggers can display complex data structures, multi-threaded environments, or full runtime stacks easily and in a more readable manner.
Debuggers offer many ways to reduce the time and repetitive work to do almost any debugging tasks.
Visual debuggers and console debuggers are both useful, and have many features in common.
A visual debugger integrated into an IDE also gives you convenient access to smart editing and all the other features of the IDE, in a single integrated development environment (hence the name).
Some examples of some abilities that an IDE debugger will give you over trace messages in code:
View the call stack at any point in time, giving you a context for your current stack frame.
Step into libraries that you are not able to re-compile for the purposes of adding traces (assuming you have access to the debug symbols)
Change variable values while the program is running
Edit and continue - the ability to change code while it is running and immediately see the results of the change
Be able to watch variables, seeing when they change
Be able to skip or repeat sections of code, to see how the code will perform. This allows you to test out theoretical changes before making them.
Examine memory contents in real-time
Alert you when certain exceptions are thrown, even if they are handled by the application.
Conditional breakpointing; stopping the application only in exceptional circumstances to allow you to analyse the stack and variables.
View the thread context in multi-threaded applications, which can be difficult to achieve with tracing (as the traces from different threads will be interleaved in the output).
In summary, print statements are (generally) static and you'll need to re-compile to get additional information if your original statements weren't detailed enough. The IDE removes this static barrier, giving you a dynamic toolkit at your fingertips.
When I first started coding, I couldn't understand what the big deal with debuggers was and I thought I could achieve anything with tracing (granted, that was on unix and the debugger was GDB). But once you learn how to properly use a graphical debugger, you don't want to go back to print statements.
An IDE debugger lets you change the
values of variables at run-time.
An IDE
debugger lets you see the value of
variables you didn't know you wanted
to see when execution began.
An IDE
debugger lets you see the call stack
and examine the state of the
function passed weird values.
(think this function is called from
hundreds of places, you don't know
where these weird values are coming
from)
An IDE debugger lets you
conditionally break execution at any
point in code, based on a condition,
not a line number.
An IDE debugger will let you examine the state of the program in the case of an unhandled exception instead of just crapping out.
Here's one thing that you definitely cannot debug with "print" statement, which is when a customer brings you memory dump and says "your program crashed, can you tell me why?"
Print statements all through your code reduces readability.
Adding and removing them for debug purposes only is time consuming
Debuggers track the call stack making it easy to see where you are
Variables can be modified on the fly
Adhoc commands can be executed during a pause in execution to assist diagnosing
Can be used IN CONJUNCTION with print statements : Debug.Write("...")
I think debugging using print statements is a lost art, and very important for every developer to learn. Once you know how to do that, certain classes of bugs become much easier to debug that way than through an IDE. Programmers who know this technique also have a really good feel of what's useful information to put in a log message (not to mention you'll actually end up reading the log) for non-debugging purposes as well.
That said, you really should know how to use the step-through debugger, since for a different class of bugs it is WAY easier. I'll leave it up to the other excellent answers already posted to explain why :)
Off the top of my head:
Debugging complex objects - Debuggers allow you to step deep into an object's innards. If your object has, say, an array of array of complex objects, print statements will only get you so far.
The ability to step past code - Debuggers will also allow you to skip past code you don't want to execute. True, you could do this manually as well, but it's that much more code you have to inject.
As alternative to debug in IDE you can try great Google Chrome extension PHP Console with php library that allows to:
See errors & exception in Chrome JavaScript console & in notification popups.
Dump any type variable.
Execute PHP code remotely.
Protect access by password.
Group console logs by request.
Jump to error file:line in your text editor.
Copy error/debug data to clipboard (for testers).
I haven't been developing for nearly 20 years, but I find that using a IDE / debugger I can :
see all kinds of things I might not have thought to have included in a print statement
step through code to see if it matches the path I thought it would take
set variables to certain values to make code take certain branches
One reason to use the IDE might be that modern IDEs support more than simple breakpoints. For example, Visual Studio offers the following advanced debugging features:
define conditional breakpoints (break only if a condition is met, or only on the n-th time the statement at the breakpoint is executed)
break on an unhandled exception or whenever a (specific) ecxeption is to be thrown
change variable while debugging
repeating a piece of code by setting the next line to be executed
etc.
Also, when using the debugger, you won't have to remove all your print statements once you have finished debugging.
In my experience, simple printouts have one huge advantage that no one seems to mention.
The problem with an IDE debugger is that everything happens at real time. You halt the program at a certain time, then you step through the steps one at a time and it is impossible to go back if you suddenly want to see what happened before. This is completley at odds with how our brain works. The brain collects information, and gradually forms an oppinion. It might be necessary to iterate the events several times in doing so, but once you have stepped past a certain point, you cannot go back.
In contrast to this, a selected series of printouts/logging gives you a "spatial projection of the temporal events". It gives you a complete story of what happened, and you can go back and fourth several times very easily by just scrolling up and down. It makes it easy to answer questions like "did A occur before B happened". It can make you see patterns you wernt even looking for.
So in my experience. IDE and debuggers are fantastic tools to solve simple problems when something in one single call-stack went wrong, and explore the current state of the machine at a certain crash.
However, when we approach more difficoult problems where gradual changing of state is involved. Where for example one algorithm corrupted a data structure, that in turn caused anohter algorithm to fail. Or if we want to answer questions like "how often do this happen", "do things happen in the order and in the way as I imagine them to happen". etc. Then the "old fashined" logging/printout technique has a clear advantage.
The best things is to use either technique when it is most suitable, for example use logging/printouts to get to some bugs, and pause at a breakpoint where we need to explore the current state more in detail.
There are also hybrid approaches. For example, when you do console.log(object) you get a data-structure widget in the log that you can expand and explore more in detail.This is many times a clear advantage over a "dead" text log.
One thing that I'm surprised I haven't seen in another answer is that the 2 debugging methods are not mutually exclusive.
printf debugging can work quite nicely even if you're using a standard debugger (whether IDE based or not). In particular with a logging framework so you can leave all or most of in the released product to help with diagnosing customer problems.
As noted in pretty much all the other answers here, the key nice thing about a standard debugger is that it allows you to more easily examine (and potentially change) the details of the program state. You don't have to know up front what you might want to look at - it's all available at your fingertips (more or less).
Because debugging multi-threaded applications with print statements will drive you bananas. Yes you can still do it with print statements but you'd need a lot of them and unravelling the sequential print out of statements to emulate the multi-threaded executiong would take a long long time.
Human brains are only single-threaded unfortunately.
Since you asked for pointers to books... As far as Windows debugging goes, John Robbins has several editions of a good book on Windows debugging:
Debugging Applications for Microsoft .NET and Microsoft Windows
Note that the most recent edition (Debugging Microsoft .NET 2.0 Applications) is .NET only, so you might want an older one (like in the first link) if you want native code debugging (it covers both .NET and native).
I personally feel the answer is as simple as "A integrated debugger/IDE gives you a wealth of different information quickly without the need for punching in commands. The information tends to be there in front of you without you haven't tell it what to show you.
The ease in which the information can be retrieved is what makes them better than just command-line debugging, or "printf" debugging.
Advantages of a debugger over a printf (note not an IDE debugger but any debugger)
Can set watchpoints.
This is one of my favourite ways of finding memory corruptions
Can debug a binary that you can't recompile at the moment
Can debug a binary that takes a long time to recompile
Can change variables on the fly
Can call functions on the fly
Doesn't have the problem where debug statemenets are not flushed and hence timing issue can not be debugged acuratly
Debuggers help with core dumps, print statements dont'
This is what I use most on VS.NET debugging windows:
Call stack, which is also a great way to figure out someone else's code
Locals & Watches.
Immediate window, which is basically a C# console and also lets me change variable contents, initialize stuff etc.
The ability to skip a line, set the next statement to be executed somewhere else.
The ability to hover over variables and have a tool-tip showing me their values.
In summary, it gives me a 360 degree view of the state of my executing code, not just a small window.
Never found a book teaching this kind of stuff, but then again, it seems to be quite simple, it's pretty much WYSIWYG.
A debugger can attach to a running process
Often easier to debug threaded code from a debugger
With an IDE debugger you can see the values of ALL the variables in the current scope (all the way up the call stack) whenever you halt execution.
Print statements can be great but dumping so much information to the screen at any given place can produce a whole lot of print statements.
Also, many IDE debuggers let you type in and evaluate methods, and evaluate members while you are halted, which further increases the amount of print statements you'd have to do.
I do feel that debuggers are better for some languages than for others however...
My general opinion is that IDE debuggers are absolutely, amazingly wonderful for managed languages like Java or C#, are fairly useful for C++, and are not very useful for scripting languages like Python (but it could be that I just haven't tried a good debugger for any scripting languages yet).
I absolutely love the debugger in IntelliJ IDEA when I do Java development. I just use print statements when I use Python.
As someone said above: Debugger != IDE.
gdb and (back in the day) TurboDebugger (stand-alone) work just fine for the languages they support[ed], thank you. (or an even older technology: Clipper debugger linked into the xBase executable itself) -- none of these required an IDE
Also, though C/++ coding is more rare, printf statements sometimes mask off the very bug you are trying to find! (initialization problems in auto vars on the stack, for instance, or memory allocation/alignment)
Finally, as others stated, you can use both. Some real-time-ish problems almost require a print, or at least a judicious "*video_dbg = ( is_good ? '+' : '-');" somewhere into video memory. My age is showing, this was under DOS :-)
TMTOWTDI
In addition to much of what the other posters have said, I really like stepping through one line at a time along with the computer, as it forces me to think about one line at a time. Often I will catch the bug without even looking at variable values simply because I am forced to look at it as I click the 'next line' button. However, I don't think my answer will help you, Bill, because you probably have this skill already.
As far as learning resources go, I haven't used any -- I just explore all the menus and options.
Is this even real question from real programmer?
Anyone who spent even 5 mins debugging with print statements and debugging with IDE - it will OCCUR to him/her without even asking!
I've used both prints and IDEs for debugging and I would much rather debug using an IDE. The only time for me when that doesn't work is in time critical situations (like debugging online games) where you litter the code with print statements and then look at the log files after it has gone horribly wrong. Then if you still cannot figure it out, add more prints and repeat.
Just wanted to mention a useful feature of a console debugger vs printf and vs debugger in an IDE.
You can attach to a remote application (obvioustly, compiled in DEBUG mode) and inspect its state dumping the debugger output to a file using POSIX tee utility. Compared to printf, you can choose where to output the state in run-time.
It helped me a lot when I was debugging Adobe Flash applications deployed in an agressive environment. You just need to define some actions that print required state in each breakpoint, start the console debugger with fdb | tee output.log, and walk through some breakpoints. After that you can print the log and analyse the information by thorough comparison of the state in different breakpoints.
Unfortunatelly, this feature [logging to a file] is rarely available in GUI debuggers, making developers compare the state of objects in their head.
By the way, my opinion is that one should plan where and what to debug before staring a debugger.
Well another thing is that if you join a new old project and nobody really knows how the code is doing what it's doing, then you can't debug by echoing variables/objects/... b/c you have no idea what code is executed at all.
At my job I am facing exactly that kind of situation and visual XDebuging helps me getting an idea about what is going on and where, at all.
Best regards
Raffael
In addition to the many things that have been already mentioned, one of the most important advantages of a debugger over printf is that using printf statements assumes that you know in which function the bug resides. In many cases you don't, so you have to make a few guesses and add print statements to many other functions in order to localise it. The bug may be in framework code or somewhere far removed from where you think it is. In a debugger it is far easier to set breakpoints to examine the state in different areas of the code and at different points in time.
Also, a decent debugger will let you do printf-style debugging by attaching conditions and actions to breakpoints, so that you still retain the benefits of printf debugging, but without modifying the code.
Debugging in an IDE is invaluable in an environment where error logs and shell access are unavailable, such as a shared host. In that case, an IDE with a remote debugger is the only tool which allows you to do simple things such as view stderr or stdout.
A problem with using print statements is it makes a mess of your code. IE, you have a function with 10 parts to it and you know it crashes somewhere, but you're not sure where. So you add in 10 extra print statements to pinpoint where the bug is. Once you've found and solved your bug, you now have to clean up by removing all of those print statements. Maybe you'll do that. Maybe you'll forget and it'll end up in production and your user's console will be full of debug prints.
Wauw, do I like this question. I never dared to pose it...
It seems that people just have different ways of working.
For me what works best is:
Having a solid mind model of my code, including memory management
Using instrumentation (like print statements) to follow what's happening.
I've earned my living programming for over 40 years now, working at non-trivial technical and scientific applications in C++ and Python daily, and I have the personal experience that a debugger doesn't help me a bit.
I don't say that's good. I don't say that's bad. I just want to share it.
It's not just debugging. An IDE helps you build better software faster in a lot of ways:
refactoring tools
intellisense to make api's more discoverable, or remind of exact spelling/case of familiar items(not much use if you've used the same system for 15 years, but that's rare)
save on typing by autocompleting variable and class names
find certain kinds of errors before you even start to compile
Automatically jump to variable/method/class declarations/definitions, even if they're not in the same file or folder.
Break on unhandled and handled exceptions
I could go on.

What is the most challenging development environment you've ever had to work in and what did you do to get around the limitations?

By 'challenging development environment' I don't mean you're on a small boat that's rocking up and down and someone is holding a gun to your head. I mean, are the tools at your disposal making the problem difficult?
Development is typically a cycle of code, run, observe the result, repeat. In some environments this is a very quick and painless process, but in others it's very difficult. We end up using little tricks to help us observe the result and run the code faster.
I was thinking of this because I just started using SSIS (an ETL tool included with SQL Server 2005/8). It's been quite challenging for me to make progress, mainly because there's no guidance on what all the dialogs mean and also because the errors are very cryptic and most of the time don't really tell you what the problem is.
I think the easiest environment I've had to work in was VB6 because there you can edit code while the application is running and it will continue running with your new code! You don't even have to run it again. This can save you a lot of time. Surprisingly, in Netbeans with Java code, you can do the same. It steps out of the method and re-runs the method with the new code.
In SQL Server 2000 when there is an error in a trigger you get no stack trace, which can make it really tricky to locate where the problem occurred since an insert can have a cascading effect and trigger many triggers. In Oracle you get a very nice little stack trace with line numbers so resolving the problem is very easy.
Some of the things that I see really help in locating problems:
Good error messages when things go wrong.
Providing a stack trace when a problem occurs.
Debug environment where you can pause, then output the value of variables and step to follow the execution path.
A graphical debug environment that shows the code as it's running.
A screen that can show the current values of variables so you can print to them.
Ability to turn on debug logging on a production system.
What is the worst you've seen and what can be done to get around these limitations?
EDIT
I really didn't intend for this to be flame bait. I'm really just looking for ideas to improve systems so that if I'm creating something I'll think about these things and not contribute to people's problems. I'm also looking for creative ways around these limitations that I can use if I find myself in this position.
I was working on making modifications to Magento for a client. There is very little information on how the Magento system is organized. There are hundreds of folders and files, and there are at least a thousand view files. There was little support available from Magento forums, and I suspect the main reason for this lack of information is because the creators of Magento want you to pay them to become a certified Magento developer. Also, at that time last year there was no StackOverflow :)
My first task was to figure out how the database schema worked and which table stored some attributes I was looking for. There are over 300 tables in Magento, and I couldn't find out how the SQL queries were being done. So I had just one option...
I exported the entire database (300+ tables, and at least 20,000 lines of SQL code) into a .sql file using PhpMyAdmin, and I 'committed' this file into the subversion repositry. Then, I made some changes to the database using the Magento administration panel, and redownloaded the .sql file. Then, I ran a DIFF using TortioseSvn, and scrolled through the 20k+ lines file to find which lines had changed, LOL. As crazy as it sounds, it did work, and I was able to figure out which tables I needed to access.
My 2nd problem was, because of the crazy directory structure, I had to ftp to about 3 folders at the same time for trivial changes. So I had to keep 3 windows of my ftp program open, switch between them and ftp each time.
The 3rd problem was figuring out how the url mapping worked and where some of the code I wanted was being stored. Here, by sheer luck, I managed to find the Model class I was looking for.
Mostly by sheer luck and other similar crazy adventures I managed to work my way through and complete the project. Since then, StackOverflow was started and by a helpful answer to this bounty question I was able to finally get enough information about Magento that I can do future projects in a less crazy manner (hopefully).
Try keypunching your card deck in Fortran, complete with IBM JCL (Job Control Language), handing it in at the data center window, coming back the next morning and getting an inch-thick stack of printer paper with the hex dump of your crash, and a list of the charges to your account.
Grows hair on your fingernails.
I guess that was an improvement on the prior method of sitting at the console, toggling switches and reading the lights.
Occam on a 400x transputer network. As there was only one transputer that could output to console debugging was a nightmare. Had to build a test harness on a Sun network.
I took a class once, that was loosely based on SICP, except it was taught in Dylan rather than Scheme. Actually, it was in the old Dylan syntax, the prefix one that was based on Scheme. But because there were no interpreters for that old version of Dylan, the professor wrote one. In Java. As an applet. Which meant that it had no access to the filesystem; you had to write all of your code in a separate text editor, and then paste it into the Dylan interpreter. Oh, and it had no debugging facilities, of course. And being a Dylan interpreter written in Java, and this was back in 2000, it was ungodly slow.
Print statement debugging, lots of copying and pasting, and an awful lot of cursing at the interpreter were involved.
Back in the 90's, I was developing applications in Clipper, a compilable dBase-like language. I don't remember if it came with a debugger, we often used a 3rd-party debugger called 'Mr Debug' (really!). Although Clipper was fast, some of our more intensive routines were written in C. If you prayed to the correct gods and uttered the necessary incantations, you could use Microsoft's CodeView debugger to debug the C code. But usually not for more than a few minutes, as the program usually didn't like to spend much time running with CodeView (usually memory problems).
I had a series of makefile switches that I used to stub out the sections of code that I didn't need to debug at the time. My debugging environment was very sparse so there was as much free memory as possible for the program. I also think I drank a lot more back then...
Some years ago I reverse engineered game copy protections. Because the protections was written in C or C++ they were fairly easy to disassemble and understand what was going on. But in some cases it got hairy when the copy protection took a detour into the kernel to obfuscate what was happening. A few of them also started to use of custom made virtual machines to make the problem less understandable. I spent hours writing hooks and debuggers to be able to trace into them. The environment really offered a competetive and innovative mind. I had everything at my disposal save time. Misstakes caused reboots and very little feedback what went wrong. I realized thinking before acting is often a better solution.
Today I dispise debuggers. If the problem is in code visible to me I find it easiest to use verbose logging. (Sometimes the error is not understanding the interface/environment then debuggers are good.) I have also realized time is of an essance. You need to have a good working environment with possibility to test your code instantly. If you compiler takes 15 sec, your environment takes 20 sec to update or your caches takes 5 minutes to clear find another way to test your code. Progress keeps me motivated and without a good working environment I get bored, angry and frustrated.
The last job I had I was a Sitecore Developer. Bugfixing can be very painful if the bug only occurs on the client's system, and they do not have Visual Studio installed on the system, with the remote debugging off, and the problem only happens on the production server (not the staging server).
The worst in recent memory was developing SSRS reports using Dundas controls. We were doing quite a bit with the grids which required coding. The pain was the bugginess of the controls, and the lack of debugging support.
I never got around the limitations, but just worked through them.

Resources