diff mbox

[RFC/PATCH] / command : fast way to change option(s), run a command, reset option(s).

Message ID 20190408203030.1677-1-philippe.waroquiers@skynet.be
State New
Headers show

Commit Message

Philippe Waroquiers April 8, 2019, 8:30 p.m. UTC
GDB has a lot of settings related to how commands behaves and/or print things.

There is however currently no easy way to temporarily change a setting
for a command.
This patch implements a new command called /, that allows to temporarily
change the settings in order to execute another command.
The objective for this / command is to be fast to type, so it uses
1 or 2 letters codes to identify which option(s) to temporarily set.

Examples with printing an array:
********************************
gdb -ex 'b 24' -ex 'run' ./gdb/testsuite/outputs/gdb.ada/tick_length_array_enum_idx/foo_n207_004

     Default setting: arrays are not pretty printed.
        p full
          $1 = (false, true, false, true, false)
    To 'one time' pretty print an array, we currently need to do:
         set print array on
         p full
           $2 = (false,
             true,
             false,
             true,
             false)
         set print array off
    With the / command:
         /a p full
    To set it off, put !.
    E.g.:
        /a! p some_array
     is the same as:
         set print array off
         p full
         set print array on
     Note: the / command in fact restores the setting in force before /
     changes them.
     I.e., if 'set print array' was on before '/a! p some_array'
     then after the execution of / command, it is still on.
   Other example: to print the array indexes:
      /i p full
   is the equivalent of:
       set print array-index on
       p full
       set print array-index off

   These can be combined, i.e.
     /ai p full
   is the equivalent of:
       set print array on
       set print array-index on
       p full
       set print array-index off
       set print array off

Numeric options can also be overriden.  For example, the nr of elements
to print for an array (default is 200):
       set print elements 2
       p full
       set print elements 200
     is the equivalent of:
       /e2 p full
     If e is not followed by a digit, then this is the same as no limit.

One can also do:
      /aie2 p full
to print full with temporarily setting of pretty printing 2 elements,
with array indexes.

There is not enough letter to support all the print options etc.
So, the upper case letters are used to introduce prefixes, typically
used for the less used options.
For example:
     stepi
     bt
       #0  0x0000555555555a3f in foo_n207_004 () at ...
     set print address off
     bt
       #0  foo_n207_004 () at ...
     set print address on
  The last 3 commands are the equivalent of:
     /Pa bt

It is also possible to override enum options.
E.g.
  set print frame-arguments all
  bt
  set print frame-arguments scalars
is equivalent to:
  /fa bt

Similarly, one can now easily set the language for just one command:
  set language c
  print something
  set language auto
is equivalent to:
  /lc print something

In terms of implementation:
A argument slash_seq (with default value nullptr) has been added to:
  add_setshow_boolean_cmd
  add_setshow_uinteger_cmd
  add_setshow_enum_cmd.

The above results are just obtained by passing the relevant string value.
E.g. to have a shortcut /a for pretty printing array, the "a" has been
added as last argument in the below call:
    add_setshow_boolean_cmd ("array", class_support,
			   &user_print_options.prettyformat_arrays, _("\
Set pretty formatting of arrays."), _("\
Show pretty formatting of arrays."), NULL,
			   NULL,
			   show_prettyformat_arrays,
			   &setprintlist, &showprintlist,
			   "a");

The set print frame-arguments shortcuts are obtained by adding
"f[asn]" as last argument, where 'a' means 'all', 's' means 'scalars',
'n' means 'none'.

The full list of options that can be used is given by 'help /' (see below).
Of course, we might add some more options to the below list.
For example, the (probably soon to land) 'set print max-depth'
should added to the list (the letter m is still available for this one :).

(gdb) help /
Usage: /[OPTION]... COMMAND
Temporarily set options according to OPTIONS, run COMMAND,
and then restore the options to their previous value.
Each temporarily settable option is identified by a sequence
of letters optionally followed by a value.
A boolean option is temporarily activated (set to "on") by giving
its sequence of letters.  If the boolean sequence of letters is followed
by !, the boolean option is deactivated (set to "off").
An integer option can be temporarily set by giving its sequence of letters
followed by the temporary value desired.
If no value is giving after the integer sequence of letters, the integer
option is temporarily set to an unlimited value
An enum option can be temporarily set by giving its sequence of letters
followed by a letter designating the chosen value.
Example:
  /e100a!i print some_array
is equivalent to:
  # save current values of the options set print elements/array/array-index.
  set print elements 100
  set print array off
  set print array-index on
  print some_array
--Type <RET> for more, q to quit, c to continue without paging--
  # restore the saved values of the changed options.
The temporarily settable options are:
/Be       set backtrace past-entry       Boolean on|off
/Bl       set backtrace limit            Unsigned integer (0 means unlimited)
/Bm       set backtrace past-main        Boolean on|off
/Co       set print object               Boolean on|off
/Cs       set print static-members       Boolean on|off
/Cv       set print vtbl                 Boolean on|off
/Pa       set print address              Boolean on|off
/Ps       set print symbol               Boolean on|off
/Rf       set print raw frame-arguments  Boolean on|off
/a        set print array                Boolean on|off
/e        set print elements             Unsigned integer (0 means unlimited)
/f[asn]   set print frame-arguments      Enum, a = all, s = scalars, n = none
/i        set print array-indexes        Boolean on|off
/l[aluAscCdfgmMOo] set language          Enum, a = auto, l = local,
                                u = unknown, A = ada, s = asm, c = c, C = c++,
                                d = d, f = fortran, g = go, m = minimal,
                                M = modula-2, O = objective-c, o = opencl
/n        set print null-stop            Boolean on|off
/p        set print pretty               Boolean on|off
/r        set print repeats              Unsigned integer (0 means unlimited)
--Type <RET> for more, q to quit, c to continue without paging--
/u        set print union                Boolean on|off
/v        set varsize-limit              Unsigned integer (0 means unlimited)
(gdb)

The code should be reasonable to use, it has (somewhat) be manually tested.
What is missing:
   manual
   NEWS
   tests
---
 gdb/ada-lang.c       |   3 +-
 gdb/cli/cli-decode.c | 324 ++++++++++++++++++++++++++++++++++++++++---
 gdb/cli/cli-decode.h |  43 ++++++
 gdb/command.h        |   9 +-
 gdb/cp-valprint.c    |   9 +-
 gdb/frame.c          |   9 +-
 gdb/language.c       |   3 +-
 gdb/stack.c          |   6 +-
 gdb/top.c            |  36 +++++
 gdb/valprint.c       |  27 ++--
 10 files changed, 429 insertions(+), 40 deletions(-)
diff mbox

Patch

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 51615dcd36..6f163eb51e 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -14549,7 +14549,8 @@  Set the maximum number of bytes allowed in a variable-size object."), _("\
 Show the maximum number of bytes allowed in a variable-size object."), _("\
 Attempts to access an object whose size is not a compile-time constant\n\
 and exceeds this limit will cause an error."),
-			    NULL, NULL, &setlist, &showlist);
+			    NULL, NULL, &setlist, &showlist,
+			    "v");
 
   add_info ("exceptions", info_exceptions_command,
 	    _("\
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 50430953c7..a39cacac8e 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -429,13 +429,205 @@  empty_sfunc (const char *args, int from_tty, struct cmd_list_element *c)
 {
 }
 
+/* List of cmd_list_elements whose var can be temporarily set by the
+   / command.  */
+
+static std::vector<cmd_list_element *> slash_options;
+
+/* A printable representation of the var_types the / command accepts.  */
+typedef struct
+{
+  const char *var_type_name;
+  enum var_types var_type;
+} slash_options_var_type_name;
+
+static const slash_options_var_type_name slash_options_var_type_names[] =
+{
+  { "Boolean on|off", var_boolean },
+  { "Unsigned integer (0 means unlimited)", var_uinteger },
+  { "Enum", var_enum },
+  { NULL, var_boolean }
+};
+
+static const char *
+get_slash_options_var_type_name (var_types var_type)
+{
+  for (const slash_options_var_type_name *v = slash_options_var_type_names;
+       v->var_type_name;
+       v++)
+    if (v->var_type == var_type)
+      return v->var_type_name;
+
+  gdb_assert (0);
+}
+
+/* Like strncmp, but stops the comparison at n char or when a [ character
+   or null character is encountered.  */
+static int
+slash_ncmp (const char *left, const char *right, size_t n)
+{
+  size_t l;
+  size_t r;
+
+  for (l = 0; l <= n && left[l] != 0 && left[l] != '['; l++)
+    ;
+  for (r = 0; r <= n && right[r] != 0 && right[r] != '['; r++)
+    ;
+
+  return strncmp (left, right, l > r ? r : l);
+}
+
+/* See cli-decode.h  */
+
+scoped_set_restore_slash_options::scoped_set_restore_slash_options
+(const char *args)
+{
+  const char *p = args;
+  const char *beg;
+
+  /* First accumulate all the cmd_list_elements in saved vector,
+     with the new desired value.  We will then swap the values
+     once we have succesfully parse all the options.  */
+  while (*p && *p != ' ')
+    {
+      slash_option_value v;
+
+      beg = p;
+      while (isupper (*p))
+	p++;
+      for (auto it = slash_options.begin (); it != slash_options.end (); it++)
+	{
+	  cmd_list_element *o = *it;
+
+	  if (slash_ncmp (o->slash_seq, beg, p + 1 - beg) == 0)
+	    {
+	      v.cmd = o;
+	      p++;
+	      switch (o->var_type)
+		{
+		case var_boolean:
+		  if (*(p) == '!')
+		    {
+		      p++;
+		      v.value.boolean_var = 0;
+		    }
+		  else
+		    v.value.boolean_var = 1;
+		  break;
+
+		case var_uinteger:
+		  if (isdigit (*p))
+		    {
+		      v.value.uinteger_val = atoi (p);
+		      if (v.value.uinteger_val == 0)
+			v.value.uinteger_val = UINT_MAX;
+		      while (isdigit (*p))
+			p++;
+		    }
+		  else
+		    v.value.uinteger_val = UINT_MAX;
+		  break;
+
+		case var_enum:
+		  {
+		    const char *l = o->slash_seq;
+		    const char *f;
+
+		    while (*l && *l != '[')
+		      l++;
+		    gdb_assert (*l == '[');
+		    l++;
+		    f = l;
+		    while (*f && *f != *p)
+		      f++;
+		    if (!*f)
+		      error (_("Value for slash option '%c'"
+			       " not recognised at \"%s\""),
+			     *beg, beg + 1);
+		    p++;
+		    v.value.enum_val = o->enums[f - l];
+		    break;
+		  }
+
+		default:
+		  gdb_assert (0);
+		}
+
+	      saved.emplace_back (v);
+	      break;
+	    }
+	}
+      if (beg == p)
+	error (_("Slash option not recognised at \"%s\""), beg);
+    }
+
+  while (*p == ' ')
+    p++;
+  this->cmd = p;
+
+  swap_values ();
+}
+
+/* See cli-decode.h  */
+
+const char *scoped_set_restore_slash_options::command ()
+{
+  return this->cmd;
+}
+
+/* See cli-decode.h  */
+
+scoped_set_restore_slash_options::~scoped_set_restore_slash_options ()
+{
+  swap_values ();
+}
+
+/* See cli-decode.h  */
+
+void scoped_set_restore_slash_options::swap_values ()
+{
+#define SWAPVAL_SAVED_I(TYPE, COMP) do {	      \
+    TYPE tmp = saved[i].value.COMP;		      \
+    saved[i].value.COMP = *(TYPE*)saved[i].cmd->var;  \
+    *(TYPE*)saved[i].cmd->var = tmp; } while (0)
+
+  for (int i = 0; i < saved.size (); i++)
+    {
+      switch (saved[i].cmd->var_type)
+	{
+	case var_boolean:
+	  SWAPVAL_SAVED_I (int, boolean_var);
+	  break;
+
+	case var_uinteger:
+	  SWAPVAL_SAVED_I (unsigned int, uinteger_val);
+	  break;
+
+	case var_enum:
+	  SWAPVAL_SAVED_I (const char *, enum_val);
+	  break;
+
+	default:
+	  gdb_assert (0);
+	}
+      /* If some function must be run after changing the value, call it.
+	 Note: we harcode NULL for the ARGS parameter.
+	 We might instead "print" the *_val values in a string and pass
+	 this printed value instead.  A NULL seems to be ok for now.  */
+      if (cmd_func_p (saved[i].cmd))
+	cmd_func (saved[i].cmd, NULL, 0);
+    }
+}
+
 /* Add element named NAME to command list LIST (the list for set/show
    or some sublist thereof).
    TYPE is set_cmd or show_cmd.
    CLASS is as in add_cmd.
    VAR_TYPE is the kind of thing we are setting.
    VAR is address of the variable being controlled by this command.
-   DOC is the documentation string.  */
+   DOC is the documentation string.
+   if SLASH_SEQ is not null, the option controlled by this command
+   will be temporarily settable by the / command.  */
 
 static struct cmd_list_element *
 add_set_or_show_cmd (const char *name,
@@ -444,17 +636,53 @@  add_set_or_show_cmd (const char *name,
 		     var_types var_type,
 		     void *var,
 		     const char *doc,
-		     struct cmd_list_element **list)
+		     struct cmd_list_element **list,
+		     const char *slash_seq)
 {
   struct cmd_list_element *c = add_cmd (name, theclass, doc, list);
 
   gdb_assert (type == set_cmd || type == show_cmd);
+  gdb_assert (type == set_cmd || slash_seq == nullptr);
+
   c->type = type;
   c->var_type = var_type;
   c->var = var;
   /* This needs to be something besides NULL so that this isn't
      treated as a help class.  */
   set_cmd_sfunc (c, empty_sfunc);
+
+  /* Handle the optional slash_seq.  */
+  if (slash_seq != nullptr)
+    {
+      int cmp = 1;
+
+      for (auto it = slash_options.begin (); it != slash_options.end (); it++)
+	{
+	  cmd_list_element *o = *it;
+
+	  cmp = slash_ncmp (slash_seq, o->slash_seq, UINT_MAX);
+	  if (cmp == 0)
+	    {
+	      printf_unfiltered ("slash_seq /%s for cmd %s%s\n",
+				 slash_seq, c->prefix == nullptr
+				 ? "" : c->prefix->prefixname,
+				 c->name);
+	      printf_unfiltered ("conflicts with slash_seq /%s for cmd %s%s\n",
+				 o->slash_seq, o->prefix == nullptr
+				 ? "" : o->prefix->prefixname,
+				 o->name);
+	      gdb_assert (0);
+	    }
+	  else if (cmp < 0)
+	    {
+	      slash_options.insert (it, c);
+	      break;
+	    }
+	}
+      if (cmp > 0)
+	slash_options.emplace_back (c);
+      c->slash_seq = slash_seq;
+    }
   return c;
 }
 
@@ -465,7 +693,9 @@  add_set_or_show_cmd (const char *name,
    non-NULL).  SET_DOC, SHOW_DOC and HELP_DOC are the documentation
    strings.  PRINT the format string to print the value.  SET_RESULT
    and SHOW_RESULT, if not NULL, are set to the resulting command
-   structures.  */
+   structures.  If not nullptr, SLASH_SEQ gives the sequence of letters
+   that can be used in the / command to temporarily set
+   the value of this option.  */
 
 static void
 add_setshow_cmd_full (const char *name,
@@ -478,7 +708,8 @@  add_setshow_cmd_full (const char *name,
 		      struct cmd_list_element **set_list,
 		      struct cmd_list_element **show_list,
 		      struct cmd_list_element **set_result,
-		      struct cmd_list_element **show_result)
+		      struct cmd_list_element **show_result,
+		      const char *slash_seq = nullptr)
 {
   struct cmd_list_element *set;
   struct cmd_list_element *show;
@@ -496,7 +727,7 @@  add_setshow_cmd_full (const char *name,
       full_show_doc = xstrdup (show_doc);
     }
   set = add_set_or_show_cmd (name, set_cmd, theclass, var_type, var,
-			     full_set_doc, set_list);
+			     full_set_doc, set_list, slash_seq);
   set->doc_allocated = 1;
 
   if (set_func != NULL)
@@ -505,7 +736,7 @@  add_setshow_cmd_full (const char *name,
   set_cmd_prefix (set, set_list);
 
   show = add_set_or_show_cmd (name, show_cmd, theclass, var_type, var,
-			      full_show_doc, show_list);
+			      full_show_doc, show_list, NULL);
   show->doc_allocated = 1;
   show->show_value_func = show_func;
 
@@ -532,7 +763,8 @@  add_setshow_enum_cmd (const char *name,
 		      show_value_ftype *show_func,
 		      struct cmd_list_element **set_list,
 		      struct cmd_list_element **show_list,
-		      void *context)
+		      void *context,
+		      const char *slash_seq)
 {
   struct cmd_list_element *c, *show;
 
@@ -540,7 +772,8 @@  add_setshow_enum_cmd (const char *name,
 			set_doc, show_doc, help_doc,
 			set_func, show_func,
 			set_list, show_list,
-			&c, &show);
+			&c, &show,
+			slash_seq);
   c->enums = enumlist;
 
   set_cmd_context (c, context);
@@ -585,7 +818,8 @@  add_setshow_boolean_cmd (const char *name, enum command_class theclass, int *var
 			 cmd_const_sfunc_ftype *set_func,
 			 show_value_ftype *show_func,
 			 struct cmd_list_element **set_list,
-			 struct cmd_list_element **show_list)
+			 struct cmd_list_element **show_list,
+			 const char *slash_seq)
 {
   static const char *boolean_enums[] = { "on", "off", NULL };
   struct cmd_list_element *c;
@@ -594,7 +828,7 @@  add_setshow_boolean_cmd (const char *name, enum command_class theclass, int *var
 			set_doc, show_doc, help_doc,
 			set_func, show_func,
 			set_list, show_list,
-			&c, NULL);
+			&c, NULL, slash_seq);
   c->enums = boolean_enums;
 }
 
@@ -740,7 +974,8 @@  add_setshow_uinteger_cmd (const char *name, enum command_class theclass,
 			  cmd_const_sfunc_ftype *set_func,
 			  show_value_ftype *show_func,
 			  struct cmd_list_element **set_list,
-			  struct cmd_list_element **show_list)
+			  struct cmd_list_element **show_list,
+			  const char *slash_seq)
 {
   struct cmd_list_element *set;
 
@@ -748,7 +983,7 @@  add_setshow_uinteger_cmd (const char *name, enum command_class theclass,
 			set_doc, show_doc, help_doc,
 			set_func, show_func,
 			set_list, show_list,
-			&set, NULL);
+			&set, NULL, slash_seq);
 
   set_cmd_completer (set, integer_unlimited_completer);
 }
@@ -965,6 +1200,22 @@  apropos_cmd (struct ui_file *stream,
 				      0 /* don't recurse */, stream);
 	    }
 	}
+      if (c->slash_seq != NULL)
+	{
+	  size_t slash_seq_len = strlen (c->slash_seq) + 1;
+	  char slash_seq [slash_seq_len + 1];
+
+	  slash_seq[0] = '/';
+	  slash_seq[1] = 0;
+	  strcat (slash_seq + 1, c->slash_seq);
+	  returnvalue = regex.search (slash_seq, slash_seq_len, 0,
+				      slash_seq_len, NULL);
+	  if (returnvalue >= 0)
+	    {
+	      print_help_for_command (c, prefix,
+				      0 /* don't recurse */, stream);
+	    }
+	}
       if (c->doc != NULL && returnvalue < 0)
 	{
 	  size_t doc_len = strlen (c->doc);
@@ -1034,7 +1285,43 @@  help_cmd (const char *command, struct ui_file *stream)
      listed.  */
 
   fputs_filtered (c->doc, stream);
-  fputs_filtered ("\n", stream);
+  if (strcmp (c->name, "/") == 0)
+    {
+      /* The / command help needs to be completed by the list of
+	 options that can be temporarily set.  */
+      for (const cmd_list_element *o : slash_options)
+	{
+	  std::string msg = string_printf ("/%-8s %s%s", o->slash_seq,
+					   o->prefix == nullptr
+					   ? "" : o->prefix->prefixname,
+					   o->name);
+	  fprintf_filtered (stream, "%-40s %s", msg.c_str (),
+			    get_slash_options_var_type_name (o->var_type));
+	  if (o->var_type == var_enum)
+	    {
+	      const char *l = o->slash_seq;
+	      const char *f;
+
+	      while (*l && *l != '[')
+		l++;
+	      gdb_assert (*l == '[');
+	      l++;
+	      f = l;
+	      while (*f && *f != ']')
+		{
+		  fprintf_filtered (stream, ",");
+		  wrap_here ("                               ");
+		  fprintf_filtered (stream, " %c = %s",
+				    *f, o->enums[f - l]);
+		  f++;
+		}
+	      gdb_assert (*f == ']');
+	    }
+	  fputs_filtered ("\n", stream);
+	}
+    }
+  else
+    fputs_filtered ("\n", stream);
 
   if (c->prefixlist == 0 && c->func != NULL)
     return;
@@ -1212,10 +1499,11 @@  static void
 print_help_for_command (struct cmd_list_element *c, const char *prefix,
 			int recurse, struct ui_file *stream)
 {
-  fprintf_filtered (stream, "%s%s -- ", prefix, c->name);
+  fprintf_filtered (stream, "%s%s%s%s -- ", prefix, c->name,
+		    c->slash_seq == nullptr ? "" : ", /",
+		    c->slash_seq == nullptr ? "" : c->slash_seq);
   print_doc_line (stream, c->doc);
   fputs_filtered ("\n", stream);
-  
   if (recurse
       && c->prefixlist != 0
       && c->abbrev_flag == 0)
@@ -1311,9 +1599,9 @@  find_command_name_length (const char *text)
      Note that this is larger than the character set allowed when
      creating user-defined commands.  */
 
-  /* Recognize '!' as a single character command so that, e.g., "!ls"
-     works as expected.  */
-  if (*p == '!')
+  /* Recognize '!' and '/' as a single character command so that, e.g., "!ls"
+     and "/ai print some array" works as expected.  */
+  if (*p == '!' || *p == '/')
     return 1;
 
   while (isalnum (*p) || *p == '-' || *p == '_'
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index c53683d95c..b5873020ef 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -209,6 +209,10 @@  struct cmd_list_element
        matter if type is not_set.  */
     void *var = nullptr;
 
+    /* Sequence of characters that can be used by the / command
+       to temporarily set the variable affected by "set".  */
+    const char *slash_seq = nullptr;
+
     /* Pointer to NULL terminated list of enumerated values (like
        argv).  */
     const char *const *enums = nullptr;
@@ -270,4 +274,43 @@  extern int cli_user_command_p (struct cmd_list_element *);
 
 extern int find_command_name_length (const char *);
 
+/* A structure that maintains a value for a variable settable using CMD.  */
+
+typedef struct
+{
+  cmd_list_element *cmd;
+  union {
+    int boolean_var;
+    unsigned int uinteger_val;
+    const char *enum_val;
+  } value;
+} slash_option_value;
+
+/* Processes the given ARGS to set the options according to the
+   slash options found in ARGS.
+   Restore the previous options value upon destruction.  */
+
+class scoped_set_restore_slash_options
+{
+public:
+
+  explicit scoped_set_restore_slash_options (const char *args);
+
+  /* Returns the command to execute after having processed the / options
+     found in ARGS.  */
+  const char *command ();
+
+  ~scoped_set_restore_slash_options ();
+
+  DISABLE_COPY_AND_ASSIGN (scoped_set_restore_slash_options);
+
+private:
+  /* The command to execute with the temporarily set options.  */
+  const char *cmd;
+  std::vector<slash_option_value> saved;
+
+  /* Swap the values in saved with the real options values.  */ 
+  void swap_values ();
+};
+
 #endif /* CLI_CLI_DECODE_H */
diff --git a/gdb/command.h b/gdb/command.h
index 4a239a7196..c8f4d19792 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -315,7 +315,8 @@  extern void add_setshow_enum_cmd (const char *name,
 				  show_value_ftype *show_func,
 				  struct cmd_list_element **set_list,
 				  struct cmd_list_element **show_list,
-				  void *context = nullptr);
+				  void *context = nullptr,
+				  const char *slash_seq = nullptr);
 
 extern void add_setshow_auto_boolean_cmd (const char *name,
 					  enum command_class theclass,
@@ -336,7 +337,8 @@  extern void add_setshow_boolean_cmd (const char *name,
 				     cmd_const_sfunc_ftype *set_func,
 				     show_value_ftype *show_func,
 				     struct cmd_list_element **set_list,
-				     struct cmd_list_element **show_list);
+				     struct cmd_list_element **show_list,
+				     const char *slash_seq = nullptr);
 
 extern void add_setshow_filename_cmd (const char *name,
 				      enum command_class theclass,
@@ -403,7 +405,8 @@  extern void add_setshow_uinteger_cmd (const char *name,
 				      cmd_const_sfunc_ftype *set_func,
 				      show_value_ftype *show_func,
 				      struct cmd_list_element **set_list,
-				      struct cmd_list_element **show_list);
+				      struct cmd_list_element **show_list,
+				      const char *slash_seq = nullptr);
 
 extern void add_setshow_zinteger_cmd (const char *name,
 				      enum command_class theclass,
diff --git a/gdb/cp-valprint.c b/gdb/cp-valprint.c
index 443c3b06da..3df104598e 100644
--- a/gdb/cp-valprint.c
+++ b/gdb/cp-valprint.c
@@ -813,7 +813,8 @@  Set printing of C++ static members."), _("\
 Show printing of C++ static members."), NULL,
 			   NULL,
 			   show_static_field_print,
-			   &setprintlist, &showprintlist);
+			   &setprintlist, &showprintlist,
+			   "Cs");
 
   add_setshow_boolean_cmd ("vtbl", class_support,
 			   &user_print_options.vtblprint, _("\
@@ -821,7 +822,8 @@  Set printing of C++ virtual function tables."), _("\
 Show printing of C++ virtual function tables."), NULL,
 			   NULL,
 			   show_vtblprint,
-			   &setprintlist, &showprintlist);
+			   &setprintlist, &showprintlist,
+			   "Cv");
 
   add_setshow_boolean_cmd ("object", class_support,
 			   &user_print_options.objectprint, _("\
@@ -829,7 +831,8 @@  Set printing of object's derived type based on vtable info."), _("\
 Show printing of object's derived type based on vtable info."), NULL,
 			   NULL,
 			   show_objectprint,
-			   &setprintlist, &showprintlist);
+			   &setprintlist, &showprintlist,
+			   "Co");
 
   obstack_begin (&dont_print_stat_array_obstack,
 		 32 * sizeof (struct type *));
diff --git a/gdb/frame.c b/gdb/frame.c
index d8b5f819f1..5e18eda794 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -2932,7 +2932,8 @@  of the stack trace."),
 			   NULL,
 			   show_backtrace_past_main,
 			   &set_backtrace_cmdlist,
-			   &show_backtrace_cmdlist);
+			   &show_backtrace_cmdlist,
+			   "Bm");
 
   add_setshow_boolean_cmd ("past-entry", class_obscure,
 			   &backtrace_past_entry, _("\
@@ -2946,7 +2947,8 @@  the rest of the stack trace."),
 			   NULL,
 			   show_backtrace_past_entry,
 			   &set_backtrace_cmdlist,
-			   &show_backtrace_cmdlist);
+			   &show_backtrace_cmdlist,
+			   "Be");
 
   add_setshow_uinteger_cmd ("limit", class_obscure,
 			    &backtrace_limit, _("\
@@ -2957,7 +2959,8 @@  Literal \"unlimited\" or zero means no limit."),
 			    NULL,
 			    show_backtrace_limit,
 			    &set_backtrace_cmdlist,
-			    &show_backtrace_cmdlist);
+			    &show_backtrace_cmdlist,
+			    "Bl");
 
   /* Debug this files internals.  */
   add_setshow_zuinteger_cmd ("frame", class_maintenance, &frame_debug,  _("\
diff --git a/gdb/language.c b/gdb/language.c
index ea294e670b..633242766e 100644
--- a/gdb/language.c
+++ b/gdb/language.c
@@ -585,7 +585,8 @@  add_set_language_command ()
 			_("Show the current source language."),
 			NULL, set_language_command,
 			show_language_command,
-			&setlist, &showlist);
+			&setlist, &showlist, NULL,
+			"l[aluAscCdfgmMOo]");
 }
 
 /* Iterate through all registered languages looking for and calling
diff --git a/gdb/stack.c b/gdb/stack.c
index bce8d58f54..48cad5e5bb 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -3114,7 +3114,8 @@  Usage: func NAME"));
 			print_frame_arguments_choices, &print_frame_arguments,
 			_("Set printing of non-scalar frame arguments"),
 			_("Show printing of non-scalar frame arguments"),
-			NULL, NULL, NULL, &setprintlist, &showprintlist);
+			NULL, NULL, NULL, &setprintlist, &showprintlist,
+			NULL, "f[asn]");
 
   add_setshow_boolean_cmd ("frame-arguments", no_class,
 			   &print_raw_frame_arguments, _("\
@@ -3123,7 +3124,8 @@  Show whether to print frame arguments in raw form."), _("\
 If set, frame arguments are printed in raw form, bypassing any\n\
 pretty-printers for that value."),
 			   NULL, NULL,
-			   &setprintrawlist, &showprintrawlist);
+			   &setprintrawlist, &showprintrawlist,
+			   "Rf");
 
   add_setshow_auto_boolean_cmd ("disassemble-next-line", class_stack,
 			        &disassemble_next_line, _("\
diff --git a/gdb/top.c b/gdb/top.c
index 1fc259fe0f..9478bd463a 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -2020,6 +2020,16 @@  init_gdb_version_vars (void)
   set_internalvar_integer (minor_version_var, vminor + (vrevision > 0));
 }
 
+/* Implementation of the "/" slash command.  */
+
+static void
+slash_command (const char *args, int from_tty)
+{
+  scoped_set_restore_slash_options options (args);
+
+  execute_command (options.command (), from_tty);
+}
+
 static void
 init_main (void)
 {
@@ -2168,6 +2178,32 @@  Create a new UI.  It takes two arguments:\n\
 The first argument is the name of the interpreter to run.\n\
 The second argument is the terminal the UI runs on.\n"), &cmdlist);
   set_cmd_completer (c, interpreter_completer);
+
+  add_com ("/", class_support, slash_command, _("\
+Usage: /[OPTION]... COMMAND\n\
+Temporarily set options according to OPTIONS, run COMMAND,\n\
+and then restore the options to their previous value.\n\
+Each temporarily settable option is identified by a sequence\n\
+of letters optionally followed by a value.\n\
+A boolean option is temporarily activated (set to \"on\") by giving\n\
+its sequence of letters.  If the boolean sequence of letters is followed\n\
+by !, the boolean option is deactivated (set to \"off\").\n\
+An integer option can be temporarily set by giving its sequence of letters\n\
+followed by the temporary value desired.\n\
+If no value is giving after the integer sequence of letters, the integer\n\
+option is temporarily set to an unlimited value\n\
+An enum option can be temporarily set by giving its sequence of letters\n\
+followed by a letter designating the chosen value.\n\
+Example:\n\
+  /e100a!i print some_array\n\
+is equivalent to:\n\
+  # save current values of the options set print elements/array/array-index.\n\
+  set print elements 100\n\
+  set print array off\n\
+  set print array-index on\n\
+  print some_array\n\
+  # restore the saved values of the changed options.\n\
+The temporarily settable options are:\n"));
 }
 
 void
diff --git a/gdb/valprint.c b/gdb/valprint.c
index 10020901c2..bd54a5354a 100644
--- a/gdb/valprint.c
+++ b/gdb/valprint.c
@@ -3056,7 +3056,8 @@  Show limit on string chars or array elements to print."), _("\
 \"set print elements unlimited\" causes there to be no limit."),
 			    NULL,
 			    show_print_max,
-			    &setprintlist, &showprintlist);
+			    &setprintlist, &showprintlist,
+			    "e");
 
   add_setshow_boolean_cmd ("null-stop", no_class,
 			   &user_print_options.stop_print_at_null, _("\
@@ -3064,7 +3065,8 @@  Set printing of char arrays to stop at first null char."), _("\
 Show printing of char arrays to stop at first null char."), NULL,
 			   NULL,
 			   show_stop_print_at_null,
-			   &setprintlist, &showprintlist);
+			   &setprintlist, &showprintlist,
+			   "n");
 
   add_setshow_uinteger_cmd ("repeats", no_class,
 			    &user_print_options.repeat_count_threshold, _("\
@@ -3073,7 +3075,8 @@  Show threshold for repeated print elements."), _("\
 \"set print repeats unlimited\" causes all elements to be individually printed."),
 			    NULL,
 			    show_repeat_count_threshold,
-			    &setprintlist, &showprintlist);
+			    &setprintlist, &showprintlist,
+			    "r");
 
   add_setshow_boolean_cmd ("pretty", class_support,
 			   &user_print_options.prettyformat_structs, _("\
@@ -3081,7 +3084,8 @@  Set pretty formatting of structures."), _("\
 Show pretty formatting of structures."), NULL,
 			   NULL,
 			   show_prettyformat_structs,
-			   &setprintlist, &showprintlist);
+			   &setprintlist, &showprintlist,
+			   "p");
 
   add_setshow_boolean_cmd ("union", class_support,
 			   &user_print_options.unionprint, _("\
@@ -3089,7 +3093,8 @@  Set printing of unions interior to structures."), _("\
 Show printing of unions interior to structures."), NULL,
 			   NULL,
 			   show_unionprint,
-			   &setprintlist, &showprintlist);
+			   &setprintlist, &showprintlist,
+			   "u");
 
   add_setshow_boolean_cmd ("array", class_support,
 			   &user_print_options.prettyformat_arrays, _("\
@@ -3097,7 +3102,8 @@  Set pretty formatting of arrays."), _("\
 Show pretty formatting of arrays."), NULL,
 			   NULL,
 			   show_prettyformat_arrays,
-			   &setprintlist, &showprintlist);
+			   &setprintlist, &showprintlist,
+			   "a");
 
   add_setshow_boolean_cmd ("address", class_support,
 			   &user_print_options.addressprint, _("\
@@ -3105,7 +3111,8 @@  Set printing of addresses."), _("\
 Show printing of addresses."), NULL,
 			   NULL,
 			   show_addressprint,
-			   &setprintlist, &showprintlist);
+			   &setprintlist, &showprintlist,
+			   "Pa");
 
   add_setshow_boolean_cmd ("symbol", class_support,
 			   &user_print_options.symbol_print, _("\
@@ -3113,7 +3120,8 @@  Set printing of symbol names when printing pointers."), _("\
 Show printing of symbol names when printing pointers."),
 			   NULL, NULL,
 			   show_symbol_print,
-			   &setprintlist, &showprintlist);
+			   &setprintlist, &showprintlist,
+			   "Ps");
 
   add_setshow_zuinteger_cmd ("input-radix", class_support, &input_radix_1,
 			     _("\
@@ -3152,5 +3160,6 @@  Use 'show input-radix' or 'show output-radix' to independently show each."),
                            &user_print_options.print_array_indexes, _("\
 Set printing of array indexes."), _("\
 Show printing of array indexes"), NULL, NULL, show_print_array_indexes,
-                           &setprintlist, &showprintlist);
+                           &setprintlist, &showprintlist,
+			   "i");
 }