[google] Add intermediate text format for gcov (issue4595053)

Xinliang David Li davidxl@google.com
Thu Jun 16 00:43:00 GMT 2011


Ok for google/main.

David

On Tue, Jun 14, 2011 at 1:50 PM, Sharad Singhai (शरद सिंघई)
<singhai@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  <singhai@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 } } } */
>



More information about the Gcc-patches mailing list