Previous Up Next

5.3  Debugging the Program

The main tool to debug a program is the tracer tool. The tracer is one of the development tools, all of which can be accessed from the Tools menu of TkECLiPSe. Select Tracer from the menu as shown below, and a new window for the tracer tool should appear.




Starting the Tracer Tool
Run the query colour again. To save you from typing in the query, you can use the up-arrow on your keyboard to step back to a previous query. Type return when colour appears in the query window again.




Figure 5.2: The Tracer Tool




The trace lines displayed by the tracer has the following:

 +(22) 14 *EXIT<3>  inform_colour(1, 1)

 1  2   3 4   5 6        7

  1. A '+' displayed here shows that the procedure has a spy point set.
  2. The invocation number of this goal, which uniquely identifies it. The `To Invoc:' button can be used to jump to the next port with the specified invocation number.
  3. The depth of the goal, i.e. the number of its ancestors. The `To Depth:' button can be used to jump to the next port within the specified depth range.
  4. An asterisk before an EXIT means that this procedure is nondeterministic and that it might be resatisfied.
  5. The type of the port. The `To Port:' button can be used to select the type of port to jump to.
  6. This only appears if the goal is executing at a different priority than 12, the normal priority. The number is the priority that the goal is executed at.
  7. The goal is printed according to the current instantiations of its variables.


Figure 5.3: Debugger Trace Line



The tracer tool traces the execution of the program, like the traditional Prolog debugger, it stops at `debug ports' of predicates that are executed.
See the Debugging chapter in the User Manual for more details on the model used in Prolog debuggers.
Currently, it is stopped at the call port of the query colour. The buttons in the middle of the tool are for debugger commands. Try pressing Creep several times, and you should observe something similar to Figure 5.2. Unlike the traditional debugger, the execution trace is shown on two text windows: the bottom `Trace Log' window, which shows a log of the debugger ports much as a traditional debugger does; and the top `Call Stack' window, showing the ancestors (`call stack') of the current goal, which is updated at each debug port. The goals are displayed with different colours: blue for a call port, green (success) for an exit port. Red (failure) for a fail port. Note that in the call stack, the ancestor goals are displayed in black: this indicates that the goal is not `current', i.e. the bindings shown are as they were when the goal was called, and not necessarily what they are now. We will show how these bindings can be `refreshed' later on.

To avoid stepping through the whole program, we will add a spy-point to a predicate that may be causing the problem. Spy-points can be added in the traditional way, using the spy/1 predicate. However, we can also use the predicate browser tool: start the Predicate Browser tool from the Tools menu of TkECLiPSe. This tool allows you to observe and change various properties of the predicates in your program. A list of predicates are displayed on the left hand side, and a list of properties on the right. Currently the predicate list is showing all the predicates defined in our program (i.e. in the eclipse module). Looking at this list, not_same_colour/3's name suggests that it checks that neighbouring countries do not have the same colour. Select it by clicking on it, and now the right hand side should display the properties of this predicate:




The Predicate Browser Tool


We can now view the source code for the predicate by clicking on the Show source button, which opens a source display window to show the source of the selected predicate. The code for the predicate is:

not_same_colour(Solver, C1-C2, Countries) :-
      % get the colours for the countries C1 and C2
      arg(C1, Countries, Colour1),
      arg(C2, Countries, Colour2),
      % send constraint to either the fd or ic solver
      Solver: (Colour1 #\= Colour2).

The code does indeed check that the countries C1 and C2 do not have the same colour.

For our example program, the list is not very long, but some programs may have many predicates, and it could be difficult to find the predicate you want. The predicate list has a search facility: typing in part of the name of the predicate in the predicate list will search for the predicate you want. You can try typing in not_same_colour / 3 to see how this works.

The predicate browser allows us to change some of the properties of a predicate. We can add a spy-point to the predicate by clicking on the radio button for spy:




Setting Spy Property to On


With TkECLiPSe, we can do more than just place a spy point on a predicate: we can specify further conditions for when the tracer should stop at a spy point, using the filter tool.

Start the filter tool by selecting Configure filter from the Options menu of the tracer tool:




Starting the Filter Tool from the Tracer



Figure 5.4: The Tracer Filter Tool



The filter tool opens in a new window, as shown in Figure 5.4. This tool allows us to specify a `filter' for the debug ports so that the tracer will only stop at a port with the properties specified by the tool. In our case, we want to see not_same_colour/3 only when countries 3 and 4 are involved. This can be done with the “Predicate specification” facility, enabled by the Specific predicate instance: radio button. Pressing this button will allow us to specify a condition in Prolog syntax which will be checked at each debug port. For our purpose, we enter the following:




Setting Conditions for Specific Predicate Instances


This specifies that the filter should stop at a not_same_colour/3 goal, when one of the countries in the pair X-Y is country 4: the Goal template is used to specify the template the debug port goal should match, and the Condition: can be any ECLiPSe goal, perhaps with variables from the Goal template, as in our case. The test is done by unifying the goal with the template, and then executing the condition. Note that any bindings are undone after the test.

Note that we have also deselected the exit port in the filter condition. You can do this by clicking on the exit radio button. This means that the tracer does not stop at any exit port.

Press Go on the filter tool to start the tracer running with the filter. You can also press the Filter command button on the tracer to do the same thing. We see that the tracer has jumped to a not_same_colour/3 goal involving country 4 as expected. However, there is a gap in the call stack as we skipped over the tracing of some ancestor goals. We can see these goals by refreshing the goal stack. This can be done by pressing and holding down the right mouse button while the mouse cursor is over a goal in the call stack, which will popup a menu for the goal:




Popup Menu for a Goal in Tracer's Call Stack


In this case, we have opened the menu over not_same_colour/3, and the options are for this goal. Various options are available, but for now we choose the Refresh goal stack option. This will result in the following goal stack display:



Refreshed Call Stack


Notice that the colour of the goals in the goal stack are now all blue, indicating that the bindings shown are current.

Press Filter on the tracer several times to jump to other ports involving country 4. You will see that none of them involve countries 3 and 4. So perhaps countries 3 and 4 are not checked by not_same_colour/3, i.e. 3-4 or 4-3 are never passed to not_same_colour/3. Looking at the call stack, we can see that the country pair in not_same_colour/3 seem to appear as an element in a list of country pairs, as far back as colouring(...). Unfortunately, the debugger does not display the whole list. We see something like:

do_colouring(prolog, input_order, indomain, [4 - 2, 4 - 1, ...
due to the `print depth' feature, which shortens the printing of large terms. We can examine the whole list by using the inspector to examine the goal. To do this, we double click on the do_colouring(...) goal to `open' it for inspection.

This will launch the Inspector tool on the do_colouring goal. The inspector displays the term in a hierarchical fashion as a tree, which allows us to navigate the term. The initial display is shown on the left panel below. We are interested in examining the full list. We can look at this list by double clicking on it to expand the node, which results in the display in the right panel below. You may need to scroll down to see the whole list:




Using the Inspector


The inspector shows that this list does not contain the pair 4-3 or 3-4, which should be there so that not_same_colour can check that these two countries are not assigned the same colour.

The inspector tool is modal – when it is open, the rest of TkECLiPSe is inaccessible. Close the Inspector by clicking on its Close button, go back to the tracer, and see where the country pair list comes from. It first appears in the ancestor goals do_colouring(prolog,...), as the next parent colouring(prolog,...) does not have this list. So the list is created in a body goal of colouring(...) before do_colouring(...) is called. We can look at the source of colouring(...) to see how this list is created. To do this, we can select Display source option from the popup menu for the colouring(...) goal:




Displaying Source for a Goal in the Call Stack


The code for this predicate is quite long, but for our purposes we are only interested in the country-pair list that is passed to do_colouring:

colouring1(Type, Select, Choice0, N, Backtracks) :-
        ....
        findall(C1-C2, (neighbour(C1,C2), C1=<N,C2=<N), Neighbours),
        ....
        do_colouring(Type, Select, Choice, Neighbours, Countries,
                     CountryList, Backtracks), 
        ....

Looking at this source and the Call stack goal, we can see that the country pair list is constructed from neighbour/2 calls. Let's look at the source for neighbour/2. We can do this from the predicate browser, by selecting neighbour/2 and pushing the Show source button. We see the following:

neighbour / 2 in file buggy_data.map, line 2:
%neighbour(4, 3).
neighbour(4, 2).
neighbour(4, 1).
neighbour(4, 2).
neighbour(3, 1).
neighbour(3, 2).
neighbour(1, 2).

So neighbour(4,3) was indeed missing (it is commented out). Another way to check neighbour/2, without looking at the source, would be using the Simple Query tool. This tool is again started from TkECLiPSe's Tools menu. It can be used to send simple queries to ECLiPSe, even while another query is being executed (as we are here, executing the colour query). We can use this tool to check if neighbour(4,3) or neighbour(3,4) are defined or not:




The Simple Query Tool


To send a query, simply type it in the entry window and press return, and the reply will be sent to the reply window. In the example above, we have tried neighbour(4,3), followed by neighbour(3,4), and both failed, indicating that there is no neighbour relationship defined between countries 3 and 4.

We can fix the program by editing the file buggy_data.map and adding the neighbour(4, 3) line back. First, we end our current debugging session by closing the tracer window. You can see from the map display that the execution continues until a solution is produced. Pressing Done on the map display will return control to ECLiPSe. Alternatively, if continuing the execution is undesirable, press the Abort command button in the tracer, which would abort the execution.

Once we have made the correction to the program and saved it, we compile it by pressing the Make button on TkECLiPSe. This recompiles any files that have been updated since ECLiPSe last compiled the file.

Running the program again will show that the bug is indeed fixed.


In TkECLiPSe, you can usually perform these operations on an object while the mouse cursor is over it:
left-click
selects the object.
double (left)-click
`opens' the object. This can mean expanding it (e.g. in the inspector), or calling the inspector on it (e.g. on a goal in the call stack).
Right-click and hold
Opens a menu which gives further option/information on the object.
Right-mouse button functionality are alternatively available through the left-mouse button with the control key pressed.


Figure 5.5: Mouse Button Operations on Objects




Compile scratch pad
allow simple programs to be written and compiled. Equivalent to [user] in command line ECLiPSe.
Source file manager
manage source files for this ECLiPSe session.
Predicate browser
view/change properties of predicates.
Delayed goals
view delayed goals.
Tracer
debugger for ECLiPSe programs.
Inspector
term inspector. Useful for viewing large terms.
Visualisation client
start a visualisation client.
Global settings
view/change global ECLiPSe settings.
Statistics
show statistics. Information is updated dynamically.
Simple query
send simple queries to ECLiPSe.
Library browser and help
interface to ECLiPSe documentation.
TkECLiPSe preference editor
view/change TkECLiPSe settings.


Figure 5.6: Available Development tools




Previous Up Next