X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Fskip.c;h=127b11dc4431b4d8fd192db8345f31cdcee83b4e;hb=7d8062de98203eeec70d4439ab460b9ef50a2e0f;hp=4bda3c42b5bc5fe1d1d66b4f7a3ae3319f99a1fa;hpb=1bfeeb0f75817b38f7ca569b347cce0873754985;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/skip.c b/gdb/skip.c index 4bda3c42b5..127b11dc44 100644 --- a/gdb/skip.c +++ b/gdb/skip.c @@ -1,6 +1,6 @@ /* Skipping uninteresting files and functions while stepping. - Copyright (C) 2011 Free Software Foundation, Inc. + Copyright (C) 2011-2019 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 @@ -20,7 +20,6 @@ #include "value.h" #include "valprint.h" #include "ui-out.h" -#include "gdb_string.h" #include "symtab.h" #include "gdbcmd.h" #include "command.h" @@ -30,317 +29,416 @@ #include "arch-utils.h" #include "linespec.h" #include "objfiles.h" -#include "exceptions.h" #include "breakpoint.h" /* for get_sal_arch () */ - -struct skiplist_entry +#include "source.h" +#include "filenames.h" +#include "fnmatch.h" +#include "gdb_regex.h" +#include "common/gdb_optional.h" +#include + +/* True if we want to print debug printouts related to file/function + skipping. */ +static int debug_skip = 0; + +class skiplist_entry { - int number; - - /* NULL if this isn't a skiplist entry for an entire file. - The skiplist entry owns this pointer. */ - char *filename; - - /* The name of the marked-for-skip function, if this is a skiplist - entry for a function. Note that this might be non-null even if - the pc is 0 if the entry is pending a shared library load. - - The skiplist entry owns this pointer. */ - char *function_name; - - /* 0 if this is a skiplist entry for an entire file, or if this - entry will be on a function, pending a shared library load. */ - CORE_ADDR pc; - - /* Architecture we used to create the skiplist entry. May be null - if the entry is pending a shared library load. */ - struct gdbarch *gdbarch; - - int enabled; - int pending; - - struct skiplist_entry *next; +public: + /* Create a skiplist_entry object and add it to the chain. */ + static void add_entry (bool file_is_glob, + std::string &&file, + bool function_is_regexp, + std::string &&function); + + /* Return true if the skip entry has a file or glob-style file + pattern that matches FUNCTION_SAL. */ + bool skip_file_p (const symtab_and_line &function_sal) const; + + /* Return true if the skip entry has a function or function regexp + that matches FUNCTION_NAME. */ + bool skip_function_p (const char *function_name) const; + + /* Getters. */ + int number () const { return m_number; }; + bool enabled () const { return m_enabled; }; + bool file_is_glob () const { return m_file_is_glob; } + const std::string &file () const { return m_file; } + const std::string &function () const { return m_function; } + bool function_is_regexp () const { return m_function_is_regexp; } + + /* Setters. */ + void enable () { m_enabled = true; }; + void disable () { m_enabled = false; }; + + /* Disable copy. */ + skiplist_entry (const skiplist_entry &) = delete; + void operator= (const skiplist_entry &) = delete; + +private: + /* Key that grants access to the constructor. */ + struct private_key {}; +public: + /* Public so we can construct with container::emplace_back. Since + it requires a private class key, it can't be called from outside. + Use the add_entry static factory method to construct instead. */ + skiplist_entry (bool file_is_glob, std::string &&file, + bool function_is_regexp, std::string &&function, + private_key); + +private: + /* Return true if we're stopped at a file to be skipped. */ + bool do_skip_file_p (const symtab_and_line &function_sal) const; + + /* Return true if we're stopped at a globbed file to be skipped. */ + bool do_skip_gfile_p (const symtab_and_line &function_sal) const; + +private: /* data */ + int m_number = -1; + + /* True if FILE is a glob-style pattern. + Otherwise it is the plain file name (possibly with directories). */ + bool m_file_is_glob; + + /* The name of the file or empty if no name. */ + std::string m_file; + + /* True if FUNCTION is a regexp. + Otherwise it is a plain function name (possibly with arguments, + for C++). */ + bool m_function_is_regexp; + + /* The name of the function or empty if no name. */ + std::string m_function; + + /* If this is a function regexp, the compiled form. */ + gdb::optional m_compiled_function_regexp; + + /* Enabled/disabled state. */ + bool m_enabled = true; }; -static void skip_function_command (char *arg, int from_tty); -static void skip_file_command (char *arg, int from_tty); -static void skip_info (char *arg, int from_tty); +static std::list skiplist_entries; +static int highest_skiplist_entry_num = 0; + +skiplist_entry::skiplist_entry (bool file_is_glob, + std::string &&file, + bool function_is_regexp, + std::string &&function, + private_key) + : m_file_is_glob (file_is_glob), + m_file (std::move (file)), + m_function_is_regexp (function_is_regexp), + m_function (std::move (function)) +{ + gdb_assert (!m_file.empty () || !m_function.empty ()); + + if (m_file_is_glob) + gdb_assert (!m_file.empty ()); -static void add_skiplist_entry (struct skiplist_entry *e); -static void skip_function_pc (CORE_ADDR pc, char *name, - struct gdbarch *arch, - int pending); + if (m_function_is_regexp) + { + gdb_assert (!m_function.empty ()); -static struct skiplist_entry *skiplist_entry_chain; -static int skiplist_entry_count; + int flags = REG_NOSUB; +#ifdef REG_EXTENDED + flags |= REG_EXTENDED; +#endif -#define ALL_SKIPLIST_ENTRIES(E) \ - for (E = skiplist_entry_chain; E; E = E->next) + gdb_assert (!m_function.empty ()); + m_compiled_function_regexp.emplace (m_function.c_str (), flags, + _("regexp")); + } +} -#define ALL_SKIPLIST_ENTRIES_SAFE(E,TMP) \ - for (E = skiplist_entry_chain; \ - E ? (TMP = E->next, 1) : 0; \ - E = TMP) +void +skiplist_entry::add_entry (bool file_is_glob, std::string &&file, + bool function_is_regexp, std::string &&function) +{ + skiplist_entries.emplace_back (file_is_glob, + std::move (file), + function_is_regexp, + std::move (function), + private_key {}); + + /* Incremented after push_back, in case push_back throws. */ + skiplist_entries.back ().m_number = ++highest_skiplist_entry_num; +} static void -skip_file_command (char *arg, int from_tty) +skip_file_command (const char *arg, int from_tty) { - struct skiplist_entry *e; struct symtab *symtab; - int pending = 0; - char *filename = 0; + const char *filename = NULL; /* If no argument was given, try to default to the last displayed codepoint. */ - if (arg == 0) + if (arg == NULL) { symtab = get_last_displayed_symtab (); - if (symtab == 0) + if (symtab == NULL) error (_("No default file now.")); - else - filename = symtab->filename; - } - else - { - symtab = lookup_symtab (arg); - if (symtab == 0) - { - fprintf_filtered (gdb_stderr, _("No source file named %s.\n"), arg); - if (!nquery (_("\ -Ignore file pending future shared library load? "))) - return; - pending = 1; - filename = arg; - } - else - filename = symtab->filename; + /* It is not a typo, symtab_to_filename_for_display woule be needlessly + ambiguous. */ + filename = symtab_to_fullname (symtab); } + else + filename = arg; - e = XZALLOC (struct skiplist_entry); - e->filename = xstrdup (filename); - e->enabled = 1; - e->pending = pending; - if (symtab != 0) - e->gdbarch = get_objfile_arch (symtab->objfile); - - add_skiplist_entry (e); + skiplist_entry::add_entry (false, std::string (filename), + false, std::string ()); 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) { - CORE_ADDR func_pc; - char *name = NULL; + skiplist_entry::add_entry (false, std::string (), false, std::string (name)); + + printf_filtered (_("Function %s will be skipped when stepping.\n"), name); +} +static void +skip_function_command (const char *arg, int from_tty) +{ /* Default to the current function if no argument is given. */ - if (arg == 0) + if (arg == NULL) { + const char *name = NULL; CORE_ADDR pc; + if (!last_displayed_sal_is_valid ()) error (_("No default function now.")); pc = get_last_displayed_addr (); - if (!find_pc_partial_function (pc, &name, &func_pc, 0)) + if (!find_pc_partial_function (pc, &name, NULL, NULL)) { error (_("No function found containing current program point %s."), paddress (get_current_arch (), pc)); } - skip_function_pc (func_pc, name, get_current_arch (), 0); + skip_function (name); + return; } - else - { - /* Decode arg. We set funfirstline=1 so decode_line_1 will give us the - first line of the function specified, if it can, and so that we'll - reject variable names and the like. */ - int i; - int pending = 0; - char *orig_arg = arg; /* decode_line_1 modifies the arg pointer. */ - volatile struct gdb_exception decode_exception; - struct symtabs_and_lines sals; + skip_function (arg); +} - TRY_CATCH (decode_exception, RETURN_MASK_ERROR) - { - sals = decode_line_1 (&arg, 1, 0, 0, 0); - } +/* Process "skip ..." that does not match "skip file" or "skip function". */ - if (decode_exception.reason < 0) - { - if (decode_exception.error != NOT_FOUND_ERROR) - throw_exception (decode_exception); +static void +skip_command (const char *arg, int from_tty) +{ + const char *file = NULL; + const char *gfile = NULL; + const char *function = NULL; + const char *rfunction = NULL; + int i; - fprintf_filtered (gdb_stderr, - _("No function found named %s.\n"), orig_arg); + if (arg == NULL) + { + skip_function_command (arg, from_tty); + return; + } - if (nquery (_("\ -Ignore function pending future shared library load? "))) - { - /* Add the pending skiplist entry. */ - skip_function_pc (0, orig_arg, 0, 1); - } + gdb_argv argv (arg); + 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". */ + skip_function_command (arg, from_tty); return; } + else + error (_("Invalid argument: %s"), p); + } - if (sals.nelts > 1) - error (_("Specify just one function at a time.")); - if (strlen (arg) != 0) - error (_("Junk at end of arguments.")); - - /* The pc decode_line_1 gives us is the first line of the function, - but we actually want the line before that. The call to - find_pc_partial_function gets us the value we actually want. */ + 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); + + std::string entry_file; + if (file != NULL) + entry_file = file; + else if (gfile != NULL) + entry_file = gfile; + + std::string entry_function; + if (function != NULL) + entry_function = function; + else if (rfunction != NULL) + entry_function = rfunction; + + skiplist_entry::add_entry (gfile != NULL, std::move (entry_file), + rfunction != NULL, std::move (entry_function)); + + /* 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) { - struct symtab_and_line sal = sals.sals[0]; - CORE_ADDR pc = sal.pc; - CORE_ADDR func_start = 0; - struct gdbarch *arch = get_sal_arch (sal); - - if (!find_pc_partial_function (pc, &name, &func_start, 0)) - { - error (_("No function found containing program point %s."), - paddress (arch, pc)); - } - - skip_function_pc (func_start, name, arch, 0); + 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); + } + } } static void -skip_info (char *arg, int from_tty) +info_skip_command (const char *arg, int from_tty) { - struct skiplist_entry *e; int num_printable_entries = 0; - int address_width = 10; struct value_print_options opts; - struct cleanup *tbl_chain; get_user_print_options (&opts); /* Count the number of rows in the table and see if we need space for a 64-bit address anywhere. */ - ALL_SKIPLIST_ENTRIES (e) - if (arg == 0 || number_is_in_list (arg, e->number)) - { - num_printable_entries++; - if (e->gdbarch && gdbarch_addr_bit (e->gdbarch) > 32) - address_width = 18; - } + for (const skiplist_entry &e : skiplist_entries) + if (arg == NULL || number_is_in_list (arg, e.number ())) + num_printable_entries++; if (num_printable_entries == 0) { - if (arg == 0) - ui_out_message (current_uiout, 0, _("\ -Not skipping any files or functions.\n")); + if (arg == NULL) + current_uiout->message (_("Not skipping any files or functions.\n")); else - ui_out_message (current_uiout, 0, - _("No skiplist entries found with number %s.\n"), arg); + current_uiout->message ( + _("No skiplist entries found with number %s.\n"), arg); return; } - if (opts.addressprint) - tbl_chain = make_cleanup_ui_out_table_begin_end (current_uiout, 5, - num_printable_entries, - "SkiplistTable"); - else - tbl_chain - = make_cleanup_ui_out_table_begin_end (current_uiout, 4, - 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 */ - if (opts.addressprint) - { - ui_out_table_header (current_uiout, address_width, ui_left, - "addr", "Address"); /* 4 */ - } - ui_out_table_header (current_uiout, 40, ui_noalign, "what", "What"); /* 5 */ - ui_out_table_body (current_uiout); + ui_out_emit_table table_emitter (current_uiout, 6, num_printable_entries, + "SkiplistTable"); - ALL_SKIPLIST_ENTRIES (e) - { - struct cleanup *entry_chain; + current_uiout->table_header (5, ui_left, "number", "Num"); /* 1 */ + current_uiout->table_header (3, ui_left, "enabled", "Enb"); /* 2 */ + current_uiout->table_header (4, ui_right, "regexp", "Glob"); /* 3 */ + current_uiout->table_header (20, ui_left, "file", "File"); /* 4 */ + current_uiout->table_header (2, ui_right, "regexp", "RE"); /* 5 */ + current_uiout->table_header (40, ui_noalign, "function", "Function"); /* 6 */ + current_uiout->table_body (); + for (const skiplist_entry &e : skiplist_entries) + { QUIT; - if (arg != 0 && !number_is_in_list (arg, e->number)) + if (arg != NULL && !number_is_in_list (arg, e.number ())) continue; - 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_emit_tuple tuple_emitter (current_uiout, "blklst-entry"); + current_uiout->field_int ("number", e.number ()); /* 1 */ - if (e->function_name != 0) - ui_out_field_string (current_uiout, "type", "function"); /* 2 */ - else if (e->filename != 0) - ui_out_field_string (current_uiout, "type", "file"); /* 2 */ + if (e.enabled ()) + current_uiout->field_string ("enabled", "y"); /* 2 */ else - internal_error (__FILE__, __LINE__, _("\ -Skiplist entry should have either a filename or a function name.")); + current_uiout->field_string ("enabled", "n"); /* 2 */ - if (e->enabled) - ui_out_field_string (current_uiout, "enabled", "y"); /* 3 */ + if (e.file_is_glob ()) + current_uiout->field_string ("regexp", "y"); /* 3 */ else - ui_out_field_string (current_uiout, "enabled", "n"); /* 3 */ + current_uiout->field_string ("regexp", "n"); /* 3 */ + + current_uiout->field_string ("file", + e.file ().empty () ? "" + : e.file ().c_str (), + ui_out_style_kind::FILE); /* 4 */ + if (e.function_is_regexp ()) + current_uiout->field_string ("regexp", "y"); /* 5 */ + else + current_uiout->field_string ("regexp", "n"); /* 5 */ - if (opts.addressprint) - { - if (e->pc != 0) - ui_out_field_core_addr (current_uiout, "addr", - e->gdbarch, e->pc); /* 4 */ - else - ui_out_field_string (current_uiout, "addr", ""); /* 4 */ - } + current_uiout->field_string ("function", + e.function ().empty () ? "" + : e.function ().c_str (), + ui_out_style_kind::FUNCTION); /* 6 */ - if (!e->pending && e->function_name != 0) - { - struct symbol *sym; - - gdb_assert (e->pc != 0); - sym = find_pc_function (e->pc); - if (sym) - ui_out_field_fmt (current_uiout, "what", "%s at %s:%d", - sym->ginfo.name, - sym->symtab->filename, - sym->line); - else - ui_out_field_string (current_uiout, "what", "?"); - } - else if (e->pending && e->function_name != 0) - { - ui_out_field_fmt (current_uiout, "what", "%s (PENDING)", - e->function_name); - } - else if (!e->pending && e->filename != 0) - ui_out_field_string (current_uiout, "what", e->filename); - else if (e->pending && e->filename != 0) - ui_out_field_fmt (current_uiout, "what", "%s (PENDING)", - e->filename); - - ui_out_text (current_uiout, "\n"); - do_cleanups (entry_chain); + current_uiout->text ("\n"); } - - do_cleanups (tbl_chain); } static void -skip_enable_command (char *arg, int from_tty) +skip_enable_command (const char *arg, int from_tty) { - struct skiplist_entry *e; - int found = 0; + bool found = false; - ALL_SKIPLIST_ENTRIES (e) - if (arg == 0 || number_is_in_list (arg, e->number)) + for (skiplist_entry &e : skiplist_entries) + if (arg == NULL || number_is_in_list (arg, e.number ())) { - e->enabled = 1; - found = 1; + e.enable (); + found = true; } if (!found) @@ -348,16 +446,15 @@ skip_enable_command (char *arg, int from_tty) } static void -skip_disable_command (char *arg, int from_tty) +skip_disable_command (const char *arg, int from_tty) { - struct skiplist_entry *e; - int found = 0; + bool found = false; - ALL_SKIPLIST_ENTRIES (e) - if (arg == 0 || number_is_in_list (arg, e->number)) + for (skiplist_entry &e : skiplist_entries) + if (arg == NULL || number_is_in_list (arg, e.number ())) { - e->enabled = 0; - found = 1; + e.disable (); + found = true; } if (!found) @@ -365,238 +462,277 @@ skip_disable_command (char *arg, int from_tty) } static void -skip_delete_command (char *arg, int from_tty) +skip_delete_command (const char *arg, int from_tty) { - struct skiplist_entry *e, *temp, *b_prev; - int found = 0; + bool found = false; - b_prev = 0; - ALL_SKIPLIST_ENTRIES_SAFE (e, temp) - if (arg == 0 || number_is_in_list (arg, e->number)) - { - if (b_prev != 0) - b_prev->next = e->next; - else - skiplist_entry_chain = e->next; - - xfree (e->function_name); - xfree (e->filename); - xfree (e); - found = 1; - } - else - { - b_prev = e; - } + for (auto it = skiplist_entries.begin (), + end = skiplist_entries.end (); + it != end;) + { + const skiplist_entry &e = *it; + + if (arg == NULL || number_is_in_list (arg, e.number ())) + { + it = skiplist_entries.erase (it); + found = true; + } + else + ++it; + } if (!found) error (_("No skiplist entries found with number %s."), arg); } -/* Create a skiplist entry for the given pc corresponding to the given - function name and add it to the list. */ - -static void -skip_function_pc (CORE_ADDR pc, char *name, struct gdbarch *arch, - int pending) +bool +skiplist_entry::do_skip_file_p (const symtab_and_line &function_sal) const { - struct skiplist_entry *e = XZALLOC (struct skiplist_entry); + if (debug_skip) + fprintf_unfiltered (gdb_stdlog, + "skip: checking if file %s matches non-glob %s...", + function_sal.symtab->filename, m_file.c_str ()); + + bool result; + + /* 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, + m_file.c_str ())) + result = true; + + /* Before we invoke realpath, which can get expensive when many + files are involved, do a quick comparison of the basenames. */ + else if (!basenames_may_differ + && filename_cmp (lbasename (function_sal.symtab->filename), + lbasename (m_file.c_str ())) != 0) + result = false; + else + { + /* Note: symtab_to_fullname caches its result, thus we don't have to. */ + const char *fullname = symtab_to_fullname (function_sal.symtab); - e->pc = pc; - e->gdbarch = arch; - e->enabled = 1; - e->pending = pending; - e->function_name = xstrdup (name); + result = compare_filenames_for_search (fullname, m_file.c_str ()); + } - add_skiplist_entry (e); + if (debug_skip) + fprintf_unfiltered (gdb_stdlog, result ? "yes.\n" : "no.\n"); - if (!pending) - printf_filtered (_("Function %s at %s will be skipped when stepping.\n"), - name, paddress (get_current_arch (), pc)); - else - printf_filtered (_("Function %s will be skipped when stepping, " - "pending shared library load.\n"), - name); + return result; } -/* Add the given skiplist entry to our list, and set the entry's number. */ +bool +skiplist_entry::do_skip_gfile_p (const symtab_and_line &function_sal) const +{ + if (debug_skip) + fprintf_unfiltered (gdb_stdlog, + "skip: checking if file %s matches glob %s...", + function_sal.symtab->filename, m_file.c_str ()); + + bool result; + + /* Check first sole SYMTAB->FILENAME. It may not be a substring of + symtab_to_fullname as it may contain "./" etc. */ + if (gdb_filename_fnmatch (m_file.c_str (), function_sal.symtab->filename, + FNM_FILE_NAME | FNM_NOESCAPE) == 0) + result = true; + + /* 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. */ + else if (!basenames_may_differ + && gdb_filename_fnmatch (lbasename (m_file.c_str ()), + lbasename (function_sal.symtab->filename), + FNM_FILE_NAME | FNM_NOESCAPE) != 0) + result = false; + else + { + /* Note: symtab_to_fullname caches its result, thus we don't have to. */ + const char *fullname = symtab_to_fullname (function_sal.symtab); -static void -add_skiplist_entry (struct skiplist_entry *e) + result = compare_glob_filenames_for_search (fullname, m_file.c_str ()); + } + + if (debug_skip) + fprintf_unfiltered (gdb_stdlog, result ? "yes.\n" : "no.\n"); + + return result; +} + +bool +skiplist_entry::skip_file_p (const symtab_and_line &function_sal) const { - struct skiplist_entry *e1; + if (m_file.empty ()) + return false; + + if (function_sal.symtab == NULL) + return false; + + if (m_file_is_glob) + return do_skip_gfile_p (function_sal); + else + return do_skip_file_p (function_sal); +} - e->number = ++skiplist_entry_count; +bool +skiplist_entry::skip_function_p (const char *function_name) const +{ + if (m_function.empty ()) + return false; - /* Add to the end of the chain so that the list of - skiplist entries will be in numerical order. */ + bool result; - e1 = skiplist_entry_chain; - if (e1 == 0) - skiplist_entry_chain = e; + if (m_function_is_regexp) + { + if (debug_skip) + fprintf_unfiltered (gdb_stdlog, + "skip: checking if function %s matches regex %s...", + function_name, m_function.c_str ()); + + gdb_assert (m_compiled_function_regexp); + result + = (m_compiled_function_regexp->exec (function_name, 0, NULL, 0) == 0); + } else { - while (e1->next) - e1 = e1->next; - e1->next = e; + if (debug_skip) + fprintf_unfiltered (gdb_stdlog, + ("skip: checking if function %s matches non-regex " + "%s..."), + function_name, m_function.c_str ()); + result = (strcmp_iw (function_name, m_function.c_str ()) == 0); } + + if (debug_skip) + fprintf_unfiltered (gdb_stdlog, result ? "yes.\n" : "no.\n"); + + return result; } -/* Does the given pc correspond to the beginning of a skipped function? */ +/* See skip.h. */ -int -function_pc_is_marked_for_skip (CORE_ADDR pc) +bool +function_name_is_marked_for_skip (const char *function_name, + const symtab_and_line &function_sal) { - int searched_for_sal = 0; - struct symtab_and_line sal; - char *filename = NULL; - struct skiplist_entry *e; + if (function_name == NULL) + return false; - ALL_SKIPLIST_ENTRIES (e) + for (const skiplist_entry &e : skiplist_entries) { - if (!e->enabled || e->pending) + if (!e.enabled ()) continue; - /* Does the pc we're stepping into match e's stored pc? */ - if (e->pc != 0 && pc == e->pc) - return 1; + bool skip_by_file = e.skip_file_p (function_sal); + bool skip_by_function = e.skip_function_p (function_name); - if (e->filename != 0) + /* If both file and function must match, make sure we don't errantly + exit if only one of them match. */ + if (!e.file ().empty () && !e.function ().empty ()) { - /* Get the filename corresponding to this pc, if we haven't - * yet. */ - if (!searched_for_sal) - { - sal = find_pc_line (pc, 0); - if (sal.symtab != 0) - filename = sal.symtab->filename; - searched_for_sal = 1; - } - if (filename != 0 && strcmp (filename, e->filename) == 0) - return 1; + if (skip_by_file && skip_by_function) + return true; } + /* Only one of file/function is specified. */ + else if (skip_by_file || skip_by_function) + return true; } - return 0; + return false; } -/* Re-set the skip list after symbols have been re-loaded. */ -void -skip_re_set (void) +/* Completer for skip numbers. */ + +static void +complete_skip_number (cmd_list_element *cmd, + completion_tracker &completer, + const char *text, const char *word) { - struct skiplist_entry *e; + size_t word_len = strlen (word); - ALL_SKIPLIST_ENTRIES (e) + for (const skiplist_entry &entry : skiplist_entries) { - if (e->filename != 0) - { - /* If it's an entry telling us to skip a file, but the entry is - currently pending a solib load, let's see if we now know - about the file. */ - struct symtab *symtab = lookup_symtab (e->filename); - if (symtab != 0) - { - xfree (e->filename); - e->filename = xstrdup (symtab->filename); - e->gdbarch = get_objfile_arch (symtab->objfile); - e->pending = 0; - } - else - { - e->pending = 1; - } - } - else if (e->function_name != 0) - { - char *func_name = e->function_name; - struct symtabs_and_lines sals; - volatile struct gdb_exception decode_exception; - - TRY_CATCH (decode_exception, RETURN_MASK_ERROR) - { - sals = decode_line_1 (&func_name, 1, 0, 0, 0); - } - - if (decode_exception.reason >= 0 - && sals.nelts == 1 && strlen (func_name) == 0) - { - struct symtab_and_line sal = sals.sals[0]; - CORE_ADDR pc = sal.pc; - CORE_ADDR func_start = 0; - struct gdbarch *arch = get_sal_arch (sal); - char *func_name; - - if (find_pc_partial_function (pc, &func_name, &func_start, 0)) - { - e->pending = 0; - e->function_name = xstrdup (func_name); - e->pc = func_start; - e->gdbarch = arch; - } - } - else - { - e->pending = 1; - } - } + gdb::unique_xmalloc_ptr name (xstrprintf ("%d", entry.number ())); + if (strncmp (word, name.get (), word_len) == 0) + completer.add_completion (std::move (name)); } } void _initialize_step_skip (void) { + static struct cmd_list_element *skiplist = NULL; struct cmd_list_element *c; - 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); - add_cmd ("enable", class_breakpoint, skip_enable_command, _("\ + c = add_cmd ("enable", class_breakpoint, skip_enable_command, _("\ Enable skip entries. You can specify numbers (e.g. \"skip enable 1 3\"), \ ranges (e.g. \"skip enable 4-8\"), or both (e.g. \"skip enable 1 3 4-8\").\n\n\ If you don't specify any numbers or ranges, we'll enable all skip entries.\n\n\ -Usage: skip enable [NUMBERS AND/OR RANGES]"), - &skiplist); +Usage: skip enable [NUMBER | RANGE]..."), + &skiplist); + set_cmd_completer (c, complete_skip_number); - add_cmd ("disable", class_breakpoint, skip_disable_command, _("\ + c = add_cmd ("disable", class_breakpoint, skip_disable_command, _("\ Disable skip entries. You can specify numbers (e.g. \"skip disable 1 3\"), \ ranges (e.g. \"skip disable 4-8\"), or both (e.g. \"skip disable 1 3 4-8\").\n\n\ If you don't specify any numbers or ranges, we'll disable all skip entries.\n\n\ -Usage: skip disable [NUMBERS AND/OR RANGES]"), - &skiplist); +Usage: skip disable [NUMBER | RANGE]..."), + &skiplist); + set_cmd_completer (c, complete_skip_number); - add_cmd ("delete", class_breakpoint, skip_delete_command, _("\ + c = add_cmd ("delete", class_breakpoint, skip_delete_command, _("\ Delete skip entries. You can specify numbers (e.g. \"skip delete 1 3\"), \ ranges (e.g. \"skip delete 4-8\"), or both (e.g. \"skip delete 1 3 4-8\").\n\n\ If you don't specify any numbers or ranges, we'll delete all skip entries.\n\n\ -Usage: skip delete [NUMBERS AND/OR RANGES]"), - &skiplist); +Usage: skip delete [NUMBER | RANGES]..."), + &skiplist); + set_cmd_completer (c, complete_skip_number); - add_info ("skip", skip_info, _("\ -Display the status of skips. You can specify numbers (e.g. \"skip info 1 3\"), \ -ranges (e.g. \"skip info 4-8\"), or both (e.g. \"skip info 1 3 4-8\").\n\n\ + add_info ("skip", info_skip_command, _("\ +Display the status of skips. You can specify numbers (e.g. \"info skip 1 3\"), \ +ranges (e.g. \"info skip 4-8\"), or both (e.g. \"info skip 1 3 4-8\").\n\n\ If you don't specify any numbers or ranges, we'll show all skips.\n\n\ -Usage: skip info [NUMBERS AND/OR RANGES]\n\ -The \"Type\" column indicates one of:\n\ -\tfile - ignored file\n\ -\tfunction - ignored function")); +Usage: info skip [NUMBER | RANGES]...")); + set_cmd_completer (c, complete_skip_number); + + add_setshow_boolean_cmd ("skip", class_maintenance, + &debug_skip, _("\ +Set whether to print the debug output about skipping files and functions."), + _("\ +Show whether the debug output about skipping files and functions is printed"), + _("\ +When non-zero, debug output about skipping files and functions is displayed."), + NULL, NULL, + &setdebuglist, &showdebuglist); }