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





   

>From cf05cc4b236d7fd58091a4fd3fd4e88bfee28c00 Mon Sep 17 00:00:00 2001
From: marxin <mli...@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  <mli...@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


Reply via email to