Source Code Debugger

Amzi! Prolog includes a full source code debugger. It works on interpreted and compiled code that is running on the local or a remote machine. This makes it very easy to debug Prolog modules embedded in other languages (such as Java, C++, Delphi or VB) and Web servers (using JSP/Servlets or ASP.NET). The debugger is based on the Clocksin & Mellish box-model of Prolog execution that allows you to trace and interact with interpreted or compiled clauses as they are running.

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 single step through your program from port to port, stopping at each one. The debugger can stop at every CALL, REDO, FAIL and EXIT and ask what to do next. Alternatively, you can run until a breakpoint is reached. In this case, the intervening ports are not displayed.

    When using 'Step Into' you can limit which of the ports are stopped at by selecting them from the pull-down debug menu (to the right of the step buttons). Then, when you Step Into, all the ports will be displayed, but the debugger will only break at the ones which are checked on the menu. For example, you might want to break only at CALLs and REDOs. By default, all ports are stepped into when the debugger is started.

    Debugging Perspective

    The Eclipse Debugging Perspective looks like this:

    If your screen does not look like the screen shot above, you can select Window / Reset Perspective to restore this arrangement of the views. The debugging perspective consists of the following views (starting from the top left):

    Running the Debugger

    Launch Configurations

    In order to run or debug a Prolog component or program, the Amzi! IDE needs a 'Launch Configuration'. Launch Configurations define all the necessary parameters for the runtime or debug environment. For Prolog, these are the libraries and Logic Server extensions to load, the path to run from, source files or executable file. 'Launch Shortcuts' automatically build an appropriate Launch Configuration for the current project. See the Eclipse documentation for details.

    There are four types of configurations/shortcuts for debugging: interpreted code run locally, single file interpreted code run locally, compiled code run locally and compiled code run 'remotely' (embedded in another application on the same or a different machine). The parameters for each launch configuration are:

    Interpreted Project

    Directory to Run From -- The directory where execution is started. This is usually the project folder because it contains the source files and the amzi.cfg configuration file.

    Optional Pathname for Config File -- The full pathname of the .cfg file to use when running this project. If left blank, amzi.cfg in the run directory is used.

    List of Source Files to Consult -- The files to consult into the Listener.

    List of Libraries to Load -- The Amzi! libraries to load into the Listener. These are taken from the Project Properties.

    List of Extensions to Load -- The Logic Server Extensions to load into the Listener. These are take from the Project Properties.

    Interpreted Single File Project

    Directory to Run From -- The directory where execution is started. This is usually the project folder because it contains the source files and the amzi.cfg configuration file.

    Optional Pathname for Config File -- The full pathname of the .cfg file to use when running this project. If left blank, amzi.cfg in the run directory is used.

    List of Source Files to Consult -- The files to consult into the Listener.

    List of Libraries to Load -- The Amzi! libraries to load into the Listener. These are taken from the Project Properties.

    List of Extensions to Load -- The Logic Server Extensions to load into the Listener. These are take from the Project Properties.

    Compiled Project

    Directory to Run From -- The directory where execution is started. This is usually the project folder because it contains the source files and the amzi.cfg configuration file.

    Optional Pathname for Config File -- The full pathname of the .cfg file to use when running this project. If left blank, amzi.cfg in the run directory is used.

    Compiled File to Run -- The xpl file to execute. (The libraries are linked into the xpl file). This file is usually in a bin subdirectory.

    List of Extensions to Load -- The Logic Server Extensions to load into the Listener. These are take from the Project Properties.

    Remote Prolog Application

    Project Name -- The name of an open Eclipse project that corresponds to the remote Prolog application. (This is not checked, so you must ensure you have a project with source code that matches the source code used to build the remote application.)

    Debug Port Number -- The port to connect to the Prolog engine on. This is set in the amzi.cfg file for the remote Prolog application.

    Note you must set the Project Property to 'Debug' for executable file type in order to run the source code debugger on an xpl file. Do not ship files built in Debug mode as they have considerable overhead in terms of memory and CPU usage.

    Starting and Exiting

    To start the debugger select a Prolog Project in the Navigator View or an open source file editor (for a file that is in a Prolog Project), then:

    Amzi! IDE
    Shortcuts
    Run / Debug As / Interpreted Project
    Run / Debug As / Compiled Project
    Run / Debug As / Interpreted Single File
    Run / Debug As / Remote Prolog Application
    To Create a Launch Configuration for the Selected Project
    Run / Debug ...
    New

    When debugging a remote application, you will be prompted to start it. Wait until you receive the prompt before starting the remote application. Note, you need to specify a debug_host and debug_port, and load the adebug.lsx file in your amzi.cfg file or program code before the remote debugger will operate. See Remote Debugging below.

    To exit the debugger:

    Amzi! IDE
    Interpreted Project
    Type 'quit.' in the Debug Listener
    or
    Press the 'Terminate' Button
    Remote Prolog
    Press the 'Terminate' or 'Disconnect' Button

    You can switch back to the Prolog Perspective by pressing on the Paw button on the left edge.

    You can rerun your last debug session by selecting the launch configuration from either the Debug | Debug History menu or from the Debug command button.

    Remote Debugging

    The Amzi! Eclipse remote debugger is implemented using the TCP/IP protocol. There are two parts: the Amzi! Eclipse IDE and the LSX (adebug) that communicates on behalf of the compiled Prolog application with the IDE. The remote application must load adebug.lsx either explicitly by calling AddLSX in the host program or by specifying it in the amzi.cfg file.

    You can control the port used for this communication. In the IDE this is done in the launch configuration (the default is port 8000). For the remote Prolog application, this is done in the amzi.cfg file (via debug_host and debug_port). If you are running through a firewall you will need to select a port that is open. Contact your network administrator.

    Problems with the connection are reported by adebug.lsx by throwing errors to the host application. So we strongly recommend that you catch and display any errors from your calls to lsExec, lsExecStr, lsMain, lsCall, lsCallStr, lsRedo, etc.This is how you will find out if adebug.lsx is unable to connect to the Amzi! Eclipse IDE.

    To initiate remote debugging, follow these steps:

    1. Build your Prolog Project in 'debug' mode.
    2. Copy the .xpl file to the remote PC. (We recommend that you do not run an xpl file built for debugging in a production application as it will have degraded performance.)
    3. Copy amzi.dll (Windows) or libamzi.so (Unix) and adebug.lsx to the remote PC.
    4. Create an amzi.cfg file in the same directory as your .xpl file on the remote PC and uncomment debug_host, debug_port and load adebug.lsx as follows:
      debug_host = 127.0.0.1
      debug_port = 8000
      lsxload = adebug
    5. Start the debugger in Eclipse on your Prolog Project by selecting Run | Debug As | Remote Prolog Application
    6. When prompted start your remote application. For a stand-alone Prolog program, use 'arun myprog.xpl'. For a Prolog component embedded in a language or webserver, start the host application. Debugging should start when you initalize the Logic Server.

    Display

    The debugger works by displaying the list of active stack frames in the upper left Debug View. A stack frame includes the port name and term that execution passes through in the course of a computation. When you click on a stack frame the variable bindings are displayed in the Variables View to the right.

    The stack frames take the following form:

    For example:

    Notice that variables are represented using the Hn notation.

    PORT: is one of CALL, REDO, FAIL, or EXIT corresponding to the ports of the box model discussed above or blank for information-only.

    TERM is the goal that caused entry to the call or redo port of the box.

    VARIABLES is the list of variable bindings (you can also see these in the Variables View)

    Debug Commands

    When the Debug Listener View is active you can type a goal (or other input required by your program). When it is dimmed, you can select one of the debugging actions from the Debug View tool bar. The options are:

    Button Name Description
    Resume Runs to the next breakpoint.
    Suspend Suspend the running program at the next port.
    Terminate Stops the debugger.
    Step Into Steps to the very next port.
    Step Over Steps over the current term to the next one.
    Auto Step When set, runs your code by automatically stepping into the next line at the speed set on the Debug View menu.
    Disconnect Disconnects a remote debugging session.
    Listener Opens a listener for user queries on the current debugging session. This is useful for examining or changing the dynamic database, running code and determining execution state.

    When the debugger is ready to stop at a line of code, it checks the leashing. Leashing tells the debugger which execution ports are to be displayed. The choices are the four standard ports, call, exit, redo, and fail, plus one additional pseudo-port, called info, which stops at the head of a clause. Leashing is set using the buttons. When a port is clicked, then the port will be displayed when execution stops at that line of code. The options shown are:

    Button Name Description
    Display Call Ports If not set, debugger skips call ports.
    Display Redo Ports If not set, debugger skips redo ports.
    Display Fail Ports If not set, debugger skips fail ports.
    Display Exit Ports If not set, debugger skips exit ports.
    Display Clause Heads If not set, debugger skips clause heads.

    There are also commands on the Debug View menu:

    Command Description
    Auto Step Speed
    Select from Very Fast to Very Slow to set the speed for the Auto Step command button.
    Cut Display Controls whether or not cut stack frames are displayed. When this feature is enabled, execution may require additional control stack space. You can increase the control stack size in the amzi.cfg file.
    DCG Variable Display

    Controls what difference list variables are displayed during execution:

    • All displays the difference lists for each goal
    • First and Last the difference lists on input and output
    • None does not display difference lists

    Breakpoints

    Breakpoints are used to stop the debugger at particular predicates. Execution always pauses at a breakpoint regardless whether you are running or stepping.

    Under the Amzi! IDE, you can double-click in the left margin of the Editor on the line you want to break at. A blue dot will appear. If you double-click again, the breakpoint is removed. You can also right-click in the left margin to set or clear breakpoints.

    The list of all breakpoints is displayed in the Breakpoints View in the upper left. You can also clear one, some or all the breakpoints in this window.

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

    Breaking at Debug Ports

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

    Step into takes us to the ports of predicates in between the breakpoints. 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 the Debug View pull-down menu.

    Breaking at a specified port is not related to particular predicates, as breakpoints are, but is instead a characteristic of each of the four types of ports, CALL, REDO, FAIL and EXIT (and INFO). By default, the debugger will break at all ports, so all ports of predicates that are stepped into to are paused at. But port breaking can be turned off for the ports of these intermediate predicates so they display but don't require step-by-step interaction from you.

    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.

    Amzi! IDE
    File / Import / Existing Project into Workspace
    Browse to amzi/samples/prolog/duckworld
    Double-click on dw_rules.pro to edit it
    Select duckworld project folder
    Run / Debug As / Interpreted Project

    Next, we set connect as a breakpoint and break only the call port. Then we call main.

    Amzi! IDE
    Click connect/2 in the Outline
    Double-click in the left margin next to the connect/2 clauses
    On the pull-down menu, uncheck Display Redo, Fail, Exit and Info Ports
    In the Debug Listener window type:
    ?- main. 

    We start to step through the program by pressing the Step Into button. Note, we pause only on CALL ports. Notice after we step into:

    write(` Welcome to Duck World `),nl,

    the output appears in the Debug Listener View. Now press the Resume button. The Debug Listener View now reads:

    Welcome to Duck World
    ... Go get an egg >> goto(yard).

    Our run was interrupted for some user input, so we type "goto(yard)." We step onwards watching the variable bindings and backtracking. Every time we reach a predicate that is a breakpoint, we pause. Note that although we are only leashing CALLs, we pause at the other ports for connect/2.

    When you reach a predicate that you are not interested in seeing, so press Step Over to go directly to the EXIT port. This is illustrated nicely by stepping over demons/0 in the go/0 predicate.

    Finally, we end the debugger:

    Amzi! IDE
    Press Terminate or type:
    >> quit.
    Quitter 
    ?- quit.

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