From 5b9afe8a3531f45255ee97465b52d2769a9a8ab5 Mon Sep 17 00:00:00 2001 From: Yao Qi Date: Thu, 9 Aug 2012 12:53:46 +0000 Subject: [PATCH] gdb/ * cli/cli-decode.c (set_cmd_prefix): New. (lookup_cmd_for_prefixlist): New. (add_prefix_cmd): Call set_cmd_prefix and update field 'prefix' of each cmd_list_element in *prefixlist. (add_setshow_cmd_full): set_cmd_prefix. (add_alias_cmd): Likewise. * cli/cli-decode.h (struct cmd_list_element) : New field. Declare 'auto_boolean_enums'. * cli/cli-setshow.c: Include "observer.h". (notify_command_param_changed_p): New. (add_setshow_auto_boolean_cmd): Move auto_boolean_enums out. Remove 'static'. (do_setshow_command): Split it to ... (do_set_command, do_show_command): ... them. New. (do_set_command): Call observer_notify_command_param_changed if notify_command_param_changed_p returns true. (cmd_show_list): Caller update. * auto-load.c (set_auto_load_cmd): Likewise. * remote.c (show_remote_cmd): Likewise. * cli/cli-setshow.h: Update declarations. * top.c (execute_command): Call do_set_command and do_show_command. * NEWS: Mention new MI notification. * mi/mi-interp.c: Declare mi_command_param_changed. (mi_interpreter_init): Attach mi_command_param_changed to observer command_param_changed. (mi_command_param_changed): New. Remove mi_suppress_breakpoint_notifications. Define global variable mi_suppress_notification. (mi_breakpoint_created): Update. (mi_breakpoint_deleted): Likewise. (mi_breakpoint_modified): Likewise. * mi/mi-main.c (mi_cmd_execute): Likewise. Check command 'gdb-set' and set mi_suppress_notification. * mi/mi-main.h: (mi_suppress_notification): New struct. gdb/doc/ * observer.texi: New observer command_param_changed. * gdb.texinfo (GDB/MI Async Records): Doc for '=cmd-param-changed'. gdb/testsuite/ * gdb.mi/mi-cmd-param-changed.exp: New. * gdb.mi/mi-cli.exp: Update for MI notification "=cmd-param-changed". * gdb.mi/mi-var-rtti.exp, gdb.mi/mi2-cli.exp: Likewise. * gdb.mi/mi2-prompt.exp: Likewise. --- gdb/ChangeLog | 38 ++ gdb/NEWS | 5 + gdb/auto-load.c | 2 +- gdb/cli/cli-decode.c | 67 +- gdb/cli/cli-decode.h | 4 + gdb/cli/cli-setshow.c | 615 ++++++++++++------ gdb/cli/cli-setshow.h | 10 +- gdb/doc/ChangeLog | 5 + gdb/doc/gdb.texinfo | 6 + gdb/doc/observer.texi | 8 + gdb/mi/mi-interp.c | 44 +- gdb/mi/mi-main.c | 11 +- gdb/mi/mi-main.h | 10 +- gdb/remote.c | 2 +- gdb/testsuite/ChangeLog | 7 + gdb/testsuite/gdb.mi/mi-cli.exp | 4 +- gdb/testsuite/gdb.mi/mi-cmd-param-changed.exp | 103 +++ gdb/testsuite/gdb.mi/mi-var-rtti.exp | 2 +- gdb/testsuite/gdb.mi/mi2-cli.exp | 4 +- gdb/testsuite/gdb.mi/mi2-prompt.exp | 5 +- gdb/top.c | 6 +- 21 files changed, 721 insertions(+), 237 deletions(-) create mode 100644 gdb/testsuite/gdb.mi/mi-cmd-param-changed.exp diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 49c1d297e2..f1e503be00 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,41 @@ +2012-08-09 Yao Qi + + * cli/cli-decode.c (set_cmd_prefix): New. + (lookup_cmd_for_prefixlist): New. + (add_prefix_cmd): Call set_cmd_prefix and update field 'prefix' + of each cmd_list_element in *prefixlist. + (add_setshow_cmd_full): set_cmd_prefix. + (add_alias_cmd): Likewise. + * cli/cli-decode.h (struct cmd_list_element) : New field. + Declare 'auto_boolean_enums'. + * cli/cli-setshow.c: Include "observer.h". + (notify_command_param_changed_p): New. + (add_setshow_auto_boolean_cmd): Move auto_boolean_enums out. + Remove 'static'. + (do_setshow_command): Split it to ... + (do_set_command, do_show_command): ... them. New. + (do_set_command): Call observer_notify_command_param_changed if + notify_command_param_changed_p returns true. + (cmd_show_list): Caller update. + * auto-load.c (set_auto_load_cmd): Likewise. + * remote.c (show_remote_cmd): Likewise. + * cli/cli-setshow.h: Update declarations. + * top.c (execute_command): Call do_set_command and do_show_command. + + * NEWS: Mention new MI notification. + * mi/mi-interp.c: Declare mi_command_param_changed. + (mi_interpreter_init): Attach mi_command_param_changed to + observer command_param_changed. + (mi_command_param_changed): New. + Remove mi_suppress_breakpoint_notifications. + Define global variable mi_suppress_notification. + (mi_breakpoint_created): Update. + (mi_breakpoint_deleted): Likewise. + (mi_breakpoint_modified): Likewise. + * mi/mi-main.c (mi_cmd_execute): Likewise. Check command + 'gdb-set' and set mi_suppress_notification. + * mi/mi-main.h: (mi_suppress_notification): New struct. + 2012-08-09 Andreas Tobler Jan Kratochvil diff --git a/gdb/NEWS b/gdb/NEWS index 06df79e8a1..d693e64dd8 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -20,6 +20,11 @@ maint info bfds List the BFDs known to GDB. +* MI changes + + ** Command parameter changes are now notified using new async record + "=cmd-param-changed". + *** Changes in GDB 7.5 * GDB now supports x32 ABI. Visit diff --git a/gdb/auto-load.c b/gdb/auto-load.c index 2cc52c6df5..03a7539458 100644 --- a/gdb/auto-load.c +++ b/gdb/auto-load.c @@ -1022,7 +1022,7 @@ set_auto_load_cmd (char *args, int from_tty) if (list->var_type == var_boolean) { gdb_assert (list->type == set_cmd); - do_setshow_command (args, from_tty, list); + do_set_command (args, from_tty, list); } } diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c index c337b43f81..3c2e1526c9 100644 --- a/gdb/cli/cli-decode.c +++ b/gdb/cli/cli-decode.c @@ -52,6 +52,53 @@ static struct cmd_list_element *find_cmd (char *command, static void help_all (struct ui_file *stream); +/* Look up a command whose 'prefixlist' is KEY. Return the command if found, + otherwise return NULL. */ + +static struct cmd_list_element * +lookup_cmd_for_prefixlist (struct cmd_list_element **key, + struct cmd_list_element *list) +{ + struct cmd_list_element *p = NULL; + + for (p = list; p != NULL; p = p->next) + { + struct cmd_list_element *q; + + if (p->prefixlist == NULL) + continue; + else if (p->prefixlist == key) + return p; + + q = lookup_cmd_for_prefixlist (key, *(p->prefixlist)); + if (q != NULL) + return q; + } + + return NULL; +} + +static void +set_cmd_prefix (struct cmd_list_element *c, struct cmd_list_element **list) +{ + struct cmd_list_element *p; + + /* Check to see if *LIST contains any element other than C. */ + for (p = *list; p != NULL; p = p->next) + if (p != c) + break; + + if (p == NULL) + { + /* *SET_LIST only contains SET. */ + p = lookup_cmd_for_prefixlist (list, setlist); + + c->prefix = p ? (p->cmd_pointer ? p->cmd_pointer : p) : p; + } + else + c->prefix = p->prefix; +} + static void print_help_for_command (struct cmd_list_element *c, char *prefix, int recurse, struct ui_file *stream); @@ -193,6 +240,7 @@ add_cmd (char *name, enum command_class class, void (*fun) (char *, int), c->prefixlist = NULL; c->prefixname = NULL; c->allow_unknown = 0; + c->prefix = NULL; c->abbrev_flag = 0; set_cmd_completer (c, make_symbol_completion_list_fn); c->destroyer = NULL; @@ -268,6 +316,8 @@ add_alias_cmd (char *name, char *oldname, enum command_class class, c->cmd_pointer = old; c->alias_chain = old->aliases; old->aliases = c; + + set_cmd_prefix (c, list); return c; } @@ -284,10 +334,21 @@ add_prefix_cmd (char *name, enum command_class class, struct cmd_list_element **list) { struct cmd_list_element *c = add_cmd (name, class, fun, doc, list); + struct cmd_list_element *p; c->prefixlist = prefixlist; c->prefixname = prefixname; c->allow_unknown = allow_unknown; + + if (list == &cmdlist) + c->prefix = NULL; + else + set_cmd_prefix (c, list); + + /* Update the field 'prefix' of each cmd_list_element in *PREFIXLIST. */ + for (p = *prefixlist; p != NULL; p = p->next) + p->prefix = c; + return c; } @@ -392,6 +453,9 @@ add_setshow_cmd_full (char *name, full_set_doc, set_list); if (set_func != NULL) set_cmd_sfunc (set, set_func); + + set_cmd_prefix (set, set_list); + show = add_set_or_show_cmd (name, show_cmd, class, var_type, var, full_show_doc, show_list); show->show_value_func = show_func; @@ -430,6 +494,8 @@ add_setshow_enum_cmd (char *name, c->enums = enumlist; } +const char * const auto_boolean_enums[] = { "on", "off", "auto", NULL }; + /* Add an auto-boolean command named NAME to both the set and show command list lists. CLASS is as in add_cmd. VAR is address of the variable which will contain the value. DOC is the documentation @@ -445,7 +511,6 @@ add_setshow_auto_boolean_cmd (char *name, struct cmd_list_element **set_list, struct cmd_list_element **show_list) { - static const char *auto_boolean_enums[] = { "on", "off", "auto", NULL }; struct cmd_list_element *c; add_setshow_cmd_full (name, class, var_auto_boolean, var, diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h index b5e0790649..edae6e8f23 100644 --- a/gdb/cli/cli-decode.h +++ b/gdb/cli/cli-decode.h @@ -149,6 +149,9 @@ struct cmd_list_element recognized; call the prefix's own function in that case. */ char allow_unknown; + /* The prefix command of this command. */ + struct cmd_list_element *prefix; + /* Nonzero says this is an abbreviation, and should not be mentioned in lists of commands. This allows "br" to complete to "break", which it @@ -232,5 +235,6 @@ extern void not_just_help_class_command (char *arg, int from_tty); extern void print_doc_line (struct ui_file *, char *); +extern const char * const auto_boolean_enums[]; #endif /* !defined (CLI_DECODE_H) */ diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c index 7ffb89e3ee..89e095a953 100644 --- a/gdb/cli/cli-setshow.c +++ b/gdb/cli/cli-setshow.c @@ -21,6 +21,7 @@ #include #include "gdb_string.h" #include "arch-utils.h" +#include "observer.h" #include "ui-out.h" @@ -32,6 +33,21 @@ static int parse_binary_operation (char *); +/* Return true if the change of command parameter should be notified. */ + +static int +notify_command_param_changed_p (int param_changed, struct cmd_list_element *c) +{ + if (param_changed == 0) + return 0; + + if (c->class == class_maintenance || c->class == class_deprecated + || c->class == class_obscure) + return 0; + + return 1; +} + static enum auto_boolean parse_auto_binary_operation (const char *arg) @@ -116,283 +132,462 @@ deprecated_show_value_hack (struct ui_file *ignore_file, } } -/* Do a "set" or "show" command. ARG is NULL if no argument, or the +/* Do a "set" command. ARG is NULL if no argument, or the text of the argument, and FROM_TTY is nonzero if this command is being entered directly by the user (i.e. these are just like any other command). C is the command list element for the command. */ void -do_setshow_command (char *arg, int from_tty, struct cmd_list_element *c) +do_set_command (char *arg, int from_tty, struct cmd_list_element *c) { - struct ui_out *uiout = current_uiout; + /* A flag to indicate the option is changed or not. */ + int option_changed = 0; + + gdb_assert (c->type == set_cmd); - if (c->type == set_cmd) + switch (c->var_type) { - switch (c->var_type) - { - case var_string: + case var_string: + { + char *new; + char *p; + char *q; + int ch; + + if (arg == NULL) + arg = ""; + new = (char *) xmalloc (strlen (arg) + 2); + p = arg; + q = new; + while ((ch = *p++) != '\000') { - char *new; - char *p; - char *q; - int ch; - - if (arg == NULL) - arg = ""; - new = (char *) xmalloc (strlen (arg) + 2); - p = arg; - q = new; - while ((ch = *p++) != '\000') + if (ch == '\\') { - if (ch == '\\') - { - /* \ at end of argument is used after spaces - so they won't be lost. */ - /* This is obsolete now that we no longer strip - trailing whitespace and actually, the backslash - didn't get here in my test, readline or - something did something funky with a backslash - right before a newline. */ - if (*p == 0) - break; - ch = parse_escape (get_current_arch (), &p); - if (ch == 0) - break; /* C loses */ - else if (ch > 0) - *q++ = ch; - } - else + /* \ at end of argument is used after spaces + so they won't be lost. */ + /* This is obsolete now that we no longer strip + trailing whitespace and actually, the backslash + didn't get here in my test, readline or + something did something funky with a backslash + right before a newline. */ + if (*p == 0) + break; + ch = parse_escape (get_current_arch (), &p); + if (ch == 0) + break; /* C loses */ + else if (ch > 0) *q++ = ch; } + else + *q++ = ch; + } #if 0 - if (*(p - 1) != '\\') - *q++ = ' '; + if (*(p - 1) != '\\') + *q++ = ' '; #endif - *q++ = '\0'; - new = (char *) xrealloc (new, q - new); + *q++ = '\0'; + new = (char *) xrealloc (new, q - new); + + if (*(char **) c->var == NULL + || strcmp (*(char **) c->var, new) != 0) + { xfree (*(char **) c->var); *(char **) c->var = new; + + option_changed = 1; } - break; - case var_string_noescape: - if (arg == NULL) - arg = ""; + else + xfree (new); + } + break; + case var_string_noescape: + if (arg == NULL) + arg = ""; + + if (*(char **) c->var == NULL || strcmp (*(char **) c->var, arg) != 0) + { xfree (*(char **) c->var); *(char **) c->var = xstrdup (arg); - break; - case var_filename: - if (arg == NULL) - error_no_arg (_("filename to set it to.")); - /* FALLTHROUGH */ - case var_optional_filename: - xfree (*(char **) c->var); - if (arg != NULL) - { - /* Clear trailing whitespace of filename. */ - char *ptr = arg + strlen (arg) - 1; + option_changed = 1; + } + break; + case var_filename: + if (arg == NULL) + error_no_arg (_("filename to set it to.")); + /* FALLTHROUGH */ + case var_optional_filename: + { + char *val = NULL; - while (ptr >= arg && (*ptr == ' ' || *ptr == '\t')) - ptr--; - *(ptr + 1) = '\0'; + if (arg != NULL) + { + /* Clear trailing whitespace of filename. */ + char *ptr = arg + strlen (arg) - 1; - *(char **) c->var = tilde_expand (arg); - } - else - *(char **) c->var = xstrdup (""); - break; - case var_boolean: - *(int *) c->var = parse_binary_operation (arg); - break; - case var_auto_boolean: - *(enum auto_boolean *) c->var = parse_auto_binary_operation (arg); - break; - case var_uinteger: - case var_zuinteger: - if (arg == NULL) - error_no_arg (_("integer to set it to.")); - *(unsigned int *) c->var = parse_and_eval_long (arg); - if (c->var_type == var_uinteger && *(unsigned int *) c->var == 0) - *(unsigned int *) c->var = UINT_MAX; - break; - case var_integer: - case var_zinteger: + while (ptr >= arg && (*ptr == ' ' || *ptr == '\t')) + ptr--; + *(ptr + 1) = '\0'; + + val = tilde_expand (arg); + } + else + val = xstrdup (""); + + if (*(char **) c->var == NULL + || strcmp (*(char **) c->var, val) != 0) { - unsigned int val; - - if (arg == NULL) - error_no_arg (_("integer to set it to.")); - val = parse_and_eval_long (arg); - if (val == 0 && c->var_type == var_integer) - *(int *) c->var = INT_MAX; - else if (val >= INT_MAX) - error (_("integer %u out of range"), val); - else - *(int *) c->var = val; - break; + xfree (*(char **) c->var); + *(char **) c->var = val; + + option_changed = 1; } - case var_enum: + else + xfree (val); + } + break; + case var_boolean: + { + int val = parse_binary_operation (arg); + + if (val != *(int *) c->var) + { + *(int *) c->var = val; + + option_changed = 1; + } + } + break; + case var_auto_boolean: + { + enum auto_boolean val = parse_auto_binary_operation (arg); + + if (*(enum auto_boolean *) c->var != val) + { + *(enum auto_boolean *) c->var = val; + + option_changed = 1; + } + } + break; + case var_uinteger: + case var_zuinteger: + if (arg == NULL) + error_no_arg (_("integer to set it to.")); + { + unsigned int val = parse_and_eval_long (arg); + + if (c->var_type == var_uinteger && val == 0) + val = UINT_MAX; + + if (*(unsigned int *) c->var != val) + { + *(unsigned int *) c->var = val; + + option_changed = 1; + } + } + break; + case var_integer: + case var_zinteger: + { + unsigned int val; + + if (arg == NULL) + error_no_arg (_("integer to set it to.")); + val = parse_and_eval_long (arg); + if (val == 0 && c->var_type == var_integer) + val = INT_MAX; + else if (val >= INT_MAX) + error (_("integer %u out of range"), val); + + if (*(int *) c->var != val) { - int i; - int len; - int nmatches; - const char *match = NULL; - char *p; - - /* If no argument was supplied, print an informative error - message. */ - if (arg == NULL) + *(int *) c->var = val; + + option_changed = 1; + } + break; + } + case var_enum: + { + int i; + int len; + int nmatches; + const char *match = NULL; + char *p; + + /* If no argument was supplied, print an informative error + message. */ + if (arg == NULL) + { + char *msg; + int msg_len = 0; + + for (i = 0; c->enums[i]; i++) + msg_len += strlen (c->enums[i]) + 2; + + msg = xmalloc (msg_len); + *msg = '\0'; + make_cleanup (xfree, msg); + + for (i = 0; c->enums[i]; i++) { - char *msg; - int msg_len = 0; - - for (i = 0; c->enums[i]; i++) - msg_len += strlen (c->enums[i]) + 2; - - msg = xmalloc (msg_len); - *msg = '\0'; - make_cleanup (xfree, msg); - - for (i = 0; c->enums[i]; i++) - { - if (i != 0) - strcat (msg, ", "); - strcat (msg, c->enums[i]); - } - error (_("Requires an argument. Valid arguments are %s."), - msg); + if (i != 0) + strcat (msg, ", "); + strcat (msg, c->enums[i]); } + error (_("Requires an argument. Valid arguments are %s."), + msg); + } - p = strchr (arg, ' '); + p = strchr (arg, ' '); - if (p) - len = p - arg; - else - len = strlen (arg); + if (p) + len = p - arg; + else + len = strlen (arg); - nmatches = 0; - for (i = 0; c->enums[i]; i++) - if (strncmp (arg, c->enums[i], len) == 0) + nmatches = 0; + for (i = 0; c->enums[i]; i++) + if (strncmp (arg, c->enums[i], len) == 0) + { + if (c->enums[i][len] == '\0') + { + match = c->enums[i]; + nmatches = 1; + break; /* Exact match. */ + } + else { - if (c->enums[i][len] == '\0') - { - match = c->enums[i]; - nmatches = 1; - break; /* Exact match. */ - } - else - { - match = c->enums[i]; - nmatches++; - } + match = c->enums[i]; + nmatches++; } + } - if (nmatches <= 0) - error (_("Undefined item: \"%s\"."), arg); + if (nmatches <= 0) + error (_("Undefined item: \"%s\"."), arg); - if (nmatches > 1) - error (_("Ambiguous item \"%s\"."), arg); + if (nmatches > 1) + error (_("Ambiguous item \"%s\"."), arg); + if (*(const char **) c->var != match) + { *(const char **) c->var = match; + + option_changed = 1; } - break; - default: - error (_("gdb internal error: bad var_type in do_setshow_command")); - } + } + break; + default: + error (_("gdb internal error: bad var_type in do_setshow_command")); } - else if (c->type == show_cmd) + c->func (c, NULL, from_tty); + if (deprecated_set_hook) + deprecated_set_hook (c); + + if (notify_command_param_changed_p (option_changed, c)) { - struct cleanup *old_chain; - struct ui_file *stb; + char *name, *cp; + struct cmd_list_element **cmds; + struct cmd_list_element *p; + int i; + int length = 0; + + /* Compute the whole multi-word command options. If user types command + 'set foo bar baz on', c->name is 'baz', and GDB can't pass "bar" to + command option change notification, because it is confusing. We can + trace back through field 'prefix' to compute the whole options, + and pass "foo bar baz" to notification. */ + + for (i = 0, p = c; p != NULL; i++) + { + length += strlen (p->name); + length++; + + p = p->prefix; + } + cp = name = xmalloc (length); + cmds = xmalloc (sizeof (struct cmd_list_element *) * i); + + /* Track back through filed 'prefix' and cache them in CMDS. */ + for (i = 0, p = c; p != NULL; i++) + { + cmds[i] = p; + p = p->prefix; + } + + /* Don't trigger any observer notification if prefixlist is not + setlist. */ + i--; + if (cmds[i]->prefixlist != &setlist) + { + xfree (cmds); + xfree (name); + + return; + } + /* Traverse them in the reversed order, and copy their names into + NAME. */ + for (i--; i >= 0; i--) + { + memcpy (cp, cmds[i]->name, strlen (cmds[i]->name)); + cp += strlen (cmds[i]->name); - stb = mem_fileopen (); - old_chain = make_cleanup_ui_file_delete (stb); + if (i != 0) + { + cp[0] = ' '; + cp++; + } + } + cp[0] = 0; - /* Possibly call the pre hook. */ - if (c->pre_show_hook) - (c->pre_show_hook) (c); + xfree (cmds); switch (c->var_type) { case var_string: - if (*(char **) c->var) - fputstr_filtered (*(char **) c->var, '"', stb); - break; case var_string_noescape: - case var_optional_filename: case var_filename: + case var_optional_filename: case var_enum: - if (*(char **) c->var) - fputs_filtered (*(char **) c->var, stb); + observer_notify_command_param_changed (name, *(char **) c->var); break; case var_boolean: - fputs_filtered (*(int *) c->var ? "on" : "off", stb); + { + char *opt = *(int *) c->var ? "on" : "off"; + + observer_notify_command_param_changed (name, opt); + } break; case var_auto_boolean: - switch (*(enum auto_boolean*) c->var) - { - case AUTO_BOOLEAN_TRUE: - fputs_filtered ("on", stb); - break; - case AUTO_BOOLEAN_FALSE: - fputs_filtered ("off", stb); - break; - case AUTO_BOOLEAN_AUTO: - fputs_filtered ("auto", stb); - break; - default: - internal_error (__FILE__, __LINE__, - _("do_setshow_command: " - "invalid var_auto_boolean")); - break; - } + { + const char *s = auto_boolean_enums[*(enum auto_boolean *) c->var]; + + observer_notify_command_param_changed (name, s); + } break; case var_uinteger: case var_zuinteger: - if (c->var_type == var_uinteger - && *(unsigned int *) c->var == UINT_MAX) - fputs_filtered ("unlimited", stb); - else - fprintf_filtered (stb, "%u", *(unsigned int *) c->var); + { + char s[64]; + + xsnprintf (s, sizeof s, "%u", *(unsigned int *) c->var); + observer_notify_command_param_changed (name, s); + } break; case var_integer: case var_zinteger: - if (c->var_type == var_integer - && *(int *) c->var == INT_MAX) - fputs_filtered ("unlimited", stb); - else - fprintf_filtered (stb, "%d", *(int *) c->var); - break; + { + char s[64]; - default: - error (_("gdb internal error: bad var_type in do_setshow_command")); + xsnprintf (s, sizeof s, "%d", *(int *) c->var); + observer_notify_command_param_changed (name, s); + } + break; } + xfree (name); + } +} +/* Do a "show" command. ARG is NULL if no argument, or the + text of the argument, and FROM_TTY is nonzero if this command is + being entered directly by the user (i.e. these are just like any + other command). C is the command list element for the command. */ - /* FIXME: cagney/2005-02-10: Need to split this in half: code to - convert the value into a string (esentially the above); and - code to print the value out. For the latter there should be - MI and CLI specific versions. */ +void +do_show_command (char *arg, int from_tty, struct cmd_list_element *c) +{ + struct ui_out *uiout = current_uiout; + struct cleanup *old_chain; + struct ui_file *stb; - if (ui_out_is_mi_like_p (uiout)) - ui_out_field_stream (uiout, "value", stb); - else - { - char *value = ui_file_xstrdup (stb, NULL); + gdb_assert (c->type == show_cmd); + + stb = mem_fileopen (); + old_chain = make_cleanup_ui_file_delete (stb); - make_cleanup (xfree, value); - if (c->show_value_func != NULL) - c->show_value_func (gdb_stdout, from_tty, c, value); - else - deprecated_show_value_hack (gdb_stdout, from_tty, c, value); + /* Possibly call the pre hook. */ + if (c->pre_show_hook) + (c->pre_show_hook) (c); + + switch (c->var_type) + { + case var_string: + if (*(char **) c->var) + fputstr_filtered (*(char **) c->var, '"', stb); + break; + case var_string_noescape: + case var_optional_filename: + case var_filename: + case var_enum: + if (*(char **) c->var) + fputs_filtered (*(char **) c->var, stb); + break; + case var_boolean: + fputs_filtered (*(int *) c->var ? "on" : "off", stb); + break; + case var_auto_boolean: + switch (*(enum auto_boolean*) c->var) + { + case AUTO_BOOLEAN_TRUE: + fputs_filtered ("on", stb); + break; + case AUTO_BOOLEAN_FALSE: + fputs_filtered ("off", stb); + break; + case AUTO_BOOLEAN_AUTO: + fputs_filtered ("auto", stb); + break; + default: + internal_error (__FILE__, __LINE__, + _("do_show_command: " + "invalid var_auto_boolean")); + break; } - do_cleanups (old_chain); + break; + case var_uinteger: + case var_zuinteger: + if (c->var_type == var_uinteger + && *(unsigned int *) c->var == UINT_MAX) + fputs_filtered ("unlimited", stb); + else + fprintf_filtered (stb, "%u", *(unsigned int *) c->var); + break; + case var_integer: + case var_zinteger: + if (c->var_type == var_integer + && *(int *) c->var == INT_MAX) + fputs_filtered ("unlimited", stb); + else + fprintf_filtered (stb, "%d", *(int *) c->var); + break; + + default: + error (_("gdb internal error: bad var_type in do_show_command")); } + + + /* FIXME: cagney/2005-02-10: Need to split this in half: code to + convert the value into a string (esentially the above); and + code to print the value out. For the latter there should be + MI and CLI specific versions. */ + + if (ui_out_is_mi_like_p (uiout)) + ui_out_field_stream (uiout, "value", stb); else - error (_("gdb internal error: bad cmd_type in do_setshow_command")); + { + char *value = ui_file_xstrdup (stb, NULL); + + make_cleanup (xfree, value); + if (c->show_value_func != NULL) + c->show_value_func (gdb_stdout, from_tty, c, value); + else + deprecated_show_value_hack (gdb_stdout, from_tty, c, value); + } + do_cleanups (old_chain); + c->func (c, NULL, from_tty); - if (c->type == set_cmd && deprecated_set_hook) - deprecated_set_hook (c); } /* Show all the settings in a list of show commands. */ @@ -431,7 +626,7 @@ cmd_show_list (struct cmd_list_element *list, int from_tty, char *prefix) ui_out_field_string (uiout, "name", list->name); ui_out_text (uiout, ": "); if (list->type == show_cmd) - do_setshow_command ((char *) NULL, from_tty, list); + do_show_command ((char *) NULL, from_tty, list); else cmd_func (list, NULL, from_tty); /* Close the tuple. */ diff --git a/gdb/cli/cli-setshow.h b/gdb/cli/cli-setshow.h index cb8d2c5b42..ffe9abddda 100644 --- a/gdb/cli/cli-setshow.h +++ b/gdb/cli/cli-setshow.h @@ -21,12 +21,10 @@ struct cmd_list_element; /* Exported to cli/cli-cmds.c and gdb/top.c */ -/* Do a "set" or "show" command. ARG is NULL if no argument, or the - text of the argument, and FROM_TTY is nonzero if this command is - being entered directly by the user (i.e. these are just like any - other command). C is the command list element for the command. */ -extern void do_setshow_command (char *arg, int from_tty, - struct cmd_list_element *c); +extern void do_set_command (char *arg, int from_tty, + struct cmd_list_element *c); +extern void do_show_command (char *arg, int from_tty, + struct cmd_list_element *c); /* Exported to cli/cli-cmds.c and gdb/top.c, language.c and valprint.c */ diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index b8cd695bd7..cfd2bd5242 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,8 @@ +2012-08-09 Yao Qi + + * observer.texi: New observer command_param_changed. + * gdb.texinfo (GDB/MI Async Records): Doc for '=cmd-param-changed'. + 2012-08-07 Jan Kratochvil * gdbint.texinfo (Debugging GDB): In section diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 4e0342a183..a03532ee67 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -27620,6 +27620,12 @@ breakpoint commands; @xref{GDB/MI Breakpoint Commands}. The Note that if a breakpoint is emitted in the result record of a command, then it will not also be emitted in an async record. +@item =cmd-param-changed,param=@var{param},value=@var{value} +Reports that a parameter of the command @code{set @var{param}} is +changed to @var{value}. In the multi-word @code{set} command, +the @var{param} is the whole parameter list to @code{set} command. +For example, In command @code{set check type on}, @var{param} +is @code{check type} and @var{value} is @code{on}. @end table @node GDB/MI Frame Information diff --git a/gdb/doc/observer.texi b/gdb/doc/observer.texi index 6827ed8f98..c81e137273 100644 --- a/gdb/doc/observer.texi +++ b/gdb/doc/observer.texi @@ -230,6 +230,14 @@ the current top-level prompt. Variable gdb_datadir has been set. The value may not necessarily change. @end deftypefun +@deftypefun void command_param_changed (const char *@var{param}, const char *@var{value}) +The parameter of some @code{set} commands in console are changed. This +method is called after a command @code{set @var{param} @var{value}}. +@var{param} is the parameter of @code{set} command, and @var{value} +is the value of changed parameter. + +@end deftypefun + @deftypefun void test_notification (int @var{somearg}) This observer is used for internal testing. Do not use. See testsuite/gdb.gdb/observer.exp. diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c index b48713676e..94df818b18 100644 --- a/gdb/mi/mi-interp.c +++ b/gdb/mi/mi-interp.c @@ -71,6 +71,7 @@ static void mi_about_to_proceed (void); static void mi_breakpoint_created (struct breakpoint *b); static void mi_breakpoint_deleted (struct breakpoint *b); static void mi_breakpoint_modified (struct breakpoint *b); +static void mi_command_param_changed (const char *param, const char *value); static int report_initial_inferior (struct inferior *inf, void *closure); @@ -128,6 +129,7 @@ mi_interpreter_init (struct interp *interp, int top_level) observer_attach_breakpoint_created (mi_breakpoint_created); observer_attach_breakpoint_deleted (mi_breakpoint_deleted); observer_attach_breakpoint_modified (mi_breakpoint_modified); + observer_attach_command_param_changed (mi_command_param_changed); /* The initial inferior is created before this function is called, so we need to report it explicitly. Use iteration in @@ -501,10 +503,14 @@ mi_about_to_proceed (void) mi_proceeded = 1; } -/* When non-zero, no MI notifications will be emitted in - response to breakpoint change observers. */ +/* When the element is non-zero, no MI notifications will be emitted in + response to the corresponding observers. */ -int mi_suppress_breakpoint_notifications = 0; +struct mi_suppress_notification mi_suppress_notification = + { + 0, + 0, + }; /* Emit notification about a created breakpoint. */ @@ -515,7 +521,7 @@ mi_breakpoint_created (struct breakpoint *b) struct ui_out *mi_uiout = interp_ui_out (top_level_interpreter ()); volatile struct gdb_exception e; - if (mi_suppress_breakpoint_notifications) + if (mi_suppress_notification.breakpoint) return; if (b->number <= 0) @@ -546,7 +552,7 @@ mi_breakpoint_deleted (struct breakpoint *b) { struct mi_interp *mi = top_level_interpreter_data (); - if (mi_suppress_breakpoint_notifications) + if (mi_suppress_notification.breakpoint) return; if (b->number <= 0) @@ -569,7 +575,7 @@ mi_breakpoint_modified (struct breakpoint *b) struct ui_out *mi_uiout = interp_ui_out (top_level_interpreter ()); volatile struct gdb_exception e; - if (mi_suppress_breakpoint_notifications) + if (mi_suppress_notification.breakpoint) return; if (b->number <= 0) @@ -730,6 +736,32 @@ mi_solib_unloaded (struct so_list *solib) gdb_flush (mi->event_channel); } +/* Emit notification about the command parameter change. */ + +static void +mi_command_param_changed (const char *param, const char *value) +{ + struct mi_interp *mi = top_level_interpreter_data (); + struct ui_out *mi_uiout = interp_ui_out (top_level_interpreter ()); + + if (mi_suppress_notification.cmd_param_changed) + return; + + target_terminal_ours (); + + fprintf_unfiltered (mi->event_channel, + "cmd-param-changed"); + + ui_out_redirect (mi_uiout, mi->event_channel); + + ui_out_field_string (mi_uiout, "param", param); + ui_out_field_string (mi_uiout, "value", value); + + ui_out_redirect (mi_uiout, NULL); + + gdb_flush (mi->event_channel); +} + static int report_initial_inferior (struct inferior *inf, void *closure) { diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c index dfb489243c..4db36529bc 100644 --- a/gdb/mi/mi-main.c +++ b/gdb/mi/mi-main.c @@ -2099,8 +2099,15 @@ mi_cmd_execute (struct mi_parse *parse) if (strncmp (parse->command, "break-", sizeof ("break-") - 1 ) == 0) { - make_cleanup_restore_integer (&mi_suppress_breakpoint_notifications); - mi_suppress_breakpoint_notifications = 1; + make_cleanup_restore_integer (&mi_suppress_notification.breakpoint); + mi_suppress_notification.breakpoint = 1; + } + else if (strncmp (parse->command, "gdb-set", sizeof ("gdb-set") - 1) == 0) + { + int *p = &mi_suppress_notification.cmd_param_changed; + + make_cleanup_restore_integer (p); + mi_suppress_notification.cmd_param_changed = 1; } if (parse->cmd->argv_func != NULL) diff --git a/gdb/mi/mi-main.h b/gdb/mi/mi-main.h index beac2cde97..f4268c26b0 100644 --- a/gdb/mi/mi-main.h +++ b/gdb/mi/mi-main.h @@ -32,7 +32,15 @@ extern char *current_token; extern int running_result_record_printed; extern int mi_proceeded; -extern int mi_suppress_breakpoint_notifications; + +struct mi_suppress_notification +{ + /* Breakpoint notification suppressed? */ + int breakpoint; + /* Command param changed notification suppressed? */ + int cmd_param_changed; +}; +extern struct mi_suppress_notification mi_suppress_notification; #endif diff --git a/gdb/remote.c b/gdb/remote.c index 67802122e0..a974dc1bdf 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -11232,7 +11232,7 @@ show_remote_cmd (char *args, int from_tty) ui_out_field_string (uiout, "name", list->name); ui_out_text (uiout, ": "); if (list->type == show_cmd) - do_setshow_command ((char *) NULL, from_tty, list); + do_show_command ((char *) NULL, from_tty, list); else cmd_func (list, NULL, from_tty); /* Close the tuple. */ diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 7b0cb22f4a..8d6aca0862 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2012-08-09 Yao Qi + + * gdb.mi/mi-cmd-param-changed.exp: New. + * gdb.mi/mi-cli.exp: Update for MI notification "=cmd-param-changed". + * gdb.mi/mi-var-rtti.exp, gdb.mi/mi2-cli.exp: Likewise. + * gdb.mi/mi2-prompt.exp: Likewise. + 2012-08-08 Doug Evans * gdb.base/debug-expr.c: New file. diff --git a/gdb/testsuite/gdb.mi/mi-cli.exp b/gdb/testsuite/gdb.mi/mi-cli.exp index b7abbc7239..f487cbd229 100644 --- a/gdb/testsuite/gdb.mi/mi-cli.exp +++ b/gdb/testsuite/gdb.mi/mi-cli.exp @@ -70,7 +70,7 @@ set line_callee4_body [expr $line_callee4_head + 2] set line_callee4_next [expr $line_callee4_body + 1] mi_gdb_test "-interpreter-exec console \"set args foobar\"" \ - {\^done} \ + ".*=cmd-param-changed,param=\"args\",value=\"foobar\".*\\^done" \ "-interpreter-exec console \"set args foobar\"" mi_gdb_test "-interpreter-exec console \"show args\"" \ @@ -90,7 +90,7 @@ mi_gdb_test "-interpreter-exec console \"info break\"" \ "-interpreter-exec console \"info break\"" mi_gdb_test "-interpreter-exec console \"set listsize 1\"" \ - {\^done} \ + ".*=cmd-param-changed,param=\"listsize\",value=\"1\".*\\^done" \ "-interpreter-exec console \"set listsize 1\"" # {.*\~"32[ \t(\\t)]*callee1.*\\n".*\^done } diff --git a/gdb/testsuite/gdb.mi/mi-cmd-param-changed.exp b/gdb/testsuite/gdb.mi/mi-cmd-param-changed.exp new file mode 100644 index 0000000000..8c2195ccc5 --- /dev/null +++ b/gdb/testsuite/gdb.mi/mi-cmd-param-changed.exp @@ -0,0 +1,103 @@ +# Copyright 2012 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +load_lib mi-support.exp +set MIFLAGS "-i=mi" + +standard_testfile basics.c + +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { + untested mi-cmd-param-changed.exp + return -1 +} + +proc test_command_param_changed { } { with_test_prefix "cmd param" { + if [mi_gdb_start] { + return + } + mi_run_to_main + + foreach opt { "on" "off" "step" } { + mi_gdb_test "set scheduler-locking ${opt}" \ + ".*=cmd-param-changed,param=\"scheduler-locking\",value=\"${opt}\".*\\^done" \ + "\"set scheduler-locking ${opt}\"" + } + foreach opt { "on" "off" "step" } { + mi_gdb_test "interpreter-exec console \"set scheduler-locking ${opt}\"" \ + ".*=cmd-param-changed,param=\"scheduler-locking\",value=\"${opt}\".*\\^done" \ + "interpreter-exec \"set scheduler-locking ${opt}\"" + } + # Don't emit MI notification for request from MI. + mi_gdb_test "-gdb-set scheduler-locking on" \ + {\^done} \ + "\"set scheduler-locking on\" no event (requested by MI)" + + mi_gdb_test "interpreter-exec mi \"-gdb-set scheduler-locking step\"" \ + "\\&\"interpreter-exec mi .*\"-gdb-set scheduler-locking step.*\"\\\\n\"\r\n\\^done\r\n\\^done" \ + "\"set scheduler-locking step\" no event (requested by MI interp)" + mi_gdb_test "set scheduler-locking step" \ + "\\&\"set scheduler-locking step\\\\n\"\r\n\\^done" \ + "\"set scheduler-locking stepr\" no event" + + + foreach command { "circular-trace-buffer" "check type" } { + + # The default value of each command option may be different, so we first + # set it to 'off', and this may or may not trigger MI notification. + mi_gdb_test "set ${command} off" ".*\\^done" "\"set ${command}\" warmup" + + foreach boolean_opt { "on" "off" } { + mi_gdb_test "set ${command} ${boolean_opt}" \ + ".*=cmd-param-changed,param=\"${command}\",value=\"${boolean_opt}\".*\\^done" \ + "\"set ${command} ${boolean_opt}\"" + } + mi_gdb_test "set ${command} off" \ + "\\&\"set ${command} off\\\\n\"\r\n\\^done" \ + "\"set ${command}\" no event" + } + + + foreach command { "trace-notes" "remote exec-file" } { + foreach str_opt { "foo" "bar" } { + mi_gdb_test "set ${command} ${str_opt}" \ + ".*=cmd-param-changed,param=\"${command}\",value=\"${str_opt}\".*\\^done" \ + "\"set ${command} ${str_opt}\"" + } + mi_gdb_test "set ${command} bar" \ + "\\&\"set ${command} bar\\\\n\"\r\n(\\&\"warning.*|)\\^done" \ + "\"set ${command} bar\" no event" + } + + # No notification is emitted for 'maint set' commands. + foreach command { "profile" "show-debug-regs" } { + foreach boolean_opt { "on" "off" } { + mi_gdb_test "maint set ${command} ${boolean_opt}" \ + "\\&\"maint set ${command} ${boolean_opt}\\\\n\"\r\n\\^done" \ + "\"maint set ${command} ${boolean_opt}\"" + } + } + + # Full command parameters are included in the notification when a + # abbreviated one is typed. + mi_gdb_test "set ch type on" \ + ".*=cmd-param-changed,param=\"check type\",value=\"on\".*\\^done" \ + "\"set ch type on\"" + + mi_gdb_exit +}} + +test_command_param_changed + +return 0 diff --git a/gdb/testsuite/gdb.mi/mi-var-rtti.exp b/gdb/testsuite/gdb.mi/mi-var-rtti.exp index a718ffba56..dc2f922232 100644 --- a/gdb/testsuite/gdb.mi/mi-var-rtti.exp +++ b/gdb/testsuite/gdb.mi/mi-var-rtti.exp @@ -37,7 +37,7 @@ mi_prepare_inline_tests $srcfile # Enable using RTTI to determine real types of the objects proc set_print_object {state testname} { mi_gdb_test "-interpreter-exec console \"set print object ${state}\"" \ - {\^done} \ + "(.*=cmd-param-changed,param=\"print object\",value=\"${state}\".*|)\\^done" \ "-interpreter-exec console \"set print object ${state}\" in $testname" } diff --git a/gdb/testsuite/gdb.mi/mi2-cli.exp b/gdb/testsuite/gdb.mi/mi2-cli.exp index e84b1910e7..9ab75182c3 100644 --- a/gdb/testsuite/gdb.mi/mi2-cli.exp +++ b/gdb/testsuite/gdb.mi/mi2-cli.exp @@ -69,7 +69,7 @@ set line_callee4_head [gdb_get_line_number "callee4 ("] set line_callee4_body [expr $line_callee4_head + 2] mi_gdb_test "-interpreter-exec console \"set args foobar\"" \ - {\^done} \ + ".*=cmd-param-changed,param=\"args\",value=\"foobar\".*\\^done" \ "-interpreter-exec console \"set args foobar\"" mi_gdb_test "-interpreter-exec console \"show args\"" \ @@ -89,7 +89,7 @@ mi_gdb_test "-interpreter-exec console \"info break\"" \ "-interpreter-exec console \"info break\"" mi_gdb_test "-interpreter-exec console \"set listsize 1\"" \ - {\^done} \ + ".*=cmd-param-changed,param=\"listsize\",value=\"1\".*\\^done" \ "-interpreter-exec console \"set listsize 1\"" # {.*\~"32[ \t(\\t)]*callee1.*\\n".*\^done } diff --git a/gdb/testsuite/gdb.mi/mi2-prompt.exp b/gdb/testsuite/gdb.mi/mi2-prompt.exp index 6145c38fc1..eb9edec211 100644 --- a/gdb/testsuite/gdb.mi/mi2-prompt.exp +++ b/gdb/testsuite/gdb.mi/mi2-prompt.exp @@ -23,8 +23,9 @@ if [mi_gdb_start] { # Check console 'set prompt' does not affect the MI output. -mi_gdb_test {-interpreter-exec console "set prompt (banana) "} {\^done} \ - "console set prompt" +mi_gdb_test {-interpreter-exec console "set prompt (banana) "} \ + ".*=cmd-param-changed,param=\"prompt\",value=\"\\(banana\\) \".*\\^done" \ + "console set prompt" mi_gdb_test "-break-list" ".*}" "-break-list" gdb_exit diff --git a/gdb/top.c b/gdb/top.c index 213c68cab9..8251d1b9d4 100644 --- a/gdb/top.c +++ b/gdb/top.c @@ -474,8 +474,10 @@ execute_command (char *p, int from_tty) /* c->user_commands would be NULL in the case of a python command. */ if (c->class == class_user && c->user_commands) execute_user_command (c, arg); - else if (c->type == set_cmd || c->type == show_cmd) - do_setshow_command (arg, from_tty, c); + else if (c->type == set_cmd) + do_set_command (arg, from_tty, c); + else if (c->type == show_cmd) + do_show_command (arg, from_tty, c); else if (!cmd_func_p (c)) error (_("That is not a command, just a help topic.")); else if (deprecated_call_command_hook) -- 2.34.1