for review: subdivision of mercury_trace.c

Zoltan Somogyi zs at cs.mu.OZ.AU
Thu Apr 9 18:57:43 AEST 1998


This ought to make further development easier.

runtime/mercury_trace.[ch]:
runtime/mercury_trace_cmd.h:
runtime/mercury_trace_internal.[ch]:
runtime/mercury_trace_external.[ch]:
runtime/mercury_trace_util.[ch]:
	Divide the old mercury_trace.c into several components, with one
	module for the internal debugger, one for the interface to the
	external debugger, one for utilities needed by both. Mercury_trace.c
	now has only the top-level stuff that steers between the two
	debuggers. It now has two interface files: mercury_trace.h (which
	is unchanged) to compiled code and mercury_trace_cmd.h to allow
	the internal debugger module to control how events are handled.

runtime/Mmakefile:
	Update for the new source and header files.

Zoltan.

::::::::::::::
mercury_trace.c
::::::::::::::
/*
** Copyright (C) 1997-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.
*/

/*
** mercury_trace.c - implements the top levels of the tracing subsystem,
** which interface to the automatically generated code in user programs.
** The trace events can be processed either by our internal debugger,
** or by an external trace-analysis style debugger. These are in
** mercury_trace_internal.c and mercury_trace_external.c respectively.
** Utility functions usable by both the internal and external debuggers
** are in mercury_trace_util.c.
**
** This source file has two header files. One, mercury_trace.h, contains
** the interface we export to user level programs. The other, which is
** in mercury_trace_cmd.h contains the interface we export to the
** internal debugger to allow it to control how events are handled.
**
** Main author: Zoltan Somogyi.
*/

#include "mercury_imp.h"
#include "mercury_trace.h"
#include "mercury_trace_cmd.h"
#include "mercury_trace_util.h"
#include "mercury_trace_internal.h"
#include "mercury_trace_external.h"
#include "mercury_engine.h"
#include "mercury_wrapper.h"
#include "mercury_misc.h"
#include <stdio.h>
#include <unistd.h>		/* for the write system call */

/*
** Do we want to use the debugger within this process, or do want to use
** the Opium-style trace analyzer debugger implemented by an external process.
** This variable is set in mercury_wrapper.c and never modified afterwards.
*/

MR_trace_type	MR_trace_handler = MR_TRACE_INTERNAL;

/*
** Compiler generated tracing code will check whether MR_trace_enabled is true,
** before calling MR_trace. For now, and until we implement interface tracing,
** MR_trace_enabled should keep the same value throughout the execution of
** the entire program after being set in mercury_wrapper.c. There is one
** exception to this: the Mercury routines called as part of the functionality
** of the tracer itself (e.g. the term browser) should always be executed
** with MR_trace_enabled set to FALSE.
*/

bool	MR_trace_enabled = FALSE;

/*
** MR_trace_call_seqno counts distinct calls. The prologue of every
** procedure assigns the current value of this counter as the sequence number
** of that invocation and increments the counter. This is the only way that
** MR_trace_call_seqno is modified.
**
** 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_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
** always derive their values from the saved values of these two global
** variables.
*/

int	MR_trace_call_seqno = 0;
int	MR_trace_call_depth = 0;

/*
** MR_trace_event_number is a simple counter of events. This is used in
** two places: here, for display to the user and for skipping a given number
** of events, and when printing an abort message, so that the programmer
** can zero in on the source of the problem more quickly.
*/

int	MR_trace_event_number = 0;

MR_trace_cmd_type	MR_trace_cmd = MR_CMD_GOTO;
int			MR_trace_stop_seqno = 0;
int			MR_trace_stop_event = 0;
bool			MR_trace_print_intermediate = FALSE;

static void	MR_trace_event(MR_trace_interact interact,
			const MR_Stack_Layout_Label *layout,
			MR_trace_port port, int seqno, int depth,
			const char *path, int max_r_num);

void
MR_trace_init(void)
{
#ifdef MR_USE_EXTERNAL_DEBUGGER
	if (MR_trace_handler == MR_TRACE_EXTERNAL)
		MR_trace_init_external();
#endif
}

void
MR_trace_end(void)
{
#ifdef MR_USE_EXTERNAL_DEBUGGER
	if (MR_trace_handler == MR_TRACE_EXTERNAL)
		MR_trace_end_external();
#endif
}

/*
** This function is called from compiled code whenever an event to be traced
** occurs.
*/

void
MR_trace(const MR_Stack_Layout_Label *layout, MR_trace_port port,
	int seqno, int depth, const char *path, int max_r_num)
{
	MR_trace_event_number++;
	switch (MR_trace_cmd) {
		case MR_CMD_FINISH:
			if (MR_trace_stop_seqno == seqno
			&& MR_port_is_final(port)) {
				MR_trace_event(MR_INTERACT, layout,
					port, seqno, depth, path, max_r_num);

			} else if (MR_trace_print_intermediate) {
				MR_trace_event(MR_NO_INTERACT, layout,
					port, seqno, depth, path, max_r_num);
			}

			break;

		case MR_CMD_GOTO:
			if (MR_trace_event_number >= MR_trace_stop_event
			|| MR_event_matches_spy_point(layout)) {
				MR_trace_event(MR_INTERACT, layout,
					port, seqno, depth, path, max_r_num);
			} else if (MR_trace_print_intermediate) {
				MR_trace_event(MR_NO_INTERACT, layout,
					port, seqno, depth, path, max_r_num);
			}

			break;

		case MR_CMD_RESUME_FORWARD:
			if (MR_port_is_final(port)) {
				MR_trace_event(MR_INTERACT, layout,
					port, seqno, depth, path, max_r_num);
			} else if (MR_trace_print_intermediate) {
				MR_trace_event(MR_NO_INTERACT, layout,
					port, seqno, depth, path, max_r_num);
			}

			break;

		case MR_CMD_TO_END:
			if (MR_event_matches_spy_point(layout)) {
				MR_trace_event(MR_INTERACT, layout,
					port, seqno, depth, path, max_r_num);
			} else if (MR_trace_print_intermediate) {
				MR_trace_event(MR_NO_INTERACT, layout,
					port, seqno, depth, path, max_r_num);
			}

			break;

		default:
			fatal_error("invalid cmd in MR_trace");
			break;
	}
}

static void
MR_trace_event(MR_trace_interact interact,
	const MR_Stack_Layout_Label *layout, MR_trace_port port,
	int seqno, int depth, const char *path, int max_r_num)
{
	int	max_mr_num;

	if (max_r_num + MR_NUM_SPECIAL_REG > MR_MAX_SPECIAL_REG_MR) {
		max_mr_num = max_r_num + MR_NUM_SPECIAL_REG;
	} else {
		max_mr_num = MR_MAX_SPECIAL_REG_MR;
	}

	MR_copy_regs_to_saved_regs(max_mr_num);
#ifdef MR_USE_EXTERNAL_DEBUGGER
	if (MR_trace_debugger == MR_TRACE_EXTERNAL) {
		MR_debugger_step(interact, layout, port, seqno, depth, path);
	} else {
		MR_trace_display_user(interact, layout, port, seqno, depth,
			path);
	}
#else
	/*
	** We should get here only if MR_trace_debugger == MR_TRACE_INTERNAL.
	** This is enforced by mercury_wrapper.c.
	*/

	MR_trace_display_user(interact, layout, port, seqno, depth, path);
#endif
	MR_copy_saved_regs_to_regs(max_mr_num);
}

void
MR_trace_report(FILE *fp)
{
	if (MR_trace_event_number > 0) {
		/*
		** This means that the executable was compiled with tracing,
		** which implies that the user wants trace info on abort.
		*/

		fprintf(fp, "Last trace event was event #%d.\n",
			MR_trace_event_number);
	}
}

void
MR_trace_report_raw(int fd)
{
	char	buf[80];	/* that ought to be more than long enough */

	if (MR_trace_event_number > 0) {
		/*
		** This means that the executable was compiled with tracing,
		** which implies that the user wants trace info on abort.
		*/

		sprintf(buf, "Last trace event was event #%d.\n",
			MR_trace_event_number);
		write(fd, buf, strlen(buf));
	}
}
::::::::::::::
mercury_trace.h
::::::::::::::
/*
** Copyright (C) 1997-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.
*/

/*
** mercury_trace.h - defines the interface between
** the tracing subsystem and compiled code.
**
** The macros and functions defined in this module are intended to be called
** only from code generated by the Mercury compiler, and from hand-written
** code in the Mercury runtime or the Mercury standard library.
*/

#ifndef MERCURY_TRACE_H
#define MERCURY_TRACE_H

#define	MR_trace_incr_seq()	(++MR_trace_call_seqno)
#define	MR_trace_incr_depth()	(++MR_trace_call_depth)
#define	MR_trace_reset_depth(d)	do { MR_trace_call_depth = (d); } while (0)

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_PRAGMA_FIRST,
	MR_PORT_PRAGMA_LATER
} MR_trace_port;

extern	void	MR_trace(
	const MR_Stack_Layout_Label *,	/* layout info for the event */
	MR_trace_port,
	int,			/* call sequence number */
	int,			/* call depth */
	const char *,		/* path to event goal within procedure */
	int);			/* highest numbered rN register in use */

/*
** These functions will report the number of the last event,
** if there have been some events, and will do nothing otherwise.
*/

extern	void	MR_trace_report(FILE *fp);
extern	void	MR_trace_report_raw(int fd);

/*
** 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);

typedef	enum {
	MR_TRACE_INTERNAL,
	MR_TRACE_EXTERNAL
} MR_trace_type;

extern	MR_trace_type	MR_trace_handler;
extern	bool		MR_trace_enabled;

extern	int		MR_trace_event_number;

#endif /* MERCURY_TRACE_H */
::::::::::::::
mercury_trace_cmd.h
::::::::::::::
/*
** 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.
*/

/*
** This file defines the commands meaningful to the trace event handler
** and declares the variables whose values give the parameters of those
** commands.
*/

/*
** MR_trace_cmd says what mode the tracer is in, i.e. what the last
** trace command was.
**
** If MR_trace_cmd == MR_CMD_GOTO, the event handler will stop at the next
** event whose event number is equal to or greater than MR_trace_stop_event.
**
** If MR_trace_cmd == MR_CMD_FINISH, the event handler will stop at the next
** event that specifies the procedure invocation whose call number is in
** MR_trace_stop_seqno and whose port is final.
**
** If MR_trace_cmd == MR_CMD_RESUME_FORWARD, the event handler will stop at
** the next event of any call whose port is *not* final.
**
** If MR_trace_cmd == MR_CMD_TO_END, the event handler will not stop
** until the end of the program.
**
** If the event handler does not stop at an event, it will print the
** summary line for the event if MR_trace_print_intermediate is true.
*/

typedef enum {
	MR_CMD_GOTO,		/* stop at an event with a given number    */
	MR_CMD_FINISH,		/* stop when exiting/failing out of a proc */
	MR_CMD_RESUME_FORWARD,	/* stop at the next non-final port         */
	MR_CMD_TO_END		/* do not stop until the end of execution  */
} MR_trace_cmd_type;

extern	MR_trace_cmd_type	MR_trace_cmd;
extern	int			MR_trace_stop_seqno;
extern	int			MR_trace_stop_event;
extern	bool			MR_trace_print_intermediate;

#define	MR_port_is_final(port)	(port == MR_PORT_EXIT || port == MR_PORT_FAIL)

/*
** When displaying an event to the user, is interaction allowed?
** I.e. are we stopping at this event?
*/

typedef	enum {
	MR_INTERACT,
	MR_NO_INTERACT
} MR_trace_interact;
::::::::::::::
mercury_trace_external.c
::::::::::::::
/*
** 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.
*/

/*
** This file contains the code that interfaces between the Mercury program
** being debugged and the external trace analysis process.
**
** For the general basis of trace analysis systems, see the paper
** "Opium: An extendable trace analyser for Prolog" by Mireille Ducasse,
** available from http://www.irisa.fr/lande/ducasse.
**
** Main authors: Erwan Jahier and Fergus Henderson.
*/

#ifdef MR_USE_EXTERNAL_DEBUGGER

#include "mercury_imp.h"
#include "mercury_trace.h"
#include "mercury_trace_external.h"
#include "mercury_trace_util.h"
#include <stdio.h>
#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>

/*
** 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;

/*
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.
*/

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 bool	MR_found_match(const MR_Stack_Layout_Label *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_Label *layout,
			MR_trace_port port, int seqno, int depth,
			Word var_list,
			const char *path, Word current_request);

#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_external(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_external(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.)
	*/
}

void
MR_debugger_step(MR_trace_interact interact,
	const MR_Stack_Layout_Label *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_TO_END;
				return;

			default:
				fatal_error("unexpected request read from "
					"debugger socket");
		}
	}
}

static void
MR_output_current(const MR_Stack_Layout_Label *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_sll_entry->MR_sle_def_module,
		layout->MR_sll_entry->MR_sle_name,
		layout->MR_sll_entry->MR_sle_arity,
		layout->MR_sll_entry->MR_sle_mode,
		layout->MR_sll_entry->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_Label *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_sll_entry->MR_sle_def_module,
		layout->MR_sll_entry->MR_sle_name,
		layout->MR_sll_entry->MR_sle_arity,
		layout->MR_sll_entry->MR_sle_mode,
		layout->MR_sll_entry->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++;
}

#endif /* MR_USE_EXTERNAL_DEBUGGER */
::::::::::::::
mercury_trace_external.h
::::::::::::::
/*
** 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.
*/

#ifndef	MERCURY_TRACE_EXTERNAL_H
#ifdef	MR_USE_EXTERNAL_DEBUGGER

extern	void	MR_trace_init_external(void);
extern	void	MR_trace_end_external(void);
extern	void	MR_debugger_step(MR_trace_interact interact,
			const MR_Stack_Layout_Label *layout,
			MR_trace_port port, int seqno, int depth,
			const char *path);

#endif	/* MR_USE_EXTERNAL_DEBUGGER */
#endif	/* MERCURY_TRACE_EXTERNAL_H */
::::::::::::::
mercury_trace_internal.c
::::::::::::::
/*
** 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.
*/

/*
** This file contains the code of the internal, in-process debugger.
**
** Main author: Zoltan Somogyi.
*/

#include "mercury_imp.h"
#include "mercury_trace.h"
#include "mercury_trace_cmd.h"
#include "mercury_trace_internal.h"
#include "mercury_trace_util.h"
#include <stdio.h>
#include <ctype.h>

#define	MR_NAME_LEN		80
#define	MR_MAX_SPY_POINTS	100
#define	MR_LOG10_MAX_SPY_POINTS	20

typedef struct {
	bool	enabled;
	char	module_name[MR_NAME_LEN];
	char	pred_name[MR_NAME_LEN];
} MR_spy_point;

static	MR_spy_point	MR_spy_points[MR_MAX_SPY_POINTS];
static	int		MR_next_spy_point = 0;

static void	MR_trace_browse(int var_count,
			const MR_Stack_Layout_Vars *var_info);
static void	MR_trace_browse_var(const char *name,
			const MR_Stack_Layout_Var *var, Word *type_params);

static void	MR_add_spy_point(void);
static void	MR_list_spy_points(void);
static void	MR_change_spy_point_status(bool status);

static int	MR_trace_skip_spaces(int c);
static void	MR_trace_discard_to_eol(int c);
static int	MR_trace_get_word(int *c, char word[], int len);
static void	MR_trace_help(void);

void
MR_trace_display_user(MR_trace_interact interact,
	const MR_Stack_Layout_Label *layout,
	MR_trace_port port, int seqno, int depth, const char *path)
{
	int	i;
	int	c;
	int	count;
	bool	count_given;

	printf("%8d: %6d %2d ", MR_trace_event_number, seqno, depth);

	switch (port) {
		case MR_PORT_CALL:
			printf("CALL ");
			break;

		case MR_PORT_EXIT:
			printf("EXIT ");
			break;

		case MR_PORT_FAIL:
			printf("FAIL ");
			break;

		case MR_PORT_THEN:
			printf("THEN ");
			break;

		case MR_PORT_ELSE:
			printf("ELSE ");
			break;

		case MR_PORT_DISJ:
			printf("DISJ ");
			break;

		case MR_PORT_SWITCH:
			printf("SWTC ");
			break;

		case MR_PORT_PRAGMA_FIRST:
			printf("FRST ");
			break;

		case MR_PORT_PRAGMA_LATER:
			printf("LATR ");
			break;

		default:
			fatal_error("MR_trace_display_user called "
					"with bad port");
	}

	switch ((int) layout->MR_sll_entry->MR_sle_detism) {
		case MR_DETISM_DET:
			printf("DET   ");
			break;

		case MR_DETISM_SEMI:
			printf("SEMI  ");
			break;

		case MR_DETISM_NON:
			printf("NON   ");
			break;

		case MR_DETISM_MULTI:
			printf("MUL   ");
			break;

		case MR_DETISM_ERRONEOUS:
			printf("ERR   ");
			break;

		case MR_DETISM_FAILURE:
			printf("FAIL  ");
			break;

		case MR_DETISM_CCNON:
			printf("CCNON ");
			break;

		case MR_DETISM_CCMULTI:
			printf("CCMUL ");
			break;
		
		default:
			printf("BAD DETERMINISM\n");
			break;
	}

	/*
	** The following should be a full identification of the procedure
	** provided (a) there was no intermodule optimization and (b) we are
	** not interested in tracing compiler-generated procedures.
	*/

	printf("%s:%s/%ld-%ld %s\n",
		layout->MR_sll_entry->MR_sle_def_module,
		layout->MR_sll_entry->MR_sle_name,
		(long) layout->MR_sll_entry->MR_sle_arity,
		(long) layout->MR_sll_entry->MR_sle_mode,
		path);

	while (interact == MR_INTERACT) {
		printf("mtrace> ");

		count = 1;
		count_given = FALSE;
		MR_trace_print_intermediate = FALSE;

		c = MR_trace_skip_spaces(' ');
		if (isdigit(c)) {
			count_given = TRUE;
			count = c - '0';
			c = getchar();
			while (c != EOF && isdigit(c)) {
				count = (count * 10) + c - '0';
				c = getchar();
			}

			c = MR_trace_skip_spaces(c);
		}

		switch (c) {
			case 'S':
				MR_trace_print_intermediate = TRUE;
				/* fall through */

			case 's':
			case '\n':
				MR_trace_cmd = MR_CMD_GOTO;
				MR_trace_stop_event =
					MR_trace_event_number + count;
				MR_trace_discard_to_eol(c);
				break;

			case 'G':
				MR_trace_print_intermediate = TRUE;
				/* fall through */

			case 'g':
				if (! count_given) {
					MR_trace_discard_to_eol(c);
					printf("mtrace: no count given\n");
					continue;
				}

				MR_trace_cmd = MR_CMD_GOTO;
				MR_trace_stop_event = count;
				MR_trace_discard_to_eol(c);
				break;

			case 'F':
				MR_trace_print_intermediate = TRUE;
				/* fall through */

			case 'f':
				if (MR_port_is_final(port)) {
					MR_trace_discard_to_eol(c);
					printf("mtrace: this port is "
						"already final\n");
					continue;
				} else {
					MR_trace_cmd = MR_CMD_FINISH;
					MR_trace_stop_seqno = seqno;
				}

				MR_trace_discard_to_eol(c);
				break;

			case 'C':
				MR_trace_print_intermediate = TRUE;
				/* fall through */

			case 'c':
				if (count_given)
					printf("mtrace: count ignored\n");

				MR_trace_cmd = MR_CMD_TO_END;
				MR_trace_discard_to_eol(c);
				break;

			case 'p':
				if (count_given)
					printf("mtrace: count ignored\n");

				MR_trace_discard_to_eol(c);
				MR_trace_browse((int)
					layout->MR_sll_var_count,
					&layout->MR_sll_var_info);

				continue;

			case 'r':
				if (count_given)
					printf("mtrace: count ignored\n");

				MR_trace_cmd = MR_CMD_RESUME_FORWARD;
				MR_trace_discard_to_eol(c);
				break;

			case 'b':
				if (count_given)
					printf("mtrace: count ignored\n");

				MR_add_spy_point();
				continue;

			case '?':
				if (count_given)
					printf("mtrace: count ignored\n");

				MR_list_spy_points();
				continue;

			case '+':
				if (count_given)
					printf("mtrace: count ignored\n");

				MR_change_spy_point_status(TRUE);
				continue;

			case '-':
				if (count_given)
					printf("mtrace: count ignored\n");

				MR_change_spy_point_status(FALSE);
				continue;

			case 'a':
			case EOF:
				MR_trace_discard_to_eol(c);
				printf("mtrace: are you sure"
						" you want to abort? ");

				c = MR_trace_skip_spaces(' ');
				if (c == 'y' || c == EOF) {
					/*
					** We reset MR_trace_event_number
					** that fatal_error will not
					** print the last trace event number
					** (since in this case it is not
					** meaningful).
					*/

					MR_trace_event_number = 0;
					fatal_error("aborting the execution "
						"on user request");
				}

				MR_trace_discard_to_eol(c);
				continue;

			default:
				MR_trace_discard_to_eol(c);
				MR_trace_help();
				continue;
		}

		interact = MR_NO_INTERACT;
	}
}

static void
MR_trace_browse(int var_count, const MR_Stack_Layout_Vars *vars)
{
	Word	*type_params;
	bool	succeeded;
	int	i;

	if (var_count == 0) {
		printf("mtrace: no live variables\n");
		return;
	}

	type_params = checked_malloc((vars->MR_slvs_tvar_count + 1)
		* sizeof(Word));
	/* type_params should look like a typeinfo; type_params[0] is empty */
	for (i = 0; i < vars->MR_slvs_tvar_count; i++) {
		type_params[i+1] = MR_trace_lookup_live_lval(
			vars->MR_slvs_tvars[i], &succeeded);
		if (!succeeded) {
			fatal_error("missing type param in MR_trace_browse");
		}
	}

	for (i = 0; i < var_count; i++) {
		MR_trace_browse_var(MR_name_if_present(vars, i),
			&vars->MR_slvs_pairs[i], type_params);
	}

	free(type_params);
}

static void
MR_trace_browse_var(const char *name, const MR_Stack_Layout_Var *var,
	Word *type_params)
{
	Word	value, type_info;
	bool	print_value;
	int	i;

	/*
	** XXX The printing of type_infos is buggy at the moment
	** due to the fake arity of the type mercury_builtin:typeinfo/1.
	**
	** XXX The printing of large data structures is painful
	** at the moment due to the lack of a true browser.
	*/

	if ((strncmp(name, "TypeInfo", 8) == 0)
	|| (strncmp(name, "ModuleInfo", 10) == 0)
	|| (strncmp(name, "HLDS", 4) == 0))
		return;

	/* 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");
	}

	fflush(stdout);

	/*
	** "variables" representing the saved values of succip, hp etc,
	** which are the "variables" for which get_type_and_value fails,
	** are not of interest to the user.
	*/

	if (MR_trace_get_type_and_value(var, type_params, &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 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...
		**
		** call_engine expects the transient registers to be
		** in fake_reg, others in their normal homes.
		** The code below works by placing r1, r2 and all other
		** transient registers both in their normal homes and
		** and in fake_reg as well.
		*/

		MR_trace_enabled = FALSE;
		for (i = 0; i < MR_MAX_SPECIAL_REG_MR; i++) {
			fake_reg[i] = MR_saved_regs[i];
		}
		restore_registers();
		r1 = type_info;
		r2 = value;
		save_transient_registers(); /* r1 or r2 may be transient */
		call_engine(MR_library_trace_browser);
		MR_trace_enabled = TRUE;
	}

	printf("\n");
}

static void
MR_add_spy_point(void)
{
	int	c;

	c = getchar();

	if (MR_next_spy_point >= MR_MAX_SPY_POINTS) {
		MR_trace_discard_to_eol(c);
		printf("mtrace: no room for more spy points\n");
		return;
	}

	if (MR_trace_get_word(&c, MR_spy_points[MR_next_spy_point].module_name,
			MR_NAME_LEN)
	&& MR_trace_get_word(&c, MR_spy_points[MR_next_spy_point].pred_name,
			MR_NAME_LEN)) {
		MR_trace_discard_to_eol(c);
		MR_spy_points[MR_next_spy_point].enabled = TRUE;
		MR_next_spy_point++;
	}
	else {
		printf("usage: \"b module_name pred_name\"\n");
	}
}

static void
MR_list_spy_points(void)
{
	int	i;

	for (i = 0; i < MR_next_spy_point; i++) {
		printf("%2d: %s %s:%s\n", i,
			MR_spy_points[i].enabled? "+": "-",
			MR_spy_points[i].module_name,
			MR_spy_points[i].pred_name);
	}

	MR_trace_discard_to_eol(getchar());
}

static void
MR_change_spy_point_status(bool status)
{
	char	buf[MR_LOG10_MAX_SPY_POINTS];
	int	c;
	int	i;

	c = getchar();

	if (MR_trace_get_word(&c, buf, MR_LOG10_MAX_SPY_POINTS)) {
		if (sscanf(buf, "%d", &i) == 1) {
			if (0 <= i && i < MR_next_spy_point) {
				MR_spy_points[i].enabled = status;
			} else {
				printf("spy point #%d does not exist\n", i);
			}
		} else if (strcmp(buf, "*") == 0) {
			for (i = 0; i < MR_next_spy_point; i++) {
				MR_spy_points[i].enabled = status;
			}
		} else {
			printf("garbled spy point number\n");
		}
	} else {
		printf("missing spy point number\n");
	}

	MR_trace_discard_to_eol(c);
}

bool
MR_event_matches_spy_point(const MR_Stack_Layout_Label *layout)
{
	const	MR_Stack_Layout_Entry	*entry;
	int				i;

	entry = layout->MR_sll_entry;

	for (i = 0; i < MR_next_spy_point; i++) {
		if (MR_spy_points[i].enabled
		&& streq(MR_spy_points[i].pred_name,
				entry->MR_sle_name)
		&& streq(MR_spy_points[i].module_name,
				entry->MR_sle_def_module)) {
			return TRUE;
		}
	}

	return FALSE;
}

static int
MR_trace_skip_spaces(int c)
{
	while (c != EOF && c != '\n' && isspace(c))
		c = getchar();

	return c;
}

static void
MR_trace_discard_to_eol(int c)
{
	while (c != EOF && c != '\n')
		c = getchar();
}

static int
MR_trace_get_word(int *cptr, char word[], int len)
{
	int	c;
	int	i;

	c = MR_trace_skip_spaces(*cptr);

	i = 0;
	while (c != EOF && (isalnum(c) || c == '_')) {
		if (i < len) {
			word[i++] = c;
		}

		c = getchar();
	}

	*cptr = c;

	if (i > 0) {
		word[i] = '\0';
		return TRUE;
	}

	return FALSE;
}

static void
MR_trace_help(void)
{
	printf("valid commands are:\n"
		"a, EOF:\t\t"
		"\tabort the current execution.\n"
		"b <module> <name>:"
		"\tset a breakpoint on the predicate\n"
		"\t\t\tor function <module>:<name>.\n"
		"c:\t\t"
		"\tcontinue to end of program, not printing the trace.\n"
		"C:\t\t"
		"\tcontinue to end of program, printing the trace.\n"
		"f:\t\t"
		"\tfinish this call, not printing the trace.\n"
		"F:\t\t"
		"\tfinish this call, printing the trace.\n"
		"<N> g:\t\t"
		"\tgo to event #N, not printing the trace.\n"
		"<N> G:\t\t"
		"\tgo to event #N, printing the trace.\n"
		"p:\t\t"
		"\tprint the variables live at this point.\n"
		"r:\t\t"
		"\tcontinue until forward execution is resumed.\n"
		"[<N>] [s]:\t"
		"\tskip N events, not printing the trace (default: N=1).\n"
		"[<N>] S:\t"
		"\tskip N events, printing the trace (default: N=1).\n"
		"?:\t\t"
		"\tlist all the breakpoints.\n"
		"+ <N>:\t\t"
		"\tenable breakpoint #N.\n"
		"- <N>:\t\t"
		"\tdisable breakpoint #N.\n"
	);
}
::::::::::::::
mercury_trace_internal.h
::::::::::::::
/*
** 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.
*/

#ifndef	MERCURY_TRACE_INTERNAL_H

extern	void	MR_trace_display_user(MR_trace_interact interact,
			const MR_Stack_Layout_Label *layout,
			MR_trace_port port, int seqno, int depth,
			const char *path);

extern	bool	MR_event_matches_spy_point(const MR_Stack_Layout_Label *layout);

#endif	/* MERCURY_TRACE_INTERNAL_H */
::::::::::::::
mercury_trace_util.c
::::::::::::::
/*
** 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.
*/

/*
** This file implements utilities that can be useful
** for both the internal and external debuggers.
**
** Main authors: Zoltan Somogyi and Fergus Henderson.
*/

#include "mercury_imp.h"
#include "mercury_stack_layout.h"
#include "mercury_trace_util.h"

/* XXX fix this ref to the library */
extern	Word	*ML_create_type_info(Word *term_type_info,
			Word *arg_pseudo_type_info);

Word	MR_saved_regs[MAX_FAKE_REG];

void
MR_copy_regs_to_saved_regs(int max_mr_num)
{
	/*
	** In the process of browsing, we call Mercury code,
	** which may clobber the contents of the virtual machine registers,
	** both control and general purpose, and both real and virtual
	** registers. We must therefore save and restore these.
	** We store them in the MR_saved_regs array.
	**
	** The call to MR_trace will clobber the transient registers
	** on architectures that have them. The compiler generated code
	** will therefore call save_transient_registers to save the transient
	** registers in the fake_reg array. We here restore them to the
	** real registers, save them with the other registers back in
	** fake_reg, and then copy all fake_reg entries to MR_saved_regs.
	**
	** If any code invoked by MR_trace is itself traced,
	** MR_saved_regs will be overwritten, leading to a crash later on.
	** This is one reason (but not the only one) why we turn off
	** tracing when we call back Mercury code from this file.
	*/

	int i;

	restore_transient_registers();
	save_registers();

	for (i = 0; i <= max_mr_num; i++) {
		MR_saved_regs[i] = fake_reg[i];
	}
}

void
MR_copy_saved_regs_to_regs(int max_mr_num)
{
	/*
	** We execute the converse procedure to MR_copy_regs_to_saved_regs.
	** The save_transient_registers is there so that a call to the
	** restore_transient_registers macro after MR_trace will do the
	** right thing.
	*/

	int i;

	for (i = 0; i <= max_mr_num; i++) {
		fake_reg[i] = MR_saved_regs[i];
	}

	restore_registers();
	save_transient_registers();
}

Word
MR_trace_make_var_list(const MR_Stack_Layout_Label *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();

	var_count = layout->MR_sll_var_count;
	vars = &layout->MR_sll_var_info;

	/* 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)
		*/

		name = MR_name_if_present(vars, i);
		var = &vars->MR_slvs_pairs[i];

		/*
		** XXX The printing of type_infos is buggy at the moment
		** due to the fake arity of mercury_builtin:typeinfo/1.
		**
		** XXX The printing of large data structures is painful
		** at the moment due to the lack of a true browser.
		**
		** "variables" representing the saved values of succip, hp etc,
		** which are the "variables" for which get_type_and_value
		** fails, are not of interest to the trace analyzer.
		*/

		if ((strncmp(name, "TypeInfo", 8) == 0)
		|| (strncmp(name, "ModuleInfo", 10) == 0)
		|| (strncmp(name, "HLDS", 4) == 0)
		|| !MR_trace_get_type_and_value(var, NULL, &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;
}

/* if you want to debug this code, you may want to set this var to TRUE */
static	bool	MR_trace_print_locn = FALSE;

Word
MR_trace_lookup_live_lval(MR_Live_Lval locn, bool *succeeded)
{
	int	locn_num;
	Word	value;

	*succeeded = FALSE;
	value = 0;

	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);
			*succeeded = TRUE;
			break;

		case MR_LVAL_TYPE_F:
			if (MR_trace_print_locn)
				printf("f%d", locn_num);
			break;

		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);
			*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);
			*succeeded = TRUE;
			break;

		case MR_LVAL_TYPE_SUCCIP:
			if (MR_trace_print_locn)
				printf("succip");
			break;

		case MR_LVAL_TYPE_MAXFR:
			if (MR_trace_print_locn)
				printf("maxfr");
			break;

		case MR_LVAL_TYPE_CURFR:
			if (MR_trace_print_locn)
				printf("curfr");
			break;

		case MR_LVAL_TYPE_HP:
			if (MR_trace_print_locn)
				printf("hp");
			break;

		case MR_LVAL_TYPE_SP:
			if (MR_trace_print_locn)
				printf("sp");
			break;

		case MR_LVAL_TYPE_UNKNOWN:
			if (MR_trace_print_locn)
				printf("unknown");
			break;

		default:
			if (MR_trace_print_locn)
				printf("DEFAULT");
			break;
	}

	return value;
}

bool
MR_trace_get_type_and_value(const MR_Stack_Layout_Var *var,
	Word *type_params, Word *type_info, Word *value)
{
	bool	succeeded;
	Word	*pseudo_type_info;
	int	i;

	if (!MR_LIVE_TYPE_IS_VAR(var->MR_slv_live_type)) {
		return FALSE;
	}

	pseudo_type_info = MR_LIVE_TYPE_GET_VAR_TYPE(var->MR_slv_live_type);
	*type_info = (Word) ML_create_type_info(type_params, pseudo_type_info);
	*value = MR_trace_lookup_live_lval(var->MR_slv_locn, &succeeded);
	return succeeded;
}
::::::::::::::
mercury_trace_util.h
::::::::::::::
/*
** 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.
*/

#ifndef	MERCURY_TRACE_UTIL_H

extern	Word    MR_saved_regs[MAX_FAKE_REG];
extern	void	MR_copy_regs_to_saved_regs(int max_mr_num);
extern	void	MR_copy_saved_regs_to_regs(int max_mr_num);

extern	Word	MR_trace_make_var_list(const MR_Stack_Layout_Label *layout);
extern	Word	MR_trace_lookup_live_lval(MR_Live_Lval locn, bool *succeeded);
extern	bool	MR_trace_get_type_and_value(const MR_Stack_Layout_Var *var,
			Word *type_params, Word *type_info, Word *value);

#endif	/* MERCURY_TRACE_UTIL_H */

Index: Mmakefile
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/Mmakefile,v
retrieving revision 1.25
diff -u -u -r1.25 Mmakefile
--- Mmakefile	1998/03/30 13:03:13	1.25
+++ Mmakefile	1998/04/09 08:34:38
@@ -59,6 +59,10 @@
 			mercury_tags.h		\
 			mercury_timing.h	\
 			mercury_trace.h		\
+			mercury_trace_cmd.h	\
+			mercury_trace_external.h \
+			mercury_trace_internal.h \
+			mercury_trace_util.h	\
 			mercury_trail.h		\
 			mercury_types.h		\
 			mercury_type_info.h	\
@@ -100,6 +104,9 @@
 			mercury_table.c		\
 			mercury_timing.c	\
 			mercury_trace.c		\
+			mercury_trace_external.c \
+			mercury_trace_internal.c \
+			mercury_trace_util.c	\
 			mercury_trail.c 	\
 			mercury_type_info.c	\
 			mercury_wrapper.c



More information about the developers mailing list