Patchwork [google,gcc-4_8] port gcov-tool to gcc-4_8

login
register
mail settings
Submitter Rong Xu
Date Jan. 17, 2014, 7:15 p.m.
Message ID <CAF1bQ=QvYE1SC+8AAdSadk-JJ==en93w=m+7qqHMuWELBKkkMQ@mail.gmail.com>
Download mbox | patch
Permalink /patch/312175/
State New
Headers show

Comments

Rong Xu - Jan. 17, 2014, 7:15 p.m.
Hi,

This patch port the gcov-tool work to google/gcc-4_8 branches.

Tested with spec2006, profiledbootstrap and google internal benchmarks.

-Rong
2014-01-17  Rong Xu  <xur@google.com>

  Backport the gcov-tool code from trunk (trunk version is
  pending review). Add LIPO specific code.
Xinliang David Li - Jan. 17, 2014, 8:05 p.m.
For LIPO parameters, you can do this
1) isolate all LIPO specific parameters into lipo_params.def, and
include it from params.def.
2) include lipo_params.def (after proper definition of DEFPARAM) in
the profile tool source dir.

By so doing, the compiler setting and profile tool will be in sync.
(Unfortuately, for historic reason, the lipo_cutoff default val is
still set in dyn-ipa.c even with the parameter, so some comments needs
to be added in dyn-ipa.c to make sure the value should be doubly
updated).

David

On Fri, Jan 17, 2014 at 11:15 AM, Rong Xu <xur@google.com> wrote:
> Hi,
>
> This patch port the gcov-tool work to google/gcc-4_8 branches.
>
> Tested with spec2006, profiledbootstrap and google internal benchmarks.
>
> -Rong

Patch

Index: gcc/gcov-tool.c
===================================================================
--- gcc/gcov-tool.c	(revision 0)
+++ gcc/gcov-tool.c	(revision 0)
@@ -0,0 +1,580 @@ 
+/* Gcc offline profile processing tool support. */
+/* Compile this one with gcc.  */
+/* Copyright (C) 2014 Free Software Foundation, Inc.
+   Contributed by Rong Xu <xur@google.com>.
+
+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 "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>
+#include <getopt.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 int gcov_profile_scale2 (struct gcov_info*, int, int);
+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_set_verbose (void);
+
+static int verbose;
+
+gcov_unsigned_t __gcov_lipo_grouping_algorithm = 1;
+gcov_unsigned_t __gcov_lipo_merge_modu_edges;
+gcov_unsigned_t __gcov_lipo_weak_inclusion;
+gcov_unsigned_t __gcov_lipo_max_mem = 3000000;
+gcov_unsigned_t __gcov_lipo_random_group_size;
+gcov_unsigned_t __gcov_lipo_cutoff = 80;
+gcov_unsigned_t __gcov_lipo_random_seed;
+gcov_unsigned_t __gcov_lipo_dump_cgraph = 0;  
+gcov_unsigned_t __gcov_lipo_propagate_scale;
+
+/* Remove file NAME if it has a gcda suffix. */
+
+static int
+unlink_gcda_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 (GCOV_DATA_SUFFIX);
+
+  if (len > len1 && !strncmp (len -len1 + name, GCOV_DATA_SUFFIX, len1))
+    remove (name);
+
+  if (ret)
+    {
+      fnotice (stderr, "error in removing %s\n", name);
+      exit (FATAL_EXIT_CODE);
+    }
+
+  return ret;
+}
+
+/* Remove the gcda files in PATH recursively.  */
+
+static int
+unlink_profile_dir (const char *path)
+{
+    return nftw(path, unlink_gcda_file, 64, FTW_DEPTH | FTW_PHYS);
+}
+
+/* Merging profile D1 and D2 with weight as W1 and W2, respectively.
+   The result profile is written to directory OUT.
+   Return 0 on success.  */
+
+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 new profile.  */
+  unlink_profile_dir (out);
+  mkdir (out, 0755);
+  pwd = getcwd (NULL, 0);
+  gcc_assert (pwd);
+  ret = chdir (out);
+  gcc_assert (ret == 0);
+
+  set_gcov_list (d1_profile);
+  gcov_exit ();
+
+  ret = chdir (pwd);
+  free (pwd);
+  return 0;
+}
+
+/* Usage message for profile merge.  */
+
+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, --output <dir>                  Output directory\n");
+  fnotice (file, "    -w, --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 }
+};
+
+/* Print merge usage and exit.  */
+
+static void
+merge_usage (void)
+{
+  fnotice (stderr, "Merge subcomand usage:");
+  print_merge_usage_message (true);
+  exit (FATAL_EXIT_CODE);
+}
+
+/* Driver for profile merge sub-command.  */
+
+static int
+do_merge (int argc, char **argv)
+{
+  int opt;
+  int ret;
+  const char *output_dir = 0;
+  int w1 = 1, w2 = 1;
+
+  optind = 0;
+  while ((opt = getopt_long (argc, argv, "vo:w:", merge_options, NULL)) != -1)
+    {
+      switch (opt)
+        {
+        case 'v':
+          verbose = 1;
+          gcov_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;
+}
+
+/* Scale the counters in profile DIR by a factor of N/D.
+   Result is written to dir OUT. Return 0 on success.  */
+
+static int
+profile_rewrite2 (const char *dir, const char *out, int n, int d)
+{
+  char *pwd;
+  int ret;
+  struct gcov_info * profile;
+
+
+  profile = gcov_read_profile_dir (dir, 0);
+  if (!profile)
+    return 1;
+
+  /* Output new profile.  */
+  unlink_profile_dir (out);
+  mkdir (out, 0755);
+  pwd = getcwd (NULL, 0);
+  gcc_assert (pwd);
+  ret = chdir (out);
+  gcc_assert (ret == 0);
+
+  gcov_profile_scale2 (profile, n, d);
+
+  set_gcov_list (profile);
+  gcov_exit ();
+
+  ret = chdir (pwd);
+  free (pwd);
+  return 0;
+}
+
+/* If N_VAL is no-zero, normalize the profile by setting the largest counter
+   counter value to N_VAL and scale others counters proportionally.
+   Otherwise, multiply the all counters by SCALE.  */
+
+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 new profile.  */
+  unlink_profile_dir (out);
+  mkdir (out, 0755);
+  pwd = getcwd (NULL, 0);
+  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;
+}
+
+/* Usage function for profile rewrite.  */
+
+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, --output <dir>                  Output directory\n");
+  fnotice (file, "    -s, --scale <float or simple-frac>  Scale the profile counters\n");
+  fnotice (file, "    -n, --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 }
+};
+
+/* Print profile rewrite usage and exit.  */
+
+static void
+rewrite_usage (void)
+{
+  fnotice (stderr, "Rewrite subcommand usage:");
+  print_rewrite_usage_message (true);
+  exit (FATAL_EXIT_CODE);
+}
+
+/* Driver for profile rewrite sub-command. */
+
+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;
+  int numerator = -1;
+  int denominator = -1;
+
+  optind = 0;
+  while ((opt = getopt_long (argc, argv, "vo:s:n:", rewrite_options, NULL)) != -1)
+    {
+      switch (opt)
+        {
+        case 'v':
+          verbose = 1;
+          gcov_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':
+          ret = 0;
+          if (strstr (optarg, "/"))
+            {
+              ret = sscanf (optarg, "%d/%d", &numerator, &denominator);
+              if (ret == 2)
+                {
+                  gcc_assert (numerator >= 0);
+                  gcc_assert (denominator > 0);
+                  scale = 0.0;
+                }
+            }
+          if (ret != 2)
+            {
+              ret = sscanf (optarg, "%f", &scale);
+              gcc_assert (ret == 1);
+            }
+
+          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)
+    {
+      if (denominator > 0)
+        ret = profile_rewrite2 (argv[optind],  output_dir, numerator, denominator);
+      else
+        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");
+  fnotice (file, "  -A, --lipo_algorithm <0|1>            Choose LIPO module grouping algorithm\n"); 
+  fnotice (file, "  -E, --lipo_merge_edge                 Merge module edges in LIPO module grouping\n");
+  fnotice (file, "  -W, --lipo_weak_inclusion             Don't force strict inclusion in grouping\n");
+  fnotice (file, "  -C, --lipo_cutoff <0..100>            Set LIPO module grouping cutoff\n");
+  fnotice (file, "  -M, --lipo_max_memory <int>           Set the max memory in LIPO module grouping\n");
+  fnotice (file, "  -R, --lipo_random_group_size <int>    Set LIPO random grouping size\n");
+  fnotice (file, "  -S, --lipo_random_group_seed <int>    Set LIPO random grouping seed\n");
+  fnotice (file, "  -D, --lipo_dump_cgraph                Dump dynamic call graph\n");
+  fnotice (file, "  -P, --lipo_propagate_scale            Set LIPO propagate scale to true\n");
+  fnotice (file, "\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 2014 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' },
+  { "lipo_algorithm",         required_argument, NULL, 'A' },
+  { "lipo_merge_edge",        no_argument,       NULL, 'E' },
+  { "lipo_weak_inclusion",    no_argument,       NULL, 'W' },
+  { "lipo_cutoff",            required_argument, NULL, 'C' },
+  { "lipo_max_memory",        required_argument, NULL, 'M' },
+  { "lipo_random_group_size", required_argument, NULL, 'R' },
+  { "lipo_random_group_seed", required_argument, NULL, 'S' },
+  { "lipo_dump_cgraph",       no_argument,       NULL, 'D' },
+  { "lipo_propagate_scale",   no_argument,       NULL, 'P' },
+  { 0, 0, 0, 0 }
+};
+
+/* Process args, return index to first non-arg.  */
+
+static int
+process_args (int argc, char **argv)
+{
+  int opt;
+  int ret;
+
+  while ((opt = getopt_long (argc, argv, "+hvA:EWC:M:R:S:DP", options, NULL)) != -1)
+    {
+      switch (opt)
+        {
+        case 'h':
+          print_usage (false);
+          /* Print_usage will exit.  */
+        case 'v':
+          print_version ();
+          /* Print_version will exit.  */
+        case 'E':
+          __gcov_lipo_merge_modu_edges = 1;
+          break;
+        case 'W':
+          __gcov_lipo_weak_inclusion = 1;
+          break;
+        case 'D':
+          __gcov_lipo_dump_cgraph = 1; 
+          break;
+        case 'P':
+          __gcov_lipo_propagate_scale = 1;
+          break;
+        case 'A':
+          sscanf (optarg, "%d", &ret);
+          if (ret != 0 && ret != 1)
+            {
+              fprintf (stderr, "LIPO grouping algorithm can only be 0 or 1. \n");
+              exit (-1);
+            }
+          __gcov_lipo_grouping_algorithm = ret;
+          break;
+        case 'R':
+          sscanf (optarg, "%d", &ret);
+          if (ret < 1)
+            {
+              fprintf (stderr, "LIPO random group size needs to be positive.\n");
+              exit (-1);
+            }
+          __gcov_lipo_random_group_size = ret;
+          break;
+        case 'S':
+          sscanf (optarg, "%d", &ret);
+          __gcov_lipo_random_seed = ret;;
+          break;
+        case 'M':
+          sscanf (optarg, "%d", &ret);
+          if (ret < 0)
+            {
+              fprintf (stderr, "LIPO max-memory size needs to be positive. \n");
+              exit (-1);
+            }
+          __gcov_lipo_max_mem = ret;
+          break;
+        case 'C':
+          sscanf (optarg, "%d", &ret);
+          if (ret < 0 || ret > 100)
+            {
+              fprintf (stderr, "LIPO cutoff value range is [0, 100]. \n");
+              exit (-1);
+            }
+          __gcov_lipo_cutoff = ret;;
+          break;
+        default:
+          print_usage (true);
+          /* Print_usage will exit.  */
+        }
+    }
+
+  return optind;
+}
+
+/* Main function for gcov-tool.  */
+
+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/gcov-io.c
===================================================================
--- gcc/gcov-io.c	(revision 206671)
+++ gcc/gcov-io.c	(working copy)
@@ -555,7 +555,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_GCOV_TOOL
 GCOV_LINKAGE const char *
 gcov_read_string (void)
 {
@@ -632,7 +632,7 @@  gcov_read_summary (struct gcov_summary *summary)
     }
 }
 
-#if !IN_LIBGCOV && IN_GCOV != 1
+#if IN_GCOV_TOOL || !IN_LIBGCOV && IN_GCOV != 1
 /* Read LEN words (unsigned type) and construct MOD_INFO.  */
 
 GCOV_LINKAGE void
@@ -685,7 +685,7 @@  gcov_read_module_info (struct gcov_module_info *mo
 }
 #endif
 
-#if !IN_LIBGCOV
+#if !IN_LIBGCOV || IN_GCOV_TOOL
 /* Reset to a known position.  BASE should have been obtained from
    gcov_position, LENGTH should be a record length.  */
 
Index: gcc/Makefile.in
===================================================================
--- gcc/Makefile.in	(revision 206671)
+++ 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) gcov-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.
@@ -194,6 +195,7 @@  GCC_WARN_CXXFLAGS = $(LOOSE_WARN) $($(@D)-warn) $(
 build/gengtype-lex.o-warn = -Wno-error
 gengtype-lex.o-warn = -Wno-error
 expmed.o-warn = -Wno-error
+libgcov-util.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
@@ -755,6 +757,7 @@  GCC_TARGET_INSTALL_NAME := $(target_noncanonical)-
 CPP_INSTALL_NAME := $(shell echo cpp|sed '$(program_transform_name)')
 GCOV_INSTALL_NAME := $(shell echo gcov|sed '$(program_transform_name)')
 PROFILE_TOOL_INSTALL_NAME := $(shell echo profile_tool|sed '$(program_transform_name)')
+GCOV_TOOL_INSTALL_NAME := $(shell echo gcov-tool|sed '$(program_transform_name)')
 
 # Setup the testing framework, if you have one
 EXPECT = `if [ -f $${rootme}/../expect/expect ] ; then \
@@ -1480,7 +1483,8 @@  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)
+  $(COLLECT2_OBJS) $(EXTRA_GCC_OBJS) $(GCOV_OBJS) $(GCOV_DUMP_OBJS) \
+  $(GCOV_TOOL_OBJS)
 
 # This lists all host object files, whether they are included in this
 # compilation or not.
@@ -1505,6 +1509,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) \
+ gcov-tool$(exeect) \
  gengtype$(exeext) *.[0-9][0-9].* *.[si] *-checksum.c libbackend.a \
  libcommon-target.a libcommon.a libgcc.mk
 
@@ -4070,6 +4075,24 @@  GCOV_DUMP_OBJS = gcov-dump.o vec.o ggc-none.o
 gcov-dump$(exeext): $(GCOV_DUMP_OBJS) $(LIBDEPS)
 	+$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_DUMP_OBJS) \
 		$(LIBS) -o $@
+
+libgcov-util.o: $(srcdir)/../libgcc/libgcov-util.c gcov-io.c $(GCOV_IO_H) \
+  $(srcdir)/../libgcc/libgcov-driver.c $(srcdir)/../libgcc/libgcov-driver-system.c \
+  $(srcdir)/../libgcc/libgcov-merge.c $(srcdir)/../libgcc/libgcov.h \
+  $(SYSTEM_H) coretypes.h $(TM_H) $(CONFIG_H) version.h intl.h $(DIAGNOSTIC_H)
+	+$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -o $@ $<
+dyn-ipa.o: $(srcdir)/../libgcc/dyn-ipa.c gcov-io.c $(srcdir)/../libgcc/libgcov.h \
+   $(GCOV_IO_H) $(SYSTEM_H) coretypes.h \
+   $(TM_H) $(CONFIG_H) version.h intl.h $(DIAGNOSTIC_H)
+	+$(COMPILER) -DIN_GCOV_TOOL -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) \
+	  $(INCLUDES) -o $@ $<
+
+GCOV_TOOL_OBJS = gcov-tool.o libgcov-util.o dyn-ipa.o
+gcov-tool.o: gcov-tool.c $(GCOV_IO_H) intl.h $(SYSTEM_H) coretypes.h $(TM_H) \
+   $(CONFIG_H) version.h $(DIAGNOSTIC_H)
+gcov-tool$(exeext): $(GCOV_TOOL_OBJS) $(LIBDEPS)
+	+$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_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
@@ -4697,6 +4720,13 @@  install-common: native lang.install-common install
 	    $(INSTALL_PROGRAM) $(srcdir)/../contrib/profile_tool \
 	    $(DESTDIR)$(bindir)/$(PROFILE_TOOL_INSTALL_NAME)$(exeext); \
 	fi
+# Install gcov-tool if it was compiled.
+	-if [ -f gcov-tool$(exeext) ]; \
+	then \
+	    rm -f $(DESTDIR)$(bindir)/$(GCOV_TOOL_INSTALL_NAME)$(exeext); \
+	    $(INSTALL_PROGRAM) \
+	    gcov-tool$(exeext) $(DESTDIR)$(bindir)/$(GCOV_TOOL_INSTALL_NAME)$(exeext); \
+	fi
 
 # Install the driver program as $(target_noncanonical)-gcc,
 # $(target_noncanonical)-gcc-$(version)
Index: gcc/coretypes.h
===================================================================
--- gcc/coretypes.h	(revision 206671)
+++ gcc/coretypes.h	(working copy)
@@ -43,6 +43,9 @@  see the files COPYING3 and COPYING.RUNTIME respect
 
 #ifndef USED_FOR_TARGET
 
+typedef HOST_WIDEST_INT gcov_type;
+typedef unsigned HOST_WIDEST_INT gcov_type_unsigned;
+
 struct bitmap_head_def;
 typedef struct bitmap_head_def *bitmap;
 typedef const struct bitmap_head_def *const_bitmap;
Index: libgcc/libgcov.h
===================================================================
--- libgcc/libgcov.h	(revision 206671)
+++ libgcc/libgcov.h	(working copy)
@@ -34,7 +34,13 @@  see the files COPYING3 and COPYING.RUNTIME respect
 #ifndef xcalloc
 #define xcalloc calloc
 #endif
+#ifndef xrealloc
+#define xrealloc realloc
+#endif
 
+#ifndef IN_GCOV_TOOL
+/* About the target.  */
+
 #include "tconfig.h"
 #include "tsystem.h"
 #include "coretypes.h"
@@ -47,6 +53,25 @@  see the files COPYING3 and COPYING.RUNTIME respect
 #define THREAD_PREFIX
 #endif
 
+#else /* IN_GCOV_TOOL */
+/* About the host.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+
+typedef unsigned gcov_unsigned_t;
+typedef unsigned gcov_position_t;
+/* gcov_type is typedef'd elsewhere for the compiler */
+#if defined (HOST_HAS_F_SETLKW)
+#define GCOV_LOCKED 1
+#else
+#define GCOV_LOCKED 0
+#endif
+
+#endif /* !IN_GCOV_TOOL */
+
 #if defined(inhibit_libc)
 #define IN_LIBGCOV (-1)
 #else
@@ -203,8 +228,13 @@  struct gcov_info
 					  unused) */
 
   unsigned n_functions;		/* number of functions */
+
+#ifndef IN_GCOV_TOOL
   const struct gcov_fn_info *const *functions; /* pointer to pointers
-					          to function information  */
+                                                  to function information  */
+#else
+  const struct gcov_fn_info **functions;
+#endif /* !IN_GCOV_TOOL */
 };
 
 /* Information about a single imported module.  */
Index: libgcc/libgcov-driver.c
===================================================================
--- libgcc/libgcov-driver.c	(revision 206671)
+++ libgcc/libgcov-driver.c	(working copy)
@@ -53,6 +53,7 @@  extern int get_gcov_dump_complete (void) ATTRIBUTE
 extern void set_gcov_list (struct gcov_info *) ATTRIBUTE_HIDDEN;
 extern struct gcov_info *get_gcov_list (void) ATTRIBUTE_HIDDEN;
 
+#ifndef IN_GCOV_TOOL
 /* Create a strong reference to these symbols so that they are
    unconditionally pulled into the instrumented binary, even when
    the only reference is a weak reference. This is necessary because
@@ -76,6 +77,7 @@  __coverage_callback (gcov_type funcdef_no __attrib
 {
    /* nothing */
 }
+#endif
 
 /* Flag when the profile has already been dumped via __gcov_dump().  */
 static int gcov_dump_complete;
@@ -135,8 +137,11 @@  get_gcov_list (void)
 /* Size of the longest file name. */
 static size_t gcov_max_filename = 0;
 
+#ifndef IN_GCOV_TOOL
+/* Emitted in coverage.c.  */
 extern gcov_unsigned_t __gcov_sampling_period;
 static int gcov_sampling_period_initialized = 0;
+#endif
 
 /* Unique identifier assigned to each module (object file).  */
 static gcov_unsigned_t gcov_cur_module_id = 0;
@@ -384,7 +389,7 @@  gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
              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));
+              xmalloc (sizeof(struct gcov_summary_buffer));
           (*sum_tail)->summary = tmp;
           (*sum_tail)->next = 0;
           sum_tail = &((*sum_tail)->next);
@@ -852,7 +857,8 @@  gcov_exit (void)
 
       /* The IS_PRIMARY field is overloaded to indicate if this module
          is FDO/LIPO.  */
-      dump_module_info |= gi_ptr->mod_info->is_primary;
+      if (gi_ptr->mod_info)
+        dump_module_info |= gi_ptr->mod_info->is_primary;
     }
 
   if (dump_module_info)
@@ -898,6 +904,7 @@  gcov_clear (void)
 void
 __gcov_init (struct gcov_info *info)
 {
+#ifndef IN_GCOV_TOOL
   if (!gcov_sampling_period_initialized)
     {
       const char* env_value_str = getenv ("GCOV_SAMPLING_PERIOD");
@@ -909,6 +916,7 @@  __gcov_init (struct gcov_info *info)
         }
       gcov_sampling_period_initialized = 1;
     }
+#endif /* IN_GCOV_TOOL */
 
   if (!info->version || !info->n_functions)
     return;
Index: libgcc/libgcov-merge.c
===================================================================
--- libgcc/libgcov-merge.c	(revision 206671)
+++ libgcc/libgcov-merge.c	(working copy)
@@ -45,6 +45,26 @@  void __gcov_merge_delta (gcov_type *counters  __at
 
 #else
 
+static inline gcov_type
+gcov_get_counter (void)
+{
+#ifndef IN_GCOV_TOOL
+  return gcov_read_counter ();
+#else
+  return gcov_read_counter_mem () * gcov_get_merge_weight ();
+#endif
+}
+
+static inline gcov_type
+gcov_get_counter_target (void)
+{
+#ifndef IN_GCOV_TOOL
+  return gcov_read_counter ();
+#else
+  return gcov_read_counter_mem ();
+#endif
+}
+
 #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
@@ -53,7 +73,7 @@  void
 __gcov_merge_add (gcov_type *counters, unsigned n_counters)
 {
   for (; n_counters; counters++, n_counters--)
-    *counters += gcov_read_counter ();
+    *counters += gcov_get_counter ();
 }
 #endif /* L_gcov_merge_add */
 
@@ -65,7 +85,7 @@  void
 __gcov_merge_ior (gcov_type *counters, unsigned n_counters)
 {
   for (; n_counters; counters++, n_counters--)
-    *counters |= gcov_read_counter ();
+    *counters |= gcov_get_counter ();
 }
 #endif
 
@@ -94,8 +114,8 @@  __gcov_merge_dc (gcov_type *counters, unsigned n_c
   gcc_assert (!(n_counters % 2));
   for (i = 0; i < n_counters; i += 2)
     {
-      gcov_type global_id = gcov_read_counter ();
-      gcov_type call_count = gcov_read_counter ();
+      gcov_type global_id = gcov_get_counter_target ();
+      gcov_type call_count = gcov_get_counter ();
 
       /* Note that global id counter may never have been set if no calls were
 	 made from this call-site.  */
@@ -156,12 +176,12 @@  __gcov_merge_icall_topn (gcov_type *counters, unsi
         }
 
       /* Skip the number_of_eviction entry.  */
-      gcov_read_counter ();
+      gcov_get_counter ();
       for (k = 0; k < GCOV_ICALL_TOPN_NCOUNTS - 1; k += 2)
         {
           int found = 0;
-          gcov_type global_id = gcov_read_counter ();
-          gcov_type call_count = gcov_read_counter ();
+          gcov_type global_id = gcov_get_counter_target ();
+          gcov_type call_count = gcov_get_counter ();
           for (m = 0; m < j; m += 2)
             {
               if (tmp_array[m] == global_id)
@@ -212,9 +232,9 @@  __gcov_merge_single (gcov_type *counters, unsigned
   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 ();
+      value = gcov_get_counter_target ();
+      counter = gcov_get_counter ();
+      all = gcov_get_counter ();
 
       if (counters[0] == value)
 	counters[1] += counter;
@@ -251,10 +271,10 @@  __gcov_merge_delta (gcov_type *counters, unsigned
   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 ();
+      /* last = */ gcov_get_counter ();
+      value = gcov_get_counter_target  ();
+      counter = gcov_get_counter ();
+      all = gcov_get_counter ();
 
       if (counters[1] == value)
 	counters[2] += counter;
Index: libgcc/dyn-ipa.c
===================================================================
--- libgcc/dyn-ipa.c	(revision 206671)
+++ libgcc/dyn-ipa.c	(working copy)
@@ -28,11 +28,13 @@  see the files COPYING3 and COPYING.RUNTIME respect
 
 struct dyn_pointer_set;
 
+#ifndef IN_GCOV_TOOL
 #define XNEWVEC(type,ne) (type *)malloc(sizeof(type) * (ne))
 #define XCNEWVEC(type,ne) (type *)calloc(1, sizeof(type) * (ne))
 #define XNEW(type) (type *)malloc(sizeof(type))
 #define XDELETEVEC(p) free(p)
 #define XDELETE(p) free(p)
+#endif
 
 struct dyn_cgraph_node
 {
@@ -262,7 +264,7 @@  get_cgraph_node (gcov_type func_guid)
   if (func_id > the_dyn_call_graph.sup_modules[mod_idx].max_func_ident)
     return 0;
 
-  return *(pointer_set_find_or_insert
+  return (struct dyn_cgraph_node*) *(pointer_set_find_or_insert
 	   (the_dyn_call_graph.call_graph_nodes[mod_idx], func_id));
 }
 
@@ -486,7 +488,7 @@  __gcov_finalize_dyn_callgraph (void)
           struct dyn_cgraph_node *node;
           struct dyn_cgraph_edge *callees, *next_callee;
           fi_ptr = gi_ptr->functions[f_ix];
-          node = *(pointer_set_find_or_insert
+          node = (struct dyn_cgraph_node *) *(pointer_set_find_or_insert
                    (the_dyn_call_graph.call_graph_nodes[i], fi_ptr->ident));
           gcc_assert (node);
           callees = node->callees;
@@ -658,7 +660,7 @@  gcov_build_callgraph (void)
           fi_ptr = gi_ptr->functions[f_ix];
           ci_ptr = fi_ptr->ctrs;
 
-          caller = *(pointer_set_find_or_insert
+          caller = (struct dyn_cgraph_node *) *(pointer_set_find_or_insert
                     (the_dyn_call_graph.call_graph_nodes[m_ix],
                      fi_ptr->ident));
           gcc_assert (caller);
@@ -907,7 +909,7 @@  gcov_compute_cutoff_count (void)
 
 	  fi_ptr = gi_ptr->functions[f_ix];
 
-	  node = *(pointer_set_find_or_insert
+	  node = (struct dyn_cgraph_node *) *(pointer_set_find_or_insert
 		   (the_dyn_call_graph.call_graph_nodes[m_ix], fi_ptr->ident));
 	  gcc_assert (node);
 
@@ -920,7 +922,7 @@  gcov_compute_cutoff_count (void)
               else
                 {
                   capacity = capacity + (capacity >> 1);
-                  edges = (struct dyn_cgraph_edge **)realloc (edges, sizeof (void*) * capacity);
+                  edges = (struct dyn_cgraph_edge **)xrealloc (edges, sizeof (void*) * capacity);
                   edges[num_edges - 1] = callees;
                 }
               callees = callees->next_callee;
@@ -1267,7 +1269,7 @@  static fibnode_t fibnode_remove (fibnode_t);
 static dyn_fibheap_t
 dyn_fibheap_new (void)
 {
-  return (dyn_fibheap_t) calloc (1, sizeof (struct dyn_fibheap));
+  return (dyn_fibheap_t) xcalloc (1, sizeof (struct dyn_fibheap));
 }
 
 /* Create a new fibonacci heap node.  */
@@ -1276,7 +1278,7 @@  fibnode_new (void)
 {
   fibnode_t node;
 
-  node = (fibnode_t) calloc (1, sizeof *node);
+  node = (fibnode_t) xcalloc (1, sizeof *node);
   node->left = node;
   node->right = node;
 
@@ -1634,7 +1636,7 @@  build_modu_graph (gcov_type cutoff_count)
 	  struct dyn_cgraph_node *node;
 
 	  fi_ptr = gi_ptr->functions[f_ix];
-	  node = *(pointer_set_find_or_insert
+	  node = (struct dyn_cgraph_node *) *(pointer_set_find_or_insert
 		   (the_dyn_call_graph.call_graph_nodes[m_ix], fi_ptr->ident));
 	  if (!node)
             {
@@ -1954,7 +1956,7 @@  gcov_compute_module_groups_eager_propagation (gcov
 	  struct dyn_cgraph_node *node;
 
 	  fi_ptr = gi_ptr->functions[f_ix];
-	  node = *(pointer_set_find_or_insert
+	  node = (struct dyn_cgraph_node *) *(pointer_set_find_or_insert
 		   (the_dyn_call_graph.call_graph_nodes[m_ix], fi_ptr->ident));
 	  gcc_assert (node);
           if (node->visited)
@@ -1978,7 +1980,7 @@  gcov_compute_module_groups_eager_propagation (gcov
           struct dyn_pointer_set *imp_modules;
 
 	  fi_ptr = gi_ptr->functions[f_ix];
-	  node = *(pointer_set_find_or_insert
+	  node = (struct dyn_cgraph_node *) *(pointer_set_find_or_insert
 		   (the_dyn_call_graph.call_graph_nodes[m_ix], fi_ptr->ident));
 	  gcc_assert (node);
 
@@ -2337,7 +2339,7 @@  gcov_dump_callgraph (gcov_type cutoff_count)
 	  struct dyn_cgraph_node *node;
 
 	  fi_ptr = gi_ptr->functions[f_ix];
-	  node = *(pointer_set_find_or_insert
+	  node = (struct dyn_cgraph_node *) *(pointer_set_find_or_insert
 		   (the_dyn_call_graph.call_graph_nodes[m_ix], fi_ptr->ident));
 	  gcc_assert (node);
 
Index: libgcc/libgcov-util.c
===================================================================
--- libgcc/libgcov-util.c	(revision 0)
+++ libgcc/libgcov-util.c	(revision 0)
@@ -0,0 +1,1049 @@ 
+/* Utility functions for reading gcda files into in-memory
+   gcov_info structures and offline profile processing. */
+/* Copyright (C) 2014 Free Software Foundation, Inc.
+   Contributed by Rong Xu <xur@google.com>.
+
+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/>.  */
+
+
+#define IN_GCOV_TOOL 1
+#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_icall_topn 1
+#define L_gcov_merge_dc 1
+#define L_gcov_merge_ior 1
+#define L_gcov_merge_reusedist 1
+
+#include "libgcov.h"
+#include "intl.h"
+#include "diagnostic.h"
+#include "version.h"
+#include "demangle.h"
+
+extern gcov_type gcov_read_counter_mem();
+extern unsigned gcov_get_merge_weight();
+
+/* 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_set_verbose (void)
+{
+  verbose = 1;
+}
+
+/* The following part is to read Gcda and reconstruct GCOV_INFO.  */
+
+#include "obstack.h"
+#include <unistd.h>
+#include <ftw.h>
+
+static void tag_function (unsigned, unsigned);
+static void tag_blocks (unsigned, unsigned);
+static void tag_arcs (unsigned, unsigned);
+static void tag_lines (unsigned, unsigned);
+static void tag_counters (unsigned, unsigned);
+static void tag_summary (unsigned, unsigned);
+static void tag_module_info (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 points to the module being processed.  */
+static struct gcov_module_info *curr_module_info;
+/* 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,
+    __gcov_merge_icall_topn,
+    __gcov_merge_dc,
+};
+
+/* Set the ctrs field in gcvo_fn_info object 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) (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},
+  {GCOV_TAG_MODULE_INFO, "MODULE INFO", tag_module_info},
+  {0, NULL, NULL}
+};
+
+/* Handler for reading function tag.  */
+
+static void
+tag_function (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_ptr_grow (&fn_info, curr_fn_info);
+    }
+
+  /* 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 (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+  gcc_assert (0);
+}
+
+/* Handler for reading flow arc tag.  */
+
+static void
+tag_arcs (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+  gcc_assert (0);
+}
+
+/* Handler for reading line tag.  */
+
+static void
+tag_lines (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+  gcc_assert (0);
+}
+
+/* Handler for reading counters array tag with value as TAG and length of LENGTH.  */
+
+static void
+tag_counters (unsigned tag, unsigned length)
+{
+  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 (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+  struct gcov_summary summary;
+
+  gcov_read_summary (&summary);
+}
+
+/* This function is called at the end of reading a gcda file.
+   It flushes the contents in curr_fn_info to gcov_info object OBJ_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];
+    }
+
+  obj_info->mod_info = curr_module_info;
+}
+
+extern void gcov_read_module_info (struct gcov_module_info *mod_info,
+    gcov_unsigned_t len);
+
+/* This function reads module_info from a gcda file.  */
+
+static void
+tag_module_info (unsigned tag ATTRIBUTE_UNUSED, unsigned length)
+{
+  struct gcov_module_info* mod_info;
+
+  mod_info = (struct gcov_module_info *)
+      xmalloc ((length + 2) * sizeof (gcov_unsigned_t));
+
+  gcov_read_module_info (mod_info, length);
+
+  if (mod_info->is_primary)
+    curr_module_info = mod_info;
+  else
+    free (mod_info);
+}
+
+/* Read the content of a gcda file FILENAME, 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;
+  curr_module_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)
+        {
+	  unsigned long actual_length;
+
+	  (*format->proc) (tag, length);
+
+	  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;
+}
+
+/* This will be called by ftw(). It opens and read a gcda file FILENAME.
+   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_DATA_SUFFIX);
+
+  if (filename_len <= suffix_len)
+    return 0;
+
+  if (strcmp(filename + filename_len - suffix_len, GCOV_DATA_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;
+}
+
+/* Initializer for reading a profile dir.  */
+
+static inline void
+read_profile_dir_init (void)
+{
+  gcov_info_head = 0;
+}
+
+/* Driver for read a profile directory and convert into gcov_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 = getcwd (NULL, 0);
+  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 records the
+     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;;
+}
+
+/* This part of the code is to merge profile counters.  */
+
+static gcov_type *gcov_value_buf;
+static gcov_unsigned_t gcov_value_buf_size;
+static gcov_unsigned_t gcov_value_buf_pos;
+static unsigned gcov_merge_weight;
+
+/* Read a counter value from gcov_value_buf array.  */
+
+gcov_type
+gcov_read_counter_mem (void)
+{
+  gcov_type ret;
+  gcc_assert (gcov_value_buf_pos < gcov_value_buf_size);
+  ret = *(gcov_value_buf + gcov_value_buf_pos);
+  ++gcov_value_buf_pos;
+  return ret;
+}
+
+/* Return the recorded merge weight.  */
+
+unsigned
+gcov_get_merge_weight (void)
+{
+  return gcov_merge_weight;
+}
+
+/* A wrapper function for merge functions. It sets up the
+   value buffer and weights and then calls the merge function.  */
+
+static void
+merge_wrapper (gcov_merge_fn f, gcov_type *v1, gcov_unsigned_t n,
+               gcov_type *v2, unsigned w)
+{
+  gcov_value_buf = v2;
+  gcov_value_buf_pos = 0;
+  gcov_value_buf_size = n;
+  gcov_merge_weight = w;
+  (*f) (v1, n);
+}
+
+/* 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 multiply by
+     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 by 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);
+          merge_wrapper (merge1, ci_ptr1->values, ci_ptr1->num, ci_ptr2->values, w);
+          ci_ptr1++;
+          ci_ptr2++;
+        }
+    }
+
+  return has_mismatch;
+}
+
+/* Find and return the match gcov_info object for INFO from ARRAY.
+   SIZE is the length of ARRAY.
+   Return NULL if there is no match.  */
+
+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;
+      /* For LIPO, it's easy as we can just match the module_id.  */
+      if (gi_ptr->mod_info && info->mod_info)
+        {   
+          if (gi_ptr->mod_info->ident == info->mod_info->ident)
+            {   
+              ret = gi_ptr;
+              array[i] = 0;
+              break;
+            }   
+        }   
+      else /* For FDO, we have to match the name. This can be expensive.
+              Maybe we should use hash here.  */
+        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;
+}
+
+/* Merge the list of gcov_info list from SRC_PROFILE to TGT_PROFILE.
+   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);
+    }
+
+  /* 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;
+    }
+
+  return 0;
+}
+
+/* This part of code is to scale profile counters.  */
+
+/* Type of function used to normalize counters.  */
+typedef void (*gcov_scale_fn) (gcov_type *, gcov_unsigned_t, double);
+
+/* Scale arc counters. N_COUNTERS of counter value in COUNTERS array are
+   multiplied by a factor F.  */
+
+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. Multiplied the counters in COUNTERS array
+   by a factor of F.  */
+
+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. Multiplied the counters in COUNTERS array
+   by a factor of F.  */
+
+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;
+    }
+}
+
+/* Scale indirect-call profile counters. Multiplied the counters in COUNTERS
+   array by a factor of F.  */
+
+static void
+__gcov_scale_icall_topn (gcov_type *counters, unsigned n_counters, double f)
+{
+  unsigned i;
+
+  gcc_assert (!(n_counters % GCOV_ICALL_TOPN_NCOUNTS));
+  for (i = 0; i < n_counters; i += GCOV_ICALL_TOPN_NCOUNTS)
+    {
+      unsigned j;
+      gcov_type *value_array = &counters[i + 1];
+
+      for (j = 0; j < GCOV_ICALL_TOPN_NCOUNTS - 1; j += 2)
+        value_array[1] *= f;
+    }
+}
+
+/* Scale direct-call profile counters. Multiplied the counters in COUNTERS
+   by a factor of F.  */
+
+static void
+__gcov_scale_dc (gcov_type *counters, unsigned n_counters, double f)
+{
+  unsigned i;
+
+  gcc_assert (!(n_counters % 2));
+  for (i = 0; i < n_counters; i += 2)
+    counters[1] *= 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,
+    __gcov_scale_icall_topn,
+    __gcov_scale_dc,
+};
+
+/* Driver for scaling 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);
+
+  /* Scaling 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;
+
+  /* Find 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);
+}
+
+/* Type of function used to normalize counters.  */
+typedef void (*gcov_scale2_fn) (gcov_type *, gcov_unsigned_t, int, int);
+
+/* Scale2 arc counters.  */
+
+static void
+__gcov_scale2_add (gcov_type *counters, unsigned n_counters, int n, int d)
+{
+  for (; n_counters; counters++, n_counters--)
+    {
+      gcov_type val = *counters;
+      *counters = (val / d) * n;
+    }
+}
+
+/* Scale2 ior counters.  */
+
+static void
+__gcov_scale2_ior (gcov_type *counters ATTRIBUTE_UNUSED,
+                      unsigned n_counters ATTRIBUTE_UNUSED,
+                      int n ATTRIBUTE_UNUSED,
+                      int d ATTRIBUTE_UNUSED)
+{
+  /* do nothing.  */
+}
+
+/* Scale2 delta counters.  */
+
+static void
+__gcov_scale2_delta (gcov_type *counters, unsigned n_counters, int n, int d)
+{
+  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] = (counters[2] / d) * n;
+      counters[3] = (counters[3] / d) * n;
+    }
+}
+
+/* Scale2 single counters.  */
+
+static void
+__gcov_scale2_single (gcov_type *counters, unsigned n_counters, int n, int d)
+{
+  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] = (counters[1] / d) * n;
+      counters[2] = (counters[2] / d) * n;
+    }
+}
+
+/* Scale2 indirect-call profile counters. Multiplied the counters in COUNTERS
+   array by a factor of F.  */
+
+static void
+__gcov_scale2_icall_topn (gcov_type *counters, unsigned n_counters, int n, int d)
+{
+  unsigned i;
+
+  gcc_assert (!(n_counters % GCOV_ICALL_TOPN_NCOUNTS));
+  for (i = 0; i < n_counters; i += GCOV_ICALL_TOPN_NCOUNTS)
+    {
+      unsigned j;
+      gcov_type *value_array = &counters[i+1];
+
+      for (j = 0; j < GCOV_ICALL_TOPN_NCOUNTS - 1; j += 2)
+        value_array[j+1] = (value_array[j+1] / d) * n;
+    }
+}
+
+/* Scale2 direct-call profile counters. Multiplied the counters in COUNTERS
+   by a factor of F.  */
+
+static void
+__gcov_scale2_dc (gcov_type *counters, unsigned n_counters, int n, int d)
+{
+  unsigned i;
+
+  gcc_assert (!(n_counters % 2));
+  for (i = 0; i < n_counters; i += 2)
+    counters[i+1] = (counters[i+1] / d) * n;
+}
+
+/* Scale2 functions for counters.  */
+static gcov_scale2_fn ctr_scale2_functions[GCOV_COUNTERS] = {
+    __gcov_scale2_add,
+    __gcov_scale2_add,
+    __gcov_scale2_add,
+    __gcov_scale2_single,
+    __gcov_scale2_delta,
+    __gcov_scale2_single,
+    __gcov_scale2_add,
+    __gcov_scale2_ior,
+    __gcov_scale2_icall_topn,
+    __gcov_scale2_dc,
+};
+
+/* Driver for scale2 profile counters.  */
+
+int
+gcov_profile_scale2 (struct gcov_info *profile, int n, int d)
+{
+  struct gcov_info *gi_ptr;
+  unsigned f_ix;
+
+  if (verbose)
+    fprintf (stdout, "scale_factor is %d/%d\n", n, d);
+
+  gcc_assert (n >= 0 && d > 0);
+
+  /* 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_scale2_functions[t_ix]) (ci_ptr->values, ci_ptr->num, n, d);
+            ci_ptr++;
+          }
+      }
+
+  return 0;
+}
+