diff mbox

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

Message ID 20180828180327.30992-1-andrew.burgess@embecosm.com
State New
Headers show

Commit Message

Andrew Burgess Aug. 28, 2018, 6:03 p.m. UTC
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

Comments

Eli Zaretskii Aug. 28, 2018, 6:20 p.m. UTC | #1
> 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.
Andrew Burgess Sept. 5, 2018, 7:45 a.m. UTC | #2
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,
>  }
>  
>  
> -/* 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;
> +}
> +
> +
> +
> +/* 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
>
Pedro Alves Sept. 13, 2018, 6:02 p.m. UTC | #3
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,
>  }
>  
>  
> -/* 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;
> +}
> +
> +
> +
> +/* 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
diff mbox

Patch

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,
 }
 
 
-/* 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;
+}
+
+
+
+/* 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"