From cce0e92333b872cfe036aae611b6b5d61cf58186 Mon Sep 17 00:00:00 2001 From: Doug Evans Date: Tue, 23 Feb 2016 13:25:18 -0800 Subject: [PATCH] Extend "skip" command to support -file, -gfile, -function, -rfunction. gdb/ChangeLog: Extend "skip" command to support -file, -gfile, -function, -rfunction. * NEWS: Document new features. * skip.c: #include "fnmatch.h", "gdb_regex.h". (skiplist_entry) : Renamed from filename. : Renamed from function_name. : New members. : New members. (make_skip_entry): New function. (free_skiplist_entry, free_skiplist_entry_cleanup): New functions. (make_free_skiplist_entry_cleanup): New function. (skip_file_command): Update. (skip_function, skip_function_command): Update. (compile_skip_regexp): New functions. (skip_command): Add support for new options. (skip_info): Update. (skip_file_p, skip_gfile_p): New functions. (skip_function_p, skip_rfunction_p): New functions. (function_name_is_marked_for_skip): Update and simplify. (_initialize_step_skip): Update. * symtab.c: #include "fnmatch.h". (compare_glob_filenames_for_search): New function. * symtab.h (compare_glob_filenames_for_search): Declare. * utils.c (count_path_elements): New function. (strip_leading_path_elements): New function. * utils.h (count_path_elements): Declare. (strip_leading_path_elements): Declare. gdb/doc/ChangeLog: * gdb.texinfo (Skipping Over Functions and Files): Document new options to "skip" command. Update docs of output of "info skip". gdb/testsuite/ChangeLog: * gdb.base/skip.c (test_skip): New function. (end_test_skip_file_and_function): New function. (test_skip_file_and_function): New function. * gdb.base/skip1.c (test_skip): New function. (skip1_test_skip_file_and_function): New function. * gdb.base/skip.exp: Add tests for new skip options. * gdb.base/skip-solib.exp: Update expected output. * gdb.perf/skip-command.cc: New file. * gdb.perf/skip-command.exp: New file. * gdb.perf/skip-command.py: New file. --- gdb/ChangeLog | 30 ++ gdb/NEWS | 8 + gdb/doc/ChangeLog | 5 + gdb/doc/gdb.texinfo | 105 ++++- gdb/skip.c | 523 +++++++++++++++++++----- gdb/symtab.c | 36 +- gdb/symtab.h | 3 + gdb/testsuite/ChangeLog | 13 + gdb/testsuite/gdb.base/skip-solib.exp | 8 +- gdb/testsuite/gdb.base/skip.c | 20 + gdb/testsuite/gdb.base/skip.exp | 207 +++++++--- gdb/testsuite/gdb.base/skip1.c | 11 + gdb/testsuite/gdb.perf/skip-command.cc | 46 +++ gdb/testsuite/gdb.perf/skip-command.exp | 138 +++++++ gdb/testsuite/gdb.perf/skip-command.py | 34 ++ gdb/utils.c | 77 ++++ gdb/utils.h | 4 + 17 files changed, 1085 insertions(+), 183 deletions(-) create mode 100644 gdb/testsuite/gdb.perf/skip-command.cc create mode 100644 gdb/testsuite/gdb.perf/skip-command.exp create mode 100644 gdb/testsuite/gdb.perf/skip-command.py diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 650b7bb9e9e..b684b5b5337 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,33 @@ +2016-02-23 Doug Evans + + Extend "skip" command to support -file, -gfile, -function, -rfunction. + * NEWS: Document new features. + * skip.c: #include "fnmatch.h", "gdb_regex.h". + (skiplist_entry) : Renamed from filename. + : Renamed from function_name. + : New members. + : + New members. + (make_skip_entry): New function. + (free_skiplist_entry, free_skiplist_entry_cleanup): New functions. + (make_free_skiplist_entry_cleanup): New function. + (skip_file_command): Update. + (skip_function, skip_function_command): Update. + (compile_skip_regexp): New functions. + (skip_command): Add support for new options. + (skip_info): Update. + (skip_file_p, skip_gfile_p): New functions. + (skip_function_p, skip_rfunction_p): New functions. + (function_name_is_marked_for_skip): Update and simplify. + (_initialize_step_skip): Update. + * symtab.c: #include "fnmatch.h". + (compare_glob_filenames_for_search): New function. + * symtab.h (compare_glob_filenames_for_search): Declare. + * utils.c (count_path_elements): New function. + (strip_leading_path_elements): New function. + * utils.h (count_path_elements): Declare. + (strip_leading_path_elements): Declare. + 2016-02-23 Simon Marchi * arm-tdep.c (arm_decode_svc_copro): Remove "to" parameter. diff --git a/gdb/NEWS b/gdb/NEWS index 64c4869009c..fc7914262ad 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -141,6 +141,14 @@ show max-value-size allocate for value contents. Prevents incorrect programs from causing GDB to allocate overly large buffers. Default is 64k. +skip -file file +skip -gfile file-glob-pattern +skip -function function +skip -rfunction regular-expression + A generalized form of the skip command, with new support for + glob-style file names and regular expressions for function names. + Additionally, a file spec and a function spec may now be combined. + * The "disassemble" command accepts a new modifier: /s. It prints mixed source+disassembly like /m with two differences: - disassembled instructions are now printed in program order, and diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 189dfdc433f..c9f2afc90ed 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,8 @@ +2016-02-23 Doug Evans + + * gdb.texinfo (Skipping Over Functions and Files): Document new + options to "skip" command. Update docs of output of "info skip". + 2016-02-18 Walfred Tedeschi * gdb.texinfo (Signals): Add bound violation display hints for diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 5db7cf2c09b..4ec0ec108f8 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -5528,7 +5528,8 @@ default is @code{on}. The program you are debugging may contain some functions which are uninteresting to debug. The @code{skip} command lets you tell @value{GDBN} to -skip a function or all functions in a file when stepping. +skip a function, all functions in a file or a particular function in +a particular file when stepping. For example, consider the following C function: @@ -5555,13 +5556,75 @@ A more flexible solution is to execute @kbd{skip boring}. This instructs @code{step} at line 103, you'll step over @code{boring} and directly into @code{foo}. -You can also instruct @value{GDBN} to skip all functions in a file, with, for -example, @code{skip file boring.c}. +Functions may be skipped by providing either a function name, linespec +(@pxref{Specify Location}), regular expression that matches the function's +name, file name or a @code{glob}-style pattern that matches the file name. + +On Posix systems the form of the regular expression is +``Extended Regular Expressions''. See for example @samp{man 7 regex} +on @sc{gnu}/Linux systems. On non-Posix systems the form of the regular +expression is whatever is provided by the @code{regcomp} function of +the underlying system. +See for example @samp{man 7 glob} on @sc{gnu}/Linux systems for a +description of @code{glob}-style patterns. + +@table @code +@kindex skip +@item skip @r{[}@var{options}@r{]} +The basic form of the @code{skip} command takes zero or more options +that specify what to skip. +The @var{options} argument is any useful combination of the following: @table @code +@item -file @var{file} +@itemx -fi @var{file} +Functions in @var{file} will be skipped over when stepping. + +@item -gfile @var{file-glob-pattern} +@itemx -gfi @var{file-glob-pattern} +@cindex skipping over files via glob-style patterns +Functions in files matching @var{file-glob-pattern} will be skipped +over when stepping. + +@smallexample +(gdb) skip -gfi utils/*.c +@end smallexample + +@item -function @var{linespec} +@itemx -fu @var{linespec} +Functions named by @var{linespec} or the function containing the line +named by @var{linespec} will be skipped over when stepping. +@xref{Specify Location}. + +@item -rfunction @var{regexp} +@itemx -rfu @var{regexp} +@cindex skipping over functions via regular expressions +Functions whose name matches @var{regexp} will be skipped over when stepping. + +This form is useful for complex function names. +For example, there is generally no need to step into C@t{++} @code{std::string} +constructors or destructors. Plus with C@t{++} templates it can be hard to +write out the full name of the function, and often it doesn't matter what +the template arguments are. Specifying the function to be skipped as a +regular expression makes this easier. + +@smallexample +(gdb) skip -rfu ^std::(allocator|basic_string)<.*>::~?\1 *\( +@end smallexample + +If you want to skip every templated C@t{++} constructor and destructor +in the @code{std} namespace you can do: + +@smallexample +(gdb) skip -rfu ^std::([a-zA-z0-9_]+)<.*>::~?\1 *\( +@end smallexample +@end table + +If no options are specified, the function you're currently debugging +will be skipped. + @kindex skip function -@item skip @r{[}@var{linespec}@r{]} -@itemx skip function @r{[}@var{linespec}@r{]} +@item skip function @r{[}@var{linespec}@r{]} After running this command, the function named by @var{linespec} or the function containing the line named by @var{linespec} will be skipped over when stepping. @xref{Specify Location}. @@ -5577,6 +5640,11 @@ will be skipped. After running this command, any function whose source lives in @var{filename} will be skipped over when stepping. +@smallexample +(gdb) skip file boring.c +File boring.c will be skipped when stepping. +@end smallexample + If you do not specify @var{filename}, functions whose source lives in the file you're currently debugging will be skipped. @end table @@ -5594,20 +5662,21 @@ print a table with details about all functions and files marked for skipping. @table @emph @item Identifier A number identifying this skip. -@item Type -The type of this skip, either @samp{function} or @samp{file}. @item Enabled or Disabled -Enabled skips are marked with @samp{y}. Disabled skips are marked with @samp{n}. -@item Address -For function skips, this column indicates the address in memory of the function -being skipped. If you've set a function skip on a function which has not yet -been loaded, this field will contain @samp{}. Once a shared library -which has the function is loaded, @code{info skip} will show the function's -address here. -@item What -For file skips, this field contains the filename being skipped. For functions -skips, this field contains the function name and its line number in the file -where it is defined. +Enabled skips are marked with @samp{y}. +Disabled skips are marked with @samp{n}. +@item Glob +If the file name is a @samp{glob} pattern this is @samp{y}. +Otherwise it is @samp{n}. +@item File +The name or @samp{glob} pattern of the file to be skipped. +If no file is specified this is @samp{}. +@item RE +If the function name is a @samp{regular expression} this is @samp{y}. +Otherwise it is @samp{n}. +@item Function +The name or regular expression of the function to skip. +If no function is specified this is @samp{}. @end table @kindex skip delete diff --git a/gdb/skip.c b/gdb/skip.c index d90910d8980..15681cc7f28 100644 --- a/gdb/skip.c +++ b/gdb/skip.c @@ -32,19 +32,35 @@ #include "breakpoint.h" /* for get_sal_arch () */ #include "source.h" #include "filenames.h" +#include "fnmatch.h" +#include "gdb_regex.h" struct skiplist_entry { int number; - /* NULL if this isn't a skiplist entry for an entire file. + /* Non-zero if FILE is a glob-style pattern. + Otherewise it is the plain file name (possibly with directories). */ + int file_is_glob; + + /* The name of the file or NULL. The skiplist entry owns this pointer. */ - char *filename; + char *file; + + /* Non-zero if FUNCTION is a regexp. + Otherwise it is a plain function name (possibly with arguments, + for C++). */ + int function_is_regexp; - /* The name of the marked-for-skip function, if this is a skiplist - entry for a function. + /* The name of the function or NULL. The skiplist entry owns this pointer. */ - char *function_name; + char *function; + + /* If this is a function regexp, the compiled form. */ + regex_t compiled_function_regexp; + + /* Non-zero if the function regexp has been compiled. */ + int compiled_function_regexp_is_valid; int enabled; @@ -52,7 +68,6 @@ struct skiplist_entry }; static void add_skiplist_entry (struct skiplist_entry *e); -static void skip_function (const char *name); static struct skiplist_entry *skiplist_entry_chain; static int skiplist_entry_count; @@ -65,10 +80,62 @@ static int skiplist_entry_count; E ? (TMP = E->next, 1) : 0; \ E = TMP) +/* Create a skip object. */ + +static struct skiplist_entry * +make_skip_entry (int file_is_glob, const char *file, + int function_is_regexp, const char *function) +{ + struct skiplist_entry *e = XCNEW (struct skiplist_entry); + + gdb_assert (file != NULL || function != NULL); + if (file_is_glob) + gdb_assert (file != NULL); + if (function_is_regexp) + gdb_assert (function != NULL); + + if (file != NULL) + e->file = xstrdup (file); + if (function != NULL) + e->function = xstrdup (function); + e->file_is_glob = file_is_glob; + e->function_is_regexp = function_is_regexp; + e->enabled = 1; + + return e; +} + +/* Free a skiplist entry. */ + +static void +free_skiplist_entry (struct skiplist_entry *e) +{ + xfree (e->file); + xfree (e->function); + if (e->function_is_regexp && e->compiled_function_regexp_is_valid) + regfree (&e->compiled_function_regexp); + xfree (e); +} + +/* Wrapper to free_skiplist_entry for use as a cleanup. */ + +static void +free_skiplist_entry_cleanup (void *e) +{ + free_skiplist_entry ((struct skiplist_entry *) e); +} + +/* Create a cleanup to free skiplist entry E. */ + +static struct cleanup * +make_free_skiplist_entry_cleanup (struct skiplist_entry *e) +{ + return make_cleanup (free_skiplist_entry_cleanup, e); +} + static void skip_file_command (char *arg, int from_tty) { - struct skiplist_entry *e; struct symtab *symtab; const char *filename = NULL; @@ -85,37 +152,31 @@ skip_file_command (char *arg, int from_tty) filename = symtab_to_fullname (symtab); } else - { - symtab = lookup_symtab (arg); - if (symtab == NULL) - { - fprintf_filtered (gdb_stderr, _("No source file named %s.\n"), arg); - if (!nquery (_("\ -Ignore file pending future shared library load? "))) - return; - } - /* Do not use SYMTAB's filename, later loaded shared libraries may match - given ARG but not SYMTAB's filename. */ - filename = arg; - } + filename = arg; - e = XCNEW (struct skiplist_entry); - e->filename = xstrdup (filename); - e->enabled = 1; - - add_skiplist_entry (e); + add_skiplist_entry (make_skip_entry (0, filename, 0, NULL)); printf_filtered (_("File %s will be skipped when stepping.\n"), filename); } +/* Create a skiplist entry for the given function NAME and add it to the + list. */ + static void -skip_function_command (char *arg, int from_tty) +skip_function (const char *name) { - const char *name = NULL; + add_skiplist_entry (make_skip_entry (0, NULL, 0, name)); + + printf_filtered (_("Function %s will be skipped when stepping.\n"), name); +} +static void +skip_function_command (char *arg, int from_tty) +{ /* Default to the current function if no argument is given. */ if (arg == NULL) { + const char *name = NULL; CORE_ADDR pc; if (!last_displayed_sal_is_valid ()) @@ -128,25 +189,169 @@ skip_function_command (char *arg, int from_tty) paddress (get_current_arch (), pc)); } skip_function (name); + return; } - else + + skip_function (arg); +} + +/* Compile the regexp in E. + An error is thrown if there's an error. + MESSAGE is used as a prefix of the error message. */ + +static void +compile_skip_regexp (struct skiplist_entry *e, const char *message) +{ + int code; + int flags = REG_NOSUB; + +#ifdef REG_EXTENDED + flags |= REG_EXTENDED; +#endif + + gdb_assert (e->function_is_regexp && e->function != NULL); + + code = regcomp (&e->compiled_function_regexp, e->function, flags); + if (code != 0) { - if (lookup_symbol (arg, NULL, VAR_DOMAIN, NULL).symbol == NULL) - { - fprintf_filtered (gdb_stderr, - _("No function found named %s.\n"), arg); + char *err = get_regcomp_error (code, &e->compiled_function_regexp); - if (nquery (_("\ -Ignore function pending future shared library load? "))) - { - /* Add the unverified skiplist entry. */ - skip_function (arg); - } + make_cleanup (xfree, err); + error (_("%s: %s"), message, err); + } + e->compiled_function_regexp_is_valid = 1; +} + +/* Process "skip ..." that does not match "skip file" or "skip function". */ + +static void +skip_command (char *arg, int from_tty) +{ + const char *file = NULL; + const char *gfile = NULL; + const char *function = NULL; + const char *rfunction = NULL; + char **argv; + struct cleanup *cleanups; + struct skiplist_entry *e; + int i; + + if (arg == NULL) + { + skip_function_command (arg, from_tty); + return; + } + + argv = buildargv (arg); + cleanups = make_cleanup_freeargv (argv); + + for (i = 0; argv[i] != NULL; ++i) + { + const char *p = argv[i]; + const char *value = argv[i + 1]; + + if (strcmp (p, "-fi") == 0 + || strcmp (p, "-file") == 0) + { + if (value == NULL) + error (_("Missing value for %s option."), p); + file = value; + ++i; + } + else if (strcmp (p, "-gfi") == 0 + || strcmp (p, "-gfile") == 0) + { + if (value == NULL) + error (_("Missing value for %s option."), p); + gfile = value; + ++i; + } + else if (strcmp (p, "-fu") == 0 + || strcmp (p, "-function") == 0) + { + if (value == NULL) + error (_("Missing value for %s option."), p); + function = value; + ++i; + } + else if (strcmp (p, "-rfu") == 0 + || strcmp (p, "-rfunction") == 0) + { + if (value == NULL) + error (_("Missing value for %s option."), p); + rfunction = value; + ++i; + } + else if (*p == '-') + error (_("Invalid skip option: %s"), p); + else if (i == 0) + { + /* Assume the user entered "skip FUNCTION-NAME". + FUNCTION-NAME may be `foo (int)', and therefore we pass the + complete original arg to skip_function command as if the user + typed "skip function arg". */ + do_cleanups (cleanups); + skip_function_command (arg, from_tty); return; } + else + error (_("Invalid argument: %s"), p); + } + + if (file != NULL && gfile != NULL) + error (_("Cannot specify both -file and -gfile.")); + + if (function != NULL && rfunction != NULL) + error (_("Cannot specify both -function and -rfunction.")); + + /* This shouldn't happen as "skip" by itself gets punted to + skip_function_command. */ + gdb_assert (file != NULL || gfile != NULL + || function != NULL || rfunction != NULL); + + e = make_skip_entry (gfile != NULL, file ? file : gfile, + rfunction != NULL, function ? function : rfunction); + if (rfunction != NULL) + { + struct cleanup *rf_cleanups = make_free_skiplist_entry_cleanup (e); - skip_function (arg); + compile_skip_regexp (e, _("regexp")); + discard_cleanups (rf_cleanups); } + add_skiplist_entry (e); + + /* I18N concerns drive some of the choices here (we can't piece together + the output too much). OTOH we want to keep this simple. Therefore the + only polish we add to the output is to append "(s)" to "File" or + "Function" if they're a glob/regexp. */ + { + const char *file_to_print = file != NULL ? file : gfile; + const char *function_to_print = function != NULL ? function : rfunction; + const char *file_text = gfile != NULL ? _("File(s)") : _("File"); + const char *lower_file_text = gfile != NULL ? _("file(s)") : _("file"); + const char *function_text + = rfunction != NULL ? _("Function(s)") : _("Function"); + + if (function_to_print == NULL) + { + printf_filtered (_("%s %s will be skipped when stepping.\n"), + file_text, file_to_print); + } + else if (file_to_print == NULL) + { + printf_filtered (_("%s %s will be skipped when stepping.\n"), + function_text, function_to_print); + } + else + { + printf_filtered (_("%s %s in %s %s will be skipped" + " when stepping.\n"), + function_text, function_to_print, + lower_file_text, file_to_print); + } + } + + do_cleanups (cleanups); } static void @@ -177,14 +382,17 @@ Not skipping any files or functions.\n")); return; } - tbl_chain = make_cleanup_ui_out_table_begin_end (current_uiout, 4, + tbl_chain = make_cleanup_ui_out_table_begin_end (current_uiout, 6, num_printable_entries, "SkiplistTable"); - ui_out_table_header (current_uiout, 7, ui_left, "number", "Num"); /* 1 */ - ui_out_table_header (current_uiout, 14, ui_left, "type", "Type"); /* 2 */ - ui_out_table_header (current_uiout, 3, ui_left, "enabled", "Enb"); /* 3 */ - ui_out_table_header (current_uiout, 40, ui_noalign, "what", "What"); /* 4 */ + ui_out_table_header (current_uiout, 5, ui_left, "number", "Num"); /* 1 */ + ui_out_table_header (current_uiout, 3, ui_left, "enabled", "Enb"); /* 2 */ + ui_out_table_header (current_uiout, 4, ui_right, "regexp", "Glob"); /* 3 */ + ui_out_table_header (current_uiout, 20, ui_left, "file", "File"); /* 4 */ + ui_out_table_header (current_uiout, 2, ui_right, "regexp", "RE"); /* 5 */ + ui_out_table_header (current_uiout, 40, ui_noalign, + "function", "Function"); /* 6 */ ui_out_table_body (current_uiout); ALL_SKIPLIST_ENTRIES (e) @@ -197,25 +405,27 @@ Not skipping any files or functions.\n")); entry_chain = make_cleanup_ui_out_tuple_begin_end (current_uiout, "blklst-entry"); - ui_out_field_int (current_uiout, "number", e->number); /* 1 */ + ui_out_field_int (current_uiout, "number", e->number); /* 1 */ - if (e->function_name != NULL) - ui_out_field_string (current_uiout, "type", "function"); /* 2 */ - else if (e->filename != NULL) - ui_out_field_string (current_uiout, "type", "file"); /* 2 */ + if (e->enabled) + ui_out_field_string (current_uiout, "enabled", "y"); /* 2 */ else - internal_error (__FILE__, __LINE__, _("\ -Skiplist entry should have either a filename or a function name.")); + ui_out_field_string (current_uiout, "enabled", "n"); /* 2 */ - if (e->enabled) - ui_out_field_string (current_uiout, "enabled", "y"); /* 3 */ + if (e->file_is_glob) + ui_out_field_string (current_uiout, "regexp", "y"); /* 3 */ else - ui_out_field_string (current_uiout, "enabled", "n"); /* 3 */ + ui_out_field_string (current_uiout, "regexp", "n"); /* 3 */ - if (e->function_name != NULL) - ui_out_field_string (current_uiout, "what", e->function_name); /* 4 */ - else if (e->filename != NULL) - ui_out_field_string (current_uiout, "what", e->filename); /* 4 */ + ui_out_field_string (current_uiout, "file", + e->file ? e->file : ""); /* 4 */ + if (e->function_is_regexp) + ui_out_field_string (current_uiout, "regexp", "y"); /* 5 */ + else + ui_out_field_string (current_uiout, "regexp", "n"); /* 5 */ + + ui_out_field_string (current_uiout, "function", + e->function ? e->function : ""); /* 6 */ ui_out_text (current_uiout, "\n"); do_cleanups (entry_chain); @@ -273,9 +483,7 @@ skip_delete_command (char *arg, int from_tty) else skiplist_entry_chain = e->next; - xfree (e->function_name); - xfree (e->filename); - xfree (e); + free_skiplist_entry (e); found = 1; } else @@ -287,22 +495,6 @@ skip_delete_command (char *arg, int from_tty) error (_("No skiplist entries found with number %s."), arg); } -/* Create a skiplist entry for the given function NAME and add it to the - list. */ - -static void -skip_function (const char *name) -{ - struct skiplist_entry *e = XCNEW (struct skiplist_entry); - - e->enabled = 1; - e->function_name = xstrdup (name); - - add_skiplist_entry (e); - - printf_filtered (_("Function %s will be skipped when stepping.\n"), name); -} - /* Add the given skiplist entry to our list, and set the entry's number. */ static void @@ -326,6 +518,98 @@ add_skiplist_entry (struct skiplist_entry *e) } } +/* Return non-zero if we're stopped at a file to be skipped. */ + +static int +skip_file_p (struct skiplist_entry *e, + const struct symtab_and_line *function_sal) +{ + gdb_assert (e->file != NULL && !e->file_is_glob); + + if (function_sal->symtab == NULL) + return 0; + + /* Check first sole SYMTAB->FILENAME. It may not be a substring of + symtab_to_fullname as it may contain "./" etc. */ + if (compare_filenames_for_search (function_sal->symtab->filename, e->file)) + return 1; + + /* Before we invoke realpath, which can get expensive when many + files are involved, do a quick comparison of the basenames. */ + if (!basenames_may_differ + && filename_cmp (lbasename (function_sal->symtab->filename), + lbasename (e->file)) != 0) + return 0; + + /* Note: symtab_to_fullname caches its result, thus we don't have to. */ + { + const char *fullname = symtab_to_fullname (function_sal->symtab); + + if (compare_filenames_for_search (fullname, e->file)) + return 1; + } + + return 0; +} + +/* Return non-zero if we're stopped at a globbed file to be skipped. */ + +static int +skip_gfile_p (struct skiplist_entry *e, + const struct symtab_and_line *function_sal) +{ + gdb_assert (e->file != NULL && e->file_is_glob); + + if (function_sal->symtab == NULL) + return 0; + + /* Check first sole SYMTAB->FILENAME. It may not be a substring of + symtab_to_fullname as it may contain "./" etc. */ + if (gdb_filename_fnmatch (e->file, function_sal->symtab->filename, + FNM_FILE_NAME | FNM_NOESCAPE) == 0) + return 1; + + /* Before we invoke symtab_to_fullname, which is expensive, do a quick + comparison of the basenames. + Note that we assume that lbasename works with glob-style patterns. + If the basename of the glob pattern is something like "*.c" then this + isn't much of a win. Oh well. */ + if (!basenames_may_differ + && gdb_filename_fnmatch (lbasename (e->file), + lbasename (function_sal->symtab->filename), + FNM_FILE_NAME | FNM_NOESCAPE) != 0) + return 0; + + /* Note: symtab_to_fullname caches its result, thus we don't have to. */ + { + const char *fullname = symtab_to_fullname (function_sal->symtab); + + if (compare_glob_filenames_for_search (fullname, e->file)) + return 1; + } + + return 0; +} + +/* Return non-zero if we're stopped at a function to be skipped. */ + +static int +skip_function_p (struct skiplist_entry *e, const char *function_name) +{ + gdb_assert (e->function != NULL && !e->function_is_regexp); + return strcmp_iw (function_name, e->function) == 0; +} + +/* Return non-zero if we're stopped at a function regexp to be skipped. */ + +static int +skip_rfunction_p (struct skiplist_entry *e, const char *function_name) +{ + gdb_assert (e->function != NULL && e->function_is_regexp + && e->compiled_function_regexp_is_valid); + return (regexec (&e->compiled_function_regexp, function_name, 0, NULL, 0) + == 0); +} /* See skip.h. */ @@ -333,8 +617,6 @@ int function_name_is_marked_for_skip (const char *function_name, const struct symtab_and_line *function_sal) { - int searched_for_fullname = 0; - const char *fullname = NULL; struct skiplist_entry *e; if (function_name == NULL) @@ -342,43 +624,49 @@ function_name_is_marked_for_skip (const char *function_name, ALL_SKIPLIST_ENTRIES (e) { + int skip_by_file = 0; + int skip_by_function = 0; + if (!e->enabled) continue; - /* Does the pc we're stepping into match e's stored pc? */ - if (e->function_name != NULL - && strcmp_iw (function_name, e->function_name) == 0) - return 1; - - if (e->filename != NULL) + if (e->file != NULL) { - /* Check first sole SYMTAB->FILENAME. It does not need to be - a substring of symtab_to_fullname as it may contain "./" etc. */ - if (function_sal->symtab != NULL - && compare_filenames_for_search (function_sal->symtab->filename, - e->filename)) - return 1; - - /* Before we invoke realpath, which can get expensive when many - files are involved, do a quick comparison of the basenames. */ - if (!basenames_may_differ - && (function_sal->symtab == NULL - || filename_cmp (lbasename (function_sal->symtab->filename), - lbasename (e->filename)) != 0)) - continue; - - /* Get the filename corresponding to this FUNCTION_SAL, if we haven't - yet. */ - if (!searched_for_fullname) + if (e->file_is_glob) + { + if (skip_gfile_p (e, function_sal)) + skip_by_file = 1; + } + else { - if (function_sal->symtab != NULL) - fullname = symtab_to_fullname (function_sal->symtab); - searched_for_fullname = 1; + if (skip_file_p (e, function_sal)) + skip_by_file = 1; } - if (fullname != NULL - && compare_filenames_for_search (fullname, e->filename)) + } + if (e->function != NULL) + { + if (e->function_is_regexp) + { + if (skip_rfunction_p (e, function_name)) + skip_by_function = 1; + } + else + { + if (skip_function_p (e, function_name)) + skip_by_function = 1; + } + } + + /* If both file and function must match, make sure we don't errantly + exit if only one of them match. */ + if (e->file != NULL && e->function != NULL) + { + if (skip_by_file && skip_by_function) return 1; } + /* Only one of file/function is specified. */ + else if (skip_by_file || skip_by_function) + return 1; } return 0; @@ -396,22 +684,31 @@ _initialize_step_skip (void) skiplist_entry_chain = 0; skiplist_entry_count = 0; - add_prefix_cmd ("skip", class_breakpoint, skip_function_command, _("\ + add_prefix_cmd ("skip", class_breakpoint, skip_command, _("\ Ignore a function while stepping.\n\ -Usage: skip [FUNCTION NAME]\n\ -If no function name is given, ignore the current function."), +\n\ +Usage: skip [FUNCTION-NAME]\n\ + skip [] []\n\ +If no arguments are given, ignore the current function.\n\ +\n\ + is one of:\n\ + -fi|-file FILE-NAME\n\ + -gfi|-gfile GLOB-FILE-PATTERN\n\ + is one of:\n\ + -fu|-function FUNCTION-NAME\n\ + -rfu|-rfunction FUNCTION-NAME-REGULAR-EXPRESSION"), &skiplist, "skip ", 1, &cmdlist); c = add_cmd ("file", class_breakpoint, skip_file_command, _("\ Ignore a file while stepping.\n\ -Usage: skip file [FILENAME]\n\ +Usage: skip file [FILE-NAME]\n\ If no filename is given, ignore the current file."), &skiplist); set_cmd_completer (c, filename_completer); c = add_cmd ("function", class_breakpoint, skip_function_command, _("\ Ignore a function while stepping.\n\ -Usage: skip function [FUNCTION NAME]\n\ +Usage: skip function [FUNCTION-NAME]\n\ If no function name is given, skip the current function."), &skiplist); set_cmd_completer (c, location_completer); diff --git a/gdb/symtab.c b/gdb/symtab.c index 95b3a110dd7..e06104b38f0 100644 --- a/gdb/symtab.c +++ b/gdb/symtab.c @@ -41,7 +41,7 @@ #include "p-lang.h" #include "addrmap.h" #include "cli/cli-utils.h" - +#include "fnmatch.h" #include "hashtab.h" #include "gdb_obstack.h" @@ -342,6 +342,40 @@ compare_filenames_for_search (const char *filename, const char *search_name) && STRIP_DRIVE_SPEC (filename) == &filename[len - search_len])); } +/* Same as compare_filenames_for_search, but for glob-style patterns. + Heads up on the order of the arguments. They match the order of + compare_filenames_for_search, but it's the opposite of the order of + arguments to gdb_filename_fnmatch. */ + +int +compare_glob_filenames_for_search (const char *filename, + const char *search_name) +{ + /* We rely on the property of glob-style patterns with FNM_FILE_NAME that + all /s have to be explicitly specified. */ + int file_path_elements = count_path_elements (filename); + int search_path_elements = count_path_elements (search_name); + + if (search_path_elements > file_path_elements) + return 0; + + if (IS_ABSOLUTE_PATH (search_name)) + { + return (search_path_elements == file_path_elements + && gdb_filename_fnmatch (search_name, filename, + FNM_FILE_NAME | FNM_NOESCAPE) == 0); + } + + { + const char *file_to_compare + = strip_leading_path_elements (filename, + file_path_elements - search_path_elements); + + return gdb_filename_fnmatch (search_name, file_to_compare, + FNM_FILE_NAME | FNM_NOESCAPE) == 0; + } +} + /* Check for a symtab of a specific name by searching some symtabs. This is a helper function for callbacks of iterate_over_symtabs. diff --git a/gdb/symtab.h b/gdb/symtab.h index f7884b97179..6f00baf463f 100644 --- a/gdb/symtab.h +++ b/gdb/symtab.h @@ -1600,6 +1600,9 @@ extern int basenames_may_differ; int compare_filenames_for_search (const char *filename, const char *search_name); +int compare_glob_filenames_for_search (const char *filename, + const char *search_name); + int iterate_over_some_symtabs (const char *name, const char *real_path, int (*callback) (struct symtab *symtab, diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 8acc5c59ea1..54dcfd66741 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,16 @@ +2016-02-23 Doug Evans + + * gdb.base/skip.c (test_skip): New function. + (end_test_skip_file_and_function): New function. + (test_skip_file_and_function): New function. + * gdb.base/skip1.c (test_skip): New function. + (skip1_test_skip_file_and_function): New function. + * gdb.base/skip.exp: Add tests for new skip options. + * gdb.base/skip-solib.exp: Update expected output. + * gdb.perf/skip-command.cc: New file. + * gdb.perf/skip-command.exp: New file. + * gdb.perf/skip-command.py: New file. + 2016-02-22 Marcin Kościelnicki * gdb.trace/unavailable.exp (gdb_unavailable_registers_test_1): Fix diff --git a/gdb/testsuite/gdb.base/skip-solib.exp b/gdb/testsuite/gdb.base/skip-solib.exp index a4047c37ead..ed81d65fac1 100644 --- a/gdb/testsuite/gdb.base/skip-solib.exp +++ b/gdb/testsuite/gdb.base/skip-solib.exp @@ -73,8 +73,8 @@ Ignore file pending future shared library load.*" \ # Checkinfo skip list. # gdb_test "info skip" \ - "Num\\s+Type\\s+Enb\\s+What\\s* -1\\s+file\\s+y\\s+${srcfile_lib}\\s*" \ + "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s* +1\\s+y\\s+n\\s+${srcfile_lib}\\s+n\\s+\\s*" \ "info skip with pending file" if ![runto_main] { fail "skip tests suppressed" } @@ -108,8 +108,8 @@ gdb_test "step" "square.*" # Now our entry should no longer be pending. # gdb_test "info skip" \ - "Num\\s+Type\\s+Enb\\s+What\\s* -1\\s+function\\s+y\\s+multiply\\s*" \ + "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s* +1\\s+y\\s+n\\s+\\s+n\\s+multiply\\s*" \ "info skip for function multiply" # diff --git a/gdb/testsuite/gdb.base/skip.c b/gdb/testsuite/gdb.base/skip.c index 35682960c10..b9db2a78986 100644 --- a/gdb/testsuite/gdb.base/skip.c +++ b/gdb/testsuite/gdb.base/skip.c @@ -20,6 +20,7 @@ int foo (void); int bar (void); int baz (int); +void skip1_test_skip_file_and_function (void); int main () @@ -33,3 +34,22 @@ foo () { return 0; } + +static void +test_skip (void) +{ +} + +static void +end_test_skip_file_and_function (void) +{ + abort (); +} + +void +test_skip_file_and_function (void) +{ + test_skip (); + skip1_test_skip_file_and_function (); + end_test_skip_file_and_function (); +} diff --git a/gdb/testsuite/gdb.base/skip.exp b/gdb/testsuite/gdb.base/skip.exp index 40f31037e22..7d28b8b3407 100644 --- a/gdb/testsuite/gdb.base/skip.exp +++ b/gdb/testsuite/gdb.base/skip.exp @@ -14,6 +14,7 @@ # along with this program. If not, see . # This file was written by Justin Lebar. (justin.lebar@gmail.com) +# And further hacked on by Doug Evans. (dje@google.com) if { [prepare_for_testing skip.exp "skip" \ {skip.c skip1.c } \ @@ -30,7 +31,24 @@ gdb_test "skip file" "No default file now." "skip file (no default file)" gdb_test "skip function" "No default function now." gdb_test "skip" "No default function now." "skip (no default function)" -if ![runto_main] { fail "skip tests suppressed" } +# Test elided args. + +gdb_test "skip -fi" "Missing value for -fi option." +gdb_test "skip -file" "Missing value for -file option." +gdb_test "skip -fu" "Missing value for -fu option." +gdb_test "skip -function" "Missing value for -function option." +gdb_test "skip -rfu" "Missing value for -rfu option." +gdb_test "skip -rfunction" "Missing value for -rfunction option." + +# Test other invalid option combinations. + +gdb_test "skip -x" "Invalid skip option: -x" +gdb_test "skip -rfu foo.* xyzzy" "Invalid argument: xyzzy" + +if ![runto_main] { + fail "Can't run to main" + return +} # Test |info skip| with an empty skiplist. @@ -62,34 +80,39 @@ gdb_test "info skip 999" "No skiplist entries found with number 999." # Does |info skip| look right? gdb_test "info skip" \ - "Num\\s+Type\\s+Enb\\s+What\\s* -1\\s+file\\s+y\\s+.*$srcfile\\s* -2\\s+function\\s+y\\s+main\\s* -3\\s+file\\s+y\\s+$srcfile1\\s* -4\\s+function\\s+y\\s+baz\\s*" + "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s* +1\\s+y\\s+n\\s+.*$srcfile\\s+n\\s+\\s* +2\\s+y\\s+n\\s+\\s+n\\s+main\\s* +3\\s+y\\s+n\\s+$srcfile1\\s+n\\s+\\s* +4\\s+y\\s+n\\s+\\s+n\\s+baz\\s*" # Right now, we have an outstanding skiplist entry on both source # files, so when we step into the first line in main(), we should step # right over it and go to the second line of main(). -if ![runto_main] { fail "skip tests suppressed" } +if ![runto_main] { + fail "Can't run to main" + return +} gdb_test "step" ".*" "step in the main" gdb_test "bt" "\\s*\\#0\\s+main.*" "step after all ignored" # Now remove skip.c from the skiplist. Our first step should take us -# into foo(), and our second step should take us to the next line in -# main(). +# into foo(), and our second step should take us to the next line in main(). gdb_test "skip delete 1" # Check that entry 1 is missing from |info skip| gdb_test "info skip" \ - "Num\\s+Type\\s+Enb\\s+What\\s* -2\\s+function\\s+y\\s+main\\s* -3\\s+file\\s+y\\s+$srcfile1\\s* -4\\s+function\\s+y\\s+baz\\s*" \ + "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s* +2\\s+y\\s+n\\s+\\s+n\\s+main\\s* +3\\s+y\\s+n\\s+$srcfile1\\s+n\\s+\\s* +4\\s+y\\s+n\\s+\\s+n\\s+baz\\s*" \ "info skip (delete 1)" -if ![runto_main] { fail "skip tests suppressed" } +if ![runto_main] { + fail "Can't run to main" + return +} set test "step after deleting 1" gdb_test "step" "foo \\(\\) at.*" "$test (1)" gdb_test "step" ".*" "$test (2)" ; # Return from foo() @@ -100,10 +123,14 @@ gdb_test "step" "main \\(\\) at.*" "$test (3)" gdb_test "skip disable 3" # Is entry 3 disabled in |info skip|? -gdb_test "info skip 3" ".*\\n3\\s+file\\s+n.*" \ - "info skip shows entry as disabled" +gdb_test "info skip 3" \ + "3\\s+n\\s+n\\s+$srcfile1\\s+n\\s+\\s*" \ + "info skip shows entry as disabled" -if ![runto_main] { fail "skip tests suppressed" } +if ![runto_main] { + fail "Can't run to main" + return +} set test "step after disabling 3" gdb_test "step" "bar \\(\\) at.*" "$test (1)" gdb_test "step" ".*" "$test (2)"; # Return from foo() @@ -115,9 +142,13 @@ gdb_test "step" "main \\(\\) at.*" "$test (5)" gdb_test "skip enable 3" # Is entry 3 enabled in |info skip|? -gdb_test "info skip 3" ".*\\n3\\s+file\\s+y.*" \ - "info skip shows entry as enabled" -if ![runto_main] { fail "skip tests suppressed" } +gdb_test "info skip 3" \ + "3\\s+y\\s+n\\s+$srcfile1\\s+n\\s+\\s*" \ + "info skip shows entry as enabled" +if ![runto_main] { + fail "Can't run to main" + return +} set test "step after enable 3" gdb_test "step" "foo \\(\\) at.*" "$test (1)" gdb_test "step" ".*" "$test (2)"; # Return from foo() @@ -125,47 +156,129 @@ gdb_test "step" "main \\(\\) at.*" "$test (3)" gdb_test "skip disable" gdb_test "info skip" \ - "Num\\s+Type\\s+Enb\\s+What\\s* -2\\s+function\\s+n\\s+main\\s* -3\\s+file\\s+n\\s+$srcfile1\\s* -4\\s+function\\s+n\\s+baz\\s*" \ - "info skip after disabling all" + "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s* +2\\s+n\\s+n\\s+\\s+n\\s+main\\s* +3\\s+n\\s+n\\s+$srcfile1\\s+n\\s+\\s* +4\\s+n\\s+n\\s+\\s+n\\s+baz\\s*" \ + "info skip after disabling all" gdb_test "skip enable" gdb_test "info skip" \ - "Num\\s+Type\\s+Enb\\s+What\\s* -2\\s+function\\s+y\\s+main\\s* -3\\s+file\\s+y\\s+$srcfile1\\s* -4\\s+function\\s+y\\s+baz\\s*" \ - "info skip after enabling all" + "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s* +2\\s+y\\s+n\\s+\\s+n\\s+main\\s* +3\\s+y\\s+n\\s+$srcfile1\\s+n\\s+\\s* +4\\s+y\\s+n\\s+\\s+n\\s+baz\\s*" \ + "info skip after enabling all" gdb_test "skip disable 4 2-3" gdb_test "info skip" \ - "Num\\s+Type\\s+Enb\\s+What\\s* -2\\s+function\\s+n\\s+main\\s* -3\\s+file\\s+n\\s+$srcfile1\\s* -4\\s+function\\s+n\\s+baz\\s*" \ - "info skip after disabling 4 2-3" + "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s* +2\\s+n\\s+n\\s+\\s+n\\s+main\\s* +3\\s+n\\s+n\\s+$srcfile1\\s+n\\s+\\s* +4\\s+n\\s+n\\s+\\s+n\\s+baz\\s*" \ + "info skip after disabling 4 2-3" gdb_test "skip enable 2-3" gdb_test "info skip" \ - "Num\\s+Type\\s+Enb\\s+What\\s* -2\\s+function\\s+y\\s+main\\s* -3\\s+file\\s+y\\s+$srcfile1\\s* -4\\s+function\\s+n\\s+baz\\s*" \ - "info skip after enabling 2-3" + "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s* +2\\s+y\\s+n\\s+\\s+n\\s+main\\s* +3\\s+y\\s+n\\s+$srcfile1\\s+n\\s+\\s* +4\\s+n\\s+n\\s+\\s+n\\s+baz\\s*" \ + "info skip after enabling 2-3" gdb_test "info skip 2-3" \ - "Num\\s+Type\\s+Enb\\s+What\\s* -2\\s+function\\s+y\\s+main\\s* -3\\s+file\\s+y\\s+$srcfile1\\s*" \ - "info skip 2-3" + "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s* +2\\s+y\\s+n\\s+\\s+n\\s+main\\s* +3\\s+y\\s+n\\s+$srcfile1\\s+n\\s+\\s*" \ + "info skip 2-3" gdb_test "skip delete 2 3" gdb_test "info skip" \ - "4\\s+function\\s+n\\s+baz\\s*" \ - "info skip after deleting 2 3" + "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s* +4\\s+n\\s+n\\s+\\s+n\\s+baz\\s*" \ + "info skip after deleting 2 3" gdb_test "skip delete" gdb_test "info skip" "Not skipping any files or functions\." \ - "info skip after deleting all" + "info skip after deleting all" + +# Now test skip -fi, etc. + +# Create a skiplist entry for a specified file and function. +gdb_test "skip -fi skip1.c" "File .*$srcfile1 will be skipped when stepping\." +gdb_test "skip -gfi sk*1.c" "File\\(s\\) sk\\*1.c will be skipped when stepping\." +gdb_test "skip -fu baz" "Function baz will be skipped when stepping\." +gdb_test "skip -rfu ^b.z$" "Function\\(s\\) \\^b\\.z\\$ will be skipped when stepping." + +if ![runto_main] { + fail "Can't run to main" + return +} + +set test "step using -fi" +gdb_test_no_output "skip disable" +gdb_test_no_output "skip enable 5" +gdb_test "step" "foo \\(\\) at.*" "$test (1)" +gdb_test "step" ".*" "$test (2)"; # Return from foo() +gdb_test "step" "main \\(\\) at.*" "$test (3)" + +if ![runto_main] { + fail "Can't run to main" + return +} + +set test "step using -gfi" +gdb_test_no_output "skip disable" +gdb_test_no_output "skip enable 6" +gdb_test "step" "foo \\(\\) at.*" "$test (1)" +gdb_test "step" ".*" "$test (2)"; # Return from foo() +gdb_test "step" "main \\(\\) at.*" "$test (3)" + +if ![runto_main] { + fail "Can't run to main" + return +} + +set test "step using -fu for baz" +gdb_test_no_output "skip disable" +gdb_test_no_output "skip enable 7" +gdb_test "step" "bar \\(\\) at.*" "$test (1)" +gdb_test "step" ".*" "$test (2)"; # Return from bar() +gdb_test "step" "foo \\(\\) at.*" "$test (3)" +gdb_test "step" ".*" "$test (4)"; # Return from foo() +gdb_test "step" "main \\(\\) at.*" "$test (5)" + +if ![runto_main] { + fail "Can't run to main" + return +} + +set test "step using -rfu for baz" +gdb_test_no_output "skip disable" +gdb_test_no_output "skip enable 8" +gdb_test "step" "bar \\(\\) at.*" "$test (1)" +gdb_test "step" ".*" "$test (2)"; # Return from bar() +gdb_test "step" "foo \\(\\) at.*" "$test (3)" +gdb_test "step" ".*" "$test (4)"; # Return from foo() +gdb_test "step" "main \\(\\) at.*" "$test (5)" + +# Test -fi + -fu. + +if ![runto_main] { + fail "Can't run to main" + return +} + +set test "step using -fi + -fu" +gdb_test_no_output "skip delete" +gdb_test "skip -fi skip1.c -fu test_skip" \ + "Function test_skip in file skip1.c will be skipped when stepping\." +gdb_breakpoint "test_skip_file_and_function" +gdb_breakpoint "end_test_skip_file_and_function" +gdb_test "call test_skip_file_and_function ()" "silently stop." +# Verify we can step into skip.c:test_skip but not skip1.c:test_skip. +gdb_test "step" "test_skip \\(\\) at.*" "$test (1)" +gdb_test "step" "test_skip_file_and_function \\(\\) at.*" "$test (2)"; # Return from test_skip() +gdb_test "step" "skip1_test_skip_file_and_function \\(\\) at.*" "$test (4)" +gdb_test "step" ".*" "$test (5)"; # Skip over test_skip() +gdb_test "step" "test_skip_file_and_function \\(\\) at.*" "$test (6)"; # Return from skip1_test_skip_file_and_function() diff --git a/gdb/testsuite/gdb.base/skip1.c b/gdb/testsuite/gdb.base/skip1.c index 9a06e6211a3..7d94332330e 100644 --- a/gdb/testsuite/gdb.base/skip1.c +++ b/gdb/testsuite/gdb.base/skip1.c @@ -26,3 +26,14 @@ baz (int a) { return a + 1; } + +static void +test_skip (void) +{ +} + +void +skip1_test_skip_file_and_function (void) +{ + test_skip (); +} diff --git a/gdb/testsuite/gdb.perf/skip-command.cc b/gdb/testsuite/gdb.perf/skip-command.cc new file mode 100644 index 00000000000..5820ef70d75 --- /dev/null +++ b/gdb/testsuite/gdb.perf/skip-command.cc @@ -0,0 +1,46 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright (C) 2016 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 . */ + +volatile int flag; + +int +func () +{ + return 42; +} + +class c +{ + public: + int _x; + c () : _x (42) {} +}; + +void +call_me (int x, c y) +{ +} + +int +main () +{ + while (flag) + { + call_me (func (), c ()); + } + return 0; +} diff --git a/gdb/testsuite/gdb.perf/skip-command.exp b/gdb/testsuite/gdb.perf/skip-command.exp new file mode 100644 index 00000000000..d251725916e --- /dev/null +++ b/gdb/testsuite/gdb.perf/skip-command.exp @@ -0,0 +1,138 @@ +# Copyright (C) 2016 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 . + +# This test case is to test the speed of GDB when it is single-stepping +# with skip directives active. There's no need to test skip directives that +# match functions we're stepping through. That's not the interesting case. +# The interesting case is where there are 100s or more classes or libraries, +# each providing their own set of skip directives. +# +# Parameters: +# SKIP_STEP_COUNT: the number of single steps GDB performs +# SKIP_DIRECTIVE_COUNT: the number of skip directives to create + +load_lib perftest.exp + +if [skip_perf_tests] { + return 0 +} + +standard_testfile .cc skip-funcs.cc +set executable $testfile +set skip_func_file [standard_output_file $srcfile2] +set expfile $testfile.exp + +# make check-perf RUNTESTFLAGS='skip-command.exp SKIP_STEP_COUNT=1000 ...' +if ![info exists SKIP_STEP_COUNT] { + set SKIP_STEP_COUNT 1000 +} +if ![info exists SKIP_DIRECTIVE_COUNT] { + set SKIP_DIRECTIVE_COUNT 1000 +} + +proc delete_all_skips { } { + # FIXME: skip currently doesn't ask for confirmation + # FIXME: "skip delete" with no skips -> + # "No skiplist entries found with number (null)." + gdb_test_no_output "set confirm off" + gdb_test "skip delete" "" + gdb_test_no_output "set confirm on" +} + +proc install_skips { kind text nr_skips } { + global gdb_prompt + set test "install_skips" + delete_all_skips + switch $kind { + "function" { set skip_arg "-function" } + "regexp" { set skip_arg "-rfunction" } + default { + perror "bad skip kind: $kind" + return + } + } + for { set i 0 } { $i < $nr_skips } { incr i } { + gdb_test "skip $skip_arg [format $text $i]" "" + } + # There could be 1000's of these, which can overflow the buffer. + # However, it's good to have this in the log, so we go to the effort + # to read it all in. + gdb_test_multiple "info skip" $test { + -re "\[^\r\n\]*\r\n" { exp_continue } + -re "\[\r\n\]*$gdb_prompt $" { + pass $test + } + timeout { + fail "$test (timeout)" + } + } +} + +proc write_skip_func_source { file_name func_name_prefix nr_funcs } { + set f [open $file_name "w"] + puts $f "// DO NOT EDIT, machine generated file. See skip-command.exp." + for { set i 0 } { $i < $nr_funcs } { incr i } { + set func_name [format "${func_name_prefix}_%02d" $i] + puts $f "int $func_name () { return 0; }" + } + close $f +} + +proc run_skip_bench { kind text } { + global SKIP_STEP_COUNT SKIP_DIRECTIVE_COUNT + + if ![runto_main] { + fail "Can't run to main" + return -1 + } + + gdb_test_no_output "set variable flag = 1" + + for { set i 0 } { $i < 5 } { incr i } { + set nr_skips [expr $i * $SKIP_DIRECTIVE_COUNT] + install_skips $kind $text $nr_skips + gdb_test_no_output "python SkipCommand\(\"skip-$kind-$nr_skips\", ${SKIP_STEP_COUNT}\).run()" + } + + gdb_test "set variable flag = 0" +} + +PerfTest::assemble { + global srcdir subdir srcfile binfile skip_func_file + global SKIP_DIRECTIVE_COUNT + + write_skip_func_source $skip_func_file "skip_func" [expr 4 * $SKIP_DIRECTIVE_COUNT] + if { [gdb_compile [list "$srcdir/$subdir/$srcfile" $skip_func_file] ${binfile} executable {c++ debug}] != "" } { + return -1 + } + return 0 +} { + global binfile + clean_restart $binfile + return 0 +} { + global SKIP_STEP_COUNT SKIP_DIRECTIVE_COUNT + + with_test_prefix "time_skip_func" { + # N.B. The function name must match the ones in skip-command.cc. + run_skip_bench "function" "skip_func_%02d" + } + + with_test_prefix "time_skip_constructor" { + run_skip_bench "regexp" "^(skip_class_%02d)::\\1 *\\(" + } + + return 0 +} diff --git a/gdb/testsuite/gdb.perf/skip-command.py b/gdb/testsuite/gdb.perf/skip-command.py new file mode 100644 index 00000000000..12c28f20be5 --- /dev/null +++ b/gdb/testsuite/gdb.perf/skip-command.py @@ -0,0 +1,34 @@ +# Copyright (C) 2016 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 . + +from perftest import perftest + +class SkipCommand (perftest.TestCaseWithBasicMeasurements): + def __init__(self, name, step): + super (SkipCommand, self).__init__ (name) + self.step = step + + def warm_up(self): + for _ in range(0, 10): + gdb.execute("step", False, True) + + def _run(self, r): + for _ in range(0, r): + gdb.execute("step", False, True) + + def execute_test(self): + for i in range(1, 5): + func = lambda: self._run(i * self.step) + self.measure.measure(func, i * self.step) diff --git a/gdb/utils.c b/gdb/utils.c index e34c12efcc3..977314b95a2 100644 --- a/gdb/utils.c +++ b/gdb/utils.c @@ -3380,6 +3380,83 @@ gdb_filename_fnmatch (const char *pattern, const char *string, int flags) return fnmatch (pattern, string, flags); } +/* Return the number of path elements in PATH. + / = 1 + /foo = 2 + /foo/ = 2 + foo/bar = 2 + foo/ = 1 */ + +int +count_path_elements (const char *path) +{ + int count = 0; + const char *p = path; + + if (HAS_DRIVE_SPEC (p)) + { + p = STRIP_DRIVE_SPEC (p); + ++count; + } + + while (*p != '\0') + { + if (IS_DIR_SEPARATOR (*p)) + ++count; + ++p; + } + + /* Backup one if last character is /, unless it's the only one. */ + if (p > path + 1 && IS_DIR_SEPARATOR (p[-1])) + --count; + + /* Add one for the file name, if present. */ + if (p > path && !IS_DIR_SEPARATOR (p[-1])) + ++count; + + return count; +} + +/* Remove N leading path elements from PATH. + N must be non-negative. + If PATH has more than N path elements then return NULL. + If PATH has exactly N path elements then return "". + See count_path_elements for a description of how we do the counting. */ + +const char * +strip_leading_path_elements (const char *path, int n) +{ + int i = 0; + const char *p = path; + + gdb_assert (n >= 0); + + if (n == 0) + return p; + + if (HAS_DRIVE_SPEC (p)) + { + p = STRIP_DRIVE_SPEC (p); + ++i; + } + + while (i < n) + { + while (*p != '\0' && !IS_DIR_SEPARATOR (*p)) + ++p; + if (*p == '\0') + { + if (i + 1 == n) + return ""; + return NULL; + } + ++p; + ++i; + } + + return p; +} + /* Provide a prototype to silence -Wmissing-prototypes. */ extern initialize_file_ftype _initialize_utils; diff --git a/gdb/utils.h b/gdb/utils.h index 4571aaf54f6..0687c86c19d 100644 --- a/gdb/utils.h +++ b/gdb/utils.h @@ -135,6 +135,10 @@ extern void substitute_path_component (char **stringp, const char *from, const char *to); char *ldirname (const char *filename); + +extern int count_path_elements (const char *path); + +extern const char *strip_leading_path_elements (const char *path, int n); /* GDB output, ui_file utilities. */ -- 2.30.2