Fergus's recent change to the handling of some builtins broke the tracing

Estimated hours taken: 20

Fergus's recent change to the handling of some builtins broke the tracing
of those builtins. The following changes are a fix for this.

compiler/polymorphism.m:
	Export the predicate that checks whether a predicate is a builtin
	that lacks the usually necessary typeinfos.

	Comment out a misleading and in any case not very useful progress
	message.

compiler/liveness.m:
	Turn off type_info liveness for builtins without typeinfos.
	Since these builtins establish no gc points and shouldn't be
	execution traced, this is OK.

	Make type_info liveness part of live_info, since it can now be
	incorrect to look up the value of the option. (This may yield
	a speedup.)

compiler/live_vars.m:
compiler/store_alloc.m:
	Pass the pred_id to initial_liveness to liveness.m can do the test.

compiler/passes_aux.m:
	Add a new traversal type that passes along the pred_id.

compiler/mercury_compile.m:
	Turn off execution tracing for the modules builtin.m and
	private_builtin.m. The latter contains the interface predicates
	for the builtins without typeinfos. Since the interface predicates
	also lack the typeinfos, the compiler would get an internal abort
	if we left execution tracing on.

	In any case, these two modules contain stuff that users should
	consider language builtins, which means they should not be execution
	traced (they can still be stack traced in the right grade).

	Use the new traversal type for the modules that now need the pred_id.

compiler/globals.m:
	Allow the trace level to be set from outside, in this case
	mercury_compile.m.

The next batch of changes have to do with adding a stack dump command
to the debugger. Since debugging is possible even in non-debug grades,
this in turn requires allowing stack tracing to work in non-debug grades,
on programs in which only some modules are compiled with execution
(and hence stack) tracing.

compiler/llds_out.m:
compiler/mercury_compile.m:
runtime/mercury_conf_param.h:
	Llds_out used to output "#include <mercury_imp.h>" as the first
	substantive thing in the generated C file. The set of #define
	parameters in effect when mercury_imp.h is processed determines
	whether the macros that optionally register stack layouts for label
	actually do so or not. The values of these parameters are derived
	from the grade, which means that with this setup it is not possible
	for a non-debug grade program to register its stack layouts in the
	label table.

	The new version of llds_out looks up the option that says whether
	this module is compiled with execution tracing or not, and if it is,
	it generates a #define MR_STACK_TRACE_THIS_MODULE *before* the #include
	of mercury_imp.h. This causes mercury_conf_param.h, included from
	mercury_imp.h, to define the macros MR_USE_STACK_LAYOUTS and
	and MR_INSERT_LABELS, which in turn cause stack layouts for labels
	in this module to be generated and to be inserted into the label
	table, *without* changing the grade string (this last part is why
	we do not simply define MR_STACK_TRACE).

	Use the same mechanism to #include mercury_trace.h when doing
	execution tracing, since it is simpler than the mechanism we
	used to use (mercury_compile.m including the #include in a list
	of C header file fragments).

compiler/mercury_compile.m:
runtime/mercury_conf_param.h:
	Split the MR_NEED_INITIALIZATION_CODE macro into two parts.
	The first, MR_MAY_NEED_INITIALIZATION, now controls whether
	initialization code makes it into the object file of a module.
	The second, MR_NEED_INITIALIZATION_AT_START, determines whether
	the initialization code is called before main/2.

	When a module is compiled with execution tracing, the macro
	MR_INSERT_LABELS turns on MR_MAY_NEED_INITIALIZATION but not
	MR_NEED_INITIALIZATION_AT_START. The debugger will make sure
	that the initialization code has been called before it tries
	to do a stack dump (which needs the initialization code to have
	been executed, because it needs labels to have been put into the label
	table so that from a return address it can find the layout of the
	proc to which it belongs).

	Define MR_NEED_INITIALIZATION_AT_START if PROFILE_TIME is defined,
	since if PROFILE_TIME is defined mercury_wrapper.c calls init_modules.
	The fact that MR_NEED_INITIALIZATION_CODE didn't used to be defined
	when PROFILE_TIME was defined was, I believe, a bug, which was
	not detected because we do not turn on PROFILE_TIME without also
	turning on PROFILE_CALLS.

runtime/mercury_stack_trace.[ch]:
	Change the way stack dumps are done, to make it possible to
	print stack dumps from the debugger and to use trivial run-length
	encoding on the output (so that 100 consecutive calls to p
	yield the line "p * 100", rather than 100 lines of "p").

	The stack routine now returns an indication of whether the stack dump
	was fully successful, and if not, a description of the reason why not.
	This requires knowing when we have found the end of the stack dump,
	so we provide a global variable, MR_stack_trace_bottom, which
	mercury_wrapper.c will set to global_success, the address main/2
	goes to on success.

	s/multidet/multi/

runtime/mercury_wrapper.c:
	Set MR_stack_trace_bottom to the address of globals_success.
	Use MR_NEED_INITIALIZATION_AT_START to decide whether to call
	do_init_modules.

runtime/mercury_stacks.h:
	Provide variants of detstackvar(n) and framevar(n) that look up sp and
	curfr in an array of saved regs, for use by the debugger.

runtime/mercury_trace_util.c:
	Use the new variants of detstackvar(n) and framevar(n). This fixes
	an old bug on SPARCs.

runtime/mercury_trace_internal.c:
	Completely reorganize the way debugger commands are handled.
	Centralize reading in command lines, and the breaking up of command
	lines into words. The command names are the same as they were,
	but command syntax is now much easier to change.

	Add a new command "d" to dump as much of the stack as the available
	information will allow.

runtime/mercury_goto.h:
	Cosmetic changes to avoid the use of two different conditional
	compilation layout styles.

util/mkinit.c:
	Since we cannot know when we generate the _init.c file whether any
	modules will be compiled with execution tracing and will thus need
	stack tracing, we must now include in the generated _init.c file the
	code to call the initialization functions in all the modules, even if
	MR_NEED_INITIALIZATION_AT_START is not set, since init_modules
	can be called later, from the debugger. (We should be able to
	use the same approach with the accurate collector.)
This commit is contained in:
Zoltan Somogyi
1998-06-08 08:29:17 +00:00
parent af6f18ab3f
commit 09141817ba
8 changed files with 621 additions and 343 deletions

View File

@@ -79,6 +79,9 @@
:- pred globals__set_options(globals::in, option_table::in, globals::out)
is det.
:- pred globals__set_trace_level(globals::in, trace_level::in, globals::out)
is det.
:- pred globals__lookup_option(globals::in, option::in, option_data::out)
is det.
@@ -136,6 +139,9 @@
:- pred globals__io_set_option(option::in, option_data::in,
io__state::di, io__state::uo) is det.
:- pred globals__io_set_trace_level(trace_level::in,
io__state::di, io__state::uo) is det.
:- pred globals__io_lookup_option(option::in, option_data::out,
io__state::di, io__state::uo) is det.
@@ -230,6 +236,9 @@ globals__get_trace_level(globals(_, _, _, _, _, _, TraceLevel), TraceLevel).
globals__set_options(globals(_, B, C, D, E, F, G), Options,
globals(Options, B, C, D, E, F, G)).
globals__set_trace_level(globals(A, B, C, D, E, F, _), TraceLevel,
globals(A, B, C, D, E, F, TraceLevel)).
globals__lookup_option(Globals, Option, OptionData) :-
globals__get_options(Globals, OptionTable),
map__lookup(OptionTable, Option, OptionData).
@@ -353,6 +362,14 @@ globals__io_set_option(Option, OptionData) -->
{ unsafe_promise_unique(Globals1, Globals) },
globals__io_set_globals(Globals).
globals__io_set_trace_level(TraceLevel) -->
globals__io_get_globals(Globals0),
{ globals__set_trace_level(Globals0, TraceLevel, Globals1) },
{ unsafe_promise_unique(Globals1, Globals) },
% XXX there is a bit of a design flaw with regard to
% uniqueness and io__set_globals
globals__io_set_globals(Globals).
%-----------------------------------------------------------------------------%
globals__io_lookup_bool_option(Option, Value) -->

View File

@@ -123,7 +123,7 @@
** MR_DEBUG_NONDET_STACK
** Include a "name" field in the nondet stack frames.
** (Since this affects binary compatibility,
** This is a "compilation model" option which affects the grade.)
** this is a "compilation model" option which affects the grade.)
*/
/*
@@ -181,19 +181,20 @@
#ifdef MR_USE_STACK_LAYOUTS
#error "MR_USE_STACK_LAYOUTS should not be defined on the command line"
#endif
#if defined(MR_STACK_TRACE) || defined(NATIVE_GC)
#if defined(MR_STACK_TRACE) || defined(NATIVE_GC) || defined(MR_STACK_TRACE_THIS_MODULE)
#define MR_USE_STACK_LAYOUTS
#endif
/*
** MR_INSERT_LABELS -- labels need to be inserted into the label table.
** (this also means the initialization code needs
** to be run).
** to be run some time before the first use of the
** label table).
*/
#ifdef MR_INSERT_LABELS
#error "MR_INSERT_LABELS should not be defined on the command line"
#endif
#if defined(MR_STACK_TRACE) || defined(NATIVE_GC) || defined(MR_DEBUG_GOTOS)
#if defined(MR_STACK_TRACE) || defined(NATIVE_GC) || defined(MR_DEBUG_GOTOS) || defined(MR_STACK_TRACE_THIS_MODULE)
#define MR_INSERT_LABELS
#endif
@@ -209,20 +210,35 @@
#endif
/*
** MR_NEED_INITIALIZATION_CODE -- the module specific initialization code
** is needed (doesn't actually run the code,
** however).
** MR_NEED_INITIALIZATION_AT_START -- the module specific initialization code
** must be run before any Mercury code
** is run.
**
** You need to run initialization code for grades without static
** code addresses, for profiling, and any time you need to insert
** labels into the label table.
*/
#ifdef MR_NEED_INITIALIZATION_CODE
#error "NEED_INITIALIZATION_CODE should not be defined on the command line"
#ifdef MR_NEED_INITIALIZATION_AT_START
#error "MR_NEED_INITIALIZATION_AT_START should not be defined on the command line"
#endif
#if !defined(MR_STATIC_CODE_ADDRESSES) || defined(PROFILE_CALLS) \
|| defined(DEBUG_LABELS) || defined(MR_INSERT_LABELS)
#define MR_NEED_INITIALIZATION_CODE
|| defined(PROFILE_TIME) || defined(DEBUG_LABELS)
#define MR_NEED_INITIALIZATION_AT_START
#endif
/*
** MR_MAY_NEED_INITIALIZATION -- the module specific initialization code
** may be needed, either at start or later.
**
** You need to run initialization code for grades without static
** code addresses, for profiling, and any time you need to insert
** labels into the label table.
*/
#ifdef MR_MAY_NEED_INITIALIZATION
#error "MR_MAY_NEED_INITIALIZATION should not be defined on the command line"
#endif
#if defined(MR_NEED_INITIALIZATION_AT_START) || defined(MR_INSERT_LABELS)
#define MR_MAY_NEED_INITIALIZATION
#endif
/*---------------------------------------------------------------------------*/

View File

@@ -18,12 +18,12 @@
#define entry(label) paste(_entry_,label)
#define skip(label) paste(skip_,label)
#ifdef MR_USE_STACK_LAYOUTS
#if defined(MR_USE_STACK_LAYOUTS)
#define MR_STACK_LAYOUT(label) (Word *) (Word) \
&(paste(mercury_data__layout__,label))
#else
#define MR_STACK_LAYOUT(label) (Word *) NULL
#endif /* MR_USE_STACK_LAYOUTS */
#define MR_STACK_LAYOUT(label) (Word *) NULL
#endif
/*
@@ -64,7 +64,6 @@
#define make_entry_sl(n, a, l) /* nothing */
#endif
#ifdef SPLIT_C_FILES
#define MODULE_STATIC_OR_EXTERN extern
#else

View File

@@ -22,57 +22,73 @@ static const char * detism_names[] = {
"erroneous", /* 4 */
"", /* 5 */
"det", /* 6 */
"multidet", /* 7 */
"multi", /* 7 */
"", /* 8 */
"", /* 9 */
"cc_nondet", /* 10 */
"", /* 11 */
"", /* 12 */
"", /* 13 */
"cc_multidet" /* 14 */
"cc_multi" /* 14 */
};
static void MR_dump_stack_record_init(void);
static void MR_dump_stack_record_frame(FILE *fp, MR_Stack_Layout_Entry *);
static void MR_dump_stack_record_flush(FILE *fp);
static void MR_dump_stack_record_print(FILE *fp, MR_Stack_Layout_Entry *,
int);
void
MR_dump_stack(Code *success_pointer, Word *det_stack_pointer,
Word *current_frame)
{
Label *label;
MR_Live_Lval location;
MR_Stack_Layout_Label *layout;
MR_Stack_Layout_Entry *entry_layout;
MR_Lval_Type type;
int number, determinism;
#ifndef MR_STACK_TRACE
fprintf(stderr, "Stack dump not available in this grade.\n");
#else
Label *label;
MR_Stack_Layout_Label *layout;
MR_Stack_Layout_Entry *entry_layout;
char *result;
fprintf(stderr, "Stack dump follows:\n");
do {
label = lookup_label_addr(success_pointer);
if (label == NULL) {
fatal_error("internal label not found");
}
label = lookup_label_addr(success_pointer);
if (label == NULL) {
fprintf(stderr, "internal label not found\n");
} else {
layout = (MR_Stack_Layout_Label *) label->e_layout;
entry_layout = layout->MR_sll_entry;
label = lookup_label_addr(
entry_layout->MR_sle_code_addr);
if (label == NULL) {
fatal_error("entry label not found");
}
result = MR_dump_stack_from_layout(stderr, entry_layout,
det_stack_pointer, current_frame);
if (result != NULL) {
fprintf(stderr, "%s\n", result);
}
}
#endif
}
const char *
MR_dump_stack_from_layout(FILE *fp, MR_Stack_Layout_Entry *entry_layout,
Word *det_stack_pointer, Word *current_frame)
{
Label *label;
MR_Live_Lval location;
MR_Stack_Layout_Label *layout;
MR_Lval_Type type;
Code *success_pointer;
int number, determinism;
MR_dump_stack_record_init();
do {
location = entry_layout->MR_sle_succip_locn;
type = MR_LIVE_LVAL_TYPE(location);
number = MR_LIVE_LVAL_NUMBER(location);
determinism = entry_layout->MR_sle_detism;
/*
/*
** A negative determinism means handwritten code has
** been reached. Usually this means we have reached
** "global_success", so we should stop dumping the stack.
@@ -83,34 +99,95 @@ MR_dump_stack(Code *success_pointer, Word *det_stack_pointer,
*/
if (determinism < 0) {
break;
MR_dump_stack_record_flush(fp);
return "reached procedure with no stack trace info";
}
MR_dump_stack_record_frame(fp, entry_layout);
if (MR_DETISM_DET_STACK(determinism)) {
fprintf(stderr, "\t%s:%s/%ld (mode %ld, %s)\n",
entry_layout->MR_sle_def_module,
entry_layout->MR_sle_name,
(long) entry_layout->MR_sle_arity,
(long) entry_layout->MR_sle_mode,
detism_names[entry_layout->MR_sle_detism]);
if (type == MR_LVAL_TYPE_STACKVAR) {
success_pointer = (Code *) field(0,
success_pointer = (Code *) field(0,
det_stack_pointer, -number);
} else {
fatal_error("can only handle stackvars");
MR_dump_stack_record_flush(fp);
return "can only handle stackvars";
}
det_stack_pointer = det_stack_pointer -
det_stack_pointer = det_stack_pointer -
entry_layout->MR_sle_stack_slots;
} else {
fprintf(stderr, "\t%s:%s/%ld (mode %ld, %s)\n",
entry_layout->MR_sle_def_module,
entry_layout->MR_sle_name,
(long) entry_layout->MR_sle_arity,
(long) entry_layout->MR_sle_mode,
detism_names[entry_layout->MR_sle_detism]);
success_pointer = bt_succip(current_frame);
current_frame = bt_succfr(current_frame);
}
if (success_pointer == MR_stack_trace_bottom) {
MR_dump_stack_record_flush(fp);
return NULL;
}
label = lookup_label_addr(success_pointer);
if (label == NULL) {
MR_dump_stack_record_flush(fp);
return "reached label with no stack trace info";
}
layout = (MR_Stack_Layout_Label *) label->e_layout;
if (layout == NULL) {
MR_dump_stack_record_flush(fp);
return "reached label with no stack layout info";
}
entry_layout = layout->MR_sll_entry;
} while (TRUE);
#endif /* MR_STACK_TRACE */
/*NOTREACHED*/
return "internal error in MR_dump_stack_from_layout";
}
static MR_Stack_Layout_Entry *prev_entry_layout;
static int prev_entry_layout_count;
static void
MR_dump_stack_record_init(void)
{
prev_entry_layout = NULL;
prev_entry_layout_count = 0;
}
static void
MR_dump_stack_record_frame(FILE *fp, MR_Stack_Layout_Entry *entry_layout)
{
if (entry_layout == prev_entry_layout) {
prev_entry_layout_count++;
} else {
MR_dump_stack_record_flush(fp);
prev_entry_layout = entry_layout;
prev_entry_layout_count = 1;
}
}
static void
MR_dump_stack_record_flush(FILE *fp)
{
if (prev_entry_layout != NULL) {
MR_dump_stack_record_print(fp, prev_entry_layout,
prev_entry_layout_count);
}
}
static void
MR_dump_stack_record_print(FILE *fp, MR_Stack_Layout_Entry *entry_layout,
int count)
{
fprintf(fp, "\t%s:%s/%ld (mode %ld, %s)",
entry_layout->MR_sle_def_module,
entry_layout->MR_sle_name,
(long) entry_layout->MR_sle_arity,
(long) entry_layout->MR_sle_mode,
detism_names[entry_layout->MR_sle_detism]);
if (count > 1) {
fprintf(fp, " * %d\n", count);
} else {
putc('\n', fp);
}
}

View File

@@ -7,6 +7,8 @@
#ifndef MERCURY_STACK_TRACE_H
#define MERCURY_STACK_TRACE_H
#include "mercury_stack_layout.h"
/*
** mercury_stack_trace.h -
** Definitions for use by the stack tracing.
@@ -21,7 +23,7 @@
** stack.
** NOTE: MR_dump_stack will assume that the succip is for the
** topmost stack frame. If you call MR_dump_stack from some
** pragma c_code that may not be the case.
** pragma c_code, that may not be the case.
** Due to some optimizations (or lack thereof) the MR_dump_stack call
** may end up inside code that has a stack frame allocated, but
** that has a succip for the previous stack frame.
@@ -35,7 +37,30 @@
** using `:- external'.
*/
extern void MR_dump_stack(Code *success_pointer, Word *det_stack_pointer,
Word *current_frame);
extern void MR_dump_stack(Code *success_pointer,
Word *det_stack_pointer,
Word *current_frame);
/*
** MR_dump_stack_from_layout:
** This function does the same job and makes the same assumptions
** as MR_dump_stack, but instead of the succip, it takes the entry
** layout of the current procedure as input. It also takes a parameter
** that tells it where to put the stack dump. If the entire stack
** was printed successfully, the return value is NULL; otherwise,
** it is a string indicating why the dump was cut short.
*/
extern const char *MR_dump_stack_from_layout(FILE *fp,
MR_Stack_Layout_Entry *entry_layout,
Word *det_stack_pointer, Word *current_frame);
/*
** MR_stack_trace_bottom should be set to the address of global_success,
** the label main/2 goes to on success. Stack dumps terminate when they
** reach a stack frame whose saved succip slot contains this address.
*/
Code *MR_stack_trace_bottom;
#endif /* MERCURY_STACK_TRACE_H */

View File

@@ -18,6 +18,7 @@
/* DEFINITIONS FOR MANIPULATING THE DET STACK */
#define detstackvar(n) (MR_sp[-n])
#define saved_detstackvar(save_area, n) (MR_saved_sp(save_area)[-n])
#define incr_sp_push_msg(n, msg) \
( \
@@ -100,6 +101,8 @@
#define cursuccfr bt_succfr(MR_curfr)
#define framevar(n) bt_var(MR_curfr,n)
#define saved_framevar(save_area, n) bt_var(MR_saved_curfr(save_area), n)
/* DEFINITIONS FOR MANIPULATING THE NONDET STACK */
#ifdef MR_DEBUG_NONDET_STACK

View File

@@ -17,33 +17,45 @@
#include <stdio.h>
#include <ctype.h>
#define MR_isdigit(c) isdigit((unsigned char) (c))
#define MR_isspace(c) isspace((unsigned char) (c))
#define MR_NAME_LEN 80
#define MR_MAX_SPY_POINTS 100
#define MR_LOG10_MAX_SPY_POINTS 20
#define MR_MAX_LINE_LEN 256
typedef struct {
bool enabled;
char module_name[MR_NAME_LEN];
char pred_name[MR_NAME_LEN];
} MR_spy_point;
typedef enum {
KEEP_INTERACTING,
STOP_INTERACTING
} MR_next;
static MR_spy_point MR_spy_points[MR_MAX_SPY_POINTS];
static int MR_next_spy_point = 0;
static MR_next MR_trace_debug_cmd(MR_trace_cmd_info *cmd,
const MR_Stack_Layout_Label *layout,
MR_trace_port port, int seqno, int depth,
const char *path);
static void MR_trace_browse(int var_count,
const MR_Stack_Layout_Vars *var_info);
static void MR_trace_browse_var(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);
static bool MR_trace_is_number(char *word, int *value);
static bool MR_trace_is_number_prefix(char *word, char **suffix,
int *value);
static int MR_trace_break_into_words(char line[], char *words[]);
static int MR_trace_getline(FILE *file, char line[], int line_max);
static void MR_trace_print_port(MR_trace_port port);
static void MR_trace_print_detism(Word detism);
@@ -67,163 +79,9 @@ MR_trace_event_internal(MR_trace_cmd_info *cmd,
saved_depth = MR_trace_call_depth;
saved_event = MR_trace_event_number;
for (;;) {
printf("mtrace> ");
count = 1;
count_given = FALSE;
cmd->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':
cmd->MR_trace_print_intermediate = TRUE;
/* fall through */
case 's':
case '\n':
cmd->MR_trace_cmd = MR_CMD_GOTO;
cmd->MR_trace_stop_event =
MR_trace_event_number + count;
MR_trace_discard_to_eol(c);
break;
case 'G':
cmd->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;
}
cmd->MR_trace_cmd = MR_CMD_GOTO;
cmd->MR_trace_stop_event = count;
MR_trace_discard_to_eol(c);
break;
case 'F':
cmd->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 {
cmd->MR_trace_cmd = MR_CMD_FINISH;
cmd->MR_trace_stop_seqno = seqno;
}
MR_trace_discard_to_eol(c);
break;
case 'C':
cmd->MR_trace_print_intermediate = TRUE;
/* fall through */
case 'c':
if (count_given)
printf("mtrace: count ignored\n");
cmd->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");
cmd->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;
}
break;
while (MR_trace_debug_cmd(cmd, layout, port, seqno, depth, path)
== KEEP_INTERACTING) {
; /* all the work is done in MR_trace_debug_cmd */
}
MR_trace_call_seqno = saved_seqno;
@@ -231,6 +89,314 @@ MR_trace_event_internal(MR_trace_cmd_info *cmd,
MR_trace_event_number = saved_event;
}
static MR_next
MR_trace_debug_cmd(MR_trace_cmd_info *cmd, const MR_Stack_Layout_Label *layout,
MR_trace_port port, int seqno, int depth, const char *path)
{
char line[MR_MAX_LINE_LEN];
char count_buf[MR_MAX_LINE_LEN];
char *raw_words[MR_MAX_LINE_LEN / 2 + 1];
char **words;
char raw_word_count;
char word_count;
char *s;
int i, n;
printf("mtrace> ");
if (MR_trace_getline(stdin, line, MR_MAX_LINE_LEN) == 0) {
/*
** We got a line without even a newline character,
** which must mean that the user typed EOF.
** We arrange things so we don't have to treat this case
** specially in the command interpreter below.
*/
(void) strcpy(line, "a\n");
}
/*
** Handle a possible number prefix on the first word on the line,
** separating it out into a word on its own.
*/
raw_word_count = MR_trace_break_into_words(line, raw_words + 1);
if (raw_word_count > 0 && MR_isdigit(*raw_words[1])) {
i = 0;
s = raw_words[1];
while (MR_isdigit(*s)) {
count_buf[i] = *s;
i++;
s++;
}
count_buf[i] = '\0';
if (*s == '\0') {
/* all of the first word constitutes a number */
/* exchange it with the command, if it exists */
if (raw_word_count > 1) {
s = raw_words[1];
raw_words[1] = raw_words[2];
raw_words[2] = s;
}
words = raw_words + 1;
word_count = raw_word_count;
} else {
/* only part of the first word constitutes a number */
/* put it in an extra word at the start */
raw_words[0] = count_buf;
raw_words[1] = s;
words = raw_words;
word_count = raw_word_count + 1;
}
} else {
words = raw_words + 1;
word_count = raw_word_count;
}
/*
** If the first word is a number, try to exchange it
** with the command word, to put the command word first.
*/
if (word_count > 1 && MR_trace_is_number(words[0], &n)
&& ! MR_trace_is_number(words[1], &n)) {
s = words[0];
words[0] = words[1];
words[1] = s;
}
/*
** At this point, the first word_count members of the words
** array contain the command.
*/
cmd->MR_trace_print_intermediate = FALSE;
if (word_count == 0) {
cmd->MR_trace_cmd = MR_CMD_GOTO;
cmd->MR_trace_print_intermediate = FALSE;
cmd->MR_trace_stop_event = MR_trace_event_number + 1;
return STOP_INTERACTING;
} else if (MR_trace_is_number(words[0], &n)) {
if (word_count == 1) {
cmd->MR_trace_cmd = MR_CMD_GOTO;
cmd->MR_trace_print_intermediate = FALSE;
cmd->MR_trace_stop_event = MR_trace_event_number + n;
return STOP_INTERACTING;
} else {
printf("One of the first two words "
"must be a command.\n");
}
} else if (streq(words[0], "S") || streq(words[0], "s")) {
if (word_count == 1) {
cmd->MR_trace_cmd = MR_CMD_GOTO;
cmd->MR_trace_print_intermediate = streq(words[0], "S");
cmd->MR_trace_stop_event = MR_trace_event_number + 1;
return STOP_INTERACTING;
} else if (word_count == 2
&& MR_trace_is_number(words[1], &n)) {
cmd->MR_trace_cmd = MR_CMD_GOTO;
cmd->MR_trace_print_intermediate = streq(words[0], "S");
cmd->MR_trace_stop_event = MR_trace_event_number + n;
return STOP_INTERACTING;
} else {
printf("This command expects at most one argument,\n"
"which must be a number.\n");
}
} else if (streq(words[0], "g") || streq(words[0], "G")) {
if (word_count == 2 && MR_trace_is_number(words[1], &n)) {
if (MR_trace_event_number < n) {
cmd->MR_trace_cmd = MR_CMD_GOTO;
cmd->MR_trace_print_intermediate =
streq(words[0], "G");
cmd->MR_trace_stop_event = n;
return STOP_INTERACTING;
} else {
printf("The debugger cannot go "
"to a past event.\n");
}
} else {
printf("This command expects exactly one argument,\n");
printf("which must be a number.\n");
}
} else if (streq(words[0], "f") || streq(words[0], "F")) {
if (word_count == 1) {
if (MR_port_is_final(port)) {
printf("This command is a no-op "
"from this port.\n");
} else {
cmd->MR_trace_cmd = MR_CMD_FINISH;
cmd->MR_trace_print_intermediate =
streq(words[0], "F");
cmd->MR_trace_stop_seqno = seqno;
return STOP_INTERACTING;
}
} else {
printf("This command expects no argument.\n");
}
} else if (streq(words[0], "c") || streq(words[0], "C")) {
if (word_count == 1) {
cmd->MR_trace_cmd = MR_CMD_TO_END;
cmd->MR_trace_print_intermediate =
streq(words[0], "C");
return STOP_INTERACTING;
} else {
printf("This command expects no argument.\n");
}
} else if (streq(words[0], "r") || streq(words[0], "R")) {
if (word_count == 1) {
cmd->MR_trace_cmd = MR_CMD_RESUME_FORWARD;
cmd->MR_trace_print_intermediate =
streq(words[0], "R");
return STOP_INTERACTING;
} else {
printf("This command expects no argument.\n");
}
} else if (streq(words[0], "p")) {
if (word_count == 1) {
MR_trace_browse((int)
layout->MR_sll_var_count,
&layout->MR_sll_var_info);
} else {
printf("This command expects no argument.\n");
}
} else if (streq(words[0], "d")) {
if (word_count == 1) {
const char *result;
do_init_modules();
result = MR_dump_stack_from_layout(stdout,
layout->MR_sll_entry, sp, maxfr);
if (result != NULL) {
printf("%s\n", result);
}
} else {
printf("This command expects no argument.\n");
}
} else if (streq(words[0], "b")) {
if (word_count != 3) {
printf("This command expects two arguments,\n");
printf("a module name and a predicate name.\n");
} else {
if (MR_next_spy_point >= MR_MAX_SPY_POINTS) {
printf("There is no room "
"for any more spy points.\n");
} else {
printf("%2d: %s %s:%s\n", MR_next_spy_point,
"+", words[1], words[2]);
strcpy(MR_spy_points[MR_next_spy_point]
.module_name, words[1]);
strcpy(MR_spy_points[MR_next_spy_point]
.pred_name, words[2]);
MR_spy_points[MR_next_spy_point].enabled
= TRUE;
MR_next_spy_point++;
}
}
} else if (streq(words[0], "?")) {
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);
}
} else if (streq(words[0], "+")) {
if (word_count == 2) {
if (MR_trace_is_number(words[1], &n)) {
if (0 <= n && n < MR_next_spy_point) {
MR_spy_points[n].enabled = TRUE;
printf("%2d: %s %s:%s\n", n,
"+",
MR_spy_points[n].module_name,
MR_spy_points[n].pred_name);
} else {
printf("Break point #%d "
"does not exist.\n", n);
}
} else if (streq(words[1], "*")) {
for (i = 0; i < MR_next_spy_point; i++) {
MR_spy_points[i].enabled = TRUE;
printf("%2d: %s %s:%s\n", i,
"+",
MR_spy_points[i].module_name,
MR_spy_points[i].pred_name);
}
} else {
printf("The argument of this command must be "
"a break point number or a `*'.\n");
}
} else {
printf("This command expects one argument,\n");
printf("which must be a break point number "
"or a `*'.\n");
}
} else if (streq(words[0], "-")) {
if (word_count == 2) {
if (MR_trace_is_number(words[1], &n)) {
if (0 <= n && n < MR_next_spy_point) {
MR_spy_points[n].enabled = FALSE;
printf("%2d: %s %s:%s\n", n,
"-",
MR_spy_points[n].module_name,
MR_spy_points[n].pred_name);
} else {
printf("Break point #%d "
"does not exist.\n", n);
}
} else if (streq(words[1], "*")) {
for (i = 0; i < MR_next_spy_point; i++) {
MR_spy_points[i].enabled = FALSE;
printf("%2d: %s %s:%s\n", i,
"-",
MR_spy_points[i].module_name,
MR_spy_points[i].pred_name);
}
} else {
printf("The argument of this command must be "
"a break point number or a `*'.\n");
}
} else {
printf("This command expects one argument,\n");
printf("which must be a break point number "
"or a `*'.\n");
}
} else if (streq(words[0], "h")) {
if (word_count != 1) {
printf("This command expects no argument.\n");
}
MR_trace_help();
} else if (streq(words[0], "a")) {
if (word_count == 1) {
printf("mtrace: are you sure you want to abort? ");
if (MR_trace_getline(stdin, line, MR_MAX_LINE_LEN)
== 0) {
/* This means the user input EOF. */
exit(0);
} else {
for (i = 0; MR_isspace(line[i]); i++)
;
if (line[i] == 'y' || line[i] == 'Y') {
exit(0);
}
}
} else {
printf("This command expects no argument.\n");
}
} else {
printf("Command not recognized. "
"Give the command `h' for help.\n");
}
return KEEP_INTERACTING;
}
static void
MR_trace_browse(int var_count, const MR_Stack_Layout_Vars *vars)
{
@@ -344,77 +510,6 @@ MR_trace_browse_var(const char *name, const MR_Stack_Layout_Var *var,
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)
{
@@ -436,49 +531,107 @@ MR_event_matches_spy_point(const MR_Stack_Layout_Label *layout)
return FALSE;
}
static int
MR_trace_skip_spaces(int c)
/*
** Is the string pointed to by word an integer?
** If yes, return its value in *value.
*/
static bool
MR_trace_is_number(char *word, int *value)
{
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;
if (MR_isdigit(*word)) {
*value = *word - '0';
word++;
while (MR_isdigit(*word)) {
*value = (*value * 10) + *word - '0';
word++;
}
c = getchar();
}
*cptr = c;
if (i > 0) {
word[i] = '\0';
return TRUE;
if (*word == '\0') {
return TRUE;
}
}
return FALSE;
}
/*
** Given a text line, break it up into words composed of non-space characters
** separated by space characters. Make each word a NULL-terminated string
** (overwriting some spaces in the line array in the process), return pointers
** to them in the words array, and return the number of words in the return
** value of the function.
**
** This function assumes that the words array is as long as necessary.
** This can be (and is) ensured by making the words array have one element
** for every two characters in the line array (since you need at least one
** non-space and one space or newline character per word).
**
** This function also assumes that line[] is guaranteed to have a white space
** character (which will usually be a newline) just before the null character.
*/
static int
MR_trace_break_into_words(char line[], char *words[])
{
int token_number;
int char_pos;
int int_val;
token_number = 0;
char_pos = 0;
/* each iteration of this loop processes one token, or end of line */
for (;;) {
while (line[char_pos] != '\0' && MR_isspace(line[char_pos])) {
char_pos++;
}
if (line[char_pos] == '\0') {
return token_number;
}
words[token_number] = line + char_pos;
while (line[char_pos] != '\0' && !MR_isspace(line[char_pos])) {
char_pos++;
}
line[char_pos] = '\0';
char_pos++;
token_number++;
}
}
/*
** Read a line from a file. If the line does not fit in the array,
** read the whole line anyway but store only the first part.
** If the last line ends without a newline, insert it.
** Return the length of the (possibly truncated) line.
** This will be zero only if getline is called at EOF;
** it will be one only if line contains a single newline;
** otherwise it will contain a newline terminated string.
*/
static int
MR_trace_getline(FILE *file, char line[], int line_max)
{
int c, i;
i = 0;
while ((c = getc(file)) != EOF && c != '\n') {
if (i < line_max-1) {
line[i++] = c;
}
}
if (c == '\n' || i > 0) {
line[i++] = '\n';
}
line[i] = '\0';
return i;
}
void
MR_trace_event_internal_report(const MR_Stack_Layout_Label *layout,
MR_trace_port port, int seqno, int depth, const char *path)

View File

@@ -178,12 +178,6 @@ static const char main_func[] =
"}\n"
;
static const char if_need_to_init[] =
"#if defined(MR_NEED_INITIALIZATION_CODE)\n\n"
;
/* --- function prototypes --- */
static void parse_options(int argc, char *argv[]);
static void usage(void);
@@ -315,8 +309,6 @@ output_sub_init_functions(void)
{
int filenum;
fputs(if_need_to_init, stdout);
fputs("static void init_modules_0(void)\n", stdout);
fputs("{\n", stdout);
@@ -324,8 +316,7 @@ output_sub_init_functions(void)
process_file(files[filenum]);
}
fputs("}\n", stdout);
fputs("\n#endif\n\n", stdout);
fputs("}\n\n", stdout);
}
static void
@@ -336,12 +327,9 @@ output_main_init_function(void)
fputs("static void init_modules(void)\n", stdout);
fputs("{\n", stdout);
fputs(if_need_to_init, stdout);
for (i = 0; i <= num_modules; i++) {
printf("\tinit_modules_%d();\n", i);
}
fputs("#endif\n", stdout);
fputs("}\n", stdout);
}