[v3,16/24] Make "backtrace" support -OPT options

Message ID 20190604223444.26472-17-palves@redhat.com
State New, archived
Headers

Commit Message

Pedro Alves June 4, 2019, 10:34 p.m. UTC
  This adds support for comand options to the "backtrace" command.  We'll get:

  (gdb) bt -
  -entry-values         -hide                 -past-main
  -frame-arguments      -no-filters           -raw-frame-arguments
  -full                 -past-entry

~~~~
(gdb) help backtrace
Print backtrace of all stack frames, or innermost COUNT frames.
Usage: backtrace [OPTION]... [QUALIFIER]... [COUNT | -COUNT]

Options:
  -entry-values no|only|preferred|if-needed|both|compact|default
    Set printing of function arguments at function entry
    GDB can sometimes determine the values of function arguments at entry,
    in addition to their current values.  This option tells GDB whether
    to print the current value, the value at entry (marked as val@entry),
    or both.  Note that one or both of these values may be <optimized out>.

  -frame-arguments all|scalars|none
    Set printing of non-scalar frame arguments

  -raw-frame-arguments [on|off]
    Set whether to print frame arguments in raw form.
    If set, frame arguments are printed in raw form, bypassing any
    pretty-printers for that value.

  -past-main [on|off]
    Set whether backtraces should continue past "main".
    Normally the caller of "main" is not of interest, so GDB will terminate
    the backtrace at "main".  Set this if you need to see the rest
    of the stack trace.

  -past-entry [on|off]
    Set whether backtraces should continue past the entry point of a program.
    Normally there are no callers beyond the entry point of a program, so GDB
    will terminate the backtrace there.  Set this if you need to see
    the rest of the stack trace.

  -full
    Print values of local variables.

  -no-filters
    Prohibit frame filters from executing on a backtrace.

  -hide
    Causes Python frame filter elided frames to not be printed.

For backward compatibility, the following qualifiers are supported:

   full       - same as -full option.
   no-filters - same as -no-filters option.
   hide       - same as -hide.

With a negative COUNT, print outermost -COUNT frames.
~~~~

Implementation wise, this:

- Moves relevant options/settings globals to structures.
- Tweaks a number of functions to pass down references to such structures.
- Adds option_def structures describing the options/settings.
- Makes backtrace_command parse the options, with gdb::option::process_options.
- Tweaks "backtrace"'s help to describe the new options.
- Adds testcases.

Note that backtrace is a PROCESS_OPTIONS_UNKNOWN_IS_OPERAND command,
because of the "-COUNT" argument.

The COUNT/-COUNT argument is currently parsed as an expression.  I
considered whether it would be prudent here to require "--", but
concluded that the risk of causing a significant breakage here is much
lower compared to "print", since printing the expression is not the
whole point of the "backtrace" command.  Seems OK to me to require
typing "backtrace -past-main -- -p" if the user truly wants to refer
to the negative of a backtrace count stored in an inferior variable
called "p".

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* frame.c: Include "cli/cli-option.h.
	(user_set_backtrace_options): New.
	(backtrace_past_main, backtrace_past_entry, backtrace_limit):
	Delete.
	(get_prev_frame): Adjust.
	(boolean_option_def, uinteger_option_def)
	(set_backtrace_option_defs): New.
	(_initialize_frame): Adjust and use
	gdb::option::add_setshow_cmds_for_options to install "set
	backtrace past-main" and "set backtrace past-entry".
	* frame.h: Include "cli/cli-option.h".
	(struct frame_print_options): Forward declare.
	(print_frame_arguments_all, print_frame_arguments_scalars)
	(print_frame_arguments_none): Declare.
	(print_entry_values): Delete declaration.
	(struct frame_print_options, user_frame_print_options): New.
	(struct set_backtrace_options): New.
	(set_backtrace_option_defs, user_set_backtrace_options): Declare.
	* mi/mi-cmd-stack.c (mi_cmd_stack_list_frames)
	(mi_cmd_stack_list_locals, mi_cmd_stack_list_args)
	(mi_cmd_stack_list_variables): Pass down USER_FRAME_PRINT_OPTIONS.
	(list_args_or_locals): Add frame_print_options parameter.
	(mi_cmd_stack_info_frame): Pass down USER_FRAME_PRINT_OPTIONS.
	* python/py-framefilter.c (enumerate_args): Pass down
	USER_FRAME_PRINT_OPTIONS.
	* stack.c: Include "cli/cli-option.h".
	(print_frame_arguments_all, print_frame_arguments_scalars)
	(print_frame_arguments_none): Declare.
	(print_raw_frame_arguments, print_entry_values): Delete.
	(user_frame_print_options): New.
	(boolean_option_def, enum_option_def, frame_print_option_defs):
	New.
	(struct backtrace_cmd_options): New.
	(bt_flag_option_def): New.
	(backtrace_command_option_defs): New.
	(print_stack_frame): Pass down USER_FRAME_PRINT_OPTIONS.
	(print_frame_arg, read_frame_arg, print_frame_args)
	(print_frame_info, print_frame): Add frame_print_options parameter
	and use it.
	(info_frame_command_core): Pass down USER_FRAME_PRINT_OPTIONS.
	(backtrace_command_1): Add frame_print_options and
	backtrace_cmd_options parameters and use them.
	(make_backtrace_options_def_group): New.
	(backtrace_command): Process command options with
	gdb::option::process_options.
	(backtrace_command_completer): New.
	(_initialize_stack): Extend "backtrace"'s help to mention
	supported options.  Install completer for "backtrace".
	Install some settings commands with add_setshow_cmds_for_options.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.base/options.exp (test-backtrace): New.
	(top level): Call it.
---
 gdb/frame.c                        |  84 ++++++----
 gdb/frame.h                        |  55 +++++-
 gdb/mi/mi-cmd-stack.c              |  23 ++-
 gdb/python/py-framefilter.c        |   3 +-
 gdb/stack.c                        | 336 ++++++++++++++++++++++++++-----------
 gdb/testsuite/gdb.base/options.exp |  60 ++++++-
 6 files changed, 416 insertions(+), 145 deletions(-)
  

Patch

diff --git a/gdb/frame.c b/gdb/frame.c
index f4303d13cc1..84e0397db98 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -42,6 +42,7 @@ 
 #include "tracepoint.h"
 #include "hashtab.h"
 #include "valprint.h"
+#include "cli/cli-option.h"
 
 /* The sentinel frame terminates the innermost end of the frame chain.
    If unwound, it returns the information needed to construct an
@@ -52,6 +53,9 @@ 
 
 static struct frame_info *sentinel_frame;
 
+/* The values behind the global "set backtrace ..." settings.  */
+set_backtrace_options user_set_backtrace_options;
+
 static struct frame_info *get_prev_frame_raw (struct frame_info *this_frame);
 static const char *frame_stop_reason_symbol_string (enum unwind_stop_reason reason);
 
@@ -295,9 +299,8 @@  show_frame_debug (struct ui_file *file, int from_tty,
   fprintf_filtered (file, _("Frame debugging is %s.\n"), value);
 }
 
-/* Flag to indicate whether backtraces should stop at main et.al.  */
+/* Implementation of "show backtrace past-main".  */
 
-static int backtrace_past_main;
 static void
 show_backtrace_past_main (struct ui_file *file, int from_tty,
 			  struct cmd_list_element *c, const char *value)
@@ -308,7 +311,8 @@  show_backtrace_past_main (struct ui_file *file, int from_tty,
 		    value);
 }
 
-static int backtrace_past_entry;
+/* Implementation of "show backtrace past-entry".  */
+
 static void
 show_backtrace_past_entry (struct ui_file *file, int from_tty,
 			   struct cmd_list_element *c, const char *value)
@@ -318,7 +322,8 @@  show_backtrace_past_entry (struct ui_file *file, int from_tty,
 		    value);
 }
 
-static unsigned int backtrace_limit = UINT_MAX;
+/* Implementation of "show backtrace limit".  */
+
 static void
 show_backtrace_limit (struct ui_file *file, int from_tty,
 		      struct cmd_list_element *c, const char *value)
@@ -2276,7 +2281,7 @@  get_prev_frame (struct frame_info *this_frame)
      point inside the main function.  */
   if (this_frame->level >= 0
       && get_frame_type (this_frame) == NORMAL_FRAME
-      && !backtrace_past_main
+      && !user_set_backtrace_options.backtrace_past_main
       && frame_pc_p
       && inside_main_func (this_frame))
     /* Don't unwind past main().  Note, this is done _before_ the
@@ -2293,7 +2298,7 @@  get_prev_frame (struct frame_info *this_frame)
      being 1-based and the level being 0-based, and the other accounts for
      the level of the new frame instead of the level of the current
      frame.  */
-  if (this_frame->level + 2 > backtrace_limit)
+  if (this_frame->level + 2 > user_set_backtrace_options.backtrace_limit)
     {
       frame_debug_got_null_frame (this_frame, "backtrace limit exceeded");
       return NULL;
@@ -2323,7 +2328,7 @@  get_prev_frame (struct frame_info *this_frame)
      application.  */
   if (this_frame->level >= 0
       && get_frame_type (this_frame) == NORMAL_FRAME
-      && !backtrace_past_entry
+      && !user_set_backtrace_options.backtrace_past_entry
       && frame_pc_p
       && inside_entry_func (this_frame))
     {
@@ -2896,6 +2901,39 @@  show_backtrace_cmd (const char *args, int from_tty)
   cmd_show_list (show_backtrace_cmdlist, from_tty, "");
 }
 
+/* Definition of the "set backtrace" settings that are exposed as
+   "backtrace" command options.  */
+
+using boolean_option_def
+  = gdb::option::boolean_option_def<set_backtrace_options>;
+using uinteger_option_def
+  = gdb::option::uinteger_option_def<set_backtrace_options>;
+
+const gdb::option::option_def set_backtrace_option_defs[] = {
+
+  boolean_option_def {
+    "past-main",
+    [] (set_backtrace_options *opt) { return &opt->backtrace_past_main; },
+    show_backtrace_past_main, /* show_cmd_cb */
+    N_("Set whether backtraces should continue past \"main\"."),
+    N_("Show whether backtraces should continue past \"main\"."),
+    N_("Normally the caller of \"main\" is not of interest, so GDB will terminate\n\
+the backtrace at \"main\".  Set this if you need to see the rest\n\
+of the stack trace."),
+  },
+
+  boolean_option_def {
+    "past-entry",
+    [] (set_backtrace_options *opt) { return &opt->backtrace_past_entry; },
+    show_backtrace_past_entry, /* show_cmd_cb */
+    N_("Set whether backtraces should continue past the entry point of a program."),
+    N_("Show whether backtraces should continue past the entry point of a program."),
+    N_("Normally there are no callers beyond the entry point of a program, so GDB\n\
+will terminate the backtrace there.  Set this if you need to see\n\
+the rest of the stack trace."),
+  },
+};
+
 void
 _initialize_frame (void)
 {
@@ -2916,34 +2954,8 @@  Show backtrace variables such as the backtrace limit"),
 		  &show_backtrace_cmdlist, "show backtrace ",
 		  0/*allow-unknown*/, &showlist);
 
-  add_setshow_boolean_cmd ("past-main", class_obscure,
-			   &backtrace_past_main, _("\
-Set whether backtraces should continue past \"main\"."), _("\
-Show whether backtraces should continue past \"main\"."), _("\
-Normally the caller of \"main\" is not of interest, so GDB will terminate\n\
-the backtrace at \"main\".  Set this variable if you need to see the rest\n\
-of the stack trace."),
-			   NULL,
-			   show_backtrace_past_main,
-			   &set_backtrace_cmdlist,
-			   &show_backtrace_cmdlist);
-
-  add_setshow_boolean_cmd ("past-entry", class_obscure,
-			   &backtrace_past_entry, _("\
-Set whether backtraces should continue past the entry point of a program."),
-			   _("\
-Show whether backtraces should continue past the entry point of a program."),
-			   _("\
-Normally there are no callers beyond the entry point of a program, so GDB\n\
-will terminate the backtrace there.  Set this variable if you need to see\n\
-the rest of the stack trace."),
-			   NULL,
-			   show_backtrace_past_entry,
-			   &set_backtrace_cmdlist,
-			   &show_backtrace_cmdlist);
-
   add_setshow_uinteger_cmd ("limit", class_obscure,
-			    &backtrace_limit, _("\
+			    &user_set_backtrace_options.backtrace_limit, _("\
 Set an upper bound on the number of backtrace levels."), _("\
 Show the upper bound on the number of backtrace levels."), _("\
 No more than the specified number of frames can be displayed or examined.\n\
@@ -2953,6 +2965,10 @@  Literal \"unlimited\" or zero means no limit."),
 			    &set_backtrace_cmdlist,
 			    &show_backtrace_cmdlist);
 
+  gdb::option::add_setshow_cmds_for_options
+    (class_stack, &user_set_backtrace_options,
+     set_backtrace_option_defs, &set_backtrace_cmdlist, &show_backtrace_cmdlist);
+
   /* Debug this files internals.  */
   add_setshow_zuinteger_cmd ("frame", class_maintenance, &frame_debug,  _("\
 Set frame debugging."), _("\
diff --git a/gdb/frame.h b/gdb/frame.h
index 0a0baf46a0c..a79eeeeab16 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -70,6 +70,7 @@ 
    */
 
 #include "language.h"
+#include "cli/cli-option.h"
 
 struct symtab_and_line;
 struct frame_unwind;
@@ -78,6 +79,7 @@  struct block;
 struct gdbarch;
 struct ui_file;
 struct ui_out;
+struct frame_print_options;
 
 /* Status of a given frame's stack.  */
 
@@ -753,7 +755,8 @@  extern void print_stack_frame (struct frame_info *, int print_level,
 			       enum print_what print_what,
 			       int set_current_sal);
 
-extern void print_frame_info (struct frame_info *, int print_level,
+extern void print_frame_info (const frame_print_options &fp_opts,
+			      struct frame_info *, int print_level,
 			      enum print_what print_what, int args,
 			      int set_current_sal);
 
@@ -764,6 +767,12 @@  extern int deprecated_frame_register_read (struct frame_info *frame, int regnum,
 
 /* From stack.c.  */
 
+/* The possible choices of "set print frame-arguments".  */
+extern const char print_frame_arguments_all[];
+extern const char print_frame_arguments_scalars[];
+extern const char print_frame_arguments_none[];
+
+/* The possible choices of "set print entry-values".  */
 extern const char print_entry_values_no[];
 extern const char print_entry_values_only[];
 extern const char print_entry_values_preferred[];
@@ -771,7 +780,22 @@  extern const char print_entry_values_if_needed[];
 extern const char print_entry_values_both[];
 extern const char print_entry_values_compact[];
 extern const char print_entry_values_default[];
-extern const char *print_entry_values;
+
+/* Data for the frame-printing "set print" settings exposed as command
+   options.  */
+
+struct frame_print_options
+{
+  const char *print_frame_arguments = print_frame_arguments_scalars;
+  const char *print_entry_values = print_entry_values_default;
+
+  /* If non-zero, don't invoke pretty-printers for frame
+     arguments.  */
+  int print_raw_frame_arguments;
+};
+
+/* The values behind the global "set print ..." settings.  */
+extern frame_print_options user_frame_print_options;
 
 /* Inferior function parameter value read in from a frame.  */
 
@@ -800,7 +824,8 @@  struct frame_arg
   const char *entry_kind;
 };
 
-extern void read_frame_arg (struct symbol *sym, struct frame_info *frame,
+extern void read_frame_arg (const frame_print_options &fp_opts,
+			    symbol *sym, frame_info *frame,
 			    struct frame_arg *argp,
 			    struct frame_arg *entryargp);
 extern void read_frame_local (struct symbol *sym, struct frame_info *frame,
@@ -881,4 +906,28 @@  extern struct frame_info *skip_tailcall_frames (struct frame_info *frame);
 
 extern struct frame_info *skip_unwritable_frames (struct frame_info *frame);
 
+/* Data for the "set backtrace" settings.  */
+
+struct set_backtrace_options
+{
+  /* Flag to indicate whether backtraces should continue past
+     main.  */
+  int backtrace_past_main = 0;
+
+  /* Flag to indicate whether backtraces should continue past
+     entry.  */
+  int backtrace_past_entry = 0;
+
+  /* Upper bound on the number of backtrace levels.  Note this is not
+     exposed as a command option, because "backtrace" and "frame
+     apply" already have other means to set a frame count limit.  */
+  unsigned int backtrace_limit = UINT_MAX;
+};
+
+/* The corresponding option definitions.  */
+extern const gdb::option::option_def set_backtrace_option_defs[2];
+
+/* The values behind the global "set backtrace ..." settings.  */
+extern set_backtrace_options user_set_backtrace_options;
+
 #endif /* !defined (FRAME_H)  */
diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c
index 0dc0b55bc91..80bc7a1968e 100644
--- a/gdb/mi/mi-cmd-stack.c
+++ b/gdb/mi/mi-cmd-stack.c
@@ -39,7 +39,8 @@ 
 
 enum what_to_list { locals, arguments, all };
 
-static void list_args_or_locals (enum what_to_list what,
+static void list_args_or_locals (const frame_print_options &fp_opts,
+				 enum what_to_list what,
 				 enum print_values values,
 				 struct frame_info *fi,
 				 int skip_unavailable);
@@ -175,7 +176,8 @@  mi_cmd_stack_list_frames (const char *command, char **argv, int argc)
 	  QUIT;
 	  /* Print the location and the address always, even for level 0.
 	     If args is 0, don't print the arguments.  */
-	  print_frame_info (fi, 1, LOC_AND_ADDRESS, 0 /* args */, 0);
+	  print_frame_info (user_frame_print_options,
+			    fi, 1, LOC_AND_ADDRESS, 0 /* args */, 0);
 	}
     }
 }
@@ -275,7 +277,8 @@  mi_cmd_stack_list_locals (const char *command, char **argv, int argc)
       if "--no-frame-filters" has been specified from the command.  */
    if (! frame_filters || raw_arg  || result == EXT_LANG_BT_NO_FILTERS)
      {
-       list_args_or_locals (locals, print_value, frame,
+       list_args_or_locals (user_frame_print_options,
+			    locals, print_value, frame,
 			    skip_unavailable);
      }
 }
@@ -389,7 +392,8 @@  mi_cmd_stack_list_args (const char *command, char **argv, int argc)
 	  QUIT;
 	  ui_out_emit_tuple tuple_emitter (uiout, "frame");
 	  uiout->field_int ("level", i);
-	  list_args_or_locals (arguments, print_values, fi, skip_unavailable);
+	  list_args_or_locals (user_frame_print_options,
+			       arguments, print_values, fi, skip_unavailable);
 	}
     }
 }
@@ -465,7 +469,8 @@  mi_cmd_stack_list_variables (const char *command, char **argv, int argc)
       if "--no-frame-filters" has been specified from the command.  */
    if (! frame_filters || raw_arg  || result == EXT_LANG_BT_NO_FILTERS)
      {
-       list_args_or_locals (all, print_value, frame,
+       list_args_or_locals (user_frame_print_options,
+			    all, print_value, frame,
 			    skip_unavailable);
      }
 }
@@ -557,7 +562,8 @@  list_arg_or_local (const struct frame_arg *arg, enum what_to_list what,
    are available.  */
 
 static void
-list_args_or_locals (enum what_to_list what, enum print_values values,
+list_args_or_locals (const frame_print_options &fp_opts,
+		     enum what_to_list what, enum print_values values,
 		     struct frame_info *fi, int skip_unavailable)
 {
   const struct block *block;
@@ -652,7 +658,7 @@  list_args_or_locals (enum what_to_list what, enum print_values values,
 		    {
 		case PRINT_ALL_VALUES:
 		  if (SYMBOL_IS_ARGUMENT (sym))
-		    read_frame_arg (sym2, fi, &arg, &entryarg);
+		    read_frame_arg (fp_opts, sym2, fi, &arg, &entryarg);
 		  else
 		    read_frame_local (sym2, fi, &arg);
 		    }
@@ -764,5 +770,6 @@  mi_cmd_stack_info_frame (const char *command, char **argv, int argc)
   if (argc > 0)
     error (_("-stack-info-frame: No arguments allowed"));
 
-  print_frame_info (get_selected_frame (NULL), 1, LOC_AND_ADDRESS, 0, 1);
+  print_frame_info (user_frame_print_options,
+		    get_selected_frame (NULL), 1, LOC_AND_ADDRESS, 0, 1);
 }
diff --git a/gdb/python/py-framefilter.c b/gdb/python/py-framefilter.c
index 017ea90f619..95ad410f23f 100644
--- a/gdb/python/py-framefilter.c
+++ b/gdb/python/py-framefilter.c
@@ -481,7 +481,8 @@  enumerate_args (PyObject *iter,
 	      return EXT_LANG_BT_ERROR;
 	    }
 
-	  read_frame_arg (sym, frame, &arg, &entryarg);
+	  read_frame_arg (user_frame_print_options,
+			  sym, frame, &arg, &entryarg);
 
 	  gdb::unique_xmalloc_ptr<char> arg_holder (arg.error);
 	  gdb::unique_xmalloc_ptr<char> entry_holder (entryarg.error);
diff --git a/gdb/stack.c b/gdb/stack.c
index d1b195c84d6..5e878d3c887 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -52,16 +52,22 @@ 
 #include "extension.h"
 #include "observable.h"
 #include "common/def-vector.h"
+#include "cli/cli-option.h"
 
 /* The possible choices of "set print frame-arguments", and the value
    of this setting.  */
 
-static const char *const print_frame_arguments_choices[] =
-  {"all", "scalars", "none", NULL};
-static const char *print_frame_arguments = "scalars";
+const char print_frame_arguments_all[] = "all";
+const char print_frame_arguments_scalars[] = "scalars";
+const char print_frame_arguments_none[] = "none";
 
-/* If non-zero, don't invoke pretty-printers for frame arguments.  */
-static int print_raw_frame_arguments;
+static const char *const print_frame_arguments_choices[] =
+{
+  print_frame_arguments_all,
+  print_frame_arguments_scalars,
+  print_frame_arguments_none,
+  NULL
+};
 
 /* The possible choices of "set print entry-values", and the value
    of this setting.  */
@@ -84,7 +90,85 @@  static const char *const print_entry_values_choices[] =
   print_entry_values_default,
   NULL
 };
-const char *print_entry_values = print_entry_values_default;
+
+/* See frame.h.  */
+frame_print_options user_frame_print_options;
+
+/* Option definitions for some frame-related "set print ..."
+   settings.  */
+
+using boolean_option_def
+  = gdb::option::boolean_option_def<frame_print_options>;
+using enum_option_def
+  = gdb::option::enum_option_def<frame_print_options>;
+
+static const gdb::option::option_def frame_print_option_defs[] = {
+
+  enum_option_def {
+    "entry-values",
+    print_entry_values_choices,
+    [] (frame_print_options *opt) { return &opt->print_entry_values; },
+    NULL, /* show_cmd_cb */
+    N_("Set printing of function arguments at function entry"),
+    N_("Show printing of function arguments at function entry"),
+    N_("GDB can sometimes determine the values of function arguments at entry,\n\
+in addition to their current values.  This option tells GDB whether\n\
+to print the current value, the value at entry (marked as val@entry),\n\
+or both.  Note that one or both of these values may be <optimized out>."),
+  },
+
+  enum_option_def {
+    "frame-arguments",
+    print_frame_arguments_choices,
+    [] (frame_print_options *opt) { return &opt->print_frame_arguments; },
+    NULL, /* show_cmd_cb */
+    N_("Set printing of non-scalar frame arguments"),
+    N_("Show printing of non-scalar frame arguments"),
+    NULL /* help_doc */
+  },
+
+  boolean_option_def {
+    "raw-frame-arguments",
+    [] (frame_print_options *opt) { return &opt->print_raw_frame_arguments; },
+    NULL, /* show_cmd_cb */
+    N_("Set whether to print frame arguments in raw form."),
+    N_("Show whether to print frame arguments in raw form."),
+    N_("If set, frame arguments are printed in raw form, bypassing any\n\
+pretty-printers for that value.")
+  },
+};
+
+/* Options for the "backtrace" command.  */
+
+struct backtrace_cmd_options
+{
+  int full = 0;
+  int no_filters = 0;
+  int hide = 0;
+};
+
+using bt_flag_option_def
+  = gdb::option::flag_option_def<backtrace_cmd_options>;
+
+static const gdb::option::option_def backtrace_command_option_defs[] = {
+  bt_flag_option_def {
+    "full",
+    [] (backtrace_cmd_options *opt) { return &opt->full; },
+    N_("Print values of local variables.")
+  },
+
+  bt_flag_option_def {
+    "no-filters",
+    [] (backtrace_cmd_options *opt) { return &opt->no_filters; },
+    N_("Prohibit frame filters from executing on a backtrace."),
+  },
+
+  bt_flag_option_def {
+    "hide",
+    [] (backtrace_cmd_options *opt) { return &opt->hide; },
+    N_("Causes Python frame filter elided frames to not be printed."),
+  },
+};
 
 /* Prototypes for local functions.  */
 
@@ -93,7 +177,8 @@  static void print_frame_local_vars (struct frame_info *frame,
 				    const char *regexp, const char *t_regexp,
 				    int num_tabs, struct ui_file *stream);
 
-static void print_frame (struct frame_info *frame, int print_level,
+static void print_frame (const frame_print_options &opts,
+			 frame_info *frame, int print_level,
 			 enum print_what print_what,  int print_args,
 			 struct symtab_and_line sal);
 
@@ -177,7 +262,8 @@  print_stack_frame (struct frame_info *frame, int print_level,
 
   try
     {
-      print_frame_info (frame, print_level, print_what, 1 /* print_args */,
+      print_frame_info (user_frame_print_options,
+			frame, print_level, print_what, 1 /* print_args */,
 			set_current_sal);
       if (set_current_sal)
 	set_current_sal_from_frame (frame);
@@ -225,7 +311,8 @@  print_frame_nameless_args (struct frame_info *frame, long start, int num,
    iff it should not be printed accoring to user settings.  */
 
 static void
-print_frame_arg (const struct frame_arg *arg)
+print_frame_arg (const frame_print_options &fp_opts,
+		 const struct frame_arg *arg)
 {
   struct ui_out *uiout = current_uiout;
 
@@ -269,7 +356,7 @@  print_frame_arg (const struct frame_arg *arg)
 	  try
 	    {
 	      const struct language_defn *language;
-	      struct value_print_options opts;
+	      struct value_print_options vp_opts;
 
 	      /* Avoid value_print because it will deref ref parameters.  We
 		 just want to print their addresses.  Print ??? for args whose
@@ -286,14 +373,15 @@  print_frame_arg (const struct frame_arg *arg)
 	      else
 		language = current_language;
 
-	      get_no_prettyformat_print_options (&opts);
-	      opts.deref_ref = 1;
-	      opts.raw = print_raw_frame_arguments;
+	      get_no_prettyformat_print_options (&vp_opts);
+	      vp_opts.deref_ref = 1;
+	      vp_opts.raw = fp_opts.print_raw_frame_arguments;
 
 	      /* True in "summary" mode, false otherwise.  */
-	      opts.summary = !strcmp (print_frame_arguments, "scalars");
+	      vp_opts.summary
+		= fp_opts.print_frame_arguments == print_frame_arguments_scalars;
 
-	      common_val_print (arg->val, &stb, 2, &opts, language);
+	      common_val_print (arg->val, &stb, 2, &vp_opts, language);
 	    }
 	  catch (const gdb_exception_error &except)
 	    {
@@ -333,15 +421,16 @@  read_frame_local (struct symbol *sym, struct frame_info *frame,
    exception.  */
 
 void
-read_frame_arg (struct symbol *sym, struct frame_info *frame,
+read_frame_arg (const frame_print_options &fp_opts,
+		symbol *sym, frame_info *frame,
 	        struct frame_arg *argp, struct frame_arg *entryargp)
 {
   struct value *val = NULL, *entryval = NULL;
   char *val_error = NULL, *entryval_error = NULL;
   int val_equal = 0;
 
-  if (print_entry_values != print_entry_values_only
-      && print_entry_values != print_entry_values_preferred)
+  if (fp_opts.print_entry_values != print_entry_values_only
+      && fp_opts.print_entry_values != print_entry_values_preferred)
     {
       try
 	{
@@ -356,8 +445,8 @@  read_frame_arg (struct symbol *sym, struct frame_info *frame,
 
   if (SYMBOL_COMPUTED_OPS (sym) != NULL
       && SYMBOL_COMPUTED_OPS (sym)->read_variable_at_entry != NULL
-      && print_entry_values != print_entry_values_no
-      && (print_entry_values != print_entry_values_if_needed
+      && fp_opts.print_entry_values != print_entry_values_no
+      && (fp_opts.print_entry_values != print_entry_values_if_needed
 	  || !val || value_optimized_out (val)))
     {
       try
@@ -379,8 +468,8 @@  read_frame_arg (struct symbol *sym, struct frame_info *frame,
       if (entryval != NULL && value_optimized_out (entryval))
 	entryval = NULL;
 
-      if (print_entry_values == print_entry_values_compact
-	  || print_entry_values == print_entry_values_default)
+      if (fp_opts.print_entry_values == print_entry_values_compact
+	  || fp_opts.print_entry_values == print_entry_values_default)
 	{
 	  /* For MI do not try to use print_entry_values_compact for ARGP.  */
 
@@ -463,7 +552,7 @@  read_frame_arg (struct symbol *sym, struct frame_info *frame,
 
   if (entryval == NULL)
     {
-      if (print_entry_values == print_entry_values_preferred)
+      if (fp_opts.print_entry_values == print_entry_values_preferred)
 	{
 	  gdb_assert (val == NULL);
 
@@ -477,18 +566,18 @@  read_frame_arg (struct symbol *sym, struct frame_info *frame,
 	      strcpy (val_error, except.what ());
 	    }
 	}
-      if (print_entry_values == print_entry_values_only
-	  || print_entry_values == print_entry_values_both
-	  || (print_entry_values == print_entry_values_preferred
+      if (fp_opts.print_entry_values == print_entry_values_only
+	  || fp_opts.print_entry_values == print_entry_values_both
+	  || (fp_opts.print_entry_values == print_entry_values_preferred
 	      && (!val || value_optimized_out (val))))
 	{
 	  entryval = allocate_optimized_out_value (SYMBOL_TYPE (sym));
 	  entryval_error = NULL;
 	}
     }
-  if ((print_entry_values == print_entry_values_compact
-       || print_entry_values == print_entry_values_if_needed
-       || print_entry_values == print_entry_values_preferred)
+  if ((fp_opts.print_entry_values == print_entry_values_compact
+       || fp_opts.print_entry_values == print_entry_values_if_needed
+       || fp_opts.print_entry_values == print_entry_values_preferred)
       && (!val || value_optimized_out (val)) && entryval != NULL)
     {
       val = NULL;
@@ -500,8 +589,9 @@  read_frame_arg (struct symbol *sym, struct frame_info *frame,
   argp->error = val_error ? xstrdup (val_error) : NULL;
   if (!val && !val_error)
     argp->entry_kind = print_entry_values_only;
-  else if ((print_entry_values == print_entry_values_compact
-	   || print_entry_values == print_entry_values_default) && val_equal)
+  else if ((fp_opts.print_entry_values == print_entry_values_compact
+	   || fp_opts.print_entry_values == print_entry_values_default)
+	   && val_equal)
     {
       argp->entry_kind = print_entry_values_compact;
       gdb_assert (!current_uiout->is_mi_like_p ());
@@ -528,7 +618,8 @@  read_frame_arg (struct symbol *sym, struct frame_info *frame,
    ints of arguments according to the stack frame".  */
 
 static void
-print_frame_args (struct symbol *func, struct frame_info *frame,
+print_frame_args (const frame_print_options &fp_opts,
+		  struct symbol *func, struct frame_info *frame,
 		  int num, struct ui_file *stream)
 {
   struct ui_out *uiout = current_uiout;
@@ -540,7 +631,8 @@  print_frame_args (struct symbol *func, struct frame_info *frame,
   /* Number of ints of arguments that we have printed so far.  */
   int args_printed = 0;
   /* True if we should print arguments, false otherwise.  */
-  int print_args = strcmp (print_frame_arguments, "none");
+  bool print_args
+    = fp_opts.print_frame_arguments != print_frame_arguments_none;
 
   if (func)
     {
@@ -674,10 +766,10 @@  print_frame_args (struct symbol *func, struct frame_info *frame,
 	      entryarg.entry_kind = print_entry_values_no;
 	    }
 	  else
-	    read_frame_arg (sym, frame, &arg, &entryarg);
+	    read_frame_arg (fp_opts, sym, frame, &arg, &entryarg);
 
 	  if (arg.entry_kind != print_entry_values_only)
-	    print_frame_arg (&arg);
+	    print_frame_arg (fp_opts, &arg);
 
 	  if (entryarg.entry_kind != print_entry_values_no)
 	    {
@@ -687,7 +779,7 @@  print_frame_args (struct symbol *func, struct frame_info *frame,
 		  uiout->wrap_hint ("    ");
 		}
 
-	      print_frame_arg (&entryarg);
+	      print_frame_arg (fp_opts, &entryarg);
 	    }
 
 	  xfree (arg.error);
@@ -778,7 +870,8 @@  do_gdb_disassembly (struct gdbarch *gdbarch,
    messages.  */
 
 void
-print_frame_info (struct frame_info *frame, int print_level,
+print_frame_info (const frame_print_options &fp_opts,
+		  frame_info *frame, int print_level,
 		  enum print_what print_what, int print_args,
 		  int set_current_sal)
 {
@@ -855,7 +948,7 @@  print_frame_info (struct frame_info *frame, int print_level,
 		    || print_what == SRC_AND_LOC);
 
   if (location_print || !sal.symtab)
-    print_frame (frame, print_level, print_what, print_args, sal);
+    print_frame (fp_opts, frame, print_level, print_what, print_args, sal);
 
   source_print = (print_what == SRC_LINE || print_what == SRC_AND_LOC);
 
@@ -1128,7 +1221,8 @@  find_frame_funname (struct frame_info *frame, enum language *funlang,
 }
 
 static void
-print_frame (struct frame_info *frame, int print_level,
+print_frame (const frame_print_options &fp_opts,
+	     frame_info *frame, int print_level,
 	     enum print_what print_what, int print_args,
 	     struct symtab_and_line sal)
 {
@@ -1198,7 +1292,7 @@  print_frame (struct frame_info *frame, int print_level,
 	  ui_out_emit_list list_emitter (uiout, "args");
 	  try
 	    {
-	      print_frame_args (func, frame, numargs, gdb_stdout);
+	      print_frame_args (fp_opts, func, frame, numargs, gdb_stdout);
 	    }
 	  catch (const gdb_exception_error &e)
 	    {
@@ -1477,7 +1571,8 @@  info_frame_command_core (struct frame_info *fi, bool selected_frame_p)
 	    else
 	      printf_filtered (" %d args: ", numargs);
 	  }
-	print_frame_args (func, fi, numargs, gdb_stdout);
+	print_frame_args (user_frame_print_options,
+			  func, fi, numargs, gdb_stdout);
 	puts_filtered ("\n");
       }
   }
@@ -1791,8 +1886,10 @@  static frame_command_helper <select_frame_command_core> select_frame_cmd;
    frames.  */
 
 static void
-backtrace_command_1 (const char *count_exp, frame_filter_flags flags,
-		     int no_filters, int from_tty)
+backtrace_command_1 (const frame_print_options &fp_opts,
+		     const backtrace_cmd_options &bt_opts,
+		     const char *count_exp, int from_tty)
+
 {
   struct frame_info *fi;
   int count;
@@ -1821,7 +1918,14 @@  backtrace_command_1 (const char *count_exp, frame_filter_flags flags,
       count = -1;
     }
 
-  if (! no_filters)
+  frame_filter_flags flags = 0;
+
+  if (bt_opts.full)
+    flags |= PRINT_LOCALS;
+  if (bt_opts.hide)
+    flags |= PRINT_HIDE;
+
+  if (!bt_opts.no_filters)
     {
       enum ext_lang_frame_args arg_type;
 
@@ -1829,9 +1933,9 @@  backtrace_command_1 (const char *count_exp, frame_filter_flags flags,
       if (from_tty)
 	flags |= PRINT_MORE_FRAMES;
 
-      if (!strcmp (print_frame_arguments, "scalars"))
+      if (fp_opts.print_frame_arguments == print_frame_arguments_scalars)
 	arg_type = CLI_SCALAR_VALUES;
-      else if (!strcmp (print_frame_arguments, "all"))
+      else if (fp_opts.print_frame_arguments == print_frame_arguments_all)
 	arg_type = CLI_ALL_VALUES;
       else
 	arg_type = NO_VALUES;
@@ -1842,8 +1946,8 @@  backtrace_command_1 (const char *count_exp, frame_filter_flags flags,
     }
 
   /* Run the inbuilt backtrace if there are no filters registered, or
-     "no-filters" has been specified from the command.  */
-  if (no_filters ||  result == EXT_LANG_BT_NO_FILTERS)
+     "-no-filters" has been specified from the command.  */
+  if (bt_opts.no_filters || result == EXT_LANG_BT_NO_FILTERS)
     {
       struct frame_info *trailing;
 
@@ -1869,7 +1973,7 @@  backtrace_command_1 (const char *count_exp, frame_filter_flags flags,
 	     hand, perhaps the code does or could be fixed to make sure
 	     the frame->prev field gets set to NULL in that case).  */
 
-	  print_frame_info (fi, 1, LOCATION, 1, 0);
+	  print_frame_info (fp_opts, fi, 1, LOCATION, 1, 0);
 	  if ((flags & PRINT_LOCALS) != 0)
 	    {
 	      struct frame_id frame_id = get_frame_id (fi);
@@ -1908,17 +2012,38 @@  backtrace_command_1 (const char *count_exp, frame_filter_flags flags,
     }
 }
 
+/* Create an option_def_group array grouping all the "backtrace"
+   options, with FP_OPTS, BT_CMD_OPT, SET_BT_OPTS as contexts.  */
+
+static inline std::array<gdb::option::option_def_group, 3>
+make_backtrace_options_def_group (frame_print_options *fp_opts,
+				  backtrace_cmd_options *bt_cmd_opts,
+				  set_backtrace_options *set_bt_opts)
+{
+  return {{
+    { {frame_print_option_defs}, fp_opts },
+    { {set_backtrace_option_defs}, set_bt_opts },
+    { {backtrace_command_option_defs}, bt_cmd_opts }
+  }};
+}
+
 static void
 backtrace_command (const char *arg, int from_tty)
 {
-  bool filters = true;
-  frame_filter_flags flags = 0;
+  frame_print_options fp_opts = user_frame_print_options;
+  backtrace_cmd_options bt_cmd_opts;
+  set_backtrace_options set_bt_opts = user_set_backtrace_options;
 
-  if (arg)
-    {
-      bool done = false;
+  auto grp
+    = make_backtrace_options_def_group (&fp_opts, &bt_cmd_opts, &set_bt_opts);
+  gdb::option::process_options
+    (&arg, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, grp);
 
-      while (!done)
+  /* Parse non-'-'-prefixed qualifiers, for backwards
+     compatibility.  */
+  if (arg != NULL)
+    {
+      while (true)
 	{
 	  const char *save_arg = arg;
 	  std::string this_arg = extract_arg (&arg);
@@ -1927,16 +2052,16 @@  backtrace_command (const char *arg, int from_tty)
 	    break;
 
 	  if (subset_compare (this_arg.c_str (), "no-filters"))
-	    filters = false;
+	    bt_cmd_opts.no_filters = true;
 	  else if (subset_compare (this_arg.c_str (), "full"))
-	    flags |= PRINT_LOCALS;
+	    bt_cmd_opts.full = true;
 	  else if (subset_compare (this_arg.c_str (), "hide"))
-	    flags |= PRINT_HIDE;
+	    bt_cmd_opts.hide = true;
 	  else
 	    {
 	      /* Not a recognized argument, so stop.  */
 	      arg = save_arg;
-	      done = true;
+	      break;
 	    }
 	}
 
@@ -1944,7 +2069,29 @@  backtrace_command (const char *arg, int from_tty)
 	arg = NULL;
     }
 
-  backtrace_command_1 (arg, flags, !filters /* no frame-filters */, from_tty);
+  /* These options are handled quite deep in the unwind machinery, so
+     we get to pass them down by swapping globals.  */
+  scoped_restore restore_set_backtrace_options
+    = make_scoped_restore (&user_set_backtrace_options, set_bt_opts);
+
+  backtrace_command_1 (fp_opts, bt_cmd_opts, arg, from_tty);
+}
+
+/* Completer for the "backtrace" command.  */
+
+static void
+backtrace_command_completer (struct cmd_list_element *ignore,
+			     completion_tracker &tracker,
+			     const char *text, const char */*word*/)
+{
+  const auto group
+    = make_backtrace_options_def_group (nullptr, nullptr, nullptr);
+  if (gdb::option::complete_options
+      (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group))
+    return;
+
+  const char *word = advance_to_expression_complete_word_point (tracker, text);
+  expression_completer (ignore, tracker, text, word);
 }
 
 /* Iterate over the local variables of a block B, calling CB with
@@ -3030,13 +3177,30 @@  Usage: select-frame level LEVEL"),
 			 &select_frame_cmd_list,
 			 &cli_suppress_notification.user_selected_context);
 
-  add_com ("backtrace", class_stack, backtrace_command, _("\
+  const auto backtrace_opts
+    = make_backtrace_options_def_group (nullptr, nullptr, nullptr);
+
+  static std::string backtrace_help
+    = gdb::option::build_help (N_("\
 Print backtrace of all stack frames, or innermost COUNT frames.\n\
-Usage: backtrace [QUALIFIERS]... [COUNT]\n\
-With a negative argument, print outermost -COUNT frames.\n\
-Use of the 'full' qualifier also prints the values of the local variables.\n\
-Use of the 'no-filters' qualifier prohibits frame filters from executing\n\
-on this backtrace."));
+Usage: backtrace [OPTION]... [QUALIFIER]... [COUNT | -COUNT]\n\
+\n\
+Options:\n\
+%OPTIONS%\
+For backward compatibility, the following qualifiers are supported:\n\
+\n\
+   full       - same as -full option.\n\
+   no-filters - same as -no-filters option.\n\
+   hide       - same as -hide.\n\
+\n\
+With a negative COUNT, print outermost -COUNT frames."),
+			       backtrace_opts);
+
+  cmd_list_element *c = add_com ("backtrace", class_stack,
+				 backtrace_command,
+				 backtrace_help.c_str ());
+  set_cmd_completer_handle_brkchars (c, backtrace_command_completer);
+
   add_com_alias ("bt", "backtrace", class_stack, 0);
 
   add_com_alias ("where", "backtrace", class_alias, 0);
@@ -3100,33 +3264,20 @@  Prints the argument variables of the current stack frame.\n"),
 Select the stack frame that contains NAME.\n\
 Usage: func NAME"));
 
-  add_setshow_enum_cmd ("frame-arguments", class_stack,
-			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);
-
   /* Install "set print raw frame-arguments", a deprecated spelling of
      "set print raw-frame-arguments".  */
-  cmd = add_setshow_boolean_cmd ("frame-arguments", no_class,
-				 &print_raw_frame_arguments, _("\
+  cmd = add_setshow_boolean_cmd
+    ("frame-arguments", no_class,
+     &user_frame_print_options.print_raw_frame_arguments,
+     _("\
 Set whether to print frame arguments in raw form."), _("\
 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);
+     NULL, NULL,
+     &setprintrawlist, &showprintrawlist);
   deprecate_cmd (cmd, "set print raw-frame-arguments");
 
-  add_setshow_boolean_cmd ("raw-frame-arguments", no_class,
-			   &print_raw_frame_arguments, _("\
-Set whether to print frame arguments in raw form."), _("\
-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,
-			   &setprintlist, &showprintlist);
-
   add_setshow_auto_boolean_cmd ("disassemble-next-line", class_stack,
 			        &disassemble_next_line, _("\
 Set whether to disassemble next source line or insn when execution stops."),
@@ -3147,16 +3298,7 @@  source line."),
 			        &setlist, &showlist);
   disassemble_next_line = AUTO_BOOLEAN_FALSE;
 
-  add_setshow_enum_cmd ("entry-values", class_stack,
-			print_entry_values_choices, &print_entry_values,
-			_("Set printing of function arguments at function "
-			  "entry"),
-			_("Show printing of function arguments at function "
-			  "entry"),
-			_("\
-GDB can sometimes determine the values of function arguments at entry,\n\
-in addition to their current values.  This option tells GDB whether\n\
-to print the current value, the value at entry (marked as val@entry),\n\
-or both.  Note that one or both of these values may be <optimized out>."),
-			NULL, NULL, &setprintlist, &showprintlist);
+  gdb::option::add_setshow_cmds_for_options
+    (class_stack, &user_frame_print_options,
+     frame_print_option_defs, &setprintlist, &showprintlist);
 }
diff --git a/gdb/testsuite/gdb.base/options.exp b/gdb/testsuite/gdb.base/options.exp
index 3b4e7ee189f..17573460b4d 100644
--- a/gdb/testsuite/gdb.base/options.exp
+++ b/gdb/testsuite/gdb.base/options.exp
@@ -20,8 +20,11 @@ 
 # The test uses the "maintenance test-options" subcommands to exercise
 # TAB-completion and option processing.
 #
-# It also tests option integration in various commands, including
-# "print" and "compile print".
+# It also tests option integration in various commands, including:
+#
+#  - print
+#  - compile print
+#  - backtrace
 
 load_lib completion-support.exp
 
@@ -231,6 +234,56 @@  proc_with_prefix test-print {{prefix ""}} {
 	"Left operand of assignment is not an lvalue\\."
 }
 
+# Basic option-machinery + "backtrace" command integration tests.
+proc_with_prefix test-backtrace {} {
+    clean_restart
+
+    test_gdb_complete_unique "backtrace" "backtrace"
+    test_gdb_complete_none "backtrace "
+
+    gdb_test "backtrace -" "Ambiguous option at: -"
+    gdb_test "backtrace --" "No stack\\."
+    gdb_test "backtrace -- -" "No stack\\."
+
+    test_gdb_complete_multiple "backtrace " "-" "" {
+	"-entry-values"
+	"-frame-arguments"
+	"-full"
+	"-hide"
+	"-no-filters"
+	"-past-entry"
+	"-past-main"
+	"-raw-frame-arguments"
+    }
+
+    global binfile
+    clean_restart $binfile
+
+    if ![runto_main] {
+	fail "cannot run to main"
+	return
+    }
+
+    # COUNT in "backtrace COUNT" is parsed as an expression.  Check
+    # that we complete expressions.
+
+    test_gdb_complete_unique \
+	"backtrace xxx" \
+	"backtrace xxx1"
+
+    test_gdb_complete_unique \
+	"backtrace -xxx" \
+	"backtrace -xxx1"
+
+    test_gdb_complete_unique \
+	"backtrace 1 + xxx" \
+	"backtrace 1 + xxx1"
+
+    test_gdb_complete_unique \
+	"backtrace (1 + xxx" \
+	"backtrace (1 + xxx1"
+}
+
 # Miscellaneous tests.
 proc_with_prefix test-misc {variant} {
     global all_options
@@ -674,3 +727,6 @@  test-print ""
 if ![skip_compile_feature_tests] {
     test-print "compile "
 }
+
+# Basic "backtrace" integration tests.
+test-backtrace