diff mbox

gcov intermediate format

Message ID CAKxPW6526=7G9BJRq3+dMPrf2CjHkmN_s2bNovLBqVejbu4pUw@mail.gmail.com
State New
Headers show

Commit Message

Sharad Singhai May 28, 2013, 6:35 p.m. UTC
Sorry, my patch had bad formatting in one of the functions
(output_gcov_file). Here is the corrected version.

Thanks,
Sharad

(2013-05-28    <singhai@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  <singhai@google.com>

* g++.dg/gcov/gcov-8.C: New testcase.
* lib/gcov.exp: Handle intermediate format.

     } else {
  pass "$testname gcov"
  clean-gcov $testcase

Comments

Sharad Singhai June 5, 2013, 6:18 p.m. UTC | #1
Ping.

Thanks,
Sharad


On Tue, May 28, 2013 at 11:35 AM, Sharad Singhai <singhai@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    <singhai@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  <singhai@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
Sharad Singhai June 17, 2013, 6:45 p.m. UTC | #2
Ping.

Thanks,
Sharad


On Wed, Jun 5, 2013 at 11:18 AM, Sharad Singhai <singhai@google.com> wrote:
> Ping.
>
> Thanks,
> Sharad
>
>
> On Tue, May 28, 2013 at 11:35 AM, Sharad Singhai <singhai@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    <singhai@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  <singhai@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
Jan Hubicka June 18, 2013, 10:28 a.m. UTC | #3
> Ping.
Th patch is OK, thanks!
I see you added gcov.exp file support, do you have a testcases?

Honza
Sharad Singhai June 18, 2013, 4:56 p.m. UTC | #4
On Tue, Jun 18, 2013 at 3:28 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>> Ping.
> Th patch is OK, thanks!
> I see you added gcov.exp file support, do you have a testcases?

Yes, I added support for verifying intermediate format in gcov.exp. I
also added a minimal testcase for intermediate format in
testsuite/g++.dg/gcov directory.

Thanks,
Sharad

>
> Honza
diff mbox

Patch

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"