Quintus Prolog Manual


(PREV) (NEXT)

e-2: The Source Linked Debugger

e-2-0: Introduction

Quintus Prolog's source linked debugger allows you to see the source for the code you are running as you step through the debugging. It also provides convenient, window-based ways of debugging your code, and provides several optional continuously-updated views of your program's execution state as debugging proceeds.

+--------------------------------------------------------------------+---+
| - |                Quintus Debugger:  family.pl                    | o |
+--------------------------------------------------------------------+---+
| File  Options  Debug  Window  Travel                              Help |
+-----+----+----+---+----------+-----+----+--------+----------+----------+
|Creep|Skip|Leap|Zip|Quasi-skip|Retry|Fail|Frame Up|Frame Down|Frame Back|
+-----+----+----+---+----------+-----+----+--------+----------+----------+
| Depth:      Predicate:                                                 |
+----------------------------------------------------------------------+-+
|   %  Family Relationships example                                    | |
|                                                                      | |
|   %  parent(?Parent, ?Child)                                         | |
|                                                                      | |
|   parent(henry, peter).                                              | |
|   parent(marie, peter).                                              | |
|   parent(henry, judy).                                               | |
|   parent(marie, judy).                                               | |
|   parent(henry, henry2).                                             | |
|   parent(marie, henry2).                                             | |
|   parent(henry, susan).                                              | |
|   parent(marie, susan).                                              | |
|   parent(peter, peter2).                                             | |
+----------------------------------------------------------------------+-+
+----------------------------------------------------------------------+-+

This picture shows what the source-linked debugger looks like. At the top of the picture is the debugger window's title bar, which shows the name of the file currently being shown in the source code window. Below this is a menu bar showing names of the available menus. Next is a row of buttons that are used to travel between ports while debugging. Below this is a status panel that shows you useful information about the current debugging state. Finally, a scrolling window shows you where in your source code your current execution state is. All of these parts of the debugger are explained below.

The source-linked debugger is displayed either by selecting one of the first three options in the Debug pulldown in the menu bar of the QUI main window (see {manual(c-2-1-1)}), by selecting the Trace button in the interrupt dialogue (see {manual(c-2-4)}), or by turning on the debugger with the trace/0, debug/0, or prolog_flag/3 built-ins (see {manual(e-1-4-1)}).

e-2-1: Showing Your Place In The Source Code

The source linked debugger shows your place in your source code by positioning an arrow near the goal being executed or clause to be tried. The position of the arrow and the direction in which it points reflect which debugger port you are at ({manual(e-1-1)}).

e-2-1-1: The Call Port

The call port is shown by an arrow to the left of a goal, pointing toward it. This indicates the arrival at a goal.

descendant(X, Y) :-
   ---> parent(X, Y).
descendant(X, Z) :-
        parent(X, Y),
        descendant(Y, Z).

e-2-1-2: The Exit And Done Ports

The exit and done ports are shown by an arrow to the right of a goal, pointing away from it. This indicates the successful completion of a goal. The exit port is distinguished from the done port by having a forked tail; this is meant to reflect the fact that this is only one of possibly many solutions to this goal. The done port signifies a determinate exit. This will help you find goals that are non-determinate and shouldn't be.

                                      descendant(X, Y) :-
descendant(X, Y) :-                           parent(X, Y). --->
        parent(X, Y). ===>            descendant(X, Z) :-
descendant(X, Z) :-                           parent(X, Y),
        parent(X, Y),                         descendant(Y, Z).
        descendant(Y, Z).
                                      

The Done Port

The Exit Port

e-2-1-3: The Redo Port

The redo port is shown by an arrow to the right of the goal, pointing toward it. This indicates that an alternate solution to a completed goal is being sought.

descendant(X, Y) :-
        parent(X, Y). <---
descendant(X, Z) :-
        parent(X, Y),
        descendant(Y, Z).

The Redo Port

e-2-1-4: The Fail Port

The fail port is shown by an arrow to the left of the goal, pointing away from it. This indicates that an no (more) solutions to this goal can be found.

descendant(X, Y) :-
   <--- parent(X, Y).
descendant(X, Z) :-
        parent(X, Y),
        descendant(Y, Z).

The Fail Port

e-2-1-5: The Head Port

The head port is shown as an to the left of the clause, pointing toward it. This indicates which clause is about to be tried. If there are other clauses to be tried after this one, The tail of the arrow will be forked (suggesting that this is only one of possibly many clauses to be tried) and a smaller arrow will indicate the next clause to be tried. Note that indexing may mean that this is not the textually following clause.

                                           parent(henry, peter).
     parent(henry, peter).                 parent(marie, peter).
     parent(marie, peter).                 parent(henry, judy).
===> parent(henry, judy).                  parent(marie, judy).
     parent(marie, judy).                  parent(henry, henry2).
  -> parent(henry, henry2).                parent(marie, henry2).
     parent(marie, henry2).           ---> parent(henry, susan).
     parent(henry, susan).                 parent(marie, susan).
     parent(marie, susan).
                                      

Determinate Head Port

Nondeterminate Head Port

e-2-1-6: The Exception Port

The exception port is shown as arrow an to the left of the goal, pointing away from it. The tail of this arrow is broken, suggesting that something may be wrong with the program or data.

descendant(X, Y, 1) :-
        parent(X, Y).
descendant(X, Z, N) :-
        parent(X, Y),
   <- - descendant(Y, Z, N1),
        N1 is N+1.

The Exception Port

e-2-2: When Source Linking Is Not Possible

Sometimes the debugger cannot find the source code for a predicate. This will happen when there is no source code, or when the correspondence between the compiled code and source code cannot be determined. For example, a dynamic predicate does not necessarily have source code, and so the debugger currently cannot show source. Similarly, a meta-call (executing a term with call/1) does not have any source code. The debugger also often cannot find the source code of clauses produced by term_expansion/2. Predicates that are compiled from "user" do not have source either!

When source linking is not possible, the debugger will show as much of the clause as it knows in place of the source file, with the appropriate arrows. At a head port, it will show the goal being called, followed by ":- ..." indicating that this goal will be matched with the head of a clause. For the head of a predicate descendent/2, it might look like this:

===> dynamic_pred(_743) :- ....

At a call port whose source code cannot be shown, the debugger will show "... :-" followed by the goal, indicating that this goal is in some unknown clause. The arrow will be as appropriate for that port. The call port for a call to descendant/2 might look like this:

... :- ---> descendant(peter, _749).

e-2-3: Traveling Between Ports

The source linked debugger provides a set of buttons to allow you to move from port to port while debugging. The debugger's travel commands are described in {manual(e-1-3)}; these buttons are labeled with the names of the commands, so using them should be straightforward.

+-----+----+----+---+----------+-----+----+-     +----------+
|Creep|Skip|Leap|Zip|Quasi-skip|Retry|Fail|  ... |Frame Back|
+-----+----+----+---+----------+-----+----+-     +----------+

The Traveling Buttons

When framed up, the skip, retry, and fail buttons operate relative to the invocation shown. Framing up is explained in {manual(e-2-4)}.

e-2-4: Seeing Ancestor Frames

The source linked debugger also provides a set of buttons to allow you to view ancestor invocation frames.

-+--------+----------+----------+
 |Frame Up|Frame Down|Frame Back|
-+--------+----------+----------+

The Framing Buttons

The Frame Up button will show you the invocation before the one you are viewing. That means that the arrow will point to the place the invocation you are currently viewing was called from. Repeatedly hitting the Frame Up button will cause you to continue to traverse up through parent invocations, eventually stopping at the goal you typed at the top level prompt.

The Frame Down button may be used after you have framed up to take you back down toward the current invocation frame.

The Frame Back button will immediately take you back to the current invocation frame. This may also be used when you have framed up or scrolled the source window, and want to instantly scroll back to show the current invocation frame, or even if you are viewing another source file in the source window and want to get back.

In order to remind you when the goal you are viewing is not the current invocation frame, the arrow shown in the source window is hollow, or "ghosted", rather than the usual solid arrow.

        |\
+-------+ \
|          >
+-------+ /
        |/

Ghost Arrow Shows Ancestor Frame

When you have framed up, certain travel buttons are interpreted relative to the frame you are currently viewing. Skip will skip over the invocation you are viewing (this is very handy if you have accidentally crept into a procedure you don't want to debug). Redo and Fail will take you to the call and fail ports of the selected invocation, respectively. All other travel buttons are disabled when you have framed up. You must either frame down or frame back in order to travel from the current invocation.

e-2-5: Debugger Menus

The source linked debugger has many options and commands that are invoked by menus. These are the menus available in the debugger window:

+---------------------------------------------------------+
| File  Options  Spypoints  Window  Travel           Help |
+---------------------------------------------------------+

Debugger Menus

Below is a description of each menu and what it is used for.

e-2-5-1: The File Menu

The File menu contains commands that affect the file that is being debugged, and the debugger as a whole. The file menu is selected by clicking its button in the dubber window. The commands in the File menu are:

+---------------+
| Open...       |
+---------------+
| Edit Source   |
+---------------+
|===============|
+---------------+
| Nonstop       |
+---------------+
| Break         |
+---------------+
| Abort         |
+---------------+
|===============|
+---------------+
| Quit Debugger |
+---------------+

Selecting The File Menu

The Open command allows you to view another file in the source debugger window. This gives you a convenient way to set spypoints. When you select Open, the debugger pops up a dialogue which allows you to select the file to view. Note that only files that have been loaded into Prolog can be viewed. The Frame Back button, described above, provides a convenient way to return to the current debugging invocation frame.

The Edit Source command provides a quick and convenient way to begin editing the file in the debugger window. A Quintus User Interface editor window is opened on the file currently shown in the debugger window.

The Nonstop command turns off the debugger for the rest of the execution of the top-level goal. When the execution of this goal is completed, the debugger returns to its current mode (trace, debug, or zip). This option does not turn the debugger off; to turn the debugger off, you must use the Quit Debugger option, or type "nodebug." at the main Prolog prompt.

The Break command calls the built-in predicate break/0, thus suspending the execution so far and putting you at the equivalent of a new Prolog top level. (See the description of break/0 in {manual(g-11-1)}.) The new execution is separate from the suspended one, and invocation numbers will start again from 1. The debugger is turned off, and the debugger window is closed, when the break level is entered, although the spypoints and leashing of the suspended level are retained. When you end the break (by typing the end-of-file character), execution will resume and you will be prompted once again at the port which you left. Changes to leashing or to spypoints will remain in effect after the break has finished.

The Abort command aborts (abandons) the current execution. All the execution states built so far are destroyed, and execution restarts at the top level (or current break level).

The Quit Debugger command turns off the debugger altogether, just like the nodebug/0 command (see {manual(l-3)}). The debugger window is also closed at this time, since it is only open when debugging is on.

e-2-5-2: The Options Menu

The Options menu allows you to change various aspects of the behavior of the source linked debugger. The Options menu is selected by clicking its button in the debugger window as shown.

+---------------------------+
| Print Format...           |
+---------------------------+
| Leashing...               |
+---------------------------+
|===========================|
+---------------------------+
| * Creep Initially (Trace) |
+---------------------------+
| o Leap Initially (Debug)  |
+---------------------------+
| o Zip Initially           |
+---------------------------+

Selecting The Options Menu

Beginning with the bottom part of the menu, the Creep, Leap, and Zip Initially toggles allow you to set the current debugging mode (see {manual(e-1-4-1)}).

The Print Format button brings up a dialogue that will allow you to change the way goals will be printed when source linkage is impossible (see {manual(e-2-2)}). This dialogue has a toggle button for each true/false write_term/[2,3] option (see Reference page), and a place to enter a print depth limit. Leaving the print depth empty (or setting it to 0) will mean that there is no depth limit; the goal will be printed in full.

+-------------------------------------------+
| * quoted                * portrayed       |
|                                           |
| * character_escapes     o number_vars     |
|                        +------+           |
| o ignore_ops           | 5    | max_depth |
|                        +------+           |
| +----+  +--------+                        |
| | Ok |  | Cancel |                        |
| +----+  +--------+                        |
+-------------------------------------------+

The Print Format Dialogue

The Leashing button brings up a dialogue that lets you set your leashing mode (see {manual(e-1-4-2)}).

+-----------------------------------+
| * Call          * Redo            |
|                                   |
| * Head          * Fail            |
|                                   |
| * Exit          * Exception       |
|                                   |
| * Done                            |
|                                   |
| +----+  +--------+                |
| | Ok |  | Cancel |                |
| +----+  +--------+                |
+-----------------------------------+

The Leashing Dialogue

e-2-5-3: The Spypoint Menu

The Spypoint menu allows you to set spypoints in your code by selecting the goal to be spied with the mouse, thereby highlighting the goal, and then selecting a spy command (see {manual(e-1-3-2)} for an explanation of spypoints). You may add or remove spypoints from goals or predicates this way.

+-----------------+
| Spy Goal        |
+-----------------+
| Nospy Goal      |
+-----------------+
| Spy Predicate   |
+-----------------+
| Nospy Predicate |
+-----------------+

Selecting The Spypoints Menu

Selecting the Spy Goal command will place a spypoint on the currently selected goal, and selecting Nospy Goal will remove the spypoint. Selecting the Spy Predicate command will place a spypoint on the predicate for which a goal (or clause head) is currently selected, and Nospy Predicate will remove the spypoint. The difference between goal and predicate spypoints is that a spypoint on a predicate will stop the debugger regardless of how that predicate is called, while a goal spypoint will only stop when the predicate is called from that particular goal.

When a spypoint is placed on a goal, a small stop-sign is placed before that goal in the debugger window, indicating that it is spied. Similarly, when a predicate is spied, a stop-sign is placed before the first clause for that predicate.

e-2-5-4: The Window Menu

The Window menu allows you to pop up various windows which provide useful information not present in the debugger window. Selecting a window from this menu will open the specified window, or, if it is already open, cause it to pop to the front of any windows that might be covering it. This menu is available in all debugger windows.

+--------------------------+
| Debugger Window          |
+--------------------------+
| Bindings Window          |
+--------------------------+
| Standard Debugger Window |
+--------------------------+
| Ancestors Window         |
+--------------------------+

The Windows Menu

Debugger Window refers to the main debugger window, and may be used when the debugger window is covered by other windows to bring it to the front. The other windows, the Variable Bindings Window, Standard Debugger Window, and Ancestors Window are discussed in depth below. (see {manual(e-2-7)})

e-2-5-5: The Travel Menu

The Travel Menu provides all the same commands as in the travel and framing panels, as described in {manual(e-2-3)}. They are provided in a menu for those who are more comfortable using menus, and also to document the keyboard shortcuts for the traveling and framing commands.

+----------------------+
| Creep       C        |
| Skip        S        |
| Leap        L        |
| Zip         Z        |
| Quasi-skip  Q        |
| Retry       R        |
| Fail        F        |
+----------------------+
| Frame Up    Ctrl + U |
| Frame Down  Ctrl + D |
| Frame Back  Ctrl + B |
+----------------------+

The Travel Menu

e-2-5-6: The Help Menu

The On This Window item opens up the help window viewing the documentation on this window. This menu is available in all debugger windows.

+----------------+
| On This Window |
+----------------+

The Help Menu

e-2-6: The Status Panel

The status panel is located just below the buttons in the debugger window, and shows the invocation depth of the current arrow, as well as information about the called predicate.

+-----------------------------------------------+
| Depth: 1    Predicate:  descendant/2          |
+-----------------------------------------------+

The Status Panel

Note that the depth shown is the depth of the frame currently being shown, so if you use the Frame Up button to show ancestors (see {manual(e-2-4)}), the depth will reflect the frame being shown.

The Predicate field first shows the module (if other than user), name, and arity of the predicate being called. Second is shown information about the predicate and the port, which is only shown when the there is something unusual or interesting to be noted about the predicate or port. The information about the predicate is one of the following:

built_in
for built-in predicates; or
locked
for locked predicates; or
undefined
for undefined predicates; or
foreign
for foreign predicates (defined in another programming language); or
dynamic
for dynamic predicates; or
multifile
for multifile predicates

Nothing is shown for ordinary user-defined static (compiled) procedures defined all in a single file. The debugger also gives information that may help you understand why it has stopped where it has. This information may be one of the following:

skipped
if you previously skipped (or quasi-skipped) from this invocation; or
spied
if there is a spypoint on this goal or predicate

e-2-7: Other Windows

Of course, you will want to know more than just which predicate is being called or which clause is about to be tried. Other useful information includes the bindings of the variables in the goal being executed, the history of your debugging session, and the ancestors (the call stack) of the current goal. This information is available in separate windows which you may bring up using the window menu, as described in {manual(e-2-5-4)}.

e-2-7-1: The Variable Bindings Window

The variable binding window shows the current bindings of the variables that appear in source code for the goal being called. This information is displayed in a format similar to that used when Prolog returns to the top level and shows the results of a user-typed goal.

For example, the variable bindings window might show the following:

+---+------------------------------------------------------------+
| - |             Quintus Debugger Variable Bindings             |
+---+------------------------------------------------------------+
| File  Options  Window                                     Help |
+----------------------------------------------------------------+
| X = henry                                                    | |
| Y = _749                                                     | |
|                                                              | |
|                                                              | |
+--------------------------------------------------------------+-+
+--------------------------------------------------------------+-+

The Variable Bindings Window

Unlike the other windows provided by the debugger, the variable bindings window changes its display depending on which frame is being shown in the main debugger window. This allows you to examine the current bindings for the arguments to any ancestor goal. See {manual(e-2-4)} for more information on the debugger's framing commands.

e-2-7-2: The Standard Debugger Window

The standard debugger window shows what would be shown by the standard debugger (see {manual(e-3)}). This gives you a history of the debugging session from the time you opened the Standard Debugger window that you may scroll back through later to review what has happened. The standard debugger window might look like this:

+---+------------------------------------------------------------+
| - |                 Quintus Standard Debugger                  |
+---+------------------------------------------------------------+
| File  Options  Window                                     Help |
+----------------------------------------------------------------+
|   (2) 1 Exit: parent(henry,peter)                            | |
|   (1) 0 Exit: descendant(henry,peter)                        | |
|   (1) 0 Redo: descendant(henry,peter)                        | |
|   (2) 1 Redo: parent(henry,peter)                            | |
|                                                              | |
+--------------------------------------------------------------+-+
+--------------------------------------------------------------+-+

The Standard Debugger Window

e-2-7-3: The Ancestors Window

The ancestors window shows the current invocation and all its ancestors, and is continuously updated. It can be a very powerful tool in debugging, as it lets you quickly see how variable bindings propagate to ancestor goals. The ancestors window might look like this:

+---+------------------------------------------------------------+
| - |               Quintus Debugger Ancestor List               |
+---+------------------------------------------------------------+
| File  Options  Window                                     Help |
+----------------------------------------------------------------+
|   (8) 3 : parent(peter2,_749)                                | |
|   (7) 2 : descendant(peter2,_749)                            | |
|   (4) 1 : descendant(peter,_749)                             | |
|   (1) 0 : descendant(henry,_749)                             | |
|                                                              | |
+--------------------------------------------------------------+-+
+--------------------------------------------------------------+-+

The Ancestors Window

Since the ancestors window is updated every time the debugger stops, when there are very many ancestors, you may notice some slowdown in the debugger. In this case, you may wish to close the ancestors window, and only open it when you really need to examine the ancestors. Usually, though, the slowdown caused by having the ancestors window open is small.

e-2-7-4: Menus For These Windows

All of these extra windows have the same menus, with the same options. These menus are as follows:

+----------------------------------------------------------------+
| File  Options  Window                                     Help |
+----------------------------------------------------------------+

Extra Window Menus

The File menu contains only a close command, which simply closes that window:

+-------+
| Close |
+-------+

The Extra Window File Menu

The Options menu contains only a Print Format command:

+-----------------+
| Print Format... |
+-----------------+

The Extra Window Options Menu

Selecting Print Format will pop up a dialogue which allows you to control the printout of the information in that window. The operation of this dialogue is explained in {manual(e-2-5-2)}.

Note that changing the print format of the variable bindings window or the ancestors window will cause that window to be updated immediately, to reflect the new print format. However, changing the print format of the standard debugger window will only change the format of subsequent entries in the window; lines already written will not be changed.


Copyright (C) 1997 AI International Ltd
contact: product support sales information