This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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


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
> 


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]