Table of Contents
No Scheme system would be complete without facilities to assist the programmer in debugging his or her code. SISC provides aid for passive debugging (requiring no action on the part of the programmer) and active debugging (requiring code instrumentation to facilitate debugging).
Passive debugging facilities are provided that collect information on an error that occurred at runtime and was not caught by the executing code. The programmer can then inspect the last error, obtain information about the call stack of the error, or even attempt to restart the computation.
One of the most common desires is to obtain a trace of the call stack, to determine what sequence of calls resulted in the error. SISC provides procedures for accessing the call stack of any continuation.
procedure:
(stack-trace continuation) => list
Returns the stack trace for continuation in form of a list. Each element in the list is a pair that represents one level in the call stack, starting from the top. The car of an element a is a SISC virtual machine expression while the cdr is an association list identifying the corresponding Scheme code source location using the following keys: source-file, line-number, column-number.
procedure:
(print-stack-trace continuation)
Displays the call stack of the continuation in a human-readable form.
The error message, location information and call stack associated with an exception can be displayed in human-readable form using the following procedure.
procedure:
(print-exception exception [stack-trace?])
Displays the error message and location of exception. A stack trace is displayed if stack-trace? is absent or set to #t. Furthermore the procedure calls itself recursively in order to display similar information for nested exceptions.
In order to obtain the source file location of a call, your Scheme code must have been loaded while SISC's reader had annotations enabled. Annotations are a means of attaching metadata to compiled Scheme code. To allow the reader to attach annotations related to the source file position of the code it reads, enable the emission of annotations with the emitAnnotations configuration parameter (see the section called “Configuration Parameters”).
SISC can also produce more detailed stack traces if code was generated with debugging symbols. These are extra annotations generated by the compiler that track function and variable names that would ordinarily be discarded. By including these annotations, the stack trace can display the name of more of the calls involved. To enable the generation of debugging symbols, the emitDebuggingSymbols configuration parameter must be set to true (see the section called “Configuration Parameters”).
Finally, when debugging a program for a long period of time, it may be desirable to have stack traces displayed whenever an error occurs, rather than needing to invoke print-exception or other functions each time. For this, the stackTraceOnError configuratin parameter must be set to true (see the section called “Configuration Parameters”).
Requires: (import debugging)
SISC provides active debugging aids; procedures and syntax that can be used in source code to assist in tracing the activities of running Scheme code.
When a function is traced, each call to the function will be displayed to the console with the function's trace identifier and the values of the operands the function is being applied to. Each nested call is indented slightly, so as to illustrate the depth of calls. When the function application returns, the value of the function-call is displayed at the same indentation as the call itself. Once indented to a certain depth, the same indentation is kept for further nesting, but the depth of the call is displayed as an integer preceding the call.
syntax:
(trace-lambda trace-name formals body) => procedure
When replaced with a trace-lambda, all calls to a lambda defined procedure are traced on the console. trace-name is an identifier which will disambiguate the procedure in the trace. formals and body have identical semantics to lambda.
syntax:
(trace-let loop-identifier formal-bindings body) => value
Replaces a named-let expression in a similar manner to trace-lambda.
procedure:
(trace [symbol] ...) => undefined
Begins traces on the procedures named by the symbols given. The procedures must be defined in the top-level environment.
If no procedures are given, a message is displayed indicating the names of top-level procedures currently being traced.
procedure:
(untrace symbol [symbol] ...) => undefined
Stops tracing the top-level procedures named by the symbols given.
trace-lambda and trace-let are useful for debugging anonymous lambdas and named-lets respectively. trace and untrace ar useful for tracing any top-level bound procedure, including calls to builtin procedures and stored continuations.
Tracing a function installs instrumentation code around the procedure which does not preserve the continuation of a call to that function. Thus, tail calls made in a traced function are no longer tail calls. This may affect the memory usage characteristics of running code.
A user may wish to halt execution of a running Scheme program when a given procedure is called. SISC provides means to install breakpoints at top-level visible functions without having to redefine the function.
When a breakpoint is set using set-breakpoint!, and the function is called, execution will halt, returning to the REPL and displaying an informational message indicating a break, the procedure called, the arguments passed to the breakpointed procedure, and, if possible, the location in a source file of the call. The user may then continue execution using the continue procedure.
procedure:
(set-breakpoint! symbol) => undefined
Instruments the top-level procedure named by the given symbol, such that when called, execution will halt and return to the REPL and the location of and arguments to the breakpointed function are displayed.
procedure:
(clear-breakpoint! symbol) => undefined
Removes the instrumentation on the named top-level procedure, if present. Execution will continue normally through occurances of the formally breakpointed procedure.
procedure:
(continue) => does not return
Continues execution from the most recent break. It is an error to call this procedure if a breakpoint has not been hit, or to call this procedure more than once for a given break.