public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [RFC] libgcov.c re-factoring and offline profile-tool
@ 2013-11-01 21:08 Rong Xu
  2013-11-04  6:55 ` Xinliang David Li
  0 siblings, 1 reply; 55+ messages in thread
From: Rong Xu @ 2013-11-01 21:08 UTC (permalink / raw)
  To: GCC Patches; +Cc: Jan Hubicka

libgcov.c is the main file to generate libgcov.a. This single source generates
21 object files and then archives to a library. These objects are of
very different purposes but they are in the same file guarded by various macros.
The source file becomes quite large and its readability becomes very
low. In its current state:
1) The code is very hard to understand by new developers;
2) It makes regressions more likely when making simple changes;
More importantly,
3) it makes code reuse/sharing very difficult. For instance, Using
libgcov to implement an offline profile tool (see below); kernel FDO
support etc.

We came to this issue when working on an offline tool to handle
profile counters.
Our plan is to have a profile-tool can merge, diff, collect statistics, and
better dump raw profile counters. This is one of the most requested features
from out internal users. This tool can also be very useful for any FDO/coverage
users. In the first part of tool, we want to have the weight
merge. Again, to reuse the code, we have to change libgcov.c. But we don't want
to further diverge libgcov.a in our google branches from the trunk.

Another issue in libgcov.c is function gcov_exit(). It is a huge function
with about 467 lines of code. This function traverses gcov_list and dumps all
the gcda files. The code for merging the gcda files also sits in the same
 function. The structure makes the code-reuse and extension really difficult
(like in kernel FDO, we have to break this function to reuse some of the code).

We propose to break gcov_exit into smaller functions and split libgcov.c into
several files. Our plan for profile-tool is listed in (3).

1. break gcov_exit() into the following structure:
   gcov_exit()
      --> gcov_exit_compute_summary()
      --> allocate_filename_struct()
          for gi_ptr in gcov_list
            --> gcov_exit_dump_gcov()
                   --> gcov_exit_open_gcda_file()
                   --> gcov_exit_merge_gcda ()
                   --> gcov_exit_write_gcda ()

2. split libgcov.c into the following files:
     libgcov-profiler.c
     libgcov-merge.c
     libgcov-interface.c
     libgcov-driver.c
       libgcov-driver-system.c (source included into libgcov-driver.c)

The details of the splitting:
libgcov-interface.c
/* This file contains API functions to other programs and the supporting
   functions.  */
  __gcov_dump()
  __gcov_execl()
  __gcov_execle()
  __gcov_execlp()
  __gcov_execv()
  __gcov_execve()
  __gcov_execvp()
  __gcov_flush()
  __gcov_fork()
  __gcov_reset()
  init_mx()
  init_mx_once()

libgcov-profiler.c
/* This file contains runtime profile handlers.  */
  variables:
    __gcov_indirect_call_callee
    __gcov_indirect_call_counters
  functions:
    __gcov_average_profiler()
    __gcov_indirect_call_profiler()
    __gcov_indirect_call_profiler_v2()
    __gcov_interval_profiler()
    __gcov_ior_profiler()
    __gcov_one_value_profiler()
    __gcov_one_value_profiler_body()
    __gcov_pow2_profiler()

libgcov-merge.c
/* This file contains the merge functions for various counters.  */
  functions:
    __gcov_merge_add()
    __gcov_merge_delta()
    __gcov_merge_ior()
    __gcov_merge_single()

libcov-driver.c
/* This file contains the gcov dumping functions. We separate the
   system dependent part to libgcov-driver-system.c.  */
  variables:
    gcov_list
    gcov_max_filename
    gcov_dump_complete
    ------ newly added file static variables --
    this_prg
    all_prg
    crc32
    gi_filename
    fn_buffer
    fn_tail
    sum_buffer
    next_sum_buffer
    sum_tail
    ----- end -----
  functions:
    free_fn_data()
    buffer_fn_data()
    crc32_unsigned()
    gcov_version()
    gcov_histogram_insert()
    gcov_compute_histogram()
    gcov_clear()
    __gcov_init()
    gcov_exit()
    ------- newly added static functions --
    gcov_exit_compute_summary ()
    gcov_exit_merge_gcda()
    gcov_exit_write_gcda()
    gcov_exit_dump_gcov()
    ----- end -----

libgcov-driver-system.c
/* system dependent part of ligcov-driver.c.  */
  functions:
    create_file_directory()
    ------- newly added static functions --
    gcov_error() /* This replaces all fprintf(stderr, ...) */
    allocate_filename_struct()
    gcov_exit_open_gcda_file()

3. add offline profile-tool support.
   We will change the interface of merge functions to make it
   take in-memory gcov_info list, and a weight as the arguments.

   We will add libgcov-tool.c. It has two APIs:
   (1) read_profile_dir(): read a profile directory and reconstruct the
       gcov_info link list in-memory.
   (2) merge_profiles(): merge two in-memory gcov_info link list.

   We also add profile-tool.c in gcc directory. It will source-include
   libgcov-tool.c and build a host binary. (We don't do library style
   because that will need a hard dependence from gcc to libgcc).
   profile-tool.c will mainly handle user options and the write-out of
   gcov-info link list. Some changes in gcov-io.[ch] will be also needed.

Thanks,

-Rong

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-01 21:08 [RFC] libgcov.c re-factoring and offline profile-tool Rong Xu
@ 2013-11-04  6:55 ` Xinliang David Li
  2013-11-04  9:55   ` Jan Hubicka
  0 siblings, 1 reply; 55+ messages in thread
From: Xinliang David Li @ 2013-11-04  6:55 UTC (permalink / raw)
  To: Rong Xu; +Cc: GCC Patches, Jan Hubicka

Thanks! Can you attach the patch file for the proposed change?

David

On Fri, Nov 1, 2013 at 2:08 PM, Rong Xu <xur@google.com> wrote:
> libgcov.c is the main file to generate libgcov.a. This single source generates
> 21 object files and then archives to a library. These objects are of
> very different purposes but they are in the same file guarded by various macros.
> The source file becomes quite large and its readability becomes very
> low. In its current state:
> 1) The code is very hard to understand by new developers;
> 2) It makes regressions more likely when making simple changes;
> More importantly,
> 3) it makes code reuse/sharing very difficult. For instance, Using
> libgcov to implement an offline profile tool (see below); kernel FDO
> support etc.
>
> We came to this issue when working on an offline tool to handle
> profile counters.
> Our plan is to have a profile-tool can merge, diff, collect statistics, and
> better dump raw profile counters. This is one of the most requested features
> from out internal users. This tool can also be very useful for any FDO/coverage
> users. In the first part of tool, we want to have the weight
> merge. Again, to reuse the code, we have to change libgcov.c. But we don't want
> to further diverge libgcov.a in our google branches from the trunk.
>
> Another issue in libgcov.c is function gcov_exit(). It is a huge function
> with about 467 lines of code. This function traverses gcov_list and dumps all
> the gcda files. The code for merging the gcda files also sits in the same
>  function. The structure makes the code-reuse and extension really difficult
> (like in kernel FDO, we have to break this function to reuse some of the code).
>
> We propose to break gcov_exit into smaller functions and split libgcov.c into
> several files. Our plan for profile-tool is listed in (3).
>
> 1. break gcov_exit() into the following structure:
>    gcov_exit()
>       --> gcov_exit_compute_summary()
>       --> allocate_filename_struct()
>           for gi_ptr in gcov_list
>             --> gcov_exit_dump_gcov()
>                    --> gcov_exit_open_gcda_file()
>                    --> gcov_exit_merge_gcda ()
>                    --> gcov_exit_write_gcda ()
>
> 2. split libgcov.c into the following files:
>      libgcov-profiler.c
>      libgcov-merge.c
>      libgcov-interface.c
>      libgcov-driver.c
>        libgcov-driver-system.c (source included into libgcov-driver.c)
>
> The details of the splitting:
> libgcov-interface.c
> /* This file contains API functions to other programs and the supporting
>    functions.  */
>   __gcov_dump()
>   __gcov_execl()
>   __gcov_execle()
>   __gcov_execlp()
>   __gcov_execv()
>   __gcov_execve()
>   __gcov_execvp()
>   __gcov_flush()
>   __gcov_fork()
>   __gcov_reset()
>   init_mx()
>   init_mx_once()
>
> libgcov-profiler.c
> /* This file contains runtime profile handlers.  */
>   variables:
>     __gcov_indirect_call_callee
>     __gcov_indirect_call_counters
>   functions:
>     __gcov_average_profiler()
>     __gcov_indirect_call_profiler()
>     __gcov_indirect_call_profiler_v2()
>     __gcov_interval_profiler()
>     __gcov_ior_profiler()
>     __gcov_one_value_profiler()
>     __gcov_one_value_profiler_body()
>     __gcov_pow2_profiler()
>
> libgcov-merge.c
> /* This file contains the merge functions for various counters.  */
>   functions:
>     __gcov_merge_add()
>     __gcov_merge_delta()
>     __gcov_merge_ior()
>     __gcov_merge_single()
>
> libcov-driver.c
> /* This file contains the gcov dumping functions. We separate the
>    system dependent part to libgcov-driver-system.c.  */
>   variables:
>     gcov_list
>     gcov_max_filename
>     gcov_dump_complete
>     ------ newly added file static variables --
>     this_prg
>     all_prg
>     crc32
>     gi_filename
>     fn_buffer
>     fn_tail
>     sum_buffer
>     next_sum_buffer
>     sum_tail
>     ----- end -----
>   functions:
>     free_fn_data()
>     buffer_fn_data()
>     crc32_unsigned()
>     gcov_version()
>     gcov_histogram_insert()
>     gcov_compute_histogram()
>     gcov_clear()
>     __gcov_init()
>     gcov_exit()
>     ------- newly added static functions --
>     gcov_exit_compute_summary ()
>     gcov_exit_merge_gcda()
>     gcov_exit_write_gcda()
>     gcov_exit_dump_gcov()
>     ----- end -----
>
> libgcov-driver-system.c
> /* system dependent part of ligcov-driver.c.  */
>   functions:
>     create_file_directory()
>     ------- newly added static functions --
>     gcov_error() /* This replaces all fprintf(stderr, ...) */
>     allocate_filename_struct()
>     gcov_exit_open_gcda_file()
>
> 3. add offline profile-tool support.
>    We will change the interface of merge functions to make it
>    take in-memory gcov_info list, and a weight as the arguments.
>
>    We will add libgcov-tool.c. It has two APIs:
>    (1) read_profile_dir(): read a profile directory and reconstruct the
>        gcov_info link list in-memory.
>    (2) merge_profiles(): merge two in-memory gcov_info link list.
>
>    We also add profile-tool.c in gcc directory. It will source-include
>    libgcov-tool.c and build a host binary. (We don't do library style
>    because that will need a hard dependence from gcc to libgcc).
>    profile-tool.c will mainly handle user options and the write-out of
>    gcov-info link list. Some changes in gcov-io.[ch] will be also needed.
>
> Thanks,
>
> -Rong

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-04  6:55 ` Xinliang David Li
@ 2013-11-04  9:55   ` Jan Hubicka
  2013-11-04 22:46     ` Rong Xu
  0 siblings, 1 reply; 55+ messages in thread
From: Jan Hubicka @ 2013-11-04  9:55 UTC (permalink / raw)
  To: Xinliang David Li; +Cc: Rong Xu, GCC Patches, Jan Hubicka

> Thanks! Can you attach the patch file for the proposed change?
> 
> David
> 
> On Fri, Nov 1, 2013 at 2:08 PM, Rong Xu <xur@google.com> wrote:
> > libgcov.c is the main file to generate libgcov.a. This single source generates
> > 21 object files and then archives to a library. These objects are of
> > very different purposes but they are in the same file guarded by various macros.
> > The source file becomes quite large and its readability becomes very
> > low. In its current state:
> > 1) The code is very hard to understand by new developers;

Agreed, libgcov has became hard to maintain since several parts are written
in very low-level way.
> > 2) It makes regressions more likely when making simple changes;
> > More importantly,
> > 3) it makes code reuse/sharing very difficult. For instance, Using
> > libgcov to implement an offline profile tool (see below); kernel FDO
> > support etc.

Yep, it was my longer term plan to do this, too.
> >
> > We came to this issue when working on an offline tool to handle
> > profile counters.
> > Our plan is to have a profile-tool can merge, diff, collect statistics, and
> > better dump raw profile counters. This is one of the most requested features
> > from out internal users. This tool can also be very useful for any FDO/coverage
> > users. In the first part of tool, we want to have the weight
> > merge. Again, to reuse the code, we have to change libgcov.c. But we don't want
> > to further diverge libgcov.a in our google branches from the trunk.
> >
> > Another issue in libgcov.c is function gcov_exit(). It is a huge function
> > with about 467 lines of code. This function traverses gcov_list and dumps all
> > the gcda files. The code for merging the gcda files also sits in the same
> >  function. The structure makes the code-reuse and extension really difficult
> > (like in kernel FDO, we have to break this function to reuse some of the code).
> >
> > We propose to break gcov_exit into smaller functions and split libgcov.c into
> > several files. Our plan for profile-tool is listed in (3).
> >
> > 1. break gcov_exit() into the following structure:
> >    gcov_exit()
> >       --> gcov_exit_compute_summary()
> >       --> allocate_filename_struct()
> >           for gi_ptr in gcov_list
> >             --> gcov_exit_dump_gcov()
> >                    --> gcov_exit_open_gcda_file()
> >                    --> gcov_exit_merge_gcda ()
> >                    --> gcov_exit_write_gcda ()

Just a short comment that summaries are also produced during the merging - i.e. they are
done on per-file basis. But otherwise I agree.
We should be cureful to not increase footprint of libgcov much for embedded users, but
I believe changes like this can be done w/o additional overhead in the optimized library.
> >
> > 2. split libgcov.c into the following files:
> >      libgcov-profiler.c
> >      libgcov-merge.c
> >      libgcov-interface.c
> >      libgcov-driver.c
> >        libgcov-driver-system.c (source included into libgcov-driver.c)

Seems resonable.  Splitting i/o stuff into separate module (driver) is something I planned
for long time.  What is difference in between libgcov-interface and libgcov-driver?
> >
> > The details of the splitting:
> > libgcov-interface.c
> > /* This file contains API functions to other programs and the supporting
> >    functions.  */
> >   __gcov_dump()
> >   __gcov_execl()
> >   __gcov_execle()
> >   __gcov_execlp()
> >   __gcov_execv()
> >   __gcov_execve()
> >   __gcov_execvp()
> >   __gcov_flush()
> >   __gcov_fork()
> >   __gcov_reset()
> >   init_mx()
> >   init_mx_once()
> >
> > libgcov-profiler.c
> > /* This file contains runtime profile handlers.  */
> >   variables:
> >     __gcov_indirect_call_callee
> >     __gcov_indirect_call_counters
> >   functions:
> >     __gcov_average_profiler()
> >     __gcov_indirect_call_profiler()
> >     __gcov_indirect_call_profiler_v2()
> >     __gcov_interval_profiler()
> >     __gcov_ior_profiler()
> >     __gcov_one_value_profiler()
> >     __gcov_one_value_profiler_body()
> >     __gcov_pow2_profiler()
> >
> > libgcov-merge.c
> > /* This file contains the merge functions for various counters.  */
> >   functions:
> >     __gcov_merge_add()
> >     __gcov_merge_delta()
> >     __gcov_merge_ior()
> >     __gcov_merge_single()
> >
> > libcov-driver.c
> > /* This file contains the gcov dumping functions. We separate the
> >    system dependent part to libgcov-driver-system.c.  */
> >   variables:
> >     gcov_list
> >     gcov_max_filename
> >     gcov_dump_complete
> >     ------ newly added file static variables --
> >     this_prg
> >     all_prg
> >     crc32
> >     gi_filename
> >     fn_buffer
> >     fn_tail
> >     sum_buffer
> >     next_sum_buffer
> >     sum_tail
> >     ----- end -----
> >   functions:
> >     free_fn_data()
> >     buffer_fn_data()
> >     crc32_unsigned()
> >     gcov_version()
> >     gcov_histogram_insert()
> >     gcov_compute_histogram()
> >     gcov_clear()
> >     __gcov_init()
> >     gcov_exit()
> >     ------- newly added static functions --
> >     gcov_exit_compute_summary ()
> >     gcov_exit_merge_gcda()
> >     gcov_exit_write_gcda()
> >     gcov_exit_dump_gcov()
> >     ----- end -----
> >
> > libgcov-driver-system.c
> > /* system dependent part of ligcov-driver.c.  */
> >   functions:
> >     create_file_directory()
> >     ------- newly added static functions --
> >     gcov_error() /* This replaces all fprintf(stderr, ...) */
> >     allocate_filename_struct()
> >     gcov_exit_open_gcda_file()
> >
> > 3. add offline profile-tool support.
> >    We will change the interface of merge functions to make it
> >    take in-memory gcov_info list, and a weight as the arguments.
> >
> >    We will add libgcov-tool.c. It has two APIs:
> >    (1) read_profile_dir(): read a profile directory and reconstruct the
> >        gcov_info link list in-memory.
> >    (2) merge_profiles(): merge two in-memory gcov_info link list.
> >
> >    We also add profile-tool.c in gcc directory. It will source-include
> >    libgcov-tool.c and build a host binary. (We don't do library style
> >    because that will need a hard dependence from gcc to libgcc).
> >    profile-tool.c will mainly handle user options and the write-out of
> >    gcov-info link list. Some changes in gcov-io.[ch] will be also needed.
> >
> > Thanks,

Thanks, it all looks good to me.  One thing I also always wondered about is why
libgcov ended up being partly in libgcc and partly in gcc (the headers) directory.
It would make more sense to have libgcov directory like we have for other runtime
libraries.

Honza
> >
> > -Rong

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-04  9:55   ` Jan Hubicka
@ 2013-11-04 22:46     ` Rong Xu
  2013-11-04 22:52       ` Rong Xu
                         ` (3 more replies)
  0 siblings, 4 replies; 55+ messages in thread
From: Rong Xu @ 2013-11-04 22:46 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Xinliang David Li, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 7711 bytes --]

Honza, Thanks for the comments!
Attached is the patch.
Passed spec2006 fdo build and profiledbootstrap in gcc.
I'm doing some other tests.

-Rong

On Mon, Nov 4, 2013 at 1:55 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>> Thanks! Can you attach the patch file for the proposed change?
>>
>> David
>>
>> On Fri, Nov 1, 2013 at 2:08 PM, Rong Xu <xur@google.com> wrote:
>> > libgcov.c is the main file to generate libgcov.a. This single source generates
>> > 21 object files and then archives to a library. These objects are of
>> > very different purposes but they are in the same file guarded by various macros.
>> > The source file becomes quite large and its readability becomes very
>> > low. In its current state:
>> > 1) The code is very hard to understand by new developers;
>
> Agreed, libgcov has became hard to maintain since several parts are written
> in very low-level way.
>> > 2) It makes regressions more likely when making simple changes;
>> > More importantly,
>> > 3) it makes code reuse/sharing very difficult. For instance, Using
>> > libgcov to implement an offline profile tool (see below); kernel FDO
>> > support etc.
>
> Yep, it was my longer term plan to do this, too.
>> >
>> > We came to this issue when working on an offline tool to handle
>> > profile counters.
>> > Our plan is to have a profile-tool can merge, diff, collect statistics, and
>> > better dump raw profile counters. This is one of the most requested features
>> > from out internal users. This tool can also be very useful for any FDO/coverage
>> > users. In the first part of tool, we want to have the weight
>> > merge. Again, to reuse the code, we have to change libgcov.c. But we don't want
>> > to further diverge libgcov.a in our google branches from the trunk.
>> >
>> > Another issue in libgcov.c is function gcov_exit(). It is a huge function
>> > with about 467 lines of code. This function traverses gcov_list and dumps all
>> > the gcda files. The code for merging the gcda files also sits in the same
>> >  function. The structure makes the code-reuse and extension really difficult
>> > (like in kernel FDO, we have to break this function to reuse some of the code).
>> >
>> > We propose to break gcov_exit into smaller functions and split libgcov.c into
>> > several files. Our plan for profile-tool is listed in (3).
>> >
>> > 1. break gcov_exit() into the following structure:
>> >    gcov_exit()
>> >       --> gcov_exit_compute_summary()
>> >       --> allocate_filename_struct()
>> >           for gi_ptr in gcov_list
>> >             --> gcov_exit_dump_gcov()
>> >                    --> gcov_exit_open_gcda_file()
>> >                    --> gcov_exit_merge_gcda ()
>> >                    --> gcov_exit_write_gcda ()
>
> Just a short comment that summaries are also produced during the merging - i.e. they are
> done on per-file basis. But otherwise I agree.
> We should be cureful to not increase footprint of libgcov much for embedded users, but
> I believe changes like this can be done w/o additional overhead in the optimized library.

The merge of summary has two parts. One is to find the summary to
merge to. That is in gcov_exit_merge_gcda().
The other part of do the actual merging of summary is in gcov_exit_dump_gcov().

I tried to keep the current work flow. So except for a few more calls,
I don't think there is much overhead.

>> >
>> > 2. split libgcov.c into the following files:
>> >      libgcov-profiler.c
>> >      libgcov-merge.c
>> >      libgcov-interface.c
>> >      libgcov-driver.c
>> >        libgcov-driver-system.c (source included into libgcov-driver.c)
>
> Seems resonable.  Splitting i/o stuff into separate module (driver) is something I planned
> for long time.  What is difference in between libgcov-interface and libgcov-driver?

Basically libgcov-driver handles all the dumping of gcov-info.
libgcov-interface is the API used by instrumentation, like the wrapper for fork.


>> >
>> > The details of the splitting:
>> > libgcov-interface.c
>> > /* This file contains API functions to other programs and the supporting
>> >    functions.  */
>> >   __gcov_dump()
>> >   __gcov_execl()
>> >   __gcov_execle()
>> >   __gcov_execlp()
>> >   __gcov_execv()
>> >   __gcov_execve()
>> >   __gcov_execvp()
>> >   __gcov_flush()
>> >   __gcov_fork()
>> >   __gcov_reset()
>> >   init_mx()
>> >   init_mx_once()
>> >
>> > libgcov-profiler.c
>> > /* This file contains runtime profile handlers.  */
>> >   variables:
>> >     __gcov_indirect_call_callee
>> >     __gcov_indirect_call_counters
>> >   functions:
>> >     __gcov_average_profiler()
>> >     __gcov_indirect_call_profiler()
>> >     __gcov_indirect_call_profiler_v2()
>> >     __gcov_interval_profiler()
>> >     __gcov_ior_profiler()
>> >     __gcov_one_value_profiler()
>> >     __gcov_one_value_profiler_body()
>> >     __gcov_pow2_profiler()
>> >
>> > libgcov-merge.c
>> > /* This file contains the merge functions for various counters.  */
>> >   functions:
>> >     __gcov_merge_add()
>> >     __gcov_merge_delta()
>> >     __gcov_merge_ior()
>> >     __gcov_merge_single()
>> >
>> > libcov-driver.c
>> > /* This file contains the gcov dumping functions. We separate the
>> >    system dependent part to libgcov-driver-system.c.  */
>> >   variables:
>> >     gcov_list
>> >     gcov_max_filename
>> >     gcov_dump_complete
>> >     ------ newly added file static variables --
>> >     this_prg
>> >     all_prg
>> >     crc32
>> >     gi_filename
>> >     fn_buffer
>> >     fn_tail
>> >     sum_buffer
>> >     next_sum_buffer
>> >     sum_tail
>> >     ----- end -----
>> >   functions:
>> >     free_fn_data()
>> >     buffer_fn_data()
>> >     crc32_unsigned()
>> >     gcov_version()
>> >     gcov_histogram_insert()
>> >     gcov_compute_histogram()
>> >     gcov_clear()
>> >     __gcov_init()
>> >     gcov_exit()
>> >     ------- newly added static functions --
>> >     gcov_exit_compute_summary ()
>> >     gcov_exit_merge_gcda()
>> >     gcov_exit_write_gcda()
>> >     gcov_exit_dump_gcov()
>> >     ----- end -----
>> >
>> > libgcov-driver-system.c
>> > /* system dependent part of ligcov-driver.c.  */
>> >   functions:
>> >     create_file_directory()
>> >     ------- newly added static functions --
>> >     gcov_error() /* This replaces all fprintf(stderr, ...) */
>> >     allocate_filename_struct()
>> >     gcov_exit_open_gcda_file()
>> >
>> > 3. add offline profile-tool support.
>> >    We will change the interface of merge functions to make it
>> >    take in-memory gcov_info list, and a weight as the arguments.
>> >
>> >    We will add libgcov-tool.c. It has two APIs:
>> >    (1) read_profile_dir(): read a profile directory and reconstruct the
>> >        gcov_info link list in-memory.
>> >    (2) merge_profiles(): merge two in-memory gcov_info link list.
>> >
>> >    We also add profile-tool.c in gcc directory. It will source-include
>> >    libgcov-tool.c and build a host binary. (We don't do library style
>> >    because that will need a hard dependence from gcc to libgcc).
>> >    profile-tool.c will mainly handle user options and the write-out of
>> >    gcov-info link list. Some changes in gcov-io.[ch] will be also needed.
>> >
>> > Thanks,
>
> Thanks, it all looks good to me.  One thing I also always wondered about is why
> libgcov ended up being partly in libgcc and partly in gcc (the headers) directory.
> It would make more sense to have libgcov directory like we have for other runtime
> libraries.
>
I agree with you -- it's better to have a seperate directoy for libgcov.

The other part should be re-refatored is the gcov-io.[ch]. I think they
are even messier than libgcov.c. I plan to clean them after libgcvo.c.

> Honza
>> >
>> > -Rong

[-- Attachment #2: libgcov_patch.txt --]
[-- Type: text/plain, Size: 93855 bytes --]

2013-11-04  Rong Xu  <xur@google.com>

	* libgcc/libgcov.c: Delete as part of re-factoring.
	* libgcc/libgcov-profiler.c (__gcov_interval_profiler): Moved from
	libgcov.c
	(__gcov_pow2_profiler): Ditto.
	(__gcov_one_value_profiler_body): Ditto.
	(__gcov_one_value_profiler): Ditto.
	(__gcov_indirect_call_profiler): Ditto.
	(__gcov_indirect_call_profiler_v2): Ditto.
	(__gcov_average_profiler): Ditto.
	(__gcov_ior_profiler): Ditto.
	* libgcc/libgcov-driver.c (this_prg): Make it file scope
	static variable.
	(all_prg): Ditto.
	(crc32): Ditto.
	(gi_filename): Ditto.
	(fn_buffer): Ditto.
	(sum_buffer): Ditto.
        (struct gcov_filename_aux): New types to store auxiliary information
        for gi_filename.
	(gcov_version): Moved from libgcov.c.
	(crc32_unsigned): Ditto.
	(gcov_histogram_insert): Ditto.
	(gcov_compute_histogram): Ditto.
	(gcov_exit): Ditto.
	(gcov_clear): Ditto.
	(__gcov_init): Ditto.
	(gcov_exit_compute_summary): New function split from gcov_exit().
	(gcov_exit_merge_gcda): Ditto.
	(gcov_exit_write_gcda): Ditto.
	(gcov_exit_dump_gcov): Ditto.
	* libgcc/libgcov-interface.c (init_mx): Moved from libgcov.c.
	(init_mx_once): Ditto.
	(__gcov_flush): Ditto.
	(__gcov_reset): Ditto.
	(__gcov_dump): Ditto.
	(__gcov_fork): Ditto.
	(__gcov_execl): Ditto.
	(__gcov_execlp): Ditto.
	(__gcov_execle): Ditto.
	(__gcov_execv): Ditto.
	(__gcov_execvp): Ditto.
	(__gcov_execve): Ditto.
	* libgcc/libgcov-driver-system.c (gcov_error): New utility function.
	(allocate_filename_struct): New function split from gcov_exit().
	(gcov_exit_open_gcda_file): Ditto.
        (create_file_directory): Moved from libgcov.c.
	* libgcc/libgcov-merge.c:
	(__gcov_merge_add): Moved from libgcov.c.
	(__gcov_merge_ior): Ditto.
	(__gcov_merge_single): Ditto.
	(__gcov_merge_delta): Ditto.
	* libgcc/Makefile.in: Change to build newly added files. 
	* gcc/gcov-io.h (__gcov_merge_ior): Add the decl to avoid warning.

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

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-04 22:46     ` Rong Xu
@ 2013-11-04 22:52       ` Rong Xu
  2013-11-04 22:56       ` Xinliang David Li
                         ` (2 subsequent siblings)
  3 siblings, 0 replies; 55+ messages in thread
From: Rong Xu @ 2013-11-04 22:52 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Xinliang David Li, GCC Patches

BTW, this part of patch only does the re-factoring work.
I haven't finished the porting of profile-tool part of patch to the
trunk. They should be in a separate patch anyway.

-Rong

On Mon, Nov 4, 2013 at 2:43 PM, Rong Xu <xur@google.com> wrote:
> Honza, Thanks for the comments!
> Attached is the patch.
> Passed spec2006 fdo build and profiledbootstrap in gcc.
> I'm doing some other tests.
>
> -Rong
>
> On Mon, Nov 4, 2013 at 1:55 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>>> Thanks! Can you attach the patch file for the proposed change?
>>>
>>> David
>>>
>>> On Fri, Nov 1, 2013 at 2:08 PM, Rong Xu <xur@google.com> wrote:
>>> > libgcov.c is the main file to generate libgcov.a. This single source generates
>>> > 21 object files and then archives to a library. These objects are of
>>> > very different purposes but they are in the same file guarded by various macros.
>>> > The source file becomes quite large and its readability becomes very
>>> > low. In its current state:
>>> > 1) The code is very hard to understand by new developers;
>>
>> Agreed, libgcov has became hard to maintain since several parts are written
>> in very low-level way.
>>> > 2) It makes regressions more likely when making simple changes;
>>> > More importantly,
>>> > 3) it makes code reuse/sharing very difficult. For instance, Using
>>> > libgcov to implement an offline profile tool (see below); kernel FDO
>>> > support etc.
>>
>> Yep, it was my longer term plan to do this, too.
>>> >
>>> > We came to this issue when working on an offline tool to handle
>>> > profile counters.
>>> > Our plan is to have a profile-tool can merge, diff, collect statistics, and
>>> > better dump raw profile counters. This is one of the most requested features
>>> > from out internal users. This tool can also be very useful for any FDO/coverage
>>> > users. In the first part of tool, we want to have the weight
>>> > merge. Again, to reuse the code, we have to change libgcov.c. But we don't want
>>> > to further diverge libgcov.a in our google branches from the trunk.
>>> >
>>> > Another issue in libgcov.c is function gcov_exit(). It is a huge function
>>> > with about 467 lines of code. This function traverses gcov_list and dumps all
>>> > the gcda files. The code for merging the gcda files also sits in the same
>>> >  function. The structure makes the code-reuse and extension really difficult
>>> > (like in kernel FDO, we have to break this function to reuse some of the code).
>>> >
>>> > We propose to break gcov_exit into smaller functions and split libgcov.c into
>>> > several files. Our plan for profile-tool is listed in (3).
>>> >
>>> > 1. break gcov_exit() into the following structure:
>>> >    gcov_exit()
>>> >       --> gcov_exit_compute_summary()
>>> >       --> allocate_filename_struct()
>>> >           for gi_ptr in gcov_list
>>> >             --> gcov_exit_dump_gcov()
>>> >                    --> gcov_exit_open_gcda_file()
>>> >                    --> gcov_exit_merge_gcda ()
>>> >                    --> gcov_exit_write_gcda ()
>>
>> Just a short comment that summaries are also produced during the merging - i.e. they are
>> done on per-file basis. But otherwise I agree.
>> We should be cureful to not increase footprint of libgcov much for embedded users, but
>> I believe changes like this can be done w/o additional overhead in the optimized library.
>
> The merge of summary has two parts. One is to find the summary to
> merge to. That is in gcov_exit_merge_gcda().
> The other part of do the actual merging of summary is in gcov_exit_dump_gcov().
>
> I tried to keep the current work flow. So except for a few more calls,
> I don't think there is much overhead.
>
>>> >
>>> > 2. split libgcov.c into the following files:
>>> >      libgcov-profiler.c
>>> >      libgcov-merge.c
>>> >      libgcov-interface.c
>>> >      libgcov-driver.c
>>> >        libgcov-driver-system.c (source included into libgcov-driver.c)
>>
>> Seems resonable.  Splitting i/o stuff into separate module (driver) is something I planned
>> for long time.  What is difference in between libgcov-interface and libgcov-driver?
>
> Basically libgcov-driver handles all the dumping of gcov-info.
> libgcov-interface is the API used by instrumentation, like the wrapper for fork.
>
>
>>> >
>>> > The details of the splitting:
>>> > libgcov-interface.c
>>> > /* This file contains API functions to other programs and the supporting
>>> >    functions.  */
>>> >   __gcov_dump()
>>> >   __gcov_execl()
>>> >   __gcov_execle()
>>> >   __gcov_execlp()
>>> >   __gcov_execv()
>>> >   __gcov_execve()
>>> >   __gcov_execvp()
>>> >   __gcov_flush()
>>> >   __gcov_fork()
>>> >   __gcov_reset()
>>> >   init_mx()
>>> >   init_mx_once()
>>> >
>>> > libgcov-profiler.c
>>> > /* This file contains runtime profile handlers.  */
>>> >   variables:
>>> >     __gcov_indirect_call_callee
>>> >     __gcov_indirect_call_counters
>>> >   functions:
>>> >     __gcov_average_profiler()
>>> >     __gcov_indirect_call_profiler()
>>> >     __gcov_indirect_call_profiler_v2()
>>> >     __gcov_interval_profiler()
>>> >     __gcov_ior_profiler()
>>> >     __gcov_one_value_profiler()
>>> >     __gcov_one_value_profiler_body()
>>> >     __gcov_pow2_profiler()
>>> >
>>> > libgcov-merge.c
>>> > /* This file contains the merge functions for various counters.  */
>>> >   functions:
>>> >     __gcov_merge_add()
>>> >     __gcov_merge_delta()
>>> >     __gcov_merge_ior()
>>> >     __gcov_merge_single()
>>> >
>>> > libcov-driver.c
>>> > /* This file contains the gcov dumping functions. We separate the
>>> >    system dependent part to libgcov-driver-system.c.  */
>>> >   variables:
>>> >     gcov_list
>>> >     gcov_max_filename
>>> >     gcov_dump_complete
>>> >     ------ newly added file static variables --
>>> >     this_prg
>>> >     all_prg
>>> >     crc32
>>> >     gi_filename
>>> >     fn_buffer
>>> >     fn_tail
>>> >     sum_buffer
>>> >     next_sum_buffer
>>> >     sum_tail
>>> >     ----- end -----
>>> >   functions:
>>> >     free_fn_data()
>>> >     buffer_fn_data()
>>> >     crc32_unsigned()
>>> >     gcov_version()
>>> >     gcov_histogram_insert()
>>> >     gcov_compute_histogram()
>>> >     gcov_clear()
>>> >     __gcov_init()
>>> >     gcov_exit()
>>> >     ------- newly added static functions --
>>> >     gcov_exit_compute_summary ()
>>> >     gcov_exit_merge_gcda()
>>> >     gcov_exit_write_gcda()
>>> >     gcov_exit_dump_gcov()
>>> >     ----- end -----
>>> >
>>> > libgcov-driver-system.c
>>> > /* system dependent part of ligcov-driver.c.  */
>>> >   functions:
>>> >     create_file_directory()
>>> >     ------- newly added static functions --
>>> >     gcov_error() /* This replaces all fprintf(stderr, ...) */
>>> >     allocate_filename_struct()
>>> >     gcov_exit_open_gcda_file()
>>> >
>>> > 3. add offline profile-tool support.
>>> >    We will change the interface of merge functions to make it
>>> >    take in-memory gcov_info list, and a weight as the arguments.
>>> >
>>> >    We will add libgcov-tool.c. It has two APIs:
>>> >    (1) read_profile_dir(): read a profile directory and reconstruct the
>>> >        gcov_info link list in-memory.
>>> >    (2) merge_profiles(): merge two in-memory gcov_info link list.
>>> >
>>> >    We also add profile-tool.c in gcc directory. It will source-include
>>> >    libgcov-tool.c and build a host binary. (We don't do library style
>>> >    because that will need a hard dependence from gcc to libgcc).
>>> >    profile-tool.c will mainly handle user options and the write-out of
>>> >    gcov-info link list. Some changes in gcov-io.[ch] will be also needed.
>>> >
>>> > Thanks,
>>
>> Thanks, it all looks good to me.  One thing I also always wondered about is why
>> libgcov ended up being partly in libgcc and partly in gcc (the headers) directory.
>> It would make more sense to have libgcov directory like we have for other runtime
>> libraries.
>>
> I agree with you -- it's better to have a seperate directoy for libgcov.
>
> The other part should be re-refatored is the gcov-io.[ch]. I think they
> are even messier than libgcov.c. I plan to clean them after libgcvo.c.
>
>> Honza
>>> >
>>> > -Rong

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-04 22:46     ` Rong Xu
  2013-11-04 22:52       ` Rong Xu
@ 2013-11-04 22:56       ` Xinliang David Li
  2013-11-05  0:22       ` Xinliang David Li
  2013-11-11 16:16       ` Jan Hubicka
  3 siblings, 0 replies; 55+ messages in thread
From: Xinliang David Li @ 2013-11-04 22:56 UTC (permalink / raw)
  To: Rong Xu; +Cc: Jan Hubicka, GCC Patches

On Mon, Nov 4, 2013 at 2:43 PM, Rong Xu <xur@google.com> wrote:
> Honza, Thanks for the comments!
> Attached is the patch.
> Passed spec2006 fdo build and profiledbootstrap in gcc.
> I'm doing some other tests.
>
> -Rong
>
> On Mon, Nov 4, 2013 at 1:55 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>>> Thanks! Can you attach the patch file for the proposed change?
>>>
>>> David
>>>
>>> On Fri, Nov 1, 2013 at 2:08 PM, Rong Xu <xur@google.com> wrote:
>>> > libgcov.c is the main file to generate libgcov.a. This single source generates
>>> > 21 object files and then archives to a library. These objects are of
>>> > very different purposes but they are in the same file guarded by various macros.
>>> > The source file becomes quite large and its readability becomes very
>>> > low. In its current state:
>>> > 1) The code is very hard to understand by new developers;
>>
>> Agreed, libgcov has became hard to maintain since several parts are written
>> in very low-level way.
>>> > 2) It makes regressions more likely when making simple changes;
>>> > More importantly,
>>> > 3) it makes code reuse/sharing very difficult. For instance, Using
>>> > libgcov to implement an offline profile tool (see below); kernel FDO
>>> > support etc.
>>
>> Yep, it was my longer term plan to do this, too.
>>> >
>>> > We came to this issue when working on an offline tool to handle
>>> > profile counters.
>>> > Our plan is to have a profile-tool can merge, diff, collect statistics, and
>>> > better dump raw profile counters. This is one of the most requested features
>>> > from out internal users. This tool can also be very useful for any FDO/coverage
>>> > users. In the first part of tool, we want to have the weight
>>> > merge. Again, to reuse the code, we have to change libgcov.c. But we don't want
>>> > to further diverge libgcov.a in our google branches from the trunk.
>>> >
>>> > Another issue in libgcov.c is function gcov_exit(). It is a huge function
>>> > with about 467 lines of code. This function traverses gcov_list and dumps all
>>> > the gcda files. The code for merging the gcda files also sits in the same
>>> >  function. The structure makes the code-reuse and extension really difficult
>>> > (like in kernel FDO, we have to break this function to reuse some of the code).
>>> >
>>> > We propose to break gcov_exit into smaller functions and split libgcov.c into
>>> > several files. Our plan for profile-tool is listed in (3).
>>> >
>>> > 1. break gcov_exit() into the following structure:
>>> >    gcov_exit()
>>> >       --> gcov_exit_compute_summary()
>>> >       --> allocate_filename_struct()
>>> >           for gi_ptr in gcov_list
>>> >             --> gcov_exit_dump_gcov()
>>> >                    --> gcov_exit_open_gcda_file()
>>> >                    --> gcov_exit_merge_gcda ()
>>> >                    --> gcov_exit_write_gcda ()
>>
>> Just a short comment that summaries are also produced during the merging - i.e. they are
>> done on per-file basis. But otherwise I agree.
>> We should be cureful to not increase footprint of libgcov much for embedded users, but
>> I believe changes like this can be done w/o additional overhead in the optimized library.
>
> The merge of summary has two parts. One is to find the summary to
> merge to. That is in gcov_exit_merge_gcda().
> The other part of do the actual merging of summary is in gcov_exit_dump_gcov().
>
> I tried to keep the current work flow. So except for a few more calls,
> I don't think there is much overhead.
>
>>> >
>>> > 2. split libgcov.c into the following files:
>>> >      libgcov-profiler.c
>>> >      libgcov-merge.c
>>> >      libgcov-interface.c
>>> >      libgcov-driver.c
>>> >        libgcov-driver-system.c (source included into libgcov-driver.c)
>>
>> Seems resonable.  Splitting i/o stuff into separate module (driver) is something I planned
>> for long time.  What is difference in between libgcov-interface and libgcov-driver?
>
> Basically libgcov-driver handles all the dumping of gcov-info.
> libgcov-interface is the API used by instrumentation, like the wrapper for fork.

Those are profiling related APIs exposed to user program.

David



>
>
>>> >
>>> > The details of the splitting:
>>> > libgcov-interface.c
>>> > /* This file contains API functions to other programs and the supporting
>>> >    functions.  */
>>> >   __gcov_dump()
>>> >   __gcov_execl()
>>> >   __gcov_execle()
>>> >   __gcov_execlp()
>>> >   __gcov_execv()
>>> >   __gcov_execve()
>>> >   __gcov_execvp()
>>> >   __gcov_flush()
>>> >   __gcov_fork()
>>> >   __gcov_reset()
>>> >   init_mx()
>>> >   init_mx_once()
>>> >
>>> > libgcov-profiler.c
>>> > /* This file contains runtime profile handlers.  */
>>> >   variables:
>>> >     __gcov_indirect_call_callee
>>> >     __gcov_indirect_call_counters
>>> >   functions:
>>> >     __gcov_average_profiler()
>>> >     __gcov_indirect_call_profiler()
>>> >     __gcov_indirect_call_profiler_v2()
>>> >     __gcov_interval_profiler()
>>> >     __gcov_ior_profiler()
>>> >     __gcov_one_value_profiler()
>>> >     __gcov_one_value_profiler_body()
>>> >     __gcov_pow2_profiler()
>>> >
>>> > libgcov-merge.c
>>> > /* This file contains the merge functions for various counters.  */
>>> >   functions:
>>> >     __gcov_merge_add()
>>> >     __gcov_merge_delta()
>>> >     __gcov_merge_ior()
>>> >     __gcov_merge_single()
>>> >
>>> > libcov-driver.c
>>> > /* This file contains the gcov dumping functions. We separate the
>>> >    system dependent part to libgcov-driver-system.c.  */
>>> >   variables:
>>> >     gcov_list
>>> >     gcov_max_filename
>>> >     gcov_dump_complete
>>> >     ------ newly added file static variables --
>>> >     this_prg
>>> >     all_prg
>>> >     crc32
>>> >     gi_filename
>>> >     fn_buffer
>>> >     fn_tail
>>> >     sum_buffer
>>> >     next_sum_buffer
>>> >     sum_tail
>>> >     ----- end -----
>>> >   functions:
>>> >     free_fn_data()
>>> >     buffer_fn_data()
>>> >     crc32_unsigned()
>>> >     gcov_version()
>>> >     gcov_histogram_insert()
>>> >     gcov_compute_histogram()
>>> >     gcov_clear()
>>> >     __gcov_init()
>>> >     gcov_exit()
>>> >     ------- newly added static functions --
>>> >     gcov_exit_compute_summary ()
>>> >     gcov_exit_merge_gcda()
>>> >     gcov_exit_write_gcda()
>>> >     gcov_exit_dump_gcov()
>>> >     ----- end -----
>>> >
>>> > libgcov-driver-system.c
>>> > /* system dependent part of ligcov-driver.c.  */
>>> >   functions:
>>> >     create_file_directory()
>>> >     ------- newly added static functions --
>>> >     gcov_error() /* This replaces all fprintf(stderr, ...) */
>>> >     allocate_filename_struct()
>>> >     gcov_exit_open_gcda_file()
>>> >
>>> > 3. add offline profile-tool support.
>>> >    We will change the interface of merge functions to make it
>>> >    take in-memory gcov_info list, and a weight as the arguments.
>>> >
>>> >    We will add libgcov-tool.c. It has two APIs:
>>> >    (1) read_profile_dir(): read a profile directory and reconstruct the
>>> >        gcov_info link list in-memory.
>>> >    (2) merge_profiles(): merge two in-memory gcov_info link list.
>>> >
>>> >    We also add profile-tool.c in gcc directory. It will source-include
>>> >    libgcov-tool.c and build a host binary. (We don't do library style
>>> >    because that will need a hard dependence from gcc to libgcc).
>>> >    profile-tool.c will mainly handle user options and the write-out of
>>> >    gcov-info link list. Some changes in gcov-io.[ch] will be also needed.
>>> >
>>> > Thanks,
>>
>> Thanks, it all looks good to me.  One thing I also always wondered about is why
>> libgcov ended up being partly in libgcc and partly in gcc (the headers) directory.
>> It would make more sense to have libgcov directory like we have for other runtime
>> libraries.
>>
> I agree with you -- it's better to have a seperate directoy for libgcov.
>
> The other part should be re-refatored is the gcov-io.[ch]. I think they
> are even messier than libgcov.c. I plan to clean them after libgcvo.c.
>
>> Honza
>>> >
>>> > -Rong

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-04 22:46     ` Rong Xu
  2013-11-04 22:52       ` Rong Xu
  2013-11-04 22:56       ` Xinliang David Li
@ 2013-11-05  0:22       ` Xinliang David Li
  2013-11-05  9:23         ` Jan Hubicka
  2013-11-11 16:16       ` Jan Hubicka
  3 siblings, 1 reply; 55+ messages in thread
From: Xinliang David Li @ 2013-11-05  0:22 UTC (permalink / raw)
  To: Rong Xu; +Cc: Jan Hubicka, GCC Patches

I wonder if it makes sense to get rid of the conditional compile such as

#ifdef L_gcov_xxxxx
..

#endif

This has the advantage of producing slightly smaller instrumented
binary, but this benefit can also be achieved via -ffunction-sections
and let linker to garbage collect unused functions.

(probably as a follow up if it makes sense).

David

On Mon, Nov 4, 2013 at 2:43 PM, Rong Xu <xur@google.com> wrote:
> Honza, Thanks for the comments!
> Attached is the patch.
> Passed spec2006 fdo build and profiledbootstrap in gcc.
> I'm doing some other tests.
>
> -Rong
>
> On Mon, Nov 4, 2013 at 1:55 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>>> Thanks! Can you attach the patch file for the proposed change?
>>>
>>> David
>>>
>>> On Fri, Nov 1, 2013 at 2:08 PM, Rong Xu <xur@google.com> wrote:
>>> > libgcov.c is the main file to generate libgcov.a. This single source generates
>>> > 21 object files and then archives to a library. These objects are of
>>> > very different purposes but they are in the same file guarded by various macros.
>>> > The source file becomes quite large and its readability becomes very
>>> > low. In its current state:
>>> > 1) The code is very hard to understand by new developers;
>>
>> Agreed, libgcov has became hard to maintain since several parts are written
>> in very low-level way.
>>> > 2) It makes regressions more likely when making simple changes;
>>> > More importantly,
>>> > 3) it makes code reuse/sharing very difficult. For instance, Using
>>> > libgcov to implement an offline profile tool (see below); kernel FDO
>>> > support etc.
>>
>> Yep, it was my longer term plan to do this, too.
>>> >
>>> > We came to this issue when working on an offline tool to handle
>>> > profile counters.
>>> > Our plan is to have a profile-tool can merge, diff, collect statistics, and
>>> > better dump raw profile counters. This is one of the most requested features
>>> > from out internal users. This tool can also be very useful for any FDO/coverage
>>> > users. In the first part of tool, we want to have the weight
>>> > merge. Again, to reuse the code, we have to change libgcov.c. But we don't want
>>> > to further diverge libgcov.a in our google branches from the trunk.
>>> >
>>> > Another issue in libgcov.c is function gcov_exit(). It is a huge function
>>> > with about 467 lines of code. This function traverses gcov_list and dumps all
>>> > the gcda files. The code for merging the gcda files also sits in the same
>>> >  function. The structure makes the code-reuse and extension really difficult
>>> > (like in kernel FDO, we have to break this function to reuse some of the code).
>>> >
>>> > We propose to break gcov_exit into smaller functions and split libgcov.c into
>>> > several files. Our plan for profile-tool is listed in (3).
>>> >
>>> > 1. break gcov_exit() into the following structure:
>>> >    gcov_exit()
>>> >       --> gcov_exit_compute_summary()
>>> >       --> allocate_filename_struct()
>>> >           for gi_ptr in gcov_list
>>> >             --> gcov_exit_dump_gcov()
>>> >                    --> gcov_exit_open_gcda_file()
>>> >                    --> gcov_exit_merge_gcda ()
>>> >                    --> gcov_exit_write_gcda ()
>>
>> Just a short comment that summaries are also produced during the merging - i.e. they are
>> done on per-file basis. But otherwise I agree.
>> We should be cureful to not increase footprint of libgcov much for embedded users, but
>> I believe changes like this can be done w/o additional overhead in the optimized library.
>
> The merge of summary has two parts. One is to find the summary to
> merge to. That is in gcov_exit_merge_gcda().
> The other part of do the actual merging of summary is in gcov_exit_dump_gcov().
>
> I tried to keep the current work flow. So except for a few more calls,
> I don't think there is much overhead.
>
>>> >
>>> > 2. split libgcov.c into the following files:
>>> >      libgcov-profiler.c
>>> >      libgcov-merge.c
>>> >      libgcov-interface.c
>>> >      libgcov-driver.c
>>> >        libgcov-driver-system.c (source included into libgcov-driver.c)
>>
>> Seems resonable.  Splitting i/o stuff into separate module (driver) is something I planned
>> for long time.  What is difference in between libgcov-interface and libgcov-driver?
>
> Basically libgcov-driver handles all the dumping of gcov-info.
> libgcov-interface is the API used by instrumentation, like the wrapper for fork.
>
>
>>> >
>>> > The details of the splitting:
>>> > libgcov-interface.c
>>> > /* This file contains API functions to other programs and the supporting
>>> >    functions.  */
>>> >   __gcov_dump()
>>> >   __gcov_execl()
>>> >   __gcov_execle()
>>> >   __gcov_execlp()
>>> >   __gcov_execv()
>>> >   __gcov_execve()
>>> >   __gcov_execvp()
>>> >   __gcov_flush()
>>> >   __gcov_fork()
>>> >   __gcov_reset()
>>> >   init_mx()
>>> >   init_mx_once()
>>> >
>>> > libgcov-profiler.c
>>> > /* This file contains runtime profile handlers.  */
>>> >   variables:
>>> >     __gcov_indirect_call_callee
>>> >     __gcov_indirect_call_counters
>>> >   functions:
>>> >     __gcov_average_profiler()
>>> >     __gcov_indirect_call_profiler()
>>> >     __gcov_indirect_call_profiler_v2()
>>> >     __gcov_interval_profiler()
>>> >     __gcov_ior_profiler()
>>> >     __gcov_one_value_profiler()
>>> >     __gcov_one_value_profiler_body()
>>> >     __gcov_pow2_profiler()
>>> >
>>> > libgcov-merge.c
>>> > /* This file contains the merge functions for various counters.  */
>>> >   functions:
>>> >     __gcov_merge_add()
>>> >     __gcov_merge_delta()
>>> >     __gcov_merge_ior()
>>> >     __gcov_merge_single()
>>> >
>>> > libcov-driver.c
>>> > /* This file contains the gcov dumping functions. We separate the
>>> >    system dependent part to libgcov-driver-system.c.  */
>>> >   variables:
>>> >     gcov_list
>>> >     gcov_max_filename
>>> >     gcov_dump_complete
>>> >     ------ newly added file static variables --
>>> >     this_prg
>>> >     all_prg
>>> >     crc32
>>> >     gi_filename
>>> >     fn_buffer
>>> >     fn_tail
>>> >     sum_buffer
>>> >     next_sum_buffer
>>> >     sum_tail
>>> >     ----- end -----
>>> >   functions:
>>> >     free_fn_data()
>>> >     buffer_fn_data()
>>> >     crc32_unsigned()
>>> >     gcov_version()
>>> >     gcov_histogram_insert()
>>> >     gcov_compute_histogram()
>>> >     gcov_clear()
>>> >     __gcov_init()
>>> >     gcov_exit()
>>> >     ------- newly added static functions --
>>> >     gcov_exit_compute_summary ()
>>> >     gcov_exit_merge_gcda()
>>> >     gcov_exit_write_gcda()
>>> >     gcov_exit_dump_gcov()
>>> >     ----- end -----
>>> >
>>> > libgcov-driver-system.c
>>> > /* system dependent part of ligcov-driver.c.  */
>>> >   functions:
>>> >     create_file_directory()
>>> >     ------- newly added static functions --
>>> >     gcov_error() /* This replaces all fprintf(stderr, ...) */
>>> >     allocate_filename_struct()
>>> >     gcov_exit_open_gcda_file()
>>> >
>>> > 3. add offline profile-tool support.
>>> >    We will change the interface of merge functions to make it
>>> >    take in-memory gcov_info list, and a weight as the arguments.
>>> >
>>> >    We will add libgcov-tool.c. It has two APIs:
>>> >    (1) read_profile_dir(): read a profile directory and reconstruct the
>>> >        gcov_info link list in-memory.
>>> >    (2) merge_profiles(): merge two in-memory gcov_info link list.
>>> >
>>> >    We also add profile-tool.c in gcc directory. It will source-include
>>> >    libgcov-tool.c and build a host binary. (We don't do library style
>>> >    because that will need a hard dependence from gcc to libgcc).
>>> >    profile-tool.c will mainly handle user options and the write-out of
>>> >    gcov-info link list. Some changes in gcov-io.[ch] will be also needed.
>>> >
>>> > Thanks,
>>
>> Thanks, it all looks good to me.  One thing I also always wondered about is why
>> libgcov ended up being partly in libgcc and partly in gcc (the headers) directory.
>> It would make more sense to have libgcov directory like we have for other runtime
>> libraries.
>>
> I agree with you -- it's better to have a seperate directoy for libgcov.
>
> The other part should be re-refatored is the gcov-io.[ch]. I think they
> are even messier than libgcov.c. I plan to clean them after libgcvo.c.
>
>> Honza
>>> >
>>> > -Rong

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-05  0:22       ` Xinliang David Li
@ 2013-11-05  9:23         ` Jan Hubicka
  2013-11-05  9:34           ` Jakub Jelinek
  0 siblings, 1 reply; 55+ messages in thread
From: Jan Hubicka @ 2013-11-05  9:23 UTC (permalink / raw)
  To: Xinliang David Li; +Cc: Rong Xu, Jan Hubicka, GCC Patches

> I wonder if it makes sense to get rid of the conditional compile such as
> 
> #ifdef L_gcov_xxxxx
> ..
> 
> #endif
> 
> This has the advantage of producing slightly smaller instrumented
> binary, but this benefit can also be achieved via -ffunction-sections
> and let linker to garbage collect unused functions.
> 
> (probably as a follow up if it makes sense).

I believe we use this funny scheme primarily for targets that have no function
section support (and also the hand made splitting can be more sane).
For a low-level library like libgcov is that is supposed to have small footprint,
I guess the current scheme is resonable thing to do....

Honza

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-05  9:23         ` Jan Hubicka
@ 2013-11-05  9:34           ` Jakub Jelinek
  2013-11-05 17:04             ` Xinliang David Li
  0 siblings, 1 reply; 55+ messages in thread
From: Jakub Jelinek @ 2013-11-05  9:34 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Xinliang David Li, Rong Xu, GCC Patches

On Tue, Nov 05, 2013 at 10:18:50AM +0100, Jan Hubicka wrote:
> > I wonder if it makes sense to get rid of the conditional compile such as
> > 
> > #ifdef L_gcov_xxxxx
> > ..
> > 
> > #endif
> > 
> > This has the advantage of producing slightly smaller instrumented
> > binary, but this benefit can also be achieved via -ffunction-sections
> > and let linker to garbage collect unused functions.
> > 
> > (probably as a follow up if it makes sense).
> 
> I believe we use this funny scheme primarily for targets that have no function
> section support (and also the hand made splitting can be more sane).
> For a low-level library like libgcov is that is supposed to have small footprint,
> I guess the current scheme is resonable thing to do....

I think the #ifdef L* stuff is there mainly so that we have small
granularity in the *.a libraries (one function per object file), so that one
links only what is really required (at least, that is why libgcc.a does
that).  Of course, if there are many interdependencies between the functions
and you always essentially link in everything, the usefulness of that is
lower.

	Jakub

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-05  9:34           ` Jakub Jelinek
@ 2013-11-05 17:04             ` Xinliang David Li
  2013-11-06 21:54               ` Rong Xu
  0 siblings, 1 reply; 55+ messages in thread
From: Xinliang David Li @ 2013-11-05 17:04 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Jan Hubicka, Rong Xu, GCC Patches

Those functions are not referencing each other, but they get used in
different logical groups, so at least control granularity can be
improved. For instance,
1) all gcov user interfaces such as gcov_dump, gcov_flush, gcov_reset
are either not used at all, or all of them are used -- there is no
need to split this group. After Rong's refactoring (move them into a
separate file), the guarding macro can be removed for them
2) _gcov_merge_add is used by 4 different profilers, so it is almost
always linked in

It is unclear how other value profilers are used on other targets. For
x86, they are on by default, so the profiler bodies and merge
functions are also always linked in.

David

On Tue, Nov 5, 2013 at 1:23 AM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Tue, Nov 05, 2013 at 10:18:50AM +0100, Jan Hubicka wrote:
>> > I wonder if it makes sense to get rid of the conditional compile such as
>> >
>> > #ifdef L_gcov_xxxxx
>> > ..
>> >
>> > #endif
>> >
>> > This has the advantage of producing slightly smaller instrumented
>> > binary, but this benefit can also be achieved via -ffunction-sections
>> > and let linker to garbage collect unused functions.
>> >
>> > (probably as a follow up if it makes sense).
>>
>> I believe we use this funny scheme primarily for targets that have no function
>> section support (and also the hand made splitting can be more sane).
>> For a low-level library like libgcov is that is supposed to have small footprint,
>> I guess the current scheme is resonable thing to do....
>
> I think the #ifdef L* stuff is there mainly so that we have small
> granularity in the *.a libraries (one function per object file), so that one
> links only what is really required (at least, that is why libgcc.a does
> that).  Of course, if there are many interdependencies between the functions
> and you always essentially link in everything, the usefulness of that is
> lower.
>
>         Jakub

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-05 17:04             ` Xinliang David Li
@ 2013-11-06 21:54               ` Rong Xu
  2013-11-06 23:33                 ` Xinliang David Li
  2013-11-11 16:22                 ` Jan Hubicka
  0 siblings, 2 replies; 55+ messages in thread
From: Rong Xu @ 2013-11-06 21:54 UTC (permalink / raw)
  To: Xinliang David Li; +Cc: Jakub Jelinek, Jan Hubicka, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 2294 bytes --]

Here is the patch that includes profile-tool.
Profile-tool now has two functions: merge and rewrite. I'll add diff later.

Compiler is tested with spec2006 and profiledbootstrap.
profile-tool is tested with spec2006 profiles.

-Rong

On Tue, Nov 5, 2013 at 8:58 AM, Xinliang David Li <davidxl@google.com> wrote:
> Those functions are not referencing each other, but they get used in
> different logical groups, so at least control granularity can be
> improved. For instance,
> 1) all gcov user interfaces such as gcov_dump, gcov_flush, gcov_reset
> are either not used at all, or all of them are used -- there is no
> need to split this group. After Rong's refactoring (move them into a
> separate file), the guarding macro can be removed for them
> 2) _gcov_merge_add is used by 4 different profilers, so it is almost
> always linked in
>
> It is unclear how other value profilers are used on other targets. For
> x86, they are on by default, so the profiler bodies and merge
> functions are also always linked in.
>
> David
>
> On Tue, Nov 5, 2013 at 1:23 AM, Jakub Jelinek <jakub@redhat.com> wrote:
>> On Tue, Nov 05, 2013 at 10:18:50AM +0100, Jan Hubicka wrote:
>>> > I wonder if it makes sense to get rid of the conditional compile such as
>>> >
>>> > #ifdef L_gcov_xxxxx
>>> > ..
>>> >
>>> > #endif
>>> >
>>> > This has the advantage of producing slightly smaller instrumented
>>> > binary, but this benefit can also be achieved via -ffunction-sections
>>> > and let linker to garbage collect unused functions.
>>> >
>>> > (probably as a follow up if it makes sense).
>>>
>>> I believe we use this funny scheme primarily for targets that have no function
>>> section support (and also the hand made splitting can be more sane).
>>> For a low-level library like libgcov is that is supposed to have small footprint,
>>> I guess the current scheme is resonable thing to do....
>>
>> I think the #ifdef L* stuff is there mainly so that we have small
>> granularity in the *.a libraries (one function per object file), so that one
>> links only what is really required (at least, that is why libgcc.a does
>> that).  Of course, if there are many interdependencies between the functions
>> and you always essentially link in everything, the usefulness of that is
>> lower.
>>
>>         Jakub

[-- Attachment #2: profile_tool_patch.txt --]
[-- Type: text/plain, Size: 128280 bytes --]

2013-11-06  Rong Xu  <xur@google.com>

Index: libgcc/libgcov-tool.c
===================================================================
--- libgcc/libgcov-tool.c	(revision 0)
+++ libgcc/libgcov-tool.c	(revision 0)
@@ -0,0 +1,800 @@
+/* GCC instrumentation plugin for ThreadSanitizer.
+   Copyright (C) 2011-2013 Free Software Foundation, Inc.
+   Contributed by Dmitry Vyukov <dvyukov@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.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* work around the poisoned malloc/calloc in system.h.  */
+#ifndef xmalloc
+#define xmalloc malloc 
+#define xcalloc calloc 
+#endif
+
+#define PROFILE_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
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "intl.h"
+#include "diagnostic.h"
+#include "version.h"
+#include "demangle.h"
+
+/* We need the dumping and merge part of code in libgcov.  */
+#include "libgcov-driver.c"
+#include "libgcov-merge.c"
+
+/* Verbose mode for debug.  */
+static int verbose;
+
+/* Set verbose flag.  */
+void gcov_tool_set_verbose (void)
+{
+  verbose = 1;
+}
+
+/* -------- Read Gcda and Reconstruct GCOV_INFO ----------- */
+
+#include "obstack.h"
+#include <unistd.h>
+#include <ftw.h>
+
+static void tag_function (const char *, unsigned, unsigned);
+static void tag_blocks (const char *, unsigned, unsigned);
+static void tag_arcs (const char *, unsigned, unsigned);
+static void tag_lines (const char *, unsigned, unsigned);
+static void tag_counters (const char *, unsigned, unsigned);
+static void tag_summary (const char *, unsigned, unsigned);
+
+/* The gcov_info for the first module.  */
+static struct gcov_info *curr_gcov_info;
+/* The gcov_info being processed.  */
+static struct gcov_info *gcov_info_head;
+/* This variable contains all the functions in current module.  */
+static struct obstack fn_info;
+/* The function being processed.  */
+static struct gcov_fn_info *curr_fn_info;
+/* The number of functions seen so far.  */
+static unsigned num_fn_info;
+/* This variable contains all the counters for current module.  */
+static int k_ctrs_mask[GCOV_COUNTERS];
+/* The kind of counters that have been seen.  */
+static struct gcov_ctr_info k_ctrs[GCOV_COUNTERS];
+/* Number of kind of counters that have been seen.  */
+static int k_ctrs_types;
+/* The longest length of all the filenames.  */
+static int max_filename_len;
+
+/* Merge functions for counters.  */
+static gcov_merge_fn ctr_merge_functions[GCOV_COUNTERS] = {
+    __gcov_merge_add,
+    __gcov_merge_add,
+    __gcov_merge_add,
+    __gcov_merge_single,
+    __gcov_merge_delta,
+    __gcov_merge_single,
+    __gcov_merge_add,
+    __gcov_merge_ior,
+};
+
+/* Set the fn_ctrs structure in fn_info.  */
+
+static void
+set_fn_ctrs (struct gcov_fn_info *fn_info)
+{
+  int j = 0, i;
+
+  for (i = 0; i < GCOV_COUNTERS; i++)
+    {
+      if (k_ctrs_mask[i] == 0)
+        continue;
+      fn_info->ctrs[j].num = k_ctrs[i].num;
+      fn_info->ctrs[j].values = k_ctrs[i].values;
+      j++;
+    }
+  if (k_ctrs_types == 0)
+    k_ctrs_types = j;
+  else
+    gcc_assert (j == k_ctrs_types);
+}
+
+typedef struct tag_format
+{
+    unsigned tag;
+    char const *name;
+    void (*proc) (const char *, unsigned, unsigned);
+} tag_format_t;
+
+static const tag_format_t tag_table[] =
+{
+  {0, "NOP", NULL},
+  {0, "UNKNOWN", NULL},
+  {0, "COUNTERS", tag_counters},
+  {GCOV_TAG_FUNCTION, "FUNCTION", tag_function},
+  {GCOV_TAG_BLOCKS, "BLOCKS", tag_blocks},
+  {GCOV_TAG_ARCS, "ARCS", tag_arcs},
+  {GCOV_TAG_LINES, "LINES", tag_lines},
+  {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary},
+  {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary},
+  {0, NULL, NULL}
+};
+
+/* Handler for reading funtion tag.  */
+
+static void
+tag_function (const char *filename ATTRIBUTE_UNUSED,
+	      unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+  int i;
+
+  /* write out previous fn_info. */
+  if (num_fn_info)
+    {
+      set_fn_ctrs (curr_fn_info);
+      /* obstack_grow (&fn_info, &curr_fn_info, sizeof (struct gcov_fn_info*)); */
+      obstack_ptr_grow (&fn_info, curr_fn_info);
+    }
+  /* first time.  */
+    {
+      /* Here we over allocate a bit, using GCOV_COUNTERS instead of the actual active
+         counter types.  */
+      curr_fn_info = (struct gcov_fn_info *) xcalloc (sizeof (struct gcov_fn_info)
+                              + GCOV_COUNTERS * sizeof (struct gcov_ctr_info), 1);
+    }
+
+  for (i = 0; i < GCOV_COUNTERS; i++)
+     k_ctrs[i].num = 0;
+  k_ctrs_types = 0;
+
+  curr_fn_info->key = curr_gcov_info;
+  curr_fn_info->ident = gcov_read_unsigned ();
+  curr_fn_info->lineno_checksum = gcov_read_unsigned ();
+  curr_fn_info->cfg_checksum = gcov_read_unsigned ();
+  num_fn_info++;
+
+  if (verbose)
+    fprintf (stderr, "tag one function id=%d \n", curr_fn_info->ident);
+}
+
+/* Handler for reading block tag.  */
+
+static void
+tag_blocks (const char *filename ATTRIBUTE_UNUSED,
+	    unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+  gcc_assert (0);
+}
+
+/* Handler for reading flow arc tag.  */
+
+static void
+tag_arcs (const char *filename ATTRIBUTE_UNUSED,
+	  unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+  gcc_assert (0);
+}
+
+/* Handler for reading line tag.  */
+
+static void
+tag_lines (const char *filename ATTRIBUTE_UNUSED,
+	   unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+  gcc_assert (0);
+}
+
+/* Handler for reading counters array tag.  */
+
+static void
+tag_counters (const char *filename ATTRIBUTE_UNUSED,
+	      unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+  unsigned n_counts = GCOV_TAG_COUNTER_NUM (length);
+  gcov_type *values;
+  unsigned ix;
+  unsigned tag_ix;
+
+  tag_ix = GCOV_COUNTER_FOR_TAG (tag);
+  gcc_assert (tag_ix < GCOV_COUNTERS);
+  k_ctrs_mask [tag_ix] = 1;
+  gcc_assert (k_ctrs[tag_ix].num == 0);
+  k_ctrs[tag_ix].num = n_counts;
+
+  /* fprintf(stderr, "read %d\n", n_counts); */
+   k_ctrs[tag_ix].values = values = (gcov_type *)xmalloc (n_counts * sizeof (gcov_type));
+  gcc_assert (values);
+
+  for (ix = 0; ix != n_counts; ix++)
+    values[ix] = gcov_read_counter ();
+}
+
+/* Handler for reading summary tag.  */
+
+static void
+tag_summary (const char *filename ATTRIBUTE_UNUSED,
+	     unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+  struct gcov_summary summary;
+
+  gcov_read_summary (&summary);
+  if (!saved_summary_checksum)
+    saved_summary_checksum = summary.checksum;
+}
+
+/* This function is called at the end of reading a gcda file.
+   It flushes the contents in curr_fn_info to the gcov_info.  */
+
+static void
+read_gcda_finalize (struct gcov_info *obj_info)
+{
+  int i;
+
+  set_fn_ctrs (curr_fn_info);
+  obstack_ptr_grow (&fn_info, curr_fn_info);
+
+  /* We set the following fields: merge, n_functions, and functions.  */
+  obj_info->n_functions = num_fn_info;
+  obj_info->functions = (const struct gcov_fn_info**) obstack_finish (&fn_info);
+
+  /* wrap all the counter array. */
+  for (i=0; i< GCOV_COUNTERS; i++)
+    {
+      if (k_ctrs_mask[i])
+        obj_info->merge[i] = ctr_merge_functions[i];
+    }
+}
+
+/* Read the content of a gcda file, and return a gcov_info data structure.
+   Program level summary CURRENT_SUMMARY will also be updated.  */
+
+static struct gcov_info *
+read_gcda_file (const char *filename)
+{
+  unsigned tags[4];
+  unsigned depth = 0;
+  unsigned magic, version;
+  struct gcov_info *obj_info;
+  int i;
+
+  for (i=0; i< GCOV_COUNTERS; i++)
+    k_ctrs_mask[i] = 0;
+  k_ctrs_types = 0;
+
+  if (!gcov_open (filename))
+    {
+      fprintf (stderr, "%s:cannot open\n", filename);
+      return NULL;
+    }
+
+  /* read magic.  */
+  magic = gcov_read_unsigned ();
+  if (magic != GCOV_DATA_MAGIC)
+    {
+      fprintf (stderr, "%s:Not a gcov data file.\n", filename);
+      gcov_close ();
+      return NULL;
+    }
+
+  /* read version.  */
+  version = gcov_read_unsigned ();
+  if (version != GCOV_VERSION)
+    {
+      fprintf (stderr, "%s:Incorrect gcov version. %d vs %d \n", filename, version, GCOV_VERSION);
+      gcov_close ();
+      return NULL;
+    }
+
+  /* Instantiate a gcov_info object.  */
+  curr_gcov_info = obj_info = (struct gcov_info *) xcalloc (sizeof (struct gcov_info) +
+             sizeof (struct gcov_ctr_info) * GCOV_COUNTERS, 1);
+
+  obj_info->version = version;
+  obstack_init (&fn_info);
+  num_fn_info = 0;
+  curr_fn_info = 0;
+  {
+    char *str_dup = (char*) xmalloc (strlen (filename) + 1);
+    int len;
+
+    strcpy (str_dup, filename);
+    obj_info->filename = str_dup;
+    if ((len = strlen (filename)) > max_filename_len)
+      max_filename_len = len;
+  }
+
+  /* read stamp.  */
+  obj_info->stamp = gcov_read_unsigned ();
+
+  while (1)
+    {
+      gcov_position_t base;
+      unsigned tag, length;
+      tag_format_t const *format;
+      unsigned tag_depth;
+      int error;
+      unsigned mask;
+
+      tag = gcov_read_unsigned ();
+      if (!tag)
+	break;
+      length = gcov_read_unsigned ();
+      base = gcov_position ();
+      mask = GCOV_TAG_MASK (tag) >> 1;
+      for (tag_depth = 4; mask; mask >>= 8)
+	{
+	  if (((mask & 0xff) != 0xff) /*&& ((mask & 0xff) != 0x3)*/)
+	    {
+	      /* fprintf (stderr, "Warning: %s:tag `%08x' is invalid\n", filename, tag); */
+	      break;
+	    }
+	  tag_depth--;
+	}
+      for (format = tag_table; format->name; format++)
+	if (format->tag == tag)
+	  goto found;
+      format = &tag_table[GCOV_TAG_IS_COUNTER (tag) ? 2 : 1];
+    found:;
+      if (tag)
+	{
+	  if (depth && depth < tag_depth)
+	    {
+	      if (!GCOV_TAG_IS_SUBTAG (tags[depth - 1], tag))
+		fprintf (stderr, "Warning: %s:tag `%08x' is incorrectly nested\n",
+			filename, tag);
+	    }
+	  depth = tag_depth;
+	  tags[depth - 1] = tag;
+	}
+
+      if (format->proc)
+	(*format->proc) (filename, tag, length);
+
+      if (format->proc)
+	{
+	  unsigned long actual_length = gcov_position () - base;
+
+	  if (actual_length > length)
+	    fprintf (stderr,"Warning: %s:record size mismatch %lu bytes overread\n",
+		    filename, actual_length - length);
+	  else if (length > actual_length)
+	    fprintf (stderr,"Warning: %s:record size mismatch %lu bytes unread\n",
+		    filename, length - actual_length);
+	}
+      gcov_sync (base, length);
+      if ((error = gcov_is_error ()))
+	{
+	  fprintf (stderr,error < 0 ? "Warning:%s:counter overflow at %lu\n" :
+		  "Warning:%s:read error at %lu\n", filename,
+		  (long unsigned) gcov_position ());
+	  break;
+	}
+    }
+
+  read_gcda_finalize (obj_info);
+  gcov_close ();
+
+  return obj_info;
+}
+
+#define GCOV_SUFFIX ".gcda"
+
+/* This will be called by ftw. Return a non-zero value
+   to stop the tree walk.  */
+
+static int
+ftw_read_file ( const char *filename,
+                const struct stat *status ATTRIBUTE_UNUSED,
+                int type)
+{
+  int filename_len;
+  int suffix_len;
+  struct gcov_info *obj_info;
+
+  /* Only read regular files.  */
+  if (type != FTW_F)
+    return 0;
+
+  filename_len = strlen (filename);
+  suffix_len = strlen (GCOV_SUFFIX);
+
+  if (filename_len <= suffix_len)
+    return 0;
+
+  if (strcmp(filename + filename_len - suffix_len, GCOV_SUFFIX))
+    return 0;
+
+   if (verbose)
+    printf ("Read file: %s \n", filename);
+
+  obj_info = read_gcda_file (filename);
+
+  obj_info->next = gcov_info_head;
+  gcov_info_head = obj_info;
+
+  return 0;
+}
+
+static inline void
+read_profile_dir_init (void)
+{
+  gcov_info_head = 0;
+}
+
+/* Driver for read a profile directory and convert into gcvo_info list in memory.
+   Return NULL on error,
+   Return the head of gcov_info list on success.
+   Note the file static variable GCOV_MAX_FILENAME is also set.  */
+
+struct gcov_info *
+gcov_read_profile_dir (const char* dir_name, int recompute_summary ATTRIBUTE_UNUSED)
+{
+  char *pwd;
+  int ret;
+
+  read_profile_dir_init ();
+
+  if (access (dir_name, R_OK) != 0)
+    {
+      fprintf (stderr, "Cannot access directory %s.\n", dir_name);
+      return NULL;
+    }
+  pwd = get_current_dir_name ();
+  gcc_assert (pwd);
+  ret = chdir (dir_name);
+  if (ret !=0)
+    {
+      fprintf (stderr, "%s is not a directory.\n", dir_name);
+      return NULL;
+    }
+  ftw (".", ftw_read_file, 50);
+  ret = chdir (pwd);
+  free (pwd);
+
+
+  /* gcov_max_filename is defined in libgcov.c that recored
+     max filename len. We need to set it here to allocate the
+     array for dumping.  */
+  gcov_max_filename = max_filename_len;
+
+  return gcov_info_head;;
+}
+
+/* -------------- Merge Profile Counters ------------------ */
+
+/* Offline tool to manipulate profile data.
+   This tool targets on matched profiles. But it has some tolerance on
+   unmatched profiles.
+   When merging p1 to p2 (p2 is the dst),
+   * m.gcda in p1 but not in p2: append m.gcda to p2 with specified weight; emit warning
+   * m.gcda in p2 but not in p1: keep m.gcda in p2 and multiplying specified weight; emit warning.
+   * m.gcda in both p1 and p2:
+   ** p1->m.gcda->f checksum matches p2->m.gcda->f: simple merge.
+   ** p1->m.gcda->f checksum does not matches p2->m.gcda->f: keep p2->m.gcda->f and
+      drop p1->m.gcda->f. A warning is emitted.  */
+
+/* Add INFO2's counter to INFO1, multiplying weight W.  */
+
+static int
+gcov_merge (struct gcov_info *info1, struct gcov_info *info2, int w)
+{
+  unsigned f_ix;
+  unsigned n_functions = info1->n_functions;
+  int has_mismatch = 0;
+
+  gcc_assert (info2->n_functions == n_functions);
+  for (f_ix = 0; f_ix < n_functions; f_ix++)
+    {
+      unsigned t_ix;
+      const struct gcov_fn_info *gfi_ptr1 = info1->functions[f_ix];
+      const struct gcov_fn_info *gfi_ptr2 = info2->functions[f_ix];
+      const struct gcov_ctr_info *ci_ptr1, *ci_ptr2;
+
+      if (!gfi_ptr1 || gfi_ptr1->key != info1)
+        continue;
+      if (!gfi_ptr2 || gfi_ptr2->key != info2)
+        continue;
+
+      if (gfi_ptr1->cfg_checksum != gfi_ptr2->cfg_checksum)
+        {
+          fprintf (stderr, "In %s, cfg_checksum mismatch, skipping ...\n",
+                  info1->filename);
+          has_mismatch = 1;
+          continue;
+        }
+      ci_ptr1 = gfi_ptr1->ctrs;
+      ci_ptr2 = gfi_ptr2->ctrs;
+      for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++)
+        {
+          gcov_merge_fn merge1 = info1->merge[t_ix];
+          gcov_merge_fn merge2 = info2->merge[t_ix];
+
+          gcc_assert (merge1 == merge2);
+          if (!merge1)
+            continue;
+          gcc_assert (ci_ptr1->num == ci_ptr2->num);
+          (*merge1) (ci_ptr1->values, ci_ptr1->num, ci_ptr2->values, w);
+          ci_ptr1++;
+          ci_ptr2++;
+        }
+    }
+
+  return has_mismatch;
+}
+
+static struct gcov_info *
+find_match_gcov_info (struct gcov_info **array, int size, struct gcov_info *info)
+{
+  struct gcov_info *gi_ptr;
+  struct gcov_info *ret = NULL;
+  int i;
+
+  for (i = 0; i < size; i++)
+    {
+      gi_ptr = array[i];
+      if (gi_ptr == 0)
+        continue;
+      if (!strcmp (gi_ptr->filename, info->filename))
+        {
+          ret = gi_ptr;
+          array[i] = 0;
+          break;
+        }
+    }
+
+  if (ret && ret->n_functions != info->n_functions)
+    {
+      fprintf (stderr, "Mismatched profiles in %s (%d functions"
+                       " vs %d functions).\n",
+                       ret->filename,
+                       ret->n_functions,
+                       info->n_functions);
+      ret = NULL;
+    }
+  return ret;
+}
+
+/* return 0 on success: without mismatch.
+   reutrn 1 on error.  */
+
+int
+gcov_profile_merge (struct gcov_info *tgt_profile, struct gcov_info *src_profile,
+                    int w1, int w2)
+{
+  struct gcov_info *gi_ptr;
+  struct gcov_info **tgt_infos;
+  struct gcov_info *tgt_tail;
+  struct gcov_info **in_src_not_tgt;
+  unsigned tgt_cnt = 0, src_cnt = 0;
+  unsigned unmatch_info_cnt = 0;
+  unsigned int i;
+
+  for (gi_ptr = tgt_profile; gi_ptr; gi_ptr = gi_ptr->next)
+    tgt_cnt++;
+  for (gi_ptr = src_profile; gi_ptr; gi_ptr = gi_ptr->next)
+    src_cnt++;
+  tgt_infos = (struct gcov_info **) xmalloc (sizeof (struct gcov_info *) * tgt_cnt);
+  gcc_assert (tgt_infos);
+  in_src_not_tgt = (struct gcov_info **) xmalloc (sizeof (struct gcov_info *) * src_cnt);
+  gcc_assert (in_src_not_tgt);
+
+  for (gi_ptr = tgt_profile, i = 0; gi_ptr; gi_ptr = gi_ptr->next, i++)
+    tgt_infos[i] = gi_ptr;
+
+  tgt_tail = tgt_infos[tgt_cnt - 1];
+
+  /* First pass on tgt_profile, we multiply w1 to all counters.  */
+  if (w1 > 1)
+    {
+       for (i = 0; i < tgt_cnt; i++)
+         gcov_merge (tgt_infos[i], tgt_infos[i], w1-1);
+    }
+
+  /* Second pass, add src_profile to the tgt_profile.  */
+  for (gi_ptr = src_profile; gi_ptr; gi_ptr = gi_ptr->next)
+    {
+      struct gcov_info *gi_ptr1;
+
+      gi_ptr1 = find_match_gcov_info (tgt_infos, tgt_cnt, gi_ptr);
+      if (gi_ptr1 == NULL)
+        {
+          in_src_not_tgt[unmatch_info_cnt++] = gi_ptr;
+          continue;
+        }
+      gcov_merge (gi_ptr1, gi_ptr, w2);
+    }
+
+#if 0
+  /* For modules left in the array. They are not in src_prfile.  */
+  for (i = 0; i < tgt_cnt; i++)
+    {
+      gi_ptr = tgt_infos[i];
+      if (!gi_ptr)
+        continue;
+      gcov_merge (gi_ptr, gi_ptr, w2);
+    }
+
+  /* For modules in src but not in tgt. We adjust the counter and append.  */
+  for (i = 0; i < unmatch_info_cnt; i++)
+    {
+      gi_ptr = in_src_not_tgt[i];
+      gcov_merge (gi_ptr, gi_ptr, w1+w2-1);
+    }
+#else
+  /* For modules in src but not in tgt. We adjust the counter and append.  */
+  for (i = 0; i < unmatch_info_cnt; i++)
+    {
+      gi_ptr = in_src_not_tgt[i];
+      gcov_merge (gi_ptr, gi_ptr, w2 - 1);
+      tgt_tail->next = gi_ptr;
+      tgt_tail = gi_ptr;
+    }
+#endif
+
+  return 0;
+}
+
+/* -------------- Scale Profile Counters ------------------ */
+
+/* Type of function used to normalize counters.  */
+typedef void (*gcov_scale_fn) (gcov_type *, gcov_unsigned_t, double);
+
+static void
+__gcov_scale_add (gcov_type *counters, unsigned n_counters, double f)
+{
+  for (; n_counters; counters++, n_counters--)
+    {
+      gcov_type val = *counters;
+      *counters = val * f;
+    }
+}
+
+/* Scale ior counters.  */
+
+static void
+__gcov_scale_ior (gcov_type *counters ATTRIBUTE_UNUSED,
+                      unsigned n_counters ATTRIBUTE_UNUSED,
+                      double f ATTRIBUTE_UNUSED)
+{
+  /* do nothing.  */
+}
+
+/* Scale delta counters.  */
+
+static void
+__gcov_scale_delta (gcov_type *counters, unsigned n_counters, double f)
+{
+  unsigned i, n_measures;
+
+  gcc_assert (!(n_counters % 4));
+  n_measures = n_counters / 4;
+  for (i = 0; i < n_measures; i++, counters += 4)
+    {
+      counters[2] *= f;
+      counters[3] *= f;
+    }
+}
+
+/* Scale single counters.  */
+
+static void
+__gcov_scale_single (gcov_type *counters, unsigned n_counters, double f)
+{
+  unsigned i, n_measures;
+
+  gcc_assert (!(n_counters % 3));
+  n_measures = n_counters / 3;
+  for (i = 0; i < n_measures; i++, counters += 3)
+    {
+      counters[1] *= f;
+      counters[2] *= f;
+    }
+}
+
+/* Scaling functions for counters.  */
+static gcov_scale_fn ctr_scale_functions[GCOV_COUNTERS] = {
+    __gcov_scale_add,
+    __gcov_scale_add,
+    __gcov_scale_add,
+    __gcov_scale_single,
+    __gcov_scale_delta,
+    __gcov_scale_single,
+    __gcov_scale_add,
+    __gcov_scale_ior,
+};
+
+
+/* Driver for scale profile counters.  */
+
+int
+gcov_profile_scale (struct gcov_info *profile, float scale_factor)
+{
+  struct gcov_info *gi_ptr;
+  unsigned f_ix;
+
+  if (verbose)
+    fprintf (stderr, "scale_factor is %f\n", scale_factor);
+
+  /* scale the counters.  */
+  for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next)
+    for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
+      {
+        unsigned t_ix;
+        const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix];
+        const struct gcov_ctr_info *ci_ptr;
+
+        if (!gfi_ptr || gfi_ptr->key != gi_ptr)
+          continue;
+
+        ci_ptr = gfi_ptr->ctrs;
+        for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++)
+          {
+            gcov_merge_fn merge = gi_ptr->merge[t_ix];
+
+            if (!merge)
+              continue;
+            (*ctr_scale_functions[t_ix]) (ci_ptr->values, ci_ptr->num, scale_factor);
+            ci_ptr++;
+          }
+      }
+
+  return 0;
+}
+
+/* Driver for normalize profile counters.  */
+
+int
+gcov_profile_normalize (struct gcov_info *profile, gcov_type max_val)
+{
+  struct gcov_info *gi_ptr;
+  gcov_type curr_max_val = 0;
+  unsigned f_ix;
+  unsigned int i;
+  float scale_factor;
+
+  /* get the larest count value.  */
+  for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next)
+    for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
+      {
+        unsigned t_ix;
+        const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix];
+        const struct gcov_ctr_info *ci_ptr;
+
+        if (!gfi_ptr || gfi_ptr->key != gi_ptr)
+          continue;
+
+        ci_ptr = gfi_ptr->ctrs;
+        for (t_ix = 0; t_ix < 1; t_ix++)
+          {
+            for (i = 0; i < ci_ptr->num; i++)
+              if (ci_ptr->values[i] > curr_max_val)
+                curr_max_val = ci_ptr->values[i];
+            ci_ptr++;
+          }
+      }
+
+  scale_factor = (float)max_val / curr_max_val;
+  if (verbose)
+    fprintf (stderr, "max_val is %lld\n", (long long) curr_max_val);
+
+  return gcov_profile_scale (profile, scale_factor);
+}
Index: libgcc/libgcov-interface.c
===================================================================
--- libgcc/libgcov-interface.c	(revision 0)
+++ libgcc/libgcov-interface.c	(revision 0)
@@ -0,0 +1,282 @@
+/* Routines required for instrumenting a program.  */
+/* Compile this one with gcc.  */
+/* Copyright (C) 1989-2013 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+#include "tconfig.h"
+#include "tsystem.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "libgcc_tm.h"
+#include "gthr.h"
+
+#if defined(inhibit_libc)
+#define IN_LIBGCOV (-1)
+#else
+#define IN_LIBGCOV 1
+#if defined(L_gcov_flush)
+#define GCOV_LINKAGE /* nothing */
+#endif
+#endif
+#include "gcov-io.h"
+
+#if defined(inhibit_libc)
+
+#ifdef L_gcov_flush
+void __gcov_flush (void) {}
+#endif
+
+#ifdef L_gcov_reset
+void __gcov_reset (void) {}
+#endif
+
+#ifdef L_gcov_dump
+void __gcov_dump (void) {}
+#endif
+
+#else
+
+extern void gcov_clear (void) ATTRIBUTE_HIDDEN;
+extern void gcov_exit (void) ATTRIBUTE_HIDDEN;
+extern void set_gcov_dump_complete (void) ATTRIBUTE_HIDDEN;
+extern void reset_gcov_dump_complete (void) ATTRIBUTE_HIDDEN;
+
+#ifdef L_gcov_flush
+
+#ifdef __GTHREAD_MUTEX_INIT
+ATTRIBUTE_HIDDEN __gthread_mutex_t __gcov_flush_mx = __GTHREAD_MUTEX_INIT;
+#define init_mx_once()
+#else
+__gthread_mutex_t __gcov_flush_mx ATTRIBUTE_HIDDEN;
+
+static void
+init_mx (void)
+{
+  __GTHREAD_MUTEX_INIT_FUNCTION (&__gcov_flush_mx);
+}
+static void
+init_mx_once (void)
+{
+  static __gthread_once_t once = __GTHREAD_ONCE_INIT;
+  __gthread_once (&once, init_mx);
+}
+#endif
+
+/* Called before fork or exec - write out profile information gathered so
+   far and reset it to zero.  This avoids duplication or loss of the
+   profile information gathered so far.  */
+
+void
+__gcov_flush (void)
+{
+  init_mx_once ();
+  __gthread_mutex_lock (&__gcov_flush_mx);
+
+  gcov_exit ();
+  gcov_clear ();
+
+  __gthread_mutex_unlock (&__gcov_flush_mx);
+}
+
+#endif /* L_gcov_flush */
+
+#ifdef L_gcov_reset
+
+/* Function that can be called from application to reset counters to zero,
+   in order to collect profile in region of interest.  */
+
+void
+__gcov_reset (void)
+{
+  gcov_clear ();
+  /* Re-enable dumping to support collecting profile in multiple regions
+     of interest.  */
+  reset_gcov_dump_complete ();
+}
+
+#endif /* L_gcov_reset */
+
+#ifdef L_gcov_dump
+
+/* Function that can be called from application to write profile collected
+   so far, in order to collect profile in region of interest.  */
+
+void
+__gcov_dump (void)
+{
+  gcov_exit ();
+  /* Prevent profile from being dumped a second time on application exit.  */
+  set_gcov_dump_complete ();
+}
+
+#endif /* L_gcov_dump */
+
+
+#ifdef L_gcov_fork
+/* A wrapper for the fork function.  Flushes the accumulated profiling data, so
+   that they are not counted twice.  */
+
+pid_t
+__gcov_fork (void)
+{
+  pid_t pid;
+  extern __gthread_mutex_t __gcov_flush_mx;
+  __gcov_flush ();
+  pid = fork ();
+  if (pid == 0)
+    __GTHREAD_MUTEX_INIT_FUNCTION (&__gcov_flush_mx);
+  return pid;
+}
+#endif
+
+#ifdef L_gcov_execl
+/* A wrapper for the execl function.  Flushes the accumulated profiling data, so
+   that they are not lost.  */
+
+int
+__gcov_execl (const char *path, char *arg, ...)
+{
+  va_list ap, aq;
+  unsigned i, length;
+  char **args;
+
+  __gcov_flush ();
+
+  va_start (ap, arg);
+  va_copy (aq, ap);
+
+  length = 2;
+  while (va_arg (ap, char *))
+    length++;
+  va_end (ap);
+
+  args = (char **) alloca (length * sizeof (void *));
+  args[0] = arg;
+  for (i = 1; i < length; i++)
+    args[i] = va_arg (aq, char *);
+  va_end (aq);
+
+  return execv (path, args);
+}
+#endif
+
+#ifdef L_gcov_execlp
+/* A wrapper for the execlp function.  Flushes the accumulated profiling data, so
+   that they are not lost.  */
+
+int
+__gcov_execlp (const char *path, char *arg, ...)
+{
+  va_list ap, aq;
+  unsigned i, length;
+  char **args;
+
+  __gcov_flush ();
+
+  va_start (ap, arg);
+  va_copy (aq, ap);
+
+  length = 2;
+  while (va_arg (ap, char *))
+    length++;
+  va_end (ap);
+
+  args = (char **) alloca (length * sizeof (void *));
+  args[0] = arg;
+  for (i = 1; i < length; i++)
+    args[i] = va_arg (aq, char *);
+  va_end (aq);
+
+  return execvp (path, args);
+}
+#endif
+
+#ifdef L_gcov_execle
+/* A wrapper for the execle function.  Flushes the accumulated profiling data, so
+   that they are not lost.  */
+
+int
+__gcov_execle (const char *path, char *arg, ...)
+{
+  va_list ap, aq;
+  unsigned i, length;
+  char **args;
+  char **envp;
+
+  __gcov_flush ();
+
+  va_start (ap, arg);
+  va_copy (aq, ap);
+
+  length = 2;
+  while (va_arg (ap, char *))
+    length++;
+  va_end (ap);
+
+  args = (char **) alloca (length * sizeof (void *));
+  args[0] = arg;
+  for (i = 1; i < length; i++)
+    args[i] = va_arg (aq, char *);
+  envp = va_arg (aq, char **);
+  va_end (aq);
+
+  return execve (path, args, envp);
+}
+#endif
+
+#ifdef L_gcov_execv
+/* A wrapper for the execv function.  Flushes the accumulated profiling data, so
+   that they are not lost.  */
+
+int
+__gcov_execv (const char *path, char *const argv[])
+{
+  __gcov_flush ();
+  return execv (path, argv);
+}
+#endif
+
+#ifdef L_gcov_execvp
+/* A wrapper for the execvp function.  Flushes the accumulated profiling data, so
+   that they are not lost.  */
+
+int
+__gcov_execvp (const char *path, char *const argv[])
+{
+  __gcov_flush ();
+  return execvp (path, argv);
+}
+#endif
+
+#ifdef L_gcov_execve
+/* A wrapper for the execve function.  Flushes the accumulated profiling data, so
+   that they are not lost.  */
+
+int
+__gcov_execve (const char *path, char *const argv[], char *const envp[])
+{
+  __gcov_flush ();
+  return execve (path, argv, envp);
+}
+#endif
+#endif /* inhibit_libc */
Index: libgcc/libgcov.c
===================================================================
--- libgcc/libgcov.c	(revision 204285)
+++ libgcc/libgcov.c	(working copy)
@@ -1,1374 +0,0 @@
-/* Routines required for instrumenting a program.  */
-/* Compile this one with gcc.  */
-/* Copyright (C) 1989-2013 Free Software Foundation, Inc.
-
-This file is part of GCC.
-
-GCC is free software; you can redistribute it and/or modify it under
-the terms of the GNU General Public License as published by the Free
-Software Foundation; either version 3, or (at your option) any later
-version.
-
-GCC is distributed in the hope that it will be useful, but WITHOUT ANY
-WARRANTY; without even the implied warranty of MERCHANTABILITY or
-FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-for more details.
-
-Under Section 7 of GPL version 3, you are granted additional
-permissions described in the GCC Runtime Library Exception, version
-3.1, as published by the Free Software Foundation.
-
-You should have received a copy of the GNU General Public License and
-a copy of the GCC Runtime Library Exception along with this program;
-see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
-<http://www.gnu.org/licenses/>.  */
-
-#include "tconfig.h"
-#include "tsystem.h"
-#include "coretypes.h"
-#include "tm.h"
-#include "libgcc_tm.h"
-#include "gthr.h"
-
-#if defined(inhibit_libc)
-#define IN_LIBGCOV (-1)
-#else
-#define IN_LIBGCOV 1
-#if defined(L_gcov)
-#define GCOV_LINKAGE /* nothing */
-#endif
-#endif
-#include "gcov-io.h"
-
-#if defined(inhibit_libc)
-/* If libc and its header files are not available, provide dummy functions.  */
-
-#ifdef L_gcov
-void __gcov_init (struct gcov_info *p __attribute__ ((unused))) {}
-void __gcov_flush (void) {}
-#endif
-
-#ifdef L_gcov_reset
-void __gcov_reset (void) {}
-#endif
-
-#ifdef L_gcov_dump
-void __gcov_dump (void) {}
-#endif
-
-#ifdef L_gcov_merge_add
-void __gcov_merge_add (gcov_type *counters  __attribute__ ((unused)),
-		       unsigned n_counters __attribute__ ((unused))) {}
-#endif
-
-#ifdef L_gcov_merge_single
-void __gcov_merge_single (gcov_type *counters  __attribute__ ((unused)),
-			  unsigned n_counters __attribute__ ((unused))) {}
-#endif
-
-#ifdef L_gcov_merge_delta
-void __gcov_merge_delta (gcov_type *counters  __attribute__ ((unused)),
-			 unsigned n_counters __attribute__ ((unused))) {}
-#endif
-
-#else
-
-#include <string.h>
-#if GCOV_LOCKED
-#include <fcntl.h>
-#include <errno.h>
-#include <sys/stat.h>
-#endif
-
-extern void gcov_clear (void) ATTRIBUTE_HIDDEN;
-extern void gcov_exit (void) ATTRIBUTE_HIDDEN;
-extern int gcov_dump_complete ATTRIBUTE_HIDDEN;
-
-#ifdef L_gcov
-#include "gcov-io.c"
-
-struct gcov_fn_buffer
-{
-  struct gcov_fn_buffer *next;
-  unsigned fn_ix;
-  struct gcov_fn_info info;
-  /* note gcov_fn_info ends in a trailing array.  */
-};
-
-struct gcov_summary_buffer
-{
-  struct gcov_summary_buffer *next;
-  struct gcov_summary summary;
-};
-
-/* Chain of per-object gcov structures.  */
-static struct gcov_info *gcov_list;
-
-/* Size of the longest file name. */
-static size_t gcov_max_filename = 0;
-
-/* Flag when the profile has already been dumped via __gcov_dump().  */
-int gcov_dump_complete = 0;
-
-/* Make sure path component of the given FILENAME exists, create
-   missing directories. FILENAME must be writable.
-   Returns zero on success, or -1 if an error occurred.  */
-
-static int
-create_file_directory (char *filename)
-{
-#if !defined(TARGET_POSIX_IO) && !defined(_WIN32)
-  (void) filename;
-  return -1;
-#else
-  char *s;
-
-  s = filename;
-
-  if (HAS_DRIVE_SPEC(s))
-    s += 2;
-  if (IS_DIR_SEPARATOR(*s))
-    ++s;
-  for (; *s != '\0'; s++)
-    if (IS_DIR_SEPARATOR(*s))
-      {
-        char sep = *s;
-	*s  = '\0';
-
-        /* Try to make directory if it doesn't already exist.  */
-        if (access (filename, F_OK) == -1
-#ifdef TARGET_POSIX_IO
-            && mkdir (filename, 0755) == -1
-#else
-            && mkdir (filename) == -1
-#endif
-            /* The directory might have been made by another process.  */
-	    && errno != EEXIST)
-	  {
-            fprintf (stderr, "profiling:%s:Cannot create directory\n",
-		     filename);
-            *s = sep;
-	    return -1;
-	  };
-
-	*s = sep;
-      };
-  return 0;
-#endif
-}
-
-static struct gcov_fn_buffer *
-free_fn_data (const struct gcov_info *gi_ptr, struct gcov_fn_buffer *buffer,
-	      unsigned limit)
-{
-  struct gcov_fn_buffer *next;
-  unsigned ix, n_ctr = 0;
-  
-  if (!buffer)
-    return 0;
-  next = buffer->next;
-
-  for (ix = 0; ix != limit; ix++)
-    if (gi_ptr->merge[ix])
-      free (buffer->info.ctrs[n_ctr++].values);
-  free (buffer);
-  return next;
-}
-  
-static struct gcov_fn_buffer **
-buffer_fn_data (const char *filename, const struct gcov_info *gi_ptr,
-		struct gcov_fn_buffer **end_ptr, unsigned fn_ix)
-{
-  unsigned n_ctrs = 0, ix = 0;
-  struct gcov_fn_buffer *fn_buffer;
-  unsigned len;
-
-  for (ix = GCOV_COUNTERS; ix--;)
-    if (gi_ptr->merge[ix])
-      n_ctrs++;
-
-  len = sizeof (*fn_buffer) + sizeof (fn_buffer->info.ctrs[0]) * n_ctrs;
-  fn_buffer = (struct gcov_fn_buffer *)malloc (len);
-
-  if (!fn_buffer)
-    goto fail;
-  
-  fn_buffer->next = 0;
-  fn_buffer->fn_ix = fn_ix;
-  fn_buffer->info.ident = gcov_read_unsigned ();
-  fn_buffer->info.lineno_checksum = gcov_read_unsigned ();
-  fn_buffer->info.cfg_checksum = gcov_read_unsigned ();
-
-  for (n_ctrs = ix = 0; ix != GCOV_COUNTERS; ix++)
-    {
-      gcov_unsigned_t length;
-      gcov_type *values;
-
-      if (!gi_ptr->merge[ix])
-	continue;
-      
-      if (gcov_read_unsigned () != GCOV_TAG_FOR_COUNTER (ix))
-	{
-	  len = 0;
-	  goto fail;
-	}
-
-      length = GCOV_TAG_COUNTER_NUM (gcov_read_unsigned ());
-      len = length * sizeof (gcov_type);
-      values = (gcov_type *)malloc (len);
-      if (!values)
-	goto fail;
-      
-      fn_buffer->info.ctrs[n_ctrs].num = length;
-      fn_buffer->info.ctrs[n_ctrs].values = values;
-
-      while (length--)
-	*values++ = gcov_read_counter ();
-      n_ctrs++;
-    }
-  
-  *end_ptr = fn_buffer;
-  return &fn_buffer->next;
-
- fail:
-  fprintf (stderr, "profiling:%s:Function %u %s %u \n", filename, fn_ix,
-	   len ? "cannot allocate" : "counter mismatch", len ? len : ix);
-
-  return (struct gcov_fn_buffer **)free_fn_data (gi_ptr, fn_buffer, ix);
-}
-
-/* Add an unsigned value to the current crc */
-
-static gcov_unsigned_t
-crc32_unsigned (gcov_unsigned_t crc32, gcov_unsigned_t value)
-{
-  unsigned ix;
-
-  for (ix = 32; ix--; value <<= 1)
-    {
-      unsigned feedback;
-
-      feedback = (value ^ crc32) & 0x80000000 ? 0x04c11db7 : 0;
-      crc32 <<= 1;
-      crc32 ^= feedback;
-    }
-
-  return crc32;
-}
-
-/* Check if VERSION of the info block PTR matches libgcov one.
-   Return 1 on success, or zero in case of versions mismatch.
-   If FILENAME is not NULL, its value used for reporting purposes
-   instead of value from the info block.  */
-
-static int
-gcov_version (struct gcov_info *ptr, gcov_unsigned_t version,
-	      const char *filename)
-{
-  if (version != GCOV_VERSION)
-    {
-      char v[4], e[4];
-
-      GCOV_UNSIGNED2STRING (v, version);
-      GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
-
-      fprintf (stderr,
-	       "profiling:%s:Version mismatch - expected %.4s got %.4s\n",
-	       filename? filename : ptr->filename, e, v);
-      return 0;
-    }
-  return 1;
-}
-
-/* Insert counter VALUE into HISTOGRAM.  */
-
-static void
-gcov_histogram_insert(gcov_bucket_type *histogram, gcov_type value)
-{
-  unsigned i;
-
-  i = gcov_histo_index(value);
-  histogram[i].num_counters++;
-  histogram[i].cum_value += value;
-  if (value < histogram[i].min_value)
-    histogram[i].min_value = value;
-}
-
-/* Computes a histogram of the arc counters to place in the summary SUM.  */
-
-static void
-gcov_compute_histogram (struct gcov_summary *sum)
-{
-  struct gcov_info *gi_ptr;
-  const struct gcov_fn_info *gfi_ptr;
-  const struct gcov_ctr_info *ci_ptr;
-  struct gcov_ctr_summary *cs_ptr;
-  unsigned t_ix, f_ix, ctr_info_ix, ix;
-  int h_ix;
-
-  /* This currently only applies to arc counters.  */
-  t_ix = GCOV_COUNTER_ARCS;
-
-  /* First check if there are any counts recorded for this counter.  */
-  cs_ptr = &(sum->ctrs[t_ix]);
-  if (!cs_ptr->num)
-    return;
-
-  for (h_ix = 0; h_ix < GCOV_HISTOGRAM_SIZE; h_ix++)
-    {
-      cs_ptr->histogram[h_ix].num_counters = 0;
-      cs_ptr->histogram[h_ix].min_value = cs_ptr->run_max;
-      cs_ptr->histogram[h_ix].cum_value = 0;
-    }
-
-  /* Walk through all the per-object structures and record each of
-     the count values in histogram.  */
-  for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
-    {
-      if (!gi_ptr->merge[t_ix])
-        continue;
-
-      /* Find the appropriate index into the gcov_ctr_info array
-         for the counter we are currently working on based on the
-         existence of the merge function pointer for this object.  */
-      for (ix = 0, ctr_info_ix = 0; ix < t_ix; ix++)
-        {
-          if (gi_ptr->merge[ix])
-            ctr_info_ix++;
-        }
-      for (f_ix = 0; f_ix != gi_ptr->n_functions; f_ix++)
-        {
-          gfi_ptr = gi_ptr->functions[f_ix];
-
-          if (!gfi_ptr || gfi_ptr->key != gi_ptr)
-            continue;
-
-          ci_ptr = &gfi_ptr->ctrs[ctr_info_ix];
-          for (ix = 0; ix < ci_ptr->num; ix++)
-            gcov_histogram_insert (cs_ptr->histogram, ci_ptr->values[ix]);
-        }
-    }
-}
-
-/* Dump the coverage counts. We merge with existing counts when
-   possible, to avoid growing the .da files ad infinitum. We use this
-   program's checksum to make sure we only accumulate whole program
-   statistics to the correct summary. An object file might be embedded
-   in two separate programs, and we must keep the two program
-   summaries separate.  */
-
-void
-gcov_exit (void)
-{
-  struct gcov_info *gi_ptr;
-  const struct gcov_fn_info *gfi_ptr;
-  struct gcov_summary this_prg; /* summary for program.  */
-#if !GCOV_LOCKED
-  struct gcov_summary all_prg;  /* summary for all instances of program.  */
-#endif
-  struct gcov_ctr_summary *cs_ptr;
-  const struct gcov_ctr_info *ci_ptr;
-  unsigned t_ix;
-  int f_ix;
-  gcov_unsigned_t c_num;
-  const char *gcov_prefix;
-  int gcov_prefix_strip = 0;
-  size_t prefix_length;
-  char *gi_filename, *gi_filename_up;
-  gcov_unsigned_t crc32 = 0;
-
-  /* Prevent the counters from being dumped a second time on exit when the
-     application already wrote out the profile using __gcov_dump().  */
-  if (gcov_dump_complete)
-    return;
-
-#if !GCOV_LOCKED
-  memset (&all_prg, 0, sizeof (all_prg));
-#endif
-  /* Find the totals for this execution.  */
-  memset (&this_prg, 0, sizeof (this_prg));
-  for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
-    {
-      crc32 = crc32_unsigned (crc32, gi_ptr->stamp);
-      crc32 = crc32_unsigned (crc32, gi_ptr->n_functions);
-      
-      for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; f_ix++)
-	{
-	  gfi_ptr = gi_ptr->functions[f_ix];
-
-	  if (gfi_ptr && gfi_ptr->key != gi_ptr)
-	    gfi_ptr = 0;
-	  
-	  crc32 = crc32_unsigned (crc32, gfi_ptr ? gfi_ptr->cfg_checksum : 0);
-	  crc32 = crc32_unsigned (crc32,
-				  gfi_ptr ? gfi_ptr->lineno_checksum : 0);
-	  if (!gfi_ptr)
-	    continue;
-
-	  ci_ptr = gfi_ptr->ctrs;
-	  for (t_ix = 0; t_ix != GCOV_COUNTERS_SUMMABLE; t_ix++)
-	    {
-	      if (!gi_ptr->merge[t_ix])
-		continue;
-
-	      cs_ptr = &this_prg.ctrs[t_ix];
-	      cs_ptr->num += ci_ptr->num;
-	      crc32 = crc32_unsigned (crc32, ci_ptr->num);
-	      
-	      for (c_num = 0; c_num < ci_ptr->num; c_num++)
-		{
-		  cs_ptr->sum_all += ci_ptr->values[c_num];
-		  if (cs_ptr->run_max < ci_ptr->values[c_num])
-		    cs_ptr->run_max = ci_ptr->values[c_num];
-		}
-	      ci_ptr++;
-	    }
-	}
-    }
-  gcov_compute_histogram (&this_prg);
-
-  {
-    /* Check if the level of dirs to strip off specified. */
-    char *tmp = getenv("GCOV_PREFIX_STRIP");
-    if (tmp)
-      {
-	gcov_prefix_strip = atoi (tmp);
-	/* Do not consider negative values. */
-	if (gcov_prefix_strip < 0)
-	  gcov_prefix_strip = 0;
-      }
-  }
-
-  /* Get file name relocation prefix.  Non-absolute values are ignored. */
-  gcov_prefix = getenv("GCOV_PREFIX");
-  if (gcov_prefix)
-    {
-      prefix_length = strlen(gcov_prefix);
-
-      /* Remove an unnecessary trailing '/' */
-      if (IS_DIR_SEPARATOR (gcov_prefix[prefix_length - 1]))
-	prefix_length--;
-    }
-  else
-    prefix_length = 0;
-
-  /* If no prefix was specified and a prefix stip, then we assume
-     relative.  */
-  if (gcov_prefix_strip != 0 && prefix_length == 0)
-    {
-      gcov_prefix = ".";
-      prefix_length = 1;
-    }
-  /* Allocate and initialize the filename scratch space plus one.  */
-  gi_filename = (char *) alloca (prefix_length + gcov_max_filename + 2);
-  if (prefix_length)
-    memcpy (gi_filename, gcov_prefix, prefix_length);
-  gi_filename_up = gi_filename + prefix_length;
-
-  /* Now merge each file.  */
-  for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
-    {
-      unsigned n_counts;
-      struct gcov_summary prg; /* summary for this object over all
-				  program.  */
-      struct gcov_ctr_summary *cs_prg, *cs_tprg;
-#if !GCOV_LOCKED
-      struct gcov_ctr_summary *cs_all;
-#endif
-      int error = 0;
-      gcov_unsigned_t tag, length;
-      gcov_position_t summary_pos = 0;
-      gcov_position_t eof_pos = 0;
-      const char *fname, *s;
-      struct gcov_fn_buffer *fn_buffer = 0;
-      struct gcov_fn_buffer **fn_tail = &fn_buffer;
-      struct gcov_summary_buffer *next_sum_buffer, *sum_buffer = 0;
-      struct gcov_summary_buffer **sum_tail = &sum_buffer;
-
-      fname = gi_ptr->filename;
-
-      /* Avoid to add multiple drive letters into combined path.  */
-      if (prefix_length != 0 && HAS_DRIVE_SPEC(fname))
-        fname += 2;
-
-      /* Build relocated filename, stripping off leading
-         directories from the initial filename if requested. */
-      if (gcov_prefix_strip > 0)
-        {
-          int level = 0;
-          s = fname;
-          if (IS_DIR_SEPARATOR(*s))
-            ++s;
-
-          /* Skip selected directory levels. */
-	  for (; (*s != '\0') && (level < gcov_prefix_strip); s++)
-	    if (IS_DIR_SEPARATOR(*s))
-	      {
-		fname = s;
-		level++;
-	      }
-        }
-
-      /* Update complete filename with stripped original. */
-      if (prefix_length != 0 && !IS_DIR_SEPARATOR (*fname))
-        {
-          /* If prefix is given, add directory separator.  */
-	  strcpy (gi_filename_up, "/");
-	  strcpy (gi_filename_up + 1, fname);
-	}
-      else
-        strcpy (gi_filename_up, fname);
-
-      if (!gcov_open (gi_filename))
-	{
-	  /* Open failed likely due to missed directory.
-	     Create directory and retry to open file. */
-          if (create_file_directory (gi_filename))
-	    {
-	      fprintf (stderr, "profiling:%s:Skip\n", gi_filename);
-	      continue;
-	    }
-	  if (!gcov_open (gi_filename))
-	    {
-              fprintf (stderr, "profiling:%s:Cannot open\n", gi_filename);
-	      continue;
-	    }
-	}
-
-      tag = gcov_read_unsigned ();
-      if (tag)
-	{
-	  /* Merge data from file.  */
-	  if (tag != GCOV_DATA_MAGIC)
-	    {
-	      fprintf (stderr, "profiling:%s:Not a gcov data file\n",
-		       gi_filename);
-	      goto read_fatal;
-	    }
-	  length = gcov_read_unsigned ();
-	  if (!gcov_version (gi_ptr, length, gi_filename))
-	    goto read_fatal;
-
-	  length = gcov_read_unsigned ();
-	  if (length != gi_ptr->stamp)
-	    /* Read from a different compilation. Overwrite the file.  */
-	    goto rewrite;
-
-	  /* Look for program summary.  */
-	  for (f_ix = 0;;)
-	    {
-	      struct gcov_summary tmp;
-	      
-	      eof_pos = gcov_position ();
-	      tag = gcov_read_unsigned ();
-	      if (tag != GCOV_TAG_PROGRAM_SUMMARY)
-		break;
-
-	      f_ix--;
-	      length = gcov_read_unsigned ();
-	      gcov_read_summary (&tmp);
-	      if ((error = gcov_is_error ()))
-		goto read_error;
-	      if (summary_pos)
-                {
-                  /* Save all summaries after the one that will be
-                     merged into below. These will need to be rewritten
-                     as histogram merging may change the number of non-zero
-                     histogram entries that will be emitted, and thus the
-                     size of the merged summary.  */
-                  (*sum_tail) = (struct gcov_summary_buffer *)
-                      malloc (sizeof(struct gcov_summary_buffer));
-                  (*sum_tail)->summary = tmp;
-                  (*sum_tail)->next = 0;
-                  sum_tail = &((*sum_tail)->next);
-                  goto next_summary;
-                }
-	      if (tmp.checksum != crc32)
-                goto next_summary;
-	      
-	      for (t_ix = 0; t_ix != GCOV_COUNTERS_SUMMABLE; t_ix++)
-		if (tmp.ctrs[t_ix].num != this_prg.ctrs[t_ix].num)
-                  goto next_summary;
-	      prg = tmp;
-	      summary_pos = eof_pos;
-
-	    next_summary:;
-	    }
-	  
-	  /* Merge execution counts for each function.  */
-	  for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions;
-	       f_ix++, tag = gcov_read_unsigned ())
-	    {
-	      gfi_ptr = gi_ptr->functions[f_ix];
-
-	      if (tag != GCOV_TAG_FUNCTION)
-		goto read_mismatch;
-
-	      length = gcov_read_unsigned ();
-	      if (!length)
-		/* This function did not appear in the other program.
-		   We have nothing to merge.  */
-		continue;
-
-	      if (length != GCOV_TAG_FUNCTION_LENGTH)
-		goto read_mismatch;
-	      
-	      if (!gfi_ptr || gfi_ptr->key != gi_ptr)
-		{
-		  /* This function appears in the other program.  We
-		     need to buffer the information in order to write
-		     it back out -- we'll be inserting data before
-		     this point, so cannot simply keep the data in the
-		     file.  */
-		  fn_tail = buffer_fn_data (gi_filename,
-					    gi_ptr, fn_tail, f_ix);
-		  if (!fn_tail)
-		    goto read_mismatch;
-		  continue;
-		}
-
-	      length = gcov_read_unsigned ();
-	      if (length != gfi_ptr->ident)
-		goto read_mismatch;
-	      
-	      length = gcov_read_unsigned ();
-	      if (length != gfi_ptr->lineno_checksum)
-		goto read_mismatch;
-	      
-	      length = gcov_read_unsigned ();
-	      if (length != gfi_ptr->cfg_checksum)
-		goto read_mismatch;
-	      
-	      ci_ptr = gfi_ptr->ctrs;
-	      for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
-		{
-		  gcov_merge_fn merge = gi_ptr->merge[t_ix];
-
-		  if (!merge)
-		    continue;
-
-		  tag = gcov_read_unsigned ();
-		  length = gcov_read_unsigned ();
-		  if (tag != GCOV_TAG_FOR_COUNTER (t_ix)
-		      || length != GCOV_TAG_COUNTER_LENGTH (ci_ptr->num))
-		    goto read_mismatch;
-		  (*merge) (ci_ptr->values, ci_ptr->num);
-		  ci_ptr++;
-		}
-	      if ((error = gcov_is_error ()))
-		goto read_error;
-	    }
-
-	  if (tag)
-	    {
-	    read_mismatch:;
-	      fprintf (stderr, "profiling:%s:Merge mismatch for %s %u\n",
-		       gi_filename, f_ix >= 0 ? "function" : "summary",
-		       f_ix < 0 ? -1 - f_ix : f_ix);
-	      goto read_fatal;
-	    }
-	}
-      goto rewrite;
-
-    read_error:;
-      fprintf (stderr, "profiling:%s:%s merging\n", gi_filename,
-	       error < 0 ? "Overflow": "Error");
-
-      goto read_fatal;
-
-    rewrite:;
-      gcov_rewrite ();
-      if (!summary_pos)
-	{
-	  memset (&prg, 0, sizeof (prg));
-	  summary_pos = eof_pos;
-	}
-
-      /* Merge the summaries.  */
-      for (t_ix = 0; t_ix < GCOV_COUNTERS_SUMMABLE; t_ix++)
-	{
-	  cs_prg = &prg.ctrs[t_ix];
-	  cs_tprg = &this_prg.ctrs[t_ix];
-
-	  if (gi_ptr->merge[t_ix])
-	    {
-	      if (!cs_prg->runs++)
-	        cs_prg->num = cs_tprg->num;
-	      cs_prg->sum_all += cs_tprg->sum_all;
-	      if (cs_prg->run_max < cs_tprg->run_max)
-		cs_prg->run_max = cs_tprg->run_max;
-	      cs_prg->sum_max += cs_tprg->run_max;
-              if (cs_prg->runs == 1)
-                memcpy (cs_prg->histogram, cs_tprg->histogram,
-                        sizeof (gcov_bucket_type) * GCOV_HISTOGRAM_SIZE);
-              else
-                gcov_histogram_merge (cs_prg->histogram, cs_tprg->histogram);
-	    }
-	  else if (cs_prg->runs)
-	    goto read_mismatch;
-
-#if !GCOV_LOCKED
-	  cs_all = &all_prg.ctrs[t_ix];
-	  if (!cs_all->runs && cs_prg->runs)
-            {
-              cs_all->num = cs_prg->num;
-              cs_all->runs = cs_prg->runs;
-              cs_all->sum_all = cs_prg->sum_all;
-              cs_all->run_max = cs_prg->run_max;
-              cs_all->sum_max = cs_prg->sum_max;
-            }
-	  else if (!all_prg.checksum
-                   /* Don't compare the histograms, which may have slight
-                      variations depending on the order they were updated
-                      due to the truncating integer divides used in the
-                      merge.  */
-                   && (cs_all->num != cs_prg->num
-                       || cs_all->runs != cs_prg->runs
-                       || cs_all->sum_all != cs_prg->sum_all
-                       || cs_all->run_max != cs_prg->run_max
-                       || cs_all->sum_max != cs_prg->sum_max))
-	    {
-	      fprintf (stderr,
-                       "profiling:%s:Data file mismatch - some data files may "
-                       "have been concurrently updated without locking support\n",
-		       gi_filename);
-	      all_prg.checksum = ~0u;
-	    }
-#endif
-	}
-
-      prg.checksum = crc32;
-
-      /* Write out the data.  */
-      if (!eof_pos)
-	{
-	  gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION);
-	  gcov_write_unsigned (gi_ptr->stamp);
-	}
-
-      if (summary_pos)
-	gcov_seek (summary_pos);
-
-      /* Generate whole program statistics.  */
-      gcov_write_summary (GCOV_TAG_PROGRAM_SUMMARY, &prg);
-
-      /* Rewrite all the summaries that were after the summary we merged
-         into. This is necessary as the merged summary may have a different
-         size due to the number of non-zero histogram entries changing after
-         merging.  */
-      
-      while (sum_buffer)
-        {
-          gcov_write_summary (GCOV_TAG_PROGRAM_SUMMARY, &sum_buffer->summary);
-          next_sum_buffer = sum_buffer->next;
-          free (sum_buffer);
-          sum_buffer = next_sum_buffer;
-        }
-
-      /* Write execution counts for each function.  */
-      for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; f_ix++)
-	{
-	  unsigned buffered = 0;
-
-	  if (fn_buffer && fn_buffer->fn_ix == (unsigned)f_ix)
-	    {
-	      /* Buffered data from another program.  */
-	      buffered = 1;
-	      gfi_ptr = &fn_buffer->info;
-	      length = GCOV_TAG_FUNCTION_LENGTH;
-	    }
-	  else
-	    {
-	      gfi_ptr = gi_ptr->functions[f_ix];
-	      if (gfi_ptr && gfi_ptr->key == gi_ptr)
-		length = GCOV_TAG_FUNCTION_LENGTH;
-	      else
-		length = 0;
-	    }
-	  
-	  gcov_write_tag_length (GCOV_TAG_FUNCTION, length);
-	  if (!length)
-	    continue;
-	  
-	  gcov_write_unsigned (gfi_ptr->ident);
-	  gcov_write_unsigned (gfi_ptr->lineno_checksum);
-	  gcov_write_unsigned (gfi_ptr->cfg_checksum);
-
-	  ci_ptr = gfi_ptr->ctrs;
-	  for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
-	    {
-	      if (!gi_ptr->merge[t_ix])
-		continue;
-
-	      n_counts = ci_ptr->num;
-	      gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
-				     GCOV_TAG_COUNTER_LENGTH (n_counts));
-	      gcov_type *c_ptr = ci_ptr->values;
-	      while (n_counts--)
-		gcov_write_counter (*c_ptr++);
-	      ci_ptr++;
-	    }
-	  if (buffered)
-	    fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
-	}
-
-      gcov_write_unsigned (0);
-
-    read_fatal:;
-      while (fn_buffer)
-	fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
-
-      if ((error = gcov_close ()))
-	  fprintf (stderr, error  < 0 ?
-		   "profiling:%s:Overflow writing\n" :
-		   "profiling:%s:Error writing\n",
-		   gi_filename);
-    }
-}
-
-/* Reset all counters to zero.  */
-
-void
-gcov_clear (void)
-{
-  const struct gcov_info *gi_ptr;
-
-  for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
-    {
-      unsigned f_ix;
-
-      for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
-	{
-	  unsigned t_ix;
-	  const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix];
-
-	  if (!gfi_ptr || gfi_ptr->key != gi_ptr)
-	    continue;
-	  const struct gcov_ctr_info *ci_ptr = gfi_ptr->ctrs;
-	  for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++)
-	    {
-	      if (!gi_ptr->merge[t_ix])
-		continue;
-	      
-	      memset (ci_ptr->values, 0, sizeof (gcov_type) * ci_ptr->num);
-	      ci_ptr++;
-	    }
-	}
-    }
-}
-
-/* Add a new object file onto the bb chain.  Invoked automatically
-   when running an object file's global ctors.  */
-
-void
-__gcov_init (struct gcov_info *info)
-{
-  if (!info->version || !info->n_functions)
-    return;
-  if (gcov_version (info, info->version, 0))
-    {
-      size_t filename_length = strlen(info->filename);
-
-      /* Refresh the longest file name information */
-      if (filename_length > gcov_max_filename)
-        gcov_max_filename = filename_length;
-
-      if (!gcov_list)
-	atexit (gcov_exit);
-
-      info->next = gcov_list;
-      gcov_list = info;
-    }
-  info->version = 0;
-}
-
-#ifdef __GTHREAD_MUTEX_INIT
-ATTRIBUTE_HIDDEN __gthread_mutex_t __gcov_flush_mx = __GTHREAD_MUTEX_INIT;
-#define init_mx_once()
-#else
-__gthread_mutex_t __gcov_flush_mx ATTRIBUTE_HIDDEN;
-
-static void
-init_mx (void)
-{
-  __GTHREAD_MUTEX_INIT_FUNCTION (&__gcov_flush_mx);
-}
-static void
-init_mx_once (void)
-{
-  static __gthread_once_t once = __GTHREAD_ONCE_INIT;
-  __gthread_once (&once, init_mx);
-}
-#endif
-
-/* Called before fork or exec - write out profile information gathered so
-   far and reset it to zero.  This avoids duplication or loss of the
-   profile information gathered so far.  */
-
-void
-__gcov_flush (void)
-{
-  init_mx_once ();
-  __gthread_mutex_lock (&__gcov_flush_mx);
-
-  gcov_exit ();
-  gcov_clear ();
-
-  __gthread_mutex_unlock (&__gcov_flush_mx);
-}
-
-#endif /* L_gcov */
-
-#ifdef L_gcov_reset
-
-/* Function that can be called from application to reset counters to zero,
-   in order to collect profile in region of interest.  */
-
-void
-__gcov_reset (void)
-{
-  gcov_clear ();
-  /* Re-enable dumping to support collecting profile in multiple regions
-     of interest.  */
-  gcov_dump_complete = 0;
-}
-
-#endif /* L_gcov_reset */
-
-#ifdef L_gcov_dump
-
-/* Function that can be called from application to write profile collected
-   so far, in order to collect profile in region of interest.  */
-
-void
-__gcov_dump (void)
-{
-  gcov_exit ();
-  /* Prevent profile from being dumped a second time on application exit.  */
-  gcov_dump_complete = 1;
-}
-
-#endif /* L_gcov_dump */
-
-#ifdef L_gcov_merge_add
-/* The profile merging function that just adds the counters.  It is given
-   an array COUNTERS of N_COUNTERS old counters and it reads the same number
-   of counters from the gcov file.  */
-void
-__gcov_merge_add (gcov_type *counters, unsigned n_counters)
-{
-  for (; n_counters; counters++, n_counters--)
-    *counters += gcov_read_counter ();
-}
-#endif /* L_gcov_merge_add */
-
-#ifdef L_gcov_merge_ior
-/* The profile merging function that just adds the counters.  It is given
-   an array COUNTERS of N_COUNTERS old counters and it reads the same number
-   of counters from the gcov file.  */
-void
-__gcov_merge_ior (gcov_type *counters, unsigned n_counters)
-{
-  for (; n_counters; counters++, n_counters--)
-    *counters |= gcov_read_counter ();
-}
-#endif
-
-#ifdef L_gcov_merge_single
-/* The profile merging function for choosing the most common value.
-   It is given an array COUNTERS of N_COUNTERS old counters and it
-   reads the same number of counters from the gcov file.  The counters
-   are split into 3-tuples where the members of the tuple have
-   meanings:
-
-   -- the stored candidate on the most common value of the measured entity
-   -- counter
-   -- total number of evaluations of the value  */
-void
-__gcov_merge_single (gcov_type *counters, unsigned n_counters)
-{
-  unsigned i, n_measures;
-  gcov_type value, counter, all;
-
-  gcc_assert (!(n_counters % 3));
-  n_measures = n_counters / 3;
-  for (i = 0; i < n_measures; i++, counters += 3)
-    {
-      value = gcov_read_counter ();
-      counter = gcov_read_counter ();
-      all = gcov_read_counter ();
-
-      if (counters[0] == value)
-	counters[1] += counter;
-      else if (counter > counters[1])
-	{
-	  counters[0] = value;
-	  counters[1] = counter - counters[1];
-	}
-      else
-	counters[1] -= counter;
-      counters[2] += all;
-    }
-}
-#endif /* L_gcov_merge_single */
-
-#ifdef L_gcov_merge_delta
-/* The profile merging function for choosing the most common
-   difference between two consecutive evaluations of the value.  It is
-   given an array COUNTERS of N_COUNTERS old counters and it reads the
-   same number of counters from the gcov file.  The counters are split
-   into 4-tuples where the members of the tuple have meanings:
-
-   -- the last value of the measured entity
-   -- the stored candidate on the most common difference
-   -- counter
-   -- total number of evaluations of the value  */
-void
-__gcov_merge_delta (gcov_type *counters, unsigned n_counters)
-{
-  unsigned i, n_measures;
-  gcov_type value, counter, all;
-
-  gcc_assert (!(n_counters % 4));
-  n_measures = n_counters / 4;
-  for (i = 0; i < n_measures; i++, counters += 4)
-    {
-      /* last = */ gcov_read_counter ();
-      value = gcov_read_counter ();
-      counter = gcov_read_counter ();
-      all = gcov_read_counter ();
-
-      if (counters[1] == value)
-	counters[2] += counter;
-      else if (counter > counters[2])
-	{
-	  counters[1] = value;
-	  counters[2] = counter - counters[2];
-	}
-      else
-	counters[2] -= counter;
-      counters[3] += all;
-    }
-}
-#endif /* L_gcov_merge_delta */
-
-#ifdef L_gcov_interval_profiler
-/* If VALUE is in interval <START, START + STEPS - 1>, then increases the
-   corresponding counter in COUNTERS.  If the VALUE is above or below
-   the interval, COUNTERS[STEPS] or COUNTERS[STEPS + 1] is increased
-   instead.  */
-
-void
-__gcov_interval_profiler (gcov_type *counters, gcov_type value,
-			  int start, unsigned steps)
-{
-  gcov_type delta = value - start;
-  if (delta < 0)
-    counters[steps + 1]++;
-  else if (delta >= steps)
-    counters[steps]++;
-  else
-    counters[delta]++;
-}
-#endif
-
-#ifdef L_gcov_pow2_profiler
-/* If VALUE is a power of two, COUNTERS[1] is incremented.  Otherwise
-   COUNTERS[0] is incremented.  */
-
-void
-__gcov_pow2_profiler (gcov_type *counters, gcov_type value)
-{
-  if (value & (value - 1))
-    counters[0]++;
-  else
-    counters[1]++;
-}
-#endif
-
-/* Tries to determine the most common value among its inputs.  Checks if the
-   value stored in COUNTERS[0] matches VALUE.  If this is the case, COUNTERS[1]
-   is incremented.  If this is not the case and COUNTERS[1] is not zero,
-   COUNTERS[1] is decremented.  Otherwise COUNTERS[1] is set to one and
-   VALUE is stored to COUNTERS[0].  This algorithm guarantees that if this
-   function is called more than 50% of the time with one value, this value
-   will be in COUNTERS[0] in the end.
-
-   In any case, COUNTERS[2] is incremented.  */
-
-static inline void
-__gcov_one_value_profiler_body (gcov_type *counters, gcov_type value)
-{
-  if (value == counters[0])
-    counters[1]++;
-  else if (counters[1] == 0)
-    {
-      counters[1] = 1;
-      counters[0] = value;
-    }
-  else
-    counters[1]--;
-  counters[2]++;
-}
-
-#ifdef L_gcov_one_value_profiler
-void
-__gcov_one_value_profiler (gcov_type *counters, gcov_type value)
-{
-  __gcov_one_value_profiler_body (counters, value);
-}
-#endif
-
-#ifdef L_gcov_indirect_call_profiler
-/* This function exist only for workaround of binutils bug 14342.
-   Once this compatibility hack is obsolette, it can be removed.  */
-
-/* By default, the C++ compiler will use function addresses in the
-   vtable entries.  Setting TARGET_VTABLE_USES_DESCRIPTORS to nonzero
-   tells the compiler to use function descriptors instead.  The value
-   of this macro says how many words wide the descriptor is (normally 2),
-   but it may be dependent on target flags.  Since we do not have access
-   to the target flags here we just check to see if it is set and use
-   that to set VTABLE_USES_DESCRIPTORS to 0 or 1.
-
-   It is assumed that the address of a function descriptor may be treated
-   as a pointer to a function.  */
-
-#ifdef TARGET_VTABLE_USES_DESCRIPTORS
-#define VTABLE_USES_DESCRIPTORS 1
-#else
-#define VTABLE_USES_DESCRIPTORS 0
-#endif
-
-/* Tries to determine the most common value among its inputs. */
-void
-__gcov_indirect_call_profiler (gcov_type* counter, gcov_type value,
-                               void* cur_func, void* callee_func)
-{
-  /* If the C++ virtual tables contain function descriptors then one
-     function may have multiple descriptors and we need to dereference
-     the descriptors to see if they point to the same function.  */
-  if (cur_func == callee_func
-      || (VTABLE_USES_DESCRIPTORS && callee_func
-          && *(void **) cur_func == *(void **) callee_func))
-    __gcov_one_value_profiler_body (counter, value);
-}
-
-#endif
-#ifdef L_gcov_indirect_call_profiler_v2
-
-/* These two variables are used to actually track caller and callee.  Keep
-   them in TLS memory so races are not common (they are written to often).
-   The variables are set directly by GCC instrumented code, so declaration
-   here must match one in tree-profile.c  */
-
-#if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS)
-__thread 
-#endif
-void * __gcov_indirect_call_callee;
-#if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS) 
-__thread 
-#endif
-gcov_type * __gcov_indirect_call_counters;
-
-/* By default, the C++ compiler will use function addresses in the
-   vtable entries.  Setting TARGET_VTABLE_USES_DESCRIPTORS to nonzero
-   tells the compiler to use function descriptors instead.  The value
-   of this macro says how many words wide the descriptor is (normally 2),
-   but it may be dependent on target flags.  Since we do not have access
-   to the target flags here we just check to see if it is set and use
-   that to set VTABLE_USES_DESCRIPTORS to 0 or 1.
-
-   It is assumed that the address of a function descriptor may be treated
-   as a pointer to a function.  */
-
-#ifdef TARGET_VTABLE_USES_DESCRIPTORS
-#define VTABLE_USES_DESCRIPTORS 1
-#else
-#define VTABLE_USES_DESCRIPTORS 0
-#endif
-
-/* Tries to determine the most common value among its inputs. */
-void
-__gcov_indirect_call_profiler_v2 (gcov_type value, void* cur_func)
-{
-  /* If the C++ virtual tables contain function descriptors then one
-     function may have multiple descriptors and we need to dereference
-     the descriptors to see if they point to the same function.  */
-  if (cur_func == __gcov_indirect_call_callee
-      || (VTABLE_USES_DESCRIPTORS && __gcov_indirect_call_callee
-	  && *(void **) cur_func == *(void **) __gcov_indirect_call_callee))
-    __gcov_one_value_profiler_body (__gcov_indirect_call_counters, value);
-}
-#endif
-
-#ifdef L_gcov_average_profiler
-/* Increase corresponding COUNTER by VALUE.  FIXME: Perhaps we want
-   to saturate up.  */
-
-void
-__gcov_average_profiler (gcov_type *counters, gcov_type value)
-{
-  counters[0] += value;
-  counters[1] ++;
-}
-#endif
-
-#ifdef L_gcov_ior_profiler
-/* Bitwise-OR VALUE into COUNTER.  */
-
-void
-__gcov_ior_profiler (gcov_type *counters, gcov_type value)
-{
-  *counters |= value;
-}
-#endif
-
-#ifdef L_gcov_fork
-/* A wrapper for the fork function.  Flushes the accumulated profiling data, so
-   that they are not counted twice.  */
-
-pid_t
-__gcov_fork (void)
-{
-  pid_t pid;
-  extern __gthread_mutex_t __gcov_flush_mx;
-  __gcov_flush ();
-  pid = fork ();
-  if (pid == 0)
-    __GTHREAD_MUTEX_INIT_FUNCTION (&__gcov_flush_mx);
-  return pid;
-}
-#endif
-
-#ifdef L_gcov_execl
-/* A wrapper for the execl function.  Flushes the accumulated profiling data, so
-   that they are not lost.  */
-
-int
-__gcov_execl (const char *path, char *arg, ...)
-{
-  va_list ap, aq;
-  unsigned i, length;
-  char **args;
-
-  __gcov_flush ();
-
-  va_start (ap, arg);
-  va_copy (aq, ap);
-
-  length = 2;
-  while (va_arg (ap, char *))
-    length++;
-  va_end (ap);
-
-  args = (char **) alloca (length * sizeof (void *));
-  args[0] = arg;
-  for (i = 1; i < length; i++)
-    args[i] = va_arg (aq, char *);
-  va_end (aq);
-
-  return execv (path, args);
-}
-#endif
-
-#ifdef L_gcov_execlp
-/* A wrapper for the execlp function.  Flushes the accumulated profiling data, so
-   that they are not lost.  */
-
-int
-__gcov_execlp (const char *path, char *arg, ...)
-{
-  va_list ap, aq;
-  unsigned i, length;
-  char **args;
-
-  __gcov_flush ();
-
-  va_start (ap, arg);
-  va_copy (aq, ap);
-
-  length = 2;
-  while (va_arg (ap, char *))
-    length++;
-  va_end (ap);
-
-  args = (char **) alloca (length * sizeof (void *));
-  args[0] = arg;
-  for (i = 1; i < length; i++)
-    args[i] = va_arg (aq, char *);
-  va_end (aq);
-
-  return execvp (path, args);
-}
-#endif
-
-#ifdef L_gcov_execle
-/* A wrapper for the execle function.  Flushes the accumulated profiling data, so
-   that they are not lost.  */
-
-int
-__gcov_execle (const char *path, char *arg, ...)
-{
-  va_list ap, aq;
-  unsigned i, length;
-  char **args;
-  char **envp;
-
-  __gcov_flush ();
-
-  va_start (ap, arg);
-  va_copy (aq, ap);
-
-  length = 2;
-  while (va_arg (ap, char *))
-    length++;
-  va_end (ap);
-
-  args = (char **) alloca (length * sizeof (void *));
-  args[0] = arg;
-  for (i = 1; i < length; i++)
-    args[i] = va_arg (aq, char *);
-  envp = va_arg (aq, char **);
-  va_end (aq);
-
-  return execve (path, args, envp);
-}
-#endif
-
-#ifdef L_gcov_execv
-/* A wrapper for the execv function.  Flushes the accumulated profiling data, so
-   that they are not lost.  */
-
-int
-__gcov_execv (const char *path, char *const argv[])
-{
-  __gcov_flush ();
-  return execv (path, argv);
-}
-#endif
-
-#ifdef L_gcov_execvp
-/* A wrapper for the execvp function.  Flushes the accumulated profiling data, so
-   that they are not lost.  */
-
-int
-__gcov_execvp (const char *path, char *const argv[])
-{
-  __gcov_flush ();
-  return execvp (path, argv);
-}
-#endif
-
-#ifdef L_gcov_execve
-/* A wrapper for the execve function.  Flushes the accumulated profiling data, so
-   that they are not lost.  */
-
-int
-__gcov_execve (const char *path, char *const argv[], char *const envp[])
-{
-  __gcov_flush ();
-  return execve (path, argv, envp);
-}
-#endif
-#endif /* inhibit_libc */
Index: libgcc/libgcov-profiler.c
===================================================================
--- libgcc/libgcov-profiler.c	(revision 0)
+++ libgcc/libgcov-profiler.c	(revision 0)
@@ -0,0 +1,208 @@
+/* Routines required for instrumenting a program.  */
+/* Compile this one with gcc.  */
+/* Copyright (C) 1989-2013 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+#include "tconfig.h"
+#include "tsystem.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "libgcc_tm.h"
+
+#if !defined(inhibit_libc)
+#define IN_LIBGCOV 1
+#include "gcov-io.h"
+
+#ifdef L_gcov_interval_profiler
+/* If VALUE is in interval <START, START + STEPS - 1>, then increases the
+   corresponding counter in COUNTERS.  If the VALUE is above or below
+   the interval, COUNTERS[STEPS] or COUNTERS[STEPS + 1] is increased
+   instead.  */
+
+void
+__gcov_interval_profiler (gcov_type *counters, gcov_type value,
+                          int start, unsigned steps)
+{
+  gcov_type delta = value - start;
+  if (delta < 0)
+    counters[steps + 1]++;
+  else if (delta >= steps)
+    counters[steps]++;
+  else
+    counters[delta]++;
+}
+#endif
+
+#ifdef L_gcov_pow2_profiler
+/* If VALUE is a power of two, COUNTERS[1] is incremented.  Otherwise
+   COUNTERS[0] is incremented.  */
+
+void
+__gcov_pow2_profiler (gcov_type *counters, gcov_type value)
+{
+  if (value & (value - 1))
+    counters[0]++;
+  else
+    counters[1]++;
+}
+#endif
+
+/* Tries to determine the most common value among its inputs.  Checks if the
+   value stored in COUNTERS[0] matches VALUE.  If this is the case, COUNTERS[1]
+   is incremented.  If this is not the case and COUNTERS[1] is not zero,
+   COUNTERS[1] is decremented.  Otherwise COUNTERS[1] is set to one and
+   VALUE is stored to COUNTERS[0].  This algorithm guarantees that if this
+   function is called more than 50% of the time with one value, this value
+   will be in COUNTERS[0] in the end.
+
+   In any case, COUNTERS[2] is incremented.  */
+
+static inline void
+__gcov_one_value_profiler_body (gcov_type *counters, gcov_type value)
+{
+  if (value == counters[0])
+    counters[1]++;
+  else if (counters[1] == 0)
+    {
+      counters[1] = 1;
+      counters[0] = value;
+    }
+  else
+    counters[1]--;
+  counters[2]++;
+}
+
+#ifdef L_gcov_one_value_profiler
+void
+__gcov_one_value_profiler (gcov_type *counters, gcov_type value)
+{
+  __gcov_one_value_profiler_body (counters, value);
+}
+#endif
+
+#ifdef L_gcov_indirect_call_profiler
+/* This function exist only for workaround of binutils bug 14342.
+   Once this compatibility hack is obsolette, it can be removed.  */
+
+/* By default, the C++ compiler will use function addresses in the
+   vtable entries.  Setting TARGET_VTABLE_USES_DESCRIPTORS to nonzero
+   tells the compiler to use function descriptors instead.  The value
+   of this macro says how many words wide the descriptor is (normally 2),
+   but it may be dependent on target flags.  Since we do not have access
+   to the target flags here we just check to see if it is set and use
+   that to set VTABLE_USES_DESCRIPTORS to 0 or 1.
+
+   It is assumed that the address of a function descriptor may be treated
+   as a pointer to a function.  */
+
+#ifdef TARGET_VTABLE_USES_DESCRIPTORS
+#define VTABLE_USES_DESCRIPTORS 1
+#else
+#define VTABLE_USES_DESCRIPTORS 0
+#endif
+
+/* Tries to determine the most common value among its inputs. */
+void
+__gcov_indirect_call_profiler (gcov_type* counter, gcov_type value,
+                               void* cur_func, void* callee_func)
+{
+  /* If the C++ virtual tables contain function descriptors then one
+     function may have multiple descriptors and we need to dereference
+     the descriptors to see if they point to the same function.  */
+  if (cur_func == callee_func
+      || (VTABLE_USES_DESCRIPTORS && callee_func
+          && *(void **) cur_func == *(void **) callee_func))
+    __gcov_one_value_profiler_body (counter, value);
+}
+
+#endif
+#ifdef L_gcov_indirect_call_profiler_v2
+
+/* These two variables are used to actually track caller and callee.  Keep
+   them in TLS memory so races are not common (they are written to often).
+   The variables are set directly by GCC instrumented code, so declaration
+   here must match one in tree-profile.c  */
+
+#if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS)
+__thread
+#endif
+void * __gcov_indirect_call_callee;
+#if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS)
+__thread
+#endif
+gcov_type * __gcov_indirect_call_counters;
+
+/* By default, the C++ compiler will use function addresses in the
+   vtable entries.  Setting TARGET_VTABLE_USES_DESCRIPTORS to nonzero
+   tells the compiler to use function descriptors instead.  The value
+   of this macro says how many words wide the descriptor is (normally 2),
+   but it may be dependent on target flags.  Since we do not have access
+   to the target flags here we just check to see if it is set and use
+   that to set VTABLE_USES_DESCRIPTORS to 0 or 1.
+
+   It is assumed that the address of a function descriptor may be treated
+   as a pointer to a function.  */
+
+#ifdef TARGET_VTABLE_USES_DESCRIPTORS
+#define VTABLE_USES_DESCRIPTORS 1
+#else
+#define VTABLE_USES_DESCRIPTORS 0
+#endif
+
+/* Tries to determine the most common value among its inputs. */
+void
+__gcov_indirect_call_profiler_v2 (gcov_type value, void* cur_func)
+{
+  /* If the C++ virtual tables contain function descriptors then one
+     function may have multiple descriptors and we need to dereference
+     the descriptors to see if they point to the same function.  */
+  if (cur_func == __gcov_indirect_call_callee
+      || (VTABLE_USES_DESCRIPTORS && __gcov_indirect_call_callee
+          && *(void **) cur_func == *(void **) __gcov_indirect_call_callee))
+    __gcov_one_value_profiler_body (__gcov_indirect_call_counters, value);
+}
+#endif
+
+#ifdef L_gcov_average_profiler
+/* Increase corresponding COUNTER by VALUE.  FIXME: Perhaps we want
+   to saturate up.  */
+
+void
+__gcov_average_profiler (gcov_type *counters, gcov_type value)
+{
+  counters[0] += value;
+  counters[1] ++;
+}
+#endif
+
+#ifdef L_gcov_ior_profiler
+/* Bitwise-OR VALUE into COUNTER.  */
+
+void
+__gcov_ior_profiler (gcov_type *counters, gcov_type value)
+{
+  *counters |= value;
+}
+#endif
+
+#endif /* inhibit_libc */
Index: libgcc/libgcov-driver.c
===================================================================
--- libgcc/libgcov-driver.c	(revision 0)
+++ libgcc/libgcov-driver.c	(revision 0)
@@ -0,0 +1,851 @@
+/* Routines required for instrumenting a program.  */
+/* Compile this one with gcc.  */
+/* Copyright (C) 1989-2013 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef xmalloc
+#define xmalloc malloc 
+#endif
+
+#ifndef PROFILE_TOOL
+#include "tconfig.h"
+#include "tsystem.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "libgcc_tm.h"
+#endif
+
+#if defined(inhibit_libc)
+#define IN_LIBGCOV (-1)
+#else
+#define IN_LIBGCOV 1
+#if defined(L_gcov)
+#define GCOV_LINKAGE /* nothing */
+#endif
+#endif
+#include "gcov-io.h"
+
+#if defined(inhibit_libc)
+/* If libc and its header files are not available, provide dummy functions.  */
+
+#if defined(L_gcov)
+void __gcov_init (struct gcov_info *p __attribute__ ((unused))) {}
+#endif
+
+#else /* inhibit_libc */
+
+#include <string.h>
+#if GCOV_LOCKED
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#endif
+
+extern void gcov_clear (void) ATTRIBUTE_HIDDEN;
+extern void gcov_exit (void) ATTRIBUTE_HIDDEN;
+
+#ifdef L_gcov
+#include "gcov-io.c"
+
+struct gcov_fn_buffer
+{
+  struct gcov_fn_buffer *next;
+  unsigned fn_ix;
+  struct gcov_fn_info info;
+  /* note gcov_fn_info ends in a trailing array.  */
+};
+
+struct gcov_summary_buffer
+{
+  struct gcov_summary_buffer *next;
+  struct gcov_summary summary;
+};
+
+/* Chain of per-object gcov structures.  */
+static struct gcov_info *gcov_list;
+
+/* Set the head of gcov_list.  */
+void
+set_gcov_list (struct gcov_info *head)
+{
+  gcov_list = head;
+}
+
+/* Use this summary checksum rather the computed one if the value is
+   non-zero.  */
+static gcov_unsigned_t saved_summary_checksum;
+
+/* Size of the longest file name. */
+static size_t gcov_max_filename = 0;
+
+/* Flag when the profile has already been dumped via __gcov_dump().  */
+static int gcov_dump_complete;
+
+inline void
+set_gcov_dump_complete (void)
+{
+  gcov_dump_complete = 1;
+}
+
+inline void
+reset_gcov_dump_complete (void)
+{
+  gcov_dump_complete = 0;
+}
+
+/* A utility function for outputing errors.  */
+static int gcov_error (const char *, ...);
+
+static struct gcov_fn_buffer *
+free_fn_data (const struct gcov_info *gi_ptr, struct gcov_fn_buffer *buffer,
+              unsigned limit)
+{
+  struct gcov_fn_buffer *next;
+  unsigned ix, n_ctr = 0;
+
+  if (!buffer)
+    return 0;
+  next = buffer->next;
+
+  for (ix = 0; ix != limit; ix++)
+    if (gi_ptr->merge[ix])
+      free (buffer->info.ctrs[n_ctr++].values);
+  free (buffer);
+  return next;
+}
+
+static struct gcov_fn_buffer **
+buffer_fn_data (const char *filename, const struct gcov_info *gi_ptr,
+                struct gcov_fn_buffer **end_ptr, unsigned fn_ix)
+{
+  unsigned n_ctrs = 0, ix = 0;
+  struct gcov_fn_buffer *fn_buffer;
+  unsigned len;
+
+  for (ix = GCOV_COUNTERS; ix--;)
+    if (gi_ptr->merge[ix])
+      n_ctrs++;
+
+  len = sizeof (*fn_buffer) + sizeof (fn_buffer->info.ctrs[0]) * n_ctrs;
+  fn_buffer = (struct gcov_fn_buffer *)xmalloc (len);
+
+  if (!fn_buffer)
+    goto fail;
+
+  fn_buffer->next = 0;
+  fn_buffer->fn_ix = fn_ix;
+  fn_buffer->info.ident = gcov_read_unsigned ();
+  fn_buffer->info.lineno_checksum = gcov_read_unsigned ();
+  fn_buffer->info.cfg_checksum = gcov_read_unsigned ();
+
+  for (n_ctrs = ix = 0; ix != GCOV_COUNTERS; ix++)
+    {
+      gcov_unsigned_t length;
+      gcov_type *values;
+
+      if (!gi_ptr->merge[ix])
+        continue;
+
+      if (gcov_read_unsigned () != GCOV_TAG_FOR_COUNTER (ix))
+        {
+          len = 0;
+          goto fail;
+        }
+
+      length = GCOV_TAG_COUNTER_NUM (gcov_read_unsigned ());
+      len = length * sizeof (gcov_type);
+      values = (gcov_type *)xmalloc (len);
+      if (!values)
+        goto fail;
+
+      fn_buffer->info.ctrs[n_ctrs].num = length;
+      fn_buffer->info.ctrs[n_ctrs].values = values;
+
+      while (length--)
+        *values++ = gcov_read_counter ();
+      n_ctrs++;
+    }
+
+  *end_ptr = fn_buffer;
+  return &fn_buffer->next;
+
+fail:
+  gcov_error ("profiling:%s:Function %u %s %u \n", filename, fn_ix,
+              len ? "cannot allocate" : "counter mismatch", len ? len : ix);
+
+  return (struct gcov_fn_buffer **)free_fn_data (gi_ptr, fn_buffer, ix);
+}
+
+/* Add an unsigned value to the current crc */
+
+static gcov_unsigned_t
+crc32_unsigned (gcov_unsigned_t crc32, gcov_unsigned_t value)
+{
+  unsigned ix;
+
+  for (ix = 32; ix--; value <<= 1)
+    {
+      unsigned feedback;
+
+      feedback = (value ^ crc32) & 0x80000000 ? 0x04c11db7 : 0;
+      crc32 <<= 1;
+      crc32 ^= feedback;
+    }
+
+  return crc32;
+}
+
+/* Check if VERSION of the info block PTR matches libgcov one.
+   Return 1 on success, or zero in case of versions mismatch.
+   If FILENAME is not NULL, its value used for reporting purposes
+   instead of value from the info block.  */
+
+static int
+gcov_version (struct gcov_info *ptr, gcov_unsigned_t version,
+              const char *filename)
+{
+  if (version != GCOV_VERSION)
+    {
+      char v[4], e[4];
+
+      GCOV_UNSIGNED2STRING (v, version);
+      GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
+
+      gcov_error ("profiling:%s:Version mismatch - expected %.4s got %.4s\n",
+                  filename? filename : ptr->filename, e, v);
+      return 0;
+    }
+  return 1;
+}
+
+/* Insert counter VALUE into HISTOGRAM.  */
+
+static void
+gcov_histogram_insert(gcov_bucket_type *histogram, gcov_type value)
+{
+  unsigned i;
+
+  i = gcov_histo_index(value);
+  histogram[i].num_counters++;
+  histogram[i].cum_value += value;
+  if (value < histogram[i].min_value)
+    histogram[i].min_value = value;
+}
+
+/* Computes a histogram of the arc counters to place in the summary SUM.  */
+
+static void
+gcov_compute_histogram (struct gcov_summary *sum)
+{
+  struct gcov_info *gi_ptr;
+  const struct gcov_fn_info *gfi_ptr;
+  const struct gcov_ctr_info *ci_ptr;
+  struct gcov_ctr_summary *cs_ptr;
+  unsigned t_ix, f_ix, ctr_info_ix, ix;
+  int h_ix;
+
+  /* This currently only applies to arc counters.  */
+  t_ix = GCOV_COUNTER_ARCS;
+
+  /* First check if there are any counts recorded for this counter.  */
+  cs_ptr = &(sum->ctrs[t_ix]);
+  if (!cs_ptr->num)
+    return;
+
+  for (h_ix = 0; h_ix < GCOV_HISTOGRAM_SIZE; h_ix++)
+    {
+      cs_ptr->histogram[h_ix].num_counters = 0;
+      cs_ptr->histogram[h_ix].min_value = cs_ptr->run_max;
+      cs_ptr->histogram[h_ix].cum_value = 0;
+    }
+
+  /* Walk through all the per-object structures and record each of
+     the count values in histogram.  */
+  for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
+    {
+      if (!gi_ptr->merge[t_ix])
+        continue;
+
+      /* Find the appropriate index into the gcov_ctr_info array
+         for the counter we are currently working on based on the
+         existence of the merge function pointer for this object.  */
+      for (ix = 0, ctr_info_ix = 0; ix < t_ix; ix++)
+        {
+          if (gi_ptr->merge[ix])
+            ctr_info_ix++;
+        }
+      for (f_ix = 0; f_ix != gi_ptr->n_functions; f_ix++)
+        {
+          gfi_ptr = gi_ptr->functions[f_ix];
+
+          if (!gfi_ptr || gfi_ptr->key != gi_ptr)
+            continue;
+
+          ci_ptr = &gfi_ptr->ctrs[ctr_info_ix];
+          for (ix = 0; ix < ci_ptr->num; ix++)
+            gcov_histogram_insert (cs_ptr->histogram, ci_ptr->values[ix]);
+        }
+    }
+}
+
+/* summary for program.  */
+static struct gcov_summary this_prg;
+#if !GCOV_LOCKED
+/* summary for all instances of program.  */
+static struct gcov_summary all_prg;
+#endif
+/* crc32 for this program.  */
+static gcov_unsigned_t crc32;
+/* gcda filename.  */
+static char *gi_filename;
+/* buffer for the fn_data from another program.  */
+static struct gcov_fn_buffer *fn_buffer;
+/* buffer for summary from other programs to be written out. */
+static struct gcov_summary_buffer *sum_buffer;
+
+/* This funtions computes the program level summary and the histo-gram.
+   It initializes ALL_PRG, computes CRC32, and stores the summary in
+   THIS_PRG. All these three variables are file statics.  */
+
+static void
+gcov_exit_compute_summary (void)
+{
+  struct gcov_info *gi_ptr;
+  const struct gcov_fn_info *gfi_ptr;
+  struct gcov_ctr_summary *cs_ptr;
+  const struct gcov_ctr_info *ci_ptr;
+  int f_ix;
+  unsigned t_ix;
+  gcov_unsigned_t c_num;
+
+#if !GCOV_LOCKED
+  memset (&all_prg, 0, sizeof (all_prg));
+#endif
+  /* Find the totals for this execution.  */
+  memset (&this_prg, 0, sizeof (this_prg));
+  for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
+    {
+      crc32 = crc32_unsigned (crc32, gi_ptr->stamp);
+      crc32 = crc32_unsigned (crc32, gi_ptr->n_functions);
+
+      for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; f_ix++)
+        {
+          gfi_ptr = gi_ptr->functions[f_ix];
+
+          if (gfi_ptr && gfi_ptr->key != gi_ptr)
+            gfi_ptr = 0;
+
+          crc32 = crc32_unsigned (crc32, gfi_ptr ? gfi_ptr->cfg_checksum : 0);
+          crc32 = crc32_unsigned (crc32,
+                                  gfi_ptr ? gfi_ptr->lineno_checksum : 0);
+          if (!gfi_ptr)
+            continue;
+
+          ci_ptr = gfi_ptr->ctrs;
+          for (t_ix = 0; t_ix != GCOV_COUNTERS_SUMMABLE; t_ix++)
+            {
+              if (!gi_ptr->merge[t_ix])
+                continue;
+
+              cs_ptr = &this_prg.ctrs[t_ix];
+              cs_ptr->num += ci_ptr->num;
+              crc32 = crc32_unsigned (crc32, ci_ptr->num);
+
+              for (c_num = 0; c_num < ci_ptr->num; c_num++)
+                {
+                  cs_ptr->sum_all += ci_ptr->values[c_num];
+                  if (cs_ptr->run_max < ci_ptr->values[c_num])
+                    cs_ptr->run_max = ci_ptr->values[c_num];
+                }
+              ci_ptr++;
+            }
+        }
+    }
+  gcov_compute_histogram (&this_prg);
+}
+
+/* A struct that bundles all the related information about the
+   gcda filename.  */
+struct gcov_filename_aux{
+  char *gi_filename_up;
+  int gcov_prefix_strip;
+  size_t prefix_length;
+};
+
+/* Including system dependent components. */
+#include "libgcov-driver-system.c"
+
+/* This function merges counters in GI_PTR to an existing gcda file.
+   Return 0 on success.
+   Return -1 on error. In this case, caller will goto read_fatal.  */
+
+static int
+gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
+                      struct gcov_summary *prg_p,
+                      gcov_position_t *summary_pos_p,
+                      gcov_position_t *eof_pos_p)
+{
+  gcov_unsigned_t tag, length;
+  unsigned t_ix;
+  int f_ix;
+  int error = 0;
+  struct gcov_fn_buffer **fn_tail = &fn_buffer;
+  struct gcov_summary_buffer **sum_tail = &sum_buffer;
+
+  length = gcov_read_unsigned ();
+  if (!gcov_version (gi_ptr, length, gi_filename))
+    return -1;
+
+  length = gcov_read_unsigned ();
+  if (length != gi_ptr->stamp)
+    /* Read from a different compilation. Overwrite the file.  */
+    return 0;
+
+  /* Look for program summary.  */
+  for (f_ix = 0;;)
+    {
+      struct gcov_summary tmp;
+
+      *eof_pos_p = gcov_position ();
+      tag = gcov_read_unsigned ();
+      if (tag != GCOV_TAG_PROGRAM_SUMMARY)
+        break;
+
+      f_ix--;
+      length = gcov_read_unsigned ();
+      gcov_read_summary (&tmp);
+      if ((error = gcov_is_error ()))
+        goto read_error;
+      if (*summary_pos_p)
+        {
+          /* Save all summaries after the one that will be
+             merged into below. These will need to be rewritten
+             as histogram merging may change the number of non-zero
+             histogram entries that will be emitted, and thus the
+             size of the merged summary.  */
+          (*sum_tail) = (struct gcov_summary_buffer *)
+              xmalloc (sizeof(struct gcov_summary_buffer));
+          (*sum_tail)->summary = tmp;
+          (*sum_tail)->next = 0;
+          sum_tail = &((*sum_tail)->next);
+          goto next_summary;
+        }
+      if (tmp.checksum != crc32)
+        goto next_summary;
+
+      for (t_ix = 0; t_ix != GCOV_COUNTERS_SUMMABLE; t_ix++)
+        if (tmp.ctrs[t_ix].num != this_prg.ctrs[t_ix].num)
+          goto next_summary;
+      *prg_p = tmp;
+      *summary_pos_p = *eof_pos_p;
+
+    next_summary:;
+    }
+
+  /* Merge execution counts for each function.  */
+  for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions;
+       f_ix++, tag = gcov_read_unsigned ())
+    {
+      const struct gcov_ctr_info *ci_ptr;
+      const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix];
+
+      if (tag != GCOV_TAG_FUNCTION)
+        goto read_mismatch;
+
+      length = gcov_read_unsigned ();
+      if (!length)
+        /* This function did not appear in the other program.
+           We have nothing to merge.  */
+        continue;
+
+      if (length != GCOV_TAG_FUNCTION_LENGTH)
+        goto read_mismatch;
+
+      if (!gfi_ptr || gfi_ptr->key != gi_ptr)
+        {
+          /* This function appears in the other program.  We
+             need to buffer the information in order to write
+             it back out -- we'll be inserting data before
+             this point, so cannot simply keep the data in the
+             file.  */
+          fn_tail = buffer_fn_data (gi_filename,
+                                    gi_ptr, fn_tail, f_ix);
+          if (!fn_tail)
+            goto read_mismatch;
+          continue;
+        }
+
+      length = gcov_read_unsigned ();
+      if (length != gfi_ptr->ident)
+        goto read_mismatch;
+
+      length = gcov_read_unsigned ();
+      if (length != gfi_ptr->lineno_checksum)
+        goto read_mismatch;
+
+      length = gcov_read_unsigned ();
+      if (length != gfi_ptr->cfg_checksum)
+        goto read_mismatch;
+
+      ci_ptr = gfi_ptr->ctrs;
+      for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
+        {
+          gcov_merge_fn merge = gi_ptr->merge[t_ix];
+
+          if (!merge)
+            continue;
+
+          tag = gcov_read_unsigned ();
+          length = gcov_read_unsigned ();
+          if (tag != GCOV_TAG_FOR_COUNTER (t_ix)
+              || length != GCOV_TAG_COUNTER_LENGTH (ci_ptr->num))
+            goto read_mismatch;
+          (*merge) (ci_ptr->values, ci_ptr->num, NULL, 1);
+          ci_ptr++;
+        }
+      if ((error = gcov_is_error ()))
+        goto read_error;
+    }
+
+  if (tag)
+    {
+    read_mismatch:;
+      gcov_error ("profiling:%s:Merge mismatch for %s %u\n",
+                  gi_filename, f_ix >= 0 ? "function" : "summary",
+                  f_ix < 0 ? -1 - f_ix : f_ix);
+      return -1;
+    }
+  return 0;
+
+read_error:
+  gcov_error ("profiling:%s:%s merging\n", gi_filename,
+              error < 0 ? "Overflow": "Error");
+  return -1;
+}
+
+/* Write counters in GI_PTR and the summary in PRG to a gcda file. In
+   the case of appending to an existing file, SUMMARY_POS will be non-zero.
+   We will write the file starting from SUMMAY_POS.  */
+
+static void
+gcov_exit_write_gcda (const struct gcov_info *gi_ptr,
+                      const struct gcov_summary *prg_p,
+                      const gcov_position_t summary_pos)
+{
+  unsigned f_ix;
+  struct gcov_summary_buffer *next_sum_buffer;
+
+  /* Write out the data.  */
+  if (!summary_pos)
+    {
+      gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION);
+      gcov_write_unsigned (gi_ptr->stamp);
+    }
+  else
+    gcov_seek (summary_pos);
+
+  /* Generate whole program statistics.  */
+  gcov_write_summary (GCOV_TAG_PROGRAM_SUMMARY, prg_p);
+
+  /* Rewrite all the summaries that were after the summary we merged
+     into. This is necessary as the merged summary may have a different
+     size due to the number of non-zero histogram entries changing after
+     merging.  */
+
+  while (sum_buffer)
+    {
+      gcov_write_summary (GCOV_TAG_PROGRAM_SUMMARY, &sum_buffer->summary);
+      next_sum_buffer = sum_buffer->next;
+      free (sum_buffer);
+      sum_buffer = next_sum_buffer;
+    }
+
+  /* Write execution counts for each function.  */
+  for (f_ix = 0; f_ix != gi_ptr->n_functions; f_ix++)
+    {
+      unsigned buffered = 0;
+      const struct gcov_fn_info *gfi_ptr;
+      const struct gcov_ctr_info *ci_ptr;
+      gcov_unsigned_t length;
+      unsigned t_ix;
+
+      if (fn_buffer && fn_buffer->fn_ix == f_ix)
+        {
+          /* Buffered data from another program.  */
+          buffered = 1;
+          gfi_ptr = &fn_buffer->info;
+          length = GCOV_TAG_FUNCTION_LENGTH;
+        }
+      else
+        {
+          gfi_ptr = gi_ptr->functions[f_ix];
+          if (gfi_ptr && gfi_ptr->key == gi_ptr)
+            length = GCOV_TAG_FUNCTION_LENGTH;
+          else
+                length = 0;
+        }
+
+      gcov_write_tag_length (GCOV_TAG_FUNCTION, length);
+      if (!length)
+        continue;
+
+      gcov_write_unsigned (gfi_ptr->ident);
+      gcov_write_unsigned (gfi_ptr->lineno_checksum);
+      gcov_write_unsigned (gfi_ptr->cfg_checksum);
+
+      ci_ptr = gfi_ptr->ctrs;
+      for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
+        {
+          gcov_unsigned_t n_counts;
+          gcov_type *c_ptr;
+
+          if (!gi_ptr->merge[t_ix])
+            continue;
+
+          n_counts = ci_ptr->num;
+          gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
+                                 GCOV_TAG_COUNTER_LENGTH (n_counts));
+          c_ptr = ci_ptr->values;
+          while (n_counts--)
+            gcov_write_counter (*c_ptr++);
+          ci_ptr++;
+        }
+      if (buffered)
+        fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
+    }
+
+  gcov_write_unsigned (0);
+}
+
+/* Dump the coverage counts for one gcov_info object. We merge with existing
+   counts when possible, to avoid growing the .da files ad infinitum. We use
+   this program's checksum to make sure we only accumulate whole program
+   statistics to the correct summary. An object file might be embedded
+   in two separate programs, and we must keep the two program
+   summaries separate.  */
+
+static void
+gcov_exit_dump_gcov (struct gcov_info *gi_ptr, struct gcov_filename_aux *gf)
+{
+  struct gcov_summary prg; /* summary for this object over all
+                                  program.  */
+  struct gcov_ctr_summary *cs_prg, *cs_tprg;
+#if !GCOV_LOCKED
+  struct gcov_ctr_summary *cs_all;
+#endif
+  int error = 0, ret;
+  gcov_unsigned_t tag;
+  gcov_position_t summary_pos = 0;
+  gcov_position_t eof_pos = 0;
+  unsigned t_ix;
+
+  fn_buffer = 0;
+  sum_buffer = 0;
+
+  ret = gcov_exit_open_gcda_file (gi_ptr, gf);
+  if (ret != 0)
+    return;
+
+  tag = gcov_read_unsigned ();
+  if (tag)
+    {
+      /* Merge data from file.  */
+      if (tag != GCOV_DATA_MAGIC)
+        {
+          gcov_error ("profiling:%s:Not a gcov data file\n", gi_filename);
+          goto read_fatal;
+        }
+      ret = gcov_exit_merge_gcda (gi_ptr, &prg, &summary_pos, &eof_pos);
+      if (ret == -1)
+        goto read_fatal;
+    }
+
+  gcov_rewrite ();
+
+  if (!summary_pos)
+    {
+      memset (&prg, 0, sizeof (prg));
+      summary_pos = eof_pos;
+    }
+
+  /* Merge the summaries.  */
+  for (t_ix = 0; t_ix < GCOV_COUNTERS_SUMMABLE; t_ix++)
+    {
+      cs_prg = &prg.ctrs[t_ix];
+      cs_tprg = &this_prg.ctrs[t_ix];
+
+      if (gi_ptr->merge[t_ix])
+        {
+          if (!cs_prg->runs++)
+            cs_prg->num = cs_tprg->num;
+          cs_prg->sum_all += cs_tprg->sum_all;
+          if (cs_prg->run_max < cs_tprg->run_max)
+            cs_prg->run_max = cs_tprg->run_max;
+          cs_prg->sum_max += cs_tprg->run_max;
+          if (cs_prg->runs == 1)
+            memcpy (cs_prg->histogram, cs_tprg->histogram,
+                   sizeof (gcov_bucket_type) * GCOV_HISTOGRAM_SIZE);
+          else
+            gcov_histogram_merge (cs_prg->histogram, cs_tprg->histogram);
+        }
+      else if (cs_prg->runs)
+        {
+          gcov_error ("profiling:%s:Merge mismatch for summary.\n",
+                      gi_filename);
+          goto read_fatal;
+        }
+
+#if !GCOV_LOCKED
+      cs_all = &all_prg.ctrs[t_ix];
+      if (!cs_all->runs && cs_prg->runs)
+        {
+          cs_all->num = cs_prg->num;
+          cs_all->runs = cs_prg->runs;
+          cs_all->sum_all = cs_prg->sum_all;
+          cs_all->run_max = cs_prg->run_max;
+          cs_all->sum_max = cs_prg->sum_max;
+        }
+      else if (!all_prg.checksum
+               /* Don't compare the histograms, which may have slight
+                  variations depending on the order they were updated
+                  due to the truncating integer divides used in the
+                  merge.  */
+               && (cs_all->num != cs_prg->num
+                   || cs_all->runs != cs_prg->runs
+                   || cs_all->sum_all != cs_prg->sum_all
+                   || cs_all->run_max != cs_prg->run_max
+                   || cs_all->sum_max != cs_prg->sum_max))
+             {
+               gcov_error ("profiling:%s:Data file mismatch - some "
+                           "data files may have been concurrently "
+                           "updated without locking support\n", gi_filename);
+               all_prg.checksum = ~0u;
+             }
+#endif
+    }
+
+  if (saved_summary_checksum)
+    prg.checksum = saved_summary_checksum;
+  else
+    prg.checksum = crc32;
+
+  gcov_exit_write_gcda (gi_ptr, &prg, summary_pos);
+  /* fall through */
+
+read_fatal:;
+  while (fn_buffer)
+    fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
+
+  if ((error = gcov_close ()))
+    gcov_error (error  < 0 ?
+                "profiling:%s:Overflow writing\n" :
+                "profiling:%s:Error writing\n",
+                gi_filename);
+}
+
+
+/* Dump all the coverage counts for the program. It first computes program
+   summary and then traverses gcov_list list and dumps the gcov_info
+   objects one by one.  */
+
+void
+gcov_exit (void)
+{
+  struct gcov_info *gi_ptr;
+  struct gcov_filename_aux gf;
+
+  /* Prevent the counters from being dumped a second time on exit when the
+     application already wrote out the profile using __gcov_dump().  */
+  if (gcov_dump_complete)
+    return;
+
+  gcov_exit_compute_summary ();
+
+  allocate_filename_struct (&gf);
+
+  /* Now merge each file.  */
+  for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
+    gcov_exit_dump_gcov (gi_ptr, &gf);
+
+  if (gi_filename)
+    free (gi_filename);
+}
+
+/* Reset all counters to zero.  */
+
+void
+gcov_clear (void)
+{
+  const struct gcov_info *gi_ptr;
+
+  for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
+    {
+      unsigned f_ix;
+
+      for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
+        {
+          unsigned t_ix;
+          const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix];
+
+          if (!gfi_ptr || gfi_ptr->key != gi_ptr)
+            continue;
+          const struct gcov_ctr_info *ci_ptr = gfi_ptr->ctrs;
+          for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++)
+            {
+              if (!gi_ptr->merge[t_ix])
+                continue;
+
+              memset (ci_ptr->values, 0, sizeof (gcov_type) * ci_ptr->num);
+              ci_ptr++;
+            }
+        }
+    }
+}
+
+/* Add a new object file onto the bb chain.  Invoked automatically
+  when running an object file's global ctors.  */
+
+void
+__gcov_init (struct gcov_info *info)
+{
+  if (!info->version || !info->n_functions)
+    return;
+  if (gcov_version (info, info->version, 0))
+    {
+      size_t filename_length = strlen(info->filename);
+
+      /* Refresh the longest file name information */
+      if (filename_length > gcov_max_filename)
+        gcov_max_filename = filename_length;
+
+      if (!gcov_list)
+        atexit (gcov_exit);
+
+      info->next = gcov_list;
+      gcov_list = info;
+    }
+  info->version = 0;
+}
+
+#endif /* L_gcov */
+#endif /* inhibit_libc */
Index: libgcc/libgcov-merge.c
===================================================================
--- libgcc/libgcov-merge.c	(revision 0)
+++ libgcc/libgcov-merge.c	(revision 0)
@@ -0,0 +1,217 @@
+/* Routines required for instrumenting a program.  */
+/* Compile this one with gcc.  */
+/* Copyright (C) 1989-2013 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef PROFILE_TOOL
+#include "tconfig.h"
+#include "tsystem.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "libgcc_tm.h"
+#endif
+
+#if defined(inhibit_libc)
+#define IN_LIBGCOV (-1)
+#else
+#define IN_LIBGCOV 1
+#endif
+
+#include "gcov-io.h"
+
+#if defined(inhibit_libc)
+/* If libc and its header files are not available, provide dummy functions.  */
+
+#ifdef L_gcov_merge_add
+void __gcov_merge_add (gcov_type *counters  __attribute__ ((unused)),
+                       unsigned n_counters __attribute__ ((unused)),
+                       unsigned gcov_type *src __attribute__ ((unused)),
+                       unsigned w __attribute__ ((unused))) {}
+#endif
+
+#ifdef L_gcov_merge_single
+void __gcov_merge_single (gcov_type *counters  __attribute__ ((unused)),
+                          unsigned n_counters __attribute__ ((unused)),
+                          unsigned gcov_type *src __attribute__ ((unused)),
+                          unsigned w __attribute__ ((unused))) {}
+#endif
+
+#ifdef L_gcov_merge_delta
+void __gcov_merge_delta (gcov_type *counters  __attribute__ ((unused)),
+                         unsigned n_counters __attribute__ ((unused)),
+                         unsigned gcov_type *src __attribute__ ((unused)),
+                         unsigned w __attribute__ ((unused))) {}
+#endif
+
+#else
+
+#ifdef L_gcov_merge_add
+/* The profile merging function that just adds the counters.  It is given
+   an array COUNTERS of N_COUNTERS old counters and it reads the same number
+   of counters from the gcov file.  */
+void
+__gcov_merge_add (gcov_type *counters, unsigned n_counters,
+                  gcov_type *src, unsigned w)
+{
+  int in_mem = (src != 0);
+
+  for (; n_counters; counters++, n_counters--)
+    {
+      gcov_type value;
+
+      if (in_mem)
+        value = *(src++);
+      else
+        value = gcov_read_counter ();
+
+      *counters += w * value;
+    }
+}
+#endif /* L_gcov_merge_add */
+
+#ifdef L_gcov_merge_ior
+/* The profile merging function that just adds the counters.  It is given
+   an array COUNTERS of N_COUNTERS old counters and it reads the same number
+   of counters from the gcov file.  */
+void
+__gcov_merge_ior (gcov_type *counters, unsigned n_counters,
+                  gcov_type *src, unsigned w __attribute__ ((unused)))
+{
+  int in_mem = (src != 0);
+
+  for (; n_counters; counters++, n_counters--)
+    {
+      gcov_type value;
+
+      if (in_mem)
+        value = *(src++);
+      else
+        value = gcov_read_counter ();
+
+      *counters |= value;
+    }
+}
+#endif
+
+#ifdef L_gcov_merge_single
+/* The profile merging function for choosing the most common value.
+   It is given an array COUNTERS of N_COUNTERS old counters and it
+   reads the same number of counters from the gcov file.  The counters
+   are split into 3-tuples where the members of the tuple have
+   meanings:
+
+   -- the stored candidate on the most common value of the measured entity
+   -- counter
+   -- total number of evaluations of the value  */
+void
+__gcov_merge_single (gcov_type *counters, unsigned n_counters,
+                     gcov_type *src, unsigned w)
+{
+  unsigned i, n_measures;
+  gcov_type value, counter, all;
+  int in_mem = (src != 0);
+
+  gcc_assert (!(n_counters % 3));
+  n_measures = n_counters / 3;
+  for (i = 0; i < n_measures; i++, counters += 3, src += 3)
+    {
+      if (in_mem)
+        {
+          value = src[0];
+          counter = src[1];
+          all = src[2];
+        }
+      else
+        {
+          value = gcov_read_counter ();
+          counter = gcov_read_counter ();
+          all = gcov_read_counter ();
+        }
+      counter *= w;
+
+      if (counters[0] == value)
+        counters[1] += counter;
+      else if (counter > counters[1])
+        {
+          counters[0] = value;
+          counters[1] = counter - counters[1];
+        }
+      else
+        counters[1] -= counter;
+      counters[2] += all;
+    }
+}
+#endif /* L_gcov_merge_single */
+
+#ifdef L_gcov_merge_delta
+/* The profile merging function for choosing the most common
+   difference between two consecutive evaluations of the value.  It is
+   given an array COUNTERS of N_COUNTERS old counters and it reads the
+   same number of counters from the gcov file.  The counters are split
+   into 4-tuples where the members of the tuple have meanings:
+
+   -- the last value of the measured entity
+   -- the stored candidate on the most common difference
+   -- counter
+   -- total number of evaluations of the value  */
+void
+__gcov_merge_delta (gcov_type *counters, unsigned n_counters,
+                    gcov_type *src, unsigned w)
+{
+  unsigned i, n_measures;
+  gcov_type value, counter, all;
+  int in_mem = (src != 0);
+
+  gcc_assert (!(n_counters % 4));
+  n_measures = n_counters / 4;
+  for (i = 0; i < n_measures; i++, counters += 4, src += 4)
+    {
+      if (in_mem)
+        {
+          value = src[1];
+          counter = src[2];
+          all = src[3];
+        }
+      else
+        {
+          /* last = */ gcov_read_counter ();
+          value = gcov_read_counter ();
+          counter = gcov_read_counter ();
+          all = gcov_read_counter ();
+        }
+      counter *= w;
+
+      if (counters[1] == value)
+        counters[2] += counter;
+      else if (counter > counters[2])
+        {
+          counters[1] = value;
+          counters[2] = counter - counters[2];
+        }
+      else
+        counters[2] -= counter;
+      counters[3] += all;
+    }
+}
+#endif /* L_gcov_merge_delta */
+#endif /* inhibit_libc */
Index: libgcc/Makefile.in
===================================================================
--- libgcc/Makefile.in	(revision 204285)
+++ libgcc/Makefile.in	(working copy)
@@ -853,17 +853,37 @@ include $(iterator)
 # Build libgcov components.
 
 # Defined in libgcov.c, included only in gcov library
-LIBGCOV = _gcov _gcov_merge_add _gcov_merge_single _gcov_merge_delta \
-    _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
-    _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump \
-    _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \
+##LIBGCOV = _gcov _gcov_merge_add _gcov_merge_single _gcov_merge_delta \
+##    _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
+##    _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump \
+##    _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \
+##    _gcov_indirect_call_profiler _gcov_average_profiler _gcov_ior_profiler \
+##    _gcov_merge_ior _gcov_indirect_call_profiler_v2
+
+LIBGCOV_MERGE = _gcov_merge_add _gcov_merge_single _gcov_merge_delta _gcov_merge_ior
+LIBGCOV_PROFILER = _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \
     _gcov_indirect_call_profiler _gcov_average_profiler _gcov_ior_profiler \
-    _gcov_merge_ior _gcov_indirect_call_profiler_v2
+    _gcov_indirect_call_profiler_v2
+LIBGCOV_INTERFACE = _gcov_flush _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
+    _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump
+LIBGCOV_DRIVER = _gcov 
 
-libgcov-objects = $(patsubst %,%$(objext),$(LIBGCOV))
+libgcov-merge-objects = $(patsubst %,%$(objext),$(LIBGCOV_MERGE))
+libgcov-profiler-objects = $(patsubst %,%$(objext),$(LIBGCOV_PROFILER))
+libgcov-interface-objects = $(patsubst %,%$(objext),$(LIBGCOV_INTERFACE))
+libgcov-driver-objects = $(patsubst %,%$(objext),$(LIBGCOV_DRIVER))
+libgcov-objects = $(libgcov-merge-objects) $(libgcov-profiler-objects) \
+                 $(libgcov-interface-objects) $(libgcov-driver-objects)
 
-$(libgcov-objects): %$(objext): $(srcdir)/libgcov.c
-	$(gcc_compile) -DL$* -c $(srcdir)/libgcov.c
+$(libgcov-merge-objects): %$(objext): $(srcdir)/libgcov-merge.c
+	$(gcc_compile) -DL$* -c $(srcdir)/libgcov-merge.c
+$(libgcov-profiler-objects): %$(objext): $(srcdir)/libgcov-profiler.c
+	$(gcc_compile) -DL$* -c $(srcdir)/libgcov-profiler.c
+$(libgcov-interface-objects): %$(objext): $(srcdir)/libgcov-interface.c
+	$(gcc_compile) -DL$* -c $(srcdir)/libgcov-interface.c
+$(libgcov-driver-objects): %$(objext): $(srcdir)/libgcov-driver.c \
+  $(srcdir)/libgcov-driver-system.c
+	$(gcc_compile) -DL$* -c $(srcdir)/libgcov-driver.c
 
 
 # Static libraries.
Index: gcc/gcov-io.c
===================================================================
--- gcc/gcov-io.c	(revision 204285)
+++ gcc/gcov-io.c	(working copy)
@@ -27,7 +27,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
 /* Routines declared in gcov-io.h.  This file should be #included by
    another source file, after having #included gcov-io.h.  */
 
-#if !IN_GCOV
+#if (!IN_GCOV)
 static void gcov_write_block (unsigned);
 static gcov_unsigned_t *gcov_write_words (unsigned);
 #endif
@@ -36,6 +36,10 @@ static const gcov_unsigned_t *gcov_read_words (uns
 static void gcov_allocate (unsigned);
 #endif
 
+#if IN_LIBGCOV >= 0
+/*GCOV_LINKAGE*/ struct gcov_var gcov_var ATTRIBUTE_HIDDEN;
+#endif
+
 static inline gcov_unsigned_t from_file (gcov_unsigned_t value)
 {
 #if !IN_LIBGCOV
@@ -236,7 +240,9 @@ gcov_write_words (unsigned words)
 {
   gcov_unsigned_t *result;
 
+#ifndef PROFILE_TOOL
   gcc_assert (gcov_var.mode < 0);
+#endif
 #if IN_LIBGCOV
   if (gcov_var.offset >= GCOV_BLOCK_SIZE)
     {
@@ -503,7 +509,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 || PROFILE_TOOL
 GCOV_LINKAGE const char *
 gcov_read_string (void)
 {
@@ -580,7 +586,7 @@ gcov_read_summary (struct gcov_summary *summary)
     }
 }
 
-#if !IN_LIBGCOV
+#if !IN_LIBGCOV || PROFILE_TOOL
 /* Reset to a known position.  BASE should have been obtained from
    gcov_position, LENGTH should be a record length.  */
 
@@ -606,7 +612,9 @@ gcov_sync (gcov_position_t base, gcov_unsigned_t l
 GCOV_LINKAGE void
 gcov_seek (gcov_position_t base)
 {
+#ifndef PROFILE_TOOL
   gcc_assert (gcov_var.mode < 0);
+#endif
   if (gcov_var.offset)
     gcov_write_block (gcov_var.offset);
   fseek (gcov_var.file, base << 2, SEEK_SET);
Index: gcc/gcov-io.h
===================================================================
--- gcc/gcov-io.h	(revision 204285)
+++ gcc/gcov-io.h	(working copy)
@@ -164,7 +164,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
 #ifndef GCC_GCOV_IO_H
 #define GCC_GCOV_IO_H
 
-#if IN_LIBGCOV
+#if (IN_LIBGCOV && !PROFILE_TOOL)
 /* About the target */
 
 #if BITS_PER_UNIT == 8
@@ -214,7 +214,7 @@ typedef unsigned gcov_type_unsigned __attribute__
 typedef unsigned gcov_unsigned_t;
 typedef unsigned gcov_position_t;
 /* gcov_type is typedef'd elsewhere for the compiler */
-#if IN_GCOV
+#if IN_GCOV || PROfILE_TOOL
 #define GCOV_LINKAGE static
 typedef HOST_WIDEST_INT gcov_type;
 typedef unsigned HOST_WIDEST_INT gcov_type_unsigned;
@@ -259,7 +259,9 @@ typedef unsigned HOST_WIDEST_INT gcov_type_unsigne
 
 /* Poison these, so they don't accidentally slip in.  */
 #pragma GCC poison gcov_write_string gcov_write_tag gcov_write_length
+#ifndef PROFILE_TOOL
 #pragma GCC poison gcov_read_string gcov_sync gcov_time gcov_magic
+#endif
 
 #ifdef HAVE_GAS_HIDDEN
 #define ATTRIBUTE_HIDDEN  __attribute__ ((__visibility__ ("hidden")))
@@ -467,7 +469,8 @@ struct gcov_fn_info
 };
 
 /* Type of function used to merge counters.  */
-typedef void (*gcov_merge_fn) (gcov_type *, gcov_unsigned_t);
+typedef void (*gcov_merge_fn) (gcov_type *, gcov_unsigned_t,
+                               gcov_type *, unsigned);
 
 /* Information about a single object file.  */
 struct gcov_info
@@ -482,8 +485,12 @@ struct gcov_info
 					  unused) */
   
   unsigned n_functions;		/* number of functions */
+#ifndef PROFILE_TOOL
   const struct gcov_fn_info *const *functions; /* pointer to pointers
 					          to function information  */
+#else
+  const struct gcov_fn_info **functions;
+#endif
 };
 
 /* Register a new object file module.  */
@@ -499,22 +506,28 @@ extern void __gcov_reset (void);
 extern void __gcov_dump (void);
 
 /* The merge function that just sums the counters.  */
-extern void __gcov_merge_add (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
+extern void __gcov_merge_add (gcov_type *, unsigned, gcov_type *,
+                              unsigned) ATTRIBUTE_HIDDEN;
 
 /* The merge function to choose the most common value.  */
-extern void __gcov_merge_single (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
+extern void __gcov_merge_single (gcov_type *, unsigned, gcov_type *,
+                              unsigned) ATTRIBUTE_HIDDEN;
 
 /* The merge function to choose the most common difference between
    consecutive values.  */
-extern void __gcov_merge_delta (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
+extern void __gcov_merge_delta (gcov_type *, unsigned, gcov_type *,
+                              unsigned) ATTRIBUTE_HIDDEN;
 
 /* The merge function that just ors the counters together.  */
-extern void __gcov_merge_ior (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
+extern void __gcov_merge_ior (gcov_type *, unsigned, gcov_type *,
+                              unsigned) ATTRIBUTE_HIDDEN;
 
 /* The profiler functions.  */
 extern void __gcov_interval_profiler (gcov_type *, gcov_type, int, unsigned);
 extern void __gcov_pow2_profiler (gcov_type *, gcov_type);
 extern void __gcov_one_value_profiler (gcov_type *, gcov_type);
+extern void __gcov_indirect_call_profiler (gcov_type*, gcov_type,
+                                           void*, void*);
 extern void __gcov_indirect_call_profiler_v2 (gcov_type, void *);
 extern void __gcov_average_profiler (gcov_type *, gcov_type);
 extern void __gcov_ior_profiler (gcov_type *, gcov_type);
@@ -538,7 +551,7 @@ extern int __gcov_execve (const char *, char  *con
 /* Optimum number of gcov_unsigned_t's read from or written to disk.  */
 #define GCOV_BLOCK_SIZE (1 << 10)
 
-GCOV_LINKAGE struct gcov_var
+struct gcov_var
 {
   FILE *file;
   gcov_position_t start;	/* Position of first byte of block */
@@ -560,7 +573,8 @@ extern int __gcov_execve (const char *, char  *con
   size_t alloc;
   gcov_unsigned_t *buffer;
 #endif
-} gcov_var ATTRIBUTE_HIDDEN;
+};
+extern struct gcov_var gcov_var ATTRIBUTE_HIDDEN;
 
 /* Functions for reading and writing gcov files. In libgcov you can
    open the file for reading then writing. Elsewhere you can open the
@@ -587,7 +601,7 @@ GCOV_LINKAGE gcov_unsigned_t gcov_read_unsigned (v
 GCOV_LINKAGE gcov_type gcov_read_counter (void) ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE void gcov_read_summary (struct gcov_summary *) ATTRIBUTE_HIDDEN;
 
-#if IN_LIBGCOV
+#if (IN_LIBGCOV)
 /* Available only in libgcov */
 GCOV_LINKAGE void gcov_write_counter (gcov_type) ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE void gcov_write_tag_length (gcov_unsigned_t, gcov_unsigned_t)
@@ -597,7 +611,9 @@ GCOV_LINKAGE void gcov_write_summary (gcov_unsigne
     ATTRIBUTE_HIDDEN;
 static void gcov_rewrite (void);
 GCOV_LINKAGE void gcov_seek (gcov_position_t /*position*/) ATTRIBUTE_HIDDEN;
-#else
+#endif
+
+#if (!IN_LIBGCOV || PROFILE_TOOL)
 /* Available outside libgcov */
 GCOV_LINKAGE const char *gcov_read_string (void);
 GCOV_LINKAGE void gcov_sync (gcov_position_t /*base*/,
Index: gcc/Makefile.in
===================================================================
--- gcc/Makefile.in	(revision 204285)
+++ gcc/Makefile.in	(working copy)
@@ -123,7 +123,8 @@ SUBDIRS =@subdirs@ build
 
 # Selection of languages to be made.
 CONFIG_LANGUAGES = @all_selected_languages@
-LANGUAGES = c gcov$(exeext) gcov-dump$(exeext) $(CONFIG_LANGUAGES)
+LANGUAGES = c gcov$(exeext) gcov-dump$(exeext) profile-tool$(exeext) \
+	    $(CONFIG_LANGUAGES)
 
 # Default values for variables overridden in Makefile fragments.
 # CFLAGS is for the user to override to, e.g., do a cross build with -O2.
@@ -196,6 +197,8 @@ 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
+profile-tool.o-warn = -Wno-error
+libgcov-tool.o-warn = -Wno-error
 
 # All warnings have to be shut off in stage1 if the compiler used then
 # isn't gcc; configure determines that.  WARN_CFLAGS will be either
@@ -1480,7 +1483,7 @@ ALL_HOST_FRONTEND_OBJS = $(foreach v,$(CONFIG_LANG
 ALL_HOST_BACKEND_OBJS = $(GCC_OBJS) $(OBJS) $(OBJS-libcommon) \
   $(OBJS-libcommon-target) @TREEBROWSER@ main.o c-family/cppspec.o \
   $(COLLECT2_OBJS) $(EXTRA_GCC_OBJS) $(GCOV_OBJS) $(GCOV_DUMP_OBJS) \
-  lto-wrapper.o
+  $(PROFILE_TOOL_OBJS) libgcov-tool.o lto-wrapper.o
 
 # This lists all host object files, whether they are included in this
 # compilation or not.
@@ -1505,6 +1508,7 @@ MOSTLYCLEANFILES = insn-flags.h insn-config.h insn
  $(SPECS) collect2$(exeext) gcc-ar$(exeext) gcc-nm$(exeext) \
  gcc-ranlib$(exeext) \
  gcov-iov$(build_exeext) gcov$(exeext) gcov-dump$(exeext) \
+ profile-tool$(exeect) \
  gengtype$(exeext) *.[0-9][0-9].* *.[si] *-checksum.c libbackend.a \
  libcommon-target.a libcommon.a libgcc.mk
 
@@ -2560,6 +2564,16 @@ GCOV_DUMP_OBJS = gcov-dump.o
 gcov-dump$(exeext): $(GCOV_DUMP_OBJS) $(LIBDEPS)
 	+$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_DUMP_OBJS) \
 		$(LIBS) -o $@
+libgcov-tool.o: $(srcdir)/../libgcc/libgcov-tool.c gcov-io.c $(GCOV_IO_H) \
+  $(srcdir)/../libgcc/libgcov-driver.c $(srcdir)/../libgcc/libgcov-driver-system.c \
+  $(srcdir)/../libgcc/libgcov-merge.c \
+  $(SYSTEM_H) coretypes.h $(TM_H) $(CONFIG_H) version.h intl.h $(DIAGNOSTIC_H)
+	+$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -o $@ $<
+
+PROFILE_TOOL_OBJS = profile-tool.o libgcov-tool.o
+profile-tool$(exeext): $(PROFILE_TOOL_OBJS) $(LIBDEPS)
+	+$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(PROFILE_TOOL_OBJS) \
+	  $(LIBS) -o $@
 #\f
 # Build the include directories.  The stamp files are stmp-* rather than
 # s-* so that mostlyclean does not force the include directory to
@@ -3183,6 +3197,13 @@ install-common: native lang.install-common install
 	    rm -f $(DESTDIR)$(bindir)/$(GCOV_INSTALL_NAME)$(exeext); \
 	    $(INSTALL_PROGRAM) gcov$(exeext) $(DESTDIR)$(bindir)/$(GCOV_INSTALL_NAME)$(exeext); \
 	fi
+# Install profile-tool if it was compiled.
+	-if [ -f profile-tool$(exeext) ]; \
+	then \
+	    rm -f $(DESTDIR)$(bindir)/$(PROFILE_TOOL_INSTALL_NAME)$(exeext); \
+	    $(INSTALL_PROGRAM) \
+	    profile-tool$(exeext) $(DESTDIR)$(bindir)/$(PROFILE_TOOL_INSTALL_NAME)$(exeext); \
+	fi
 
 # Install the driver program as $(target_noncanonical)-gcc,
 # $(target_noncanonical)-gcc-$(version), and also as gcc if native.
Index: gcc/profile-tool.c
===================================================================
--- gcc/profile-tool.c	(revision 0)
+++ gcc/profile-tool.c	(revision 0)
@@ -0,0 +1,340 @@
+/* GCC instrumentation plugin for ThreadSanitizer.
+   Copyright (C) 2011-2013 Free Software Foundation, Inc.
+   Contributed by Dmitry Vyukov <dvyukov@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.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#define PROFILE_TOOL 1
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <ftw.h>
+#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"
+
+extern int gcov_profile_merge (struct gcov_info*, struct gcov_info*, int, int);
+extern int gcov_profile_normalize (struct gcov_info*, gcov_type);
+extern int gcov_profile_scale (struct gcov_info*, float);
+extern struct gcov_info* gcov_read_profile_dir (const char*, int);
+extern void gcov_exit (void);
+extern void set_gcov_list (struct gcov_info *);
+extern void gcov_tool_set_verbose (void);
+
+
+static int verbose;
+
+static void print_merge_usage_message (void);
+
+static int
+unlink_file (const char *name,
+             const struct stat *status ATTRIBUTE_UNUSED,
+             int type ATTRIBUTE_UNUSED,
+             struct FTW *ftwbuf ATTRIBUTE_UNUSED)
+{
+  int ret = remove (name);
+
+  if (ret)
+    {
+      fprintf (stderr, "Error in removing %s.\n", name);
+      exit (-1);
+    }
+
+  return ret;
+}
+
+static int
+unlink_dir (const char *path)
+{
+    return nftw(path, unlink_file, 64, FTW_DEPTH | FTW_PHYS);
+}
+
+#define COUNT_MAX_VAL 1000000
+static int
+profile_merge (const char *d1, const char *d2, const char *out, int w1, int w2)
+{
+  char *pwd;
+  int ret;
+  struct gcov_info * d1_profile;
+  struct gcov_info * d2_profile;
+
+
+  d1_profile = gcov_read_profile_dir (d1, 0);
+  if (!d1_profile)
+    return 1;
+
+  if (d2)
+    {
+      d2_profile = gcov_read_profile_dir (d2, 0);
+      if (!d2_profile)
+        return 1;
+
+      /* the actual merge: we overwrite to d1_profile.  */
+      ret = gcov_profile_merge (d1_profile, d2_profile, w1, w2);
+
+      if (ret)
+        return ret;
+    }
+
+  /* output */
+  unlink_dir (out);
+  mkdir (out, 0755);
+  pwd = get_current_dir_name ();
+  gcc_assert (pwd);
+  ret = chdir (out);
+  gcc_assert (ret == 0);
+
+  set_gcov_list (d1_profile);
+  gcov_exit ();
+
+  ret = chdir (pwd);
+  free (pwd);
+  return 0;
+}
+
+static void
+print_merge_usage_message (void)
+{
+  puts ("\n Merge sub-command: merge coverage file contents.");
+  puts ("   merge [options] <dir1> <dir2>");
+  puts ("     -v, --verbose");
+  puts ("     \t  Verbose mode.");
+  puts ("     -o <dir>, --output <dir>");
+  puts ("     \t  Output directory.");
+  puts ("     -w <w1,w2>, --weight <w1,w2>");
+  puts ("     \t  Set weights (float point values).");
+}
+
+static const struct option merge_options[] =
+{
+  { "verbose",                no_argument,       NULL, 'v' },
+  { "output",                 required_argument, NULL, 'o' },
+  { "weight",                 required_argument, NULL, 'w' },
+  { 0, 0, 0, 0 }
+};
+
+static void
+merge_usage (void)
+{
+  puts ("Usage:");
+  print_merge_usage_message ();
+  exit (-1);
+}
+
+static int
+do_merge (int argc, char **argv)
+{
+  int opt;
+  int ret;
+  const char *output_dir = 0;
+  int w1 = 1, w2 = 1;
+
+  while ((opt = getopt_long (argc, argv, "vo:w:", merge_options, NULL)) != -1)
+    {
+      switch (opt)
+        {
+        case 'v':
+          verbose = 1;
+          gcov_tool_set_verbose ();
+          break;
+        case 'o':
+          output_dir = optarg;
+          break;
+        case 'w':
+          sscanf (optarg, "%d,%d", &w1, &w2);
+          if (w1 < 0 || w2 < 0)
+            {
+              fprintf (stderr, "Weights need to be non-negative. \n");
+              exit (-1);
+            }
+          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;
+}
+
+#define COUNT_MAX_VAL 1000000
+static int
+profile_rewrite (const char *d1, const char *out, long long n_val, float scale)
+{
+  char *pwd;
+  int ret;
+  struct gcov_info * d1_profile;
+
+
+  d1_profile = gcov_read_profile_dir (d1, 0);
+  if (!d1_profile)
+    return 1;
+
+  /* output */
+  unlink_dir (out);
+  mkdir (out, 0755);
+  pwd = get_current_dir_name ();
+  gcc_assert (pwd);
+  ret = chdir (out);
+  gcc_assert (ret == 0);
+
+  if (n_val)
+    gcov_profile_normalize (d1_profile, (gcov_type) n_val);
+  else
+    gcov_profile_scale (d1_profile, scale);
+
+  set_gcov_list (d1_profile);
+  gcov_exit ();
+
+  ret = chdir (pwd);
+  free (pwd);
+  return 0;
+}
+
+static void
+print_rewrite_usage_message (void)
+{
+  puts ("\n Rewrite sub-command: rewrite coverage file contents.");
+  puts ("    rewrite [options] <dir>");
+  puts ("      -v, --verbose");
+  puts ("      \t  Verbose mode.");
+  puts ("      -o <dir>, --output <dir>");
+  puts ("      \t  Output directory.");
+  puts ("      -s <float>, --scale <float>");
+  puts ("      \t  Set weights (float point values).");
+  puts ("      -n <long long>, --normalize <long long>");
+  puts ("      \t  Normalize the profile.");
+}
+
+static const struct option rewrite_options[] =
+{
+  { "verbose",                no_argument,       NULL, 'v' },
+  { "output",                 required_argument, NULL, 'o' },
+  { "scale",                  required_argument, NULL, 's' },
+  { "normalize",              required_argument, NULL, 'n' },
+  { 0, 0, 0, 0 }
+};
+
+static void
+rewrite_usage (void)
+{
+  puts ("Usage:");
+  print_merge_usage_message ();
+  exit (-1);
+}
+
+static int
+do_rewrite (int argc, char **argv)
+{
+  int opt;
+  int ret;
+  const char *output_dir = 0;
+  long long normalize_val = 0;
+  float scale = 1.0;
+
+  while ((opt = getopt_long (argc, argv, "vo:s:n:", rewrite_options, NULL)) != -1)
+    {
+      switch (opt)
+        {
+        case 'v':
+          verbose = 1;
+          gcov_tool_set_verbose ();
+          break;
+        case 'o':
+          output_dir = optarg;
+          break;
+        case 'n':
+          if (scale != 1.0)
+            {
+              fprintf (stderr, "Scaling cannot co-exist with normalization.\n");
+              scale = 1.0;
+            }
+          normalize_val = atoll (optarg);
+          break;
+        case 's':
+          sscanf (optarg, "%f", &scale);
+          if (scale < 0.0)
+            {
+              fprintf (stderr, "Scale needs to be non-negative. \n");
+              exit (-1);
+            }
+          if (normalize_val != 0)
+            {
+              fprintf (stderr, "Normalization cannot co-exist with scaling.\n");
+              normalize_val = 0;
+            }
+          break;
+        default:
+          rewrite_usage ();
+        }
+    }
+
+  if (output_dir == NULL)
+    output_dir = "rewrite_profile";
+
+  if (argc - optind == 1)
+    ret = profile_rewrite (argv[optind],  output_dir, normalize_val, scale);
+  else
+    rewrite_usage ();
+
+  return ret;
+}
+static void
+print_usage (const char* cmd)
+{
+  if (cmd)
+    printf ("Usage: %s <sub_command> [options]\n", cmd);
+  print_merge_usage_message ();
+  print_rewrite_usage_message ();
+}
+
+int
+main (int argc, char **argv)
+{
+  int ret = -1;
+  const char *sub_command;
+
+  if (argc < 2)
+    {
+      print_usage (argv[0]);
+      return ret;
+    }
+
+  sub_command = argv[1];
+
+  if (!strcmp (sub_command, "merge"))
+    ret = do_merge (argc - 1, argv + 1);
+  else if (!strcmp (sub_command, "rewrite"))
+    ret = do_rewrite (argc - 1, argv + 1);
+  else
+    print_usage (argv[0]);
+
+  return ret;
+}
+

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-06 21:54               ` Rong Xu
@ 2013-11-06 23:33                 ` Xinliang David Li
  2013-11-07  1:09                   ` Rong Xu
  2013-11-11 16:22                 ` Jan Hubicka
  1 sibling, 1 reply; 55+ messages in thread
From: Xinliang David Li @ 2013-11-06 23:33 UTC (permalink / raw)
  To: Rong Xu; +Cc: Jakub Jelinek, Jan Hubicka, GCC Patches

I have not looked this in detail, but it looks like the copy right
header of the new file is wrong (including the year).

You should 1) document the command line syntax somewhere, possibly in
comment?  2) add a help command for the tool to dump out help message.

David

On Wed, Nov 6, 2013 at 1:25 PM, Rong Xu <xur@google.com> wrote:
> Here is the patch that includes profile-tool.
> Profile-tool now has two functions: merge and rewrite. I'll add diff later.
>
> Compiler is tested with spec2006 and profiledbootstrap.
> profile-tool is tested with spec2006 profiles.
>
> -Rong
>
> On Tue, Nov 5, 2013 at 8:58 AM, Xinliang David Li <davidxl@google.com> wrote:
>> Those functions are not referencing each other, but they get used in
>> different logical groups, so at least control granularity can be
>> improved. For instance,
>> 1) all gcov user interfaces such as gcov_dump, gcov_flush, gcov_reset
>> are either not used at all, or all of them are used -- there is no
>> need to split this group. After Rong's refactoring (move them into a
>> separate file), the guarding macro can be removed for them
>> 2) _gcov_merge_add is used by 4 different profilers, so it is almost
>> always linked in
>>
>> It is unclear how other value profilers are used on other targets. For
>> x86, they are on by default, so the profiler bodies and merge
>> functions are also always linked in.
>>
>> David
>>
>> On Tue, Nov 5, 2013 at 1:23 AM, Jakub Jelinek <jakub@redhat.com> wrote:
>>> On Tue, Nov 05, 2013 at 10:18:50AM +0100, Jan Hubicka wrote:
>>>> > I wonder if it makes sense to get rid of the conditional compile such as
>>>> >
>>>> > #ifdef L_gcov_xxxxx
>>>> > ..
>>>> >
>>>> > #endif
>>>> >
>>>> > This has the advantage of producing slightly smaller instrumented
>>>> > binary, but this benefit can also be achieved via -ffunction-sections
>>>> > and let linker to garbage collect unused functions.
>>>> >
>>>> > (probably as a follow up if it makes sense).
>>>>
>>>> I believe we use this funny scheme primarily for targets that have no function
>>>> section support (and also the hand made splitting can be more sane).
>>>> For a low-level library like libgcov is that is supposed to have small footprint,
>>>> I guess the current scheme is resonable thing to do....
>>>
>>> I think the #ifdef L* stuff is there mainly so that we have small
>>> granularity in the *.a libraries (one function per object file), so that one
>>> links only what is really required (at least, that is why libgcc.a does
>>> that).  Of course, if there are many interdependencies between the functions
>>> and you always essentially link in everything, the usefulness of that is
>>> lower.
>>>
>>>         Jakub

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-06 23:33                 ` Xinliang David Li
@ 2013-11-07  1:09                   ` Rong Xu
  2013-11-07  2:49                     ` Joseph S. Myers
  0 siblings, 1 reply; 55+ messages in thread
From: Rong Xu @ 2013-11-07  1:09 UTC (permalink / raw)
  To: Xinliang David Li; +Cc: Jakub Jelinek, Jan Hubicka, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 3883 bytes --]

On Wed, Nov 6, 2013 at 3:11 PM, Xinliang David Li <davidxl@google.com> wrote:
> I have not looked this in detail, but it looks like the copy right
> header of the new file is wrong (including the year).

Thanks for spotting this. I've fixed in the attached new patch.

>
> You should 1) document the command line syntax somewhere, possibly in
> comment?  2) add a help command for the tool to dump out help message.
>

In current implementation, if you use 'profile-tool help', it will
print out the usage and exit.

Here is the usage:
-----------------------------------------------------
Usage: gcc/profile-tool <sub_command> [options]

 Merge sub-command: merge coverage file contents.
   merge [options] <dir1> <dir2>
     -v, --verbose
       Verbose mode.
     -o <dir>, --output <dir>
       Output directory.
     -w <w1,w2>, --weight <w1,w2>
       Set weights (float point values).

 Rewrite sub-command: rewrite coverage file contents.
    rewrite [options] <dir>
      -v, --verbose
       Verbose mode.
      -o <dir>, --output <dir>
       Output directory.
      -s <float>, --scale <float>
       Scale the profile counters.
      -n <long long>, --normalize <long long>
       Normalize the profile.
-----------------------------------------------

I break the last patch into two patches.
The first is the re-factoring the libgcov.c, and the second one is for
profile-tool.

-Rong


> David
>
> On Wed, Nov 6, 2013 at 1:25 PM, Rong Xu <xur@google.com> wrote:
>> Here is the patch that includes profile-tool.
>> Profile-tool now has two functions: merge and rewrite. I'll add diff later.
>>
>> Compiler is tested with spec2006 and profiledbootstrap.
>> profile-tool is tested with spec2006 profiles.
>>
>> -Rong
>>
>> On Tue, Nov 5, 2013 at 8:58 AM, Xinliang David Li <davidxl@google.com> wrote:
>>> Those functions are not referencing each other, but they get used in
>>> different logical groups, so at least control granularity can be
>>> improved. For instance,
>>> 1) all gcov user interfaces such as gcov_dump, gcov_flush, gcov_reset
>>> are either not used at all, or all of them are used -- there is no
>>> need to split this group. After Rong's refactoring (move them into a
>>> separate file), the guarding macro can be removed for them
>>> 2) _gcov_merge_add is used by 4 different profilers, so it is almost
>>> always linked in
>>>
>>> It is unclear how other value profilers are used on other targets. For
>>> x86, they are on by default, so the profiler bodies and merge
>>> functions are also always linked in.
>>>
>>> David
>>>
>>> On Tue, Nov 5, 2013 at 1:23 AM, Jakub Jelinek <jakub@redhat.com> wrote:
>>>> On Tue, Nov 05, 2013 at 10:18:50AM +0100, Jan Hubicka wrote:
>>>>> > I wonder if it makes sense to get rid of the conditional compile such as
>>>>> >
>>>>> > #ifdef L_gcov_xxxxx
>>>>> > ..
>>>>> >
>>>>> > #endif
>>>>> >
>>>>> > This has the advantage of producing slightly smaller instrumented
>>>>> > binary, but this benefit can also be achieved via -ffunction-sections
>>>>> > and let linker to garbage collect unused functions.
>>>>> >
>>>>> > (probably as a follow up if it makes sense).
>>>>>
>>>>> I believe we use this funny scheme primarily for targets that have no function
>>>>> section support (and also the hand made splitting can be more sane).
>>>>> For a low-level library like libgcov is that is supposed to have small footprint,
>>>>> I guess the current scheme is resonable thing to do....
>>>>
>>>> I think the #ifdef L* stuff is there mainly so that we have small
>>>> granularity in the *.a libraries (one function per object file), so that one
>>>> links only what is really required (at least, that is why libgcc.a does
>>>> that).  Of course, if there are many interdependencies between the functions
>>>> and you always essentially link in everything, the usefulness of that is
>>>> lower.
>>>>
>>>>         Jakub

[-- Attachment #2: libgcov_patch2.txt --]
[-- Type: text/plain, Size: 86868 bytes --]

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

[-- Attachment #3: profile_tool_patch2.txt --]
[-- Type: text/plain, Size: 40882 bytes --]

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

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-07  1:09                   ` Rong Xu
@ 2013-11-07  2:49                     ` Joseph S. Myers
  2013-11-07 17:40                       ` Rong Xu
  0 siblings, 1 reply; 55+ messages in thread
From: Joseph S. Myers @ 2013-11-07  2:49 UTC (permalink / raw)
  To: Rong Xu; +Cc: Xinliang David Li, Jakub Jelinek, Jan Hubicka, GCC Patches

On Wed, 6 Nov 2013, Rong Xu wrote:

> In current implementation, if you use 'profile-tool help', it will
> print out the usage and exit.

Please make sure you follow the GNU Coding Standards.  Any installed tool 
needs to support --help, with output following the GNU Coding Standards, 
to standard output, with exit status 0 and with the output including the 
configured bug-reporting URL.  Any installed tool also needs to support 
--version, reporting the version number (with any configured package 
version) in the same format as other GCC tools, with copyright / licensing 
information.

This is in addition to documenting the tool in the Texinfo manuals.

You should also use the common GCC diagnostic functions rather than 
fprintf to stderr (and in any case make sure diagnostics follow the GNU 
Coding Standards for formatting - do not start with a capital letter or 
end with ".").

All English messages need to be appropriately marked for translation.  
Look at the initialization in gcov.c including

  gcc_init_libintl ();

  diagnostic_initialize (global_dc, 0);

and if you use standard diagnostic functions then translation is handled 
automatically; for random usage messages, fnotice may be helpful to avoid 
needing explicit _() markings.

It is always wrong to include system headers before config.h, as config.h 
may define feature test macros that only work if defined before any system 
header is included.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-07  2:49                     ` Joseph S. Myers
@ 2013-11-07 17:40                       ` Rong Xu
  2013-11-07 17:48                         ` Joseph S. Myers
  0 siblings, 1 reply; 55+ messages in thread
From: Rong Xu @ 2013-11-07 17:40 UTC (permalink / raw)
  To: Joseph S. Myers
  Cc: Xinliang David Li, Jakub Jelinek, Jan Hubicka, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 1919 bytes --]

Thanks Joseph for these detailed comments/suggestions.
The fixed patch is attached to this email.
The only thing left out is the Texinfo manual. Do you mean this tool
should have its it's own texi file in gcc/doc?
I prefer to do the documentation later as I'm sure the final version
will from current one.

-Rong

On Wed, Nov 6, 2013 at 5:17 PM, Joseph S. Myers <joseph@codesourcery.com> wrote:
> On Wed, 6 Nov 2013, Rong Xu wrote:
>
>> In current implementation, if you use 'profile-tool help', it will
>> print out the usage and exit.
>
> Please make sure you follow the GNU Coding Standards.  Any installed tool
> needs to support --help, with output following the GNU Coding Standards,
> to standard output, with exit status 0 and with the output including the
> configured bug-reporting URL.  Any installed tool also needs to support
> --version, reporting the version number (with any configured package
> version) in the same format as other GCC tools, with copyright / licensing
> information.
>
> This is in addition to documenting the tool in the Texinfo manuals.
>
> You should also use the common GCC diagnostic functions rather than
> fprintf to stderr (and in any case make sure diagnostics follow the GNU
> Coding Standards for formatting - do not start with a capital letter or
> end with ".").
>
> All English messages need to be appropriately marked for translation.
> Look at the initialization in gcov.c including
>
>   gcc_init_libintl ();
>
>   diagnostic_initialize (global_dc, 0);
>
> and if you use standard diagnostic functions then translation is handled
> automatically; for random usage messages, fnotice may be helpful to avoid
> needing explicit _() markings.
>
> It is always wrong to include system headers before config.h, as config.h
> may define feature test macros that only work if defined before any system
> header is included.
>
> --
> Joseph S. Myers
> joseph@codesourcery.com

[-- Attachment #2: profile_tool_patch3.txt --]
[-- Type: text/plain, Size: 43101 bytes --]

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

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-07 17:40                       ` Rong Xu
@ 2013-11-07 17:48                         ` Joseph S. Myers
  2013-11-08  1:13                           ` Rong Xu
  0 siblings, 1 reply; 55+ messages in thread
From: Joseph S. Myers @ 2013-11-07 17:48 UTC (permalink / raw)
  To: Rong Xu; +Cc: Xinliang David Li, Jakub Jelinek, Jan Hubicka, GCC Patches

On Thu, 7 Nov 2013, Rong Xu wrote:

> Thanks Joseph for these detailed comments/suggestions.
> The fixed patch is attached to this email.
> The only thing left out is the Texinfo manual. Do you mean this tool
> should have its it's own texi file in gcc/doc?

Its own texi file, probably included as a chapter by gcc.texi (like 
gcov.texi is).

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-07 17:48                         ` Joseph S. Myers
@ 2013-11-08  1:13                           ` Rong Xu
  2013-11-08 18:45                             ` Xinliang David Li
  0 siblings, 1 reply; 55+ messages in thread
From: Rong Xu @ 2013-11-08  1:13 UTC (permalink / raw)
  To: Joseph S. Myers
  Cc: Xinliang David Li, Jakub Jelinek, Jan Hubicka, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 704 bytes --]

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

OK. will add this in.

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

Also did some minor adjust of the code in libgcov.

Thanks,

-Rong


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

[-- Attachment #2: libgcov_patch4.txt --]
[-- Type: text/plain, Size: 93819 bytes --]

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

[-- Attachment #3: profile_tool_patch4.txt --]
[-- Type: text/plain, Size: 43253 bytes --]

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

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-08  1:13                           ` Rong Xu
@ 2013-11-08 18:45                             ` Xinliang David Li
  2013-11-08 19:01                               ` Xinliang David Li
  2013-11-08 19:10                               ` Rong Xu
  0 siblings, 2 replies; 55+ messages in thread
From: Xinliang David Li @ 2013-11-08 18:45 UTC (permalink / raw)
  To: Rong Xu; +Cc: Joseph S. Myers, Jakub Jelinek, Jan Hubicka, GCC Patches

in libgcov-driver.c

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

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

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


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

in libgcov-profiler.c, line 142

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

trailing whilte space.


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

trailing white space.


in libgcov-merge.c

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

David






On Thu, Nov 7, 2013 at 3:34 PM, Rong Xu <xur@google.com> wrote:
> On Thu, Nov 7, 2013 at 9:40 AM, Joseph S. Myers <joseph@codesourcery.com> wrote:
>> On Thu, 7 Nov 2013, Rong Xu wrote:
>>
>>> Thanks Joseph for these detailed comments/suggestions.
>>> The fixed patch is attached to this email.
>>> The only thing left out is the Texinfo manual. Do you mean this tool
>>> should have its it's own texi file in gcc/doc?
>>
>> Its own texi file, probably included as a chapter by gcc.texi (like
>> gcov.texi is).
>
> OK. will add this in.
>
> My last patch that changes the command handling is actually broken.
> Please ignore that patch and use the one in this email.
>
> Also did some minor adjust of the code in libgcov.
>
> Thanks,
>
> -Rong
>
>
>>
>> --
>> Joseph S. Myers
>> joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-08 18:45                             ` Xinliang David Li
@ 2013-11-08 19:01                               ` Xinliang David Li
  2013-11-08 19:10                               ` Rong Xu
  1 sibling, 0 replies; 55+ messages in thread
From: Xinliang David Li @ 2013-11-08 19:01 UTC (permalink / raw)
  To: Rong Xu; +Cc: Joseph S. Myers, Jakub Jelinek, Jan Hubicka, GCC Patches

> 1) I don't think you need in_mem flag. For in memory merge, the source != NULL.

discard this comment.

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

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-08 18:45                             ` Xinliang David Li
  2013-11-08 19:01                               ` Xinliang David Li
@ 2013-11-08 19:10                               ` Rong Xu
  2013-11-08 19:13                                 ` Rong Xu
  2013-11-08 19:30                                 ` Xinliang David Li
  1 sibling, 2 replies; 55+ messages in thread
From: Rong Xu @ 2013-11-08 19:10 UTC (permalink / raw)
  To: Xinliang David Li
  Cc: Joseph S. Myers, Jakub Jelinek, Jan Hubicka, GCC Patches

On Fri, Nov 8, 2013 at 10:22 AM, Xinliang David Li <davidxl@google.com> wrote:
> in libgcov-driver.c
>
>> /* Flag when the profile has already been dumped via __gcov_dump().  */
>> static int gcov_dump_complete;
>
>> inline void
>> set_gcov_dump_complete (void)
>>{
>   > gcov_dump_complete = 1;
>>}
>
>>inline void
>>reset_gcov_dump_complete (void)
>>{
>>  gcov_dump_complete = 0;
>>}
>
>
> These should be moved to libgcov-interface.c. Is there reason to not
> mark them as static?

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

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

Will fix.

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

Will fix.

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

Will do.

> 3) How do you deal with integer overflow ?

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

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

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-08 19:10                               ` Rong Xu
@ 2013-11-08 19:13                                 ` Rong Xu
  2013-11-08 19:39                                   ` Xinliang David Li
  2013-11-08 21:46                                   ` Joseph S. Myers
  2013-11-08 19:30                                 ` Xinliang David Li
  1 sibling, 2 replies; 55+ messages in thread
From: Rong Xu @ 2013-11-08 19:13 UTC (permalink / raw)
  To: Xinliang David Li
  Cc: Joseph S. Myers, Jakub Jelinek, Jan Hubicka, GCC Patches

A question about inhibit_libc.
When inhibit_libc is defined, we provide dummy functions for all the
__gcov_* methods.
Is it purposely to minimize the footprint?
I'm thinking to allow some codes that are independent of libc (and its
headers) under this. Is it OK?

-Rong

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

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-08 19:10                               ` Rong Xu
  2013-11-08 19:13                                 ` Rong Xu
@ 2013-11-08 19:30                                 ` Xinliang David Li
  2013-11-08 19:42                                   ` Rong Xu
  1 sibling, 1 reply; 55+ messages in thread
From: Xinliang David Li @ 2013-11-08 19:30 UTC (permalink / raw)
  To: Rong Xu; +Cc: Joseph S. Myers, Jakub Jelinek, Jan Hubicka, GCC Patches

On Fri, Nov 8, 2013 at 10:48 AM, Rong Xu <xur@google.com> wrote:
> On Fri, Nov 8, 2013 at 10:22 AM, Xinliang David Li <davidxl@google.com> wrote:
>> in libgcov-driver.c
>>
>>> /* Flag when the profile has already been dumped via __gcov_dump().  */
>>> static int gcov_dump_complete;
>>
>>> inline void
>>> set_gcov_dump_complete (void)
>>>{
>>   > gcov_dump_complete = 1;
>>>}
>>
>>>inline void
>>>reset_gcov_dump_complete (void)
>>>{
>>>  gcov_dump_complete = 0;
>>>}
>>
>>
>> These should be moved to libgcov-interface.c. Is there reason to not
>> mark them as static?
>
> gcov_dump_complete is used in gcov_exit() which is in
> libgcov-driver.c, but it's set in __gcov_dump and __gcov_reset, both
> in libgcov-interface.c.
> I want gcov_dump_complete a static. So I have to add to global
> functions that accessible from libgcov-interface.c.
> Another choice is to move __gcov_dump and __gcov_reset to
> libgcov-driver.c and that will simplify the code.
>

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

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

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

David



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

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-08 19:13                                 ` Rong Xu
@ 2013-11-08 19:39                                   ` Xinliang David Li
  2013-11-08 21:46                                   ` Joseph S. Myers
  1 sibling, 0 replies; 55+ messages in thread
From: Xinliang David Li @ 2013-11-08 19:39 UTC (permalink / raw)
  To: Rong Xu; +Cc: Joseph S. Myers, Jakub Jelinek, Jan Hubicka, GCC Patches

On Fri, Nov 8, 2013 at 10:56 AM, Rong Xu <xur@google.com> wrote:
> A question about inhibit_libc.
> When inhibit_libc is defined, we provide dummy functions for all the
> __gcov_* methods.
> Is it purposely to minimize the footprint?
> I'm thinking to allow some codes that are independent of libc (and its
> headers) under this. Is it OK?
>


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

David


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

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-08 19:30                                 ` Xinliang David Li
@ 2013-11-08 19:42                                   ` Rong Xu
  2013-11-08 20:06                                     ` Xinliang David Li
  0 siblings, 1 reply; 55+ messages in thread
From: Rong Xu @ 2013-11-08 19:42 UTC (permalink / raw)
  To: Xinliang David Li
  Cc: Joseph S. Myers, Jakub Jelinek, Jan Hubicka, GCC Patches

On Fri, Nov 8, 2013 at 11:02 AM, Xinliang David Li <davidxl@google.com> wrote:
> On Fri, Nov 8, 2013 at 10:48 AM, Rong Xu <xur@google.com> wrote:
>> On Fri, Nov 8, 2013 at 10:22 AM, Xinliang David Li <davidxl@google.com> wrote:
>>> in libgcov-driver.c
>>>
>>>> /* Flag when the profile has already been dumped via __gcov_dump().  */
>>>> static int gcov_dump_complete;
>>>
>>>> inline void
>>>> set_gcov_dump_complete (void)
>>>>{
>>>   > gcov_dump_complete = 1;
>>>>}
>>>
>>>>inline void
>>>>reset_gcov_dump_complete (void)
>>>>{
>>>>  gcov_dump_complete = 0;
>>>>}
>>>
>>>
>>> These should be moved to libgcov-interface.c. Is there reason to not
>>> mark them as static?
>>
>> gcov_dump_complete is used in gcov_exit() which is in
>> libgcov-driver.c, but it's set in __gcov_dump and __gcov_reset, both
>> in libgcov-interface.c.
>> I want gcov_dump_complete a static. So I have to add to global
>> functions that accessible from libgcov-interface.c.
>> Another choice is to move __gcov_dump and __gcov_reset to
>> libgcov-driver.c and that will simplify the code.
>>
>
> Ok  then you should remove the 'inline' keyword for the set and reset
> functions and keep them in libgcov-driver.c.

Will remove 'inline' keyword.

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

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

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

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

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

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


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

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-08 19:42                                   ` Rong Xu
@ 2013-11-08 20:06                                     ` Xinliang David Li
  2013-11-08 20:31                                       ` Rong Xu
  0 siblings, 1 reply; 55+ messages in thread
From: Xinliang David Li @ 2013-11-08 20:06 UTC (permalink / raw)
  To: Rong Xu; +Cc: Joseph S. Myers, Jakub Jelinek, Jan Hubicka, GCC Patches

On Fri, Nov 8, 2013 at 11:21 AM, Rong Xu <xur@google.com> wrote:
> On Fri, Nov 8, 2013 at 11:02 AM, Xinliang David Li <davidxl@google.com> wrote:
>> On Fri, Nov 8, 2013 at 10:48 AM, Rong Xu <xur@google.com> wrote:
>>> On Fri, Nov 8, 2013 at 10:22 AM, Xinliang David Li <davidxl@google.com> wrote:
>>>> in libgcov-driver.c
>>>>
>>>>> /* Flag when the profile has already been dumped via __gcov_dump().  */
>>>>> static int gcov_dump_complete;
>>>>
>>>>> inline void
>>>>> set_gcov_dump_complete (void)
>>>>>{
>>>>   > gcov_dump_complete = 1;
>>>>>}
>>>>
>>>>>inline void
>>>>>reset_gcov_dump_complete (void)
>>>>>{
>>>>>  gcov_dump_complete = 0;
>>>>>}
>>>>
>>>>
>>>> These should be moved to libgcov-interface.c. Is there reason to not
>>>> mark them as static?
>>>
>>> gcov_dump_complete is used in gcov_exit() which is in
>>> libgcov-driver.c, but it's set in __gcov_dump and __gcov_reset, both
>>> in libgcov-interface.c.
>>> I want gcov_dump_complete a static. So I have to add to global
>>> functions that accessible from libgcov-interface.c.
>>> Another choice is to move __gcov_dump and __gcov_reset to
>>> libgcov-driver.c and that will simplify the code.
>>>
>>
>> Ok  then you should remove the 'inline' keyword for the set and reset
>> functions and keep them in libgcov-driver.c.
>
> Will remove 'inline' keyword.
>
>>
>>>>
>>>> in libgcov-profiler.c, line 142
>>>>
>>>>> #if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS)
>>>>> __thread
>>>>
>>>> trailing whilte space.
>>>>
>>>
>>> Will fix.
>>>
>>>>
>>>>>     || (VTABLE_USES_DESCRIPTORS && __gcov_indirect_call_callee
>>>>>         && *(void **) cur_func == *(void **) __gcov_indirect_call_callee))
>>>>
>>>> trailing white space.
>>>>
>>>
>>> Will fix.
>>>
>>>>
>>>> in libgcov-merge.c
>>>>
>>>> 1) I don't think you need in_mem flag. For in memory merge, the source != NULL.
>>>> 2) document the new source and weight parameter -- especially the weight.
>>>
>>> Will do.
>>>
>>>> 3) How do you deal with integer overflow ?
>>>
>>> This is good question. gcvo_type is (typically) long long. I thought
>>> the count value will not be nearly close to the max of long long.
>>> (We did see overflow in compiler, but that's because of the scaling --
>>> some of the scaling factors are really large.)
>>>
>>
>> Why not passing weight as a scaled PERCENT  (as branch prob) for the
>> source? THis way, the merge tool does not need to do scaling twice.
>>
>
> there are two weights, so unless you have two weights in the merge_add
> functions, you have to
> traverse the list twice. Do you mean pass addition parameters?
>
> Currently, merge tools is done this way:
> merge (p1, p2, w1, w2)
> first pass: merge_add (p1, p1, w1)
> second pass: merge_add (p1, p2, w2)
>

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


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

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

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

thanks,

David

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

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-08 20:06                                     ` Xinliang David Li
@ 2013-11-08 20:31                                       ` Rong Xu
  0 siblings, 0 replies; 55+ messages in thread
From: Rong Xu @ 2013-11-08 20:31 UTC (permalink / raw)
  To: Xinliang David Li
  Cc: Joseph S. Myers, Jakub Jelinek, Jan Hubicka, GCC Patches

On Fri, Nov 8, 2013 at 11:30 AM, Xinliang David Li <davidxl@google.com> wrote:
> On Fri, Nov 8, 2013 at 11:21 AM, Rong Xu <xur@google.com> wrote:
>> On Fri, Nov 8, 2013 at 11:02 AM, Xinliang David Li <davidxl@google.com> wrote:
>>> On Fri, Nov 8, 2013 at 10:48 AM, Rong Xu <xur@google.com> wrote:
>>>> On Fri, Nov 8, 2013 at 10:22 AM, Xinliang David Li <davidxl@google.com> wrote:
>>>>> in libgcov-driver.c
>>>>>
>>>>>> /* Flag when the profile has already been dumped via __gcov_dump().  */
>>>>>> static int gcov_dump_complete;
>>>>>
>>>>>> inline void
>>>>>> set_gcov_dump_complete (void)
>>>>>>{
>>>>>   > gcov_dump_complete = 1;
>>>>>>}
>>>>>
>>>>>>inline void
>>>>>>reset_gcov_dump_complete (void)
>>>>>>{
>>>>>>  gcov_dump_complete = 0;
>>>>>>}
>>>>>
>>>>>
>>>>> These should be moved to libgcov-interface.c. Is there reason to not
>>>>> mark them as static?
>>>>
>>>> gcov_dump_complete is used in gcov_exit() which is in
>>>> libgcov-driver.c, but it's set in __gcov_dump and __gcov_reset, both
>>>> in libgcov-interface.c.
>>>> I want gcov_dump_complete a static. So I have to add to global
>>>> functions that accessible from libgcov-interface.c.
>>>> Another choice is to move __gcov_dump and __gcov_reset to
>>>> libgcov-driver.c and that will simplify the code.
>>>>
>>>
>>> Ok  then you should remove the 'inline' keyword for the set and reset
>>> functions and keep them in libgcov-driver.c.
>>
>> Will remove 'inline' keyword.
>>
>>>
>>>>>
>>>>> in libgcov-profiler.c, line 142
>>>>>
>>>>>> #if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS)
>>>>>> __thread
>>>>>
>>>>> trailing whilte space.
>>>>>
>>>>
>>>> Will fix.
>>>>
>>>>>
>>>>>>     || (VTABLE_USES_DESCRIPTORS && __gcov_indirect_call_callee
>>>>>>         && *(void **) cur_func == *(void **) __gcov_indirect_call_callee))
>>>>>
>>>>> trailing white space.
>>>>>
>>>>
>>>> Will fix.
>>>>
>>>>>
>>>>> in libgcov-merge.c
>>>>>
>>>>> 1) I don't think you need in_mem flag. For in memory merge, the source != NULL.
>>>>> 2) document the new source and weight parameter -- especially the weight.
>>>>
>>>> Will do.
>>>>
>>>>> 3) How do you deal with integer overflow ?
>>>>
>>>> This is good question. gcvo_type is (typically) long long. I thought
>>>> the count value will not be nearly close to the max of long long.
>>>> (We did see overflow in compiler, but that's because of the scaling --
>>>> some of the scaling factors are really large.)
>>>>
>>>
>>> Why not passing weight as a scaled PERCENT  (as branch prob) for the
>>> source? THis way, the merge tool does not need to do scaling twice.
>>>
>>
>> there are two weights, so unless you have two weights in the merge_add
>> functions, you have to
>> traverse the list twice. Do you mean pass addition parameters?
>>
>> Currently, merge tools is done this way:
>> merge (p1, p2, w1, w2)
>> first pass: merge_add (p1, p1, w1)
>> second pass: merge_add (p1, p2, w2)
>>
>
> Only need to pass the normalized the weight (in percent) for one
> profile source -- say norm_w2 : w2/(w1+w2), and the merge function can
> do this in one pass as norm_w1 = 1-norm_w2.
>
>
>> I initially had both weights in merge_add function. but later I found that
>> to deal with mis-match profile, I need to find out the gcov_info in p1
>> but not p2, (they need to multiply by w1 only).
>> So I choose the above structure.
>>
>> Another thing about the PERCENT is the inaccuracy for floating points.
>
> This is how profile update work in profile-use compilation -- so that
> should not be a big problem.
>
> Before you fix this, I'd like to hear what Honza and other reviewers think.
>
> thanks,
>
> David
>

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

-Rong

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

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-08 19:13                                 ` Rong Xu
  2013-11-08 19:39                                   ` Xinliang David Li
@ 2013-11-08 21:46                                   ` Joseph S. Myers
  1 sibling, 0 replies; 55+ messages in thread
From: Joseph S. Myers @ 2013-11-08 21:46 UTC (permalink / raw)
  To: Rong Xu; +Cc: Xinliang David Li, Jakub Jelinek, Jan Hubicka, GCC Patches

On Fri, 8 Nov 2013, Rong Xu wrote:

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

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

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-04 22:46     ` Rong Xu
                         ` (2 preceding siblings ...)
  2013-11-05  0:22       ` Xinliang David Li
@ 2013-11-11 16:16       ` Jan Hubicka
  2013-11-11 19:10         ` Rong Xu
  3 siblings, 1 reply; 55+ messages in thread
From: Jan Hubicka @ 2013-11-11 16:16 UTC (permalink / raw)
  To: Rong Xu; +Cc: Jan Hubicka, Xinliang David Li, GCC Patches

> 2013-11-04  Rong Xu  <xur@google.com>
> 
> 	* libgcc/libgcov.c: Delete as part of re-factoring.
> 	* libgcc/libgcov-profiler.c (__gcov_interval_profiler): Moved from
> 	libgcov.c
> 	(__gcov_pow2_profiler): Ditto.
> 	(__gcov_one_value_profiler_body): Ditto.
> 	(__gcov_one_value_profiler): Ditto.
> 	(__gcov_indirect_call_profiler): Ditto.
> 	(__gcov_indirect_call_profiler_v2): Ditto.
> 	(__gcov_average_profiler): Ditto.
> 	(__gcov_ior_profiler): Ditto.
> 	* libgcc/libgcov-driver.c (this_prg): Make it file scope
> 	static variable.
> 	(all_prg): Ditto.
> 	(crc32): Ditto.
> 	(gi_filename): Ditto.
> 	(fn_buffer): Ditto.
> 	(sum_buffer): Ditto.
>         (struct gcov_filename_aux): New types to store auxiliary information
>         for gi_filename.
> 	(gcov_version): Moved from libgcov.c.
> 	(crc32_unsigned): Ditto.
> 	(gcov_histogram_insert): Ditto.
> 	(gcov_compute_histogram): Ditto.
> 	(gcov_exit): Ditto.
> 	(gcov_clear): Ditto.
> 	(__gcov_init): Ditto.
> 	(gcov_exit_compute_summary): New function split from gcov_exit().
> 	(gcov_exit_merge_gcda): Ditto.
> 	(gcov_exit_write_gcda): Ditto.
> 	(gcov_exit_dump_gcov): Ditto.
> 	* libgcc/libgcov-interface.c (init_mx): Moved from libgcov.c.
> 	(init_mx_once): Ditto.
> 	(__gcov_flush): Ditto.
> 	(__gcov_reset): Ditto.
> 	(__gcov_dump): Ditto.
> 	(__gcov_fork): Ditto.
> 	(__gcov_execl): Ditto.
> 	(__gcov_execlp): Ditto.
> 	(__gcov_execle): Ditto.
> 	(__gcov_execv): Ditto.
> 	(__gcov_execvp): Ditto.
> 	(__gcov_execve): Ditto.
> 	* libgcc/libgcov-driver-system.c (gcov_error): New utility function.
> 	(allocate_filename_struct): New function split from gcov_exit().
> 	(gcov_exit_open_gcda_file): Ditto.
>         (create_file_directory): Moved from libgcov.c.
> 	* libgcc/libgcov-merge.c:
> 	(__gcov_merge_add): Moved from libgcov.c.
> 	(__gcov_merge_ior): Ditto.
> 	(__gcov_merge_single): Ditto.
> 	(__gcov_merge_delta): Ditto.
> 	* libgcc/Makefile.in: Change to build newly added files. 
> 	* gcc/gcov-io.h (__gcov_merge_ior): Add the decl to avoid warning.
> 

Hi,
the patch looks resonable. I take your word on the fact that there are no changes
in the code, I did not read it all ;)))
> Index: libgcc/Makefile.in
> ===================================================================
> --- libgcc/Makefile.in	(revision 204285)
> +++ libgcc/Makefile.in	(working copy)
> @@ -853,17 +853,37 @@ include $(iterator)
>  # Build libgcov components.
>  
>  # Defined in libgcov.c, included only in gcov library
> -LIBGCOV = _gcov _gcov_merge_add _gcov_merge_single _gcov_merge_delta \
> -    _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
> -    _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump \
> -    _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \
> +##LIBGCOV = _gcov _gcov_merge_add _gcov_merge_single _gcov_merge_delta \
> +##    _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
> +##    _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump \
> +##    _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \
> +##    _gcov_indirect_call_profiler _gcov_average_profiler _gcov_ior_profiler \
> +##    _gcov_merge_ior _gcov_indirect_call_profiler_v2

Probably no need for this commnet block.
> +
> +LIBGCOV_MERGE = _gcov_merge_add _gcov_merge_single _gcov_merge_delta _gcov_merge_ior
> +LIBGCOV_PROFILER = _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \
>      _gcov_indirect_call_profiler _gcov_average_profiler _gcov_ior_profiler \
> -    _gcov_merge_ior _gcov_indirect_call_profiler_v2
> +    _gcov_indirect_call_profiler_v2
> +LIBGCOV_INTERFACE = _gcov_flush _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
> +    _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump
> +LIBGCOV_DRIVER = _gcov 
>  
> -libgcov-objects = $(patsubst %,%$(objext),$(LIBGCOV))
> +libgcov-merge-objects = $(patsubst %,%$(objext),$(LIBGCOV_MERGE))
> +libgcov-profiler-objects = $(patsubst %,%$(objext),$(LIBGCOV_PROFILER))
> +libgcov-interface-objects = $(patsubst %,%$(objext),$(LIBGCOV_INTERFACE))
> +libgcov-driver-objects = $(patsubst %,%$(objext),$(LIBGCOV_DRIVER))
> +libgcov-objects = $(libgcov-merge-objects) $(libgcov-profiler-objects) \
> +                 $(libgcov-interface-objects) $(libgcov-driver-objects)
>  
> -$(libgcov-objects): %$(objext): $(srcdir)/libgcov.c
> -	$(gcc_compile) -DL$* -c $(srcdir)/libgcov.c
> +$(libgcov-merge-objects): %$(objext): $(srcdir)/libgcov-merge.c
> +	$(gcc_compile) -DL$* -c $(srcdir)/libgcov-merge.c
> +$(libgcov-profiler-objects): %$(objext): $(srcdir)/libgcov-profiler.c
> +	$(gcc_compile) -DL$* -c $(srcdir)/libgcov-profiler.c
> +$(libgcov-interface-objects): %$(objext): $(srcdir)/libgcov-interface.c
> +	$(gcc_compile) -DL$* -c $(srcdir)/libgcov-interface.c
> +$(libgcov-driver-objects): %$(objext): $(srcdir)/libgcov-driver.c \
> +  $(srcdir)/libgcov-driver-system.c
> +	$(gcc_compile) -DL$* -c $(srcdir)/libgcov-driver.c
>  
>  
>  # Static libraries.
> Index: gcc/gcov-io.h
> ===================================================================
> --- gcc/gcov-io.h	(revision 204285)
> +++ gcc/gcov-io.h	(working copy)
> @@ -515,6 +515,8 @@ extern void __gcov_merge_ior (gcov_type *, unsigne
>  extern void __gcov_interval_profiler (gcov_type *, gcov_type, int, unsigned);
>  extern void __gcov_pow2_profiler (gcov_type *, gcov_type);
>  extern void __gcov_one_value_profiler (gcov_type *, gcov_type);
> +extern void __gcov_indirect_call_profiler (gcov_type*, gcov_type,
> +                                           void*, void*);
>  extern void __gcov_indirect_call_profiler_v2 (gcov_type, void *);
>  extern void __gcov_average_profiler (gcov_type *, gcov_type);
>  extern void __gcov_ior_profiler (gcov_type *, gcov_type);

The patch is OK with the change above.
Honza

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-06 21:54               ` Rong Xu
  2013-11-06 23:33                 ` Xinliang David Li
@ 2013-11-11 16:22                 ` Jan Hubicka
  2013-11-11 21:03                   ` Rong Xu
  1 sibling, 1 reply; 55+ messages in thread
From: Jan Hubicka @ 2013-11-11 16:22 UTC (permalink / raw)
  To: Rong Xu; +Cc: Xinliang David Li, Jakub Jelinek, Jan Hubicka, GCC Patches

> Here is the patch that includes profile-tool.
> Profile-tool now has two functions: merge and rewrite. I'll add diff later.
> 
> Compiler is tested with spec2006 and profiledbootstrap.
> profile-tool is tested with spec2006 profiles.

Hi,
it would be nice if you could elaborate bit more on the tool and what it does.
I plan to implement relink only instrumentatino for LTO and for that I will need
profile reading/handling that is independent on functio nbodies, so perhaps
we can unify the infrastructure.  My plans always was to merge the gcov and
profile.c profile handling logic and make it bit more generic.

Also I think profile-tool is a bit generic name. I.e. it does not realy say it
is related to GCC's coverage profiling.  Maybe gcc-profile-tool or
gcov-profile-tool may be better?
Perhaps an resonable option would also be to bundle it into gcov binary...
> Index: libgcc/libgcov-tool.c
> ===================================================================
> --- libgcc/libgcov-tool.c	(revision 0)
> +++ libgcc/libgcov-tool.c	(revision 0)
> @@ -0,0 +1,800 @@
> +/* GCC instrumentation plugin for ThreadSanitizer.
> +   Copyright (C) 2011-2013 Free Software Foundation, Inc.
> +   Contributed by Dmitry Vyukov <dvyukov@google.com>

You need to update this...
Why there needs to be libgcov changes?

Honza

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-11 16:16       ` Jan Hubicka
@ 2013-11-11 19:10         ` Rong Xu
  2013-11-13  6:18           ` Rong Xu
  0 siblings, 1 reply; 55+ messages in thread
From: Rong Xu @ 2013-11-11 19:10 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Xinliang David Li, GCC Patches

On Mon, Nov 11, 2013 at 7:02 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>> 2013-11-04  Rong Xu  <xur@google.com>
>>
>>       * libgcc/libgcov.c: Delete as part of re-factoring.
>>       * libgcc/libgcov-profiler.c (__gcov_interval_profiler): Moved from
>>       libgcov.c
>>       (__gcov_pow2_profiler): Ditto.
>>       (__gcov_one_value_profiler_body): Ditto.
>>       (__gcov_one_value_profiler): Ditto.
>>       (__gcov_indirect_call_profiler): Ditto.
>>       (__gcov_indirect_call_profiler_v2): Ditto.
>>       (__gcov_average_profiler): Ditto.
>>       (__gcov_ior_profiler): Ditto.
>>       * libgcc/libgcov-driver.c (this_prg): Make it file scope
>>       static variable.
>>       (all_prg): Ditto.
>>       (crc32): Ditto.
>>       (gi_filename): Ditto.
>>       (fn_buffer): Ditto.
>>       (sum_buffer): Ditto.
>>         (struct gcov_filename_aux): New types to store auxiliary information
>>         for gi_filename.
>>       (gcov_version): Moved from libgcov.c.
>>       (crc32_unsigned): Ditto.
>>       (gcov_histogram_insert): Ditto.
>>       (gcov_compute_histogram): Ditto.
>>       (gcov_exit): Ditto.
>>       (gcov_clear): Ditto.
>>       (__gcov_init): Ditto.
>>       (gcov_exit_compute_summary): New function split from gcov_exit().
>>       (gcov_exit_merge_gcda): Ditto.
>>       (gcov_exit_write_gcda): Ditto.
>>       (gcov_exit_dump_gcov): Ditto.
>>       * libgcc/libgcov-interface.c (init_mx): Moved from libgcov.c.
>>       (init_mx_once): Ditto.
>>       (__gcov_flush): Ditto.
>>       (__gcov_reset): Ditto.
>>       (__gcov_dump): Ditto.
>>       (__gcov_fork): Ditto.
>>       (__gcov_execl): Ditto.
>>       (__gcov_execlp): Ditto.
>>       (__gcov_execle): Ditto.
>>       (__gcov_execv): Ditto.
>>       (__gcov_execvp): Ditto.
>>       (__gcov_execve): Ditto.
>>       * libgcc/libgcov-driver-system.c (gcov_error): New utility function.
>>       (allocate_filename_struct): New function split from gcov_exit().
>>       (gcov_exit_open_gcda_file): Ditto.
>>         (create_file_directory): Moved from libgcov.c.
>>       * libgcc/libgcov-merge.c:
>>       (__gcov_merge_add): Moved from libgcov.c.
>>       (__gcov_merge_ior): Ditto.
>>       (__gcov_merge_single): Ditto.
>>       (__gcov_merge_delta): Ditto.
>>       * libgcc/Makefile.in: Change to build newly added files.
>>       * gcc/gcov-io.h (__gcov_merge_ior): Add the decl to avoid warning.
>>
>
> Hi,
> the patch looks resonable. I take your word on the fact that there are no changes
> in the code, I did not read it all ;)))

We did split gcov_exit() into more functions.
But, semantically the code is the same.

>> Index: libgcc/Makefile.in
>> ===================================================================
>> --- libgcc/Makefile.in        (revision 204285)
>> +++ libgcc/Makefile.in        (working copy)
>> @@ -853,17 +853,37 @@ include $(iterator)
>>  # Build libgcov components.
>>
>>  # Defined in libgcov.c, included only in gcov library
>> -LIBGCOV = _gcov _gcov_merge_add _gcov_merge_single _gcov_merge_delta \
>> -    _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
>> -    _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump \
>> -    _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \
>> +##LIBGCOV = _gcov _gcov_merge_add _gcov_merge_single _gcov_merge_delta \
>> +##    _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
>> +##    _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump \
>> +##    _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \
>> +##    _gcov_indirect_call_profiler _gcov_average_profiler _gcov_ior_profiler \
>> +##    _gcov_merge_ior _gcov_indirect_call_profiler_v2
>
> Probably no need for this commnet block.

Yes. I ready remove in the most recently patch.

>> +
>> +LIBGCOV_MERGE = _gcov_merge_add _gcov_merge_single _gcov_merge_delta _gcov_merge_ior
>> +LIBGCOV_PROFILER = _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \
>>      _gcov_indirect_call_profiler _gcov_average_profiler _gcov_ior_profiler \
>> -    _gcov_merge_ior _gcov_indirect_call_profiler_v2
>> +    _gcov_indirect_call_profiler_v2
>> +LIBGCOV_INTERFACE = _gcov_flush _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
>> +    _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump
>> +LIBGCOV_DRIVER = _gcov
>>
>> -libgcov-objects = $(patsubst %,%$(objext),$(LIBGCOV))
>> +libgcov-merge-objects = $(patsubst %,%$(objext),$(LIBGCOV_MERGE))
>> +libgcov-profiler-objects = $(patsubst %,%$(objext),$(LIBGCOV_PROFILER))
>> +libgcov-interface-objects = $(patsubst %,%$(objext),$(LIBGCOV_INTERFACE))
>> +libgcov-driver-objects = $(patsubst %,%$(objext),$(LIBGCOV_DRIVER))
>> +libgcov-objects = $(libgcov-merge-objects) $(libgcov-profiler-objects) \
>> +                 $(libgcov-interface-objects) $(libgcov-driver-objects)
>>
>> -$(libgcov-objects): %$(objext): $(srcdir)/libgcov.c
>> -     $(gcc_compile) -DL$* -c $(srcdir)/libgcov.c
>> +$(libgcov-merge-objects): %$(objext): $(srcdir)/libgcov-merge.c
>> +     $(gcc_compile) -DL$* -c $(srcdir)/libgcov-merge.c
>> +$(libgcov-profiler-objects): %$(objext): $(srcdir)/libgcov-profiler.c
>> +     $(gcc_compile) -DL$* -c $(srcdir)/libgcov-profiler.c
>> +$(libgcov-interface-objects): %$(objext): $(srcdir)/libgcov-interface.c
>> +     $(gcc_compile) -DL$* -c $(srcdir)/libgcov-interface.c
>> +$(libgcov-driver-objects): %$(objext): $(srcdir)/libgcov-driver.c \
>> +  $(srcdir)/libgcov-driver-system.c
>> +     $(gcc_compile) -DL$* -c $(srcdir)/libgcov-driver.c
>>
>>
>>  # Static libraries.
>> Index: gcc/gcov-io.h
>> ===================================================================
>> --- gcc/gcov-io.h     (revision 204285)
>> +++ gcc/gcov-io.h     (working copy)
>> @@ -515,6 +515,8 @@ extern void __gcov_merge_ior (gcov_type *, unsigne
>>  extern void __gcov_interval_profiler (gcov_type *, gcov_type, int, unsigned);
>>  extern void __gcov_pow2_profiler (gcov_type *, gcov_type);
>>  extern void __gcov_one_value_profiler (gcov_type *, gcov_type);
>> +extern void __gcov_indirect_call_profiler (gcov_type*, gcov_type,
>> +                                           void*, void*);
>>  extern void __gcov_indirect_call_profiler_v2 (gcov_type, void *);
>>  extern void __gcov_average_profiler (gcov_type *, gcov_type);
>>  extern void __gcov_ior_profiler (gcov_type *, gcov_type);
>
> The patch is OK with the change above.

Thanks for the OK. I'll do some final tests on a clean client. Then I'll commit.

> Honza

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-11 16:22                 ` Jan Hubicka
@ 2013-11-11 21:03                   ` Rong Xu
  0 siblings, 0 replies; 55+ messages in thread
From: Rong Xu @ 2013-11-11 21:03 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Xinliang David Li, Jakub Jelinek, GCC Patches

On Mon, Nov 11, 2013 at 7:17 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>> Here is the patch that includes profile-tool.
>> Profile-tool now has two functions: merge and rewrite. I'll add diff later.
>>
>> Compiler is tested with spec2006 and profiledbootstrap.
>> profile-tool is tested with spec2006 profiles.
>
> Hi,
> it would be nice if you could elaborate bit more on the tool and what it does.
> I plan to implement relink only instrumentatino for LTO and for that I will need
> profile reading/handling that is independent on functio nbodies, so perhaps
> we can unify the infrastructure.  My plans always was to merge the gcov and
> profile.c profile handling logic and make it bit more generic.

This tool is designed to manipulate raw profile counters. We plan to
have the following functionality:

(1) merge the gcda file with weights. We have fairly complicated build
system. Sometime online merging in
current libgcov.a not feasible (or not desirable). We need offline
tool the merge the FDO profiles.
The program may have multiple training inputs with different
importance -- this is where weighted merging
comes from.

(2) diff the profile. We want to know how different of two profile
data. Some tolerance of mismatch is required here.
This is crucial for performance triaging.

(3) rewrite the gcda file. We can do some (user-direct) fix-up work in
the rewrite. This is may not be important for FDO. But
this will be very useful for LIPO (like fix-up the indirect-target due
to mismatch, rewrite module-grouping)

(4) better dumping: like dump top-n-hottest
functions/objects/indirectly-targets etc.

This tool reconstructs the gcov_info list from gcda files and then
uses significant part of libgcov.c code:
merge, compute summary/histo-gram, and dump gcda files etc.

The merging into current libgcov can also use this piece of code to do
the in-memory merge. The advantage
here is we will have precise histro-gram.

>
> Also I think profile-tool is a bit generic name. I.e. it does not realy say it
> is related to GCC's coverage profiling.  Maybe gcc-profile-tool or
> gcov-profile-tool may be better?

Yes. gcov-profile-tool seems to be better, or simply gcov-tool?

> Perhaps an resonable option would also be to bundle it into gcov binary...

Bundling into gcov is an interesting idea. I do plan to read gcno file
to extract more information (at least the function name).
Bungling into gcda means the tool can work on bb level, rather on the
raw counters. Let me think a bit more on this.

>> Index: libgcc/libgcov-tool.c
>> ===================================================================
>> --- libgcc/libgcov-tool.c     (revision 0)
>> +++ libgcc/libgcov-tool.c     (revision 0)
>> @@ -0,0 +1,800 @@
>> +/* GCC instrumentation plugin for ThreadSanitizer.
>> +   Copyright (C) 2011-2013 Free Software Foundation, Inc.
>> +   Contributed by Dmitry Vyukov <dvyukov@google.com>
>
> You need to update this...
> Why there needs to be libgcov changes?

David pointed out the copyright issue in an earlier email. I fixed it.

The changes in libgcov are
(1) ifdef the target headers. I need all the libgcov functionality to
read/write the gcda files, as well as summary and histo-gram. But the
binary is for host machines.
(2) change the merge function so that I can take an in-memory
gcov-info as input.

>
> Honza

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-11 19:10         ` Rong Xu
@ 2013-11-13  6:18           ` Rong Xu
  2013-11-18 21:52             ` Rong Xu
  0 siblings, 1 reply; 55+ messages in thread
From: Rong Xu @ 2013-11-13  6:18 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Xinliang David Li, GCC Patches

The patch was checked in as r204730.

Tested with
configure with --enable-languages=all,obj-c++
(ada is currently broken, so I was not be able test)
bootstrap and profiledbootstrap
regression for -m32 and -m64.
spec2006

The only semantic change is __gcov_flush() used to be under L_gcov.
I put the function to libgcov-interface.c and made it under
L_gcov_flush (newly added).
So if you need this function, you have to enable L_gcov_flush in the
libgcc/Makefile.in.

Thanks,

-Rong

On Mon, Nov 11, 2013 at 10:19 AM, Rong Xu <xur@google.com> wrote:
> On Mon, Nov 11, 2013 at 7:02 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>>> 2013-11-04  Rong Xu  <xur@google.com>
>>>
>>>       * libgcc/libgcov.c: Delete as part of re-factoring.
>>>       * libgcc/libgcov-profiler.c (__gcov_interval_profiler): Moved from
>>>       libgcov.c
>>>       (__gcov_pow2_profiler): Ditto.
>>>       (__gcov_one_value_profiler_body): Ditto.
>>>       (__gcov_one_value_profiler): Ditto.
>>>       (__gcov_indirect_call_profiler): Ditto.
>>>       (__gcov_indirect_call_profiler_v2): Ditto.
>>>       (__gcov_average_profiler): Ditto.
>>>       (__gcov_ior_profiler): Ditto.
>>>       * libgcc/libgcov-driver.c (this_prg): Make it file scope
>>>       static variable.
>>>       (all_prg): Ditto.
>>>       (crc32): Ditto.
>>>       (gi_filename): Ditto.
>>>       (fn_buffer): Ditto.
>>>       (sum_buffer): Ditto.
>>>         (struct gcov_filename_aux): New types to store auxiliary information
>>>         for gi_filename.
>>>       (gcov_version): Moved from libgcov.c.
>>>       (crc32_unsigned): Ditto.
>>>       (gcov_histogram_insert): Ditto.
>>>       (gcov_compute_histogram): Ditto.
>>>       (gcov_exit): Ditto.
>>>       (gcov_clear): Ditto.
>>>       (__gcov_init): Ditto.
>>>       (gcov_exit_compute_summary): New function split from gcov_exit().
>>>       (gcov_exit_merge_gcda): Ditto.
>>>       (gcov_exit_write_gcda): Ditto.
>>>       (gcov_exit_dump_gcov): Ditto.
>>>       * libgcc/libgcov-interface.c (init_mx): Moved from libgcov.c.
>>>       (init_mx_once): Ditto.
>>>       (__gcov_flush): Ditto.
>>>       (__gcov_reset): Ditto.
>>>       (__gcov_dump): Ditto.
>>>       (__gcov_fork): Ditto.
>>>       (__gcov_execl): Ditto.
>>>       (__gcov_execlp): Ditto.
>>>       (__gcov_execle): Ditto.
>>>       (__gcov_execv): Ditto.
>>>       (__gcov_execvp): Ditto.
>>>       (__gcov_execve): Ditto.
>>>       * libgcc/libgcov-driver-system.c (gcov_error): New utility function.
>>>       (allocate_filename_struct): New function split from gcov_exit().
>>>       (gcov_exit_open_gcda_file): Ditto.
>>>         (create_file_directory): Moved from libgcov.c.
>>>       * libgcc/libgcov-merge.c:
>>>       (__gcov_merge_add): Moved from libgcov.c.
>>>       (__gcov_merge_ior): Ditto.
>>>       (__gcov_merge_single): Ditto.
>>>       (__gcov_merge_delta): Ditto.
>>>       * libgcc/Makefile.in: Change to build newly added files.
>>>       * gcc/gcov-io.h (__gcov_merge_ior): Add the decl to avoid warning.
>>>
>>
>> Hi,
>> the patch looks resonable. I take your word on the fact that there are no changes
>> in the code, I did not read it all ;)))
>
> We did split gcov_exit() into more functions.
> But, semantically the code is the same.
>
>>> Index: libgcc/Makefile.in
>>> ===================================================================
>>> --- libgcc/Makefile.in        (revision 204285)
>>> +++ libgcc/Makefile.in        (working copy)
>>> @@ -853,17 +853,37 @@ include $(iterator)
>>>  # Build libgcov components.
>>>
>>>  # Defined in libgcov.c, included only in gcov library
>>> -LIBGCOV = _gcov _gcov_merge_add _gcov_merge_single _gcov_merge_delta \
>>> -    _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
>>> -    _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump \
>>> -    _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \
>>> +##LIBGCOV = _gcov _gcov_merge_add _gcov_merge_single _gcov_merge_delta \
>>> +##    _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
>>> +##    _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump \
>>> +##    _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \
>>> +##    _gcov_indirect_call_profiler _gcov_average_profiler _gcov_ior_profiler \
>>> +##    _gcov_merge_ior _gcov_indirect_call_profiler_v2
>>
>> Probably no need for this commnet block.
>
> Yes. I ready remove in the most recently patch.
>
>>> +
>>> +LIBGCOV_MERGE = _gcov_merge_add _gcov_merge_single _gcov_merge_delta _gcov_merge_ior
>>> +LIBGCOV_PROFILER = _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \
>>>      _gcov_indirect_call_profiler _gcov_average_profiler _gcov_ior_profiler \
>>> -    _gcov_merge_ior _gcov_indirect_call_profiler_v2
>>> +    _gcov_indirect_call_profiler_v2
>>> +LIBGCOV_INTERFACE = _gcov_flush _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
>>> +    _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump
>>> +LIBGCOV_DRIVER = _gcov
>>>
>>> -libgcov-objects = $(patsubst %,%$(objext),$(LIBGCOV))
>>> +libgcov-merge-objects = $(patsubst %,%$(objext),$(LIBGCOV_MERGE))
>>> +libgcov-profiler-objects = $(patsubst %,%$(objext),$(LIBGCOV_PROFILER))
>>> +libgcov-interface-objects = $(patsubst %,%$(objext),$(LIBGCOV_INTERFACE))
>>> +libgcov-driver-objects = $(patsubst %,%$(objext),$(LIBGCOV_DRIVER))
>>> +libgcov-objects = $(libgcov-merge-objects) $(libgcov-profiler-objects) \
>>> +                 $(libgcov-interface-objects) $(libgcov-driver-objects)
>>>
>>> -$(libgcov-objects): %$(objext): $(srcdir)/libgcov.c
>>> -     $(gcc_compile) -DL$* -c $(srcdir)/libgcov.c
>>> +$(libgcov-merge-objects): %$(objext): $(srcdir)/libgcov-merge.c
>>> +     $(gcc_compile) -DL$* -c $(srcdir)/libgcov-merge.c
>>> +$(libgcov-profiler-objects): %$(objext): $(srcdir)/libgcov-profiler.c
>>> +     $(gcc_compile) -DL$* -c $(srcdir)/libgcov-profiler.c
>>> +$(libgcov-interface-objects): %$(objext): $(srcdir)/libgcov-interface.c
>>> +     $(gcc_compile) -DL$* -c $(srcdir)/libgcov-interface.c
>>> +$(libgcov-driver-objects): %$(objext): $(srcdir)/libgcov-driver.c \
>>> +  $(srcdir)/libgcov-driver-system.c
>>> +     $(gcc_compile) -DL$* -c $(srcdir)/libgcov-driver.c
>>>
>>>
>>>  # Static libraries.
>>> Index: gcc/gcov-io.h
>>> ===================================================================
>>> --- gcc/gcov-io.h     (revision 204285)
>>> +++ gcc/gcov-io.h     (working copy)
>>> @@ -515,6 +515,8 @@ extern void __gcov_merge_ior (gcov_type *, unsigne
>>>  extern void __gcov_interval_profiler (gcov_type *, gcov_type, int, unsigned);
>>>  extern void __gcov_pow2_profiler (gcov_type *, gcov_type);
>>>  extern void __gcov_one_value_profiler (gcov_type *, gcov_type);
>>> +extern void __gcov_indirect_call_profiler (gcov_type*, gcov_type,
>>> +                                           void*, void*);
>>>  extern void __gcov_indirect_call_profiler_v2 (gcov_type, void *);
>>>  extern void __gcov_average_profiler (gcov_type *, gcov_type);
>>>  extern void __gcov_ior_profiler (gcov_type *, gcov_type);
>>
>> The patch is OK with the change above.
>
> Thanks for the OK. I'll do some final tests on a clean client. Then I'll commit.
>
>> Honza

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-13  6:18           ` Rong Xu
@ 2013-11-18 21:52             ` Rong Xu
  2013-11-18 22:18               ` Xinliang David Li
  2013-12-06 14:23               ` Jan Hubicka
  0 siblings, 2 replies; 55+ messages in thread
From: Rong Xu @ 2013-11-18 21:52 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Xinliang David Li, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 8468 bytes --]

Hi, all

This is the new patch for gcov-tool (previously profile-tool).

Honza: can you comment on the new merge interface? David posted some
comments in an earlier email and we want to know what's your opinion.

Test patch has been tested with boostrap, regresssion,
profiledbootstrap and SPEC2006.

Noticeable changes from the earlier version:

1. create a new file libgcov.h and move libgcov-*.h headers to libgcov.h
So we can included multiple libgcov-*.c without adding new macros.

2. split libgcov.h specific code in gcvo-io.h to libcc/libgcov.h
Avoid multiple-page of code under IN_LIBGCOV macro -- this
improves the readability.

3. make gcov_var static, and move the definition from gcov-io.h to
gcov-io.c. Also
   move some static functions accessing gcov_var to gcvo-io.c
Current code rely on GCOV_LINKAGE tricks to avoid multi-definition. I don't see
a reason that gcov_var needs to exposed as a global.

4. expose gcov_write_strings() and gcov_sync() to gcov_tool usage

5. rename profile-tool to gcov-tool per Honza's suggestion.

Thanks,

-Rong

On Tue, Nov 12, 2013 at 4:27 PM, Rong Xu <xur@google.com> wrote:
> The patch was checked in as r204730.
>
> Tested with
> configure with --enable-languages=all,obj-c++
> (ada is currently broken, so I was not be able test)
> bootstrap and profiledbootstrap
> regression for -m32 and -m64.
> spec2006
>
> The only semantic change is __gcov_flush() used to be under L_gcov.
> I put the function to libgcov-interface.c and made it under
> L_gcov_flush (newly added).
> So if you need this function, you have to enable L_gcov_flush in the
> libgcc/Makefile.in.
>
> Thanks,
>
> -Rong
>
> On Mon, Nov 11, 2013 at 10:19 AM, Rong Xu <xur@google.com> wrote:
>> On Mon, Nov 11, 2013 at 7:02 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>>>> 2013-11-04  Rong Xu  <xur@google.com>
>>>>
>>>>       * libgcc/libgcov.c: Delete as part of re-factoring.
>>>>       * libgcc/libgcov-profiler.c (__gcov_interval_profiler): Moved from
>>>>       libgcov.c
>>>>       (__gcov_pow2_profiler): Ditto.
>>>>       (__gcov_one_value_profiler_body): Ditto.
>>>>       (__gcov_one_value_profiler): Ditto.
>>>>       (__gcov_indirect_call_profiler): Ditto.
>>>>       (__gcov_indirect_call_profiler_v2): Ditto.
>>>>       (__gcov_average_profiler): Ditto.
>>>>       (__gcov_ior_profiler): Ditto.
>>>>       * libgcc/libgcov-driver.c (this_prg): Make it file scope
>>>>       static variable.
>>>>       (all_prg): Ditto.
>>>>       (crc32): Ditto.
>>>>       (gi_filename): Ditto.
>>>>       (fn_buffer): Ditto.
>>>>       (sum_buffer): Ditto.
>>>>         (struct gcov_filename_aux): New types to store auxiliary information
>>>>         for gi_filename.
>>>>       (gcov_version): Moved from libgcov.c.
>>>>       (crc32_unsigned): Ditto.
>>>>       (gcov_histogram_insert): Ditto.
>>>>       (gcov_compute_histogram): Ditto.
>>>>       (gcov_exit): Ditto.
>>>>       (gcov_clear): Ditto.
>>>>       (__gcov_init): Ditto.
>>>>       (gcov_exit_compute_summary): New function split from gcov_exit().
>>>>       (gcov_exit_merge_gcda): Ditto.
>>>>       (gcov_exit_write_gcda): Ditto.
>>>>       (gcov_exit_dump_gcov): Ditto.
>>>>       * libgcc/libgcov-interface.c (init_mx): Moved from libgcov.c.
>>>>       (init_mx_once): Ditto.
>>>>       (__gcov_flush): Ditto.
>>>>       (__gcov_reset): Ditto.
>>>>       (__gcov_dump): Ditto.
>>>>       (__gcov_fork): Ditto.
>>>>       (__gcov_execl): Ditto.
>>>>       (__gcov_execlp): Ditto.
>>>>       (__gcov_execle): Ditto.
>>>>       (__gcov_execv): Ditto.
>>>>       (__gcov_execvp): Ditto.
>>>>       (__gcov_execve): Ditto.
>>>>       * libgcc/libgcov-driver-system.c (gcov_error): New utility function.
>>>>       (allocate_filename_struct): New function split from gcov_exit().
>>>>       (gcov_exit_open_gcda_file): Ditto.
>>>>         (create_file_directory): Moved from libgcov.c.
>>>>       * libgcc/libgcov-merge.c:
>>>>       (__gcov_merge_add): Moved from libgcov.c.
>>>>       (__gcov_merge_ior): Ditto.
>>>>       (__gcov_merge_single): Ditto.
>>>>       (__gcov_merge_delta): Ditto.
>>>>       * libgcc/Makefile.in: Change to build newly added files.
>>>>       * gcc/gcov-io.h (__gcov_merge_ior): Add the decl to avoid warning.
>>>>
>>>
>>> Hi,
>>> the patch looks resonable. I take your word on the fact that there are no changes
>>> in the code, I did not read it all ;)))
>>
>> We did split gcov_exit() into more functions.
>> But, semantically the code is the same.
>>
>>>> Index: libgcc/Makefile.in
>>>> ===================================================================
>>>> --- libgcc/Makefile.in        (revision 204285)
>>>> +++ libgcc/Makefile.in        (working copy)
>>>> @@ -853,17 +853,37 @@ include $(iterator)
>>>>  # Build libgcov components.
>>>>
>>>>  # Defined in libgcov.c, included only in gcov library
>>>> -LIBGCOV = _gcov _gcov_merge_add _gcov_merge_single _gcov_merge_delta \
>>>> -    _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
>>>> -    _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump \
>>>> -    _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \
>>>> +##LIBGCOV = _gcov _gcov_merge_add _gcov_merge_single _gcov_merge_delta \
>>>> +##    _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
>>>> +##    _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump \
>>>> +##    _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \
>>>> +##    _gcov_indirect_call_profiler _gcov_average_profiler _gcov_ior_profiler \
>>>> +##    _gcov_merge_ior _gcov_indirect_call_profiler_v2
>>>
>>> Probably no need for this commnet block.
>>
>> Yes. I ready remove in the most recently patch.
>>
>>>> +
>>>> +LIBGCOV_MERGE = _gcov_merge_add _gcov_merge_single _gcov_merge_delta _gcov_merge_ior
>>>> +LIBGCOV_PROFILER = _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \
>>>>      _gcov_indirect_call_profiler _gcov_average_profiler _gcov_ior_profiler \
>>>> -    _gcov_merge_ior _gcov_indirect_call_profiler_v2
>>>> +    _gcov_indirect_call_profiler_v2
>>>> +LIBGCOV_INTERFACE = _gcov_flush _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
>>>> +    _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump
>>>> +LIBGCOV_DRIVER = _gcov
>>>>
>>>> -libgcov-objects = $(patsubst %,%$(objext),$(LIBGCOV))
>>>> +libgcov-merge-objects = $(patsubst %,%$(objext),$(LIBGCOV_MERGE))
>>>> +libgcov-profiler-objects = $(patsubst %,%$(objext),$(LIBGCOV_PROFILER))
>>>> +libgcov-interface-objects = $(patsubst %,%$(objext),$(LIBGCOV_INTERFACE))
>>>> +libgcov-driver-objects = $(patsubst %,%$(objext),$(LIBGCOV_DRIVER))
>>>> +libgcov-objects = $(libgcov-merge-objects) $(libgcov-profiler-objects) \
>>>> +                 $(libgcov-interface-objects) $(libgcov-driver-objects)
>>>>
>>>> -$(libgcov-objects): %$(objext): $(srcdir)/libgcov.c
>>>> -     $(gcc_compile) -DL$* -c $(srcdir)/libgcov.c
>>>> +$(libgcov-merge-objects): %$(objext): $(srcdir)/libgcov-merge.c
>>>> +     $(gcc_compile) -DL$* -c $(srcdir)/libgcov-merge.c
>>>> +$(libgcov-profiler-objects): %$(objext): $(srcdir)/libgcov-profiler.c
>>>> +     $(gcc_compile) -DL$* -c $(srcdir)/libgcov-profiler.c
>>>> +$(libgcov-interface-objects): %$(objext): $(srcdir)/libgcov-interface.c
>>>> +     $(gcc_compile) -DL$* -c $(srcdir)/libgcov-interface.c
>>>> +$(libgcov-driver-objects): %$(objext): $(srcdir)/libgcov-driver.c \
>>>> +  $(srcdir)/libgcov-driver-system.c
>>>> +     $(gcc_compile) -DL$* -c $(srcdir)/libgcov-driver.c
>>>>
>>>>
>>>>  # Static libraries.
>>>> Index: gcc/gcov-io.h
>>>> ===================================================================
>>>> --- gcc/gcov-io.h     (revision 204285)
>>>> +++ gcc/gcov-io.h     (working copy)
>>>> @@ -515,6 +515,8 @@ extern void __gcov_merge_ior (gcov_type *, unsigne
>>>>  extern void __gcov_interval_profiler (gcov_type *, gcov_type, int, unsigned);
>>>>  extern void __gcov_pow2_profiler (gcov_type *, gcov_type);
>>>>  extern void __gcov_one_value_profiler (gcov_type *, gcov_type);
>>>> +extern void __gcov_indirect_call_profiler (gcov_type*, gcov_type,
>>>> +                                           void*, void*);
>>>>  extern void __gcov_indirect_call_profiler_v2 (gcov_type, void *);
>>>>  extern void __gcov_average_profiler (gcov_type *, gcov_type);
>>>>  extern void __gcov_ior_profiler (gcov_type *, gcov_type);
>>>
>>> The patch is OK with the change above.
>>
>> Thanks for the OK. I'll do some final tests on a clean client. Then I'll commit.
>>
>>> Honza

[-- Attachment #2: gcovtool_patch3.txt --]
[-- Type: text/plain, Size: 80889 bytes --]

2013-11-18  Rong Xu  <xur@google.com>

	* gcc/gcov-io.c (gcov_var): Moved from gcov-io.h and make it static.
	(gcov_position): Move from gcov-io.h
	(gcov_is_error): Ditto.
	(gcov_rewrite): Ditto.
	* gcc/gcov-io.h: Re-factoring. Move gcov_var to gcov-io.h and
        move the libgcov only part of libgcc/libgcov.h.
	* libgcc/libgcov.h: New common header files for libgcov-*.h
	* libgcc/Makefile.in: Add dependence to libgcov.h
	* libgcc/libgcov-profiler.c: Use libgcov.h
	* libgcc/libgcov-driver.c: Ditto.
	* libgcc/libgcov-interface.c: Ditto.
	* libgcc/libgcov-driver-system.c (allocate_filename_struct): use
	xmalloc instread of malloc.
	* libgcc/libgcov-merge.c (void __gcov_merge_delta): Add more
	parameters to merge function.
	(__gcov_merge_add): Ditto.
	(__gcov_merge_ior): Ditto.
	(__gcov_merge_time_profile): Ditto.
	(__gcov_merge_single): Ditto.
	(__gcov_merge_delta): Ditto.
	* libgcc/libgcov-tool.c (void gcov_tool_set_verbose): New for
	gcov-tool support.
	(set_fn_ctrs): Ditto.
	(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_merge): Ditto.
	(find_match_gcov_inf Ditto.o):
	(gcov_profile_merge): Ditto.
	(__gcov_scale_add): Ditto.
	(__gcov_scale_ior): Ditto.
	(__gcov_scale_delta): Ditto.
	(__gcov_scale_single): Ditto.
	(gcov_profile_scale): Ditto.
	(gcov_profile_normalize): Ditto.
	(__gcov_scale2_add): Ditto.
	(__gcov_scale2_ior): Ditto.
	(__gcov_scale2_delta): Ditto.
	(__gcov_scale2_single): Ditto.
	(gcov_profile_scale2): Ditto.
	* gcc/gcov-tool.c (unlink_file): Gcov-tool driver support.
	(unlink_dir): Ditto.
	(profile_merge): Ditto.
	(print_merge_usage_message): Ditto.
	(merge_usage): Ditto.
	(do_merge): Ditto.
	(profile_rewrite2): Ditto.
	(profile_rewrite): Ditto.
	(print_rewrite_usage_message): Ditto.
	(rewrite_usage): Ditto.
	(do_rewrite): Ditto.
	(print_usage): Ditto.
	(print_version): Ditto.
	(process_args): Ditto.
	(main): Ditto.
	* gcc/Makefile.in: Build and install gcov-tool.
Index: gcc/gcov-tool.c
===================================================================
--- gcc/gcov-tool.c	(revision 0)
+++ gcc/gcov-tool.c	(revision 0)
@@ -0,0 +1,462 @@
+/* GCC gcov-tool support.
+   Contributed by Rong Xu <xur@google.com>.
+   Copyright (C) 2013 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#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_tool_set_verbose (void);
+
+
+static int verbose;
+
+static int
+unlink_file (const char *name,
+             const struct stat *status ATTRIBUTE_UNUSED,
+             int type ATTRIBUTE_UNUSED,
+             struct FTW *ftwbuf ATTRIBUTE_UNUSED)
+{
+  int ret = 0;
+  int len = strlen (name);
+  int len1 = strlen (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;
+}
+
+static int
+unlink_dir (const char *path)
+{
+    return nftw(path, unlink_file, 64, FTW_DEPTH | FTW_PHYS);
+}
+
+static int
+profile_merge (const char *d1, const char *d2, const char *out, int w1, int w2)
+{
+  char *pwd;
+  int ret;
+  struct gcov_info * d1_profile;
+  struct gcov_info * d2_profile;
+
+
+  d1_profile = gcov_read_profile_dir (d1, 0);
+  if (!d1_profile)
+    return 1;
+
+  if (d2)
+    {
+      d2_profile = gcov_read_profile_dir (d2, 0);
+      if (!d2_profile)
+        return 1;
+
+      /* the actual merge: we overwrite to d1_profile.  */
+      ret = gcov_profile_merge (d1_profile, d2_profile, w1, w2);
+
+      if (ret)
+        return ret;
+    }
+
+  /* output */
+  unlink_dir (out);
+  mkdir (out, 0755);
+  pwd = 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;
+}
+
+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 }
+};
+
+static void
+merge_usage (void)
+{
+  fnotice (stderr, "Merge subcomand usage:");
+  print_merge_usage_message (true);
+  exit (FATAL_EXIT_CODE);
+}
+
+static int
+do_merge (int argc, char **argv)
+{
+  int opt;
+  int ret;
+  const char *output_dir = 0;
+  int w1 = 1, w2 = 1;
+
+  while ((opt = getopt_long (argc, argv, "vo:w:", merge_options, NULL)) != -1)
+    {
+      switch (opt)
+        {
+        case 'v':
+          verbose = 1;
+          gcov_tool_set_verbose ();
+          break;
+        case 'o':
+          output_dir = optarg;
+          break;
+        case 'w':
+          sscanf (optarg, "%d,%d", &w1, &w2);
+          if (w1 < 0 || w2 < 0)
+            {
+              fnotice (stderr, "weights need to be non-negative\n");
+              exit (FATAL_EXIT_CODE);
+            }
+          break;
+        default:
+          merge_usage ();
+        }
+    }
+
+  if (output_dir == NULL)
+    output_dir = "merged_profile";
+
+  if (argc - optind == 2)
+    ret = profile_merge (argv[optind], argv[optind+1], output_dir, w1, w2);
+  else
+    merge_usage ();
+
+  return ret;
+}
+
+static int
+profile_rewrite2 (const char *d1, const char *out, 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 profile */
+  unlink_dir (out);
+  mkdir (out, 0755);
+  pwd = getcwd (NULL, 0);
+  gcc_assert (pwd);
+  ret = chdir (out);
+  gcc_assert (ret == 0);
+
+  gcov_profile_scale2 (d1_profile, n, d);
+
+  set_gcov_list (d1_profile);
+  gcov_exit ();
+
+  ret = chdir (pwd);
+  free (pwd);
+  return 0;
+}
+
+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 profile */
+  unlink_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;
+}
+
+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 }
+};
+
+static void
+rewrite_usage (void)
+{
+  fnotice (stderr, "Rewrite subcommand usage:");
+  print_rewrite_usage_message (true);
+  exit (FATAL_EXIT_CODE);
+}
+
+static int
+do_rewrite (int argc, char **argv)
+{
+  int opt;
+  int ret;
+  const char *output_dir = 0;
+  long long normalize_val = 0;
+  float scale = 1.0;
+  int numerator = -1; 
+  int denominator = -1;
+
+  while ((opt = getopt_long (argc, argv, "vo:s:n:", rewrite_options, NULL)) != -1)
+    {
+      switch (opt)
+        {
+        case 'v':
+          verbose = 1;
+          gcov_tool_set_verbose ();
+          break;
+        case 'o':
+          output_dir = optarg;
+          break;
+        case 'n':
+          if (scale != 1.0)
+            {
+              fnotice (stderr, "scaling cannot co-exist with normalization\n");
+              scale = 1.0;
+            }
+          normalize_val = atoll (optarg);
+          break;
+        case 's':
+          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");
+  print_merge_usage_message (error_p);
+  print_rewrite_usage_message (error_p);
+  fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
+           bug_report_url);
+  exit (status);
+}
+
+/* Print version information and exit.  */
+
+static void
+print_version (void)
+{
+  fnotice (stdout, "%s %s%s\n", progname, pkgversion_string, version_string);
+  fprintf (stdout, "Copyright %s 2013 Free Software Foundation, Inc.\n",
+           _("(C)"));
+  fnotice (stdout,
+           _("This is free software; see the source for copying conditions.\n"
+             "There is NO warranty; not even for MERCHANTABILITY or \n"
+             "FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
+  exit (SUCCESS_EXIT_CODE);
+}
+
+static const struct option options[] =
+{
+  { "help",                 no_argument,       NULL, 'h' },
+  { "version",              no_argument,       NULL, 'v' },
+  { 0, 0, 0, 0 }
+};
+
+/* Process args, return index to first non-arg.  */
+
+static int
+process_args (int argc, char **argv)
+{
+  int opt;
+
+  while ((opt = getopt_long (argc, argv, "+hv", options, NULL)) != -1)  
+    {    
+      switch (opt)
+        {    
+        case 'h': 
+          print_usage (false);
+          /* print_usage will exit.  */
+        case 'v':
+          print_version ();
+          /* print_version will exit.  */
+        default:
+          print_usage (true);
+          /* print_usage will exit.  */
+        }
+    }
+
+  return optind;
+}
+
+int
+main (int argc, char **argv)
+{
+  const char *p;
+  const char *sub_command;
+
+  p = argv[0] + strlen (argv[0]);
+  while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
+    --p;
+  progname = p;
+
+  xmalloc_set_program_name (progname);
+
+  /* Unlock the stdio streams.  */
+  unlock_std_streams ();
+
+  gcc_init_libintl ();
+
+  diagnostic_initialize (global_dc, 0);
+
+  /* Handle response files.  */
+  expandargv (&argc, &argv);
+
+  process_args (argc, argv);
+  if (optind >= argc)
+    print_usage (true);
+
+  sub_command = argv[optind];
+
+  if (!strcmp (sub_command, "merge"))
+    return do_merge (argc - optind, argv + optind);
+  else if (!strcmp (sub_command, "rewrite"))
+    return do_rewrite (argc - optind, argv + optind);
+
+  print_usage (true);
+}
Index: gcc/Makefile.in
===================================================================
--- gcc/Makefile.in	(revision 204895)
+++ 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-tool.o-warn = -Wno-error
 
 # All warnings have to be shut off in stage1 if the compiler used then
 # isn't gcc; configure determines that.  WARN_CFLAGS will be either
@@ -1486,7 +1488,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) libgcov-tool.o lto-wrapper.o
 
 # This lists all host object files, whether they are included in this
 # compilation or not.
@@ -1511,6 +1513,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
 
@@ -2566,6 +2569,16 @@ GCOV_DUMP_OBJS = gcov-dump.o
 gcov-dump$(exeext): $(GCOV_DUMP_OBJS) $(LIBDEPS)
 	+$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_DUMP_OBJS) \
 		$(LIBS) -o $@
+libgcov-tool.o: $(srcdir)/../libgcc/libgcov-tool.c gcov-io.c $(GCOV_IO_H) \
+  $(srcdir)/../libgcc/libgcov-driver.c $(srcdir)/../libgcc/libgcov-driver-system.c \
+  $(srcdir)/../libgcc/libgcov-merge.c \
+  $(SYSTEM_H) coretypes.h $(TM_H) $(CONFIG_H) version.h intl.h $(DIAGNOSTIC_H)
+	+$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -o $@ $<
+
+GCOV_TOOL_OBJS = gcov-tool.o libgcov-tool.o
+gcov-tool$(exeext): $(GCOV_TOOL_OBJS) $(LIBDEPS)
+	+$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_TOOL_OBJS) \
+	  $(LIBS) -o $@
 #\f
 # Build the include directories.  The stamp files are stmp-* rather than
 # s-* so that mostlyclean does not force the include directory to
@@ -3189,6 +3202,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-io.c
===================================================================
--- gcc/gcov-io.c	(revision 204895)
+++ gcc/gcov-io.c	(working copy)
@@ -36,6 +36,37 @@ static const gcov_unsigned_t *gcov_read_words (uns
 static void gcov_allocate (unsigned);
 #endif
 
+/* Moved for gcov-io.h and make it static.  */
+static struct gcov_var gcov_var;
+
+/* Save the current position in the gcov file.  */
+static inline gcov_position_t
+gcov_position (void)
+{
+  gcc_assert (gcov_var.mode > 0); 
+  return gcov_var.start + gcov_var.offset;
+}
+
+/* Return nonzero if the error flag is set.  */
+static inline int 
+gcov_is_error (void)
+{
+  return gcov_var.file ? gcov_var.error : 1;
+}
+
+#if IN_LIBGCOV
+/* Move to beginning of file and initialize for writing.  */
+static inline void
+gcov_rewrite (void)
+{
+  gcc_assert (gcov_var.mode > 0); 
+  gcov_var.mode = -1; 
+  gcov_var.start = 0;
+  gcov_var.offset = 0;
+  fseek (gcov_var.file, 0L, SEEK_SET);
+}
+#endif
+
 static inline gcov_unsigned_t from_file (gcov_unsigned_t value)
 {
 #if !IN_LIBGCOV
@@ -503,7 +534,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)
 {
@@ -580,7 +611,7 @@ gcov_read_summary (struct gcov_summary *summary)
     }
 }
 
-#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/gcov-io.h
===================================================================
--- gcc/gcov-io.h	(revision 204895)
+++ gcc/gcov-io.h	(working copy)
@@ -164,51 +164,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
 #ifndef GCC_GCOV_IO_H
 #define GCC_GCOV_IO_H
 
-#if IN_LIBGCOV
-/* About the target */
-
-#if BITS_PER_UNIT == 8
-typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI)));
-typedef unsigned gcov_position_t __attribute__ ((mode (SI)));
-#if LONG_LONG_TYPE_SIZE > 32
-typedef signed gcov_type __attribute__ ((mode (DI)));
-typedef unsigned gcov_type_unsigned __attribute__ ((mode (DI)));
-#else
-typedef signed gcov_type __attribute__ ((mode (SI)));
-typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
-#endif
-#else
-#if BITS_PER_UNIT == 16
-typedef unsigned gcov_unsigned_t __attribute__ ((mode (HI)));
-typedef unsigned gcov_position_t __attribute__ ((mode (HI)));
-#if LONG_LONG_TYPE_SIZE > 32
-typedef signed gcov_type __attribute__ ((mode (SI)));
-typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
-#else
-typedef signed gcov_type __attribute__ ((mode (HI)));
-typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
-#endif
-#else
-typedef unsigned gcov_unsigned_t __attribute__ ((mode (QI)));
-typedef unsigned gcov_position_t __attribute__ ((mode (QI)));
-#if LONG_LONG_TYPE_SIZE > 32
-typedef signed gcov_type __attribute__ ((mode (HI)));
-typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
-#else
-typedef signed gcov_type __attribute__ ((mode (QI)));
-typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI)));
-#endif
-#endif
-#endif
-
-
-#if defined (TARGET_POSIX_IO)
-#define GCOV_LOCKED 1
-#else
-#define GCOV_LOCKED 0
-#endif
-
-#else /* !IN_LIBGCOV */
+#ifndef IN_LIBGCOV
 /* About the host */
 
 typedef unsigned gcov_unsigned_t;
@@ -231,48 +187,10 @@ typedef unsigned HOST_WIDEST_INT gcov_type_unsigne
 #define GCOV_LOCKED 0
 #endif
 
-#endif /* !IN_LIBGCOV */
-
-/* In gcov we want function linkage to be static.  In the compiler we want
-   it extern, so that they can be accessed from elsewhere.  In libgcov we
-   need these functions to be extern, so prefix them with __gcov.  In
-   libgcov they must also be hidden so that the instance in the executable
-   is not also used in a DSO.  */
-#if IN_LIBGCOV
-
-#include "tconfig.h"
-
-#define gcov_var __gcov_var
-#define gcov_open __gcov_open
-#define gcov_close __gcov_close
-#define gcov_write_tag_length __gcov_write_tag_length
-#define gcov_position __gcov_position
-#define gcov_seek __gcov_seek
-#define gcov_rewrite __gcov_rewrite
-#define gcov_is_error __gcov_is_error
-#define gcov_write_unsigned __gcov_write_unsigned
-#define gcov_write_counter __gcov_write_counter
-#define gcov_write_summary __gcov_write_summary
-#define gcov_read_unsigned __gcov_read_unsigned
-#define gcov_read_counter __gcov_read_counter
-#define gcov_read_summary __gcov_read_summary
-
-/* Poison these, so they don't accidentally slip in.  */
-#pragma GCC poison gcov_write_string gcov_write_tag gcov_write_length
-#pragma GCC poison gcov_read_string gcov_sync gcov_time gcov_magic
-
-#ifdef HAVE_GAS_HIDDEN
-#define ATTRIBUTE_HIDDEN  __attribute__ ((__visibility__ ("hidden")))
-#else
 #define ATTRIBUTE_HIDDEN
-#endif
 
-#else
+#endif /* !IN_LIBGOCV */
 
-#define ATTRIBUTE_HIDDEN
-
-#endif
-
 #ifndef GCOV_LINKAGE
 #define GCOV_LINKAGE extern
 #endif
@@ -342,10 +260,11 @@ typedef unsigned HOST_WIDEST_INT gcov_type_unsigne
 				      counter.  */
 #define GCOV_COUNTER_IOR	7  /* IOR of the all values passed to
 				      counter.  */
-#define GCOV_TIME_PROFILER  8 /* Time profile collecting first run of a function */
+#define GCOV_TIME_PROFILER      8  /* Time profile collecting first run of a
+                                      function */
 #define GCOV_LAST_VALUE_COUNTER 8  /* The last of counters used for value
-				      profiling.  */
-#define GCOV_COUNTERS		9
+                                      profiling.  */
+#define GCOV_COUNTERS           9
 
 /* Number of counters used for value profiling.  */
 #define GCOV_N_VALUE_COUNTERS \
@@ -353,7 +272,8 @@ typedef unsigned HOST_WIDEST_INT gcov_type_unsigne
 
   /* A list of human readable names of the counters */
 #define GCOV_COUNTER_NAMES	{"arcs", "interval", "pow2", "single", \
-              "delta", "indirect_call", "average", "ior", "time_profiler"}
+      				 "delta", "indirect_call", "average", "ior", \
+      				 "time_profiler"}
 
   /* Names of merge functions for counters.  */
 #define GCOV_MERGE_FUNCTIONS	{"__gcov_merge_add",	\
@@ -363,8 +283,8 @@ typedef unsigned HOST_WIDEST_INT gcov_type_unsigne
 				 "__gcov_merge_delta",  \
 				 "__gcov_merge_single", \
 				 "__gcov_merge_add",	\
-				 "__gcov_merge_ior",  \
-         "__gcov_merge_time_profile" }
+				 "__gcov_merge_ior",    \
+				 "__gcov_merge_time_profile"}
 
 /* Convert a counter index to a tag.  */
 #define GCOV_TAG_FOR_COUNTER(COUNT)				\
@@ -442,110 +362,12 @@ struct gcov_summary
   struct gcov_ctr_summary ctrs[GCOV_COUNTERS_SUMMABLE];
 };
 
-/* Structures embedded in coveraged program.  The structures generated
-   by write_profile must match these.  */
+#if !defined(inhibit_libc)
 
-#if IN_LIBGCOV
-/* Information about counters for a single function.  */
-struct gcov_ctr_info
-{
-  gcov_unsigned_t num;		/* number of counters.  */
-  gcov_type *values;		/* their values.  */
-};
-
-/* Information about a single function.  This uses the trailing array
-   idiom. The number of counters is determined from the merge pointer
-   array in gcov_info.  The key is used to detect which of a set of
-   comdat functions was selected -- it points to the gcov_info object
-   of the object file containing the selected comdat function.  */
-
-struct gcov_fn_info
-{
-  const struct gcov_info *key;		/* comdat key */
-  gcov_unsigned_t ident;		/* unique ident of function */
-  gcov_unsigned_t lineno_checksum;	/* function lineo_checksum */
-  gcov_unsigned_t cfg_checksum;		/* function cfg checksum */
-  struct gcov_ctr_info ctrs[0];		/* instrumented counters */
-};
-
-/* Type of function used to merge counters.  */
-typedef void (*gcov_merge_fn) (gcov_type *, gcov_unsigned_t);
-
-/* Information about a single object file.  */
-struct gcov_info
-{
-  gcov_unsigned_t version;	/* expected version number */
-  struct gcov_info *next;	/* link to next, used by libgcov */
-
-  gcov_unsigned_t stamp;	/* uniquifying time stamp */
-  const char *filename;		/* output file name */
-
-  gcov_merge_fn merge[GCOV_COUNTERS];  /* merge functions (null for
-					  unused) */
-  
-  unsigned n_functions;		/* number of functions */
-  const struct gcov_fn_info *const *functions; /* pointer to pointers
-					          to function information  */
-};
-
-/* Register a new object file module.  */
-extern void __gcov_init (struct gcov_info *) ATTRIBUTE_HIDDEN;
-
-/* Called before fork, to avoid double counting.  */
-extern void __gcov_flush (void) ATTRIBUTE_HIDDEN;
-
-/* Function to reset all counters to 0.  */
-extern void __gcov_reset (void);
-
-/* Function to enable early write of profile information so far.  */
-extern void __gcov_dump (void);
-
-/* The merge function that just sums the counters.  */
-extern void __gcov_merge_add (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
-
-/* The merge function to choose the most common value.  */
-extern void __gcov_merge_single (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
-
-/* The merge function to choose the most common difference between
-   consecutive values.  */
-extern void __gcov_merge_delta (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
-
-/* The merge function that just ors the counters together.  */
-extern void __gcov_merge_ior (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
-
-extern void __gcov_merge_time_profile (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
-
-/* The profiler functions.  */
-extern void __gcov_interval_profiler (gcov_type *, gcov_type, int, unsigned);
-extern void __gcov_pow2_profiler (gcov_type *, gcov_type);
-extern void __gcov_one_value_profiler (gcov_type *, gcov_type);
-extern void __gcov_indirect_call_profiler (gcov_type*, gcov_type,
-                                           void*, void*);
-extern void __gcov_indirect_call_profiler_v2 (gcov_type, void *);
-extern void __gcov_average_profiler (gcov_type *, gcov_type);
-extern void __gcov_ior_profiler (gcov_type *, gcov_type);
-extern void __gcov_time_profiler (gcov_type *);
-
-#ifndef inhibit_libc
-/* The wrappers around some library functions..  */
-extern pid_t __gcov_fork (void) ATTRIBUTE_HIDDEN;
-extern int __gcov_execl (const char *, char *, ...) ATTRIBUTE_HIDDEN;
-extern int __gcov_execlp (const char *, char *, ...) ATTRIBUTE_HIDDEN;
-extern int __gcov_execle (const char *, char *, ...) ATTRIBUTE_HIDDEN;
-extern int __gcov_execv (const char *, char *const []) ATTRIBUTE_HIDDEN;
-extern int __gcov_execvp (const char *, char *const []) ATTRIBUTE_HIDDEN;
-extern int __gcov_execve (const char *, char  *const [], char *const [])
-  ATTRIBUTE_HIDDEN;
-#endif
-
-#endif /* IN_LIBGCOV */
-
-#if IN_LIBGCOV >= 0
-
 /* Optimum number of gcov_unsigned_t's read from or written to disk.  */
 #define GCOV_BLOCK_SIZE (1 << 10)
 
-GCOV_LINKAGE struct gcov_var
+struct gcov_var
 {
   FILE *file;
   gcov_position_t start;	/* Position of first byte of block */
@@ -567,7 +389,7 @@ struct gcov_summary
   size_t alloc;
   gcov_unsigned_t *buffer;
 #endif
-} gcov_var ATTRIBUTE_HIDDEN;
+};
 
 /* Functions for reading and writing gcov files. In libgcov you can
    open the file for reading then writing. Elsewhere you can open the
@@ -578,52 +400,33 @@ struct gcov_summary
    you use the functions for reading, then gcov_rewrite then the
    functions for writing.  Your file may become corrupted if you break
    these invariants.  */
-#if IN_LIBGCOV
-GCOV_LINKAGE int gcov_open (const char */*name*/) ATTRIBUTE_HIDDEN;
-#else
-GCOV_LINKAGE int gcov_open (const char */*name*/, int /*direction*/);
-GCOV_LINKAGE int gcov_magic (gcov_unsigned_t, gcov_unsigned_t);
-#endif
-GCOV_LINKAGE int gcov_close (void) ATTRIBUTE_HIDDEN;
 
 /* Available everywhere.  */
-static gcov_position_t gcov_position (void);
-static int gcov_is_error (void);
-
+GCOV_LINKAGE int gcov_close (void) ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE gcov_unsigned_t gcov_read_unsigned (void) ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE gcov_type gcov_read_counter (void) ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE void gcov_read_summary (struct gcov_summary *) ATTRIBUTE_HIDDEN;
-
-#if IN_LIBGCOV
-/* Available only in libgcov */
-GCOV_LINKAGE void gcov_write_counter (gcov_type) ATTRIBUTE_HIDDEN;
-GCOV_LINKAGE void gcov_write_tag_length (gcov_unsigned_t, gcov_unsigned_t)
-    ATTRIBUTE_HIDDEN;
-GCOV_LINKAGE void gcov_write_summary (gcov_unsigned_t /*tag*/,
-				      const struct gcov_summary *)
-    ATTRIBUTE_HIDDEN;
-static void gcov_rewrite (void);
-GCOV_LINKAGE void gcov_seek (gcov_position_t /*position*/) ATTRIBUTE_HIDDEN;
-#else
-/* Available outside libgcov */
 GCOV_LINKAGE const char *gcov_read_string (void);
 GCOV_LINKAGE void gcov_sync (gcov_position_t /*base*/,
 			     gcov_unsigned_t /*length */);
-#endif
 
-#if !IN_GCOV
-/* Available outside gcov */
-GCOV_LINKAGE void gcov_write_unsigned (gcov_unsigned_t) ATTRIBUTE_HIDDEN;
-#endif
+#if !IN_LIBGCOV
+GCOV_LINKAGE int gcov_open (const char */*name*/, int /*direction*/);
+GCOV_LINKAGE int gcov_magic (gcov_unsigned_t, gcov_unsigned_t);
+#endif 
 
-#if !IN_GCOV && !IN_LIBGCOV
-/* Available only in compiler */
+#if !IN_LIBGCOV && !IN_GCOV
+/* in compiler only */
 GCOV_LINKAGE unsigned gcov_histo_index (gcov_type value);
 GCOV_LINKAGE void gcov_write_string (const char *);
 GCOV_LINKAGE gcov_position_t gcov_write_tag (gcov_unsigned_t);
 GCOV_LINKAGE void gcov_write_length (gcov_position_t /*position*/);
 #endif
 
+#if !IN_GCOV
+GCOV_LINKAGE void gcov_write_unsigned (gcov_unsigned_t) ATTRIBUTE_HIDDEN;
+#endif
+
 #if IN_GCOV <= 0 && !IN_LIBGCOV
 /* Available in gcov-dump and the compiler.  */
 
@@ -646,42 +449,11 @@ GCOV_LINKAGE void compute_working_sets (const stru
                                         gcov_working_set_t *gcov_working_sets);
 #endif
 
-#if IN_GCOV > 0
-/* Available in gcov */
+#if IN_GCOV>0
+/* Available only in gcov */
 GCOV_LINKAGE time_t gcov_time (void);
 #endif
 
-/* Save the current position in the gcov file.  */
+#endif /* !inhibit_libc  */
 
-static inline gcov_position_t
-gcov_position (void)
-{
-  gcc_assert (gcov_var.mode > 0);
-  return gcov_var.start + gcov_var.offset;
-}
-
-/* Return nonzero if the error flag is set.  */
-
-static inline int
-gcov_is_error (void)
-{
-  return gcov_var.file ? gcov_var.error : 1;
-}
-
-#if IN_LIBGCOV
-/* Move to beginning of file and initialize for writing.  */
-
-static inline void
-gcov_rewrite (void)
-{
-  gcc_assert (gcov_var.mode > 0);
-  gcov_var.mode = -1;
-  gcov_var.start = 0;
-  gcov_var.offset = 0;
-  fseek (gcov_var.file, 0L, SEEK_SET);
-}
-#endif
-
-#endif /* IN_LIBGCOV >= 0 */
-
 #endif /* GCC_GCOV_IO_H */
Index: libgcc/libgcov-profiler.c
===================================================================
--- libgcc/libgcov-profiler.c	(revision 204895)
+++ libgcc/libgcov-profiler.c	(working copy)
@@ -23,15 +23,8 @@ a copy of the GCC Runtime Library Exception along
 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 <http://www.gnu.org/licenses/>.  */
 
-#include "tconfig.h"
-#include "tsystem.h"
-#include "coretypes.h"
-#include "tm.h"
-#include "libgcc_tm.h"
-
+#include "libgcov.h"
 #if !defined(inhibit_libc)
-#define IN_LIBGCOV 1
-#include "gcov-io.h"
 
 #ifdef L_gcov_interval_profiler
 /* If VALUE is in interval <START, START + STEPS - 1>, then increases the
Index: libgcc/libgcov-driver.c
===================================================================
--- libgcc/libgcov-driver.c	(revision 204895)
+++ libgcc/libgcov-driver.c	(working copy)
@@ -23,23 +23,9 @@ a copy of the GCC Runtime Library Exception along
 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 <http://www.gnu.org/licenses/>.  */
 
-#include "tconfig.h"
-#include "tsystem.h"
-#include "coretypes.h"
-#include "tm.h"
-#include "libgcc_tm.h"
+#include "libgcov.h"
 
 #if defined(inhibit_libc)
-#define IN_LIBGCOV (-1)
-#else
-#define IN_LIBGCOV 1
-#if defined(L_gcov)
-#define GCOV_LINKAGE /* nothing */
-#endif
-#endif
-#include "gcov-io.h"
-
-#if defined(inhibit_libc)
 /* If libc and its header files are not available, provide dummy functions.  */
 
 #if defined(L_gcov)
@@ -156,7 +142,7 @@ buffer_fn_data (const char *filename, const struct
       n_ctrs++;
 
   len = sizeof (*fn_buffer) + sizeof (fn_buffer->info.ctrs[0]) * n_ctrs;
-  fn_buffer = (struct gcov_fn_buffer *)malloc (len);
+  fn_buffer = (struct gcov_fn_buffer *) xmalloc (len);
 
   if (!fn_buffer)
     goto fail;
@@ -183,7 +169,7 @@ buffer_fn_data (const char *filename, const struct
 
       length = GCOV_TAG_COUNTER_NUM (gcov_read_unsigned ());
       len = length * sizeof (gcov_type);
-      values = (gcov_type *)malloc (len);
+      values = (gcov_type *) xmalloc (len);
       if (!values)
         goto fail;
 
@@ -325,6 +311,9 @@ static struct gcov_summary all_prg;
 #endif
 /* crc32 for this program.  */
 static gcov_unsigned_t crc32;
+/* Use this summary checksum rather the computed one if the value is
+ *    non-zero.  */
+static gcov_unsigned_t saved_summary_checksum;
 /* gcda filename.  */
 static char *gi_filename;
 /* buffer for the fn_data from another program.  */
@@ -453,7 +442,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);
@@ -529,7 +518,7 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
           if (tag != GCOV_TAG_FOR_COUNTER (t_ix)
               || length != GCOV_TAG_COUNTER_LENGTH (ci_ptr->num))
             goto read_mismatch;
-          (*merge) (ci_ptr->values, ci_ptr->num);
+          (*merge) (ci_ptr->values, ci_ptr->num, NULL, 1);
           ci_ptr++;
         }
       if ((error = gcov_is_error ()))
@@ -715,9 +704,12 @@ gcov_exit_merge_summary (const struct gcov_info *g
              }
 #endif
     }
+  
+  if (saved_summary_checksum)
+    prg->checksum = saved_summary_checksum;
+  else
+    prg->checksum = crc32;
 
-  prg->checksum = crc32;
-
   return 0;
 }
 
Index: libgcc/libgcov-interface.c
===================================================================
--- libgcc/libgcov-interface.c	(revision 204895)
+++ libgcc/libgcov-interface.c	(working copy)
@@ -23,22 +23,11 @@ a copy of the GCC Runtime Library Exception along
 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 <http://www.gnu.org/licenses/>.  */
 
-#include "tconfig.h"
-#include "tsystem.h"
-#include "coretypes.h"
-#include "tm.h"
-#include "libgcc_tm.h"
+#include "libgcov.h"
 #include "gthr.h"
 
 #if defined(inhibit_libc)
-#define IN_LIBGCOV (-1)
-#else
-#define IN_LIBGCOV 1
-#endif
-#include "gcov-io.h"
 
-#if defined(inhibit_libc)
-
 #ifdef L_gcov_flush
 void __gcov_flush (void) {}
 #endif
Index: libgcc/libgcov.h
===================================================================
--- libgcc/libgcov.h	(revision 0)
+++ libgcc/libgcov.h	(revision 0)
@@ -0,0 +1,252 @@
+/* Header file for libgcov-*.c.
+   Contributed by Rong Xu <xur@google.com>.
+   Copyright (C) 2013 Free Software Foundation, Inc.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it under
+   the terms of the GNU General Public License as published by the Free
+   Software Foundation; either version 3, or (at your option) any later
+   version.
+
+   GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+   for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GCC; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_LIBGCOV_H
+#define GCC_LIBGCOV_H
+
+/* work around the poisoned malloc/calloc in system.h.  */
+#ifndef xmalloc
+#define xmalloc malloc 
+#endif
+#ifndef xcalloc
+#define xcalloc calloc 
+#endif
+
+#ifndef LIBGCOV_FOR_HOST
+/* About the target */
+
+#include "tconfig.h"
+#include "tsystem.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "libgcc_tm.h"
+
+#if BITS_PER_UNIT == 8
+typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI)));
+typedef unsigned gcov_position_t __attribute__ ((mode (SI)));
+#if LONG_LONG_TYPE_SIZE > 32
+typedef signed gcov_type __attribute__ ((mode (DI)));
+typedef unsigned gcov_type_unsigned __attribute__ ((mode (DI)));
+#else
+typedef signed gcov_type __attribute__ ((mode (SI)));
+typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
+#endif
+#else
+#if BITS_PER_UNIT == 16
+typedef unsigned gcov_unsigned_t __attribute__ ((mode (HI)));
+typedef unsigned gcov_position_t __attribute__ ((mode (HI)));
+#if LONG_LONG_TYPE_SIZE > 32
+typedef signed gcov_type __attribute__ ((mode (SI)));
+typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
+#else
+typedef signed gcov_type __attribute__ ((mode (HI)));
+typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
+#endif
+#else
+typedef unsigned gcov_unsigned_t __attribute__ ((mode (QI)));
+typedef unsigned gcov_position_t __attribute__ ((mode (QI)));
+#if LONG_LONG_TYPE_SIZE > 32
+typedef signed gcov_type __attribute__ ((mode (HI)));
+typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
+#else
+typedef signed gcov_type __attribute__ ((mode (QI)));
+typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI)));
+#endif
+#endif
+#endif
+
+#if defined (TARGET_POSIX_IO)
+#define GCOV_LOCKED 1
+#else
+#define GCOV_LOCKED 0
+#endif
+
+#else /* ! LIBGCOV_FOR_HOST_BUILD */
+/* 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 /* ! LIBGCOV_FOR_HOST_BUILD */
+
+#if defined(inhibit_libc)
+#define IN_LIBGCOV (-1)
+#else
+#define IN_LIBGCOV 1
+#if defined(L_gcov)
+#define GCOV_LINKAGE /* nothing */
+#endif
+#endif
+
+/* In libgcov we need these functions to be extern, so prefix them with
+   __gcov.  In libgcov they must also be hidden so that the instance in
+   the executable is not also used in a DSO.  */
+#define gcov_var __gcov_var
+#define gcov_open __gcov_open
+#define gcov_close __gcov_close
+#define gcov_write_tag_length __gcov_write_tag_length
+#define gcov_position __gcov_position
+#define gcov_seek __gcov_seek
+#define gcov_rewrite __gcov_rewrite
+#define gcov_is_error __gcov_is_error
+#define gcov_write_unsigned __gcov_write_unsigned
+#define gcov_write_counter __gcov_write_counter
+#define gcov_write_summary __gcov_write_summary
+#define gcov_read_unsigned __gcov_read_unsigned
+#define gcov_read_counter __gcov_read_counter
+#define gcov_read_summary __gcov_read_summary
+
+/* Poison these, so they don't accidentally slip in.  */
+#pragma GCC poison gcov_write_string gcov_write_tag gcov_write_length
+#pragma GCC poison gcov_time gcov_magic
+
+#ifdef HAVE_GAS_HIDDEN
+#define ATTRIBUTE_HIDDEN  __attribute__ ((__visibility__ ("hidden")))
+#else
+#define ATTRIBUTE_HIDDEN
+#endif
+
+#include "gcov-io.h"
+
+/* Structures embedded in coveraged program.  The structures generated
+   by write_profile must match these.  */
+
+/* Information about counters for a single function.  */
+struct gcov_ctr_info
+{
+  gcov_unsigned_t num;		/* number of counters.  */
+  gcov_type *values;		/* their values.  */
+};
+
+/* Information about a single function.  This uses the trailing array
+   idiom. The number of counters is determined from the merge pointer
+   array in gcov_info.  The key is used to detect which of a set of
+   comdat functions was selected -- it points to the gcov_info object
+   of the object file containing the selected comdat function.  */
+
+struct gcov_fn_info
+{
+  const struct gcov_info *key;		/* comdat key */
+  gcov_unsigned_t ident;		/* unique ident of function */
+  gcov_unsigned_t lineno_checksum;	/* function lineo_checksum */
+  gcov_unsigned_t cfg_checksum;		/* function cfg checksum */
+  struct gcov_ctr_info ctrs[0];		/* instrumented counters */
+};
+
+/* Type of function used to merge counters.  */
+typedef void (*gcov_merge_fn) (gcov_type *, gcov_unsigned_t,
+                               gcov_type *, unsigned);
+
+/* Information about a single object file.  */
+struct gcov_info
+{
+  gcov_unsigned_t version;	/* expected version number */
+  struct gcov_info *next;	/* link to next, used by libgcov */
+
+  gcov_unsigned_t stamp;	/* uniquifying time stamp */
+  const char *filename;		/* output file name */
+
+  gcov_merge_fn merge[GCOV_COUNTERS];  /* merge functions (null for
+					  unused) */
+  
+  unsigned n_functions;		/* number of functions */
+#if !LIBGCOV_FOR_HOST
+  const struct gcov_fn_info *const *functions; /* pointer to pointers
+					          to function information  */
+#else
+  const struct gcov_fn_info **functions;
+#endif /* !!LIBGCOV_FOR_HOST */
+};
+
+/* Register a new object file module.  */
+extern void __gcov_init (struct gcov_info *) ATTRIBUTE_HIDDEN;
+
+/* Called before fork, to avoid double counting.  */
+extern void __gcov_flush (void) ATTRIBUTE_HIDDEN;
+
+/* Function to reset all counters to 0.  */
+extern void __gcov_reset (void);
+
+/* Function to enable early write of profile information so far.  */
+extern void __gcov_dump (void);
+
+/* The merge function that just sums the counters.  */
+extern void __gcov_merge_add (gcov_type *, unsigned, gcov_type *,
+                              unsigned) ATTRIBUTE_HIDDEN;
+
+/* The merge function to choose the most common value.  */
+extern void __gcov_merge_single (gcov_type *, unsigned, gcov_type *,
+                              unsigned) ATTRIBUTE_HIDDEN;
+
+/* The merge function to choose the most common difference between
+   consecutive values.  */
+extern void __gcov_merge_delta (gcov_type *, unsigned, gcov_type *,
+                              unsigned) ATTRIBUTE_HIDDEN;
+
+/* The merge function that just ors the counters together.  */
+extern void __gcov_merge_ior (gcov_type *, unsigned, gcov_type *,
+                              unsigned) ATTRIBUTE_HIDDEN;
+
+/* The profiler functions.  */
+extern void __gcov_interval_profiler (gcov_type *, gcov_type, int, unsigned);
+extern void __gcov_pow2_profiler (gcov_type *, gcov_type);
+extern void __gcov_one_value_profiler (gcov_type *, gcov_type);
+extern void __gcov_indirect_call_profiler (gcov_type*, gcov_type,
+                                           void*, void*);
+extern void __gcov_indirect_call_profiler_v2 (gcov_type, void *);
+extern void __gcov_average_profiler (gcov_type *, gcov_type);
+extern void __gcov_ior_profiler (gcov_type *, gcov_type);
+
+#ifndef inhibit_libc
+/* The wrappers around some library functions..  */
+extern pid_t __gcov_fork (void) ATTRIBUTE_HIDDEN;
+extern int __gcov_execl (const char *, char *, ...) ATTRIBUTE_HIDDEN;
+extern int __gcov_execlp (const char *, char *, ...) ATTRIBUTE_HIDDEN;
+extern int __gcov_execle (const char *, char *, ...) ATTRIBUTE_HIDDEN;
+extern int __gcov_execv (const char *, char *const []) ATTRIBUTE_HIDDEN;
+extern int __gcov_execvp (const char *, char *const []) ATTRIBUTE_HIDDEN;
+extern int __gcov_execve (const char *, char  *const [], char *const [])
+  ATTRIBUTE_HIDDEN;
+
+/* Functions that only available in libgcov.  */
+GCOV_LINKAGE int gcov_open (const char */*name*/) ATTRIBUTE_HIDDEN;
+GCOV_LINKAGE void gcov_write_counter (gcov_type) ATTRIBUTE_HIDDEN;
+GCOV_LINKAGE void gcov_write_tag_length (gcov_unsigned_t, gcov_unsigned_t)
+    ATTRIBUTE_HIDDEN;
+GCOV_LINKAGE void gcov_write_summary (gcov_unsigned_t /*tag*/,
+                                      const struct gcov_summary *)
+    ATTRIBUTE_HIDDEN;
+GCOV_LINKAGE void gcov_seek (gcov_position_t /*position*/) ATTRIBUTE_HIDDEN;
+static inline void gcov_rewrite (void);
+
+#endif /* !inhibit_libc */
+
+#endif /* GCC_LIBGCOV_H */
Index: libgcc/libgcov-driver-system.c
===================================================================
--- libgcc/libgcov-driver-system.c	(revision 204895)
+++ libgcc/libgcov-driver-system.c	(working copy)
@@ -124,7 +124,7 @@ allocate_filename_struct (struct gcov_filename_aux
       prefix_length = 1;
     }
   /* Allocate and initialize the filename scratch space plus one.  */
-  gi_filename = (char *) malloc (prefix_length + gcov_max_filename + 2);
+  gi_filename = (char *) xmalloc (prefix_length + gcov_max_filename + 2);
   if (prefix_length)
     memcpy (gi_filename, gcov_prefix, prefix_length);
   gi_filename_up = gi_filename + prefix_length;
Index: libgcc/libgcov-merge.c
===================================================================
--- libgcc/libgcov-merge.c	(revision 204895)
+++ libgcc/libgcov-merge.c	(working copy)
@@ -23,107 +23,150 @@ a copy of the GCC Runtime Library Exception along
 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 <http://www.gnu.org/licenses/>.  */
 
-#include "tconfig.h"
-#include "tsystem.h"
-#include "coretypes.h"
-#include "tm.h"
-#include "libgcc_tm.h"
+#include "libgcov.h"
 
-#if defined(inhibit_libc)
-#define IN_LIBGCOV (-1)
-#else
-#define IN_LIBGCOV 1
+#include "gcov-io-libgcov.h"
 #endif
 
-#include "gcov-io.h"
-
 #if defined(inhibit_libc)
 /* If libc and its header files are not available, provide dummy functions.  */
 
 #ifdef L_gcov_merge_add
 void __gcov_merge_add (gcov_type *counters  __attribute__ ((unused)),
-                       unsigned n_counters __attribute__ ((unused))) {}
+                       unsigned n_counters __attribute__ ((unused)),
+                       unsigned gcov_type *src __attribute__ ((unused)),
+                       unsigned w __attribute__ ((unused))) {}
 #endif
 
 #ifdef L_gcov_merge_single
 void __gcov_merge_single (gcov_type *counters  __attribute__ ((unused)),
-                          unsigned n_counters __attribute__ ((unused))) {}
+                          unsigned n_counters __attribute__ ((unused)),
+                          unsigned gcov_type *src __attribute__ ((unused)),
+                          unsigned w __attribute__ ((unused))) {}
 #endif
 
 #ifdef L_gcov_merge_delta
 void __gcov_merge_delta (gcov_type *counters  __attribute__ ((unused)),
-                         unsigned n_counters __attribute__ ((unused))) {}
+                         unsigned n_counters __attribute__ ((unused)),
+                         unsigned gcov_type *src __attribute__ ((unused)),
+                         unsigned w __attribute__ ((unused))) {}
 #endif
 
 #else
 
 #ifdef L_gcov_merge_add
 /* The profile merging function that just adds the counters.  It is given
-   an array COUNTERS of N_COUNTERS old counters and it reads the same number
-   of counters from the gcov file.  */
+   an array COUNTERS of N_COUNTERS old counters. 
+   When SRC==NULL, it reads the same number of counters from the gcov file.
+   Otherwise, it reads from SRC array. These read values will be multiplied
+   by weight W before adding to the old counters.  */
 void
-__gcov_merge_add (gcov_type *counters, unsigned n_counters)
+__gcov_merge_add (gcov_type *counters, unsigned n_counters,
+                  gcov_type *src, unsigned w)
 {
+  int in_mem = (src != 0);
+
   for (; n_counters; counters++, n_counters--)
-    *counters += gcov_read_counter ();
+    {
+      gcov_type value;
+
+      if (in_mem)
+        value = *(src++);
+      else
+        value = gcov_read_counter ();
+
+      *counters += w * value;
+    }
 }
 #endif /* L_gcov_merge_add */
 
 #ifdef L_gcov_merge_ior
 /* The profile merging function that just adds the counters.  It is given
-   an array COUNTERS of N_COUNTERS old counters and it reads the same number
-   of counters from the gcov file.  */
+   an array COUNTERS of N_COUNTERS old counters. 
+   When SRC==NULL, it reads the same number of counters from the gcov file.
+   Otherwise, it reads from SRC array.  */
 void
-__gcov_merge_ior (gcov_type *counters, unsigned n_counters)
+__gcov_merge_ior (gcov_type *counters, unsigned n_counters,
+                  gcov_type *src, unsigned w __attribute__ ((unused)))
 {
+  int in_mem = (src != 0);
+
   for (; n_counters; counters++, n_counters--)
-    *counters |= gcov_read_counter ();
+    {
+      gcov_type value;
+
+      if (in_mem)
+        value = *(src++);
+      else
+        value = gcov_read_counter ();
+
+      *counters |= value;
+    }
 }
 #endif
 
 #ifdef L_gcov_merge_time_profile
 /* Time profiles are merged so that minimum from all valid (greater than zero)
    is stored. There could be a fork that creates new counters. To have
-   the profile stable, we chosen to pick the smallest function visit time.  */
+   the profile stable, we chosen to pick the smallest function visit time.  */  
 void
-__gcov_merge_time_profile (gcov_type *counters, unsigned n_counters)
+__gcov_merge_time_profile (gcov_type *counters, unsigned n_counters,
+                           gcov_type *src, unsigned w __attribute__ ((unused)))
 {
   unsigned int i;
-  gcov_type value;
+  int in_mem = (src != 0);
 
   for (i = 0; i < n_counters; i++)
-    {
-      value = gcov_read_counter ();
+    {   
+      gcov_type value;
 
+      if (in_mem)
+        value = *(src++);
+      else
+        value = gcov_read_counter (); 
+
       if (value && (!counters[i] || value < counters[i]))
         counters[i] = value;
-    }
+    }   
 }
 #endif /* L_gcov_merge_time_profile */
 
 #ifdef L_gcov_merge_single
 /* The profile merging function for choosing the most common value.
-   It is given an array COUNTERS of N_COUNTERS old counters and it
-   reads the same number of counters from the gcov file.  The counters
-   are split into 3-tuples where the members of the tuple have
-   meanings:
+   It is given an array COUNTERS of N_COUNTERS old counters. 
+   When SRC==NULL, it reads the same number of counters from the gcov file.
+   Otherwise, it reads from SRC array. These read values will be multiplied
+   by weight W before mergeing. The counters are split into 3-tuples where
+   the members of the tuple have meanings:
 
    -- the stored candidate on the most common value of the measured entity
    -- counter
    -- total number of evaluations of the value  */
 void
-__gcov_merge_single (gcov_type *counters, unsigned n_counters)
+__gcov_merge_single (gcov_type *counters, unsigned n_counters,
+                     gcov_type *src, unsigned w)
 {
   unsigned i, n_measures;
   gcov_type value, counter, all;
+  int in_mem = (src != 0);
 
   gcc_assert (!(n_counters % 3));
   n_measures = n_counters / 3;
-  for (i = 0; i < n_measures; i++, counters += 3)
+  for (i = 0; i < n_measures; i++, counters += 3, src += 3)
     {
-      value = gcov_read_counter ();
-      counter = gcov_read_counter ();
-      all = gcov_read_counter ();
+      if (in_mem)
+        {
+          value = src[0];
+          counter = src[1];
+          all = src[2];
+        }
+      else
+        {
+          value = gcov_read_counter ();
+          counter = gcov_read_counter ();
+          all = gcov_read_counter ();
+        }
+      counter *= w;
 
       if (counters[0] == value)
         counters[1] += counter;
@@ -141,9 +184,10 @@ void
 
 #ifdef L_gcov_merge_delta
 /* The profile merging function for choosing the most common
-   difference between two consecutive evaluations of the value.  It is
-   given an array COUNTERS of N_COUNTERS old counters and it reads the
-   same number of counters from the gcov file.  The counters are split
+   It is given an array COUNTERS of N_COUNTERS old counters. 
+   When SRC==NULL, it reads the same number of counters from the gcov file.
+   Otherwise, it reads from SRC array. These read values will be multiplied
+   by weight W before mergeing.  The counters are split
    into 4-tuples where the members of the tuple have meanings:
 
    -- the last value of the measured entity
@@ -151,19 +195,31 @@ void
    -- counter
    -- total number of evaluations of the value  */
 void
-__gcov_merge_delta (gcov_type *counters, unsigned n_counters)
+__gcov_merge_delta (gcov_type *counters, unsigned n_counters,
+                    gcov_type *src, unsigned w)
 {
   unsigned i, n_measures;
   gcov_type value, counter, all;
+  int in_mem = (src != 0);
 
   gcc_assert (!(n_counters % 4));
   n_measures = n_counters / 4;
-  for (i = 0; i < n_measures; i++, counters += 4)
+  for (i = 0; i < n_measures; i++, counters += 4, src += 4)
     {
-      /* last = */ gcov_read_counter ();
-      value = gcov_read_counter ();
-      counter = gcov_read_counter ();
-      all = gcov_read_counter ();
+      if (in_mem)
+        {
+          value = src[1];
+          counter = src[2];
+          all = src[3];
+        }
+      else
+        {
+          /* last = */ gcov_read_counter ();
+          value = gcov_read_counter ();
+          counter = gcov_read_counter ();
+          all = gcov_read_counter ();
+        }
+      counter *= w;
 
       if (counters[1] == value)
         counters[2] += counter;
Index: libgcc/libgcov-tool.c
===================================================================
--- libgcc/libgcov-tool.c	(revision 0)
+++ libgcc/libgcov-tool.c	(revision 0)
@@ -0,0 +1,906 @@
+/* GCC gcov-tool support.
+   Contributed by Rong Xu <xur@google.com>.
+   Copyright (C) 2013 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#define LIBGCOV_FOR_HOST 1
+#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"
+
+/* We need the dumping and merge part of code in libgcov.  */
+#include "libgcov-driver.c"
+#include "libgcov-merge.c"
+
+/* Verbose mode for debug.  */
+static int verbose;
+
+/* Set verbose flag.  */
+void gcov_tool_set_verbose (void)
+{
+  verbose = 1;
+}
+
+/* -------- Read Gcda and Reconstruct GCOV_INFO ----------- */
+
+#include "obstack.h"
+#include <unistd.h>
+#include <ftw.h>
+
+static void tag_function (const char *, unsigned, unsigned);
+static void tag_blocks (const char *, unsigned, unsigned);
+static void tag_arcs (const char *, unsigned, unsigned);
+static void tag_lines (const char *, unsigned, unsigned);
+static void tag_counters (const char *, unsigned, unsigned);
+static void tag_summary (const char *, unsigned, unsigned);
+
+/* The gcov_info for the first module.  */
+static struct gcov_info *curr_gcov_info;
+/* The gcov_info being processed.  */
+static struct gcov_info *gcov_info_head;
+/* This variable contains all the functions in current module.  */
+static struct obstack fn_info;
+/* The function being processed.  */
+static struct gcov_fn_info *curr_fn_info;
+/* The number of functions seen so far.  */
+static unsigned num_fn_info;
+/* This variable contains all the counters for current module.  */
+static int k_ctrs_mask[GCOV_COUNTERS];
+/* The kind of counters that have been seen.  */
+static struct gcov_ctr_info k_ctrs[GCOV_COUNTERS];
+/* Number of kind of counters that have been seen.  */
+static int k_ctrs_types;
+/* The longest length of all the filenames.  */
+static int max_filename_len;
+
+/* Merge functions for counters.  */
+static gcov_merge_fn ctr_merge_functions[GCOV_COUNTERS] = {
+    __gcov_merge_add,
+    __gcov_merge_add,
+    __gcov_merge_add,
+    __gcov_merge_single,
+    __gcov_merge_delta,
+    __gcov_merge_single,
+    __gcov_merge_add,
+    __gcov_merge_ior,
+    __gcov_merge_time_profile,
+};
+
+/* Set the fn_ctrs structure in fn_info.  */
+
+static void
+set_fn_ctrs (struct gcov_fn_info *fn_info)
+{
+  int j = 0, i;
+
+  for (i = 0; i < GCOV_COUNTERS; i++)
+    {
+      if (k_ctrs_mask[i] == 0)
+        continue;
+      fn_info->ctrs[j].num = k_ctrs[i].num;
+      fn_info->ctrs[j].values = k_ctrs[i].values;
+      j++;
+    }
+  if (k_ctrs_types == 0)
+    k_ctrs_types = j;
+  else
+    gcc_assert (j == k_ctrs_types);
+}
+
+typedef struct tag_format
+{
+    unsigned tag;
+    char const *name;
+    void (*proc) (const char *, unsigned, unsigned);
+} tag_format_t;
+
+static const tag_format_t tag_table[] =
+{
+  {0, "NOP", NULL},
+  {0, "UNKNOWN", NULL},
+  {0, "COUNTERS", tag_counters},
+  {GCOV_TAG_FUNCTION, "FUNCTION", tag_function},
+  {GCOV_TAG_BLOCKS, "BLOCKS", tag_blocks},
+  {GCOV_TAG_ARCS, "ARCS", tag_arcs},
+  {GCOV_TAG_LINES, "LINES", tag_lines},
+  {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary},
+  {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary},
+  {0, NULL, NULL}
+};
+
+/* Handler for reading funtion tag.  */
+
+static void
+tag_function (const char *filename ATTRIBUTE_UNUSED,
+	      unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+  int i;
+
+  /* write out previous fn_info. */
+  if (num_fn_info)
+    {
+      set_fn_ctrs (curr_fn_info);
+      /* obstack_grow (&fn_info, &curr_fn_info, sizeof (struct gcov_fn_info*)); */
+      obstack_ptr_grow (&fn_info, curr_fn_info);
+    }
+  /* first time.  */
+    {
+      /* Here we over allocate a bit, using GCOV_COUNTERS instead of the actual active
+         counter types.  */
+      curr_fn_info = (struct gcov_fn_info *) xcalloc (sizeof (struct gcov_fn_info)
+                              + GCOV_COUNTERS * sizeof (struct gcov_ctr_info), 1);
+    }
+
+  for (i = 0; i < GCOV_COUNTERS; i++)
+     k_ctrs[i].num = 0;
+  k_ctrs_types = 0;
+
+  curr_fn_info->key = curr_gcov_info;
+  curr_fn_info->ident = gcov_read_unsigned ();
+  curr_fn_info->lineno_checksum = gcov_read_unsigned ();
+  curr_fn_info->cfg_checksum = gcov_read_unsigned ();
+  num_fn_info++;
+
+  if (verbose)
+    fprintf (stdout, "tag one function id=%d\n", curr_fn_info->ident);
+}
+
+/* Handler for reading block tag.  */
+
+static void
+tag_blocks (const char *filename ATTRIBUTE_UNUSED,
+	    unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+  gcc_assert (0);
+}
+
+/* Handler for reading flow arc tag.  */
+
+static void
+tag_arcs (const char *filename ATTRIBUTE_UNUSED,
+	  unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+  gcc_assert (0);
+}
+
+/* Handler for reading line tag.  */
+
+static void
+tag_lines (const char *filename ATTRIBUTE_UNUSED,
+	   unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+  gcc_assert (0);
+}
+
+/* Handler for reading counters array tag.  */
+
+static void
+tag_counters (const char *filename ATTRIBUTE_UNUSED,
+	      unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+  unsigned n_counts = GCOV_TAG_COUNTER_NUM (length);
+  gcov_type *values;
+  unsigned ix;
+  unsigned tag_ix;
+
+  tag_ix = GCOV_COUNTER_FOR_TAG (tag);
+  gcc_assert (tag_ix < GCOV_COUNTERS);
+  k_ctrs_mask [tag_ix] = 1;
+  gcc_assert (k_ctrs[tag_ix].num == 0);
+  k_ctrs[tag_ix].num = n_counts;
+
+  k_ctrs[tag_ix].values = values = (gcov_type *) xmalloc (n_counts * sizeof (gcov_type));
+  gcc_assert (values);
+
+  for (ix = 0; ix != n_counts; ix++)
+    values[ix] = gcov_read_counter ();
+}
+
+/* Handler for reading summary tag.  */
+
+static void
+tag_summary (const char *filename ATTRIBUTE_UNUSED,
+	     unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+  struct gcov_summary summary;
+
+  gcov_read_summary (&summary);
+  if (!saved_summary_checksum)
+    saved_summary_checksum = summary.checksum;
+}
+
+/* This function is called at the end of reading a gcda file.
+   It flushes the contents in curr_fn_info to the gcov_info.  */
+
+static void
+read_gcda_finalize (struct gcov_info *obj_info)
+{
+  int i;
+
+  set_fn_ctrs (curr_fn_info);
+  obstack_ptr_grow (&fn_info, curr_fn_info);
+
+  /* We set the following fields: merge, n_functions, and functions.  */
+  obj_info->n_functions = num_fn_info;
+  obj_info->functions = (const struct gcov_fn_info**) obstack_finish (&fn_info);
+
+  /* wrap all the counter array. */
+  for (i=0; i< GCOV_COUNTERS; i++)
+    {
+      if (k_ctrs_mask[i])
+        obj_info->merge[i] = ctr_merge_functions[i];
+    }
+}
+
+/* Read the content of a gcda file, and return a gcov_info data structure.
+   Program level summary CURRENT_SUMMARY will also be updated.  */
+
+static struct gcov_info *
+read_gcda_file (const char *filename)
+{
+  unsigned tags[4];
+  unsigned depth = 0;
+  unsigned magic, version;
+  struct gcov_info *obj_info;
+  int i;
+
+  for (i=0; i< GCOV_COUNTERS; i++)
+    k_ctrs_mask[i] = 0;
+  k_ctrs_types = 0;
+
+  if (!gcov_open (filename))
+    {
+      fprintf (stderr, "%s:cannot open\n", filename);
+      return NULL;
+    }
+
+  /* read magic.  */
+  magic = gcov_read_unsigned ();
+  if (magic != GCOV_DATA_MAGIC)
+    {
+      fprintf (stderr, "%s:not a gcov data file\n", filename);
+      gcov_close ();
+      return NULL;
+    }
+
+  /* read version.  */
+  version = gcov_read_unsigned ();
+  if (version != GCOV_VERSION)
+    {
+      fprintf (stderr, "%s:incorrect gcov version %d vs %d \n", filename, version, GCOV_VERSION);
+      gcov_close ();
+      return NULL;
+    }
+
+  /* Instantiate a gcov_info object.  */
+  curr_gcov_info = obj_info = (struct gcov_info *) xcalloc (sizeof (struct gcov_info) +
+             sizeof (struct gcov_ctr_info) * GCOV_COUNTERS, 1);
+
+  obj_info->version = version;
+  obstack_init (&fn_info);
+  num_fn_info = 0;
+  curr_fn_info = 0;
+  {
+    char *str_dup = (char*) xmalloc (strlen (filename) + 1);
+    int len;
+
+    strcpy (str_dup, filename);
+    obj_info->filename = str_dup;
+    if ((len = strlen (filename)) > max_filename_len)
+      max_filename_len = len;
+  }
+
+  /* read stamp.  */
+  obj_info->stamp = gcov_read_unsigned ();
+
+  while (1)
+    {
+      gcov_position_t base;
+      unsigned tag, length;
+      tag_format_t const *format;
+      unsigned tag_depth;
+      int error;
+      unsigned mask;
+
+      tag = gcov_read_unsigned ();
+      if (!tag)
+	break;
+      length = gcov_read_unsigned ();
+      base = gcov_position ();
+      mask = GCOV_TAG_MASK (tag) >> 1;
+      for (tag_depth = 4; mask; mask >>= 8)
+	{
+	  if (((mask & 0xff) != 0xff))
+	    {
+	      fprintf (stderr, "warning: %s:tag `%08x' is invalid\n", filename, tag);
+	      break;
+	    }
+	  tag_depth--;
+	}
+      for (format = tag_table; format->name; format++)
+	if (format->tag == tag)
+	  goto found;
+      format = &tag_table[GCOV_TAG_IS_COUNTER (tag) ? 2 : 1];
+    found:;
+      if (tag)
+	{
+	  if (depth && depth < tag_depth)
+	    {
+	      if (!GCOV_TAG_IS_SUBTAG (tags[depth - 1], tag))
+		fprintf (stderr, "warning: %s:tag `%08x' is incorrectly nested\n",
+			filename, tag);
+	    }
+	  depth = tag_depth;
+	  tags[depth - 1] = tag;
+	}
+
+      if (format->proc)
+	(*format->proc) (filename, tag, length);
+
+      if (format->proc)
+	{
+	  unsigned long actual_length = gcov_position () - base;
+
+	  if (actual_length > length)
+	    fprintf (stderr,"warning: %s:record size mismatch %lu bytes overread\n",
+		    filename, actual_length - length);
+	  else if (length > actual_length)
+	    fprintf (stderr,"warning: %s:record size mismatch %lu bytes unread\n",
+		    filename, length - actual_length);
+	}
+      gcov_sync (base, length);
+      if ((error = gcov_is_error ()))
+	{
+	  fprintf (stderr,error < 0 ? "warning:%s:counter overflow at %lu\n" :
+		  "Warning:%s:read error at %lu\n", filename,
+		  (long unsigned) gcov_position ());
+	  break;
+	}
+    }
+
+  read_gcda_finalize (obj_info);
+  gcov_close ();
+
+  return obj_info;
+}
+
+#define GCOV_SUFFIX ".gcda"
+
+/* This will be called by ftw. Return a non-zero value
+   to stop the tree walk.  */
+
+static int
+ftw_read_file ( const char *filename,
+                const struct stat *status ATTRIBUTE_UNUSED,
+                int type)
+{
+  int filename_len;
+  int suffix_len;
+  struct gcov_info *obj_info;
+
+  /* Only read regular files.  */
+  if (type != FTW_F)
+    return 0;
+
+  filename_len = strlen (filename);
+  suffix_len = strlen (GCOV_SUFFIX);
+
+  if (filename_len <= suffix_len)
+    return 0;
+
+  if (strcmp(filename + filename_len - suffix_len, GCOV_SUFFIX))
+    return 0;
+
+   if (verbose)
+    fprintf (stderr, "reading file: %s\n", filename);
+
+  obj_info = read_gcda_file (filename);
+
+  obj_info->next = gcov_info_head;
+  gcov_info_head = obj_info;
+
+  return 0;
+}
+
+static inline void
+read_profile_dir_init (void)
+{
+  gcov_info_head = 0;
+}
+
+/* Driver for read a profile directory and convert into gcvo_info list in memory.
+   Return NULL on error,
+   Return the head of gcov_info list on success.
+   Note the file static variable GCOV_MAX_FILENAME is also set.  */
+
+struct gcov_info *
+gcov_read_profile_dir (const char* dir_name, int recompute_summary ATTRIBUTE_UNUSED)
+{
+  char *pwd;
+  int ret;
+
+  read_profile_dir_init ();
+
+  if (access (dir_name, R_OK) != 0)
+    {
+      fprintf (stderr, "cannot access directory %s\n", dir_name);
+      return NULL;
+    }
+  pwd = 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 recored
+     max filename len. We need to set it here to allocate the
+     array for dumping.  */
+  gcov_max_filename = max_filename_len;
+
+  return gcov_info_head;;
+}
+
+/* -------------- Merge Profile Counters ------------------ */
+
+/* Offline tool to manipulate profile data.
+   This tool targets on matched profiles. But it has some tolerance on
+   unmatched profiles.
+   When merging p1 to p2 (p2 is the dst),
+   * m.gcda in p1 but not in p2: append m.gcda to p2 with specified weight; emit warning
+   * m.gcda in p2 but not in p1: keep m.gcda in p2 and multiplying specified weight; emit warning.
+   * m.gcda in both p1 and p2:
+   ** p1->m.gcda->f checksum matches p2->m.gcda->f: simple merge.
+   ** p1->m.gcda->f checksum does not matches p2->m.gcda->f: keep p2->m.gcda->f and
+      drop p1->m.gcda->f. A warning is emitted.  */
+
+/* Add INFO2's counter to INFO1, multiplying weight W.  */
+
+static int
+gcov_merge (struct gcov_info *info1, struct gcov_info *info2, int w)
+{
+  unsigned f_ix;
+  unsigned n_functions = info1->n_functions;
+  int has_mismatch = 0;
+
+  gcc_assert (info2->n_functions == n_functions);
+  for (f_ix = 0; f_ix < n_functions; f_ix++)
+    {
+      unsigned t_ix;
+      const struct gcov_fn_info *gfi_ptr1 = info1->functions[f_ix];
+      const struct gcov_fn_info *gfi_ptr2 = info2->functions[f_ix];
+      const struct gcov_ctr_info *ci_ptr1, *ci_ptr2;
+
+      if (!gfi_ptr1 || gfi_ptr1->key != info1)
+        continue;
+      if (!gfi_ptr2 || gfi_ptr2->key != info2)
+        continue;
+
+      if (gfi_ptr1->cfg_checksum != gfi_ptr2->cfg_checksum)
+        {
+          fprintf (stderr, "in %s, cfg_checksum mismatch, skipping\n",
+                  info1->filename);
+          has_mismatch = 1;
+          continue;
+        }
+      ci_ptr1 = gfi_ptr1->ctrs;
+      ci_ptr2 = gfi_ptr2->ctrs;
+      for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++)
+        {
+          gcov_merge_fn merge1 = info1->merge[t_ix];
+          gcov_merge_fn merge2 = info2->merge[t_ix];
+
+          gcc_assert (merge1 == merge2);
+          if (!merge1)
+            continue;
+          gcc_assert (ci_ptr1->num == ci_ptr2->num);
+          (*merge1) (ci_ptr1->values, ci_ptr1->num, ci_ptr2->values, w);
+          ci_ptr1++;
+          ci_ptr2++;
+        }
+    }
+
+  return has_mismatch;
+}
+
+static struct gcov_info *
+find_match_gcov_info (struct gcov_info **array, int size, struct gcov_info *info)
+{
+  struct gcov_info *gi_ptr;
+  struct gcov_info *ret = NULL;
+  int i;
+
+  for (i = 0; i < size; i++)
+    {
+      gi_ptr = array[i];
+      if (gi_ptr == 0)
+        continue;
+      if (!strcmp (gi_ptr->filename, info->filename))
+        {
+          ret = gi_ptr;
+          array[i] = 0;
+          break;
+        }
+    }
+
+  if (ret && ret->n_functions != info->n_functions)
+    {
+      fprintf (stderr, "mismatched profiles in %s (%d functions"
+                       " vs %d functions)\n",
+                       ret->filename,
+                       ret->n_functions,
+                       info->n_functions);
+      ret = NULL;
+    }
+  return ret;
+}
+
+/* return 0 on success: without mismatch.
+   reutrn 1 on error.  */
+
+int
+gcov_profile_merge (struct gcov_info *tgt_profile, struct gcov_info *src_profile,
+                    int w1, int w2)
+{
+  struct gcov_info *gi_ptr;
+  struct gcov_info **tgt_infos;
+  struct gcov_info *tgt_tail;
+  struct gcov_info **in_src_not_tgt;
+  unsigned tgt_cnt = 0, src_cnt = 0;
+  unsigned unmatch_info_cnt = 0;
+  unsigned int i;
+
+  for (gi_ptr = tgt_profile; gi_ptr; gi_ptr = gi_ptr->next)
+    tgt_cnt++;
+  for (gi_ptr = src_profile; gi_ptr; gi_ptr = gi_ptr->next)
+    src_cnt++;
+  tgt_infos = (struct gcov_info **) xmalloc (sizeof (struct gcov_info *) * tgt_cnt);
+  gcc_assert (tgt_infos);
+  in_src_not_tgt = (struct gcov_info **) xmalloc (sizeof (struct gcov_info *) * src_cnt);
+  gcc_assert (in_src_not_tgt);
+
+  for (gi_ptr = tgt_profile, i = 0; gi_ptr; gi_ptr = gi_ptr->next, i++)
+    tgt_infos[i] = gi_ptr;
+
+  tgt_tail = tgt_infos[tgt_cnt - 1];
+
+  /* First pass on tgt_profile, we multiply w1 to all counters.  */
+  if (w1 > 1)
+    {
+       for (i = 0; i < tgt_cnt; i++)
+         gcov_merge (tgt_infos[i], tgt_infos[i], w1-1);
+    }
+
+  /* Second pass, add src_profile to the tgt_profile.  */
+  for (gi_ptr = src_profile; gi_ptr; gi_ptr = gi_ptr->next)
+    {
+      struct gcov_info *gi_ptr1;
+
+      gi_ptr1 = find_match_gcov_info (tgt_infos, tgt_cnt, gi_ptr);
+      if (gi_ptr1 == NULL)
+        {
+          in_src_not_tgt[unmatch_info_cnt++] = gi_ptr;
+          continue;
+        }
+      gcov_merge (gi_ptr1, gi_ptr, w2);
+    }
+
+#if 0
+  /* For modules left in the array. They are not in src_prfile.  */
+  for (i = 0; i < tgt_cnt; i++)
+    {
+      gi_ptr = tgt_infos[i];
+      if (!gi_ptr)
+        continue;
+      gcov_merge (gi_ptr, gi_ptr, w2);
+    }
+
+  /* For modules in src but not in tgt. We adjust the counter and append.  */
+  for (i = 0; i < unmatch_info_cnt; i++)
+    {
+      gi_ptr = in_src_not_tgt[i];
+      gcov_merge (gi_ptr, gi_ptr, w1+w2-1);
+    }
+#else
+  /* For modules in src but not in tgt. We adjust the counter and append.  */
+  for (i = 0; i < unmatch_info_cnt; i++)
+    {
+      gi_ptr = in_src_not_tgt[i];
+      gcov_merge (gi_ptr, gi_ptr, w2 - 1);
+      tgt_tail->next = gi_ptr;
+      tgt_tail = gi_ptr;
+    }
+#endif
+
+  return 0;
+}
+
+/* -------------- Scale Profile Counters ------------------ */
+
+/* Type of function used to normalize counters.  */
+typedef void (*gcov_scale_fn) (gcov_type *, gcov_unsigned_t, double);
+
+/* Scale arc counters.  */
+
+static void
+__gcov_scale_add (gcov_type *counters, unsigned n_counters, double f)
+{
+  for (; n_counters; counters++, n_counters--)
+    {
+      gcov_type val = *counters;
+      *counters = val * f;
+    }
+}
+
+/* Scale ior counters.  */
+
+static void
+__gcov_scale_ior (gcov_type *counters ATTRIBUTE_UNUSED,
+                      unsigned n_counters ATTRIBUTE_UNUSED,
+                      double f ATTRIBUTE_UNUSED)
+{
+  /* do nothing.  */
+}
+
+/* Scale delta counters.  */
+
+static void
+__gcov_scale_delta (gcov_type *counters, unsigned n_counters, double f)
+{
+  unsigned i, n_measures;
+
+  gcc_assert (!(n_counters % 4));
+  n_measures = n_counters / 4;
+  for (i = 0; i < n_measures; i++, counters += 4)
+    {
+      counters[2] *= f;
+      counters[3] *= f;
+    }
+}
+
+/* Scale single counters.  */
+
+static void
+__gcov_scale_single (gcov_type *counters, unsigned n_counters, double f)
+{
+  unsigned i, n_measures;
+
+  gcc_assert (!(n_counters % 3));
+  n_measures = n_counters / 3;
+  for (i = 0; i < n_measures; i++, counters += 3)
+    {
+      counters[1] *= f;
+      counters[2] *= f;
+    }
+}
+
+/* Scaling functions for counters.  */
+static gcov_scale_fn ctr_scale_functions[GCOV_COUNTERS] = {
+    __gcov_scale_add,
+    __gcov_scale_add,
+    __gcov_scale_add,
+    __gcov_scale_single,
+    __gcov_scale_delta,
+    __gcov_scale_single,
+    __gcov_scale_add,
+    __gcov_scale_ior,
+    __gcov_scale_ior,
+};
+
+/* Driver for scale profile counters.  */
+
+int
+gcov_profile_scale (struct gcov_info *profile, float scale_factor)
+{
+  struct gcov_info *gi_ptr;
+  unsigned f_ix;
+
+  if (verbose)
+    fprintf (stdout, "scale_factor is %f\n", scale_factor);
+
+  /* scale the counters.  */
+  for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next)
+    for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
+      {
+        unsigned t_ix;
+        const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix];
+        const struct gcov_ctr_info *ci_ptr;
+
+        if (!gfi_ptr || gfi_ptr->key != gi_ptr)
+          continue;
+
+        ci_ptr = gfi_ptr->ctrs;
+        for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++)
+          {
+            gcov_merge_fn merge = gi_ptr->merge[t_ix];
+
+            if (!merge)
+              continue;
+            (*ctr_scale_functions[t_ix]) (ci_ptr->values, ci_ptr->num, scale_factor);
+            ci_ptr++;
+          }
+      }
+
+  return 0;
+}
+
+/* Driver for normalize profile counters.  */
+
+int
+gcov_profile_normalize (struct gcov_info *profile, gcov_type max_val)
+{
+  struct gcov_info *gi_ptr;
+  gcov_type curr_max_val = 0;
+  unsigned f_ix;
+  unsigned int i;
+  float scale_factor;
+
+  /* get the larest count value.  */
+  for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next)
+    for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
+      {
+        unsigned t_ix;
+        const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix];
+        const struct gcov_ctr_info *ci_ptr;
+
+        if (!gfi_ptr || gfi_ptr->key != gi_ptr)
+          continue;
+
+        ci_ptr = gfi_ptr->ctrs;
+        for (t_ix = 0; t_ix < 1; t_ix++)
+          {
+            for (i = 0; i < ci_ptr->num; i++)
+              if (ci_ptr->values[i] > curr_max_val)
+                curr_max_val = ci_ptr->values[i];
+            ci_ptr++;
+          }
+      }
+
+  scale_factor = (float)max_val / curr_max_val;
+  if (verbose)
+    fprintf (stdout, "max_val is %lld\n", (long long) curr_max_val);
+
+  return gcov_profile_scale (profile, scale_factor);
+}
+
+/* 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[2] / d) * n;
+      counters[2] = (counters[2] / 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_ior,
+};
+
+/* 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;
+}
+
Index: libgcc/Makefile.in
===================================================================
--- libgcc/Makefile.in	(revision 204895)
+++ libgcc/Makefile.in	(working copy)
@@ -868,14 +868,14 @@ libgcov-driver-objects = $(patsubst %,%$(objext),$
 libgcov-objects = $(libgcov-merge-objects) $(libgcov-profiler-objects) \
                  $(libgcov-interface-objects) $(libgcov-driver-objects)
 
-$(libgcov-merge-objects): %$(objext): $(srcdir)/libgcov-merge.c
+$(libgcov-merge-objects): %$(objext): $(srcdir)/libgcov-merge.c $(srcdir)/libgcov.h
 	$(gcc_compile) -DL$* -c $(srcdir)/libgcov-merge.c
-$(libgcov-profiler-objects): %$(objext): $(srcdir)/libgcov-profiler.c
+$(libgcov-profiler-objects): %$(objext): $(srcdir)/libgcov-profiler.c $(srcdir)/libgcov.h
 	$(gcc_compile) -DL$* -c $(srcdir)/libgcov-profiler.c
-$(libgcov-interface-objects): %$(objext): $(srcdir)/libgcov-interface.c
+$(libgcov-interface-objects): %$(objext): $(srcdir)/libgcov-interface.c $(srcdir)/libgcov.h
 	$(gcc_compile) -DL$* -c $(srcdir)/libgcov-interface.c
 $(libgcov-driver-objects): %$(objext): $(srcdir)/libgcov-driver.c \
-  $(srcdir)/libgcov-driver-system.c
+  $(srcdir)/libgcov-driver-system.c $(srcdir)/libgcov.h
 	$(gcc_compile) -DL$* -c $(srcdir)/libgcov-driver.c
 

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-18 21:52             ` Rong Xu
@ 2013-11-18 22:18               ` Xinliang David Li
  2013-11-21  8:21                 ` Rong Xu
  2013-12-06 14:23               ` Jan Hubicka
  1 sibling, 1 reply; 55+ messages in thread
From: Xinliang David Li @ 2013-11-18 22:18 UTC (permalink / raw)
  To: Rong Xu; +Cc: Jan Hubicka, GCC Patches

gcov-dump tool does raw dump of profile data. In the long run, we
should have only one gcov profile manipulation tool, so it might be
better to incorporate gcov-dump into gcov-tool and get rid of
'gcov-dump'.

David

On Mon, Nov 18, 2013 at 12:24 PM, Rong Xu <xur@google.com> wrote:
> Hi, all
>
> This is the new patch for gcov-tool (previously profile-tool).
>
> Honza: can you comment on the new merge interface? David posted some
> comments in an earlier email and we want to know what's your opinion.
>
> Test patch has been tested with boostrap, regresssion,
> profiledbootstrap and SPEC2006.
>
> Noticeable changes from the earlier version:
>
> 1. create a new file libgcov.h and move libgcov-*.h headers to libgcov.h
> So we can included multiple libgcov-*.c without adding new macros.
>
> 2. split libgcov.h specific code in gcvo-io.h to libcc/libgcov.h
> Avoid multiple-page of code under IN_LIBGCOV macro -- this
> improves the readability.
>
> 3. make gcov_var static, and move the definition from gcov-io.h to
> gcov-io.c. Also
>    move some static functions accessing gcov_var to gcvo-io.c
> Current code rely on GCOV_LINKAGE tricks to avoid multi-definition. I don't see
> a reason that gcov_var needs to exposed as a global.
>
> 4. expose gcov_write_strings() and gcov_sync() to gcov_tool usage
>
> 5. rename profile-tool to gcov-tool per Honza's suggestion.
>
> Thanks,
>
> -Rong
>
> On Tue, Nov 12, 2013 at 4:27 PM, Rong Xu <xur@google.com> wrote:
>> The patch was checked in as r204730.
>>
>> Tested with
>> configure with --enable-languages=all,obj-c++
>> (ada is currently broken, so I was not be able test)
>> bootstrap and profiledbootstrap
>> regression for -m32 and -m64.
>> spec2006
>>
>> The only semantic change is __gcov_flush() used to be under L_gcov.
>> I put the function to libgcov-interface.c and made it under
>> L_gcov_flush (newly added).
>> So if you need this function, you have to enable L_gcov_flush in the
>> libgcc/Makefile.in.
>>
>> Thanks,
>>
>> -Rong
>>
>> On Mon, Nov 11, 2013 at 10:19 AM, Rong Xu <xur@google.com> wrote:
>>> On Mon, Nov 11, 2013 at 7:02 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>>>>> 2013-11-04  Rong Xu  <xur@google.com>
>>>>>
>>>>>       * libgcc/libgcov.c: Delete as part of re-factoring.
>>>>>       * libgcc/libgcov-profiler.c (__gcov_interval_profiler): Moved from
>>>>>       libgcov.c
>>>>>       (__gcov_pow2_profiler): Ditto.
>>>>>       (__gcov_one_value_profiler_body): Ditto.
>>>>>       (__gcov_one_value_profiler): Ditto.
>>>>>       (__gcov_indirect_call_profiler): Ditto.
>>>>>       (__gcov_indirect_call_profiler_v2): Ditto.
>>>>>       (__gcov_average_profiler): Ditto.
>>>>>       (__gcov_ior_profiler): Ditto.
>>>>>       * libgcc/libgcov-driver.c (this_prg): Make it file scope
>>>>>       static variable.
>>>>>       (all_prg): Ditto.
>>>>>       (crc32): Ditto.
>>>>>       (gi_filename): Ditto.
>>>>>       (fn_buffer): Ditto.
>>>>>       (sum_buffer): Ditto.
>>>>>         (struct gcov_filename_aux): New types to store auxiliary information
>>>>>         for gi_filename.
>>>>>       (gcov_version): Moved from libgcov.c.
>>>>>       (crc32_unsigned): Ditto.
>>>>>       (gcov_histogram_insert): Ditto.
>>>>>       (gcov_compute_histogram): Ditto.
>>>>>       (gcov_exit): Ditto.
>>>>>       (gcov_clear): Ditto.
>>>>>       (__gcov_init): Ditto.
>>>>>       (gcov_exit_compute_summary): New function split from gcov_exit().
>>>>>       (gcov_exit_merge_gcda): Ditto.
>>>>>       (gcov_exit_write_gcda): Ditto.
>>>>>       (gcov_exit_dump_gcov): Ditto.
>>>>>       * libgcc/libgcov-interface.c (init_mx): Moved from libgcov.c.
>>>>>       (init_mx_once): Ditto.
>>>>>       (__gcov_flush): Ditto.
>>>>>       (__gcov_reset): Ditto.
>>>>>       (__gcov_dump): Ditto.
>>>>>       (__gcov_fork): Ditto.
>>>>>       (__gcov_execl): Ditto.
>>>>>       (__gcov_execlp): Ditto.
>>>>>       (__gcov_execle): Ditto.
>>>>>       (__gcov_execv): Ditto.
>>>>>       (__gcov_execvp): Ditto.
>>>>>       (__gcov_execve): Ditto.
>>>>>       * libgcc/libgcov-driver-system.c (gcov_error): New utility function.
>>>>>       (allocate_filename_struct): New function split from gcov_exit().
>>>>>       (gcov_exit_open_gcda_file): Ditto.
>>>>>         (create_file_directory): Moved from libgcov.c.
>>>>>       * libgcc/libgcov-merge.c:
>>>>>       (__gcov_merge_add): Moved from libgcov.c.
>>>>>       (__gcov_merge_ior): Ditto.
>>>>>       (__gcov_merge_single): Ditto.
>>>>>       (__gcov_merge_delta): Ditto.
>>>>>       * libgcc/Makefile.in: Change to build newly added files.
>>>>>       * gcc/gcov-io.h (__gcov_merge_ior): Add the decl to avoid warning.
>>>>>
>>>>
>>>> Hi,
>>>> the patch looks resonable. I take your word on the fact that there are no changes
>>>> in the code, I did not read it all ;)))
>>>
>>> We did split gcov_exit() into more functions.
>>> But, semantically the code is the same.
>>>
>>>>> Index: libgcc/Makefile.in
>>>>> ===================================================================
>>>>> --- libgcc/Makefile.in        (revision 204285)
>>>>> +++ libgcc/Makefile.in        (working copy)
>>>>> @@ -853,17 +853,37 @@ include $(iterator)
>>>>>  # Build libgcov components.
>>>>>
>>>>>  # Defined in libgcov.c, included only in gcov library
>>>>> -LIBGCOV = _gcov _gcov_merge_add _gcov_merge_single _gcov_merge_delta \
>>>>> -    _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
>>>>> -    _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump \
>>>>> -    _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \
>>>>> +##LIBGCOV = _gcov _gcov_merge_add _gcov_merge_single _gcov_merge_delta \
>>>>> +##    _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
>>>>> +##    _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump \
>>>>> +##    _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \
>>>>> +##    _gcov_indirect_call_profiler _gcov_average_profiler _gcov_ior_profiler \
>>>>> +##    _gcov_merge_ior _gcov_indirect_call_profiler_v2
>>>>
>>>> Probably no need for this commnet block.
>>>
>>> Yes. I ready remove in the most recently patch.
>>>
>>>>> +
>>>>> +LIBGCOV_MERGE = _gcov_merge_add _gcov_merge_single _gcov_merge_delta _gcov_merge_ior
>>>>> +LIBGCOV_PROFILER = _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \
>>>>>      _gcov_indirect_call_profiler _gcov_average_profiler _gcov_ior_profiler \
>>>>> -    _gcov_merge_ior _gcov_indirect_call_profiler_v2
>>>>> +    _gcov_indirect_call_profiler_v2
>>>>> +LIBGCOV_INTERFACE = _gcov_flush _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
>>>>> +    _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump
>>>>> +LIBGCOV_DRIVER = _gcov
>>>>>
>>>>> -libgcov-objects = $(patsubst %,%$(objext),$(LIBGCOV))
>>>>> +libgcov-merge-objects = $(patsubst %,%$(objext),$(LIBGCOV_MERGE))
>>>>> +libgcov-profiler-objects = $(patsubst %,%$(objext),$(LIBGCOV_PROFILER))
>>>>> +libgcov-interface-objects = $(patsubst %,%$(objext),$(LIBGCOV_INTERFACE))
>>>>> +libgcov-driver-objects = $(patsubst %,%$(objext),$(LIBGCOV_DRIVER))
>>>>> +libgcov-objects = $(libgcov-merge-objects) $(libgcov-profiler-objects) \
>>>>> +                 $(libgcov-interface-objects) $(libgcov-driver-objects)
>>>>>
>>>>> -$(libgcov-objects): %$(objext): $(srcdir)/libgcov.c
>>>>> -     $(gcc_compile) -DL$* -c $(srcdir)/libgcov.c
>>>>> +$(libgcov-merge-objects): %$(objext): $(srcdir)/libgcov-merge.c
>>>>> +     $(gcc_compile) -DL$* -c $(srcdir)/libgcov-merge.c
>>>>> +$(libgcov-profiler-objects): %$(objext): $(srcdir)/libgcov-profiler.c
>>>>> +     $(gcc_compile) -DL$* -c $(srcdir)/libgcov-profiler.c
>>>>> +$(libgcov-interface-objects): %$(objext): $(srcdir)/libgcov-interface.c
>>>>> +     $(gcc_compile) -DL$* -c $(srcdir)/libgcov-interface.c
>>>>> +$(libgcov-driver-objects): %$(objext): $(srcdir)/libgcov-driver.c \
>>>>> +  $(srcdir)/libgcov-driver-system.c
>>>>> +     $(gcc_compile) -DL$* -c $(srcdir)/libgcov-driver.c
>>>>>
>>>>>
>>>>>  # Static libraries.
>>>>> Index: gcc/gcov-io.h
>>>>> ===================================================================
>>>>> --- gcc/gcov-io.h     (revision 204285)
>>>>> +++ gcc/gcov-io.h     (working copy)
>>>>> @@ -515,6 +515,8 @@ extern void __gcov_merge_ior (gcov_type *, unsigne
>>>>>  extern void __gcov_interval_profiler (gcov_type *, gcov_type, int, unsigned);
>>>>>  extern void __gcov_pow2_profiler (gcov_type *, gcov_type);
>>>>>  extern void __gcov_one_value_profiler (gcov_type *, gcov_type);
>>>>> +extern void __gcov_indirect_call_profiler (gcov_type*, gcov_type,
>>>>> +                                           void*, void*);
>>>>>  extern void __gcov_indirect_call_profiler_v2 (gcov_type, void *);
>>>>>  extern void __gcov_average_profiler (gcov_type *, gcov_type);
>>>>>  extern void __gcov_ior_profiler (gcov_type *, gcov_type);
>>>>
>>>> The patch is OK with the change above.
>>>
>>> Thanks for the OK. I'll do some final tests on a clean client. Then I'll commit.
>>>
>>>> Honza

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-18 22:18               ` Xinliang David Li
@ 2013-11-21  8:21                 ` Rong Xu
  0 siblings, 0 replies; 55+ messages in thread
From: Rong Xu @ 2013-11-21  8:21 UTC (permalink / raw)
  To: Xinliang David Li; +Cc: Jan Hubicka, GCC Patches

Ping.

On Mon, Nov 18, 2013 at 12:31 PM, Xinliang David Li <davidxl@google.com> wrote:
> gcov-dump tool does raw dump of profile data. In the long run, we
> should have only one gcov profile manipulation tool, so it might be
> better to incorporate gcov-dump into gcov-tool and get rid of
> 'gcov-dump'.
>
> David
>
> On Mon, Nov 18, 2013 at 12:24 PM, Rong Xu <xur@google.com> wrote:
>> Hi, all
>>
>> This is the new patch for gcov-tool (previously profile-tool).
>>
>> Honza: can you comment on the new merge interface? David posted some
>> comments in an earlier email and we want to know what's your opinion.
>>
>> Test patch has been tested with boostrap, regresssion,
>> profiledbootstrap and SPEC2006.
>>
>> Noticeable changes from the earlier version:
>>
>> 1. create a new file libgcov.h and move libgcov-*.h headers to libgcov.h
>> So we can included multiple libgcov-*.c without adding new macros.
>>
>> 2. split libgcov.h specific code in gcvo-io.h to libcc/libgcov.h
>> Avoid multiple-page of code under IN_LIBGCOV macro -- this
>> improves the readability.
>>
>> 3. make gcov_var static, and move the definition from gcov-io.h to
>> gcov-io.c. Also
>>    move some static functions accessing gcov_var to gcvo-io.c
>> Current code rely on GCOV_LINKAGE tricks to avoid multi-definition. I don't see
>> a reason that gcov_var needs to exposed as a global.
>>
>> 4. expose gcov_write_strings() and gcov_sync() to gcov_tool usage
>>
>> 5. rename profile-tool to gcov-tool per Honza's suggestion.
>>
>> Thanks,
>>
>> -Rong
>>
>> On Tue, Nov 12, 2013 at 4:27 PM, Rong Xu <xur@google.com> wrote:
>>> The patch was checked in as r204730.
>>>
>>> Tested with
>>> configure with --enable-languages=all,obj-c++
>>> (ada is currently broken, so I was not be able test)
>>> bootstrap and profiledbootstrap
>>> regression for -m32 and -m64.
>>> spec2006
>>>
>>> The only semantic change is __gcov_flush() used to be under L_gcov.
>>> I put the function to libgcov-interface.c and made it under
>>> L_gcov_flush (newly added).
>>> So if you need this function, you have to enable L_gcov_flush in the
>>> libgcc/Makefile.in.
>>>
>>> Thanks,
>>>
>>> -Rong
>>>
>>> On Mon, Nov 11, 2013 at 10:19 AM, Rong Xu <xur@google.com> wrote:
>>>> On Mon, Nov 11, 2013 at 7:02 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>>>>>> 2013-11-04  Rong Xu  <xur@google.com>
>>>>>>
>>>>>>       * libgcc/libgcov.c: Delete as part of re-factoring.
>>>>>>       * libgcc/libgcov-profiler.c (__gcov_interval_profiler): Moved from
>>>>>>       libgcov.c
>>>>>>       (__gcov_pow2_profiler): Ditto.
>>>>>>       (__gcov_one_value_profiler_body): Ditto.
>>>>>>       (__gcov_one_value_profiler): Ditto.
>>>>>>       (__gcov_indirect_call_profiler): Ditto.
>>>>>>       (__gcov_indirect_call_profiler_v2): Ditto.
>>>>>>       (__gcov_average_profiler): Ditto.
>>>>>>       (__gcov_ior_profiler): Ditto.
>>>>>>       * libgcc/libgcov-driver.c (this_prg): Make it file scope
>>>>>>       static variable.
>>>>>>       (all_prg): Ditto.
>>>>>>       (crc32): Ditto.
>>>>>>       (gi_filename): Ditto.
>>>>>>       (fn_buffer): Ditto.
>>>>>>       (sum_buffer): Ditto.
>>>>>>         (struct gcov_filename_aux): New types to store auxiliary information
>>>>>>         for gi_filename.
>>>>>>       (gcov_version): Moved from libgcov.c.
>>>>>>       (crc32_unsigned): Ditto.
>>>>>>       (gcov_histogram_insert): Ditto.
>>>>>>       (gcov_compute_histogram): Ditto.
>>>>>>       (gcov_exit): Ditto.
>>>>>>       (gcov_clear): Ditto.
>>>>>>       (__gcov_init): Ditto.
>>>>>>       (gcov_exit_compute_summary): New function split from gcov_exit().
>>>>>>       (gcov_exit_merge_gcda): Ditto.
>>>>>>       (gcov_exit_write_gcda): Ditto.
>>>>>>       (gcov_exit_dump_gcov): Ditto.
>>>>>>       * libgcc/libgcov-interface.c (init_mx): Moved from libgcov.c.
>>>>>>       (init_mx_once): Ditto.
>>>>>>       (__gcov_flush): Ditto.
>>>>>>       (__gcov_reset): Ditto.
>>>>>>       (__gcov_dump): Ditto.
>>>>>>       (__gcov_fork): Ditto.
>>>>>>       (__gcov_execl): Ditto.
>>>>>>       (__gcov_execlp): Ditto.
>>>>>>       (__gcov_execle): Ditto.
>>>>>>       (__gcov_execv): Ditto.
>>>>>>       (__gcov_execvp): Ditto.
>>>>>>       (__gcov_execve): Ditto.
>>>>>>       * libgcc/libgcov-driver-system.c (gcov_error): New utility function.
>>>>>>       (allocate_filename_struct): New function split from gcov_exit().
>>>>>>       (gcov_exit_open_gcda_file): Ditto.
>>>>>>         (create_file_directory): Moved from libgcov.c.
>>>>>>       * libgcc/libgcov-merge.c:
>>>>>>       (__gcov_merge_add): Moved from libgcov.c.
>>>>>>       (__gcov_merge_ior): Ditto.
>>>>>>       (__gcov_merge_single): Ditto.
>>>>>>       (__gcov_merge_delta): Ditto.
>>>>>>       * libgcc/Makefile.in: Change to build newly added files.
>>>>>>       * gcc/gcov-io.h (__gcov_merge_ior): Add the decl to avoid warning.
>>>>>>
>>>>>
>>>>> Hi,
>>>>> the patch looks resonable. I take your word on the fact that there are no changes
>>>>> in the code, I did not read it all ;)))
>>>>
>>>> We did split gcov_exit() into more functions.
>>>> But, semantically the code is the same.
>>>>
>>>>>> Index: libgcc/Makefile.in
>>>>>> ===================================================================
>>>>>> --- libgcc/Makefile.in        (revision 204285)
>>>>>> +++ libgcc/Makefile.in        (working copy)
>>>>>> @@ -853,17 +853,37 @@ include $(iterator)
>>>>>>  # Build libgcov components.
>>>>>>
>>>>>>  # Defined in libgcov.c, included only in gcov library
>>>>>> -LIBGCOV = _gcov _gcov_merge_add _gcov_merge_single _gcov_merge_delta \
>>>>>> -    _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
>>>>>> -    _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump \
>>>>>> -    _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \
>>>>>> +##LIBGCOV = _gcov _gcov_merge_add _gcov_merge_single _gcov_merge_delta \
>>>>>> +##    _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
>>>>>> +##    _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump \
>>>>>> +##    _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \
>>>>>> +##    _gcov_indirect_call_profiler _gcov_average_profiler _gcov_ior_profiler \
>>>>>> +##    _gcov_merge_ior _gcov_indirect_call_profiler_v2
>>>>>
>>>>> Probably no need for this commnet block.
>>>>
>>>> Yes. I ready remove in the most recently patch.
>>>>
>>>>>> +
>>>>>> +LIBGCOV_MERGE = _gcov_merge_add _gcov_merge_single _gcov_merge_delta _gcov_merge_ior
>>>>>> +LIBGCOV_PROFILER = _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \
>>>>>>      _gcov_indirect_call_profiler _gcov_average_profiler _gcov_ior_profiler \
>>>>>> -    _gcov_merge_ior _gcov_indirect_call_profiler_v2
>>>>>> +    _gcov_indirect_call_profiler_v2
>>>>>> +LIBGCOV_INTERFACE = _gcov_flush _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
>>>>>> +    _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump
>>>>>> +LIBGCOV_DRIVER = _gcov
>>>>>>
>>>>>> -libgcov-objects = $(patsubst %,%$(objext),$(LIBGCOV))
>>>>>> +libgcov-merge-objects = $(patsubst %,%$(objext),$(LIBGCOV_MERGE))
>>>>>> +libgcov-profiler-objects = $(patsubst %,%$(objext),$(LIBGCOV_PROFILER))
>>>>>> +libgcov-interface-objects = $(patsubst %,%$(objext),$(LIBGCOV_INTERFACE))
>>>>>> +libgcov-driver-objects = $(patsubst %,%$(objext),$(LIBGCOV_DRIVER))
>>>>>> +libgcov-objects = $(libgcov-merge-objects) $(libgcov-profiler-objects) \
>>>>>> +                 $(libgcov-interface-objects) $(libgcov-driver-objects)
>>>>>>
>>>>>> -$(libgcov-objects): %$(objext): $(srcdir)/libgcov.c
>>>>>> -     $(gcc_compile) -DL$* -c $(srcdir)/libgcov.c
>>>>>> +$(libgcov-merge-objects): %$(objext): $(srcdir)/libgcov-merge.c
>>>>>> +     $(gcc_compile) -DL$* -c $(srcdir)/libgcov-merge.c
>>>>>> +$(libgcov-profiler-objects): %$(objext): $(srcdir)/libgcov-profiler.c
>>>>>> +     $(gcc_compile) -DL$* -c $(srcdir)/libgcov-profiler.c
>>>>>> +$(libgcov-interface-objects): %$(objext): $(srcdir)/libgcov-interface.c
>>>>>> +     $(gcc_compile) -DL$* -c $(srcdir)/libgcov-interface.c
>>>>>> +$(libgcov-driver-objects): %$(objext): $(srcdir)/libgcov-driver.c \
>>>>>> +  $(srcdir)/libgcov-driver-system.c
>>>>>> +     $(gcc_compile) -DL$* -c $(srcdir)/libgcov-driver.c
>>>>>>
>>>>>>
>>>>>>  # Static libraries.
>>>>>> Index: gcc/gcov-io.h
>>>>>> ===================================================================
>>>>>> --- gcc/gcov-io.h     (revision 204285)
>>>>>> +++ gcc/gcov-io.h     (working copy)
>>>>>> @@ -515,6 +515,8 @@ extern void __gcov_merge_ior (gcov_type *, unsigne
>>>>>>  extern void __gcov_interval_profiler (gcov_type *, gcov_type, int, unsigned);
>>>>>>  extern void __gcov_pow2_profiler (gcov_type *, gcov_type);
>>>>>>  extern void __gcov_one_value_profiler (gcov_type *, gcov_type);
>>>>>> +extern void __gcov_indirect_call_profiler (gcov_type*, gcov_type,
>>>>>> +                                           void*, void*);
>>>>>>  extern void __gcov_indirect_call_profiler_v2 (gcov_type, void *);
>>>>>>  extern void __gcov_average_profiler (gcov_type *, gcov_type);
>>>>>>  extern void __gcov_ior_profiler (gcov_type *, gcov_type);
>>>>>
>>>>> The patch is OK with the change above.
>>>>
>>>> Thanks for the OK. I'll do some final tests on a clean client. Then I'll commit.
>>>>
>>>>> Honza

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-11-18 21:52             ` Rong Xu
  2013-11-18 22:18               ` Xinliang David Li
@ 2013-12-06 14:23               ` Jan Hubicka
  2013-12-10 19:02                 ` Xinliang David Li
                                   ` (4 more replies)
  1 sibling, 5 replies; 55+ messages in thread
From: Jan Hubicka @ 2013-12-06 14:23 UTC (permalink / raw)
  To: Rong Xu; +Cc: Jan Hubicka, Xinliang David Li, GCC Patches

> Hi, all
> 
> This is the new patch for gcov-tool (previously profile-tool).
> 
> Honza: can you comment on the new merge interface? David posted some
> comments in an earlier email and we want to know what's your opinion.
> 
> Test patch has been tested with boostrap, regresssion,
> profiledbootstrap and SPEC2006.
> 
> Noticeable changes from the earlier version:
> 
> 1. create a new file libgcov.h and move libgcov-*.h headers to libgcov.h
> So we can included multiple libgcov-*.c without adding new macros.
> 
> 2. split libgcov.h specific code in gcvo-io.h to libcc/libgcov.h
> Avoid multiple-page of code under IN_LIBGCOV macro -- this
> improves the readability.
> 
> 3. make gcov_var static, and move the definition from gcov-io.h to
> gcov-io.c. Also
>    move some static functions accessing gcov_var to gcvo-io.c
> Current code rely on GCOV_LINKAGE tricks to avoid multi-definition. I don't see
> a reason that gcov_var needs to exposed as a global.
> 
> 4. expose gcov_write_strings() and gcov_sync() to gcov_tool usage
> 
> 5. rename profile-tool to gcov-tool per Honza's suggestion.
> 
> Thanks,

Hi,
I did not read in deatil the gcov-tool source itself, but lets first make the interface changes
needed.

> 2013-11-18  Rong Xu  <xur@google.com>
> 
> 	* gcc/gcov-io.c (gcov_var): Moved from gcov-io.h and make it static.
> 	(gcov_position): Move from gcov-io.h
> 	(gcov_is_error): Ditto.
> 	(gcov_rewrite): Ditto.
> 	* gcc/gcov-io.h: Re-factoring. Move gcov_var to gcov-io.h and
>         move the libgcov only part of libgcc/libgcov.h.
> 	* libgcc/libgcov.h: New common header files for libgcov-*.h
> 	* libgcc/Makefile.in: Add dependence to libgcov.h
> 	* libgcc/libgcov-profiler.c: Use libgcov.h
> 	* libgcc/libgcov-driver.c: Ditto.
> 	* libgcc/libgcov-interface.c: Ditto.
> 	* libgcc/libgcov-driver-system.c (allocate_filename_struct): use
> 	xmalloc instread of malloc.
> 	* libgcc/libgcov-merge.c (void __gcov_merge_delta): Add more
> 	parameters to merge function.
> 	(__gcov_merge_add): Ditto.
> 	(__gcov_merge_ior): Ditto.
> 	(__gcov_merge_time_profile): Ditto.
> 	(__gcov_merge_single): Ditto.
> 	(__gcov_merge_delta): Ditto.
> 	* libgcc/libgcov-tool.c (void gcov_tool_set_verbose): New for
> 	gcov-tool support.
> 	(set_fn_ctrs): Ditto.
> 	(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_merge): Ditto.
> 	(find_match_gcov_inf Ditto.o):
> 	(gcov_profile_merge): Ditto.
> 	(__gcov_scale_add): Ditto.
> 	(__gcov_scale_ior): Ditto.
> 	(__gcov_scale_delta): Ditto.
> 	(__gcov_scale_single): Ditto.
> 	(gcov_profile_scale): Ditto.
> 	(gcov_profile_normalize): Ditto.
> 	(__gcov_scale2_add): Ditto.
> 	(__gcov_scale2_ior): Ditto.
> 	(__gcov_scale2_delta): Ditto.
> 	(__gcov_scale2_single): Ditto.
> 	(gcov_profile_scale2): Ditto.
> 	* gcc/gcov-tool.c (unlink_file): Gcov-tool driver support.
> 	(unlink_dir): Ditto.
> 	(profile_merge): Ditto.
> 	(print_merge_usage_message): Ditto.
> 	(merge_usage): Ditto.
> 	(do_merge): Ditto.
> 	(profile_rewrite2): Ditto.
> 	(profile_rewrite): Ditto.
> 	(print_rewrite_usage_message): Ditto.
> 	(rewrite_usage): Ditto.
> 	(do_rewrite): Ditto.
> 	(print_usage): Ditto.
> 	(print_version): Ditto.
> 	(process_args): Ditto.
> 	(main): Ditto.
> 	* gcc/Makefile.in: Build and install gcov-tool.

> Index: gcc/gcov-io.c
> ===================================================================
> --- gcc/gcov-io.c	(revision 204895)
> +++ gcc/gcov-io.c	(working copy)
> @@ -36,6 +36,37 @@ static const gcov_unsigned_t *gcov_read_words (uns
>  static void gcov_allocate (unsigned);
>  #endif
>  
> +/* Moved for gcov-io.h and make it static.  */
> +static struct gcov_var gcov_var;

This is more an changelog message than a comment in source file.
Just describe what gcov_var is.

Do you know how the size of libgcov changed with your patch? 
Quick check of current mainline on compiling empty main gives:

jh@gcc10:~/trunk/build/gcc$ cat t.c
main()
{
}
jh@gcc10:~/trunk/build/gcc$ ./xgcc -B ./ -O2 -fprofile-generate -o a.out-new --static t.c
jh@gcc10:~/trunk/build/gcc$ gcc -O2 -fprofile-generate -o a.out-old --static t.c
jh@gcc10:~/trunk/build/gcc$ size a.out-old
   text    data     bss     dec     hex filename
 608141    3560   16728  628429   996cd a.out-old
jh@gcc10:~/trunk/build/gcc$ size a.out-new
   text    data     bss     dec     hex filename
 612621    3688   22880  639189   9c0d5 a.out-new

Without profiling I get:
jh@gcc10:~/trunk/build/gcc$ size a.out-new-no
jh@gcc10:~/trunk/build/gcc$ size a.out-old-no
   text    data     bss     dec     hex filename
 599719    3448   12568  615735   96537 a.out-old-no
   text    data     bss     dec     hex filename
 600247    3448   12568  616263   96747 a.out-new-no

Quite big for empty program, but mostly glibc fault, I suppose
(that won't be an issue for embedded platforms). But anyway
we increased text size overhead from 8k to 12k, BSS size
overhead from 4k to 10k and data by another 1k.

   text    data     bss     dec     hex filename
    126       0       0     126      7e _gcov_merge_add.o (ex libgcov.a)
    251       0       0     251      fb _gcov_merge_single.o (ex libgcov.a)
    242       0       0     242      f2 _gcov_merge_delta.o (ex libgcov.a)
    126       0       0     126      7e _gcov_merge_ior.o (ex libgcov.a)
    156       0       0     156      9c _gcov_merge_time_profile.o (ex libgcov.a)
     89       0       0      89      59 _gcov_interval_profiler.o (ex libgcov.a)
     69       0       0      69      45 _gcov_pow2_profiler.o (ex libgcov.a)
    115       0       0     115      73 _gcov_one_value_profiler.o (ex libgcov.a)
    122       0       0     122      7a _gcov_indirect_call_profiler.o (ex libgcov.a)
     57       0       0      57      39 _gcov_average_profiler.o (ex libgcov.a)
     52       0       0      52      34 _gcov_ior_profiler.o (ex libgcov.a)
    178       0      16     194      c2 _gcov_indirect_call_profiler_v2.o (ex libgcov.a)
     77       0       8      85      55 _gcov_time_profiler.o (ex libgcov.a)
    126       0      40     166      a6 _gcov_flush.o (ex libgcov.a)
    101       0       0     101      65 _gcov_fork.o (ex libgcov.a)
    471       0       0     471     1d7 _gcov_execl.o (ex libgcov.a)
    471       0       0     471     1d7 _gcov_execlp.o (ex libgcov.a)
    524       0       0     524     20c _gcov_execle.o (ex libgcov.a)
     98       0       0      98      62 _gcov_execv.o (ex libgcov.a)
     98       0       0      98      62 _gcov_execvp.o (ex libgcov.a)
    108       0       0     108      6c _gcov_execve.o (ex libgcov.a)
     66       0       0      66      42 _gcov_reset.o (ex libgcov.a)
     66       0       0      66      42 _gcov_dump.o (ex libgcov.a)
   9505       0    6144   15649    3d21 _gcov.o (ex libgcov.a)

I think we definitely need to move those 6k of bss space out.  I think those are new
static vars you introduced that I think are unsafe anyway because multiple streaming
may run at once in threaded program where locking mechanizm fails.
(it will probably do other bad things, but definitely we do not want to
conflict on things like filename to write into).

Compared to my system gcov:
   text    data     bss     dec     hex filename
   9765       0      64    9829    2665 _gcov.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
    125       0       0     125      7d _gcov_merge_add.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
    251       0       0     251      fb _gcov_merge_single.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
    242       0       0     242      f2 _gcov_merge_delta.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
    101       0       0     101      65 _gcov_fork.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
    471       0       0     471     1d7 _gcov_execl.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
    471       0       0     471     1d7 _gcov_execlp.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
    524       0       0     524     20c _gcov_execle.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
     98       0       0      98      62 _gcov_execv.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
     98       0       0      98      62 _gcov_execvp.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
    108       0       0     108      6c _gcov_execve.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
     72       0       0      72      48 _gcov_reset.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
     72       0       0      72      48 _gcov_dump.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
     89       0       0      89      59 _gcov_interval_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
     69       0       0      69      45 _gcov_pow2_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
    115       0       0     115      73 _gcov_one_value_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
    122       0       0     122      7a _gcov_indirect_call_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
     57       0       0      57      39 _gcov_average_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
     52       0       0      52      34 _gcov_ior_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
    125       0       0     125      7d _gcov_merge_ior.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)

> Index: gcc/gcov-io.h
> ===================================================================
> --- gcc/gcov-io.h	(revision 204895)
> +++ gcc/gcov-io.h	(working copy)
> @@ -164,51 +164,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
>  #ifndef GCC_GCOV_IO_H
>  #define GCC_GCOV_IO_H
>  
> -#if IN_LIBGCOV
> -/* About the target */
> -
> -#if BITS_PER_UNIT == 8
> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI)));
> -typedef unsigned gcov_position_t __attribute__ ((mode (SI)));
> -#if LONG_LONG_TYPE_SIZE > 32
> -typedef signed gcov_type __attribute__ ((mode (DI)));
> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (DI)));
> -#else
> -typedef signed gcov_type __attribute__ ((mode (SI)));
> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
> -#endif
> -#else
> -#if BITS_PER_UNIT == 16
> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (HI)));
> -typedef unsigned gcov_position_t __attribute__ ((mode (HI)));
> -#if LONG_LONG_TYPE_SIZE > 32
> -typedef signed gcov_type __attribute__ ((mode (SI)));
> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
> -#else
> -typedef signed gcov_type __attribute__ ((mode (HI)));
> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
> -#endif
> -#else
> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (QI)));
> -typedef unsigned gcov_position_t __attribute__ ((mode (QI)));
> -#if LONG_LONG_TYPE_SIZE > 32
> -typedef signed gcov_type __attribute__ ((mode (HI)));
> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
> -#else
> -typedef signed gcov_type __attribute__ ((mode (QI)));
> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI)));
> -#endif
> -#endif
> -#endif

So this part basically moves libgcov specific bits into libgcov.h? That OK fine by
itself.
> Index: libgcc/libgcov-profiler.c
> ===================================================================
> --- libgcc/libgcov-profiler.c	(revision 204895)
> +++ libgcc/libgcov-profiler.c	(working copy)
> @@ -23,15 +23,8 @@ a copy of the GCC Runtime Library Exception along
>  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>  <http://www.gnu.org/licenses/>.  */
>  
> -#include "tconfig.h"
> -#include "tsystem.h"
> -#include "coretypes.h"
> -#include "tm.h"
> -#include "libgcc_tm.h"
> -
> +#include "libgcov.h"
>  #if !defined(inhibit_libc)
> -#define IN_LIBGCOV 1
> -#include "gcov-io.h"

I did not pay that much attention into the current include file changes, but wasn't
idea to avoid #include file to include random other #includes?
So perhaps the first block of includes should stay, followed by libgcov.h and gcov-io.h
last?
> @@ -325,6 +311,9 @@ static struct gcov_summary all_prg;
>  #endif
>  /* crc32 for this program.  */
>  static gcov_unsigned_t crc32;
> +/* Use this summary checksum rather the computed one if the value is
> + *    non-zero.  */
> +static gcov_unsigned_t saved_summary_checksum;

Why do you need to save the checksum? Won't it reset summary back with multiple streaming?

I would really like to avoid introducing those static vars that are used exclusively
by gcov_exit.  What about putting them into an gcov_context structure that
is passed around the functions that was broken out?
> Index: libgcc/libgcov-merge.c
> ===================================================================
> --- libgcc/libgcov-merge.c	(revision 204895)
> +++ libgcc/libgcov-merge.c	(working copy)
> @@ -23,107 +23,150 @@ a copy of the GCC Runtime Library Exception along
>  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>  <http://www.gnu.org/licenses/>.  */
>  
> -#include "tconfig.h"
> -#include "tsystem.h"
> -#include "coretypes.h"
> -#include "tm.h"
> -#include "libgcc_tm.h"
> +#include "libgcov.h"
>  
> -#if defined(inhibit_libc)
> -#define IN_LIBGCOV (-1)
> -#else
> -#define IN_LIBGCOV 1
> +#include "gcov-io-libgcov.h"
>  #endif
>  
> -#include "gcov-io.h"
> -
>  #if defined(inhibit_libc)
>  /* If libc and its header files are not available, provide dummy functions.  */
>  
>  #ifdef L_gcov_merge_add
>  void __gcov_merge_add (gcov_type *counters  __attribute__ ((unused)),
> -                       unsigned n_counters __attribute__ ((unused))) {}
> +                       unsigned n_counters __attribute__ ((unused)),
> +                       unsigned gcov_type *src __attribute__ ((unused)),
> +                       unsigned w __attribute__ ((unused))) {}
>  #endif
>  
>  #ifdef L_gcov_merge_single
>  void __gcov_merge_single (gcov_type *counters  __attribute__ ((unused)),
> -                          unsigned n_counters __attribute__ ((unused))) {}
> +                          unsigned n_counters __attribute__ ((unused)),
> +                          unsigned gcov_type *src __attribute__ ((unused)),
> +                          unsigned w __attribute__ ((unused))) {}
>  #endif
>  
>  #ifdef L_gcov_merge_delta
>  void __gcov_merge_delta (gcov_type *counters  __attribute__ ((unused)),
> -                         unsigned n_counters __attribute__ ((unused))) {}
> +                         unsigned n_counters __attribute__ ((unused)),
> +                         unsigned gcov_type *src __attribute__ ((unused)),
> +                         unsigned w __attribute__ ((unused))) {}
>  #endif
>  
>  #else
>  
>  #ifdef L_gcov_merge_add
>  /* The profile merging function that just adds the counters.  It is given
> -   an array COUNTERS of N_COUNTERS old counters and it reads the same number
> -   of counters from the gcov file.  */
> +   an array COUNTERS of N_COUNTERS old counters. 
> +   When SRC==NULL, it reads the same number of counters from the gcov file.
> +   Otherwise, it reads from SRC array. These read values will be multiplied
> +   by weight W before adding to the old counters.  */
>  void
> -__gcov_merge_add (gcov_type *counters, unsigned n_counters)
> +__gcov_merge_add (gcov_type *counters, unsigned n_counters,
> +                  gcov_type *src, unsigned w)
>  {
> +  int in_mem = (src != 0);
> +
>    for (; n_counters; counters++, n_counters--)
> -    *counters += gcov_read_counter ();
> +    {
> +      gcov_type value;
> +
> +      if (in_mem)
> +        value = *(src++);
> +      else
> +        value = gcov_read_counter ();
> +
> +      *counters += w * value;
> +    }
>  }
>  #endif /* L_gcov_merge_add */
>  
>  #ifdef L_gcov_merge_ior
>  /* The profile merging function that just adds the counters.  It is given
> -   an array COUNTERS of N_COUNTERS old counters and it reads the same number
> -   of counters from the gcov file.  */
> +   an array COUNTERS of N_COUNTERS old counters. 
> +   When SRC==NULL, it reads the same number of counters from the gcov file.
> +   Otherwise, it reads from SRC array.  */
>  void
> -__gcov_merge_ior (gcov_type *counters, unsigned n_counters)
> +__gcov_merge_ior (gcov_type *counters, unsigned n_counters,
> +                  gcov_type *src, unsigned w __attribute__ ((unused)))

So the new in-memory variants are introduced for merging tool, while libgcc use gcov_read_counter
interface?
Perhaps we can actually just duplicate the functions to avoid runtime to do all the scalling
and in_mem tests it won't need?

I would suggest going with libgcov.h changes and clenaups first, with interface changes next
and the gcov-tool is probably quite obvious at the end?
Do you think you can split the patch this way?

Thanks and sorry for taking long to review. I should have more time again now.
Honza

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-12-06 14:23               ` Jan Hubicka
@ 2013-12-10 19:02                 ` Xinliang David Li
  2013-12-10 20:01                   ` Teresa Johnson
  2013-12-12  6:05                 ` Teresa Johnson
                                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 55+ messages in thread
From: Xinliang David Li @ 2013-12-10 19:02 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Rong Xu, GCC Patches, Teresa Johnson

I agree with the staged checkin plan proposed.  Teresa, can you help
with spliting out the libgcov.h/gcov-io.h change out (Rong is on a
trip..)

thanks,

David

On Fri, Dec 6, 2013 at 6:23 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>> Hi, all
>>
>> This is the new patch for gcov-tool (previously profile-tool).
>>
>> Honza: can you comment on the new merge interface? David posted some
>> comments in an earlier email and we want to know what's your opinion.
>>
>> Test patch has been tested with boostrap, regresssion,
>> profiledbootstrap and SPEC2006.
>>
>> Noticeable changes from the earlier version:
>>
>> 1. create a new file libgcov.h and move libgcov-*.h headers to libgcov.h
>> So we can included multiple libgcov-*.c without adding new macros.
>>
>> 2. split libgcov.h specific code in gcvo-io.h to libcc/libgcov.h
>> Avoid multiple-page of code under IN_LIBGCOV macro -- this
>> improves the readability.
>>
>> 3. make gcov_var static, and move the definition from gcov-io.h to
>> gcov-io.c. Also
>>    move some static functions accessing gcov_var to gcvo-io.c
>> Current code rely on GCOV_LINKAGE tricks to avoid multi-definition. I don't see
>> a reason that gcov_var needs to exposed as a global.
>>
>> 4. expose gcov_write_strings() and gcov_sync() to gcov_tool usage
>>
>> 5. rename profile-tool to gcov-tool per Honza's suggestion.
>>
>> Thanks,
>
> Hi,
> I did not read in deatil the gcov-tool source itself, but lets first make the interface changes
> needed.
>
>> 2013-11-18  Rong Xu  <xur@google.com>
>>
>>       * gcc/gcov-io.c (gcov_var): Moved from gcov-io.h and make it static.
>>       (gcov_position): Move from gcov-io.h
>>       (gcov_is_error): Ditto.
>>       (gcov_rewrite): Ditto.
>>       * gcc/gcov-io.h: Re-factoring. Move gcov_var to gcov-io.h and
>>         move the libgcov only part of libgcc/libgcov.h.
>>       * libgcc/libgcov.h: New common header files for libgcov-*.h
>>       * libgcc/Makefile.in: Add dependence to libgcov.h
>>       * libgcc/libgcov-profiler.c: Use libgcov.h
>>       * libgcc/libgcov-driver.c: Ditto.
>>       * libgcc/libgcov-interface.c: Ditto.
>>       * libgcc/libgcov-driver-system.c (allocate_filename_struct): use
>>       xmalloc instread of malloc.
>>       * libgcc/libgcov-merge.c (void __gcov_merge_delta): Add more
>>       parameters to merge function.
>>       (__gcov_merge_add): Ditto.
>>       (__gcov_merge_ior): Ditto.
>>       (__gcov_merge_time_profile): Ditto.
>>       (__gcov_merge_single): Ditto.
>>       (__gcov_merge_delta): Ditto.
>>       * libgcc/libgcov-tool.c (void gcov_tool_set_verbose): New for
>>       gcov-tool support.
>>       (set_fn_ctrs): Ditto.
>>       (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_merge): Ditto.
>>       (find_match_gcov_inf Ditto.o):
>>       (gcov_profile_merge): Ditto.
>>       (__gcov_scale_add): Ditto.
>>       (__gcov_scale_ior): Ditto.
>>       (__gcov_scale_delta): Ditto.
>>       (__gcov_scale_single): Ditto.
>>       (gcov_profile_scale): Ditto.
>>       (gcov_profile_normalize): Ditto.
>>       (__gcov_scale2_add): Ditto.
>>       (__gcov_scale2_ior): Ditto.
>>       (__gcov_scale2_delta): Ditto.
>>       (__gcov_scale2_single): Ditto.
>>       (gcov_profile_scale2): Ditto.
>>       * gcc/gcov-tool.c (unlink_file): Gcov-tool driver support.
>>       (unlink_dir): Ditto.
>>       (profile_merge): Ditto.
>>       (print_merge_usage_message): Ditto.
>>       (merge_usage): Ditto.
>>       (do_merge): Ditto.
>>       (profile_rewrite2): Ditto.
>>       (profile_rewrite): Ditto.
>>       (print_rewrite_usage_message): Ditto.
>>       (rewrite_usage): Ditto.
>>       (do_rewrite): Ditto.
>>       (print_usage): Ditto.
>>       (print_version): Ditto.
>>       (process_args): Ditto.
>>       (main): Ditto.
>>       * gcc/Makefile.in: Build and install gcov-tool.
>
>> Index: gcc/gcov-io.c
>> ===================================================================
>> --- gcc/gcov-io.c     (revision 204895)
>> +++ gcc/gcov-io.c     (working copy)
>> @@ -36,6 +36,37 @@ static const gcov_unsigned_t *gcov_read_words (uns
>>  static void gcov_allocate (unsigned);
>>  #endif
>>
>> +/* Moved for gcov-io.h and make it static.  */
>> +static struct gcov_var gcov_var;
>
> This is more an changelog message than a comment in source file.
> Just describe what gcov_var is.
>
> Do you know how the size of libgcov changed with your patch?
> Quick check of current mainline on compiling empty main gives:
>
> jh@gcc10:~/trunk/build/gcc$ cat t.c
> main()
> {
> }
> jh@gcc10:~/trunk/build/gcc$ ./xgcc -B ./ -O2 -fprofile-generate -o a.out-new --static t.c
> jh@gcc10:~/trunk/build/gcc$ gcc -O2 -fprofile-generate -o a.out-old --static t.c
> jh@gcc10:~/trunk/build/gcc$ size a.out-old
>    text    data     bss     dec     hex filename
>  608141    3560   16728  628429   996cd a.out-old
> jh@gcc10:~/trunk/build/gcc$ size a.out-new
>    text    data     bss     dec     hex filename
>  612621    3688   22880  639189   9c0d5 a.out-new
>
> Without profiling I get:
> jh@gcc10:~/trunk/build/gcc$ size a.out-new-no
> jh@gcc10:~/trunk/build/gcc$ size a.out-old-no
>    text    data     bss     dec     hex filename
>  599719    3448   12568  615735   96537 a.out-old-no
>    text    data     bss     dec     hex filename
>  600247    3448   12568  616263   96747 a.out-new-no
>
> Quite big for empty program, but mostly glibc fault, I suppose
> (that won't be an issue for embedded platforms). But anyway
> we increased text size overhead from 8k to 12k, BSS size
> overhead from 4k to 10k and data by another 1k.
>
>    text    data     bss     dec     hex filename
>     126       0       0     126      7e _gcov_merge_add.o (ex libgcov.a)
>     251       0       0     251      fb _gcov_merge_single.o (ex libgcov.a)
>     242       0       0     242      f2 _gcov_merge_delta.o (ex libgcov.a)
>     126       0       0     126      7e _gcov_merge_ior.o (ex libgcov.a)
>     156       0       0     156      9c _gcov_merge_time_profile.o (ex libgcov.a)
>      89       0       0      89      59 _gcov_interval_profiler.o (ex libgcov.a)
>      69       0       0      69      45 _gcov_pow2_profiler.o (ex libgcov.a)
>     115       0       0     115      73 _gcov_one_value_profiler.o (ex libgcov.a)
>     122       0       0     122      7a _gcov_indirect_call_profiler.o (ex libgcov.a)
>      57       0       0      57      39 _gcov_average_profiler.o (ex libgcov.a)
>      52       0       0      52      34 _gcov_ior_profiler.o (ex libgcov.a)
>     178       0      16     194      c2 _gcov_indirect_call_profiler_v2.o (ex libgcov.a)
>      77       0       8      85      55 _gcov_time_profiler.o (ex libgcov.a)
>     126       0      40     166      a6 _gcov_flush.o (ex libgcov.a)
>     101       0       0     101      65 _gcov_fork.o (ex libgcov.a)
>     471       0       0     471     1d7 _gcov_execl.o (ex libgcov.a)
>     471       0       0     471     1d7 _gcov_execlp.o (ex libgcov.a)
>     524       0       0     524     20c _gcov_execle.o (ex libgcov.a)
>      98       0       0      98      62 _gcov_execv.o (ex libgcov.a)
>      98       0       0      98      62 _gcov_execvp.o (ex libgcov.a)
>     108       0       0     108      6c _gcov_execve.o (ex libgcov.a)
>      66       0       0      66      42 _gcov_reset.o (ex libgcov.a)
>      66       0       0      66      42 _gcov_dump.o (ex libgcov.a)
>    9505       0    6144   15649    3d21 _gcov.o (ex libgcov.a)
>
> I think we definitely need to move those 6k of bss space out.  I think those are new
> static vars you introduced that I think are unsafe anyway because multiple streaming
> may run at once in threaded program where locking mechanizm fails.
> (it will probably do other bad things, but definitely we do not want to
> conflict on things like filename to write into).
>
> Compared to my system gcov:
>    text    data     bss     dec     hex filename
>    9765       0      64    9829    2665 _gcov.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     125       0       0     125      7d _gcov_merge_add.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     251       0       0     251      fb _gcov_merge_single.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     242       0       0     242      f2 _gcov_merge_delta.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     101       0       0     101      65 _gcov_fork.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     471       0       0     471     1d7 _gcov_execl.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     471       0       0     471     1d7 _gcov_execlp.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     524       0       0     524     20c _gcov_execle.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>      98       0       0      98      62 _gcov_execv.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>      98       0       0      98      62 _gcov_execvp.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     108       0       0     108      6c _gcov_execve.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>      72       0       0      72      48 _gcov_reset.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>      72       0       0      72      48 _gcov_dump.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>      89       0       0      89      59 _gcov_interval_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>      69       0       0      69      45 _gcov_pow2_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     115       0       0     115      73 _gcov_one_value_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     122       0       0     122      7a _gcov_indirect_call_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>      57       0       0      57      39 _gcov_average_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>      52       0       0      52      34 _gcov_ior_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     125       0       0     125      7d _gcov_merge_ior.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>
>> Index: gcc/gcov-io.h
>> ===================================================================
>> --- gcc/gcov-io.h     (revision 204895)
>> +++ gcc/gcov-io.h     (working copy)
>> @@ -164,51 +164,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
>>  #ifndef GCC_GCOV_IO_H
>>  #define GCC_GCOV_IO_H
>>
>> -#if IN_LIBGCOV
>> -/* About the target */
>> -
>> -#if BITS_PER_UNIT == 8
>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI)));
>> -typedef unsigned gcov_position_t __attribute__ ((mode (SI)));
>> -#if LONG_LONG_TYPE_SIZE > 32
>> -typedef signed gcov_type __attribute__ ((mode (DI)));
>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (DI)));
>> -#else
>> -typedef signed gcov_type __attribute__ ((mode (SI)));
>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
>> -#endif
>> -#else
>> -#if BITS_PER_UNIT == 16
>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (HI)));
>> -typedef unsigned gcov_position_t __attribute__ ((mode (HI)));
>> -#if LONG_LONG_TYPE_SIZE > 32
>> -typedef signed gcov_type __attribute__ ((mode (SI)));
>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
>> -#else
>> -typedef signed gcov_type __attribute__ ((mode (HI)));
>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
>> -#endif
>> -#else
>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (QI)));
>> -typedef unsigned gcov_position_t __attribute__ ((mode (QI)));
>> -#if LONG_LONG_TYPE_SIZE > 32
>> -typedef signed gcov_type __attribute__ ((mode (HI)));
>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
>> -#else
>> -typedef signed gcov_type __attribute__ ((mode (QI)));
>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI)));
>> -#endif
>> -#endif
>> -#endif
>
> So this part basically moves libgcov specific bits into libgcov.h? That OK fine by
> itself.
>> Index: libgcc/libgcov-profiler.c
>> ===================================================================
>> --- libgcc/libgcov-profiler.c (revision 204895)
>> +++ libgcc/libgcov-profiler.c (working copy)
>> @@ -23,15 +23,8 @@ a copy of the GCC Runtime Library Exception along
>>  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>>  <http://www.gnu.org/licenses/>.  */
>>
>> -#include "tconfig.h"
>> -#include "tsystem.h"
>> -#include "coretypes.h"
>> -#include "tm.h"
>> -#include "libgcc_tm.h"
>> -
>> +#include "libgcov.h"
>>  #if !defined(inhibit_libc)
>> -#define IN_LIBGCOV 1
>> -#include "gcov-io.h"
>
> I did not pay that much attention into the current include file changes, but wasn't
> idea to avoid #include file to include random other #includes?
> So perhaps the first block of includes should stay, followed by libgcov.h and gcov-io.h
> last?
>> @@ -325,6 +311,9 @@ static struct gcov_summary all_prg;
>>  #endif
>>  /* crc32 for this program.  */
>>  static gcov_unsigned_t crc32;
>> +/* Use this summary checksum rather the computed one if the value is
>> + *    non-zero.  */
>> +static gcov_unsigned_t saved_summary_checksum;
>
> Why do you need to save the checksum? Won't it reset summary back with multiple streaming?
>
> I would really like to avoid introducing those static vars that are used exclusively
> by gcov_exit.  What about putting them into an gcov_context structure that
> is passed around the functions that was broken out?
>> Index: libgcc/libgcov-merge.c
>> ===================================================================
>> --- libgcc/libgcov-merge.c    (revision 204895)
>> +++ libgcc/libgcov-merge.c    (working copy)
>> @@ -23,107 +23,150 @@ a copy of the GCC Runtime Library Exception along
>>  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>>  <http://www.gnu.org/licenses/>.  */
>>
>> -#include "tconfig.h"
>> -#include "tsystem.h"
>> -#include "coretypes.h"
>> -#include "tm.h"
>> -#include "libgcc_tm.h"
>> +#include "libgcov.h"
>>
>> -#if defined(inhibit_libc)
>> -#define IN_LIBGCOV (-1)
>> -#else
>> -#define IN_LIBGCOV 1
>> +#include "gcov-io-libgcov.h"
>>  #endif
>>
>> -#include "gcov-io.h"
>> -
>>  #if defined(inhibit_libc)
>>  /* If libc and its header files are not available, provide dummy functions.  */
>>
>>  #ifdef L_gcov_merge_add
>>  void __gcov_merge_add (gcov_type *counters  __attribute__ ((unused)),
>> -                       unsigned n_counters __attribute__ ((unused))) {}
>> +                       unsigned n_counters __attribute__ ((unused)),
>> +                       unsigned gcov_type *src __attribute__ ((unused)),
>> +                       unsigned w __attribute__ ((unused))) {}
>>  #endif
>>
>>  #ifdef L_gcov_merge_single
>>  void __gcov_merge_single (gcov_type *counters  __attribute__ ((unused)),
>> -                          unsigned n_counters __attribute__ ((unused))) {}
>> +                          unsigned n_counters __attribute__ ((unused)),
>> +                          unsigned gcov_type *src __attribute__ ((unused)),
>> +                          unsigned w __attribute__ ((unused))) {}
>>  #endif
>>
>>  #ifdef L_gcov_merge_delta
>>  void __gcov_merge_delta (gcov_type *counters  __attribute__ ((unused)),
>> -                         unsigned n_counters __attribute__ ((unused))) {}
>> +                         unsigned n_counters __attribute__ ((unused)),
>> +                         unsigned gcov_type *src __attribute__ ((unused)),
>> +                         unsigned w __attribute__ ((unused))) {}
>>  #endif
>>
>>  #else
>>
>>  #ifdef L_gcov_merge_add
>>  /* The profile merging function that just adds the counters.  It is given
>> -   an array COUNTERS of N_COUNTERS old counters and it reads the same number
>> -   of counters from the gcov file.  */
>> +   an array COUNTERS of N_COUNTERS old counters.
>> +   When SRC==NULL, it reads the same number of counters from the gcov file.
>> +   Otherwise, it reads from SRC array. These read values will be multiplied
>> +   by weight W before adding to the old counters.  */
>>  void
>> -__gcov_merge_add (gcov_type *counters, unsigned n_counters)
>> +__gcov_merge_add (gcov_type *counters, unsigned n_counters,
>> +                  gcov_type *src, unsigned w)
>>  {
>> +  int in_mem = (src != 0);
>> +
>>    for (; n_counters; counters++, n_counters--)
>> -    *counters += gcov_read_counter ();
>> +    {
>> +      gcov_type value;
>> +
>> +      if (in_mem)
>> +        value = *(src++);
>> +      else
>> +        value = gcov_read_counter ();
>> +
>> +      *counters += w * value;
>> +    }
>>  }
>>  #endif /* L_gcov_merge_add */
>>
>>  #ifdef L_gcov_merge_ior
>>  /* The profile merging function that just adds the counters.  It is given
>> -   an array COUNTERS of N_COUNTERS old counters and it reads the same number
>> -   of counters from the gcov file.  */
>> +   an array COUNTERS of N_COUNTERS old counters.
>> +   When SRC==NULL, it reads the same number of counters from the gcov file.
>> +   Otherwise, it reads from SRC array.  */
>>  void
>> -__gcov_merge_ior (gcov_type *counters, unsigned n_counters)
>> +__gcov_merge_ior (gcov_type *counters, unsigned n_counters,
>> +                  gcov_type *src, unsigned w __attribute__ ((unused)))
>
> So the new in-memory variants are introduced for merging tool, while libgcc use gcov_read_counter
> interface?
> Perhaps we can actually just duplicate the functions to avoid runtime to do all the scalling
> and in_mem tests it won't need?
>
> I would suggest going with libgcov.h changes and clenaups first, with interface changes next
> and the gcov-tool is probably quite obvious at the end?
> Do you think you can split the patch this way?
>
> Thanks and sorry for taking long to review. I should have more time again now.
> Honza

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-12-10 19:02                 ` Xinliang David Li
@ 2013-12-10 20:01                   ` Teresa Johnson
  0 siblings, 0 replies; 55+ messages in thread
From: Teresa Johnson @ 2013-12-10 20:01 UTC (permalink / raw)
  To: Xinliang David Li; +Cc: Jan Hubicka, Rong Xu, GCC Patches

On Tue, Dec 10, 2013 at 11:01 AM, Xinliang David Li <davidxl@google.com> wrote:
> I agree with the staged checkin plan proposed.  Teresa, can you help
> with spliting out the libgcov.h/gcov-io.h change out (Rong is on a
> trip..)

Sure, I will work on that part and send a patch once it is tested.

Teresa

>
> thanks,
>
> David
>
> On Fri, Dec 6, 2013 at 6:23 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>>> Hi, all
>>>
>>> This is the new patch for gcov-tool (previously profile-tool).
>>>
>>> Honza: can you comment on the new merge interface? David posted some
>>> comments in an earlier email and we want to know what's your opinion.
>>>
>>> Test patch has been tested with boostrap, regresssion,
>>> profiledbootstrap and SPEC2006.
>>>
>>> Noticeable changes from the earlier version:
>>>
>>> 1. create a new file libgcov.h and move libgcov-*.h headers to libgcov.h
>>> So we can included multiple libgcov-*.c without adding new macros.
>>>
>>> 2. split libgcov.h specific code in gcvo-io.h to libcc/libgcov.h
>>> Avoid multiple-page of code under IN_LIBGCOV macro -- this
>>> improves the readability.
>>>
>>> 3. make gcov_var static, and move the definition from gcov-io.h to
>>> gcov-io.c. Also
>>>    move some static functions accessing gcov_var to gcvo-io.c
>>> Current code rely on GCOV_LINKAGE tricks to avoid multi-definition. I don't see
>>> a reason that gcov_var needs to exposed as a global.
>>>
>>> 4. expose gcov_write_strings() and gcov_sync() to gcov_tool usage
>>>
>>> 5. rename profile-tool to gcov-tool per Honza's suggestion.
>>>
>>> Thanks,
>>
>> Hi,
>> I did not read in deatil the gcov-tool source itself, but lets first make the interface changes
>> needed.
>>
>>> 2013-11-18  Rong Xu  <xur@google.com>
>>>
>>>       * gcc/gcov-io.c (gcov_var): Moved from gcov-io.h and make it static.
>>>       (gcov_position): Move from gcov-io.h
>>>       (gcov_is_error): Ditto.
>>>       (gcov_rewrite): Ditto.
>>>       * gcc/gcov-io.h: Re-factoring. Move gcov_var to gcov-io.h and
>>>         move the libgcov only part of libgcc/libgcov.h.
>>>       * libgcc/libgcov.h: New common header files for libgcov-*.h
>>>       * libgcc/Makefile.in: Add dependence to libgcov.h
>>>       * libgcc/libgcov-profiler.c: Use libgcov.h
>>>       * libgcc/libgcov-driver.c: Ditto.
>>>       * libgcc/libgcov-interface.c: Ditto.
>>>       * libgcc/libgcov-driver-system.c (allocate_filename_struct): use
>>>       xmalloc instread of malloc.
>>>       * libgcc/libgcov-merge.c (void __gcov_merge_delta): Add more
>>>       parameters to merge function.
>>>       (__gcov_merge_add): Ditto.
>>>       (__gcov_merge_ior): Ditto.
>>>       (__gcov_merge_time_profile): Ditto.
>>>       (__gcov_merge_single): Ditto.
>>>       (__gcov_merge_delta): Ditto.
>>>       * libgcc/libgcov-tool.c (void gcov_tool_set_verbose): New for
>>>       gcov-tool support.
>>>       (set_fn_ctrs): Ditto.
>>>       (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_merge): Ditto.
>>>       (find_match_gcov_inf Ditto.o):
>>>       (gcov_profile_merge): Ditto.
>>>       (__gcov_scale_add): Ditto.
>>>       (__gcov_scale_ior): Ditto.
>>>       (__gcov_scale_delta): Ditto.
>>>       (__gcov_scale_single): Ditto.
>>>       (gcov_profile_scale): Ditto.
>>>       (gcov_profile_normalize): Ditto.
>>>       (__gcov_scale2_add): Ditto.
>>>       (__gcov_scale2_ior): Ditto.
>>>       (__gcov_scale2_delta): Ditto.
>>>       (__gcov_scale2_single): Ditto.
>>>       (gcov_profile_scale2): Ditto.
>>>       * gcc/gcov-tool.c (unlink_file): Gcov-tool driver support.
>>>       (unlink_dir): Ditto.
>>>       (profile_merge): Ditto.
>>>       (print_merge_usage_message): Ditto.
>>>       (merge_usage): Ditto.
>>>       (do_merge): Ditto.
>>>       (profile_rewrite2): Ditto.
>>>       (profile_rewrite): Ditto.
>>>       (print_rewrite_usage_message): Ditto.
>>>       (rewrite_usage): Ditto.
>>>       (do_rewrite): Ditto.
>>>       (print_usage): Ditto.
>>>       (print_version): Ditto.
>>>       (process_args): Ditto.
>>>       (main): Ditto.
>>>       * gcc/Makefile.in: Build and install gcov-tool.
>>
>>> Index: gcc/gcov-io.c
>>> ===================================================================
>>> --- gcc/gcov-io.c     (revision 204895)
>>> +++ gcc/gcov-io.c     (working copy)
>>> @@ -36,6 +36,37 @@ static const gcov_unsigned_t *gcov_read_words (uns
>>>  static void gcov_allocate (unsigned);
>>>  #endif
>>>
>>> +/* Moved for gcov-io.h and make it static.  */
>>> +static struct gcov_var gcov_var;
>>
>> This is more an changelog message than a comment in source file.
>> Just describe what gcov_var is.
>>
>> Do you know how the size of libgcov changed with your patch?
>> Quick check of current mainline on compiling empty main gives:
>>
>> jh@gcc10:~/trunk/build/gcc$ cat t.c
>> main()
>> {
>> }
>> jh@gcc10:~/trunk/build/gcc$ ./xgcc -B ./ -O2 -fprofile-generate -o a.out-new --static t.c
>> jh@gcc10:~/trunk/build/gcc$ gcc -O2 -fprofile-generate -o a.out-old --static t.c
>> jh@gcc10:~/trunk/build/gcc$ size a.out-old
>>    text    data     bss     dec     hex filename
>>  608141    3560   16728  628429   996cd a.out-old
>> jh@gcc10:~/trunk/build/gcc$ size a.out-new
>>    text    data     bss     dec     hex filename
>>  612621    3688   22880  639189   9c0d5 a.out-new
>>
>> Without profiling I get:
>> jh@gcc10:~/trunk/build/gcc$ size a.out-new-no
>> jh@gcc10:~/trunk/build/gcc$ size a.out-old-no
>>    text    data     bss     dec     hex filename
>>  599719    3448   12568  615735   96537 a.out-old-no
>>    text    data     bss     dec     hex filename
>>  600247    3448   12568  616263   96747 a.out-new-no
>>
>> Quite big for empty program, but mostly glibc fault, I suppose
>> (that won't be an issue for embedded platforms). But anyway
>> we increased text size overhead from 8k to 12k, BSS size
>> overhead from 4k to 10k and data by another 1k.
>>
>>    text    data     bss     dec     hex filename
>>     126       0       0     126      7e _gcov_merge_add.o (ex libgcov.a)
>>     251       0       0     251      fb _gcov_merge_single.o (ex libgcov.a)
>>     242       0       0     242      f2 _gcov_merge_delta.o (ex libgcov.a)
>>     126       0       0     126      7e _gcov_merge_ior.o (ex libgcov.a)
>>     156       0       0     156      9c _gcov_merge_time_profile.o (ex libgcov.a)
>>      89       0       0      89      59 _gcov_interval_profiler.o (ex libgcov.a)
>>      69       0       0      69      45 _gcov_pow2_profiler.o (ex libgcov.a)
>>     115       0       0     115      73 _gcov_one_value_profiler.o (ex libgcov.a)
>>     122       0       0     122      7a _gcov_indirect_call_profiler.o (ex libgcov.a)
>>      57       0       0      57      39 _gcov_average_profiler.o (ex libgcov.a)
>>      52       0       0      52      34 _gcov_ior_profiler.o (ex libgcov.a)
>>     178       0      16     194      c2 _gcov_indirect_call_profiler_v2.o (ex libgcov.a)
>>      77       0       8      85      55 _gcov_time_profiler.o (ex libgcov.a)
>>     126       0      40     166      a6 _gcov_flush.o (ex libgcov.a)
>>     101       0       0     101      65 _gcov_fork.o (ex libgcov.a)
>>     471       0       0     471     1d7 _gcov_execl.o (ex libgcov.a)
>>     471       0       0     471     1d7 _gcov_execlp.o (ex libgcov.a)
>>     524       0       0     524     20c _gcov_execle.o (ex libgcov.a)
>>      98       0       0      98      62 _gcov_execv.o (ex libgcov.a)
>>      98       0       0      98      62 _gcov_execvp.o (ex libgcov.a)
>>     108       0       0     108      6c _gcov_execve.o (ex libgcov.a)
>>      66       0       0      66      42 _gcov_reset.o (ex libgcov.a)
>>      66       0       0      66      42 _gcov_dump.o (ex libgcov.a)
>>    9505       0    6144   15649    3d21 _gcov.o (ex libgcov.a)
>>
>> I think we definitely need to move those 6k of bss space out.  I think those are new
>> static vars you introduced that I think are unsafe anyway because multiple streaming
>> may run at once in threaded program where locking mechanizm fails.
>> (it will probably do other bad things, but definitely we do not want to
>> conflict on things like filename to write into).
>>
>> Compared to my system gcov:
>>    text    data     bss     dec     hex filename
>>    9765       0      64    9829    2665 _gcov.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>     125       0       0     125      7d _gcov_merge_add.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>     251       0       0     251      fb _gcov_merge_single.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>     242       0       0     242      f2 _gcov_merge_delta.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>     101       0       0     101      65 _gcov_fork.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>     471       0       0     471     1d7 _gcov_execl.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>     471       0       0     471     1d7 _gcov_execlp.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>     524       0       0     524     20c _gcov_execle.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>      98       0       0      98      62 _gcov_execv.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>      98       0       0      98      62 _gcov_execvp.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>     108       0       0     108      6c _gcov_execve.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>      72       0       0      72      48 _gcov_reset.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>      72       0       0      72      48 _gcov_dump.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>      89       0       0      89      59 _gcov_interval_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>      69       0       0      69      45 _gcov_pow2_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>     115       0       0     115      73 _gcov_one_value_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>     122       0       0     122      7a _gcov_indirect_call_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>      57       0       0      57      39 _gcov_average_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>      52       0       0      52      34 _gcov_ior_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>     125       0       0     125      7d _gcov_merge_ior.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>
>>> Index: gcc/gcov-io.h
>>> ===================================================================
>>> --- gcc/gcov-io.h     (revision 204895)
>>> +++ gcc/gcov-io.h     (working copy)
>>> @@ -164,51 +164,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
>>>  #ifndef GCC_GCOV_IO_H
>>>  #define GCC_GCOV_IO_H
>>>
>>> -#if IN_LIBGCOV
>>> -/* About the target */
>>> -
>>> -#if BITS_PER_UNIT == 8
>>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI)));
>>> -typedef unsigned gcov_position_t __attribute__ ((mode (SI)));
>>> -#if LONG_LONG_TYPE_SIZE > 32
>>> -typedef signed gcov_type __attribute__ ((mode (DI)));
>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (DI)));
>>> -#else
>>> -typedef signed gcov_type __attribute__ ((mode (SI)));
>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
>>> -#endif
>>> -#else
>>> -#if BITS_PER_UNIT == 16
>>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (HI)));
>>> -typedef unsigned gcov_position_t __attribute__ ((mode (HI)));
>>> -#if LONG_LONG_TYPE_SIZE > 32
>>> -typedef signed gcov_type __attribute__ ((mode (SI)));
>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
>>> -#else
>>> -typedef signed gcov_type __attribute__ ((mode (HI)));
>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
>>> -#endif
>>> -#else
>>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (QI)));
>>> -typedef unsigned gcov_position_t __attribute__ ((mode (QI)));
>>> -#if LONG_LONG_TYPE_SIZE > 32
>>> -typedef signed gcov_type __attribute__ ((mode (HI)));
>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
>>> -#else
>>> -typedef signed gcov_type __attribute__ ((mode (QI)));
>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI)));
>>> -#endif
>>> -#endif
>>> -#endif
>>
>> So this part basically moves libgcov specific bits into libgcov.h? That OK fine by
>> itself.
>>> Index: libgcc/libgcov-profiler.c
>>> ===================================================================
>>> --- libgcc/libgcov-profiler.c (revision 204895)
>>> +++ libgcc/libgcov-profiler.c (working copy)
>>> @@ -23,15 +23,8 @@ a copy of the GCC Runtime Library Exception along
>>>  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>>>  <http://www.gnu.org/licenses/>.  */
>>>
>>> -#include "tconfig.h"
>>> -#include "tsystem.h"
>>> -#include "coretypes.h"
>>> -#include "tm.h"
>>> -#include "libgcc_tm.h"
>>> -
>>> +#include "libgcov.h"
>>>  #if !defined(inhibit_libc)
>>> -#define IN_LIBGCOV 1
>>> -#include "gcov-io.h"
>>
>> I did not pay that much attention into the current include file changes, but wasn't
>> idea to avoid #include file to include random other #includes?
>> So perhaps the first block of includes should stay, followed by libgcov.h and gcov-io.h
>> last?
>>> @@ -325,6 +311,9 @@ static struct gcov_summary all_prg;
>>>  #endif
>>>  /* crc32 for this program.  */
>>>  static gcov_unsigned_t crc32;
>>> +/* Use this summary checksum rather the computed one if the value is
>>> + *    non-zero.  */
>>> +static gcov_unsigned_t saved_summary_checksum;
>>
>> Why do you need to save the checksum? Won't it reset summary back with multiple streaming?
>>
>> I would really like to avoid introducing those static vars that are used exclusively
>> by gcov_exit.  What about putting them into an gcov_context structure that
>> is passed around the functions that was broken out?
>>> Index: libgcc/libgcov-merge.c
>>> ===================================================================
>>> --- libgcc/libgcov-merge.c    (revision 204895)
>>> +++ libgcc/libgcov-merge.c    (working copy)
>>> @@ -23,107 +23,150 @@ a copy of the GCC Runtime Library Exception along
>>>  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>>>  <http://www.gnu.org/licenses/>.  */
>>>
>>> -#include "tconfig.h"
>>> -#include "tsystem.h"
>>> -#include "coretypes.h"
>>> -#include "tm.h"
>>> -#include "libgcc_tm.h"
>>> +#include "libgcov.h"
>>>
>>> -#if defined(inhibit_libc)
>>> -#define IN_LIBGCOV (-1)
>>> -#else
>>> -#define IN_LIBGCOV 1
>>> +#include "gcov-io-libgcov.h"
>>>  #endif
>>>
>>> -#include "gcov-io.h"
>>> -
>>>  #if defined(inhibit_libc)
>>>  /* If libc and its header files are not available, provide dummy functions.  */
>>>
>>>  #ifdef L_gcov_merge_add
>>>  void __gcov_merge_add (gcov_type *counters  __attribute__ ((unused)),
>>> -                       unsigned n_counters __attribute__ ((unused))) {}
>>> +                       unsigned n_counters __attribute__ ((unused)),
>>> +                       unsigned gcov_type *src __attribute__ ((unused)),
>>> +                       unsigned w __attribute__ ((unused))) {}
>>>  #endif
>>>
>>>  #ifdef L_gcov_merge_single
>>>  void __gcov_merge_single (gcov_type *counters  __attribute__ ((unused)),
>>> -                          unsigned n_counters __attribute__ ((unused))) {}
>>> +                          unsigned n_counters __attribute__ ((unused)),
>>> +                          unsigned gcov_type *src __attribute__ ((unused)),
>>> +                          unsigned w __attribute__ ((unused))) {}
>>>  #endif
>>>
>>>  #ifdef L_gcov_merge_delta
>>>  void __gcov_merge_delta (gcov_type *counters  __attribute__ ((unused)),
>>> -                         unsigned n_counters __attribute__ ((unused))) {}
>>> +                         unsigned n_counters __attribute__ ((unused)),
>>> +                         unsigned gcov_type *src __attribute__ ((unused)),
>>> +                         unsigned w __attribute__ ((unused))) {}
>>>  #endif
>>>
>>>  #else
>>>
>>>  #ifdef L_gcov_merge_add
>>>  /* The profile merging function that just adds the counters.  It is given
>>> -   an array COUNTERS of N_COUNTERS old counters and it reads the same number
>>> -   of counters from the gcov file.  */
>>> +   an array COUNTERS of N_COUNTERS old counters.
>>> +   When SRC==NULL, it reads the same number of counters from the gcov file.
>>> +   Otherwise, it reads from SRC array. These read values will be multiplied
>>> +   by weight W before adding to the old counters.  */
>>>  void
>>> -__gcov_merge_add (gcov_type *counters, unsigned n_counters)
>>> +__gcov_merge_add (gcov_type *counters, unsigned n_counters,
>>> +                  gcov_type *src, unsigned w)
>>>  {
>>> +  int in_mem = (src != 0);
>>> +
>>>    for (; n_counters; counters++, n_counters--)
>>> -    *counters += gcov_read_counter ();
>>> +    {
>>> +      gcov_type value;
>>> +
>>> +      if (in_mem)
>>> +        value = *(src++);
>>> +      else
>>> +        value = gcov_read_counter ();
>>> +
>>> +      *counters += w * value;
>>> +    }
>>>  }
>>>  #endif /* L_gcov_merge_add */
>>>
>>>  #ifdef L_gcov_merge_ior
>>>  /* The profile merging function that just adds the counters.  It is given
>>> -   an array COUNTERS of N_COUNTERS old counters and it reads the same number
>>> -   of counters from the gcov file.  */
>>> +   an array COUNTERS of N_COUNTERS old counters.
>>> +   When SRC==NULL, it reads the same number of counters from the gcov file.
>>> +   Otherwise, it reads from SRC array.  */
>>>  void
>>> -__gcov_merge_ior (gcov_type *counters, unsigned n_counters)
>>> +__gcov_merge_ior (gcov_type *counters, unsigned n_counters,
>>> +                  gcov_type *src, unsigned w __attribute__ ((unused)))
>>
>> So the new in-memory variants are introduced for merging tool, while libgcc use gcov_read_counter
>> interface?
>> Perhaps we can actually just duplicate the functions to avoid runtime to do all the scalling
>> and in_mem tests it won't need?
>>
>> I would suggest going with libgcov.h changes and clenaups first, with interface changes next
>> and the gcov-tool is probably quite obvious at the end?
>> Do you think you can split the patch this way?
>>
>> Thanks and sorry for taking long to review. I should have more time again now.
>> Honza



-- 
Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-12-06 14:23               ` Jan Hubicka
  2013-12-10 19:02                 ` Xinliang David Li
@ 2013-12-12  6:05                 ` Teresa Johnson
  2013-12-12 20:11                   ` Teresa Johnson
  2013-12-18 17:28                 ` Xinliang David Li
                                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 55+ messages in thread
From: Teresa Johnson @ 2013-12-12  6:05 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Rong Xu, Xinliang David Li, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 18545 bytes --]

On Fri, Dec 6, 2013 at 6:23 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>> Hi, all
>>
>> This is the new patch for gcov-tool (previously profile-tool).
>>
>> Honza: can you comment on the new merge interface? David posted some
>> comments in an earlier email and we want to know what's your opinion.
>>
>> Test patch has been tested with boostrap, regresssion,
>> profiledbootstrap and SPEC2006.
>>
>> Noticeable changes from the earlier version:
>>
>> 1. create a new file libgcov.h and move libgcov-*.h headers to libgcov.h
>> So we can included multiple libgcov-*.c without adding new macros.
>>
>> 2. split libgcov.h specific code in gcvo-io.h to libcc/libgcov.h
>> Avoid multiple-page of code under IN_LIBGCOV macro -- this
>> improves the readability.
>>
>> 3. make gcov_var static, and move the definition from gcov-io.h to
>> gcov-io.c. Also
>>    move some static functions accessing gcov_var to gcvo-io.c
>> Current code rely on GCOV_LINKAGE tricks to avoid multi-definition. I don't see
>> a reason that gcov_var needs to exposed as a global.
>>
>> 4. expose gcov_write_strings() and gcov_sync() to gcov_tool usage
>>
>> 5. rename profile-tool to gcov-tool per Honza's suggestion.
>>
>> Thanks,
>
> Hi,
> I did not read in deatil the gcov-tool source itself, but lets first make the interface changes
> needed.
>
>> 2013-11-18  Rong Xu  <xur@google.com>
>>
>>       * gcc/gcov-io.c (gcov_var): Moved from gcov-io.h and make it static.
>>       (gcov_position): Move from gcov-io.h
>>       (gcov_is_error): Ditto.
>>       (gcov_rewrite): Ditto.
>>       * gcc/gcov-io.h: Re-factoring. Move gcov_var to gcov-io.h and
>>         move the libgcov only part of libgcc/libgcov.h.
>>       * libgcc/libgcov.h: New common header files for libgcov-*.h
>>       * libgcc/Makefile.in: Add dependence to libgcov.h
>>       * libgcc/libgcov-profiler.c: Use libgcov.h
>>       * libgcc/libgcov-driver.c: Ditto.
>>       * libgcc/libgcov-interface.c: Ditto.
>>       * libgcc/libgcov-driver-system.c (allocate_filename_struct): use
>>       xmalloc instread of malloc.
>>       * libgcc/libgcov-merge.c (void __gcov_merge_delta): Add more
>>       parameters to merge function.
>>       (__gcov_merge_add): Ditto.
>>       (__gcov_merge_ior): Ditto.
>>       (__gcov_merge_time_profile): Ditto.
>>       (__gcov_merge_single): Ditto.
>>       (__gcov_merge_delta): Ditto.
>>       * libgcc/libgcov-tool.c (void gcov_tool_set_verbose): New for
>>       gcov-tool support.
>>       (set_fn_ctrs): Ditto.
>>       (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_merge): Ditto.
>>       (find_match_gcov_inf Ditto.o):
>>       (gcov_profile_merge): Ditto.
>>       (__gcov_scale_add): Ditto.
>>       (__gcov_scale_ior): Ditto.
>>       (__gcov_scale_delta): Ditto.
>>       (__gcov_scale_single): Ditto.
>>       (gcov_profile_scale): Ditto.
>>       (gcov_profile_normalize): Ditto.
>>       (__gcov_scale2_add): Ditto.
>>       (__gcov_scale2_ior): Ditto.
>>       (__gcov_scale2_delta): Ditto.
>>       (__gcov_scale2_single): Ditto.
>>       (gcov_profile_scale2): Ditto.
>>       * gcc/gcov-tool.c (unlink_file): Gcov-tool driver support.
>>       (unlink_dir): Ditto.
>>       (profile_merge): Ditto.
>>       (print_merge_usage_message): Ditto.
>>       (merge_usage): Ditto.
>>       (do_merge): Ditto.
>>       (profile_rewrite2): Ditto.
>>       (profile_rewrite): Ditto.
>>       (print_rewrite_usage_message): Ditto.
>>       (rewrite_usage): Ditto.
>>       (do_rewrite): Ditto.
>>       (print_usage): Ditto.
>>       (print_version): Ditto.
>>       (process_args): Ditto.
>>       (main): Ditto.
>>       * gcc/Makefile.in: Build and install gcov-tool.
>
>> Index: gcc/gcov-io.c
>> ===================================================================
>> --- gcc/gcov-io.c     (revision 204895)
>> +++ gcc/gcov-io.c     (working copy)
>> @@ -36,6 +36,37 @@ static const gcov_unsigned_t *gcov_read_words (uns
>>  static void gcov_allocate (unsigned);
>>  #endif
>>
>> +/* Moved for gcov-io.h and make it static.  */
>> +static struct gcov_var gcov_var;
>
> This is more an changelog message than a comment in source file.
> Just describe what gcov_var is.

I changed this so gcov_var is no longer static, but global as before.

>
> Do you know how the size of libgcov changed with your patch?
> Quick check of current mainline on compiling empty main gives:
>
> jh@gcc10:~/trunk/build/gcc$ cat t.c
> main()
> {
> }
> jh@gcc10:~/trunk/build/gcc$ ./xgcc -B ./ -O2 -fprofile-generate -o a.out-new --static t.c
> jh@gcc10:~/trunk/build/gcc$ gcc -O2 -fprofile-generate -o a.out-old --static t.c
> jh@gcc10:~/trunk/build/gcc$ size a.out-old
>    text    data     bss     dec     hex filename
>  608141    3560   16728  628429   996cd a.out-old
> jh@gcc10:~/trunk/build/gcc$ size a.out-new
>    text    data     bss     dec     hex filename
>  612621    3688   22880  639189   9c0d5 a.out-new
>
> Without profiling I get:
> jh@gcc10:~/trunk/build/gcc$ size a.out-new-no
> jh@gcc10:~/trunk/build/gcc$ size a.out-old-no
>    text    data     bss     dec     hex filename
>  599719    3448   12568  615735   96537 a.out-old-no
>    text    data     bss     dec     hex filename
>  600247    3448   12568  616263   96747 a.out-new-no
>
> Quite big for empty program, but mostly glibc fault, I suppose
> (that won't be an issue for embedded platforms). But anyway
> we increased text size overhead from 8k to 12k, BSS size
> overhead from 4k to 10k and data by another 1k.
>
>    text    data     bss     dec     hex filename
>     126       0       0     126      7e _gcov_merge_add.o (ex libgcov.a)
>     251       0       0     251      fb _gcov_merge_single.o (ex libgcov.a)
>     242       0       0     242      f2 _gcov_merge_delta.o (ex libgcov.a)
>     126       0       0     126      7e _gcov_merge_ior.o (ex libgcov.a)
>     156       0       0     156      9c _gcov_merge_time_profile.o (ex libgcov.a)
>      89       0       0      89      59 _gcov_interval_profiler.o (ex libgcov.a)
>      69       0       0      69      45 _gcov_pow2_profiler.o (ex libgcov.a)
>     115       0       0     115      73 _gcov_one_value_profiler.o (ex libgcov.a)
>     122       0       0     122      7a _gcov_indirect_call_profiler.o (ex libgcov.a)
>      57       0       0      57      39 _gcov_average_profiler.o (ex libgcov.a)
>      52       0       0      52      34 _gcov_ior_profiler.o (ex libgcov.a)
>     178       0      16     194      c2 _gcov_indirect_call_profiler_v2.o (ex libgcov.a)
>      77       0       8      85      55 _gcov_time_profiler.o (ex libgcov.a)
>     126       0      40     166      a6 _gcov_flush.o (ex libgcov.a)
>     101       0       0     101      65 _gcov_fork.o (ex libgcov.a)
>     471       0       0     471     1d7 _gcov_execl.o (ex libgcov.a)
>     471       0       0     471     1d7 _gcov_execlp.o (ex libgcov.a)
>     524       0       0     524     20c _gcov_execle.o (ex libgcov.a)
>      98       0       0      98      62 _gcov_execv.o (ex libgcov.a)
>      98       0       0      98      62 _gcov_execvp.o (ex libgcov.a)
>     108       0       0     108      6c _gcov_execve.o (ex libgcov.a)
>      66       0       0      66      42 _gcov_reset.o (ex libgcov.a)
>      66       0       0      66      42 _gcov_dump.o (ex libgcov.a)
>    9505       0    6144   15649    3d21 _gcov.o (ex libgcov.a)
>
> I think we definitely need to move those 6k of bss space out.  I think those are new
> static vars you introduced that I think are unsafe anyway because multiple streaming
> may run at once in threaded program where locking mechanizm fails.
> (it will probably do other bad things, but definitely we do not want to
> conflict on things like filename to write into).
>
> Compared to my system gcov:
>    text    data     bss     dec     hex filename
>    9765       0      64    9829    2665 _gcov.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     125       0       0     125      7d _gcov_merge_add.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     251       0       0     251      fb _gcov_merge_single.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     242       0       0     242      f2 _gcov_merge_delta.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     101       0       0     101      65 _gcov_fork.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     471       0       0     471     1d7 _gcov_execl.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     471       0       0     471     1d7 _gcov_execlp.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     524       0       0     524     20c _gcov_execle.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>      98       0       0      98      62 _gcov_execv.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>      98       0       0      98      62 _gcov_execvp.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     108       0       0     108      6c _gcov_execve.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>      72       0       0      72      48 _gcov_reset.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>      72       0       0      72      48 _gcov_dump.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>      89       0       0      89      59 _gcov_interval_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>      69       0       0      69      45 _gcov_pow2_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     115       0       0     115      73 _gcov_one_value_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     122       0       0     122      7a _gcov_indirect_call_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>      57       0       0      57      39 _gcov_average_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>      52       0       0      52      34 _gcov_ior_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     125       0       0     125      7d _gcov_merge_ior.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>
>> Index: gcc/gcov-io.h
>> ===================================================================
>> --- gcc/gcov-io.h     (revision 204895)
>> +++ gcc/gcov-io.h     (working copy)
>> @@ -164,51 +164,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
>>  #ifndef GCC_GCOV_IO_H
>>  #define GCC_GCOV_IO_H
>>
>> -#if IN_LIBGCOV
>> -/* About the target */
>> -
>> -#if BITS_PER_UNIT == 8
>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI)));
>> -typedef unsigned gcov_position_t __attribute__ ((mode (SI)));
>> -#if LONG_LONG_TYPE_SIZE > 32
>> -typedef signed gcov_type __attribute__ ((mode (DI)));
>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (DI)));
>> -#else
>> -typedef signed gcov_type __attribute__ ((mode (SI)));
>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
>> -#endif
>> -#else
>> -#if BITS_PER_UNIT == 16
>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (HI)));
>> -typedef unsigned gcov_position_t __attribute__ ((mode (HI)));
>> -#if LONG_LONG_TYPE_SIZE > 32
>> -typedef signed gcov_type __attribute__ ((mode (SI)));
>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
>> -#else
>> -typedef signed gcov_type __attribute__ ((mode (HI)));
>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
>> -#endif
>> -#else
>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (QI)));
>> -typedef unsigned gcov_position_t __attribute__ ((mode (QI)));
>> -#if LONG_LONG_TYPE_SIZE > 32
>> -typedef signed gcov_type __attribute__ ((mode (HI)));
>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
>> -#else
>> -typedef signed gcov_type __attribute__ ((mode (QI)));
>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI)));
>> -#endif
>> -#endif
>> -#endif
>
> So this part basically moves libgcov specific bits into libgcov.h? That OK fine by
> itself.
>> Index: libgcc/libgcov-profiler.c
>> ===================================================================
>> --- libgcc/libgcov-profiler.c (revision 204895)
>> +++ libgcc/libgcov-profiler.c (working copy)
>> @@ -23,15 +23,8 @@ a copy of the GCC Runtime Library Exception along
>>  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>>  <http://www.gnu.org/licenses/>.  */
>>
>> -#include "tconfig.h"
>> -#include "tsystem.h"
>> -#include "coretypes.h"
>> -#include "tm.h"
>> -#include "libgcc_tm.h"
>> -
>> +#include "libgcov.h"
>>  #if !defined(inhibit_libc)
>> -#define IN_LIBGCOV 1
>> -#include "gcov-io.h"
>
> I did not pay that much attention into the current include file changes, but wasn't
> idea to avoid #include file to include random other #includes?
> So perhaps the first block of includes should stay, followed by libgcov.h and gcov-io.h
> last?

I'm not sure I understand the issue here? The patch basically moves
the same includes into libgcov.h, and includes that instead. I see
many other header files in gcc that include other headers.

>> @@ -325,6 +311,9 @@ static struct gcov_summary all_prg;
>>  #endif
>>  /* crc32 for this program.  */
>>  static gcov_unsigned_t crc32;
>> +/* Use this summary checksum rather the computed one if the value is
>> + *    non-zero.  */
>> +static gcov_unsigned_t saved_summary_checksum;
>
> Why do you need to save the checksum? Won't it reset summary back with multiple streaming?

This has been removed.

>
> I would really like to avoid introducing those static vars that are used exclusively
> by gcov_exit.  What about putting them into an gcov_context structure that
> is passed around the functions that was broken out?
>> Index: libgcc/libgcov-merge.c
>> ===================================================================
>> --- libgcc/libgcov-merge.c    (revision 204895)
>> +++ libgcc/libgcov-merge.c    (working copy)
>> @@ -23,107 +23,150 @@ a copy of the GCC Runtime Library Exception along
>>  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>>  <http://www.gnu.org/licenses/>.  */
>>
>> -#include "tconfig.h"
>> -#include "tsystem.h"
>> -#include "coretypes.h"
>> -#include "tm.h"
>> -#include "libgcc_tm.h"
>> +#include "libgcov.h"
>>
>> -#if defined(inhibit_libc)
>> -#define IN_LIBGCOV (-1)
>> -#else
>> -#define IN_LIBGCOV 1
>> +#include "gcov-io-libgcov.h"
>>  #endif
>>
>> -#include "gcov-io.h"
>> -
>>  #if defined(inhibit_libc)
>>  /* If libc and its header files are not available, provide dummy functions.  */
>>
>>  #ifdef L_gcov_merge_add
>>  void __gcov_merge_add (gcov_type *counters  __attribute__ ((unused)),
>> -                       unsigned n_counters __attribute__ ((unused))) {}
>> +                       unsigned n_counters __attribute__ ((unused)),
>> +                       unsigned gcov_type *src __attribute__ ((unused)),
>> +                       unsigned w __attribute__ ((unused))) {}
>>  #endif
>>
>>  #ifdef L_gcov_merge_single
>>  void __gcov_merge_single (gcov_type *counters  __attribute__ ((unused)),
>> -                          unsigned n_counters __attribute__ ((unused))) {}
>> +                          unsigned n_counters __attribute__ ((unused)),
>> +                          unsigned gcov_type *src __attribute__ ((unused)),
>> +                          unsigned w __attribute__ ((unused))) {}
>>  #endif
>>
>>  #ifdef L_gcov_merge_delta
>>  void __gcov_merge_delta (gcov_type *counters  __attribute__ ((unused)),
>> -                         unsigned n_counters __attribute__ ((unused))) {}
>> +                         unsigned n_counters __attribute__ ((unused)),
>> +                         unsigned gcov_type *src __attribute__ ((unused)),
>> +                         unsigned w __attribute__ ((unused))) {}
>>  #endif
>>
>>  #else
>>
>>  #ifdef L_gcov_merge_add
>>  /* The profile merging function that just adds the counters.  It is given
>> -   an array COUNTERS of N_COUNTERS old counters and it reads the same number
>> -   of counters from the gcov file.  */
>> +   an array COUNTERS of N_COUNTERS old counters.
>> +   When SRC==NULL, it reads the same number of counters from the gcov file.
>> +   Otherwise, it reads from SRC array. These read values will be multiplied
>> +   by weight W before adding to the old counters.  */
>>  void
>> -__gcov_merge_add (gcov_type *counters, unsigned n_counters)
>> +__gcov_merge_add (gcov_type *counters, unsigned n_counters,
>> +                  gcov_type *src, unsigned w)
>>  {
>> +  int in_mem = (src != 0);
>> +
>>    for (; n_counters; counters++, n_counters--)
>> -    *counters += gcov_read_counter ();
>> +    {
>> +      gcov_type value;
>> +
>> +      if (in_mem)
>> +        value = *(src++);
>> +      else
>> +        value = gcov_read_counter ();
>> +
>> +      *counters += w * value;
>> +    }
>>  }
>>  #endif /* L_gcov_merge_add */
>>
>>  #ifdef L_gcov_merge_ior
>>  /* The profile merging function that just adds the counters.  It is given
>> -   an array COUNTERS of N_COUNTERS old counters and it reads the same number
>> -   of counters from the gcov file.  */
>> +   an array COUNTERS of N_COUNTERS old counters.
>> +   When SRC==NULL, it reads the same number of counters from the gcov file.
>> +   Otherwise, it reads from SRC array.  */
>>  void
>> -__gcov_merge_ior (gcov_type *counters, unsigned n_counters)
>> +__gcov_merge_ior (gcov_type *counters, unsigned n_counters,
>> +                  gcov_type *src, unsigned w __attribute__ ((unused)))
>
> So the new in-memory variants are introduced for merging tool, while libgcc use gcov_read_counter
> interface?
> Perhaps we can actually just duplicate the functions to avoid runtime to do all the scalling
> and in_mem tests it won't need?
>
> I would suggest going with libgcov.h changes and clenaups first, with interface changes next
> and the gcov-tool is probably quite obvious at the end?
> Do you think you can split the patch this way?
>
> Thanks and sorry for taking long to review. I should have more time again now.
> Honza

The libgcov.h related changes are in the attached patch. I think it
addresses your concerns. Bootstrapped and tested on
x86-64-unknown-linux-gnu. A profiledbootstrap is in progress.

Ok for trunk if profiledbootstrap passes?

Thanks,
Teresa


-- 
Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413

[-- Attachment #2: patch.txt --]
[-- Type: text/plain, Size: 26927 bytes --]

2013-12-11  Rong Xu  <xur@google.com>

	* gcc/gcov-io.c (gcov_var): Move from gcov-io.h.
	(gcov_position): Ditto.
	(gcov_is_error): Ditto.
	(gcov_rewrite): Ditto.
	* gcc/gcov-io.h: Refactor. Move gcov_var to gcov-io.h, and libgcov
        only part to libgcc/libgcov.h.
	* libgcc/libgcov-driver.c: Use libgcov.h.
        (buffer_fn_data): Use xmalloc instead of malloc.
	(gcov_exit_merge_gcda): Ditto.
	* libgcc/libgcov-driver-system.c (allocate_filename_struct): Ditto.
	* libgcc/libgcov.h: New common header files for libgcov-*.h.
	* libgcc/libgcov-interface.c: Use libgcov.h
	* libgcc/libgcov-merge.c: Ditto.
	* libgcc/libgcov-profiler.c: Ditto.
	* libgcc/Makefile.in: Add dependence to libgcov.h

Index: gcc/gcov-io.c
===================================================================
--- gcc/gcov-io.c	(revision 205895)
+++ gcc/gcov-io.c	(working copy)
@@ -36,6 +36,36 @@ static const gcov_unsigned_t *gcov_read_words (uns
 static void gcov_allocate (unsigned);
 #endif
 
+GCOV_LINKAGE struct gcov_var gcov_var;
+
+/* Save the current position in the gcov file.  */
+static inline gcov_position_t
+gcov_position (void)
+{
+  gcc_assert (gcov_var.mode > 0); 
+  return gcov_var.start + gcov_var.offset;
+}
+
+/* Return nonzero if the error flag is set.  */
+static inline int 
+gcov_is_error (void)
+{
+  return gcov_var.file ? gcov_var.error : 1;
+}
+
+#if IN_LIBGCOV
+/* Move to beginning of file and initialize for writing.  */
+GCOV_LINKAGE inline void
+gcov_rewrite (void)
+{
+  gcc_assert (gcov_var.mode > 0); 
+  gcov_var.mode = -1; 
+  gcov_var.start = 0;
+  gcov_var.offset = 0;
+  fseek (gcov_var.file, 0L, SEEK_SET);
+}
+#endif
+
 static inline gcov_unsigned_t from_file (gcov_unsigned_t value)
 {
 #if !IN_LIBGCOV
Index: gcc/gcov-io.h
===================================================================
--- gcc/gcov-io.h	(revision 205895)
+++ gcc/gcov-io.h	(working copy)
@@ -164,51 +164,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
 #ifndef GCC_GCOV_IO_H
 #define GCC_GCOV_IO_H
 
-#if IN_LIBGCOV
-/* About the target */
-
-#if BITS_PER_UNIT == 8
-typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI)));
-typedef unsigned gcov_position_t __attribute__ ((mode (SI)));
-#if LONG_LONG_TYPE_SIZE > 32
-typedef signed gcov_type __attribute__ ((mode (DI)));
-typedef unsigned gcov_type_unsigned __attribute__ ((mode (DI)));
-#else
-typedef signed gcov_type __attribute__ ((mode (SI)));
-typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
-#endif
-#else
-#if BITS_PER_UNIT == 16
-typedef unsigned gcov_unsigned_t __attribute__ ((mode (HI)));
-typedef unsigned gcov_position_t __attribute__ ((mode (HI)));
-#if LONG_LONG_TYPE_SIZE > 32
-typedef signed gcov_type __attribute__ ((mode (SI)));
-typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
-#else
-typedef signed gcov_type __attribute__ ((mode (HI)));
-typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
-#endif
-#else
-typedef unsigned gcov_unsigned_t __attribute__ ((mode (QI)));
-typedef unsigned gcov_position_t __attribute__ ((mode (QI)));
-#if LONG_LONG_TYPE_SIZE > 32
-typedef signed gcov_type __attribute__ ((mode (HI)));
-typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
-#else
-typedef signed gcov_type __attribute__ ((mode (QI)));
-typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI)));
-#endif
-#endif
-#endif
-
-
-#if defined (TARGET_POSIX_IO)
-#define GCOV_LOCKED 1
-#else
-#define GCOV_LOCKED 0
-#endif
-
-#else /* !IN_LIBGCOV */
+#ifndef IN_LIBGCOV
 /* About the host */
 
 typedef unsigned gcov_unsigned_t;
@@ -231,48 +187,10 @@ typedef unsigned HOST_WIDEST_INT gcov_type_unsigne
 #define GCOV_LOCKED 0
 #endif
 
-#endif /* !IN_LIBGCOV */
-
-/* In gcov we want function linkage to be static.  In the compiler we want
-   it extern, so that they can be accessed from elsewhere.  In libgcov we
-   need these functions to be extern, so prefix them with __gcov.  In
-   libgcov they must also be hidden so that the instance in the executable
-   is not also used in a DSO.  */
-#if IN_LIBGCOV
-
-#include "tconfig.h"
-
-#define gcov_var __gcov_var
-#define gcov_open __gcov_open
-#define gcov_close __gcov_close
-#define gcov_write_tag_length __gcov_write_tag_length
-#define gcov_position __gcov_position
-#define gcov_seek __gcov_seek
-#define gcov_rewrite __gcov_rewrite
-#define gcov_is_error __gcov_is_error
-#define gcov_write_unsigned __gcov_write_unsigned
-#define gcov_write_counter __gcov_write_counter
-#define gcov_write_summary __gcov_write_summary
-#define gcov_read_unsigned __gcov_read_unsigned
-#define gcov_read_counter __gcov_read_counter
-#define gcov_read_summary __gcov_read_summary
-
-/* Poison these, so they don't accidentally slip in.  */
-#pragma GCC poison gcov_write_string gcov_write_tag gcov_write_length
-#pragma GCC poison gcov_read_string gcov_sync gcov_time gcov_magic
-
-#ifdef HAVE_GAS_HIDDEN
-#define ATTRIBUTE_HIDDEN  __attribute__ ((__visibility__ ("hidden")))
-#else
 #define ATTRIBUTE_HIDDEN
-#endif
 
-#else
+#endif /* !IN_LIBGOCV */
 
-#define ATTRIBUTE_HIDDEN
-
-#endif
-
 #ifndef GCOV_LINKAGE
 #define GCOV_LINKAGE extern
 #endif
@@ -442,110 +360,12 @@ struct gcov_summary
   struct gcov_ctr_summary ctrs[GCOV_COUNTERS_SUMMABLE];
 };
 
-/* Structures embedded in coveraged program.  The structures generated
-   by write_profile must match these.  */
+#if !defined(inhibit_libc)
 
-#if IN_LIBGCOV
-/* Information about counters for a single function.  */
-struct gcov_ctr_info
-{
-  gcov_unsigned_t num;		/* number of counters.  */
-  gcov_type *values;		/* their values.  */
-};
-
-/* Information about a single function.  This uses the trailing array
-   idiom. The number of counters is determined from the merge pointer
-   array in gcov_info.  The key is used to detect which of a set of
-   comdat functions was selected -- it points to the gcov_info object
-   of the object file containing the selected comdat function.  */
-
-struct gcov_fn_info
-{
-  const struct gcov_info *key;		/* comdat key */
-  gcov_unsigned_t ident;		/* unique ident of function */
-  gcov_unsigned_t lineno_checksum;	/* function lineo_checksum */
-  gcov_unsigned_t cfg_checksum;		/* function cfg checksum */
-  struct gcov_ctr_info ctrs[0];		/* instrumented counters */
-};
-
-/* Type of function used to merge counters.  */
-typedef void (*gcov_merge_fn) (gcov_type *, gcov_unsigned_t);
-
-/* Information about a single object file.  */
-struct gcov_info
-{
-  gcov_unsigned_t version;	/* expected version number */
-  struct gcov_info *next;	/* link to next, used by libgcov */
-
-  gcov_unsigned_t stamp;	/* uniquifying time stamp */
-  const char *filename;		/* output file name */
-
-  gcov_merge_fn merge[GCOV_COUNTERS];  /* merge functions (null for
-					  unused) */
-  
-  unsigned n_functions;		/* number of functions */
-  const struct gcov_fn_info *const *functions; /* pointer to pointers
-					          to function information  */
-};
-
-/* Register a new object file module.  */
-extern void __gcov_init (struct gcov_info *) ATTRIBUTE_HIDDEN;
-
-/* Called before fork, to avoid double counting.  */
-extern void __gcov_flush (void) ATTRIBUTE_HIDDEN;
-
-/* Function to reset all counters to 0.  */
-extern void __gcov_reset (void);
-
-/* Function to enable early write of profile information so far.  */
-extern void __gcov_dump (void);
-
-/* The merge function that just sums the counters.  */
-extern void __gcov_merge_add (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
-
-/* The merge function to choose the most common value.  */
-extern void __gcov_merge_single (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
-
-/* The merge function to choose the most common difference between
-   consecutive values.  */
-extern void __gcov_merge_delta (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
-
-/* The merge function that just ors the counters together.  */
-extern void __gcov_merge_ior (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
-
-extern void __gcov_merge_time_profile (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
-
-/* The profiler functions.  */
-extern void __gcov_interval_profiler (gcov_type *, gcov_type, int, unsigned);
-extern void __gcov_pow2_profiler (gcov_type *, gcov_type);
-extern void __gcov_one_value_profiler (gcov_type *, gcov_type);
-extern void __gcov_indirect_call_profiler (gcov_type*, gcov_type,
-                                           void*, void*);
-extern void __gcov_indirect_call_profiler_v2 (gcov_type, void *);
-extern void __gcov_average_profiler (gcov_type *, gcov_type);
-extern void __gcov_ior_profiler (gcov_type *, gcov_type);
-extern void __gcov_time_profiler (gcov_type *);
-
-#ifndef inhibit_libc
-/* The wrappers around some library functions..  */
-extern pid_t __gcov_fork (void) ATTRIBUTE_HIDDEN;
-extern int __gcov_execl (const char *, char *, ...) ATTRIBUTE_HIDDEN;
-extern int __gcov_execlp (const char *, char *, ...) ATTRIBUTE_HIDDEN;
-extern int __gcov_execle (const char *, char *, ...) ATTRIBUTE_HIDDEN;
-extern int __gcov_execv (const char *, char *const []) ATTRIBUTE_HIDDEN;
-extern int __gcov_execvp (const char *, char *const []) ATTRIBUTE_HIDDEN;
-extern int __gcov_execve (const char *, char  *const [], char *const [])
-  ATTRIBUTE_HIDDEN;
-#endif
-
-#endif /* IN_LIBGCOV */
-
-#if IN_LIBGCOV >= 0
-
 /* Optimum number of gcov_unsigned_t's read from or written to disk.  */
 #define GCOV_BLOCK_SIZE (1 << 10)
 
-GCOV_LINKAGE struct gcov_var
+struct gcov_var
 {
   FILE *file;
   gcov_position_t start;	/* Position of first byte of block */
@@ -567,7 +387,7 @@ struct gcov_summary
   size_t alloc;
   gcov_unsigned_t *buffer;
 #endif
-} gcov_var ATTRIBUTE_HIDDEN;
+};
 
 /* Functions for reading and writing gcov files. In libgcov you can
    open the file for reading then writing. Elsewhere you can open the
@@ -578,38 +398,20 @@ struct gcov_summary
    you use the functions for reading, then gcov_rewrite then the
    functions for writing.  Your file may become corrupted if you break
    these invariants.  */
-#if IN_LIBGCOV
-GCOV_LINKAGE int gcov_open (const char */*name*/) ATTRIBUTE_HIDDEN;
-#else
+
+#if !IN_LIBGCOV
 GCOV_LINKAGE int gcov_open (const char */*name*/, int /*direction*/);
 GCOV_LINKAGE int gcov_magic (gcov_unsigned_t, gcov_unsigned_t);
 #endif
-GCOV_LINKAGE int gcov_close (void) ATTRIBUTE_HIDDEN;
 
 /* Available everywhere.  */
-static gcov_position_t gcov_position (void);
-static int gcov_is_error (void);
-
+GCOV_LINKAGE int gcov_close (void) ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE gcov_unsigned_t gcov_read_unsigned (void) ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE gcov_type gcov_read_counter (void) ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE void gcov_read_summary (struct gcov_summary *) ATTRIBUTE_HIDDEN;
-
-#if IN_LIBGCOV
-/* Available only in libgcov */
-GCOV_LINKAGE void gcov_write_counter (gcov_type) ATTRIBUTE_HIDDEN;
-GCOV_LINKAGE void gcov_write_tag_length (gcov_unsigned_t, gcov_unsigned_t)
-    ATTRIBUTE_HIDDEN;
-GCOV_LINKAGE void gcov_write_summary (gcov_unsigned_t /*tag*/,
-				      const struct gcov_summary *)
-    ATTRIBUTE_HIDDEN;
-static void gcov_rewrite (void);
-GCOV_LINKAGE void gcov_seek (gcov_position_t /*position*/) ATTRIBUTE_HIDDEN;
-#else
-/* Available outside libgcov */
 GCOV_LINKAGE const char *gcov_read_string (void);
 GCOV_LINKAGE void gcov_sync (gcov_position_t /*base*/,
 			     gcov_unsigned_t /*length */);
-#endif
 
 #if !IN_GCOV
 /* Available outside gcov */
@@ -651,37 +453,6 @@ GCOV_LINKAGE void compute_working_sets (const stru
 GCOV_LINKAGE time_t gcov_time (void);
 #endif
 
-/* Save the current position in the gcov file.  */
+#endif /* !inhibit_libc  */
 
-static inline gcov_position_t
-gcov_position (void)
-{
-  gcc_assert (gcov_var.mode > 0);
-  return gcov_var.start + gcov_var.offset;
-}
-
-/* Return nonzero if the error flag is set.  */
-
-static inline int
-gcov_is_error (void)
-{
-  return gcov_var.file ? gcov_var.error : 1;
-}
-
-#if IN_LIBGCOV
-/* Move to beginning of file and initialize for writing.  */
-
-static inline void
-gcov_rewrite (void)
-{
-  gcc_assert (gcov_var.mode > 0);
-  gcov_var.mode = -1;
-  gcov_var.start = 0;
-  gcov_var.offset = 0;
-  fseek (gcov_var.file, 0L, SEEK_SET);
-}
-#endif
-
-#endif /* IN_LIBGCOV >= 0 */
-
 #endif /* GCC_GCOV_IO_H */
Index: libgcc/libgcov-driver.c
===================================================================
--- libgcc/libgcov-driver.c	(revision 205895)
+++ libgcc/libgcov-driver.c	(working copy)
@@ -23,23 +23,9 @@ a copy of the GCC Runtime Library Exception along
 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 <http://www.gnu.org/licenses/>.  */
 
-#include "tconfig.h"
-#include "tsystem.h"
-#include "coretypes.h"
-#include "tm.h"
-#include "libgcc_tm.h"
+#include "libgcov.h"
 
 #if defined(inhibit_libc)
-#define IN_LIBGCOV (-1)
-#else
-#define IN_LIBGCOV 1
-#if defined(L_gcov)
-#define GCOV_LINKAGE /* nothing */
-#endif
-#endif
-#include "gcov-io.h"
-
-#if defined(inhibit_libc)
 /* If libc and its header files are not available, provide dummy functions.  */
 
 #if defined(L_gcov)
@@ -156,7 +142,7 @@ buffer_fn_data (const char *filename, const struct
       n_ctrs++;
 
   len = sizeof (*fn_buffer) + sizeof (fn_buffer->info.ctrs[0]) * n_ctrs;
-  fn_buffer = (struct gcov_fn_buffer *)malloc (len);
+  fn_buffer = (struct gcov_fn_buffer *) xmalloc (len);
 
   if (!fn_buffer)
     goto fail;
@@ -183,7 +169,7 @@ buffer_fn_data (const char *filename, const struct
 
       length = GCOV_TAG_COUNTER_NUM (gcov_read_unsigned ());
       len = length * sizeof (gcov_type);
-      values = (gcov_type *)malloc (len);
+      values = (gcov_type *) xmalloc (len);
       if (!values)
         goto fail;
 
@@ -450,7 +436,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);
@@ -718,7 +704,7 @@ gcov_exit_merge_summary (const struct gcov_info *g
              }
 #endif
     }
-
+  
   prg->checksum = crc32;
 
   return 0;
Index: libgcc/libgcov-driver-system.c
===================================================================
--- libgcc/libgcov-driver-system.c	(revision 205895)
+++ libgcc/libgcov-driver-system.c	(working copy)
@@ -124,7 +124,7 @@ allocate_filename_struct (struct gcov_filename_aux
       prefix_length = 1;
     }
   /* Allocate and initialize the filename scratch space plus one.  */
-  gi_filename = (char *) malloc (prefix_length + gcov_max_filename + 2);
+  gi_filename = (char *) xmalloc (prefix_length + gcov_max_filename + 2);
   if (prefix_length)
     memcpy (gi_filename, gcov_prefix, prefix_length);
   gi_filename_up = gi_filename + prefix_length;
Index: libgcc/libgcov.h
===================================================================
--- libgcc/libgcov.h	(revision 0)
+++ libgcc/libgcov.h	(revision 0)
@@ -0,0 +1,225 @@
+/* Header file for libgcov-*.c.
+   Contributed by Rong Xu <xur@google.com>.
+   Copyright (C) 2013 Free Software Foundation, Inc.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it under
+   the terms of the GNU General Public License as published by the Free
+   Software Foundation; either version 3, or (at your option) any later
+   version.
+
+   GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+   for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GCC; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_LIBGCOV_H
+#define GCC_LIBGCOV_H
+
+/* work around the poisoned malloc/calloc in system.h.  */
+#ifndef xmalloc
+#define xmalloc malloc
+#endif
+#ifndef xcalloc
+#define xcalloc calloc
+#endif
+
+#include "tconfig.h"
+#include "tsystem.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "libgcc_tm.h"
+
+#if BITS_PER_UNIT == 8
+typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI)));
+typedef unsigned gcov_position_t __attribute__ ((mode (SI)));
+#if LONG_LONG_TYPE_SIZE > 32
+typedef signed gcov_type __attribute__ ((mode (DI)));
+typedef unsigned gcov_type_unsigned __attribute__ ((mode (DI)));
+#else
+typedef signed gcov_type __attribute__ ((mode (SI)));
+typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
+#endif
+#else
+#if BITS_PER_UNIT == 16
+typedef unsigned gcov_unsigned_t __attribute__ ((mode (HI)));
+typedef unsigned gcov_position_t __attribute__ ((mode (HI)));
+#if LONG_LONG_TYPE_SIZE > 32
+typedef signed gcov_type __attribute__ ((mode (SI)));
+typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
+#else
+typedef signed gcov_type __attribute__ ((mode (HI)));
+typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
+#endif
+#else
+typedef unsigned gcov_unsigned_t __attribute__ ((mode (QI)));
+typedef unsigned gcov_position_t __attribute__ ((mode (QI)));
+#if LONG_LONG_TYPE_SIZE > 32
+typedef signed gcov_type __attribute__ ((mode (HI)));
+typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
+#else
+typedef signed gcov_type __attribute__ ((mode (QI)));
+typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI)));
+#endif
+#endif
+#endif
+
+#if defined (TARGET_POSIX_IO)
+#define GCOV_LOCKED 1
+#else
+#define GCOV_LOCKED 0
+#endif
+
+#if defined(inhibit_libc)
+#define IN_LIBGCOV (-1)
+#else
+#define IN_LIBGCOV 1
+#if defined(L_gcov)
+#define GCOV_LINKAGE /* nothing */
+#endif
+#endif
+
+/* In libgcov we need these functions to be extern, so prefix them with
+   __gcov.  In libgcov they must also be hidden so that the instance in
+   the executable is not also used in a DSO.  */
+#define gcov_var __gcov_var
+#define gcov_open __gcov_open
+#define gcov_close __gcov_close
+#define gcov_write_tag_length __gcov_write_tag_length
+#define gcov_position __gcov_position
+#define gcov_seek __gcov_seek
+#define gcov_rewrite __gcov_rewrite
+#define gcov_is_error __gcov_is_error
+#define gcov_write_unsigned __gcov_write_unsigned
+#define gcov_write_counter __gcov_write_counter
+#define gcov_write_summary __gcov_write_summary
+#define gcov_read_unsigned __gcov_read_unsigned
+#define gcov_read_counter __gcov_read_counter
+#define gcov_read_summary __gcov_read_summary
+
+/* Poison these, so they don't accidentally slip in.  */
+#pragma GCC poison gcov_write_string gcov_write_tag gcov_write_length
+#pragma GCC poison gcov_time gcov_magic
+
+#ifdef HAVE_GAS_HIDDEN
+#define ATTRIBUTE_HIDDEN  __attribute__ ((__visibility__ ("hidden")))
+#else
+#define ATTRIBUTE_HIDDEN
+#endif
+
+#include "gcov-io.h"
+
+/* Structures embedded in coveraged program.  The structures generated
+   by write_profile must match these.  */
+
+/* Information about counters for a single function.  */
+struct gcov_ctr_info
+{
+  gcov_unsigned_t num;		/* number of counters.  */
+  gcov_type *values;		/* their values.  */
+};
+
+/* Information about a single function.  This uses the trailing array
+   idiom. The number of counters is determined from the merge pointer
+   array in gcov_info.  The key is used to detect which of a set of
+   comdat functions was selected -- it points to the gcov_info object
+   of the object file containing the selected comdat function.  */
+
+struct gcov_fn_info
+{
+  const struct gcov_info *key;		/* comdat key */
+  gcov_unsigned_t ident;		/* unique ident of function */
+  gcov_unsigned_t lineno_checksum;	/* function lineo_checksum */
+  gcov_unsigned_t cfg_checksum;		/* function cfg checksum */
+  struct gcov_ctr_info ctrs[0];		/* instrumented counters */
+};
+
+/* Type of function used to merge counters.  */
+typedef void (*gcov_merge_fn) (gcov_type *, gcov_unsigned_t);
+
+/* Information about a single object file.  */
+struct gcov_info
+{
+  gcov_unsigned_t version;	/* expected version number */
+  struct gcov_info *next;	/* link to next, used by libgcov */
+
+  gcov_unsigned_t stamp;	/* uniquifying time stamp */
+  const char *filename;		/* output file name */
+
+  gcov_merge_fn merge[GCOV_COUNTERS];  /* merge functions (null for
+					  unused) */
+  
+  unsigned n_functions;		/* number of functions */
+  const struct gcov_fn_info *const *functions; /* pointer to pointers
+					          to function information  */
+};
+
+/* Register a new object file module.  */
+extern void __gcov_init (struct gcov_info *) ATTRIBUTE_HIDDEN;
+
+/* Called before fork, to avoid double counting.  */
+extern void __gcov_flush (void) ATTRIBUTE_HIDDEN;
+
+/* Function to reset all counters to 0.  */
+extern void __gcov_reset (void);
+
+/* Function to enable early write of profile information so far.  */
+extern void __gcov_dump (void);
+
+/* The merge function that just sums the counters.  */
+extern void __gcov_merge_add (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
+
+/* The merge function to select the minimum valid counter value.  */
+extern void __gcov_merge_time_profile (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
+
+/* The merge function to choose the most common value.  */
+extern void __gcov_merge_single (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
+
+/* The merge function to choose the most common difference between
+   consecutive values.  */
+extern void __gcov_merge_delta (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
+
+/* The merge function that just ors the counters together.  */
+extern void __gcov_merge_ior (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
+
+/* The profiler functions.  */
+extern void __gcov_interval_profiler (gcov_type *, gcov_type, int, unsigned);
+extern void __gcov_pow2_profiler (gcov_type *, gcov_type);
+extern void __gcov_one_value_profiler (gcov_type *, gcov_type);
+extern void __gcov_indirect_call_profiler (gcov_type*, gcov_type,
+                                           void*, void*);
+extern void __gcov_indirect_call_profiler_v2 (gcov_type, void *);
+extern void __gcov_time_profiler (gcov_type *);
+extern void __gcov_average_profiler (gcov_type *, gcov_type);
+extern void __gcov_ior_profiler (gcov_type *, gcov_type);
+
+#ifndef inhibit_libc
+/* The wrappers around some library functions..  */
+extern pid_t __gcov_fork (void) ATTRIBUTE_HIDDEN;
+extern int __gcov_execl (const char *, char *, ...) ATTRIBUTE_HIDDEN;
+extern int __gcov_execlp (const char *, char *, ...) ATTRIBUTE_HIDDEN;
+extern int __gcov_execle (const char *, char *, ...) ATTRIBUTE_HIDDEN;
+extern int __gcov_execv (const char *, char *const []) ATTRIBUTE_HIDDEN;
+extern int __gcov_execvp (const char *, char *const []) ATTRIBUTE_HIDDEN;
+extern int __gcov_execve (const char *, char  *const [], char *const [])
+  ATTRIBUTE_HIDDEN;
+
+/* Functions that only available in libgcov.  */
+GCOV_LINKAGE int gcov_open (const char */*name*/) ATTRIBUTE_HIDDEN;
+GCOV_LINKAGE void gcov_write_counter (gcov_type) ATTRIBUTE_HIDDEN;
+GCOV_LINKAGE void gcov_write_tag_length (gcov_unsigned_t, gcov_unsigned_t)
+    ATTRIBUTE_HIDDEN;
+GCOV_LINKAGE void gcov_write_summary (gcov_unsigned_t /*tag*/,
+                                      const struct gcov_summary *)
+    ATTRIBUTE_HIDDEN;
+GCOV_LINKAGE void gcov_seek (gcov_position_t /*position*/) ATTRIBUTE_HIDDEN;
+GCOV_LINKAGE inline void gcov_rewrite (void);
+
+#endif /* !inhibit_libc */
+
+#endif /* GCC_LIBGCOV_H */
Index: libgcc/libgcov-interface.c
===================================================================
--- libgcc/libgcov-interface.c	(revision 205895)
+++ libgcc/libgcov-interface.c	(working copy)
@@ -23,22 +23,11 @@ a copy of the GCC Runtime Library Exception along
 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 <http://www.gnu.org/licenses/>.  */
 
-#include "tconfig.h"
-#include "tsystem.h"
-#include "coretypes.h"
-#include "tm.h"
-#include "libgcc_tm.h"
+#include "libgcov.h"
 #include "gthr.h"
 
 #if defined(inhibit_libc)
-#define IN_LIBGCOV (-1)
-#else
-#define IN_LIBGCOV 1
-#endif
-#include "gcov-io.h"
 
-#if defined(inhibit_libc)
-
 #ifdef L_gcov_flush
 void __gcov_flush (void) {}
 #endif
Index: libgcc/libgcov-merge.c
===================================================================
--- libgcc/libgcov-merge.c	(revision 205895)
+++ libgcc/libgcov-merge.c	(working copy)
@@ -23,21 +23,9 @@ a copy of the GCC Runtime Library Exception along
 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 <http://www.gnu.org/licenses/>.  */
 
-#include "tconfig.h"
-#include "tsystem.h"
-#include "coretypes.h"
-#include "tm.h"
-#include "libgcc_tm.h"
+#include "libgcov.h"
 
 #if defined(inhibit_libc)
-#define IN_LIBGCOV (-1)
-#else
-#define IN_LIBGCOV 1
-#endif
-
-#include "gcov-io.h"
-
-#if defined(inhibit_libc)
 /* If libc and its header files are not available, provide dummy functions.  */
 
 #ifdef L_gcov_merge_add
Index: libgcc/libgcov-profiler.c
===================================================================
--- libgcc/libgcov-profiler.c	(revision 205895)
+++ libgcc/libgcov-profiler.c	(working copy)
@@ -23,15 +23,8 @@ a copy of the GCC Runtime Library Exception along
 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 <http://www.gnu.org/licenses/>.  */
 
-#include "tconfig.h"
-#include "tsystem.h"
-#include "coretypes.h"
-#include "tm.h"
-#include "libgcc_tm.h"
-
+#include "libgcov.h"
 #if !defined(inhibit_libc)
-#define IN_LIBGCOV 1
-#include "gcov-io.h"
 
 #ifdef L_gcov_interval_profiler
 /* If VALUE is in interval <START, START + STEPS - 1>, then increases the
Index: libgcc/Makefile.in
===================================================================
--- libgcc/Makefile.in	(revision 205895)
+++ libgcc/Makefile.in	(working copy)
@@ -868,14 +868,14 @@ libgcov-driver-objects = $(patsubst %,%$(objext),$
 libgcov-objects = $(libgcov-merge-objects) $(libgcov-profiler-objects) \
                  $(libgcov-interface-objects) $(libgcov-driver-objects)
 
-$(libgcov-merge-objects): %$(objext): $(srcdir)/libgcov-merge.c
+$(libgcov-merge-objects): %$(objext): $(srcdir)/libgcov-merge.c $(srcdir)/libgcov.h
 	$(gcc_compile) -DL$* -c $(srcdir)/libgcov-merge.c
-$(libgcov-profiler-objects): %$(objext): $(srcdir)/libgcov-profiler.c
+$(libgcov-profiler-objects): %$(objext): $(srcdir)/libgcov-profiler.c $(srcdir)/libgcov.h
 	$(gcc_compile) -DL$* -c $(srcdir)/libgcov-profiler.c
-$(libgcov-interface-objects): %$(objext): $(srcdir)/libgcov-interface.c
+$(libgcov-interface-objects): %$(objext): $(srcdir)/libgcov-interface.c $(srcdir)/libgcov.h
 	$(gcc_compile) -DL$* -c $(srcdir)/libgcov-interface.c
 $(libgcov-driver-objects): %$(objext): $(srcdir)/libgcov-driver.c \
-  $(srcdir)/libgcov-driver-system.c
+  $(srcdir)/libgcov-driver-system.c $(srcdir)/libgcov.h
 	$(gcc_compile) -DL$* -c $(srcdir)/libgcov-driver.c
 
 

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-12-12  6:05                 ` Teresa Johnson
@ 2013-12-12 20:11                   ` Teresa Johnson
  2013-12-16 20:55                     ` Xinliang David Li
  0 siblings, 1 reply; 55+ messages in thread
From: Teresa Johnson @ 2013-12-12 20:11 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Rong Xu, Xinliang David Li, GCC Patches

On Wed, Dec 11, 2013 at 10:05 PM, Teresa Johnson <tejohnson@google.com> wrote:
> On Fri, Dec 6, 2013 at 6:23 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>>> Hi, all
>>>
>>> This is the new patch for gcov-tool (previously profile-tool).
>>>
>>> Honza: can you comment on the new merge interface? David posted some
>>> comments in an earlier email and we want to know what's your opinion.
>>>
>>> Test patch has been tested with boostrap, regresssion,
>>> profiledbootstrap and SPEC2006.
>>>
>>> Noticeable changes from the earlier version:
>>>
>>> 1. create a new file libgcov.h and move libgcov-*.h headers to libgcov.h
>>> So we can included multiple libgcov-*.c without adding new macros.
>>>
>>> 2. split libgcov.h specific code in gcvo-io.h to libcc/libgcov.h
>>> Avoid multiple-page of code under IN_LIBGCOV macro -- this
>>> improves the readability.
>>>
>>> 3. make gcov_var static, and move the definition from gcov-io.h to
>>> gcov-io.c. Also
>>>    move some static functions accessing gcov_var to gcvo-io.c
>>> Current code rely on GCOV_LINKAGE tricks to avoid multi-definition. I don't see
>>> a reason that gcov_var needs to exposed as a global.
>>>
>>> 4. expose gcov_write_strings() and gcov_sync() to gcov_tool usage
>>>
>>> 5. rename profile-tool to gcov-tool per Honza's suggestion.
>>>
>>> Thanks,
>>
>> Hi,
>> I did not read in deatil the gcov-tool source itself, but lets first make the interface changes
>> needed.
>>
>>> 2013-11-18  Rong Xu  <xur@google.com>
>>>
>>>       * gcc/gcov-io.c (gcov_var): Moved from gcov-io.h and make it static.
>>>       (gcov_position): Move from gcov-io.h
>>>       (gcov_is_error): Ditto.
>>>       (gcov_rewrite): Ditto.
>>>       * gcc/gcov-io.h: Re-factoring. Move gcov_var to gcov-io.h and
>>>         move the libgcov only part of libgcc/libgcov.h.
>>>       * libgcc/libgcov.h: New common header files for libgcov-*.h
>>>       * libgcc/Makefile.in: Add dependence to libgcov.h
>>>       * libgcc/libgcov-profiler.c: Use libgcov.h
>>>       * libgcc/libgcov-driver.c: Ditto.
>>>       * libgcc/libgcov-interface.c: Ditto.
>>>       * libgcc/libgcov-driver-system.c (allocate_filename_struct): use
>>>       xmalloc instread of malloc.
>>>       * libgcc/libgcov-merge.c (void __gcov_merge_delta): Add more
>>>       parameters to merge function.
>>>       (__gcov_merge_add): Ditto.
>>>       (__gcov_merge_ior): Ditto.
>>>       (__gcov_merge_time_profile): Ditto.
>>>       (__gcov_merge_single): Ditto.
>>>       (__gcov_merge_delta): Ditto.
>>>       * libgcc/libgcov-tool.c (void gcov_tool_set_verbose): New for
>>>       gcov-tool support.
>>>       (set_fn_ctrs): Ditto.
>>>       (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_merge): Ditto.
>>>       (find_match_gcov_inf Ditto.o):
>>>       (gcov_profile_merge): Ditto.
>>>       (__gcov_scale_add): Ditto.
>>>       (__gcov_scale_ior): Ditto.
>>>       (__gcov_scale_delta): Ditto.
>>>       (__gcov_scale_single): Ditto.
>>>       (gcov_profile_scale): Ditto.
>>>       (gcov_profile_normalize): Ditto.
>>>       (__gcov_scale2_add): Ditto.
>>>       (__gcov_scale2_ior): Ditto.
>>>       (__gcov_scale2_delta): Ditto.
>>>       (__gcov_scale2_single): Ditto.
>>>       (gcov_profile_scale2): Ditto.
>>>       * gcc/gcov-tool.c (unlink_file): Gcov-tool driver support.
>>>       (unlink_dir): Ditto.
>>>       (profile_merge): Ditto.
>>>       (print_merge_usage_message): Ditto.
>>>       (merge_usage): Ditto.
>>>       (do_merge): Ditto.
>>>       (profile_rewrite2): Ditto.
>>>       (profile_rewrite): Ditto.
>>>       (print_rewrite_usage_message): Ditto.
>>>       (rewrite_usage): Ditto.
>>>       (do_rewrite): Ditto.
>>>       (print_usage): Ditto.
>>>       (print_version): Ditto.
>>>       (process_args): Ditto.
>>>       (main): Ditto.
>>>       * gcc/Makefile.in: Build and install gcov-tool.
>>
>>> Index: gcc/gcov-io.c
>>> ===================================================================
>>> --- gcc/gcov-io.c     (revision 204895)
>>> +++ gcc/gcov-io.c     (working copy)
>>> @@ -36,6 +36,37 @@ static const gcov_unsigned_t *gcov_read_words (uns
>>>  static void gcov_allocate (unsigned);
>>>  #endif
>>>
>>> +/* Moved for gcov-io.h and make it static.  */
>>> +static struct gcov_var gcov_var;
>>
>> This is more an changelog message than a comment in source file.
>> Just describe what gcov_var is.
>
> I changed this so gcov_var is no longer static, but global as before.
>
>>
>> Do you know how the size of libgcov changed with your patch?
>> Quick check of current mainline on compiling empty main gives:
>>
>> jh@gcc10:~/trunk/build/gcc$ cat t.c
>> main()
>> {
>> }
>> jh@gcc10:~/trunk/build/gcc$ ./xgcc -B ./ -O2 -fprofile-generate -o a.out-new --static t.c
>> jh@gcc10:~/trunk/build/gcc$ gcc -O2 -fprofile-generate -o a.out-old --static t.c
>> jh@gcc10:~/trunk/build/gcc$ size a.out-old
>>    text    data     bss     dec     hex filename
>>  608141    3560   16728  628429   996cd a.out-old
>> jh@gcc10:~/trunk/build/gcc$ size a.out-new
>>    text    data     bss     dec     hex filename
>>  612621    3688   22880  639189   9c0d5 a.out-new
>>
>> Without profiling I get:
>> jh@gcc10:~/trunk/build/gcc$ size a.out-new-no
>> jh@gcc10:~/trunk/build/gcc$ size a.out-old-no
>>    text    data     bss     dec     hex filename
>>  599719    3448   12568  615735   96537 a.out-old-no
>>    text    data     bss     dec     hex filename
>>  600247    3448   12568  616263   96747 a.out-new-no
>>
>> Quite big for empty program, but mostly glibc fault, I suppose
>> (that won't be an issue for embedded platforms). But anyway
>> we increased text size overhead from 8k to 12k, BSS size
>> overhead from 4k to 10k and data by another 1k.
>>
>>    text    data     bss     dec     hex filename
>>     126       0       0     126      7e _gcov_merge_add.o (ex libgcov.a)
>>     251       0       0     251      fb _gcov_merge_single.o (ex libgcov.a)
>>     242       0       0     242      f2 _gcov_merge_delta.o (ex libgcov.a)
>>     126       0       0     126      7e _gcov_merge_ior.o (ex libgcov.a)
>>     156       0       0     156      9c _gcov_merge_time_profile.o (ex libgcov.a)
>>      89       0       0      89      59 _gcov_interval_profiler.o (ex libgcov.a)
>>      69       0       0      69      45 _gcov_pow2_profiler.o (ex libgcov.a)
>>     115       0       0     115      73 _gcov_one_value_profiler.o (ex libgcov.a)
>>     122       0       0     122      7a _gcov_indirect_call_profiler.o (ex libgcov.a)
>>      57       0       0      57      39 _gcov_average_profiler.o (ex libgcov.a)
>>      52       0       0      52      34 _gcov_ior_profiler.o (ex libgcov.a)
>>     178       0      16     194      c2 _gcov_indirect_call_profiler_v2.o (ex libgcov.a)
>>      77       0       8      85      55 _gcov_time_profiler.o (ex libgcov.a)
>>     126       0      40     166      a6 _gcov_flush.o (ex libgcov.a)
>>     101       0       0     101      65 _gcov_fork.o (ex libgcov.a)
>>     471       0       0     471     1d7 _gcov_execl.o (ex libgcov.a)
>>     471       0       0     471     1d7 _gcov_execlp.o (ex libgcov.a)
>>     524       0       0     524     20c _gcov_execle.o (ex libgcov.a)
>>      98       0       0      98      62 _gcov_execv.o (ex libgcov.a)
>>      98       0       0      98      62 _gcov_execvp.o (ex libgcov.a)
>>     108       0       0     108      6c _gcov_execve.o (ex libgcov.a)
>>      66       0       0      66      42 _gcov_reset.o (ex libgcov.a)
>>      66       0       0      66      42 _gcov_dump.o (ex libgcov.a)
>>    9505       0    6144   15649    3d21 _gcov.o (ex libgcov.a)
>>
>> I think we definitely need to move those 6k of bss space out.  I think those are new
>> static vars you introduced that I think are unsafe anyway because multiple streaming
>> may run at once in threaded program where locking mechanizm fails.
>> (it will probably do other bad things, but definitely we do not want to
>> conflict on things like filename to write into).
>>
>> Compared to my system gcov:
>>    text    data     bss     dec     hex filename
>>    9765       0      64    9829    2665 _gcov.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>     125       0       0     125      7d _gcov_merge_add.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>     251       0       0     251      fb _gcov_merge_single.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>     242       0       0     242      f2 _gcov_merge_delta.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>     101       0       0     101      65 _gcov_fork.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>     471       0       0     471     1d7 _gcov_execl.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>     471       0       0     471     1d7 _gcov_execlp.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>     524       0       0     524     20c _gcov_execle.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>      98       0       0      98      62 _gcov_execv.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>      98       0       0      98      62 _gcov_execvp.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>     108       0       0     108      6c _gcov_execve.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>      72       0       0      72      48 _gcov_reset.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>      72       0       0      72      48 _gcov_dump.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>      89       0       0      89      59 _gcov_interval_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>      69       0       0      69      45 _gcov_pow2_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>     115       0       0     115      73 _gcov_one_value_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>     122       0       0     122      7a _gcov_indirect_call_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>      57       0       0      57      39 _gcov_average_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>      52       0       0      52      34 _gcov_ior_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>     125       0       0     125      7d _gcov_merge_ior.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>
>>> Index: gcc/gcov-io.h
>>> ===================================================================
>>> --- gcc/gcov-io.h     (revision 204895)
>>> +++ gcc/gcov-io.h     (working copy)
>>> @@ -164,51 +164,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
>>>  #ifndef GCC_GCOV_IO_H
>>>  #define GCC_GCOV_IO_H
>>>
>>> -#if IN_LIBGCOV
>>> -/* About the target */
>>> -
>>> -#if BITS_PER_UNIT == 8
>>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI)));
>>> -typedef unsigned gcov_position_t __attribute__ ((mode (SI)));
>>> -#if LONG_LONG_TYPE_SIZE > 32
>>> -typedef signed gcov_type __attribute__ ((mode (DI)));
>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (DI)));
>>> -#else
>>> -typedef signed gcov_type __attribute__ ((mode (SI)));
>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
>>> -#endif
>>> -#else
>>> -#if BITS_PER_UNIT == 16
>>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (HI)));
>>> -typedef unsigned gcov_position_t __attribute__ ((mode (HI)));
>>> -#if LONG_LONG_TYPE_SIZE > 32
>>> -typedef signed gcov_type __attribute__ ((mode (SI)));
>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
>>> -#else
>>> -typedef signed gcov_type __attribute__ ((mode (HI)));
>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
>>> -#endif
>>> -#else
>>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (QI)));
>>> -typedef unsigned gcov_position_t __attribute__ ((mode (QI)));
>>> -#if LONG_LONG_TYPE_SIZE > 32
>>> -typedef signed gcov_type __attribute__ ((mode (HI)));
>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
>>> -#else
>>> -typedef signed gcov_type __attribute__ ((mode (QI)));
>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI)));
>>> -#endif
>>> -#endif
>>> -#endif
>>
>> So this part basically moves libgcov specific bits into libgcov.h? That OK fine by
>> itself.
>>> Index: libgcc/libgcov-profiler.c
>>> ===================================================================
>>> --- libgcc/libgcov-profiler.c (revision 204895)
>>> +++ libgcc/libgcov-profiler.c (working copy)
>>> @@ -23,15 +23,8 @@ a copy of the GCC Runtime Library Exception along
>>>  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>>>  <http://www.gnu.org/licenses/>.  */
>>>
>>> -#include "tconfig.h"
>>> -#include "tsystem.h"
>>> -#include "coretypes.h"
>>> -#include "tm.h"
>>> -#include "libgcc_tm.h"
>>> -
>>> +#include "libgcov.h"
>>>  #if !defined(inhibit_libc)
>>> -#define IN_LIBGCOV 1
>>> -#include "gcov-io.h"
>>
>> I did not pay that much attention into the current include file changes, but wasn't
>> idea to avoid #include file to include random other #includes?
>> So perhaps the first block of includes should stay, followed by libgcov.h and gcov-io.h
>> last?
>
> I'm not sure I understand the issue here? The patch basically moves
> the same includes into libgcov.h, and includes that instead. I see
> many other header files in gcc that include other headers.
>
>>> @@ -325,6 +311,9 @@ static struct gcov_summary all_prg;
>>>  #endif
>>>  /* crc32 for this program.  */
>>>  static gcov_unsigned_t crc32;
>>> +/* Use this summary checksum rather the computed one if the value is
>>> + *    non-zero.  */
>>> +static gcov_unsigned_t saved_summary_checksum;
>>
>> Why do you need to save the checksum? Won't it reset summary back with multiple streaming?
>
> This has been removed.
>
>>
>> I would really like to avoid introducing those static vars that are used exclusively
>> by gcov_exit.  What about putting them into an gcov_context structure that
>> is passed around the functions that was broken out?
>>> Index: libgcc/libgcov-merge.c
>>> ===================================================================
>>> --- libgcc/libgcov-merge.c    (revision 204895)
>>> +++ libgcc/libgcov-merge.c    (working copy)
>>> @@ -23,107 +23,150 @@ a copy of the GCC Runtime Library Exception along
>>>  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>>>  <http://www.gnu.org/licenses/>.  */
>>>
>>> -#include "tconfig.h"
>>> -#include "tsystem.h"
>>> -#include "coretypes.h"
>>> -#include "tm.h"
>>> -#include "libgcc_tm.h"
>>> +#include "libgcov.h"
>>>
>>> -#if defined(inhibit_libc)
>>> -#define IN_LIBGCOV (-1)
>>> -#else
>>> -#define IN_LIBGCOV 1
>>> +#include "gcov-io-libgcov.h"
>>>  #endif
>>>
>>> -#include "gcov-io.h"
>>> -
>>>  #if defined(inhibit_libc)
>>>  /* If libc and its header files are not available, provide dummy functions.  */
>>>
>>>  #ifdef L_gcov_merge_add
>>>  void __gcov_merge_add (gcov_type *counters  __attribute__ ((unused)),
>>> -                       unsigned n_counters __attribute__ ((unused))) {}
>>> +                       unsigned n_counters __attribute__ ((unused)),
>>> +                       unsigned gcov_type *src __attribute__ ((unused)),
>>> +                       unsigned w __attribute__ ((unused))) {}
>>>  #endif
>>>
>>>  #ifdef L_gcov_merge_single
>>>  void __gcov_merge_single (gcov_type *counters  __attribute__ ((unused)),
>>> -                          unsigned n_counters __attribute__ ((unused))) {}
>>> +                          unsigned n_counters __attribute__ ((unused)),
>>> +                          unsigned gcov_type *src __attribute__ ((unused)),
>>> +                          unsigned w __attribute__ ((unused))) {}
>>>  #endif
>>>
>>>  #ifdef L_gcov_merge_delta
>>>  void __gcov_merge_delta (gcov_type *counters  __attribute__ ((unused)),
>>> -                         unsigned n_counters __attribute__ ((unused))) {}
>>> +                         unsigned n_counters __attribute__ ((unused)),
>>> +                         unsigned gcov_type *src __attribute__ ((unused)),
>>> +                         unsigned w __attribute__ ((unused))) {}
>>>  #endif
>>>
>>>  #else
>>>
>>>  #ifdef L_gcov_merge_add
>>>  /* The profile merging function that just adds the counters.  It is given
>>> -   an array COUNTERS of N_COUNTERS old counters and it reads the same number
>>> -   of counters from the gcov file.  */
>>> +   an array COUNTERS of N_COUNTERS old counters.
>>> +   When SRC==NULL, it reads the same number of counters from the gcov file.
>>> +   Otherwise, it reads from SRC array. These read values will be multiplied
>>> +   by weight W before adding to the old counters.  */
>>>  void
>>> -__gcov_merge_add (gcov_type *counters, unsigned n_counters)
>>> +__gcov_merge_add (gcov_type *counters, unsigned n_counters,
>>> +                  gcov_type *src, unsigned w)
>>>  {
>>> +  int in_mem = (src != 0);
>>> +
>>>    for (; n_counters; counters++, n_counters--)
>>> -    *counters += gcov_read_counter ();
>>> +    {
>>> +      gcov_type value;
>>> +
>>> +      if (in_mem)
>>> +        value = *(src++);
>>> +      else
>>> +        value = gcov_read_counter ();
>>> +
>>> +      *counters += w * value;
>>> +    }
>>>  }
>>>  #endif /* L_gcov_merge_add */
>>>
>>>  #ifdef L_gcov_merge_ior
>>>  /* The profile merging function that just adds the counters.  It is given
>>> -   an array COUNTERS of N_COUNTERS old counters and it reads the same number
>>> -   of counters from the gcov file.  */
>>> +   an array COUNTERS of N_COUNTERS old counters.
>>> +   When SRC==NULL, it reads the same number of counters from the gcov file.
>>> +   Otherwise, it reads from SRC array.  */
>>>  void
>>> -__gcov_merge_ior (gcov_type *counters, unsigned n_counters)
>>> +__gcov_merge_ior (gcov_type *counters, unsigned n_counters,
>>> +                  gcov_type *src, unsigned w __attribute__ ((unused)))
>>
>> So the new in-memory variants are introduced for merging tool, while libgcc use gcov_read_counter
>> interface?
>> Perhaps we can actually just duplicate the functions to avoid runtime to do all the scalling
>> and in_mem tests it won't need?
>>
>> I would suggest going with libgcov.h changes and clenaups first, with interface changes next
>> and the gcov-tool is probably quite obvious at the end?
>> Do you think you can split the patch this way?
>>
>> Thanks and sorry for taking long to review. I should have more time again now.
>> Honza
>
> The libgcov.h related changes are in the attached patch. I think it
> addresses your concerns. Bootstrapped and tested on
> x86-64-unknown-linux-gnu. A profiledbootstrap is in progress.
>
> Ok for trunk if profiledbootstrap passes?

Both a profiledbootstrap and LTO profiledbootstrap pass.

Teresa

>
> Thanks,
> Teresa
>
>
> --
> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413



-- 
Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-12-12 20:11                   ` Teresa Johnson
@ 2013-12-16 20:55                     ` Xinliang David Li
  2013-12-16 21:17                       ` Teresa Johnson
  0 siblings, 1 reply; 55+ messages in thread
From: Xinliang David Li @ 2013-12-16 20:55 UTC (permalink / raw)
  To: Teresa Johnson; +Cc: Jan Hubicka, Rong Xu, GCC Patches

gcov_rewrite function is only needed (and defined) with IN_LIBGCOV.
Should it be moved from common file gcov-io.c to libgcov.c?


David

On Thu, Dec 12, 2013 at 12:11 PM, Teresa Johnson <tejohnson@google.com> wrote:
> On Wed, Dec 11, 2013 at 10:05 PM, Teresa Johnson <tejohnson@google.com> wrote:
>> On Fri, Dec 6, 2013 at 6:23 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>>>> Hi, all
>>>>
>>>> This is the new patch for gcov-tool (previously profile-tool).
>>>>
>>>> Honza: can you comment on the new merge interface? David posted some
>>>> comments in an earlier email and we want to know what's your opinion.
>>>>
>>>> Test patch has been tested with boostrap, regresssion,
>>>> profiledbootstrap and SPEC2006.
>>>>
>>>> Noticeable changes from the earlier version:
>>>>
>>>> 1. create a new file libgcov.h and move libgcov-*.h headers to libgcov.h
>>>> So we can included multiple libgcov-*.c without adding new macros.
>>>>
>>>> 2. split libgcov.h specific code in gcvo-io.h to libcc/libgcov.h
>>>> Avoid multiple-page of code under IN_LIBGCOV macro -- this
>>>> improves the readability.
>>>>
>>>> 3. make gcov_var static, and move the definition from gcov-io.h to
>>>> gcov-io.c. Also
>>>>    move some static functions accessing gcov_var to gcvo-io.c
>>>> Current code rely on GCOV_LINKAGE tricks to avoid multi-definition. I don't see
>>>> a reason that gcov_var needs to exposed as a global.
>>>>
>>>> 4. expose gcov_write_strings() and gcov_sync() to gcov_tool usage
>>>>
>>>> 5. rename profile-tool to gcov-tool per Honza's suggestion.
>>>>
>>>> Thanks,
>>>
>>> Hi,
>>> I did not read in deatil the gcov-tool source itself, but lets first make the interface changes
>>> needed.
>>>
>>>> 2013-11-18  Rong Xu  <xur@google.com>
>>>>
>>>>       * gcc/gcov-io.c (gcov_var): Moved from gcov-io.h and make it static.
>>>>       (gcov_position): Move from gcov-io.h
>>>>       (gcov_is_error): Ditto.
>>>>       (gcov_rewrite): Ditto.
>>>>       * gcc/gcov-io.h: Re-factoring. Move gcov_var to gcov-io.h and
>>>>         move the libgcov only part of libgcc/libgcov.h.
>>>>       * libgcc/libgcov.h: New common header files for libgcov-*.h
>>>>       * libgcc/Makefile.in: Add dependence to libgcov.h
>>>>       * libgcc/libgcov-profiler.c: Use libgcov.h
>>>>       * libgcc/libgcov-driver.c: Ditto.
>>>>       * libgcc/libgcov-interface.c: Ditto.
>>>>       * libgcc/libgcov-driver-system.c (allocate_filename_struct): use
>>>>       xmalloc instread of malloc.
>>>>       * libgcc/libgcov-merge.c (void __gcov_merge_delta): Add more
>>>>       parameters to merge function.
>>>>       (__gcov_merge_add): Ditto.
>>>>       (__gcov_merge_ior): Ditto.
>>>>       (__gcov_merge_time_profile): Ditto.
>>>>       (__gcov_merge_single): Ditto.
>>>>       (__gcov_merge_delta): Ditto.
>>>>       * libgcc/libgcov-tool.c (void gcov_tool_set_verbose): New for
>>>>       gcov-tool support.
>>>>       (set_fn_ctrs): Ditto.
>>>>       (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_merge): Ditto.
>>>>       (find_match_gcov_inf Ditto.o):
>>>>       (gcov_profile_merge): Ditto.
>>>>       (__gcov_scale_add): Ditto.
>>>>       (__gcov_scale_ior): Ditto.
>>>>       (__gcov_scale_delta): Ditto.
>>>>       (__gcov_scale_single): Ditto.
>>>>       (gcov_profile_scale): Ditto.
>>>>       (gcov_profile_normalize): Ditto.
>>>>       (__gcov_scale2_add): Ditto.
>>>>       (__gcov_scale2_ior): Ditto.
>>>>       (__gcov_scale2_delta): Ditto.
>>>>       (__gcov_scale2_single): Ditto.
>>>>       (gcov_profile_scale2): Ditto.
>>>>       * gcc/gcov-tool.c (unlink_file): Gcov-tool driver support.
>>>>       (unlink_dir): Ditto.
>>>>       (profile_merge): Ditto.
>>>>       (print_merge_usage_message): Ditto.
>>>>       (merge_usage): Ditto.
>>>>       (do_merge): Ditto.
>>>>       (profile_rewrite2): Ditto.
>>>>       (profile_rewrite): Ditto.
>>>>       (print_rewrite_usage_message): Ditto.
>>>>       (rewrite_usage): Ditto.
>>>>       (do_rewrite): Ditto.
>>>>       (print_usage): Ditto.
>>>>       (print_version): Ditto.
>>>>       (process_args): Ditto.
>>>>       (main): Ditto.
>>>>       * gcc/Makefile.in: Build and install gcov-tool.
>>>
>>>> Index: gcc/gcov-io.c
>>>> ===================================================================
>>>> --- gcc/gcov-io.c     (revision 204895)
>>>> +++ gcc/gcov-io.c     (working copy)
>>>> @@ -36,6 +36,37 @@ static const gcov_unsigned_t *gcov_read_words (uns
>>>>  static void gcov_allocate (unsigned);
>>>>  #endif
>>>>
>>>> +/* Moved for gcov-io.h and make it static.  */
>>>> +static struct gcov_var gcov_var;
>>>
>>> This is more an changelog message than a comment in source file.
>>> Just describe what gcov_var is.
>>
>> I changed this so gcov_var is no longer static, but global as before.
>>
>>>
>>> Do you know how the size of libgcov changed with your patch?
>>> Quick check of current mainline on compiling empty main gives:
>>>
>>> jh@gcc10:~/trunk/build/gcc$ cat t.c
>>> main()
>>> {
>>> }
>>> jh@gcc10:~/trunk/build/gcc$ ./xgcc -B ./ -O2 -fprofile-generate -o a.out-new --static t.c
>>> jh@gcc10:~/trunk/build/gcc$ gcc -O2 -fprofile-generate -o a.out-old --static t.c
>>> jh@gcc10:~/trunk/build/gcc$ size a.out-old
>>>    text    data     bss     dec     hex filename
>>>  608141    3560   16728  628429   996cd a.out-old
>>> jh@gcc10:~/trunk/build/gcc$ size a.out-new
>>>    text    data     bss     dec     hex filename
>>>  612621    3688   22880  639189   9c0d5 a.out-new
>>>
>>> Without profiling I get:
>>> jh@gcc10:~/trunk/build/gcc$ size a.out-new-no
>>> jh@gcc10:~/trunk/build/gcc$ size a.out-old-no
>>>    text    data     bss     dec     hex filename
>>>  599719    3448   12568  615735   96537 a.out-old-no
>>>    text    data     bss     dec     hex filename
>>>  600247    3448   12568  616263   96747 a.out-new-no
>>>
>>> Quite big for empty program, but mostly glibc fault, I suppose
>>> (that won't be an issue for embedded platforms). But anyway
>>> we increased text size overhead from 8k to 12k, BSS size
>>> overhead from 4k to 10k and data by another 1k.
>>>
>>>    text    data     bss     dec     hex filename
>>>     126       0       0     126      7e _gcov_merge_add.o (ex libgcov.a)
>>>     251       0       0     251      fb _gcov_merge_single.o (ex libgcov.a)
>>>     242       0       0     242      f2 _gcov_merge_delta.o (ex libgcov.a)
>>>     126       0       0     126      7e _gcov_merge_ior.o (ex libgcov.a)
>>>     156       0       0     156      9c _gcov_merge_time_profile.o (ex libgcov.a)
>>>      89       0       0      89      59 _gcov_interval_profiler.o (ex libgcov.a)
>>>      69       0       0      69      45 _gcov_pow2_profiler.o (ex libgcov.a)
>>>     115       0       0     115      73 _gcov_one_value_profiler.o (ex libgcov.a)
>>>     122       0       0     122      7a _gcov_indirect_call_profiler.o (ex libgcov.a)
>>>      57       0       0      57      39 _gcov_average_profiler.o (ex libgcov.a)
>>>      52       0       0      52      34 _gcov_ior_profiler.o (ex libgcov.a)
>>>     178       0      16     194      c2 _gcov_indirect_call_profiler_v2.o (ex libgcov.a)
>>>      77       0       8      85      55 _gcov_time_profiler.o (ex libgcov.a)
>>>     126       0      40     166      a6 _gcov_flush.o (ex libgcov.a)
>>>     101       0       0     101      65 _gcov_fork.o (ex libgcov.a)
>>>     471       0       0     471     1d7 _gcov_execl.o (ex libgcov.a)
>>>     471       0       0     471     1d7 _gcov_execlp.o (ex libgcov.a)
>>>     524       0       0     524     20c _gcov_execle.o (ex libgcov.a)
>>>      98       0       0      98      62 _gcov_execv.o (ex libgcov.a)
>>>      98       0       0      98      62 _gcov_execvp.o (ex libgcov.a)
>>>     108       0       0     108      6c _gcov_execve.o (ex libgcov.a)
>>>      66       0       0      66      42 _gcov_reset.o (ex libgcov.a)
>>>      66       0       0      66      42 _gcov_dump.o (ex libgcov.a)
>>>    9505       0    6144   15649    3d21 _gcov.o (ex libgcov.a)
>>>
>>> I think we definitely need to move those 6k of bss space out.  I think those are new
>>> static vars you introduced that I think are unsafe anyway because multiple streaming
>>> may run at once in threaded program where locking mechanizm fails.
>>> (it will probably do other bad things, but definitely we do not want to
>>> conflict on things like filename to write into).
>>>
>>> Compared to my system gcov:
>>>    text    data     bss     dec     hex filename
>>>    9765       0      64    9829    2665 _gcov.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>     125       0       0     125      7d _gcov_merge_add.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>     251       0       0     251      fb _gcov_merge_single.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>     242       0       0     242      f2 _gcov_merge_delta.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>     101       0       0     101      65 _gcov_fork.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>     471       0       0     471     1d7 _gcov_execl.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>     471       0       0     471     1d7 _gcov_execlp.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>     524       0       0     524     20c _gcov_execle.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>      98       0       0      98      62 _gcov_execv.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>      98       0       0      98      62 _gcov_execvp.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>     108       0       0     108      6c _gcov_execve.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>      72       0       0      72      48 _gcov_reset.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>      72       0       0      72      48 _gcov_dump.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>      89       0       0      89      59 _gcov_interval_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>      69       0       0      69      45 _gcov_pow2_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>     115       0       0     115      73 _gcov_one_value_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>     122       0       0     122      7a _gcov_indirect_call_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>      57       0       0      57      39 _gcov_average_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>      52       0       0      52      34 _gcov_ior_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>     125       0       0     125      7d _gcov_merge_ior.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>
>>>> Index: gcc/gcov-io.h
>>>> ===================================================================
>>>> --- gcc/gcov-io.h     (revision 204895)
>>>> +++ gcc/gcov-io.h     (working copy)
>>>> @@ -164,51 +164,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
>>>>  #ifndef GCC_GCOV_IO_H
>>>>  #define GCC_GCOV_IO_H
>>>>
>>>> -#if IN_LIBGCOV
>>>> -/* About the target */
>>>> -
>>>> -#if BITS_PER_UNIT == 8
>>>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI)));
>>>> -typedef unsigned gcov_position_t __attribute__ ((mode (SI)));
>>>> -#if LONG_LONG_TYPE_SIZE > 32
>>>> -typedef signed gcov_type __attribute__ ((mode (DI)));
>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (DI)));
>>>> -#else
>>>> -typedef signed gcov_type __attribute__ ((mode (SI)));
>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
>>>> -#endif
>>>> -#else
>>>> -#if BITS_PER_UNIT == 16
>>>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (HI)));
>>>> -typedef unsigned gcov_position_t __attribute__ ((mode (HI)));
>>>> -#if LONG_LONG_TYPE_SIZE > 32
>>>> -typedef signed gcov_type __attribute__ ((mode (SI)));
>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
>>>> -#else
>>>> -typedef signed gcov_type __attribute__ ((mode (HI)));
>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
>>>> -#endif
>>>> -#else
>>>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (QI)));
>>>> -typedef unsigned gcov_position_t __attribute__ ((mode (QI)));
>>>> -#if LONG_LONG_TYPE_SIZE > 32
>>>> -typedef signed gcov_type __attribute__ ((mode (HI)));
>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
>>>> -#else
>>>> -typedef signed gcov_type __attribute__ ((mode (QI)));
>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI)));
>>>> -#endif
>>>> -#endif
>>>> -#endif
>>>
>>> So this part basically moves libgcov specific bits into libgcov.h? That OK fine by
>>> itself.
>>>> Index: libgcc/libgcov-profiler.c
>>>> ===================================================================
>>>> --- libgcc/libgcov-profiler.c (revision 204895)
>>>> +++ libgcc/libgcov-profiler.c (working copy)
>>>> @@ -23,15 +23,8 @@ a copy of the GCC Runtime Library Exception along
>>>>  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>>>>  <http://www.gnu.org/licenses/>.  */
>>>>
>>>> -#include "tconfig.h"
>>>> -#include "tsystem.h"
>>>> -#include "coretypes.h"
>>>> -#include "tm.h"
>>>> -#include "libgcc_tm.h"
>>>> -
>>>> +#include "libgcov.h"
>>>>  #if !defined(inhibit_libc)
>>>> -#define IN_LIBGCOV 1
>>>> -#include "gcov-io.h"
>>>
>>> I did not pay that much attention into the current include file changes, but wasn't
>>> idea to avoid #include file to include random other #includes?
>>> So perhaps the first block of includes should stay, followed by libgcov.h and gcov-io.h
>>> last?
>>
>> I'm not sure I understand the issue here? The patch basically moves
>> the same includes into libgcov.h, and includes that instead. I see
>> many other header files in gcc that include other headers.
>>
>>>> @@ -325,6 +311,9 @@ static struct gcov_summary all_prg;
>>>>  #endif
>>>>  /* crc32 for this program.  */
>>>>  static gcov_unsigned_t crc32;
>>>> +/* Use this summary checksum rather the computed one if the value is
>>>> + *    non-zero.  */
>>>> +static gcov_unsigned_t saved_summary_checksum;
>>>
>>> Why do you need to save the checksum? Won't it reset summary back with multiple streaming?
>>
>> This has been removed.
>>
>>>
>>> I would really like to avoid introducing those static vars that are used exclusively
>>> by gcov_exit.  What about putting them into an gcov_context structure that
>>> is passed around the functions that was broken out?
>>>> Index: libgcc/libgcov-merge.c
>>>> ===================================================================
>>>> --- libgcc/libgcov-merge.c    (revision 204895)
>>>> +++ libgcc/libgcov-merge.c    (working copy)
>>>> @@ -23,107 +23,150 @@ a copy of the GCC Runtime Library Exception along
>>>>  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>>>>  <http://www.gnu.org/licenses/>.  */
>>>>
>>>> -#include "tconfig.h"
>>>> -#include "tsystem.h"
>>>> -#include "coretypes.h"
>>>> -#include "tm.h"
>>>> -#include "libgcc_tm.h"
>>>> +#include "libgcov.h"
>>>>
>>>> -#if defined(inhibit_libc)
>>>> -#define IN_LIBGCOV (-1)
>>>> -#else
>>>> -#define IN_LIBGCOV 1
>>>> +#include "gcov-io-libgcov.h"
>>>>  #endif
>>>>
>>>> -#include "gcov-io.h"
>>>> -
>>>>  #if defined(inhibit_libc)
>>>>  /* If libc and its header files are not available, provide dummy functions.  */
>>>>
>>>>  #ifdef L_gcov_merge_add
>>>>  void __gcov_merge_add (gcov_type *counters  __attribute__ ((unused)),
>>>> -                       unsigned n_counters __attribute__ ((unused))) {}
>>>> +                       unsigned n_counters __attribute__ ((unused)),
>>>> +                       unsigned gcov_type *src __attribute__ ((unused)),
>>>> +                       unsigned w __attribute__ ((unused))) {}
>>>>  #endif
>>>>
>>>>  #ifdef L_gcov_merge_single
>>>>  void __gcov_merge_single (gcov_type *counters  __attribute__ ((unused)),
>>>> -                          unsigned n_counters __attribute__ ((unused))) {}
>>>> +                          unsigned n_counters __attribute__ ((unused)),
>>>> +                          unsigned gcov_type *src __attribute__ ((unused)),
>>>> +                          unsigned w __attribute__ ((unused))) {}
>>>>  #endif
>>>>
>>>>  #ifdef L_gcov_merge_delta
>>>>  void __gcov_merge_delta (gcov_type *counters  __attribute__ ((unused)),
>>>> -                         unsigned n_counters __attribute__ ((unused))) {}
>>>> +                         unsigned n_counters __attribute__ ((unused)),
>>>> +                         unsigned gcov_type *src __attribute__ ((unused)),
>>>> +                         unsigned w __attribute__ ((unused))) {}
>>>>  #endif
>>>>
>>>>  #else
>>>>
>>>>  #ifdef L_gcov_merge_add
>>>>  /* The profile merging function that just adds the counters.  It is given
>>>> -   an array COUNTERS of N_COUNTERS old counters and it reads the same number
>>>> -   of counters from the gcov file.  */
>>>> +   an array COUNTERS of N_COUNTERS old counters.
>>>> +   When SRC==NULL, it reads the same number of counters from the gcov file.
>>>> +   Otherwise, it reads from SRC array. These read values will be multiplied
>>>> +   by weight W before adding to the old counters.  */
>>>>  void
>>>> -__gcov_merge_add (gcov_type *counters, unsigned n_counters)
>>>> +__gcov_merge_add (gcov_type *counters, unsigned n_counters,
>>>> +                  gcov_type *src, unsigned w)
>>>>  {
>>>> +  int in_mem = (src != 0);
>>>> +
>>>>    for (; n_counters; counters++, n_counters--)
>>>> -    *counters += gcov_read_counter ();
>>>> +    {
>>>> +      gcov_type value;
>>>> +
>>>> +      if (in_mem)
>>>> +        value = *(src++);
>>>> +      else
>>>> +        value = gcov_read_counter ();
>>>> +
>>>> +      *counters += w * value;
>>>> +    }
>>>>  }
>>>>  #endif /* L_gcov_merge_add */
>>>>
>>>>  #ifdef L_gcov_merge_ior
>>>>  /* The profile merging function that just adds the counters.  It is given
>>>> -   an array COUNTERS of N_COUNTERS old counters and it reads the same number
>>>> -   of counters from the gcov file.  */
>>>> +   an array COUNTERS of N_COUNTERS old counters.
>>>> +   When SRC==NULL, it reads the same number of counters from the gcov file.
>>>> +   Otherwise, it reads from SRC array.  */
>>>>  void
>>>> -__gcov_merge_ior (gcov_type *counters, unsigned n_counters)
>>>> +__gcov_merge_ior (gcov_type *counters, unsigned n_counters,
>>>> +                  gcov_type *src, unsigned w __attribute__ ((unused)))
>>>
>>> So the new in-memory variants are introduced for merging tool, while libgcc use gcov_read_counter
>>> interface?
>>> Perhaps we can actually just duplicate the functions to avoid runtime to do all the scalling
>>> and in_mem tests it won't need?
>>>
>>> I would suggest going with libgcov.h changes and clenaups first, with interface changes next
>>> and the gcov-tool is probably quite obvious at the end?
>>> Do you think you can split the patch this way?
>>>
>>> Thanks and sorry for taking long to review. I should have more time again now.
>>> Honza
>>
>> The libgcov.h related changes are in the attached patch. I think it
>> addresses your concerns. Bootstrapped and tested on
>> x86-64-unknown-linux-gnu. A profiledbootstrap is in progress.
>>
>> Ok for trunk if profiledbootstrap passes?
>
> Both a profiledbootstrap and LTO profiledbootstrap pass.
>
> Teresa
>
>>
>> Thanks,
>> Teresa
>>
>>
>> --
>> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413
>
>
>
> --
> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-12-16 20:55                     ` Xinliang David Li
@ 2013-12-16 21:17                       ` Teresa Johnson
  2013-12-16 21:51                         ` Xinliang David Li
  0 siblings, 1 reply; 55+ messages in thread
From: Teresa Johnson @ 2013-12-16 21:17 UTC (permalink / raw)
  To: Xinliang David Li; +Cc: Jan Hubicka, Rong Xu, GCC Patches

On Mon, Dec 16, 2013 at 12:55 PM, Xinliang David Li <davidxl@google.com> wrote:
> gcov_rewrite function is only needed (and defined) with IN_LIBGCOV.
> Should it be moved from common file gcov-io.c to libgcov.c?

Possibly. I just looked through gcov-io.c and there are several
additional functions that are only defined under "#ifdef IN_LIBGCOV"
and only used in libgcov*c (or each other):

gcov_write_counter
gcov_write_tag_length
gcov_write_summary
gcov_seek

Should they all, plus gcov_rewrite, be moved to libgcov-driver.c?

Teresa

>
>
> David
>
> On Thu, Dec 12, 2013 at 12:11 PM, Teresa Johnson <tejohnson@google.com> wrote:
>> On Wed, Dec 11, 2013 at 10:05 PM, Teresa Johnson <tejohnson@google.com> wrote:
>>> On Fri, Dec 6, 2013 at 6:23 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>>>>> Hi, all
>>>>>
>>>>> This is the new patch for gcov-tool (previously profile-tool).
>>>>>
>>>>> Honza: can you comment on the new merge interface? David posted some
>>>>> comments in an earlier email and we want to know what's your opinion.
>>>>>
>>>>> Test patch has been tested with boostrap, regresssion,
>>>>> profiledbootstrap and SPEC2006.
>>>>>
>>>>> Noticeable changes from the earlier version:
>>>>>
>>>>> 1. create a new file libgcov.h and move libgcov-*.h headers to libgcov.h
>>>>> So we can included multiple libgcov-*.c without adding new macros.
>>>>>
>>>>> 2. split libgcov.h specific code in gcvo-io.h to libcc/libgcov.h
>>>>> Avoid multiple-page of code under IN_LIBGCOV macro -- this
>>>>> improves the readability.
>>>>>
>>>>> 3. make gcov_var static, and move the definition from gcov-io.h to
>>>>> gcov-io.c. Also
>>>>>    move some static functions accessing gcov_var to gcvo-io.c
>>>>> Current code rely on GCOV_LINKAGE tricks to avoid multi-definition. I don't see
>>>>> a reason that gcov_var needs to exposed as a global.
>>>>>
>>>>> 4. expose gcov_write_strings() and gcov_sync() to gcov_tool usage
>>>>>
>>>>> 5. rename profile-tool to gcov-tool per Honza's suggestion.
>>>>>
>>>>> Thanks,
>>>>
>>>> Hi,
>>>> I did not read in deatil the gcov-tool source itself, but lets first make the interface changes
>>>> needed.
>>>>
>>>>> 2013-11-18  Rong Xu  <xur@google.com>
>>>>>
>>>>>       * gcc/gcov-io.c (gcov_var): Moved from gcov-io.h and make it static.
>>>>>       (gcov_position): Move from gcov-io.h
>>>>>       (gcov_is_error): Ditto.
>>>>>       (gcov_rewrite): Ditto.
>>>>>       * gcc/gcov-io.h: Re-factoring. Move gcov_var to gcov-io.h and
>>>>>         move the libgcov only part of libgcc/libgcov.h.
>>>>>       * libgcc/libgcov.h: New common header files for libgcov-*.h
>>>>>       * libgcc/Makefile.in: Add dependence to libgcov.h
>>>>>       * libgcc/libgcov-profiler.c: Use libgcov.h
>>>>>       * libgcc/libgcov-driver.c: Ditto.
>>>>>       * libgcc/libgcov-interface.c: Ditto.
>>>>>       * libgcc/libgcov-driver-system.c (allocate_filename_struct): use
>>>>>       xmalloc instread of malloc.
>>>>>       * libgcc/libgcov-merge.c (void __gcov_merge_delta): Add more
>>>>>       parameters to merge function.
>>>>>       (__gcov_merge_add): Ditto.
>>>>>       (__gcov_merge_ior): Ditto.
>>>>>       (__gcov_merge_time_profile): Ditto.
>>>>>       (__gcov_merge_single): Ditto.
>>>>>       (__gcov_merge_delta): Ditto.
>>>>>       * libgcc/libgcov-tool.c (void gcov_tool_set_verbose): New for
>>>>>       gcov-tool support.
>>>>>       (set_fn_ctrs): Ditto.
>>>>>       (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_merge): Ditto.
>>>>>       (find_match_gcov_inf Ditto.o):
>>>>>       (gcov_profile_merge): Ditto.
>>>>>       (__gcov_scale_add): Ditto.
>>>>>       (__gcov_scale_ior): Ditto.
>>>>>       (__gcov_scale_delta): Ditto.
>>>>>       (__gcov_scale_single): Ditto.
>>>>>       (gcov_profile_scale): Ditto.
>>>>>       (gcov_profile_normalize): Ditto.
>>>>>       (__gcov_scale2_add): Ditto.
>>>>>       (__gcov_scale2_ior): Ditto.
>>>>>       (__gcov_scale2_delta): Ditto.
>>>>>       (__gcov_scale2_single): Ditto.
>>>>>       (gcov_profile_scale2): Ditto.
>>>>>       * gcc/gcov-tool.c (unlink_file): Gcov-tool driver support.
>>>>>       (unlink_dir): Ditto.
>>>>>       (profile_merge): Ditto.
>>>>>       (print_merge_usage_message): Ditto.
>>>>>       (merge_usage): Ditto.
>>>>>       (do_merge): Ditto.
>>>>>       (profile_rewrite2): Ditto.
>>>>>       (profile_rewrite): Ditto.
>>>>>       (print_rewrite_usage_message): Ditto.
>>>>>       (rewrite_usage): Ditto.
>>>>>       (do_rewrite): Ditto.
>>>>>       (print_usage): Ditto.
>>>>>       (print_version): Ditto.
>>>>>       (process_args): Ditto.
>>>>>       (main): Ditto.
>>>>>       * gcc/Makefile.in: Build and install gcov-tool.
>>>>
>>>>> Index: gcc/gcov-io.c
>>>>> ===================================================================
>>>>> --- gcc/gcov-io.c     (revision 204895)
>>>>> +++ gcc/gcov-io.c     (working copy)
>>>>> @@ -36,6 +36,37 @@ static const gcov_unsigned_t *gcov_read_words (uns
>>>>>  static void gcov_allocate (unsigned);
>>>>>  #endif
>>>>>
>>>>> +/* Moved for gcov-io.h and make it static.  */
>>>>> +static struct gcov_var gcov_var;
>>>>
>>>> This is more an changelog message than a comment in source file.
>>>> Just describe what gcov_var is.
>>>
>>> I changed this so gcov_var is no longer static, but global as before.
>>>
>>>>
>>>> Do you know how the size of libgcov changed with your patch?
>>>> Quick check of current mainline on compiling empty main gives:
>>>>
>>>> jh@gcc10:~/trunk/build/gcc$ cat t.c
>>>> main()
>>>> {
>>>> }
>>>> jh@gcc10:~/trunk/build/gcc$ ./xgcc -B ./ -O2 -fprofile-generate -o a.out-new --static t.c
>>>> jh@gcc10:~/trunk/build/gcc$ gcc -O2 -fprofile-generate -o a.out-old --static t.c
>>>> jh@gcc10:~/trunk/build/gcc$ size a.out-old
>>>>    text    data     bss     dec     hex filename
>>>>  608141    3560   16728  628429   996cd a.out-old
>>>> jh@gcc10:~/trunk/build/gcc$ size a.out-new
>>>>    text    data     bss     dec     hex filename
>>>>  612621    3688   22880  639189   9c0d5 a.out-new
>>>>
>>>> Without profiling I get:
>>>> jh@gcc10:~/trunk/build/gcc$ size a.out-new-no
>>>> jh@gcc10:~/trunk/build/gcc$ size a.out-old-no
>>>>    text    data     bss     dec     hex filename
>>>>  599719    3448   12568  615735   96537 a.out-old-no
>>>>    text    data     bss     dec     hex filename
>>>>  600247    3448   12568  616263   96747 a.out-new-no
>>>>
>>>> Quite big for empty program, but mostly glibc fault, I suppose
>>>> (that won't be an issue for embedded platforms). But anyway
>>>> we increased text size overhead from 8k to 12k, BSS size
>>>> overhead from 4k to 10k and data by another 1k.
>>>>
>>>>    text    data     bss     dec     hex filename
>>>>     126       0       0     126      7e _gcov_merge_add.o (ex libgcov.a)
>>>>     251       0       0     251      fb _gcov_merge_single.o (ex libgcov.a)
>>>>     242       0       0     242      f2 _gcov_merge_delta.o (ex libgcov.a)
>>>>     126       0       0     126      7e _gcov_merge_ior.o (ex libgcov.a)
>>>>     156       0       0     156      9c _gcov_merge_time_profile.o (ex libgcov.a)
>>>>      89       0       0      89      59 _gcov_interval_profiler.o (ex libgcov.a)
>>>>      69       0       0      69      45 _gcov_pow2_profiler.o (ex libgcov.a)
>>>>     115       0       0     115      73 _gcov_one_value_profiler.o (ex libgcov.a)
>>>>     122       0       0     122      7a _gcov_indirect_call_profiler.o (ex libgcov.a)
>>>>      57       0       0      57      39 _gcov_average_profiler.o (ex libgcov.a)
>>>>      52       0       0      52      34 _gcov_ior_profiler.o (ex libgcov.a)
>>>>     178       0      16     194      c2 _gcov_indirect_call_profiler_v2.o (ex libgcov.a)
>>>>      77       0       8      85      55 _gcov_time_profiler.o (ex libgcov.a)
>>>>     126       0      40     166      a6 _gcov_flush.o (ex libgcov.a)
>>>>     101       0       0     101      65 _gcov_fork.o (ex libgcov.a)
>>>>     471       0       0     471     1d7 _gcov_execl.o (ex libgcov.a)
>>>>     471       0       0     471     1d7 _gcov_execlp.o (ex libgcov.a)
>>>>     524       0       0     524     20c _gcov_execle.o (ex libgcov.a)
>>>>      98       0       0      98      62 _gcov_execv.o (ex libgcov.a)
>>>>      98       0       0      98      62 _gcov_execvp.o (ex libgcov.a)
>>>>     108       0       0     108      6c _gcov_execve.o (ex libgcov.a)
>>>>      66       0       0      66      42 _gcov_reset.o (ex libgcov.a)
>>>>      66       0       0      66      42 _gcov_dump.o (ex libgcov.a)
>>>>    9505       0    6144   15649    3d21 _gcov.o (ex libgcov.a)
>>>>
>>>> I think we definitely need to move those 6k of bss space out.  I think those are new
>>>> static vars you introduced that I think are unsafe anyway because multiple streaming
>>>> may run at once in threaded program where locking mechanizm fails.
>>>> (it will probably do other bad things, but definitely we do not want to
>>>> conflict on things like filename to write into).
>>>>
>>>> Compared to my system gcov:
>>>>    text    data     bss     dec     hex filename
>>>>    9765       0      64    9829    2665 _gcov.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>     125       0       0     125      7d _gcov_merge_add.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>     251       0       0     251      fb _gcov_merge_single.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>     242       0       0     242      f2 _gcov_merge_delta.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>     101       0       0     101      65 _gcov_fork.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>     471       0       0     471     1d7 _gcov_execl.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>     471       0       0     471     1d7 _gcov_execlp.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>     524       0       0     524     20c _gcov_execle.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>      98       0       0      98      62 _gcov_execv.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>      98       0       0      98      62 _gcov_execvp.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>     108       0       0     108      6c _gcov_execve.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>      72       0       0      72      48 _gcov_reset.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>      72       0       0      72      48 _gcov_dump.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>      89       0       0      89      59 _gcov_interval_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>      69       0       0      69      45 _gcov_pow2_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>     115       0       0     115      73 _gcov_one_value_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>     122       0       0     122      7a _gcov_indirect_call_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>      57       0       0      57      39 _gcov_average_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>      52       0       0      52      34 _gcov_ior_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>     125       0       0     125      7d _gcov_merge_ior.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>
>>>>> Index: gcc/gcov-io.h
>>>>> ===================================================================
>>>>> --- gcc/gcov-io.h     (revision 204895)
>>>>> +++ gcc/gcov-io.h     (working copy)
>>>>> @@ -164,51 +164,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
>>>>>  #ifndef GCC_GCOV_IO_H
>>>>>  #define GCC_GCOV_IO_H
>>>>>
>>>>> -#if IN_LIBGCOV
>>>>> -/* About the target */
>>>>> -
>>>>> -#if BITS_PER_UNIT == 8
>>>>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI)));
>>>>> -typedef unsigned gcov_position_t __attribute__ ((mode (SI)));
>>>>> -#if LONG_LONG_TYPE_SIZE > 32
>>>>> -typedef signed gcov_type __attribute__ ((mode (DI)));
>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (DI)));
>>>>> -#else
>>>>> -typedef signed gcov_type __attribute__ ((mode (SI)));
>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
>>>>> -#endif
>>>>> -#else
>>>>> -#if BITS_PER_UNIT == 16
>>>>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (HI)));
>>>>> -typedef unsigned gcov_position_t __attribute__ ((mode (HI)));
>>>>> -#if LONG_LONG_TYPE_SIZE > 32
>>>>> -typedef signed gcov_type __attribute__ ((mode (SI)));
>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
>>>>> -#else
>>>>> -typedef signed gcov_type __attribute__ ((mode (HI)));
>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
>>>>> -#endif
>>>>> -#else
>>>>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (QI)));
>>>>> -typedef unsigned gcov_position_t __attribute__ ((mode (QI)));
>>>>> -#if LONG_LONG_TYPE_SIZE > 32
>>>>> -typedef signed gcov_type __attribute__ ((mode (HI)));
>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
>>>>> -#else
>>>>> -typedef signed gcov_type __attribute__ ((mode (QI)));
>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI)));
>>>>> -#endif
>>>>> -#endif
>>>>> -#endif
>>>>
>>>> So this part basically moves libgcov specific bits into libgcov.h? That OK fine by
>>>> itself.
>>>>> Index: libgcc/libgcov-profiler.c
>>>>> ===================================================================
>>>>> --- libgcc/libgcov-profiler.c (revision 204895)
>>>>> +++ libgcc/libgcov-profiler.c (working copy)
>>>>> @@ -23,15 +23,8 @@ a copy of the GCC Runtime Library Exception along
>>>>>  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>>>>>  <http://www.gnu.org/licenses/>.  */
>>>>>
>>>>> -#include "tconfig.h"
>>>>> -#include "tsystem.h"
>>>>> -#include "coretypes.h"
>>>>> -#include "tm.h"
>>>>> -#include "libgcc_tm.h"
>>>>> -
>>>>> +#include "libgcov.h"
>>>>>  #if !defined(inhibit_libc)
>>>>> -#define IN_LIBGCOV 1
>>>>> -#include "gcov-io.h"
>>>>
>>>> I did not pay that much attention into the current include file changes, but wasn't
>>>> idea to avoid #include file to include random other #includes?
>>>> So perhaps the first block of includes should stay, followed by libgcov.h and gcov-io.h
>>>> last?
>>>
>>> I'm not sure I understand the issue here? The patch basically moves
>>> the same includes into libgcov.h, and includes that instead. I see
>>> many other header files in gcc that include other headers.
>>>
>>>>> @@ -325,6 +311,9 @@ static struct gcov_summary all_prg;
>>>>>  #endif
>>>>>  /* crc32 for this program.  */
>>>>>  static gcov_unsigned_t crc32;
>>>>> +/* Use this summary checksum rather the computed one if the value is
>>>>> + *    non-zero.  */
>>>>> +static gcov_unsigned_t saved_summary_checksum;
>>>>
>>>> Why do you need to save the checksum? Won't it reset summary back with multiple streaming?
>>>
>>> This has been removed.
>>>
>>>>
>>>> I would really like to avoid introducing those static vars that are used exclusively
>>>> by gcov_exit.  What about putting them into an gcov_context structure that
>>>> is passed around the functions that was broken out?
>>>>> Index: libgcc/libgcov-merge.c
>>>>> ===================================================================
>>>>> --- libgcc/libgcov-merge.c    (revision 204895)
>>>>> +++ libgcc/libgcov-merge.c    (working copy)
>>>>> @@ -23,107 +23,150 @@ a copy of the GCC Runtime Library Exception along
>>>>>  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>>>>>  <http://www.gnu.org/licenses/>.  */
>>>>>
>>>>> -#include "tconfig.h"
>>>>> -#include "tsystem.h"
>>>>> -#include "coretypes.h"
>>>>> -#include "tm.h"
>>>>> -#include "libgcc_tm.h"
>>>>> +#include "libgcov.h"
>>>>>
>>>>> -#if defined(inhibit_libc)
>>>>> -#define IN_LIBGCOV (-1)
>>>>> -#else
>>>>> -#define IN_LIBGCOV 1
>>>>> +#include "gcov-io-libgcov.h"
>>>>>  #endif
>>>>>
>>>>> -#include "gcov-io.h"
>>>>> -
>>>>>  #if defined(inhibit_libc)
>>>>>  /* If libc and its header files are not available, provide dummy functions.  */
>>>>>
>>>>>  #ifdef L_gcov_merge_add
>>>>>  void __gcov_merge_add (gcov_type *counters  __attribute__ ((unused)),
>>>>> -                       unsigned n_counters __attribute__ ((unused))) {}
>>>>> +                       unsigned n_counters __attribute__ ((unused)),
>>>>> +                       unsigned gcov_type *src __attribute__ ((unused)),
>>>>> +                       unsigned w __attribute__ ((unused))) {}
>>>>>  #endif
>>>>>
>>>>>  #ifdef L_gcov_merge_single
>>>>>  void __gcov_merge_single (gcov_type *counters  __attribute__ ((unused)),
>>>>> -                          unsigned n_counters __attribute__ ((unused))) {}
>>>>> +                          unsigned n_counters __attribute__ ((unused)),
>>>>> +                          unsigned gcov_type *src __attribute__ ((unused)),
>>>>> +                          unsigned w __attribute__ ((unused))) {}
>>>>>  #endif
>>>>>
>>>>>  #ifdef L_gcov_merge_delta
>>>>>  void __gcov_merge_delta (gcov_type *counters  __attribute__ ((unused)),
>>>>> -                         unsigned n_counters __attribute__ ((unused))) {}
>>>>> +                         unsigned n_counters __attribute__ ((unused)),
>>>>> +                         unsigned gcov_type *src __attribute__ ((unused)),
>>>>> +                         unsigned w __attribute__ ((unused))) {}
>>>>>  #endif
>>>>>
>>>>>  #else
>>>>>
>>>>>  #ifdef L_gcov_merge_add
>>>>>  /* The profile merging function that just adds the counters.  It is given
>>>>> -   an array COUNTERS of N_COUNTERS old counters and it reads the same number
>>>>> -   of counters from the gcov file.  */
>>>>> +   an array COUNTERS of N_COUNTERS old counters.
>>>>> +   When SRC==NULL, it reads the same number of counters from the gcov file.
>>>>> +   Otherwise, it reads from SRC array. These read values will be multiplied
>>>>> +   by weight W before adding to the old counters.  */
>>>>>  void
>>>>> -__gcov_merge_add (gcov_type *counters, unsigned n_counters)
>>>>> +__gcov_merge_add (gcov_type *counters, unsigned n_counters,
>>>>> +                  gcov_type *src, unsigned w)
>>>>>  {
>>>>> +  int in_mem = (src != 0);
>>>>> +
>>>>>    for (; n_counters; counters++, n_counters--)
>>>>> -    *counters += gcov_read_counter ();
>>>>> +    {
>>>>> +      gcov_type value;
>>>>> +
>>>>> +      if (in_mem)
>>>>> +        value = *(src++);
>>>>> +      else
>>>>> +        value = gcov_read_counter ();
>>>>> +
>>>>> +      *counters += w * value;
>>>>> +    }
>>>>>  }
>>>>>  #endif /* L_gcov_merge_add */
>>>>>
>>>>>  #ifdef L_gcov_merge_ior
>>>>>  /* The profile merging function that just adds the counters.  It is given
>>>>> -   an array COUNTERS of N_COUNTERS old counters and it reads the same number
>>>>> -   of counters from the gcov file.  */
>>>>> +   an array COUNTERS of N_COUNTERS old counters.
>>>>> +   When SRC==NULL, it reads the same number of counters from the gcov file.
>>>>> +   Otherwise, it reads from SRC array.  */
>>>>>  void
>>>>> -__gcov_merge_ior (gcov_type *counters, unsigned n_counters)
>>>>> +__gcov_merge_ior (gcov_type *counters, unsigned n_counters,
>>>>> +                  gcov_type *src, unsigned w __attribute__ ((unused)))
>>>>
>>>> So the new in-memory variants are introduced for merging tool, while libgcc use gcov_read_counter
>>>> interface?
>>>> Perhaps we can actually just duplicate the functions to avoid runtime to do all the scalling
>>>> and in_mem tests it won't need?
>>>>
>>>> I would suggest going with libgcov.h changes and clenaups first, with interface changes next
>>>> and the gcov-tool is probably quite obvious at the end?
>>>> Do you think you can split the patch this way?
>>>>
>>>> Thanks and sorry for taking long to review. I should have more time again now.
>>>> Honza
>>>
>>> The libgcov.h related changes are in the attached patch. I think it
>>> addresses your concerns. Bootstrapped and tested on
>>> x86-64-unknown-linux-gnu. A profiledbootstrap is in progress.
>>>
>>> Ok for trunk if profiledbootstrap passes?
>>
>> Both a profiledbootstrap and LTO profiledbootstrap pass.
>>
>> Teresa
>>
>>>
>>> Thanks,
>>> Teresa
>>>
>>>
>>> --
>>> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413
>>
>>
>>
>> --
>> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413



-- 
Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-12-16 21:17                       ` Teresa Johnson
@ 2013-12-16 21:51                         ` Xinliang David Li
  2013-12-16 22:34                           ` Jan Hubicka
  0 siblings, 1 reply; 55+ messages in thread
From: Xinliang David Li @ 2013-12-16 21:51 UTC (permalink / raw)
  To: Teresa Johnson; +Cc: Jan Hubicka, Rong Xu, GCC Patches

I think so -- they are private to libgcov.  Honza, what do you think?

thanks,

David

On Mon, Dec 16, 2013 at 1:17 PM, Teresa Johnson <tejohnson@google.com> wrote:
> On Mon, Dec 16, 2013 at 12:55 PM, Xinliang David Li <davidxl@google.com> wrote:
>> gcov_rewrite function is only needed (and defined) with IN_LIBGCOV.
>> Should it be moved from common file gcov-io.c to libgcov.c?
>
> Possibly. I just looked through gcov-io.c and there are several
> additional functions that are only defined under "#ifdef IN_LIBGCOV"
> and only used in libgcov*c (or each other):
>
> gcov_write_counter
> gcov_write_tag_length
> gcov_write_summary
> gcov_seek
>
> Should they all, plus gcov_rewrite, be moved to libgcov-driver.c?
>
> Teresa
>
>>
>>
>> David
>>
>> On Thu, Dec 12, 2013 at 12:11 PM, Teresa Johnson <tejohnson@google.com> wrote:
>>> On Wed, Dec 11, 2013 at 10:05 PM, Teresa Johnson <tejohnson@google.com> wrote:
>>>> On Fri, Dec 6, 2013 at 6:23 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>>>>>> Hi, all
>>>>>>
>>>>>> This is the new patch for gcov-tool (previously profile-tool).
>>>>>>
>>>>>> Honza: can you comment on the new merge interface? David posted some
>>>>>> comments in an earlier email and we want to know what's your opinion.
>>>>>>
>>>>>> Test patch has been tested with boostrap, regresssion,
>>>>>> profiledbootstrap and SPEC2006.
>>>>>>
>>>>>> Noticeable changes from the earlier version:
>>>>>>
>>>>>> 1. create a new file libgcov.h and move libgcov-*.h headers to libgcov.h
>>>>>> So we can included multiple libgcov-*.c without adding new macros.
>>>>>>
>>>>>> 2. split libgcov.h specific code in gcvo-io.h to libcc/libgcov.h
>>>>>> Avoid multiple-page of code under IN_LIBGCOV macro -- this
>>>>>> improves the readability.
>>>>>>
>>>>>> 3. make gcov_var static, and move the definition from gcov-io.h to
>>>>>> gcov-io.c. Also
>>>>>>    move some static functions accessing gcov_var to gcvo-io.c
>>>>>> Current code rely on GCOV_LINKAGE tricks to avoid multi-definition. I don't see
>>>>>> a reason that gcov_var needs to exposed as a global.
>>>>>>
>>>>>> 4. expose gcov_write_strings() and gcov_sync() to gcov_tool usage
>>>>>>
>>>>>> 5. rename profile-tool to gcov-tool per Honza's suggestion.
>>>>>>
>>>>>> Thanks,
>>>>>
>>>>> Hi,
>>>>> I did not read in deatil the gcov-tool source itself, but lets first make the interface changes
>>>>> needed.
>>>>>
>>>>>> 2013-11-18  Rong Xu  <xur@google.com>
>>>>>>
>>>>>>       * gcc/gcov-io.c (gcov_var): Moved from gcov-io.h and make it static.
>>>>>>       (gcov_position): Move from gcov-io.h
>>>>>>       (gcov_is_error): Ditto.
>>>>>>       (gcov_rewrite): Ditto.
>>>>>>       * gcc/gcov-io.h: Re-factoring. Move gcov_var to gcov-io.h and
>>>>>>         move the libgcov only part of libgcc/libgcov.h.
>>>>>>       * libgcc/libgcov.h: New common header files for libgcov-*.h
>>>>>>       * libgcc/Makefile.in: Add dependence to libgcov.h
>>>>>>       * libgcc/libgcov-profiler.c: Use libgcov.h
>>>>>>       * libgcc/libgcov-driver.c: Ditto.
>>>>>>       * libgcc/libgcov-interface.c: Ditto.
>>>>>>       * libgcc/libgcov-driver-system.c (allocate_filename_struct): use
>>>>>>       xmalloc instread of malloc.
>>>>>>       * libgcc/libgcov-merge.c (void __gcov_merge_delta): Add more
>>>>>>       parameters to merge function.
>>>>>>       (__gcov_merge_add): Ditto.
>>>>>>       (__gcov_merge_ior): Ditto.
>>>>>>       (__gcov_merge_time_profile): Ditto.
>>>>>>       (__gcov_merge_single): Ditto.
>>>>>>       (__gcov_merge_delta): Ditto.
>>>>>>       * libgcc/libgcov-tool.c (void gcov_tool_set_verbose): New for
>>>>>>       gcov-tool support.
>>>>>>       (set_fn_ctrs): Ditto.
>>>>>>       (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_merge): Ditto.
>>>>>>       (find_match_gcov_inf Ditto.o):
>>>>>>       (gcov_profile_merge): Ditto.
>>>>>>       (__gcov_scale_add): Ditto.
>>>>>>       (__gcov_scale_ior): Ditto.
>>>>>>       (__gcov_scale_delta): Ditto.
>>>>>>       (__gcov_scale_single): Ditto.
>>>>>>       (gcov_profile_scale): Ditto.
>>>>>>       (gcov_profile_normalize): Ditto.
>>>>>>       (__gcov_scale2_add): Ditto.
>>>>>>       (__gcov_scale2_ior): Ditto.
>>>>>>       (__gcov_scale2_delta): Ditto.
>>>>>>       (__gcov_scale2_single): Ditto.
>>>>>>       (gcov_profile_scale2): Ditto.
>>>>>>       * gcc/gcov-tool.c (unlink_file): Gcov-tool driver support.
>>>>>>       (unlink_dir): Ditto.
>>>>>>       (profile_merge): Ditto.
>>>>>>       (print_merge_usage_message): Ditto.
>>>>>>       (merge_usage): Ditto.
>>>>>>       (do_merge): Ditto.
>>>>>>       (profile_rewrite2): Ditto.
>>>>>>       (profile_rewrite): Ditto.
>>>>>>       (print_rewrite_usage_message): Ditto.
>>>>>>       (rewrite_usage): Ditto.
>>>>>>       (do_rewrite): Ditto.
>>>>>>       (print_usage): Ditto.
>>>>>>       (print_version): Ditto.
>>>>>>       (process_args): Ditto.
>>>>>>       (main): Ditto.
>>>>>>       * gcc/Makefile.in: Build and install gcov-tool.
>>>>>
>>>>>> Index: gcc/gcov-io.c
>>>>>> ===================================================================
>>>>>> --- gcc/gcov-io.c     (revision 204895)
>>>>>> +++ gcc/gcov-io.c     (working copy)
>>>>>> @@ -36,6 +36,37 @@ static const gcov_unsigned_t *gcov_read_words (uns
>>>>>>  static void gcov_allocate (unsigned);
>>>>>>  #endif
>>>>>>
>>>>>> +/* Moved for gcov-io.h and make it static.  */
>>>>>> +static struct gcov_var gcov_var;
>>>>>
>>>>> This is more an changelog message than a comment in source file.
>>>>> Just describe what gcov_var is.
>>>>
>>>> I changed this so gcov_var is no longer static, but global as before.
>>>>
>>>>>
>>>>> Do you know how the size of libgcov changed with your patch?
>>>>> Quick check of current mainline on compiling empty main gives:
>>>>>
>>>>> jh@gcc10:~/trunk/build/gcc$ cat t.c
>>>>> main()
>>>>> {
>>>>> }
>>>>> jh@gcc10:~/trunk/build/gcc$ ./xgcc -B ./ -O2 -fprofile-generate -o a.out-new --static t.c
>>>>> jh@gcc10:~/trunk/build/gcc$ gcc -O2 -fprofile-generate -o a.out-old --static t.c
>>>>> jh@gcc10:~/trunk/build/gcc$ size a.out-old
>>>>>    text    data     bss     dec     hex filename
>>>>>  608141    3560   16728  628429   996cd a.out-old
>>>>> jh@gcc10:~/trunk/build/gcc$ size a.out-new
>>>>>    text    data     bss     dec     hex filename
>>>>>  612621    3688   22880  639189   9c0d5 a.out-new
>>>>>
>>>>> Without profiling I get:
>>>>> jh@gcc10:~/trunk/build/gcc$ size a.out-new-no
>>>>> jh@gcc10:~/trunk/build/gcc$ size a.out-old-no
>>>>>    text    data     bss     dec     hex filename
>>>>>  599719    3448   12568  615735   96537 a.out-old-no
>>>>>    text    data     bss     dec     hex filename
>>>>>  600247    3448   12568  616263   96747 a.out-new-no
>>>>>
>>>>> Quite big for empty program, but mostly glibc fault, I suppose
>>>>> (that won't be an issue for embedded platforms). But anyway
>>>>> we increased text size overhead from 8k to 12k, BSS size
>>>>> overhead from 4k to 10k and data by another 1k.
>>>>>
>>>>>    text    data     bss     dec     hex filename
>>>>>     126       0       0     126      7e _gcov_merge_add.o (ex libgcov.a)
>>>>>     251       0       0     251      fb _gcov_merge_single.o (ex libgcov.a)
>>>>>     242       0       0     242      f2 _gcov_merge_delta.o (ex libgcov.a)
>>>>>     126       0       0     126      7e _gcov_merge_ior.o (ex libgcov.a)
>>>>>     156       0       0     156      9c _gcov_merge_time_profile.o (ex libgcov.a)
>>>>>      89       0       0      89      59 _gcov_interval_profiler.o (ex libgcov.a)
>>>>>      69       0       0      69      45 _gcov_pow2_profiler.o (ex libgcov.a)
>>>>>     115       0       0     115      73 _gcov_one_value_profiler.o (ex libgcov.a)
>>>>>     122       0       0     122      7a _gcov_indirect_call_profiler.o (ex libgcov.a)
>>>>>      57       0       0      57      39 _gcov_average_profiler.o (ex libgcov.a)
>>>>>      52       0       0      52      34 _gcov_ior_profiler.o (ex libgcov.a)
>>>>>     178       0      16     194      c2 _gcov_indirect_call_profiler_v2.o (ex libgcov.a)
>>>>>      77       0       8      85      55 _gcov_time_profiler.o (ex libgcov.a)
>>>>>     126       0      40     166      a6 _gcov_flush.o (ex libgcov.a)
>>>>>     101       0       0     101      65 _gcov_fork.o (ex libgcov.a)
>>>>>     471       0       0     471     1d7 _gcov_execl.o (ex libgcov.a)
>>>>>     471       0       0     471     1d7 _gcov_execlp.o (ex libgcov.a)
>>>>>     524       0       0     524     20c _gcov_execle.o (ex libgcov.a)
>>>>>      98       0       0      98      62 _gcov_execv.o (ex libgcov.a)
>>>>>      98       0       0      98      62 _gcov_execvp.o (ex libgcov.a)
>>>>>     108       0       0     108      6c _gcov_execve.o (ex libgcov.a)
>>>>>      66       0       0      66      42 _gcov_reset.o (ex libgcov.a)
>>>>>      66       0       0      66      42 _gcov_dump.o (ex libgcov.a)
>>>>>    9505       0    6144   15649    3d21 _gcov.o (ex libgcov.a)
>>>>>
>>>>> I think we definitely need to move those 6k of bss space out.  I think those are new
>>>>> static vars you introduced that I think are unsafe anyway because multiple streaming
>>>>> may run at once in threaded program where locking mechanizm fails.
>>>>> (it will probably do other bad things, but definitely we do not want to
>>>>> conflict on things like filename to write into).
>>>>>
>>>>> Compared to my system gcov:
>>>>>    text    data     bss     dec     hex filename
>>>>>    9765       0      64    9829    2665 _gcov.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>>     125       0       0     125      7d _gcov_merge_add.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>>     251       0       0     251      fb _gcov_merge_single.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>>     242       0       0     242      f2 _gcov_merge_delta.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>>     101       0       0     101      65 _gcov_fork.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>>     471       0       0     471     1d7 _gcov_execl.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>>     471       0       0     471     1d7 _gcov_execlp.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>>     524       0       0     524     20c _gcov_execle.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>>      98       0       0      98      62 _gcov_execv.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>>      98       0       0      98      62 _gcov_execvp.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>>     108       0       0     108      6c _gcov_execve.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>>      72       0       0      72      48 _gcov_reset.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>>      72       0       0      72      48 _gcov_dump.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>>      89       0       0      89      59 _gcov_interval_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>>      69       0       0      69      45 _gcov_pow2_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>>     115       0       0     115      73 _gcov_one_value_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>>     122       0       0     122      7a _gcov_indirect_call_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>>      57       0       0      57      39 _gcov_average_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>>      52       0       0      52      34 _gcov_ior_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>>     125       0       0     125      7d _gcov_merge_ior.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>>>
>>>>>> Index: gcc/gcov-io.h
>>>>>> ===================================================================
>>>>>> --- gcc/gcov-io.h     (revision 204895)
>>>>>> +++ gcc/gcov-io.h     (working copy)
>>>>>> @@ -164,51 +164,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
>>>>>>  #ifndef GCC_GCOV_IO_H
>>>>>>  #define GCC_GCOV_IO_H
>>>>>>
>>>>>> -#if IN_LIBGCOV
>>>>>> -/* About the target */
>>>>>> -
>>>>>> -#if BITS_PER_UNIT == 8
>>>>>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI)));
>>>>>> -typedef unsigned gcov_position_t __attribute__ ((mode (SI)));
>>>>>> -#if LONG_LONG_TYPE_SIZE > 32
>>>>>> -typedef signed gcov_type __attribute__ ((mode (DI)));
>>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (DI)));
>>>>>> -#else
>>>>>> -typedef signed gcov_type __attribute__ ((mode (SI)));
>>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
>>>>>> -#endif
>>>>>> -#else
>>>>>> -#if BITS_PER_UNIT == 16
>>>>>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (HI)));
>>>>>> -typedef unsigned gcov_position_t __attribute__ ((mode (HI)));
>>>>>> -#if LONG_LONG_TYPE_SIZE > 32
>>>>>> -typedef signed gcov_type __attribute__ ((mode (SI)));
>>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
>>>>>> -#else
>>>>>> -typedef signed gcov_type __attribute__ ((mode (HI)));
>>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
>>>>>> -#endif
>>>>>> -#else
>>>>>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (QI)));
>>>>>> -typedef unsigned gcov_position_t __attribute__ ((mode (QI)));
>>>>>> -#if LONG_LONG_TYPE_SIZE > 32
>>>>>> -typedef signed gcov_type __attribute__ ((mode (HI)));
>>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
>>>>>> -#else
>>>>>> -typedef signed gcov_type __attribute__ ((mode (QI)));
>>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI)));
>>>>>> -#endif
>>>>>> -#endif
>>>>>> -#endif
>>>>>
>>>>> So this part basically moves libgcov specific bits into libgcov.h? That OK fine by
>>>>> itself.
>>>>>> Index: libgcc/libgcov-profiler.c
>>>>>> ===================================================================
>>>>>> --- libgcc/libgcov-profiler.c (revision 204895)
>>>>>> +++ libgcc/libgcov-profiler.c (working copy)
>>>>>> @@ -23,15 +23,8 @@ a copy of the GCC Runtime Library Exception along
>>>>>>  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>>>>>>  <http://www.gnu.org/licenses/>.  */
>>>>>>
>>>>>> -#include "tconfig.h"
>>>>>> -#include "tsystem.h"
>>>>>> -#include "coretypes.h"
>>>>>> -#include "tm.h"
>>>>>> -#include "libgcc_tm.h"
>>>>>> -
>>>>>> +#include "libgcov.h"
>>>>>>  #if !defined(inhibit_libc)
>>>>>> -#define IN_LIBGCOV 1
>>>>>> -#include "gcov-io.h"
>>>>>
>>>>> I did not pay that much attention into the current include file changes, but wasn't
>>>>> idea to avoid #include file to include random other #includes?
>>>>> So perhaps the first block of includes should stay, followed by libgcov.h and gcov-io.h
>>>>> last?
>>>>
>>>> I'm not sure I understand the issue here? The patch basically moves
>>>> the same includes into libgcov.h, and includes that instead. I see
>>>> many other header files in gcc that include other headers.
>>>>
>>>>>> @@ -325,6 +311,9 @@ static struct gcov_summary all_prg;
>>>>>>  #endif
>>>>>>  /* crc32 for this program.  */
>>>>>>  static gcov_unsigned_t crc32;
>>>>>> +/* Use this summary checksum rather the computed one if the value is
>>>>>> + *    non-zero.  */
>>>>>> +static gcov_unsigned_t saved_summary_checksum;
>>>>>
>>>>> Why do you need to save the checksum? Won't it reset summary back with multiple streaming?
>>>>
>>>> This has been removed.
>>>>
>>>>>
>>>>> I would really like to avoid introducing those static vars that are used exclusively
>>>>> by gcov_exit.  What about putting them into an gcov_context structure that
>>>>> is passed around the functions that was broken out?
>>>>>> Index: libgcc/libgcov-merge.c
>>>>>> ===================================================================
>>>>>> --- libgcc/libgcov-merge.c    (revision 204895)
>>>>>> +++ libgcc/libgcov-merge.c    (working copy)
>>>>>> @@ -23,107 +23,150 @@ a copy of the GCC Runtime Library Exception along
>>>>>>  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>>>>>>  <http://www.gnu.org/licenses/>.  */
>>>>>>
>>>>>> -#include "tconfig.h"
>>>>>> -#include "tsystem.h"
>>>>>> -#include "coretypes.h"
>>>>>> -#include "tm.h"
>>>>>> -#include "libgcc_tm.h"
>>>>>> +#include "libgcov.h"
>>>>>>
>>>>>> -#if defined(inhibit_libc)
>>>>>> -#define IN_LIBGCOV (-1)
>>>>>> -#else
>>>>>> -#define IN_LIBGCOV 1
>>>>>> +#include "gcov-io-libgcov.h"
>>>>>>  #endif
>>>>>>
>>>>>> -#include "gcov-io.h"
>>>>>> -
>>>>>>  #if defined(inhibit_libc)
>>>>>>  /* If libc and its header files are not available, provide dummy functions.  */
>>>>>>
>>>>>>  #ifdef L_gcov_merge_add
>>>>>>  void __gcov_merge_add (gcov_type *counters  __attribute__ ((unused)),
>>>>>> -                       unsigned n_counters __attribute__ ((unused))) {}
>>>>>> +                       unsigned n_counters __attribute__ ((unused)),
>>>>>> +                       unsigned gcov_type *src __attribute__ ((unused)),
>>>>>> +                       unsigned w __attribute__ ((unused))) {}
>>>>>>  #endif
>>>>>>
>>>>>>  #ifdef L_gcov_merge_single
>>>>>>  void __gcov_merge_single (gcov_type *counters  __attribute__ ((unused)),
>>>>>> -                          unsigned n_counters __attribute__ ((unused))) {}
>>>>>> +                          unsigned n_counters __attribute__ ((unused)),
>>>>>> +                          unsigned gcov_type *src __attribute__ ((unused)),
>>>>>> +                          unsigned w __attribute__ ((unused))) {}
>>>>>>  #endif
>>>>>>
>>>>>>  #ifdef L_gcov_merge_delta
>>>>>>  void __gcov_merge_delta (gcov_type *counters  __attribute__ ((unused)),
>>>>>> -                         unsigned n_counters __attribute__ ((unused))) {}
>>>>>> +                         unsigned n_counters __attribute__ ((unused)),
>>>>>> +                         unsigned gcov_type *src __attribute__ ((unused)),
>>>>>> +                         unsigned w __attribute__ ((unused))) {}
>>>>>>  #endif
>>>>>>
>>>>>>  #else
>>>>>>
>>>>>>  #ifdef L_gcov_merge_add
>>>>>>  /* The profile merging function that just adds the counters.  It is given
>>>>>> -   an array COUNTERS of N_COUNTERS old counters and it reads the same number
>>>>>> -   of counters from the gcov file.  */
>>>>>> +   an array COUNTERS of N_COUNTERS old counters.
>>>>>> +   When SRC==NULL, it reads the same number of counters from the gcov file.
>>>>>> +   Otherwise, it reads from SRC array. These read values will be multiplied
>>>>>> +   by weight W before adding to the old counters.  */
>>>>>>  void
>>>>>> -__gcov_merge_add (gcov_type *counters, unsigned n_counters)
>>>>>> +__gcov_merge_add (gcov_type *counters, unsigned n_counters,
>>>>>> +                  gcov_type *src, unsigned w)
>>>>>>  {
>>>>>> +  int in_mem = (src != 0);
>>>>>> +
>>>>>>    for (; n_counters; counters++, n_counters--)
>>>>>> -    *counters += gcov_read_counter ();
>>>>>> +    {
>>>>>> +      gcov_type value;
>>>>>> +
>>>>>> +      if (in_mem)
>>>>>> +        value = *(src++);
>>>>>> +      else
>>>>>> +        value = gcov_read_counter ();
>>>>>> +
>>>>>> +      *counters += w * value;
>>>>>> +    }
>>>>>>  }
>>>>>>  #endif /* L_gcov_merge_add */
>>>>>>
>>>>>>  #ifdef L_gcov_merge_ior
>>>>>>  /* The profile merging function that just adds the counters.  It is given
>>>>>> -   an array COUNTERS of N_COUNTERS old counters and it reads the same number
>>>>>> -   of counters from the gcov file.  */
>>>>>> +   an array COUNTERS of N_COUNTERS old counters.
>>>>>> +   When SRC==NULL, it reads the same number of counters from the gcov file.
>>>>>> +   Otherwise, it reads from SRC array.  */
>>>>>>  void
>>>>>> -__gcov_merge_ior (gcov_type *counters, unsigned n_counters)
>>>>>> +__gcov_merge_ior (gcov_type *counters, unsigned n_counters,
>>>>>> +                  gcov_type *src, unsigned w __attribute__ ((unused)))
>>>>>
>>>>> So the new in-memory variants are introduced for merging tool, while libgcc use gcov_read_counter
>>>>> interface?
>>>>> Perhaps we can actually just duplicate the functions to avoid runtime to do all the scalling
>>>>> and in_mem tests it won't need?
>>>>>
>>>>> I would suggest going with libgcov.h changes and clenaups first, with interface changes next
>>>>> and the gcov-tool is probably quite obvious at the end?
>>>>> Do you think you can split the patch this way?
>>>>>
>>>>> Thanks and sorry for taking long to review. I should have more time again now.
>>>>> Honza
>>>>
>>>> The libgcov.h related changes are in the attached patch. I think it
>>>> addresses your concerns. Bootstrapped and tested on
>>>> x86-64-unknown-linux-gnu. A profiledbootstrap is in progress.
>>>>
>>>> Ok for trunk if profiledbootstrap passes?
>>>
>>> Both a profiledbootstrap and LTO profiledbootstrap pass.
>>>
>>> Teresa
>>>
>>>>
>>>> Thanks,
>>>> Teresa
>>>>
>>>>
>>>> --
>>>> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413
>>>
>>>
>>>
>>> --
>>> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413
>
>
>
> --
> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-12-16 21:51                         ` Xinliang David Li
@ 2013-12-16 22:34                           ` Jan Hubicka
  2013-12-16 22:48                             ` Xinliang David Li
  0 siblings, 1 reply; 55+ messages in thread
From: Jan Hubicka @ 2013-12-16 22:34 UTC (permalink / raw)
  To: Xinliang David Li; +Cc: Teresa Johnson, Jan Hubicka, Rong Xu, GCC Patches

> I think so -- they are private to libgcov.  Honza, what do you think?

Hmm, the purpose of gcov-io was to be low level IO library for the basic
gcov file format.  I am not sure if gcov_write_tag_length should really resist
in other file than gcov_write_tag.

I see a desire to isolate actual stdio calls so one can have replacement driver
for i.e. Linux kernel. For that reason things like gcov_seek and friends probably
should be separated, but what is reason for splitting the file handling itself?

Honza
> 
> thanks,
> 
> David
> 
> On Mon, Dec 16, 2013 at 1:17 PM, Teresa Johnson <tejohnson@google.com> wrote:
> > On Mon, Dec 16, 2013 at 12:55 PM, Xinliang David Li <davidxl@google.com> wrote:
> >> gcov_rewrite function is only needed (and defined) with IN_LIBGCOV.
> >> Should it be moved from common file gcov-io.c to libgcov.c?
> >
> > Possibly. I just looked through gcov-io.c and there are several
> > additional functions that are only defined under "#ifdef IN_LIBGCOV"
> > and only used in libgcov*c (or each other):
> >
> > gcov_write_counter
> > gcov_write_tag_length
> > gcov_write_summary
> > gcov_seek
> >
> > Should they all, plus gcov_rewrite, be moved to libgcov-driver.c?
> >
> > Teresa
> >
> >>
> >>
> >> David
> >>
> >> On Thu, Dec 12, 2013 at 12:11 PM, Teresa Johnson <tejohnson@google.com> wrote:
> >>> On Wed, Dec 11, 2013 at 10:05 PM, Teresa Johnson <tejohnson@google.com> wrote:
> >>>> On Fri, Dec 6, 2013 at 6:23 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
> >>>>>> Hi, all
> >>>>>>
> >>>>>> This is the new patch for gcov-tool (previously profile-tool).
> >>>>>>
> >>>>>> Honza: can you comment on the new merge interface? David posted some
> >>>>>> comments in an earlier email and we want to know what's your opinion.
> >>>>>>
> >>>>>> Test patch has been tested with boostrap, regresssion,
> >>>>>> profiledbootstrap and SPEC2006.
> >>>>>>
> >>>>>> Noticeable changes from the earlier version:
> >>>>>>
> >>>>>> 1. create a new file libgcov.h and move libgcov-*.h headers to libgcov.h
> >>>>>> So we can included multiple libgcov-*.c without adding new macros.
> >>>>>>
> >>>>>> 2. split libgcov.h specific code in gcvo-io.h to libcc/libgcov.h
> >>>>>> Avoid multiple-page of code under IN_LIBGCOV macro -- this
> >>>>>> improves the readability.
> >>>>>>
> >>>>>> 3. make gcov_var static, and move the definition from gcov-io.h to
> >>>>>> gcov-io.c. Also
> >>>>>>    move some static functions accessing gcov_var to gcvo-io.c
> >>>>>> Current code rely on GCOV_LINKAGE tricks to avoid multi-definition. I don't see
> >>>>>> a reason that gcov_var needs to exposed as a global.
> >>>>>>
> >>>>>> 4. expose gcov_write_strings() and gcov_sync() to gcov_tool usage
> >>>>>>
> >>>>>> 5. rename profile-tool to gcov-tool per Honza's suggestion.
> >>>>>>
> >>>>>> Thanks,
> >>>>>
> >>>>> Hi,
> >>>>> I did not read in deatil the gcov-tool source itself, but lets first make the interface changes
> >>>>> needed.
> >>>>>
> >>>>>> 2013-11-18  Rong Xu  <xur@google.com>
> >>>>>>
> >>>>>>       * gcc/gcov-io.c (gcov_var): Moved from gcov-io.h and make it static.
> >>>>>>       (gcov_position): Move from gcov-io.h
> >>>>>>       (gcov_is_error): Ditto.
> >>>>>>       (gcov_rewrite): Ditto.
> >>>>>>       * gcc/gcov-io.h: Re-factoring. Move gcov_var to gcov-io.h and
> >>>>>>         move the libgcov only part of libgcc/libgcov.h.
> >>>>>>       * libgcc/libgcov.h: New common header files for libgcov-*.h
> >>>>>>       * libgcc/Makefile.in: Add dependence to libgcov.h
> >>>>>>       * libgcc/libgcov-profiler.c: Use libgcov.h
> >>>>>>       * libgcc/libgcov-driver.c: Ditto.
> >>>>>>       * libgcc/libgcov-interface.c: Ditto.
> >>>>>>       * libgcc/libgcov-driver-system.c (allocate_filename_struct): use
> >>>>>>       xmalloc instread of malloc.
> >>>>>>       * libgcc/libgcov-merge.c (void __gcov_merge_delta): Add more
> >>>>>>       parameters to merge function.
> >>>>>>       (__gcov_merge_add): Ditto.
> >>>>>>       (__gcov_merge_ior): Ditto.
> >>>>>>       (__gcov_merge_time_profile): Ditto.
> >>>>>>       (__gcov_merge_single): Ditto.
> >>>>>>       (__gcov_merge_delta): Ditto.
> >>>>>>       * libgcc/libgcov-tool.c (void gcov_tool_set_verbose): New for
> >>>>>>       gcov-tool support.
> >>>>>>       (set_fn_ctrs): Ditto.
> >>>>>>       (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_merge): Ditto.
> >>>>>>       (find_match_gcov_inf Ditto.o):
> >>>>>>       (gcov_profile_merge): Ditto.
> >>>>>>       (__gcov_scale_add): Ditto.
> >>>>>>       (__gcov_scale_ior): Ditto.
> >>>>>>       (__gcov_scale_delta): Ditto.
> >>>>>>       (__gcov_scale_single): Ditto.
> >>>>>>       (gcov_profile_scale): Ditto.
> >>>>>>       (gcov_profile_normalize): Ditto.
> >>>>>>       (__gcov_scale2_add): Ditto.
> >>>>>>       (__gcov_scale2_ior): Ditto.
> >>>>>>       (__gcov_scale2_delta): Ditto.
> >>>>>>       (__gcov_scale2_single): Ditto.
> >>>>>>       (gcov_profile_scale2): Ditto.
> >>>>>>       * gcc/gcov-tool.c (unlink_file): Gcov-tool driver support.
> >>>>>>       (unlink_dir): Ditto.
> >>>>>>       (profile_merge): Ditto.
> >>>>>>       (print_merge_usage_message): Ditto.
> >>>>>>       (merge_usage): Ditto.
> >>>>>>       (do_merge): Ditto.
> >>>>>>       (profile_rewrite2): Ditto.
> >>>>>>       (profile_rewrite): Ditto.
> >>>>>>       (print_rewrite_usage_message): Ditto.
> >>>>>>       (rewrite_usage): Ditto.
> >>>>>>       (do_rewrite): Ditto.
> >>>>>>       (print_usage): Ditto.
> >>>>>>       (print_version): Ditto.
> >>>>>>       (process_args): Ditto.
> >>>>>>       (main): Ditto.
> >>>>>>       * gcc/Makefile.in: Build and install gcov-tool.
> >>>>>
> >>>>>> Index: gcc/gcov-io.c
> >>>>>> ===================================================================
> >>>>>> --- gcc/gcov-io.c     (revision 204895)
> >>>>>> +++ gcc/gcov-io.c     (working copy)
> >>>>>> @@ -36,6 +36,37 @@ static const gcov_unsigned_t *gcov_read_words (uns
> >>>>>>  static void gcov_allocate (unsigned);
> >>>>>>  #endif
> >>>>>>
> >>>>>> +/* Moved for gcov-io.h and make it static.  */
> >>>>>> +static struct gcov_var gcov_var;
> >>>>>
> >>>>> This is more an changelog message than a comment in source file.
> >>>>> Just describe what gcov_var is.
> >>>>
> >>>> I changed this so gcov_var is no longer static, but global as before.
> >>>>
> >>>>>
> >>>>> Do you know how the size of libgcov changed with your patch?
> >>>>> Quick check of current mainline on compiling empty main gives:
> >>>>>
> >>>>> jh@gcc10:~/trunk/build/gcc$ cat t.c
> >>>>> main()
> >>>>> {
> >>>>> }
> >>>>> jh@gcc10:~/trunk/build/gcc$ ./xgcc -B ./ -O2 -fprofile-generate -o a.out-new --static t.c
> >>>>> jh@gcc10:~/trunk/build/gcc$ gcc -O2 -fprofile-generate -o a.out-old --static t.c
> >>>>> jh@gcc10:~/trunk/build/gcc$ size a.out-old
> >>>>>    text    data     bss     dec     hex filename
> >>>>>  608141    3560   16728  628429   996cd a.out-old
> >>>>> jh@gcc10:~/trunk/build/gcc$ size a.out-new
> >>>>>    text    data     bss     dec     hex filename
> >>>>>  612621    3688   22880  639189   9c0d5 a.out-new
> >>>>>
> >>>>> Without profiling I get:
> >>>>> jh@gcc10:~/trunk/build/gcc$ size a.out-new-no
> >>>>> jh@gcc10:~/trunk/build/gcc$ size a.out-old-no
> >>>>>    text    data     bss     dec     hex filename
> >>>>>  599719    3448   12568  615735   96537 a.out-old-no
> >>>>>    text    data     bss     dec     hex filename
> >>>>>  600247    3448   12568  616263   96747 a.out-new-no
> >>>>>
> >>>>> Quite big for empty program, but mostly glibc fault, I suppose
> >>>>> (that won't be an issue for embedded platforms). But anyway
> >>>>> we increased text size overhead from 8k to 12k, BSS size
> >>>>> overhead from 4k to 10k and data by another 1k.
> >>>>>
> >>>>>    text    data     bss     dec     hex filename
> >>>>>     126       0       0     126      7e _gcov_merge_add.o (ex libgcov.a)
> >>>>>     251       0       0     251      fb _gcov_merge_single.o (ex libgcov.a)
> >>>>>     242       0       0     242      f2 _gcov_merge_delta.o (ex libgcov.a)
> >>>>>     126       0       0     126      7e _gcov_merge_ior.o (ex libgcov.a)
> >>>>>     156       0       0     156      9c _gcov_merge_time_profile.o (ex libgcov.a)
> >>>>>      89       0       0      89      59 _gcov_interval_profiler.o (ex libgcov.a)
> >>>>>      69       0       0      69      45 _gcov_pow2_profiler.o (ex libgcov.a)
> >>>>>     115       0       0     115      73 _gcov_one_value_profiler.o (ex libgcov.a)
> >>>>>     122       0       0     122      7a _gcov_indirect_call_profiler.o (ex libgcov.a)
> >>>>>      57       0       0      57      39 _gcov_average_profiler.o (ex libgcov.a)
> >>>>>      52       0       0      52      34 _gcov_ior_profiler.o (ex libgcov.a)
> >>>>>     178       0      16     194      c2 _gcov_indirect_call_profiler_v2.o (ex libgcov.a)
> >>>>>      77       0       8      85      55 _gcov_time_profiler.o (ex libgcov.a)
> >>>>>     126       0      40     166      a6 _gcov_flush.o (ex libgcov.a)
> >>>>>     101       0       0     101      65 _gcov_fork.o (ex libgcov.a)
> >>>>>     471       0       0     471     1d7 _gcov_execl.o (ex libgcov.a)
> >>>>>     471       0       0     471     1d7 _gcov_execlp.o (ex libgcov.a)
> >>>>>     524       0       0     524     20c _gcov_execle.o (ex libgcov.a)
> >>>>>      98       0       0      98      62 _gcov_execv.o (ex libgcov.a)
> >>>>>      98       0       0      98      62 _gcov_execvp.o (ex libgcov.a)
> >>>>>     108       0       0     108      6c _gcov_execve.o (ex libgcov.a)
> >>>>>      66       0       0      66      42 _gcov_reset.o (ex libgcov.a)
> >>>>>      66       0       0      66      42 _gcov_dump.o (ex libgcov.a)
> >>>>>    9505       0    6144   15649    3d21 _gcov.o (ex libgcov.a)
> >>>>>
> >>>>> I think we definitely need to move those 6k of bss space out.  I think those are new
> >>>>> static vars you introduced that I think are unsafe anyway because multiple streaming
> >>>>> may run at once in threaded program where locking mechanizm fails.
> >>>>> (it will probably do other bad things, but definitely we do not want to
> >>>>> conflict on things like filename to write into).
> >>>>>
> >>>>> Compared to my system gcov:
> >>>>>    text    data     bss     dec     hex filename
> >>>>>    9765       0      64    9829    2665 _gcov.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
> >>>>>     125       0       0     125      7d _gcov_merge_add.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
> >>>>>     251       0       0     251      fb _gcov_merge_single.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
> >>>>>     242       0       0     242      f2 _gcov_merge_delta.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
> >>>>>     101       0       0     101      65 _gcov_fork.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
> >>>>>     471       0       0     471     1d7 _gcov_execl.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
> >>>>>     471       0       0     471     1d7 _gcov_execlp.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
> >>>>>     524       0       0     524     20c _gcov_execle.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
> >>>>>      98       0       0      98      62 _gcov_execv.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
> >>>>>      98       0       0      98      62 _gcov_execvp.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
> >>>>>     108       0       0     108      6c _gcov_execve.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
> >>>>>      72       0       0      72      48 _gcov_reset.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
> >>>>>      72       0       0      72      48 _gcov_dump.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
> >>>>>      89       0       0      89      59 _gcov_interval_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
> >>>>>      69       0       0      69      45 _gcov_pow2_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
> >>>>>     115       0       0     115      73 _gcov_one_value_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
> >>>>>     122       0       0     122      7a _gcov_indirect_call_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
> >>>>>      57       0       0      57      39 _gcov_average_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
> >>>>>      52       0       0      52      34 _gcov_ior_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
> >>>>>     125       0       0     125      7d _gcov_merge_ior.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
> >>>>>
> >>>>>> Index: gcc/gcov-io.h
> >>>>>> ===================================================================
> >>>>>> --- gcc/gcov-io.h     (revision 204895)
> >>>>>> +++ gcc/gcov-io.h     (working copy)
> >>>>>> @@ -164,51 +164,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
> >>>>>>  #ifndef GCC_GCOV_IO_H
> >>>>>>  #define GCC_GCOV_IO_H
> >>>>>>
> >>>>>> -#if IN_LIBGCOV
> >>>>>> -/* About the target */
> >>>>>> -
> >>>>>> -#if BITS_PER_UNIT == 8
> >>>>>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI)));
> >>>>>> -typedef unsigned gcov_position_t __attribute__ ((mode (SI)));
> >>>>>> -#if LONG_LONG_TYPE_SIZE > 32
> >>>>>> -typedef signed gcov_type __attribute__ ((mode (DI)));
> >>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (DI)));
> >>>>>> -#else
> >>>>>> -typedef signed gcov_type __attribute__ ((mode (SI)));
> >>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
> >>>>>> -#endif
> >>>>>> -#else
> >>>>>> -#if BITS_PER_UNIT == 16
> >>>>>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (HI)));
> >>>>>> -typedef unsigned gcov_position_t __attribute__ ((mode (HI)));
> >>>>>> -#if LONG_LONG_TYPE_SIZE > 32
> >>>>>> -typedef signed gcov_type __attribute__ ((mode (SI)));
> >>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
> >>>>>> -#else
> >>>>>> -typedef signed gcov_type __attribute__ ((mode (HI)));
> >>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
> >>>>>> -#endif
> >>>>>> -#else
> >>>>>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (QI)));
> >>>>>> -typedef unsigned gcov_position_t __attribute__ ((mode (QI)));
> >>>>>> -#if LONG_LONG_TYPE_SIZE > 32
> >>>>>> -typedef signed gcov_type __attribute__ ((mode (HI)));
> >>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
> >>>>>> -#else
> >>>>>> -typedef signed gcov_type __attribute__ ((mode (QI)));
> >>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI)));
> >>>>>> -#endif
> >>>>>> -#endif
> >>>>>> -#endif
> >>>>>
> >>>>> So this part basically moves libgcov specific bits into libgcov.h? That OK fine by
> >>>>> itself.
> >>>>>> Index: libgcc/libgcov-profiler.c
> >>>>>> ===================================================================
> >>>>>> --- libgcc/libgcov-profiler.c (revision 204895)
> >>>>>> +++ libgcc/libgcov-profiler.c (working copy)
> >>>>>> @@ -23,15 +23,8 @@ a copy of the GCC Runtime Library Exception along
> >>>>>>  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
> >>>>>>  <http://www.gnu.org/licenses/>.  */
> >>>>>>
> >>>>>> -#include "tconfig.h"
> >>>>>> -#include "tsystem.h"
> >>>>>> -#include "coretypes.h"
> >>>>>> -#include "tm.h"
> >>>>>> -#include "libgcc_tm.h"
> >>>>>> -
> >>>>>> +#include "libgcov.h"
> >>>>>>  #if !defined(inhibit_libc)
> >>>>>> -#define IN_LIBGCOV 1
> >>>>>> -#include "gcov-io.h"
> >>>>>
> >>>>> I did not pay that much attention into the current include file changes, but wasn't
> >>>>> idea to avoid #include file to include random other #includes?
> >>>>> So perhaps the first block of includes should stay, followed by libgcov.h and gcov-io.h
> >>>>> last?
> >>>>
> >>>> I'm not sure I understand the issue here? The patch basically moves
> >>>> the same includes into libgcov.h, and includes that instead. I see
> >>>> many other header files in gcc that include other headers.
> >>>>
> >>>>>> @@ -325,6 +311,9 @@ static struct gcov_summary all_prg;
> >>>>>>  #endif
> >>>>>>  /* crc32 for this program.  */
> >>>>>>  static gcov_unsigned_t crc32;
> >>>>>> +/* Use this summary checksum rather the computed one if the value is
> >>>>>> + *    non-zero.  */
> >>>>>> +static gcov_unsigned_t saved_summary_checksum;
> >>>>>
> >>>>> Why do you need to save the checksum? Won't it reset summary back with multiple streaming?
> >>>>
> >>>> This has been removed.
> >>>>
> >>>>>
> >>>>> I would really like to avoid introducing those static vars that are used exclusively
> >>>>> by gcov_exit.  What about putting them into an gcov_context structure that
> >>>>> is passed around the functions that was broken out?
> >>>>>> Index: libgcc/libgcov-merge.c
> >>>>>> ===================================================================
> >>>>>> --- libgcc/libgcov-merge.c    (revision 204895)
> >>>>>> +++ libgcc/libgcov-merge.c    (working copy)
> >>>>>> @@ -23,107 +23,150 @@ a copy of the GCC Runtime Library Exception along
> >>>>>>  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
> >>>>>>  <http://www.gnu.org/licenses/>.  */
> >>>>>>
> >>>>>> -#include "tconfig.h"
> >>>>>> -#include "tsystem.h"
> >>>>>> -#include "coretypes.h"
> >>>>>> -#include "tm.h"
> >>>>>> -#include "libgcc_tm.h"
> >>>>>> +#include "libgcov.h"
> >>>>>>
> >>>>>> -#if defined(inhibit_libc)
> >>>>>> -#define IN_LIBGCOV (-1)
> >>>>>> -#else
> >>>>>> -#define IN_LIBGCOV 1
> >>>>>> +#include "gcov-io-libgcov.h"
> >>>>>>  #endif
> >>>>>>
> >>>>>> -#include "gcov-io.h"
> >>>>>> -
> >>>>>>  #if defined(inhibit_libc)
> >>>>>>  /* If libc and its header files are not available, provide dummy functions.  */
> >>>>>>
> >>>>>>  #ifdef L_gcov_merge_add
> >>>>>>  void __gcov_merge_add (gcov_type *counters  __attribute__ ((unused)),
> >>>>>> -                       unsigned n_counters __attribute__ ((unused))) {}
> >>>>>> +                       unsigned n_counters __attribute__ ((unused)),
> >>>>>> +                       unsigned gcov_type *src __attribute__ ((unused)),
> >>>>>> +                       unsigned w __attribute__ ((unused))) {}
> >>>>>>  #endif
> >>>>>>
> >>>>>>  #ifdef L_gcov_merge_single
> >>>>>>  void __gcov_merge_single (gcov_type *counters  __attribute__ ((unused)),
> >>>>>> -                          unsigned n_counters __attribute__ ((unused))) {}
> >>>>>> +                          unsigned n_counters __attribute__ ((unused)),
> >>>>>> +                          unsigned gcov_type *src __attribute__ ((unused)),
> >>>>>> +                          unsigned w __attribute__ ((unused))) {}
> >>>>>>  #endif
> >>>>>>
> >>>>>>  #ifdef L_gcov_merge_delta
> >>>>>>  void __gcov_merge_delta (gcov_type *counters  __attribute__ ((unused)),
> >>>>>> -                         unsigned n_counters __attribute__ ((unused))) {}
> >>>>>> +                         unsigned n_counters __attribute__ ((unused)),
> >>>>>> +                         unsigned gcov_type *src __attribute__ ((unused)),
> >>>>>> +                         unsigned w __attribute__ ((unused))) {}
> >>>>>>  #endif
> >>>>>>
> >>>>>>  #else
> >>>>>>
> >>>>>>  #ifdef L_gcov_merge_add
> >>>>>>  /* The profile merging function that just adds the counters.  It is given
> >>>>>> -   an array COUNTERS of N_COUNTERS old counters and it reads the same number
> >>>>>> -   of counters from the gcov file.  */
> >>>>>> +   an array COUNTERS of N_COUNTERS old counters.
> >>>>>> +   When SRC==NULL, it reads the same number of counters from the gcov file.
> >>>>>> +   Otherwise, it reads from SRC array. These read values will be multiplied
> >>>>>> +   by weight W before adding to the old counters.  */
> >>>>>>  void
> >>>>>> -__gcov_merge_add (gcov_type *counters, unsigned n_counters)
> >>>>>> +__gcov_merge_add (gcov_type *counters, unsigned n_counters,
> >>>>>> +                  gcov_type *src, unsigned w)
> >>>>>>  {
> >>>>>> +  int in_mem = (src != 0);
> >>>>>> +
> >>>>>>    for (; n_counters; counters++, n_counters--)
> >>>>>> -    *counters += gcov_read_counter ();
> >>>>>> +    {
> >>>>>> +      gcov_type value;
> >>>>>> +
> >>>>>> +      if (in_mem)
> >>>>>> +        value = *(src++);
> >>>>>> +      else
> >>>>>> +        value = gcov_read_counter ();
> >>>>>> +
> >>>>>> +      *counters += w * value;
> >>>>>> +    }
> >>>>>>  }
> >>>>>>  #endif /* L_gcov_merge_add */
> >>>>>>
> >>>>>>  #ifdef L_gcov_merge_ior
> >>>>>>  /* The profile merging function that just adds the counters.  It is given
> >>>>>> -   an array COUNTERS of N_COUNTERS old counters and it reads the same number
> >>>>>> -   of counters from the gcov file.  */
> >>>>>> +   an array COUNTERS of N_COUNTERS old counters.
> >>>>>> +   When SRC==NULL, it reads the same number of counters from the gcov file.
> >>>>>> +   Otherwise, it reads from SRC array.  */
> >>>>>>  void
> >>>>>> -__gcov_merge_ior (gcov_type *counters, unsigned n_counters)
> >>>>>> +__gcov_merge_ior (gcov_type *counters, unsigned n_counters,
> >>>>>> +                  gcov_type *src, unsigned w __attribute__ ((unused)))
> >>>>>
> >>>>> So the new in-memory variants are introduced for merging tool, while libgcc use gcov_read_counter
> >>>>> interface?
> >>>>> Perhaps we can actually just duplicate the functions to avoid runtime to do all the scalling
> >>>>> and in_mem tests it won't need?
> >>>>>
> >>>>> I would suggest going with libgcov.h changes and clenaups first, with interface changes next
> >>>>> and the gcov-tool is probably quite obvious at the end?
> >>>>> Do you think you can split the patch this way?
> >>>>>
> >>>>> Thanks and sorry for taking long to review. I should have more time again now.
> >>>>> Honza
> >>>>
> >>>> The libgcov.h related changes are in the attached patch. I think it
> >>>> addresses your concerns. Bootstrapped and tested on
> >>>> x86-64-unknown-linux-gnu. A profiledbootstrap is in progress.
> >>>>
> >>>> Ok for trunk if profiledbootstrap passes?
> >>>
> >>> Both a profiledbootstrap and LTO profiledbootstrap pass.
> >>>
> >>> Teresa
> >>>
> >>>>
> >>>> Thanks,
> >>>> Teresa
> >>>>
> >>>>
> >>>> --
> >>>> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413
> >>>
> >>>
> >>>
> >>> --
> >>> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413
> >
> >
> >
> > --
> > Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-12-16 22:34                           ` Jan Hubicka
@ 2013-12-16 22:48                             ` Xinliang David Li
  2013-12-17 15:48                               ` Teresa Johnson
  0 siblings, 1 reply; 55+ messages in thread
From: Xinliang David Li @ 2013-12-16 22:48 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Teresa Johnson, Rong Xu, GCC Patches

Ok -- gcov_write_counter and gcov_write_tag_length are qualified as
low level primitives for basic gcov format and probably should be kept
in gcov-io.c.

gcov_rewrite is petty much libgcov runtime implementation details so I
think it should be moved out. gcov_write_summary is not related to
gcov low level format either, neither is gcov_seek.  Ok for them to be
moved?

thanks,

David


On Mon, Dec 16, 2013 at 2:34 PM, Jan Hubicka <hubicka@ucw.cz> wrote:
>> I think so -- they are private to libgcov.  Honza, what do you think?
>
> Hmm, the purpose of gcov-io was to be low level IO library for the basic
> gcov file format.  I am not sure if gcov_write_tag_length should really resist
> in other file than gcov_write_tag.
>
> I see a desire to isolate actual stdio calls so one can have replacement driver
> for i.e. Linux kernel. For that reason things like gcov_seek and friends probably
> should be separated, but what is reason for splitting the file handling itself?
>
> Honza
>>
>> thanks,
>>
>> David
>>
>> On Mon, Dec 16, 2013 at 1:17 PM, Teresa Johnson <tejohnson@google.com> wrote:
>> > On Mon, Dec 16, 2013 at 12:55 PM, Xinliang David Li <davidxl@google.com> wrote:
>> >> gcov_rewrite function is only needed (and defined) with IN_LIBGCOV.
>> >> Should it be moved from common file gcov-io.c to libgcov.c?
>> >
>> > Possibly. I just looked through gcov-io.c and there are several
>> > additional functions that are only defined under "#ifdef IN_LIBGCOV"
>> > and only used in libgcov*c (or each other):
>> >
>> > gcov_write_counter
>> > gcov_write_tag_length
>> > gcov_write_summary
>> > gcov_seek
>> >
>> > Should they all, plus gcov_rewrite, be moved to libgcov-driver.c?
>> >
>> > Teresa
>> >
>> >>
>> >>
>> >> David
>> >>
>> >> On Thu, Dec 12, 2013 at 12:11 PM, Teresa Johnson <tejohnson@google.com> wrote:
>> >>> On Wed, Dec 11, 2013 at 10:05 PM, Teresa Johnson <tejohnson@google.com> wrote:
>> >>>> On Fri, Dec 6, 2013 at 6:23 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>> >>>>>> Hi, all
>> >>>>>>
>> >>>>>> This is the new patch for gcov-tool (previously profile-tool).
>> >>>>>>
>> >>>>>> Honza: can you comment on the new merge interface? David posted some
>> >>>>>> comments in an earlier email and we want to know what's your opinion.
>> >>>>>>
>> >>>>>> Test patch has been tested with boostrap, regresssion,
>> >>>>>> profiledbootstrap and SPEC2006.
>> >>>>>>
>> >>>>>> Noticeable changes from the earlier version:
>> >>>>>>
>> >>>>>> 1. create a new file libgcov.h and move libgcov-*.h headers to libgcov.h
>> >>>>>> So we can included multiple libgcov-*.c without adding new macros.
>> >>>>>>
>> >>>>>> 2. split libgcov.h specific code in gcvo-io.h to libcc/libgcov.h
>> >>>>>> Avoid multiple-page of code under IN_LIBGCOV macro -- this
>> >>>>>> improves the readability.
>> >>>>>>
>> >>>>>> 3. make gcov_var static, and move the definition from gcov-io.h to
>> >>>>>> gcov-io.c. Also
>> >>>>>>    move some static functions accessing gcov_var to gcvo-io.c
>> >>>>>> Current code rely on GCOV_LINKAGE tricks to avoid multi-definition. I don't see
>> >>>>>> a reason that gcov_var needs to exposed as a global.
>> >>>>>>
>> >>>>>> 4. expose gcov_write_strings() and gcov_sync() to gcov_tool usage
>> >>>>>>
>> >>>>>> 5. rename profile-tool to gcov-tool per Honza's suggestion.
>> >>>>>>
>> >>>>>> Thanks,
>> >>>>>
>> >>>>> Hi,
>> >>>>> I did not read in deatil the gcov-tool source itself, but lets first make the interface changes
>> >>>>> needed.
>> >>>>>
>> >>>>>> 2013-11-18  Rong Xu  <xur@google.com>
>> >>>>>>
>> >>>>>>       * gcc/gcov-io.c (gcov_var): Moved from gcov-io.h and make it static.
>> >>>>>>       (gcov_position): Move from gcov-io.h
>> >>>>>>       (gcov_is_error): Ditto.
>> >>>>>>       (gcov_rewrite): Ditto.
>> >>>>>>       * gcc/gcov-io.h: Re-factoring. Move gcov_var to gcov-io.h and
>> >>>>>>         move the libgcov only part of libgcc/libgcov.h.
>> >>>>>>       * libgcc/libgcov.h: New common header files for libgcov-*.h
>> >>>>>>       * libgcc/Makefile.in: Add dependence to libgcov.h
>> >>>>>>       * libgcc/libgcov-profiler.c: Use libgcov.h
>> >>>>>>       * libgcc/libgcov-driver.c: Ditto.
>> >>>>>>       * libgcc/libgcov-interface.c: Ditto.
>> >>>>>>       * libgcc/libgcov-driver-system.c (allocate_filename_struct): use
>> >>>>>>       xmalloc instread of malloc.
>> >>>>>>       * libgcc/libgcov-merge.c (void __gcov_merge_delta): Add more
>> >>>>>>       parameters to merge function.
>> >>>>>>       (__gcov_merge_add): Ditto.
>> >>>>>>       (__gcov_merge_ior): Ditto.
>> >>>>>>       (__gcov_merge_time_profile): Ditto.
>> >>>>>>       (__gcov_merge_single): Ditto.
>> >>>>>>       (__gcov_merge_delta): Ditto.
>> >>>>>>       * libgcc/libgcov-tool.c (void gcov_tool_set_verbose): New for
>> >>>>>>       gcov-tool support.
>> >>>>>>       (set_fn_ctrs): Ditto.
>> >>>>>>       (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_merge): Ditto.
>> >>>>>>       (find_match_gcov_inf Ditto.o):
>> >>>>>>       (gcov_profile_merge): Ditto.
>> >>>>>>       (__gcov_scale_add): Ditto.
>> >>>>>>       (__gcov_scale_ior): Ditto.
>> >>>>>>       (__gcov_scale_delta): Ditto.
>> >>>>>>       (__gcov_scale_single): Ditto.
>> >>>>>>       (gcov_profile_scale): Ditto.
>> >>>>>>       (gcov_profile_normalize): Ditto.
>> >>>>>>       (__gcov_scale2_add): Ditto.
>> >>>>>>       (__gcov_scale2_ior): Ditto.
>> >>>>>>       (__gcov_scale2_delta): Ditto.
>> >>>>>>       (__gcov_scale2_single): Ditto.
>> >>>>>>       (gcov_profile_scale2): Ditto.
>> >>>>>>       * gcc/gcov-tool.c (unlink_file): Gcov-tool driver support.
>> >>>>>>       (unlink_dir): Ditto.
>> >>>>>>       (profile_merge): Ditto.
>> >>>>>>       (print_merge_usage_message): Ditto.
>> >>>>>>       (merge_usage): Ditto.
>> >>>>>>       (do_merge): Ditto.
>> >>>>>>       (profile_rewrite2): Ditto.
>> >>>>>>       (profile_rewrite): Ditto.
>> >>>>>>       (print_rewrite_usage_message): Ditto.
>> >>>>>>       (rewrite_usage): Ditto.
>> >>>>>>       (do_rewrite): Ditto.
>> >>>>>>       (print_usage): Ditto.
>> >>>>>>       (print_version): Ditto.
>> >>>>>>       (process_args): Ditto.
>> >>>>>>       (main): Ditto.
>> >>>>>>       * gcc/Makefile.in: Build and install gcov-tool.
>> >>>>>
>> >>>>>> Index: gcc/gcov-io.c
>> >>>>>> ===================================================================
>> >>>>>> --- gcc/gcov-io.c     (revision 204895)
>> >>>>>> +++ gcc/gcov-io.c     (working copy)
>> >>>>>> @@ -36,6 +36,37 @@ static const gcov_unsigned_t *gcov_read_words (uns
>> >>>>>>  static void gcov_allocate (unsigned);
>> >>>>>>  #endif
>> >>>>>>
>> >>>>>> +/* Moved for gcov-io.h and make it static.  */
>> >>>>>> +static struct gcov_var gcov_var;
>> >>>>>
>> >>>>> This is more an changelog message than a comment in source file.
>> >>>>> Just describe what gcov_var is.
>> >>>>
>> >>>> I changed this so gcov_var is no longer static, but global as before.
>> >>>>
>> >>>>>
>> >>>>> Do you know how the size of libgcov changed with your patch?
>> >>>>> Quick check of current mainline on compiling empty main gives:
>> >>>>>
>> >>>>> jh@gcc10:~/trunk/build/gcc$ cat t.c
>> >>>>> main()
>> >>>>> {
>> >>>>> }
>> >>>>> jh@gcc10:~/trunk/build/gcc$ ./xgcc -B ./ -O2 -fprofile-generate -o a.out-new --static t.c
>> >>>>> jh@gcc10:~/trunk/build/gcc$ gcc -O2 -fprofile-generate -o a.out-old --static t.c
>> >>>>> jh@gcc10:~/trunk/build/gcc$ size a.out-old
>> >>>>>    text    data     bss     dec     hex filename
>> >>>>>  608141    3560   16728  628429   996cd a.out-old
>> >>>>> jh@gcc10:~/trunk/build/gcc$ size a.out-new
>> >>>>>    text    data     bss     dec     hex filename
>> >>>>>  612621    3688   22880  639189   9c0d5 a.out-new
>> >>>>>
>> >>>>> Without profiling I get:
>> >>>>> jh@gcc10:~/trunk/build/gcc$ size a.out-new-no
>> >>>>> jh@gcc10:~/trunk/build/gcc$ size a.out-old-no
>> >>>>>    text    data     bss     dec     hex filename
>> >>>>>  599719    3448   12568  615735   96537 a.out-old-no
>> >>>>>    text    data     bss     dec     hex filename
>> >>>>>  600247    3448   12568  616263   96747 a.out-new-no
>> >>>>>
>> >>>>> Quite big for empty program, but mostly glibc fault, I suppose
>> >>>>> (that won't be an issue for embedded platforms). But anyway
>> >>>>> we increased text size overhead from 8k to 12k, BSS size
>> >>>>> overhead from 4k to 10k and data by another 1k.
>> >>>>>
>> >>>>>    text    data     bss     dec     hex filename
>> >>>>>     126       0       0     126      7e _gcov_merge_add.o (ex libgcov.a)
>> >>>>>     251       0       0     251      fb _gcov_merge_single.o (ex libgcov.a)
>> >>>>>     242       0       0     242      f2 _gcov_merge_delta.o (ex libgcov.a)
>> >>>>>     126       0       0     126      7e _gcov_merge_ior.o (ex libgcov.a)
>> >>>>>     156       0       0     156      9c _gcov_merge_time_profile.o (ex libgcov.a)
>> >>>>>      89       0       0      89      59 _gcov_interval_profiler.o (ex libgcov.a)
>> >>>>>      69       0       0      69      45 _gcov_pow2_profiler.o (ex libgcov.a)
>> >>>>>     115       0       0     115      73 _gcov_one_value_profiler.o (ex libgcov.a)
>> >>>>>     122       0       0     122      7a _gcov_indirect_call_profiler.o (ex libgcov.a)
>> >>>>>      57       0       0      57      39 _gcov_average_profiler.o (ex libgcov.a)
>> >>>>>      52       0       0      52      34 _gcov_ior_profiler.o (ex libgcov.a)
>> >>>>>     178       0      16     194      c2 _gcov_indirect_call_profiler_v2.o (ex libgcov.a)
>> >>>>>      77       0       8      85      55 _gcov_time_profiler.o (ex libgcov.a)
>> >>>>>     126       0      40     166      a6 _gcov_flush.o (ex libgcov.a)
>> >>>>>     101       0       0     101      65 _gcov_fork.o (ex libgcov.a)
>> >>>>>     471       0       0     471     1d7 _gcov_execl.o (ex libgcov.a)
>> >>>>>     471       0       0     471     1d7 _gcov_execlp.o (ex libgcov.a)
>> >>>>>     524       0       0     524     20c _gcov_execle.o (ex libgcov.a)
>> >>>>>      98       0       0      98      62 _gcov_execv.o (ex libgcov.a)
>> >>>>>      98       0       0      98      62 _gcov_execvp.o (ex libgcov.a)
>> >>>>>     108       0       0     108      6c _gcov_execve.o (ex libgcov.a)
>> >>>>>      66       0       0      66      42 _gcov_reset.o (ex libgcov.a)
>> >>>>>      66       0       0      66      42 _gcov_dump.o (ex libgcov.a)
>> >>>>>    9505       0    6144   15649    3d21 _gcov.o (ex libgcov.a)
>> >>>>>
>> >>>>> I think we definitely need to move those 6k of bss space out.  I think those are new
>> >>>>> static vars you introduced that I think are unsafe anyway because multiple streaming
>> >>>>> may run at once in threaded program where locking mechanizm fails.
>> >>>>> (it will probably do other bad things, but definitely we do not want to
>> >>>>> conflict on things like filename to write into).
>> >>>>>
>> >>>>> Compared to my system gcov:
>> >>>>>    text    data     bss     dec     hex filename
>> >>>>>    9765       0      64    9829    2665 _gcov.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>> >>>>>     125       0       0     125      7d _gcov_merge_add.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>> >>>>>     251       0       0     251      fb _gcov_merge_single.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>> >>>>>     242       0       0     242      f2 _gcov_merge_delta.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>> >>>>>     101       0       0     101      65 _gcov_fork.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>> >>>>>     471       0       0     471     1d7 _gcov_execl.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>> >>>>>     471       0       0     471     1d7 _gcov_execlp.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>> >>>>>     524       0       0     524     20c _gcov_execle.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>> >>>>>      98       0       0      98      62 _gcov_execv.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>> >>>>>      98       0       0      98      62 _gcov_execvp.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>> >>>>>     108       0       0     108      6c _gcov_execve.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>> >>>>>      72       0       0      72      48 _gcov_reset.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>> >>>>>      72       0       0      72      48 _gcov_dump.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>> >>>>>      89       0       0      89      59 _gcov_interval_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>> >>>>>      69       0       0      69      45 _gcov_pow2_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>> >>>>>     115       0       0     115      73 _gcov_one_value_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>> >>>>>     122       0       0     122      7a _gcov_indirect_call_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>> >>>>>      57       0       0      57      39 _gcov_average_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>> >>>>>      52       0       0      52      34 _gcov_ior_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>> >>>>>     125       0       0     125      7d _gcov_merge_ior.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>> >>>>>
>> >>>>>> Index: gcc/gcov-io.h
>> >>>>>> ===================================================================
>> >>>>>> --- gcc/gcov-io.h     (revision 204895)
>> >>>>>> +++ gcc/gcov-io.h     (working copy)
>> >>>>>> @@ -164,51 +164,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
>> >>>>>>  #ifndef GCC_GCOV_IO_H
>> >>>>>>  #define GCC_GCOV_IO_H
>> >>>>>>
>> >>>>>> -#if IN_LIBGCOV
>> >>>>>> -/* About the target */
>> >>>>>> -
>> >>>>>> -#if BITS_PER_UNIT == 8
>> >>>>>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI)));
>> >>>>>> -typedef unsigned gcov_position_t __attribute__ ((mode (SI)));
>> >>>>>> -#if LONG_LONG_TYPE_SIZE > 32
>> >>>>>> -typedef signed gcov_type __attribute__ ((mode (DI)));
>> >>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (DI)));
>> >>>>>> -#else
>> >>>>>> -typedef signed gcov_type __attribute__ ((mode (SI)));
>> >>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
>> >>>>>> -#endif
>> >>>>>> -#else
>> >>>>>> -#if BITS_PER_UNIT == 16
>> >>>>>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (HI)));
>> >>>>>> -typedef unsigned gcov_position_t __attribute__ ((mode (HI)));
>> >>>>>> -#if LONG_LONG_TYPE_SIZE > 32
>> >>>>>> -typedef signed gcov_type __attribute__ ((mode (SI)));
>> >>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
>> >>>>>> -#else
>> >>>>>> -typedef signed gcov_type __attribute__ ((mode (HI)));
>> >>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
>> >>>>>> -#endif
>> >>>>>> -#else
>> >>>>>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (QI)));
>> >>>>>> -typedef unsigned gcov_position_t __attribute__ ((mode (QI)));
>> >>>>>> -#if LONG_LONG_TYPE_SIZE > 32
>> >>>>>> -typedef signed gcov_type __attribute__ ((mode (HI)));
>> >>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
>> >>>>>> -#else
>> >>>>>> -typedef signed gcov_type __attribute__ ((mode (QI)));
>> >>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI)));
>> >>>>>> -#endif
>> >>>>>> -#endif
>> >>>>>> -#endif
>> >>>>>
>> >>>>> So this part basically moves libgcov specific bits into libgcov.h? That OK fine by
>> >>>>> itself.
>> >>>>>> Index: libgcc/libgcov-profiler.c
>> >>>>>> ===================================================================
>> >>>>>> --- libgcc/libgcov-profiler.c (revision 204895)
>> >>>>>> +++ libgcc/libgcov-profiler.c (working copy)
>> >>>>>> @@ -23,15 +23,8 @@ a copy of the GCC Runtime Library Exception along
>> >>>>>>  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>> >>>>>>  <http://www.gnu.org/licenses/>.  */
>> >>>>>>
>> >>>>>> -#include "tconfig.h"
>> >>>>>> -#include "tsystem.h"
>> >>>>>> -#include "coretypes.h"
>> >>>>>> -#include "tm.h"
>> >>>>>> -#include "libgcc_tm.h"
>> >>>>>> -
>> >>>>>> +#include "libgcov.h"
>> >>>>>>  #if !defined(inhibit_libc)
>> >>>>>> -#define IN_LIBGCOV 1
>> >>>>>> -#include "gcov-io.h"
>> >>>>>
>> >>>>> I did not pay that much attention into the current include file changes, but wasn't
>> >>>>> idea to avoid #include file to include random other #includes?
>> >>>>> So perhaps the first block of includes should stay, followed by libgcov.h and gcov-io.h
>> >>>>> last?
>> >>>>
>> >>>> I'm not sure I understand the issue here? The patch basically moves
>> >>>> the same includes into libgcov.h, and includes that instead. I see
>> >>>> many other header files in gcc that include other headers.
>> >>>>
>> >>>>>> @@ -325,6 +311,9 @@ static struct gcov_summary all_prg;
>> >>>>>>  #endif
>> >>>>>>  /* crc32 for this program.  */
>> >>>>>>  static gcov_unsigned_t crc32;
>> >>>>>> +/* Use this summary checksum rather the computed one if the value is
>> >>>>>> + *    non-zero.  */
>> >>>>>> +static gcov_unsigned_t saved_summary_checksum;
>> >>>>>
>> >>>>> Why do you need to save the checksum? Won't it reset summary back with multiple streaming?
>> >>>>
>> >>>> This has been removed.
>> >>>>
>> >>>>>
>> >>>>> I would really like to avoid introducing those static vars that are used exclusively
>> >>>>> by gcov_exit.  What about putting them into an gcov_context structure that
>> >>>>> is passed around the functions that was broken out?
>> >>>>>> Index: libgcc/libgcov-merge.c
>> >>>>>> ===================================================================
>> >>>>>> --- libgcc/libgcov-merge.c    (revision 204895)
>> >>>>>> +++ libgcc/libgcov-merge.c    (working copy)
>> >>>>>> @@ -23,107 +23,150 @@ a copy of the GCC Runtime Library Exception along
>> >>>>>>  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>> >>>>>>  <http://www.gnu.org/licenses/>.  */
>> >>>>>>
>> >>>>>> -#include "tconfig.h"
>> >>>>>> -#include "tsystem.h"
>> >>>>>> -#include "coretypes.h"
>> >>>>>> -#include "tm.h"
>> >>>>>> -#include "libgcc_tm.h"
>> >>>>>> +#include "libgcov.h"
>> >>>>>>
>> >>>>>> -#if defined(inhibit_libc)
>> >>>>>> -#define IN_LIBGCOV (-1)
>> >>>>>> -#else
>> >>>>>> -#define IN_LIBGCOV 1
>> >>>>>> +#include "gcov-io-libgcov.h"
>> >>>>>>  #endif
>> >>>>>>
>> >>>>>> -#include "gcov-io.h"
>> >>>>>> -
>> >>>>>>  #if defined(inhibit_libc)
>> >>>>>>  /* If libc and its header files are not available, provide dummy functions.  */
>> >>>>>>
>> >>>>>>  #ifdef L_gcov_merge_add
>> >>>>>>  void __gcov_merge_add (gcov_type *counters  __attribute__ ((unused)),
>> >>>>>> -                       unsigned n_counters __attribute__ ((unused))) {}
>> >>>>>> +                       unsigned n_counters __attribute__ ((unused)),
>> >>>>>> +                       unsigned gcov_type *src __attribute__ ((unused)),
>> >>>>>> +                       unsigned w __attribute__ ((unused))) {}
>> >>>>>>  #endif
>> >>>>>>
>> >>>>>>  #ifdef L_gcov_merge_single
>> >>>>>>  void __gcov_merge_single (gcov_type *counters  __attribute__ ((unused)),
>> >>>>>> -                          unsigned n_counters __attribute__ ((unused))) {}
>> >>>>>> +                          unsigned n_counters __attribute__ ((unused)),
>> >>>>>> +                          unsigned gcov_type *src __attribute__ ((unused)),
>> >>>>>> +                          unsigned w __attribute__ ((unused))) {}
>> >>>>>>  #endif
>> >>>>>>
>> >>>>>>  #ifdef L_gcov_merge_delta
>> >>>>>>  void __gcov_merge_delta (gcov_type *counters  __attribute__ ((unused)),
>> >>>>>> -                         unsigned n_counters __attribute__ ((unused))) {}
>> >>>>>> +                         unsigned n_counters __attribute__ ((unused)),
>> >>>>>> +                         unsigned gcov_type *src __attribute__ ((unused)),
>> >>>>>> +                         unsigned w __attribute__ ((unused))) {}
>> >>>>>>  #endif
>> >>>>>>
>> >>>>>>  #else
>> >>>>>>
>> >>>>>>  #ifdef L_gcov_merge_add
>> >>>>>>  /* The profile merging function that just adds the counters.  It is given
>> >>>>>> -   an array COUNTERS of N_COUNTERS old counters and it reads the same number
>> >>>>>> -   of counters from the gcov file.  */
>> >>>>>> +   an array COUNTERS of N_COUNTERS old counters.
>> >>>>>> +   When SRC==NULL, it reads the same number of counters from the gcov file.
>> >>>>>> +   Otherwise, it reads from SRC array. These read values will be multiplied
>> >>>>>> +   by weight W before adding to the old counters.  */
>> >>>>>>  void
>> >>>>>> -__gcov_merge_add (gcov_type *counters, unsigned n_counters)
>> >>>>>> +__gcov_merge_add (gcov_type *counters, unsigned n_counters,
>> >>>>>> +                  gcov_type *src, unsigned w)
>> >>>>>>  {
>> >>>>>> +  int in_mem = (src != 0);
>> >>>>>> +
>> >>>>>>    for (; n_counters; counters++, n_counters--)
>> >>>>>> -    *counters += gcov_read_counter ();
>> >>>>>> +    {
>> >>>>>> +      gcov_type value;
>> >>>>>> +
>> >>>>>> +      if (in_mem)
>> >>>>>> +        value = *(src++);
>> >>>>>> +      else
>> >>>>>> +        value = gcov_read_counter ();
>> >>>>>> +
>> >>>>>> +      *counters += w * value;
>> >>>>>> +    }
>> >>>>>>  }
>> >>>>>>  #endif /* L_gcov_merge_add */
>> >>>>>>
>> >>>>>>  #ifdef L_gcov_merge_ior
>> >>>>>>  /* The profile merging function that just adds the counters.  It is given
>> >>>>>> -   an array COUNTERS of N_COUNTERS old counters and it reads the same number
>> >>>>>> -   of counters from the gcov file.  */
>> >>>>>> +   an array COUNTERS of N_COUNTERS old counters.
>> >>>>>> +   When SRC==NULL, it reads the same number of counters from the gcov file.
>> >>>>>> +   Otherwise, it reads from SRC array.  */
>> >>>>>>  void
>> >>>>>> -__gcov_merge_ior (gcov_type *counters, unsigned n_counters)
>> >>>>>> +__gcov_merge_ior (gcov_type *counters, unsigned n_counters,
>> >>>>>> +                  gcov_type *src, unsigned w __attribute__ ((unused)))
>> >>>>>
>> >>>>> So the new in-memory variants are introduced for merging tool, while libgcc use gcov_read_counter
>> >>>>> interface?
>> >>>>> Perhaps we can actually just duplicate the functions to avoid runtime to do all the scalling
>> >>>>> and in_mem tests it won't need?
>> >>>>>
>> >>>>> I would suggest going with libgcov.h changes and clenaups first, with interface changes next
>> >>>>> and the gcov-tool is probably quite obvious at the end?
>> >>>>> Do you think you can split the patch this way?
>> >>>>>
>> >>>>> Thanks and sorry for taking long to review. I should have more time again now.
>> >>>>> Honza
>> >>>>
>> >>>> The libgcov.h related changes are in the attached patch. I think it
>> >>>> addresses your concerns. Bootstrapped and tested on
>> >>>> x86-64-unknown-linux-gnu. A profiledbootstrap is in progress.
>> >>>>
>> >>>> Ok for trunk if profiledbootstrap passes?
>> >>>
>> >>> Both a profiledbootstrap and LTO profiledbootstrap pass.
>> >>>
>> >>> Teresa
>> >>>
>> >>>>
>> >>>> Thanks,
>> >>>> Teresa
>> >>>>
>> >>>>
>> >>>> --
>> >>>> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413
>> >>>
>> >>>
>> >>>
>> >>> --
>> >>> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413
>> >
>> >
>> >
>> > --
>> > Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-12-16 22:48                             ` Xinliang David Li
@ 2013-12-17 15:48                               ` Teresa Johnson
  2013-12-17 17:58                                 ` Xinliang David Li
  0 siblings, 1 reply; 55+ messages in thread
From: Teresa Johnson @ 2013-12-17 15:48 UTC (permalink / raw)
  To: Xinliang David Li; +Cc: Jan Hubicka, Rong Xu, GCC Patches

On Mon, Dec 16, 2013 at 2:48 PM, Xinliang David Li <davidxl@google.com> wrote:
> Ok -- gcov_write_counter and gcov_write_tag_length are qualified as
> low level primitives for basic gcov format and probably should be kept
> in gcov-io.c.
>
> gcov_rewrite is petty much libgcov runtime implementation details so I
> think it should be moved out. gcov_write_summary is not related to
> gcov low level format either, neither is gcov_seek.  Ok for them to be
> moved?

After looking at these some more, with the idea that gcov-io.c should
encapsulate the lower level IO routines, then I think all of these
(including gcov_rewrite) should remain in gcov-io.c. I think
gcov_write_summary belongs there since all of the other gcov_write_*
are there. And gcov_seek and gcov_rewrite are both adjusting gcov_var
fields to affect the file IO operations. And there are currently no
references to gcov_var within libgcc/libgcov* files.

So I think we should leave the patch as-is. Honza, is the current
patch ok for trunk?

Thanks,
Teresa

>
> thanks,
>
> David
>
>
> On Mon, Dec 16, 2013 at 2:34 PM, Jan Hubicka <hubicka@ucw.cz> wrote:
>>> I think so -- they are private to libgcov.  Honza, what do you think?
>>
>> Hmm, the purpose of gcov-io was to be low level IO library for the basic
>> gcov file format.  I am not sure if gcov_write_tag_length should really resist
>> in other file than gcov_write_tag.
>>
>> I see a desire to isolate actual stdio calls so one can have replacement driver
>> for i.e. Linux kernel. For that reason things like gcov_seek and friends probably
>> should be separated, but what is reason for splitting the file handling itself?
>>
>> Honza
>>>
>>> thanks,
>>>
>>> David
>>>
>>> On Mon, Dec 16, 2013 at 1:17 PM, Teresa Johnson <tejohnson@google.com> wrote:
>>> > On Mon, Dec 16, 2013 at 12:55 PM, Xinliang David Li <davidxl@google.com> wrote:
>>> >> gcov_rewrite function is only needed (and defined) with IN_LIBGCOV.
>>> >> Should it be moved from common file gcov-io.c to libgcov.c?
>>> >
>>> > Possibly. I just looked through gcov-io.c and there are several
>>> > additional functions that are only defined under "#ifdef IN_LIBGCOV"
>>> > and only used in libgcov*c (or each other):
>>> >
>>> > gcov_write_counter
>>> > gcov_write_tag_length
>>> > gcov_write_summary
>>> > gcov_seek
>>> >
>>> > Should they all, plus gcov_rewrite, be moved to libgcov-driver.c?
>>> >
>>> > Teresa
>>> >
>>> >>
>>> >>
>>> >> David
>>> >>
>>> >> On Thu, Dec 12, 2013 at 12:11 PM, Teresa Johnson <tejohnson@google.com> wrote:
>>> >>> On Wed, Dec 11, 2013 at 10:05 PM, Teresa Johnson <tejohnson@google.com> wrote:
>>> >>>> On Fri, Dec 6, 2013 at 6:23 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>>> >>>>>> Hi, all
>>> >>>>>>
>>> >>>>>> This is the new patch for gcov-tool (previously profile-tool).
>>> >>>>>>
>>> >>>>>> Honza: can you comment on the new merge interface? David posted some
>>> >>>>>> comments in an earlier email and we want to know what's your opinion.
>>> >>>>>>
>>> >>>>>> Test patch has been tested with boostrap, regresssion,
>>> >>>>>> profiledbootstrap and SPEC2006.
>>> >>>>>>
>>> >>>>>> Noticeable changes from the earlier version:
>>> >>>>>>
>>> >>>>>> 1. create a new file libgcov.h and move libgcov-*.h headers to libgcov.h
>>> >>>>>> So we can included multiple libgcov-*.c without adding new macros.
>>> >>>>>>
>>> >>>>>> 2. split libgcov.h specific code in gcvo-io.h to libcc/libgcov.h
>>> >>>>>> Avoid multiple-page of code under IN_LIBGCOV macro -- this
>>> >>>>>> improves the readability.
>>> >>>>>>
>>> >>>>>> 3. make gcov_var static, and move the definition from gcov-io.h to
>>> >>>>>> gcov-io.c. Also
>>> >>>>>>    move some static functions accessing gcov_var to gcvo-io.c
>>> >>>>>> Current code rely on GCOV_LINKAGE tricks to avoid multi-definition. I don't see
>>> >>>>>> a reason that gcov_var needs to exposed as a global.
>>> >>>>>>
>>> >>>>>> 4. expose gcov_write_strings() and gcov_sync() to gcov_tool usage
>>> >>>>>>
>>> >>>>>> 5. rename profile-tool to gcov-tool per Honza's suggestion.
>>> >>>>>>
>>> >>>>>> Thanks,
>>> >>>>>
>>> >>>>> Hi,
>>> >>>>> I did not read in deatil the gcov-tool source itself, but lets first make the interface changes
>>> >>>>> needed.
>>> >>>>>
>>> >>>>>> 2013-11-18  Rong Xu  <xur@google.com>
>>> >>>>>>
>>> >>>>>>       * gcc/gcov-io.c (gcov_var): Moved from gcov-io.h and make it static.
>>> >>>>>>       (gcov_position): Move from gcov-io.h
>>> >>>>>>       (gcov_is_error): Ditto.
>>> >>>>>>       (gcov_rewrite): Ditto.
>>> >>>>>>       * gcc/gcov-io.h: Re-factoring. Move gcov_var to gcov-io.h and
>>> >>>>>>         move the libgcov only part of libgcc/libgcov.h.
>>> >>>>>>       * libgcc/libgcov.h: New common header files for libgcov-*.h
>>> >>>>>>       * libgcc/Makefile.in: Add dependence to libgcov.h
>>> >>>>>>       * libgcc/libgcov-profiler.c: Use libgcov.h
>>> >>>>>>       * libgcc/libgcov-driver.c: Ditto.
>>> >>>>>>       * libgcc/libgcov-interface.c: Ditto.
>>> >>>>>>       * libgcc/libgcov-driver-system.c (allocate_filename_struct): use
>>> >>>>>>       xmalloc instread of malloc.
>>> >>>>>>       * libgcc/libgcov-merge.c (void __gcov_merge_delta): Add more
>>> >>>>>>       parameters to merge function.
>>> >>>>>>       (__gcov_merge_add): Ditto.
>>> >>>>>>       (__gcov_merge_ior): Ditto.
>>> >>>>>>       (__gcov_merge_time_profile): Ditto.
>>> >>>>>>       (__gcov_merge_single): Ditto.
>>> >>>>>>       (__gcov_merge_delta): Ditto.
>>> >>>>>>       * libgcc/libgcov-tool.c (void gcov_tool_set_verbose): New for
>>> >>>>>>       gcov-tool support.
>>> >>>>>>       (set_fn_ctrs): Ditto.
>>> >>>>>>       (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_merge): Ditto.
>>> >>>>>>       (find_match_gcov_inf Ditto.o):
>>> >>>>>>       (gcov_profile_merge): Ditto.
>>> >>>>>>       (__gcov_scale_add): Ditto.
>>> >>>>>>       (__gcov_scale_ior): Ditto.
>>> >>>>>>       (__gcov_scale_delta): Ditto.
>>> >>>>>>       (__gcov_scale_single): Ditto.
>>> >>>>>>       (gcov_profile_scale): Ditto.
>>> >>>>>>       (gcov_profile_normalize): Ditto.
>>> >>>>>>       (__gcov_scale2_add): Ditto.
>>> >>>>>>       (__gcov_scale2_ior): Ditto.
>>> >>>>>>       (__gcov_scale2_delta): Ditto.
>>> >>>>>>       (__gcov_scale2_single): Ditto.
>>> >>>>>>       (gcov_profile_scale2): Ditto.
>>> >>>>>>       * gcc/gcov-tool.c (unlink_file): Gcov-tool driver support.
>>> >>>>>>       (unlink_dir): Ditto.
>>> >>>>>>       (profile_merge): Ditto.
>>> >>>>>>       (print_merge_usage_message): Ditto.
>>> >>>>>>       (merge_usage): Ditto.
>>> >>>>>>       (do_merge): Ditto.
>>> >>>>>>       (profile_rewrite2): Ditto.
>>> >>>>>>       (profile_rewrite): Ditto.
>>> >>>>>>       (print_rewrite_usage_message): Ditto.
>>> >>>>>>       (rewrite_usage): Ditto.
>>> >>>>>>       (do_rewrite): Ditto.
>>> >>>>>>       (print_usage): Ditto.
>>> >>>>>>       (print_version): Ditto.
>>> >>>>>>       (process_args): Ditto.
>>> >>>>>>       (main): Ditto.
>>> >>>>>>       * gcc/Makefile.in: Build and install gcov-tool.
>>> >>>>>
>>> >>>>>> Index: gcc/gcov-io.c
>>> >>>>>> ===================================================================
>>> >>>>>> --- gcc/gcov-io.c     (revision 204895)
>>> >>>>>> +++ gcc/gcov-io.c     (working copy)
>>> >>>>>> @@ -36,6 +36,37 @@ static const gcov_unsigned_t *gcov_read_words (uns
>>> >>>>>>  static void gcov_allocate (unsigned);
>>> >>>>>>  #endif
>>> >>>>>>
>>> >>>>>> +/* Moved for gcov-io.h and make it static.  */
>>> >>>>>> +static struct gcov_var gcov_var;
>>> >>>>>
>>> >>>>> This is more an changelog message than a comment in source file.
>>> >>>>> Just describe what gcov_var is.
>>> >>>>
>>> >>>> I changed this so gcov_var is no longer static, but global as before.
>>> >>>>
>>> >>>>>
>>> >>>>> Do you know how the size of libgcov changed with your patch?
>>> >>>>> Quick check of current mainline on compiling empty main gives:
>>> >>>>>
>>> >>>>> jh@gcc10:~/trunk/build/gcc$ cat t.c
>>> >>>>> main()
>>> >>>>> {
>>> >>>>> }
>>> >>>>> jh@gcc10:~/trunk/build/gcc$ ./xgcc -B ./ -O2 -fprofile-generate -o a.out-new --static t.c
>>> >>>>> jh@gcc10:~/trunk/build/gcc$ gcc -O2 -fprofile-generate -o a.out-old --static t.c
>>> >>>>> jh@gcc10:~/trunk/build/gcc$ size a.out-old
>>> >>>>>    text    data     bss     dec     hex filename
>>> >>>>>  608141    3560   16728  628429   996cd a.out-old
>>> >>>>> jh@gcc10:~/trunk/build/gcc$ size a.out-new
>>> >>>>>    text    data     bss     dec     hex filename
>>> >>>>>  612621    3688   22880  639189   9c0d5 a.out-new
>>> >>>>>
>>> >>>>> Without profiling I get:
>>> >>>>> jh@gcc10:~/trunk/build/gcc$ size a.out-new-no
>>> >>>>> jh@gcc10:~/trunk/build/gcc$ size a.out-old-no
>>> >>>>>    text    data     bss     dec     hex filename
>>> >>>>>  599719    3448   12568  615735   96537 a.out-old-no
>>> >>>>>    text    data     bss     dec     hex filename
>>> >>>>>  600247    3448   12568  616263   96747 a.out-new-no
>>> >>>>>
>>> >>>>> Quite big for empty program, but mostly glibc fault, I suppose
>>> >>>>> (that won't be an issue for embedded platforms). But anyway
>>> >>>>> we increased text size overhead from 8k to 12k, BSS size
>>> >>>>> overhead from 4k to 10k and data by another 1k.
>>> >>>>>
>>> >>>>>    text    data     bss     dec     hex filename
>>> >>>>>     126       0       0     126      7e _gcov_merge_add.o (ex libgcov.a)
>>> >>>>>     251       0       0     251      fb _gcov_merge_single.o (ex libgcov.a)
>>> >>>>>     242       0       0     242      f2 _gcov_merge_delta.o (ex libgcov.a)
>>> >>>>>     126       0       0     126      7e _gcov_merge_ior.o (ex libgcov.a)
>>> >>>>>     156       0       0     156      9c _gcov_merge_time_profile.o (ex libgcov.a)
>>> >>>>>      89       0       0      89      59 _gcov_interval_profiler.o (ex libgcov.a)
>>> >>>>>      69       0       0      69      45 _gcov_pow2_profiler.o (ex libgcov.a)
>>> >>>>>     115       0       0     115      73 _gcov_one_value_profiler.o (ex libgcov.a)
>>> >>>>>     122       0       0     122      7a _gcov_indirect_call_profiler.o (ex libgcov.a)
>>> >>>>>      57       0       0      57      39 _gcov_average_profiler.o (ex libgcov.a)
>>> >>>>>      52       0       0      52      34 _gcov_ior_profiler.o (ex libgcov.a)
>>> >>>>>     178       0      16     194      c2 _gcov_indirect_call_profiler_v2.o (ex libgcov.a)
>>> >>>>>      77       0       8      85      55 _gcov_time_profiler.o (ex libgcov.a)
>>> >>>>>     126       0      40     166      a6 _gcov_flush.o (ex libgcov.a)
>>> >>>>>     101       0       0     101      65 _gcov_fork.o (ex libgcov.a)
>>> >>>>>     471       0       0     471     1d7 _gcov_execl.o (ex libgcov.a)
>>> >>>>>     471       0       0     471     1d7 _gcov_execlp.o (ex libgcov.a)
>>> >>>>>     524       0       0     524     20c _gcov_execle.o (ex libgcov.a)
>>> >>>>>      98       0       0      98      62 _gcov_execv.o (ex libgcov.a)
>>> >>>>>      98       0       0      98      62 _gcov_execvp.o (ex libgcov.a)
>>> >>>>>     108       0       0     108      6c _gcov_execve.o (ex libgcov.a)
>>> >>>>>      66       0       0      66      42 _gcov_reset.o (ex libgcov.a)
>>> >>>>>      66       0       0      66      42 _gcov_dump.o (ex libgcov.a)
>>> >>>>>    9505       0    6144   15649    3d21 _gcov.o (ex libgcov.a)
>>> >>>>>
>>> >>>>> I think we definitely need to move those 6k of bss space out.  I think those are new
>>> >>>>> static vars you introduced that I think are unsafe anyway because multiple streaming
>>> >>>>> may run at once in threaded program where locking mechanizm fails.
>>> >>>>> (it will probably do other bad things, but definitely we do not want to
>>> >>>>> conflict on things like filename to write into).
>>> >>>>>
>>> >>>>> Compared to my system gcov:
>>> >>>>>    text    data     bss     dec     hex filename
>>> >>>>>    9765       0      64    9829    2665 _gcov.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>> >>>>>     125       0       0     125      7d _gcov_merge_add.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>> >>>>>     251       0       0     251      fb _gcov_merge_single.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>> >>>>>     242       0       0     242      f2 _gcov_merge_delta.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>> >>>>>     101       0       0     101      65 _gcov_fork.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>> >>>>>     471       0       0     471     1d7 _gcov_execl.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>> >>>>>     471       0       0     471     1d7 _gcov_execlp.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>> >>>>>     524       0       0     524     20c _gcov_execle.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>> >>>>>      98       0       0      98      62 _gcov_execv.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>> >>>>>      98       0       0      98      62 _gcov_execvp.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>> >>>>>     108       0       0     108      6c _gcov_execve.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>> >>>>>      72       0       0      72      48 _gcov_reset.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>> >>>>>      72       0       0      72      48 _gcov_dump.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>> >>>>>      89       0       0      89      59 _gcov_interval_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>> >>>>>      69       0       0      69      45 _gcov_pow2_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>> >>>>>     115       0       0     115      73 _gcov_one_value_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>> >>>>>     122       0       0     122      7a _gcov_indirect_call_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>> >>>>>      57       0       0      57      39 _gcov_average_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>> >>>>>      52       0       0      52      34 _gcov_ior_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>> >>>>>     125       0       0     125      7d _gcov_merge_ior.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>> >>>>>
>>> >>>>>> Index: gcc/gcov-io.h
>>> >>>>>> ===================================================================
>>> >>>>>> --- gcc/gcov-io.h     (revision 204895)
>>> >>>>>> +++ gcc/gcov-io.h     (working copy)
>>> >>>>>> @@ -164,51 +164,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
>>> >>>>>>  #ifndef GCC_GCOV_IO_H
>>> >>>>>>  #define GCC_GCOV_IO_H
>>> >>>>>>
>>> >>>>>> -#if IN_LIBGCOV
>>> >>>>>> -/* About the target */
>>> >>>>>> -
>>> >>>>>> -#if BITS_PER_UNIT == 8
>>> >>>>>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI)));
>>> >>>>>> -typedef unsigned gcov_position_t __attribute__ ((mode (SI)));
>>> >>>>>> -#if LONG_LONG_TYPE_SIZE > 32
>>> >>>>>> -typedef signed gcov_type __attribute__ ((mode (DI)));
>>> >>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (DI)));
>>> >>>>>> -#else
>>> >>>>>> -typedef signed gcov_type __attribute__ ((mode (SI)));
>>> >>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
>>> >>>>>> -#endif
>>> >>>>>> -#else
>>> >>>>>> -#if BITS_PER_UNIT == 16
>>> >>>>>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (HI)));
>>> >>>>>> -typedef unsigned gcov_position_t __attribute__ ((mode (HI)));
>>> >>>>>> -#if LONG_LONG_TYPE_SIZE > 32
>>> >>>>>> -typedef signed gcov_type __attribute__ ((mode (SI)));
>>> >>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
>>> >>>>>> -#else
>>> >>>>>> -typedef signed gcov_type __attribute__ ((mode (HI)));
>>> >>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
>>> >>>>>> -#endif
>>> >>>>>> -#else
>>> >>>>>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (QI)));
>>> >>>>>> -typedef unsigned gcov_position_t __attribute__ ((mode (QI)));
>>> >>>>>> -#if LONG_LONG_TYPE_SIZE > 32
>>> >>>>>> -typedef signed gcov_type __attribute__ ((mode (HI)));
>>> >>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
>>> >>>>>> -#else
>>> >>>>>> -typedef signed gcov_type __attribute__ ((mode (QI)));
>>> >>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI)));
>>> >>>>>> -#endif
>>> >>>>>> -#endif
>>> >>>>>> -#endif
>>> >>>>>
>>> >>>>> So this part basically moves libgcov specific bits into libgcov.h? That OK fine by
>>> >>>>> itself.
>>> >>>>>> Index: libgcc/libgcov-profiler.c
>>> >>>>>> ===================================================================
>>> >>>>>> --- libgcc/libgcov-profiler.c (revision 204895)
>>> >>>>>> +++ libgcc/libgcov-profiler.c (working copy)
>>> >>>>>> @@ -23,15 +23,8 @@ a copy of the GCC Runtime Library Exception along
>>> >>>>>>  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>>> >>>>>>  <http://www.gnu.org/licenses/>.  */
>>> >>>>>>
>>> >>>>>> -#include "tconfig.h"
>>> >>>>>> -#include "tsystem.h"
>>> >>>>>> -#include "coretypes.h"
>>> >>>>>> -#include "tm.h"
>>> >>>>>> -#include "libgcc_tm.h"
>>> >>>>>> -
>>> >>>>>> +#include "libgcov.h"
>>> >>>>>>  #if !defined(inhibit_libc)
>>> >>>>>> -#define IN_LIBGCOV 1
>>> >>>>>> -#include "gcov-io.h"
>>> >>>>>
>>> >>>>> I did not pay that much attention into the current include file changes, but wasn't
>>> >>>>> idea to avoid #include file to include random other #includes?
>>> >>>>> So perhaps the first block of includes should stay, followed by libgcov.h and gcov-io.h
>>> >>>>> last?
>>> >>>>
>>> >>>> I'm not sure I understand the issue here? The patch basically moves
>>> >>>> the same includes into libgcov.h, and includes that instead. I see
>>> >>>> many other header files in gcc that include other headers.
>>> >>>>
>>> >>>>>> @@ -325,6 +311,9 @@ static struct gcov_summary all_prg;
>>> >>>>>>  #endif
>>> >>>>>>  /* crc32 for this program.  */
>>> >>>>>>  static gcov_unsigned_t crc32;
>>> >>>>>> +/* Use this summary checksum rather the computed one if the value is
>>> >>>>>> + *    non-zero.  */
>>> >>>>>> +static gcov_unsigned_t saved_summary_checksum;
>>> >>>>>
>>> >>>>> Why do you need to save the checksum? Won't it reset summary back with multiple streaming?
>>> >>>>
>>> >>>> This has been removed.
>>> >>>>
>>> >>>>>
>>> >>>>> I would really like to avoid introducing those static vars that are used exclusively
>>> >>>>> by gcov_exit.  What about putting them into an gcov_context structure that
>>> >>>>> is passed around the functions that was broken out?
>>> >>>>>> Index: libgcc/libgcov-merge.c
>>> >>>>>> ===================================================================
>>> >>>>>> --- libgcc/libgcov-merge.c    (revision 204895)
>>> >>>>>> +++ libgcc/libgcov-merge.c    (working copy)
>>> >>>>>> @@ -23,107 +23,150 @@ a copy of the GCC Runtime Library Exception along
>>> >>>>>>  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>>> >>>>>>  <http://www.gnu.org/licenses/>.  */
>>> >>>>>>
>>> >>>>>> -#include "tconfig.h"
>>> >>>>>> -#include "tsystem.h"
>>> >>>>>> -#include "coretypes.h"
>>> >>>>>> -#include "tm.h"
>>> >>>>>> -#include "libgcc_tm.h"
>>> >>>>>> +#include "libgcov.h"
>>> >>>>>>
>>> >>>>>> -#if defined(inhibit_libc)
>>> >>>>>> -#define IN_LIBGCOV (-1)
>>> >>>>>> -#else
>>> >>>>>> -#define IN_LIBGCOV 1
>>> >>>>>> +#include "gcov-io-libgcov.h"
>>> >>>>>>  #endif
>>> >>>>>>
>>> >>>>>> -#include "gcov-io.h"
>>> >>>>>> -
>>> >>>>>>  #if defined(inhibit_libc)
>>> >>>>>>  /* If libc and its header files are not available, provide dummy functions.  */
>>> >>>>>>
>>> >>>>>>  #ifdef L_gcov_merge_add
>>> >>>>>>  void __gcov_merge_add (gcov_type *counters  __attribute__ ((unused)),
>>> >>>>>> -                       unsigned n_counters __attribute__ ((unused))) {}
>>> >>>>>> +                       unsigned n_counters __attribute__ ((unused)),
>>> >>>>>> +                       unsigned gcov_type *src __attribute__ ((unused)),
>>> >>>>>> +                       unsigned w __attribute__ ((unused))) {}
>>> >>>>>>  #endif
>>> >>>>>>
>>> >>>>>>  #ifdef L_gcov_merge_single
>>> >>>>>>  void __gcov_merge_single (gcov_type *counters  __attribute__ ((unused)),
>>> >>>>>> -                          unsigned n_counters __attribute__ ((unused))) {}
>>> >>>>>> +                          unsigned n_counters __attribute__ ((unused)),
>>> >>>>>> +                          unsigned gcov_type *src __attribute__ ((unused)),
>>> >>>>>> +                          unsigned w __attribute__ ((unused))) {}
>>> >>>>>>  #endif
>>> >>>>>>
>>> >>>>>>  #ifdef L_gcov_merge_delta
>>> >>>>>>  void __gcov_merge_delta (gcov_type *counters  __attribute__ ((unused)),
>>> >>>>>> -                         unsigned n_counters __attribute__ ((unused))) {}
>>> >>>>>> +                         unsigned n_counters __attribute__ ((unused)),
>>> >>>>>> +                         unsigned gcov_type *src __attribute__ ((unused)),
>>> >>>>>> +                         unsigned w __attribute__ ((unused))) {}
>>> >>>>>>  #endif
>>> >>>>>>
>>> >>>>>>  #else
>>> >>>>>>
>>> >>>>>>  #ifdef L_gcov_merge_add
>>> >>>>>>  /* The profile merging function that just adds the counters.  It is given
>>> >>>>>> -   an array COUNTERS of N_COUNTERS old counters and it reads the same number
>>> >>>>>> -   of counters from the gcov file.  */
>>> >>>>>> +   an array COUNTERS of N_COUNTERS old counters.
>>> >>>>>> +   When SRC==NULL, it reads the same number of counters from the gcov file.
>>> >>>>>> +   Otherwise, it reads from SRC array. These read values will be multiplied
>>> >>>>>> +   by weight W before adding to the old counters.  */
>>> >>>>>>  void
>>> >>>>>> -__gcov_merge_add (gcov_type *counters, unsigned n_counters)
>>> >>>>>> +__gcov_merge_add (gcov_type *counters, unsigned n_counters,
>>> >>>>>> +                  gcov_type *src, unsigned w)
>>> >>>>>>  {
>>> >>>>>> +  int in_mem = (src != 0);
>>> >>>>>> +
>>> >>>>>>    for (; n_counters; counters++, n_counters--)
>>> >>>>>> -    *counters += gcov_read_counter ();
>>> >>>>>> +    {
>>> >>>>>> +      gcov_type value;
>>> >>>>>> +
>>> >>>>>> +      if (in_mem)
>>> >>>>>> +        value = *(src++);
>>> >>>>>> +      else
>>> >>>>>> +        value = gcov_read_counter ();
>>> >>>>>> +
>>> >>>>>> +      *counters += w * value;
>>> >>>>>> +    }
>>> >>>>>>  }
>>> >>>>>>  #endif /* L_gcov_merge_add */
>>> >>>>>>
>>> >>>>>>  #ifdef L_gcov_merge_ior
>>> >>>>>>  /* The profile merging function that just adds the counters.  It is given
>>> >>>>>> -   an array COUNTERS of N_COUNTERS old counters and it reads the same number
>>> >>>>>> -   of counters from the gcov file.  */
>>> >>>>>> +   an array COUNTERS of N_COUNTERS old counters.
>>> >>>>>> +   When SRC==NULL, it reads the same number of counters from the gcov file.
>>> >>>>>> +   Otherwise, it reads from SRC array.  */
>>> >>>>>>  void
>>> >>>>>> -__gcov_merge_ior (gcov_type *counters, unsigned n_counters)
>>> >>>>>> +__gcov_merge_ior (gcov_type *counters, unsigned n_counters,
>>> >>>>>> +                  gcov_type *src, unsigned w __attribute__ ((unused)))
>>> >>>>>
>>> >>>>> So the new in-memory variants are introduced for merging tool, while libgcc use gcov_read_counter
>>> >>>>> interface?
>>> >>>>> Perhaps we can actually just duplicate the functions to avoid runtime to do all the scalling
>>> >>>>> and in_mem tests it won't need?
>>> >>>>>
>>> >>>>> I would suggest going with libgcov.h changes and clenaups first, with interface changes next
>>> >>>>> and the gcov-tool is probably quite obvious at the end?
>>> >>>>> Do you think you can split the patch this way?
>>> >>>>>
>>> >>>>> Thanks and sorry for taking long to review. I should have more time again now.
>>> >>>>> Honza
>>> >>>>
>>> >>>> The libgcov.h related changes are in the attached patch. I think it
>>> >>>> addresses your concerns. Bootstrapped and tested on
>>> >>>> x86-64-unknown-linux-gnu. A profiledbootstrap is in progress.
>>> >>>>
>>> >>>> Ok for trunk if profiledbootstrap passes?
>>> >>>
>>> >>> Both a profiledbootstrap and LTO profiledbootstrap pass.
>>> >>>
>>> >>> Teresa
>>> >>>
>>> >>>>
>>> >>>> Thanks,
>>> >>>> Teresa
>>> >>>>
>>> >>>>
>>> >>>> --
>>> >>>> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413
>>> >>>
>>> >>>
>>> >>>
>>> >>> --
>>> >>> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413
>>> >
>>> >
>>> >
>>> > --
>>> > Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413



-- 
Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-12-17 15:48                               ` Teresa Johnson
@ 2013-12-17 17:58                                 ` Xinliang David Li
  0 siblings, 0 replies; 55+ messages in thread
From: Xinliang David Li @ 2013-12-17 17:58 UTC (permalink / raw)
  To: Teresa Johnson; +Cc: Jan Hubicka, Rong Xu, GCC Patches

On Tue, Dec 17, 2013 at 7:48 AM, Teresa Johnson <tejohnson@google.com> wrote:
> On Mon, Dec 16, 2013 at 2:48 PM, Xinliang David Li <davidxl@google.com> wrote:
>> Ok -- gcov_write_counter and gcov_write_tag_length are qualified as
>> low level primitives for basic gcov format and probably should be kept
>> in gcov-io.c.
>>
>> gcov_rewrite is petty much libgcov runtime implementation details so I
>> think it should be moved out. gcov_write_summary is not related to
>> gcov low level format either, neither is gcov_seek.  Ok for them to be
>> moved?
>
> After looking at these some more, with the idea that gcov-io.c should
> encapsulate the lower level IO routines, then I think all of these
> (including gcov_rewrite) should remain in gcov-io.c. I think
> gcov_write_summary belongs there since all of the other gcov_write_*
> are there. And gcov_seek and gcov_rewrite are both adjusting gcov_var
> fields to affect the file IO operations. And there are currently no
> references to gcov_var within libgcc/libgcov* files.
>
> So I think we should leave the patch as-is.

Sounds fine to me.

David

> Honza, is the current
> patch ok for trunk?
>
> Thanks,
> Teresa
>
>>
>> thanks,
>>
>> David
>>
>>
>> On Mon, Dec 16, 2013 at 2:34 PM, Jan Hubicka <hubicka@ucw.cz> wrote:
>>>> I think so -- they are private to libgcov.  Honza, what do you think?
>>>
>>> Hmm, the purpose of gcov-io was to be low level IO library for the basic
>>> gcov file format.  I am not sure if gcov_write_tag_length should really resist
>>> in other file than gcov_write_tag.
>>>
>>> I see a desire to isolate actual stdio calls so one can have replacement driver
>>> for i.e. Linux kernel. For that reason things like gcov_seek and friends probably
>>> should be separated, but what is reason for splitting the file handling itself?
>>>
>>> Honza
>>>>
>>>> thanks,
>>>>
>>>> David
>>>>
>>>> On Mon, Dec 16, 2013 at 1:17 PM, Teresa Johnson <tejohnson@google.com> wrote:
>>>> > On Mon, Dec 16, 2013 at 12:55 PM, Xinliang David Li <davidxl@google.com> wrote:
>>>> >> gcov_rewrite function is only needed (and defined) with IN_LIBGCOV.
>>>> >> Should it be moved from common file gcov-io.c to libgcov.c?
>>>> >
>>>> > Possibly. I just looked through gcov-io.c and there are several
>>>> > additional functions that are only defined under "#ifdef IN_LIBGCOV"
>>>> > and only used in libgcov*c (or each other):
>>>> >
>>>> > gcov_write_counter
>>>> > gcov_write_tag_length
>>>> > gcov_write_summary
>>>> > gcov_seek
>>>> >
>>>> > Should they all, plus gcov_rewrite, be moved to libgcov-driver.c?
>>>> >
>>>> > Teresa
>>>> >
>>>> >>
>>>> >>
>>>> >> David
>>>> >>
>>>> >> On Thu, Dec 12, 2013 at 12:11 PM, Teresa Johnson <tejohnson@google.com> wrote:
>>>> >>> On Wed, Dec 11, 2013 at 10:05 PM, Teresa Johnson <tejohnson@google.com> wrote:
>>>> >>>> On Fri, Dec 6, 2013 at 6:23 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>>>> >>>>>> Hi, all
>>>> >>>>>>
>>>> >>>>>> This is the new patch for gcov-tool (previously profile-tool).
>>>> >>>>>>
>>>> >>>>>> Honza: can you comment on the new merge interface? David posted some
>>>> >>>>>> comments in an earlier email and we want to know what's your opinion.
>>>> >>>>>>
>>>> >>>>>> Test patch has been tested with boostrap, regresssion,
>>>> >>>>>> profiledbootstrap and SPEC2006.
>>>> >>>>>>
>>>> >>>>>> Noticeable changes from the earlier version:
>>>> >>>>>>
>>>> >>>>>> 1. create a new file libgcov.h and move libgcov-*.h headers to libgcov.h
>>>> >>>>>> So we can included multiple libgcov-*.c without adding new macros.
>>>> >>>>>>
>>>> >>>>>> 2. split libgcov.h specific code in gcvo-io.h to libcc/libgcov.h
>>>> >>>>>> Avoid multiple-page of code under IN_LIBGCOV macro -- this
>>>> >>>>>> improves the readability.
>>>> >>>>>>
>>>> >>>>>> 3. make gcov_var static, and move the definition from gcov-io.h to
>>>> >>>>>> gcov-io.c. Also
>>>> >>>>>>    move some static functions accessing gcov_var to gcvo-io.c
>>>> >>>>>> Current code rely on GCOV_LINKAGE tricks to avoid multi-definition. I don't see
>>>> >>>>>> a reason that gcov_var needs to exposed as a global.
>>>> >>>>>>
>>>> >>>>>> 4. expose gcov_write_strings() and gcov_sync() to gcov_tool usage
>>>> >>>>>>
>>>> >>>>>> 5. rename profile-tool to gcov-tool per Honza's suggestion.
>>>> >>>>>>
>>>> >>>>>> Thanks,
>>>> >>>>>
>>>> >>>>> Hi,
>>>> >>>>> I did not read in deatil the gcov-tool source itself, but lets first make the interface changes
>>>> >>>>> needed.
>>>> >>>>>
>>>> >>>>>> 2013-11-18  Rong Xu  <xur@google.com>
>>>> >>>>>>
>>>> >>>>>>       * gcc/gcov-io.c (gcov_var): Moved from gcov-io.h and make it static.
>>>> >>>>>>       (gcov_position): Move from gcov-io.h
>>>> >>>>>>       (gcov_is_error): Ditto.
>>>> >>>>>>       (gcov_rewrite): Ditto.
>>>> >>>>>>       * gcc/gcov-io.h: Re-factoring. Move gcov_var to gcov-io.h and
>>>> >>>>>>         move the libgcov only part of libgcc/libgcov.h.
>>>> >>>>>>       * libgcc/libgcov.h: New common header files for libgcov-*.h
>>>> >>>>>>       * libgcc/Makefile.in: Add dependence to libgcov.h
>>>> >>>>>>       * libgcc/libgcov-profiler.c: Use libgcov.h
>>>> >>>>>>       * libgcc/libgcov-driver.c: Ditto.
>>>> >>>>>>       * libgcc/libgcov-interface.c: Ditto.
>>>> >>>>>>       * libgcc/libgcov-driver-system.c (allocate_filename_struct): use
>>>> >>>>>>       xmalloc instread of malloc.
>>>> >>>>>>       * libgcc/libgcov-merge.c (void __gcov_merge_delta): Add more
>>>> >>>>>>       parameters to merge function.
>>>> >>>>>>       (__gcov_merge_add): Ditto.
>>>> >>>>>>       (__gcov_merge_ior): Ditto.
>>>> >>>>>>       (__gcov_merge_time_profile): Ditto.
>>>> >>>>>>       (__gcov_merge_single): Ditto.
>>>> >>>>>>       (__gcov_merge_delta): Ditto.
>>>> >>>>>>       * libgcc/libgcov-tool.c (void gcov_tool_set_verbose): New for
>>>> >>>>>>       gcov-tool support.
>>>> >>>>>>       (set_fn_ctrs): Ditto.
>>>> >>>>>>       (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_merge): Ditto.
>>>> >>>>>>       (find_match_gcov_inf Ditto.o):
>>>> >>>>>>       (gcov_profile_merge): Ditto.
>>>> >>>>>>       (__gcov_scale_add): Ditto.
>>>> >>>>>>       (__gcov_scale_ior): Ditto.
>>>> >>>>>>       (__gcov_scale_delta): Ditto.
>>>> >>>>>>       (__gcov_scale_single): Ditto.
>>>> >>>>>>       (gcov_profile_scale): Ditto.
>>>> >>>>>>       (gcov_profile_normalize): Ditto.
>>>> >>>>>>       (__gcov_scale2_add): Ditto.
>>>> >>>>>>       (__gcov_scale2_ior): Ditto.
>>>> >>>>>>       (__gcov_scale2_delta): Ditto.
>>>> >>>>>>       (__gcov_scale2_single): Ditto.
>>>> >>>>>>       (gcov_profile_scale2): Ditto.
>>>> >>>>>>       * gcc/gcov-tool.c (unlink_file): Gcov-tool driver support.
>>>> >>>>>>       (unlink_dir): Ditto.
>>>> >>>>>>       (profile_merge): Ditto.
>>>> >>>>>>       (print_merge_usage_message): Ditto.
>>>> >>>>>>       (merge_usage): Ditto.
>>>> >>>>>>       (do_merge): Ditto.
>>>> >>>>>>       (profile_rewrite2): Ditto.
>>>> >>>>>>       (profile_rewrite): Ditto.
>>>> >>>>>>       (print_rewrite_usage_message): Ditto.
>>>> >>>>>>       (rewrite_usage): Ditto.
>>>> >>>>>>       (do_rewrite): Ditto.
>>>> >>>>>>       (print_usage): Ditto.
>>>> >>>>>>       (print_version): Ditto.
>>>> >>>>>>       (process_args): Ditto.
>>>> >>>>>>       (main): Ditto.
>>>> >>>>>>       * gcc/Makefile.in: Build and install gcov-tool.
>>>> >>>>>
>>>> >>>>>> Index: gcc/gcov-io.c
>>>> >>>>>> ===================================================================
>>>> >>>>>> --- gcc/gcov-io.c     (revision 204895)
>>>> >>>>>> +++ gcc/gcov-io.c     (working copy)
>>>> >>>>>> @@ -36,6 +36,37 @@ static const gcov_unsigned_t *gcov_read_words (uns
>>>> >>>>>>  static void gcov_allocate (unsigned);
>>>> >>>>>>  #endif
>>>> >>>>>>
>>>> >>>>>> +/* Moved for gcov-io.h and make it static.  */
>>>> >>>>>> +static struct gcov_var gcov_var;
>>>> >>>>>
>>>> >>>>> This is more an changelog message than a comment in source file.
>>>> >>>>> Just describe what gcov_var is.
>>>> >>>>
>>>> >>>> I changed this so gcov_var is no longer static, but global as before.
>>>> >>>>
>>>> >>>>>
>>>> >>>>> Do you know how the size of libgcov changed with your patch?
>>>> >>>>> Quick check of current mainline on compiling empty main gives:
>>>> >>>>>
>>>> >>>>> jh@gcc10:~/trunk/build/gcc$ cat t.c
>>>> >>>>> main()
>>>> >>>>> {
>>>> >>>>> }
>>>> >>>>> jh@gcc10:~/trunk/build/gcc$ ./xgcc -B ./ -O2 -fprofile-generate -o a.out-new --static t.c
>>>> >>>>> jh@gcc10:~/trunk/build/gcc$ gcc -O2 -fprofile-generate -o a.out-old --static t.c
>>>> >>>>> jh@gcc10:~/trunk/build/gcc$ size a.out-old
>>>> >>>>>    text    data     bss     dec     hex filename
>>>> >>>>>  608141    3560   16728  628429   996cd a.out-old
>>>> >>>>> jh@gcc10:~/trunk/build/gcc$ size a.out-new
>>>> >>>>>    text    data     bss     dec     hex filename
>>>> >>>>>  612621    3688   22880  639189   9c0d5 a.out-new
>>>> >>>>>
>>>> >>>>> Without profiling I get:
>>>> >>>>> jh@gcc10:~/trunk/build/gcc$ size a.out-new-no
>>>> >>>>> jh@gcc10:~/trunk/build/gcc$ size a.out-old-no
>>>> >>>>>    text    data     bss     dec     hex filename
>>>> >>>>>  599719    3448   12568  615735   96537 a.out-old-no
>>>> >>>>>    text    data     bss     dec     hex filename
>>>> >>>>>  600247    3448   12568  616263   96747 a.out-new-no
>>>> >>>>>
>>>> >>>>> Quite big for empty program, but mostly glibc fault, I suppose
>>>> >>>>> (that won't be an issue for embedded platforms). But anyway
>>>> >>>>> we increased text size overhead from 8k to 12k, BSS size
>>>> >>>>> overhead from 4k to 10k and data by another 1k.
>>>> >>>>>
>>>> >>>>>    text    data     bss     dec     hex filename
>>>> >>>>>     126       0       0     126      7e _gcov_merge_add.o (ex libgcov.a)
>>>> >>>>>     251       0       0     251      fb _gcov_merge_single.o (ex libgcov.a)
>>>> >>>>>     242       0       0     242      f2 _gcov_merge_delta.o (ex libgcov.a)
>>>> >>>>>     126       0       0     126      7e _gcov_merge_ior.o (ex libgcov.a)
>>>> >>>>>     156       0       0     156      9c _gcov_merge_time_profile.o (ex libgcov.a)
>>>> >>>>>      89       0       0      89      59 _gcov_interval_profiler.o (ex libgcov.a)
>>>> >>>>>      69       0       0      69      45 _gcov_pow2_profiler.o (ex libgcov.a)
>>>> >>>>>     115       0       0     115      73 _gcov_one_value_profiler.o (ex libgcov.a)
>>>> >>>>>     122       0       0     122      7a _gcov_indirect_call_profiler.o (ex libgcov.a)
>>>> >>>>>      57       0       0      57      39 _gcov_average_profiler.o (ex libgcov.a)
>>>> >>>>>      52       0       0      52      34 _gcov_ior_profiler.o (ex libgcov.a)
>>>> >>>>>     178       0      16     194      c2 _gcov_indirect_call_profiler_v2.o (ex libgcov.a)
>>>> >>>>>      77       0       8      85      55 _gcov_time_profiler.o (ex libgcov.a)
>>>> >>>>>     126       0      40     166      a6 _gcov_flush.o (ex libgcov.a)
>>>> >>>>>     101       0       0     101      65 _gcov_fork.o (ex libgcov.a)
>>>> >>>>>     471       0       0     471     1d7 _gcov_execl.o (ex libgcov.a)
>>>> >>>>>     471       0       0     471     1d7 _gcov_execlp.o (ex libgcov.a)
>>>> >>>>>     524       0       0     524     20c _gcov_execle.o (ex libgcov.a)
>>>> >>>>>      98       0       0      98      62 _gcov_execv.o (ex libgcov.a)
>>>> >>>>>      98       0       0      98      62 _gcov_execvp.o (ex libgcov.a)
>>>> >>>>>     108       0       0     108      6c _gcov_execve.o (ex libgcov.a)
>>>> >>>>>      66       0       0      66      42 _gcov_reset.o (ex libgcov.a)
>>>> >>>>>      66       0       0      66      42 _gcov_dump.o (ex libgcov.a)
>>>> >>>>>    9505       0    6144   15649    3d21 _gcov.o (ex libgcov.a)
>>>> >>>>>
>>>> >>>>> I think we definitely need to move those 6k of bss space out.  I think those are new
>>>> >>>>> static vars you introduced that I think are unsafe anyway because multiple streaming
>>>> >>>>> may run at once in threaded program where locking mechanizm fails.
>>>> >>>>> (it will probably do other bad things, but definitely we do not want to
>>>> >>>>> conflict on things like filename to write into).
>>>> >>>>>
>>>> >>>>> Compared to my system gcov:
>>>> >>>>>    text    data     bss     dec     hex filename
>>>> >>>>>    9765       0      64    9829    2665 _gcov.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>> >>>>>     125       0       0     125      7d _gcov_merge_add.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>> >>>>>     251       0       0     251      fb _gcov_merge_single.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>> >>>>>     242       0       0     242      f2 _gcov_merge_delta.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>> >>>>>     101       0       0     101      65 _gcov_fork.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>> >>>>>     471       0       0     471     1d7 _gcov_execl.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>> >>>>>     471       0       0     471     1d7 _gcov_execlp.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>> >>>>>     524       0       0     524     20c _gcov_execle.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>> >>>>>      98       0       0      98      62 _gcov_execv.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>> >>>>>      98       0       0      98      62 _gcov_execvp.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>> >>>>>     108       0       0     108      6c _gcov_execve.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>> >>>>>      72       0       0      72      48 _gcov_reset.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>> >>>>>      72       0       0      72      48 _gcov_dump.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>> >>>>>      89       0       0      89      59 _gcov_interval_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>> >>>>>      69       0       0      69      45 _gcov_pow2_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>> >>>>>     115       0       0     115      73 _gcov_one_value_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>> >>>>>     122       0       0     122      7a _gcov_indirect_call_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>> >>>>>      57       0       0      57      39 _gcov_average_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>> >>>>>      52       0       0      52      34 _gcov_ior_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>> >>>>>     125       0       0     125      7d _gcov_merge_ior.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>>>> >>>>>
>>>> >>>>>> Index: gcc/gcov-io.h
>>>> >>>>>> ===================================================================
>>>> >>>>>> --- gcc/gcov-io.h     (revision 204895)
>>>> >>>>>> +++ gcc/gcov-io.h     (working copy)
>>>> >>>>>> @@ -164,51 +164,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
>>>> >>>>>>  #ifndef GCC_GCOV_IO_H
>>>> >>>>>>  #define GCC_GCOV_IO_H
>>>> >>>>>>
>>>> >>>>>> -#if IN_LIBGCOV
>>>> >>>>>> -/* About the target */
>>>> >>>>>> -
>>>> >>>>>> -#if BITS_PER_UNIT == 8
>>>> >>>>>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI)));
>>>> >>>>>> -typedef unsigned gcov_position_t __attribute__ ((mode (SI)));
>>>> >>>>>> -#if LONG_LONG_TYPE_SIZE > 32
>>>> >>>>>> -typedef signed gcov_type __attribute__ ((mode (DI)));
>>>> >>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (DI)));
>>>> >>>>>> -#else
>>>> >>>>>> -typedef signed gcov_type __attribute__ ((mode (SI)));
>>>> >>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
>>>> >>>>>> -#endif
>>>> >>>>>> -#else
>>>> >>>>>> -#if BITS_PER_UNIT == 16
>>>> >>>>>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (HI)));
>>>> >>>>>> -typedef unsigned gcov_position_t __attribute__ ((mode (HI)));
>>>> >>>>>> -#if LONG_LONG_TYPE_SIZE > 32
>>>> >>>>>> -typedef signed gcov_type __attribute__ ((mode (SI)));
>>>> >>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
>>>> >>>>>> -#else
>>>> >>>>>> -typedef signed gcov_type __attribute__ ((mode (HI)));
>>>> >>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
>>>> >>>>>> -#endif
>>>> >>>>>> -#else
>>>> >>>>>> -typedef unsigned gcov_unsigned_t __attribute__ ((mode (QI)));
>>>> >>>>>> -typedef unsigned gcov_position_t __attribute__ ((mode (QI)));
>>>> >>>>>> -#if LONG_LONG_TYPE_SIZE > 32
>>>> >>>>>> -typedef signed gcov_type __attribute__ ((mode (HI)));
>>>> >>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
>>>> >>>>>> -#else
>>>> >>>>>> -typedef signed gcov_type __attribute__ ((mode (QI)));
>>>> >>>>>> -typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI)));
>>>> >>>>>> -#endif
>>>> >>>>>> -#endif
>>>> >>>>>> -#endif
>>>> >>>>>
>>>> >>>>> So this part basically moves libgcov specific bits into libgcov.h? That OK fine by
>>>> >>>>> itself.
>>>> >>>>>> Index: libgcc/libgcov-profiler.c
>>>> >>>>>> ===================================================================
>>>> >>>>>> --- libgcc/libgcov-profiler.c (revision 204895)
>>>> >>>>>> +++ libgcc/libgcov-profiler.c (working copy)
>>>> >>>>>> @@ -23,15 +23,8 @@ a copy of the GCC Runtime Library Exception along
>>>> >>>>>>  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>>>> >>>>>>  <http://www.gnu.org/licenses/>.  */
>>>> >>>>>>
>>>> >>>>>> -#include "tconfig.h"
>>>> >>>>>> -#include "tsystem.h"
>>>> >>>>>> -#include "coretypes.h"
>>>> >>>>>> -#include "tm.h"
>>>> >>>>>> -#include "libgcc_tm.h"
>>>> >>>>>> -
>>>> >>>>>> +#include "libgcov.h"
>>>> >>>>>>  #if !defined(inhibit_libc)
>>>> >>>>>> -#define IN_LIBGCOV 1
>>>> >>>>>> -#include "gcov-io.h"
>>>> >>>>>
>>>> >>>>> I did not pay that much attention into the current include file changes, but wasn't
>>>> >>>>> idea to avoid #include file to include random other #includes?
>>>> >>>>> So perhaps the first block of includes should stay, followed by libgcov.h and gcov-io.h
>>>> >>>>> last?
>>>> >>>>
>>>> >>>> I'm not sure I understand the issue here? The patch basically moves
>>>> >>>> the same includes into libgcov.h, and includes that instead. I see
>>>> >>>> many other header files in gcc that include other headers.
>>>> >>>>
>>>> >>>>>> @@ -325,6 +311,9 @@ static struct gcov_summary all_prg;
>>>> >>>>>>  #endif
>>>> >>>>>>  /* crc32 for this program.  */
>>>> >>>>>>  static gcov_unsigned_t crc32;
>>>> >>>>>> +/* Use this summary checksum rather the computed one if the value is
>>>> >>>>>> + *    non-zero.  */
>>>> >>>>>> +static gcov_unsigned_t saved_summary_checksum;
>>>> >>>>>
>>>> >>>>> Why do you need to save the checksum? Won't it reset summary back with multiple streaming?
>>>> >>>>
>>>> >>>> This has been removed.
>>>> >>>>
>>>> >>>>>
>>>> >>>>> I would really like to avoid introducing those static vars that are used exclusively
>>>> >>>>> by gcov_exit.  What about putting them into an gcov_context structure that
>>>> >>>>> is passed around the functions that was broken out?
>>>> >>>>>> Index: libgcc/libgcov-merge.c
>>>> >>>>>> ===================================================================
>>>> >>>>>> --- libgcc/libgcov-merge.c    (revision 204895)
>>>> >>>>>> +++ libgcc/libgcov-merge.c    (working copy)
>>>> >>>>>> @@ -23,107 +23,150 @@ a copy of the GCC Runtime Library Exception along
>>>> >>>>>>  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>>>> >>>>>>  <http://www.gnu.org/licenses/>.  */
>>>> >>>>>>
>>>> >>>>>> -#include "tconfig.h"
>>>> >>>>>> -#include "tsystem.h"
>>>> >>>>>> -#include "coretypes.h"
>>>> >>>>>> -#include "tm.h"
>>>> >>>>>> -#include "libgcc_tm.h"
>>>> >>>>>> +#include "libgcov.h"
>>>> >>>>>>
>>>> >>>>>> -#if defined(inhibit_libc)
>>>> >>>>>> -#define IN_LIBGCOV (-1)
>>>> >>>>>> -#else
>>>> >>>>>> -#define IN_LIBGCOV 1
>>>> >>>>>> +#include "gcov-io-libgcov.h"
>>>> >>>>>>  #endif
>>>> >>>>>>
>>>> >>>>>> -#include "gcov-io.h"
>>>> >>>>>> -
>>>> >>>>>>  #if defined(inhibit_libc)
>>>> >>>>>>  /* If libc and its header files are not available, provide dummy functions.  */
>>>> >>>>>>
>>>> >>>>>>  #ifdef L_gcov_merge_add
>>>> >>>>>>  void __gcov_merge_add (gcov_type *counters  __attribute__ ((unused)),
>>>> >>>>>> -                       unsigned n_counters __attribute__ ((unused))) {}
>>>> >>>>>> +                       unsigned n_counters __attribute__ ((unused)),
>>>> >>>>>> +                       unsigned gcov_type *src __attribute__ ((unused)),
>>>> >>>>>> +                       unsigned w __attribute__ ((unused))) {}
>>>> >>>>>>  #endif
>>>> >>>>>>
>>>> >>>>>>  #ifdef L_gcov_merge_single
>>>> >>>>>>  void __gcov_merge_single (gcov_type *counters  __attribute__ ((unused)),
>>>> >>>>>> -                          unsigned n_counters __attribute__ ((unused))) {}
>>>> >>>>>> +                          unsigned n_counters __attribute__ ((unused)),
>>>> >>>>>> +                          unsigned gcov_type *src __attribute__ ((unused)),
>>>> >>>>>> +                          unsigned w __attribute__ ((unused))) {}
>>>> >>>>>>  #endif
>>>> >>>>>>
>>>> >>>>>>  #ifdef L_gcov_merge_delta
>>>> >>>>>>  void __gcov_merge_delta (gcov_type *counters  __attribute__ ((unused)),
>>>> >>>>>> -                         unsigned n_counters __attribute__ ((unused))) {}
>>>> >>>>>> +                         unsigned n_counters __attribute__ ((unused)),
>>>> >>>>>> +                         unsigned gcov_type *src __attribute__ ((unused)),
>>>> >>>>>> +                         unsigned w __attribute__ ((unused))) {}
>>>> >>>>>>  #endif
>>>> >>>>>>
>>>> >>>>>>  #else
>>>> >>>>>>
>>>> >>>>>>  #ifdef L_gcov_merge_add
>>>> >>>>>>  /* The profile merging function that just adds the counters.  It is given
>>>> >>>>>> -   an array COUNTERS of N_COUNTERS old counters and it reads the same number
>>>> >>>>>> -   of counters from the gcov file.  */
>>>> >>>>>> +   an array COUNTERS of N_COUNTERS old counters.
>>>> >>>>>> +   When SRC==NULL, it reads the same number of counters from the gcov file.
>>>> >>>>>> +   Otherwise, it reads from SRC array. These read values will be multiplied
>>>> >>>>>> +   by weight W before adding to the old counters.  */
>>>> >>>>>>  void
>>>> >>>>>> -__gcov_merge_add (gcov_type *counters, unsigned n_counters)
>>>> >>>>>> +__gcov_merge_add (gcov_type *counters, unsigned n_counters,
>>>> >>>>>> +                  gcov_type *src, unsigned w)
>>>> >>>>>>  {
>>>> >>>>>> +  int in_mem = (src != 0);
>>>> >>>>>> +
>>>> >>>>>>    for (; n_counters; counters++, n_counters--)
>>>> >>>>>> -    *counters += gcov_read_counter ();
>>>> >>>>>> +    {
>>>> >>>>>> +      gcov_type value;
>>>> >>>>>> +
>>>> >>>>>> +      if (in_mem)
>>>> >>>>>> +        value = *(src++);
>>>> >>>>>> +      else
>>>> >>>>>> +        value = gcov_read_counter ();
>>>> >>>>>> +
>>>> >>>>>> +      *counters += w * value;
>>>> >>>>>> +    }
>>>> >>>>>>  }
>>>> >>>>>>  #endif /* L_gcov_merge_add */
>>>> >>>>>>
>>>> >>>>>>  #ifdef L_gcov_merge_ior
>>>> >>>>>>  /* The profile merging function that just adds the counters.  It is given
>>>> >>>>>> -   an array COUNTERS of N_COUNTERS old counters and it reads the same number
>>>> >>>>>> -   of counters from the gcov file.  */
>>>> >>>>>> +   an array COUNTERS of N_COUNTERS old counters.
>>>> >>>>>> +   When SRC==NULL, it reads the same number of counters from the gcov file.
>>>> >>>>>> +   Otherwise, it reads from SRC array.  */
>>>> >>>>>>  void
>>>> >>>>>> -__gcov_merge_ior (gcov_type *counters, unsigned n_counters)
>>>> >>>>>> +__gcov_merge_ior (gcov_type *counters, unsigned n_counters,
>>>> >>>>>> +                  gcov_type *src, unsigned w __attribute__ ((unused)))
>>>> >>>>>
>>>> >>>>> So the new in-memory variants are introduced for merging tool, while libgcc use gcov_read_counter
>>>> >>>>> interface?
>>>> >>>>> Perhaps we can actually just duplicate the functions to avoid runtime to do all the scalling
>>>> >>>>> and in_mem tests it won't need?
>>>> >>>>>
>>>> >>>>> I would suggest going with libgcov.h changes and clenaups first, with interface changes next
>>>> >>>>> and the gcov-tool is probably quite obvious at the end?
>>>> >>>>> Do you think you can split the patch this way?
>>>> >>>>>
>>>> >>>>> Thanks and sorry for taking long to review. I should have more time again now.
>>>> >>>>> Honza
>>>> >>>>
>>>> >>>> The libgcov.h related changes are in the attached patch. I think it
>>>> >>>> addresses your concerns. Bootstrapped and tested on
>>>> >>>> x86-64-unknown-linux-gnu. A profiledbootstrap is in progress.
>>>> >>>>
>>>> >>>> Ok for trunk if profiledbootstrap passes?
>>>> >>>
>>>> >>> Both a profiledbootstrap and LTO profiledbootstrap pass.
>>>> >>>
>>>> >>> Teresa
>>>> >>>
>>>> >>>>
>>>> >>>> Thanks,
>>>> >>>> Teresa
>>>> >>>>
>>>> >>>>
>>>> >>>> --
>>>> >>>> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413
>>>> >>>
>>>> >>>
>>>> >>>
>>>> >>> --
>>>> >>> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413
>>>> >
>>>> >
>>>> >
>>>> > --
>>>> > Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413
>
>
>
> --
> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-12-06 14:23               ` Jan Hubicka
  2013-12-10 19:02                 ` Xinliang David Li
  2013-12-12  6:05                 ` Teresa Johnson
@ 2013-12-18 17:28                 ` Xinliang David Li
  2014-01-08 22:20                   ` Rong Xu
  2014-01-08 22:10                 ` Rong Xu
  2014-01-08 22:33                 ` Rong Xu
  4 siblings, 1 reply; 55+ messages in thread
From: Xinliang David Li @ 2013-12-18 17:28 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Rong Xu, GCC Patches

>>
>>  #ifdef L_gcov_merge_ior
>>  /* The profile merging function that just adds the counters.  It is given
>> -   an array COUNTERS of N_COUNTERS old counters and it reads the same number
>> -   of counters from the gcov file.  */
>> +   an array COUNTERS of N_COUNTERS old counters.
>> +   When SRC==NULL, it reads the same number of counters from the gcov file.
>> +   Otherwise, it reads from SRC array.  */
>>  void
>> -__gcov_merge_ior (gcov_type *counters, unsigned n_counters)
>> +__gcov_merge_ior (gcov_type *counters, unsigned n_counters,
>> +                  gcov_type *src, unsigned w __attribute__ ((unused)))
>
> So the new in-memory variants are introduced for merging tool, while libgcc use gcov_read_counter
> interface?
> Perhaps we can actually just duplicate the functions to avoid runtime to do all the scalling
> and in_mem tests it won't need?


I thought about this one a little. How about making the interface
change conditionally, but still share the implementation?  The merge
function bodies mostly remain unchanged and there is no runtime
penalty for libgcov.  The new macros can be shared across most of the
mergers.

#ifdef IN_PREOFILE_TOOL
#define GCOV_MERGE_EXTRA_ARGS  gcov_type *src, unsigned w
#define GCOV_READ_COUNTER  *(src++) * w
#else
#define GCOV_MERGE_EXTRA_ARGS
#define GCOV_READ_COUNTER gcov_read_counter ()
#endif

__gcov_merge_add (gcov_type *counters, unsigned n_counters,
                                  GCOV_MERGE_EXTRA_ARGS)
{

 for (; n_counters; counters++, n_counters--)
  {
      *counters += GCOV_READ_COUNTER ;
   }

}

thanks,

David

>
> I would suggest going with libgcov.h changes and clenaups first, with interface changes next
> and the gcov-tool is probably quite obvious at the end?
> Do you think you can split the patch this way?
>
> Thanks and sorry for taking long to review. I should have more time again now.
> Honza

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-12-06 14:23               ` Jan Hubicka
                                   ` (2 preceding siblings ...)
  2013-12-18 17:28                 ` Xinliang David Li
@ 2014-01-08 22:10                 ` Rong Xu
  2014-01-09 12:07                   ` Jan Hubicka
  2014-01-08 22:33                 ` Rong Xu
  4 siblings, 1 reply; 55+ messages in thread
From: Rong Xu @ 2014-01-08 22:10 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Xinliang David Li, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 10693 bytes --]

Here is the patch that addresses Honza's concern about bss increment.
It just makes this_prg a local variable.

Some comments are inlined.

On Fri, Dec 6, 2013 at 6:23 AM, Jan Hubicka <hubicka@ucw.cz> wrote:

> ....
> Do you know how the size of libgcov changed with your patch?
> Quick check of current mainline on compiling empty main gives:
>
> jh@gcc10:~/trunk/build/gcc$ cat t.c
> main()
> {
> }
> jh@gcc10:~/trunk/build/gcc$ ./xgcc -B ./ -O2 -fprofile-generate -o a.out-new --static t.c
> jh@gcc10:~/trunk/build/gcc$ gcc -O2 -fprofile-generate -o a.out-old --static t.c
> jh@gcc10:~/trunk/build/gcc$ size a.out-old
>    text    data     bss     dec     hex filename
>  608141    3560   16728  628429   996cd a.out-old
> jh@gcc10:~/trunk/build/gcc$ size a.out-new
>    text    data     bss     dec     hex filename
>  612621    3688   22880  639189   9c0d5 a.out-new
>
> Without profiling I get:
> jh@gcc10:~/trunk/build/gcc$ size a.out-new-no
> jh@gcc10:~/trunk/build/gcc$ size a.out-old-no
>    text    data     bss     dec     hex filename
>  599719    3448   12568  615735   96537 a.out-old-no
>    text    data     bss     dec     hex filename
>  600247    3448   12568  616263   96747 a.out-new-no
>
> Quite big for empty program, but mostly glibc fault, I suppose
> (that won't be an issue for embedded platforms). But anyway
> we increased text size overhead from 8k to 12k, BSS size
> overhead from 4k to 10k and data by another 1k.
>

I think it would more fair to compare r204729 and r204730. Your
comparison had some other changes in libgcov such as time_profiler and
indirecto_call_profiler_v2.

Using the same empty t.c, for r204729, we have
xur2%208:gcc >> ./xgcc -B ./ -O2 -fprofile-generate --static -o
a.out-r204729 t.c
xur2%209:gcc >> size a.out-r204729
   text   data    bss    dec    hex filename
 803207   6352  15448 825007  c96af a.out-r204729
xur2%210:gcc >> ./xgcc -B ./ -O2 --static -o a.out-r204729-no t.c
xur2%211:gcc >> size a.out-r204729-no
   text   data    bss    dec    hex filename
 790337   6112  11336 807785  c5369 a.out-r204729-no

For r204730, we have
xur2%216:gcc >> ./xgcc -B ./ -O2 -fprofile-generate --static -o
a.out-r204730 t.c
xur2%217:gcc >> size a.out-r204730
   text   data    bss    dec    hex filename
 802919   6384  21592 830895  cadaf a.out-r204730
xur2%218:gcc >> ./xgcc -B ./ -O2  --static -o a.out-r204730-no t.c
xur2%219:gcc >> size a.out-r204730-no
   text   data    bss    dec    hex filename
 790337   6112  11336 807785  c5369 a.out-r204730-no

r204730 actually has smaller text, data size with -fprofile-generate.
You are right about there are 6kb more bss space due to the static
variables introduced. It mostly caused by this_prg object.

With the attached trunk patch that localizes this_prg, we have
xur2%42:fdo >> size a.out-new
   text   data    bss    dec    hex filename
 803479   6456  15512 825447  c9867 a.out-new
xur2%43:fdo >> size a.out-new-no
   text   data    bss    dec    hex filename
 790545   6112  11368 808025  c5459 a.out-new-no

We are now using 64 more bytes in m64.

Objects size for r204730:
   text   data    bss    dec    hex filename
     57      0      0     57     39 _gcov_average_profiler.o
     66      0      0     66     42 _gcov_dump.o
    516      0      0    516    204 _gcov_execle.o
    476      0      0    476    1dc _gcov_execl.o
    476      0      0    476    1dc _gcov_execlp.o
    108      0      0    108     6c _gcov_execve.o
     98      0      0     98     62 _gcov_execv.o
     98      0      0     98     62 _gcov_execvp.o
    126      0     40    166     a6 _gcov_flush.o
    101      0      0    101     65 _gcov_fork.o
    122      0      0    122     7a _gcov_indirect_call_profiler.o
    178      0     16    194     c2 _gcov_indirect_call_profiler_v2.o
     89      0      0     89     59 _gcov_interval_profiler.o
     52      0      0     52     34 _gcov_ior_profiler.o
    126      0      0    126     7e _gcov_merge_add.o
    242      0      0    242     f2 _gcov_merge_delta.o
    126      0      0    126     7e _gcov_merge_ior.o
    251      0      0    251     fb _gcov_merge_single.o
    156      0      0    156     9c _gcov_merge_time_profile.o
   9252      0   6144  15396   3c24 _gcov.o
    115      0      0    115     73 _gcov_one_value_profiler.o
     69      0      0     69     45 _gcov_pow2_profiler.o
     66      0      0     66     42 _gcov_reset.o
     77      0      8     85     55 _gcov_time_profiler.o

Objects size for r204729:
   text   data    bss    dec    hex filename
     57      0      0     57     39 _gcov_average_profiler.o
     72      0      0     72     48 _gcov_dump.o
    516      0      0    516    204 _gcov_execle.o
    476      0      0    476    1dc _gcov_execl.o
    476      0      0    476    1dc _gcov_execlp.o
    108      0      0    108     6c _gcov_execve.o
     98      0      0     98     62 _gcov_execv.o
     98      0      0     98     62 _gcov_execvp.o
    101      0      0    101     65 _gcov_fork.o
    122      0      0    122     7a _gcov_indirect_call_profiler.o
    178      0     16    194     c2 _gcov_indirect_call_profiler_v2.o
     89      0      0     89     59 _gcov_interval_profiler.o
     52      0      0     52     34 _gcov_ior_profiler.o
    126      0      0    126     7e _gcov_merge_add.o
    242      0      0    242     f2 _gcov_merge_delta.o
    126      0      0    126     7e _gcov_merge_ior.o
    251      0      0    251     fb _gcov_merge_single.o
    156      0      0    156     9c _gcov_merge_time_profile.o
   9564      0     64   9628   259c _gcov.o
    115      0      0    115     73 _gcov_one_value_profiler.o
     69      0      0     69     45 _gcov_pow2_profiler.o
     72      0      0     72     48 _gcov_reset.o
     77      0      0     77     4d _gcov_time_profiler.o


>    text    data     bss     dec     hex filename
>     126       0       0     126      7e _gcov_merge_add.o (ex libgcov.a)
>     251       0       0     251      fb _gcov_merge_single.o (ex libgcov.a)
>     242       0       0     242      f2 _gcov_merge_delta.o (ex libgcov.a)
>     126       0       0     126      7e _gcov_merge_ior.o (ex libgcov.a)
>     156       0       0     156      9c _gcov_merge_time_profile.o (ex libgcov.a)
>      89       0       0      89      59 _gcov_interval_profiler.o (ex libgcov.a)
>      69       0       0      69      45 _gcov_pow2_profiler.o (ex libgcov.a)
>     115       0       0     115      73 _gcov_one_value_profiler.o (ex libgcov.a)
>     122       0       0     122      7a _gcov_indirect_call_profiler.o (ex libgcov.a)
>      57       0       0      57      39 _gcov_average_profiler.o (ex libgcov.a)
>      52       0       0      52      34 _gcov_ior_profiler.o (ex libgcov.a)
>     178       0      16     194      c2 _gcov_indirect_call_profiler_v2.o (ex libgcov.a)
>      77       0       8      85      55 _gcov_time_profiler.o (ex libgcov.a)
>     126       0      40     166      a6 _gcov_flush.o (ex libgcov.a)
>     101       0       0     101      65 _gcov_fork.o (ex libgcov.a)
>     471       0       0     471     1d7 _gcov_execl.o (ex libgcov.a)
>     471       0       0     471     1d7 _gcov_execlp.o (ex libgcov.a)
>     524       0       0     524     20c _gcov_execle.o (ex libgcov.a)
>      98       0       0      98      62 _gcov_execv.o (ex libgcov.a)
>      98       0       0      98      62 _gcov_execvp.o (ex libgcov.a)
>     108       0       0     108      6c _gcov_execve.o (ex libgcov.a)
>      66       0       0      66      42 _gcov_reset.o (ex libgcov.a)
>      66       0       0      66      42 _gcov_dump.o (ex libgcov.a)
>    9505       0    6144   15649    3d21 _gcov.o (ex libgcov.a)
>
> I think we definitely need to move those 6k of bss space out.  I think those are new
> static vars you introduced that I think are unsafe anyway because multiple streaming
> may run at once in threaded program where locking mechanizm fails.
> (it will probably do other bad things, but definitely we do not want to
> conflict on things like filename to write into).

I don't understand why static variables can cause any safety issue in
multi-thread programs.
For any process, gcov_dump will be called only once and there will be
one instance of globals.
Where is the problem?
The file locking is for multi-process program. For that case, each
process has its own instance of globals.
I don't see this causes any new problems.


>
> Compared to my system gcov:
>    text    data     bss     dec     hex filename
>    9765       0      64    9829    2665 _gcov.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     125       0       0     125      7d _gcov_merge_add.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     251       0       0     251      fb _gcov_merge_single.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     242       0       0     242      f2 _gcov_merge_delta.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     101       0       0     101      65 _gcov_fork.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     471       0       0     471     1d7 _gcov_execl.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     471       0       0     471     1d7 _gcov_execlp.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     524       0       0     524     20c _gcov_execle.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>      98       0       0      98      62 _gcov_execv.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>      98       0       0      98      62 _gcov_execvp.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     108       0       0     108      6c _gcov_execve.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>      72       0       0      72      48 _gcov_reset.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>      72       0       0      72      48 _gcov_dump.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>      89       0       0      89      59 _gcov_interval_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>      69       0       0      69      45 _gcov_pow2_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     115       0       0     115      73 _gcov_one_value_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     122       0       0     122      7a _gcov_indirect_call_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>      57       0       0      57      39 _gcov_average_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>      52       0       0      52      34 _gcov_ior_profiler.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>     125       0       0     125      7d _gcov_merge_ior.o (ex ./lib64/gcc/x86_64-suse-linux/4.8/libgcov.a)
>

[-- Attachment #2: libgcov_patch.txt --]
[-- Type: text/plain, Size: 5364 bytes --]

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

	* libgcc/libgcov-driver.c (this_prg): make it local to save
        bss space.
	(gcov_exit_compute_summary): Ditto.
	(gcov_exit_merge_gcda): Ditto.
	(gcov_exit_merge_summary): Ditto.
	(gcov_exit_dump_gcov): Ditto.

Index: libgcc/libgcov-driver.c
===================================================================
--- libgcc/libgcov-driver.c	(revision 206437)
+++ libgcc/libgcov-driver.c	(working copy)
@@ -303,8 +303,6 @@ gcov_compute_histogram (struct gcov_summary *sum)
     }
 }
 
-/* summary for program.  */
-static struct gcov_summary this_prg;
 /* gcda filename.  */
 static char *gi_filename;
 /* buffer for the fn_data from another program.  */
@@ -317,10 +315,10 @@ static struct gcov_summary_buffer *sum_buffer;
 static int run_accounted = 0;
 
 /* This funtions computes the program level summary and the histo-gram.
-   It computes and returns CRC32  and stored summari in THIS_PRG.  */
+   It computes and returns CRC32 and stored summary in THIS_PRG.  */
 
 static gcov_unsigned_t
-gcov_exit_compute_summary (void)
+gcov_exit_compute_summary (struct gcov_summary *this_prg)
 {
   struct gcov_info *gi_ptr;
   const struct gcov_fn_info *gfi_ptr;
@@ -332,7 +330,7 @@ static gcov_unsigned_t
   gcov_unsigned_t crc32 = 0;
 
   /* Find the totals for this execution.  */
-  memset (&this_prg, 0, sizeof (this_prg));
+  memset (this_prg, 0, sizeof (*this_prg));
   for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
     {
       crc32 = crc32_unsigned (crc32, gi_ptr->stamp);
@@ -357,7 +355,7 @@ static gcov_unsigned_t
               if (!gi_ptr->merge[t_ix])
                 continue;
 
-              cs_ptr = &this_prg.ctrs[t_ix];
+              cs_ptr = &(this_prg->ctrs[t_ix]);
               cs_ptr->num += ci_ptr->num;
               crc32 = crc32_unsigned (crc32, ci_ptr->num);
 
@@ -371,7 +369,7 @@ static gcov_unsigned_t
             }
         }
     }
-  gcov_compute_histogram (&this_prg);
+  gcov_compute_histogram (this_prg);
   return crc32;
 }
 
@@ -393,6 +391,7 @@ struct gcov_filename_aux{
 static int
 gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
                       struct gcov_summary *prg_p,
+                      struct gcov_summary *this_prg,
                       gcov_position_t *summary_pos_p,
                       gcov_position_t *eof_pos_p,
 		      gcov_unsigned_t crc32)
@@ -446,7 +445,7 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
         goto next_summary;
 
       for (t_ix = 0; t_ix != GCOV_COUNTERS_SUMMABLE; t_ix++)
-        if (tmp.ctrs[t_ix].num != this_prg.ctrs[t_ix].num)
+        if (tmp.ctrs[t_ix].num != this_prg->ctrs[t_ix].num)
           goto next_summary;
       *prg_p = tmp;
       *summary_pos_p = *eof_pos_p;
@@ -636,7 +635,7 @@ gcov_exit_write_gcda (const struct gcov_info *gi_p
 
 static int
 gcov_exit_merge_summary (const struct gcov_info *gi_ptr, struct gcov_summary *prg,
-			 gcov_unsigned_t crc32,
+                         struct gcov_summary *this_prg, gcov_unsigned_t crc32,
 			 struct gcov_summary *all_prg __attribute__ ((unused)))
 {
   struct gcov_ctr_summary *cs_prg, *cs_tprg;
@@ -650,7 +649,7 @@ gcov_exit_merge_summary (const struct gcov_info *g
   for (t_ix = 0; t_ix < GCOV_COUNTERS_SUMMABLE; t_ix++)
     {
       cs_prg = &(prg->ctrs[t_ix]);
-      cs_tprg = &this_prg.ctrs[t_ix];
+      cs_tprg = &(this_prg->ctrs[t_ix]);
 
       if (gi_ptr->merge[t_ix])
         {
@@ -719,7 +718,8 @@ gcov_exit_merge_summary (const struct gcov_info *g
 
 static void
 gcov_exit_dump_gcov (struct gcov_info *gi_ptr, struct gcov_filename_aux *gf,
-		     gcov_unsigned_t crc32, struct gcov_summary *all_prg)
+		     gcov_unsigned_t crc32, struct gcov_summary *all_prg,
+                     struct gcov_summary *this_prg)
 {
   struct gcov_summary prg; /* summary for this object over all program.  */
   int error;
@@ -743,7 +743,7 @@ gcov_exit_dump_gcov (struct gcov_info *gi_ptr, str
           gcov_error ("profiling:%s:Not a gcov data file\n", gi_filename);
           goto read_fatal;
         }
-      error = gcov_exit_merge_gcda (gi_ptr, &prg, &summary_pos, &eof_pos,
+      error = gcov_exit_merge_gcda (gi_ptr, &prg, this_prg, &summary_pos, &eof_pos,
 				    crc32);
       if (error == -1)
         goto read_fatal;
@@ -757,7 +757,7 @@ gcov_exit_dump_gcov (struct gcov_info *gi_ptr, str
       summary_pos = eof_pos;
     }
 
-  error = gcov_exit_merge_summary (gi_ptr, &prg, crc32, all_prg);
+  error = gcov_exit_merge_summary (gi_ptr, &prg, this_prg, crc32, all_prg);
   if (error == -1)
     goto read_fatal;
 
@@ -787,13 +787,14 @@ gcov_exit (void)
   struct gcov_filename_aux gf;
   gcov_unsigned_t crc32;
   struct gcov_summary all_prg;
+  struct gcov_summary this_prg;
 
   /* Prevent the counters from being dumped a second time on exit when the
      application already wrote out the profile using __gcov_dump().  */
   if (gcov_dump_complete)
     return;
 
-  crc32 = gcov_exit_compute_summary ();
+  crc32 = gcov_exit_compute_summary (&this_prg);
 
   allocate_filename_struct (&gf);
 #if !GCOV_LOCKED
@@ -802,7 +803,7 @@ gcov_exit (void)
 
   /* Now merge each file.  */
   for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
-    gcov_exit_dump_gcov (gi_ptr, &gf, crc32, &all_prg);
+    gcov_exit_dump_gcov (gi_ptr, &gf, crc32, &all_prg, &this_prg);
   run_accounted = 1;
 
   if (gi_filename)

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-12-18 17:28                 ` Xinliang David Li
@ 2014-01-08 22:20                   ` Rong Xu
  2014-01-09  6:46                     ` Xinliang David Li
  0 siblings, 1 reply; 55+ messages in thread
From: Rong Xu @ 2014-01-08 22:20 UTC (permalink / raw)
  To: Xinliang David Li; +Cc: Jan Hubicka, GCC Patches

On Wed, Dec 18, 2013 at 9:28 AM, Xinliang David Li <davidxl@google.com> wrote:
>>>
>>>  #ifdef L_gcov_merge_ior
>>>  /* The profile merging function that just adds the counters.  It is given
>>> -   an array COUNTERS of N_COUNTERS old counters and it reads the same number
>>> -   of counters from the gcov file.  */
>>> +   an array COUNTERS of N_COUNTERS old counters.
>>> +   When SRC==NULL, it reads the same number of counters from the gcov file.
>>> +   Otherwise, it reads from SRC array.  */
>>>  void
>>> -__gcov_merge_ior (gcov_type *counters, unsigned n_counters)
>>> +__gcov_merge_ior (gcov_type *counters, unsigned n_counters,
>>> +                  gcov_type *src, unsigned w __attribute__ ((unused)))
>>
>> So the new in-memory variants are introduced for merging tool, while libgcc use gcov_read_counter
>> interface?
>> Perhaps we can actually just duplicate the functions to avoid runtime to do all the scalling
>> and in_mem tests it won't need?
>
>
> I thought about this one a little. How about making the interface
> change conditionally, but still share the implementation?  The merge
> function bodies mostly remain unchanged and there is no runtime
> penalty for libgcov.  The new macros can be shared across most of the
> mergers.
>
> #ifdef IN_PREOFILE_TOOL
> #define GCOV_MERGE_EXTRA_ARGS  gcov_type *src, unsigned w
> #define GCOV_READ_COUNTER  *(src++) * w
> #else
> #define GCOV_MERGE_EXTRA_ARGS
> #define GCOV_READ_COUNTER gcov_read_counter ()
> #endif
>
> __gcov_merge_add (gcov_type *counters, unsigned n_counters,
>                                   GCOV_MERGE_EXTRA_ARGS)
> {
>
>  for (; n_counters; counters++, n_counters--)
>   {
>       *counters += GCOV_READ_COUNTER ;
>    }
>
> }
>
> thanks,

Personally I don't think the run time test of in_mem will cause any
issue. This is in profile dumping, why don't we care a few more cycle
heres? it won't pollute the profile.

If you really don't like that, we can use the above approach, or I can
hide the logic in gcov_read_counter(), i.e. overload
gcov_read_counter() in profile_tool. For that, I will need a new
global variable SRC and set it before calling the merge function.
I would prefer to keep weight in _gcov_merge_* argument list.

What do you think?

-Rong

>
> David
>
>>
>> I would suggest going with libgcov.h changes and clenaups first, with interface changes next
>> and the gcov-tool is probably quite obvious at the end?
>> Do you think you can split the patch this way?
>>
>> Thanks and sorry for taking long to review. I should have more time again now.
>> Honza

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2013-12-06 14:23               ` Jan Hubicka
                                   ` (3 preceding siblings ...)
  2014-01-08 22:10                 ` Rong Xu
@ 2014-01-08 22:33                 ` Rong Xu
  2014-01-09 20:11                   ` H.J. Lu
  4 siblings, 1 reply; 55+ messages in thread
From: Rong Xu @ 2014-01-08 22:33 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Xinliang David Li, GCC Patches

On Fri, Dec 6, 2013 at 6:23 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>> @@ -325,6 +311,9 @@ static struct gcov_summary all_prg;
>>  #endif
>>  /* crc32 for this program.  */
>>  static gcov_unsigned_t crc32;
>> +/* Use this summary checksum rather the computed one if the value is
>> + *    non-zero.  */
>> +static gcov_unsigned_t saved_summary_checksum;
>
> Why do you need to save the checksum? Won't it reset summary back with multiple streaming?

This was for the gcov_tool. checksum will be recomputed in gcov_exit
and the value will depend on
the order of gcov_info list. (the order will be different after
reading from gcda files to memory). The purpose was
to have the same summary_checksum so that I can get identical gcov-dump output.

>
> I would really like to avoid introducing those static vars that are used exclusively
> by gcov_exit.  What about putting them into an gcov_context structure that
> is passed around the functions that was broken out?

With my recently patch the localizes this_prg, we only use 64 more
bytes in bss. Do you still we have to remove
all these statics?

>

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2014-01-08 22:20                   ` Rong Xu
@ 2014-01-09  6:46                     ` Xinliang David Li
  0 siblings, 0 replies; 55+ messages in thread
From: Xinliang David Li @ 2014-01-09  6:46 UTC (permalink / raw)
  To: Rong Xu; +Cc: Jan Hubicka, GCC Patches

On Wed, Jan 8, 2014 at 2:20 PM, Rong Xu <xur@google.com> wrote:
> On Wed, Dec 18, 2013 at 9:28 AM, Xinliang David Li <davidxl@google.com> wrote:
>>>>
>>>>  #ifdef L_gcov_merge_ior
>>>>  /* The profile merging function that just adds the counters.  It is given
>>>> -   an array COUNTERS of N_COUNTERS old counters and it reads the same number
>>>> -   of counters from the gcov file.  */
>>>> +   an array COUNTERS of N_COUNTERS old counters.
>>>> +   When SRC==NULL, it reads the same number of counters from the gcov file.
>>>> +   Otherwise, it reads from SRC array.  */
>>>>  void
>>>> -__gcov_merge_ior (gcov_type *counters, unsigned n_counters)
>>>> +__gcov_merge_ior (gcov_type *counters, unsigned n_counters,
>>>> +                  gcov_type *src, unsigned w __attribute__ ((unused)))
>>>
>>> So the new in-memory variants are introduced for merging tool, while libgcc use gcov_read_counter
>>> interface?
>>> Perhaps we can actually just duplicate the functions to avoid runtime to do all the scalling
>>> and in_mem tests it won't need?
>>
>>
>> I thought about this one a little. How about making the interface
>> change conditionally, but still share the implementation?  The merge
>> function bodies mostly remain unchanged and there is no runtime
>> penalty for libgcov.  The new macros can be shared across most of the
>> mergers.
>>
>> #ifdef IN_PREOFILE_TOOL
>> #define GCOV_MERGE_EXTRA_ARGS  gcov_type *src, unsigned w
>> #define GCOV_READ_COUNTER  *(src++) * w
>> #else
>> #define GCOV_MERGE_EXTRA_ARGS
>> #define GCOV_READ_COUNTER gcov_read_counter ()
>> #endif
>>
>> __gcov_merge_add (gcov_type *counters, unsigned n_counters,
>>                                   GCOV_MERGE_EXTRA_ARGS)
>> {
>>
>>  for (; n_counters; counters++, n_counters--)
>>   {
>>       *counters += GCOV_READ_COUNTER ;
>>    }
>>
>> }
>>
>> thanks,
>
> Personally I don't think the run time test of in_mem will cause any
> issue. This is in profile dumping, why don't we care a few more cycle
> heres? it won't pollute the profile.
>
> If you really don't like that, we can use the above approach, or I can
> hide the logic in gcov_read_counter(), i.e. overload
> gcov_read_counter() in profile_tool. For that, I will need a new
> global variable SRC and set it before calling the merge function.
> I would prefer to keep weight in _gcov_merge_* argument list.
>
> What do you think?

or perhaps just define an inline wrapper function to read counter.
This wrapper function takes src as input. if src is NULL, call
gcov_read_counter.

David

>
> -Rong
>
>>
>> David
>>
>>>
>>> I would suggest going with libgcov.h changes and clenaups first, with interface changes next
>>> and the gcov-tool is probably quite obvious at the end?
>>> Do you think you can split the patch this way?
>>>
>>> Thanks and sorry for taking long to review. I should have more time again now.
>>> Honza

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2014-01-08 22:10                 ` Rong Xu
@ 2014-01-09 12:07                   ` Jan Hubicka
  0 siblings, 0 replies; 55+ messages in thread
From: Jan Hubicka @ 2014-01-09 12:07 UTC (permalink / raw)
  To: Rong Xu; +Cc: Jan Hubicka, Xinliang David Li, GCC Patches

> 
> I don't understand why static variables can cause any safety issue in
> multi-thread programs.
> For any process, gcov_dump will be called only once and there will be
> one instance of globals.

(v)fork, failing exec and other cases can lead to gcov_flush being called
several times  and that calls gcov_exit that uses the global state.

I believe that a simple program containing two threads that both are executing
execve of non-existing file will trigger concurent writes on systems not
having __gthread_mutex_lock  that seems to be in place to prevent it.

I wonder if we should move move locking into gcov_exit itself?
Is there somethign that will promise us that paralellel streaming invoked by
other thread at the failing execve is not going to end up in parallel with
gcov_exit called via atexit handler?

In any case for sanity of setups without gthread support, I think we need to
keep eye on not doing something evil in this case - like writting into random
file names or corrupting memory/files.

> 2014-01-08  Rong Xu  <xur@google.com>
> 
> 	* libgcc/libgcov-driver.c (this_prg): make it local to save
>         bss space.
> 	(gcov_exit_compute_summary): Ditto.
> 	(gcov_exit_merge_gcda): Ditto.
> 	(gcov_exit_merge_summary): Ditto.
> 	(gcov_exit_dump_gcov): Ditto.

This is OK, thanks!
Honza

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2014-01-08 22:33                 ` Rong Xu
@ 2014-01-09 20:11                   ` H.J. Lu
  2014-01-09 20:13                     ` Rong Xu
  0 siblings, 1 reply; 55+ messages in thread
From: H.J. Lu @ 2014-01-09 20:11 UTC (permalink / raw)
  To: Rong Xu; +Cc: Jan Hubicka, Xinliang David Li, GCC Patches

On Wed, Jan 8, 2014 at 2:33 PM, Rong Xu <xur@google.com> wrote:
> On Fri, Dec 6, 2013 at 6:23 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>>> @@ -325,6 +311,9 @@ static struct gcov_summary all_prg;
>>>  #endif
>>>  /* crc32 for this program.  */
>>>  static gcov_unsigned_t crc32;
>>> +/* Use this summary checksum rather the computed one if the value is
>>> + *    non-zero.  */
>>> +static gcov_unsigned_t saved_summary_checksum;
>>
>> Why do you need to save the checksum? Won't it reset summary back with multiple streaming?
>
> This was for the gcov_tool. checksum will be recomputed in gcov_exit
> and the value will depend on
> the order of gcov_info list. (the order will be different after
> reading from gcda files to memory). The purpose was
> to have the same summary_checksum so that I can get identical gcov-dump output.
>
>>
>> I would really like to avoid introducing those static vars that are used exclusively
>> by gcov_exit.  What about putting them into an gcov_context structure that
>> is passed around the functions that was broken out?
>
> With my recently patch the localizes this_prg, we only use 64 more
> bytes in bss. Do you still we have to remove
> all these statics?
>
>>

libgcc ChangeLog entries should be in libgcc/ChangeLog,
not gcc/ChangeLog.  I checked in a patch to move them
to libgcc/ChangeLog.

-- 
H.J.

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFC] libgcov.c re-factoring and offline profile-tool
  2014-01-09 20:11                   ` H.J. Lu
@ 2014-01-09 20:13                     ` Rong Xu
  0 siblings, 0 replies; 55+ messages in thread
From: Rong Xu @ 2014-01-09 20:13 UTC (permalink / raw)
  To: H.J. Lu; +Cc: Jan Hubicka, Xinliang David Li, GCC Patches

My bad.
Thanks for the fix!

-Rong

On Thu, Jan 9, 2014 at 11:47 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Wed, Jan 8, 2014 at 2:33 PM, Rong Xu <xur@google.com> wrote:
>> On Fri, Dec 6, 2013 at 6:23 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>>>> @@ -325,6 +311,9 @@ static struct gcov_summary all_prg;
>>>>  #endif
>>>>  /* crc32 for this program.  */
>>>>  static gcov_unsigned_t crc32;
>>>> +/* Use this summary checksum rather the computed one if the value is
>>>> + *    non-zero.  */
>>>> +static gcov_unsigned_t saved_summary_checksum;
>>>
>>> Why do you need to save the checksum? Won't it reset summary back with multiple streaming?
>>
>> This was for the gcov_tool. checksum will be recomputed in gcov_exit
>> and the value will depend on
>> the order of gcov_info list. (the order will be different after
>> reading from gcda files to memory). The purpose was
>> to have the same summary_checksum so that I can get identical gcov-dump output.
>>
>>>
>>> I would really like to avoid introducing those static vars that are used exclusively
>>> by gcov_exit.  What about putting them into an gcov_context structure that
>>> is passed around the functions that was broken out?
>>
>> With my recently patch the localizes this_prg, we only use 64 more
>> bytes in bss. Do you still we have to remove
>> all these statics?
>>
>>>
>
> libgcc ChangeLog entries should be in libgcc/ChangeLog,
> not gcc/ChangeLog.  I checked in a patch to move them
> to libgcc/ChangeLog.
>
> --
> H.J.

^ permalink raw reply	[flat|nested] 55+ messages in thread

end of thread, other threads:[~2014-01-09 20:13 UTC | newest]

Thread overview: 55+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-11-01 21:08 [RFC] libgcov.c re-factoring and offline profile-tool Rong Xu
2013-11-04  6:55 ` Xinliang David Li
2013-11-04  9:55   ` Jan Hubicka
2013-11-04 22:46     ` Rong Xu
2013-11-04 22:52       ` Rong Xu
2013-11-04 22:56       ` Xinliang David Li
2013-11-05  0:22       ` Xinliang David Li
2013-11-05  9:23         ` Jan Hubicka
2013-11-05  9:34           ` Jakub Jelinek
2013-11-05 17:04             ` Xinliang David Li
2013-11-06 21:54               ` Rong Xu
2013-11-06 23:33                 ` Xinliang David Li
2013-11-07  1:09                   ` Rong Xu
2013-11-07  2:49                     ` Joseph S. Myers
2013-11-07 17:40                       ` Rong Xu
2013-11-07 17:48                         ` Joseph S. Myers
2013-11-08  1:13                           ` Rong Xu
2013-11-08 18:45                             ` Xinliang David Li
2013-11-08 19:01                               ` Xinliang David Li
2013-11-08 19:10                               ` Rong Xu
2013-11-08 19:13                                 ` Rong Xu
2013-11-08 19:39                                   ` Xinliang David Li
2013-11-08 21:46                                   ` Joseph S. Myers
2013-11-08 19:30                                 ` Xinliang David Li
2013-11-08 19:42                                   ` Rong Xu
2013-11-08 20:06                                     ` Xinliang David Li
2013-11-08 20:31                                       ` Rong Xu
2013-11-11 16:22                 ` Jan Hubicka
2013-11-11 21:03                   ` Rong Xu
2013-11-11 16:16       ` Jan Hubicka
2013-11-11 19:10         ` Rong Xu
2013-11-13  6:18           ` Rong Xu
2013-11-18 21:52             ` Rong Xu
2013-11-18 22:18               ` Xinliang David Li
2013-11-21  8:21                 ` Rong Xu
2013-12-06 14:23               ` Jan Hubicka
2013-12-10 19:02                 ` Xinliang David Li
2013-12-10 20:01                   ` Teresa Johnson
2013-12-12  6:05                 ` Teresa Johnson
2013-12-12 20:11                   ` Teresa Johnson
2013-12-16 20:55                     ` Xinliang David Li
2013-12-16 21:17                       ` Teresa Johnson
2013-12-16 21:51                         ` Xinliang David Li
2013-12-16 22:34                           ` Jan Hubicka
2013-12-16 22:48                             ` Xinliang David Li
2013-12-17 15:48                               ` Teresa Johnson
2013-12-17 17:58                                 ` Xinliang David Li
2013-12-18 17:28                 ` Xinliang David Li
2014-01-08 22:20                   ` Rong Xu
2014-01-09  6:46                     ` Xinliang David Li
2014-01-08 22:10                 ` Rong Xu
2014-01-09 12:07                   ` Jan Hubicka
2014-01-08 22:33                 ` Rong Xu
2014-01-09 20:11                   ` H.J. Lu
2014-01-09 20:13                     ` Rong Xu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).