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