diff mbox

[3/8] Simplify representation of locations of a block.

Message ID 4f99730aa8b94384864c11e9026bcb9036d121e7.1493371589.git.mliska@suse.cz
State New
Headers show

Commit Message

Martin Liška April 21, 2017, 2:02 p.m. UTC
gcc/ChangeLog:

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

	* gcov.c (struct block_location_info): New struct.
	(process_file): Fill up the new structure.
	(read_graph_file): Replace usage of encoding by the newly added
	struct.
	(add_line_counts): Likewise.
	(accumulate_line_counts): Remove usage of the union.
	(function_info::function_info): New function.
	(function_info::~function_info): Likewise.
	(process_file): Call delete instead of release_function.
	(release_function): Release the function.
	(release_structures): Call delete instead of release_function.
	(solve_flow_graph): Replace usage of num_blocks.
	(find_exception_blocks): Likewise.
	(output_lines): Fix GNU coding style.
---
 gcc/gcov.c | 253 ++++++++++++++++++++++++++++---------------------------------
 1 file changed, 114 insertions(+), 139 deletions(-)

Comments

Nathan Sidwell April 28, 2017, 11:47 a.m. UTC | #1
On 04/21/2017 10:02 AM, marxin wrote:
> gcc/ChangeLog:
> 
> 2017-04-26  Martin Liska  <mliska@suse.cz>
> 
> 	* gcov.c (struct block_location_info): New struct.
> 	(process_file): Fill up the new structure.
> 	(read_graph_file): Replace usage of encoding by the newly added
> 	struct.
> 	(add_line_counts): Likewise.
> 	(accumulate_line_counts): Remove usage of the union.
> 	(function_info::function_info): New function.
> 	(function_info::~function_info): Likewise.
> 	(process_file): Call delete instead of release_function.
> 	(release_function): Release the function.
> 	(release_structures): Call delete instead of release_function.
> 	(solve_flow_graph): Replace usage of num_blocks.
> 	(find_exception_blocks): Likewise.
> 	(output_lines): Fix GNU coding style.

> diff --git a/gcc/gcov.c b/gcc/gcov.c
> index 63f6a75f1af..7400cdee110 100644
> --- a/gcc/gcov.c
> +++ b/gcc/gcov.c
> @@ -114,6 +114,16 @@ typedef struct arc_info
>     struct arc_info *pred_next;
>   } arc_t;
>   
> +struct block_location_info

/* needs comment.  */



> @@ -427,9 +429,31 @@ static void output_lines (FILE *, const source_t *);
>   static char *make_gcov_file_name (const char *, const char *);
>   static char *mangle_name (const char *, char *);
>   static void release_structures (void);
> -static void release_function (function_t *);
>   extern int main (int, char **);
>   
> +function_info::function_info ()
> +{
> +  memset (this, 0, sizeof (*this));

EW.  ok with a comment about function_info's c++11's PoDness.

ok with those fixed.

nathan
Martin Sebor April 28, 2017, 4:28 p.m. UTC | #2
On 04/28/2017 05:47 AM, Nathan Sidwell wrote:
> On 04/21/2017 10:02 AM, marxin wrote:
>> gcc/ChangeLog:
>>
>> 2017-04-26  Martin Liska  <mliska@suse.cz>
>>
>>     * gcov.c (struct block_location_info): New struct.
>>     (process_file): Fill up the new structure.
>>     (read_graph_file): Replace usage of encoding by the newly added
>>     struct.
>>     (add_line_counts): Likewise.
>>     (accumulate_line_counts): Remove usage of the union.
>>     (function_info::function_info): New function.
>>     (function_info::~function_info): Likewise.
>>     (process_file): Call delete instead of release_function.
>>     (release_function): Release the function.
>>     (release_structures): Call delete instead of release_function.
>>     (solve_flow_graph): Replace usage of num_blocks.
>>     (find_exception_blocks): Likewise.
>>     (output_lines): Fix GNU coding style.
>
>> diff --git a/gcc/gcov.c b/gcc/gcov.c
>> index 63f6a75f1af..7400cdee110 100644
>> --- a/gcc/gcov.c
>> +++ b/gcc/gcov.c
>> @@ -114,6 +114,16 @@ typedef struct arc_info
>>     struct arc_info *pred_next;
>>   } arc_t;
>>   +struct block_location_info
>
> /* needs comment.  */
>
>
>
>> @@ -427,9 +429,31 @@ static void output_lines (FILE *, const source_t *);
>>   static char *make_gcov_file_name (const char *, const char *);
>>   static char *mangle_name (const char *, char *);
>>   static void release_structures (void);
>> -static void release_function (function_t *);
>>   extern int main (int, char **);
>>   +function_info::function_info ()
>> +{
>> +  memset (this, 0, sizeof (*this));
>
> EW.  ok with a comment about function_info's c++11's PoDness.

Unless it's some other kind of vector, the patch adds a vector
member to the class, which makes it not a PoD.(*)

In addition, it would make the class ever so slightly safer to
use if it were made non-copyable (by declaring its copy ctor
private).  Otherwise, accidentally creating a copy of an object
of the type could lead to a double free in the newly added dtor.

Martin

[*]  Strictly speaking using memset to initialize pointers to
null isn't guaranteed to work on targets where a null pointer
isn't all bits clear.  I don't know if GCC is meant to build
on such targets but the code would be cleaner (albeit more
verbose) if it defeault-initialized each member in the ctor
initializer list.
Nathan Sidwell April 28, 2017, 5:23 p.m. UTC | #3
On 04/28/2017 12:28 PM, Martin Sebor wrote:

> Unless it's some other kind of vector, the patch adds a vector
> member to the class, which makes it not a PoD.(*)

oh, well double ew.  Your code is bad and you should feel bad.

Write proper member initializers please.

> [*]  Strictly speaking using memset to initialize pointers to
> null isn't guaranteed to work on targets where a null pointer
> isn't all bits clear.  I don't know if GCC is meant to build

Such targets are unuseable.  I know of one that started like that 
(xputers), but eventually had to change so that all-bits zero meant 
NULL.  Too much user code exposes that assumption.

nathan
Pedro Alves April 28, 2017, 7:01 p.m. UTC | #4
On 04/28/2017 05:28 PM, Martin Sebor wrote:
> On 04/28/2017 05:47 AM, Nathan Sidwell wrote:

>>> @@ -427,9 +429,31 @@ static void output_lines (FILE *, const source_t
>>> *);
>>>   static char *make_gcov_file_name (const char *, const char *);
>>>   static char *mangle_name (const char *, char *);
>>>   static void release_structures (void);
>>> -static void release_function (function_t *);
>>>   extern int main (int, char **);
>>>   +function_info::function_info ()
>>> +{
>>> +  memset (this, 0, sizeof (*this));
>>
>> EW.  ok with a comment about function_info's c++11's PoDness.
> 
> Unless it's some other kind of vector, the patch adds a vector
> member to the class, which makes it not a PoD.(*)

Funny, just this week we added this to gdb to catch such misuses
at compile time:
 https://sourceware.org/ml/gdb-patches/2017-04/msg00378.html
 https://sourceware.org/ml/gdb-patches/2017-04/msg00381.html

Thanks,
Pedro Alves
Martin Sebor April 28, 2017, 7:13 p.m. UTC | #5
On 04/28/2017 01:01 PM, Pedro Alves wrote:
> On 04/28/2017 05:28 PM, Martin Sebor wrote:
>> On 04/28/2017 05:47 AM, Nathan Sidwell wrote:
>
>>>> @@ -427,9 +429,31 @@ static void output_lines (FILE *, const source_t
>>>> *);
>>>>   static char *make_gcov_file_name (const char *, const char *);
>>>>   static char *mangle_name (const char *, char *);
>>>>   static void release_structures (void);
>>>> -static void release_function (function_t *);
>>>>   extern int main (int, char **);
>>>>   +function_info::function_info ()
>>>> +{
>>>> +  memset (this, 0, sizeof (*this));
>>>
>>> EW.  ok with a comment about function_info's c++11's PoDness.
>>
>> Unless it's some other kind of vector, the patch adds a vector
>> member to the class, which makes it not a PoD.(*)
>
> Funny, just this week we added this to gdb to catch such misuses
> at compile time:
>  https://sourceware.org/ml/gdb-patches/2017-04/msg00378.html
>  https://sourceware.org/ml/gdb-patches/2017-04/msg00381.html

It's all too easy to turn a POD into a class and miss that its
objects are still being treated as PODs, especially in a C code
base that's (slowly) transitioning to C++.  It's also easy form
GCC to detect them.  I'm playing with a simple enhancement that
makes GCC warn on these kinds of misuses.

Martin
Pedro Alves April 28, 2017, 7:13 p.m. UTC | #6
On 04/28/2017 08:01 PM, Pedro Alves wrote:
> On 04/28/2017 05:28 PM, Martin Sebor wrote:
>> On 04/28/2017 05:47 AM, Nathan Sidwell wrote:
> 
>>>> @@ -427,9 +429,31 @@ static void output_lines (FILE *, const source_t
>>>> *);
>>>>   static char *make_gcov_file_name (const char *, const char *);
>>>>   static char *mangle_name (const char *, char *);
>>>>   static void release_structures (void);
>>>> -static void release_function (function_t *);
>>>>   extern int main (int, char **);
>>>>   +function_info::function_info ()
>>>> +{
>>>> +  memset (this, 0, sizeof (*this));
>>>
>>> EW.  ok with a comment about function_info's c++11's PoDness.
>>
>> Unless it's some other kind of vector, the patch adds a vector
>> member to the class, which makes it not a PoD.(*)
> 
> Funny, just this week we added this to gdb to catch such misuses
> at compile time:
>  https://sourceware.org/ml/gdb-patches/2017-04/msg00378.html
>  https://sourceware.org/ml/gdb-patches/2017-04/msg00381.html

I think that teaching g++ new warnings for these issues would
be neat, BTW.

Thanks,
Pedro Alves
diff mbox

Patch

diff --git a/gcc/gcov.c b/gcc/gcov.c
index 63f6a75f1af..7400cdee110 100644
--- a/gcc/gcov.c
+++ b/gcc/gcov.c
@@ -114,6 +114,16 @@  typedef struct arc_info
   struct arc_info *pred_next;
 } arc_t;
 
+struct block_location_info
+{
+  block_location_info (unsigned _source_file_idx):
+    source_file_idx (_source_file_idx)
+  {}
+
+  unsigned source_file_idx;
+  vector<unsigned> lines;
+};
+
 /* Describes a basic block. Contains lists of arcs to successor and
    predecessor blocks.  */
 
@@ -141,26 +151,16 @@  typedef struct block_info
   /* Block is a landing pad for longjmp or throw.  */
   unsigned is_nonlocal_return : 1;
 
-  union
+  vector<block_location_info> locations;
+
+  struct
   {
-    struct
-    {
-     /* Array of line numbers and source files. source files are
-        introduced by a linenumber of zero, the next 'line number' is
-        the number of the source file.  Always starts with a source
-        file.  */
-      unsigned *encoding;
-      unsigned num;
-    } line; /* Valid until blocks are linked onto lines */
-    struct
-    {
-      /* Single line graph cycle workspace.  Used for all-blocks
-	 mode.  */
-      arc_t *arc;
-      unsigned ident;
-    } cycle; /* Used in all-blocks mode, after blocks are linked onto
-	       lines.  */
-  } u;
+    /* Single line graph cycle workspace.  Used for all-blocks
+       mode.  */
+    arc_t *arc;
+    unsigned ident;
+  } cycle; /* Used in all-blocks mode, after blocks are linked onto
+	     lines.  */
 
   /* Temporary chain for solving graph, and for chaining blocks on one
      line.  */
@@ -172,6 +172,9 @@  typedef struct block_info
 
 typedef struct function_info
 {
+  function_info ();
+  ~function_info ();
+
   /* Name of function.  */
   char *name;
   char *demangled_name;
@@ -186,8 +189,7 @@  typedef struct function_info
      at blocks[0] and the exit block is at blocks[1].  */
 #define ENTRY_BLOCK (0)
 #define EXIT_BLOCK (1)
-  block_t *blocks;
-  unsigned num_blocks;
+  vector<block_t> blocks;
   unsigned blocks_executed;
 
   /* Raw arc coverage counts.  */
@@ -427,9 +429,31 @@  static void output_lines (FILE *, const source_t *);
 static char *make_gcov_file_name (const char *, const char *);
 static char *mangle_name (const char *, char *);
 static void release_structures (void);
-static void release_function (function_t *);
 extern int main (int, char **);
 
+function_info::function_info ()
+{
+  memset (this, 0, sizeof (*this));
+}
+
+function_info::~function_info ()
+{
+  for (int i = blocks.size () - 1; i >= 0; i--)
+    {
+      arc_t *arc, *arc_n;
+
+      for (arc = blocks[i].succ; arc; arc = arc_n)
+	{
+	  arc_n = arc->succ_next;
+	  free (arc);
+	}
+    }
+  free (counts);
+  if (flag_demangled_names && demangled_name != name)
+    free (demangled_name);
+  free (name);
+}
+
 /* 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
@@ -906,29 +930,26 @@  process_file (const char *file_name)
 	  *prev = fn;
 
 	  /* Mark last line in files touched by function.  */
-	  for (block_no = 0; block_no != fn->num_blocks; block_no++)
+	  for (block_no = 0; block_no != fn->blocks.size (); block_no++)
 	    {
-	      unsigned *enc = fn->blocks[block_no].u.line.encoding;
-	      unsigned num = fn->blocks[block_no].u.line.num;
+	      block_t *block = &fn->blocks[block_no];
+	      for (unsigned i = 0; i < block->locations.size (); i++)
+		{
+		  unsigned s = block->locations[i].source_file_idx;
 
-	      for (; num--; enc++)
-		if (!*enc)
-		  {
-		    if (enc[1] != src)
-		      {
-			if (line >= sources[src].num_lines)
-			  sources[src].num_lines = line + 1;
-			line = 0;
-			src = enc[1];
-		      }
-		    enc++;
-		    num--;
-		  }
-		else if (*enc > line)
-		  line = *enc;
+		  /* Sort lines of locations.  */
+		  sort (block->locations[i].lines.begin (),
+			block->locations[i].lines.end ());
+
+		  if (!block->locations[i].lines.empty ())
+		    {
+		      unsigned last_line
+			= block->locations[i].lines.back () + 1;
+		      if (last_line > sources[s].num_lines)
+			sources[s].num_lines = last_line;
+		    }
+		}
 	    }
-	  if (line >= sources[src].num_lines)
-	    sources[src].num_lines = line + 1;
 
 	  solve_flow_graph (fn);
 	  if (fn->has_catch)
@@ -939,7 +960,7 @@  process_file (const char *file_name)
       else
 	/* The function was not in the executable -- some other
 	   instance must have been selected.  */
-	release_function (fn);
+	delete fn;
     }
 }
 
@@ -1040,31 +1061,6 @@  generate_results (const char *file_name)
     executed_summary (total_lines, total_executed);
 }
 
-/* Release a function structure */
-
-static void
-release_function (function_t *fn)
-{
-  unsigned ix;
-  block_t *block;
-
-  for (ix = fn->num_blocks, block = fn->blocks; ix--; block++)
-    {
-      arc_t *arc, *arc_n;
-
-      for (arc = block->succ; arc; arc = arc_n)
-	{
-	  arc_n = arc->succ_next;
-	  free (arc);
-	}
-    }
-  free (fn->blocks);
-  free (fn->counts);
-  if (flag_demangled_names && fn->demangled_name != fn->name)
-    free (fn->demangled_name);
-  free (fn->name);
-}
-
 /* Release all memory used.  */
 
 static void
@@ -1084,7 +1080,7 @@  release_structures (void)
   while ((fn = functions))
     {
       functions = fn->next;
-      release_function (fn);
+      delete fn;
     }
 }
 
@@ -1298,8 +1294,6 @@  read_graph_file (void)
   function_t *fn = NULL;
   function_t *fns = NULL;
   function_t **fns_end = &fns;
-  unsigned src_idx = 0;
-  unsigned ix;
   unsigned tag;
 
   if (!gcov_open (bbg_file_name, 1))
@@ -1343,10 +1337,10 @@  read_graph_file (void)
 	  lineno_checksum = gcov_read_unsigned ();
 	  cfg_checksum = gcov_read_unsigned ();
 	  function_name = xstrdup (gcov_read_string ());
-	  src_idx = find_source (gcov_read_string ());
+	  unsigned src_idx = find_source (gcov_read_string ());
 	  lineno = gcov_read_unsigned ();
 
-	  fn = XCNEW (function_t);
+	  fn = new function_t;
 	  fn->name = function_name;
 	  if (flag_demangled_names)
 	    {
@@ -1368,14 +1362,11 @@  read_graph_file (void)
 	}
       else if (fn && tag == GCOV_TAG_BLOCKS)
 	{
-	  if (fn->blocks)
+	  if (!fn->blocks.empty ())
 	    fnotice (stderr, "%s:already seen blocks for '%s'\n",
 		     bbg_file_name, fn->name);
 	  else
-	    {
-	      fn->num_blocks = gcov_read_unsigned ();
-	      fn->blocks = XCNEWVEC (block_t, fn->num_blocks);
-	    }
+	    fn->blocks.resize (gcov_read_unsigned ());
 	}
       else if (fn && tag == GCOV_TAG_ARCS)
 	{
@@ -1385,7 +1376,7 @@  read_graph_file (void)
 	  unsigned mark_catches = 0;
 	  struct arc_info *arc;
 
-	  if (src >= fn->num_blocks || fn->blocks[src].succ)
+	  if (src >= fn->blocks.size () || fn->blocks[src].succ)
 	    goto corrupt;
 
 	  while (num_dests--)
@@ -1393,7 +1384,7 @@  read_graph_file (void)
 	      unsigned dest = gcov_read_unsigned ();
 	      unsigned flags = gcov_read_unsigned ();
 
-	      if (dest >= fn->num_blocks)
+	      if (dest >= fn->blocks.size ())
 		goto corrupt;
 	      arc = XCNEW (arc_t);
 
@@ -1454,38 +1445,27 @@  read_graph_file (void)
       else if (fn && tag == GCOV_TAG_LINES)
 	{
 	  unsigned blockno = gcov_read_unsigned ();
-	  unsigned *line_nos = XCNEWVEC (unsigned, length - 1);
+	  block_t *block = &fn->blocks[blockno];
 
-	  if (blockno >= fn->num_blocks || fn->blocks[blockno].u.line.encoding)
+	  if (blockno >= fn->blocks.size ())
 	    goto corrupt;
 
-	  for (ix = 0; ;  )
+	  while (true)
 	    {
 	      unsigned lineno = gcov_read_unsigned ();
 
 	      if (lineno)
-		{
-		  if (!ix)
-		    {
-		      line_nos[ix++] = 0;
-		      line_nos[ix++] = src_idx;
-		    }
-		  line_nos[ix++] = lineno;
-		}
+		block->locations.back ().lines.push_back (lineno);
 	      else
 		{
 		  const char *file_name = gcov_read_string ();
 
 		  if (!file_name)
 		    break;
-		  src_idx = find_source (file_name);
-		  line_nos[ix++] = 0;
-		  line_nos[ix++] = src_idx;
+		  block->locations.push_back (block_location_info
+					      (find_source (file_name)));
 		}
 	    }
-
-	  fn->blocks[blockno].u.line.encoding = line_nos;
-	  fn->blocks[blockno].u.line.num = ix;
 	}
       else if (current_tag && !GCOV_TAG_IS_SUBTAG (current_tag, tag))
 	{
@@ -1643,7 +1623,7 @@  solve_flow_graph (function_t *fn)
   block_t *invalid_blocks = NULL;  /* invalid, but inferable blocks.  */
 
   /* The arcs were built in reverse order.  Fix that now.  */
-  for (ix = fn->num_blocks; ix--;)
+  for (ix = fn->blocks.size (); ix--;)
     {
       arc_t *arc_p, *arc_n;
 
@@ -1664,7 +1644,7 @@  solve_flow_graph (function_t *fn)
       fn->blocks[ix].pred = arc_p;
     }
 
-  if (fn->num_blocks < 2)
+  if (fn->blocks.size () < 2)
     fnotice (stderr, "%s:'%s' lacks entry and/or exit blocks\n",
 	     bbg_file_name, fn->name);
   else
@@ -1688,8 +1668,9 @@  solve_flow_graph (function_t *fn)
 
   /* Propagate the measured counts, this must be done in the same
      order as the code in profile.c  */
-  for (ix = 0, blk = fn->blocks; ix != fn->num_blocks; ix++, blk++)
+  for (unsigned i = 0; i < fn->blocks.size (); i++)
     {
+      blk = &fn->blocks[i];
       block_t const *prev_dst = NULL;
       int out_of_order = 0;
       int non_fake_succ = 0;
@@ -1883,8 +1864,8 @@  solve_flow_graph (function_t *fn)
 
   /* If the graph has been correctly solved, every block will have a
      valid count.  */
-  for (ix = 0; ix < fn->num_blocks; ix++)
-    if (!fn->blocks[ix].count_valid)
+  for (unsigned i = 0; ix < fn->blocks.size (); i++)
+    if (!fn->blocks[i].count_valid)
       {
 	fnotice (stderr, "%s:graph is unsolvable for '%s'\n",
 		 bbg_file_name, fn->name);
@@ -1898,14 +1879,14 @@  static void
 find_exception_blocks (function_t *fn)
 {
   unsigned ix;
-  block_t **queue = XALLOCAVEC (block_t *, fn->num_blocks);
+  block_t **queue = XALLOCAVEC (block_t *, fn->blocks.size ());
 
   /* First mark all blocks as exceptional.  */
-  for (ix = fn->num_blocks; ix--;)
+  for (ix = fn->blocks.size (); ix--;)
     fn->blocks[ix].exceptional = 1;
 
   /* Now mark all the blocks reachable via non-fake edges */
-  queue[0] = fn->blocks;
+  queue[0] = &fn->blocks[0];
   queue[0]->exceptional = 0;
   for (ix = 1; ix;)
     {
@@ -2247,43 +2228,36 @@  add_line_counts (coverage_t *coverage, function_t *fn)
 			  next.  */
 
   /* Scan each basic block.  */
-  for (ix = 0; ix != fn->num_blocks; ix++)
+  for (ix = 0; ix != fn->blocks.size (); ix++)
     {
       block_t *block = &fn->blocks[ix];
-      unsigned *encoding;
-      const source_t *src = NULL;
-      unsigned jx;
-
-      if (block->count && ix && ix + 1 != fn->num_blocks)
+      if (block->count && ix && ix + 1 != fn->blocks.size ())
 	fn->blocks_executed++;
-      for (jx = 0, encoding = block->u.line.encoding;
-	   jx != block->u.line.num; jx++, encoding++)
-	if (!*encoding)
-	  {
-	    src = &sources[*++encoding];
-	    jx++;
-	  }
-	else
-	  {
-	    line = &src->lines[*encoding];
+      for (unsigned i = 0; i < block->locations.size (); i++)
+	{
+	  const source_t *src = &sources[block->locations[i].source_file_idx];
 
-	    if (coverage)
-	      {
-		if (!line->exists)
-		  coverage->lines++;
-		if (!line->count && block->count)
-		  coverage->lines_executed++;
-	      }
-	    line->exists = 1;
-	    if (!block->exceptional)
-	      line->unexceptional = 1;
-	    line->count += block->count;
-	  }
-      free (block->u.line.encoding);
-      block->u.cycle.arc = NULL;
-      block->u.cycle.ident = ~0U;
+	  vector<unsigned> &lines = block->locations[i].lines;
+	  for (unsigned j = 0; j < lines.size (); j++)
+	    {
+	      line = &src->lines[lines[j]];
+	      if (coverage)
+		{
+		  if (!line->exists)
+		    coverage->lines++;
+		  if (!line->count && block->count)
+		    coverage->lines_executed++;
+		}
+	      line->exists = 1;
+	      if (!block->exceptional)
+		line->unexceptional = 1;
+	      line->count += block->count;
+	    }
+	}
+      block->cycle.arc = NULL;
+      block->cycle.ident = ~0U;
 
-      if (!ix || ix + 1 == fn->num_blocks)
+      if (!ix || ix + 1 == fn->blocks.size ())
 	/* Entry or exit block */;
       else if (flag_all_blocks)
 	{
@@ -2363,7 +2337,7 @@  accumulate_line_counts (source_t *src)
 	    {
 	      block_n = block->chain;
 	      block->chain = block_p;
-	      block->u.cycle.ident = ix;
+	      block->cycle.ident = ix;
 	    }
 	  line->u.blocks = block_p;
 
@@ -2533,7 +2507,8 @@  output_lines (FILE *gcov_file, const source_t *src)
 	  fprintf (gcov_file, " returned %s",
 		   format_gcov (return_count, called_count, 0));
 	  fprintf (gcov_file, " blocks executed %s",
-		   format_gcov (fn->blocks_executed, fn->num_blocks - 2, 0));
+		   format_gcov (fn->blocks_executed, fn->blocks.size () - 2,
+				0));
 	  fprintf (gcov_file, "\n");
 	}