The standard set of ports in the debugger’s box model can be extended by the programmer. This facility is not so much intended for applications, but rather for libraries that want to allow debugging in terms of concepts of the library. Specific ports can be used to identify the interesting events during execution of the library code (while the standard tracing of the library internals can be suppressed by compiling the library in nodebug-mode).
The system provides 4 primitives that can generate 4 kinds of box model ports. When inserted into the code, and when the debugger is on, they will cause execution to stop and enter the debugger, displaying a trace line with the user-defined port and data:
For example, trace_call_port/3 and trace_exit_port/0 can be used to create a more readable trace in the presence of source transformations. Imagine that the goal Y is X*X-1 has been flattened into the goal sequence *(X,X,T),-(T,1,Y). By inserting the trace primitives the debugger can still show the original source before transformation:
p(X,Y) :- trace_call_port(call,_, Y is X*X-1), *(X,X,T), -(T,1,Y), trace_exit_port.
The trace then looks like this:
[eclipse 8]: p(3,Y). (1) 1 CALL p(3, Y) %> creep (2) 2 CALL Y is 3 * 3 - 1 %> skip (2) 2 EXIT 8 is 3 * 3 - 1 %> creep (1) 1 EXIT p(3, 8) %> creep Y = 8
Another example is the insertion of additional ports for existing boxes, in particular the current parent box:
p :- trace_parent_port(clause1), writeln(hello), fail. p :- trace_parent_port(clause2), writeln(world).
This gives rise to the following trace:
?- p. (1) 1 CALL p %> creep (1) 1 CLAUSE1 p %> creep S (2) 2 CALL writeln(hello) %> creep hello S (2) 2 EXIT writeln(hello) %> creep (3) 2 CALL fail %> creep (3) 2 FAIL fail %> creep (1) 1 NEXT p %> creep (1) 1 CLAUSE2 p %> creep S (4) 2 CALL writeln(world) %> creep world S (4) 2 EXIT writeln(world) %> creep (1) 1 EXIT p %> creep Yes (0.00s cpu)
Note that the additional ports share the parent’s invocation number, so the i command can be used to skip from one to the other.
The tracer consists of a trace generation component (which is part of the ECLiPSe runtime kernel), and a user interface (which is part of the development system). The standard ECLiPSe distribution contains two user interfaces, a console-based one, and a graphical one which is part of TkECLiPSe. A programmable tracer interface (OPIUM/LSD) is under development in the group of Mireille Ducasse at IRISA/Rennes. Connecting new interfaces is relatively easy, for more detailed information contact the ECLiPSe development team.