Extending the Debugger

## 15.7 Extending the Debugger

- User-defined Ports
- Attaching a Different User Interface

### 15.7.1 User-defined Ports

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:

- trace_call_port(+Port, ?Invoc, ?Term) is used to create new ports similar to CALL ports, but the port name can be chosen freely. Such a port creates a new box. There must be a corresponding trace_exit_port/0 to exit the box on success.
- trace_exit_port is used in conjunction with trace_call_port/3 to exit a box on success.
- trace_point_port(+Port, ?Invoc, ?Term) is used to create a standalone port, i.e., a port that causes the tracer to create a trace line, but does not create, enter or leave any box.
- trace_parent_port(+Port) is used to create an additional port for the parent box, but does not enter or leave the box.

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.

### 15.7.2 Attaching a Different User Interface

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.