Command-Line Debugger

Amzi! Prolog includes a debugger, based on the Clocksin & Mellish box-model of Prolog execution, that allows you to trace and interact with interpreted clauses as they are running. It is run from alis using built-in predicates. It also runs in the Listener in the Amzi! IDE. The IDE also has its own source code debugger that also supports debugging of remote programs.

Box Model

The box model of Prolog execution is a conceptual tool that gives a procedural interpretation of flow-of-control through a Prolog program. As such it is useful for understanding the execution of a program. To use the box model envision each Prolog goal surrounded by a box:

Each box has four ports through which Prolog may enter (call and redo ports) or leave (exit or fail ports) the goal.

  • call - when Prolog is initially asked to prove the goal it enters through the call port.
  • exit - having entered through call, Prolog leaves through exit if it can prove the goal.
  • fail - if Prolog cannot prove the goal it leaves the box via the fail port.
  • redo - if at some further point in the program (i.e., after having entered through call and left via exit) Prolog backtracking occurs then eventually (if backtracking reaches this stage back in the proof) Prolog reenters the goal via the redo port.
  • There is one box per goal in a program, and the boxes are linked from port to port. Ports that we stop at in the debugger are known as "debug ports."

    You can creep through your program from port to port, stopping at each one. You might think of this as "single stepping" your program. The debugger can stop at every CALL, REDO, FAIL and EXIT and ask what to do next.

    Using leash (described later in this section) you can limit which of the ports are stopped at. Then, when you creep, all the ports will be displayed, but the debugger will only stop at the ones which are leashed. For example, you might want to stop only at CALLs and REDOs. By default, all ports are leashed when the debugger is started.

    Alternatively, you can "leap" to only the ports of predicates specified as spypoints. You might think of this as running until a "breakpoint" is reached. In this case, the intervening ports are not displayed.

    By combining your use of creeping and leaping, you can cause the debugger to stop at certain ports and interact with them before continuing. Thus you can rapidly trace through code which is of no interest (because it has already been debugged) and concentrate on a predicate or two at will.

    Starting and Exiting

    To start the debugger in the listener type:

    Command-Line alis
    ?- debug. 

    To exit the debugger:

    Command-Line alis
    ??- quit. 

    Note, in command-line alis, the debug listener is identified by an extra "?" in the prompt. When you exit the debug listener you are returned to the original listener.

    Display

    The debugger output is intermixed with other output in the normal scrolling dialog of the listener. It is controlled by keyboard commands.

    The debugger works by displaying the names of the ports that execution passes through in the course of a computation.

    The message output by the debugger at a port is of the following form:

    For example:

    Notice that variables are represented using the Hn notation.

    DEPTH is a number of leading spaces indicating how many ancestors the goal involved in this port has, i.e., how deeply nested in the proof it is. So top-level goals (typed in response to ?-) are indented 0 spaces (for a depth of 0). Goals in the body of the clause proving this goal are indented 1 space (for a depth of 1). Goals in the bodies of clauses proving these goals are indented 2 spaces and so on. Depths greater than 10 are indicated by both a number and spaces.

    PORT: is one of CALL: REDO: FAIL: EXIT: corresponding to the ports of the box model discussed above.

    CLAUSE# is the number of the clause being executed. It is not displayed for built-in predicates because that is not useful.

    TERM is the goal (with any variable bindings) that caused entry to the call or redo port of the box.

    PROMPT if this port is a debugging port and we are running under a command-line environment, then ? is printed and user input is awaited, otherwise mouse or keyboard input is awaited.

    portray(GOAL)

    portray/1 is a user-defined predicate that is called by the debugger when it displays a goal. You can create portray/1 clauses to generate formatted output of complex goals that are difficult to follow in the normal debugger listing.

    Debug Port Options

    At each debugging port you have a number of options for controlling the execution of the debugger. The usual response is one character (with no need to press [Enter]). The options are:

    Command
    -Line
    Windows
    -IDE
    Option
    Description
    [Enter]
    c
    Creep Creep to the very next port. If the next port is leashed (or is a spypoint) then prompt for further input else automatically creep to the next port after that. Consequently, if leashing is set to "none", creeping at one port will produce a tracing of all the ports between it and the next spypoint.
    f Fail Force the debugger to go to the fail port for this predicate invocation.
    l Leap Leap to the next spypoint. The next port to be displayed will be the next encountered spypoint.
    s Skip Skip. Only used at a call or redo port. This useful option turns off display from the debugger until it reaches the corresponding exit or fail port for this call. Thus it can be used to turn off the debugger during the course of a complex subproof. No messages will be issued by the debugger during this subproofeven if a spypoint is encountered.
    a Stop Aborts the debugger and returns to the debug listener.

    Other options are available for looking at and changing the Prolog environment under command-line alis. They are:

    Command- Line Windows- IDE Option Description
    n n/a Notrace. Turns off the debugger and then continues with the proof in progress. That is, runs the rest of your program.
    b n/a Break to a new listener. Temporarily turns off the debugger and invokes a new listener (with a ???- prompt). This allows you to work with the program however you please without leaving the trace. (You might want to separately test some predicates, change some, or whatever.) When the new listener is exited (by typing 'quit.'), the debugger is turned back on and the interrupted proof continues.
    d n/a Display the current goal with all of the current variable bindingsthen prompt again.
    h n/a Halts Prolog and exits.
    @ n/a Prompts for you to enter a Prolog goal. Temporarily turns off the debugger, like n, but instead lets you enter a single Prolog goal. It tries to prove it and then turns the debugger back on and continues the suspended proof. This is useful for quick checks of other parts of the program and for changing leashing or spypoints while debugging.
    ? n/a Displays a listing of debug port options.
    [^Break] n/a If your program is stuck in a loop, you can press [Ctrl-Break] and the debugger will stop at the next port and prompt for an option.

    Spypoints

    Spypoints are used to stop the debugger at particular predicates. Execution always pauses at a spypoint regardless of what ports are leashed and whether you are leaping or creeping.

    A spypoint is set on a predicate by using spy/1. It is removed by using nospy/1. To remove all spypoints, use nospyall/0. The current spypoints are listed by using spy/0.

    The argument to spy and nospy is one of:

  • name
  • name/arity
  • [list of name and/or name/arity]
  • Ttypically you will use the "@" debug port option to control your spypoints by entering the spy, nospy and nospyall commands as goals.

    Note that entering and exiting the debugger does not remove or otherwise alter the setting of spypoints. The only way to remove spypoints is to use one of the above.

    Leashing Debug Ports

    As mentioned above we can either creep or leap to a port. When we leap to a port, it is a port for a predicate that has a spypoint set on it, and the debugger pauses to allow interaction.

    Creeping takes us to the ports of predicates in between the spypoints. The debugger might pause at a port, or it may simply display it without giving you the opportunity to interact, as execution continues. The different behavior at ports is controlled by leashing.

    Leashing is not related to particular predicates, as spypoints are, but is instead a characteristic of each of the four types of ports, CALL, REDO, FAIL and EXIT. By default, all ports have leashing turned on, so all ports of predicates that are crept to are paused at. But leashing can be turned off for the ports of these intermediate predicates so they display but don't require step-by-step interaction from you.

    You use the leash/1 predicate to specify the ports to make debugging ports as follows:

    Note that unmentioned ports are automatically unleashed. To find out what ports are currently being leashed use leash/0.

    Typically you will use the "@" debug port option to control your leashing by entering the leash command as a goal.

    Note that entering and exiting the debugger does not remove or otherwise alter the ports currently being leashed. The only way to change leashing is to use one of the above.

    Logging

    The log-file capability allows you to record a transcript of a Prolog session. This is especially useful in recording long traces during debugging.

    Logging can be controlled from a listener, or within a Prolog program or using the "@" debug port option through the use of built-in predicates.

  • openlog(Fname) - This opens the file Fname and sets a flag letting Prolog know it is logging. The file overwrites any previous file of the same name. Fname must be an atom, such as 'temp.log' or simply log.
  • closelog - This closes the log file and stops the logging process.
  • writelog(X) - Writes X just to the log file.
  • nllog - Writes a newline to the log file.
  • Logging can also be turned on for an application by specifying a log file in the application's .cfg file.

    Example

    Let's look at an example of creeping, leaping and leashing using Duck World presented in A Quick Tutorial. First we consult our source code and enter the debugger.

    Command-Line alis
    ?- [duck1,duck2].
    yes
    ?- debug. 

    Next, we set connect as a spypoint and leash only the call port. Then we call main.

    Command-Line alis
    ??- spy(connect).
    yes
    ??- leash(call).
    yes
    ??- main. 

    We start to creep through the program by typing "c". Note, we pause only on CALL ports.Tthe program output is interspersed with the debugging information. Here we show the command-line I/O with the program I/O shown in italics:

    Now we type "l" (alis) to leap to our spypoint. No more debugging ports will be displayed until we get to the spypoint. But we will get a warning if there are no clauses matching a call.

    Our leap was interrupted for some user input, so we type "goto(yard)." We creep onwards watching the variable bindings and backtracking.

    We continue to creep forwards. Every time we reach a predicate that is a spypoint, we pause. Note that although we are only leashing CALLs, we pause at the other ports for connect, as shown below.

    We reach a predicate which we are not interested in seeing, so we type "s" (alis) to skip to the EXIT or FAIL port for this call.

    Finally, we turn off trace and finish running the program:

    Command-Line alis
    CALL: ! ? n
    >> quit.
    Quitter 

    Copyright ©1987-2011 Amzi! inc. All Rights Reserved. Amzi! is a registered trademark and Logic Server is a trademark of Amzi! inc.