public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCHv2 1/2] gdb: Split func_command into two parts.
  2018-05-08 16:58 [PATCHv2 0/2] Changes to frame selection Andrew Burgess
@ 2018-05-08 16:58 ` Andrew Burgess
  2018-05-18 19:57   ` Pedro Alves
  2018-05-08 16:59 ` [PATCHv2 2/2] gdb: Change how frames are selected for 'frame' and 'info frame' Andrew Burgess
  1 sibling, 1 reply; 35+ messages in thread
From: Andrew Burgess @ 2018-05-08 16:58 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

The func_command function is used to emulate the dbx 'func' command.
However, finding a stack frame based on function name might be a useful
feature, and so the core of func_command is now split out into a
separate function.

gdb/ChangeLog:

        * stack.c (select_and_print_frame): Delete.
        (func_command): Most content moved into new function
        find_frame_for_function, use new function, print result, add
        function comment.
	(find_frame_for_function): New function, now returns a result.
---
 gdb/ChangeLog |  8 ++++++++
 gdb/stack.c   | 46 ++++++++++++++++++++++++++++------------------
 2 files changed, 36 insertions(+), 18 deletions(-)

diff --git a/gdb/stack.c b/gdb/stack.c
index ecf1ee83793..5e99a42053c 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -2157,16 +2157,6 @@ info_args_command (const char *ignore, int from_tty)
   print_frame_arg_vars (get_selected_frame (_("No frame selected.")),
 			gdb_stdout);
 }
-
-/* Select frame FRAME.  Also print the stack frame and show the source
-   if this is the tui version.  */
-static void
-select_and_print_frame (struct frame_info *frame)
-{
-  select_frame (frame);
-  if (frame)
-    print_stack_frame (frame, 1, SRC_AND_LOC, 1);
-}
 \f
 /* Return the symbol-block in which the selected frame is executing.
    Can return zero under various legitimate circumstances.
@@ -2460,19 +2450,19 @@ struct function_bounds
   CORE_ADDR low, high;
 };
 
-static void
-func_command (const char *arg, int from_tty)
+static struct frame_info *
+find_frame_for_function (const char *function_name)
 {
   struct frame_info *frame;
   int found = 0;
   int level = 1;
 
-  if (arg == NULL)
-    return;
+  gdb_assert (function_name != NULL);
 
   frame = get_current_frame ();
   std::vector<symtab_and_line> sals
-    = decode_line_with_current_source (arg, DECODE_LINE_FUNFIRSTLINE);
+    = decode_line_with_current_source (function_name,
+				       DECODE_LINE_FUNFIRSTLINE);
   gdb::def_vector<function_bounds> func_bounds (sals.size ());
   for (size_t i = 0; (i < sals.size () && !found); i++)
     {
@@ -2501,9 +2491,29 @@ func_command (const char *arg, int from_tty)
   while (!found && level == 0);
 
   if (!found)
-    printf_filtered (_("'%s' not within current stack frame.\n"), arg);
-  else if (frame != get_selected_frame (NULL))
-    select_and_print_frame (frame);
+    frame = NULL;
+
+  return frame;
+}
+
+/* Implements the dbx 'func' command.  */
+
+static void
+func_command (const char *arg, int from_tty)
+{
+  struct frame_info *frame;
+
+  if (arg == NULL)
+    return;
+
+  frame = find_frame_for_function (arg);
+  if (frame == NULL)
+    error (_("'%s' not within current stack frame.\n"), arg);
+  if (frame != get_selected_frame (NULL))
+    {
+      select_frame (frame);
+      print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
+    }
 }
 
 void
-- 
2.14.3

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCHv2 0/2] Changes to frame selection.
@ 2018-05-08 16:58 Andrew Burgess
  2018-05-08 16:58 ` [PATCHv2 1/2] gdb: Split func_command into two parts Andrew Burgess
  2018-05-08 16:59 ` [PATCHv2 2/2] gdb: Change how frames are selected for 'frame' and 'info frame' Andrew Burgess
  0 siblings, 2 replies; 35+ messages in thread
From: Andrew Burgess @ 2018-05-08 16:58 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This is an update of this patch series:

  https://sourceware.org/ml/gdb-patches/2015-09/msg00248.html

In summary this patch means that instead of simply:

  (gdb) frame NUMBER|ADDRESS-OF-EXISTING-FRAME|ADDRESS-OF-NEW-FRAME

we now have:

  (gdb) frame NUMBER
  (gdb) frame level NUMBER
  (gdb) frame address ADDRESS-OF-EXISTING-FRAME
  (gdb) frame create ADDRESS-OF-NEW-FRAME
  (gdb) frame function NAME

The same flexibility is added to 'select-frame' and 'info frame'.

From the original patch series, some of the patches are no longer
needed (due to other upstream changes) and some of the patches were
already approved and merged.  It is only the last two patches that are
needed, and these are presented here.

Patch #1 is preperation work, and is just a rebase of what I had
before.  This patch was actually approved previously, but I didn't
merge it as without the second patch this doesn't make much sense.

Patch #2 is the interesting stuff, and this has been rebased, and
updated inline with the feedback I originally got, specifically, the
frame specification is now implemented using sub-commands, rather than
using a single parse function.

I'm aware of this patch series:

   https://sourceware.org/ml/gdb-patches/2018-05/msg00152.html

That also makes changes to the 'frame' command, and though there will
obviously be textual conflicts, I don't think there's any
functionality conflicts.

--

Andrew Burgess (2):
  gdb: Split func_command into two parts.
  gdb: Change how frames are selected for 'frame' and 'info frame'.

 gdb/ChangeLog                               |  40 ++
 gdb/NEWS                                    |  29 ++
 gdb/cli/cli-decode.c                        |  44 ++-
 gdb/command.h                               |  14 +
 gdb/doc/ChangeLog                           |   8 +
 gdb/doc/gdb.texinfo                         |  91 +++--
 gdb/mi/mi-cmd-stack.c                       |   4 +-
 gdb/stack.c                                 | 564 +++++++++++++++++++---------
 gdb/stack.h                                 |   2 +-
 gdb/testsuite/ChangeLog                     |   7 +
 gdb/testsuite/gdb.base/frame-selection.c    |  52 +++
 gdb/testsuite/gdb.base/frame-selection.exp  | 156 ++++++++
 gdb/testsuite/gdb.mi/mi-frame-selection.c   |  34 ++
 gdb/testsuite/gdb.mi/mi-frame-selection.exp |  90 +++++
 14 files changed, 920 insertions(+), 215 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/frame-selection.c
 create mode 100644 gdb/testsuite/gdb.base/frame-selection.exp
 create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.c
 create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.exp

-- 
2.14.3

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCHv2 2/2] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-05-08 16:58 [PATCHv2 0/2] Changes to frame selection Andrew Burgess
  2018-05-08 16:58 ` [PATCHv2 1/2] gdb: Split func_command into two parts Andrew Burgess
@ 2018-05-08 16:59 ` Andrew Burgess
  2018-05-11 15:44   ` Eli Zaretskii
  2018-06-07 16:19   ` [PATCHv3] " Andrew Burgess
  1 sibling, 2 replies; 35+ messages in thread
From: Andrew Burgess @ 2018-05-08 16:59 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

The 'frame' command, and thanks to code reuse the 'info frame' and
'select-frame' commands, currently have an overloaded mechanism for
selecting a frame.

These commands take one or two parameters, if it's one parameter then
we first try to use the parameter as an integer to select a frame by
level (or depth in the stack).  If that fails then we treat the
parameter as an address and try to select a stack frame by
stack-address.  If we still have not selected a stack frame, or we
initially had two parameters, then we create a new stack frame and
select that.

The result of this is that a typo by the user, entering the wrong stack
frame level for example, can result in a brand new frame being created
rather than an error.

The purpose of this commit is to remove this overloading, while still
offering the same functionality through some new sub-commands.  By
making the default behaviour of 'frame' (and friends) be to select a
stack frame by level number, it is hoped that enough
backwards-compatibility is maintained that users will not be overly
inconvenienced.

The 'frame', 'select-frame', and 'info frame' commands now all take a
frame specification string as an argument, this string can be any of the
following:

  (1) An integer.  This is treated as a frame number.  If a frame for
  that number does not exist then the user gets an error.

  (2) A string like 'level <NUMBER>', where <NUMBER> is a frame number
  as in option (1) above.

  (3) A string like 'address <ADDRESS>', where <ADDRESS> is a
  stack-frame address.  If there is no frame for this address then the
  user gets an error.

  (4) A string like 'function <NAME>', where <NAME> is a function name,
  the inner most frame for function <NAME> is selected.  If there is no
  frame for function <NAME> then the user gets an error.

  (5) A string like 'create <STACK-ADDRESS>', this creates a new frame
  with stack address <STACK-ADDRESS>.

  (6) A string like 'create <STACK-ADDRESS> <PC-ADDRESS>', this creates
  a new frame with stack address <STACK-ADDRESS> an the pc <PC-ADDRESS>.

This change assumes that the most common use of the commands like
'frame' is to select a frame by frame level number, it is for this
reason that this is the behaviour that is kept for backwards
compatibility.  Any of the alternative behaviours, which are assumed to
be less used, now require a change in user behaviour.

The MI command '-stack-select-frame' has also changed in the same way.
The default behaviour is to select a frame by level number, but the
other selection methods are also available.

gdb/ChangeLog:

	(NEWS): Mention changes to frame related commands.
	* cli/cli-decode.c (add_cmd_suppress_notification): New function.
	(add_prefix_cmd_suppress_notification): New function.
	(add_com_suppress_notification): Call
	add_cmd_suppress_notification.
	* command.h (add_cmd_suppress_notification): Declare.
	(add_prefix_cmd_suppress_notification): Declare.
	* mi/mi-cmd-stack.c (mi_cmd_stack_select_frame): Use new function
	select_frame_from_spec.
	* stack.c (find_frame_for_function): Add declaration.
	(find_frame_for_address): New function.
	(parse_frame_specification): Delete function.
	(frame_selection_by_function_completer): New function.
	(info_frame_command): Rename to...
	(info_frame_command_core): ...this, and update parameter types.
	(select_frame_command): Rename to...
	(select_frame_command_core): ...this, and update parameter types.
	(frame_command): Rename to...
	(frame_command_core): ...this, and update parameter types.
	(class frame_command_helper): New class to wrap implementations of
	frame related sub-commands.
	(select_frame_from_spec): New function.
	(frame_cmd_list): New global for sub-commands.
	(select_frame_cmd_list): New global for sub-commands.
	(info_frame_cmd_list): New global for sub-commands.
	(_initialize_stack): Register sub-commands for 'frame',
	'select-frame', and 'info frame'.
	* stack.h (select_frame_command): Delete declarationn.
	(select_frame_from_spec): Declare new function.

gdb/doc/ChangeLog:

	* gdb.texinfo (Selection): Rewrite documentation for 'frame' and
	'select-frame' commands.
	(Frame Info): Rewrite documentation for 'info frame' command.
	(GDB/MI Stack Manipulation): Update description of
	'-stack-select-frame'.

gdb/testsuite/ChangeLog:

	* gdb.base/frame-selection.exp: New file.
	* gdb.base/frame-selection.c: New file.
	* gdb.mi/mi-frame-selection.exp: New file.
	* gdb.mi/mi-frame-selection.c: New file.
---
 gdb/ChangeLog                               |  32 ++
 gdb/NEWS                                    |  29 ++
 gdb/cli/cli-decode.c                        |  44 ++-
 gdb/command.h                               |  14 +
 gdb/doc/ChangeLog                           |   8 +
 gdb/doc/gdb.texinfo                         |  91 +++--
 gdb/mi/mi-cmd-stack.c                       |   4 +-
 gdb/stack.c                                 | 518 +++++++++++++++++++---------
 gdb/stack.h                                 |   2 +-
 gdb/testsuite/ChangeLog                     |   7 +
 gdb/testsuite/gdb.base/frame-selection.c    |  52 +++
 gdb/testsuite/gdb.base/frame-selection.exp  | 156 +++++++++
 gdb/testsuite/gdb.mi/mi-frame-selection.c   |  34 ++
 gdb/testsuite/gdb.mi/mi-frame-selection.exp |  90 +++++
 14 files changed, 884 insertions(+), 197 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/frame-selection.c
 create mode 100644 gdb/testsuite/gdb.base/frame-selection.exp
 create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.c
 create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index 46f6635dda0..fe91887cde4 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -12,6 +12,35 @@
 * C expressions can now use _Alignof, and C++ expressions can now use
   alignof.
 
+* Changes to the "frame", "select-frame", and "info frame" CLI
+  commands, as well as the "-stack-select-frame" MI command.
+  Selecting frames by number remains unchanged, however, selecting
+  frames by stack-address, or creating new frames now requires the use
+  of a sub-command.  Various sub-commands now exist for the various
+  methods of selecting a frame; level, address, function, and create.
+  As an example, here are the variants of "frame" that are now available:
+  - frame <number>
+  - frame level <number>
+    Both of these select frame at level <number>.
+  - frame address <stack-address>
+    Select frame for <stack-address>.
+  - frame function <name>
+    Select inner most frame for function <name>.
+  - frame create <stack-address>
+    Create a frame for <stack-address>.
+  - frame create <stack-address> <pc-address>
+    Create a frame for <stack-address> <pc-address>.
+  There are similar variants for the "select-frame" and "info frame"
+  commands.  The MI command "-stack-select-frame" places the
+  sub-command name after the command name as a keyword, the available
+  variants are:
+  -stack-select-frame <number>
+  -stack-select-frame level <number>
+  -stack-select-frame address <stack-address>
+  -stack-select-frame function <name>
+  -stack-select-frame create <stack-address>
+  -stack-select-frame create <stack-address> <pc-address>
+
 * New commands
 
 set debug fbsd-nat
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 0afe36aa4e1..3ec20346b08 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -278,6 +278,23 @@ add_cmd (const char *name, enum command_class theclass,
   return result;
 }
 
+/* Add an element with a suppress notification to the LIST of commands.  */
+
+struct cmd_list_element *
+add_cmd_suppress_notification (const char *name, enum command_class theclass,
+			       cmd_const_cfunc_ftype *fun, const char *doc,
+			       struct cmd_list_element **list,
+			       int *suppress_notification)
+{
+  struct cmd_list_element *element;
+
+  element = add_cmd (name, theclass, fun, doc, list);
+  element->suppress_notification = suppress_notification;
+
+  return element;
+}
+
+
 /* Deprecates a command CMD.
    REPLACEMENT is the name of the command which should be used in
    place of this command, or NULL if no such command exists.
@@ -389,6 +406,25 @@ add_prefix_cmd (const char *name, enum command_class theclass,
   return c;
 }
 
+/* Like ADD_PREFIX_CMD but sets the suppress_notification pointer on the
+   new command list element.  */
+
+struct cmd_list_element *
+add_prefix_cmd_suppress_notification
+               (const char *name, enum command_class theclass,
+		cmd_const_cfunc_ftype *fun,
+		const char *doc, struct cmd_list_element **prefixlist,
+		const char *prefixname, int allow_unknown,
+		struct cmd_list_element **list,
+		int *suppress_notification)
+{
+  struct cmd_list_element *element
+    = add_prefix_cmd (name, theclass, fun, doc, prefixlist,
+		      prefixname, allow_unknown, list);
+  element->suppress_notification = suppress_notification;
+  return element;
+}
+
 /* Like add_prefix_cmd but sets the abbrev_flag on the new command.  */
 
 struct cmd_list_element *
@@ -922,12 +958,8 @@ add_com_suppress_notification (const char *name, enum command_class theclass,
 			       cmd_const_cfunc_ftype *fun, const char *doc,
 			       int *suppress_notification)
 {
-  struct cmd_list_element *element;
-
-  element = add_cmd (name, theclass, fun, doc, &cmdlist);
-  element->suppress_notification = suppress_notification;
-
-  return element;
+  return add_cmd_suppress_notification (name, theclass, fun, doc,
+					&cmdlist, suppress_notification);
 }
 
 /* Recursively walk the commandlist structures, and print out the
diff --git a/gdb/command.h b/gdb/command.h
index 3dde2475cb1..e3d55c2dcba 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -148,6 +148,12 @@ extern struct cmd_list_element *add_cmd (const char *, enum command_class,
 					 const char *,
 					 struct cmd_list_element **);
 
+extern struct cmd_list_element *add_cmd_suppress_notification
+			(const char *name, enum command_class theclass,
+			 cmd_const_cfunc_ftype *fun, const char *doc,
+			 struct cmd_list_element **list,
+			 int *suppress_notification);
+
 extern struct cmd_list_element *add_alias_cmd (const char *, const char *,
 					       enum command_class, int,
 					       struct cmd_list_element **);
@@ -165,6 +171,14 @@ extern struct cmd_list_element *add_prefix_cmd (const char *, enum command_class
 						const char *, int,
 						struct cmd_list_element **);
 
+extern struct cmd_list_element *add_prefix_cmd_suppress_notification
+			(const char *name, enum command_class theclass,
+			 cmd_const_cfunc_ftype *fun,
+			 const char *doc, struct cmd_list_element **prefixlist,
+			 const char *prefixname, int allow_unknown,
+			 struct cmd_list_element **list,
+			 int *suppress_notification);
+
 extern struct cmd_list_element *add_abbrev_prefix_cmd (const char *,
 						       enum command_class,
 						       cmd_const_cfunc_ftype *fun,
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 28f083f96e3..61471d32e51 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -7573,21 +7573,58 @@
 @table @code
 @kindex frame@r{, selecting}
 @kindex f @r{(@code{frame})}
-@item frame @var{n}
-@itemx f @var{n}
-Select frame number @var{n}.  Recall that frame zero is the innermost
-(currently executing) frame, frame one is the frame that called the
-innermost one, and so on.  The highest-numbered frame is the one for
-@code{main}.
+@item frame @r{[} @var{frame-selection-spec} @r{]}
+@item f @r{[} @var{frame-selection-spec} @r{]}
+The @command{frame} command allows different stack frames to be
+selected.  The @var{frame-selection-spec} can be any of the following:
+
+@table @code
+@kindex frame level
+@item @var{num}
+@item level @var{num}
+Select frame number @var{num}.  Recall that frame zero is the
+innermost (currently executing) frame, frame one is the frame that
+called the innermost one, and so on.  The highest-numbered frame is
+the one for @code{main}.
+
+As this is the most common method of navigating the frame stack then
+the string @command{level} can be dropped, the following two commands
+are equivalent:
+
+@smallexample
+(@value{GDBP}) frame 3
+(@value{GDBP}) frame level 3
+@end smallexample
 
-@item frame @var{stack-addr} [ @var{pc-addr} ]
-@itemx f @var{stack-addr} [ @var{pc-addr} ]
-Select the frame at address @var{stack-addr}.  This is useful mainly if the
-chaining of stack frames has been damaged by a bug, making it
-impossible for @value{GDBN} to assign numbers properly to all frames.  In
-addition, this can be useful when your program has multiple stacks and
-switches between them.  The optional @var{pc-addr} can also be given to
-specify the value of PC for the stack frame.
+@kindex frame address
+@item address @var{stack-address}
+Select the frame with stack address @var{stack-address}.  Recall that
+each stack frame has a stack frame address, which roughly corresponds
+to the location on the stack where the stack frame local variables are
+stored.
+
+@kindex frame function
+@item function @var{function-name}
+Select the stack frame for function @var{function-name}.  If there are
+multiple stack frames for function @var{function-name} then the inner
+most stack frame is selected.
+
+@kindex frame create
+@item create @var{stack-address} @r{[} @var{pc-addr} @r{]}
+Create and then select a new frame at stack address @var{stack-addr}.
+This is useful mainly if the chaining of stack frames has been damaged
+by a bug, making it impossible for @value{GDBN} to assign numbers
+properly to all frames.  In addition, this can be useful when your
+program has multiple stacks and switches between them.  The optional
+@var{pc-addr} can also be given to specify the value of PC for the
+stack frame.
+
+When a frame has been created and selected uing @command{frame create}
+then you can always return to the original stack using one of the
+previous stack frame selection instructions, for example
+@command{frame level 0}.
+
+@end table
 
 @kindex up
 @item up @var{n}
@@ -7630,11 +7667,13 @@
 
 @table @code
 @kindex select-frame
-@item select-frame
+@item select-frame @r{[} @var{frame-selection-spec} @r{]}
 The @code{select-frame} command is a variant of @code{frame} that does
 not display the new frame after selecting it.  This command is
 intended primarily for use in @value{GDBN} command scripts, where the
-output might be unnecessary and distracting.
+output might be unnecessary and distracting.  The
+@var{frame-selection-spec} is as for the @command{frame} command
+described in @ref{Selection, ,Selecting a Frame}.
 
 @kindex down-silently
 @kindex up-silently
@@ -7692,13 +7731,12 @@
 something has gone wrong that has made the stack format fail to fit
 the usual conventions.
 
-@item info frame @var{addr}
-@itemx info f @var{addr}
-Print a verbose description of the frame at address @var{addr}, without
-selecting that frame.  The selected frame remains unchanged by this
-command.  This requires the same kind of address (more than one for some
-architectures) that you specify in the @code{frame} command.
-@xref{Selection, ,Selecting a Frame}.
+@item info frame @r{[} @var{frame-selection-spec} @r{]}
+@itemx info f @r{[} @var{frame-selection-spec} @r{]}
+Print a verbose description of the frame selected by
+@var{frame-selection-spec}.  The @var{frame-selection-spec} is the
+same as for the @command{frame} command (@pxref{Selection, ,Selecting
+a Frame}).  The selected frame remains unchanged by this command.
 
 @kindex info args
 @item info args
@@ -29996,11 +30034,12 @@
 @subsubheading Synopsis
 
 @smallexample
- -stack-select-frame @var{framenum}
+ -stack-select-frame @var{frame-selection-spec}
 @end smallexample
 
-Change the selected frame.  Select a different frame @var{framenum} on
-the stack.
+Change the selected frame, the @var{frame-selection-spec} is as for
+the @command{frame} command described in @ref{Selection, ,Selecting a
+Frame}.
 
 This command in deprecated in favor of passing the @samp{--frame}
 option to every command.
diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c
index 443d1ed4183..c79d7a95e59 100644
--- a/gdb/mi/mi-cmd-stack.c
+++ b/gdb/mi/mi-cmd-stack.c
@@ -680,10 +680,8 @@ list_args_or_locals (enum what_to_list what, enum print_values values,
 void
 mi_cmd_stack_select_frame (const char *command, char **argv, int argc)
 {
-  if (argc == 0 || argc > 1)
+  if (!select_frame_from_spec (argv, argc, 0))
     error (_("-stack-select-frame: Usage: FRAME_SPEC"));
-
-  select_frame_command (argv[0], 1 /* not used */ );
 }
 
 void
diff --git a/gdb/stack.c b/gdb/stack.c
index 5e99a42053c..6f122395558 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -102,6 +102,9 @@ static void set_last_displayed_sal (int valid,
 				    struct symtab *symtab,
 				    int line);
 
+static struct frame_info *find_frame_for_function (const char *);
+static struct frame_info *find_frame_for_address (CORE_ADDR);
+
 /* Zero means do things normally; we are interacting directly with the
    user.  One means print the full filename and linenumber when a
    frame is printed, and do so in a format emacs18/emacs19.22 can
@@ -1255,134 +1258,34 @@ print_frame (struct frame_info *frame, int print_level,
 }
 \f
 
-/* Read a frame specification in whatever the appropriate format is from
-   FRAME_EXP.  Call error() if the specification is in any way invalid (so
-   this function never returns NULL).  When SELECTED_FRAME_P is non-NULL
-   set its target to indicate that the default selected frame was used.  */
+/* Completion function for "frame function", "info frame function", and
+   "select-frame function" commands.  */
 
-static struct frame_info *
-parse_frame_specification (const char *frame_exp, int *selected_frame_p)
+void
+frame_selection_by_function_completer (struct cmd_list_element *ignore,
+				       completion_tracker &tracker,
+				       const char *text, const char *word)
 {
-  int numargs;
-  struct value *args[4];
-  CORE_ADDR addrs[ARRAY_SIZE (args)];
-
-  if (frame_exp == NULL)
-    numargs = 0;
-  else
-    {
-      numargs = 0;
-      while (1)
-	{
-	  const char *p;
-
-	  /* Skip leading white space, bail of EOL.  */
-	  frame_exp = skip_spaces (frame_exp);
-	  if (!*frame_exp)
-	    break;
-
-	  /* Parse the argument, extract it, save it.  */
-	  for (p = frame_exp;
-	       *p && !ISSPACE (*p);
-	       p++);
-	  std::string addr_string (frame_exp, p - frame_exp);
-	  frame_exp = p;
-	  
-	  /* NOTE: Parse and evaluate expression, but do not use
-	     functions such as parse_and_eval_long or
-	     parse_and_eval_address to also extract the value.
-	     Instead value_as_long and value_as_address are used.
-	     This avoids problems with expressions that contain
-	     side-effects.  */
-	  if (numargs >= ARRAY_SIZE (args))
-	    error (_("Too many args in frame specification"));
-	  args[numargs++] = parse_and_eval (addr_string.c_str ());
-	}
-    }
-
-  /* If no args, default to the selected frame.  */
-  if (numargs == 0)
-    {
-      if (selected_frame_p != NULL)
-	(*selected_frame_p) = 1;
-      return get_selected_frame (_("No stack."));
-    }
-
-  /* None of the remaining use the selected frame.  */
-  if (selected_frame_p != NULL)
-    (*selected_frame_p) = 0;
-
-  /* Assume the single arg[0] is an integer, and try using that to
-     select a frame relative to current.  */
-  if (numargs == 1)
-    {
-      struct frame_info *fid;
-      int level = value_as_long (args[0]);
-
-      fid = find_relative_frame (get_current_frame (), &level);
-      if (level == 0)
-	/* find_relative_frame was successful.  */
-	return fid;
-    }
-
-  /* Convert each value into a corresponding address.  */
-  {
-    int i;
-
-    for (i = 0; i < numargs; i++)
-      addrs[i] = value_as_address (args[i]);
-  }
-
-  /* Assume that the single arg[0] is an address, use that to identify
-     a frame with a matching ID.  Should this also accept stack/pc or
-     stack/pc/special.  */
-  if (numargs == 1)
-    {
-      struct frame_id id = frame_id_build_wild (addrs[0]);
-      struct frame_info *fid;
-
-      /* If (s)he specifies the frame with an address, he deserves
-	 what (s)he gets.  Still, give the highest one that matches.
-	 (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't
-	 know).  */
-      for (fid = get_current_frame ();
-	   fid != NULL;
-	   fid = get_prev_frame (fid))
-	{
-	  if (frame_id_eq (id, get_frame_id (fid)))
-	    {
-	      struct frame_info *prev_frame;
-
-	      while (1)
-		{
-		  prev_frame = get_prev_frame (fid);
-		  if (!prev_frame
-		      || !frame_id_eq (id, get_frame_id (prev_frame)))
-		    break;
-		  fid = prev_frame;
-		}
-	      return fid;
-	    }
-	}
-      }
-
-  /* We couldn't identify the frame as an existing frame, but
-     perhaps we can create one with a single argument.  */
-  if (numargs == 1)
-    return create_new_frame (addrs[0], 0);
-  else if (numargs == 2)
-    return create_new_frame (addrs[0], addrs[1]);
-  else
-    error (_("Too many args in frame specification"));
+  /* This is used to complete function names within a stack.  It would be
+     nice if instead of offering all available function names, we only
+     offered functions that were actually in the stack.  However, this
+     would probably mean unwinding the stack to completion, which could
+     take too long (or on a corrupted stack, possibly not end).  For now I
+     offer all symbol names as a safer choice.  */
+  collect_symbol_completion_matches (tracker,
+				     complete_symbol_mode::EXPRESSION,
+				     symbol_name_match_type::EXPRESSION,
+				     text, word);
 }
 
-/* Print verbosely the selected frame or the frame at address
-   ADDR_EXP.  Absolutely all information in the frame is printed.  */
+/* Core of all the "info frame" sub-commands.  Print verbosely the frame FI
+   if SELECTED_FRAME_P is true then frame FI is the current frame, which
+   was selected as a default due to the user not providing any arguments
+   to select some other frame.  */
 
 static void
-info_frame_command (const char *addr_exp, int from_tty)
+info_frame_command_core (struct frame_info *fi, bool selected_frame_p)
 {
-  struct frame_info *fi;
   struct symbol *func;
   struct symtab *s;
   struct frame_info *calling_frame_info;
@@ -1390,7 +1293,6 @@ info_frame_command (const char *addr_exp, int from_tty)
   const char *funname = 0;
   enum language funlang = language_unknown;
   const char *pc_regname;
-  int selected_frame_p;
   struct gdbarch *gdbarch;
   CORE_ADDR frame_pc;
   int frame_pc_p;
@@ -1398,7 +1300,6 @@ info_frame_command (const char *addr_exp, int from_tty)
   CORE_ADDR caller_pc = 0;
   int caller_pc_p = 0;
 
-  fi = parse_frame_specification (addr_exp, &selected_frame_p);
   gdbarch = get_frame_arch (fi);
 
   /* Name of the value returned by get_frame_pc().  Per comments, "pc"
@@ -1687,6 +1588,140 @@ info_frame_command (const char *addr_exp, int from_tty)
   }
 }
 
+/* The core of all the "select-frame" sub-commands.  Just wraps a call to
+   SELECT_FRAME.  */
+
+static void
+select_frame_command_core (struct frame_info *fi, bool ignored)
+{
+  struct frame_info *prev_frame = get_selected_frame_if_set ();
+  select_frame (fi);
+  if (get_selected_frame_if_set () != prev_frame)
+    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
+}
+
+/* The core of all the "frame" sub-commands.  Select frame FI, and if this
+   means we change frame send out a change notification (otherwise, just
+   reprint the current frame summary).   */
+
+static void
+frame_command_core (struct frame_info *fi, bool ignored)
+{
+  struct frame_info *prev_frame = get_selected_frame_if_set ();
+
+  select_frame (fi);
+  if (get_selected_frame_if_set () != prev_frame)
+    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
+  else
+    print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME);
+}
+
+/* The three commands 'frame', 'select-frame', and 'info frame' all have a
+   common set of sub-commands that allow a specific frame to be selected.
+   All of the sub-command functions are static methods within this class
+   template which is then instantiated below.  */
+
+template <void (*FPTR) (struct frame_info *, bool)>
+class frame_command_helper
+{
+public:
+
+  /* The "frame level" family of commands.  The ARG is a number that is
+     the frame's level in the stack.  */
+  static void
+  level (const char *arg, int from_tty)
+  {
+    int level = value_as_long (parse_and_eval (arg));
+    struct frame_info *fid
+      = find_relative_frame (get_current_frame (), &level);
+    if (level != 0)
+      error (_("No frame at level %s."), arg);
+    FPTR (fid, false);
+  }
+
+  /* The "frame address" family of commands.  ARG is a stack-pointer
+     address for an existing frame.  This command does not allow new
+     frames to be created.  */
+
+  static void
+  address (const char *arg, int from_tty)
+  {
+    CORE_ADDR addr = value_as_address (parse_and_eval (arg));
+    struct frame_info *fid = find_frame_for_address (addr);
+    if (fid == NULL)
+      error (_("No frame at address %s."), arg);
+    FPTR (fid, false);
+  }
+
+  /* The "frame create" family of commands.  ARG is one or two addresses
+     and is used to create a frame.  The addresses are stack-pointer
+     address, and (optional) pc-address.  */
+
+  static void
+  create (const char *args, int from_tty)
+  {
+    struct frame_info *fid;
+
+    if (args == NULL)
+    error (_("Missing address argument to create a frame"));
+
+    gdb_argv argv (args);
+
+    if (argv.count () == 2)
+      {
+	CORE_ADDR addr[2];
+
+	addr [0] = value_as_address (parse_and_eval (argv[0]));
+	addr [1] = value_as_address (parse_and_eval (argv[1]));
+	fid = create_new_frame (addr[0], addr[1]);
+      }
+    else
+      {
+	CORE_ADDR addr = value_as_address (parse_and_eval (argv[0]));
+	fid = create_new_frame (addr, false);
+      }
+    FPTR (fid, false);
+  }
+
+  /* The "frame function" family of commands.  ARG is the name of a
+     function within the stack, the first function (searching from frame
+     0) with that name will be selected.  */
+
+  static void
+  function (const char *arg, int from_tty)
+  {
+    if (arg == NULL)
+      error (_("Missing function name argument"));
+    struct frame_info *fid = find_frame_for_function (arg);
+    if (fid == NULL)
+      error (_("No frame for function \"%s\"."), arg);
+    FPTR (fid, false);
+  }
+
+  /* The "frame" base command, that is, when no sub-command is specified.
+     If one argument is provided then we assume that this is a frame level
+     number as historically, this was the supported command syntax that was
+     used most often.
+
+     If no argument is provided, then the current frame is selected.  */
+
+  static void
+  base_command (const char *arg, int from_tty)
+  {
+    if (arg == NULL)
+      FPTR (get_selected_frame (_("No stack.")), true);
+    else
+      level (arg, from_tty);
+  }
+};
+
+/* Create three FRAME_COMMAND_HELPER instances to implement the
+   sub-commands for 'info frame', 'frame', and 'select-frame' commands.  */
+
+static frame_command_helper <info_frame_command_core> info_frame_cmd;
+static frame_command_helper <frame_command_core> frame_cmd;
+static frame_command_helper <select_frame_command_core> select_frame_cmd;
+
 /* Print briefly all stack frames or just the innermost COUNT_EXP
    frames.  */
 
@@ -2212,37 +2247,43 @@ find_relative_frame (struct frame_info *frame, int *level_offset_ptr)
   return frame;
 }
 
-/* The "select_frame" command.  With no argument this is a NOP.
-   Select the frame at level LEVEL_EXP if it is a valid level.
-   Otherwise, treat LEVEL_EXP as an address expression and select it.
-
-   See parse_frame_specification for more info on proper frame
-   expressions.  */
+/* Select a frame using ARGV.  This is used from the MI -stack-select-frame
+   to provide the same frame specification mechanism that the CLI has for
+   commands like 'frame'.  The return value is true if the contents of
+   ARGV looked like a sensible attempt to change the frame (an error might
+   still be thrown though), or false if the contents of ARGV are not a
+   correct frame specification.  */
 
-void
-select_frame_command (const char *level_exp, int from_tty)
+bool
+select_frame_from_spec (char **argv, int argc, int from_tty)
 {
-  struct frame_info *prev_frame = get_selected_frame_if_set ();
-
-  select_frame (parse_frame_specification (level_exp, NULL));
-  if (get_selected_frame_if_set () != prev_frame)
-    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
-}
-
-/* The "frame" command.  With no argument, print the selected frame
-   briefly.  With an argument, behave like select_frame and then print
-   the selected frame.  */
+  if (argc == 1)
+    select_frame_cmd.level (argv[0], from_tty);
+  else if (argc == 2
+	   && strncasecmp ("level", argv[0], strlen (argv[0])) == 0)
+    select_frame_cmd.level (argv[1], from_tty);
+  else if (argc == 2
+	   && strncasecmp ("address", argv[0], strlen (argv[0])) == 0)
+    select_frame_cmd.address (argv[1], from_tty);
+  else if (argc == 2
+	   && strncasecmp ("function", argv[0], strlen (argv[0])) == 0)
+    select_frame_cmd.function (argv[1], from_tty);
+  else if ((argc == 2 || argc == 3)
+	   && strncasecmp ("create", argv[0], strlen (argv[0])) == 0)
+    {
+      std::string arg;
 
-static void
-frame_command (const char *level_exp, int from_tty)
-{
-  struct frame_info *prev_frame = get_selected_frame_if_set ();
+      if (argc == 2)
+	arg = string_printf ("%s", argv[1]);
+      else
+	arg = string_printf ("%s %s", argv[1], argv[2]);
 
-  select_frame (parse_frame_specification (level_exp, NULL));
-  if (get_selected_frame_if_set () != prev_frame)
-    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
+      select_frame_cmd.create (arg.c_str (), from_tty);
+    }
   else
-    print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME);
+    return false;
+
+  return true;
 }
 
 /* Select the frame up one or COUNT_EXP stack levels from the
@@ -2516,9 +2557,59 @@ func_command (const char *arg, int from_tty)
     }
 }
 
+/* Find inner-mode frame with frame address ADDRESS.  Return NULL if no
+   matching frame can be found.  */
+
+static struct frame_info *
+find_frame_for_address (CORE_ADDR address)
+{
+  struct frame_id id;
+  struct frame_info *fid;
+
+  id = frame_id_build_wild (address);
+
+  /* If (s)he specifies the frame with an address, he deserves
+     what (s)he gets.  Still, give the highest one that matches.
+     (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't
+     know).  */
+  for (fid = get_current_frame ();
+       fid != NULL;
+       fid = get_prev_frame (fid))
+    {
+      if (frame_id_eq (id, get_frame_id (fid)))
+	{
+	  struct frame_info *prev_frame;
+
+	  while (1)
+	    {
+	      prev_frame = get_prev_frame (fid);
+	      if (!prev_frame
+		  || !frame_id_eq (id, get_frame_id (prev_frame)))
+		break;
+	      fid = prev_frame;
+	    }
+	  return fid;
+	}
+    }
+  return NULL;
+}
+
+\f
+
+/* Commands with a prefix of `frame'.  */
+static struct cmd_list_element *frame_cmd_list = NULL;
+
+/* Commands with a prefix of `frame'.  */
+static struct cmd_list_element *select_frame_cmd_list = NULL;
+
+/* Commands with a prefix of `info frame'.  */
+static struct cmd_list_element *info_frame_cmd_list = NULL;
+
 void
 _initialize_stack (void)
 {
+  struct cmd_list_element *cmd;
+
   add_com ("return", class_stack, return_command, _("\
 Make selected stack frame return to its caller.\n\
 Control remains in the debugger, but when you continue\n\
@@ -2541,20 +2632,89 @@ An argument says how many frames down to go."));
 Same as the `down' command, but does not print anything.\n\
 This is useful in command scripts."));
 
-  add_com ("frame", class_stack, frame_command, _("\
-Select and print a stack frame.\nWith no argument, \
-print the selected stack frame.  (See also \"info frame\").\n\
-An argument specifies the frame to select.\n\
-It can be a stack frame number or the address of the frame."));
-
+  add_prefix_cmd ("frame", class_stack,
+		  &frame_cmd.base_command, _("\
+Select and print a stack frame.\n\
+With no argument, print the selected stack frame.  (See also \"info frame\").\n\
+A single numerical argument specifies the frame to select."),
+		  &frame_cmd_list, "frame ", 1, &cmdlist);
   add_com_alias ("f", "frame", class_stack, 1);
 
-  add_com_suppress_notification ("select-frame", class_stack, select_frame_command, _("\
+  add_cmd ("address", class_stack, &frame_cmd.address,
+	   _("\
+Select and print a stack frame by stack address\n\
+\n\
+Usage: frame address STACK-ADDRESS"),
+	   &frame_cmd_list);
+
+  add_cmd ("create", class_stack, &frame_cmd.create,
+	   _("\
+Create, select, and print a new stack frame.\n\
+\n\
+Usage: frame create STACK-ADDRESS\n\
+       frame create STACK-ADDRESS PC-ADDRESS"),
+	   &frame_cmd_list);
+
+  cmd = add_cmd ("function", class_stack, &frame_cmd.function,
+	   _("\
+Select and print a stack frame by function name.\n\
+\n\
+Usage: frame function NAME\n\
+\n\
+The innermost frame that visited function NAME is selected."),
+	   &frame_cmd_list);
+  set_cmd_completer (cmd, frame_selection_by_function_completer);
+
+
+  add_cmd ("level", class_stack, &frame_cmd.level,
+	   _("\
+Select and print a stack frame by level.\n\
+\n\
+Usage: frame level NUMBER"),
+	   &frame_cmd_list);
+
+  cmd = add_prefix_cmd_suppress_notification ("select-frame", class_stack,
+		      &select_frame_cmd.base_command, _("\
 Select a stack frame without printing anything.\n\
-An argument specifies the frame to select.\n\
-It can be a stack frame number or the address of the frame."),
+A single numerical argument specifies the frame to select."),
+		      &select_frame_cmd_list, "select-frame ", 1, &cmdlist,
+		      &cli_suppress_notification.user_selected_context);
+
+  add_cmd_suppress_notification ("address", class_stack,
+			 &select_frame_cmd.address, _("\
+Select a stack frame by stack address.\n\
+\n\
+Usage: frame address STACK-ADDRESS"),
+			 &select_frame_cmd_list,
+			 &cli_suppress_notification.user_selected_context);
+
+
+  add_cmd_suppress_notification ("create", class_stack,
+		 &select_frame_cmd.create, _("\
+Create and select a new stack frame.\n\
+\n\
+Usage: frame create STACK-ADDRESS\n\
+       frame create STACK-ADDRESS PC-ADDRESS"),
+		 &select_frame_cmd_list,
 		 &cli_suppress_notification.user_selected_context);
 
+  cmd = add_cmd_suppress_notification ("function", class_stack,
+	       &select_frame_cmd.function, _("\
+Select a stack frame by function name.\n\
+\n\
+Usage: frame function NAME"),
+	       &select_frame_cmd_list,
+	       &cli_suppress_notification.user_selected_context);
+  set_cmd_completer (cmd, frame_selection_by_function_completer);
+
+  add_cmd_suppress_notification ("level", class_stack,
+			 &select_frame_cmd.level, _("\
+Select a stack frame by level.\n\
+\n\
+Usage: frame level NUMBER"),
+			 &select_frame_cmd_list,
+			 &cli_suppress_notification.user_selected_context);
+
   add_com ("backtrace", class_stack, backtrace_command, _("\
 Print backtrace of all stack frames, or innermost COUNT frames.\n\
 Usage: backtrace [QUALIFIERS]... [COUNT]\n\
@@ -2568,9 +2728,45 @@ on this backtrace."));
   add_info ("stack", backtrace_command,
 	    _("Backtrace of the stack, or innermost COUNT frames."));
   add_info_alias ("s", "stack", 1);
-  add_info ("frame", info_frame_command,
-	    _("All about selected stack frame, or frame at ADDR."));
-  add_info_alias ("f", "frame", 1);
+
+  add_prefix_cmd ("frame", class_info, &info_frame_cmd.base_command,
+		  _("All about the selected stack frame.\n\
+With no arguments, displays information about the currently selected stack\n\
+frame.  Alternatively a frame specification may be provided (See \"frame\")\n\
+the information is then printed about the specified frame."),
+		  &info_frame_cmd_list, "info frame ", 1, &infolist);
+  add_com_alias ("f", "frame", class_stack, 1);
+
+  add_cmd ("address", class_stack, &info_frame_cmd.address,
+	   _("\
+Print information about a stack frame selected by stack address.\n\
+\n\
+Usage: frame address STACK-ADDRESS"),
+	   &info_frame_cmd_list);
+
+  add_cmd ("create", class_stack, &info_frame_cmd.create,
+	   _("\
+Create a new stack frame, and print information about it.\n\
+\n\
+Usage: frame create STACK-ADDRESS\n\
+       frame create STACK-ADDRESS PC-ADDRESS"),
+	   &info_frame_cmd_list);
+
+  cmd = add_cmd ("function", class_stack, &info_frame_cmd.function,
+	   _("\
+Print information about a stack frame selected by function name.\n\
+\n\
+Usage: frame function NAME"),
+	   &info_frame_cmd_list);
+  set_cmd_completer (cmd, frame_selection_by_function_completer);
+
+  add_cmd ("level", class_stack, &info_frame_cmd.level,
+	   _("\
+Print information about a stack frame selected by level.\n\
+\n\
+Usage: frame level NUMBER"),
+	   &info_frame_cmd_list);
+
   add_info ("locals", info_locals_command,
 	    _("Local variables of current stack frame."));
   add_info ("args", info_args_command,
diff --git a/gdb/stack.h b/gdb/stack.h
index ca190efa9c3..29794872d70 100644
--- a/gdb/stack.h
+++ b/gdb/stack.h
@@ -20,7 +20,7 @@
 #ifndef STACK_H
 #define STACK_H
 
-void select_frame_command (const char *level_exp, int from_tty);
+bool select_frame_from_spec (char **argv, int argc, int from_tty);
 
 gdb::unique_xmalloc_ptr<char> find_frame_funname (struct frame_info *frame,
 						  enum language *funlang,
diff --git a/gdb/testsuite/gdb.base/frame-selection.c b/gdb/testsuite/gdb.base/frame-selection.c
new file mode 100644
index 00000000000..f3d81f223e0
--- /dev/null
+++ b/gdb/testsuite/gdb.base/frame-selection.c
@@ -0,0 +1,52 @@
+/* Copyright 2018 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int
+frame_2 (void)
+{
+  return 0;
+}
+
+int
+frame_1 (void)
+{
+  return frame_2 ();
+}
+
+int
+recursive (int arg)
+{
+  int v;
+
+  if (arg < 2)
+    v = recursive (arg + 1);
+  else
+    v = frame_2 ();
+
+  return v;
+}
+
+int
+main (void)
+{
+  int i, j;
+
+  i = frame_1 ();
+  j = recursive (0);
+
+  return i + j;
+}
diff --git a/gdb/testsuite/gdb.base/frame-selection.exp b/gdb/testsuite/gdb.base/frame-selection.exp
new file mode 100644
index 00000000000..6df85494839
--- /dev/null
+++ b/gdb/testsuite/gdb.base/frame-selection.exp
@@ -0,0 +1,156 @@
+# Copyright 2018 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+if { [prepare_for_testing frame-cmds.exp "frame-cmds" {frame-cmds.c} {debug nowarnings}] } {
+    return -1
+}
+set srcfile frame-cmds.c
+
+runto_main
+gdb_breakpoint frame_2
+gdb_continue_to_breakpoint frame_2
+
+gdb_test "bt" "#0  frame_2.*#1  $hex in frame_1.*#2  $hex in main.*" "backtrace at breakpoint"
+
+# Perform "info frame" to extract the frames address.
+proc get_frame_address { {testname ""} } {
+    global hex gdb_prompt
+
+    set testname "get_frame_address: ${testname}"
+    set frame_address "unknown"
+
+    send_gdb "info frame\n"
+    gdb_expect {
+	-re ", frame at ($hex):\r\n.*\r\n$gdb_prompt $" {
+	    set frame_address $expect_out(1,string)
+	    pass $testname
+	}
+	-re ".*$gdb_prompt $" { fail $testname }
+	timeout { fail "$testname (timeout)" }
+    }
+
+    return $frame_address
+}
+
+# Passes a list of addresses.  Return a new address that is not in the
+# list.
+proc get_new_address { {addresses {}} } {
+    return [format "%#x" [expr [lindex $addresses [llength addresses]] + 0x10 ]]
+}
+
+proc check_frame { level address function } {
+    global hex gdb_prompt
+
+    set testname "check frame level ${level}"
+    send_gdb "info frame\n"
+    gdb_expect {
+	-re "Stack level ${level}, frame at ($address):\r\n rip = $hex in ${function} \(\[^\r\n\]*\); saved rip = $hex\r\n.*\r\n$gdb_prompt $" {
+	    pass $testname
+	}
+	-re ".*$gdb_prompt $" { fail $testname }
+	timeout { fail "$testname (timeout)" }
+    }
+}
+
+# Select frame using level number, but relying on this being the
+# default action, so "frame 0" performs "frame level 0".
+gdb_test "frame 0" "#0  frame_2.*"
+set frame_0_address [ get_frame_address "frame 0" ]
+gdb_test "frame 1" "#1  $hex in frame_1.*"
+set frame_1_address [ get_frame_address "frame 1" ]
+gdb_test "frame 2" "#2  $hex in main.*"
+set frame_2_address [ get_frame_address "frame 2" ]
+gdb_test "frame 3" "No frame at level 3\."
+
+# Find an address that matches no frame
+set no_frame_address [ get_new_address [list $frame_0_address \
+					     $frame_1_address \
+					     $frame_2_address] ]
+
+# Select frame using 'level' specification.
+gdb_test "frame level 0" "#0  frame_2.*"
+gdb_test "frame level 1" "#1  $hex in frame_1.*"
+gdb_test "frame level 2" "#2  $hex in main.*"
+gdb_test "frame level 3" "No frame at level 3\."
+
+# Select frame by address.
+gdb_test "frame address ${frame_0_address}" "#0  frame_2.*"
+gdb_test "frame address ${frame_1_address}" "#1  $hex in frame_1.*"
+gdb_test "frame address ${frame_2_address}" "#2  $hex in main.*"
+gdb_test "frame address ${no_frame_address}" \
+    "No frame at address ${no_frame_address}\."
+
+# Select frame by function.
+gdb_test "frame function frame_2" "#0  frame_2.*"
+gdb_test "frame function frame_1" "#1  $hex in frame_1.*"
+gdb_test "frame function main" "#2  $hex in main.*"
+# Check for a distinction between a known function not in the stack
+# trace, and an unknown function.
+gdb_test "frame function recursive" "No frame for function \"recursive\"."
+gdb_test "frame function foo" "Function \"foo\" not defined."
+
+
+gdb_test_no_output "select-frame 0"
+check_frame "0" "${frame_0_address}" "frame_2"
+gdb_test_no_output "select-frame 1"
+check_frame "1" "${frame_1_address}" "frame_1"
+gdb_test_no_output "select-frame 2"
+check_frame "2" "${frame_2_address}" "main"
+gdb_test "select-frame 3" "No frame at level 3\."
+
+gdb_test_no_output "select-frame level 0"
+check_frame "0" "${frame_0_address}" "frame_2"
+gdb_test_no_output "select-frame level 1"
+check_frame "1" "${frame_1_address}" "frame_1"
+gdb_test_no_output "select-frame level 2"
+check_frame "2" "${frame_2_address}" "main"
+gdb_test "select-frame level 3" "No frame at level 3\."
+
+gdb_test_no_output "select-frame address ${frame_0_address}"
+check_frame "0" "${frame_0_address}" "frame_2"
+gdb_test_no_output "select-frame address ${frame_1_address}"
+check_frame "1" "${frame_1_address}" "frame_1"
+gdb_test_no_output "select-frame address ${frame_2_address}"
+check_frame "2" "${frame_2_address}" "main"
+gdb_test "select-frame address ${no_frame_address}" \
+    "No frame at address ${no_frame_address}\."
+
+gdb_test_no_output "select-frame function frame_2"
+check_frame "0" "${frame_0_address}" "frame_2"
+gdb_test_no_output "select-frame function frame_1"
+check_frame "1" "${frame_1_address}" "frame_1"
+gdb_test_no_output "select-frame function main"
+check_frame "2" "${frame_2_address}" "main"
+# Check for a distinction between a known function not in the stack
+# trace, and an unknown function.
+gdb_test "select-frame function recursive" \
+    "No frame for function \"recursive\"."
+gdb_test "select-frame function foo" \
+    "Function \"foo\" not defined."
+
+# Now continue until we hit the breakpoint again.
+gdb_continue_to_breakpoint frame_2
+gdb_test "bt" \
+    "#0  frame_2.*#1  $hex in recursive.*#2  $hex in recursive.*#3  $hex in recursive.*#4  $hex in main.*" \
+    "backtrace at breakpoint with recursive frames"
+
+# Check "frame function" when a function name occurs multiple times in
+# the stack.  The inner most (lowest level number) should always be
+# selected.
+gdb_test "frame function frame_2" "#0  frame_2.*"
+gdb_test "frame function recursive" "#1  $hex in recursive.*"
+gdb_test "frame function recursive" "#1  $hex in recursive.*"
+gdb_test "frame function main" "#4  $hex in main.*"
+gdb_test "frame function recursive" "#1  $hex in recursive.*"
diff --git a/gdb/testsuite/gdb.mi/mi-frame-selection.c b/gdb/testsuite/gdb.mi/mi-frame-selection.c
new file mode 100644
index 00000000000..70b992c9149
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-frame-selection.c
@@ -0,0 +1,34 @@
+/* Copyright 2018 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int
+frame_2 (void)
+{
+  return 0;
+}
+
+int
+frame_1 (void)
+{
+  return frame_2 ();
+}
+
+int
+main (void)
+{
+  return frame_1 ();
+}
diff --git a/gdb/testsuite/gdb.mi/mi-frame-selection.exp b/gdb/testsuite/gdb.mi/mi-frame-selection.exp
new file mode 100644
index 00000000000..d51b0ae9315
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-frame-selection.exp
@@ -0,0 +1,90 @@
+# Copyright 2018 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This tests an issue relating to updating var objects in user created
+# frames.
+
+load_lib mi-support.exp
+set MIFLAGS "-i=mi"
+
+gdb_exit
+if [mi_gdb_start] {
+    continue
+}
+
+standard_testfile
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    untested $testfile
+    return -1
+}
+
+mi_delete_breakpoints
+mi_gdb_reinitialize_dir $srcdir/$subdir
+mi_gdb_load ${binfile}
+
+mi_create_breakpoint "frame_2" \
+    "break-insert into frame_2" \
+    -number 1 -func frame_2 -file ".*${srcfile}"
+
+mi_run_cmd
+mi_expect_stop "breakpoint-hit" "frame_2" "" ".*${srcfile}" \
+	".*" { "" "disp=\"keep\"" } "run to breakpoint"
+
+mi_gdb_test "-stack-info-depth" \
+    "\\^done,depth=\"3\"" \
+    "stack info-depth"
+
+for {set i 0} {$i < 5} {incr i} {
+
+    if { $i < 3 } then {
+	mi_gdb_test "-stack-select-frame $i" \
+	    {\^done} \
+	    "-stack-select-frame $i"
+
+	mi_gdb_test "-stack-info-frame" \
+	    "\\^done,frame=\\\{level=\"$i\"\[^\}\]+\\\}" \
+	    "check level has has changed to $i"
+
+    } else {
+	mi_gdb_test "-stack-select-frame create $i" \
+	    {\^done} \
+	    "-stack-select-frame create frame at $i"
+
+	mi_gdb_test "-stack-info-frame" \
+	    "\\^done,frame=\\\{level=\"0\"\[^\}\]+\\\}" \
+	    "check level has has changed to 0, when creating a frame at sp=$i"
+    }
+}
+
+mi_gdb_test "-stack-select-frame 5" \
+    {\^error,msg="No frame at level 5."} \
+    "-stack-select-frame 5, with no keyword"
+
+mi_gdb_test "-stack-select-frame level 5" \
+    {\^error,msg="No frame at level 5."} \
+    "-stack-select-frame 5, using 'level' keyword"
+
+mi_gdb_test "-stack-select-frame function main" \
+    {\^done} \
+    "-stack-select-frame select frame for function main"
+
+mi_gdb_test "-stack-info-frame" \
+    "\\^done,frame=\\\{level=\"2\"\[^\}\]+\\\}" \
+    "check level has has changed to 2 for function main"
+
+mi_gdb_test "-stack-select-frame function unknown_function" \
+    {\^error,msg=\"Function \\\"unknown_function\\\" not defined.\"} \
+    "-stack-select-frame check error on undefined function name"
-- 
2.14.3

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCHv2 2/2] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-05-08 16:59 ` [PATCHv2 2/2] gdb: Change how frames are selected for 'frame' and 'info frame' Andrew Burgess
@ 2018-05-11 15:44   ` Eli Zaretskii
  2018-05-21 12:16     ` Andrew Burgess
  2018-06-07 16:19   ` [PATCHv3] " Andrew Burgess
  1 sibling, 1 reply; 35+ messages in thread
From: Eli Zaretskii @ 2018-05-11 15:44 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

> From: Andrew Burgess <andrew.burgess@embecosm.com>
> Cc: Andrew Burgess <andrew.burgess@embecosm.com>
> Date: Tue,  8 May 2018 17:58:45 +0100
> 
> The 'frame' command, and thanks to code reuse the 'info frame' and
> 'select-frame' commands, currently have an overloaded mechanism for
> selecting a frame.
> 
> These commands take one or two parameters, if it's one parameter then
> we first try to use the parameter as an integer to select a frame by
> level (or depth in the stack).  If that fails then we treat the
> parameter as an address and try to select a stack frame by
> stack-address.  If we still have not selected a stack frame, or we
> initially had two parameters, then we create a new stack frame and
> select that.
> 
> The result of this is that a typo by the user, entering the wrong stack
> frame level for example, can result in a brand new frame being created
> rather than an error.
> 
> The purpose of this commit is to remove this overloading, while still
> offering the same functionality through some new sub-commands.

An alternative could be to request confirmation before creating a new
frame.

> diff --git a/gdb/NEWS b/gdb/NEWS
> index 46f6635dda0..fe91887cde4 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -12,6 +12,35 @@
>  * C expressions can now use _Alignof, and C++ expressions can now use
>    alignof.
>  
> +* Changes to the "frame", "select-frame", and "info frame" CLI
> +  commands, as well as the "-stack-select-frame" MI command.
> +  Selecting frames by number remains unchanged, however, selecting
> +  frames by stack-address, or creating new frames now requires the use
> +  of a sub-command.  Various sub-commands now exist for the various
> +  methods of selecting a frame; level, address, function, and create.
> +  As an example, here are the variants of "frame" that are now available:
> +  - frame <number>
> +  - frame level <number>
> +    Both of these select frame at level <number>.
> +  - frame address <stack-address>
> +    Select frame for <stack-address>.
> +  - frame function <name>
> +    Select inner most frame for function <name>.
> +  - frame create <stack-address>
> +    Create a frame for <stack-address>.
> +  - frame create <stack-address> <pc-address>
> +    Create a frame for <stack-address> <pc-address>.
> +  There are similar variants for the "select-frame" and "info frame"
> +  commands.  The MI command "-stack-select-frame" places the
> +  sub-command name after the command name as a keyword, the available
> +  variants are:
> +  -stack-select-frame <number>
> +  -stack-select-frame level <number>
> +  -stack-select-frame address <stack-address>
> +  -stack-select-frame function <name>
> +  -stack-select-frame create <stack-address>
> +  -stack-select-frame create <stack-address> <pc-address>

This is IMO too wordy for NEWS.  I think it's enough to mention that
new sub-command and tell that creation of new frames now requires that
sub-command.

> -@item frame @var{stack-addr} [ @var{pc-addr} ]
> -@itemx f @var{stack-addr} [ @var{pc-addr} ]
> -Select the frame at address @var{stack-addr}.  This is useful mainly if the
> -chaining of stack frames has been damaged by a bug, making it
> -impossible for @value{GDBN} to assign numbers properly to all frames.  In
> -addition, this can be useful when your program has multiple stacks and
> -switches between them.  The optional @var{pc-addr} can also be given to
> -specify the value of PC for the stack frame.
> +@kindex frame address
> +@item address @var{stack-address}
> +Select the frame with stack address @var{stack-address}.  Recall that
> +each stack frame has a stack frame address, which roughly corresponds
> +to the location on the stack where the stack frame local variables are
> +stored.

The text in the "Recall" sentence IMO doesn't convey anything useful,
it just says that each frame has a stack address which can be used to
select it, i.e. repeats what the previous sentence already said.

> +@kindex frame create
> +@item create @var{stack-address} @r{[} @var{pc-addr} @r{]}
> +Create and then select a new frame at stack address @var{stack-addr}.
> +This is useful mainly if the chaining of stack frames has been damaged
> +by a bug, making it impossible for @value{GDBN} to assign numbers
> +properly to all frames.  In addition, this can be useful when your
> +program has multiple stacks and switches between them.  The optional
> +@var{pc-addr} can also be given to specify the value of PC for the
> +stack frame.

I'm surprised: is it true that PC-ADDR is used only for creating new
stack frames?  The description refers to programs with multiple
stacks, in which case I'd expect to be able to refer to existing stack
frames on another stack.  The original text seemed to allow that, at
least implicitly.  Also, is it true that damaged stack is only
relevant when creating new frames?

Thanks.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCHv2 1/2] gdb: Split func_command into two parts.
  2018-05-08 16:58 ` [PATCHv2 1/2] gdb: Split func_command into two parts Andrew Burgess
@ 2018-05-18 19:57   ` Pedro Alves
  2018-05-21 15:52     ` Andrew Burgess
  0 siblings, 1 reply; 35+ messages in thread
From: Pedro Alves @ 2018-05-18 19:57 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

On 05/08/2018 05:58 PM, Andrew Burgess wrote:
> The func_command function is used to emulate the dbx 'func' command.
> However, finding a stack frame based on function name might be a useful
> feature, and so the core of func_command is now split out into a
> separate function.
> 
> gdb/ChangeLog:
> 
>         * stack.c (select_and_print_frame): Delete.
>         (func_command): Most content moved into new function
>         find_frame_for_function, use new function, print result, add
>         function comment.
> 	(find_frame_for_function): New function, now returns a result.

This LGTM, with a couple minor nits.

>  /* Return the symbol-block in which the selected frame is executing.
>     Can return zero under various legitimate circumstances.
> @@ -2460,19 +2450,19 @@ struct function_bounds
>    CORE_ADDR low, high;
>  };
>  
> -static void
> -func_command (const char *arg, int from_tty)
> +static struct frame_info *
> +find_frame_for_function (const char *function_name)

There's a comment above the structure that is describing
what the func_command function did.  That needs to be
updated to describe what the new function does.

> +
> +/* Implements the dbx 'func' command.  */
> +
> +static void
> +func_command (const char *arg, int from_tty)
> +{
> +  struct frame_info *frame;
> +
> +  if (arg == NULL)
> +    return;
> +
> +  frame = find_frame_for_function (arg);

You can declare and initialize at the same time:

func_command (const char *arg, int from_tty)
{
  if (arg == NULL)
    return;

  frame_info *frame = find_frame_for_function (arg);

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCHv2 2/2] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-05-11 15:44   ` Eli Zaretskii
@ 2018-05-21 12:16     ` Andrew Burgess
  2018-05-21 17:46       ` Eli Zaretskii
  0 siblings, 1 reply; 35+ messages in thread
From: Andrew Burgess @ 2018-05-21 12:16 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches

Eli,

Thanks for your feedback.  I've addressed two of your comments, but
wanted to better understand your third point so that I could address
is correctly...

* Eli Zaretskii <eliz@gnu.org> [2018-05-11 16:46:30 +0300]:

> > From: Andrew Burgess <andrew.burgess@embecosm.com>
> > Cc: Andrew Burgess <andrew.burgess@embecosm.com>
> > Date: Tue,  8 May 2018 17:58:45 +0100
> > 
> > The 'frame' command, and thanks to code reuse the 'info frame' and
> > 'select-frame' commands, currently have an overloaded mechanism for
> > selecting a frame.
> > 
> > These commands take one or two parameters, if it's one parameter then
> > we first try to use the parameter as an integer to select a frame by
> > level (or depth in the stack).  If that fails then we treat the
> > parameter as an address and try to select a stack frame by
> > stack-address.  If we still have not selected a stack frame, or we
> > initially had two parameters, then we create a new stack frame and
> > select that.
> > 
> > The result of this is that a typo by the user, entering the wrong stack
> > frame level for example, can result in a brand new frame being created
> > rather than an error.
> > 
> > The purpose of this commit is to remove this overloading, while still
> > offering the same functionality through some new sub-commands.
> 
> An alternative could be to request confirmation before creating a new
> frame.
> 
> > diff --git a/gdb/NEWS b/gdb/NEWS
> > index 46f6635dda0..fe91887cde4 100644
> > --- a/gdb/NEWS
> > +++ b/gdb/NEWS
> > @@ -12,6 +12,35 @@
> >  * C expressions can now use _Alignof, and C++ expressions can now use
> >    alignof.
> >  
> > +* Changes to the "frame", "select-frame", and "info frame" CLI
> > +  commands, as well as the "-stack-select-frame" MI command.
> > +  Selecting frames by number remains unchanged, however, selecting
> > +  frames by stack-address, or creating new frames now requires the use
> > +  of a sub-command.  Various sub-commands now exist for the various
> > +  methods of selecting a frame; level, address, function, and create.
> > +  As an example, here are the variants of "frame" that are now available:
> > +  - frame <number>
> > +  - frame level <number>
> > +    Both of these select frame at level <number>.
> > +  - frame address <stack-address>
> > +    Select frame for <stack-address>.
> > +  - frame function <name>
> > +    Select inner most frame for function <name>.
> > +  - frame create <stack-address>
> > +    Create a frame for <stack-address>.
> > +  - frame create <stack-address> <pc-address>
> > +    Create a frame for <stack-address> <pc-address>.
> > +  There are similar variants for the "select-frame" and "info frame"
> > +  commands.  The MI command "-stack-select-frame" places the
> > +  sub-command name after the command name as a keyword, the available
> > +  variants are:
> > +  -stack-select-frame <number>
> > +  -stack-select-frame level <number>
> > +  -stack-select-frame address <stack-address>
> > +  -stack-select-frame function <name>
> > +  -stack-select-frame create <stack-address>
> > +  -stack-select-frame create <stack-address> <pc-address>
> 
> This is IMO too wordy for NEWS.  I think it's enough to mention that
> new sub-command and tell that creation of new frames now requires that
> sub-command.
> 
> > -@item frame @var{stack-addr} [ @var{pc-addr} ]
> > -@itemx f @var{stack-addr} [ @var{pc-addr} ]
> > -Select the frame at address @var{stack-addr}.  This is useful mainly if the
> > -chaining of stack frames has been damaged by a bug, making it
> > -impossible for @value{GDBN} to assign numbers properly to all frames.  In
> > -addition, this can be useful when your program has multiple stacks and
> > -switches between them.  The optional @var{pc-addr} can also be given to
> > -specify the value of PC for the stack frame.
> > +@kindex frame address
> > +@item address @var{stack-address}
> > +Select the frame with stack address @var{stack-address}.  Recall that
> > +each stack frame has a stack frame address, which roughly corresponds
> > +to the location on the stack where the stack frame local variables are
> > +stored.
> 
> The text in the "Recall" sentence IMO doesn't convey anything useful,
> it just says that each frame has a stack address which can be used to
> select it, i.e. repeats what the previous sentence already said.
> 
> > +@kindex frame create
> > +@item create @var{stack-address} @r{[} @var{pc-addr} @r{]}
> > +Create and then select a new frame at stack address @var{stack-addr}.
> > +This is useful mainly if the chaining of stack frames has been damaged
> > +by a bug, making it impossible for @value{GDBN} to assign numbers
> > +properly to all frames.  In addition, this can be useful when your
> > +program has multiple stacks and switches between them.  The optional
> > +@var{pc-addr} can also be given to specify the value of PC for the
> > +stack frame.
> 
> I'm surprised: is it true that PC-ADDR is used only for creating new
> stack frames?  The description refers to programs with multiple
> stacks, in which case I'd expect to be able to refer to existing stack
> frames on another stack.  The original text seemed to allow that, at
> least implicitly.  Also, is it true that damaged stack is only
> relevant when creating new frames?

I don't really understand what it is you're asking here, but I think
it might be related to overloading of the word "frame", and possibly
the keyword "create" not being a good choice.

Sure, within the inferior there are a set of frames, some of these
form the current stack, and some might exist on some alternative
stack.  Those frames exist regardless of GDB's ability to visualise
them.

However, there's a second use of the word frame, which we use for
GDB's representation of a stack-frame.  Under this second meaning the
only frames that exist are those GDB can derive from the current
machine state.  So, if the user asks for a backtrace and is told about
#0 A, #1 B, #2 C, then GDB only knows about those 3 frames.

If the user knows of #3 D that called C (but the unwind failed for
some reason) then they can use: 'frame create STACK-ADDR PC-ADDR' to
"create" a suitable frame and examine the machine state.  The frame
being "created" here is really a GDB frame, that is, a representation
of a preexisting inferior frame.

Similarly, if there's a second stack the user can "create" frames to
represent the frames on the second stack.

What we /can't/ do at the moment is backtrace out of these newly
created frames, which is a bit of a shame.  I do have a plan to work
on this, but that's for another day...

Anyway, I'm happy to rework the text if you can suggest some
improvements.  Alternatively, maybe it was my choice of "create" as
the keyword that was confusing... again, if you have any better
suggestions I'm happy to change it, I'm not tied to "create".

Thanks,

Andrew

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCHv2 1/2] gdb: Split func_command into two parts.
  2018-05-18 19:57   ` Pedro Alves
@ 2018-05-21 15:52     ` Andrew Burgess
  2018-05-21 16:06       ` Pedro Alves
  0 siblings, 1 reply; 35+ messages in thread
From: Andrew Burgess @ 2018-05-21 15:52 UTC (permalink / raw)
  To: gdb-patches; +Cc: Pedro Alves

Thanks for the feedback.

* Pedro Alves <palves@redhat.com> [2018-05-18 20:38:26 +0100]:

> On 05/08/2018 05:58 PM, Andrew Burgess wrote:
> > The func_command function is used to emulate the dbx 'func' command.
> > However, finding a stack frame based on function name might be a useful
> > feature, and so the core of func_command is now split out into a
> > separate function.
> > 
> > gdb/ChangeLog:
> > 
> >         * stack.c (select_and_print_frame): Delete.
> >         (func_command): Most content moved into new function
> >         find_frame_for_function, use new function, print result, add
> >         function comment.
> > 	(find_frame_for_function): New function, now returns a result.
> 
> This LGTM, with a couple minor nits.
> 
> >  /* Return the symbol-block in which the selected frame is executing.
> >     Can return zero under various legitimate circumstances.
> > @@ -2460,19 +2450,19 @@ struct function_bounds
> >    CORE_ADDR low, high;
> >  };
> >  
> > -static void
> > -func_command (const char *arg, int from_tty)
> > +static struct frame_info *
> > +find_frame_for_function (const char *function_name)
> 
> There's a comment above the structure that is describing
> what the func_command function did.  That needs to be
> updated to describe what the new function does.

As the structure was so small, and only used in that one function, I
moved the structure into the function entirely (and added an updated
comment to both the structure, and the function).  I had a little look
through GDB and there are a few other cases of structures declared
within a function, so hopefully this is OK.  Let me know if you'd
rather see this moved back out again.

> 
> > +
> > +/* Implements the dbx 'func' command.  */
> > +
> > +static void
> > +func_command (const char *arg, int from_tty)
> > +{
> > +  struct frame_info *frame;
> > +
> > +  if (arg == NULL)
> > +    return;
> > +
> > +  frame = find_frame_for_function (arg);
> 
> You can declare and initialize at the same time:

Done.

---

gdb: Split func_command into two parts.

The func_command function is used to emulate the dbx 'func' command.
However, finding a stack frame based on function name might be a useful
feature, and so the core of func_command is now split out into a
separate function.

gdb/ChangeLog:

	* stack.c (select_and_print_frame): Delete.
	(struct function_bounds): Move struct within function.
	(func_command): Most content moved into new function
	find_frame_for_function, use new function, print result, add
	function comment.
	(find_frame_for_function): New function, now returns a result.
---
 gdb/ChangeLog |  9 ++++++++
 gdb/stack.c   | 67 +++++++++++++++++++++++++++++++++--------------------------
 2 files changed, 46 insertions(+), 30 deletions(-)

diff --git a/gdb/stack.c b/gdb/stack.c
index 74c92537da4..5cd17de3193 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -2156,16 +2156,6 @@ info_args_command (const char *ignore, int from_tty)
   print_frame_arg_vars (get_selected_frame (_("No frame selected.")),
 			gdb_stdout);
 }
-
-/* Select frame FRAME.  Also print the stack frame and show the source
-   if this is the tui version.  */
-static void
-select_and_print_frame (struct frame_info *frame)
-{
-  select_frame (frame);
-  if (frame)
-    print_stack_frame (frame, 1, SRC_AND_LOC, 1);
-}
 \f
 /* Return the symbol-block in which the selected frame is executing.
    Can return zero under various legitimate circumstances.
@@ -2451,29 +2441,30 @@ return_command (const char *retval_exp, int from_tty)
     print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
 }
 
-/* Sets the scope to input function name, provided that the function
-   is within the current stack frame.  */
-
-struct function_bounds
-{
-  CORE_ADDR low, high;
-};
+/* Find the most inner frame in the current stack for a function called
+   FUNCTION_NAME.  If no matching frame is found return NULL.  */
 
-static void
-func_command (const char *arg, int from_tty)
+static struct frame_info *
+find_frame_for_function (const char *function_name)
 {
+  /* Used to hold the lower and upper addresses for each of the
+     SYMTAB_AND_LINEs found for functions matching FUNCTION_NAME.  */
+  struct function_bounds
+  {
+    CORE_ADDR low, high;
+  };
   struct frame_info *frame;
-  int found = 0;
+  bool found = false;
   int level = 1;
 
-  if (arg == NULL)
-    return;
+  gdb_assert (function_name != NULL);
 
   frame = get_current_frame ();
   std::vector<symtab_and_line> sals
-    = decode_line_with_current_source (arg, DECODE_LINE_FUNFIRSTLINE);
+    = decode_line_with_current_source (function_name,
+				       DECODE_LINE_FUNFIRSTLINE);
   gdb::def_vector<function_bounds> func_bounds (sals.size ());
-  for (size_t i = 0; (i < sals.size () && !found); i++)
+  for (size_t i = 0; i < sals.size (); i++)
     {
       if (sals[i].pspace != current_program_space)
 	func_bounds[i].low = func_bounds[i].high = 0;
@@ -2481,9 +2472,7 @@ func_command (const char *arg, int from_tty)
 	       || find_pc_partial_function (sals[i].pc, NULL,
 					    &func_bounds[i].low,
 					    &func_bounds[i].high) == 0)
-	{
-	  func_bounds[i].low = func_bounds[i].high = 0;
-	}
+	func_bounds[i].low = func_bounds[i].high = 0;
     }
 
   do
@@ -2500,9 +2489,27 @@ func_command (const char *arg, int from_tty)
   while (!found && level == 0);
 
   if (!found)
-    printf_filtered (_("'%s' not within current stack frame.\n"), arg);
-  else if (frame != get_selected_frame (NULL))
-    select_and_print_frame (frame);
+    frame = NULL;
+
+  return frame;
+}
+
+/* Implements the dbx 'func' command.  */
+
+static void
+func_command (const char *arg, int from_tty)
+{
+  if (arg == NULL)
+    return;
+
+  struct frame_info *frame = find_frame_for_function (arg);
+  if (frame == NULL)
+    error (_("'%s' not within current stack frame.\n"), arg);
+  if (frame != get_selected_frame (NULL))
+    {
+      select_frame (frame);
+      print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
+    }
 }
 
 void
-- 
2.14.3

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCHv2 1/2] gdb: Split func_command into two parts.
  2018-05-21 15:52     ` Andrew Burgess
@ 2018-05-21 16:06       ` Pedro Alves
  0 siblings, 0 replies; 35+ messages in thread
From: Pedro Alves @ 2018-05-21 16:06 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

On 05/21/2018 04:27 PM, Andrew Burgess wrote:

> As the structure was so small, and only used in that one function, I
> moved the structure into the function entirely (and added an updated
> comment to both the structure, and the function).  I had a little look
> through GDB and there are a few other cases of structures declared
> within a function, so hopefully this is OK.  Let me know if you'd
> rather see this moved back out again.

This is fine with me as is.  Feel free to push it in immediately
to get it out of the way.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCHv2 2/2] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-05-21 12:16     ` Andrew Burgess
@ 2018-05-21 17:46       ` Eli Zaretskii
  2018-06-05 18:53         ` Andrew Burgess
  0 siblings, 1 reply; 35+ messages in thread
From: Eli Zaretskii @ 2018-05-21 17:46 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

> Date: Mon, 21 May 2018 12:53:57 +0100
> From: Andrew Burgess <andrew.burgess@embecosm.com>
> Cc: gdb-patches@sourceware.org
> 
> I don't really understand what it is you're asking here, but I think
> it might be related to overloading of the word "frame", and possibly
> the keyword "create" not being a good choice.
> 
> Sure, within the inferior there are a set of frames, some of these
> form the current stack, and some might exist on some alternative
> stack.  Those frames exist regardless of GDB's ability to visualise
> them.
> 
> However, there's a second use of the word frame, which we use for
> GDB's representation of a stack-frame.  Under this second meaning the
> only frames that exist are those GDB can derive from the current
> machine state.  So, if the user asks for a backtrace and is told about
> #0 A, #1 B, #2 C, then GDB only knows about those 3 frames.
> 
> If the user knows of #3 D that called C (but the unwind failed for
> some reason) then they can use: 'frame create STACK-ADDR PC-ADDR' to
> "create" a suitable frame and examine the machine state.  The frame
> being "created" here is really a GDB frame, that is, a representation
> of a preexisting inferior frame.

The old text was this:

> > > -@item frame @var{stack-addr} [ @var{pc-addr} ]
> > > -@itemx f @var{stack-addr} [ @var{pc-addr} ]
> > > -Select the frame at address @var{stack-addr}.  This is useful mainly if the
> > > -chaining of stack frames has been damaged by a bug, making it
> > > -impossible for @value{GDBN} to assign numbers properly to all frames.  In
> > > -addition, this can be useful when your program has multiple stacks and
> > > -switches between them.  The optional @var{pc-addr} can also be given to
> > > -specify the value of PC for the stack frame.

This seems to imply that if the frame #3 D existed, but the unwind
failed to find it, the user could say "frame STACK-ADDR PC-ADDR" to
refer to that frame.

By contrast, your new text:

> > > +@kindex frame create
> > > +@item create @var{stack-address} @r{[} @var{pc-addr} @r{]}
> > > +Create and then select a new frame at stack address @var{stack-addr}.
> > > +This is useful mainly if the chaining of stack frames has been damaged
> > > +by a bug, making it impossible for @value{GDBN} to assign numbers
> > > +properly to all frames.  In addition, this can be useful when your
> > > +program has multiple stacks and switches between them.  The optional
> > > +@var{pc-addr} can also be given to specify the value of PC for the
> > > +stack frame.

forces the use of "create", which is confusing, since the frame does
exist on the stack, albeit unbeknownst to GDB.

IOW, I always thought of stack frames as existing or not independently
of whether the GDB unwinder succeeded to find them, so it's strange
for me to talk about "creating" a frame in this use case.  For that
matter, I don't understand why we need to force the user to type
"create" explicitly.

> Anyway, I'm happy to rework the text if you can suggest some
> improvements.  Alternatively, maybe it was my choice of "create" as
> the keyword that was confusing... again, if you have any better
> suggestions I'm happy to change it, I'm not tied to "create".

Why do we need an extra keyword, again?

If we do need a keyword, how about "frame add"?

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCHv2 2/2] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-05-21 17:46       ` Eli Zaretskii
@ 2018-06-05 18:53         ` Andrew Burgess
  2018-06-05 21:16           ` Philippe Waroquiers
  0 siblings, 1 reply; 35+ messages in thread
From: Andrew Burgess @ 2018-06-05 18:53 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches

Eli,

Sorry for the delay in replying, this dropped off my radar for a while
with other things...

* Eli Zaretskii <eliz@gnu.org> [2018-05-21 19:32:33 +0300]:

> > Date: Mon, 21 May 2018 12:53:57 +0100
> > From: Andrew Burgess <andrew.burgess@embecosm.com>
> > Cc: gdb-patches@sourceware.org
> > 
> > I don't really understand what it is you're asking here, but I think
> > it might be related to overloading of the word "frame", and possibly
> > the keyword "create" not being a good choice.
> > 
> > Sure, within the inferior there are a set of frames, some of these
> > form the current stack, and some might exist on some alternative
> > stack.  Those frames exist regardless of GDB's ability to visualise
> > them.
> > 
> > However, there's a second use of the word frame, which we use for
> > GDB's representation of a stack-frame.  Under this second meaning the
> > only frames that exist are those GDB can derive from the current
> > machine state.  So, if the user asks for a backtrace and is told about
> > #0 A, #1 B, #2 C, then GDB only knows about those 3 frames.
> > 
> > If the user knows of #3 D that called C (but the unwind failed for
> > some reason) then they can use: 'frame create STACK-ADDR PC-ADDR' to
> > "create" a suitable frame and examine the machine state.  The frame
> > being "created" here is really a GDB frame, that is, a representation
> > of a preexisting inferior frame.
> 
> The old text was this:
> 
> > > > -@item frame @var{stack-addr} [ @var{pc-addr} ]
> > > > -@itemx f @var{stack-addr} [ @var{pc-addr} ]
> > > > -Select the frame at address @var{stack-addr}.  This is useful mainly if the
> > > > -chaining of stack frames has been damaged by a bug, making it
> > > > -impossible for @value{GDBN} to assign numbers properly to all frames.  In
> > > > -addition, this can be useful when your program has multiple stacks and
> > > > -switches between them.  The optional @var{pc-addr} can also be given to
> > > > -specify the value of PC for the stack frame.
> 
> This seems to imply that if the frame #3 D existed, but the unwind
> failed to find it, the user could say "frame STACK-ADDR PC-ADDR" to
> refer to that frame.
> 
> By contrast, your new text:
> 
> > > > +@kindex frame create
> > > > +@item create @var{stack-address} @r{[} @var{pc-addr} @r{]}
> > > > +Create and then select a new frame at stack address @var{stack-addr}.
> > > > +This is useful mainly if the chaining of stack frames has been damaged
> > > > +by a bug, making it impossible for @value{GDBN} to assign numbers
> > > > +properly to all frames.  In addition, this can be useful when your
> > > > +program has multiple stacks and switches between them.  The optional
> > > > +@var{pc-addr} can also be given to specify the value of PC for the
> > > > +stack frame.
> 
> forces the use of "create", which is confusing, since the frame does
> exist on the stack, albeit unbeknownst to GDB.
> 
> IOW, I always thought of stack frames as existing or not independently
> of whether the GDB unwinder succeeded to find them, so it's strange
> for me to talk about "creating" a frame in this use case.

Sure. I think our mental models for stack frames and GDB are pretty
much in sync.  Like I previously tried to explain the "create" here is
only "creating" within GDB, not within the inferior....

>                                                            For that
> matter, I don't understand why we need to force the user to type
> "create" explicitly.

Because, whether we call it "creating" or "viewing an existing frame
that is in the inferior, but not in the backtrace", I do think that
there are two _very similar_ but different actions here.

One action is that the user wants to select a frame that GDB is aware
of, and is part of the backtrace.

The second action is that the user wants to use a $sp and $pc value to
visualise a stack frame that is not part of GDB's backtrace.

My assumption going into this work is that the first case (selecting
by number) is what most users do, most of the time, and that the
second case is much rarer.

By forcing the second case to live behind a new keyword we prevent the
user from accidentally visualising a new stack frame in the case where
they miss-type the stack frame number.  Under the new model,
miss-typing a frame number would give an error.

> 
> > Anyway, I'm happy to rework the text if you can suggest some
> > improvements.  Alternatively, maybe it was my choice of "create" as
> > the keyword that was confusing... again, if you have any better
> > suggestions I'm happy to change it, I'm not tied to "create".
> 
> Why do we need an extra keyword, again?
> 
> If we do need a keyword, how about "frame add"?

Personally, I think 'add' is worse than 'create' - what's the frame
being added too?  But I do acknowledge that 'create' is not ideal
either.

I wonder if 'new' is better than 'create', maybe implies less "making
something in the inferior"?  Or how about, 'for' instead, like this:

  (gdb) frame for STACK-ADDR PC-ADDR

Alternatively, could this problem be solved by a better description in
the manual, maybe something like this:

    Create within GDB a representation of a stack frame that is not
    part of the current backtrace.  The newly created frame has a
    stack address @var{stack-addr}, and an optional program counter
    address @var{pc-addr}.

    This is useful mainly if the chaining of stack frames has been
    damaged by a bug, making it impossible for @value{GDBN} to assign
    numbers properly to all frames.  In addition, this can be useful
    when your program has multiple stacks and switches between them.

Thanks for all your help reviewing this patch.

Andrew

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCHv2 2/2] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-06-05 18:53         ` Andrew Burgess
@ 2018-06-05 21:16           ` Philippe Waroquiers
  2018-06-06  8:22             ` Andrew Burgess
  0 siblings, 1 reply; 35+ messages in thread
From: Philippe Waroquiers @ 2018-06-05 21:16 UTC (permalink / raw)
  To: Andrew Burgess, Eli Zaretskii; +Cc: gdb-patches

On Tue, 2018-06-05 at 19:53 +0100, Andrew Burgess wrote:
> > If we do need a keyword, how about "frame add"?
> 
> Personally, I think 'add' is worse than 'create' - what's the frame
> being added too?  But I do acknowledge that 'create' is not ideal
> either.
> 
> I wonder if 'new' is better than 'create', maybe implies less "making
> something in the inferior"?  Or how about, 'for' instead, like this:
> 
>   (gdb) frame for STACK-ADDR PC-ADDR

If nothing is added or created or ...,
maybe you could also use one of:
    (gdb) frame interpret STACK-ADDR PC-ADDR
or
    (gdb) frame look STACK-ADDR PC-ADDR
or
    (gdb) frame view STACK-ADDR PC-ADDR
?

Philippe

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCHv2 2/2] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-06-05 21:16           ` Philippe Waroquiers
@ 2018-06-06  8:22             ` Andrew Burgess
  2018-06-06 14:56               ` Eli Zaretskii
  0 siblings, 1 reply; 35+ messages in thread
From: Andrew Burgess @ 2018-06-06  8:22 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Philippe Waroquiers, gdb-patches

* Philippe Waroquiers <philippe.waroquiers@skynet.be> [2018-06-05 23:15:16 +0200]:

> On Tue, 2018-06-05 at 19:53 +0100, Andrew Burgess wrote:
> > > If we do need a keyword, how about "frame add"?
> > 
> > Personally, I think 'add' is worse than 'create' - what's the frame
> > being added too?  But I do acknowledge that 'create' is not ideal
> > either.
> > 
> > I wonder if 'new' is better than 'create', maybe implies less "making
> > something in the inferior"?  Or how about, 'for' instead, like this:
> > 
> >   (gdb) frame for STACK-ADDR PC-ADDR
> 
> If nothing is added or created or ...,
> maybe you could also use one of:
>     (gdb) frame interpret STACK-ADDR PC-ADDR
> or
>     (gdb) frame look STACK-ADDR PC-ADDR
> or
>     (gdb) frame view STACK-ADDR PC-ADDR

I like 'view', the docs could read:

    View a frame that is not part of GDB backtrace.  The frame viewed
    has stack address @var{stack-addr}, and optionally, a program
    counter address of @var{pc-addr}.

    This is useful mainly if the chaining of stack frames has been
    damaged by a bug, making it impossible for @value{GDBN} to assign
    numbers properly to all frames.  In addition, this can be useful
    when your program has multiple stacks and switches between them.

Eli, how does this sound?

Thanks,
Andrew

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCHv2 2/2] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-06-06  8:22             ` Andrew Burgess
@ 2018-06-06 14:56               ` Eli Zaretskii
  0 siblings, 0 replies; 35+ messages in thread
From: Eli Zaretskii @ 2018-06-06 14:56 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: philippe.waroquiers, gdb-patches

> Date: Wed, 6 Jun 2018 09:22:11 +0100
> From: Andrew Burgess <andrew.burgess@embecosm.com>
> Cc: Philippe Waroquiers <philippe.waroquiers@skynet.be>,
> 	gdb-patches@sourceware.org
> 
>     View a frame that is not part of GDB backtrace.  The frame viewed
>     has stack address @var{stack-addr}, and optionally, a program
>     counter address of @var{pc-addr}.
> 
>     This is useful mainly if the chaining of stack frames has been
>     damaged by a bug, making it impossible for @value{GDBN} to assign
>     numbers properly to all frames.  In addition, this can be useful
>     when your program has multiple stacks and switches between them.
> 
> Eli, how does this sound?

Sounds good (modulo "@value{GDBN}" instead of "GDB").

Thanks.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCHv3] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-05-08 16:59 ` [PATCHv2 2/2] gdb: Change how frames are selected for 'frame' and 'info frame' Andrew Burgess
  2018-05-11 15:44   ` Eli Zaretskii
@ 2018-06-07 16:19   ` Andrew Burgess
  2018-06-29 12:23     ` Andrew Burgess
  2018-07-17 15:58     ` [PATCHv4] " Andrew Burgess
  1 sibling, 2 replies; 35+ messages in thread
From: Andrew Burgess @ 2018-06-07 16:19 UTC (permalink / raw)
  To: gdb-patches; +Cc: philippe.waroquiers, eliz, Andrew Burgess

Thanks to Eli and Philippe for their feedback.  This version is
updated in line with the their guidance, rebased onto current HEAD,
and retested.

---

The 'frame' command, and thanks to code reuse the 'info frame' and
'select-frame' commands, currently have an overloaded mechanism for
selecting a frame.

These commands take one or two parameters, if it's one parameter then
we first try to use the parameter as an integer to select a frame by
level (or depth in the stack).  If that fails then we treat the
parameter as an address and try to select a stack frame by
stack-address.  If we still have not selected a stack frame, or we
initially had two parameters, then GDB allows the user to view a stack
frame that is not part of the current backtrace.  Internally, a new
frame is created with the given stack and pc addresses, and this is
shown to the user.

The result of this is that a typo by the user, entering the wrong stack
frame level for example, can result in a brand new frame being viewed
rather than an error.

The purpose of this commit is to remove this overloading, while still
offering the same functionality through some new sub-commands.  By
making the default behaviour of 'frame' (and friends) be to select a
stack frame by level number, it is hoped that enough
backwards-compatibility is maintained that users will not be overly
inconvenienced.

The 'frame', 'select-frame', and 'info frame' commands now all take a
frame specification string as an argument, this string can be any of the
following:

  (1) An integer.  This is treated as a frame number.  If a frame for
  that number does not exist then the user gets an error.

  (2) A string like 'level <NUMBER>', where <NUMBER> is a frame number
  as in option (1) above.

  (3) A string like 'address <ADDRESS>', where <ADDRESS> is a
  stack-frame address.  If there is no frame for this address then the
  user gets an error.

  (4) A string like 'function <NAME>', where <NAME> is a function name,
  the inner most frame for function <NAME> is selected.  If there is no
  frame for function <NAME> then the user gets an error.

  (5) A string like 'view <STACK-ADDRESS>', this views a new frame
  with stack address <STACK-ADDRESS>.

  (6) A string like 'view <STACK-ADDRESS> <PC-ADDRESS>', this views
  a new frame with stack address <STACK-ADDRESS> an the pc <PC-ADDRESS>.

This change assumes that the most common use of the commands like
'frame' is to select a frame by frame level number, it is for this
reason that this is the behaviour that is kept for backwards
compatibility.  Any of the alternative behaviours, which are assumed to
be less used, now require a change in user behaviour.

The MI command '-stack-select-frame' has also changed in the same way.
The default behaviour is to select a frame by level number, but the
other selection methods are also available.

gdb/ChangeLog:

	(NEWS): Mention changes to frame related commands.
	* cli/cli-decode.c (add_cmd_suppress_notification): New function.
	(add_prefix_cmd_suppress_notification): New function.
	(add_com_suppress_notification): Call
	add_cmd_suppress_notification.
	* command.h (add_cmd_suppress_notification): Declare.
	(add_prefix_cmd_suppress_notification): Declare.
	* mi/mi-cmd-stack.c (mi_cmd_stack_select_frame): Use new function
	select_frame_from_spec.
	* stack.c (find_frame_for_function): Add declaration.
	(find_frame_for_address): New function.
	(parse_frame_specification): Delete function.
	(frame_selection_by_function_completer): New function.
	(info_frame_command): Rename to...
	(info_frame_command_core): ...this, and update parameter types.
	(select_frame_command): Rename to...
	(select_frame_command_core): ...this, and update parameter types.
	(frame_command): Rename to...
	(frame_command_core): ...this, and update parameter types.
	(class frame_command_helper): New class to wrap implementations of
	frame related sub-commands.
	(select_frame_from_spec): New function.
	(frame_cmd_list): New global for sub-commands.
	(select_frame_cmd_list): New global for sub-commands.
	(info_frame_cmd_list): New global for sub-commands.
	(_initialize_stack): Register sub-commands for 'frame',
	'select-frame', and 'info frame'.
	* stack.h (select_frame_command): Delete declarationn.
	(select_frame_from_spec): Declare new function.

gdb/doc/ChangeLog:

	* gdb.texinfo (Selection): Rewrite documentation for 'frame' and
	'select-frame' commands.
	(Frame Info): Rewrite documentation for 'info frame' command.
	(GDB/MI Stack Manipulation): Update description of
	'-stack-select-frame'.

gdb/testsuite/ChangeLog:

	* gdb.base/frame-selection.exp: New file.
	* gdb.base/frame-selection.c: New file.
	* gdb.mi/mi-frame-selection.exp: New file.
	* gdb.mi/mi-frame-selection.c: New file.
---
 gdb/ChangeLog                               |  32 ++
 gdb/NEWS                                    |   8 +
 gdb/cli/cli-decode.c                        |  44 ++-
 gdb/command.h                               |  14 +
 gdb/doc/ChangeLog                           |   8 +
 gdb/doc/gdb.texinfo                         |  89 +++--
 gdb/mi/mi-cmd-stack.c                       |   4 +-
 gdb/stack.c                                 | 516 +++++++++++++++++++---------
 gdb/stack.h                                 |   2 +-
 gdb/testsuite/ChangeLog                     |   7 +
 gdb/testsuite/gdb.base/frame-selection.c    |  52 +++
 gdb/testsuite/gdb.base/frame-selection.exp  | 156 +++++++++
 gdb/testsuite/gdb.mi/mi-frame-selection.c   |  34 ++
 gdb/testsuite/gdb.mi/mi-frame-selection.exp |  89 +++++
 14 files changed, 859 insertions(+), 196 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/frame-selection.c
 create mode 100644 gdb/testsuite/gdb.base/frame-selection.exp
 create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.c
 create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index 8fb6a2ad488..3de6620e4e8 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -21,6 +21,14 @@
 * C expressions can now use _Alignof, and C++ expressions can now use
   alignof.
 
+* Changes to the "frame", "select-frame", and "info frame" CLI
+  commands, as well as the "-stack-select-frame" MI command.  These
+  commands all now take a frame specification which is either a frame
+  number, or one of the keywords 'level', 'address', 'function', or
+  'view' followed by a parameter.  Selecting a frame by address, or
+  viewing a frame outside the current backtrace now requires the use
+  of a keyword.  Selecting a frame by number is unchanged.
+
 * New commands
 
 set debug fbsd-nat
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index c8dda700ca8..fb44fc1627f 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -251,6 +251,23 @@ add_cmd (const char *name, enum command_class theclass,
   return result;
 }
 
+/* Add an element with a suppress notification to the LIST of commands.  */
+
+struct cmd_list_element *
+add_cmd_suppress_notification (const char *name, enum command_class theclass,
+			       cmd_const_cfunc_ftype *fun, const char *doc,
+			       struct cmd_list_element **list,
+			       int *suppress_notification)
+{
+  struct cmd_list_element *element;
+
+  element = add_cmd (name, theclass, fun, doc, list);
+  element->suppress_notification = suppress_notification;
+
+  return element;
+}
+
+
 /* Deprecates a command CMD.
    REPLACEMENT is the name of the command which should be used in
    place of this command, or NULL if no such command exists.
@@ -362,6 +379,25 @@ add_prefix_cmd (const char *name, enum command_class theclass,
   return c;
 }
 
+/* Like ADD_PREFIX_CMD but sets the suppress_notification pointer on the
+   new command list element.  */
+
+struct cmd_list_element *
+add_prefix_cmd_suppress_notification
+               (const char *name, enum command_class theclass,
+		cmd_const_cfunc_ftype *fun,
+		const char *doc, struct cmd_list_element **prefixlist,
+		const char *prefixname, int allow_unknown,
+		struct cmd_list_element **list,
+		int *suppress_notification)
+{
+  struct cmd_list_element *element
+    = add_prefix_cmd (name, theclass, fun, doc, prefixlist,
+		      prefixname, allow_unknown, list);
+  element->suppress_notification = suppress_notification;
+  return element;
+}
+
 /* Like add_prefix_cmd but sets the abbrev_flag on the new command.  */
 
 struct cmd_list_element *
@@ -893,12 +929,8 @@ add_com_suppress_notification (const char *name, enum command_class theclass,
 			       cmd_const_cfunc_ftype *fun, const char *doc,
 			       int *suppress_notification)
 {
-  struct cmd_list_element *element;
-
-  element = add_cmd (name, theclass, fun, doc, &cmdlist);
-  element->suppress_notification = suppress_notification;
-
-  return element;
+  return add_cmd_suppress_notification (name, theclass, fun, doc,
+					&cmdlist, suppress_notification);
 }
 
 /* Recursively walk the commandlist structures, and print out the
diff --git a/gdb/command.h b/gdb/command.h
index 3dde2475cb1..e3d55c2dcba 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -148,6 +148,12 @@ extern struct cmd_list_element *add_cmd (const char *, enum command_class,
 					 const char *,
 					 struct cmd_list_element **);
 
+extern struct cmd_list_element *add_cmd_suppress_notification
+			(const char *name, enum command_class theclass,
+			 cmd_const_cfunc_ftype *fun, const char *doc,
+			 struct cmd_list_element **list,
+			 int *suppress_notification);
+
 extern struct cmd_list_element *add_alias_cmd (const char *, const char *,
 					       enum command_class, int,
 					       struct cmd_list_element **);
@@ -165,6 +171,14 @@ extern struct cmd_list_element *add_prefix_cmd (const char *, enum command_class
 						const char *, int,
 						struct cmd_list_element **);
 
+extern struct cmd_list_element *add_prefix_cmd_suppress_notification
+			(const char *name, enum command_class theclass,
+			 cmd_const_cfunc_ftype *fun,
+			 const char *doc, struct cmd_list_element **prefixlist,
+			 const char *prefixname, int allow_unknown,
+			 struct cmd_list_element **list,
+			 int *suppress_notification);
+
 extern struct cmd_list_element *add_abbrev_prefix_cmd (const char *,
 						       enum command_class,
 						       cmd_const_cfunc_ftype *fun,
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 4968b374afe..134dcb22d7c 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -7576,21 +7576,56 @@
 @table @code
 @kindex frame@r{, selecting}
 @kindex f @r{(@code{frame})}
-@item frame @var{n}
-@itemx f @var{n}
-Select frame number @var{n}.  Recall that frame zero is the innermost
-(currently executing) frame, frame one is the frame that called the
-innermost one, and so on.  The highest-numbered frame is the one for
-@code{main}.
+@item frame @r{[} @var{frame-selection-spec} @r{]}
+@item f @r{[} @var{frame-selection-spec} @r{]}
+The @command{frame} command allows different stack frames to be
+selected.  The @var{frame-selection-spec} can be any of the following:
+
+@table @code
+@kindex frame level
+@item @var{num}
+@item level @var{num}
+Select frame number @var{num}.  Recall that frame zero is the
+innermost (currently executing) frame, frame one is the frame that
+called the innermost one, and so on.  The highest-numbered frame is
+the one for @code{main}.
+
+As this is the most common method of navigating the frame stack then
+the string @command{level} can be dropped, the following two commands
+are equivalent:
+
+@smallexample
+(@value{GDBP}) frame 3
+(@value{GDBP}) frame level 3
+@end smallexample
 
-@item frame @var{stack-addr} [ @var{pc-addr} ]
-@itemx f @var{stack-addr} [ @var{pc-addr} ]
-Select the frame at address @var{stack-addr}.  This is useful mainly if the
-chaining of stack frames has been damaged by a bug, making it
-impossible for @value{GDBN} to assign numbers properly to all frames.  In
-addition, this can be useful when your program has multiple stacks and
-switches between them.  The optional @var{pc-addr} can also be given to
-specify the value of PC for the stack frame.
+@kindex frame address
+@item address @var{stack-address}
+Select the frame with stack address @var{stack-address}.
+
+@kindex frame function
+@item function @var{function-name}
+Select the stack frame for function @var{function-name}.  If there are
+multiple stack frames for function @var{function-name} then the inner
+most stack frame is selected.
+
+@kindex frame view
+@item view @var{stack-address} @r{[} @var{pc-addr} @r{]}
+View a frame that is not part of @value{GDBN}'s backtrace.  The frame
+viewed has stack address @var{stack-addr}, and optionally, a program
+counter address of @var{pc-addr}.
+
+This is useful mainly if the chaining of stack frames has been
+damaged by a bug, making it impossible for @value{GDBN} to assign
+numbers properly to all frames.  In addition, this can be useful
+when your program has multiple stacks and switches between them.
+
+When viewing a frame outside the current backtrace using
+@command{frame view} then you can always return to the original
+stack using one of the previous stack frame selection instructions,
+for example @command{frame level 0}.
+
+@end table
 
 @kindex up
 @item up @var{n}
@@ -7633,11 +7668,13 @@
 
 @table @code
 @kindex select-frame
-@item select-frame
+@item select-frame @r{[} @var{frame-selection-spec} @r{]}
 The @code{select-frame} command is a variant of @code{frame} that does
 not display the new frame after selecting it.  This command is
 intended primarily for use in @value{GDBN} command scripts, where the
-output might be unnecessary and distracting.
+output might be unnecessary and distracting.  The
+@var{frame-selection-spec} is as for the @command{frame} command
+described in @ref{Selection, ,Selecting a Frame}.
 
 @kindex down-silently
 @kindex up-silently
@@ -7695,13 +7732,12 @@
 something has gone wrong that has made the stack format fail to fit
 the usual conventions.
 
-@item info frame @var{addr}
-@itemx info f @var{addr}
-Print a verbose description of the frame at address @var{addr}, without
-selecting that frame.  The selected frame remains unchanged by this
-command.  This requires the same kind of address (more than one for some
-architectures) that you specify in the @code{frame} command.
-@xref{Selection, ,Selecting a Frame}.
+@item info frame @r{[} @var{frame-selection-spec} @r{]}
+@itemx info f @r{[} @var{frame-selection-spec} @r{]}
+Print a verbose description of the frame selected by
+@var{frame-selection-spec}.  The @var{frame-selection-spec} is the
+same as for the @command{frame} command (@pxref{Selection, ,Selecting
+a Frame}).  The selected frame remains unchanged by this command.
 
 @kindex info args
 @item info args
@@ -30010,11 +30046,12 @@
 @subsubheading Synopsis
 
 @smallexample
- -stack-select-frame @var{framenum}
+ -stack-select-frame @var{frame-selection-spec}
 @end smallexample
 
-Change the selected frame.  Select a different frame @var{framenum} on
-the stack.
+Change the selected frame, the @var{frame-selection-spec} is as for
+the @command{frame} command described in @ref{Selection, ,Selecting a
+Frame}.
 
 This command in deprecated in favor of passing the @samp{--frame}
 option to every command.
diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c
index 52660bdd498..33ac7663ba8 100644
--- a/gdb/mi/mi-cmd-stack.c
+++ b/gdb/mi/mi-cmd-stack.c
@@ -681,10 +681,8 @@ list_args_or_locals (enum what_to_list what, enum print_values values,
 void
 mi_cmd_stack_select_frame (const char *command, char **argv, int argc)
 {
-  if (argc == 0 || argc > 1)
+  if (!select_frame_from_spec (argv, argc, 0))
     error (_("-stack-select-frame: Usage: FRAME_SPEC"));
-
-  select_frame_command (argv[0], 1 /* not used */ );
 }
 
 void
diff --git a/gdb/stack.c b/gdb/stack.c
index 97ebc8bc23f..ab7d7e19f2a 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -102,6 +102,9 @@ static void set_last_displayed_sal (int valid,
 				    struct symtab *symtab,
 				    int line);
 
+static struct frame_info *find_frame_for_function (const char *);
+static struct frame_info *find_frame_for_address (CORE_ADDR);
+
 /* Zero means do things normally; we are interacting directly with the
    user.  One means print the full filename and linenumber when a
    frame is printed, and do so in a format emacs18/emacs19.22 can
@@ -1255,134 +1258,34 @@ print_frame (struct frame_info *frame, int print_level,
 }
 \f
 
-/* Read a frame specification in whatever the appropriate format is from
-   FRAME_EXP.  Call error() if the specification is in any way invalid (so
-   this function never returns NULL).  When SELECTED_FRAME_P is non-NULL
-   set its target to indicate that the default selected frame was used.  */
+/* Completion function for "frame function", "info frame function", and
+   "select-frame function" commands.  */
 
-static struct frame_info *
-parse_frame_specification (const char *frame_exp, int *selected_frame_p)
+void
+frame_selection_by_function_completer (struct cmd_list_element *ignore,
+				       completion_tracker &tracker,
+				       const char *text, const char *word)
 {
-  int numargs;
-  struct value *args[4];
-  CORE_ADDR addrs[ARRAY_SIZE (args)];
-
-  if (frame_exp == NULL)
-    numargs = 0;
-  else
-    {
-      numargs = 0;
-      while (1)
-	{
-	  const char *p;
-
-	  /* Skip leading white space, bail of EOL.  */
-	  frame_exp = skip_spaces (frame_exp);
-	  if (!*frame_exp)
-	    break;
-
-	  /* Parse the argument, extract it, save it.  */
-	  for (p = frame_exp;
-	       *p && !ISSPACE (*p);
-	       p++);
-	  std::string addr_string (frame_exp, p - frame_exp);
-	  frame_exp = p;
-	  
-	  /* NOTE: Parse and evaluate expression, but do not use
-	     functions such as parse_and_eval_long or
-	     parse_and_eval_address to also extract the value.
-	     Instead value_as_long and value_as_address are used.
-	     This avoids problems with expressions that contain
-	     side-effects.  */
-	  if (numargs >= ARRAY_SIZE (args))
-	    error (_("Too many args in frame specification"));
-	  args[numargs++] = parse_and_eval (addr_string.c_str ());
-	}
-    }
-
-  /* If no args, default to the selected frame.  */
-  if (numargs == 0)
-    {
-      if (selected_frame_p != NULL)
-	(*selected_frame_p) = 1;
-      return get_selected_frame (_("No stack."));
-    }
-
-  /* None of the remaining use the selected frame.  */
-  if (selected_frame_p != NULL)
-    (*selected_frame_p) = 0;
-
-  /* Assume the single arg[0] is an integer, and try using that to
-     select a frame relative to current.  */
-  if (numargs == 1)
-    {
-      struct frame_info *fid;
-      int level = value_as_long (args[0]);
-
-      fid = find_relative_frame (get_current_frame (), &level);
-      if (level == 0)
-	/* find_relative_frame was successful.  */
-	return fid;
-    }
-
-  /* Convert each value into a corresponding address.  */
-  {
-    int i;
-
-    for (i = 0; i < numargs; i++)
-      addrs[i] = value_as_address (args[i]);
-  }
-
-  /* Assume that the single arg[0] is an address, use that to identify
-     a frame with a matching ID.  Should this also accept stack/pc or
-     stack/pc/special.  */
-  if (numargs == 1)
-    {
-      struct frame_id id = frame_id_build_wild (addrs[0]);
-      struct frame_info *fid;
-
-      /* If (s)he specifies the frame with an address, he deserves
-	 what (s)he gets.  Still, give the highest one that matches.
-	 (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't
-	 know).  */
-      for (fid = get_current_frame ();
-	   fid != NULL;
-	   fid = get_prev_frame (fid))
-	{
-	  if (frame_id_eq (id, get_frame_id (fid)))
-	    {
-	      struct frame_info *prev_frame;
-
-	      while (1)
-		{
-		  prev_frame = get_prev_frame (fid);
-		  if (!prev_frame
-		      || !frame_id_eq (id, get_frame_id (prev_frame)))
-		    break;
-		  fid = prev_frame;
-		}
-	      return fid;
-	    }
-	}
-      }
-
-  /* We couldn't identify the frame as an existing frame, but
-     perhaps we can create one with a single argument.  */
-  if (numargs == 1)
-    return create_new_frame (addrs[0], 0);
-  else if (numargs == 2)
-    return create_new_frame (addrs[0], addrs[1]);
-  else
-    error (_("Too many args in frame specification"));
+  /* This is used to complete function names within a stack.  It would be
+     nice if instead of offering all available function names, we only
+     offered functions that were actually in the stack.  However, this
+     would probably mean unwinding the stack to completion, which could
+     take too long (or on a corrupted stack, possibly not end).  For now I
+     offer all symbol names as a safer choice.  */
+  collect_symbol_completion_matches (tracker,
+				     complete_symbol_mode::EXPRESSION,
+				     symbol_name_match_type::EXPRESSION,
+				     text, word);
 }
 
-/* Print verbosely the selected frame or the frame at address
-   ADDR_EXP.  Absolutely all information in the frame is printed.  */
+/* Core of all the "info frame" sub-commands.  Print verbosely the frame FI
+   if SELECTED_FRAME_P is true then frame FI is the current frame, which
+   was selected as a default due to the user not providing any arguments
+   to select some other frame.  */
 
 static void
-info_frame_command (const char *addr_exp, int from_tty)
+info_frame_command_core (struct frame_info *fi, bool selected_frame_p)
 {
-  struct frame_info *fi;
   struct symbol *func;
   struct symtab *s;
   struct frame_info *calling_frame_info;
@@ -1390,7 +1293,6 @@ info_frame_command (const char *addr_exp, int from_tty)
   const char *funname = 0;
   enum language funlang = language_unknown;
   const char *pc_regname;
-  int selected_frame_p;
   struct gdbarch *gdbarch;
   CORE_ADDR frame_pc;
   int frame_pc_p;
@@ -1398,7 +1300,6 @@ info_frame_command (const char *addr_exp, int from_tty)
   CORE_ADDR caller_pc = 0;
   int caller_pc_p = 0;
 
-  fi = parse_frame_specification (addr_exp, &selected_frame_p);
   gdbarch = get_frame_arch (fi);
 
   /* Name of the value returned by get_frame_pc().  Per comments, "pc"
@@ -1687,6 +1588,140 @@ info_frame_command (const char *addr_exp, int from_tty)
   }
 }
 
+/* The core of all the "select-frame" sub-commands.  Just wraps a call to
+   SELECT_FRAME.  */
+
+static void
+select_frame_command_core (struct frame_info *fi, bool ignored)
+{
+  struct frame_info *prev_frame = get_selected_frame_if_set ();
+  select_frame (fi);
+  if (get_selected_frame_if_set () != prev_frame)
+    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
+}
+
+/* The core of all the "frame" sub-commands.  Select frame FI, and if this
+   means we change frame send out a change notification (otherwise, just
+   reprint the current frame summary).   */
+
+static void
+frame_command_core (struct frame_info *fi, bool ignored)
+{
+  struct frame_info *prev_frame = get_selected_frame_if_set ();
+
+  select_frame (fi);
+  if (get_selected_frame_if_set () != prev_frame)
+    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
+  else
+    print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME);
+}
+
+/* The three commands 'frame', 'select-frame', and 'info frame' all have a
+   common set of sub-commands that allow a specific frame to be selected.
+   All of the sub-command functions are static methods within this class
+   template which is then instantiated below.  */
+
+template <void (*FPTR) (struct frame_info *, bool)>
+class frame_command_helper
+{
+public:
+
+  /* The "frame level" family of commands.  The ARG is a number that is
+     the frame's level in the stack.  */
+  static void
+  level (const char *arg, int from_tty)
+  {
+    int level = value_as_long (parse_and_eval (arg));
+    struct frame_info *fid
+      = find_relative_frame (get_current_frame (), &level);
+    if (level != 0)
+      error (_("No frame at level %s."), arg);
+    FPTR (fid, false);
+  }
+
+  /* The "frame address" family of commands.  ARG is a stack-pointer
+     address for an existing frame.  This command does not allow new
+     frames to be created.  */
+
+  static void
+  address (const char *arg, int from_tty)
+  {
+    CORE_ADDR addr = value_as_address (parse_and_eval (arg));
+    struct frame_info *fid = find_frame_for_address (addr);
+    if (fid == NULL)
+      error (_("No frame at address %s."), arg);
+    FPTR (fid, false);
+  }
+
+  /* The "frame view" family of commands.  ARG is one or two addresses and
+     is used to view a frame that might be outside the current backtrace.
+     The addresses are stack-pointer address, and (optional) pc-address.  */
+
+  static void
+  view (const char *args, int from_tty)
+  {
+    struct frame_info *fid;
+
+    if (args == NULL)
+    error (_("Missing address argument to view a frame"));
+
+    gdb_argv argv (args);
+
+    if (argv.count () == 2)
+      {
+	CORE_ADDR addr[2];
+
+	addr [0] = value_as_address (parse_and_eval (argv[0]));
+	addr [1] = value_as_address (parse_and_eval (argv[1]));
+	fid = create_new_frame (addr[0], addr[1]);
+      }
+    else
+      {
+	CORE_ADDR addr = value_as_address (parse_and_eval (argv[0]));
+	fid = create_new_frame (addr, false);
+      }
+    FPTR (fid, false);
+  }
+
+  /* The "frame function" family of commands.  ARG is the name of a
+     function within the stack, the first function (searching from frame
+     0) with that name will be selected.  */
+
+  static void
+  function (const char *arg, int from_tty)
+  {
+    if (arg == NULL)
+      error (_("Missing function name argument"));
+    struct frame_info *fid = find_frame_for_function (arg);
+    if (fid == NULL)
+      error (_("No frame for function \"%s\"."), arg);
+    FPTR (fid, false);
+  }
+
+  /* The "frame" base command, that is, when no sub-command is specified.
+     If one argument is provided then we assume that this is a frame level
+     number as historically, this was the supported command syntax that was
+     used most often.
+
+     If no argument is provided, then the current frame is selected.  */
+
+  static void
+  base_command (const char *arg, int from_tty)
+  {
+    if (arg == NULL)
+      FPTR (get_selected_frame (_("No stack.")), true);
+    else
+      level (arg, from_tty);
+  }
+};
+
+/* Instantiate three FRAME_COMMAND_HELPER instances to implement the
+   sub-commands for 'info frame', 'frame', and 'select-frame' commands.  */
+
+static frame_command_helper <info_frame_command_core> info_frame_cmd;
+static frame_command_helper <frame_command_core> frame_cmd;
+static frame_command_helper <select_frame_command_core> select_frame_cmd;
+
 /* Print briefly all stack frames or just the innermost COUNT_EXP
    frames.  */
 
@@ -2193,37 +2228,43 @@ find_relative_frame (struct frame_info *frame, int *level_offset_ptr)
   return frame;
 }
 
-/* The "select_frame" command.  With no argument this is a NOP.
-   Select the frame at level LEVEL_EXP if it is a valid level.
-   Otherwise, treat LEVEL_EXP as an address expression and select it.
+/* Select a frame using ARGV.  This is used from the MI -stack-select-frame
+   to provide the same frame specification mechanism that the CLI has for
+   commands like 'frame'.  The return value is true if the contents of
+   ARGV looked like a sensible attempt to change the frame (an error might
+   still be thrown though), or false if the contents of ARGV are not a
+   correct frame specification.  */
 
-   See parse_frame_specification for more info on proper frame
-   expressions.  */
-
-void
-select_frame_command (const char *level_exp, int from_tty)
+bool
+select_frame_from_spec (char **argv, int argc, int from_tty)
 {
-  struct frame_info *prev_frame = get_selected_frame_if_set ();
-
-  select_frame (parse_frame_specification (level_exp, NULL));
-  if (get_selected_frame_if_set () != prev_frame)
-    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
-}
-
-/* The "frame" command.  With no argument, print the selected frame
-   briefly.  With an argument, behave like select_frame and then print
-   the selected frame.  */
+  if (argc == 1)
+    select_frame_cmd.level (argv[0], from_tty);
+  else if (argc == 2
+	   && strncasecmp ("level", argv[0], strlen (argv[0])) == 0)
+    select_frame_cmd.level (argv[1], from_tty);
+  else if (argc == 2
+	   && strncasecmp ("address", argv[0], strlen (argv[0])) == 0)
+    select_frame_cmd.address (argv[1], from_tty);
+  else if (argc == 2
+	   && strncasecmp ("function", argv[0], strlen (argv[0])) == 0)
+    select_frame_cmd.function (argv[1], from_tty);
+  else if ((argc == 2 || argc == 3)
+	   && strncasecmp ("view", argv[0], strlen (argv[0])) == 0)
+    {
+      std::string arg;
 
-static void
-frame_command (const char *level_exp, int from_tty)
-{
-  struct frame_info *prev_frame = get_selected_frame_if_set ();
+      if (argc == 2)
+	arg = string_printf ("%s", argv[1]);
+      else
+	arg = string_printf ("%s %s", argv[1], argv[2]);
 
-  select_frame (parse_frame_specification (level_exp, NULL));
-  if (get_selected_frame_if_set () != prev_frame)
-    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
+      select_frame_cmd.view (arg.c_str (), from_tty);
+    }
   else
-    print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME);
+    return false;
+
+  return true;
 }
 
 /* Select the frame up one or COUNT_EXP stack levels from the
@@ -2494,9 +2535,59 @@ func_command (const char *arg, int from_tty)
     }
 }
 
+/* Find inner-mode frame with frame address ADDRESS.  Return NULL if no
+   matching frame can be found.  */
+
+static struct frame_info *
+find_frame_for_address (CORE_ADDR address)
+{
+  struct frame_id id;
+  struct frame_info *fid;
+
+  id = frame_id_build_wild (address);
+
+  /* If (s)he specifies the frame with an address, he deserves
+     what (s)he gets.  Still, give the highest one that matches.
+     (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't
+     know).  */
+  for (fid = get_current_frame ();
+       fid != NULL;
+       fid = get_prev_frame (fid))
+    {
+      if (frame_id_eq (id, get_frame_id (fid)))
+	{
+	  struct frame_info *prev_frame;
+
+	  while (1)
+	    {
+	      prev_frame = get_prev_frame (fid);
+	      if (!prev_frame
+		  || !frame_id_eq (id, get_frame_id (prev_frame)))
+		break;
+	      fid = prev_frame;
+	    }
+	  return fid;
+	}
+    }
+  return NULL;
+}
+
+\f
+
+/* Commands with a prefix of `frame'.  */
+static struct cmd_list_element *frame_cmd_list = NULL;
+
+/* Commands with a prefix of `frame'.  */
+static struct cmd_list_element *select_frame_cmd_list = NULL;
+
+/* Commands with a prefix of `info frame'.  */
+static struct cmd_list_element *info_frame_cmd_list = NULL;
+
 void
 _initialize_stack (void)
 {
+  struct cmd_list_element *cmd;
+
   add_com ("return", class_stack, return_command, _("\
 Make selected stack frame return to its caller.\n\
 Control remains in the debugger, but when you continue\n\
@@ -2519,20 +2610,89 @@ An argument says how many frames down to go."));
 Same as the `down' command, but does not print anything.\n\
 This is useful in command scripts."));
 
-  add_com ("frame", class_stack, frame_command, _("\
-Select and print a stack frame.\nWith no argument, \
-print the selected stack frame.  (See also \"info frame\").\n\
-An argument specifies the frame to select.\n\
-It can be a stack frame number or the address of the frame."));
-
+  add_prefix_cmd ("frame", class_stack,
+		  &frame_cmd.base_command, _("\
+Select and print a stack frame.\n\
+With no argument, print the selected stack frame.  (See also \"info frame\").\n\
+A single numerical argument specifies the frame to select."),
+		  &frame_cmd_list, "frame ", 1, &cmdlist);
   add_com_alias ("f", "frame", class_stack, 1);
 
-  add_com_suppress_notification ("select-frame", class_stack, select_frame_command, _("\
+  add_cmd ("address", class_stack, &frame_cmd.address,
+	   _("\
+Select and print a stack frame by stack address\n\
+\n\
+Usage: frame address STACK-ADDRESS"),
+	   &frame_cmd_list);
+
+  add_cmd ("view", class_stack, &frame_cmd.view,
+	   _("\
+View a stack frame that might be outside the current backtrace.\n\
+\n\
+Usage: frame view STACK-ADDRESS\n\
+       frame view STACK-ADDRESS PC-ADDRESS"),
+	   &frame_cmd_list);
+
+  cmd = add_cmd ("function", class_stack, &frame_cmd.function,
+	   _("\
+Select and print a stack frame by function name.\n\
+\n\
+Usage: frame function NAME\n\
+\n\
+The innermost frame that visited function NAME is selected."),
+	   &frame_cmd_list);
+  set_cmd_completer (cmd, frame_selection_by_function_completer);
+
+
+  add_cmd ("level", class_stack, &frame_cmd.level,
+	   _("\
+Select and print a stack frame by level.\n\
+\n\
+Usage: frame level NUMBER"),
+	   &frame_cmd_list);
+
+  cmd = add_prefix_cmd_suppress_notification ("select-frame", class_stack,
+		      &select_frame_cmd.base_command, _("\
 Select a stack frame without printing anything.\n\
-An argument specifies the frame to select.\n\
-It can be a stack frame number or the address of the frame."),
+A single numerical argument specifies the frame to select."),
+		      &select_frame_cmd_list, "select-frame ", 1, &cmdlist,
+		      &cli_suppress_notification.user_selected_context);
+
+  add_cmd_suppress_notification ("address", class_stack,
+			 &select_frame_cmd.address, _("\
+Select a stack frame by stack address.\n\
+\n\
+Usage: select-frame address STACK-ADDRESS"),
+			 &select_frame_cmd_list,
+			 &cli_suppress_notification.user_selected_context);
+
+
+  add_cmd_suppress_notification ("view", class_stack,
+		 &select_frame_cmd.view, _("\
+Select a stack frame that might be outside the current backtrace.\n\
+\n\
+Usage: select-frame view STACK-ADDRESS\n\
+       select-frame view STACK-ADDRESS PC-ADDRESS"),
+		 &select_frame_cmd_list,
 		 &cli_suppress_notification.user_selected_context);
 
+  cmd = add_cmd_suppress_notification ("function", class_stack,
+	       &select_frame_cmd.function, _("\
+Select a stack frame by function name.\n\
+\n\
+Usage: select-frame function NAME"),
+	       &select_frame_cmd_list,
+	       &cli_suppress_notification.user_selected_context);
+  set_cmd_completer (cmd, frame_selection_by_function_completer);
+
+  add_cmd_suppress_notification ("level", class_stack,
+			 &select_frame_cmd.level, _("\
+Select a stack frame by level.\n\
+\n\
+Usage: select-frame level NUMBER"),
+			 &select_frame_cmd_list,
+			 &cli_suppress_notification.user_selected_context);
+
   add_com ("backtrace", class_stack, backtrace_command, _("\
 Print backtrace of all stack frames, or innermost COUNT frames.\n\
 Usage: backtrace [QUALIFIERS]... [COUNT]\n\
@@ -2546,9 +2706,45 @@ on this backtrace."));
   add_info ("stack", backtrace_command,
 	    _("Backtrace of the stack, or innermost COUNT frames."));
   add_info_alias ("s", "stack", 1);
-  add_info ("frame", info_frame_command,
-	    _("All about selected stack frame, or frame at ADDR."));
+
+  add_prefix_cmd ("frame", class_info, &info_frame_cmd.base_command,
+		  _("All about the selected stack frame.\n\
+With no arguments, displays information about the currently selected stack\n\
+frame.  Alternatively a frame specification may be provided (See \"frame\")\n\
+the information is then printed about the specified frame."),
+		  &info_frame_cmd_list, "info frame ", 1, &infolist);
   add_info_alias ("f", "frame", 1);
+
+  add_cmd ("address", class_stack, &info_frame_cmd.address,
+	   _("\
+Print information about a stack frame selected by stack address.\n\
+\n\
+Usage: info frame address STACK-ADDRESS"),
+	   &info_frame_cmd_list);
+
+  add_cmd ("view", class_stack, &info_frame_cmd.view,
+	   _("\
+Print information about a stack frame outside the current backtrace.\n\
+\n\
+Usage: info frame view STACK-ADDRESS\n\
+       info frame view STACK-ADDRESS PC-ADDRESS"),
+	   &info_frame_cmd_list);
+
+  cmd = add_cmd ("function", class_stack, &info_frame_cmd.function,
+	   _("\
+Print information about a stack frame selected by function name.\n\
+\n\
+Usage: info frame function NAME"),
+	   &info_frame_cmd_list);
+  set_cmd_completer (cmd, frame_selection_by_function_completer);
+
+  add_cmd ("level", class_stack, &info_frame_cmd.level,
+	   _("\
+Print information about a stack frame selected by level.\n\
+\n\
+Usage: info frame level NUMBER"),
+	   &info_frame_cmd_list);
+
   add_info ("locals", info_locals_command,
 	    _("Local variables of current stack frame."));
   add_info ("args", info_args_command,
diff --git a/gdb/stack.h b/gdb/stack.h
index ca190efa9c3..29794872d70 100644
--- a/gdb/stack.h
+++ b/gdb/stack.h
@@ -20,7 +20,7 @@
 #ifndef STACK_H
 #define STACK_H
 
-void select_frame_command (const char *level_exp, int from_tty);
+bool select_frame_from_spec (char **argv, int argc, int from_tty);
 
 gdb::unique_xmalloc_ptr<char> find_frame_funname (struct frame_info *frame,
 						  enum language *funlang,
diff --git a/gdb/testsuite/gdb.base/frame-selection.c b/gdb/testsuite/gdb.base/frame-selection.c
new file mode 100644
index 00000000000..f3d81f223e0
--- /dev/null
+++ b/gdb/testsuite/gdb.base/frame-selection.c
@@ -0,0 +1,52 @@
+/* Copyright 2018 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int
+frame_2 (void)
+{
+  return 0;
+}
+
+int
+frame_1 (void)
+{
+  return frame_2 ();
+}
+
+int
+recursive (int arg)
+{
+  int v;
+
+  if (arg < 2)
+    v = recursive (arg + 1);
+  else
+    v = frame_2 ();
+
+  return v;
+}
+
+int
+main (void)
+{
+  int i, j;
+
+  i = frame_1 ();
+  j = recursive (0);
+
+  return i + j;
+}
diff --git a/gdb/testsuite/gdb.base/frame-selection.exp b/gdb/testsuite/gdb.base/frame-selection.exp
new file mode 100644
index 00000000000..6df85494839
--- /dev/null
+++ b/gdb/testsuite/gdb.base/frame-selection.exp
@@ -0,0 +1,156 @@
+# Copyright 2018 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+if { [prepare_for_testing frame-cmds.exp "frame-cmds" {frame-cmds.c} {debug nowarnings}] } {
+    return -1
+}
+set srcfile frame-cmds.c
+
+runto_main
+gdb_breakpoint frame_2
+gdb_continue_to_breakpoint frame_2
+
+gdb_test "bt" "#0  frame_2.*#1  $hex in frame_1.*#2  $hex in main.*" "backtrace at breakpoint"
+
+# Perform "info frame" to extract the frames address.
+proc get_frame_address { {testname ""} } {
+    global hex gdb_prompt
+
+    set testname "get_frame_address: ${testname}"
+    set frame_address "unknown"
+
+    send_gdb "info frame\n"
+    gdb_expect {
+	-re ", frame at ($hex):\r\n.*\r\n$gdb_prompt $" {
+	    set frame_address $expect_out(1,string)
+	    pass $testname
+	}
+	-re ".*$gdb_prompt $" { fail $testname }
+	timeout { fail "$testname (timeout)" }
+    }
+
+    return $frame_address
+}
+
+# Passes a list of addresses.  Return a new address that is not in the
+# list.
+proc get_new_address { {addresses {}} } {
+    return [format "%#x" [expr [lindex $addresses [llength addresses]] + 0x10 ]]
+}
+
+proc check_frame { level address function } {
+    global hex gdb_prompt
+
+    set testname "check frame level ${level}"
+    send_gdb "info frame\n"
+    gdb_expect {
+	-re "Stack level ${level}, frame at ($address):\r\n rip = $hex in ${function} \(\[^\r\n\]*\); saved rip = $hex\r\n.*\r\n$gdb_prompt $" {
+	    pass $testname
+	}
+	-re ".*$gdb_prompt $" { fail $testname }
+	timeout { fail "$testname (timeout)" }
+    }
+}
+
+# Select frame using level number, but relying on this being the
+# default action, so "frame 0" performs "frame level 0".
+gdb_test "frame 0" "#0  frame_2.*"
+set frame_0_address [ get_frame_address "frame 0" ]
+gdb_test "frame 1" "#1  $hex in frame_1.*"
+set frame_1_address [ get_frame_address "frame 1" ]
+gdb_test "frame 2" "#2  $hex in main.*"
+set frame_2_address [ get_frame_address "frame 2" ]
+gdb_test "frame 3" "No frame at level 3\."
+
+# Find an address that matches no frame
+set no_frame_address [ get_new_address [list $frame_0_address \
+					     $frame_1_address \
+					     $frame_2_address] ]
+
+# Select frame using 'level' specification.
+gdb_test "frame level 0" "#0  frame_2.*"
+gdb_test "frame level 1" "#1  $hex in frame_1.*"
+gdb_test "frame level 2" "#2  $hex in main.*"
+gdb_test "frame level 3" "No frame at level 3\."
+
+# Select frame by address.
+gdb_test "frame address ${frame_0_address}" "#0  frame_2.*"
+gdb_test "frame address ${frame_1_address}" "#1  $hex in frame_1.*"
+gdb_test "frame address ${frame_2_address}" "#2  $hex in main.*"
+gdb_test "frame address ${no_frame_address}" \
+    "No frame at address ${no_frame_address}\."
+
+# Select frame by function.
+gdb_test "frame function frame_2" "#0  frame_2.*"
+gdb_test "frame function frame_1" "#1  $hex in frame_1.*"
+gdb_test "frame function main" "#2  $hex in main.*"
+# Check for a distinction between a known function not in the stack
+# trace, and an unknown function.
+gdb_test "frame function recursive" "No frame for function \"recursive\"."
+gdb_test "frame function foo" "Function \"foo\" not defined."
+
+
+gdb_test_no_output "select-frame 0"
+check_frame "0" "${frame_0_address}" "frame_2"
+gdb_test_no_output "select-frame 1"
+check_frame "1" "${frame_1_address}" "frame_1"
+gdb_test_no_output "select-frame 2"
+check_frame "2" "${frame_2_address}" "main"
+gdb_test "select-frame 3" "No frame at level 3\."
+
+gdb_test_no_output "select-frame level 0"
+check_frame "0" "${frame_0_address}" "frame_2"
+gdb_test_no_output "select-frame level 1"
+check_frame "1" "${frame_1_address}" "frame_1"
+gdb_test_no_output "select-frame level 2"
+check_frame "2" "${frame_2_address}" "main"
+gdb_test "select-frame level 3" "No frame at level 3\."
+
+gdb_test_no_output "select-frame address ${frame_0_address}"
+check_frame "0" "${frame_0_address}" "frame_2"
+gdb_test_no_output "select-frame address ${frame_1_address}"
+check_frame "1" "${frame_1_address}" "frame_1"
+gdb_test_no_output "select-frame address ${frame_2_address}"
+check_frame "2" "${frame_2_address}" "main"
+gdb_test "select-frame address ${no_frame_address}" \
+    "No frame at address ${no_frame_address}\."
+
+gdb_test_no_output "select-frame function frame_2"
+check_frame "0" "${frame_0_address}" "frame_2"
+gdb_test_no_output "select-frame function frame_1"
+check_frame "1" "${frame_1_address}" "frame_1"
+gdb_test_no_output "select-frame function main"
+check_frame "2" "${frame_2_address}" "main"
+# Check for a distinction between a known function not in the stack
+# trace, and an unknown function.
+gdb_test "select-frame function recursive" \
+    "No frame for function \"recursive\"."
+gdb_test "select-frame function foo" \
+    "Function \"foo\" not defined."
+
+# Now continue until we hit the breakpoint again.
+gdb_continue_to_breakpoint frame_2
+gdb_test "bt" \
+    "#0  frame_2.*#1  $hex in recursive.*#2  $hex in recursive.*#3  $hex in recursive.*#4  $hex in main.*" \
+    "backtrace at breakpoint with recursive frames"
+
+# Check "frame function" when a function name occurs multiple times in
+# the stack.  The inner most (lowest level number) should always be
+# selected.
+gdb_test "frame function frame_2" "#0  frame_2.*"
+gdb_test "frame function recursive" "#1  $hex in recursive.*"
+gdb_test "frame function recursive" "#1  $hex in recursive.*"
+gdb_test "frame function main" "#4  $hex in main.*"
+gdb_test "frame function recursive" "#1  $hex in recursive.*"
diff --git a/gdb/testsuite/gdb.mi/mi-frame-selection.c b/gdb/testsuite/gdb.mi/mi-frame-selection.c
new file mode 100644
index 00000000000..70b992c9149
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-frame-selection.c
@@ -0,0 +1,34 @@
+/* Copyright 2018 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int
+frame_2 (void)
+{
+  return 0;
+}
+
+int
+frame_1 (void)
+{
+  return frame_2 ();
+}
+
+int
+main (void)
+{
+  return frame_1 ();
+}
diff --git a/gdb/testsuite/gdb.mi/mi-frame-selection.exp b/gdb/testsuite/gdb.mi/mi-frame-selection.exp
new file mode 100644
index 00000000000..e24c7179b9d
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-frame-selection.exp
@@ -0,0 +1,89 @@
+# Copyright 2018 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test the frame selection syntax with the -stack-select-frame command.
+
+load_lib mi-support.exp
+set MIFLAGS "-i=mi"
+
+gdb_exit
+if [mi_gdb_start] {
+    continue
+}
+
+standard_testfile
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    untested $testfile
+    return -1
+}
+
+mi_delete_breakpoints
+mi_gdb_reinitialize_dir $srcdir/$subdir
+mi_gdb_load ${binfile}
+
+mi_create_breakpoint "frame_2" \
+    "break-insert into frame_2" \
+    -number 1 -func frame_2 -file ".*${srcfile}"
+
+mi_run_cmd
+mi_expect_stop "breakpoint-hit" "frame_2" "" ".*${srcfile}" \
+	".*" { "" "disp=\"keep\"" } "run to breakpoint"
+
+mi_gdb_test "-stack-info-depth" \
+    "\\^done,depth=\"3\"" \
+    "stack info-depth"
+
+for {set i 0} {$i < 5} {incr i} {
+
+    if { $i < 3 } then {
+	mi_gdb_test "-stack-select-frame $i" \
+	    {\^done} \
+	    "-stack-select-frame $i"
+
+	mi_gdb_test "-stack-info-frame" \
+	    "\\^done,frame=\\\{level=\"$i\"\[^\}\]+\\\}" \
+	    "check level has has changed to $i"
+
+    } else {
+	mi_gdb_test "-stack-select-frame view $i" \
+	    {\^done} \
+	    "-stack-select-frame view frame at $i"
+
+	mi_gdb_test "-stack-info-frame" \
+	    "\\^done,frame=\\\{level=\"0\"\[^\}\]+\\\}" \
+	    "check level has has changed to 0, when creating a frame at sp=$i"
+    }
+}
+
+mi_gdb_test "-stack-select-frame 5" \
+    {\^error,msg="No frame at level 5."} \
+    "-stack-select-frame 5, with no keyword"
+
+mi_gdb_test "-stack-select-frame level 5" \
+    {\^error,msg="No frame at level 5."} \
+    "-stack-select-frame 5, using 'level' keyword"
+
+mi_gdb_test "-stack-select-frame function main" \
+    {\^done} \
+    "-stack-select-frame select frame for function main"
+
+mi_gdb_test "-stack-info-frame" \
+    "\\^done,frame=\\\{level=\"2\"\[^\}\]+\\\}" \
+    "check level has has changed to 2 for function main"
+
+mi_gdb_test "-stack-select-frame function unknown_function" \
+    {\^error,msg=\"Function \\\"unknown_function\\\" not defined.\"} \
+    "-stack-select-frame check error on undefined function name"
-- 
2.14.3

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCHv3] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-06-07 16:19   ` [PATCHv3] " Andrew Burgess
@ 2018-06-29 12:23     ` Andrew Burgess
  2018-07-17 15:58     ` [PATCHv4] " Andrew Burgess
  1 sibling, 0 replies; 35+ messages in thread
From: Andrew Burgess @ 2018-06-29 12:23 UTC (permalink / raw)
  To: gdb-patches

Ping!

Thanks,
Andrew

* Andrew Burgess <andrew.burgess@embecosm.com> [2018-06-07 17:19:15 +0100]:

> Thanks to Eli and Philippe for their feedback.  This version is
> updated in line with the their guidance, rebased onto current HEAD,
> and retested.
> 
> ---
> 
> The 'frame' command, and thanks to code reuse the 'info frame' and
> 'select-frame' commands, currently have an overloaded mechanism for
> selecting a frame.
> 
> These commands take one or two parameters, if it's one parameter then
> we first try to use the parameter as an integer to select a frame by
> level (or depth in the stack).  If that fails then we treat the
> parameter as an address and try to select a stack frame by
> stack-address.  If we still have not selected a stack frame, or we
> initially had two parameters, then GDB allows the user to view a stack
> frame that is not part of the current backtrace.  Internally, a new
> frame is created with the given stack and pc addresses, and this is
> shown to the user.
> 
> The result of this is that a typo by the user, entering the wrong stack
> frame level for example, can result in a brand new frame being viewed
> rather than an error.
> 
> The purpose of this commit is to remove this overloading, while still
> offering the same functionality through some new sub-commands.  By
> making the default behaviour of 'frame' (and friends) be to select a
> stack frame by level number, it is hoped that enough
> backwards-compatibility is maintained that users will not be overly
> inconvenienced.
> 
> The 'frame', 'select-frame', and 'info frame' commands now all take a
> frame specification string as an argument, this string can be any of the
> following:
> 
>   (1) An integer.  This is treated as a frame number.  If a frame for
>   that number does not exist then the user gets an error.
> 
>   (2) A string like 'level <NUMBER>', where <NUMBER> is a frame number
>   as in option (1) above.
> 
>   (3) A string like 'address <ADDRESS>', where <ADDRESS> is a
>   stack-frame address.  If there is no frame for this address then the
>   user gets an error.
> 
>   (4) A string like 'function <NAME>', where <NAME> is a function name,
>   the inner most frame for function <NAME> is selected.  If there is no
>   frame for function <NAME> then the user gets an error.
> 
>   (5) A string like 'view <STACK-ADDRESS>', this views a new frame
>   with stack address <STACK-ADDRESS>.
> 
>   (6) A string like 'view <STACK-ADDRESS> <PC-ADDRESS>', this views
>   a new frame with stack address <STACK-ADDRESS> an the pc <PC-ADDRESS>.
> 
> This change assumes that the most common use of the commands like
> 'frame' is to select a frame by frame level number, it is for this
> reason that this is the behaviour that is kept for backwards
> compatibility.  Any of the alternative behaviours, which are assumed to
> be less used, now require a change in user behaviour.
> 
> The MI command '-stack-select-frame' has also changed in the same way.
> The default behaviour is to select a frame by level number, but the
> other selection methods are also available.
> 
> gdb/ChangeLog:
> 
> 	(NEWS): Mention changes to frame related commands.
> 	* cli/cli-decode.c (add_cmd_suppress_notification): New function.
> 	(add_prefix_cmd_suppress_notification): New function.
> 	(add_com_suppress_notification): Call
> 	add_cmd_suppress_notification.
> 	* command.h (add_cmd_suppress_notification): Declare.
> 	(add_prefix_cmd_suppress_notification): Declare.
> 	* mi/mi-cmd-stack.c (mi_cmd_stack_select_frame): Use new function
> 	select_frame_from_spec.
> 	* stack.c (find_frame_for_function): Add declaration.
> 	(find_frame_for_address): New function.
> 	(parse_frame_specification): Delete function.
> 	(frame_selection_by_function_completer): New function.
> 	(info_frame_command): Rename to...
> 	(info_frame_command_core): ...this, and update parameter types.
> 	(select_frame_command): Rename to...
> 	(select_frame_command_core): ...this, and update parameter types.
> 	(frame_command): Rename to...
> 	(frame_command_core): ...this, and update parameter types.
> 	(class frame_command_helper): New class to wrap implementations of
> 	frame related sub-commands.
> 	(select_frame_from_spec): New function.
> 	(frame_cmd_list): New global for sub-commands.
> 	(select_frame_cmd_list): New global for sub-commands.
> 	(info_frame_cmd_list): New global for sub-commands.
> 	(_initialize_stack): Register sub-commands for 'frame',
> 	'select-frame', and 'info frame'.
> 	* stack.h (select_frame_command): Delete declarationn.
> 	(select_frame_from_spec): Declare new function.
> 
> gdb/doc/ChangeLog:
> 
> 	* gdb.texinfo (Selection): Rewrite documentation for 'frame' and
> 	'select-frame' commands.
> 	(Frame Info): Rewrite documentation for 'info frame' command.
> 	(GDB/MI Stack Manipulation): Update description of
> 	'-stack-select-frame'.
> 
> gdb/testsuite/ChangeLog:
> 
> 	* gdb.base/frame-selection.exp: New file.
> 	* gdb.base/frame-selection.c: New file.
> 	* gdb.mi/mi-frame-selection.exp: New file.
> 	* gdb.mi/mi-frame-selection.c: New file.
> ---
>  gdb/ChangeLog                               |  32 ++
>  gdb/NEWS                                    |   8 +
>  gdb/cli/cli-decode.c                        |  44 ++-
>  gdb/command.h                               |  14 +
>  gdb/doc/ChangeLog                           |   8 +
>  gdb/doc/gdb.texinfo                         |  89 +++--
>  gdb/mi/mi-cmd-stack.c                       |   4 +-
>  gdb/stack.c                                 | 516 +++++++++++++++++++---------
>  gdb/stack.h                                 |   2 +-
>  gdb/testsuite/ChangeLog                     |   7 +
>  gdb/testsuite/gdb.base/frame-selection.c    |  52 +++
>  gdb/testsuite/gdb.base/frame-selection.exp  | 156 +++++++++
>  gdb/testsuite/gdb.mi/mi-frame-selection.c   |  34 ++
>  gdb/testsuite/gdb.mi/mi-frame-selection.exp |  89 +++++
>  14 files changed, 859 insertions(+), 196 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.base/frame-selection.c
>  create mode 100644 gdb/testsuite/gdb.base/frame-selection.exp
>  create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.c
>  create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.exp
> 
> diff --git a/gdb/NEWS b/gdb/NEWS
> index 8fb6a2ad488..3de6620e4e8 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -21,6 +21,14 @@
>  * C expressions can now use _Alignof, and C++ expressions can now use
>    alignof.
>  
> +* Changes to the "frame", "select-frame", and "info frame" CLI
> +  commands, as well as the "-stack-select-frame" MI command.  These
> +  commands all now take a frame specification which is either a frame
> +  number, or one of the keywords 'level', 'address', 'function', or
> +  'view' followed by a parameter.  Selecting a frame by address, or
> +  viewing a frame outside the current backtrace now requires the use
> +  of a keyword.  Selecting a frame by number is unchanged.
> +
>  * New commands
>  
>  set debug fbsd-nat
> diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
> index c8dda700ca8..fb44fc1627f 100644
> --- a/gdb/cli/cli-decode.c
> +++ b/gdb/cli/cli-decode.c
> @@ -251,6 +251,23 @@ add_cmd (const char *name, enum command_class theclass,
>    return result;
>  }
>  
> +/* Add an element with a suppress notification to the LIST of commands.  */
> +
> +struct cmd_list_element *
> +add_cmd_suppress_notification (const char *name, enum command_class theclass,
> +			       cmd_const_cfunc_ftype *fun, const char *doc,
> +			       struct cmd_list_element **list,
> +			       int *suppress_notification)
> +{
> +  struct cmd_list_element *element;
> +
> +  element = add_cmd (name, theclass, fun, doc, list);
> +  element->suppress_notification = suppress_notification;
> +
> +  return element;
> +}
> +
> +
>  /* Deprecates a command CMD.
>     REPLACEMENT is the name of the command which should be used in
>     place of this command, or NULL if no such command exists.
> @@ -362,6 +379,25 @@ add_prefix_cmd (const char *name, enum command_class theclass,
>    return c;
>  }
>  
> +/* Like ADD_PREFIX_CMD but sets the suppress_notification pointer on the
> +   new command list element.  */
> +
> +struct cmd_list_element *
> +add_prefix_cmd_suppress_notification
> +               (const char *name, enum command_class theclass,
> +		cmd_const_cfunc_ftype *fun,
> +		const char *doc, struct cmd_list_element **prefixlist,
> +		const char *prefixname, int allow_unknown,
> +		struct cmd_list_element **list,
> +		int *suppress_notification)
> +{
> +  struct cmd_list_element *element
> +    = add_prefix_cmd (name, theclass, fun, doc, prefixlist,
> +		      prefixname, allow_unknown, list);
> +  element->suppress_notification = suppress_notification;
> +  return element;
> +}
> +
>  /* Like add_prefix_cmd but sets the abbrev_flag on the new command.  */
>  
>  struct cmd_list_element *
> @@ -893,12 +929,8 @@ add_com_suppress_notification (const char *name, enum command_class theclass,
>  			       cmd_const_cfunc_ftype *fun, const char *doc,
>  			       int *suppress_notification)
>  {
> -  struct cmd_list_element *element;
> -
> -  element = add_cmd (name, theclass, fun, doc, &cmdlist);
> -  element->suppress_notification = suppress_notification;
> -
> -  return element;
> +  return add_cmd_suppress_notification (name, theclass, fun, doc,
> +					&cmdlist, suppress_notification);
>  }
>  
>  /* Recursively walk the commandlist structures, and print out the
> diff --git a/gdb/command.h b/gdb/command.h
> index 3dde2475cb1..e3d55c2dcba 100644
> --- a/gdb/command.h
> +++ b/gdb/command.h
> @@ -148,6 +148,12 @@ extern struct cmd_list_element *add_cmd (const char *, enum command_class,
>  					 const char *,
>  					 struct cmd_list_element **);
>  
> +extern struct cmd_list_element *add_cmd_suppress_notification
> +			(const char *name, enum command_class theclass,
> +			 cmd_const_cfunc_ftype *fun, const char *doc,
> +			 struct cmd_list_element **list,
> +			 int *suppress_notification);
> +
>  extern struct cmd_list_element *add_alias_cmd (const char *, const char *,
>  					       enum command_class, int,
>  					       struct cmd_list_element **);
> @@ -165,6 +171,14 @@ extern struct cmd_list_element *add_prefix_cmd (const char *, enum command_class
>  						const char *, int,
>  						struct cmd_list_element **);
>  
> +extern struct cmd_list_element *add_prefix_cmd_suppress_notification
> +			(const char *name, enum command_class theclass,
> +			 cmd_const_cfunc_ftype *fun,
> +			 const char *doc, struct cmd_list_element **prefixlist,
> +			 const char *prefixname, int allow_unknown,
> +			 struct cmd_list_element **list,
> +			 int *suppress_notification);
> +
>  extern struct cmd_list_element *add_abbrev_prefix_cmd (const char *,
>  						       enum command_class,
>  						       cmd_const_cfunc_ftype *fun,
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 4968b374afe..134dcb22d7c 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -7576,21 +7576,56 @@
>  @table @code
>  @kindex frame@r{, selecting}
>  @kindex f @r{(@code{frame})}
> -@item frame @var{n}
> -@itemx f @var{n}
> -Select frame number @var{n}.  Recall that frame zero is the innermost
> -(currently executing) frame, frame one is the frame that called the
> -innermost one, and so on.  The highest-numbered frame is the one for
> -@code{main}.
> +@item frame @r{[} @var{frame-selection-spec} @r{]}
> +@item f @r{[} @var{frame-selection-spec} @r{]}
> +The @command{frame} command allows different stack frames to be
> +selected.  The @var{frame-selection-spec} can be any of the following:
> +
> +@table @code
> +@kindex frame level
> +@item @var{num}
> +@item level @var{num}
> +Select frame number @var{num}.  Recall that frame zero is the
> +innermost (currently executing) frame, frame one is the frame that
> +called the innermost one, and so on.  The highest-numbered frame is
> +the one for @code{main}.
> +
> +As this is the most common method of navigating the frame stack then
> +the string @command{level} can be dropped, the following two commands
> +are equivalent:
> +
> +@smallexample
> +(@value{GDBP}) frame 3
> +(@value{GDBP}) frame level 3
> +@end smallexample
>  
> -@item frame @var{stack-addr} [ @var{pc-addr} ]
> -@itemx f @var{stack-addr} [ @var{pc-addr} ]
> -Select the frame at address @var{stack-addr}.  This is useful mainly if the
> -chaining of stack frames has been damaged by a bug, making it
> -impossible for @value{GDBN} to assign numbers properly to all frames.  In
> -addition, this can be useful when your program has multiple stacks and
> -switches between them.  The optional @var{pc-addr} can also be given to
> -specify the value of PC for the stack frame.
> +@kindex frame address
> +@item address @var{stack-address}
> +Select the frame with stack address @var{stack-address}.
> +
> +@kindex frame function
> +@item function @var{function-name}
> +Select the stack frame for function @var{function-name}.  If there are
> +multiple stack frames for function @var{function-name} then the inner
> +most stack frame is selected.
> +
> +@kindex frame view
> +@item view @var{stack-address} @r{[} @var{pc-addr} @r{]}
> +View a frame that is not part of @value{GDBN}'s backtrace.  The frame
> +viewed has stack address @var{stack-addr}, and optionally, a program
> +counter address of @var{pc-addr}.
> +
> +This is useful mainly if the chaining of stack frames has been
> +damaged by a bug, making it impossible for @value{GDBN} to assign
> +numbers properly to all frames.  In addition, this can be useful
> +when your program has multiple stacks and switches between them.
> +
> +When viewing a frame outside the current backtrace using
> +@command{frame view} then you can always return to the original
> +stack using one of the previous stack frame selection instructions,
> +for example @command{frame level 0}.
> +
> +@end table
>  
>  @kindex up
>  @item up @var{n}
> @@ -7633,11 +7668,13 @@
>  
>  @table @code
>  @kindex select-frame
> -@item select-frame
> +@item select-frame @r{[} @var{frame-selection-spec} @r{]}
>  The @code{select-frame} command is a variant of @code{frame} that does
>  not display the new frame after selecting it.  This command is
>  intended primarily for use in @value{GDBN} command scripts, where the
> -output might be unnecessary and distracting.
> +output might be unnecessary and distracting.  The
> +@var{frame-selection-spec} is as for the @command{frame} command
> +described in @ref{Selection, ,Selecting a Frame}.
>  
>  @kindex down-silently
>  @kindex up-silently
> @@ -7695,13 +7732,12 @@
>  something has gone wrong that has made the stack format fail to fit
>  the usual conventions.
>  
> -@item info frame @var{addr}
> -@itemx info f @var{addr}
> -Print a verbose description of the frame at address @var{addr}, without
> -selecting that frame.  The selected frame remains unchanged by this
> -command.  This requires the same kind of address (more than one for some
> -architectures) that you specify in the @code{frame} command.
> -@xref{Selection, ,Selecting a Frame}.
> +@item info frame @r{[} @var{frame-selection-spec} @r{]}
> +@itemx info f @r{[} @var{frame-selection-spec} @r{]}
> +Print a verbose description of the frame selected by
> +@var{frame-selection-spec}.  The @var{frame-selection-spec} is the
> +same as for the @command{frame} command (@pxref{Selection, ,Selecting
> +a Frame}).  The selected frame remains unchanged by this command.
>  
>  @kindex info args
>  @item info args
> @@ -30010,11 +30046,12 @@
>  @subsubheading Synopsis
>  
>  @smallexample
> - -stack-select-frame @var{framenum}
> + -stack-select-frame @var{frame-selection-spec}
>  @end smallexample
>  
> -Change the selected frame.  Select a different frame @var{framenum} on
> -the stack.
> +Change the selected frame, the @var{frame-selection-spec} is as for
> +the @command{frame} command described in @ref{Selection, ,Selecting a
> +Frame}.
>  
>  This command in deprecated in favor of passing the @samp{--frame}
>  option to every command.
> diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c
> index 52660bdd498..33ac7663ba8 100644
> --- a/gdb/mi/mi-cmd-stack.c
> +++ b/gdb/mi/mi-cmd-stack.c
> @@ -681,10 +681,8 @@ list_args_or_locals (enum what_to_list what, enum print_values values,
>  void
>  mi_cmd_stack_select_frame (const char *command, char **argv, int argc)
>  {
> -  if (argc == 0 || argc > 1)
> +  if (!select_frame_from_spec (argv, argc, 0))
>      error (_("-stack-select-frame: Usage: FRAME_SPEC"));
> -
> -  select_frame_command (argv[0], 1 /* not used */ );
>  }
>  
>  void
> diff --git a/gdb/stack.c b/gdb/stack.c
> index 97ebc8bc23f..ab7d7e19f2a 100644
> --- a/gdb/stack.c
> +++ b/gdb/stack.c
> @@ -102,6 +102,9 @@ static void set_last_displayed_sal (int valid,
>  				    struct symtab *symtab,
>  				    int line);
>  
> +static struct frame_info *find_frame_for_function (const char *);
> +static struct frame_info *find_frame_for_address (CORE_ADDR);
> +
>  /* Zero means do things normally; we are interacting directly with the
>     user.  One means print the full filename and linenumber when a
>     frame is printed, and do so in a format emacs18/emacs19.22 can
> @@ -1255,134 +1258,34 @@ print_frame (struct frame_info *frame, int print_level,
>  }
>  \f
>  
> -/* Read a frame specification in whatever the appropriate format is from
> -   FRAME_EXP.  Call error() if the specification is in any way invalid (so
> -   this function never returns NULL).  When SELECTED_FRAME_P is non-NULL
> -   set its target to indicate that the default selected frame was used.  */
> +/* Completion function for "frame function", "info frame function", and
> +   "select-frame function" commands.  */
>  
> -static struct frame_info *
> -parse_frame_specification (const char *frame_exp, int *selected_frame_p)
> +void
> +frame_selection_by_function_completer (struct cmd_list_element *ignore,
> +				       completion_tracker &tracker,
> +				       const char *text, const char *word)
>  {
> -  int numargs;
> -  struct value *args[4];
> -  CORE_ADDR addrs[ARRAY_SIZE (args)];
> -
> -  if (frame_exp == NULL)
> -    numargs = 0;
> -  else
> -    {
> -      numargs = 0;
> -      while (1)
> -	{
> -	  const char *p;
> -
> -	  /* Skip leading white space, bail of EOL.  */
> -	  frame_exp = skip_spaces (frame_exp);
> -	  if (!*frame_exp)
> -	    break;
> -
> -	  /* Parse the argument, extract it, save it.  */
> -	  for (p = frame_exp;
> -	       *p && !ISSPACE (*p);
> -	       p++);
> -	  std::string addr_string (frame_exp, p - frame_exp);
> -	  frame_exp = p;
> -	  
> -	  /* NOTE: Parse and evaluate expression, but do not use
> -	     functions such as parse_and_eval_long or
> -	     parse_and_eval_address to also extract the value.
> -	     Instead value_as_long and value_as_address are used.
> -	     This avoids problems with expressions that contain
> -	     side-effects.  */
> -	  if (numargs >= ARRAY_SIZE (args))
> -	    error (_("Too many args in frame specification"));
> -	  args[numargs++] = parse_and_eval (addr_string.c_str ());
> -	}
> -    }
> -
> -  /* If no args, default to the selected frame.  */
> -  if (numargs == 0)
> -    {
> -      if (selected_frame_p != NULL)
> -	(*selected_frame_p) = 1;
> -      return get_selected_frame (_("No stack."));
> -    }
> -
> -  /* None of the remaining use the selected frame.  */
> -  if (selected_frame_p != NULL)
> -    (*selected_frame_p) = 0;
> -
> -  /* Assume the single arg[0] is an integer, and try using that to
> -     select a frame relative to current.  */
> -  if (numargs == 1)
> -    {
> -      struct frame_info *fid;
> -      int level = value_as_long (args[0]);
> -
> -      fid = find_relative_frame (get_current_frame (), &level);
> -      if (level == 0)
> -	/* find_relative_frame was successful.  */
> -	return fid;
> -    }
> -
> -  /* Convert each value into a corresponding address.  */
> -  {
> -    int i;
> -
> -    for (i = 0; i < numargs; i++)
> -      addrs[i] = value_as_address (args[i]);
> -  }
> -
> -  /* Assume that the single arg[0] is an address, use that to identify
> -     a frame with a matching ID.  Should this also accept stack/pc or
> -     stack/pc/special.  */
> -  if (numargs == 1)
> -    {
> -      struct frame_id id = frame_id_build_wild (addrs[0]);
> -      struct frame_info *fid;
> -
> -      /* If (s)he specifies the frame with an address, he deserves
> -	 what (s)he gets.  Still, give the highest one that matches.
> -	 (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't
> -	 know).  */
> -      for (fid = get_current_frame ();
> -	   fid != NULL;
> -	   fid = get_prev_frame (fid))
> -	{
> -	  if (frame_id_eq (id, get_frame_id (fid)))
> -	    {
> -	      struct frame_info *prev_frame;
> -
> -	      while (1)
> -		{
> -		  prev_frame = get_prev_frame (fid);
> -		  if (!prev_frame
> -		      || !frame_id_eq (id, get_frame_id (prev_frame)))
> -		    break;
> -		  fid = prev_frame;
> -		}
> -	      return fid;
> -	    }
> -	}
> -      }
> -
> -  /* We couldn't identify the frame as an existing frame, but
> -     perhaps we can create one with a single argument.  */
> -  if (numargs == 1)
> -    return create_new_frame (addrs[0], 0);
> -  else if (numargs == 2)
> -    return create_new_frame (addrs[0], addrs[1]);
> -  else
> -    error (_("Too many args in frame specification"));
> +  /* This is used to complete function names within a stack.  It would be
> +     nice if instead of offering all available function names, we only
> +     offered functions that were actually in the stack.  However, this
> +     would probably mean unwinding the stack to completion, which could
> +     take too long (or on a corrupted stack, possibly not end).  For now I
> +     offer all symbol names as a safer choice.  */
> +  collect_symbol_completion_matches (tracker,
> +				     complete_symbol_mode::EXPRESSION,
> +				     symbol_name_match_type::EXPRESSION,
> +				     text, word);
>  }
>  
> -/* Print verbosely the selected frame or the frame at address
> -   ADDR_EXP.  Absolutely all information in the frame is printed.  */
> +/* Core of all the "info frame" sub-commands.  Print verbosely the frame FI
> +   if SELECTED_FRAME_P is true then frame FI is the current frame, which
> +   was selected as a default due to the user not providing any arguments
> +   to select some other frame.  */
>  
>  static void
> -info_frame_command (const char *addr_exp, int from_tty)
> +info_frame_command_core (struct frame_info *fi, bool selected_frame_p)
>  {
> -  struct frame_info *fi;
>    struct symbol *func;
>    struct symtab *s;
>    struct frame_info *calling_frame_info;
> @@ -1390,7 +1293,6 @@ info_frame_command (const char *addr_exp, int from_tty)
>    const char *funname = 0;
>    enum language funlang = language_unknown;
>    const char *pc_regname;
> -  int selected_frame_p;
>    struct gdbarch *gdbarch;
>    CORE_ADDR frame_pc;
>    int frame_pc_p;
> @@ -1398,7 +1300,6 @@ info_frame_command (const char *addr_exp, int from_tty)
>    CORE_ADDR caller_pc = 0;
>    int caller_pc_p = 0;
>  
> -  fi = parse_frame_specification (addr_exp, &selected_frame_p);
>    gdbarch = get_frame_arch (fi);
>  
>    /* Name of the value returned by get_frame_pc().  Per comments, "pc"
> @@ -1687,6 +1588,140 @@ info_frame_command (const char *addr_exp, int from_tty)
>    }
>  }
>  
> +/* The core of all the "select-frame" sub-commands.  Just wraps a call to
> +   SELECT_FRAME.  */
> +
> +static void
> +select_frame_command_core (struct frame_info *fi, bool ignored)
> +{
> +  struct frame_info *prev_frame = get_selected_frame_if_set ();
> +  select_frame (fi);
> +  if (get_selected_frame_if_set () != prev_frame)
> +    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
> +}
> +
> +/* The core of all the "frame" sub-commands.  Select frame FI, and if this
> +   means we change frame send out a change notification (otherwise, just
> +   reprint the current frame summary).   */
> +
> +static void
> +frame_command_core (struct frame_info *fi, bool ignored)
> +{
> +  struct frame_info *prev_frame = get_selected_frame_if_set ();
> +
> +  select_frame (fi);
> +  if (get_selected_frame_if_set () != prev_frame)
> +    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
> +  else
> +    print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME);
> +}
> +
> +/* The three commands 'frame', 'select-frame', and 'info frame' all have a
> +   common set of sub-commands that allow a specific frame to be selected.
> +   All of the sub-command functions are static methods within this class
> +   template which is then instantiated below.  */
> +
> +template <void (*FPTR) (struct frame_info *, bool)>
> +class frame_command_helper
> +{
> +public:
> +
> +  /* The "frame level" family of commands.  The ARG is a number that is
> +     the frame's level in the stack.  */
> +  static void
> +  level (const char *arg, int from_tty)
> +  {
> +    int level = value_as_long (parse_and_eval (arg));
> +    struct frame_info *fid
> +      = find_relative_frame (get_current_frame (), &level);
> +    if (level != 0)
> +      error (_("No frame at level %s."), arg);
> +    FPTR (fid, false);
> +  }
> +
> +  /* The "frame address" family of commands.  ARG is a stack-pointer
> +     address for an existing frame.  This command does not allow new
> +     frames to be created.  */
> +
> +  static void
> +  address (const char *arg, int from_tty)
> +  {
> +    CORE_ADDR addr = value_as_address (parse_and_eval (arg));
> +    struct frame_info *fid = find_frame_for_address (addr);
> +    if (fid == NULL)
> +      error (_("No frame at address %s."), arg);
> +    FPTR (fid, false);
> +  }
> +
> +  /* The "frame view" family of commands.  ARG is one or two addresses and
> +     is used to view a frame that might be outside the current backtrace.
> +     The addresses are stack-pointer address, and (optional) pc-address.  */
> +
> +  static void
> +  view (const char *args, int from_tty)
> +  {
> +    struct frame_info *fid;
> +
> +    if (args == NULL)
> +    error (_("Missing address argument to view a frame"));
> +
> +    gdb_argv argv (args);
> +
> +    if (argv.count () == 2)
> +      {
> +	CORE_ADDR addr[2];
> +
> +	addr [0] = value_as_address (parse_and_eval (argv[0]));
> +	addr [1] = value_as_address (parse_and_eval (argv[1]));
> +	fid = create_new_frame (addr[0], addr[1]);
> +      }
> +    else
> +      {
> +	CORE_ADDR addr = value_as_address (parse_and_eval (argv[0]));
> +	fid = create_new_frame (addr, false);
> +      }
> +    FPTR (fid, false);
> +  }
> +
> +  /* The "frame function" family of commands.  ARG is the name of a
> +     function within the stack, the first function (searching from frame
> +     0) with that name will be selected.  */
> +
> +  static void
> +  function (const char *arg, int from_tty)
> +  {
> +    if (arg == NULL)
> +      error (_("Missing function name argument"));
> +    struct frame_info *fid = find_frame_for_function (arg);
> +    if (fid == NULL)
> +      error (_("No frame for function \"%s\"."), arg);
> +    FPTR (fid, false);
> +  }
> +
> +  /* The "frame" base command, that is, when no sub-command is specified.
> +     If one argument is provided then we assume that this is a frame level
> +     number as historically, this was the supported command syntax that was
> +     used most often.
> +
> +     If no argument is provided, then the current frame is selected.  */
> +
> +  static void
> +  base_command (const char *arg, int from_tty)
> +  {
> +    if (arg == NULL)
> +      FPTR (get_selected_frame (_("No stack.")), true);
> +    else
> +      level (arg, from_tty);
> +  }
> +};
> +
> +/* Instantiate three FRAME_COMMAND_HELPER instances to implement the
> +   sub-commands for 'info frame', 'frame', and 'select-frame' commands.  */
> +
> +static frame_command_helper <info_frame_command_core> info_frame_cmd;
> +static frame_command_helper <frame_command_core> frame_cmd;
> +static frame_command_helper <select_frame_command_core> select_frame_cmd;
> +
>  /* Print briefly all stack frames or just the innermost COUNT_EXP
>     frames.  */
>  
> @@ -2193,37 +2228,43 @@ find_relative_frame (struct frame_info *frame, int *level_offset_ptr)
>    return frame;
>  }
>  
> -/* The "select_frame" command.  With no argument this is a NOP.
> -   Select the frame at level LEVEL_EXP if it is a valid level.
> -   Otherwise, treat LEVEL_EXP as an address expression and select it.
> +/* Select a frame using ARGV.  This is used from the MI -stack-select-frame
> +   to provide the same frame specification mechanism that the CLI has for
> +   commands like 'frame'.  The return value is true if the contents of
> +   ARGV looked like a sensible attempt to change the frame (an error might
> +   still be thrown though), or false if the contents of ARGV are not a
> +   correct frame specification.  */
>  
> -   See parse_frame_specification for more info on proper frame
> -   expressions.  */
> -
> -void
> -select_frame_command (const char *level_exp, int from_tty)
> +bool
> +select_frame_from_spec (char **argv, int argc, int from_tty)
>  {
> -  struct frame_info *prev_frame = get_selected_frame_if_set ();
> -
> -  select_frame (parse_frame_specification (level_exp, NULL));
> -  if (get_selected_frame_if_set () != prev_frame)
> -    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
> -}
> -
> -/* The "frame" command.  With no argument, print the selected frame
> -   briefly.  With an argument, behave like select_frame and then print
> -   the selected frame.  */
> +  if (argc == 1)
> +    select_frame_cmd.level (argv[0], from_tty);
> +  else if (argc == 2
> +	   && strncasecmp ("level", argv[0], strlen (argv[0])) == 0)
> +    select_frame_cmd.level (argv[1], from_tty);
> +  else if (argc == 2
> +	   && strncasecmp ("address", argv[0], strlen (argv[0])) == 0)
> +    select_frame_cmd.address (argv[1], from_tty);
> +  else if (argc == 2
> +	   && strncasecmp ("function", argv[0], strlen (argv[0])) == 0)
> +    select_frame_cmd.function (argv[1], from_tty);
> +  else if ((argc == 2 || argc == 3)
> +	   && strncasecmp ("view", argv[0], strlen (argv[0])) == 0)
> +    {
> +      std::string arg;
>  
> -static void
> -frame_command (const char *level_exp, int from_tty)
> -{
> -  struct frame_info *prev_frame = get_selected_frame_if_set ();
> +      if (argc == 2)
> +	arg = string_printf ("%s", argv[1]);
> +      else
> +	arg = string_printf ("%s %s", argv[1], argv[2]);
>  
> -  select_frame (parse_frame_specification (level_exp, NULL));
> -  if (get_selected_frame_if_set () != prev_frame)
> -    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
> +      select_frame_cmd.view (arg.c_str (), from_tty);
> +    }
>    else
> -    print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME);
> +    return false;
> +
> +  return true;
>  }
>  
>  /* Select the frame up one or COUNT_EXP stack levels from the
> @@ -2494,9 +2535,59 @@ func_command (const char *arg, int from_tty)
>      }
>  }
>  
> +/* Find inner-mode frame with frame address ADDRESS.  Return NULL if no
> +   matching frame can be found.  */
> +
> +static struct frame_info *
> +find_frame_for_address (CORE_ADDR address)
> +{
> +  struct frame_id id;
> +  struct frame_info *fid;
> +
> +  id = frame_id_build_wild (address);
> +
> +  /* If (s)he specifies the frame with an address, he deserves
> +     what (s)he gets.  Still, give the highest one that matches.
> +     (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't
> +     know).  */
> +  for (fid = get_current_frame ();
> +       fid != NULL;
> +       fid = get_prev_frame (fid))
> +    {
> +      if (frame_id_eq (id, get_frame_id (fid)))
> +	{
> +	  struct frame_info *prev_frame;
> +
> +	  while (1)
> +	    {
> +	      prev_frame = get_prev_frame (fid);
> +	      if (!prev_frame
> +		  || !frame_id_eq (id, get_frame_id (prev_frame)))
> +		break;
> +	      fid = prev_frame;
> +	    }
> +	  return fid;
> +	}
> +    }
> +  return NULL;
> +}
> +
> +\f
> +
> +/* Commands with a prefix of `frame'.  */
> +static struct cmd_list_element *frame_cmd_list = NULL;
> +
> +/* Commands with a prefix of `frame'.  */
> +static struct cmd_list_element *select_frame_cmd_list = NULL;
> +
> +/* Commands with a prefix of `info frame'.  */
> +static struct cmd_list_element *info_frame_cmd_list = NULL;
> +
>  void
>  _initialize_stack (void)
>  {
> +  struct cmd_list_element *cmd;
> +
>    add_com ("return", class_stack, return_command, _("\
>  Make selected stack frame return to its caller.\n\
>  Control remains in the debugger, but when you continue\n\
> @@ -2519,20 +2610,89 @@ An argument says how many frames down to go."));
>  Same as the `down' command, but does not print anything.\n\
>  This is useful in command scripts."));
>  
> -  add_com ("frame", class_stack, frame_command, _("\
> -Select and print a stack frame.\nWith no argument, \
> -print the selected stack frame.  (See also \"info frame\").\n\
> -An argument specifies the frame to select.\n\
> -It can be a stack frame number or the address of the frame."));
> -
> +  add_prefix_cmd ("frame", class_stack,
> +		  &frame_cmd.base_command, _("\
> +Select and print a stack frame.\n\
> +With no argument, print the selected stack frame.  (See also \"info frame\").\n\
> +A single numerical argument specifies the frame to select."),
> +		  &frame_cmd_list, "frame ", 1, &cmdlist);
>    add_com_alias ("f", "frame", class_stack, 1);
>  
> -  add_com_suppress_notification ("select-frame", class_stack, select_frame_command, _("\
> +  add_cmd ("address", class_stack, &frame_cmd.address,
> +	   _("\
> +Select and print a stack frame by stack address\n\
> +\n\
> +Usage: frame address STACK-ADDRESS"),
> +	   &frame_cmd_list);
> +
> +  add_cmd ("view", class_stack, &frame_cmd.view,
> +	   _("\
> +View a stack frame that might be outside the current backtrace.\n\
> +\n\
> +Usage: frame view STACK-ADDRESS\n\
> +       frame view STACK-ADDRESS PC-ADDRESS"),
> +	   &frame_cmd_list);
> +
> +  cmd = add_cmd ("function", class_stack, &frame_cmd.function,
> +	   _("\
> +Select and print a stack frame by function name.\n\
> +\n\
> +Usage: frame function NAME\n\
> +\n\
> +The innermost frame that visited function NAME is selected."),
> +	   &frame_cmd_list);
> +  set_cmd_completer (cmd, frame_selection_by_function_completer);
> +
> +
> +  add_cmd ("level", class_stack, &frame_cmd.level,
> +	   _("\
> +Select and print a stack frame by level.\n\
> +\n\
> +Usage: frame level NUMBER"),
> +	   &frame_cmd_list);
> +
> +  cmd = add_prefix_cmd_suppress_notification ("select-frame", class_stack,
> +		      &select_frame_cmd.base_command, _("\
>  Select a stack frame without printing anything.\n\
> -An argument specifies the frame to select.\n\
> -It can be a stack frame number or the address of the frame."),
> +A single numerical argument specifies the frame to select."),
> +		      &select_frame_cmd_list, "select-frame ", 1, &cmdlist,
> +		      &cli_suppress_notification.user_selected_context);
> +
> +  add_cmd_suppress_notification ("address", class_stack,
> +			 &select_frame_cmd.address, _("\
> +Select a stack frame by stack address.\n\
> +\n\
> +Usage: select-frame address STACK-ADDRESS"),
> +			 &select_frame_cmd_list,
> +			 &cli_suppress_notification.user_selected_context);
> +
> +
> +  add_cmd_suppress_notification ("view", class_stack,
> +		 &select_frame_cmd.view, _("\
> +Select a stack frame that might be outside the current backtrace.\n\
> +\n\
> +Usage: select-frame view STACK-ADDRESS\n\
> +       select-frame view STACK-ADDRESS PC-ADDRESS"),
> +		 &select_frame_cmd_list,
>  		 &cli_suppress_notification.user_selected_context);
>  
> +  cmd = add_cmd_suppress_notification ("function", class_stack,
> +	       &select_frame_cmd.function, _("\
> +Select a stack frame by function name.\n\
> +\n\
> +Usage: select-frame function NAME"),
> +	       &select_frame_cmd_list,
> +	       &cli_suppress_notification.user_selected_context);
> +  set_cmd_completer (cmd, frame_selection_by_function_completer);
> +
> +  add_cmd_suppress_notification ("level", class_stack,
> +			 &select_frame_cmd.level, _("\
> +Select a stack frame by level.\n\
> +\n\
> +Usage: select-frame level NUMBER"),
> +			 &select_frame_cmd_list,
> +			 &cli_suppress_notification.user_selected_context);
> +
>    add_com ("backtrace", class_stack, backtrace_command, _("\
>  Print backtrace of all stack frames, or innermost COUNT frames.\n\
>  Usage: backtrace [QUALIFIERS]... [COUNT]\n\
> @@ -2546,9 +2706,45 @@ on this backtrace."));
>    add_info ("stack", backtrace_command,
>  	    _("Backtrace of the stack, or innermost COUNT frames."));
>    add_info_alias ("s", "stack", 1);
> -  add_info ("frame", info_frame_command,
> -	    _("All about selected stack frame, or frame at ADDR."));
> +
> +  add_prefix_cmd ("frame", class_info, &info_frame_cmd.base_command,
> +		  _("All about the selected stack frame.\n\
> +With no arguments, displays information about the currently selected stack\n\
> +frame.  Alternatively a frame specification may be provided (See \"frame\")\n\
> +the information is then printed about the specified frame."),
> +		  &info_frame_cmd_list, "info frame ", 1, &infolist);
>    add_info_alias ("f", "frame", 1);
> +
> +  add_cmd ("address", class_stack, &info_frame_cmd.address,
> +	   _("\
> +Print information about a stack frame selected by stack address.\n\
> +\n\
> +Usage: info frame address STACK-ADDRESS"),
> +	   &info_frame_cmd_list);
> +
> +  add_cmd ("view", class_stack, &info_frame_cmd.view,
> +	   _("\
> +Print information about a stack frame outside the current backtrace.\n\
> +\n\
> +Usage: info frame view STACK-ADDRESS\n\
> +       info frame view STACK-ADDRESS PC-ADDRESS"),
> +	   &info_frame_cmd_list);
> +
> +  cmd = add_cmd ("function", class_stack, &info_frame_cmd.function,
> +	   _("\
> +Print information about a stack frame selected by function name.\n\
> +\n\
> +Usage: info frame function NAME"),
> +	   &info_frame_cmd_list);
> +  set_cmd_completer (cmd, frame_selection_by_function_completer);
> +
> +  add_cmd ("level", class_stack, &info_frame_cmd.level,
> +	   _("\
> +Print information about a stack frame selected by level.\n\
> +\n\
> +Usage: info frame level NUMBER"),
> +	   &info_frame_cmd_list);
> +
>    add_info ("locals", info_locals_command,
>  	    _("Local variables of current stack frame."));
>    add_info ("args", info_args_command,
> diff --git a/gdb/stack.h b/gdb/stack.h
> index ca190efa9c3..29794872d70 100644
> --- a/gdb/stack.h
> +++ b/gdb/stack.h
> @@ -20,7 +20,7 @@
>  #ifndef STACK_H
>  #define STACK_H
>  
> -void select_frame_command (const char *level_exp, int from_tty);
> +bool select_frame_from_spec (char **argv, int argc, int from_tty);
>  
>  gdb::unique_xmalloc_ptr<char> find_frame_funname (struct frame_info *frame,
>  						  enum language *funlang,
> diff --git a/gdb/testsuite/gdb.base/frame-selection.c b/gdb/testsuite/gdb.base/frame-selection.c
> new file mode 100644
> index 00000000000..f3d81f223e0
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/frame-selection.c
> @@ -0,0 +1,52 @@
> +/* Copyright 2018 Free Software Foundation, Inc.
> +
> +   This file is part of GDB.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
> +
> +int
> +frame_2 (void)
> +{
> +  return 0;
> +}
> +
> +int
> +frame_1 (void)
> +{
> +  return frame_2 ();
> +}
> +
> +int
> +recursive (int arg)
> +{
> +  int v;
> +
> +  if (arg < 2)
> +    v = recursive (arg + 1);
> +  else
> +    v = frame_2 ();
> +
> +  return v;
> +}
> +
> +int
> +main (void)
> +{
> +  int i, j;
> +
> +  i = frame_1 ();
> +  j = recursive (0);
> +
> +  return i + j;
> +}
> diff --git a/gdb/testsuite/gdb.base/frame-selection.exp b/gdb/testsuite/gdb.base/frame-selection.exp
> new file mode 100644
> index 00000000000..6df85494839
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/frame-selection.exp
> @@ -0,0 +1,156 @@
> +# Copyright 2018 Free Software Foundation, Inc.
> +
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +
> +if { [prepare_for_testing frame-cmds.exp "frame-cmds" {frame-cmds.c} {debug nowarnings}] } {
> +    return -1
> +}
> +set srcfile frame-cmds.c
> +
> +runto_main
> +gdb_breakpoint frame_2
> +gdb_continue_to_breakpoint frame_2
> +
> +gdb_test "bt" "#0  frame_2.*#1  $hex in frame_1.*#2  $hex in main.*" "backtrace at breakpoint"
> +
> +# Perform "info frame" to extract the frames address.
> +proc get_frame_address { {testname ""} } {
> +    global hex gdb_prompt
> +
> +    set testname "get_frame_address: ${testname}"
> +    set frame_address "unknown"
> +
> +    send_gdb "info frame\n"
> +    gdb_expect {
> +	-re ", frame at ($hex):\r\n.*\r\n$gdb_prompt $" {
> +	    set frame_address $expect_out(1,string)
> +	    pass $testname
> +	}
> +	-re ".*$gdb_prompt $" { fail $testname }
> +	timeout { fail "$testname (timeout)" }
> +    }
> +
> +    return $frame_address
> +}
> +
> +# Passes a list of addresses.  Return a new address that is not in the
> +# list.
> +proc get_new_address { {addresses {}} } {
> +    return [format "%#x" [expr [lindex $addresses [llength addresses]] + 0x10 ]]
> +}
> +
> +proc check_frame { level address function } {
> +    global hex gdb_prompt
> +
> +    set testname "check frame level ${level}"
> +    send_gdb "info frame\n"
> +    gdb_expect {
> +	-re "Stack level ${level}, frame at ($address):\r\n rip = $hex in ${function} \(\[^\r\n\]*\); saved rip = $hex\r\n.*\r\n$gdb_prompt $" {
> +	    pass $testname
> +	}
> +	-re ".*$gdb_prompt $" { fail $testname }
> +	timeout { fail "$testname (timeout)" }
> +    }
> +}
> +
> +# Select frame using level number, but relying on this being the
> +# default action, so "frame 0" performs "frame level 0".
> +gdb_test "frame 0" "#0  frame_2.*"
> +set frame_0_address [ get_frame_address "frame 0" ]
> +gdb_test "frame 1" "#1  $hex in frame_1.*"
> +set frame_1_address [ get_frame_address "frame 1" ]
> +gdb_test "frame 2" "#2  $hex in main.*"
> +set frame_2_address [ get_frame_address "frame 2" ]
> +gdb_test "frame 3" "No frame at level 3\."
> +
> +# Find an address that matches no frame
> +set no_frame_address [ get_new_address [list $frame_0_address \
> +					     $frame_1_address \
> +					     $frame_2_address] ]
> +
> +# Select frame using 'level' specification.
> +gdb_test "frame level 0" "#0  frame_2.*"
> +gdb_test "frame level 1" "#1  $hex in frame_1.*"
> +gdb_test "frame level 2" "#2  $hex in main.*"
> +gdb_test "frame level 3" "No frame at level 3\."
> +
> +# Select frame by address.
> +gdb_test "frame address ${frame_0_address}" "#0  frame_2.*"
> +gdb_test "frame address ${frame_1_address}" "#1  $hex in frame_1.*"
> +gdb_test "frame address ${frame_2_address}" "#2  $hex in main.*"
> +gdb_test "frame address ${no_frame_address}" \
> +    "No frame at address ${no_frame_address}\."
> +
> +# Select frame by function.
> +gdb_test "frame function frame_2" "#0  frame_2.*"
> +gdb_test "frame function frame_1" "#1  $hex in frame_1.*"
> +gdb_test "frame function main" "#2  $hex in main.*"
> +# Check for a distinction between a known function not in the stack
> +# trace, and an unknown function.
> +gdb_test "frame function recursive" "No frame for function \"recursive\"."
> +gdb_test "frame function foo" "Function \"foo\" not defined."
> +
> +
> +gdb_test_no_output "select-frame 0"
> +check_frame "0" "${frame_0_address}" "frame_2"
> +gdb_test_no_output "select-frame 1"
> +check_frame "1" "${frame_1_address}" "frame_1"
> +gdb_test_no_output "select-frame 2"
> +check_frame "2" "${frame_2_address}" "main"
> +gdb_test "select-frame 3" "No frame at level 3\."
> +
> +gdb_test_no_output "select-frame level 0"
> +check_frame "0" "${frame_0_address}" "frame_2"
> +gdb_test_no_output "select-frame level 1"
> +check_frame "1" "${frame_1_address}" "frame_1"
> +gdb_test_no_output "select-frame level 2"
> +check_frame "2" "${frame_2_address}" "main"
> +gdb_test "select-frame level 3" "No frame at level 3\."
> +
> +gdb_test_no_output "select-frame address ${frame_0_address}"
> +check_frame "0" "${frame_0_address}" "frame_2"
> +gdb_test_no_output "select-frame address ${frame_1_address}"
> +check_frame "1" "${frame_1_address}" "frame_1"
> +gdb_test_no_output "select-frame address ${frame_2_address}"
> +check_frame "2" "${frame_2_address}" "main"
> +gdb_test "select-frame address ${no_frame_address}" \
> +    "No frame at address ${no_frame_address}\."
> +
> +gdb_test_no_output "select-frame function frame_2"
> +check_frame "0" "${frame_0_address}" "frame_2"
> +gdb_test_no_output "select-frame function frame_1"
> +check_frame "1" "${frame_1_address}" "frame_1"
> +gdb_test_no_output "select-frame function main"
> +check_frame "2" "${frame_2_address}" "main"
> +# Check for a distinction between a known function not in the stack
> +# trace, and an unknown function.
> +gdb_test "select-frame function recursive" \
> +    "No frame for function \"recursive\"."
> +gdb_test "select-frame function foo" \
> +    "Function \"foo\" not defined."
> +
> +# Now continue until we hit the breakpoint again.
> +gdb_continue_to_breakpoint frame_2
> +gdb_test "bt" \
> +    "#0  frame_2.*#1  $hex in recursive.*#2  $hex in recursive.*#3  $hex in recursive.*#4  $hex in main.*" \
> +    "backtrace at breakpoint with recursive frames"
> +
> +# Check "frame function" when a function name occurs multiple times in
> +# the stack.  The inner most (lowest level number) should always be
> +# selected.
> +gdb_test "frame function frame_2" "#0  frame_2.*"
> +gdb_test "frame function recursive" "#1  $hex in recursive.*"
> +gdb_test "frame function recursive" "#1  $hex in recursive.*"
> +gdb_test "frame function main" "#4  $hex in main.*"
> +gdb_test "frame function recursive" "#1  $hex in recursive.*"
> diff --git a/gdb/testsuite/gdb.mi/mi-frame-selection.c b/gdb/testsuite/gdb.mi/mi-frame-selection.c
> new file mode 100644
> index 00000000000..70b992c9149
> --- /dev/null
> +++ b/gdb/testsuite/gdb.mi/mi-frame-selection.c
> @@ -0,0 +1,34 @@
> +/* Copyright 2018 Free Software Foundation, Inc.
> +
> +   This file is part of GDB.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
> +
> +int
> +frame_2 (void)
> +{
> +  return 0;
> +}
> +
> +int
> +frame_1 (void)
> +{
> +  return frame_2 ();
> +}
> +
> +int
> +main (void)
> +{
> +  return frame_1 ();
> +}
> diff --git a/gdb/testsuite/gdb.mi/mi-frame-selection.exp b/gdb/testsuite/gdb.mi/mi-frame-selection.exp
> new file mode 100644
> index 00000000000..e24c7179b9d
> --- /dev/null
> +++ b/gdb/testsuite/gdb.mi/mi-frame-selection.exp
> @@ -0,0 +1,89 @@
> +# Copyright 2018 Free Software Foundation, Inc.
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +
> +# Test the frame selection syntax with the -stack-select-frame command.
> +
> +load_lib mi-support.exp
> +set MIFLAGS "-i=mi"
> +
> +gdb_exit
> +if [mi_gdb_start] {
> +    continue
> +}
> +
> +standard_testfile
> +
> +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
> +    untested $testfile
> +    return -1
> +}
> +
> +mi_delete_breakpoints
> +mi_gdb_reinitialize_dir $srcdir/$subdir
> +mi_gdb_load ${binfile}
> +
> +mi_create_breakpoint "frame_2" \
> +    "break-insert into frame_2" \
> +    -number 1 -func frame_2 -file ".*${srcfile}"
> +
> +mi_run_cmd
> +mi_expect_stop "breakpoint-hit" "frame_2" "" ".*${srcfile}" \
> +	".*" { "" "disp=\"keep\"" } "run to breakpoint"
> +
> +mi_gdb_test "-stack-info-depth" \
> +    "\\^done,depth=\"3\"" \
> +    "stack info-depth"
> +
> +for {set i 0} {$i < 5} {incr i} {
> +
> +    if { $i < 3 } then {
> +	mi_gdb_test "-stack-select-frame $i" \
> +	    {\^done} \
> +	    "-stack-select-frame $i"
> +
> +	mi_gdb_test "-stack-info-frame" \
> +	    "\\^done,frame=\\\{level=\"$i\"\[^\}\]+\\\}" \
> +	    "check level has has changed to $i"
> +
> +    } else {
> +	mi_gdb_test "-stack-select-frame view $i" \
> +	    {\^done} \
> +	    "-stack-select-frame view frame at $i"
> +
> +	mi_gdb_test "-stack-info-frame" \
> +	    "\\^done,frame=\\\{level=\"0\"\[^\}\]+\\\}" \
> +	    "check level has has changed to 0, when creating a frame at sp=$i"
> +    }
> +}
> +
> +mi_gdb_test "-stack-select-frame 5" \
> +    {\^error,msg="No frame at level 5."} \
> +    "-stack-select-frame 5, with no keyword"
> +
> +mi_gdb_test "-stack-select-frame level 5" \
> +    {\^error,msg="No frame at level 5."} \
> +    "-stack-select-frame 5, using 'level' keyword"
> +
> +mi_gdb_test "-stack-select-frame function main" \
> +    {\^done} \
> +    "-stack-select-frame select frame for function main"
> +
> +mi_gdb_test "-stack-info-frame" \
> +    "\\^done,frame=\\\{level=\"2\"\[^\}\]+\\\}" \
> +    "check level has has changed to 2 for function main"
> +
> +mi_gdb_test "-stack-select-frame function unknown_function" \
> +    {\^error,msg=\"Function \\\"unknown_function\\\" not defined.\"} \
> +    "-stack-select-frame check error on undefined function name"
> -- 
> 2.14.3
> 

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCHv4] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-06-07 16:19   ` [PATCHv3] " Andrew Burgess
  2018-06-29 12:23     ` Andrew Burgess
@ 2018-07-17 15:58     ` Andrew Burgess
  2018-07-23 20:46       ` Philippe Waroquiers
  1 sibling, 1 reply; 35+ messages in thread
From: Andrew Burgess @ 2018-07-17 15:58 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

Now that the 'frame apply' command has been merged to master, this is
a rebase of the v3 patch.  Merge conflicts have been resolved, but the
content is largely unchanged.

Thanks,
Andrew

---

The 'frame' command, and thanks to code reuse the 'info frame' and
'select-frame' commands, currently have an overloaded mechanism for
selecting a frame.

These commands take one or two parameters, if it's one parameter then
we first try to use the parameter as an integer to select a frame by
level (or depth in the stack).  If that fails then we treat the
parameter as an address and try to select a stack frame by
stack-address.  If we still have not selected a stack frame, or we
initially had two parameters, then GDB allows the user to view a stack
frame that is not part of the current backtrace.  Internally, a new
frame is created with the given stack and pc addresses, and this is
shown to the user.

The result of this is that a typo by the user, entering the wrong stack
frame level for example, can result in a brand new frame being viewed
rather than an error.

The purpose of this commit is to remove this overloading, while still
offering the same functionality through some new sub-commands.  By
making the default behaviour of 'frame' (and friends) be to select a
stack frame by level number, it is hoped that enough
backwards-compatibility is maintained that users will not be overly
inconvenienced.

The 'frame', 'select-frame', and 'info frame' commands now all take a
frame specification string as an argument, this string can be any of the
following:

  (1) An integer.  This is treated as a frame number.  If a frame for
  that number does not exist then the user gets an error.

  (2) A string like 'level <NUMBER>', where <NUMBER> is a frame number
  as in option (1) above.

  (3) A string like 'address <ADDRESS>', where <ADDRESS> is a
  stack-frame address.  If there is no frame for this address then the
  user gets an error.

  (4) A string like 'function <NAME>', where <NAME> is a function name,
  the inner most frame for function <NAME> is selected.  If there is no
  frame for function <NAME> then the user gets an error.

  (5) A string like 'view <STACK-ADDRESS>', this views a new frame
  with stack address <STACK-ADDRESS>.

  (6) A string like 'view <STACK-ADDRESS> <PC-ADDRESS>', this views
  a new frame with stack address <STACK-ADDRESS> an the pc <PC-ADDRESS>.

This change assumes that the most common use of the commands like
'frame' is to select a frame by frame level number, it is for this
reason that this is the behaviour that is kept for backwards
compatibility.  Any of the alternative behaviours, which are assumed to
be less used, now require a change in user behaviour.

The MI command '-stack-select-frame' has also changed in the same way.
The default behaviour is to select a frame by level number, but the
other selection methods are also available.

gdb/ChangeLog:

	(NEWS): Mention changes to frame related commands.
	* cli/cli-decode.c (add_cmd_suppress_notification): New function.
	(add_prefix_cmd_suppress_notification): New function.
	(add_com_suppress_notification): Call
	add_cmd_suppress_notification.
	* command.h (add_cmd_suppress_notification): Declare.
	(add_prefix_cmd_suppress_notification): Declare.
	* mi/mi-cmd-stack.c (mi_cmd_stack_select_frame): Use new function
	select_frame_from_spec.
	* stack.c (find_frame_for_function): Add declaration.
	(find_frame_for_address): New function.
	(parse_frame_specification): Delete function.
	(frame_selection_by_function_completer): New function.
	(info_frame_command): Rename to...
	(info_frame_command_core): ...this, and update parameter types.
	(select_frame_command): Rename to...
	(select_frame_command_core): ...this, and update parameter types.
	(frame_command): Rename to...
	(frame_command_core): ...this, and update parameter types.
	(class frame_command_helper): New class to wrap implementations of
	frame related sub-commands.
	(select_frame_from_spec): New function.
	(frame_apply_cmd_list): New static global.
	(frame_cmd_list): Make static.
	(select_frame_cmd_list): New global for sub-commands.
	(info_frame_cmd_list): New global for sub-commands.
	(_initialize_stack): Register sub-commands for 'frame',
	'select-frame', and 'info frame'.  Update 'frame apply' commands
	to use frame_apply_cmd_list.  Move function local static
	frame_apply_list to file static frame_apply_cmd_list for
	consistency.
	* stack.h (select_frame_command): Delete declarationn.
	(select_frame_from_spec): Declare new function.

gdb/doc/ChangeLog:

	* gdb.texinfo (Selection): Rewrite documentation for 'frame' and
	'select-frame' commands.
	(Frame Info): Rewrite documentation for 'info frame' command.
	(GDB/MI Stack Manipulation): Update description of
	'-stack-select-frame'.

gdb/testsuite/ChangeLog:

	* gdb.base/frame-selection.exp: New file.
	* gdb.base/frame-selection.c: New file.
	* gdb.mi/mi-frame-selection.exp: New file.
	* gdb.mi/mi-frame-selection.c: New file.
---
 gdb/ChangeLog                               |  36 ++
 gdb/NEWS                                    |   8 +
 gdb/cli/cli-decode.c                        |  44 ++-
 gdb/command.h                               |  14 +
 gdb/doc/ChangeLog                           |   8 +
 gdb/doc/gdb.texinfo                         |  89 +++--
 gdb/mi/mi-cmd-stack.c                       |   4 +-
 gdb/stack.c                                 | 535 +++++++++++++++++++---------
 gdb/stack.h                                 |   2 +-
 gdb/testsuite/ChangeLog                     |   7 +
 gdb/testsuite/gdb.base/frame-selection.c    |  52 +++
 gdb/testsuite/gdb.base/frame-selection.exp  | 157 ++++++++
 gdb/testsuite/gdb.mi/mi-frame-selection.c   |  34 ++
 gdb/testsuite/gdb.mi/mi-frame-selection.exp |  89 +++++
 14 files changed, 877 insertions(+), 202 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/frame-selection.c
 create mode 100644 gdb/testsuite/gdb.base/frame-selection.exp
 create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.c
 create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index 2b51465495f..524a3a13913 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -67,6 +67,14 @@ thread apply [all | COUNT | -COUNT] [FLAG]... COMMAND
 * C expressions can now use _Alignof, and C++ expressions can now use
   alignof.
 
+* Changes to the "frame", "select-frame", and "info frame" CLI
+  commands, as well as the "-stack-select-frame" MI command.  These
+  commands all now take a frame specification which is either a frame
+  number, or one of the keywords 'level', 'address', 'function', or
+  'view' followed by a parameter.  Selecting a frame by address, or
+  viewing a frame outside the current backtrace now requires the use
+  of a keyword.  Selecting a frame by number is unchanged.
+
 * New commands
 
 set debug fbsd-nat
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index c8dda700ca8..fb44fc1627f 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -251,6 +251,23 @@ add_cmd (const char *name, enum command_class theclass,
   return result;
 }
 
+/* Add an element with a suppress notification to the LIST of commands.  */
+
+struct cmd_list_element *
+add_cmd_suppress_notification (const char *name, enum command_class theclass,
+			       cmd_const_cfunc_ftype *fun, const char *doc,
+			       struct cmd_list_element **list,
+			       int *suppress_notification)
+{
+  struct cmd_list_element *element;
+
+  element = add_cmd (name, theclass, fun, doc, list);
+  element->suppress_notification = suppress_notification;
+
+  return element;
+}
+
+
 /* Deprecates a command CMD.
    REPLACEMENT is the name of the command which should be used in
    place of this command, or NULL if no such command exists.
@@ -362,6 +379,25 @@ add_prefix_cmd (const char *name, enum command_class theclass,
   return c;
 }
 
+/* Like ADD_PREFIX_CMD but sets the suppress_notification pointer on the
+   new command list element.  */
+
+struct cmd_list_element *
+add_prefix_cmd_suppress_notification
+               (const char *name, enum command_class theclass,
+		cmd_const_cfunc_ftype *fun,
+		const char *doc, struct cmd_list_element **prefixlist,
+		const char *prefixname, int allow_unknown,
+		struct cmd_list_element **list,
+		int *suppress_notification)
+{
+  struct cmd_list_element *element
+    = add_prefix_cmd (name, theclass, fun, doc, prefixlist,
+		      prefixname, allow_unknown, list);
+  element->suppress_notification = suppress_notification;
+  return element;
+}
+
 /* Like add_prefix_cmd but sets the abbrev_flag on the new command.  */
 
 struct cmd_list_element *
@@ -893,12 +929,8 @@ add_com_suppress_notification (const char *name, enum command_class theclass,
 			       cmd_const_cfunc_ftype *fun, const char *doc,
 			       int *suppress_notification)
 {
-  struct cmd_list_element *element;
-
-  element = add_cmd (name, theclass, fun, doc, &cmdlist);
-  element->suppress_notification = suppress_notification;
-
-  return element;
+  return add_cmd_suppress_notification (name, theclass, fun, doc,
+					&cmdlist, suppress_notification);
 }
 
 /* Recursively walk the commandlist structures, and print out the
diff --git a/gdb/command.h b/gdb/command.h
index 3dde2475cb1..e3d55c2dcba 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -148,6 +148,12 @@ extern struct cmd_list_element *add_cmd (const char *, enum command_class,
 					 const char *,
 					 struct cmd_list_element **);
 
+extern struct cmd_list_element *add_cmd_suppress_notification
+			(const char *name, enum command_class theclass,
+			 cmd_const_cfunc_ftype *fun, const char *doc,
+			 struct cmd_list_element **list,
+			 int *suppress_notification);
+
 extern struct cmd_list_element *add_alias_cmd (const char *, const char *,
 					       enum command_class, int,
 					       struct cmd_list_element **);
@@ -165,6 +171,14 @@ extern struct cmd_list_element *add_prefix_cmd (const char *, enum command_class
 						const char *, int,
 						struct cmd_list_element **);
 
+extern struct cmd_list_element *add_prefix_cmd_suppress_notification
+			(const char *name, enum command_class theclass,
+			 cmd_const_cfunc_ftype *fun,
+			 const char *doc, struct cmd_list_element **prefixlist,
+			 const char *prefixname, int allow_unknown,
+			 struct cmd_list_element **list,
+			 int *suppress_notification);
+
 extern struct cmd_list_element *add_abbrev_prefix_cmd (const char *,
 						       enum command_class,
 						       cmd_const_cfunc_ftype *fun,
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index b36a39b5b4c..2eb89e11551 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -7631,21 +7631,56 @@
 @table @code
 @kindex frame@r{, selecting}
 @kindex f @r{(@code{frame})}
-@item frame @var{n}
-@itemx f @var{n}
-Select frame number @var{n}.  Recall that frame zero is the innermost
-(currently executing) frame, frame one is the frame that called the
-innermost one, and so on.  The highest-numbered frame is the one for
-@code{main}.
+@item frame @r{[} @var{frame-selection-spec} @r{]}
+@item f @r{[} @var{frame-selection-spec} @r{]}
+The @command{frame} command allows different stack frames to be
+selected.  The @var{frame-selection-spec} can be any of the following:
+
+@table @code
+@kindex frame level
+@item @var{num}
+@item level @var{num}
+Select frame number @var{num}.  Recall that frame zero is the
+innermost (currently executing) frame, frame one is the frame that
+called the innermost one, and so on.  The highest-numbered frame is
+the one for @code{main}.
+
+As this is the most common method of navigating the frame stack then
+the string @command{level} can be dropped, the following two commands
+are equivalent:
+
+@smallexample
+(@value{GDBP}) frame 3
+(@value{GDBP}) frame level 3
+@end smallexample
 
-@item frame @var{stack-addr} [ @var{pc-addr} ]
-@itemx f @var{stack-addr} [ @var{pc-addr} ]
-Select the frame at address @var{stack-addr}.  This is useful mainly if the
-chaining of stack frames has been damaged by a bug, making it
-impossible for @value{GDBN} to assign numbers properly to all frames.  In
-addition, this can be useful when your program has multiple stacks and
-switches between them.  The optional @var{pc-addr} can also be given to
-specify the value of PC for the stack frame.
+@kindex frame address
+@item address @var{stack-address}
+Select the frame with stack address @var{stack-address}.
+
+@kindex frame function
+@item function @var{function-name}
+Select the stack frame for function @var{function-name}.  If there are
+multiple stack frames for function @var{function-name} then the inner
+most stack frame is selected.
+
+@kindex frame view
+@item view @var{stack-address} @r{[} @var{pc-addr} @r{]}
+View a frame that is not part of @value{GDBN}'s backtrace.  The frame
+viewed has stack address @var{stack-addr}, and optionally, a program
+counter address of @var{pc-addr}.
+
+This is useful mainly if the chaining of stack frames has been
+damaged by a bug, making it impossible for @value{GDBN} to assign
+numbers properly to all frames.  In addition, this can be useful
+when your program has multiple stacks and switches between them.
+
+When viewing a frame outside the current backtrace using
+@command{frame view} then you can always return to the original
+stack using one of the previous stack frame selection instructions,
+for example @command{frame level 0}.
+
+@end table
 
 @kindex up
 @item up @var{n}
@@ -7688,11 +7723,13 @@
 
 @table @code
 @kindex select-frame
-@item select-frame
+@item select-frame @r{[} @var{frame-selection-spec} @r{]}
 The @code{select-frame} command is a variant of @code{frame} that does
 not display the new frame after selecting it.  This command is
 intended primarily for use in @value{GDBN} command scripts, where the
-output might be unnecessary and distracting.
+output might be unnecessary and distracting.  The
+@var{frame-selection-spec} is as for the @command{frame} command
+described in @ref{Selection, ,Selecting a Frame}.
 
 @kindex down-silently
 @kindex up-silently
@@ -7750,13 +7787,12 @@
 something has gone wrong that has made the stack format fail to fit
 the usual conventions.
 
-@item info frame @var{addr}
-@itemx info f @var{addr}
-Print a verbose description of the frame at address @var{addr}, without
-selecting that frame.  The selected frame remains unchanged by this
-command.  This requires the same kind of address (more than one for some
-architectures) that you specify in the @code{frame} command.
-@xref{Selection, ,Selecting a Frame}.
+@item info frame @r{[} @var{frame-selection-spec} @r{]}
+@itemx info f @r{[} @var{frame-selection-spec} @r{]}
+Print a verbose description of the frame selected by
+@var{frame-selection-spec}.  The @var{frame-selection-spec} is the
+same as for the @command{frame} command (@pxref{Selection, ,Selecting
+a Frame}).  The selected frame remains unchanged by this command.
 
 @kindex info args
 @item info args
@@ -30252,11 +30288,12 @@
 @subsubheading Synopsis
 
 @smallexample
- -stack-select-frame @var{framenum}
+ -stack-select-frame @var{frame-selection-spec}
 @end smallexample
 
-Change the selected frame.  Select a different frame @var{framenum} on
-the stack.
+Change the selected frame, the @var{frame-selection-spec} is as for
+the @command{frame} command described in @ref{Selection, ,Selecting a
+Frame}.
 
 This command in deprecated in favor of passing the @samp{--frame}
 option to every command.
diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c
index 52660bdd498..33ac7663ba8 100644
--- a/gdb/mi/mi-cmd-stack.c
+++ b/gdb/mi/mi-cmd-stack.c
@@ -681,10 +681,8 @@ list_args_or_locals (enum what_to_list what, enum print_values values,
 void
 mi_cmd_stack_select_frame (const char *command, char **argv, int argc)
 {
-  if (argc == 0 || argc > 1)
+  if (!select_frame_from_spec (argv, argc, 0))
     error (_("-stack-select-frame: Usage: FRAME_SPEC"));
-
-  select_frame_command (argv[0], 1 /* not used */ );
 }
 
 void
diff --git a/gdb/stack.c b/gdb/stack.c
index e51c689f437..e32b3efa245 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -102,6 +102,9 @@ static void set_last_displayed_sal (int valid,
 				    struct symtab *symtab,
 				    int line);
 
+static struct frame_info *find_frame_for_function (const char *);
+static struct frame_info *find_frame_for_address (CORE_ADDR);
+
 /* Zero means do things normally; we are interacting directly with the
    user.  One means print the full filename and linenumber when a
    frame is printed, and do so in a format emacs18/emacs19.22 can
@@ -1255,134 +1258,34 @@ print_frame (struct frame_info *frame, int print_level,
 }
 \f
 
-/* Read a frame specification in whatever the appropriate format is from
-   FRAME_EXP.  Call error() if the specification is in any way invalid (so
-   this function never returns NULL).  When SELECTED_FRAME_P is non-NULL
-   set its target to indicate that the default selected frame was used.  */
+/* Completion function for "frame function", "info frame function", and
+   "select-frame function" commands.  */
 
-static struct frame_info *
-parse_frame_specification (const char *frame_exp, int *selected_frame_p)
+void
+frame_selection_by_function_completer (struct cmd_list_element *ignore,
+				       completion_tracker &tracker,
+				       const char *text, const char *word)
 {
-  int numargs;
-  struct value *args[4];
-  CORE_ADDR addrs[ARRAY_SIZE (args)];
-
-  if (frame_exp == NULL)
-    numargs = 0;
-  else
-    {
-      numargs = 0;
-      while (1)
-	{
-	  const char *p;
-
-	  /* Skip leading white space, bail of EOL.  */
-	  frame_exp = skip_spaces (frame_exp);
-	  if (!*frame_exp)
-	    break;
-
-	  /* Parse the argument, extract it, save it.  */
-	  for (p = frame_exp;
-	       *p && !ISSPACE (*p);
-	       p++);
-	  std::string addr_string (frame_exp, p - frame_exp);
-	  frame_exp = p;
-	  
-	  /* NOTE: Parse and evaluate expression, but do not use
-	     functions such as parse_and_eval_long or
-	     parse_and_eval_address to also extract the value.
-	     Instead value_as_long and value_as_address are used.
-	     This avoids problems with expressions that contain
-	     side-effects.  */
-	  if (numargs >= ARRAY_SIZE (args))
-	    error (_("Too many args in frame specification"));
-	  args[numargs++] = parse_and_eval (addr_string.c_str ());
-	}
-    }
-
-  /* If no args, default to the selected frame.  */
-  if (numargs == 0)
-    {
-      if (selected_frame_p != NULL)
-	(*selected_frame_p) = 1;
-      return get_selected_frame (_("No stack."));
-    }
-
-  /* None of the remaining use the selected frame.  */
-  if (selected_frame_p != NULL)
-    (*selected_frame_p) = 0;
-
-  /* Assume the single arg[0] is an integer, and try using that to
-     select a frame relative to current.  */
-  if (numargs == 1)
-    {
-      struct frame_info *fid;
-      int level = value_as_long (args[0]);
-
-      fid = find_relative_frame (get_current_frame (), &level);
-      if (level == 0)
-	/* find_relative_frame was successful.  */
-	return fid;
-    }
-
-  /* Convert each value into a corresponding address.  */
-  {
-    int i;
-
-    for (i = 0; i < numargs; i++)
-      addrs[i] = value_as_address (args[i]);
-  }
-
-  /* Assume that the single arg[0] is an address, use that to identify
-     a frame with a matching ID.  Should this also accept stack/pc or
-     stack/pc/special.  */
-  if (numargs == 1)
-    {
-      struct frame_id id = frame_id_build_wild (addrs[0]);
-      struct frame_info *fid;
-
-      /* If (s)he specifies the frame with an address, he deserves
-	 what (s)he gets.  Still, give the highest one that matches.
-	 (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't
-	 know).  */
-      for (fid = get_current_frame ();
-	   fid != NULL;
-	   fid = get_prev_frame (fid))
-	{
-	  if (frame_id_eq (id, get_frame_id (fid)))
-	    {
-	      struct frame_info *prev_frame;
-
-	      while (1)
-		{
-		  prev_frame = get_prev_frame (fid);
-		  if (!prev_frame
-		      || !frame_id_eq (id, get_frame_id (prev_frame)))
-		    break;
-		  fid = prev_frame;
-		}
-	      return fid;
-	    }
-	}
-      }
-
-  /* We couldn't identify the frame as an existing frame, but
-     perhaps we can create one with a single argument.  */
-  if (numargs == 1)
-    return create_new_frame (addrs[0], 0);
-  else if (numargs == 2)
-    return create_new_frame (addrs[0], addrs[1]);
-  else
-    error (_("Too many args in frame specification"));
+  /* This is used to complete function names within a stack.  It would be
+     nice if instead of offering all available function names, we only
+     offered functions that were actually in the stack.  However, this
+     would probably mean unwinding the stack to completion, which could
+     take too long (or on a corrupted stack, possibly not end).  For now I
+     offer all symbol names as a safer choice.  */
+  collect_symbol_completion_matches (tracker,
+				     complete_symbol_mode::EXPRESSION,
+				     symbol_name_match_type::EXPRESSION,
+				     text, word);
 }
 
-/* Print verbosely the selected frame or the frame at address
-   ADDR_EXP.  Absolutely all information in the frame is printed.  */
+/* Core of all the "info frame" sub-commands.  Print verbosely the frame FI
+   if SELECTED_FRAME_P is true then frame FI is the current frame, which
+   was selected as a default due to the user not providing any arguments
+   to select some other frame.  */
 
 static void
-info_frame_command (const char *addr_exp, int from_tty)
+info_frame_command_core (struct frame_info *fi, bool selected_frame_p)
 {
-  struct frame_info *fi;
   struct symbol *func;
   struct symtab *s;
   struct frame_info *calling_frame_info;
@@ -1390,7 +1293,6 @@ info_frame_command (const char *addr_exp, int from_tty)
   const char *funname = 0;
   enum language funlang = language_unknown;
   const char *pc_regname;
-  int selected_frame_p;
   struct gdbarch *gdbarch;
   CORE_ADDR frame_pc;
   int frame_pc_p;
@@ -1398,7 +1300,6 @@ info_frame_command (const char *addr_exp, int from_tty)
   CORE_ADDR caller_pc = 0;
   int caller_pc_p = 0;
 
-  fi = parse_frame_specification (addr_exp, &selected_frame_p);
   gdbarch = get_frame_arch (fi);
 
   /* Name of the value returned by get_frame_pc().  Per comments, "pc"
@@ -1739,6 +1640,140 @@ trailing_outermost_frame (int count)
   return trailing;
 }
 
+/* The core of all the "select-frame" sub-commands.  Just wraps a call to
+   SELECT_FRAME.  */
+
+static void
+select_frame_command_core (struct frame_info *fi, bool ignored)
+{
+  struct frame_info *prev_frame = get_selected_frame_if_set ();
+  select_frame (fi);
+  if (get_selected_frame_if_set () != prev_frame)
+    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
+}
+
+/* The core of all the "frame" sub-commands.  Select frame FI, and if this
+   means we change frame send out a change notification (otherwise, just
+   reprint the current frame summary).   */
+
+static void
+frame_command_core (struct frame_info *fi, bool ignored)
+{
+  struct frame_info *prev_frame = get_selected_frame_if_set ();
+
+  select_frame (fi);
+  if (get_selected_frame_if_set () != prev_frame)
+    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
+  else
+    print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME);
+}
+
+/* The three commands 'frame', 'select-frame', and 'info frame' all have a
+   common set of sub-commands that allow a specific frame to be selected.
+   All of the sub-command functions are static methods within this class
+   template which is then instantiated below.  */
+
+template <void (*FPTR) (struct frame_info *, bool)>
+class frame_command_helper
+{
+public:
+
+  /* The "frame level" family of commands.  The ARG is a number that is
+     the frame's level in the stack.  */
+  static void
+  level (const char *arg, int from_tty)
+  {
+    int level = value_as_long (parse_and_eval (arg));
+    struct frame_info *fid
+      = find_relative_frame (get_current_frame (), &level);
+    if (level != 0)
+      error (_("No frame at level %s."), arg);
+    FPTR (fid, false);
+  }
+
+  /* The "frame address" family of commands.  ARG is a stack-pointer
+     address for an existing frame.  This command does not allow new
+     frames to be created.  */
+
+  static void
+  address (const char *arg, int from_tty)
+  {
+    CORE_ADDR addr = value_as_address (parse_and_eval (arg));
+    struct frame_info *fid = find_frame_for_address (addr);
+    if (fid == NULL)
+      error (_("No frame at address %s."), arg);
+    FPTR (fid, false);
+  }
+
+  /* The "frame view" family of commands.  ARG is one or two addresses and
+     is used to view a frame that might be outside the current backtrace.
+     The addresses are stack-pointer address, and (optional) pc-address.  */
+
+  static void
+  view (const char *args, int from_tty)
+  {
+    struct frame_info *fid;
+
+    if (args == NULL)
+    error (_("Missing address argument to view a frame"));
+
+    gdb_argv argv (args);
+
+    if (argv.count () == 2)
+      {
+	CORE_ADDR addr[2];
+
+	addr [0] = value_as_address (parse_and_eval (argv[0]));
+	addr [1] = value_as_address (parse_and_eval (argv[1]));
+	fid = create_new_frame (addr[0], addr[1]);
+      }
+    else
+      {
+	CORE_ADDR addr = value_as_address (parse_and_eval (argv[0]));
+	fid = create_new_frame (addr, false);
+      }
+    FPTR (fid, false);
+  }
+
+  /* The "frame function" family of commands.  ARG is the name of a
+     function within the stack, the first function (searching from frame
+     0) with that name will be selected.  */
+
+  static void
+  function (const char *arg, int from_tty)
+  {
+    if (arg == NULL)
+      error (_("Missing function name argument"));
+    struct frame_info *fid = find_frame_for_function (arg);
+    if (fid == NULL)
+      error (_("No frame for function \"%s\"."), arg);
+    FPTR (fid, false);
+  }
+
+  /* The "frame" base command, that is, when no sub-command is specified.
+     If one argument is provided then we assume that this is a frame level
+     number as historically, this was the supported command syntax that was
+     used most often.
+
+     If no argument is provided, then the current frame is selected.  */
+
+  static void
+  base_command (const char *arg, int from_tty)
+  {
+    if (arg == NULL)
+      FPTR (get_selected_frame (_("No stack.")), true);
+    else
+      level (arg, from_tty);
+  }
+};
+
+/* Instantiate three FRAME_COMMAND_HELPER instances to implement the
+   sub-commands for 'info frame', 'frame', and 'select-frame' commands.  */
+
+static frame_command_helper <info_frame_command_core> info_frame_cmd;
+static frame_command_helper <frame_command_core> frame_cmd;
+static frame_command_helper <select_frame_command_core> select_frame_cmd;
+
 /* Print briefly all stack frames or just the innermost COUNT_EXP
    frames.  */
 
@@ -2227,37 +2262,43 @@ find_relative_frame (struct frame_info *frame, int *level_offset_ptr)
   return frame;
 }
 
-/* The "select_frame" command.  With no argument this is a NOP.
-   Select the frame at level LEVEL_EXP if it is a valid level.
-   Otherwise, treat LEVEL_EXP as an address expression and select it.
-
-   See parse_frame_specification for more info on proper frame
-   expressions.  */
-
-void
-select_frame_command (const char *level_exp, int from_tty)
-{
-  struct frame_info *prev_frame = get_selected_frame_if_set ();
-
-  select_frame (parse_frame_specification (level_exp, NULL));
-  if (get_selected_frame_if_set () != prev_frame)
-    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
-}
-
-/* The "frame" command.  With no argument, print the selected frame
-   briefly.  With an argument, behave like select_frame and then print
-   the selected frame.  */
+/* Select a frame using ARGV.  This is used from the MI -stack-select-frame
+   to provide the same frame specification mechanism that the CLI has for
+   commands like 'frame'.  The return value is true if the contents of
+   ARGV looked like a sensible attempt to change the frame (an error might
+   still be thrown though), or false if the contents of ARGV are not a
+   correct frame specification.  */
 
-static void
-frame_command (const char *level_exp, int from_tty)
+bool
+select_frame_from_spec (char **argv, int argc, int from_tty)
 {
-  struct frame_info *prev_frame = get_selected_frame_if_set ();
+  if (argc == 1)
+    select_frame_cmd.level (argv[0], from_tty);
+  else if (argc == 2
+	   && strncasecmp ("level", argv[0], strlen (argv[0])) == 0)
+    select_frame_cmd.level (argv[1], from_tty);
+  else if (argc == 2
+	   && strncasecmp ("address", argv[0], strlen (argv[0])) == 0)
+    select_frame_cmd.address (argv[1], from_tty);
+  else if (argc == 2
+	   && strncasecmp ("function", argv[0], strlen (argv[0])) == 0)
+    select_frame_cmd.function (argv[1], from_tty);
+  else if ((argc == 2 || argc == 3)
+	   && strncasecmp ("view", argv[0], strlen (argv[0])) == 0)
+    {
+      std::string arg;
+
+      if (argc == 2)
+	arg = string_printf ("%s", argv[1]);
+      else
+	arg = string_printf ("%s %s", argv[1], argv[2]);
 
-  select_frame (parse_frame_specification (level_exp, NULL));
-  if (get_selected_frame_if_set () != prev_frame)
-    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
+      select_frame_cmd.view (arg.c_str (), from_tty);
+    }
   else
-    print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME);
+    return false;
+
+  return true;
 }
 
 /* Select the frame up one or COUNT_EXP stack levels from the
@@ -2712,13 +2753,61 @@ faas_command (const char *cmd, int from_tty)
 }
 
 
+/* Find inner-mode frame with frame address ADDRESS.  Return NULL if no
+   matching frame can be found.  */
+
+static struct frame_info *
+find_frame_for_address (CORE_ADDR address)
+{
+  struct frame_id id;
+  struct frame_info *fid;
+
+  id = frame_id_build_wild (address);
+
+  /* If (s)he specifies the frame with an address, he deserves
+     what (s)he gets.  Still, give the highest one that matches.
+     (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't
+     know).  */
+  for (fid = get_current_frame ();
+       fid != NULL;
+       fid = get_prev_frame (fid))
+    {
+      if (frame_id_eq (id, get_frame_id (fid)))
+	{
+	  struct frame_info *prev_frame;
+
+	  while (1)
+	    {
+	      prev_frame = get_prev_frame (fid);
+	      if (!prev_frame
+		  || !frame_id_eq (id, get_frame_id (prev_frame)))
+		break;
+	      fid = prev_frame;
+	    }
+	  return fid;
+	}
+    }
+  return NULL;
+}
+
+\f
+
+/* Commands with a prefix of `frame apply'.  */
+static struct cmd_list_element *frame_apply_cmd_list = NULL;
+
 /* Commands with a prefix of `frame'.  */
-struct cmd_list_element *frame_cmd_list = NULL;
+static struct cmd_list_element *frame_cmd_list = NULL;
+
+/* Commands with a prefix of `select frame'.  */
+static struct cmd_list_element *select_frame_cmd_list = NULL;
+
+/* Commands with a prefix of `info frame'.  */
+static struct cmd_list_element *info_frame_cmd_list = NULL;
 
 void
 _initialize_stack (void)
 {
-  static struct cmd_list_element *frame_apply_list = NULL;
+  struct cmd_list_element *cmd;
 
   add_com ("return", class_stack, return_command, _("\
 Make selected stack frame return to its caller.\n\
@@ -2742,12 +2831,12 @@ An argument says how many frames down to go."));
 Same as the `down' command, but does not print anything.\n\
 This is useful in command scripts."));
 
-  add_prefix_cmd ("frame", class_stack, frame_command, _("\
-Select and print a stack frame.\nWith no argument, \
-print the selected stack frame.  (See also \"info frame\").\n\
-An argument specifies the frame to select.\n\
-It can be a stack frame number or the address of the frame."),
-		  &frame_cmd_list, "frame ", 1, &cmdlist);
+  add_prefix_cmd ("frame", class_stack,
+                  &frame_cmd.base_command, _("\
+Select and print a stack frame.\n\
+With no argument, print the selected stack frame.  (See also \"info frame\").\n\
+A single numerical argument specifies the frame to select."),
+                  &frame_cmd_list, "frame ", 1, &cmdlist);
 
   add_com_alias ("f", "frame", class_stack, 1);
 
@@ -2765,7 +2854,7 @@ or produces no output."
 Usage: frame apply COUNT [FLAG]... COMMAND\n\
 With a negative COUNT argument, applies the command on outermost -COUNT frames.\n"
 FRAME_APPLY_FLAGS_HELP),
-		  &frame_apply_list, "frame apply ", 1, &frame_cmd_list);
+		  &frame_apply_cmd_list, "frame apply ", 1, &frame_cmd_list);
 
   add_cmd ("all", class_stack, frame_apply_all_command,
 	   _("\
@@ -2773,7 +2862,7 @@ Apply a command to all frames.\n\
 \n\
 Usage: frame apply all [FLAG]... COMMAND\n"
 FRAME_APPLY_FLAGS_HELP),
-	   &frame_apply_list);
+	   &frame_apply_cmd_list);
 
   add_cmd ("level", class_stack, frame_apply_level_command,
 	   _("\
@@ -2782,19 +2871,97 @@ Apply a command to a list of frames.\n\
 Usage: frame apply level LEVEL... [FLAG]... COMMAND\n\
 ID is a space-separated list of LEVELs of frames to apply COMMAND on.\n"
 FRAME_APPLY_FLAGS_HELP),
-	   &frame_apply_list);
+	   &frame_apply_cmd_list);
 
   add_com ("faas", class_stack, faas_command, _("\
 Apply a command to all frames (ignoring errors and empty output).\n\
 Usage: faas COMMAND\n\
 shortcut for 'frame apply all -s COMMAND'"));
 
-  add_com_suppress_notification ("select-frame", class_stack, select_frame_command, _("\
+
+  add_prefix_cmd ("frame", class_stack,
+		  &frame_cmd.base_command, _("\
+Select and print a stack frame.\n\
+With no argument, print the selected stack frame.  (See also \"info frame\").\n\
+A single numerical argument specifies the frame to select."),
+		  &frame_cmd_list, "frame ", 1, &cmdlist);
+  add_com_alias ("f", "frame", class_stack, 1);
+
+  add_cmd ("address", class_stack, &frame_cmd.address,
+	   _("\
+Select and print a stack frame by stack address\n\
+\n\
+Usage: frame address STACK-ADDRESS"),
+	   &frame_cmd_list);
+
+  add_cmd ("view", class_stack, &frame_cmd.view,
+	   _("\
+View a stack frame that might be outside the current backtrace.\n\
+\n\
+Usage: frame view STACK-ADDRESS\n\
+       frame view STACK-ADDRESS PC-ADDRESS"),
+	   &frame_cmd_list);
+
+  cmd = add_cmd ("function", class_stack, &frame_cmd.function,
+	   _("\
+Select and print a stack frame by function name.\n\
+\n\
+Usage: frame function NAME\n\
+\n\
+The innermost frame that visited function NAME is selected."),
+	   &frame_cmd_list);
+  set_cmd_completer (cmd, frame_selection_by_function_completer);
+
+
+  add_cmd ("level", class_stack, &frame_cmd.level,
+	   _("\
+Select and print a stack frame by level.\n\
+\n\
+Usage: frame level NUMBER"),
+	   &frame_cmd_list);
+
+  cmd = add_prefix_cmd_suppress_notification ("select-frame", class_stack,
+		      &select_frame_cmd.base_command, _("\
 Select a stack frame without printing anything.\n\
-An argument specifies the frame to select.\n\
-It can be a stack frame number or the address of the frame."),
+A single numerical argument specifies the frame to select."),
+		      &select_frame_cmd_list, "select-frame ", 1, &cmdlist,
+		      &cli_suppress_notification.user_selected_context);
+
+  add_cmd_suppress_notification ("address", class_stack,
+			 &select_frame_cmd.address, _("\
+Select a stack frame by stack address.\n\
+\n\
+Usage: select-frame address STACK-ADDRESS"),
+			 &select_frame_cmd_list,
+			 &cli_suppress_notification.user_selected_context);
+
+
+  add_cmd_suppress_notification ("view", class_stack,
+		 &select_frame_cmd.view, _("\
+Select a stack frame that might be outside the current backtrace.\n\
+\n\
+Usage: select-frame view STACK-ADDRESS\n\
+       select-frame view STACK-ADDRESS PC-ADDRESS"),
+		 &select_frame_cmd_list,
 		 &cli_suppress_notification.user_selected_context);
 
+  cmd = add_cmd_suppress_notification ("function", class_stack,
+	       &select_frame_cmd.function, _("\
+Select a stack frame by function name.\n\
+\n\
+Usage: select-frame function NAME"),
+	       &select_frame_cmd_list,
+	       &cli_suppress_notification.user_selected_context);
+  set_cmd_completer (cmd, frame_selection_by_function_completer);
+
+  add_cmd_suppress_notification ("level", class_stack,
+			 &select_frame_cmd.level, _("\
+Select a stack frame by level.\n\
+\n\
+Usage: select-frame level NUMBER"),
+			 &select_frame_cmd_list,
+			 &cli_suppress_notification.user_selected_context);
+
   add_com ("backtrace", class_stack, backtrace_command, _("\
 Print backtrace of all stack frames, or innermost COUNT frames.\n\
 Usage: backtrace [QUALIFIERS]... [COUNT]\n\
@@ -2808,9 +2975,45 @@ on this backtrace."));
   add_info ("stack", backtrace_command,
 	    _("Backtrace of the stack, or innermost COUNT frames."));
   add_info_alias ("s", "stack", 1);
-  add_info ("frame", info_frame_command,
-	    _("All about selected stack frame, or frame at ADDR."));
+
+  add_prefix_cmd ("frame", class_info, &info_frame_cmd.base_command,
+		  _("All about the selected stack frame.\n\
+With no arguments, displays information about the currently selected stack\n\
+frame.  Alternatively a frame specification may be provided (See \"frame\")\n\
+the information is then printed about the specified frame."),
+		  &info_frame_cmd_list, "info frame ", 1, &infolist);
   add_info_alias ("f", "frame", 1);
+
+  add_cmd ("address", class_stack, &info_frame_cmd.address,
+	   _("\
+Print information about a stack frame selected by stack address.\n\
+\n\
+Usage: info frame address STACK-ADDRESS"),
+	   &info_frame_cmd_list);
+
+  add_cmd ("view", class_stack, &info_frame_cmd.view,
+	   _("\
+Print information about a stack frame outside the current backtrace.\n\
+\n\
+Usage: info frame view STACK-ADDRESS\n\
+       info frame view STACK-ADDRESS PC-ADDRESS"),
+	   &info_frame_cmd_list);
+
+  cmd = add_cmd ("function", class_stack, &info_frame_cmd.function,
+	   _("\
+Print information about a stack frame selected by function name.\n\
+\n\
+Usage: info frame function NAME"),
+	   &info_frame_cmd_list);
+  set_cmd_completer (cmd, frame_selection_by_function_completer);
+
+  add_cmd ("level", class_stack, &info_frame_cmd.level,
+	   _("\
+Print information about a stack frame selected by level.\n\
+\n\
+Usage: info frame level NUMBER"),
+	   &info_frame_cmd_list);
+
   add_info ("locals", info_locals_command,
 	    _("Local variables of current stack frame."));
   add_info ("args", info_args_command,
diff --git a/gdb/stack.h b/gdb/stack.h
index ca190efa9c3..29794872d70 100644
--- a/gdb/stack.h
+++ b/gdb/stack.h
@@ -20,7 +20,7 @@
 #ifndef STACK_H
 #define STACK_H
 
-void select_frame_command (const char *level_exp, int from_tty);
+bool select_frame_from_spec (char **argv, int argc, int from_tty);
 
 gdb::unique_xmalloc_ptr<char> find_frame_funname (struct frame_info *frame,
 						  enum language *funlang,
diff --git a/gdb/testsuite/gdb.base/frame-selection.c b/gdb/testsuite/gdb.base/frame-selection.c
new file mode 100644
index 00000000000..f3d81f223e0
--- /dev/null
+++ b/gdb/testsuite/gdb.base/frame-selection.c
@@ -0,0 +1,52 @@
+/* Copyright 2018 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int
+frame_2 (void)
+{
+  return 0;
+}
+
+int
+frame_1 (void)
+{
+  return frame_2 ();
+}
+
+int
+recursive (int arg)
+{
+  int v;
+
+  if (arg < 2)
+    v = recursive (arg + 1);
+  else
+    v = frame_2 ();
+
+  return v;
+}
+
+int
+main (void)
+{
+  int i, j;
+
+  i = frame_1 ();
+  j = recursive (0);
+
+  return i + j;
+}
diff --git a/gdb/testsuite/gdb.base/frame-selection.exp b/gdb/testsuite/gdb.base/frame-selection.exp
new file mode 100644
index 00000000000..3bfcf7b47aa
--- /dev/null
+++ b/gdb/testsuite/gdb.base/frame-selection.exp
@@ -0,0 +1,157 @@
+# Copyright 2018 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+standard_testfile
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug}]} {
+    return -1
+}
+
+runto_main
+gdb_breakpoint frame_2
+gdb_continue_to_breakpoint frame_2
+
+gdb_test "bt" "#0  frame_2.*#1  $hex in frame_1.*#2  $hex in main.*" "backtrace at breakpoint"
+
+# Perform "info frame" to extract the frames address.
+proc get_frame_address { {testname ""} } {
+    global hex gdb_prompt
+
+    set testname "get_frame_address: ${testname}"
+    set frame_address "unknown"
+
+    send_gdb "info frame\n"
+    gdb_expect {
+	-re ", frame at ($hex):\r\n.*\r\n$gdb_prompt $" {
+	    set frame_address $expect_out(1,string)
+	    pass $testname
+	}
+	-re ".*$gdb_prompt $" { fail $testname }
+	timeout { fail "$testname (timeout)" }
+    }
+
+    return $frame_address
+}
+
+# Passes a list of addresses.  Return a new address that is not in the
+# list.
+proc get_new_address { {addresses {}} } {
+    return [format "%#x" [expr [lindex $addresses [llength addresses]] + 0x10 ]]
+}
+
+proc check_frame { level address function } {
+    global hex gdb_prompt
+
+    set testname "check frame level ${level}"
+    send_gdb "info frame\n"
+    gdb_expect {
+	-re "Stack level ${level}, frame at ($address):\r\n rip = $hex in ${function} \(\[^\r\n\]*\); saved rip = $hex\r\n.*\r\n$gdb_prompt $" {
+	    pass $testname
+	}
+	-re ".*$gdb_prompt $" { fail $testname }
+	timeout { fail "$testname (timeout)" }
+    }
+}
+
+# Select frame using level number, but relying on this being the
+# default action, so "frame 0" performs "frame level 0".
+gdb_test "frame 0" "#0  frame_2.*"
+set frame_0_address [ get_frame_address "frame 0" ]
+gdb_test "frame 1" "#1  $hex in frame_1.*"
+set frame_1_address [ get_frame_address "frame 1" ]
+gdb_test "frame 2" "#2  $hex in main.*"
+set frame_2_address [ get_frame_address "frame 2" ]
+gdb_test "frame 3" "No frame at level 3\."
+
+# Find an address that matches no frame
+set no_frame_address [ get_new_address [list $frame_0_address \
+					     $frame_1_address \
+					     $frame_2_address] ]
+
+# Select frame using 'level' specification.
+gdb_test "frame level 0" "#0  frame_2.*"
+gdb_test "frame level 1" "#1  $hex in frame_1.*"
+gdb_test "frame level 2" "#2  $hex in main.*"
+gdb_test "frame level 3" "No frame at level 3\."
+
+# Select frame by address.
+gdb_test "frame address ${frame_0_address}" "#0  frame_2.*"
+gdb_test "frame address ${frame_1_address}" "#1  $hex in frame_1.*"
+gdb_test "frame address ${frame_2_address}" "#2  $hex in main.*"
+gdb_test "frame address ${no_frame_address}" \
+    "No frame at address ${no_frame_address}\."
+
+# Select frame by function.
+gdb_test "frame function frame_2" "#0  frame_2.*"
+gdb_test "frame function frame_1" "#1  $hex in frame_1.*"
+gdb_test "frame function main" "#2  $hex in main.*"
+# Check for a distinction between a known function not in the stack
+# trace, and an unknown function.
+gdb_test "frame function recursive" "No frame for function \"recursive\"."
+gdb_test "frame function foo" "Function \"foo\" not defined."
+
+
+gdb_test_no_output "select-frame 0"
+check_frame "0" "${frame_0_address}" "frame_2"
+gdb_test_no_output "select-frame 1"
+check_frame "1" "${frame_1_address}" "frame_1"
+gdb_test_no_output "select-frame 2"
+check_frame "2" "${frame_2_address}" "main"
+gdb_test "select-frame 3" "No frame at level 3\."
+
+gdb_test_no_output "select-frame level 0"
+check_frame "0" "${frame_0_address}" "frame_2"
+gdb_test_no_output "select-frame level 1"
+check_frame "1" "${frame_1_address}" "frame_1"
+gdb_test_no_output "select-frame level 2"
+check_frame "2" "${frame_2_address}" "main"
+gdb_test "select-frame level 3" "No frame at level 3\."
+
+gdb_test_no_output "select-frame address ${frame_0_address}"
+check_frame "0" "${frame_0_address}" "frame_2"
+gdb_test_no_output "select-frame address ${frame_1_address}"
+check_frame "1" "${frame_1_address}" "frame_1"
+gdb_test_no_output "select-frame address ${frame_2_address}"
+check_frame "2" "${frame_2_address}" "main"
+gdb_test "select-frame address ${no_frame_address}" \
+    "No frame at address ${no_frame_address}\."
+
+gdb_test_no_output "select-frame function frame_2"
+check_frame "0" "${frame_0_address}" "frame_2"
+gdb_test_no_output "select-frame function frame_1"
+check_frame "1" "${frame_1_address}" "frame_1"
+gdb_test_no_output "select-frame function main"
+check_frame "2" "${frame_2_address}" "main"
+# Check for a distinction between a known function not in the stack
+# trace, and an unknown function.
+gdb_test "select-frame function recursive" \
+    "No frame for function \"recursive\"."
+gdb_test "select-frame function foo" \
+    "Function \"foo\" not defined."
+
+# Now continue until we hit the breakpoint again.
+gdb_continue_to_breakpoint frame_2
+gdb_test "bt" \
+    "#0  frame_2.*#1  $hex in recursive.*#2  $hex in recursive.*#3  $hex in recursive.*#4  $hex in main.*" \
+    "backtrace at breakpoint with recursive frames"
+
+# Check "frame function" when a function name occurs multiple times in
+# the stack.  The inner most (lowest level number) should always be
+# selected.
+gdb_test "frame function frame_2" "#0  frame_2.*"
+gdb_test "frame function recursive" "#1  $hex in recursive.*"
+gdb_test "frame function recursive" "#1  $hex in recursive.*"
+gdb_test "frame function main" "#4  $hex in main.*"
+gdb_test "frame function recursive" "#1  $hex in recursive.*"
diff --git a/gdb/testsuite/gdb.mi/mi-frame-selection.c b/gdb/testsuite/gdb.mi/mi-frame-selection.c
new file mode 100644
index 00000000000..70b992c9149
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-frame-selection.c
@@ -0,0 +1,34 @@
+/* Copyright 2018 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int
+frame_2 (void)
+{
+  return 0;
+}
+
+int
+frame_1 (void)
+{
+  return frame_2 ();
+}
+
+int
+main (void)
+{
+  return frame_1 ();
+}
diff --git a/gdb/testsuite/gdb.mi/mi-frame-selection.exp b/gdb/testsuite/gdb.mi/mi-frame-selection.exp
new file mode 100644
index 00000000000..e24c7179b9d
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-frame-selection.exp
@@ -0,0 +1,89 @@
+# Copyright 2018 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test the frame selection syntax with the -stack-select-frame command.
+
+load_lib mi-support.exp
+set MIFLAGS "-i=mi"
+
+gdb_exit
+if [mi_gdb_start] {
+    continue
+}
+
+standard_testfile
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    untested $testfile
+    return -1
+}
+
+mi_delete_breakpoints
+mi_gdb_reinitialize_dir $srcdir/$subdir
+mi_gdb_load ${binfile}
+
+mi_create_breakpoint "frame_2" \
+    "break-insert into frame_2" \
+    -number 1 -func frame_2 -file ".*${srcfile}"
+
+mi_run_cmd
+mi_expect_stop "breakpoint-hit" "frame_2" "" ".*${srcfile}" \
+	".*" { "" "disp=\"keep\"" } "run to breakpoint"
+
+mi_gdb_test "-stack-info-depth" \
+    "\\^done,depth=\"3\"" \
+    "stack info-depth"
+
+for {set i 0} {$i < 5} {incr i} {
+
+    if { $i < 3 } then {
+	mi_gdb_test "-stack-select-frame $i" \
+	    {\^done} \
+	    "-stack-select-frame $i"
+
+	mi_gdb_test "-stack-info-frame" \
+	    "\\^done,frame=\\\{level=\"$i\"\[^\}\]+\\\}" \
+	    "check level has has changed to $i"
+
+    } else {
+	mi_gdb_test "-stack-select-frame view $i" \
+	    {\^done} \
+	    "-stack-select-frame view frame at $i"
+
+	mi_gdb_test "-stack-info-frame" \
+	    "\\^done,frame=\\\{level=\"0\"\[^\}\]+\\\}" \
+	    "check level has has changed to 0, when creating a frame at sp=$i"
+    }
+}
+
+mi_gdb_test "-stack-select-frame 5" \
+    {\^error,msg="No frame at level 5."} \
+    "-stack-select-frame 5, with no keyword"
+
+mi_gdb_test "-stack-select-frame level 5" \
+    {\^error,msg="No frame at level 5."} \
+    "-stack-select-frame 5, using 'level' keyword"
+
+mi_gdb_test "-stack-select-frame function main" \
+    {\^done} \
+    "-stack-select-frame select frame for function main"
+
+mi_gdb_test "-stack-info-frame" \
+    "\\^done,frame=\\\{level=\"2\"\[^\}\]+\\\}" \
+    "check level has has changed to 2 for function main"
+
+mi_gdb_test "-stack-select-frame function unknown_function" \
+    {\^error,msg=\"Function \\\"unknown_function\\\" not defined.\"} \
+    "-stack-select-frame check error on undefined function name"
-- 
2.14.4

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCHv4] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-07-17 15:58     ` [PATCHv4] " Andrew Burgess
@ 2018-07-23 20:46       ` Philippe Waroquiers
  2018-07-25 18:14         ` Andrew Burgess
  0 siblings, 1 reply; 35+ messages in thread
From: Philippe Waroquiers @ 2018-07-23 20:46 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

On Tue, 2018-07-17 at 16:57 +0100, Andrew Burgess wrote:
> Now that the 'frame apply' command has been merged to master, this is
> a rebase of the v3 patch.  Merge conflicts have been resolved, but the
> content is largely unchanged.
FWIW, I like the idea of the patch, as it is too easy to
(too silently) get a wrong frame with 'frame <a wrong frame level/number>'.

Find below some comments to discuss the terminology 'frame level'
versus 'frame number'.

Philippe

> 
> Thanks,
> Andrew
> 
> ---
> 
> The 'frame' command, and thanks to code reuse the 'info frame' and
> 'select-frame' commands, currently have an overloaded mechanism for
> selecting a frame.
> 
> These commands take one or two parameters, if it's one parameter then
> we first try to use the parameter as an integer to select a frame by
> level (or depth in the stack).  If that fails then we treat the
> parameter as an address and try to select a stack frame by
> stack-address.  If we still have not selected a stack frame, or we
> initially had two parameters, then GDB allows the user to view a stack
> frame that is not part of the current backtrace.  Internally, a new
> frame is created with the given stack and pc addresses, and this is
> shown to the user.
> 
> The result of this is that a typo by the user, entering the wrong stack
> frame level for example, can result in a brand new frame being viewed
> rather than an error.
> 
> The purpose of this commit is to remove this overloading, while still
> offering the same functionality through some new sub-commands.  By
> making the default behaviour of 'frame' (and friends) be to select a
> stack frame by level number, it is hoped that enough
> backwards-compatibility is maintained that users will not be overly
> inconvenienced.
> 
> The 'frame', 'select-frame', and 'info frame' commands now all take a
> frame specification string as an argument, this string can be any of the
> following:
> 
>   (1) An integer.  This is treated as a frame number.  If a frame for
>   that number does not exist then the user gets an error.
> 
>   (2) A string like 'level <NUMBER>', where <NUMBER> is a frame number
>   as in option (1) above.

I did a trial to scan the documentation for the usage of 'frame number',
in order to replace it by 'frame level'.
Note that Eli has some reserve about this idea/replacement,
see thread https://sourceware.org/ml/gdb-patches/2018-07/msg00365.html.

IMO, the sentences (1) and (2) above should preferably
use consistently 'frame level' 'level <LEVEL>' : at the moment,
we seem to semi-randomly use level or number, explaining at some places
that level is a frame number. 

It would be good to clarify exactly what naming convention should
be used, and apply it (more) systematically (in the existing
documentation and/or in this patch).



>   (3) A string like 'address <ADDRESS>', where <ADDRESS> is a
>   stack-frame address.  If there is no frame for this address then the
>   user gets an error.
Below, (5) and (6) are using STACK-ADDRESS instead of ADDRESS.
I am not sure to understand if there is (or not) a difference of concept
between this ADDRESS and the below STACK-ADDRESS.
I am wondering which address we are speaking about.
When doing 'info frame', I get something like:
  (gdb) info frame
  Stack level 1, frame at 0x7fffffffdf80:
   rip = 0x555555554f5e in sleeper_or_burner (sleepers.c:86); saved rip = 0x55555555549d
   called by frame at 0x7fffffffe040, caller of frame at 0x7fffffffdf40
   source language c.
   Arglist at 0x7fffffffdf70, args: v=0x7fffffffdf90
   Locals at 0x7fffffffdf70, Previous frame's sp is 0x7fffffffdf80
   Saved registers:
    rbp at 0x7fffffffdf70, rip at 0x7fffffffdf78
  (gdb) 
Maybe it would be good to reference in the doc the field given
in the above output ?


Note that the above command output uses 'Stack level 1',
and not 'Stack number 1' :).


> 
>   (4) A string like 'function <NAME>', where <NAME> is a function name,
>   the inner most frame for function <NAME> is selected.  If there is no
>   frame for function <NAME> then the user gets an error.
> 
>   (5) A string like 'view <STACK-ADDRESS>', this views a new frame
>   with stack address <STACK-ADDRESS>.
> 
>   (6) A string like 'view <STACK-ADDRESS> <PC-ADDRESS>', this views
>   a new frame with stack address <STACK-ADDRESS> an the pc <PC-ADDRESS>.
> 
> This change assumes that the most common use of the commands like
> 'frame' is to select a frame by frame level number, it is for this
> reason that this is the behaviour that is kept for backwards
> compatibility.  Any of the alternative behaviours, which are assumed to
> be less used, now require a change in user behaviour.
> 
> The MI command '-stack-select-frame' has also changed in the same way.
> The default behaviour is to select a frame by level number, but the
> other selection methods are also available.

In the rest of the patch, assuming there is an agreement about
using frame level instead of frame number, there are some other
occurrences of number to replace/clarify.

Philippe

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCHv4] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-07-23 20:46       ` Philippe Waroquiers
@ 2018-07-25 18:14         ` Andrew Burgess
  2018-08-13 22:20           ` [PATCHv5 0/2] " Andrew Burgess
                             ` (2 more replies)
  0 siblings, 3 replies; 35+ messages in thread
From: Andrew Burgess @ 2018-07-25 18:14 UTC (permalink / raw)
  To: Philippe Waroquiers; +Cc: gdb-patches, Eli Zaretskii

* Philippe Waroquiers <philippe.waroquiers@skynet.be> [2018-07-23 22:45:55 +0200]:

> On Tue, 2018-07-17 at 16:57 +0100, Andrew Burgess wrote:
> > Now that the 'frame apply' command has been merged to master, this is
> > a rebase of the v3 patch.  Merge conflicts have been resolved, but the
> > content is largely unchanged.
> FWIW, I like the idea of the patch, as it is too easy to
> (too silently) get a wrong frame with 'frame <a wrong frame level/number>'.
> 
> Find below some comments to discuss the terminology 'frame level'
> versus 'frame number'.
> 
> Philippe
> 
> > 
> > Thanks,
> > Andrew
> > 
> > ---
> > 
> > The 'frame' command, and thanks to code reuse the 'info frame' and
> > 'select-frame' commands, currently have an overloaded mechanism for
> > selecting a frame.
> > 
> > These commands take one or two parameters, if it's one parameter then
> > we first try to use the parameter as an integer to select a frame by
> > level (or depth in the stack).  If that fails then we treat the
> > parameter as an address and try to select a stack frame by
> > stack-address.  If we still have not selected a stack frame, or we
> > initially had two parameters, then GDB allows the user to view a stack
> > frame that is not part of the current backtrace.  Internally, a new
> > frame is created with the given stack and pc addresses, and this is
> > shown to the user.
> > 
> > The result of this is that a typo by the user, entering the wrong stack
> > frame level for example, can result in a brand new frame being viewed
> > rather than an error.
> > 
> > The purpose of this commit is to remove this overloading, while still
> > offering the same functionality through some new sub-commands.  By
> > making the default behaviour of 'frame' (and friends) be to select a
> > stack frame by level number, it is hoped that enough
> > backwards-compatibility is maintained that users will not be overly
> > inconvenienced.
> > 
> > The 'frame', 'select-frame', and 'info frame' commands now all take a
> > frame specification string as an argument, this string can be any of the
> > following:
> > 
> >   (1) An integer.  This is treated as a frame number.  If a frame for
> >   that number does not exist then the user gets an error.
> > 
> >   (2) A string like 'level <NUMBER>', where <NUMBER> is a frame number
> >   as in option (1) above.
> 
> I did a trial to scan the documentation for the usage of 'frame number',
> in order to replace it by 'frame level'.
> Note that Eli has some reserve about this idea/replacement,
> see thread
> https://sourceware.org/ml/gdb-patches/2018-07/msg00365.html.

I seem to have opened a can of works with the choice of level here.

It's been a while since I wrote the original patch (almost 3 years)
so, honestly, I can't recall why I picked 'level' over 'number'.  It
probably seemed more descriptive at the time, but honestly I would be
just as happy to use 'number' instead.

So, what I'd really like is some guidance from .... well .... anyone
who cares really .... level or number ?

Thanks,
Andrew








> 
> IMO, the sentences (1) and (2) above should preferably
> use consistently 'frame level' 'level <LEVEL>' : at the moment,
> we seem to semi-randomly use level or number, explaining at some places
> that level is a frame number. 
> 
> It would be good to clarify exactly what naming convention should
> be used, and apply it (more) systematically (in the existing
> documentation and/or in this patch).
> 
> 
> 
> >   (3) A string like 'address <ADDRESS>', where <ADDRESS> is a
> >   stack-frame address.  If there is no frame for this address then the
> >   user gets an error.
> Below, (5) and (6) are using STACK-ADDRESS instead of ADDRESS.
> I am not sure to understand if there is (or not) a difference of concept
> between this ADDRESS and the below STACK-ADDRESS.
> I am wondering which address we are speaking about.
> When doing 'info frame', I get something like:
>   (gdb) info frame
>   Stack level 1, frame at 0x7fffffffdf80:
>    rip = 0x555555554f5e in sleeper_or_burner (sleepers.c:86); saved rip = 0x55555555549d
>    called by frame at 0x7fffffffe040, caller of frame at 0x7fffffffdf40
>    source language c.
>    Arglist at 0x7fffffffdf70, args: v=0x7fffffffdf90
>    Locals at 0x7fffffffdf70, Previous frame's sp is 0x7fffffffdf80
>    Saved registers:
>     rbp at 0x7fffffffdf70, rip at 0x7fffffffdf78
>   (gdb) 
> Maybe it would be good to reference in the doc the field given
> in the above output ?
> 
> 
> Note that the above command output uses 'Stack level 1',
> and not 'Stack number 1' :).
> 
> 
> > 
> >   (4) A string like 'function <NAME>', where <NAME> is a function name,
> >   the inner most frame for function <NAME> is selected.  If there is no
> >   frame for function <NAME> then the user gets an error.
> > 
> >   (5) A string like 'view <STACK-ADDRESS>', this views a new frame
> >   with stack address <STACK-ADDRESS>.
> > 
> >   (6) A string like 'view <STACK-ADDRESS> <PC-ADDRESS>', this views
> >   a new frame with stack address <STACK-ADDRESS> an the pc <PC-ADDRESS>.
> > 
> > This change assumes that the most common use of the commands like
> > 'frame' is to select a frame by frame level number, it is for this
> > reason that this is the behaviour that is kept for backwards
> > compatibility.  Any of the alternative behaviours, which are assumed to
> > be less used, now require a change in user behaviour.
> > 
> > The MI command '-stack-select-frame' has also changed in the same way.
> > The default behaviour is to select a frame by level number, but the
> > other selection methods are also available.
> 
> In the rest of the patch, assuming there is an agreement about
> using frame level instead of frame number, there are some other
> occurrences of number to replace/clarify.
> 
> Philippe
> 

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCHv5_B 2/2] gdb: Change how frames are selected for 'frame' and 'info frame'.
       [not found]           ` <cover.1534197765.git.andrew.burgess@embecosm.com>
@ 2018-08-13 22:20             ` Andrew Burgess
  0 siblings, 0 replies; 35+ messages in thread
From: Andrew Burgess @ 2018-08-13 22:20 UTC (permalink / raw)
  To: gdb-patches; +Cc: Philippe Waroquiers, Eli Zaretskii, Andrew Burgess

The 'frame' command, and thanks to code reuse the 'info frame' and
'select-frame' commands, currently have an overloaded mechanism for
selecting a frame.

These commands take one or two parameters, if it's one parameter then
we first try to use the parameter as an integer to select a frame by
level (or depth in the stack).  If that fails then we treat the
parameter as an address and try to select a stack frame by
stack-address.  If we still have not selected a stack frame, or we
initially had two parameters, then GDB allows the user to view a stack
frame that is not part of the current backtrace.  Internally, a new
frame is created with the given stack and pc addresses, and this is
shown to the user.

The result of this is that a typo by the user, entering the wrong stack
frame level for example, can result in a brand new frame being viewed
rather than an error.

The purpose of this commit is to remove this overloading, while still
offering the same functionality through some new sub-commands.  By
making the default behaviour of 'frame' (and friends) be to select a
stack frame by level index, it is hoped that enough
backwards-compatibility is maintained that users will not be overly
inconvenienced.

The 'frame', 'select-frame', and 'info frame' commands now all take a
frame specification string as an argument, this string can be any of the
following:

  (1) An integer.  This is treated as a frame level.  If a frame for
  that level does not exist then the user gets an error.

  (2) A string like 'level <LEVEL>', where <LEVEL> is a frame level
  as in option (1) above.

  (3) A string like 'address <STACK-ADDRESS>', where <STACK-ADDRESS>
  is a stack-frame address.  If there is no frame for this address
  then the user gets an error.

  (4) A string like 'function <NAME>', where <NAME> is a function name,
  the inner most frame for function <NAME> is selected.  If there is no
  frame for function <NAME> then the user gets an error.

  (5) A string like 'view <STACK-ADDRESS>', this views a new frame
  with stack address <STACK-ADDRESS>.

  (6) A string like 'view <STACK-ADDRESS> <PC-ADDRESS>', this views
  a new frame with stack address <STACK-ADDRESS> an the pc <PC-ADDRESS>.

This change assumes that the most common use of the commands like
'frame' is to select a frame by frame level, it is for this reason
that this is the behaviour that is kept for backwards compatibility.
Any of the alternative behaviours, which are assumed to be less used,
now require a change in user behaviour.

The MI command '-stack-select-frame' has also changed in the same way.
The default behaviour is to select a frame by level, but the other
selection methods are also available.

gdb/ChangeLog:

	(NEWS): Mention changes to frame related commands.
	* cli/cli-decode.c (add_cmd_suppress_notification): New function.
	(add_prefix_cmd_suppress_notification): New function.
	(add_com_suppress_notification): Call
	add_cmd_suppress_notification.
	* command.h (add_cmd_suppress_notification): Declare.
	(add_prefix_cmd_suppress_notification): Declare.
	* mi/mi-cmd-stack.c (mi_cmd_stack_select_frame): Use new function
	select_frame_from_spec.
	* stack.c (find_frame_for_function): Add declaration.
	(find_frame_for_address): New function.
	(parse_frame_specification): Delete function.
	(frame_selection_by_function_completer): New function.
	(info_frame_command): Rename to...
	(info_frame_command_core): ...this, and update parameter types.
	(select_frame_command): Rename to...
	(select_frame_command_core): ...this, and update parameter types.
	(frame_command): Rename to...
	(frame_command_core): ...this, and update parameter types.
	(class frame_command_helper): New class to wrap implementations of
	frame related sub-commands.
	(select_frame_from_spec): New function.
	(frame_apply_cmd_list): New static global.
	(frame_cmd_list): Make static.
	(select_frame_cmd_list): New global for sub-commands.
	(info_frame_cmd_list): New global for sub-commands.
	(_initialize_stack): Register sub-commands for 'frame',
	'select-frame', and 'info frame'.  Update 'frame apply' commands
	to use frame_apply_cmd_list.  Move function local static
	frame_apply_list to file static frame_apply_cmd_list for
	consistency.
	* stack.h (select_frame_command): Delete declarationn.
	(select_frame_from_spec): Declare new function.

gdb/doc/ChangeLog:

	* gdb.texinfo (Selection): Rewrite documentation for 'frame' and
	'select-frame' commands.
	(Frame Info): Rewrite documentation for 'info frame' command.
	(GDB/MI Stack Manipulation): Update description of
	'-stack-select-frame'.

gdb/testsuite/ChangeLog:

	* gdb.base/frame-selection.exp: New file.
	* gdb.base/frame-selection.c: New file.
	* gdb.mi/mi-frame-selection.exp: New file.
	* gdb.mi/mi-frame-selection.c: New file.
---
 gdb/ChangeLog                               |  36 ++
 gdb/NEWS                                    |   8 +
 gdb/cli/cli-decode.c                        |  44 ++-
 gdb/command.h                               |  14 +
 gdb/doc/ChangeLog                           |   8 +
 gdb/doc/gdb.texinfo                         | 108 ++++--
 gdb/mi/mi-cmd-stack.c                       |   4 +-
 gdb/stack.c                                 | 535 +++++++++++++++++++---------
 gdb/stack.h                                 |   2 +-
 gdb/testsuite/ChangeLog                     |   7 +
 gdb/testsuite/gdb.base/frame-selection.c    |  52 +++
 gdb/testsuite/gdb.base/frame-selection.exp  | 156 ++++++++
 gdb/testsuite/gdb.mi/mi-frame-selection.c   |  34 ++
 gdb/testsuite/gdb.mi/mi-frame-selection.exp |  89 +++++
 14 files changed, 895 insertions(+), 202 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/frame-selection.c
 create mode 100644 gdb/testsuite/gdb.base/frame-selection.exp
 create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.c
 create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index 7f9466567f2..daa3468aa95 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -74,6 +74,14 @@ thread apply [all | COUNT | -COUNT] [FLAG]... COMMAND
 * C expressions can now use _Alignof, and C++ expressions can now use
   alignof.
 
+* Changes to the "frame", "select-frame", and "info frame" CLI
+  commands, as well as the "-stack-select-frame" MI command.  These
+  commands all now take a frame specification which is either a frame
+  level, or one of the keywords 'level', 'address', 'function', or
+  'view' followed by a parameter.  Selecting a frame by address, or
+  viewing a frame outside the current backtrace now requires the use
+  of a keyword.  Selecting a frame by level is unchanged.
+
 * New commands
 
 set debug fbsd-nat
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 83dd67efe3f..5d798e877e8 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -251,6 +251,23 @@ add_cmd (const char *name, enum command_class theclass,
   return result;
 }
 
+/* Add an element with a suppress notification to the LIST of commands.  */
+
+struct cmd_list_element *
+add_cmd_suppress_notification (const char *name, enum command_class theclass,
+			       cmd_const_cfunc_ftype *fun, const char *doc,
+			       struct cmd_list_element **list,
+			       int *suppress_notification)
+{
+  struct cmd_list_element *element;
+
+  element = add_cmd (name, theclass, fun, doc, list);
+  element->suppress_notification = suppress_notification;
+
+  return element;
+}
+
+
 /* Deprecates a command CMD.
    REPLACEMENT is the name of the command which should be used in
    place of this command, or NULL if no such command exists.
@@ -362,6 +379,25 @@ add_prefix_cmd (const char *name, enum command_class theclass,
   return c;
 }
 
+/* Like ADD_PREFIX_CMD but sets the suppress_notification pointer on the
+   new command list element.  */
+
+struct cmd_list_element *
+add_prefix_cmd_suppress_notification
+               (const char *name, enum command_class theclass,
+		cmd_const_cfunc_ftype *fun,
+		const char *doc, struct cmd_list_element **prefixlist,
+		const char *prefixname, int allow_unknown,
+		struct cmd_list_element **list,
+		int *suppress_notification)
+{
+  struct cmd_list_element *element
+    = add_prefix_cmd (name, theclass, fun, doc, prefixlist,
+		      prefixname, allow_unknown, list);
+  element->suppress_notification = suppress_notification;
+  return element;
+}
+
 /* Like add_prefix_cmd but sets the abbrev_flag on the new command.  */
 
 struct cmd_list_element *
@@ -893,12 +929,8 @@ add_com_suppress_notification (const char *name, enum command_class theclass,
 			       cmd_const_cfunc_ftype *fun, const char *doc,
 			       int *suppress_notification)
 {
-  struct cmd_list_element *element;
-
-  element = add_cmd (name, theclass, fun, doc, &cmdlist);
-  element->suppress_notification = suppress_notification;
-
-  return element;
+  return add_cmd_suppress_notification (name, theclass, fun, doc,
+					&cmdlist, suppress_notification);
 }
 
 /* Recursively walk the commandlist structures, and print out the
diff --git a/gdb/command.h b/gdb/command.h
index 3dde2475cb1..e3d55c2dcba 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -148,6 +148,12 @@ extern struct cmd_list_element *add_cmd (const char *, enum command_class,
 					 const char *,
 					 struct cmd_list_element **);
 
+extern struct cmd_list_element *add_cmd_suppress_notification
+			(const char *name, enum command_class theclass,
+			 cmd_const_cfunc_ftype *fun, const char *doc,
+			 struct cmd_list_element **list,
+			 int *suppress_notification);
+
 extern struct cmd_list_element *add_alias_cmd (const char *, const char *,
 					       enum command_class, int,
 					       struct cmd_list_element **);
@@ -165,6 +171,14 @@ extern struct cmd_list_element *add_prefix_cmd (const char *, enum command_class
 						const char *, int,
 						struct cmd_list_element **);
 
+extern struct cmd_list_element *add_prefix_cmd_suppress_notification
+			(const char *name, enum command_class theclass,
+			 cmd_const_cfunc_ftype *fun,
+			 const char *doc, struct cmd_list_element **prefixlist,
+			 const char *prefixname, int allow_unknown,
+			 struct cmd_list_element **list,
+			 int *suppress_notification);
+
 extern struct cmd_list_element *add_abbrev_prefix_cmd (const char *,
 						       enum command_class,
 						       cmd_const_cfunc_ftype *fun,
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index b9318344002..0c2fe9bd01b 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -7631,21 +7631,75 @@
 @table @code
 @kindex frame@r{, selecting}
 @kindex f @r{(@code{frame})}
-@item frame @var{n}
-@itemx f @var{n}
-Select frame number @var{n}.  Recall that frame zero is the innermost
-(currently executing) frame, frame one is the frame that called the
-innermost one, and so on.  The highest-numbered frame is the one for
-@code{main}.
+@item frame @r{[} @var{frame-selection-spec} @r{]}
+@item f @r{[} @var{frame-selection-spec} @r{]}
+The @command{frame} command allows different stack frames to be
+selected.  The @var{frame-selection-spec} can be any of the following:
+
+@table @code
+@kindex frame level
+@item @var{num}
+@item level @var{num}
+Select frame level @var{num}.  Recall that frame zero is the
+innermost (currently executing) frame, frame one is the frame that
+called the innermost one, and so on.  The highest level frame is
+the one for @code{main}.
+
+As this is the most common method of navigating the frame stack then
+the string @command{level} can be dropped, the following two commands
+are equivalent:
 
-@item frame @var{stack-addr} [ @var{pc-addr} ]
-@itemx f @var{stack-addr} [ @var{pc-addr} ]
-Select the frame at address @var{stack-addr}.  This is useful mainly if the
-chaining of stack frames has been damaged by a bug, making it
-impossible for @value{GDBN} to assign numbers properly to all frames.  In
-addition, this can be useful when your program has multiple stacks and
-switches between them.  The optional @var{pc-addr} can also be given to
-specify the value of PC for the stack frame.
+@smallexample
+(@value{GDBP}) frame 3
+(@value{GDBP}) frame level 3
+@end smallexample
+
+@kindex frame address
+@item address @var{stack-address}
+Select the frame with stack address @var{stack-address}.  The
+@var{stack-address} for a frame can be seen in the output of
+@command{info frame}, for example:
+
+@smallexample
+(gdb) info frame
+Stack level 1, frame at 0x7fffffffda30:
+ rip = 0x40066d in b (amd64-entry-value.cc:59); saved rip 0x4004c5
+ tail call frame, caller of frame at 0x7fffffffda30
+ source language c++.
+ Arglist at unknown address.
+ Locals at unknown address, Previous frame's sp is 0x7fffffffda30
+@end smallexample
+
+The @var{stack-address} for this frame is @code{0x7fffffffda30} as
+indicated by the line:
+
+@smallexample
+Stack level 1, frame at 0x7fffffffda30:
+@end smallexample
+
+@kindex frame function
+@item function @var{function-name}
+Select the stack frame for function @var{function-name}.  If there are
+multiple stack frames for function @var{function-name} then the inner
+most stack frame is selected.
+
+@kindex frame view
+@item view @var{stack-address} @r{[} @var{pc-addr} @r{]}
+View a frame that is not part of @value{GDBN}'s backtrace.  The frame
+viewed has stack address @var{stack-addr}, and optionally, a program
+counter address of @var{pc-addr}.
+
+This is useful mainly if the chaining of stack frames has been
+damaged by a bug, making it impossible for @value{GDBN} to assign
+numbers properly to all frames.  In addition, this can be useful
+when your program has multiple stacks and switches between them.
+
+When viewing a frame outside the current backtrace using
+@command{frame view} then you can always return to the original
+stack using one of the previous stack frame selection instructions,
+for example @command{frame level 0}.
+
+@end table
 
 @kindex up
 @item up @var{n}
@@ -7688,11 +7742,13 @@
 
 @table @code
 @kindex select-frame
-@item select-frame
+@item select-frame @r{[} @var{frame-selection-spec} @r{]}
 The @code{select-frame} command is a variant of @code{frame} that does
 not display the new frame after selecting it.  This command is
 intended primarily for use in @value{GDBN} command scripts, where the
-output might be unnecessary and distracting.
+output might be unnecessary and distracting.  The
+@var{frame-selection-spec} is as for the @command{frame} command
+described in @ref{Selection, ,Selecting a Frame}.
 
 @kindex down-silently
 @kindex up-silently
@@ -7750,13 +7806,12 @@
 something has gone wrong that has made the stack format fail to fit
 the usual conventions.
 
-@item info frame @var{addr}
-@itemx info f @var{addr}
-Print a verbose description of the frame at address @var{addr}, without
-selecting that frame.  The selected frame remains unchanged by this
-command.  This requires the same kind of address (more than one for some
-architectures) that you specify in the @code{frame} command.
-@xref{Selection, ,Selecting a Frame}.
+@item info frame @r{[} @var{frame-selection-spec} @r{]}
+@itemx info f @r{[} @var{frame-selection-spec} @r{]}
+Print a verbose description of the frame selected by
+@var{frame-selection-spec}.  The @var{frame-selection-spec} is the
+same as for the @command{frame} command (@pxref{Selection, ,Selecting
+a Frame}).  The selected frame remains unchanged by this command.
 
 @kindex info args
 @item info args
@@ -30280,11 +30335,12 @@
 @subsubheading Synopsis
 
 @smallexample
- -stack-select-frame @var{framenum}
+ -stack-select-frame @var{frame-selection-spec}
 @end smallexample
 
-Change the selected frame.  Select a different frame @var{framenum} on
-the stack.
+Change the selected frame, the @var{frame-selection-spec} is as for
+the @command{frame} command described in @ref{Selection, ,Selecting a
+Frame}.
 
 This command in deprecated in favor of passing the @samp{--frame}
 option to every command.
diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c
index 52660bdd498..33ac7663ba8 100644
--- a/gdb/mi/mi-cmd-stack.c
+++ b/gdb/mi/mi-cmd-stack.c
@@ -681,10 +681,8 @@ list_args_or_locals (enum what_to_list what, enum print_values values,
 void
 mi_cmd_stack_select_frame (const char *command, char **argv, int argc)
 {
-  if (argc == 0 || argc > 1)
+  if (!select_frame_from_spec (argv, argc, 0))
     error (_("-stack-select-frame: Usage: FRAME_SPEC"));
-
-  select_frame_command (argv[0], 1 /* not used */ );
 }
 
 void
diff --git a/gdb/stack.c b/gdb/stack.c
index 3da46dd955a..f064d0cbe2b 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -102,6 +102,9 @@ static void set_last_displayed_sal (int valid,
 				    struct symtab *symtab,
 				    int line);
 
+static struct frame_info *find_frame_for_function (const char *);
+static struct frame_info *find_frame_for_address (CORE_ADDR);
+
 /* Zero means do things normally; we are interacting directly with the
    user.  One means print the full filename and linenumber when a
    frame is printed, and do so in a format emacs18/emacs19.22 can
@@ -1255,134 +1258,34 @@ print_frame (struct frame_info *frame, int print_level,
 }
 \f
 
-/* Read a frame specification in whatever the appropriate format is from
-   FRAME_EXP.  Call error() if the specification is in any way invalid (so
-   this function never returns NULL).  When SELECTED_FRAME_P is non-NULL
-   set its target to indicate that the default selected frame was used.  */
+/* Completion function for "frame function", "info frame function", and
+   "select-frame function" commands.  */
 
-static struct frame_info *
-parse_frame_specification (const char *frame_exp, int *selected_frame_p)
+void
+frame_selection_by_function_completer (struct cmd_list_element *ignore,
+				       completion_tracker &tracker,
+				       const char *text, const char *word)
 {
-  int numargs;
-  struct value *args[4];
-  CORE_ADDR addrs[ARRAY_SIZE (args)];
-
-  if (frame_exp == NULL)
-    numargs = 0;
-  else
-    {
-      numargs = 0;
-      while (1)
-	{
-	  const char *p;
-
-	  /* Skip leading white space, bail of EOL.  */
-	  frame_exp = skip_spaces (frame_exp);
-	  if (!*frame_exp)
-	    break;
-
-	  /* Parse the argument, extract it, save it.  */
-	  for (p = frame_exp;
-	       *p && !ISSPACE (*p);
-	       p++);
-	  std::string addr_string (frame_exp, p - frame_exp);
-	  frame_exp = p;
-	  
-	  /* NOTE: Parse and evaluate expression, but do not use
-	     functions such as parse_and_eval_long or
-	     parse_and_eval_address to also extract the value.
-	     Instead value_as_long and value_as_address are used.
-	     This avoids problems with expressions that contain
-	     side-effects.  */
-	  if (numargs >= ARRAY_SIZE (args))
-	    error (_("Too many args in frame specification"));
-	  args[numargs++] = parse_and_eval (addr_string.c_str ());
-	}
-    }
-
-  /* If no args, default to the selected frame.  */
-  if (numargs == 0)
-    {
-      if (selected_frame_p != NULL)
-	(*selected_frame_p) = 1;
-      return get_selected_frame (_("No stack."));
-    }
-
-  /* None of the remaining use the selected frame.  */
-  if (selected_frame_p != NULL)
-    (*selected_frame_p) = 0;
-
-  /* Assume the single arg[0] is an integer, and try using that to
-     select a frame relative to current.  */
-  if (numargs == 1)
-    {
-      struct frame_info *fid;
-      int level = value_as_long (args[0]);
-
-      fid = find_relative_frame (get_current_frame (), &level);
-      if (level == 0)
-	/* find_relative_frame was successful.  */
-	return fid;
-    }
-
-  /* Convert each value into a corresponding address.  */
-  {
-    int i;
-
-    for (i = 0; i < numargs; i++)
-      addrs[i] = value_as_address (args[i]);
-  }
-
-  /* Assume that the single arg[0] is an address, use that to identify
-     a frame with a matching ID.  Should this also accept stack/pc or
-     stack/pc/special.  */
-  if (numargs == 1)
-    {
-      struct frame_id id = frame_id_build_wild (addrs[0]);
-      struct frame_info *fid;
-
-      /* If (s)he specifies the frame with an address, he deserves
-	 what (s)he gets.  Still, give the highest one that matches.
-	 (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't
-	 know).  */
-      for (fid = get_current_frame ();
-	   fid != NULL;
-	   fid = get_prev_frame (fid))
-	{
-	  if (frame_id_eq (id, get_frame_id (fid)))
-	    {
-	      struct frame_info *prev_frame;
-
-	      while (1)
-		{
-		  prev_frame = get_prev_frame (fid);
-		  if (!prev_frame
-		      || !frame_id_eq (id, get_frame_id (prev_frame)))
-		    break;
-		  fid = prev_frame;
-		}
-	      return fid;
-	    }
-	}
-      }
-
-  /* We couldn't identify the frame as an existing frame, but
-     perhaps we can create one with a single argument.  */
-  if (numargs == 1)
-    return create_new_frame (addrs[0], 0);
-  else if (numargs == 2)
-    return create_new_frame (addrs[0], addrs[1]);
-  else
-    error (_("Too many args in frame specification"));
+  /* This is used to complete function names within a stack.  It would be
+     nice if instead of offering all available function names, we only
+     offered functions that were actually in the stack.  However, this
+     would probably mean unwinding the stack to completion, which could
+     take too long (or on a corrupted stack, possibly not end).  For now I
+     offer all symbol names as a safer choice.  */
+  collect_symbol_completion_matches (tracker,
+				     complete_symbol_mode::EXPRESSION,
+				     symbol_name_match_type::EXPRESSION,
+				     text, word);
 }
 
-/* Print verbosely the selected frame or the frame at address
-   ADDR_EXP.  Absolutely all information in the frame is printed.  */
+/* Core of all the "info frame" sub-commands.  Print verbosely the frame FI
+   if SELECTED_FRAME_P is true then frame FI is the current frame, which
+   was selected as a default due to the user not providing any arguments
+   to select some other frame.  */
 
 static void
-info_frame_command (const char *addr_exp, int from_tty)
+info_frame_command_core (struct frame_info *fi, bool selected_frame_p)
 {
-  struct frame_info *fi;
   struct symbol *func;
   struct symtab *s;
   struct frame_info *calling_frame_info;
@@ -1390,7 +1293,6 @@ info_frame_command (const char *addr_exp, int from_tty)
   const char *funname = 0;
   enum language funlang = language_unknown;
   const char *pc_regname;
-  int selected_frame_p;
   struct gdbarch *gdbarch;
   CORE_ADDR frame_pc;
   int frame_pc_p;
@@ -1398,7 +1300,6 @@ info_frame_command (const char *addr_exp, int from_tty)
   CORE_ADDR caller_pc = 0;
   int caller_pc_p = 0;
 
-  fi = parse_frame_specification (addr_exp, &selected_frame_p);
   gdbarch = get_frame_arch (fi);
 
   /* Name of the value returned by get_frame_pc().  Per comments, "pc"
@@ -1739,6 +1640,140 @@ trailing_outermost_frame (int count)
   return trailing;
 }
 
+/* The core of all the "select-frame" sub-commands.  Just wraps a call to
+   SELECT_FRAME.  */
+
+static void
+select_frame_command_core (struct frame_info *fi, bool ignored)
+{
+  struct frame_info *prev_frame = get_selected_frame_if_set ();
+  select_frame (fi);
+  if (get_selected_frame_if_set () != prev_frame)
+    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
+}
+
+/* The core of all the "frame" sub-commands.  Select frame FI, and if this
+   means we change frame send out a change notification (otherwise, just
+   reprint the current frame summary).   */
+
+static void
+frame_command_core (struct frame_info *fi, bool ignored)
+{
+  struct frame_info *prev_frame = get_selected_frame_if_set ();
+
+  select_frame (fi);
+  if (get_selected_frame_if_set () != prev_frame)
+    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
+  else
+    print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME);
+}
+
+/* The three commands 'frame', 'select-frame', and 'info frame' all have a
+   common set of sub-commands that allow a specific frame to be selected.
+   All of the sub-command functions are static methods within this class
+   template which is then instantiated below.  */
+
+template <void (*FPTR) (struct frame_info *, bool)>
+class frame_command_helper
+{
+public:
+
+  /* The "frame level" family of commands.  The ARG is an integer that is
+     the frame's level in the stack.  */
+  static void
+  level (const char *arg, int from_tty)
+  {
+    int level = value_as_long (parse_and_eval (arg));
+    struct frame_info *fid
+      = find_relative_frame (get_current_frame (), &level);
+    if (level != 0)
+      error (_("No frame at level %s."), arg);
+    FPTR (fid, false);
+  }
+
+  /* The "frame address" family of commands.  ARG is a stack-pointer
+     address for an existing frame.  This command does not allow new
+     frames to be created.  */
+
+  static void
+  address (const char *arg, int from_tty)
+  {
+    CORE_ADDR addr = value_as_address (parse_and_eval (arg));
+    struct frame_info *fid = find_frame_for_address (addr);
+    if (fid == NULL)
+      error (_("No frame at address %s."), arg);
+    FPTR (fid, false);
+  }
+
+  /* The "frame view" family of commands.  ARG is one or two addresses and
+     is used to view a frame that might be outside the current backtrace.
+     The addresses are stack-pointer address, and (optional) pc-address.  */
+
+  static void
+  view (const char *args, int from_tty)
+  {
+    struct frame_info *fid;
+
+    if (args == NULL)
+    error (_("Missing address argument to view a frame"));
+
+    gdb_argv argv (args);
+
+    if (argv.count () == 2)
+      {
+	CORE_ADDR addr[2];
+
+	addr [0] = value_as_address (parse_and_eval (argv[0]));
+	addr [1] = value_as_address (parse_and_eval (argv[1]));
+	fid = create_new_frame (addr[0], addr[1]);
+      }
+    else
+      {
+	CORE_ADDR addr = value_as_address (parse_and_eval (argv[0]));
+	fid = create_new_frame (addr, false);
+      }
+    FPTR (fid, false);
+  }
+
+  /* The "frame function" family of commands.  ARG is the name of a
+     function within the stack, the first function (searching from frame
+     0) with that name will be selected.  */
+
+  static void
+  function (const char *arg, int from_tty)
+  {
+    if (arg == NULL)
+      error (_("Missing function name argument"));
+    struct frame_info *fid = find_frame_for_function (arg);
+    if (fid == NULL)
+      error (_("No frame for function \"%s\"."), arg);
+    FPTR (fid, false);
+  }
+
+  /* The "frame" base command, that is, when no sub-command is specified.
+     If one argument is provided then we assume that this is a frame's
+     level as historically, this was the supported command syntax that was
+     used most often.
+
+     If no argument is provided, then the current frame is selected.  */
+
+  static void
+  base_command (const char *arg, int from_tty)
+  {
+    if (arg == NULL)
+      FPTR (get_selected_frame (_("No stack.")), true);
+    else
+      level (arg, from_tty);
+  }
+};
+
+/* Instantiate three FRAME_COMMAND_HELPER instances to implement the
+   sub-commands for 'info frame', 'frame', and 'select-frame' commands.  */
+
+static frame_command_helper <info_frame_command_core> info_frame_cmd;
+static frame_command_helper <frame_command_core> frame_cmd;
+static frame_command_helper <select_frame_command_core> select_frame_cmd;
+
 /* Print briefly all stack frames or just the innermost COUNT_EXP
    frames.  */
 
@@ -2227,37 +2262,43 @@ find_relative_frame (struct frame_info *frame, int *level_offset_ptr)
   return frame;
 }
 
-/* The "select_frame" command.  With no argument this is a NOP.
-   Select the frame at level LEVEL_EXP if it is a valid level.
-   Otherwise, treat LEVEL_EXP as an address expression and select it.
-
-   See parse_frame_specification for more info on proper frame
-   expressions.  */
-
-void
-select_frame_command (const char *level_exp, int from_tty)
-{
-  struct frame_info *prev_frame = get_selected_frame_if_set ();
-
-  select_frame (parse_frame_specification (level_exp, NULL));
-  if (get_selected_frame_if_set () != prev_frame)
-    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
-}
-
-/* The "frame" command.  With no argument, print the selected frame
-   briefly.  With an argument, behave like select_frame and then print
-   the selected frame.  */
+/* Select a frame using ARGV.  This is used from the MI -stack-select-frame
+   to provide the same frame specification mechanism that the CLI has for
+   commands like 'frame'.  The return value is true if the contents of
+   ARGV looked like a sensible attempt to change the frame (an error might
+   still be thrown though), or false if the contents of ARGV are not a
+   correct frame specification.  */
 
-static void
-frame_command (const char *level_exp, int from_tty)
+bool
+select_frame_from_spec (char **argv, int argc, int from_tty)
 {
-  struct frame_info *prev_frame = get_selected_frame_if_set ();
+  if (argc == 1)
+    select_frame_cmd.level (argv[0], from_tty);
+  else if (argc == 2
+	   && strncasecmp ("level", argv[0], strlen (argv[0])) == 0)
+    select_frame_cmd.level (argv[1], from_tty);
+  else if (argc == 2
+	   && strncasecmp ("address", argv[0], strlen (argv[0])) == 0)
+    select_frame_cmd.address (argv[1], from_tty);
+  else if (argc == 2
+	   && strncasecmp ("function", argv[0], strlen (argv[0])) == 0)
+    select_frame_cmd.function (argv[1], from_tty);
+  else if ((argc == 2 || argc == 3)
+	   && strncasecmp ("view", argv[0], strlen (argv[0])) == 0)
+    {
+      std::string arg;
+
+      if (argc == 2)
+	arg = string_printf ("%s", argv[1]);
+      else
+	arg = string_printf ("%s %s", argv[1], argv[2]);
 
-  select_frame (parse_frame_specification (level_exp, NULL));
-  if (get_selected_frame_if_set () != prev_frame)
-    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
+      select_frame_cmd.view (arg.c_str (), from_tty);
+    }
   else
-    print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME);
+    return false;
+
+  return true;
 }
 
 /* Select the frame up one or COUNT_EXP stack levels from the
@@ -2713,13 +2754,61 @@ faas_command (const char *cmd, int from_tty)
 }
 
 
+/* Find inner-mode frame with frame address ADDRESS.  Return NULL if no
+   matching frame can be found.  */
+
+static struct frame_info *
+find_frame_for_address (CORE_ADDR address)
+{
+  struct frame_id id;
+  struct frame_info *fid;
+
+  id = frame_id_build_wild (address);
+
+  /* If (s)he specifies the frame with an address, he deserves
+     what (s)he gets.  Still, give the highest one that matches.
+     (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't
+     know).  */
+  for (fid = get_current_frame ();
+       fid != NULL;
+       fid = get_prev_frame (fid))
+    {
+      if (frame_id_eq (id, get_frame_id (fid)))
+	{
+	  struct frame_info *prev_frame;
+
+	  while (1)
+	    {
+	      prev_frame = get_prev_frame (fid);
+	      if (!prev_frame
+		  || !frame_id_eq (id, get_frame_id (prev_frame)))
+		break;
+	      fid = prev_frame;
+	    }
+	  return fid;
+	}
+    }
+  return NULL;
+}
+
+\f
+
+/* Commands with a prefix of `frame apply'.  */
+static struct cmd_list_element *frame_apply_cmd_list = NULL;
+
 /* Commands with a prefix of `frame'.  */
-struct cmd_list_element *frame_cmd_list = NULL;
+static struct cmd_list_element *frame_cmd_list = NULL;
+
+/* Commands with a prefix of `select frame'.  */
+static struct cmd_list_element *select_frame_cmd_list = NULL;
+
+/* Commands with a prefix of `info frame'.  */
+static struct cmd_list_element *info_frame_cmd_list = NULL;
 
 void
 _initialize_stack (void)
 {
-  static struct cmd_list_element *frame_apply_list = NULL;
+  struct cmd_list_element *cmd;
 
   add_com ("return", class_stack, return_command, _("\
 Make selected stack frame return to its caller.\n\
@@ -2743,12 +2832,12 @@ An argument says how many frames down to go."));
 Same as the `down' command, but does not print anything.\n\
 This is useful in command scripts."));
 
-  add_prefix_cmd ("frame", class_stack, frame_command, _("\
-Select and print a stack frame.\nWith no argument, \
-print the selected stack frame.  (See also \"info frame\").\n\
-An argument specifies the frame to select.\n\
-It can be a stack frame number or the address of the frame."),
-		  &frame_cmd_list, "frame ", 1, &cmdlist);
+  add_prefix_cmd ("frame", class_stack,
+                  &frame_cmd.base_command, _("\
+Select and print a stack frame.\n\
+With no argument, print the selected stack frame.  (See also \"info frame\").\n\
+A single numerical argument specifies the frame to select."),
+                  &frame_cmd_list, "frame ", 1, &cmdlist);
 
   add_com_alias ("f", "frame", class_stack, 1);
 
@@ -2766,7 +2855,7 @@ or produces no output."
 Usage: frame apply COUNT [FLAG]... COMMAND\n\
 With a negative COUNT argument, applies the command on outermost -COUNT frames.\n"
 FRAME_APPLY_FLAGS_HELP),
-		  &frame_apply_list, "frame apply ", 1, &frame_cmd_list);
+		  &frame_apply_cmd_list, "frame apply ", 1, &frame_cmd_list);
 
   add_cmd ("all", class_stack, frame_apply_all_command,
 	   _("\
@@ -2774,7 +2863,7 @@ Apply a command to all frames.\n\
 \n\
 Usage: frame apply all [FLAG]... COMMAND\n"
 FRAME_APPLY_FLAGS_HELP),
-	   &frame_apply_list);
+	   &frame_apply_cmd_list);
 
   add_cmd ("level", class_stack, frame_apply_level_command,
 	   _("\
@@ -2783,19 +2872,97 @@ Apply a command to a list of frames.\n\
 Usage: frame apply level LEVEL... [FLAG]... COMMAND\n\
 ID is a space-separated list of LEVELs of frames to apply COMMAND on.\n"
 FRAME_APPLY_FLAGS_HELP),
-	   &frame_apply_list);
+	   &frame_apply_cmd_list);
 
   add_com ("faas", class_stack, faas_command, _("\
 Apply a command to all frames (ignoring errors and empty output).\n\
 Usage: faas COMMAND\n\
 shortcut for 'frame apply all -s COMMAND'"));
 
-  add_com_suppress_notification ("select-frame", class_stack, select_frame_command, _("\
+
+  add_prefix_cmd ("frame", class_stack,
+		  &frame_cmd.base_command, _("\
+Select and print a stack frame.\n\
+With no argument, print the selected stack frame.  (See also \"info frame\").\n\
+A single numerical argument specifies the frame to select."),
+		  &frame_cmd_list, "frame ", 1, &cmdlist);
+  add_com_alias ("f", "frame", class_stack, 1);
+
+  add_cmd ("address", class_stack, &frame_cmd.address,
+	   _("\
+Select and print a stack frame by stack address\n\
+\n\
+Usage: frame address STACK-ADDRESS"),
+	   &frame_cmd_list);
+
+  add_cmd ("view", class_stack, &frame_cmd.view,
+	   _("\
+View a stack frame that might be outside the current backtrace.\n\
+\n\
+Usage: frame view STACK-ADDRESS\n\
+       frame view STACK-ADDRESS PC-ADDRESS"),
+	   &frame_cmd_list);
+
+  cmd = add_cmd ("function", class_stack, &frame_cmd.function,
+	   _("\
+Select and print a stack frame by function name.\n\
+\n\
+Usage: frame function NAME\n\
+\n\
+The innermost frame that visited function NAME is selected."),
+	   &frame_cmd_list);
+  set_cmd_completer (cmd, frame_selection_by_function_completer);
+
+
+  add_cmd ("level", class_stack, &frame_cmd.level,
+	   _("\
+Select and print a stack frame by level.\n\
+\n\
+Usage: frame level LEVEL"),
+	   &frame_cmd_list);
+
+  cmd = add_prefix_cmd_suppress_notification ("select-frame", class_stack,
+		      &select_frame_cmd.base_command, _("\
 Select a stack frame without printing anything.\n\
-An argument specifies the frame to select.\n\
-It can be a stack frame number or the address of the frame."),
+A single numerical argument specifies the frame to select."),
+		      &select_frame_cmd_list, "select-frame ", 1, &cmdlist,
+		      &cli_suppress_notification.user_selected_context);
+
+  add_cmd_suppress_notification ("address", class_stack,
+			 &select_frame_cmd.address, _("\
+Select a stack frame by stack address.\n\
+\n\
+Usage: select-frame address STACK-ADDRESS"),
+			 &select_frame_cmd_list,
+			 &cli_suppress_notification.user_selected_context);
+
+
+  add_cmd_suppress_notification ("view", class_stack,
+		 &select_frame_cmd.view, _("\
+Select a stack frame that might be outside the current backtrace.\n\
+\n\
+Usage: select-frame view STACK-ADDRESS\n\
+       select-frame view STACK-ADDRESS PC-ADDRESS"),
+		 &select_frame_cmd_list,
 		 &cli_suppress_notification.user_selected_context);
 
+  cmd = add_cmd_suppress_notification ("function", class_stack,
+	       &select_frame_cmd.function, _("\
+Select a stack frame by function name.\n\
+\n\
+Usage: select-frame function NAME"),
+	       &select_frame_cmd_list,
+	       &cli_suppress_notification.user_selected_context);
+  set_cmd_completer (cmd, frame_selection_by_function_completer);
+
+  add_cmd_suppress_notification ("level", class_stack,
+			 &select_frame_cmd.level, _("\
+Select a stack frame by level.\n\
+\n\
+Usage: select-frame level LEVEL"),
+			 &select_frame_cmd_list,
+			 &cli_suppress_notification.user_selected_context);
+
   add_com ("backtrace", class_stack, backtrace_command, _("\
 Print backtrace of all stack frames, or innermost COUNT frames.\n\
 Usage: backtrace [QUALIFIERS]... [COUNT]\n\
@@ -2809,9 +2976,45 @@ on this backtrace."));
   add_info ("stack", backtrace_command,
 	    _("Backtrace of the stack, or innermost COUNT frames."));
   add_info_alias ("s", "stack", 1);
-  add_info ("frame", info_frame_command,
-	    _("All about selected stack frame, or frame at ADDR."));
+
+  add_prefix_cmd ("frame", class_info, &info_frame_cmd.base_command,
+		  _("All about the selected stack frame.\n\
+With no arguments, displays information about the currently selected stack\n\
+frame.  Alternatively a frame specification may be provided (See \"frame\")\n\
+the information is then printed about the specified frame."),
+		  &info_frame_cmd_list, "info frame ", 1, &infolist);
   add_info_alias ("f", "frame", 1);
+
+  add_cmd ("address", class_stack, &info_frame_cmd.address,
+	   _("\
+Print information about a stack frame selected by stack address.\n\
+\n\
+Usage: info frame address STACK-ADDRESS"),
+	   &info_frame_cmd_list);
+
+  add_cmd ("view", class_stack, &info_frame_cmd.view,
+	   _("\
+Print information about a stack frame outside the current backtrace.\n\
+\n\
+Usage: info frame view STACK-ADDRESS\n\
+       info frame view STACK-ADDRESS PC-ADDRESS"),
+	   &info_frame_cmd_list);
+
+  cmd = add_cmd ("function", class_stack, &info_frame_cmd.function,
+	   _("\
+Print information about a stack frame selected by function name.\n\
+\n\
+Usage: info frame function NAME"),
+	   &info_frame_cmd_list);
+  set_cmd_completer (cmd, frame_selection_by_function_completer);
+
+  add_cmd ("level", class_stack, &info_frame_cmd.level,
+	   _("\
+Print information about a stack frame selected by level.\n\
+\n\
+Usage: info frame level LEVEL"),
+	   &info_frame_cmd_list);
+
   add_info ("locals", info_locals_command,
 	    _("Local variables of current stack frame."));
   add_info ("args", info_args_command,
diff --git a/gdb/stack.h b/gdb/stack.h
index ca190efa9c3..29794872d70 100644
--- a/gdb/stack.h
+++ b/gdb/stack.h
@@ -20,7 +20,7 @@
 #ifndef STACK_H
 #define STACK_H
 
-void select_frame_command (const char *level_exp, int from_tty);
+bool select_frame_from_spec (char **argv, int argc, int from_tty);
 
 gdb::unique_xmalloc_ptr<char> find_frame_funname (struct frame_info *frame,
 						  enum language *funlang,
diff --git a/gdb/testsuite/gdb.base/frame-selection.c b/gdb/testsuite/gdb.base/frame-selection.c
new file mode 100644
index 00000000000..f3d81f223e0
--- /dev/null
+++ b/gdb/testsuite/gdb.base/frame-selection.c
@@ -0,0 +1,52 @@
+/* Copyright 2018 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int
+frame_2 (void)
+{
+  return 0;
+}
+
+int
+frame_1 (void)
+{
+  return frame_2 ();
+}
+
+int
+recursive (int arg)
+{
+  int v;
+
+  if (arg < 2)
+    v = recursive (arg + 1);
+  else
+    v = frame_2 ();
+
+  return v;
+}
+
+int
+main (void)
+{
+  int i, j;
+
+  i = frame_1 ();
+  j = recursive (0);
+
+  return i + j;
+}
diff --git a/gdb/testsuite/gdb.base/frame-selection.exp b/gdb/testsuite/gdb.base/frame-selection.exp
new file mode 100644
index 00000000000..1b3649b2700
--- /dev/null
+++ b/gdb/testsuite/gdb.base/frame-selection.exp
@@ -0,0 +1,156 @@
+# Copyright 2018 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+standard_testfile
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug}]} {
+    return -1
+}
+
+runto_main
+gdb_breakpoint frame_2
+gdb_continue_to_breakpoint frame_2
+
+gdb_test "bt" "#0  frame_2.*#1  $hex in frame_1.*#2  $hex in main.*" "backtrace at breakpoint"
+
+# Perform "info frame" to extract the frames address.
+proc get_frame_address { {testname ""} } {
+    global hex gdb_prompt
+
+    set testname "get_frame_address: ${testname}"
+    set frame_address "unknown"
+
+    send_gdb "info frame\n"
+    gdb_expect {
+	-re ", frame at ($hex):\r\n.*\r\n$gdb_prompt $" {
+	    set frame_address $expect_out(1,string)
+	    pass $testname
+	}
+	-re ".*$gdb_prompt $" { fail $testname }
+	timeout { fail "$testname (timeout)" }
+    }
+
+    return $frame_address
+}
+
+# Passes a list of addresses.  Return a new address that is not in the
+# list.
+proc get_new_address { {addresses {}} } {
+    return [format "%#x" [expr [lindex $addresses [llength addresses]] + 0x10 ]]
+}
+
+proc check_frame { level address function } {
+    global hex gdb_prompt
+
+    set testname "check frame level ${level}"
+    send_gdb "info frame\n"
+    gdb_expect {
+	-re "Stack level ${level}, frame at ($address):\r\n rip = $hex in ${function} \(\[^\r\n\]*\); saved rip = $hex\r\n.*\r\n$gdb_prompt $" {
+	    pass $testname
+	}
+	-re ".*$gdb_prompt $" { fail $testname }
+	timeout { fail "$testname (timeout)" }
+    }
+}
+
+# Select frame using level, but relying on this being the default
+# action, so "frame 0" performs "frame level 0".
+gdb_test "frame 0" "#0  frame_2.*"
+set frame_0_address [ get_frame_address "frame 0" ]
+gdb_test "frame 1" "#1  $hex in frame_1.*"
+set frame_1_address [ get_frame_address "frame 1" ]
+gdb_test "frame 2" "#2  $hex in main.*"
+set frame_2_address [ get_frame_address "frame 2" ]
+gdb_test "frame 3" "No frame at level 3\."
+
+# Find an address that matches no frame
+set no_frame_address [ get_new_address [list $frame_0_address \
+					     $frame_1_address \
+					     $frame_2_address] ]
+
+# Select frame using 'level' specification.
+gdb_test "frame level 0" "#0  frame_2.*"
+gdb_test "frame level 1" "#1  $hex in frame_1.*"
+gdb_test "frame level 2" "#2  $hex in main.*"
+gdb_test "frame level 3" "No frame at level 3\."
+
+# Select frame by address.
+gdb_test "frame address ${frame_0_address}" "#0  frame_2.*"
+gdb_test "frame address ${frame_1_address}" "#1  $hex in frame_1.*"
+gdb_test "frame address ${frame_2_address}" "#2  $hex in main.*"
+gdb_test "frame address ${no_frame_address}" \
+    "No frame at address ${no_frame_address}\."
+
+# Select frame by function.
+gdb_test "frame function frame_2" "#0  frame_2.*"
+gdb_test "frame function frame_1" "#1  $hex in frame_1.*"
+gdb_test "frame function main" "#2  $hex in main.*"
+# Check for a distinction between a known function not in the stack
+# trace, and an unknown function.
+gdb_test "frame function recursive" "No frame for function \"recursive\"."
+gdb_test "frame function foo" "Function \"foo\" not defined."
+
+
+gdb_test_no_output "select-frame 0"
+check_frame "0" "${frame_0_address}" "frame_2"
+gdb_test_no_output "select-frame 1"
+check_frame "1" "${frame_1_address}" "frame_1"
+gdb_test_no_output "select-frame 2"
+check_frame "2" "${frame_2_address}" "main"
+gdb_test "select-frame 3" "No frame at level 3\."
+
+gdb_test_no_output "select-frame level 0"
+check_frame "0" "${frame_0_address}" "frame_2"
+gdb_test_no_output "select-frame level 1"
+check_frame "1" "${frame_1_address}" "frame_1"
+gdb_test_no_output "select-frame level 2"
+check_frame "2" "${frame_2_address}" "main"
+gdb_test "select-frame level 3" "No frame at level 3\."
+
+gdb_test_no_output "select-frame address ${frame_0_address}"
+check_frame "0" "${frame_0_address}" "frame_2"
+gdb_test_no_output "select-frame address ${frame_1_address}"
+check_frame "1" "${frame_1_address}" "frame_1"
+gdb_test_no_output "select-frame address ${frame_2_address}"
+check_frame "2" "${frame_2_address}" "main"
+gdb_test "select-frame address ${no_frame_address}" \
+    "No frame at address ${no_frame_address}\."
+
+gdb_test_no_output "select-frame function frame_2"
+check_frame "0" "${frame_0_address}" "frame_2"
+gdb_test_no_output "select-frame function frame_1"
+check_frame "1" "${frame_1_address}" "frame_1"
+gdb_test_no_output "select-frame function main"
+check_frame "2" "${frame_2_address}" "main"
+# Check for a distinction between a known function not in the stack
+# trace, and an unknown function.
+gdb_test "select-frame function recursive" \
+    "No frame for function \"recursive\"."
+gdb_test "select-frame function foo" \
+    "Function \"foo\" not defined."
+
+# Now continue until we hit the breakpoint again.
+gdb_continue_to_breakpoint frame_2
+gdb_test "bt" \
+    "#0  frame_2.*#1  $hex in recursive.*#2  $hex in recursive.*#3  $hex in recursive.*#4  $hex in main.*" \
+    "backtrace at breakpoint with recursive frames"
+
+# Check "frame function" when a function name occurs multiple times in
+# the stack.  The inner most (lowest level) should always be selected.
+gdb_test "frame function frame_2" "#0  frame_2.*"
+gdb_test "frame function recursive" "#1  $hex in recursive.*"
+gdb_test "frame function recursive" "#1  $hex in recursive.*"
+gdb_test "frame function main" "#4  $hex in main.*"
+gdb_test "frame function recursive" "#1  $hex in recursive.*"
diff --git a/gdb/testsuite/gdb.mi/mi-frame-selection.c b/gdb/testsuite/gdb.mi/mi-frame-selection.c
new file mode 100644
index 00000000000..70b992c9149
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-frame-selection.c
@@ -0,0 +1,34 @@
+/* Copyright 2018 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int
+frame_2 (void)
+{
+  return 0;
+}
+
+int
+frame_1 (void)
+{
+  return frame_2 ();
+}
+
+int
+main (void)
+{
+  return frame_1 ();
+}
diff --git a/gdb/testsuite/gdb.mi/mi-frame-selection.exp b/gdb/testsuite/gdb.mi/mi-frame-selection.exp
new file mode 100644
index 00000000000..e24c7179b9d
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-frame-selection.exp
@@ -0,0 +1,89 @@
+# Copyright 2018 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test the frame selection syntax with the -stack-select-frame command.
+
+load_lib mi-support.exp
+set MIFLAGS "-i=mi"
+
+gdb_exit
+if [mi_gdb_start] {
+    continue
+}
+
+standard_testfile
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    untested $testfile
+    return -1
+}
+
+mi_delete_breakpoints
+mi_gdb_reinitialize_dir $srcdir/$subdir
+mi_gdb_load ${binfile}
+
+mi_create_breakpoint "frame_2" \
+    "break-insert into frame_2" \
+    -number 1 -func frame_2 -file ".*${srcfile}"
+
+mi_run_cmd
+mi_expect_stop "breakpoint-hit" "frame_2" "" ".*${srcfile}" \
+	".*" { "" "disp=\"keep\"" } "run to breakpoint"
+
+mi_gdb_test "-stack-info-depth" \
+    "\\^done,depth=\"3\"" \
+    "stack info-depth"
+
+for {set i 0} {$i < 5} {incr i} {
+
+    if { $i < 3 } then {
+	mi_gdb_test "-stack-select-frame $i" \
+	    {\^done} \
+	    "-stack-select-frame $i"
+
+	mi_gdb_test "-stack-info-frame" \
+	    "\\^done,frame=\\\{level=\"$i\"\[^\}\]+\\\}" \
+	    "check level has has changed to $i"
+
+    } else {
+	mi_gdb_test "-stack-select-frame view $i" \
+	    {\^done} \
+	    "-stack-select-frame view frame at $i"
+
+	mi_gdb_test "-stack-info-frame" \
+	    "\\^done,frame=\\\{level=\"0\"\[^\}\]+\\\}" \
+	    "check level has has changed to 0, when creating a frame at sp=$i"
+    }
+}
+
+mi_gdb_test "-stack-select-frame 5" \
+    {\^error,msg="No frame at level 5."} \
+    "-stack-select-frame 5, with no keyword"
+
+mi_gdb_test "-stack-select-frame level 5" \
+    {\^error,msg="No frame at level 5."} \
+    "-stack-select-frame 5, using 'level' keyword"
+
+mi_gdb_test "-stack-select-frame function main" \
+    {\^done} \
+    "-stack-select-frame select frame for function main"
+
+mi_gdb_test "-stack-info-frame" \
+    "\\^done,frame=\\\{level=\"2\"\[^\}\]+\\\}" \
+    "check level has has changed to 2 for function main"
+
+mi_gdb_test "-stack-select-frame function unknown_function" \
+    {\^error,msg=\"Function \\\"unknown_function\\\" not defined.\"} \
+    "-stack-select-frame check error on undefined function name"
-- 
2.14.4

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCHv5 0/2] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-07-25 18:14         ` Andrew Burgess
@ 2018-08-13 22:20           ` Andrew Burgess
  2018-08-14 10:31             ` Philippe Waroquiers
  2018-08-27 11:04             ` Andrew Burgess
       [not found]           ` <cover.1534197765.git.andrew.burgess@embecosm.com>
  2018-08-13 22:20           ` [PATCHv5_A 1/2] " Andrew Burgess
  2 siblings, 2 replies; 35+ messages in thread
From: Andrew Burgess @ 2018-08-13 22:20 UTC (permalink / raw)
  To: gdb-patches; +Cc: Philippe Waroquiers, Eli Zaretskii, Andrew Burgess

Given a lack of feedback on v4, I've put together two alternative
versions to pick between...

  (A) In this version I took onboard the feedback from Eli and
      Philippe that using "level" might be confusing to users, as
      "number" is historically what has been used for the integer
      label we give to frames.

      The command for selecting a frame by integer is now, 'frame
      number <NUMBER>' and almost all references to "level" have now
      been removed from the patch.

  (B) In this version I've taken onboard the advice from Philippe,
      identifying places in the patch where I was mixing use of
      "level" and "number".  I've doubled down on "level" and removed
      most uses of "number" from the patch.

If we select patch 'A' then me might want to update 'frame apply level
...' to 'frame apply number ...' for consistency.

If we select patch 'B' then we will probably need a follow up patch
that goes through the documentation to tighten up references to
"number" vs "level".

I really don't mind which approach we take, I guess I'd probably pick
'B' over 'A' given we already have 'frame apply level ...' in GDB, but
if there's preference for 'A' then that's fine.

I'm also happy to do the follow up patches once we have some agreement
on which way to go.

Thanks,
Andrew

---

Andrew Burgess (1):
  gdb: Change how frames are selected for 'frame' and 'info frame'.

 gdb/ChangeLog                               |  36 ++
 gdb/NEWS                                    |   8 +
 gdb/cli/cli-decode.c                        |  44 ++-
 gdb/command.h                               |  14 +
 gdb/doc/ChangeLog                           |   8 +
 gdb/doc/gdb.texinfo                         | 108 ++++--
 gdb/mi/mi-cmd-stack.c                       |   4 +-
 gdb/stack.c                                 | 535 +++++++++++++++++++---------
 gdb/stack.h                                 |   2 +-
 gdb/testsuite/ChangeLog                     |   7 +
 gdb/testsuite/gdb.base/frame-selection.c    |  52 +++
 gdb/testsuite/gdb.base/frame-selection.exp  | 157 ++++++++
 gdb/testsuite/gdb.mi/mi-frame-selection.c   |  34 ++
 gdb/testsuite/gdb.mi/mi-frame-selection.exp |  89 +++++
 14 files changed, 896 insertions(+), 202 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/frame-selection.c
 create mode 100644 gdb/testsuite/gdb.base/frame-selection.exp
 create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.c
 create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.exp

-- 
2.14.4

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCHv5_A 1/2] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-07-25 18:14         ` Andrew Burgess
  2018-08-13 22:20           ` [PATCHv5 0/2] " Andrew Burgess
       [not found]           ` <cover.1534197765.git.andrew.burgess@embecosm.com>
@ 2018-08-13 22:20           ` Andrew Burgess
  2 siblings, 0 replies; 35+ messages in thread
From: Andrew Burgess @ 2018-08-13 22:20 UTC (permalink / raw)
  To: gdb-patches; +Cc: Philippe Waroquiers, Eli Zaretskii, Andrew Burgess

The 'frame' command, and thanks to code reuse the 'info frame' and
'select-frame' commands, currently have an overloaded mechanism for
selecting a frame.

These commands take one or two parameters, if it's one parameter then
we first try to use the parameter as an integer to select a frame by
number.  If that fails then we treat the parameter as an address and
try to select a stack frame by stack-address.  If we still have not
selected a stack frame, or we initially had two parameters, then GDB
allows the user to view a stack frame that is not part of the current
backtrace.  Internally, a new frame is created with the given stack
and pc addresses, and this is shown to the user.

The result of this is that a typo by the user, entering the wrong stack
frame number for example, can result in a brand new frame being viewed
rather than an error.

The purpose of this commit is to remove this overloading, while still
offering the same functionality through some new sub-commands.  By
making the default behaviour of 'frame' (and friends) be to select a
stack frame by number, it is hoped that enough backwards-compatibility
is maintained that users will not be overly inconvenienced.

The 'frame', 'select-frame', and 'info frame' commands now all take a
frame specification string as an argument, this string can be any of the
following:

  (1) An integer.  This is treated as a frame number.  If a frame with
  that number does not exist then the user gets an error.

  (2) A string like 'number <NUMBER>', where <NUMBER> is the frame to
  select as in option (1) above.

  (3) A string like 'address <STACK-ADDRESS>', where <STACK-ADDRESS>
  is a stack-frame address.  If there is no frame for this address
  then the user gets an error.

  (4) A string like 'function <NAME>', where <NAME> is a function name,
  the inner most frame for function <NAME> is selected.  If there is no
  frame for function <NAME> then the user gets an error.

  (5) A string like 'view <STACK-ADDRESS>', this views a new frame
  with stack address <STACK-ADDRESS>.

  (6) A string like 'view <STACK-ADDRESS> <PC-ADDRESS>', this views
  a new frame with stack address <STACK-ADDRESS> an the pc <PC-ADDRESS>.

This change assumes that the most common use of the commands like
'frame' is to select a frame by number, it is for this reason that
this is the behaviour that is kept for backwards compatibility.  Any
of the alternative behaviours, which are assumed to be less used, now
require a change in user behaviour.

The MI command '-stack-select-frame' has also changed in the same way.
The default behaviour is to select a frame by number, but the other
selection methods are also available.

gdb/ChangeLog:

	(NEWS): Mention changes to frame related commands.
	* cli/cli-decode.c (add_cmd_suppress_notification): New function.
	(add_prefix_cmd_suppress_notification): New function.
	(add_com_suppress_notification): Call
	add_cmd_suppress_notification.
	* command.h (add_cmd_suppress_notification): Declare.
	(add_prefix_cmd_suppress_notification): Declare.
	* mi/mi-cmd-stack.c (mi_cmd_stack_select_frame): Use new function
	select_frame_from_spec.
	* stack.c (find_frame_for_function): Add declaration.
	(find_frame_for_address): New function.
	(parse_frame_specification): Delete function.
	(frame_selection_by_function_completer): New function.
	(info_frame_command): Rename to...
	(info_frame_command_core): ...this, and update parameter types.
	(select_frame_command): Rename to...
	(select_frame_command_core): ...this, and update parameter types.
	(frame_command): Rename to...
	(frame_command_core): ...this, and update parameter types.
	(class frame_command_helper): New class to wrap implementations of
	frame related sub-commands.
	(select_frame_from_spec): New function.
	(frame_apply_cmd_list): New static global.
	(frame_cmd_list): Make static.
	(select_frame_cmd_list): New global for sub-commands.
	(info_frame_cmd_list): New global for sub-commands.
	(_initialize_stack): Register sub-commands for 'frame',
	'select-frame', and 'info frame'.  Update 'frame apply' commands
	to use frame_apply_cmd_list.  Move function local static
	frame_apply_list to file static frame_apply_cmd_list for
	consistency.
	* stack.h (select_frame_command): Delete declarationn.
	(select_frame_from_spec): Declare new function.

gdb/doc/ChangeLog:

	* gdb.texinfo (Selection): Rewrite documentation for 'frame' and
	'select-frame' commands.
	(Frame Info): Rewrite documentation for 'info frame' command.
	(GDB/MI Stack Manipulation): Update description of
	'-stack-select-frame'.

gdb/testsuite/ChangeLog:

	* gdb.base/frame-selection.exp: New file.
	* gdb.base/frame-selection.c: New file.
	* gdb.mi/mi-frame-selection.exp: New file.
	* gdb.mi/mi-frame-selection.c: New file.
---
 gdb/ChangeLog                               |  36 ++
 gdb/NEWS                                    |   8 +
 gdb/cli/cli-decode.c                        |  44 ++-
 gdb/command.h                               |  14 +
 gdb/doc/ChangeLog                           |   8 +
 gdb/doc/gdb.texinfo                         | 108 ++++--
 gdb/mi/mi-cmd-stack.c                       |   4 +-
 gdb/stack.c                                 | 535 +++++++++++++++++++---------
 gdb/stack.h                                 |   2 +-
 gdb/testsuite/ChangeLog                     |   7 +
 gdb/testsuite/gdb.base/frame-selection.c    |  52 +++
 gdb/testsuite/gdb.base/frame-selection.exp  | 157 ++++++++
 gdb/testsuite/gdb.mi/mi-frame-selection.c   |  34 ++
 gdb/testsuite/gdb.mi/mi-frame-selection.exp |  89 +++++
 14 files changed, 896 insertions(+), 202 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/frame-selection.c
 create mode 100644 gdb/testsuite/gdb.base/frame-selection.exp
 create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.c
 create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index 7f9466567f2..282f8a88f0e 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -74,6 +74,14 @@ thread apply [all | COUNT | -COUNT] [FLAG]... COMMAND
 * C expressions can now use _Alignof, and C++ expressions can now use
   alignof.
 
+* Changes to the "frame", "select-frame", and "info frame" CLI
+  commands, as well as the "-stack-select-frame" MI command.  These
+  commands all now take a frame specification which is either a frame
+  number, or one of the keywords 'number', 'address', 'function', or
+  'view' followed by a parameter.  Selecting a frame by address, or
+  viewing a frame outside the current backtrace now requires the use
+  of a keyword.  Selecting a frame by number is unchanged.
+
 * New commands
 
 set debug fbsd-nat
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 83dd67efe3f..5d798e877e8 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -251,6 +251,23 @@ add_cmd (const char *name, enum command_class theclass,
   return result;
 }
 
+/* Add an element with a suppress notification to the LIST of commands.  */
+
+struct cmd_list_element *
+add_cmd_suppress_notification (const char *name, enum command_class theclass,
+			       cmd_const_cfunc_ftype *fun, const char *doc,
+			       struct cmd_list_element **list,
+			       int *suppress_notification)
+{
+  struct cmd_list_element *element;
+
+  element = add_cmd (name, theclass, fun, doc, list);
+  element->suppress_notification = suppress_notification;
+
+  return element;
+}
+
+
 /* Deprecates a command CMD.
    REPLACEMENT is the name of the command which should be used in
    place of this command, or NULL if no such command exists.
@@ -362,6 +379,25 @@ add_prefix_cmd (const char *name, enum command_class theclass,
   return c;
 }
 
+/* Like ADD_PREFIX_CMD but sets the suppress_notification pointer on the
+   new command list element.  */
+
+struct cmd_list_element *
+add_prefix_cmd_suppress_notification
+               (const char *name, enum command_class theclass,
+		cmd_const_cfunc_ftype *fun,
+		const char *doc, struct cmd_list_element **prefixlist,
+		const char *prefixname, int allow_unknown,
+		struct cmd_list_element **list,
+		int *suppress_notification)
+{
+  struct cmd_list_element *element
+    = add_prefix_cmd (name, theclass, fun, doc, prefixlist,
+		      prefixname, allow_unknown, list);
+  element->suppress_notification = suppress_notification;
+  return element;
+}
+
 /* Like add_prefix_cmd but sets the abbrev_flag on the new command.  */
 
 struct cmd_list_element *
@@ -893,12 +929,8 @@ add_com_suppress_notification (const char *name, enum command_class theclass,
 			       cmd_const_cfunc_ftype *fun, const char *doc,
 			       int *suppress_notification)
 {
-  struct cmd_list_element *element;
-
-  element = add_cmd (name, theclass, fun, doc, &cmdlist);
-  element->suppress_notification = suppress_notification;
-
-  return element;
+  return add_cmd_suppress_notification (name, theclass, fun, doc,
+					&cmdlist, suppress_notification);
 }
 
 /* Recursively walk the commandlist structures, and print out the
diff --git a/gdb/command.h b/gdb/command.h
index 3dde2475cb1..e3d55c2dcba 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -148,6 +148,12 @@ extern struct cmd_list_element *add_cmd (const char *, enum command_class,
 					 const char *,
 					 struct cmd_list_element **);
 
+extern struct cmd_list_element *add_cmd_suppress_notification
+			(const char *name, enum command_class theclass,
+			 cmd_const_cfunc_ftype *fun, const char *doc,
+			 struct cmd_list_element **list,
+			 int *suppress_notification);
+
 extern struct cmd_list_element *add_alias_cmd (const char *, const char *,
 					       enum command_class, int,
 					       struct cmd_list_element **);
@@ -165,6 +171,14 @@ extern struct cmd_list_element *add_prefix_cmd (const char *, enum command_class
 						const char *, int,
 						struct cmd_list_element **);
 
+extern struct cmd_list_element *add_prefix_cmd_suppress_notification
+			(const char *name, enum command_class theclass,
+			 cmd_const_cfunc_ftype *fun,
+			 const char *doc, struct cmd_list_element **prefixlist,
+			 const char *prefixname, int allow_unknown,
+			 struct cmd_list_element **list,
+			 int *suppress_notification);
+
 extern struct cmd_list_element *add_abbrev_prefix_cmd (const char *,
 						       enum command_class,
 						       cmd_const_cfunc_ftype *fun,
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index b9318344002..f91e2f434ed 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -7631,21 +7631,75 @@
 @table @code
 @kindex frame@r{, selecting}
 @kindex f @r{(@code{frame})}
-@item frame @var{n}
-@itemx f @var{n}
-Select frame number @var{n}.  Recall that frame zero is the innermost
-(currently executing) frame, frame one is the frame that called the
-innermost one, and so on.  The highest-numbered frame is the one for
-@code{main}.
+@item frame @r{[} @var{frame-selection-spec} @r{]}
+@item f @r{[} @var{frame-selection-spec} @r{]}
+The @command{frame} command allows different stack frames to be
+selected.  The @var{frame-selection-spec} can be any of the following:
+
+@table @code
+@kindex frame number
+@item @var{num}
+@item number @var{num}
+Select frame number @var{num}.  Recall that frame zero is the
+innermost (currently executing) frame, frame one is the frame that
+called the innermost one, and so on.  The highest numbered frame is
+the one for @code{main}.
+
+As this is the most common method of navigating the frame stack then
+the string @command{number} can be dropped, the following two commands
+are equivalent:
 
-@item frame @var{stack-addr} [ @var{pc-addr} ]
-@itemx f @var{stack-addr} [ @var{pc-addr} ]
-Select the frame at address @var{stack-addr}.  This is useful mainly if the
-chaining of stack frames has been damaged by a bug, making it
-impossible for @value{GDBN} to assign numbers properly to all frames.  In
-addition, this can be useful when your program has multiple stacks and
-switches between them.  The optional @var{pc-addr} can also be given to
-specify the value of PC for the stack frame.
+@smallexample
+(@value{GDBP}) frame 3
+(@value{GDBP}) frame number 3
+@end smallexample
+
+@kindex frame address
+@item address @var{stack-address}
+Select the frame with stack address @var{stack-address}.  The
+@var{stack-address} for a frame can be seen in the output of
+@command{info frame}, for example:
+
+@smallexample
+(gdb) info frame
+Stack level 1, frame at 0x7fffffffda30:
+ rip = 0x40066d in b (amd64-entry-value.cc:59); saved rip 0x4004c5
+ tail call frame, caller of frame at 0x7fffffffda30
+ source language c++.
+ Arglist at unknown address.
+ Locals at unknown address, Previous frame's sp is 0x7fffffffda30
+@end smallexample
+
+The @var{stack-address} for this frame is @code{0x7fffffffda30} as
+indicated by the line:
+
+@smallexample
+Stack level 1, frame at 0x7fffffffda30:
+@end smallexample
+
+@kindex frame function
+@item function @var{function-name}
+Select the stack frame for function @var{function-name}.  If there are
+multiple stack frames for function @var{function-name} then the inner
+most stack frame is selected.
+
+@kindex frame view
+@item view @var{stack-address} @r{[} @var{pc-addr} @r{]}
+View a frame that is not part of @value{GDBN}'s backtrace.  The frame
+viewed has stack address @var{stack-addr}, and optionally, a program
+counter address of @var{pc-addr}.
+
+This is useful mainly if the chaining of stack frames has been
+damaged by a bug, making it impossible for @value{GDBN} to assign
+numbers properly to all frames.  In addition, this can be useful
+when your program has multiple stacks and switches between them.
+
+When viewing a frame outside the current backtrace using
+@command{frame view} then you can always return to the original
+stack using one of the previous stack frame selection instructions,
+for example @command{frame number 0}.
+
+@end table
 
 @kindex up
 @item up @var{n}
@@ -7688,11 +7742,13 @@
 
 @table @code
 @kindex select-frame
-@item select-frame
+@item select-frame @r{[} @var{frame-selection-spec} @r{]}
 The @code{select-frame} command is a variant of @code{frame} that does
 not display the new frame after selecting it.  This command is
 intended primarily for use in @value{GDBN} command scripts, where the
-output might be unnecessary and distracting.
+output might be unnecessary and distracting.  The
+@var{frame-selection-spec} is as for the @command{frame} command
+described in @ref{Selection, ,Selecting a Frame}.
 
 @kindex down-silently
 @kindex up-silently
@@ -7750,13 +7806,12 @@
 something has gone wrong that has made the stack format fail to fit
 the usual conventions.
 
-@item info frame @var{addr}
-@itemx info f @var{addr}
-Print a verbose description of the frame at address @var{addr}, without
-selecting that frame.  The selected frame remains unchanged by this
-command.  This requires the same kind of address (more than one for some
-architectures) that you specify in the @code{frame} command.
-@xref{Selection, ,Selecting a Frame}.
+@item info frame @r{[} @var{frame-selection-spec} @r{]}
+@itemx info f @r{[} @var{frame-selection-spec} @r{]}
+Print a verbose description of the frame selected by
+@var{frame-selection-spec}.  The @var{frame-selection-spec} is the
+same as for the @command{frame} command (@pxref{Selection, ,Selecting
+a Frame}).  The selected frame remains unchanged by this command.
 
 @kindex info args
 @item info args
@@ -30280,11 +30335,12 @@
 @subsubheading Synopsis
 
 @smallexample
- -stack-select-frame @var{framenum}
+ -stack-select-frame @var{frame-selection-spec}
 @end smallexample
 
-Change the selected frame.  Select a different frame @var{framenum} on
-the stack.
+Change the selected frame, the @var{frame-selection-spec} is as for
+the @command{frame} command described in @ref{Selection, ,Selecting a
+Frame}.
 
 This command in deprecated in favor of passing the @samp{--frame}
 option to every command.
diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c
index 52660bdd498..33ac7663ba8 100644
--- a/gdb/mi/mi-cmd-stack.c
+++ b/gdb/mi/mi-cmd-stack.c
@@ -681,10 +681,8 @@ list_args_or_locals (enum what_to_list what, enum print_values values,
 void
 mi_cmd_stack_select_frame (const char *command, char **argv, int argc)
 {
-  if (argc == 0 || argc > 1)
+  if (!select_frame_from_spec (argv, argc, 0))
     error (_("-stack-select-frame: Usage: FRAME_SPEC"));
-
-  select_frame_command (argv[0], 1 /* not used */ );
 }
 
 void
diff --git a/gdb/stack.c b/gdb/stack.c
index 3da46dd955a..046e32b5dba 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -102,6 +102,9 @@ static void set_last_displayed_sal (int valid,
 				    struct symtab *symtab,
 				    int line);
 
+static struct frame_info *find_frame_for_function (const char *);
+static struct frame_info *find_frame_for_address (CORE_ADDR);
+
 /* Zero means do things normally; we are interacting directly with the
    user.  One means print the full filename and linenumber when a
    frame is printed, and do so in a format emacs18/emacs19.22 can
@@ -1255,134 +1258,34 @@ print_frame (struct frame_info *frame, int print_level,
 }
 \f
 
-/* Read a frame specification in whatever the appropriate format is from
-   FRAME_EXP.  Call error() if the specification is in any way invalid (so
-   this function never returns NULL).  When SELECTED_FRAME_P is non-NULL
-   set its target to indicate that the default selected frame was used.  */
+/* Completion function for "frame function", "info frame function", and
+   "select-frame function" commands.  */
 
-static struct frame_info *
-parse_frame_specification (const char *frame_exp, int *selected_frame_p)
+void
+frame_selection_by_function_completer (struct cmd_list_element *ignore,
+				       completion_tracker &tracker,
+				       const char *text, const char *word)
 {
-  int numargs;
-  struct value *args[4];
-  CORE_ADDR addrs[ARRAY_SIZE (args)];
-
-  if (frame_exp == NULL)
-    numargs = 0;
-  else
-    {
-      numargs = 0;
-      while (1)
-	{
-	  const char *p;
-
-	  /* Skip leading white space, bail of EOL.  */
-	  frame_exp = skip_spaces (frame_exp);
-	  if (!*frame_exp)
-	    break;
-
-	  /* Parse the argument, extract it, save it.  */
-	  for (p = frame_exp;
-	       *p && !ISSPACE (*p);
-	       p++);
-	  std::string addr_string (frame_exp, p - frame_exp);
-	  frame_exp = p;
-	  
-	  /* NOTE: Parse and evaluate expression, but do not use
-	     functions such as parse_and_eval_long or
-	     parse_and_eval_address to also extract the value.
-	     Instead value_as_long and value_as_address are used.
-	     This avoids problems with expressions that contain
-	     side-effects.  */
-	  if (numargs >= ARRAY_SIZE (args))
-	    error (_("Too many args in frame specification"));
-	  args[numargs++] = parse_and_eval (addr_string.c_str ());
-	}
-    }
-
-  /* If no args, default to the selected frame.  */
-  if (numargs == 0)
-    {
-      if (selected_frame_p != NULL)
-	(*selected_frame_p) = 1;
-      return get_selected_frame (_("No stack."));
-    }
-
-  /* None of the remaining use the selected frame.  */
-  if (selected_frame_p != NULL)
-    (*selected_frame_p) = 0;
-
-  /* Assume the single arg[0] is an integer, and try using that to
-     select a frame relative to current.  */
-  if (numargs == 1)
-    {
-      struct frame_info *fid;
-      int level = value_as_long (args[0]);
-
-      fid = find_relative_frame (get_current_frame (), &level);
-      if (level == 0)
-	/* find_relative_frame was successful.  */
-	return fid;
-    }
-
-  /* Convert each value into a corresponding address.  */
-  {
-    int i;
-
-    for (i = 0; i < numargs; i++)
-      addrs[i] = value_as_address (args[i]);
-  }
-
-  /* Assume that the single arg[0] is an address, use that to identify
-     a frame with a matching ID.  Should this also accept stack/pc or
-     stack/pc/special.  */
-  if (numargs == 1)
-    {
-      struct frame_id id = frame_id_build_wild (addrs[0]);
-      struct frame_info *fid;
-
-      /* If (s)he specifies the frame with an address, he deserves
-	 what (s)he gets.  Still, give the highest one that matches.
-	 (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't
-	 know).  */
-      for (fid = get_current_frame ();
-	   fid != NULL;
-	   fid = get_prev_frame (fid))
-	{
-	  if (frame_id_eq (id, get_frame_id (fid)))
-	    {
-	      struct frame_info *prev_frame;
-
-	      while (1)
-		{
-		  prev_frame = get_prev_frame (fid);
-		  if (!prev_frame
-		      || !frame_id_eq (id, get_frame_id (prev_frame)))
-		    break;
-		  fid = prev_frame;
-		}
-	      return fid;
-	    }
-	}
-      }
-
-  /* We couldn't identify the frame as an existing frame, but
-     perhaps we can create one with a single argument.  */
-  if (numargs == 1)
-    return create_new_frame (addrs[0], 0);
-  else if (numargs == 2)
-    return create_new_frame (addrs[0], addrs[1]);
-  else
-    error (_("Too many args in frame specification"));
+  /* This is used to complete function names within a stack.  It would be
+     nice if instead of offering all available function names, we only
+     offered functions that were actually in the stack.  However, this
+     would probably mean unwinding the stack to completion, which could
+     take too long (or on a corrupted stack, possibly not end).  For now I
+     offer all symbol names as a safer choice.  */
+  collect_symbol_completion_matches (tracker,
+				     complete_symbol_mode::EXPRESSION,
+				     symbol_name_match_type::EXPRESSION,
+				     text, word);
 }
 
-/* Print verbosely the selected frame or the frame at address
-   ADDR_EXP.  Absolutely all information in the frame is printed.  */
+/* Core of all the "info frame" sub-commands.  Print verbosely the frame FI
+   if SELECTED_FRAME_P is true then frame FI is the current frame, which
+   was selected as a default due to the user not providing any arguments
+   to select some other frame.  */
 
 static void
-info_frame_command (const char *addr_exp, int from_tty)
+info_frame_command_core (struct frame_info *fi, bool selected_frame_p)
 {
-  struct frame_info *fi;
   struct symbol *func;
   struct symtab *s;
   struct frame_info *calling_frame_info;
@@ -1390,7 +1293,6 @@ info_frame_command (const char *addr_exp, int from_tty)
   const char *funname = 0;
   enum language funlang = language_unknown;
   const char *pc_regname;
-  int selected_frame_p;
   struct gdbarch *gdbarch;
   CORE_ADDR frame_pc;
   int frame_pc_p;
@@ -1398,7 +1300,6 @@ info_frame_command (const char *addr_exp, int from_tty)
   CORE_ADDR caller_pc = 0;
   int caller_pc_p = 0;
 
-  fi = parse_frame_specification (addr_exp, &selected_frame_p);
   gdbarch = get_frame_arch (fi);
 
   /* Name of the value returned by get_frame_pc().  Per comments, "pc"
@@ -1739,6 +1640,140 @@ trailing_outermost_frame (int count)
   return trailing;
 }
 
+/* The core of all the "select-frame" sub-commands.  Just wraps a call to
+   SELECT_FRAME.  */
+
+static void
+select_frame_command_core (struct frame_info *fi, bool ignored)
+{
+  struct frame_info *prev_frame = get_selected_frame_if_set ();
+  select_frame (fi);
+  if (get_selected_frame_if_set () != prev_frame)
+    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
+}
+
+/* The core of all the "frame" sub-commands.  Select frame FI, and if this
+   means we change frame send out a change notification (otherwise, just
+   reprint the current frame summary).   */
+
+static void
+frame_command_core (struct frame_info *fi, bool ignored)
+{
+  struct frame_info *prev_frame = get_selected_frame_if_set ();
+
+  select_frame (fi);
+  if (get_selected_frame_if_set () != prev_frame)
+    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
+  else
+    print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME);
+}
+
+/* The three commands 'frame', 'select-frame', and 'info frame' all have a
+   common set of sub-commands that allow a specific frame to be selected.
+   All of the sub-command functions are static methods within this class
+   template which is then instantiated below.  */
+
+template <void (*FPTR) (struct frame_info *, bool)>
+class frame_command_helper
+{
+public:
+
+  /* The "frame number" family of commands.  The ARG is a number that is
+     the frame's level in the stack.  */
+  static void
+  number (const char *arg, int from_tty)
+  {
+    int number = value_as_long (parse_and_eval (arg));
+    struct frame_info *fid
+      = find_relative_frame (get_current_frame (), &number);
+    if (number != 0)
+      error (_("No frame number %s."), arg);
+    FPTR (fid, false);
+  }
+
+  /* The "frame address" family of commands.  ARG is a stack-pointer
+     address for an existing frame.  This command does not allow new
+     frames to be created.  */
+
+  static void
+  address (const char *arg, int from_tty)
+  {
+    CORE_ADDR addr = value_as_address (parse_and_eval (arg));
+    struct frame_info *fid = find_frame_for_address (addr);
+    if (fid == NULL)
+      error (_("No frame at address %s."), arg);
+    FPTR (fid, false);
+  }
+
+  /* The "frame view" family of commands.  ARG is one or two addresses and
+     is used to view a frame that might be outside the current backtrace.
+     The addresses are stack-pointer address, and (optional) pc-address.  */
+
+  static void
+  view (const char *args, int from_tty)
+  {
+    struct frame_info *fid;
+
+    if (args == NULL)
+    error (_("Missing address argument to view a frame"));
+
+    gdb_argv argv (args);
+
+    if (argv.count () == 2)
+      {
+	CORE_ADDR addr[2];
+
+	addr [0] = value_as_address (parse_and_eval (argv[0]));
+	addr [1] = value_as_address (parse_and_eval (argv[1]));
+	fid = create_new_frame (addr[0], addr[1]);
+      }
+    else
+      {
+	CORE_ADDR addr = value_as_address (parse_and_eval (argv[0]));
+	fid = create_new_frame (addr, false);
+      }
+    FPTR (fid, false);
+  }
+
+  /* The "frame function" family of commands.  ARG is the name of a
+     function within the stack, the first function (searching from frame
+     0) with that name will be selected.  */
+
+  static void
+  function (const char *arg, int from_tty)
+  {
+    if (arg == NULL)
+      error (_("Missing function name argument"));
+    struct frame_info *fid = find_frame_for_function (arg);
+    if (fid == NULL)
+      error (_("No frame for function \"%s\"."), arg);
+    FPTR (fid, false);
+  }
+
+  /* The "frame" base command, that is, when no sub-command is specified.
+     If one argument is provided then we assume that this is a frame number
+     as historically, this was the supported command syntax that was used
+     most often.
+
+     If no argument is provided, then the current frame is selected.  */
+
+  static void
+  base_command (const char *arg, int from_tty)
+  {
+    if (arg == NULL)
+      FPTR (get_selected_frame (_("No stack.")), true);
+    else
+      number (arg, from_tty);
+  }
+};
+
+/* Instantiate three FRAME_COMMAND_HELPER instances to implement the
+   sub-commands for 'info frame', 'frame', and 'select-frame' commands.  */
+
+static frame_command_helper <info_frame_command_core> info_frame_cmd;
+static frame_command_helper <frame_command_core> frame_cmd;
+static frame_command_helper <select_frame_command_core> select_frame_cmd;
+
 /* Print briefly all stack frames or just the innermost COUNT_EXP
    frames.  */
 
@@ -2227,37 +2262,43 @@ find_relative_frame (struct frame_info *frame, int *level_offset_ptr)
   return frame;
 }
 
-/* The "select_frame" command.  With no argument this is a NOP.
-   Select the frame at level LEVEL_EXP if it is a valid level.
-   Otherwise, treat LEVEL_EXP as an address expression and select it.
-
-   See parse_frame_specification for more info on proper frame
-   expressions.  */
-
-void
-select_frame_command (const char *level_exp, int from_tty)
-{
-  struct frame_info *prev_frame = get_selected_frame_if_set ();
-
-  select_frame (parse_frame_specification (level_exp, NULL));
-  if (get_selected_frame_if_set () != prev_frame)
-    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
-}
-
-/* The "frame" command.  With no argument, print the selected frame
-   briefly.  With an argument, behave like select_frame and then print
-   the selected frame.  */
+/* Select a frame using ARGV.  This is used from the MI -stack-select-frame
+   to provide the same frame specification mechanism that the CLI has for
+   commands like 'frame'.  The return value is true if the contents of
+   ARGV looked like a sensible attempt to change the frame (an error might
+   still be thrown though), or false if the contents of ARGV are not a
+   correct frame specification.  */
 
-static void
-frame_command (const char *level_exp, int from_tty)
+bool
+select_frame_from_spec (char **argv, int argc, int from_tty)
 {
-  struct frame_info *prev_frame = get_selected_frame_if_set ();
+  if (argc == 1)
+    select_frame_cmd.number (argv[0], from_tty);
+  else if (argc == 2
+	   && strncasecmp ("number", argv[0], strlen (argv[0])) == 0)
+    select_frame_cmd.number (argv[1], from_tty);
+  else if (argc == 2
+	   && strncasecmp ("address", argv[0], strlen (argv[0])) == 0)
+    select_frame_cmd.address (argv[1], from_tty);
+  else if (argc == 2
+	   && strncasecmp ("function", argv[0], strlen (argv[0])) == 0)
+    select_frame_cmd.function (argv[1], from_tty);
+  else if ((argc == 2 || argc == 3)
+	   && strncasecmp ("view", argv[0], strlen (argv[0])) == 0)
+    {
+      std::string arg;
+
+      if (argc == 2)
+	arg = string_printf ("%s", argv[1]);
+      else
+	arg = string_printf ("%s %s", argv[1], argv[2]);
 
-  select_frame (parse_frame_specification (level_exp, NULL));
-  if (get_selected_frame_if_set () != prev_frame)
-    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
+      select_frame_cmd.view (arg.c_str (), from_tty);
+    }
   else
-    print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME);
+    return false;
+
+  return true;
 }
 
 /* Select the frame up one or COUNT_EXP stack levels from the
@@ -2713,13 +2754,61 @@ faas_command (const char *cmd, int from_tty)
 }
 
 
+/* Find inner-mode frame with frame address ADDRESS.  Return NULL if no
+   matching frame can be found.  */
+
+static struct frame_info *
+find_frame_for_address (CORE_ADDR address)
+{
+  struct frame_id id;
+  struct frame_info *fid;
+
+  id = frame_id_build_wild (address);
+
+  /* If (s)he specifies the frame with an address, he deserves
+     what (s)he gets.  Still, give the highest one that matches.
+     (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't
+     know).  */
+  for (fid = get_current_frame ();
+       fid != NULL;
+       fid = get_prev_frame (fid))
+    {
+      if (frame_id_eq (id, get_frame_id (fid)))
+	{
+	  struct frame_info *prev_frame;
+
+	  while (1)
+	    {
+	      prev_frame = get_prev_frame (fid);
+	      if (!prev_frame
+		  || !frame_id_eq (id, get_frame_id (prev_frame)))
+		break;
+	      fid = prev_frame;
+	    }
+	  return fid;
+	}
+    }
+  return NULL;
+}
+
+\f
+
+/* Commands with a prefix of `frame apply'.  */
+static struct cmd_list_element *frame_apply_cmd_list = NULL;
+
 /* Commands with a prefix of `frame'.  */
-struct cmd_list_element *frame_cmd_list = NULL;
+static struct cmd_list_element *frame_cmd_list = NULL;
+
+/* Commands with a prefix of `select frame'.  */
+static struct cmd_list_element *select_frame_cmd_list = NULL;
+
+/* Commands with a prefix of `info frame'.  */
+static struct cmd_list_element *info_frame_cmd_list = NULL;
 
 void
 _initialize_stack (void)
 {
-  static struct cmd_list_element *frame_apply_list = NULL;
+  struct cmd_list_element *cmd;
 
   add_com ("return", class_stack, return_command, _("\
 Make selected stack frame return to its caller.\n\
@@ -2743,12 +2832,12 @@ An argument says how many frames down to go."));
 Same as the `down' command, but does not print anything.\n\
 This is useful in command scripts."));
 
-  add_prefix_cmd ("frame", class_stack, frame_command, _("\
-Select and print a stack frame.\nWith no argument, \
-print the selected stack frame.  (See also \"info frame\").\n\
-An argument specifies the frame to select.\n\
-It can be a stack frame number or the address of the frame."),
-		  &frame_cmd_list, "frame ", 1, &cmdlist);
+  add_prefix_cmd ("frame", class_stack,
+                  &frame_cmd.base_command, _("\
+Select and print a stack frame.\n\
+With no argument, print the selected stack frame.  (See also \"info frame\").\n\
+A single numerical argument specifies the frame to select."),
+                  &frame_cmd_list, "frame ", 1, &cmdlist);
 
   add_com_alias ("f", "frame", class_stack, 1);
 
@@ -2766,7 +2855,7 @@ or produces no output."
 Usage: frame apply COUNT [FLAG]... COMMAND\n\
 With a negative COUNT argument, applies the command on outermost -COUNT frames.\n"
 FRAME_APPLY_FLAGS_HELP),
-		  &frame_apply_list, "frame apply ", 1, &frame_cmd_list);
+		  &frame_apply_cmd_list, "frame apply ", 1, &frame_cmd_list);
 
   add_cmd ("all", class_stack, frame_apply_all_command,
 	   _("\
@@ -2774,7 +2863,7 @@ Apply a command to all frames.\n\
 \n\
 Usage: frame apply all [FLAG]... COMMAND\n"
 FRAME_APPLY_FLAGS_HELP),
-	   &frame_apply_list);
+	   &frame_apply_cmd_list);
 
   add_cmd ("level", class_stack, frame_apply_level_command,
 	   _("\
@@ -2783,19 +2872,97 @@ Apply a command to a list of frames.\n\
 Usage: frame apply level LEVEL... [FLAG]... COMMAND\n\
 ID is a space-separated list of LEVELs of frames to apply COMMAND on.\n"
 FRAME_APPLY_FLAGS_HELP),
-	   &frame_apply_list);
+	   &frame_apply_cmd_list);
 
   add_com ("faas", class_stack, faas_command, _("\
 Apply a command to all frames (ignoring errors and empty output).\n\
 Usage: faas COMMAND\n\
 shortcut for 'frame apply all -s COMMAND'"));
 
-  add_com_suppress_notification ("select-frame", class_stack, select_frame_command, _("\
+
+  add_prefix_cmd ("frame", class_stack,
+		  &frame_cmd.base_command, _("\
+Select and print a stack frame.\n\
+With no argument, print the selected stack frame.  (See also \"info frame\").\n\
+A single numerical argument specifies the frame to select."),
+		  &frame_cmd_list, "frame ", 1, &cmdlist);
+  add_com_alias ("f", "frame", class_stack, 1);
+
+  add_cmd ("address", class_stack, &frame_cmd.address,
+	   _("\
+Select and print a stack frame by stack address\n\
+\n\
+Usage: frame address STACK-ADDRESS"),
+	   &frame_cmd_list);
+
+  add_cmd ("view", class_stack, &frame_cmd.view,
+	   _("\
+View a stack frame that might be outside the current backtrace.\n\
+\n\
+Usage: frame view STACK-ADDRESS\n\
+       frame view STACK-ADDRESS PC-ADDRESS"),
+	   &frame_cmd_list);
+
+  cmd = add_cmd ("function", class_stack, &frame_cmd.function,
+	   _("\
+Select and print a stack frame by function name.\n\
+\n\
+Usage: frame function NAME\n\
+\n\
+The innermost frame that visited function NAME is selected."),
+	   &frame_cmd_list);
+  set_cmd_completer (cmd, frame_selection_by_function_completer);
+
+
+  add_cmd ("number", class_stack, &frame_cmd.number,
+	   _("\
+Select and print a stack frame by its number.\n\
+\n\
+Usage: frame number NUMBER"),
+	   &frame_cmd_list);
+
+  cmd = add_prefix_cmd_suppress_notification ("select-frame", class_stack,
+		      &select_frame_cmd.base_command, _("\
 Select a stack frame without printing anything.\n\
-An argument specifies the frame to select.\n\
-It can be a stack frame number or the address of the frame."),
+A single numerical argument specifies the frame to select."),
+		      &select_frame_cmd_list, "select-frame ", 1, &cmdlist,
+		      &cli_suppress_notification.user_selected_context);
+
+  add_cmd_suppress_notification ("address", class_stack,
+			 &select_frame_cmd.address, _("\
+Select a stack frame by stack address.\n\
+\n\
+Usage: select-frame address STACK-ADDRESS"),
+			 &select_frame_cmd_list,
+			 &cli_suppress_notification.user_selected_context);
+
+
+  add_cmd_suppress_notification ("view", class_stack,
+		 &select_frame_cmd.view, _("\
+Select a stack frame that might be outside the current backtrace.\n\
+\n\
+Usage: select-frame view STACK-ADDRESS\n\
+       select-frame view STACK-ADDRESS PC-ADDRESS"),
+		 &select_frame_cmd_list,
 		 &cli_suppress_notification.user_selected_context);
 
+  cmd = add_cmd_suppress_notification ("function", class_stack,
+	       &select_frame_cmd.function, _("\
+Select a stack frame by function name.\n\
+\n\
+Usage: select-frame function NAME"),
+	       &select_frame_cmd_list,
+	       &cli_suppress_notification.user_selected_context);
+  set_cmd_completer (cmd, frame_selection_by_function_completer);
+
+  add_cmd_suppress_notification ("number", class_stack,
+			 &select_frame_cmd.number, _("\
+Select a stack frame by its number.\n\
+\n\
+Usage: select-frame number NUMBER"),
+			 &select_frame_cmd_list,
+			 &cli_suppress_notification.user_selected_context);
+
   add_com ("backtrace", class_stack, backtrace_command, _("\
 Print backtrace of all stack frames, or innermost COUNT frames.\n\
 Usage: backtrace [QUALIFIERS]... [COUNT]\n\
@@ -2809,9 +2976,45 @@ on this backtrace."));
   add_info ("stack", backtrace_command,
 	    _("Backtrace of the stack, or innermost COUNT frames."));
   add_info_alias ("s", "stack", 1);
-  add_info ("frame", info_frame_command,
-	    _("All about selected stack frame, or frame at ADDR."));
+
+  add_prefix_cmd ("frame", class_info, &info_frame_cmd.base_command,
+		  _("All about the selected stack frame.\n\
+With no arguments, displays information about the currently selected stack\n\
+frame.  Alternatively a frame specification may be provided (See \"frame\")\n\
+the information is then printed about the specified frame."),
+		  &info_frame_cmd_list, "info frame ", 1, &infolist);
   add_info_alias ("f", "frame", 1);
+
+  add_cmd ("address", class_stack, &info_frame_cmd.address,
+	   _("\
+Print information about a stack frame selected by stack address.\n\
+\n\
+Usage: info frame address STACK-ADDRESS"),
+	   &info_frame_cmd_list);
+
+  add_cmd ("view", class_stack, &info_frame_cmd.view,
+	   _("\
+Print information about a stack frame outside the current backtrace.\n\
+\n\
+Usage: info frame view STACK-ADDRESS\n\
+       info frame view STACK-ADDRESS PC-ADDRESS"),
+	   &info_frame_cmd_list);
+
+  cmd = add_cmd ("function", class_stack, &info_frame_cmd.function,
+	   _("\
+Print information about a stack frame selected by function name.\n\
+\n\
+Usage: info frame function NAME"),
+	   &info_frame_cmd_list);
+  set_cmd_completer (cmd, frame_selection_by_function_completer);
+
+  add_cmd ("number", class_stack, &info_frame_cmd.number,
+	   _("\
+Print information about a stack frame selected by its number.\n\
+\n\
+Usage: info frame number NUMBER"),
+	   &info_frame_cmd_list);
+
   add_info ("locals", info_locals_command,
 	    _("Local variables of current stack frame."));
   add_info ("args", info_args_command,
diff --git a/gdb/stack.h b/gdb/stack.h
index ca190efa9c3..29794872d70 100644
--- a/gdb/stack.h
+++ b/gdb/stack.h
@@ -20,7 +20,7 @@
 #ifndef STACK_H
 #define STACK_H
 
-void select_frame_command (const char *level_exp, int from_tty);
+bool select_frame_from_spec (char **argv, int argc, int from_tty);
 
 gdb::unique_xmalloc_ptr<char> find_frame_funname (struct frame_info *frame,
 						  enum language *funlang,
diff --git a/gdb/testsuite/gdb.base/frame-selection.c b/gdb/testsuite/gdb.base/frame-selection.c
new file mode 100644
index 00000000000..f3d81f223e0
--- /dev/null
+++ b/gdb/testsuite/gdb.base/frame-selection.c
@@ -0,0 +1,52 @@
+/* Copyright 2018 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int
+frame_2 (void)
+{
+  return 0;
+}
+
+int
+frame_1 (void)
+{
+  return frame_2 ();
+}
+
+int
+recursive (int arg)
+{
+  int v;
+
+  if (arg < 2)
+    v = recursive (arg + 1);
+  else
+    v = frame_2 ();
+
+  return v;
+}
+
+int
+main (void)
+{
+  int i, j;
+
+  i = frame_1 ();
+  j = recursive (0);
+
+  return i + j;
+}
diff --git a/gdb/testsuite/gdb.base/frame-selection.exp b/gdb/testsuite/gdb.base/frame-selection.exp
new file mode 100644
index 00000000000..8c2b0ed910e
--- /dev/null
+++ b/gdb/testsuite/gdb.base/frame-selection.exp
@@ -0,0 +1,157 @@
+# Copyright 2018 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+standard_testfile
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug}]} {
+    return -1
+}
+
+runto_main
+gdb_breakpoint frame_2
+gdb_continue_to_breakpoint frame_2
+
+gdb_test "bt" "#0  frame_2.*#1  $hex in frame_1.*#2  $hex in main.*" "backtrace at breakpoint"
+
+# Perform "info frame" to extract the frames address.
+proc get_frame_address { {testname ""} } {
+    global hex gdb_prompt
+
+    set testname "get_frame_address: ${testname}"
+    set frame_address "unknown"
+
+    send_gdb "info frame\n"
+    gdb_expect {
+	-re ", frame at ($hex):\r\n.*\r\n$gdb_prompt $" {
+	    set frame_address $expect_out(1,string)
+	    pass $testname
+	}
+	-re ".*$gdb_prompt $" { fail $testname }
+	timeout { fail "$testname (timeout)" }
+    }
+
+    return $frame_address
+}
+
+# Passes a list of addresses.  Return a new address that is not in the
+# list.
+proc get_new_address { {addresses {}} } {
+    return [format "%#x" [expr [lindex $addresses [llength addresses]] + 0x10 ]]
+}
+
+proc check_frame { number address function } {
+    global hex gdb_prompt
+
+    set testname "check frame number ${number}"
+    send_gdb "info frame\n"
+    gdb_expect {
+	-re "Stack level ${number}, frame at ($address):\r\n rip = $hex in ${function} \(\[^\r\n\]*\); saved rip = $hex\r\n.*\r\n$gdb_prompt $" {
+	    pass $testname
+	}
+	-re ".*$gdb_prompt $" { fail $testname }
+	timeout { fail "$testname (timeout)" }
+    }
+}
+
+# Select frame number, but relying on this being the default action,
+# so "frame 0" performs "frame number 0".
+gdb_test "frame 0" "#0  frame_2.*"
+set frame_0_address [ get_frame_address "frame 0" ]
+gdb_test "frame 1" "#1  $hex in frame_1.*"
+set frame_1_address [ get_frame_address "frame 1" ]
+gdb_test "frame 2" "#2  $hex in main.*"
+set frame_2_address [ get_frame_address "frame 2" ]
+gdb_test "frame 3" "No frame number 3\."
+
+# Find an address that matches no frame
+set no_frame_address [ get_new_address [list $frame_0_address \
+					     $frame_1_address \
+					     $frame_2_address] ]
+
+# Select frame using 'number' specification.
+gdb_test "frame number 0" "#0  frame_2.*"
+gdb_test "frame number 1" "#1  $hex in frame_1.*"
+gdb_test "frame number 2" "#2  $hex in main.*"
+gdb_test "frame number 3" "No frame number 3\."
+
+# Select frame by address.
+gdb_test "frame address ${frame_0_address}" "#0  frame_2.*"
+gdb_test "frame address ${frame_1_address}" "#1  $hex in frame_1.*"
+gdb_test "frame address ${frame_2_address}" "#2  $hex in main.*"
+gdb_test "frame address ${no_frame_address}" \
+    "No frame at address ${no_frame_address}\."
+
+# Select frame by function.
+gdb_test "frame function frame_2" "#0  frame_2.*"
+gdb_test "frame function frame_1" "#1  $hex in frame_1.*"
+gdb_test "frame function main" "#2  $hex in main.*"
+# Check for a distinction between a known function not in the stack
+# trace, and an unknown function.
+gdb_test "frame function recursive" "No frame for function \"recursive\"."
+gdb_test "frame function foo" "Function \"foo\" not defined."
+
+
+gdb_test_no_output "select-frame 0"
+check_frame "0" "${frame_0_address}" "frame_2"
+gdb_test_no_output "select-frame 1"
+check_frame "1" "${frame_1_address}" "frame_1"
+gdb_test_no_output "select-frame 2"
+check_frame "2" "${frame_2_address}" "main"
+gdb_test "select-frame 3" "No frame number 3\."
+
+gdb_test_no_output "select-frame number 0"
+check_frame "0" "${frame_0_address}" "frame_2"
+gdb_test_no_output "select-frame number 1"
+check_frame "1" "${frame_1_address}" "frame_1"
+gdb_test_no_output "select-frame number 2"
+check_frame "2" "${frame_2_address}" "main"
+gdb_test "select-frame number 3" "No frame number 3\."
+
+gdb_test_no_output "select-frame address ${frame_0_address}"
+check_frame "0" "${frame_0_address}" "frame_2"
+gdb_test_no_output "select-frame address ${frame_1_address}"
+check_frame "1" "${frame_1_address}" "frame_1"
+gdb_test_no_output "select-frame address ${frame_2_address}"
+check_frame "2" "${frame_2_address}" "main"
+gdb_test "select-frame address ${no_frame_address}" \
+    "No frame at address ${no_frame_address}\."
+
+gdb_test_no_output "select-frame function frame_2"
+check_frame "0" "${frame_0_address}" "frame_2"
+gdb_test_no_output "select-frame function frame_1"
+check_frame "1" "${frame_1_address}" "frame_1"
+gdb_test_no_output "select-frame function main"
+check_frame "2" "${frame_2_address}" "main"
+# Check for a distinction between a known function not in the stack
+# trace, and an unknown function.
+gdb_test "select-frame function recursive" \
+    "No frame for function \"recursive\"."
+gdb_test "select-frame function foo" \
+    "Function \"foo\" not defined."
+
+# Now continue until we hit the breakpoint again.
+gdb_continue_to_breakpoint frame_2
+gdb_test "bt" \
+    "#0  frame_2.*#1  $hex in recursive.*#2  $hex in recursive.*#3  $hex in recursive.*#4  $hex in main.*" \
+    "backtrace at breakpoint with recursive frames"
+
+# Check "frame function" when a function name occurs multiple times in
+# the stack.  The inner most (lowest number) should always be
+# selected.
+gdb_test "frame function frame_2" "#0  frame_2.*"
+gdb_test "frame function recursive" "#1  $hex in recursive.*"
+gdb_test "frame function recursive" "#1  $hex in recursive.*"
+gdb_test "frame function main" "#4  $hex in main.*"
+gdb_test "frame function recursive" "#1  $hex in recursive.*"
diff --git a/gdb/testsuite/gdb.mi/mi-frame-selection.c b/gdb/testsuite/gdb.mi/mi-frame-selection.c
new file mode 100644
index 00000000000..70b992c9149
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-frame-selection.c
@@ -0,0 +1,34 @@
+/* Copyright 2018 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int
+frame_2 (void)
+{
+  return 0;
+}
+
+int
+frame_1 (void)
+{
+  return frame_2 ();
+}
+
+int
+main (void)
+{
+  return frame_1 ();
+}
diff --git a/gdb/testsuite/gdb.mi/mi-frame-selection.exp b/gdb/testsuite/gdb.mi/mi-frame-selection.exp
new file mode 100644
index 00000000000..8cb6cc95028
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-frame-selection.exp
@@ -0,0 +1,89 @@
+# Copyright 2018 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test the frame selection syntax with the -stack-select-frame command.
+
+load_lib mi-support.exp
+set MIFLAGS "-i=mi"
+
+gdb_exit
+if [mi_gdb_start] {
+    continue
+}
+
+standard_testfile
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    untested $testfile
+    return -1
+}
+
+mi_delete_breakpoints
+mi_gdb_reinitialize_dir $srcdir/$subdir
+mi_gdb_load ${binfile}
+
+mi_create_breakpoint "frame_2" \
+    "break-insert into frame_2" \
+    -number 1 -func frame_2 -file ".*${srcfile}"
+
+mi_run_cmd
+mi_expect_stop "breakpoint-hit" "frame_2" "" ".*${srcfile}" \
+	".*" { "" "disp=\"keep\"" } "run to breakpoint"
+
+mi_gdb_test "-stack-info-depth" \
+    "\\^done,depth=\"3\"" \
+    "stack info-depth"
+
+for {set i 0} {$i < 5} {incr i} {
+
+    if { $i < 3 } then {
+	mi_gdb_test "-stack-select-frame $i" \
+	    {\^done} \
+	    "-stack-select-frame $i"
+
+	mi_gdb_test "-stack-info-frame" \
+	    "\\^done,frame=\\\{level=\"$i\"\[^\}\]+\\\}" \
+	    "check level has has changed to $i"
+
+    } else {
+	mi_gdb_test "-stack-select-frame view $i" \
+	    {\^done} \
+	    "-stack-select-frame view frame at $i"
+
+	mi_gdb_test "-stack-info-frame" \
+	    "\\^done,frame=\\\{level=\"0\"\[^\}\]+\\\}" \
+	    "check level has has changed to 0, when creating a frame at sp=$i"
+    }
+}
+
+mi_gdb_test "-stack-select-frame 5" \
+    {\^error,msg="No frame number 5."} \
+    "-stack-select-frame 5, with no keyword"
+
+mi_gdb_test "-stack-select-frame number 5" \
+    {\^error,msg="No frame number 5."} \
+    "-stack-select-frame 5, using 'number' keyword"
+
+mi_gdb_test "-stack-select-frame function main" \
+    {\^done} \
+    "-stack-select-frame select frame for function main"
+
+mi_gdb_test "-stack-info-frame" \
+    "\\^done,frame=\\\{level=\"2\"\[^\}\]+\\\}" \
+    "check level has has changed to 2 for function main"
+
+mi_gdb_test "-stack-select-frame function unknown_function" \
+    {\^error,msg=\"Function \\\"unknown_function\\\" not defined.\"} \
+    "-stack-select-frame check error on undefined function name"
-- 
2.14.4

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCHv5 0/2] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-08-13 22:20           ` [PATCHv5 0/2] " Andrew Burgess
@ 2018-08-14 10:31             ` Philippe Waroquiers
  2018-08-21 13:10               ` Joel Brobecker
  2018-08-27 11:04             ` Andrew Burgess
  1 sibling, 1 reply; 35+ messages in thread
From: Philippe Waroquiers @ 2018-08-14 10:31 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: Eli Zaretskii

On Mon, 2018-08-13 at 23:20 +0100, Andrew Burgess wrote:
> Given a lack of feedback on v4, I've put together two alternative
> versions to pick between...
> 
>   (A) In this version I took onboard the feedback from Eli and
>       Philippe that using "level" might be confusing to users, as
>       "number" is historically what has been used for the integer
>       label we give to frames.

What I find confusing is to have the same concept
('frame something_identifying_a_frame')
be called sometimes 'frame number' and sometimes 'frame level'.

My personal preference is to use 'frame level' because:
  * IMO, frame level better matches a stack/backtrace concept than the
    very general frame number (and number is overloaded e.g.
    in 'number of frames').
  * frame number is used for another concept (traceframe number,
    but without always using trace word.  E.g. the MI interface uses
    frame-number for 'frame something_identifying_a_traceframe'),
    which has (IIUC) not much to do with a backtrace frame.
  * there are a lot more usages of 'frame level' that 'frame number'
    (see below the result of analysing some grep 'frame/number/level'
    on the code and on the documentation).
    From what I can see, we have less than 20 occurrences of using
    number for a frame 'something', and we have a lot more places (80?)
    where the wording level is used for a frame 'something'
    (about 20 being the usage of frame_relative_level function).
  * I think we can (mostly?) fix all the inconsitencies by
    switching to frame level for a backtrace frame something,
    while I think it is (mostly?) desperate to fix the inconsistencies
    created by 'frame number' (due e.g. to MI using frame number for
    trace frame number').
    

> 
>       The command for selecting a frame by integer is now, 'frame
>       number <NUMBER>' and almost all references to "level" have now
>       been removed from the patch.
> 
>   (B) In this version I've taken onboard the advice from Philippe,
>       identifying places in the patch where I was mixing use of
>       "level" and "number".  I've doubled down on "level" and removed
>       most uses of "number" from the patch.
> 
> If we select patch 'A' then me might want to update 'frame apply level
> ...' to 'frame apply number ...' for consistency.
As indicated above, I prefer 'frame level', but no problem for me
to do a patch to change 'frame apply level' to 'frame apply number'
if that is what is decided).

> 
> If we select patch 'B' then we will probably need a follow up patch
> that goes through the documentation to tighten up references to
> "number" vs "level".
> 
> I really don't mind which approach we take, I guess I'd probably pick
> 'B' over 'A' given we already have 'frame apply level ...' in GDB, but
> if there's preference for 'A' then that's fine.
> 
> I'm also happy to do the follow up patches once we have some agreement
> on which way to go.
> 
> Thanks,
> Andrew
> 
> ---
> 
> Andrew Burgess (1):
>   gdb: Change how frames are selected for 'frame' and 'info frame'.
> 
>  gdb/ChangeLog                               |  36 ++
>  gdb/NEWS                                    |   8 +
>  gdb/cli/cli-decode.c                        |  44 ++-
>  gdb/command.h                               |  14 +
>  gdb/doc/ChangeLog                           |   8 +
>  gdb/doc/gdb.texinfo                         | 108 ++++--
>  gdb/mi/mi-cmd-stack.c                       |   4 +-
>  gdb/stack.c                                 | 535 +++++++++++++++++++---------
>  gdb/stack.h                                 |   2 +-
>  gdb/testsuite/ChangeLog                     |   7 +
>  gdb/testsuite/gdb.base/frame-selection.c    |  52 +++
>  gdb/testsuite/gdb.base/frame-selection.exp  | 157 ++++++++
>  gdb/testsuite/gdb.mi/mi-frame-selection.c   |  34 ++
>  gdb/testsuite/gdb.mi/mi-frame-selection.exp |  89 +++++
>  14 files changed, 896 insertions(+), 202 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.base/frame-selection.c
>  create mode 100644 gdb/testsuite/gdb.base/frame-selection.exp
>  create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.c
>  create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.exp
> 

Usages of level:
---------------

It is used in the info frame output:
(gdb) info frame
Stack level 1, frame at 0x7fffffffdf90:
 rip = 0x555555554f5e in sleeper_or_burner (sleepers.c:86); saved rip = 0x55555555549d
 called by frame at 0x7fffffffe050, caller of frame at 0x7fffffffdf50
 source language c.
 Arglist at 0x7fffffffdf80, args: v=0x7fffffffdfa0
 Locals at 0x7fffffffdf80, Previous frame's sp is 0x7fffffffdf90
 Saved registers:
  rbp at 0x7fffffffdf80, rip at 0x7fffffffdf88
(gdb)

infcmd.c:2023:      /* Print info on the selected frame, including level number but not
stack.c:2192:/* Find a frame a certain number of levels away from FRAME.
ada-lang.h:57:/* The maximum number of frame levels searched for non-local,
ada-lang.h:60:#define MAX_ENCLOSING_FRAME_LEVELS 7
defs.h:595:extern void (*selected_frame_level_changed_hook) (int);
    Obsolete ? I cannot find any reference to selected_frame_level_changed_hook.
extension.h:88:    /* Set this flag if frame level is to be printed.  */

frame.h:502:/* The frame's level: 0 for innermost, 1 for its caller, ...; or -1
frame.h:504:extern int frame_relative_level (struct frame_info *fi);
gdbthread.h:640:  int m_selected_frame_level;
stack.h:23:void select_frame_command (const char *level_exp, int from_tty);
ada-lang.c:12221:  int frame_level;
ada-lang.c:12230:  for (frame_level = 0; frame_level < 3; frame_level += 1)
ada-tasks.c:1339:                     frame_relative_level (get_selected_frame (NULL)),
amd64-tdep.c:2852:  if (frame_relative_level (this_frame) == 0)
annotate.c:435:annotate_frame_begin (int level, struct gdbarch *gdbarch, CORE_ADDR pc)
arm-tdep.c:2778:  if (frame_relative_level (this_frame) == 0)
dwarf2-frame-tailcall.c:175:  int retval = (frame_relative_level (this_frame)
dwarf2-frame-tailcall.c:176:		- frame_relative_level (cache->next_bottom_frame) - 1);

frame.c:87:  /* Level of this frame.  The inner-most (youngest) frame is at level
frame.c:88:     0.  As you move towards the outer-most (oldest) frame, the level
.... there are about 30 occurences of level in frame.c

hppa-tdep.c:1888:      frame_relative_level(this_frame));
hppa-tdep.c:2337:			frame_relative_level (this_frame));
i386-tdep.c:2236:  if (frame_relative_level (this_frame) == 0)
i386-tdep.c:2391:  if (frame_relative_level (this_frame) == 0)

infcmd.c:2023:      /* Print info on the selected frame, including level number but not
infcmd.c:2039:  /* Print info on the selected frame, including level number but not
microblaze-tdep.c:392:  if (frame_relative_level (next_frame) >= 0)
mips-tdep.c:1395:  if (frame_relative_level (next_frame) >= 0 && mips_in_frame_stub (pc))
nds32-tdep.c:1299:  if (frame_relative_level (this_frame) == 0)
or1k-tdep.c:539:			frame_relative_level (next_frame));
or1k-tdep.c:559:			frame_relative_level (next_frame));
rs6000-tdep.c:3443:  if (frame_relative_level (this_frame) == 0)

stack.c:1690:/* Return the innermost frame at level LEVEL.  */
stack.c:1693:leading_innermost_frame (int level)
... stack.c contains a lot of other occurences (some due to the new GDB 8.3
   frame apply level

value.c:3827:	 so that the frame level will be shown correctly.  */
value.c:3835:			  frame_relative_level (frame), regnum,

thread.c:1285:			       /* For MI output, print frame level.  */
thread.c:1403:restore_selected_frame (struct frame_id a_frame_id, int frame_level)
thread.c:1409:  if (frame_level == -1)
thread.c:1415:  gdb_assert (frame_level >= 0);
thread.c:1421:  count = frame_level;
thread.c:1449:  if (frame_level > 0 && !current_uiout->is_mi_like_p ())
thread.c:1453:	       frame_level);
thread.c:1487:    restore_selected_frame (m_selected_frame_id, m_selected_frame_level);
thread.c:1521:      m_selected_frame_level = frame_relative_level (frame);

python/py-framefilter.c:734:    (in the case of elided frames), and LEVELS_PRINTED is a hash-table
python/py-framefilter.c:735:    containing all the frames level that have already been printed.
python/py-framefilter.c:736:    If a frame level has been printed, do not print it again (in the
python/py-framefilter.c:818:  /* Print frame level.  MI does not require the level if
python/py-framefilter.c:828:      level = frame_relative_level (frame);
python/py-framefilter.c:839:	  annotate_frame_begin (print_level ? level : 0,


It looks like btrace.h uses level for a btrace frame, but the outermost frame
is numbered 0 (so the other way around as in backtrace command).
  /* The function level in a back trace across the entire branch trace.
     A caller's level is one lower than the level of its callee.
record-btrace.c:1759:  DEBUG ("[frame] unwound PC in %s on level %d: %s",
record-btrace.c:1809:  DEBUG ("[frame] sniffed frame for %s on level %d",
record-btrace.c:1851:  DEBUG ("[frame] sniffed tailcall frame for %s on level %d",

gdb.texinfo:5536:control reaches a different line of code at the original stack level
gdb.texinfo:7564:If you need to examine the startup code, or limit the number of levels
in a backtrace, you can change this behavior:
gdb.texinfo:7598:Limit the backtrace to @var{n} levels.  A value of @code{unlimited}
gdb.texinfo:7599:or zero means unlimited levels.
gdb.texinfo:7602:Display the current limit on backtrace levels.

gdb.texinfo:7778:@item frame apply [all | @var{count} | @var{-count} | level @var{level}@dots{}] [@var{flag}]@dots{} @var{command}
   (and 5 other occurences due to the new 'frame apply level')
gdb.texinfo:12510:Stack level 1, frame at 0x7fffffffda30:
gdb.texinfo:27863:The level of the stack frame.  The innermost frame has the level of
gdb.texinfo:29202:   frame=@{level="0",addr="0xffffe410",func="__kernel_vsyscall",
  and a whole bunch of references to level in the mi documentation/examples
  e.g.
gdb.texinfo:29941:For a stack with frame levels 0 through 11:
gdb.texinfo:29977:at the corresponding level.  It is an error if @var{low-frame} is
gdb.texinfo:30009:frame=@{level="0",addr="0x00010734",func="callee4",
gdb.texinfo:30012:frame=@{level="1",addr="0x0001076c",func="callee3",
gdb.texinfo:30015:frame=@{level="2",addr="0x0001078c",func="callee2",
gdb.texinfo:30018:frame=@{level="3",addr="0x000107b4",func="callee1",


The below might  be more clear as 'a low level unique identifier for a frame' ?
frame.h:442:   get_frame_id: A low level frame unique identifier, that consists of

Usages of number
----------------
stack.c:1765:	  /* The argument to apply_ext_lang_frame_filter is the number
stack.c:2750:It can be a stack frame number or the address of the frame."),
stack.c:2796:It can be a stack frame number or the address of the frame."),
cli/cli-cmds.c:1688:The stack is made up of stack frames.  Gdb assigns numbers to stack frames\n\
cli/cli-cmds.c:1693:The commands below can be used to select other frames by number or address."),
mi/mi-cmd-stack.c:76:   specifying the frame numbers at which to start and stop the


The below should be (or are in fact) traceframe number (but some cannot be changed
as they are part of the mi interface).
remote.c:13266:  /* Lookups other than by absolute frame number depend on the current
tracepoint.c:2196:   of information: a frame number, a tracepoint number, and an
tracepoint.c:2200:   target does not give us a frame number or a tracepoint number).
tracepoint.c:2203:   F<hexnum>    (gives the selected frame number)
gdbserver/tracepoint.c:949:   not need to keep a frame number, because they are all sequential
gdbserver/tracepoint.c:951:   always frame number N.  */
mi/mi-main.c:2330:  if (strcmp (mode, "frame-number") == 0)
mi/mi-main.c:2333:	error (_("frame number is required"));

gdb.texinfo:7396:@cindex frame number
@value{GDBN} assigns numbers to all existing stack frames, starting with
and so on upward.  These numbers do not really exist in your program;

**** In the below, the positive number is not a frame number
gdb.texinfo:7447:@itemx @var{n}
Print only the innermost @var{n} frames, where @var{n} is a positive
number.
@item -@var{n}
@itemx -@var{n}
Print only the outermost @var{n} frames, where @var{n} is a positive
number.
gdb.texinfo:7458:with a number to limit the number of frames shown.



gdb.texinfo:7489:Each line in the backtrace shows the frame number and the function name.

*** in the below, this is rather level that is being used ... as otherwise we should
say 'limit the number of numbers' :).
gdb.texinfo:7564:If you need to examine the startup code, or limit the number of levels


gdb.texinfo:7636:Select frame number @var{n}.  Recall that frame zero is the innermost
gdb.texinfo:7666:frame.  The first line shows the frame number, the function name, the

*** also in the below, this is using level (explaining that a level is a number)
gdb.texinfo:7797:levels as @var{level1}-@var{level2}.  The frame level is the number shown


*** the below is really a number, not a 'frame number'
gdb.texinfo:11204:it is the number of frames up in the stack to look.
gdb.texinfo:11231:it is the number of frames up in the stack to look.
gdb.texinfo:11240:it is the number of frames up in the stack to look.
gdb.texinfo:11254:it is the number of frames up in the stack to look.


*** I think the below should be traceframe.
gdb.texinfo:14099:The current trace snapshot (a.k.a.@: @dfn{frame}) number, or -1 if no


*** the below tells that frame level is frame number
@item @var{level}
The frame number, 0 being the topmost frame, i.e., the innermost function.

*** the below should be traceframe number, but that cannot really be changed
as this is part of MI interface.
gdb.texinfo:31858:@item frame-number
*** the below should be traceframe number
gdb.texinfo:39503:The selected frame is number @var{n} in the trace frame buffer;

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCHv5 0/2] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-08-14 10:31             ` Philippe Waroquiers
@ 2018-08-21 13:10               ` Joel Brobecker
  0 siblings, 0 replies; 35+ messages in thread
From: Joel Brobecker @ 2018-08-21 13:10 UTC (permalink / raw)
  To: Philippe Waroquiers; +Cc: Andrew Burgess, gdb-patches, Eli Zaretskii

> What I find confusing is to have the same concept
> ('frame something_identifying_a_frame')
> be called sometimes 'frame number' and sometimes 'frame level'.
> 
> My personal preference is to use 'frame level' because:

FWIW, that is my preference as well.


-- 
Joel

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCHv5 0/2] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-08-13 22:20           ` [PATCHv5 0/2] " Andrew Burgess
  2018-08-14 10:31             ` Philippe Waroquiers
@ 2018-08-27 11:04             ` Andrew Burgess
  2018-08-27 15:23               ` Eli Zaretskii
  1 sibling, 1 reply; 35+ messages in thread
From: Andrew Burgess @ 2018-08-27 11:04 UTC (permalink / raw)
  To: gdb-patches; +Cc: Philippe Waroquiers, Eli Zaretskii

Ping!

Eli:

  In this message:
      https://sourceware.org/ml/gdb-patches/2018-07/msg00670.html
  Philippe highlighted that you might have some reservations about
  this patch series, which I think is currently the main blocker for
  this patch getting approval.

  In the thread started here:
      https://sourceware.org/ml/gdb-patches/2018-05/msg00299.html
  and ending here:
      https://sourceware.org/ml/gdb-patches/2018-06/msg00142.html
  you did review and approve one of the original patch variants, which
  is most like the "level" variant of the patch submitted here:
      https://sourceware.org/ml/gdb-patches/2018-08/msg00337.html

  I would be really grateful if you could let me know your current
  thoughts on this patch, are you happy to have the "level" variant
  merged based on your previous approval, or has you position changed?

  Thanks.

Philippe:

  Thanks for the review and feedback in:
      https://sourceware.org/ml/gdb-patches/2018-08/msg00349.html

  I wasn't sure if the second part of you mail was suggesting that
  those instances of level vs number needed to be resolved as part of
  this patch for you to be happy, or if this was just identifying what
  we should clean up if/when this patch is merged.  Additional clarity
  here would help me figure out my next steps for getting this patch
  merged.

  Thanks.

Many thanks to everyone who's taken time to review this for me.

Thanks,
Andrew



* Andrew Burgess <andrew.burgess@embecosm.com> [2018-08-13 23:20:09 +0100]:

> Given a lack of feedback on v4, I've put together two alternative
> versions to pick between...
> 
>   (A) In this version I took onboard the feedback from Eli and
>       Philippe that using "level" might be confusing to users, as
>       "number" is historically what has been used for the integer
>       label we give to frames.
> 
>       The command for selecting a frame by integer is now, 'frame
>       number <NUMBER>' and almost all references to "level" have now
>       been removed from the patch.
> 
>   (B) In this version I've taken onboard the advice from Philippe,
>       identifying places in the patch where I was mixing use of
>       "level" and "number".  I've doubled down on "level" and removed
>       most uses of "number" from the patch.
> 
> If we select patch 'A' then me might want to update 'frame apply level
> ...' to 'frame apply number ...' for consistency.
> 
> If we select patch 'B' then we will probably need a follow up patch
> that goes through the documentation to tighten up references to
> "number" vs "level".
> 
> I really don't mind which approach we take, I guess I'd probably pick
> 'B' over 'A' given we already have 'frame apply level ...' in GDB, but
> if there's preference for 'A' then that's fine.
> 
> I'm also happy to do the follow up patches once we have some agreement
> on which way to go.
> 
> Thanks,
> Andrew
> 
> ---
> 
> Andrew Burgess (1):
>   gdb: Change how frames are selected for 'frame' and 'info frame'.
> 
>  gdb/ChangeLog                               |  36 ++
>  gdb/NEWS                                    |   8 +
>  gdb/cli/cli-decode.c                        |  44 ++-
>  gdb/command.h                               |  14 +
>  gdb/doc/ChangeLog                           |   8 +
>  gdb/doc/gdb.texinfo                         | 108 ++++--
>  gdb/mi/mi-cmd-stack.c                       |   4 +-
>  gdb/stack.c                                 | 535 +++++++++++++++++++---------
>  gdb/stack.h                                 |   2 +-
>  gdb/testsuite/ChangeLog                     |   7 +
>  gdb/testsuite/gdb.base/frame-selection.c    |  52 +++
>  gdb/testsuite/gdb.base/frame-selection.exp  | 157 ++++++++
>  gdb/testsuite/gdb.mi/mi-frame-selection.c   |  34 ++
>  gdb/testsuite/gdb.mi/mi-frame-selection.exp |  89 +++++
>  14 files changed, 896 insertions(+), 202 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.base/frame-selection.c
>  create mode 100644 gdb/testsuite/gdb.base/frame-selection.exp
>  create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.c
>  create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.exp
> 
> -- 
> 2.14.4
> 

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCHv5 0/2] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-08-27 11:04             ` Andrew Burgess
@ 2018-08-27 15:23               ` Eli Zaretskii
  2018-08-28  8:43                 ` Andrew Burgess
  0 siblings, 1 reply; 35+ messages in thread
From: Eli Zaretskii @ 2018-08-27 15:23 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches, philippe.waroquiers

> Date: Mon, 27 Aug 2018 12:03:54 +0100
> From: Andrew Burgess <andrew.burgess@embecosm.com>
> Cc: Philippe Waroquiers <philippe.waroquiers@skynet.be>,
> 	Eli Zaretskii <eliz@gnu.org>
> 
> Eli:
> 
>   In this message:
>       https://sourceware.org/ml/gdb-patches/2018-07/msg00670.html
>   Philippe highlighted that you might have some reservations about
>   this patch series, which I think is currently the main blocker for
>   this patch getting approval.
> 
>   In the thread started here:
>       https://sourceware.org/ml/gdb-patches/2018-05/msg00299.html
>   and ending here:
>       https://sourceware.org/ml/gdb-patches/2018-06/msg00142.html
>   you did review and approve one of the original patch variants, which
>   is most like the "level" variant of the patch submitted here:
>       https://sourceware.org/ml/gdb-patches/2018-08/msg00337.html
> 
>   I would be really grateful if you could let me know your current
>   thoughts on this patch, are you happy to have the "level" variant
>   merged based on your previous approval, or has you position changed?

I just said

  I question the wisdom of changing such veteran terminology.

And then:

  If we are going to make this change, then I would suggest to keep the
  index entry, _add_ to it an entry about "frame level", and explain
  here what that level is, something like this:

    @value{GDBN} labels each existing stack frame with a @dfn{level}, a
    number that is zero for the innermost frame, one for the frame that
    called it, and so on upward.  These level numbers give you a way of
    designating stack frames in @value{GDBN} commands.

IOW, I urged us to think whether we really want the change (and
invited others to comment).  Then I had a small suggestion for if we
do make the change.

That is all.  It was never my intention to block the patch, and if my
words were unfortunate enough to be interpreted that way, I apologize.

Thanks.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCHv5 0/2] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-08-27 15:23               ` Eli Zaretskii
@ 2018-08-28  8:43                 ` Andrew Burgess
  2018-08-28  9:08                   ` Eli Zaretskii
  0 siblings, 1 reply; 35+ messages in thread
From: Andrew Burgess @ 2018-08-28  8:43 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches, philippe.waroquiers

* Eli Zaretskii <eliz@gnu.org> [2018-08-27 18:23:37 +0300]:

> > Date: Mon, 27 Aug 2018 12:03:54 +0100
> > From: Andrew Burgess <andrew.burgess@embecosm.com>
> > Cc: Philippe Waroquiers <philippe.waroquiers@skynet.be>,
> > 	Eli Zaretskii <eliz@gnu.org>
> > 
> > Eli:
> > 
> >   In this message:
> >       https://sourceware.org/ml/gdb-patches/2018-07/msg00670.html
> >   Philippe highlighted that you might have some reservations about
> >   this patch series, which I think is currently the main blocker for
> >   this patch getting approval.
> > 
> >   In the thread started here:
> >       https://sourceware.org/ml/gdb-patches/2018-05/msg00299.html
> >   and ending here:
> >       https://sourceware.org/ml/gdb-patches/2018-06/msg00142.html
> >   you did review and approve one of the original patch variants, which
> >   is most like the "level" variant of the patch submitted here:
> >       https://sourceware.org/ml/gdb-patches/2018-08/msg00337.html
> > 
> >   I would be really grateful if you could let me know your current
> >   thoughts on this patch, are you happy to have the "level" variant
> >   merged based on your previous approval, or has you position changed?
> 
> I just said
> 
>   I question the wisdom of changing such veteran terminology.
> 
> And then:
> 
>   If we are going to make this change, then I would suggest to keep the
>   index entry, _add_ to it an entry about "frame level", and explain
>   here what that level is, something like this:
> 
>     @value{GDBN} labels each existing stack frame with a @dfn{level}, a
>     number that is zero for the innermost frame, one for the frame that
>     called it, and so on upward.  These level numbers give you a way of
>     designating stack frames in @value{GDBN} commands.
> 
> IOW, I urged us to think whether we really want the change (and
> invited others to comment).  Then I had a small suggestion for if we
> do make the change.
> 
> That is all.  It was never my intention to block the patch, and if my
> words were unfortunate enough to be interpreted that way, I apologize.

Thanks for your feedback.

I didn't mean you were intentionally blocking the patch, rather, there
was some uncertainty, and _that_ was blocking.

I understand your concerns about changing existing terminology, and I
agree that this is a real issue that should be considered.

I like your proposed new paragraph as it mentions both the word
'level' and the word 'number', and I wondered if we couldn't sidestep
the issue of which term is "correct", at least for a little while,
with something like this:

    @value{GDBN} labels each existing stack frame with a @dfn{level}, a
    number that is zero for the innermost frame, one for the frame that
    called it, and so on upward.  These level numbers give you a way of
    designating stack frames in @value{GDBN} commands.  The terms
    @dfn{frame number} and @dfn{frame level} can be used interchangably to
    describe this number.

I'd also propose we add both 'frame number' and 'frame level' to the
concept index (see patch below).  Do you think that if I added this
chunk to the existing level patch we would have something that was
clear enough to merge?

Thanks,
Andrew

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index a2e405d5dfc..694f2ca5e95 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -7393,12 +7393,14 @@
 in a register called the @dfn{frame pointer register}
 (@pxref{Registers, $fp}) while execution is going on in that frame.
 
+@cindex frame level
 @cindex frame number
-@value{GDBN} assigns numbers to all existing stack frames, starting with
-zero for the innermost frame, one for the frame that called it,
-and so on upward.  These numbers do not really exist in your program;
-they are assigned by @value{GDBN} to give you a way of designating stack
-frames in @value{GDBN} commands.
+@value{GDBN} labels each existing stack frame with a @dfn{level}, a
+number that is zero for the innermost frame, one for the frame that
+called it, and so on upward.  These level numbers give you a way of
+designating stack frames in @value{GDBN} commands.  The terms
+@dfn{frame number} and @dfn{frame level} can be used interchangably to
+describe this number.
 
 @c The -fomit-frame-pointer below perennially causes hbox overflow
 @c underflow problems.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCHv5 0/2] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-08-28  8:43                 ` Andrew Burgess
@ 2018-08-28  9:08                   ` Eli Zaretskii
  2018-08-28 18:03                     ` [PATCHv6] " Andrew Burgess
  0 siblings, 1 reply; 35+ messages in thread
From: Eli Zaretskii @ 2018-08-28  9:08 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches, philippe.waroquiers

> Date: Tue, 28 Aug 2018 09:43:32 +0100
> From: Andrew Burgess <andrew.burgess@embecosm.com>
> Cc: gdb-patches@sourceware.org, philippe.waroquiers@skynet.be
> 
> I like your proposed new paragraph as it mentions both the word
> 'level' and the word 'number', and I wondered if we couldn't sidestep
> the issue of which term is "correct", at least for a little while,
> with something like this:
> 
>     @value{GDBN} labels each existing stack frame with a @dfn{level}, a
>     number that is zero for the innermost frame, one for the frame that
>     called it, and so on upward.  These level numbers give you a way of
>     designating stack frames in @value{GDBN} commands.  The terms
>     @dfn{frame number} and @dfn{frame level} can be used interchangably to
>     describe this number.
> 
> I'd also propose we add both 'frame number' and 'frame level' to the
> concept index (see patch below).  Do you think that if I added this
> chunk to the existing level patch we would have something that was
> clear enough to merge?

Yes, thanks.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCHv6] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-08-28  9:08                   ` Eli Zaretskii
@ 2018-08-28 18:03                     ` Andrew Burgess
  2018-08-28 18:20                       ` Eli Zaretskii
                                         ` (2 more replies)
  0 siblings, 3 replies; 35+ messages in thread
From: Andrew Burgess @ 2018-08-28 18:03 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

I believe that this variant addresses all existing review comments.
Philippe provided additional feedback in this message:

   https://sourceware.org/ml/gdb-patches/2018-08/msg00349.html

but I believe this was more about things we need to do to unify the
docs/code/comments on one of either 'number' or 'level', rather than
things that need to be fixed before this patch could be merged.

This version of the patch is based off v5(B) and uses the keyword
'level' instead of 'number'.

The only changes over the previous version are an additional change in
the docs as previously discussed with Eli, and I've rebased the patch
onto current HEAD (and retested).

OK to commit?

Thanks,
Andrew

---

The 'frame' command, and thanks to code reuse the 'info frame' and
'select-frame' commands, currently have an overloaded mechanism for
selecting a frame.

These commands take one or two parameters, if it's one parameter then
we first try to use the parameter as an integer to select a frame by
level (or depth in the stack).  If that fails then we treat the
parameter as an address and try to select a stack frame by
stack-address.  If we still have not selected a stack frame, or we
initially had two parameters, then GDB allows the user to view a stack
frame that is not part of the current backtrace.  Internally, a new
frame is created with the given stack and pc addresses, and this is
shown to the user.

The result of this is that a typo by the user, entering the wrong stack
frame level for example, can result in a brand new frame being viewed
rather than an error.

The purpose of this commit is to remove this overloading, while still
offering the same functionality through some new sub-commands.  By
making the default behaviour of 'frame' (and friends) be to select a
stack frame by level index, it is hoped that enough
backwards-compatibility is maintained that users will not be overly
inconvenienced.

The 'frame', 'select-frame', and 'info frame' commands now all take a
frame specification string as an argument, this string can be any of the
following:

  (1) An integer.  This is treated as a frame level.  If a frame for
  that level does not exist then the user gets an error.

  (2) A string like 'level <LEVEL>', where <LEVEL> is a frame level
  as in option (1) above.

  (3) A string like 'address <STACK-ADDRESS>', where <STACK-ADDRESS>
  is a stack-frame address.  If there is no frame for this address
  then the user gets an error.

  (4) A string like 'function <NAME>', where <NAME> is a function name,
  the inner most frame for function <NAME> is selected.  If there is no
  frame for function <NAME> then the user gets an error.

  (5) A string like 'view <STACK-ADDRESS>', this views a new frame
  with stack address <STACK-ADDRESS>.

  (6) A string like 'view <STACK-ADDRESS> <PC-ADDRESS>', this views
  a new frame with stack address <STACK-ADDRESS> an the pc <PC-ADDRESS>.

This change assumes that the most common use of the commands like
'frame' is to select a frame by frame level, it is for this reason
that this is the behaviour that is kept for backwards compatibility.
Any of the alternative behaviours, which are assumed to be less used,
now require a change in user behaviour.

The MI command '-stack-select-frame' has also changed in the same way.
The default behaviour is to select a frame by level, but the other
selection methods are also available.

gdb/ChangeLog:

	(NEWS): Mention changes to frame related commands.
	* cli/cli-decode.c (add_cmd_suppress_notification): New function.
	(add_prefix_cmd_suppress_notification): New function.
	(add_com_suppress_notification): Call
	add_cmd_suppress_notification.
	* command.h (add_cmd_suppress_notification): Declare.
	(add_prefix_cmd_suppress_notification): Declare.
	* mi/mi-cmd-stack.c (mi_cmd_stack_select_frame): Use new function
	select_frame_from_spec.
	* stack.c (find_frame_for_function): Add declaration.
	(find_frame_for_address): New function.
	(parse_frame_specification): Delete function.
	(frame_selection_by_function_completer): New function.
	(info_frame_command): Rename to...
	(info_frame_command_core): ...this, and update parameter types.
	(select_frame_command): Rename to...
	(select_frame_command_core): ...this, and update parameter types.
	(frame_command): Rename to...
	(frame_command_core): ...this, and update parameter types.
	(class frame_command_helper): New class to wrap implementations of
	frame related sub-commands.
	(select_frame_from_spec): New function.
	(frame_apply_cmd_list): New static global.
	(frame_cmd_list): Make static.
	(select_frame_cmd_list): New global for sub-commands.
	(info_frame_cmd_list): New global for sub-commands.
	(_initialize_stack): Register sub-commands for 'frame',
	'select-frame', and 'info frame'.  Update 'frame apply' commands
	to use frame_apply_cmd_list.  Move function local static
	frame_apply_list to file static frame_apply_cmd_list for
	consistency.
	* stack.h (select_frame_command): Delete declarationn.
	(select_frame_from_spec): Declare new function.

gdb/doc/ChangeLog:

	* gdb.texinfo (Frames): Rewrite the description of 'frame number'
	to highlight that the number is also the frame's level.
	(Selection): Rewrite documentation for 'frame' and 'select-frame'
	commands.
	(Frame Info): Rewrite documentation for 'info frame' command.
	(GDB/MI Stack Manipulation): Update description of
	'-stack-select-frame'.

gdb/testsuite/ChangeLog:

	* gdb.base/frame-selection.exp: New file.
	* gdb.base/frame-selection.c: New file.
	* gdb.mi/mi-frame-selection.exp: New file.
	* gdb.mi/mi-frame-selection.c: New file.
---
 gdb/ChangeLog                               |  36 ++
 gdb/NEWS                                    |   8 +
 gdb/cli/cli-decode.c                        |  44 ++-
 gdb/command.h                               |  14 +
 gdb/doc/ChangeLog                           |  10 +
 gdb/doc/gdb.texinfo                         | 118 ++++--
 gdb/mi/mi-cmd-stack.c                       |   4 +-
 gdb/stack.c                                 | 535 +++++++++++++++++++---------
 gdb/stack.h                                 |   2 +-
 gdb/testsuite/ChangeLog                     |   7 +
 gdb/testsuite/gdb.base/frame-selection.c    |  52 +++
 gdb/testsuite/gdb.base/frame-selection.exp  | 156 ++++++++
 gdb/testsuite/gdb.mi/mi-frame-selection.c   |  34 ++
 gdb/testsuite/gdb.mi/mi-frame-selection.exp |  89 +++++
 14 files changed, 903 insertions(+), 206 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/frame-selection.c
 create mode 100644 gdb/testsuite/gdb.base/frame-selection.exp
 create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.c
 create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index 45514b10c51..b0b2d93939e 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -10,6 +10,14 @@
 * DWARF index cache: GDB can now automatically save indices of DWARF
   symbols on disk to speed up further loading of the same binaries.
 
+* Changes to the "frame", "select-frame", and "info frame" CLI
+  commands, as well as the "-stack-select-frame" MI command.  These
+  commands all now take a frame specification which is either a frame
+  level, or one of the keywords 'level', 'address', 'function', or
+  'view' followed by a parameter.  Selecting a frame by address, or
+  viewing a frame outside the current backtrace now requires the use
+  of a keyword.  Selecting a frame by level is unchanged.
+
 * New commands
 
 frame apply [all | COUNT | -COUNT | level LEVEL...] [FLAG]... COMMAND
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 83dd67efe3f..5d798e877e8 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -251,6 +251,23 @@ add_cmd (const char *name, enum command_class theclass,
   return result;
 }
 
+/* Add an element with a suppress notification to the LIST of commands.  */
+
+struct cmd_list_element *
+add_cmd_suppress_notification (const char *name, enum command_class theclass,
+			       cmd_const_cfunc_ftype *fun, const char *doc,
+			       struct cmd_list_element **list,
+			       int *suppress_notification)
+{
+  struct cmd_list_element *element;
+
+  element = add_cmd (name, theclass, fun, doc, list);
+  element->suppress_notification = suppress_notification;
+
+  return element;
+}
+
+
 /* Deprecates a command CMD.
    REPLACEMENT is the name of the command which should be used in
    place of this command, or NULL if no such command exists.
@@ -362,6 +379,25 @@ add_prefix_cmd (const char *name, enum command_class theclass,
   return c;
 }
 
+/* Like ADD_PREFIX_CMD but sets the suppress_notification pointer on the
+   new command list element.  */
+
+struct cmd_list_element *
+add_prefix_cmd_suppress_notification
+               (const char *name, enum command_class theclass,
+		cmd_const_cfunc_ftype *fun,
+		const char *doc, struct cmd_list_element **prefixlist,
+		const char *prefixname, int allow_unknown,
+		struct cmd_list_element **list,
+		int *suppress_notification)
+{
+  struct cmd_list_element *element
+    = add_prefix_cmd (name, theclass, fun, doc, prefixlist,
+		      prefixname, allow_unknown, list);
+  element->suppress_notification = suppress_notification;
+  return element;
+}
+
 /* Like add_prefix_cmd but sets the abbrev_flag on the new command.  */
 
 struct cmd_list_element *
@@ -893,12 +929,8 @@ add_com_suppress_notification (const char *name, enum command_class theclass,
 			       cmd_const_cfunc_ftype *fun, const char *doc,
 			       int *suppress_notification)
 {
-  struct cmd_list_element *element;
-
-  element = add_cmd (name, theclass, fun, doc, &cmdlist);
-  element->suppress_notification = suppress_notification;
-
-  return element;
+  return add_cmd_suppress_notification (name, theclass, fun, doc,
+					&cmdlist, suppress_notification);
 }
 
 /* Recursively walk the commandlist structures, and print out the
diff --git a/gdb/command.h b/gdb/command.h
index 3dde2475cb1..e3d55c2dcba 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -148,6 +148,12 @@ extern struct cmd_list_element *add_cmd (const char *, enum command_class,
 					 const char *,
 					 struct cmd_list_element **);
 
+extern struct cmd_list_element *add_cmd_suppress_notification
+			(const char *name, enum command_class theclass,
+			 cmd_const_cfunc_ftype *fun, const char *doc,
+			 struct cmd_list_element **list,
+			 int *suppress_notification);
+
 extern struct cmd_list_element *add_alias_cmd (const char *, const char *,
 					       enum command_class, int,
 					       struct cmd_list_element **);
@@ -165,6 +171,14 @@ extern struct cmd_list_element *add_prefix_cmd (const char *, enum command_class
 						const char *, int,
 						struct cmd_list_element **);
 
+extern struct cmd_list_element *add_prefix_cmd_suppress_notification
+			(const char *name, enum command_class theclass,
+			 cmd_const_cfunc_ftype *fun,
+			 const char *doc, struct cmd_list_element **prefixlist,
+			 const char *prefixname, int allow_unknown,
+			 struct cmd_list_element **list,
+			 int *suppress_notification);
+
 extern struct cmd_list_element *add_abbrev_prefix_cmd (const char *,
 						       enum command_class,
 						       cmd_const_cfunc_ftype *fun,
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 5068c0ac81f..1cafea97976 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -7393,12 +7393,14 @@
 in a register called the @dfn{frame pointer register}
 (@pxref{Registers, $fp}) while execution is going on in that frame.
 
+@cindex frame level
 @cindex frame number
-@value{GDBN} assigns numbers to all existing stack frames, starting with
-zero for the innermost frame, one for the frame that called it,
-and so on upward.  These numbers do not really exist in your program;
-they are assigned by @value{GDBN} to give you a way of designating stack
-frames in @value{GDBN} commands.
+@value{GDBN} labels each existing stack frame with a @dfn{level}, a
+number that is zero for the innermost frame, one for the frame that
+called it, and so on upward.  These level numbers give you a way of
+designating stack frames in @value{GDBN} commands.  The terms
+@dfn{frame number} and @dfn{frame level} can be used interchangably to
+describe this number.
 
 @c The -fomit-frame-pointer below perennially causes hbox overflow
 @c underflow problems.
@@ -7631,21 +7633,75 @@
 @table @code
 @kindex frame@r{, selecting}
 @kindex f @r{(@code{frame})}
-@item frame @var{n}
-@itemx f @var{n}
-Select frame number @var{n}.  Recall that frame zero is the innermost
+@item frame @r{[} @var{frame-selection-spec} @r{]}
+@item f @r{[} @var{frame-selection-spec} @r{]}
+The @command{frame} command allows different stack frames to be
+selected.  The @var{frame-selection-spec} can be any of the following:
+
+@table @code
+@kindex frame level
+@item @var{num}
+@item level @var{num}
+Select frame level @var{num}.  Recall that frame zero is the innermost
 (currently executing) frame, frame one is the frame that called the
-innermost one, and so on.  The highest-numbered frame is the one for
-@code{main}.
+innermost one, and so on.  The highest level frame is usually the one
+for @code{main}.
+
+As this is the most common method of navigating the frame stack then
+the string @command{level} can be dropped, the following two commands
+are equivalent:
 
-@item frame @var{stack-addr} [ @var{pc-addr} ]
-@itemx f @var{stack-addr} [ @var{pc-addr} ]
-Select the frame at address @var{stack-addr}.  This is useful mainly if the
-chaining of stack frames has been damaged by a bug, making it
-impossible for @value{GDBN} to assign numbers properly to all frames.  In
-addition, this can be useful when your program has multiple stacks and
-switches between them.  The optional @var{pc-addr} can also be given to
-specify the value of PC for the stack frame.
+@smallexample
+(@value{GDBP}) frame 3
+(@value{GDBP}) frame level 3
+@end smallexample
+
+@kindex frame address
+@item address @var{stack-address}
+Select the frame with stack address @var{stack-address}.  The
+@var{stack-address} for a frame can be seen in the output of
+@command{info frame}, for example:
+
+@smallexample
+(gdb) info frame
+Stack level 1, frame at 0x7fffffffda30:
+ rip = 0x40066d in b (amd64-entry-value.cc:59); saved rip 0x4004c5
+ tail call frame, caller of frame at 0x7fffffffda30
+ source language c++.
+ Arglist at unknown address.
+ Locals at unknown address, Previous frame's sp is 0x7fffffffda30
+@end smallexample
+
+The @var{stack-address} for this frame is @code{0x7fffffffda30} as
+indicated by the line:
+
+@smallexample
+Stack level 1, frame at 0x7fffffffda30:
+@end smallexample
+
+@kindex frame function
+@item function @var{function-name}
+Select the stack frame for function @var{function-name}.  If there are
+multiple stack frames for function @var{function-name} then the inner
+most stack frame is selected.
+
+@kindex frame view
+@item view @var{stack-address} @r{[} @var{pc-addr} @r{]}
+View a frame that is not part of @value{GDBN}'s backtrace.  The frame
+viewed has stack address @var{stack-addr}, and optionally, a program
+counter address of @var{pc-addr}.
+
+This is useful mainly if the chaining of stack frames has been
+damaged by a bug, making it impossible for @value{GDBN} to assign
+numbers properly to all frames.  In addition, this can be useful
+when your program has multiple stacks and switches between them.
+
+When viewing a frame outside the current backtrace using
+@command{frame view} then you can always return to the original
+stack using one of the previous stack frame selection instructions,
+for example @command{frame level 0}.
+
+@end table
 
 @kindex up
 @item up @var{n}
@@ -7688,11 +7744,13 @@
 
 @table @code
 @kindex select-frame
-@item select-frame
+@item select-frame @r{[} @var{frame-selection-spec} @r{]}
 The @code{select-frame} command is a variant of @code{frame} that does
 not display the new frame after selecting it.  This command is
 intended primarily for use in @value{GDBN} command scripts, where the
-output might be unnecessary and distracting.
+output might be unnecessary and distracting.  The
+@var{frame-selection-spec} is as for the @command{frame} command
+described in @ref{Selection, ,Selecting a Frame}.
 
 @kindex down-silently
 @kindex up-silently
@@ -7750,13 +7808,12 @@
 something has gone wrong that has made the stack format fail to fit
 the usual conventions.
 
-@item info frame @var{addr}
-@itemx info f @var{addr}
-Print a verbose description of the frame at address @var{addr}, without
-selecting that frame.  The selected frame remains unchanged by this
-command.  This requires the same kind of address (more than one for some
-architectures) that you specify in the @code{frame} command.
-@xref{Selection, ,Selecting a Frame}.
+@item info frame @r{[} @var{frame-selection-spec} @r{]}
+@itemx info f @r{[} @var{frame-selection-spec} @r{]}
+Print a verbose description of the frame selected by
+@var{frame-selection-spec}.  The @var{frame-selection-spec} is the
+same as for the @command{frame} command (@pxref{Selection, ,Selecting
+a Frame}).  The selected frame remains unchanged by this command.
 
 @kindex info args
 @item info args
@@ -30329,11 +30386,12 @@
 @subsubheading Synopsis
 
 @smallexample
- -stack-select-frame @var{framenum}
+ -stack-select-frame @var{frame-selection-spec}
 @end smallexample
 
-Change the selected frame.  Select a different frame @var{framenum} on
-the stack.
+Change the selected frame, the @var{frame-selection-spec} is as for
+the @command{frame} command described in @ref{Selection, ,Selecting a
+Frame}.
 
 This command in deprecated in favor of passing the @samp{--frame}
 option to every command.
diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c
index 52660bdd498..33ac7663ba8 100644
--- a/gdb/mi/mi-cmd-stack.c
+++ b/gdb/mi/mi-cmd-stack.c
@@ -681,10 +681,8 @@ list_args_or_locals (enum what_to_list what, enum print_values values,
 void
 mi_cmd_stack_select_frame (const char *command, char **argv, int argc)
 {
-  if (argc == 0 || argc > 1)
+  if (!select_frame_from_spec (argv, argc, 0))
     error (_("-stack-select-frame: Usage: FRAME_SPEC"));
-
-  select_frame_command (argv[0], 1 /* not used */ );
 }
 
 void
diff --git a/gdb/stack.c b/gdb/stack.c
index 40ff99b8fa6..68029f56412 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -102,6 +102,9 @@ static void set_last_displayed_sal (int valid,
 				    struct symtab *symtab,
 				    int line);
 
+static struct frame_info *find_frame_for_function (const char *);
+static struct frame_info *find_frame_for_address (CORE_ADDR);
+
 /* Zero means do things normally; we are interacting directly with the
    user.  One means print the full filename and linenumber when a
    frame is printed, and do so in a format emacs18/emacs19.22 can
@@ -1258,134 +1261,34 @@ print_frame (struct frame_info *frame, int print_level,
 }
 \f
 
-/* Read a frame specification in whatever the appropriate format is from
-   FRAME_EXP.  Call error() if the specification is in any way invalid (so
-   this function never returns NULL).  When SELECTED_FRAME_P is non-NULL
-   set its target to indicate that the default selected frame was used.  */
+/* Completion function for "frame function", "info frame function", and
+   "select-frame function" commands.  */
 
-static struct frame_info *
-parse_frame_specification (const char *frame_exp, int *selected_frame_p)
+void
+frame_selection_by_function_completer (struct cmd_list_element *ignore,
+				       completion_tracker &tracker,
+				       const char *text, const char *word)
 {
-  int numargs;
-  struct value *args[4];
-  CORE_ADDR addrs[ARRAY_SIZE (args)];
-
-  if (frame_exp == NULL)
-    numargs = 0;
-  else
-    {
-      numargs = 0;
-      while (1)
-	{
-	  const char *p;
-
-	  /* Skip leading white space, bail of EOL.  */
-	  frame_exp = skip_spaces (frame_exp);
-	  if (!*frame_exp)
-	    break;
-
-	  /* Parse the argument, extract it, save it.  */
-	  for (p = frame_exp;
-	       *p && !ISSPACE (*p);
-	       p++);
-	  std::string addr_string (frame_exp, p - frame_exp);
-	  frame_exp = p;
-	  
-	  /* NOTE: Parse and evaluate expression, but do not use
-	     functions such as parse_and_eval_long or
-	     parse_and_eval_address to also extract the value.
-	     Instead value_as_long and value_as_address are used.
-	     This avoids problems with expressions that contain
-	     side-effects.  */
-	  if (numargs >= ARRAY_SIZE (args))
-	    error (_("Too many args in frame specification"));
-	  args[numargs++] = parse_and_eval (addr_string.c_str ());
-	}
-    }
-
-  /* If no args, default to the selected frame.  */
-  if (numargs == 0)
-    {
-      if (selected_frame_p != NULL)
-	(*selected_frame_p) = 1;
-      return get_selected_frame (_("No stack."));
-    }
-
-  /* None of the remaining use the selected frame.  */
-  if (selected_frame_p != NULL)
-    (*selected_frame_p) = 0;
-
-  /* Assume the single arg[0] is an integer, and try using that to
-     select a frame relative to current.  */
-  if (numargs == 1)
-    {
-      struct frame_info *fid;
-      int level = value_as_long (args[0]);
-
-      fid = find_relative_frame (get_current_frame (), &level);
-      if (level == 0)
-	/* find_relative_frame was successful.  */
-	return fid;
-    }
-
-  /* Convert each value into a corresponding address.  */
-  {
-    int i;
-
-    for (i = 0; i < numargs; i++)
-      addrs[i] = value_as_address (args[i]);
-  }
-
-  /* Assume that the single arg[0] is an address, use that to identify
-     a frame with a matching ID.  Should this also accept stack/pc or
-     stack/pc/special.  */
-  if (numargs == 1)
-    {
-      struct frame_id id = frame_id_build_wild (addrs[0]);
-      struct frame_info *fid;
-
-      /* If (s)he specifies the frame with an address, he deserves
-	 what (s)he gets.  Still, give the highest one that matches.
-	 (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't
-	 know).  */
-      for (fid = get_current_frame ();
-	   fid != NULL;
-	   fid = get_prev_frame (fid))
-	{
-	  if (frame_id_eq (id, get_frame_id (fid)))
-	    {
-	      struct frame_info *prev_frame;
-
-	      while (1)
-		{
-		  prev_frame = get_prev_frame (fid);
-		  if (!prev_frame
-		      || !frame_id_eq (id, get_frame_id (prev_frame)))
-		    break;
-		  fid = prev_frame;
-		}
-	      return fid;
-	    }
-	}
-      }
-
-  /* We couldn't identify the frame as an existing frame, but
-     perhaps we can create one with a single argument.  */
-  if (numargs == 1)
-    return create_new_frame (addrs[0], 0);
-  else if (numargs == 2)
-    return create_new_frame (addrs[0], addrs[1]);
-  else
-    error (_("Too many args in frame specification"));
+  /* This is used to complete function names within a stack.  It would be
+     nice if instead of offering all available function names, we only
+     offered functions that were actually in the stack.  However, this
+     would probably mean unwinding the stack to completion, which could
+     take too long (or on a corrupted stack, possibly not end).  For now I
+     offer all symbol names as a safer choice.  */
+  collect_symbol_completion_matches (tracker,
+				     complete_symbol_mode::EXPRESSION,
+				     symbol_name_match_type::EXPRESSION,
+				     text, word);
 }
 
-/* Print verbosely the selected frame or the frame at address
-   ADDR_EXP.  Absolutely all information in the frame is printed.  */
+/* Core of all the "info frame" sub-commands.  Print verbosely the frame FI
+   if SELECTED_FRAME_P is true then frame FI is the current frame, which
+   was selected as a default due to the user not providing any arguments
+   to select some other frame.  */
 
 static void
-info_frame_command (const char *addr_exp, int from_tty)
+info_frame_command_core (struct frame_info *fi, bool selected_frame_p)
 {
-  struct frame_info *fi;
   struct symbol *func;
   struct symtab *s;
   struct frame_info *calling_frame_info;
@@ -1393,7 +1296,6 @@ info_frame_command (const char *addr_exp, int from_tty)
   const char *funname = 0;
   enum language funlang = language_unknown;
   const char *pc_regname;
-  int selected_frame_p;
   struct gdbarch *gdbarch;
   CORE_ADDR frame_pc;
   int frame_pc_p;
@@ -1401,7 +1303,6 @@ info_frame_command (const char *addr_exp, int from_tty)
   CORE_ADDR caller_pc = 0;
   int caller_pc_p = 0;
 
-  fi = parse_frame_specification (addr_exp, &selected_frame_p);
   gdbarch = get_frame_arch (fi);
 
   /* Name of the value returned by get_frame_pc().  Per comments, "pc"
@@ -1742,6 +1643,140 @@ trailing_outermost_frame (int count)
   return trailing;
 }
 
+/* The core of all the "select-frame" sub-commands.  Just wraps a call to
+   SELECT_FRAME.  */
+
+static void
+select_frame_command_core (struct frame_info *fi, bool ignored)
+{
+  struct frame_info *prev_frame = get_selected_frame_if_set ();
+  select_frame (fi);
+  if (get_selected_frame_if_set () != prev_frame)
+    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
+}
+
+/* The core of all the "frame" sub-commands.  Select frame FI, and if this
+   means we change frame send out a change notification (otherwise, just
+   reprint the current frame summary).   */
+
+static void
+frame_command_core (struct frame_info *fi, bool ignored)
+{
+  struct frame_info *prev_frame = get_selected_frame_if_set ();
+
+  select_frame (fi);
+  if (get_selected_frame_if_set () != prev_frame)
+    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
+  else
+    print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME);
+}
+
+/* The three commands 'frame', 'select-frame', and 'info frame' all have a
+   common set of sub-commands that allow a specific frame to be selected.
+   All of the sub-command functions are static methods within this class
+   template which is then instantiated below.  */
+
+template <void (*FPTR) (struct frame_info *, bool)>
+class frame_command_helper
+{
+public:
+
+  /* The "frame level" family of commands.  The ARG is an integer that is
+     the frame's level in the stack.  */
+  static void
+  level (const char *arg, int from_tty)
+  {
+    int level = value_as_long (parse_and_eval (arg));
+    struct frame_info *fid
+      = find_relative_frame (get_current_frame (), &level);
+    if (level != 0)
+      error (_("No frame at level %s."), arg);
+    FPTR (fid, false);
+  }
+
+  /* The "frame address" family of commands.  ARG is a stack-pointer
+     address for an existing frame.  This command does not allow new
+     frames to be created.  */
+
+  static void
+  address (const char *arg, int from_tty)
+  {
+    CORE_ADDR addr = value_as_address (parse_and_eval (arg));
+    struct frame_info *fid = find_frame_for_address (addr);
+    if (fid == NULL)
+      error (_("No frame at address %s."), arg);
+    FPTR (fid, false);
+  }
+
+  /* The "frame view" family of commands.  ARG is one or two addresses and
+     is used to view a frame that might be outside the current backtrace.
+     The addresses are stack-pointer address, and (optional) pc-address.  */
+
+  static void
+  view (const char *args, int from_tty)
+  {
+    struct frame_info *fid;
+
+    if (args == NULL)
+    error (_("Missing address argument to view a frame"));
+
+    gdb_argv argv (args);
+
+    if (argv.count () == 2)
+      {
+	CORE_ADDR addr[2];
+
+	addr [0] = value_as_address (parse_and_eval (argv[0]));
+	addr [1] = value_as_address (parse_and_eval (argv[1]));
+	fid = create_new_frame (addr[0], addr[1]);
+      }
+    else
+      {
+	CORE_ADDR addr = value_as_address (parse_and_eval (argv[0]));
+	fid = create_new_frame (addr, false);
+      }
+    FPTR (fid, false);
+  }
+
+  /* The "frame function" family of commands.  ARG is the name of a
+     function within the stack, the first function (searching from frame
+     0) with that name will be selected.  */
+
+  static void
+  function (const char *arg, int from_tty)
+  {
+    if (arg == NULL)
+      error (_("Missing function name argument"));
+    struct frame_info *fid = find_frame_for_function (arg);
+    if (fid == NULL)
+      error (_("No frame for function \"%s\"."), arg);
+    FPTR (fid, false);
+  }
+
+  /* The "frame" base command, that is, when no sub-command is specified.
+     If one argument is provided then we assume that this is a frame's
+     level as historically, this was the supported command syntax that was
+     used most often.
+
+     If no argument is provided, then the current frame is selected.  */
+
+  static void
+  base_command (const char *arg, int from_tty)
+  {
+    if (arg == NULL)
+      FPTR (get_selected_frame (_("No stack.")), true);
+    else
+      level (arg, from_tty);
+  }
+};
+
+/* Instantiate three FRAME_COMMAND_HELPER instances to implement the
+   sub-commands for 'info frame', 'frame', and 'select-frame' commands.  */
+
+static frame_command_helper <info_frame_command_core> info_frame_cmd;
+static frame_command_helper <frame_command_core> frame_cmd;
+static frame_command_helper <select_frame_command_core> select_frame_cmd;
+
 /* Print briefly all stack frames or just the innermost COUNT_EXP
    frames.  */
 
@@ -2230,37 +2265,43 @@ find_relative_frame (struct frame_info *frame, int *level_offset_ptr)
   return frame;
 }
 
-/* The "select_frame" command.  With no argument this is a NOP.
-   Select the frame at level LEVEL_EXP if it is a valid level.
-   Otherwise, treat LEVEL_EXP as an address expression and select it.
-
-   See parse_frame_specification for more info on proper frame
-   expressions.  */
-
-void
-select_frame_command (const char *level_exp, int from_tty)
-{
-  struct frame_info *prev_frame = get_selected_frame_if_set ();
-
-  select_frame (parse_frame_specification (level_exp, NULL));
-  if (get_selected_frame_if_set () != prev_frame)
-    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
-}
-
-/* The "frame" command.  With no argument, print the selected frame
-   briefly.  With an argument, behave like select_frame and then print
-   the selected frame.  */
+/* Select a frame using ARGV.  This is used from the MI -stack-select-frame
+   to provide the same frame specification mechanism that the CLI has for
+   commands like 'frame'.  The return value is true if the contents of
+   ARGV looked like a sensible attempt to change the frame (an error might
+   still be thrown though), or false if the contents of ARGV are not a
+   correct frame specification.  */
 
-static void
-frame_command (const char *level_exp, int from_tty)
+bool
+select_frame_from_spec (char **argv, int argc, int from_tty)
 {
-  struct frame_info *prev_frame = get_selected_frame_if_set ();
+  if (argc == 1)
+    select_frame_cmd.level (argv[0], from_tty);
+  else if (argc == 2
+	   && strncasecmp ("level", argv[0], strlen (argv[0])) == 0)
+    select_frame_cmd.level (argv[1], from_tty);
+  else if (argc == 2
+	   && strncasecmp ("address", argv[0], strlen (argv[0])) == 0)
+    select_frame_cmd.address (argv[1], from_tty);
+  else if (argc == 2
+	   && strncasecmp ("function", argv[0], strlen (argv[0])) == 0)
+    select_frame_cmd.function (argv[1], from_tty);
+  else if ((argc == 2 || argc == 3)
+	   && strncasecmp ("view", argv[0], strlen (argv[0])) == 0)
+    {
+      std::string arg;
+
+      if (argc == 2)
+	arg = string_printf ("%s", argv[1]);
+      else
+	arg = string_printf ("%s %s", argv[1], argv[2]);
 
-  select_frame (parse_frame_specification (level_exp, NULL));
-  if (get_selected_frame_if_set () != prev_frame)
-    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
+      select_frame_cmd.view (arg.c_str (), from_tty);
+    }
   else
-    print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME);
+    return false;
+
+  return true;
 }
 
 /* Select the frame up one or COUNT_EXP stack levels from the
@@ -2716,13 +2757,61 @@ faas_command (const char *cmd, int from_tty)
 }
 
 
+/* Find inner-mode frame with frame address ADDRESS.  Return NULL if no
+   matching frame can be found.  */
+
+static struct frame_info *
+find_frame_for_address (CORE_ADDR address)
+{
+  struct frame_id id;
+  struct frame_info *fid;
+
+  id = frame_id_build_wild (address);
+
+  /* If (s)he specifies the frame with an address, he deserves
+     what (s)he gets.  Still, give the highest one that matches.
+     (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't
+     know).  */
+  for (fid = get_current_frame ();
+       fid != NULL;
+       fid = get_prev_frame (fid))
+    {
+      if (frame_id_eq (id, get_frame_id (fid)))
+	{
+	  struct frame_info *prev_frame;
+
+	  while (1)
+	    {
+	      prev_frame = get_prev_frame (fid);
+	      if (!prev_frame
+		  || !frame_id_eq (id, get_frame_id (prev_frame)))
+		break;
+	      fid = prev_frame;
+	    }
+	  return fid;
+	}
+    }
+  return NULL;
+}
+
+\f
+
+/* Commands with a prefix of `frame apply'.  */
+static struct cmd_list_element *frame_apply_cmd_list = NULL;
+
 /* Commands with a prefix of `frame'.  */
-struct cmd_list_element *frame_cmd_list = NULL;
+static struct cmd_list_element *frame_cmd_list = NULL;
+
+/* Commands with a prefix of `select frame'.  */
+static struct cmd_list_element *select_frame_cmd_list = NULL;
+
+/* Commands with a prefix of `info frame'.  */
+static struct cmd_list_element *info_frame_cmd_list = NULL;
 
 void
 _initialize_stack (void)
 {
-  static struct cmd_list_element *frame_apply_list = NULL;
+  struct cmd_list_element *cmd;
 
   add_com ("return", class_stack, return_command, _("\
 Make selected stack frame return to its caller.\n\
@@ -2746,12 +2835,12 @@ An argument says how many frames down to go."));
 Same as the `down' command, but does not print anything.\n\
 This is useful in command scripts."));
 
-  add_prefix_cmd ("frame", class_stack, frame_command, _("\
-Select and print a stack frame.\nWith no argument, \
-print the selected stack frame.  (See also \"info frame\").\n\
-An argument specifies the frame to select.\n\
-It can be a stack frame number or the address of the frame."),
-		  &frame_cmd_list, "frame ", 1, &cmdlist);
+  add_prefix_cmd ("frame", class_stack,
+                  &frame_cmd.base_command, _("\
+Select and print a stack frame.\n\
+With no argument, print the selected stack frame.  (See also \"info frame\").\n\
+A single numerical argument specifies the frame to select."),
+                  &frame_cmd_list, "frame ", 1, &cmdlist);
 
   add_com_alias ("f", "frame", class_stack, 1);
 
@@ -2769,7 +2858,7 @@ or produces no output."
 Usage: frame apply COUNT [FLAG]... COMMAND\n\
 With a negative COUNT argument, applies the command on outermost -COUNT frames.\n"
 FRAME_APPLY_FLAGS_HELP),
-		  &frame_apply_list, "frame apply ", 1, &frame_cmd_list);
+		  &frame_apply_cmd_list, "frame apply ", 1, &frame_cmd_list);
 
   add_cmd ("all", class_stack, frame_apply_all_command,
 	   _("\
@@ -2777,7 +2866,7 @@ Apply a command to all frames.\n\
 \n\
 Usage: frame apply all [FLAG]... COMMAND\n"
 FRAME_APPLY_FLAGS_HELP),
-	   &frame_apply_list);
+	   &frame_apply_cmd_list);
 
   add_cmd ("level", class_stack, frame_apply_level_command,
 	   _("\
@@ -2786,19 +2875,97 @@ Apply a command to a list of frames.\n\
 Usage: frame apply level LEVEL... [FLAG]... COMMAND\n\
 ID is a space-separated list of LEVELs of frames to apply COMMAND on.\n"
 FRAME_APPLY_FLAGS_HELP),
-	   &frame_apply_list);
+	   &frame_apply_cmd_list);
 
   add_com ("faas", class_stack, faas_command, _("\
 Apply a command to all frames (ignoring errors and empty output).\n\
 Usage: faas COMMAND\n\
 shortcut for 'frame apply all -s COMMAND'"));
 
-  add_com_suppress_notification ("select-frame", class_stack, select_frame_command, _("\
+
+  add_prefix_cmd ("frame", class_stack,
+		  &frame_cmd.base_command, _("\
+Select and print a stack frame.\n\
+With no argument, print the selected stack frame.  (See also \"info frame\").\n\
+A single numerical argument specifies the frame to select."),
+		  &frame_cmd_list, "frame ", 1, &cmdlist);
+  add_com_alias ("f", "frame", class_stack, 1);
+
+  add_cmd ("address", class_stack, &frame_cmd.address,
+	   _("\
+Select and print a stack frame by stack address\n\
+\n\
+Usage: frame address STACK-ADDRESS"),
+	   &frame_cmd_list);
+
+  add_cmd ("view", class_stack, &frame_cmd.view,
+	   _("\
+View a stack frame that might be outside the current backtrace.\n\
+\n\
+Usage: frame view STACK-ADDRESS\n\
+       frame view STACK-ADDRESS PC-ADDRESS"),
+	   &frame_cmd_list);
+
+  cmd = add_cmd ("function", class_stack, &frame_cmd.function,
+	   _("\
+Select and print a stack frame by function name.\n\
+\n\
+Usage: frame function NAME\n\
+\n\
+The innermost frame that visited function NAME is selected."),
+	   &frame_cmd_list);
+  set_cmd_completer (cmd, frame_selection_by_function_completer);
+
+
+  add_cmd ("level", class_stack, &frame_cmd.level,
+	   _("\
+Select and print a stack frame by level.\n\
+\n\
+Usage: frame level LEVEL"),
+	   &frame_cmd_list);
+
+  cmd = add_prefix_cmd_suppress_notification ("select-frame", class_stack,
+		      &select_frame_cmd.base_command, _("\
 Select a stack frame without printing anything.\n\
-An argument specifies the frame to select.\n\
-It can be a stack frame number or the address of the frame."),
+A single numerical argument specifies the frame to select."),
+		      &select_frame_cmd_list, "select-frame ", 1, &cmdlist,
+		      &cli_suppress_notification.user_selected_context);
+
+  add_cmd_suppress_notification ("address", class_stack,
+			 &select_frame_cmd.address, _("\
+Select a stack frame by stack address.\n\
+\n\
+Usage: select-frame address STACK-ADDRESS"),
+			 &select_frame_cmd_list,
+			 &cli_suppress_notification.user_selected_context);
+
+
+  add_cmd_suppress_notification ("view", class_stack,
+		 &select_frame_cmd.view, _("\
+Select a stack frame that might be outside the current backtrace.\n\
+\n\
+Usage: select-frame view STACK-ADDRESS\n\
+       select-frame view STACK-ADDRESS PC-ADDRESS"),
+		 &select_frame_cmd_list,
 		 &cli_suppress_notification.user_selected_context);
 
+  cmd = add_cmd_suppress_notification ("function", class_stack,
+	       &select_frame_cmd.function, _("\
+Select a stack frame by function name.\n\
+\n\
+Usage: select-frame function NAME"),
+	       &select_frame_cmd_list,
+	       &cli_suppress_notification.user_selected_context);
+  set_cmd_completer (cmd, frame_selection_by_function_completer);
+
+  add_cmd_suppress_notification ("level", class_stack,
+			 &select_frame_cmd.level, _("\
+Select a stack frame by level.\n\
+\n\
+Usage: select-frame level LEVEL"),
+			 &select_frame_cmd_list,
+			 &cli_suppress_notification.user_selected_context);
+
   add_com ("backtrace", class_stack, backtrace_command, _("\
 Print backtrace of all stack frames, or innermost COUNT frames.\n\
 Usage: backtrace [QUALIFIERS]... [COUNT]\n\
@@ -2812,9 +2979,45 @@ on this backtrace."));
   add_info ("stack", backtrace_command,
 	    _("Backtrace of the stack, or innermost COUNT frames."));
   add_info_alias ("s", "stack", 1);
-  add_info ("frame", info_frame_command,
-	    _("All about selected stack frame, or frame at ADDR."));
+
+  add_prefix_cmd ("frame", class_info, &info_frame_cmd.base_command,
+		  _("All about the selected stack frame.\n\
+With no arguments, displays information about the currently selected stack\n\
+frame.  Alternatively a frame specification may be provided (See \"frame\")\n\
+the information is then printed about the specified frame."),
+		  &info_frame_cmd_list, "info frame ", 1, &infolist);
   add_info_alias ("f", "frame", 1);
+
+  add_cmd ("address", class_stack, &info_frame_cmd.address,
+	   _("\
+Print information about a stack frame selected by stack address.\n\
+\n\
+Usage: info frame address STACK-ADDRESS"),
+	   &info_frame_cmd_list);
+
+  add_cmd ("view", class_stack, &info_frame_cmd.view,
+	   _("\
+Print information about a stack frame outside the current backtrace.\n\
+\n\
+Usage: info frame view STACK-ADDRESS\n\
+       info frame view STACK-ADDRESS PC-ADDRESS"),
+	   &info_frame_cmd_list);
+
+  cmd = add_cmd ("function", class_stack, &info_frame_cmd.function,
+	   _("\
+Print information about a stack frame selected by function name.\n\
+\n\
+Usage: info frame function NAME"),
+	   &info_frame_cmd_list);
+  set_cmd_completer (cmd, frame_selection_by_function_completer);
+
+  add_cmd ("level", class_stack, &info_frame_cmd.level,
+	   _("\
+Print information about a stack frame selected by level.\n\
+\n\
+Usage: info frame level LEVEL"),
+	   &info_frame_cmd_list);
+
   add_info ("locals", info_locals_command,
 	    _("Local variables of current stack frame."));
   add_info ("args", info_args_command,
diff --git a/gdb/stack.h b/gdb/stack.h
index ca190efa9c3..29794872d70 100644
--- a/gdb/stack.h
+++ b/gdb/stack.h
@@ -20,7 +20,7 @@
 #ifndef STACK_H
 #define STACK_H
 
-void select_frame_command (const char *level_exp, int from_tty);
+bool select_frame_from_spec (char **argv, int argc, int from_tty);
 
 gdb::unique_xmalloc_ptr<char> find_frame_funname (struct frame_info *frame,
 						  enum language *funlang,
diff --git a/gdb/testsuite/gdb.base/frame-selection.c b/gdb/testsuite/gdb.base/frame-selection.c
new file mode 100644
index 00000000000..f3d81f223e0
--- /dev/null
+++ b/gdb/testsuite/gdb.base/frame-selection.c
@@ -0,0 +1,52 @@
+/* Copyright 2018 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int
+frame_2 (void)
+{
+  return 0;
+}
+
+int
+frame_1 (void)
+{
+  return frame_2 ();
+}
+
+int
+recursive (int arg)
+{
+  int v;
+
+  if (arg < 2)
+    v = recursive (arg + 1);
+  else
+    v = frame_2 ();
+
+  return v;
+}
+
+int
+main (void)
+{
+  int i, j;
+
+  i = frame_1 ();
+  j = recursive (0);
+
+  return i + j;
+}
diff --git a/gdb/testsuite/gdb.base/frame-selection.exp b/gdb/testsuite/gdb.base/frame-selection.exp
new file mode 100644
index 00000000000..1b3649b2700
--- /dev/null
+++ b/gdb/testsuite/gdb.base/frame-selection.exp
@@ -0,0 +1,156 @@
+# Copyright 2018 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+standard_testfile
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug}]} {
+    return -1
+}
+
+runto_main
+gdb_breakpoint frame_2
+gdb_continue_to_breakpoint frame_2
+
+gdb_test "bt" "#0  frame_2.*#1  $hex in frame_1.*#2  $hex in main.*" "backtrace at breakpoint"
+
+# Perform "info frame" to extract the frames address.
+proc get_frame_address { {testname ""} } {
+    global hex gdb_prompt
+
+    set testname "get_frame_address: ${testname}"
+    set frame_address "unknown"
+
+    send_gdb "info frame\n"
+    gdb_expect {
+	-re ", frame at ($hex):\r\n.*\r\n$gdb_prompt $" {
+	    set frame_address $expect_out(1,string)
+	    pass $testname
+	}
+	-re ".*$gdb_prompt $" { fail $testname }
+	timeout { fail "$testname (timeout)" }
+    }
+
+    return $frame_address
+}
+
+# Passes a list of addresses.  Return a new address that is not in the
+# list.
+proc get_new_address { {addresses {}} } {
+    return [format "%#x" [expr [lindex $addresses [llength addresses]] + 0x10 ]]
+}
+
+proc check_frame { level address function } {
+    global hex gdb_prompt
+
+    set testname "check frame level ${level}"
+    send_gdb "info frame\n"
+    gdb_expect {
+	-re "Stack level ${level}, frame at ($address):\r\n rip = $hex in ${function} \(\[^\r\n\]*\); saved rip = $hex\r\n.*\r\n$gdb_prompt $" {
+	    pass $testname
+	}
+	-re ".*$gdb_prompt $" { fail $testname }
+	timeout { fail "$testname (timeout)" }
+    }
+}
+
+# Select frame using level, but relying on this being the default
+# action, so "frame 0" performs "frame level 0".
+gdb_test "frame 0" "#0  frame_2.*"
+set frame_0_address [ get_frame_address "frame 0" ]
+gdb_test "frame 1" "#1  $hex in frame_1.*"
+set frame_1_address [ get_frame_address "frame 1" ]
+gdb_test "frame 2" "#2  $hex in main.*"
+set frame_2_address [ get_frame_address "frame 2" ]
+gdb_test "frame 3" "No frame at level 3\."
+
+# Find an address that matches no frame
+set no_frame_address [ get_new_address [list $frame_0_address \
+					     $frame_1_address \
+					     $frame_2_address] ]
+
+# Select frame using 'level' specification.
+gdb_test "frame level 0" "#0  frame_2.*"
+gdb_test "frame level 1" "#1  $hex in frame_1.*"
+gdb_test "frame level 2" "#2  $hex in main.*"
+gdb_test "frame level 3" "No frame at level 3\."
+
+# Select frame by address.
+gdb_test "frame address ${frame_0_address}" "#0  frame_2.*"
+gdb_test "frame address ${frame_1_address}" "#1  $hex in frame_1.*"
+gdb_test "frame address ${frame_2_address}" "#2  $hex in main.*"
+gdb_test "frame address ${no_frame_address}" \
+    "No frame at address ${no_frame_address}\."
+
+# Select frame by function.
+gdb_test "frame function frame_2" "#0  frame_2.*"
+gdb_test "frame function frame_1" "#1  $hex in frame_1.*"
+gdb_test "frame function main" "#2  $hex in main.*"
+# Check for a distinction between a known function not in the stack
+# trace, and an unknown function.
+gdb_test "frame function recursive" "No frame for function \"recursive\"."
+gdb_test "frame function foo" "Function \"foo\" not defined."
+
+
+gdb_test_no_output "select-frame 0"
+check_frame "0" "${frame_0_address}" "frame_2"
+gdb_test_no_output "select-frame 1"
+check_frame "1" "${frame_1_address}" "frame_1"
+gdb_test_no_output "select-frame 2"
+check_frame "2" "${frame_2_address}" "main"
+gdb_test "select-frame 3" "No frame at level 3\."
+
+gdb_test_no_output "select-frame level 0"
+check_frame "0" "${frame_0_address}" "frame_2"
+gdb_test_no_output "select-frame level 1"
+check_frame "1" "${frame_1_address}" "frame_1"
+gdb_test_no_output "select-frame level 2"
+check_frame "2" "${frame_2_address}" "main"
+gdb_test "select-frame level 3" "No frame at level 3\."
+
+gdb_test_no_output "select-frame address ${frame_0_address}"
+check_frame "0" "${frame_0_address}" "frame_2"
+gdb_test_no_output "select-frame address ${frame_1_address}"
+check_frame "1" "${frame_1_address}" "frame_1"
+gdb_test_no_output "select-frame address ${frame_2_address}"
+check_frame "2" "${frame_2_address}" "main"
+gdb_test "select-frame address ${no_frame_address}" \
+    "No frame at address ${no_frame_address}\."
+
+gdb_test_no_output "select-frame function frame_2"
+check_frame "0" "${frame_0_address}" "frame_2"
+gdb_test_no_output "select-frame function frame_1"
+check_frame "1" "${frame_1_address}" "frame_1"
+gdb_test_no_output "select-frame function main"
+check_frame "2" "${frame_2_address}" "main"
+# Check for a distinction between a known function not in the stack
+# trace, and an unknown function.
+gdb_test "select-frame function recursive" \
+    "No frame for function \"recursive\"."
+gdb_test "select-frame function foo" \
+    "Function \"foo\" not defined."
+
+# Now continue until we hit the breakpoint again.
+gdb_continue_to_breakpoint frame_2
+gdb_test "bt" \
+    "#0  frame_2.*#1  $hex in recursive.*#2  $hex in recursive.*#3  $hex in recursive.*#4  $hex in main.*" \
+    "backtrace at breakpoint with recursive frames"
+
+# Check "frame function" when a function name occurs multiple times in
+# the stack.  The inner most (lowest level) should always be selected.
+gdb_test "frame function frame_2" "#0  frame_2.*"
+gdb_test "frame function recursive" "#1  $hex in recursive.*"
+gdb_test "frame function recursive" "#1  $hex in recursive.*"
+gdb_test "frame function main" "#4  $hex in main.*"
+gdb_test "frame function recursive" "#1  $hex in recursive.*"
diff --git a/gdb/testsuite/gdb.mi/mi-frame-selection.c b/gdb/testsuite/gdb.mi/mi-frame-selection.c
new file mode 100644
index 00000000000..70b992c9149
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-frame-selection.c
@@ -0,0 +1,34 @@
+/* Copyright 2018 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int
+frame_2 (void)
+{
+  return 0;
+}
+
+int
+frame_1 (void)
+{
+  return frame_2 ();
+}
+
+int
+main (void)
+{
+  return frame_1 ();
+}
diff --git a/gdb/testsuite/gdb.mi/mi-frame-selection.exp b/gdb/testsuite/gdb.mi/mi-frame-selection.exp
new file mode 100644
index 00000000000..e24c7179b9d
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-frame-selection.exp
@@ -0,0 +1,89 @@
+# Copyright 2018 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test the frame selection syntax with the -stack-select-frame command.
+
+load_lib mi-support.exp
+set MIFLAGS "-i=mi"
+
+gdb_exit
+if [mi_gdb_start] {
+    continue
+}
+
+standard_testfile
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    untested $testfile
+    return -1
+}
+
+mi_delete_breakpoints
+mi_gdb_reinitialize_dir $srcdir/$subdir
+mi_gdb_load ${binfile}
+
+mi_create_breakpoint "frame_2" \
+    "break-insert into frame_2" \
+    -number 1 -func frame_2 -file ".*${srcfile}"
+
+mi_run_cmd
+mi_expect_stop "breakpoint-hit" "frame_2" "" ".*${srcfile}" \
+	".*" { "" "disp=\"keep\"" } "run to breakpoint"
+
+mi_gdb_test "-stack-info-depth" \
+    "\\^done,depth=\"3\"" \
+    "stack info-depth"
+
+for {set i 0} {$i < 5} {incr i} {
+
+    if { $i < 3 } then {
+	mi_gdb_test "-stack-select-frame $i" \
+	    {\^done} \
+	    "-stack-select-frame $i"
+
+	mi_gdb_test "-stack-info-frame" \
+	    "\\^done,frame=\\\{level=\"$i\"\[^\}\]+\\\}" \
+	    "check level has has changed to $i"
+
+    } else {
+	mi_gdb_test "-stack-select-frame view $i" \
+	    {\^done} \
+	    "-stack-select-frame view frame at $i"
+
+	mi_gdb_test "-stack-info-frame" \
+	    "\\^done,frame=\\\{level=\"0\"\[^\}\]+\\\}" \
+	    "check level has has changed to 0, when creating a frame at sp=$i"
+    }
+}
+
+mi_gdb_test "-stack-select-frame 5" \
+    {\^error,msg="No frame at level 5."} \
+    "-stack-select-frame 5, with no keyword"
+
+mi_gdb_test "-stack-select-frame level 5" \
+    {\^error,msg="No frame at level 5."} \
+    "-stack-select-frame 5, using 'level' keyword"
+
+mi_gdb_test "-stack-select-frame function main" \
+    {\^done} \
+    "-stack-select-frame select frame for function main"
+
+mi_gdb_test "-stack-info-frame" \
+    "\\^done,frame=\\\{level=\"2\"\[^\}\]+\\\}" \
+    "check level has has changed to 2 for function main"
+
+mi_gdb_test "-stack-select-frame function unknown_function" \
+    {\^error,msg=\"Function \\\"unknown_function\\\" not defined.\"} \
+    "-stack-select-frame check error on undefined function name"
-- 
2.14.4

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCHv6] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-08-28 18:03                     ` [PATCHv6] " Andrew Burgess
@ 2018-08-28 18:20                       ` Eli Zaretskii
  2018-09-05  7:46                       ` PING: " Andrew Burgess
  2018-09-13 18:02                       ` Pedro Alves
  2 siblings, 0 replies; 35+ messages in thread
From: Eli Zaretskii @ 2018-08-28 18:20 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

> From: Andrew Burgess <andrew.burgess@embecosm.com>
> Cc: Andrew Burgess <andrew.burgess@embecosm.com>
> Date: Tue, 28 Aug 2018 19:03:27 +0100
> 
> OK to commit?

The documentation changes are OK, with this typo fixed:

> +@dfn{frame number} and @dfn{frame level} can be used interchangably to
                                                        ^^^^^^^^^^^^^^
It should be "interchangeably".

Thanks.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* PING: Re: [PATCHv6] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-08-28 18:03                     ` [PATCHv6] " Andrew Burgess
  2018-08-28 18:20                       ` Eli Zaretskii
@ 2018-09-05  7:46                       ` Andrew Burgess
  2018-09-13 18:02                       ` Pedro Alves
  2 siblings, 0 replies; 35+ messages in thread
From: Andrew Burgess @ 2018-09-05  7:46 UTC (permalink / raw)
  To: gdb-patches

Ping!

Thanks,
Andrew

* Andrew Burgess <andrew.burgess@embecosm.com> [2018-08-28 19:03:27 +0100]:

> I believe that this variant addresses all existing review comments.
> Philippe provided additional feedback in this message:
> 
>    https://sourceware.org/ml/gdb-patches/2018-08/msg00349.html
> 
> but I believe this was more about things we need to do to unify the
> docs/code/comments on one of either 'number' or 'level', rather than
> things that need to be fixed before this patch could be merged.
> 
> This version of the patch is based off v5(B) and uses the keyword
> 'level' instead of 'number'.
> 
> The only changes over the previous version are an additional change in
> the docs as previously discussed with Eli, and I've rebased the patch
> onto current HEAD (and retested).
> 
> OK to commit?
> 
> Thanks,
> Andrew
> 
> ---
> 
> The 'frame' command, and thanks to code reuse the 'info frame' and
> 'select-frame' commands, currently have an overloaded mechanism for
> selecting a frame.
> 
> These commands take one or two parameters, if it's one parameter then
> we first try to use the parameter as an integer to select a frame by
> level (or depth in the stack).  If that fails then we treat the
> parameter as an address and try to select a stack frame by
> stack-address.  If we still have not selected a stack frame, or we
> initially had two parameters, then GDB allows the user to view a stack
> frame that is not part of the current backtrace.  Internally, a new
> frame is created with the given stack and pc addresses, and this is
> shown to the user.
> 
> The result of this is that a typo by the user, entering the wrong stack
> frame level for example, can result in a brand new frame being viewed
> rather than an error.
> 
> The purpose of this commit is to remove this overloading, while still
> offering the same functionality through some new sub-commands.  By
> making the default behaviour of 'frame' (and friends) be to select a
> stack frame by level index, it is hoped that enough
> backwards-compatibility is maintained that users will not be overly
> inconvenienced.
> 
> The 'frame', 'select-frame', and 'info frame' commands now all take a
> frame specification string as an argument, this string can be any of the
> following:
> 
>   (1) An integer.  This is treated as a frame level.  If a frame for
>   that level does not exist then the user gets an error.
> 
>   (2) A string like 'level <LEVEL>', where <LEVEL> is a frame level
>   as in option (1) above.
> 
>   (3) A string like 'address <STACK-ADDRESS>', where <STACK-ADDRESS>
>   is a stack-frame address.  If there is no frame for this address
>   then the user gets an error.
> 
>   (4) A string like 'function <NAME>', where <NAME> is a function name,
>   the inner most frame for function <NAME> is selected.  If there is no
>   frame for function <NAME> then the user gets an error.
> 
>   (5) A string like 'view <STACK-ADDRESS>', this views a new frame
>   with stack address <STACK-ADDRESS>.
> 
>   (6) A string like 'view <STACK-ADDRESS> <PC-ADDRESS>', this views
>   a new frame with stack address <STACK-ADDRESS> an the pc <PC-ADDRESS>.
> 
> This change assumes that the most common use of the commands like
> 'frame' is to select a frame by frame level, it is for this reason
> that this is the behaviour that is kept for backwards compatibility.
> Any of the alternative behaviours, which are assumed to be less used,
> now require a change in user behaviour.
> 
> The MI command '-stack-select-frame' has also changed in the same way.
> The default behaviour is to select a frame by level, but the other
> selection methods are also available.
> 
> gdb/ChangeLog:
> 
> 	(NEWS): Mention changes to frame related commands.
> 	* cli/cli-decode.c (add_cmd_suppress_notification): New function.
> 	(add_prefix_cmd_suppress_notification): New function.
> 	(add_com_suppress_notification): Call
> 	add_cmd_suppress_notification.
> 	* command.h (add_cmd_suppress_notification): Declare.
> 	(add_prefix_cmd_suppress_notification): Declare.
> 	* mi/mi-cmd-stack.c (mi_cmd_stack_select_frame): Use new function
> 	select_frame_from_spec.
> 	* stack.c (find_frame_for_function): Add declaration.
> 	(find_frame_for_address): New function.
> 	(parse_frame_specification): Delete function.
> 	(frame_selection_by_function_completer): New function.
> 	(info_frame_command): Rename to...
> 	(info_frame_command_core): ...this, and update parameter types.
> 	(select_frame_command): Rename to...
> 	(select_frame_command_core): ...this, and update parameter types.
> 	(frame_command): Rename to...
> 	(frame_command_core): ...this, and update parameter types.
> 	(class frame_command_helper): New class to wrap implementations of
> 	frame related sub-commands.
> 	(select_frame_from_spec): New function.
> 	(frame_apply_cmd_list): New static global.
> 	(frame_cmd_list): Make static.
> 	(select_frame_cmd_list): New global for sub-commands.
> 	(info_frame_cmd_list): New global for sub-commands.
> 	(_initialize_stack): Register sub-commands for 'frame',
> 	'select-frame', and 'info frame'.  Update 'frame apply' commands
> 	to use frame_apply_cmd_list.  Move function local static
> 	frame_apply_list to file static frame_apply_cmd_list for
> 	consistency.
> 	* stack.h (select_frame_command): Delete declarationn.
> 	(select_frame_from_spec): Declare new function.
> 
> gdb/doc/ChangeLog:
> 
> 	* gdb.texinfo (Frames): Rewrite the description of 'frame number'
> 	to highlight that the number is also the frame's level.
> 	(Selection): Rewrite documentation for 'frame' and 'select-frame'
> 	commands.
> 	(Frame Info): Rewrite documentation for 'info frame' command.
> 	(GDB/MI Stack Manipulation): Update description of
> 	'-stack-select-frame'.
> 
> gdb/testsuite/ChangeLog:
> 
> 	* gdb.base/frame-selection.exp: New file.
> 	* gdb.base/frame-selection.c: New file.
> 	* gdb.mi/mi-frame-selection.exp: New file.
> 	* gdb.mi/mi-frame-selection.c: New file.
> ---
>  gdb/ChangeLog                               |  36 ++
>  gdb/NEWS                                    |   8 +
>  gdb/cli/cli-decode.c                        |  44 ++-
>  gdb/command.h                               |  14 +
>  gdb/doc/ChangeLog                           |  10 +
>  gdb/doc/gdb.texinfo                         | 118 ++++--
>  gdb/mi/mi-cmd-stack.c                       |   4 +-
>  gdb/stack.c                                 | 535 +++++++++++++++++++---------
>  gdb/stack.h                                 |   2 +-
>  gdb/testsuite/ChangeLog                     |   7 +
>  gdb/testsuite/gdb.base/frame-selection.c    |  52 +++
>  gdb/testsuite/gdb.base/frame-selection.exp  | 156 ++++++++
>  gdb/testsuite/gdb.mi/mi-frame-selection.c   |  34 ++
>  gdb/testsuite/gdb.mi/mi-frame-selection.exp |  89 +++++
>  14 files changed, 903 insertions(+), 206 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.base/frame-selection.c
>  create mode 100644 gdb/testsuite/gdb.base/frame-selection.exp
>  create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.c
>  create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.exp
> 
> diff --git a/gdb/NEWS b/gdb/NEWS
> index 45514b10c51..b0b2d93939e 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -10,6 +10,14 @@
>  * DWARF index cache: GDB can now automatically save indices of DWARF
>    symbols on disk to speed up further loading of the same binaries.
>  
> +* Changes to the "frame", "select-frame", and "info frame" CLI
> +  commands, as well as the "-stack-select-frame" MI command.  These
> +  commands all now take a frame specification which is either a frame
> +  level, or one of the keywords 'level', 'address', 'function', or
> +  'view' followed by a parameter.  Selecting a frame by address, or
> +  viewing a frame outside the current backtrace now requires the use
> +  of a keyword.  Selecting a frame by level is unchanged.
> +
>  * New commands
>  
>  frame apply [all | COUNT | -COUNT | level LEVEL...] [FLAG]... COMMAND
> diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
> index 83dd67efe3f..5d798e877e8 100644
> --- a/gdb/cli/cli-decode.c
> +++ b/gdb/cli/cli-decode.c
> @@ -251,6 +251,23 @@ add_cmd (const char *name, enum command_class theclass,
>    return result;
>  }
>  
> +/* Add an element with a suppress notification to the LIST of commands.  */
> +
> +struct cmd_list_element *
> +add_cmd_suppress_notification (const char *name, enum command_class theclass,
> +			       cmd_const_cfunc_ftype *fun, const char *doc,
> +			       struct cmd_list_element **list,
> +			       int *suppress_notification)
> +{
> +  struct cmd_list_element *element;
> +
> +  element = add_cmd (name, theclass, fun, doc, list);
> +  element->suppress_notification = suppress_notification;
> +
> +  return element;
> +}
> +
> +
>  /* Deprecates a command CMD.
>     REPLACEMENT is the name of the command which should be used in
>     place of this command, or NULL if no such command exists.
> @@ -362,6 +379,25 @@ add_prefix_cmd (const char *name, enum command_class theclass,
>    return c;
>  }
>  
> +/* Like ADD_PREFIX_CMD but sets the suppress_notification pointer on the
> +   new command list element.  */
> +
> +struct cmd_list_element *
> +add_prefix_cmd_suppress_notification
> +               (const char *name, enum command_class theclass,
> +		cmd_const_cfunc_ftype *fun,
> +		const char *doc, struct cmd_list_element **prefixlist,
> +		const char *prefixname, int allow_unknown,
> +		struct cmd_list_element **list,
> +		int *suppress_notification)
> +{
> +  struct cmd_list_element *element
> +    = add_prefix_cmd (name, theclass, fun, doc, prefixlist,
> +		      prefixname, allow_unknown, list);
> +  element->suppress_notification = suppress_notification;
> +  return element;
> +}
> +
>  /* Like add_prefix_cmd but sets the abbrev_flag on the new command.  */
>  
>  struct cmd_list_element *
> @@ -893,12 +929,8 @@ add_com_suppress_notification (const char *name, enum command_class theclass,
>  			       cmd_const_cfunc_ftype *fun, const char *doc,
>  			       int *suppress_notification)
>  {
> -  struct cmd_list_element *element;
> -
> -  element = add_cmd (name, theclass, fun, doc, &cmdlist);
> -  element->suppress_notification = suppress_notification;
> -
> -  return element;
> +  return add_cmd_suppress_notification (name, theclass, fun, doc,
> +					&cmdlist, suppress_notification);
>  }
>  
>  /* Recursively walk the commandlist structures, and print out the
> diff --git a/gdb/command.h b/gdb/command.h
> index 3dde2475cb1..e3d55c2dcba 100644
> --- a/gdb/command.h
> +++ b/gdb/command.h
> @@ -148,6 +148,12 @@ extern struct cmd_list_element *add_cmd (const char *, enum command_class,
>  					 const char *,
>  					 struct cmd_list_element **);
>  
> +extern struct cmd_list_element *add_cmd_suppress_notification
> +			(const char *name, enum command_class theclass,
> +			 cmd_const_cfunc_ftype *fun, const char *doc,
> +			 struct cmd_list_element **list,
> +			 int *suppress_notification);
> +
>  extern struct cmd_list_element *add_alias_cmd (const char *, const char *,
>  					       enum command_class, int,
>  					       struct cmd_list_element **);
> @@ -165,6 +171,14 @@ extern struct cmd_list_element *add_prefix_cmd (const char *, enum command_class
>  						const char *, int,
>  						struct cmd_list_element **);
>  
> +extern struct cmd_list_element *add_prefix_cmd_suppress_notification
> +			(const char *name, enum command_class theclass,
> +			 cmd_const_cfunc_ftype *fun,
> +			 const char *doc, struct cmd_list_element **prefixlist,
> +			 const char *prefixname, int allow_unknown,
> +			 struct cmd_list_element **list,
> +			 int *suppress_notification);
> +
>  extern struct cmd_list_element *add_abbrev_prefix_cmd (const char *,
>  						       enum command_class,
>  						       cmd_const_cfunc_ftype *fun,
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 5068c0ac81f..1cafea97976 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -7393,12 +7393,14 @@
>  in a register called the @dfn{frame pointer register}
>  (@pxref{Registers, $fp}) while execution is going on in that frame.
>  
> +@cindex frame level
>  @cindex frame number
> -@value{GDBN} assigns numbers to all existing stack frames, starting with
> -zero for the innermost frame, one for the frame that called it,
> -and so on upward.  These numbers do not really exist in your program;
> -they are assigned by @value{GDBN} to give you a way of designating stack
> -frames in @value{GDBN} commands.
> +@value{GDBN} labels each existing stack frame with a @dfn{level}, a
> +number that is zero for the innermost frame, one for the frame that
> +called it, and so on upward.  These level numbers give you a way of
> +designating stack frames in @value{GDBN} commands.  The terms
> +@dfn{frame number} and @dfn{frame level} can be used interchangably to
> +describe this number.
>  
>  @c The -fomit-frame-pointer below perennially causes hbox overflow
>  @c underflow problems.
> @@ -7631,21 +7633,75 @@
>  @table @code
>  @kindex frame@r{, selecting}
>  @kindex f @r{(@code{frame})}
> -@item frame @var{n}
> -@itemx f @var{n}
> -Select frame number @var{n}.  Recall that frame zero is the innermost
> +@item frame @r{[} @var{frame-selection-spec} @r{]}
> +@item f @r{[} @var{frame-selection-spec} @r{]}
> +The @command{frame} command allows different stack frames to be
> +selected.  The @var{frame-selection-spec} can be any of the following:
> +
> +@table @code
> +@kindex frame level
> +@item @var{num}
> +@item level @var{num}
> +Select frame level @var{num}.  Recall that frame zero is the innermost
>  (currently executing) frame, frame one is the frame that called the
> -innermost one, and so on.  The highest-numbered frame is the one for
> -@code{main}.
> +innermost one, and so on.  The highest level frame is usually the one
> +for @code{main}.
> +
> +As this is the most common method of navigating the frame stack then
> +the string @command{level} can be dropped, the following two commands
> +are equivalent:
>  
> -@item frame @var{stack-addr} [ @var{pc-addr} ]
> -@itemx f @var{stack-addr} [ @var{pc-addr} ]
> -Select the frame at address @var{stack-addr}.  This is useful mainly if the
> -chaining of stack frames has been damaged by a bug, making it
> -impossible for @value{GDBN} to assign numbers properly to all frames.  In
> -addition, this can be useful when your program has multiple stacks and
> -switches between them.  The optional @var{pc-addr} can also be given to
> -specify the value of PC for the stack frame.
> +@smallexample
> +(@value{GDBP}) frame 3
> +(@value{GDBP}) frame level 3
> +@end smallexample
> +
> +@kindex frame address
> +@item address @var{stack-address}
> +Select the frame with stack address @var{stack-address}.  The
> +@var{stack-address} for a frame can be seen in the output of
> +@command{info frame}, for example:
> +
> +@smallexample
> +(gdb) info frame
> +Stack level 1, frame at 0x7fffffffda30:
> + rip = 0x40066d in b (amd64-entry-value.cc:59); saved rip 0x4004c5
> + tail call frame, caller of frame at 0x7fffffffda30
> + source language c++.
> + Arglist at unknown address.
> + Locals at unknown address, Previous frame's sp is 0x7fffffffda30
> +@end smallexample
> +
> +The @var{stack-address} for this frame is @code{0x7fffffffda30} as
> +indicated by the line:
> +
> +@smallexample
> +Stack level 1, frame at 0x7fffffffda30:
> +@end smallexample
> +
> +@kindex frame function
> +@item function @var{function-name}
> +Select the stack frame for function @var{function-name}.  If there are
> +multiple stack frames for function @var{function-name} then the inner
> +most stack frame is selected.
> +
> +@kindex frame view
> +@item view @var{stack-address} @r{[} @var{pc-addr} @r{]}
> +View a frame that is not part of @value{GDBN}'s backtrace.  The frame
> +viewed has stack address @var{stack-addr}, and optionally, a program
> +counter address of @var{pc-addr}.
> +
> +This is useful mainly if the chaining of stack frames has been
> +damaged by a bug, making it impossible for @value{GDBN} to assign
> +numbers properly to all frames.  In addition, this can be useful
> +when your program has multiple stacks and switches between them.
> +
> +When viewing a frame outside the current backtrace using
> +@command{frame view} then you can always return to the original
> +stack using one of the previous stack frame selection instructions,
> +for example @command{frame level 0}.
> +
> +@end table
>  
>  @kindex up
>  @item up @var{n}
> @@ -7688,11 +7744,13 @@
>  
>  @table @code
>  @kindex select-frame
> -@item select-frame
> +@item select-frame @r{[} @var{frame-selection-spec} @r{]}
>  The @code{select-frame} command is a variant of @code{frame} that does
>  not display the new frame after selecting it.  This command is
>  intended primarily for use in @value{GDBN} command scripts, where the
> -output might be unnecessary and distracting.
> +output might be unnecessary and distracting.  The
> +@var{frame-selection-spec} is as for the @command{frame} command
> +described in @ref{Selection, ,Selecting a Frame}.
>  
>  @kindex down-silently
>  @kindex up-silently
> @@ -7750,13 +7808,12 @@
>  something has gone wrong that has made the stack format fail to fit
>  the usual conventions.
>  
> -@item info frame @var{addr}
> -@itemx info f @var{addr}
> -Print a verbose description of the frame at address @var{addr}, without
> -selecting that frame.  The selected frame remains unchanged by this
> -command.  This requires the same kind of address (more than one for some
> -architectures) that you specify in the @code{frame} command.
> -@xref{Selection, ,Selecting a Frame}.
> +@item info frame @r{[} @var{frame-selection-spec} @r{]}
> +@itemx info f @r{[} @var{frame-selection-spec} @r{]}
> +Print a verbose description of the frame selected by
> +@var{frame-selection-spec}.  The @var{frame-selection-spec} is the
> +same as for the @command{frame} command (@pxref{Selection, ,Selecting
> +a Frame}).  The selected frame remains unchanged by this command.
>  
>  @kindex info args
>  @item info args
> @@ -30329,11 +30386,12 @@
>  @subsubheading Synopsis
>  
>  @smallexample
> - -stack-select-frame @var{framenum}
> + -stack-select-frame @var{frame-selection-spec}
>  @end smallexample
>  
> -Change the selected frame.  Select a different frame @var{framenum} on
> -the stack.
> +Change the selected frame, the @var{frame-selection-spec} is as for
> +the @command{frame} command described in @ref{Selection, ,Selecting a
> +Frame}.
>  
>  This command in deprecated in favor of passing the @samp{--frame}
>  option to every command.
> diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c
> index 52660bdd498..33ac7663ba8 100644
> --- a/gdb/mi/mi-cmd-stack.c
> +++ b/gdb/mi/mi-cmd-stack.c
> @@ -681,10 +681,8 @@ list_args_or_locals (enum what_to_list what, enum print_values values,
>  void
>  mi_cmd_stack_select_frame (const char *command, char **argv, int argc)
>  {
> -  if (argc == 0 || argc > 1)
> +  if (!select_frame_from_spec (argv, argc, 0))
>      error (_("-stack-select-frame: Usage: FRAME_SPEC"));
> -
> -  select_frame_command (argv[0], 1 /* not used */ );
>  }
>  
>  void
> diff --git a/gdb/stack.c b/gdb/stack.c
> index 40ff99b8fa6..68029f56412 100644
> --- a/gdb/stack.c
> +++ b/gdb/stack.c
> @@ -102,6 +102,9 @@ static void set_last_displayed_sal (int valid,
>  				    struct symtab *symtab,
>  				    int line);
>  
> +static struct frame_info *find_frame_for_function (const char *);
> +static struct frame_info *find_frame_for_address (CORE_ADDR);
> +
>  /* Zero means do things normally; we are interacting directly with the
>     user.  One means print the full filename and linenumber when a
>     frame is printed, and do so in a format emacs18/emacs19.22 can
> @@ -1258,134 +1261,34 @@ print_frame (struct frame_info *frame, int print_level,
>  }
>  \f
>  
> -/* Read a frame specification in whatever the appropriate format is from
> -   FRAME_EXP.  Call error() if the specification is in any way invalid (so
> -   this function never returns NULL).  When SELECTED_FRAME_P is non-NULL
> -   set its target to indicate that the default selected frame was used.  */
> +/* Completion function for "frame function", "info frame function", and
> +   "select-frame function" commands.  */
>  
> -static struct frame_info *
> -parse_frame_specification (const char *frame_exp, int *selected_frame_p)
> +void
> +frame_selection_by_function_completer (struct cmd_list_element *ignore,
> +				       completion_tracker &tracker,
> +				       const char *text, const char *word)
>  {
> -  int numargs;
> -  struct value *args[4];
> -  CORE_ADDR addrs[ARRAY_SIZE (args)];
> -
> -  if (frame_exp == NULL)
> -    numargs = 0;
> -  else
> -    {
> -      numargs = 0;
> -      while (1)
> -	{
> -	  const char *p;
> -
> -	  /* Skip leading white space, bail of EOL.  */
> -	  frame_exp = skip_spaces (frame_exp);
> -	  if (!*frame_exp)
> -	    break;
> -
> -	  /* Parse the argument, extract it, save it.  */
> -	  for (p = frame_exp;
> -	       *p && !ISSPACE (*p);
> -	       p++);
> -	  std::string addr_string (frame_exp, p - frame_exp);
> -	  frame_exp = p;
> -	  
> -	  /* NOTE: Parse and evaluate expression, but do not use
> -	     functions such as parse_and_eval_long or
> -	     parse_and_eval_address to also extract the value.
> -	     Instead value_as_long and value_as_address are used.
> -	     This avoids problems with expressions that contain
> -	     side-effects.  */
> -	  if (numargs >= ARRAY_SIZE (args))
> -	    error (_("Too many args in frame specification"));
> -	  args[numargs++] = parse_and_eval (addr_string.c_str ());
> -	}
> -    }
> -
> -  /* If no args, default to the selected frame.  */
> -  if (numargs == 0)
> -    {
> -      if (selected_frame_p != NULL)
> -	(*selected_frame_p) = 1;
> -      return get_selected_frame (_("No stack."));
> -    }
> -
> -  /* None of the remaining use the selected frame.  */
> -  if (selected_frame_p != NULL)
> -    (*selected_frame_p) = 0;
> -
> -  /* Assume the single arg[0] is an integer, and try using that to
> -     select a frame relative to current.  */
> -  if (numargs == 1)
> -    {
> -      struct frame_info *fid;
> -      int level = value_as_long (args[0]);
> -
> -      fid = find_relative_frame (get_current_frame (), &level);
> -      if (level == 0)
> -	/* find_relative_frame was successful.  */
> -	return fid;
> -    }
> -
> -  /* Convert each value into a corresponding address.  */
> -  {
> -    int i;
> -
> -    for (i = 0; i < numargs; i++)
> -      addrs[i] = value_as_address (args[i]);
> -  }
> -
> -  /* Assume that the single arg[0] is an address, use that to identify
> -     a frame with a matching ID.  Should this also accept stack/pc or
> -     stack/pc/special.  */
> -  if (numargs == 1)
> -    {
> -      struct frame_id id = frame_id_build_wild (addrs[0]);
> -      struct frame_info *fid;
> -
> -      /* If (s)he specifies the frame with an address, he deserves
> -	 what (s)he gets.  Still, give the highest one that matches.
> -	 (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't
> -	 know).  */
> -      for (fid = get_current_frame ();
> -	   fid != NULL;
> -	   fid = get_prev_frame (fid))
> -	{
> -	  if (frame_id_eq (id, get_frame_id (fid)))
> -	    {
> -	      struct frame_info *prev_frame;
> -
> -	      while (1)
> -		{
> -		  prev_frame = get_prev_frame (fid);
> -		  if (!prev_frame
> -		      || !frame_id_eq (id, get_frame_id (prev_frame)))
> -		    break;
> -		  fid = prev_frame;
> -		}
> -	      return fid;
> -	    }
> -	}
> -      }
> -
> -  /* We couldn't identify the frame as an existing frame, but
> -     perhaps we can create one with a single argument.  */
> -  if (numargs == 1)
> -    return create_new_frame (addrs[0], 0);
> -  else if (numargs == 2)
> -    return create_new_frame (addrs[0], addrs[1]);
> -  else
> -    error (_("Too many args in frame specification"));
> +  /* This is used to complete function names within a stack.  It would be
> +     nice if instead of offering all available function names, we only
> +     offered functions that were actually in the stack.  However, this
> +     would probably mean unwinding the stack to completion, which could
> +     take too long (or on a corrupted stack, possibly not end).  For now I
> +     offer all symbol names as a safer choice.  */
> +  collect_symbol_completion_matches (tracker,
> +				     complete_symbol_mode::EXPRESSION,
> +				     symbol_name_match_type::EXPRESSION,
> +				     text, word);
>  }
>  
> -/* Print verbosely the selected frame or the frame at address
> -   ADDR_EXP.  Absolutely all information in the frame is printed.  */
> +/* Core of all the "info frame" sub-commands.  Print verbosely the frame FI
> +   if SELECTED_FRAME_P is true then frame FI is the current frame, which
> +   was selected as a default due to the user not providing any arguments
> +   to select some other frame.  */
>  
>  static void
> -info_frame_command (const char *addr_exp, int from_tty)
> +info_frame_command_core (struct frame_info *fi, bool selected_frame_p)
>  {
> -  struct frame_info *fi;
>    struct symbol *func;
>    struct symtab *s;
>    struct frame_info *calling_frame_info;
> @@ -1393,7 +1296,6 @@ info_frame_command (const char *addr_exp, int from_tty)
>    const char *funname = 0;
>    enum language funlang = language_unknown;
>    const char *pc_regname;
> -  int selected_frame_p;
>    struct gdbarch *gdbarch;
>    CORE_ADDR frame_pc;
>    int frame_pc_p;
> @@ -1401,7 +1303,6 @@ info_frame_command (const char *addr_exp, int from_tty)
>    CORE_ADDR caller_pc = 0;
>    int caller_pc_p = 0;
>  
> -  fi = parse_frame_specification (addr_exp, &selected_frame_p);
>    gdbarch = get_frame_arch (fi);
>  
>    /* Name of the value returned by get_frame_pc().  Per comments, "pc"
> @@ -1742,6 +1643,140 @@ trailing_outermost_frame (int count)
>    return trailing;
>  }
>  
> +/* The core of all the "select-frame" sub-commands.  Just wraps a call to
> +   SELECT_FRAME.  */
> +
> +static void
> +select_frame_command_core (struct frame_info *fi, bool ignored)
> +{
> +  struct frame_info *prev_frame = get_selected_frame_if_set ();
> +  select_frame (fi);
> +  if (get_selected_frame_if_set () != prev_frame)
> +    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
> +}
> +
> +/* The core of all the "frame" sub-commands.  Select frame FI, and if this
> +   means we change frame send out a change notification (otherwise, just
> +   reprint the current frame summary).   */
> +
> +static void
> +frame_command_core (struct frame_info *fi, bool ignored)
> +{
> +  struct frame_info *prev_frame = get_selected_frame_if_set ();
> +
> +  select_frame (fi);
> +  if (get_selected_frame_if_set () != prev_frame)
> +    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
> +  else
> +    print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME);
> +}
> +
> +/* The three commands 'frame', 'select-frame', and 'info frame' all have a
> +   common set of sub-commands that allow a specific frame to be selected.
> +   All of the sub-command functions are static methods within this class
> +   template which is then instantiated below.  */
> +
> +template <void (*FPTR) (struct frame_info *, bool)>
> +class frame_command_helper
> +{
> +public:
> +
> +  /* The "frame level" family of commands.  The ARG is an integer that is
> +     the frame's level in the stack.  */
> +  static void
> +  level (const char *arg, int from_tty)
> +  {
> +    int level = value_as_long (parse_and_eval (arg));
> +    struct frame_info *fid
> +      = find_relative_frame (get_current_frame (), &level);
> +    if (level != 0)
> +      error (_("No frame at level %s."), arg);
> +    FPTR (fid, false);
> +  }
> +
> +  /* The "frame address" family of commands.  ARG is a stack-pointer
> +     address for an existing frame.  This command does not allow new
> +     frames to be created.  */
> +
> +  static void
> +  address (const char *arg, int from_tty)
> +  {
> +    CORE_ADDR addr = value_as_address (parse_and_eval (arg));
> +    struct frame_info *fid = find_frame_for_address (addr);
> +    if (fid == NULL)
> +      error (_("No frame at address %s."), arg);
> +    FPTR (fid, false);
> +  }
> +
> +  /* The "frame view" family of commands.  ARG is one or two addresses and
> +     is used to view a frame that might be outside the current backtrace.
> +     The addresses are stack-pointer address, and (optional) pc-address.  */
> +
> +  static void
> +  view (const char *args, int from_tty)
> +  {
> +    struct frame_info *fid;
> +
> +    if (args == NULL)
> +    error (_("Missing address argument to view a frame"));
> +
> +    gdb_argv argv (args);
> +
> +    if (argv.count () == 2)
> +      {
> +	CORE_ADDR addr[2];
> +
> +	addr [0] = value_as_address (parse_and_eval (argv[0]));
> +	addr [1] = value_as_address (parse_and_eval (argv[1]));
> +	fid = create_new_frame (addr[0], addr[1]);
> +      }
> +    else
> +      {
> +	CORE_ADDR addr = value_as_address (parse_and_eval (argv[0]));
> +	fid = create_new_frame (addr, false);
> +      }
> +    FPTR (fid, false);
> +  }
> +
> +  /* The "frame function" family of commands.  ARG is the name of a
> +     function within the stack, the first function (searching from frame
> +     0) with that name will be selected.  */
> +
> +  static void
> +  function (const char *arg, int from_tty)
> +  {
> +    if (arg == NULL)
> +      error (_("Missing function name argument"));
> +    struct frame_info *fid = find_frame_for_function (arg);
> +    if (fid == NULL)
> +      error (_("No frame for function \"%s\"."), arg);
> +    FPTR (fid, false);
> +  }
> +
> +  /* The "frame" base command, that is, when no sub-command is specified.
> +     If one argument is provided then we assume that this is a frame's
> +     level as historically, this was the supported command syntax that was
> +     used most often.
> +
> +     If no argument is provided, then the current frame is selected.  */
> +
> +  static void
> +  base_command (const char *arg, int from_tty)
> +  {
> +    if (arg == NULL)
> +      FPTR (get_selected_frame (_("No stack.")), true);
> +    else
> +      level (arg, from_tty);
> +  }
> +};
> +
> +/* Instantiate three FRAME_COMMAND_HELPER instances to implement the
> +   sub-commands for 'info frame', 'frame', and 'select-frame' commands.  */
> +
> +static frame_command_helper <info_frame_command_core> info_frame_cmd;
> +static frame_command_helper <frame_command_core> frame_cmd;
> +static frame_command_helper <select_frame_command_core> select_frame_cmd;
> +
>  /* Print briefly all stack frames or just the innermost COUNT_EXP
>     frames.  */
>  
> @@ -2230,37 +2265,43 @@ find_relative_frame (struct frame_info *frame, int *level_offset_ptr)
>    return frame;
>  }
>  
> -/* The "select_frame" command.  With no argument this is a NOP.
> -   Select the frame at level LEVEL_EXP if it is a valid level.
> -   Otherwise, treat LEVEL_EXP as an address expression and select it.
> -
> -   See parse_frame_specification for more info on proper frame
> -   expressions.  */
> -
> -void
> -select_frame_command (const char *level_exp, int from_tty)
> -{
> -  struct frame_info *prev_frame = get_selected_frame_if_set ();
> -
> -  select_frame (parse_frame_specification (level_exp, NULL));
> -  if (get_selected_frame_if_set () != prev_frame)
> -    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
> -}
> -
> -/* The "frame" command.  With no argument, print the selected frame
> -   briefly.  With an argument, behave like select_frame and then print
> -   the selected frame.  */
> +/* Select a frame using ARGV.  This is used from the MI -stack-select-frame
> +   to provide the same frame specification mechanism that the CLI has for
> +   commands like 'frame'.  The return value is true if the contents of
> +   ARGV looked like a sensible attempt to change the frame (an error might
> +   still be thrown though), or false if the contents of ARGV are not a
> +   correct frame specification.  */
>  
> -static void
> -frame_command (const char *level_exp, int from_tty)
> +bool
> +select_frame_from_spec (char **argv, int argc, int from_tty)
>  {
> -  struct frame_info *prev_frame = get_selected_frame_if_set ();
> +  if (argc == 1)
> +    select_frame_cmd.level (argv[0], from_tty);
> +  else if (argc == 2
> +	   && strncasecmp ("level", argv[0], strlen (argv[0])) == 0)
> +    select_frame_cmd.level (argv[1], from_tty);
> +  else if (argc == 2
> +	   && strncasecmp ("address", argv[0], strlen (argv[0])) == 0)
> +    select_frame_cmd.address (argv[1], from_tty);
> +  else if (argc == 2
> +	   && strncasecmp ("function", argv[0], strlen (argv[0])) == 0)
> +    select_frame_cmd.function (argv[1], from_tty);
> +  else if ((argc == 2 || argc == 3)
> +	   && strncasecmp ("view", argv[0], strlen (argv[0])) == 0)
> +    {
> +      std::string arg;
> +
> +      if (argc == 2)
> +	arg = string_printf ("%s", argv[1]);
> +      else
> +	arg = string_printf ("%s %s", argv[1], argv[2]);
>  
> -  select_frame (parse_frame_specification (level_exp, NULL));
> -  if (get_selected_frame_if_set () != prev_frame)
> -    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
> +      select_frame_cmd.view (arg.c_str (), from_tty);
> +    }
>    else
> -    print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME);
> +    return false;
> +
> +  return true;
>  }
>  
>  /* Select the frame up one or COUNT_EXP stack levels from the
> @@ -2716,13 +2757,61 @@ faas_command (const char *cmd, int from_tty)
>  }
>  
>  
> +/* Find inner-mode frame with frame address ADDRESS.  Return NULL if no
> +   matching frame can be found.  */
> +
> +static struct frame_info *
> +find_frame_for_address (CORE_ADDR address)
> +{
> +  struct frame_id id;
> +  struct frame_info *fid;
> +
> +  id = frame_id_build_wild (address);
> +
> +  /* If (s)he specifies the frame with an address, he deserves
> +     what (s)he gets.  Still, give the highest one that matches.
> +     (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't
> +     know).  */
> +  for (fid = get_current_frame ();
> +       fid != NULL;
> +       fid = get_prev_frame (fid))
> +    {
> +      if (frame_id_eq (id, get_frame_id (fid)))
> +	{
> +	  struct frame_info *prev_frame;
> +
> +	  while (1)
> +	    {
> +	      prev_frame = get_prev_frame (fid);
> +	      if (!prev_frame
> +		  || !frame_id_eq (id, get_frame_id (prev_frame)))
> +		break;
> +	      fid = prev_frame;
> +	    }
> +	  return fid;
> +	}
> +    }
> +  return NULL;
> +}
> +
> +\f
> +
> +/* Commands with a prefix of `frame apply'.  */
> +static struct cmd_list_element *frame_apply_cmd_list = NULL;
> +
>  /* Commands with a prefix of `frame'.  */
> -struct cmd_list_element *frame_cmd_list = NULL;
> +static struct cmd_list_element *frame_cmd_list = NULL;
> +
> +/* Commands with a prefix of `select frame'.  */
> +static struct cmd_list_element *select_frame_cmd_list = NULL;
> +
> +/* Commands with a prefix of `info frame'.  */
> +static struct cmd_list_element *info_frame_cmd_list = NULL;
>  
>  void
>  _initialize_stack (void)
>  {
> -  static struct cmd_list_element *frame_apply_list = NULL;
> +  struct cmd_list_element *cmd;
>  
>    add_com ("return", class_stack, return_command, _("\
>  Make selected stack frame return to its caller.\n\
> @@ -2746,12 +2835,12 @@ An argument says how many frames down to go."));
>  Same as the `down' command, but does not print anything.\n\
>  This is useful in command scripts."));
>  
> -  add_prefix_cmd ("frame", class_stack, frame_command, _("\
> -Select and print a stack frame.\nWith no argument, \
> -print the selected stack frame.  (See also \"info frame\").\n\
> -An argument specifies the frame to select.\n\
> -It can be a stack frame number or the address of the frame."),
> -		  &frame_cmd_list, "frame ", 1, &cmdlist);
> +  add_prefix_cmd ("frame", class_stack,
> +                  &frame_cmd.base_command, _("\
> +Select and print a stack frame.\n\
> +With no argument, print the selected stack frame.  (See also \"info frame\").\n\
> +A single numerical argument specifies the frame to select."),
> +                  &frame_cmd_list, "frame ", 1, &cmdlist);
>  
>    add_com_alias ("f", "frame", class_stack, 1);
>  
> @@ -2769,7 +2858,7 @@ or produces no output."
>  Usage: frame apply COUNT [FLAG]... COMMAND\n\
>  With a negative COUNT argument, applies the command on outermost -COUNT frames.\n"
>  FRAME_APPLY_FLAGS_HELP),
> -		  &frame_apply_list, "frame apply ", 1, &frame_cmd_list);
> +		  &frame_apply_cmd_list, "frame apply ", 1, &frame_cmd_list);
>  
>    add_cmd ("all", class_stack, frame_apply_all_command,
>  	   _("\
> @@ -2777,7 +2866,7 @@ Apply a command to all frames.\n\
>  \n\
>  Usage: frame apply all [FLAG]... COMMAND\n"
>  FRAME_APPLY_FLAGS_HELP),
> -	   &frame_apply_list);
> +	   &frame_apply_cmd_list);
>  
>    add_cmd ("level", class_stack, frame_apply_level_command,
>  	   _("\
> @@ -2786,19 +2875,97 @@ Apply a command to a list of frames.\n\
>  Usage: frame apply level LEVEL... [FLAG]... COMMAND\n\
>  ID is a space-separated list of LEVELs of frames to apply COMMAND on.\n"
>  FRAME_APPLY_FLAGS_HELP),
> -	   &frame_apply_list);
> +	   &frame_apply_cmd_list);
>  
>    add_com ("faas", class_stack, faas_command, _("\
>  Apply a command to all frames (ignoring errors and empty output).\n\
>  Usage: faas COMMAND\n\
>  shortcut for 'frame apply all -s COMMAND'"));
>  
> -  add_com_suppress_notification ("select-frame", class_stack, select_frame_command, _("\
> +
> +  add_prefix_cmd ("frame", class_stack,
> +		  &frame_cmd.base_command, _("\
> +Select and print a stack frame.\n\
> +With no argument, print the selected stack frame.  (See also \"info frame\").\n\
> +A single numerical argument specifies the frame to select."),
> +		  &frame_cmd_list, "frame ", 1, &cmdlist);
> +  add_com_alias ("f", "frame", class_stack, 1);
> +
> +  add_cmd ("address", class_stack, &frame_cmd.address,
> +	   _("\
> +Select and print a stack frame by stack address\n\
> +\n\
> +Usage: frame address STACK-ADDRESS"),
> +	   &frame_cmd_list);
> +
> +  add_cmd ("view", class_stack, &frame_cmd.view,
> +	   _("\
> +View a stack frame that might be outside the current backtrace.\n\
> +\n\
> +Usage: frame view STACK-ADDRESS\n\
> +       frame view STACK-ADDRESS PC-ADDRESS"),
> +	   &frame_cmd_list);
> +
> +  cmd = add_cmd ("function", class_stack, &frame_cmd.function,
> +	   _("\
> +Select and print a stack frame by function name.\n\
> +\n\
> +Usage: frame function NAME\n\
> +\n\
> +The innermost frame that visited function NAME is selected."),
> +	   &frame_cmd_list);
> +  set_cmd_completer (cmd, frame_selection_by_function_completer);
> +
> +
> +  add_cmd ("level", class_stack, &frame_cmd.level,
> +	   _("\
> +Select and print a stack frame by level.\n\
> +\n\
> +Usage: frame level LEVEL"),
> +	   &frame_cmd_list);
> +
> +  cmd = add_prefix_cmd_suppress_notification ("select-frame", class_stack,
> +		      &select_frame_cmd.base_command, _("\
>  Select a stack frame without printing anything.\n\
> -An argument specifies the frame to select.\n\
> -It can be a stack frame number or the address of the frame."),
> +A single numerical argument specifies the frame to select."),
> +		      &select_frame_cmd_list, "select-frame ", 1, &cmdlist,
> +		      &cli_suppress_notification.user_selected_context);
> +
> +  add_cmd_suppress_notification ("address", class_stack,
> +			 &select_frame_cmd.address, _("\
> +Select a stack frame by stack address.\n\
> +\n\
> +Usage: select-frame address STACK-ADDRESS"),
> +			 &select_frame_cmd_list,
> +			 &cli_suppress_notification.user_selected_context);
> +
> +
> +  add_cmd_suppress_notification ("view", class_stack,
> +		 &select_frame_cmd.view, _("\
> +Select a stack frame that might be outside the current backtrace.\n\
> +\n\
> +Usage: select-frame view STACK-ADDRESS\n\
> +       select-frame view STACK-ADDRESS PC-ADDRESS"),
> +		 &select_frame_cmd_list,
>  		 &cli_suppress_notification.user_selected_context);
>  
> +  cmd = add_cmd_suppress_notification ("function", class_stack,
> +	       &select_frame_cmd.function, _("\
> +Select a stack frame by function name.\n\
> +\n\
> +Usage: select-frame function NAME"),
> +	       &select_frame_cmd_list,
> +	       &cli_suppress_notification.user_selected_context);
> +  set_cmd_completer (cmd, frame_selection_by_function_completer);
> +
> +  add_cmd_suppress_notification ("level", class_stack,
> +			 &select_frame_cmd.level, _("\
> +Select a stack frame by level.\n\
> +\n\
> +Usage: select-frame level LEVEL"),
> +			 &select_frame_cmd_list,
> +			 &cli_suppress_notification.user_selected_context);
> +
>    add_com ("backtrace", class_stack, backtrace_command, _("\
>  Print backtrace of all stack frames, or innermost COUNT frames.\n\
>  Usage: backtrace [QUALIFIERS]... [COUNT]\n\
> @@ -2812,9 +2979,45 @@ on this backtrace."));
>    add_info ("stack", backtrace_command,
>  	    _("Backtrace of the stack, or innermost COUNT frames."));
>    add_info_alias ("s", "stack", 1);
> -  add_info ("frame", info_frame_command,
> -	    _("All about selected stack frame, or frame at ADDR."));
> +
> +  add_prefix_cmd ("frame", class_info, &info_frame_cmd.base_command,
> +		  _("All about the selected stack frame.\n\
> +With no arguments, displays information about the currently selected stack\n\
> +frame.  Alternatively a frame specification may be provided (See \"frame\")\n\
> +the information is then printed about the specified frame."),
> +		  &info_frame_cmd_list, "info frame ", 1, &infolist);
>    add_info_alias ("f", "frame", 1);
> +
> +  add_cmd ("address", class_stack, &info_frame_cmd.address,
> +	   _("\
> +Print information about a stack frame selected by stack address.\n\
> +\n\
> +Usage: info frame address STACK-ADDRESS"),
> +	   &info_frame_cmd_list);
> +
> +  add_cmd ("view", class_stack, &info_frame_cmd.view,
> +	   _("\
> +Print information about a stack frame outside the current backtrace.\n\
> +\n\
> +Usage: info frame view STACK-ADDRESS\n\
> +       info frame view STACK-ADDRESS PC-ADDRESS"),
> +	   &info_frame_cmd_list);
> +
> +  cmd = add_cmd ("function", class_stack, &info_frame_cmd.function,
> +	   _("\
> +Print information about a stack frame selected by function name.\n\
> +\n\
> +Usage: info frame function NAME"),
> +	   &info_frame_cmd_list);
> +  set_cmd_completer (cmd, frame_selection_by_function_completer);
> +
> +  add_cmd ("level", class_stack, &info_frame_cmd.level,
> +	   _("\
> +Print information about a stack frame selected by level.\n\
> +\n\
> +Usage: info frame level LEVEL"),
> +	   &info_frame_cmd_list);
> +
>    add_info ("locals", info_locals_command,
>  	    _("Local variables of current stack frame."));
>    add_info ("args", info_args_command,
> diff --git a/gdb/stack.h b/gdb/stack.h
> index ca190efa9c3..29794872d70 100644
> --- a/gdb/stack.h
> +++ b/gdb/stack.h
> @@ -20,7 +20,7 @@
>  #ifndef STACK_H
>  #define STACK_H
>  
> -void select_frame_command (const char *level_exp, int from_tty);
> +bool select_frame_from_spec (char **argv, int argc, int from_tty);
>  
>  gdb::unique_xmalloc_ptr<char> find_frame_funname (struct frame_info *frame,
>  						  enum language *funlang,
> diff --git a/gdb/testsuite/gdb.base/frame-selection.c b/gdb/testsuite/gdb.base/frame-selection.c
> new file mode 100644
> index 00000000000..f3d81f223e0
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/frame-selection.c
> @@ -0,0 +1,52 @@
> +/* Copyright 2018 Free Software Foundation, Inc.
> +
> +   This file is part of GDB.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
> +
> +int
> +frame_2 (void)
> +{
> +  return 0;
> +}
> +
> +int
> +frame_1 (void)
> +{
> +  return frame_2 ();
> +}
> +
> +int
> +recursive (int arg)
> +{
> +  int v;
> +
> +  if (arg < 2)
> +    v = recursive (arg + 1);
> +  else
> +    v = frame_2 ();
> +
> +  return v;
> +}
> +
> +int
> +main (void)
> +{
> +  int i, j;
> +
> +  i = frame_1 ();
> +  j = recursive (0);
> +
> +  return i + j;
> +}
> diff --git a/gdb/testsuite/gdb.base/frame-selection.exp b/gdb/testsuite/gdb.base/frame-selection.exp
> new file mode 100644
> index 00000000000..1b3649b2700
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/frame-selection.exp
> @@ -0,0 +1,156 @@
> +# Copyright 2018 Free Software Foundation, Inc.
> +
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +
> +standard_testfile
> +
> +if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug}]} {
> +    return -1
> +}
> +
> +runto_main
> +gdb_breakpoint frame_2
> +gdb_continue_to_breakpoint frame_2
> +
> +gdb_test "bt" "#0  frame_2.*#1  $hex in frame_1.*#2  $hex in main.*" "backtrace at breakpoint"
> +
> +# Perform "info frame" to extract the frames address.
> +proc get_frame_address { {testname ""} } {
> +    global hex gdb_prompt
> +
> +    set testname "get_frame_address: ${testname}"
> +    set frame_address "unknown"
> +
> +    send_gdb "info frame\n"
> +    gdb_expect {
> +	-re ", frame at ($hex):\r\n.*\r\n$gdb_prompt $" {
> +	    set frame_address $expect_out(1,string)
> +	    pass $testname
> +	}
> +	-re ".*$gdb_prompt $" { fail $testname }
> +	timeout { fail "$testname (timeout)" }
> +    }
> +
> +    return $frame_address
> +}
> +
> +# Passes a list of addresses.  Return a new address that is not in the
> +# list.
> +proc get_new_address { {addresses {}} } {
> +    return [format "%#x" [expr [lindex $addresses [llength addresses]] + 0x10 ]]
> +}
> +
> +proc check_frame { level address function } {
> +    global hex gdb_prompt
> +
> +    set testname "check frame level ${level}"
> +    send_gdb "info frame\n"
> +    gdb_expect {
> +	-re "Stack level ${level}, frame at ($address):\r\n rip = $hex in ${function} \(\[^\r\n\]*\); saved rip = $hex\r\n.*\r\n$gdb_prompt $" {
> +	    pass $testname
> +	}
> +	-re ".*$gdb_prompt $" { fail $testname }
> +	timeout { fail "$testname (timeout)" }
> +    }
> +}
> +
> +# Select frame using level, but relying on this being the default
> +# action, so "frame 0" performs "frame level 0".
> +gdb_test "frame 0" "#0  frame_2.*"
> +set frame_0_address [ get_frame_address "frame 0" ]
> +gdb_test "frame 1" "#1  $hex in frame_1.*"
> +set frame_1_address [ get_frame_address "frame 1" ]
> +gdb_test "frame 2" "#2  $hex in main.*"
> +set frame_2_address [ get_frame_address "frame 2" ]
> +gdb_test "frame 3" "No frame at level 3\."
> +
> +# Find an address that matches no frame
> +set no_frame_address [ get_new_address [list $frame_0_address \
> +					     $frame_1_address \
> +					     $frame_2_address] ]
> +
> +# Select frame using 'level' specification.
> +gdb_test "frame level 0" "#0  frame_2.*"
> +gdb_test "frame level 1" "#1  $hex in frame_1.*"
> +gdb_test "frame level 2" "#2  $hex in main.*"
> +gdb_test "frame level 3" "No frame at level 3\."
> +
> +# Select frame by address.
> +gdb_test "frame address ${frame_0_address}" "#0  frame_2.*"
> +gdb_test "frame address ${frame_1_address}" "#1  $hex in frame_1.*"
> +gdb_test "frame address ${frame_2_address}" "#2  $hex in main.*"
> +gdb_test "frame address ${no_frame_address}" \
> +    "No frame at address ${no_frame_address}\."
> +
> +# Select frame by function.
> +gdb_test "frame function frame_2" "#0  frame_2.*"
> +gdb_test "frame function frame_1" "#1  $hex in frame_1.*"
> +gdb_test "frame function main" "#2  $hex in main.*"
> +# Check for a distinction between a known function not in the stack
> +# trace, and an unknown function.
> +gdb_test "frame function recursive" "No frame for function \"recursive\"."
> +gdb_test "frame function foo" "Function \"foo\" not defined."
> +
> +
> +gdb_test_no_output "select-frame 0"
> +check_frame "0" "${frame_0_address}" "frame_2"
> +gdb_test_no_output "select-frame 1"
> +check_frame "1" "${frame_1_address}" "frame_1"
> +gdb_test_no_output "select-frame 2"
> +check_frame "2" "${frame_2_address}" "main"
> +gdb_test "select-frame 3" "No frame at level 3\."
> +
> +gdb_test_no_output "select-frame level 0"
> +check_frame "0" "${frame_0_address}" "frame_2"
> +gdb_test_no_output "select-frame level 1"
> +check_frame "1" "${frame_1_address}" "frame_1"
> +gdb_test_no_output "select-frame level 2"
> +check_frame "2" "${frame_2_address}" "main"
> +gdb_test "select-frame level 3" "No frame at level 3\."
> +
> +gdb_test_no_output "select-frame address ${frame_0_address}"
> +check_frame "0" "${frame_0_address}" "frame_2"
> +gdb_test_no_output "select-frame address ${frame_1_address}"
> +check_frame "1" "${frame_1_address}" "frame_1"
> +gdb_test_no_output "select-frame address ${frame_2_address}"
> +check_frame "2" "${frame_2_address}" "main"
> +gdb_test "select-frame address ${no_frame_address}" \
> +    "No frame at address ${no_frame_address}\."
> +
> +gdb_test_no_output "select-frame function frame_2"
> +check_frame "0" "${frame_0_address}" "frame_2"
> +gdb_test_no_output "select-frame function frame_1"
> +check_frame "1" "${frame_1_address}" "frame_1"
> +gdb_test_no_output "select-frame function main"
> +check_frame "2" "${frame_2_address}" "main"
> +# Check for a distinction between a known function not in the stack
> +# trace, and an unknown function.
> +gdb_test "select-frame function recursive" \
> +    "No frame for function \"recursive\"."
> +gdb_test "select-frame function foo" \
> +    "Function \"foo\" not defined."
> +
> +# Now continue until we hit the breakpoint again.
> +gdb_continue_to_breakpoint frame_2
> +gdb_test "bt" \
> +    "#0  frame_2.*#1  $hex in recursive.*#2  $hex in recursive.*#3  $hex in recursive.*#4  $hex in main.*" \
> +    "backtrace at breakpoint with recursive frames"
> +
> +# Check "frame function" when a function name occurs multiple times in
> +# the stack.  The inner most (lowest level) should always be selected.
> +gdb_test "frame function frame_2" "#0  frame_2.*"
> +gdb_test "frame function recursive" "#1  $hex in recursive.*"
> +gdb_test "frame function recursive" "#1  $hex in recursive.*"
> +gdb_test "frame function main" "#4  $hex in main.*"
> +gdb_test "frame function recursive" "#1  $hex in recursive.*"
> diff --git a/gdb/testsuite/gdb.mi/mi-frame-selection.c b/gdb/testsuite/gdb.mi/mi-frame-selection.c
> new file mode 100644
> index 00000000000..70b992c9149
> --- /dev/null
> +++ b/gdb/testsuite/gdb.mi/mi-frame-selection.c
> @@ -0,0 +1,34 @@
> +/* Copyright 2018 Free Software Foundation, Inc.
> +
> +   This file is part of GDB.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
> +
> +int
> +frame_2 (void)
> +{
> +  return 0;
> +}
> +
> +int
> +frame_1 (void)
> +{
> +  return frame_2 ();
> +}
> +
> +int
> +main (void)
> +{
> +  return frame_1 ();
> +}
> diff --git a/gdb/testsuite/gdb.mi/mi-frame-selection.exp b/gdb/testsuite/gdb.mi/mi-frame-selection.exp
> new file mode 100644
> index 00000000000..e24c7179b9d
> --- /dev/null
> +++ b/gdb/testsuite/gdb.mi/mi-frame-selection.exp
> @@ -0,0 +1,89 @@
> +# Copyright 2018 Free Software Foundation, Inc.
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +
> +# Test the frame selection syntax with the -stack-select-frame command.
> +
> +load_lib mi-support.exp
> +set MIFLAGS "-i=mi"
> +
> +gdb_exit
> +if [mi_gdb_start] {
> +    continue
> +}
> +
> +standard_testfile
> +
> +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
> +    untested $testfile
> +    return -1
> +}
> +
> +mi_delete_breakpoints
> +mi_gdb_reinitialize_dir $srcdir/$subdir
> +mi_gdb_load ${binfile}
> +
> +mi_create_breakpoint "frame_2" \
> +    "break-insert into frame_2" \
> +    -number 1 -func frame_2 -file ".*${srcfile}"
> +
> +mi_run_cmd
> +mi_expect_stop "breakpoint-hit" "frame_2" "" ".*${srcfile}" \
> +	".*" { "" "disp=\"keep\"" } "run to breakpoint"
> +
> +mi_gdb_test "-stack-info-depth" \
> +    "\\^done,depth=\"3\"" \
> +    "stack info-depth"
> +
> +for {set i 0} {$i < 5} {incr i} {
> +
> +    if { $i < 3 } then {
> +	mi_gdb_test "-stack-select-frame $i" \
> +	    {\^done} \
> +	    "-stack-select-frame $i"
> +
> +	mi_gdb_test "-stack-info-frame" \
> +	    "\\^done,frame=\\\{level=\"$i\"\[^\}\]+\\\}" \
> +	    "check level has has changed to $i"
> +
> +    } else {
> +	mi_gdb_test "-stack-select-frame view $i" \
> +	    {\^done} \
> +	    "-stack-select-frame view frame at $i"
> +
> +	mi_gdb_test "-stack-info-frame" \
> +	    "\\^done,frame=\\\{level=\"0\"\[^\}\]+\\\}" \
> +	    "check level has has changed to 0, when creating a frame at sp=$i"
> +    }
> +}
> +
> +mi_gdb_test "-stack-select-frame 5" \
> +    {\^error,msg="No frame at level 5."} \
> +    "-stack-select-frame 5, with no keyword"
> +
> +mi_gdb_test "-stack-select-frame level 5" \
> +    {\^error,msg="No frame at level 5."} \
> +    "-stack-select-frame 5, using 'level' keyword"
> +
> +mi_gdb_test "-stack-select-frame function main" \
> +    {\^done} \
> +    "-stack-select-frame select frame for function main"
> +
> +mi_gdb_test "-stack-info-frame" \
> +    "\\^done,frame=\\\{level=\"2\"\[^\}\]+\\\}" \
> +    "check level has has changed to 2 for function main"
> +
> +mi_gdb_test "-stack-select-frame function unknown_function" \
> +    {\^error,msg=\"Function \\\"unknown_function\\\" not defined.\"} \
> +    "-stack-select-frame check error on undefined function name"
> -- 
> 2.14.4
> 

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCHv6] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-08-28 18:03                     ` [PATCHv6] " Andrew Burgess
  2018-08-28 18:20                       ` Eli Zaretskii
  2018-09-05  7:46                       ` PING: " Andrew Burgess
@ 2018-09-13 18:02                       ` Pedro Alves
  2018-09-18 23:01                         ` Andrew Burgess
  2 siblings, 1 reply; 35+ messages in thread
From: Pedro Alves @ 2018-09-13 18:02 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

Hi,

On 08/28/2018 07:03 PM, Andrew Burgess wrote:
> I believe that this variant addresses all existing review comments.
> Philippe provided additional feedback in this message:
> 
>    https://sourceware.org/ml/gdb-patches/2018-08/msg00349.html
> 
> but I believe this was more about things we need to do to unify the
> docs/code/comments on one of either 'number' or 'level', rather than
> things that need to be fixed before this patch could be merged.
> 
> This version of the patch is based off v5(B) and uses the keyword
> 'level' instead of 'number'.
> 
> The only changes over the previous version are an additional change in
> the docs as previously discussed with Eli, and I've rebased the patch
> onto current HEAD (and retested).
> 
> OK to commit?

First off, I'm very much for this patch, having argued for something
like it before <https://sourceware.org/ml/gdb/2014-11/msg00028.html>.

As we discussed at the Cauldron, my main concern about this
patch/direction is the "view" variant.  It's a bit odd in that
it doesn't "select" a frame from the current stack, like the
other variants, which makes it a little odd, but I understand where
it's coming from.  There's more to it however.  Let me go over
my main concern.

Specifically, if you do "frame view 0xADDR", and then do "up/down", then
GDB moves to the next/previous frame relative to the 0xADDR frame, as I
would expect.  That's fine.

However, if you do "backtrace", we still show a backtrace starting at the
thread's real current frame instead of at the selected "view" frame.
This is IMO inconsistent, and surprising.  Though, it's also how current gdb
behaves with "frame 0xADDR", so it's not really a behavior change.

Also, related, after "frame view 0xADDR", if you do anything that might
flush the frame cache, then you lose the "view"'d frame.  E.g., that
can be with "info threads" (if you have multiple threads"), or if
in non-stop mode while threads are running, some thread hits some
temporary event (in which case I think GDB just loses the viewed
frame without the user realizing).  Again, these aren't really
problems the patch is adding, since you get the same problems
with the current "frame 0xADDR":

 (gdb) frame 0x0011
 #0  0x0000000000000000 in ?? ()
 (gdb) frame
 #0  0x0000000000000000 in ?? ()
 (gdb) info threads 
   Id   Target Id                                     Frame 
 * 1    Thread 0x7ffff7fb6740 (LWP 32581) "threads"   0x0000000000000000 in ?? ()
   2    Thread 0x7ffff7803700 (LWP 32585) "threads"   0x00007ffff78c7460 in __GI___nanosleep  
   3    Thread 0x7ffff7002700 (LWP 32586) "threads"   0x00007ffff78c7460 in __GI___nanosleep  
 (gdb) info threads 
   Id   Target Id                                     Frame 
 * 1    Thread 0x7ffff7fb6740 (LWP 32581) "threads"   0x00007ffff7bc28ad in __pthread_join
   2    Thread 0x7ffff7803700 (LWP 32585) "threads"   0x00007ffff78c7460 in __GI___nanosleep
   3    Thread 0x7ffff7002700 (LWP 32586) "threads"   0x00007ffff78c7460 in __GI___nanosleep
 (gdb) frame
 #0  0x00007ffff7bc28ad in __pthread_join () at pthread_join.c:90
 90          lll_wait_tid (pd->tid);
 (gdb) 

Notice also how thread 1's "Frame" column showed the "viewed" frame
the first time, which is incorrect, IMO.  The second time we see
the thread's current frame, because the first "info threads" command
lost the "viewed" frame, as can be also seen in the last "frame"
command.

Again, this is not a new problem (and above I'm not using
your patch to show it).  But, it does highlight problems with
the "frame view" direction that (I think I mentioned before)
should ideally be addressed somehow.  Particularly if we're now
proposing adding some new UI to expose the functionality, it
makes me wonder whether it's a good idea to expose functionality
in the UI that we may end up removing soon enough.  I.e., my
concern is with whether addressing this would result in
getting rid of "frame view".

E.g., the idea of using "frame level 0" to get back to the
thread's real current frame (as mentioned in the documentation
bits added by this patch) conflicts with the fact that
"up/down" after "frame view" moves the selected stack
frame within the viewed stack, which means those frames
also have a relative frame level within that viewed
stack.  One could very reasonably expect that
"frame view 0xADDR; up; frame level 0" would get you back
to the frame viewed at 0xADDR.

At the Cauldron, you had a nice idea of adding some separate
command to create alternate stacks, like "(gdb) create-stack 0xADDR",
coupled with some way to list the created stacks, like "info stack",
and a way to switch between the created stacks and the thread's real
current stack.  I think something like that would be really nice.

I'm not going to insist on going that direction before
accepting this patch, though I must admit that I'd
be delighted to seeing it explored.  I'll reserve the
right to not fell guilty about (someone) ripping out "view"
later on if this goes in as is.

Actually, "view" is being exposed at the MI level, which
makes me a bit more nervous about it, since it's a bit
harder to change MI and CLI.  Maybe we should just not
expose that at the MI level...  Bleh.

Onwards with the current patch.  See comments below.

> 
> The 'frame' command, and thanks to code reuse the 'info frame' and
> 'select-frame' commands, currently have an overloaded mechanism for
> selecting a frame.
> 
> These commands take one or two parameters, if it's one parameter then
> we first try to use the parameter as an integer to select a frame by
> level (or depth in the stack).  If that fails then we treat the
> parameter as an address and try to select a stack frame by
> stack-address.  If we still have not selected a stack frame, or we
> initially had two parameters, then GDB allows the user to view a stack
> frame that is not part of the current backtrace.  Internally, a new
> frame is created with the given stack and pc addresses, and this is
> shown to the user.
> 
> The result of this is that a typo by the user, entering the wrong stack
> frame level for example, can result in a brand new frame being viewed
> rather than an error.
> 
> The purpose of this commit is to remove this overloading, while still
> offering the same functionality through some new sub-commands.  By
> making the default behaviour of 'frame' (and friends) be to select a
> stack frame by level index, it is hoped that enough
> backwards-compatibility is maintained that users will not be overly
> inconvenienced.
> 
> The 'frame', 'select-frame', and 'info frame' commands now all take a
> frame specification string as an argument, this string can be any of the
> following:
> 
>   (1) An integer.  This is treated as a frame level.  If a frame for
>   that level does not exist then the user gets an error.
> 
>   (2) A string like 'level <LEVEL>', where <LEVEL> is a frame level
>   as in option (1) above.
> 
>   (3) A string like 'address <STACK-ADDRESS>', where <STACK-ADDRESS>
>   is a stack-frame address.  If there is no frame for this address
>   then the user gets an error.
> 
>   (4) A string like 'function <NAME>', where <NAME> is a function name,
>   the inner most frame for function <NAME> is selected.  If there is no
>   frame for function <NAME> then the user gets an error.
> 
>   (5) A string like 'view <STACK-ADDRESS>', this views a new frame
>   with stack address <STACK-ADDRESS>.
> 
>   (6) A string like 'view <STACK-ADDRESS> <PC-ADDRESS>', this views
>   a new frame with stack address <STACK-ADDRESS> an the pc <PC-ADDRESS>.

"an" -> "and"

> 
> This change assumes that the most common use of the commands like
> 'frame' is to select a frame by frame level, it is for this reason
> that this is the behaviour that is kept for backwards compatibility.
> Any of the alternative behaviours, which are assumed to be less used,
> now require a change in user behaviour.
> 
> The MI command '-stack-select-frame' has also changed in the same way.
> The default behaviour is to select a frame by level, but the other
> selection methods are also available.


Hopefully this won't break MI frontends.


> 
> gdb/ChangeLog:
> 
> 	(NEWS): Mention changes to frame related commands.
> 	* cli/cli-decode.c (add_cmd_suppress_notification): New function.
> 	(add_prefix_cmd_suppress_notification): New function.
> 	(add_com_suppress_notification): Call
> 	add_cmd_suppress_notification.
> 	* command.h (add_cmd_suppress_notification): Declare.
> 	(add_prefix_cmd_suppress_notification): Declare.
> 	* mi/mi-cmd-stack.c (mi_cmd_stack_select_frame): Use new function
> 	select_frame_from_spec.
> 	* stack.c (find_frame_for_function): Add declaration.
> 	(find_frame_for_address): New function.
> 	(parse_frame_specification): Delete function.
> 	(frame_selection_by_function_completer): New function.
> 	(info_frame_command): Rename to...
> 	(info_frame_command_core): ...this, and update parameter types.
> 	(select_frame_command): Rename to...
> 	(select_frame_command_core): ...this, and update parameter types.
> 	(frame_command): Rename to...
> 	(frame_command_core): ...this, and update parameter types.
> 	(class frame_command_helper): New class to wrap implementations of
> 	frame related sub-commands.
> 	(select_frame_from_spec): New function.
> 	(frame_apply_cmd_list): New static global.
> 	(frame_cmd_list): Make static.
> 	(select_frame_cmd_list): New global for sub-commands.
> 	(info_frame_cmd_list): New global for sub-commands.
> 	(_initialize_stack): Register sub-commands for 'frame',
> 	'select-frame', and 'info frame'.  Update 'frame apply' commands
> 	to use frame_apply_cmd_list.  Move function local static
> 	frame_apply_list to file static frame_apply_cmd_list for
> 	consistency.
> 	* stack.h (select_frame_command): Delete declarationn.
> 	(select_frame_from_spec): Declare new function.
> 
> gdb/doc/ChangeLog:
> 
> 	* gdb.texinfo (Frames): Rewrite the description of 'frame number'
> 	to highlight that the number is also the frame's level.
> 	(Selection): Rewrite documentation for 'frame' and 'select-frame'
> 	commands.
> 	(Frame Info): Rewrite documentation for 'info frame' command.
> 	(GDB/MI Stack Manipulation): Update description of
> 	'-stack-select-frame'.
> 
> gdb/testsuite/ChangeLog:
> 
> 	* gdb.base/frame-selection.exp: New file.
> 	* gdb.base/frame-selection.c: New file.
> 	* gdb.mi/mi-frame-selection.exp: New file.
> 	* gdb.mi/mi-frame-selection.c: New file.
> ---
>  gdb/ChangeLog                               |  36 ++
>  gdb/NEWS                                    |   8 +
>  gdb/cli/cli-decode.c                        |  44 ++-
>  gdb/command.h                               |  14 +
>  gdb/doc/ChangeLog                           |  10 +
>  gdb/doc/gdb.texinfo                         | 118 ++++--
>  gdb/mi/mi-cmd-stack.c                       |   4 +-
>  gdb/stack.c                                 | 535 +++++++++++++++++++---------
>  gdb/stack.h                                 |   2 +-
>  gdb/testsuite/ChangeLog                     |   7 +
>  gdb/testsuite/gdb.base/frame-selection.c    |  52 +++
>  gdb/testsuite/gdb.base/frame-selection.exp  | 156 ++++++++
>  gdb/testsuite/gdb.mi/mi-frame-selection.c   |  34 ++
>  gdb/testsuite/gdb.mi/mi-frame-selection.exp |  89 +++++
>  14 files changed, 903 insertions(+), 206 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.base/frame-selection.c
>  create mode 100644 gdb/testsuite/gdb.base/frame-selection.exp
>  create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.c
>  create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.exp
> 
> diff --git a/gdb/NEWS b/gdb/NEWS
> index 45514b10c51..b0b2d93939e 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -10,6 +10,14 @@
>  * DWARF index cache: GDB can now automatically save indices of DWARF
>    symbols on disk to speed up further loading of the same binaries.
>  
> +* Changes to the "frame", "select-frame", and "info frame" CLI
> +  commands, as well as the "-stack-select-frame" MI command.  These
> +  commands all now take a frame specification which is either a frame
> +  level, or one of the keywords 'level', 'address', 'function', or
> +  'view' followed by a parameter.  Selecting a frame by address, or
> +  viewing a frame outside the current backtrace now requires the use
> +  of a keyword.  Selecting a frame by level is unchanged.
> +
>  * New commands
>  
>  frame apply [all | COUNT | -COUNT | level LEVEL...] [FLAG]... COMMAND
> diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
> index 83dd67efe3f..5d798e877e8 100644
> --- a/gdb/cli/cli-decode.c
> +++ b/gdb/cli/cli-decode.c
> @@ -251,6 +251,23 @@ add_cmd (const char *name, enum command_class theclass,
>    return result;
>  }
>  
> +/* Add an element with a suppress notification to the LIST of commands.  */
> +
> +struct cmd_list_element *
> +add_cmd_suppress_notification (const char *name, enum command_class theclass,
> +			       cmd_const_cfunc_ftype *fun, const char *doc,
> +			       struct cmd_list_element **list,
> +			       int *suppress_notification)
> +{
> +  struct cmd_list_element *element;
> +
> +  element = add_cmd (name, theclass, fun, doc, list);
> +  element->suppress_notification = suppress_notification;
> +
> +  return element;
> +}
> +
> +
>  /* Deprecates a command CMD.
>     REPLACEMENT is the name of the command which should be used in
>     place of this command, or NULL if no such command exists.
> @@ -362,6 +379,25 @@ add_prefix_cmd (const char *name, enum command_class theclass,
>    return c;
>  }
>  
> +/* Like ADD_PREFIX_CMD but sets the suppress_notification pointer on the
> +   new command list element.  */
> +
> +struct cmd_list_element *
> +add_prefix_cmd_suppress_notification
> +               (const char *name, enum command_class theclass,
> +		cmd_const_cfunc_ftype *fun,
> +		const char *doc, struct cmd_list_element **prefixlist,
> +		const char *prefixname, int allow_unknown,
> +		struct cmd_list_element **list,
> +		int *suppress_notification)
> +{
> +  struct cmd_list_element *element
> +    = add_prefix_cmd (name, theclass, fun, doc, prefixlist,
> +		      prefixname, allow_unknown, list);
> +  element->suppress_notification = suppress_notification;
> +  return element;
> +}
> +
>  /* Like add_prefix_cmd but sets the abbrev_flag on the new command.  */
>  
>  struct cmd_list_element *
> @@ -893,12 +929,8 @@ add_com_suppress_notification (const char *name, enum command_class theclass,
>  			       cmd_const_cfunc_ftype *fun, const char *doc,
>  			       int *suppress_notification)
>  {
> -  struct cmd_list_element *element;
> -
> -  element = add_cmd (name, theclass, fun, doc, &cmdlist);
> -  element->suppress_notification = suppress_notification;
> -
> -  return element;
> +  return add_cmd_suppress_notification (name, theclass, fun, doc,
> +					&cmdlist, suppress_notification);
>  }
>  
>  /* Recursively walk the commandlist structures, and print out the
> diff --git a/gdb/command.h b/gdb/command.h
> index 3dde2475cb1..e3d55c2dcba 100644
> --- a/gdb/command.h
> +++ b/gdb/command.h
> @@ -148,6 +148,12 @@ extern struct cmd_list_element *add_cmd (const char *, enum command_class,
>  					 const char *,
>  					 struct cmd_list_element **);
>  
> +extern struct cmd_list_element *add_cmd_suppress_notification
> +			(const char *name, enum command_class theclass,
> +			 cmd_const_cfunc_ftype *fun, const char *doc,
> +			 struct cmd_list_element **list,
> +			 int *suppress_notification);
> +
>  extern struct cmd_list_element *add_alias_cmd (const char *, const char *,
>  					       enum command_class, int,
>  					       struct cmd_list_element **);
> @@ -165,6 +171,14 @@ extern struct cmd_list_element *add_prefix_cmd (const char *, enum command_class
>  						const char *, int,
>  						struct cmd_list_element **);
>  
> +extern struct cmd_list_element *add_prefix_cmd_suppress_notification
> +			(const char *name, enum command_class theclass,
> +			 cmd_const_cfunc_ftype *fun,
> +			 const char *doc, struct cmd_list_element **prefixlist,
> +			 const char *prefixname, int allow_unknown,
> +			 struct cmd_list_element **list,
> +			 int *suppress_notification);
> +
>  extern struct cmd_list_element *add_abbrev_prefix_cmd (const char *,
>  						       enum command_class,
>  						       cmd_const_cfunc_ftype *fun,
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 5068c0ac81f..1cafea97976 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -7393,12 +7393,14 @@
>  in a register called the @dfn{frame pointer register}
>  (@pxref{Registers, $fp}) while execution is going on in that frame.
>  
> +@cindex frame level
>  @cindex frame number
> -@value{GDBN} assigns numbers to all existing stack frames, starting with
> -zero for the innermost frame, one for the frame that called it,
> -and so on upward.  These numbers do not really exist in your program;
> -they are assigned by @value{GDBN} to give you a way of designating stack
> -frames in @value{GDBN} commands.
> +@value{GDBN} labels each existing stack frame with a @dfn{level}, a
> +number that is zero for the innermost frame, one for the frame that
> +called it, and so on upward.  These level numbers give you a way of
> +designating stack frames in @value{GDBN} commands.  The terms
> +@dfn{frame number} and @dfn{frame level} can be used interchangably to
> +describe this number.

Typo: "interchangably" -> "interchangeably"

>  
>  @c The -fomit-frame-pointer below perennially causes hbox overflow
>  @c underflow problems.
> @@ -7631,21 +7633,75 @@
>  @table @code
>  @kindex frame@r{, selecting}
>  @kindex f @r{(@code{frame})}
> -@item frame @var{n}
> -@itemx f @var{n}
> -Select frame number @var{n}.  Recall that frame zero is the innermost
> +@item frame @r{[} @var{frame-selection-spec} @r{]}
> +@item f @r{[} @var{frame-selection-spec} @r{]}
> +The @command{frame} command allows different stack frames to be
> +selected.  The @var{frame-selection-spec} can be any of the following:
> +
> +@table @code
> +@kindex frame level
> +@item @var{num}
> +@item level @var{num}
> +Select frame level @var{num}.  Recall that frame zero is the innermost
>  (currently executing) frame, frame one is the frame that called the
> -innermost one, and so on.  The highest-numbered frame is the one for
> -@code{main}.
> +innermost one, and so on.  The highest level frame is usually the one
> +for @code{main}.
> +
> +As this is the most common method of navigating the frame stack then
> +the string @command{level} can be dropped, the following two commands
> +are equivalent:

I find this style of using comma to split sentences confusing to
read.  The "then" doesn't seem to help for me.  I'd suggest
using "omit" instead of "drop".  Like:

 As this is the most common method of navigating the frame stack,
 the string @command{level} can be omitted.  For example, the following
 two commands are equivalent:

>  
> -@item frame @var{stack-addr} [ @var{pc-addr} ]
> -@itemx f @var{stack-addr} [ @var{pc-addr} ]
> -Select the frame at address @var{stack-addr}.  This is useful mainly if the
> -chaining of stack frames has been damaged by a bug, making it
> -impossible for @value{GDBN} to assign numbers properly to all frames.  In
> -addition, this can be useful when your program has multiple stacks and
> -switches between them.  The optional @var{pc-addr} can also be given to
> -specify the value of PC for the stack frame.
> +@smallexample
> +(@value{GDBP}) frame 3
> +(@value{GDBP}) frame level 3
> +@end smallexample
> +
> +@kindex frame address
> +@item address @var{stack-address}
> +Select the frame with stack address @var{stack-address}.  The
> +@var{stack-address} for a frame can be seen in the output of
> +@command{info frame}, for example:
> +
> +@smallexample
> +(gdb) info frame
> +Stack level 1, frame at 0x7fffffffda30:
> + rip = 0x40066d in b (amd64-entry-value.cc:59); saved rip 0x4004c5
> + tail call frame, caller of frame at 0x7fffffffda30
> + source language c++.
> + Arglist at unknown address.
> + Locals at unknown address, Previous frame's sp is 0x7fffffffda30
> +@end smallexample
> +
> +The @var{stack-address} for this frame is @code{0x7fffffffda30} as
> +indicated by the line:
> +
> +@smallexample
> +Stack level 1, frame at 0x7fffffffda30:
> +@end smallexample
> +
> +@kindex frame function
> +@item function @var{function-name}
> +Select the stack frame for function @var{function-name}.  If there are
> +multiple stack frames for function @var{function-name} then the inner
> +most stack frame is selected.
> +
> +@kindex frame view
> +@item view @var{stack-address} @r{[} @var{pc-addr} @r{]}
> +View a frame that is not part of @value{GDBN}'s backtrace.  The frame
> +viewed has stack address @var{stack-addr}, and optionally, a program
> +counter address of @var{pc-addr}.
> +
> +This is useful mainly if the chaining of stack frames has been
> +damaged by a bug, making it impossible for @value{GDBN} to assign
> +numbers properly to all frames.  In addition, this can be useful
> +when your program has multiple stacks and switches between them.
> +
> +When viewing a frame outside the current backtrace using
> +@command{frame view} then you can always return to the original
> +stack using one of the previous stack frame selection instructions,
> +for example @command{frame level 0}.
> +
> +@end table
>  
>  @kindex up
>  @item up @var{n}
> @@ -7688,11 +7744,13 @@
>  
>  @table @code
>  @kindex select-frame
> -@item select-frame
> +@item select-frame @r{[} @var{frame-selection-spec} @r{]}
>  The @code{select-frame} command is a variant of @code{frame} that does
>  not display the new frame after selecting it.  This command is
>  intended primarily for use in @value{GDBN} command scripts, where the
> -output might be unnecessary and distracting.
> +output might be unnecessary and distracting.  The
> +@var{frame-selection-spec} is as for the @command{frame} command
> +described in @ref{Selection, ,Selecting a Frame}.
>  
>  @kindex down-silently
>  @kindex up-silently
> @@ -7750,13 +7808,12 @@
>  something has gone wrong that has made the stack format fail to fit
>  the usual conventions.
>  
> -@item info frame @var{addr}
> -@itemx info f @var{addr}
> -Print a verbose description of the frame at address @var{addr}, without
> -selecting that frame.  The selected frame remains unchanged by this
> -command.  This requires the same kind of address (more than one for some
> -architectures) that you specify in the @code{frame} command.
> -@xref{Selection, ,Selecting a Frame}.
> +@item info frame @r{[} @var{frame-selection-spec} @r{]}
> +@itemx info f @r{[} @var{frame-selection-spec} @r{]}
> +Print a verbose description of the frame selected by
> +@var{frame-selection-spec}.  The @var{frame-selection-spec} is the
> +same as for the @command{frame} command (@pxref{Selection, ,Selecting
> +a Frame}).  The selected frame remains unchanged by this command.
>  
>  @kindex info args
>  @item info args
> @@ -30329,11 +30386,12 @@
>  @subsubheading Synopsis
>  
>  @smallexample
> - -stack-select-frame @var{framenum}
> + -stack-select-frame @var{frame-selection-spec}
>  @end smallexample
>  
> -Change the selected frame.  Select a different frame @var{framenum} on
> -the stack.
> +Change the selected frame, the @var{frame-selection-spec} is as for
> +the @command{frame} command described in @ref{Selection, ,Selecting a
> +Frame}.
>  
>  This command in deprecated in favor of passing the @samp{--frame}
>  option to every command.
> diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c
> index 52660bdd498..33ac7663ba8 100644
> --- a/gdb/mi/mi-cmd-stack.c
> +++ b/gdb/mi/mi-cmd-stack.c
> @@ -681,10 +681,8 @@ list_args_or_locals (enum what_to_list what, enum print_values values,
>  void
>  mi_cmd_stack_select_frame (const char *command, char **argv, int argc)
>  {
> -  if (argc == 0 || argc > 1)
> +  if (!select_frame_from_spec (argv, argc, 0))
>      error (_("-stack-select-frame: Usage: FRAME_SPEC"));
> -
> -  select_frame_command (argv[0], 1 /* not used */ );
>  }
>  
>  void
> diff --git a/gdb/stack.c b/gdb/stack.c
> index 40ff99b8fa6..68029f56412 100644
> --- a/gdb/stack.c
> +++ b/gdb/stack.c
> @@ -102,6 +102,9 @@ static void set_last_displayed_sal (int valid,
>  				    struct symtab *symtab,
>  				    int line);
>  
> +static struct frame_info *find_frame_for_function (const char *);
> +static struct frame_info *find_frame_for_address (CORE_ADDR);
> +
>  /* Zero means do things normally; we are interacting directly with the
>     user.  One means print the full filename and linenumber when a
>     frame is printed, and do so in a format emacs18/emacs19.22 can
> @@ -1258,134 +1261,34 @@ print_frame (struct frame_info *frame, int print_level,
>  }
>  \f
>  
> -/* Read a frame specification in whatever the appropriate format is from
> -   FRAME_EXP.  Call error() if the specification is in any way invalid (so
> -   this function never returns NULL).  When SELECTED_FRAME_P is non-NULL
> -   set its target to indicate that the default selected frame was used.  */
> +/* Completion function for "frame function", "info frame function", and
> +   "select-frame function" commands.  */
>  
> -static struct frame_info *
> -parse_frame_specification (const char *frame_exp, int *selected_frame_p)
> +void
> +frame_selection_by_function_completer (struct cmd_list_element *ignore,
> +				       completion_tracker &tracker,
> +				       const char *text, const char *word)
>  {
> -  int numargs;
> -  struct value *args[4];
> -  CORE_ADDR addrs[ARRAY_SIZE (args)];
> -
> -  if (frame_exp == NULL)
> -    numargs = 0;
> -  else
> -    {
> -      numargs = 0;
> -      while (1)
> -	{
> -	  const char *p;
> -
> -	  /* Skip leading white space, bail of EOL.  */
> -	  frame_exp = skip_spaces (frame_exp);
> -	  if (!*frame_exp)
> -	    break;
> -
> -	  /* Parse the argument, extract it, save it.  */
> -	  for (p = frame_exp;
> -	       *p && !ISSPACE (*p);
> -	       p++);
> -	  std::string addr_string (frame_exp, p - frame_exp);
> -	  frame_exp = p;
> -	  
> -	  /* NOTE: Parse and evaluate expression, but do not use
> -	     functions such as parse_and_eval_long or
> -	     parse_and_eval_address to also extract the value.
> -	     Instead value_as_long and value_as_address are used.
> -	     This avoids problems with expressions that contain
> -	     side-effects.  */
> -	  if (numargs >= ARRAY_SIZE (args))
> -	    error (_("Too many args in frame specification"));
> -	  args[numargs++] = parse_and_eval (addr_string.c_str ());
> -	}
> -    }
> -
> -  /* If no args, default to the selected frame.  */
> -  if (numargs == 0)
> -    {
> -      if (selected_frame_p != NULL)
> -	(*selected_frame_p) = 1;
> -      return get_selected_frame (_("No stack."));
> -    }
> -
> -  /* None of the remaining use the selected frame.  */
> -  if (selected_frame_p != NULL)
> -    (*selected_frame_p) = 0;
> -
> -  /* Assume the single arg[0] is an integer, and try using that to
> -     select a frame relative to current.  */
> -  if (numargs == 1)
> -    {
> -      struct frame_info *fid;
> -      int level = value_as_long (args[0]);
> -
> -      fid = find_relative_frame (get_current_frame (), &level);
> -      if (level == 0)
> -	/* find_relative_frame was successful.  */
> -	return fid;
> -    }
> -
> -  /* Convert each value into a corresponding address.  */
> -  {
> -    int i;
> -
> -    for (i = 0; i < numargs; i++)
> -      addrs[i] = value_as_address (args[i]);
> -  }
> -
> -  /* Assume that the single arg[0] is an address, use that to identify
> -     a frame with a matching ID.  Should this also accept stack/pc or
> -     stack/pc/special.  */
> -  if (numargs == 1)
> -    {
> -      struct frame_id id = frame_id_build_wild (addrs[0]);
> -      struct frame_info *fid;
> -
> -      /* If (s)he specifies the frame with an address, he deserves
> -	 what (s)he gets.  Still, give the highest one that matches.
> -	 (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't
> -	 know).  */
> -      for (fid = get_current_frame ();
> -	   fid != NULL;
> -	   fid = get_prev_frame (fid))
> -	{
> -	  if (frame_id_eq (id, get_frame_id (fid)))
> -	    {
> -	      struct frame_info *prev_frame;
> -
> -	      while (1)
> -		{
> -		  prev_frame = get_prev_frame (fid);
> -		  if (!prev_frame
> -		      || !frame_id_eq (id, get_frame_id (prev_frame)))
> -		    break;
> -		  fid = prev_frame;
> -		}
> -	      return fid;
> -	    }
> -	}
> -      }
> -
> -  /* We couldn't identify the frame as an existing frame, but
> -     perhaps we can create one with a single argument.  */
> -  if (numargs == 1)
> -    return create_new_frame (addrs[0], 0);
> -  else if (numargs == 2)
> -    return create_new_frame (addrs[0], addrs[1]);
> -  else
> -    error (_("Too many args in frame specification"));
> +  /* This is used to complete function names within a stack.  It would be
> +     nice if instead of offering all available function names, we only
> +     offered functions that were actually in the stack.  However, this
> +     would probably mean unwinding the stack to completion, which could
> +     take too long (or on a corrupted stack, possibly not end).  For now I
> +     offer all symbol names as a safer choice.  */

That "I" reads a bit personal.  :-)

Also, that "probably" is actually a certainty.  While at it, we can drop
a little bit of redundancy, with this suggested alternative text:

  /* This is used to complete function names within a stack.  It would be
     nice if we only offered functions that were actually in the stack.
     However, this would mean unwinding the stack to completion, which
     could take too long, or on a corrupted stack, possibly not end.
     Instead, we offer all symbol names as a safer choice.  */

> +  collect_symbol_completion_matches (tracker,
> +				     complete_symbol_mode::EXPRESSION,
> +				     symbol_name_match_type::EXPRESSION,
> +				     text, word);


If we trickled down a search_domain, we could have this complete only on
function names (FUNCTIONS_DOMAIN), instead of all symbols.  But that's
for another day.

>  }
>  
> -/* Print verbosely the selected frame or the frame at address
> -   ADDR_EXP.  Absolutely all information in the frame is printed.  */
> +/* Core of all the "info frame" sub-commands.  Print verbosely the frame FI
> +   if SELECTED_FRAME_P is true then frame FI is the current frame, which
> +   was selected as a default due to the user not providing any arguments
> +   to select some other frame.  */


I have trouble parsing the last sentence.  Can you rephrase, and
potentially break it into more than one sentence?


>  
>  static void
> -info_frame_command (const char *addr_exp, int from_tty)
> +info_frame_command_core (struct frame_info *fi, bool selected_frame_p)
>  {
> -  struct frame_info *fi;
>    struct symbol *func;
>    struct symtab *s;
>    struct frame_info *calling_frame_info;
> @@ -1393,7 +1296,6 @@ info_frame_command (const char *addr_exp, int from_tty)
>    const char *funname = 0;
>    enum language funlang = language_unknown;
>    const char *pc_regname;
> -  int selected_frame_p;
>    struct gdbarch *gdbarch;
>    CORE_ADDR frame_pc;
>    int frame_pc_p;
> @@ -1401,7 +1303,6 @@ info_frame_command (const char *addr_exp, int from_tty)
>    CORE_ADDR caller_pc = 0;
>    int caller_pc_p = 0;
>  
> -  fi = parse_frame_specification (addr_exp, &selected_frame_p);
>    gdbarch = get_frame_arch (fi);
>  
>    /* Name of the value returned by get_frame_pc().  Per comments, "pc"
> @@ -1742,6 +1643,140 @@ trailing_outermost_frame (int count)
>    return trailing;
>  }
>  
> +/* The core of all the "select-frame" sub-commands.  Just wraps a call to
> +   SELECT_FRAME.  */
> +
> +static void
> +select_frame_command_core (struct frame_info *fi, bool ignored)
> +{
> +  struct frame_info *prev_frame = get_selected_frame_if_set ();
> +  select_frame (fi);
> +  if (get_selected_frame_if_set () != prev_frame)
> +    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
> +}
> +
> +/* The core of all the "frame" sub-commands.  Select frame FI, and if this
> +   means we change frame send out a change notification (otherwise, just
> +   reprint the current frame summary).   */
> +
> +static void
> +frame_command_core (struct frame_info *fi, bool ignored)
> +{
> +  struct frame_info *prev_frame = get_selected_frame_if_set ();
> +
> +  select_frame (fi);
> +  if (get_selected_frame_if_set () != prev_frame)
> +    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
> +  else
> +    print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME);
> +}
> +
> +/* The three commands 'frame', 'select-frame', and 'info frame' all have a
> +   common set of sub-commands that allow a specific frame to be selected.
> +   All of the sub-command functions are static methods within this class
> +   template which is then instantiated below.  */
> +
> +template <void (*FPTR) (struct frame_info *, bool)>


Could you document the frame_info/bool parameters?  E.g., adding pseudo
variable names, like:

  template <void (*FPTR) (struct frame_info * /*foo*/, bool /*bar*/)>

and then mentioning FOO and BAR in the comment.

> +class frame_command_helper
> +{
> +public:
> +
> +  /* The "frame level" family of commands.  The ARG is an integer that is
> +     the frame's level in the stack.  */
> +  static void
> +  level (const char *arg, int from_tty)
> +  {
> +    int level = value_as_long (parse_and_eval (arg));
> +    struct frame_info *fid
> +      = find_relative_frame (get_current_frame (), &level);
> +    if (level != 0)
> +      error (_("No frame at level %s."), arg);
> +    FPTR (fid, false);
> +  }
> +
> +  /* The "frame address" family of commands.  ARG is a stack-pointer
> +     address for an existing frame.  This command does not allow new
> +     frames to be created.  */
> +
> +  static void
> +  address (const char *arg, int from_tty)
> +  {
> +    CORE_ADDR addr = value_as_address (parse_and_eval (arg));
> +    struct frame_info *fid = find_frame_for_address (addr);
> +    if (fid == NULL)
> +      error (_("No frame at address %s."), arg);
> +    FPTR (fid, false);
> +  }
> +
> +  /* The "frame view" family of commands.  ARG is one or two addresses and
> +     is used to view a frame that might be outside the current backtrace.
> +     The addresses are stack-pointer address, and (optional) pc-address.  */
> +
> +  static void
> +  view (const char *args, int from_tty)
> +  {
> +    struct frame_info *fid;
> +
> +    if (args == NULL)
> +    error (_("Missing address argument to view a frame"));
> +
> +    gdb_argv argv (args);
> +
> +    if (argv.count () == 2)
> +      {
> +	CORE_ADDR addr[2];
> +
> +	addr [0] = value_as_address (parse_and_eval (argv[0]));
> +	addr [1] = value_as_address (parse_and_eval (argv[1]));
> +	fid = create_new_frame (addr[0], addr[1]);
> +      }
> +    else
> +      {
> +	CORE_ADDR addr = value_as_address (parse_and_eval (argv[0]));
> +	fid = create_new_frame (addr, false);
> +      }
> +    FPTR (fid, false);
> +  }
> +
> +  /* The "frame function" family of commands.  ARG is the name of a
> +     function within the stack, the first function (searching from frame
> +     0) with that name will be selected.  */
> +
> +  static void
> +  function (const char *arg, int from_tty)
> +  {
> +    if (arg == NULL)
> +      error (_("Missing function name argument"));
> +    struct frame_info *fid = find_frame_for_function (arg);
> +    if (fid == NULL)
> +      error (_("No frame for function \"%s\"."), arg);
> +    FPTR (fid, false);
> +  }
> +
> +  /* The "frame" base command, that is, when no sub-command is specified.
> +     If one argument is provided then we assume that this is a frame's
> +     level as historically, this was the supported command syntax that was
> +     used most often.
> +
> +     If no argument is provided, then the current frame is selected.  */
> +
> +  static void
> +  base_command (const char *arg, int from_tty)
> +  {
> +    if (arg == NULL)
> +      FPTR (get_selected_frame (_("No stack.")), true);
> +    else
> +      level (arg, from_tty);
> +  }
> +};
> +
> +/* Instantiate three FRAME_COMMAND_HELPER instances to implement the
> +   sub-commands for 'info frame', 'frame', and 'select-frame' commands.  */
> +
> +static frame_command_helper <info_frame_command_core> info_frame_cmd;
> +static frame_command_helper <frame_command_core> frame_cmd;
> +static frame_command_helper <select_frame_command_core> select_frame_cmd;
> +
>  /* Print briefly all stack frames or just the innermost COUNT_EXP
>     frames.  */
>  
> @@ -2230,37 +2265,43 @@ find_relative_frame (struct frame_info *frame, int *level_offset_ptr)
>    return frame;
>  }
>  
> -/* The "select_frame" command.  With no argument this is a NOP.
> -   Select the frame at level LEVEL_EXP if it is a valid level.
> -   Otherwise, treat LEVEL_EXP as an address expression and select it.
> -
> -   See parse_frame_specification for more info on proper frame
> -   expressions.  */
> -
> -void
> -select_frame_command (const char *level_exp, int from_tty)
> -{
> -  struct frame_info *prev_frame = get_selected_frame_if_set ();
> -
> -  select_frame (parse_frame_specification (level_exp, NULL));
> -  if (get_selected_frame_if_set () != prev_frame)
> -    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
> -}
> -
> -/* The "frame" command.  With no argument, print the selected frame
> -   briefly.  With an argument, behave like select_frame and then print
> -   the selected frame.  */
> +/* Select a frame using ARGV.  This is used from the MI -stack-select-frame
> +   to provide the same frame specification mechanism that the CLI has for
> +   commands like 'frame'.  The return value is true if the contents of
> +   ARGV looked like a sensible attempt to change the frame (an error might
> +   still be thrown though), or false if the contents of ARGV are not a
> +   correct frame specification.  */
>  
> -static void
> -frame_command (const char *level_exp, int from_tty)
> +bool
> +select_frame_from_spec (char **argv, int argc, int from_tty)
>  {
> -  struct frame_info *prev_frame = get_selected_frame_if_set ();
> +  if (argc == 1)
> +    select_frame_cmd.level (argv[0], from_tty);
> +  else if (argc == 2
> +	   && strncasecmp ("level", argv[0], strlen (argv[0])) == 0)
> +    select_frame_cmd.level (argv[1], from_tty);
> +  else if (argc == 2
> +	   && strncasecmp ("address", argv[0], strlen (argv[0])) == 0)
> +    select_frame_cmd.address (argv[1], from_tty);
> +  else if (argc == 2
> +	   && strncasecmp ("function", argv[0], strlen (argv[0])) == 0)
> +    select_frame_cmd.function (argv[1], from_tty);
> +  else if ((argc == 2 || argc == 3)
> +	   && strncasecmp ("view", argv[0], strlen (argv[0])) == 0)
> +    {
> +      std::string arg;
> +
> +      if (argc == 2)
> +	arg = string_printf ("%s", argv[1]);
> +      else
> +	arg = string_printf ("%s %s", argv[1], argv[2]);
>  
> -  select_frame (parse_frame_specification (level_exp, NULL));
> -  if (get_selected_frame_if_set () != prev_frame)
> -    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
> +      select_frame_cmd.view (arg.c_str (), from_tty);
> +    }
>    else
> -    print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME);
> +    return false;
> +
> +  return true;
>  }
>  
>  /* Select the frame up one or COUNT_EXP stack levels from the
> @@ -2716,13 +2757,61 @@ faas_command (const char *cmd, int from_tty)
>  }
>  
>  
> +/* Find inner-mode frame with frame address ADDRESS.  Return NULL if no
> +   matching frame can be found.  */
> +
> +static struct frame_info *
> +find_frame_for_address (CORE_ADDR address)
> +{
> +  struct frame_id id;
> +  struct frame_info *fid;
> +
> +  id = frame_id_build_wild (address);
> +
> +  /* If (s)he specifies the frame with an address, he deserves
> +     what (s)he gets.  Still, give the highest one that matches.
> +     (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't
> +     know).  */
> +  for (fid = get_current_frame ();
> +       fid != NULL;
> +       fid = get_prev_frame (fid))
> +    {
> +      if (frame_id_eq (id, get_frame_id (fid)))
> +	{
> +	  struct frame_info *prev_frame;
> +
> +	  while (1)
> +	    {
> +	      prev_frame = get_prev_frame (fid);
> +	      if (!prev_frame
> +		  || !frame_id_eq (id, get_frame_id (prev_frame)))
> +		break;
> +	      fid = prev_frame;
> +	    }
> +	  return fid;
> +	}
> +    }
> +  return NULL;
> +}
> +
> +\f
> +
> +/* Commands with a prefix of `frame apply'.  */
> +static struct cmd_list_element *frame_apply_cmd_list = NULL;
> +
>  /* Commands with a prefix of `frame'.  */
> -struct cmd_list_element *frame_cmd_list = NULL;
> +static struct cmd_list_element *frame_cmd_list = NULL;
> +
> +/* Commands with a prefix of `select frame'.  */
> +static struct cmd_list_element *select_frame_cmd_list = NULL;
> +
> +/* Commands with a prefix of `info frame'.  */
> +static struct cmd_list_element *info_frame_cmd_list = NULL;
>  
>  void
>  _initialize_stack (void)
>  {
> -  static struct cmd_list_element *frame_apply_list = NULL;
> +  struct cmd_list_element *cmd;
>  
>    add_com ("return", class_stack, return_command, _("\
>  Make selected stack frame return to its caller.\n\
> @@ -2746,12 +2835,12 @@ An argument says how many frames down to go."));
>  Same as the `down' command, but does not print anything.\n\
>  This is useful in command scripts."));
>  
> -  add_prefix_cmd ("frame", class_stack, frame_command, _("\
> -Select and print a stack frame.\nWith no argument, \
> -print the selected stack frame.  (See also \"info frame\").\n\
> -An argument specifies the frame to select.\n\
> -It can be a stack frame number or the address of the frame."),
> -		  &frame_cmd_list, "frame ", 1, &cmdlist);
> +  add_prefix_cmd ("frame", class_stack,
> +                  &frame_cmd.base_command, _("\
> +Select and print a stack frame.\n\
> +With no argument, print the selected stack frame.  (See also \"info frame\").\n\
> +A single numerical argument specifies the frame to select."),
> +                  &frame_cmd_list, "frame ", 1, &cmdlist);
>  
>    add_com_alias ("f", "frame", class_stack, 1);
>  
> @@ -2769,7 +2858,7 @@ or produces no output."
>  Usage: frame apply COUNT [FLAG]... COMMAND\n\
>  With a negative COUNT argument, applies the command on outermost -COUNT frames.\n"
>  FRAME_APPLY_FLAGS_HELP),
> -		  &frame_apply_list, "frame apply ", 1, &frame_cmd_list);
> +		  &frame_apply_cmd_list, "frame apply ", 1, &frame_cmd_list);
>  
>    add_cmd ("all", class_stack, frame_apply_all_command,
>  	   _("\
> @@ -2777,7 +2866,7 @@ Apply a command to all frames.\n\
>  \n\
>  Usage: frame apply all [FLAG]... COMMAND\n"
>  FRAME_APPLY_FLAGS_HELP),
> -	   &frame_apply_list);
> +	   &frame_apply_cmd_list);
>  
>    add_cmd ("level", class_stack, frame_apply_level_command,
>  	   _("\
> @@ -2786,19 +2875,97 @@ Apply a command to a list of frames.\n\
>  Usage: frame apply level LEVEL... [FLAG]... COMMAND\n\
>  ID is a space-separated list of LEVELs of frames to apply COMMAND on.\n"
>  FRAME_APPLY_FLAGS_HELP),
> -	   &frame_apply_list);
> +	   &frame_apply_cmd_list);
>  
>    add_com ("faas", class_stack, faas_command, _("\
>  Apply a command to all frames (ignoring errors and empty output).\n\
>  Usage: faas COMMAND\n\
>  shortcut for 'frame apply all -s COMMAND'"));
>  
> -  add_com_suppress_notification ("select-frame", class_stack, select_frame_command, _("\
> +
> +  add_prefix_cmd ("frame", class_stack,
> +		  &frame_cmd.base_command, _("\
> +Select and print a stack frame.\n\
> +With no argument, print the selected stack frame.  (See also \"info frame\").\n\
> +A single numerical argument specifies the frame to select."),
> +		  &frame_cmd_list, "frame ", 1, &cmdlist);
> +  add_com_alias ("f", "frame", class_stack, 1);
> +
> +  add_cmd ("address", class_stack, &frame_cmd.address,
> +	   _("\
> +Select and print a stack frame by stack address\n\
> +\n\
> +Usage: frame address STACK-ADDRESS"),
> +	   &frame_cmd_list);
> +
> +  add_cmd ("view", class_stack, &frame_cmd.view,
> +	   _("\
> +View a stack frame that might be outside the current backtrace.\n\
> +\n\
> +Usage: frame view STACK-ADDRESS\n\
> +       frame view STACK-ADDRESS PC-ADDRESS"),
> +	   &frame_cmd_list);
> +
> +  cmd = add_cmd ("function", class_stack, &frame_cmd.function,
> +	   _("\
> +Select and print a stack frame by function name.\n\
> +\n\
> +Usage: frame function NAME\n\
> +\n\
> +The innermost frame that visited function NAME is selected."),
> +	   &frame_cmd_list);
> +  set_cmd_completer (cmd, frame_selection_by_function_completer);
> +
> +
> +  add_cmd ("level", class_stack, &frame_cmd.level,
> +	   _("\
> +Select and print a stack frame by level.\n\
> +\n\
> +Usage: frame level LEVEL"),
> +	   &frame_cmd_list);
> +
> +  cmd = add_prefix_cmd_suppress_notification ("select-frame", class_stack,
> +		      &select_frame_cmd.base_command, _("\
>  Select a stack frame without printing anything.\n\
> -An argument specifies the frame to select.\n\
> -It can be a stack frame number or the address of the frame."),
> +A single numerical argument specifies the frame to select."),
> +		      &select_frame_cmd_list, "select-frame ", 1, &cmdlist,
> +		      &cli_suppress_notification.user_selected_context);
> +
> +  add_cmd_suppress_notification ("address", class_stack,
> +			 &select_frame_cmd.address, _("\
> +Select a stack frame by stack address.\n\
> +\n\
> +Usage: select-frame address STACK-ADDRESS"),
> +			 &select_frame_cmd_list,
> +			 &cli_suppress_notification.user_selected_context);
> +
> +
> +  add_cmd_suppress_notification ("view", class_stack,
> +		 &select_frame_cmd.view, _("\
> +Select a stack frame that might be outside the current backtrace.\n\
> +\n\
> +Usage: select-frame view STACK-ADDRESS\n\
> +       select-frame view STACK-ADDRESS PC-ADDRESS"),
> +		 &select_frame_cmd_list,
>  		 &cli_suppress_notification.user_selected_context);
>  
> +  cmd = add_cmd_suppress_notification ("function", class_stack,
> +	       &select_frame_cmd.function, _("\
> +Select a stack frame by function name.\n\
> +\n\
> +Usage: select-frame function NAME"),
> +	       &select_frame_cmd_list,
> +	       &cli_suppress_notification.user_selected_context);
> +  set_cmd_completer (cmd, frame_selection_by_function_completer);
> +
> +  add_cmd_suppress_notification ("level", class_stack,
> +			 &select_frame_cmd.level, _("\
> +Select a stack frame by level.\n\
> +\n\
> +Usage: select-frame level LEVEL"),
> +			 &select_frame_cmd_list,
> +			 &cli_suppress_notification.user_selected_context);
> +
>    add_com ("backtrace", class_stack, backtrace_command, _("\
>  Print backtrace of all stack frames, or innermost COUNT frames.\n\
>  Usage: backtrace [QUALIFIERS]... [COUNT]\n\
> @@ -2812,9 +2979,45 @@ on this backtrace."));
>    add_info ("stack", backtrace_command,
>  	    _("Backtrace of the stack, or innermost COUNT frames."));
>    add_info_alias ("s", "stack", 1);
> -  add_info ("frame", info_frame_command,
> -	    _("All about selected stack frame, or frame at ADDR."));
> +
> +  add_prefix_cmd ("frame", class_info, &info_frame_cmd.base_command,
> +		  _("All about the selected stack frame.\n\
> +With no arguments, displays information about the currently selected stack\n\
> +frame.  Alternatively a frame specification may be provided (See \"frame\")\n\
> +the information is then printed about the specified frame."),
> +		  &info_frame_cmd_list, "info frame ", 1, &infolist);
>    add_info_alias ("f", "frame", 1);
> +
> +  add_cmd ("address", class_stack, &info_frame_cmd.address,
> +	   _("\
> +Print information about a stack frame selected by stack address.\n\
> +\n\
> +Usage: info frame address STACK-ADDRESS"),
> +	   &info_frame_cmd_list);
> +
> +  add_cmd ("view", class_stack, &info_frame_cmd.view,
> +	   _("\
> +Print information about a stack frame outside the current backtrace.\n\
> +\n\
> +Usage: info frame view STACK-ADDRESS\n\
> +       info frame view STACK-ADDRESS PC-ADDRESS"),
> +	   &info_frame_cmd_list);
> +
> +  cmd = add_cmd ("function", class_stack, &info_frame_cmd.function,
> +	   _("\
> +Print information about a stack frame selected by function name.\n\
> +\n\
> +Usage: info frame function NAME"),
> +	   &info_frame_cmd_list);
> +  set_cmd_completer (cmd, frame_selection_by_function_completer);
> +
> +  add_cmd ("level", class_stack, &info_frame_cmd.level,
> +	   _("\
> +Print information about a stack frame selected by level.\n\
> +\n\
> +Usage: info frame level LEVEL"),
> +	   &info_frame_cmd_list);
> +
>    add_info ("locals", info_locals_command,
>  	    _("Local variables of current stack frame."));
>    add_info ("args", info_args_command,
> diff --git a/gdb/stack.h b/gdb/stack.h
> index ca190efa9c3..29794872d70 100644
> --- a/gdb/stack.h
> +++ b/gdb/stack.h
> @@ -20,7 +20,7 @@
>  #ifndef STACK_H
>  #define STACK_H
>  
> -void select_frame_command (const char *level_exp, int from_tty);
> +bool select_frame_from_spec (char **argv, int argc, int from_tty);
>  
>  gdb::unique_xmalloc_ptr<char> find_frame_funname (struct frame_info *frame,
>  						  enum language *funlang,
> diff --git a/gdb/testsuite/gdb.base/frame-selection.c b/gdb/testsuite/gdb.base/frame-selection.c
> new file mode 100644
> index 00000000000..f3d81f223e0
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/frame-selection.c
> @@ -0,0 +1,52 @@
> +/* Copyright 2018 Free Software Foundation, Inc.
> +
> +   This file is part of GDB.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
> +
> +int
> +frame_2 (void)
> +{
> +  return 0;
> +}
> +
> +int
> +frame_1 (void)
> +{
> +  return frame_2 ();
> +}
> +
> +int
> +recursive (int arg)
> +{
> +  int v;
> +
> +  if (arg < 2)
> +    v = recursive (arg + 1);
> +  else
> +    v = frame_2 ();
> +
> +  return v;
> +}
> +
> +int
> +main (void)
> +{
> +  int i, j;
> +
> +  i = frame_1 ();
> +  j = recursive (0);
> +
> +  return i + j;
> +}
> diff --git a/gdb/testsuite/gdb.base/frame-selection.exp b/gdb/testsuite/gdb.base/frame-selection.exp
> new file mode 100644
> index 00000000000..1b3649b2700
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/frame-selection.exp
> @@ -0,0 +1,156 @@
> +# Copyright 2018 Free Software Foundation, Inc.
> +
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +

Please add a sentence mentioning what the testcase is about.

> +standard_testfile
> +
> +if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug}]} {
> +    return -1
> +}
> +
> +runto_main
> +gdb_breakpoint frame_2
> +gdb_continue_to_breakpoint frame_2
> +
> +gdb_test "bt" "#0  frame_2.*#1  $hex in frame_1.*#2  $hex in main.*" "backtrace at breakpoint"
> +
> +# Perform "info frame" to extract the frames address.
> +proc get_frame_address { {testname ""} } {
> +    global hex gdb_prompt
> +
> +    set testname "get_frame_address: ${testname}"
> +    set frame_address "unknown"
> +
> +    send_gdb "info frame\n"
> +    gdb_expect {
> +	-re ", frame at ($hex):\r\n.*\r\n$gdb_prompt $" {
> +	    set frame_address $expect_out(1,string)
> +	    pass $testname
> +	}
> +	-re ".*$gdb_prompt $" { fail $testname }
> +	timeout { fail "$testname (timeout)" }
> +    }

Why gdb_expect instead of gdb_test_multiple?

> +
> +    return $frame_address
> +}
> +
> +# Passes a list of addresses.  Return a new address that is not in the
> +# list.

WDYM by "Passes"?

> +proc get_new_address { {addresses {}} } {
> +    return [format "%#x" [expr [lindex $addresses [llength addresses]] + 0x10 ]]
> +}
> +
> +proc check_frame { level address function } {

Missing comment.

> +    global hex gdb_prompt
> +
> +    set testname "check frame level ${level}"
> +    send_gdb "info frame\n"
> +    gdb_expect {
> +	-re "Stack level ${level}, frame at ($address):\r\n rip = $hex in ${function} \(\[^\r\n\]*\); saved rip = $hex\r\n.*\r\n$gdb_prompt $" {

"rip" is x86-64-specific.  Won't work in other ports.

> +	    pass $testname
> +	}
> +	-re ".*$gdb_prompt $" { fail $testname }
> +	timeout { fail "$testname (timeout)" }
> +    }

gdb_test_multiple ?

> +}
> +
> +# Select frame using level, but relying on this being the default
> +# action, so "frame 0" performs "frame level 0".
> +gdb_test "frame 0" "#0  frame_2.*"
> +set frame_0_address [ get_frame_address "frame 0" ]
> +gdb_test "frame 1" "#1  $hex in frame_1.*"
> +set frame_1_address [ get_frame_address "frame 1" ]
> +gdb_test "frame 2" "#2  $hex in main.*"
> +set frame_2_address [ get_frame_address "frame 2" ]
> +gdb_test "frame 3" "No frame at level 3\."
> +
> +# Find an address that matches no frame

Missing period.

> +set no_frame_address [ get_new_address [list $frame_0_address \
> +					     $frame_1_address \
> +					     $frame_2_address] ]
> +
> +# Select frame using 'level' specification.
> +gdb_test "frame level 0" "#0  frame_2.*"
> +gdb_test "frame level 1" "#1  $hex in frame_1.*"
> +gdb_test "frame level 2" "#2  $hex in main.*"
> +gdb_test "frame level 3" "No frame at level 3\."
> +
> +# Select frame by address.
> +gdb_test "frame address ${frame_0_address}" "#0  frame_2.*"
> +gdb_test "frame address ${frame_1_address}" "#1  $hex in frame_1.*"
> +gdb_test "frame address ${frame_2_address}" "#2  $hex in main.*"
> +gdb_test "frame address ${no_frame_address}" \
> +    "No frame at address ${no_frame_address}\."

This is going to include the frames' addresses in gdb.sum,
which are not going to be stable.  Please add explicit test names
that avoid it.  Other tests below seem to have the same issue.

> +
> +# Select frame by function.
> +gdb_test "frame function frame_2" "#0  frame_2.*"
> +gdb_test "frame function frame_1" "#1  $hex in frame_1.*"
> +gdb_test "frame function main" "#2  $hex in main.*"
> +# Check for a distinction between a known function not in the stack
> +# trace, and an unknown function.
> +gdb_test "frame function recursive" "No frame for function \"recursive\"."
> +gdb_test "frame function foo" "Function \"foo\" not defined."
> +
> +
> +gdb_test_no_output "select-frame 0"
> +check_frame "0" "${frame_0_address}" "frame_2"
> +gdb_test_no_output "select-frame 1"
> +check_frame "1" "${frame_1_address}" "frame_1"
> +gdb_test_no_output "select-frame 2"
> +check_frame "2" "${frame_2_address}" "main"
> +gdb_test "select-frame 3" "No frame at level 3\."
> +
> +gdb_test_no_output "select-frame level 0"
> +check_frame "0" "${frame_0_address}" "frame_2"
> +gdb_test_no_output "select-frame level 1"
> +check_frame "1" "${frame_1_address}" "frame_1"
> +gdb_test_no_output "select-frame level 2"
> +check_frame "2" "${frame_2_address}" "main"
> +gdb_test "select-frame level 3" "No frame at level 3\."
> +
> +gdb_test_no_output "select-frame address ${frame_0_address}"
> +check_frame "0" "${frame_0_address}" "frame_2"
> +gdb_test_no_output "select-frame address ${frame_1_address}"
> +check_frame "1" "${frame_1_address}" "frame_1"
> +gdb_test_no_output "select-frame address ${frame_2_address}"
> +check_frame "2" "${frame_2_address}" "main"
> +gdb_test "select-frame address ${no_frame_address}" \
> +    "No frame at address ${no_frame_address}\."
> +
> +gdb_test_no_output "select-frame function frame_2"
> +check_frame "0" "${frame_0_address}" "frame_2"
> +gdb_test_no_output "select-frame function frame_1"
> +check_frame "1" "${frame_1_address}" "frame_1"
> +gdb_test_no_output "select-frame function main"
> +check_frame "2" "${frame_2_address}" "main"
> +# Check for a distinction between a known function not in the stack
> +# trace, and an unknown function.
> +gdb_test "select-frame function recursive" \
> +    "No frame for function \"recursive\"."
> +gdb_test "select-frame function foo" \
> +    "Function \"foo\" not defined."
> +
> +# Now continue until we hit the breakpoint again.
> +gdb_continue_to_breakpoint frame_2
> +gdb_test "bt" \
> +    "#0  frame_2.*#1  $hex in recursive.*#2  $hex in recursive.*#3  $hex in recursive.*#4  $hex in main.*" \
> +    "backtrace at breakpoint with recursive frames"
> +
> +# Check "frame function" when a function name occurs multiple times in
> +# the stack.  The inner most (lowest level) should always be selected.
> +gdb_test "frame function frame_2" "#0  frame_2.*"
> +gdb_test "frame function recursive" "#1  $hex in recursive.*"
> +gdb_test "frame function recursive" "#1  $hex in recursive.*"
> +gdb_test "frame function main" "#4  $hex in main.*"
> +gdb_test "frame function recursive" "#1  $hex in recursive.*"

Duplicate test names ?

 https://sourceware.org/gdb/wiki/GDBTestcaseCookbook#Make_sure_test_messages_are_unique

> diff --git a/gdb/testsuite/gdb.mi/mi-frame-selection.c b/gdb/testsuite/gdb.mi/mi-frame-selection.c
> new file mode 100644
> index 00000000000..70b992c9149
> --- /dev/null
> +++ b/gdb/testsuite/gdb.mi/mi-frame-selection.c
> @@ -0,0 +1,34 @@
> +/* Copyright 2018 Free Software Foundation, Inc.
> +
> +   This file is part of GDB.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
> +
> +int
> +frame_2 (void)
> +{
> +  return 0;
> +}
> +
> +int
> +frame_1 (void)
> +{
> +  return frame_2 ();
> +}
> +
> +int
> +main (void)
> +{
> +  return frame_1 ();
> +}
> diff --git a/gdb/testsuite/gdb.mi/mi-frame-selection.exp b/gdb/testsuite/gdb.mi/mi-frame-selection.exp
> new file mode 100644
> index 00000000000..e24c7179b9d
> --- /dev/null
> +++ b/gdb/testsuite/gdb.mi/mi-frame-selection.exp
> @@ -0,0 +1,89 @@
> +# Copyright 2018 Free Software Foundation, Inc.
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +
> +# Test the frame selection syntax with the -stack-select-frame command.
> +
> +load_lib mi-support.exp
> +set MIFLAGS "-i=mi"
> +
> +gdb_exit
> +if [mi_gdb_start] {
> +    continue
> +}
> +
> +standard_testfile
> +
> +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
> +    untested $testfile
> +    return -1
> +}
> +
> +mi_delete_breakpoints
> +mi_gdb_reinitialize_dir $srcdir/$subdir
> +mi_gdb_load ${binfile}
> +
> +mi_create_breakpoint "frame_2" \
> +    "break-insert into frame_2" \
> +    -number 1 -func frame_2 -file ".*${srcfile}"
> +
> +mi_run_cmd
> +mi_expect_stop "breakpoint-hit" "frame_2" "" ".*${srcfile}" \
> +	".*" { "" "disp=\"keep\"" } "run to breakpoint"
> +
> +mi_gdb_test "-stack-info-depth" \
> +    "\\^done,depth=\"3\"" \
> +    "stack info-depth"
> +
> +for {set i 0} {$i < 5} {incr i} {
> +
> +    if { $i < 3 } then {
> +	mi_gdb_test "-stack-select-frame $i" \
> +	    {\^done} \
> +	    "-stack-select-frame $i"
> +
> +	mi_gdb_test "-stack-info-frame" \
> +	    "\\^done,frame=\\\{level=\"$i\"\[^\}\]+\\\}" \
> +	    "check level has has changed to $i"


Double "has has".  "check" is redundant, these are
all tests and checks -- "level changed to $i" is just
as clear IMO.


> +
> +    } else {
> +	mi_gdb_test "-stack-select-frame view $i" \
> +	    {\^done} \
> +	    "-stack-select-frame view frame at $i"
> +
> +	mi_gdb_test "-stack-info-frame" \
> +	    "\\^done,frame=\\\{level=\"0\"\[^\}\]+\\\}" \
> +	    "check level has has changed to 0, when creating a frame at sp=$i"

Likewise.

> +    }
> +}
> +
> +mi_gdb_test "-stack-select-frame 5" \
> +    {\^error,msg="No frame at level 5."} \
> +    "-stack-select-frame 5, with no keyword"
> +
> +mi_gdb_test "-stack-select-frame level 5" \
> +    {\^error,msg="No frame at level 5."} \
> +    "-stack-select-frame 5, using 'level' keyword"
> +
> +mi_gdb_test "-stack-select-frame function main" \
> +    {\^done} \
> +    "-stack-select-frame select frame for function main"
> +
> +mi_gdb_test "-stack-info-frame" \
> +    "\\^done,frame=\\\{level=\"2\"\[^\}\]+\\\}" \
> +    "check level has has changed to 2 for function main"

Likewise.

> +
> +mi_gdb_test "-stack-select-frame function unknown_function" \
> +    {\^error,msg=\"Function \\\"unknown_function\\\" not defined.\"} \
> +    "-stack-select-frame check error on undefined function name"
> 

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCHv6] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-09-13 18:02                       ` Pedro Alves
@ 2018-09-18 23:01                         ` Andrew Burgess
  2018-09-19 16:26                           ` Pedro Alves
  0 siblings, 1 reply; 35+ messages in thread
From: Andrew Burgess @ 2018-09-18 23:01 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

* Pedro Alves <palves@redhat.com> [2018-09-13 19:02:31 +0100]:

> Hi,
> 
> On 08/28/2018 07:03 PM, Andrew Burgess wrote:
> > I believe that this variant addresses all existing review comments.
> > Philippe provided additional feedback in this message:
> > 
> >    https://sourceware.org/ml/gdb-patches/2018-08/msg00349.html
> > 
> > but I believe this was more about things we need to do to unify the
> > docs/code/comments on one of either 'number' or 'level', rather than
> > things that need to be fixed before this patch could be merged.
> > 
> > This version of the patch is based off v5(B) and uses the keyword
> > 'level' instead of 'number'.
> > 
> > The only changes over the previous version are an additional change in
> > the docs as previously discussed with Eli, and I've rebased the patch
> > onto current HEAD (and retested).
> > 
> > OK to commit?
> 
> First off, I'm very much for this patch, having argued for something
> like it before <https://sourceware.org/ml/gdb/2014-11/msg00028.html>.
> 
> As we discussed at the Cauldron, my main concern about this
> patch/direction is the "view" variant.  It's a bit odd in that
> it doesn't "select" a frame from the current stack, like the
> other variants, which makes it a little odd, but I understand where
> it's coming from.  There's more to it however.  Let me go over
> my main concern.
> 
> Specifically, if you do "frame view 0xADDR", and then do "up/down", then
> GDB moves to the next/previous frame relative to the 0xADDR frame, as I
> would expect.  That's fine.
> 
> However, if you do "backtrace", we still show a backtrace starting at the
> thread's real current frame instead of at the selected "view" frame.
> This is IMO inconsistent, and surprising.  Though, it's also how current gdb
> behaves with "frame 0xADDR", so it's not really a behavior change.
> 
> Also, related, after "frame view 0xADDR", if you do anything that might
> flush the frame cache, then you lose the "view"'d frame.  E.g., that
> can be with "info threads" (if you have multiple threads"), or if
> in non-stop mode while threads are running, some thread hits some
> temporary event (in which case I think GDB just loses the viewed
> frame without the user realizing).  Again, these aren't really
> problems the patch is adding, since you get the same problems
> with the current "frame 0xADDR":
> 
>  (gdb) frame 0x0011
>  #0  0x0000000000000000 in ?? ()
>  (gdb) frame
>  #0  0x0000000000000000 in ?? ()
>  (gdb) info threads 
>    Id   Target Id                                     Frame 
>  * 1    Thread 0x7ffff7fb6740 (LWP 32581) "threads"   0x0000000000000000 in ?? ()
>    2    Thread 0x7ffff7803700 (LWP 32585) "threads"   0x00007ffff78c7460 in __GI___nanosleep  
>    3    Thread 0x7ffff7002700 (LWP 32586) "threads"   0x00007ffff78c7460 in __GI___nanosleep  
>  (gdb) info threads 
>    Id   Target Id                                     Frame 
>  * 1    Thread 0x7ffff7fb6740 (LWP 32581) "threads"   0x00007ffff7bc28ad in __pthread_join
>    2    Thread 0x7ffff7803700 (LWP 32585) "threads"   0x00007ffff78c7460 in __GI___nanosleep
>    3    Thread 0x7ffff7002700 (LWP 32586) "threads"   0x00007ffff78c7460 in __GI___nanosleep
>  (gdb) frame
>  #0  0x00007ffff7bc28ad in __pthread_join () at pthread_join.c:90
>  90          lll_wait_tid (pd->tid);
>  (gdb) 
> 
> Notice also how thread 1's "Frame" column showed the "viewed" frame
> the first time, which is incorrect, IMO.  The second time we see
> the thread's current frame, because the first "info threads" command
> lost the "viewed" frame, as can be also seen in the last "frame"
> command.
> 
> Again, this is not a new problem (and above I'm not using
> your patch to show it).  But, it does highlight problems with
> the "frame view" direction that (I think I mentioned before)
> should ideally be addressed somehow.  Particularly if we're now
> proposing adding some new UI to expose the functionality, it
> makes me wonder whether it's a good idea to expose functionality
> in the UI that we may end up removing soon enough.  I.e., my
> concern is with whether addressing this would result in
> getting rid of "frame view".
> 
> E.g., the idea of using "frame level 0" to get back to the
> thread's real current frame (as mentioned in the documentation
> bits added by this patch) conflicts with the fact that
> "up/down" after "frame view" moves the selected stack
> frame within the viewed stack, which means those frames
> also have a relative frame level within that viewed
> stack.  One could very reasonably expect that
> "frame view 0xADDR; up; frame level 0" would get you back
> to the frame viewed at 0xADDR.
> 
> At the Cauldron, you had a nice idea of adding some separate
> command to create alternate stacks, like "(gdb) create-stack 0xADDR",
> coupled with some way to list the created stacks, like "info stack",
> and a way to switch between the created stacks and the thread's real
> current stack.  I think something like that would be really nice.
> 
> I'm not going to insist on going that direction before
> accepting this patch, though I must admit that I'd
> be delighted to seeing it explored.  I'll reserve the
> right to not fell guilty about (someone) ripping out "view"
> later on if this goes in as is.

I agree with everything you've written above.  I really hope that
someone does rip out 'frame view' and I hope it's me.  But if someone
beats me to it, then great.

My intention here is not to make 'frame view' a brand new feature we
can advertise on the box (GDB 9: Now with 'frame view'!), but rather
to split this kind-of-sometimes-almost-useful feature out from the
core frame selection feature so it doesn't clash with it.  In my
experience, most users, most of the time, don't want the 'frame view'
functionality, and when/if they trigger it by accident, it confuses
them.

The problem with the feature behind 'frame view' is it's just useful
and functional enough to be needed in some cases, but, realistically,
it's not either useful enough, nor broken enough for anyone to have
yet made it really good.

Personally, I think just having the general frame selection split from
the frame view feature is enough of a positive change to justify this
patch, even if 'frame view' was never improved.  But, this is my
patch, so of course I'd have the position.  Other might disagree.

> Actually, "view" is being exposed at the MI level, which
> makes me a bit more nervous about it, since it's a bit
> harder to change MI and CLI.  Maybe we should just not
> expose that at the MI level...  Bleh.

OK, so this I do agree could be more of a problem.  So in this new
version of the patch the MI interface is reverted to the current one.

Actually the existing documentation only says the parameter to
-stack-select-frame can be a frame level, passing a frame address to
"create" (or view) a new frame is never mentioned.  But I don't think
we can rely on people not having discovered the hidden features...

Anyway, with nothing changing on the MI front now, no existing
frontend should break, and no frontend can start using 'view' only to
have it disappear later on.

> 
> Onwards with the current patch.  See comments below.

All the rest of the comments are formatting / spelling type stuff.  I
believe that these are all addressed in the latest version of the
patch.

Thanks,
Andrew

---

gdb: Change how frames are selected for 'frame' and 'info frame'.

The 'frame' command, and thanks to code reuse the 'info frame' and
'select-frame' commands, currently have an overloaded mechanism for
selecting a frame.

These commands take one or two parameters, if it's one parameter then
we first try to use the parameter as an integer to select a frame by
level (or depth in the stack).  If that fails then we treat the
parameter as an address and try to select a stack frame by
stack-address.  If we still have not selected a stack frame, or we
initially had two parameters, then GDB allows the user to view a stack
frame that is not part of the current backtrace.  Internally, a new
frame is created with the given stack and pc addresses, and this is
shown to the user.

The result of this is that a typo by the user, entering the wrong stack
frame level for example, can result in a brand new frame being viewed
rather than an error.

The purpose of this commit is to remove this overloading, while still
offering the same functionality through some new sub-commands.  By
making the default behaviour of 'frame' (and friends) be to select a
stack frame by level index, it is hoped that enough
backwards-compatibility is maintained that users will not be overly
inconvenienced.

The 'frame', 'select-frame', and 'info frame' commands now all take a
frame specification string as an argument, this string can be any of the
following:

  (1) An integer.  This is treated as a frame level.  If a frame for
  that level does not exist then the user gets an error.

  (2) A string like 'level <LEVEL>', where <LEVEL> is a frame level
  as in option (1) above.

  (3) A string like 'address <STACK-ADDRESS>', where <STACK-ADDRESS>
  is a stack-frame address.  If there is no frame for this address
  then the user gets an error.

  (4) A string like 'function <NAME>', where <NAME> is a function name,
  the inner most frame for function <NAME> is selected.  If there is no
  frame for function <NAME> then the user gets an error.

  (5) A string like 'view <STACK-ADDRESS>', this views a new frame
  with stack address <STACK-ADDRESS>.

  (6) A string like 'view <STACK-ADDRESS> <PC-ADDRESS>', this views
  a new frame with stack address <STACK-ADDRESS> and the pc <PC-ADDRESS>.

This change assumes that the most common use of the commands like
'frame' is to select a frame by frame level, it is for this reason
that this is the behaviour that is kept for backwards compatibility.
Any of the alternative behaviours, which are assumed to be less used,
now require a change in user behaviour.

The MI command '-stack-select-frame' has not been changed.  This
ensures that we maintain backwards compatibility for existing
frontends.

gdb/ChangeLog:

	(NEWS): Mention changes to frame related commands.
	* cli/cli-decode.c (add_cmd_suppress_notification): New function.
	(add_prefix_cmd_suppress_notification): New function.
	(add_com_suppress_notification): Call
	add_cmd_suppress_notification.
	* command.h (add_cmd_suppress_notification): Declare.
	(add_prefix_cmd_suppress_notification): Declare.
	* mi/mi-cmd-stack.c: Add 'safe-ctype.h' include.
	(parse_frame_specification): Moved from stack.c, with minor
	simplification and cleanup.
	(mi_cmd_stack_select_frame): Use parse_frame_specification, the
	switch to the selected frame.  Add a header comment.
	* stack.c: Remove 'safe-ctype.h' include.
	(find_frame_for_function): Add declaration.
	(find_frame_for_address): New function.
	(parse_frame_specification): Moved into mi/mi-cmd-stack.c.
	(frame_selection_by_function_completer): New function.
	(info_frame_command): Rename to...
	(info_frame_command_core): ...this, and update parameter types.
	(select_frame_command): Rename to...
	(select_frame_command_core): ...this, and update parameter types.
	(frame_command): Rename to...
	(frame_command_core): ...this, and update parameter types.
	(class frame_command_helper): New class to wrap implementations of
	frame related sub-commands.
	(frame_apply_cmd_list): New static global.
	(frame_cmd_list): Make static.
	(select_frame_cmd_list): New global for sub-commands.
	(info_frame_cmd_list): New global for sub-commands.
	(_initialize_stack): Register sub-commands for 'frame',
	'select-frame', and 'info frame'.  Update 'frame apply' commands
	to use frame_apply_cmd_list.  Move function local static
	frame_apply_list to file static frame_apply_cmd_list for
	consistency.
	* stack.h (select_frame_command): Delete declarationn.
	(select_frame_for_mi): Declare new function.

gdb/doc/ChangeLog:

	* gdb.texinfo (Frames): Rewrite the description of 'frame number'
	to highlight that the number is also the frame's level.
	(Selection): Rewrite documentation for 'frame' and 'select-frame'
	commands.
	(Frame Info): Rewrite documentation for 'info frame' command.

gdb/testsuite/ChangeLog:

	* gdb.base/frame-selection.exp: New file.
	* gdb.base/frame-selection.c: New file.
---
 gdb/ChangeLog                              |  39 +++
 gdb/NEWS                                   |   8 +
 gdb/cli/cli-decode.c                       |  44 ++-
 gdb/command.h                              |  14 +
 gdb/doc/ChangeLog                          |   8 +
 gdb/doc/gdb.texinfo                        | 111 ++++--
 gdb/mi/mi-cmd-stack.c                      | 113 ++++++-
 gdb/stack.c                                | 526 +++++++++++++++++++----------
 gdb/stack.h                                |   6 +-
 gdb/testsuite/ChangeLog                    |   5 +
 gdb/testsuite/gdb.base/frame-selection.c   |  52 +++
 gdb/testsuite/gdb.base/frame-selection.exp | 185 ++++++++++
 12 files changed, 903 insertions(+), 208 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/frame-selection.c
 create mode 100644 gdb/testsuite/gdb.base/frame-selection.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index a1936ca1ccb..2669fa91a12 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -21,6 +21,14 @@
 * GDB in batch mode now exits with status 1 if the last command to be
   executed failed.
 
+* Changes to the "frame", "select-frame", and "info frame" CLI
+  commands.  These commands all now take a frame specification which
+  is either a frame level, or one of the keywords 'level', 'address',
+  'function', or 'view' followed by a parameter.  Selecting a frame by
+  address, or viewing a frame outside the current backtrace now
+  requires the use of a keyword.  Selecting a frame by level is
+  unchanged.  The MI comment "-stack-select-frame" is unchanged.
+
 * New commands
 
 set debug compile-cplus-types
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 83dd67efe3f..5d798e877e8 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -251,6 +251,23 @@ add_cmd (const char *name, enum command_class theclass,
   return result;
 }
 
+/* Add an element with a suppress notification to the LIST of commands.  */
+
+struct cmd_list_element *
+add_cmd_suppress_notification (const char *name, enum command_class theclass,
+			       cmd_const_cfunc_ftype *fun, const char *doc,
+			       struct cmd_list_element **list,
+			       int *suppress_notification)
+{
+  struct cmd_list_element *element;
+
+  element = add_cmd (name, theclass, fun, doc, list);
+  element->suppress_notification = suppress_notification;
+
+  return element;
+}
+
+
 /* Deprecates a command CMD.
    REPLACEMENT is the name of the command which should be used in
    place of this command, or NULL if no such command exists.
@@ -362,6 +379,25 @@ add_prefix_cmd (const char *name, enum command_class theclass,
   return c;
 }
 
+/* Like ADD_PREFIX_CMD but sets the suppress_notification pointer on the
+   new command list element.  */
+
+struct cmd_list_element *
+add_prefix_cmd_suppress_notification
+               (const char *name, enum command_class theclass,
+		cmd_const_cfunc_ftype *fun,
+		const char *doc, struct cmd_list_element **prefixlist,
+		const char *prefixname, int allow_unknown,
+		struct cmd_list_element **list,
+		int *suppress_notification)
+{
+  struct cmd_list_element *element
+    = add_prefix_cmd (name, theclass, fun, doc, prefixlist,
+		      prefixname, allow_unknown, list);
+  element->suppress_notification = suppress_notification;
+  return element;
+}
+
 /* Like add_prefix_cmd but sets the abbrev_flag on the new command.  */
 
 struct cmd_list_element *
@@ -893,12 +929,8 @@ add_com_suppress_notification (const char *name, enum command_class theclass,
 			       cmd_const_cfunc_ftype *fun, const char *doc,
 			       int *suppress_notification)
 {
-  struct cmd_list_element *element;
-
-  element = add_cmd (name, theclass, fun, doc, &cmdlist);
-  element->suppress_notification = suppress_notification;
-
-  return element;
+  return add_cmd_suppress_notification (name, theclass, fun, doc,
+					&cmdlist, suppress_notification);
 }
 
 /* Recursively walk the commandlist structures, and print out the
diff --git a/gdb/command.h b/gdb/command.h
index 3dde2475cb1..e3d55c2dcba 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -148,6 +148,12 @@ extern struct cmd_list_element *add_cmd (const char *, enum command_class,
 					 const char *,
 					 struct cmd_list_element **);
 
+extern struct cmd_list_element *add_cmd_suppress_notification
+			(const char *name, enum command_class theclass,
+			 cmd_const_cfunc_ftype *fun, const char *doc,
+			 struct cmd_list_element **list,
+			 int *suppress_notification);
+
 extern struct cmd_list_element *add_alias_cmd (const char *, const char *,
 					       enum command_class, int,
 					       struct cmd_list_element **);
@@ -165,6 +171,14 @@ extern struct cmd_list_element *add_prefix_cmd (const char *, enum command_class
 						const char *, int,
 						struct cmd_list_element **);
 
+extern struct cmd_list_element *add_prefix_cmd_suppress_notification
+			(const char *name, enum command_class theclass,
+			 cmd_const_cfunc_ftype *fun,
+			 const char *doc, struct cmd_list_element **prefixlist,
+			 const char *prefixname, int allow_unknown,
+			 struct cmd_list_element **list,
+			 int *suppress_notification);
+
 extern struct cmd_list_element *add_abbrev_prefix_cmd (const char *,
 						       enum command_class,
 						       cmd_const_cfunc_ftype *fun,
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index b5b6089153a..62486410981 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -7401,12 +7401,14 @@
 in a register called the @dfn{frame pointer register}
 (@pxref{Registers, $fp}) while execution is going on in that frame.
 
+@cindex frame level
 @cindex frame number
-@value{GDBN} assigns numbers to all existing stack frames, starting with
-zero for the innermost frame, one for the frame that called it,
-and so on upward.  These numbers do not really exist in your program;
-they are assigned by @value{GDBN} to give you a way of designating stack
-frames in @value{GDBN} commands.
+@value{GDBN} labels each existing stack frame with a @dfn{level}, a
+number that is zero for the innermost frame, one for the frame that
+called it, and so on upward.  These level numbers give you a way of
+designating stack frames in @value{GDBN} commands.  The terms
+@dfn{frame number} and @dfn{frame level} can be used interchangeably to
+describe this number.
 
 @c The -fomit-frame-pointer below perennially causes hbox overflow
 @c underflow problems.
@@ -7639,21 +7641,75 @@
 @table @code
 @kindex frame@r{, selecting}
 @kindex f @r{(@code{frame})}
-@item frame @var{n}
-@itemx f @var{n}
-Select frame number @var{n}.  Recall that frame zero is the innermost
+@item frame @r{[} @var{frame-selection-spec} @r{]}
+@item f @r{[} @var{frame-selection-spec} @r{]}
+The @command{frame} command allows different stack frames to be
+selected.  The @var{frame-selection-spec} can be any of the following:
+
+@table @code
+@kindex frame level
+@item @var{num}
+@item level @var{num}
+Select frame level @var{num}.  Recall that frame zero is the innermost
 (currently executing) frame, frame one is the frame that called the
-innermost one, and so on.  The highest-numbered frame is the one for
-@code{main}.
+innermost one, and so on.  The highest level frame is usually the one
+for @code{main}.
+
+As this is the most common method of navigating the frame stack, the
+string @command{level} can be omitted.  For example, the following two
+commands are equivalent:
 
-@item frame @var{stack-addr} [ @var{pc-addr} ]
-@itemx f @var{stack-addr} [ @var{pc-addr} ]
-Select the frame at address @var{stack-addr}.  This is useful mainly if the
-chaining of stack frames has been damaged by a bug, making it
-impossible for @value{GDBN} to assign numbers properly to all frames.  In
-addition, this can be useful when your program has multiple stacks and
-switches between them.  The optional @var{pc-addr} can also be given to
-specify the value of PC for the stack frame.
+@smallexample
+(@value{GDBP}) frame 3
+(@value{GDBP}) frame level 3
+@end smallexample
+
+@kindex frame address
+@item address @var{stack-address}
+Select the frame with stack address @var{stack-address}.  The
+@var{stack-address} for a frame can be seen in the output of
+@command{info frame}, for example:
+
+@smallexample
+(gdb) info frame
+Stack level 1, frame at 0x7fffffffda30:
+ rip = 0x40066d in b (amd64-entry-value.cc:59); saved rip 0x4004c5
+ tail call frame, caller of frame at 0x7fffffffda30
+ source language c++.
+ Arglist at unknown address.
+ Locals at unknown address, Previous frame's sp is 0x7fffffffda30
+@end smallexample
+
+The @var{stack-address} for this frame is @code{0x7fffffffda30} as
+indicated by the line:
+
+@smallexample
+Stack level 1, frame at 0x7fffffffda30:
+@end smallexample
+
+@kindex frame function
+@item function @var{function-name}
+Select the stack frame for function @var{function-name}.  If there are
+multiple stack frames for function @var{function-name} then the inner
+most stack frame is selected.
+
+@kindex frame view
+@item view @var{stack-address} @r{[} @var{pc-addr} @r{]}
+View a frame that is not part of @value{GDBN}'s backtrace.  The frame
+viewed has stack address @var{stack-addr}, and optionally, a program
+counter address of @var{pc-addr}.
+
+This is useful mainly if the chaining of stack frames has been
+damaged by a bug, making it impossible for @value{GDBN} to assign
+numbers properly to all frames.  In addition, this can be useful
+when your program has multiple stacks and switches between them.
+
+When viewing a frame outside the current backtrace using
+@command{frame view} then you can always return to the original
+stack using one of the previous stack frame selection instructions,
+for example @command{frame level 0}.
+
+@end table
 
 @kindex up
 @item up @var{n}
@@ -7696,11 +7752,13 @@
 
 @table @code
 @kindex select-frame
-@item select-frame
+@item select-frame @r{[} @var{frame-selection-spec} @r{]}
 The @code{select-frame} command is a variant of @code{frame} that does
 not display the new frame after selecting it.  This command is
 intended primarily for use in @value{GDBN} command scripts, where the
-output might be unnecessary and distracting.
+output might be unnecessary and distracting.  The
+@var{frame-selection-spec} is as for the @command{frame} command
+described in @ref{Selection, ,Selecting a Frame}.
 
 @kindex down-silently
 @kindex up-silently
@@ -7758,13 +7816,12 @@
 something has gone wrong that has made the stack format fail to fit
 the usual conventions.
 
-@item info frame @var{addr}
-@itemx info f @var{addr}
-Print a verbose description of the frame at address @var{addr}, without
-selecting that frame.  The selected frame remains unchanged by this
-command.  This requires the same kind of address (more than one for some
-architectures) that you specify in the @code{frame} command.
-@xref{Selection, ,Selecting a Frame}.
+@item info frame @r{[} @var{frame-selection-spec} @r{]}
+@itemx info f @r{[} @var{frame-selection-spec} @r{]}
+Print a verbose description of the frame selected by
+@var{frame-selection-spec}.  The @var{frame-selection-spec} is the
+same as for the @command{frame} command (@pxref{Selection, ,Selecting
+a Frame}).  The selected frame remains unchanged by this command.
 
 @kindex info args
 @item info args
diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c
index 52660bdd498..11f77bba8e2 100644
--- a/gdb/mi/mi-cmd-stack.c
+++ b/gdb/mi/mi-cmd-stack.c
@@ -35,6 +35,7 @@
 #include <ctype.h>
 #include "mi-parse.h"
 #include "common/gdb_optional.h"
+#include "safe-ctype.h"
 
 enum what_to_list { locals, arguments, all };
 
@@ -678,13 +679,123 @@ list_args_or_locals (enum what_to_list what, enum print_values values,
     }
 }
 
+/* Read a frame specification in whatever the appropriate format is from
+   FRAME_EXP.  Call error() if the specification is in any way invalid (so
+   this function never returns NULL).  */
+
+static struct frame_info *
+parse_frame_specification (const char *frame_exp)
+{
+  int numargs;
+  struct value *args[4];
+  CORE_ADDR addrs[ARRAY_SIZE (args)];
+
+  if (frame_exp == NULL)
+    numargs = 0;
+  else
+    {
+      numargs = 0;
+      while (1)
+        {
+          const char *p;
+
+          /* Skip leading white space, bail of EOL.  */
+          frame_exp = skip_spaces (frame_exp);
+          if (!*frame_exp)
+            break;
+
+          /* Parse the argument, extract it, save it.  */
+          for (p = frame_exp;
+               *p && !ISSPACE (*p);
+               p++);
+          std::string addr_string (frame_exp, p - frame_exp);
+          frame_exp = p;
+
+          /* NOTE: Parse and evaluate expression, but do not use
+             functions such as parse_and_eval_long or
+             parse_and_eval_address to also extract the value.
+             Instead value_as_long and value_as_address are used.
+             This avoids problems with expressions that contain
+             side-effects.  */
+          if (numargs >= ARRAY_SIZE (args))
+            error (_("Too many args in frame specification"));
+          args[numargs++] = parse_and_eval (addr_string.c_str ());
+        }
+    }
+
+  /* If no args, default to the selected frame.  */
+  if (numargs == 0)
+    return get_selected_frame (_("No stack."));
+
+  /* Assume the single arg[0] is an integer, and try using that to
+     select a frame relative to current.  */
+  if (numargs == 1)
+    {
+      struct frame_info *fid;
+      int level = value_as_long (args[0]);
+
+      fid = find_relative_frame (get_current_frame (), &level);
+      if (level == 0)
+        /* find_relative_frame was successful.  */
+        return fid;
+    }
+
+  /* Convert each value into a corresponding address.  */
+  for (int i = 0; i < numargs; i++)
+    addrs[i] = value_as_address (args[i]);
+
+  /* Assume that the single arg[0] is an address, use that to identify
+     a frame with a matching ID.  Should this also accept stack/pc or
+     stack/pc/special.  */
+  if (numargs == 1)
+    {
+      struct frame_id id = frame_id_build_wild (addrs[0]);
+      struct frame_info *fid;
+
+      /* If (s)he specifies the frame with an address, he deserves
+         what (s)he gets.  Still, give the highest one that matches.
+         (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't
+         know).  */
+      for (fid = get_current_frame ();
+           fid != NULL;
+           fid = get_prev_frame (fid))
+        {
+          if (frame_id_eq (id, get_frame_id (fid)))
+            {
+              struct frame_info *prev_frame;
+
+              while (1)
+                {
+                  prev_frame = get_prev_frame (fid);
+                  if (!prev_frame
+                      || !frame_id_eq (id, get_frame_id (prev_frame)))
+                    break;
+                  fid = prev_frame;
+                }
+              return fid;
+            }
+        }
+      }
+
+  /* We couldn't identify the frame as an existing frame, but
+     perhaps we can create one with a single argument.  */
+  if (numargs == 1)
+    return create_new_frame (addrs[0], 0);
+  else if (numargs == 2)
+    return create_new_frame (addrs[0], addrs[1]);
+  else
+    error (_("Too many args in frame specification"));
+}
+
+/* Implement the -stack-select-frame MI command.  */
+
 void
 mi_cmd_stack_select_frame (const char *command, char **argv, int argc)
 {
   if (argc == 0 || argc > 1)
     error (_("-stack-select-frame: Usage: FRAME_SPEC"));
 
-  select_frame_command (argv[0], 1 /* not used */ );
+  select_frame_for_mi (parse_frame_specification (argv[0]));
 }
 
 void
diff --git a/gdb/stack.c b/gdb/stack.c
index 40ff99b8fa6..fc1ccb33fa6 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -48,7 +48,6 @@
 #include "cli/cli-utils.h"
 #include "objfiles.h"
 
-#include "safe-ctype.h"
 #include "symfile.h"
 #include "extension.h"
 #include "observable.h"
@@ -102,6 +101,9 @@ static void set_last_displayed_sal (int valid,
 				    struct symtab *symtab,
 				    int line);
 
+static struct frame_info *find_frame_for_function (const char *);
+static struct frame_info *find_frame_for_address (CORE_ADDR);
+
 /* Zero means do things normally; we are interacting directly with the
    user.  One means print the full filename and linenumber when a
    frame is printed, and do so in a format emacs18/emacs19.22 can
@@ -1258,134 +1260,34 @@ print_frame (struct frame_info *frame, int print_level,
 }
 \f
 
-/* Read a frame specification in whatever the appropriate format is from
-   FRAME_EXP.  Call error() if the specification is in any way invalid (so
-   this function never returns NULL).  When SELECTED_FRAME_P is non-NULL
-   set its target to indicate that the default selected frame was used.  */
+/* Completion function for "frame function", "info frame function", and
+   "select-frame function" commands.  */
 
-static struct frame_info *
-parse_frame_specification (const char *frame_exp, int *selected_frame_p)
+void
+frame_selection_by_function_completer (struct cmd_list_element *ignore,
+				       completion_tracker &tracker,
+				       const char *text, const char *word)
 {
-  int numargs;
-  struct value *args[4];
-  CORE_ADDR addrs[ARRAY_SIZE (args)];
-
-  if (frame_exp == NULL)
-    numargs = 0;
-  else
-    {
-      numargs = 0;
-      while (1)
-	{
-	  const char *p;
-
-	  /* Skip leading white space, bail of EOL.  */
-	  frame_exp = skip_spaces (frame_exp);
-	  if (!*frame_exp)
-	    break;
-
-	  /* Parse the argument, extract it, save it.  */
-	  for (p = frame_exp;
-	       *p && !ISSPACE (*p);
-	       p++);
-	  std::string addr_string (frame_exp, p - frame_exp);
-	  frame_exp = p;
-	  
-	  /* NOTE: Parse and evaluate expression, but do not use
-	     functions such as parse_and_eval_long or
-	     parse_and_eval_address to also extract the value.
-	     Instead value_as_long and value_as_address are used.
-	     This avoids problems with expressions that contain
-	     side-effects.  */
-	  if (numargs >= ARRAY_SIZE (args))
-	    error (_("Too many args in frame specification"));
-	  args[numargs++] = parse_and_eval (addr_string.c_str ());
-	}
-    }
-
-  /* If no args, default to the selected frame.  */
-  if (numargs == 0)
-    {
-      if (selected_frame_p != NULL)
-	(*selected_frame_p) = 1;
-      return get_selected_frame (_("No stack."));
-    }
-
-  /* None of the remaining use the selected frame.  */
-  if (selected_frame_p != NULL)
-    (*selected_frame_p) = 0;
-
-  /* Assume the single arg[0] is an integer, and try using that to
-     select a frame relative to current.  */
-  if (numargs == 1)
-    {
-      struct frame_info *fid;
-      int level = value_as_long (args[0]);
-
-      fid = find_relative_frame (get_current_frame (), &level);
-      if (level == 0)
-	/* find_relative_frame was successful.  */
-	return fid;
-    }
-
-  /* Convert each value into a corresponding address.  */
-  {
-    int i;
-
-    for (i = 0; i < numargs; i++)
-      addrs[i] = value_as_address (args[i]);
-  }
-
-  /* Assume that the single arg[0] is an address, use that to identify
-     a frame with a matching ID.  Should this also accept stack/pc or
-     stack/pc/special.  */
-  if (numargs == 1)
-    {
-      struct frame_id id = frame_id_build_wild (addrs[0]);
-      struct frame_info *fid;
-
-      /* If (s)he specifies the frame with an address, he deserves
-	 what (s)he gets.  Still, give the highest one that matches.
-	 (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't
-	 know).  */
-      for (fid = get_current_frame ();
-	   fid != NULL;
-	   fid = get_prev_frame (fid))
-	{
-	  if (frame_id_eq (id, get_frame_id (fid)))
-	    {
-	      struct frame_info *prev_frame;
-
-	      while (1)
-		{
-		  prev_frame = get_prev_frame (fid);
-		  if (!prev_frame
-		      || !frame_id_eq (id, get_frame_id (prev_frame)))
-		    break;
-		  fid = prev_frame;
-		}
-	      return fid;
-	    }
-	}
-      }
-
-  /* We couldn't identify the frame as an existing frame, but
-     perhaps we can create one with a single argument.  */
-  if (numargs == 1)
-    return create_new_frame (addrs[0], 0);
-  else if (numargs == 2)
-    return create_new_frame (addrs[0], addrs[1]);
-  else
-    error (_("Too many args in frame specification"));
+  /* This is used to complete function names within a stack.  It would be
+     nice if we only offered functions that were actually in the stack.
+     However, this would mean unwinding the stack to completion, which
+     could take too long, or on a corrupted stack, possibly not end.
+     Instead, we offer all symbol names as a safer choice.  */
+  collect_symbol_completion_matches (tracker,
+				     complete_symbol_mode::EXPRESSION,
+				     symbol_name_match_type::EXPRESSION,
+				     text, word);
 }
 
-/* Print verbosely the selected frame or the frame at address
-   ADDR_EXP.  Absolutely all information in the frame is printed.  */
+/* Core of all the "info frame" sub-commands.  Print information about a
+   frame FI.  If SELECTED_FRAME_P is true then the user didn't provide a
+   frame specification, they just entered 'info frame'.  If the user did
+   provide a frame specification (for example 'info frame 0', 'info frame
+   level 1') then SELECTED_FRAME_P will be false.  */
 
 static void
-info_frame_command (const char *addr_exp, int from_tty)
+info_frame_command_core (struct frame_info *fi, bool selected_frame_p)
 {
-  struct frame_info *fi;
   struct symbol *func;
   struct symtab *s;
   struct frame_info *calling_frame_info;
@@ -1393,7 +1295,6 @@ info_frame_command (const char *addr_exp, int from_tty)
   const char *funname = 0;
   enum language funlang = language_unknown;
   const char *pc_regname;
-  int selected_frame_p;
   struct gdbarch *gdbarch;
   CORE_ADDR frame_pc;
   int frame_pc_p;
@@ -1401,7 +1302,6 @@ info_frame_command (const char *addr_exp, int from_tty)
   CORE_ADDR caller_pc = 0;
   int caller_pc_p = 0;
 
-  fi = parse_frame_specification (addr_exp, &selected_frame_p);
   gdbarch = get_frame_arch (fi);
 
   /* Name of the value returned by get_frame_pc().  Per comments, "pc"
@@ -1742,6 +1642,157 @@ trailing_outermost_frame (int count)
   return trailing;
 }
 
+/* The core of all the "select-frame" sub-commands.  Just wraps a call to
+   SELECT_FRAME.  */
+
+static void
+select_frame_command_core (struct frame_info *fi, bool ignored)
+{
+  struct frame_info *prev_frame = get_selected_frame_if_set ();
+  select_frame (fi);
+  if (get_selected_frame_if_set () != prev_frame)
+    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
+}
+
+/* See stack.h.  */
+
+void
+select_frame_for_mi (struct frame_info *fi)
+{
+  select_frame_command_core (fi, FALSE /* Ignored.  */);
+}
+
+/* The core of all the "frame" sub-commands.  Select frame FI, and if this
+   means we change frame send out a change notification (otherwise, just
+   reprint the current frame summary).   */
+
+static void
+frame_command_core (struct frame_info *fi, bool ignored)
+{
+  struct frame_info *prev_frame = get_selected_frame_if_set ();
+
+  select_frame (fi);
+  if (get_selected_frame_if_set () != prev_frame)
+    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
+  else
+    print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME);
+}
+
+/* The three commands 'frame', 'select-frame', and 'info frame' all have a
+   common set of sub-commands that allow a specific frame to be selected.
+   All of the sub-command functions are static methods within this class
+   template which is then instantiated below.  The template parameter is a
+   callback used to implement the functionality of the base command
+   ('frame', 'select-frame', or 'info frame').
+
+   In the template parameter FI is the frame being selected.  The
+   SELECTED_FRAME_P flag is true if the frame being selected was done by
+   default, which happens when the user uses the base command with no
+   arguments.  For example the commands 'info frame', 'select-frame',
+   'frame' will all cause SELECTED_FRAME_P to be true.  In all other cases
+   SELECTED_FRAME_P is false.  */
+
+template <void (*FPTR) (struct frame_info *fi, bool selected_frame_p)>
+class frame_command_helper
+{
+public:
+
+  /* The "frame level" family of commands.  The ARG is an integer that is
+     the frame's level in the stack.  */
+  static void
+  level (const char *arg, int from_tty)
+  {
+    int level = value_as_long (parse_and_eval (arg));
+    struct frame_info *fid
+      = find_relative_frame (get_current_frame (), &level);
+    if (level != 0)
+      error (_("No frame at level %s."), arg);
+    FPTR (fid, false);
+  }
+
+  /* The "frame address" family of commands.  ARG is a stack-pointer
+     address for an existing frame.  This command does not allow new
+     frames to be created.  */
+
+  static void
+  address (const char *arg, int from_tty)
+  {
+    CORE_ADDR addr = value_as_address (parse_and_eval (arg));
+    struct frame_info *fid = find_frame_for_address (addr);
+    if (fid == NULL)
+      error (_("No frame at address %s."), arg);
+    FPTR (fid, false);
+  }
+
+  /* The "frame view" family of commands.  ARG is one or two addresses and
+     is used to view a frame that might be outside the current backtrace.
+     The addresses are stack-pointer address, and (optional) pc-address.  */
+
+  static void
+  view (const char *args, int from_tty)
+  {
+    struct frame_info *fid;
+
+    if (args == NULL)
+    error (_("Missing address argument to view a frame"));
+
+    gdb_argv argv (args);
+
+    if (argv.count () == 2)
+      {
+	CORE_ADDR addr[2];
+
+	addr [0] = value_as_address (parse_and_eval (argv[0]));
+	addr [1] = value_as_address (parse_and_eval (argv[1]));
+	fid = create_new_frame (addr[0], addr[1]);
+      }
+    else
+      {
+	CORE_ADDR addr = value_as_address (parse_and_eval (argv[0]));
+	fid = create_new_frame (addr, false);
+      }
+    FPTR (fid, false);
+  }
+
+  /* The "frame function" family of commands.  ARG is the name of a
+     function within the stack, the first function (searching from frame
+     0) with that name will be selected.  */
+
+  static void
+  function (const char *arg, int from_tty)
+  {
+    if (arg == NULL)
+      error (_("Missing function name argument"));
+    struct frame_info *fid = find_frame_for_function (arg);
+    if (fid == NULL)
+      error (_("No frame for function \"%s\"."), arg);
+    FPTR (fid, false);
+  }
+
+  /* The "frame" base command, that is, when no sub-command is specified.
+     If one argument is provided then we assume that this is a frame's
+     level as historically, this was the supported command syntax that was
+     used most often.
+
+     If no argument is provided, then the current frame is selected.  */
+
+  static void
+  base_command (const char *arg, int from_tty)
+  {
+    if (arg == NULL)
+      FPTR (get_selected_frame (_("No stack.")), true);
+    else
+      level (arg, from_tty);
+  }
+};
+
+/* Instantiate three FRAME_COMMAND_HELPER instances to implement the
+   sub-commands for 'info frame', 'frame', and 'select-frame' commands.  */
+
+static frame_command_helper <info_frame_command_core> info_frame_cmd;
+static frame_command_helper <frame_command_core> frame_cmd;
+static frame_command_helper <select_frame_command_core> select_frame_cmd;
+
 /* Print briefly all stack frames or just the innermost COUNT_EXP
    frames.  */
 
@@ -2230,39 +2281,6 @@ find_relative_frame (struct frame_info *frame, int *level_offset_ptr)
   return frame;
 }
 
-/* The "select_frame" command.  With no argument this is a NOP.
-   Select the frame at level LEVEL_EXP if it is a valid level.
-   Otherwise, treat LEVEL_EXP as an address expression and select it.
-
-   See parse_frame_specification for more info on proper frame
-   expressions.  */
-
-void
-select_frame_command (const char *level_exp, int from_tty)
-{
-  struct frame_info *prev_frame = get_selected_frame_if_set ();
-
-  select_frame (parse_frame_specification (level_exp, NULL));
-  if (get_selected_frame_if_set () != prev_frame)
-    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
-}
-
-/* The "frame" command.  With no argument, print the selected frame
-   briefly.  With an argument, behave like select_frame and then print
-   the selected frame.  */
-
-static void
-frame_command (const char *level_exp, int from_tty)
-{
-  struct frame_info *prev_frame = get_selected_frame_if_set ();
-
-  select_frame (parse_frame_specification (level_exp, NULL));
-  if (get_selected_frame_if_set () != prev_frame)
-    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
-  else
-    print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME);
-}
-
 /* Select the frame up one or COUNT_EXP stack levels from the
    previously selected frame, and print it briefly.  */
 
@@ -2716,13 +2734,61 @@ faas_command (const char *cmd, int from_tty)
 }
 
 
+/* Find inner-mode frame with frame address ADDRESS.  Return NULL if no
+   matching frame can be found.  */
+
+static struct frame_info *
+find_frame_for_address (CORE_ADDR address)
+{
+  struct frame_id id;
+  struct frame_info *fid;
+
+  id = frame_id_build_wild (address);
+
+  /* If (s)he specifies the frame with an address, he deserves
+     what (s)he gets.  Still, give the highest one that matches.
+     (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't
+     know).  */
+  for (fid = get_current_frame ();
+       fid != NULL;
+       fid = get_prev_frame (fid))
+    {
+      if (frame_id_eq (id, get_frame_id (fid)))
+	{
+	  struct frame_info *prev_frame;
+
+	  while (1)
+	    {
+	      prev_frame = get_prev_frame (fid);
+	      if (!prev_frame
+		  || !frame_id_eq (id, get_frame_id (prev_frame)))
+		break;
+	      fid = prev_frame;
+	    }
+	  return fid;
+	}
+    }
+  return NULL;
+}
+
+\f
+
+/* Commands with a prefix of `frame apply'.  */
+static struct cmd_list_element *frame_apply_cmd_list = NULL;
+
 /* Commands with a prefix of `frame'.  */
-struct cmd_list_element *frame_cmd_list = NULL;
+static struct cmd_list_element *frame_cmd_list = NULL;
+
+/* Commands with a prefix of `select frame'.  */
+static struct cmd_list_element *select_frame_cmd_list = NULL;
+
+/* Commands with a prefix of `info frame'.  */
+static struct cmd_list_element *info_frame_cmd_list = NULL;
 
 void
 _initialize_stack (void)
 {
-  static struct cmd_list_element *frame_apply_list = NULL;
+  struct cmd_list_element *cmd;
 
   add_com ("return", class_stack, return_command, _("\
 Make selected stack frame return to its caller.\n\
@@ -2746,12 +2812,12 @@ An argument says how many frames down to go."));
 Same as the `down' command, but does not print anything.\n\
 This is useful in command scripts."));
 
-  add_prefix_cmd ("frame", class_stack, frame_command, _("\
-Select and print a stack frame.\nWith no argument, \
-print the selected stack frame.  (See also \"info frame\").\n\
-An argument specifies the frame to select.\n\
-It can be a stack frame number or the address of the frame."),
-		  &frame_cmd_list, "frame ", 1, &cmdlist);
+  add_prefix_cmd ("frame", class_stack,
+                  &frame_cmd.base_command, _("\
+Select and print a stack frame.\n\
+With no argument, print the selected stack frame.  (See also \"info frame\").\n\
+A single numerical argument specifies the frame to select."),
+                  &frame_cmd_list, "frame ", 1, &cmdlist);
 
   add_com_alias ("f", "frame", class_stack, 1);
 
@@ -2769,7 +2835,7 @@ or produces no output."
 Usage: frame apply COUNT [FLAG]... COMMAND\n\
 With a negative COUNT argument, applies the command on outermost -COUNT frames.\n"
 FRAME_APPLY_FLAGS_HELP),
-		  &frame_apply_list, "frame apply ", 1, &frame_cmd_list);
+		  &frame_apply_cmd_list, "frame apply ", 1, &frame_cmd_list);
 
   add_cmd ("all", class_stack, frame_apply_all_command,
 	   _("\
@@ -2777,7 +2843,7 @@ Apply a command to all frames.\n\
 \n\
 Usage: frame apply all [FLAG]... COMMAND\n"
 FRAME_APPLY_FLAGS_HELP),
-	   &frame_apply_list);
+	   &frame_apply_cmd_list);
 
   add_cmd ("level", class_stack, frame_apply_level_command,
 	   _("\
@@ -2786,19 +2852,97 @@ Apply a command to a list of frames.\n\
 Usage: frame apply level LEVEL... [FLAG]... COMMAND\n\
 ID is a space-separated list of LEVELs of frames to apply COMMAND on.\n"
 FRAME_APPLY_FLAGS_HELP),
-	   &frame_apply_list);
+	   &frame_apply_cmd_list);
 
   add_com ("faas", class_stack, faas_command, _("\
 Apply a command to all frames (ignoring errors and empty output).\n\
 Usage: faas COMMAND\n\
 shortcut for 'frame apply all -s COMMAND'"));
 
-  add_com_suppress_notification ("select-frame", class_stack, select_frame_command, _("\
+
+  add_prefix_cmd ("frame", class_stack,
+		  &frame_cmd.base_command, _("\
+Select and print a stack frame.\n\
+With no argument, print the selected stack frame.  (See also \"info frame\").\n\
+A single numerical argument specifies the frame to select."),
+		  &frame_cmd_list, "frame ", 1, &cmdlist);
+  add_com_alias ("f", "frame", class_stack, 1);
+
+  add_cmd ("address", class_stack, &frame_cmd.address,
+	   _("\
+Select and print a stack frame by stack address\n\
+\n\
+Usage: frame address STACK-ADDRESS"),
+	   &frame_cmd_list);
+
+  add_cmd ("view", class_stack, &frame_cmd.view,
+	   _("\
+View a stack frame that might be outside the current backtrace.\n\
+\n\
+Usage: frame view STACK-ADDRESS\n\
+       frame view STACK-ADDRESS PC-ADDRESS"),
+	   &frame_cmd_list);
+
+  cmd = add_cmd ("function", class_stack, &frame_cmd.function,
+	   _("\
+Select and print a stack frame by function name.\n\
+\n\
+Usage: frame function NAME\n\
+\n\
+The innermost frame that visited function NAME is selected."),
+	   &frame_cmd_list);
+  set_cmd_completer (cmd, frame_selection_by_function_completer);
+
+
+  add_cmd ("level", class_stack, &frame_cmd.level,
+	   _("\
+Select and print a stack frame by level.\n\
+\n\
+Usage: frame level LEVEL"),
+	   &frame_cmd_list);
+
+  cmd = add_prefix_cmd_suppress_notification ("select-frame", class_stack,
+		      &select_frame_cmd.base_command, _("\
 Select a stack frame without printing anything.\n\
-An argument specifies the frame to select.\n\
-It can be a stack frame number or the address of the frame."),
+A single numerical argument specifies the frame to select."),
+		      &select_frame_cmd_list, "select-frame ", 1, &cmdlist,
+		      &cli_suppress_notification.user_selected_context);
+
+  add_cmd_suppress_notification ("address", class_stack,
+			 &select_frame_cmd.address, _("\
+Select a stack frame by stack address.\n\
+\n\
+Usage: select-frame address STACK-ADDRESS"),
+			 &select_frame_cmd_list,
+			 &cli_suppress_notification.user_selected_context);
+
+
+  add_cmd_suppress_notification ("view", class_stack,
+		 &select_frame_cmd.view, _("\
+Select a stack frame that might be outside the current backtrace.\n\
+\n\
+Usage: select-frame view STACK-ADDRESS\n\
+       select-frame view STACK-ADDRESS PC-ADDRESS"),
+		 &select_frame_cmd_list,
 		 &cli_suppress_notification.user_selected_context);
 
+  cmd = add_cmd_suppress_notification ("function", class_stack,
+	       &select_frame_cmd.function, _("\
+Select a stack frame by function name.\n\
+\n\
+Usage: select-frame function NAME"),
+	       &select_frame_cmd_list,
+	       &cli_suppress_notification.user_selected_context);
+  set_cmd_completer (cmd, frame_selection_by_function_completer);
+
+  add_cmd_suppress_notification ("level", class_stack,
+			 &select_frame_cmd.level, _("\
+Select a stack frame by level.\n\
+\n\
+Usage: select-frame level LEVEL"),
+			 &select_frame_cmd_list,
+			 &cli_suppress_notification.user_selected_context);
+
   add_com ("backtrace", class_stack, backtrace_command, _("\
 Print backtrace of all stack frames, or innermost COUNT frames.\n\
 Usage: backtrace [QUALIFIERS]... [COUNT]\n\
@@ -2812,9 +2956,45 @@ on this backtrace."));
   add_info ("stack", backtrace_command,
 	    _("Backtrace of the stack, or innermost COUNT frames."));
   add_info_alias ("s", "stack", 1);
-  add_info ("frame", info_frame_command,
-	    _("All about selected stack frame, or frame at ADDR."));
+
+  add_prefix_cmd ("frame", class_info, &info_frame_cmd.base_command,
+		  _("All about the selected stack frame.\n\
+With no arguments, displays information about the currently selected stack\n\
+frame.  Alternatively a frame specification may be provided (See \"frame\")\n\
+the information is then printed about the specified frame."),
+		  &info_frame_cmd_list, "info frame ", 1, &infolist);
   add_info_alias ("f", "frame", 1);
+
+  add_cmd ("address", class_stack, &info_frame_cmd.address,
+	   _("\
+Print information about a stack frame selected by stack address.\n\
+\n\
+Usage: info frame address STACK-ADDRESS"),
+	   &info_frame_cmd_list);
+
+  add_cmd ("view", class_stack, &info_frame_cmd.view,
+	   _("\
+Print information about a stack frame outside the current backtrace.\n\
+\n\
+Usage: info frame view STACK-ADDRESS\n\
+       info frame view STACK-ADDRESS PC-ADDRESS"),
+	   &info_frame_cmd_list);
+
+  cmd = add_cmd ("function", class_stack, &info_frame_cmd.function,
+	   _("\
+Print information about a stack frame selected by function name.\n\
+\n\
+Usage: info frame function NAME"),
+	   &info_frame_cmd_list);
+  set_cmd_completer (cmd, frame_selection_by_function_completer);
+
+  add_cmd ("level", class_stack, &info_frame_cmd.level,
+	   _("\
+Print information about a stack frame selected by level.\n\
+\n\
+Usage: info frame level LEVEL"),
+	   &info_frame_cmd_list);
+
   add_info ("locals", info_locals_command,
 	    _("Local variables of current stack frame."));
   add_info ("args", info_args_command,
diff --git a/gdb/stack.h b/gdb/stack.h
index ca190efa9c3..3681120a776 100644
--- a/gdb/stack.h
+++ b/gdb/stack.h
@@ -20,7 +20,11 @@
 #ifndef STACK_H
 #define STACK_H
 
-void select_frame_command (const char *level_exp, int from_tty);
+/* Access method used by the MI -stack-select-frame command to switch to
+   frame FI.  This differs from SELECT_FRAME in that the observers for a
+   user selected context change will be triggered.  */
+
+void select_frame_for_mi (struct frame_info *fi);
 
 gdb::unique_xmalloc_ptr<char> find_frame_funname (struct frame_info *frame,
 						  enum language *funlang,
diff --git a/gdb/testsuite/gdb.base/frame-selection.c b/gdb/testsuite/gdb.base/frame-selection.c
new file mode 100644
index 00000000000..f3d81f223e0
--- /dev/null
+++ b/gdb/testsuite/gdb.base/frame-selection.c
@@ -0,0 +1,52 @@
+/* Copyright 2018 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int
+frame_2 (void)
+{
+  return 0;
+}
+
+int
+frame_1 (void)
+{
+  return frame_2 ();
+}
+
+int
+recursive (int arg)
+{
+  int v;
+
+  if (arg < 2)
+    v = recursive (arg + 1);
+  else
+    v = frame_2 ();
+
+  return v;
+}
+
+int
+main (void)
+{
+  int i, j;
+
+  i = frame_1 ();
+  j = recursive (0);
+
+  return i + j;
+}
diff --git a/gdb/testsuite/gdb.base/frame-selection.exp b/gdb/testsuite/gdb.base/frame-selection.exp
new file mode 100644
index 00000000000..526344f5e36
--- /dev/null
+++ b/gdb/testsuite/gdb.base/frame-selection.exp
@@ -0,0 +1,185 @@
+# Copyright 2018 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This tests GDBs frame selection as used by the 'frame',
+# 'select-frame', and 'info frame' commands.
+
+standard_testfile
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug}]} {
+    return -1
+}
+
+runto_main
+gdb_breakpoint frame_2
+gdb_continue_to_breakpoint frame_2
+
+gdb_test "bt" "#0  frame_2.*#1  $hex in frame_1.*#2  $hex in main.*" "backtrace at breakpoint"
+
+# Perform "info frame" to extract the frames address.
+proc get_frame_address { {testname ""} } {
+    global hex gdb_prompt
+
+    set frame_address "unknown"
+    set testname "get_frame_address: ${testname}"
+    gdb_test_multiple "info frame" $testname {
+	-re ", frame at ($hex):\r\n.*\r\n$gdb_prompt $" {
+	    set frame_address $expect_out(1,string)
+	    pass $testname
+	}
+    }
+
+    return $frame_address
+}
+
+# Passed a list of addresses.  Return a new address that is not in the
+# list.
+proc get_new_address { {addresses {}} } {
+    return [format "%#x" [expr [lindex $addresses [llength addresses]] + 0x10 ]]
+}
+
+
+# 	-re "Stack level ${level}, frame at ($address):\r\n .* = $hex in ${function} \(\[^\r\n\]*\); saved .* = $hex\r\n.*\r\n$gdb_prompt $" {
+
+# Check that the current frame is at stack depth LEVEL, at frame
+# address ADDRESS, and is in FUNCTION.
+proc check_frame { level address function } {
+    global hex gdb_prompt
+
+    set re [multi_line \
+		"Stack level ${level}, frame at ($address):" \
+		".* = $hex in ${function} \(\[^\r\n\]*\); saved .* = $hex" \
+		".*\r\n$gdb_prompt $" ]
+
+    set testname "check frame level ${level}"
+    gdb_test_multiple "info frame" $testname {
+	-re $re {
+	    pass $testname
+	}
+    }
+}
+
+# Select frame using level, but relying on this being the default
+# action, so "frame 0" performs "frame level 0".
+gdb_test "frame 0" "#0  frame_2.*"
+set frame_0_address [ get_frame_address "frame 0" ]
+gdb_test "frame 1" "#1  $hex in frame_1.*"
+set frame_1_address [ get_frame_address "frame 1" ]
+gdb_test "frame 2" "#2  $hex in main.*"
+set frame_2_address [ get_frame_address "frame 2" ]
+gdb_test "frame 3" "No frame at level 3\."
+
+# Find an address that matches no frame.
+set no_frame_address [ get_new_address [list $frame_0_address \
+					     $frame_1_address \
+					     $frame_2_address] ]
+
+# Select frame using 'level' specification.
+gdb_test "frame level 0" "#0  frame_2.*"
+gdb_test "frame level 1" "#1  $hex in frame_1.*"
+gdb_test "frame level 2" "#2  $hex in main.*"
+gdb_test "frame level 3" "No frame at level 3\."
+
+# Select frame by address.
+gdb_test "frame address ${frame_0_address}" "#0  frame_2.*" \
+    "select frame 0 by address"
+gdb_test "frame address ${frame_1_address}" "#1  $hex in frame_1.*" \
+    "select frame 1 by address"
+gdb_test "frame address ${frame_2_address}" "#2  $hex in main.*" \
+    "select frame 2 by address"
+gdb_test "frame address ${no_frame_address}" \
+    "No frame at address ${no_frame_address}\." \
+    "attempt to select a frame at an invalid address"
+
+# Select frame by function.
+gdb_test "frame function frame_2" "#0  frame_2.*"
+gdb_test "frame function frame_1" "#1  $hex in frame_1.*"
+gdb_test "frame function main" "#2  $hex in main.*"
+
+# Check for a distinction between a known function not in the stack
+# trace, and an unknown function.
+gdb_test "frame function recursive" "No frame for function \"recursive\"."
+gdb_test "frame function foo" "Function \"foo\" not defined."
+
+
+with_test_prefix "select-frame, no keyword" {
+    gdb_test_no_output "select-frame 0"
+    check_frame "0" "${frame_0_address}" "frame_2"
+    gdb_test_no_output "select-frame 1"
+    check_frame "1" "${frame_1_address}" "frame_1"
+    gdb_test_no_output "select-frame 2"
+    check_frame "2" "${frame_2_address}" "main"
+    gdb_test "select-frame 3" "No frame at level 3\."
+}
+
+with_test_prefix "select-frame, keyword=level" {
+    gdb_test_no_output "select-frame level 0"
+    check_frame "0" "${frame_0_address}" "frame_2"
+    gdb_test_no_output "select-frame level 1"
+    check_frame "1" "${frame_1_address}" "frame_1"
+    gdb_test_no_output "select-frame level 2"
+    check_frame "2" "${frame_2_address}" "main"
+    gdb_test "select-frame level 3" "No frame at level 3\."
+}
+
+with_test_prefix "select-frame, keyword=address" {
+    gdb_test_no_output "select-frame address ${frame_0_address}" \
+	"select frame 0 by address"
+    check_frame "0" "${frame_0_address}" "frame_2"
+    gdb_test_no_output "select-frame address ${frame_1_address}" \
+	"select frame 1 by address"
+    check_frame "1" "${frame_1_address}" "frame_1"
+    gdb_test_no_output "select-frame address ${frame_2_address}" \
+	"select frame 2 by address"
+    check_frame "2" "${frame_2_address}" "main"
+    gdb_test "select-frame address ${no_frame_address}" \
+	"No frame at address ${no_frame_address}\." \
+	"select-frame for an invalid address"
+}
+
+with_test_prefix "select-frame, keyword=function" {
+    gdb_test_no_output "select-frame function frame_2"
+    check_frame "0" "${frame_0_address}" "frame_2"
+    gdb_test_no_output "select-frame function frame_1"
+    check_frame "1" "${frame_1_address}" "frame_1"
+    gdb_test_no_output "select-frame function main"
+    check_frame "2" "${frame_2_address}" "main"
+}
+
+# Check for a distinction between a known function not in the stack
+# trace, and an unknown function.
+gdb_test "select-frame function recursive" \
+    "No frame for function \"recursive\"."
+gdb_test "select-frame function foo" \
+    "Function \"foo\" not defined."
+
+# Now continue until we hit the breakpoint again.
+with_test_prefix "second frame_2 breakpoint" {
+    gdb_continue_to_breakpoint frame_2
+    gdb_test "bt" \
+	"#0  frame_2.*#1  $hex in recursive.*#2  $hex in recursive.*#3  $hex in recursive.*#4  $hex in main.*" \
+ 	"backtrace at breakpoint with recursive frames"
+
+    # Check "frame function" when a function name occurs multiple times in
+    # the stack.  The inner most (lowest level) should always be selected.
+    gdb_test "frame function frame_2" "#0  frame_2.*"
+    gdb_test "frame function recursive" "#1  $hex in recursive.*" \
+	"select frame for function recursive, first attempt"
+    gdb_test "frame function recursive" "#1  $hex in recursive.*" \
+		"select frame for function recursive, second attempt"
+    gdb_test "frame function main" "#4  $hex in main.*"
+    gdb_test "frame function recursive" "#1  $hex in recursive.*" \
+	"select frame for function recursive, third attempt"
+}
-- 
2.14.4

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCHv6] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-09-18 23:01                         ` Andrew Burgess
@ 2018-09-19 16:26                           ` Pedro Alves
  2018-09-26 23:06                             ` Andrew Burgess
  0 siblings, 1 reply; 35+ messages in thread
From: Pedro Alves @ 2018-09-19 16:26 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

On 09/19/2018 12:01 AM, Andrew Burgess wrote:
> * Pedro Alves <palves@redhat.com> [2018-09-13 19:02:31 +0100]:
> 
>> Hi,
>>
>> On 08/28/2018 07:03 PM, Andrew Burgess wrote:
>>> I believe that this variant addresses all existing review comments.
>>> Philippe provided additional feedback in this message:
>>>
>>>    https://sourceware.org/ml/gdb-patches/2018-08/msg00349.html
>>>
>>> but I believe this was more about things we need to do to unify the
>>> docs/code/comments on one of either 'number' or 'level', rather than
>>> things that need to be fixed before this patch could be merged.
>>>
>>> This version of the patch is based off v5(B) and uses the keyword
>>> 'level' instead of 'number'.
>>>
>>> The only changes over the previous version are an additional change in
>>> the docs as previously discussed with Eli, and I've rebased the patch
>>> onto current HEAD (and retested).
>>>
>>> OK to commit?
>>
>> First off, I'm very much for this patch, having argued for something
>> like it before <https://sourceware.org/ml/gdb/2014-11/msg00028.html>.
>>
>> As we discussed at the Cauldron, my main concern about this
>> patch/direction is the "view" variant.  It's a bit odd in that
>> it doesn't "select" a frame from the current stack, like the
>> other variants, which makes it a little odd, but I understand where
>> it's coming from.  There's more to it however.  Let me go over
>> my main concern.
>>
>> Specifically, if you do "frame view 0xADDR", and then do "up/down", then
>> GDB moves to the next/previous frame relative to the 0xADDR frame, as I
>> would expect.  That's fine.
>>
>> However, if you do "backtrace", we still show a backtrace starting at the
>> thread's real current frame instead of at the selected "view" frame.
>> This is IMO inconsistent, and surprising.  Though, it's also how current gdb
>> behaves with "frame 0xADDR", so it's not really a behavior change.
>>
>> Also, related, after "frame view 0xADDR", if you do anything that might
>> flush the frame cache, then you lose the "view"'d frame.  E.g., that
>> can be with "info threads" (if you have multiple threads"), or if
>> in non-stop mode while threads are running, some thread hits some
>> temporary event (in which case I think GDB just loses the viewed
>> frame without the user realizing).  Again, these aren't really
>> problems the patch is adding, since you get the same problems
>> with the current "frame 0xADDR":
>>
>>  (gdb) frame 0x0011
>>  #0  0x0000000000000000 in ?? ()
>>  (gdb) frame
>>  #0  0x0000000000000000 in ?? ()
>>  (gdb) info threads 
>>    Id   Target Id                                     Frame 
>>  * 1    Thread 0x7ffff7fb6740 (LWP 32581) "threads"   0x0000000000000000 in ?? ()
>>    2    Thread 0x7ffff7803700 (LWP 32585) "threads"   0x00007ffff78c7460 in __GI___nanosleep  
>>    3    Thread 0x7ffff7002700 (LWP 32586) "threads"   0x00007ffff78c7460 in __GI___nanosleep  
>>  (gdb) info threads 
>>    Id   Target Id                                     Frame 
>>  * 1    Thread 0x7ffff7fb6740 (LWP 32581) "threads"   0x00007ffff7bc28ad in __pthread_join
>>    2    Thread 0x7ffff7803700 (LWP 32585) "threads"   0x00007ffff78c7460 in __GI___nanosleep
>>    3    Thread 0x7ffff7002700 (LWP 32586) "threads"   0x00007ffff78c7460 in __GI___nanosleep
>>  (gdb) frame
>>  #0  0x00007ffff7bc28ad in __pthread_join () at pthread_join.c:90
>>  90          lll_wait_tid (pd->tid);
>>  (gdb) 
>>
>> Notice also how thread 1's "Frame" column showed the "viewed" frame
>> the first time, which is incorrect, IMO.  The second time we see
>> the thread's current frame, because the first "info threads" command
>> lost the "viewed" frame, as can be also seen in the last "frame"
>> command.
>>
>> Again, this is not a new problem (and above I'm not using
>> your patch to show it).  But, it does highlight problems with
>> the "frame view" direction that (I think I mentioned before)
>> should ideally be addressed somehow.  Particularly if we're now
>> proposing adding some new UI to expose the functionality, it
>> makes me wonder whether it's a good idea to expose functionality
>> in the UI that we may end up removing soon enough.  I.e., my
>> concern is with whether addressing this would result in
>> getting rid of "frame view".
>>
>> E.g., the idea of using "frame level 0" to get back to the
>> thread's real current frame (as mentioned in the documentation
>> bits added by this patch) conflicts with the fact that
>> "up/down" after "frame view" moves the selected stack
>> frame within the viewed stack, which means those frames
>> also have a relative frame level within that viewed
>> stack.  One could very reasonably expect that
>> "frame view 0xADDR; up; frame level 0" would get you back
>> to the frame viewed at 0xADDR.
>>
>> At the Cauldron, you had a nice idea of adding some separate
>> command to create alternate stacks, like "(gdb) create-stack 0xADDR",
>> coupled with some way to list the created stacks, like "info stack",
>> and a way to switch between the created stacks and the thread's real
>> current stack.  I think something like that would be really nice.
>>
>> I'm not going to insist on going that direction before
>> accepting this patch, though I must admit that I'd
>> be delighted to seeing it explored.  I'll reserve the
>> right to not fell guilty about (someone) ripping out "view"
>> later on if this goes in as is.
> 
> I agree with everything you've written above.  I really hope that
> someone does rip out 'frame view' and I hope it's me.  But if someone
> beats me to it, then great.
> 

Great.  Glad we're in sync.

> All the rest of the comments are formatting / spelling type stuff.  I
> believe that these are all addressed in the latest version of the
> patch.
> 

This is OK with the nits below addressed.


>  enum what_to_list { locals, arguments, all };
>  
> @@ -678,13 +679,123 @@ list_args_or_locals (enum what_to_list what, enum print_values values,
>      }
>  }
>  
> +/* Read a frame specification in whatever the appropriate format is from
> +   FRAME_EXP.  Call error() if the specification is in any way invalid (so
> +   this function never returns NULL).  */
> +

Could you expand the comment a bit to mention that that format
is either a level, or an address, the latter undocumented but kept for
for backward compatibility?

> +static struct frame_info *
> +parse_frame_specification (const char *frame_exp)
> +{

> +
> +# This tests GDBs frame selection as used by the 'frame',
> +# 'select-frame', and 'info frame' commands.

typo: GDB's

> +
> +# Perform "info frame" to extract the frames address.

typo: "frame's".

> +proc get_frame_address { {testname ""} } {
> +    global hex gdb_prompt
> +
> +    set frame_address "unknown"
> +    set testname "get_frame_address: ${testname}"
> +    gdb_test_multiple "info frame" $testname {
> +	-re ", frame at ($hex):\r\n.*\r\n$gdb_prompt $" {
> +	    set frame_address $expect_out(1,string)
> +	    pass $testname
> +	}
> +    }
> +
> +    return $frame_address
> +}
> +
> +# Passed a list of addresses.  Return a new address that is not in the
> +# list.
> +proc get_new_address { {addresses {}} } {
> +    return [format "%#x" [expr [lindex $addresses [llength addresses]] + 0x10 ]]
> +}

I still had trouble understanding the comment.  :-(

This seem to return the last address in the list, plus 0x10.  That
assumes that that address is not in the list yet, which is
unlike what I'd assume the implementation would do, given
the comment alone.  E.g., I'd assume a function with that comment
would iterate over all elements, get the max element, and then
return that + 0x10.  As is, the implementation is assuming
the stack grows in the same direction on all platforms (hp-pa
grows up, for example).

> +
> +
> +# 	-re "Stack level ${level}, frame at ($address):\r\n .* = $hex in ${function} \(\[^\r\n\]*\); saved .* = $hex\r\n.*\r\n$gdb_prompt $" {

Leftover ?

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCHv6] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-09-19 16:26                           ` Pedro Alves
@ 2018-09-26 23:06                             ` Andrew Burgess
  2018-09-27 20:58                               ` Pedro Alves
  0 siblings, 1 reply; 35+ messages in thread
From: Andrew Burgess @ 2018-09-26 23:06 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

Thanks for the review.

This revision makes the following changes:

  - Addresses the two small grammatical errors, and removes the random
    comment line in the testsuite.

  - Rewrites the get_new_address function in the testsuite so that is
    actually does what it says on the tin (in line with your
    suggestion).

  - Extends the header comment on parse_frame_specification.  While
    doing this I realised that due to the way this function is now
    used it can be simplified a lot.  We now only handle a single
    argument.  I'd rather not start extending the existing MI
    interface with new features at this point, and I didn't think
    keeping the more complex parse_frame_specification around "just in
    case" was good practice.

It's because of this last change that I've posted the patch for
another review.  I think the change is sensible (and the tests pass),
but I wanted another opinion.

Thanks,
Andrew

---

gdb: Change how frames are selected for 'frame' and 'info frame'.

The 'frame' command, and thanks to code reuse the 'info frame' and
'select-frame' commands, currently have an overloaded mechanism for
selecting a frame.

These commands take one or two parameters, if it's one parameter then
we first try to use the parameter as an integer to select a frame by
level (or depth in the stack).  If that fails then we treat the
parameter as an address and try to select a stack frame by
stack-address.  If we still have not selected a stack frame, or we
initially had two parameters, then GDB allows the user to view a stack
frame that is not part of the current backtrace.  Internally, a new
frame is created with the given stack and pc addresses, and this is
shown to the user.

The result of this is that a typo by the user, entering the wrong stack
frame level for example, can result in a brand new frame being viewed
rather than an error.

The purpose of this commit is to remove this overloading, while still
offering the same functionality through some new sub-commands.  By
making the default behaviour of 'frame' (and friends) be to select a
stack frame by level index, it is hoped that enough
backwards-compatibility is maintained that users will not be overly
inconvenienced.

The 'frame', 'select-frame', and 'info frame' commands now all take a
frame specification string as an argument, this string can be any of the
following:

  (1) An integer.  This is treated as a frame level.  If a frame for
  that level does not exist then the user gets an error.

  (2) A string like 'level <LEVEL>', where <LEVEL> is a frame level
  as in option (1) above.

  (3) A string like 'address <STACK-ADDRESS>', where <STACK-ADDRESS>
  is a stack-frame address.  If there is no frame for this address
  then the user gets an error.

  (4) A string like 'function <NAME>', where <NAME> is a function name,
  the inner most frame for function <NAME> is selected.  If there is no
  frame for function <NAME> then the user gets an error.

  (5) A string like 'view <STACK-ADDRESS>', this views a new frame
  with stack address <STACK-ADDRESS>.

  (6) A string like 'view <STACK-ADDRESS> <PC-ADDRESS>', this views
  a new frame with stack address <STACK-ADDRESS> and the pc <PC-ADDRESS>.

This change assumes that the most common use of the commands like
'frame' is to select a frame by frame level, it is for this reason
that this is the behaviour that is kept for backwards compatibility.
Any of the alternative behaviours, which are assumed to be less used,
now require a change in user behaviour.

The MI command '-stack-select-frame' has not been changed.  This
ensures that we maintain backwards compatibility for existing
frontends.

gdb/ChangeLog:

	(NEWS): Mention changes to frame related commands.
	* cli/cli-decode.c (add_cmd_suppress_notification): New function.
	(add_prefix_cmd_suppress_notification): New function.
	(add_com_suppress_notification): Call
	add_cmd_suppress_notification.
	* command.h (add_cmd_suppress_notification): Declare.
	(add_prefix_cmd_suppress_notification): Declare.
	* mi/mi-cmd-stack.c: Add 'safe-ctype.h' include.
	(parse_frame_specification): Moved from stack.c, with
	simplification to handle a single argument.
	(mi_cmd_stack_select_frame): Use parse_frame_specification, the
	switch to the selected frame.  Add a header comment.
	* stack.c: Remove 'safe-ctype.h' include.
	(find_frame_for_function): Add declaration.
	(find_frame_for_address): New function.
	(parse_frame_specification): Moved into mi/mi-cmd-stack.c.
	(frame_selection_by_function_completer): New function.
	(info_frame_command): Rename to...
	(info_frame_command_core): ...this, and update parameter types.
	(select_frame_command): Rename to...
	(select_frame_command_core): ...this, and update parameter types.
	(frame_command): Rename to...
	(frame_command_core): ...this, and update parameter types.
	(class frame_command_helper): New class to wrap implementations of
	frame related sub-commands.
	(frame_apply_cmd_list): New static global.
	(frame_cmd_list): Make static.
	(select_frame_cmd_list): New global for sub-commands.
	(info_frame_cmd_list): New global for sub-commands.
	(_initialize_stack): Register sub-commands for 'frame',
	'select-frame', and 'info frame'.  Update 'frame apply' commands
	to use frame_apply_cmd_list.  Move function local static
	frame_apply_list to file static frame_apply_cmd_list for
	consistency.
	* stack.h (select_frame_command): Delete declarationn.
	(select_frame_for_mi): Declare new function.

gdb/doc/ChangeLog:

	* gdb.texinfo (Frames): Rewrite the description of 'frame number'
	to highlight that the number is also the frame's level.
	(Selection): Rewrite documentation for 'frame' and 'select-frame'
	commands.
	(Frame Info): Rewrite documentation for 'info frame' command.

gdb/testsuite/ChangeLog:

	* gdb.base/frame-selection.exp: New file.
	* gdb.base/frame-selection.c: New file.
---
 gdb/ChangeLog                              |  41 +++
 gdb/NEWS                                   |   8 +
 gdb/cli/cli-decode.c                       |  44 ++-
 gdb/command.h                              |  14 +
 gdb/doc/ChangeLog                          |   8 +
 gdb/doc/gdb.texinfo                        | 111 ++++--
 gdb/mi/mi-cmd-stack.c                      |  77 ++++-
 gdb/stack.c                                | 526 +++++++++++++++++++----------
 gdb/stack.h                                |   6 +-
 gdb/testsuite/ChangeLog                    |   5 +
 gdb/testsuite/gdb.base/frame-selection.c   |  52 +++
 gdb/testsuite/gdb.base/frame-selection.exp | 188 +++++++++++
 12 files changed, 872 insertions(+), 208 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/frame-selection.c
 create mode 100644 gdb/testsuite/gdb.base/frame-selection.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index a1936ca1ccb..2669fa91a12 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -21,6 +21,14 @@
 * GDB in batch mode now exits with status 1 if the last command to be
   executed failed.
 
+* Changes to the "frame", "select-frame", and "info frame" CLI
+  commands.  These commands all now take a frame specification which
+  is either a frame level, or one of the keywords 'level', 'address',
+  'function', or 'view' followed by a parameter.  Selecting a frame by
+  address, or viewing a frame outside the current backtrace now
+  requires the use of a keyword.  Selecting a frame by level is
+  unchanged.  The MI comment "-stack-select-frame" is unchanged.
+
 * New commands
 
 set debug compile-cplus-types
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 83dd67efe3f..5d798e877e8 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -251,6 +251,23 @@ add_cmd (const char *name, enum command_class theclass,
   return result;
 }
 
+/* Add an element with a suppress notification to the LIST of commands.  */
+
+struct cmd_list_element *
+add_cmd_suppress_notification (const char *name, enum command_class theclass,
+			       cmd_const_cfunc_ftype *fun, const char *doc,
+			       struct cmd_list_element **list,
+			       int *suppress_notification)
+{
+  struct cmd_list_element *element;
+
+  element = add_cmd (name, theclass, fun, doc, list);
+  element->suppress_notification = suppress_notification;
+
+  return element;
+}
+
+
 /* Deprecates a command CMD.
    REPLACEMENT is the name of the command which should be used in
    place of this command, or NULL if no such command exists.
@@ -362,6 +379,25 @@ add_prefix_cmd (const char *name, enum command_class theclass,
   return c;
 }
 
+/* Like ADD_PREFIX_CMD but sets the suppress_notification pointer on the
+   new command list element.  */
+
+struct cmd_list_element *
+add_prefix_cmd_suppress_notification
+               (const char *name, enum command_class theclass,
+		cmd_const_cfunc_ftype *fun,
+		const char *doc, struct cmd_list_element **prefixlist,
+		const char *prefixname, int allow_unknown,
+		struct cmd_list_element **list,
+		int *suppress_notification)
+{
+  struct cmd_list_element *element
+    = add_prefix_cmd (name, theclass, fun, doc, prefixlist,
+		      prefixname, allow_unknown, list);
+  element->suppress_notification = suppress_notification;
+  return element;
+}
+
 /* Like add_prefix_cmd but sets the abbrev_flag on the new command.  */
 
 struct cmd_list_element *
@@ -893,12 +929,8 @@ add_com_suppress_notification (const char *name, enum command_class theclass,
 			       cmd_const_cfunc_ftype *fun, const char *doc,
 			       int *suppress_notification)
 {
-  struct cmd_list_element *element;
-
-  element = add_cmd (name, theclass, fun, doc, &cmdlist);
-  element->suppress_notification = suppress_notification;
-
-  return element;
+  return add_cmd_suppress_notification (name, theclass, fun, doc,
+					&cmdlist, suppress_notification);
 }
 
 /* Recursively walk the commandlist structures, and print out the
diff --git a/gdb/command.h b/gdb/command.h
index 3dde2475cb1..e3d55c2dcba 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -148,6 +148,12 @@ extern struct cmd_list_element *add_cmd (const char *, enum command_class,
 					 const char *,
 					 struct cmd_list_element **);
 
+extern struct cmd_list_element *add_cmd_suppress_notification
+			(const char *name, enum command_class theclass,
+			 cmd_const_cfunc_ftype *fun, const char *doc,
+			 struct cmd_list_element **list,
+			 int *suppress_notification);
+
 extern struct cmd_list_element *add_alias_cmd (const char *, const char *,
 					       enum command_class, int,
 					       struct cmd_list_element **);
@@ -165,6 +171,14 @@ extern struct cmd_list_element *add_prefix_cmd (const char *, enum command_class
 						const char *, int,
 						struct cmd_list_element **);
 
+extern struct cmd_list_element *add_prefix_cmd_suppress_notification
+			(const char *name, enum command_class theclass,
+			 cmd_const_cfunc_ftype *fun,
+			 const char *doc, struct cmd_list_element **prefixlist,
+			 const char *prefixname, int allow_unknown,
+			 struct cmd_list_element **list,
+			 int *suppress_notification);
+
 extern struct cmd_list_element *add_abbrev_prefix_cmd (const char *,
 						       enum command_class,
 						       cmd_const_cfunc_ftype *fun,
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index b5b6089153a..62486410981 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -7401,12 +7401,14 @@
 in a register called the @dfn{frame pointer register}
 (@pxref{Registers, $fp}) while execution is going on in that frame.
 
+@cindex frame level
 @cindex frame number
-@value{GDBN} assigns numbers to all existing stack frames, starting with
-zero for the innermost frame, one for the frame that called it,
-and so on upward.  These numbers do not really exist in your program;
-they are assigned by @value{GDBN} to give you a way of designating stack
-frames in @value{GDBN} commands.
+@value{GDBN} labels each existing stack frame with a @dfn{level}, a
+number that is zero for the innermost frame, one for the frame that
+called it, and so on upward.  These level numbers give you a way of
+designating stack frames in @value{GDBN} commands.  The terms
+@dfn{frame number} and @dfn{frame level} can be used interchangeably to
+describe this number.
 
 @c The -fomit-frame-pointer below perennially causes hbox overflow
 @c underflow problems.
@@ -7639,21 +7641,75 @@
 @table @code
 @kindex frame@r{, selecting}
 @kindex f @r{(@code{frame})}
-@item frame @var{n}
-@itemx f @var{n}
-Select frame number @var{n}.  Recall that frame zero is the innermost
+@item frame @r{[} @var{frame-selection-spec} @r{]}
+@item f @r{[} @var{frame-selection-spec} @r{]}
+The @command{frame} command allows different stack frames to be
+selected.  The @var{frame-selection-spec} can be any of the following:
+
+@table @code
+@kindex frame level
+@item @var{num}
+@item level @var{num}
+Select frame level @var{num}.  Recall that frame zero is the innermost
 (currently executing) frame, frame one is the frame that called the
-innermost one, and so on.  The highest-numbered frame is the one for
-@code{main}.
+innermost one, and so on.  The highest level frame is usually the one
+for @code{main}.
+
+As this is the most common method of navigating the frame stack, the
+string @command{level} can be omitted.  For example, the following two
+commands are equivalent:
 
-@item frame @var{stack-addr} [ @var{pc-addr} ]
-@itemx f @var{stack-addr} [ @var{pc-addr} ]
-Select the frame at address @var{stack-addr}.  This is useful mainly if the
-chaining of stack frames has been damaged by a bug, making it
-impossible for @value{GDBN} to assign numbers properly to all frames.  In
-addition, this can be useful when your program has multiple stacks and
-switches between them.  The optional @var{pc-addr} can also be given to
-specify the value of PC for the stack frame.
+@smallexample
+(@value{GDBP}) frame 3
+(@value{GDBP}) frame level 3
+@end smallexample
+
+@kindex frame address
+@item address @var{stack-address}
+Select the frame with stack address @var{stack-address}.  The
+@var{stack-address} for a frame can be seen in the output of
+@command{info frame}, for example:
+
+@smallexample
+(gdb) info frame
+Stack level 1, frame at 0x7fffffffda30:
+ rip = 0x40066d in b (amd64-entry-value.cc:59); saved rip 0x4004c5
+ tail call frame, caller of frame at 0x7fffffffda30
+ source language c++.
+ Arglist at unknown address.
+ Locals at unknown address, Previous frame's sp is 0x7fffffffda30
+@end smallexample
+
+The @var{stack-address} for this frame is @code{0x7fffffffda30} as
+indicated by the line:
+
+@smallexample
+Stack level 1, frame at 0x7fffffffda30:
+@end smallexample
+
+@kindex frame function
+@item function @var{function-name}
+Select the stack frame for function @var{function-name}.  If there are
+multiple stack frames for function @var{function-name} then the inner
+most stack frame is selected.
+
+@kindex frame view
+@item view @var{stack-address} @r{[} @var{pc-addr} @r{]}
+View a frame that is not part of @value{GDBN}'s backtrace.  The frame
+viewed has stack address @var{stack-addr}, and optionally, a program
+counter address of @var{pc-addr}.
+
+This is useful mainly if the chaining of stack frames has been
+damaged by a bug, making it impossible for @value{GDBN} to assign
+numbers properly to all frames.  In addition, this can be useful
+when your program has multiple stacks and switches between them.
+
+When viewing a frame outside the current backtrace using
+@command{frame view} then you can always return to the original
+stack using one of the previous stack frame selection instructions,
+for example @command{frame level 0}.
+
+@end table
 
 @kindex up
 @item up @var{n}
@@ -7696,11 +7752,13 @@
 
 @table @code
 @kindex select-frame
-@item select-frame
+@item select-frame @r{[} @var{frame-selection-spec} @r{]}
 The @code{select-frame} command is a variant of @code{frame} that does
 not display the new frame after selecting it.  This command is
 intended primarily for use in @value{GDBN} command scripts, where the
-output might be unnecessary and distracting.
+output might be unnecessary and distracting.  The
+@var{frame-selection-spec} is as for the @command{frame} command
+described in @ref{Selection, ,Selecting a Frame}.
 
 @kindex down-silently
 @kindex up-silently
@@ -7758,13 +7816,12 @@
 something has gone wrong that has made the stack format fail to fit
 the usual conventions.
 
-@item info frame @var{addr}
-@itemx info f @var{addr}
-Print a verbose description of the frame at address @var{addr}, without
-selecting that frame.  The selected frame remains unchanged by this
-command.  This requires the same kind of address (more than one for some
-architectures) that you specify in the @code{frame} command.
-@xref{Selection, ,Selecting a Frame}.
+@item info frame @r{[} @var{frame-selection-spec} @r{]}
+@itemx info f @r{[} @var{frame-selection-spec} @r{]}
+Print a verbose description of the frame selected by
+@var{frame-selection-spec}.  The @var{frame-selection-spec} is the
+same as for the @command{frame} command (@pxref{Selection, ,Selecting
+a Frame}).  The selected frame remains unchanged by this command.
 
 @kindex info args
 @item info args
diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c
index 52660bdd498..0679c5739ff 100644
--- a/gdb/mi/mi-cmd-stack.c
+++ b/gdb/mi/mi-cmd-stack.c
@@ -35,6 +35,7 @@
 #include <ctype.h>
 #include "mi-parse.h"
 #include "common/gdb_optional.h"
+#include "safe-ctype.h"
 
 enum what_to_list { locals, arguments, all };
 
@@ -678,13 +679,87 @@ list_args_or_locals (enum what_to_list what, enum print_values values,
     }
 }
 
+/* Read a frame specification from FRAME_EXP and return the selected frame.
+   Call error() if the specification is in any way invalid (so this
+   function never returns NULL).
+
+   The frame specification is usually an integer level number, however if
+   the number does not match a valid frame level then it will be treated as
+   a frame address.  The frame address will then be used to find a matching
+   frame in the stack.  If no matching frame is found then a new frame will
+   be created.
+
+   The use of FRAME_EXP as an address is undocumented in the GDB user
+   manual, this feature is supported here purely for backward
+   compatibility.  */
+
+static struct frame_info *
+parse_frame_specification (const char *frame_exp)
+{
+  gdb_assert (frame_exp != NULL);
+
+  /* NOTE: Parse and evaluate expression, but do not use
+     functions such as parse_and_eval_long or
+     parse_and_eval_address to also extract the value.
+     Instead value_as_long and value_as_address are used.
+     This avoids problems with expressions that contain
+     side-effects.  */
+  struct value *arg = parse_and_eval (frame_exp);
+
+  /* Assume ARG is an integer, and try using that to select a frame.  */
+  struct frame_info *fid;
+  int level = value_as_long (arg);
+
+  fid = find_relative_frame (get_current_frame (), &level);
+  if (level == 0)
+    /* find_relative_frame was successful.  */
+    return fid;
+
+  /* Convert the value into a corresponding address.  */
+  CORE_ADDR addr = value_as_address (arg);
+
+  /* Assume that ADDR is an address, use that to identify a frame with a
+     matching ID.  */
+  struct frame_id id = frame_id_build_wild (addr);
+
+  /* If (s)he specifies the frame with an address, he deserves
+     what (s)he gets.  Still, give the highest one that matches.
+     (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't
+     know).  */
+  for (fid = get_current_frame ();
+       fid != NULL;
+       fid = get_prev_frame (fid))
+    {
+      if (frame_id_eq (id, get_frame_id (fid)))
+	{
+	  struct frame_info *prev_frame;
+
+	  while (1)
+	    {
+	      prev_frame = get_prev_frame (fid);
+	      if (!prev_frame
+		  || !frame_id_eq (id, get_frame_id (prev_frame)))
+		break;
+	      fid = prev_frame;
+	    }
+	  return fid;
+	}
+    }
+
+  /* We couldn't identify the frame as an existing frame, but
+     perhaps we can create one with a single argument.  */
+  return create_new_frame (addr, 0);
+}
+
+/* Implement the -stack-select-frame MI command.  */
+
 void
 mi_cmd_stack_select_frame (const char *command, char **argv, int argc)
 {
   if (argc == 0 || argc > 1)
     error (_("-stack-select-frame: Usage: FRAME_SPEC"));
 
-  select_frame_command (argv[0], 1 /* not used */ );
+  select_frame_for_mi (parse_frame_specification (argv[0]));
 }
 
 void
diff --git a/gdb/stack.c b/gdb/stack.c
index 40ff99b8fa6..fc1ccb33fa6 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -48,7 +48,6 @@
 #include "cli/cli-utils.h"
 #include "objfiles.h"
 
-#include "safe-ctype.h"
 #include "symfile.h"
 #include "extension.h"
 #include "observable.h"
@@ -102,6 +101,9 @@ static void set_last_displayed_sal (int valid,
 				    struct symtab *symtab,
 				    int line);
 
+static struct frame_info *find_frame_for_function (const char *);
+static struct frame_info *find_frame_for_address (CORE_ADDR);
+
 /* Zero means do things normally; we are interacting directly with the
    user.  One means print the full filename and linenumber when a
    frame is printed, and do so in a format emacs18/emacs19.22 can
@@ -1258,134 +1260,34 @@ print_frame (struct frame_info *frame, int print_level,
 }
 \f
 
-/* Read a frame specification in whatever the appropriate format is from
-   FRAME_EXP.  Call error() if the specification is in any way invalid (so
-   this function never returns NULL).  When SELECTED_FRAME_P is non-NULL
-   set its target to indicate that the default selected frame was used.  */
+/* Completion function for "frame function", "info frame function", and
+   "select-frame function" commands.  */
 
-static struct frame_info *
-parse_frame_specification (const char *frame_exp, int *selected_frame_p)
+void
+frame_selection_by_function_completer (struct cmd_list_element *ignore,
+				       completion_tracker &tracker,
+				       const char *text, const char *word)
 {
-  int numargs;
-  struct value *args[4];
-  CORE_ADDR addrs[ARRAY_SIZE (args)];
-
-  if (frame_exp == NULL)
-    numargs = 0;
-  else
-    {
-      numargs = 0;
-      while (1)
-	{
-	  const char *p;
-
-	  /* Skip leading white space, bail of EOL.  */
-	  frame_exp = skip_spaces (frame_exp);
-	  if (!*frame_exp)
-	    break;
-
-	  /* Parse the argument, extract it, save it.  */
-	  for (p = frame_exp;
-	       *p && !ISSPACE (*p);
-	       p++);
-	  std::string addr_string (frame_exp, p - frame_exp);
-	  frame_exp = p;
-	  
-	  /* NOTE: Parse and evaluate expression, but do not use
-	     functions such as parse_and_eval_long or
-	     parse_and_eval_address to also extract the value.
-	     Instead value_as_long and value_as_address are used.
-	     This avoids problems with expressions that contain
-	     side-effects.  */
-	  if (numargs >= ARRAY_SIZE (args))
-	    error (_("Too many args in frame specification"));
-	  args[numargs++] = parse_and_eval (addr_string.c_str ());
-	}
-    }
-
-  /* If no args, default to the selected frame.  */
-  if (numargs == 0)
-    {
-      if (selected_frame_p != NULL)
-	(*selected_frame_p) = 1;
-      return get_selected_frame (_("No stack."));
-    }
-
-  /* None of the remaining use the selected frame.  */
-  if (selected_frame_p != NULL)
-    (*selected_frame_p) = 0;
-
-  /* Assume the single arg[0] is an integer, and try using that to
-     select a frame relative to current.  */
-  if (numargs == 1)
-    {
-      struct frame_info *fid;
-      int level = value_as_long (args[0]);
-
-      fid = find_relative_frame (get_current_frame (), &level);
-      if (level == 0)
-	/* find_relative_frame was successful.  */
-	return fid;
-    }
-
-  /* Convert each value into a corresponding address.  */
-  {
-    int i;
-
-    for (i = 0; i < numargs; i++)
-      addrs[i] = value_as_address (args[i]);
-  }
-
-  /* Assume that the single arg[0] is an address, use that to identify
-     a frame with a matching ID.  Should this also accept stack/pc or
-     stack/pc/special.  */
-  if (numargs == 1)
-    {
-      struct frame_id id = frame_id_build_wild (addrs[0]);
-      struct frame_info *fid;
-
-      /* If (s)he specifies the frame with an address, he deserves
-	 what (s)he gets.  Still, give the highest one that matches.
-	 (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't
-	 know).  */
-      for (fid = get_current_frame ();
-	   fid != NULL;
-	   fid = get_prev_frame (fid))
-	{
-	  if (frame_id_eq (id, get_frame_id (fid)))
-	    {
-	      struct frame_info *prev_frame;
-
-	      while (1)
-		{
-		  prev_frame = get_prev_frame (fid);
-		  if (!prev_frame
-		      || !frame_id_eq (id, get_frame_id (prev_frame)))
-		    break;
-		  fid = prev_frame;
-		}
-	      return fid;
-	    }
-	}
-      }
-
-  /* We couldn't identify the frame as an existing frame, but
-     perhaps we can create one with a single argument.  */
-  if (numargs == 1)
-    return create_new_frame (addrs[0], 0);
-  else if (numargs == 2)
-    return create_new_frame (addrs[0], addrs[1]);
-  else
-    error (_("Too many args in frame specification"));
+  /* This is used to complete function names within a stack.  It would be
+     nice if we only offered functions that were actually in the stack.
+     However, this would mean unwinding the stack to completion, which
+     could take too long, or on a corrupted stack, possibly not end.
+     Instead, we offer all symbol names as a safer choice.  */
+  collect_symbol_completion_matches (tracker,
+				     complete_symbol_mode::EXPRESSION,
+				     symbol_name_match_type::EXPRESSION,
+				     text, word);
 }
 
-/* Print verbosely the selected frame or the frame at address
-   ADDR_EXP.  Absolutely all information in the frame is printed.  */
+/* Core of all the "info frame" sub-commands.  Print information about a
+   frame FI.  If SELECTED_FRAME_P is true then the user didn't provide a
+   frame specification, they just entered 'info frame'.  If the user did
+   provide a frame specification (for example 'info frame 0', 'info frame
+   level 1') then SELECTED_FRAME_P will be false.  */
 
 static void
-info_frame_command (const char *addr_exp, int from_tty)
+info_frame_command_core (struct frame_info *fi, bool selected_frame_p)
 {
-  struct frame_info *fi;
   struct symbol *func;
   struct symtab *s;
   struct frame_info *calling_frame_info;
@@ -1393,7 +1295,6 @@ info_frame_command (const char *addr_exp, int from_tty)
   const char *funname = 0;
   enum language funlang = language_unknown;
   const char *pc_regname;
-  int selected_frame_p;
   struct gdbarch *gdbarch;
   CORE_ADDR frame_pc;
   int frame_pc_p;
@@ -1401,7 +1302,6 @@ info_frame_command (const char *addr_exp, int from_tty)
   CORE_ADDR caller_pc = 0;
   int caller_pc_p = 0;
 
-  fi = parse_frame_specification (addr_exp, &selected_frame_p);
   gdbarch = get_frame_arch (fi);
 
   /* Name of the value returned by get_frame_pc().  Per comments, "pc"
@@ -1742,6 +1642,157 @@ trailing_outermost_frame (int count)
   return trailing;
 }
 
+/* The core of all the "select-frame" sub-commands.  Just wraps a call to
+   SELECT_FRAME.  */
+
+static void
+select_frame_command_core (struct frame_info *fi, bool ignored)
+{
+  struct frame_info *prev_frame = get_selected_frame_if_set ();
+  select_frame (fi);
+  if (get_selected_frame_if_set () != prev_frame)
+    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
+}
+
+/* See stack.h.  */
+
+void
+select_frame_for_mi (struct frame_info *fi)
+{
+  select_frame_command_core (fi, FALSE /* Ignored.  */);
+}
+
+/* The core of all the "frame" sub-commands.  Select frame FI, and if this
+   means we change frame send out a change notification (otherwise, just
+   reprint the current frame summary).   */
+
+static void
+frame_command_core (struct frame_info *fi, bool ignored)
+{
+  struct frame_info *prev_frame = get_selected_frame_if_set ();
+
+  select_frame (fi);
+  if (get_selected_frame_if_set () != prev_frame)
+    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
+  else
+    print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME);
+}
+
+/* The three commands 'frame', 'select-frame', and 'info frame' all have a
+   common set of sub-commands that allow a specific frame to be selected.
+   All of the sub-command functions are static methods within this class
+   template which is then instantiated below.  The template parameter is a
+   callback used to implement the functionality of the base command
+   ('frame', 'select-frame', or 'info frame').
+
+   In the template parameter FI is the frame being selected.  The
+   SELECTED_FRAME_P flag is true if the frame being selected was done by
+   default, which happens when the user uses the base command with no
+   arguments.  For example the commands 'info frame', 'select-frame',
+   'frame' will all cause SELECTED_FRAME_P to be true.  In all other cases
+   SELECTED_FRAME_P is false.  */
+
+template <void (*FPTR) (struct frame_info *fi, bool selected_frame_p)>
+class frame_command_helper
+{
+public:
+
+  /* The "frame level" family of commands.  The ARG is an integer that is
+     the frame's level in the stack.  */
+  static void
+  level (const char *arg, int from_tty)
+  {
+    int level = value_as_long (parse_and_eval (arg));
+    struct frame_info *fid
+      = find_relative_frame (get_current_frame (), &level);
+    if (level != 0)
+      error (_("No frame at level %s."), arg);
+    FPTR (fid, false);
+  }
+
+  /* The "frame address" family of commands.  ARG is a stack-pointer
+     address for an existing frame.  This command does not allow new
+     frames to be created.  */
+
+  static void
+  address (const char *arg, int from_tty)
+  {
+    CORE_ADDR addr = value_as_address (parse_and_eval (arg));
+    struct frame_info *fid = find_frame_for_address (addr);
+    if (fid == NULL)
+      error (_("No frame at address %s."), arg);
+    FPTR (fid, false);
+  }
+
+  /* The "frame view" family of commands.  ARG is one or two addresses and
+     is used to view a frame that might be outside the current backtrace.
+     The addresses are stack-pointer address, and (optional) pc-address.  */
+
+  static void
+  view (const char *args, int from_tty)
+  {
+    struct frame_info *fid;
+
+    if (args == NULL)
+    error (_("Missing address argument to view a frame"));
+
+    gdb_argv argv (args);
+
+    if (argv.count () == 2)
+      {
+	CORE_ADDR addr[2];
+
+	addr [0] = value_as_address (parse_and_eval (argv[0]));
+	addr [1] = value_as_address (parse_and_eval (argv[1]));
+	fid = create_new_frame (addr[0], addr[1]);
+      }
+    else
+      {
+	CORE_ADDR addr = value_as_address (parse_and_eval (argv[0]));
+	fid = create_new_frame (addr, false);
+      }
+    FPTR (fid, false);
+  }
+
+  /* The "frame function" family of commands.  ARG is the name of a
+     function within the stack, the first function (searching from frame
+     0) with that name will be selected.  */
+
+  static void
+  function (const char *arg, int from_tty)
+  {
+    if (arg == NULL)
+      error (_("Missing function name argument"));
+    struct frame_info *fid = find_frame_for_function (arg);
+    if (fid == NULL)
+      error (_("No frame for function \"%s\"."), arg);
+    FPTR (fid, false);
+  }
+
+  /* The "frame" base command, that is, when no sub-command is specified.
+     If one argument is provided then we assume that this is a frame's
+     level as historically, this was the supported command syntax that was
+     used most often.
+
+     If no argument is provided, then the current frame is selected.  */
+
+  static void
+  base_command (const char *arg, int from_tty)
+  {
+    if (arg == NULL)
+      FPTR (get_selected_frame (_("No stack.")), true);
+    else
+      level (arg, from_tty);
+  }
+};
+
+/* Instantiate three FRAME_COMMAND_HELPER instances to implement the
+   sub-commands for 'info frame', 'frame', and 'select-frame' commands.  */
+
+static frame_command_helper <info_frame_command_core> info_frame_cmd;
+static frame_command_helper <frame_command_core> frame_cmd;
+static frame_command_helper <select_frame_command_core> select_frame_cmd;
+
 /* Print briefly all stack frames or just the innermost COUNT_EXP
    frames.  */
 
@@ -2230,39 +2281,6 @@ find_relative_frame (struct frame_info *frame, int *level_offset_ptr)
   return frame;
 }
 
-/* The "select_frame" command.  With no argument this is a NOP.
-   Select the frame at level LEVEL_EXP if it is a valid level.
-   Otherwise, treat LEVEL_EXP as an address expression and select it.
-
-   See parse_frame_specification for more info on proper frame
-   expressions.  */
-
-void
-select_frame_command (const char *level_exp, int from_tty)
-{
-  struct frame_info *prev_frame = get_selected_frame_if_set ();
-
-  select_frame (parse_frame_specification (level_exp, NULL));
-  if (get_selected_frame_if_set () != prev_frame)
-    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
-}
-
-/* The "frame" command.  With no argument, print the selected frame
-   briefly.  With an argument, behave like select_frame and then print
-   the selected frame.  */
-
-static void
-frame_command (const char *level_exp, int from_tty)
-{
-  struct frame_info *prev_frame = get_selected_frame_if_set ();
-
-  select_frame (parse_frame_specification (level_exp, NULL));
-  if (get_selected_frame_if_set () != prev_frame)
-    gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
-  else
-    print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME);
-}
-
 /* Select the frame up one or COUNT_EXP stack levels from the
    previously selected frame, and print it briefly.  */
 
@@ -2716,13 +2734,61 @@ faas_command (const char *cmd, int from_tty)
 }
 
 
+/* Find inner-mode frame with frame address ADDRESS.  Return NULL if no
+   matching frame can be found.  */
+
+static struct frame_info *
+find_frame_for_address (CORE_ADDR address)
+{
+  struct frame_id id;
+  struct frame_info *fid;
+
+  id = frame_id_build_wild (address);
+
+  /* If (s)he specifies the frame with an address, he deserves
+     what (s)he gets.  Still, give the highest one that matches.
+     (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't
+     know).  */
+  for (fid = get_current_frame ();
+       fid != NULL;
+       fid = get_prev_frame (fid))
+    {
+      if (frame_id_eq (id, get_frame_id (fid)))
+	{
+	  struct frame_info *prev_frame;
+
+	  while (1)
+	    {
+	      prev_frame = get_prev_frame (fid);
+	      if (!prev_frame
+		  || !frame_id_eq (id, get_frame_id (prev_frame)))
+		break;
+	      fid = prev_frame;
+	    }
+	  return fid;
+	}
+    }
+  return NULL;
+}
+
+\f
+
+/* Commands with a prefix of `frame apply'.  */
+static struct cmd_list_element *frame_apply_cmd_list = NULL;
+
 /* Commands with a prefix of `frame'.  */
-struct cmd_list_element *frame_cmd_list = NULL;
+static struct cmd_list_element *frame_cmd_list = NULL;
+
+/* Commands with a prefix of `select frame'.  */
+static struct cmd_list_element *select_frame_cmd_list = NULL;
+
+/* Commands with a prefix of `info frame'.  */
+static struct cmd_list_element *info_frame_cmd_list = NULL;
 
 void
 _initialize_stack (void)
 {
-  static struct cmd_list_element *frame_apply_list = NULL;
+  struct cmd_list_element *cmd;
 
   add_com ("return", class_stack, return_command, _("\
 Make selected stack frame return to its caller.\n\
@@ -2746,12 +2812,12 @@ An argument says how many frames down to go."));
 Same as the `down' command, but does not print anything.\n\
 This is useful in command scripts."));
 
-  add_prefix_cmd ("frame", class_stack, frame_command, _("\
-Select and print a stack frame.\nWith no argument, \
-print the selected stack frame.  (See also \"info frame\").\n\
-An argument specifies the frame to select.\n\
-It can be a stack frame number or the address of the frame."),
-		  &frame_cmd_list, "frame ", 1, &cmdlist);
+  add_prefix_cmd ("frame", class_stack,
+                  &frame_cmd.base_command, _("\
+Select and print a stack frame.\n\
+With no argument, print the selected stack frame.  (See also \"info frame\").\n\
+A single numerical argument specifies the frame to select."),
+                  &frame_cmd_list, "frame ", 1, &cmdlist);
 
   add_com_alias ("f", "frame", class_stack, 1);
 
@@ -2769,7 +2835,7 @@ or produces no output."
 Usage: frame apply COUNT [FLAG]... COMMAND\n\
 With a negative COUNT argument, applies the command on outermost -COUNT frames.\n"
 FRAME_APPLY_FLAGS_HELP),
-		  &frame_apply_list, "frame apply ", 1, &frame_cmd_list);
+		  &frame_apply_cmd_list, "frame apply ", 1, &frame_cmd_list);
 
   add_cmd ("all", class_stack, frame_apply_all_command,
 	   _("\
@@ -2777,7 +2843,7 @@ Apply a command to all frames.\n\
 \n\
 Usage: frame apply all [FLAG]... COMMAND\n"
 FRAME_APPLY_FLAGS_HELP),
-	   &frame_apply_list);
+	   &frame_apply_cmd_list);
 
   add_cmd ("level", class_stack, frame_apply_level_command,
 	   _("\
@@ -2786,19 +2852,97 @@ Apply a command to a list of frames.\n\
 Usage: frame apply level LEVEL... [FLAG]... COMMAND\n\
 ID is a space-separated list of LEVELs of frames to apply COMMAND on.\n"
 FRAME_APPLY_FLAGS_HELP),
-	   &frame_apply_list);
+	   &frame_apply_cmd_list);
 
   add_com ("faas", class_stack, faas_command, _("\
 Apply a command to all frames (ignoring errors and empty output).\n\
 Usage: faas COMMAND\n\
 shortcut for 'frame apply all -s COMMAND'"));
 
-  add_com_suppress_notification ("select-frame", class_stack, select_frame_command, _("\
+
+  add_prefix_cmd ("frame", class_stack,
+		  &frame_cmd.base_command, _("\
+Select and print a stack frame.\n\
+With no argument, print the selected stack frame.  (See also \"info frame\").\n\
+A single numerical argument specifies the frame to select."),
+		  &frame_cmd_list, "frame ", 1, &cmdlist);
+  add_com_alias ("f", "frame", class_stack, 1);
+
+  add_cmd ("address", class_stack, &frame_cmd.address,
+	   _("\
+Select and print a stack frame by stack address\n\
+\n\
+Usage: frame address STACK-ADDRESS"),
+	   &frame_cmd_list);
+
+  add_cmd ("view", class_stack, &frame_cmd.view,
+	   _("\
+View a stack frame that might be outside the current backtrace.\n\
+\n\
+Usage: frame view STACK-ADDRESS\n\
+       frame view STACK-ADDRESS PC-ADDRESS"),
+	   &frame_cmd_list);
+
+  cmd = add_cmd ("function", class_stack, &frame_cmd.function,
+	   _("\
+Select and print a stack frame by function name.\n\
+\n\
+Usage: frame function NAME\n\
+\n\
+The innermost frame that visited function NAME is selected."),
+	   &frame_cmd_list);
+  set_cmd_completer (cmd, frame_selection_by_function_completer);
+
+
+  add_cmd ("level", class_stack, &frame_cmd.level,
+	   _("\
+Select and print a stack frame by level.\n\
+\n\
+Usage: frame level LEVEL"),
+	   &frame_cmd_list);
+
+  cmd = add_prefix_cmd_suppress_notification ("select-frame", class_stack,
+		      &select_frame_cmd.base_command, _("\
 Select a stack frame without printing anything.\n\
-An argument specifies the frame to select.\n\
-It can be a stack frame number or the address of the frame."),
+A single numerical argument specifies the frame to select."),
+		      &select_frame_cmd_list, "select-frame ", 1, &cmdlist,
+		      &cli_suppress_notification.user_selected_context);
+
+  add_cmd_suppress_notification ("address", class_stack,
+			 &select_frame_cmd.address, _("\
+Select a stack frame by stack address.\n\
+\n\
+Usage: select-frame address STACK-ADDRESS"),
+			 &select_frame_cmd_list,
+			 &cli_suppress_notification.user_selected_context);
+
+
+  add_cmd_suppress_notification ("view", class_stack,
+		 &select_frame_cmd.view, _("\
+Select a stack frame that might be outside the current backtrace.\n\
+\n\
+Usage: select-frame view STACK-ADDRESS\n\
+       select-frame view STACK-ADDRESS PC-ADDRESS"),
+		 &select_frame_cmd_list,
 		 &cli_suppress_notification.user_selected_context);
 
+  cmd = add_cmd_suppress_notification ("function", class_stack,
+	       &select_frame_cmd.function, _("\
+Select a stack frame by function name.\n\
+\n\
+Usage: select-frame function NAME"),
+	       &select_frame_cmd_list,
+	       &cli_suppress_notification.user_selected_context);
+  set_cmd_completer (cmd, frame_selection_by_function_completer);
+
+  add_cmd_suppress_notification ("level", class_stack,
+			 &select_frame_cmd.level, _("\
+Select a stack frame by level.\n\
+\n\
+Usage: select-frame level LEVEL"),
+			 &select_frame_cmd_list,
+			 &cli_suppress_notification.user_selected_context);
+
   add_com ("backtrace", class_stack, backtrace_command, _("\
 Print backtrace of all stack frames, or innermost COUNT frames.\n\
 Usage: backtrace [QUALIFIERS]... [COUNT]\n\
@@ -2812,9 +2956,45 @@ on this backtrace."));
   add_info ("stack", backtrace_command,
 	    _("Backtrace of the stack, or innermost COUNT frames."));
   add_info_alias ("s", "stack", 1);
-  add_info ("frame", info_frame_command,
-	    _("All about selected stack frame, or frame at ADDR."));
+
+  add_prefix_cmd ("frame", class_info, &info_frame_cmd.base_command,
+		  _("All about the selected stack frame.\n\
+With no arguments, displays information about the currently selected stack\n\
+frame.  Alternatively a frame specification may be provided (See \"frame\")\n\
+the information is then printed about the specified frame."),
+		  &info_frame_cmd_list, "info frame ", 1, &infolist);
   add_info_alias ("f", "frame", 1);
+
+  add_cmd ("address", class_stack, &info_frame_cmd.address,
+	   _("\
+Print information about a stack frame selected by stack address.\n\
+\n\
+Usage: info frame address STACK-ADDRESS"),
+	   &info_frame_cmd_list);
+
+  add_cmd ("view", class_stack, &info_frame_cmd.view,
+	   _("\
+Print information about a stack frame outside the current backtrace.\n\
+\n\
+Usage: info frame view STACK-ADDRESS\n\
+       info frame view STACK-ADDRESS PC-ADDRESS"),
+	   &info_frame_cmd_list);
+
+  cmd = add_cmd ("function", class_stack, &info_frame_cmd.function,
+	   _("\
+Print information about a stack frame selected by function name.\n\
+\n\
+Usage: info frame function NAME"),
+	   &info_frame_cmd_list);
+  set_cmd_completer (cmd, frame_selection_by_function_completer);
+
+  add_cmd ("level", class_stack, &info_frame_cmd.level,
+	   _("\
+Print information about a stack frame selected by level.\n\
+\n\
+Usage: info frame level LEVEL"),
+	   &info_frame_cmd_list);
+
   add_info ("locals", info_locals_command,
 	    _("Local variables of current stack frame."));
   add_info ("args", info_args_command,
diff --git a/gdb/stack.h b/gdb/stack.h
index ca190efa9c3..3681120a776 100644
--- a/gdb/stack.h
+++ b/gdb/stack.h
@@ -20,7 +20,11 @@
 #ifndef STACK_H
 #define STACK_H
 
-void select_frame_command (const char *level_exp, int from_tty);
+/* Access method used by the MI -stack-select-frame command to switch to
+   frame FI.  This differs from SELECT_FRAME in that the observers for a
+   user selected context change will be triggered.  */
+
+void select_frame_for_mi (struct frame_info *fi);
 
 gdb::unique_xmalloc_ptr<char> find_frame_funname (struct frame_info *frame,
 						  enum language *funlang,
diff --git a/gdb/testsuite/gdb.base/frame-selection.c b/gdb/testsuite/gdb.base/frame-selection.c
new file mode 100644
index 00000000000..f3d81f223e0
--- /dev/null
+++ b/gdb/testsuite/gdb.base/frame-selection.c
@@ -0,0 +1,52 @@
+/* Copyright 2018 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int
+frame_2 (void)
+{
+  return 0;
+}
+
+int
+frame_1 (void)
+{
+  return frame_2 ();
+}
+
+int
+recursive (int arg)
+{
+  int v;
+
+  if (arg < 2)
+    v = recursive (arg + 1);
+  else
+    v = frame_2 ();
+
+  return v;
+}
+
+int
+main (void)
+{
+  int i, j;
+
+  i = frame_1 ();
+  j = recursive (0);
+
+  return i + j;
+}
diff --git a/gdb/testsuite/gdb.base/frame-selection.exp b/gdb/testsuite/gdb.base/frame-selection.exp
new file mode 100644
index 00000000000..2dde33fef0d
--- /dev/null
+++ b/gdb/testsuite/gdb.base/frame-selection.exp
@@ -0,0 +1,188 @@
+# Copyright 2018 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This tests GDB's frame selection as used by the 'frame',
+# 'select-frame', and 'info frame' commands.
+
+standard_testfile
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug}]} {
+    return -1
+}
+
+runto_main
+gdb_breakpoint frame_2
+gdb_continue_to_breakpoint frame_2
+
+gdb_test "bt" "#0  frame_2.*#1  $hex in frame_1.*#2  $hex in main.*" "backtrace at breakpoint"
+
+# Perform "info frame" to extract the frame's address.
+proc get_frame_address { {testname ""} } {
+    global hex gdb_prompt
+
+    set frame_address "unknown"
+    set testname "get_frame_address: ${testname}"
+    gdb_test_multiple "info frame" $testname {
+	-re ", frame at ($hex):\r\n.*\r\n$gdb_prompt $" {
+	    set frame_address $expect_out(1,string)
+	    pass $testname
+	}
+    }
+
+    return $frame_address
+}
+
+# Passed a list of addresses.  Return a new address that is not in the
+# list by sorting the addresses and adding 0x10 to the highest
+# address.
+proc get_new_address { {addresses {}} } {
+    # Find the highest element in the list.
+    set elem [lindex [lsort -integer -decreasing $addresses] 0]
+
+    # Return a new address as a hex formatted string.
+    return [format "%#x" [expr $elem + 0x10]]
+}
+
+
+# Check that the current frame is at stack depth LEVEL, at frame
+# address ADDRESS, and is in FUNCTION.
+proc check_frame { level address function } {
+    global hex gdb_prompt
+
+    set re [multi_line \
+		"Stack level ${level}, frame at ($address):" \
+		".* = $hex in ${function} \(\[^\r\n\]*\); saved .* = $hex" \
+		".*\r\n$gdb_prompt $" ]
+
+    set testname "check frame level ${level}"
+    gdb_test_multiple "info frame" $testname {
+	-re $re {
+	    pass $testname
+	}
+    }
+}
+
+# Select frame using level, but relying on this being the default
+# action, so "frame 0" performs "frame level 0".
+gdb_test "frame 0" "#0  frame_2.*"
+set frame_0_address [ get_frame_address "frame 0" ]
+gdb_test "frame 1" "#1  $hex in frame_1.*"
+set frame_1_address [ get_frame_address "frame 1" ]
+gdb_test "frame 2" "#2  $hex in main.*"
+set frame_2_address [ get_frame_address "frame 2" ]
+gdb_test "frame 3" "No frame at level 3\."
+
+# Find an address that matches no frame.
+set no_frame_address [ get_new_address [list $frame_0_address \
+					     $frame_1_address \
+					     $frame_2_address] ]
+
+# Select frame using 'level' specification.
+gdb_test "frame level 0" "#0  frame_2.*"
+gdb_test "frame level 1" "#1  $hex in frame_1.*"
+gdb_test "frame level 2" "#2  $hex in main.*"
+gdb_test "frame level 3" "No frame at level 3\."
+
+# Select frame by address.
+gdb_test "frame address ${frame_0_address}" "#0  frame_2.*" \
+    "select frame 0 by address"
+gdb_test "frame address ${frame_1_address}" "#1  $hex in frame_1.*" \
+    "select frame 1 by address"
+gdb_test "frame address ${frame_2_address}" "#2  $hex in main.*" \
+    "select frame 2 by address"
+gdb_test "frame address ${no_frame_address}" \
+    "No frame at address ${no_frame_address}\." \
+    "attempt to select a frame at an invalid address"
+
+# Select frame by function.
+gdb_test "frame function frame_2" "#0  frame_2.*"
+gdb_test "frame function frame_1" "#1  $hex in frame_1.*"
+gdb_test "frame function main" "#2  $hex in main.*"
+
+# Check for a distinction between a known function not in the stack
+# trace, and an unknown function.
+gdb_test "frame function recursive" "No frame for function \"recursive\"."
+gdb_test "frame function foo" "Function \"foo\" not defined."
+
+
+with_test_prefix "select-frame, no keyword" {
+    gdb_test_no_output "select-frame 0"
+    check_frame "0" "${frame_0_address}" "frame_2"
+    gdb_test_no_output "select-frame 1"
+    check_frame "1" "${frame_1_address}" "frame_1"
+    gdb_test_no_output "select-frame 2"
+    check_frame "2" "${frame_2_address}" "main"
+    gdb_test "select-frame 3" "No frame at level 3\."
+}
+
+with_test_prefix "select-frame, keyword=level" {
+    gdb_test_no_output "select-frame level 0"
+    check_frame "0" "${frame_0_address}" "frame_2"
+    gdb_test_no_output "select-frame level 1"
+    check_frame "1" "${frame_1_address}" "frame_1"
+    gdb_test_no_output "select-frame level 2"
+    check_frame "2" "${frame_2_address}" "main"
+    gdb_test "select-frame level 3" "No frame at level 3\."
+}
+
+with_test_prefix "select-frame, keyword=address" {
+    gdb_test_no_output "select-frame address ${frame_0_address}" \
+	"select frame 0 by address"
+    check_frame "0" "${frame_0_address}" "frame_2"
+    gdb_test_no_output "select-frame address ${frame_1_address}" \
+	"select frame 1 by address"
+    check_frame "1" "${frame_1_address}" "frame_1"
+    gdb_test_no_output "select-frame address ${frame_2_address}" \
+	"select frame 2 by address"
+    check_frame "2" "${frame_2_address}" "main"
+    gdb_test "select-frame address ${no_frame_address}" \
+	"No frame at address ${no_frame_address}\." \
+	"select-frame for an invalid address"
+}
+
+with_test_prefix "select-frame, keyword=function" {
+    gdb_test_no_output "select-frame function frame_2"
+    check_frame "0" "${frame_0_address}" "frame_2"
+    gdb_test_no_output "select-frame function frame_1"
+    check_frame "1" "${frame_1_address}" "frame_1"
+    gdb_test_no_output "select-frame function main"
+    check_frame "2" "${frame_2_address}" "main"
+}
+
+# Check for a distinction between a known function not in the stack
+# trace, and an unknown function.
+gdb_test "select-frame function recursive" \
+    "No frame for function \"recursive\"."
+gdb_test "select-frame function foo" \
+    "Function \"foo\" not defined."
+
+# Now continue until we hit the breakpoint again.
+with_test_prefix "second frame_2 breakpoint" {
+    gdb_continue_to_breakpoint frame_2
+    gdb_test "bt" \
+	"#0  frame_2.*#1  $hex in recursive.*#2  $hex in recursive.*#3  $hex in recursive.*#4  $hex in main.*" \
+ 	"backtrace at breakpoint with recursive frames"
+
+    # Check "frame function" when a function name occurs multiple times in
+    # the stack.  The inner most (lowest level) should always be selected.
+    gdb_test "frame function frame_2" "#0  frame_2.*"
+    gdb_test "frame function recursive" "#1  $hex in recursive.*" \
+	"select frame for function recursive, first attempt"
+    gdb_test "frame function recursive" "#1  $hex in recursive.*" \
+		"select frame for function recursive, second attempt"
+    gdb_test "frame function main" "#4  $hex in main.*"
+    gdb_test "frame function recursive" "#1  $hex in recursive.*" \
+	"select frame for function recursive, third attempt"
+}
-- 
2.14.4

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCHv6] gdb: Change how frames are selected for 'frame' and 'info frame'.
  2018-09-26 23:06                             ` Andrew Burgess
@ 2018-09-27 20:58                               ` Pedro Alves
  0 siblings, 0 replies; 35+ messages in thread
From: Pedro Alves @ 2018-09-27 20:58 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

On 09/27/2018 12:06 AM, Andrew Burgess wrote:> Thanks for the review.
> 
> This revision makes the following changes:
> 
>   - Addresses the two small grammatical errors, and removes the random
>     comment line in the testsuite.
> 
>   - Rewrites the get_new_address function in the testsuite so that is
>     actually does what it says on the tin (in line with your
>     suggestion).
> 
>   - Extends the header comment on parse_frame_specification.  While
>     doing this I realised that due to the way this function is now
>     used it can be simplified a lot.  We now only handle a single
>     argument.  I'd rather not start extending the existing MI
>     interface with new features at this point, and I didn't think
>     keeping the more complex parse_frame_specification around "just in
>     case" was good practice.
> 
> It's because of this last change that I've posted the patch for
> another review.  I think the change is sensible (and the tests pass),
> but I wanted another opinion.

I agree.  LGTM.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 35+ messages in thread

end of thread, other threads:[~2018-09-27 20:58 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-05-08 16:58 [PATCHv2 0/2] Changes to frame selection Andrew Burgess
2018-05-08 16:58 ` [PATCHv2 1/2] gdb: Split func_command into two parts Andrew Burgess
2018-05-18 19:57   ` Pedro Alves
2018-05-21 15:52     ` Andrew Burgess
2018-05-21 16:06       ` Pedro Alves
2018-05-08 16:59 ` [PATCHv2 2/2] gdb: Change how frames are selected for 'frame' and 'info frame' Andrew Burgess
2018-05-11 15:44   ` Eli Zaretskii
2018-05-21 12:16     ` Andrew Burgess
2018-05-21 17:46       ` Eli Zaretskii
2018-06-05 18:53         ` Andrew Burgess
2018-06-05 21:16           ` Philippe Waroquiers
2018-06-06  8:22             ` Andrew Burgess
2018-06-06 14:56               ` Eli Zaretskii
2018-06-07 16:19   ` [PATCHv3] " Andrew Burgess
2018-06-29 12:23     ` Andrew Burgess
2018-07-17 15:58     ` [PATCHv4] " Andrew Burgess
2018-07-23 20:46       ` Philippe Waroquiers
2018-07-25 18:14         ` Andrew Burgess
2018-08-13 22:20           ` [PATCHv5 0/2] " Andrew Burgess
2018-08-14 10:31             ` Philippe Waroquiers
2018-08-21 13:10               ` Joel Brobecker
2018-08-27 11:04             ` Andrew Burgess
2018-08-27 15:23               ` Eli Zaretskii
2018-08-28  8:43                 ` Andrew Burgess
2018-08-28  9:08                   ` Eli Zaretskii
2018-08-28 18:03                     ` [PATCHv6] " Andrew Burgess
2018-08-28 18:20                       ` Eli Zaretskii
2018-09-05  7:46                       ` PING: " Andrew Burgess
2018-09-13 18:02                       ` Pedro Alves
2018-09-18 23:01                         ` Andrew Burgess
2018-09-19 16:26                           ` Pedro Alves
2018-09-26 23:06                             ` Andrew Burgess
2018-09-27 20:58                               ` Pedro Alves
     [not found]           ` <cover.1534197765.git.andrew.burgess@embecosm.com>
2018-08-13 22:20             ` [PATCHv5_B 2/2] " Andrew Burgess
2018-08-13 22:20           ` [PATCHv5_A 1/2] " Andrew Burgess

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).