diff --git a/compiler/globals.m b/compiler/globals.m index 0bb65677d..9482dfe92 100644 --- a/compiler/globals.m +++ b/compiler/globals.m @@ -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) --> diff --git a/runtime/mercury_conf_param.h b/runtime/mercury_conf_param.h index 90075f5ae..e49acfa6a 100644 --- a/runtime/mercury_conf_param.h +++ b/runtime/mercury_conf_param.h @@ -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 /*---------------------------------------------------------------------------*/ diff --git a/runtime/mercury_goto.h b/runtime/mercury_goto.h index 97f6634d4..788f2074c 100644 --- a/runtime/mercury_goto.h +++ b/runtime/mercury_goto.h @@ -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 diff --git a/runtime/mercury_stack_trace.c b/runtime/mercury_stack_trace.c index 9a5503a05..e1f1a8974 100644 --- a/runtime/mercury_stack_trace.c +++ b/runtime/mercury_stack_trace.c @@ -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); + } +} diff --git a/runtime/mercury_stack_trace.h b/runtime/mercury_stack_trace.h index 7f52766e5..bb75365ff 100644 --- a/runtime/mercury_stack_trace.h +++ b/runtime/mercury_stack_trace.h @@ -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 */ diff --git a/runtime/mercury_stacks.h b/runtime/mercury_stacks.h index a5c22da27..137976cee 100644 --- a/runtime/mercury_stacks.h +++ b/runtime/mercury_stacks.h @@ -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 diff --git a/runtime/mercury_trace_internal.c b/runtime/mercury_trace_internal.c index 4e077d92d..08883ef34 100644 --- a/runtime/mercury_trace_internal.c +++ b/runtime/mercury_trace_internal.c @@ -17,33 +17,45 @@ #include #include +#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) diff --git a/util/mkinit.c b/util/mkinit.c index 3a5068c43..0e1366004 100644 --- a/util/mkinit.c +++ b/util/mkinit.c @@ -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); }