diff: interface to external opium-style debugger (round 2)

Fergus Henderson fjh at cs.mu.OZ.AU
Thu Mar 12 08:36:44 AEDT 1998


Hi,

This version addresses most of the comments Zoltan raised
in his review.  It also includes (working) code for sending the
procedure arguments down the socket to the debugger.

I plan to commit this one shortly.

----------------------------------------------------------------------

Add support to the Mercury tracer for interacting with an external
Opium-style debugger via a socket.

TODO: currently there is no way for the debugger to specify
constraints on the arguments for a `forward_move' query.
The current code for doing that doesn't work.
(However, the reporting of arguments to the debugger
for an `output_current' query works fine.)

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.

runtime/stack_layout.h:
	Fix a bug where the structures here did not match
	the output produced by compiler/stack_layout.m.

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/io.m:
	When printing out values of type `c_pointer', print out
	the "<<c_pointer>>" string in single quotes, to ensure
	that it has valid syntax for a Prolog or Mercury term.
	This is necessary because otherwise c_pointers in procedure
	arguments could lead to the Mercury process sending a
	syntactically invalid term down the socket to the debugger,
	which would then be unable to parse it.

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.

cvs diff: Diffing .
cvs diff: Diffing bindist
cvs diff: Diffing boehm_gc
cvs diff: Diffing boehm_gc/Mac_files
cvs diff: Diffing boehm_gc/cord
cvs diff: Diffing boehm_gc/cord/private
cvs diff: Diffing boehm_gc/include
cvs diff: Diffing boehm_gc/include/private
cvs diff: Diffing bytecode
cvs diff: Diffing bytecode/test
cvs diff: Diffing compiler
cvs diff: Diffing compiler/notes
Index: compiler/notes/authors.html
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/notes/authors.html,v
retrieving revision 1.1
diff -u -u -r1.1 authors.html
--- authors.html	1997/04/03 05:17:03	1.1
+++ authors.html	1998/02/27 16:44:19
@@ -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>
cvs diff: Diffing doc
Index: doc/Mmakefile
===================================================================
RCS file: /home/mercury1/repository/mercury/doc/Mmakefile,v
retrieving revision 1.6
diff -u -u -r1.6 Mmakefile
--- Mmakefile	1997/10/14 09:34:54	1.6
+++ Mmakefile	1998/02/27 16:47:09
@@ -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: \
cvs diff: Diffing extras
cvs diff: Diffing extras/cgi
cvs diff: Diffing extras/clpr
cvs diff: Diffing extras/clpr/clpr
cvs diff: Diffing extras/clpr/samples
cvs diff: Diffing extras/complex_numbers
cvs diff: Diffing extras/complex_numbers/samples
cvs diff: Diffing extras/complex_numbers/tests
cvs diff: Diffing extras/graphics
cvs diff: Diffing extras/graphics/Togl-1.2
cvs diff: Diffing extras/graphics/mercury_opengl
cvs diff: Diffing extras/graphics/mercury_tcltk
cvs diff: Diffing extras/graphics/samples
cvs diff: Diffing extras/graphics/samples/calc
cvs diff: Diffing extras/graphics/samples/maze
cvs diff: Diffing extras/odbc
cvs diff: Diffing extras/references
cvs diff: Diffing extras/trailed_update
cvs diff: Diffing extras/trailed_update/samples
cvs diff: Diffing library
Index: library/io.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/io.m,v
retrieving revision 1.151
diff -u -u -r1.151 io.m
--- io.m	1998/02/27 16:11:15	1.151
+++ io.m	1998/03/11 19:16:40
@@ -1598,7 +1598,7 @@
 
 io__write_c_pointer(_C_Pointer) -->
 	% XXX what should we do here?
-	io__write_string("<<c_pointer>>").
+	io__write_string("'<<c_pointer>>'").
 
 %-----------------------------------------------------------------------------%
 
@@ -2038,11 +2038,6 @@
 ** Mercury files are not quite the same as C stdio FILEs,
 ** 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;
Index: library/library.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/library.m,v
retrieving revision 1.39
diff -u -u -r1.39 library.m
--- library.m	1998/01/23 12:33:20	1.39
+++ library.m	1998/02/27 16:44:31
@@ -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
cvs diff: Diffing lp_solve
cvs diff: Diffing lp_solve/lp_examples
cvs diff: Diffing profiler
cvs diff: Diffing runtime
Index: runtime/mercury_init.h
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_init.h,v
retrieving revision 1.2
diff -u -u -r1.2 mercury_init.h
--- mercury_init.h	1997/11/23 07:21:25	1.2
+++ mercury_init.h	1998/03/11 21:28:11
@@ -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, Integer, 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, Integer, Word, String, Word);
+		/* normally ML_DI_found_match (found_match/12) */
+void	ML_DI_read_request_from_socket(Word, Word *, Integer *);
+
+
 #endif /* not MERCURY_INIT_H */
 
 /*---------------------------------------------------------------------------*/
Index: runtime/mercury_stack_layout.h
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_stack_layout.h,v
retrieving revision 1.1
diff -u -u -r1.1 mercury_stack_layout.h
--- mercury_stack_layout.h	1998/03/11 05:58:38	1.1
+++ mercury_stack_layout.h	1998/03/11 20:30:28
@@ -130,8 +130,8 @@
 } MR_Lval_NonVar;
 
 typedef struct { 
-	Word	type;
-	Word	inst;
+	Word	type;	/* contains a type_info */
+	Word	inst;	/* not yet used; currently always -1 */
 } MR_Var_Shape_Info;
 
 #define MR_LIVE_TYPE_IS_VAR(T)         ( (Word) T > TYPELAYOUT_MAX_VARINT )
@@ -227,14 +227,9 @@
 ** Structs and macros to support stack layouts.
 */
 
-typedef	struct MR_stack_layout_shape_struct {
-	Word			*MR_sls_type;
-	Word			MR_sls_inst;
-} MR_stack_layout_shape;
-
 typedef	struct MR_stack_layout_var_struct {
-	Integer			MR_slv_locn;
-	MR_stack_layout_shape	*MR_slv_shape;
+	MR_Live_Lval		MR_slv_locn;
+	MR_Live_Type		MR_slv_live_type;
 } MR_stack_layout_var;
 
 typedef	struct MR_stack_layout_vars_struct {
Index: runtime/mercury_trace.c
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_trace.c,v
retrieving revision 1.7
diff -u -u -r1.7 mercury_trace.c
--- mercury_trace.c	1998/02/04 03:55:31	1.7
+++ mercury_trace.c	1998/03/11 21:29:21
@@ -29,8 +29,8 @@
 ** MR_trace_call_depth records the current depth of the call tree. The prologue
 ** of every procedure assigns the current value of this variable plus one
 ** as the depth of that invocation. Just before making a call, the caller
-** will set MR_trace_call_depth to its own remembered depth value. These
-** are the only ways in which MR_trace_call_seqno is modified.
+** will set MR_trace_call_depth to its own remembered depth value. 
+** These are the only ways in which MR_trace_call_depth is modified.
 **
 ** Although neither MR_trace_call_seqno nor MR_trace_call_depth are used
 ** directly in this module, the seqno and depth arguments of MR_trace
@@ -46,7 +46,7 @@
 ** use it for display.
 */
 
-static	int			MR_trace_event_number = 0;
+static	int	MR_trace_event_number = 0;
 
 /*
 ** MR_trace_cmd and MR_trace_seqno are globals variables that we use
@@ -61,7 +61,6 @@
 */
 
 typedef enum {
-	MR_CMD_ABORT,	/* a: abort the current execution		  */
 	MR_CMD_CONT,	/* c: continue to end, not printing the trace	  */
 	MR_CMD_DUMP,	/* d: continue to end, printing the trace	  */
 	MR_CMD_NEXT,	/* n: go to the next trace event		  */
@@ -69,6 +68,19 @@
 	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_REPLY = 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_debugger_request_type;
+
 static	MR_trace_cmd_type	MR_trace_cmd = MR_CMD_NEXT;
 static	int			MR_trace_seqno = 0;
 
@@ -77,7 +89,11 @@
 	MR_NO_INTERACT
 } MR_trace_interact;
 
-static void	MR_trace_display(MR_trace_interact interact,
+static void	MR_trace_event(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_display_user(MR_trace_interact interact,
 			const MR_stack_layout_entry *layout,
 			MR_trace_port port, int seqno, int depth,
 			const char *path);
@@ -88,6 +104,48 @@
 static int	MR_trace_get_cmd(void);
 static void	MR_trace_help(void);
 
+static Word	MR_trace_make_var_list(MR_trace_port port,
+			const MR_stack_layout_entry *layout);
+static Word	MR_trace_lookup_live_lval(MR_Live_Lval locn, bool *succeeded);
+static bool	MR_trace_get_type_and_value(const MR_stack_layout_var *var,
+			Word *type_info, Word *value);
+
+/*
+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
+
+static MercuryFile MR_debugger_socket_in;
+static MercuryFile MR_debugger_socket_out;
+
+static void	MR_send_message_to_socket(const char *message);
+static void	MR_read_request_from_socket(
+			Word *debugger_request_ptr, 
+			Integer *debugger_request_type_ptr);
+	
+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,
+			Word var_list,
+			const char *path, Word current_request);
+
+static void	MR_copy_saved_regs_to_regs(void);
+static void	MR_copy_regs_to_saved_regs(void);
+
+#endif
+
 #define	MR_port_is_final(port)	(port == MR_PORT_EXIT || port == MR_PORT_FAIL)
 
 /*
@@ -107,7 +165,7 @@
 	MR_trace_event_number++;
 	switch (MR_trace_cmd) {
 		case MR_CMD_NEXT:
-			MR_trace_display(MR_INTERACT, layout,
+			MR_trace_event(MR_INTERACT, layout,
 				port, seqno, depth, path);
 			break;
 
@@ -119,7 +177,7 @@
 				interact = MR_NO_INTERACT;
 			}
 
-			MR_trace_display(interact, layout,
+			MR_trace_event(interact, layout,
 				port, seqno, depth, path);
 
 			break;
@@ -127,7 +185,7 @@
 		case MR_CMD_SKIP:
 			if (MR_trace_seqno == seqno && MR_port_is_final(port))
 			{
-				MR_trace_display(MR_INTERACT, layout,
+				MR_trace_event(MR_INTERACT, layout,
 					port, seqno, depth, path);
 			}
 
@@ -137,14 +195,10 @@
 			break;
 
 		case MR_CMD_DUMP:
-			MR_trace_display(MR_NO_INTERACT, layout,
+			MR_trace_event(MR_NO_INTERACT, layout,
 				port, seqno, depth, path);
 			break;
 
-		case MR_CMD_ABORT:
-			fatal_error("aborting the execution on user request");
-			break;
-
 		default:
 			fatal_error("MR_trace called with inappropriate port");
 			break;
@@ -152,7 +206,407 @@
 }
 
 static void
-MR_trace_display(MR_trace_interact interact,
+MR_trace_event(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_copy_regs_to_saved_regs();
+	MR_debugger_step(interact, layout, port, seqno, depth, path);
+	MR_copy_saved_regs_to_regs();
+#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 bool MR_debug_socket = FALSE;
+
+void
+MR_trace_init(void)
+{
+	int fd;
+	int len;
+	FILE *file_in;
+	FILE *file_out;
+	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) {
+	
+		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");
+	} else if (MR_debug_socket) {
+		fprintf(stderr,"Mercury runtime: creation of socket ok\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");
+	} else if (MR_debug_socket) {
+		fprintf(stderr, "Mercury runtime: connection to socket: ok\n");
+	}
+
+	/*
+	** Convert the socket fd to a Mercury stream
+	*/
+	file_in = fdopen(fd, "r"); 
+	file_out = fdopen(fd, "w");
+	if ((file_in == NULL)||(file_out == NULL)) {
+		fprintf(stderr, "Mercury runtime: fdopen() failed: %s\n",
+			strerror(errno));
+		fatal_error("cannot open debugger socket");
+	} else if (MR_debug_socket) {
+		fprintf(stderr, "Mercury runtime: fdopen(): ok\n");
+	}
+
+	MR_debugger_socket_in.file = file_in;
+	MR_debugger_socket_in.line_number = 1;
+
+	MR_debugger_socket_out.file = file_out;
+	MR_debugger_socket_out.line_number = 1;
+
+	/*
+	** Send hello
+	*/
+
+	MR_send_message_to_socket("hello");
+	if (MR_debug_socket) {
+		fprintf(stderr, "Mercury runtime: Send hello\n");
+	}
+
+	/*
+	** Wait for hello_reply  
+	*/
+
+	MR_read_request_from_socket(&debugger_request, &debugger_request_type);
+
+	if (debugger_request_type != MR_REQUEST_HELLO_REPLY) {
+		fatal_error("unexpected command on debugger socket");
+	} else if (MR_debug_socket) {
+		fprintf(stderr, "Mercury runtime: read hello_reply\n");
+	}
+
+	/*
+	** Send start to start the synchronous communication with the debugger
+	*/
+
+	MR_send_message_to_socket("start"); 
+	if (MR_debug_socket) {
+		fprintf(stderr, "Mercury runtime: start send\n");
+	}
+}
+
+void
+MR_trace_end(void)
+{
+	/*
+	** 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_to_socket("forward_move_match_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;
+	Word var_list;
+
+do_search:
+	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_to_socket("forward_move_match_found");
+			searching = FALSE;
+		} else {
+			return;
+		}
+	}
+
+	/* loop to process requests read from the debugger socket */
+	for(;;) {
+		MR_read_request_from_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:
+				if (MR_debug_socket) {
+					fprintf(stderr, "\nMercury runtime: "
+						"FORWARD_MOVE\n");
+				}
+				search_data = debugger_request;
+			        searching = TRUE;
+				goto do_search;
+			       			      		
+			case MR_REQUEST_CURRENT:
+				if (MR_debug_socket) {
+					fprintf(stderr, "\nMercury runtime: "
+						"REQUEST_CURRENT\n");
+				}
+				var_list = MR_trace_make_var_list(port, layout);
+				MR_output_current(layout, port, seqno, depth,
+					var_list, path, debugger_request);
+				break;
+				
+			case MR_REQUEST_NO_TRACE:
+				MR_trace_cmd = MR_CMD_CONT;
+				return;
+
+			default:
+				fatal_error("unexpected request read from "
+					"debugger socket");
+		}
+	}
+}
+
+static void
+MR_output_current(const MR_stack_layout_entry *layout,
+	MR_trace_port port, int seqno, int depth,
+	Word var_list,
+	const char *path, Word current_request)
+{
+	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,
+		var_list,
+		(String) (Word) path,
+		current_request,
+		(Word) &MR_debugger_socket_out);
+}
+
+static void
+MR_read_request_from_socket(
+			Word *debugger_request_ptr, 
+			Integer *debugger_request_type_ptr)
+{		
+	fflush(MR_debugger_socket_in.file);
+	MR_DI_read_request_from_socket(
+		(Word) &MR_debugger_socket_in, 
+		debugger_request_ptr, 
+		debugger_request_type_ptr);
+}
+ 
+
+static bool
+MR_found_match(const MR_stack_layout_entry *layout,
+	MR_trace_port port, int seqno, int depth,
+	/* XXX live vars */
+	const char *path, Word search_data)
+{
+	bool result;
+
+	/* XXX get live vars from registers */
+	Word arguments = /* XXX FIXME!!! */ 0; 
+	result = 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);
+	return result;
+}
+
+static void
+MR_send_message_to_socket(const char *message)
+{
+	fprintf(MR_debugger_socket_out.file, "%s.\n", message);
+	fflush(MR_debugger_socket_out.file);
+	MR_debugger_socket_out.line_number++;
+}
+
+#else /* !MR_USE_DEBUGGER */
+
+void MR_trace_init(void) {}
+void MR_trace_end(void) {}
+
+#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)
 {
@@ -195,7 +649,8 @@
 			break;
 
 		default:
-			fatal_error("MR_trace_display called with bad port");
+			fatal_error("MR_trace_display_user called "
+					"with bad port");
 	}
 
 	switch ((int) layout->MR_sle_detism) {
@@ -232,7 +687,7 @@
 			break;
 		
 		default:
-			fprintf(stderr, "???  ");
+			fprintf(stderr, "mercury_trace.c: ???  ");
 			break;
 	}
 
@@ -312,11 +767,10 @@
 						" you want to abort? ");
 
 				if (MR_trace_get_cmd() == 'y') {
-					MR_trace_cmd = MR_CMD_ABORT;
-					break;
-				} else {
-					continue;
+					fatal_error("aborting the execution "
+						"on user request");
 				}
+				continue;
 
 			default:
 				MR_trace_help();
@@ -330,74 +784,149 @@
 static Word	MR_saved_regs[MAX_FAKE_REG];
 
 static void
-MR_trace_browse(int var_count, const MR_stack_layout_vars *vars)
+MR_copy_regs_to_saved_regs(void)
 {
-	int	i;
-	char	*name;
-
-	if (var_count == 0) {
-		printf("mtrace: no live variables\n");
-		return;
-	}
-
 	/*
 	** In the process of browsing, we call Mercury code,
 	** which may clobber the contents of the registers,
 	** both the control registers and the general purpose registers.
 	** We must therefore save and restore these.
 	**
+	** XXX This is very inefficient!
+	**
 	** Some are in real machine registers; others in the fake_reg array.
+	** We need to copy them all to the fake_reg array, because the
+	** calling convention for calling Mercury functions exported to C
+	** assumes that they will be in the fake_reg array.
 	*/
 
-	save_regs_to_mem(MR_saved_regs);
-	for (i = NUM_REAL_REGS; i < MAX_FAKE_REG; i++)
+	int i;
+
+	restore_transient_registers();
+	save_registers();
+	for (i = 0; i < MAX_FAKE_REG; i++) {
 		MR_saved_regs[i] = fake_reg[i];
+	}
+}
+
+static void
+MR_copy_saved_regs_to_regs(void)
+{
+	int i;
+
+	for (i = 0; i < MAX_FAKE_REG; i++) {
+		fake_reg[i] = MR_saved_regs[i];
+	}
+	restore_registers();
+	save_transient_registers();
+}
+
+static Word
+MR_trace_make_var_list(MR_trace_port port, const MR_stack_layout_entry *layout)
+{
+	int 				var_count;
+	const MR_stack_layout_vars 	*vars;
+	int				i;
+	const char			*name;
+
+	Word				univ_list;
+	MR_stack_layout_var*		var;
+	Word				univ, value;
+	MR_Live_Type			live_type;
+	Word				type_info;
+
+	restore_transient_registers();
+
+	if (port == MR_PORT_CALL) {
+		var_count = layout->MR_sle_in_arg_count;
+		vars = &layout->MR_sle_in_arg_info;
+	} else if (port == MR_PORT_EXIT) {
+		var_count = layout->MR_sle_out_arg_count;
+		vars = &layout->MR_sle_out_arg_info;
+	} else {
+		return list_empty();
+	}
+
+	/* build up the live variable list, starting from the end */
+	univ_list = list_empty();
+	for (i = var_count - 1; i >= 0; i--) {
+		/*
+		** Look up the name, the type and value
+		** (XXX we don't include the name or the inst
+		** in the list that we return)
+		*/
+		if (vars->MR_slvs_names != NULL &&
+				vars->MR_slvs_names[i] != NULL)
+		{
+			name = vars->MR_slvs_names[i];
+		} else {
+			name = "";
+		}
+		var = &vars->MR_slvs_pairs[i];
+		if (!MR_trace_get_type_and_value(var, &type_info, &value)) {
+			continue;
+		}
+
+		/* create a term of type `univ' to hold the type & value */
+		incr_hp(univ, 2);
+		field(mktag(0), univ, UNIV_OFFSET_FOR_TYPEINFO) = type_info;
+		field(mktag(0), univ, UNIV_OFFSET_FOR_DATA) = value;
+		
+		univ_list = list_cons(univ, univ_list);
+	}
+
+	save_transient_registers();
+
+	return univ_list;
+}
+
+static void
+MR_trace_browse(int var_count, const MR_stack_layout_vars *vars)
+{
+	int	i;
+	char	*name;
+
+	if (var_count == 0) {
+		printf("mtrace: no live variables\n");
+		return;
+	}
+
+	MR_copy_regs_to_saved_regs();
 
 	for (i = 0; i < var_count; i++) {
 		if (vars->MR_slvs_names != NULL &&
 				vars->MR_slvs_names[i] != NULL)
+		{
 			name = vars->MR_slvs_names[i];
-		else
+		} else {
 			name = NULL;
+		}
 
 		MR_trace_browse_var(name, &vars->MR_slvs_pairs[i]);
 	}
 
-	restore_regs_from_mem(MR_saved_regs);
-	for (i = NUM_REAL_REGS; i < MAX_FAKE_REG; i++)
-		 fake_reg[i] = MR_saved_regs[i];
+	MR_copy_saved_regs_to_regs();
 }
 
-/* if you want to debug this code, you may want to set this var to 1 */
-static	int	MR_trace_print_locn = 0;
+/* if you want to debug this code, you may want to set this var to TRUE */
+static	bool	MR_trace_print_locn = FALSE;
 
-static void
-MR_trace_browse_var(char *name, const MR_stack_layout_var *var)
+static Word
+MR_trace_lookup_live_lval(MR_Live_Lval locn, bool *succeeded)
 {
-	Integer			locn;
-	Word			value;
-	int			print_value;
-	int			locn_num;
-
-	/* The initial blanks are to visually separate */
-	/* the variable names from the prompt. */
-
-	if (name != NULL)
-		printf("%10s%-21s\t", "", name);
-	else
-		printf("%10s%-21s\t", "", "anonymous variable");
+	int	locn_num;
+	Word	value;
 
-	value = 0; /* not used; this shuts up a compiler warning */
-	print_value = FALSE;
+	*succeeded = FALSE;
+	value = 0;
 
-	locn = var->MR_slv_locn;
 	locn_num = (int) MR_LIVE_LVAL_NUMBER(locn);
 	switch (MR_LIVE_LVAL_TYPE(locn)) {
 		case MR_LVAL_TYPE_R:
 			if (MR_trace_print_locn)
 				printf("r%d", locn_num);
 			value = saved_reg(MR_saved_regs, locn_num);
-			print_value = TRUE;
+			*succeeded = TRUE;
 			break;
 
 		case MR_LVAL_TYPE_F:
@@ -408,15 +937,19 @@
 		case MR_LVAL_TYPE_STACKVAR:
 			if (MR_trace_print_locn)
 				printf("stackvar%d", locn_num);
+			/* XXX BUG! detstackvar() will give wrong results
+			   because MR_sp is not valid */
 			value = detstackvar(locn_num);
-			print_value = TRUE;
+			*succeeded = TRUE;
 			break;
 
 		case MR_LVAL_TYPE_FRAMEVAR:
 			if (MR_trace_print_locn)
 				printf("framevar%d", locn_num);
+			/* XXX BUG! detstackvar() will give wrong results
+			   because MR_currfr is not valid */
 			value = framevar(locn_num);
-			print_value = TRUE;
+			*succeeded = TRUE;
 			break;
 
 		case MR_LVAL_TYPE_SUCCIP:
@@ -455,19 +988,53 @@
 			break;
 	}
 
-	if (print_value) {
+	return value;
+}
+
+static bool
+MR_trace_get_type_and_value(const MR_stack_layout_var *var,
+	Word *type_info, Word *value)
+{
+	bool succeeded;
+
+	if (MR_LIVE_TYPE_IS_VAR(var->MR_slv_live_type)) {
+		*type_info = MR_LIVE_TYPE_GET_VAR_TYPE(var->MR_slv_live_type);
+	} else {
+		return FALSE;
+	}
+	*value = MR_trace_lookup_live_lval(var->MR_slv_locn, &succeeded);
+	return succeeded;
+}
+
+static void
+MR_trace_browse_var(char *name, const MR_stack_layout_var *var)
+{
+	Word			value, type_info;
+	bool			print_value;
+
+	/* The initial blanks are to visually separate */
+	/* the variable names from the prompt. */
+
+	if (name != NULL) {
+		printf("%10s%-21s\t", "", name);
+	} else {
+		printf("%10s%-21s\t", "", "anonymous variable");
+	}
+
+	if (MR_trace_get_type_and_value(var, &type_info, &value)) {
 		printf("\t");
 
 		/*
 		** XXX It would be nice if we could call an exported C
 		** function version of the browser predicate, and thus
 		** avoid going through call_engine, but for some unknown
-		** reason, that seems to cause the Mercury code in the
+		** reason, that seemed to cause the Mercury code in the
 		** browser to clobber part of the C stack.
+		** Probably that was due to a bug which has since been
+		** fixed, so we should change the code below back again...
 		*/
-
-		r1 = (Word) var->MR_slv_shape->MR_sls_type;
-		r2 = (Word) value;
+		r1 = type_info;
+		r2 = value;
 		call_engine(MR_library_trace_browser);
 	}
 
Index: runtime/mercury_trace.h
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_trace.h,v
retrieving revision 1.5
diff -u -u -r1.5 mercury_trace.h
--- mercury_trace.h	1998/02/03 08:17:26	1.5
+++ mercury_trace.h	1998/03/11 19:38:45
@@ -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,14 @@
 	int,			/* call sequence number */
 	int,			/* call depth */
 	const char *);		/* path to event goal within procedure */
+
+/*
+** MR_trace_init() is called from mercury_runtime_init()
+** when the debuggee programs begins.
+** MR_trace_end() is called from mercury_runtime_terminate()
+** when the debuggee programs is exiting.
+*/
+extern	void	MR_trace_init(void);
+extern	void	MR_trace_end(void);
 
 #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 -u -r1.8 mercury_types.h
--- mercury_types.h	1998/02/03 08:17:28	1.8
+++ mercury_types.h	1998/02/27 16:44:31
@@ -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 -u -r1.6 mercury_wrapper.c
--- mercury_wrapper.c	1998/02/03 08:17:31	1.6
+++ mercury_wrapper.c	1998/03/11 21:28:20
@@ -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, Integer, 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, Integer, Word, String, Word);
+		/* normally ML_DI_found_match (output_current/12) */
+void	(*MR_DI_read_request_from_socket)(Word, Word *, Integer *);
 
 #ifdef USE_GCC_NONLOCAL_GOTOS
 
@@ -237,6 +245,8 @@
 	restore_transient_registers();
 	save_registers();
 
+	MR_trace_init();
+
 	/* initialize the Mercury library */
 	(*MR_library_initializer)();
 
@@ -959,6 +969,8 @@
 	** and our caller may need them.
 	*/
 	save_regs_to_mem(c_regs);
+
+	MR_trace_end();
 
 	(*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 -u -r1.5 mercury_wrapper.h
--- mercury_wrapper.h	1998/02/03 08:17:32	1.5
+++ mercury_wrapper.h	1998/03/11 21:28:16
@@ -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, Integer, 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, Integer, Word, String, Word);
+		/* normally ML_DI_found_match (output_current/12) */
+void	(*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;
cvs diff: Diffing runtime/machdeps
cvs diff: Diffing samples
cvs diff: Diffing samples/c_interface
cvs diff: Diffing samples/c_interface/c_calls_mercury
cvs diff: Diffing samples/c_interface/cplusplus_calls_mercury
cvs diff: Diffing samples/c_interface/mercury_calls_c
cvs diff: Diffing samples/c_interface/mercury_calls_cplusplus
cvs diff: Diffing samples/c_interface/simpler_c_calls_mercury
cvs diff: Diffing samples/c_interface/simpler_cplusplus_calls_mercury
cvs diff: Diffing samples/diff
cvs diff: Diffing scripts
cvs diff: Diffing tests
cvs diff: Diffing tests/benchmarks
cvs diff: Diffing tests/general
cvs diff: Diffing tests/hard_coded
cvs diff: Diffing tests/hard_coded/typeclasses
cvs diff: Diffing tests/invalid
cvs diff: Diffing tests/misc_tests
cvs diff: Diffing tests/term
cvs diff: Diffing tests/valid
cvs diff: Diffing tests/warnings
cvs diff: Diffing tools
cvs diff: Diffing trial
cvs diff: Diffing util
Index: util/mkinit.c
===================================================================
RCS file: /home/mercury1/repository/mercury/util/mkinit.c,v
retrieving revision 1.28
diff -u -u -r1.28 mkinit.c
--- mkinit.c	1998/03/11 06:01:17	1.28
+++ mkinit.c	1998/03/11 20:00:08
@@ -133,6 +133,11 @@
 	"	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"
cvs diff: Diffing www
cvs diff: Diffing www/bench
cvs diff: Diffing www/developer
cvs diff: Diffing www/doc
cvs diff: Diffing www/papers
cvs diff: Diffing www/patches
cvs diff: Diffing www/pics

-- 
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