Ok for google/main. David
On Tue, Jun 14, 2011 at 1:50 PM, Sharad Singhai (शरद सिंघई) <sing...@google.com> wrote: > Sorry, Rietveld didn't send out the updated patch along with my mail. > Here it is. > > Sharad > > 2011-06-14 Sharad Singhai <sing...@google.com> > > Google Ref 39999 > > * doc/gcov.texi: Document gcov intermediate format. > * gcov.c (get_gcov_file_intermediate_name): New function. > (output_intermediate_file): New function. > * testsuite/lib/gcov.exp: Handle intermediate format. > * testsuite/g++.dg/gcov/gcov-7.C: New test. > > Index: doc/gcov.texi > =================================================================== > --- doc/gcov.texi (revision 174926) > +++ doc/gcov.texi (working copy) > @@ -130,6 +130,7 @@ > [@option{-f}|@option{--function-summaries}] > [@option{-o}|@option{--object-directory} @var{directory|file}] > @var{sourcefiles} > [@option{-u}|@option{--unconditional-branches}] > + [@option{-i}|@option{--intermediate-format}] > [@option{-d}|@option{--display-progress}] > @c man end > @c man begin SEEALSO > @@ -216,6 +217,32 @@ > @itemx --display-progress > Display the progress on the standard output. > > +@item -i > +@itemx --intermediate-format > +Output gcov file in an intermediate text format that can be used by > +@command{lcov} or other applications. It will output a single *.gcov file per > +*.gcda file. No source code is required. > + > +The format of the intermediate @file{.gcov} file is plain text with > +one entry per line > + > +@smallexample > +SF:@var{source_file_name} > +FN:@var{line_number},@var{function_name} > +FNDA:@var{execution_count},@var{function_name} > +BA:@var{line_num},@var{branch_coverage_type} > +DA:@var{line number},@var{execution_count} > + > +Where the @var{branch_coverage_type} is > + 0 (Branch not executed) > + 1 (Branch executed, but not taken) > + 2 (Branch executed and taken) > + > +There can be multiple SF entries in an intermediate gcov file. All > +entries following SF pertain to that source file until the next SF > +entry. > +@end smallexample > + > @end table > > @command{gcov} should be run with the current directory the same as that > Index: gcov.c > =================================================================== > --- gcov.c (revision 174926) > +++ gcov.c (working copy) > @@ -38,6 +38,7 @@ > #include "tm.h" > #include "intl.h" > #include "version.h" > +#include "demangle.h" > > #include <getopt.h> > > @@ -310,6 +311,9 @@ > > static int flag_display_progress = 0; > > +/* Output *.gcov file in intermediate format used by 'lcov'. */ > +static int flag_intermediate_format = 0; > + > /* For included files, make the gcov output file name include the name > of the input source file. For example, if x.h is included in a.c, > then the output file name is a.c##x.h.gcov instead of x.h.gcov. */ > @@ -436,6 +440,11 @@ > 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"); > fnotice (file, " -u, --unconditional-branches Show > unconditional branch counts too\n"); > + fnotice (file, " -i, --intermediate-format Output .gcov file > in an intermediate text\n\ > + format that can be used by 'lcov' > or other\n\ > + applications. It will output a single\n\ > + .gcov file per .gcda file. No > source file\n\ > + is required.\n"); > fnotice (file, " -d, --display-progress Display progress > information\n"); > fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n", > bug_report_url); > @@ -472,6 +481,7 @@ > { "object-file", required_argument, NULL, 'o' }, > { "unconditional-branches", no_argument, NULL, 'u' }, > { "display-progress", no_argument, NULL, 'd' }, > + { "intermediate-format", no_argument, NULL, 'i' }, > { 0, 0, 0, 0 } > }; > > @@ -482,7 +492,8 @@ > { > int opt; > > - while ((opt = getopt_long (argc, argv, "abcdfhlno:puv", options, > NULL)) != -1) > + while ((opt = getopt_long (argc, argv, "abcdfhilno:puv", options, NULL)) != > + -1) > { > switch (opt) > { > @@ -516,6 +527,10 @@ > case 'u': > flag_unconditional = 1; > break; > + case 'i': > + flag_intermediate_format = 1; > + flag_gcov_file = 1; > + break; > case 'd': > flag_display_progress = 1; > break; > @@ -531,6 +546,109 @@ > return optind; > } > > +/* Get the name of the gcov file. The return value must be free'd. > + > + It appends the '.gcov' extension to the *basename* of the file. > + The resulting file name will be in PWD. > + > + e.g., > + input: foo.da, output: foo.da.gcov > + input: a/b/foo.cc, output: foo.cc.gcov */ > + > +static char * > +get_gcov_file_intermediate_name (const char *file_name) > +{ > + const char *gcov = ".gcov"; > + char *result; > + const char *cptr; > + > + /* Find the 'basename'. */ > + cptr = lbasename (file_name); > + > + result = XNEWVEC(char, strlen (cptr) + strlen (gcov) + 1); > + sprintf (result, "%s%s", cptr, gcov); > + > + return result; > +} > + > +/* Output the result in intermediate format used by 'lcov'. > + > +This format contains a single file named 'foo.cc.gcov', with no source > +code included. > + > +SF:/home/.../foo.h > +DA:10,1 > +DA:30,0 > +DA:35,1 > +SF:/home/.../bar.h > +DA:12,0 > +DA:33,0 > +DA:55,1 > +SF:/home/.../foo.cc > +FN:30,<function_name> > +FNDA:2,<function_name> > +DA:42,0 > +DA:53,1 > +BA:55,1 > +BA:55,2 > +DA:95,1 > +... > + > +The default format contains 3 separate files: 'foo.h.gcov', 'foo.cc.gcov', > +'bar.h.gcov', each with source code included. */ > + > +static void > +output_intermediate_file (FILE *gcov_file, source_t *src) > +{ > + unsigned line_num; /* current line number. */ > + const line_t *line; /* current line info ptr. */ > + function_t *fn; /* current function info ptr. */ > + > + fprintf (gcov_file, "SF:%s\n", src->name); /* source file name */ > + > + /* NOTE: 'gcov' sometimes output 2 extra lines (including 1 EOF line) > + in the end, search for string *EOF* in this file. > + > + Likely related: > + http://gcc.gnu.org/bugzilla/show_bug.cgi?id=30257 > + http://gcc.gnu.org/bugzilla/show_bug.cgi?id=24550 */ > + > + for (fn = src->functions; fn; fn = fn->line_next) > + { > + char *demangled_name; > + demangled_name = cplus_demangle (fn->name, DMGL_PARAMS); > + /* FN:<line_number>,<function_name> */ > + fprintf (gcov_file, "FN:%d,%s\n", fn->line, > + demangled_name ? demangled_name : fn->name); > + /* FNDA:<execution_count>,<function_name> */ > + fprintf (gcov_file, "FNDA:%s,%s\n", > + format_gcov (fn->blocks[0].count, 0, -1), > + demangled_name ? demangled_name : fn->name); > + } > + > + for (line_num = 1, line = &src->lines[line_num]; > + line_num < src->num_lines; > + line_num++, line++) > + { > + arc_t *arc; > + if (line->exists) > + fprintf (gcov_file, "DA:%u,%d\n", line_num, > + line->count != 0 ? 1 : 0); > + if (flag_branches) > + for (arc = line->u.branches; arc; arc = arc->line_next) > + { > + /* BA:<line_num>,<branch_coverage_type> > + branch_coverage_type: 0 (Branch not executed) > + : 1 (Branch executed, but not taken) > + : 2 (Branch executed and taken) > + */ > + if (!arc->is_unconditional && !arc->is_call_non_return) > + fprintf(gcov_file, "BA:%d,%d\n", line_num, > + arc->src->count ? (arc->count > 0) + 1 : 0); > + } > + } > +} > + > /* Process a single source file. */ > > static void > @@ -547,7 +665,7 @@ > > create_file_names (file_name); > if (read_graph_file ()) > - return; > + exit (FATAL_EXIT_CODE); > > if (!functions) > { > @@ -556,7 +674,7 @@ > } > > if (read_count_file ()) > - return; > + exit (FATAL_EXIT_CODE); > > for (fn_p = NULL, fn = functions; fn; fn_p = fn, fn = fn->next) > solve_flow_graph (fn); > @@ -570,6 +688,8 @@ > { > source_t *src; > function_t *fn; > + FILE *gcov_file_intermediate = NULL; > + char *gcov_file_intermediate_name = NULL; > > for (src = sources; src; src = src->next) > src->lines = XCNEWVEC (line_t, src->num_lines); > @@ -587,32 +707,56 @@ > } > } > > + if (flag_gcov_file && flag_intermediate_format) > + { > + /* We open the file now. */ > + gcov_file_intermediate_name = > + get_gcov_file_intermediate_name (file_name); > + gcov_file_intermediate = fopen (gcov_file_intermediate_name, "w"); > + } > for (src = sources; src; src = src->next) > { > accumulate_line_counts (src); > function_summary (&src->coverage, "File"); > if (flag_gcov_file) > { > - char *gcov_file_name = make_gcov_file_name (file_name, src->name); > - FILE *gcov_file = fopen (gcov_file_name, "w"); > + if (flag_intermediate_format) > + /* Now output in the intermediate format without requiring > + source files. This outputs a section to a *single* file. */ > + output_intermediate_file (gcov_file_intermediate, src); > + else > + { > + /* Now output the version with source files. > + This outputs a separate *.gcov file for each source file > + involved. */ > + char *gcov_file_name = make_gcov_file_name (file_name, > src->name); > + FILE *gcov_file = fopen (gcov_file_name, "w"); > > - if (gcov_file) > - { > - fnotice (stdout, "%s:creating '%s'\n", > - src->name, gcov_file_name); > - output_lines (gcov_file, src); > - if (ferror (gcov_file)) > - fnotice (stderr, "%s:error writing output file '%s'\n", > - src->name, gcov_file_name); > - fclose (gcov_file); > - } > - else > - fnotice (stderr, "%s:could not open output file '%s'\n", > - src->name, gcov_file_name); > - free (gcov_file_name); > - } > - fnotice (stdout, "\n"); > + if (gcov_file) > + { > + fnotice (stdout, "%s:creating '%s'\n", > + src->name, gcov_file_name); > + output_lines (gcov_file, src); > + if (ferror (gcov_file)) > + fnotice (stderr, "%s:error writing output file '%s'\n", > + src->name, gcov_file_name); > + fclose (gcov_file); > + } > + else > + fnotice (stderr, "%s:could not open output file '%s'\n", > + src->name, gcov_file_name); > + free (gcov_file_name); > + } > + fnotice (stdout, "\n"); > + } > } > + > + if (flag_gcov_file && flag_intermediate_format) > + { > + /* Now we've finished writing the intermediate file. */ > + fclose (gcov_file_intermediate); > + XDELETEVEC (gcov_file_intermediate_name); > + } > } > > /* Release all memory used. */ > @@ -841,6 +985,7 @@ > functions = fn; > current_tag = tag; > > + /* NOTE: Here is how *EOF* comes to effect. */ > if (lineno >= src->num_lines) > src->num_lines = lineno + 1; > /* Now insert it into the source file's list of > @@ -949,6 +1094,7 @@ > line_nos[ix++] = src->index; > } > line_nos[ix++] = lineno; > + /* NOTE: Here is how *EOF* comes to effect. */ > if (lineno >= src->num_lines) > src->num_lines = lineno + 1; > } > Index: testsuite/lib/gcov.exp > =================================================================== > --- testsuite/lib/gcov.exp (revision 174926) > +++ testsuite/lib/gcov.exp (working copy) > @@ -60,6 +60,59 @@ > } > > # > +# verify-intermediate -- check that intermediate file has certain lines > +# > +# TESTCASE is the name of the test. > +# FILE is the name of the gcov output file. > +# > +# Checks are very loose, they are based on being certain tags present > +# in the output. They do not check for exact expected execution > +# counts. For that the regular gcov format should be checked. > +# > +proc verify-intermediate { testcase file } { > + set failed 0 > + set sf 0 > + set fn 0 > + set fnda 0 > + set da 0 > + set fd [open $file r] > + while { [gets $fd line] >= 0 } { > + if [regexp "^SF:" $line] { > + incr sf > + } > + if [regexp "^FN:(\[0-9\]+)," $line] { > + incr fn > + } > + if [regexp "^FNDA:(\[0-9\]+)," $line] { > + incr fnda > + } > + if [regexp "^DA:(\[0-9\]+),(\[0-9\]+)" $line] { > + incr da > + } > + } > + > + # We should see at least one tag of each type > + if {$sf == 0} { > + fail "expected SF: not found" > + incr failed > + } > + if {$fn == 0} { > + fail "expected FN: not found" > + incr failed > + } > + if {$fnda == 0} { > + fail "expected FNDA: not found" > + incr failed > + } > + if {$da == 0} { > + fail "expected DA: not found" > + incr failed > + } > + return $failed > +} > + > + > +# > # verify-branches -- check that branch percentages are as expected > # > # TESTCASE is the name of the test. > @@ -234,6 +287,8 @@ > > set gcov_verify_calls 0 > set gcov_verify_branches 0 > + set gcov_verify_lines 1 > + set gcov_verify_intermediate 0 > set gcov_execute_xfail "" > set gcov_verify_xfail "" > > @@ -242,6 +297,11 @@ > set gcov_verify_calls 1 > } elseif { $a == "branches" } { > set gcov_verify_branches 1 > + } elseif { $a == "intermediate" } { > + set gcov_verify_intermediate 1 > + set gcov_verify_calls 0 > + set gcov_verify_branches 0 > + set gcov_verify_lines 0 > } > } > > @@ -274,8 +334,12 @@ > eval setup_xfail [split $gcov_verify_xfail] > } > > - # Check that line execution counts are as expected. > - set lfailed [verify-lines $testcase $testcase.gcov] > + if { $gcov_verify_lines } { > + # Check that line execution counts are as expected. > + set lfailed [verify-lines $testcase $testcase.gcov] > + } else { > + set lfailed 0 > + } > > # If requested via the .x file, check that branch and call information > # is correct. > @@ -289,12 +353,18 @@ > } else { > set cfailed 0 > } > + if { $gcov_verify_intermediate } { > + # Check that intermediate format has the expected format > + set ifailed [verify-intermediate $testcase $testcase.gcov] > + } else { > + set ifailed 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 + $cfailed] > + set tfailed [expr $lfailed + $bfailed + $cfailed + $ifailed] > if { $tfailed > 0 } { > - fail "$subdir/$testcase gcov: $lfailed failures in line counts, > $bfailed in branch percentages, $cfailed in return percentages" > + fail "$subdir/$testcase gcov: $lfailed failures in line counts, > $bfailed in branch percentages, $cfailed in return percentages, > $ifailed in intermediate format" > } else { > pass "$subdir/$testcase gcov" > clean-gcov $testcase > Index: testsuite/g++.dg/gcov/gcov-7.C > =================================================================== > --- testsuite/g++.dg/gcov/gcov-7.C (revision 0) > +++ testsuite/g++.dg/gcov/gcov-7.C (revision 0) > @@ -0,0 +1,32 @@ > +/* Verify that intermediate coverage format can be generated for > simple code. */ > + > +/* { dg-options "-fprofile-arcs -ftest-coverage" } */ > +/* { dg-do run { target native } } */ > + > +class C { > +public: > + C() > + { > + i = 0; /* count(1) */ > + } > + ~C() {} > + void seti (int j) > + { > + i = j; /* count(1) */ > + } > +private: > + int i; > +}; > + > +void foo() > +{ > + C c; /* count(2) */ > + c.seti (1); /* count(1) */ > +} > + > +int main() > +{ > + foo(); /* count(1) */ > +} > + > +/* { dg-final { run-gcov intermediate { -i gcov-7.C } } } */ >