<< Prev | - Up - | Next >> |
This chapter describes the various possibilities of exercising fine-grained control over the execution of programs. To do so, we'll first define at what program points thread execution can be stopped, the so-called step points.
Step points are defined in terms of syntactic constructs in source code (in particular, debugging is not line-oriented). Many Oz constructs are designated to define step points, and in fact each such construct defines two step points: one at the entry and one at the exit. The entry point creates a new stack frame, which is popped at the corresponding exit point. When stopping at a step point, Emacs highlights the line containing it and inserts an additional mark within the line to indicate its exact position (where the precise syntactic construct begins or ends).
Constructs
The following gives a non-exhaustive list of what constructs constitute step points and how they are described in stack frames.
The definition of a procedure, function or class constitutes a step point. The entry point is the proc
, fun
, or class
keyword, respectively, while the exit is the end
keyword. The frame description consists of the single word definition
.
Procedure, function, object and method applications are step points. The entry and exit points are the opening and closing braces (or the comma in the case of a method application). The frame description mimics application syntax, displaying the procedure name (or $
if it is anonymous) and the argument values.
Boolean and pattern-matching conditionals are step points. The entry point is the if
or case
keyword (or elseif
, elsecase
, or catch
, to be precise), the exit point the corresponding end
. The frame description consists of the word conditional
, followed by the value tested by the conditional, if it could be determined.
Thread creation using the thread
... end
construct is a step point, with the obvious entry and exit points. Thread creation frames consist of the word thread
.
The try
... end
construct defines step points (unless it has neither catch
nor finally
, in which case it is ignored), with a frame description of exception handler
. The actual handling of exceptions is a step point by virtue of the fact that conditionals are step points.
The lock
... end
constructs are step points, with the frame description containing both the word lock
and the lock itself.
The for
... end
construct defines step points, with frame description loop
.
Step Into
The simplest way to control the execution of a thread is to single-step, i. e., to proceed from one step point to the next. This is exactly what Step Into () does. This is the most fine-grained visualization of program execution.
Step Over
In contrast, the Step Over action (), when performed at an entry point, causes thread execution to next stop at the corresponding exit point, not stopping at any nested step points. On an exit point, it behaves exactly as Step Into does.
Action Unleash
Actually, Step Over is a special case of a more generic action, called Unleash (). It causes execution of the thread to continue until the selected stack frame is about to be popped from the stack (or until the thread has finished executing the whole stack if no stack frame is selected); this makes Step Over identical to an Unleash with the topmost frame selected. Remember you can select a stack frame by clicking on it or by walking to it using the up and down cursor keys.
Example
To illustrate the unleash action, we'll consider the following program which computes the factorial function:
local
fun {Fac N}
if N < 2 then 1
else
N * {Fac N-1}
end
end
in
{Show {Fac 5}}
end
Go and activate Step Into () a couple of times, so as to build up a bit of stack, and select Frame 5. Your Ozcar should look similar to Picture 5.1.
Let's decide to immediately compute the value of {Fac 3}
, in other words, continue the thread's execution until Frame 5 is about to be removed from the stack. Unleash () with Frame 5 selected does exactly this. Picture 5.2 displays the result of this action.
Single-stepping is nice, but often somewhat inconvenient, because you may need a lot of steps until you reach the interesting section of your program. This is where breakpoints come in. Ozcar supports them in two flavours: static breakpoints and dynamic breakpoints.
A static breakpoint can be defined by editing source code to contain an application {Ozcar.breakpoint}
and recompiling the program (which is why they're called static). This also means that static breakpoints will persist across multiple debugging sessions.
Example
Let's take the factorial example again and assume we need to debug the base case of the recursion. We can do this with a static breakpoint as follows:
local
fun {Fac N}
if N < 2 then
{Ozcar.breakpoint} 1
else
N * {Fac N-1}
end
end
in
{Show {Fac 5}}
end
After feeding the code and performing the Unleash () action twice, we arrive directly at the desired program point.
Sometimes we'll want to insert a breakpoint in the course of a debugging session. Then we need dynamic breakpoints.
Setting Breakpoints
Dynamic breakpoints can easily be set from within Emacs: Position the cursor on the line where you want to set or reset breakpoints and press C-x space to set or C-u C-x space to delete breakpoints. Note that this will affect all entry points on the corresponding line (there may be several, or none).
Currently, there is no way to list all currently defined dynamic breakpoints.
<< Prev | - Up - | Next >> |