diff mbox

[doc,RFA] Add "skip regexp"

Message ID 94eb2c0b86103073b2052abf11e5@google.com
State New
Headers show

Commit Message

Doug Evans Feb. 2, 2016, 1:03 a.m. UTC
Hi.

The "skip" command is great, but it can be really cumbersome to use with  
c++.

E.g., consider stepping into functions that take std::string arguments.

   foo (bar ("abc"));

where bar takes a std::string argument.

There are four functions you generally always want to skip in this case
(cut-n-pasted from my gdb session):

std::allocator<char>::allocator (this=0x7fffffffe6bf)
std::basic_string<char, std::char_traits<char>, std::allocator<char>  
>::basic_string (this=0x7fffffffe6b0, __s=0x400ad1 "abc", __a=...)
std::basic_string<char, std::char_traits<char>, std::allocator<char>  
>::~basic_string (this=0x7fffffffe6b0, __in_chrg=<optimized out>)
std::allocator<char>::~allocator (this=0x7fffffffe6bf, __in_chrg=<optimized  
out>)

With this patch one can specify the skip as:

skip regexp ^std::(allocator|basic_string)<.*>::~?\1 *\(

Skipping every templated class constructor/destructor in std
could be specified as:

skip regexp ^std::([a-zA-z0-9_]+)<.*>::~?\1 *\(

I wanted to measure the perf impact of such a feature.
In my use case there can be 100s (or 1000s) of classes all
adding their own skips.
I added a test to gdb.perf so we have some numbers.

The short answer for stepping 4000 times:
with 4000 active skip function: 2.5 seconds
with 4000 active skip regexps: 5.3 seconds

About half of time the regexp is actually checked,
gdb only does skip checks when it steps into a different function.
It seems reasonable enough that I can scale this up without
slowing things down too much, at least in interactive sessions.
It'd be useful to rerun the test with more complex regexps though,
just to get more data.

skip-function-0 cpu_time 1000 0.62917
skip-function-0 cpu_time 2000 1.125956
skip-function-0 cpu_time 3000 1.691457
skip-function-0 cpu_time 4000 2.255912
skip-function-0 wall_time 1000 0.630132913589
skip-function-0 wall_time 2000 1.12823104858
skip-function-0 wall_time 3000 1.69549393654
skip-function-0 wall_time 4000 2.26372599602
skip-function-0 vmsize 1000 44912
skip-function-0 vmsize 2000 44912
skip-function-0 vmsize 3000 44912
skip-function-0 vmsize 4000 44912
skip-function-1000 cpu_time 1000 0.593435
skip-function-1000 cpu_time 2000 1.197598
skip-function-1000 cpu_time 3000 1.785339
skip-function-1000 cpu_time 4000 2.404062
skip-function-1000 wall_time 1000 0.594219923019
skip-function-1000 wall_time 2000 1.20304894447
skip-function-1000 wall_time 3000 1.79683208466
skip-function-1000 wall_time 4000 2.41939306259
skip-function-1000 vmsize 1000 48504
skip-function-1000 vmsize 2000 48504
skip-function-1000 vmsize 3000 48504
skip-function-1000 vmsize 4000 48504
skip-function-2000 cpu_time 1000 0.61971
skip-function-2000 cpu_time 2000 1.219753
skip-function-2000 cpu_time 3000 1.799796
skip-function-2000 cpu_time 4000 2.400764
skip-function-2000 wall_time 1000 0.622272968292
skip-function-2000 wall_time 2000 1.22521615028
skip-function-2000 wall_time 3000 1.8118929863
skip-function-2000 wall_time 4000 2.41736388206
skip-function-2000 vmsize 1000 48504
skip-function-2000 vmsize 2000 48504
skip-function-2000 vmsize 3000 48504
skip-function-2000 vmsize 4000 48504
skip-function-3000 cpu_time 1000 0.626897
skip-function-3000 cpu_time 2000 1.244155
skip-function-3000 cpu_time 3000 1.882615
skip-function-3000 cpu_time 4000 2.501074
skip-function-3000 wall_time 1000 0.629244089127
skip-function-3000 wall_time 2000 1.25092792511
skip-function-3000 wall_time 3000 1.89381504059
skip-function-3000 wall_time 4000 2.51427912712
skip-function-3000 vmsize 1000 48504
skip-function-3000 vmsize 2000 48504
skip-function-3000 vmsize 3000 48504
skip-function-3000 vmsize 4000 48504
skip-function-4000 cpu_time 1000 0.637122
skip-function-4000 cpu_time 2000 1.28074
skip-function-4000 cpu_time 3000 1.92615
skip-function-4000 cpu_time 4000 2.535431
skip-function-4000 wall_time 1000 0.638993024826
skip-function-4000 wall_time 2000 1.28889894485
skip-function-4000 wall_time 3000 1.9389591217
skip-function-4000 wall_time 4000 2.54448986053
skip-function-4000 vmsize 1000 48504
skip-function-4000 vmsize 2000 48504
skip-function-4000 vmsize 3000 48504
skip-function-4000 vmsize 4000 48504
skip-regexp-0 cpu_time 1000 0.580578
skip-regexp-0 cpu_time 2000 1.157049
skip-regexp-0 cpu_time 3000 1.742978
skip-regexp-0 cpu_time 4000 2.307753
skip-regexp-0 wall_time 1000 0.58118391037
skip-regexp-0 wall_time 2000 1.1583070755
skip-regexp-0 wall_time 3000 1.74652791023
skip-regexp-0 wall_time 4000 2.31054210663
skip-regexp-0 vmsize 1000 49000
skip-regexp-0 vmsize 2000 49000
skip-regexp-0 vmsize 3000 49000
skip-regexp-0 vmsize 4000 49000
skip-regexp-1000 cpu_time 1000 0.763724
skip-regexp-1000 cpu_time 2000 1.510762
skip-regexp-1000 cpu_time 3000 2.261737
skip-regexp-1000 cpu_time 4000 3.022399
skip-regexp-1000 wall_time 1000 0.764821052551
skip-regexp-1000 wall_time 2000 1.51259303093
skip-regexp-1000 wall_time 3000 2.26896214485
skip-regexp-1000 wall_time 4000 3.03822803497
skip-regexp-1000 vmsize 1000 53752
skip-regexp-1000 vmsize 2000 53752
skip-regexp-1000 vmsize 3000 53752
skip-regexp-1000 vmsize 4000 53752
skip-regexp-2000 cpu_time 1000 0.950312
skip-regexp-2000 cpu_time 2000 1.904746
skip-regexp-2000 cpu_time 3000 2.878043
skip-regexp-2000 cpu_time 4000 3.785029
skip-regexp-2000 wall_time 1000 0.953982830048
skip-regexp-2000 wall_time 2000 1.91325807571
skip-regexp-2000 wall_time 3000 2.89499402046
skip-regexp-2000 wall_time 4000 3.79832100868
skip-regexp-2000 vmsize 1000 59692
skip-regexp-2000 vmsize 2000 59692
skip-regexp-2000 vmsize 3000 59692
skip-regexp-2000 vmsize 4000 59692
skip-regexp-3000 cpu_time 1000 1.138258
skip-regexp-3000 cpu_time 2000 2.258269
skip-regexp-3000 cpu_time 3000 3.39071
skip-regexp-3000 cpu_time 4000 4.580428
skip-regexp-3000 wall_time 1000 1.14261198044
skip-regexp-3000 wall_time 2000 2.26143598557
skip-regexp-3000 wall_time 3000 3.39634680748
skip-regexp-3000 wall_time 4000 4.59546899796
skip-regexp-3000 vmsize 1000 65632
skip-regexp-3000 vmsize 2000 65632
skip-regexp-3000 vmsize 3000 65632
skip-regexp-3000 vmsize 4000 65632
skip-regexp-4000 cpu_time 1000 1.306872
skip-regexp-4000 cpu_time 2000 2.623364
skip-regexp-4000 cpu_time 3000 3.958511
skip-regexp-4000 cpu_time 4000 5.308965
skip-regexp-4000 wall_time 1000 1.31211209297
skip-regexp-4000 wall_time 2000 2.6355907917
skip-regexp-4000 wall_time 3000 3.97539687157
skip-regexp-4000 wall_time 4000 5.32494211197
skip-regexp-4000 vmsize 1000 71724
skip-regexp-4000 vmsize 2000 71724
skip-regexp-4000 vmsize 3000 71724
skip-regexp-4000 vmsize 4000 71724

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".

	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.

Comments

Eli Zaretskii Feb. 2, 2016, 4:09 p.m. UTC | #1
> 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.
Pedro Alves Feb. 4, 2016, 1:47 p.m. UTC | #2
On 02/02/2016 01:03 AM, Doug Evans wrote:
> Hi.
> 
> The "skip" command is great, but it can be really cumbersome to use with  
> c++.
> 
> E.g., consider stepping into functions that take std::string arguments.
> 
>    foo (bar ("abc"));
> 
> where bar takes a std::string argument.
> 
> There are four functions you generally always want to skip in this case
> (cut-n-pasted from my gdb session):
> 
> std::allocator<char>::allocator (this=0x7fffffffe6bf)
> std::basic_string<char, std::char_traits<char>, std::allocator<char>  
>> ::basic_string (this=0x7fffffffe6b0, __s=0x400ad1 "abc", __a=...)
> std::basic_string<char, std::char_traits<char>, std::allocator<char>  
>> ::~basic_string (this=0x7fffffffe6b0, __in_chrg=<optimized out>)
> std::allocator<char>::~allocator (this=0x7fffffffe6bf, __in_chrg=<optimized  
> out>)
> 
> With this patch one can specify the skip as:
> 
> skip regexp ^std::(allocator|basic_string)<.*>::~?\1 *\(
> 
> Skipping every templated class constructor/destructor in std
> could be specified as:
> 
> skip regexp ^std::([a-zA-z0-9_]+)<.*>::~?\1 *\(

I think this is great.  Thanks for tackling this.  This was
originally discussed in the context of the original
implementation, but was left out:

 https://sourceware.org/bugzilla/show_bug.cgi?id=8287

I wonder about the UI though.  The command name doesn't make it
clear, so I was wondered whether "skip regexp" also matches the
filename the function is implemented in.  I figured out it doesn't
from the documentation, but it got to me consider how we'd extend
the UI if/when we want to skip files with a regexp too, as
then "skip regexp" becomes ambiguous.  Maybe "skip rfunction" instead,
and then we'd have "skip rfile" ?


Also, ot asking you to implement this, just thinking out loud, but
I also wondered if it wouldn't have made more sense to be able
to mix file and function names, using command options, rather than
have distinct skip types.  Like:

 skip -file foo.c -function foo
 skip -rfile libfoo/*.c -rfunction ^foo_.*

I guess we could still have that and leave "skip function", etc.
for compatibility.

> 
> +skip regexp regular-expression
> +  A variation of "skip function" where the function name is specified
> +  as a regular expression.
> +



> +If you wanted to skip every C++ constructor and destructor in the  
> @code{std}
> +namespace you could do:

Using past tense reads a bit odd to me.  I'd suggest:

 If you want to skip every C++ constructor and destructor in the
 @code{std} namespace you can do:

> 
> +/* 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");
> +    }

Line breaks before return.

> +}
> +
> +/* 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;
> +}
> +

Seems like all these three functions could be replaced by
a single:

static struct skiplist_entry *
make_skiplist_entry (enum skip_kind kind, const char *text)
{
   struct skiplist_entry *e = XCNEW (struct skiplist_entry);

  e->kind = kind;
  e->text = xstrdup (text);
  e->enabled = 1;

  return e;
}

Thanks,
Pedro Alves
diff mbox

Patch

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..5a2668d 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 of 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 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..791207e
--- /dev/null
+++ b/gdb/testsuite/gdb.perf/skip-command.exp
@@ -0,0 +1,129 @@ 
+# 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 step GDB performs
+
+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_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)