This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Re: [PATCH, doc RFA] Add "skip regexp"
- From: Doug Evans <dje at google dot com>
- To: Eli Zaretskii <eliz at gnu dot org>, gdb-patches at sourceware dot org
- Date: Tue, 02 Feb 2016 18:05:34 +0000
- Subject: Re: [PATCH, doc RFA] Add "skip regexp"
- Authentication-results: sourceware.org; auth=none
Eli Zaretskii writes:
> > Date: Tue, 02 Feb 2016 01:03:19 +0000
> > From: Doug Evans <dje@google.com>
> >
> > With this patch one can specify the skip as:
> >
> > skip regexp ^std::(allocator|basic_string)<.*>::~?\1 *\(
>
> Thanks.
>
> > 2016-02-01 Doug Evans <dje@google.com>
> >
> > New command "skip regexp regular-expression".
> > * NEWS: Document the new feature.
> > * skip.c (skip_kind): New enum.
> > (skiplist_entry) <filename,function_name>: Delete.
> > <kind,text,regex,regex_valid>: New members.
> > (skiplist_entry_kind_name): New function.
> > (make_skip_file, make_skip_function, make_skip_regexp): New function.
> > (free_skiplist_entry, free_skiplist_entry_cleanup): New functions.
> > (make_free_skiplist_entry_cleanup): New function.
> > (skip_file_command): Update.
> > (skip_function): Update.
> > (compile_skip_regexp, skip_regexp_command): New functions.
> > (skip_info): Update.
> > (sal_and_fullname): New struct.
> > (skip_file_p, skip_function_p, skip_regexp_p): New functions.
> > (function_name_is_marked_for_skip): Update and simplify.
> > (_initialize_step_skip): Add "skip regexp" command.
> >
> > doc/
> > * gdb.texinfo (Skipping Over Functions and Files): Document
> > "skip regexp".
>
> The documentation parts are approved, with the following nit:
>
> > +Functions may be skipped by providing either a function name, linespec
> > +(@pxref{Specify Location}), file name, or regular expression of the
> > +function's name. ^^^^^^^^^^^^^^^^^^^^^^^^^
> ^^^^^^^^^^^^^^^
> "regular expression that matches the function's name" is more accurate
> (and you also use it elsewhere in the patch).
>
> Otherwise, fine with me, thanks.
Thanks.
I included an older version of the perf testcase in my previous email.
This is an updated patch.
2016-02-02 Doug Evans <dje@google.com>
New command "skip regexp regular-expression".
* NEWS: Document the new feature.
* skip.c (skip_kind): New enum.
(skiplist_entry) <filename,function_name>: Delete.
<kind,text,regex,regex_valid>: New members.
(skiplist_entry_kind_name): New function.
(make_skip_file, make_skip_function, make_skip_regexp): New function.
(free_skiplist_entry, free_skiplist_entry_cleanup): New functions.
(make_free_skiplist_entry_cleanup): New function.
(skip_file_command): Update.
(skip_function): Update.
(compile_skip_regexp, skip_regexp_command): New functions.
(skip_info): Update.
(sal_and_fullname): New struct.
(skip_file_p, skip_function_p, skip_regexp_p): New functions.
(function_name_is_marked_for_skip): Update and simplify.
(_initialize_step_skip): Add "skip regexp" command.
doc/
* gdb.texinfo (Skipping Over Functions and Files): Document
"skip regexp".
testsuite/
* gdb.base/skip.exp: Add tests for "skip regexp".
* gdb.perf/skip-command.cc: New file.
* gdb.perf/skip-command.exp: New file.
* gdb.perf/skip-command.py: New file.
diff --git a/gdb/NEWS b/gdb/NEWS
index 962eae4..388daaa 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -125,6 +125,10 @@ show max-value-size
allocate for value contents. Prevents incorrect programs from
causing GDB to allocate overly large buffers. Default is 64k.
+skip regexp regular-expression
+ A variation of "skip function" where the function name is specified
+ as a regular expression.
+
* 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/gdb.texinfo b/gdb/doc/gdb.texinfo
index 2d09d13..f249e3a 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -5555,8 +5555,9 @@ 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}), file name, or regular expression that matches
+the function's name.
@table @code
@kindex skip function
@@ -5577,8 +5578,42 @@ 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.
+
+@kindex skip regexp
+@item skip regexp @var{regular-expression}
+After running this command, any function whose name matches
+@var{regular-expression} 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++ std::string
+constructors or destructors. Plus with C++ 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.
+
+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.
+
+@smallexample
+(gdb) skip regexp ^std::(allocator|basic_string)<.*>::~?\1 *\(
+@end smallexample
+
+If you wanted to skip every templated C++ constructor and destructor
+in the @code{std} namespace you could do:
+
+@smallexample
+(gdb) skip regexp ^std::([a-zA-z0-9_]+)<.*>::~?\1 *\(
+@end smallexample
@end table
Skips can be listed, deleted, disabled, and enabled, much like breakpoints.
@@ -5595,7 +5630,7 @@ print a table with details about all functions and
files marked for skipping.
@item Identifier
A number identifying this skip.
@item Type
-The type of this skip, either @samp{function} or @samp{file}.
+The type of this skip, either @samp{function}, @samp{file} or
@samp{regexp}.
@item Enabled or Disabled
Enabled skips are marked with @samp{y}. Disabled skips are marked with
@samp{n}.
@item Address
diff --git a/gdb/skip.c b/gdb/skip.c
index d90910d..31e240c 100644
--- a/gdb/skip.c
+++ b/gdb/skip.c
@@ -32,19 +32,31 @@
#include "breakpoint.h" /* for get_sal_arch () */
#include "source.h"
#include "filenames.h"
+#include "gdb_regex.h"
+
+enum skip_kind
+{
+ SKIP_FILE,
+ SKIP_FUNCTION,
+ SKIP_REGEXP
+};
struct skiplist_entry
{
int number;
- /* NULL if this isn't a skiplist entry for an entire file.
+ enum skip_kind kind;
+
+ /* The text provided by the user.
The skiplist entry owns this pointer. */
- char *filename;
+ char *text;
- /* The name of the marked-for-skip function, if this is a skiplist
- entry for a function.
+ /* If this is a regexp, the compiled form.
The skiplist entry owns this pointer. */
- char *function_name;
+ regex_t regex;
+
+ /* Non-zero if regex has been compiled. */
+ int regex_valid;
int enabled;
@@ -65,10 +77,101 @@ static int skiplist_entry_count;
E ? (TMP = E->next, 1) : 0; \
E = TMP)
+/* Return the name of the skiplist entry kind. */
+
+static const char *
+skiplist_entry_kind_name (struct skiplist_entry *e)
+{
+ switch (e->kind)
+ {
+ case SKIP_FILE: return "file";
+ case SKIP_FUNCTION: return "function";
+ case SKIP_REGEXP: return "regexp";
+ default:
+ gdb_assert_not_reached ("bad skiplist_entry kind");
+ }
+}
+
+/* Create a SKIP_FILE object. */
+
+static struct skiplist_entry *
+make_skip_file (const char *name)
+{
+ struct skiplist_entry *e = XCNEW (struct skiplist_entry);
+
+ e->kind = SKIP_FILE;
+ e->text = xstrdup (name);
+ e->enabled = 1;
+
+ return e;
+}
+
+/* Create a SKIP_FUNCTION object. */
+
+static struct skiplist_entry *
+make_skip_function (const char *name)
+{
+ struct skiplist_entry *e = XCNEW (struct skiplist_entry);
+
+ e->kind = SKIP_FUNCTION;
+ e->enabled = 1;
+ e->text = xstrdup (name);
+
+ return e;
+}
+
+/* Create a SKIP_REGEXP object.
+ The regexp is not parsed, the caller must do that afterwards. */
+
+static struct skiplist_entry *
+make_skip_regexp (const char *regexp)
+{
+ struct skiplist_entry *e = XCNEW (struct skiplist_entry);
+
+ e->kind = SKIP_REGEXP;
+ e->enabled = 1;
+ e->text = xstrdup (regexp);
+
+ return e;
+}
+
+/* Free a skiplist entry. */
+
+static void
+free_skiplist_entry (struct skiplist_entry *e)
+{
+ xfree (e->text);
+ switch (e->kind)
+ {
+ case SKIP_REGEXP:
+ if (e->regex_valid)
+ regfree (&e->regex);
+ break;
+ default:
+ break;
+ }
+ 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;
@@ -99,15 +202,22 @@ Ignore file pending future shared library load? ")))
filename = arg;
}
- e = XCNEW (struct skiplist_entry);
- e->filename = xstrdup (filename);
- e->enabled = 1;
-
- add_skiplist_entry (e);
+ add_skiplist_entry (make_skip_file (filename));
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 (const char *name)
+{
+ add_skiplist_entry (make_skip_function (name));
+
+ printf_filtered (_("Function %s will be skipped when stepping.\n"),
name);
+}
+
static void
skip_function_command (char *arg, int from_tty)
{
@@ -149,6 +259,54 @@ Ignore function pending future shared library
load? ")))
}
}
+/* 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->kind == SKIP_REGEXP);
+
+ code = regcomp (&e->regex, e->text, flags);
+ if (code != 0)
+ {
+ char *err = get_regcomp_error (code, &e->regex);
+
+ make_cleanup (xfree, err);
+ error (("%s: %s"), message, err);
+ }
+ e->regex_valid = 1;
+}
+
+static void
+skip_regexp_command (char *arg, int from_tty)
+{
+ struct skiplist_entry *e;
+ struct cleanup *cleanups;
+
+ /* If no argument was given, try to default to the last
+ displayed codepoint. */
+ if (arg == NULL)
+ error (_("Missing regexp."));
+
+ e = make_skip_regexp (arg);
+ cleanups = make_free_skiplist_entry_cleanup (e);
+ compile_skip_regexp (e, _("regexp"));
+ discard_cleanups (cleanups);
+ add_skiplist_entry (e);
+
+ printf_filtered (_("Functions matching regexp %s will be skipped"
+ " when stepping.\n"), arg);
+}
+
static void
skip_info (char *arg, int from_tty)
{
@@ -199,23 +357,15 @@ Not skipping any files or functions.\n"));
"blklst-entry");
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 */
- else
- internal_error (__FILE__, __LINE__, _("\
-Skiplist entry should have either a filename or a function name."));
+ ui_out_field_string (current_uiout, "type",
+ skiplist_entry_kind_name (e)); /* 2 */
if (e->enabled)
ui_out_field_string (current_uiout, "enabled", "y"); /* 3 */
else
ui_out_field_string (current_uiout, "enabled", "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, "what", e->text);
/* 4 */
ui_out_text (current_uiout, "\n");
do_cleanups (entry_chain);
@@ -273,9 +423,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 +435,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 +458,73 @@ add_skiplist_entry (struct skiplist_entry *e)
}
}
+/* The file-based location where we're stopped.
+ This simplifies calling skip_file_p because we only want to call
+ symtab_to_fullname once. */
+
+struct sal_and_fullname
+{
+ const struct symtab_and_line *function_sal;
+ int searched_for_fullname;
+ const char *fullname;
+};
+
+/* Return non-zero if we're stopped at a file to be skipped. */
+
+static int
+skip_file_p (struct skiplist_entry *e, struct sal_and_fullname
*file_location)
+{
+ const struct symtab_and_line *function_sal = file_location->function_sal;
+
+ gdb_assert (e->kind == SKIP_FILE);
+
+ /* 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->text))
+ 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->text)) != 0))
+ return 0;
+
+ /* Get the filename corresponding to this FUNCTION_SAL, if we haven't
+ yet. */
+ if (!file_location->searched_for_fullname)
+ {
+ if (function_sal->symtab != NULL)
+ file_location->fullname = symtab_to_fullname (function_sal->symtab);
+ file_location->searched_for_fullname = 1;
+ }
+ if (file_location->fullname != NULL
+ && compare_filenames_for_search (file_location->fullname, e->text))
+ 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->kind == SKIP_FUNCTION);
+ return strcmp_iw (function_name, e->text) == 0;
+}
+
+/* Return non-zero if we're stopped at a function regexp to be skipped. */
+
+static int
+skip_regexp_p (struct skiplist_entry *e, const char *function_name)
+{
+ gdb_assert (e->kind == SKIP_REGEXP);
+ return regexec (&e->regex, function_name, 0, NULL, 0) == 0;
+}
/* See skip.h. */
@@ -333,8 +532,7 @@ 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 sal_and_fullname file_location = { function_sal, 0, NULL };
struct skiplist_entry *e;
if (function_name == NULL)
@@ -345,39 +543,22 @@ function_name_is_marked_for_skip (const char
*function_name,
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)
+ switch (e->kind)
{
- /* 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))
+ case SKIP_FILE:
+ if (skip_file_p (e, &file_location))
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 (function_sal->symtab != NULL)
- fullname = symtab_to_fullname (function_sal->symtab);
- searched_for_fullname = 1;
- }
- if (fullname != NULL
- && compare_filenames_for_search (fullname, e->filename))
+ break;
+ case SKIP_FUNCTION:
+ if (skip_function_p (e, function_name))
return 1;
+ break;
+ case SKIP_REGEXP:
+ if (skip_regexp_p (e, function_name))
+ return 1;
+ break;
+ default:
+ gdb_assert_not_reached ("bad skiplist_entry kind");
}
}
@@ -416,6 +597,12 @@ If no function name is given, skip the current
function."),
&skiplist);
set_cmd_completer (c, location_completer);
+ c = add_cmd ("regexp", class_breakpoint,
+ skip_regexp_command, _("\
+Ignore a function while stepping.\n\
+Usage: skip regexp [FUNCTION-NAME-REGEXP]."),
+ &skiplist);
+
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\
diff --git a/gdb/testsuite/gdb.base/skip.exp
b/gdb/testsuite/gdb.base/skip.exp
index 9fa4acf..cb88829 100644
--- a/gdb/testsuite/gdb.base/skip.exp
+++ b/gdb/testsuite/gdb.base/skip.exp
@@ -31,6 +31,11 @@ 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)"
+#
+# Regexps don't have a default, but we can still test an elided arg.
+#
+gdb_test "skip regexp" "Missing regexp."
+
if ![runto_main] { fail "skip tests suppressed" }
#
@@ -51,6 +56,12 @@ gdb_test "skip file skip1.c" "File .*$srcfile1 will be
skipped when stepping\."
gdb_test "skip function baz" "Function baz will be skipped when stepping\."
#
+# Create a regexp of skipping baz, disabled until we need it so as to not
+# interfere with "skip function baz"
+gdb_test "skip regexp ^b.z$" "Functions matching regexp \\^b\\.z\\$ will
be skipped when stepping."
+gdb_test "skip disable 5"
+
+#
# Test bad skiplist entry modification commands
#
gdb_test "skip enable 999" "No skiplist entries found with number 999."
@@ -73,7 +84,8 @@ gdb_test "info skip" \
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*"
+4\\s+function\\s+y\\s+baz\\s*
+5\\s+regexp\\s+n\\s+\\^b\\.z\\$\\s*"
#
# Right now, we have an outstanding skiplist entry on both source
@@ -96,7 +108,8 @@ 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*" \
+4\\s+function\\s+y\\s+baz\\s*
+5\\s+regexp\\s+n\\s+\\^b\\.z\\$\\s*" \
"info skip (delete 1)"
if ![runto_main] { fail "skip tests suppressed" }
@@ -123,6 +136,22 @@ gdb_test "step" ".*" "$test (4)"; # Return from bar()
gdb_test "step" "main \\(\\) at.*" "$test (5)"
#
+# Repeat, but replace "skip function baz" (#4) with its regexp (#5).
+#
+gdb_test "skip disable 4"
+gdb_test "skip enable 5"
+if ![runto_main] { fail "skip tests suppressed" }
+set test "step using regexp for baz"
+gdb_test "step" "bar \\(\\) at.*" "$test (1)"
+gdb_test "step" ".*" "$test (2)"; # Return from foo()
+gdb_test "step" "foo \\(\\) at.*" "$test (3)"
+gdb_test "step" ".*" "$test (4)"; # Return from bar()
+gdb_test "step" "main \\(\\) at.*" "$test (5)"
+# Restore.
+gdb_test "skip enable 4"
+gdb_test "skip disable 5"
+
+#
# Enable skiplist entry 3 and make sure we step over it like before.
#
gdb_test "skip enable 3"
@@ -140,7 +169,8 @@ 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*" \
+4\\s+function\\s+n\\s+baz\\s*
+5\\s+regexp\\s+n\\s+\\^b\\.z\\$\\s*" \
"info skip after disabling all"
gdb_test "skip enable"
@@ -148,7 +178,8 @@ 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*" \
+4\\s+function\\s+y\\s+baz\\s*
+5\\s+regexp\\s+y\\s+\\^b\\.z\\$\\s*" \
"info skip after enabling all"
gdb_test "skip disable 4 2-3"
@@ -156,7 +187,8 @@ 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*" \
+4\\s+function\\s+n\\s+baz\\s*
+5\\s+regexp\\s+y\\s+\\^b\\.z\\$\\s*" \
"info skip after disabling 4 2-3"
gdb_test "skip enable 2-3"
@@ -164,7 +196,8 @@ 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*" \
+4\\s+function\\s+n\\s+baz\\s*
+5\\s+regexp\\s+y\\s+\\^b\\.z\\$\\s*" \
"info skip after enabling 2-3"
gdb_test "info skip 2-3" \
@@ -173,7 +206,7 @@ gdb_test "info skip 2-3" \
3\\s+file\\s+y\\s+$srcfile1\\s*" \
"info skip 2-3"
-gdb_test "skip delete 2 3"
+gdb_test "skip delete 2 3 5"
gdb_test "info skip" \
"4\\s+function\\s+n\\s+baz\\s*" \
"info skip after deleting 2 3"
diff --git a/gdb/testsuite/gdb.perf/skip-command.cc
b/gdb/testsuite/gdb.perf/skip-command.cc
new file mode 100644
index 0000000..5820ef7
--- /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 <http://www.gnu.org/licenses/>.
*/
+
+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 0000000..2a6fc81
--- /dev/null
+++ b/gdb/testsuite/gdb.perf/skip-command.exp
@@ -0,0 +1,130 @@
+# 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 <http://www.gnu.org/licenses/>.
+
+# 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
+ for { set i 0 } { $i < $nr_skips } { incr i } {
+ gdb_test "skip $kind [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 0000000..12c28f2
--- /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 <http://www.gnu.org/licenses/>.
+
+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)