OmniCore X-develop

OmniCore X-develop, the successor of CodeGuide, is an IDE for Java, C#, J#, Visual Basic, JSP, and more. It has a “back-in-time debugging” feature that permits to step forward as well as backward while debugging:

Imagine using a debugger to step through some code looking for a bug. Suddenly you find out that the bug must have been caused by a method invocation you just stepped over. To investigate the bug you would need to go back in time and step into the method. Conventional debuggers do not allow that. CodeGuide 7 does. You can step back, step into the method and investigate what exactly caused the problem. Another example: An exception is thrown and you want to find out what caused it. With CodeGuide 7 you set an exception breakpoint and when the exception is thrown you can use the debugger to go back in time and find out what caused the exception.

Follow this link for a more detailed description.

The engine

I have not been able to find much information about how the system is implemented, other than the fact that classes are instrumented:

In order to make back-in-time debugging possible X-develop's integrated compiler instruments the class files. While performance should not be an issue if no breakpoints are set because method tracing is turned on only for “interesting” areas (e.g. methods with breakpoints, their callers and callees), the generated .class files are considerably enlarged.

I guess more precise information is not publicly available. The concept of “interesting areas” is, indeed, interesting. It is a smart way to reduce the amount of trace data without requiring user intervention, interesting areas being algorithmically determined.

The interface

It is obvious that a lot of effort has been put in the implementation of debugging facilities. The back-in-time debugging feature is perfectly integrated with the IDE, but even standard debugging is above the average.

Stepping into a line that contains multiple method calls or expressions

Let's debug the following line:

root.addFragment(createMonthFragmentOfWeeks(lines));

This line calls two methods and uses a local variable (root) and a method parameter (lines).

Step into: root is underlined and its value appears in a tooltip
Step into: lines is underlined and its value appears in a tooltip
Step into: current execution point goes into the createMonthFragmentOfWeeks method
Step out: execution point stops at the return statement, and the value to be returned appears in a tooltip
Step into: we're back at the original method, createMonthFragmentOfWeeks(lines) is underlined and the returned value appears in a tooltip
Step into: execution point goes into the addFragment method

At any time during this sequence it is possible to step backwards and again forward. The interaction is very smooth: the user doesn't have to know that he is navigating execution history or really executing new steps. The debugger behaves almost exactly in the same way when stepping into code that has not been executed (normal debugging) or when stepping into the execution history. The only difference is that when navigating the execution history, it is not possible to step into methods of non-instrumented classes, such as JDK classes.

Also, when stepping into not yet executed code, in methods of non-instrumented classes, the step backwards buttons are disabled, except for “step back out” which goes back to the point where the method was called.

Other fearures & details

  • Assignment to local variables, instance variables and method calls are traced
  • When debugging, there is a pane on the left that shows the current call stack in the form of a tree. Top-level items in the call stack are methods. When a method is expanded it shows the events that occured during the execution of the method. An event corresponding to a method or constructor call can in turn be expanded to show the events that occured during this call, etc. Clicking an event brings the “virtual” execution point to that event, so that further stepping (backward or forward) is from that event.
  • Among forward and backward stepping actions are very convenient “step to cursor” actions that bring the execution point to the closest event that matches the current line of the editor.

Limitations & missing features

  • The execution history trace has a fixed, user-defineable size limit (per thread). Older events are discarded.
  • It is not possible to find, for instance, the last event that changed the value of an instance variable, or the last call to a given method.
  • Object inspection is only valid at the current “real” execution point. Inspecting an object's value while navigating in the execution history doesn't show the object as it was at that given point in time
  • Object inspection is tree-based only, customization of visualizations is limited to showing or not showing the result of calling the “toString” method on objects.