Message ID | 20180607161915.9425-1-andrew.burgess@embecosm.com |
---|---|
State | New |
Headers | show |
Ping! Thanks, Andrew * Andrew Burgess <andrew.burgess@embecosm.com> [2018-06-07 17:19:15 +0100]: > Thanks to Eli and Philippe for their feedback. This version is > updated in line with the their guidance, rebased onto current HEAD, > and retested. > > --- > > The 'frame' command, and thanks to code reuse the 'info frame' and > 'select-frame' commands, currently have an overloaded mechanism for > selecting a frame. > > These commands take one or two parameters, if it's one parameter then > we first try to use the parameter as an integer to select a frame by > level (or depth in the stack). If that fails then we treat the > parameter as an address and try to select a stack frame by > stack-address. If we still have not selected a stack frame, or we > initially had two parameters, then GDB allows the user to view a stack > frame that is not part of the current backtrace. Internally, a new > frame is created with the given stack and pc addresses, and this is > shown to the user. > > The result of this is that a typo by the user, entering the wrong stack > frame level for example, can result in a brand new frame being viewed > rather than an error. > > The purpose of this commit is to remove this overloading, while still > offering the same functionality through some new sub-commands. By > making the default behaviour of 'frame' (and friends) be to select a > stack frame by level number, it is hoped that enough > backwards-compatibility is maintained that users will not be overly > inconvenienced. > > The 'frame', 'select-frame', and 'info frame' commands now all take a > frame specification string as an argument, this string can be any of the > following: > > (1) An integer. This is treated as a frame number. If a frame for > that number does not exist then the user gets an error. > > (2) A string like 'level <NUMBER>', where <NUMBER> is a frame number > as in option (1) above. > > (3) A string like 'address <ADDRESS>', where <ADDRESS> is a > stack-frame address. If there is no frame for this address then the > user gets an error. > > (4) A string like 'function <NAME>', where <NAME> is a function name, > the inner most frame for function <NAME> is selected. If there is no > frame for function <NAME> then the user gets an error. > > (5) A string like 'view <STACK-ADDRESS>', this views a new frame > with stack address <STACK-ADDRESS>. > > (6) A string like 'view <STACK-ADDRESS> <PC-ADDRESS>', this views > a new frame with stack address <STACK-ADDRESS> an the pc <PC-ADDRESS>. > > This change assumes that the most common use of the commands like > 'frame' is to select a frame by frame level number, it is for this > reason that this is the behaviour that is kept for backwards > compatibility. Any of the alternative behaviours, which are assumed to > be less used, now require a change in user behaviour. > > The MI command '-stack-select-frame' has also changed in the same way. > The default behaviour is to select a frame by level number, but the > other selection methods are also available. > > gdb/ChangeLog: > > (NEWS): Mention changes to frame related commands. > * cli/cli-decode.c (add_cmd_suppress_notification): New function. > (add_prefix_cmd_suppress_notification): New function. > (add_com_suppress_notification): Call > add_cmd_suppress_notification. > * command.h (add_cmd_suppress_notification): Declare. > (add_prefix_cmd_suppress_notification): Declare. > * mi/mi-cmd-stack.c (mi_cmd_stack_select_frame): Use new function > select_frame_from_spec. > * stack.c (find_frame_for_function): Add declaration. > (find_frame_for_address): New function. > (parse_frame_specification): Delete function. > (frame_selection_by_function_completer): New function. > (info_frame_command): Rename to... > (info_frame_command_core): ...this, and update parameter types. > (select_frame_command): Rename to... > (select_frame_command_core): ...this, and update parameter types. > (frame_command): Rename to... > (frame_command_core): ...this, and update parameter types. > (class frame_command_helper): New class to wrap implementations of > frame related sub-commands. > (select_frame_from_spec): New function. > (frame_cmd_list): New global for sub-commands. > (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'. > * stack.h (select_frame_command): Delete declarationn. > (select_frame_from_spec): Declare new function. > > gdb/doc/ChangeLog: > > * gdb.texinfo (Selection): Rewrite documentation for 'frame' and > 'select-frame' commands. > (Frame Info): Rewrite documentation for 'info frame' command. > (GDB/MI Stack Manipulation): Update description of > '-stack-select-frame'. > > gdb/testsuite/ChangeLog: > > * gdb.base/frame-selection.exp: New file. > * gdb.base/frame-selection.c: New file. > * gdb.mi/mi-frame-selection.exp: New file. > * gdb.mi/mi-frame-selection.c: New file. > --- > gdb/ChangeLog | 32 ++ > gdb/NEWS | 8 + > gdb/cli/cli-decode.c | 44 ++- > gdb/command.h | 14 + > gdb/doc/ChangeLog | 8 + > gdb/doc/gdb.texinfo | 89 +++-- > gdb/mi/mi-cmd-stack.c | 4 +- > gdb/stack.c | 516 +++++++++++++++++++--------- > gdb/stack.h | 2 +- > gdb/testsuite/ChangeLog | 7 + > gdb/testsuite/gdb.base/frame-selection.c | 52 +++ > gdb/testsuite/gdb.base/frame-selection.exp | 156 +++++++++ > gdb/testsuite/gdb.mi/mi-frame-selection.c | 34 ++ > gdb/testsuite/gdb.mi/mi-frame-selection.exp | 89 +++++ > 14 files changed, 859 insertions(+), 196 deletions(-) > create mode 100644 gdb/testsuite/gdb.base/frame-selection.c > create mode 100644 gdb/testsuite/gdb.base/frame-selection.exp > create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.c > create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.exp > > diff --git a/gdb/NEWS b/gdb/NEWS > index 8fb6a2ad488..3de6620e4e8 100644 > --- a/gdb/NEWS > +++ b/gdb/NEWS > @@ -21,6 +21,14 @@ > * C expressions can now use _Alignof, and C++ expressions can now use > alignof. > > +* Changes to the "frame", "select-frame", and "info frame" CLI > + commands, as well as the "-stack-select-frame" MI command. These > + commands all now take a frame specification which is either a frame > + number, or one of the keywords '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 number is unchanged. > + > * New commands > > set debug fbsd-nat > diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c > index c8dda700ca8..fb44fc1627f 100644 > --- a/gdb/cli/cli-decode.c > +++ b/gdb/cli/cli-decode.c > @@ -251,6 +251,23 @@ add_cmd (const char *name, enum command_class theclass, > return result; > } > > +/* Add an element with a suppress notification to the LIST of commands. */ > + > +struct cmd_list_element * > +add_cmd_suppress_notification (const char *name, enum command_class theclass, > + cmd_const_cfunc_ftype *fun, const char *doc, > + struct cmd_list_element **list, > + int *suppress_notification) > +{ > + struct cmd_list_element *element; > + > + element = add_cmd (name, theclass, fun, doc, list); > + element->suppress_notification = suppress_notification; > + > + return element; > +} > + > + > /* Deprecates a command CMD. > REPLACEMENT is the name of the command which should be used in > place of this command, or NULL if no such command exists. > @@ -362,6 +379,25 @@ add_prefix_cmd (const char *name, enum command_class theclass, > return c; > } > > +/* Like ADD_PREFIX_CMD but sets the suppress_notification pointer on the > + new command list element. */ > + > +struct cmd_list_element * > +add_prefix_cmd_suppress_notification > + (const char *name, enum command_class theclass, > + cmd_const_cfunc_ftype *fun, > + const char *doc, struct cmd_list_element **prefixlist, > + const char *prefixname, int allow_unknown, > + struct cmd_list_element **list, > + int *suppress_notification) > +{ > + struct cmd_list_element *element > + = add_prefix_cmd (name, theclass, fun, doc, prefixlist, > + prefixname, allow_unknown, list); > + element->suppress_notification = suppress_notification; > + return element; > +} > + > /* Like add_prefix_cmd but sets the abbrev_flag on the new command. */ > > struct cmd_list_element * > @@ -893,12 +929,8 @@ add_com_suppress_notification (const char *name, enum command_class theclass, > cmd_const_cfunc_ftype *fun, const char *doc, > int *suppress_notification) > { > - struct cmd_list_element *element; > - > - element = add_cmd (name, theclass, fun, doc, &cmdlist); > - element->suppress_notification = suppress_notification; > - > - return element; > + return add_cmd_suppress_notification (name, theclass, fun, doc, > + &cmdlist, suppress_notification); > } > > /* Recursively walk the commandlist structures, and print out the > diff --git a/gdb/command.h b/gdb/command.h > index 3dde2475cb1..e3d55c2dcba 100644 > --- a/gdb/command.h > +++ b/gdb/command.h > @@ -148,6 +148,12 @@ extern struct cmd_list_element *add_cmd (const char *, enum command_class, > const char *, > struct cmd_list_element **); > > +extern struct cmd_list_element *add_cmd_suppress_notification > + (const char *name, enum command_class theclass, > + cmd_const_cfunc_ftype *fun, const char *doc, > + struct cmd_list_element **list, > + int *suppress_notification); > + > extern struct cmd_list_element *add_alias_cmd (const char *, const char *, > enum command_class, int, > struct cmd_list_element **); > @@ -165,6 +171,14 @@ extern struct cmd_list_element *add_prefix_cmd (const char *, enum command_class > const char *, int, > struct cmd_list_element **); > > +extern struct cmd_list_element *add_prefix_cmd_suppress_notification > + (const char *name, enum command_class theclass, > + cmd_const_cfunc_ftype *fun, > + const char *doc, struct cmd_list_element **prefixlist, > + const char *prefixname, int allow_unknown, > + struct cmd_list_element **list, > + int *suppress_notification); > + > extern struct cmd_list_element *add_abbrev_prefix_cmd (const char *, > enum command_class, > cmd_const_cfunc_ftype *fun, > diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo > index 4968b374afe..134dcb22d7c 100644 > --- a/gdb/doc/gdb.texinfo > +++ b/gdb/doc/gdb.texinfo > @@ -7576,21 +7576,56 @@ > @table @code > @kindex frame@r{, selecting} > @kindex f @r{(@code{frame})} > -@item frame @var{n} > -@itemx f @var{n} > -Select frame number @var{n}. Recall that frame zero is the innermost > -(currently executing) frame, frame one is the frame that called the > -innermost one, and so on. The highest-numbered frame is the one for > -@code{main}. > +@item frame @r{[} @var{frame-selection-spec} @r{]} > +@item f @r{[} @var{frame-selection-spec} @r{]} > +The @command{frame} command allows different stack frames to be > +selected. The @var{frame-selection-spec} can be any of the following: > + > +@table @code > +@kindex frame level > +@item @var{num} > +@item level @var{num} > +Select frame number @var{num}. Recall that frame zero is the > +innermost (currently executing) frame, frame one is the frame that > +called the innermost one, and so on. The highest-numbered frame is > +the one for @code{main}. > + > +As this is the most common method of navigating the frame stack then > +the string @command{level} can be dropped, the following two commands > +are equivalent: > + > +@smallexample > +(@value{GDBP}) frame 3 > +(@value{GDBP}) frame level 3 > +@end smallexample > > -@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. > +@kindex frame address > +@item address @var{stack-address} > +Select the frame with stack address @var{stack-address}. > + > +@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} > @@ -7633,11 +7668,13 @@ > > @table @code > @kindex select-frame > -@item select-frame > +@item select-frame @r{[} @var{frame-selection-spec} @r{]} > The @code{select-frame} command is a variant of @code{frame} that does > not display the new frame after selecting it. This command is > intended primarily for use in @value{GDBN} command scripts, where the > -output might be unnecessary and distracting. > +output might be unnecessary and distracting. The > +@var{frame-selection-spec} is as for the @command{frame} command > +described in @ref{Selection, ,Selecting a Frame}. > > @kindex down-silently > @kindex up-silently > @@ -7695,13 +7732,12 @@ > something has gone wrong that has made the stack format fail to fit > the usual conventions. > > -@item info frame @var{addr} > -@itemx info f @var{addr} > -Print a verbose description of the frame at address @var{addr}, without > -selecting that frame. The selected frame remains unchanged by this > -command. This requires the same kind of address (more than one for some > -architectures) that you specify in the @code{frame} command. > -@xref{Selection, ,Selecting a Frame}. > +@item info frame @r{[} @var{frame-selection-spec} @r{]} > +@itemx info f @r{[} @var{frame-selection-spec} @r{]} > +Print a verbose description of the frame selected by > +@var{frame-selection-spec}. The @var{frame-selection-spec} is the > +same as for the @command{frame} command (@pxref{Selection, ,Selecting > +a Frame}). The selected frame remains unchanged by this command. > > @kindex info args > @item info args > @@ -30010,11 +30046,12 @@ > @subsubheading Synopsis > > @smallexample > - -stack-select-frame @var{framenum} > + -stack-select-frame @var{frame-selection-spec} > @end smallexample > > -Change the selected frame. Select a different frame @var{framenum} on > -the stack. > +Change the selected frame, the @var{frame-selection-spec} is as for > +the @command{frame} command described in @ref{Selection, ,Selecting a > +Frame}. > > This command in deprecated in favor of passing the @samp{--frame} > option to every command. > diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c > index 52660bdd498..33ac7663ba8 100644 > --- a/gdb/mi/mi-cmd-stack.c > +++ b/gdb/mi/mi-cmd-stack.c > @@ -681,10 +681,8 @@ list_args_or_locals (enum what_to_list what, enum print_values values, > void > mi_cmd_stack_select_frame (const char *command, char **argv, int argc) > { > - if (argc == 0 || argc > 1) > + if (!select_frame_from_spec (argv, argc, 0)) > error (_("-stack-select-frame: Usage: FRAME_SPEC")); > - > - select_frame_command (argv[0], 1 /* not used */ ); > } > > void > diff --git a/gdb/stack.c b/gdb/stack.c > index 97ebc8bc23f..ab7d7e19f2a 100644 > --- a/gdb/stack.c > +++ b/gdb/stack.c > @@ -102,6 +102,9 @@ static void set_last_displayed_sal (int valid, > struct symtab *symtab, > int line); > > +static struct frame_info *find_frame_for_function (const char *); > +static struct frame_info *find_frame_for_address (CORE_ADDR); > + > /* Zero means do things normally; we are interacting directly with the > user. One means print the full filename and linenumber when a > frame is printed, and do so in a format emacs18/emacs19.22 can > @@ -1255,134 +1258,34 @@ print_frame (struct frame_info *frame, int print_level, > } > > > -/* Read a frame specification in whatever the appropriate format is from > - FRAME_EXP. Call error() if the specification is in any way invalid (so > - this function never returns NULL). When SELECTED_FRAME_P is non-NULL > - set its target to indicate that the default selected frame was used. */ > +/* Completion function for "frame function", "info frame function", and > + "select-frame function" commands. */ > > -static struct frame_info * > -parse_frame_specification (const char *frame_exp, int *selected_frame_p) > +void > +frame_selection_by_function_completer (struct cmd_list_element *ignore, > + completion_tracker &tracker, > + const char *text, const char *word) > { > - int numargs; > - struct value *args[4]; > - CORE_ADDR addrs[ARRAY_SIZE (args)]; > - > - if (frame_exp == NULL) > - numargs = 0; > - else > - { > - numargs = 0; > - while (1) > - { > - const char *p; > - > - /* Skip leading white space, bail of EOL. */ > - frame_exp = skip_spaces (frame_exp); > - if (!*frame_exp) > - break; > - > - /* Parse the argument, extract it, save it. */ > - for (p = frame_exp; > - *p && !ISSPACE (*p); > - p++); > - std::string addr_string (frame_exp, p - frame_exp); > - frame_exp = p; > - > - /* NOTE: Parse and evaluate expression, but do not use > - functions such as parse_and_eval_long or > - parse_and_eval_address to also extract the value. > - Instead value_as_long and value_as_address are used. > - This avoids problems with expressions that contain > - side-effects. */ > - if (numargs >= ARRAY_SIZE (args)) > - error (_("Too many args in frame specification")); > - args[numargs++] = parse_and_eval (addr_string.c_str ()); > - } > - } > - > - /* If no args, default to the selected frame. */ > - if (numargs == 0) > - { > - if (selected_frame_p != NULL) > - (*selected_frame_p) = 1; > - return get_selected_frame (_("No stack.")); > - } > - > - /* None of the remaining use the selected frame. */ > - if (selected_frame_p != NULL) > - (*selected_frame_p) = 0; > - > - /* Assume the single arg[0] is an integer, and try using that to > - select a frame relative to current. */ > - if (numargs == 1) > - { > - struct frame_info *fid; > - int level = value_as_long (args[0]); > - > - fid = find_relative_frame (get_current_frame (), &level); > - if (level == 0) > - /* find_relative_frame was successful. */ > - return fid; > - } > - > - /* Convert each value into a corresponding address. */ > - { > - int i; > - > - for (i = 0; i < numargs; i++) > - addrs[i] = value_as_address (args[i]); > - } > - > - /* Assume that the single arg[0] is an address, use that to identify > - a frame with a matching ID. Should this also accept stack/pc or > - stack/pc/special. */ > - if (numargs == 1) > - { > - struct frame_id id = frame_id_build_wild (addrs[0]); > - struct frame_info *fid; > - > - /* If (s)he specifies the frame with an address, he deserves > - what (s)he gets. Still, give the highest one that matches. > - (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't > - know). */ > - for (fid = get_current_frame (); > - fid != NULL; > - fid = get_prev_frame (fid)) > - { > - if (frame_id_eq (id, get_frame_id (fid))) > - { > - struct frame_info *prev_frame; > - > - while (1) > - { > - prev_frame = get_prev_frame (fid); > - if (!prev_frame > - || !frame_id_eq (id, get_frame_id (prev_frame))) > - break; > - fid = prev_frame; > - } > - return fid; > - } > - } > - } > - > - /* We couldn't identify the frame as an existing frame, but > - perhaps we can create one with a single argument. */ > - if (numargs == 1) > - return create_new_frame (addrs[0], 0); > - else if (numargs == 2) > - return create_new_frame (addrs[0], addrs[1]); > - else > - error (_("Too many args in frame specification")); > + /* This is used to complete function names within a stack. It would be > + nice if instead of offering all available function names, we only > + offered functions that were actually in the stack. However, this > + would probably mean unwinding the stack to completion, which could > + take too long (or on a corrupted stack, possibly not end). For now I > + offer all symbol names as a safer choice. */ > + collect_symbol_completion_matches (tracker, > + complete_symbol_mode::EXPRESSION, > + symbol_name_match_type::EXPRESSION, > + text, word); > } > > -/* Print verbosely the selected frame or the frame at address > - ADDR_EXP. Absolutely all information in the frame is printed. */ > +/* Core of all the "info frame" sub-commands. Print verbosely the frame FI > + if SELECTED_FRAME_P is true then frame FI is the current frame, which > + was selected as a default due to the user not providing any arguments > + to select some other frame. */ > > static void > -info_frame_command (const char *addr_exp, int from_tty) > +info_frame_command_core (struct frame_info *fi, bool selected_frame_p) > { > - struct frame_info *fi; > struct symbol *func; > struct symtab *s; > struct frame_info *calling_frame_info; > @@ -1390,7 +1293,6 @@ info_frame_command (const char *addr_exp, int from_tty) > const char *funname = 0; > enum language funlang = language_unknown; > const char *pc_regname; > - int selected_frame_p; > struct gdbarch *gdbarch; > CORE_ADDR frame_pc; > int frame_pc_p; > @@ -1398,7 +1300,6 @@ info_frame_command (const char *addr_exp, int from_tty) > CORE_ADDR caller_pc = 0; > int caller_pc_p = 0; > > - fi = parse_frame_specification (addr_exp, &selected_frame_p); > gdbarch = get_frame_arch (fi); > > /* Name of the value returned by get_frame_pc(). Per comments, "pc" > @@ -1687,6 +1588,140 @@ info_frame_command (const char *addr_exp, int from_tty) > } > } > > +/* The core of all the "select-frame" sub-commands. Just wraps a call to > + SELECT_FRAME. */ > + > +static void > +select_frame_command_core (struct frame_info *fi, bool ignored) > +{ > + struct frame_info *prev_frame = get_selected_frame_if_set (); > + select_frame (fi); > + if (get_selected_frame_if_set () != prev_frame) > + gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME); > +} > + > +/* The core of all the "frame" sub-commands. Select frame FI, and if this > + means we change frame send out a change notification (otherwise, just > + reprint the current frame summary). */ > + > +static void > +frame_command_core (struct frame_info *fi, bool ignored) > +{ > + struct frame_info *prev_frame = get_selected_frame_if_set (); > + > + select_frame (fi); > + if (get_selected_frame_if_set () != prev_frame) > + gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME); > + else > + print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME); > +} > + > +/* The three commands 'frame', 'select-frame', and 'info frame' all have a > + common set of sub-commands that allow a specific frame to be selected. > + All of the sub-command functions are static methods within this class > + template which is then instantiated below. */ > + > +template <void (*FPTR) (struct frame_info *, bool)> > +class frame_command_helper > +{ > +public: > + > + /* The "frame level" family of commands. The ARG is a number 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 level > + number as historically, this was the supported command syntax that was > + used most often. > + > + If no argument is provided, then the current frame is selected. */ > + > + static void > + base_command (const char *arg, int from_tty) > + { > + if (arg == NULL) > + FPTR (get_selected_frame (_("No stack.")), true); > + else > + 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. */ > > @@ -2193,37 +2228,43 @@ find_relative_frame (struct frame_info *frame, int *level_offset_ptr) > return frame; > } > > -/* The "select_frame" command. With no argument this is a NOP. > - Select the frame at level LEVEL_EXP if it is a valid level. > - Otherwise, treat LEVEL_EXP as an address expression and select it. > +/* Select a frame using ARGV. This is used from the MI -stack-select-frame > + to provide the same frame specification mechanism that the CLI has for > + commands like 'frame'. The return value is true if the contents of > + ARGV looked like a sensible attempt to change the frame (an error might > + still be thrown though), or false if the contents of ARGV are not a > + correct frame specification. */ > > - See parse_frame_specification for more info on proper frame > - expressions. */ > - > -void > -select_frame_command (const char *level_exp, int from_tty) > +bool > +select_frame_from_spec (char **argv, int argc, int from_tty) > { > - struct frame_info *prev_frame = get_selected_frame_if_set (); > - > - 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. */ > + if (argc == 1) > + select_frame_cmd.level (argv[0], from_tty); > + else if (argc == 2 > + && strncasecmp ("level", argv[0], strlen (argv[0])) == 0) > + select_frame_cmd.level (argv[1], from_tty); > + else if (argc == 2 > + && strncasecmp ("address", argv[0], strlen (argv[0])) == 0) > + select_frame_cmd.address (argv[1], from_tty); > + else if (argc == 2 > + && strncasecmp ("function", argv[0], strlen (argv[0])) == 0) > + select_frame_cmd.function (argv[1], from_tty); > + else if ((argc == 2 || argc == 3) > + && strncasecmp ("view", argv[0], strlen (argv[0])) == 0) > + { > + std::string arg; > > -static void > -frame_command (const char *level_exp, int from_tty) > -{ > - struct frame_info *prev_frame = get_selected_frame_if_set (); > + if (argc == 2) > + arg = string_printf ("%s", argv[1]); > + else > + arg = string_printf ("%s %s", argv[1], argv[2]); > > - select_frame (parse_frame_specification (level_exp, NULL)); > - if (get_selected_frame_if_set () != prev_frame) > - gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME); > + select_frame_cmd.view (arg.c_str (), from_tty); > + } > else > - print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME); > + return false; > + > + return true; > } > > /* Select the frame up one or COUNT_EXP stack levels from the > @@ -2494,9 +2535,59 @@ func_command (const char *arg, 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'. */ > +static struct cmd_list_element *frame_cmd_list = NULL; > + > +/* Commands with a prefix of `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) > { > + struct cmd_list_element *cmd; > + > add_com ("return", class_stack, return_command, _("\ > Make selected stack frame return to its caller.\n\ > Control remains in the debugger, but when you continue\n\ > @@ -2519,20 +2610,89 @@ 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_com ("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.")); > - > + 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_com_suppress_notification ("select-frame", class_stack, select_frame_command, _("\ > + 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 NUMBER"), > + &frame_cmd_list); > + > + cmd = add_prefix_cmd_suppress_notification ("select-frame", class_stack, > + &select_frame_cmd.base_command, _("\ > Select a stack frame without printing anything.\n\ > -An argument specifies the frame to select.\n\ > -It can be a stack frame number or the address of the frame."), > +A single numerical argument specifies the frame to select."), > + &select_frame_cmd_list, "select-frame ", 1, &cmdlist, > + &cli_suppress_notification.user_selected_context); > + > + add_cmd_suppress_notification ("address", class_stack, > + &select_frame_cmd.address, _("\ > +Select a stack frame by stack address.\n\ > +\n\ > +Usage: select-frame address STACK-ADDRESS"), > + &select_frame_cmd_list, > + &cli_suppress_notification.user_selected_context); > + > + > + add_cmd_suppress_notification ("view", class_stack, > + &select_frame_cmd.view, _("\ > +Select a stack frame that might be outside the current backtrace.\n\ > +\n\ > +Usage: select-frame view STACK-ADDRESS\n\ > + select-frame view STACK-ADDRESS PC-ADDRESS"), > + &select_frame_cmd_list, > &cli_suppress_notification.user_selected_context); > > + cmd = add_cmd_suppress_notification ("function", class_stack, > + &select_frame_cmd.function, _("\ > +Select a stack frame by function name.\n\ > +\n\ > +Usage: select-frame function NAME"), > + &select_frame_cmd_list, > + &cli_suppress_notification.user_selected_context); > + set_cmd_completer (cmd, frame_selection_by_function_completer); > + > + add_cmd_suppress_notification ("level", class_stack, > + &select_frame_cmd.level, _("\ > +Select a stack frame by level.\n\ > +\n\ > +Usage: select-frame level NUMBER"), > + &select_frame_cmd_list, > + &cli_suppress_notification.user_selected_context); > + > add_com ("backtrace", class_stack, backtrace_command, _("\ > Print backtrace of all stack frames, or innermost COUNT frames.\n\ > Usage: backtrace [QUALIFIERS]... [COUNT]\n\ > @@ -2546,9 +2706,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 NUMBER"), > + &info_frame_cmd_list); > + > add_info ("locals", info_locals_command, > _("Local variables of current stack frame.")); > add_info ("args", info_args_command, > diff --git a/gdb/stack.h b/gdb/stack.h > index ca190efa9c3..29794872d70 100644 > --- a/gdb/stack.h > +++ b/gdb/stack.h > @@ -20,7 +20,7 @@ > #ifndef STACK_H > #define STACK_H > > -void select_frame_command (const char *level_exp, int from_tty); > +bool select_frame_from_spec (char **argv, int argc, int from_tty); > > gdb::unique_xmalloc_ptr<char> find_frame_funname (struct frame_info *frame, > enum language *funlang, > diff --git a/gdb/testsuite/gdb.base/frame-selection.c b/gdb/testsuite/gdb.base/frame-selection.c > new file mode 100644 > index 00000000000..f3d81f223e0 > --- /dev/null > +++ b/gdb/testsuite/gdb.base/frame-selection.c > @@ -0,0 +1,52 @@ > +/* Copyright 2018 Free Software Foundation, Inc. > + > + This file is part of GDB. > + > + This program is free software; you can redistribute it and/or modify > + it under the terms of the GNU General Public License as published by > + the Free Software Foundation; either version 3 of the License, or > + (at your option) any later version. > + > + This program is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + GNU General Public License for more details. > + > + You should have received a copy of the GNU General Public License > + along with this program. If not, see <http://www.gnu.org/licenses/>. */ > + > +int > +frame_2 (void) > +{ > + return 0; > +} > + > +int > +frame_1 (void) > +{ > + return frame_2 (); > +} > + > +int > +recursive (int arg) > +{ > + int v; > + > + if (arg < 2) > + v = recursive (arg + 1); > + else > + v = frame_2 (); > + > + return v; > +} > + > +int > +main (void) > +{ > + int i, j; > + > + i = frame_1 (); > + j = recursive (0); > + > + return i + j; > +} > diff --git a/gdb/testsuite/gdb.base/frame-selection.exp b/gdb/testsuite/gdb.base/frame-selection.exp > new file mode 100644 > index 00000000000..6df85494839 > --- /dev/null > +++ b/gdb/testsuite/gdb.base/frame-selection.exp > @@ -0,0 +1,156 @@ > +# Copyright 2018 Free Software Foundation, Inc. > + > +# This program is free software; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation; either version 3 of the License, or > +# (at your option) any later version. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program. If not, see <http://www.gnu.org/licenses/>. > + > +if { [prepare_for_testing frame-cmds.exp "frame-cmds" {frame-cmds.c} {debug nowarnings}] } { > + return -1 > +} > +set srcfile frame-cmds.c > + > +runto_main > +gdb_breakpoint frame_2 > +gdb_continue_to_breakpoint frame_2 > + > +gdb_test "bt" "#0 frame_2.*#1 $hex in frame_1.*#2 $hex in main.*" "backtrace at breakpoint" > + > +# Perform "info frame" to extract the frames address. > +proc get_frame_address { {testname ""} } { > + global hex gdb_prompt > + > + set testname "get_frame_address: ${testname}" > + set frame_address "unknown" > + > + send_gdb "info frame\n" > + gdb_expect { > + -re ", frame at ($hex):\r\n.*\r\n$gdb_prompt $" { > + set frame_address $expect_out(1,string) > + pass $testname > + } > + -re ".*$gdb_prompt $" { fail $testname } > + timeout { fail "$testname (timeout)" } > + } > + > + return $frame_address > +} > + > +# Passes a list of addresses. Return a new address that is not in the > +# list. > +proc get_new_address { {addresses {}} } { > + return [format "%#x" [expr [lindex $addresses [llength addresses]] + 0x10 ]] > +} > + > +proc check_frame { level address function } { > + global hex gdb_prompt > + > + set testname "check frame level ${level}" > + send_gdb "info frame\n" > + gdb_expect { > + -re "Stack level ${level}, frame at ($address):\r\n rip = $hex in ${function} \(\[^\r\n\]*\); saved rip = $hex\r\n.*\r\n$gdb_prompt $" { > + pass $testname > + } > + -re ".*$gdb_prompt $" { fail $testname } > + timeout { fail "$testname (timeout)" } > + } > +} > + > +# Select frame using level number, but relying on this being the > +# default action, so "frame 0" performs "frame level 0". > +gdb_test "frame 0" "#0 frame_2.*" > +set frame_0_address [ get_frame_address "frame 0" ] > +gdb_test "frame 1" "#1 $hex in frame_1.*" > +set frame_1_address [ get_frame_address "frame 1" ] > +gdb_test "frame 2" "#2 $hex in main.*" > +set frame_2_address [ get_frame_address "frame 2" ] > +gdb_test "frame 3" "No frame at level 3\." > + > +# Find an address that matches no frame > +set no_frame_address [ get_new_address [list $frame_0_address \ > + $frame_1_address \ > + $frame_2_address] ] > + > +# Select frame using 'level' specification. > +gdb_test "frame level 0" "#0 frame_2.*" > +gdb_test "frame level 1" "#1 $hex in frame_1.*" > +gdb_test "frame level 2" "#2 $hex in main.*" > +gdb_test "frame level 3" "No frame at level 3\." > + > +# Select frame by address. > +gdb_test "frame address ${frame_0_address}" "#0 frame_2.*" > +gdb_test "frame address ${frame_1_address}" "#1 $hex in frame_1.*" > +gdb_test "frame address ${frame_2_address}" "#2 $hex in main.*" > +gdb_test "frame address ${no_frame_address}" \ > + "No frame at address ${no_frame_address}\." > + > +# Select frame by function. > +gdb_test "frame function frame_2" "#0 frame_2.*" > +gdb_test "frame function frame_1" "#1 $hex in frame_1.*" > +gdb_test "frame function main" "#2 $hex in main.*" > +# Check for a distinction between a known function not in the stack > +# trace, and an unknown function. > +gdb_test "frame function recursive" "No frame for function \"recursive\"." > +gdb_test "frame function foo" "Function \"foo\" not defined." > + > + > +gdb_test_no_output "select-frame 0" > +check_frame "0" "${frame_0_address}" "frame_2" > +gdb_test_no_output "select-frame 1" > +check_frame "1" "${frame_1_address}" "frame_1" > +gdb_test_no_output "select-frame 2" > +check_frame "2" "${frame_2_address}" "main" > +gdb_test "select-frame 3" "No frame at level 3\." > + > +gdb_test_no_output "select-frame level 0" > +check_frame "0" "${frame_0_address}" "frame_2" > +gdb_test_no_output "select-frame level 1" > +check_frame "1" "${frame_1_address}" "frame_1" > +gdb_test_no_output "select-frame level 2" > +check_frame "2" "${frame_2_address}" "main" > +gdb_test "select-frame level 3" "No frame at level 3\." > + > +gdb_test_no_output "select-frame address ${frame_0_address}" > +check_frame "0" "${frame_0_address}" "frame_2" > +gdb_test_no_output "select-frame address ${frame_1_address}" > +check_frame "1" "${frame_1_address}" "frame_1" > +gdb_test_no_output "select-frame address ${frame_2_address}" > +check_frame "2" "${frame_2_address}" "main" > +gdb_test "select-frame address ${no_frame_address}" \ > + "No frame at address ${no_frame_address}\." > + > +gdb_test_no_output "select-frame function frame_2" > +check_frame "0" "${frame_0_address}" "frame_2" > +gdb_test_no_output "select-frame function frame_1" > +check_frame "1" "${frame_1_address}" "frame_1" > +gdb_test_no_output "select-frame function main" > +check_frame "2" "${frame_2_address}" "main" > +# Check for a distinction between a known function not in the stack > +# trace, and an unknown function. > +gdb_test "select-frame function recursive" \ > + "No frame for function \"recursive\"." > +gdb_test "select-frame function foo" \ > + "Function \"foo\" not defined." > + > +# Now continue until we hit the breakpoint again. > +gdb_continue_to_breakpoint frame_2 > +gdb_test "bt" \ > + "#0 frame_2.*#1 $hex in recursive.*#2 $hex in recursive.*#3 $hex in recursive.*#4 $hex in main.*" \ > + "backtrace at breakpoint with recursive frames" > + > +# Check "frame function" when a function name occurs multiple times in > +# the stack. The inner most (lowest level number) should always be > +# selected. > +gdb_test "frame function frame_2" "#0 frame_2.*" > +gdb_test "frame function recursive" "#1 $hex in recursive.*" > +gdb_test "frame function recursive" "#1 $hex in recursive.*" > +gdb_test "frame function main" "#4 $hex in main.*" > +gdb_test "frame function recursive" "#1 $hex in recursive.*" > diff --git a/gdb/testsuite/gdb.mi/mi-frame-selection.c b/gdb/testsuite/gdb.mi/mi-frame-selection.c > new file mode 100644 > index 00000000000..70b992c9149 > --- /dev/null > +++ b/gdb/testsuite/gdb.mi/mi-frame-selection.c > @@ -0,0 +1,34 @@ > +/* Copyright 2018 Free Software Foundation, Inc. > + > + This file is part of GDB. > + > + This program is free software; you can redistribute it and/or modify > + it under the terms of the GNU General Public License as published by > + the Free Software Foundation; either version 3 of the License, or > + (at your option) any later version. > + > + This program is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + GNU General Public License for more details. > + > + You should have received a copy of the GNU General Public License > + along with this program. If not, see <http://www.gnu.org/licenses/>. */ > + > +int > +frame_2 (void) > +{ > + return 0; > +} > + > +int > +frame_1 (void) > +{ > + return frame_2 (); > +} > + > +int > +main (void) > +{ > + return frame_1 (); > +} > diff --git a/gdb/testsuite/gdb.mi/mi-frame-selection.exp b/gdb/testsuite/gdb.mi/mi-frame-selection.exp > new file mode 100644 > index 00000000000..e24c7179b9d > --- /dev/null > +++ b/gdb/testsuite/gdb.mi/mi-frame-selection.exp > @@ -0,0 +1,89 @@ > +# Copyright 2018 Free Software Foundation, Inc. > +# > +# This program is free software; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation; either version 3 of the License, or > +# (at your option) any later version. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program. If not, see <http://www.gnu.org/licenses/>. > + > +# Test the frame selection syntax with the -stack-select-frame command. > + > +load_lib mi-support.exp > +set MIFLAGS "-i=mi" > + > +gdb_exit > +if [mi_gdb_start] { > + continue > +} > + > +standard_testfile > + > +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { > + untested $testfile > + return -1 > +} > + > +mi_delete_breakpoints > +mi_gdb_reinitialize_dir $srcdir/$subdir > +mi_gdb_load ${binfile} > + > +mi_create_breakpoint "frame_2" \ > + "break-insert into frame_2" \ > + -number 1 -func frame_2 -file ".*${srcfile}" > + > +mi_run_cmd > +mi_expect_stop "breakpoint-hit" "frame_2" "" ".*${srcfile}" \ > + ".*" { "" "disp=\"keep\"" } "run to breakpoint" > + > +mi_gdb_test "-stack-info-depth" \ > + "\\^done,depth=\"3\"" \ > + "stack info-depth" > + > +for {set i 0} {$i < 5} {incr i} { > + > + if { $i < 3 } then { > + mi_gdb_test "-stack-select-frame $i" \ > + {\^done} \ > + "-stack-select-frame $i" > + > + mi_gdb_test "-stack-info-frame" \ > + "\\^done,frame=\\\{level=\"$i\"\[^\}\]+\\\}" \ > + "check level has has changed to $i" > + > + } else { > + mi_gdb_test "-stack-select-frame view $i" \ > + {\^done} \ > + "-stack-select-frame view frame at $i" > + > + mi_gdb_test "-stack-info-frame" \ > + "\\^done,frame=\\\{level=\"0\"\[^\}\]+\\\}" \ > + "check level has has changed to 0, when creating a frame at sp=$i" > + } > +} > + > +mi_gdb_test "-stack-select-frame 5" \ > + {\^error,msg="No frame at level 5."} \ > + "-stack-select-frame 5, with no keyword" > + > +mi_gdb_test "-stack-select-frame level 5" \ > + {\^error,msg="No frame at level 5."} \ > + "-stack-select-frame 5, using 'level' keyword" > + > +mi_gdb_test "-stack-select-frame function main" \ > + {\^done} \ > + "-stack-select-frame select frame for function main" > + > +mi_gdb_test "-stack-info-frame" \ > + "\\^done,frame=\\\{level=\"2\"\[^\}\]+\\\}" \ > + "check level has has changed to 2 for function main" > + > +mi_gdb_test "-stack-select-frame function unknown_function" \ > + {\^error,msg=\"Function \\\"unknown_function\\\" not defined.\"} \ > + "-stack-select-frame check error on undefined function name" > -- > 2.14.3 >
diff --git a/gdb/NEWS b/gdb/NEWS index 8fb6a2ad488..3de6620e4e8 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -21,6 +21,14 @@ * C expressions can now use _Alignof, and C++ expressions can now use alignof. +* Changes to the "frame", "select-frame", and "info frame" CLI + commands, as well as the "-stack-select-frame" MI command. These + commands all now take a frame specification which is either a frame + number, or one of the keywords '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 number is unchanged. + * New commands set debug fbsd-nat diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c index c8dda700ca8..fb44fc1627f 100644 --- a/gdb/cli/cli-decode.c +++ b/gdb/cli/cli-decode.c @@ -251,6 +251,23 @@ add_cmd (const char *name, enum command_class theclass, return result; } +/* Add an element with a suppress notification to the LIST of commands. */ + +struct cmd_list_element * +add_cmd_suppress_notification (const char *name, enum command_class theclass, + cmd_const_cfunc_ftype *fun, const char *doc, + struct cmd_list_element **list, + int *suppress_notification) +{ + struct cmd_list_element *element; + + element = add_cmd (name, theclass, fun, doc, list); + element->suppress_notification = suppress_notification; + + return element; +} + + /* Deprecates a command CMD. REPLACEMENT is the name of the command which should be used in place of this command, or NULL if no such command exists. @@ -362,6 +379,25 @@ add_prefix_cmd (const char *name, enum command_class theclass, return c; } +/* Like ADD_PREFIX_CMD but sets the suppress_notification pointer on the + new command list element. */ + +struct cmd_list_element * +add_prefix_cmd_suppress_notification + (const char *name, enum command_class theclass, + cmd_const_cfunc_ftype *fun, + const char *doc, struct cmd_list_element **prefixlist, + const char *prefixname, int allow_unknown, + struct cmd_list_element **list, + int *suppress_notification) +{ + struct cmd_list_element *element + = add_prefix_cmd (name, theclass, fun, doc, prefixlist, + prefixname, allow_unknown, list); + element->suppress_notification = suppress_notification; + return element; +} + /* Like add_prefix_cmd but sets the abbrev_flag on the new command. */ struct cmd_list_element * @@ -893,12 +929,8 @@ add_com_suppress_notification (const char *name, enum command_class theclass, cmd_const_cfunc_ftype *fun, const char *doc, int *suppress_notification) { - struct cmd_list_element *element; - - element = add_cmd (name, theclass, fun, doc, &cmdlist); - element->suppress_notification = suppress_notification; - - return element; + return add_cmd_suppress_notification (name, theclass, fun, doc, + &cmdlist, suppress_notification); } /* Recursively walk the commandlist structures, and print out the diff --git a/gdb/command.h b/gdb/command.h index 3dde2475cb1..e3d55c2dcba 100644 --- a/gdb/command.h +++ b/gdb/command.h @@ -148,6 +148,12 @@ extern struct cmd_list_element *add_cmd (const char *, enum command_class, const char *, struct cmd_list_element **); +extern struct cmd_list_element *add_cmd_suppress_notification + (const char *name, enum command_class theclass, + cmd_const_cfunc_ftype *fun, const char *doc, + struct cmd_list_element **list, + int *suppress_notification); + extern struct cmd_list_element *add_alias_cmd (const char *, const char *, enum command_class, int, struct cmd_list_element **); @@ -165,6 +171,14 @@ extern struct cmd_list_element *add_prefix_cmd (const char *, enum command_class const char *, int, struct cmd_list_element **); +extern struct cmd_list_element *add_prefix_cmd_suppress_notification + (const char *name, enum command_class theclass, + cmd_const_cfunc_ftype *fun, + const char *doc, struct cmd_list_element **prefixlist, + const char *prefixname, int allow_unknown, + struct cmd_list_element **list, + int *suppress_notification); + extern struct cmd_list_element *add_abbrev_prefix_cmd (const char *, enum command_class, cmd_const_cfunc_ftype *fun, diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 4968b374afe..134dcb22d7c 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -7576,21 +7576,56 @@ @table @code @kindex frame@r{, selecting} @kindex f @r{(@code{frame})} -@item frame @var{n} -@itemx f @var{n} -Select frame number @var{n}. Recall that frame zero is the innermost -(currently executing) frame, frame one is the frame that called the -innermost one, and so on. The highest-numbered frame is the one for -@code{main}. +@item frame @r{[} @var{frame-selection-spec} @r{]} +@item f @r{[} @var{frame-selection-spec} @r{]} +The @command{frame} command allows different stack frames to be +selected. The @var{frame-selection-spec} can be any of the following: + +@table @code +@kindex frame level +@item @var{num} +@item level @var{num} +Select frame number @var{num}. Recall that frame zero is the +innermost (currently executing) frame, frame one is the frame that +called the innermost one, and so on. The highest-numbered frame is +the one for @code{main}. + +As this is the most common method of navigating the frame stack then +the string @command{level} can be dropped, the following two commands +are equivalent: + +@smallexample +(@value{GDBP}) frame 3 +(@value{GDBP}) frame level 3 +@end smallexample -@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. +@kindex frame address +@item address @var{stack-address} +Select the frame with stack address @var{stack-address}. + +@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} @@ -7633,11 +7668,13 @@ @table @code @kindex select-frame -@item select-frame +@item select-frame @r{[} @var{frame-selection-spec} @r{]} The @code{select-frame} command is a variant of @code{frame} that does not display the new frame after selecting it. This command is intended primarily for use in @value{GDBN} command scripts, where the -output might be unnecessary and distracting. +output might be unnecessary and distracting. The +@var{frame-selection-spec} is as for the @command{frame} command +described in @ref{Selection, ,Selecting a Frame}. @kindex down-silently @kindex up-silently @@ -7695,13 +7732,12 @@ something has gone wrong that has made the stack format fail to fit the usual conventions. -@item info frame @var{addr} -@itemx info f @var{addr} -Print a verbose description of the frame at address @var{addr}, without -selecting that frame. The selected frame remains unchanged by this -command. This requires the same kind of address (more than one for some -architectures) that you specify in the @code{frame} command. -@xref{Selection, ,Selecting a Frame}. +@item info frame @r{[} @var{frame-selection-spec} @r{]} +@itemx info f @r{[} @var{frame-selection-spec} @r{]} +Print a verbose description of the frame selected by +@var{frame-selection-spec}. The @var{frame-selection-spec} is the +same as for the @command{frame} command (@pxref{Selection, ,Selecting +a Frame}). The selected frame remains unchanged by this command. @kindex info args @item info args @@ -30010,11 +30046,12 @@ @subsubheading Synopsis @smallexample - -stack-select-frame @var{framenum} + -stack-select-frame @var{frame-selection-spec} @end smallexample -Change the selected frame. Select a different frame @var{framenum} on -the stack. +Change the selected frame, the @var{frame-selection-spec} is as for +the @command{frame} command described in @ref{Selection, ,Selecting a +Frame}. This command in deprecated in favor of passing the @samp{--frame} option to every command. diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c index 52660bdd498..33ac7663ba8 100644 --- a/gdb/mi/mi-cmd-stack.c +++ b/gdb/mi/mi-cmd-stack.c @@ -681,10 +681,8 @@ list_args_or_locals (enum what_to_list what, enum print_values values, void mi_cmd_stack_select_frame (const char *command, char **argv, int argc) { - if (argc == 0 || argc > 1) + if (!select_frame_from_spec (argv, argc, 0)) error (_("-stack-select-frame: Usage: FRAME_SPEC")); - - select_frame_command (argv[0], 1 /* not used */ ); } void diff --git a/gdb/stack.c b/gdb/stack.c index 97ebc8bc23f..ab7d7e19f2a 100644 --- a/gdb/stack.c +++ b/gdb/stack.c @@ -102,6 +102,9 @@ static void set_last_displayed_sal (int valid, struct symtab *symtab, int line); +static struct frame_info *find_frame_for_function (const char *); +static struct frame_info *find_frame_for_address (CORE_ADDR); + /* Zero means do things normally; we are interacting directly with the user. One means print the full filename and linenumber when a frame is printed, and do so in a format emacs18/emacs19.22 can @@ -1255,134 +1258,34 @@ print_frame (struct frame_info *frame, int print_level, } -/* Read a frame specification in whatever the appropriate format is from - FRAME_EXP. Call error() if the specification is in any way invalid (so - this function never returns NULL). When SELECTED_FRAME_P is non-NULL - set its target to indicate that the default selected frame was used. */ +/* Completion function for "frame function", "info frame function", and + "select-frame function" commands. */ -static struct frame_info * -parse_frame_specification (const char *frame_exp, int *selected_frame_p) +void +frame_selection_by_function_completer (struct cmd_list_element *ignore, + completion_tracker &tracker, + const char *text, const char *word) { - int numargs; - struct value *args[4]; - CORE_ADDR addrs[ARRAY_SIZE (args)]; - - if (frame_exp == NULL) - numargs = 0; - else - { - numargs = 0; - while (1) - { - const char *p; - - /* Skip leading white space, bail of EOL. */ - frame_exp = skip_spaces (frame_exp); - if (!*frame_exp) - break; - - /* Parse the argument, extract it, save it. */ - for (p = frame_exp; - *p && !ISSPACE (*p); - p++); - std::string addr_string (frame_exp, p - frame_exp); - frame_exp = p; - - /* NOTE: Parse and evaluate expression, but do not use - functions such as parse_and_eval_long or - parse_and_eval_address to also extract the value. - Instead value_as_long and value_as_address are used. - This avoids problems with expressions that contain - side-effects. */ - if (numargs >= ARRAY_SIZE (args)) - error (_("Too many args in frame specification")); - args[numargs++] = parse_and_eval (addr_string.c_str ()); - } - } - - /* If no args, default to the selected frame. */ - if (numargs == 0) - { - if (selected_frame_p != NULL) - (*selected_frame_p) = 1; - return get_selected_frame (_("No stack.")); - } - - /* None of the remaining use the selected frame. */ - if (selected_frame_p != NULL) - (*selected_frame_p) = 0; - - /* Assume the single arg[0] is an integer, and try using that to - select a frame relative to current. */ - if (numargs == 1) - { - struct frame_info *fid; - int level = value_as_long (args[0]); - - fid = find_relative_frame (get_current_frame (), &level); - if (level == 0) - /* find_relative_frame was successful. */ - return fid; - } - - /* Convert each value into a corresponding address. */ - { - int i; - - for (i = 0; i < numargs; i++) - addrs[i] = value_as_address (args[i]); - } - - /* Assume that the single arg[0] is an address, use that to identify - a frame with a matching ID. Should this also accept stack/pc or - stack/pc/special. */ - if (numargs == 1) - { - struct frame_id id = frame_id_build_wild (addrs[0]); - struct frame_info *fid; - - /* If (s)he specifies the frame with an address, he deserves - what (s)he gets. Still, give the highest one that matches. - (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't - know). */ - for (fid = get_current_frame (); - fid != NULL; - fid = get_prev_frame (fid)) - { - if (frame_id_eq (id, get_frame_id (fid))) - { - struct frame_info *prev_frame; - - while (1) - { - prev_frame = get_prev_frame (fid); - if (!prev_frame - || !frame_id_eq (id, get_frame_id (prev_frame))) - break; - fid = prev_frame; - } - return fid; - } - } - } - - /* We couldn't identify the frame as an existing frame, but - perhaps we can create one with a single argument. */ - if (numargs == 1) - return create_new_frame (addrs[0], 0); - else if (numargs == 2) - return create_new_frame (addrs[0], addrs[1]); - else - error (_("Too many args in frame specification")); + /* This is used to complete function names within a stack. It would be + nice if instead of offering all available function names, we only + offered functions that were actually in the stack. However, this + would probably mean unwinding the stack to completion, which could + take too long (or on a corrupted stack, possibly not end). For now I + offer all symbol names as a safer choice. */ + collect_symbol_completion_matches (tracker, + complete_symbol_mode::EXPRESSION, + symbol_name_match_type::EXPRESSION, + text, word); } -/* Print verbosely the selected frame or the frame at address - ADDR_EXP. Absolutely all information in the frame is printed. */ +/* Core of all the "info frame" sub-commands. Print verbosely the frame FI + if SELECTED_FRAME_P is true then frame FI is the current frame, which + was selected as a default due to the user not providing any arguments + to select some other frame. */ static void -info_frame_command (const char *addr_exp, int from_tty) +info_frame_command_core (struct frame_info *fi, bool selected_frame_p) { - struct frame_info *fi; struct symbol *func; struct symtab *s; struct frame_info *calling_frame_info; @@ -1390,7 +1293,6 @@ info_frame_command (const char *addr_exp, int from_tty) const char *funname = 0; enum language funlang = language_unknown; const char *pc_regname; - int selected_frame_p; struct gdbarch *gdbarch; CORE_ADDR frame_pc; int frame_pc_p; @@ -1398,7 +1300,6 @@ info_frame_command (const char *addr_exp, int from_tty) CORE_ADDR caller_pc = 0; int caller_pc_p = 0; - fi = parse_frame_specification (addr_exp, &selected_frame_p); gdbarch = get_frame_arch (fi); /* Name of the value returned by get_frame_pc(). Per comments, "pc" @@ -1687,6 +1588,140 @@ info_frame_command (const char *addr_exp, int from_tty) } } +/* The core of all the "select-frame" sub-commands. Just wraps a call to + SELECT_FRAME. */ + +static void +select_frame_command_core (struct frame_info *fi, bool ignored) +{ + struct frame_info *prev_frame = get_selected_frame_if_set (); + select_frame (fi); + if (get_selected_frame_if_set () != prev_frame) + gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME); +} + +/* The core of all the "frame" sub-commands. Select frame FI, and if this + means we change frame send out a change notification (otherwise, just + reprint the current frame summary). */ + +static void +frame_command_core (struct frame_info *fi, bool ignored) +{ + struct frame_info *prev_frame = get_selected_frame_if_set (); + + select_frame (fi); + if (get_selected_frame_if_set () != prev_frame) + gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME); + else + print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME); +} + +/* The three commands 'frame', 'select-frame', and 'info frame' all have a + common set of sub-commands that allow a specific frame to be selected. + All of the sub-command functions are static methods within this class + template which is then instantiated below. */ + +template <void (*FPTR) (struct frame_info *, bool)> +class frame_command_helper +{ +public: + + /* The "frame level" family of commands. The ARG is a number 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 level + number as historically, this was the supported command syntax that was + used most often. + + If no argument is provided, then the current frame is selected. */ + + static void + base_command (const char *arg, int from_tty) + { + if (arg == NULL) + FPTR (get_selected_frame (_("No stack.")), true); + else + 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. */ @@ -2193,37 +2228,43 @@ find_relative_frame (struct frame_info *frame, int *level_offset_ptr) return frame; } -/* The "select_frame" command. With no argument this is a NOP. - Select the frame at level LEVEL_EXP if it is a valid level. - Otherwise, treat LEVEL_EXP as an address expression and select it. +/* Select a frame using ARGV. This is used from the MI -stack-select-frame + to provide the same frame specification mechanism that the CLI has for + commands like 'frame'. The return value is true if the contents of + ARGV looked like a sensible attempt to change the frame (an error might + still be thrown though), or false if the contents of ARGV are not a + correct frame specification. */ - See parse_frame_specification for more info on proper frame - expressions. */ - -void -select_frame_command (const char *level_exp, int from_tty) +bool +select_frame_from_spec (char **argv, int argc, int from_tty) { - struct frame_info *prev_frame = get_selected_frame_if_set (); - - 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. */ + if (argc == 1) + select_frame_cmd.level (argv[0], from_tty); + else if (argc == 2 + && strncasecmp ("level", argv[0], strlen (argv[0])) == 0) + select_frame_cmd.level (argv[1], from_tty); + else if (argc == 2 + && strncasecmp ("address", argv[0], strlen (argv[0])) == 0) + select_frame_cmd.address (argv[1], from_tty); + else if (argc == 2 + && strncasecmp ("function", argv[0], strlen (argv[0])) == 0) + select_frame_cmd.function (argv[1], from_tty); + else if ((argc == 2 || argc == 3) + && strncasecmp ("view", argv[0], strlen (argv[0])) == 0) + { + std::string arg; -static void -frame_command (const char *level_exp, int from_tty) -{ - struct frame_info *prev_frame = get_selected_frame_if_set (); + if (argc == 2) + arg = string_printf ("%s", argv[1]); + else + arg = string_printf ("%s %s", argv[1], argv[2]); - select_frame (parse_frame_specification (level_exp, NULL)); - if (get_selected_frame_if_set () != prev_frame) - gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME); + select_frame_cmd.view (arg.c_str (), from_tty); + } else - print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME); + return false; + + return true; } /* Select the frame up one or COUNT_EXP stack levels from the @@ -2494,9 +2535,59 @@ func_command (const char *arg, 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'. */ +static struct cmd_list_element *frame_cmd_list = NULL; + +/* Commands with a prefix of `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) { + struct cmd_list_element *cmd; + add_com ("return", class_stack, return_command, _("\ Make selected stack frame return to its caller.\n\ Control remains in the debugger, but when you continue\n\ @@ -2519,20 +2610,89 @@ 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_com ("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.")); - + 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_com_suppress_notification ("select-frame", class_stack, select_frame_command, _("\ + 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 NUMBER"), + &frame_cmd_list); + + cmd = add_prefix_cmd_suppress_notification ("select-frame", class_stack, + &select_frame_cmd.base_command, _("\ Select a stack frame without printing anything.\n\ -An argument specifies the frame to select.\n\ -It can be a stack frame number or the address of the frame."), +A single numerical argument specifies the frame to select."), + &select_frame_cmd_list, "select-frame ", 1, &cmdlist, + &cli_suppress_notification.user_selected_context); + + add_cmd_suppress_notification ("address", class_stack, + &select_frame_cmd.address, _("\ +Select a stack frame by stack address.\n\ +\n\ +Usage: select-frame address STACK-ADDRESS"), + &select_frame_cmd_list, + &cli_suppress_notification.user_selected_context); + + + add_cmd_suppress_notification ("view", class_stack, + &select_frame_cmd.view, _("\ +Select a stack frame that might be outside the current backtrace.\n\ +\n\ +Usage: select-frame view STACK-ADDRESS\n\ + select-frame view STACK-ADDRESS PC-ADDRESS"), + &select_frame_cmd_list, &cli_suppress_notification.user_selected_context); + cmd = add_cmd_suppress_notification ("function", class_stack, + &select_frame_cmd.function, _("\ +Select a stack frame by function name.\n\ +\n\ +Usage: select-frame function NAME"), + &select_frame_cmd_list, + &cli_suppress_notification.user_selected_context); + set_cmd_completer (cmd, frame_selection_by_function_completer); + + add_cmd_suppress_notification ("level", class_stack, + &select_frame_cmd.level, _("\ +Select a stack frame by level.\n\ +\n\ +Usage: select-frame level NUMBER"), + &select_frame_cmd_list, + &cli_suppress_notification.user_selected_context); + add_com ("backtrace", class_stack, backtrace_command, _("\ Print backtrace of all stack frames, or innermost COUNT frames.\n\ Usage: backtrace [QUALIFIERS]... [COUNT]\n\ @@ -2546,9 +2706,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 NUMBER"), + &info_frame_cmd_list); + add_info ("locals", info_locals_command, _("Local variables of current stack frame.")); add_info ("args", info_args_command, diff --git a/gdb/stack.h b/gdb/stack.h index ca190efa9c3..29794872d70 100644 --- a/gdb/stack.h +++ b/gdb/stack.h @@ -20,7 +20,7 @@ #ifndef STACK_H #define STACK_H -void select_frame_command (const char *level_exp, int from_tty); +bool select_frame_from_spec (char **argv, int argc, int from_tty); gdb::unique_xmalloc_ptr<char> find_frame_funname (struct frame_info *frame, enum language *funlang, diff --git a/gdb/testsuite/gdb.base/frame-selection.c b/gdb/testsuite/gdb.base/frame-selection.c new file mode 100644 index 00000000000..f3d81f223e0 --- /dev/null +++ b/gdb/testsuite/gdb.base/frame-selection.c @@ -0,0 +1,52 @@ +/* Copyright 2018 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +int +frame_2 (void) +{ + return 0; +} + +int +frame_1 (void) +{ + return frame_2 (); +} + +int +recursive (int arg) +{ + int v; + + if (arg < 2) + v = recursive (arg + 1); + else + v = frame_2 (); + + return v; +} + +int +main (void) +{ + int i, j; + + i = frame_1 (); + j = recursive (0); + + return i + j; +} diff --git a/gdb/testsuite/gdb.base/frame-selection.exp b/gdb/testsuite/gdb.base/frame-selection.exp new file mode 100644 index 00000000000..6df85494839 --- /dev/null +++ b/gdb/testsuite/gdb.base/frame-selection.exp @@ -0,0 +1,156 @@ +# Copyright 2018 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +if { [prepare_for_testing frame-cmds.exp "frame-cmds" {frame-cmds.c} {debug nowarnings}] } { + return -1 +} +set srcfile frame-cmds.c + +runto_main +gdb_breakpoint frame_2 +gdb_continue_to_breakpoint frame_2 + +gdb_test "bt" "#0 frame_2.*#1 $hex in frame_1.*#2 $hex in main.*" "backtrace at breakpoint" + +# Perform "info frame" to extract the frames address. +proc get_frame_address { {testname ""} } { + global hex gdb_prompt + + set testname "get_frame_address: ${testname}" + set frame_address "unknown" + + send_gdb "info frame\n" + gdb_expect { + -re ", frame at ($hex):\r\n.*\r\n$gdb_prompt $" { + set frame_address $expect_out(1,string) + pass $testname + } + -re ".*$gdb_prompt $" { fail $testname } + timeout { fail "$testname (timeout)" } + } + + return $frame_address +} + +# Passes a list of addresses. Return a new address that is not in the +# list. +proc get_new_address { {addresses {}} } { + return [format "%#x" [expr [lindex $addresses [llength addresses]] + 0x10 ]] +} + +proc check_frame { level address function } { + global hex gdb_prompt + + set testname "check frame level ${level}" + send_gdb "info frame\n" + gdb_expect { + -re "Stack level ${level}, frame at ($address):\r\n rip = $hex in ${function} \(\[^\r\n\]*\); saved rip = $hex\r\n.*\r\n$gdb_prompt $" { + pass $testname + } + -re ".*$gdb_prompt $" { fail $testname } + timeout { fail "$testname (timeout)" } + } +} + +# Select frame using level number, but relying on this being the +# default action, so "frame 0" performs "frame level 0". +gdb_test "frame 0" "#0 frame_2.*" +set frame_0_address [ get_frame_address "frame 0" ] +gdb_test "frame 1" "#1 $hex in frame_1.*" +set frame_1_address [ get_frame_address "frame 1" ] +gdb_test "frame 2" "#2 $hex in main.*" +set frame_2_address [ get_frame_address "frame 2" ] +gdb_test "frame 3" "No frame at level 3\." + +# Find an address that matches no frame +set no_frame_address [ get_new_address [list $frame_0_address \ + $frame_1_address \ + $frame_2_address] ] + +# Select frame using 'level' specification. +gdb_test "frame level 0" "#0 frame_2.*" +gdb_test "frame level 1" "#1 $hex in frame_1.*" +gdb_test "frame level 2" "#2 $hex in main.*" +gdb_test "frame level 3" "No frame at level 3\." + +# Select frame by address. +gdb_test "frame address ${frame_0_address}" "#0 frame_2.*" +gdb_test "frame address ${frame_1_address}" "#1 $hex in frame_1.*" +gdb_test "frame address ${frame_2_address}" "#2 $hex in main.*" +gdb_test "frame address ${no_frame_address}" \ + "No frame at address ${no_frame_address}\." + +# Select frame by function. +gdb_test "frame function frame_2" "#0 frame_2.*" +gdb_test "frame function frame_1" "#1 $hex in frame_1.*" +gdb_test "frame function main" "#2 $hex in main.*" +# Check for a distinction between a known function not in the stack +# trace, and an unknown function. +gdb_test "frame function recursive" "No frame for function \"recursive\"." +gdb_test "frame function foo" "Function \"foo\" not defined." + + +gdb_test_no_output "select-frame 0" +check_frame "0" "${frame_0_address}" "frame_2" +gdb_test_no_output "select-frame 1" +check_frame "1" "${frame_1_address}" "frame_1" +gdb_test_no_output "select-frame 2" +check_frame "2" "${frame_2_address}" "main" +gdb_test "select-frame 3" "No frame at level 3\." + +gdb_test_no_output "select-frame level 0" +check_frame "0" "${frame_0_address}" "frame_2" +gdb_test_no_output "select-frame level 1" +check_frame "1" "${frame_1_address}" "frame_1" +gdb_test_no_output "select-frame level 2" +check_frame "2" "${frame_2_address}" "main" +gdb_test "select-frame level 3" "No frame at level 3\." + +gdb_test_no_output "select-frame address ${frame_0_address}" +check_frame "0" "${frame_0_address}" "frame_2" +gdb_test_no_output "select-frame address ${frame_1_address}" +check_frame "1" "${frame_1_address}" "frame_1" +gdb_test_no_output "select-frame address ${frame_2_address}" +check_frame "2" "${frame_2_address}" "main" +gdb_test "select-frame address ${no_frame_address}" \ + "No frame at address ${no_frame_address}\." + +gdb_test_no_output "select-frame function frame_2" +check_frame "0" "${frame_0_address}" "frame_2" +gdb_test_no_output "select-frame function frame_1" +check_frame "1" "${frame_1_address}" "frame_1" +gdb_test_no_output "select-frame function main" +check_frame "2" "${frame_2_address}" "main" +# Check for a distinction between a known function not in the stack +# trace, and an unknown function. +gdb_test "select-frame function recursive" \ + "No frame for function \"recursive\"." +gdb_test "select-frame function foo" \ + "Function \"foo\" not defined." + +# Now continue until we hit the breakpoint again. +gdb_continue_to_breakpoint frame_2 +gdb_test "bt" \ + "#0 frame_2.*#1 $hex in recursive.*#2 $hex in recursive.*#3 $hex in recursive.*#4 $hex in main.*" \ + "backtrace at breakpoint with recursive frames" + +# Check "frame function" when a function name occurs multiple times in +# the stack. The inner most (lowest level number) should always be +# selected. +gdb_test "frame function frame_2" "#0 frame_2.*" +gdb_test "frame function recursive" "#1 $hex in recursive.*" +gdb_test "frame function recursive" "#1 $hex in recursive.*" +gdb_test "frame function main" "#4 $hex in main.*" +gdb_test "frame function recursive" "#1 $hex in recursive.*" diff --git a/gdb/testsuite/gdb.mi/mi-frame-selection.c b/gdb/testsuite/gdb.mi/mi-frame-selection.c new file mode 100644 index 00000000000..70b992c9149 --- /dev/null +++ b/gdb/testsuite/gdb.mi/mi-frame-selection.c @@ -0,0 +1,34 @@ +/* Copyright 2018 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +int +frame_2 (void) +{ + return 0; +} + +int +frame_1 (void) +{ + return frame_2 (); +} + +int +main (void) +{ + return frame_1 (); +} diff --git a/gdb/testsuite/gdb.mi/mi-frame-selection.exp b/gdb/testsuite/gdb.mi/mi-frame-selection.exp new file mode 100644 index 00000000000..e24c7179b9d --- /dev/null +++ b/gdb/testsuite/gdb.mi/mi-frame-selection.exp @@ -0,0 +1,89 @@ +# Copyright 2018 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Test the frame selection syntax with the -stack-select-frame command. + +load_lib mi-support.exp +set MIFLAGS "-i=mi" + +gdb_exit +if [mi_gdb_start] { + continue +} + +standard_testfile + +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { + untested $testfile + return -1 +} + +mi_delete_breakpoints +mi_gdb_reinitialize_dir $srcdir/$subdir +mi_gdb_load ${binfile} + +mi_create_breakpoint "frame_2" \ + "break-insert into frame_2" \ + -number 1 -func frame_2 -file ".*${srcfile}" + +mi_run_cmd +mi_expect_stop "breakpoint-hit" "frame_2" "" ".*${srcfile}" \ + ".*" { "" "disp=\"keep\"" } "run to breakpoint" + +mi_gdb_test "-stack-info-depth" \ + "\\^done,depth=\"3\"" \ + "stack info-depth" + +for {set i 0} {$i < 5} {incr i} { + + if { $i < 3 } then { + mi_gdb_test "-stack-select-frame $i" \ + {\^done} \ + "-stack-select-frame $i" + + mi_gdb_test "-stack-info-frame" \ + "\\^done,frame=\\\{level=\"$i\"\[^\}\]+\\\}" \ + "check level has has changed to $i" + + } else { + mi_gdb_test "-stack-select-frame view $i" \ + {\^done} \ + "-stack-select-frame view frame at $i" + + mi_gdb_test "-stack-info-frame" \ + "\\^done,frame=\\\{level=\"0\"\[^\}\]+\\\}" \ + "check level has has changed to 0, when creating a frame at sp=$i" + } +} + +mi_gdb_test "-stack-select-frame 5" \ + {\^error,msg="No frame at level 5."} \ + "-stack-select-frame 5, with no keyword" + +mi_gdb_test "-stack-select-frame level 5" \ + {\^error,msg="No frame at level 5."} \ + "-stack-select-frame 5, using 'level' keyword" + +mi_gdb_test "-stack-select-frame function main" \ + {\^done} \ + "-stack-select-frame select frame for function main" + +mi_gdb_test "-stack-info-frame" \ + "\\^done,frame=\\\{level=\"2\"\[^\}\]+\\\}" \ + "check level has has changed to 2 for function main" + +mi_gdb_test "-stack-select-frame function unknown_function" \ + {\^error,msg=\"Function \\\"unknown_function\\\" not defined.\"} \ + "-stack-select-frame check error on undefined function name"