diff options
author | Doug Evans <dje@google.com> | 2016-02-23 13:25:18 -0800 |
---|---|---|
committer | Doug Evans <dje@google.com> | 2016-02-23 13:25:18 -0800 |
commit | cce0e92333b872cfe036aae611b6b5d61cf58186 (patch) | |
tree | 8d6534f65086b7fefee20c5ce8b33b8ad877e676 /gdb/skip.c | |
parent | 742e5034ef645112e4ef204e84e28cf312c1b1c5 (diff) | |
download | binutils-cce0e92333b872cfe036aae611b6b5d61cf58186.zip binutils-cce0e92333b872cfe036aae611b6b5d61cf58186.tar.gz binutils-cce0e92333b872cfe036aae611b6b5d61cf58186.tar.bz2 |
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) <file>: Renamed from filename.
<function>: Renamed from function_name.
<file_is_glob, function_is_regexp>: New members.
<compiled_function_regexp, compiled_function_regexp_is_valid>:
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.
Diffstat (limited to 'gdb/skip.c')
-rw-r--r-- | gdb/skip.c | 523 |
1 files changed, 410 insertions, 113 deletions
@@ -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 : "<none>"); /* 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 : "<none>"); /* 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 [<file-spec>] [<function-spec>]\n\ +If no arguments are given, ignore the current function.\n\ +\n\ +<file-spec> is one of:\n\ + -fi|-file FILE-NAME\n\ + -gfi|-gfile GLOB-FILE-PATTERN\n\ +<function-spec> 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); |