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