From patchwork Tue Feb 2 01:03:19 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Doug Evans X-Patchwork-Id: 10699 Received: (qmail 22651 invoked by alias); 2 Feb 2016 01:03:28 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 22594 invoked by uid 89); 2 Feb 2016 01:03:25 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.4 required=5.0 tests=AWL, BAYES_50, RCVD_IN_DNSWL_LOW, RP_MATCHES_RCVD, SPF_PASS autolearn=ham version=3.3.2 spammy=es, sc, instruct, $subdir X-HELO: mail-pa0-f74.google.com Received: from mail-pa0-f74.google.com (HELO mail-pa0-f74.google.com) (209.85.220.74) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-GCM-SHA256 encrypted) ESMTPS; Tue, 02 Feb 2016 01:03:20 +0000 Received: by mail-pa0-f74.google.com with SMTP id uo6so10723390pac.0 for ; Mon, 01 Feb 2016 17:03:20 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:message-id:date:subject:from:to :content-type; bh=oIR6prxcjpTtcickY4//FTGnzM1oMD/vu78OPYEcn8Y=; b=Lh4d8ribnZvAUHYMXA6OM1umKeqieQRN/4+DWan4rIQ1nPnrf8A9ZLryFI7TeoukK8 qVbUHazI09BsxU58DYovDzZh/qOxIySj58NwbjTvsEdUQF0bOTFCVw/MOIbzTTZcKHks 3Qii2q5KS1nj7kYkidg44X5/WXDXL4hV8jyoMtz/MCsmlE98UraK8L7OUwGnWk1Sn2pf yEL/sil69d4qLlIL7KR6CksSnFER6rZlXq9QsyKJN39DeMPbjFhtn5cWGgmA4DYP9c/b 7+V//x5DXdEJX9OaTGF3jSFneuZamm/nYiZ3ljAxFwlOw1RRdoTCc1vfe+LEhc0nSaPS v8Vg== X-Gm-Message-State: AG10YOSPMNzSYWJ0mR1bCpcaLLHFc1ss07cphLsiC2FcIE7SYVC8VpvdHWT/83yePdKVi1xvJYfyP9psVIb/e/ulnqjPoAguEsARdI2UQrW0WjjsD4nuFYnqMTU8hmf0MVarUuqBIHE6xQgo5DoY93syk8TJHXsg/KUJ6e4NucLrhSgS/H2yhA== MIME-Version: 1.0 X-Received: by 10.98.72.199 with SMTP id q68mr17704594pfi.14.1454374999035; Mon, 01 Feb 2016 17:03:19 -0800 (PST) Message-ID: <94eb2c0b86103073b2052abf11e5@google.com> Date: Tue, 02 Feb 2016 01:03:19 +0000 Subject: [PATCH, doc RFA] Add "skip regexp" From: Doug Evans To: gdb-patches@sourceware.org X-IsSubscribed: yes 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::allocator (this=0x7fffffffe6bf) std::basic_string, std::allocator >::basic_string (this=0x7fffffffe6b0, __s=0x400ad1 "abc", __a=...) std::basic_string, std::allocator >::~basic_string (this=0x7fffffffe6b0, __in_chrg=) std::allocator::~allocator (this=0x7fffffffe6bf, __in_chrg=) 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 New command "skip regexp regular-expression". * NEWS: Document the new feature. * skip.c (skip_kind): New enum. (skiplist_entry) : Delete. : 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 . */ + +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 . + +# 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 . + +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)