diff options
-rw-r--r-- | gdb/ChangeLog | 39 | ||||
-rw-r--r-- | gdb/NEWS | 8 | ||||
-rw-r--r-- | gdb/cli/cli-decode.c | 44 | ||||
-rw-r--r-- | gdb/command.h | 14 | ||||
-rw-r--r-- | gdb/doc/ChangeLog | 8 | ||||
-rw-r--r-- | gdb/doc/gdb.texinfo | 111 | ||||
-rw-r--r-- | gdb/mi/mi-cmd-stack.c | 77 | ||||
-rw-r--r-- | gdb/stack.c | 526 | ||||
-rw-r--r-- | gdb/stack.h | 6 | ||||
-rw-r--r-- | gdb/testsuite/ChangeLog | 5 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/frame-selection.c | 52 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/frame-selection.exp | 188 |
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. @@ -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" +} |