for review: interface to external opium-style debugger
Fergus Henderson
fjh at cs.mu.OZ.AU
Tue Feb 10 04:33:50 AEDT 1998
Hi,
Zoltan, can you please review this?
Add support to the Mercury tracer for interacting with an external
Opium-style debugger via a socket.
TODO: currently it does not deal with reporting procedure arguments
to the external debugger. (Also currently the socket protocol is fairly
minimal. It might make sense to support a broader range of commands.)
runtime/mercury_trace.c:
Add code to MR_trace() to open a socket connection to
an external debugger, and to process requests from that
debugger, and send responses back.
runtime/mercury_trace.h:
runtime/mercury_wrapper.c:
Call MR_trace_end() from mercury_runtime_terminate() so that
the external debugger gets notified when the program does
a (normal) exit.
library/debugger_interface.m:
New file. Declares the Mercury types used for the debugger
socket interface and defines support routines used by
runtime/mercury_trace.c.
library/io.m:
library/mercury_types.h:
Move the definition of MercuryFile from library/io.m to
runtime/mercury_types.h, for use by runtime/mercury_trace.c.
library/library.m:
Add reference to debugger_interface.m, so that it gets
include in libmercury.a.
doc/Mmakefile:
Make sure that the library/debugger_interface.m does *not*
get included in the Mercury library reference manual.
runtime/mercury_wrapper.h:
runtime/mercury_wrapper.c:
runtime/mercury_init.h:
util/mkinit.c:
Declare, define, and initialize pointers to the C functions
exported by library/debugger_interface.m.
(This is to avoid having a circular dependency where the
runtime depends on the library.)
compiler/notes/authors.html:
Add Erwan Jahier.
Index: compiler/notes/authors.html
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/notes/authors.html,v
retrieving revision 1.1
diff -u -r1.1 authors.html
--- authors.html 1997/04/03 05:17:03 1.1
+++ authors.html 1998/02/06 08:34:02
@@ -38,6 +38,8 @@
<tr>
<td>fjh <td>Fergus Henderson <td>fjh at cs.mu.oz.au </tr>
<tr>
+<td>jahier <td>Erwan Jahier <td>jahier at irisa.fr </tr>
+<tr>
<td>ksiew <td>Steven Siew <td>ksiew at cs.mu.oz.au </tr>
<tr>
<td>petdr <td>Peter Ross <td>petdr at cs.mu.oz.au </tr>
Index: doc/Mmakefile
===================================================================
RCS file: /home/mercury1/repository/mercury/doc/Mmakefile,v
retrieving revision 1.6
diff -u -r1.6 Mmakefile
--- Mmakefile 1997/10/14 09:34:54 1.6
+++ Mmakefile 1998/02/09 10:31:58
@@ -94,30 +94,44 @@
# The following rules automatically build the library documentation
# by extracting the module interfaces from the library source code.
+# Note that the debugger_interface.m module is just an implementation
+# detail of the library, so it is not documented.
library-menu.texi: $(LIBRARY_DIR)/*.m
- { \
- echo ""; \
- for file in $(LIBRARY_DIR)/*.m; do \
- echo "* `basename $$file .m`::"; \
- done; \
+ { \
+ echo ""; \
+ for filename in $(LIBRARY_DIR)/*.m; do \
+ case $$filename in \
+ $(LIBRARY_DIR)/debugger_interface.m) \
+ ;; \
+ *) \
+ echo "* `basename $$filename .m`::"; \
+ ;; \
+ esac; \
+ done; \
} > library-menu.texi
library-chapters.texi: $(LIBRARY_DIR)/*.m
- for filename in $(LIBRARY_DIR)/*.m; do \
- file="`basename $$filename .m`"; \
- echo "@node $$file"; \
- echo "@chapter $$file"; \
- echo "@example"; \
- sed -n -e '/:- implementation/q' \
- -e 's/^%----*----% *$$/%--------------------------------------------------%/' \
- -e 's/@/@@/g' \
- -e 's/{/@{/g' \
- -e 's/}/@}/g' \
- -e 'p' \
- "$$filename"; \
- echo "@end example"; \
- echo ""; \
+ for filename in $(LIBRARY_DIR)/*.m; do \
+ case $$filename in \
+ $(LIBRARY_DIR)/debugger_interface.m) \
+ ;; \
+ *) \
+ file="`basename $$filename .m`"; \
+ echo "@node $$file"; \
+ echo "@chapter $$file"; \
+ echo "@example"; \
+ sed -n -e '/:- implementation/q' \
+ -e 's/^%----*----% *$$/%--------------------------------------------------%/' \
+ -e 's/@/@@/g' \
+ -e 's/{/@{/g' \
+ -e 's/}/@}/g' \
+ -e 'p' \
+ "$$filename"; \
+ echo "@end example"; \
+ echo ""; \
+ ;; \
+ esac; \
done > library-chapters.texi
library.dvi library_toc.html mercury_library.info: \
Index: library/debugger_interface.m
===================================================================
RCS file: debugger_interface.m
diff -N debugger_interface.m
--- /dev/null Mon Feb 9 21:27:23 1998
+++ debugger_interface.m Fri Feb 6 20:23:43 1998
@@ -0,0 +1,321 @@
+%-----------------------------------------------------------------------------%
+% Copyright (C) 1998 The University of Melbourne.
+% This file may only be copied under the terms of the GNU Library General
+% Public License - see the file COPYING.LIB in the Mercury distribution.
+%-----------------------------------------------------------------------------%
+% File: debugger_interface.m
+% Authors: fjh, jahier
+% Purpose:
+% This module provide support routines needed by runtime/mercury_trace.c
+% for interfacing to an external (in a different process) debugger,
+% in particular an Opium-style debugger.
+%
+% This module corresponds to what is called the "Query Handler" in Opium.
+
+:- module debugger_interface.
+:- interface.
+
+% This module exports the following C functions:
+% ML_DI_output_current
+% ML_DI_found_match
+% These are used by runtime/mercury_trace.c.
+
+:- pred dummy_pred_to_avoid_warning_about_nothing_exported is det.
+
+:- implementation.
+:- import_module io, require.
+:- import_module list, bool, std_util.
+
+dummy_pred_to_avoid_warning_about_nothing_exported.
+
+% The stuff defined below is also defined in modules prog_data, hlds_data.
+
+:- type arity == int.
+
+:- type determinism
+ ---> det
+ ; semidet
+ ; nondet
+ ; multidet
+ ; cc_nondet
+ ; cc_multidet
+ ; erroneous
+ ; failure.
+
+% The stuff defined below is similar to types goal_path and trace_port
+% defined in modules compiler/hlds_goal.m and compiler/trace.m.
+
+:- type trace_port_type
+ ---> call
+ ; exit
+ ; fail
+ ; ite_then
+ ; ite_else
+ ; switch
+ ; disj
+ .
+
+:- type goal_path_string == string.
+
+
+
+% This is known as "debugger_query" in the Opium documentation.
+% The debugger_request type is used for request sent
+% from the debugger process to the Mercury program being debugged.
+% This type would need to be extended to handle spypoints, etc.
+:- type debugger_request
+ ---> hello % are you there?
+
+ % A `forward_move' request instructs the debuggee
+ % to step forward until we reach a trace event
+ % which matches the specified attributes.
+ ; forward_move(
+ match(event_number),
+ match(call_number),
+ match(depth_number),
+ match(trace_port_type),
+ match(string), % module name
+ match(string), % pred name
+ match(arity),
+ match(int), % mode number
+ match(determinism),
+ match(list(univ)), % the arguments
+ % XXX we could provide better ways of
+ % matching on arguments
+ match(goal_path_string)
+ )
+
+ % A `current' request instructs the debuggee to
+ % retrieve the specified attributes of the current
+ % trace event and report them to the debugger
+ % via the socket.
+ ; current(
+ wanted, % event_number
+ wanted, % call_number
+ wanted, % depth_number
+ wanted, % port
+ wanted, % module name
+ wanted, % pred name
+ wanted, % arity
+ wanted, % proc_id (mode number)
+ wanted, % determinism
+ wanted, % arguments
+ % XXX should provide a way of getting
+ % just part of the arguments
+ % (to reduce unnecessary socket traffic)
+ wanted % goal_path_string
+ )
+ ; abort_prog
+ % just abort the program
+ ; no_trace
+ % stop tracing, and run the program to completion
+ ; error(string)
+ % something went wrong when trying to get the
+ % next request
+
+ ; ok % XXX this "request" type is probably not needed
+ .
+
+:- type event_number == int.
+:- type call_number == int.
+:- type depth_number == int.
+
+
+% `match' is called "get status" in the Opium documentation.
+% This type defines a unary predicate which determines whether
+% or not a particular value will be selected.
+:- type match(T)
+ ---> nop % nop: value = -
+ ; exact(T) % exact(X): value = X
+ ; neg(T) % neg(X): value \= X
+ ; list(list(T)) % list(L): list__member(value, L)
+ ; interval(T,T) % interval(Low, High): Low =< X, X =< High
+ .
+
+
+% The debugger_response type is used for response sent
+% to the debugger process from the Mercury program being debugged.
+% This type would need to be extended.
+:- type debugger_response
+ % responses to hello
+ ---> hello_reply % yes, I'm here
+ % responses to forward_move
+ ; forward_move_match_found
+ ; forward_move_match_not_found
+ % responses to current
+ ; current(
+ maybe(event_number),
+ maybe(call_number),
+ maybe(depth_number),
+ maybe(trace_port_type),
+ maybe(string), % module name
+ maybe(string), % pred name
+ maybe(arity),
+ maybe(int), % mode number
+ maybe(determinism),
+ maybe(list(univ)), % the arguments
+ % XXX we could provide better ways of
+ % matching on arguments
+ maybe(goal_path_string)
+ )
+ % responses to abort_prog or no_trace
+ ; ok
+ % responses to anything
+ ; error(string)
+ .
+
+% `wanted' is called "current status" in the Opium documentation
+:- type wanted == bool.
+
+
+%-----------------------------------------------------------------------------%
+
+% output_current:
+% send to the debugger (e.g. Opium) the wanted features.
+
+:- pragma export(output_current(in, in, in, in, in, in, in, in, in, in,
+ in, in, in, di, uo), "ML_DI_output_current").
+
+:- pred output_current(event_number, call_number, depth_number, trace_port_type,
+ /* module name */ string, /* pred name */ string, arity,
+ /* mode num */ int, determinism, /* the arguments */ list(univ),
+ goal_path_string, debugger_request,
+ io__output_stream, io__state, io__state).
+:- mode output_current(in, in, in, in, in, in, in, in, in, in, in, in, in,
+ di, uo) is det.
+
+output_current(EventNumber, CallNumber, DepthNumber, Port,
+ ModuleName, PredName, Arity, ModeNum, Determinism, Args,
+ Path, DebuggerRequest, OutputStream) -->
+ (
+ { DebuggerRequest = current(WantEventNumber,
+ WantCallNumber, WantDepthNumber, WantPort,
+ WantModuleName, WantPredName, WantArity,
+ WantModeNum, WantDeterminism, WantArgs, WantPath) }
+ ->
+ {
+ get_if_wanted(WantEventNumber, EventNumber,
+ MaybeEventNumber),
+ get_if_wanted(WantCallNumber, CallNumber, MaybeCallNumber),
+ get_if_wanted(WantDepthNumber, DepthNumber,
+ MaybeDepthNumber),
+ get_if_wanted(WantPort, Port, MaybePort),
+ get_if_wanted(WantModuleName, ModuleName, MaybeModuleName),
+ get_if_wanted(WantPredName, PredName, MaybePredName),
+ get_if_wanted(WantArity, Arity, MaybeArity),
+ get_if_wanted(WantModeNum, ModeNum, MaybeModeNum),
+ get_if_wanted(WantDeterminism, Determinism,
+ MaybeDeterminism),
+ get_if_wanted(WantArgs, Args, MaybeArgs),
+ get_if_wanted(WantPath, Path, MaybePath),
+ CurrentTraceInfo = current(MaybeEventNumber,
+ MaybeCallNumber, MaybeDepthNumber, MaybePort,
+ MaybeModuleName, MaybePredName, MaybeArity,
+ MaybeModeNum, MaybeDeterminism, MaybeArgs,
+ MaybePath)
+ },
+ % 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") }
+ ).
+
+:- pred get_if_wanted(wanted, T, maybe(T)).
+:- mode get_if_wanted(in, in, out) is det.
+% get_if_wanted(Wanted, Value, MaybeValue).
+get_if_wanted(yes, X, yes(X)).
+get_if_wanted(no, _, no).
+
+%-----------------------------------------------------------------------------%
+
+:- pragma export(found_match(in, in, in, in, in, in, in, in, in, in,
+ in, in), "ML_DI_found_match").
+
+:- pred found_match(event_number, call_number, depth_number, trace_port_type,
+ /* module name */ string, /* pred name */ string, arity,
+ /* mode num */ int, determinism, /* the arguments */ list(univ),
+ % XXX we could provide better ways of
+ % matching on arguments
+ goal_path_string, debugger_request).
+:- mode found_match(in, in, in, in, in, in, in, in, in, in, in, in)
+ is semidet.
+
+found_match(EventNumber, CallNumber, DepthNumber, Port,
+ ModuleName, PredName, Arity, ModeNum, Determinism, Args,
+ Path, DebuggerRequest) :-
+ (
+ DebuggerRequest = forward_move(MatchEventNumber,
+ MatchCallNumber, MatchDepthNumber, MatchPort,
+ MatchModuleName, MatchPredName, MatchArity,
+ MatchModeNum, MatchDeterminism, MatchArgs, MatchPath)
+ ->
+ match(MatchEventNumber, EventNumber),
+ match(MatchCallNumber, CallNumber),
+ match(MatchDepthNumber, DepthNumber),
+ match(MatchPort, Port),
+ match(MatchModuleName, ModuleName),
+ match(MatchPredName, PredName),
+ match(MatchArity, Arity),
+ match(MatchModeNum, ModeNum),
+ match(MatchDeterminism, Determinism),
+ match(MatchArgs, Args),
+ match(MatchPath, Path)
+ ;
+ error("found_match: forward_move expected")
+ ).
+
+
+% match(MatchPattern, Value) is true iff Value matches the specified pattern.
+:- pred match(match(T), T).
+:- mode match(in, in) is semidet.
+
+match(nop, _).
+match(exact(X), X).
+match(neg(X), Y) :- X \= Y.
+match(list(L), X) :- list__member(X, L).
+match(interval(Low, High), X) :-
+ % X >= Low, X =< High
+ compare(LE, X, High),
+ (LE = (<) ; LE = (=)),
+ compare(GE, X, Low),
+ (GE = (>) ; GE = (=)).
+
+
+%-----------------------------------------------------------------------------%
+
+:- pred read_request_from_socket(io__input_stream, debugger_request, int,
+ io__state, io__state).
+
+:- mode read_request_from_socket(in, out, out, di, uo) is det.
+
+:- pragma export(read_request_from_socket(in, out, out, di, uo),
+ "ML_DI_read_request_from_socket").
+
+read_request_from_socket(SocketStream, Request, RequestType) -->
+ io__read(SocketStream, MaybeRequest),
+ ( { MaybeRequest = ok(Request0) },
+ { Request = Request0 }
+ ; { MaybeRequest = error(ErrorMsg, _LineNum) },
+ { Request = error(ErrorMsg) }
+ ; { MaybeRequest = eof },
+ { Request = error("end of file") }
+ ),
+ { classify_request(Request, RequestType) }.
+
+:- pred classify_request(debugger_request, int).
+:- mode classify_request(in, out) is det.
+
+% the numbers here should match the definition of
+% MR_debugger_request_type in runtime/mercury_trace.c.
+
+classify_request(hello, 0).
+classify_request(forward_move(_, _, _, _, _, _, _, _, _, _, _), 1).
+classify_request(current(_, _, _, _, _, _, _, _, _, _, _), 2).
+classify_request(abort_prog, 3).
+classify_request(no_trace, 4).
+classify_request(error(_), 5).
+classify_request(ok, 6).
+
+%-----------------------------------------------------------------------------%
Index: library/io.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/io.m,v
retrieving revision 1.149
diff -u -r1.149 io.m
--- io.m 1998/02/04 07:18:34 1.149
+++ io.m 1998/02/06 05:33:39
@@ -2023,11 +2023,6 @@
** because we keep track of a little bit more information.
*/
-typedef struct mercury_file {
- FILE *file;
- int line_number;
-} MercuryFile;
-
extern MercuryFile mercury_stdin;
extern MercuryFile mercury_stdout;
extern MercuryFile mercury_stderr;
Index: library/library.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/library.m,v
retrieving revision 1.39
diff -u -r1.39 library.m
--- library.m 1998/01/23 12:33:20 1.39
+++ library.m 1998/02/06 07:09:03
@@ -32,6 +32,7 @@
:- import_module std_util, string, term, term_io, tree234, varset.
:- import_module store, rbtree, parser, lexer, ops.
:- import_module prolog.
+:- import_module debugger_interface.
% library__version must be implemented using pragma c_code,
% so we can get at the MR_VERSION and MR_FULLARCH configuration
Index: runtime/mercury_init.h
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_init.h,v
retrieving revision 1.2
diff -u -r1.2 mercury_init.h
--- mercury_init.h 1997/11/23 07:21:25 1.2
+++ mercury_init.h 1998/02/06 08:05:59
@@ -97,6 +97,16 @@
extern void ML_io_init_state(void); /* in the Mercury library */
extern void ML_io_finalize_state(void); /* in the Mercury library */
+/* in library/debugger_interface.m */
+void ML_DI_output_current(Integer, Integer, Integer, Word, String,
+ String, Integer, Integer, Word, Word, String, Word, Word);
+ /* normally ML_DI_output_current (output_current/13) */
+bool ML_DI_found_match(Integer, Integer, Integer, Word, String, String,
+ Integer, Integer, Word, Word, String, Word);
+ /* normally ML_DI_found_match (output_current/12) */
+void ML_DI_read_request_from_socket(Word, Word *, Integer *);
+
+
#endif /* not MERCURY_INIT_H */
/*---------------------------------------------------------------------------*/
Index: runtime/mercury_trace.c
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_trace.c,v
retrieving revision 1.7
diff -u -r1.7 mercury_trace.c
--- mercury_trace.c 1998/02/04 03:55:31 1.7
+++ mercury_trace.c 1998/02/09 02:10:52
@@ -69,6 +69,20 @@
MR_CMD_JUMP /* j: jump to end of current call, printing trace */
} MR_trace_cmd_type;
+/*
+** This type must match the definition of classify_request in
+** library/debugger_interface.m.
+*/
+typedef enum {
+ MR_REQUEST_HELLO = 0, /* initiate debugging session */
+ MR_REQUEST_FORWARD_MOVE = 1, /* go to the next matching trace event */
+ MR_REQUEST_CURRENT = 2, /* report data for current trace event */
+ MR_REQUEST_NO_TRACE = 3, /* continue to end, not tracing */
+ MR_REQUEST_ABORT_PROG = 4, /* abort the current execution */
+ MR_REQUEST_ERROR = 5, /* something went wrong */
+ MR_REQUEST_OK = 6, /* ok */
+} MR_debugger_request_type;
+
static MR_trace_cmd_type MR_trace_cmd = MR_CMD_NEXT;
static int MR_trace_seqno = 0;
@@ -81,6 +95,10 @@
const MR_stack_layout_entry *layout,
MR_trace_port port, int seqno, int depth,
const char *path);
+static void MR_trace_display_user(MR_trace_interact interact,
+ const MR_stack_layout_entry *layout,
+ MR_trace_port port, int seqno, int depth,
+ const char *path);
static void MR_trace_browse(int var_count,
const MR_stack_layout_vars *var_info);
static void MR_trace_browse_var(char *name,
@@ -88,6 +106,31 @@
static int MR_trace_get_cmd(void);
static void MR_trace_help(void);
+#ifdef MR_USE_DEBUGGER
+
+/* MR_debugger_socket is initialized in MR_init_socket(); */
+static bool MR_socket_initialized = FALSE;
+static MercuryFile MR_debugger_socket;
+
+static void MR_debugger_step(MR_trace_interact interact,
+ const MR_stack_layout_entry *layout,
+ MR_trace_port port, int seqno, int depth,
+ const char *path);
+static bool MR_found_match(const MR_stack_layout_entry *layout,
+ MR_trace_port port, int seqno, int depth,
+ /* XXX registers */
+ const char *path, Word search_data);
+static void MR_output_current(const MR_stack_layout_entry *layout,
+ MR_trace_port port, int seqno, int depth,
+ /* XXX registers */
+ const char *path, Word current_request);
+static void MR_init_socket(void);
+static void MR_send_message_found(void);
+static void MR_send_message_not_found(void);
+static void MR_send_message_hello_reply(void);
+
+#endif
+
#define MR_port_is_final(port) (port == MR_PORT_EXIT || port == MR_PORT_FAIL)
/*
@@ -102,6 +145,22 @@
const MR_stack_layout_entry *layout;
MR_trace_interact interact;
+/*
+We could use
+ if (MR_use_debugger) { ...
+instead of #if; this would be better in the long run.
+This would require changing mercury_wrapper.c to
+check for an additional flag in the MERCURY_OPTIONS
+environment variable and set MR_use_debugger accordingly.
+*/
+#ifdef MR_USE_DEBUGGER
+ /* connect up to Opium */
+ if (!MR_socket_initialized) {
+ MR_init_socket();
+ MR_socket_initialized = TRUE;
+ }
+#endif
+
layout = (const MR_stack_layout_entry *) layout_word;
MR_trace_event_number++;
@@ -153,6 +212,368 @@
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
+}
+
+#ifdef MR_USE_DEBUGGER
+
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#if 0
+This pseudocode should go in the debugger process:
+
+#define SOCKET_PATH "/var/tmp/" /* +5 for pid = 14 chars */
+#define SOCKET_PERM S_IRWXU /* rwx for user only */
+
+sprintf(Name, "%s%05d", SOCKET_PATH, getpid());
+
+socket(unix, stream, Sock),
+bind(sock, Name, Socket_file),
+if (do_it_manually) {
+ printf("user: you must do
+ setenv MERCURY_INET_DEBUGGER_SOCKET Name
+ and then run the program");
+ ... just wait for the user do it ...
+} else {
+ fork()
+ if (child) {
+ setenv(MERCURY_UNIX_DEBUGGER_SOCKET, Name);
+ exec(user program)
+ }
+}
+listen(Sock, 1),
+accept(Sock, _, New_Sock).
+
+#endif
+
+#if 0
+
+static void
+MR_init_unix_address(const char *name, struct sockaddr_un *unix_addr)
+{
+ /*
+ ** The code here is adapted from Stevens, "Advanced Programming
+ ** in the UNIX environment", page 501.
+ ** Don't blame me, I'm just copying this code from Stevens ;-)
+ */
+
+ memset(unix_addr, 0, sizeof(unix_addr));
+ unix_addr->sun_family = AF_UNIX;
+ strcpy(unix_addr->sun_path, name);
+ #ifdef SCM_RIGHTS
+ len = sizeof(unix_addr->sun_len) +
+ sizeof(unix_addr->sun_family) +
+ strlen(unix_addr->sun_path) + 1;
+ unix_addr->sun_len = len;
+ #else
+ len = strlen(unix_addr->sun_path) +
+ sizeof(unix_addr->sun_family);
+ if (len != 16) {
+ fatal_error("unix socket: length != 16");
+ }
+ #endif
+}
+#endif
+
+static void
+MR_init_socket(void)
+{
+ int fd;
+ int len;
+ FILE *file;
+ int addr_family;
+ char *unix_socket;
+ char *inet_socket;
+ struct sockaddr_un unix_address;
+ struct sockaddr_in inet_address;
+ struct sockaddr* addr;
+ Word debugger_request;
+ Integer debugger_request_type;
+
+ /*
+ ** We presume that the user's program has been invoked from
+ ** within the debugger (e.g. Opium).
+ ** The debugger (or the user) should set the
+ ** MERCURY_DEBUGGER_UNIX_SOCKET or MERCURY_DEBUGGER_INET_SOCKET
+ ** environment variable to tell the user's program which socket
+ ** it needs to connect to.
+ */
+ unix_socket = getenv("MERCURY_DEBUGGER_UNIX_SOCKET");
+ inet_socket = getenv("MERCURY_DEBUGGER_INET_SOCKET");
+ if (unix_socket == NULL && inet_socket == NULL) {
+ fatal_error("you must set either the "
+ "MERCURY_DEBUGGER_UNIX_SOCKET\n"
+ "or MERCURY_DEBUGGER_INET_SOCKET "
+ "environment variable");
+ }
+ if (unix_socket != NULL && inet_socket != NULL) {
+ fatal_error("you must set only one of the "
+ "MERCURY_DEBUGGER_UNIX_SOCKET "
+ "and MERCURY_DEBUGGER_INET_SOCKET\n"
+ "environment variables");
+ }
+ if (unix_socket) {
+ unlink(unix_socket); /* in case it already exists */
+
+ addr_family = AF_UNIX;
+ memset(&unix_address, 0, sizeof(unix_address));
+ unix_address.sun_family = AF_UNIX;
+ strcpy(unix_address.sun_path, unix_socket);
+ addr = (struct sockaddr *) &unix_address;
+ len = SUN_LEN(&unix_address);
+ } else {
+ char hostname[255];
+ char port_string[255];
+ unsigned short port;
+ int host_addr;
+
+ /*
+ ** Parse the MERCURY_DEBUGGER_INET_SOCKET environment variable.
+ ** It should be in the format "<hostname> <port>",
+ ** where <hostname> is numeric (e.g. "123.456.78.90").
+ */
+ if (sscanf(inet_socket, "%254s %254s", hostname, port_string)
+ != 2)
+ {
+ fatal_error("MERCURY_DEBUGGER_INET_SOCKET invalid");
+ }
+ host_addr = inet_network(hostname);
+ if (host_addr == -1) {
+ fatal_error("MERCURY_DEBUGGER_INET_SOCKET: "
+ "invalid address");
+ }
+ if (sscanf(port_string, "%hu", &port) != 1) {
+ fatal_error("MERCURY_DEBUGGER_INET_SOCKET: "
+ "invalid port");
+ }
+
+ fprintf(stderr, "Mercury runtime: host = %s, port = %d\n",
+ hostname, port);
+
+ inet_address.sin_family = AF_INET;
+ inet_address.sin_addr.s_addr = host_addr;
+ inet_address.sin_port = htons(port);
+ addr_family = AF_INET;
+ addr = (struct sockaddr *) &inet_address;
+ len = sizeof(inet_address);
+ }
+
+ /*
+ ** Open the socket.
+ */
+
+ fd = socket(addr_family, SOCK_STREAM, 0);
+ if (fd < 0) {
+ fprintf(stderr, "Mercury runtime: socket() failed: %s\n",
+ strerror(errno));
+ fatal_error("cannot open socket for debugger");
+ }
+
+ fprintf(stderr, "Mercury runtime: connecting to debugger...\n");
+
+ /*
+ ** Connect to the socket
+ */
+ if (connect(fd, addr, len) < 0) {
+ fprintf(stderr, "Mercury runtime: connect() failed: %s\n",
+ strerror(errno));
+ fatal_error("can't connect to debugger socket");
+ }
+
+ fprintf(stderr, "Mercury runtime: connected to debugger.\n");
+
+ /*
+ ** Convert the socket fd to a Mercury stream
+ */
+ file = fdopen(fd, "r+");
+ if (file == NULL) {
+ fprintf(stderr, "Mercury runtime: fdopen() failed: %s\n",
+ strerror(errno));
+ fatal_error("cannot open debugger socket");
+ }
+
+ MR_debugger_socket.file = file;
+ MR_debugger_socket.line_number = 1;
+
+ /*
+ ** Wait for hello
+ */
+ MR_DI_read_request_from_socket((Word) &MR_debugger_socket,
+ &debugger_request, &debugger_request_type);
+ if (debugger_request_type != MR_REQUEST_HELLO) {
+ fatal_error("unexpected command on debugger socket");
+ }
+
+ /*
+ ** Send hello_reply
+ */
+ MR_send_message_hello_reply();
+}
+
+void
+MR_trace_end(void)
+{
+ if (MR_socket_initialized) {
+ /*
+ ** This can only happen during a forward_move(),
+ ** in which case we want to tell the debugger that
+ ** no match was found.
+ */
+ MR_send_message_not_found();
+ }
+ /*
+ ** Maybe we should loop to process requests from the
+ ** debugger socket here? Currently we just return,
+ ** which will result in the debuggee terminating.
+ ** (This will need to change if the debuggee is to record
+ ** trace history and support a `move_backward' request.)
+ */
+}
+
+static void
+MR_debugger_step(MR_trace_interact interact,
+ const MR_stack_layout_entry *layout,
+ MR_trace_port port, int seqno, int depth, const char *path)
+{
+ static bool searching = FALSE;
+ static Word search_data;
+ Word debugger_request;
+ Integer debugger_request_type;
+
+ if (searching) {
+ /* XXX should also pass registers here,
+ since they're needed for checking for matches with the
+ arguments */
+ if (MR_found_match(layout, port, seqno, depth,
+ /* XXX registers */ path, search_data))
+ {
+ MR_send_message_found();
+ searching = FALSE;
+ } else {
+ return;
+ }
+ }
+
+ /* loop to process requests read from the debugger socket */
+ for(;;) {
+ MR_DI_read_request_from_socket((Word) &MR_debugger_socket,
+ &debugger_request, &debugger_request_type);
+ switch((int) debugger_request_type) {
+ case MR_REQUEST_ABORT_PROG:
+ fatal_error("aborting the execution on "
+ "user request");
+
+ case MR_REQUEST_FORWARD_MOVE:
+ search_data = debugger_request;
+ searching = TRUE;
+ return;
+
+ case MR_REQUEST_CURRENT:
+ /* XXX should also pass registers here,
+ since they're needed for printing the
+ arguments */
+ MR_output_current(layout, port, seqno, depth,
+ /* XXX registers */
+ path, debugger_request);
+ break;
+
+ case MR_REQUEST_NO_TRACE:
+ MR_trace_cmd = MR_CMD_CONT;
+ return;
+
+ default:
+ fatal_error("unexpected request read from "
+ "debuggger socket");
+ }
+ }
+}
+
+static void
+MR_output_current(const MR_stack_layout_entry *layout,
+ MR_trace_port port, int seqno, int depth,
+ /* XXX registers */
+ const char *path, Word current_request)
+{
+ /* XXX get arguments from registers */
+ Word arguments = /* XXX FIXME!!! */ 0;
+ MR_DI_output_current(
+ MR_trace_event_number,
+ seqno,
+ depth,
+ port,
+ layout->MR_sle_def_module,
+ layout->MR_sle_name,
+ layout->MR_sle_arity,
+ layout->MR_sle_mode,
+ layout->MR_sle_detism,
+ arguments,
+ (String) (Word) path,
+ current_request,
+ (Word) &MR_debugger_socket);
+}
+
+static bool
+MR_found_match(const MR_stack_layout_entry *layout,
+ MR_trace_port port, int seqno, int depth,
+ /* XXX registers */
+ const char *path, Word search_data)
+{
+ /* XXX get arguments from registers */
+ Word arguments = /* XXX FIXME!!! */ 0;
+ return MR_DI_found_match(
+ MR_trace_event_number,
+ seqno,
+ depth,
+ port,
+ layout->MR_sle_def_module,
+ layout->MR_sle_name,
+ layout->MR_sle_arity,
+ layout->MR_sle_mode,
+ layout->MR_sle_detism,
+ arguments,
+ (String) (Word) path,
+ search_data);
+}
+
+static void
+MR_send_message_found(void)
+{
+ fprintf(MR_debugger_socket.file, "forward_move_match_found.\n");
+ MR_debugger_socket.line_number++;
+}
+
+static void
+MR_send_message_not_found(void)
+{
+ fprintf(MR_debugger_socket.file, "forward_move_match_not_found.\n");
+ MR_debugger_socket.line_number++;
+}
+
+static void
+MR_send_message_hello_reply(void)
+{
+ fprintf(MR_debugger_socket.file, "hello_reply.\n");
+ MR_debugger_socket.line_number++;
+}
+
+#endif /* MR_USE_DEBUGGER */
+
+static void
+MR_trace_display_user(MR_trace_interact interact,
const MR_stack_layout_entry *layout,
MR_trace_port port, int seqno, int depth, const char *path)
{
Index: runtime/mercury_trace.h
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_trace.h,v
retrieving revision 1.5
diff -u -r1.5 mercury_trace.h
--- mercury_trace.h 1998/02/03 08:17:26 1.5
+++ mercury_trace.h 1998/02/06 07:40:44
@@ -23,9 +23,18 @@
extern int MR_trace_call_seqno;
extern int MR_trace_call_depth;
+/*
+** This enum should exactly match the definition of the `trace_port' type in
+** library/debugger_interface.
+*/
typedef enum {
- MR_PORT_CALL, MR_PORT_EXIT, MR_PORT_FAIL,
- MR_PORT_THEN, MR_PORT_ELSE, MR_PORT_DISJ, MR_PORT_SWITCH
+ MR_PORT_CALL,
+ MR_PORT_EXIT,
+ MR_PORT_FAIL,
+ MR_PORT_THEN,
+ MR_PORT_ELSE,
+ MR_PORT_DISJ,
+ MR_PORT_SWITCH
} MR_trace_port;
extern void MR_trace(
@@ -34,5 +43,13 @@
int, /* call sequence number */
int, /* call depth */
const char *); /* path to event goal within procedure */
+
+#ifdef MR_USE_DEBUGGER
+/*
+** MR_trace_end() is called from mercury_runtime_terminate()
+** when the debuggee programs is exiting.
+*/
+extern void MR_trace_end(void);
+#endif
#endif /* MERCURY_TRACE_H */
Index: runtime/mercury_types.h
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_types.h,v
retrieving revision 1.8
diff -u -r1.8 mercury_types.h
--- mercury_types.h 1998/02/03 08:17:28 1.8
+++ mercury_types.h 1998/02/06 05:34:55
@@ -13,6 +13,7 @@
#define MERCURY_TYPES_H
#include "mercury_conf.h"
+#include <stdio.h> /* for `FILE' */
/*
** Note that we require sizeof(Word) == sizeof(Integer) == sizeof(Code*)
@@ -54,6 +55,12 @@
/* spinlocks -- see mercury_spinlock.h */
typedef Word SpinLock;
+
+/* MercuryFile is used for the Mercury io__stream type in library/io.m */
+typedef struct mercury_file {
+ FILE *file;
+ int line_number;
+} MercuryFile;
/*
** semidet predicates indicate success or failure by leaving nonzero or zero
Index: runtime/mercury_wrapper.c
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_wrapper.c,v
retrieving revision 1.6
diff -u -r1.6 mercury_wrapper.c
--- mercury_wrapper.c 1998/02/03 08:17:31 1.6
+++ mercury_wrapper.c 1998/02/06 07:50:55
@@ -37,6 +37,7 @@
#include "mercury_getopt.h"
#include "mercury_init.h"
#include "mercury_dummy.h"
+#include "mercury_trace.h"
/* global variables concerned with testing (i.e. not with the engine) */
@@ -129,6 +130,13 @@
/* normally ML_io_finalize_state (io__finalize_state/2) */
Code *MR_library_trace_browser;
/* normally mercury__io__print_3_0 (io__print/3) */
+void (*MR_DI_output_current_ptr)(Integer, Integer, Integer, Word, String,
+ String, Integer, Integer, Word, Word, String, Word, Word);
+ /* normally ML_DI_output_current (output_current/13) */
+bool (*MR_DI_found_match)(Integer, Integer, Integer, Word, String, String,
+ Integer, Integer, Word, Word, String, Word);
+ /* normally ML_DI_found_match (output_current/12) */
+bool (*MR_DI_read_request_from_socket)(Word, Word *, Integer *);
#ifdef USE_GCC_NONLOCAL_GOTOS
@@ -959,6 +967,10 @@
** and our caller may need them.
*/
save_regs_to_mem(c_regs);
+
+#ifdef MR_USE_DEBUGGER
+ MR_trace_end();
+#endif
(*MR_library_finalizer)();
Index: runtime/mercury_wrapper.h
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_wrapper.h,v
retrieving revision 1.5
diff -u -r1.5 mercury_wrapper.h
--- mercury_wrapper.h 1998/02/03 08:17:32 1.5
+++ mercury_wrapper.h 1998/02/06 07:06:35
@@ -53,6 +53,20 @@
extern void (*address_of_init_gc)(void);
#endif
+/*
+** Similarly, these are for the debugger interface; they're defined in
+** library/debugger_interface.m.
+*/
+void (*MR_DI_output_current)(Integer, Integer, Integer, Word, String,
+ String, Integer, Integer, Word, Word, String, Word, Word);
+ /* normally ML_DI_output_current (output_current/13) */
+bool (*MR_DI_found_match)(Integer, Integer, Integer, Word, String, String,
+ Integer, Integer, Word, Word, String, Word);
+ /* normally ML_DI_found_match (output_current/12) */
+bool (*MR_DI_read_request_from_socket)(Word, Word *, Integer *);
+ /* normally ML_DI_read_request_from_socket
+ (read_request_from_socket/5) */
+
extern void do_init_modules(void);
extern const char * progname;
Index: util/mkinit.c
===================================================================
RCS file: /home/mercury1/repository/mercury/util/mkinit.c,v
retrieving revision 1.26
diff -u -r1.26 mkinit.c
--- mkinit.c 1998/02/03 08:20:12 1.26
+++ mkinit.c 1998/02/06 07:25:58
@@ -133,6 +133,12 @@
" MR_library_initializer = ML_io_init_state;\n"
" MR_library_finalizer = ML_io_finalize_state;\n"
" MR_library_trace_browser = ENTRY(mercury__io__print_3_0);\n"
+ "#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"
"#if defined(USE_GCC_NONLOCAL_GOTOS) && !defined(USE_ASM_LABELS)\n"
" do_init_modules();\n"
"#endif\n"
--
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