Example #1
0
static int
merge_one_data (const char *filename,
		struct gcov_info *gi_ptr,
		struct gcov_summary *prg_p,
		struct gcov_summary *this_prg,
		gcov_position_t *summary_pos_p,
		gcov_position_t *eof_pos_p,
		gcov_unsigned_t crc32)
{
  gcov_unsigned_t tag, length;
  unsigned t_ix;
  int f_ix;
  int error = 0;
  struct gcov_fn_buffer **fn_tail = &fn_buffer;
  struct gcov_summary_buffer **sum_tail = &sum_buffer;

  length = gcov_read_unsigned ();
  if (!gcov_version (gi_ptr, length, filename))
    return -1;

  length = gcov_read_unsigned ();
  if (length != gi_ptr->stamp)
    /* Read from a different compilation. Overwrite the file.  */
    return 0;

  /* Look for program summary.  */
  for (f_ix = 0;;)
    {
      struct gcov_summary tmp;

      *eof_pos_p = gcov_position ();
      tag = gcov_read_unsigned ();
      if (tag != GCOV_TAG_PROGRAM_SUMMARY)
        break;

      f_ix--;
      length = gcov_read_unsigned ();
      gcov_read_summary (&tmp);
      if ((error = gcov_is_error ()))
        goto read_error;
      if (*summary_pos_p)
        {
          /* Save all summaries after the one that will be
             merged into below. These will need to be rewritten
             as histogram merging may change the number of non-zero
             histogram entries that will be emitted, and thus the
             size of the merged summary.  */
          (*sum_tail) = (struct gcov_summary_buffer *)
              xmalloc (sizeof(struct gcov_summary_buffer));
          (*sum_tail)->summary = tmp;
          (*sum_tail)->next = 0;
          sum_tail = &((*sum_tail)->next);
          goto next_summary;
        }
      if (tmp.checksum != crc32)
        goto next_summary;

      for (t_ix = 0; t_ix != GCOV_COUNTERS_SUMMABLE; t_ix++)
        if (tmp.ctrs[t_ix].num != this_prg->ctrs[t_ix].num)
          goto next_summary;
      *prg_p = tmp;
      *summary_pos_p = *eof_pos_p;

    next_summary:;
    }

  /* Merge execution counts for each function.  */
  for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions;
       f_ix++, tag = gcov_read_unsigned ())
    {
      const struct gcov_ctr_info *ci_ptr;
      const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix];

      if (tag != GCOV_TAG_FUNCTION)
        goto read_mismatch;

      length = gcov_read_unsigned ();
      if (!length)
        /* This function did not appear in the other program.
           We have nothing to merge.  */
        continue;

      if (length != GCOV_TAG_FUNCTION_LENGTH)
        goto read_mismatch;

      if (!gfi_ptr || gfi_ptr->key != gi_ptr)
        {
          /* This function appears in the other program.  We
             need to buffer the information in order to write
             it back out -- we'll be inserting data before
             this point, so cannot simply keep the data in the
             file.  */
          fn_tail = buffer_fn_data (filename, gi_ptr, fn_tail, f_ix);
          if (!fn_tail)
            goto read_mismatch;
          continue;
        }

      length = gcov_read_unsigned ();
      if (length != gfi_ptr->ident)
        goto read_mismatch;

      length = gcov_read_unsigned ();
      if (length != gfi_ptr->lineno_checksum)
        goto read_mismatch;

      length = gcov_read_unsigned ();
      if (length != gfi_ptr->cfg_checksum)
        goto read_mismatch;

      ci_ptr = gfi_ptr->ctrs;
      for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
        {
          gcov_merge_fn merge = gi_ptr->merge[t_ix];

          if (!merge)
            continue;

          tag = gcov_read_unsigned ();
          length = gcov_read_unsigned ();
          if (tag != GCOV_TAG_FOR_COUNTER (t_ix)
              || length != GCOV_TAG_COUNTER_LENGTH (ci_ptr->num))
            goto read_mismatch;
          (*merge) (ci_ptr->values, ci_ptr->num);
          ci_ptr++;
        }
      if ((error = gcov_is_error ()))
        goto read_error;
    }

  if (tag)
    {
    read_mismatch:;
      gcov_error ("profiling:%s:Merge mismatch for %s %u\n",
                  filename, f_ix >= 0 ? "function" : "summary",
                  f_ix < 0 ? -1 - f_ix : f_ix);
      return -1;
    }
  return 0;

read_error:
  gcov_error ("profiling:%s:%s merging\n", filename,
              error < 0 ? "Overflow": "Error");
  return -1;
}
Example #2
0
static void
gcov_exit (void)
{
  struct gcov_info *gi_ptr;
  const struct gcov_fn_info *gfi_ptr;
  struct gcov_summary this_prg; /* summary for program.  */
  struct gcov_summary all_prg;  /* summary for all instances of program.  */
  struct gcov_ctr_summary *cs_ptr;
  const struct gcov_ctr_info *ci_ptr;
  unsigned t_ix;
  int f_ix;
  gcov_unsigned_t c_num;
  const char *gcov_prefix;
  int gcov_prefix_strip = 0;
  size_t prefix_length;
  char *gi_filename, *gi_filename_up;
  gcov_unsigned_t crc32 = 0;

  memset (&all_prg, 0, sizeof (all_prg));
  /* Find the totals for this execution.  */
  memset (&this_prg, 0, sizeof (this_prg));
  for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
    {
      crc32 = crc32_unsigned (crc32, gi_ptr->stamp);
      crc32 = crc32_unsigned (crc32, gi_ptr->n_functions);
      
      for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; f_ix++)
	{
	  gfi_ptr = gi_ptr->functions[f_ix];

	  if (gfi_ptr && gfi_ptr->key != gi_ptr)
	    gfi_ptr = 0;
	  
	  crc32 = crc32_unsigned (crc32, gfi_ptr ? gfi_ptr->cfg_checksum : 0);
	  crc32 = crc32_unsigned (crc32,
				  gfi_ptr ? gfi_ptr->lineno_checksum : 0);
	  if (!gfi_ptr)
	    continue;

	  ci_ptr = gfi_ptr->ctrs;
	  for (t_ix = 0; t_ix != GCOV_COUNTERS_SUMMABLE; t_ix++)
	    {
	      if (!gi_ptr->merge[t_ix])
		continue;

	      cs_ptr = &this_prg.ctrs[t_ix];
	      cs_ptr->num += ci_ptr->num;
	      crc32 = crc32_unsigned (crc32, ci_ptr->num);
	      
	      for (c_num = 0; c_num < ci_ptr->num; c_num++)
		{
		  cs_ptr->sum_all += ci_ptr->values[c_num];
		  if (cs_ptr->run_max < ci_ptr->values[c_num])
		    cs_ptr->run_max = ci_ptr->values[c_num];
		}
	      ci_ptr++;
	    }
	}
    }

  {
    /* Check if the level of dirs to strip off specified. */
    char *tmp = getenv("GCOV_PREFIX_STRIP");
    if (tmp)
      {
	gcov_prefix_strip = atoi (tmp);
	/* Do not consider negative values. */
	if (gcov_prefix_strip < 0)
	  gcov_prefix_strip = 0;
      }
  }

  /* Get file name relocation prefix.  Non-absolute values are ignored. */
  gcov_prefix = getenv("GCOV_PREFIX");
  if (gcov_prefix)
    {
      prefix_length = strlen(gcov_prefix);

      /* Remove an unnecessary trailing '/' */
      if (IS_DIR_SEPARATOR (gcov_prefix[prefix_length - 1]))
	prefix_length--;
    }
  else
    prefix_length = 0;

  /* If no prefix was specified and a prefix stip, then we assume
     relative.  */
  if (gcov_prefix_strip != 0 && prefix_length == 0)
    {
      gcov_prefix = ".";
      prefix_length = 1;
    }
  /* Allocate and initialize the filename scratch space plus one.  */
  gi_filename = (char *) alloca (prefix_length + gcov_max_filename + 2
				 + GCOV_TARGET_SUFFIX_LENGTH);
  if (prefix_length)
    memcpy (gi_filename, gcov_prefix, prefix_length);
  gi_filename_up = gi_filename + prefix_length;

  /* Now merge each file.  */
  for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
    {
      unsigned n_counts;
      struct gcov_summary prg; /* summary for this object over all
				  program.  */
      struct gcov_ctr_summary *cs_prg, *cs_tprg, *cs_all;
      int error = 0;
      gcov_unsigned_t tag, length;
      gcov_position_t summary_pos = 0;
      gcov_position_t eof_pos = 0;
      const char *fname, *s;
      struct gcov_fn_buffer *fn_buffer = 0;
      struct gcov_fn_buffer **fn_tail = &fn_buffer;

      fname = gi_ptr->filename;

      /* Avoid to add multiple drive letters into combined path.  */
      if (prefix_length != 0 && HAS_DRIVE_SPEC(fname))
        fname += 2;

      /* Build relocated filename, stripping off leading
         directories from the initial filename if requested. */
      if (gcov_prefix_strip > 0)
        {
          int level = 0;
          s = fname;
          if (IS_DIR_SEPARATOR(*s))
            ++s;

          /* Skip selected directory levels. */
	  for (; (*s != '\0') && (level < gcov_prefix_strip); s++)
	    if (IS_DIR_SEPARATOR(*s))
	      {
		fname = s;
		level++;
	      }
        }

      /* Update complete filename with stripped original. */
      if (prefix_length != 0 && !IS_DIR_SEPARATOR (*fname))
        {
          /* If prefix is given, add directory separator.  */
	  strcpy (gi_filename_up, "/");
	  strcpy (gi_filename_up + 1, fname);
	}
      else
        strcpy (gi_filename_up, fname);

#ifdef ADD_GCOV_TARGET_SUFFIX
      /* Give a chance to target to change the name.  */
      ADD_GCOV_TARGET_SUFFIX (gi_filename_up);
#endif
      if (!gcov_open (gi_filename))
	{
	  /* Open failed likely due to missed directory.
	     Create directory and retry to open file. */
          if (create_file_directory (gi_filename))
	    {
	      fprintf (stderr, "profiling:%s:Skip\n", gi_filename);
	      continue;
	    }
	  if (!gcov_open (gi_filename))
	    {
              fprintf (stderr, "profiling:%s:Cannot open\n", gi_filename);
	      continue;
	    }
	}

      tag = gcov_read_unsigned ();
      if (tag)
	{
	  /* Merge data from file.  */
	  if (tag != GCOV_DATA_MAGIC)
	    {
	      fprintf (stderr, "profiling:%s:Not a gcov data file\n",
		       gi_filename);
	      goto read_fatal;
	    }
	  length = gcov_read_unsigned ();
	  if (!gcov_version (gi_ptr, length, gi_filename))
	    goto read_fatal;

	  length = gcov_read_unsigned ();
	  if (length != gi_ptr->stamp)
	    /* Read from a different compilation. Overwrite the file.  */
	    goto rewrite;

	  /* Look for program summary.  */
	  for (f_ix = 0;;)
	    {
	      struct gcov_summary tmp;
	      
	      eof_pos = gcov_position ();
	      tag = gcov_read_unsigned ();
	      if (tag != GCOV_TAG_PROGRAM_SUMMARY)
		break;

	      f_ix--;
	      length = gcov_read_unsigned ();
	      if (length != GCOV_TAG_SUMMARY_LENGTH)
		goto read_mismatch;
	      gcov_read_summary (&tmp);
	      if ((error = gcov_is_error ()))
		goto read_error;
	      if (summary_pos || tmp.checksum != crc32)
		goto next_summary;
	      
	      for (t_ix = 0; t_ix != GCOV_COUNTERS_SUMMABLE; t_ix++)
		if (tmp.ctrs[t_ix].num != this_prg.ctrs[t_ix].num)
		  goto next_summary;
	      prg = tmp;
	      summary_pos = eof_pos;

	    next_summary:;
	    }
	  
	  /* Merge execution counts for each function.  */
	  for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions;
	       f_ix++, tag = gcov_read_unsigned ())
	    {
	      gfi_ptr = gi_ptr->functions[f_ix];

	      if (tag != GCOV_TAG_FUNCTION)
		goto read_mismatch;

	      length = gcov_read_unsigned ();
	      if (!length)
		/* This function did not appear in the other program.
		   We have nothing to merge.  */
		continue;

	      if (length != GCOV_TAG_FUNCTION_LENGTH)
		goto read_mismatch;
	      
	      if (!gfi_ptr || gfi_ptr->key != gi_ptr)
		{
		  /* This function appears in the other program.  We
		     need to buffer the information in order to write
		     it back out -- we'll be inserting data before
		     this point, so cannot simply keep the data in the
		     file.  */
		  fn_tail = buffer_fn_data (gi_filename,
					    gi_ptr, fn_tail, f_ix);
		  if (!fn_tail)
		    goto read_mismatch;
		  continue;
		}

	      length = gcov_read_unsigned ();
	      if (length != gfi_ptr->ident)
		goto read_mismatch;
	      
	      length = gcov_read_unsigned ();
	      if (length != gfi_ptr->lineno_checksum)
		goto read_mismatch;
	      
	      length = gcov_read_unsigned ();
	      if (length != gfi_ptr->cfg_checksum)
		goto read_mismatch;
	      
	      ci_ptr = gfi_ptr->ctrs;
	      for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
		{
		  gcov_merge_fn merge = gi_ptr->merge[t_ix];

		  if (!merge)
		    continue;

		  tag = gcov_read_unsigned ();
		  length = gcov_read_unsigned ();
		  if (tag != GCOV_TAG_FOR_COUNTER (t_ix)
		      || length != GCOV_TAG_COUNTER_LENGTH (ci_ptr->num))
		    goto read_mismatch;
		  (*merge) (ci_ptr->values, ci_ptr->num);
		  ci_ptr++;
		}
	      if ((error = gcov_is_error ()))
		goto read_error;
	    }

	  if (tag)
	    {
	    read_mismatch:;
	      fprintf (stderr, "profiling:%s:Merge mismatch for %s %u\n",
		       gi_filename, f_ix >= 0 ? "function" : "summary",
		       f_ix < 0 ? -1 - f_ix : f_ix);
	      goto read_fatal;
	    }
	}
      goto rewrite;

    read_error:;
      fprintf (stderr, "profiling:%s:%s merging\n", gi_filename,
	       error < 0 ? "Overflow": "Error");

      goto read_fatal;

    rewrite:;
      gcov_rewrite ();
      if (!summary_pos)
	{
	  memset (&prg, 0, sizeof (prg));
	  summary_pos = eof_pos;
	}

      /* Merge the summaries.  */
      for (t_ix = 0; t_ix < GCOV_COUNTERS_SUMMABLE; t_ix++)
	{
	  cs_prg = &prg.ctrs[t_ix];
	  cs_tprg = &this_prg.ctrs[t_ix];
	  cs_all = &all_prg.ctrs[t_ix];

	  if (gi_ptr->merge[t_ix])
	    {
	      if (!cs_prg->runs++)
		cs_prg->num = cs_tprg->num;
	      cs_prg->sum_all += cs_tprg->sum_all;
	      if (cs_prg->run_max < cs_tprg->run_max)
		cs_prg->run_max = cs_tprg->run_max;
	      cs_prg->sum_max += cs_tprg->run_max;
	    }
	  else if (cs_prg->runs)
	    goto read_mismatch;

	  if (!cs_all->runs && cs_prg->runs)
	    memcpy (cs_all, cs_prg, sizeof (*cs_all));
	  else if (!all_prg.checksum
		   && (!GCOV_LOCKED || cs_all->runs == cs_prg->runs)
		   && memcmp (cs_all, cs_prg, sizeof (*cs_all)))
	    {
	      fprintf (stderr, "profiling:%s:Invocation mismatch - some data files may have been removed%s\n",
		       gi_filename, GCOV_LOCKED
		       ? "" : " or concurrently updated without locking support");
	      all_prg.checksum = ~0u;
	    }
	}

      prg.checksum = crc32;

      /* Write out the data.  */
      if (!eof_pos)
	{
	  gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION);
	  gcov_write_unsigned (gi_ptr->stamp);
	}

      if (summary_pos)
	gcov_seek (summary_pos);

      /* Generate whole program statistics.  */
      gcov_write_summary (GCOV_TAG_PROGRAM_SUMMARY, &prg);

      if (summary_pos < eof_pos)
	gcov_seek (eof_pos);

      /* Write execution counts for each function.  */
      for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; f_ix++)
	{
	  unsigned buffered = 0;

	  if (fn_buffer && fn_buffer->fn_ix == (unsigned)f_ix)
	    {
	      /* Buffered data from another program.  */
	      buffered = 1;
	      gfi_ptr = &fn_buffer->info;
	      length = GCOV_TAG_FUNCTION_LENGTH;
	    }
	  else
	    {
	      gfi_ptr = gi_ptr->functions[f_ix];
	      if (gfi_ptr && gfi_ptr->key == gi_ptr)
		length = GCOV_TAG_FUNCTION_LENGTH;
	      else
		length = 0;
	    }
	  
	  gcov_write_tag_length (GCOV_TAG_FUNCTION, length);
	  if (!length)
	    continue;
	  
	  gcov_write_unsigned (gfi_ptr->ident);
	  gcov_write_unsigned (gfi_ptr->lineno_checksum);
	  gcov_write_unsigned (gfi_ptr->cfg_checksum);

	  ci_ptr = gfi_ptr->ctrs;
	  for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
	    {
	      if (!gi_ptr->merge[t_ix])
		continue;

	      n_counts = ci_ptr->num;
	      gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
				     GCOV_TAG_COUNTER_LENGTH (n_counts));
	      gcov_type *c_ptr = ci_ptr->values;
	      while (n_counts--)
		gcov_write_counter (*c_ptr++);
	      ci_ptr++;
	    }
	  if (buffered)
	    fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
	}

      gcov_write_unsigned (0);

    read_fatal:;
      while (fn_buffer)
	fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);

      if ((error = gcov_close ()))
	  fprintf (stderr, error  < 0 ?
		   "profiling:%s:Overflow writing\n" :
		   "profiling:%s:Error writing\n",
		   gi_filename);
    }
}