The traditional debugger as we know it hasnt changed since the dawn of programming; which is to say it has remained pretty much the same since 1970s. Lets take a deeper look at some of its fundamental design principals and whether they are still relevant in 2011.
Traditional Debugger
Design Principles
The traditional debugger is designed around the idea that :
- Programs are single threaded
- Flow of execution is sequential
- Bugs are always reproducible.
- Programs run for short periods of time
Implementation
Sequential flow of execution and single threaded-ness
This principle is clearly reflected in the interface of the debugger which has the ‘stepping’ buttons which allow you to navigate the execution of your program sequentially. There is no well defined semantic for what happens when you say ‘step forward’ in one thread, with respect to all the other threads.
Reproducible bugs and short runs of a program
The traditional debugger relies on the ‘breakpoint’ model which assumes that the person debugging has a well defined and fully reproducible set of actions. It also assumes that the program doesn’t run for very long otherwise you would have to set a breakpoint and wait hours for it to hit.
Not multithreaded by design
Although most debuggers can stop and show you the stack frames of all the active threads when you hit a breakpoint, that is more of a evolution of the traditional design of just showing the stack frames of the single sole thread which the program is assumed to be running on. The rest of the debugging elements are not designed around the fact that the program flow is not merely sequential and data is being modified by multiple threads.
But we are in 2011…
None of the assumptions of the traditional debugger hold true anymore in 2011:
- Almost all programs are multi threaded
- Flow of execution is not 100% sequential. Data can be modified by multiple threads at the same time.
- Bugs are becoming increasingly non reproducible due to race conditions and just the increasing complexity of programs.
- Programs run for days, months and even years on servers.
Anybody who has had to debug a multi threaded program knows that merely showing the active stack frames does not help much in detecting race conditions. Not only that, but just breaking the program modifies the execution and timings of various threads leading to the bug becoming non reproducible while debugging.
The ‘breakpoint’ model is broken since for long running server side programs you can’t realistically set a breakpoint and wait for days to hit the breakpoint, only to start all over again once you step over a line you didn’t intend to.
And that leads us to Log files…
The failure of the debugger to keep up with programs written in the 21st century has led to the rise of logging and huge log files.
Logging is fundamentally broken by its very nature because :
- You are trying to predict the errors in your program, in advance, which you dont even know of.
- Since you usually put a logging statement where, you might think the error would be, you have usually hardened the code around that area already. Thus in real world situations the program usually breaks where there wasn’t any log statement at all, because the programmer never thought he might encounter an error in that piece of code.
- Long running programs generate enormous log files and you usually have to write another set of programs just to parse through those log files.
- Writing logging statements is a distraction from programming and results in clutter of code.
Thus the obsoleteness of the traditional debugger has led to people coding their own custom debugging mechanisms for every program they write.
Chronon – Reinventing the debugger for 2011
When we started designing the Chronon Time Travelling Debugger, we built it with programs of the 21st century in mind. Our assumptions were:
- Programs are inherently multi-threaded
- Flow of execution not entirely sequential. Data may be changed by other threads. Calls to a method may be interleaved across threads.
- Bugs are tough, if not, impossible to reproduce in a multithreaded world with race conditions.
- Programs run for (very) long periods of time.
Implementation
Record everything, no need to reproduce
The Chronon Recorder records the entire execution of the Java program. The recording is then subsequently used to debug the program in the time travelling debugger. This ensures that no bugs need to be reproduced.
No breakpoints, built for long running programs
Chronon does away with the concept of breakpoints entirely. You can jump to any point in time of the execution of your program instantly. Thus you might have a recording that is 5 hrs long and maybe you want to get to an exception that was thrown after 4 hrs. Chronon allows you to jump to the exception instantly with a click of a button, instead of making you wait for 4 hrs like your traditional debugger would.
We even came up with the Chronon Recording Server recently which is specifically designed for long running programs. It takes care of splitting the recording after a pre defined time interval or if the physical size of the recording gets too large.
Embraces multi-threaded, non sequential nature of programs
Although Chronon still has the stepping buttons, including a ‘step back’ button, to allow examining sequential execution of a single thread, the rest of the interface is designed with multithreaded, non-sequential execution in mind.
All the views like the Exceptions view, Variable History and Method History view show you data independently of threads. You can then proceed to filter them by the thread you want to examine.
Showing data independently of threads and then allowing you to jump to any point in time and examine the sequence that led to that particular state embraces both the multi threaded data manipulation as well as the single threaded sequential nature of the execution of the program.
Conclusion
The traditional debugger as we know it is of not much use in 2011. This is the reason people have resorted to the use of log files and custom debugging mechanisms. With Chronon, we have solved a lot of the issues with the traditional debugger and designed it to debug the way modern programs are written and used.
We believe that in 2011, you should not need to litter your code with logging or any other kind of custom debugging mechanism. Our current product and upcoming enhancements are steps in that direction.