diff mbox

[RFC] libgcov.c re-factoring and offline profile-tool

Message ID CAF1bQ=QxT8i=dyKeKor-ze_YADDBQZo+Q2GqiCqOCVSrZKRKaw@mail.gmail.com
State New
Headers show

Commit Message

Rong Xu Nov. 7, 2013, 11:34 p.m. UTC
On Thu, Nov 7, 2013 at 9:40 AM, Joseph S. Myers <joseph@codesourcery.com> wrote:
> On Thu, 7 Nov 2013, Rong Xu wrote:
>
>> Thanks Joseph for these detailed comments/suggestions.
>> The fixed patch is attached to this email.
>> The only thing left out is the Texinfo manual. Do you mean this tool
>> should have its it's own texi file in gcc/doc?
>
> Its own texi file, probably included as a chapter by gcc.texi (like
> gcov.texi is).

OK. will add this in.

My last patch that changes the command handling is actually broken.
Please ignore that patch and use the one in this email.

Also did some minor adjust of the code in libgcov.

Thanks,

-Rong


>
> --
> Joseph S. Myers
> joseph@codesourcery.com

Index: libgcc/libgcov-tool.c
===================================================================
--- libgcc/libgcov-tool.c	(revision 0)
+++ libgcc/libgcov-tool.c	(revision 0)
@@ -0,0 +1,802 @@
+/* GCC Profile-tool support.
+   Contributed by Rong Xu <xur@google.com>.
+   Copyright (C) 2013 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* work around the poisoned malloc/calloc in system.h.  */
+#ifndef xmalloc
+#define xmalloc malloc 
+#endif
+#ifndef xcalloc
+#define xcalloc calloc 
+#endif
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "intl.h"
+#include "diagnostic.h"
+#include "version.h"
+#include "demangle.h"
+
+#define L_gcov 1
+#define L_gcov_merge_add 1
+#define L_gcov_merge_single 1
+#define L_gcov_merge_delta 1
+#define L_gcov_merge_ior 1
+#define IN_PROFILE_TOOL 1
+#define GCOV_FOR_HOST_BUILD 1
+
+/* We need the dumping and merge part of code in libgcov.  */
+#include "libgcov-driver.c"
+#include "libgcov-merge.c"
+
+/* Verbose mode for debug.  */
+static int verbose;
+
+/* Set verbose flag.  */
+void gcov_tool_set_verbose (void)
+{
+  verbose = 1;
+}
+
+/* -------- Read Gcda and Reconstruct GCOV_INFO ----------- */
+
+#include "obstack.h"
+#include <unistd.h>
+#include <ftw.h>
+
+static void tag_function (const char *, unsigned, unsigned);
+static void tag_blocks (const char *, unsigned, unsigned);
+static void tag_arcs (const char *, unsigned, unsigned);
+static void tag_lines (const char *, unsigned, unsigned);
+static void tag_counters (const char *, unsigned, unsigned);
+static void tag_summary (const char *, unsigned, unsigned);
+
+/* The gcov_info for the first module.  */
+static struct gcov_info *curr_gcov_info;
+/* The gcov_info being processed.  */
+static struct gcov_info *gcov_info_head;
+/* This variable contains all the functions in current module.  */
+static struct obstack fn_info;
+/* The function being processed.  */
+static struct gcov_fn_info *curr_fn_info;
+/* The number of functions seen so far.  */
+static unsigned num_fn_info;
+/* This variable contains all the counters for current module.  */
+static int k_ctrs_mask[GCOV_COUNTERS];
+/* The kind of counters that have been seen.  */
+static struct gcov_ctr_info k_ctrs[GCOV_COUNTERS];
+/* Number of kind of counters that have been seen.  */
+static int k_ctrs_types;
+/* The longest length of all the filenames.  */
+static int max_filename_len;
+
+/* Merge functions for counters.  */
+static gcov_merge_fn ctr_merge_functions[GCOV_COUNTERS] = {
+    __gcov_merge_add,
+    __gcov_merge_add,
+    __gcov_merge_add,
+    __gcov_merge_single,
+    __gcov_merge_delta,
+    __gcov_merge_single,
+    __gcov_merge_add,
+    __gcov_merge_ior,
+};
+
+/* Set the fn_ctrs structure in fn_info.  */
+
+static void
+set_fn_ctrs (struct gcov_fn_info *fn_info)
+{
+  int j = 0, i;
+
+  for (i = 0; i < GCOV_COUNTERS; i++)
+    {
+      if (k_ctrs_mask[i] == 0)
+        continue;
+      fn_info->ctrs[j].num = k_ctrs[i].num;
+      fn_info->ctrs[j].values = k_ctrs[i].values;
+      j++;
+    }
+  if (k_ctrs_types == 0)
+    k_ctrs_types = j;
+  else
+    gcc_assert (j == k_ctrs_types);
+}
+
+typedef struct tag_format
+{
+    unsigned tag;
+    char const *name;
+    void (*proc) (const char *, unsigned, unsigned);
+} tag_format_t;
+
+static const tag_format_t tag_table[] =
+{
+  {0, "NOP", NULL},
+  {0, "UNKNOWN", NULL},
+  {0, "COUNTERS", tag_counters},
+  {GCOV_TAG_FUNCTION, "FUNCTION", tag_function},
+  {GCOV_TAG_BLOCKS, "BLOCKS", tag_blocks},
+  {GCOV_TAG_ARCS, "ARCS", tag_arcs},
+  {GCOV_TAG_LINES, "LINES", tag_lines},
+  {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary},
+  {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary},
+  {0, NULL, NULL}
+};
+
+/* Handler for reading funtion tag.  */
+
+static void
+tag_function (const char *filename ATTRIBUTE_UNUSED,
+	      unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+  int i;
+
+  /* write out previous fn_info. */
+  if (num_fn_info)
+    {
+      set_fn_ctrs (curr_fn_info);
+      /* obstack_grow (&fn_info, &curr_fn_info, sizeof (struct gcov_fn_info*)); */
+      obstack_ptr_grow (&fn_info, curr_fn_info);
+    }
+  /* first time.  */
+    {
+      /* Here we over allocate a bit, using GCOV_COUNTERS instead of the actual active
+         counter types.  */
+      curr_fn_info = (struct gcov_fn_info *) xcalloc (sizeof (struct gcov_fn_info)
+                              + GCOV_COUNTERS * sizeof (struct gcov_ctr_info), 1);
+    }
+
+  for (i = 0; i < GCOV_COUNTERS; i++)
+     k_ctrs[i].num = 0;
+  k_ctrs_types = 0;
+
+  curr_fn_info->key = curr_gcov_info;
+  curr_fn_info->ident = gcov_read_unsigned ();
+  curr_fn_info->lineno_checksum = gcov_read_unsigned ();
+  curr_fn_info->cfg_checksum = gcov_read_unsigned ();
+  num_fn_info++;
+
+  if (verbose)
+    fprintf (stdout, "tag one function id=%d\n", curr_fn_info->ident);
+}
+
+/* Handler for reading block tag.  */
+
+static void
+tag_blocks (const char *filename ATTRIBUTE_UNUSED,
+	    unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+  gcc_assert (0);
+}
+
+/* Handler for reading flow arc tag.  */
+
+static void
+tag_arcs (const char *filename ATTRIBUTE_UNUSED,
+	  unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+  gcc_assert (0);
+}
+
+/* Handler for reading line tag.  */
+
+static void
+tag_lines (const char *filename ATTRIBUTE_UNUSED,
+	   unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+  gcc_assert (0);
+}
+
+/* Handler for reading counters array tag.  */
+
+static void
+tag_counters (const char *filename ATTRIBUTE_UNUSED,
+	      unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+  unsigned n_counts = GCOV_TAG_COUNTER_NUM (length);
+  gcov_type *values;
+  unsigned ix;
+  unsigned tag_ix;
+
+  tag_ix = GCOV_COUNTER_FOR_TAG (tag);
+  gcc_assert (tag_ix < GCOV_COUNTERS);
+  k_ctrs_mask [tag_ix] = 1;
+  gcc_assert (k_ctrs[tag_ix].num == 0);
+  k_ctrs[tag_ix].num = n_counts;
+
+  k_ctrs[tag_ix].values = values = (gcov_type *)xmalloc (n_counts * sizeof (gcov_type));
+  gcc_assert (values);
+
+  for (ix = 0; ix != n_counts; ix++)
+    values[ix] = gcov_read_counter ();
+}
+
+/* Handler for reading summary tag.  */
+
+static void
+tag_summary (const char *filename ATTRIBUTE_UNUSED,
+	     unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+  struct gcov_summary summary;
+
+  gcov_read_summary (&summary);
+  if (!saved_summary_checksum)
+    saved_summary_checksum = summary.checksum;
+}
+
+/* This function is called at the end of reading a gcda file.
+   It flushes the contents in curr_fn_info to the gcov_info.  */
+
+static void
+read_gcda_finalize (struct gcov_info *obj_info)
+{
+  int i;
+
+  set_fn_ctrs (curr_fn_info);
+  obstack_ptr_grow (&fn_info, curr_fn_info);
+
+  /* We set the following fields: merge, n_functions, and functions.  */
+  obj_info->n_functions = num_fn_info;
+  obj_info->functions = (const struct gcov_fn_info**) obstack_finish (&fn_info);
+
+  /* wrap all the counter array. */
+  for (i=0; i< GCOV_COUNTERS; i++)
+    {
+      if (k_ctrs_mask[i])
+        obj_info->merge[i] = ctr_merge_functions[i];
+    }
+}
+
+/* Read the content of a gcda file, and return a gcov_info data structure.
+   Program level summary CURRENT_SUMMARY will also be updated.  */
+
+static struct gcov_info *
+read_gcda_file (const char *filename)
+{
+  unsigned tags[4];
+  unsigned depth = 0;
+  unsigned magic, version;
+  struct gcov_info *obj_info;
+  int i;
+
+  for (i=0; i< GCOV_COUNTERS; i++)
+    k_ctrs_mask[i] = 0;
+  k_ctrs_types = 0;
+
+  if (!gcov_open (filename))
+    {
+      fprintf (stderr, "%s:cannot open\n", filename);
+      return NULL;
+    }
+
+  /* read magic.  */
+  magic = gcov_read_unsigned ();
+  if (magic != GCOV_DATA_MAGIC)
+    {
+      fprintf (stderr, "%s:not a gcov data file\n", filename);
+      gcov_close ();
+      return NULL;
+    }
+
+  /* read version.  */
+  version = gcov_read_unsigned ();
+  if (version != GCOV_VERSION)
+    {
+      fprintf (stderr, "%s:incorrect gcov version %d vs %d \n", filename, version, GCOV_VERSION);
+      gcov_close ();
+      return NULL;
+    }
+
+  /* Instantiate a gcov_info object.  */
+  curr_gcov_info = obj_info = (struct gcov_info *) xcalloc (sizeof (struct gcov_info) +
+             sizeof (struct gcov_ctr_info) * GCOV_COUNTERS, 1);
+
+  obj_info->version = version;
+  obstack_init (&fn_info);
+  num_fn_info = 0;
+  curr_fn_info = 0;
+  {
+    char *str_dup = (char*) xmalloc (strlen (filename) + 1);
+    int len;
+
+    strcpy (str_dup, filename);
+    obj_info->filename = str_dup;
+    if ((len = strlen (filename)) > max_filename_len)
+      max_filename_len = len;
+  }
+
+  /* read stamp.  */
+  obj_info->stamp = gcov_read_unsigned ();
+
+  while (1)
+    {
+      gcov_position_t base;
+      unsigned tag, length;
+      tag_format_t const *format;
+      unsigned tag_depth;
+      int error;
+      unsigned mask;
+
+      tag = gcov_read_unsigned ();
+      if (!tag)
+	break;
+      length = gcov_read_unsigned ();
+      base = gcov_position ();
+      mask = GCOV_TAG_MASK (tag) >> 1;
+      for (tag_depth = 4; mask; mask >>= 8)
+	{
+	  if (((mask & 0xff) != 0xff))
+	    {
+	      fprintf (stderr, "warning: %s:tag `%08x' is invalid\n", filename, tag);
+	      break;
+	    }
+	  tag_depth--;
+	}
+      for (format = tag_table; format->name; format++)
+	if (format->tag == tag)
+	  goto found;
+      format = &tag_table[GCOV_TAG_IS_COUNTER (tag) ? 2 : 1];
+    found:;
+      if (tag)
+	{
+	  if (depth && depth < tag_depth)
+	    {
+	      if (!GCOV_TAG_IS_SUBTAG (tags[depth - 1], tag))
+		fprintf (stderr, "warning: %s:tag `%08x' is incorrectly nested\n",
+			filename, tag);
+	    }
+	  depth = tag_depth;
+	  tags[depth - 1] = tag;
+	}
+
+      if (format->proc)
+	(*format->proc) (filename, tag, length);
+
+      if (format->proc)
+	{
+	  unsigned long actual_length = gcov_position () - base;
+
+	  if (actual_length > length)
+	    fprintf (stderr,"warning: %s:record size mismatch %lu bytes overread\n",
+		    filename, actual_length - length);
+	  else if (length > actual_length)
+	    fprintf (stderr,"warning: %s:record size mismatch %lu bytes unread\n",
+		    filename, length - actual_length);
+	}
+      gcov_sync (base, length);
+      if ((error = gcov_is_error ()))
+	{
+	  fprintf (stderr,error < 0 ? "warning:%s:counter overflow at %lu\n" :
+		  "Warning:%s:read error at %lu\n", filename,
+		  (long unsigned) gcov_position ());
+	  break;
+	}
+    }
+
+  read_gcda_finalize (obj_info);
+  gcov_close ();
+
+  return obj_info;
+}
+
+#define GCOV_SUFFIX ".gcda"
+
+/* This will be called by ftw. Return a non-zero value
+   to stop the tree walk.  */
+
+static int
+ftw_read_file ( const char *filename,
+                const struct stat *status ATTRIBUTE_UNUSED,
+                int type)
+{
+  int filename_len;
+  int suffix_len;
+  struct gcov_info *obj_info;
+
+  /* Only read regular files.  */
+  if (type != FTW_F)
+    return 0;
+
+  filename_len = strlen (filename);
+  suffix_len = strlen (GCOV_SUFFIX);
+
+  if (filename_len <= suffix_len)
+    return 0;
+
+  if (strcmp(filename + filename_len - suffix_len, GCOV_SUFFIX))
+    return 0;
+
+   if (verbose)
+    fprintf (stderr, "reading file: %s\n", filename);
+
+  obj_info = read_gcda_file (filename);
+
+  obj_info->next = gcov_info_head;
+  gcov_info_head = obj_info;
+
+  return 0;
+}
+
+static inline void
+read_profile_dir_init (void)
+{
+  gcov_info_head = 0;
+}
+
+/* Driver for read a profile directory and convert into gcvo_info list in memory.
+   Return NULL on error,
+   Return the head of gcov_info list on success.
+   Note the file static variable GCOV_MAX_FILENAME is also set.  */
+
+struct gcov_info *
+gcov_read_profile_dir (const char* dir_name, int recompute_summary ATTRIBUTE_UNUSED)
+{
+  char *pwd;
+  int ret;
+
+  read_profile_dir_init ();
+
+  if (access (dir_name, R_OK) != 0)
+    {
+      fprintf (stderr, "cannot access directory %s\n", dir_name);
+      return NULL;
+    }
+  pwd = get_current_dir_name ();
+  gcc_assert (pwd);
+  ret = chdir (dir_name);
+  if (ret !=0)
+    {
+      fprintf (stderr, "%s is not a directory\n", dir_name);
+      return NULL;
+    }
+  ftw (".", ftw_read_file, 50);
+  ret = chdir (pwd);
+  free (pwd);
+
+
+  /* gcov_max_filename is defined in libgcov.c that recored
+     max filename len. We need to set it here to allocate the
+     array for dumping.  */
+  gcov_max_filename = max_filename_len;
+
+  return gcov_info_head;;
+}
+
+/* -------------- Merge Profile Counters ------------------ */
+
+/* Offline tool to manipulate profile data.
+   This tool targets on matched profiles. But it has some tolerance on
+   unmatched profiles.
+   When merging p1 to p2 (p2 is the dst),
+   * m.gcda in p1 but not in p2: append m.gcda to p2 with specified weight; emit warning
+   * m.gcda in p2 but not in p1: keep m.gcda in p2 and multiplying specified weight; emit warning.
+   * m.gcda in both p1 and p2:
+   ** p1->m.gcda->f checksum matches p2->m.gcda->f: simple merge.
+   ** p1->m.gcda->f checksum does not matches p2->m.gcda->f: keep p2->m.gcda->f and
+      drop p1->m.gcda->f. A warning is emitted.  */
+
+/* Add INFO2's counter to INFO1, multiplying weight W.  */
+
+static int
+gcov_merge (struct gcov_info *info1, struct gcov_info *info2, int w)
+{
+  unsigned f_ix;
+  unsigned n_functions = info1->n_functions;
+  int has_mismatch = 0;
+
+  gcc_assert (info2->n_functions == n_functions);
+  for (f_ix = 0; f_ix < n_functions; f_ix++)
+    {
+      unsigned t_ix;
+      const struct gcov_fn_info *gfi_ptr1 = info1->functions[f_ix];
+      const struct gcov_fn_info *gfi_ptr2 = info2->functions[f_ix];
+      const struct gcov_ctr_info *ci_ptr1, *ci_ptr2;
+
+      if (!gfi_ptr1 || gfi_ptr1->key != info1)
+        continue;
+      if (!gfi_ptr2 || gfi_ptr2->key != info2)
+        continue;
+
+      if (gfi_ptr1->cfg_checksum != gfi_ptr2->cfg_checksum)
+        {
+          fprintf (stderr, "in %s, cfg_checksum mismatch, skipping\n",
+                  info1->filename);
+          has_mismatch = 1;
+          continue;
+        }
+      ci_ptr1 = gfi_ptr1->ctrs;
+      ci_ptr2 = gfi_ptr2->ctrs;
+      for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++)
+        {
+          gcov_merge_fn merge1 = info1->merge[t_ix];
+          gcov_merge_fn merge2 = info2->merge[t_ix];
+
+          gcc_assert (merge1 == merge2);
+          if (!merge1)
+            continue;
+          gcc_assert (ci_ptr1->num == ci_ptr2->num);
+          (*merge1) (ci_ptr1->values, ci_ptr1->num, ci_ptr2->values, w);
+          ci_ptr1++;
+          ci_ptr2++;
+        }
+    }
+
+  return has_mismatch;
+}
+
+static struct gcov_info *
+find_match_gcov_info (struct gcov_info **array, int size, struct gcov_info *info)
+{
+  struct gcov_info *gi_ptr;
+  struct gcov_info *ret = NULL;
+  int i;
+
+  for (i = 0; i < size; i++)
+    {
+      gi_ptr = array[i];
+      if (gi_ptr == 0)
+        continue;
+      if (!strcmp (gi_ptr->filename, info->filename))
+        {
+          ret = gi_ptr;
+          array[i] = 0;
+          break;
+        }
+    }
+
+  if (ret && ret->n_functions != info->n_functions)
+    {
+      fprintf (stderr, "mismatched profiles in %s (%d functions"
+                       " vs %d functions)\n",
+                       ret->filename,
+                       ret->n_functions,
+                       info->n_functions);
+      ret = NULL;
+    }
+  return ret;
+}
+
+/* return 0 on success: without mismatch.
+   reutrn 1 on error.  */
+
+int
+gcov_profile_merge (struct gcov_info *tgt_profile, struct gcov_info *src_profile,
+                    int w1, int w2)
+{
+  struct gcov_info *gi_ptr;
+  struct gcov_info **tgt_infos;
+  struct gcov_info *tgt_tail;
+  struct gcov_info **in_src_not_tgt;
+  unsigned tgt_cnt = 0, src_cnt = 0;
+  unsigned unmatch_info_cnt = 0;
+  unsigned int i;
+
+  for (gi_ptr = tgt_profile; gi_ptr; gi_ptr = gi_ptr->next)
+    tgt_cnt++;
+  for (gi_ptr = src_profile; gi_ptr; gi_ptr = gi_ptr->next)
+    src_cnt++;
+  tgt_infos = (struct gcov_info **) xmalloc (sizeof (struct gcov_info *) * tgt_cnt);
+  gcc_assert (tgt_infos);
+  in_src_not_tgt = (struct gcov_info **) xmalloc (sizeof (struct gcov_info *) * src_cnt);
+  gcc_assert (in_src_not_tgt);
+
+  for (gi_ptr = tgt_profile, i = 0; gi_ptr; gi_ptr = gi_ptr->next, i++)
+    tgt_infos[i] = gi_ptr;
+
+  tgt_tail = tgt_infos[tgt_cnt - 1];
+
+  /* First pass on tgt_profile, we multiply w1 to all counters.  */
+  if (w1 > 1)
+    {
+       for (i = 0; i < tgt_cnt; i++)
+         gcov_merge (tgt_infos[i], tgt_infos[i], w1-1);
+    }
+
+  /* Second pass, add src_profile to the tgt_profile.  */
+  for (gi_ptr = src_profile; gi_ptr; gi_ptr = gi_ptr->next)
+    {
+      struct gcov_info *gi_ptr1;
+
+      gi_ptr1 = find_match_gcov_info (tgt_infos, tgt_cnt, gi_ptr);
+      if (gi_ptr1 == NULL)
+        {
+          in_src_not_tgt[unmatch_info_cnt++] = gi_ptr;
+          continue;
+        }
+      gcov_merge (gi_ptr1, gi_ptr, w2);
+    }
+
+#if 0
+  /* For modules left in the array. They are not in src_prfile.  */
+  for (i = 0; i < tgt_cnt; i++)
+    {
+      gi_ptr = tgt_infos[i];
+      if (!gi_ptr)
+        continue;
+      gcov_merge (gi_ptr, gi_ptr, w2);
+    }
+
+  /* For modules in src but not in tgt. We adjust the counter and append.  */
+  for (i = 0; i < unmatch_info_cnt; i++)
+    {
+      gi_ptr = in_src_not_tgt[i];
+      gcov_merge (gi_ptr, gi_ptr, w1+w2-1);
+    }
+#else
+  /* For modules in src but not in tgt. We adjust the counter and append.  */
+  for (i = 0; i < unmatch_info_cnt; i++)
+    {
+      gi_ptr = in_src_not_tgt[i];
+      gcov_merge (gi_ptr, gi_ptr, w2 - 1);
+      tgt_tail->next = gi_ptr;
+      tgt_tail = gi_ptr;
+    }
+#endif
+
+  return 0;
+}
+
+/* -------------- Scale Profile Counters ------------------ */
+
+/* Type of function used to normalize counters.  */
+typedef void (*gcov_scale_fn) (gcov_type *, gcov_unsigned_t, double);
+
+static void
+__gcov_scale_add (gcov_type *counters, unsigned n_counters, double f)
+{
+  for (; n_counters; counters++, n_counters--)
+    {
+      gcov_type val = *counters;
+      *counters = val * f;
+    }
+}
+
+/* Scale ior counters.  */
+
+static void
+__gcov_scale_ior (gcov_type *counters ATTRIBUTE_UNUSED,
+                      unsigned n_counters ATTRIBUTE_UNUSED,
+                      double f ATTRIBUTE_UNUSED)
+{
+  /* do nothing.  */
+}
+
+/* Scale delta counters.  */
+
+static void
+__gcov_scale_delta (gcov_type *counters, unsigned n_counters, double f)
+{
+  unsigned i, n_measures;
+
+  gcc_assert (!(n_counters % 4));
+  n_measures = n_counters / 4;
+  for (i = 0; i < n_measures; i++, counters += 4)
+    {
+      counters[2] *= f;
+      counters[3] *= f;
+    }
+}
+
+/* Scale single counters.  */
+
+static void
+__gcov_scale_single (gcov_type *counters, unsigned n_counters, double f)
+{
+  unsigned i, n_measures;
+
+  gcc_assert (!(n_counters % 3));
+  n_measures = n_counters / 3;
+  for (i = 0; i < n_measures; i++, counters += 3)
+    {
+      counters[1] *= f;
+      counters[2] *= f;
+    }
+}
+
+/* Scaling functions for counters.  */
+static gcov_scale_fn ctr_scale_functions[GCOV_COUNTERS] = {
+    __gcov_scale_add,
+    __gcov_scale_add,
+    __gcov_scale_add,
+    __gcov_scale_single,
+    __gcov_scale_delta,
+    __gcov_scale_single,
+    __gcov_scale_add,
+    __gcov_scale_ior,
+};
+
+
+/* Driver for scale profile counters.  */
+
+int
+gcov_profile_scale (struct gcov_info *profile, float scale_factor)
+{
+  struct gcov_info *gi_ptr;
+  unsigned f_ix;
+
+  if (verbose)
+    fprintf (stdout, "scale_factor is %f\n", scale_factor);
+
+  /* scale the counters.  */
+  for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next)
+    for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
+      {
+        unsigned t_ix;
+        const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix];
+        const struct gcov_ctr_info *ci_ptr;
+
+        if (!gfi_ptr || gfi_ptr->key != gi_ptr)
+          continue;
+
+        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;
+            (*ctr_scale_functions[t_ix]) (ci_ptr->values, ci_ptr->num, scale_factor);
+            ci_ptr++;
+          }
+      }
+
+  return 0;
+}
+
+/* Driver for normalize profile counters.  */
+
+int
+gcov_profile_normalize (struct gcov_info *profile, gcov_type max_val)
+{
+  struct gcov_info *gi_ptr;
+  gcov_type curr_max_val = 0;
+  unsigned f_ix;
+  unsigned int i;
+  float scale_factor;
+
+  /* get the larest count value.  */
+  for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next)
+    for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
+      {
+        unsigned t_ix;
+        const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix];
+        const struct gcov_ctr_info *ci_ptr;
+
+        if (!gfi_ptr || gfi_ptr->key != gi_ptr)
+          continue;
+
+        ci_ptr = gfi_ptr->ctrs;
+        for (t_ix = 0; t_ix < 1; t_ix++)
+          {
+            for (i = 0; i < ci_ptr->num; i++)
+              if (ci_ptr->values[i] > curr_max_val)
+                curr_max_val = ci_ptr->values[i];
+            ci_ptr++;
+          }
+      }
+
+  scale_factor = (float)max_val / curr_max_val;
+  if (verbose)
+    fprintf (stdout, "max_val is %lld\n", (long long) curr_max_val);
+
+  return gcov_profile_scale (profile, scale_factor);
+}
Index: gcc/gcov-io.h
===================================================================
--- gcc/gcov-io.h	(revision 204285)
+++ gcc/gcov-io.h	(working copy)
@@ -164,7 +164,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
 #ifndef GCC_GCOV_IO_H
 #define GCC_GCOV_IO_H
 
-#if IN_LIBGCOV
+#if (IN_LIBGCOV && !IN_PROFILE_TOOL)
 /* About the target */
 
 #if BITS_PER_UNIT == 8
@@ -214,7 +214,7 @@ typedef unsigned gcov_type_unsigned __attribute__
 typedef unsigned gcov_unsigned_t;
 typedef unsigned gcov_position_t;
 /* gcov_type is typedef'd elsewhere for the compiler */
-#if IN_GCOV
+#if IN_GCOV || IN_PROfILE_TOOL
 #define GCOV_LINKAGE static
 typedef HOST_WIDEST_INT gcov_type;
 typedef unsigned HOST_WIDEST_INT gcov_type_unsigned;
@@ -240,7 +240,9 @@ typedef unsigned HOST_WIDEST_INT gcov_type_unsigne
    is not also used in a DSO.  */
 #if IN_LIBGCOV
 
+#if !IN_PROFILE_TOOL
 #include "tconfig.h"
+#endif /* !IN_PROFILE_TOOL */
 
 #define gcov_var __gcov_var
 #define gcov_open __gcov_open
@@ -259,7 +261,10 @@ typedef unsigned HOST_WIDEST_INT gcov_type_unsigne
 
 /* Poison these, so they don't accidentally slip in.  */
 #pragma GCC poison gcov_write_string gcov_write_tag gcov_write_length
-#pragma GCC poison gcov_read_string gcov_sync gcov_time gcov_magic
+#pragma GCC poison gcov_time gcov_magic
+#if !IN_PROFILE_TOOL
+#pragma GCC poison gcov_read_string gcov_sync
+#endif /* !IN_PROFILE_TOOL */
 
 #ifdef HAVE_GAS_HIDDEN
 #define ATTRIBUTE_HIDDEN  __attribute__ ((__visibility__ ("hidden")))
@@ -467,7 +472,8 @@ struct gcov_fn_info
 };
 
 /* Type of function used to merge counters.  */
-typedef void (*gcov_merge_fn) (gcov_type *, gcov_unsigned_t);
+typedef void (*gcov_merge_fn) (gcov_type *, gcov_unsigned_t,
+                               gcov_type *, unsigned);
 
 /* Information about a single object file.  */
 struct gcov_info
@@ -482,8 +488,12 @@ struct gcov_info
 					  unused) */
   
   unsigned n_functions;		/* number of functions */
+#if !IN_PROFILE_TOOL
   const struct gcov_fn_info *const *functions; /* pointer to pointers
 					          to function information  */
+#else
+  const struct gcov_fn_info **functions;
+#endif /* !IN_PROFILE_TOOL */
 };
 
 /* Register a new object file module.  */
@@ -499,22 +509,28 @@ extern void __gcov_reset (void);
 extern void __gcov_dump (void);
 
 /* The merge function that just sums the counters.  */
-extern void __gcov_merge_add (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
+extern void __gcov_merge_add (gcov_type *, unsigned, gcov_type *,
+                              unsigned) ATTRIBUTE_HIDDEN;
 
 /* The merge function to choose the most common value.  */
-extern void __gcov_merge_single (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
+extern void __gcov_merge_single (gcov_type *, unsigned, gcov_type *,
+                              unsigned) ATTRIBUTE_HIDDEN;
 
 /* The merge function to choose the most common difference between
    consecutive values.  */
-extern void __gcov_merge_delta (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
+extern void __gcov_merge_delta (gcov_type *, unsigned, gcov_type *,
+                              unsigned) ATTRIBUTE_HIDDEN;
 
 /* The merge function that just ors the counters together.  */
-extern void __gcov_merge_ior (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
+extern void __gcov_merge_ior (gcov_type *, unsigned, gcov_type *,
+                              unsigned) ATTRIBUTE_HIDDEN;
 
 /* The profiler functions.  */
 extern void __gcov_interval_profiler (gcov_type *, gcov_type, int, unsigned);
 extern void __gcov_pow2_profiler (gcov_type *, gcov_type);
 extern void __gcov_one_value_profiler (gcov_type *, gcov_type);
+extern void __gcov_indirect_call_profiler (gcov_type*, gcov_type,
+                                           void*, void*);
 extern void __gcov_indirect_call_profiler_v2 (gcov_type, void *);
 extern void __gcov_average_profiler (gcov_type *, gcov_type);
 extern void __gcov_ior_profiler (gcov_type *, gcov_type);
@@ -538,7 +554,7 @@ extern int __gcov_execve (const char *, char  *con
 /* Optimum number of gcov_unsigned_t's read from or written to disk.  */
 #define GCOV_BLOCK_SIZE (1 << 10)
 
-GCOV_LINKAGE struct gcov_var
+struct gcov_var
 {
   FILE *file;
   gcov_position_t start;	/* Position of first byte of block */
@@ -560,7 +576,8 @@ extern int __gcov_execve (const char *, char  *con
   size_t alloc;
   gcov_unsigned_t *buffer;
 #endif
-} gcov_var ATTRIBUTE_HIDDEN;
+};
+extern struct gcov_var gcov_var ATTRIBUTE_HIDDEN;
 
 /* Functions for reading and writing gcov files. In libgcov you can
    open the file for reading then writing. Elsewhere you can open the
@@ -587,7 +604,7 @@ GCOV_LINKAGE gcov_unsigned_t gcov_read_unsigned (v
 GCOV_LINKAGE gcov_type gcov_read_counter (void) ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE void gcov_read_summary (struct gcov_summary *) ATTRIBUTE_HIDDEN;
 
-#if IN_LIBGCOV
+#if (IN_LIBGCOV)
 /* Available only in libgcov */
 GCOV_LINKAGE void gcov_write_counter (gcov_type) ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE void gcov_write_tag_length (gcov_unsigned_t, gcov_unsigned_t)
@@ -597,7 +614,9 @@ GCOV_LINKAGE void gcov_write_summary (gcov_unsigne
     ATTRIBUTE_HIDDEN;
 static void gcov_rewrite (void);
 GCOV_LINKAGE void gcov_seek (gcov_position_t /*position*/) ATTRIBUTE_HIDDEN;
-#else
+#endif
+
+#if (!IN_LIBGCOV || IN_PROFILE_TOOL)
 /* Available outside libgcov */
 GCOV_LINKAGE const char *gcov_read_string (void);
 GCOV_LINKAGE void gcov_sync (gcov_position_t /*base*/,
Index: gcc/gcov-io.c
===================================================================
--- gcc/gcov-io.c	(revision 204285)
+++ gcc/gcov-io.c	(working copy)
@@ -27,7 +27,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
 /* Routines declared in gcov-io.h.  This file should be #included by
    another source file, after having #included gcov-io.h.  */
 
-#if !IN_GCOV
+#if (!IN_GCOV)
 static void gcov_write_block (unsigned);
 static gcov_unsigned_t *gcov_write_words (unsigned);
 #endif
@@ -36,6 +36,10 @@ static const gcov_unsigned_t *gcov_read_words (uns
 static void gcov_allocate (unsigned);
 #endif
 
+#if IN_LIBGCOV >= 0
+/*GCOV_LINKAGE*/ struct gcov_var gcov_var ATTRIBUTE_HIDDEN;
+#endif
+
 static inline gcov_unsigned_t from_file (gcov_unsigned_t value)
 {
 #if !IN_LIBGCOV
@@ -503,7 +507,7 @@ gcov_read_counter (void)
    buffer, or NULL on empty string. You must copy the string before
    calling another gcov function.  */
 
-#if !IN_LIBGCOV
+#if !IN_LIBGCOV || IN_PROFILE_TOOL
 GCOV_LINKAGE const char *
 gcov_read_string (void)
 {
@@ -580,7 +584,7 @@ gcov_read_summary (struct gcov_summary *summary)
     }
 }
 
-#if !IN_LIBGCOV
+#if !IN_LIBGCOV || IN_PROFILE_TOOL
 /* Reset to a known position.  BASE should have been obtained from
    gcov_position, LENGTH should be a record length.  */
 
Index: gcc/profile-tool.c
===================================================================
--- gcc/profile-tool.c	(revision 0)
+++ gcc/profile-tool.c	(revision 0)
@@ -0,0 +1,410 @@
+/* GCC Profile-tool suuport.
+   Contributed by Rong Xu <xur@google.com>.
+   Copyright (C) 2013 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#define IN_PROFILE_TOOL 1
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "intl.h"
+#include "diagnostic.h"
+#include "version.h"
+#include "gcov-io.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <ftw.h>
+
+extern int gcov_profile_merge (struct gcov_info*, struct gcov_info*, int, int);
+extern int gcov_profile_normalize (struct gcov_info*, gcov_type);
+extern int gcov_profile_scale (struct gcov_info*, float);
+extern struct gcov_info* gcov_read_profile_dir (const char*, int);
+extern void gcov_exit (void);
+extern void set_gcov_list (struct gcov_info *);
+extern void gcov_tool_set_verbose (void);
+
+
+static int verbose;
+
+#define GCDA_SUFFIX ".gcda"
+
+static int
+unlink_file (const char *name,
+             const struct stat *status ATTRIBUTE_UNUSED,
+             int type ATTRIBUTE_UNUSED,
+             struct FTW *ftwbuf ATTRIBUTE_UNUSED)
+{
+  int ret = 0;
+  int len = strlen (name);
+  int len1 = strlen (GCDA_SUFFIX);
+  
+  if (len > len1 && !strncmp (len -len1 + name, GCDA_SUFFIX, len1))
+    remove (name);
+
+  if (ret)
+    {
+      fnotice (stderr, "error in removing %s\n", name);
+      exit (FATAL_EXIT_CODE);
+    }
+
+  return ret;
+}
+
+static int
+unlink_dir (const char *path)
+{
+    return nftw(path, unlink_file, 64, FTW_DEPTH | FTW_PHYS);
+}
+
+static int
+profile_merge (const char *d1, const char *d2, const char *out, int w1, int w2)
+{
+  char *pwd;
+  int ret;
+  struct gcov_info * d1_profile;
+  struct gcov_info * d2_profile;
+
+
+  d1_profile = gcov_read_profile_dir (d1, 0);
+  if (!d1_profile)
+    return 1;
+
+  if (d2)
+    {
+      d2_profile = gcov_read_profile_dir (d2, 0);
+      if (!d2_profile)
+        return 1;
+
+      /* the actual merge: we overwrite to d1_profile.  */
+      ret = gcov_profile_merge (d1_profile, d2_profile, w1, w2);
+
+      if (ret)
+        return ret;
+    }
+
+  /* output */
+  unlink_dir (out);
+  mkdir (out, 0755);
+  pwd = get_current_dir_name ();
+  gcc_assert (pwd);
+  ret = chdir (out);
+  gcc_assert (ret == 0);
+
+  set_gcov_list (d1_profile);
+  gcov_exit ();
+
+  ret = chdir (pwd);
+  free (pwd);
+  return 0;
+}
+
+static void
+print_merge_usage_message (int error_p)
+{
+  FILE *file = error_p ? stderr : stdout;
+
+  fnotice (file, "  merge [options] <dir1> <dir2>              Merge coverage file contents\n");
+  fnotice (file, "    -v, --verbose                            Verbose mode\n");
+  fnotice (file, "    -o <dir>, --output <dir>                 Output directory\n");
+  fnotice (file, "    -w <w1,w2>, --weight <w1,w2>             Set weights (float point values)\n");
+}
+
+static const struct option merge_options[] =
+{
+  { "verbose",                no_argument,       NULL, 'v' },
+  { "output",                 required_argument, NULL, 'o' },
+  { "weight",                 required_argument, NULL, 'w' },
+  { 0, 0, 0, 0 }
+};
+
+static void
+merge_usage (void)
+{
+  fnotice (stderr, "Merge subcomand usage:");
+  print_merge_usage_message (true);
+  exit (FATAL_EXIT_CODE);
+}
+
+static int
+do_merge (int argc, char **argv)
+{
+  int opt;
+  int ret;
+  const char *output_dir = 0;
+  int w1 = 1, w2 = 1;
+
+  while ((opt = getopt_long (argc, argv, "vo:w:", merge_options, NULL)) != -1)
+    {
+      switch (opt)
+        {
+        case 'v':
+          verbose = 1;
+          gcov_tool_set_verbose ();
+          break;
+        case 'o':
+          output_dir = optarg;
+          break;
+        case 'w':
+          sscanf (optarg, "%d,%d", &w1, &w2);
+          if (w1 < 0 || w2 < 0)
+            {
+              fnotice (stderr, "weights need to be non-negative\n");
+              exit (FATAL_EXIT_CODE);
+            }
+          break;
+        default:
+          merge_usage ();
+        }
+    }
+
+  if (output_dir == NULL)
+    output_dir = "merged_profile";
+
+  if (argc - optind == 2)
+    ret = profile_merge (argv[optind], argv[optind+1], output_dir, w1, w2);
+  else
+    merge_usage ();
+
+  return ret;
+}
+
+static int
+profile_rewrite (const char *d1, const char *out, long long n_val, float scale)
+{
+  char *pwd;
+  int ret;
+  struct gcov_info * d1_profile;
+
+
+  d1_profile = gcov_read_profile_dir (d1, 0);
+  if (!d1_profile)
+    return 1;
+
+  /* output */
+  unlink_dir (out);
+  mkdir (out, 0755);
+  pwd = get_current_dir_name ();
+  gcc_assert (pwd);
+  ret = chdir (out);
+  gcc_assert (ret == 0);
+
+  if (n_val)
+    gcov_profile_normalize (d1_profile, (gcov_type) n_val);
+  else
+    gcov_profile_scale (d1_profile, scale);
+
+  set_gcov_list (d1_profile);
+  gcov_exit ();
+
+  ret = chdir (pwd);
+  free (pwd);
+  return 0;
+}
+
+static void
+print_rewrite_usage_message (int error_p)
+{
+  FILE *file = error_p ? stderr : stdout;
+
+  fnotice (file, "  rewrite [options] <dir>                    Rewrite coverage file contents\n");
+  fnotice (file, "    -v, --verbose                            Verbose mode\n");
+  fnotice (file, "    -o <dir>, --output <dir>                 Output directory\n");
+  fnotice (file, "    -s <float>, --scale <float>              Scale the profile counters\n");
+  fnotice (file, "    -n <long long>, --normalize <long long>  Normalize the profile\n");
+}
+
+static const struct option rewrite_options[] =
+{
+  { "verbose",                no_argument,       NULL, 'v' },
+  { "output",                 required_argument, NULL, 'o' },
+  { "scale",                  required_argument, NULL, 's' },
+  { "normalize",              required_argument, NULL, 'n' },
+  { 0, 0, 0, 0 }
+};
+
+static void
+rewrite_usage (void)
+{
+  fnotice (stderr, "Rewrite subcommand usage:");
+  print_rewrite_usage_message (true);
+  exit (FATAL_EXIT_CODE);
+}
+
+static int
+do_rewrite (int argc, char **argv)
+{
+  int opt;
+  int ret;
+  const char *output_dir = 0;
+  long long normalize_val = 0;
+  float scale = 1.0;
+
+  while ((opt = getopt_long (argc, argv, "vo:s:n:", rewrite_options, NULL)) != -1)
+    {
+      switch (opt)
+        {
+        case 'v':
+          verbose = 1;
+          gcov_tool_set_verbose ();
+          break;
+        case 'o':
+          output_dir = optarg;
+          break;
+        case 'n':
+          if (scale != 1.0)
+            {
+              fnotice (stderr, "scaling cannot co-exist with normalization\n");
+              scale = 1.0;
+            }
+          normalize_val = atoll (optarg);
+          break;
+        case 's':
+          sscanf (optarg, "%f", &scale);
+          if (scale < 0.0)
+            {
+              fnotice (stderr, "scale needs to be non-negative\n");
+              exit (FATAL_EXIT_CODE);
+            }
+          if (normalize_val != 0)
+            {
+              fnotice (stderr, "normalization cannot co-exist with scaling\n");
+              normalize_val = 0;
+            }
+          break;
+        default:
+          rewrite_usage ();
+        }
+    }
+
+  if (output_dir == NULL)
+    output_dir = "rewrite_profile";
+
+  if (argc - optind == 1)
+    ret = profile_rewrite (argv[optind],  output_dir, normalize_val, scale);
+  else
+    rewrite_usage ();
+
+  return ret;
+}
+
+/* Print a usage message and exit.  If ERROR_P is nonzero, this is an error,
+   otherwise the output of --help.  */
+
+static void
+print_usage (int error_p)
+{
+  FILE *file = error_p ? stderr : stdout;
+  int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE;
+
+  fnotice (file, "Usage: %s [OPTION]... SUB_COMMAND [OPTION]...\n\n", progname);
+  fnotice (file, "Offline tool to handle gcda counts.\n\n");
+  fnotice (file, "  -h, --help                                 Print this help, then exit\n");
+  fnotice (file, "  -v, --version                              Print version number, then exit\n");
+  print_merge_usage_message (error_p);
+  print_rewrite_usage_message (error_p);
+  fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
+           bug_report_url);
+  exit (status);
+}
+
+/* Print version information and exit.  */
+
+static void
+print_version (void)
+{
+  fnotice (stdout, "%s %s%s\n", progname, pkgversion_string, version_string);
+  fprintf (stdout, "Copyright %s 2013 Free Software Foundation, Inc.\n",
+           _("(C)"));
+  fnotice (stdout,
+           _("This is free software; see the source for copying conditions.\n"
+             "There is NO warranty; not even for MERCHANTABILITY or \n"
+             "FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
+  exit (SUCCESS_EXIT_CODE);
+}
+
+static const struct option options[] =
+{
+  { "help",                 no_argument,       NULL, 'h' },
+  { "version",              no_argument,       NULL, 'v' },
+  { 0, 0, 0, 0 }
+};
+
+/* Process args, return index to first non-arg.  */
+
+static int
+process_args (int argc, char **argv)
+{
+  int opt;
+
+  while ((opt = getopt_long (argc, argv, "+hv", options, NULL)) != -1)  
+    {    
+      switch (opt)
+        {    
+        case 'h': 
+          print_usage (false);
+          /* print_usage will exit.  */
+        case 'v':
+          print_version ();
+          /* print_version will exit.  */
+        default:
+          print_usage (true);
+          /* print_usage will exit.  */
+        }
+    }
+
+  return optind;
+}
+
+int
+main (int argc, char **argv)
+{
+  const char *p;
+  const char *sub_command;
+
+  p = argv[0] + strlen (argv[0]);
+  while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
+    --p;
+  progname = p;
+
+  xmalloc_set_program_name (progname);
+
+  /* Unlock the stdio streams.  */
+  unlock_std_streams ();
+
+  gcc_init_libintl ();
+
+  diagnostic_initialize (global_dc, 0);
+
+  /* Handle response files.  */
+  expandargv (&argc, &argv);
+
+  process_args (argc, argv);
+  if (optind >= argc)
+    print_usage (true);
+
+  sub_command = argv[optind];
+
+  if (!strcmp (sub_command, "merge"))
+    return do_merge (argc - optind, argv + optind);
+  else if (!strcmp (sub_command, "rewrite"))
+    return do_rewrite (argc - optind, argv + optind);
+
+  print_usage (true);
+}
Index: gcc/Makefile.in
===================================================================
--- gcc/Makefile.in	(revision 204285)
+++ gcc/Makefile.in	(working copy)
@@ -123,7 +123,8 @@ SUBDIRS =@subdirs@ build
 
 # Selection of languages to be made.
 CONFIG_LANGUAGES = @all_selected_languages@
-LANGUAGES = c gcov$(exeext) gcov-dump$(exeext) $(CONFIG_LANGUAGES)
+LANGUAGES = c gcov$(exeext) gcov-dump$(exeext) profile-tool$(exeext) \
+	    $(CONFIG_LANGUAGES)
 
 # Default values for variables overridden in Makefile fragments.
 # CFLAGS is for the user to override to, e.g., do a cross build with -O2.
@@ -196,6 +197,7 @@ GCC_WARN_CXXFLAGS = $(LOOSE_WARN) $($(@D)-warn) $(
 # flex output may yield harmless "no previous prototype" warnings
 build/gengtype-lex.o-warn = -Wno-error
 gengtype-lex.o-warn = -Wno-error
+libgcov-tool.o-warn = -Wno-error
 
 # All warnings have to be shut off in stage1 if the compiler used then
 # isn't gcc; configure determines that.  WARN_CFLAGS will be either
@@ -1480,7 +1482,7 @@ ALL_HOST_FRONTEND_OBJS = $(foreach v,$(CONFIG_LANG
 ALL_HOST_BACKEND_OBJS = $(GCC_OBJS) $(OBJS) $(OBJS-libcommon) \
   $(OBJS-libcommon-target) @TREEBROWSER@ main.o c-family/cppspec.o \
   $(COLLECT2_OBJS) $(EXTRA_GCC_OBJS) $(GCOV_OBJS) $(GCOV_DUMP_OBJS) \
-  lto-wrapper.o
+  $(PROFILE_TOOL_OBJS) libgcov-tool.o lto-wrapper.o
 
 # This lists all host object files, whether they are included in this
 # compilation or not.
@@ -1505,6 +1507,7 @@ MOSTLYCLEANFILES = insn-flags.h insn-config.h insn
  $(SPECS) collect2$(exeext) gcc-ar$(exeext) gcc-nm$(exeext) \
  gcc-ranlib$(exeext) \
  gcov-iov$(build_exeext) gcov$(exeext) gcov-dump$(exeext) \
+ profile-tool$(exeect) \
  gengtype$(exeext) *.[0-9][0-9].* *.[si] *-checksum.c libbackend.a \
  libcommon-target.a libcommon.a libgcc.mk
 
@@ -2560,6 +2563,16 @@ GCOV_DUMP_OBJS = gcov-dump.o
 gcov-dump$(exeext): $(GCOV_DUMP_OBJS) $(LIBDEPS)
 	+$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_DUMP_OBJS) \
 		$(LIBS) -o $@
+libgcov-tool.o: $(srcdir)/../libgcc/libgcov-tool.c gcov-io.c $(GCOV_IO_H) \
+  $(srcdir)/../libgcc/libgcov-driver.c $(srcdir)/../libgcc/libgcov-driver-system.c \
+  $(srcdir)/../libgcc/libgcov-merge.c \
+  $(SYSTEM_H) coretypes.h $(TM_H) $(CONFIG_H) version.h intl.h $(DIAGNOSTIC_H)
+	+$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -o $@ $<
+
+PROFILE_TOOL_OBJS = profile-tool.o libgcov-tool.o
+profile-tool$(exeext): $(PROFILE_TOOL_OBJS) $(LIBDEPS)
+	+$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(PROFILE_TOOL_OBJS) \
+	  $(LIBS) -o $@
 #
 # Build the include directories.  The stamp files are stmp-* rather than
 # s-* so that mostlyclean does not force the include directory to
@@ -3183,6 +3196,13 @@ install-common: native lang.install-common install
 	    rm -f $(DESTDIR)$(bindir)/$(GCOV_INSTALL_NAME)$(exeext); \
 	    $(INSTALL_PROGRAM) gcov$(exeext) $(DESTDIR)$(bindir)/$(GCOV_INSTALL_NAME)$(exeext); \
 	fi
+# Install profile-tool if it was compiled.
+	-if [ -f profile-tool$(exeext) ]; \
+	then \
+	    rm -f $(DESTDIR)$(bindir)/$(PROFILE_TOOL_INSTALL_NAME)$(exeext); \
+	    $(INSTALL_PROGRAM) \
+	    profile-tool$(exeext) $(DESTDIR)$(bindir)/$(PROFILE_TOOL_INSTALL_NAME)$(exeext); \
+	fi
 
 # Install the driver program as $(target_noncanonical)-gcc,
 # $(target_noncanonical)-gcc-$(version), and also as gcc if native.

Comments

Xinliang David Li Nov. 8, 2013, 6:22 p.m. UTC | #1
in libgcov-driver.c

> /* Flag when the profile has already been dumped via __gcov_dump().  */
> static int gcov_dump_complete;

> inline void
> set_gcov_dump_complete (void)
>{
  > gcov_dump_complete = 1;
>}

>inline void
>reset_gcov_dump_complete (void)
>{
>  gcov_dump_complete = 0;
>}


These should be moved to libgcov-interface.c. Is there reason to not
mark them as static?

in libgcov-profiler.c, line 142

> #if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS)
> __thread

trailing whilte space.


>     || (VTABLE_USES_DESCRIPTORS && __gcov_indirect_call_callee
>         && *(void **) cur_func == *(void **) __gcov_indirect_call_callee))

trailing white space.


in libgcov-merge.c

1) I don't think you need in_mem flag. For in memory merge, the source != NULL.
2) document the new source and weight parameter -- especially the weight.
3) How do you deal with integer overflow ?

David






On Thu, Nov 7, 2013 at 3:34 PM, Rong Xu <xur@google.com> wrote:
> On Thu, Nov 7, 2013 at 9:40 AM, Joseph S. Myers <joseph@codesourcery.com> wrote:
>> On Thu, 7 Nov 2013, Rong Xu wrote:
>>
>>> Thanks Joseph for these detailed comments/suggestions.
>>> The fixed patch is attached to this email.
>>> The only thing left out is the Texinfo manual. Do you mean this tool
>>> should have its it's own texi file in gcc/doc?
>>
>> Its own texi file, probably included as a chapter by gcc.texi (like
>> gcov.texi is).
>
> OK. will add this in.
>
> My last patch that changes the command handling is actually broken.
> Please ignore that patch and use the one in this email.
>
> Also did some minor adjust of the code in libgcov.
>
> Thanks,
>
> -Rong
>
>
>>
>> --
>> Joseph S. Myers
>> joseph@codesourcery.com
Xinliang David Li Nov. 8, 2013, 6:37 p.m. UTC | #2
> 1) I don't think you need in_mem flag. For in memory merge, the source != NULL.

discard this comment.

David
>
>
>
>
>
>
> On Thu, Nov 7, 2013 at 3:34 PM, Rong Xu <xur@google.com> wrote:
>> On Thu, Nov 7, 2013 at 9:40 AM, Joseph S. Myers <joseph@codesourcery.com> wrote:
>>> On Thu, 7 Nov 2013, Rong Xu wrote:
>>>
>>>> Thanks Joseph for these detailed comments/suggestions.
>>>> The fixed patch is attached to this email.
>>>> The only thing left out is the Texinfo manual. Do you mean this tool
>>>> should have its it's own texi file in gcc/doc?
>>>
>>> Its own texi file, probably included as a chapter by gcc.texi (like
>>> gcov.texi is).
>>
>> OK. will add this in.
>>
>> My last patch that changes the command handling is actually broken.
>> Please ignore that patch and use the one in this email.
>>
>> Also did some minor adjust of the code in libgcov.
>>
>> Thanks,
>>
>> -Rong
>>
>>
>>>
>>> --
>>> Joseph S. Myers
>>> joseph@codesourcery.com
Rong Xu Nov. 8, 2013, 6:48 p.m. UTC | #3
On Fri, Nov 8, 2013 at 10:22 AM, Xinliang David Li <davidxl@google.com> wrote:
> in libgcov-driver.c
>
>> /* Flag when the profile has already been dumped via __gcov_dump().  */
>> static int gcov_dump_complete;
>
>> inline void
>> set_gcov_dump_complete (void)
>>{
>   > gcov_dump_complete = 1;
>>}
>
>>inline void
>>reset_gcov_dump_complete (void)
>>{
>>  gcov_dump_complete = 0;
>>}
>
>
> These should be moved to libgcov-interface.c. Is there reason to not
> mark them as static?

gcov_dump_complete is used in gcov_exit() which is in
libgcov-driver.c, but it's set in __gcov_dump and __gcov_reset, both
in libgcov-interface.c.
I want gcov_dump_complete a static. So I have to add to global
functions that accessible from libgcov-interface.c.
Another choice is to move __gcov_dump and __gcov_reset to
libgcov-driver.c and that will simplify the code.

>
> in libgcov-profiler.c, line 142
>
>> #if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS)
>> __thread
>
> trailing whilte space.
>

Will fix.

>
>>     || (VTABLE_USES_DESCRIPTORS && __gcov_indirect_call_callee
>>         && *(void **) cur_func == *(void **) __gcov_indirect_call_callee))
>
> trailing white space.
>

Will fix.

>
> in libgcov-merge.c
>
> 1) I don't think you need in_mem flag. For in memory merge, the source != NULL.
> 2) document the new source and weight parameter -- especially the weight.

Will do.

> 3) How do you deal with integer overflow ?

This is good question. gcvo_type is (typically) long long. I thought
the count value will not be nearly close to the max of long long.
(We did see overflow in compiler, but that's because of the scaling --
some of the scaling factors are really large.)

>
> David
>
>
>
>
>
>
> On Thu, Nov 7, 2013 at 3:34 PM, Rong Xu <xur@google.com> wrote:
>> On Thu, Nov 7, 2013 at 9:40 AM, Joseph S. Myers <joseph@codesourcery.com> wrote:
>>> On Thu, 7 Nov 2013, Rong Xu wrote:
>>>
>>>> Thanks Joseph for these detailed comments/suggestions.
>>>> The fixed patch is attached to this email.
>>>> The only thing left out is the Texinfo manual. Do you mean this tool
>>>> should have its it's own texi file in gcc/doc?
>>>
>>> Its own texi file, probably included as a chapter by gcc.texi (like
>>> gcov.texi is).
>>
>> OK. will add this in.
>>
>> My last patch that changes the command handling is actually broken.
>> Please ignore that patch and use the one in this email.
>>
>> Also did some minor adjust of the code in libgcov.
>>
>> Thanks,
>>
>> -Rong
>>
>>
>>>
>>> --
>>> Joseph S. Myers
>>> joseph@codesourcery.com
Rong Xu Nov. 8, 2013, 6:56 p.m. UTC | #4
A question about inhibit_libc.
When inhibit_libc is defined, we provide dummy functions for all the
__gcov_* methods.
Is it purposely to minimize the footprint?
I'm thinking to allow some codes that are independent of libc (and its
headers) under this. Is it OK?

-Rong

On Fri, Nov 8, 2013 at 10:48 AM, Rong Xu <xur@google.com> wrote:
> On Fri, Nov 8, 2013 at 10:22 AM, Xinliang David Li <davidxl@google.com> wrote:
>> in libgcov-driver.c
>>
>>> /* Flag when the profile has already been dumped via __gcov_dump().  */
>>> static int gcov_dump_complete;
>>
>>> inline void
>>> set_gcov_dump_complete (void)
>>>{
>>   > gcov_dump_complete = 1;
>>>}
>>
>>>inline void
>>>reset_gcov_dump_complete (void)
>>>{
>>>  gcov_dump_complete = 0;
>>>}
>>
>>
>> These should be moved to libgcov-interface.c. Is there reason to not
>> mark them as static?
>
> gcov_dump_complete is used in gcov_exit() which is in
> libgcov-driver.c, but it's set in __gcov_dump and __gcov_reset, both
> in libgcov-interface.c.
> I want gcov_dump_complete a static. So I have to add to global
> functions that accessible from libgcov-interface.c.
> Another choice is to move __gcov_dump and __gcov_reset to
> libgcov-driver.c and that will simplify the code.
>
>>
>> in libgcov-profiler.c, line 142
>>
>>> #if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS)
>>> __thread
>>
>> trailing whilte space.
>>
>
> Will fix.
>
>>
>>>     || (VTABLE_USES_DESCRIPTORS && __gcov_indirect_call_callee
>>>         && *(void **) cur_func == *(void **) __gcov_indirect_call_callee))
>>
>> trailing white space.
>>
>
> Will fix.
>
>>
>> in libgcov-merge.c
>>
>> 1) I don't think you need in_mem flag. For in memory merge, the source != NULL.
>> 2) document the new source and weight parameter -- especially the weight.
>
> Will do.
>
>> 3) How do you deal with integer overflow ?
>
> This is good question. gcvo_type is (typically) long long. I thought
> the count value will not be nearly close to the max of long long.
> (We did see overflow in compiler, but that's because of the scaling --
> some of the scaling factors are really large.)
>
>>
>> David
>>
>>
>>
>>
>>
>>
>> On Thu, Nov 7, 2013 at 3:34 PM, Rong Xu <xur@google.com> wrote:
>>> On Thu, Nov 7, 2013 at 9:40 AM, Joseph S. Myers <joseph@codesourcery.com> wrote:
>>>> On Thu, 7 Nov 2013, Rong Xu wrote:
>>>>
>>>>> Thanks Joseph for these detailed comments/suggestions.
>>>>> The fixed patch is attached to this email.
>>>>> The only thing left out is the Texinfo manual. Do you mean this tool
>>>>> should have its it's own texi file in gcc/doc?
>>>>
>>>> Its own texi file, probably included as a chapter by gcc.texi (like
>>>> gcov.texi is).
>>>
>>> OK. will add this in.
>>>
>>> My last patch that changes the command handling is actually broken.
>>> Please ignore that patch and use the one in this email.
>>>
>>> Also did some minor adjust of the code in libgcov.
>>>
>>> Thanks,
>>>
>>> -Rong
>>>
>>>
>>>>
>>>> --
>>>> Joseph S. Myers
>>>> joseph@codesourcery.com
Xinliang David Li Nov. 8, 2013, 7:02 p.m. UTC | #5
On Fri, Nov 8, 2013 at 10:48 AM, Rong Xu <xur@google.com> wrote:
> On Fri, Nov 8, 2013 at 10:22 AM, Xinliang David Li <davidxl@google.com> wrote:
>> in libgcov-driver.c
>>
>>> /* Flag when the profile has already been dumped via __gcov_dump().  */
>>> static int gcov_dump_complete;
>>
>>> inline void
>>> set_gcov_dump_complete (void)
>>>{
>>   > gcov_dump_complete = 1;
>>>}
>>
>>>inline void
>>>reset_gcov_dump_complete (void)
>>>{
>>>  gcov_dump_complete = 0;
>>>}
>>
>>
>> These should be moved to libgcov-interface.c. Is there reason to not
>> mark them as static?
>
> gcov_dump_complete is used in gcov_exit() which is in
> libgcov-driver.c, but it's set in __gcov_dump and __gcov_reset, both
> in libgcov-interface.c.
> I want gcov_dump_complete a static. So I have to add to global
> functions that accessible from libgcov-interface.c.
> Another choice is to move __gcov_dump and __gcov_reset to
> libgcov-driver.c and that will simplify the code.
>

Ok  then you should remove the 'inline' keyword for the set and reset
functions and keep them in libgcov-driver.c.

>>
>> in libgcov-profiler.c, line 142
>>
>>> #if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS)
>>> __thread
>>
>> trailing whilte space.
>>
>
> Will fix.
>
>>
>>>     || (VTABLE_USES_DESCRIPTORS && __gcov_indirect_call_callee
>>>         && *(void **) cur_func == *(void **) __gcov_indirect_call_callee))
>>
>> trailing white space.
>>
>
> Will fix.
>
>>
>> in libgcov-merge.c
>>
>> 1) I don't think you need in_mem flag. For in memory merge, the source != NULL.
>> 2) document the new source and weight parameter -- especially the weight.
>
> Will do.
>
>> 3) How do you deal with integer overflow ?
>
> This is good question. gcvo_type is (typically) long long. I thought
> the count value will not be nearly close to the max of long long.
> (We did see overflow in compiler, but that's because of the scaling --
> some of the scaling factors are really large.)
>

Why not passing weight as a scaled PERCENT  (as branch prob) for the
source? THis way, the merge tool does not need to do scaling twice.

David



>>
>> David
>>
>>
>>
>>
>>
>>
>> On Thu, Nov 7, 2013 at 3:34 PM, Rong Xu <xur@google.com> wrote:
>>> On Thu, Nov 7, 2013 at 9:40 AM, Joseph S. Myers <joseph@codesourcery.com> wrote:
>>>> On Thu, 7 Nov 2013, Rong Xu wrote:
>>>>
>>>>> Thanks Joseph for these detailed comments/suggestions.
>>>>> The fixed patch is attached to this email.
>>>>> The only thing left out is the Texinfo manual. Do you mean this tool
>>>>> should have its it's own texi file in gcc/doc?
>>>>
>>>> Its own texi file, probably included as a chapter by gcc.texi (like
>>>> gcov.texi is).
>>>
>>> OK. will add this in.
>>>
>>> My last patch that changes the command handling is actually broken.
>>> Please ignore that patch and use the one in this email.
>>>
>>> Also did some minor adjust of the code in libgcov.
>>>
>>> Thanks,
>>>
>>> -Rong
>>>
>>>
>>>>
>>>> --
>>>> Joseph S. Myers
>>>> joseph@codesourcery.com
Xinliang David Li Nov. 8, 2013, 7:13 p.m. UTC | #6
On Fri, Nov 8, 2013 at 10:56 AM, Rong Xu <xur@google.com> wrote:
> A question about inhibit_libc.
> When inhibit_libc is defined, we provide dummy functions for all the
> __gcov_* methods.
> Is it purposely to minimize the footprint?
> I'm thinking to allow some codes that are independent of libc (and its
> headers) under this. Is it OK?
>


I only saw the macro defined for mcore targets -- not sure if they are
obsolete or not.

David


> -Rong
>
> On Fri, Nov 8, 2013 at 10:48 AM, Rong Xu <xur@google.com> wrote:
>> On Fri, Nov 8, 2013 at 10:22 AM, Xinliang David Li <davidxl@google.com> wrote:
>>> in libgcov-driver.c
>>>
>>>> /* Flag when the profile has already been dumped via __gcov_dump().  */
>>>> static int gcov_dump_complete;
>>>
>>>> inline void
>>>> set_gcov_dump_complete (void)
>>>>{
>>>   > gcov_dump_complete = 1;
>>>>}
>>>
>>>>inline void
>>>>reset_gcov_dump_complete (void)
>>>>{
>>>>  gcov_dump_complete = 0;
>>>>}
>>>
>>>
>>> These should be moved to libgcov-interface.c. Is there reason to not
>>> mark them as static?
>>
>> gcov_dump_complete is used in gcov_exit() which is in
>> libgcov-driver.c, but it's set in __gcov_dump and __gcov_reset, both
>> in libgcov-interface.c.
>> I want gcov_dump_complete a static. So I have to add to global
>> functions that accessible from libgcov-interface.c.
>> Another choice is to move __gcov_dump and __gcov_reset to
>> libgcov-driver.c and that will simplify the code.
>>
>>>
>>> in libgcov-profiler.c, line 142
>>>
>>>> #if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS)
>>>> __thread
>>>
>>> trailing whilte space.
>>>
>>
>> Will fix.
>>
>>>
>>>>     || (VTABLE_USES_DESCRIPTORS && __gcov_indirect_call_callee
>>>>         && *(void **) cur_func == *(void **) __gcov_indirect_call_callee))
>>>
>>> trailing white space.
>>>
>>
>> Will fix.
>>
>>>
>>> in libgcov-merge.c
>>>
>>> 1) I don't think you need in_mem flag. For in memory merge, the source != NULL.
>>> 2) document the new source and weight parameter -- especially the weight.
>>
>> Will do.
>>
>>> 3) How do you deal with integer overflow ?
>>
>> This is good question. gcvo_type is (typically) long long. I thought
>> the count value will not be nearly close to the max of long long.
>> (We did see overflow in compiler, but that's because of the scaling --
>> some of the scaling factors are really large.)
>>
>>>
>>> David
>>>
>>>
>>>
>>>
>>>
>>>
>>> On Thu, Nov 7, 2013 at 3:34 PM, Rong Xu <xur@google.com> wrote:
>>>> On Thu, Nov 7, 2013 at 9:40 AM, Joseph S. Myers <joseph@codesourcery.com> wrote:
>>>>> On Thu, 7 Nov 2013, Rong Xu wrote:
>>>>>
>>>>>> Thanks Joseph for these detailed comments/suggestions.
>>>>>> The fixed patch is attached to this email.
>>>>>> The only thing left out is the Texinfo manual. Do you mean this tool
>>>>>> should have its it's own texi file in gcc/doc?
>>>>>
>>>>> Its own texi file, probably included as a chapter by gcc.texi (like
>>>>> gcov.texi is).
>>>>
>>>> OK. will add this in.
>>>>
>>>> My last patch that changes the command handling is actually broken.
>>>> Please ignore that patch and use the one in this email.
>>>>
>>>> Also did some minor adjust of the code in libgcov.
>>>>
>>>> Thanks,
>>>>
>>>> -Rong
>>>>
>>>>
>>>>>
>>>>> --
>>>>> Joseph S. Myers
>>>>> joseph@codesourcery.com
Rong Xu Nov. 8, 2013, 7:21 p.m. UTC | #7
On Fri, Nov 8, 2013 at 11:02 AM, Xinliang David Li <davidxl@google.com> wrote:
> On Fri, Nov 8, 2013 at 10:48 AM, Rong Xu <xur@google.com> wrote:
>> On Fri, Nov 8, 2013 at 10:22 AM, Xinliang David Li <davidxl@google.com> wrote:
>>> in libgcov-driver.c
>>>
>>>> /* Flag when the profile has already been dumped via __gcov_dump().  */
>>>> static int gcov_dump_complete;
>>>
>>>> inline void
>>>> set_gcov_dump_complete (void)
>>>>{
>>>   > gcov_dump_complete = 1;
>>>>}
>>>
>>>>inline void
>>>>reset_gcov_dump_complete (void)
>>>>{
>>>>  gcov_dump_complete = 0;
>>>>}
>>>
>>>
>>> These should be moved to libgcov-interface.c. Is there reason to not
>>> mark them as static?
>>
>> gcov_dump_complete is used in gcov_exit() which is in
>> libgcov-driver.c, but it's set in __gcov_dump and __gcov_reset, both
>> in libgcov-interface.c.
>> I want gcov_dump_complete a static. So I have to add to global
>> functions that accessible from libgcov-interface.c.
>> Another choice is to move __gcov_dump and __gcov_reset to
>> libgcov-driver.c and that will simplify the code.
>>
>
> Ok  then you should remove the 'inline' keyword for the set and reset
> functions and keep them in libgcov-driver.c.

Will remove 'inline' keyword.

>
>>>
>>> in libgcov-profiler.c, line 142
>>>
>>>> #if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS)
>>>> __thread
>>>
>>> trailing whilte space.
>>>
>>
>> Will fix.
>>
>>>
>>>>     || (VTABLE_USES_DESCRIPTORS && __gcov_indirect_call_callee
>>>>         && *(void **) cur_func == *(void **) __gcov_indirect_call_callee))
>>>
>>> trailing white space.
>>>
>>
>> Will fix.
>>
>>>
>>> in libgcov-merge.c
>>>
>>> 1) I don't think you need in_mem flag. For in memory merge, the source != NULL.
>>> 2) document the new source and weight parameter -- especially the weight.
>>
>> Will do.
>>
>>> 3) How do you deal with integer overflow ?
>>
>> This is good question. gcvo_type is (typically) long long. I thought
>> the count value will not be nearly close to the max of long long.
>> (We did see overflow in compiler, but that's because of the scaling --
>> some of the scaling factors are really large.)
>>
>
> Why not passing weight as a scaled PERCENT  (as branch prob) for the
> source? THis way, the merge tool does not need to do scaling twice.
>

there are two weights, so unless you have two weights in the merge_add
functions, you have to
traverse the list twice. Do you mean pass addition parameters?

Currently, merge tools is done this way:
merge (p1, p2, w1, w2)
first pass: merge_add (p1, p1, w1)
second pass: merge_add (p1, p2, w2)

I initially had both weights in merge_add function. but later I found that
to deal with mis-match profile, I need to find out the gcov_info in p1
but not p2, (they need to multiply by w1 only).
So I choose the above structure.

Another thing about the PERCENT is the inaccuracy for floating points.

I have the scaling function in rewrite sub-command mainly for debug purpose:
I merge the same profile with a weight 1:9.
Then I scale the result profile by 0.1. I was expecting identical
profile as the source. But due to floating point inaccuracy, some
counters are off slightly.


> David
>
>
>
>>>
>>> David
>>>
>>>
>>>
>>>
>>>
>>>
>>> On Thu, Nov 7, 2013 at 3:34 PM, Rong Xu <xur@google.com> wrote:
>>>> On Thu, Nov 7, 2013 at 9:40 AM, Joseph S. Myers <joseph@codesourcery.com> wrote:
>>>>> On Thu, 7 Nov 2013, Rong Xu wrote:
>>>>>
>>>>>> Thanks Joseph for these detailed comments/suggestions.
>>>>>> The fixed patch is attached to this email.
>>>>>> The only thing left out is the Texinfo manual. Do you mean this tool
>>>>>> should have its it's own texi file in gcc/doc?
>>>>>
>>>>> Its own texi file, probably included as a chapter by gcc.texi (like
>>>>> gcov.texi is).
>>>>
>>>> OK. will add this in.
>>>>
>>>> My last patch that changes the command handling is actually broken.
>>>> Please ignore that patch and use the one in this email.
>>>>
>>>> Also did some minor adjust of the code in libgcov.
>>>>
>>>> Thanks,
>>>>
>>>> -Rong
>>>>
>>>>
>>>>>
>>>>> --
>>>>> Joseph S. Myers
>>>>> joseph@codesourcery.com
Xinliang David Li Nov. 8, 2013, 7:30 p.m. UTC | #8
On Fri, Nov 8, 2013 at 11:21 AM, Rong Xu <xur@google.com> wrote:
> On Fri, Nov 8, 2013 at 11:02 AM, Xinliang David Li <davidxl@google.com> wrote:
>> On Fri, Nov 8, 2013 at 10:48 AM, Rong Xu <xur@google.com> wrote:
>>> On Fri, Nov 8, 2013 at 10:22 AM, Xinliang David Li <davidxl@google.com> wrote:
>>>> in libgcov-driver.c
>>>>
>>>>> /* Flag when the profile has already been dumped via __gcov_dump().  */
>>>>> static int gcov_dump_complete;
>>>>
>>>>> inline void
>>>>> set_gcov_dump_complete (void)
>>>>>{
>>>>   > gcov_dump_complete = 1;
>>>>>}
>>>>
>>>>>inline void
>>>>>reset_gcov_dump_complete (void)
>>>>>{
>>>>>  gcov_dump_complete = 0;
>>>>>}
>>>>
>>>>
>>>> These should be moved to libgcov-interface.c. Is there reason to not
>>>> mark them as static?
>>>
>>> gcov_dump_complete is used in gcov_exit() which is in
>>> libgcov-driver.c, but it's set in __gcov_dump and __gcov_reset, both
>>> in libgcov-interface.c.
>>> I want gcov_dump_complete a static. So I have to add to global
>>> functions that accessible from libgcov-interface.c.
>>> Another choice is to move __gcov_dump and __gcov_reset to
>>> libgcov-driver.c and that will simplify the code.
>>>
>>
>> Ok  then you should remove the 'inline' keyword for the set and reset
>> functions and keep them in libgcov-driver.c.
>
> Will remove 'inline' keyword.
>
>>
>>>>
>>>> in libgcov-profiler.c, line 142
>>>>
>>>>> #if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS)
>>>>> __thread
>>>>
>>>> trailing whilte space.
>>>>
>>>
>>> Will fix.
>>>
>>>>
>>>>>     || (VTABLE_USES_DESCRIPTORS && __gcov_indirect_call_callee
>>>>>         && *(void **) cur_func == *(void **) __gcov_indirect_call_callee))
>>>>
>>>> trailing white space.
>>>>
>>>
>>> Will fix.
>>>
>>>>
>>>> in libgcov-merge.c
>>>>
>>>> 1) I don't think you need in_mem flag. For in memory merge, the source != NULL.
>>>> 2) document the new source and weight parameter -- especially the weight.
>>>
>>> Will do.
>>>
>>>> 3) How do you deal with integer overflow ?
>>>
>>> This is good question. gcvo_type is (typically) long long. I thought
>>> the count value will not be nearly close to the max of long long.
>>> (We did see overflow in compiler, but that's because of the scaling --
>>> some of the scaling factors are really large.)
>>>
>>
>> Why not passing weight as a scaled PERCENT  (as branch prob) for the
>> source? THis way, the merge tool does not need to do scaling twice.
>>
>
> there are two weights, so unless you have two weights in the merge_add
> functions, you have to
> traverse the list twice. Do you mean pass addition parameters?
>
> Currently, merge tools is done this way:
> merge (p1, p2, w1, w2)
> first pass: merge_add (p1, p1, w1)
> second pass: merge_add (p1, p2, w2)
>

Only need to pass the normalized the weight (in percent) for one
profile source -- say norm_w2 : w2/(w1+w2), and the merge function can
do this in one pass as norm_w1 = 1-norm_w2.


> I initially had both weights in merge_add function. but later I found that
> to deal with mis-match profile, I need to find out the gcov_info in p1
> but not p2, (they need to multiply by w1 only).
> So I choose the above structure.
>
> Another thing about the PERCENT is the inaccuracy for floating points.

This is how profile update work in profile-use compilation -- so that
should not be a big problem.

Before you fix this, I'd like to hear what Honza and other reviewers think.

thanks,

David

>
> I have the scaling function in rewrite sub-command mainly for debug purpose:
> I merge the same profile with a weight 1:9.
> Then I scale the result profile by 0.1. I was expecting identical
> profile as the source. But due to floating point inaccuracy, some
> counters are off slightly.
>
>
>> David
>>
>>
>>
>>>>
>>>> David
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>> On Thu, Nov 7, 2013 at 3:34 PM, Rong Xu <xur@google.com> wrote:
>>>>> On Thu, Nov 7, 2013 at 9:40 AM, Joseph S. Myers <joseph@codesourcery.com> wrote:
>>>>>> On Thu, 7 Nov 2013, Rong Xu wrote:
>>>>>>
>>>>>>> Thanks Joseph for these detailed comments/suggestions.
>>>>>>> The fixed patch is attached to this email.
>>>>>>> The only thing left out is the Texinfo manual. Do you mean this tool
>>>>>>> should have its it's own texi file in gcc/doc?
>>>>>>
>>>>>> Its own texi file, probably included as a chapter by gcc.texi (like
>>>>>> gcov.texi is).
>>>>>
>>>>> OK. will add this in.
>>>>>
>>>>> My last patch that changes the command handling is actually broken.
>>>>> Please ignore that patch and use the one in this email.
>>>>>
>>>>> Also did some minor adjust of the code in libgcov.
>>>>>
>>>>> Thanks,
>>>>>
>>>>> -Rong
>>>>>
>>>>>
>>>>>>
>>>>>> --
>>>>>> Joseph S. Myers
>>>>>> joseph@codesourcery.com
Rong Xu Nov. 8, 2013, 7:42 p.m. UTC | #9
On Fri, Nov 8, 2013 at 11:30 AM, Xinliang David Li <davidxl@google.com> wrote:
> On Fri, Nov 8, 2013 at 11:21 AM, Rong Xu <xur@google.com> wrote:
>> On Fri, Nov 8, 2013 at 11:02 AM, Xinliang David Li <davidxl@google.com> wrote:
>>> On Fri, Nov 8, 2013 at 10:48 AM, Rong Xu <xur@google.com> wrote:
>>>> On Fri, Nov 8, 2013 at 10:22 AM, Xinliang David Li <davidxl@google.com> wrote:
>>>>> in libgcov-driver.c
>>>>>
>>>>>> /* Flag when the profile has already been dumped via __gcov_dump().  */
>>>>>> static int gcov_dump_complete;
>>>>>
>>>>>> inline void
>>>>>> set_gcov_dump_complete (void)
>>>>>>{
>>>>>   > gcov_dump_complete = 1;
>>>>>>}
>>>>>
>>>>>>inline void
>>>>>>reset_gcov_dump_complete (void)
>>>>>>{
>>>>>>  gcov_dump_complete = 0;
>>>>>>}
>>>>>
>>>>>
>>>>> These should be moved to libgcov-interface.c. Is there reason to not
>>>>> mark them as static?
>>>>
>>>> gcov_dump_complete is used in gcov_exit() which is in
>>>> libgcov-driver.c, but it's set in __gcov_dump and __gcov_reset, both
>>>> in libgcov-interface.c.
>>>> I want gcov_dump_complete a static. So I have to add to global
>>>> functions that accessible from libgcov-interface.c.
>>>> Another choice is to move __gcov_dump and __gcov_reset to
>>>> libgcov-driver.c and that will simplify the code.
>>>>
>>>
>>> Ok  then you should remove the 'inline' keyword for the set and reset
>>> functions and keep them in libgcov-driver.c.
>>
>> Will remove 'inline' keyword.
>>
>>>
>>>>>
>>>>> in libgcov-profiler.c, line 142
>>>>>
>>>>>> #if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS)
>>>>>> __thread
>>>>>
>>>>> trailing whilte space.
>>>>>
>>>>
>>>> Will fix.
>>>>
>>>>>
>>>>>>     || (VTABLE_USES_DESCRIPTORS && __gcov_indirect_call_callee
>>>>>>         && *(void **) cur_func == *(void **) __gcov_indirect_call_callee))
>>>>>
>>>>> trailing white space.
>>>>>
>>>>
>>>> Will fix.
>>>>
>>>>>
>>>>> in libgcov-merge.c
>>>>>
>>>>> 1) I don't think you need in_mem flag. For in memory merge, the source != NULL.
>>>>> 2) document the new source and weight parameter -- especially the weight.
>>>>
>>>> Will do.
>>>>
>>>>> 3) How do you deal with integer overflow ?
>>>>
>>>> This is good question. gcvo_type is (typically) long long. I thought
>>>> the count value will not be nearly close to the max of long long.
>>>> (We did see overflow in compiler, but that's because of the scaling --
>>>> some of the scaling factors are really large.)
>>>>
>>>
>>> Why not passing weight as a scaled PERCENT  (as branch prob) for the
>>> source? THis way, the merge tool does not need to do scaling twice.
>>>
>>
>> there are two weights, so unless you have two weights in the merge_add
>> functions, you have to
>> traverse the list twice. Do you mean pass addition parameters?
>>
>> Currently, merge tools is done this way:
>> merge (p1, p2, w1, w2)
>> first pass: merge_add (p1, p1, w1)
>> second pass: merge_add (p1, p2, w2)
>>
>
> Only need to pass the normalized the weight (in percent) for one
> profile source -- say norm_w2 : w2/(w1+w2), and the merge function can
> do this in one pass as norm_w1 = 1-norm_w2.
>
>
>> I initially had both weights in merge_add function. but later I found that
>> to deal with mis-match profile, I need to find out the gcov_info in p1
>> but not p2, (they need to multiply by w1 only).
>> So I choose the above structure.
>>
>> Another thing about the PERCENT is the inaccuracy for floating points.
>
> This is how profile update work in profile-use compilation -- so that
> should not be a big problem.
>
> Before you fix this, I'd like to hear what Honza and other reviewers think.
>
> thanks,
>
> David
>

OK. Let's get more comments on this before I re-work on this.
I also want to point out that the traversal the gcov-info list is
cheap. Even with the huge profile for google internal programs. The
majority of the time of the merge-tool is in read/write gcda files..

-Rong

>>
>> I have the scaling function in rewrite sub-command mainly for debug purpose:
>> I merge the same profile with a weight 1:9.
>> Then I scale the result profile by 0.1. I was expecting identical
>> profile as the source. But due to floating point inaccuracy, some
>> counters are off slightly.
>>
>>
>>> David
>>>
>>>
>>>
>>>>>
>>>>> David
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> On Thu, Nov 7, 2013 at 3:34 PM, Rong Xu <xur@google.com> wrote:
>>>>>> On Thu, Nov 7, 2013 at 9:40 AM, Joseph S. Myers <joseph@codesourcery.com> wrote:
>>>>>>> On Thu, 7 Nov 2013, Rong Xu wrote:
>>>>>>>
>>>>>>>> Thanks Joseph for these detailed comments/suggestions.
>>>>>>>> The fixed patch is attached to this email.
>>>>>>>> The only thing left out is the Texinfo manual. Do you mean this tool
>>>>>>>> should have its it's own texi file in gcc/doc?
>>>>>>>
>>>>>>> Its own texi file, probably included as a chapter by gcc.texi (like
>>>>>>> gcov.texi is).
>>>>>>
>>>>>> OK. will add this in.
>>>>>>
>>>>>> My last patch that changes the command handling is actually broken.
>>>>>> Please ignore that patch and use the one in this email.
>>>>>>
>>>>>> Also did some minor adjust of the code in libgcov.
>>>>>>
>>>>>> Thanks,
>>>>>>
>>>>>> -Rong
>>>>>>
>>>>>>
>>>>>>>
>>>>>>> --
>>>>>>> Joseph S. Myers
>>>>>>> joseph@codesourcery.com
Joseph Myers Nov. 8, 2013, 9:29 p.m. UTC | #10
On Fri, 8 Nov 2013, Rong Xu wrote:

> A question about inhibit_libc.
> When inhibit_libc is defined, we provide dummy functions for all the
> __gcov_* methods.
> Is it purposely to minimize the footprint?

inhibit_libc is for use in bootstrap builds when libc headers aren't 
available - for building the compiler used to configure libc and install 
its headers.  (It may have other purposes, but that's one of them.)
diff mbox

Patch

Index: libgcc/libgcov-driver.c
===================================================================
--- libgcc/libgcov-driver.c	(revision 0)
+++ libgcc/libgcov-driver.c	(revision 0)
@@ -0,0 +1,845 @@ 
+/* Routines required for instrumenting a program.  */
+/* Compile this one with gcc.  */
+/* Copyright (C) 1989-2013 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef xmalloc
+#define xmalloc malloc 
+#endif
+
+#include "libgcov.h"
+
+#if defined(inhibit_libc)
+#define IN_LIBGCOV (-1)
+#else
+#define IN_LIBGCOV 1
+#if defined(L_gcov)
+#define GCOV_LINKAGE /* nothing */
+#endif
+#endif
+#include "gcov-io.h"
+
+#if defined(inhibit_libc)
+/* If libc and its header files are not available, provide dummy functions.  */
+
+#if defined(L_gcov)
+void __gcov_init (struct gcov_info *p __attribute__ ((unused))) {}
+#endif
+
+#else /* inhibit_libc */
+
+#include <string.h>
+#if GCOV_LOCKED
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#endif
+
+extern void gcov_clear (void) ATTRIBUTE_HIDDEN;
+extern void gcov_exit (void) ATTRIBUTE_HIDDEN;
+
+#ifdef L_gcov
+#include "gcov-io.c"
+
+struct gcov_fn_buffer
+{
+  struct gcov_fn_buffer *next;
+  unsigned fn_ix;
+  struct gcov_fn_info info;
+  /* note gcov_fn_info ends in a trailing array.  */
+};
+
+struct gcov_summary_buffer
+{
+  struct gcov_summary_buffer *next;
+  struct gcov_summary summary;
+};
+
+/* Chain of per-object gcov structures.  */
+static struct gcov_info *gcov_list;
+
+/* Set the head of gcov_list.  */
+void
+set_gcov_list (struct gcov_info *head)
+{
+  gcov_list = head;
+}
+
+/* Use this summary checksum rather the computed one if the value is
+   non-zero.  */
+static gcov_unsigned_t saved_summary_checksum;
+
+/* Size of the longest file name. */
+static size_t gcov_max_filename = 0;
+
+/* Flag when the profile has already been dumped via __gcov_dump().  */
+static int gcov_dump_complete;
+
+inline void
+set_gcov_dump_complete (void)
+{
+  gcov_dump_complete = 1;
+}
+
+inline void
+reset_gcov_dump_complete (void)
+{
+  gcov_dump_complete = 0;
+}
+
+/* A utility function for outputing errors.  */
+static int gcov_error (const char *, ...);
+
+static struct gcov_fn_buffer *
+free_fn_data (const struct gcov_info *gi_ptr, struct gcov_fn_buffer *buffer,
+              unsigned limit)
+{
+  struct gcov_fn_buffer *next;
+  unsigned ix, n_ctr = 0;
+
+  if (!buffer)
+    return 0;
+  next = buffer->next;
+
+  for (ix = 0; ix != limit; ix++)
+    if (gi_ptr->merge[ix])
+      free (buffer->info.ctrs[n_ctr++].values);
+  free (buffer);
+  return next;
+}
+
+static struct gcov_fn_buffer **
+buffer_fn_data (const char *filename, const struct gcov_info *gi_ptr,
+                struct gcov_fn_buffer **end_ptr, unsigned fn_ix)
+{
+  unsigned n_ctrs = 0, ix = 0;
+  struct gcov_fn_buffer *fn_buffer;
+  unsigned len;
+
+  for (ix = GCOV_COUNTERS; ix--;)
+    if (gi_ptr->merge[ix])
+      n_ctrs++;
+
+  len = sizeof (*fn_buffer) + sizeof (fn_buffer->info.ctrs[0]) * n_ctrs;
+  fn_buffer = (struct gcov_fn_buffer *)xmalloc (len);
+
+  if (!fn_buffer)
+    goto fail;
+
+  fn_buffer->next = 0;
+  fn_buffer->fn_ix = fn_ix;
+  fn_buffer->info.ident = gcov_read_unsigned ();
+  fn_buffer->info.lineno_checksum = gcov_read_unsigned ();
+  fn_buffer->info.cfg_checksum = gcov_read_unsigned ();
+
+  for (n_ctrs = ix = 0; ix != GCOV_COUNTERS; ix++)
+    {
+      gcov_unsigned_t length;
+      gcov_type *values;
+
+      if (!gi_ptr->merge[ix])
+        continue;
+
+      if (gcov_read_unsigned () != GCOV_TAG_FOR_COUNTER (ix))
+        {
+          len = 0;
+          goto fail;
+        }
+
+      length = GCOV_TAG_COUNTER_NUM (gcov_read_unsigned ());
+      len = length * sizeof (gcov_type);
+      values = (gcov_type *)xmalloc (len);
+      if (!values)
+        goto fail;
+
+      fn_buffer->info.ctrs[n_ctrs].num = length;
+      fn_buffer->info.ctrs[n_ctrs].values = values;
+
+      while (length--)
+        *values++ = gcov_read_counter ();
+      n_ctrs++;
+    }
+
+  *end_ptr = fn_buffer;
+  return &fn_buffer->next;
+
+fail:
+  gcov_error ("profiling:%s:Function %u %s %u \n", filename, fn_ix,
+              len ? "cannot allocate" : "counter mismatch", len ? len : ix);
+
+  return (struct gcov_fn_buffer **)free_fn_data (gi_ptr, fn_buffer, ix);
+}
+
+/* Add an unsigned value to the current crc */
+
+static gcov_unsigned_t
+crc32_unsigned (gcov_unsigned_t crc32, gcov_unsigned_t value)
+{
+  unsigned ix;
+
+  for (ix = 32; ix--; value <<= 1)
+    {
+      unsigned feedback;
+
+      feedback = (value ^ crc32) & 0x80000000 ? 0x04c11db7 : 0;
+      crc32 <<= 1;
+      crc32 ^= feedback;
+    }
+
+  return crc32;
+}
+
+/* Check if VERSION of the info block PTR matches libgcov one.
+   Return 1 on success, or zero in case of versions mismatch.
+   If FILENAME is not NULL, its value used for reporting purposes
+   instead of value from the info block.  */
+
+static int
+gcov_version (struct gcov_info *ptr, gcov_unsigned_t version,
+              const char *filename)
+{
+  if (version != GCOV_VERSION)
+    {
+      char v[4], e[4];
+
+      GCOV_UNSIGNED2STRING (v, version);
+      GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
+
+      gcov_error ("profiling:%s:Version mismatch - expected %.4s got %.4s\n",
+                  filename? filename : ptr->filename, e, v);
+      return 0;
+    }
+  return 1;
+}
+
+/* Insert counter VALUE into HISTOGRAM.  */
+
+static void
+gcov_histogram_insert(gcov_bucket_type *histogram, gcov_type value)
+{
+  unsigned i;
+
+  i = gcov_histo_index(value);
+  histogram[i].num_counters++;
+  histogram[i].cum_value += value;
+  if (value < histogram[i].min_value)
+    histogram[i].min_value = value;
+}
+
+/* Computes a histogram of the arc counters to place in the summary SUM.  */
+
+static void
+gcov_compute_histogram (struct gcov_summary *sum)
+{
+  struct gcov_info *gi_ptr;
+  const struct gcov_fn_info *gfi_ptr;
+  const struct gcov_ctr_info *ci_ptr;
+  struct gcov_ctr_summary *cs_ptr;
+  unsigned t_ix, f_ix, ctr_info_ix, ix;
+  int h_ix;
+
+  /* This currently only applies to arc counters.  */
+  t_ix = GCOV_COUNTER_ARCS;
+
+  /* First check if there are any counts recorded for this counter.  */
+  cs_ptr = &(sum->ctrs[t_ix]);
+  if (!cs_ptr->num)
+    return;
+
+  for (h_ix = 0; h_ix < GCOV_HISTOGRAM_SIZE; h_ix++)
+    {
+      cs_ptr->histogram[h_ix].num_counters = 0;
+      cs_ptr->histogram[h_ix].min_value = cs_ptr->run_max;
+      cs_ptr->histogram[h_ix].cum_value = 0;
+    }
+
+  /* Walk through all the per-object structures and record each of
+     the count values in histogram.  */
+  for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
+    {
+      if (!gi_ptr->merge[t_ix])
+        continue;
+
+      /* Find the appropriate index into the gcov_ctr_info array
+         for the counter we are currently working on based on the
+         existence of the merge function pointer for this object.  */
+      for (ix = 0, ctr_info_ix = 0; ix < t_ix; ix++)
+        {
+          if (gi_ptr->merge[ix])
+            ctr_info_ix++;
+        }
+      for (f_ix = 0; f_ix != gi_ptr->n_functions; f_ix++)
+        {
+          gfi_ptr = gi_ptr->functions[f_ix];
+
+          if (!gfi_ptr || gfi_ptr->key != gi_ptr)
+            continue;
+
+          ci_ptr = &gfi_ptr->ctrs[ctr_info_ix];
+          for (ix = 0; ix < ci_ptr->num; ix++)
+            gcov_histogram_insert (cs_ptr->histogram, ci_ptr->values[ix]);
+        }
+    }
+}
+
+/* summary for program.  */
+static struct gcov_summary this_prg;
+#if !GCOV_LOCKED
+/* summary for all instances of program.  */
+static struct gcov_summary all_prg;
+#endif
+/* crc32 for this program.  */
+static gcov_unsigned_t crc32;
+/* gcda filename.  */
+static char *gi_filename;
+/* buffer for the fn_data from another program.  */
+static struct gcov_fn_buffer *fn_buffer;
+/* buffer for summary from other programs to be written out. */
+static struct gcov_summary_buffer *sum_buffer;
+
+/* This funtions computes the program level summary and the histo-gram.
+   It initializes ALL_PRG, computes CRC32, and stores the summary in
+   THIS_PRG. All these three variables are file statics.  */
+
+static void
+gcov_exit_compute_summary (void)
+{
+  struct gcov_info *gi_ptr;
+  const struct gcov_fn_info *gfi_ptr;
+  struct gcov_ctr_summary *cs_ptr;
+  const struct gcov_ctr_info *ci_ptr;
+  int f_ix;
+  unsigned t_ix;
+  gcov_unsigned_t c_num;
+
+#if !GCOV_LOCKED
+  memset (&all_prg, 0, sizeof (all_prg));
+#endif
+  /* 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++;
+            }
+        }
+    }
+  gcov_compute_histogram (&this_prg);
+}
+
+/* A struct that bundles all the related information about the
+   gcda filename.  */
+struct gcov_filename_aux{
+  char *gi_filename_up;
+  int gcov_prefix_strip;
+  size_t prefix_length;
+};
+
+/* Including system dependent components. */
+#include "libgcov-driver-system.c"
+
+/* This function merges counters in GI_PTR to an existing gcda file.
+   Return 0 on success.
+   Return -1 on error. In this case, caller will goto read_fatal.  */
+
+static int
+gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
+                      struct gcov_summary *prg_p,
+                      gcov_position_t *summary_pos_p,
+                      gcov_position_t *eof_pos_p)
+{
+  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, gi_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 (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, NULL, 1);
+          ci_ptr++;
+        }
+      if ((error = gcov_is_error ()))
+        goto read_error;
+    }
+
+  if (tag)
+    {
+    read_mismatch:;
+      gcov_error ("profiling:%s:Merge mismatch for %s %u\n",
+                  gi_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", gi_filename,
+              error < 0 ? "Overflow": "Error");
+  return -1;
+}
+
+/* Write counters in GI_PTR and the summary in PRG to a gcda file. In
+   the case of appending to an existing file, SUMMARY_POS will be non-zero.
+   We will write the file starting from SUMMAY_POS.  */
+
+static void
+gcov_exit_write_gcda (const struct gcov_info *gi_ptr,
+                      const struct gcov_summary *prg_p,
+                      const gcov_position_t summary_pos)
+{
+  unsigned f_ix;
+  struct gcov_summary_buffer *next_sum_buffer;
+
+  /* Write out the data.  */
+  if (!summary_pos)
+    {
+      gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION);
+      gcov_write_unsigned (gi_ptr->stamp);
+    }
+  else
+    gcov_seek (summary_pos);
+
+  /* Generate whole program statistics.  */
+  gcov_write_summary (GCOV_TAG_PROGRAM_SUMMARY, prg_p);
+
+  /* Rewrite all the summaries that were after the summary we merged
+     into. This is necessary as the merged summary may have a different
+     size due to the number of non-zero histogram entries changing after
+     merging.  */
+
+  while (sum_buffer)
+    {
+      gcov_write_summary (GCOV_TAG_PROGRAM_SUMMARY, &sum_buffer->summary);
+      next_sum_buffer = sum_buffer->next;
+      free (sum_buffer);
+      sum_buffer = next_sum_buffer;
+    }
+
+  /* Write execution counts for each function.  */
+  for (f_ix = 0; f_ix != gi_ptr->n_functions; f_ix++)
+    {
+      unsigned buffered = 0;
+      const struct gcov_fn_info *gfi_ptr;
+      const struct gcov_ctr_info *ci_ptr;
+      gcov_unsigned_t length;
+      unsigned t_ix;
+
+      if (fn_buffer && fn_buffer->fn_ix == 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++)
+        {
+          gcov_unsigned_t n_counts;
+          gcov_type *c_ptr;
+
+          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));
+          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);
+}
+
+/* Dump the coverage counts for one gcov_info object. We merge with existing
+   counts when possible, to avoid growing the .da files ad infinitum. We use
+   this program's checksum to make sure we only accumulate whole program
+   statistics to the correct summary. An object file might be embedded
+   in two separate programs, and we must keep the two program
+   summaries separate.  */
+
+static void
+gcov_exit_dump_gcov (struct gcov_info *gi_ptr, struct gcov_filename_aux *gf)
+{
+  struct gcov_summary prg; /* summary for this object over all
+                                  program.  */
+  struct gcov_ctr_summary *cs_prg, *cs_tprg;
+#if !GCOV_LOCKED
+  struct gcov_ctr_summary *cs_all;
+#endif
+  int error = 0, ret;
+  gcov_unsigned_t tag;
+  gcov_position_t summary_pos = 0;
+  gcov_position_t eof_pos = 0;
+  unsigned t_ix;
+
+  fn_buffer = 0;
+  sum_buffer = 0;
+
+  ret = gcov_exit_open_gcda_file (gi_ptr, gf);
+  if (ret != 0)
+    return;
+
+  tag = gcov_read_unsigned ();
+  if (tag)
+    {
+      /* Merge data from file.  */
+      if (tag != GCOV_DATA_MAGIC)
+        {
+          gcov_error ("profiling:%s:Not a gcov data file\n", gi_filename);
+          goto read_fatal;
+        }
+      ret = gcov_exit_merge_gcda (gi_ptr, &prg, &summary_pos, &eof_pos);
+      if (ret == -1)
+        goto read_fatal;
+    }
+
+  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];
+
+      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;
+          if (cs_prg->runs == 1)
+            memcpy (cs_prg->histogram, cs_tprg->histogram,
+                   sizeof (gcov_bucket_type) * GCOV_HISTOGRAM_SIZE);
+          else
+            gcov_histogram_merge (cs_prg->histogram, cs_tprg->histogram);
+        }
+      else if (cs_prg->runs)
+        {
+          gcov_error ("profiling:%s:Merge mismatch for summary.\n",
+                      gi_filename);
+          goto read_fatal;
+        }
+
+#if !GCOV_LOCKED
+      cs_all = &all_prg.ctrs[t_ix];
+      if (!cs_all->runs && cs_prg->runs)
+        {
+          cs_all->num = cs_prg->num;
+          cs_all->runs = cs_prg->runs;
+          cs_all->sum_all = cs_prg->sum_all;
+          cs_all->run_max = cs_prg->run_max;
+          cs_all->sum_max = cs_prg->sum_max;
+        }
+      else if (!all_prg.checksum
+               /* Don't compare the histograms, which may have slight
+                  variations depending on the order they were updated
+                  due to the truncating integer divides used in the
+                  merge.  */
+               && (cs_all->num != cs_prg->num
+                   || cs_all->runs != cs_prg->runs
+                   || cs_all->sum_all != cs_prg->sum_all
+                   || cs_all->run_max != cs_prg->run_max
+                   || cs_all->sum_max != cs_prg->sum_max))
+             {
+               gcov_error ("profiling:%s:Data file mismatch - some "
+                           "data files may have been concurrently "
+                           "updated without locking support\n", gi_filename);
+               all_prg.checksum = ~0u;
+             }
+#endif
+    }
+
+  if (saved_summary_checksum)
+    prg.checksum = saved_summary_checksum;
+  else
+    prg.checksum = crc32;
+
+  gcov_exit_write_gcda (gi_ptr, &prg, summary_pos);
+  /* fall through */
+
+read_fatal:;
+  while (fn_buffer)
+    fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
+
+  if ((error = gcov_close ()))
+    gcov_error (error  < 0 ?
+                "profiling:%s:Overflow writing\n" :
+                "profiling:%s:Error writing\n",
+                gi_filename);
+}
+
+
+/* Dump all the coverage counts for the program. It first computes program
+   summary and then traverses gcov_list list and dumps the gcov_info
+   objects one by one.  */
+
+void
+gcov_exit (void)
+{
+  struct gcov_info *gi_ptr;
+  struct gcov_filename_aux gf;
+
+  /* Prevent the counters from being dumped a second time on exit when the
+     application already wrote out the profile using __gcov_dump().  */
+  if (gcov_dump_complete)
+    return;
+
+  gcov_exit_compute_summary ();
+
+  allocate_filename_struct (&gf);
+
+  /* Now merge each file.  */
+  for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
+    gcov_exit_dump_gcov (gi_ptr, &gf);
+
+  if (gi_filename)
+    free (gi_filename);
+}
+
+/* Reset all counters to zero.  */
+
+void
+gcov_clear (void)
+{
+  const struct gcov_info *gi_ptr;
+
+  for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
+    {
+      unsigned f_ix;
+
+      for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
+        {
+          unsigned t_ix;
+          const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix];
+
+          if (!gfi_ptr || gfi_ptr->key != gi_ptr)
+            continue;
+          const struct gcov_ctr_info *ci_ptr = gfi_ptr->ctrs;
+          for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++)
+            {
+              if (!gi_ptr->merge[t_ix])
+                continue;
+
+              memset (ci_ptr->values, 0, sizeof (gcov_type) * ci_ptr->num);
+              ci_ptr++;
+            }
+        }
+    }
+}
+
+/* Add a new object file onto the bb chain.  Invoked automatically
+  when running an object file's global ctors.  */
+
+void
+__gcov_init (struct gcov_info *info)
+{
+  if (!info->version || !info->n_functions)
+    return;
+  if (gcov_version (info, info->version, 0))
+    {
+      size_t filename_length = strlen(info->filename);
+
+      /* Refresh the longest file name information */
+      if (filename_length > gcov_max_filename)
+        gcov_max_filename = filename_length;
+
+      if (!gcov_list)
+        atexit (gcov_exit);
+
+      info->next = gcov_list;
+      gcov_list = info;
+    }
+  info->version = 0;
+}
+
+#endif /* L_gcov */
+#endif /* inhibit_libc */
Index: libgcc/libgcov-driver-system.c
===================================================================
--- libgcc/libgcov-driver-system.c	(revision 0)
+++ libgcc/libgcov-driver-system.c	(revision 0)
@@ -0,0 +1,205 @@ 
+/* Routines required for instrumenting a program.  */
+/* Compile this one with gcc.  */
+/* Copyright (C) 1989-2013 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+#include "libgcov.h"
+
+/* A utility function for outputing errors.  */
+
+static int __attribute__((format(printf, 1, 2)))
+gcov_error (const char *fmt, ...)
+{
+  int ret;
+  va_list argp;
+  va_start (argp, fmt);
+  ret = vfprintf (stderr, fmt, argp);
+  va_end (argp);
+  return ret;
+}
+
+/* Make sure path component of the given FILENAME exists, create
+   missing directories. FILENAME must be writable.
+   Returns zero on success, or -1 if an error occurred.  */
+
+static int
+create_file_directory (char *filename)
+{
+#if !defined(TARGET_POSIX_IO) && !defined(_WIN32)
+  (void) filename;
+  return -1;
+#else
+  char *s;
+
+  s = filename;
+
+  if (HAS_DRIVE_SPEC(s))
+    s += 2;
+  if (IS_DIR_SEPARATOR(*s))
+    ++s;
+  for (; *s != '\0'; s++)
+    if (IS_DIR_SEPARATOR(*s))
+      {
+        char sep = *s;
+        *s  = '\0';
+
+        /* Try to make directory if it doesn't already exist.  */
+        if (access (filename, F_OK) == -1
+#ifdef TARGET_POSIX_IO
+            && mkdir (filename, 0755) == -1
+#else
+            && mkdir (filename) == -1
+#endif
+            /* The directory might have been made by another process.  */
+            && errno != EEXIST)
+          {
+            gcov_error ("profiling:%s:Cannot create directory\n", filename);
+            *s = sep;
+            return -1;
+          };
+
+        *s = sep;
+      };
+  return 0;
+#endif
+}
+
+static void
+allocate_filename_struct (struct gcov_filename_aux *gf)
+{
+  const char *gcov_prefix;
+  int gcov_prefix_strip = 0;
+  size_t prefix_length;
+  char *gi_filename_up;
+
+  gcc_assert (gf);
+  {
+    /* 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 *) xmalloc (prefix_length + gcov_max_filename + 2);
+  if (prefix_length)
+    memcpy (gi_filename, gcov_prefix, prefix_length);
+  gi_filename_up = gi_filename + prefix_length;
+
+  gf->gi_filename_up = gi_filename_up;
+  gf->prefix_length = prefix_length;
+  gf->gcov_prefix_strip = gcov_prefix_strip;
+}
+
+/* Open a gcda file specified by GI_FILENAME.
+   Return -1 on error.  Return 0 on success.  */
+
+static int
+gcov_exit_open_gcda_file (struct gcov_info *gi_ptr, struct gcov_filename_aux *gf)
+{
+  int gcov_prefix_strip;
+  size_t prefix_length;
+  char *gi_filename_up;
+  const char *fname, *s;
+
+  gcov_prefix_strip = gf->gcov_prefix_strip;
+  gi_filename_up = gf->gi_filename_up;
+  prefix_length = gf->prefix_length;
+  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);
+
+  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);
+          return -1;
+        }
+      if (!gcov_open (gi_filename))
+        {
+          fprintf (stderr, "profiling:%s:Cannot open\n", gi_filename);
+          return -1;
+        }
+    }
+
+  return 0;
+}
Index: libgcc/libgcov-interface.c
===================================================================
--- libgcc/libgcov-interface.c	(revision 0)
+++ libgcc/libgcov-interface.c	(revision 0)
@@ -0,0 +1,278 @@ 
+/* Routines required for instrumenting a program.  */
+/* Compile this one with gcc.  */
+/* Copyright (C) 1989-2013 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+#include "libgcov.h"
+#include "gthr.h"
+
+#if defined(inhibit_libc)
+#define IN_LIBGCOV (-1)
+#else
+#define IN_LIBGCOV 1
+#if defined(L_gcov_flush)
+#define GCOV_LINKAGE /* nothing */
+#endif
+#endif
+#include "gcov-io.h"
+
+#if defined(inhibit_libc)
+
+#ifdef L_gcov_flush
+void __gcov_flush (void) {}
+#endif
+
+#ifdef L_gcov_reset
+void __gcov_reset (void) {}
+#endif
+
+#ifdef L_gcov_dump
+void __gcov_dump (void) {}
+#endif
+
+#else
+
+extern void gcov_clear (void) ATTRIBUTE_HIDDEN;
+extern void gcov_exit (void) ATTRIBUTE_HIDDEN;
+extern void set_gcov_dump_complete (void) ATTRIBUTE_HIDDEN;
+extern void reset_gcov_dump_complete (void) ATTRIBUTE_HIDDEN;
+
+#ifdef L_gcov_flush
+
+#ifdef __GTHREAD_MUTEX_INIT
+ATTRIBUTE_HIDDEN __gthread_mutex_t __gcov_flush_mx = __GTHREAD_MUTEX_INIT;
+#define init_mx_once()
+#else
+__gthread_mutex_t __gcov_flush_mx ATTRIBUTE_HIDDEN;
+
+static void
+init_mx (void)
+{
+  __GTHREAD_MUTEX_INIT_FUNCTION (&__gcov_flush_mx);
+}
+static void
+init_mx_once (void)
+{
+  static __gthread_once_t once = __GTHREAD_ONCE_INIT;
+  __gthread_once (&once, init_mx);
+}
+#endif
+
+/* Called before fork or exec - write out profile information gathered so
+   far and reset it to zero.  This avoids duplication or loss of the
+   profile information gathered so far.  */
+
+void
+__gcov_flush (void)
+{
+  init_mx_once ();
+  __gthread_mutex_lock (&__gcov_flush_mx);
+
+  gcov_exit ();
+  gcov_clear ();
+
+  __gthread_mutex_unlock (&__gcov_flush_mx);
+}
+
+#endif /* L_gcov_flush */
+
+#ifdef L_gcov_reset
+
+/* Function that can be called from application to reset counters to zero,
+   in order to collect profile in region of interest.  */
+
+void
+__gcov_reset (void)
+{
+  gcov_clear ();
+  /* Re-enable dumping to support collecting profile in multiple regions
+     of interest.  */
+  reset_gcov_dump_complete ();
+}
+
+#endif /* L_gcov_reset */
+
+#ifdef L_gcov_dump
+
+/* Function that can be called from application to write profile collected
+   so far, in order to collect profile in region of interest.  */
+
+void
+__gcov_dump (void)
+{
+  gcov_exit ();
+  /* Prevent profile from being dumped a second time on application exit.  */
+  set_gcov_dump_complete ();
+}
+
+#endif /* L_gcov_dump */
+
+
+#ifdef L_gcov_fork
+/* A wrapper for the fork function.  Flushes the accumulated profiling data, so
+   that they are not counted twice.  */
+
+pid_t
+__gcov_fork (void)
+{
+  pid_t pid;
+  extern __gthread_mutex_t __gcov_flush_mx;
+  __gcov_flush ();
+  pid = fork ();
+  if (pid == 0)
+    __GTHREAD_MUTEX_INIT_FUNCTION (&__gcov_flush_mx);
+  return pid;
+}
+#endif
+
+#ifdef L_gcov_execl
+/* A wrapper for the execl function.  Flushes the accumulated profiling data, so
+   that they are not lost.  */
+
+int
+__gcov_execl (const char *path, char *arg, ...)
+{
+  va_list ap, aq;
+  unsigned i, length;
+  char **args;
+
+  __gcov_flush ();
+
+  va_start (ap, arg);
+  va_copy (aq, ap);
+
+  length = 2;
+  while (va_arg (ap, char *))
+    length++;
+  va_end (ap);
+
+  args = (char **) alloca (length * sizeof (void *));
+  args[0] = arg;
+  for (i = 1; i < length; i++)
+    args[i] = va_arg (aq, char *);
+  va_end (aq);
+
+  return execv (path, args);
+}
+#endif
+
+#ifdef L_gcov_execlp
+/* A wrapper for the execlp function.  Flushes the accumulated profiling data, so
+   that they are not lost.  */
+
+int
+__gcov_execlp (const char *path, char *arg, ...)
+{
+  va_list ap, aq;
+  unsigned i, length;
+  char **args;
+
+  __gcov_flush ();
+
+  va_start (ap, arg);
+  va_copy (aq, ap);
+
+  length = 2;
+  while (va_arg (ap, char *))
+    length++;
+  va_end (ap);
+
+  args = (char **) alloca (length * sizeof (void *));
+  args[0] = arg;
+  for (i = 1; i < length; i++)
+    args[i] = va_arg (aq, char *);
+  va_end (aq);
+
+  return execvp (path, args);
+}
+#endif
+
+#ifdef L_gcov_execle
+/* A wrapper for the execle function.  Flushes the accumulated profiling data, so
+   that they are not lost.  */
+
+int
+__gcov_execle (const char *path, char *arg, ...)
+{
+  va_list ap, aq;
+  unsigned i, length;
+  char **args;
+  char **envp;
+
+  __gcov_flush ();
+
+  va_start (ap, arg);
+  va_copy (aq, ap);
+
+  length = 2;
+  while (va_arg (ap, char *))
+    length++;
+  va_end (ap);
+
+  args = (char **) alloca (length * sizeof (void *));
+  args[0] = arg;
+  for (i = 1; i < length; i++)
+    args[i] = va_arg (aq, char *);
+  envp = va_arg (aq, char **);
+  va_end (aq);
+
+  return execve (path, args, envp);
+}
+#endif
+
+#ifdef L_gcov_execv
+/* A wrapper for the execv function.  Flushes the accumulated profiling data, so
+   that they are not lost.  */
+
+int
+__gcov_execv (const char *path, char *const argv[])
+{
+  __gcov_flush ();
+  return execv (path, argv);
+}
+#endif
+
+#ifdef L_gcov_execvp
+/* A wrapper for the execvp function.  Flushes the accumulated profiling data, so
+   that they are not lost.  */
+
+int
+__gcov_execvp (const char *path, char *const argv[])
+{
+  __gcov_flush ();
+  return execvp (path, argv);
+}
+#endif
+
+#ifdef L_gcov_execve
+/* A wrapper for the execve function.  Flushes the accumulated profiling data, so
+   that they are not lost.  */
+
+int
+__gcov_execve (const char *path, char *const argv[], char *const envp[])
+{
+  __gcov_flush ();
+  return execve (path, argv, envp);
+}
+#endif
+#endif /* inhibit_libc */
Index: libgcc/libgcov-merge.c
===================================================================
--- libgcc/libgcov-merge.c	(revision 0)
+++ libgcc/libgcov-merge.c	(revision 0)
@@ -0,0 +1,211 @@ 
+/* Routines required for instrumenting a program.  */
+/* Compile this one with gcc.  */
+/* Copyright (C) 1989-2013 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+#include "libgcov.h"
+
+#if defined(inhibit_libc)
+#define IN_LIBGCOV (-1)
+#else
+#define IN_LIBGCOV 1
+#endif
+
+#include "gcov-io.h"
+
+#if defined(inhibit_libc)
+/* If libc and its header files are not available, provide dummy functions.  */
+
+#ifdef L_gcov_merge_add
+void __gcov_merge_add (gcov_type *counters  __attribute__ ((unused)),
+                       unsigned n_counters __attribute__ ((unused)),
+                       unsigned gcov_type *src __attribute__ ((unused)),
+                       unsigned w __attribute__ ((unused))) {}
+#endif
+
+#ifdef L_gcov_merge_single
+void __gcov_merge_single (gcov_type *counters  __attribute__ ((unused)),
+                          unsigned n_counters __attribute__ ((unused)),
+                          unsigned gcov_type *src __attribute__ ((unused)),
+                          unsigned w __attribute__ ((unused))) {}
+#endif
+
+#ifdef L_gcov_merge_delta
+void __gcov_merge_delta (gcov_type *counters  __attribute__ ((unused)),
+                         unsigned n_counters __attribute__ ((unused)),
+                         unsigned gcov_type *src __attribute__ ((unused)),
+                         unsigned w __attribute__ ((unused))) {}
+#endif
+
+#else
+
+#ifdef L_gcov_merge_add
+/* The profile merging function that just adds the counters.  It is given
+   an array COUNTERS of N_COUNTERS old counters and it reads the same number
+   of counters from the gcov file.  */
+void
+__gcov_merge_add (gcov_type *counters, unsigned n_counters,
+                  gcov_type *src, unsigned w)
+{
+  int in_mem = (src != 0);
+
+  for (; n_counters; counters++, n_counters--)
+    {
+      gcov_type value;
+
+      if (in_mem)
+        value = *(src++);
+      else
+        value = gcov_read_counter ();
+
+      *counters += w * value;
+    }
+}
+#endif /* L_gcov_merge_add */
+
+#ifdef L_gcov_merge_ior
+/* The profile merging function that just adds the counters.  It is given
+   an array COUNTERS of N_COUNTERS old counters and it reads the same number
+   of counters from the gcov file.  */
+void
+__gcov_merge_ior (gcov_type *counters, unsigned n_counters,
+                  gcov_type *src, unsigned w __attribute__ ((unused)))
+{
+  int in_mem = (src != 0);
+
+  for (; n_counters; counters++, n_counters--)
+    {
+      gcov_type value;
+
+      if (in_mem)
+        value = *(src++);
+      else
+        value = gcov_read_counter ();
+
+      *counters |= value;
+    }
+}
+#endif
+
+#ifdef L_gcov_merge_single
+/* The profile merging function for choosing the most common value.
+   It is given an array COUNTERS of N_COUNTERS old counters and it
+   reads the same number of counters from the gcov file.  The counters
+   are split into 3-tuples where the members of the tuple have
+   meanings:
+
+   -- the stored candidate on the most common value of the measured entity
+   -- counter
+   -- total number of evaluations of the value  */
+void
+__gcov_merge_single (gcov_type *counters, unsigned n_counters,
+                     gcov_type *src, unsigned w)
+{
+  unsigned i, n_measures;
+  gcov_type value, counter, all;
+  int in_mem = (src != 0);
+
+  gcc_assert (!(n_counters % 3));
+  n_measures = n_counters / 3;
+  for (i = 0; i < n_measures; i++, counters += 3, src += 3)
+    {
+      if (in_mem)
+        {
+          value = src[0];
+          counter = src[1];
+          all = src[2];
+        }
+      else
+        {
+          value = gcov_read_counter ();
+          counter = gcov_read_counter ();
+          all = gcov_read_counter ();
+        }
+      counter *= w;
+
+      if (counters[0] == value)
+        counters[1] += counter;
+      else if (counter > counters[1])
+        {
+          counters[0] = value;
+          counters[1] = counter - counters[1];
+        }
+      else
+        counters[1] -= counter;
+      counters[2] += all;
+    }
+}
+#endif /* L_gcov_merge_single */
+
+#ifdef L_gcov_merge_delta
+/* The profile merging function for choosing the most common
+   difference between two consecutive evaluations of the value.  It is
+   given an array COUNTERS of N_COUNTERS old counters and it reads the
+   same number of counters from the gcov file.  The counters are split
+   into 4-tuples where the members of the tuple have meanings:
+
+   -- the last value of the measured entity
+   -- the stored candidate on the most common difference
+   -- counter
+   -- total number of evaluations of the value  */
+void
+__gcov_merge_delta (gcov_type *counters, unsigned n_counters,
+                    gcov_type *src, unsigned w)
+{
+  unsigned i, n_measures;
+  gcov_type value, counter, all;
+  int in_mem = (src != 0);
+
+  gcc_assert (!(n_counters % 4));
+  n_measures = n_counters / 4;
+  for (i = 0; i < n_measures; i++, counters += 4, src += 4)
+    {
+      if (in_mem)
+        {
+          value = src[1];
+          counter = src[2];
+          all = src[3];
+        }
+      else
+        {
+          /* last = */ gcov_read_counter ();
+          value = gcov_read_counter ();
+          counter = gcov_read_counter ();
+          all = gcov_read_counter ();
+        }
+      counter *= w;
+
+      if (counters[1] == value)
+        counters[2] += counter;
+      else if (counter > counters[2])
+        {
+          counters[1] = value;
+          counters[2] = counter - counters[2];
+        }
+      else
+        counters[2] -= counter;
+      counters[3] += all;
+    }
+}
+#endif /* L_gcov_merge_delta */
+#endif /* inhibit_libc */
Index: libgcc/libgcov-profiler.c
===================================================================
--- libgcc/libgcov-profiler.c	(revision 0)
+++ libgcc/libgcov-profiler.c	(revision 0)
@@ -0,0 +1,204 @@ 
+/* Routines required for instrumenting a program.  */
+/* Compile this one with gcc.  */
+/* Copyright (C) 1989-2013 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+#include "libgcov.h"
+
+#if !defined(inhibit_libc)
+#define IN_LIBGCOV 1
+#include "gcov-io.h"
+
+#ifdef L_gcov_interval_profiler
+/* If VALUE is in interval <START, START + STEPS - 1>, then increases the
+   corresponding counter in COUNTERS.  If the VALUE is above or below
+   the interval, COUNTERS[STEPS] or COUNTERS[STEPS + 1] is increased
+   instead.  */
+
+void
+__gcov_interval_profiler (gcov_type *counters, gcov_type value,
+                          int start, unsigned steps)
+{
+  gcov_type delta = value - start;
+  if (delta < 0)
+    counters[steps + 1]++;
+  else if (delta >= steps)
+    counters[steps]++;
+  else
+    counters[delta]++;
+}
+#endif
+
+#ifdef L_gcov_pow2_profiler
+/* If VALUE is a power of two, COUNTERS[1] is incremented.  Otherwise
+   COUNTERS[0] is incremented.  */
+
+void
+__gcov_pow2_profiler (gcov_type *counters, gcov_type value)
+{
+  if (value & (value - 1))
+    counters[0]++;
+  else
+    counters[1]++;
+}
+#endif
+
+/* Tries to determine the most common value among its inputs.  Checks if the
+   value stored in COUNTERS[0] matches VALUE.  If this is the case, COUNTERS[1]
+   is incremented.  If this is not the case and COUNTERS[1] is not zero,
+   COUNTERS[1] is decremented.  Otherwise COUNTERS[1] is set to one and
+   VALUE is stored to COUNTERS[0].  This algorithm guarantees that if this
+   function is called more than 50% of the time with one value, this value
+   will be in COUNTERS[0] in the end.
+
+   In any case, COUNTERS[2] is incremented.  */
+
+static inline void
+__gcov_one_value_profiler_body (gcov_type *counters, gcov_type value)
+{
+  if (value == counters[0])
+    counters[1]++;
+  else if (counters[1] == 0)
+    {
+      counters[1] = 1;
+      counters[0] = value;
+    }
+  else
+    counters[1]--;
+  counters[2]++;
+}
+
+#ifdef L_gcov_one_value_profiler
+void
+__gcov_one_value_profiler (gcov_type *counters, gcov_type value)
+{
+  __gcov_one_value_profiler_body (counters, value);
+}
+#endif
+
+#ifdef L_gcov_indirect_call_profiler
+/* This function exist only for workaround of binutils bug 14342.
+   Once this compatibility hack is obsolette, it can be removed.  */
+
+/* By default, the C++ compiler will use function addresses in the
+   vtable entries.  Setting TARGET_VTABLE_USES_DESCRIPTORS to nonzero
+   tells the compiler to use function descriptors instead.  The value
+   of this macro says how many words wide the descriptor is (normally 2),
+   but it may be dependent on target flags.  Since we do not have access
+   to the target flags here we just check to see if it is set and use
+   that to set VTABLE_USES_DESCRIPTORS to 0 or 1.
+
+   It is assumed that the address of a function descriptor may be treated
+   as a pointer to a function.  */
+
+#ifdef TARGET_VTABLE_USES_DESCRIPTORS
+#define VTABLE_USES_DESCRIPTORS 1
+#else
+#define VTABLE_USES_DESCRIPTORS 0
+#endif
+
+/* Tries to determine the most common value among its inputs. */
+void
+__gcov_indirect_call_profiler (gcov_type* counter, gcov_type value,
+                               void* cur_func, void* callee_func)
+{
+  /* If the C++ virtual tables contain function descriptors then one
+     function may have multiple descriptors and we need to dereference
+     the descriptors to see if they point to the same function.  */
+  if (cur_func == callee_func
+      || (VTABLE_USES_DESCRIPTORS && callee_func
+          && *(void **) cur_func == *(void **) callee_func))
+    __gcov_one_value_profiler_body (counter, value);
+}
+
+#endif
+#ifdef L_gcov_indirect_call_profiler_v2
+
+/* These two variables are used to actually track caller and callee.  Keep
+   them in TLS memory so races are not common (they are written to often).
+   The variables are set directly by GCC instrumented code, so declaration
+   here must match one in tree-profile.c  */
+
+#if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS)
+__thread
+#endif
+void * __gcov_indirect_call_callee;
+#if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS)
+__thread
+#endif
+gcov_type * __gcov_indirect_call_counters;
+
+/* By default, the C++ compiler will use function addresses in the
+   vtable entries.  Setting TARGET_VTABLE_USES_DESCRIPTORS to nonzero
+   tells the compiler to use function descriptors instead.  The value
+   of this macro says how many words wide the descriptor is (normally 2),
+   but it may be dependent on target flags.  Since we do not have access
+   to the target flags here we just check to see if it is set and use
+   that to set VTABLE_USES_DESCRIPTORS to 0 or 1.
+
+   It is assumed that the address of a function descriptor may be treated
+   as a pointer to a function.  */
+
+#ifdef TARGET_VTABLE_USES_DESCRIPTORS
+#define VTABLE_USES_DESCRIPTORS 1
+#else
+#define VTABLE_USES_DESCRIPTORS 0
+#endif
+
+/* Tries to determine the most common value among its inputs. */
+void
+__gcov_indirect_call_profiler_v2 (gcov_type value, void* cur_func)
+{
+  /* If the C++ virtual tables contain function descriptors then one
+     function may have multiple descriptors and we need to dereference
+     the descriptors to see if they point to the same function.  */
+  if (cur_func == __gcov_indirect_call_callee
+      || (VTABLE_USES_DESCRIPTORS && __gcov_indirect_call_callee
+          && *(void **) cur_func == *(void **) __gcov_indirect_call_callee))
+    __gcov_one_value_profiler_body (__gcov_indirect_call_counters, value);
+}
+#endif
+
+#ifdef L_gcov_average_profiler
+/* Increase corresponding COUNTER by VALUE.  FIXME: Perhaps we want
+   to saturate up.  */
+
+void
+__gcov_average_profiler (gcov_type *counters, gcov_type value)
+{
+  counters[0] += value;
+  counters[1] ++;
+}
+#endif
+
+#ifdef L_gcov_ior_profiler
+/* Bitwise-OR VALUE into COUNTER.  */
+
+void
+__gcov_ior_profiler (gcov_type *counters, gcov_type value)
+{
+  *counters |= value;
+}
+#endif
+
+#endif /* inhibit_libc */
Index: libgcc/libgcov.h
===================================================================
--- libgcc/libgcov.h	(revision 0)
+++ libgcc/libgcov.h	(revision 0)
@@ -0,0 +1,35 @@ 
+/* Header file for libgcov-*.c.
+   Contributed by Rong Xu <xur@google.com>.
+   Copyright (C) 2013 Free Software Foundation, Inc.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it under
+   the terms of the GNU General Public License as published by the Free
+   Software Foundation; either version 3, or (at your option) any later
+   version.
+
+   GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+   for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GCC; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_LIBGCOV_H
+#define GCC_LIBGCOV_H
+
+#ifndef GCOV_FOR_HOST_BUILD
+
+#include "tconfig.h"
+#include "tsystem.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "libgcc_tm.h"
+
+#endif /* GCOV_FOR_HOST_BUILD */
+
+#endif /* ! GCC_LIBGCOV_H */
+
Index: libgcc/Makefile.in
===================================================================
--- libgcc/Makefile.in	(revision 204285)
+++ libgcc/Makefile.in	(working copy)
@@ -852,18 +852,30 @@  include $(iterator)
 
 # Build libgcov components.
 
-# Defined in libgcov.c, included only in gcov library
-LIBGCOV = _gcov _gcov_merge_add _gcov_merge_single _gcov_merge_delta \
-    _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
-    _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump \
-    _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \
+LIBGCOV_MERGE = _gcov_merge_add _gcov_merge_single _gcov_merge_delta _gcov_merge_ior
+LIBGCOV_PROFILER = _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \
     _gcov_indirect_call_profiler _gcov_average_profiler _gcov_ior_profiler \
-    _gcov_merge_ior _gcov_indirect_call_profiler_v2
+    _gcov_indirect_call_profiler_v2
+LIBGCOV_INTERFACE = _gcov_flush _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
+    _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump
+LIBGCOV_DRIVER = _gcov 
 
-libgcov-objects = $(patsubst %,%$(objext),$(LIBGCOV))
+libgcov-merge-objects = $(patsubst %,%$(objext),$(LIBGCOV_MERGE))
+libgcov-profiler-objects = $(patsubst %,%$(objext),$(LIBGCOV_PROFILER))
+libgcov-interface-objects = $(patsubst %,%$(objext),$(LIBGCOV_INTERFACE))
+libgcov-driver-objects = $(patsubst %,%$(objext),$(LIBGCOV_DRIVER))
+libgcov-objects = $(libgcov-merge-objects) $(libgcov-profiler-objects) \
+                 $(libgcov-interface-objects) $(libgcov-driver-objects)
 
-$(libgcov-objects): %$(objext): $(srcdir)/libgcov.c
-	$(gcc_compile) -DL$* -c $(srcdir)/libgcov.c
+$(libgcov-merge-objects): %$(objext): $(srcdir)/libgcov-merge.c $(srcdir)/libgcov.h
+	$(gcc_compile) -DL$* -c $(srcdir)/libgcov-merge.c
+$(libgcov-profiler-objects): %$(objext): $(srcdir)/libgcov-profiler.c $(srcdir)/libgcov.h
+	$(gcc_compile) -DL$* -c $(srcdir)/libgcov-profiler.c
+$(libgcov-interface-objects): %$(objext): $(srcdir)/libgcov-interface.c $(srcdir)/libgcov.h
+	$(gcc_compile) -DL$* -c $(srcdir)/libgcov-interface.c
+$(libgcov-driver-objects): %$(objext): $(srcdir)/libgcov-driver.c \
+  $(srcdir)/libgcov-driver-system.c $(srcdir)/libgcov.h
+	$(gcc_compile) -DL$* -c $(srcdir)/libgcov-driver.c
 
 
 # Static libraries.
Index: libgcc/libgcov.c
===================================================================
--- libgcc/libgcov.c	(revision 204285)
+++ libgcc/libgcov.c	(working copy)
@@ -1,1374 +0,0 @@ 
-/* Routines required for instrumenting a program.  */
-/* Compile this one with gcc.  */
-/* Copyright (C) 1989-2013 Free Software Foundation, Inc.
-
-This file is part of GCC.
-
-GCC is free software; you can redistribute it and/or modify it under
-the terms of the GNU General Public License as published by the Free
-Software Foundation; either version 3, or (at your option) any later
-version.
-
-GCC is distributed in the hope that it will be useful, but WITHOUT ANY
-WARRANTY; without even the implied warranty of MERCHANTABILITY or
-FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-for more details.
-
-Under Section 7 of GPL version 3, you are granted additional
-permissions described in the GCC Runtime Library Exception, version
-3.1, as published by the Free Software Foundation.
-
-You should have received a copy of the GNU General Public License and
-a copy of the GCC Runtime Library Exception along with this program;
-see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
-<http://www.gnu.org/licenses/>.  */
-
-#include "tconfig.h"
-#include "tsystem.h"
-#include "coretypes.h"
-#include "tm.h"
-#include "libgcc_tm.h"
-#include "gthr.h"
-
-#if defined(inhibit_libc)
-#define IN_LIBGCOV (-1)
-#else
-#define IN_LIBGCOV 1
-#if defined(L_gcov)
-#define GCOV_LINKAGE /* nothing */
-#endif
-#endif
-#include "gcov-io.h"
-
-#if defined(inhibit_libc)
-/* If libc and its header files are not available, provide dummy functions.  */
-
-#ifdef L_gcov
-void __gcov_init (struct gcov_info *p __attribute__ ((unused))) {}
-void __gcov_flush (void) {}
-#endif
-
-#ifdef L_gcov_reset
-void __gcov_reset (void) {}
-#endif
-
-#ifdef L_gcov_dump
-void __gcov_dump (void) {}
-#endif
-
-#ifdef L_gcov_merge_add
-void __gcov_merge_add (gcov_type *counters  __attribute__ ((unused)),
-		       unsigned n_counters __attribute__ ((unused))) {}
-#endif
-
-#ifdef L_gcov_merge_single
-void __gcov_merge_single (gcov_type *counters  __attribute__ ((unused)),
-			  unsigned n_counters __attribute__ ((unused))) {}
-#endif
-
-#ifdef L_gcov_merge_delta
-void __gcov_merge_delta (gcov_type *counters  __attribute__ ((unused)),
-			 unsigned n_counters __attribute__ ((unused))) {}
-#endif
-
-#else
-
-#include <string.h>
-#if GCOV_LOCKED
-#include <fcntl.h>
-#include <errno.h>
-#include <sys/stat.h>
-#endif
-
-extern void gcov_clear (void) ATTRIBUTE_HIDDEN;
-extern void gcov_exit (void) ATTRIBUTE_HIDDEN;
-extern int gcov_dump_complete ATTRIBUTE_HIDDEN;
-
-#ifdef L_gcov
-#include "gcov-io.c"
-
-struct gcov_fn_buffer
-{
-  struct gcov_fn_buffer *next;
-  unsigned fn_ix;
-  struct gcov_fn_info info;
-  /* note gcov_fn_info ends in a trailing array.  */
-};
-
-struct gcov_summary_buffer
-{
-  struct gcov_summary_buffer *next;
-  struct gcov_summary summary;
-};
-
-/* Chain of per-object gcov structures.  */
-static struct gcov_info *gcov_list;
-
-/* Size of the longest file name. */
-static size_t gcov_max_filename = 0;
-
-/* Flag when the profile has already been dumped via __gcov_dump().  */
-int gcov_dump_complete = 0;
-
-/* Make sure path component of the given FILENAME exists, create
-   missing directories. FILENAME must be writable.
-   Returns zero on success, or -1 if an error occurred.  */
-
-static int
-create_file_directory (char *filename)
-{
-#if !defined(TARGET_POSIX_IO) && !defined(_WIN32)
-  (void) filename;
-  return -1;
-#else
-  char *s;
-
-  s = filename;
-
-  if (HAS_DRIVE_SPEC(s))
-    s += 2;
-  if (IS_DIR_SEPARATOR(*s))
-    ++s;
-  for (; *s != '\0'; s++)
-    if (IS_DIR_SEPARATOR(*s))
-      {
-        char sep = *s;
-	*s  = '\0';
-
-        /* Try to make directory if it doesn't already exist.  */
-        if (access (filename, F_OK) == -1
-#ifdef TARGET_POSIX_IO
-            && mkdir (filename, 0755) == -1
-#else
-            && mkdir (filename) == -1
-#endif
-            /* The directory might have been made by another process.  */
-	    && errno != EEXIST)
-	  {
-            fprintf (stderr, "profiling:%s:Cannot create directory\n",
-		     filename);
-            *s = sep;
-	    return -1;
-	  };
-
-	*s = sep;
-      };
-  return 0;
-#endif
-}
-
-static struct gcov_fn_buffer *
-free_fn_data (const struct gcov_info *gi_ptr, struct gcov_fn_buffer *buffer,
-	      unsigned limit)
-{
-  struct gcov_fn_buffer *next;
-  unsigned ix, n_ctr = 0;
-  
-  if (!buffer)
-    return 0;
-  next = buffer->next;
-
-  for (ix = 0; ix != limit; ix++)
-    if (gi_ptr->merge[ix])
-      free (buffer->info.ctrs[n_ctr++].values);
-  free (buffer);
-  return next;
-}
-  
-static struct gcov_fn_buffer **
-buffer_fn_data (const char *filename, const struct gcov_info *gi_ptr,
-		struct gcov_fn_buffer **end_ptr, unsigned fn_ix)
-{
-  unsigned n_ctrs = 0, ix = 0;
-  struct gcov_fn_buffer *fn_buffer;
-  unsigned len;
-
-  for (ix = GCOV_COUNTERS; ix--;)
-    if (gi_ptr->merge[ix])
-      n_ctrs++;
-
-  len = sizeof (*fn_buffer) + sizeof (fn_buffer->info.ctrs[0]) * n_ctrs;
-  fn_buffer = (struct gcov_fn_buffer *)malloc (len);
-
-  if (!fn_buffer)
-    goto fail;
-  
-  fn_buffer->next = 0;
-  fn_buffer->fn_ix = fn_ix;
-  fn_buffer->info.ident = gcov_read_unsigned ();
-  fn_buffer->info.lineno_checksum = gcov_read_unsigned ();
-  fn_buffer->info.cfg_checksum = gcov_read_unsigned ();
-
-  for (n_ctrs = ix = 0; ix != GCOV_COUNTERS; ix++)
-    {
-      gcov_unsigned_t length;
-      gcov_type *values;
-
-      if (!gi_ptr->merge[ix])
-	continue;
-      
-      if (gcov_read_unsigned () != GCOV_TAG_FOR_COUNTER (ix))
-	{
-	  len = 0;
-	  goto fail;
-	}
-
-      length = GCOV_TAG_COUNTER_NUM (gcov_read_unsigned ());
-      len = length * sizeof (gcov_type);
-      values = (gcov_type *)malloc (len);
-      if (!values)
-	goto fail;
-      
-      fn_buffer->info.ctrs[n_ctrs].num = length;
-      fn_buffer->info.ctrs[n_ctrs].values = values;
-
-      while (length--)
-	*values++ = gcov_read_counter ();
-      n_ctrs++;
-    }
-  
-  *end_ptr = fn_buffer;
-  return &fn_buffer->next;
-
- fail:
-  fprintf (stderr, "profiling:%s:Function %u %s %u \n", filename, fn_ix,
-	   len ? "cannot allocate" : "counter mismatch", len ? len : ix);
-
-  return (struct gcov_fn_buffer **)free_fn_data (gi_ptr, fn_buffer, ix);
-}
-
-/* Add an unsigned value to the current crc */
-
-static gcov_unsigned_t
-crc32_unsigned (gcov_unsigned_t crc32, gcov_unsigned_t value)
-{
-  unsigned ix;
-
-  for (ix = 32; ix--; value <<= 1)
-    {
-      unsigned feedback;
-
-      feedback = (value ^ crc32) & 0x80000000 ? 0x04c11db7 : 0;
-      crc32 <<= 1;
-      crc32 ^= feedback;
-    }
-
-  return crc32;
-}
-
-/* Check if VERSION of the info block PTR matches libgcov one.
-   Return 1 on success, or zero in case of versions mismatch.
-   If FILENAME is not NULL, its value used for reporting purposes
-   instead of value from the info block.  */
-
-static int
-gcov_version (struct gcov_info *ptr, gcov_unsigned_t version,
-	      const char *filename)
-{
-  if (version != GCOV_VERSION)
-    {
-      char v[4], e[4];
-
-      GCOV_UNSIGNED2STRING (v, version);
-      GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
-
-      fprintf (stderr,
-	       "profiling:%s:Version mismatch - expected %.4s got %.4s\n",
-	       filename? filename : ptr->filename, e, v);
-      return 0;
-    }
-  return 1;
-}
-
-/* Insert counter VALUE into HISTOGRAM.  */
-
-static void
-gcov_histogram_insert(gcov_bucket_type *histogram, gcov_type value)
-{
-  unsigned i;
-
-  i = gcov_histo_index(value);
-  histogram[i].num_counters++;
-  histogram[i].cum_value += value;
-  if (value < histogram[i].min_value)
-    histogram[i].min_value = value;
-}
-
-/* Computes a histogram of the arc counters to place in the summary SUM.  */
-
-static void
-gcov_compute_histogram (struct gcov_summary *sum)
-{
-  struct gcov_info *gi_ptr;
-  const struct gcov_fn_info *gfi_ptr;
-  const struct gcov_ctr_info *ci_ptr;
-  struct gcov_ctr_summary *cs_ptr;
-  unsigned t_ix, f_ix, ctr_info_ix, ix;
-  int h_ix;
-
-  /* This currently only applies to arc counters.  */
-  t_ix = GCOV_COUNTER_ARCS;
-
-  /* First check if there are any counts recorded for this counter.  */
-  cs_ptr = &(sum->ctrs[t_ix]);
-  if (!cs_ptr->num)
-    return;
-
-  for (h_ix = 0; h_ix < GCOV_HISTOGRAM_SIZE; h_ix++)
-    {
-      cs_ptr->histogram[h_ix].num_counters = 0;
-      cs_ptr->histogram[h_ix].min_value = cs_ptr->run_max;
-      cs_ptr->histogram[h_ix].cum_value = 0;
-    }
-
-  /* Walk through all the per-object structures and record each of
-     the count values in histogram.  */
-  for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
-    {
-      if (!gi_ptr->merge[t_ix])
-        continue;
-
-      /* Find the appropriate index into the gcov_ctr_info array
-         for the counter we are currently working on based on the
-         existence of the merge function pointer for this object.  */
-      for (ix = 0, ctr_info_ix = 0; ix < t_ix; ix++)
-        {
-          if (gi_ptr->merge[ix])
-            ctr_info_ix++;
-        }
-      for (f_ix = 0; f_ix != gi_ptr->n_functions; f_ix++)
-        {
-          gfi_ptr = gi_ptr->functions[f_ix];
-
-          if (!gfi_ptr || gfi_ptr->key != gi_ptr)
-            continue;
-
-          ci_ptr = &gfi_ptr->ctrs[ctr_info_ix];
-          for (ix = 0; ix < ci_ptr->num; ix++)
-            gcov_histogram_insert (cs_ptr->histogram, ci_ptr->values[ix]);
-        }
-    }
-}
-
-/* Dump the coverage counts. We merge with existing counts when
-   possible, to avoid growing the .da files ad infinitum. We use this
-   program's checksum to make sure we only accumulate whole program
-   statistics to the correct summary. An object file might be embedded
-   in two separate programs, and we must keep the two program
-   summaries separate.  */
-
-void
-gcov_exit (void)
-{
-  struct gcov_info *gi_ptr;
-  const struct gcov_fn_info *gfi_ptr;
-  struct gcov_summary this_prg; /* summary for program.  */
-#if !GCOV_LOCKED
-  struct gcov_summary all_prg;  /* summary for all instances of program.  */
-#endif
-  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;
-
-  /* Prevent the counters from being dumped a second time on exit when the
-     application already wrote out the profile using __gcov_dump().  */
-  if (gcov_dump_complete)
-    return;
-
-#if !GCOV_LOCKED
-  memset (&all_prg, 0, sizeof (all_prg));
-#endif
-  /* 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++;
-	    }
-	}
-    }
-  gcov_compute_histogram (&this_prg);
-
-  {
-    /* 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);
-  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;
-#if !GCOV_LOCKED
-      struct gcov_ctr_summary *cs_all;
-#endif
-      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;
-      struct gcov_summary_buffer *next_sum_buffer, *sum_buffer = 0;
-      struct gcov_summary_buffer **sum_tail = &sum_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);
-
-      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 ();
-	      gcov_read_summary (&tmp);
-	      if ((error = gcov_is_error ()))
-		goto read_error;
-	      if (summary_pos)
-                {
-                  /* 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 *)
-                      malloc (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 = 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];
-
-	  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;
-              if (cs_prg->runs == 1)
-                memcpy (cs_prg->histogram, cs_tprg->histogram,
-                        sizeof (gcov_bucket_type) * GCOV_HISTOGRAM_SIZE);
-              else
-                gcov_histogram_merge (cs_prg->histogram, cs_tprg->histogram);
-	    }
-	  else if (cs_prg->runs)
-	    goto read_mismatch;
-
-#if !GCOV_LOCKED
-	  cs_all = &all_prg.ctrs[t_ix];
-	  if (!cs_all->runs && cs_prg->runs)
-            {
-              cs_all->num = cs_prg->num;
-              cs_all->runs = cs_prg->runs;
-              cs_all->sum_all = cs_prg->sum_all;
-              cs_all->run_max = cs_prg->run_max;
-              cs_all->sum_max = cs_prg->sum_max;
-            }
-	  else if (!all_prg.checksum
-                   /* Don't compare the histograms, which may have slight
-                      variations depending on the order they were updated
-                      due to the truncating integer divides used in the
-                      merge.  */
-                   && (cs_all->num != cs_prg->num
-                       || cs_all->runs != cs_prg->runs
-                       || cs_all->sum_all != cs_prg->sum_all
-                       || cs_all->run_max != cs_prg->run_max
-                       || cs_all->sum_max != cs_prg->sum_max))
-	    {
-	      fprintf (stderr,
-                       "profiling:%s:Data file mismatch - some data files may "
-                       "have been concurrently updated without locking support\n",
-		       gi_filename);
-	      all_prg.checksum = ~0u;
-	    }
-#endif
-	}
-
-      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);
-
-      /* Rewrite all the summaries that were after the summary we merged
-         into. This is necessary as the merged summary may have a different
-         size due to the number of non-zero histogram entries changing after
-         merging.  */
-      
-      while (sum_buffer)
-        {
-          gcov_write_summary (GCOV_TAG_PROGRAM_SUMMARY, &sum_buffer->summary);
-          next_sum_buffer = sum_buffer->next;
-          free (sum_buffer);
-          sum_buffer = next_sum_buffer;
-        }
-
-      /* 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);
-    }
-}
-
-/* Reset all counters to zero.  */
-
-void
-gcov_clear (void)
-{
-  const struct gcov_info *gi_ptr;
-
-  for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
-    {
-      unsigned f_ix;
-
-      for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
-	{
-	  unsigned t_ix;
-	  const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix];
-
-	  if (!gfi_ptr || gfi_ptr->key != gi_ptr)
-	    continue;
-	  const struct gcov_ctr_info *ci_ptr = gfi_ptr->ctrs;
-	  for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++)
-	    {
-	      if (!gi_ptr->merge[t_ix])
-		continue;
-	      
-	      memset (ci_ptr->values, 0, sizeof (gcov_type) * ci_ptr->num);
-	      ci_ptr++;
-	    }
-	}
-    }
-}
-
-/* Add a new object file onto the bb chain.  Invoked automatically
-   when running an object file's global ctors.  */
-
-void
-__gcov_init (struct gcov_info *info)
-{
-  if (!info->version || !info->n_functions)
-    return;
-  if (gcov_version (info, info->version, 0))
-    {
-      size_t filename_length = strlen(info->filename);
-
-      /* Refresh the longest file name information */
-      if (filename_length > gcov_max_filename)
-        gcov_max_filename = filename_length;
-
-      if (!gcov_list)
-	atexit (gcov_exit);
-
-      info->next = gcov_list;
-      gcov_list = info;
-    }
-  info->version = 0;
-}
-
-#ifdef __GTHREAD_MUTEX_INIT
-ATTRIBUTE_HIDDEN __gthread_mutex_t __gcov_flush_mx = __GTHREAD_MUTEX_INIT;
-#define init_mx_once()
-#else
-__gthread_mutex_t __gcov_flush_mx ATTRIBUTE_HIDDEN;
-
-static void
-init_mx (void)
-{
-  __GTHREAD_MUTEX_INIT_FUNCTION (&__gcov_flush_mx);
-}
-static void
-init_mx_once (void)
-{
-  static __gthread_once_t once = __GTHREAD_ONCE_INIT;
-  __gthread_once (&once, init_mx);
-}
-#endif
-
-/* Called before fork or exec - write out profile information gathered so
-   far and reset it to zero.  This avoids duplication or loss of the
-   profile information gathered so far.  */
-
-void
-__gcov_flush (void)
-{
-  init_mx_once ();
-  __gthread_mutex_lock (&__gcov_flush_mx);
-
-  gcov_exit ();
-  gcov_clear ();
-
-  __gthread_mutex_unlock (&__gcov_flush_mx);
-}
-
-#endif /* L_gcov */
-
-#ifdef L_gcov_reset
-
-/* Function that can be called from application to reset counters to zero,
-   in order to collect profile in region of interest.  */
-
-void
-__gcov_reset (void)
-{
-  gcov_clear ();
-  /* Re-enable dumping to support collecting profile in multiple regions
-     of interest.  */
-  gcov_dump_complete = 0;
-}
-
-#endif /* L_gcov_reset */
-
-#ifdef L_gcov_dump
-
-/* Function that can be called from application to write profile collected
-   so far, in order to collect profile in region of interest.  */
-
-void
-__gcov_dump (void)
-{
-  gcov_exit ();
-  /* Prevent profile from being dumped a second time on application exit.  */
-  gcov_dump_complete = 1;
-}
-
-#endif /* L_gcov_dump */
-
-#ifdef L_gcov_merge_add
-/* The profile merging function that just adds the counters.  It is given
-   an array COUNTERS of N_COUNTERS old counters and it reads the same number
-   of counters from the gcov file.  */
-void
-__gcov_merge_add (gcov_type *counters, unsigned n_counters)
-{
-  for (; n_counters; counters++, n_counters--)
-    *counters += gcov_read_counter ();
-}
-#endif /* L_gcov_merge_add */
-
-#ifdef L_gcov_merge_ior
-/* The profile merging function that just adds the counters.  It is given
-   an array COUNTERS of N_COUNTERS old counters and it reads the same number
-   of counters from the gcov file.  */
-void
-__gcov_merge_ior (gcov_type *counters, unsigned n_counters)
-{
-  for (; n_counters; counters++, n_counters--)
-    *counters |= gcov_read_counter ();
-}
-#endif
-
-#ifdef L_gcov_merge_single
-/* The profile merging function for choosing the most common value.
-   It is given an array COUNTERS of N_COUNTERS old counters and it
-   reads the same number of counters from the gcov file.  The counters
-   are split into 3-tuples where the members of the tuple have
-   meanings:
-
-   -- the stored candidate on the most common value of the measured entity
-   -- counter
-   -- total number of evaluations of the value  */
-void
-__gcov_merge_single (gcov_type *counters, unsigned n_counters)
-{
-  unsigned i, n_measures;
-  gcov_type value, counter, all;
-
-  gcc_assert (!(n_counters % 3));
-  n_measures = n_counters / 3;
-  for (i = 0; i < n_measures; i++, counters += 3)
-    {
-      value = gcov_read_counter ();
-      counter = gcov_read_counter ();
-      all = gcov_read_counter ();
-
-      if (counters[0] == value)
-	counters[1] += counter;
-      else if (counter > counters[1])
-	{
-	  counters[0] = value;
-	  counters[1] = counter - counters[1];
-	}
-      else
-	counters[1] -= counter;
-      counters[2] += all;
-    }
-}
-#endif /* L_gcov_merge_single */
-
-#ifdef L_gcov_merge_delta
-/* The profile merging function for choosing the most common
-   difference between two consecutive evaluations of the value.  It is
-   given an array COUNTERS of N_COUNTERS old counters and it reads the
-   same number of counters from the gcov file.  The counters are split
-   into 4-tuples where the members of the tuple have meanings:
-
-   -- the last value of the measured entity
-   -- the stored candidate on the most common difference
-   -- counter
-   -- total number of evaluations of the value  */
-void
-__gcov_merge_delta (gcov_type *counters, unsigned n_counters)
-{
-  unsigned i, n_measures;
-  gcov_type value, counter, all;
-
-  gcc_assert (!(n_counters % 4));
-  n_measures = n_counters / 4;
-  for (i = 0; i < n_measures; i++, counters += 4)
-    {
-      /* last = */ gcov_read_counter ();
-      value = gcov_read_counter ();
-      counter = gcov_read_counter ();
-      all = gcov_read_counter ();
-
-      if (counters[1] == value)
-	counters[2] += counter;
-      else if (counter > counters[2])
-	{
-	  counters[1] = value;
-	  counters[2] = counter - counters[2];
-	}
-      else
-	counters[2] -= counter;
-      counters[3] += all;
-    }
-}
-#endif /* L_gcov_merge_delta */
-
-#ifdef L_gcov_interval_profiler
-/* If VALUE is in interval <START, START + STEPS - 1>, then increases the
-   corresponding counter in COUNTERS.  If the VALUE is above or below
-   the interval, COUNTERS[STEPS] or COUNTERS[STEPS + 1] is increased
-   instead.  */
-
-void
-__gcov_interval_profiler (gcov_type *counters, gcov_type value,
-			  int start, unsigned steps)
-{
-  gcov_type delta = value - start;
-  if (delta < 0)
-    counters[steps + 1]++;
-  else if (delta >= steps)
-    counters[steps]++;
-  else
-    counters[delta]++;
-}
-#endif
-
-#ifdef L_gcov_pow2_profiler
-/* If VALUE is a power of two, COUNTERS[1] is incremented.  Otherwise
-   COUNTERS[0] is incremented.  */
-
-void
-__gcov_pow2_profiler (gcov_type *counters, gcov_type value)
-{
-  if (value & (value - 1))
-    counters[0]++;
-  else
-    counters[1]++;
-}
-#endif
-
-/* Tries to determine the most common value among its inputs.  Checks if the
-   value stored in COUNTERS[0] matches VALUE.  If this is the case, COUNTERS[1]
-   is incremented.  If this is not the case and COUNTERS[1] is not zero,
-   COUNTERS[1] is decremented.  Otherwise COUNTERS[1] is set to one and
-   VALUE is stored to COUNTERS[0].  This algorithm guarantees that if this
-   function is called more than 50% of the time with one value, this value
-   will be in COUNTERS[0] in the end.
-
-   In any case, COUNTERS[2] is incremented.  */
-
-static inline void
-__gcov_one_value_profiler_body (gcov_type *counters, gcov_type value)
-{
-  if (value == counters[0])
-    counters[1]++;
-  else if (counters[1] == 0)
-    {
-      counters[1] = 1;
-      counters[0] = value;
-    }
-  else
-    counters[1]--;
-  counters[2]++;
-}
-
-#ifdef L_gcov_one_value_profiler
-void
-__gcov_one_value_profiler (gcov_type *counters, gcov_type value)
-{
-  __gcov_one_value_profiler_body (counters, value);
-}
-#endif
-
-#ifdef L_gcov_indirect_call_profiler
-/* This function exist only for workaround of binutils bug 14342.
-   Once this compatibility hack is obsolette, it can be removed.  */
-
-/* By default, the C++ compiler will use function addresses in the
-   vtable entries.  Setting TARGET_VTABLE_USES_DESCRIPTORS to nonzero
-   tells the compiler to use function descriptors instead.  The value
-   of this macro says how many words wide the descriptor is (normally 2),
-   but it may be dependent on target flags.  Since we do not have access
-   to the target flags here we just check to see if it is set and use
-   that to set VTABLE_USES_DESCRIPTORS to 0 or 1.
-
-   It is assumed that the address of a function descriptor may be treated
-   as a pointer to a function.  */
-
-#ifdef TARGET_VTABLE_USES_DESCRIPTORS
-#define VTABLE_USES_DESCRIPTORS 1
-#else
-#define VTABLE_USES_DESCRIPTORS 0
-#endif
-
-/* Tries to determine the most common value among its inputs. */
-void
-__gcov_indirect_call_profiler (gcov_type* counter, gcov_type value,
-                               void* cur_func, void* callee_func)
-{
-  /* If the C++ virtual tables contain function descriptors then one
-     function may have multiple descriptors and we need to dereference
-     the descriptors to see if they point to the same function.  */
-  if (cur_func == callee_func
-      || (VTABLE_USES_DESCRIPTORS && callee_func
-          && *(void **) cur_func == *(void **) callee_func))
-    __gcov_one_value_profiler_body (counter, value);
-}
-
-#endif
-#ifdef L_gcov_indirect_call_profiler_v2
-
-/* These two variables are used to actually track caller and callee.  Keep
-   them in TLS memory so races are not common (they are written to often).
-   The variables are set directly by GCC instrumented code, so declaration
-   here must match one in tree-profile.c  */
-
-#if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS)
-__thread 
-#endif
-void * __gcov_indirect_call_callee;
-#if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS) 
-__thread 
-#endif
-gcov_type * __gcov_indirect_call_counters;
-
-/* By default, the C++ compiler will use function addresses in the
-   vtable entries.  Setting TARGET_VTABLE_USES_DESCRIPTORS to nonzero
-   tells the compiler to use function descriptors instead.  The value
-   of this macro says how many words wide the descriptor is (normally 2),
-   but it may be dependent on target flags.  Since we do not have access
-   to the target flags here we just check to see if it is set and use
-   that to set VTABLE_USES_DESCRIPTORS to 0 or 1.
-
-   It is assumed that the address of a function descriptor may be treated
-   as a pointer to a function.  */
-
-#ifdef TARGET_VTABLE_USES_DESCRIPTORS
-#define VTABLE_USES_DESCRIPTORS 1
-#else
-#define VTABLE_USES_DESCRIPTORS 0
-#endif
-
-/* Tries to determine the most common value among its inputs. */
-void
-__gcov_indirect_call_profiler_v2 (gcov_type value, void* cur_func)
-{
-  /* If the C++ virtual tables contain function descriptors then one
-     function may have multiple descriptors and we need to dereference
-     the descriptors to see if they point to the same function.  */
-  if (cur_func == __gcov_indirect_call_callee
-      || (VTABLE_USES_DESCRIPTORS && __gcov_indirect_call_callee
-	  && *(void **) cur_func == *(void **) __gcov_indirect_call_callee))
-    __gcov_one_value_profiler_body (__gcov_indirect_call_counters, value);
-}
-#endif
-
-#ifdef L_gcov_average_profiler
-/* Increase corresponding COUNTER by VALUE.  FIXME: Perhaps we want
-   to saturate up.  */
-
-void
-__gcov_average_profiler (gcov_type *counters, gcov_type value)
-{
-  counters[0] += value;
-  counters[1] ++;
-}
-#endif
-
-#ifdef L_gcov_ior_profiler
-/* Bitwise-OR VALUE into COUNTER.  */
-
-void
-__gcov_ior_profiler (gcov_type *counters, gcov_type value)
-{
-  *counters |= value;
-}
-#endif
-
-#ifdef L_gcov_fork
-/* A wrapper for the fork function.  Flushes the accumulated profiling data, so
-   that they are not counted twice.  */
-
-pid_t
-__gcov_fork (void)
-{
-  pid_t pid;
-  extern __gthread_mutex_t __gcov_flush_mx;
-  __gcov_flush ();
-  pid = fork ();
-  if (pid == 0)
-    __GTHREAD_MUTEX_INIT_FUNCTION (&__gcov_flush_mx);
-  return pid;
-}
-#endif
-
-#ifdef L_gcov_execl
-/* A wrapper for the execl function.  Flushes the accumulated profiling data, so
-   that they are not lost.  */
-
-int
-__gcov_execl (const char *path, char *arg, ...)
-{
-  va_list ap, aq;
-  unsigned i, length;
-  char **args;
-
-  __gcov_flush ();
-
-  va_start (ap, arg);
-  va_copy (aq, ap);
-
-  length = 2;
-  while (va_arg (ap, char *))
-    length++;
-  va_end (ap);
-
-  args = (char **) alloca (length * sizeof (void *));
-  args[0] = arg;
-  for (i = 1; i < length; i++)
-    args[i] = va_arg (aq, char *);
-  va_end (aq);
-
-  return execv (path, args);
-}
-#endif
-
-#ifdef L_gcov_execlp
-/* A wrapper for the execlp function.  Flushes the accumulated profiling data, so
-   that they are not lost.  */
-
-int
-__gcov_execlp (const char *path, char *arg, ...)
-{
-  va_list ap, aq;
-  unsigned i, length;
-  char **args;
-
-  __gcov_flush ();
-
-  va_start (ap, arg);
-  va_copy (aq, ap);
-
-  length = 2;
-  while (va_arg (ap, char *))
-    length++;
-  va_end (ap);
-
-  args = (char **) alloca (length * sizeof (void *));
-  args[0] = arg;
-  for (i = 1; i < length; i++)
-    args[i] = va_arg (aq, char *);
-  va_end (aq);
-
-  return execvp (path, args);
-}
-#endif
-
-#ifdef L_gcov_execle
-/* A wrapper for the execle function.  Flushes the accumulated profiling data, so
-   that they are not lost.  */
-
-int
-__gcov_execle (const char *path, char *arg, ...)
-{
-  va_list ap, aq;
-  unsigned i, length;
-  char **args;
-  char **envp;
-
-  __gcov_flush ();
-
-  va_start (ap, arg);
-  va_copy (aq, ap);
-
-  length = 2;
-  while (va_arg (ap, char *))
-    length++;
-  va_end (ap);
-
-  args = (char **) alloca (length * sizeof (void *));
-  args[0] = arg;
-  for (i = 1; i < length; i++)
-    args[i] = va_arg (aq, char *);
-  envp = va_arg (aq, char **);
-  va_end (aq);
-
-  return execve (path, args, envp);
-}
-#endif
-
-#ifdef L_gcov_execv
-/* A wrapper for the execv function.  Flushes the accumulated profiling data, so
-   that they are not lost.  */
-
-int
-__gcov_execv (const char *path, char *const argv[])
-{
-  __gcov_flush ();
-  return execv (path, argv);
-}
-#endif
-
-#ifdef L_gcov_execvp
-/* A wrapper for the execvp function.  Flushes the accumulated profiling data, so
-   that they are not lost.  */
-
-int
-__gcov_execvp (const char *path, char *const argv[])
-{
-  __gcov_flush ();
-  return execvp (path, argv);
-}
-#endif
-
-#ifdef L_gcov_execve
-/* A wrapper for the execve function.  Flushes the accumulated profiling data, so
-   that they are not lost.  */
-
-int
-__gcov_execve (const char *path, char *const argv[], char *const envp[])
-{
-  __gcov_flush ();
-  return execve (path, argv, envp);
-}
-#endif
-#endif /* inhibit_libc */