From patchwork Tue Aug 11 20:45:28 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Keith Seitz X-Patchwork-Id: 8139 Received: (qmail 65015 invoked by alias); 11 Aug 2015 20:45:41 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 65005 invoked by uid 89); 11 Aug 2015 20:45:40 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=0.4 required=5.0 tests=AWL, BAYES_50, KAM_ADVERT2, KAM_LAZY_DOMAIN_SECURITY, RP_MATCHES_RCVD, SPF_HELO_PASS autolearn=no version=3.3.2 X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-GCM-SHA384 encrypted) ESMTPS; Tue, 11 Aug 2015 20:45:29 +0000 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by mx1.redhat.com (Postfix) with ESMTPS id 9BFBC9D789 for ; Tue, 11 Aug 2015 20:45:28 +0000 (UTC) Received: from valrhona.uglyboxes.com (ovpn01.gateway.prod.ext.phx2.redhat.com [10.5.9.1]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id t7BKjS8o014008 for ; Tue, 11 Aug 2015 16:45:28 -0400 From: Keith Seitz To: gdb-patches@sourceware.org Subject: Re: [PATCH v6 7/9] Explicit locations: add UI features for CLI Date: Tue, 11 Aug 2015 13:45:28 -0700 Message-Id: <1439325928-4723-1-git-send-email-keiths@redhat.com> In-Reply-To: References: X-IsSubscribed: yes Doug Evans writes: > Keith Seitz writes: > >> + else >> + { >> + /* Handle numbers first, stopping at the next whitespace or ','. */ >> + while (isdigit (*inp[0])) >> + ++(*inp); >> + if (*inp[0] == '\0' || isspace (*inp[0]) || *inp[0] == ',') >> + return savestring (start, *inp - start); >> + >> + /* Otherwise stop at the next occurrence of "SPACE -", '\0', >> + keyword, or ','. */ > >> This comment needs updating (right?). Yes, indeed! As you've identified, the code now stops at any whitespace instead of "SPACE -". Fixsd. > I have checked in the temp fix for 17960 so the "if {0}" can be removed. Excellent. I have rebased and verified that all tests pass (and they do!). For the record, this is the final version I plan on committing/pushing. gdb/ChangeLog: * completer.c: Include location.h. (enum match_type): New enum. (location_completer): Rename to ... (linespec_completer): ... this. (collect_explicit_location_matches, backup_text_ptr) (explicit_location_completer): New functions. (location_completer): "New" function; handle linespec and explicit location completions. (complete_line_internal): Remove all location completer-specific handling. * linespec.c (linespec_lexer_lex_keyword, is_ada_operator) (find_toplevel_char): Export. (linespec_parse_line_offset): Export. Issue error if STRING is not numerical. (gdb_get_linespec_parser_quote_characters): New function. * linespec.h (linespec_parse_line_offset): Declare. (get_gdb_linespec_parser_quote_characters): Declare. (is_ada_operator): Declare. (find_toplevel_char): Declare. (linespec_lexer_lex_keyword): Declare. * location.c (explicit_to_event_location): New function. (explicit_location_lex_one): New function. (string_to_explicit_location): New function. (string_to_event_location): Handle explicit locations. * location.h (explicit_to_event_location): Declare. (string_to_explicit_location): Declare. gdb/testsuite/ChangeLog: * gdb.linespec/3explicit.c: New file. * gdb.linespec/cpexplicit.cc: New file. * gdb.linespec/cpexplicit.exp: New file. * gdb.linespec/explicit.c: New file. * gdb.linespec/explicit.exp: New file. * gdb.linespec/explicit2.c: New file. * gdb.linespec/ls-errs.exp: Add explicit location tests. * lib/gdb.exp (capture_command_output): Regexp-escape `command' before using in the matching pattern. Clarify that `prefix' is a regular expression. --- gdb/completer.c | 218 ++++++++++++++-- gdb/linespec.c | 25 +- gdb/linespec.h | 20 + gdb/location.c | 208 +++++++++++++++ gdb/location.h | 15 + gdb/testsuite/gdb.linespec/3explicit.c | 28 ++ gdb/testsuite/gdb.linespec/cpexplicit.cc | 63 +++++ gdb/testsuite/gdb.linespec/cpexplicit.exp | 112 ++++++++ gdb/testsuite/gdb.linespec/explicit.c | 56 ++++ gdb/testsuite/gdb.linespec/explicit.exp | 406 +++++++++++++++++++++++++++++ gdb/testsuite/gdb.linespec/explicit2.c | 24 ++ gdb/testsuite/gdb.linespec/ls-errs.exp | 57 ++++ gdb/testsuite/lib/gdb.exp | 6 13 files changed, 1196 insertions(+), 42 deletions(-) create mode 100644 gdb/testsuite/gdb.linespec/3explicit.c create mode 100644 gdb/testsuite/gdb.linespec/cpexplicit.cc create mode 100644 gdb/testsuite/gdb.linespec/cpexplicit.exp create mode 100644 gdb/testsuite/gdb.linespec/explicit.c create mode 100644 gdb/testsuite/gdb.linespec/explicit.exp create mode 100644 gdb/testsuite/gdb.linespec/explicit2.c diff --git a/gdb/completer.c b/gdb/completer.c index d1ebf67..7fc27b1 100644 --- a/gdb/completer.c +++ b/gdb/completer.c @@ -27,6 +27,7 @@ #include "reggroups.h" #include "user-regs.h" #include "arch-utils.h" +#include "location.h" #include "cli/cli-decode.h" @@ -43,6 +44,21 @@ #include "completer.h" +/* An enumeration of the various things a user might + attempt to complete for a location. */ + +enum explicit_location_match_type +{ + /* The filename of a source file. */ + MATCH_SOURCE, + + /* The name of a function or method. */ + MATCH_FUNCTION, + + /* The name of a label. */ + MATCH_LABEL +}; + /* Prototypes for local functions. */ static char *line_completion_function (const char *text, int matches, @@ -175,7 +191,7 @@ filename_completer (struct cmd_list_element *ignore, return return_val; } -/* Complete on locations, which might be of two possible forms: +/* Complete on linespecs, which might be of two possible forms: file:line or @@ -184,9 +200,9 @@ filename_completer (struct cmd_list_element *ignore, This is intended to be used in commands that set breakpoints etc. */ -VEC (char_ptr) * -location_completer (struct cmd_list_element *ignore, - const char *text, const char *word) +static VEC (char_ptr) * +linespec_location_completer (struct cmd_list_element *ignore, + const char *text, const char *word) { int n_syms, n_files, ix; VEC (char_ptr) *fn_list = NULL; @@ -333,6 +349,182 @@ location_completer (struct cmd_list_element *ignore, return list; } +/* A helper function to collect explicit location matches for the given + LOCATION, which is attempting to match on WORD. */ + +static VEC (char_ptr) * +collect_explicit_location_matches (struct event_location *location, + enum explicit_location_match_type what, + const char *word) +{ + VEC (char_ptr) *matches = NULL; + const struct explicit_location *explicit = get_explicit_location (location); + + switch (what) + { + case MATCH_SOURCE: + { + const char *text = (explicit->source_filename == NULL + ? "" : explicit->source_filename); + + matches = make_source_files_completion_list (text, word); + } + break; + + case MATCH_FUNCTION: + { + const char *text = (explicit->function_name == NULL + ? "" : explicit->function_name); + + if (explicit->source_filename != NULL) + { + matches + = make_file_symbol_completion_list (text, word, + explicit->source_filename); + } + else + matches = make_symbol_completion_list (text, word); + } + break; + + case MATCH_LABEL: + /* Not supported. */ + break; + + default: + gdb_assert_not_reached ("unhandled explicit_location_match_type"); + } + + return matches; +} + +/* A convenience macro to (safely) back up P to the previous word. */ + +static const char * +backup_text_ptr (const char *p, const char *text) +{ + while (p > text && isspace (*p)) + --p; + for (; p > text && !isspace (p[-1]); --p) + ; + + return p; +} + +/* A completer function for explicit locations. This function + completes both options ("-source", "-line", etc) and values. */ + +static VEC (char_ptr) * +explicit_location_completer (struct cmd_list_element *ignore, + struct event_location *location, + const char *text, const char *word) +{ + const char *p; + VEC (char_ptr) *matches = NULL; + + /* Find the beginning of the word. This is necessary because + we need to know if we are completing an option name or value. We + don't get the leading '-' from the completer. */ + p = backup_text_ptr (word, text); + + if (*p == '-') + { + /* Completing on option name. */ + static const char *const keywords[] = + { + "source", + "function", + "line", + "label", + NULL + }; + + /* Skip over the '-'. */ + ++p; + + return complete_on_enum (keywords, p, p); + } + else + { + /* Completing on value (or unknown). Get the previous word to see what + the user is completing on. */ + size_t len, offset; + const char *new_word, *end; + enum explicit_location_match_type what; + struct explicit_location *explicit = get_explicit_location (location); + + /* Backup P to the previous word, which should be the option + the user is attempting to complete. */ + offset = word - p; + end = --p; + p = backup_text_ptr (p, text); + len = end - p; + + if (strncmp (p, "-source", len) == 0) + { + what = MATCH_SOURCE; + new_word = explicit->source_filename + offset; + } + else if (strncmp (p, "-function", len) == 0) + { + what = MATCH_FUNCTION; + new_word = explicit->function_name + offset; + } + else if (strncmp (p, "-label", len) == 0) + { + what = MATCH_LABEL; + new_word = explicit->label_name + offset; + } + else + { + /* The user isn't completing on any valid option name, + e.g., "break -source foo.c [tab]". */ + return NULL; + } + + /* If the user hasn't entered a search expression, e.g., + "break -function ", new_word will be NULL, but + search routines require non-NULL search words. */ + if (new_word == NULL) + new_word = ""; + + /* Now gather matches */ + matches = collect_explicit_location_matches (location, what, new_word); + } + + return matches; +} + +/* A completer for locations. */ + +VEC (char_ptr) * +location_completer (struct cmd_list_element *ignore, + const char *text, const char *word) +{ + VEC (char_ptr) *matches = NULL; + const char *copy = text; + struct event_location *location; + + location = string_to_explicit_location (©, current_language, 1); + if (location != NULL) + { + struct cleanup *cleanup; + + cleanup = make_cleanup_delete_event_location (location); + matches = explicit_location_completer (ignore, location, text, word); + do_cleanups (cleanup); + } + else + { + /* This is an address or linespec location. + Right now both of these are handled by the (old) linespec + completer. */ + matches = linespec_location_completer (ignore, text, word); + } + + return matches; +} + /* Helper for expression_completer which recursively adds field and method names from TYPE, a struct or union type, to the array OUTPUT. */ @@ -688,16 +880,6 @@ complete_line_internal (const char *text, rl_completer_word_break_characters = gdb_completer_file_name_break_characters; } - else if (c->completer == location_completer) - { - /* Commands which complete on locations want to - see the entire argument. */ - for (p = word; - p > tmp_command - && p[-1] != ' ' && p[-1] != '\t'; - p--) - ; - } if (reason == handle_brkchars && c->completer_handle_brkchars != NULL) (*c->completer_handle_brkchars) (c, p, word); @@ -766,14 +948,6 @@ complete_line_internal (const char *text, rl_completer_word_break_characters = gdb_completer_file_name_break_characters; } - else if (c->completer == location_completer) - { - for (p = word; - p > tmp_command - && p[-1] != ' ' && p[-1] != '\t'; - p--) - ; - } if (reason == handle_brkchars && c->completer_handle_brkchars != NULL) (*c->completer_handle_brkchars) (c, p, word); diff --git a/gdb/linespec.c b/gdb/linespec.c index 84e518f..ff8ae05 100644 --- a/gdb/linespec.c +++ b/gdb/linespec.c @@ -323,8 +323,6 @@ static int compare_symbols (const void *a, const void *b); static int compare_msymbols (const void *a, const void *b); -static const char *find_toplevel_char (const char *s, char c); - /* Permitted quote characters for the parser. This is different from the completer's quote characters to allow backward compatibility with the previous parser. */ @@ -419,10 +417,9 @@ linespec_lexer_lex_keyword (const char *p) return NULL; } -/* Does STRING represent an Ada operator? If so, return the length - of the decoded operator name. If not, return 0. */ +/* See description in linespec.h. */ -static int +int is_ada_operator (const char *string) { const struct ada_opname_map *mapping; @@ -1140,7 +1137,7 @@ find_methods (struct type *t, const char *name, strings. Also, ignore the char within a template name, like a ',' within foo. */ -static const char * +const char * find_toplevel_char (const char *s, char c) { int quoted = 0; /* zero if we're not in quotes; @@ -1551,11 +1548,12 @@ source_file_not_found_error (const char *name) throw_error (NOT_FOUND_ERROR, _("No source file named %s."), name); } -/* Parse and return a line offset in STRING. */ +/* See description in linespec.h. */ -static struct line_offset +struct line_offset linespec_parse_line_offset (const char *string) { + const char *start = string; struct line_offset line_offset = {0, LINE_OFFSET_NONE}; if (*string == '+') @@ -1569,6 +1567,9 @@ linespec_parse_line_offset (const char *string) ++string; } + if (*string != '\0' && !isdigit (*string)) + error (_("malformed line offset: \"%s\""), start); + /* Right now, we only allow base 10 for offsets. */ line_offset.offset = atoi (string); return line_offset; @@ -3903,3 +3904,11 @@ make_cleanup_destroy_linespec_result (struct linespec_result *ls) { return make_cleanup (cleanup_linespec_result, ls); } + +/* Return the quote characters permitted by the linespec parser. */ + +const char * +get_gdb_linespec_parser_quote_characters (void) +{ + return linespec_quote_characters; +} diff --git a/gdb/linespec.h b/gdb/linespec.h index 391ed26..2a76283 100644 --- a/gdb/linespec.h +++ b/gdb/linespec.h @@ -157,6 +157,26 @@ extern struct symtabs_and_lines decode_line_with_last_displayed (char *, int); extern const char *linespec_lexer_lex_keyword (const char *p); +/* Parse a line offset from STRING. */ + +extern struct line_offset linespec_parse_line_offset (const char *string); + +/* Return the quote characters permitted by the linespec parser. */ + +extern const char *get_gdb_linespec_parser_quote_characters (void); + +/* Does STRING represent an Ada operator? If so, return the length + of the decoded operator name. If not, return 0. */ + +extern int is_ada_operator (const char *string); + +/* Find an instance of the character C in the string S that is outside + of all parenthesis pairs, single-quoted strings, and double-quoted + strings. Also, ignore the char within a template name, like a ',' + within foo. */ + +extern const char *find_toplevel_char (const char *s, char c); + /* Find the end of the (first) linespec pointed to by *STRINGP. STRINGP will be advanced to this point. */ diff --git a/gdb/location.c b/gdb/location.c index 66e5ff9..e0c1e98 100644 --- a/gdb/location.c +++ b/gdb/location.c @@ -430,6 +430,196 @@ event_location_to_string (struct event_location *location) return result; } +/* A lexer for explicit locations. This function will advance INP + past any strings that it lexes. Returns a malloc'd copy of the + lexed string or NULL if no lexing was done. */ + +static char * +explicit_location_lex_one (const char **inp, + const struct language_defn *language) +{ + const char *start = *inp; + + if (*start == '\0') + return NULL; + + /* If quoted, skip to the ending quote. */ + if (strchr (get_gdb_linespec_parser_quote_characters (), *start)) + { + char quote_char = *start; + + /* If the input is not an Ada operator, skip to the matching + closing quote and return the string. */ + if (!(language->la_language == language_ada + && quote_char == '\"' && is_ada_operator (start))) + { + const char *end = find_toplevel_char (start + 1, quote_char); + + if (end == NULL) + error (_("Unmatched quote, %s."), start); + *inp = end + 1; + return savestring (start + 1, *inp - start - 2); + } + } + + /* If the input starts with '-' or '+', the string ends with the next + whitespace or comma. */ + if (*start == '-' || *start == '+') + { + while (*inp[0] != '\0' && *inp[0] != ',' && !isspace (*inp[0])) + ++(*inp); + } + else + { + /* Handle numbers first, stopping at the next whitespace or ','. */ + while (isdigit (*inp[0])) + ++(*inp); + if (*inp[0] == '\0' || isspace (*inp[0]) || *inp[0] == ',') + return savestring (start, *inp - start); + + /* Otherwise stop at the next occurrence of whitespace, '\0', + keyword, or ','. */ + *inp = start; + while ((*inp)[0] + && (*inp)[0] != ',' + && !(isspace ((*inp)[0]) + || linespec_lexer_lex_keyword (&(*inp)[1]))) + { + /* Special case: C++ operator,. */ + if (language->la_language == language_cplus + && strncmp (*inp, "operator", 8) + && (*inp)[9] == ',') + (*inp) += 9; + ++(*inp); + } + } + + if (*inp - start > 0) + return savestring (start, *inp - start); + + return NULL; +} + +/* See description in location.h. */ + +struct event_location * +string_to_explicit_location (const char **argp, + const struct language_defn *language, + int dont_throw) +{ + struct cleanup *cleanup; + struct event_location *location; + + /* It is assumed that input beginning with '-' and a non-digit + character is an explicit location. */ + if (argp == NULL + || *argp == '\0' + || *argp[0] != '-' + || !isalpha ((*argp)[1])) + return NULL; + + location = new_explicit_location (NULL); + cleanup = make_cleanup_delete_event_location (location); + + /* Process option/argument pairs. dprintf_command + requires that processing stop on ','. */ + while ((*argp)[0] != '\0' && (*argp)[0] != ',') + { + int len; + char *opt, *oarg; + const char *start; + struct cleanup *opt_cleanup, *oarg_cleanup; + + /* If *ARGP starts with a keyword, stop processing + options. */ + if (linespec_lexer_lex_keyword (*argp) != NULL) + break; + + /* Mark the start of the string in case we need to rewind. */ + start = *argp; + + /* Get the option string. */ + opt = explicit_location_lex_one (argp, language); + opt_cleanup = make_cleanup (xfree, opt); + + *argp = skip_spaces_const (*argp); + + /* Get the argument string. */ + oarg = explicit_location_lex_one (argp, language); + oarg_cleanup = make_cleanup (xfree, oarg); + *argp = skip_spaces_const (*argp); + + /* Use the length of the option to allow abbreviations. */ + len = strlen (opt); + + /* All options have a required argument. Checking for this required + argument is deferred until later. */ + if (strncmp (opt, "-source", len) == 0) + EL_EXPLICIT (location)->source_filename = oarg; + else if (strncmp (opt, "-function", len) == 0) + EL_EXPLICIT (location)->function_name = oarg; + else if (strncmp (opt, "-line", len) == 0) + { + if (oarg != NULL) + { + EL_EXPLICIT (location)->line_offset + = linespec_parse_line_offset (oarg); + do_cleanups (oarg_cleanup); + do_cleanups (opt_cleanup); + continue; + } + } + else if (strncmp (opt, "-label", len) == 0) + EL_EXPLICIT (location)->label_name = oarg; + /* Only emit an "invalid argument" error for options + that look like option strings. */ + else if (opt[0] == '-' && !isdigit (opt[1])) + { + if (!dont_throw) + error (_("invalid explicit location argument, \"%s\""), opt); + } + else + { + /* End of the explicit location specification. + Stop parsing and return whatever explicit location was + parsed. */ + *argp = start; + discard_cleanups (oarg_cleanup); + do_cleanups (opt_cleanup); + discard_cleanups (cleanup); + return location; + } + + /* It's a little lame to error after the fact, but in this + case, it provides a much better user experience to issue + the "invalid argument" error before any missing + argument error. */ + if (oarg == NULL && !dont_throw) + error (_("missing argument for \"%s\""), opt); + + /* The option/argument pair was successfully processed; + oarg belongs to the explicit location, and opt should + be freed. */ + discard_cleanups (oarg_cleanup); + do_cleanups (opt_cleanup); + } + + /* One special error check: If a source filename was given + without offset, function, or label, issue an error. */ + if (EL_EXPLICIT (location)->source_filename != NULL + && EL_EXPLICIT (location)->function_name == NULL + && EL_EXPLICIT (location)->label_name == NULL + && (EL_EXPLICIT (location)->line_offset.sign == LINE_OFFSET_UNKNOWN) + && !dont_throw) + { + error (_("Source filename requires function, label, or " + "line offset.")); + } + + discard_cleanups (cleanup); + return location; +} + /* See description in location.h. */ struct event_location * @@ -462,8 +652,22 @@ string_to_event_location (char **stringp, } else { - /* Everything else is a linespec. */ - location = new_linespec_location (stringp); + const char *arg, *orig; + + /* Next, try an explicit location. */ + orig = arg = *stringp; + location = string_to_explicit_location (&arg, language, 0); + if (location != NULL) + { + /* It was a valid explicit location. Advance STRINGP to + the end of input. */ + *stringp += arg - orig; + } + else + { + /* Everything else is a linespec. */ + location = new_linespec_location (stringp); + } } } diff --git a/gdb/location.h b/gdb/location.h index 3082d9e..31ae59a 100644 --- a/gdb/location.h +++ b/gdb/location.h @@ -203,6 +203,21 @@ extern struct event_location * string_to_event_location (char **argp, const struct language_defn *langauge); +/* Attempt to convert the input string in *ARGP into an explicit location. + ARGP is advanced past any processed input. Returns an event_location + (malloc'd) if an explicit location was successfully found in *ARGP, + NULL otherwise. + + IF !DONT_THROW, this function may call error() if *ARGP looks like + properly formed input, e.g., if it is called with missing argument + parameters or invalid options. If DONT_THROW is non-zero, this function + will not throw any exceptions. */ + +extern struct event_location * + string_to_explicit_location (const char **argp, + const struct language_defn *langauge, + int dont_throw); + /* A convenience function for testing for unset locations. */ extern int event_location_empty_p (const struct event_location *location); diff --git a/gdb/testsuite/gdb.linespec/3explicit.c b/gdb/testsuite/gdb.linespec/3explicit.c new file mode 100644 index 0000000..12bf277 --- /dev/null +++ b/gdb/testsuite/gdb.linespec/3explicit.c @@ -0,0 +1,28 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2013 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 . */ + +static int +myfunction4 (int arg) +{ + return arg + 2; +} + +int +myfunction3 (int arg) +{ + return myfunction4 (arg); +} diff --git a/gdb/testsuite/gdb.linespec/cpexplicit.cc b/gdb/testsuite/gdb.linespec/cpexplicit.cc new file mode 100644 index 0000000..42d50c7 --- /dev/null +++ b/gdb/testsuite/gdb.linespec/cpexplicit.cc @@ -0,0 +1,63 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2012-2013 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 . */ + +class myclass +{ +public: + static int myfunction (int arg) /* entry location */ + { + int i, j, r; + + j = 0; /* myfunction location */ + r = arg; + + top: + ++j; /* top location */ + + if (j == 10) + goto done; + + for (i = 0; i < 10; ++i) + { + r += i; + if (j % 2) + goto top; + } + + done: + return r; + } + + int operator, (const myclass& c) { return 0; } /* operator location */ +}; + +int +main (void) +{ + int i, j; + + /* Call the test function repeatedly, enough times for all our tests + without running forever if something goes wrong. */ + myclass c, d; + for (i = 0, j = 0; i < 1000; ++i) + { + j += myclass::myfunction (0); + j += (c,d); + } + + return j; +} diff --git a/gdb/testsuite/gdb.linespec/cpexplicit.exp b/gdb/testsuite/gdb.linespec/cpexplicit.exp new file mode 100644 index 0000000..90c8ce8 --- /dev/null +++ b/gdb/testsuite/gdb.linespec/cpexplicit.exp @@ -0,0 +1,112 @@ +# Copyright 2012-2015 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 . + +# Tests for explicit linespecs + +if {[skip_cplus_tests]} { + unsupported "skipping C++ tests" + return +} + +standard_testfile .cc +set exefile $testfile + +if {[prepare_for_testing $testfile $exefile $srcfile \ + {c++ debug nowarnings}]} { + return -1 +} + +# Wrap this whole test in a namespace to avoid contaminating other tests. +namespace eval $testfile { + # Test the given (explicit) LINESPEC which should cause gdb to break + # at LOCATION. + proc test_breakpoint {linespec location} { + + # Delete all breakpoints, set a new breakpoint at LINESPEC, + # and attempt to run to it. + delete_breakpoints + gdb_breakpoint $linespec + gdb_continue_to_breakpoint $linespec $location + } + + # Add the given LINESPEC to the array named in THEARRAY. GDB is expected + # to stop at LOCATION. + proc add {thearray linespec location} { + upvar $thearray ar + + lappend ar(linespecs) $linespec + lappend ar(locations) $location + } + + # Some locations used in this test + variable lineno + variable location + set lineno(normal) [gdb_get_line_number "myfunction location" $srcfile] + set lineno(entry) [gdb_get_line_number "entry location" $srcfile] + set lineno(top) [gdb_get_line_number "top location" $srcfile] + set lineno(operator) [gdb_get_line_number "operator location" $srcfile] + foreach v [array names lineno] { + set location($v) ".*[string_to_regexp "$srcfile:$lineno($v)"].*" + } + + # A list of explicit linespecs and the corresponding location + variable linespecs + set linespecs(linespecs) {} + set linespecs(location) {} + + add linespecs "-source $srcfile -function myclass::myfunction" \ + $location(normal) + add linespecs "-source $srcfile -function myclass::myfunction -label top" \ + $location(top) + + # This isn't implemented yet; -line is silently ignored. + add linespecs \ + "-source $srcfile -function myclass::myfunction -label top -line 3" \ + $location(top) + add linespecs "-source $srcfile -line $lineno(top)" $location(top) + add linespecs "-function myclass::myfunction" $location(normal) + add linespecs "-function myclass::myfunction -label top" $location(top) + + # These are also not yet supported; -line is silently ignored. + add linespecs "-function myclass::myfunction -line 3" $location(normal) + add linespecs "-function myclass::myfunction -label top -line 3" \ + $location(top) + add linespecs "-line 3" $location(normal) + add linespecs "-function myclass::operator," $location(operator) + add linespecs "-function 'myclass::operator,'" $location(operator) + add linespecs "-function \"myclass::operator,\"" $location(operator) + + # Fire up gdb. + if {![runto_main]} { + namespace delete $testfile + return -1 + } + + # Test explicit linespecs, with and without conditions. + foreach linespec $linespecs(linespecs) loc_pattern $linespecs(locations) { + # Test the linespec + test_breakpoint $linespec $loc_pattern + } + + # Special (orphaned) dprintf cases. + gdb_test "dprintf -function myclass::operator,,\"hello\"" \ + "Dprintf .*$srcfile, line $lineno(operator)\\." + gdb_test "dprintf -function 'myclass::operator,',\"hello\"" \ + "Dprintf .*$srcfile, line $lineno(operator)\\." + gdb_test "dprintf -function \"myclass::operator,\",\"hello\"" \ + "Dprintf .*$srcfile, line $lineno(operator)\\." +} + +namespace delete $testfile diff --git a/gdb/testsuite/gdb.linespec/explicit.c b/gdb/testsuite/gdb.linespec/explicit.c new file mode 100644 index 0000000..4e1c635 --- /dev/null +++ b/gdb/testsuite/gdb.linespec/explicit.c @@ -0,0 +1,56 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2012-2013 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 . */ + +extern int myfunction2 (int arg); + +static int +myfunction (int arg) +{ + int i, j, r; + + j = 0; /* myfunction location */ + r = arg; + + top: + ++j; /* top location */ + + if (j == 10) + goto done; + + for (i = 0; i < 10; ++i) + { + r += i; + if (j % 2) + goto top; + } + + done: + return r; +} + +int +main (void) +{ + int i, j; + + /* Call the test function repeatedly, enough times for all our tests + without running forever if something goes wrong. */ + for (i = 0, j = 0; i < 1000; ++i) + j += myfunction (0); + + return myfunction2 (j); +} diff --git a/gdb/testsuite/gdb.linespec/explicit.exp b/gdb/testsuite/gdb.linespec/explicit.exp new file mode 100644 index 0000000..344f1b0 --- /dev/null +++ b/gdb/testsuite/gdb.linespec/explicit.exp @@ -0,0 +1,406 @@ +# Copyright 2012-2015 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 . + +# Tests for explicit locations + +standard_testfile explicit.c explicit2.c 3explicit.c +set exefile $testfile + +if {[prepare_for_testing $testfile $exefile \ + [list $srcfile $srcfile2 $srcfile3] {debug nowarnings}]} { + return -1 +} + +# Wrap the entire test in a namespace to avoid contaminating other tests. +namespace eval $testfile { + + # Test the given (explicit) LINESPEC which should cause gdb to break + # at LOCATION. + proc test_breakpoint {linespec location} { + + set testname "set breakpoint at \"$linespec\"" + # Delete all breakpoints, set a new breakpoint at LINESPEC, + # and attempt to run to it. + delete_breakpoints + if {[gdb_breakpoint $linespec]} { + pass $testname + send_log "\nexpecting locpattern \"$location\"\n" + gdb_continue_to_breakpoint $linespec $location + } else { + fail $testname + } + } + + # Add the given LINESPEC to the array named in THEARRAY. GDB is expected + # to stop at LOCATION. + proc add {thearray linespec location} { + upvar $thearray ar + + lappend ar(linespecs) $linespec + lappend ar(locations) $location + } + + # A list of all explicit linespec arguments. + variable all_arguments + set all_arguments {"source" "function" "label" "line"} + + # Some locations used in this test + variable lineno + variable location + set lineno(normal) [gdb_get_line_number "myfunction location" $srcfile] + set lineno(top) [gdb_get_line_number "top location" $srcfile] + foreach v [array names lineno] { + set location($v) ".*[string_to_regexp "$srcfile:$lineno($v)"].*" + } + + # A list of explicit locations and the corresponding location. + variable linespecs + set linespecs(linespecs) {} + set linespecs(location) {} + + add linespecs "-source $srcfile -function myfunction" $location(normal) + add linespecs "-source $srcfile -function myfunction -label top" \ + $location(top) + + # This isn't implemented yet; -line is silently ignored. + add linespecs "-source $srcfile -function myfunction -label top -line 3" \ + $location(top) + add linespecs "-source $srcfile -line $lineno(top)" $location(top) + add linespecs "-function myfunction" $location(normal) + add linespecs "-function myfunction -label top" $location(top) + + # These are also not yet supported; -line is silently ignored. + add linespecs "-function myfunction -line 3" $location(normal) + add linespecs "-function myfunction -label top -line 3" $location(top) + add linespecs "-line 3" $location(normal) + + # Test that static tracepoints on marker ID are not interpreted + # as an erroneous explicit option. + gdb_test "strace -m gdbfoobarbaz" "You can't do that.*" + + # Fire up gdb. + if {![runto_main]} { + return -1 + } + + # Turn off queries + gdb_test_no_output "set confirm off" + + # Simple error tests (many more are tested in ls-err.exp) + foreach arg $all_arguments { + # Test missing argument + gdb_test "break -$arg" \ + [string_to_regexp "missing argument for \"-$arg\""] + + # Test abbreviations + set short [string range $arg 0 3] + gdb_test "break -$short" \ + [string_to_regexp "missing argument for \"-$short\""] + } + + # Test invalid arguments + foreach arg {"-foo" "-foo bar" "-function myfunction -foo" \ + "-function -myfunction -foo bar"} { + gdb_test "break $arg" \ + [string_to_regexp "invalid explicit location argument, \"-foo\""] + } + + # Test explicit locations, with and without conditions. + # For these tests, it is easiest to turn of pending breakpoint. + gdb_test_no_output "set breakpoint pending off" \ + "turn off pending breakpoints" + + foreach linespec $linespecs(linespecs) loc_pattern $linespecs(locations) { + + # Test the linespec + test_breakpoint $linespec $loc_pattern + + # Test with a valid condition + delete_breakpoints + set tst "set breakpoint at \"$linespec\" with valid condition" + if {[gdb_breakpoint "$linespec if arg == 0"]} { + pass $tst + + gdb_test "info break" ".*stop only if arg == 0.*" \ + "info break of conditional breakpoint at \"$linespec\"" + } else { + fail $tst + } + + # Test with invalid condition + gdb_test "break $linespec if foofoofoo == 1" \ + ".*No symbol \"foofoofoo\" in current context.*" \ + "set breakpoint at \"$linespec\" with invalid condition" + + # Test with thread + delete_breakpoints + gdb_test "break $linespec thread 123" "Unknown thread 123." + } + + # Test the explicit location completer + foreach abbrev {"fun" "so" "lab" "li"} full {"function" "source" "label" "line"} { + set tst "complete 'break -$abbrev'" + send_gdb "break -${abbrev}\t" + gdb_test_multiple "" $tst { + "break -$full " { + send_gdb "\n" + gdb_test_multiple "" $tst { + -re "missing argument for \"-$full\".*$gdb_prompt " { + pass $tst + } + } + } + } + set tst "complete -$full with no value" + send_gdb "break -$full \t" + gdb_test_multiple "" $tst { + -re ".*break -$full " { + send_gdb "\n" + gdb_test_multiple "" $tst { + -re ".*Source filename requires function, label, or line offset\..*$gdb_prompt " { + if {[string equal $full "source"]} { + pass $tst + } else { + faill $tst + } + } + -re "missing argument for \"-$full\".*$gdb_prompt " { + pass $tst + } + } + } + } + } + + set tst "complete unique function name" + send_gdb "break -function mai\t" + gdb_test_multiple "" $tst { + "break -function mai\\\x07n" { + send_gdb "\n" + gdb_test "" ".*Breakpoint \[0-9\]+.*" $tst + gdb_test_no_output "delete \$bpnum" "delete $tst breakpoint" + } + } + + set tst "complete non-unique function name" + send_gdb "break -function myfunc\t" + gdb_test_multiple "" $tst { + "break -function myfunc\\\x07tion" { + send_gdb "\t\t" + gdb_test_multiple "" $tst { + -re "\\\x07\r\nmyfunction\[ \t\]+myfunction2\[ \t\]+myfunction3\[ \t\]+myfunction4\[ \t\]+\r\n$gdb_prompt " { + gdb_test "2" ".*Breakpoint \[0-9\]+.*" $tst + gdb_test_no_output "delete \$bpnum" "delete $tst breakpoint" + } + } + } + } + + set tst "complete non-existant function name" + send_gdb "break -function foo\t" + gdb_test_multiple "" $tst { + "break -function foo\\\x07" { + send_gdb "\t\t" + gdb_test_multiple "" $tst { + -re "\\\x07\\\x07" { + send_gdb "\n" + gdb_test "" {Function "foo" not defined.} $tst + } + } + } + } + + set tst "complete unique file name" + send_gdb "break -source 3ex\t" + gdb_test_multiple "" $tst { + "break -source 3explicit.c " { + send_gdb "\n" + gdb_test "" \ + {Source filename requires function, label, or line offset.} $tst + } + } + + set tst "complete non-unique file name" + send_gdb "break -source exp\t" + gdb_test_multiple "" $tst { + "break -source exp\\\x07licit" { + send_gdb "\t\t" + gdb_test_multiple "" $tst { + -re "\\\x07\r\nexplicit.c\[ \t\]+explicit2.c\[ \t\]+\r\n$gdb_prompt" { + send_gdb "\n" + gdb_test "" \ + {Source filename requires function, label, or line offset.} \ + $tst + } + } + } + + "break -source exp\\\x07l" { + # This pattern may occur when glibc debuginfo is installed. + send_gdb "\t\t" + gdb_test_multiple "" $tst { + -re "\\\x07\r\nexplicit.c\[ \t\]+explicit2.c\[ \t\]+expl.*\r\n$gdb_prompt" { + send_gdb "\n" + gdb_test "" \ + {Source filename requires function, label, or line offset.} \ + $tst + } + } + } + } + + set tst "complete non-existant file name" + send_gdb "break -source foo\t" + gdb_test_multiple "" $tst { + "break -source foo" { + send_gdb "\t\t" + gdb_test_multiple "" $tst { + "\\\x07\\\x07" { + send_gdb "\n" + gdb_test "" \ + {Source filename requires function, label, or line offset.} \ + $tst + } + } + } + } + + set tst "complete filename and unique function name" + send_gdb "break -source explicit.c -function ma\t" + gdb_test_multiple "" $tst { + "break -source explicit.c -function main " { + send_gdb "\n" + gdb_test "" ".*Breakpoint .*" $tst + gdb_test_no_output "delete \$bpnum" "delete $tst breakpoint" + } + } + + set tst "complete filename and non-unique function name" + send_gdb "break -so 3explicit.c -func myfunc\t" + gdb_test_multiple "" $tst { + "break -so 3explicit.c -func myfunc\\\x07tion" { + send_gdb "\t\t" + gdb_test_multiple "" $tst { + -re "\\\x07\r\nmyfunction3\[ \t\]+myfunction4\[ \t\]+\r\n$gdb_prompt " { + gdb_test "3" ".*Breakpoint \[0-9\]+.*" $tst + gdb_test_no_output "delete \$bpnum" "delete $tst breakpoint" + } + } + } + } + + set tst "complete filename and non-existant function name" + send_gdb "break -sou 3explicit.c -fun foo\t" + gdb_test_multiple "" $tst { + "break -sou 3explicit.c -fun foo\\\x07" { + send_gdb "\t\t" + gdb_test_multiple "" $tst { + "\\\x07\\\x07" { + send_gdb "\n" + gdb_test "" \ + {Function "foo" not defined in "3explicit.c".} $tst + } + } + } + } + + set tst "complete filename and function reversed" + send_gdb "break -func myfunction4 -source 3ex\t" + gdb_test_multiple "" $tst { + "break -func myfunction4 -source 3explicit.c " { + send_gdb "\n" + gdb_test "" "Breakpoint \[0-9\]+.*" $tst + gdb_test_no_output "delete \$bpnum" "delete $tst breakpoint" + } + } + + # NOTE: We don't bother testing more elaborate combinations of options, + # such as "-func main -sour 3ex\t" (main is defined in explicit.c). The + # completer cannot handle these yet. + + # Test pending explicit breakpoints + gdb_exit + gdb_start + + set tst "pending invalid conditional explicit breakpoint" + if {![gdb_breakpoint "-func myfunction if foofoofoo == 1" \ + allow-pending]} { + fail "set $tst" + } else { + gdb_test "info break" ".*PENDING.*myfunction if foofoofoo == 1.*" $tst + } + + gdb_exit + gdb_start + + set tst "pending valid conditional explicit breakpoint" + if {![gdb_breakpoint "-func myfunction if arg == 0" \ + allow-pending]} { + fail "set $tst" + } else { + gdb_test "info break" ".*PENDING.*myfunction if arg == 0" $tst + + gdb_load [standard_output_file $exefile] + gdb_test "info break" \ + ".*in myfunction at .*$srcfile:.*stop only if arg == 0.*" \ + "$tst resolved" + } + + # Test interaction of condition command and explicit linespec conditons. + gdb_exit + gdb_start + gdb_load [standard_output_file $exefile] + + set tst "condition_command overrides explicit linespec condition" + if {![runto main]} { + fail $tst + } else { + if {![gdb_breakpoint "-func myfunction if arg == 1"]} { + fail "set breakpoint with condition 'arg == 1'" + } else { + gdb_test_no_output "cond 2 arg == 0" \ + "set new breakpoint condition for explicit linespec" + + gdb_continue_to_breakpoint $tst $location(normal) + } + } + + gdb_test "cond 2" [string_to_regexp "Breakpoint 2 now unconditional."] \ + "clear condition for explicit breakpoint" + set tst "info break of cleared condition of explicit breakpoint" + gdb_test_multiple "info break" $tst { + -re ".*in myfunction at .*$srcfile:.*stop only if arg == 0.*" { + fail $tst + } + -re ".*in myfunction at .*$srcfile:.*$gdb_prompt $" { + pass $tst + } + } + + # Test explicit "ranges." Make sure that using explicit + # locations doesn't alter the expected outcome. + gdb_test "list main" ".*" "list main 1" + set list_result [capture_command_output "list -,+" ""] + gdb_test "list main" ".*" "list main 2" + gdb_test "list -line -,-line +" [string_to_regexp $list_result] + + # Ditto for the reverse (except that no output is expected). + gdb_test "list myfunction" ".*" "list myfunction 1" + gdb_test_no_output "list +,-" + gdb_test "list myfunction" ".*" "list myfunction 2" + gdb_test_no_output "list -line +, -line -" +} + +namespace delete $testfile diff --git a/gdb/testsuite/gdb.linespec/explicit2.c b/gdb/testsuite/gdb.linespec/explicit2.c new file mode 100644 index 0000000..218cccb --- /dev/null +++ b/gdb/testsuite/gdb.linespec/explicit2.c @@ -0,0 +1,24 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2013 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 . */ + +extern int myfunction3 (int arg); + +int +myfunction2 (int arg) +{ + return myfunction3 (arg); +} diff --git a/gdb/testsuite/gdb.linespec/ls-errs.exp b/gdb/testsuite/gdb.linespec/ls-errs.exp index 019312c..9b30720 100644 --- a/gdb/testsuite/gdb.linespec/ls-errs.exp +++ b/gdb/testsuite/gdb.linespec/ls-errs.exp @@ -49,11 +49,16 @@ array set error_messages { invalid_var_or_func_f \ "Undefined convenience variable or function \"%s\" not defined in \"%s\"." invalid_label "No label \"%s\" defined in function \"%s\"." + invalid_parm "invalid linespec argument, \"%s\"" invalid_offset "No line %d in the current file." invalid_offset_f "No line %d in file \"%s\"." + malformed_line_offset "malformed line offset: \"%s\"" + source_incomplete \ + "Source filename requires function, label, or line offset." unexpected "malformed linespec error: unexpected %s" unexpected_opt "malformed linespec error: unexpected %s, \"%s\"" unmatched_quote "unmatched quote" + garbage "Garbage '%s' at end of command" } # Some commonly used whitespace tests around ':'. @@ -80,6 +85,7 @@ foreach x $invalid_offsets { incr offset 16 } test_break $x invalid_offset $offset + test_break "-line $x" invalid_offset $offset } # Test offsets with trailing tokens w/ and w/o spaces. @@ -91,13 +97,17 @@ foreach x $spaces { foreach x {1 +1 +100 -10} { test_break "3 $x" unexpected_opt "number" $x + test_break "-line 3 $x" garbage $x test_break "+10 $x" unexpected_opt "number" $x + test_break "-line +10 $x" garbage $x test_break "-10 $x" unexpected_opt "number" $x + test_break "-line -10 $x" garbage $x } -test_break "3 foo" unexpected_opt "string" "foo" -test_break "+10 foo" unexpected_opt "string" "foo" -test_break "-10 foo" unexpected_opt "string" "foo" +foreach x {3 +10 -10} { + test_break "$x foo" unexpected_opt "string" "foo" + test_break "-line $x foo" garbage "foo" +} # Test invalid linespecs starting with filename. foreach x [list "this_file_doesn't_exist.c" \ @@ -113,6 +123,25 @@ foreach x [list "this_file_doesn't_exist.c" \ # Remove any quoting from FILENAME for the error message. test_break "$x:3" invalid_file [string trim $x \"'] } +foreach x [list "this_file_doesn't_exist.c" \ + "file::colons.c" \ + "'file::colons.c'"] { + test_break "-source $x -line 3" \ + invalid_file [string trim $x \"'] +} + +# Test that option lexing stops at whitespace boundaries +test_break "-source this file has spaces.c -line 3" \ + invalid_file "this" + +test_break "-function function whitespace" \ + invalid_function "function" + +test_break "-source $srcfile -function function whitespace" \ + invalid_function_f "function" $srcfile + +test_break "-function main -label label whitespace" \ + invalid_label "label" "main" # Test unmatched quotes. foreach x {"\"src-file.c'" "'src-file.c"} { @@ -123,7 +152,11 @@ test_break $srcfile invalid_function $srcfile foreach x {"foo" " foo" " foo "} { # Trim any leading/trailing whitespace for error messages. test_break "$srcfile:$x" invalid_function_f [string trim $x] $srcfile + test_break "-source $srcfile -function $x" \ + invalid_function_f [string trim $x] $srcfile test_break "$srcfile:main:$x" invalid_label [string trim $x] "main" + test_break "-source $srcfile -function main -label $x" \ + invalid_label [string trim $x] "main" } foreach x $spaces { @@ -133,20 +166,26 @@ foreach x $spaces { test_break "${srcfile}::" invalid_function "${srcfile}::" test_break "$srcfile:3 1" unexpected_opt "number" "1" +test_break "-source $srcfile -line 3 1" garbage "1" test_break "$srcfile:3 +100" unexpected_opt "number" "+100" +test_break "-source $srcfile -line 3 +100" garbage "+100" test_break "$srcfile:3 -100" unexpected_opt "number" "-100" test_break "$srcfile:3 foo" unexpected_opt "string" "foo" +test_break "-source $srcfile -line 3 foo" garbage "foo" foreach x $invalid_offsets { test_break "$srcfile:$x" invalid_offset_f $x $srcfile test_break "\"$srcfile:$x\"" invalid_offset_f $x $srcfile test_break "'$srcfile:$x'" invalid_offset_f $x $srcfile + test_break "-source $srcfile -line $x" invalid_offset_f $x $srcfile } +test_break "-source $srcfile -line -x" malformed_line_offset "-x" # Test invalid filespecs starting with function. foreach x {"foobar" "foo::bar" "foo.bar" "foo ." "foo bar" "foo 1" \ "foo 0" "foo +10" "foo -10" "foo +100" "foo -100"} { test_break $x invalid_function $x + test_break "-function \"$x\"" invalid_function $x } foreach x $spaces { @@ -155,13 +194,12 @@ foreach x $spaces { test_break "main:here${x}" unexpected "end of input" } -test_break "main 3" invalid_function "main 3" -test_break "main +100" invalid_function "main +100" -test_break "main -100" invalid_function "main -100" -test_break "main foo" invalid_function "main foo" - foreach x {"3" "+100" "-100" "foo"} { + test_break "main 3" invalid_function "main 3" + test_break "-function \"main $x\"" invalid_function "main $x" test_break "main:here $x" invalid_label "here $x" "main" + test_break "-function main -label \"here $x\"" \ + invalid_label "here $x" "main" } foreach x {"if" "task" "thread"} { @@ -178,3 +216,6 @@ test_break "'main.c'+3" unexpected_opt "number" "+3" set x {$zippo} test_break $x invalid_var_or_func $x test_break "$srcfile:$x" invalid_var_or_func_f $x $srcfile + +# Explicit linespec-specific tests +test_break "-source $srcfile" source_incomplete diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp index ad2ad6e..5ecef1a 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -5552,14 +5552,16 @@ proc parse_args { argset } { # number of items expected to be passed into the procedure... } -# Capture the output of COMMAND in a string ignoring PREFIX; return that string. +# Capture the output of COMMAND in a string ignoring PREFIX (a regexp); +# return that string. + proc capture_command_output { command prefix } { global gdb_prompt global expect_out set output_string "" gdb_test_multiple "$command" "capture_command_output for $command" { - -re "${command}\[\r\n\]+${prefix}(.*)\[\r\n\]+$gdb_prompt $" { + -re "[string_to_regexp ${command}]\[\r\n\]+${prefix}(.*)\[\r\n\]+$gdb_prompt $" { set output_string $expect_out(1,string) } }