GCOV: support multiple functions per a line (PR gcov-profile/48463)
authorMartin Liska <mliska@suse.cz>
Thu, 9 Nov 2017 09:11:17 +0000 (10:11 +0100)
committerMartin Liska <marxin@gcc.gnu.org>
Thu, 9 Nov 2017 09:11:17 +0000 (09:11 +0000)
2017-11-09  Martin Liska  <mliska@suse.cz>

PR gcov-profile/48463
* 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.

From-SVN: r254562

gcc/ChangeLog
gcc/coverage.c
gcc/doc/gcov.texi
gcc/gcov-dump.c
gcc/gcov.c

index 982b43bb291de5ec5f52d1c316901685fb21f046..12fbaa0ac73fe29018f3f3e1afb5d38f550c166a 100644 (file)
@@ -1,3 +1,42 @@
+2017-11-09  Martin Liska  <mliska@suse.cz>
+
+       PR gcov-profile/48463
+       * 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.
+
 2017-11-09  Jakub Jelinek  <jakub@redhat.com>
 
        PR debug/82837
index 8a56a677f157427d9b03ec18e129b2df20df9990..f57897b83143be3d0355cc8cfb8d756b65988340 100644 (file)
@@ -663,8 +663,15 @@ 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);
+
+  expanded_location endloc = expand_location (cfun->function_end_locus);
+
+  /* Function can start in a single file and end in another one.  */
+  gcov_write_unsigned (endloc.file == xloc.file ? endloc.line : xloc.line);
   gcov_write_length (offset);
 
   return !gcov_is_error ();
index 5c4ba8a51a74689b6cf288f039fbe40b184d93e7..6f6a92c131a91f168b3a56752ecb5a467aaea94f 100644 (file)
@@ -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
index d24e72ac4a13471a658a155aafcc6618b93b1031..c4e05cd47954617668e5d95d06b9252fce5147fe 100644 (file)
@@ -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");
        }
     }
 }
index 48bcdc0d4c39ef2dcc027451f167631a393c6e40..846a232619647defb78bde94e2ce546e47f0d474 100644 (file)
@@ -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);
 }