Debugging Arity/Prolog32 code

The port model of Prolog execution

Unlike most programming languages, the execution of a Prolog program can involve both forward and backward movement. Therefore, the debugger must be able to indicate when the flow of a program is moving forward and when it is moving backward. The way this is done is through the use of ports.

Each Prolog goal is made up of four ports:

  • The call port is the port through which the program passes when the goal is encountered as execution is moving forward.
  • The exit port is the port through which the program passes when the goal has succeeded. Program execution then continues forward.
  • The fail port is the port through which the program passes when the goal fails. Failure of a goal causes program execution to backtrack.
  • The redo port is the port through which the program passes when the goal is encountered as execution is backtracking.

You can think of subgoals as being nested inside the goal. The debugger traces through a program by listing the current goal and its current port. Each time the call port of a goal or subgoal is encountered, a number is assigned to it. You can use these numbers to help keep track of the goal to which the debugger is referring.

Turning debugging on and tracing execution

Note: the debugger predicates can only be used at the interpreter prompt and therefore should not be placed within a program.

Initially when you enter the interpreter, the debugger is turned off. To begin using the debugger, you must turn it on. Once you have finished debugging, you should turn the debugger off again because execution speed is slower when the debugger is on.

trace

trace(+File)

The trace predicate turns the debugger on and places it in creep mode. Specifying a File name causes all output in the debugger window to be written to the file until the debugger is turned off. The File argument is provided as an atom or string and may contain the full path name of the file. If the file currently exists, it will be overwritten.

debug

debug(+File)

The debug predicate turns the debugger on and places it in leap mode. If a File name is specified then the debugger output is also written to the File until the debugger is turned off.

notrace

notrace(-File)

nodebug

nodebug(-File)

These predicates turn the debugger off. If debug output has been written to a file, then the notrace/1 or nodebug/1 predicates may be used to return the name of the File.

Setting spy points

The debugger lets you define predicates to be spy points. A spy point is a location where you want the debugger to interrupt the program and display a message or accept a command. You set spy points on predicates that you are interested in debugging. Spy points remain in effect until they are explicitly removed or until you exit from the interpreter. That is, if you turn the debugger off and back on again, previous spy points are still set.

spy(+Name/Arity)

spy(+[Name/Arity1,Name/Arity2,...])

Spy points are set with the spy/1 predicate. If you specify a predicate name without an arity, then a spy point is set for each predicate with that name, regardless of arity. If you include an arity with the predicate name, then a spy point is set for the particular predicate having that arity. When you spy a predicate, you automatically activate the debugger as if you had called the debug predicate.

nospy(+Name/Arity)

nospy(+[Name/Arity1,Name/Arity2,...])

The nospy/1 predicate removes a spy point from the predicate. If you specify a predicate name without an arity, then the spy point is removed for each predicate with that name, regardless of arity. If you include an arity with the predicate name, then the spy point is removed from that particular predicate.

resetspy

The resetspy/0 predicate will remove all spy points from all currently spied predicates.

Leashing Ports

You can set the debugger so that it stops only at certain ports, such as only the call and redo ports, to accept commands. Ports that are stopped at are said to be "leashed." At ports that are not leashed, the debugger displays a message indicating the port that you have arrived at, but it does not stop to accept commands.

Like spy points, leashing remains in effect until you exit from the interpreter or until you change the setting.

leash(+Mode)

You leash one or more ports with the leash predicate. You supply a Mode argument which determines which ports are leashed. The Mode argument can be one of the following atoms:

full

The debugger stops at all ports.

tight

The debugger stops at call, redo, and fail ports.

half

The debugger stops at call and redo ports.

loose

The debugger stops at call ports.

off

The debugger does not stop at any port.

You can also specify the Mode as a number between 0 and 15, representing a bit mask value. You leash a port by placing a 1 in the port's bit position; you turn off the leash by placing a 0 in the port's bit position. You must then translate the resulting binary number into decimal and use the decimal number as the argument. For example, to leash the call and redo ports, you would set the bit mask as follows:

 Call	 Exit	 Redo	 Fail	 Mask
 1	 0	 1	 0	 = 10

You would call leash with the argument, 10, as in:

?- leash(10).
yes

If you supply an uninstantiated variable as an argument to leash, it returns through backtracking the names of the ports that are currently leashed. The names are the atoms call, exit, redo, and fail.

To turn all leashing off, you would specify the number 0:

?- leash(0).
yes

Debugger commands

When you type an h at the debugger prompt, the debugger commands will be displayed. You will see:

a: abort               b: break               c: creep
d: display goal        e: exit interpreter    f: fail goal
h: help                l: leap                n: notrace
q: quasi-skip          s: skip                w: write goal
x: back to choice pt   z: zip                 @: accept goal
;: redo                ~: write refs          ]: non-det leap
<enter> : creep        <esc>: skip
Warning

Note that reconsulting a file while debugging may produce unpredictable results.

DEBUG COMMAND DESCRIPTIONS:

;

Used at an exit port, this command causes the debugger to proceed to the redo port of the current goal.

@

Allows you to call any Prolog goal and return immediately to the debugger when the goal has completed.

a

Aborts the program and returns you to the interpreter prompt. You should use this command rather than Ctrl-C.

b

Places you at the interpreter prompt without terminating the program. You can end the break and continue debugging the program by typing <Control-Z>. For each break that is in effect, the interpreter adds another question mark to its prompt. For example, if there are three breaks in effect, the interpreter prompt would look like this:

????-

c or <enter>

Causes the debugger to "creep" to the next port. With this command, you can proceed from port to port of the procedure boxes for each of the predicates' goals. If the port is leashed, the debugger stops to accept commands. If the port is not leashed, the debugger simply prints a message and continues to the next port.

d

Displays the current goal, using the display predicate.

e

Exits the interpreter and returns you to the operating system command prompt.

f

Causes the debugger to proceed directly to the fail port. This command is useful if you already know that the goal is going to fail.

h

Provides help on the debugging commands.

l

Causes the debugger to "leap" from spy point to spy point.

n

Turns the debugger off. This command is equivalent to the notrace predicate.

q

Skips to the exit or fail port of a goal. However, if there is a spy point set within the goal, execution stops at the spy point. You can use this command only from a call or redo port. (See also the s and z commands.)

s or <esc>

Skips to the exit or fail port of a goal regardless of whether other spy points are set within the goal. You can use this command only from a call or redo port. (See also the q and z commands.)

w

Writes the current goal, using the write/1 predicate.

x

This command can be used at a fail or redo port. It causes the debugger to continue failing until a call or exit port is reached. Although it does not stop to accept commands, the debugger issues a message at each port that it passes through.

z

Skips to the exit or fail port of a goal regardless of whether other spy points are set within the goal. Execution does not retain debugging information on backtracking points within goal, thus it is faster and more space efficient. You can use this command only from a call or redo port. (See also the q and s commands.)

]

Causes the debugger to leap to the next non-deterministic exit port.

~

Writes each term associated with a database reference in the current goal.