Debugging Prolog Programs Prof. Geraint A. Wiggins Centre for Cognition, Computation and Culture Goldsmiths College, University of London Contents The basic Prolog debugger Tracing program execution Spying on problem predicates
The basic Prolog debugger When programs go wrong we need a means of working out why One way to do this is to analyse the logic of the program (declarative debugging) Declarative debugging is a relatively new area, and we do not yet have the technology to do it really well Another way is to look at its procedural behaviour This is less helpful, but relatively easy to do Goldsmiths College, University of London 2
The basic Prolog debugger (2) To use the debugger, we need SICStus, with our program loaded; a copy of the program, on paper or on screen; a clear idea of what the program is supposed to do; an example query, for which the program does not exhibit the correct behaviour To switch on the basic debugger, use the command?- trace. To switch it off again, use?- notrace. Goldsmiths College, University of London 3
The basic Prolog debugger (3) Once the debugger is switched on, any query you ask of Prolog will be explained as it is proven We think of each predicate in a program as having four ways in and out, or ports. These are: Call where control goes in on the first attempt to prove a goal; Succeed where control comes out when the goal succeeds; Retry where control goes in on subsequent attempts to prove a goal (ie after backtracking) Fail where control comes out when the goal fails. Some systems define a further port: Exception where control comes out on an error (eg instantiation error). Goldsmiths College, University of London 4
Tracing program execution The Prolog debugger will now stop and tell us what it is doing, each time it passes a port. For example:?- trace. {The debugger will first creep -- showing everything (trace)} yes {trace}?- append( [a], [b], X ). 1 1 Call: append([a],[b], 89)? 2 2 Call: append([],[b], 355)? 2 2 Exit: append([],[b],[b])? 1 1 Exit: append([a],[b],[a,b])? X = [a,b]? yes {trace}?- Goldsmiths College, University of London 5
Tracing program execution (2) {trace}?- append( X, Y, [a] ). 1 1 Call: append( 69, 83,[a])? 1 1 Exit: append([],[a],[a])? X = [], Y = [a]? ; 1 1 Redo: append([],[a],[a])? 2 2 Call: append( 365, 83,[])? 2 2 Exit: append([],[],[])? 1 1 Exit: append([a],[],[a])? X = [a], Y = []? ; 1 1 Redo: append([a],[],[a])? 2 2 Redo: append([],[],[])? 2 2 Fail: append( 365, 83,[])? 1 1 Fail: append( 69, 83,[a])? no Goldsmiths College, University of London 6
Tracing program execution (3) The main components of the trace output are: 2 2 Call: append( 365, 83,[])? Invocation number an identification number which is unique to this invocation of a predicate; Current depth an indication of how deep the proof process has gone; Port indictor tells us which port we are at; Current goal tells us the current goal and its instantiation; Prompt asks for what to do next. Goldsmiths College, University of London 7
Tracing program execution (4) There are several useful options to apply (mostly single character commands): c Creep (also carriage return) take one proof step to the next port; s Skip jump to the next exit port (Succeed or Fail) from this invocation of this predicate; a Abort drop out of execution and return to the Prolog prompt; n Nodebug switch off tracing and proceed as for normal execution; r Retry go back to the call whose invocation number is given? Help print out a list of available commands. Note that some commands (eg Skip) only make sense at some ports (eg input ports), and so will not work at others. Goldsmiths College, University of London 8
Spying on problem predicates In large programs, it is often not feasible to trace all the way through to an error Often, we have a clear idea of which predicate is going wrong, so we want to go straight to it, even in small programs To do this, we use spy points We can set the debugger to wait until it encounters a spy point in the program, and then it will switch on. Goldsmiths College, University of London 9
Spying on problem predicates (2) Example: append( [], L, L ). append( [H T], L, [H Z] ) :- append( T, L, Z ). test( X, XX ) :- append( X, X, XX ). Here, we can set a spy point on append/3:?- spy( append ). {The debugger will first leap -- showing spypoints (debug)} {Spypoint placed on user:append/3} yes {debug}?- Goldsmiths College, University of London 10
Spying on problem predicates (2) {debug}?- test( X, [a,b,c,d,e,f] ). + 2 2 Call: append( 69, 69,[a, 91,c, 111,e, 131])? + 3 3 Call: append( 471,[a 471],[ 91,c, 111,e, 131])? + 4 4 Call: append( 676,[a, 91 676],[c, 111,e, 131])? + 5 5 Call: append( 881,[a, 91,c 881],[ 111,e, 131])? + 5 5 Exit: append([],[a,e,c],[a,e,c])? + 4 4 Exit: append([c],[a,e,c],[c,a,e,c])? + 3 3 Exit: append([e,c],[a,e,c],[e,c,a,e,c])? + 2 2 Exit: append([a,e,c],[a,e,c],[a,e,c,a,e,c])? 1 1 Exit: test([a,e,c],[a,e,c,a,e,c])? B = e, D = a, F = c, X = [a,e,c]? + means there is a spy point here Goldsmiths College, University of London 11
Spying on problem predicates (3) The same commands as before work in debug mode, plus l Leap switch off the debugger until the next spy point is found; + Add spypoint to the current predicate; - Remove spypoint from the current predicate. It is possible to choose which debug ports stop the execution, using the leash/1 predicate. See the manual for details. Goldsmiths College, University of London 12