Next: Availability of Psd Up: Psd - a Portable Previous: Tail Recursion and

Caveats and Limitations

Psd shares the problem common to all debuggers: running the debugged program is not exactly the same as running the same program without the debugger. Psd tries to be true to the underlying environment, but there is at least one aspect that would require access to the underlying implementation: evaluation order.

In order to catch runtime errors, Psd transforms each procedure call into a call to the procedure psd-apply, with the subexpressions of the original combination as arguments. Because of the way the Scheme language is defined, it cannot be guaranteed that the evaluation order of the subexpressions is the same in the debugged program as in the original program. There is not much that can be guaranteed about the order of evaluation anyway, which makes this a non-issue for well written programs. In practice, however, debuggers are used for finding bugs in ill behaved programs, so it should be addressed somehow. If an implementation always uses a left to right or right to left evaluation, Psd preserves the evaluation order.

Another limitation is caused by the lack of a standard method for accessing top level variables. Psd provides access to all variables defined in the files that are being debugged, but all other top level variables are inaccessible. A partial solution would be to detect all nonlocal variables that are referenced in the debugged expressions. This may be implemented in future versions of Psd.

Conventional debuggers can provide some help even when an error happens in a part of a program that has not been compiled for debugging. With Psd this is not possible, because debugging with Psd relies on the correct execution of programs.

A yet unsolved problem is providing the user with backtrace information. Access to local variables in the current lexical context is easy to provide using closures, but access to nonlocal variables at the calling procedure is more difficult. It would be possible to collect backtrace by passing the backtrace as an extra parameter with every procedure call. This is not a very good solution, because it would not allow mixing debugged and undebugged code. Another solution would be to collect the same backtrace by inserting assignments to a global variable at each procedure entry and exit. This approach breaks when call-with-current-continuation is used, because a procedure invocation can be exited an arbitrary number of times.

An inherent problem with instrumenting the original source code is that the resulting instrumented files are quite large. The extreme case is the one line procedure


  (define (foo x) (+ x 1))
that is expanded from 25 bytes to 963 bytes, giving an expansion factor of 39. A more typical case is the instrumentation code of Psd that was expanded from 18058 bytes to 259309 bytes, giving a factor of 14. The time taken to instrument the instrumentation code was little over a minute on a Sun Sparcstation SLC using Aubrey Jaffer's scm interpreter.

The instrumented code is so much slower than the original that it is by no means practical to instrument all the procedures of a large application. A binary tree implementation was instrumented, and the slowdown caused by instrumentation was in the range of 170-290. Usually the problem can be pinpointed to a few procedures with a fair accuracy without a debugger, though, and the debugger can be applied only to them. In practice the slowness has not been a serious problem.



Next: Availability of Psd Up: Psd - a Portable Previous: Tail Recursion and


pk@
Fri Feb 11 09:35:36 EET 1994