Ping. Thanks, Sharad
On Tue, May 28, 2013 at 11:35 AM, Sharad Singhai <sing...@google.com> wrote: > Sorry, my patch had bad formatting in one of the functions > (output_gcov_file). Here is the corrected version. > > Thanks, > Sharad > > (2013-05-28 <sing...@google.com> > > * gcov.c (print_usage): Handle new option. > (process_args): Ditto. > (get_gcov_intermediate_filename): New function. > (output_intermediate_file): New function. > (output_gcov_file): New function > (generate_results): Handle new option. > (release_function): Relase demangled name. > (read_graph_file): Handle demangled name. > (output_lines): Ditto. > * doc/gcov.texi: Document gcov intermediate format. > > testsuite/ChangeLog: > > 2013-05-28 Sharad Singhai <sing...@google.com> > > * g++.dg/gcov/gcov-8.C: New testcase. > * lib/gcov.exp: Handle intermediate format. > > Index: doc/gcov.texi > =================================================================== > --- doc/gcov.texi (revision 199273) > +++ doc/gcov.texi (working copy) > @@ -122,15 +122,17 @@ gcov [@option{-v}|@option{--version}] [@option{-h} > [@option{-a}|@option{--all-blocks}] > [@option{-b}|@option{--branch-probabilities}] > [@option{-c}|@option{--branch-counts}] > - [@option{-u}|@option{--unconditional-branches}] > + [@option{-d}|@option{--display-progress}] > + [@option{-f}|@option{--function-summaries}] > + [@option{-i}|@option{--intermediate-format}] > + [@option{-l}|@option{--long-file-names}] > + [@option{-m}|@option{--demangled-names}] > [@option{-n}|@option{--no-output}] > - [@option{-l}|@option{--long-file-names}] > + [@option{-o}|@option{--object-directory} @var{directory|file}] > [@option{-p}|@option{--preserve-paths}] > [@option{-r}|@option{--relative-only}] > - [@option{-f}|@option{--function-summaries}] > - [@option{-o}|@option{--object-directory} @var{directory|file}] > [@option{-s}|@option{--source-prefix} @var{directory}] > - [@option{-d}|@option{--display-progress}] > + [@option{-u}|@option{--unconditional-branches}] > @var{files} > @c man end > @c man begin SEEALSO > @@ -232,6 +234,50 @@ Unconditional branches are normally not interestin > @itemx --display-progress > Display the progress on the standard output. > > +@item -i > +@itemx --intermediate-format > +Output gcov file in an easy-to-parse intermediate text format that can > +be used by @command{lcov} or other tools. The output is a single > +@file{.gcov} file per @file{.gcda} file. No source code is required. > + > +The format of the intermediate @file{.gcov} file is plain text with > +one entry per line > + > +@smallexample > +file:@var{source_file_name} > +function:@var{line_number},@var{execution_count},@var{function_name} > +lcount:@var{line number},@var{execution_count} > +branch:@var{line_number},@var{branch_coverage_type} > + > +Where the @var{branch_coverage_type} is > + notexec (Branch not executed) > + taken (Branch executed and taken) > + nottaken (Branch executed, but not taken) > + > +There can be multiple @var{file} entries in an intermediate gcov > +file. All entries following a @var{file} pertain to that source file > +until the next @var{file} entry. > +@end smallexample > + > +Here is a sample when @option{-i} is used in conjuction with > @option{-b} option: > + > +@smallexample > +file:array.cc > +function:11,1,_Z3sumRKSt6vectorIPiSaIS0_EE > +function:22,1,main > +lcount:11,1 > +lcount:12,1 > +lcount:14,1 > +branch:14,taken > +lcount:26,1 > +branch:28,nottaken > +@end smallexample > + > +@item -m > +@itemx --demangled-names > +Display demangled function names in output. The default is to show > +mangled function names. > + > @end table > > @command{gcov} should be run with the current directory the same as that > Index: gcov.c > =================================================================== > --- gcov.c (revision 199273) > +++ gcov.c (working copy) > @@ -37,6 +37,7 @@ along with Gcov; see the file COPYING3. If not se > #include "intl.h" > #include "diagnostic.h" > #include "version.h" > +#include "demangle.h" > > #include <getopt.h> > > @@ -168,6 +169,7 @@ typedef struct function_info > { > /* Name of function. */ > char *name; > + char *demangled_name; > unsigned ident; > unsigned lineno_checksum; > unsigned cfg_checksum; > @@ -325,6 +327,14 @@ static int flag_gcov_file = 1; > > static int flag_display_progress = 0; > > +/* Output *.gcov file in intermediate format used by 'lcov'. */ > + > +static int flag_intermediate_format = 0; > + > +/* Output demangled function names. */ > + > +static int flag_demangled_names = 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. */ > @@ -388,6 +398,7 @@ static void executed_summary (unsigned, unsigned); > static void function_summary (const coverage_t *, const char *); > static const char *format_gcov (gcov_type, gcov_type, int); > static void accumulate_line_counts (source_t *); > +static void output_gcov_file(const char *, source_t *); > static int output_branch_count (FILE *, int, const arc_t *); > static void output_lines (FILE *, const source_t *); > static char *make_gcov_file_name (const char *, const char *); > @@ -461,21 +472,23 @@ print_usage (int error_p) > fnotice (file, "Usage: gcov [OPTION]... SOURCE|OBJ...\n\n"); > fnotice (file, "Print code coverage information.\n\n"); > fnotice (file, " -h, --help Print this help, > then exit\n"); > - fnotice (file, " -v, --version Print version > number, then exit\n"); > fnotice (file, " -a, --all-blocks Show information > for every basic block\n"); > fnotice (file, " -b, --branch-probabilities Include branch > probabilities in output\n"); > - fnotice (file, " -c, --branch-counts Given counts of > branches taken\n\ > + fnotice (file, " -c, --branch-counts Output counts of > branches taken\n\ > rather than percentages\n"); > - fnotice (file, " -n, --no-output Do not create an > output file\n"); > + fnotice (file, " -d, --display-progress Display progress > information\n"); > + fnotice (file, " -f, --function-summaries Output summaries > for each function\n"); > + fnotice (file, " -i, --intermediate-format Output .gcov file > in intermediate text format\n"); > fnotice (file, " -l, --long-file-names Use long output > file names for included\n\ > source files\n"); > - fnotice (file, " -f, --function-summaries Output summaries > for each function\n"); > + fnotice (file, " -m, --demangled-names Output demangled > function names\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"); > + fnotice (file, " -r, --relative-only Only show data > for relative sources\n"); > fnotice (file, " -s, --source-prefix DIR Source prefix to > elide\n"); > - fnotice (file, " -r, --relative-only Only show data > for relative sources\n"); > - fnotice (file, " -p, --preserve-paths Preserve all > pathname components\n"); > fnotice (file, " -u, --unconditional-branches Show > unconditional branch counts too\n"); > - fnotice (file, " -d, --display-progress Display progress > information\n"); > + fnotice (file, " -v, --version Print version > number, then exit\n"); > fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n", > bug_report_url); > exit (status); > @@ -503,9 +516,11 @@ static const struct option options[] = > { "all-blocks", no_argument, NULL, 'a' }, > { "branch-probabilities", no_argument, NULL, 'b' }, > { "branch-counts", no_argument, NULL, 'c' }, > + { "intermediate-format", no_argument, NULL, 'i' }, > { "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' }, > { "preserve-paths", no_argument, NULL, 'p' }, > { "relative-only", no_argument, NULL, 'r' }, > { "object-directory", required_argument, NULL, 'o' }, > @@ -523,7 +538,8 @@ process_args (int argc, char **argv) > { > int opt; > > - while ((opt = getopt_long (argc, argv, "abcdfhlno:s:pruv", options, > NULL)) != -1) > + while ((opt = getopt_long (argc, argv, "abcdfhilmno:s:pruv", > options, NULL)) != > + -1) > { > switch (opt) > { > @@ -545,6 +561,9 @@ process_args (int argc, char **argv) > case 'l': > flag_long_names = 1; > break; > + case 'm': > + flag_demangled_names = 1; > + break; > case 'n': > flag_gcov_file = 0; > break; > @@ -564,6 +583,10 @@ process_args (int argc, char **argv) > case 'u': > flag_unconditional = 1; > break; > + case 'i': > + flag_intermediate_format = 1; > + flag_gcov_file = 1; > + break; > case 'd': > flag_display_progress = 1; > break; > @@ -579,6 +602,110 @@ process_args (int argc, char **argv) > 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_intermediate_filename (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'. > + > +The intermediate format contains a single file named 'foo.cc.gcov', > +with no source code included. A sample output is > + > +file:foo.cc > +function:5,1,_Z3foov > +function:13,1,main > +function:19,1,_GLOBAL__sub_I__Z3foov > +function:19,1,_Z41__static_initialization_and_destruction_0ii > +lcount:5,1 > +lcount:7,9 > +lcount:9,8 > +lcount:11,1 > +file:/.../iostream > +lcount:74,1 > +file:/.../basic_ios.h > +file:/.../ostream > +file:/.../ios_base.h > +function:157,0,_ZStorSt12_Ios_IostateS_ > +lcount:157,0 > +file:/.../char_traits.h > +function:258,0,_ZNSt11char_traitsIcE6lengthEPKc > +lcount:258,0 > +... > + > +The default gcov outputs multiple files: 'foo.cc.gcov', > +'iostream.gcov', 'ios_base.h.gcov', etc. with source code > +included. Instead the intermediate format here outputs only a single > +file 'foo.cc.gcov' similar to the above example. */ > + > +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, "file:%s\n", src->name); /* source file name */ > + > + for (fn = src->functions; fn; fn = fn->line_next) > + { > + /* function:<name>,<line_number>,<execution_count> */ > + fprintf (gcov_file, "function:%d,%s,%s\n", fn->line, > + format_gcov (fn->blocks[0].count, 0, -1), > + flag_demangled_names ? fn->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, "lcount:%u,%s\n", line_num, > + format_gcov (line->count, 0, -1)); > + if (flag_branches) > + for (arc = line->u.branches; arc; arc = arc->line_next) > + { > + if (!arc->is_unconditional && !arc->is_call_non_return) > + { > + const char *branch_type; > + /* branch:<line_num>,<branch_coverage_type> > + branch_coverage_type > + : notexec (Branch not executed) > + : taken (Branch executed and taken) > + : nottaken (Branch executed, but not taken) > + */ > + if (arc->src->count) > + branch_type = (arc->count > 0) ? "taken" : "nottaken"; > + else > + branch_type = "notexec"; > + fprintf(gcov_file, "branch:%d,%s\n", line_num, branch_type); > + } > + } > + } > +} > + > + > /* Process a single input file. */ > > static void > @@ -655,11 +782,40 @@ process_file (const char *file_name) > } > > static void > +output_gcov_file(const char *file_name, source_t *src) > +{ > + char *gcov_file_name = make_gcov_file_name (file_name, src->coverage.name); > + > + if (src->coverage.lines) > + { > + FILE *gcov_file = fopen (gcov_file_name, "w"); > + if (gcov_file) > + { > + fnotice (stdout, "Creating '%s'\n", gcov_file_name); > + output_lines (gcov_file, src); > + if (ferror (gcov_file)) > + fnotice (stderr, "Error writing output file '%s'\n", > gcov_file_name); > + fclose (gcov_file); > + } > + else > + fnotice (stderr, "Could not open output file '%s'\n", > gcov_file_name); > + } > + else > + { > + unlink (gcov_file_name); > + fnotice (stdout, "Removing '%s'\n", gcov_file_name); > + } > + free (gcov_file_name); > +} > + > +static void > generate_results (const char *file_name) > { > unsigned ix; > source_t *src; > function_t *fn; > + FILE *gcov_intermediate_file = NULL; > + char *gcov_intermediate_filename = NULL; > > for (ix = n_sources, src = sources; ix--; src++) > if (src->num_lines) > @@ -670,7 +826,7 @@ generate_results (const char *file_name) > coverage_t coverage; > > memset (&coverage, 0, sizeof (coverage)); > - coverage.name = fn->name; > + coverage.name = flag_demangled_names ? fn->demangled_name : fn->name; > add_line_counts (flag_function_summary ? &coverage : NULL, fn); > if (flag_function_summary) > { > @@ -688,7 +844,21 @@ generate_results (const char *file_name) > else > file_name = canonicalize_name (file_name); > } > - > + > + if (flag_gcov_file && flag_intermediate_format) > + { > + /* Open the intermediate file. */ > + gcov_intermediate_filename = > + get_gcov_intermediate_filename (file_name); > + gcov_intermediate_file = fopen (gcov_intermediate_filename, "w"); > + if (!gcov_intermediate_file) > + { > + fnotice (stderr, "Cannot open intermediate output file %s\n", > + gcov_intermediate_filename); > + return; > + } > + } > + > for (ix = n_sources, src = sources; ix--; src++) > { > if (flag_relative_only) > @@ -711,34 +881,21 @@ generate_results (const char *file_name) > total_executed += src->coverage.lines_executed; > if (flag_gcov_file) > { > - char *gcov_file_name > - = make_gcov_file_name (file_name, src->coverage.name); > + if (flag_intermediate_format) > + /* Output the intermediate format without requiring source > + files. This outputs a section to a *single* file. */ > + output_intermediate_file (gcov_intermediate_file, src); > + else > + output_gcov_file (file_name, src); > + fnotice (stdout, "\n"); > + } > + } > > - if (src->coverage.lines) > - { > - FILE *gcov_file = fopen (gcov_file_name, "w"); > - > - if (gcov_file) > - { > - fnotice (stdout, "Creating '%s'\n", gcov_file_name); > - output_lines (gcov_file, src); > - if (ferror (gcov_file)) > - fnotice (stderr, "Error writing output file '%s'\n", > - gcov_file_name); > - fclose (gcov_file); > - } > - else > - fnotice (stderr, "Could not open output file '%s'\n", > - gcov_file_name); > - } > - else > - { > - unlink (gcov_file_name); > - fnotice (stdout, "Removing '%s'\n", 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_intermediate_file); > + XDELETEVEC (gcov_intermediate_filename); > } > > if (!file_name) > @@ -765,6 +922,9 @@ release_function (function_t *fn) > } > free (fn->blocks); > free (fn->counts); > + if (flag_demangled_names && fn->demangled_name != fn->name) > + free (fn->demangled_name); > + free (fn->name); > } > > /* Release all memory used. */ > @@ -1050,6 +1210,12 @@ read_graph_file (void) > > fn = XCNEW (function_t); > fn->name = function_name; > + if (flag_demangled_names) > + { > + fn->demangled_name = cplus_demangle (fn->name, DMGL_PARAMS); > + if (!fn->demangled_name) > + fn->demangled_name = fn->name; > + } > fn->ident = ident; > fn->lineno_checksum = lineno_checksum; > fn->cfg_checksum = cfg_checksum; > @@ -2277,7 +2443,8 @@ output_lines (FILE *gcov_file, const source_t *src > if (arc->fake) > return_count -= arc->count; > > - fprintf (gcov_file, "function %s", fn->name); > + fprintf (gcov_file, "function %s", flag_demangled_names ? > + fn->demangled_name : fn->name); > fprintf (gcov_file, " called %s", > format_gcov (called_count, 0, -1)); > fprintf (gcov_file, " returned %s", > Index: testsuite/lib/gcov.exp > =================================================================== > --- testsuite/lib/gcov.exp (revision 199273) > +++ testsuite/lib/gcov.exp (working copy) > @@ -70,7 +70,62 @@ proc verify-lines { testname testcase file } { > return $failed > } > > + > # > +# verify-intermediate -- check that intermediate file has certain lines > +# > +# TESTNAME is the name of the test, including unique flags. > +# TESTCASE is the name of the test. > +# FILE is the name of the gcov output file. > +# > +# Checks are very loose, they are based on certain tags being 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 { testname testcase file } { > + set failed 0 > + set srcfile 0 > + set function 0 > + set lcount 0 > + set branch 0 > + set fd [open $file r] > + while { [gets $fd line] >= 0 } { > + if [regexp "^file:" $line] { > + incr srcfile > + } > + if [regexp "^function:(\[0-9\]+),(\[0-9\]+),.*" $line] { > + incr function > + } > + if [regexp "^lcount:(\[0-9\]+),(\[0-9\]+)" $line] { > + incr lcount > + } > + if [regexp "^branch:(\[0-9\]+),(taken|nottaken|notexec)" $line] { > + incr branch > + } > + } > + > + # We should see at least one tag of each type > + if {$srcfile == 0} { > + fail "$testname expected 'file:' tag not found" > + incr failed > + } > + if {$function == 0} { > + fail "$testname expected 'function:' tag not found" > + incr failed > + } > + if {$lcount == 0} { > + fail "$testname expected 'lcount:' tag not found" > + incr failed > + } > + if {$branch == 0} { > + fail "$testname expected 'branch:' tag not found" > + incr failed > + } > + return $failed > +} > + > + > +# > # verify-branches -- check that branch percentages are as expected > # > # TESTNAME is the name of the test, including unique flags. > @@ -248,6 +303,8 @@ proc run-gcov { args } { > set gcov_args "" > set gcov_verify_calls 0 > set gcov_verify_branches 0 > + set gcov_verify_lines 1 > + set gcov_verify_intermediate 0 > set xfailed 0 > > foreach a $args { > @@ -255,6 +312,11 @@ proc run-gcov { args } { > 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 > } elseif { $gcov_args == "" } { > set gcov_args $a > } else { > @@ -295,7 +357,12 @@ proc run-gcov { args } { > remote_upload host $testcase.gcov $testcase.gcov > > # Check that line execution counts are as expected. > - set lfailed [verify-lines $testname $testcase $testcase.gcov] > + if { $gcov_verify_lines } { > + # Check that line execution counts are as expected. > + set lfailed [verify-lines $testname $testcase $testcase.gcov] > + } else { > + set lfailed 0 > + } > > # If requested via the .x file, check that branch and call information > # is correct. > @@ -309,15 +376,21 @@ proc run-gcov { args } { > } else { > set cfailed 0 > } > + if { $gcov_verify_intermediate } { > + # Check that intermediate format has the expected format > + set ifailed [verify-intermediate $testname $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 { $xfailed } { > setup_xfail "*-*-*" > } > if { $tfailed > 0 } { > - fail "$testname gcov: $lfailed failures in line counts, $bfailed in > branch percentages, $cfailed in return percentages" > + fail "$testname gcov: $lfailed failures in line counts, $bfailed in > branch percentages, $cfailed in return percentages, $ifailed in > intermediate format" > } else { > pass "$testname gcov" > clean-gcov $testcase