diff mbox

offline gcda profile processing tool

Message ID CAF1bQ=SPdSH8ffMSEBkGQqH-w7b5ye3mC4n2CwqenH8EbGR+PA@mail.gmail.com
State New
Headers show

Commit Message

Rong Xu March 3, 2014, 10:02 p.m. UTC
Attached is the new patch.

Tested with bootstrap, profiledbootstrap and spec.

Thanks,

-Rong

On Thu, Jan 16, 2014 at 9:30 AM, Rong Xu <xur@google.com> wrote:
> Ping.
>
> -Rong
>
> On Mon, Jan 13, 2014 at 12:43 PM, Rong Xu <xur@google.com> wrote:
>> Hi,
>>
>> This patch implements gcov-tool, a offline profile processing tool.
>> This version supports merging two profiles with weights, and scaling
>> the profile with a floating-point / fraction weight.
>>
>> Earlier discussion can be found
>> http://gcc.gnu.org/ml/gcc-patches/2013-11/msg02631.html
>>
>> In the discussion of the earlier patch, Honza wanted a separated
>> patch for interface change for gcov_merge_*() functions. In this patch,
>> the changes for gcov_merge_*() are minimal by using a wrapper function.
>> So I include everything in this patch.
>>
>> Tested with bootstrap profiledboostrap and SPEC2006 profiles.
>>
>> Thanks,
>>
>> -Rong
2014-03-03  Rong Xu  <xur@google.com>

	* gcc/gcov-io.c (gcov_read_string): Make this routine available
        to gcov-tool.
	(gcov_sync): Ditto.
	* gcc/Makefile.in: Build and install gcov-tool. 
	* gcc/gcov-tool.c (unlink_gcda_file): Remove one gcda file.
	(unlink_profile_dir): Remove gcda files from the profile path.
	(profile_merge): Merge two profiles in directory.
	(print_merge_usage_message): Print merge usage.
	(merge_usage): Print merge usage and exit.
	(do_merge): Driver for profile merge sub-command.
	(profile_rewrite): Rewrite profile.
	(print_rewrite_usage_message): Print rewrite usage.
	(rewrite_usage): Print rewrite usage and exit.
	(do_rewrite): Driver for profile rewrite sub-command.
	(print_usage): Print gcov-info usage and exit.
	(print_version): Print gcov-info version.
	(process_args): Process arguments.
	(main): Main routine for gcov-tool.
	* libgcc/libgcov.h : Include the set of base-type headers for
        gcov-tool.
        (struct gcov_info): Make the functions field mutable in gcov-tool
        compilation.
	* libgcc/libgcov-merge.c (gcov_get_counter): New wrapper function
        to get the profile counter.
	(gcov_get_counter_target): New wrapper function to get the profile
        values that should not be scaled.
	(__gcov_merge_add): Replace gcov_read_counter() with the wrapper
        functions.
	(__gcov_merge_ior): Ditto.
	(__gcov_merge_time_profile): Ditto.
	(__gcov_merge_single): Ditto.
	(__gcov_merge_delta): Ditto.
	* libgcc/libgcov-util.c (void gcov_set_verbose): Set the verbose flag
        in the utility functions.
	(set_fn_ctrs): Utility function for reading gcda files to in-memory
        gcov_list object link lists.
	(tag_function): Ditto.
	(tag_blocks): Ditto.
	(tag_arcs): Ditto.
	(tag_lines): Ditto.
	(tag_counters): Ditto.
	(tag_summary): Ditto.
	(read_gcda_finalize): Ditto.
	(read_gcda_file): Ditto.
	(ftw_read_file): Ditto.
	(read_profile_dir_init): Ditto.
	(gcov_read_profile_dir): Ditto.
	(gcov_read_counter_mem): Ditto.
	(gcov_get_merge_weight): Ditto.
	(merge_wrapper): A wrapper function that calls merging handler.
	(gcov_merge): Merge two gcov_info objects with weights.
	(find_match_gcov_info): Find the matched gcov_info in the list.
	(gcov_profile_merge): Merge two gcov_info object lists.
	(__gcov_add_counter_op): Process edge profile counter values.
	(__gcov_ior_counter_op): Process IOR profile counter values.
	(__gcov_delta_counter_op): Process delta profile counter values.
	(__gcov_single_counter_op): Process single  profile counter values.
	(fp_scale): Callback function for float-point scaling.
	(int_scale): Callback function for integer fraction scaling. 
	(gcov_profile_scale): Scaling profile counters.
	(gcov_profile_normalize): Normalize profile counters.

Comments

Jan Hubicka April 15, 2014, 9:38 p.m. UTC | #1
Rong, David, Dehao, Teresa
I would like to have some rought idea of what we could merge this stage1. There is
certainly a lot of interesting stuff on the google branch including AutoFDO, LIPO,
the multivalue profile counters that may be used by the new devirtualization bits
and more. I also think we should switch counts into floating point representation
so Teresa's splitting patch works.

Can we get plans to make this effective? My personal schedule is quite free until
April 29 when I go to Czech Republic for wedding and I will be back in Calgary
at 14th.

> 2014-03-03  Rong Xu  <xur@google.com>
> 
> 	* gcc/gcov-io.c (gcov_read_string): Make this routine available
>         to gcov-tool.
> 	(gcov_sync): Ditto.
> 	* gcc/Makefile.in: Build and install gcov-tool. 
> 	* gcc/gcov-tool.c (unlink_gcda_file): Remove one gcda file.
> 	(unlink_profile_dir): Remove gcda files from the profile path.
> 	(profile_merge): Merge two profiles in directory.
> 	(print_merge_usage_message): Print merge usage.
> 	(merge_usage): Print merge usage and exit.
> 	(do_merge): Driver for profile merge sub-command.
> 	(profile_rewrite): Rewrite profile.
> 	(print_rewrite_usage_message): Print rewrite usage.
> 	(rewrite_usage): Print rewrite usage and exit.
> 	(do_rewrite): Driver for profile rewrite sub-command.
> 	(print_usage): Print gcov-info usage and exit.
> 	(print_version): Print gcov-info version.
> 	(process_args): Process arguments.
> 	(main): Main routine for gcov-tool.
> 	* libgcc/libgcov.h : Include the set of base-type headers for
>         gcov-tool.
>         (struct gcov_info): Make the functions field mutable in gcov-tool
>         compilation.
> 	* libgcc/libgcov-merge.c (gcov_get_counter): New wrapper function
>         to get the profile counter.
> 	(gcov_get_counter_target): New wrapper function to get the profile
>         values that should not be scaled.
> 	(__gcov_merge_add): Replace gcov_read_counter() with the wrapper
>         functions.
> 	(__gcov_merge_ior): Ditto.
> 	(__gcov_merge_time_profile): Ditto.
> 	(__gcov_merge_single): Ditto.
> 	(__gcov_merge_delta): Ditto.
> 	* libgcc/libgcov-util.c (void gcov_set_verbose): Set the verbose flag
>         in the utility functions.
> 	(set_fn_ctrs): Utility function for reading gcda files to in-memory
>         gcov_list object link lists.
> 	(tag_function): Ditto.
> 	(tag_blocks): Ditto.
> 	(tag_arcs): Ditto.
> 	(tag_lines): Ditto.
> 	(tag_counters): Ditto.
> 	(tag_summary): Ditto.
> 	(read_gcda_finalize): Ditto.
> 	(read_gcda_file): Ditto.
> 	(ftw_read_file): Ditto.
> 	(read_profile_dir_init): Ditto.
> 	(gcov_read_profile_dir): Ditto.
> 	(gcov_read_counter_mem): Ditto.
> 	(gcov_get_merge_weight): Ditto.
> 	(merge_wrapper): A wrapper function that calls merging handler.
> 	(gcov_merge): Merge two gcov_info objects with weights.
> 	(find_match_gcov_info): Find the matched gcov_info in the list.
> 	(gcov_profile_merge): Merge two gcov_info object lists.
> 	(__gcov_add_counter_op): Process edge profile counter values.
> 	(__gcov_ior_counter_op): Process IOR profile counter values.
> 	(__gcov_delta_counter_op): Process delta profile counter values.
> 	(__gcov_single_counter_op): Process single  profile counter values.
> 	(fp_scale): Callback function for float-point scaling.
> 	(int_scale): Callback function for integer fraction scaling. 
> 	(gcov_profile_scale): Scaling profile counters.
> 	(gcov_profile_normalize): Normalize profile counters.
> 
> Index: gcc/gcov-io.c
> ===================================================================
> --- gcc/gcov-io.c	(revision 208237)
> +++ gcc/gcov-io.c	(working copy)
> @@ -564,7 +564,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 || defined (IN_GCOV_TOOL)
>  GCOV_LINKAGE const char *
>  gcov_read_string (void)
>  {
> @@ -641,7 +641,7 @@ gcov_read_summary (struct gcov_summary *summary)
>      }
>  }
>  
> -#if !IN_LIBGCOV
> +#if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
>  /* Reset to a known position.  BASE should have been obtained from
>     gcov_position, LENGTH should be a record length.  */

I am slightly confused here, IN_LIBGCOV IMO means that the gcov-io is going
to be linked into the gcov runtime as opposed to gcc, gcov, gcov-dump or
gcov-tool.  Why we define IN_LIBGCOV && IN_GCOV_TOOL?
> Index: gcc/gcov-tool.c
> ===================================================================
> --- gcc/gcov-tool.c	(revision 0)
> +++ gcc/gcov-tool.c	(revision 0)
> @@ -0,0 +1,465 @@
> +/* 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>
> +

I glanced only briefly over the sources, they look resonable. I suppose we will
gain more functionality soon?

> +#ifndef IN_GCOV_TOOL
> +/* About the target.  */
> +
>  #include "tconfig.h"
>  #include "tsystem.h"
>  #include "coretypes.h"
> @@ -79,6 +82,25 @@ typedef unsigned gcov_type_unsigned __attribute__
>  #define GCOV_LOCKED 0
>  #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 */

I see, so the purepose of IN_GCOV_TOOL is that you link special purpose libgcov
into gcov-tool.
We at least need a documentation for this.
> Index: libgcc/libgcov-merge.c
> ===================================================================
> --- libgcc/libgcov-merge.c	(revision 208237)
> +++ 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
> +}

These functions needs documentation.
We now have way to separate runtime from basic IO and yet we have those rather
ugly ifdefs.  Can't we put these into an separate include rather than spreading
ifdefs around the code?

> Index: libgcc/libgcov-util.c
> ===================================================================
> --- libgcc/libgcov-util.c	(revision 0)
> +++ libgcc/libgcov-util.c	(revision 0)
> @@ -0,0 +1,855 @@
> +/* 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_ior 1
> +#define L_gcov_merge_time_profile 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"

This is rather ugly. I would preffer Makefile building libgcov-driver/libgcov-merge
separately for gcov tool. Would that be too hard to arrange?

How libgcov-util is linked into the tool?
> +
> +/* 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_time_profile,
> +};

We are gathering more and more places info about profilers is spread
and this seems to duplicate GCOV_MERGE_FUNCTIONS except for the "..".
Can't we go with gcov-counters.def file that summarizes it at one place?
> +
> +/* Set the ctrs field in gcov_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[] =

This needs documentation.
> +{
> +  {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 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);
> +}
gcc_unreachable? Perhaps with a comment why those are not read in gcda?
> +
> +/* Performaing FN upon delta counters.  */
> +
> +static void
> +__gcov_delta_counter_op (gcov_type *counters, unsigned n_counters,
> +                         counter_op_fn fn, void *data1, void *data2)
> +{
> +  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] = fn (counters[2], data1, data2);
> +      counters[3] = fn (counters[3], data1, data2);
> +    }
> +}
> +
> +/* Performing FN upon single counters.  */
> +
> +static void
> +__gcov_single_counter_op (gcov_type *counters, unsigned n_counters,
> +                          counter_op_fn fn, void *data1, void *data2)
> +{
> +  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] = fn (counters[1], data1, data2);
> +      counters[2] = fn (counters[2], data1, data2);
> +    }

Won't this get wrong answer when counters[0] is not the same?
I would expected the merging code to compare the counters first. Similarly for delta counter.
> +
> +/* Scaling the counter value V by multiplying *(float*) DATA1.  */
> +
> +static gcov_type
> +fp_scale (gcov_type v, void *data1, void *data2 ATTRIBUTE_UNUSED)
> +{
> +  float f = *(float *) data1;
> +  return (gcov_type) (v * f);
> +}
> +
> +/* Scaling the counter value V by multiplying DATA2/DATA1.  */
> +
> +static gcov_type
> +int_scale (gcov_type v, void *data1, void *data2)
> +{
> +  int n = *(int *) data1;
> +  int d = *(int *) data2;
> +  return (gcov_type) ((v / d) * n);
> +}

Adding correct rounding may actually make difference for Martin's startup time work.

The patch looks resonable in general.  I am just concerned about the interfaces within libgcov.
In a way it seems to me that there ought to be ways to make this cleaner without that many of
ifdefs.
Please send updated patch with the changes above and if you have any ideas for cleanups
of the interfaces, I would definitely welcome them.

Honza
Rong Xu April 16, 2014, 5:26 p.m. UTC | #2
On Tue, Apr 15, 2014 at 2:38 PM, Jan Hubicka <hubicka@ucw.cz> wrote:
> Rong, David, Dehao, Teresa
> I would like to have some rought idea of what we could merge this stage1. There is
> certainly a lot of interesting stuff on the google branch including AutoFDO, LIPO,
> the multivalue profile counters that may be used by the new devirtualization bits
> and more. I also think we should switch counts into floating point representation
> so Teresa's splitting patch works.
>
> Can we get plans to make this effective? My personal schedule is quite free until
> April 29 when I go to Czech Republic for wedding and I will be back in Calgary
> at 14th.
>
>> 2014-03-03  Rong Xu  <xur@google.com>
>>
>>       * gcc/gcov-io.c (gcov_read_string): Make this routine available
>>         to gcov-tool.
>>       (gcov_sync): Ditto.
>>       * gcc/Makefile.in: Build and install gcov-tool.
>>       * gcc/gcov-tool.c (unlink_gcda_file): Remove one gcda file.
>>       (unlink_profile_dir): Remove gcda files from the profile path.
>>       (profile_merge): Merge two profiles in directory.
>>       (print_merge_usage_message): Print merge usage.
>>       (merge_usage): Print merge usage and exit.
>>       (do_merge): Driver for profile merge sub-command.
>>       (profile_rewrite): Rewrite profile.
>>       (print_rewrite_usage_message): Print rewrite usage.
>>       (rewrite_usage): Print rewrite usage and exit.
>>       (do_rewrite): Driver for profile rewrite sub-command.
>>       (print_usage): Print gcov-info usage and exit.
>>       (print_version): Print gcov-info version.
>>       (process_args): Process arguments.
>>       (main): Main routine for gcov-tool.
>>       * libgcc/libgcov.h : Include the set of base-type headers for
>>         gcov-tool.
>>         (struct gcov_info): Make the functions field mutable in gcov-tool
>>         compilation.
>>       * libgcc/libgcov-merge.c (gcov_get_counter): New wrapper function
>>         to get the profile counter.
>>       (gcov_get_counter_target): New wrapper function to get the profile
>>         values that should not be scaled.
>>       (__gcov_merge_add): Replace gcov_read_counter() with the wrapper
>>         functions.
>>       (__gcov_merge_ior): Ditto.
>>       (__gcov_merge_time_profile): Ditto.
>>       (__gcov_merge_single): Ditto.
>>       (__gcov_merge_delta): Ditto.
>>       * libgcc/libgcov-util.c (void gcov_set_verbose): Set the verbose flag
>>         in the utility functions.
>>       (set_fn_ctrs): Utility function for reading gcda files to in-memory
>>         gcov_list object link lists.
>>       (tag_function): Ditto.
>>       (tag_blocks): Ditto.
>>       (tag_arcs): Ditto.
>>       (tag_lines): Ditto.
>>       (tag_counters): Ditto.
>>       (tag_summary): Ditto.
>>       (read_gcda_finalize): Ditto.
>>       (read_gcda_file): Ditto.
>>       (ftw_read_file): Ditto.
>>       (read_profile_dir_init): Ditto.
>>       (gcov_read_profile_dir): Ditto.
>>       (gcov_read_counter_mem): Ditto.
>>       (gcov_get_merge_weight): Ditto.
>>       (merge_wrapper): A wrapper function that calls merging handler.
>>       (gcov_merge): Merge two gcov_info objects with weights.
>>       (find_match_gcov_info): Find the matched gcov_info in the list.
>>       (gcov_profile_merge): Merge two gcov_info object lists.
>>       (__gcov_add_counter_op): Process edge profile counter values.
>>       (__gcov_ior_counter_op): Process IOR profile counter values.
>>       (__gcov_delta_counter_op): Process delta profile counter values.
>>       (__gcov_single_counter_op): Process single  profile counter values.
>>       (fp_scale): Callback function for float-point scaling.
>>       (int_scale): Callback function for integer fraction scaling.
>>       (gcov_profile_scale): Scaling profile counters.
>>       (gcov_profile_normalize): Normalize profile counters.
>>
>> Index: gcc/gcov-io.c
>> ===================================================================
>> --- gcc/gcov-io.c     (revision 208237)
>> +++ gcc/gcov-io.c     (working copy)
>> @@ -564,7 +564,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 || defined (IN_GCOV_TOOL)
>>  GCOV_LINKAGE const char *
>>  gcov_read_string (void)
>>  {
>> @@ -641,7 +641,7 @@ gcov_read_summary (struct gcov_summary *summary)
>>      }
>>  }
>>
>> -#if !IN_LIBGCOV
>> +#if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
>>  /* Reset to a known position.  BASE should have been obtained from
>>     gcov_position, LENGTH should be a record length.  */
>
> I am slightly confused here, IN_LIBGCOV IMO means that the gcov-io is going
> to be linked into the gcov runtime as opposed to gcc, gcov, gcov-dump or
> gcov-tool.  Why we define IN_LIBGCOV && IN_GCOV_TOOL?

GCOT_TOOL needs to use this function to read the string in gcda file
to memory to construct gcov_info objects.
As you noticed, gcov runtime does not need this interface. But
gcov-tool links with gcov runtime and it also uses the function.
We could make it available in gcov_runtime, but that will slightly
increase the memory footprint.

>> Index: gcc/gcov-tool.c
>> ===================================================================
>> --- gcc/gcov-tool.c   (revision 0)
>> +++ gcc/gcov-tool.c   (revision 0)
>> @@ -0,0 +1,465 @@
>> +/* 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>
>> +
>
> I glanced only briefly over the sources, they look resonable. I suppose we will
> gain more functionality soon?

The planned new functions for trunk version is: (1) overlap score b/w
two profiles (2) better dumping (top hot objects/function/counters)
and statistics.
Once this basic version is in, we can start to add the new functionality.

>
>> +#ifndef IN_GCOV_TOOL
>> +/* About the target.  */
>> +
>>  #include "tconfig.h"
>>  #include "tsystem.h"
>>  #include "coretypes.h"
>> @@ -79,6 +82,25 @@ typedef unsigned gcov_type_unsigned __attribute__
>>  #define GCOV_LOCKED 0
>>  #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 */
>
> I see, so the purepose of IN_GCOV_TOOL is that you link special purpose libgcov
> into gcov-tool.
> We at least need a documentation for this.

OK. Will document in the updated patch.

>> Index: libgcc/libgcov-merge.c
>> ===================================================================
>> --- libgcc/libgcov-merge.c    (revision 208237)
>> +++ 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
>> +}
>
> These functions needs documentation.
> We now have way to separate runtime from basic IO and yet we have those rather
> ugly ifdefs.  Can't we put these into an separate include rather than spreading
> ifdefs around the code?

OK. I'll try to refactor this in the new patch.

>
>> Index: libgcc/libgcov-util.c
>> ===================================================================
>> --- libgcc/libgcov-util.c     (revision 0)
>> +++ libgcc/libgcov-util.c     (revision 0)
>> @@ -0,0 +1,855 @@
>> +/* 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_ior 1
>> +#define L_gcov_merge_time_profile 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"
>
> This is rather ugly. I would preffer Makefile building libgcov-driver/libgcov-merge
> separately for gcov tool. Would that be too hard to arrange?
>
> How libgcov-util is linked into the tool?

libgcov-util.o is built in gcc/ directory, rather in libgcc.
It's directly linked to gcov-tool.
So libgcov-util.o is built for HOST, not TARGET.
With makefile changes, we can built HOST version of libgcov-driver.o
and libgcov-merge.o.
I also need to make some static functions/variables public.

I did the way in this patch because it incurs least change in existing files.
But, yes, your preferred is doable.

>> +
>> +/* 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_time_profile,
>> +};
>
> We are gathering more and more places info about profilers is spread
> and this seems to duplicate GCOV_MERGE_FUNCTIONS except for the "..".
> Can't we go with gcov-counters.def file that summarizes it at one place?

OK. I'll try to re-factor this.

>> +
>> +/* Set the ctrs field in gcov_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[] =
>
> This needs documentation.

Will do in the new patch.

>> +{
>> +  {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 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);
>> +}
> gcc_unreachable? Perhaps with a comment why those are not read in gcda?

gcov-tool currently does not handle gcno file.
Will add comments here.

>> +
>> +/* Performaing FN upon delta counters.  */
>> +
>> +static void
>> +__gcov_delta_counter_op (gcov_type *counters, unsigned n_counters,
>> +                         counter_op_fn fn, void *data1, void *data2)
>> +{
>> +  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] = fn (counters[2], data1, data2);
>> +      counters[3] = fn (counters[3], data1, data2);
>> +    }
>> +}
>> +
>> +/* Performing FN upon single counters.  */
>> +
>> +static void
>> +__gcov_single_counter_op (gcov_type *counters, unsigned n_counters,
>> +                          counter_op_fn fn, void *data1, void *data2)
>> +{
>> +  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] = fn (counters[1], data1, data2);
>> +      counters[2] = fn (counters[2], data1, data2);
>> +    }
>
> Won't this get wrong answer when counters[0] is not the same?
> I would expected the merging code to compare the counters first. Similarly for delta counter.

These *_op functions are for scaling only. So there is only one
profile involved (thus there is no comparison).
The merge handles are in libgcov-merge.c which have the code to handle
mismatched profile targets.

>> +
>> +/* Scaling the counter value V by multiplying *(float*) DATA1.  */
>> +
>> +static gcov_type
>> +fp_scale (gcov_type v, void *data1, void *data2 ATTRIBUTE_UNUSED)
>> +{
>> +  float f = *(float *) data1;
>> +  return (gcov_type) (v * f);
>> +}
>> +
>> +/* Scaling the counter value V by multiplying DATA2/DATA1.  */
>> +
>> +static gcov_type
>> +int_scale (gcov_type v, void *data1, void *data2)
>> +{
>> +  int n = *(int *) data1;
>> +  int d = *(int *) data2;
>> +  return (gcov_type) ((v / d) * n);
>> +}
>
> Adding correct rounding may actually make difference for Martin's startup time work.

Do you mean to use something like in RDIV macro?

>
> The patch looks resonable in general.  I am just concerned about the interfaces within libgcov.
> In a way it seems to me that there ought to be ways to make this cleaner without that many of
> ifdefs.
> Please send updated patch with the changes above and if you have any ideas for cleanups
> of the interfaces, I would definitely welcome them.

Thanks for the review and suggestion. I'll send an updated patch.

>
> Honza
Jan Hubicka April 17, 2014, 3:34 a.m. UTC | #3
> GCOT_TOOL needs to use this function to read the string in gcda file
> to memory to construct gcov_info objects.
> As you noticed, gcov runtime does not need this interface. But
> gcov-tool links with gcov runtime and it also uses the function.
> We could make it available in gcov_runtime, but that will slightly
> increase the memory footprint.

Yep, it is not really pretty. I wrote bellow some plan how things may be
structured in more convenient way.  Any work in that direction would be welcome.
> 
> The planned new functions for trunk version is: (1) overlap score b/w
> two profiles (2) better dumping (top hot objects/function/counters)
> and statistics.
> Once this basic version is in, we can start to add the new functionality.

Sounds good. I assume the autoFDO does not go via gcov tool but rather uses
custom reader of profile data at GCC side?
I wonder, are there any recent overview papers/slides/text of design of AutoFDO
and other features to be merged?
I remember the talk from GCC Summit and I did read some of pre-LTO LIPO
sources & papers, but it would be nice to have somethin up to date.
> 
> libgcov-util.o is built in gcc/ directory, rather in libgcc.
> It's directly linked to gcov-tool.
> So libgcov-util.o is built for HOST, not TARGET.
> With makefile changes, we can built HOST version of libgcov-driver.o
> and libgcov-merge.o.
> I also need to make some static functions/variables public.

I suppose that can go with IN_GCOV_TOOL ifdef.

So we currently have basic gcov io handling in gcc/gcov-io.c that is borrowed
by libgcc/libgcov* code.  We also will get libgcov-util.c in libgcc directory
that is actually borrowed by by gcc/gcov-tool.c code.

We now have one runtime using STDIO for streaming and kernel has custom version
that goes via /proc interface (last time I looked).  We added some abstraction
into libgcov-interface that is the part that relies on STDIO, partly via gcov-io.c
code and now we have in-memory handling code in libgcov-util.

I guess it would make most sentse to put all the gcov code into a new directory
(libgcov) and make it stand-alone library that can be configured 
1) for stdio based runtime as we do not
2) for runtime missing the interface and relyin on user providing it
3) for use within gcov file manipulation tools with reorg of
GCC/gcov/gcov-dump/gcov-tool to all use the same low-level interfaces.
In a longer term, I would like to make FDO profiling intstrumentation to happen
at linktime. For that I need to make the instrumentation code (minimal spaning
tree & friends) to work w/o cgraph that would ideally be done in a shared
implementation
> > Won't this get wrong answer when counters[0] is not the same?
> > I would expected the merging code to compare the counters first. Similarly for delta counter.
> 
> These *_op functions are for scaling only. So there is only one
> profile involved (thus there is no comparison).
> The merge handles are in libgcov-merge.c which have the code to handle
> mismatched profile targets.

I see, OK then.
> >
> > Adding correct rounding may actually make difference for Martin's startup time work.
> 
> Do you mean to use something like in RDIV macro?

Yes.

Honza
diff mbox

Patch

Index: gcc/gcov-io.c
===================================================================
--- gcc/gcov-io.c	(revision 208237)
+++ gcc/gcov-io.c	(working copy)
@@ -564,7 +564,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 || defined (IN_GCOV_TOOL)
 GCOV_LINKAGE const char *
 gcov_read_string (void)
 {
@@ -641,7 +641,7 @@  gcov_read_summary (struct gcov_summary *summary)
     }
 }
 
-#if !IN_LIBGCOV
+#if !IN_LIBGCOV || defined (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 208237)
+++ 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.
@@ -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-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
@@ -769,6 +771,7 @@  GCC_INSTALL_NAME := $(shell echo gcc|sed '$(progra
 GCC_TARGET_INSTALL_NAME := $(target_noncanonical)-$(shell echo gcc|sed '$(program_transform_name)')
 CPP_INSTALL_NAME := $(shell echo cpp|sed '$(program_transform_name)')
 GCOV_INSTALL_NAME := $(shell echo gcov|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 \
@@ -1487,7 +1490,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
+  $(GCOV_TOOL_OBJS) lto-wrapper.o
 
 # This lists all host object files, whether they are included in this
 # compilation or not.
@@ -1512,6 +1515,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
 
@@ -2570,6 +2574,16 @@  GCOV_DUMP_OBJS = gcov-dump.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 \
+  $(SYSTEM_H) coretypes.h $(TM_H) $(CONFIG_H) version.h intl.h $(DIAGNOSTIC_H)
+	+$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -o $@ $<
+GCOV_TOOL_OBJS = gcov-tool.o libgcov-util.o
+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
@@ -3198,6 +3212,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 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), and also as gcc if native.
Index: gcc/gcov-tool.c
===================================================================
--- gcc/gcov-tool.c	(revision 0)
+++ gcc/gcov-tool.c	(revision 0)
@@ -0,0 +1,465 @@ 
+/* 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, 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;
+
+/* 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;
+}
+
+/* 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, int n, int d)
+{
+  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, n, d);
+
+  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 = 0;
+
+  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_rewrite (argv[optind],  output_dir, 0, 0.0, numerator, denominator);
+      else
+        ret = profile_rewrite (argv[optind],  output_dir, normalize_val, scale, 0, 0);
+    }
+  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);
+  fnotice (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' },
+  { 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;
+}
+
+/* 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: libgcc/libgcov.h
===================================================================
--- libgcc/libgcov.h	(revision 208237)
+++ libgcc/libgcov.h	(working copy)
@@ -33,6 +33,9 @@ 
 #define xcalloc calloc
 #endif
 
+#ifndef IN_GCOV_TOOL
+/* About the target.  */
+
 #include "tconfig.h"
 #include "tsystem.h"
 #include "coretypes.h"
@@ -79,6 +82,25 @@  typedef unsigned gcov_type_unsigned __attribute__
 #define GCOV_LOCKED 0
 #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
@@ -159,8 +181,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 */
 };
 
 /* Register a new object file module.  */
Index: libgcc/libgcov-merge.c
===================================================================
--- libgcc/libgcov-merge.c	(revision 208237)
+++ 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
 
@@ -81,7 +101,7 @@  __gcov_merge_time_profile (gcov_type *counters, un
 
   for (i = 0; i < n_counters; i++)
     {
-      value = gcov_read_counter ();
+      value = gcov_get_counter ();
 
       if (value && (!counters[i] || value < counters[i]))
         counters[i] = value;
@@ -109,9 +129,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;
@@ -148,10 +168,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/libgcov-util.c
===================================================================
--- libgcc/libgcov-util.c	(revision 0)
+++ libgcc/libgcov-util.c	(revision 0)
@@ -0,0 +1,855 @@ 
+/* 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_ior 1
+#define L_gcov_merge_time_profile 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);
+
+/* 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,
+    __gcov_merge_time_profile,
+};
+
+/* Set the ctrs field in gcov_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},
+  {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];
+    }
+}
+
+/* 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;
+  {
+    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;
+      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 objects 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;
+}
+
+typedef gcov_type (*counter_op_fn) (gcov_type, void*, void*);
+
+/* Performing FN upon arc counters.  */
+
+static void
+__gcov_add_counter_op (gcov_type *counters, unsigned n_counters,
+                       counter_op_fn fn, void *data1, void *data2)
+{
+  for (; n_counters; counters++, n_counters--)
+    {
+      gcov_type val = *counters;
+      *counters = fn(val, data1, data2);
+    }
+}
+
+/* Performing FN upon ior counters.  */
+
+static void
+__gcov_ior_counter_op (gcov_type *counters ATTRIBUTE_UNUSED,
+                       unsigned n_counters ATTRIBUTE_UNUSED,
+                       counter_op_fn fn ATTRIBUTE_UNUSED,
+                       void *data1 ATTRIBUTE_UNUSED,
+                       void *data2 ATTRIBUTE_UNUSED)
+{
+  /* Do nothing.  */
+}
+
+/* Performaing FN upon delta counters.  */
+
+static void
+__gcov_delta_counter_op (gcov_type *counters, unsigned n_counters,
+                         counter_op_fn fn, void *data1, void *data2)
+{
+  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] = fn (counters[2], data1, data2);
+      counters[3] = fn (counters[3], data1, data2);
+    }
+}
+
+/* Performing FN upon single counters.  */
+
+static void
+__gcov_single_counter_op (gcov_type *counters, unsigned n_counters,
+                          counter_op_fn fn, void *data1, void *data2)
+{
+  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] = fn (counters[1], data1, data2);
+      counters[2] = fn (counters[2], data1, data2);
+    }
+}
+
+/* Scaling the counter value V by multiplying *(float*) DATA1.  */
+
+static gcov_type
+fp_scale (gcov_type v, void *data1, void *data2 ATTRIBUTE_UNUSED)
+{
+  float f = *(float *) data1;
+  return (gcov_type) (v * f);
+}
+
+/* Scaling the counter value V by multiplying DATA2/DATA1.  */
+
+static gcov_type
+int_scale (gcov_type v, void *data1, void *data2)
+{
+  int n = *(int *) data1;
+  int d = *(int *) data2;
+  return (gcov_type) ((v / d) * n);
+}
+
+/* Type of function used to process counters.  */
+typedef void (*gcov_counter_fn) (gcov_type *, gcov_unsigned_t,
+                          counter_op_fn, void *, void *);
+
+/* Function array to process profile counters.  */
+static gcov_counter_fn ctr_functions[GCOV_COUNTERS] = {
+    __gcov_add_counter_op,
+    __gcov_add_counter_op,
+    __gcov_add_counter_op,
+    __gcov_single_counter_op,
+    __gcov_delta_counter_op,
+    __gcov_single_counter_op,
+    __gcov_add_counter_op,
+    __gcov_ior_counter_op,
+    __gcov_ior_counter_op,
+};
+
+/* Driver for scaling profile counters.  */
+
+int
+gcov_profile_scale (struct gcov_info *profile, float scale_factor, int n, int d)
+{
+  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;
+            if (d == 0)
+              (*ctr_functions[t_ix]) (ci_ptr->values, ci_ptr->num,
+                                      fp_scale, &scale_factor, NULL);
+            else
+              (*ctr_functions[t_ix]) (ci_ptr->values, ci_ptr->num,
+                                      int_scale, &n, &d);
+            ci_ptr++;
+          }
+      }
+
+  return 0;
+}
+
+/* Driver to 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 largest 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, 0, 0);
+}