public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH 0/7] GCOV: another set of improvements
@ 2017-10-26  8:12 marxin
  2017-10-26  8:12 ` [PATCH 2/7] GCOV: introduce usage of terminal colors marxin
                   ` (7 more replies)
  0 siblings, 8 replies; 33+ messages in thread
From: marxin @ 2017-10-26  8:12 UTC (permalink / raw)
  To: gcc-patches

Hi.

As I've spent recently some time in gcov, I decided to enhance the tool
a bit. My main target is PR48463 which will remove assumption that multiple
functions can't start on a same line. I've got pending patch that will be
send soon.

Thanks for review,
Martin

marxin (7):
  GCOV: document behavior of -fkeep-{static,inline}-functions (PR
    gcov-profile/82633).
  GCOV: introduce usage of terminal colors.
  GCOV: add support for lines with an unexecuted lines.
  GCOV: add -j argument (human readable format).
  GCOV: std::vector refactoring.
  GCOV: Vector refactoring II
  GCOV: std::vector refactoring III

 gcc/color-macros.h                         |  50 +++
 gcc/configure                              |   4 +-
 gcc/configure.ac                           |   4 +-
 gcc/diagnostic-color.c                     |  27 +-
 gcc/doc/gcov.texi                          |  32 +-
 gcc/gcov.c                                 | 552 ++++++++++++++++-------------
 gcc/testsuite/g++.dg/gcov/gcov-threads-1.C |   4 +-
 gcc/testsuite/g++.dg/gcov/loop.C           |  27 ++
 gcc/testsuite/g++.dg/gcov/ternary.C        |  12 +
 gcc/testsuite/lib/gcov.exp                 |   4 +-
 10 files changed, 438 insertions(+), 278 deletions(-)
 create mode 100644 gcc/color-macros.h
 create mode 100644 gcc/testsuite/g++.dg/gcov/loop.C
 create mode 100644 gcc/testsuite/g++.dg/gcov/ternary.C

-- 
2.14.2

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 3/7] GCOV: add support for lines with an unexecuted lines.
  2017-10-26  8:12 [PATCH 0/7] GCOV: another set of improvements marxin
                   ` (4 preceding siblings ...)
  2017-10-26  8:12 ` [PATCH 7/7] GCOV: std::vector refactoring III marxin
@ 2017-10-26  8:12 ` marxin
  2017-10-30 12:27   ` Nathan Sidwell
  2017-11-02 15:33   ` Eric Botcazou
  2017-10-26  8:19 ` [PATCH 6/7] GCOV: Vector refactoring II marxin
  2017-10-26  8:47 ` [PATCH 8/N][RFC] GCOV: support multiple functions per a line Martin Liška
  7 siblings, 2 replies; 33+ messages in thread
From: marxin @ 2017-10-26  8:12 UTC (permalink / raw)
  To: gcc-patches

It's possible to have a line of code that has a non-zero coverage.
However, it can contain unexecuted blocks and I hope adding a
notification can be usefull. LLVM also does that:

        -:    0:Source:ternary.c
        -:    0:Graph:ternary.gcno
        -:    0:Data:ternary.gcda
        -:    0:Runs:1
        -:    0:Programs:1
        -:    1:int b, c, d, e;
        -:    2:
        1:    3:int main()
        -:    4:{
       1*:    5:	int a = b < 1 ? (c < 3 ? d : c) : e;
        1:    6:        return a;
        -:    7:}

It's also implemented for intermediate format, as well as color output
supports that.

gcc/ChangeLog:

2017-10-23  Martin Liska  <mliska@suse.cz>

	* doc/gcov.texi: Document that.
	* gcov.c (add_line_counts): Mark lines with a non-executed
	statement.
	(output_line_beginning): Handle such lines.
	(output_lines): Pass new argument.
	(output_intermediate_file): Print it in intermediate format.

gcc/testsuite/ChangeLog:

2017-10-23  Martin Liska  <mliska@suse.cz>

	* g++.dg/gcov/ternary.C: New test.
	* g++.dg/gcov/gcov-threads-1.C (main): Update expected line
	count.
	* lib/gcov.exp: Support new format for intermediate file format.
---
 gcc/doc/gcov.texi                          | 13 +++++---
 gcc/gcov.c                                 | 50 ++++++++++++++----------------
 gcc/testsuite/g++.dg/gcov/gcov-threads-1.C |  4 +--
 gcc/testsuite/g++.dg/gcov/ternary.C        | 12 +++++++
 gcc/testsuite/lib/gcov.exp                 |  2 +-
 5 files changed, 46 insertions(+), 35 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/gcov/ternary.C

diff --git a/gcc/doc/gcov.texi b/gcc/doc/gcov.texi
index 2aa7166e35d..4029ccb0a93 100644
--- a/gcc/doc/gcov.texi
+++ b/gcc/doc/gcov.texi
@@ -189,7 +189,7 @@ 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}
+lcount:@var{line number},@var{execution_count},@var{has_unexecuted_statement}
 branch:@var{line_number},@var{branch_coverage_type}
 
 Where the @var{branch_coverage_type} is
@@ -208,11 +208,11 @@ Here is a sample when @option{-i} is used in conjunction with @option{-b} option
 file:array.cc
 function:11,1,_Z3sumRKSt6vectorIPiSaIS0_EE
 function:22,1,main
-lcount:11,1
-lcount:12,1
-lcount:14,1
+lcount:11,1,0
+lcount:12,1,0
+lcount:14,1,0
 branch:14,taken
-lcount:26,1
+lcount:26,1,0
 branch:28,nottaken
 @end smallexample
 
@@ -341,6 +341,9 @@ used in a compilation unit.  Such functions are marked with @samp{-}
 even though they contain a code.  Use @option{-fkeep-inline-functions} and
 @option{-fkeep-static-functions} in order to properly
 record @var{execution_count} of such functions.
+Executed lines having a statement with zero @var{execution_count} end with
+@samp{*} character and are colored with magenta color with @option{-k}
+option.
 
 Some lines of information at the start have @var{line_number} of zero.
 These preamble lines are of the form
diff --git a/gcc/gcov.c b/gcc/gcov.c
index e53bcf0fd88..f9334f96eb3 100644
--- a/gcc/gcov.c
+++ b/gcc/gcov.c
@@ -256,6 +256,7 @@ typedef struct line_info
 			      Used in all-blocks mode.  */
   unsigned exists : 1;
   unsigned unexceptional : 1;
+  unsigned has_unexecuted_block : 1;
 } line_t;
 
 bool
@@ -850,28 +851,7 @@ process_args (int argc, char **argv)
 /* 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
-...
+with no source code included.
 
 The default gcov outputs multiple files: 'foo.cc.gcov',
 'iostream.gcov', 'ios_base.h.gcov', etc. with source code
@@ -901,8 +881,8 @@ output_intermediate_file (FILE *gcov_file, source_t *src)
     {
       arc_t *arc;
       if (line->exists)
-	fprintf (gcov_file, "lcount:%u,%s\n", line_num,
-		 format_gcov (line->count, 0, -1));
+	fprintf (gcov_file, "lcount:%u,%s,%d\n", line_num,
+		 format_gcov (line->count, 0, -1), line->has_unexecuted_block);
       if (flag_branches)
 	for (arc = line->branches; arc; arc = arc->line_next)
           {
@@ -2289,7 +2269,11 @@ add_line_counts (coverage_t *coverage, function_t *fn)
 		}
 	      line->exists = 1;
 	      if (!block->exceptional)
-		line->unexceptional = 1;
+		{
+		  line->unexceptional = 1;
+		  if (block->count == 0)
+		    line->has_unexecuted_block = 1;
+		}
 	      line->count += block->count;
 	    }
 	}
@@ -2496,6 +2480,7 @@ pad_count_string (string &s)
 
 static void
 output_line_beginning (FILE *f, bool exists, bool unexceptional,
+		       bool has_unexecuted_block,
 		       gcov_type count, unsigned line_num,
 		       const char *exceptional_string,
 		       const char *unexceptional_string)
@@ -2506,6 +2491,17 @@ output_line_beginning (FILE *f, bool exists, bool unexceptional,
       if (count > 0)
 	{
 	  s = format_gcov (count, 0, -1);
+	  if (has_unexecuted_block)
+	    {
+	      if (flag_use_colors)
+		{
+		  pad_count_string (s);
+		  s = SGR_SEQ (COLOR_BG_MAGENTA COLOR_SEPARATOR COLOR_FG_WHITE);
+		  s += SGR_RESET;
+		}
+	      else
+		s += "*";
+	    }
 	  pad_count_string (s);
 	}
       else
@@ -2610,7 +2606,7 @@ output_lines (FILE *gcov_file, const source_t *src)
 	 There are 16 spaces of indentation added before the source
 	 line so that tabs won't be messed up.  */
       output_line_beginning (gcov_file, line->exists, line->unexceptional,
-			     line->count, line_num,
+			     line->has_unexecuted_block, line->count, line_num,
 			     "=====", "#####");
       fprintf (gcov_file, ":%s\n", retval ? retval : "/*EOF*/");
 
@@ -2626,7 +2622,7 @@ output_lines (FILE *gcov_file, const source_t *src)
 	      if (!block->is_call_return)
 		{
 		  output_line_beginning (gcov_file, line->exists,
-					 block->exceptional,
+					 block->exceptional, false,
 					 block->count, line_num,
 					 "%%%%%", "$$$$$");
 		  fprintf (gcov_file, "-block %2d", ix++);
diff --git a/gcc/testsuite/g++.dg/gcov/gcov-threads-1.C b/gcc/testsuite/g++.dg/gcov/gcov-threads-1.C
index cc9266ab8ea..cc912f9ddf4 100644
--- a/gcc/testsuite/g++.dg/gcov/gcov-threads-1.C
+++ b/gcc/testsuite/g++.dg/gcov/gcov-threads-1.C
@@ -31,14 +31,14 @@ int main(int argc, char **argv) {
   {
     ids[i] = i;
     int r = pthread_create (&t[i], NULL, ContentionNoDeadlock_thread, &ids[i]);
-    assert (r == 0);				/* count(5) */
+    assert (r == 0);				/* count(5*) */
   }
 
   int ret;
   for (int i = 0; i < NR; i++)
     {
       int r = pthread_join (t[i], (void**)&ret);
-      assert (r == 0);				/* count(5) */
+      assert (r == 0);				/* count(5*) */
     }
 
   return 0;					/* count(1) */
diff --git a/gcc/testsuite/g++.dg/gcov/ternary.C b/gcc/testsuite/g++.dg/gcov/ternary.C
new file mode 100644
index 00000000000..d055928c295
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gcov/ternary.C
@@ -0,0 +1,12 @@
+// { dg-options "-fprofile-arcs -ftest-coverage" }
+// { dg-do run { target native } }
+
+int b, c, d, e;
+
+int main()
+{
+  int a = b < 1 ? (c < 3 ? d : c) : e;	/* count(1*) */
+  return a;
+}
+
+// { dg-final { run-gcov remove-gcda ternary.C } }
diff --git a/gcc/testsuite/lib/gcov.exp b/gcc/testsuite/lib/gcov.exp
index 632d50667a7..18adc71351a 100644
--- a/gcc/testsuite/lib/gcov.exp
+++ b/gcc/testsuite/lib/gcov.exp
@@ -108,7 +108,7 @@ proc verify-intermediate { testname testcase file } {
 	if [regexp "^function:(\[0-9\]+),(\[0-9\]+),.*" $line] {
 	    incr function
 	}
-	if [regexp "^lcount:(\[0-9\]+),(\[0-9\]+)" $line] {
+	if [regexp "^lcount:(\[0-9\]+),(\[0-9\]+),(\[01\])" $line] {
 	    incr lcount
 	}
 	if [regexp "^branch:(\[0-9\]+),(taken|nottaken|notexec)" $line] {
-- 
2.14.2


^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 5/7] GCOV: std::vector refactoring.
  2017-10-26  8:12 [PATCH 0/7] GCOV: another set of improvements marxin
  2017-10-26  8:12 ` [PATCH 2/7] GCOV: introduce usage of terminal colors marxin
@ 2017-10-26  8:12 ` marxin
  2017-10-30 14:17   ` Nathan Sidwell
  2017-10-26  8:12 ` [PATCH 1/7] GCOV: document behavior of -fkeep-{static,inline}-functions (PR gcov-profile/82633) marxin
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 33+ messages in thread
From: marxin @ 2017-10-26  8:12 UTC (permalink / raw)
  To: gcc-patches

gcc/ChangeLog:

2017-10-26  Martin Liska  <mliska@suse.cz>

	* gcov.c (struct source_info): Remove typedef.
	(source_info::source_info): Add proper ctor.
	(accumulate_line_counts): Use struct, not it's typedef.
	(output_gcov_file): Likewise.
	(output_lines): Likewise.
	(main): Do not allocate an array.
	(output_intermediate_file): Use size of vector container.
	(process_file): Resize the vector.
	(generate_results): Do not preallocate, use newly added vector
	lines.
	(release_structures): Do not release sources.
	(find_source): Use vector methods.
	(add_line_counts): Do not use typedef.
---
 gcc/gcov.c | 89 +++++++++++++++++++++++++++-----------------------------------
 1 file changed, 39 insertions(+), 50 deletions(-)

diff --git a/gcc/gcov.c b/gcc/gcov.c
index 8ba63f002d8..e2d33edb984 100644
--- a/gcc/gcov.c
+++ b/gcc/gcov.c
@@ -272,22 +272,29 @@ line_t::has_block (block_t *needle)
 /* Describes a file mentioned in the block graph.  Contains an array
    of line info.  */
 
-typedef struct source_info
+struct source_info
 {
+  /* Default constructor.  */
+  source_info ();
+
   /* Canonical name of source file.  */
   char *name;
   time_t file_time;
 
-  /* Array of line information.  */
-  line_t *lines;
-  unsigned num_lines;
+  /* Vector of line information.  */
+  vector<line_t> lines;
 
   coverage_t coverage;
 
   /* Functions in this source file.  These are in ascending line
      number order.  */
   function_t *functions;
-} source_t;
+};
+
+source_info::source_info (): name (NULL), file_time (), lines (),
+  coverage (), functions (NULL)
+{
+}
 
 typedef struct name_map
 {
@@ -300,9 +307,8 @@ typedef struct name_map
 static function_t *functions;
 static function_t **fn_end = &functions;
 
-static source_t *sources;   /* Array of source files  */
-static unsigned n_sources;  /* Number of sources */
-static unsigned a_sources;  /* Allocated sources */
+/* Vector of source files.  */
+static vector<source_info> sources;
 
 static name_map_t *names;   /* Mapping of file names to sources */
 static unsigned n_names;    /* Number of names */
@@ -448,10 +454,10 @@ static void add_line_counts (coverage_t *, function_t *);
 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 void accumulate_line_counts (source_info *);
+static void output_gcov_file (const char *, source_info *);
 static int output_branch_count (FILE *, int, const arc_t *);
-static void output_lines (FILE *, const source_t *);
+static void output_lines (FILE *, const source_info *);
 static char *make_gcov_file_name (const char *, const char *);
 static char *mangle_name (const char *, char *);
 static void release_structures (void);
@@ -668,8 +674,6 @@ main (int argc, char **argv)
 
   a_names = 10;
   names = XNEWVEC (name_map_t, a_names);
-  a_sources = 10;
-  sources = XNEWVEC (source_t, a_sources);
 
   argno = process_args (argc, argv);
   if (optind == argc)
@@ -868,7 +872,7 @@ 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)
+output_intermediate_file (FILE *gcov_file, source_info *src)
 {
   unsigned line_num;    /* current line number.  */
   const line_t *line;   /* current line info ptr.  */
@@ -885,7 +889,7 @@ output_intermediate_file (FILE *gcov_file, source_t *src)
     }
 
   for (line_num = 1, line = &src->lines[line_num];
-       line_num < src->num_lines;
+       line_num < src->lines.size ();
        line_num++, line++)
     {
       arc_t *arc;
@@ -967,8 +971,8 @@ process_file (const char *file_name)
 		    {
 		      unsigned last_line
 			= block->locations[i].lines.back () + 1;
-		      if (last_line > sources[s].num_lines)
-			sources[s].num_lines = last_line;
+		      if (last_line > sources[s].lines.size ())
+			sources[s].lines.resize (last_line);
 		    }
 		}
 	    }
@@ -987,7 +991,7 @@ process_file (const char *file_name)
 }
 
 static void
-output_gcov_file (const char *file_name, source_t *src)
+output_gcov_file (const char *file_name, source_info *src)
 {
   char *gcov_file_name = make_gcov_file_name (file_name, src->coverage.name);
 
@@ -1020,14 +1024,8 @@ output_gcov_file (const char *file_name, source_t *src)
 static void
 generate_results (const char *file_name)
 {
-  unsigned ix;
-  source_t *src;
   function_t *fn;
 
-  for (ix = n_sources, src = sources; ix--; src++)
-    if (src->num_lines)
-      src->lines = XCNEWVEC (line_t, src->num_lines);
-
   for (fn = functions; fn; fn = fn->next)
     {
       coverage_t coverage;
@@ -1052,8 +1050,10 @@ generate_results (const char *file_name)
 	file_name = canonicalize_name (file_name);
     }
 
-  for (ix = n_sources, src = sources; ix--; src++)
+  for (vector<source_info>::iterator it = sources.begin ();
+       it != sources.end (); it++)
     {
+      source_info *src = &(*it);
       if (flag_relative_only)
 	{
 	  /* Ignore this source, if it is an absolute path (after
@@ -1091,10 +1091,6 @@ release_structures (void)
   unsigned ix;
   function_t *fn;
 
-  for (ix = n_sources; ix--;)
-    free (sources[ix].lines);
-  free (sources);
-
   for (ix = n_names; ix--;)
     free (names[ix].name);
   free (names);
@@ -1239,25 +1235,15 @@ find_source (const char *file_name)
   if (!name_map)
     {
       /* Not found with canonical name, create a new source.  */
-      source_t *src;
-
-      if (n_sources == a_sources)
-	{
-	  a_sources *= 2;
-	  src = XNEWVEC (source_t, a_sources);
-	  memcpy (src, sources, n_sources * sizeof (*sources));
-	  free (sources);
-	  sources = src;
-	}
-
-      idx = n_sources;
+      source_info *src;
 
+      idx = sources.size ();
       name_map = &names[n_names++];
       name_map->name = canon;
       name_map->src = idx;
 
-      src = &sources[n_sources++];
-      memset (src, 0, sizeof (*src));
+      sources.push_back (source_info ());
+      src = &sources.back ();
       src->name = canon;
       src->coverage.name = src->name;
       if (source_length
@@ -2293,7 +2279,7 @@ add_line_counts (coverage_t *coverage, function_t *fn)
 	fn->blocks_executed++;
       for (unsigned i = 0; i < block->locations.size (); i++)
 	{
-	  const source_t *src = &sources[block->locations[i].source_file_idx];
+	  source_info *src = &sources[block->locations[i].source_file_idx];
 
 	  vector<unsigned> &lines = block->locations[i].lines;
 	  for (unsigned j = 0; j < lines.size (); j++)
@@ -2349,11 +2335,10 @@ add_line_counts (coverage_t *coverage, function_t *fn)
 /* Accumulate the line counts of a file.  */
 
 static void
-accumulate_line_counts (source_t *src)
+accumulate_line_counts (source_info *src)
 {
-  line_t *line;
   function_t *fn, *fn_p, *fn_n;
-  unsigned ix;
+  unsigned ix = 0;
 
   /* Reverse the function order.  */
   for (fn = src->functions, fn_p = NULL; fn; fn_p = fn, fn = fn_n)
@@ -2363,8 +2348,10 @@ accumulate_line_counts (source_t *src)
     }
   src->functions = fn_p;
 
-  for (ix = src->num_lines, line = src->lines; ix--; line++)
+  for (vector<line_t>::reverse_iterator it = src->lines.rbegin ();
+       it != src->lines.rend (); it++)
     {
+      line_t *line = &(*it);
       if (line->blocks)
 	{
 	  /* The user expects the line count to be the number of times
@@ -2417,6 +2404,8 @@ accumulate_line_counts (source_t *src)
 	  if (line->count)
 	    src->coverage.lines_executed++;
 	}
+
+      ix++;
     }
 }
 
@@ -2578,7 +2567,7 @@ output_line_beginning (FILE *f, bool exists, bool unexceptional,
    information.  */
 
 static void
-output_lines (FILE *gcov_file, const source_t *src)
+output_lines (FILE *gcov_file, const source_info *src)
 {
 #define  DEFAULT_LINE_START "        -:    0:"
 
@@ -2611,7 +2600,7 @@ output_lines (FILE *gcov_file, const source_t *src)
     fn = src->functions;
 
   for (line_num = 1, line = &src->lines[line_num];
-       line_num < src->num_lines; line_num++, line++)
+       line_num < src->lines.size (); line_num++, line++)
     {
       for (; fn && fn->line == line_num; fn = fn->next_file_fn)
 	{
-- 
2.14.2


^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 7/7] GCOV: std::vector refactoring III
  2017-10-26  8:12 [PATCH 0/7] GCOV: another set of improvements marxin
                   ` (3 preceding siblings ...)
  2017-10-26  8:12 ` [PATCH 4/7] GCOV: add -j argument (human readable format) marxin
@ 2017-10-26  8:12 ` marxin
  2017-10-30 14:23   ` Nathan Sidwell
  2017-10-26  8:12 ` [PATCH 3/7] GCOV: add support for lines with an unexecuted lines marxin
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 33+ messages in thread
From: marxin @ 2017-10-26  8:12 UTC (permalink / raw)
  To: gcc-patches

gcc/ChangeLog:

2017-10-26  Martin Liska  <mliska@suse.cz>

	* gcov.c (struct name_map): do not use typedef.
	Define operator== and operator<.
	(name_search): Remove.
	(name_sort): Remove.
	(main): Do not allocate names.
	(process_file): Add vertical space.
	(generate_results): Use std::find.
	(release_structures): Do not release memory.
	(find_source): Use std::find.
---
 gcc/gcov.c | 132 ++++++++++++++++++++++++++-----------------------------------
 1 file changed, 57 insertions(+), 75 deletions(-)

diff --git a/gcc/gcov.c b/gcc/gcov.c
index 7f6268c6460..865deaaafae 100644
--- a/gcc/gcov.c
+++ b/gcc/gcov.c
@@ -302,11 +302,37 @@ source_info::source_info (): name (NULL), file_time (), lines (),
 {
 }
 
-typedef struct name_map
+struct name_map
 {
-  char *name;  /* Source file name */
+  name_map ()
+  {
+  }
+
+  name_map (char *_name, unsigned _src): name (_name), src (_src)
+  {
+  }
+
+  bool operator== (const name_map &rhs) const
+  {
+#if HAVE_DOS_BASED_FILE_SYSTEM
+    return strcasecmp (this->name, rhs.name) == 0;
+#else
+    return strcmp (this->name, rhs.name) == 0;
+#endif
+  }
+
+  bool operator< (const name_map &rhs) const
+  {
+#if HAVE_DOS_BASED_FILE_SYSTEM
+    return strcasecmp (this->name, rhs.name) < 0;
+#else
+    return strcmp (this->name, rhs.name) < 0;
+#endif
+  }
+
+  const char *name;  /* Source file name */
   unsigned src;  /* Source file */
-} name_map_t;
+};
 
 /* Holds a list of function basic block graphs.  */
 
@@ -316,9 +342,8 @@ static function_t **fn_end = &functions;
 /* Vector of source files.  */
 static vector<source_info> sources;
 
-static name_map_t *names;   /* Mapping of file names to sources */
-static unsigned n_names;    /* Number of names */
-static unsigned a_names;    /* Allocated names */
+/* Mapping of file names to sources */
+static vector<name_map> names;
 
 /* This holds data summary information.  */
 
@@ -447,8 +472,6 @@ static void print_version (void) ATTRIBUTE_NORETURN;
 static void process_file (const char *);
 static void generate_results (const char *);
 static void create_file_names (const char *);
-static int name_search (const void *, const void *);
-static int name_sort (const void *, const void *);
 static char *canonicalize_name (const char *);
 static unsigned find_source (const char *);
 static function_t *read_graph_file (void);
@@ -679,9 +702,6 @@ main (int argc, char **argv)
   /* Handle response files.  */
   expandargv (&argc, &argv);
 
-  a_names = 10;
-  names = XNEWVEC (name_map_t, a_names);
-
   argno = process_args (argc, argv);
   if (optind == argc)
     print_usage (true);
@@ -950,7 +970,7 @@ process_file (const char *file_name)
 	  unsigned line = fn->line;
 	  unsigned block_no;
 	  function_t *probe, **prev;
-	  
+
 	  /* Now insert it into the source file's list of
 	     functions. Normally functions will be encountered in
 	     ascending order, so a simple scan is quick.  Note we're
@@ -1047,12 +1067,15 @@ generate_results (const char *file_name)
 	}
     }
 
+  name_map needle;
+
   if (file_name)
     {
-      name_map_t *name_map = (name_map_t *)bsearch
-	(file_name, names, n_names, sizeof (*names), name_search);
-      if (name_map)
-	file_name = sources[name_map->src].coverage.name;
+      needle.name = file_name;
+      vector<name_map>::iterator it = std::find (names.begin (), names.end (),
+						 needle);
+      if (it != names.end ())
+	file_name = sources[it->src].coverage.name;
       else
 	file_name = canonicalize_name (file_name);
     }
@@ -1095,13 +1118,8 @@ generate_results (const char *file_name)
 static void
 release_structures (void)
 {
-  unsigned ix;
   function_t *fn;
 
-  for (ix = n_names; ix--;)
-    free (names[ix].name);
-  free (names);
-
   while ((fn = functions))
     {
       functions = fn->next;
@@ -1177,77 +1195,42 @@ create_file_names (const char *file_name)
   return;
 }
 
-/* A is a string and B is a pointer to name_map_t.  Compare for file
-   name orderability.  */
-
-static int
-name_search (const void *a_, const void *b_)
-{
-  const char *a = (const char *)a_;
-  const name_map_t *b = (const name_map_t *)b_;
-
-#if HAVE_DOS_BASED_FILE_SYSTEM
-  return strcasecmp (a, b->name);
-#else
-  return strcmp (a, b->name);
-#endif
-}
-
-/* A and B are a pointer to name_map_t.  Compare for file name
-   orderability.  */
-
-static int
-name_sort (const void *a_, const void *b_)
-{
-  const name_map_t *a = (const name_map_t *)a_;
-  return name_search (a->name, b_);
-}
-
 /* Find or create a source file structure for FILE_NAME. Copies
    FILE_NAME on creation */
 
 static unsigned
 find_source (const char *file_name)
 {
-  name_map_t *name_map;
   char *canon;
   unsigned idx;
   struct stat status;
 
   if (!file_name)
     file_name = "<unknown>";
-  name_map = (name_map_t *)bsearch
-    (file_name, names, n_names, sizeof (*names), name_search);
-  if (name_map)
-    {
-      idx = name_map->src;
-      goto check_date;
-    }
 
-  if (n_names + 2 > a_names)
+  name_map needle;
+  needle.name = file_name;
+
+  vector<name_map>::iterator it = std::find (names.begin (), names.end (),
+					     needle);
+  if (it != names.end ())
     {
-      /* Extend the name map array -- we'll be inserting one or two
-	 entries.  */
-      a_names *= 2;
-      name_map = XNEWVEC (name_map_t, a_names);
-      memcpy (name_map, names, n_names * sizeof (*names));
-      free (names);
-      names = name_map;
+      idx = it->src;
+      goto check_date;
     }
 
   /* Not found, try the canonical name. */
   canon = canonicalize_name (file_name);
-  name_map = (name_map_t *) bsearch (canon, names, n_names, sizeof (*names),
-				     name_search);
-  if (!name_map)
+  needle.name = canon;
+  it = std::find (names.begin (), names.end (), needle);
+  if (it == names.end ())
     {
       /* Not found with canonical name, create a new source.  */
       source_info *src;
 
       idx = sources.size ();
-      name_map = &names[n_names++];
-      name_map->name = canon;
-      name_map->src = idx;
+      needle = name_map (canon, idx);
+      names.push_back (needle);
 
       sources.push_back (source_info ());
       src = &sources.back ();
@@ -1267,18 +1250,17 @@ find_source (const char *file_name)
 	src->file_time = status.st_mtime;
     }
   else
-    idx = name_map->src;
+    idx = it->src;
 
-  if (name_search (file_name, name_map))
+  needle.name = file_name;
+  if (std::find (names.begin (), names.end (), needle) == names.end ())
     {
       /* Append the non-canonical name.  */
-      name_map = &names[n_names++];
-      name_map->name = xstrdup (file_name);
-      name_map->src = idx;
+      names.push_back (name_map (xstrdup (file_name), idx));
     }
 
   /* Resort the name map.  */
-  qsort (names, n_names, sizeof (*names), name_sort);
+  std::sort (names.begin (), names.end ());
 
  check_date:
   if (sources[idx].file_time > bbg_file_time)
-- 
2.14.2

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 2/7] GCOV: introduce usage of terminal colors.
  2017-10-26  8:12 [PATCH 0/7] GCOV: another set of improvements marxin
@ 2017-10-26  8:12 ` marxin
  2017-10-30 12:20   ` Nathan Sidwell
  2017-10-26  8:12 ` [PATCH 5/7] GCOV: std::vector refactoring marxin
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 33+ messages in thread
From: marxin @ 2017-10-26  8:12 UTC (permalink / raw)
  To: gcc-patches

I consider using colors in context of gcov as very useful. There's
example for tramp3d:
https://pste.eu/p/Tl2D.html

gcc/ChangeLog:

2017-10-23  Martin Liska  <mliska@suse.cz>

	* color-macros.h: New file.
	* diagnostic-color.c: Factor out color related to macros to
	color-macros.h.
	* doc/gcov.texi: Document -k option.
	* gcov.c (INCLUDE_STRING): Include string.h.
	(print_usage): Add -k option.
	(process_args): Parse it.
	(pad_count_string): New function.
	(output_line_beginning): Likewise.
	(DEFAULT_LINE_START): New macro.
	(output_lines): Support color output.
---
 gcc/color-macros.h     |  50 +++++++++++++++++++++++
 gcc/diagnostic-color.c |  27 +------------
 gcc/doc/gcov.texi      |   9 +++++
 gcc/gcov.c             | 105 +++++++++++++++++++++++++++++++++++++++++--------
 4 files changed, 148 insertions(+), 43 deletions(-)
 create mode 100644 gcc/color-macros.h

diff --git a/gcc/color-macros.h b/gcc/color-macros.h
new file mode 100644
index 00000000000..b23cae49df7
--- /dev/null
+++ b/gcc/color-macros.h
@@ -0,0 +1,50 @@
+/* Terminal color manipulation macros.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_COLOR_MACROS_H
+#define GCC_COLOR_MACROS_H
+
+#define COLOR_SEPARATOR		";"
+#define COLOR_NONE		"00"
+#define COLOR_BOLD		"01"
+#define COLOR_UNDERSCORE	"04"
+#define COLOR_BLINK		"05"
+#define COLOR_REVERSE		"07"
+#define COLOR_FG_BLACK		"30"
+#define COLOR_FG_RED		"31"
+#define COLOR_FG_GREEN		"32"
+#define COLOR_FG_YELLOW		"33"
+#define COLOR_FG_BLUE		"34"
+#define COLOR_FG_MAGENTA	"35"
+#define COLOR_FG_CYAN		"36"
+#define COLOR_FG_WHITE		"37"
+#define COLOR_BG_BLACK		"40"
+#define COLOR_BG_RED		"41"
+#define COLOR_BG_GREEN		"42"
+#define COLOR_BG_YELLOW		"43"
+#define COLOR_BG_BLUE		"44"
+#define COLOR_BG_MAGENTA	"45"
+#define COLOR_BG_CYAN		"46"
+#define COLOR_BG_WHITE		"47"
+#define SGR_START		"\33["
+#define SGR_END			"m\33[K"
+#define SGR_SEQ(str)		SGR_START str SGR_END
+#define SGR_RESET		SGR_SEQ("")
+
+#endif  /* GCC_COLOR_MACROS_H */
diff --git a/gcc/diagnostic-color.c b/gcc/diagnostic-color.c
index b8cf6f2c045..0ee4bb287e0 100644
--- a/gcc/diagnostic-color.c
+++ b/gcc/diagnostic-color.c
@@ -81,32 +81,7 @@
       It would be impractical for GCC to become a full-fledged
       terminal program linked against ncurses or the like, so it will
       not detect terminfo(5) capabilities.  */
-#define COLOR_SEPARATOR		";"
-#define COLOR_NONE		"00"
-#define COLOR_BOLD		"01"
-#define COLOR_UNDERSCORE	"04"
-#define COLOR_BLINK		"05"
-#define COLOR_REVERSE		"07"
-#define COLOR_FG_BLACK		"30"
-#define COLOR_FG_RED		"31"
-#define COLOR_FG_GREEN		"32"
-#define COLOR_FG_YELLOW		"33"
-#define COLOR_FG_BLUE		"34"
-#define COLOR_FG_MAGENTA	"35"
-#define COLOR_FG_CYAN		"36"
-#define COLOR_FG_WHITE		"37"
-#define COLOR_BG_BLACK		"40"
-#define COLOR_BG_RED		"41"
-#define COLOR_BG_GREEN		"42"
-#define COLOR_BG_YELLOW		"43"
-#define COLOR_BG_BLUE		"44"
-#define COLOR_BG_MAGENTA	"45"
-#define COLOR_BG_CYAN		"46"
-#define COLOR_BG_WHITE		"47"
-#define SGR_START		"\33["
-#define SGR_END			"m\33[K"
-#define SGR_SEQ(str)		SGR_START str SGR_END
-#define SGR_RESET		SGR_SEQ("")
+#include "color-macros.h"
 
 
 /* The context and logic for choosing default --color screen attributes
diff --git a/gcc/doc/gcov.texi b/gcc/doc/gcov.texi
index c527b89f13b..2aa7166e35d 100644
--- a/gcc/doc/gcov.texi
+++ b/gcc/doc/gcov.texi
@@ -125,6 +125,7 @@ gcov [@option{-v}|@option{--version}] [@option{-h}|@option{--help}]
      [@option{-d}|@option{--display-progress}]
      [@option{-f}|@option{--function-summaries}]
      [@option{-i}|@option{--intermediate-format}]
+     [@option{-k}|@option{--use-colors}]
      [@option{-l}|@option{--long-file-names}]
      [@option{-m}|@option{--demangled-names}]
      [@option{-n}|@option{--no-output}]
@@ -215,6 +216,14 @@ lcount:26,1
 branch:28,nottaken
 @end smallexample
 
+@item -k
+@itemx --use-colors
+
+Use colors for lines of code that have zero coverage.  We use red color for
+non-exceptional lines and cyan for exceptional.  Same colors are used for
+basic blocks with @option{-a} option.
+
+
 @item -l
 @itemx --long-file-names
 Create long file names for included source files.  For example, if the
diff --git a/gcc/gcov.c b/gcc/gcov.c
index c56bac20278..e53bcf0fd88 100644
--- a/gcc/gcov.c
+++ b/gcc/gcov.c
@@ -33,6 +33,7 @@ along with Gcov; see the file COPYING3.  If not see
 #include "config.h"
 #define INCLUDE_ALGORITHM
 #define INCLUDE_VECTOR
+#define INCLUDE_STRING
 #include "system.h"
 #include "coretypes.h"
 #include "tm.h"
@@ -40,6 +41,7 @@ along with Gcov; see the file COPYING3.  If not see
 #include "diagnostic.h"
 #include "version.h"
 #include "demangle.h"
+#include "color-macros.h"
 
 #include <getopt.h>
 
@@ -381,6 +383,10 @@ static int flag_hash_filenames = 0;
 
 static int flag_verbose = 0;
 
+/* Print colored output.  */
+
+static int flag_use_colors = 0;
+
 /* Output count information for every basic block, not merely those
    that contain line number information.  */
 
@@ -703,6 +709,7 @@ print_usage (int error_p)
   fnotice (file, "  -f, --function-summaries        Output summaries for each function\n");
   fnotice (file, "  -h, --help                      Print this help, then exit\n");
   fnotice (file, "  -i, --intermediate-format       Output .gcov file in intermediate text format\n");
+  fnotice (file, "  -k, --use-colors                Emit colored output\n");
   fnotice (file, "  -l, --long-file-names           Use long output file names for included\n\
                                     source files\n");
   fnotice (file, "  -m, --demangled-names           Output demangled function names\n");
@@ -756,6 +763,7 @@ static const struct option options[] =
   { "unconditional-branches", no_argument,     NULL, 'u' },
   { "display-progress",     no_argument,       NULL, 'd' },
   { "hash-filenames",	    no_argument,       NULL, 'x' },
+  { "use-colors",	    no_argument,       NULL, 'k' },
   { 0, 0, 0, 0 }
 };
 
@@ -766,7 +774,7 @@ process_args (int argc, char **argv)
 {
   int opt;
 
-  const char *opts = "abcdfhilmno:prs:uvwx";
+  const char *opts = "abcdfhiklmno:prs:uvwx";
   while ((opt = getopt_long (argc, argv, opts, options, NULL)) != -1)
     {
       switch (opt)
@@ -789,6 +797,9 @@ process_args (int argc, char **argv)
 	case 'l':
 	  flag_long_names = 1;
 	  break;
+	case 'k':
+	  flag_use_colors = 1;
+	  break;
 	case 'm':
 	  flag_demangled_names = 1;
 	  break;
@@ -2468,6 +2479,65 @@ read_line (FILE *file)
   return pos ? string : NULL;
 }
 
+/* Pad string S with spaces from left to have total width equal to 9.  */
+
+static void
+pad_count_string (string &s)
+{
+  if (s.size () < 9)
+    s.insert (0, 9 - s.size (), ' ');
+}
+
+/* Print GCOV line beginning to F stream.  If EXISTS is set to true, the
+   line exists in source file.  UNEXCEPTIONAL indicated that it's not in
+   an exceptional statement.  The output is printed for LINE_NUM of given
+   COUNT of executions.  EXCEPTIONAL_STRING and UNEXCEPTIONAL_STRING are
+   used to indicate non-executed blocks.  */
+
+static void
+output_line_beginning (FILE *f, bool exists, bool unexceptional,
+		       gcov_type count, unsigned line_num,
+		       const char *exceptional_string,
+		       const char *unexceptional_string)
+{
+  string s;
+  if (exists)
+    {
+      if (count > 0)
+	{
+	  s = format_gcov (count, 0, -1);
+	  pad_count_string (s);
+	}
+      else
+	{
+	  if (flag_use_colors)
+	    {
+	      s = "0";
+	      pad_count_string (s);
+	      if (unexceptional)
+		s.insert (0, SGR_SEQ (COLOR_BG_RED
+				      COLOR_SEPARATOR COLOR_FG_WHITE));
+	      else
+		s.insert (0, SGR_SEQ (COLOR_BG_CYAN
+				      COLOR_SEPARATOR COLOR_FG_WHITE));
+	      s += SGR_RESET;
+	    }
+	  else
+	    {
+	      s = unexceptional ? unexceptional_string : exceptional_string;
+	      pad_count_string (s);
+	    }
+	}
+    }
+  else
+    {
+      s = "-";
+      pad_count_string (s);
+    }
+
+  fprintf (f, "%s:%5u", s.c_str (), line_num);
+}
+
 /* Read in the source file one line at a time, and output that line to
    the gcov file preceded by its execution count and other
    information.  */
@@ -2475,21 +2545,23 @@ read_line (FILE *file)
 static void
 output_lines (FILE *gcov_file, const source_t *src)
 {
+#define  DEFAULT_LINE_START "        -:    0:"
+
   FILE *source_file;
   unsigned line_num;	/* current line number.  */
   const line_t *line;           /* current line info ptr.  */
   const char *retval = "";	/* status of source file reading.  */
   function_t *fn = NULL;
 
-  fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, src->coverage.name);
+  fprintf (gcov_file, DEFAULT_LINE_START "Source:%s\n", src->coverage.name);
   if (!multiple_files)
     {
-      fprintf (gcov_file, "%9s:%5d:Graph:%s\n", "-", 0, bbg_file_name);
-      fprintf (gcov_file, "%9s:%5d:Data:%s\n", "-", 0,
+      fprintf (gcov_file, DEFAULT_LINE_START "Graph:%s\n", bbg_file_name);
+      fprintf (gcov_file, DEFAULT_LINE_START "Data:%s\n",
 	       no_data_file ? "-" : da_file_name);
-      fprintf (gcov_file, "%9s:%5d:Runs:%u\n", "-", 0, object_runs);
+      fprintf (gcov_file, DEFAULT_LINE_START "Runs:%u\n", object_runs);
     }
-  fprintf (gcov_file, "%9s:%5d:Programs:%u\n", "-", 0, program_count);
+  fprintf (gcov_file, DEFAULT_LINE_START "Programs:%u\n", program_count);
 
   source_file = fopen (src->name, "r");
   if (!source_file)
@@ -2498,7 +2570,7 @@ output_lines (FILE *gcov_file, const source_t *src)
       retval = NULL;
     }
   else if (src->file_time == 0)
-    fprintf (gcov_file, "%9s:%5d:Source is newer than graph\n", "-", 0);
+    fprintf (gcov_file, DEFAULT_LINE_START "Source is newer than graph\n");
 
   if (flag_branches)
     fn = src->functions;
@@ -2537,11 +2609,10 @@ output_lines (FILE *gcov_file, const source_t *src)
 	 Otherwise, print the execution count before the source line.
 	 There are 16 spaces of indentation added before the source
 	 line so that tabs won't be messed up.  */
-      fprintf (gcov_file, "%9s:%5u:%s\n",
-	       !line->exists ? "-" : line->count
-	       ? format_gcov (line->count, 0, -1)
-	       : line->unexceptional ? "#####" : "=====", line_num,
-	       retval ? retval : "/*EOF*/");
+      output_line_beginning (gcov_file, line->exists, line->unexceptional,
+			     line->count, line_num,
+			     "=====", "#####");
+      fprintf (gcov_file, ":%s\n", retval ? retval : "/*EOF*/");
 
       if (flag_all_blocks)
 	{
@@ -2554,11 +2625,11 @@ output_lines (FILE *gcov_file, const source_t *src)
 	    {
 	      if (!block->is_call_return)
 		{
-		  fprintf (gcov_file, "%9s:%5u-block %2d",
-			   !line->exists ? "-" : block->count
-			   ? format_gcov (block->count, 0, -1)
-			   : block->exceptional ? "%%%%%" : "$$$$$",
-			   line_num, ix++);
+		  output_line_beginning (gcov_file, line->exists,
+					 block->exceptional,
+					 block->count, line_num,
+					 "%%%%%", "$$$$$");
+		  fprintf (gcov_file, "-block %2d", ix++);
 		  if (flag_verbose)
 		    fprintf (gcov_file, " (BB %u)", block->id);
 		  fprintf (gcov_file, "\n");
-- 
2.14.2


^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 4/7] GCOV: add -j argument (human readable format).
  2017-10-26  8:12 [PATCH 0/7] GCOV: another set of improvements marxin
                   ` (2 preceding siblings ...)
  2017-10-26  8:12 ` [PATCH 1/7] GCOV: document behavior of -fkeep-{static,inline}-functions (PR gcov-profile/82633) marxin
@ 2017-10-26  8:12 ` marxin
  2017-10-30 12:44   ` Nathan Sidwell
  2017-10-26  8:12 ` [PATCH 7/7] GCOV: std::vector refactoring III marxin
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 33+ messages in thread
From: marxin @ 2017-10-26  8:12 UTC (permalink / raw)
  To: gcc-patches

Human readable format is quite useful in my opinion. There's example:

        -:    1:unsigned
   14.00K:    2:loop (unsigned n, int value)
        -:    3:{
   21.00M:    4:  for (unsigned i = 0; i < n - 1; i++)
        -:    5:  {
   20.99M:    6:    value += i;
        -:    7:  }
        -:    8:
   14.00K:    9:  return value;
        -:   10:}
        -:   11:
        1:   12:int main(int argc)
        -:   13:{
        1:   14:  unsigned sum = 0;
    7.00K:   15:  for (unsigned i = 0; i < 7 * 1000; i++)
        -:   16:  {
    7.00K:   17:    sum += loop (1000, sum);
    7.00K:   18:    sum += loop (2000, sum);
        -:   19:  }
        -:   20:
        1:   21:  return 0;
        -:   22:}

Question is do we want to do it by default, or a new option is fine?
Note that all external tools using gcov should use intermediate format
which is obviously unchanged.

gcc/ChangeLog:

2017-10-23  Martin Liska  <mliska@suse.cz>

	* doc/gcov.texi: Document new option.
	* gcov.c (print_usage): Likewise print it.
	(process_args): Support the argument.
	(format_count): New function.
	(format_gcov): Use the function.

gcc/testsuite/ChangeLog:

2017-10-23  Martin Liska  <mliska@suse.cz>

	* g++.dg/gcov/loop.C: New test.
	* lib/gcov.exp: Support human readable format for counts.
---
 gcc/doc/gcov.texi                |  5 +++++
 gcc/gcov.c                       | 43 ++++++++++++++++++++++++++++++++++++++--
 gcc/testsuite/g++.dg/gcov/loop.C | 27 +++++++++++++++++++++++++
 gcc/testsuite/lib/gcov.exp       |  2 +-
 4 files changed, 74 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/gcov/loop.C

diff --git a/gcc/doc/gcov.texi b/gcc/doc/gcov.texi
index 4029ccb0a93..9d955827706 100644
--- a/gcc/doc/gcov.texi
+++ b/gcc/doc/gcov.texi
@@ -125,6 +125,7 @@ gcov [@option{-v}|@option{--version}] [@option{-h}|@option{--help}]
      [@option{-d}|@option{--display-progress}]
      [@option{-f}|@option{--function-summaries}]
      [@option{-i}|@option{--intermediate-format}]
+     [@option{-j}|@option{--human-numbers}]
      [@option{-k}|@option{--use-colors}]
      [@option{-l}|@option{--long-file-names}]
      [@option{-m}|@option{--demangled-names}]
@@ -186,6 +187,10 @@ be used by @command{lcov} or other tools. The output is a single
 The format of the intermediate @file{.gcov} file is plain text with
 one entry per line
 
+@item -j
+@itemx --human-numbers
+Write counts in human readable format (like 24.64K).
+
 @smallexample
 file:@var{source_file_name}
 function:@var{line_number},@var{execution_count},@var{function_name}
diff --git a/gcc/gcov.c b/gcc/gcov.c
index f9334f96eb3..8ba63f002d8 100644
--- a/gcc/gcov.c
+++ b/gcc/gcov.c
@@ -393,6 +393,10 @@ static int flag_use_colors = 0;
 
 static int flag_all_blocks = 0;
 
+/* Output human readable numbers.  */
+
+static int flag_human_readable_numbers = 0;
+
 /* Output summary info for each function.  */
 
 static int flag_function_summary = 0;
@@ -710,6 +714,7 @@ print_usage (int error_p)
   fnotice (file, "  -f, --function-summaries        Output summaries for each function\n");
   fnotice (file, "  -h, --help                      Print this help, then exit\n");
   fnotice (file, "  -i, --intermediate-format       Output .gcov file in intermediate text format\n");
+  fnotice (file, "  -j, --human-numbers             Output human readable numbers\n");
   fnotice (file, "  -k, --use-colors                Emit colored output\n");
   fnotice (file, "  -l, --long-file-names           Use long output file names for included\n\
                                     source files\n");
@@ -752,6 +757,7 @@ static const struct option options[] =
   { "branch-probabilities", no_argument,       NULL, 'b' },
   { "branch-counts",        no_argument,       NULL, 'c' },
   { "intermediate-format",  no_argument,       NULL, 'i' },
+  { "human-numbers",	    no_argument,       NULL, 'j' },
   { "no-output",            no_argument,       NULL, 'n' },
   { "long-file-names",      no_argument,       NULL, 'l' },
   { "function-summaries",   no_argument,       NULL, 'f' },
@@ -775,7 +781,7 @@ process_args (int argc, char **argv)
 {
   int opt;
 
-  const char *opts = "abcdfhiklmno:prs:uvwx";
+  const char *opts = "abcdfhijklmno:prs:uvwx";
   while ((opt = getopt_long (argc, argv, opts, options, NULL)) != -1)
     {
       switch (opt)
@@ -798,6 +804,9 @@ process_args (int argc, char **argv)
 	case 'l':
 	  flag_long_names = 1;
 	  break;
+	case 'j':
+	  flag_human_readable_numbers = 1;
+	  break;
 	case 'k':
 	  flag_use_colors = 1;
 	  break;
@@ -1938,6 +1947,36 @@ add_branch_counts (coverage_t *coverage, const arc_t *arc)
     }
 }
 
+/* Format COUNT, if flag_human_readable_numbers is set, return it human
+   readable format.  */
+
+static char const *
+format_count (gcov_type count)
+{
+  static char buffer[64];
+  float v = count;
+  const char *units[] = {"", "K", "M", "G", "Y", "P", "E", "Z"};
+
+  if (count < 1000 || !flag_human_readable_numbers)
+    {
+      sprintf (buffer, "%" PRId64, count);
+      return buffer;
+    }
+
+  for (unsigned i = 0; i < sizeof (units); i++)
+    {
+      if (v < 1000.0f)
+	{
+	  sprintf (buffer, "%3.2f%s", v, units[i]);
+	  return buffer;
+	}
+
+      v /= 1000.0f;
+    }
+
+  gcc_unreachable ();
+}
+
 /* Format a GCOV_TYPE integer as either a percent ratio, or absolute
    count.  If dp >= 0, format TOP/BOTTOM * 100 to DP decimal places.
    If DP is zero, no decimal point is printed. Only print 100% when
@@ -1985,7 +2024,7 @@ format_gcov (gcov_type top, gcov_type bottom, int dp)
 	}
     }
   else
-    sprintf (buffer, "%" PRId64, (int64_t)top);
+    return format_count (top);
 
   return buffer;
 }
diff --git a/gcc/testsuite/g++.dg/gcov/loop.C b/gcc/testsuite/g++.dg/gcov/loop.C
new file mode 100644
index 00000000000..a9d404e7e11
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gcov/loop.C
@@ -0,0 +1,27 @@
+/* { dg-options "-fprofile-arcs -ftest-coverage" } */
+/* { dg-do run { target native } } */
+
+unsigned
+loop (unsigned n, int value)		  /* count(14.00K) */
+{
+  for (unsigned i = 0; i < n - 1; i++)
+  {
+    value += i;				  /* count(20.99M) */
+  }
+
+  return value;
+}
+
+int main(int argc, char **argv)
+{
+  unsigned sum = 0;
+  for (unsigned i = 0; i < 7 * 1000; i++)
+  {
+    sum += loop (1000, sum);
+    sum += loop (2000, sum);		  /* count(7.00K) */
+  }
+
+  return 0;				  /* count(1) */
+}
+
+/* { dg-final { run-gcov branches { -abj loop.C } } } */
diff --git a/gcc/testsuite/lib/gcov.exp b/gcc/testsuite/lib/gcov.exp
index 18adc71351a..38f0db09afc 100644
--- a/gcc/testsuite/lib/gcov.exp
+++ b/gcc/testsuite/lib/gcov.exp
@@ -59,7 +59,7 @@ proc verify-lines { testname testcase file } {
     while { [gets $fd line] >= 0 } {
         # We want to match both "-" and "#####" as count as well as numbers,
         # since we want to detect lines that shouldn't be marked as covered.
-	if [regexp "^ *(\[^:]*): *(\[0-9\\-#]+):.*count\\((\[0-9\\-#=]+)\\)(.*)" \
+	if [regexp "^ *(\[^:]*): *(\[0-9\\-#]+):.*count\\((\[0-9\\-#=\\.KMGYPEZ]+)\\)(.*)" \
 		"$line" all is n shouldbe rest] {
 	    if [regexp "^ *{(.*)}" $rest all xfailed] {
 		switch [dg-process-target $xfailed] {
-- 
2.14.2


^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 1/7] GCOV: document behavior of -fkeep-{static,inline}-functions (PR gcov-profile/82633).
  2017-10-26  8:12 [PATCH 0/7] GCOV: another set of improvements marxin
  2017-10-26  8:12 ` [PATCH 2/7] GCOV: introduce usage of terminal colors marxin
  2017-10-26  8:12 ` [PATCH 5/7] GCOV: std::vector refactoring marxin
@ 2017-10-26  8:12 ` marxin
  2017-10-30 12:17   ` Nathan Sidwell
  2017-10-26  8:12 ` [PATCH 4/7] GCOV: add -j argument (human readable format) marxin
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 33+ messages in thread
From: marxin @ 2017-10-26  8:12 UTC (permalink / raw)
  To: gcc-patches

gcc/ChangeLog:

2017-10-23  Martin Liska  <mliska@suse.cz>

	PR gcov-profile/82633
	* doc/gcov.texi: Document -fkeep-{static,inline}-functions and
	their interaction with GCOV infrastructure.
	* configure.ac: Add -fkeep-{inline,static}-functions to
	coverage_flags.
	* configure: Regenerate.
---
 gcc/configure     | 4 ++--
 gcc/configure.ac  | 4 ++--
 gcc/doc/gcov.texi | 5 +++++
 3 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/gcc/configure b/gcc/configure
index aa5937df84c..7f9d740e93c 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -7321,10 +7321,10 @@ fi
 if test "${enable_coverage+set}" = set; then :
   enableval=$enable_coverage; case "${enableval}" in
   yes|noopt)
-    coverage_flags="-fprofile-arcs -ftest-coverage -frandom-seed=\$@ -O0"
+    coverage_flags="-fprofile-arcs -ftest-coverage -frandom-seed=\$@ -O0 -fkeep-inline-functions -fkeep-static-functions"
     ;;
   opt)
-    coverage_flags="-fprofile-arcs -ftest-coverage -frandom-seed=\$@ -O2"
+    coverage_flags="-fprofile-arcs -ftest-coverage -frandom-seed=\$@ -O2 -fkeep-inline-functions -fkeep-static-functions"
     ;;
   no)
     # a.k.a. --disable-coverage
diff --git a/gcc/configure.ac b/gcc/configure.ac
index d905d0d980a..46b4a80b9a1 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -728,10 +728,10 @@ AC_ARG_ENABLE(coverage,
 		 default is noopt])],
 [case "${enableval}" in
   yes|noopt)
-    coverage_flags="-fprofile-arcs -ftest-coverage -frandom-seed=\$@ -O0"
+    coverage_flags="-fprofile-arcs -ftest-coverage -frandom-seed=\$@ -O0 -fkeep-inline-functions -fkeep-static-functions"
     ;;
   opt)
-    coverage_flags="-fprofile-arcs -ftest-coverage -frandom-seed=\$@ -O2"
+    coverage_flags="-fprofile-arcs -ftest-coverage -frandom-seed=\$@ -O2 -fkeep-inline-functions -fkeep-static-functions"
     ;;
   no)
     # a.k.a. --disable-coverage
diff --git a/gcc/doc/gcov.texi b/gcc/doc/gcov.texi
index 706aa6cf0b0..c527b89f13b 100644
--- a/gcc/doc/gcov.texi
+++ b/gcc/doc/gcov.texi
@@ -327,6 +327,11 @@ non-exceptional paths or only exceptional paths such as C++ exception
 handlers, respectively. Given @samp{-a} option, unexecuted blocks are
 marked @samp{$$$$$} or @samp{%%%%%}, depending on whether a basic block
 is reachable via non-exceptional or exceptional paths.
+Note that GCC can perform function removal for functions obviously not
+used in a compilation unit.  Such functions are marked with @samp{-}
+even though they contain a code.  Use @option{-fkeep-inline-functions} and
+@option{-fkeep-static-functions} in order to properly
+record @var{execution_count} of such functions.
 
 Some lines of information at the start have @var{line_number} of zero.
 These preamble lines are of the form
-- 
2.14.2


^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 6/7] GCOV: Vector refactoring II
  2017-10-26  8:12 [PATCH 0/7] GCOV: another set of improvements marxin
                   ` (5 preceding siblings ...)
  2017-10-26  8:12 ` [PATCH 3/7] GCOV: add support for lines with an unexecuted lines marxin
@ 2017-10-26  8:19 ` marxin
  2017-10-30 14:19   ` Nathan Sidwell
  2017-10-26  8:47 ` [PATCH 8/N][RFC] GCOV: support multiple functions per a line Martin Liška
  7 siblings, 1 reply; 33+ messages in thread
From: marxin @ 2017-10-26  8:19 UTC (permalink / raw)
  To: gcc-patches

gcc/ChangeLog:

2017-10-26  Martin Liska  <mliska@suse.cz>

	* gcov.c (struct line_info): Remove it's typedef.
	(line_info::line_info): Add proper ctor.
	(line_info::has_block): Do not use a typedef.
	(struct source_info): Do not use typedef.
	(circuit): Likewise.
	(get_cycles_count): Likewise.
	(output_intermediate_file): Iterate via vector iterator.
	(add_line_counts): Use std::vector methods.
	(accumulate_line_counts): Likewise.
	(output_lines): Likewise.
---
 gcc/gcov.c | 149 ++++++++++++++++++++++++++++++-------------------------------
 1 file changed, 73 insertions(+), 76 deletions(-)

diff --git a/gcc/gcov.c b/gcc/gcov.c
index e2d33edb984..7f6268c6460 100644
--- a/gcc/gcov.c
+++ b/gcc/gcov.c
@@ -108,9 +108,6 @@ typedef struct arc_info
   /* Loop making arc.  */
   unsigned int cycle : 1;
 
-  /* Next branch on line.  */
-  struct arc_info *line_next;
-
   /* Links to next arc on src and dst lists.  */
   struct arc_info *succ_next;
   struct arc_info *pred_next;
@@ -245,28 +242,37 @@ typedef struct coverage_info
 /* Describes a single line of source. Contains a chain of basic blocks
    with code on it.  */
 
-typedef struct line_info
+struct line_info
 {
+  /* Default constructor.  */
+  line_info ();
+
   /* Return true when NEEDLE is one of basic blocks the line belongs to.  */
   bool has_block (block_t *needle);
 
-  gcov_type count;	   /* execution count */
-  arc_t *branches;	   /* branches from blocks that end on this line.  */
-  block_t *blocks;	   /* blocks which start on this line.
-			      Used in all-blocks mode.  */
+  /* Execution count.  */
+  gcov_type count;
+
+  /* Branches from blocks that end on this line.  */
+  vector<arc_t *> branches;
+
+  /* blocks which start on this line.  Used in all-blocks mode.  */
+  vector<block_t *> blocks;
+
   unsigned exists : 1;
   unsigned unexceptional : 1;
   unsigned has_unexecuted_block : 1;
-} line_t;
+};
 
-bool
-line_t::has_block (block_t *needle)
+line_info::line_info (): count (0), branches (), blocks (), exists (false),
+  unexceptional (0), has_unexecuted_block (0)
 {
-  for (block_t *n = blocks; n; n = n->chain)
-    if (n == needle)
-      return true;
+}
 
-  return false;
+bool
+line_info::has_block (block_t *needle)
+{
+  return std::find (blocks.begin (), blocks.end (), needle) != blocks.end ();
 }
 
 /* Describes a file mentioned in the block graph.  Contains an array
@@ -282,7 +288,7 @@ struct source_info
   time_t file_time;
 
   /* Vector of line information.  */
-  vector<line_t> lines;
+  vector<line_info> lines;
 
   coverage_t coverage;
 
@@ -573,7 +579,7 @@ unblock (const block_t *u, block_vector_t &blocked,
 static loop_type
 circuit (block_t *v, arc_vector_t &path, block_t *start,
 	 block_vector_t &blocked, vector<block_vector_t> &block_lists,
-	 line_t &linfo, int64_t &count)
+	 line_info &linfo, int64_t &count)
 {
   loop_type result = NO_LOOP;
 
@@ -622,7 +628,7 @@ circuit (block_t *v, arc_vector_t &path, block_t *start,
    contains a negative loop, then perform the same function once again.  */
 
 static gcov_type
-get_cycles_count (line_t &linfo, bool handle_negative_cycles = true)
+get_cycles_count (line_info &linfo, bool handle_negative_cycles = true)
 {
   /* Note that this algorithm works even if blocks aren't in sorted order.
      Each iteration of the circuit detection is completely independent
@@ -632,12 +638,13 @@ get_cycles_count (line_t &linfo, bool handle_negative_cycles = true)
 
   loop_type result = NO_LOOP;
   gcov_type count = 0;
-  for (block_t *block = linfo.blocks; block; block = block->chain)
+  for (vector<block_t *>::iterator it = linfo.blocks.begin ();
+       it != linfo.blocks.end (); it++)
     {
       arc_vector_t path;
       block_vector_t blocked;
       vector<block_vector_t > block_lists;
-      result |= circuit (block, path, block, blocked, block_lists, linfo,
+      result |= circuit (*it, path, *it, blocked, block_lists, linfo,
 			 count);
     }
 
@@ -875,7 +882,7 @@ static void
 output_intermediate_file (FILE *gcov_file, source_info *src)
 {
   unsigned line_num;    /* current line number.  */
-  const line_t *line;   /* current line info ptr.  */
+  const line_info *line;   /* current line info ptr.  */
   function_t *fn;       /* current function info ptr. */
 
   fprintf (gcov_file, "file:%s\n", src->name);    /* source file name */
@@ -892,29 +899,29 @@ output_intermediate_file (FILE *gcov_file, source_info *src)
        line_num < src->lines.size ();
        line_num++, line++)
     {
-      arc_t *arc;
       if (line->exists)
 	fprintf (gcov_file, "lcount:%u,%s,%d\n", line_num,
 		 format_gcov (line->count, 0, -1), line->has_unexecuted_block);
       if (flag_branches)
-	for (arc = line->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);
-              }
-          }
+	for (vector<arc_t *>::const_iterator it = line->branches.begin ();
+	     it != line->branches.end (); it++)
+	  {
+	    if (!(*it)->is_unconditional && !(*it)->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 ((*it)->src->count)
+		  branch_type = ((*it)->count > 0) ? "taken" : "nottaken";
+		else
+		  branch_type = "notexec";
+		fprintf (gcov_file, "branch:%d,%s\n", line_num, branch_type);
+	      }
+	  }
     }
 }
 
@@ -2273,7 +2280,7 @@ add_line_counts (coverage_t *coverage, function_t *fn)
   /* Scan each basic block.  */
   for (unsigned ix = 0; ix != fn->blocks.size (); ix++)
     {
-      line_t *line = NULL;
+      line_info *line = NULL;
       block_t *block = &fn->blocks[ix];
       if (block->count && ix && ix + 1 != fn->blocks.size ())
 	fn->blocks_executed++;
@@ -2310,8 +2317,7 @@ add_line_counts (coverage_t *coverage, function_t *fn)
 	/* Entry or exit block */;
       else if (line != NULL)
 	{
-	  block->chain = line->blocks;
-	  line->blocks = block;
+	  line->blocks.push_back (block);
 
 	  if (flag_branches)
 	    {
@@ -2319,8 +2325,7 @@ add_line_counts (coverage_t *coverage, function_t *fn)
 
 	      for (arc = block->succ; arc; arc = arc->succ_next)
 		{
-		  arc->line_next = line->branches;
-		  line->branches = arc;
+		  line->branches.push_back (arc);
 		  if (coverage && !arc->is_unconditional)
 		    add_branch_counts (coverage, arc);
 		}
@@ -2348,11 +2353,11 @@ accumulate_line_counts (source_info *src)
     }
   src->functions = fn_p;
 
-  for (vector<line_t>::reverse_iterator it = src->lines.rbegin ();
+  for (vector<line_info>::reverse_iterator it = src->lines.rbegin ();
        it != src->lines.rend (); it++)
     {
-      line_t *line = &(*it);
-      if (line->blocks)
+      line_info *line = &(*it);
+      if (!line->blocks.empty ())
 	{
 	  /* The user expects the line count to be the number of times
 	     a line has been executed. Simply summing the block count
@@ -2360,36 +2365,27 @@ accumulate_line_counts (source_info *src)
 	     is to sum the entry counts to the graph of blocks on this
 	     line, then find the elementary cycles of the local graph
 	     and add the transition counts of those cycles.  */
-	  block_t *block, *block_p, *block_n;
 	  gcov_type count = 0;
 
-	  /* Reverse the block information.  */
-	  for (block = line->blocks, block_p = NULL; block;
-	       block_p = block, block = block_n)
-	    {
-	      block_n = block->chain;
-	      block->chain = block_p;
-	      block->cycle.ident = ix;
-	    }
-	  line->blocks = block_p;
-
 	  /* Sum the entry arcs.  */
-	  for (block = line->blocks; block; block = block->chain)
+	  for (vector<block_t *>::iterator it = line->blocks.begin ();
+	       it != line->blocks.end (); it++)
 	    {
 	      arc_t *arc;
 
-	      for (arc = block->pred; arc; arc = arc->pred_next)
+	      for (arc = (*it)->pred; arc; arc = arc->pred_next)
 		if (flag_branches)
 		  add_branch_counts (&src->coverage, arc);
 	    }
 
 	  /* Cycle detection.  */
-	  for (block = line->blocks; block; block = block->chain)
+	  for (vector<block_t *>::iterator it = line->blocks.begin ();
+	       it != line->blocks.end (); it++)
 	    {
-	      for (arc_t *arc = block->pred; arc; arc = arc->pred_next)
+	      for (arc_t *arc = (*it)->pred; arc; arc = arc->pred_next)
 		if (!line->has_block (arc->src))
 		  count += arc->count;
-	      for (arc_t *arc = block->succ; arc; arc = arc->succ_next)
+	      for (arc_t *arc = (*it)->succ; arc; arc = arc->succ_next)
 		arc->cs_count = arc->count;
 	    }
 
@@ -2573,7 +2569,7 @@ output_lines (FILE *gcov_file, const source_info *src)
 
   FILE *source_file;
   unsigned line_num;	/* current line number.  */
-  const line_t *line;           /* current line info ptr.  */
+  const line_info *line;  /* current line info ptr.  */
   const char *retval = "";	/* status of source file reading.  */
   function_t *fn = NULL;
 
@@ -2640,36 +2636,37 @@ output_lines (FILE *gcov_file, const source_info *src)
 
       if (flag_all_blocks)
 	{
-	  block_t *block;
 	  arc_t *arc;
 	  int ix, jx;
 
-	  for (ix = jx = 0, block = line->blocks; block;
-	       block = block->chain)
+	  ix = jx = 0;
+	  for (vector<block_t *>::const_iterator it = line->blocks.begin ();
+	       it != line->blocks.end (); it++)
 	    {
-	      if (!block->is_call_return)
+	      if (!(*it)->is_call_return)
 		{
 		  output_line_beginning (gcov_file, line->exists,
-					 block->exceptional, false,
-					 block->count, line_num,
+					 (*it)->exceptional, false,
+					 (*it)->count, line_num,
 					 "%%%%%", "$$$$$");
 		  fprintf (gcov_file, "-block %2d", ix++);
 		  if (flag_verbose)
-		    fprintf (gcov_file, " (BB %u)", block->id);
+		    fprintf (gcov_file, " (BB %u)", (*it)->id);
 		  fprintf (gcov_file, "\n");
 		}
 	      if (flag_branches)
-		for (arc = block->succ; arc; arc = arc->succ_next)
+		for (arc = (*it)->succ; arc; arc = arc->succ_next)
 		  jx += output_branch_count (gcov_file, jx, arc);
 	    }
 	}
       else if (flag_branches)
 	{
 	  int ix;
-	  arc_t *arc;
 
-	  for (ix = 0, arc = line->branches; arc; arc = arc->line_next)
-	    ix += output_branch_count (gcov_file, ix, arc);
+	  ix = 0;
+	  for (vector<arc_t *>::const_iterator it = line->branches.begin ();
+	       it != line->branches.end (); it++)
+	    ix += output_branch_count (gcov_file, ix, (*it));
 	}
     }
 
-- 
2.14.2


^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 8/N][RFC] GCOV: support multiple functions per a line
  2017-10-26  8:12 [PATCH 0/7] GCOV: another set of improvements marxin
                   ` (6 preceding siblings ...)
  2017-10-26  8:19 ` [PATCH 6/7] GCOV: Vector refactoring II marxin
@ 2017-10-26  8:47 ` Martin Liška
  2017-10-26 12:06   ` Nathan Sidwell
  7 siblings, 1 reply; 33+ messages in thread
From: Martin Liška @ 2017-10-26  8:47 UTC (permalink / raw)
  To: gcc-patches; +Cc: Nathan Sidwell

[-- Attachment #1: Type: text/plain, Size: 2223 bytes --]

Hi.

As mentioned in cover letter this patch was main motivation for the whole series.
Currently we have a list of lines (source_info::lines) per a source file. That's
changed in the patch, now each functions has:
map<unsigned, vector<line_info>> source_lines;
Thus separate lines for function for each source file the function lives in.
Having a group of function starting on a line, we print first summary and then
each individual function:

        -:    1:template<class T>
        -:    2:class Foo
        -:    3:{
        -:    4:  public:
        3:    5:  Foo()
        -:    6:  {
        3:    7:    b = 123;
        3:    8:  }
------------------
Foo<int>::Foo():
        1:    5:  Foo()
        -:    6:  {
        1:    7:    b = 123;
        1:    8:  }
------------------
Foo<float>::Foo():
        2:    5:  Foo()
        -:    6:  {
        2:    7:    b = 123;
        2:    8:  }
------------------
        -:    9:
       1*:   10:  void test() { if (!b) __builtin_abort (); b = 111; }
------------------
Foo<int>::test():
       1*:   10:  void test() { if (!b) __builtin_abort (); b = 111; }
        1:   10-block  0
    %%%%%:   10-block  1
------------------
Foo<float>::test():
    #####:   10:  void test() { if (!b) __builtin_abort (); b = 111; }
    %%%%%:   10-block  0
    %%%%%:   10-block  1
------------------
        -:   11:
        -:   12:  private:
        -:   13:  int b;
        -:   14:};
        -:   15:
        -:   16:template class Foo<float>;
        -:   17:template class Foo<int>;
        -:   18:
        1:   19:int main()
        -:   20:{
        1:   21:  Foo<int> xx;
        1:   21-block  0
        1:   22:  Foo<float> yy;
        1:   22-block  0
        1:   23:  Foo<float> zz;
        1:   23-block  0
        1:   24:  xx.test();
        1:   24-block  0
        -:   25:
        1:   26:  return 0;
        1:   26-block  0
        -:   27:}

It's also reflected in intermediate format, where lines are repeated. Currently no summary is done.

That patch is work in progress, tests are missing, documentation should be improved significantly
and changelog has to be written.

However I would like to get a feedback before I'll finish it?

Thanks,
Martin


[-- Attachment #2: 0001-GCOV-support-multiple-functions-per-a-line.patch --]
[-- Type: text/x-patch, Size: 28435 bytes --]

From 98b91fc744448f4ac70e0bc626ef08afb1f999eb Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Thu, 26 Oct 2017 10:39:40 +0200
Subject: [PATCH] GCOV: support multiple functions per a line

---
 gcc/coverage.c  |   1 +
 gcc/gcov-dump.c |   3 +
 gcc/gcov.c      | 635 +++++++++++++++++++++++++++++++++-----------------------
 3 files changed, 379 insertions(+), 260 deletions(-)

diff --git a/gcc/coverage.c b/gcc/coverage.c
index 8a56a677f15..c84cc634bb1 100644
--- a/gcc/coverage.c
+++ b/gcc/coverage.c
@@ -663,6 +663,7 @@ coverage_begin_function (unsigned lineno_checksum, unsigned cfg_checksum)
   gcov_write_unsigned (cfg_checksum);
   gcov_write_string (IDENTIFIER_POINTER
 		     (DECL_ASSEMBLER_NAME (current_function_decl)));
+  gcov_write_unsigned (DECL_ARTIFICIAL (current_function_decl));
   gcov_write_filename (xloc.file);
   gcov_write_unsigned (xloc.line);
   gcov_write_length (offset);
diff --git a/gcc/gcov-dump.c b/gcc/gcov-dump.c
index d24e72ac4a1..08524123f89 100644
--- a/gcc/gcov-dump.c
+++ b/gcc/gcov-dump.c
@@ -308,9 +308,12 @@ tag_function (const char *filename ATTRIBUTE_UNUSED,
 	  
 	  name = gcov_read_string ();
 	  printf (", `%s'", name ? name : "NULL");
+	  unsigned artificial = gcov_read_unsigned ();
 	  name = gcov_read_string ();
 	  printf (" %s", name ? name : "NULL");
 	  printf (":%u", gcov_read_unsigned ());
+	  if (artificial)
+	    printf (", artificial");
 	}
     }
 }
diff --git a/gcc/gcov.c b/gcc/gcov.c
index 865deaaafae..e250cd831a2 100644
--- a/gcc/gcov.c
+++ b/gcc/gcov.c
@@ -34,6 +34,8 @@ along with Gcov; see the file COPYING3.  If not see
 #define INCLUDE_ALGORITHM
 #define INCLUDE_VECTOR
 #define INCLUDE_STRING
+#define INCLUDE_MAP
+#define INCLUDE_SET
 #include "system.h"
 #include "coretypes.h"
 #include "tm.h"
@@ -183,6 +185,42 @@ block_info::block_info (): succ (NULL), pred (NULL), num_succ (0), num_pred (0),
   cycle.arc = NULL;
 }
 
+/* Describes a single line of source. Contains a chain of basic blocks
+   with code on it.  */
+
+struct line_info
+{
+  /* Default constructor.  */
+  line_info ();
+
+  /* Return true when NEEDLE is one of basic blocks the line belongs to.  */
+  bool has_block (block_t *needle);
+
+  /* Execution count.  */
+  gcov_type count;
+
+  /* Branches from blocks that end on this line.  */
+  vector<arc_t *> branches;
+
+  /* blocks which start on this line.  Used in all-blocks mode.  */
+  vector<block_t *> blocks;
+
+  unsigned exists : 1;
+  unsigned unexceptional : 1;
+  unsigned has_unexecuted_block : 1;
+};
+
+line_info::line_info (): count (0), branches (), blocks (), exists (false),
+  unexceptional (0), has_unexecuted_block (0)
+{
+}
+
+bool
+line_info::has_block (block_t *needle)
+{
+  return std::find (blocks.begin (), blocks.end (), needle) != blocks.end ();
+}
+
 /* Describes a single function. Contains an array of basic blocks.  */
 
 typedef struct function_info
@@ -200,6 +238,8 @@ typedef struct function_info
   /* The graph contains at least one fake incoming edge.  */
   unsigned has_catch : 1;
 
+  unsigned artificial : 1;
+
   /* Array of basic blocks.  Like in GCC, the entry block is
      at blocks[0] and the exit block is at blocks[1].  */
 #define ENTRY_BLOCK (0)
@@ -215,8 +255,8 @@ typedef struct function_info
   unsigned line;
   unsigned src;
 
-  /* Next function in same source file.  */
-  struct function_info *next_file_fn;
+  /* Vector of line information.  */
+  map<unsigned, vector<line_info>> source_lines;
 
   /* Next function.  */
   struct function_info *next;
@@ -239,42 +279,6 @@ typedef struct coverage_info
   char *name;
 } coverage_t;
 
-/* Describes a single line of source. Contains a chain of basic blocks
-   with code on it.  */
-
-struct line_info
-{
-  /* Default constructor.  */
-  line_info ();
-
-  /* Return true when NEEDLE is one of basic blocks the line belongs to.  */
-  bool has_block (block_t *needle);
-
-  /* Execution count.  */
-  gcov_type count;
-
-  /* Branches from blocks that end on this line.  */
-  vector<arc_t *> branches;
-
-  /* blocks which start on this line.  Used in all-blocks mode.  */
-  vector<block_t *> blocks;
-
-  unsigned exists : 1;
-  unsigned unexceptional : 1;
-  unsigned has_unexecuted_block : 1;
-};
-
-line_info::line_info (): count (0), branches (), blocks (), exists (false),
-  unexceptional (0), has_unexecuted_block (0)
-{
-}
-
-bool
-line_info::has_block (block_t *needle)
-{
-  return std::find (blocks.begin (), blocks.end (), needle) != blocks.end ();
-}
-
 /* Describes a file mentioned in the block graph.  Contains an array
    of line info.  */
 
@@ -283,25 +287,40 @@ struct source_info
   /* Default constructor.  */
   source_info ();
 
+  vector<function_t *> get_functions_at_location (unsigned line_num) const;
+
   /* Canonical name of source file.  */
   char *name;
   time_t file_time;
-
-  /* Vector of line information.  */
-  vector<line_info> lines;
+  unsigned index;
 
   coverage_t coverage;
 
   /* Functions in this source file.  These are in ascending line
      number order.  */
-  function_t *functions;
+  vector <function_t *> functions;
 };
 
-source_info::source_info (): name (NULL), file_time (), lines (),
-  coverage (), functions (NULL)
+source_info::source_info (): name (NULL), file_time (),
+  coverage (), functions ()
 {
 }
 
+vector<function_t *>
+source_info::get_functions_at_location (unsigned line_num) const
+{
+  vector<function_t *> r;
+
+  for (vector<function_t *>::const_iterator it = functions.begin ();
+       it != functions.end (); it++)
+    {
+      if ((*it)->line == line_num && (*it)->src == index)
+	r.push_back (*it);
+    }
+
+  return r;
+}
+
 struct name_map
 {
   name_map ()
@@ -483,7 +502,7 @@ static void add_line_counts (coverage_t *, function_t *);
 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_info *);
+static void accumulate_line_counts (source_info *, unsigned);
 static void output_gcov_file (const char *, source_info *);
 static int output_branch_count (FILE *, int, const arc_t *);
 static void output_lines (FILE *, const source_info *);
@@ -495,7 +514,7 @@ extern int main (int, char **);
 function_info::function_info (): name (NULL), demangled_name (NULL),
   ident (0), lineno_checksum (0), cfg_checksum (0), has_catch (0),
   blocks (), blocks_executed (0), counts (NULL), num_counts (0),
-  line (0), src (0), next_file_fn (NULL), next (NULL)
+  line (0), src (0), source_lines (), next (NULL)
 {
 }
 
@@ -888,6 +907,31 @@ process_args (int argc, char **argv)
   return optind;
 }
 
+static vector<unsigned>
+sum_lines_for_functions (const vector<function_t *> &fns, unsigned src_idx)
+{
+  unsigned fn_start = 0;
+  vector<unsigned> result;
+
+  for (vector<function_t *>::const_iterator it = fns.begin ();
+       it != fns.end (); it++)
+    {
+      vector<line_info> lines = (*it)->source_lines[src_idx];
+      if (fn_start == 0)
+	{
+	  result.resize (lines.size ());
+	  fn_start = (*it)->line;
+	}
+      else
+	gcc_assert (fn_start == (*it)->line && lines.size () == result.size ());
+
+    for (unsigned i = 0; i < lines.size (); i++)
+      result[i] += lines[i].count;
+    }
+
+  return result;
+}
+
 /* Output the result in intermediate format used by 'lcov'.
 
 The intermediate format contains a single file named 'foo.cc.gcov',
@@ -901,47 +945,60 @@ file 'foo.cc.gcov' similar to the above example. */
 static void
 output_intermediate_file (FILE *gcov_file, source_info *src)
 {
-  unsigned line_num;    /* current line number.  */
-  const line_info *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->next_file_fn)
+  set<unsigned> lines;
+
+  for (vector<function_t *>::iterator it = src->functions.begin ();
+       it != src->functions.end (); it++)
     {
       /* 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);
+      fprintf (gcov_file, "function:%d,%s,%s\n", (*it)->line,
+	       format_gcov ((*it)->blocks[0].count, 0, -1),
+	       flag_demangled_names ? (*it)->demangled_name : (*it)->name);
+
+      lines.insert ((*it)->line);
     }
 
-  for (line_num = 1, line = &src->lines[line_num];
-       line_num < src->lines.size ();
-       line_num++, line++)
+  for (set<unsigned>::iterator it = lines.begin (); it != lines.end (); it++)
     {
-      if (line->exists)
-	fprintf (gcov_file, "lcount:%u,%s,%d\n", line_num,
-		 format_gcov (line->count, 0, -1), line->has_unexecuted_block);
-      if (flag_branches)
-	for (vector<arc_t *>::const_iterator it = line->branches.begin ();
-	     it != line->branches.end (); it++)
-	  {
-	    if (!(*it)->is_unconditional && !(*it)->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 ((*it)->src->count)
-		  branch_type = ((*it)->count > 0) ? "taken" : "nottaken";
-		else
-		  branch_type = "notexec";
-		fprintf (gcov_file, "branch:%d,%s\n", line_num, branch_type);
-	      }
-	  }
+      vector<function_t *> fns = src->get_functions_at_location (*it);
+      for (vector<function_t *>::iterator it2 = fns.begin ();
+	   it2 != fns.end (); it2++)
+	{
+	  vector<line_info> &lines = (*it2)->source_lines[src->index];
+	  /* Print all lines covered by the function.  */
+	  for (unsigned i = 0; i < lines.size (); i++)
+	    {
+	      line_info *line = &lines[i];
+	      unsigned line_num = (*it2)->line + i;
+	      fprintf (gcov_file, "lcount:%u,%s,%d\n", line_num,
+		       format_gcov (lines[i].count, 0, -1),
+		       lines[i].has_unexecuted_block);
+
+	      if (flag_branches)
+		for (vector<arc_t *>::const_iterator it = line->branches.begin ();
+		     it != line->branches.end (); it++)
+		  {
+		    if (!(*it)->is_unconditional && !(*it)->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 ((*it)->src->count)
+			  branch_type = ((*it)->count > 0) ? "taken" : "nottaken";
+			else
+			  branch_type = "notexec";
+			fprintf (gcov_file, "branch:%d,%s\n", line_num,
+				 branch_type);
+		      }
+		  }
+	    }
+	}
     }
 }
 
@@ -967,46 +1024,46 @@ process_file (const char *file_name)
       if (fn->counts || no_data_file)
 	{
 	  unsigned src = fn->src;
-	  unsigned line = fn->line;
 	  unsigned block_no;
-	  function_t *probe, **prev;
-
-	  /* Now insert it into the source file's list of
-	     functions. Normally functions will be encountered in
-	     ascending order, so a simple scan is quick.  Note we're
-	     building this list in reverse order.  */
-	  for (prev = &sources[src].functions;
-	       (probe = *prev); prev = &probe->next_file_fn)
-	    if (probe->line <= line)
-	      break;
-	  fn->next_file_fn = probe;
-	  *prev = fn;
 
-	  /* Mark last line in files touched by function.  */
-	  for (block_no = 0; block_no != fn->blocks.size (); block_no++)
+	  /* Process only non-artificial functions.  */
+	  if (!fn->artificial)
 	    {
-	      block_t *block = &fn->blocks[block_no];
-	      for (unsigned i = 0; i < block->locations.size (); i++)
-		{
-		  unsigned s = block->locations[i].source_file_idx;
+	      sources[src].functions.push_back (fn);
 
-		  /* Sort lines of locations.  */
-		  sort (block->locations[i].lines.begin (),
-			block->locations[i].lines.end ());
-
-		  if (!block->locations[i].lines.empty ())
+	      /* Mark last line in files touched by function.  */
+	      for (block_no = 0; block_no != fn->blocks.size (); block_no++)
+		{
+		  block_t *block = &fn->blocks[block_no];
+		  for (unsigned i = 0; i < block->locations.size (); i++)
 		    {
-		      unsigned last_line
-			= block->locations[i].lines.back () + 1;
-		      if (last_line > sources[s].lines.size ())
-			sources[s].lines.resize (last_line);
+		      unsigned s = block->locations[i].source_file_idx;
+
+		      /* Sort lines of locations.  */
+		      sort (block->locations[i].lines.begin (),
+			    block->locations[i].lines.end ());
+
+		      if (!block->locations[i].lines.empty ())
+			{
+			  unsigned last_line
+			    = block->locations[i].lines.back ();
+			  map<unsigned, vector<line_info>>::iterator it
+			    = fn->source_lines.find (s);
+
+			  unsigned size = last_line - fn->line + 1;
+			  if (it == fn->source_lines.end ())
+			    fn->source_lines[s] = vector<line_info> (size);
+			  else if (fn->source_lines[s].size () < size)
+			    fn->source_lines[s].resize (size);
+			}
 		    }
 		}
+
+	      solve_flow_graph (fn);
+	      if (fn->has_catch)
+		find_exception_blocks (fn);
 	    }
 
-	  solve_flow_graph (fn);
-	  if (fn->has_catch)
-	    find_exception_blocks (fn);
 	  *fn_end = fn;
 	  fn_end = &fn->next;
 	}
@@ -1080,8 +1137,10 @@ generate_results (const char *file_name)
 	file_name = canonicalize_name (file_name);
     }
 
+  unsigned i = 0;
+
   for (vector<source_info>::iterator it = sources.begin ();
-       it != sources.end (); it++)
+       it != sources.end (); it++, i++)
     {
       source_info *src = &(*it);
       if (flag_relative_only)
@@ -1098,7 +1157,7 @@ generate_results (const char *file_name)
 	    continue;
 	}
 
-      accumulate_line_counts (src);
+      accumulate_line_counts (src, i);
       function_summary (&src->coverage, "File");
       total_lines += src->coverage.lines;
       total_executed += src->coverage.lines_executed;
@@ -1236,6 +1295,7 @@ find_source (const char *file_name)
       src = &sources.back ();
       src->name = canon;
       src->coverage.name = src->name;
+      src->index = idx;
       if (source_length
 #if HAVE_DOS_BASED_FILE_SYSTEM
 	  /* You lose if separators don't match exactly in the
@@ -1334,6 +1394,7 @@ read_graph_file (void)
 	  lineno_checksum = gcov_read_unsigned ();
 	  cfg_checksum = gcov_read_unsigned ();
 	  function_name = xstrdup (gcov_read_string ());
+	  unsigned artificial = gcov_read_unsigned ();
 	  unsigned src_idx = find_source (gcov_read_string ());
 	  lineno = gcov_read_unsigned ();
 
@@ -1350,8 +1411,8 @@ read_graph_file (void)
 	  fn->cfg_checksum = cfg_checksum;
 	  fn->src = src_idx;
 	  fn->line = lineno;
+	  fn->artificial = artificial;
 
-	  fn->next_file_fn = NULL;
 	  fn->next = NULL;
 	  *fns_end = fn;
 	  fns_end = &fn->next;
@@ -2268,27 +2329,34 @@ add_line_counts (coverage_t *coverage, function_t *fn)
 	fn->blocks_executed++;
       for (unsigned i = 0; i < block->locations.size (); i++)
 	{
-	  source_info *src = &sources[block->locations[i].source_file_idx];
+	  unsigned src_idx = block->locations[i].source_file_idx;
+
+	  map<unsigned, vector<line_info>>::iterator it
+	    = fn->source_lines.find (src_idx);
 
-	  vector<unsigned> &lines = block->locations[i].lines;
-	  for (unsigned j = 0; j < lines.size (); j++)
+	  if (it != fn->source_lines.end ())
 	    {
-	      line = &src->lines[lines[j]];
-	      if (coverage)
+	      vector<unsigned> &lines = block->locations[i].lines;
+	      for (unsigned j = 0; j < lines.size (); j++)
 		{
-		  if (!line->exists)
-		    coverage->lines++;
-		  if (!line->count && block->count)
-		    coverage->lines_executed++;
-		}
-	      line->exists = 1;
-	      if (!block->exceptional)
-		{
-		  line->unexceptional = 1;
-		  if (block->count == 0)
-		    line->has_unexecuted_block = 1;
+		  gcc_assert (lines[j] - fn->line < it->second.size ());
+		  line = &(it->second[lines[j] - fn->line]);
+		  if (coverage)
+		    {
+		      if (!line->exists)
+			coverage->lines++;
+		      if (!line->count && block->count)
+			coverage->lines_executed++;
+		    }
+		  line->exists = 1;
+		  if (!block->exceptional)
+		    {
+		      line->unexceptional = 1;
+		      if (block->count == 0)
+			line->has_unexecuted_block = 1;
+		    }
+		  line->count += block->count;
 		}
-	      line->count += block->count;
 	    }
 	}
       block->cycle.arc = NULL;
@@ -2322,68 +2390,63 @@ add_line_counts (coverage_t *coverage, function_t *fn)
 /* Accumulate the line counts of a file.  */
 
 static void
-accumulate_line_counts (source_info *src)
+accumulate_line_counts (source_info *src, unsigned src_idx)
 {
-  function_t *fn, *fn_p, *fn_n;
-  unsigned ix = 0;
-
-  /* Reverse the function order.  */
-  for (fn = src->functions, fn_p = NULL; fn; fn_p = fn, fn = fn_n)
+  for (vector<function_t *>::iterator it = src->functions.begin ();
+       it != src->functions.end (); it++)
     {
-      fn_n = fn->next_file_fn;
-      fn->next_file_fn = fn_p;
-    }
-  src->functions = fn_p;
+      map<unsigned, vector<line_info>>::iterator it2
+	= (*it)->source_lines.find (src_idx);
 
-  for (vector<line_info>::reverse_iterator it = src->lines.rbegin ();
-       it != src->lines.rend (); it++)
-    {
-      line_info *line = &(*it);
-      if (!line->blocks.empty ())
-	{
-	  /* The user expects the line count to be the number of times
-	     a line has been executed. Simply summing the block count
-	     will give an artificially high number.  The Right Thing
-	     is to sum the entry counts to the graph of blocks on this
-	     line, then find the elementary cycles of the local graph
-	     and add the transition counts of those cycles.  */
-	  gcov_type count = 0;
-
-	  /* Sum the entry arcs.  */
-	  for (vector<block_t *>::iterator it = line->blocks.begin ();
-	       it != line->blocks.end (); it++)
-	    {
-	      arc_t *arc;
-
-	      for (arc = (*it)->pred; arc; arc = arc->pred_next)
-		if (flag_branches)
-		  add_branch_counts (&src->coverage, arc);
-	    }
+      if (it2 != (*it)->source_lines.end ())
+	for (vector<line_info>::iterator it3 = it2->second.begin ();
+	     it3 != it2->second.end (); it3++)
+	  {
+	    line_info *line = &(*it3);
+	    if (!line->blocks.empty ())
+	      {
+		/* The user expects the line count to be the number of times
+		   a line has been executed. Simply summing the block count
+		   will give an artificially high number.  The Right Thing
+		   is to sum the entry counts to the graph of blocks on this
+		   line, then find the elementary cycles of the local graph
+		   and add the transition counts of those cycles.  */
+		gcov_type count = 0;
+
+		/* Sum the entry arcs.  */
+		for (vector<block_t *>::iterator it = line->blocks.begin ();
+		     it != line->blocks.end (); it++)
+		  {
+		    arc_t *arc;
 
-	  /* Cycle detection.  */
-	  for (vector<block_t *>::iterator it = line->blocks.begin ();
-	       it != line->blocks.end (); it++)
-	    {
-	      for (arc_t *arc = (*it)->pred; arc; arc = arc->pred_next)
-		if (!line->has_block (arc->src))
-		  count += arc->count;
-	      for (arc_t *arc = (*it)->succ; arc; arc = arc->succ_next)
-		arc->cs_count = arc->count;
-	    }
+		    for (arc = (*it)->pred; arc; arc = arc->pred_next)
+		      if (flag_branches)
+			add_branch_counts (&src->coverage, arc);
+		  }
 
-	  /* Now, add the count of loops entirely on this line.  */
-	  count += get_cycles_count (*line);
-	  line->count = count;
-	}
+		/* Cycle detection.  */
+		for (vector<block_t *>::iterator it = line->blocks.begin ();
+		     it != line->blocks.end (); it++)
+		  {
+		    for (arc_t *arc = (*it)->pred; arc; arc = arc->pred_next)
+		      if (!line->has_block (arc->src))
+			count += arc->count;
+		    for (arc_t *arc = (*it)->succ; arc; arc = arc->succ_next)
+		      arc->cs_count = arc->count;
+		  }
 
-      if (line->exists)
-	{
-	  src->coverage.lines++;
-	  if (line->count)
-	    src->coverage.lines_executed++;
-	}
+		/* Now, add the count of loops entirely on this line.  */
+		count += get_cycles_count (*line);
+		line->count = count;
+	      }
 
-      ix++;
+	    if (line->exists)
+	      {
+		src->coverage.lines++;
+		if (line->count)
+		  src->coverage.lines_executed++;
+	      }
+	  }
     }
 }
 
@@ -2540,6 +2603,16 @@ output_line_beginning (FILE *f, bool exists, bool unexceptional,
   fprintf (f, "%s:%5u", s.c_str (), line_num);
 }
 
+static void
+print_source_line (FILE *f, const vector<const char *> &source_lines,
+		   unsigned line)
+{
+  gcc_assert (line >= 1);
+  gcc_assert (line <= source_lines.size ());
+
+  fprintf (f, ":%s\n", source_lines[line - 1]);
+}
+
 /* Read in the source file one line at a time, and output that line to
    the gcov file preceded by its execution count and other
    information.  */
@@ -2550,10 +2623,7 @@ output_lines (FILE *gcov_file, const source_info *src)
 #define  DEFAULT_LINE_START "        -:    0:"
 
   FILE *source_file;
-  unsigned line_num;	/* current line number.  */
-  const line_info *line;  /* current line info ptr.  */
   const char *retval = "";	/* status of source file reading.  */
-  function_t *fn = NULL;
 
   fprintf (gcov_file, DEFAULT_LINE_START "Source:%s\n", src->coverage.name);
   if (!multiple_files)
@@ -2574,92 +2644,137 @@ output_lines (FILE *gcov_file, const source_info *src)
   else if (src->file_time == 0)
     fprintf (gcov_file, DEFAULT_LINE_START "Source is newer than graph\n");
 
-  if (flag_branches)
-    fn = src->functions;
+  vector<const char *> source_lines;
+  while ((retval = read_line (source_file)) != NULL)
+    source_lines.push_back (xstrdup (retval));
 
-  for (line_num = 1, line = &src->lines[line_num];
-       line_num < src->lines.size (); line_num++, line++)
+  for (unsigned line_num = 1; line_num <= source_lines.size (); line_num++)
     {
-      for (; fn && fn->line == line_num; fn = fn->next_file_fn)
-	{
-	  arc_t *arc = fn->blocks[EXIT_BLOCK].pred;
-	  gcov_type return_count = fn->blocks[EXIT_BLOCK].count;
-	  gcov_type called_count = fn->blocks[ENTRY_BLOCK].count;
-
-	  for (; arc; arc = arc->pred_next)
-	    if (arc->fake)
-	      return_count -= arc->count;
-
-	  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",
-		   format_gcov (return_count, called_count, 0));
-	  fprintf (gcov_file, " blocks executed %s",
-		   format_gcov (fn->blocks_executed, fn->blocks.size () - 2,
-				0));
-	  fprintf (gcov_file, "\n");
-	}
+      vector<function_t *> fns = src->get_functions_at_location (line_num);
 
-      if (retval)
-	retval = read_line (source_file);
-
-      /* For lines which don't exist in the .bb file, print '-' before
-	 the source line.  For lines which exist but were never
-	 executed, print '#####' or '=====' before the source line.
-	 Otherwise, print the execution count before the source line.
-	 There are 16 spaces of indentation added before the source
-	 line so that tabs won't be messed up.  */
-      output_line_beginning (gcov_file, line->exists, line->unexceptional,
-			     line->has_unexecuted_block, line->count, line_num,
-			     "=====", "#####");
-      fprintf (gcov_file, ":%s\n", retval ? retval : "/*EOF*/");
-
-      if (flag_all_blocks)
+      if (!fns.empty ())
 	{
-	  arc_t *arc;
-	  int ix, jx;
+	  bool is_group = fns.size () > 1;
+	  /* Print line summary for group of function.  */
+	  if (is_group)
+	    {
+	      vector<unsigned> sums = sum_lines_for_functions (fns, src->index);
+	      for (unsigned i = 0; i < sums.size (); i++)
+		{
+		  line_info *line = &fns[0]->source_lines[src->index][i];
+		  output_line_beginning (gcov_file, line->exists, line->unexceptional,
+					 line->has_unexecuted_block, sums[i],
+					 line_num + i, "=====", "#####");
+
+		  print_source_line (gcov_file, source_lines,
+				     line_num + i);
+		}
+	    }
 
-	  ix = jx = 0;
-	  for (vector<block_t *>::const_iterator it = line->blocks.begin ();
-	       it != line->blocks.end (); it++)
+	  unsigned line_covered = 0;
+	  for (vector<function_t *>::iterator it = fns.begin ();
+	       it != fns.end (); it++)
 	    {
-	      if (!(*it)->is_call_return)
+	      function_info *fn = *it;
+	      vector<line_info> &lines = fn->source_lines[src->index];
+	      line_covered = lines.size ();
+
+	      if (is_group)
 		{
-		  output_line_beginning (gcov_file, line->exists,
-					 (*it)->exceptional, false,
-					 (*it)->count, line_num,
-					 "%%%%%", "$$$$$");
-		  fprintf (gcov_file, "-block %2d", ix++);
-		  if (flag_verbose)
-		    fprintf (gcov_file, " (BB %u)", (*it)->id);
-		  fprintf (gcov_file, "\n");
+		  fprintf (gcov_file, "------------------\n");
+		  fprintf (gcov_file, "%s:\n", flag_demangled_names ?
+			   fn->demangled_name : fn->name);
 		}
+
 	      if (flag_branches)
-		for (arc = (*it)->succ; arc; arc = arc->succ_next)
-		  jx += output_branch_count (gcov_file, jx, arc);
+		{
+		  arc_t *arc = fn->blocks[EXIT_BLOCK].pred;
+		  gcov_type return_count = fn->blocks[EXIT_BLOCK].count;
+		  gcov_type called_count = fn->blocks[ENTRY_BLOCK].count;
+
+		  for (; arc; arc = arc->pred_next)
+		    if (arc->fake)
+		      return_count -= arc->count;
+
+		  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",
+			   format_gcov (return_count, called_count, 0));
+		  fprintf (gcov_file, " blocks executed %s",
+			   format_gcov (fn->blocks_executed, fn->blocks.size () - 2,
+					0));
+		  fprintf (gcov_file, "\n");
+		}
+
+	      /* Print all lines covered by the function.  */
+	      for (unsigned i = 0; i < lines.size (); i++)
+		{
+		  line_info *line = &lines[i];
+		  unsigned l = fn->line + i;
+
+		  /* For lines which don't exist in the .bb file, print '-' before
+		     the source line.  For lines which exist but were never
+		     executed, print '#####' or '=====' before the source line.
+		     Otherwise, print the execution count before the source line.
+		     There are 16 spaces of indentation added before the source
+		     line so that tabs won't be messed up.  */
+		  output_line_beginning (gcov_file, line->exists, line->unexceptional,
+					 line->has_unexecuted_block, line->count,
+					 l, "=====", "#####");
+
+		  print_source_line (gcov_file, source_lines, l);
+		  if (flag_all_blocks)
+		    {
+		      arc_t *arc;
+		      int ix, jx;
+
+		      ix = jx = 0;
+		      for (vector<block_t *>::const_iterator it = line->blocks.begin();
+			   it != line->blocks.end (); it++)
+			{
+			  if (!(*it)->is_call_return)
+			    {
+			      output_line_beginning (gcov_file, line->exists,
+						     (*it)->exceptional, false,
+						     (*it)->count, l,
+						     "%%%%%", "$$$$$");
+			      fprintf (gcov_file, "-block %2d", ix++);
+			      if (flag_verbose)
+				fprintf (gcov_file, " (BB %u)", (*it)->id);
+			      fprintf (gcov_file, "\n");
+			    }
+			  if (flag_branches)
+			    for (arc = (*it)->succ; arc; arc = arc->succ_next)
+			      jx += output_branch_count (gcov_file, jx, arc);
+			}
+		    }
+		  else if (flag_branches)
+		    {
+		      int ix;
+
+		      ix = 0;
+		      for (vector<arc_t *>::const_iterator it = line->branches.begin ();
+			   it != line->branches.end (); it++)
+			ix += output_branch_count (gcov_file, ix, (*it));
+		    }
+
+		}
 	    }
+
+	  if (is_group)
+	    fprintf (gcov_file, "------------------\n");
+
+	  line_num += (line_covered - 1);
 	}
-      else if (flag_branches)
+      else
 	{
-	  int ix;
-
-	  ix = 0;
-	  for (vector<arc_t *>::const_iterator it = line->branches.begin ();
-	       it != line->branches.end (); it++)
-	    ix += output_branch_count (gcov_file, ix, (*it));
+	  fprintf (gcov_file, "%9s:%5u", "-", line_num);
+	  print_source_line (gcov_file, source_lines, line_num);
 	}
     }
 
-  /* Handle all remaining source lines.  There may be lines after the
-     last line of code.  */
-  if (retval)
-    {
-      for (; (retval = read_line (source_file)); line_num++)
-	fprintf (gcov_file, "%9s:%5u:%s\n", "-", line_num, retval);
-    }
-
   if (source_file)
     fclose (source_file);
 }
-- 
2.14.2


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 8/N][RFC] GCOV: support multiple functions per a line
  2017-10-26  8:47 ` [PATCH 8/N][RFC] GCOV: support multiple functions per a line Martin Liška
@ 2017-10-26 12:06   ` Nathan Sidwell
  2017-11-01  8:00     ` [PATCH 8/N][RFC] v2 " Martin Liška
  0 siblings, 1 reply; 33+ messages in thread
From: Nathan Sidwell @ 2017-10-26 12:06 UTC (permalink / raw)
  To: Martin Liška, gcc-patches

On 10/26/2017 04:44 AM, Martin Liška wrote:
> Hi.
> 
> As mentioned in cover letter this patch was main motivation for the whole series.
> Currently we have a list of lines (source_info::lines) per a source file. That's
> changed in the patch, now each functions has:
> map<unsigned, vector<line_info>> source_lines;
> Thus separate lines for function for each source file the function lives in.
> Having a group of function starting on a line, we print first summary and then
> each individual function:

This looks great
> 


-- 
Nathan Sidwell

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 1/7] GCOV: document behavior of -fkeep-{static,inline}-functions (PR gcov-profile/82633).
  2017-10-26  8:12 ` [PATCH 1/7] GCOV: document behavior of -fkeep-{static,inline}-functions (PR gcov-profile/82633) marxin
@ 2017-10-30 12:17   ` Nathan Sidwell
  2017-10-31 11:12     ` Martin Liška
  0 siblings, 1 reply; 33+ messages in thread
From: Nathan Sidwell @ 2017-10-30 12:17 UTC (permalink / raw)
  To: marxin, gcc-patches

On 10/26/2017 04:11 AM, marxin wrote:
> gcc/ChangeLog:

> +++ b/gcc/doc/gcov.texi
> @@ -327,6 +327,11 @@ non-exceptional paths or only exceptional paths such as C++ exception
>   handlers, respectively. Given @samp{-a} option, unexecuted blocks are
>   marked @samp{$$$$$} or @samp{%%%%%}, depending on whether a basic block
>   is reachable via non-exceptional or exceptional paths.
> +Note that GCC can perform function removal for functions obviously not
> +used in a compilation unit.  Such functions are marked with @samp{-}
> +even though they contain a code.  Use @option{-fkeep-inline-functions} and
> +@option{-fkeep-static-functions} in order to properly
> +record @var{execution_count} of such functions.

This reads a little oddly.  How about:

Note that GCC can completely remove the bodies of functions that are not 
needed -- for instance if they are inlined everywhere.  Such functions 
are marked with @samp{-}, which can be confusing.  Use the 
@option{-fkeep-inline-functions} and @option{-fkeep-static-functions} 
options to retain these functions and allow gcov to properly show their 
@var{execution_count}.

Ok with that (or something approximating it).

nathan
-- 
Nathan Sidwell

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 2/7] GCOV: introduce usage of terminal colors.
  2017-10-26  8:12 ` [PATCH 2/7] GCOV: introduce usage of terminal colors marxin
@ 2017-10-30 12:20   ` Nathan Sidwell
  2017-10-30 14:53     ` David Malcolm
  0 siblings, 1 reply; 33+ messages in thread
From: Nathan Sidwell @ 2017-10-30 12:20 UTC (permalink / raw)
  To: marxin, gcc-patches; +Cc: David Malcolm

On 10/26/2017 04:11 AM, marxin wrote:
> I consider using colors in context of gcov as very useful. There's
> example for tramp3d:
> https://pste.eu/p/Tl2D.html

nice!

> gcc/ChangeLog:
> 
> 2017-10-23  Martin Liska  <mliska@suse.cz>
> 
> 	* color-macros.h: New file.
> 	* diagnostic-color.c: Factor out color related to macros to
> 	color-macros.h.
> 	* doc/gcov.texi: Document -k option.
> 	* gcov.c (INCLUDE_STRING): Include string.h.
> 	(print_usage): Add -k option.
> 	(process_args): Parse it.
> 	(pad_count_string): New function.
> 	(output_line_beginning): Likewise.
> 	(DEFAULT_LINE_START): New macro.
> 	(output_lines): Support color output.

The gcov changes are ok.  I guess David has the review ball for the 
diagnostic refactoring?

nathan

-- 
Nathan Sidwell

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 3/7] GCOV: add support for lines with an unexecuted lines.
  2017-10-26  8:12 ` [PATCH 3/7] GCOV: add support for lines with an unexecuted lines marxin
@ 2017-10-30 12:27   ` Nathan Sidwell
  2017-10-31 11:29     ` Martin Liška
  2017-11-02 15:33   ` Eric Botcazou
  1 sibling, 1 reply; 33+ messages in thread
From: Nathan Sidwell @ 2017-10-30 12:27 UTC (permalink / raw)
  To: marxin, gcc-patches

On 10/26/2017 04:11 AM, marxin wrote:
> It's possible to have a line of code that has a non-zero coverage.
> However, it can contain unexecuted blocks and I hope adding a
> notification can be usefull. LLVM also does that:

>         1*:    5:	int a = b < 1 ? (c < 3 ? d : c) : e;

A useful enhancement.



>   function:@var{line_number},@var{execution_count},@var{function_name}
> -lcount:@var{line number},@var{execution_count}
> +lcount:@var{line number},@var{execution_count},@var{has_unexecuted_statement}

Is 'statement' the right phrase.  Pedantically it is 'basic block', 
which the documentation does discuss.  So perhaps 'has_unexecuted_block'?

>   
> @@ -341,6 +341,9 @@ used in a compilation unit.  Such functions are marked with @samp{-}
>   even though they contain a code.  Use @option{-fkeep-inline-functions} and
>   @option{-fkeep-static-functions} in order to properly
>   record @var{execution_count} of such functions.
> +Executed lines having a statement with zero @var{execution_count} end with
> +@samp{*} character and are colored with magenta color with @option{-k}
> +option.

Same comment.


> +  unsigned has_unexecuted_block : 1;

Heh, and the code matches my thought :)

Looks good otherwise, WDYT?

nathan

-- 
Nathan Sidwell

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 4/7] GCOV: add -j argument (human readable format).
  2017-10-26  8:12 ` [PATCH 4/7] GCOV: add -j argument (human readable format) marxin
@ 2017-10-30 12:44   ` Nathan Sidwell
  2017-10-31 11:54     ` Martin Liška
  0 siblings, 1 reply; 33+ messages in thread
From: Nathan Sidwell @ 2017-10-30 12:44 UTC (permalink / raw)
  To: marxin, gcc-patches

On 10/26/2017 04:11 AM, marxin wrote:
> Human readable format is quite useful in my opinion. There's example:
> 
>          -:    1:unsigned
>     14.00K:    2:loop (unsigned n, int value)

My first thought is 'why 2 decimal places'?  That seems excessive.  Zero 
surely suffices?

> Question is do we want to do it by default, or a new option is fine?
> Note that all external tools using gcov should use intermediate format
> which is obviously unchanged.

I don't think other tools do it by default.


> +     [@option{-j}|@option{--human-numbers}]

man ls:
       -h, --human-readable
               with -l and/or -s, print human readable sizes (e.g., 1K 
234M 2G)

Sadly '-h' is help (IIRC).  but we could at least copy the long form.


> +  const char *units[] = {"", "K", "M", "G", "Y", "P", "E", "Z"};

Those aren't right  KMGTPEZY
http://www.npl.co.uk/reference/measurement-units/si-prefixes/


> +  for (unsigned i = 0; i < sizeof (units); i++)
> +    {
> +      if (v < 1000.0f)
> +	{
> +	  sprintf (buffer, "%3.2f%s", v, units[i]);
> +	  return buffer;
> +	}
> +
> +      v /= 1000.0f;
> +    }

that's going to fail on certain roundings.  You're doing multiple 
divisions by 1000, which itself will have roundings.  But more 
importantly, numbers after scaling like 999.999 will round to 1000, and 
you probably don't want that.  This kind of formatting is tricky.  My 
inclination is to keep it in the integer domain.  Determine a scaling 
factor.  Divide once by that and then explicitly round to nearest 
even[*] with a check for the 999.9 case.  Then print as an unsigned.

nathan

[*] I presume you're familiar with RNE?  That's probably overkill and 
plain RN would be adequate.

-- 
Nathan Sidwell

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 5/7] GCOV: std::vector refactoring.
  2017-10-26  8:12 ` [PATCH 5/7] GCOV: std::vector refactoring marxin
@ 2017-10-30 14:17   ` Nathan Sidwell
  0 siblings, 0 replies; 33+ messages in thread
From: Nathan Sidwell @ 2017-10-30 14:17 UTC (permalink / raw)
  To: marxin, gcc-patches

On 10/26/2017 04:11 AM, marxin wrote:
> gcc/ChangeLog:
> 
> 2017-10-26  Martin Liska  <mliska@suse.cz>
> 
> 	* gcov.c (struct source_info): Remove typedef.
> 	(source_info::source_info): Add proper ctor.
> 	(accumulate_line_counts): Use struct, not it's typedef.
> 	(output_gcov_file): Likewise.
> 	(output_lines): Likewise.
> 	(main): Do not allocate an array.
> 	(output_intermediate_file): Use size of vector container.
> 	(process_file): Resize the vector.
> 	(generate_results): Do not preallocate, use newly added vector
> 	lines.
> 	(release_structures): Do not release sources.
> 	(find_source): Use vector methods.
> 	(add_line_counts): Do not use typedef.

ok


-- 
Nathan Sidwell

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 6/7] GCOV: Vector refactoring II
  2017-10-26  8:19 ` [PATCH 6/7] GCOV: Vector refactoring II marxin
@ 2017-10-30 14:19   ` Nathan Sidwell
  0 siblings, 0 replies; 33+ messages in thread
From: Nathan Sidwell @ 2017-10-30 14:19 UTC (permalink / raw)
  To: marxin, gcc-patches

On 10/26/2017 04:11 AM, marxin wrote:
> gcc/ChangeLog:
> 
> 2017-10-26  Martin Liska  <mliska@suse.cz>
> 
> 	* gcov.c (struct line_info): Remove it's typedef.
> 	(line_info::line_info): Add proper ctor.
> 	(line_info::has_block): Do not use a typedef.
> 	(struct source_info): Do not use typedef.
> 	(circuit): Likewise.
> 	(get_cycles_count): Likewise.
> 	(output_intermediate_file): Iterate via vector iterator.
> 	(add_line_counts): Use std::vector methods.
> 	(accumulate_line_counts): Likewise.
> 	(output_lines): Likewise.

ok.


-- 
Nathan Sidwell

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 7/7] GCOV: std::vector refactoring III
  2017-10-26  8:12 ` [PATCH 7/7] GCOV: std::vector refactoring III marxin
@ 2017-10-30 14:23   ` Nathan Sidwell
  0 siblings, 0 replies; 33+ messages in thread
From: Nathan Sidwell @ 2017-10-30 14:23 UTC (permalink / raw)
  To: marxin, gcc-patches

On 10/26/2017 04:11 AM, marxin wrote:
> gcc/ChangeLog:
> 
> 2017-10-26  Martin Liska  <mliska@suse.cz>
> 
> 	* gcov.c (struct name_map): do not use typedef.
> 	Define operator== and operator<.
> 	(name_search): Remove.
> 	(name_sort): Remove.
> 	(main): Do not allocate names.
> 	(process_file): Add vertical space.
> 	(generate_results): Use std::find.
> 	(release_structures): Do not release memory.
> 	(find_source): Use std::find.

You're giving it operators, so perhaps rename it to class but with 
public accessors?

Ok with/without that change.

nathan

-- 
Nathan Sidwell

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 2/7] GCOV: introduce usage of terminal colors.
  2017-10-30 12:20   ` Nathan Sidwell
@ 2017-10-30 14:53     ` David Malcolm
  2017-10-31 11:14       ` Martin Liška
  0 siblings, 1 reply; 33+ messages in thread
From: David Malcolm @ 2017-10-30 14:53 UTC (permalink / raw)
  To: Nathan Sidwell, marxin, gcc-patches

On Mon, 2017-10-30 at 08:17 -0400, Nathan Sidwell wrote:
> On 10/26/2017 04:11 AM, marxin wrote:
> > I consider using colors in context of gcov as very useful. There's
> > example for tramp3d:
> > https://pste.eu/p/Tl2D.html
> 
> nice!
> 
> > gcc/ChangeLog:
> > 
> > 2017-10-23  Martin Liska  <mliska@suse.cz>
> > 
> > 	* color-macros.h: New file.
> > 	* diagnostic-color.c: Factor out color related to macros to
> > 	color-macros.h.
> > 	* doc/gcov.texi: Document -k option.
> > 	* gcov.c (INCLUDE_STRING): Include string.h.
> > 	(print_usage): Add -k option.
> > 	(process_args): Parse it.
> > 	(pad_count_string): New function.
> > 	(output_line_beginning): Likewise.
> > 	(DEFAULT_LINE_START): New macro.
> > 	(output_lines): Support color output.
> 
> The gcov changes are ok.  I guess David has the review ball for the 
> diagnostic refactoring?
> 
> nathan

The comments beginning:
+/* Select Graphic Rendition (SGR, "\33[...m") strings.  */

and ending:
      It would be impractical for GCC to become a full-fledged
      terminal program linked against ncurses or the like, so it will
      not detect terminfo(5) capabilities.  */

are still in diagnostic-color.c after your patch, but they are
describing the macros, and in particular, if I'm reading them right,
are a rationale for why SGR_END contains a "\33[K".

Hence I think that those comments should also be moved to color-
macros.h.

Other than that the diagnostic changes mostly look good to me, but the
color-macros.h has:
+   Copyright (C) 2017 Free Software Foundation, Inc.

Shouldn't that copyright line express the full range of years for the
existing content that's being moved from diagnostic-color.c?

The macros there were introduced by the creation of diagnostic-color.c
in r197842 in April 2013 (aka dc604d41825b3cbd09045baeef09b1b88fc5a02),
which had the copyright line:

+   Copyright 2011-2013 Free Software Foundation, Inc.


FWIW, the macros seem to come from this patch by Manu:
"RFC: color diagnostics markers"
  https://gcc.gnu.org/ml/gcc-patches/2013-03/msg01365.html

where the code in question has:

/* Based on code from: */
+/* grep.c - main driver file for grep.
+   Copyright (C) 1992, 1997-2002, 2004-2013 Free Software Foundation,
Inc.

though as far as I can tell the only material taken directly from
"grep" are the comments I mentioned above beginning i.e. it appears to
me that the actual macros in the new file were written by Manu in 2013.

The comments mentioned above come from GNU grep; looking at the history
in grep's git repo shows it comes from commit
56623b5129d487cb6673fa5a582d094edc1fe983:

2005-06-20  Charles Levert  <charles_levert@gna.org>

       * src/grep.c: Extensively document the SGR/EL-to-Right issue.

Hence I believe the color-macros.h copyright line should range from 
2005-2017 if you move the comments (please do), or from 2013-2017 as-
is.

Diagnostic refactoring is OK otherwise.

Dave

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 1/7] GCOV: document behavior of -fkeep-{static,inline}-functions (PR gcov-profile/82633).
  2017-10-30 12:17   ` Nathan Sidwell
@ 2017-10-31 11:12     ` Martin Liška
  0 siblings, 0 replies; 33+ messages in thread
From: Martin Liška @ 2017-10-31 11:12 UTC (permalink / raw)
  To: Nathan Sidwell, gcc-patches

On 10/30/2017 01:15 PM, Nathan Sidwell wrote:
> On 10/26/2017 04:11 AM, marxin wrote:
>> gcc/ChangeLog:
> 
>> +++ b/gcc/doc/gcov.texi
>> @@ -327,6 +327,11 @@ non-exceptional paths or only exceptional paths such as C++ exception
>>   handlers, respectively. Given @samp{-a} option, unexecuted blocks are
>>   marked @samp{$$$$$} or @samp{%%%%%}, depending on whether a basic block
>>   is reachable via non-exceptional or exceptional paths.
>> +Note that GCC can perform function removal for functions obviously not
>> +used in a compilation unit.  Such functions are marked with @samp{-}
>> +even though they contain a code.  Use @option{-fkeep-inline-functions} and
>> +@option{-fkeep-static-functions} in order to properly
>> +record @var{execution_count} of such functions.
> 
> This reads a little oddly.  How about:
> 
> Note that GCC can completely remove the bodies of functions that are not needed -- for instance if they are inlined everywhere.  Such functions are marked with @samp{-}, which can be confusing.  Use the @option{-fkeep-inline-functions} and @option{-fkeep-static-functions} options to retain these functions and allow gcov to properly show their @var{execution_count}.

Hi.

Thanks for rewriting that. Native speakers always produce more readable documentation.
I'll install your version as it is.

Martin

> 
> Ok with that (or something approximating it).
> 
> nathan

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 2/7] GCOV: introduce usage of terminal colors.
  2017-10-30 14:53     ` David Malcolm
@ 2017-10-31 11:14       ` Martin Liška
  0 siblings, 0 replies; 33+ messages in thread
From: Martin Liška @ 2017-10-31 11:14 UTC (permalink / raw)
  To: David Malcolm, Nathan Sidwell, gcc-patches

On 10/30/2017 03:49 PM, David Malcolm wrote:
> On Mon, 2017-10-30 at 08:17 -0400, Nathan Sidwell wrote:
>> On 10/26/2017 04:11 AM, marxin wrote:
>>> I consider using colors in context of gcov as very useful. There's
>>> example for tramp3d:
>>> https://pste.eu/p/Tl2D.html
>>
>> nice!
>>
>>> gcc/ChangeLog:
>>>
>>> 2017-10-23  Martin Liska  <mliska@suse.cz>
>>>
>>> 	* color-macros.h: New file.
>>> 	* diagnostic-color.c: Factor out color related to macros to
>>> 	color-macros.h.
>>> 	* doc/gcov.texi: Document -k option.
>>> 	* gcov.c (INCLUDE_STRING): Include string.h.
>>> 	(print_usage): Add -k option.
>>> 	(process_args): Parse it.
>>> 	(pad_count_string): New function.
>>> 	(output_line_beginning): Likewise.
>>> 	(DEFAULT_LINE_START): New macro.
>>> 	(output_lines): Support color output.
>>
>> The gcov changes are ok.  I guess David has the review ball for the
>> diagnostic refactoring?
>>
>> nathan
> 
> The comments beginning:
> +/* Select Graphic Rendition (SGR, "\33[...m") strings.  */
> 
> and ending:
>        It would be impractical for GCC to become a full-fledged
>        terminal program linked against ncurses or the like, so it will
>        not detect terminfo(5) capabilities.  */
> 
> are still in diagnostic-color.c after your patch, but they are
> describing the macros, and in particular, if I'm reading them right,
> are a rationale for why SGR_END contains a "\33[K".
> 
> Hence I think that those comments should also be moved to color-
> macros.h.

Hi.

Will do that.

> 
> Other than that the diagnostic changes mostly look good to me, but the
> color-macros.h has:
> +   Copyright (C) 2017 Free Software Foundation, Inc.
> 
> Shouldn't that copyright line express the full range of years for the
> existing content that's being moved from diagnostic-color.c?
> 
> The macros there were introduced by the creation of diagnostic-color.c
> in r197842 in April 2013 (aka dc604d41825b3cbd09045baeef09b1b88fc5a02),
> which had the copyright line:
> 
> +   Copyright 2011-2013 Free Software Foundation, Inc.
> 
> 
> FWIW, the macros seem to come from this patch by Manu:
> "RFC: color diagnostics markers"
>    https://gcc.gnu.org/ml/gcc-patches/2013-03/msg01365.html
> 
> where the code in question has:
> 
> /* Based on code from: */
> +/* grep.c - main driver file for grep.
> +   Copyright (C) 1992, 1997-2002, 2004-2013 Free Software Foundation,
> Inc.
> 
> though as far as I can tell the only material taken directly from
> "grep" are the comments I mentioned above beginning i.e. it appears to
> me that the actual macros in the new file were written by Manu in 2013.
> 
> The comments mentioned above come from GNU grep; looking at the history
> in grep's git repo shows it comes from commit
> 56623b5129d487cb6673fa5a582d094edc1fe983:
> 
> 2005-06-20  Charles Levert  <charles_levert@gna.org>
> 
>         * src/grep.c: Extensively document the SGR/EL-to-Right issue.
> 
> Hence I believe the color-macros.h copyright line should range from
> 2005-2017 if you move the comments (please do), or from 2013-2017 as-
> is.

Thanks for clarification, 2005-2017 will be in final version of patch
I'm planning to install.

Martin

> 
> Diagnostic refactoring is OK otherwise.
> 
> Dave
> 

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 3/7] GCOV: add support for lines with an unexecuted lines.
  2017-10-30 12:27   ` Nathan Sidwell
@ 2017-10-31 11:29     ` Martin Liška
  0 siblings, 0 replies; 33+ messages in thread
From: Martin Liška @ 2017-10-31 11:29 UTC (permalink / raw)
  To: Nathan Sidwell, gcc-patches

On 10/30/2017 01:21 PM, Nathan Sidwell wrote:
> 
> Looks good otherwise, WDYT?

Hi.

Works for me, it's more precise!

Thanks,
Martin

> 
> nathan

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 4/7] GCOV: add -j argument (human readable format).
  2017-10-30 12:44   ` Nathan Sidwell
@ 2017-10-31 11:54     ` Martin Liška
  2017-10-31 12:10       ` Nathan Sidwell
  0 siblings, 1 reply; 33+ messages in thread
From: Martin Liška @ 2017-10-31 11:54 UTC (permalink / raw)
  To: Nathan Sidwell, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 2288 bytes --]

On 10/30/2017 01:35 PM, Nathan Sidwell wrote:
> On 10/26/2017 04:11 AM, marxin wrote:
>> Human readable format is quite useful in my opinion. There's example:
>>
>>          -:    1:unsigned
>>     14.00K:    2:loop (unsigned n, int value)
> 
> My first thought is 'why 2 decimal places'?  That seems excessive.  Zero surely suffices?

Hi.

Agree.

> 
>> Question is do we want to do it by default, or a new option is fine?
>> Note that all external tools using gcov should use intermediate format
>> which is obviously unchanged.
> 
> I don't think other tools do it by default.

Works for me/

> 
> 
>> +     [@option{-j}|@option{--human-numbers}]
> 
> man ls:
>        -h, --human-readable
>                with -l and/or -s, print human readable sizes (e.g., 1K 234M 2G)
> 
> Sadly '-h' is help (IIRC).  but we could at least copy the long form.

Yep, copied.

> 
> 
>> +  const char *units[] = {"", "K", "M", "G", "Y", "P", "E", "Z"};
> 
> Those aren't right  KMGTPEZY
> http://www.npl.co.uk/reference/measurement-units/si-prefixes/

Ha, SI units. Last time I heart about it was at grammar school during Physics classes ;)
Following that.

> 
> 
>> +  for (unsigned i = 0; i < sizeof (units); i++)
>> +    {
>> +      if (v < 1000.0f)
>> +    {
>> +      sprintf (buffer, "%3.2f%s", v, units[i]);
>> +      return buffer;
>> +    }
>> +
>> +      v /= 1000.0f;
>> +    }
> 
> that's going to fail on certain roundings.  You're doing multiple divisions by 1000, which itself will have roundings.  But more importantly, numbers after scaling like 999.999 will round to 1000, and you probably don't want that.  This kind of formatting is tricky.  My inclination is to keep it in the integer domain.  Determine a scaling factor.  Divide once by that and then explicitly round to nearest even[*] with a check for the 999.9 case.  Then print as an unsigned.

Done that.

Is the patch fine to be installed?

Martin

> 
> nathan
> 
> [*] I presume you're familiar with RNE?  That's probably overkill and plain RN would be adequate.
> 


[-- Attachment #2: 0004-GCOV-add-j-argument-human-readable-format-v2.patch --]
[-- Type: text/x-patch, Size: 7543 bytes --]

From b14bed2b376dc60eb35c07c5521007d16b137c78 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Thu, 26 Oct 2017 10:11:16 +0200
Subject: [PATCH 4/8] GCOV: add -j argument (human readable format).

Human readable format is quite useful in my opinion. There's example:

        -:    4:unsigned
      14k:    5:loop (unsigned n, int value)		  /* count(14k) */
        -:    6:{
      21M:    7:  for (unsigned i = 0; i < n - 1; i++)
        -:    8:  {
      21M:    9:    value += i;				  /* count(21M) */
        -:   10:  }
        -:   11:
      14k:   12:  return value;
        -:   13:}
        -:   14:
        1:   15:int main(int argc, char **argv)
        -:   16:{
        1:   17:  unsigned sum = 0;
       7k:   18:  for (unsigned i = 0; i < 7 * 1000; i++)
        -:   19:  {
       7k:   20:    sum += loop (1000, sum);
       7k:   21:    sum += loop (2000, sum);		  /* count(7k) */
        -:   22:  }
        -:   23:
        1:   24:  return 0;				  /* count(1) */
        -:   25:}
        -:   26:

gcc/ChangeLog:

2017-10-23  Martin Liska  <mliska@suse.cz>

	* doc/gcov.texi: Document new option.
	* gcov.c (print_usage): Likewise print it.
	(process_args): Support the argument.
	(format_count): New function.
	(format_gcov): Use the function.

gcc/testsuite/ChangeLog:

2017-10-23  Martin Liska  <mliska@suse.cz>

	* g++.dg/gcov/loop.C: New test.
	* lib/gcov.exp: Support human readable format for counts.
---
 gcc/doc/gcov.texi                |  5 +++++
 gcc/gcov.c                       | 45 ++++++++++++++++++++++++++++++++++++++--
 gcc/testsuite/g++.dg/gcov/loop.C | 27 ++++++++++++++++++++++++
 gcc/testsuite/lib/gcov.exp       |  2 +-
 4 files changed, 76 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/gcov/loop.C

diff --git a/gcc/doc/gcov.texi b/gcc/doc/gcov.texi
index e186ac6e1ea..5c4ba8a51a7 100644
--- a/gcc/doc/gcov.texi
+++ b/gcc/doc/gcov.texi
@@ -125,6 +125,7 @@ gcov [@option{-v}|@option{--version}] [@option{-h}|@option{--help}]
      [@option{-d}|@option{--display-progress}]
      [@option{-f}|@option{--function-summaries}]
      [@option{-i}|@option{--intermediate-format}]
+     [@option{-j}|@option{--human-readable}]
      [@option{-k}|@option{--use-colors}]
      [@option{-l}|@option{--long-file-names}]
      [@option{-m}|@option{--demangled-names}]
@@ -186,6 +187,10 @@ be used by @command{lcov} or other tools. The output is a single
 The format of the intermediate @file{.gcov} file is plain text with
 one entry per line
 
+@item -j
+@itemx --human-readable
+Write counts in human readable format (like 24k).
+
 @smallexample
 file:@var{source_file_name}
 function:@var{line_number},@var{execution_count},@var{function_name}
diff --git a/gcc/gcov.c b/gcc/gcov.c
index f9334f96eb3..ff246c104e4 100644
--- a/gcc/gcov.c
+++ b/gcc/gcov.c
@@ -44,6 +44,7 @@ along with Gcov; see the file COPYING3.  If not see
 #include "color-macros.h"
 
 #include <getopt.h>
+#include <math.h>
 
 #include "md5.h"
 
@@ -393,6 +394,10 @@ static int flag_use_colors = 0;
 
 static int flag_all_blocks = 0;
 
+/* Output human readable numbers.  */
+
+static int flag_human_readable_numbers = 0;
+
 /* Output summary info for each function.  */
 
 static int flag_function_summary = 0;
@@ -710,6 +715,7 @@ print_usage (int error_p)
   fnotice (file, "  -f, --function-summaries        Output summaries for each function\n");
   fnotice (file, "  -h, --help                      Print this help, then exit\n");
   fnotice (file, "  -i, --intermediate-format       Output .gcov file in intermediate text format\n");
+  fnotice (file, "  -j, --human-readable            Output human readable numbers\n");
   fnotice (file, "  -k, --use-colors                Emit colored output\n");
   fnotice (file, "  -l, --long-file-names           Use long output file names for included\n\
                                     source files\n");
@@ -752,6 +758,7 @@ static const struct option options[] =
   { "branch-probabilities", no_argument,       NULL, 'b' },
   { "branch-counts",        no_argument,       NULL, 'c' },
   { "intermediate-format",  no_argument,       NULL, 'i' },
+  { "human-readable",	    no_argument,       NULL, 'j' },
   { "no-output",            no_argument,       NULL, 'n' },
   { "long-file-names",      no_argument,       NULL, 'l' },
   { "function-summaries",   no_argument,       NULL, 'f' },
@@ -775,7 +782,7 @@ process_args (int argc, char **argv)
 {
   int opt;
 
-  const char *opts = "abcdfhiklmno:prs:uvwx";
+  const char *opts = "abcdfhijklmno:prs:uvwx";
   while ((opt = getopt_long (argc, argv, opts, options, NULL)) != -1)
     {
       switch (opt)
@@ -798,6 +805,9 @@ process_args (int argc, char **argv)
 	case 'l':
 	  flag_long_names = 1;
 	  break;
+	case 'j':
+	  flag_human_readable_numbers = 1;
+	  break;
 	case 'k':
 	  flag_use_colors = 1;
 	  break;
@@ -1938,6 +1948,37 @@ add_branch_counts (coverage_t *coverage, const arc_t *arc)
     }
 }
 
+/* Format COUNT, if flag_human_readable_numbers is set, return it human
+   readable format.  */
+
+static char const *
+format_count (gcov_type count)
+{
+  static char buffer[64];
+  float v = count;
+  const char *units = " kMGTPEZY";
+
+  if (count < 1000 || !flag_human_readable_numbers)
+    {
+      sprintf (buffer, "%" PRId64, count);
+      return buffer;
+    }
+
+  gcov_type divisor = 1;
+  for (unsigned i = 0; i < strlen (units); i++)
+    {
+      if (v < (1000 * divisor))
+	{
+	  gcov_type r = (gcov_type)roundf (v / divisor);
+	  sprintf (buffer, "%" PRId64 "%c", r, units[i]);
+	  return buffer;
+	}
+      divisor *= 1000;
+    }
+
+  gcc_unreachable ();
+}
+
 /* Format a GCOV_TYPE integer as either a percent ratio, or absolute
    count.  If dp >= 0, format TOP/BOTTOM * 100 to DP decimal places.
    If DP is zero, no decimal point is printed. Only print 100% when
@@ -1985,7 +2026,7 @@ format_gcov (gcov_type top, gcov_type bottom, int dp)
 	}
     }
   else
-    sprintf (buffer, "%" PRId64, (int64_t)top);
+    return format_count (top);
 
   return buffer;
 }
diff --git a/gcc/testsuite/g++.dg/gcov/loop.C b/gcc/testsuite/g++.dg/gcov/loop.C
new file mode 100644
index 00000000000..7f3be5587af
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gcov/loop.C
@@ -0,0 +1,27 @@
+/* { dg-options "-fprofile-arcs -ftest-coverage" } */
+/* { dg-do run { target native } } */
+
+unsigned
+loop (unsigned n, int value)		  /* count(14k) */
+{
+  for (unsigned i = 0; i < n - 1; i++)
+  {
+    value += i;				  /* count(21M) */
+  }
+
+  return value;
+}
+
+int main(int argc, char **argv)
+{
+  unsigned sum = 0;
+  for (unsigned i = 0; i < 7 * 1000; i++)
+  {
+    sum += loop (1000, sum);
+    sum += loop (2000, sum);		  /* count(7k) */
+  }
+
+  return 0;				  /* count(1) */
+}
+
+/* { dg-final { run-gcov branches { -abj loop.C } } } */
diff --git a/gcc/testsuite/lib/gcov.exp b/gcc/testsuite/lib/gcov.exp
index 18adc71351a..ede01e70212 100644
--- a/gcc/testsuite/lib/gcov.exp
+++ b/gcc/testsuite/lib/gcov.exp
@@ -59,7 +59,7 @@ proc verify-lines { testname testcase file } {
     while { [gets $fd line] >= 0 } {
         # We want to match both "-" and "#####" as count as well as numbers,
         # since we want to detect lines that shouldn't be marked as covered.
-	if [regexp "^ *(\[^:]*): *(\[0-9\\-#]+):.*count\\((\[0-9\\-#=]+)\\)(.*)" \
+	if [regexp "^ *(\[^:]*): *(\[0-9\\-#]+):.*count\\((\[0-9\\-#=\\.kMGTPEZY]+)\\)(.*)" \
 		"$line" all is n shouldbe rest] {
 	    if [regexp "^ *{(.*)}" $rest all xfailed] {
 		switch [dg-process-target $xfailed] {
-- 
2.14.3


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 4/7] GCOV: add -j argument (human readable format).
  2017-10-31 11:54     ` Martin Liška
@ 2017-10-31 12:10       ` Nathan Sidwell
  2017-10-31 14:04         ` Martin Liška
  0 siblings, 1 reply; 33+ messages in thread
From: Nathan Sidwell @ 2017-10-31 12:10 UTC (permalink / raw)
  To: Martin Liška, gcc-patches

On 10/31/2017 07:49 AM, Martin Liška wrote:
> On 10/30/2017 01:35 PM, Nathan Sidwell wrote:

> Is the patch fine to be installed?
not quite ...


+  gcov_type divisor = 1;
+  for (unsigned i = 0; i < strlen (units); i++)
+    {
+      if (v < (1000 * divisor))
+	{
+	  gcov_type r = (gcov_type)roundf (v / divisor);
+	  sprintf (buffer, "%" PRId64 "%c", r, units[i]);
+	  return buffer;
+	}
+      divisor *= 1000;
+    }

This doesn't deal with rounding to 1000, which will look a bit odd. 
It'll also overflow if we ever get 10^24 counts (yeah, like that'll ever 
happen in this universe).  We may as well not use floats either.

Excuse the poor formatting

   unsigned i;
   for (i = 0; units[i+1]; i++, divisor *= 1000) {
     if (count + divisor / 2 < 1000 * divisor)
        break;
   }
   gcov_type r  = (count + divisor / 2) / divisor;
   sprintf (buffer, "%" PRId64 "%c", r, units[i]);
   return buffer;

-- 
Nathan Sidwell

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 4/7] GCOV: add -j argument (human readable format).
  2017-10-31 12:10       ` Nathan Sidwell
@ 2017-10-31 14:04         ` Martin Liška
  2017-10-31 14:39           ` Nathan Sidwell
  0 siblings, 1 reply; 33+ messages in thread
From: Martin Liška @ 2017-10-31 14:04 UTC (permalink / raw)
  To: Nathan Sidwell, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1377 bytes --]

On 10/31/2017 01:05 PM, Nathan Sidwell wrote:
> On 10/31/2017 07:49 AM, Martin Liška wrote:
>> On 10/30/2017 01:35 PM, Nathan Sidwell wrote:
> 
>> Is the patch fine to be installed?
> not quite ...
> 
> 
> +  gcov_type divisor = 1;
> +  for (unsigned i = 0; i < strlen (units); i++)
> +    {
> +      if (v < (1000 * divisor))
> +    {
> +      gcov_type r = (gcov_type)roundf (v / divisor);
> +      sprintf (buffer, "%" PRId64 "%c", r, units[i]);
> +      return buffer;
> +    }
> +      divisor *= 1000;
> +    }
> 
> This doesn't deal with rounding to 1000, which will look a bit odd. It'll also overflow if we ever get 10^24 counts (yeah, like that'll ever happen in this universe).  We may as well not use floats either.

Hi.

Do you mean numbers a bit smaller than 1000 or bit bigger? Do you have an example that will
be handled in a different way?

> 
> Excuse the poor formatting
> 
>    unsigned i;
>    for (i = 0; units[i+1]; i++, divisor *= 1000) {
>      if (count + divisor / 2 < 1000 * divisor)
>         break;
>    }
>    gcov_type r  = (count + divisor / 2) / divisor;
>    sprintf (buffer, "%" PRId64 "%c", r, units[i]);
>    return buffer;
> 

I've adapted your code snippet.

Martin

[-- Attachment #2: 0004-GCOV-add-j-argument-human-readable-format-v3.patch --]
[-- Type: text/x-patch, Size: 7506 bytes --]

From 902208a24eafaf1a6a4b334a2c8ea32afaec63df Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Thu, 26 Oct 2017 10:11:16 +0200
Subject: [PATCH 4/8] GCOV: add -j argument (human readable format).

Human readable format is quite useful in my opinion. There's example:

        -:    4:unsigned
      14k:    5:loop (unsigned n, int value)		  /* count(14k) */
        -:    6:{
      21M:    7:  for (unsigned i = 0; i < n - 1; i++)
        -:    8:  {
      21M:    9:    value += i;				  /* count(21M) */
        -:   10:  }
        -:   11:
      14k:   12:  return value;
        -:   13:}
        -:   14:
        1:   15:int main(int argc, char **argv)
        -:   16:{
        1:   17:  unsigned sum = 0;
       7k:   18:  for (unsigned i = 0; i < 7 * 1000; i++)
        -:   19:  {
       7k:   20:    sum += loop (1000, sum);
       7k:   21:    sum += loop (2000, sum);		  /* count(7k) */
        -:   22:  }
        -:   23:
        1:   24:  return 0;				  /* count(1) */
        -:   25:}
        -:   26:

gcc/ChangeLog:

2017-10-23  Martin Liska  <mliska@suse.cz>

	* doc/gcov.texi: Document new option.
	* gcov.c (print_usage): Likewise print it.
	(process_args): Support the argument.
	(format_count): New function.
	(format_gcov): Use the function.

gcc/testsuite/ChangeLog:

2017-10-23  Martin Liska  <mliska@suse.cz>

	* g++.dg/gcov/loop.C: New test.
	* lib/gcov.exp: Support human readable format for counts.
---
 gcc/doc/gcov.texi                |  5 +++++
 gcc/gcov.c                       | 41 ++++++++++++++++++++++++++++++++++++++--
 gcc/testsuite/g++.dg/gcov/loop.C | 27 ++++++++++++++++++++++++++
 gcc/testsuite/lib/gcov.exp       |  2 +-
 4 files changed, 72 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/gcov/loop.C

diff --git a/gcc/doc/gcov.texi b/gcc/doc/gcov.texi
index e186ac6e1ea..5c4ba8a51a7 100644
--- a/gcc/doc/gcov.texi
+++ b/gcc/doc/gcov.texi
@@ -125,6 +125,7 @@ gcov [@option{-v}|@option{--version}] [@option{-h}|@option{--help}]
      [@option{-d}|@option{--display-progress}]
      [@option{-f}|@option{--function-summaries}]
      [@option{-i}|@option{--intermediate-format}]
+     [@option{-j}|@option{--human-readable}]
      [@option{-k}|@option{--use-colors}]
      [@option{-l}|@option{--long-file-names}]
      [@option{-m}|@option{--demangled-names}]
@@ -186,6 +187,10 @@ be used by @command{lcov} or other tools. The output is a single
 The format of the intermediate @file{.gcov} file is plain text with
 one entry per line
 
+@item -j
+@itemx --human-readable
+Write counts in human readable format (like 24k).
+
 @smallexample
 file:@var{source_file_name}
 function:@var{line_number},@var{execution_count},@var{function_name}
diff --git a/gcc/gcov.c b/gcc/gcov.c
index f9334f96eb3..972e567160e 100644
--- a/gcc/gcov.c
+++ b/gcc/gcov.c
@@ -44,6 +44,7 @@ along with Gcov; see the file COPYING3.  If not see
 #include "color-macros.h"
 
 #include <getopt.h>
+#include <math.h>
 
 #include "md5.h"
 
@@ -393,6 +394,10 @@ static int flag_use_colors = 0;
 
 static int flag_all_blocks = 0;
 
+/* Output human readable numbers.  */
+
+static int flag_human_readable_numbers = 0;
+
 /* Output summary info for each function.  */
 
 static int flag_function_summary = 0;
@@ -710,6 +715,7 @@ print_usage (int error_p)
   fnotice (file, "  -f, --function-summaries        Output summaries for each function\n");
   fnotice (file, "  -h, --help                      Print this help, then exit\n");
   fnotice (file, "  -i, --intermediate-format       Output .gcov file in intermediate text format\n");
+  fnotice (file, "  -j, --human-readable            Output human readable numbers\n");
   fnotice (file, "  -k, --use-colors                Emit colored output\n");
   fnotice (file, "  -l, --long-file-names           Use long output file names for included\n\
                                     source files\n");
@@ -752,6 +758,7 @@ static const struct option options[] =
   { "branch-probabilities", no_argument,       NULL, 'b' },
   { "branch-counts",        no_argument,       NULL, 'c' },
   { "intermediate-format",  no_argument,       NULL, 'i' },
+  { "human-readable",	    no_argument,       NULL, 'j' },
   { "no-output",            no_argument,       NULL, 'n' },
   { "long-file-names",      no_argument,       NULL, 'l' },
   { "function-summaries",   no_argument,       NULL, 'f' },
@@ -775,7 +782,7 @@ process_args (int argc, char **argv)
 {
   int opt;
 
-  const char *opts = "abcdfhiklmno:prs:uvwx";
+  const char *opts = "abcdfhijklmno:prs:uvwx";
   while ((opt = getopt_long (argc, argv, opts, options, NULL)) != -1)
     {
       switch (opt)
@@ -798,6 +805,9 @@ process_args (int argc, char **argv)
 	case 'l':
 	  flag_long_names = 1;
 	  break;
+	case 'j':
+	  flag_human_readable_numbers = 1;
+	  break;
 	case 'k':
 	  flag_use_colors = 1;
 	  break;
@@ -1938,6 +1948,33 @@ add_branch_counts (coverage_t *coverage, const arc_t *arc)
     }
 }
 
+/* Format COUNT, if flag_human_readable_numbers is set, return it human
+   readable format.  */
+
+static char const *
+format_count (gcov_type count)
+{
+  static char buffer[64];
+  const char *units = " kMGTPEZY";
+
+  if (count < 1000 || !flag_human_readable_numbers)
+    {
+      sprintf (buffer, "%" PRId64, count);
+      return buffer;
+    }
+
+  unsigned i;
+  gcov_type divisor = 1;
+  for (i = 0; units[i+1]; i++, divisor *= 1000)
+    {
+      if (count + divisor / 2 < 1000 * divisor)
+	break;
+    }
+  gcov_type r  = (count + divisor / 2) / divisor;
+  sprintf (buffer, "%" PRId64 "%c", r, units[i]);
+  return buffer;
+}
+
 /* Format a GCOV_TYPE integer as either a percent ratio, or absolute
    count.  If dp >= 0, format TOP/BOTTOM * 100 to DP decimal places.
    If DP is zero, no decimal point is printed. Only print 100% when
@@ -1985,7 +2022,7 @@ format_gcov (gcov_type top, gcov_type bottom, int dp)
 	}
     }
   else
-    sprintf (buffer, "%" PRId64, (int64_t)top);
+    return format_count (top);
 
   return buffer;
 }
diff --git a/gcc/testsuite/g++.dg/gcov/loop.C b/gcc/testsuite/g++.dg/gcov/loop.C
new file mode 100644
index 00000000000..7f3be5587af
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gcov/loop.C
@@ -0,0 +1,27 @@
+/* { dg-options "-fprofile-arcs -ftest-coverage" } */
+/* { dg-do run { target native } } */
+
+unsigned
+loop (unsigned n, int value)		  /* count(14k) */
+{
+  for (unsigned i = 0; i < n - 1; i++)
+  {
+    value += i;				  /* count(21M) */
+  }
+
+  return value;
+}
+
+int main(int argc, char **argv)
+{
+  unsigned sum = 0;
+  for (unsigned i = 0; i < 7 * 1000; i++)
+  {
+    sum += loop (1000, sum);
+    sum += loop (2000, sum);		  /* count(7k) */
+  }
+
+  return 0;				  /* count(1) */
+}
+
+/* { dg-final { run-gcov branches { -abj loop.C } } } */
diff --git a/gcc/testsuite/lib/gcov.exp b/gcc/testsuite/lib/gcov.exp
index 18adc71351a..ede01e70212 100644
--- a/gcc/testsuite/lib/gcov.exp
+++ b/gcc/testsuite/lib/gcov.exp
@@ -59,7 +59,7 @@ proc verify-lines { testname testcase file } {
     while { [gets $fd line] >= 0 } {
         # We want to match both "-" and "#####" as count as well as numbers,
         # since we want to detect lines that shouldn't be marked as covered.
-	if [regexp "^ *(\[^:]*): *(\[0-9\\-#]+):.*count\\((\[0-9\\-#=]+)\\)(.*)" \
+	if [regexp "^ *(\[^:]*): *(\[0-9\\-#]+):.*count\\((\[0-9\\-#=\\.kMGTPEZY]+)\\)(.*)" \
 		"$line" all is n shouldbe rest] {
 	    if [regexp "^ *{(.*)}" $rest all xfailed] {
 		switch [dg-process-target $xfailed] {
-- 
2.14.3


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 4/7] GCOV: add -j argument (human readable format).
  2017-10-31 14:04         ` Martin Liška
@ 2017-10-31 14:39           ` Nathan Sidwell
  2017-10-31 15:33             ` Martin Liška
  0 siblings, 1 reply; 33+ messages in thread
From: Nathan Sidwell @ 2017-10-31 14:39 UTC (permalink / raw)
  To: Martin Liška, gcc-patches

On 10/31/2017 10:04 AM, Martin Liška wrote:

> Do you mean numbers a bit smaller than 1000 or bit bigger? Do you have 
> an example that will
> be handled in a different way?

A count like 999500 would have been scaled to 999.5 and then rounded to 
1000.  We'd print 1000k rather than 1M.

> I've adapted your code snippet.

thanks, this is ok.

nathan

-- 
Nathan Sidwell

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 4/7] GCOV: add -j argument (human readable format).
  2017-10-31 14:39           ` Nathan Sidwell
@ 2017-10-31 15:33             ` Martin Liška
  0 siblings, 0 replies; 33+ messages in thread
From: Martin Liška @ 2017-10-31 15:33 UTC (permalink / raw)
  To: Nathan Sidwell, gcc-patches

On 10/31/2017 03:35 PM, Nathan Sidwell wrote:
> On 10/31/2017 10:04 AM, Martin Liška wrote:
> 
>> Do you mean numbers a bit smaller than 1000 or bit bigger? Do you have an example that will
>> be handled in a different way?
> 
> A count like 999500 would have been scaled to 999.5 and then rounded to 1000.  We'd print 1000k rather than 1M.

I see! Thank you for clarification.

> 
>> I've adapted your code snippet.
> 
> thanks, this is ok.

Has been just installed.

Martin

> 
> nathan
> 

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 8/N][RFC] v2 GCOV: support multiple functions per a line
  2017-10-26 12:06   ` Nathan Sidwell
@ 2017-11-01  8:00     ` Martin Liška
  2017-11-07 10:53       ` [PATCH 8/N][RFC][v3]: " Martin Liška
  0 siblings, 1 reply; 33+ messages in thread
From: Martin Liška @ 2017-11-01  8:00 UTC (permalink / raw)
  To: Nathan Sidwell, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 4722 bytes --]

Hi.

As shown from examples with -O2 optimization level, previous approach was not viable.
A more complex approach is necessary and can be shortly described as follows:

1) each function has assigned start and end line in a source file
2) group functions are these functions that begin on a line and there's another function
that begins on the same line (template initializations, deleting dtors, ...)
3) both source_file and function have 'vector<line_info> lines':
   a) source_file::lines represent all lines of non-group functions
   b) function_info::lines exists if a function is a group function and represent lines that belong to the function
4) function_info::lines for a group of functions are summed to source_file::lines; that makes function
   output_lines easier
5) branches, calls and blocks are printed per function in a group, not to 'global scope' lines
6) DECL_ARTIFICIAL functions are ignored

There are 2 examples that illustrate the approach:

        1:    7:class ClassB
        -:    8:{
        -:    9:public:
        -:   10:  virtual ~ClassB ();
        -:   11:};
        -:   12:
        3:   13:ClassB::~ClassB ()
        -:   14:{
        1:   15:  int r = inline_me (glob);
        1:   16:  std::cout << "Bey, ClassB!" << r << std::endl;
        2:   17:}
------------------
_ZN6ClassBD0Ev:
        1:   13:ClassB::~ClassB ()
        -:   14:{
        -:   15:  int r = inline_me (glob);
        -:   16:  std::cout << "Bey, ClassB!" << r << std::endl;
        1:   17:}
------------------
_ZN6ClassBD2Ev:
        2:   13:ClassB::~ClassB ()
        -:   14:{
        1:   15:  int r = inline_me (glob);
        1:   16:  std::cout << "Bey, ClassB!" << r << std::endl;
        1:   17:}
------------------
        -:   18:
        -:   19:int
        1:   20:main (int argc, char **argv)
        -:   21:{
        1:   22:  ClassB *pObjB = new ClassB ();
        1:   23:  delete pObjB;
        -:   24:
        1:   25:  if (argc == 123)
    #####:   26:    return 1;
        -:   27:
        -:   28:  return 0;
        -:   29:}
        -:   30:
        -:   31:
        -:   32:static int
    #####:   33:inline_me(int a)
        -:   34:{
       1*:   35:  return a * a;
        -:   36:}

It's compiled with -O2 and it shows that ZN6ClassBD2Ev function executes also line 35, but the line is not counted
to function_info::lines as it does not live in function scope. Note that line 35 is marked with '*'. That's because
I used -fkeep-inline-functions.

        -:    0:Source:test.cpp
        -:    0:Graph:test.gcno
        -:    0:Data:test.gcda
        -:    0:Runs:1
        -:    0:Programs:1
        -:    1:template<class T>
        -:    2:class Foo
        -:    3:{
        -:    4:  public:
        3:    5:  Foo()
        -:    6:  {
        3:    7:    b = 123;
        3:    8:  }
------------------
Foo<char>::Foo():
    #####:    5:  Foo()
        -:    6:  {
    #####:    7:    b = 123;
    #####:    8:  }
------------------
Foo<int>::Foo():
        1:    5:  Foo()
        -:    6:  {
        1:    7:    b = 123;
        1:    8:  }
------------------
Foo<float>::Foo():
        2:    5:  Foo()
        -:    6:  {
        2:    7:    b = 123;
        2:    8:  }
------------------
        -:    9:
        1:   10:  void test() { if (!b) __builtin_abort (); b = 111; }
------------------
Foo<char>::test():
    #####:   10:  void test() { if (!b) __builtin_abort (); b = 111; }
    %%%%%:   10-block  0
    %%%%%:   10-block  1
------------------
Foo<int>::test():
       1*:   10:  void test() { if (!b) __builtin_abort (); b = 111; }
        1:   10-block  0
    %%%%%:   10-block  1
------------------
Foo<float>::test():
    #####:   10:  void test() { if (!b) __builtin_abort (); b = 111; }
    %%%%%:   10-block  0
    %%%%%:   10-block  1
------------------
        -:   11:
        -:   12:  private:
        -:   13:  int b;
        -:   14:};
        -:   15:
        -:   16:template class Foo<float>;
        -:   17:template class Foo<int>;
        -:   18:template class Foo<char>;
        -:   19:
        1:   20:int main()
        -:   21:{
        1:   22:  Foo<int> xx;
        1:   22-block  0
        1:   23:  Foo<float> yy;
        1:   23-block  0
        1:   24:  Foo<float> zz;
        1:   24-block  0
        1:   25:  xx.test();
        1:   25-block  0
        -:   26:
        1:   27:  return 0;
        1:   27-block  0
        -:   28:}

This sample is illustration of how we aggregate statistics.

I've added changelog entry and I'm still planning to enhance documentation, add
some tests (which would be tricky) and small follow-up clean-up is planned.
In meantime I would appreciate a feedback.

Thanks,
Martin





   


[-- Attachment #2: 0001-GCOV-support-multiple-functions-per-a-line.patch --]
[-- Type: text/x-patch, Size: 35856 bytes --]

From cf05cc4b236d7fd58091a4fd3fd4e88bfee28c00 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Thu, 26 Oct 2017 10:39:40 +0200
Subject: [PATCH] GCOV: support multiple functions per a line

gcc/ChangeLog:

2017-11-01  Martin Liska  <mliska@suse.cz>

	* coverage.c (coverage_begin_function): Output also end locus
	of a function and information whether the function is
	artificial.
	* gcov-dump.c (tag_function): Parse and print the information.
	* gcov.c (INCLUDE_MAP): Add include.
	(INCLUDE_SET): Likewise.
	(struct line_info): Move earlier in the source file because
	of vector<line_info> in function_info structure.
	(line_info::line_info): Likewise.
	(line_info::has_block): Likewise.
	(struct source_info): Add new member index.
	(source_info::get_functions_at_location): New function.
	(function_info::group_line_p): New function.
	(output_intermediate_line): New function.
	(output_intermediate_file): Use the mentioned function.
	(struct function_start): New.
	(struct function_start_pair_hash): Likewise.
	(process_file): Add code that identifies group functions.
	Assign lines either to global or function scope.
	(generate_results): Skip artificial functions.
	(find_source): Assign index for each source file.
	(read_graph_file): Read new flag artificial and end_line.
	(add_line_counts): Assign it either to global of function scope.
	(accumulate_line_counts): Isolate core of the function to
	accumulate_line_info and call it for both function and global
	scope lines.
	(accumulate_line_info): New function.
	(output_line_beginning): Fix GNU coding style.
	(print_source_line): New function.
	(output_line_details): Likewise.
	(output_function_details): Likewise.
	(output_lines): Iterate both source (global) scope and function
	scope.
---
 gcc/coverage.c  |   2 +
 gcc/gcov-dump.c |   7 +-
 gcc/gcov.c      | 823 ++++++++++++++++++++++++++++++++++++++------------------
 3 files changed, 569 insertions(+), 263 deletions(-)

diff --git a/gcc/coverage.c b/gcc/coverage.c
index 8a56a677f15..8ac593920b6 100644
--- a/gcc/coverage.c
+++ b/gcc/coverage.c
@@ -663,8 +663,10 @@ coverage_begin_function (unsigned lineno_checksum, unsigned cfg_checksum)
   gcov_write_unsigned (cfg_checksum);
   gcov_write_string (IDENTIFIER_POINTER
 		     (DECL_ASSEMBLER_NAME (current_function_decl)));
+  gcov_write_unsigned (DECL_ARTIFICIAL (current_function_decl));
   gcov_write_filename (xloc.file);
   gcov_write_unsigned (xloc.line);
+  gcov_write_unsigned (expand_location (cfun->function_end_locus).line);
   gcov_write_length (offset);
 
   return !gcov_is_error ();
diff --git a/gcc/gcov-dump.c b/gcc/gcov-dump.c
index d24e72ac4a1..22d08c04c1e 100644
--- a/gcc/gcov-dump.c
+++ b/gcc/gcov-dump.c
@@ -308,9 +308,14 @@ tag_function (const char *filename ATTRIBUTE_UNUSED,
 	  
 	  name = gcov_read_string ();
 	  printf (", `%s'", name ? name : "NULL");
+	  unsigned artificial = gcov_read_unsigned ();
 	  name = gcov_read_string ();
 	  printf (" %s", name ? name : "NULL");
-	  printf (":%u", gcov_read_unsigned ());
+	  unsigned line_start = gcov_read_unsigned ();
+	  unsigned line_end = gcov_read_unsigned ();
+	  printf (":%u:%u", line_start, line_end);
+	  if (artificial)
+	    printf (", artificial");
 	}
     }
 }
diff --git a/gcc/gcov.c b/gcc/gcov.c
index f1e7777cf94..9a6c74cd2d6 100644
--- a/gcc/gcov.c
+++ b/gcc/gcov.c
@@ -34,6 +34,8 @@ along with Gcov; see the file COPYING3.  If not see
 #define INCLUDE_ALGORITHM
 #define INCLUDE_VECTOR
 #define INCLUDE_STRING
+#define INCLUDE_MAP
+#define INCLUDE_SET
 #include "system.h"
 #include "coretypes.h"
 #include "tm.h"
@@ -183,6 +185,42 @@ block_info::block_info (): succ (NULL), pred (NULL), num_succ (0), num_pred (0),
   cycle.arc = NULL;
 }
 
+/* Describes a single line of source.  Contains a chain of basic blocks
+   with code on it.  */
+
+struct line_info
+{
+  /* Default constructor.  */
+  line_info ();
+
+  /* Return true when NEEDLE is one of basic blocks the line belongs to.  */
+  bool has_block (block_t *needle);
+
+  /* Execution count.  */
+  gcov_type count;
+
+  /* Branches from blocks that end on this line.  */
+  vector<arc_t *> branches;
+
+  /* blocks which start on this line.  Used in all-blocks mode.  */
+  vector<block_t *> blocks;
+
+  unsigned exists : 1;
+  unsigned unexceptional : 1;
+  unsigned has_unexecuted_block : 1;
+};
+
+line_info::line_info (): count (0), branches (), blocks (), exists (false),
+  unexceptional (0), has_unexecuted_block (0)
+{
+}
+
+bool
+line_info::has_block (block_t *needle)
+{
+  return std::find (blocks.begin (), blocks.end (), needle) != blocks.end ();
+}
+
 /* Describes a single function. Contains an array of basic blocks.  */
 
 typedef struct function_info
@@ -190,6 +228,10 @@ typedef struct function_info
   function_info ();
   ~function_info ();
 
+  /* Return true when line N belongs to the function in source file SRC_IDX.
+     The line must be defined in body of the function, can't be inlined.  */
+  bool group_line_p (unsigned n, unsigned src_idx);
+
   /* Name of function.  */
   char *name;
   char *demangled_name;
@@ -200,6 +242,13 @@ typedef struct function_info
   /* The graph contains at least one fake incoming edge.  */
   unsigned has_catch : 1;
 
+  /* True when the function is artificial and does not exist
+     in a source file.  */
+  unsigned artificial : 1;
+
+  /* True when multiple functions start at a line in a source file.  */
+  unsigned is_group : 1;
+
   /* Array of basic blocks.  Like in GCC, the entry block is
      at blocks[0] and the exit block is at blocks[1].  */
 #define ENTRY_BLOCK (0)
@@ -211,12 +260,17 @@ typedef struct function_info
   gcov_type *counts;
   unsigned num_counts;
 
-  /* First line number & file.  */
-  unsigned line;
+  /* First line number.  */
+  unsigned start_line;
+
+  /* Last line number.  */
+  unsigned end_line;
+
+  /* Index of source file where the function is defined.  */
   unsigned src;
 
-  /* Next function in same source file.  */
-  struct function_info *next_file_fn;
+  /* Vector of line information.  */
+  vector<line_info> lines;
 
   /* Next function.  */
   struct function_info *next;
@@ -239,42 +293,6 @@ typedef struct coverage_info
   char *name;
 } coverage_t;
 
-/* Describes a single line of source. Contains a chain of basic blocks
-   with code on it.  */
-
-struct line_info
-{
-  /* Default constructor.  */
-  line_info ();
-
-  /* Return true when NEEDLE is one of basic blocks the line belongs to.  */
-  bool has_block (block_t *needle);
-
-  /* Execution count.  */
-  gcov_type count;
-
-  /* Branches from blocks that end on this line.  */
-  vector<arc_t *> branches;
-
-  /* blocks which start on this line.  Used in all-blocks mode.  */
-  vector<block_t *> blocks;
-
-  unsigned exists : 1;
-  unsigned unexceptional : 1;
-  unsigned has_unexecuted_block : 1;
-};
-
-line_info::line_info (): count (0), branches (), blocks (), exists (false),
-  unexceptional (0), has_unexecuted_block (0)
-{
-}
-
-bool
-line_info::has_block (block_t *needle)
-{
-  return std::find (blocks.begin (), blocks.end (), needle) != blocks.end ();
-}
-
 /* Describes a file mentioned in the block graph.  Contains an array
    of line info.  */
 
@@ -283,6 +301,11 @@ struct source_info
   /* Default constructor.  */
   source_info ();
 
+  vector<function_t *> get_functions_at_location (unsigned line_num) const;
+
+  /* Index of the source_info in sources vector.  */
+  unsigned index;
+
   /* Canonical name of source file.  */
   char *name;
   time_t file_time;
@@ -294,14 +317,29 @@ struct source_info
 
   /* Functions in this source file.  These are in ascending line
      number order.  */
-  function_t *functions;
+  vector <function_t *> functions;
 };
 
-source_info::source_info (): name (NULL), file_time (), lines (),
-  coverage (), functions (NULL)
+source_info::source_info (): name (NULL), file_time (),
+  lines (), coverage (), functions ()
 {
 }
 
+vector<function_t *>
+source_info::get_functions_at_location (unsigned line_num) const
+{
+  vector<function_t *> r;
+
+  for (vector<function_t *>::const_iterator it = functions.begin ();
+       it != functions.end (); it++)
+    {
+      if ((*it)->start_line == line_num && (*it)->src == index)
+	r.push_back (*it);
+    }
+
+  return r;
+}
+
 class name_map
 {
 public:
@@ -491,8 +529,9 @@ extern int main (int, char **);
 
 function_info::function_info (): name (NULL), demangled_name (NULL),
   ident (0), lineno_checksum (0), cfg_checksum (0), has_catch (0),
+  artificial (0), is_group (0),
   blocks (), blocks_executed (0), counts (NULL), num_counts (0),
-  line (0), src (0), next_file_fn (NULL), next (NULL)
+  start_line (0), end_line (0), src (0), lines (), next (NULL)
 {
 }
 
@@ -514,6 +553,11 @@ function_info::~function_info ()
   free (name);
 }
 
+bool function_info::group_line_p (unsigned n, unsigned src_idx)
+{
+  return is_group && src == src_idx && start_line <= n && n <= end_line;
+}
+
 /* Cycle detection!
    There are a bajillion algorithms that do this.  Boost's function is named
    hawick_cycles, so I used the algorithm by K. A. Hawick and H. A. James in
@@ -880,6 +924,42 @@ process_args (int argc, char **argv)
   return optind;
 }
 
+/* Output intermediate LINE sitting on LINE_NUM to output file F.  */
+
+static void
+output_intermediate_line (FILE *f, line_info *line, unsigned line_num)
+{
+  if (!line->exists)
+    return;
+
+  fprintf (f, "lcount:%u,%s,%d\n", line_num,
+	   format_gcov (line->count, 0, -1),
+	   line->has_unexecuted_block);
+
+  vector<arc_t *>::const_iterator it;
+  if (flag_branches)
+    for (it = line->branches.begin (); it != line->branches.end ();
+	 it++)
+      {
+	if (!(*it)->is_unconditional && !(*it)->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 ((*it)->src->count)
+		 branch_type
+			= ((*it)->count > 0) ? "taken" : "nottaken";
+	    else
+	      branch_type = "notexec";
+	    fprintf (f, "branch:%d,%s\n", line_num, branch_type);
+	  }
+      }
+}
+
 /* Output the result in intermediate format used by 'lcov'.
 
 The intermediate format contains a single file named 'foo.cc.gcov',
@@ -893,50 +973,94 @@ file 'foo.cc.gcov' similar to the above example. */
 static void
 output_intermediate_file (FILE *gcov_file, source_info *src)
 {
-  unsigned line_num;    /* current line number.  */
-  const line_info *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->next_file_fn)
+  for (vector<function_t *>::iterator it = src->functions.begin ();
+       it != src->functions.end (); it++)
     {
       /* 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);
+      fprintf (gcov_file, "function:%d,%s,%s\n", (*it)->start_line,
+	       format_gcov ((*it)->blocks[0].count, 0, -1),
+	       flag_demangled_names ? (*it)->demangled_name : (*it)->name);
     }
 
-  for (line_num = 1, line = &src->lines[line_num];
-       line_num < src->lines.size ();
-       line_num++, line++)
+  for (unsigned line_num = 0; line_num <= src->lines.size (); line_num++)
     {
-      if (line->exists)
-	fprintf (gcov_file, "lcount:%u,%s,%d\n", line_num,
-		 format_gcov (line->count, 0, -1), line->has_unexecuted_block);
-      if (flag_branches)
-	for (vector<arc_t *>::const_iterator it = line->branches.begin ();
-	     it != line->branches.end (); it++)
-	  {
-	    if (!(*it)->is_unconditional && !(*it)->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 ((*it)->src->count)
-		  branch_type = ((*it)->count > 0) ? "taken" : "nottaken";
-		else
-		  branch_type = "notexec";
-		fprintf (gcov_file, "branch:%d,%s\n", line_num, branch_type);
-	      }
-	  }
+      vector<function_t *> fns = src->get_functions_at_location (line_num);
+
+      /* Print first group functions that begin on the line.  */
+      for (vector<function_t *>::iterator it2 = fns.begin ();
+	   it2 != fns.end (); it2++)
+	{
+	  vector<line_info> &lines = (*it2)->lines;
+	  /* Print all lines covered by the function.  */
+	  for (unsigned i = 0; i < lines.size (); i++)
+	    {
+	      line_info *line = &lines[i];
+	      output_intermediate_line (gcov_file, line, line_num + i);
+	    }
+	}
+
+      /* Follow with lines associated with the source file.  */
+      output_intermediate_line (gcov_file, &src->lines[line_num], line_num);
     }
 }
 
+/* Function start pair.  */
+struct function_start
+{
+  unsigned source_file_idx;
+  unsigned start_line;
+};
+
+/* Traits class for function start hash maps below.  */
+
+struct function_start_pair_hash : typed_noop_remove <function_start>
+{
+  typedef function_start value_type;
+  typedef function_start compare_type;
+
+  static hashval_t
+  hash (const function_start &ref)
+  {
+    inchash::hash hstate (0);
+    hstate.add_int (ref.source_file_idx);
+    hstate.add_int (ref.start_line);
+    return hstate.end ();
+  }
+
+  static bool
+  equal (const function_start &ref1, const function_start &ref2)
+  {
+    return (ref1.source_file_idx == ref2.source_file_idx
+	    && ref1.start_line == ref2.start_line);
+  }
+
+  static void
+  mark_deleted (function_start &ref)
+  {
+    ref.start_line = ~1U;
+  }
+
+  static void
+  mark_empty (function_start &ref)
+  {
+    ref.start_line = ~2U;
+  }
+
+  static bool
+  is_deleted (const function_start &ref)
+  {
+    return ref.start_line == ~1U;
+  }
+
+  static bool
+  is_empty (const function_start &ref)
+  {
+    return ref.start_line == ~2U;
+  }
+};
+
 /* Process a single input file.  */
 
 static void
@@ -949,6 +1073,27 @@ process_file (const char *file_name)
   if (!fns)
     return;
 
+  hash_map<function_start_pair_hash, function_t *> fn_map;
+
+  /* Identify group functions.  */
+  for (function_t *f = fns; f; f = f->next)
+    if (!f->artificial)
+      {
+	function_start needle;
+	needle.source_file_idx = f->src;
+	needle.start_line = f->start_line;
+
+	function_t **slot = fn_map.get (needle);
+	if (slot)
+	  {
+	    gcc_assert ((*slot)->end_line == f->end_line);
+	    (*slot)->is_group = 1;
+	    f->is_group = 1;
+	  }
+	else
+	  fn_map.put (needle, f);
+      }
+
   read_count_file (fns);
   while (fns)
     {
@@ -959,46 +1104,61 @@ process_file (const char *file_name)
       if (fn->counts || no_data_file)
 	{
 	  unsigned src = fn->src;
-	  unsigned line = fn->line;
 	  unsigned block_no;
-	  function_t *probe, **prev;
-
-	  /* Now insert it into the source file's list of
-	     functions. Normally functions will be encountered in
-	     ascending order, so a simple scan is quick.  Note we're
-	     building this list in reverse order.  */
-	  for (prev = &sources[src].functions;
-	       (probe = *prev); prev = &probe->next_file_fn)
-	    if (probe->line <= line)
-	      break;
-	  fn->next_file_fn = probe;
-	  *prev = fn;
 
-	  /* Mark last line in files touched by function.  */
-	  for (block_no = 0; block_no != fn->blocks.size (); block_no++)
+	  /* Process only non-artificial functions.  */
+	  if (!fn->artificial)
 	    {
-	      block_t *block = &fn->blocks[block_no];
-	      for (unsigned i = 0; i < block->locations.size (); i++)
-		{
-		  unsigned s = block->locations[i].source_file_idx;
+	      source_info *s = &sources[src];
+	      s->functions.push_back (fn);
 
-		  /* Sort lines of locations.  */
-		  sort (block->locations[i].lines.begin (),
-			block->locations[i].lines.end ());
-
-		  if (!block->locations[i].lines.empty ())
+	      /* Mark last line in files touched by function.  */
+	      for (block_no = 0; block_no != fn->blocks.size (); block_no++)
+		{
+		  block_t *block = &fn->blocks[block_no];
+		  for (unsigned i = 0; i < block->locations.size (); i++)
 		    {
-		      unsigned last_line
-			= block->locations[i].lines.back () + 1;
-		      if (last_line > sources[s].lines.size ())
-			sources[s].lines.resize (last_line);
+		      /* Sort lines of locations.  */
+		      sort (block->locations[i].lines.begin (),
+			    block->locations[i].lines.end ());
+
+		      if (!block->locations[i].lines.empty ())
+			{
+			  s = &sources[block->locations[i].source_file_idx];
+			  unsigned last_line
+			    = block->locations[i].lines.back ();
+
+			  /* Record new lines for the function.  */
+			  if (last_line >= s->lines.size ())
+			    {
+			      /* Record new lines for a source file.  */
+			      s->lines.resize (last_line + 1);
+			    }
+
+			  /* It's possible that a function has some inlined
+			     lines that are out of scope of the function.  */
+			  for (int j = block->locations[i].lines.size () - 1;
+			      j >= 0; j--)
+			  {
+			    unsigned ln = block->locations[i].lines[j];
+			    if (fn->group_line_p (ln, s->index))
+			    {
+			      unsigned size = ln - fn->start_line + 1;
+			      if (fn->lines.size () < size)
+				fn->lines.resize (size);
+
+			      break;
+			    }
+			  }
+			}
 		    }
 		}
+
+	      solve_flow_graph (fn);
+	      if (fn->has_catch)
+		find_exception_blocks (fn);
 	    }
 
-	  solve_flow_graph (fn);
-	  if (fn->has_catch)
-	    find_exception_blocks (fn);
 	  *fn_end = fn;
 	  fn_end = &fn->next;
 	}
@@ -1048,6 +1208,8 @@ generate_results (const char *file_name)
   for (fn = functions; fn; fn = fn->next)
     {
       coverage_t coverage;
+      if (fn->artificial)
+	continue;
 
       memset (&coverage, 0, sizeof (coverage));
       coverage.name = flag_demangled_names ? fn->demangled_name : fn->name;
@@ -1228,6 +1390,7 @@ find_source (const char *file_name)
       src = &sources.back ();
       src->name = canon;
       src->coverage.name = src->name;
+      src->index = idx;
       if (source_length
 #if HAVE_DOS_BASED_FILE_SYSTEM
 	  /* You lose if separators don't match exactly in the
@@ -1319,15 +1482,17 @@ read_graph_file (void)
       if (tag == GCOV_TAG_FUNCTION)
 	{
 	  char *function_name;
-	  unsigned ident, lineno;
+	  unsigned ident;
 	  unsigned lineno_checksum, cfg_checksum;
 
 	  ident = gcov_read_unsigned ();
 	  lineno_checksum = gcov_read_unsigned ();
 	  cfg_checksum = gcov_read_unsigned ();
 	  function_name = xstrdup (gcov_read_string ());
+	  unsigned artificial = gcov_read_unsigned ();
 	  unsigned src_idx = find_source (gcov_read_string ());
-	  lineno = gcov_read_unsigned ();
+	  unsigned start_line = gcov_read_unsigned ();
+	  unsigned end_line = gcov_read_unsigned ();
 
 	  fn = new function_t;
 	  fn->name = function_name;
@@ -1341,9 +1506,10 @@ read_graph_file (void)
 	  fn->lineno_checksum = lineno_checksum;
 	  fn->cfg_checksum = cfg_checksum;
 	  fn->src = src_idx;
-	  fn->line = lineno;
+	  fn->start_line = start_line;
+	  fn->end_line = end_line;
+	  fn->artificial = artificial;
 
-	  fn->next_file_fn = NULL;
 	  fn->next = NULL;
 	  *fns_end = fn;
 	  fns_end = &fn->next;
@@ -2230,48 +2396,70 @@ add_line_counts (coverage_t *coverage, function_t *fn)
 	fn->blocks_executed++;
       for (unsigned i = 0; i < block->locations.size (); i++)
 	{
-	  source_info *src = &sources[block->locations[i].source_file_idx];
-
+	  unsigned src_idx = block->locations[i].source_file_idx;
 	  vector<unsigned> &lines = block->locations[i].lines;
+
+	  block->cycle.arc = NULL;
+	  block->cycle.ident = ~0U;
+
 	  for (unsigned j = 0; j < lines.size (); j++)
 	    {
-	      line = &src->lines[lines[j]];
-	      if (coverage)
+	      unsigned ln = lines[j];
+
+	      /* Line belongs to a function that is in a group.  */
+	      if (fn->group_line_p (ln, src_idx))
 		{
-		  if (!line->exists)
-		    coverage->lines++;
-		  if (!line->count && block->count)
-		    coverage->lines_executed++;
+		  gcc_assert (lines[j] - fn->start_line < fn->lines.size ());
+		  line = &(fn->lines[lines[j] - fn->start_line]);
+		  line->exists = 1;
+		  if (!block->exceptional)
+		    {
+		      line->unexceptional = 1;
+		      if (block->count == 0)
+			line->has_unexecuted_block = 1;
+		    }
+		  line->count += block->count;
 		}
-	      line->exists = 1;
-	      if (!block->exceptional)
+	      else
 		{
-		  line->unexceptional = 1;
-		  if (block->count == 0)
-		    line->has_unexecuted_block = 1;
+		  gcc_assert (ln < sources[src_idx].lines.size ());
+		  line = &(sources[src_idx].lines[ln]);
+		  if (coverage)
+		    {
+		      if (!line->exists)
+			coverage->lines++;
+		      if (!line->count && block->count)
+			coverage->lines_executed++;
+		    }
+		  line->exists = 1;
+		  if (!block->exceptional)
+		    {
+		      line->unexceptional = 1;
+		      if (block->count == 0)
+			line->has_unexecuted_block = 1;
+		    }
+		  line->count += block->count;
 		}
-	      line->count += block->count;
 	    }
-	}
-      block->cycle.arc = NULL;
-      block->cycle.ident = ~0U;
-      has_any_line = true;
 
-      if (!ix || ix + 1 == fn->blocks.size ())
-	/* Entry or exit block */;
-      else if (line != NULL)
-	{
-	  line->blocks.push_back (block);
+	  has_any_line = true;
 
-	  if (flag_branches)
+	  if (!ix || ix + 1 == fn->blocks.size ())
+	    /* Entry or exit block.  */;
+	  else if (line != NULL)
 	    {
-	      arc_t *arc;
+	      line->blocks.push_back (block);
 
-	      for (arc = block->succ; arc; arc = arc->succ_next)
+	      if (flag_branches)
 		{
-		  line->branches.push_back (arc);
-		  if (coverage && !arc->is_unconditional)
-		    add_branch_counts (coverage, arc);
+		  arc_t *arc;
+
+		  for (arc = block->succ; arc; arc = arc->succ_next)
+		    {
+		      line->branches.push_back (arc);
+		      if (coverage && !arc->is_unconditional)
+			add_branch_counts (coverage, arc);
+		    }
 		}
 	    }
 	}
@@ -2281,72 +2469,105 @@ add_line_counts (coverage_t *coverage, function_t *fn)
     fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name, fn->name);
 }
 
-/* Accumulate the line counts of a file.  */
+/* Accumulate info for LINE that belongs to SRC source file.  If ADD_COVERAGE
+   is set to true, update source file summary.  */
 
-static void
-accumulate_line_counts (source_info *src)
+static void accumulate_line_info (line_info *line, source_info *src,
+				  bool add_coverage)
 {
-  function_t *fn, *fn_p, *fn_n;
-  unsigned ix = 0;
+  if (!line->blocks.empty ())
+    {
+      /* The user expects the line count to be the number of times
+	 a line has been executed.  Simply summing the block count
+	 will give an artificially high number.  The Right Thing
+	 is to sum the entry counts to the graph of blocks on this
+	 line, then find the elementary cycles of the local graph
+	 and add the transition counts of those cycles.  */
+      gcov_type count = 0;
+
+      /* Sum the entry arcs.  */
+      for (vector<block_t *>::iterator it = line->blocks.begin ();
+	   it != line->blocks.end (); it++)
+	{
+	  arc_t *arc;
 
-  /* Reverse the function order.  */
-  for (fn = src->functions, fn_p = NULL; fn; fn_p = fn, fn = fn_n)
+	  for (arc = (*it)->pred; arc; arc = arc->pred_next)
+	    if (flag_branches && add_coverage)
+	      add_branch_counts (&src->coverage, arc);
+	}
+
+      /* Cycle detection.  */
+      for (vector<block_t *>::iterator it = line->blocks.begin ();
+	   it != line->blocks.end (); it++)
+	{
+	  for (arc_t *arc = (*it)->pred; arc; arc = arc->pred_next)
+	    if (!line->has_block (arc->src))
+	      count += arc->count;
+	  for (arc_t *arc = (*it)->succ; arc; arc = arc->succ_next)
+	    arc->cs_count = arc->count;
+	}
+
+      /* Now, add the count of loops entirely on this line.  */
+      count += get_cycles_count (*line);
+      line->count = count;
+    }
+
+  if (line->exists && add_coverage)
     {
-      fn_n = fn->next_file_fn;
-      fn->next_file_fn = fn_p;
+      src->coverage.lines++;
+      if (line->count)
+	src->coverage.lines_executed++;
     }
-  src->functions = fn_p;
+}
 
-  for (vector<line_info>::reverse_iterator it = src->lines.rbegin ();
-       it != src->lines.rend (); it++)
+/* Accumulate the line counts of a file.  */
+
+static void
+accumulate_line_counts (source_info *src)
+{
+  /* First work on group functions.  */
+  for (vector<function_t *>::iterator it = src->functions.begin ();
+       it != src->functions.end (); it++)
     {
-      line_info *line = &(*it);
-      if (!line->blocks.empty ())
-	{
-	  /* The user expects the line count to be the number of times
-	     a line has been executed. Simply summing the block count
-	     will give an artificially high number.  The Right Thing
-	     is to sum the entry counts to the graph of blocks on this
-	     line, then find the elementary cycles of the local graph
-	     and add the transition counts of those cycles.  */
-	  gcov_type count = 0;
-
-	  /* Sum the entry arcs.  */
-	  for (vector<block_t *>::iterator it = line->blocks.begin ();
-	       it != line->blocks.end (); it++)
-	    {
-	      arc_t *arc;
+      function_info *fn = *it;
 
-	      for (arc = (*it)->pred; arc; arc = arc->pred_next)
-		if (flag_branches)
-		  add_branch_counts (&src->coverage, arc);
-	    }
+      if (fn->src != src->index || !fn->is_group)
+	continue;
 
-	  /* Cycle detection.  */
-	  for (vector<block_t *>::iterator it = line->blocks.begin ();
-	       it != line->blocks.end (); it++)
-	    {
-	      for (arc_t *arc = (*it)->pred; arc; arc = arc->pred_next)
-		if (!line->has_block (arc->src))
-		  count += arc->count;
-	      for (arc_t *arc = (*it)->succ; arc; arc = arc->succ_next)
-		arc->cs_count = arc->count;
-	    }
+      for (vector<line_info>::iterator it2 = fn->lines.begin ();
+	   it2 != fn->lines.end (); it2++)
+	  {
+	    line_info *line = &(*it2);
+	    accumulate_line_info (line, src, false);
+	  }
+    }
 
-	  /* Now, add the count of loops entirely on this line.  */
-	  count += get_cycles_count (*line);
-	  line->count = count;
-	}
+  /* Work on global lines that line in source file SRC.  */
+  for (vector<line_info>::iterator it = src->lines.begin ();
+       it != src->lines.end (); it++)
+    accumulate_line_info (&(*it), src, true);
 
-      if (line->exists)
-	{
-	  src->coverage.lines++;
-	  if (line->count)
-	    src->coverage.lines_executed++;
-	}
+  /* If not using intermediate mode, sum lines of group functions and
+     add them to lines that live in a source file.  */
+  if (!flag_intermediate_format)
+    for (vector<function_t *>::iterator it = src->functions.begin ();
+	 it != src->functions.end (); it++)
+      {
+	function_info *fn = *it;
 
-      ix++;
-    }
+	if (fn->src != src->index || !fn->is_group)
+	  continue;
+
+	for (unsigned i = 0; i < fn->lines.size (); i++)
+	  {
+	    line_info &line = fn->lines[i];
+	    if (line.exists)
+	      {
+		src->lines[fn->start_line + i].count += line.count;
+		src->lines[fn->start_line + i].exists = 1;
+	      }
+	  }
+      }
 }
 
 /* Output information about ARC number IX.  Returns nonzero if
@@ -2464,7 +2685,8 @@ output_line_beginning (FILE *f, bool exists, bool unexceptional,
 	      if (flag_use_colors)
 		{
 		  pad_count_string (s);
-		  s = SGR_SEQ (COLOR_BG_MAGENTA COLOR_SEPARATOR COLOR_FG_WHITE);
+		  s.insert (0, SGR_SEQ (COLOR_BG_MAGENTA
+					COLOR_SEPARATOR COLOR_FG_WHITE));
 		  s += SGR_RESET;
 		}
 	      else
@@ -2502,6 +2724,86 @@ output_line_beginning (FILE *f, bool exists, bool unexceptional,
   fprintf (f, "%s:%5u", s.c_str (), line_num);
 }
 
+static void
+print_source_line (FILE *f, const vector<const char *> &source_lines,
+		   unsigned line)
+{
+  gcc_assert (line >= 1);
+  gcc_assert (line <= source_lines.size ());
+
+  fprintf (f, ":%s\n", source_lines[line - 1]);
+}
+
+/* Output line details for LINE and print it to F file.  LINE lives on
+   LINE_NUM.  */
+
+static void
+output_line_details (FILE *f, const line_info *line, unsigned line_num)
+{
+  if (flag_all_blocks)
+    {
+      arc_t *arc;
+      int ix, jx;
+
+      ix = jx = 0;
+      for (vector<block_t *>::const_iterator it = line->blocks.begin ();
+	   it != line->blocks.end (); it++)
+	{
+	  if (!(*it)->is_call_return)
+	    {
+	      output_line_beginning (f, line->exists,
+				     (*it)->exceptional, false,
+				     (*it)->count, line_num,
+				     "%%%%%", "$$$$$");
+	      fprintf (f, "-block %2d", ix++);
+	      if (flag_verbose)
+		fprintf (f, " (BB %u)", (*it)->id);
+	      fprintf (f, "\n");
+	    }
+	  if (flag_branches)
+	    for (arc = (*it)->succ; arc; arc = arc->succ_next)
+	      jx += output_branch_count (f, jx, arc);
+	}
+    }
+  else if (flag_branches)
+    {
+      int ix;
+
+      ix = 0;
+      for (vector<arc_t *>::const_iterator it = line->branches.begin ();
+	   it != line->branches.end (); it++)
+	ix += output_branch_count (f, ix, (*it));
+    }
+}
+
+/* Output detail statistics about function FN to file F.  */
+
+static void
+output_function_details (FILE *f, const function_info *fn)
+{
+  if (!flag_branches)
+    return;
+
+  arc_t *arc = fn->blocks[EXIT_BLOCK].pred;
+  gcov_type return_count = fn->blocks[EXIT_BLOCK].count;
+  gcov_type called_count = fn->blocks[ENTRY_BLOCK].count;
+
+  for (; arc; arc = arc->pred_next)
+    if (arc->fake)
+      return_count -= arc->count;
+
+  fprintf (f, "function %s",
+	   flag_demangled_names ? fn->demangled_name : fn->name);
+  fprintf (f, " called %s",
+	   format_gcov (called_count, 0, -1));
+  fprintf (f, " returned %s",
+	   format_gcov (return_count, called_count, 0));
+  fprintf (f, " blocks executed %s",
+	   format_gcov (fn->blocks_executed, fn->blocks.size () - 2,
+			0));
+  fprintf (f, "\n");
+}
+
 /* Read in the source file one line at a time, and output that line to
    the gcov file preceded by its execution count and other
    information.  */
@@ -2512,10 +2814,7 @@ output_lines (FILE *gcov_file, const source_info *src)
 #define  DEFAULT_LINE_START "        -:    0:"
 
   FILE *source_file;
-  unsigned line_num;	/* current line number.  */
-  const line_info *line;  /* current line info ptr.  */
   const char *retval = "";	/* status of source file reading.  */
-  function_t *fn = NULL;
 
   fprintf (gcov_file, DEFAULT_LINE_START "Source:%s\n", src->coverage.name);
   if (!multiple_files)
@@ -2536,36 +2835,35 @@ output_lines (FILE *gcov_file, const source_info *src)
   else if (src->file_time == 0)
     fprintf (gcov_file, DEFAULT_LINE_START "Source is newer than graph\n");
 
-  if (flag_branches)
-    fn = src->functions;
+  vector<const char *> source_lines;
+  while ((retval = read_line (source_file)) != NULL)
+    source_lines.push_back (xstrdup (retval));
 
-  for (line_num = 1, line = &src->lines[line_num];
-       line_num < src->lines.size (); line_num++, line++)
+  unsigned line_start_group = 0;
+  vector<function_t *> fns;
+
+  for (unsigned line_num = 1; line_num <= source_lines.size (); line_num++)
     {
-      for (; fn && fn->line == line_num; fn = fn->next_file_fn)
+      if (line_num >= src->lines.size ())
 	{
-	  arc_t *arc = fn->blocks[EXIT_BLOCK].pred;
-	  gcov_type return_count = fn->blocks[EXIT_BLOCK].count;
-	  gcov_type called_count = fn->blocks[ENTRY_BLOCK].count;
-
-	  for (; arc; arc = arc->pred_next)
-	    if (arc->fake)
-	      return_count -= arc->count;
-
-	  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",
-		   format_gcov (return_count, called_count, 0));
-	  fprintf (gcov_file, " blocks executed %s",
-		   format_gcov (fn->blocks_executed, fn->blocks.size () - 2,
-				0));
-	  fprintf (gcov_file, "\n");
+	  fprintf (gcov_file, "%9s:%5u", "-", line_num);
+	  print_source_line (gcov_file, source_lines, line_num);
+	  continue;
 	}
 
-      if (retval)
-	retval = read_line (source_file);
+      const line_info *line = &src->lines[line_num];
+
+      if (line_start_group == 0)
+	{
+	  fns = src->get_functions_at_location (line_num);
+	  if (fns.size () > 1)
+	    line_start_group = fns[0]->end_line;
+	  else if (fns.size () == 1)
+	    {
+	      function_t *fn = fns[0];
+	      output_function_details (gcov_file, fn);
+	    }
+	}
 
       /* For lines which don't exist in the .bb file, print '-' before
 	 the source line.  For lines which exist but were never
@@ -2574,54 +2872,55 @@ output_lines (FILE *gcov_file, const source_info *src)
 	 There are 16 spaces of indentation added before the source
 	 line so that tabs won't be messed up.  */
       output_line_beginning (gcov_file, line->exists, line->unexceptional,
-			     line->has_unexecuted_block, line->count, line_num,
-			     "=====", "#####");
-      fprintf (gcov_file, ":%s\n", retval ? retval : "/*EOF*/");
+			     line->has_unexecuted_block, line->count,
+			     line_num, "=====", "#####");
 
-      if (flag_all_blocks)
-	{
-	  arc_t *arc;
-	  int ix, jx;
+      print_source_line (gcov_file, source_lines, line_num);
+      output_line_details (gcov_file, line, line_num);
 
-	  ix = jx = 0;
-	  for (vector<block_t *>::const_iterator it = line->blocks.begin ();
-	       it != line->blocks.end (); it++)
+      if (line_start_group == line_num)
+	{
+	  for (vector<function_t *>::iterator it = fns.begin ();
+	       it != fns.end (); it++)
 	    {
-	      if (!(*it)->is_call_return)
+	      function_info *fn = *it;
+	      vector<line_info> &lines = fn->lines;
+
+	      fprintf (gcov_file, "------------------\n");
+	      fprintf (gcov_file, "%s:\n", flag_demangled_names
+		       ? fn->demangled_name : fn->name);
+
+	      output_function_details (gcov_file, fn);
+
+	      /* Print all lines covered by the function.  */
+	      for (unsigned i = 0; i < lines.size (); i++)
 		{
+		  line_info *line = &lines[i];
+		  unsigned l = fn->start_line + i;
+
+		  /* For lines which don't exist in the .bb file, print '-'
+		     before the source line.  For lines which exist but
+		     were never executed, print '#####' or '=====' before
+		     the source line.  Otherwise, print the execution count
+		     before the source line.
+		     There are 16 spaces of indentation added before the source
+		     line so that tabs won't be messed up.  */
 		  output_line_beginning (gcov_file, line->exists,
-					 (*it)->exceptional, false,
-					 (*it)->count, line_num,
-					 "%%%%%", "$$$$$");
-		  fprintf (gcov_file, "-block %2d", ix++);
-		  if (flag_verbose)
-		    fprintf (gcov_file, " (BB %u)", (*it)->id);
-		  fprintf (gcov_file, "\n");
+					 line->unexceptional,
+					 line->has_unexecuted_block,
+					 line->count,
+					 l, "=====", "#####");
+
+		  print_source_line (gcov_file, source_lines, l);
+		  output_line_details (gcov_file, line, l);
 		}
-	      if (flag_branches)
-		for (arc = (*it)->succ; arc; arc = arc->succ_next)
-		  jx += output_branch_count (gcov_file, jx, arc);
 	    }
-	}
-      else if (flag_branches)
-	{
-	  int ix;
 
-	  ix = 0;
-	  for (vector<arc_t *>::const_iterator it = line->branches.begin ();
-	       it != line->branches.end (); it++)
-	    ix += output_branch_count (gcov_file, ix, (*it));
+	  fprintf (gcov_file, "------------------\n");
+	  line_start_group = 0;
 	}
     }
 
-  /* Handle all remaining source lines.  There may be lines after the
-     last line of code.  */
-  if (retval)
-    {
-      for (; (retval = read_line (source_file)); line_num++)
-	fprintf (gcov_file, "%9s:%5u:%s\n", "-", line_num, retval);
-    }
-
   if (source_file)
     fclose (source_file);
 }
-- 
2.14.3



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 3/7] GCOV: add support for lines with an unexecuted lines.
  2017-10-26  8:12 ` [PATCH 3/7] GCOV: add support for lines with an unexecuted lines marxin
  2017-10-30 12:27   ` Nathan Sidwell
@ 2017-11-02 15:33   ` Eric Botcazou
  1 sibling, 0 replies; 33+ messages in thread
From: Eric Botcazou @ 2017-11-02 15:33 UTC (permalink / raw)
  To: marxin; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1196 bytes --]

> It's possible to have a line of code that has a non-zero coverage.
> However, it can contain unexecuted blocks and I hope adding a
> notification can be usefull. LLVM also does that:
> 
>         -:    0:Source:ternary.c
>         -:    0:Graph:ternary.gcno
>         -:    0:Data:ternary.gcda
>         -:    0:Runs:1
>         -:    0:Programs:1
>         -:    1:int b, c, d, e;
>         -:    2:
>         1:    3:int main()
>         -:    4:{
>        1*:    5:	int a = b < 1 ? (c < 3 ? d : c) : e;
>         1:    6:        return a;
>         -:    7:}

That can be annoying for languages more expressive than C's family though.

For example Ada has built-in overflow checking for addition, which means that:

  function Add (I1, I2 : Integer) return Integer is
  begin
    return I1 + I2;
  end;

will now have the '*' symbol:

        1:    3:  function Add (I1, I2 : Integer) return Integer is
        -:    4:  begin
       1*:    5:    return I1 + I2;
        -:    6:  end;

which doesn't really make sense from the user's viewpoint.  How does LLVM deal 
with that (assuming it does)?

Testcase attached, compile with gnatmake p -fprofile-arcs -ftest-coverage.

-- 
Eric Botcazou

[-- Attachment #2: p.adb --]
[-- Type: text/x-adasrc, Size: 176 bytes --]

procedure P is

  function Add (I1, I2 : Integer) return Integer is
  begin
    return I1 + I2;
  end;

begin
  if Add (1, 2) /= 3 then
    raise Program_Error;
  end if;
end;

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 8/N][RFC][v3]: GCOV: support multiple functions per a line
  2017-11-01  8:00     ` [PATCH 8/N][RFC] v2 " Martin Liška
@ 2017-11-07 10:53       ` Martin Liška
  2017-11-07 15:09         ` Nathan Sidwell
  0 siblings, 1 reply; 33+ messages in thread
From: Martin Liška @ 2017-11-07 10:53 UTC (permalink / raw)
  To: Nathan Sidwell, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 191 bytes --]

Hello.

This is slightly updated version from the previous. Various small issues were fixed
and I update documentation in order to reflect the changes.

Ready to be installed?
Thanks,
Martin

[-- Attachment #2: 0001-GCOV-support-multiple-functions-per-a-line.patch --]
[-- Type: text/x-patch, Size: 48855 bytes --]

From 5af7859e0d58d460f37c0b607adbf3c9ca927025 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Thu, 26 Oct 2017 10:39:40 +0200
Subject: [PATCH 1/8] GCOV: support multiple functions per a line

gcc/ChangeLog:

2017-11-01  Martin Liska  <mliska@suse.cz>

	* coverage.c (coverage_begin_function): Output also end locus
	of a function and information whether the function is
	artificial.
	* gcov-dump.c (tag_function): Parse and print the information.
	* gcov.c (INCLUDE_MAP): Add include.
	(INCLUDE_SET): Likewise.
	(struct line_info): Move earlier in the source file because
	of vector<line_info> in function_info structure.
	(line_info::line_info): Likewise.
	(line_info::has_block): Likewise.
	(struct source_info): Add new member index.
	(source_info::get_functions_at_location): New function.
	(function_info::group_line_p): New function.
	(output_intermediate_line): New function.
	(output_intermediate_file): Use the mentioned function.
	(struct function_start): New.
	(struct function_start_pair_hash): Likewise.
	(process_file): Add code that identifies group functions.
	Assign lines either to global or function scope.
	(generate_results): Skip artificial functions.
	(find_source): Assign index for each source file.
	(read_graph_file): Read new flag artificial and end_line.
	(add_line_counts): Assign it either to global of function scope.
	(accumulate_line_counts): Isolate core of the function to
	accumulate_line_info and call it for both function and global
	scope lines.
	(accumulate_line_info): New function.
	(output_line_beginning): Fix GNU coding style.
	(print_source_line): New function.
	(output_line_details): Likewise.
	(output_function_details): Likewise.
	(output_lines): Iterate both source (global) scope and function
	scope.
	(struct function_line_start_cmp): New class.
	* doc/gcov.texi: Reflect changes in documentation.
---
 gcc/coverage.c    |   2 +
 gcc/doc/gcov.texi | 329 +++++++++++++++------
 gcc/gcov-dump.c   |   7 +-
 gcc/gcov.c        | 847 +++++++++++++++++++++++++++++++++++++-----------------
 4 files changed, 829 insertions(+), 356 deletions(-)

diff --git a/gcc/coverage.c b/gcc/coverage.c
index 8a56a677f15..8ac593920b6 100644
--- a/gcc/coverage.c
+++ b/gcc/coverage.c
@@ -663,8 +663,10 @@ coverage_begin_function (unsigned lineno_checksum, unsigned cfg_checksum)
   gcov_write_unsigned (cfg_checksum);
   gcov_write_string (IDENTIFIER_POINTER
 		     (DECL_ASSEMBLER_NAME (current_function_decl)));
+  gcov_write_unsigned (DECL_ARTIFICIAL (current_function_decl));
   gcov_write_filename (xloc.file);
   gcov_write_unsigned (xloc.line);
+  gcov_write_unsigned (expand_location (cfun->function_end_locus).line);
   gcov_write_length (offset);
 
   return !gcov_is_error ();
diff --git a/gcc/doc/gcov.texi b/gcc/doc/gcov.texi
index 5c4ba8a51a7..6f6a92c131a 100644
--- a/gcc/doc/gcov.texi
+++ b/gcc/doc/gcov.texi
@@ -193,7 +193,7 @@ Write counts in human readable format (like 24k).
 
 @smallexample
 file:@var{source_file_name}
-function:@var{line_number},@var{execution_count},@var{function_name}
+function:@var{start_line_number},@var{end_line_number},@var{execution_count},@var{function_name}
 lcount:@var{line number},@var{execution_count},@var{has_unexecuted_block}
 branch:@var{line_number},@var{branch_coverage_type}
 
@@ -201,24 +201,55 @@ Where the @var{branch_coverage_type} is
    notexec (Branch not executed)
    taken (Branch executed and taken)
    nottaken (Branch executed, but not taken)
+@end smallexample
 
 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
+until the next @var{file} entry.  If there are multiple functions that
+start on a single line, then corresponding lcount is repeated multiple
+times.
 
 Here is a sample when @option{-i} is used in conjunction with @option{-b} option:
 
 @smallexample
-file:array.cc
-function:11,1,_Z3sumRKSt6vectorIPiSaIS0_EE
-function:22,1,main
-lcount:11,1,0
-lcount:12,1,0
-lcount:14,1,0
-branch:14,taken
-lcount:26,1,0
-branch:28,nottaken
+file:tmp.cpp
+function:7,7,0,_ZN3FooIcEC2Ev
+function:7,7,1,_ZN3FooIiEC2Ev
+function:8,8,0,_ZN3FooIcE3incEv
+function:8,8,2,_ZN3FooIiE3incEv
+function:18,37,1,main
+lcount:7,0,1
+lcount:7,1,0
+lcount:8,0,1
+lcount:8,2,0
+lcount:18,1,0
+lcount:21,1,0
+branch:21,taken
+branch:21,nottaken
+lcount:23,1,0
+branch:23,taken
+branch:23,nottaken
+lcount:24,1,0
+branch:24,taken
+branch:24,nottaken
+lcount:25,1,0
+lcount:27,11,0
+branch:27,taken
+branch:27,taken
+lcount:28,10,0
+lcount:30,1,1
+branch:30,nottaken
+branch:30,taken
+lcount:32,1,0
+branch:32,nottaken
+branch:32,taken
+lcount:33,0,1
+branch:33,notexec
+branch:33,notexec
+lcount:35,1,0
+branch:35,taken
+branch:35,nottaken
+lcount:36,1,0
 @end smallexample
 
 @item -k
@@ -391,79 +422,158 @@ source file compiled with @option{-fprofile-arcs}, an accompanying
 
 Running @command{gcov} with your program's source file names as arguments
 will now produce a listing of the code along with frequency of execution
-for each line.  For example, if your program is called @file{tmp.c}, this
+for each line.  For example, if your program is called @file{tmp.cpp}, this
 is what you see when you use the basic @command{gcov} facility:
 
 @smallexample
-$ gcc -fprofile-arcs -ftest-coverage tmp.c
+$ g++ -fprofile-arcs -ftest-coverage tmp.cpp
 $ a.out
-$ gcov tmp.c
-File 'tmp.c'
-Lines executed:90.00% of 10
-Creating 'tmp.c.gcov'
+$ gcov tmp.cpp -m
+File 'tmp.cpp'
+Lines executed:92.86% of 14
+Creating 'tmp.cpp.gcov'
 @end smallexample
 
-The file @file{tmp.c.gcov} contains output from @command{gcov}.
+The file @file{tmp.cpp.gcov} contains output from @command{gcov}.
 Here is a sample:
 
 @smallexample
-        -:    0:Source:tmp.c
+        -:    0:Source:tmp.cpp
         -:    0:Graph:tmp.gcno
         -:    0:Data:tmp.gcda
         -:    0:Runs:1
         -:    0:Programs:1
         -:    1:#include <stdio.h>
         -:    2:
-        -:    3:int main (void)
-        1:    4:@{
-        1:    5:  int i, total;
-        -:    6:
-        1:    7:  total = 0;
-        -:    8:
-       11:    9:  for (i = 0; i < 10; i++)
-       10:   10:    total += i;
-        -:   11:
-        1:   12:  if (total != 45)
-    #####:   13:    printf ("Failure\n");
-        -:   14:  else
-        1:   15:    printf ("Success\n");
-        1:   16:  return 0;
-        -:   17:@}
+        -:    3:template<class T>
+        -:    4:class Foo
+        -:    5:@{
+        -:    6:  public:
+       1*:    7:  Foo(): b (1000) @{@}
+------------------
+Foo<char>::Foo():
+    #####:    7:  Foo(): b (1000) @{@}
+------------------
+Foo<int>::Foo():
+        1:    7:  Foo(): b (1000) @{@}
+------------------
+       2*:    8:  void inc () @{ b++; @}
+------------------
+Foo<char>::inc():
+    #####:    8:  void inc () @{ b++; @}
+------------------
+Foo<int>::inc():
+        2:    8:  void inc () @{ b++; @}
+------------------
+        -:    9:
+        -:   10:  private:
+        -:   11:  int b;
+        -:   12:@};
+        -:   13:
+        -:   14:template class Foo<int>;
+        -:   15:template class Foo<char>;
+        -:   16:
+        -:   17:int
+        1:   18:main (void)
+        -:   19:@{
+        -:   20:  int i, total;
+        1:   21:  Foo<int> counter;
+        -:   22:
+        1:   23:  counter.inc();
+        1:   24:  counter.inc();
+        1:   25:  total = 0;
+        -:   26:
+       11:   27:  for (i = 0; i < 10; i++)
+       10:   28:    total += i;
+        -:   29:
+       1*:   30:  int v = total > 100 ? 1 : 2;
+        -:   31:
+        1:   32:  if (total != 45)
+    #####:   33:    printf ("Failure\n");
+        -:   34:  else
+        1:   35:    printf ("Success\n");
+        1:   36:  return 0;
+        -:   37:@}
 @end smallexample
 
+Note that line 7 is shown in the report multiple times.  First occurrence
+presents total number of execution of the line and the next two belong
+to instances of class Foo constructors.  As you can also see, line 30 contains
+some unexecuted basic blocks and thus execution count has asterisk symbol.
+
 When you use the @option{-a} option, you will get individual block
 counts, and the output looks like this:
 
 @smallexample
-        -:    0:Source:tmp.c
+        -:    0:Source:tmp.cpp
         -:    0:Graph:tmp.gcno
         -:    0:Data:tmp.gcda
         -:    0:Runs:1
         -:    0:Programs:1
         -:    1:#include <stdio.h>
         -:    2:
-        -:    3:int main (void)
-        1:    4:@{
-        1:    4-block  0
-        1:    5:  int i, total;
-        -:    6:
-        1:    7:  total = 0;
-        -:    8:
-       11:    9:  for (i = 0; i < 10; i++)
-       11:    9-block  0
-       10:   10:    total += i;
-       10:   10-block  0
-        -:   11:
-        1:   12:  if (total != 45)
-        1:   12-block  0
-    #####:   13:    printf ("Failure\n");
-    $$$$$:   13-block  0
-        -:   14:  else
-        1:   15:    printf ("Success\n");
-        1:   15-block  0
-        1:   16:  return 0;
-        1:   16-block  0
-        -:   17:@}
+        -:    3:template<class T>
+        -:    4:class Foo
+        -:    5:@{
+        -:    6:  public:
+       1*:    7:  Foo(): b (1000) @{@}
+------------------
+Foo<char>::Foo():
+    #####:    7:  Foo(): b (1000) @{@}
+------------------
+Foo<int>::Foo():
+        1:    7:  Foo(): b (1000) @{@}
+------------------
+       2*:    8:  void inc () @{ b++; @}
+------------------
+Foo<char>::inc():
+    #####:    8:  void inc () @{ b++; @}
+------------------
+Foo<int>::inc():
+        2:    8:  void inc () @{ b++; @}
+------------------
+        -:    9:
+        -:   10:  private:
+        -:   11:  int b;
+        -:   12:@};
+        -:   13:
+        -:   14:template class Foo<int>;
+        -:   15:template class Foo<char>;
+        -:   16:
+        -:   17:int
+        1:   18:main (void)
+        -:   19:@{
+        -:   20:  int i, total;
+        1:   21:  Foo<int> counter;
+        1:   21-block  0
+        -:   22:
+        1:   23:  counter.inc();
+        1:   23-block  0
+        1:   24:  counter.inc();
+        1:   24-block  0
+        1:   25:  total = 0;
+        -:   26:
+       11:   27:  for (i = 0; i < 10; i++)
+        1:   27-block  0
+       11:   27-block  1
+       10:   28:    total += i;
+       10:   28-block  0
+        -:   29:
+       1*:   30:  int v = total > 100 ? 1 : 2;
+        1:   30-block  0
+    %%%%%:   30-block  1
+        1:   30-block  2
+        -:   31:
+        1:   32:  if (total != 45)
+        1:   32-block  0
+    #####:   33:    printf ("Failure\n");
+    %%%%%:   33-block  0
+        -:   34:  else
+        1:   35:    printf ("Success\n");
+        1:   35-block  0
+        1:   36:  return 0;
+        1:   36-block  0
+        -:   37:@}
 @end smallexample
 
 In this mode, each basic block is only shown on one line -- the last
@@ -477,53 +587,94 @@ block, the branch and call counts of the block will be shown, if the
 
 Because of the way GCC instruments calls, a call count can be shown
 after a line with no individual blocks.
-As you can see, line 13 contains a basic block that was not executed.
+As you can see, line 33 contains a basic block that was not executed.
 
 @need 450
 When you use the @option{-b} option, your output looks like this:
 
 @smallexample
-$ gcov -b tmp.c
-File 'tmp.c'
-Lines executed:90.00% of 10
-Branches executed:80.00% of 5
-Taken at least once:80.00% of 5
-Calls executed:50.00% of 2
-Creating 'tmp.c.gcov'
-@end smallexample
-
-Here is a sample of a resulting @file{tmp.c.gcov} file:
-
-@smallexample
-        -:    0:Source:tmp.c
+        -:    0:Source:tmp.cpp
         -:    0:Graph:tmp.gcno
         -:    0:Data:tmp.gcda
         -:    0:Runs:1
         -:    0:Programs:1
         -:    1:#include <stdio.h>
         -:    2:
-        -:    3:int main (void)
-function main called 1 returned 1 blocks executed 75%
-        1:    4:@{
-        1:    5:  int i, total;
-        -:    6:
-        1:    7:  total = 0;
-        -:    8:
-       11:    9:  for (i = 0; i < 10; i++)
+        -:    3:template<class T>
+        -:    4:class Foo
+        -:    5:@{
+        -:    6:  public:
+       1*:    7:  Foo(): b (1000) @{@}
+------------------
+Foo<char>::Foo():
+function Foo<char>::Foo() called 0 returned 0% blocks executed 0%
+    #####:    7:  Foo(): b (1000) @{@}
+------------------
+Foo<int>::Foo():
+function Foo<int>::Foo() called 1 returned 100% blocks executed 100%
+        1:    7:  Foo(): b (1000) @{@}
+------------------
+       2*:    8:  void inc () @{ b++; @}
+------------------
+Foo<char>::inc():
+function Foo<char>::inc() called 0 returned 0% blocks executed 0%
+    #####:    8:  void inc () @{ b++; @}
+------------------
+Foo<int>::inc():
+function Foo<int>::inc() called 2 returned 100% blocks executed 100%
+        2:    8:  void inc () @{ b++; @}
+------------------
+        -:    9:
+        -:   10:  private:
+        -:   11:  int b;
+        -:   12:@};
+        -:   13:
+        -:   14:template class Foo<int>;
+        -:   15:template class Foo<char>;
+        -:   16:
+        -:   17:int
+function main called 1 returned 100% blocks executed 81%
+        1:   18:main (void)
+        -:   19:@{
+        -:   20:  int i, total;
+        1:   21:  Foo<int> counter;
+call    0 returned 100%
+branch  1 taken 100% (fallthrough)
+branch  2 taken 0% (throw)
+        -:   22:
+        1:   23:  counter.inc();
+call    0 returned 100%
+branch  1 taken 100% (fallthrough)
+branch  2 taken 0% (throw)
+        1:   24:  counter.inc();
+call    0 returned 100%
+branch  1 taken 100% (fallthrough)
+branch  2 taken 0% (throw)
+        1:   25:  total = 0;
+        -:   26:
+       11:   27:  for (i = 0; i < 10; i++)
 branch  0 taken 91% (fallthrough)
 branch  1 taken 9%
-       10:   10:    total += i;
-        -:   11:
-        1:   12:  if (total != 45)
+       10:   28:    total += i;
+        -:   29:
+       1*:   30:  int v = total > 100 ? 1 : 2;
+branch  0 taken 0% (fallthrough)
+branch  1 taken 100%
+        -:   31:
+        1:   32:  if (total != 45)
 branch  0 taken 0% (fallthrough)
 branch  1 taken 100%
-    #####:   13:    printf ("Failure\n");
+    #####:   33:    printf ("Failure\n");
 call    0 never executed
-        -:   14:  else
-        1:   15:    printf ("Success\n");
-call    0 called 1 returned 100%
-        1:   16:  return 0;
-        -:   17:@}
+branch  1 never executed
+branch  2 never executed
+        -:   34:  else
+        1:   35:    printf ("Success\n");
+call    0 returned 100%
+branch  1 taken 100% (fallthrough)
+branch  2 taken 0% (throw)
+        1:   36:  return 0;
+        -:   37:@}
 @end smallexample
 
 For each function, a line is printed showing how many times the function
diff --git a/gcc/gcov-dump.c b/gcc/gcov-dump.c
index d24e72ac4a1..22d08c04c1e 100644
--- a/gcc/gcov-dump.c
+++ b/gcc/gcov-dump.c
@@ -308,9 +308,14 @@ tag_function (const char *filename ATTRIBUTE_UNUSED,
 	  
 	  name = gcov_read_string ();
 	  printf (", `%s'", name ? name : "NULL");
+	  unsigned artificial = gcov_read_unsigned ();
 	  name = gcov_read_string ();
 	  printf (" %s", name ? name : "NULL");
-	  printf (":%u", gcov_read_unsigned ());
+	  unsigned line_start = gcov_read_unsigned ();
+	  unsigned line_end = gcov_read_unsigned ();
+	  printf (":%u:%u", line_start, line_end);
+	  if (artificial)
+	    printf (", artificial");
 	}
     }
 }
diff --git a/gcc/gcov.c b/gcc/gcov.c
index 48bcdc0d4c3..16fc456c632 100644
--- a/gcc/gcov.c
+++ b/gcc/gcov.c
@@ -34,6 +34,8 @@ along with Gcov; see the file COPYING3.  If not see
 #define INCLUDE_ALGORITHM
 #define INCLUDE_VECTOR
 #define INCLUDE_STRING
+#define INCLUDE_MAP
+#define INCLUDE_SET
 #include "system.h"
 #include "coretypes.h"
 #include "tm.h"
@@ -183,6 +185,42 @@ block_info::block_info (): succ (NULL), pred (NULL), num_succ (0), num_pred (0),
   cycle.arc = NULL;
 }
 
+/* Describes a single line of source.  Contains a chain of basic blocks
+   with code on it.  */
+
+struct line_info
+{
+  /* Default constructor.  */
+  line_info ();
+
+  /* Return true when NEEDLE is one of basic blocks the line belongs to.  */
+  bool has_block (block_t *needle);
+
+  /* Execution count.  */
+  gcov_type count;
+
+  /* Branches from blocks that end on this line.  */
+  vector<arc_t *> branches;
+
+  /* blocks which start on this line.  Used in all-blocks mode.  */
+  vector<block_t *> blocks;
+
+  unsigned exists : 1;
+  unsigned unexceptional : 1;
+  unsigned has_unexecuted_block : 1;
+};
+
+line_info::line_info (): count (0), branches (), blocks (), exists (false),
+  unexceptional (0), has_unexecuted_block (0)
+{
+}
+
+bool
+line_info::has_block (block_t *needle)
+{
+  return std::find (blocks.begin (), blocks.end (), needle) != blocks.end ();
+}
+
 /* Describes a single function. Contains an array of basic blocks.  */
 
 typedef struct function_info
@@ -190,6 +228,10 @@ typedef struct function_info
   function_info ();
   ~function_info ();
 
+  /* Return true when line N belongs to the function in source file SRC_IDX.
+     The line must be defined in body of the function, can't be inlined.  */
+  bool group_line_p (unsigned n, unsigned src_idx);
+
   /* Name of function.  */
   char *name;
   char *demangled_name;
@@ -200,6 +242,13 @@ typedef struct function_info
   /* The graph contains at least one fake incoming edge.  */
   unsigned has_catch : 1;
 
+  /* True when the function is artificial and does not exist
+     in a source file.  */
+  unsigned artificial : 1;
+
+  /* True when multiple functions start at a line in a source file.  */
+  unsigned is_group : 1;
+
   /* Array of basic blocks.  Like in GCC, the entry block is
      at blocks[0] and the exit block is at blocks[1].  */
 #define ENTRY_BLOCK (0)
@@ -211,17 +260,34 @@ typedef struct function_info
   gcov_type *counts;
   unsigned num_counts;
 
-  /* First line number & file.  */
-  unsigned line;
+  /* First line number.  */
+  unsigned start_line;
+
+  /* Last line number.  */
+  unsigned end_line;
+
+  /* Index of source file where the function is defined.  */
   unsigned src;
 
-  /* Next function in same source file.  */
-  struct function_info *next_file_fn;
+  /* Vector of line information.  */
+  vector<line_info> lines;
 
   /* Next function.  */
   struct function_info *next;
 } function_t;
 
+/* Function info comparer that will sort functions according to starting
+   line.  */
+
+struct function_line_start_cmp
+{
+  inline bool operator() (const function_info *lhs,
+			  const function_info *rhs)
+    {
+      return lhs->end_line < rhs->start_line;
+    }
+};
+
 /* Describes coverage of a file or function.  */
 
 typedef struct coverage_info
@@ -239,42 +305,6 @@ typedef struct coverage_info
   char *name;
 } coverage_t;
 
-/* Describes a single line of source. Contains a chain of basic blocks
-   with code on it.  */
-
-struct line_info
-{
-  /* Default constructor.  */
-  line_info ();
-
-  /* Return true when NEEDLE is one of basic blocks the line belongs to.  */
-  bool has_block (block_t *needle);
-
-  /* Execution count.  */
-  gcov_type count;
-
-  /* Branches from blocks that end on this line.  */
-  vector<arc_t *> branches;
-
-  /* blocks which start on this line.  Used in all-blocks mode.  */
-  vector<block_t *> blocks;
-
-  unsigned exists : 1;
-  unsigned unexceptional : 1;
-  unsigned has_unexecuted_block : 1;
-};
-
-line_info::line_info (): count (0), branches (), blocks (), exists (false),
-  unexceptional (0), has_unexecuted_block (0)
-{
-}
-
-bool
-line_info::has_block (block_t *needle)
-{
-  return std::find (blocks.begin (), blocks.end (), needle) != blocks.end ();
-}
-
 /* Describes a file mentioned in the block graph.  Contains an array
    of line info.  */
 
@@ -283,6 +313,11 @@ struct source_info
   /* Default constructor.  */
   source_info ();
 
+  vector<function_t *> get_functions_at_location (unsigned line_num) const;
+
+  /* Index of the source_info in sources vector.  */
+  unsigned index;
+
   /* Canonical name of source file.  */
   char *name;
   time_t file_time;
@@ -294,14 +329,29 @@ struct source_info
 
   /* Functions in this source file.  These are in ascending line
      number order.  */
-  function_t *functions;
+  vector <function_t *> functions;
 };
 
-source_info::source_info (): name (NULL), file_time (), lines (),
-  coverage (), functions (NULL)
+source_info::source_info (): name (NULL), file_time (),
+  lines (), coverage (), functions ()
 {
 }
 
+vector<function_t *>
+source_info::get_functions_at_location (unsigned line_num) const
+{
+  vector<function_t *> r;
+
+  for (vector<function_t *>::const_iterator it = functions.begin ();
+       it != functions.end (); it++)
+    {
+      if ((*it)->start_line == line_num && (*it)->src == index)
+	r.push_back (*it);
+    }
+
+  return r;
+}
+
 class name_map
 {
 public:
@@ -495,8 +545,9 @@ extern int main (int, char **);
 
 function_info::function_info (): name (NULL), demangled_name (NULL),
   ident (0), lineno_checksum (0), cfg_checksum (0), has_catch (0),
+  artificial (0), is_group (0),
   blocks (), blocks_executed (0), counts (NULL), num_counts (0),
-  line (0), src (0), next_file_fn (NULL), next (NULL)
+  start_line (0), end_line (0), src (0), lines (), next (NULL)
 {
 }
 
@@ -518,6 +569,11 @@ function_info::~function_info ()
   free (name);
 }
 
+bool function_info::group_line_p (unsigned n, unsigned src_idx)
+{
+  return is_group && src == src_idx && start_line <= n && n <= end_line;
+}
+
 /* Cycle detection!
    There are a bajillion algorithms that do this.  Boost's function is named
    hawick_cycles, so I used the algorithm by K. A. Hawick and H. A. James in
@@ -889,6 +945,42 @@ process_args (int argc, char **argv)
   return optind;
 }
 
+/* Output intermediate LINE sitting on LINE_NUM to output file F.  */
+
+static void
+output_intermediate_line (FILE *f, line_info *line, unsigned line_num)
+{
+  if (!line->exists)
+    return;
+
+  fprintf (f, "lcount:%u,%s,%d\n", line_num,
+	   format_gcov (line->count, 0, -1),
+	   line->has_unexecuted_block);
+
+  vector<arc_t *>::const_iterator it;
+  if (flag_branches)
+    for (it = line->branches.begin (); it != line->branches.end ();
+	 it++)
+      {
+	if (!(*it)->is_unconditional && !(*it)->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 ((*it)->src->count)
+		 branch_type
+			= ((*it)->count > 0) ? "taken" : "nottaken";
+	    else
+	      branch_type = "notexec";
+	    fprintf (f, "branch:%d,%s\n", line_num, branch_type);
+	  }
+      }
+}
+
 /* Output the result in intermediate format used by 'lcov'.
 
 The intermediate format contains a single file named 'foo.cc.gcov',
@@ -902,50 +994,96 @@ file 'foo.cc.gcov' similar to the above example. */
 static void
 output_intermediate_file (FILE *gcov_file, source_info *src)
 {
-  unsigned line_num;    /* current line number.  */
-  const line_info *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->next_file_fn)
+  std::sort (src->functions.begin (), src->functions.end (),
+	     function_line_start_cmp ());
+  for (vector<function_t *>::iterator it = src->functions.begin ();
+       it != src->functions.end (); it++)
     {
       /* 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);
+      fprintf (gcov_file, "function:%d,%d,%s,%s\n", (*it)->start_line,
+	       (*it)->end_line, format_gcov ((*it)->blocks[0].count, 0, -1),
+	       flag_demangled_names ? (*it)->demangled_name : (*it)->name);
     }
 
-  for (line_num = 1, line = &src->lines[line_num];
-       line_num < src->lines.size ();
-       line_num++, line++)
+  for (unsigned line_num = 0; line_num <= src->lines.size (); line_num++)
     {
-      if (line->exists)
-	fprintf (gcov_file, "lcount:%u,%s,%d\n", line_num,
-		 format_gcov (line->count, 0, -1), line->has_unexecuted_block);
-      if (flag_branches)
-	for (vector<arc_t *>::const_iterator it = line->branches.begin ();
-	     it != line->branches.end (); it++)
-	  {
-	    if (!(*it)->is_unconditional && !(*it)->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 ((*it)->src->count)
-		  branch_type = ((*it)->count > 0) ? "taken" : "nottaken";
-		else
-		  branch_type = "notexec";
-		fprintf (gcov_file, "branch:%d,%s\n", line_num, branch_type);
-	      }
-	  }
+      vector<function_t *> fns = src->get_functions_at_location (line_num);
+
+      /* Print first group functions that begin on the line.  */
+      for (vector<function_t *>::iterator it2 = fns.begin ();
+	   it2 != fns.end (); it2++)
+	{
+	  vector<line_info> &lines = (*it2)->lines;
+	  /* Print all lines covered by the function.  */
+	  for (unsigned i = 0; i < lines.size (); i++)
+	    {
+	      line_info *line = &lines[i];
+	      output_intermediate_line (gcov_file, line, line_num + i);
+	    }
+	}
+
+      /* Follow with lines associated with the source file.  */
+      output_intermediate_line (gcov_file, &src->lines[line_num], line_num);
     }
 }
 
+/* Function start pair.  */
+struct function_start
+{
+  unsigned source_file_idx;
+  unsigned start_line;
+};
+
+/* Traits class for function start hash maps below.  */
+
+struct function_start_pair_hash : typed_noop_remove <function_start>
+{
+  typedef function_start value_type;
+  typedef function_start compare_type;
+
+  static hashval_t
+  hash (const function_start &ref)
+  {
+    inchash::hash hstate (0);
+    hstate.add_int (ref.source_file_idx);
+    hstate.add_int (ref.start_line);
+    return hstate.end ();
+  }
+
+  static bool
+  equal (const function_start &ref1, const function_start &ref2)
+  {
+    return (ref1.source_file_idx == ref2.source_file_idx
+	    && ref1.start_line == ref2.start_line);
+  }
+
+  static void
+  mark_deleted (function_start &ref)
+  {
+    ref.start_line = ~1U;
+  }
+
+  static void
+  mark_empty (function_start &ref)
+  {
+    ref.start_line = ~2U;
+  }
+
+  static bool
+  is_deleted (const function_start &ref)
+  {
+    return ref.start_line == ~1U;
+  }
+
+  static bool
+  is_empty (const function_start &ref)
+  {
+    return ref.start_line == ~2U;
+  }
+};
+
 /* Process a single input file.  */
 
 static void
@@ -959,6 +1097,28 @@ process_file (const char *file_name)
     return;
 
   read_count_file (fns);
+
+  hash_map<function_start_pair_hash, function_t *> fn_map;
+
+  /* Identify group functions.  */
+  for (function_t *f = fns; f; f = f->next)
+    if (!f->artificial)
+      {
+	function_start needle;
+	needle.source_file_idx = f->src;
+	needle.start_line = f->start_line;
+
+	function_t **slot = fn_map.get (needle);
+	if (slot)
+	  {
+	    gcc_assert ((*slot)->end_line == f->end_line);
+	    (*slot)->is_group = 1;
+	    f->is_group = 1;
+	  }
+	else
+	  fn_map.put (needle, f);
+      }
+
   while (fns)
     {
       function_t *fn = fns;
@@ -968,46 +1128,50 @@ process_file (const char *file_name)
       if (fn->counts || no_data_file)
 	{
 	  unsigned src = fn->src;
-	  unsigned line = fn->line;
 	  unsigned block_no;
-	  function_t *probe, **prev;
-
-	  /* Now insert it into the source file's list of
-	     functions. Normally functions will be encountered in
-	     ascending order, so a simple scan is quick.  Note we're
-	     building this list in reverse order.  */
-	  for (prev = &sources[src].functions;
-	       (probe = *prev); prev = &probe->next_file_fn)
-	    if (probe->line <= line)
-	      break;
-	  fn->next_file_fn = probe;
-	  *prev = fn;
 
-	  /* Mark last line in files touched by function.  */
-	  for (block_no = 0; block_no != fn->blocks.size (); block_no++)
+	  /* Process only non-artificial functions.  */
+	  if (!fn->artificial)
 	    {
-	      block_t *block = &fn->blocks[block_no];
-	      for (unsigned i = 0; i < block->locations.size (); i++)
-		{
-		  unsigned s = block->locations[i].source_file_idx;
-
-		  /* Sort lines of locations.  */
-		  sort (block->locations[i].lines.begin (),
-			block->locations[i].lines.end ());
+	      source_info *s = &sources[src];
+	      s->functions.push_back (fn);
 
-		  if (!block->locations[i].lines.empty ())
+	      /* Mark last line in files touched by function.  */
+	      for (block_no = 0; block_no != fn->blocks.size (); block_no++)
+		{
+		  block_t *block = &fn->blocks[block_no];
+		  for (unsigned i = 0; i < block->locations.size (); i++)
 		    {
-		      unsigned last_line
-			= block->locations[i].lines.back () + 1;
-		      if (last_line > sources[s].lines.size ())
-			sources[s].lines.resize (last_line);
+		      /* Sort lines of locations.  */
+		      sort (block->locations[i].lines.begin (),
+			    block->locations[i].lines.end ());
+
+		      if (!block->locations[i].lines.empty ())
+			{
+			  s = &sources[block->locations[i].source_file_idx];
+			  unsigned last_line
+			    = block->locations[i].lines.back ();
+
+			  /* Record new lines for the function.  */
+			  if (last_line >= s->lines.size ())
+			    {
+			      /* Record new lines for a source file.  */
+			      s->lines.resize (last_line + 1);
+			    }
+			}
 		    }
+
+		  /* Allocate lines for group function, following start_line
+		     and end_line information of the function.  */
+		  if (fn->is_group)
+		    fn->lines.resize (fn->end_line - fn->start_line + 1);
 		}
+
+	      solve_flow_graph (fn);
+	      if (fn->has_catch)
+		find_exception_blocks (fn);
 	    }
 
-	  solve_flow_graph (fn);
-	  if (fn->has_catch)
-	    find_exception_blocks (fn);
 	  *fn_end = fn;
 	  fn_end = &fn->next;
 	}
@@ -1057,6 +1221,8 @@ generate_results (const char *file_name)
   for (fn = functions; fn; fn = fn->next)
     {
       coverage_t coverage;
+      if (fn->artificial)
+	continue;
 
       memset (&coverage, 0, sizeof (coverage));
       coverage.name = flag_demangled_names ? fn->demangled_name : fn->name;
@@ -1237,6 +1403,7 @@ find_source (const char *file_name)
       src = &sources.back ();
       src->name = canon;
       src->coverage.name = src->name;
+      src->index = idx;
       if (source_length
 #if HAVE_DOS_BASED_FILE_SYSTEM
 	  /* You lose if separators don't match exactly in the
@@ -1328,15 +1495,17 @@ read_graph_file (void)
       if (tag == GCOV_TAG_FUNCTION)
 	{
 	  char *function_name;
-	  unsigned ident, lineno;
+	  unsigned ident;
 	  unsigned lineno_checksum, cfg_checksum;
 
 	  ident = gcov_read_unsigned ();
 	  lineno_checksum = gcov_read_unsigned ();
 	  cfg_checksum = gcov_read_unsigned ();
 	  function_name = xstrdup (gcov_read_string ());
+	  unsigned artificial = gcov_read_unsigned ();
 	  unsigned src_idx = find_source (gcov_read_string ());
-	  lineno = gcov_read_unsigned ();
+	  unsigned start_line = gcov_read_unsigned ();
+	  unsigned end_line = gcov_read_unsigned ();
 
 	  fn = new function_t;
 	  fn->name = function_name;
@@ -1350,9 +1519,10 @@ read_graph_file (void)
 	  fn->lineno_checksum = lineno_checksum;
 	  fn->cfg_checksum = cfg_checksum;
 	  fn->src = src_idx;
-	  fn->line = lineno;
+	  fn->start_line = start_line;
+	  fn->end_line = end_line;
+	  fn->artificial = artificial;
 
-	  fn->next_file_fn = NULL;
 	  fn->next = NULL;
 	  *fns_end = fn;
 	  fns_end = &fn->next;
@@ -2266,48 +2436,66 @@ add_line_counts (coverage_t *coverage, function_t *fn)
 	fn->blocks_executed++;
       for (unsigned i = 0; i < block->locations.size (); i++)
 	{
-	  source_info *src = &sources[block->locations[i].source_file_idx];
-
+	  unsigned src_idx = block->locations[i].source_file_idx;
 	  vector<unsigned> &lines = block->locations[i].lines;
+
+	  block->cycle.arc = NULL;
+	  block->cycle.ident = ~0U;
+
 	  for (unsigned j = 0; j < lines.size (); j++)
 	    {
-	      line = &src->lines[lines[j]];
-	      if (coverage)
+	      unsigned ln = lines[j];
+
+	      /* Line belongs to a function that is in a group.  */
+	      if (fn->group_line_p (ln, src_idx))
 		{
-		  if (!line->exists)
-		    coverage->lines++;
-		  if (!line->count && block->count)
-		    coverage->lines_executed++;
+		  gcc_assert (lines[j] - fn->start_line < fn->lines.size ());
+		  line = &(fn->lines[lines[j] - fn->start_line]);
+		  line->exists = 1;
+		  if (!block->exceptional)
+		    {
+		      line->unexceptional = 1;
+		      if (block->count == 0)
+			line->has_unexecuted_block = 1;
+		    }
+		  line->count += block->count;
 		}
-	      line->exists = 1;
-	      if (!block->exceptional)
+	      else
 		{
-		  line->unexceptional = 1;
-		  if (block->count == 0)
-		    line->has_unexecuted_block = 1;
+		  gcc_assert (ln < sources[src_idx].lines.size ());
+		  line = &(sources[src_idx].lines[ln]);
+		  if (coverage)
+		    {
+		      if (!line->exists)
+			coverage->lines++;
+		      if (!line->count && block->count)
+			coverage->lines_executed++;
+		    }
+		  line->exists = 1;
+		  if (!block->exceptional)
+		    {
+		      line->unexceptional = 1;
+		      if (block->count == 0)
+			line->has_unexecuted_block = 1;
+		    }
+		  line->count += block->count;
 		}
-	      line->count += block->count;
 	    }
-	}
-      block->cycle.arc = NULL;
-      block->cycle.ident = ~0U;
-      has_any_line = true;
 
-      if (!ix || ix + 1 == fn->blocks.size ())
-	/* Entry or exit block */;
-      else if (line != NULL)
-	{
-	  line->blocks.push_back (block);
+	  has_any_line = true;
 
-	  if (flag_branches)
+	  if (!ix || ix + 1 == fn->blocks.size ())
+	    /* Entry or exit block.  */;
+	  else if (line != NULL)
 	    {
-	      arc_t *arc;
+	      line->blocks.push_back (block);
 
-	      for (arc = block->succ; arc; arc = arc->succ_next)
+	      if (flag_branches)
 		{
-		  line->branches.push_back (arc);
-		  if (coverage && !arc->is_unconditional)
-		    add_branch_counts (coverage, arc);
+		  arc_t *arc;
+
+		  for (arc = block->succ; arc; arc = arc->succ_next)
+		    line->branches.push_back (arc);
 		}
 	    }
 	}
@@ -2317,72 +2505,113 @@ add_line_counts (coverage_t *coverage, function_t *fn)
     fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name, fn->name);
 }
 
+/* Accumulate info for LINE that belongs to SRC source file.  If ADD_COVERAGE
+   is set to true, update source file summary.  */
+
+static void accumulate_line_info (line_info *line, source_info *src,
+				  bool add_coverage)
+{
+  if (add_coverage)
+    for (vector<arc_info *>::iterator it = line->branches.begin ();
+	 it != line->branches.end (); it++)
+      add_branch_counts (&src->coverage, *it);
+
+  if (!line->blocks.empty ())
+    {
+      /* The user expects the line count to be the number of times
+	 a line has been executed.  Simply summing the block count
+	 will give an artificially high number.  The Right Thing
+	 is to sum the entry counts to the graph of blocks on this
+	 line, then find the elementary cycles of the local graph
+	 and add the transition counts of those cycles.  */
+      gcov_type count = 0;
+
+      /* Cycle detection.  */
+      for (vector<block_t *>::iterator it = line->blocks.begin ();
+	   it != line->blocks.end (); it++)
+	{
+	  for (arc_t *arc = (*it)->pred; arc; arc = arc->pred_next)
+	    if (!line->has_block (arc->src))
+	      count += arc->count;
+	  for (arc_t *arc = (*it)->succ; arc; arc = arc->succ_next)
+	    arc->cs_count = arc->count;
+	}
+
+      /* Now, add the count of loops entirely on this line.  */
+      count += get_cycles_count (*line);
+      line->count = count;
+    }
+
+  if (line->exists && add_coverage)
+    {
+      src->coverage.lines++;
+      if (line->count)
+	src->coverage.lines_executed++;
+    }
+}
+
 /* Accumulate the line counts of a file.  */
 
 static void
 accumulate_line_counts (source_info *src)
 {
-  function_t *fn, *fn_p, *fn_n;
-  unsigned ix = 0;
-
-  /* Reverse the function order.  */
-  for (fn = src->functions, fn_p = NULL; fn; fn_p = fn, fn = fn_n)
+  /* First work on group functions.  */
+  for (vector<function_t *>::iterator it = src->functions.begin ();
+       it != src->functions.end (); it++)
     {
-      fn_n = fn->next_file_fn;
-      fn->next_file_fn = fn_p;
+      function_info *fn = *it;
+
+      if (fn->src != src->index || !fn->is_group)
+	continue;
+
+      for (vector<line_info>::iterator it2 = fn->lines.begin ();
+	   it2 != fn->lines.end (); it2++)
+	  {
+	    line_info *line = &(*it2);
+	    accumulate_line_info (line, src, false);
+	  }
     }
-  src->functions = fn_p;
 
-  for (vector<line_info>::reverse_iterator it = src->lines.rbegin ();
-       it != src->lines.rend (); it++)
-    {
-      line_info *line = &(*it);
-      if (!line->blocks.empty ())
-	{
-	  /* The user expects the line count to be the number of times
-	     a line has been executed. Simply summing the block count
-	     will give an artificially high number.  The Right Thing
-	     is to sum the entry counts to the graph of blocks on this
-	     line, then find the elementary cycles of the local graph
-	     and add the transition counts of those cycles.  */
-	  gcov_type count = 0;
-
-	  /* Sum the entry arcs.  */
-	  for (vector<block_t *>::iterator it = line->blocks.begin ();
-	       it != line->blocks.end (); it++)
-	    {
-	      arc_t *arc;
+  /* Work on global lines that line in source file SRC.  */
+  for (vector<line_info>::iterator it = src->lines.begin ();
+       it != src->lines.end (); it++)
+    accumulate_line_info (&(*it), src, true);
 
-	      for (arc = (*it)->pred; arc; arc = arc->pred_next)
-		if (flag_branches)
-		  add_branch_counts (&src->coverage, arc);
-	    }
+  /* If not using intermediate mode, sum lines of group functions and
+     add them to lines that live in a source file.  */
+  if (!flag_intermediate_format)
+    for (vector<function_t *>::iterator it = src->functions.begin ();
+	 it != src->functions.end (); it++)
+      {
+	function_info *fn = *it;
 
-	  /* Cycle detection.  */
-	  for (vector<block_t *>::iterator it = line->blocks.begin ();
-	       it != line->blocks.end (); it++)
-	    {
-	      for (arc_t *arc = (*it)->pred; arc; arc = arc->pred_next)
-		if (!line->has_block (arc->src))
-		  count += arc->count;
-	      for (arc_t *arc = (*it)->succ; arc; arc = arc->succ_next)
-		arc->cs_count = arc->count;
-	    }
+	if (fn->src != src->index || !fn->is_group)
+	  continue;
 
-	  /* Now, add the count of loops entirely on this line.  */
-	  count += get_cycles_count (*line);
-	  line->count = count;
-	}
+	for (unsigned i = 0; i < fn->lines.size (); i++)
+	  {
+	    line_info *fn_line = &fn->lines[i];
+	    if (fn_line->exists)
+	      {
+		unsigned ln = fn->start_line + i;
+		line_info *src_line = &src->lines[ln];
 
-      if (line->exists)
-	{
-	  src->coverage.lines++;
-	  if (line->count)
-	    src->coverage.lines_executed++;
-	}
+		if (!src_line->exists)
+		  src->coverage.lines++;
+		if (!src_line->count && fn_line->count)
+		  src->coverage.lines_executed++;
 
-      ix++;
-    }
+		src_line->count += fn_line->count;
+		src_line->exists = 1;
+
+		if (fn_line->has_unexecuted_block)
+		  src_line->has_unexecuted_block = 1;
+
+		if (fn_line->unexceptional)
+		  src_line->unexceptional = 1;
+	      }
+	  }
+      }
 }
 
 /* Output information about ARC number IX.  Returns nonzero if
@@ -2500,7 +2729,8 @@ output_line_beginning (FILE *f, bool exists, bool unexceptional,
 	      if (flag_use_colors)
 		{
 		  pad_count_string (s);
-		  s = SGR_SEQ (COLOR_BG_MAGENTA COLOR_SEPARATOR COLOR_FG_WHITE);
+		  s.insert (0, SGR_SEQ (COLOR_BG_MAGENTA
+					COLOR_SEPARATOR COLOR_FG_WHITE));
 		  s += SGR_RESET;
 		}
 	      else
@@ -2538,6 +2768,86 @@ output_line_beginning (FILE *f, bool exists, bool unexceptional,
   fprintf (f, "%s:%5u", s.c_str (), line_num);
 }
 
+static void
+print_source_line (FILE *f, const vector<const char *> &source_lines,
+		   unsigned line)
+{
+  gcc_assert (line >= 1);
+  gcc_assert (line <= source_lines.size ());
+
+  fprintf (f, ":%s\n", source_lines[line - 1]);
+}
+
+/* Output line details for LINE and print it to F file.  LINE lives on
+   LINE_NUM.  */
+
+static void
+output_line_details (FILE *f, const line_info *line, unsigned line_num)
+{
+  if (flag_all_blocks)
+    {
+      arc_t *arc;
+      int ix, jx;
+
+      ix = jx = 0;
+      for (vector<block_t *>::const_iterator it = line->blocks.begin ();
+	   it != line->blocks.end (); it++)
+	{
+	  if (!(*it)->is_call_return)
+	    {
+	      output_line_beginning (f, line->exists,
+				     (*it)->exceptional, false,
+				     (*it)->count, line_num,
+				     "%%%%%", "$$$$$");
+	      fprintf (f, "-block %2d", ix++);
+	      if (flag_verbose)
+		fprintf (f, " (BB %u)", (*it)->id);
+	      fprintf (f, "\n");
+	    }
+	  if (flag_branches)
+	    for (arc = (*it)->succ; arc; arc = arc->succ_next)
+	      jx += output_branch_count (f, jx, arc);
+	}
+    }
+  else if (flag_branches)
+    {
+      int ix;
+
+      ix = 0;
+      for (vector<arc_t *>::const_iterator it = line->branches.begin ();
+	   it != line->branches.end (); it++)
+	ix += output_branch_count (f, ix, (*it));
+    }
+}
+
+/* Output detail statistics about function FN to file F.  */
+
+static void
+output_function_details (FILE *f, const function_info *fn)
+{
+  if (!flag_branches)
+    return;
+
+  arc_t *arc = fn->blocks[EXIT_BLOCK].pred;
+  gcov_type return_count = fn->blocks[EXIT_BLOCK].count;
+  gcov_type called_count = fn->blocks[ENTRY_BLOCK].count;
+
+  for (; arc; arc = arc->pred_next)
+    if (arc->fake)
+      return_count -= arc->count;
+
+  fprintf (f, "function %s",
+	   flag_demangled_names ? fn->demangled_name : fn->name);
+  fprintf (f, " called %s",
+	   format_gcov (called_count, 0, -1));
+  fprintf (f, " returned %s",
+	   format_gcov (return_count, called_count, 0));
+  fprintf (f, " blocks executed %s",
+	   format_gcov (fn->blocks_executed, fn->blocks.size () - 2,
+			0));
+  fprintf (f, "\n");
+}
+
 /* Read in the source file one line at a time, and output that line to
    the gcov file preceded by its execution count and other
    information.  */
@@ -2546,12 +2856,10 @@ static void
 output_lines (FILE *gcov_file, const source_info *src)
 {
 #define  DEFAULT_LINE_START "        -:    0:"
+#define FN_SEPARATOR "------------------\n"
 
   FILE *source_file;
-  unsigned line_num;	/* current line number.  */
-  const line_info *line;  /* current line info ptr.  */
-  const char *retval = "";	/* status of source file reading.  */
-  function_t *fn = NULL;
+  const char *retval;
 
   fprintf (gcov_file, DEFAULT_LINE_START "Source:%s\n", src->coverage.name);
   if (!multiple_files)
@@ -2565,43 +2873,40 @@ output_lines (FILE *gcov_file, const source_info *src)
 
   source_file = fopen (src->name, "r");
   if (!source_file)
-    {
-      fnotice (stderr, "Cannot open source file %s\n", src->name);
-      retval = NULL;
-    }
+    fnotice (stderr, "Cannot open source file %s\n", src->name);
   else if (src->file_time == 0)
     fprintf (gcov_file, DEFAULT_LINE_START "Source is newer than graph\n");
 
-  if (flag_branches)
-    fn = src->functions;
+  vector<const char *> source_lines;
+  if (source_file)
+    while ((retval = read_line (source_file)) != NULL)
+      source_lines.push_back (xstrdup (retval));
 
-  for (line_num = 1, line = &src->lines[line_num];
-       line_num < src->lines.size (); line_num++, line++)
+  unsigned line_start_group = 0;
+  vector<function_t *> fns;
+
+  for (unsigned line_num = 1; line_num <= source_lines.size (); line_num++)
     {
-      for (; fn && fn->line == line_num; fn = fn->next_file_fn)
+      if (line_num >= src->lines.size ())
 	{
-	  arc_t *arc = fn->blocks[EXIT_BLOCK].pred;
-	  gcov_type return_count = fn->blocks[EXIT_BLOCK].count;
-	  gcov_type called_count = fn->blocks[ENTRY_BLOCK].count;
-
-	  for (; arc; arc = arc->pred_next)
-	    if (arc->fake)
-	      return_count -= arc->count;
-
-	  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",
-		   format_gcov (return_count, called_count, 0));
-	  fprintf (gcov_file, " blocks executed %s",
-		   format_gcov (fn->blocks_executed, fn->blocks.size () - 2,
-				0));
-	  fprintf (gcov_file, "\n");
+	  fprintf (gcov_file, "%9s:%5u", "-", line_num);
+	  print_source_line (gcov_file, source_lines, line_num);
+	  continue;
 	}
 
-      if (retval)
-	retval = read_line (source_file);
+      const line_info *line = &src->lines[line_num];
+
+      if (line_start_group == 0)
+	{
+	  fns = src->get_functions_at_location (line_num);
+	  if (fns.size () > 1)
+	    line_start_group = fns[0]->end_line;
+	  else if (fns.size () == 1)
+	    {
+	      function_t *fn = fns[0];
+	      output_function_details (gcov_file, fn);
+	    }
+	}
 
       /* For lines which don't exist in the .bb file, print '-' before
 	 the source line.  For lines which exist but were never
@@ -2610,54 +2915,64 @@ output_lines (FILE *gcov_file, const source_info *src)
 	 There are 16 spaces of indentation added before the source
 	 line so that tabs won't be messed up.  */
       output_line_beginning (gcov_file, line->exists, line->unexceptional,
-			     line->has_unexecuted_block, line->count, line_num,
-			     "=====", "#####");
-      fprintf (gcov_file, ":%s\n", retval ? retval : "/*EOF*/");
+			     line->has_unexecuted_block, line->count,
+			     line_num, "=====", "#####");
 
-      if (flag_all_blocks)
-	{
-	  arc_t *arc;
-	  int ix, jx;
+      print_source_line (gcov_file, source_lines, line_num);
+      output_line_details (gcov_file, line, line_num);
 
-	  ix = jx = 0;
-	  for (vector<block_t *>::const_iterator it = line->blocks.begin ();
-	       it != line->blocks.end (); it++)
+      if (line_start_group == line_num)
+	{
+	  for (vector<function_t *>::iterator it = fns.begin ();
+	       it != fns.end (); it++)
 	    {
-	      if (!(*it)->is_call_return)
+	      function_info *fn = *it;
+	      vector<line_info> &lines = fn->lines;
+
+	      fprintf (gcov_file, FN_SEPARATOR);
+
+	      string fn_name
+		= flag_demangled_names ? fn->demangled_name : fn->name;
+
+	      if (flag_use_colors)
 		{
+		  fn_name.insert (0, SGR_SEQ (COLOR_FG_CYAN));
+		  fn_name += SGR_RESET;
+		}
+
+	      fprintf (gcov_file, "%s:\n", fn_name.c_str ());
+
+	      output_function_details (gcov_file, fn);
+
+	      /* Print all lines covered by the function.  */
+	      for (unsigned i = 0; i < lines.size (); i++)
+		{
+		  line_info *line = &lines[i];
+		  unsigned l = fn->start_line + i;
+
+		  /* For lines which don't exist in the .bb file, print '-'
+		     before the source line.  For lines which exist but
+		     were never executed, print '#####' or '=====' before
+		     the source line.  Otherwise, print the execution count
+		     before the source line.
+		     There are 16 spaces of indentation added before the source
+		     line so that tabs won't be messed up.  */
 		  output_line_beginning (gcov_file, line->exists,
-					 (*it)->exceptional, false,
-					 (*it)->count, line_num,
-					 "%%%%%", "$$$$$");
-		  fprintf (gcov_file, "-block %2d", ix++);
-		  if (flag_verbose)
-		    fprintf (gcov_file, " (BB %u)", (*it)->id);
-		  fprintf (gcov_file, "\n");
+					 line->unexceptional,
+					 line->has_unexecuted_block,
+					 line->count,
+					 l, "=====", "#####");
+
+		  print_source_line (gcov_file, source_lines, l);
+		  output_line_details (gcov_file, line, l);
 		}
-	      if (flag_branches)
-		for (arc = (*it)->succ; arc; arc = arc->succ_next)
-		  jx += output_branch_count (gcov_file, jx, arc);
 	    }
-	}
-      else if (flag_branches)
-	{
-	  int ix;
 
-	  ix = 0;
-	  for (vector<arc_t *>::const_iterator it = line->branches.begin ();
-	       it != line->branches.end (); it++)
-	    ix += output_branch_count (gcov_file, ix, (*it));
+	  fprintf (gcov_file, FN_SEPARATOR);
+	  line_start_group = 0;
 	}
     }
 
-  /* Handle all remaining source lines.  There may be lines after the
-     last line of code.  */
-  if (retval)
-    {
-      for (; (retval = read_line (source_file)); line_num++)
-	fprintf (gcov_file, "%9s:%5u:%s\n", "-", line_num, retval);
-    }
-
   if (source_file)
     fclose (source_file);
 }
-- 
2.14.3


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 8/N][RFC][v3]: GCOV: support multiple functions per a line
  2017-11-07 10:53       ` [PATCH 8/N][RFC][v3]: " Martin Liška
@ 2017-11-07 15:09         ` Nathan Sidwell
  2017-11-08 11:42           ` Martin Liška
  0 siblings, 1 reply; 33+ messages in thread
From: Nathan Sidwell @ 2017-11-07 15:09 UTC (permalink / raw)
  To: Martin Liška, gcc-patches

On 11/07/2017 05:53 AM, Martin Liška wrote:
> Hello.
> 
> This is slightly updated version from the previous. Various small issues were fixed
> and I update documentation in order to reflect the changes.

> +  gcov_write_unsigned (DECL_ARTIFICIAL (current_function_decl));
>    gcov_write_filename (xloc.file);
>    gcov_write_unsigned (xloc.line);
> +  gcov_write_unsigned (expand_location (cfun->function_end_locus).line);
>    gcov_write_length (offset);

this is presuming the end line is in the same file as the start line.  A 
reasonable assumption, but users can have exciting ideas!  What is the 
failure mode if the function straddles a file boundary?

+  /* True when multiple functions start at a line in a source file.  */
+  unsigned is_group : 1;

Is this true for the non-template instantiation case of:

void a () {} void b () {}

What happens there?  (perhaps we cannot tell the difference?)

+  inline bool operator() (const function_info *lhs,
+			  const function_info *rhs)
+    {
+      return lhs->end_line < rhs->start_line;
+    }

This isn't stable if they start on the same line.  Will output order 
depend on the vaguaries of the sorting algorithm?

> +	  vector<line_info> &lines = (*it2)->lines;
> +	  /* Print all lines covered by the function.  */
> +	  for (unsigned i = 0; i < lines.size (); i++)

comment seems to apply to the whole block, not just the for loop? The 
lines init is merely local cache?  (this stood out because of lack of 
blank line before the comment, btw)

Otherwise looks good, and a nice improvement.  Please investigate the 
corner cases mentioned above though.

nathan
-- 
Nathan Sidwell

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 8/N][RFC][v3]: GCOV: support multiple functions per a line
  2017-11-07 15:09         ` Nathan Sidwell
@ 2017-11-08 11:42           ` Martin Liška
  2017-11-08 15:12             ` Nathan Sidwell
  0 siblings, 1 reply; 33+ messages in thread
From: Martin Liška @ 2017-11-08 11:42 UTC (permalink / raw)
  To: Nathan Sidwell, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 5616 bytes --]

On 11/07/2017 03:49 PM, Nathan Sidwell wrote:
> On 11/07/2017 05:53 AM, Martin Liška wrote:
>> Hello.
>>
>> This is slightly updated version from the previous. Various small issues were fixed
>> and I update documentation in order to reflect the changes.
> 
>> +  gcov_write_unsigned (DECL_ARTIFICIAL (current_function_decl));
>>    gcov_write_filename (xloc.file);
>>    gcov_write_unsigned (xloc.line);
>> +  gcov_write_unsigned (expand_location (cfun->function_end_locus).line);
>>    gcov_write_length (offset);
> 
> this is presuming the end line is in the same file as the start line.  A reasonable assumption, but users can have exciting ideas!  What is the failure mode if the function straddles a file boundary?

Hi.

I decided to fix that with change that set line_end = line_start if line_end < line_start.
That survives reasonably well with cases like this:

$         -:    3:template<class T>
        -:    4:class Foo
        -:    5:{
        -:    6:  public:
        1:    7:  Foo(): b (1000) {}          Foo(int _b): b (_b) {}   static void xxx() {}
------------------
Foo<char>::Foo():
        0:    7:  Foo(): b (1000) {}          Foo(int _b): b (_b) {}   static void xxx() {}
------------------
Foo<int>::Foo():
        1:    7:  Foo(): b (1000) {}          Foo(int _b): b (_b) {}   static void xxx() {}
------------------
Foo<char>::Foo(int):
        0:    7:  Foo(): b (1000) {}          Foo(int _b): b (_b) {}   static void xxx() {}
------------------
Foo<int>::Foo(int):
        0:    7:  Foo(): b (1000) {}          Foo(int _b): b (_b) {}   static void xxx() {}
------------------
Foo<char>::xxx():
        0:    7:  Foo(): b (1000) {}          Foo(int _b): b (_b) {}   static void xxx() {}
------------------
Foo<int>::xxx():
        0:    7:  Foo(): b (1000) {}          Foo(int _b): b (_b) {}   static void xxx() {}
------------------
        2:    8:  void inc () {
------------------
Foo<char>::inc():
        0:    8:  void inc () {
------------------
Foo<int>::inc():
        2:    8:  void inc () {
------------------
        2:    9:    b++;
[another gcov file]
        2:    3:b++;
        2:    4:b++;
        2:    5:b += 1;
        2:    6:}

> 
> +  /* True when multiple functions start at a line in a source file.  */
> +  unsigned is_group : 1;
> 
> Is this true for the non-template instantiation case of:
> 
> void a () {} void b () {}
> 
> What happens there?  (perhaps we cannot tell the difference?)
> 
> +  inline bool operator() (const function_info *lhs,
> +              const function_info *rhs)
> +    {
> +      return lhs->end_line < rhs->start_line;

There's typo as s/end_line/start_line.

> +    }
> 
> This isn't stable if they start on the same line.  Will output order depend on the vaguaries of the sorting algorithm?
> 
>> +      vector<line_info> &lines = (*it2)->lines;
>> +      /* Print all lines covered by the function.  */
>> +      for (unsigned i = 0; i < lines.size (); i++)

So fixed by introduction of line column that is used for sorting as well.

Now we have:

        -:    2:class Foo
        -:    3:{
        -:    4:  public:
        -:    5:  int b;
        1:    6:  Foo(): b (1000) {}
       2*:    7:  void inc () { if (b) b++; } };     static void test() {}   static void test2() {} static void test3() {} static void test4() {} static void test5() {}
------------------
Foo<int>::inc():
        1:    7:  void inc () { if (b) b++; } };     static void test() {}   static void test2() {} static void test3() {} static void test4() {} static void test5() {}
------------------
test():
    #####:    7:  void inc () { if (b) b++; } };     static void test() {}   static void test2() {} static void test3() {} static void test4() {} static void test5() {}
------------------
test2():
    #####:    7:  void inc () { if (b) b++; } };     static void test() {}   static void test2() {} static void test3() {} static void test4() {} static void test5() {}
------------------
test3():
    #####:    7:  void inc () { if (b) b++; } };     static void test() {}   static void test2() {} static void test3() {} static void test4() {} static void test5() {}
------------------
test4():
        1:    7:  void inc () { if (b) b++; } };     static void test() {}   static void test2() {} static void test3() {} static void test4() {} static void test5() {}
------------------
test5():
    #####:    7:  void inc () { if (b) b++; } };     static void test() {}   static void test2() {} static void test3() {} static void test4() {} static void test5() {}
------------------
        -:    8:
        1:    9:int main()
        -:   10:{
        1:   11:  Foo<int> xx;
        1:   12:  xx.inc();
        -:   13:
        1:   14:  test4();
        -:   15:
        1:   16:  return 0;
        -:   17:}

Which hopefully also work fine.

> 
> comment seems to apply to the whole block, not just the for loop? The lines init is merely local cache?  (this stood out because of lack of blank line before the comment, btw)

Removed it :)

> 
> Otherwise looks good, and a nice improvement.  Please investigate the corner cases mentioned above though.

Thank you.
Btw. there are people in Mozilla that have been utilizing gcov significantly:
https://marco-c.github.io/2017/07/28/code-coverage-architecture.html

They would definitely benefit from the functionality because the code base is full of heavy C++ code.

May I understand the reply as ACK?
Martin

> 
> nathan


[-- Attachment #2: 0001-GCOV-support-multiple-functions-per-a-line.patch --]
[-- Type: text/x-patch, Size: 49392 bytes --]

From 6de940856a2af8a9b1689951d45a9484e921a7ec Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Thu, 26 Oct 2017 10:39:40 +0200
Subject: [PATCH 1/8] GCOV: support multiple functions per a line

gcc/ChangeLog:

2017-11-01  Martin Liska  <mliska@suse.cz>

	* coverage.c (coverage_begin_function): Output also end locus
	of a function and information whether the function is
	artificial.
	* gcov-dump.c (tag_function): Parse and print the information.
	* gcov.c (INCLUDE_MAP): Add include.
	(INCLUDE_SET): Likewise.
	(struct line_info): Move earlier in the source file because
	of vector<line_info> in function_info structure.
	(line_info::line_info): Likewise.
	(line_info::has_block): Likewise.
	(struct source_info): Add new member index.
	(source_info::get_functions_at_location): New function.
	(function_info::group_line_p): New function.
	(output_intermediate_line): New function.
	(output_intermediate_file): Use the mentioned function.
	(struct function_start): New.
	(struct function_start_pair_hash): Likewise.
	(process_file): Add code that identifies group functions.
	Assign lines either to global or function scope.
	(generate_results): Skip artificial functions.
	(find_source): Assign index for each source file.
	(read_graph_file): Read new flag artificial and end_line.
	(add_line_counts): Assign it either to global of function scope.
	(accumulate_line_counts): Isolate core of the function to
	accumulate_line_info and call it for both function and global
	scope lines.
	(accumulate_line_info): New function.
	(output_line_beginning): Fix GNU coding style.
	(print_source_line): New function.
	(output_line_details): Likewise.
	(output_function_details): Likewise.
	(output_lines): Iterate both source (global) scope and function
	scope.
	(struct function_line_start_cmp): New class.
	* doc/gcov.texi: Reflect changes in documentation.
---
 gcc/coverage.c    |   6 +
 gcc/doc/gcov.texi | 329 +++++++++++++++------
 gcc/gcov-dump.c   |   8 +-
 gcc/gcov.c        | 855 +++++++++++++++++++++++++++++++++++++-----------------
 4 files changed, 842 insertions(+), 356 deletions(-)

diff --git a/gcc/coverage.c b/gcc/coverage.c
index 8a56a677f15..15d092f4a60 100644
--- a/gcc/coverage.c
+++ b/gcc/coverage.c
@@ -663,8 +663,14 @@ coverage_begin_function (unsigned lineno_checksum, unsigned cfg_checksum)
   gcov_write_unsigned (cfg_checksum);
   gcov_write_string (IDENTIFIER_POINTER
 		     (DECL_ASSEMBLER_NAME (current_function_decl)));
+  gcov_write_unsigned (DECL_ARTIFICIAL (current_function_decl));
   gcov_write_filename (xloc.file);
   gcov_write_unsigned (xloc.line);
+  gcov_write_unsigned (xloc.column);
+
+  /* Function can start in a single file and end in another one.  */
+  int fn_end_line = expand_location (cfun->function_end_locus).line;
+  gcov_write_unsigned (fn_end_line > xloc.line ? fn_end_line : xloc.line);
   gcov_write_length (offset);
 
   return !gcov_is_error ();
diff --git a/gcc/doc/gcov.texi b/gcc/doc/gcov.texi
index 5c4ba8a51a7..6f6a92c131a 100644
--- a/gcc/doc/gcov.texi
+++ b/gcc/doc/gcov.texi
@@ -193,7 +193,7 @@ Write counts in human readable format (like 24k).
 
 @smallexample
 file:@var{source_file_name}
-function:@var{line_number},@var{execution_count},@var{function_name}
+function:@var{start_line_number},@var{end_line_number},@var{execution_count},@var{function_name}
 lcount:@var{line number},@var{execution_count},@var{has_unexecuted_block}
 branch:@var{line_number},@var{branch_coverage_type}
 
@@ -201,24 +201,55 @@ Where the @var{branch_coverage_type} is
    notexec (Branch not executed)
    taken (Branch executed and taken)
    nottaken (Branch executed, but not taken)
+@end smallexample
 
 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
+until the next @var{file} entry.  If there are multiple functions that
+start on a single line, then corresponding lcount is repeated multiple
+times.
 
 Here is a sample when @option{-i} is used in conjunction with @option{-b} option:
 
 @smallexample
-file:array.cc
-function:11,1,_Z3sumRKSt6vectorIPiSaIS0_EE
-function:22,1,main
-lcount:11,1,0
-lcount:12,1,0
-lcount:14,1,0
-branch:14,taken
-lcount:26,1,0
-branch:28,nottaken
+file:tmp.cpp
+function:7,7,0,_ZN3FooIcEC2Ev
+function:7,7,1,_ZN3FooIiEC2Ev
+function:8,8,0,_ZN3FooIcE3incEv
+function:8,8,2,_ZN3FooIiE3incEv
+function:18,37,1,main
+lcount:7,0,1
+lcount:7,1,0
+lcount:8,0,1
+lcount:8,2,0
+lcount:18,1,0
+lcount:21,1,0
+branch:21,taken
+branch:21,nottaken
+lcount:23,1,0
+branch:23,taken
+branch:23,nottaken
+lcount:24,1,0
+branch:24,taken
+branch:24,nottaken
+lcount:25,1,0
+lcount:27,11,0
+branch:27,taken
+branch:27,taken
+lcount:28,10,0
+lcount:30,1,1
+branch:30,nottaken
+branch:30,taken
+lcount:32,1,0
+branch:32,nottaken
+branch:32,taken
+lcount:33,0,1
+branch:33,notexec
+branch:33,notexec
+lcount:35,1,0
+branch:35,taken
+branch:35,nottaken
+lcount:36,1,0
 @end smallexample
 
 @item -k
@@ -391,79 +422,158 @@ source file compiled with @option{-fprofile-arcs}, an accompanying
 
 Running @command{gcov} with your program's source file names as arguments
 will now produce a listing of the code along with frequency of execution
-for each line.  For example, if your program is called @file{tmp.c}, this
+for each line.  For example, if your program is called @file{tmp.cpp}, this
 is what you see when you use the basic @command{gcov} facility:
 
 @smallexample
-$ gcc -fprofile-arcs -ftest-coverage tmp.c
+$ g++ -fprofile-arcs -ftest-coverage tmp.cpp
 $ a.out
-$ gcov tmp.c
-File 'tmp.c'
-Lines executed:90.00% of 10
-Creating 'tmp.c.gcov'
+$ gcov tmp.cpp -m
+File 'tmp.cpp'
+Lines executed:92.86% of 14
+Creating 'tmp.cpp.gcov'
 @end smallexample
 
-The file @file{tmp.c.gcov} contains output from @command{gcov}.
+The file @file{tmp.cpp.gcov} contains output from @command{gcov}.
 Here is a sample:
 
 @smallexample
-        -:    0:Source:tmp.c
+        -:    0:Source:tmp.cpp
         -:    0:Graph:tmp.gcno
         -:    0:Data:tmp.gcda
         -:    0:Runs:1
         -:    0:Programs:1
         -:    1:#include <stdio.h>
         -:    2:
-        -:    3:int main (void)
-        1:    4:@{
-        1:    5:  int i, total;
-        -:    6:
-        1:    7:  total = 0;
-        -:    8:
-       11:    9:  for (i = 0; i < 10; i++)
-       10:   10:    total += i;
-        -:   11:
-        1:   12:  if (total != 45)
-    #####:   13:    printf ("Failure\n");
-        -:   14:  else
-        1:   15:    printf ("Success\n");
-        1:   16:  return 0;
-        -:   17:@}
+        -:    3:template<class T>
+        -:    4:class Foo
+        -:    5:@{
+        -:    6:  public:
+       1*:    7:  Foo(): b (1000) @{@}
+------------------
+Foo<char>::Foo():
+    #####:    7:  Foo(): b (1000) @{@}
+------------------
+Foo<int>::Foo():
+        1:    7:  Foo(): b (1000) @{@}
+------------------
+       2*:    8:  void inc () @{ b++; @}
+------------------
+Foo<char>::inc():
+    #####:    8:  void inc () @{ b++; @}
+------------------
+Foo<int>::inc():
+        2:    8:  void inc () @{ b++; @}
+------------------
+        -:    9:
+        -:   10:  private:
+        -:   11:  int b;
+        -:   12:@};
+        -:   13:
+        -:   14:template class Foo<int>;
+        -:   15:template class Foo<char>;
+        -:   16:
+        -:   17:int
+        1:   18:main (void)
+        -:   19:@{
+        -:   20:  int i, total;
+        1:   21:  Foo<int> counter;
+        -:   22:
+        1:   23:  counter.inc();
+        1:   24:  counter.inc();
+        1:   25:  total = 0;
+        -:   26:
+       11:   27:  for (i = 0; i < 10; i++)
+       10:   28:    total += i;
+        -:   29:
+       1*:   30:  int v = total > 100 ? 1 : 2;
+        -:   31:
+        1:   32:  if (total != 45)
+    #####:   33:    printf ("Failure\n");
+        -:   34:  else
+        1:   35:    printf ("Success\n");
+        1:   36:  return 0;
+        -:   37:@}
 @end smallexample
 
+Note that line 7 is shown in the report multiple times.  First occurrence
+presents total number of execution of the line and the next two belong
+to instances of class Foo constructors.  As you can also see, line 30 contains
+some unexecuted basic blocks and thus execution count has asterisk symbol.
+
 When you use the @option{-a} option, you will get individual block
 counts, and the output looks like this:
 
 @smallexample
-        -:    0:Source:tmp.c
+        -:    0:Source:tmp.cpp
         -:    0:Graph:tmp.gcno
         -:    0:Data:tmp.gcda
         -:    0:Runs:1
         -:    0:Programs:1
         -:    1:#include <stdio.h>
         -:    2:
-        -:    3:int main (void)
-        1:    4:@{
-        1:    4-block  0
-        1:    5:  int i, total;
-        -:    6:
-        1:    7:  total = 0;
-        -:    8:
-       11:    9:  for (i = 0; i < 10; i++)
-       11:    9-block  0
-       10:   10:    total += i;
-       10:   10-block  0
-        -:   11:
-        1:   12:  if (total != 45)
-        1:   12-block  0
-    #####:   13:    printf ("Failure\n");
-    $$$$$:   13-block  0
-        -:   14:  else
-        1:   15:    printf ("Success\n");
-        1:   15-block  0
-        1:   16:  return 0;
-        1:   16-block  0
-        -:   17:@}
+        -:    3:template<class T>
+        -:    4:class Foo
+        -:    5:@{
+        -:    6:  public:
+       1*:    7:  Foo(): b (1000) @{@}
+------------------
+Foo<char>::Foo():
+    #####:    7:  Foo(): b (1000) @{@}
+------------------
+Foo<int>::Foo():
+        1:    7:  Foo(): b (1000) @{@}
+------------------
+       2*:    8:  void inc () @{ b++; @}
+------------------
+Foo<char>::inc():
+    #####:    8:  void inc () @{ b++; @}
+------------------
+Foo<int>::inc():
+        2:    8:  void inc () @{ b++; @}
+------------------
+        -:    9:
+        -:   10:  private:
+        -:   11:  int b;
+        -:   12:@};
+        -:   13:
+        -:   14:template class Foo<int>;
+        -:   15:template class Foo<char>;
+        -:   16:
+        -:   17:int
+        1:   18:main (void)
+        -:   19:@{
+        -:   20:  int i, total;
+        1:   21:  Foo<int> counter;
+        1:   21-block  0
+        -:   22:
+        1:   23:  counter.inc();
+        1:   23-block  0
+        1:   24:  counter.inc();
+        1:   24-block  0
+        1:   25:  total = 0;
+        -:   26:
+       11:   27:  for (i = 0; i < 10; i++)
+        1:   27-block  0
+       11:   27-block  1
+       10:   28:    total += i;
+       10:   28-block  0
+        -:   29:
+       1*:   30:  int v = total > 100 ? 1 : 2;
+        1:   30-block  0
+    %%%%%:   30-block  1
+        1:   30-block  2
+        -:   31:
+        1:   32:  if (total != 45)
+        1:   32-block  0
+    #####:   33:    printf ("Failure\n");
+    %%%%%:   33-block  0
+        -:   34:  else
+        1:   35:    printf ("Success\n");
+        1:   35-block  0
+        1:   36:  return 0;
+        1:   36-block  0
+        -:   37:@}
 @end smallexample
 
 In this mode, each basic block is only shown on one line -- the last
@@ -477,53 +587,94 @@ block, the branch and call counts of the block will be shown, if the
 
 Because of the way GCC instruments calls, a call count can be shown
 after a line with no individual blocks.
-As you can see, line 13 contains a basic block that was not executed.
+As you can see, line 33 contains a basic block that was not executed.
 
 @need 450
 When you use the @option{-b} option, your output looks like this:
 
 @smallexample
-$ gcov -b tmp.c
-File 'tmp.c'
-Lines executed:90.00% of 10
-Branches executed:80.00% of 5
-Taken at least once:80.00% of 5
-Calls executed:50.00% of 2
-Creating 'tmp.c.gcov'
-@end smallexample
-
-Here is a sample of a resulting @file{tmp.c.gcov} file:
-
-@smallexample
-        -:    0:Source:tmp.c
+        -:    0:Source:tmp.cpp
         -:    0:Graph:tmp.gcno
         -:    0:Data:tmp.gcda
         -:    0:Runs:1
         -:    0:Programs:1
         -:    1:#include <stdio.h>
         -:    2:
-        -:    3:int main (void)
-function main called 1 returned 1 blocks executed 75%
-        1:    4:@{
-        1:    5:  int i, total;
-        -:    6:
-        1:    7:  total = 0;
-        -:    8:
-       11:    9:  for (i = 0; i < 10; i++)
+        -:    3:template<class T>
+        -:    4:class Foo
+        -:    5:@{
+        -:    6:  public:
+       1*:    7:  Foo(): b (1000) @{@}
+------------------
+Foo<char>::Foo():
+function Foo<char>::Foo() called 0 returned 0% blocks executed 0%
+    #####:    7:  Foo(): b (1000) @{@}
+------------------
+Foo<int>::Foo():
+function Foo<int>::Foo() called 1 returned 100% blocks executed 100%
+        1:    7:  Foo(): b (1000) @{@}
+------------------
+       2*:    8:  void inc () @{ b++; @}
+------------------
+Foo<char>::inc():
+function Foo<char>::inc() called 0 returned 0% blocks executed 0%
+    #####:    8:  void inc () @{ b++; @}
+------------------
+Foo<int>::inc():
+function Foo<int>::inc() called 2 returned 100% blocks executed 100%
+        2:    8:  void inc () @{ b++; @}
+------------------
+        -:    9:
+        -:   10:  private:
+        -:   11:  int b;
+        -:   12:@};
+        -:   13:
+        -:   14:template class Foo<int>;
+        -:   15:template class Foo<char>;
+        -:   16:
+        -:   17:int
+function main called 1 returned 100% blocks executed 81%
+        1:   18:main (void)
+        -:   19:@{
+        -:   20:  int i, total;
+        1:   21:  Foo<int> counter;
+call    0 returned 100%
+branch  1 taken 100% (fallthrough)
+branch  2 taken 0% (throw)
+        -:   22:
+        1:   23:  counter.inc();
+call    0 returned 100%
+branch  1 taken 100% (fallthrough)
+branch  2 taken 0% (throw)
+        1:   24:  counter.inc();
+call    0 returned 100%
+branch  1 taken 100% (fallthrough)
+branch  2 taken 0% (throw)
+        1:   25:  total = 0;
+        -:   26:
+       11:   27:  for (i = 0; i < 10; i++)
 branch  0 taken 91% (fallthrough)
 branch  1 taken 9%
-       10:   10:    total += i;
-        -:   11:
-        1:   12:  if (total != 45)
+       10:   28:    total += i;
+        -:   29:
+       1*:   30:  int v = total > 100 ? 1 : 2;
+branch  0 taken 0% (fallthrough)
+branch  1 taken 100%
+        -:   31:
+        1:   32:  if (total != 45)
 branch  0 taken 0% (fallthrough)
 branch  1 taken 100%
-    #####:   13:    printf ("Failure\n");
+    #####:   33:    printf ("Failure\n");
 call    0 never executed
-        -:   14:  else
-        1:   15:    printf ("Success\n");
-call    0 called 1 returned 100%
-        1:   16:  return 0;
-        -:   17:@}
+branch  1 never executed
+branch  2 never executed
+        -:   34:  else
+        1:   35:    printf ("Success\n");
+call    0 returned 100%
+branch  1 taken 100% (fallthrough)
+branch  2 taken 0% (throw)
+        1:   36:  return 0;
+        -:   37:@}
 @end smallexample
 
 For each function, a line is printed showing how many times the function
diff --git a/gcc/gcov-dump.c b/gcc/gcov-dump.c
index d24e72ac4a1..c4e05cd4795 100644
--- a/gcc/gcov-dump.c
+++ b/gcc/gcov-dump.c
@@ -308,9 +308,15 @@ tag_function (const char *filename ATTRIBUTE_UNUSED,
 	  
 	  name = gcov_read_string ();
 	  printf (", `%s'", name ? name : "NULL");
+	  unsigned artificial = gcov_read_unsigned ();
 	  name = gcov_read_string ();
 	  printf (" %s", name ? name : "NULL");
-	  printf (":%u", gcov_read_unsigned ());
+	  unsigned line_start = gcov_read_unsigned ();
+	  unsigned column_start = gcov_read_unsigned ();
+	  unsigned line_end = gcov_read_unsigned ();
+	  printf (":%u:%u:%u", line_start, column_start, line_end);
+	  if (artificial)
+	    printf (", artificial");
 	}
     }
 }
diff --git a/gcc/gcov.c b/gcc/gcov.c
index 48bcdc0d4c3..846a2326196 100644
--- a/gcc/gcov.c
+++ b/gcc/gcov.c
@@ -34,6 +34,8 @@ along with Gcov; see the file COPYING3.  If not see
 #define INCLUDE_ALGORITHM
 #define INCLUDE_VECTOR
 #define INCLUDE_STRING
+#define INCLUDE_MAP
+#define INCLUDE_SET
 #include "system.h"
 #include "coretypes.h"
 #include "tm.h"
@@ -183,6 +185,42 @@ block_info::block_info (): succ (NULL), pred (NULL), num_succ (0), num_pred (0),
   cycle.arc = NULL;
 }
 
+/* Describes a single line of source.  Contains a chain of basic blocks
+   with code on it.  */
+
+struct line_info
+{
+  /* Default constructor.  */
+  line_info ();
+
+  /* Return true when NEEDLE is one of basic blocks the line belongs to.  */
+  bool has_block (block_t *needle);
+
+  /* Execution count.  */
+  gcov_type count;
+
+  /* Branches from blocks that end on this line.  */
+  vector<arc_t *> branches;
+
+  /* blocks which start on this line.  Used in all-blocks mode.  */
+  vector<block_t *> blocks;
+
+  unsigned exists : 1;
+  unsigned unexceptional : 1;
+  unsigned has_unexecuted_block : 1;
+};
+
+line_info::line_info (): count (0), branches (), blocks (), exists (false),
+  unexceptional (0), has_unexecuted_block (0)
+{
+}
+
+bool
+line_info::has_block (block_t *needle)
+{
+  return std::find (blocks.begin (), blocks.end (), needle) != blocks.end ();
+}
+
 /* Describes a single function. Contains an array of basic blocks.  */
 
 typedef struct function_info
@@ -190,6 +228,10 @@ typedef struct function_info
   function_info ();
   ~function_info ();
 
+  /* Return true when line N belongs to the function in source file SRC_IDX.
+     The line must be defined in body of the function, can't be inlined.  */
+  bool group_line_p (unsigned n, unsigned src_idx);
+
   /* Name of function.  */
   char *name;
   char *demangled_name;
@@ -200,6 +242,13 @@ typedef struct function_info
   /* The graph contains at least one fake incoming edge.  */
   unsigned has_catch : 1;
 
+  /* True when the function is artificial and does not exist
+     in a source file.  */
+  unsigned artificial : 1;
+
+  /* True when multiple functions start at a line in a source file.  */
+  unsigned is_group : 1;
+
   /* Array of basic blocks.  Like in GCC, the entry block is
      at blocks[0] and the exit block is at blocks[1].  */
 #define ENTRY_BLOCK (0)
@@ -211,17 +260,39 @@ typedef struct function_info
   gcov_type *counts;
   unsigned num_counts;
 
-  /* First line number & file.  */
-  unsigned line;
+  /* First line number.  */
+  unsigned start_line;
+
+  /* First line column.  */
+  unsigned start_column;
+
+  /* Last line number.  */
+  unsigned end_line;
+
+  /* Index of source file where the function is defined.  */
   unsigned src;
 
-  /* Next function in same source file.  */
-  struct function_info *next_file_fn;
+  /* Vector of line information.  */
+  vector<line_info> lines;
 
   /* Next function.  */
   struct function_info *next;
 } function_t;
 
+/* Function info comparer that will sort functions according to starting
+   line.  */
+
+struct function_line_start_cmp
+{
+  inline bool operator() (const function_info *lhs,
+			  const function_info *rhs)
+    {
+      return (lhs->start_line == rhs->start_line
+	      ? lhs->start_column < rhs->start_column
+	      : lhs->start_line < rhs->start_line);
+    }
+};
+
 /* Describes coverage of a file or function.  */
 
 typedef struct coverage_info
@@ -239,42 +310,6 @@ typedef struct coverage_info
   char *name;
 } coverage_t;
 
-/* Describes a single line of source. Contains a chain of basic blocks
-   with code on it.  */
-
-struct line_info
-{
-  /* Default constructor.  */
-  line_info ();
-
-  /* Return true when NEEDLE is one of basic blocks the line belongs to.  */
-  bool has_block (block_t *needle);
-
-  /* Execution count.  */
-  gcov_type count;
-
-  /* Branches from blocks that end on this line.  */
-  vector<arc_t *> branches;
-
-  /* blocks which start on this line.  Used in all-blocks mode.  */
-  vector<block_t *> blocks;
-
-  unsigned exists : 1;
-  unsigned unexceptional : 1;
-  unsigned has_unexecuted_block : 1;
-};
-
-line_info::line_info (): count (0), branches (), blocks (), exists (false),
-  unexceptional (0), has_unexecuted_block (0)
-{
-}
-
-bool
-line_info::has_block (block_t *needle)
-{
-  return std::find (blocks.begin (), blocks.end (), needle) != blocks.end ();
-}
-
 /* Describes a file mentioned in the block graph.  Contains an array
    of line info.  */
 
@@ -283,6 +318,11 @@ struct source_info
   /* Default constructor.  */
   source_info ();
 
+  vector<function_t *> get_functions_at_location (unsigned line_num) const;
+
+  /* Index of the source_info in sources vector.  */
+  unsigned index;
+
   /* Canonical name of source file.  */
   char *name;
   time_t file_time;
@@ -294,14 +334,31 @@ struct source_info
 
   /* Functions in this source file.  These are in ascending line
      number order.  */
-  function_t *functions;
+  vector <function_t *> functions;
 };
 
-source_info::source_info (): name (NULL), file_time (), lines (),
-  coverage (), functions (NULL)
+source_info::source_info (): index (0), name (NULL), file_time (),
+  lines (), coverage (), functions ()
 {
 }
 
+vector<function_t *>
+source_info::get_functions_at_location (unsigned line_num) const
+{
+  vector<function_t *> r;
+
+  for (vector<function_t *>::const_iterator it = functions.begin ();
+       it != functions.end (); it++)
+    {
+      if ((*it)->start_line == line_num && (*it)->src == index)
+	r.push_back (*it);
+    }
+
+  std::sort (r.begin (), r.end (), function_line_start_cmp ());
+
+  return r;
+}
+
 class name_map
 {
 public:
@@ -495,8 +552,9 @@ extern int main (int, char **);
 
 function_info::function_info (): name (NULL), demangled_name (NULL),
   ident (0), lineno_checksum (0), cfg_checksum (0), has_catch (0),
+  artificial (0), is_group (0),
   blocks (), blocks_executed (0), counts (NULL), num_counts (0),
-  line (0), src (0), next_file_fn (NULL), next (NULL)
+  start_line (0), start_column (0), end_line (0), src (0), lines (), next (NULL)
 {
 }
 
@@ -518,6 +576,11 @@ function_info::~function_info ()
   free (name);
 }
 
+bool function_info::group_line_p (unsigned n, unsigned src_idx)
+{
+  return is_group && src == src_idx && start_line <= n && n <= end_line;
+}
+
 /* Cycle detection!
    There are a bajillion algorithms that do this.  Boost's function is named
    hawick_cycles, so I used the algorithm by K. A. Hawick and H. A. James in
@@ -889,6 +952,42 @@ process_args (int argc, char **argv)
   return optind;
 }
 
+/* Output intermediate LINE sitting on LINE_NUM to output file F.  */
+
+static void
+output_intermediate_line (FILE *f, line_info *line, unsigned line_num)
+{
+  if (!line->exists)
+    return;
+
+  fprintf (f, "lcount:%u,%s,%d\n", line_num,
+	   format_gcov (line->count, 0, -1),
+	   line->has_unexecuted_block);
+
+  vector<arc_t *>::const_iterator it;
+  if (flag_branches)
+    for (it = line->branches.begin (); it != line->branches.end ();
+	 it++)
+      {
+	if (!(*it)->is_unconditional && !(*it)->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 ((*it)->src->count)
+		 branch_type
+			= ((*it)->count > 0) ? "taken" : "nottaken";
+	    else
+	      branch_type = "notexec";
+	    fprintf (f, "branch:%d,%s\n", line_num, branch_type);
+	  }
+      }
+}
+
 /* Output the result in intermediate format used by 'lcov'.
 
 The intermediate format contains a single file named 'foo.cc.gcov',
@@ -902,50 +1001,95 @@ file 'foo.cc.gcov' similar to the above example. */
 static void
 output_intermediate_file (FILE *gcov_file, source_info *src)
 {
-  unsigned line_num;    /* current line number.  */
-  const line_info *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->next_file_fn)
+  std::sort (src->functions.begin (), src->functions.end (),
+	     function_line_start_cmp ());
+  for (vector<function_t *>::iterator it = src->functions.begin ();
+       it != src->functions.end (); it++)
     {
       /* 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);
+      fprintf (gcov_file, "function:%d,%d,%s,%s\n", (*it)->start_line,
+	       (*it)->end_line, format_gcov ((*it)->blocks[0].count, 0, -1),
+	       flag_demangled_names ? (*it)->demangled_name : (*it)->name);
     }
 
-  for (line_num = 1, line = &src->lines[line_num];
-       line_num < src->lines.size ();
-       line_num++, line++)
+  for (unsigned line_num = 0; line_num <= src->lines.size (); line_num++)
     {
-      if (line->exists)
-	fprintf (gcov_file, "lcount:%u,%s,%d\n", line_num,
-		 format_gcov (line->count, 0, -1), line->has_unexecuted_block);
-      if (flag_branches)
-	for (vector<arc_t *>::const_iterator it = line->branches.begin ();
-	     it != line->branches.end (); it++)
-	  {
-	    if (!(*it)->is_unconditional && !(*it)->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 ((*it)->src->count)
-		  branch_type = ((*it)->count > 0) ? "taken" : "nottaken";
-		else
-		  branch_type = "notexec";
-		fprintf (gcov_file, "branch:%d,%s\n", line_num, branch_type);
-	      }
-	  }
+      vector<function_t *> fns = src->get_functions_at_location (line_num);
+
+      /* Print first group functions that begin on the line.  */
+      for (vector<function_t *>::iterator it2 = fns.begin ();
+	   it2 != fns.end (); it2++)
+	{
+	  vector<line_info> &lines = (*it2)->lines;
+	  for (unsigned i = 0; i < lines.size (); i++)
+	    {
+	      line_info *line = &lines[i];
+	      output_intermediate_line (gcov_file, line, line_num + i);
+	    }
+	}
+
+      /* Follow with lines associated with the source file.  */
+      output_intermediate_line (gcov_file, &src->lines[line_num], line_num);
     }
 }
 
+/* Function start pair.  */
+struct function_start
+{
+  unsigned source_file_idx;
+  unsigned start_line;
+};
+
+/* Traits class for function start hash maps below.  */
+
+struct function_start_pair_hash : typed_noop_remove <function_start>
+{
+  typedef function_start value_type;
+  typedef function_start compare_type;
+
+  static hashval_t
+  hash (const function_start &ref)
+  {
+    inchash::hash hstate (0);
+    hstate.add_int (ref.source_file_idx);
+    hstate.add_int (ref.start_line);
+    return hstate.end ();
+  }
+
+  static bool
+  equal (const function_start &ref1, const function_start &ref2)
+  {
+    return (ref1.source_file_idx == ref2.source_file_idx
+	    && ref1.start_line == ref2.start_line);
+  }
+
+  static void
+  mark_deleted (function_start &ref)
+  {
+    ref.start_line = ~1U;
+  }
+
+  static void
+  mark_empty (function_start &ref)
+  {
+    ref.start_line = ~2U;
+  }
+
+  static bool
+  is_deleted (const function_start &ref)
+  {
+    return ref.start_line == ~1U;
+  }
+
+  static bool
+  is_empty (const function_start &ref)
+  {
+    return ref.start_line == ~2U;
+  }
+};
+
 /* Process a single input file.  */
 
 static void
@@ -959,6 +1103,28 @@ process_file (const char *file_name)
     return;
 
   read_count_file (fns);
+
+  hash_map<function_start_pair_hash, function_t *> fn_map;
+
+  /* Identify group functions.  */
+  for (function_t *f = fns; f; f = f->next)
+    if (!f->artificial)
+      {
+	function_start needle;
+	needle.source_file_idx = f->src;
+	needle.start_line = f->start_line;
+
+	function_t **slot = fn_map.get (needle);
+	if (slot)
+	  {
+	    gcc_assert ((*slot)->end_line == f->end_line);
+	    (*slot)->is_group = 1;
+	    f->is_group = 1;
+	  }
+	else
+	  fn_map.put (needle, f);
+      }
+
   while (fns)
     {
       function_t *fn = fns;
@@ -968,46 +1134,50 @@ process_file (const char *file_name)
       if (fn->counts || no_data_file)
 	{
 	  unsigned src = fn->src;
-	  unsigned line = fn->line;
 	  unsigned block_no;
-	  function_t *probe, **prev;
-
-	  /* Now insert it into the source file's list of
-	     functions. Normally functions will be encountered in
-	     ascending order, so a simple scan is quick.  Note we're
-	     building this list in reverse order.  */
-	  for (prev = &sources[src].functions;
-	       (probe = *prev); prev = &probe->next_file_fn)
-	    if (probe->line <= line)
-	      break;
-	  fn->next_file_fn = probe;
-	  *prev = fn;
 
-	  /* Mark last line in files touched by function.  */
-	  for (block_no = 0; block_no != fn->blocks.size (); block_no++)
+	  /* Process only non-artificial functions.  */
+	  if (!fn->artificial)
 	    {
-	      block_t *block = &fn->blocks[block_no];
-	      for (unsigned i = 0; i < block->locations.size (); i++)
-		{
-		  unsigned s = block->locations[i].source_file_idx;
-
-		  /* Sort lines of locations.  */
-		  sort (block->locations[i].lines.begin (),
-			block->locations[i].lines.end ());
+	      source_info *s = &sources[src];
+	      s->functions.push_back (fn);
 
-		  if (!block->locations[i].lines.empty ())
+	      /* Mark last line in files touched by function.  */
+	      for (block_no = 0; block_no != fn->blocks.size (); block_no++)
+		{
+		  block_t *block = &fn->blocks[block_no];
+		  for (unsigned i = 0; i < block->locations.size (); i++)
 		    {
-		      unsigned last_line
-			= block->locations[i].lines.back () + 1;
-		      if (last_line > sources[s].lines.size ())
-			sources[s].lines.resize (last_line);
+		      /* Sort lines of locations.  */
+		      sort (block->locations[i].lines.begin (),
+			    block->locations[i].lines.end ());
+
+		      if (!block->locations[i].lines.empty ())
+			{
+			  s = &sources[block->locations[i].source_file_idx];
+			  unsigned last_line
+			    = block->locations[i].lines.back ();
+
+			  /* Record new lines for the function.  */
+			  if (last_line >= s->lines.size ())
+			    {
+			      /* Record new lines for a source file.  */
+			      s->lines.resize (last_line + 1);
+			    }
+			}
 		    }
+
+		  /* Allocate lines for group function, following start_line
+		     and end_line information of the function.  */
+		  if (fn->is_group)
+		    fn->lines.resize (fn->end_line - fn->start_line + 1);
 		}
+
+	      solve_flow_graph (fn);
+	      if (fn->has_catch)
+		find_exception_blocks (fn);
 	    }
 
-	  solve_flow_graph (fn);
-	  if (fn->has_catch)
-	    find_exception_blocks (fn);
 	  *fn_end = fn;
 	  fn_end = &fn->next;
 	}
@@ -1057,6 +1227,8 @@ generate_results (const char *file_name)
   for (fn = functions; fn; fn = fn->next)
     {
       coverage_t coverage;
+      if (fn->artificial)
+	continue;
 
       memset (&coverage, 0, sizeof (coverage));
       coverage.name = flag_demangled_names ? fn->demangled_name : fn->name;
@@ -1237,6 +1409,7 @@ find_source (const char *file_name)
       src = &sources.back ();
       src->name = canon;
       src->coverage.name = src->name;
+      src->index = idx;
       if (source_length
 #if HAVE_DOS_BASED_FILE_SYSTEM
 	  /* You lose if separators don't match exactly in the
@@ -1328,15 +1501,18 @@ read_graph_file (void)
       if (tag == GCOV_TAG_FUNCTION)
 	{
 	  char *function_name;
-	  unsigned ident, lineno;
+	  unsigned ident;
 	  unsigned lineno_checksum, cfg_checksum;
 
 	  ident = gcov_read_unsigned ();
 	  lineno_checksum = gcov_read_unsigned ();
 	  cfg_checksum = gcov_read_unsigned ();
 	  function_name = xstrdup (gcov_read_string ());
+	  unsigned artificial = gcov_read_unsigned ();
 	  unsigned src_idx = find_source (gcov_read_string ());
-	  lineno = gcov_read_unsigned ();
+	  unsigned start_line = gcov_read_unsigned ();
+	  unsigned start_column = gcov_read_unsigned ();
+	  unsigned end_line = gcov_read_unsigned ();
 
 	  fn = new function_t;
 	  fn->name = function_name;
@@ -1350,9 +1526,11 @@ read_graph_file (void)
 	  fn->lineno_checksum = lineno_checksum;
 	  fn->cfg_checksum = cfg_checksum;
 	  fn->src = src_idx;
-	  fn->line = lineno;
+	  fn->start_line = start_line;
+	  fn->start_column = start_column;
+	  fn->end_line = end_line;
+	  fn->artificial = artificial;
 
-	  fn->next_file_fn = NULL;
 	  fn->next = NULL;
 	  *fns_end = fn;
 	  fns_end = &fn->next;
@@ -2266,48 +2444,66 @@ add_line_counts (coverage_t *coverage, function_t *fn)
 	fn->blocks_executed++;
       for (unsigned i = 0; i < block->locations.size (); i++)
 	{
-	  source_info *src = &sources[block->locations[i].source_file_idx];
-
+	  unsigned src_idx = block->locations[i].source_file_idx;
 	  vector<unsigned> &lines = block->locations[i].lines;
+
+	  block->cycle.arc = NULL;
+	  block->cycle.ident = ~0U;
+
 	  for (unsigned j = 0; j < lines.size (); j++)
 	    {
-	      line = &src->lines[lines[j]];
-	      if (coverage)
+	      unsigned ln = lines[j];
+
+	      /* Line belongs to a function that is in a group.  */
+	      if (fn->group_line_p (ln, src_idx))
 		{
-		  if (!line->exists)
-		    coverage->lines++;
-		  if (!line->count && block->count)
-		    coverage->lines_executed++;
+		  gcc_assert (lines[j] - fn->start_line < fn->lines.size ());
+		  line = &(fn->lines[lines[j] - fn->start_line]);
+		  line->exists = 1;
+		  if (!block->exceptional)
+		    {
+		      line->unexceptional = 1;
+		      if (block->count == 0)
+			line->has_unexecuted_block = 1;
+		    }
+		  line->count += block->count;
 		}
-	      line->exists = 1;
-	      if (!block->exceptional)
+	      else
 		{
-		  line->unexceptional = 1;
-		  if (block->count == 0)
-		    line->has_unexecuted_block = 1;
+		  gcc_assert (ln < sources[src_idx].lines.size ());
+		  line = &(sources[src_idx].lines[ln]);
+		  if (coverage)
+		    {
+		      if (!line->exists)
+			coverage->lines++;
+		      if (!line->count && block->count)
+			coverage->lines_executed++;
+		    }
+		  line->exists = 1;
+		  if (!block->exceptional)
+		    {
+		      line->unexceptional = 1;
+		      if (block->count == 0)
+			line->has_unexecuted_block = 1;
+		    }
+		  line->count += block->count;
 		}
-	      line->count += block->count;
 	    }
-	}
-      block->cycle.arc = NULL;
-      block->cycle.ident = ~0U;
-      has_any_line = true;
 
-      if (!ix || ix + 1 == fn->blocks.size ())
-	/* Entry or exit block */;
-      else if (line != NULL)
-	{
-	  line->blocks.push_back (block);
+	  has_any_line = true;
 
-	  if (flag_branches)
+	  if (!ix || ix + 1 == fn->blocks.size ())
+	    /* Entry or exit block.  */;
+	  else if (line != NULL)
 	    {
-	      arc_t *arc;
+	      line->blocks.push_back (block);
 
-	      for (arc = block->succ; arc; arc = arc->succ_next)
+	      if (flag_branches)
 		{
-		  line->branches.push_back (arc);
-		  if (coverage && !arc->is_unconditional)
-		    add_branch_counts (coverage, arc);
+		  arc_t *arc;
+
+		  for (arc = block->succ; arc; arc = arc->succ_next)
+		    line->branches.push_back (arc);
 		}
 	    }
 	}
@@ -2317,72 +2513,113 @@ add_line_counts (coverage_t *coverage, function_t *fn)
     fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name, fn->name);
 }
 
+/* Accumulate info for LINE that belongs to SRC source file.  If ADD_COVERAGE
+   is set to true, update source file summary.  */
+
+static void accumulate_line_info (line_info *line, source_info *src,
+				  bool add_coverage)
+{
+  if (add_coverage)
+    for (vector<arc_info *>::iterator it = line->branches.begin ();
+	 it != line->branches.end (); it++)
+      add_branch_counts (&src->coverage, *it);
+
+  if (!line->blocks.empty ())
+    {
+      /* The user expects the line count to be the number of times
+	 a line has been executed.  Simply summing the block count
+	 will give an artificially high number.  The Right Thing
+	 is to sum the entry counts to the graph of blocks on this
+	 line, then find the elementary cycles of the local graph
+	 and add the transition counts of those cycles.  */
+      gcov_type count = 0;
+
+      /* Cycle detection.  */
+      for (vector<block_t *>::iterator it = line->blocks.begin ();
+	   it != line->blocks.end (); it++)
+	{
+	  for (arc_t *arc = (*it)->pred; arc; arc = arc->pred_next)
+	    if (!line->has_block (arc->src))
+	      count += arc->count;
+	  for (arc_t *arc = (*it)->succ; arc; arc = arc->succ_next)
+	    arc->cs_count = arc->count;
+	}
+
+      /* Now, add the count of loops entirely on this line.  */
+      count += get_cycles_count (*line);
+      line->count = count;
+    }
+
+  if (line->exists && add_coverage)
+    {
+      src->coverage.lines++;
+      if (line->count)
+	src->coverage.lines_executed++;
+    }
+}
+
 /* Accumulate the line counts of a file.  */
 
 static void
 accumulate_line_counts (source_info *src)
 {
-  function_t *fn, *fn_p, *fn_n;
-  unsigned ix = 0;
-
-  /* Reverse the function order.  */
-  for (fn = src->functions, fn_p = NULL; fn; fn_p = fn, fn = fn_n)
+  /* First work on group functions.  */
+  for (vector<function_t *>::iterator it = src->functions.begin ();
+       it != src->functions.end (); it++)
     {
-      fn_n = fn->next_file_fn;
-      fn->next_file_fn = fn_p;
+      function_info *fn = *it;
+
+      if (fn->src != src->index || !fn->is_group)
+	continue;
+
+      for (vector<line_info>::iterator it2 = fn->lines.begin ();
+	   it2 != fn->lines.end (); it2++)
+	  {
+	    line_info *line = &(*it2);
+	    accumulate_line_info (line, src, false);
+	  }
     }
-  src->functions = fn_p;
 
-  for (vector<line_info>::reverse_iterator it = src->lines.rbegin ();
-       it != src->lines.rend (); it++)
-    {
-      line_info *line = &(*it);
-      if (!line->blocks.empty ())
-	{
-	  /* The user expects the line count to be the number of times
-	     a line has been executed. Simply summing the block count
-	     will give an artificially high number.  The Right Thing
-	     is to sum the entry counts to the graph of blocks on this
-	     line, then find the elementary cycles of the local graph
-	     and add the transition counts of those cycles.  */
-	  gcov_type count = 0;
-
-	  /* Sum the entry arcs.  */
-	  for (vector<block_t *>::iterator it = line->blocks.begin ();
-	       it != line->blocks.end (); it++)
-	    {
-	      arc_t *arc;
+  /* Work on global lines that line in source file SRC.  */
+  for (vector<line_info>::iterator it = src->lines.begin ();
+       it != src->lines.end (); it++)
+    accumulate_line_info (&(*it), src, true);
 
-	      for (arc = (*it)->pred; arc; arc = arc->pred_next)
-		if (flag_branches)
-		  add_branch_counts (&src->coverage, arc);
-	    }
+  /* If not using intermediate mode, sum lines of group functions and
+     add them to lines that live in a source file.  */
+  if (!flag_intermediate_format)
+    for (vector<function_t *>::iterator it = src->functions.begin ();
+	 it != src->functions.end (); it++)
+      {
+	function_info *fn = *it;
 
-	  /* Cycle detection.  */
-	  for (vector<block_t *>::iterator it = line->blocks.begin ();
-	       it != line->blocks.end (); it++)
-	    {
-	      for (arc_t *arc = (*it)->pred; arc; arc = arc->pred_next)
-		if (!line->has_block (arc->src))
-		  count += arc->count;
-	      for (arc_t *arc = (*it)->succ; arc; arc = arc->succ_next)
-		arc->cs_count = arc->count;
-	    }
+	if (fn->src != src->index || !fn->is_group)
+	  continue;
 
-	  /* Now, add the count of loops entirely on this line.  */
-	  count += get_cycles_count (*line);
-	  line->count = count;
-	}
+	for (unsigned i = 0; i < fn->lines.size (); i++)
+	  {
+	    line_info *fn_line = &fn->lines[i];
+	    if (fn_line->exists)
+	      {
+		unsigned ln = fn->start_line + i;
+		line_info *src_line = &src->lines[ln];
 
-      if (line->exists)
-	{
-	  src->coverage.lines++;
-	  if (line->count)
-	    src->coverage.lines_executed++;
-	}
+		if (!src_line->exists)
+		  src->coverage.lines++;
+		if (!src_line->count && fn_line->count)
+		  src->coverage.lines_executed++;
 
-      ix++;
-    }
+		src_line->count += fn_line->count;
+		src_line->exists = 1;
+
+		if (fn_line->has_unexecuted_block)
+		  src_line->has_unexecuted_block = 1;
+
+		if (fn_line->unexceptional)
+		  src_line->unexceptional = 1;
+	      }
+	  }
+      }
 }
 
 /* Output information about ARC number IX.  Returns nonzero if
@@ -2500,7 +2737,8 @@ output_line_beginning (FILE *f, bool exists, bool unexceptional,
 	      if (flag_use_colors)
 		{
 		  pad_count_string (s);
-		  s = SGR_SEQ (COLOR_BG_MAGENTA COLOR_SEPARATOR COLOR_FG_WHITE);
+		  s.insert (0, SGR_SEQ (COLOR_BG_MAGENTA
+					COLOR_SEPARATOR COLOR_FG_WHITE));
 		  s += SGR_RESET;
 		}
 	      else
@@ -2538,6 +2776,86 @@ output_line_beginning (FILE *f, bool exists, bool unexceptional,
   fprintf (f, "%s:%5u", s.c_str (), line_num);
 }
 
+static void
+print_source_line (FILE *f, const vector<const char *> &source_lines,
+		   unsigned line)
+{
+  gcc_assert (line >= 1);
+  gcc_assert (line <= source_lines.size ());
+
+  fprintf (f, ":%s\n", source_lines[line - 1]);
+}
+
+/* Output line details for LINE and print it to F file.  LINE lives on
+   LINE_NUM.  */
+
+static void
+output_line_details (FILE *f, const line_info *line, unsigned line_num)
+{
+  if (flag_all_blocks)
+    {
+      arc_t *arc;
+      int ix, jx;
+
+      ix = jx = 0;
+      for (vector<block_t *>::const_iterator it = line->blocks.begin ();
+	   it != line->blocks.end (); it++)
+	{
+	  if (!(*it)->is_call_return)
+	    {
+	      output_line_beginning (f, line->exists,
+				     (*it)->exceptional, false,
+				     (*it)->count, line_num,
+				     "%%%%%", "$$$$$");
+	      fprintf (f, "-block %2d", ix++);
+	      if (flag_verbose)
+		fprintf (f, " (BB %u)", (*it)->id);
+	      fprintf (f, "\n");
+	    }
+	  if (flag_branches)
+	    for (arc = (*it)->succ; arc; arc = arc->succ_next)
+	      jx += output_branch_count (f, jx, arc);
+	}
+    }
+  else if (flag_branches)
+    {
+      int ix;
+
+      ix = 0;
+      for (vector<arc_t *>::const_iterator it = line->branches.begin ();
+	   it != line->branches.end (); it++)
+	ix += output_branch_count (f, ix, (*it));
+    }
+}
+
+/* Output detail statistics about function FN to file F.  */
+
+static void
+output_function_details (FILE *f, const function_info *fn)
+{
+  if (!flag_branches)
+    return;
+
+  arc_t *arc = fn->blocks[EXIT_BLOCK].pred;
+  gcov_type return_count = fn->blocks[EXIT_BLOCK].count;
+  gcov_type called_count = fn->blocks[ENTRY_BLOCK].count;
+
+  for (; arc; arc = arc->pred_next)
+    if (arc->fake)
+      return_count -= arc->count;
+
+  fprintf (f, "function %s",
+	   flag_demangled_names ? fn->demangled_name : fn->name);
+  fprintf (f, " called %s",
+	   format_gcov (called_count, 0, -1));
+  fprintf (f, " returned %s",
+	   format_gcov (return_count, called_count, 0));
+  fprintf (f, " blocks executed %s",
+	   format_gcov (fn->blocks_executed, fn->blocks.size () - 2,
+			0));
+  fprintf (f, "\n");
+}
+
 /* Read in the source file one line at a time, and output that line to
    the gcov file preceded by its execution count and other
    information.  */
@@ -2546,12 +2864,10 @@ static void
 output_lines (FILE *gcov_file, const source_info *src)
 {
 #define  DEFAULT_LINE_START "        -:    0:"
+#define FN_SEPARATOR "------------------\n"
 
   FILE *source_file;
-  unsigned line_num;	/* current line number.  */
-  const line_info *line;  /* current line info ptr.  */
-  const char *retval = "";	/* status of source file reading.  */
-  function_t *fn = NULL;
+  const char *retval;
 
   fprintf (gcov_file, DEFAULT_LINE_START "Source:%s\n", src->coverage.name);
   if (!multiple_files)
@@ -2565,43 +2881,40 @@ output_lines (FILE *gcov_file, const source_info *src)
 
   source_file = fopen (src->name, "r");
   if (!source_file)
-    {
-      fnotice (stderr, "Cannot open source file %s\n", src->name);
-      retval = NULL;
-    }
+    fnotice (stderr, "Cannot open source file %s\n", src->name);
   else if (src->file_time == 0)
     fprintf (gcov_file, DEFAULT_LINE_START "Source is newer than graph\n");
 
-  if (flag_branches)
-    fn = src->functions;
+  vector<const char *> source_lines;
+  if (source_file)
+    while ((retval = read_line (source_file)) != NULL)
+      source_lines.push_back (xstrdup (retval));
 
-  for (line_num = 1, line = &src->lines[line_num];
-       line_num < src->lines.size (); line_num++, line++)
+  unsigned line_start_group = 0;
+  vector<function_t *> fns;
+
+  for (unsigned line_num = 1; line_num <= source_lines.size (); line_num++)
     {
-      for (; fn && fn->line == line_num; fn = fn->next_file_fn)
+      if (line_num >= src->lines.size ())
 	{
-	  arc_t *arc = fn->blocks[EXIT_BLOCK].pred;
-	  gcov_type return_count = fn->blocks[EXIT_BLOCK].count;
-	  gcov_type called_count = fn->blocks[ENTRY_BLOCK].count;
-
-	  for (; arc; arc = arc->pred_next)
-	    if (arc->fake)
-	      return_count -= arc->count;
-
-	  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",
-		   format_gcov (return_count, called_count, 0));
-	  fprintf (gcov_file, " blocks executed %s",
-		   format_gcov (fn->blocks_executed, fn->blocks.size () - 2,
-				0));
-	  fprintf (gcov_file, "\n");
+	  fprintf (gcov_file, "%9s:%5u", "-", line_num);
+	  print_source_line (gcov_file, source_lines, line_num);
+	  continue;
 	}
 
-      if (retval)
-	retval = read_line (source_file);
+      const line_info *line = &src->lines[line_num];
+
+      if (line_start_group == 0)
+	{
+	  fns = src->get_functions_at_location (line_num);
+	  if (fns.size () > 1)
+	    line_start_group = fns[0]->end_line;
+	  else if (fns.size () == 1)
+	    {
+	      function_t *fn = fns[0];
+	      output_function_details (gcov_file, fn);
+	    }
+	}
 
       /* For lines which don't exist in the .bb file, print '-' before
 	 the source line.  For lines which exist but were never
@@ -2610,54 +2923,64 @@ output_lines (FILE *gcov_file, const source_info *src)
 	 There are 16 spaces of indentation added before the source
 	 line so that tabs won't be messed up.  */
       output_line_beginning (gcov_file, line->exists, line->unexceptional,
-			     line->has_unexecuted_block, line->count, line_num,
-			     "=====", "#####");
-      fprintf (gcov_file, ":%s\n", retval ? retval : "/*EOF*/");
+			     line->has_unexecuted_block, line->count,
+			     line_num, "=====", "#####");
 
-      if (flag_all_blocks)
-	{
-	  arc_t *arc;
-	  int ix, jx;
+      print_source_line (gcov_file, source_lines, line_num);
+      output_line_details (gcov_file, line, line_num);
 
-	  ix = jx = 0;
-	  for (vector<block_t *>::const_iterator it = line->blocks.begin ();
-	       it != line->blocks.end (); it++)
+      if (line_start_group == line_num)
+	{
+	  for (vector<function_t *>::iterator it = fns.begin ();
+	       it != fns.end (); it++)
 	    {
-	      if (!(*it)->is_call_return)
+	      function_info *fn = *it;
+	      vector<line_info> &lines = fn->lines;
+
+	      fprintf (gcov_file, FN_SEPARATOR);
+
+	      string fn_name
+		= flag_demangled_names ? fn->demangled_name : fn->name;
+
+	      if (flag_use_colors)
 		{
+		  fn_name.insert (0, SGR_SEQ (COLOR_FG_CYAN));
+		  fn_name += SGR_RESET;
+		}
+
+	      fprintf (gcov_file, "%s:\n", fn_name.c_str ());
+
+	      output_function_details (gcov_file, fn);
+
+	      /* Print all lines covered by the function.  */
+	      for (unsigned i = 0; i < lines.size (); i++)
+		{
+		  line_info *line = &lines[i];
+		  unsigned l = fn->start_line + i;
+
+		  /* For lines which don't exist in the .bb file, print '-'
+		     before the source line.  For lines which exist but
+		     were never executed, print '#####' or '=====' before
+		     the source line.  Otherwise, print the execution count
+		     before the source line.
+		     There are 16 spaces of indentation added before the source
+		     line so that tabs won't be messed up.  */
 		  output_line_beginning (gcov_file, line->exists,
-					 (*it)->exceptional, false,
-					 (*it)->count, line_num,
-					 "%%%%%", "$$$$$");
-		  fprintf (gcov_file, "-block %2d", ix++);
-		  if (flag_verbose)
-		    fprintf (gcov_file, " (BB %u)", (*it)->id);
-		  fprintf (gcov_file, "\n");
+					 line->unexceptional,
+					 line->has_unexecuted_block,
+					 line->count,
+					 l, "=====", "#####");
+
+		  print_source_line (gcov_file, source_lines, l);
+		  output_line_details (gcov_file, line, l);
 		}
-	      if (flag_branches)
-		for (arc = (*it)->succ; arc; arc = arc->succ_next)
-		  jx += output_branch_count (gcov_file, jx, arc);
 	    }
-	}
-      else if (flag_branches)
-	{
-	  int ix;
 
-	  ix = 0;
-	  for (vector<arc_t *>::const_iterator it = line->branches.begin ();
-	       it != line->branches.end (); it++)
-	    ix += output_branch_count (gcov_file, ix, (*it));
+	  fprintf (gcov_file, FN_SEPARATOR);
+	  line_start_group = 0;
 	}
     }
 
-  /* Handle all remaining source lines.  There may be lines after the
-     last line of code.  */
-  if (retval)
-    {
-      for (; (retval = read_line (source_file)); line_num++)
-	fprintf (gcov_file, "%9s:%5u:%s\n", "-", line_num, retval);
-    }
-
   if (source_file)
     fclose (source_file);
 }
-- 
2.14.3


[-- Attachment #3: incremental.patch --]
[-- Type: text/x-patch, Size: 3740 bytes --]

diff --git a/gcc/coverage.c b/gcc/coverage.c
index 8ac593920b6..15d092f4a60 100644
--- a/gcc/coverage.c
+++ b/gcc/coverage.c
@@ -666,7 +666,11 @@ coverage_begin_function (unsigned lineno_checksum, unsigned cfg_checksum)
   gcov_write_unsigned (DECL_ARTIFICIAL (current_function_decl));
   gcov_write_filename (xloc.file);
   gcov_write_unsigned (xloc.line);
-  gcov_write_unsigned (expand_location (cfun->function_end_locus).line);
+  gcov_write_unsigned (xloc.column);
+
+  /* Function can start in a single file and end in another one.  */
+  int fn_end_line = expand_location (cfun->function_end_locus).line;
+  gcov_write_unsigned (fn_end_line > xloc.line ? fn_end_line : xloc.line);
   gcov_write_length (offset);
 
   return !gcov_is_error ();
diff --git a/gcc/gcov-dump.c b/gcc/gcov-dump.c
index 22d08c04c1e..c4e05cd4795 100644
--- a/gcc/gcov-dump.c
+++ b/gcc/gcov-dump.c
@@ -312,8 +312,9 @@ tag_function (const char *filename ATTRIBUTE_UNUSED,
 	  name = gcov_read_string ();
 	  printf (" %s", name ? name : "NULL");
 	  unsigned line_start = gcov_read_unsigned ();
+	  unsigned column_start = gcov_read_unsigned ();
 	  unsigned line_end = gcov_read_unsigned ();
-	  printf (":%u:%u", line_start, line_end);
+	  printf (":%u:%u:%u", line_start, column_start, line_end);
 	  if (artificial)
 	    printf (", artificial");
 	}
diff --git a/gcc/gcov.c b/gcc/gcov.c
index 16fc456c632..846a2326196 100644
--- a/gcc/gcov.c
+++ b/gcc/gcov.c
@@ -263,6 +263,9 @@ typedef struct function_info
   /* First line number.  */
   unsigned start_line;
 
+  /* First line column.  */
+  unsigned start_column;
+
   /* Last line number.  */
   unsigned end_line;
 
@@ -284,7 +287,9 @@ struct function_line_start_cmp
   inline bool operator() (const function_info *lhs,
 			  const function_info *rhs)
     {
-      return lhs->end_line < rhs->start_line;
+      return (lhs->start_line == rhs->start_line
+	      ? lhs->start_column < rhs->start_column
+	      : lhs->start_line < rhs->start_line);
     }
 };
 
@@ -332,7 +337,7 @@ struct source_info
   vector <function_t *> functions;
 };
 
-source_info::source_info (): name (NULL), file_time (),
+source_info::source_info (): index (0), name (NULL), file_time (),
   lines (), coverage (), functions ()
 {
 }
@@ -349,6 +354,8 @@ source_info::get_functions_at_location (unsigned line_num) const
 	r.push_back (*it);
     }
 
+  std::sort (r.begin (), r.end (), function_line_start_cmp ());
+
   return r;
 }
 
@@ -547,7 +554,7 @@ function_info::function_info (): name (NULL), demangled_name (NULL),
   ident (0), lineno_checksum (0), cfg_checksum (0), has_catch (0),
   artificial (0), is_group (0),
   blocks (), blocks_executed (0), counts (NULL), num_counts (0),
-  start_line (0), end_line (0), src (0), lines (), next (NULL)
+  start_line (0), start_column (0), end_line (0), src (0), lines (), next (NULL)
 {
 }
 
@@ -1016,7 +1023,6 @@ output_intermediate_file (FILE *gcov_file, source_info *src)
 	   it2 != fns.end (); it2++)
 	{
 	  vector<line_info> &lines = (*it2)->lines;
-	  /* Print all lines covered by the function.  */
 	  for (unsigned i = 0; i < lines.size (); i++)
 	    {
 	      line_info *line = &lines[i];
@@ -1505,6 +1511,7 @@ read_graph_file (void)
 	  unsigned artificial = gcov_read_unsigned ();
 	  unsigned src_idx = find_source (gcov_read_string ());
 	  unsigned start_line = gcov_read_unsigned ();
+	  unsigned start_column = gcov_read_unsigned ();
 	  unsigned end_line = gcov_read_unsigned ();
 
 	  fn = new function_t;
@@ -1520,6 +1527,7 @@ read_graph_file (void)
 	  fn->cfg_checksum = cfg_checksum;
 	  fn->src = src_idx;
 	  fn->start_line = start_line;
+	  fn->start_column = start_column;
 	  fn->end_line = end_line;
 	  fn->artificial = artificial;
 

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 8/N][RFC][v3]: GCOV: support multiple functions per a line
  2017-11-08 11:42           ` Martin Liška
@ 2017-11-08 15:12             ` Nathan Sidwell
  2017-11-09  9:47               ` Martin Liška
  0 siblings, 1 reply; 33+ messages in thread
From: Nathan Sidwell @ 2017-11-08 15:12 UTC (permalink / raw)
  To: Martin Liška, gcc-patches

On 11/08/2017 06:03 AM, Martin Liška wrote:
> On 11/07/2017 03:49 PM, Nathan Sidwell wrote:
>> On 11/07/2017 05:53 AM, Martin Liška wrote:
>>> Hello.
>>>
>>> This is slightly updated version from the previous. Various small issues were fixed
>>> and I update documentation in order to reflect the changes.
>>
>>> +  gcov_write_unsigned (DECL_ARTIFICIAL (current_function_decl));
>>>     gcov_write_filename (xloc.file);
>>>     gcov_write_unsigned (xloc.line);
>>> +  gcov_write_unsigned (expand_location (cfun->function_end_locus).line);
>>>     gcov_write_length (offset);
>>
>> this is presuming the end line is in the same file as the start line.  A reasonable assumption, but users can have exciting ideas!  What is the failure mode if the function straddles a file boundary?
> 
> Hi.
> 
> I decided to fix that with change that set line_end = line_start if line_end < line_start.
> That survives reasonably well with cases like this:

> +  gcov_write_unsigned (DECL_ARTIFICIAL (current_function_decl));
>    gcov_write_filename (xloc.file);
>    gcov_write_unsigned (xloc.line);
> +  gcov_write_unsigned (xloc.column);
> +
> +  /* Function can start in a single file and end in another one.  */
> +  int fn_end_line = expand_location (cfun->function_end_locus).line;
> +  gcov_write_unsigned (fn_end_line > xloc.line ? fn_end_line : xloc.line);

sorry to be picky, but of course the end line could be greater than the 
start. but in a different file.  Please check file too.


>> This isn't stable if they start on the same line.  Will output order depend on the vaguaries of the sorting algorithm?
>>
>>> +      vector<line_info> &lines = (*it2)->lines;
>>> +      /* Print all lines covered by the function.  */
>>> +      for (unsigned i = 0; i < lines.size (); i++)
> 
> So fixed by introduction of line column that is used for sorting as well.

thanks.
> May I understand the reply as ACK?

Patch ok, with the above check for file names added.

nathan

-- 
Nathan Sidwell

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 8/N][RFC][v3]: GCOV: support multiple functions per a line
  2017-11-08 15:12             ` Nathan Sidwell
@ 2017-11-09  9:47               ` Martin Liška
  0 siblings, 0 replies; 33+ messages in thread
From: Martin Liška @ 2017-11-09  9:47 UTC (permalink / raw)
  To: Nathan Sidwell, gcc-patches

On 11/08/2017 03:57 PM, Nathan Sidwell wrote:
> On 11/08/2017 06:03 AM, Martin Liška wrote:
>> On 11/07/2017 03:49 PM, Nathan Sidwell wrote:
>>> On 11/07/2017 05:53 AM, Martin Liška wrote:
>>>> Hello.
>>>>
>>>> This is slightly updated version from the previous. Various small issues were fixed
>>>> and I update documentation in order to reflect the changes.
>>>
>>>> +  gcov_write_unsigned (DECL_ARTIFICIAL (current_function_decl));
>>>>     gcov_write_filename (xloc.file);
>>>>     gcov_write_unsigned (xloc.line);
>>>> +  gcov_write_unsigned (expand_location (cfun->function_end_locus).line);
>>>>     gcov_write_length (offset);
>>>
>>> this is presuming the end line is in the same file as the start line.  A reasonable assumption, but users can have exciting ideas!  What is the failure mode if the function straddles a file boundary?
>>
>> Hi.
>>
>> I decided to fix that with change that set line_end = line_start if line_end < line_start.
>> That survives reasonably well with cases like this:
> 
>> +  gcov_write_unsigned (DECL_ARTIFICIAL (current_function_decl));
>>    gcov_write_filename (xloc.file);
>>    gcov_write_unsigned (xloc.line);
>> +  gcov_write_unsigned (xloc.column);
>> +
>> +  /* Function can start in a single file and end in another one.  */
>> +  int fn_end_line = expand_location (cfun->function_end_locus).line;
>> +  gcov_write_unsigned (fn_end_line > xloc.line ? fn_end_line : xloc.line);
> 
> sorry to be picky, but of course the end line could be greater than the start. but in a different file.  Please check file too.

That's fine, thank you for good feedback!

> 
> 
>>> This isn't stable if they start on the same line.  Will output order depend on the vaguaries of the sorting algorithm?
>>>
>>>> +      vector<line_info> &lines = (*it2)->lines;
>>>> +      /* Print all lines covered by the function.  */
>>>> +      for (unsigned i = 0; i < lines.size (); i++)
>>
>> So fixed by introduction of line column that is used for sorting as well.
> 
> thanks.
>> May I understand the reply as ACK?
> 
> Patch ok, with the above check for file names added.
> 
> nathan
> 

Just installed as r254562. I'm planning to apply some small code clean-up
and I'm done :)

Martin

^ permalink raw reply	[flat|nested] 33+ messages in thread

end of thread, other threads:[~2017-11-09  9:12 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-10-26  8:12 [PATCH 0/7] GCOV: another set of improvements marxin
2017-10-26  8:12 ` [PATCH 2/7] GCOV: introduce usage of terminal colors marxin
2017-10-30 12:20   ` Nathan Sidwell
2017-10-30 14:53     ` David Malcolm
2017-10-31 11:14       ` Martin Liška
2017-10-26  8:12 ` [PATCH 5/7] GCOV: std::vector refactoring marxin
2017-10-30 14:17   ` Nathan Sidwell
2017-10-26  8:12 ` [PATCH 1/7] GCOV: document behavior of -fkeep-{static,inline}-functions (PR gcov-profile/82633) marxin
2017-10-30 12:17   ` Nathan Sidwell
2017-10-31 11:12     ` Martin Liška
2017-10-26  8:12 ` [PATCH 4/7] GCOV: add -j argument (human readable format) marxin
2017-10-30 12:44   ` Nathan Sidwell
2017-10-31 11:54     ` Martin Liška
2017-10-31 12:10       ` Nathan Sidwell
2017-10-31 14:04         ` Martin Liška
2017-10-31 14:39           ` Nathan Sidwell
2017-10-31 15:33             ` Martin Liška
2017-10-26  8:12 ` [PATCH 7/7] GCOV: std::vector refactoring III marxin
2017-10-30 14:23   ` Nathan Sidwell
2017-10-26  8:12 ` [PATCH 3/7] GCOV: add support for lines with an unexecuted lines marxin
2017-10-30 12:27   ` Nathan Sidwell
2017-10-31 11:29     ` Martin Liška
2017-11-02 15:33   ` Eric Botcazou
2017-10-26  8:19 ` [PATCH 6/7] GCOV: Vector refactoring II marxin
2017-10-30 14:19   ` Nathan Sidwell
2017-10-26  8:47 ` [PATCH 8/N][RFC] GCOV: support multiple functions per a line Martin Liška
2017-10-26 12:06   ` Nathan Sidwell
2017-11-01  8:00     ` [PATCH 8/N][RFC] v2 " Martin Liška
2017-11-07 10:53       ` [PATCH 8/N][RFC][v3]: " Martin Liška
2017-11-07 15:09         ` Nathan Sidwell
2017-11-08 11:42           ` Martin Liška
2017-11-08 15:12             ` Nathan Sidwell
2017-11-09  9:47               ` Martin Liška

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).