[@option{-g}|@option{--conditions}]
[@option{-d}|@option{--display-progress}]
[@option{-f}|@option{--function-summaries}]
+ [@option{--include} @var{regex}]
+ [@option{--exclude} @var{regex}]
[@option{-j}|@option{--json-format}]
[@option{-H}|@option{--human-readable}]
[@option{-k}|@option{--use-colors}]
[@option{-l}|@option{--long-file-names}]
[@option{-m}|@option{--demangled-names}]
+ [@option{-M}|@option{--filter-on-demangled}]
[@option{-n}|@option{--no-output}]
[@option{-o}|@option{--object-directory} @var{directory|file}]
[@option{-p}|@option{--preserve-paths}]
@itemx --function-summaries
Output summaries for each function in addition to the file level summary.
+@item --include @var{regex}
+Include functions matching @var{regex}. This option makes
+@command{gcov} only report on functions that match the extended
+regular expression @var{regex}. This flag can be combined with
+@option{--exclude}. If a function matches both includes and excludes,
+the last include/exclude applies. By default @command{gcov} reports
+on all functions, but if a @command{--include} is used then only
+functions matching the include will be reported.
+
+@item --exclude @var{regex}
+Exclude functions matching @var{regex}. This option makes
+@command{gcov} not report on functions that match the extended regular
+expression @var{regex}. This flag can be combined with
+@option{--include}. If a function matches both includes and excludes,
+the last include/exclude applies. By default @command{gcov} reports
+on all functions, and if @option{--exclude} is used then functions
+matching it will be omitted.
+
@item -h
@itemx --help
Display help about using @command{gcov} (on the standard output), and
Display demangled function names in output. The default is to show
mangled function names.
+@item -M
+@itemx --filter-on-demangled
+Make @option{--include} and @option{--exclude} match demangled names.
+This does only affects the matching and does not imply
+@option{--demangled-names}, but it can safely be combined with it.
+
@item -n
@itemx --no-output
Do not create the @command{gcov} output file.
counts) it will ignore the contents of the file. It then adds in the
new execution counts and finally writes the data to the file.
+You can report on a subset of functions by using @option{--include}
+and @option{--exclude}. This is very useful when combined with
+@option{--stdout} trying to understand behavior and coverage for a
+particular function by running a test, looking at @command{gcov}
+output, testing another input, and running @command{gcov} again.
+
+@smallexample
+$ gcov -m --stdout --include inc tmp
+ -: 0:Source:tmp.cpp
+ -: 0:Graph:tmp.gcno
+ -: 0:Data:tmp.gcda
+ -: 0:Runs:1
+ 2*: 8: void inc () @{ b++; @}
+------------------
+Foo<char>::inc():
+ #####: 8: void inc () @{ b++; @}
+------------------
+Foo<int>::inc():
+ 2: 8: void inc () @{ b++; @}
+------------------
+@end smallexample
+
+@command{gcov} will match on mangled names by default, which you can
+control with the @option{-M} flag. Note that matching and reporting
+are independent, so you can match on mangled names while printing
+demangled names, and vice versa. To report on the @code{int}
+instantiation of @code{Foo} matching on mangled and demangled names:
+
+@smallexample
+$ gcov -t -m -M tmp --include 'Foo<int>'
+ -: 0:Source:tmp.cpp
+ -: 0:Graph:tmp.gcno
+ -: 0:Data:tmp.gcda
+ -: 0:Runs:1
+ 1: 7: Foo(): b (1000) @{@}
+ 2: 8: void inc () @{ b++; @}
+@end smallexample
+
+@smallexample
+$ gcov -t -m tmp --include 'FooIi'
+ -: 0:Source:tmp.cpp
+ -: 0:Graph:tmp.gcno
+ -: 0:Data:tmp.gcda
+ -: 0:Runs:1
+ 1: 7: Foo(): b (1000) @{@}
+ 2: 8: void inc () @{ b++; @}
+@end smallexample
+
+The arguments to @option{--include} and @option{--exclude} are
+extended regular expressions (like @command{grep -E}), so the pattern
+@code{in.?} matches both @code{inc} and @code{main}. If used with
+@option{-M} then all @code{int} instantiations of @code{Foo} would
+match too. @option{--include} and @option{--exclude} can be used
+multiple times, and if a name matches multiple filters it is the last
+one to match which takes preference. For example, to match
+@code{main} and the @code{int} instatiation of @code{inc}, while
+omitting the @code{Foo} constructor:
+
+@smallexample
+$ gcov -t -m -M --include in --exclude Foo --include '<int>::inc' tmp
+ -: 0:Source:tmp.cpp
+ -: 0:Graph:tmp.gcno
+ -: 0:Data:tmp.gcda
+ -: 0:Runs:1
+ 2: 8: void inc () @{ b++; @}
+ 1: 18:main (void)
+ -: 19:@{
+ -: 20: int i, total;
+ 1: 21: Foo<int> counter;
+ -: 22:
+ 1: 23: counter.inc();
+ 1: 24: counter.inc();
+ 1: 25: total = 0;
+ -: 26:
+ 11: 27: for (i = 0; i < 10; i++)
+ 10: 28: total += i;
+ -: 29:
+ 1*: 30: int v = total > 100 ? 1 : 2;
+ -: 31:
+ 1: 32: if (total != 45)
+ #####: 33: printf ("Failure\n");
+ -: 34: else
+ 1: 35: printf ("Success\n");
+ 1: 36: return 0;
+@end smallexample
+
@node Gcov and Optimization
@section Using @command{gcov} with GCC Optimization
#include "pretty-print.h"
#include "json.h"
#include "hwint.h"
+#include "xregex.h"
#include <zlib.h>
#include <getopt.h>
/* Return code of the tool invocation. */
static int return_code = 0;
+/* "Keep policy" when adding functions to the global function table. This will
+ be set to false when --include is used, otherwise every function should be
+ added to the table. Used for --include/exclude. */
+static bool default_keep = true;
+
+/* Include/exclude filters function based on matching the (de)mangled name.
+ The default is to match the mangled name. Note that flag_demangled_names
+ does not affect this. */
+static bool flag_filter_on_demangled = false;
+
+/* A 'function filter', a filter and action for determining if a function
+ should be included in the output or not. Used for --include/--exclude
+ filtering. */
+struct fnfilter
+{
+ /* The (extended) compiled regex for this filter. */
+ regex_t regex;
+
+ /* The action when this filter (regex) matches - if true, the function should
+ be kept, otherwise discarded. */
+ bool keep;
+
+ /* Compile the regex EXPR, or exit if pattern is malformed. */
+ void compile (const char *expr)
+ {
+ int err = regcomp (®ex, expr, REG_NOSUB | REG_EXTENDED);
+ if (err)
+ {
+ size_t len = regerror (err, ®ex, nullptr, 0);
+ char *msg = XNEWVEC (char, len);
+ regerror (err, ®ex, msg, len);
+ fprintf (stderr, "Bad regular expression: %s\n", msg);
+ free (msg);
+ exit (EXIT_FAILURE);
+ }
+ }
+};
+
+/* A collection of filter functions for including/exclude functions in the
+ output. This is empty unless --include/--exclude is used. */
+static vector<fnfilter> filters;
+
/* Forward declarations. */
static int process_args (int, char **);
static void print_usage (int) ATTRIBUTE_NORETURN;
fnotice (file, " -d, --display-progress Display progress information\n");
fnotice (file, " -D, --debug Display debugging dumps\n");
fnotice (file, " -f, --function-summaries Output summaries for each function\n");
+ fnotice (file, " --include Include functions matching this regex\n");
+ fnotice (file, " --exclude Exclude functions matching this regex\n");
fnotice (file, " -h, --help Print this help, then exit\n");
fnotice (file, " -j, --json-format Output JSON intermediate format\n\
into .gcov.json.gz file\n");
fnotice (file, " -l, --long-file-names Use long output file names for included\n\
source files\n");
fnotice (file, " -m, --demangled-names Output demangled function names\n");
+ fnotice (file, " -M, --filter-on-demangled Make --include/--exclude match on demangled\n\
+ names. This does not imply -m\n");
fnotice (file, " -n, --no-output Do not create an output file\n");
fnotice (file, " -o, --object-directory DIR|FILE Search for object files in DIR or called FILE\n");
fnotice (file, " -p, --preserve-paths Preserve all pathname components\n");
{ "branch-counts", no_argument, NULL, 'c' },
{ "conditions", no_argument, NULL, 'g' },
{ "json-format", no_argument, NULL, 'j' },
+ { "include", required_argument, NULL, 'I' },
+ { "exclude", required_argument, NULL, 'E' },
{ "human-readable", no_argument, NULL, 'H' },
{ "no-output", no_argument, NULL, 'n' },
{ "long-file-names", no_argument, NULL, 'l' },
{ "function-summaries", no_argument, NULL, 'f' },
{ "demangled-names", no_argument, NULL, 'm' },
+ { "filter-on-demangled", no_argument, NULL, 'M' },
{ "preserve-paths", no_argument, NULL, 'p' },
{ "relative-only", no_argument, NULL, 'r' },
{ "object-directory", required_argument, NULL, 'o' },
{
int opt;
- const char *opts = "abcdDfghHijklmno:pqrs:tuvwx";
+ const char *opts = "abcdDfghHijklmMno:pqrs:tuvwx";
while ((opt = getopt_long (argc, argv, opts, options, NULL)) != -1)
{
switch (opt)
case 'l':
flag_long_names = 1;
break;
+ case 'I':
+ default_keep = false;
+ filters.push_back (fnfilter {});
+ filters.back ().keep = true;
+ filters.back ().compile (optarg);
+ break;
+ case 'E':
+ filters.push_back (fnfilter {});
+ filters.back ().keep = false;
+ filters.back ().compile (optarg);
+ break;
case 'H':
flag_human_readable_numbers = 1;
break;
case 'm':
flag_demangled_names = 1;
break;
+ case 'M':
+ flag_filter_on_demangled = true;
+ break;
case 'n':
flag_gcov_file = 0;
break;
it != functions.end (); it++)
delete (*it);
+ for (fnfilter &filter : filters)
+ regfree (&filter.regex);
+
sources.resize (0);
names.resize (0);
functions.resize (0);
+ filters.resize (0);
ident_to_fn.clear ();
}
unsigned end_column = gcov_read_unsigned ();
fn = new function_info ();
- functions.push_back (fn);
- ident_to_fn[ident] = fn;
fn->m_name = function_name;
fn->ident = ident;
fn->artificial = artificial;
current_tag = tag;
+
+ /* This is separate from flag_demangled_names to support filtering on
+ mangled names while printing demangled names, or filtering on
+ demangled names while printing mangled names. An independent flag
+ makes sure the function selection does not change even if
+ demangling is turned on/off. */
+ const char *fname = function_name;
+ if (flag_filter_on_demangled)
+ fname = fn->get_demangled_name ();
+
+ bool keep = default_keep;
+ for (const fnfilter &fn : filters)
+ if (regexec (&fn.regex, fname, 0, nullptr, 0) == 0)
+ keep = fn.keep;
+
+ if (keep)
+ {
+ functions.push_back (fn);
+ ident_to_fn[ident] = fn;
+ }
}
else if (fn && tag == GCOV_TAG_BLOCKS)
{
unsigned line_start_group = 0;
vector<function_info *> *fns;
+ unsigned filtered_line_end = !filters.empty () ? 0 : source_lines.size ();
for (unsigned line_num = 1; line_num <= source_lines.size (); line_num++)
{
if (line_num >= src->lines.size ())
{
+ /* If the src->lines is truncated because the rest of the functions
+ are filtered out we must stop here, and not fall back to printing
+ the rest of the file. */
+ if (!filters.empty ())
+ break;
fprintf (gcov_file, "%9s:%5u", "-", line_num);
print_source_line (gcov_file, source_lines, line_num);
continue;
for (unsigned i = 0; i < fns->size (); i++)
if ((*fns)[i]->end_line > line_start_group)
line_start_group = (*fns)[i]->end_line;
+
+ /* When filtering, src->lines will be cut short for the last
+ selected function. To make sure the "overlapping function"
+ section is printed too, adjust the end so that it is within
+ src->lines. */
+ if (line_start_group >= src->lines.size ())
+ line_start_group = src->lines.size () - 1;
+
+ if (!filters.empty ())
+ filtered_line_end = line_start_group;
}
else if (fns != NULL && fns->size () == 1)
{
function_info *fn = (*fns)[0];
output_function_details (gcov_file, fn);
+
+ /* If functions are filtered, only the matching functions will be in
+ fns and there is no need for extra checking. */
+ if (!filters.empty ())
+ filtered_line_end = fn->end_line;
}
}
Otherwise, print the execution count before the source line.
There are 16 spaces of indentation added before the source
line so that tabs won't be messed up. */
- output_line_beginning (gcov_file, line->exists, line->unexceptional,
- line->has_unexecuted_block, line->count,
- line_num, "=====", "#####", src->maximum_count);
+ if (line_num <= filtered_line_end)
+ {
+ output_line_beginning (gcov_file, line->exists, line->unexceptional,
+ line->has_unexecuted_block, line->count,
+ line_num, "=====", "#####",
+ src->maximum_count);
- print_source_line (gcov_file, source_lines, line_num);
- output_line_details (gcov_file, line, line_num);
+ print_source_line (gcov_file, source_lines, line_num);
+ output_line_details (gcov_file, line, line_num);
+ }
if (line_start_group == line_num)
{
return $failed
}
+# Verify that report filtering includes and excludes the right functions.
+proc verify-filters { testname testcase file expected unexpected } {
+ set fd [open $file r]
+
+ set seen {}
+ set ex [concat $expected $unexpected]
+
+ while { [gets $fd line] >= 0 } {
+ foreach sym $ex {
+ if [regexp "$sym" "$line"] {
+ lappend seen $sym
+ }
+ }
+ }
+
+ set seen [lsort -unique $seen]
+
+ set expected [lmap key $expected {
+ if { $key in $seen } continue
+ set key
+ }]
+ set unexpected [lmap key $unexpected {
+ if { $key ni $seen } continue
+ set key
+ }]
+
+ foreach sym $expected {
+ fail "Did not see expected symbol '$sym'"
+ }
+
+ foreach sym $unexpected {
+ fail "Found unexpected symbol '$sym'"
+ }
+
+ close $fd
+ return [expr [llength $expected] + [llength $unexpected]]
+}
+
proc gcov-pytest-format-line { args } {
global subdir
set gcov_verify_conditions 0
set gcov_verify_lines 1
set gcov_verify_intermediate 0
+ set gcov_verify_filters 0
set gcov_remove_gcda 0
set xfailed 0
set gcov_verify_branches 1
} elseif { $a == "conditions" } {
set gcov_verify_conditions 1
+ } elseif { [lindex $a 0] == "filters" } {
+ set gcov_verify_filters 1
+ set verify_filters_expected [lindex $a 1]
+ set verify_filters_unexpected [lindex $a 2]
} elseif { $a == "intermediate" } {
set gcov_verify_intermediate 1
set gcov_verify_calls 0
set gcov_verify_branches 0
set gcov_verify_conditions 0
set gcov_verify_lines 0
+ set gcov_verify_filters 0
} elseif { $a == "remove-gcda" } {
set gcov_remove_gcda 1
} elseif { $gcov_args == "" } {
} else {
set ifailed 0
}
+ if { $gcov_verify_filters } {
+ set ffailed [verify-filters $testname $testcase $testcase.gcov $verify_filters_expected $verify_filters_unexpected]
+ } else {
+ set ffailed 0
+ }
# Report whether the gcov test passed or failed. If there were
# multiple failures then the message is a summary.
- set tfailed [expr $lfailed + $bfailed + $cdfailed + $cfailed + $ifailed]
+ set tfailed [expr $lfailed + $bfailed + $cdfailed + $cfailed + $ifailed + $ffailed]
if { $xfailed } {
setup_xfail "*-*-*"
}
if { $tfailed > 0 } {
- fail "$testname gcov: $lfailed failures in line counts, $bfailed in branch percentages, $cdfailed in condition/decision, $cfailed in return percentages, $ifailed in intermediate format"
+ fail "$testname gcov: $lfailed failures in line counts, $bfailed in branch percentages, $cdfailed in condition/decision, $cfailed in return percentages, $ifailed in intermediate format, $ffailed failed in filters"
if { $xfailed } {
clean-gcov $testcase
}