[RFA,1/3] / command : fast way to change setting(s), run a command, reset settings(s).

Message ID 20190413190016.11970-2-philippe.waroquiers@skynet.be
State New, archived
Headers

Commit Message

Philippe Waroquiers April 13, 2019, 7 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 settings(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, prefix with !.
    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 settings 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:
       /2e p full
     If e is not prefixed by a digit, then this is the same as no limit:
       set print elements unlimited
       p full
       set print elements 200
     is the equivalent of;
       /e p full

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

There is not enough letter to support all the print settings etc.
So, the upper case letters are used to introduce prefixes, typically
used for the less used settings.
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 settings.
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 behaviour is obtained by passing the relevant string value
for this new slash_seq argument.
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 settings that can be used is given by 'help /' (see below).
Of course, we might add some more settings to the below list.
For example, the (probably soon to land) 'set print max-depth'
should added to the list (the letter d is still available for this one :).

(gdb) help /
Usage: /SETTING... COMMAND
Temporarily changes settings according to SETTING, run COMMAND,
and then restore the settings to their previous values.
Each temporarily changeable setting is identified by a unique sequence
of one or more letters.
A boolean setting is temporarily activated (set to "on") by giving
its sequence of letters.  If the boolean sequence of letters is prefixed
by !, the boolean setting is deactivated (set to "off").
An integer setting is temporarily changed by using its sequence of letters
optionally prefixed by the temporary value desired.
If no prefix value is given before the integer setting letters,
the integer setting is temporarily changed to an unlimited value.
An enum setting is temporarily changed by giving its sequence of letters
followed by a letter designating the chosen enum value.
Example:
  /100e!ai print some_array
is equivalent to:
  # save current values of the settings set print elements/array/array-index.
  set print elements 100
  set print array off
  set print array-index on
  print some_array
  # restore the saved values of the changed settings.
The temporarily changeable settings 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)
/u        set print union                Boolean on|off
/v        set varsize-limit              Unsigned integer (0 means unlimited)
(gdb)

gdb/ChangeLog
2019-04-13  Philippe Waroquiers  <philippe.waroquiers@skynet.be>

	* cli/cli-decode.c (slash_settings): New variable.
	(slash_settings_var_type_name): New typedef.
	(slash_settings_var_type_names): New variable.
	(get_slash_settings_var_type_name, slash_ncmp, check_mismatch,
	scoped_set_restore_slash_settings::scoped_set_restore_slash_settings,
	scoped_set_restore_slash_settings::command, prefixname,
	scoped_set_restore_slash_settings::~scoped_set_restore_slash_settings,
	scoped_set_restore_slash_settings::swap_values): New functions.
	(add_set_or_show_cmd, add_setshow_cmd_full,
	add_setshow_enum_cmd, add_setshow_boolean_cmd,
	add_setshow_uinteger_cmd): New arg slash_seq.
	(apropos_cmd): Also match on the command slash_seq.
	(help_cmd): Output the list of slash_settings when help is requested
	for /.  If there is a slash_seq setting for a command, output
	it in the help command output.
	(print_help_cor_command): Likewise.
	(find_command_name_length): Recognise / as a command.
	* cli/cli-decode.h (struct cmd_list_element): New component slash_seq.
	(slash_setting_value): New struct.
	(scoped_set_restore_slash_settings): New class.
	command.h (add_setshow_enum_cmd, add_setshow_boolean_cmd,
	add_setshow_uinteger_cmd): New slash_sea arg.
	* top.c (slash_command): New function.
	(init_main): Add the new slash_command.
	* ada-lang.c, cp-valprint.c, gdb/frame.c, language.c, stack.c,
	valprint.c (_initialize_*): Add some slash_seq args.
---
 gdb/ada-lang.c       |   3 +-
 gdb/cli/cli-decode.c | 394 +++++++++++++++++++++++++++++++++++++++++--
 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            |  39 +++++
 gdb/valprint.c       |  27 ++-
 10 files changed, 502 insertions(+), 40 deletions(-)
  

Comments

Kevin Buettner April 14, 2019, 6:20 p.m. UTC | #1
On Sat, 13 Apr 2019 21:00:14 +0200
Philippe Waroquiers <philippe.waroquiers@skynet.be> wrote:

> 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 settings(s) to temporarily set.

Hi Philippe,

I'd like to see some discussion regarding your proposed changes to
the command line interface first.  (Though it is indeed useful to have
a patch to try out your proposed changes.)

I think it might be useful to post your examples along with a pointer
to this patch set to gdb@sourceware.org.

If this discussion has already occurred and I've somehow missed it,
I'd appreciate a link...

Kevin
  
Philippe Waroquiers April 15, 2019, 7:34 a.m. UTC | #2
On Sun, 2019-04-14 at 11:20 -0700, Kevin Buettner wrote:
> On Sat, 13 Apr 2019 21:00:14 +0200
> Philippe Waroquiers <philippe.waroquiers@skynet.be> wrote:
> 
> > 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 settings(s) to temporarily set.
> 
> Hi Philippe,
> 
> I'd like to see some discussion regarding your proposed changes to
> the command line interface first.  (Though it is indeed useful to have
> a patch to try out your proposed changes.)
> 
> I think it might be useful to post your examples along with a pointer
> to this patch set to gdb@sourceware.org.
> 
> If this discussion has already occurred and I've somehow missed it,
> I'd appreciate a link...
There was an RFC on gdb-patches but no previous discussion on gdb.
So, here it is:
https://sourceware.org/ml/gdb/2019-04/msg00006.html

Thanks
Philippe
  

Patch

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index ccf8ed8039..b106649913 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -14541,7 +14541,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..ac5008ee2a 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -429,13 +429,275 @@  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_settings;
+
+/* A printable representation of the var_types the / command accepts.  */
+typedef struct
+{
+  const char *var_type_name;
+  enum var_types var_type;
+} slash_settings_var_type_name;
+
+static const slash_settings_var_type_name slash_settings_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_settings_var_type_name (var_types var_type)
+{
+  for (const slash_settings_var_type_name *v = slash_settings_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);
+}
+
+/* If CMD has a prefix, returns its prefix name, otherwise returns "".  */
+static const char *
+prefixname (const cmd_list_element *cmd)
+{
+  return cmd->prefix == nullptr ? "" : cmd->prefix->prefixname;
+}
+
+/* Verifies that EXPECTED matches the type of FOUND.
+   If a mismatch is found, throws an error using EXPECTED_ORIGIN
+   to indicate the source of the mismatch.  */
+
+static void
+check_mismatch (var_types expected, cmd_list_element *found,
+		const char *expected_origin, const char *beg_found)
+{
+  if (expected != found->var_type)
+    error (_("Value type for / setting \"%s\" (%s%s) is '%s'.\n"
+	     "Found unexpected '%s' qualifier at \"%s\""),
+	   found->slash_seq, prefixname (found), found->name,
+	   get_slash_settings_var_type_name (found->var_type),
+	   get_slash_settings_var_type_name (expected),
+	   expected_origin);
+}
+
+/* See cli-decode.h  */
+
+scoped_set_restore_slash_settings::scoped_set_restore_slash_settings
+(const char *args)
+{
+  const char *p = args;
+
+  if (p == nullptr)
+    error (_("/ must be followed by one or more SETTING letters."));
+
+  /* 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 settings.  */
+  while (*p && *p != ' ')
+    {
+      slash_setting_value v;
+      const char *beg;
+      bool check_expected = false;
+      /* The following variables are set when (and only used if)
+	 check_expected is true.  We default initialize them to silence
+	 gcc warning.  */
+      const char *expected_origin = NULL;
+      var_types expected = var_optional_filename;
+      unsigned int uinteger_val = 1234567890;
+
+      if (*(p) == '!')
+	{
+	  check_expected = true;
+	  expected_origin = p;
+	  expected = var_boolean;
+	  p++;
+	}
+      else if (isdigit (*p))
+	{
+	  check_expected = true;
+	  expected_origin = p;
+	  expected = var_uinteger;
+	  uinteger_val = atoi (p);
+	  while (isdigit (*p))
+	    p++;
+	}
+
+      beg = p;
+      while (isupper (*p))
+	p++;
+      for (auto it = slash_settings.begin (); it != slash_settings.end (); it++)
+	{
+	  cmd_list_element *o = *it;
+
+	  if (slash_ncmp (o->slash_seq, beg, p + 1 - beg) == 0)
+	    {
+	      v.cmd = o;
+	      p++;
+	      if (check_expected)
+		check_mismatch (expected, o, expected_origin, beg);
+	      switch (o->var_type)
+		{
+		case var_boolean:
+		  if (check_expected)
+		    v.value.boolean_var = 0;
+		  else
+		    v.value.boolean_var = 1;
+		  break;
+
+		case var_uinteger:
+		  if (check_expected)
+		    {
+		      v.value.uinteger_val = uinteger_val;
+		      if (v.value.uinteger_val == 0)
+			v.value.uinteger_val = UINT_MAX;
+		    }
+		  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 setting '%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)
+	{
+	  if (*beg == ' ')
+	    {
+	      if (check_expected)
+		{
+		  if (expected == var_uinteger)
+		    error (_("Unsigned integer setting expected following"
+			     " integer value at \"%s\""), expected_origin);
+		  else if (expected == var_boolean)
+		    error (_("Boolean setting expected following"
+			     " ! at \"%s\""), expected_origin);
+		  else
+		    gdb_assert (0);
+		}
+	      else
+		error (_("No slash setting found at \"%s\""), beg);
+	    }
+	  else
+	    error (_("Slash setting not recognised at \"%s\""), beg);
+	}
+    }
+
+  while (*p == ' ')
+    p++;
+  this->cmd = p;
+
+  if (*p == 0)
+    error (_("/SETTING... must be followed by a COMMAND."));
+
+  swap_values ();
+}
+
+/* See cli-decode.h  */
+
+const char *scoped_set_restore_slash_settings::command ()
+{
+  return this->cmd;
+}
+
+/* See cli-decode.h  */
+
+scoped_set_restore_slash_settings::~scoped_set_restore_slash_settings ()
+{
+  swap_values ();
+}
+
+/* See cli-decode.h  */
+
+void scoped_set_restore_slash_settings::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 setting controlled by this command
+   will be temporarily changeable by the / command.  */
 
 static struct cmd_list_element *
 add_set_or_show_cmd (const char *name,
@@ -444,17 +706,49 @@  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_settings.begin (); it != slash_settings.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, prefixname (c), c->name);
+	      printf_unfiltered ("conflicts with slash_seq /%s for cmd %s%s\n",
+				 o->slash_seq, prefixname (o), o->name);
+	      gdb_assert (0);
+	    }
+	  else if (cmp < 0)
+	    {
+	      slash_settings.insert (it, c);
+	      break;
+	    }
+	}
+      if (cmp > 0)
+	slash_settings.emplace_back (c);
+      c->slash_seq = slash_seq;
+    }
   return c;
 }
 
@@ -465,7 +759,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 setting.  */
 
 static void
 add_setshow_cmd_full (const char *name,
@@ -478,7 +774,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 +793,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 +802,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 +829,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 +838,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 +884,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 +894,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 +1040,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 +1049,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 +1266,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 +1351,47 @@  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
+	 settings that can be temporarily set.  */
+      for (const cmd_list_element *o : slash_settings)
+	{
+	  std::string msg = string_printf ("/%-8s %s%s", o->slash_seq,
+					   prefixname (o), o->name);
+	  fprintf_filtered (stream, "%-40s %s", msg.c_str (),
+			    get_slash_settings_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->slash_seq != nullptr)
+    fprintf_filtered
+      (stream,
+       _("Use /%s COMMAND to temporarily change this setting to run COMMAND.\n"),
+       c->slash_seq);
 
   if (c->prefixlist == 0 && c->func != NULL)
     return;
@@ -1212,10 +1569,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 +1669,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..49581da0a9 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 changeable using CMD.  */
+
+typedef struct
+{
+  cmd_list_element *cmd;
+  union {
+    int boolean_var;
+    unsigned int uinteger_val;
+    const char *enum_val;
+  } value;
+} slash_setting_value;
+
+/* Processes the given ARGS to change the settings according to the
+   slash sequences found in ARGS.
+   Restore the previous settings values upon destruction.  */
+
+class scoped_set_restore_slash_settings
+{
+public:
+
+  explicit scoped_set_restore_slash_settings (const char *args);
+
+  /* Returns the command to execute after having processed the / settings
+     found in ARGS.  */
+  const char *command ();
+
+  ~scoped_set_restore_slash_settings ();
+
+  DISABLE_COPY_AND_ASSIGN (scoped_set_restore_slash_settings);
+
+private:
+  /* The command to execute with the temporarily changed settings.  */
+  const char *cmd;
+  std::vector<slash_setting_value> saved;
+
+  /* Swap the values in SAVED with the real settings 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 e883179dfa..14b553f206 100644
--- a/gdb/cp-valprint.c
+++ b/gdb/cp-valprint.c
@@ -811,7 +811,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, _("\
@@ -819,7 +820,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, _("\
@@ -827,7 +829,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 f4303d13cc..0170ae0f4d 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -2926,7 +2926,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, _("\
@@ -2940,7 +2941,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, _("\
@@ -2951,7 +2953,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 954e4c200f..b804e34efa 100644
--- a/gdb/language.c
+++ b/gdb/language.c
@@ -584,7 +584,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 f7fd9433b5..5a2491e45a 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -3103,7 +3103,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, _("\
@@ -3112,7 +3113,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 60ca74da25..76838d9d33 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -2016,6 +2016,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_settings settings (args);
+
+  execute_command (settings.command (), from_tty);
+}
+
 static void
 init_main (void)
 {
@@ -2164,6 +2174,35 @@  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: /SETTING... COMMAND\n\
+Temporarily changes settings according to SETTING, run COMMAND,\n\
+and then restore the settings to their previous values.\n\
+Each temporarily changeable setting is identified by a unique sequence\n\
+of one or more letters.\n\
+A boolean setting is temporarily activated (set to \"on\") by giving\n\
+its sequence of letters.  If the boolean sequence of letters is prefixed\n\
+by !, the boolean setting is deactivated (set to \"off\").\n\
+An integer setting is temporarily changed by using its sequence of letters\n\
+optionally prefixed by the temporary value desired.\n\
+If no prefix value is given before the integer setting letters,\n\
+the integer setting is temporarily changed to an unlimited value.\n\
+An enum setting is temporarily changed by giving its sequence of letters\n\
+followed by a letter designating the chosen enum value.\n\
+Example:\n\
+  /100e!ai print some_array\n\
+is equivalent to:\n\
+  # save current values of the settings 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 settings.\n\
+The temporarily changeable settings are:\n"));
+  /* Note that the slash_command on-line help terminates with ':', as
+     the help_cmd function will output the list of / settings that
+     can be used.  */
 }
 
 void
diff --git a/gdb/valprint.c b/gdb/valprint.c
index b02ebf6c27..1a736bb8a7 100644
--- a/gdb/valprint.c
+++ b/gdb/valprint.c
@@ -3055,7 +3055,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, _("\
@@ -3063,7 +3064,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, _("\
@@ -3072,7 +3074,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, _("\
@@ -3080,7 +3083,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, _("\
@@ -3088,7 +3092,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, _("\
@@ -3096,7 +3101,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, _("\
@@ -3104,7 +3110,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, _("\
@@ -3112,7 +3119,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,
 			     _("\
@@ -3151,5 +3159,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");
 }