[m-dev.] for review: new debugger command set (part 1 of 4)
Fergus Henderson
fjh at cs.mu.OZ.AU
Tue Oct 13 02:38:21 AEST 1998
mercury_trace.c:
> /* XXX We should consider whether the following should be thread local. */
> static MR_Spy_Point **MR_spy_points;
> static int MR_spy_point_next = 0;
> static int MR_spy_point_max = 0;
>
> static MR_Trace_Print_Level MR_default_print_level = MR_PRINT_LEVEL_SOME;
>
> static bool MR_scroll_control = FALSE;
> static int MR_scroll_next = 0;
> static int MR_scroll_limit = 24;
> static bool MR_echo_commands = FALSE;
>
> static int MR_trace_saved_call_seqno;
> static int MR_trace_saved_call_depth;
> static int MR_trace_saved_event_number;
>
> typedef struct MR_Line_Struct {
> char *MR_line_contents;
> struct MR_Line_Struct *MR_line_next;
> } MR_Line;
>
> static MR_Line *MR_line_head = NULL;
> static MR_Line *MR_line_tail = NULL;
It's not clear what is meant by "the following" in that comment --
does it just refer to the MR_spy_* variables, or is it all of the
variables above?
It would be a good idea to add comments explaining the meaning of
those static variables.
> Code *
> MR_trace_event_internal(MR_Trace_Cmd_Info *cmd, bool interactive,
...
> /*
> ** These globals can be overwritten when we call Mercury code,
> ** such as the term browser. We therefore save and restore them
> ** across calls to MR_trace_debug_cmd. However, we store the
> ** saved values in global instead of local variables, to allow them
> ** to be modified by MR_trace_retry().
> */
>
> MR_trace_saved_call_seqno = MR_trace_call_seqno;
> MR_trace_saved_call_depth = MR_trace_call_depth;
> MR_trace_saved_event_number = MR_trace_event_number;
It would perhaps be safer to save their values in a local struct,
using a global pointer to point to the local struct;
if you save the global pointer's old value on entry
and restore it on exit, then the code would work even
if MR_trace_event_internal() ends up being called recursively.
(Alternatively, you could pass a pointer to this struct
all the way down to MR_trace_retry(), rather than making
the pointer to this struct a global or static variable.)
> static const char MR_trace_banner[] =
> "Melbourne Mercury Debugger, mdb version 0.8.\n\
The version number should not be hard-coded.
Use the MR_VERSION macro instead.
> Copyright 1998 University of Melbourne, Australia.\n\
s/University/The University/
> static bool MR_trace_internal_initialized = FALSE;
>
> static void
> MR_trace_internal_ensure_init(void)
> {
> if (! MR_trace_internal_initialized) {
Shouldn't that variable be local to this function?
> MR_trace_internal_init_from_env("MERCURY_DEBUGGER_INIT") ||
> MR_trace_internal_init_from_file(MDBRC_FILENAME) ||
> MR_trace_internal_init_from_env_dir("HOME", MDBRC_FILENAME) ||
> MR_trace_internal_init_from_env("DEFAULT_MERCURY_DEBUGGER_INIT");
A comment here would be helpful.
The last line doesn't fit in 80 columns.
>
> static bool
> MR_trace_internal_init_from_env(const char *env_var)
> {
> char *init;
>
> init = getenv(env_var);
> if (init != NULL) {
> MR_trace_source(init);
> return TRUE;
Hmm, what if MR_trace_source fails, e.g. because the file doesn't exist?
Shouldn't those two lines be `return MR_trace_source(init);'?
> static bool
> MR_trace_internal_init_from_env_dir(const char *env_var, const char *filename)
> {
> char *env;
> char *buf;
> int len;
> FILE *fp;
>
> env = getenv(env_var);
> if (env == NULL) {
> return FALSE;
> }
>
> buf = checked_malloc(strlen(env) + strlen(filename) + 2);
> (void) strcpy(buf, env);
> (void) strcat(buf, "/");
> (void) strcat(buf, filename);
The use of "/" as a directory separator is system-specific.
On some systems, it should be "\\" or ":" instead.
You should define a macro called say MR_DIRECTORY_SEPARATOR
in mercury_conf.h and use that.
> static MR_Next
> MR_trace_debug_cmd(char *line, MR_Trace_Cmd_Info *cmd,
> const MR_Stack_Layout_Label *layout, Word *saved_regs,
> MR_Trace_Port port, int seqno, int depth, const char *path,
> int *ancestor_level, int *max_mr_num, Code **jumpaddr)
> {
> char **words;
> char **orig_words;
> int word_max;
> int word_count;
> const char *alias_key;
> char **alias_words;
> int alias_word_count;
> int alias_copy_start;
> const char *problem;
> char *semicolon;
> int i;
> int n;
This function is very long and has a lot of variables.
It would be nice if you could split it into smaller functions.
> if (MR_echo_commands) {
> fputs(line, stdout);
> putc('\n', stdout);
> }
>
> if ((semicolon = strchr(line, ';')) != NULL) {
> /*
> ** The line contains at least two commands.
> ** Execute only the first command now; put the others
> ** back in the input to be processed later.
> */
>
> *semicolon = '\0';
> MR_insert_line_at_head(MR_copy_string(semicolon+1));
> }
Shouldn't those two `if' statements be in the opposite order?
Otherwise the echoed commands will be a bit confusing, I think.
Also: s/+1/ + 1/
> if (word_count == 0) {
> alias_key = "EMPTY";
> alias_copy_start = 0;
> } else if (MR_trace_is_number(words[0], &n)) {
> alias_key = "NUMBER";
> alias_copy_start = 0;
> } else {
> alias_key = words[0];
> alias_copy_start = 1;
> }
>
> if (MR_trace_lookup_alias(alias_key, &alias_words, &alias_word_count))
> {
> MR_ensure_big_enough(alias_word_count, word, const char *,
> MR_INIT_WORD_COUNT);
>
> /* Move the original words (except the alias key) up. */
> for (i = word_count - 1; i >= alias_copy_start; i--) {
> words[i + alias_word_count - alias_copy_start]
> = words[i];
> }
>
> /* Move the alias body to the words array. */
> for (i = 0; i < alias_word_count; i++) {
> words[i] = alias_words[i];
> }
>
> word_count += alias_word_count - alias_copy_start;
> }
Those lines could be put into a separate function called say
MR_expand_aliases().
> } else if (streq(words[0], "step")) {
[... 17 lines ...]
> } else if (streq(words[0], "goto")) {
[... 18 lines ...]
> } else if (streq(words[0], "finish")) {
[... 25 lines ...]
[... etc. ...]
Each of these cases should be a separate function, IMHO.
> } else if (streq(words[0], "printlevel")) {
> if (word_count == 2) {
> if (streq(words[1], "none")) {
> MR_default_print_level = MR_PRINT_LEVEL_NONE;
> printf("Default print level set to none.\n");
> } else if (streq(words[1], "some")) {
> MR_default_print_level = MR_PRINT_LEVEL_SOME;
> printf("Default print level set to some.\n");
> } else if (streq(words[1], "all")) {
> MR_default_print_level = MR_PRINT_LEVEL_ALL;
> printf("Default print level set to all.\n");
s/set to none./set to `none'./
s/set to some./set to `some'./
s/set to all./set to `all'./
> } else {
> MR_trace_help_cat_item("parameter", "printlevel");
> }
> } else if (word_count == 1) {
> printf("The default print level is ");
> switch (MR_default_print_level) {
> case MR_PRINT_LEVEL_NONE:
> printf("none.\n");
> break;
> case MR_PRINT_LEVEL_SOME:
> printf("some.\n");
> break;
> case MR_PRINT_LEVEL_ALL:
> printf("all.\n");
Likewise.
> } else if (streq(words[0], "scroll")) {
> if (word_count == 2) {
> if (streq(words[1], "off")) {
> MR_scroll_control = FALSE;
> printf("Scroll control disabled\n");
For consistency with the stuff above, "\n" should be ".\n".
Same comment applies in lots of other places.
> } else if (streq(words[0], "alias")) {
> if (word_count == 1) {
> MR_trace_print_all_aliases(stdout);
> } else if (word_count == 2) {
> MR_trace_print_alias(stdout, words[1]);
> } else {
> if (MR_trace_valid_command(words[2])) {
> MR_trace_add_alias(words[1],
> words+2, word_count-2);
> MR_trace_print_alias(stdout, words[1]);
> } else {
> printf("%s is not a valid command\n", words[2]);
> }
> }
> } else if (streq(words[0], "unalias")) {
> if (word_count == 2) {
> if (MR_trace_remove_alias(words[1])) {
> printf("Alias %s removed.\n", words[1]);
> } else {
> printf("Alias %s cannot be removed, since it does not exist.\n", words[1]);
s/%s/`%s'/g
> } else if (streq(words[0], "document_category")) {
...
> msg = MR_trace_add_cat(words[2], slot, help_text);
> if (msg != NULL) {
> printf("Document category %s not added: %s.\n",
The first "%s" here should be "`%s'".
> } else if (streq(words[0], "document")) {
...
> printf("Document item %s in category %s"
s/%s/`%s'/g
> static void
> MR_trace_list_vars(const MR_Stack_Layout_Label *top_layout, Word *saved_regs,
> int ancestor_level)
> {
...
> if (var_count < 0) {
> printf("mdb: there is no information about live variables\n");
> return;
> } else if (var_count == 0) {
> printf("mdb: there are no live variables\n");
> return;
> }
The error messages are inconsistent: here they are prefixed with "mdb: ",
but for MR_trace_retry() they are not.
It would be better for all error messages to go through a single function
called e.g. `MR_trace_report_error' or `MR_trace_err_printf';
this function could add the "mdb: " prefix, and it could potentially
print the results to somewhere other than stdout.
> static void
> MR_trace_source(const char *filename)
> {
> FILE *fp;
>
> if ((fp = fopen(filename, "r")) != NULL) {
> MR_trace_source_from_open_file(fp);
> fclose(fp);
> } else {
> perror(filename);
> /*
> ** We actually want to write to stdout, but the following
> ** is too Unix-specific:
>
> if (errno < sys_nerr) {
> printf("%s: %s\n", filename, sys_errlist[errno]);
> } else {
> printf("Cannot open %s: unknown error\n", filename);
> }
> */
Use the ANSI C function `strerror()'.
There is one non-ANSI C system which we port to, namely SunOS 4.x,
so the configure script checks for strerror(); there is a definition
of strerror() in mercury_prof.c, but you'll need to add a prototype
for it to one of our header files.
--
Fergus Henderson <fjh at cs.mu.oz.au> | "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh> | of excellence is a lethal habit"
PGP: finger fjh at 128.250.37.3 | -- the last words of T. S. Garp.
More information about the developers
mailing list