interface vs full debugging

Zoltan Somogyi zs at cs.mu.OZ.AU
Thu Apr 16 18:18:50 AEST 1998


For those of you not on the Mercury developers list, we have made significant
progress recently on the initial Mercury debugger (work package 2.1). Since
this debugger is based on tracing, this work counts as progress on the extended
debugger (work package 5.3) as well.

The three main items that remain to be implemented are a more complete command
set (including support for redo), a real term browser (currently we just print
every variable in total, which is quite bad if the term is big), and a means
of controlling what code should be traced. This message is asking for
opinions on what methods people consider to be the most convenient for the
programmer in addressing the third item.

By the way, in case you are curious, the commands currently supported
by the debugger are:

a, EOF:			abort the current execution.
b <module> <name>:	set a breakpoint on the predicate
			or function <module>:<name>.
c:			continue to end of program, not printing the trace.
C:			continue to end of program, printing the trace.
f:			finish this call, not printing the trace.
F:			finish this call, printing the trace.
<N> g:			go to event #N, not printing the trace.
<N> G:			go to event #N, printing the trace.
p:			print the variables live at this point.
r:			continue until forward execution is resumed.
[<N>] [s]:		skip N events, not printing the trace (default: N=1).
[<N>] S:		skip N events, printing the trace (default: N=1).
?:			list all the breakpoints.
+ <N>:			enable breakpoint #N.
- <N>:			disable breakpoint #N.

First some background. The current system generates a trace event in
the following circumstances:

call to (entry into) a procedure
successful exit from a procedure
failure out of a procedure
entry into the then part of an if-then-else
entry into the else part of an if-then-else
entry into an arm of a switch
entry into an arm of a disjunction

The first three correspond exactly to three of the ports of a four-port
debugger for Prolog, and (most cases of) the last three correspond roughly
to the fourth port. Also, the last four between them let you know what path
execution has taken through the predicate definition (which corresponds to
the identity of the clause in Prolog).

At the moment, you can ask for tracing via the option --generate-trace
to the compiler. If you compile the whole program with tracing, you can
debug the whole program using the commands above.

Most programs have some parts that the programmer is not interested in
tracing through: append, or the list module, or the entire standard
Mercury library. At the moment, you can compile some modules without
--generate-trace, but if you do that, you can't get control in the
debugger when a non-traced predicate is called or returns, even though
you may have wanted to look at the values of the arguments (since one
symptom of buggy code is that it is calling trusted code with the wrong
arguments). What you need is a way to be told of a non-traced predicate
being called, returning or failing without having to trace the execution
of the predicate itself.

Mireille proposed a mechanism, interface tracing, for doing exactly this,
in Opium. We want to implement the same thing for Mercury.

The question I would like to ask you is: how should the programmer tell
the system which set of predicates should be traced fully, and which
should be subject to only interface tracing. There are at least three
issues here.

(1) Should the user specify this on a predicate by predicate basis,
or is a module by module basis sufficient? Module by module is good
enough for most other languages (e.g. in Prolog, you can interpret some
modules while others are compiled and thus not fully debuggable, and in
C you must specify -g for an entire source file). On the other hand,
some modules have both interesting code you want to debug and dull,
straightforward data structure manipulation predicates that you do not.
(Although for access predicates that are just conjunctions of unifications
there is no difference between full debugging and interface debugging,
since they cannot give rise to internal events).

(2) Should the user specify what should be traced fully, or what should be
interface traced? Every predicate should be one or the other. (If an
interface traced predicate calls another interface traced predicate,
the call will be ignored; if an interface traced predicate calls a
fully traced predicate -possibly via a higher order call, e.g. a callback-,
the call and the execution of the called predicate will be traced.)

(3) How should the information be conveyed to the compiler. There are at
least three alternatives:

	(a) You can have command line options such as --full-trace and
	    --interface-trace.

	(b) You can have a command line option --trace-control <filename>,
	    with filename containing all the relevant info for the entire
	    program.

	(c) You can have a pragma in the source code itself.

My preference would be for (b), with each line in the named file
naming either a module in which every predicate should be interface traced
or a predicate in a given module that should be interface traced.
Everything not covered would be fully traced. This finesses issue (1),
making it convenient to tell the system that you trust entire modules while
still allowing finer control if desired. With respect to issue 2, my
position is that code should be suspect until proven otherwise, even
if you end up trusting most of the system. In any case, even if a predicate
is compiled with full tracing you can use the 'f' command to skip all
events inside its execution, so having too many modules compiled with
full tracing is not too serious a problem, whereas discovering in the
middle of debugging that you have too few modules compiled with full
tracing requires abandoning the debugging session and recompilation.
With respect to issue (3), option (a) may be convenient for manual use,
but using it from Mmakefiles is needlessly tricky, while I don't think
that embedding this info in the source file is a good idea. For one thing,
one source file may be used by several people, who may place different
levels of trust in the module (perhaps because one says 'this module
may be buggy, but it is not my job to debug it, and I don't know enough
about it to debug it anyway, so interface tracing is sufficient').

Comments?

Zoltan.



More information about the developers mailing list