mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-11 03:45:33 +00:00
Estimated hours taken: 30
Branches: main
Give the Mercury debugger the ability to detect cliques of mutually recursive
predicates on the stack. Exploit this ability to enhance the debugger's
level, retry, finish and stack commands.
runtime/mercury_stack_trace.[ch]:
Add a function, MR_find_clique_entry, that detects the clique
that contains the top stack frame. This is used to implement the new
arguments "clentry" and "clparent" (short for clique entry and parent)
options of the level, retry and finish commands. "clique" is a synonym
for "clentry" in these commands.
Add a function, MR_dump_stack_layout_clique, that implements the
new capabilities of the stack command. It can detect more than one
clique, anywhere on the stack.
To make this possible, modify the existing functions for printing
the lines of stack traces. These used to keep some information around
between calls in global variables. Now that information is stored in
two structures that the caller passes them. One contains the parameters
that govern what is to be printed, the other contains information about
what has been buffered up to be printed, but has not been flushed yet.
(The old code was confused in its handling of parameters. Some parts
of it looked up the global variables storing them, while other parts
were given the parameter values by their callers, values that could
have been -but weren't- inconsistent.)
Change the buffer flushing code to be idempotent, since in the new
code, sometimes it is hard to avoid flushing the buffer more than once,
and we want only the first to print its contents.
Make some type names conform to our standard style.
runtime/mercury_stack_layout.h:
Add a new flag in MR_ProcLayouts: a flag that indicates that the
procedure has one or more higher order arguments. The new code in
mercury_stack_trace.c handles procedures with this flag specially:
it does not consider two non-consecutive occurrences of such procedures
on the stack to be necessarily part of the same clique. This is to
avoid having two calls to e.g. list.map in different part of the
program pulling all the procedures between those parts on the stack
into a single clique. (The deep profiler has a very similar tweak.)
Add a pointer to the corresponding part of the compiler.
compiler/hlds_pred.m:
Add a predicate to test whether a predicate has any higher order args.
compiler/stack_layout.m:
When computing the flag in proc layouts, call the new procedure in
hlds_pred.m to help figure it out.
trace/mercury_trace_cmd_backward.c:
Implement the new options of the "retry" command.
trace/mercury_trace_cmd_forward.c:
Implement the new options of the "finish" command.
trace/mercury_trace_cmd_browsing.c:
Implement the "new options of the "level" command.
Implement the new functionality of the "stack" command.
trace/mercury_trace_util.[ch]:
Add some code common to the implementations of the level, retry and
finish commands.
trace/mercury_trace_external.c:
Conform to the changes to the runtime.
doc/user_guide.texi:
Document the debugger's new capabilities.
NEWS:
Announce the debugger's new capabilities.
tests/debugger/mutrec.{m,inp,exp}:
A new test case to test the handling of the stack command
in the presence of cliques.
tests/debugger/mutrec_higher_order.{m,inp,exp}:
A new test case to test the handling of the stack command
in the presence of cliques and higher order predicates.
tests/debugger/Mmakefile:
Enable both new test cases.
1548 lines
51 KiB
C
1548 lines
51 KiB
C
/*
|
|
** vim: ts=4 sw=4 expandtab
|
|
*/
|
|
/*
|
|
** Copyright (C) 1998-2008,2011-2012 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 is enabled for systems that support
|
|
** sockets.
|
|
**
|
|
** 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 "mercury_trace_browse.h"
|
|
#include "mercury_trace_vars.h"
|
|
|
|
#include "mdb.debugger_interface.mh"
|
|
#include "mdb.collect_lib.mh"
|
|
|
|
#include "type_desc.mh"
|
|
|
|
#include "mercury_deep_copy.h"
|
|
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <stdarg.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>
|
|
#include <stdlib.h>
|
|
#ifdef MR_HAVE_DLFCN_H
|
|
#include <dlfcn.h>
|
|
#endif
|
|
|
|
/*
|
|
** This type must match the definition of classify_request in
|
|
** browser/debugger_interface.m.
|
|
*/
|
|
|
|
typedef enum {
|
|
/* initiate debugging session */
|
|
MR_REQUEST_HELLO_REPLY = 0,
|
|
|
|
/* go to the next matching trace event */
|
|
MR_REQUEST_FORWARD_MOVE = 1,
|
|
|
|
/* report data for current_vars query */
|
|
MR_REQUEST_CURRENT_VARS = 2,
|
|
|
|
/* report data for current_slots query */
|
|
MR_REQUEST_CURRENT_SLOTS = 3,
|
|
|
|
/* continue to end, not tracing */
|
|
MR_REQUEST_NO_TRACE = 4,
|
|
|
|
/* abort the current execution */
|
|
MR_REQUEST_ABORT_PROG = 5,
|
|
|
|
/* something went wrong */
|
|
MR_REQUEST_ERROR = 6,
|
|
|
|
/* report data for current_live_var_names query */
|
|
MR_REQUEST_CURRENT_LIVE_VAR_NAMES = 7,
|
|
|
|
/* report data for current_nth_var query */
|
|
MR_REQUEST_CURRENT_NTH_VAR = 8,
|
|
|
|
/* restart the execution to the call port of the current event */
|
|
MR_REQUEST_RETRY = 9,
|
|
|
|
/* print the ancestors list */
|
|
MR_REQUEST_STACK = 10,
|
|
|
|
/* print the nondet stack */
|
|
MR_REQUEST_NONDET_STACK = 11,
|
|
|
|
/* prints the contents of the virtual machine registers. */
|
|
MR_REQUEST_STACK_REGS = 12,
|
|
|
|
/* wait for a normal interactive query */
|
|
MR_REQUEST_INTERACTIVE_QUERY_NORMAL = 13,
|
|
|
|
/* wait for a cc interactive query */
|
|
MR_REQUEST_INTERACTIVE_QUERY_CC = 14,
|
|
|
|
/* wait for a io interactive query */
|
|
MR_REQUEST_INTERACTIVE_QUERY_IO = 15,
|
|
/* pass down new options to compile queries with */
|
|
MR_REQUEST_MMC_OPTIONS = 16,
|
|
|
|
/* call the term browser */
|
|
MR_REQUEST_BROWSE = 17,
|
|
/* dynamically link the collect module */
|
|
MR_REQUEST_LINK_COLLECT = 18,
|
|
|
|
/* collecting monitoring informations */
|
|
MR_REQUEST_COLLECT = 19,
|
|
|
|
/* retrieving the grade of the current program has been compiled with */
|
|
MR_REQUEST_CURRENT_GRADE = 20,
|
|
|
|
/* switch the arguments collecting on */
|
|
MR_REQUEST_COLLECT_ARG_ON = 21,
|
|
|
|
/* switch the arguments collecting off */
|
|
MR_REQUEST_COLLECT_ARG_OFF = 22
|
|
|
|
} MR_debugger_request_type;
|
|
|
|
MercuryFile MR_debugger_socket_in;
|
|
MercuryFile MR_debugger_socket_out;
|
|
|
|
static MR_String MR_external_mmc_options;
|
|
|
|
/*
|
|
** Type of a static variable that indicates in which mode the external
|
|
** debugger is. When the external debugger is in mode:
|
|
** (1) `MR_searching', it tries to find an event that matches a forward
|
|
** move request,
|
|
** (2) `MR_reading_request', it reads a new request on the socket,
|
|
** (3) `MR_collecting', it is collecting information (after a `collect'
|
|
** request).
|
|
*/
|
|
|
|
typedef enum {
|
|
MR_searching, MR_reading_request, MR_collecting
|
|
} MR_external_debugger_mode_type;
|
|
|
|
static MR_external_debugger_mode_type
|
|
external_debugger_mode = MR_reading_request;
|
|
|
|
/*
|
|
** Global variable that is used to store the information collected during
|
|
** a collect request.
|
|
*/
|
|
|
|
static MR_Word MR_accumulator_variable;
|
|
|
|
/*
|
|
** Global variable that is sent to collect caller.
|
|
*/
|
|
|
|
static MR_Word MR_collected_variable;
|
|
|
|
/*
|
|
** Function pointer used to post-process the result of the collect activity
|
|
*/
|
|
|
|
static void (*post_process_ptr)(MR_Word, MR_Word *);
|
|
|
|
/*
|
|
** Function pointer used to sent the collecting variable to the external
|
|
** debugger.
|
|
*/
|
|
|
|
static void (*send_collect_result_ptr)(MR_Word, MR_Word);
|
|
|
|
/*
|
|
** Variable generated during the dynamic linking that is needed to close
|
|
** this linking properly.
|
|
*/
|
|
|
|
static MR_Word collect_lib_maybe_handle;
|
|
|
|
/*
|
|
** Static variable that tells whether the list of arguments is available
|
|
** within a collect module.
|
|
*/
|
|
|
|
static MR_bool MR_collect_arguments = MR_FALSE;
|
|
|
|
/*
|
|
** Use a GNU C extension to enforce static type checking
|
|
** for printf-style functions.
|
|
** (See the "Function attributes" section of "C extensions"
|
|
** chapter of the GNU C manual for detailed documentation.)
|
|
*/
|
|
|
|
#ifdef __GNUC__
|
|
#define MR_LIKE_PRINTF(format_argnum, vars_argnum) \
|
|
__attribute__ ((format (printf, (format_argnum), (vars_argnum))))
|
|
#else
|
|
#define MR_LIKE_PRINTF(n, m) /* nothing */
|
|
#endif
|
|
|
|
static void MR_send_message_to_socket_format(const char *format, ...)
|
|
MR_LIKE_PRINTF(1, 2);
|
|
|
|
static void MR_send_message_to_socket(const char *message);
|
|
static void MR_read_request_from_socket(MR_Word *debugger_request_ptr,
|
|
MR_Integer *debugger_request_type_ptr);
|
|
|
|
static MR_bool MR_found_match(const MR_LabelLayout *layout,
|
|
MR_TracePort port, MR_Unsigned seqno, MR_Unsigned depth,
|
|
/* XXX registers */ const char *path, MR_Word search_data);
|
|
static void MR_output_current_slots(const MR_LabelLayout *layout,
|
|
MR_TracePort port, MR_Unsigned seqno,
|
|
MR_Unsigned depth, const char *path, int lineno);
|
|
static void MR_output_current_vars(MR_Word var_list, MR_Word string_list);
|
|
static void MR_output_current_nth_var(MR_Word var);
|
|
static void MR_output_current_live_var_names(MR_Word var_names_list,
|
|
MR_Word type_list);
|
|
static MR_Word MR_trace_make_var_list(void);
|
|
static MR_Word MR_trace_make_var_names_list(void);
|
|
static MR_Word MR_trace_make_type_list(void);
|
|
static MR_Word MR_trace_make_nth_var(MR_Word debugger_request);
|
|
static int MR_get_var_number(MR_Word debugger_request);
|
|
static void MR_print_proc_id_to_socket(const MR_ProcLayout *entry,
|
|
const char *extra, MR_Word *base_sp, MR_Word *base_curfr);
|
|
static void MR_dump_stack_record_print_to_socket(FILE *fp,
|
|
MR_bool include_trace_data,
|
|
const MR_StackFrameDumpInfo *frame_dump_info);
|
|
static void MR_get_list_modules_to_import(MR_Word debugger_request,
|
|
MR_Integer *modules_list_length_ptr,
|
|
MR_Word *modules_list_ptr);
|
|
static void MR_get_mmc_options(MR_Word debugger_request,
|
|
MR_String *mmc_options_ptr);
|
|
static void MR_get_object_file_name(MR_Word debugger_request,
|
|
MR_String *object_file_name_ptr);
|
|
static void MR_get_variable_name(MR_Word debugger_request,
|
|
MR_String *var_name_ptr);
|
|
static void MR_trace_browse_one_external(MR_VarSpec which_var);
|
|
static void MR_send_collect_result(void);
|
|
|
|
#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\n"
|
|
" setenv MERCURY_INET_DEBUGGER_SOCKET Name\n"
|
|
"and then run the program\n");
|
|
... 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) {
|
|
MR_fatal_error("unix socket: length != 16");
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
static MR_bool MR_debug_socket = MR_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;
|
|
MR_Word debugger_request;
|
|
MR_Integer debugger_request_type;
|
|
|
|
/*
|
|
** MR_external_mmc_options contains the options to pass to mmc
|
|
** when compiling queries. We initialize it to the MR_String "".
|
|
*/
|
|
|
|
MR_TRACE_CALL_MERCURY(
|
|
ML_DI_init_mercury_string(&MR_external_mmc_options)
|
|
);
|
|
|
|
/*
|
|
** We presume that the user's program has been invoked from
|
|
** within the debugger (e.g. Morphine).
|
|
** 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) {
|
|
MR_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) {
|
|
MR_fatal_error("you must set only one of the "
|
|
"MERCURY_DEBUGGER_UNIX_SOCKET\n"
|
|
"and MERCURY_DEBUGGER_INET_SOCKET environment variables");
|
|
}
|
|
if (unix_socket) {
|
|
/*
|
|
** We use `(memset)(...)' rather than `memset'
|
|
** to prevent macro expansion; this is needed because
|
|
** with GNU libc 2.1.2 and gcc 2.95, gcc barfs on the latter,
|
|
** with an error message about impossible asm due to a conflict
|
|
** between our use of global register variables and the inline
|
|
** assembler macro definition of memset.
|
|
*/
|
|
|
|
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 = strlen(unix_address.sun_path) + sizeof(unix_address.sun_family);
|
|
} 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) {
|
|
MR_fatal_error("MERCURY_DEBUGGER_INET_SOCKET invalid");
|
|
}
|
|
host_addr = inet_addr(hostname);
|
|
if (host_addr == -1) {
|
|
MR_fatal_error("MERCURY_DEBUGGER_INET_SOCKET: invalid address");
|
|
}
|
|
if (sscanf(port_string, "%hu", &port) != 1) {
|
|
MR_fatal_error("MERCURY_DEBUGGER_INET_SOCKET: invalid port");
|
|
}
|
|
|
|
if (MR_debug_socket) {
|
|
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));
|
|
MR_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));
|
|
MR_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));
|
|
MR_fatal_error("cannot open debugger socket");
|
|
} else if (MR_debug_socket) {
|
|
fprintf(stderr, "Mercury runtime: fdopen(): ok\n");
|
|
}
|
|
|
|
MR_mercuryfile_init(file_in, 1, &MR_debugger_socket_in);
|
|
MR_mercuryfile_init(file_out, 1, &MR_debugger_socket_out);
|
|
|
|
/*
|
|
** 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) {
|
|
MR_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 or a collect request.
|
|
** In the first case, we want to tell the debugger that no match was found;
|
|
** in the second one we send the result of the collect activity.
|
|
*/
|
|
|
|
switch(external_debugger_mode) {
|
|
case MR_searching:
|
|
MR_send_message_to_socket("forward_move_match_not_found");
|
|
break;
|
|
|
|
case MR_collecting:
|
|
MR_send_collect_result();
|
|
MR_send_message_to_socket("execution_terminated");
|
|
break;
|
|
|
|
default:
|
|
MR_fatal_error("Error in the external debugger");
|
|
}
|
|
|
|
/*
|
|
** 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.)
|
|
*/
|
|
}
|
|
|
|
MR_Code *
|
|
MR_trace_event_external(MR_TraceCmdInfo *cmd, MR_EventInfo *event_info)
|
|
{
|
|
static MR_Word search_data;
|
|
static void (*initialize_ptr)(MR_Word *);
|
|
static void (*get_collect_var_type_ptr)(MR_Word *);
|
|
static MR_bool collect_linked = MR_FALSE;
|
|
MR_bool stop_collecting = MR_FALSE;
|
|
MR_Integer debugger_request_type;
|
|
MR_Word debugger_request;
|
|
MR_Word var_list;
|
|
MR_Word var_names_list;
|
|
MR_Word type_list;
|
|
MR_Word var;
|
|
MR_Code *jumpaddr = NULL;
|
|
const char *message;
|
|
MR_bool include_trace_data = MR_TRUE;
|
|
const MR_LabelLayout *layout = event_info->MR_event_sll;
|
|
MR_Unsigned seqno = event_info->MR_call_seqno;
|
|
MR_Unsigned depth = event_info->MR_call_depth;
|
|
MR_TracePort port = event_info->MR_trace_port;
|
|
const char *path = event_info->MR_event_path;
|
|
MR_Word *saved_regs = event_info->MR_saved_regs;
|
|
MR_Integer modules_list_length;
|
|
MR_Word modules_list;
|
|
MR_RetryResult retry_result;
|
|
static MR_String MR_object_file_name;
|
|
int lineno = 0;
|
|
MR_bool unsafe_retry;
|
|
|
|
MR_debug_enabled = MR_FALSE;
|
|
MR_update_trace_func_enabled();
|
|
|
|
MR_trace_init_point_vars(event_info->MR_event_sll,
|
|
event_info->MR_saved_regs, event_info->MR_saved_f_regs,
|
|
event_info->MR_trace_port, MR_FALSE);
|
|
|
|
switch(external_debugger_mode) {
|
|
case MR_searching:
|
|
/*
|
|
** XXX Should also pass the registers here, since they are 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");
|
|
external_debugger_mode = MR_reading_request;
|
|
} else {
|
|
goto done;
|
|
}
|
|
break;
|
|
|
|
case MR_collecting:
|
|
MR_send_collect_result();
|
|
MR_send_message_to_socket("execution_continuing");
|
|
break;
|
|
|
|
case MR_reading_request:
|
|
break;
|
|
|
|
default:
|
|
MR_fatal_error("Software error in the debugger.\n");
|
|
}
|
|
|
|
lineno = MR_get_line_number(event_info->MR_saved_regs, layout, port);
|
|
|
|
/* 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:
|
|
exit(EXIT_SUCCESS);
|
|
|
|
case MR_REQUEST_FORWARD_MOVE:
|
|
if (MR_debug_socket) {
|
|
fprintf(stderr, "\nMercury runtime: FORWARD_MOVE\n");
|
|
}
|
|
search_data = debugger_request;
|
|
external_debugger_mode = MR_searching;
|
|
goto done;
|
|
|
|
case MR_REQUEST_CURRENT_LIVE_VAR_NAMES:
|
|
if (MR_debug_socket) {
|
|
fprintf(stderr, "\nMercury runtime: "
|
|
"MR_REQUEST_CURRENT_LIVE_VAR_NAMES\n");
|
|
}
|
|
var_names_list = MR_trace_make_var_names_list();
|
|
type_list = MR_trace_make_type_list();
|
|
MR_output_current_live_var_names(var_names_list, type_list);
|
|
break;
|
|
|
|
case MR_REQUEST_CURRENT_VARS:
|
|
if (MR_debug_socket) {
|
|
fprintf(stderr,
|
|
"\nMercury runtime: REQUEST_CURRENT_VARS\n");
|
|
}
|
|
var_list = MR_trace_make_var_list();
|
|
var_names_list = MR_trace_make_var_names_list();
|
|
MR_output_current_vars(var_list, var_names_list);
|
|
break;
|
|
|
|
case MR_REQUEST_CURRENT_NTH_VAR:
|
|
if (MR_debug_socket) {
|
|
fprintf(stderr,
|
|
"\nMercury runtime: REQUEST_NTH_CURRENT_VAR\n");
|
|
}
|
|
var = MR_trace_make_nth_var(debugger_request);
|
|
MR_output_current_nth_var(var);
|
|
break;
|
|
|
|
case MR_REQUEST_CURRENT_SLOTS:
|
|
if (MR_debug_socket) {
|
|
fprintf(stderr,
|
|
"\nMercury runtime: REQUEST_CURRENT_SLOTS\n");
|
|
}
|
|
MR_output_current_slots(layout, port, seqno, depth, path,
|
|
lineno);
|
|
break;
|
|
|
|
case MR_REQUEST_RETRY:
|
|
if (MR_debug_socket) {
|
|
fprintf(stderr, "\nMercury runtime: REQUEST_RETRY\n");
|
|
}
|
|
|
|
retry_result = MR_trace_retry(event_info, 0,
|
|
MR_RETRY_IO_ONLY_IF_SAFE, MR_FALSE, "", &unsafe_retry,
|
|
&message, NULL, NULL, &jumpaddr);
|
|
if (retry_result == MR_RETRY_OK_DIRECT) {
|
|
MR_send_message_to_socket("ok");
|
|
cmd->MR_trace_cmd = MR_CMD_STEP;
|
|
goto done;
|
|
} else {
|
|
MR_send_message_to_socket_format("error(\"%s\").\n",
|
|
message);
|
|
}
|
|
break;
|
|
|
|
case MR_REQUEST_STACK:
|
|
if (MR_debug_socket) {
|
|
fprintf(stderr, "\nMercury runtime: REQUEST_STACK\n");
|
|
}
|
|
|
|
MR_trace_init_modules();
|
|
message = MR_dump_stack_from_layout(stdout, layout,
|
|
MR_saved_sp(saved_regs), MR_saved_curfr(saved_regs),
|
|
include_trace_data, MR_FALSE, 0, 0,
|
|
&MR_dump_stack_record_print_to_socket);
|
|
|
|
MR_send_message_to_socket("end_stack");
|
|
if (message != NULL) {
|
|
MR_send_message_to_socket_format("error(\"%s\").\n",
|
|
message);
|
|
} else {
|
|
MR_send_message_to_socket("ok");
|
|
}
|
|
break;
|
|
|
|
case MR_REQUEST_NONDET_STACK:
|
|
if (MR_debug_socket) {
|
|
fprintf(stderr,
|
|
"\nMercury runtime: REQUEST_NONDET_STACK\n");
|
|
}
|
|
MR_trace_init_modules();
|
|
/*
|
|
** XXX As in stack dump, we could send the output of this
|
|
** function on the socket. But the outputs are done via
|
|
** fprintf() and printlabel(), so we would need to define new
|
|
** fprintf() and printlabel() and pass them down as parameters
|
|
** of MR_dump_nondet_stack() (as we do with
|
|
** MR_dump_stack_record_print()).
|
|
*/
|
|
MR_dump_nondet_stack(stdout, NULL, 0, 0,
|
|
MR_saved_maxfr(saved_regs));
|
|
MR_send_message_to_socket("ok");
|
|
break;
|
|
|
|
case MR_REQUEST_STACK_REGS:
|
|
if (MR_debug_socket) {
|
|
fprintf(stderr,
|
|
"\nMercury runtime: REQUEST_STACK_REGS\n");
|
|
}
|
|
MR_send_message_to_socket_format(
|
|
"stack_regs(%lu, %lu, %lu).\n",
|
|
(unsigned long) MR_saved_sp(saved_regs),
|
|
(unsigned long) MR_saved_curfr(saved_regs),
|
|
(unsigned long) MR_saved_maxfr(saved_regs));
|
|
break;
|
|
|
|
case MR_REQUEST_INTERACTIVE_QUERY_NORMAL:
|
|
if (MR_debug_socket) {
|
|
fprintf(stderr, "\nMercury runtime: "
|
|
"REQUEST_INTERACTIVE_QUERY_NORMAL\n");
|
|
}
|
|
MR_get_list_modules_to_import(debugger_request,
|
|
&modules_list_length, &modules_list);
|
|
MR_trace_query_external(MR_NORMAL_QUERY,
|
|
MR_external_mmc_options, modules_list_length,
|
|
modules_list);
|
|
break;
|
|
|
|
case MR_REQUEST_INTERACTIVE_QUERY_IO:
|
|
if (MR_debug_socket) {
|
|
fprintf(stderr, "\nMercury runtime: "
|
|
"REQUEST_INTERACTIVE_QUERY_IO\n");
|
|
}
|
|
MR_get_list_modules_to_import(debugger_request,
|
|
&modules_list_length, &modules_list);
|
|
MR_trace_query_external(MR_IO_QUERY, MR_external_mmc_options,
|
|
modules_list_length, modules_list);
|
|
break;
|
|
|
|
case MR_REQUEST_INTERACTIVE_QUERY_CC:
|
|
if (MR_debug_socket) {
|
|
fprintf(stderr, "\nMercury runtime: "
|
|
"REQUEST_INTERACTIVE_QUERY_CC\n");
|
|
}
|
|
MR_get_list_modules_to_import(debugger_request,
|
|
&modules_list_length, &modules_list);
|
|
MR_trace_query_external(MR_CC_QUERY, MR_external_mmc_options,
|
|
modules_list_length, modules_list);
|
|
break;
|
|
|
|
case MR_REQUEST_MMC_OPTIONS:
|
|
if (MR_debug_socket) {
|
|
fprintf(stderr,
|
|
"\nMercury runtime: REQUEST_MMC_OPTIONS\n");
|
|
}
|
|
MR_get_mmc_options(debugger_request, &MR_external_mmc_options);
|
|
MR_send_message_to_socket("mmc_options_ok");
|
|
break;
|
|
|
|
case MR_REQUEST_BROWSE:
|
|
{
|
|
char *var_name;
|
|
MR_VarSpec var_spec;
|
|
|
|
if (MR_debug_socket) {
|
|
fprintf(stderr,
|
|
"\nMercury runtime: REQUEST_BROWSE\n");
|
|
}
|
|
MR_get_variable_name(debugger_request, &var_name);
|
|
var_spec.MR_var_spec_kind = MR_VAR_SPEC_NAME;
|
|
var_spec.MR_var_spec_name = var_name;
|
|
MR_trace_browse_one_external(var_spec);
|
|
MR_send_message_to_socket("browser_end");
|
|
break;
|
|
}
|
|
|
|
case MR_REQUEST_NO_TRACE:
|
|
cmd->MR_trace_cmd = MR_CMD_TO_END;
|
|
external_debugger_mode = MR_searching;
|
|
goto done;
|
|
|
|
case MR_REQUEST_LINK_COLLECT:
|
|
{
|
|
MR_Char result;
|
|
MR_Word MR_accumulator_variable_type;
|
|
|
|
if (MR_debug_socket) {
|
|
fprintf(stderr,
|
|
"\nMercury runtime: REQUEST_LINK_COLLECT\n");
|
|
}
|
|
MR_get_object_file_name(debugger_request,
|
|
&MR_object_file_name);
|
|
MR_TRACE_CALL_MERCURY(
|
|
ML_CL_link_collect(MR_object_file_name,
|
|
(MR_Word *) &cmd->MR_filter_ptr,
|
|
(MR_Word *) &initialize_ptr,
|
|
(MR_Word *) &post_process_ptr,
|
|
(MR_Word *) &send_collect_result_ptr,
|
|
(MR_Word *) &get_collect_var_type_ptr,
|
|
&collect_lib_maybe_handle,
|
|
&result
|
|
)
|
|
);
|
|
collect_linked = (result == 'y');
|
|
if (collect_linked) {
|
|
MR_send_message_to_socket("link_collect_succeeded");
|
|
MR_TRACE_CALL_MERCURY(
|
|
(*get_collect_var_type_ptr)(
|
|
&MR_accumulator_variable_type);
|
|
);
|
|
MR_accumulator_variable = MR_make_permanent(
|
|
MR_accumulator_variable,
|
|
(MR_TypeInfo) MR_accumulator_variable_type);
|
|
} else {
|
|
MR_send_message_to_socket("link_collect_failed");
|
|
}
|
|
break;
|
|
}
|
|
case MR_REQUEST_COLLECT:
|
|
if (MR_debug_socket) {
|
|
fprintf(stderr, "\nMercury runtime: REQUEST_COLLECT\n");
|
|
}
|
|
if (collect_linked) {
|
|
MR_send_message_to_socket("collect_linked");
|
|
external_debugger_mode = MR_collecting;
|
|
MR_TRACE_CALL_MERCURY(
|
|
(*initialize_ptr)(&MR_accumulator_variable);
|
|
);
|
|
|
|
/*
|
|
** In order to perform the collect from the current
|
|
** event, we need to call filter once here.
|
|
*/
|
|
|
|
MR_COLLECT_filter(cmd->MR_filter_ptr, seqno, depth,
|
|
port, layout, path, lineno, &stop_collecting);
|
|
|
|
if (stop_collecting) {
|
|
MR_send_collect_result();
|
|
MR_send_message_to_socket("execution_continuing");
|
|
break;
|
|
} else {
|
|
/*
|
|
** For efficiency, the remaining calls
|
|
** to MR_COLLECT_filter() are done in MR_trace_real().
|
|
*/
|
|
|
|
cmd->MR_trace_cmd = MR_CMD_COLLECT;
|
|
cmd->MR_trace_must_check = MR_FALSE;
|
|
cmd->MR_trace_strict = MR_TRUE;
|
|
MR_init_trace_check_integrity(cmd);
|
|
cmd->MR_trace_print_level = MR_PRINT_LEVEL_NONE;
|
|
goto done;
|
|
}
|
|
} else {
|
|
MR_send_message_to_socket("collect_not_linked");
|
|
break;
|
|
}
|
|
|
|
case MR_REQUEST_CURRENT_GRADE:
|
|
if (MR_debug_socket) {
|
|
fprintf(stderr,
|
|
"\nMercury runtime: REQUEST_CURRENT_GRADE\n");
|
|
}
|
|
MR_send_message_to_socket_format("grade(\"%s\").\n",
|
|
MR_GRADE_OPT);
|
|
break;
|
|
|
|
case MR_REQUEST_COLLECT_ARG_ON:
|
|
if (MR_debug_socket) {
|
|
fprintf(stderr,
|
|
"\nMercury runtime: REQUEST_COLLECT_ARG_ON\n");
|
|
}
|
|
MR_collect_arguments = MR_TRUE;
|
|
MR_send_message_to_socket("collect_arg_on_ok");
|
|
break;
|
|
|
|
case MR_REQUEST_COLLECT_ARG_OFF:
|
|
if (MR_debug_socket) {
|
|
fprintf(stderr,
|
|
"\nMercury runtime: REQUEST_COLLECT_ARG_OFF\n");
|
|
}
|
|
MR_collect_arguments = MR_FALSE;
|
|
MR_send_message_to_socket("collect_arg_off_ok");
|
|
break;
|
|
|
|
default:
|
|
MR_fatal_error("unexpected request read from "
|
|
"debugger socket");
|
|
}
|
|
}
|
|
|
|
done:
|
|
/*
|
|
** Recalculate the `must_check' flag, in case we changed the command
|
|
** strictness or print-level.
|
|
*/
|
|
cmd->MR_trace_must_check = (! cmd->MR_trace_strict) ||
|
|
(cmd->MR_trace_print_level != MR_PRINT_LEVEL_NONE);
|
|
|
|
#ifdef MR_TRACE_CHECK_INTEGRITY
|
|
cmd->MR_trace_must_check = cmd->MR_trace_must_check
|
|
|| cmd->MR_trace_check_integrity;
|
|
#endif
|
|
|
|
MR_debug_enabled = MR_TRUE;
|
|
MR_update_trace_func_enabled();
|
|
|
|
return jumpaddr;
|
|
}
|
|
|
|
static void
|
|
MR_output_current_slots(const MR_LabelLayout *layout,
|
|
MR_TracePort port, MR_Unsigned seqno, MR_Unsigned depth,
|
|
const char *path, int lineno)
|
|
{
|
|
if (MR_PROC_LAYOUT_IS_UCI(layout->MR_sll_entry)) {
|
|
MR_TRACE_CALL_MERCURY(
|
|
ML_DI_output_current_slots_comp(
|
|
MR_trace_event_number,
|
|
seqno,
|
|
depth,
|
|
port,
|
|
(MR_String)
|
|
layout->MR_sll_entry->MR_sle_uci.MR_uci_type_name,
|
|
(MR_String)
|
|
layout->MR_sll_entry->MR_sle_uci.MR_uci_type_module,
|
|
(MR_String)
|
|
layout->MR_sll_entry->MR_sle_uci.MR_uci_def_module,
|
|
(MR_String)
|
|
layout->MR_sll_entry->MR_sle_uci.MR_uci_pred_name,
|
|
/* is the type_ctor's arity what is wanted? XXX */
|
|
layout->MR_sll_entry->MR_sle_uci.MR_uci_type_arity,
|
|
layout->MR_sll_entry->MR_sle_uci.MR_uci_mode,
|
|
layout->MR_sll_entry->MR_sle_detism,
|
|
(MR_String) (MR_Word) path,
|
|
lineno,
|
|
MR_wrap_output_stream(&MR_debugger_socket_out));
|
|
);
|
|
} else {
|
|
MR_TRACE_CALL_MERCURY(
|
|
ML_DI_output_current_slots_user(
|
|
MR_trace_event_number,
|
|
seqno,
|
|
depth,
|
|
port,
|
|
layout->MR_sll_entry->MR_sle_user.MR_user_pred_or_func,
|
|
(MR_String)
|
|
layout->MR_sll_entry->MR_sle_user.MR_user_decl_module,
|
|
(MR_String)
|
|
layout->MR_sll_entry->MR_sle_user.MR_user_def_module,
|
|
(MR_String)
|
|
layout->MR_sll_entry->MR_sle_user.MR_user_name,
|
|
layout->MR_sll_entry->MR_sle_user.MR_user_arity,
|
|
layout->MR_sll_entry->MR_sle_user.MR_user_mode,
|
|
layout->MR_sll_entry->MR_sle_detism,
|
|
(MR_String) (MR_Word) path,
|
|
lineno,
|
|
MR_wrap_output_stream(&MR_debugger_socket_out));
|
|
);
|
|
}
|
|
}
|
|
|
|
static void
|
|
MR_output_current_vars(MR_Word var_list, MR_Word string_list)
|
|
{
|
|
MR_TRACE_CALL_MERCURY(
|
|
ML_DI_output_current_vars(
|
|
var_list,
|
|
string_list,
|
|
MR_wrap_output_stream(&MR_debugger_socket_out));
|
|
);
|
|
}
|
|
|
|
static void
|
|
MR_output_current_nth_var(MR_Word var)
|
|
{
|
|
MR_TRACE_CALL_MERCURY(
|
|
ML_DI_output_current_nth_var(
|
|
var,
|
|
MR_wrap_output_stream(&MR_debugger_socket_out));
|
|
);
|
|
}
|
|
|
|
static void
|
|
MR_output_current_live_var_names(MR_Word var_names_list, MR_Word type_list)
|
|
{
|
|
MR_TRACE_CALL_MERCURY(
|
|
ML_DI_output_current_live_var_names(
|
|
var_names_list,
|
|
type_list,
|
|
MR_wrap_output_stream(&MR_debugger_socket_out));
|
|
);
|
|
}
|
|
|
|
static void
|
|
MR_read_request_from_socket(MR_Word *debugger_request_ptr,
|
|
MR_Integer *debugger_request_type_ptr)
|
|
{
|
|
fflush(MR_file(MR_debugger_socket_in));
|
|
|
|
MR_TRACE_CALL_MERCURY(
|
|
ML_DI_read_request_from_socket(
|
|
MR_wrap_input_stream(&MR_debugger_socket_in),
|
|
debugger_request_ptr,
|
|
debugger_request_type_ptr);
|
|
);
|
|
}
|
|
|
|
static MR_bool
|
|
MR_found_match(const MR_LabelLayout *layout,
|
|
MR_TracePort port, MR_Unsigned seqno, MR_Unsigned depth,
|
|
/* XXX live vars */
|
|
const char *path, MR_Word search_data)
|
|
{
|
|
MR_bool result;
|
|
|
|
/* XXX get live vars from registers */
|
|
MR_Word arguments = /* XXX FIXME!!! */ 0;
|
|
if (MR_PROC_LAYOUT_IS_UCI(layout->MR_sll_entry)) {
|
|
MR_TRACE_CALL_MERCURY(
|
|
result = ML_DI_found_match_comp(
|
|
MR_trace_event_number,
|
|
seqno,
|
|
depth,
|
|
port,
|
|
(MR_String)
|
|
layout->MR_sll_entry->MR_sle_uci.MR_uci_type_name,
|
|
(MR_String)
|
|
layout->MR_sll_entry->MR_sle_uci.MR_uci_type_module,
|
|
(MR_String)
|
|
layout->MR_sll_entry->MR_sle_uci.MR_uci_def_module,
|
|
(MR_String)
|
|
layout->MR_sll_entry->MR_sle_uci.MR_uci_pred_name,
|
|
/* is the type_ctor's arity what is wanted? XXX */
|
|
layout->MR_sll_entry->MR_sle_uci.MR_uci_type_arity,
|
|
layout->MR_sll_entry->MR_sle_uci.MR_uci_mode,
|
|
layout->MR_sll_entry->MR_sle_detism,
|
|
arguments,
|
|
(MR_String) (MR_Word) path,
|
|
search_data);
|
|
);
|
|
} else {
|
|
MR_TRACE_CALL_MERCURY(
|
|
result = ML_DI_found_match_user(
|
|
MR_trace_event_number,
|
|
seqno,
|
|
depth,
|
|
port,
|
|
layout->MR_sll_entry->MR_sle_user.MR_user_pred_or_func,
|
|
(MR_String)
|
|
layout->MR_sll_entry->MR_sle_user.MR_user_decl_module,
|
|
(MR_String)
|
|
layout->MR_sll_entry->MR_sle_user.MR_user_def_module,
|
|
(MR_String)
|
|
layout->MR_sll_entry->MR_sle_user.MR_user_name,
|
|
layout->MR_sll_entry->MR_sle_user.MR_user_arity,
|
|
layout->MR_sll_entry->MR_sle_user.MR_user_mode,
|
|
layout->MR_sll_entry->MR_sle_detism,
|
|
arguments,
|
|
(MR_String) path,
|
|
search_data);
|
|
);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
MR_send_message_to_socket_format(const char *format, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
vfprintf(MR_file(MR_debugger_socket_out), format, args);
|
|
va_end(args);
|
|
fflush(MR_file(MR_debugger_socket_out));
|
|
MR_line_number(MR_debugger_socket_out)++;
|
|
}
|
|
|
|
static void
|
|
MR_send_message_to_socket(const char *message)
|
|
{
|
|
fprintf(MR_file(MR_debugger_socket_out), "%s.\n", message);
|
|
fflush(MR_file(MR_debugger_socket_out));
|
|
MR_line_number(MR_debugger_socket_out)++;
|
|
}
|
|
|
|
/*
|
|
** This function returns the list of the currently live variables
|
|
** as a list of univs.
|
|
** The memory needed will be allocated on the Mercury heap.
|
|
**
|
|
** If no information on live variables is available, or if there
|
|
** are no live variables, return the empty list.
|
|
*/
|
|
|
|
static MR_Word
|
|
MR_trace_make_var_list(void)
|
|
{
|
|
const char *problem;
|
|
int var_count;
|
|
int i;
|
|
MR_TypeInfo type_info;
|
|
MR_Word value;
|
|
MR_Word univ;
|
|
MR_Word var_list;
|
|
|
|
var_count = MR_trace_var_count();
|
|
|
|
MR_TRACE_USE_HP(
|
|
var_list = MR_list_empty();
|
|
);
|
|
|
|
for (i = var_count; i > 0; i--) {
|
|
problem = MR_trace_return_var_info(i, NULL, &type_info, &value);
|
|
if (problem != NULL) {
|
|
MR_fatal_error(problem);
|
|
}
|
|
|
|
MR_TRACE_USE_HP(
|
|
MR_new_univ_on_hp(univ, type_info, value);
|
|
);
|
|
|
|
MR_TRACE_USE_HP(
|
|
var_list = MR_univ_list_cons(univ, var_list);
|
|
);
|
|
}
|
|
|
|
return var_list;
|
|
}
|
|
|
|
/*
|
|
** This function returns the list of the internal names of currently live
|
|
** variables.
|
|
** The memory needed will be allocated on the Mercury heap.
|
|
**
|
|
** If no information on live variables is available, or if there
|
|
** are no live variables, return the empty list.
|
|
*/
|
|
|
|
static MR_Word
|
|
MR_trace_make_var_names_list(void)
|
|
{
|
|
const char *problem;
|
|
int var_count;
|
|
int i;
|
|
const char *name;
|
|
MR_Word var_names_list;
|
|
|
|
var_count = MR_trace_var_count();
|
|
|
|
MR_TRACE_USE_HP(
|
|
var_names_list = MR_list_empty();
|
|
);
|
|
|
|
for (i = var_count; i > 0; i--) {
|
|
problem = MR_trace_return_var_info(i, &name, NULL, NULL);
|
|
if (problem != NULL) {
|
|
MR_fatal_error(problem);
|
|
}
|
|
|
|
MR_TRACE_USE_HP(
|
|
var_names_list = MR_string_list_cons((MR_Word) name,
|
|
var_names_list);
|
|
);
|
|
}
|
|
|
|
return var_names_list;
|
|
}
|
|
|
|
/*
|
|
** This function returns the list of types of currently live variables.
|
|
** The memory needed will be allocated on the Mercury heap.
|
|
**
|
|
** If no information on live variables is available, or if there
|
|
** are no live variables, return the empty list.
|
|
*/
|
|
|
|
static MR_Word
|
|
MR_trace_make_type_list(void)
|
|
{
|
|
const char *problem;
|
|
int var_count;
|
|
int i;
|
|
MR_TypeInfo type_info;
|
|
MR_String type_info_string;
|
|
MR_Word type_list;
|
|
|
|
var_count = MR_trace_var_count();
|
|
|
|
MR_TRACE_USE_HP(
|
|
type_list = MR_list_empty();
|
|
);
|
|
|
|
for (i = var_count; i > 0; i--) {
|
|
problem = MR_trace_return_var_info(i, NULL, &type_info, NULL);
|
|
if (problem != NULL) {
|
|
MR_fatal_error(problem);
|
|
}
|
|
|
|
MR_TRACE_CALL_MERCURY(
|
|
type_info_string = ML_type_name((MR_Word) type_info);
|
|
);
|
|
MR_TRACE_USE_HP(
|
|
type_list = MR_string_list_cons((MR_Word) type_info_string,
|
|
type_list);
|
|
);
|
|
}
|
|
|
|
return type_list;
|
|
}
|
|
|
|
/*
|
|
** This function returns the requested live variable, as a univ.
|
|
** Any memory needed will be allocated on the Mercury heap.
|
|
*/
|
|
|
|
static MR_Word
|
|
MR_trace_make_nth_var(MR_Word debugger_request)
|
|
{
|
|
const char *problem;
|
|
int var_number;
|
|
MR_TypeInfo type_info;
|
|
MR_Word value;
|
|
MR_Word univ;
|
|
|
|
var_number = MR_get_var_number(debugger_request);
|
|
/* debugger_request should be of the form: current_nth_var(var_number) */
|
|
|
|
problem = MR_trace_return_var_info(var_number, NULL, &type_info, &value);
|
|
if (problem == NULL) {
|
|
MR_TRACE_USE_HP(
|
|
MR_new_univ_on_hp(univ, type_info, value);
|
|
);
|
|
} else {
|
|
/*
|
|
** Should never occur since we check in the external debugger process
|
|
** if a variable is live before retrieving it.
|
|
*/
|
|
|
|
MR_fatal_error(problem);
|
|
}
|
|
|
|
return univ;
|
|
}
|
|
|
|
/*
|
|
** This function is called only when debugger_request = current_nth_var(n).
|
|
** It returns the integer 'n'.
|
|
*/
|
|
|
|
static int
|
|
MR_get_var_number(MR_Word debugger_request)
|
|
{
|
|
int num;
|
|
MR_TRACE_CALL_MERCURY(
|
|
num = ML_DI_get_var_number(debugger_request);
|
|
);
|
|
return num;
|
|
}
|
|
|
|
/*
|
|
** The protocol between the debugged Mercury program and the external debugger
|
|
** is the following:
|
|
** 1) The debugger sends "stack";
|
|
** 2) For each procedure in the stack that is not generated by the compiler, the
|
|
** debuggee sends:
|
|
** - level(int) (the level of the procedure in the stack)
|
|
** - detail(unsigned long, unsigned long, unsigned long) if available
|
|
** (the call event number, call sequence number and depth of the goal
|
|
** of the procedure)
|
|
** - the atom 'pred' or 'func' depending if the procedure is a function
|
|
** or not
|
|
** - proc(string, string, long, long) (the procedure)
|
|
** - det(string) (the determinism of the procedure)
|
|
** - def_module(string) (the name of the defining module) if different from
|
|
** the current one.
|
|
**
|
|
** For each compiler generated procedures, the debuggee sends:
|
|
** - level(int) (as above)
|
|
** - detail(unsigned long, unsigned long, unsigned long) (as above)
|
|
** - proc(string, string, string, long, long) (the name of the
|
|
** compiler-generated procedure)
|
|
** - det(string) (as above)
|
|
** - def_module(string) (as above)
|
|
** 3) The debuggee sends "end_stack"
|
|
*/
|
|
|
|
static void
|
|
MR_dump_stack_record_print_to_socket(FILE *fp, MR_bool include_trace_data,
|
|
const MR_StackFrameDumpInfo *frame_dump_info)
|
|
{
|
|
/*
|
|
** XXX If the external debugger is ever needed again, it should be updated
|
|
** to send information across the socket about about any reuse of a stack
|
|
** frame by tail recursion events.
|
|
*/
|
|
|
|
if (frame_dump_info->MR_sdi_min_level != frame_dump_info->MR_sdi_max_level)
|
|
{
|
|
MR_fatal_error(
|
|
"dumping stack frames of multiple calls to external debugger");
|
|
}
|
|
|
|
MR_send_message_to_socket_format(
|
|
"level(%" MR_INTEGER_LENGTH_MODIFIER "u).\n",
|
|
frame_dump_info->MR_sdi_min_level);
|
|
MR_print_proc_id_to_socket(frame_dump_info->MR_sdi_proc_layout, NULL,
|
|
frame_dump_info->MR_sdi_base_sp, frame_dump_info->MR_sdi_base_curfr);
|
|
}
|
|
|
|
static void
|
|
MR_print_proc_id_to_socket(const MR_ProcLayout *entry,
|
|
const char *extra, MR_Word *base_sp, MR_Word *base_curfr)
|
|
{
|
|
if (! MR_PROC_LAYOUT_HAS_PROC_ID(entry)) {
|
|
MR_fatal_error("cannot retrieve procedure id without layout");
|
|
}
|
|
|
|
if (base_sp != NULL && base_curfr != NULL) {
|
|
MR_bool print_details = MR_FALSE;
|
|
if (MR_PROC_LAYOUT_HAS_EXEC_TRACE(entry)) {
|
|
MR_Integer maybe_from_full = entry->MR_sle_maybe_from_full;
|
|
if (maybe_from_full > 0) {
|
|
/*
|
|
** For procedures compiled with shallow tracing, the details
|
|
** will be valid only if the value of MR_from_full saved in
|
|
** the appropriate stack slot was MR_TRUE.
|
|
*/
|
|
if (MR_DETISM_DET_STACK(entry->MR_sle_detism)) {
|
|
print_details = MR_based_stackvar(base_sp,
|
|
maybe_from_full);
|
|
} else {
|
|
print_details = MR_based_framevar(base_curfr,
|
|
maybe_from_full);
|
|
}
|
|
} else {
|
|
/*
|
|
** For procedures compiled with full tracing,
|
|
** always print out the details.
|
|
*/
|
|
print_details = MR_TRUE;
|
|
}
|
|
}
|
|
if (print_details) {
|
|
if (MR_DETISM_DET_STACK(entry->MR_sle_detism)) {
|
|
MR_send_message_to_socket_format(
|
|
"detail(%lu, %lu, %lu).\n",
|
|
(unsigned long) MR_event_num_stackvar(base_sp) + 1,
|
|
(unsigned long) MR_call_num_stackvar(base_sp),
|
|
(unsigned long) MR_call_depth_stackvar(base_sp));
|
|
} else {
|
|
MR_send_message_to_socket_format(
|
|
"detail(%lu, %lu, %lu).\n",
|
|
(unsigned long) MR_event_num_framevar(base_curfr) + 1,
|
|
(unsigned long) MR_call_num_framevar(base_curfr),
|
|
(unsigned long) MR_call_depth_framevar(base_curfr));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (MR_PROC_LAYOUT_IS_UCI(entry)) {
|
|
MR_send_message_to_socket_format(
|
|
/* XXX Names with " may cause some problems here */
|
|
"proc(\"%s\",\"%s\",\"%s\",%ld,%ld).\n",
|
|
entry->MR_sle_uci.MR_uci_pred_name,
|
|
entry->MR_sle_uci.MR_uci_type_module,
|
|
entry->MR_sle_uci.MR_uci_type_name,
|
|
(long) entry->MR_sle_uci.MR_uci_type_arity,
|
|
(long) entry->MR_sle_uci.MR_uci_mode);
|
|
|
|
if (strcmp(entry->MR_sle_uci.MR_uci_type_module,
|
|
entry->MR_sle_uci.MR_uci_def_module) != 0)
|
|
{
|
|
MR_send_message_to_socket_format("def_module(\"%s\").\n",
|
|
entry->MR_sle_uci.MR_uci_def_module);
|
|
}
|
|
} else {
|
|
if (entry->MR_sle_user.MR_user_pred_or_func == MR_PREDICATE) {
|
|
MR_send_message_to_socket("pred");
|
|
} else if (entry->MR_sle_user.MR_user_pred_or_func == MR_FUNCTION) {
|
|
MR_send_message_to_socket("func");
|
|
} else {
|
|
MR_fatal_error("procedure is not pred or func");
|
|
}
|
|
|
|
MR_send_message_to_socket_format(
|
|
/* XXX Names with " may cause some problems here */
|
|
"proc(\"%s\",\"%s\",%ld,%ld).\n",
|
|
entry->MR_sle_user.MR_user_decl_module,
|
|
entry->MR_sle_user.MR_user_name,
|
|
(long) entry->MR_sle_user.MR_user_arity,
|
|
(long) entry->MR_sle_user.MR_user_mode);
|
|
|
|
if (strcmp(entry->MR_sle_user.MR_user_decl_module,
|
|
entry->MR_sle_user.MR_user_def_module) != 0)
|
|
{
|
|
MR_send_message_to_socket_format("def_module(\"%s\").\n",
|
|
entry->MR_sle_user.MR_user_def_module);
|
|
}
|
|
}
|
|
|
|
MR_send_message_to_socket_format("det(\"%s\").\n",
|
|
MR_detism_names[entry->MR_sle_detism]);
|
|
}
|
|
|
|
static void
|
|
MR_get_list_modules_to_import(MR_Word debugger_request,
|
|
MR_Integer *modules_list_length_ptr, MR_Word *modules_list_ptr)
|
|
{
|
|
MR_TRACE_CALL_MERCURY(
|
|
ML_DI_get_list_modules_to_import(debugger_request,
|
|
modules_list_length_ptr, modules_list_ptr);
|
|
);
|
|
}
|
|
|
|
static void
|
|
MR_get_mmc_options(MR_Word debugger_request, MR_String *mmc_options_ptr)
|
|
{
|
|
MR_TRACE_CALL_MERCURY(
|
|
ML_DI_get_mmc_options(debugger_request, mmc_options_ptr);
|
|
);
|
|
}
|
|
|
|
static void
|
|
MR_get_object_file_name(MR_Word debugger_request,
|
|
MR_String *object_file_name_ptr)
|
|
{
|
|
MR_TRACE_CALL_MERCURY(
|
|
ML_DI_get_object_file_name(debugger_request, object_file_name_ptr);
|
|
);
|
|
}
|
|
|
|
static void
|
|
MR_get_variable_name(MR_Word debugger_request, MR_String *var_name_ptr)
|
|
{
|
|
MR_TRACE_CALL_MERCURY(
|
|
ML_DI_get_variable_name(debugger_request, var_name_ptr);
|
|
);
|
|
}
|
|
|
|
/*
|
|
** This function does the same thing as MR_trace_browse_one() defined in
|
|
** mercury_trace_internal.c except it sends/receives program-readable terms
|
|
** from/to the socket instead of sending human-readable strings from/to
|
|
** mdb_in/mdb_out.
|
|
*/
|
|
|
|
static void
|
|
MR_trace_browse_one_external(MR_VarSpec var_spec)
|
|
{
|
|
const char *problem;
|
|
|
|
problem = MR_trace_browse_one(NULL, MR_FALSE, var_spec,
|
|
MR_trace_browse_external, MR_BROWSE_CALLER_BROWSE,
|
|
MR_BROWSE_DEFAULT_FORMAT, MR_TRUE);
|
|
|
|
if (problem != NULL) {
|
|
MR_send_message_to_socket_format("error(\"%s\").\n", problem);
|
|
}
|
|
}
|
|
|
|
/*
|
|
** This function calls the collect filtering predicate defined by the user
|
|
** and dynamically link with the execution.
|
|
*/
|
|
void
|
|
MR_COLLECT_filter(MR_FilterFuncPtr filter_ptr, MR_Unsigned seqno,
|
|
MR_Unsigned depth, MR_TracePort port, const MR_LabelLayout *layout,
|
|
const char *path, int lineno, MR_bool *stop_collecting)
|
|
{
|
|
MR_Char result;
|
|
MR_Word arguments;
|
|
|
|
/*
|
|
** Only pass the arguments list down filter
|
|
** if required, i.e. if MR_collect_arguments
|
|
** is set to MR_TRUE. We need to do that in
|
|
** order to not penalize the performance
|
|
** of collect in the cases where the argument
|
|
** list (which might be very big) is not used.
|
|
**
|
|
*/
|
|
if (MR_collect_arguments) {
|
|
arguments = MR_trace_make_var_list();
|
|
} else {
|
|
MR_TRACE_USE_HP(
|
|
arguments = MR_list_empty()
|
|
);
|
|
}
|
|
MR_TRACE_CALL_MERCURY(
|
|
(*filter_ptr)(
|
|
MR_trace_event_number,
|
|
seqno,
|
|
depth,
|
|
port,
|
|
layout->MR_sll_entry->MR_sle_user.MR_user_pred_or_func,
|
|
(MR_String) layout->MR_sll_entry->MR_sle_user.MR_user_decl_module,
|
|
(MR_String) layout->MR_sll_entry->MR_sle_user.MR_user_def_module,
|
|
(MR_String) layout->MR_sll_entry->MR_sle_user.MR_user_name,
|
|
layout->MR_sll_entry->MR_sle_user.MR_user_arity,
|
|
layout->MR_sll_entry->MR_sle_user.MR_user_mode,
|
|
arguments,
|
|
layout->MR_sll_entry->MR_sle_detism,
|
|
(MR_String) path,
|
|
lineno,
|
|
MR_accumulator_variable,
|
|
&MR_accumulator_variable,
|
|
&result)
|
|
);
|
|
*stop_collecting = (result == 'y');
|
|
}
|
|
|
|
/*
|
|
** This function retrieves the line number of the current goal.
|
|
*/
|
|
|
|
int
|
|
MR_get_line_number(MR_Word *saved_regs, const MR_LabelLayout *layout,
|
|
MR_TracePort port)
|
|
{
|
|
const char *filename;
|
|
const MR_LabelLayout *parent_layout;
|
|
const char *problem;
|
|
int lineno = 0;
|
|
MR_Word *base_sp;
|
|
MR_Word *base_curfr;
|
|
MR_Unsigned reused_frames;
|
|
|
|
if MR_port_is_interface(port) {
|
|
/*
|
|
** At external events, we want the line number where the call is made,
|
|
** not the one where the procedure is defined.
|
|
*/
|
|
|
|
base_sp = MR_saved_sp(saved_regs);
|
|
base_curfr = MR_saved_curfr(saved_regs);
|
|
parent_layout = MR_find_nth_ancestor(layout, 1, &base_sp, &base_curfr,
|
|
&reused_frames, &problem);
|
|
/* The external debugger does not (yet) know about reused frames. */
|
|
assert(reused_frames == 0);
|
|
if (parent_layout != NULL) {
|
|
(void) MR_find_context(parent_layout, &filename, &lineno);
|
|
}
|
|
} else {
|
|
(void) MR_find_context(layout, &filename, &lineno);
|
|
}
|
|
|
|
return lineno;
|
|
}
|
|
|
|
static void
|
|
MR_send_collect_result(void)
|
|
{
|
|
MR_TRACE_CALL_MERCURY(
|
|
(*post_process_ptr)(MR_accumulator_variable, &MR_collected_variable);
|
|
|
|
(*send_collect_result_ptr)(MR_collected_variable,
|
|
(MR_Word) &MR_debugger_socket_out)
|
|
);
|
|
|
|
#if defined(MR_HAVE_DLFCN_H) && defined(MR_HAVE_DLCLOSE)
|
|
MR_TRACE_CALL_MERCURY(
|
|
ML_CL_unlink_collect(collect_lib_maybe_handle)
|
|
);
|
|
#endif
|
|
}
|
|
|
|
#endif /* MR_USE_EXTERNAL_DEBUGGER */
|