Files
mercury/runtime/mercury_trace_external.c
Fergus Henderson 28fdbb082e Move the #include of nonportable header files such as
Estimated hours taken: 0.25

runtime/mercury_trace_external.c:
	Move the #include of nonportable header files such as
	<arpa/inet.h>, etc. inside the `#ifdef MR_USE_EXTERNAL_DEBUGGER'.
1998-05-26 16:33:03 +00:00

439 lines
11 KiB
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.
**
** The code for using an external debugger is conditionalized
** on MR_USE_EXTERNAL_DEBUGGER (which by default is not enabled)
** because it uses sockets, which are not portable.
** Ideally we ought to use autoconf for that...
**
** Main authors: Erwan Jahier and Fergus Henderson.
*/
#include "mercury_imp.h"
#ifdef MR_USE_EXTERNAL_DEBUGGER
#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;
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, Unsigned seqno, Unsigned depth,
/* XXX registers */
const char *path, Word search_data);
static void MR_output_current(const MR_Stack_Layout_Label *layout,
MR_trace_port port, Unsigned seqno, Unsigned 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_final_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_trace_event_external(const MR_Stack_Layout_Label *layout,
MR_trace_port port, Unsigned seqno, Unsigned 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, Unsigned seqno, Unsigned 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, Unsigned seqno, Unsigned 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 */