From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 116225 invoked by alias); 5 Sep 2018 07:46:53 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Received: (qmail 65085 invoked by uid 89); 5 Sep 2018 07:46:09 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-25.4 required=5.0 tests=AWL,BAYES_00,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,KAM_SHORT,RCVD_IN_DNSWL_NONE,SPF_PASS autolearn=ham version=3.3.2 spammy=offered, briefly, observers, viewing X-HELO: mail-wr1-f50.google.com Received: from mail-wr1-f50.google.com (HELO mail-wr1-f50.google.com) (209.85.221.50) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Wed, 05 Sep 2018 07:46:01 +0000 Received: by mail-wr1-f50.google.com with SMTP id z96-v6so6512469wrb.8 for ; Wed, 05 Sep 2018 00:46:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=embecosm.com; s=google; h=date:from:to:subject:message-id:references:mime-version :content-disposition:in-reply-to:user-agent; bh=8YhY6Ut24si89a+XaiSQYVc5ripe79b5bRk/Yf+RTx4=; b=SqBaOBr4fyoqQU/2WEtSmpwGzLohrnN9scPyyGwmJd7pO8Eaj9j4FNAgVKQmBN5CEf dJabc+36h7+I9Jf+HHY6/YM3AfeY7Kbts8lebijCMiKH2KVoD+0/0Xs+VPpGK9muVm3F 5TEiTTzNdF5Hn28shiJcfUSSxrGsOVRkRTOymNda414jmmeOvwZJKOe9R/Fh/HMrcqVy IpGnayNgJvBgBOMQPhhxC0bdGAhsKjPd/2XiZV+ONfpfc6FVZ56HbXi2+Z6OF5QCWAcx LVBmLq8S1PFRS+DFlbNn80Km5HlVW47P1O53MHLug5jWsNober4W5MMiy9kGg+MJaFUf J/iA== Return-Path: Received: from localhost (host86-164-199-11.range86-164.btcentralplus.com. [86.164.199.11]) by smtp.gmail.com with ESMTPSA id z13sm865095wrw.19.2018.09.05.00.45.57 for (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Wed, 05 Sep 2018 00:45:57 -0700 (PDT) Date: Wed, 05 Sep 2018 07:46:00 -0000 From: Andrew Burgess To: gdb-patches@sourceware.org Subject: PING: Re: [PATCHv6] gdb: Change how frames are selected for 'frame' and 'info frame'. Message-ID: <20180905074556.GF22193@embecosm.com> References: <837eka504x.fsf@gnu.org> <20180828180327.30992-1-andrew.burgess@embecosm.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20180828180327.30992-1-andrew.burgess@embecosm.com> X-Fortune: Too much is not enough. X-Editor: GNU Emacs [ http://www.gnu.org/software/emacs ] User-Agent: Mutt/1.9.2 (2017-12-15) X-IsSubscribed: yes X-SW-Source: 2018-09/txt/msg00067.txt.bz2 Ping! Thanks, Andrew * Andrew Burgess [2018-08-28 19:03:27 +0100]: > I believe that this variant addresses all existing review comments. > Philippe provided additional feedback in this message: > > https://sourceware.org/ml/gdb-patches/2018-08/msg00349.html > > but I believe this was more about things we need to do to unify the > docs/code/comments on one of either 'number' or 'level', rather than > things that need to be fixed before this patch could be merged. > > This version of the patch is based off v5(B) and uses the keyword > 'level' instead of 'number'. > > The only changes over the previous version are an additional change in > the docs as previously discussed with Eli, and I've rebased the patch > onto current HEAD (and retested). > > OK to commit? > > Thanks, > Andrew > > --- > > The 'frame' command, and thanks to code reuse the 'info frame' and > 'select-frame' commands, currently have an overloaded mechanism for > selecting a frame. > > These commands take one or two parameters, if it's one parameter then > we first try to use the parameter as an integer to select a frame by > level (or depth in the stack). If that fails then we treat the > parameter as an address and try to select a stack frame by > stack-address. If we still have not selected a stack frame, or we > initially had two parameters, then GDB allows the user to view a stack > frame that is not part of the current backtrace. Internally, a new > frame is created with the given stack and pc addresses, and this is > shown to the user. > > The result of this is that a typo by the user, entering the wrong stack > frame level for example, can result in a brand new frame being viewed > rather than an error. > > The purpose of this commit is to remove this overloading, while still > offering the same functionality through some new sub-commands. By > making the default behaviour of 'frame' (and friends) be to select a > stack frame by level index, it is hoped that enough > backwards-compatibility is maintained that users will not be overly > inconvenienced. > > The 'frame', 'select-frame', and 'info frame' commands now all take a > frame specification string as an argument, this string can be any of the > following: > > (1) An integer. This is treated as a frame level. If a frame for > that level does not exist then the user gets an error. > > (2) A string like 'level ', where is a frame level > as in option (1) above. > > (3) A string like 'address ', where > is a stack-frame address. If there is no frame for this address > then the user gets an error. > > (4) A string like 'function ', where is a function name, > the inner most frame for function is selected. If there is no > frame for function then the user gets an error. > > (5) A string like 'view ', this views a new frame > with stack address . > > (6) A string like 'view ', this views > a new frame with stack address an the pc . > > This change assumes that the most common use of the commands like > 'frame' is to select a frame by frame level, it is for this reason > that this is the behaviour that is kept for backwards compatibility. > Any of the alternative behaviours, which are assumed to be less used, > now require a change in user behaviour. > > The MI command '-stack-select-frame' has also changed in the same way. > The default behaviour is to select a frame by level, but the other > selection methods are also available. > > gdb/ChangeLog: > > (NEWS): Mention changes to frame related commands. > * cli/cli-decode.c (add_cmd_suppress_notification): New function. > (add_prefix_cmd_suppress_notification): New function. > (add_com_suppress_notification): Call > add_cmd_suppress_notification. > * command.h (add_cmd_suppress_notification): Declare. > (add_prefix_cmd_suppress_notification): Declare. > * mi/mi-cmd-stack.c (mi_cmd_stack_select_frame): Use new function > select_frame_from_spec. > * stack.c (find_frame_for_function): Add declaration. > (find_frame_for_address): New function. > (parse_frame_specification): Delete function. > (frame_selection_by_function_completer): New function. > (info_frame_command): Rename to... > (info_frame_command_core): ...this, and update parameter types. > (select_frame_command): Rename to... > (select_frame_command_core): ...this, and update parameter types. > (frame_command): Rename to... > (frame_command_core): ...this, and update parameter types. > (class frame_command_helper): New class to wrap implementations of > frame related sub-commands. > (select_frame_from_spec): New function. > (frame_apply_cmd_list): New static global. > (frame_cmd_list): Make static. > (select_frame_cmd_list): New global for sub-commands. > (info_frame_cmd_list): New global for sub-commands. > (_initialize_stack): Register sub-commands for 'frame', > 'select-frame', and 'info frame'. Update 'frame apply' commands > to use frame_apply_cmd_list. Move function local static > frame_apply_list to file static frame_apply_cmd_list for > consistency. > * stack.h (select_frame_command): Delete declarationn. > (select_frame_from_spec): Declare new function. > > gdb/doc/ChangeLog: > > * gdb.texinfo (Frames): Rewrite the description of 'frame number' > to highlight that the number is also the frame's level. > (Selection): Rewrite documentation for 'frame' and 'select-frame' > commands. > (Frame Info): Rewrite documentation for 'info frame' command. > (GDB/MI Stack Manipulation): Update description of > '-stack-select-frame'. > > gdb/testsuite/ChangeLog: > > * gdb.base/frame-selection.exp: New file. > * gdb.base/frame-selection.c: New file. > * gdb.mi/mi-frame-selection.exp: New file. > * gdb.mi/mi-frame-selection.c: New file. > --- > gdb/ChangeLog | 36 ++ > gdb/NEWS | 8 + > gdb/cli/cli-decode.c | 44 ++- > gdb/command.h | 14 + > gdb/doc/ChangeLog | 10 + > gdb/doc/gdb.texinfo | 118 ++++-- > gdb/mi/mi-cmd-stack.c | 4 +- > gdb/stack.c | 535 +++++++++++++++++++--------- > gdb/stack.h | 2 +- > gdb/testsuite/ChangeLog | 7 + > gdb/testsuite/gdb.base/frame-selection.c | 52 +++ > gdb/testsuite/gdb.base/frame-selection.exp | 156 ++++++++ > gdb/testsuite/gdb.mi/mi-frame-selection.c | 34 ++ > gdb/testsuite/gdb.mi/mi-frame-selection.exp | 89 +++++ > 14 files changed, 903 insertions(+), 206 deletions(-) > create mode 100644 gdb/testsuite/gdb.base/frame-selection.c > create mode 100644 gdb/testsuite/gdb.base/frame-selection.exp > create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.c > create mode 100644 gdb/testsuite/gdb.mi/mi-frame-selection.exp > > diff --git a/gdb/NEWS b/gdb/NEWS > index 45514b10c51..b0b2d93939e 100644 > --- a/gdb/NEWS > +++ b/gdb/NEWS > @@ -10,6 +10,14 @@ > * DWARF index cache: GDB can now automatically save indices of DWARF > symbols on disk to speed up further loading of the same binaries. > > +* Changes to the "frame", "select-frame", and "info frame" CLI > + commands, as well as the "-stack-select-frame" MI command. These > + commands all now take a frame specification which is either a frame > + level, or one of the keywords 'level', 'address', 'function', or > + 'view' followed by a parameter. Selecting a frame by address, or > + viewing a frame outside the current backtrace now requires the use > + of a keyword. Selecting a frame by level is unchanged. > + > * New commands > > frame apply [all | COUNT | -COUNT | level LEVEL...] [FLAG]... COMMAND > diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c > index 83dd67efe3f..5d798e877e8 100644 > --- a/gdb/cli/cli-decode.c > +++ b/gdb/cli/cli-decode.c > @@ -251,6 +251,23 @@ add_cmd (const char *name, enum command_class theclass, > return result; > } > > +/* Add an element with a suppress notification to the LIST of commands. */ > + > +struct cmd_list_element * > +add_cmd_suppress_notification (const char *name, enum command_class theclass, > + cmd_const_cfunc_ftype *fun, const char *doc, > + struct cmd_list_element **list, > + int *suppress_notification) > +{ > + struct cmd_list_element *element; > + > + element = add_cmd (name, theclass, fun, doc, list); > + element->suppress_notification = suppress_notification; > + > + return element; > +} > + > + > /* Deprecates a command CMD. > REPLACEMENT is the name of the command which should be used in > place of this command, or NULL if no such command exists. > @@ -362,6 +379,25 @@ add_prefix_cmd (const char *name, enum command_class theclass, > return c; > } > > +/* Like ADD_PREFIX_CMD but sets the suppress_notification pointer on the > + new command list element. */ > + > +struct cmd_list_element * > +add_prefix_cmd_suppress_notification > + (const char *name, enum command_class theclass, > + cmd_const_cfunc_ftype *fun, > + const char *doc, struct cmd_list_element **prefixlist, > + const char *prefixname, int allow_unknown, > + struct cmd_list_element **list, > + int *suppress_notification) > +{ > + struct cmd_list_element *element > + = add_prefix_cmd (name, theclass, fun, doc, prefixlist, > + prefixname, allow_unknown, list); > + element->suppress_notification = suppress_notification; > + return element; > +} > + > /* Like add_prefix_cmd but sets the abbrev_flag on the new command. */ > > struct cmd_list_element * > @@ -893,12 +929,8 @@ add_com_suppress_notification (const char *name, enum command_class theclass, > cmd_const_cfunc_ftype *fun, const char *doc, > int *suppress_notification) > { > - struct cmd_list_element *element; > - > - element = add_cmd (name, theclass, fun, doc, &cmdlist); > - element->suppress_notification = suppress_notification; > - > - return element; > + return add_cmd_suppress_notification (name, theclass, fun, doc, > + &cmdlist, suppress_notification); > } > > /* Recursively walk the commandlist structures, and print out the > diff --git a/gdb/command.h b/gdb/command.h > index 3dde2475cb1..e3d55c2dcba 100644 > --- a/gdb/command.h > +++ b/gdb/command.h > @@ -148,6 +148,12 @@ extern struct cmd_list_element *add_cmd (const char *, enum command_class, > const char *, > struct cmd_list_element **); > > +extern struct cmd_list_element *add_cmd_suppress_notification > + (const char *name, enum command_class theclass, > + cmd_const_cfunc_ftype *fun, const char *doc, > + struct cmd_list_element **list, > + int *suppress_notification); > + > extern struct cmd_list_element *add_alias_cmd (const char *, const char *, > enum command_class, int, > struct cmd_list_element **); > @@ -165,6 +171,14 @@ extern struct cmd_list_element *add_prefix_cmd (const char *, enum command_class > const char *, int, > struct cmd_list_element **); > > +extern struct cmd_list_element *add_prefix_cmd_suppress_notification > + (const char *name, enum command_class theclass, > + cmd_const_cfunc_ftype *fun, > + const char *doc, struct cmd_list_element **prefixlist, > + const char *prefixname, int allow_unknown, > + struct cmd_list_element **list, > + int *suppress_notification); > + > extern struct cmd_list_element *add_abbrev_prefix_cmd (const char *, > enum command_class, > cmd_const_cfunc_ftype *fun, > diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo > index 5068c0ac81f..1cafea97976 100644 > --- a/gdb/doc/gdb.texinfo > +++ b/gdb/doc/gdb.texinfo > @@ -7393,12 +7393,14 @@ > in a register called the @dfn{frame pointer register} > (@pxref{Registers, $fp}) while execution is going on in that frame. > > +@cindex frame level > @cindex frame number > -@value{GDBN} assigns numbers to all existing stack frames, starting with > -zero for the innermost frame, one for the frame that called it, > -and so on upward. These numbers do not really exist in your program; > -they are assigned by @value{GDBN} to give you a way of designating stack > -frames in @value{GDBN} commands. > +@value{GDBN} labels each existing stack frame with a @dfn{level}, a > +number that is zero for the innermost frame, one for the frame that > +called it, and so on upward. These level numbers give you a way of > +designating stack frames in @value{GDBN} commands. The terms > +@dfn{frame number} and @dfn{frame level} can be used interchangably to > +describe this number. > > @c The -fomit-frame-pointer below perennially causes hbox overflow > @c underflow problems. > @@ -7631,21 +7633,75 @@ > @table @code > @kindex frame@r{, selecting} > @kindex f @r{(@code{frame})} > -@item frame @var{n} > -@itemx f @var{n} > -Select frame number @var{n}. Recall that frame zero is the innermost > +@item frame @r{[} @var{frame-selection-spec} @r{]} > +@item f @r{[} @var{frame-selection-spec} @r{]} > +The @command{frame} command allows different stack frames to be > +selected. The @var{frame-selection-spec} can be any of the following: > + > +@table @code > +@kindex frame level > +@item @var{num} > +@item level @var{num} > +Select frame level @var{num}. Recall that frame zero is the innermost > (currently executing) frame, frame one is the frame that called the > -innermost one, and so on. The highest-numbered frame is the one for > -@code{main}. > +innermost one, and so on. The highest level frame is usually the one > +for @code{main}. > + > +As this is the most common method of navigating the frame stack then > +the string @command{level} can be dropped, the following two commands > +are equivalent: > > -@item frame @var{stack-addr} [ @var{pc-addr} ] > -@itemx f @var{stack-addr} [ @var{pc-addr} ] > -Select the frame at address @var{stack-addr}. This is useful mainly if the > -chaining of stack frames has been damaged by a bug, making it > -impossible for @value{GDBN} to assign numbers properly to all frames. In > -addition, this can be useful when your program has multiple stacks and > -switches between them. The optional @var{pc-addr} can also be given to > -specify the value of PC for the stack frame. > +@smallexample > +(@value{GDBP}) frame 3 > +(@value{GDBP}) frame level 3 > +@end smallexample > + > +@kindex frame address > +@item address @var{stack-address} > +Select the frame with stack address @var{stack-address}. The > +@var{stack-address} for a frame can be seen in the output of > +@command{info frame}, for example: > + > +@smallexample > +(gdb) info frame > +Stack level 1, frame at 0x7fffffffda30: > + rip = 0x40066d in b (amd64-entry-value.cc:59); saved rip 0x4004c5 > + tail call frame, caller of frame at 0x7fffffffda30 > + source language c++. > + Arglist at unknown address. > + Locals at unknown address, Previous frame's sp is 0x7fffffffda30 > +@end smallexample > + > +The @var{stack-address} for this frame is @code{0x7fffffffda30} as > +indicated by the line: > + > +@smallexample > +Stack level 1, frame at 0x7fffffffda30: > +@end smallexample > + > +@kindex frame function > +@item function @var{function-name} > +Select the stack frame for function @var{function-name}. If there are > +multiple stack frames for function @var{function-name} then the inner > +most stack frame is selected. > + > +@kindex frame view > +@item view @var{stack-address} @r{[} @var{pc-addr} @r{]} > +View a frame that is not part of @value{GDBN}'s backtrace. The frame > +viewed has stack address @var{stack-addr}, and optionally, a program > +counter address of @var{pc-addr}. > + > +This is useful mainly if the chaining of stack frames has been > +damaged by a bug, making it impossible for @value{GDBN} to assign > +numbers properly to all frames. In addition, this can be useful > +when your program has multiple stacks and switches between them. > + > +When viewing a frame outside the current backtrace using > +@command{frame view} then you can always return to the original > +stack using one of the previous stack frame selection instructions, > +for example @command{frame level 0}. > + > +@end table > > @kindex up > @item up @var{n} > @@ -7688,11 +7744,13 @@ > > @table @code > @kindex select-frame > -@item select-frame > +@item select-frame @r{[} @var{frame-selection-spec} @r{]} > The @code{select-frame} command is a variant of @code{frame} that does > not display the new frame after selecting it. This command is > intended primarily for use in @value{GDBN} command scripts, where the > -output might be unnecessary and distracting. > +output might be unnecessary and distracting. The > +@var{frame-selection-spec} is as for the @command{frame} command > +described in @ref{Selection, ,Selecting a Frame}. > > @kindex down-silently > @kindex up-silently > @@ -7750,13 +7808,12 @@ > something has gone wrong that has made the stack format fail to fit > the usual conventions. > > -@item info frame @var{addr} > -@itemx info f @var{addr} > -Print a verbose description of the frame at address @var{addr}, without > -selecting that frame. The selected frame remains unchanged by this > -command. This requires the same kind of address (more than one for some > -architectures) that you specify in the @code{frame} command. > -@xref{Selection, ,Selecting a Frame}. > +@item info frame @r{[} @var{frame-selection-spec} @r{]} > +@itemx info f @r{[} @var{frame-selection-spec} @r{]} > +Print a verbose description of the frame selected by > +@var{frame-selection-spec}. The @var{frame-selection-spec} is the > +same as for the @command{frame} command (@pxref{Selection, ,Selecting > +a Frame}). The selected frame remains unchanged by this command. > > @kindex info args > @item info args > @@ -30329,11 +30386,12 @@ > @subsubheading Synopsis > > @smallexample > - -stack-select-frame @var{framenum} > + -stack-select-frame @var{frame-selection-spec} > @end smallexample > > -Change the selected frame. Select a different frame @var{framenum} on > -the stack. > +Change the selected frame, the @var{frame-selection-spec} is as for > +the @command{frame} command described in @ref{Selection, ,Selecting a > +Frame}. > > This command in deprecated in favor of passing the @samp{--frame} > option to every command. > diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c > index 52660bdd498..33ac7663ba8 100644 > --- a/gdb/mi/mi-cmd-stack.c > +++ b/gdb/mi/mi-cmd-stack.c > @@ -681,10 +681,8 @@ list_args_or_locals (enum what_to_list what, enum print_values values, > void > mi_cmd_stack_select_frame (const char *command, char **argv, int argc) > { > - if (argc == 0 || argc > 1) > + if (!select_frame_from_spec (argv, argc, 0)) > error (_("-stack-select-frame: Usage: FRAME_SPEC")); > - > - select_frame_command (argv[0], 1 /* not used */ ); > } > > void > diff --git a/gdb/stack.c b/gdb/stack.c > index 40ff99b8fa6..68029f56412 100644 > --- a/gdb/stack.c > +++ b/gdb/stack.c > @@ -102,6 +102,9 @@ static void set_last_displayed_sal (int valid, > struct symtab *symtab, > int line); > > +static struct frame_info *find_frame_for_function (const char *); > +static struct frame_info *find_frame_for_address (CORE_ADDR); > + > /* Zero means do things normally; we are interacting directly with the > user. One means print the full filename and linenumber when a > frame is printed, and do so in a format emacs18/emacs19.22 can > @@ -1258,134 +1261,34 @@ print_frame (struct frame_info *frame, int print_level, > } > > > -/* Read a frame specification in whatever the appropriate format is from > - FRAME_EXP. Call error() if the specification is in any way invalid (so > - this function never returns NULL). When SELECTED_FRAME_P is non-NULL > - set its target to indicate that the default selected frame was used. */ > +/* Completion function for "frame function", "info frame function", and > + "select-frame function" commands. */ > > -static struct frame_info * > -parse_frame_specification (const char *frame_exp, int *selected_frame_p) > +void > +frame_selection_by_function_completer (struct cmd_list_element *ignore, > + completion_tracker &tracker, > + const char *text, const char *word) > { > - int numargs; > - struct value *args[4]; > - CORE_ADDR addrs[ARRAY_SIZE (args)]; > - > - if (frame_exp == NULL) > - numargs = 0; > - else > - { > - numargs = 0; > - while (1) > - { > - const char *p; > - > - /* Skip leading white space, bail of EOL. */ > - frame_exp = skip_spaces (frame_exp); > - if (!*frame_exp) > - break; > - > - /* Parse the argument, extract it, save it. */ > - for (p = frame_exp; > - *p && !ISSPACE (*p); > - p++); > - std::string addr_string (frame_exp, p - frame_exp); > - frame_exp = p; > - > - /* NOTE: Parse and evaluate expression, but do not use > - functions such as parse_and_eval_long or > - parse_and_eval_address to also extract the value. > - Instead value_as_long and value_as_address are used. > - This avoids problems with expressions that contain > - side-effects. */ > - if (numargs >= ARRAY_SIZE (args)) > - error (_("Too many args in frame specification")); > - args[numargs++] = parse_and_eval (addr_string.c_str ()); > - } > - } > - > - /* If no args, default to the selected frame. */ > - if (numargs == 0) > - { > - if (selected_frame_p != NULL) > - (*selected_frame_p) = 1; > - return get_selected_frame (_("No stack.")); > - } > - > - /* None of the remaining use the selected frame. */ > - if (selected_frame_p != NULL) > - (*selected_frame_p) = 0; > - > - /* Assume the single arg[0] is an integer, and try using that to > - select a frame relative to current. */ > - if (numargs == 1) > - { > - struct frame_info *fid; > - int level = value_as_long (args[0]); > - > - fid = find_relative_frame (get_current_frame (), &level); > - if (level == 0) > - /* find_relative_frame was successful. */ > - return fid; > - } > - > - /* Convert each value into a corresponding address. */ > - { > - int i; > - > - for (i = 0; i < numargs; i++) > - addrs[i] = value_as_address (args[i]); > - } > - > - /* Assume that the single arg[0] is an address, use that to identify > - a frame with a matching ID. Should this also accept stack/pc or > - stack/pc/special. */ > - if (numargs == 1) > - { > - struct frame_id id = frame_id_build_wild (addrs[0]); > - struct frame_info *fid; > - > - /* If (s)he specifies the frame with an address, he deserves > - what (s)he gets. Still, give the highest one that matches. > - (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't > - know). */ > - for (fid = get_current_frame (); > - fid != NULL; > - fid = get_prev_frame (fid)) > - { > - if (frame_id_eq (id, get_frame_id (fid))) > - { > - struct frame_info *prev_frame; > - > - while (1) > - { > - prev_frame = get_prev_frame (fid); > - if (!prev_frame > - || !frame_id_eq (id, get_frame_id (prev_frame))) > - break; > - fid = prev_frame; > - } > - return fid; > - } > - } > - } > - > - /* We couldn't identify the frame as an existing frame, but > - perhaps we can create one with a single argument. */ > - if (numargs == 1) > - return create_new_frame (addrs[0], 0); > - else if (numargs == 2) > - return create_new_frame (addrs[0], addrs[1]); > - else > - error (_("Too many args in frame specification")); > + /* This is used to complete function names within a stack. It would be > + nice if instead of offering all available function names, we only > + offered functions that were actually in the stack. However, this > + would probably mean unwinding the stack to completion, which could > + take too long (or on a corrupted stack, possibly not end). For now I > + offer all symbol names as a safer choice. */ > + collect_symbol_completion_matches (tracker, > + complete_symbol_mode::EXPRESSION, > + symbol_name_match_type::EXPRESSION, > + text, word); > } > > -/* Print verbosely the selected frame or the frame at address > - ADDR_EXP. Absolutely all information in the frame is printed. */ > +/* Core of all the "info frame" sub-commands. Print verbosely the frame FI > + if SELECTED_FRAME_P is true then frame FI is the current frame, which > + was selected as a default due to the user not providing any arguments > + to select some other frame. */ > > static void > -info_frame_command (const char *addr_exp, int from_tty) > +info_frame_command_core (struct frame_info *fi, bool selected_frame_p) > { > - struct frame_info *fi; > struct symbol *func; > struct symtab *s; > struct frame_info *calling_frame_info; > @@ -1393,7 +1296,6 @@ info_frame_command (const char *addr_exp, int from_tty) > const char *funname = 0; > enum language funlang = language_unknown; > const char *pc_regname; > - int selected_frame_p; > struct gdbarch *gdbarch; > CORE_ADDR frame_pc; > int frame_pc_p; > @@ -1401,7 +1303,6 @@ info_frame_command (const char *addr_exp, int from_tty) > CORE_ADDR caller_pc = 0; > int caller_pc_p = 0; > > - fi = parse_frame_specification (addr_exp, &selected_frame_p); > gdbarch = get_frame_arch (fi); > > /* Name of the value returned by get_frame_pc(). Per comments, "pc" > @@ -1742,6 +1643,140 @@ trailing_outermost_frame (int count) > return trailing; > } > > +/* The core of all the "select-frame" sub-commands. Just wraps a call to > + SELECT_FRAME. */ > + > +static void > +select_frame_command_core (struct frame_info *fi, bool ignored) > +{ > + struct frame_info *prev_frame = get_selected_frame_if_set (); > + select_frame (fi); > + if (get_selected_frame_if_set () != prev_frame) > + gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME); > +} > + > +/* The core of all the "frame" sub-commands. Select frame FI, and if this > + means we change frame send out a change notification (otherwise, just > + reprint the current frame summary). */ > + > +static void > +frame_command_core (struct frame_info *fi, bool ignored) > +{ > + struct frame_info *prev_frame = get_selected_frame_if_set (); > + > + select_frame (fi); > + if (get_selected_frame_if_set () != prev_frame) > + gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME); > + else > + print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME); > +} > + > +/* The three commands 'frame', 'select-frame', and 'info frame' all have a > + common set of sub-commands that allow a specific frame to be selected. > + All of the sub-command functions are static methods within this class > + template which is then instantiated below. */ > + > +template > +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_cmd; > +static frame_command_helper frame_cmd; > +static frame_command_helper select_frame_cmd; > + > /* Print briefly all stack frames or just the innermost COUNT_EXP > frames. */ > > @@ -2230,37 +2265,43 @@ find_relative_frame (struct frame_info *frame, int *level_offset_ptr) > return frame; > } > > -/* The "select_frame" command. With no argument this is a NOP. > - Select the frame at level LEVEL_EXP if it is a valid level. > - Otherwise, treat LEVEL_EXP as an address expression and select it. > - > - See parse_frame_specification for more info on proper frame > - expressions. */ > - > -void > -select_frame_command (const char *level_exp, int from_tty) > -{ > - struct frame_info *prev_frame = get_selected_frame_if_set (); > - > - select_frame (parse_frame_specification (level_exp, NULL)); > - if (get_selected_frame_if_set () != prev_frame) > - gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME); > -} > - > -/* The "frame" command. With no argument, print the selected frame > - briefly. With an argument, behave like select_frame and then print > - the selected frame. */ > +/* Select a frame using ARGV. This is used from the MI -stack-select-frame > + to provide the same frame specification mechanism that the CLI has for > + commands like 'frame'. The return value is true if the contents of > + ARGV looked like a sensible attempt to change the frame (an error might > + still be thrown though), or false if the contents of ARGV are not a > + correct frame specification. */ > > -static void > -frame_command (const char *level_exp, int from_tty) > +bool > +select_frame_from_spec (char **argv, int argc, int from_tty) > { > - struct frame_info *prev_frame = get_selected_frame_if_set (); > + if (argc == 1) > + select_frame_cmd.level (argv[0], from_tty); > + else if (argc == 2 > + && strncasecmp ("level", argv[0], strlen (argv[0])) == 0) > + select_frame_cmd.level (argv[1], from_tty); > + else if (argc == 2 > + && strncasecmp ("address", argv[0], strlen (argv[0])) == 0) > + select_frame_cmd.address (argv[1], from_tty); > + else if (argc == 2 > + && strncasecmp ("function", argv[0], strlen (argv[0])) == 0) > + select_frame_cmd.function (argv[1], from_tty); > + else if ((argc == 2 || argc == 3) > + && strncasecmp ("view", argv[0], strlen (argv[0])) == 0) > + { > + std::string arg; > + > + if (argc == 2) > + arg = string_printf ("%s", argv[1]); > + else > + arg = string_printf ("%s %s", argv[1], argv[2]); > > - select_frame (parse_frame_specification (level_exp, NULL)); > - if (get_selected_frame_if_set () != prev_frame) > - gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME); > + select_frame_cmd.view (arg.c_str (), from_tty); > + } > else > - print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME); > + return false; > + > + return true; > } > > /* Select the frame up one or COUNT_EXP stack levels from the > @@ -2716,13 +2757,61 @@ faas_command (const char *cmd, int from_tty) > } > > > +/* Find inner-mode frame with frame address ADDRESS. Return NULL if no > + matching frame can be found. */ > + > +static struct frame_info * > +find_frame_for_address (CORE_ADDR address) > +{ > + struct frame_id id; > + struct frame_info *fid; > + > + id = frame_id_build_wild (address); > + > + /* If (s)he specifies the frame with an address, he deserves > + what (s)he gets. Still, give the highest one that matches. > + (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't > + know). */ > + for (fid = get_current_frame (); > + fid != NULL; > + fid = get_prev_frame (fid)) > + { > + if (frame_id_eq (id, get_frame_id (fid))) > + { > + struct frame_info *prev_frame; > + > + while (1) > + { > + prev_frame = get_prev_frame (fid); > + if (!prev_frame > + || !frame_id_eq (id, get_frame_id (prev_frame))) > + break; > + fid = prev_frame; > + } > + return fid; > + } > + } > + return NULL; > +} > + > + > + > +/* Commands with a prefix of `frame apply'. */ > +static struct cmd_list_element *frame_apply_cmd_list = NULL; > + > /* Commands with a prefix of `frame'. */ > -struct cmd_list_element *frame_cmd_list = NULL; > +static struct cmd_list_element *frame_cmd_list = NULL; > + > +/* Commands with a prefix of `select frame'. */ > +static struct cmd_list_element *select_frame_cmd_list = NULL; > + > +/* Commands with a prefix of `info frame'. */ > +static struct cmd_list_element *info_frame_cmd_list = NULL; > > void > _initialize_stack (void) > { > - static struct cmd_list_element *frame_apply_list = NULL; > + struct cmd_list_element *cmd; > > add_com ("return", class_stack, return_command, _("\ > Make selected stack frame return to its caller.\n\ > @@ -2746,12 +2835,12 @@ An argument says how many frames down to go.")); > Same as the `down' command, but does not print anything.\n\ > This is useful in command scripts.")); > > - add_prefix_cmd ("frame", class_stack, frame_command, _("\ > -Select and print a stack frame.\nWith no argument, \ > -print the selected stack frame. (See also \"info frame\").\n\ > -An argument specifies the frame to select.\n\ > -It can be a stack frame number or the address of the frame."), > - &frame_cmd_list, "frame ", 1, &cmdlist); > + add_prefix_cmd ("frame", class_stack, > + &frame_cmd.base_command, _("\ > +Select and print a stack frame.\n\ > +With no argument, print the selected stack frame. (See also \"info frame\").\n\ > +A single numerical argument specifies the frame to select."), > + &frame_cmd_list, "frame ", 1, &cmdlist); > > add_com_alias ("f", "frame", class_stack, 1); > > @@ -2769,7 +2858,7 @@ or produces no output." > Usage: frame apply COUNT [FLAG]... COMMAND\n\ > With a negative COUNT argument, applies the command on outermost -COUNT frames.\n" > FRAME_APPLY_FLAGS_HELP), > - &frame_apply_list, "frame apply ", 1, &frame_cmd_list); > + &frame_apply_cmd_list, "frame apply ", 1, &frame_cmd_list); > > add_cmd ("all", class_stack, frame_apply_all_command, > _("\ > @@ -2777,7 +2866,7 @@ Apply a command to all frames.\n\ > \n\ > Usage: frame apply all [FLAG]... COMMAND\n" > FRAME_APPLY_FLAGS_HELP), > - &frame_apply_list); > + &frame_apply_cmd_list); > > add_cmd ("level", class_stack, frame_apply_level_command, > _("\ > @@ -2786,19 +2875,97 @@ Apply a command to a list of frames.\n\ > Usage: frame apply level LEVEL... [FLAG]... COMMAND\n\ > ID is a space-separated list of LEVELs of frames to apply COMMAND on.\n" > FRAME_APPLY_FLAGS_HELP), > - &frame_apply_list); > + &frame_apply_cmd_list); > > add_com ("faas", class_stack, faas_command, _("\ > Apply a command to all frames (ignoring errors and empty output).\n\ > Usage: faas COMMAND\n\ > shortcut for 'frame apply all -s COMMAND'")); > > - add_com_suppress_notification ("select-frame", class_stack, select_frame_command, _("\ > + > + add_prefix_cmd ("frame", class_stack, > + &frame_cmd.base_command, _("\ > +Select and print a stack frame.\n\ > +With no argument, print the selected stack frame. (See also \"info frame\").\n\ > +A single numerical argument specifies the frame to select."), > + &frame_cmd_list, "frame ", 1, &cmdlist); > + add_com_alias ("f", "frame", class_stack, 1); > + > + add_cmd ("address", class_stack, &frame_cmd.address, > + _("\ > +Select and print a stack frame by stack address\n\ > +\n\ > +Usage: frame address STACK-ADDRESS"), > + &frame_cmd_list); > + > + add_cmd ("view", class_stack, &frame_cmd.view, > + _("\ > +View a stack frame that might be outside the current backtrace.\n\ > +\n\ > +Usage: frame view STACK-ADDRESS\n\ > + frame view STACK-ADDRESS PC-ADDRESS"), > + &frame_cmd_list); > + > + cmd = add_cmd ("function", class_stack, &frame_cmd.function, > + _("\ > +Select and print a stack frame by function name.\n\ > +\n\ > +Usage: frame function NAME\n\ > +\n\ > +The innermost frame that visited function NAME is selected."), > + &frame_cmd_list); > + set_cmd_completer (cmd, frame_selection_by_function_completer); > + > + > + add_cmd ("level", class_stack, &frame_cmd.level, > + _("\ > +Select and print a stack frame by level.\n\ > +\n\ > +Usage: frame level LEVEL"), > + &frame_cmd_list); > + > + cmd = add_prefix_cmd_suppress_notification ("select-frame", class_stack, > + &select_frame_cmd.base_command, _("\ > Select a stack frame without printing anything.\n\ > -An argument specifies the frame to select.\n\ > -It can be a stack frame number or the address of the frame."), > +A single numerical argument specifies the frame to select."), > + &select_frame_cmd_list, "select-frame ", 1, &cmdlist, > + &cli_suppress_notification.user_selected_context); > + > + add_cmd_suppress_notification ("address", class_stack, > + &select_frame_cmd.address, _("\ > +Select a stack frame by stack address.\n\ > +\n\ > +Usage: select-frame address STACK-ADDRESS"), > + &select_frame_cmd_list, > + &cli_suppress_notification.user_selected_context); > + > + > + add_cmd_suppress_notification ("view", class_stack, > + &select_frame_cmd.view, _("\ > +Select a stack frame that might be outside the current backtrace.\n\ > +\n\ > +Usage: select-frame view STACK-ADDRESS\n\ > + select-frame view STACK-ADDRESS PC-ADDRESS"), > + &select_frame_cmd_list, > &cli_suppress_notification.user_selected_context); > > + cmd = add_cmd_suppress_notification ("function", class_stack, > + &select_frame_cmd.function, _("\ > +Select a stack frame by function name.\n\ > +\n\ > +Usage: select-frame function NAME"), > + &select_frame_cmd_list, > + &cli_suppress_notification.user_selected_context); > + set_cmd_completer (cmd, frame_selection_by_function_completer); > + > + add_cmd_suppress_notification ("level", class_stack, > + &select_frame_cmd.level, _("\ > +Select a stack frame by level.\n\ > +\n\ > +Usage: select-frame level LEVEL"), > + &select_frame_cmd_list, > + &cli_suppress_notification.user_selected_context); > + > add_com ("backtrace", class_stack, backtrace_command, _("\ > Print backtrace of all stack frames, or innermost COUNT frames.\n\ > Usage: backtrace [QUALIFIERS]... [COUNT]\n\ > @@ -2812,9 +2979,45 @@ on this backtrace.")); > add_info ("stack", backtrace_command, > _("Backtrace of the stack, or innermost COUNT frames.")); > add_info_alias ("s", "stack", 1); > - add_info ("frame", info_frame_command, > - _("All about selected stack frame, or frame at ADDR.")); > + > + add_prefix_cmd ("frame", class_info, &info_frame_cmd.base_command, > + _("All about the selected stack frame.\n\ > +With no arguments, displays information about the currently selected stack\n\ > +frame. Alternatively a frame specification may be provided (See \"frame\")\n\ > +the information is then printed about the specified frame."), > + &info_frame_cmd_list, "info frame ", 1, &infolist); > add_info_alias ("f", "frame", 1); > + > + add_cmd ("address", class_stack, &info_frame_cmd.address, > + _("\ > +Print information about a stack frame selected by stack address.\n\ > +\n\ > +Usage: info frame address STACK-ADDRESS"), > + &info_frame_cmd_list); > + > + add_cmd ("view", class_stack, &info_frame_cmd.view, > + _("\ > +Print information about a stack frame outside the current backtrace.\n\ > +\n\ > +Usage: info frame view STACK-ADDRESS\n\ > + info frame view STACK-ADDRESS PC-ADDRESS"), > + &info_frame_cmd_list); > + > + cmd = add_cmd ("function", class_stack, &info_frame_cmd.function, > + _("\ > +Print information about a stack frame selected by function name.\n\ > +\n\ > +Usage: info frame function NAME"), > + &info_frame_cmd_list); > + set_cmd_completer (cmd, frame_selection_by_function_completer); > + > + add_cmd ("level", class_stack, &info_frame_cmd.level, > + _("\ > +Print information about a stack frame selected by level.\n\ > +\n\ > +Usage: info frame level LEVEL"), > + &info_frame_cmd_list); > + > add_info ("locals", info_locals_command, > _("Local variables of current stack frame.")); > add_info ("args", info_args_command, > diff --git a/gdb/stack.h b/gdb/stack.h > index ca190efa9c3..29794872d70 100644 > --- a/gdb/stack.h > +++ b/gdb/stack.h > @@ -20,7 +20,7 @@ > #ifndef STACK_H > #define STACK_H > > -void select_frame_command (const char *level_exp, int from_tty); > +bool select_frame_from_spec (char **argv, int argc, int from_tty); > > gdb::unique_xmalloc_ptr 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 . */ > + > +int > +frame_2 (void) > +{ > + return 0; > +} > + > +int > +frame_1 (void) > +{ > + return frame_2 (); > +} > + > +int > +recursive (int arg) > +{ > + int v; > + > + if (arg < 2) > + v = recursive (arg + 1); > + else > + v = frame_2 (); > + > + return v; > +} > + > +int > +main (void) > +{ > + int i, j; > + > + i = frame_1 (); > + j = recursive (0); > + > + return i + j; > +} > diff --git a/gdb/testsuite/gdb.base/frame-selection.exp b/gdb/testsuite/gdb.base/frame-selection.exp > new file mode 100644 > index 00000000000..1b3649b2700 > --- /dev/null > +++ b/gdb/testsuite/gdb.base/frame-selection.exp > @@ -0,0 +1,156 @@ > +# Copyright 2018 Free Software Foundation, Inc. > + > +# This program is free software; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation; either version 3 of the License, or > +# (at your option) any later version. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program. If not, see . > + > +standard_testfile > + > +if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug}]} { > + return -1 > +} > + > +runto_main > +gdb_breakpoint frame_2 > +gdb_continue_to_breakpoint frame_2 > + > +gdb_test "bt" "#0 frame_2.*#1 $hex in frame_1.*#2 $hex in main.*" "backtrace at breakpoint" > + > +# Perform "info frame" to extract the frames address. > +proc get_frame_address { {testname ""} } { > + global hex gdb_prompt > + > + set testname "get_frame_address: ${testname}" > + set frame_address "unknown" > + > + send_gdb "info frame\n" > + gdb_expect { > + -re ", frame at ($hex):\r\n.*\r\n$gdb_prompt $" { > + set frame_address $expect_out(1,string) > + pass $testname > + } > + -re ".*$gdb_prompt $" { fail $testname } > + timeout { fail "$testname (timeout)" } > + } > + > + return $frame_address > +} > + > +# Passes a list of addresses. Return a new address that is not in the > +# list. > +proc get_new_address { {addresses {}} } { > + return [format "%#x" [expr [lindex $addresses [llength addresses]] + 0x10 ]] > +} > + > +proc check_frame { level address function } { > + global hex gdb_prompt > + > + set testname "check frame level ${level}" > + send_gdb "info frame\n" > + gdb_expect { > + -re "Stack level ${level}, frame at ($address):\r\n rip = $hex in ${function} \(\[^\r\n\]*\); saved rip = $hex\r\n.*\r\n$gdb_prompt $" { > + pass $testname > + } > + -re ".*$gdb_prompt $" { fail $testname } > + timeout { fail "$testname (timeout)" } > + } > +} > + > +# Select frame using level, but relying on this being the default > +# action, so "frame 0" performs "frame level 0". > +gdb_test "frame 0" "#0 frame_2.*" > +set frame_0_address [ get_frame_address "frame 0" ] > +gdb_test "frame 1" "#1 $hex in frame_1.*" > +set frame_1_address [ get_frame_address "frame 1" ] > +gdb_test "frame 2" "#2 $hex in main.*" > +set frame_2_address [ get_frame_address "frame 2" ] > +gdb_test "frame 3" "No frame at level 3\." > + > +# Find an address that matches no frame > +set no_frame_address [ get_new_address [list $frame_0_address \ > + $frame_1_address \ > + $frame_2_address] ] > + > +# Select frame using 'level' specification. > +gdb_test "frame level 0" "#0 frame_2.*" > +gdb_test "frame level 1" "#1 $hex in frame_1.*" > +gdb_test "frame level 2" "#2 $hex in main.*" > +gdb_test "frame level 3" "No frame at level 3\." > + > +# Select frame by address. > +gdb_test "frame address ${frame_0_address}" "#0 frame_2.*" > +gdb_test "frame address ${frame_1_address}" "#1 $hex in frame_1.*" > +gdb_test "frame address ${frame_2_address}" "#2 $hex in main.*" > +gdb_test "frame address ${no_frame_address}" \ > + "No frame at address ${no_frame_address}\." > + > +# Select frame by function. > +gdb_test "frame function frame_2" "#0 frame_2.*" > +gdb_test "frame function frame_1" "#1 $hex in frame_1.*" > +gdb_test "frame function main" "#2 $hex in main.*" > +# Check for a distinction between a known function not in the stack > +# trace, and an unknown function. > +gdb_test "frame function recursive" "No frame for function \"recursive\"." > +gdb_test "frame function foo" "Function \"foo\" not defined." > + > + > +gdb_test_no_output "select-frame 0" > +check_frame "0" "${frame_0_address}" "frame_2" > +gdb_test_no_output "select-frame 1" > +check_frame "1" "${frame_1_address}" "frame_1" > +gdb_test_no_output "select-frame 2" > +check_frame "2" "${frame_2_address}" "main" > +gdb_test "select-frame 3" "No frame at level 3\." > + > +gdb_test_no_output "select-frame level 0" > +check_frame "0" "${frame_0_address}" "frame_2" > +gdb_test_no_output "select-frame level 1" > +check_frame "1" "${frame_1_address}" "frame_1" > +gdb_test_no_output "select-frame level 2" > +check_frame "2" "${frame_2_address}" "main" > +gdb_test "select-frame level 3" "No frame at level 3\." > + > +gdb_test_no_output "select-frame address ${frame_0_address}" > +check_frame "0" "${frame_0_address}" "frame_2" > +gdb_test_no_output "select-frame address ${frame_1_address}" > +check_frame "1" "${frame_1_address}" "frame_1" > +gdb_test_no_output "select-frame address ${frame_2_address}" > +check_frame "2" "${frame_2_address}" "main" > +gdb_test "select-frame address ${no_frame_address}" \ > + "No frame at address ${no_frame_address}\." > + > +gdb_test_no_output "select-frame function frame_2" > +check_frame "0" "${frame_0_address}" "frame_2" > +gdb_test_no_output "select-frame function frame_1" > +check_frame "1" "${frame_1_address}" "frame_1" > +gdb_test_no_output "select-frame function main" > +check_frame "2" "${frame_2_address}" "main" > +# Check for a distinction between a known function not in the stack > +# trace, and an unknown function. > +gdb_test "select-frame function recursive" \ > + "No frame for function \"recursive\"." > +gdb_test "select-frame function foo" \ > + "Function \"foo\" not defined." > + > +# Now continue until we hit the breakpoint again. > +gdb_continue_to_breakpoint frame_2 > +gdb_test "bt" \ > + "#0 frame_2.*#1 $hex in recursive.*#2 $hex in recursive.*#3 $hex in recursive.*#4 $hex in main.*" \ > + "backtrace at breakpoint with recursive frames" > + > +# Check "frame function" when a function name occurs multiple times in > +# the stack. The inner most (lowest level) should always be selected. > +gdb_test "frame function frame_2" "#0 frame_2.*" > +gdb_test "frame function recursive" "#1 $hex in recursive.*" > +gdb_test "frame function recursive" "#1 $hex in recursive.*" > +gdb_test "frame function main" "#4 $hex in main.*" > +gdb_test "frame function recursive" "#1 $hex in recursive.*" > diff --git a/gdb/testsuite/gdb.mi/mi-frame-selection.c b/gdb/testsuite/gdb.mi/mi-frame-selection.c > new file mode 100644 > index 00000000000..70b992c9149 > --- /dev/null > +++ b/gdb/testsuite/gdb.mi/mi-frame-selection.c > @@ -0,0 +1,34 @@ > +/* Copyright 2018 Free Software Foundation, Inc. > + > + This file is part of GDB. > + > + This program is free software; you can redistribute it and/or modify > + it under the terms of the GNU General Public License as published by > + the Free Software Foundation; either version 3 of the License, or > + (at your option) any later version. > + > + This program is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + GNU General Public License for more details. > + > + You should have received a copy of the GNU General Public License > + along with this program. If not, see . */ > + > +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 . > + > +# Test the frame selection syntax with the -stack-select-frame command. > + > +load_lib mi-support.exp > +set MIFLAGS "-i=mi" > + > +gdb_exit > +if [mi_gdb_start] { > + continue > +} > + > +standard_testfile > + > +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { > + untested $testfile > + return -1 > +} > + > +mi_delete_breakpoints > +mi_gdb_reinitialize_dir $srcdir/$subdir > +mi_gdb_load ${binfile} > + > +mi_create_breakpoint "frame_2" \ > + "break-insert into frame_2" \ > + -number 1 -func frame_2 -file ".*${srcfile}" > + > +mi_run_cmd > +mi_expect_stop "breakpoint-hit" "frame_2" "" ".*${srcfile}" \ > + ".*" { "" "disp=\"keep\"" } "run to breakpoint" > + > +mi_gdb_test "-stack-info-depth" \ > + "\\^done,depth=\"3\"" \ > + "stack info-depth" > + > +for {set i 0} {$i < 5} {incr i} { > + > + if { $i < 3 } then { > + mi_gdb_test "-stack-select-frame $i" \ > + {\^done} \ > + "-stack-select-frame $i" > + > + mi_gdb_test "-stack-info-frame" \ > + "\\^done,frame=\\\{level=\"$i\"\[^\}\]+\\\}" \ > + "check level has has changed to $i" > + > + } else { > + mi_gdb_test "-stack-select-frame view $i" \ > + {\^done} \ > + "-stack-select-frame view frame at $i" > + > + mi_gdb_test "-stack-info-frame" \ > + "\\^done,frame=\\\{level=\"0\"\[^\}\]+\\\}" \ > + "check level has has changed to 0, when creating a frame at sp=$i" > + } > +} > + > +mi_gdb_test "-stack-select-frame 5" \ > + {\^error,msg="No frame at level 5."} \ > + "-stack-select-frame 5, with no keyword" > + > +mi_gdb_test "-stack-select-frame level 5" \ > + {\^error,msg="No frame at level 5."} \ > + "-stack-select-frame 5, using 'level' keyword" > + > +mi_gdb_test "-stack-select-frame function main" \ > + {\^done} \ > + "-stack-select-frame select frame for function main" > + > +mi_gdb_test "-stack-info-frame" \ > + "\\^done,frame=\\\{level=\"2\"\[^\}\]+\\\}" \ > + "check level has has changed to 2 for function main" > + > +mi_gdb_test "-stack-select-frame function unknown_function" \ > + {\^error,msg=\"Function \\\"unknown_function\\\" not defined.\"} \ > + "-stack-select-frame check error on undefined function name" > -- > 2.14.4 >