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