for review: interface to external opium-style debugger
Fergus Henderson
fjh at cs.mu.OZ.AU
Tue Feb 17 01:34:25 AEDT 1998
Hi Zoltan,
Thanks for your review.
On 16-Feb-1998, Zoltan Somogyi <zs at cs.mu.OZ.AU> wrote:
>
> > +# Note that the debugger_interface.m module is just an implementation
> > +# detail of the library, so it is not documented.
>
> I don't like the idea of putting something in the standard library
> and then hiding it. It may be better to make this module a new library,
> one that is included in the executable by the linker when necessary.
Hmm. On the one hand I'm inclined to agree with you;
on the other, it seems like overkill to have a whole new
library if it only contains one module.
How about if we leave it as is for the moment, but split it up
if/when the stuff in debugger_interface.m becomes complicated
enough to warrant being in more than one module?
I suppose we could make a new subdirectory (with corresponding library)
and put both debugger_interface.m and most of mercury_trace.c in it.
We could change MR_trace from being a function to being a function pointer,
and remove the other function pointers.
I guess this is probably the right way to go in the long term.
But changing all the infrastructure is quite a bit of work.
Do you think we should commit it as is for now,
or do you think we should wait until we've made the changes
outlined above before committing?
> In the medium term we would probably want the debugger_interface.m
> to be rewritten in C (in the short term, the inefficiency of its code
> can be ignored, while in the long term, new optimizations should allow
> the compiler to ged rid of the inefficiencies).
I'm not convinced that it _all_ needs to be so efficient.
Certainly the MR_trace() function needs to be fast in cases which
don't touch the socket, e.g. when doing a `skip' command,
and perhaps also for the `found_match' predicate (currently
written in Mercury) which tests whether the current port
matches the constraints specified by a `forward_move' request.
But for the cases which are going to involve socket I/O,
the overhead of doing it in Mercury does not seem to me to
be excessive, and the benefits (e.g. no need to write parsing
or formatting code) are substantial.
> > + % responses to current
> > + ; current(
...
> > + maybe(list(univ)), % the arguments
> > + % XXX we could provide better ways of
> > + % matching on arguments
>
> I don't understand this comment.
Oops, that was a cut-and-paste error. The fix is to delete the comment.
> > + % XXX does the protocal require us to write out
> > + % anything special first?
> > + io__write(OutputStream, CurrentTraceInfo),
> > + io__write_string(".\n")
> > + ;
> > + { error("output_current: current expected") }
> > + ).
>
> You have to make sure that the protocol specifies how strings in arguments
> will be handled, since strings may contain the character sequence that
> terminates the answer. Whatever the answer, the above is unlikely to be right.
The code above assumes that the protocol is that requests/responses are passed
as Mercury/Prolog terms, in the format used by io__read and io__write.
That is, strings in the arguments are quoted using double-quotes,
double-quotes in the strings are escaped using backslashes, and terms
are terminated with a period followed by whitespace (e.g. \n).
This is a convenient protocol since the parsing and formatting
is trivial, on both the Prolog and the Mercury side.
However, now that I think about it, there is a problem.
The problem is that for a forward_move request, the argument
constraint is represented as a `match(list(univ))'; this
will cause problems because io__read does not support reading in univs.
But as noted in an XXX comment, `match(list(univ))' is not really
sufficient anyway -- what you really need is some way of doing partial matches.
A fix would be to change it to say `list(term)', and add code for
matching against terms. (The quick prototype version of this code is
match_with_term(Value, MatchTerm) :-
type_to_term(Value, ValueTerm),
map__init(Substitution0),
term__unify(ValueTerm, MatchTerm, Substitution0, _Subst).
but for a usable version you need to interleave the computation
of term__unify and type_to_term. Lazy evaluation would be nice here ;-)
The lack of support for univ in io__read would also cause problems
when reporting the current arguments, if the external debugger was
written in Mercury. But the current plan is to write it in Prolog.
> > +#ifdef MR_USE_DEBUGGER
> > + /* connect up to Opium */
> > + if (!MR_socket_initialized) {
> > + MR_init_socket();
> > + MR_socket_initialized = TRUE;
> > + }
> > +#endif
>
> The trace function will be executed literally billions of times per run.
> You want to squeeze out every overhead possible. The above should be
> removed from MR_trace. The _init.c file should call another function
> in mercury_trace.c to initialize the socket.
OK.
> > static void
> > MR_trace_display(MR_trace_interact interact,
> > + const MR_stack_layout_entry *layout,
> > + MR_trace_port port, int seqno, int depth, const char *path)
> > +{
> > +#ifdef MR_USE_DEBUGGER
> > + MR_debugger_step(interact, layout, port, seqno, depth, path);
> > +#else
> > + MR_trace_display_user(interact, layout, port, seqno, depth, path);
> > +#endif
> > +}
>
> Why do you want to hang this functionality on MR_trace_display?
> To reuse the current infrastructure of command modes in MR_trace?
Yes.
> You want to use a different set of command modes for trace analysis,
> and thus providing an alternate definition of MR_trace rather than
> MR_trace_display would be in order.
>
> If you don't want to do this now, at least rename MR_trace_display.
OK.
> Whether mercury_trace.c defines MR_trace_end or not should not depend
> on a #define (which should be a variable test, as the diff itself said above).
> I suggest you always define MR_trace_end, but it should not do anything
> if we are not doing trace analysis.
OK.
The problem with making it a variable test rather than a #ifdef
is that the code uses sockets, which makes it less than completely
portable. At least some of it needs to be #ifdef'd, and so it
seemed easier to #ifdef it all. But I agree that it would be
nicer if we could keep the #ifdefs in the implementation rather
than in the interface, so I'll change this one.
> mkinit.c:
> > + "#ifdef MR_USE_DEBUGGER\n"
> > + " MR_DI_output_current = ML_DI_output_current;\n"
> > + " MR_DI_found_match = ML_DI_found_match;\n"
> > + " MR_DI_read_request_from_socket = "
> > + "ML_DI_read_request_from_socket;\n"
> > + "#endif\n"
>
> And again.
Here I don't agree. If this was unconditional, it would force
debugger_interface.o to be linked into every program, which is
undesirable.
--
Fergus Henderson <fjh at cs.mu.oz.au> | "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh> | of excellence is a lethal habit"
PGP: finger fjh at 128.250.37.3 | -- the last words of T. S. Garp.
More information about the developers
mailing list