public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH, doc RFA] Add "skip regexp"
@ 2016-02-02  1:03 Doug Evans
  2016-02-02 16:10 ` Eli Zaretskii
  2016-02-04 13:47 ` Pedro Alves
  0 siblings, 2 replies; 14+ messages in thread
From: Doug Evans @ 2016-02-02  1:03 UTC (permalink / raw)
  To: gdb-patches

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.

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)

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH, doc RFA] Add "skip regexp"
  2016-02-02  1:03 [PATCH, doc RFA] Add "skip regexp" Doug Evans
@ 2016-02-02 16:10 ` Eli Zaretskii
  2016-02-04 13:47 ` Pedro Alves
  1 sibling, 0 replies; 14+ messages in thread
From: Eli Zaretskii @ 2016-02-02 16:10 UTC (permalink / raw)
  To: Doug Evans; +Cc: gdb-patches

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

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH, doc RFA] Add "skip regexp"
  2016-02-02  1:03 [PATCH, doc RFA] Add "skip regexp" Doug Evans
  2016-02-02 16:10 ` Eli Zaretskii
@ 2016-02-04 13:47 ` Pedro Alves
  1 sibling, 0 replies; 14+ messages in thread
From: Pedro Alves @ 2016-02-04 13:47 UTC (permalink / raw)
  To: Doug Evans, gdb-patches

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

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH, doc RFA] Add "skip regexp"
@ 2016-03-15 19:45 Doug Evans
  0 siblings, 0 replies; 14+ messages in thread
From: Doug Evans @ 2016-03-15 19:45 UTC (permalink / raw)
  To: Simon Marchi; +Cc: Pedro Alves, eliz, gdb-patches

Simon Marchi writes:
  > On 16-03-03 02:21 PM, Doug Evans wrote:
  > > 2016-03-03  Doug Evans  <dje@google.com>
  > >
  > > 	* gdb.base/skip.c (main): Call test_skip_file_and_function.
  > > 	* gdb.base/skip.exp: Remove hand calling test_skip_file_and_function.
  > >
  > > diff --git a/gdb/testsuite/gdb.base/skip.c  
b/gdb/testsuite/gdb.base/skip.c
  > > index b9db2a7..e43da1e 100644
  > > --- a/gdb/testsuite/gdb.base/skip.c
  > > +++ b/gdb/testsuite/gdb.base/skip.c
  > > @@ -25,8 +25,14 @@ void skip1_test_skip_file_and_function (void);
  > >   int
  > >   main ()
  > >   {
  > > +  int x;
  > > +
  > >     /* Use comma operator to sequence evaluation of bar and foo.  */
  > > -  return baz ((bar (), foo ()));
  > > +  x = baz ((bar (), foo ()));
  > > +
  > > +  test_skip_file_and_function ();
  >
  > This function should be forward-declared.
  >
  > Otherwise, it LGTM.  I tested it on the target that reported the problem  
initially,
  > and it's now 100% pass.
  >
  > Thank you very much!

Committed with the suggested change.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH, doc RFA] Add "skip regexp"
  2016-03-03 19:21 Doug Evans
@ 2016-03-03 20:40 ` Simon Marchi
  0 siblings, 0 replies; 14+ messages in thread
From: Simon Marchi @ 2016-03-03 20:40 UTC (permalink / raw)
  To: Doug Evans; +Cc: Pedro Alves, eliz, gdb-patches

On 16-03-03 02:21 PM, Doug Evans wrote:
> 2016-03-03  Doug Evans  <dje@google.com>
> 
> 	* gdb.base/skip.c (main): Call test_skip_file_and_function.
> 	* gdb.base/skip.exp: Remove hand calling test_skip_file_and_function.
> 
> diff --git a/gdb/testsuite/gdb.base/skip.c b/gdb/testsuite/gdb.base/skip.c
> index b9db2a7..e43da1e 100644
> --- a/gdb/testsuite/gdb.base/skip.c
> +++ b/gdb/testsuite/gdb.base/skip.c
> @@ -25,8 +25,14 @@ void skip1_test_skip_file_and_function (void);
>   int
>   main ()
>   {
> +  int x;
> +
>     /* Use comma operator to sequence evaluation of bar and foo.  */
> -  return baz ((bar (), foo ()));
> +  x = baz ((bar (), foo ()));
> +
> +  test_skip_file_and_function ();

This function should be forward-declared.

Otherwise, it LGTM.  I tested it on the target that reported the problem initially,
and it's now 100% pass.

Thank you very much!

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH, doc RFA] Add "skip regexp"
@ 2016-03-03 19:21 Doug Evans
  2016-03-03 20:40 ` Simon Marchi
  0 siblings, 1 reply; 14+ messages in thread
From: Doug Evans @ 2016-03-03 19:21 UTC (permalink / raw)
  To: Simon Marchi; +Cc: Pedro Alves, eliz, gdb-patches

Doug Evans writes:
  > Simon Marchi writes:
  >   > On 16-02-16 08:07 PM, Doug Evans wrote:
  >   > > +# Test -fi + -fu.
  >   > > +
  >   > > +if ![runto_main] {
  >   > > +    fail "Can't run to main"
  >   > > +    return
  >   > > +}
  >   > > +
  >   > > +set test "step using -fi + -fu"
  >   > > +gdb_test_no_output "skip delete"
  >   > > +gdb_test "skip -fi skip1.c -fu test_skip" \
  >   > > +    "Function test_skip in file skip1.c will be skipped when
  > stepping\."
  >   > > +gdb_breakpoint "test_skip_file_and_function"
  >   > > +gdb_breakpoint "end_test_skip_file_and_function"
  >   > > +gdb_test "call test_skip_file_and_function ()" "silently stop."
  >   >
  >   > Hi Doug,
  >   >
  >   > I just saw a failure of this test on a target that doesn't have  
inferior
  > calls.  It
  >   > seems to me like the function call isn't fundamental to the test and  
it
  > could be
  >   > avoided by organizing things differently.  What do you think?
  >   >
  >   > Otherwise, we would need to add a
  >   >
  >   >   if [target_info exists gdb,cannot_call_functions] {
  >
  > Bleah, righto.
  >
  > Fix forthcoming.
  > I need to cleanup skip.exp first.

2016-03-03  Doug Evans  <dje@google.com>

	* gdb.base/skip.c (main): Call test_skip_file_and_function.
	* gdb.base/skip.exp: Remove hand calling test_skip_file_and_function.

diff --git a/gdb/testsuite/gdb.base/skip.c b/gdb/testsuite/gdb.base/skip.c
index b9db2a7..e43da1e 100644
--- a/gdb/testsuite/gdb.base/skip.c
+++ b/gdb/testsuite/gdb.base/skip.c
@@ -25,8 +25,14 @@ void skip1_test_skip_file_and_function (void);
  int
  main ()
  {
+  int x;
+
    /* Use comma operator to sequence evaluation of bar and foo.  */
-  return baz ((bar (), foo ()));
+  x = baz ((bar (), foo ()));
+
+  test_skip_file_and_function ();
+
+  return 0;
  }

  int
diff --git a/gdb/testsuite/gdb.base/skip.exp  
b/gdb/testsuite/gdb.base/skip.exp
index 67ae9d9..ce55dd2 100644
--- a/gdb/testsuite/gdb.base/skip.exp
+++ b/gdb/testsuite/gdb.base/skip.exp
@@ -281,17 +281,15 @@ with_test_prefix "step using -rfu for baz" {
  # Test -fi + -fu.

  with_test_prefix "step using -fi + -fu" {
-    if ![runto_main] {
-	fail "Can't run to main"
+    gdb_test_no_output "skip delete"
+
+    if ![runto test_skip_file_and_function no-message] {
+	fail "Can't run to test_skip_file_and_function"
  	return
      }

-    gdb_test_no_output "skip delete"
      gdb_test "skip -fi skip1.c -fu test_skip" \
  	"Function test_skip in file skip1.c will be skipped when stepping\."
-    gdb_breakpoint "test_skip_file_and_function"
-    gdb_breakpoint "end_test_skip_file_and_function"
-    gdb_test "call test_skip_file_and_function ()" "silently stop."
      # Verify we can step into skip.c:test_skip but not skip1.c:test_skip.
      gdb_test "step" "test_skip \\(\\) at.*" "step 1"
      gdb_test "step" "test_skip_file_and_function \\(\\) at.*" "step 2"; #  
Return from test_skip()

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH, doc RFA] Add "skip regexp"
@ 2016-03-02 23:20 Doug Evans
  0 siblings, 0 replies; 14+ messages in thread
From: Doug Evans @ 2016-03-02 23:20 UTC (permalink / raw)
  To: Simon Marchi; +Cc: Pedro Alves, eliz, gdb-patches

Simon Marchi writes:
  > On 16-02-16 08:07 PM, Doug Evans wrote:
  > > +# Test -fi + -fu.
  > > +
  > > +if ![runto_main] {
  > > +    fail "Can't run to main"
  > > +    return
  > > +}
  > > +
  > > +set test "step using -fi + -fu"
  > > +gdb_test_no_output "skip delete"
  > > +gdb_test "skip -fi skip1.c -fu test_skip" \
  > > +    "Function test_skip in file skip1.c will be skipped when  
stepping\."
  > > +gdb_breakpoint "test_skip_file_and_function"
  > > +gdb_breakpoint "end_test_skip_file_and_function"
  > > +gdb_test "call test_skip_file_and_function ()" "silently stop."
  >
  > Hi Doug,
  >
  > I just saw a failure of this test on a target that doesn't have inferior  
calls.  It
  > seems to me like the function call isn't fundamental to the test and it  
could be
  > avoided by organizing things differently.  What do you think?
  >
  > Otherwise, we would need to add a
  >
  >   if [target_info exists gdb,cannot_call_functions] {

Bleah, righto.

Fix forthcoming.
I need to cleanup skip.exp first.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH, doc RFA] Add "skip regexp"
  2016-02-17  1:07 Doug Evans
  2016-02-17 16:07 ` Eli Zaretskii
@ 2016-02-29 18:55 ` Simon Marchi
  1 sibling, 0 replies; 14+ messages in thread
From: Simon Marchi @ 2016-02-29 18:55 UTC (permalink / raw)
  To: Doug Evans, Pedro Alves, eliz; +Cc: gdb-patches

On 16-02-16 08:07 PM, Doug Evans wrote:
> +# Test -fi + -fu.
> +
> +if ![runto_main] {
> +    fail "Can't run to main"
> +    return
> +}
> +
> +set test "step using -fi + -fu"
> +gdb_test_no_output "skip delete"
> +gdb_test "skip -fi skip1.c -fu test_skip" \
> +    "Function test_skip in file skip1.c will be skipped when stepping\."
> +gdb_breakpoint "test_skip_file_and_function"
> +gdb_breakpoint "end_test_skip_file_and_function"
> +gdb_test "call test_skip_file_and_function ()" "silently stop."

Hi Doug,

I just saw a failure of this test on a target that doesn't have inferior calls.  It
seems to me like the function call isn't fundamental to the test and it could be
avoided by organizing things differently.  What do you think?

Otherwise, we would need to add a

  if [target_info exists gdb,cannot_call_functions] {

Simon

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH, doc RFA] Add "skip regexp"
  2016-02-24  9:54 ` Joel Brobecker
@ 2016-02-24 18:21   ` Doug Evans
  0 siblings, 0 replies; 14+ messages in thread
From: Doug Evans @ 2016-02-24 18:21 UTC (permalink / raw)
  To: Joel Brobecker; +Cc: gdb-patches

On Wed, Feb 24, 2016 at 1:54 AM, Joel Brobecker <brobecker@adacore.com> wrote:
> Hi Doug,
>
>>  > > --- a/gdb/NEWS
>>  > > +++ b/gdb/NEWS
>>  > > @@ -127,6 +127,14 @@ show max-value-size
>>  > >     allocate for value contents.  Prevents incorrect programs from
>>  > >     causing GDB to allocate overly large buffers.  Default is 64k.
>>  > >
>>  > > +skip -file file
>>  > > +skip -gfile file-glob-pattern
>>  > > +skip -function function
>>  > > +skip -rfunction regular-expression
>>  > > +  A generalized form of the skip command, with new support for
>>  > > +  glob-style file names and regular expressions for function names.
>>  > > +  Additionally, a file spec and a function spec may now be combined.
>
> This entry was added in the GDB 7.11 section, but I think you meant
> to add it in the "since 7.11" section?

Whoops, fixed.
Thanks!

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH, doc RFA] Add "skip regexp"
  2016-02-23 21:36 Doug Evans
@ 2016-02-24  9:54 ` Joel Brobecker
  2016-02-24 18:21   ` Doug Evans
  0 siblings, 1 reply; 14+ messages in thread
From: Joel Brobecker @ 2016-02-24  9:54 UTC (permalink / raw)
  To: Doug Evans; +Cc: gdb-patches

Hi Doug,

>  > > --- a/gdb/NEWS
>  > > +++ b/gdb/NEWS
>  > > @@ -127,6 +127,14 @@ show max-value-size
>  > >     allocate for value contents.  Prevents incorrect programs from
>  > >     causing GDB to allocate overly large buffers.  Default is 64k.
>  > >
>  > > +skip -file file
>  > > +skip -gfile file-glob-pattern
>  > > +skip -function function
>  > > +skip -rfunction regular-expression
>  > > +  A generalized form of the skip command, with new support for
>  > > +  glob-style file names and regular expressions for function names.
>  > > +  Additionally, a file spec and a function spec may now be combined.

This entry was added in the GDB 7.11 section, but I think you meant
to add it in the "since 7.11" section?

-- 
Joel

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH, doc RFA] Add "skip regexp"
@ 2016-02-23 21:36 Doug Evans
  2016-02-24  9:54 ` Joel Brobecker
  0 siblings, 1 reply; 14+ messages in thread
From: Doug Evans @ 2016-02-23 21:36 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: palves, gdb-patches

Eli Zaretskii writes:
  > > Date: Wed, 17 Feb 2016 01:07:38 +0000
  > > From: Doug Evans <dje@google.com>
  > > Cc: gdb-patches@sourceware.org
  > >
  > > Eli: Heads up, rewritten docs, needing re-approval.
  >
  > Thanks for the heads-up.
  >
  > > --- a/gdb/NEWS
  > > +++ b/gdb/NEWS
  > > @@ -127,6 +127,14 @@ show max-value-size
  > >     allocate for value contents.  Prevents incorrect programs from
  > >     causing GDB to allocate overly large buffers.  Default is 64k.
  > >
  > > +skip -file file
  > > +skip -gfile file-glob-pattern
  > > +skip -function function
  > > +skip -rfunction regular-expression
  > > +  A generalized form of the skip command, with new support for
  > > +  glob-style file names and regular expressions for function names.
  > > +  Additionally, a file spec and a function spec may now be combined.
  > > +
  >
  > This part is OK.
  >
  > > +@item -gfile @var{file-glob-pattern}
  > > +@itemx -gfi @var{file-glob-pattern}
  > > +Functions in files matching @var{file-glob-pattern} will be skipped
  > > +over when stepping.
  > > +
  > > +@smallexample
  > > +(gdb) skip -gfi utils/*.c
  > > +@end smallexample
  >
  > Is there a way to protect wildcard characters?  If so, I think we
  > should mention it.

The docs include this:

See for example @samp{man 7 glob} on @sc{gnu}/Linux systems for a
description of @code{glob}-style patterns.

which should be sufficient. There's a lot there, so the user
has good docs on them.

  > > +For example, there is generally no need to step into C++ std::string
  >
  > C@t{++}, and "std::string" should be in @code.
  >
  > Also, this description should have a couple of @cindex entries, as
  > readers are likely to look for it without remembering that the
  > command's name is "skip".
  >
  > The documentation is OK with those fixed.
  >
  > Thanks.

Here is what I committed.
Thanks.

2016-02-23  Doug Evans  <dje@google.com>

	Extend "skip" command to support -file, -gfile, -function, -rfunction.
	* NEWS: Document new features.
	* skip.c: #include "fnmatch.h", "gdb_regex.h".
	(skiplist_entry) <file>: Renamed from filename.
	<function>: Renamed from function_name.
	<file_is_glob, function_is_regexp>: New members.
	<compiled_function_regexp, compiled_function_regexp_is_valid>:
	New members.
	(make_skip_entry): New function.
	(free_skiplist_entry, free_skiplist_entry_cleanup): New functions.
	(make_free_skiplist_entry_cleanup): New function.
	(skip_file_command): Update.
	(skip_function, skip_function_command): Update.
	(compile_skip_regexp): New functions.
	(skip_command): Add support for new options.
	(skip_info): Update.
	(skip_file_p, skip_gfile_p): New functions.
	(skip_function_p, skip_rfunction_p): New functions.
	(function_name_is_marked_for_skip): Update and simplify.
	(_initialize_step_skip): Update.
	* symtab.c: #include "fnmatch.h".
	(compare_glob_filenames_for_search): New function.
	* symtab.h (compare_glob_filenames_for_search): Declare.
	* utils.c (count_path_elements): New function.
	(strip_leading_path_elements): New function.
	* utils.h (count_path_elements): Declare.
	(strip_leading_path_elements): Declare.

	doc/
	* gdb.texinfo (Skipping Over Functions and Files): Document new
	options to "skip" command.  Update docs of output of "info skip".

	testsuite/
	* gdb.base/skip.c (test_skip): New function.
	(end_test_skip_file_and_function): New function.
	(test_skip_file_and_function): New function.
	* gdb.base/skip1.c (test_skip): New function.
	(skip1_test_skip_file_and_function): New function.
	* gdb.base/skip.exp: Add tests for new skip options.
	* gdb.base/skip-solib.exp: Update expected output.
	* gdb.perf/skip-command.cc: New file.
	* gdb.perf/skip-command.exp: New file.
	* gdb.perf/skip-command.py: New file.

diff --git a/gdb/NEWS b/gdb/NEWS
index 64c4869..fc79142 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -141,6 +141,14 @@ show max-value-size
    allocate for value contents.  Prevents incorrect programs from
    causing GDB to allocate overly large buffers.  Default is 64k.

+skip -file file
+skip -gfile file-glob-pattern
+skip -function function
+skip -rfunction regular-expression
+  A generalized form of the skip command, with new support for
+  glob-style file names and regular expressions for function names.
+  Additionally, a file spec and a function spec may now be combined.
+
  * The "disassemble" command accepts a new modifier: /s.
    It prints mixed source+disassembly like /m with two differences:
    - disassembled instructions are now printed in program order, and
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 5db7cf2..4ec0ec1 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -5528,7 +5528,8 @@ default is @code{on}.

  The program you are debugging may contain some functions which are
  uninteresting to debug.  The @code{skip} command lets you tell  
@value{GDBN} to
-skip a function or all functions in a file when stepping.
+skip a function, all functions in a file or a particular function in
+a particular file when stepping.

  For example, consider the following C function:

@@ -5555,13 +5556,75 @@ A more flexible solution is to execute @kbd{skip  
boring}.  This instructs
  @code{step} at line 103, you'll step over @code{boring} and directly into
  @code{foo}.

-You can also instruct @value{GDBN} to skip all functions in a file, with,  
for
-example, @code{skip file boring.c}.
+Functions may be skipped by providing either a function name, linespec
+(@pxref{Specify Location}), regular expression that matches the function's
+name, file name or a @code{glob}-style pattern that matches the file name.
+
+On Posix systems the form of the regular expression is
+``Extended Regular Expressions''.  See for example @samp{man 7 regex}
+on @sc{gnu}/Linux systems.  On non-Posix systems the form of the regular
+expression is whatever is provided by the @code{regcomp} function of
+the underlying system.
+See for example @samp{man 7 glob} on @sc{gnu}/Linux systems for a
+description of @code{glob}-style patterns.
+
+@table @code
+@kindex skip
+@item skip @r{[}@var{options}@r{]}
+The basic form of the @code{skip} command takes zero or more options
+that specify what to skip.
+The @var{options} argument is any useful combination of the following:

  @table @code
+@item -file @var{file}
+@itemx -fi @var{file}
+Functions in @var{file} will be skipped over when stepping.
+
+@item -gfile @var{file-glob-pattern}
+@itemx -gfi @var{file-glob-pattern}
+@cindex skipping over files via glob-style patterns
+Functions in files matching @var{file-glob-pattern} will be skipped
+over when stepping.
+
+@smallexample
+(gdb) skip -gfi utils/*.c
+@end smallexample
+
+@item -function @var{linespec}
+@itemx -fu @var{linespec}
+Functions named by @var{linespec} or the function containing the line
+named by @var{linespec} will be skipped over when stepping.
+@xref{Specify Location}.
+
+@item -rfunction @var{regexp}
+@itemx -rfu @var{regexp}
+@cindex skipping over functions via regular expressions
+Functions whose name matches @var{regexp} will be skipped over when  
stepping.
+
+This form is useful for complex function names.
+For example, there is generally no need to step into C@t{++}  
@code{std::string}
+constructors or destructors.  Plus with C@t{++} templates it can be hard to
+write out the full name of the function, and often it doesn't matter what
+the template arguments are.  Specifying the function to be skipped as a
+regular expression makes this easier.
+
+@smallexample
+(gdb) skip -rfu ^std::(allocator|basic_string)<.*>::~?\1 *\(
+@end smallexample
+
+If you want to skip every templated C@t{++} constructor and destructor
+in the @code{std} namespace you can do:
+
+@smallexample
+(gdb) skip -rfu ^std::([a-zA-z0-9_]+)<.*>::~?\1 *\(
+@end smallexample
+@end table
+
+If no options are specified, the function you're currently debugging
+will be skipped.
+
  @kindex skip function
-@item skip @r{[}@var{linespec}@r{]}
-@itemx skip function @r{[}@var{linespec}@r{]}
+@item skip function @r{[}@var{linespec}@r{]}
  After running this command, the function named by @var{linespec} or the
  function containing the line named by @var{linespec} will be skipped over  
when
  stepping.  @xref{Specify Location}.
@@ -5577,6 +5640,11 @@ will be skipped.
  After running this command, any function whose source lives in  
@var{filename}
  will be skipped over when stepping.

+@smallexample
+(gdb) skip file boring.c
+File boring.c will be skipped when stepping.
+@end smallexample
+
  If you do not specify @var{filename}, functions whose source lives in the  
file
  you're currently debugging will be skipped.
  @end table
@@ -5594,20 +5662,21 @@ print a table with details about all functions and  
files marked for skipping.
  @table @emph
  @item Identifier
  A number identifying this skip.
-@item Type
-The type of this skip, either @samp{function} or @samp{file}.
  @item Enabled or Disabled
-Enabled skips are marked with @samp{y}.  Disabled skips are marked with  
@samp{n}.
-@item Address
-For function skips, this column indicates the address in memory of the  
function
-being skipped.  If you've set a function skip on a function which has not  
yet
-been loaded, this field will contain @samp{<PENDING>}.  Once a shared  
library
-which has the function is loaded, @code{info skip} will show the function's
-address here.
-@item What
-For file skips, this field contains the filename being skipped.  For  
functions
-skips, this field contains the function name and its line number in the  
file
-where it is defined.
+Enabled skips are marked with @samp{y}.
+Disabled skips are marked with @samp{n}.
+@item Glob
+If the file name is a @samp{glob} pattern this is @samp{y}.
+Otherwise it is @samp{n}.
+@item File
+The name or @samp{glob} pattern of the file to be skipped.
+If no file is specified this is @samp{<none>}.
+@item RE
+If the function name is a @samp{regular expression} this is @samp{y}.
+Otherwise it is @samp{n}.
+@item Function
+The name or regular expression of the function to skip.
+If no function is specified this is @samp{<none>}.
  @end table

  @kindex skip delete
diff --git a/gdb/skip.c b/gdb/skip.c
index d90910d..15681cc 100644
--- a/gdb/skip.c
+++ b/gdb/skip.c
@@ -32,19 +32,35 @@
  #include "breakpoint.h" /* for get_sal_arch () */
  #include "source.h"
  #include "filenames.h"
+#include "fnmatch.h"
+#include "gdb_regex.h"

  struct skiplist_entry
  {
    int number;

-  /* NULL if this isn't a skiplist entry for an entire file.
+  /* Non-zero if FILE is a glob-style pattern.
+     Otherewise it is the plain file name (possibly with directories).  */
+  int file_is_glob;
+
+  /* The name of the file or NULL.
       The skiplist entry owns this pointer.  */
-  char *filename;
+  char *file;
+
+  /* Non-zero if FUNCTION is a regexp.
+     Otherwise it is a plain function name (possibly with arguments,
+     for C++).  */
+  int function_is_regexp;

-  /* The name of the marked-for-skip function, if this is a skiplist
-     entry for a function.
+  /* The name of the function or NULL.
       The skiplist entry owns this pointer.  */
-  char *function_name;
+  char *function;
+
+  /* If this is a function regexp, the compiled form.  */
+  regex_t compiled_function_regexp;
+
+  /* Non-zero if the function regexp has been compiled.  */
+  int compiled_function_regexp_is_valid;

    int enabled;

@@ -52,7 +68,6 @@ struct skiplist_entry
  };

  static void add_skiplist_entry (struct skiplist_entry *e);
-static void skip_function (const char *name);

  static struct skiplist_entry *skiplist_entry_chain;
  static int skiplist_entry_count;
@@ -65,10 +80,62 @@ static int skiplist_entry_count;
         E ? (TMP = E->next, 1) : 0;       \
         E = TMP)

+/* Create a skip object.  */
+
+static struct skiplist_entry *
+make_skip_entry (int file_is_glob, const char *file,
+		 int function_is_regexp, const char *function)
+{
+  struct skiplist_entry *e = XCNEW (struct skiplist_entry);
+
+  gdb_assert (file != NULL || function != NULL);
+  if (file_is_glob)
+    gdb_assert (file != NULL);
+  if (function_is_regexp)
+    gdb_assert (function != NULL);
+
+  if (file != NULL)
+    e->file = xstrdup (file);
+  if (function != NULL)
+    e->function = xstrdup (function);
+  e->file_is_glob = file_is_glob;
+  e->function_is_regexp = function_is_regexp;
+  e->enabled = 1;
+
+  return e;
+}
+
+/* Free a skiplist entry.  */
+
+static void
+free_skiplist_entry (struct skiplist_entry *e)
+{
+  xfree (e->file);
+  xfree (e->function);
+  if (e->function_is_regexp && e->compiled_function_regexp_is_valid)
+    regfree (&e->compiled_function_regexp);
+  xfree (e);
+}
+
+/* Wrapper to free_skiplist_entry for use as a cleanup.  */
+
+static void
+free_skiplist_entry_cleanup (void *e)
+{
+  free_skiplist_entry ((struct skiplist_entry *) e);
+}
+
+/* Create a cleanup to free skiplist entry E.  */
+
+static struct cleanup *
+make_free_skiplist_entry_cleanup (struct skiplist_entry *e)
+{
+  return make_cleanup (free_skiplist_entry_cleanup, e);
+}
+
  static void
  skip_file_command (char *arg, int from_tty)
  {
-  struct skiplist_entry *e;
    struct symtab *symtab;
    const char *filename = NULL;

@@ -85,37 +152,31 @@ skip_file_command (char *arg, int from_tty)
        filename = symtab_to_fullname (symtab);
      }
    else
-    {
-      symtab = lookup_symtab (arg);
-      if (symtab == NULL)
-	{
-	  fprintf_filtered (gdb_stderr, _("No source file named %s.\n"), arg);
-	  if (!nquery (_("\
-Ignore file pending future shared library load? ")))
-	    return;
-	}
-      /* Do not use SYMTAB's filename, later loaded shared libraries may  
match
-         given ARG but not SYMTAB's filename.  */
-      filename = arg;
-    }
+    filename = arg;

-  e = XCNEW (struct skiplist_entry);
-  e->filename = xstrdup (filename);
-  e->enabled = 1;
-
-  add_skiplist_entry (e);
+  add_skiplist_entry (make_skip_entry (0, filename, 0, NULL));

    printf_filtered (_("File %s will be skipped when stepping.\n"),  
filename);
  }

+/* Create a skiplist entry for the given function NAME and add it to the
+   list.  */
+
  static void
-skip_function_command (char *arg, int from_tty)
+skip_function (const char *name)
  {
-  const char *name = NULL;
+  add_skiplist_entry (make_skip_entry (0, NULL, 0, name));
+
+  printf_filtered (_("Function %s will be skipped when stepping.\n"),  
name);
+}

+static void
+skip_function_command (char *arg, int from_tty)
+{
    /* Default to the current function if no argument is given.  */
    if (arg == NULL)
      {
+      const char *name = NULL;
        CORE_ADDR pc;

        if (!last_displayed_sal_is_valid ())
@@ -128,25 +189,169 @@ skip_function_command (char *arg, int from_tty)
  		  paddress (get_current_arch (), pc));
  	}
        skip_function (name);
+      return;
      }
-  else
+
+  skip_function (arg);
+}
+
+/* Compile the regexp in E.
+   An error is thrown if there's an error.
+   MESSAGE is used as a prefix of the error message.  */
+
+static void
+compile_skip_regexp (struct skiplist_entry *e, const char *message)
+{
+  int code;
+  int flags = REG_NOSUB;
+
+#ifdef REG_EXTENDED
+  flags |= REG_EXTENDED;
+#endif
+
+  gdb_assert (e->function_is_regexp && e->function != NULL);
+
+  code = regcomp (&e->compiled_function_regexp, e->function, flags);
+  if (code != 0)
      {
-      if (lookup_symbol (arg, NULL, VAR_DOMAIN, NULL).symbol == NULL)
-        {
-	  fprintf_filtered (gdb_stderr,
-			    _("No function found named %s.\n"), arg);
+      char *err = get_regcomp_error (code, &e->compiled_function_regexp);

-	  if (nquery (_("\
-Ignore function pending future shared library load? ")))
-	    {
-	      /* Add the unverified skiplist entry.  */
-	      skip_function (arg);
-	    }
+      make_cleanup (xfree, err);
+      error (_("%s: %s"), message, err);
+    }
+  e->compiled_function_regexp_is_valid = 1;
+}
+
+/* Process "skip ..." that does not match "skip file" or "skip function".   
*/
+
+static void
+skip_command (char *arg, int from_tty)
+{
+  const char *file = NULL;
+  const char *gfile = NULL;
+  const char *function = NULL;
+  const char *rfunction = NULL;
+  char **argv;
+  struct cleanup *cleanups;
+  struct skiplist_entry *e;
+  int i;
+
+  if (arg == NULL)
+    {
+      skip_function_command (arg, from_tty);
+      return;
+    }
+
+  argv = buildargv (arg);
+  cleanups = make_cleanup_freeargv (argv);
+
+  for (i = 0; argv[i] != NULL; ++i)
+    {
+      const char *p = argv[i];
+      const char *value = argv[i + 1];
+
+      if (strcmp (p, "-fi") == 0
+	  || strcmp (p, "-file") == 0)
+	{
+	  if (value == NULL)
+	    error (_("Missing value for %s option."), p);
+	  file = value;
+	  ++i;
+	}
+      else if (strcmp (p, "-gfi") == 0
+	       || strcmp (p, "-gfile") == 0)
+	{
+	  if (value == NULL)
+	    error (_("Missing value for %s option."), p);
+	  gfile = value;
+	  ++i;
+	}
+      else if (strcmp (p, "-fu") == 0
+	       || strcmp (p, "-function") == 0)
+	{
+	  if (value == NULL)
+	    error (_("Missing value for %s option."), p);
+	  function = value;
+	  ++i;
+	}
+      else if (strcmp (p, "-rfu") == 0
+	       || strcmp (p, "-rfunction") == 0)
+	{
+	  if (value == NULL)
+	    error (_("Missing value for %s option."), p);
+	  rfunction = value;
+	  ++i;
+	}
+      else if (*p == '-')
+	error (_("Invalid skip option: %s"), p);
+      else if (i == 0)
+	{
+	  /* Assume the user entered "skip FUNCTION-NAME".
+	     FUNCTION-NAME may be `foo (int)', and therefore we pass the
+	     complete original arg to skip_function command as if the user
+	     typed "skip function arg".  */
+	  do_cleanups (cleanups);
+	  skip_function_command (arg, from_tty);
  	  return;
  	}
+      else
+	error (_("Invalid argument: %s"), p);
+    }
+
+  if (file != NULL && gfile != NULL)
+    error (_("Cannot specify both -file and -gfile."));
+
+  if (function != NULL && rfunction != NULL)
+    error (_("Cannot specify both -function and -rfunction."));
+
+  /* This shouldn't happen as "skip" by itself gets punted to
+     skip_function_command.  */
+  gdb_assert (file != NULL || gfile != NULL
+	      || function != NULL || rfunction != NULL);
+
+  e = make_skip_entry (gfile != NULL, file ? file : gfile,
+		       rfunction != NULL, function ? function : rfunction);
+  if (rfunction != NULL)
+    {
+      struct cleanup *rf_cleanups = make_free_skiplist_entry_cleanup (e);

-      skip_function (arg);
+      compile_skip_regexp (e, _("regexp"));
+      discard_cleanups (rf_cleanups);
      }
+  add_skiplist_entry (e);
+
+  /* I18N concerns drive some of the choices here (we can't piece together
+     the output too much).  OTOH we want to keep this simple.  Therefore  
the
+     only polish we add to the output is to append "(s)" to "File" or
+     "Function" if they're a glob/regexp.  */
+  {
+    const char *file_to_print = file != NULL ? file : gfile;
+    const char *function_to_print = function != NULL ? function :  
rfunction;
+    const char *file_text = gfile != NULL ? _("File(s)") : _("File");
+    const char *lower_file_text = gfile != NULL ? _("file(s)") : _("file");
+    const char *function_text
+      = rfunction != NULL ? _("Function(s)") : _("Function");
+
+    if (function_to_print == NULL)
+      {
+	printf_filtered (_("%s %s will be skipped when stepping.\n"),
+			 file_text, file_to_print);
+      }
+    else if (file_to_print == NULL)
+      {
+	printf_filtered (_("%s %s will be skipped when stepping.\n"),
+			 function_text, function_to_print);
+      }
+    else
+      {
+	printf_filtered (_("%s %s in %s %s will be skipped"
+			   " when stepping.\n"),
+			 function_text, function_to_print,
+			 lower_file_text, file_to_print);
+      }
+  }
+
+  do_cleanups (cleanups);
  }

  static void
@@ -177,14 +382,17 @@ Not skipping any files or functions.\n"));
        return;
      }

-  tbl_chain = make_cleanup_ui_out_table_begin_end (current_uiout, 4,
+  tbl_chain = make_cleanup_ui_out_table_begin_end (current_uiout, 6,
  						   num_printable_entries,
  						   "SkiplistTable");

-  ui_out_table_header (current_uiout, 7, ui_left, "number", "Num");       
/* 1 */
-  ui_out_table_header (current_uiout, 14, ui_left, "type", "Type");       
/* 2 */
-  ui_out_table_header (current_uiout, 3, ui_left, "enabled", "Enb");      
/* 3 */
-  ui_out_table_header (current_uiout, 40, ui_noalign, "what", "What");    
/* 4 */
+  ui_out_table_header (current_uiout, 5, ui_left, "number", "Num");   /* 1  
*/
+  ui_out_table_header (current_uiout, 3, ui_left, "enabled", "Enb");  /* 2  
*/
+  ui_out_table_header (current_uiout, 4, ui_right, "regexp", "Glob"); /* 3  
*/
+  ui_out_table_header (current_uiout, 20, ui_left, "file", "File");   /* 4  
*/
+  ui_out_table_header (current_uiout, 2, ui_right, "regexp", "RE");   /* 5  
*/
+  ui_out_table_header (current_uiout, 40, ui_noalign,
+		       "function", "Function"); /* 6 */
    ui_out_table_body (current_uiout);

    ALL_SKIPLIST_ENTRIES (e)
@@ -197,25 +405,27 @@ Not skipping any files or functions.\n"));

        entry_chain = make_cleanup_ui_out_tuple_begin_end (current_uiout,
  							 "blklst-entry");
-      ui_out_field_int (current_uiout, "number", e->number);              
/* 1 */
+      ui_out_field_int (current_uiout, "number", e->number); /* 1 */

-      if (e->function_name != NULL)
-	ui_out_field_string (current_uiout, "type", "function");         /* 2 */
-      else if (e->filename != NULL)
-	ui_out_field_string (current_uiout, "type", "file");             /* 2 */
+      if (e->enabled)
+	ui_out_field_string (current_uiout, "enabled", "y"); /* 2 */
        else
-	internal_error (__FILE__, __LINE__, _("\
-Skiplist entry should have either a filename or a function name."));
+	ui_out_field_string (current_uiout, "enabled", "n"); /* 2 */

-      if (e->enabled)
-	ui_out_field_string (current_uiout, "enabled", "y");             /* 3 */
+      if (e->file_is_glob)
+	ui_out_field_string (current_uiout, "regexp", "y"); /* 3 */
        else
-	ui_out_field_string (current_uiout, "enabled", "n");             /* 3 */
+	ui_out_field_string (current_uiout, "regexp", "n"); /* 3 */

-      if (e->function_name != NULL)
-	ui_out_field_string (current_uiout, "what", e->function_name);	 /* 4 */
-      else if (e->filename != NULL)
-	ui_out_field_string (current_uiout, "what", e->filename);	 /* 4 */
+      ui_out_field_string (current_uiout, "file",
+			   e->file ? e->file : "<none>"); /* 4 */
+      if (e->function_is_regexp)
+	ui_out_field_string (current_uiout, "regexp", "y"); /* 5 */
+      else
+	ui_out_field_string (current_uiout, "regexp", "n"); /* 5 */
+
+      ui_out_field_string (current_uiout, "function",
+			   e->function ? e->function : "<none>"); /* 6 */

        ui_out_text (current_uiout, "\n");
        do_cleanups (entry_chain);
@@ -273,9 +483,7 @@ skip_delete_command (char *arg, int from_tty)
  	else
  	  skiplist_entry_chain = e->next;

-	xfree (e->function_name);
-	xfree (e->filename);
-	xfree (e);
+	free_skiplist_entry (e);
          found = 1;
        }
      else
@@ -287,22 +495,6 @@ skip_delete_command (char *arg, int from_tty)
      error (_("No skiplist entries found with number %s."), arg);
  }

-/* Create a skiplist entry for the given function NAME and add it to the
-   list.  */
-
-static void
-skip_function (const char *name)
-{
-  struct skiplist_entry *e = XCNEW (struct skiplist_entry);
-
-  e->enabled = 1;
-  e->function_name = xstrdup (name);
-
-  add_skiplist_entry (e);
-
-  printf_filtered (_("Function %s will be skipped when stepping.\n"),  
name);
-}
-
  /* Add the given skiplist entry to our list, and set the entry's number.   
*/

  static void
@@ -326,6 +518,98 @@ add_skiplist_entry (struct skiplist_entry *e)
      }
  }

+/* Return non-zero if we're stopped at a file to be skipped.  */
+
+static int
+skip_file_p (struct skiplist_entry *e,
+	     const struct symtab_and_line *function_sal)
+{
+  gdb_assert (e->file != NULL && !e->file_is_glob);
+
+  if (function_sal->symtab == NULL)
+    return 0;
+
+  /* Check first sole SYMTAB->FILENAME.  It may not be a substring of
+     symtab_to_fullname as it may contain "./" etc.  */
+  if (compare_filenames_for_search (function_sal->symtab->filename,  
e->file))
+    return 1;
+
+  /* Before we invoke realpath, which can get expensive when many
+     files are involved, do a quick comparison of the basenames.  */
+  if (!basenames_may_differ
+      && filename_cmp (lbasename (function_sal->symtab->filename),
+		       lbasename (e->file)) != 0)
+    return 0;
+
+  /* Note: symtab_to_fullname caches its result, thus we don't have to.  */
+  {
+    const char *fullname = symtab_to_fullname (function_sal->symtab);
+
+    if (compare_filenames_for_search (fullname, e->file))
+      return 1;
+  }
+
+  return 0;
+}
+
+/* Return non-zero if we're stopped at a globbed file to be skipped.  */
+
+static int
+skip_gfile_p (struct skiplist_entry *e,
+	      const struct symtab_and_line *function_sal)
+{
+  gdb_assert (e->file != NULL && e->file_is_glob);
+
+  if (function_sal->symtab == NULL)
+    return 0;
+
+  /* Check first sole SYMTAB->FILENAME.  It may not be a substring of
+     symtab_to_fullname as it may contain "./" etc.  */
+  if (gdb_filename_fnmatch (e->file, function_sal->symtab->filename,
+			    FNM_FILE_NAME | FNM_NOESCAPE) == 0)
+    return 1;
+
+  /* Before we invoke symtab_to_fullname, which is expensive, do a quick
+     comparison of the basenames.
+     Note that we assume that lbasename works with glob-style patterns.
+     If the basename of the glob pattern is something like "*.c" then this
+     isn't much of a win.  Oh well.  */
+  if (!basenames_may_differ
+      && gdb_filename_fnmatch (lbasename (e->file),
+			       lbasename (function_sal->symtab->filename),
+			       FNM_FILE_NAME | FNM_NOESCAPE) != 0)
+    return 0;
+
+  /* Note: symtab_to_fullname caches its result, thus we don't have to.  */
+  {
+    const char *fullname = symtab_to_fullname (function_sal->symtab);
+
+    if (compare_glob_filenames_for_search (fullname, e->file))
+      return 1;
+  }
+
+  return 0;
+}
+
+/* Return non-zero if we're stopped at a function to be skipped.  */
+
+static int
+skip_function_p (struct skiplist_entry *e, const char *function_name)
+{
+  gdb_assert (e->function != NULL && !e->function_is_regexp);
+  return strcmp_iw (function_name, e->function) == 0;
+}
+
+/* Return non-zero if we're stopped at a function regexp to be skipped.  */
+
+static int
+skip_rfunction_p (struct skiplist_entry *e, const char *function_name)
+{
+  gdb_assert (e->function != NULL && e->function_is_regexp
+	      && e->compiled_function_regexp_is_valid);
+  return (regexec (&e->compiled_function_regexp, function_name, 0, NULL, 0)
+	  == 0);
+}

  /* See skip.h.  */

@@ -333,8 +617,6 @@ int
  function_name_is_marked_for_skip (const char *function_name,
  				  const struct symtab_and_line *function_sal)
  {
-  int searched_for_fullname = 0;
-  const char *fullname = NULL;
    struct skiplist_entry *e;

    if (function_name == NULL)
@@ -342,43 +624,49 @@ function_name_is_marked_for_skip (const char  
*function_name,

    ALL_SKIPLIST_ENTRIES (e)
      {
+      int skip_by_file = 0;
+      int skip_by_function = 0;
+
        if (!e->enabled)
  	continue;

-      /* Does the pc we're stepping into match e's stored pc? */
-      if (e->function_name != NULL
-	  && strcmp_iw (function_name, e->function_name) == 0)
-	return 1;
-
-      if (e->filename != NULL)
+      if (e->file != NULL)
  	{
-	  /* Check first sole SYMTAB->FILENAME.  It does not need to be
-	     a substring of symtab_to_fullname as it may contain "./" etc.  */
-	  if (function_sal->symtab != NULL
-	      && compare_filenames_for_search (function_sal->symtab->filename,
-					       e->filename))
-	    return 1;
-
-	  /* Before we invoke realpath, which can get expensive when many
-	     files are involved, do a quick comparison of the basenames.  */
-	  if (!basenames_may_differ
-	      && (function_sal->symtab == NULL
-	          || filename_cmp (lbasename (function_sal->symtab->filename),
-				   lbasename (e->filename)) != 0))
-	    continue;
-
-	  /* Get the filename corresponding to this FUNCTION_SAL, if we haven't
-	     yet.  */
-	  if (!searched_for_fullname)
+	  if (e->file_is_glob)
+	    {
+	      if (skip_gfile_p (e, function_sal))
+		skip_by_file = 1;
+	    }
+	  else
  	    {
-	      if (function_sal->symtab != NULL)
-		fullname = symtab_to_fullname (function_sal->symtab);
-	      searched_for_fullname = 1;
+	      if (skip_file_p (e, function_sal))
+		skip_by_file = 1;
  	    }
-	  if (fullname != NULL
-	      && compare_filenames_for_search (fullname, e->filename))
+	}
+      if (e->function != NULL)
+	{
+	  if (e->function_is_regexp)
+	    {
+	      if (skip_rfunction_p (e, function_name))
+		skip_by_function = 1;
+	    }
+	  else
+	    {
+	      if (skip_function_p (e, function_name))
+		skip_by_function = 1;
+	    }
+	}
+
+      /* If both file and function must match, make sure we don't errantly
+	 exit if only one of them match.  */
+      if (e->file != NULL && e->function != NULL)
+	{
+	  if (skip_by_file && skip_by_function)
  	    return 1;
  	}
+      /* Only one of file/function is specified.  */
+      else if (skip_by_file || skip_by_function)
+	return 1;
      }

    return 0;
@@ -396,22 +684,31 @@ _initialize_step_skip (void)
    skiplist_entry_chain = 0;
    skiplist_entry_count = 0;

-  add_prefix_cmd ("skip", class_breakpoint, skip_function_command, _("\
+  add_prefix_cmd ("skip", class_breakpoint, skip_command, _("\
  Ignore a function while stepping.\n\
-Usage: skip [FUNCTION NAME]\n\
-If no function name is given, ignore the current function."),
+\n\
+Usage: skip [FUNCTION-NAME]\n\
+       skip [<file-spec>] [<function-spec>]\n\
+If no arguments are given, ignore the current function.\n\
+\n\
+<file-spec> is one of:\n\
+       -fi|-file FILE-NAME\n\
+       -gfi|-gfile GLOB-FILE-PATTERN\n\
+<function-spec> is one of:\n\
+       -fu|-function FUNCTION-NAME\n\
+       -rfu|-rfunction FUNCTION-NAME-REGULAR-EXPRESSION"),
                    &skiplist, "skip ", 1, &cmdlist);

    c = add_cmd ("file", class_breakpoint, skip_file_command, _("\
  Ignore a file while stepping.\n\
-Usage: skip file [FILENAME]\n\
+Usage: skip file [FILE-NAME]\n\
  If no filename is given, ignore the current file."),
  	       &skiplist);
    set_cmd_completer (c, filename_completer);

    c = add_cmd ("function", class_breakpoint, skip_function_command, _("\
  Ignore a function while stepping.\n\
-Usage: skip function [FUNCTION NAME]\n\
+Usage: skip function [FUNCTION-NAME]\n\
  If no function name is given, skip the current function."),
  	       &skiplist);
    set_cmd_completer (c, location_completer);
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 95b3a11..e06104b 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -41,7 +41,7 @@
  #include "p-lang.h"
  #include "addrmap.h"
  #include "cli/cli-utils.h"
-
+#include "fnmatch.h"
  #include "hashtab.h"

  #include "gdb_obstack.h"
@@ -342,6 +342,40 @@ compare_filenames_for_search (const char *filename,  
const char *search_name)
  	      && STRIP_DRIVE_SPEC (filename) == &filename[len - search_len]));
  }

+/* Same as compare_filenames_for_search, but for glob-style patterns.
+   Heads up on the order of the arguments.  They match the order of
+   compare_filenames_for_search, but it's the opposite of the order of
+   arguments to gdb_filename_fnmatch.  */
+
+int
+compare_glob_filenames_for_search (const char *filename,
+				   const char *search_name)
+{
+  /* We rely on the property of glob-style patterns with FNM_FILE_NAME that
+     all /s have to be explicitly specified.  */
+  int file_path_elements = count_path_elements (filename);
+  int search_path_elements = count_path_elements (search_name);
+
+  if (search_path_elements > file_path_elements)
+    return 0;
+
+  if (IS_ABSOLUTE_PATH (search_name))
+    {
+      return (search_path_elements == file_path_elements
+	      && gdb_filename_fnmatch (search_name, filename,
+				       FNM_FILE_NAME | FNM_NOESCAPE) == 0);
+    }
+
+  {
+    const char *file_to_compare
+      = strip_leading_path_elements (filename,
+				     file_path_elements - search_path_elements);
+
+    return gdb_filename_fnmatch (search_name, file_to_compare,
+				 FNM_FILE_NAME | FNM_NOESCAPE) == 0;
+  }
+}
+
  /* Check for a symtab of a specific name by searching some symtabs.
     This is a helper function for callbacks of iterate_over_symtabs.

diff --git a/gdb/symtab.h b/gdb/symtab.h
index f7884b9..6f00baf 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1600,6 +1600,9 @@ extern int basenames_may_differ;
  int compare_filenames_for_search (const char *filename,
  				  const char *search_name);

+int compare_glob_filenames_for_search (const char *filename,
+				       const char *search_name);
+
  int iterate_over_some_symtabs (const char *name,
  			       const char *real_path,
  			       int (*callback) (struct symtab *symtab,
diff --git a/gdb/testsuite/gdb.base/skip-solib.exp  
b/gdb/testsuite/gdb.base/skip-solib.exp
index a4047c3..ed81d65 100644
--- a/gdb/testsuite/gdb.base/skip-solib.exp
+++ b/gdb/testsuite/gdb.base/skip-solib.exp
@@ -73,8 +73,8 @@ Ignore file pending future shared library load.*" \
  # Checkinfo skip list.
  #
  gdb_test "info skip" \
-  "Num\\s+Type\\s+Enb\\s+What\\s*
-1\\s+file\\s+y\\s+${srcfile_lib}\\s*" \
+  "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s*
+1\\s+y\\s+n\\s+${srcfile_lib}\\s+n\\s+<none>\\s*" \
    "info skip with pending file"

  if ![runto_main] { fail "skip tests suppressed" }
@@ -108,8 +108,8 @@ gdb_test "step" "square.*"
  # Now our entry should no longer be pending.
  #
  gdb_test "info skip" \
-  "Num\\s+Type\\s+Enb\\s+What\\s*
-1\\s+function\\s+y\\s+multiply\\s*" \
+  "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s*
+1\\s+y\\s+n\\s+<none>\\s+n\\s+multiply\\s*" \
    "info skip for function multiply"

  #
diff --git a/gdb/testsuite/gdb.base/skip.c b/gdb/testsuite/gdb.base/skip.c
index 3568296..b9db2a7 100644
--- a/gdb/testsuite/gdb.base/skip.c
+++ b/gdb/testsuite/gdb.base/skip.c
@@ -20,6 +20,7 @@
  int foo (void);
  int bar (void);
  int baz (int);
+void skip1_test_skip_file_and_function (void);

  int
  main ()
@@ -33,3 +34,22 @@ foo ()
  {
    return 0;
  }
+
+static void
+test_skip (void)
+{
+}
+
+static void
+end_test_skip_file_and_function (void)
+{
+  abort ();
+}
+
+void
+test_skip_file_and_function (void)
+{
+  test_skip ();
+  skip1_test_skip_file_and_function ();
+  end_test_skip_file_and_function ();
+}
diff --git a/gdb/testsuite/gdb.base/skip.exp  
b/gdb/testsuite/gdb.base/skip.exp
index 40f3103..7d28b8b 100644
--- a/gdb/testsuite/gdb.base/skip.exp
+++ b/gdb/testsuite/gdb.base/skip.exp
@@ -14,6 +14,7 @@
  # along with this program.  If not, see <http://www.gnu.org/licenses/>.

  # This file was written by Justin Lebar. (justin.lebar@gmail.com)
+# And further hacked on by Doug Evans. (dje@google.com)

  if { [prepare_for_testing skip.exp "skip" \
                            {skip.c skip1.c } \
@@ -30,7 +31,24 @@ gdb_test "skip file" "No default file now." "skip file  
(no default file)"
  gdb_test "skip function" "No default function now."
  gdb_test "skip" "No default function now." "skip (no default function)"

-if ![runto_main] { fail "skip tests suppressed" }
+# Test elided args.
+
+gdb_test "skip -fi" "Missing value for -fi option."
+gdb_test "skip -file" "Missing value for -file option."
+gdb_test "skip -fu" "Missing value for -fu option."
+gdb_test "skip -function" "Missing value for -function option."
+gdb_test "skip -rfu" "Missing value for -rfu option."
+gdb_test "skip -rfunction" "Missing value for -rfunction option."
+
+# Test other invalid option combinations.
+
+gdb_test "skip -x" "Invalid skip option: -x"
+gdb_test "skip -rfu foo.* xyzzy" "Invalid argument: xyzzy"
+
+if ![runto_main] {
+    fail "Can't run to main"
+    return
+}

  # Test |info skip| with an empty skiplist.

@@ -62,34 +80,39 @@ gdb_test "info skip 999" "No skiplist entries found  
with number 999."
  # Does |info skip| look right?

  gdb_test "info skip" \
-  "Num\\s+Type\\s+Enb\\s+What\\s*
-1\\s+file\\s+y\\s+.*$srcfile\\s*
-2\\s+function\\s+y\\s+main\\s*
-3\\s+file\\s+y\\s+$srcfile1\\s*
-4\\s+function\\s+y\\s+baz\\s*"
+    "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s*
+1\\s+y\\s+n\\s+.*$srcfile\\s+n\\s+<none>\\s*
+2\\s+y\\s+n\\s+<none>\\s+n\\s+main\\s*
+3\\s+y\\s+n\\s+$srcfile1\\s+n\\s+<none>\\s*
+4\\s+y\\s+n\\s+<none>\\s+n\\s+baz\\s*"

  # Right now, we have an outstanding skiplist entry on both source
  # files, so when we step into the first line in main(), we should step
  # right over it and go to the second line of main().

-if ![runto_main] { fail "skip tests suppressed" }
+if ![runto_main] {
+    fail "Can't run to main"
+    return
+}
  gdb_test "step" ".*" "step in the main"
  gdb_test "bt" "\\s*\\#0\\s+main.*" "step after all ignored"

  # Now remove skip.c from the skiplist.  Our first step should take us
-# into foo(), and our second step should take us to the next line in
-# main().
+# into foo(), and our second step should take us to the next line in  
main().

  gdb_test "skip delete 1"
  # Check that entry 1 is missing from |info skip|
  gdb_test "info skip" \
-  "Num\\s+Type\\s+Enb\\s+What\\s*
-2\\s+function\\s+y\\s+main\\s*
-3\\s+file\\s+y\\s+$srcfile1\\s*
-4\\s+function\\s+y\\s+baz\\s*" \
+    "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s*
+2\\s+y\\s+n\\s+<none>\\s+n\\s+main\\s*
+3\\s+y\\s+n\\s+$srcfile1\\s+n\\s+<none>\\s*
+4\\s+y\\s+n\\s+<none>\\s+n\\s+baz\\s*" \
      "info skip (delete 1)"

-if ![runto_main] { fail "skip tests suppressed" }
+if ![runto_main] {
+    fail "Can't run to main"
+    return
+}
  set test "step after deleting 1"
  gdb_test "step" "foo \\(\\) at.*" "$test (1)"
  gdb_test "step" ".*" "$test (2)" ; # Return from foo()
@@ -100,10 +123,14 @@ gdb_test "step" "main \\(\\) at.*" "$test (3)"

  gdb_test "skip disable 3"
  # Is entry 3 disabled in |info skip|?
-gdb_test "info skip 3" ".*\\n3\\s+file\\s+n.*" \
-  "info skip shows entry as disabled"
+gdb_test "info skip 3" \
+    "3\\s+n\\s+n\\s+$srcfile1\\s+n\\s+<none>\\s*" \
+    "info skip shows entry as disabled"

-if ![runto_main] { fail "skip tests suppressed" }
+if ![runto_main] {
+    fail "Can't run to main"
+    return
+}
  set test "step after disabling 3"
  gdb_test "step" "bar \\(\\) at.*" "$test (1)"
  gdb_test "step" ".*" "$test (2)"; # Return from foo()
@@ -115,9 +142,13 @@ gdb_test "step" "main \\(\\) at.*" "$test (5)"

  gdb_test "skip enable 3"
  # Is entry 3 enabled in |info skip|?
-gdb_test "info skip 3" ".*\\n3\\s+file\\s+y.*" \
-  "info skip shows entry as enabled"
-if ![runto_main] { fail "skip tests suppressed" }
+gdb_test "info skip 3" \
+    "3\\s+y\\s+n\\s+$srcfile1\\s+n\\s+<none>\\s*" \
+    "info skip shows entry as enabled"
+if ![runto_main] {
+    fail "Can't run to main"
+    return
+}
  set test "step after enable 3"
  gdb_test "step" "foo \\(\\) at.*" "$test (1)"
  gdb_test "step" ".*" "$test (2)"; # Return from foo()
@@ -125,47 +156,129 @@ gdb_test "step" "main \\(\\) at.*" "$test (3)"

  gdb_test "skip disable"
  gdb_test "info skip" \
-  "Num\\s+Type\\s+Enb\\s+What\\s*
-2\\s+function\\s+n\\s+main\\s*
-3\\s+file\\s+n\\s+$srcfile1\\s*
-4\\s+function\\s+n\\s+baz\\s*" \
-  "info skip after disabling all"
+    "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s*
+2\\s+n\\s+n\\s+<none>\\s+n\\s+main\\s*
+3\\s+n\\s+n\\s+$srcfile1\\s+n\\s+<none>\\s*
+4\\s+n\\s+n\\s+<none>\\s+n\\s+baz\\s*" \
+    "info skip after disabling all"

  gdb_test "skip enable"
  gdb_test "info skip" \
-  "Num\\s+Type\\s+Enb\\s+What\\s*
-2\\s+function\\s+y\\s+main\\s*
-3\\s+file\\s+y\\s+$srcfile1\\s*
-4\\s+function\\s+y\\s+baz\\s*" \
-  "info skip after enabling all"
+    "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s*
+2\\s+y\\s+n\\s+<none>\\s+n\\s+main\\s*
+3\\s+y\\s+n\\s+$srcfile1\\s+n\\s+<none>\\s*
+4\\s+y\\s+n\\s+<none>\\s+n\\s+baz\\s*" \
+    "info skip after enabling all"

  gdb_test "skip disable 4 2-3"
  gdb_test "info skip" \
-  "Num\\s+Type\\s+Enb\\s+What\\s*
-2\\s+function\\s+n\\s+main\\s*
-3\\s+file\\s+n\\s+$srcfile1\\s*
-4\\s+function\\s+n\\s+baz\\s*" \
-  "info skip after disabling 4 2-3"
+    "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s*
+2\\s+n\\s+n\\s+<none>\\s+n\\s+main\\s*
+3\\s+n\\s+n\\s+$srcfile1\\s+n\\s+<none>\\s*
+4\\s+n\\s+n\\s+<none>\\s+n\\s+baz\\s*" \
+    "info skip after disabling 4 2-3"

  gdb_test "skip enable 2-3"
  gdb_test "info skip" \
-  "Num\\s+Type\\s+Enb\\s+What\\s*
-2\\s+function\\s+y\\s+main\\s*
-3\\s+file\\s+y\\s+$srcfile1\\s*
-4\\s+function\\s+n\\s+baz\\s*" \
-  "info skip after enabling 2-3"
+    "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s*
+2\\s+y\\s+n\\s+<none>\\s+n\\s+main\\s*
+3\\s+y\\s+n\\s+$srcfile1\\s+n\\s+<none>\\s*
+4\\s+n\\s+n\\s+<none>\\s+n\\s+baz\\s*" \
+    "info skip after enabling 2-3"

  gdb_test "info skip 2-3" \
-  "Num\\s+Type\\s+Enb\\s+What\\s*
-2\\s+function\\s+y\\s+main\\s*
-3\\s+file\\s+y\\s+$srcfile1\\s*" \
-  "info skip 2-3"
+    "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s*
+2\\s+y\\s+n\\s+<none>\\s+n\\s+main\\s*
+3\\s+y\\s+n\\s+$srcfile1\\s+n\\s+<none>\\s*" \
+    "info skip 2-3"

  gdb_test "skip delete 2 3"
  gdb_test "info skip" \
-  "4\\s+function\\s+n\\s+baz\\s*" \
-  "info skip after deleting 2 3"
+    "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s*
+4\\s+n\\s+n\\s+<none>\\s+n\\s+baz\\s*" \
+    "info skip after deleting 2 3"

  gdb_test "skip delete"
  gdb_test "info skip" "Not skipping any files or functions\." \
-  "info skip after deleting all"
+    "info skip after deleting all"
+
+# Now test skip -fi, etc.
+
+# Create a skiplist entry for a specified file and function.
+gdb_test "skip -fi skip1.c" "File .*$srcfile1 will be skipped when  
stepping\."
+gdb_test "skip -gfi sk*1.c" "File\\(s\\) sk\\*1.c will be skipped when  
stepping\."
+gdb_test "skip -fu baz" "Function baz will be skipped when stepping\."
+gdb_test "skip -rfu ^b.z$" "Function\\(s\\) \\^b\\.z\\$ will be skipped  
when stepping."
+
+if ![runto_main] {
+    fail "Can't run to main"
+    return
+}
+
+set test "step using -fi"
+gdb_test_no_output "skip disable"
+gdb_test_no_output "skip enable 5"
+gdb_test "step" "foo \\(\\) at.*" "$test (1)"
+gdb_test "step" ".*" "$test (2)"; # Return from foo()
+gdb_test "step" "main \\(\\) at.*" "$test (3)"
+
+if ![runto_main] {
+    fail "Can't run to main"
+    return
+}
+
+set test "step using -gfi"
+gdb_test_no_output "skip disable"
+gdb_test_no_output "skip enable 6"
+gdb_test "step" "foo \\(\\) at.*" "$test (1)"
+gdb_test "step" ".*" "$test (2)"; # Return from foo()
+gdb_test "step" "main \\(\\) at.*" "$test (3)"
+
+if ![runto_main] {
+    fail "Can't run to main"
+    return
+}
+
+set test "step using -fu for baz"
+gdb_test_no_output "skip disable"
+gdb_test_no_output "skip enable 7"
+gdb_test "step" "bar \\(\\) at.*" "$test (1)"
+gdb_test "step" ".*" "$test (2)"; # Return from bar()
+gdb_test "step" "foo \\(\\) at.*" "$test (3)"
+gdb_test "step" ".*" "$test (4)"; # Return from foo()
+gdb_test "step" "main \\(\\) at.*" "$test (5)"
+
+if ![runto_main] {
+    fail "Can't run to main"
+    return
+}
+
+set test "step using -rfu for baz"
+gdb_test_no_output "skip disable"
+gdb_test_no_output "skip enable 8"
+gdb_test "step" "bar \\(\\) at.*" "$test (1)"
+gdb_test "step" ".*" "$test (2)"; # Return from bar()
+gdb_test "step" "foo \\(\\) at.*" "$test (3)"
+gdb_test "step" ".*" "$test (4)"; # Return from foo()
+gdb_test "step" "main \\(\\) at.*" "$test (5)"
+
+# Test -fi + -fu.
+
+if ![runto_main] {
+    fail "Can't run to main"
+    return
+}
+
+set test "step using -fi + -fu"
+gdb_test_no_output "skip delete"
+gdb_test "skip -fi skip1.c -fu test_skip" \
+    "Function test_skip in file skip1.c will be skipped when stepping\."
+gdb_breakpoint "test_skip_file_and_function"
+gdb_breakpoint "end_test_skip_file_and_function"
+gdb_test "call test_skip_file_and_function ()" "silently stop."
+# Verify we can step into skip.c:test_skip but not skip1.c:test_skip.
+gdb_test "step" "test_skip \\(\\) at.*" "$test (1)"
+gdb_test "step" "test_skip_file_and_function \\(\\) at.*" "$test (2)"; #  
Return from test_skip()
+gdb_test "step" "skip1_test_skip_file_and_function \\(\\) at.*" "$test (4)"
+gdb_test "step" ".*" "$test (5)"; # Skip over test_skip()
+gdb_test "step" "test_skip_file_and_function \\(\\) at.*" "$test (6)"; #  
Return from skip1_test_skip_file_and_function()
diff --git a/gdb/testsuite/gdb.base/skip1.c b/gdb/testsuite/gdb.base/skip1.c
index 9a06e62..7d94332 100644
--- a/gdb/testsuite/gdb.base/skip1.c
+++ b/gdb/testsuite/gdb.base/skip1.c
@@ -26,3 +26,14 @@ baz (int a)
  {
    return a + 1;
  }
+
+static void
+test_skip (void)
+{
+}
+
+void
+skip1_test_skip_file_and_function (void)
+{
+  test_skip ();
+}
diff --git a/gdb/testsuite/gdb.perf/skip-command.cc  
b/gdb/testsuite/gdb.perf/skip-command.cc
new file mode 100644
index 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..d251725
--- /dev/null
+++ b/gdb/testsuite/gdb.perf/skip-command.exp
@@ -0,0 +1,138 @@
+# Copyright (C) 2016 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <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
+    switch $kind {
+	"function" { set skip_arg "-function" }
+	"regexp" { set skip_arg "-rfunction" }
+	default {
+	    perror "bad skip kind: $kind"
+	    return
+	}
+    }
+    for { set i 0 } { $i < $nr_skips } { incr i } {
+	gdb_test "skip $skip_arg [format $text $i]" ""
+    }
+    # There could be 1000's of these, which can overflow the buffer.
+    # However, it's good to have this in the log, so we go to the effort
+    # to read it all in.
+    gdb_test_multiple "info skip" $test {
+	-re "\[^\r\n\]*\r\n" { exp_continue }
+	-re "\[\r\n\]*$gdb_prompt $" {
+	    pass $test
+	}
+	timeout {
+	    fail "$test (timeout)"
+	}
+    }
+}
+
+proc write_skip_func_source { file_name func_name_prefix nr_funcs } {
+    set f [open $file_name "w"]
+    puts $f "// DO NOT EDIT, machine generated file.  See  
skip-command.exp."
+    for { set i 0 } { $i < $nr_funcs } { incr i } {
+	set func_name [format "${func_name_prefix}_%02d" $i]
+	puts $f "int $func_name () { return 0; }"
+    }
+    close $f
+}
+
+proc run_skip_bench { kind text } {
+    global SKIP_STEP_COUNT SKIP_DIRECTIVE_COUNT
+
+    if ![runto_main] {
+	fail "Can't run to main"
+	return -1
+    }
+
+    gdb_test_no_output "set variable flag = 1"
+
+    for { set i 0 } { $i < 5 } { incr i } {
+	set nr_skips [expr $i * $SKIP_DIRECTIVE_COUNT]
+	install_skips $kind $text $nr_skips
+	gdb_test_no_output "python SkipCommand\(\"skip-$kind-$nr_skips\",  
${SKIP_STEP_COUNT}\).run()"
+    }
+
+    gdb_test "set variable flag = 0"
+}
+
+PerfTest::assemble {
+    global srcdir subdir srcfile binfile skip_func_file
+    global SKIP_DIRECTIVE_COUNT
+
+    write_skip_func_source $skip_func_file "skip_func" [expr 4 *  
$SKIP_DIRECTIVE_COUNT]
+    if { [gdb_compile [list "$srcdir/$subdir/$srcfile" $skip_func_file]  
${binfile} executable {c++ debug}] != "" } {
+	return -1
+    }
+    return 0
+} {
+    global binfile
+    clean_restart $binfile
+    return 0
+} {
+    global SKIP_STEP_COUNT SKIP_DIRECTIVE_COUNT
+
+    with_test_prefix "time_skip_func" {
+	# N.B. The function name must match the ones in skip-command.cc.
+	run_skip_bench "function" "skip_func_%02d"
+    }
+
+    with_test_prefix "time_skip_constructor" {
+	run_skip_bench "regexp" "^(skip_class_%02d)::\\1 *\\("
+    }
+
+    return 0
+}
diff --git a/gdb/testsuite/gdb.perf/skip-command.py  
b/gdb/testsuite/gdb.perf/skip-command.py
new file mode 100644
index 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)
diff --git a/gdb/utils.c b/gdb/utils.c
index e34c12e..977314b 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -3380,6 +3380,83 @@ gdb_filename_fnmatch (const char *pattern, const  
char *string, int flags)
    return fnmatch (pattern, string, flags);
  }

+/* Return the number of path elements in PATH.
+   / = 1
+   /foo = 2
+   /foo/ = 2
+   foo/bar = 2
+   foo/ = 1  */
+
+int
+count_path_elements (const char *path)
+{
+  int count = 0;
+  const char *p = path;
+
+  if (HAS_DRIVE_SPEC (p))
+    {
+      p = STRIP_DRIVE_SPEC (p);
+      ++count;
+    }
+
+  while (*p != '\0')
+    {
+      if (IS_DIR_SEPARATOR (*p))
+	++count;
+      ++p;
+    }
+
+  /* Backup one if last character is /, unless it's the only one.  */
+  if (p > path + 1 && IS_DIR_SEPARATOR (p[-1]))
+    --count;
+
+  /* Add one for the file name, if present.  */
+  if (p > path && !IS_DIR_SEPARATOR (p[-1]))
+    ++count;
+
+  return count;
+}
+
+/* Remove N leading path elements from PATH.
+   N must be non-negative.
+   If PATH has more than N path elements then return NULL.
+   If PATH has exactly N path elements then return "".
+   See count_path_elements for a description of how we do the counting.  */
+
+const char *
+strip_leading_path_elements (const char *path, int n)
+{
+  int i = 0;
+  const char *p = path;
+
+  gdb_assert (n >= 0);
+
+  if (n == 0)
+    return p;
+
+  if (HAS_DRIVE_SPEC (p))
+    {
+      p = STRIP_DRIVE_SPEC (p);
+      ++i;
+    }
+
+  while (i < n)
+    {
+      while (*p != '\0' && !IS_DIR_SEPARATOR (*p))
+	++p;
+      if (*p == '\0')
+	{
+	  if (i + 1 == n)
+	    return "";
+	  return NULL;
+	}
+      ++p;
+      ++i;
+    }
+
+  return p;
+}
+
  /* Provide a prototype to silence -Wmissing-prototypes.  */
  extern initialize_file_ftype _initialize_utils;

diff --git a/gdb/utils.h b/gdb/utils.h
index 4571aaf..0687c86 100644
--- a/gdb/utils.h
+++ b/gdb/utils.h
@@ -135,6 +135,10 @@ extern void substitute_path_component (char **stringp,  
const char *from,
  				       const char *to);

  char *ldirname (const char *filename);
+
+extern int count_path_elements (const char *path);
+
+extern const char *strip_leading_path_elements (const char *path, int n);
  \f
  /* GDB output, ui_file utilities.  */


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH, doc RFA] Add "skip regexp"
  2016-02-17  1:07 Doug Evans
@ 2016-02-17 16:07 ` Eli Zaretskii
  2016-02-29 18:55 ` Simon Marchi
  1 sibling, 0 replies; 14+ messages in thread
From: Eli Zaretskii @ 2016-02-17 16:07 UTC (permalink / raw)
  To: Doug Evans; +Cc: palves, gdb-patches

> Date: Wed, 17 Feb 2016 01:07:38 +0000
> From: Doug Evans <dje@google.com>
> Cc: gdb-patches@sourceware.org
> 
> Eli: Heads up, rewritten docs, needing re-approval.

Thanks for the heads-up.

> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -127,6 +127,14 @@ show max-value-size
>     allocate for value contents.  Prevents incorrect programs from
>     causing GDB to allocate overly large buffers.  Default is 64k.
> 
> +skip -file file
> +skip -gfile file-glob-pattern
> +skip -function function
> +skip -rfunction regular-expression
> +  A generalized form of the skip command, with new support for
> +  glob-style file names and regular expressions for function names.
> +  Additionally, a file spec and a function spec may now be combined.
> +

This part is OK.

> +@item -gfile @var{file-glob-pattern}
> +@itemx -gfi @var{file-glob-pattern}
> +Functions in files matching @var{file-glob-pattern} will be skipped
> +over when stepping.
> +
> +@smallexample
> +(gdb) skip -gfi utils/*.c
> +@end smallexample

Is there a way to protect wildcard characters?  If so, I think we
should mention it.

> +For example, there is generally no need to step into C++ std::string

C@t{++}, and "std::string" should be in @code.

Also, this description should have a couple of @cindex entries, as
readers are likely to look for it without remembering that the
command's name is "skip".

The documentation is OK with those fixed.

Thanks.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH, doc RFA] Add "skip regexp"
@ 2016-02-17  1:07 Doug Evans
  2016-02-17 16:07 ` Eli Zaretskii
  2016-02-29 18:55 ` Simon Marchi
  0 siblings, 2 replies; 14+ messages in thread
From: Doug Evans @ 2016-02-17  1:07 UTC (permalink / raw)
  To: Pedro Alves, eliz; +Cc: gdb-patches

Eli: Heads up, rewritten docs, needing re-approval.

Pedro Alves writes:
  > 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" ?

I started out with something like that, but regular expressions
of files runs into basenames_may_differ, and I didn't want to get
bogged down with how to deal with that (I don't want to give up
basenames_may_differ here, and that suggests separating the directory
regexp from the filename regexp). I hadn't thought of having
a name like "rfunction" though. Fine by me if it's ok with everyone else.

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

IWBN alright, but then (IMO) we have to go all out into supporting
embedded spaces, quoting, etc. If we're ok with imposing what buildargv
will require on users then ok, I don't want to have to code anything else.
It will impose on users various quoting rules (e.g., \ becomes \\,
and so on) whereas "skip rfunction blah" can just say that blah is
taken "as is" (setting aside leading/trailing whitespace which I'm ok with).

Plus we'll have to think about the syntax for file regular expressions.
Supporting foo.*/bar.* is out IMO, we need -rdirectory foo.* -rfile bar.*.
As discussed on IRC, glob-style patterns for file names is ok with you,
so the patch below uses -gfile.
I know we talked about having -file take either,
but it was simpler/clearer to distinguish -file from -gfile.

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

The functions in earlier versions of the patch didn't all
take the same arguments, and I like the extra bit of
abstraction. But ok.

2016-02-16  Doug Evans  <dje@google.com>

	Extend "skip" command to support -file, -gfile, -function, -rfunction.
	* NEWS: Document new features.
	* skip.c: #include "fnmatch.h", "gdb_regex.h".
	(skiplist_entry) <file>: Renamed from filename.
	<function>: Renamed from function_name.
	<file_is_glob, function_is_regexp>: New members.
	<compiled_function_regexp, compiled_function_regexp_is_valid>:
	New members.
	(make_skip_entry): New function.
	(free_skiplist_entry, free_skiplist_entry_cleanup): New functions.
	(make_free_skiplist_entry_cleanup): New function.
	(skip_file_command): Update.
	(skip_function, skip_function_command): Update.
	(compile_skip_regexp): New functions.
	(skip_command): Add support for new options.
	(skip_info): Update.
	(skip_file_p, skip_gfile_p): New functions.
	(skip_function_p, skip_rfunction_p): New functions.
	(function_name_is_marked_for_skip): Update and simplify.
	(_initialize_step_skip): Update.
	* symtab.c: #include "fnmatch.h".
	(compare_glob_filenames_for_search): New function.
	* symtab.h (compare_glob_filenames_for_search): Declare.
	* utils.c (count_path_elements): New function.
	(strip_leading_path_elements): New function.
	* utils.h (count_path_elements): Declare.
	(strip_leading_path_elements): Declare.

	doc/
	* gdb.texinfo (Skipping Over Functions and Files): Document new
	options to "skip" command.  Update docs of output of "info skip".

	testsuite/
	* gdb.base/skip.c (test_skip): New function.
	(end_test_skip_file_and_function): New function.
	(test_skip_file_and_function): New function.
	* gdb.base/skip1.c (test_skip): New function.
	(skip1_test_skip_file_and_function): New function.
	* gdb.base/skip.exp: Add tests for new skip options.
	* gdb.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 482bec6..9175ddbf 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -127,6 +127,14 @@ show max-value-size
    allocate for value contents.  Prevents incorrect programs from
    causing GDB to allocate overly large buffers.  Default is 64k.

+skip -file file
+skip -gfile file-glob-pattern
+skip -function function
+skip -rfunction regular-expression
+  A generalized form of the skip command, with new support for
+  glob-style file names and regular expressions for function names.
+  Additionally, a file spec and a function spec may now be combined.
+
  * The "disassemble" command accepts a new modifier: /s.
    It prints mixed source+disassembly like /m with two differences:
    - disassembled instructions are now printed in program order, and
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 9db234e..2509293 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -5528,7 +5528,8 @@ default is @code{on}.

  The program you are debugging may contain some functions which are
  uninteresting to debug.  The @code{skip} command lets you tell  
@value{GDBN} to
-skip a function or all functions in a file when stepping.
+skip a function, all functions in a file or a particular function in
+a particular file when stepping.

  For example, consider the following C function:

@@ -5555,13 +5556,73 @@ A more flexible solution is to execute @kbd{skip  
boring}.  This instructs
  @code{step} at line 103, you'll step over @code{boring} and directly into
  @code{foo}.

-You can also instruct @value{GDBN} to skip all functions in a file, with,  
for
-example, @code{skip file boring.c}.
+Functions may be skipped by providing either a function name, linespec
+(@pxref{Specify Location}), regular expression that matches the function's
+name, file name or a @code{glob}-style pattern that matches the file name.
+
+On Posix systems the form of the regular expression is
+``Extended Regular Expressions''.  See for example @samp{man 7 regex}
+on @sc{gnu}/Linux systems.  On non-Posix systems the form of the regular
+expression is whatever is provided by the @code{regcomp} function of
+the underlying system.
+See for example @samp{man 7 glob} on @sc{gnu}/Linux systems for a
+description of @code{glob}-style patterns.
+
+@table @code
+@kindex skip
+@item skip @r{[}@var{options}@r{]}
+The basic form of the @code{skip} command takes zero or more options
+that specify what to skip.
+The @var{options} argument is any useful combination of the following:

  @table @code
+@item -file @var{file}
+@itemx -fi @var{file}
+Functions in @var{file} will be skipped over when stepping.
+
+@item -gfile @var{file-glob-pattern}
+@itemx -gfi @var{file-glob-pattern}
+Functions in files matching @var{file-glob-pattern} will be skipped
+over when stepping.
+
+@smallexample
+(gdb) skip -gfi utils/*.c
+@end smallexample
+
+@item -function @var{linespec}
+@itemx -fu @var{linespec}
+Functions named by @var{linespec} or the function containing the line
+named by @var{linespec} will be skipped over when stepping.
+@xref{Specify Location}.
+
+@item -rfunction @var{regexp}
+@itemx -rfu @var{regexp}
+Functions whose name matches @var{regexp} will be skipped over when  
stepping.
+
+This form is useful for complex function names.
+For example, there is generally no need to step into C++ 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.
+
+@smallexample
+(gdb) skip -rfu ^std::(allocator|basic_string)<.*>::~?\1 *\(
+@end smallexample
+
+If you want to skip every templated C++ constructor and destructor
+in the @code{std} namespace you can do:
+
+@smallexample
+(gdb) skip -rfu ^std::([a-zA-z0-9_]+)<.*>::~?\1 *\(
+@end smallexample
+@end table
+
+If no options are specified, the function you're currently debugging
+will be skipped.
+
  @kindex skip function
-@item skip @r{[}@var{linespec}@r{]}
-@itemx skip function @r{[}@var{linespec}@r{]}
+@item skip function @r{[}@var{linespec}@r{]}
  After running this command, the function named by @var{linespec} or the
  function containing the line named by @var{linespec} will be skipped over  
when
  stepping.  @xref{Specify Location}.
@@ -5577,6 +5638,11 @@ will be skipped.
  After running this command, any function whose source lives in  
@var{filename}
  will be skipped over when stepping.

+@smallexample
+(gdb) skip file boring.c
+File boring.c will be skipped when stepping.
+@end smallexample
+
  If you do not specify @var{filename}, functions whose source lives in the  
file
  you're currently debugging will be skipped.
  @end table
@@ -5594,20 +5660,21 @@ print a table with details about all functions and  
files marked for skipping.
  @table @emph
  @item Identifier
  A number identifying this skip.
-@item Type
-The type of this skip, either @samp{function} or @samp{file}.
  @item Enabled or Disabled
-Enabled skips are marked with @samp{y}.  Disabled skips are marked with  
@samp{n}.
-@item Address
-For function skips, this column indicates the address in memory of the  
function
-being skipped.  If you've set a function skip on a function which has not  
yet
-been loaded, this field will contain @samp{<PENDING>}.  Once a shared  
library
-which has the function is loaded, @code{info skip} will show the function's
-address here.
-@item What
-For file skips, this field contains the filename being skipped.  For  
functions
-skips, this field contains the function name and its line number in the  
file
-where it is defined.
+Enabled skips are marked with @samp{y}.
+Disabled skips are marked with @samp{n}.
+@item Glob
+If the file name is a @samp{glob} pattern this is @samp{y}.
+Otherwise it is @samp{n}.
+@item File
+The name or @samp{glob} pattern of the file to be skipped.
+If no file is specified this is @samp{<none>}.
+@item RE
+If the function name is a @samp{regular expression} this is @samp{y}.
+Otherwise it is @samp{n}.
+@item Function
+The name or regular expression of the function to skip.
+If no function is specified this is @samp{<none>}.
  @end table

  @kindex skip delete
diff --git a/gdb/skip.c b/gdb/skip.c
index d90910d..15681cc 100644
--- a/gdb/skip.c
+++ b/gdb/skip.c
@@ -32,19 +32,35 @@
  #include "breakpoint.h" /* for get_sal_arch () */
  #include "source.h"
  #include "filenames.h"
+#include "fnmatch.h"
+#include "gdb_regex.h"

  struct skiplist_entry
  {
    int number;

-  /* NULL if this isn't a skiplist entry for an entire file.
+  /* Non-zero if FILE is a glob-style pattern.
+     Otherewise it is the plain file name (possibly with directories).  */
+  int file_is_glob;
+
+  /* The name of the file or NULL.
       The skiplist entry owns this pointer.  */
-  char *filename;
+  char *file;
+
+  /* Non-zero if FUNCTION is a regexp.
+     Otherwise it is a plain function name (possibly with arguments,
+     for C++).  */
+  int function_is_regexp;

-  /* The name of the marked-for-skip function, if this is a skiplist
-     entry for a function.
+  /* The name of the function or NULL.
       The skiplist entry owns this pointer.  */
-  char *function_name;
+  char *function;
+
+  /* If this is a function regexp, the compiled form.  */
+  regex_t compiled_function_regexp;
+
+  /* Non-zero if the function regexp has been compiled.  */
+  int compiled_function_regexp_is_valid;

    int enabled;

@@ -52,7 +68,6 @@ struct skiplist_entry
  };

  static void add_skiplist_entry (struct skiplist_entry *e);
-static void skip_function (const char *name);

  static struct skiplist_entry *skiplist_entry_chain;
  static int skiplist_entry_count;
@@ -65,10 +80,62 @@ static int skiplist_entry_count;
         E ? (TMP = E->next, 1) : 0;       \
         E = TMP)

+/* Create a skip object.  */
+
+static struct skiplist_entry *
+make_skip_entry (int file_is_glob, const char *file,
+		 int function_is_regexp, const char *function)
+{
+  struct skiplist_entry *e = XCNEW (struct skiplist_entry);
+
+  gdb_assert (file != NULL || function != NULL);
+  if (file_is_glob)
+    gdb_assert (file != NULL);
+  if (function_is_regexp)
+    gdb_assert (function != NULL);
+
+  if (file != NULL)
+    e->file = xstrdup (file);
+  if (function != NULL)
+    e->function = xstrdup (function);
+  e->file_is_glob = file_is_glob;
+  e->function_is_regexp = function_is_regexp;
+  e->enabled = 1;
+
+  return e;
+}
+
+/* Free a skiplist entry.  */
+
+static void
+free_skiplist_entry (struct skiplist_entry *e)
+{
+  xfree (e->file);
+  xfree (e->function);
+  if (e->function_is_regexp && e->compiled_function_regexp_is_valid)
+    regfree (&e->compiled_function_regexp);
+  xfree (e);
+}
+
+/* Wrapper to free_skiplist_entry for use as a cleanup.  */
+
+static void
+free_skiplist_entry_cleanup (void *e)
+{
+  free_skiplist_entry ((struct skiplist_entry *) e);
+}
+
+/* Create a cleanup to free skiplist entry E.  */
+
+static struct cleanup *
+make_free_skiplist_entry_cleanup (struct skiplist_entry *e)
+{
+  return make_cleanup (free_skiplist_entry_cleanup, e);
+}
+
  static void
  skip_file_command (char *arg, int from_tty)
  {
-  struct skiplist_entry *e;
    struct symtab *symtab;
    const char *filename = NULL;

@@ -85,37 +152,31 @@ skip_file_command (char *arg, int from_tty)
        filename = symtab_to_fullname (symtab);
      }
    else
-    {
-      symtab = lookup_symtab (arg);
-      if (symtab == NULL)
-	{
-	  fprintf_filtered (gdb_stderr, _("No source file named %s.\n"), arg);
-	  if (!nquery (_("\
-Ignore file pending future shared library load? ")))
-	    return;
-	}
-      /* Do not use SYMTAB's filename, later loaded shared libraries may  
match
-         given ARG but not SYMTAB's filename.  */
-      filename = arg;
-    }
+    filename = arg;

-  e = XCNEW (struct skiplist_entry);
-  e->filename = xstrdup (filename);
-  e->enabled = 1;
-
-  add_skiplist_entry (e);
+  add_skiplist_entry (make_skip_entry (0, filename, 0, NULL));

    printf_filtered (_("File %s will be skipped when stepping.\n"),  
filename);
  }

+/* Create a skiplist entry for the given function NAME and add it to the
+   list.  */
+
  static void
-skip_function_command (char *arg, int from_tty)
+skip_function (const char *name)
  {
-  const char *name = NULL;
+  add_skiplist_entry (make_skip_entry (0, NULL, 0, name));
+
+  printf_filtered (_("Function %s will be skipped when stepping.\n"),  
name);
+}

+static void
+skip_function_command (char *arg, int from_tty)
+{
    /* Default to the current function if no argument is given.  */
    if (arg == NULL)
      {
+      const char *name = NULL;
        CORE_ADDR pc;

        if (!last_displayed_sal_is_valid ())
@@ -128,25 +189,169 @@ skip_function_command (char *arg, int from_tty)
  		  paddress (get_current_arch (), pc));
  	}
        skip_function (name);
+      return;
      }
-  else
+
+  skip_function (arg);
+}
+
+/* Compile the regexp in E.
+   An error is thrown if there's an error.
+   MESSAGE is used as a prefix of the error message.  */
+
+static void
+compile_skip_regexp (struct skiplist_entry *e, const char *message)
+{
+  int code;
+  int flags = REG_NOSUB;
+
+#ifdef REG_EXTENDED
+  flags |= REG_EXTENDED;
+#endif
+
+  gdb_assert (e->function_is_regexp && e->function != NULL);
+
+  code = regcomp (&e->compiled_function_regexp, e->function, flags);
+  if (code != 0)
      {
-      if (lookup_symbol (arg, NULL, VAR_DOMAIN, NULL).symbol == NULL)
-        {
-	  fprintf_filtered (gdb_stderr,
-			    _("No function found named %s.\n"), arg);
+      char *err = get_regcomp_error (code, &e->compiled_function_regexp);

-	  if (nquery (_("\
-Ignore function pending future shared library load? ")))
-	    {
-	      /* Add the unverified skiplist entry.  */
-	      skip_function (arg);
-	    }
+      make_cleanup (xfree, err);
+      error (_("%s: %s"), message, err);
+    }
+  e->compiled_function_regexp_is_valid = 1;
+}
+
+/* Process "skip ..." that does not match "skip file" or "skip function".   
*/
+
+static void
+skip_command (char *arg, int from_tty)
+{
+  const char *file = NULL;
+  const char *gfile = NULL;
+  const char *function = NULL;
+  const char *rfunction = NULL;
+  char **argv;
+  struct cleanup *cleanups;
+  struct skiplist_entry *e;
+  int i;
+
+  if (arg == NULL)
+    {
+      skip_function_command (arg, from_tty);
+      return;
+    }
+
+  argv = buildargv (arg);
+  cleanups = make_cleanup_freeargv (argv);
+
+  for (i = 0; argv[i] != NULL; ++i)
+    {
+      const char *p = argv[i];
+      const char *value = argv[i + 1];
+
+      if (strcmp (p, "-fi") == 0
+	  || strcmp (p, "-file") == 0)
+	{
+	  if (value == NULL)
+	    error (_("Missing value for %s option."), p);
+	  file = value;
+	  ++i;
+	}
+      else if (strcmp (p, "-gfi") == 0
+	       || strcmp (p, "-gfile") == 0)
+	{
+	  if (value == NULL)
+	    error (_("Missing value for %s option."), p);
+	  gfile = value;
+	  ++i;
+	}
+      else if (strcmp (p, "-fu") == 0
+	       || strcmp (p, "-function") == 0)
+	{
+	  if (value == NULL)
+	    error (_("Missing value for %s option."), p);
+	  function = value;
+	  ++i;
+	}
+      else if (strcmp (p, "-rfu") == 0
+	       || strcmp (p, "-rfunction") == 0)
+	{
+	  if (value == NULL)
+	    error (_("Missing value for %s option."), p);
+	  rfunction = value;
+	  ++i;
+	}
+      else if (*p == '-')
+	error (_("Invalid skip option: %s"), p);
+      else if (i == 0)
+	{
+	  /* Assume the user entered "skip FUNCTION-NAME".
+	     FUNCTION-NAME may be `foo (int)', and therefore we pass the
+	     complete original arg to skip_function command as if the user
+	     typed "skip function arg".  */
+	  do_cleanups (cleanups);
+	  skip_function_command (arg, from_tty);
  	  return;
  	}
+      else
+	error (_("Invalid argument: %s"), p);
+    }
+
+  if (file != NULL && gfile != NULL)
+    error (_("Cannot specify both -file and -gfile."));
+
+  if (function != NULL && rfunction != NULL)
+    error (_("Cannot specify both -function and -rfunction."));
+
+  /* This shouldn't happen as "skip" by itself gets punted to
+     skip_function_command.  */
+  gdb_assert (file != NULL || gfile != NULL
+	      || function != NULL || rfunction != NULL);
+
+  e = make_skip_entry (gfile != NULL, file ? file : gfile,
+		       rfunction != NULL, function ? function : rfunction);
+  if (rfunction != NULL)
+    {
+      struct cleanup *rf_cleanups = make_free_skiplist_entry_cleanup (e);

-      skip_function (arg);
+      compile_skip_regexp (e, _("regexp"));
+      discard_cleanups (rf_cleanups);
      }
+  add_skiplist_entry (e);
+
+  /* I18N concerns drive some of the choices here (we can't piece together
+     the output too much).  OTOH we want to keep this simple.  Therefore  
the
+     only polish we add to the output is to append "(s)" to "File" or
+     "Function" if they're a glob/regexp.  */
+  {
+    const char *file_to_print = file != NULL ? file : gfile;
+    const char *function_to_print = function != NULL ? function :  
rfunction;
+    const char *file_text = gfile != NULL ? _("File(s)") : _("File");
+    const char *lower_file_text = gfile != NULL ? _("file(s)") : _("file");
+    const char *function_text
+      = rfunction != NULL ? _("Function(s)") : _("Function");
+
+    if (function_to_print == NULL)
+      {
+	printf_filtered (_("%s %s will be skipped when stepping.\n"),
+			 file_text, file_to_print);
+      }
+    else if (file_to_print == NULL)
+      {
+	printf_filtered (_("%s %s will be skipped when stepping.\n"),
+			 function_text, function_to_print);
+      }
+    else
+      {
+	printf_filtered (_("%s %s in %s %s will be skipped"
+			   " when stepping.\n"),
+			 function_text, function_to_print,
+			 lower_file_text, file_to_print);
+      }
+  }
+
+  do_cleanups (cleanups);
  }

  static void
@@ -177,14 +382,17 @@ Not skipping any files or functions.\n"));
        return;
      }

-  tbl_chain = make_cleanup_ui_out_table_begin_end (current_uiout, 4,
+  tbl_chain = make_cleanup_ui_out_table_begin_end (current_uiout, 6,
  						   num_printable_entries,
  						   "SkiplistTable");

-  ui_out_table_header (current_uiout, 7, ui_left, "number", "Num");       
/* 1 */
-  ui_out_table_header (current_uiout, 14, ui_left, "type", "Type");       
/* 2 */
-  ui_out_table_header (current_uiout, 3, ui_left, "enabled", "Enb");      
/* 3 */
-  ui_out_table_header (current_uiout, 40, ui_noalign, "what", "What");    
/* 4 */
+  ui_out_table_header (current_uiout, 5, ui_left, "number", "Num");   /* 1  
*/
+  ui_out_table_header (current_uiout, 3, ui_left, "enabled", "Enb");  /* 2  
*/
+  ui_out_table_header (current_uiout, 4, ui_right, "regexp", "Glob"); /* 3  
*/
+  ui_out_table_header (current_uiout, 20, ui_left, "file", "File");   /* 4  
*/
+  ui_out_table_header (current_uiout, 2, ui_right, "regexp", "RE");   /* 5  
*/
+  ui_out_table_header (current_uiout, 40, ui_noalign,
+		       "function", "Function"); /* 6 */
    ui_out_table_body (current_uiout);

    ALL_SKIPLIST_ENTRIES (e)
@@ -197,25 +405,27 @@ Not skipping any files or functions.\n"));

        entry_chain = make_cleanup_ui_out_tuple_begin_end (current_uiout,
  							 "blklst-entry");
-      ui_out_field_int (current_uiout, "number", e->number);              
/* 1 */
+      ui_out_field_int (current_uiout, "number", e->number); /* 1 */

-      if (e->function_name != NULL)
-	ui_out_field_string (current_uiout, "type", "function");         /* 2 */
-      else if (e->filename != NULL)
-	ui_out_field_string (current_uiout, "type", "file");             /* 2 */
+      if (e->enabled)
+	ui_out_field_string (current_uiout, "enabled", "y"); /* 2 */
        else
-	internal_error (__FILE__, __LINE__, _("\
-Skiplist entry should have either a filename or a function name."));
+	ui_out_field_string (current_uiout, "enabled", "n"); /* 2 */

-      if (e->enabled)
-	ui_out_field_string (current_uiout, "enabled", "y");             /* 3 */
+      if (e->file_is_glob)
+	ui_out_field_string (current_uiout, "regexp", "y"); /* 3 */
        else
-	ui_out_field_string (current_uiout, "enabled", "n");             /* 3 */
+	ui_out_field_string (current_uiout, "regexp", "n"); /* 3 */

-      if (e->function_name != NULL)
-	ui_out_field_string (current_uiout, "what", e->function_name);	 /* 4 */
-      else if (e->filename != NULL)
-	ui_out_field_string (current_uiout, "what", e->filename);	 /* 4 */
+      ui_out_field_string (current_uiout, "file",
+			   e->file ? e->file : "<none>"); /* 4 */
+      if (e->function_is_regexp)
+	ui_out_field_string (current_uiout, "regexp", "y"); /* 5 */
+      else
+	ui_out_field_string (current_uiout, "regexp", "n"); /* 5 */
+
+      ui_out_field_string (current_uiout, "function",
+			   e->function ? e->function : "<none>"); /* 6 */

        ui_out_text (current_uiout, "\n");
        do_cleanups (entry_chain);
@@ -273,9 +483,7 @@ skip_delete_command (char *arg, int from_tty)
  	else
  	  skiplist_entry_chain = e->next;

-	xfree (e->function_name);
-	xfree (e->filename);
-	xfree (e);
+	free_skiplist_entry (e);
          found = 1;
        }
      else
@@ -287,22 +495,6 @@ skip_delete_command (char *arg, int from_tty)
      error (_("No skiplist entries found with number %s."), arg);
  }

-/* Create a skiplist entry for the given function NAME and add it to the
-   list.  */
-
-static void
-skip_function (const char *name)
-{
-  struct skiplist_entry *e = XCNEW (struct skiplist_entry);
-
-  e->enabled = 1;
-  e->function_name = xstrdup (name);
-
-  add_skiplist_entry (e);
-
-  printf_filtered (_("Function %s will be skipped when stepping.\n"),  
name);
-}
-
  /* Add the given skiplist entry to our list, and set the entry's number.   
*/

  static void
@@ -326,6 +518,98 @@ add_skiplist_entry (struct skiplist_entry *e)
      }
  }

+/* Return non-zero if we're stopped at a file to be skipped.  */
+
+static int
+skip_file_p (struct skiplist_entry *e,
+	     const struct symtab_and_line *function_sal)
+{
+  gdb_assert (e->file != NULL && !e->file_is_glob);
+
+  if (function_sal->symtab == NULL)
+    return 0;
+
+  /* Check first sole SYMTAB->FILENAME.  It may not be a substring of
+     symtab_to_fullname as it may contain "./" etc.  */
+  if (compare_filenames_for_search (function_sal->symtab->filename,  
e->file))
+    return 1;
+
+  /* Before we invoke realpath, which can get expensive when many
+     files are involved, do a quick comparison of the basenames.  */
+  if (!basenames_may_differ
+      && filename_cmp (lbasename (function_sal->symtab->filename),
+		       lbasename (e->file)) != 0)
+    return 0;
+
+  /* Note: symtab_to_fullname caches its result, thus we don't have to.  */
+  {
+    const char *fullname = symtab_to_fullname (function_sal->symtab);
+
+    if (compare_filenames_for_search (fullname, e->file))
+      return 1;
+  }
+
+  return 0;
+}
+
+/* Return non-zero if we're stopped at a globbed file to be skipped.  */
+
+static int
+skip_gfile_p (struct skiplist_entry *e,
+	      const struct symtab_and_line *function_sal)
+{
+  gdb_assert (e->file != NULL && e->file_is_glob);
+
+  if (function_sal->symtab == NULL)
+    return 0;
+
+  /* Check first sole SYMTAB->FILENAME.  It may not be a substring of
+     symtab_to_fullname as it may contain "./" etc.  */
+  if (gdb_filename_fnmatch (e->file, function_sal->symtab->filename,
+			    FNM_FILE_NAME | FNM_NOESCAPE) == 0)
+    return 1;
+
+  /* Before we invoke symtab_to_fullname, which is expensive, do a quick
+     comparison of the basenames.
+     Note that we assume that lbasename works with glob-style patterns.
+     If the basename of the glob pattern is something like "*.c" then this
+     isn't much of a win.  Oh well.  */
+  if (!basenames_may_differ
+      && gdb_filename_fnmatch (lbasename (e->file),
+			       lbasename (function_sal->symtab->filename),
+			       FNM_FILE_NAME | FNM_NOESCAPE) != 0)
+    return 0;
+
+  /* Note: symtab_to_fullname caches its result, thus we don't have to.  */
+  {
+    const char *fullname = symtab_to_fullname (function_sal->symtab);
+
+    if (compare_glob_filenames_for_search (fullname, e->file))
+      return 1;
+  }
+
+  return 0;
+}
+
+/* Return non-zero if we're stopped at a function to be skipped.  */
+
+static int
+skip_function_p (struct skiplist_entry *e, const char *function_name)
+{
+  gdb_assert (e->function != NULL && !e->function_is_regexp);
+  return strcmp_iw (function_name, e->function) == 0;
+}
+
+/* Return non-zero if we're stopped at a function regexp to be skipped.  */
+
+static int
+skip_rfunction_p (struct skiplist_entry *e, const char *function_name)
+{
+  gdb_assert (e->function != NULL && e->function_is_regexp
+	      && e->compiled_function_regexp_is_valid);
+  return (regexec (&e->compiled_function_regexp, function_name, 0, NULL, 0)
+	  == 0);
+}

  /* See skip.h.  */

@@ -333,8 +617,6 @@ int
  function_name_is_marked_for_skip (const char *function_name,
  				  const struct symtab_and_line *function_sal)
  {
-  int searched_for_fullname = 0;
-  const char *fullname = NULL;
    struct skiplist_entry *e;

    if (function_name == NULL)
@@ -342,43 +624,49 @@ function_name_is_marked_for_skip (const char  
*function_name,

    ALL_SKIPLIST_ENTRIES (e)
      {
+      int skip_by_file = 0;
+      int skip_by_function = 0;
+
        if (!e->enabled)
  	continue;

-      /* Does the pc we're stepping into match e's stored pc? */
-      if (e->function_name != NULL
-	  && strcmp_iw (function_name, e->function_name) == 0)
-	return 1;
-
-      if (e->filename != NULL)
+      if (e->file != NULL)
  	{
-	  /* Check first sole SYMTAB->FILENAME.  It does not need to be
-	     a substring of symtab_to_fullname as it may contain "./" etc.  */
-	  if (function_sal->symtab != NULL
-	      && compare_filenames_for_search (function_sal->symtab->filename,
-					       e->filename))
-	    return 1;
-
-	  /* Before we invoke realpath, which can get expensive when many
-	     files are involved, do a quick comparison of the basenames.  */
-	  if (!basenames_may_differ
-	      && (function_sal->symtab == NULL
-	          || filename_cmp (lbasename (function_sal->symtab->filename),
-				   lbasename (e->filename)) != 0))
-	    continue;
-
-	  /* Get the filename corresponding to this FUNCTION_SAL, if we haven't
-	     yet.  */
-	  if (!searched_for_fullname)
+	  if (e->file_is_glob)
+	    {
+	      if (skip_gfile_p (e, function_sal))
+		skip_by_file = 1;
+	    }
+	  else
  	    {
-	      if (function_sal->symtab != NULL)
-		fullname = symtab_to_fullname (function_sal->symtab);
-	      searched_for_fullname = 1;
+	      if (skip_file_p (e, function_sal))
+		skip_by_file = 1;
  	    }
-	  if (fullname != NULL
-	      && compare_filenames_for_search (fullname, e->filename))
+	}
+      if (e->function != NULL)
+	{
+	  if (e->function_is_regexp)
+	    {
+	      if (skip_rfunction_p (e, function_name))
+		skip_by_function = 1;
+	    }
+	  else
+	    {
+	      if (skip_function_p (e, function_name))
+		skip_by_function = 1;
+	    }
+	}
+
+      /* If both file and function must match, make sure we don't errantly
+	 exit if only one of them match.  */
+      if (e->file != NULL && e->function != NULL)
+	{
+	  if (skip_by_file && skip_by_function)
  	    return 1;
  	}
+      /* Only one of file/function is specified.  */
+      else if (skip_by_file || skip_by_function)
+	return 1;
      }

    return 0;
@@ -396,22 +684,31 @@ _initialize_step_skip (void)
    skiplist_entry_chain = 0;
    skiplist_entry_count = 0;

-  add_prefix_cmd ("skip", class_breakpoint, skip_function_command, _("\
+  add_prefix_cmd ("skip", class_breakpoint, skip_command, _("\
  Ignore a function while stepping.\n\
-Usage: skip [FUNCTION NAME]\n\
-If no function name is given, ignore the current function."),
+\n\
+Usage: skip [FUNCTION-NAME]\n\
+       skip [<file-spec>] [<function-spec>]\n\
+If no arguments are given, ignore the current function.\n\
+\n\
+<file-spec> is one of:\n\
+       -fi|-file FILE-NAME\n\
+       -gfi|-gfile GLOB-FILE-PATTERN\n\
+<function-spec> is one of:\n\
+       -fu|-function FUNCTION-NAME\n\
+       -rfu|-rfunction FUNCTION-NAME-REGULAR-EXPRESSION"),
                    &skiplist, "skip ", 1, &cmdlist);

    c = add_cmd ("file", class_breakpoint, skip_file_command, _("\
  Ignore a file while stepping.\n\
-Usage: skip file [FILENAME]\n\
+Usage: skip file [FILE-NAME]\n\
  If no filename is given, ignore the current file."),
  	       &skiplist);
    set_cmd_completer (c, filename_completer);

    c = add_cmd ("function", class_breakpoint, skip_function_command, _("\
  Ignore a function while stepping.\n\
-Usage: skip function [FUNCTION NAME]\n\
+Usage: skip function [FUNCTION-NAME]\n\
  If no function name is given, skip the current function."),
  	       &skiplist);
    set_cmd_completer (c, location_completer);
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 95b3a11..e06104b 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -41,7 +41,7 @@
  #include "p-lang.h"
  #include "addrmap.h"
  #include "cli/cli-utils.h"
-
+#include "fnmatch.h"
  #include "hashtab.h"

  #include "gdb_obstack.h"
@@ -342,6 +342,40 @@ compare_filenames_for_search (const char *filename,  
const char *search_name)
  	      && STRIP_DRIVE_SPEC (filename) == &filename[len - search_len]));
  }

+/* Same as compare_filenames_for_search, but for glob-style patterns.
+   Heads up on the order of the arguments.  They match the order of
+   compare_filenames_for_search, but it's the opposite of the order of
+   arguments to gdb_filename_fnmatch.  */
+
+int
+compare_glob_filenames_for_search (const char *filename,
+				   const char *search_name)
+{
+  /* We rely on the property of glob-style patterns with FNM_FILE_NAME that
+     all /s have to be explicitly specified.  */
+  int file_path_elements = count_path_elements (filename);
+  int search_path_elements = count_path_elements (search_name);
+
+  if (search_path_elements > file_path_elements)
+    return 0;
+
+  if (IS_ABSOLUTE_PATH (search_name))
+    {
+      return (search_path_elements == file_path_elements
+	      && gdb_filename_fnmatch (search_name, filename,
+				       FNM_FILE_NAME | FNM_NOESCAPE) == 0);
+    }
+
+  {
+    const char *file_to_compare
+      = strip_leading_path_elements (filename,
+				     file_path_elements - search_path_elements);
+
+    return gdb_filename_fnmatch (search_name, file_to_compare,
+				 FNM_FILE_NAME | FNM_NOESCAPE) == 0;
+  }
+}
+
  /* Check for a symtab of a specific name by searching some symtabs.
     This is a helper function for callbacks of iterate_over_symtabs.

diff --git a/gdb/symtab.h b/gdb/symtab.h
index f7884b9..6f00baf 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1600,6 +1600,9 @@ extern int basenames_may_differ;
  int compare_filenames_for_search (const char *filename,
  				  const char *search_name);

+int compare_glob_filenames_for_search (const char *filename,
+				       const char *search_name);
+
  int iterate_over_some_symtabs (const char *name,
  			       const char *real_path,
  			       int (*callback) (struct symtab *symtab,
diff --git a/gdb/testsuite/gdb.base/skip.c b/gdb/testsuite/gdb.base/skip.c
index 3568296..b9db2a7 100644
--- a/gdb/testsuite/gdb.base/skip.c
+++ b/gdb/testsuite/gdb.base/skip.c
@@ -20,6 +20,7 @@
  int foo (void);
  int bar (void);
  int baz (int);
+void skip1_test_skip_file_and_function (void);

  int
  main ()
@@ -33,3 +34,22 @@ foo ()
  {
    return 0;
  }
+
+static void
+test_skip (void)
+{
+}
+
+static void
+end_test_skip_file_and_function (void)
+{
+  abort ();
+}
+
+void
+test_skip_file_and_function (void)
+{
+  test_skip ();
+  skip1_test_skip_file_and_function ();
+  end_test_skip_file_and_function ();
+}
diff --git a/gdb/testsuite/gdb.base/skip.exp  
b/gdb/testsuite/gdb.base/skip.exp
index 40f3103..220b145 100644
--- a/gdb/testsuite/gdb.base/skip.exp
+++ b/gdb/testsuite/gdb.base/skip.exp
@@ -14,6 +14,7 @@
  # along with this program.  If not, see <http://www.gnu.org/licenses/>.

  # This file was written by Justin Lebar. (justin.lebar@gmail.com)
+# And further hacked on by Doug Evans. (dje@google.com)

  if { [prepare_for_testing skip.exp "skip" \
                            {skip.c skip1.c } \
@@ -30,7 +31,24 @@ gdb_test "skip file" "No default file now." "skip file  
(no default file)"
  gdb_test "skip function" "No default function now."
  gdb_test "skip" "No default function now." "skip (no default function)"

-if ![runto_main] { fail "skip tests suppressed" }
+# Test elided args.
+
+gdb_test "skip -fi" "Missing value for -fi option."
+gdb_test "skip -file" "Missing value for -file option."
+gdb_test "skip -fu" "Missing value for -fu option."
+gdb_test "skip -function" "Missing value for -function option."
+gdb_test "skip -rfu" "Missing value for -rfu option."
+gdb_test "skip -rfunction" "Missing value for -rfunction option."
+
+# Test other invalid option combinations.
+
+gdb_test "skip -x" "Invalid skip option: -x"
+gdb_test "skip -rfu foo.* xyzzy" "Invalid argument: xyzzy"
+
+if ![runto_main] {
+    fail "Can't run to main"
+    return
+}

  # Test |info skip| with an empty skiplist.

@@ -62,34 +80,39 @@ gdb_test "info skip 999" "No skiplist entries found  
with number 999."
  # Does |info skip| look right?

  gdb_test "info skip" \
-  "Num\\s+Type\\s+Enb\\s+What\\s*
-1\\s+file\\s+y\\s+.*$srcfile\\s*
-2\\s+function\\s+y\\s+main\\s*
-3\\s+file\\s+y\\s+$srcfile1\\s*
-4\\s+function\\s+y\\s+baz\\s*"
+    "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s*
+1\\s+y\\s+n\\s+.*$srcfile\\s+n\\s+<none>\\s*
+2\\s+y\\s+n\\s+<none>\\s+n\\s+main\\s*
+3\\s+y\\s+n\\s+$srcfile1\\s+n\\s+<none>\\s*
+4\\s+y\\s+n\\s+<none>\\s+n\\s+baz\\s*"

  # Right now, we have an outstanding skiplist entry on both source
  # files, so when we step into the first line in main(), we should step
  # right over it and go to the second line of main().

-if ![runto_main] { fail "skip tests suppressed" }
+if ![runto_main] {
+    fail "Can't run to main"
+    return
+}
  gdb_test "step" ".*" "step in the main"
  gdb_test "bt" "\\s*\\#0\\s+main.*" "step after all ignored"

  # Now remove skip.c from the skiplist.  Our first step should take us
-# into foo(), and our second step should take us to the next line in
-# main().
+# into foo(), and our second step should take us to the next line in  
main().

  gdb_test "skip delete 1"
  # Check that entry 1 is missing from |info skip|
  gdb_test "info skip" \
-  "Num\\s+Type\\s+Enb\\s+What\\s*
-2\\s+function\\s+y\\s+main\\s*
-3\\s+file\\s+y\\s+$srcfile1\\s*
-4\\s+function\\s+y\\s+baz\\s*" \
+    "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s*
+2\\s+y\\s+n\\s+<none>\\s+n\\s+main\\s*
+3\\s+y\\s+n\\s+$srcfile1\\s+n\\s+<none>\\s*
+4\\s+y\\s+n\\s+<none>\\s+n\\s+baz\\s*" \
      "info skip (delete 1)"

-if ![runto_main] { fail "skip tests suppressed" }
+if ![runto_main] {
+    fail "Can't run to main"
+    return
+}
  set test "step after deleting 1"
  gdb_test "step" "foo \\(\\) at.*" "$test (1)"
  gdb_test "step" ".*" "$test (2)" ; # Return from foo()
@@ -100,10 +123,14 @@ gdb_test "step" "main \\(\\) at.*" "$test (3)"

  gdb_test "skip disable 3"
  # Is entry 3 disabled in |info skip|?
-gdb_test "info skip 3" ".*\\n3\\s+file\\s+n.*" \
-  "info skip shows entry as disabled"
+gdb_test "info skip 3" \
+    "3\\s+n\\s+n\\s+$srcfile1\\s+n\\s+<none>\\s*" \
+    "info skip shows entry as disabled"

-if ![runto_main] { fail "skip tests suppressed" }
+if ![runto_main] {
+    fail "Can't run to main"
+    return
+}
  set test "step after disabling 3"
  gdb_test "step" "bar \\(\\) at.*" "$test (1)"
  gdb_test "step" ".*" "$test (2)"; # Return from foo()
@@ -115,9 +142,13 @@ gdb_test "step" "main \\(\\) at.*" "$test (5)"

  gdb_test "skip enable 3"
  # Is entry 3 enabled in |info skip|?
-gdb_test "info skip 3" ".*\\n3\\s+file\\s+y.*" \
-  "info skip shows entry as enabled"
-if ![runto_main] { fail "skip tests suppressed" }
+gdb_test "info skip 3" \
+    "3\\s+y\\s+n\\s+$srcfile1\\s+n\\s+<none>\\s*" \
+    "info skip shows entry as enabled"
+if ![runto_main] {
+    fail "Can't run to main"
+    return
+}
  set test "step after enable 3"
  gdb_test "step" "foo \\(\\) at.*" "$test (1)"
  gdb_test "step" ".*" "$test (2)"; # Return from foo()
@@ -125,47 +156,129 @@ gdb_test "step" "main \\(\\) at.*" "$test (3)"

  gdb_test "skip disable"
  gdb_test "info skip" \
-  "Num\\s+Type\\s+Enb\\s+What\\s*
-2\\s+function\\s+n\\s+main\\s*
-3\\s+file\\s+n\\s+$srcfile1\\s*
-4\\s+function\\s+n\\s+baz\\s*" \
-  "info skip after disabling all"
+    "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s*
+2\\s+n\\s+n\\s+<none>\\s+n\\s+main\\s*
+3\\s+n\\s+n\\s+$srcfile1\\s+n\\s+<none>\\s*
+4\\s+n\\s+n\\s+<none>\\s+n\\s+baz\\s*" \
+    "info skip after disabling all"

  gdb_test "skip enable"
  gdb_test "info skip" \
-  "Num\\s+Type\\s+Enb\\s+What\\s*
-2\\s+function\\s+y\\s+main\\s*
-3\\s+file\\s+y\\s+$srcfile1\\s*
-4\\s+function\\s+y\\s+baz\\s*" \
-  "info skip after enabling all"
+    "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s*
+2\\s+y\\s+n\\s+<none>\\s+n\\s+main\\s*
+3\\s+y\\s+n\\s+$srcfile1\\s+n\\s+<none>\\s*
+4\\s+y\\s+n\\s+<none>\\s+n\\s+baz\\s*" \
+    "info skip after enabling all"

  gdb_test "skip disable 4 2-3"
  gdb_test "info skip" \
-  "Num\\s+Type\\s+Enb\\s+What\\s*
-2\\s+function\\s+n\\s+main\\s*
-3\\s+file\\s+n\\s+$srcfile1\\s*
-4\\s+function\\s+n\\s+baz\\s*" \
-  "info skip after disabling 4 2-3"
+    "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s*
+2\\s+n\\s+n\\s+<none>\\s+n\\s+main\\s*
+3\\s+n\\s+n\\s+$srcfile1\\s+n\\s+<none>\\s*
+4\\s+n\\s+n\\s+<none>\\s+n\\s+baz\\s*" \
+    "info skip after disabling 4 2-3"

  gdb_test "skip enable 2-3"
  gdb_test "info skip" \
-  "Num\\s+Type\\s+Enb\\s+What\\s*
-2\\s+function\\s+y\\s+main\\s*
-3\\s+file\\s+y\\s+$srcfile1\\s*
-4\\s+function\\s+n\\s+baz\\s*" \
-  "info skip after enabling 2-3"
+    "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s*
+2\\s+y\\s+n\\s+<none>\\s+n\\s+main\\s*
+3\\s+y\\s+n\\s+$srcfile1\\s+n\\s+<none>\\s*
+4\\s+n\\s+n\\s+<none>\\s+n\\s+baz\\s*" \
+    "info skip after enabling 2-3"

  gdb_test "info skip 2-3" \
-  "Num\\s+Type\\s+Enb\\s+What\\s*
-2\\s+function\\s+y\\s+main\\s*
-3\\s+file\\s+y\\s+$srcfile1\\s*" \
-  "info skip 2-3"
+    "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s*
+2\\s+y\\s+n\\s+<none>\\s+n\\s+main\\s*
+3\\s+y\\s+n\\s+$srcfile1\\s+n\\s+<none>\\s*" \
+    "info skip 2-3"

  gdb_test "skip delete 2 3"
  gdb_test "info skip" \
-  "4\\s+function\\s+n\\s+baz\\s*" \
-  "info skip after deleting 2 3"
+    "Num\\s+Enb\\s+Glob\\s+File\\s+RE\\s+Function\\s*
+4\\s+n\\s+n\\s+<none>\\s+n\\s+baz\\s*" \
+    "info skip after deleting 2 3"

  gdb_test "skip delete"
  gdb_test "info skip" "Not skipping any files or functions\." \
-  "info skip after deleting all"
+    "info skip after deleting all"
+
+# Now test skip -fi, etc.
+
+# Create a skiplist entry for a specified file and function.
+gdb_test "skip -fi skip1.c" "File .*$srcfile1 will be skipped when  
stepping\."
+gdb_test "skip -gfi sk*1.c" "File\\(s\\) sk\\*1.c will be skipped when  
stepping\."
+gdb_test "skip -fu baz" "Function baz will be skipped when stepping\."
+gdb_test "skip -rfu ^b.z$" "Function\\(s\\) \\^b\\.z\\$ will be skipped  
when stepping."
+
+if ![runto_main] {
+    fail "Can't run to main"
+    return
+}
+
+set test "step using -fi"
+gdb_test_no_output "skip disable"
+gdb_test_no_output "skip enable 5"
+gdb_test "step" "foo \\(\\) at.*" "$test (1)"
+gdb_test "step" ".*" "$test (2)"; # Return from foo()
+gdb_test "step" "main \\(\\) at.*" "$test (3)"
+
+if ![runto_main] {
+    fail "Can't run to main"
+    return
+}
+
+set test "step using -gfi"
+gdb_test_no_output "skip disable"
+gdb_test_no_output "skip enable 6"
+gdb_test "step" "foo \\(\\) at.*" "$test (1)"
+gdb_test "step" ".*" "$test (2)"; # Return from foo()
+gdb_test "step" "main \\(\\) at.*" "$test (3)"
+
+if ![runto_main] {
+    fail "Can't run to main"
+    return
+}
+
+set test "step using -fu for baz"
+gdb_test_no_output "skip disable"
+gdb_test_no_output "skip enable 7"
+gdb_test "step" "bar \\(\\) at.*" "$test (1)"
+gdb_test "step" ".*" "$test (2)"; # Return from foo()
+gdb_test "step" "foo \\(\\) at.*" "$test (3)"
+gdb_test "step" ".*" "$test (4)"; # Return from bar()
+gdb_test "step" "main \\(\\) at.*" "$test (5)"
+
+if ![runto_main] {
+    fail "Can't run to main"
+    return
+}
+
+set test "step using -rfu for baz"
+gdb_test_no_output "skip disable"
+gdb_test_no_output "skip enable 8"
+gdb_test "step" "bar \\(\\) at.*" "$test (1)"
+gdb_test "step" ".*" "$test (2)"; # Return from foo()
+gdb_test "step" "foo \\(\\) at.*" "$test (3)"
+gdb_test "step" ".*" "$test (4)"; # Return from bar()
+gdb_test "step" "main \\(\\) at.*" "$test (5)"
+
+# Test -fi + -fu.
+
+if ![runto_main] {
+    fail "Can't run to main"
+    return
+}
+
+set test "step using -fi + -fu"
+gdb_test_no_output "skip delete"
+gdb_test "skip -fi skip1.c -fu test_skip" \
+    "Function test_skip in file skip1.c will be skipped when stepping\."
+gdb_breakpoint "test_skip_file_and_function"
+gdb_breakpoint "end_test_skip_file_and_function"
+gdb_test "call test_skip_file_and_function ()" "silently stop."
+# Verify we can step into skip.c:test_skip but not skip1.c:test_skip.
+gdb_test "step" "test_skip \\(\\) at.*" "$test (1)"
+gdb_test "step" "test_skip_file_and_function \\(\\) at.*" "$test (2)"; #  
Return from test_skip()
+gdb_test "step" "skip1_test_skip_file_and_function \\(\\) at.*" "$test (4)"
+gdb_test "step" ".*" "$test (5)"; # Skip over test_skip()
+gdb_test "step" "test_skip_file_and_function \\(\\) at.*" "$test (6)"; #  
Return from skip1_test_skip_file_and_function()
diff --git a/gdb/testsuite/gdb.base/skip1.c b/gdb/testsuite/gdb.base/skip1.c
index 9a06e62..7d94332 100644
--- a/gdb/testsuite/gdb.base/skip1.c
+++ b/gdb/testsuite/gdb.base/skip1.c
@@ -26,3 +26,14 @@ baz (int a)
  {
    return a + 1;
  }
+
+static void
+test_skip (void)
+{
+}
+
+void
+skip1_test_skip_file_and_function (void)
+{
+  test_skip ();
+}
diff --git a/gdb/testsuite/gdb.perf/skip-command.cc  
b/gdb/testsuite/gdb.perf/skip-command.cc
new file mode 100644
index 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..d251725
--- /dev/null
+++ b/gdb/testsuite/gdb.perf/skip-command.exp
@@ -0,0 +1,138 @@
+# Copyright (C) 2016 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <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
+    switch $kind {
+	"function" { set skip_arg "-function" }
+	"regexp" { set skip_arg "-rfunction" }
+	default {
+	    perror "bad skip kind: $kind"
+	    return
+	}
+    }
+    for { set i 0 } { $i < $nr_skips } { incr i } {
+	gdb_test "skip $skip_arg [format $text $i]" ""
+    }
+    # There could be 1000's of these, which can overflow the buffer.
+    # However, it's good to have this in the log, so we go to the effort
+    # to read it all in.
+    gdb_test_multiple "info skip" $test {
+	-re "\[^\r\n\]*\r\n" { exp_continue }
+	-re "\[\r\n\]*$gdb_prompt $" {
+	    pass $test
+	}
+	timeout {
+	    fail "$test (timeout)"
+	}
+    }
+}
+
+proc write_skip_func_source { file_name func_name_prefix nr_funcs } {
+    set f [open $file_name "w"]
+    puts $f "// DO NOT EDIT, machine generated file.  See  
skip-command.exp."
+    for { set i 0 } { $i < $nr_funcs } { incr i } {
+	set func_name [format "${func_name_prefix}_%02d" $i]
+	puts $f "int $func_name () { return 0; }"
+    }
+    close $f
+}
+
+proc run_skip_bench { kind text } {
+    global SKIP_STEP_COUNT SKIP_DIRECTIVE_COUNT
+
+    if ![runto_main] {
+	fail "Can't run to main"
+	return -1
+    }
+
+    gdb_test_no_output "set variable flag = 1"
+
+    for { set i 0 } { $i < 5 } { incr i } {
+	set nr_skips [expr $i * $SKIP_DIRECTIVE_COUNT]
+	install_skips $kind $text $nr_skips
+	gdb_test_no_output "python SkipCommand\(\"skip-$kind-$nr_skips\",  
${SKIP_STEP_COUNT}\).run()"
+    }
+
+    gdb_test "set variable flag = 0"
+}
+
+PerfTest::assemble {
+    global srcdir subdir srcfile binfile skip_func_file
+    global SKIP_DIRECTIVE_COUNT
+
+    write_skip_func_source $skip_func_file "skip_func" [expr 4 *  
$SKIP_DIRECTIVE_COUNT]
+    if { [gdb_compile [list "$srcdir/$subdir/$srcfile" $skip_func_file]  
${binfile} executable {c++ debug}] != "" } {
+	return -1
+    }
+    return 0
+} {
+    global binfile
+    clean_restart $binfile
+    return 0
+} {
+    global SKIP_STEP_COUNT SKIP_DIRECTIVE_COUNT
+
+    with_test_prefix "time_skip_func" {
+	# N.B. The function name must match the ones in skip-command.cc.
+	run_skip_bench "function" "skip_func_%02d"
+    }
+
+    with_test_prefix "time_skip_constructor" {
+	run_skip_bench "regexp" "^(skip_class_%02d)::\\1 *\\("
+    }
+
+    return 0
+}
diff --git a/gdb/testsuite/gdb.perf/skip-command.py  
b/gdb/testsuite/gdb.perf/skip-command.py
new file mode 100644
index 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)
diff --git a/gdb/utils.c b/gdb/utils.c
index e34c12e..977314b 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -3380,6 +3380,83 @@ gdb_filename_fnmatch (const char *pattern, const  
char *string, int flags)
    return fnmatch (pattern, string, flags);
  }

+/* Return the number of path elements in PATH.
+   / = 1
+   /foo = 2
+   /foo/ = 2
+   foo/bar = 2
+   foo/ = 1  */
+
+int
+count_path_elements (const char *path)
+{
+  int count = 0;
+  const char *p = path;
+
+  if (HAS_DRIVE_SPEC (p))
+    {
+      p = STRIP_DRIVE_SPEC (p);
+      ++count;
+    }
+
+  while (*p != '\0')
+    {
+      if (IS_DIR_SEPARATOR (*p))
+	++count;
+      ++p;
+    }
+
+  /* Backup one if last character is /, unless it's the only one.  */
+  if (p > path + 1 && IS_DIR_SEPARATOR (p[-1]))
+    --count;
+
+  /* Add one for the file name, if present.  */
+  if (p > path && !IS_DIR_SEPARATOR (p[-1]))
+    ++count;
+
+  return count;
+}
+
+/* Remove N leading path elements from PATH.
+   N must be non-negative.
+   If PATH has more than N path elements then return NULL.
+   If PATH has exactly N path elements then return "".
+   See count_path_elements for a description of how we do the counting.  */
+
+const char *
+strip_leading_path_elements (const char *path, int n)
+{
+  int i = 0;
+  const char *p = path;
+
+  gdb_assert (n >= 0);
+
+  if (n == 0)
+    return p;
+
+  if (HAS_DRIVE_SPEC (p))
+    {
+      p = STRIP_DRIVE_SPEC (p);
+      ++i;
+    }
+
+  while (i < n)
+    {
+      while (*p != '\0' && !IS_DIR_SEPARATOR (*p))
+	++p;
+      if (*p == '\0')
+	{
+	  if (i + 1 == n)
+	    return "";
+	  return NULL;
+	}
+      ++p;
+      ++i;
+    }
+
+  return p;
+}
+
  /* Provide a prototype to silence -Wmissing-prototypes.  */
  extern initialize_file_ftype _initialize_utils;

diff --git a/gdb/utils.h b/gdb/utils.h
index 4571aaf..0687c86 100644
--- a/gdb/utils.h
+++ b/gdb/utils.h
@@ -135,6 +135,10 @@ extern void substitute_path_component (char **stringp,  
const char *from,
  				       const char *to);

  char *ldirname (const char *filename);
+
+extern int count_path_elements (const char *path);
+
+extern const char *strip_leading_path_elements (const char *path, int n);
  \f
  /* GDB output, ui_file utilities.  */


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH, doc RFA] Add "skip regexp"
@ 2016-02-02 18:05 Doug Evans
  0 siblings, 0 replies; 14+ messages in thread
From: Doug Evans @ 2016-02-02 18:05 UTC (permalink / raw)
  To: Eli Zaretskii, gdb-patches

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)

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2016-03-15 19:45 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-02-02  1:03 [PATCH, doc RFA] Add "skip regexp" Doug Evans
2016-02-02 16:10 ` Eli Zaretskii
2016-02-04 13:47 ` Pedro Alves
2016-02-02 18:05 Doug Evans
2016-02-17  1:07 Doug Evans
2016-02-17 16:07 ` Eli Zaretskii
2016-02-29 18:55 ` Simon Marchi
2016-02-23 21:36 Doug Evans
2016-02-24  9:54 ` Joel Brobecker
2016-02-24 18:21   ` Doug Evans
2016-03-02 23:20 Doug Evans
2016-03-03 19:21 Doug Evans
2016-03-03 20:40 ` Simon Marchi
2016-03-15 19:45 Doug Evans

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).