[v3] gcov: Add __gcov_info_to_gdca()

Message ID 20210723093911.81759-1-sebastian.huber@embedded-brains.de
State New
Headers show
Series
  • [v3] gcov: Add __gcov_info_to_gdca()
Related show

Commit Message

Sebastian Huber July 23, 2021, 9:39 a.m.
Add __gcov_info_to_gcda() to libgcov to get the gcda data for a gcda info in a
freestanding environment.  It is intended to be used with the
-fprofile-info-section option.  A crude test program which doesn't use a linker
script is (use "gcc -coverage -fprofile-info-section -lgcc test.c" to compile
it):

  #include <gcov.h>
  #include <stdio.h>
  #include <stdlib.h>

  extern const struct gcov_info *my_info;

  static void
  filename (const char *f, void *arg)
  {
    printf("filename: %s\n", f);
  }

  static void
  dump (const void *d, unsigned n, void *arg)
  {
    const unsigned char *c = d;

    for (unsigned i = 0; i < n; ++i)
      printf ("%02x", c[i]);
  }

  static void *
  allocate (unsigned length, void *arg)
  {
    return malloc (length);
  }

  int main()
  {
    __asm__ volatile (".set my_info, .LPBX2");
    __gcov_info_to_gcda (my_info, filename, dump, allocate, NULL);
    return 0;
  }

With this patch, <stdint.h> is included in libgcov-driver.c even if
inhibit_libc is defined.  This header file should be also available for
freestanding environments.  If this is not the case, then we have to define
intptr_t somehow.

The patch removes one use of memset() which makes the <string.h> include
superfluous.

gcc/

	* gcov-io.h (gcov_write): Declare.
	* gcov-io.c (gcov_write): New.
	(gcov_write_counter): Remove.
	(gcov_write_tag_length): Likewise.
	(gcov_write_summary): Replace gcov_write_tag_length() with calls to
	gcov_write_unsigned().
	* doc/invoke.texi (fprofile-info-section): Mention
	__gcov_info_to_gdca().

gcc/testsuite/

	* gcc.dg/gcov-info-to-gcda.c: New test.

libgcc/

	* Makefile.in (LIBGCOV_DRIVER): Add _gcov_info_to_gcda.
	* gcov.h (gcov_info): Declare.
	(__gcov_info_to_gdca): Likewise.
	* libgcov.h (gcov_write_counter): Remove.
	(gcov_write_tag_length): Likewise.
	* libgcov-driver.c (#include <stdint.h>): New.
	(#include <string.h>): Remove.
	(NEED_L_GCOV): Conditionally define.
	(NEED_L_GCOV_INFO_TO_GCDA): Likewise.
	(are_all_counters_zero): New.
	(gcov_dump_handler): Likewise.
	(gcov_allocate_handler): Likewise.
	(dump_unsigned): Likewise.
	(dump_counter): Likewise.
	(write_topn_counters): Add dump_fn, allocate_fn, and arg parameters.
	Use dump_unsigned() and dump_counter().
	(write_one_data): Add dump_fn, allocate_fn, and arg parameters.  Use
	dump_unsigned(), dump_counter(), and are_all_counters_zero().
	(__gcov_info_to_gcda): New.
---
 gcc/doc/invoke.texi                      |  80 +++++++--
 gcc/gcov-io.c                            |  36 ++---
 gcc/gcov-io.h                            |   1 +
 gcc/testsuite/gcc.dg/gcov-info-to-gcda.c |  60 +++++++
 libgcc/Makefile.in                       |   2 +-
 libgcc/gcov.h                            |  19 +++
 libgcc/libgcov-driver.c                  | 196 ++++++++++++++++++-----
 libgcc/libgcov.h                         |   5 -
 8 files changed, 313 insertions(+), 86 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/gcov-info-to-gcda.c

-- 
2.26.2

Comments

Martin Liška Aug. 5, 2021, 12:53 p.m. | #1
On 7/23/21 11:39 AM, Sebastian Huber wrote:
> Add __gcov_info_to_gcda() to libgcov to get the gcda data for a gcda info in a

> freestanding environment.  It is intended to be used with the

> -fprofile-info-section option.  A crude test program which doesn't use a linker

> script is (use "gcc -coverage -fprofile-info-section -lgcc test.c" to compile

> it):


The patch can be installed once the following nits are fixed:

> 

>    #include <gcov.h>

>    #include <stdio.h>

>    #include <stdlib.h>

> 

>    extern const struct gcov_info *my_info;

> 

>    static void

>    filename (const char *f, void *arg)

>    {

>      printf("filename: %s\n", f);

>    }

> 

>    static void

>    dump (const void *d, unsigned n, void *arg)

>    {

>      const unsigned char *c = d;

> 

>      for (unsigned i = 0; i < n; ++i)

>        printf ("%02x", c[i]);

>    }

> 

>    static void *

>    allocate (unsigned length, void *arg)

>    {

>      return malloc (length);

>    }

> 

>    int main()

>    {

>      __asm__ volatile (".set my_info, .LPBX2");

>      __gcov_info_to_gcda (my_info, filename, dump, allocate, NULL);

>      return 0;

>    }

> 

> With this patch, <stdint.h> is included in libgcov-driver.c even if

> inhibit_libc is defined.  This header file should be also available for

> freestanding environments.  If this is not the case, then we have to define

> intptr_t somehow.

> 

> The patch removes one use of memset() which makes the <string.h> include

> superfluous.

> 

> gcc/

> 

> 	* gcov-io.h (gcov_write): Declare.

> 	* gcov-io.c (gcov_write): New.

> 	(gcov_write_counter): Remove.

> 	(gcov_write_tag_length): Likewise.

> 	(gcov_write_summary): Replace gcov_write_tag_length() with calls to

> 	gcov_write_unsigned().

> 	* doc/invoke.texi (fprofile-info-section): Mention

> 	__gcov_info_to_gdca().

> 

> gcc/testsuite/

> 

> 	* gcc.dg/gcov-info-to-gcda.c: New test.

> 

> libgcc/

> 

> 	* Makefile.in (LIBGCOV_DRIVER): Add _gcov_info_to_gcda.

> 	* gcov.h (gcov_info): Declare.

> 	(__gcov_info_to_gdca): Likewise.

> 	* libgcov.h (gcov_write_counter): Remove.

> 	(gcov_write_tag_length): Likewise.

> 	* libgcov-driver.c (#include <stdint.h>): New.

> 	(#include <string.h>): Remove.

> 	(NEED_L_GCOV): Conditionally define.

> 	(NEED_L_GCOV_INFO_TO_GCDA): Likewise.

> 	(are_all_counters_zero): New.

> 	(gcov_dump_handler): Likewise.

> 	(gcov_allocate_handler): Likewise.

> 	(dump_unsigned): Likewise.

> 	(dump_counter): Likewise.

> 	(write_topn_counters): Add dump_fn, allocate_fn, and arg parameters.

> 	Use dump_unsigned() and dump_counter().

> 	(write_one_data): Add dump_fn, allocate_fn, and arg parameters.  Use

> 	dump_unsigned(), dump_counter(), and are_all_counters_zero().

> 	(__gcov_info_to_gcda): New.

> ---

>   gcc/doc/invoke.texi                      |  80 +++++++--

>   gcc/gcov-io.c                            |  36 ++---

>   gcc/gcov-io.h                            |   1 +

>   gcc/testsuite/gcc.dg/gcov-info-to-gcda.c |  60 +++++++

>   libgcc/Makefile.in                       |   2 +-

>   libgcc/gcov.h                            |  19 +++

>   libgcc/libgcov-driver.c                  | 196 ++++++++++++++++++-----

>   libgcc/libgcov.h                         |   5 -

>   8 files changed, 313 insertions(+), 86 deletions(-)

>   create mode 100644 gcc/testsuite/gcc.dg/gcov-info-to-gcda.c

> 

> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi

> index 32697e6117c0..5f31312b9485 100644

> --- a/gcc/doc/invoke.texi

> +++ b/gcc/doc/invoke.texi

> @@ -14798,17 +14798,17 @@ To optimize the program based on the collected profile information, use

>   Register the profile information in the specified section instead of using a

>   constructor/destructor.  The section name is @var{name} if it is specified,

>   otherwise the section name defaults to @code{.gcov_info}.  A pointer to the

> -profile information generated by @option{-fprofile-arcs} or

> -@option{-ftest-coverage} is placed in the specified section for each

> -translation unit.  This option disables the profile information registration

> -through a constructor and it disables the profile information processing

> -through a destructor.  This option is not intended to be used in hosted

> -environments such as GNU/Linux.  It targets systems with limited resources

> -which do not support constructors and destructors.  The linker could collect

> -the input sections in a continuous memory block and define start and end

> -symbols.  The runtime support could dump the profiling information registered

> -in this linker set during program termination to a serial line for example.  A

> -GNU linker script example which defines a linker output section follows:

> +profile information generated by @option{-fprofile-arcs} is placed in the

> +specified section for each translation unit.  This option disables the profile

> +information registration through a constructor and it disables the profile

> +information processing through a destructor.  This option is not intended to be

> +used in hosted environments such as GNU/Linux.  It targets free-standing

> +environments (for example embedded systems) with limited resources which do not

> +support constructors/destructors or the C library file I/O.

> +

> +The linker could collect the input sections in a continuous memory block and

> +define start and end symbols.  A GNU linker script example which defines a

> +linker output section follows:

>   

>   @smallexample

>     .gcov_info      :

> @@ -14819,6 +14819,64 @@ GNU linker script example which defines a linker output section follows:

>     @}

>   @end smallexample

>   

> +The program could dump the profiling information registered in this linker set

> +for example like this:

> +

> +@smallexample

> +#include <gcov.h>

> +#include <stdio.h>

> +#include <stdlib.h>

> +

> +extern const struct gcov_info *__gcov_info_start[];

> +extern const struct gcov_info *__gcov_info_end[];

> +

> +static void

> +filename (const char *f, void *arg)

> +@{

> +  puts (f);

> +@}

> +

> +static void

> +dump (const void *d, unsigned n, void *arg)

> +@{

> +  const unsigned char *c = d;

> +

> +  for (unsigned i = 0; i < n; ++i)

> +    printf ("%02x", c[i]);

> +@}

> +

> +static void *

> +allocate (unsigned length, void *arg)

> +@{

> +  return malloc (length);

> +@}

> +

> +static void

> +dump_gcov_info (void)

> +@{

> +  const struct gcov_info **info = __gcov_info_start;

> +  const struct gcov_info **end = __gcov_info_end;

> +

> +  /* Obfuscate variable to prevent compiler optimizations.  */

> +  __asm__ ("" : "+r" (end));

> +

> +  while (info != end)

> +  @{

> +    void *arg = NULL;

> +    __gcov_info_to_gcda (*info, filename, dump, allocate, arg);

> +    putchar ('\n');

> +    ++info;

> +  @}

> +@}

> +

> +int

> +main()

> +@{

> +  dump_gcov_info();

> +  return 0;

> +@}

> +@end smallexample

> +

>   @item -fprofile-note=@var{path}

>   @opindex fprofile-note

>   

> diff --git a/gcc/gcov-io.c b/gcc/gcov-io.c

> index 4b1e11d45305..864c01f04a97 100644

> --- a/gcc/gcov-io.c

> +++ b/gcc/gcov-io.c

> @@ -227,30 +227,25 @@ gcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected)

>   #endif

>   

>   #if !IN_GCOV

> -/* Write unsigned VALUE to coverage file.  */

> +/* Write DATA of LENGTH characters to coverage file.  */

>   

>   GCOV_LINKAGE void

> -gcov_write_unsigned (gcov_unsigned_t value)

> +gcov_write (const void *data, unsigned length)

>   {

> -  gcov_unsigned_t r = fwrite (&value, sizeof (value), 1, gcov_var.file);

> +  gcov_unsigned_t r = fwrite (data, length, 1, gcov_var.file);

>     if (r != 1)

>       gcov_var.error = 1;

>   }

>   

> -/* Write counter VALUE to coverage file.  Sets error flag

> -   appropriately.  */

> +/* Write unsigned VALUE to coverage file.  */

>   

> -#if IN_LIBGCOV

>   GCOV_LINKAGE void

> -gcov_write_counter (gcov_type value)

> +gcov_write_unsigned (gcov_unsigned_t value)

>   {

> -  gcov_write_unsigned ((gcov_unsigned_t) value);

> -  if (sizeof (value) > sizeof (gcov_unsigned_t))

> -    gcov_write_unsigned ((gcov_unsigned_t) (value >> 32));

> -  else

> -    gcov_write_unsigned (0);

> +  gcov_unsigned_t r = fwrite (&value, sizeof (value), 1, gcov_var.file);

> +  if (r != 1)

> +    gcov_var.error = 1;

>   }

> -#endif /* IN_LIBGCOV */

>   

>   #if !IN_LIBGCOV

>   /* Write STRING to coverage file.  Sets error flag on file

> @@ -347,22 +342,13 @@ gcov_write_length (gcov_position_t position)

>   

>   #else /* IN_LIBGCOV */

>   

> -/* Write a tag TAG and length LENGTH.  */

> -

> -GCOV_LINKAGE void

> -gcov_write_tag_length (gcov_unsigned_t tag, gcov_unsigned_t length)

> -{

> -  gcov_write_unsigned (tag);

> -  gcov_write_unsigned (length);

> -}

> -

> -/* Write a summary structure to the gcov file.  Return nonzero on

> -   overflow.  */

> +/* Write a summary structure to the gcov file.  */

>   

>   GCOV_LINKAGE void

>   gcov_write_summary (gcov_unsigned_t tag, const struct gcov_summary *summary)

>   {

> -  gcov_write_tag_length (tag, GCOV_TAG_SUMMARY_LENGTH);

> +  gcov_write_unsigned (tag);

> +  gcov_write_unsigned (GCOV_TAG_SUMMARY_LENGTH);

>     gcov_write_unsigned (summary->runs);

>     gcov_write_unsigned (summary->sum_max);

>   }

> diff --git a/gcc/gcov-io.h b/gcc/gcov-io.h

> index 538bee81263a..99e1964c1094 100644

> --- a/gcc/gcov-io.h

> +++ b/gcc/gcov-io.h

> @@ -367,6 +367,7 @@ char *mangle_path (char const *base);

>   

>   #if !IN_GCOV

>   /* Available outside gcov */

> +GCOV_LINKAGE void gcov_write (const void *, unsigned) ATTRIBUTE_HIDDEN;

>   GCOV_LINKAGE void gcov_write_unsigned (gcov_unsigned_t) ATTRIBUTE_HIDDEN;

>   #endif

>   

> diff --git a/gcc/testsuite/gcc.dg/gcov-info-to-gcda.c b/gcc/testsuite/gcc.dg/gcov-info-to-gcda.c

> new file mode 100644

> index 000000000000..390f377de4fe

> --- /dev/null

> +++ b/gcc/testsuite/gcc.dg/gcov-info-to-gcda.c

> @@ -0,0 +1,60 @@

> +/* { dg-do run } */

> +/* { dg-skip-if "profile-info-section" { powerpc-ibm-aix* } } */

> +/* { dg-options "-fprofile-arcs -fprofile-info-section" } */

> +

> +#define assert(expr)						\

> +  ((expr)                                                       \

> +   ? (void)0                                                    \

> +   : (__builtin_printf ("%s:%i: Assertion `%s' failed.\n",	\

> +                        __FILE__, __LINE__, #expr),		\

> +      __builtin_abort ()))

> +

> +struct gcov_info;

> +

> +extern void

> +__gcov_info_to_gcda (const struct gcov_info *__info,

> +		     void (*__filename_fn) (const char *, void *),

> +		     void (*__dump_fn) (const void *, unsigned, void *),

> +		     void *(*__allocate_fn) (unsigned, void *),

> +		     void *__arg);

> +

> +extern const struct gcov_info *my_info;

> +

> +static unsigned counter;

> +

> +static void

> +filename (const char *f, void *arg)

> +{

> +  assert (arg == &counter);

> +  assert (__builtin_strstr (f, "gcov-info-to-gcda.c") == 0);

> +}

> +

> +static void

> +dump (const void *d, unsigned n, void *arg)

> +{

> +  unsigned *m = (unsigned *)arg;

> +  assert (arg == &counter);

> +

> +  if (*m == 0)

> +  {

> +    const unsigned *u = d;

> +    assert (*u == 0x67636461);

> +  }

> +

> +  *m += n;

> +}

> +

> +static void *

> +allocate (unsigned length, void *arg)

> +{

> +  assert (arg == &counter);

> +  return __builtin_malloc (length);

> +}

> +

> +int main()

> +{

> +  __asm__ volatile (".set my_info, .LPBX2");

> +  __gcov_info_to_gcda (my_info, filename, dump, allocate, &counter);

> +  assert (counter > 4);

> +  return 0;

> +}

> diff --git a/libgcc/Makefile.in b/libgcc/Makefile.in

> index 2c8be561eb53..7ec975845544 100644

> --- a/libgcc/Makefile.in

> +++ b/libgcc/Makefile.in

> @@ -908,7 +908,7 @@ LIBGCOV_INTERFACE = _gcov_dump _gcov_fork				\

>   	_gcov_execl _gcov_execlp					\

>   	_gcov_execle _gcov_execv _gcov_execvp _gcov_execve _gcov_reset  \

>   	_gcov_lock_unlock

> -LIBGCOV_DRIVER = _gcov

> +LIBGCOV_DRIVER = _gcov _gcov_info_to_gcda

>   

>   libgcov-merge-objects = $(patsubst %,%$(objext),$(LIBGCOV_MERGE))

>   libgcov-profiler-objects = $(patsubst %,%$(objext),$(LIBGCOV_PROFILER))

> diff --git a/libgcc/gcov.h b/libgcc/gcov.h

> index e6492cdd31ea..66d03bf4e5d4 100644

> --- a/libgcc/gcov.h

> +++ b/libgcc/gcov.h

> @@ -25,6 +25,8 @@

>   #ifndef GCC_GCOV_H

>   #define GCC_GCOV_H

>   

> +struct gcov_info;

> +

>   /* Set all counters to zero.  */

>   

>   extern void __gcov_reset (void);

> @@ -33,4 +35,21 @@ extern void __gcov_reset (void);

>   

>   extern void __gcov_dump (void);

>   

> +/* Convert the gcov information referenced by INFO to a gcda data stream.

> +   The FILENAME_FN callback is called exactly once with the filename associated

> +   with the gcov information.  The filename may be NULL.  Afterwards, the

> +   DUMP_FN callback is subsequently called with chunks (the begin and length of

> +   the chunk are passed as the first two callback parameters) of the gcda data

> +   stream.  The ALLOCATE_FN callback shall allocate memory with a size in

> +   characters specified by the first callback parameter.  The ARG parameter is

> +   a user-provided argument passed as the last argument to the callback

> +   functions.  */

> +

> +extern void

> +__gcov_info_to_gcda (const struct gcov_info *__info,

> +		     void (*__filename_fn) (const char *, void *),

> +		     void (*__dump_fn) (const void *, unsigned, void *),

> +		     void *(*__allocate_fn) (unsigned, void *),

> +		     void *__arg);

> +

>   #endif /* GCC_GCOV_H */

> diff --git a/libgcc/libgcov-driver.c b/libgcc/libgcov-driver.c

> index df7ccb235677..32e312ec3cfa 100644

> --- a/libgcc/libgcov-driver.c

> +++ b/libgcc/libgcov-driver.c

> @@ -26,6 +26,20 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see

>   #include "libgcov.h"

>   #include "gcov-io.h"

>   

> +#include <stdint.h>

> +

> +/* Return 1, if all counter values are zero, otherwise 0. */

> +

> +static inline int

> +are_all_counters_zero (const struct gcov_ctr_info *ci_ptr)

> +{

> +  for (unsigned i = 0; i < ci_ptr->num; i++)

> +    if (ci_ptr->values[i] != 0)

> +      return 0;

> +

> +  return 1;

> +}

> +

>   #if defined(inhibit_libc)

>   /* If libc and its header files are not available, provide dummy functions.  */

>   

> @@ -35,8 +49,6 @@ void __gcov_init (struct gcov_info *p __attribute__ ((unused))) {}

>   

>   #else /* inhibit_libc */

>   

> -#include <string.h>

> -

>   #if GCOV_LOCKED

>   #include <fcntl.h>

>   #include <errno.h>

> @@ -51,8 +63,17 @@ void __gcov_init (struct gcov_info *p __attribute__ ((unused))) {}

>   #include <sys/mman.h>

>   #endif

>   

> -#ifdef L_gcov

> +#endif /* inhibit_libc */

> +

> +#if defined(L_gcov) && !defined(inhibit_libc)

> +#define NEED_L_GCOV

> +#endif

>   

> +#if defined(L_gcov_info_to_gcda) && !IN_GCOV_TOOL

> +#define NEED_L_GCOV_INFO_TO_GCDA

> +#endif

> +

> +#ifdef NEED_L_GCOV

>   /* A utility function for outputting errors.  */

>   static int gcov_error (const char *, ...);

>   

> @@ -343,6 +364,51 @@ read_error:

>     return -1;

>   }

>   

> +/* Write the DATA of LENGTH characters to the gcov file.  */

> +

> +static void

> +gcov_dump_handler (const void *data,

> +		   unsigned length,

> +		   void *arg ATTRIBUTE_UNUSED)

> +{

> +  gcov_write (data, length);

> +}

> +

> +/* Allocate SIZE characters and return the address of the allocated memory.  */

> +

> +static void *

> +gcov_allocate_handler (unsigned size, void *arg ATTRIBUTE_UNUSED)

> +{

> +  return xmalloc (size);

> +}

> +#endif /* NEED_L_GCOV */

> +

> +#if defined(NEED_L_GCOV) || defined(NEED_L_GCOV_INFO_TO_GCDA)

> +/* Dump the WORD using the DUMP handler called with ARG.  */

> +

> +static inline void

> +dump_unsigned (gcov_unsigned_t word,

> +	       void (*dump_fn) (const void *, unsigned, void *),

> +	       void *arg)

> +{

> +  (*dump_fn) (&word, sizeof (word), arg);

> +}

> +

> +/* Dump the COUNTER using the DUMP handler called with ARG.  */

> +

> +static inline void

> +dump_counter (gcov_type counter,

> +	      void (*dump_fn) (const void *, unsigned, void *),

> +	      void *arg)

> +{

> +  dump_unsigned ((gcov_unsigned_t)counter, dump_fn, arg);

> +

> +  if (sizeof (counter) > sizeof (gcov_unsigned_t))

> +    dump_unsigned ((gcov_unsigned_t)(counter >> 32), dump_fn, arg);

> +  else

> +    dump_unsigned (0, dump_fn, arg);

> +}

> +

>   #define MAX(X,Y) ((X) > (Y) ? (X) : (Y))

>   

>   /* Store all TOP N counters where each has a dynamic length.  */

> @@ -350,7 +416,10 @@ read_error:

>   static void

>   write_topn_counters (const struct gcov_ctr_info *ci_ptr,

>   		     unsigned t_ix,

> -		     gcov_unsigned_t n_counts)

> +		     gcov_unsigned_t n_counts,

> +		     void (*dump_fn) (const void *, unsigned, void *),

> +		     void *(*allocate_fn)(unsigned, void *),

> +		     void *arg)

>   {

>     unsigned counters = n_counts / GCOV_TOPN_MEM_COUNTERS;

>     gcc_assert (n_counts % GCOV_TOPN_MEM_COUNTERS == 0);

> @@ -365,46 +434,49 @@ write_topn_counters (const struct gcov_ctr_info *ci_ptr,

>     if (list_sizes == NULL || counters > list_size_length)

>       {

>         list_size_length = MAX (LIST_SIZE_MIN_LENGTH, 2 * counters);

> -#if HAVE_SYS_MMAN_H

> +#if !defined(inhibit_libc) && HAVE_SYS_MMAN_H

>         list_sizes

>   	= (unsigned *)malloc_mmap (list_size_length * sizeof (unsigned));

>   #endif

>   

>         /* Malloc fallback.  */

>         if (list_sizes == NULL)

> -	list_sizes = (unsigned *)xmalloc (list_size_length * sizeof (unsigned));

> +	list_sizes =

> +	  (unsigned *)(*allocate_fn) (list_size_length * sizeof (unsigned),

> +				      arg);

>       }

>   

> -  memset (list_sizes, 0, counters * sizeof (unsigned));

>     unsigned pair_total = 0;

>   

>     for (unsigned i = 0; i < counters; i++)

>       {

>         gcov_type start = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 2];

> +      unsigned sizes = 0;

> +

>         for (struct gcov_kvp *node = (struct gcov_kvp *)(intptr_t)start;

>   	   node != NULL; node = node->next)

> -	{

> -	  ++pair_total;

> -	  ++list_sizes[i];

> -	}

> +	++sizes;

> +

> +      pair_total += sizes;

> +      list_sizes[i] = sizes;

>       }

>   

>     unsigned disk_size = GCOV_TOPN_DISK_COUNTERS * counters + 2 * pair_total;

> -  gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),

> -			 GCOV_TAG_COUNTER_LENGTH (disk_size));

> +  dump_unsigned (GCOV_TAG_FOR_COUNTER (t_ix), dump_fn, arg),

> +  dump_unsigned (GCOV_TAG_COUNTER_LENGTH (disk_size), dump_fn, arg);

>   

>     for (unsigned i = 0; i < counters; i++)

>       {

> -      gcov_write_counter (ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i]);

> -      gcov_write_counter (list_sizes[i]);

> +      dump_counter (ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i], dump_fn, arg);

> +      dump_counter (list_sizes[i], dump_fn, arg);

>         gcov_type start = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 2];

>   

>         unsigned j = 0;

>         for (struct gcov_kvp *node = (struct gcov_kvp *)(intptr_t)start;

>   	   j < list_sizes[i]; node = node->next, j++)

>   	{

> -	  gcov_write_counter (node->value);

> -	  gcov_write_counter (node->count);

> +	  dump_counter (node->value, dump_fn, arg);

> +	  dump_counter (node->count, dump_fn, arg);

>   	}

>       }

>   }

> @@ -415,25 +487,36 @@ write_topn_counters (const struct gcov_ctr_info *ci_ptr,

>   

>   static void

>   write_one_data (const struct gcov_info *gi_ptr,

> -		const struct gcov_summary *prg_p)

> +		const struct gcov_summary *prg_p,

> +		void (*dump_fn) (const void *, unsigned, void *),

> +		void *(*allocate_fn) (unsigned, void *),

> +		void *arg)

>   {

>     unsigned f_ix;

>   

> -  gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION);

> -  gcov_write_unsigned (gi_ptr->stamp);

> +  dump_unsigned (GCOV_DATA_MAGIC, dump_fn, arg);

> +  dump_unsigned (GCOV_VERSION, dump_fn, arg);

> +  dump_unsigned (gi_ptr->stamp, dump_fn, arg);

>   

> +#ifdef NEED_L_GCOV

>     /* Generate whole program statistics.  */

>     gcov_write_summary (GCOV_TAG_OBJECT_SUMMARY, prg_p);

> +#else

> +  (void)prg_p;


Can you please use rather ATTRIBUTE_UNUSED for the argument?

> +#endif

>   

>     /* Write execution counts for each function.  */

>     for (f_ix = 0; f_ix != gi_ptr->n_functions; f_ix++)

>       {

> +#ifdef NEED_L_GCOV

>         unsigned buffered = 0;

> +#endif

>         const struct gcov_fn_info *gfi_ptr;

>         const struct gcov_ctr_info *ci_ptr;

>         gcov_unsigned_t length;

>         unsigned t_ix;

>   

> +#ifdef NEED_L_GCOV

>         if (fn_buffer && fn_buffer->fn_ix == f_ix)

>           {

>             /* Buffered data from another program.  */

> @@ -442,6 +525,7 @@ write_one_data (const struct gcov_info *gi_ptr,

>             length = GCOV_TAG_FUNCTION_LENGTH;

>           }

>         else

> +#endif

>           {

>             gfi_ptr = gi_ptr->functions[f_ix];

>             if (gfi_ptr && gfi_ptr->key == gi_ptr)

> @@ -450,13 +534,14 @@ write_one_data (const struct gcov_info *gi_ptr,

>                   length = 0;

>           }

>   

> -      gcov_write_tag_length (GCOV_TAG_FUNCTION, length);

> +      dump_unsigned (GCOV_TAG_FUNCTION, dump_fn, arg);

> +      dump_unsigned (length, dump_fn, arg);

>         if (!length)

>           continue;

>   

> -      gcov_write_unsigned (gfi_ptr->ident);

> -      gcov_write_unsigned (gfi_ptr->lineno_checksum);

> -      gcov_write_unsigned (gfi_ptr->cfg_checksum);

> +      dump_unsigned (gfi_ptr->ident, dump_fn, arg);

> +      dump_unsigned (gfi_ptr->lineno_checksum, dump_fn, arg);

> +      dump_unsigned (gfi_ptr->cfg_checksum, dump_fn, arg);

>   

>         ci_ptr = gfi_ptr->ctrs;

>         for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)

> @@ -469,39 +554,43 @@ write_one_data (const struct gcov_info *gi_ptr,

>   	  n_counts = ci_ptr->num;

>   

>   	  if (t_ix == GCOV_COUNTER_V_TOPN || t_ix == GCOV_COUNTER_V_INDIR)

> -	    write_topn_counters (ci_ptr, t_ix, n_counts);

> +	    write_topn_counters (ci_ptr,

> +				 t_ix,

> +				 n_counts,

> +				 dump_fn,

> +				 allocate_fn,

> +				 arg);


Please reduce the number of newlines.

>   	  else

>   	    {

> -	      /* Do not stream when all counters are zero.  */

> -	      int all_zeros = 1;

> -	      for (unsigned i = 0; i < n_counts; i++)

> -		if (ci_ptr->values[i] != 0)

> -		  {

> -		    all_zeros = 0;

> -		    break;

> -		  }

> -

> -	      if (all_zeros)

> -		gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),

> -				       GCOV_TAG_COUNTER_LENGTH (-n_counts));

> +	      dump_unsigned (GCOV_TAG_FOR_COUNTER (t_ix), dump_fn, arg);

> +	      if (are_all_counters_zero (ci_ptr))

> +		/* Do not stream when all counters are zero.  */

> +		dump_unsigned (GCOV_TAG_COUNTER_LENGTH (-n_counts),

> +			       dump_fn,

> +			       arg);


Likewise here.

>   	      else

>   		{

> -		  gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),

> -					 GCOV_TAG_COUNTER_LENGTH (n_counts));

> +		  dump_unsigned (GCOV_TAG_COUNTER_LENGTH (n_counts),

> +				 dump_fn,

> +				 arg);

>   		  for (unsigned i = 0; i < n_counts; i++)

> -		    gcov_write_counter (ci_ptr->values[i]);

> +		    dump_counter (ci_ptr->values[i], dump_fn, arg);

>   		}

>   	    }

>   

>   	  ci_ptr++;

>   	}

> +#ifdef NEED_L_GCOV

>         if (buffered)

>           fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);

> +#endif

>       }

>   

> -  gcov_write_unsigned (0);

> +  dump_unsigned (0, dump_fn, arg);

>   }

> +#endif /* NEED_L_GCOV || NEED_L_GCOV_INFO_TO_GCDA */

>   

> +#ifdef NEED_L_GCOV

>   /* 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

> @@ -550,7 +639,11 @@ dump_one_gcov (struct gcov_info *gi_ptr, struct gcov_filename *gf,

>     summary = gi_ptr->summary;

>   #endif

>   

> -  write_one_data (gi_ptr, &summary);

> +  write_one_data (gi_ptr,

> +		  &summary,

> +		  gcov_dump_handler,

> +		  gcov_allocate_handler,

> +		  NULL);


Same here.

Martin

>     /* fall through */

>   

>   read_fatal:;

> @@ -680,5 +773,20 @@ __gcov_init (struct gcov_info *info)

>       }

>   }

>   #endif /* !IN_GCOV_TOOL */

> -#endif /* L_gcov */

> -#endif /* inhibit_libc */

> +#endif /* NEED_L_GCOV */

> +

> +#ifdef NEED_L_GCOV_INFO_TO_GCDA

> +/* Convert the gcov info to a gcda data stream.  It is intended for

> +   free-standing environments which do not support the C library file I/O.  */

> +

> +void

> +__gcov_info_to_gcda (const struct gcov_info *gi_ptr,

> +		     void (*filename_fn) (const char *, void *),

> +		     void (*dump_fn) (const void *, unsigned, void *),

> +		     void *(*allocate_fn) (unsigned, void *),

> +		     void *arg)

> +{

> +  (*filename_fn) (gi_ptr->filename, arg);

> +  write_one_data (gi_ptr, NULL, dump_fn, allocate_fn, arg);

> +}

> +#endif /* NEED_L_GCOV_INFO_TO_GCDA */

> diff --git a/libgcc/libgcov.h b/libgcc/libgcov.h

> index 8d323db05386..9c537253293f 100644

> --- a/libgcc/libgcov.h

> +++ b/libgcc/libgcov.h

> @@ -114,13 +114,11 @@ typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI)));

>   #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

> @@ -345,9 +343,6 @@ extern int __gcov_execve (const char *, char  *const [], char *const [])

>   

>   /* 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;

>
Sebastian Huber Aug. 6, 2021, 5:31 a.m. | #2
On 05/08/2021 14:53, Martin Liška wrote:
> On 7/23/21 11:39 AM, Sebastian Huber wrote:

>> Add __gcov_info_to_gcda() to libgcov to get the gcda data for a gcda 

>> info in a

>> freestanding environment.  It is intended to be used with the

>> -fprofile-info-section option.  A crude test program which doesn't use 

>> a linker

>> script is (use "gcc -coverage -fprofile-info-section -lgcc test.c" to 

>> compile

>> it):

> 

> The patch can be installed once the following nits are fixed:


Thanks for the review, I checked it in like this:

https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=9124bbe1857f0d3a3015d6461d5f8d04f07cab85

I hope the format is now all right.

-- 
embedded brains GmbH
Herr Sebastian HUBER
Dornierstr. 4
82178 Puchheim
Germany
email: sebastian.huber@embedded-brains.de
phone: +49-89-18 94 741 - 16
fax:   +49-89-18 94 741 - 08

Registergericht: Amtsgericht München
Registernummer: HRB 157899
Vertretungsberechtigte Geschäftsführer: Peter Rasmussen, Thomas Dörfler
Unsere Datenschutzerklärung finden Sie hier:
https://embedded-brains.de/datenschutzerklaerung/
Ian Lance Taylor via Gcc-patches Aug. 6, 2021, 7:50 a.m. | #3
On Fri, Aug 6, 2021 at 7:31 AM Sebastian Huber <
sebastian.huber@embedded-brains.de> wrote:

> On 05/08/2021 14:53, Martin Liška wrote:

> > On 7/23/21 11:39 AM, Sebastian Huber wrote:

> >> Add __gcov_info_to_gcda() to libgcov to get the gcda data for a gcda

> >> info in a

> >> freestanding environment.  It is intended to be used with the

> >> -fprofile-info-section option.  A crude test program which doesn't use

> >> a linker

> >> script is (use "gcc -coverage -fprofile-info-section -lgcc test.c" to

> >> compile

> >> it):

> >

> > The patch can be installed once the following nits are fixed:

>

> Thanks for the review, I checked it in like this:

>

>

> https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=9124bbe1857f0d3a3015d6461d5f8d04f07cab85

>

> I hope the format is now all right.

>

>

Hi,

Looks like there's a problem with your patch:
 /tmp/1784442_7.tmpdir/aci-gcc-fsf/builds/gcc-fsf-gccsrc/obj-arm-none-eabi/gcc1/./gcc/xgcc
-B/tmp/1784442_7.tmpdir/aci-gcc-fsf/builds/gcc-fsf-gccsrc/obj-arm-none-eabi/gcc1/./gcc/
-B/tmp/1784442_7.tmpdir/aci-gcc-fsf/builds/gcc-fsf-gccsrc/tools/arm-none-eabi/bin/
-B/tmp/1784442_7.tmpdir/aci-gcc-fsf/builds/gcc-fsf-gccsrc/tools/arm-none-eabi/lib/
-isystem
/tmp/1784442_7.tmpdir/aci-gcc-fsf/builds/gcc-fsf-gccsrc/tools/arm-none-eabi/include
-isystem
/tmp/1784442_7.tmpdir/aci-gcc-fsf/builds/gcc-fsf-gccsrc/tools/arm-none-eabi/sys-include
   -g -O2 -O2  -g -O2 -DIN_GCC  -DCROSS_DIRECTORY_STRUCTURE  -W -Wall
-Wno-narrowing -Wwrite-strings -Wcast-qual -Wstrict-prototypes
-Wmissing-prototypes -Wold-style-definition  -isystem ./include
-fno-inline -g -DIN_LIBGCC2 -fbuilding-libgcc -fno-stack-protector
-Dinhibit_libc  -fno-inline -I. -I. -I../.././gcc
-I/tmp/1784442_7.tmpdir/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libgcc
-I/tmp/1784442_7.tmpdir/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libgcc/.
-I/tmp/1784442_7.tmpdir/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libgcc/../gcc
-I/tmp/1784442_7.tmpdir/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libgcc/../include
   -o _gcov.o -MT _gcov.o -MD -MP -MF _gcov.dep -DL_gcov -c
/tmp/1784442_7.tmpdir/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libgcc/libgcov-driver.c

In file included from
/tmp/1784442_7.tmpdir/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libgcc/libgcov-driver.c:29:
/tmp/1784442_7.tmpdir/aci-gcc-fsf/builds/gcc-fsf-gccsrc/obj-arm-none-eabi/gcc1/gcc/include/stdint.h:9:16:
fatal error: stdint.h: No such file or directory
    9 | # include_next <stdint.h>
      |                ^~~~~~~~~~
compilation terminated.
make[2]: *** [Makefile:928: _gcov.o] Error 1
make[2]: *** Waiting for unfinished jobs....

Can you check?

Thanks,

Christophe

-- 
> embedded brains GmbH

> Herr Sebastian HUBER

> Dornierstr. 4

> 82178 Puchheim

> Germany

> email: sebastian.huber@embedded-brains.de

> phone: +49-89-18 94 741 - 16

> fax:   +49-89-18 94 741 - 08

>

> Registergericht: Amtsgericht München

> Registernummer: HRB 157899

> Vertretungsberechtigte Geschäftsführer: Peter Rasmussen, Thomas Dörfler

> Unsere Datenschutzerklärung finden Sie hier:

> https://embedded-brains.de/datenschutzerklaerung/

>
Sebastian Huber Aug. 6, 2021, 8:05 a.m. | #4
Hello Christophe,

On 06/08/2021 09:50, Christophe Lyon wrote:
> Looks like there's a problem with your patch:

>   /tmp/1784442_7.tmpdir/aci-gcc-fsf/builds/gcc-fsf-gccsrc/obj-arm-none-eabi/gcc1/./gcc/xgcc -B/tmp/1784442_7.tmpdir/aci-gcc-fsf/builds/gcc-fsf-gccsrc/obj-arm-none-eabi/gcc1/./gcc/ -B/tmp/1784442_7.tmpdir/aci-gcc-fsf/builds/gcc-fsf-gccsrc/tools/arm-none-eabi/bin/ -B/tmp/1784442_7.tmpdir/aci-gcc-fsf/builds/gcc-fsf-gccsrc/tools/arm-none-eabi/lib/ -isystem /tmp/1784442_7.tmpdir/aci-gcc-fsf/builds/gcc-fsf-gccsrc/tools/arm-none-eabi/include -isystem /tmp/1784442_7.tmpdir/aci-gcc-fsf/builds/gcc-fsf-gccsrc/tools/arm-none-eabi/sys-include    -g -O2 -O2  -g -O2 -DIN_GCC  -DCROSS_DIRECTORY_STRUCTURE  -W -Wall -Wno-narrowing -Wwrite-strings -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wold-style-definition  -isystem ./include -fno-inline -g -DIN_LIBGCC2 -fbuilding-libgcc -fno-stack-protector -Dinhibit_libc  -fno-inline -I. -I. -I../.././gcc -I/tmp/1784442_7.tmpdir/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libgcc -I/tmp/1784442_7.tmpdir/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libgcc/. -I/tmp/1784442_7.tmpdir/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libgcc/../gcc -I/tmp/1784442_7.tmpdir/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libgcc/../include    -o _gcov.o -MT _gcov.o -MD -MP -MF _gcov.dep -DL_gcov -c /tmp/1784442_7.tmpdir/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libgcc/libgcov-driver.c

> 

> In file included from 

> /tmp/1784442_7.tmpdir/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libgcc/libgcov-driver.c:29:

> /tmp/1784442_7.tmpdir/aci-gcc-fsf/builds/gcc-fsf-gccsrc/obj-arm-none-eabi/gcc1/gcc/include/stdint.h:9:16: 

> fatal error: stdint.h: No such file or directory

>      9 | # include_next <stdint.h>

>        |                ^~~~~~~~~~

> compilation terminated.

> make[2]: *** [Makefile:928: _gcov.o] Error 1

> make[2]: *** Waiting for unfinished jobs....

> 

> Can you check?


I already feared that the <stdint.h> include may cause problems, from 
the commit message:

"With this patch, <stdint.h> is included in libgcov-driver.c even if 
inhibit_libc is defined.  This header file should be also available for 
freestanding environments.  If this is not the case, then we have to 
define intptr_t somehow."

What about the attached patch?

-- 
embedded brains GmbH
Herr Sebastian HUBER
Dornierstr. 4
82178 Puchheim
Germany
email: sebastian.huber@embedded-brains.de
phone: +49-89-18 94 741 - 16
fax:   +49-89-18 94 741 - 08

Registergericht: Amtsgericht München
Registernummer: HRB 157899
Vertretungsberechtigte Geschäftsführer: Peter Rasmussen, Thomas Dörfler
Unsere Datenschutzerklärung finden Sie hier:
https://embedded-brains.de/datenschutzerklaerung/
From 9e91c623116313312408a8809f32eac1f7ef6b16 Mon Sep 17 00:00:00 2001
From: Sebastian Huber <sebastian.huber@embedded-brains.de>

Date: Fri, 6 Aug 2021 09:57:43 +0200
Subject: [PATCH] gcov: Remove <stdint.h> from libgcov-driver.c

In the patch to add __gcov_info_to_gcda(), the include of <stdint.h> was added
to libgcov-driver.c even if inhibit_libc is defined.  It turned out that this
header file is not always available.  Remove the include of <stdin.h> and
replace the intptr_t with the compiler provided __INTPTR_TYPE__.

libgcc/

	* libgcov-driver.c (#include <stdint.h>): Remove.
	(write_topn_counters): Use __INTPTR_TYPE__ instead of intptr_t.
---
 libgcc/libgcov-driver.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/libgcc/libgcov-driver.c b/libgcc/libgcov-driver.c
index 9d7bc9c79950..087f71e01077 100644
--- a/libgcc/libgcov-driver.c
+++ b/libgcc/libgcov-driver.c
@@ -26,8 +26,6 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 #include "libgcov.h"
 #include "gcov-io.h"
 
-#include <stdint.h>
-
 /* Return 1, if all counter values are zero, otherwise 0. */
 
 static inline int
@@ -453,7 +451,7 @@ write_topn_counters (const struct gcov_ctr_info *ci_ptr,
       gcov_type start = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 2];
       unsigned sizes = 0;
 
-      for (struct gcov_kvp *node = (struct gcov_kvp *)(intptr_t)start;
+      for (struct gcov_kvp *node = (struct gcov_kvp *)(__INTPTR_TYPE__)start;
 	   node != NULL; node = node->next)
 	++sizes;
 
@@ -472,7 +470,7 @@ write_topn_counters (const struct gcov_ctr_info *ci_ptr,
       gcov_type start = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 2];
 
       unsigned j = 0;
-      for (struct gcov_kvp *node = (struct gcov_kvp *)(intptr_t)start;
+      for (struct gcov_kvp *node = (struct gcov_kvp *)(__INTPTR_TYPE__)start;
 	   j < list_sizes[i]; node = node->next, j++)
 	{
 	  dump_counter (node->value, dump_fn, arg);
-- 
2.26.2
Ian Lance Taylor via Gcc-patches Aug. 6, 2021, 9:15 a.m. | #5
On Fri, Aug 6, 2021 at 10:05 AM Sebastian Huber <
sebastian.huber@embedded-brains.de> wrote:

> Hello Christophe,

>

> On 06/08/2021 09:50, Christophe Lyon wrote:

> > Looks like there's a problem with your patch:

> >

>  /tmp/1784442_7.tmpdir/aci-gcc-fsf/builds/gcc-fsf-gccsrc/obj-arm-none-eabi/gcc1/./gcc/xgcc

> -B/tmp/1784442_7.tmpdir/aci-gcc-fsf/builds/gcc-fsf-gccsrc/obj-arm-none-eabi/gcc1/./gcc/

> -B/tmp/1784442_7.tmpdir/aci-gcc-fsf/builds/gcc-fsf-gccsrc/tools/arm-none-eabi/bin/

> -B/tmp/1784442_7.tmpdir/aci-gcc-fsf/builds/gcc-fsf-gccsrc/tools/arm-none-eabi/lib/

> -isystem

> /tmp/1784442_7.tmpdir/aci-gcc-fsf/builds/gcc-fsf-gccsrc/tools/arm-none-eabi/include

> -isystem

> /tmp/1784442_7.tmpdir/aci-gcc-fsf/builds/gcc-fsf-gccsrc/tools/arm-none-eabi/sys-include

>    -g -O2 -O2  -g -O2 -DIN_GCC  -DCROSS_DIRECTORY_STRUCTURE  -W -Wall

> -Wno-narrowing -Wwrite-strings -Wcast-qual -Wstrict-prototypes

> -Wmissing-prototypes -Wold-style-definition  -isystem ./include -fno-inline

> -g -DIN_LIBGCC2 -fbuilding-libgcc -fno-stack-protector -Dinhibit_libc

>  -fno-inline -I. -I. -I../.././gcc

> -I/tmp/1784442_7.tmpdir/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libgcc

> -I/tmp/1784442_7.tmpdir/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libgcc/.

> -I/tmp/1784442_7.tmpdir/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libgcc/../gcc

> -I/tmp/1784442_7.tmpdir/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libgcc/../include

>    -o _gcov.o -MT _gcov.o -MD -MP -MF _gcov.dep -DL_gcov -c

> /tmp/1784442_7.tmpdir/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libgcc/libgcov-driver.c

> >

> > In file included from

> >

> /tmp/1784442_7.tmpdir/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libgcc/libgcov-driver.c:29:

> >

> /tmp/1784442_7.tmpdir/aci-gcc-fsf/builds/gcc-fsf-gccsrc/obj-arm-none-eabi/gcc1/gcc/include/stdint.h:9:16:

>

> > fatal error: stdint.h: No such file or directory

> >      9 | # include_next <stdint.h>

> >        |                ^~~~~~~~~~

> > compilation terminated.

> > make[2]: *** [Makefile:928: _gcov.o] Error 1

> > make[2]: *** Waiting for unfinished jobs....

> >

> > Can you check?

>

> I already feared that the <stdint.h> include may cause problems, from

> the commit message:

>

> "With this patch, <stdint.h> is included in libgcov-driver.c even if

> inhibit_libc is defined.  This header file should be also available for

> freestanding environments.  If this is not the case, then we have to

> define intptr_t somehow."

>

> What about the attached patch?

>


Thanks, it does fix the build issue for me.

Christophe


>

> --

> embedded brains GmbH

> Herr Sebastian HUBER

> Dornierstr. 4

> 82178 Puchheim

> Germany

> email: sebastian.huber@embedded-brains.de

> phone: +49-89-18 94 741 - 16

> fax:   +49-89-18 94 741 - 08

>

> Registergericht: Amtsgericht München

> Registernummer: HRB 157899

> Vertretungsberechtigte Geschäftsführer: Peter Rasmussen, Thomas Dörfler

> Unsere Datenschutzerklärung finden Sie hier:

> https://embedded-brains.de/datenschutzerklaerung/

>
Martin Liška Aug. 6, 2021, 10:26 a.m. | #6
On 8/6/21 10:05 AM, Sebastian Huber wrote:
> What about the attached patch?


The patch is fine, please install it.

Martin
Sebastian Huber Aug. 6, 2021, 10:29 a.m. | #7
On 06/08/2021 12:26, Martin Liška wrote:
> On 8/6/21 10:05 AM, Sebastian Huber wrote:

>> What about the attached patch?

> 

> The patch is fine, please install it.


Thanks, I checked it in.

-- 
embedded brains GmbH
Herr Sebastian HUBER
Dornierstr. 4
82178 Puchheim
Germany
email: sebastian.huber@embedded-brains.de
phone: +49-89-18 94 741 - 16
fax:   +49-89-18 94 741 - 08

Registergericht: Amtsgericht München
Registernummer: HRB 157899
Vertretungsberechtigte Geschäftsführer: Peter Rasmussen, Thomas Dörfler
Unsere Datenschutzerklärung finden Sie hier:
https://embedded-brains.de/datenschutzerklaerung/

Patch

diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 32697e6117c0..5f31312b9485 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -14798,17 +14798,17 @@  To optimize the program based on the collected profile information, use
 Register the profile information in the specified section instead of using a
 constructor/destructor.  The section name is @var{name} if it is specified,
 otherwise the section name defaults to @code{.gcov_info}.  A pointer to the
-profile information generated by @option{-fprofile-arcs} or
-@option{-ftest-coverage} is placed in the specified section for each
-translation unit.  This option disables the profile information registration
-through a constructor and it disables the profile information processing
-through a destructor.  This option is not intended to be used in hosted
-environments such as GNU/Linux.  It targets systems with limited resources
-which do not support constructors and destructors.  The linker could collect
-the input sections in a continuous memory block and define start and end
-symbols.  The runtime support could dump the profiling information registered
-in this linker set during program termination to a serial line for example.  A
-GNU linker script example which defines a linker output section follows:
+profile information generated by @option{-fprofile-arcs} is placed in the
+specified section for each translation unit.  This option disables the profile
+information registration through a constructor and it disables the profile
+information processing through a destructor.  This option is not intended to be
+used in hosted environments such as GNU/Linux.  It targets free-standing
+environments (for example embedded systems) with limited resources which do not
+support constructors/destructors or the C library file I/O.
+
+The linker could collect the input sections in a continuous memory block and
+define start and end symbols.  A GNU linker script example which defines a
+linker output section follows:
 
 @smallexample
   .gcov_info      :
@@ -14819,6 +14819,64 @@  GNU linker script example which defines a linker output section follows:
   @}
 @end smallexample
 
+The program could dump the profiling information registered in this linker set
+for example like this:
+
+@smallexample
+#include <gcov.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+extern const struct gcov_info *__gcov_info_start[];
+extern const struct gcov_info *__gcov_info_end[];
+
+static void
+filename (const char *f, void *arg)
+@{
+  puts (f);
+@}
+
+static void
+dump (const void *d, unsigned n, void *arg)
+@{
+  const unsigned char *c = d;
+
+  for (unsigned i = 0; i < n; ++i)
+    printf ("%02x", c[i]);
+@}
+
+static void *
+allocate (unsigned length, void *arg)
+@{
+  return malloc (length);
+@}
+
+static void
+dump_gcov_info (void)
+@{
+  const struct gcov_info **info = __gcov_info_start;
+  const struct gcov_info **end = __gcov_info_end;
+
+  /* Obfuscate variable to prevent compiler optimizations.  */
+  __asm__ ("" : "+r" (end));
+
+  while (info != end)
+  @{
+    void *arg = NULL;
+    __gcov_info_to_gcda (*info, filename, dump, allocate, arg);
+    putchar ('\n');
+    ++info;
+  @}
+@}
+
+int
+main()
+@{
+  dump_gcov_info();
+  return 0;
+@}
+@end smallexample
+
 @item -fprofile-note=@var{path}
 @opindex fprofile-note
 
diff --git a/gcc/gcov-io.c b/gcc/gcov-io.c
index 4b1e11d45305..864c01f04a97 100644
--- a/gcc/gcov-io.c
+++ b/gcc/gcov-io.c
@@ -227,30 +227,25 @@  gcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected)
 #endif
 
 #if !IN_GCOV
-/* Write unsigned VALUE to coverage file.  */
+/* Write DATA of LENGTH characters to coverage file.  */
 
 GCOV_LINKAGE void
-gcov_write_unsigned (gcov_unsigned_t value)
+gcov_write (const void *data, unsigned length)
 {
-  gcov_unsigned_t r = fwrite (&value, sizeof (value), 1, gcov_var.file);
+  gcov_unsigned_t r = fwrite (data, length, 1, gcov_var.file);
   if (r != 1)
     gcov_var.error = 1;
 }
 
-/* Write counter VALUE to coverage file.  Sets error flag
-   appropriately.  */
+/* Write unsigned VALUE to coverage file.  */
 
-#if IN_LIBGCOV
 GCOV_LINKAGE void
-gcov_write_counter (gcov_type value)
+gcov_write_unsigned (gcov_unsigned_t value)
 {
-  gcov_write_unsigned ((gcov_unsigned_t) value);
-  if (sizeof (value) > sizeof (gcov_unsigned_t))
-    gcov_write_unsigned ((gcov_unsigned_t) (value >> 32));
-  else
-    gcov_write_unsigned (0);
+  gcov_unsigned_t r = fwrite (&value, sizeof (value), 1, gcov_var.file);
+  if (r != 1)
+    gcov_var.error = 1;
 }
-#endif /* IN_LIBGCOV */
 
 #if !IN_LIBGCOV
 /* Write STRING to coverage file.  Sets error flag on file
@@ -347,22 +342,13 @@  gcov_write_length (gcov_position_t position)
 
 #else /* IN_LIBGCOV */
 
-/* Write a tag TAG and length LENGTH.  */
-
-GCOV_LINKAGE void
-gcov_write_tag_length (gcov_unsigned_t tag, gcov_unsigned_t length)
-{
-  gcov_write_unsigned (tag);
-  gcov_write_unsigned (length);
-}
-
-/* Write a summary structure to the gcov file.  Return nonzero on
-   overflow.  */
+/* Write a summary structure to the gcov file.  */
 
 GCOV_LINKAGE void
 gcov_write_summary (gcov_unsigned_t tag, const struct gcov_summary *summary)
 {
-  gcov_write_tag_length (tag, GCOV_TAG_SUMMARY_LENGTH);
+  gcov_write_unsigned (tag);
+  gcov_write_unsigned (GCOV_TAG_SUMMARY_LENGTH);
   gcov_write_unsigned (summary->runs);
   gcov_write_unsigned (summary->sum_max);
 }
diff --git a/gcc/gcov-io.h b/gcc/gcov-io.h
index 538bee81263a..99e1964c1094 100644
--- a/gcc/gcov-io.h
+++ b/gcc/gcov-io.h
@@ -367,6 +367,7 @@  char *mangle_path (char const *base);
 
 #if !IN_GCOV
 /* Available outside gcov */
+GCOV_LINKAGE void gcov_write (const void *, unsigned) ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE void gcov_write_unsigned (gcov_unsigned_t) ATTRIBUTE_HIDDEN;
 #endif
 
diff --git a/gcc/testsuite/gcc.dg/gcov-info-to-gcda.c b/gcc/testsuite/gcc.dg/gcov-info-to-gcda.c
new file mode 100644
index 000000000000..390f377de4fe
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gcov-info-to-gcda.c
@@ -0,0 +1,60 @@ 
+/* { dg-do run } */
+/* { dg-skip-if "profile-info-section" { powerpc-ibm-aix* } } */
+/* { dg-options "-fprofile-arcs -fprofile-info-section" } */
+
+#define assert(expr)						\
+  ((expr)                                                       \
+   ? (void)0                                                    \
+   : (__builtin_printf ("%s:%i: Assertion `%s' failed.\n",	\
+                        __FILE__, __LINE__, #expr),		\
+      __builtin_abort ()))
+
+struct gcov_info;
+
+extern void
+__gcov_info_to_gcda (const struct gcov_info *__info,
+		     void (*__filename_fn) (const char *, void *),
+		     void (*__dump_fn) (const void *, unsigned, void *),
+		     void *(*__allocate_fn) (unsigned, void *),
+		     void *__arg);
+
+extern const struct gcov_info *my_info;
+
+static unsigned counter;
+
+static void
+filename (const char *f, void *arg)
+{
+  assert (arg == &counter);
+  assert (__builtin_strstr (f, "gcov-info-to-gcda.c") == 0);
+}
+
+static void
+dump (const void *d, unsigned n, void *arg)
+{
+  unsigned *m = (unsigned *)arg;
+  assert (arg == &counter);
+
+  if (*m == 0)
+  {
+    const unsigned *u = d;
+    assert (*u == 0x67636461);
+  }
+
+  *m += n;
+}
+
+static void *
+allocate (unsigned length, void *arg)
+{
+  assert (arg == &counter);
+  return __builtin_malloc (length);
+}
+
+int main()
+{
+  __asm__ volatile (".set my_info, .LPBX2");
+  __gcov_info_to_gcda (my_info, filename, dump, allocate, &counter);
+  assert (counter > 4);
+  return 0;
+}
diff --git a/libgcc/Makefile.in b/libgcc/Makefile.in
index 2c8be561eb53..7ec975845544 100644
--- a/libgcc/Makefile.in
+++ b/libgcc/Makefile.in
@@ -908,7 +908,7 @@  LIBGCOV_INTERFACE = _gcov_dump _gcov_fork				\
 	_gcov_execl _gcov_execlp					\
 	_gcov_execle _gcov_execv _gcov_execvp _gcov_execve _gcov_reset  \
 	_gcov_lock_unlock
-LIBGCOV_DRIVER = _gcov
+LIBGCOV_DRIVER = _gcov _gcov_info_to_gcda
 
 libgcov-merge-objects = $(patsubst %,%$(objext),$(LIBGCOV_MERGE))
 libgcov-profiler-objects = $(patsubst %,%$(objext),$(LIBGCOV_PROFILER))
diff --git a/libgcc/gcov.h b/libgcc/gcov.h
index e6492cdd31ea..66d03bf4e5d4 100644
--- a/libgcc/gcov.h
+++ b/libgcc/gcov.h
@@ -25,6 +25,8 @@ 
 #ifndef GCC_GCOV_H
 #define GCC_GCOV_H
 
+struct gcov_info;
+
 /* Set all counters to zero.  */
 
 extern void __gcov_reset (void);
@@ -33,4 +35,21 @@  extern void __gcov_reset (void);
 
 extern void __gcov_dump (void);
 
+/* Convert the gcov information referenced by INFO to a gcda data stream.
+   The FILENAME_FN callback is called exactly once with the filename associated
+   with the gcov information.  The filename may be NULL.  Afterwards, the
+   DUMP_FN callback is subsequently called with chunks (the begin and length of
+   the chunk are passed as the first two callback parameters) of the gcda data
+   stream.  The ALLOCATE_FN callback shall allocate memory with a size in
+   characters specified by the first callback parameter.  The ARG parameter is
+   a user-provided argument passed as the last argument to the callback
+   functions.  */
+
+extern void
+__gcov_info_to_gcda (const struct gcov_info *__info,
+		     void (*__filename_fn) (const char *, void *),
+		     void (*__dump_fn) (const void *, unsigned, void *),
+		     void *(*__allocate_fn) (unsigned, void *),
+		     void *__arg);
+
 #endif /* GCC_GCOV_H */
diff --git a/libgcc/libgcov-driver.c b/libgcc/libgcov-driver.c
index df7ccb235677..32e312ec3cfa 100644
--- a/libgcc/libgcov-driver.c
+++ b/libgcc/libgcov-driver.c
@@ -26,6 +26,20 @@  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 #include "libgcov.h"
 #include "gcov-io.h"
 
+#include <stdint.h>
+
+/* Return 1, if all counter values are zero, otherwise 0. */
+
+static inline int
+are_all_counters_zero (const struct gcov_ctr_info *ci_ptr)
+{
+  for (unsigned i = 0; i < ci_ptr->num; i++)
+    if (ci_ptr->values[i] != 0)
+      return 0;
+
+  return 1;
+}
+
 #if defined(inhibit_libc)
 /* If libc and its header files are not available, provide dummy functions.  */
 
@@ -35,8 +49,6 @@  void __gcov_init (struct gcov_info *p __attribute__ ((unused))) {}
 
 #else /* inhibit_libc */
 
-#include <string.h>
-
 #if GCOV_LOCKED
 #include <fcntl.h>
 #include <errno.h>
@@ -51,8 +63,17 @@  void __gcov_init (struct gcov_info *p __attribute__ ((unused))) {}
 #include <sys/mman.h>
 #endif
 
-#ifdef L_gcov
+#endif /* inhibit_libc */
+
+#if defined(L_gcov) && !defined(inhibit_libc)
+#define NEED_L_GCOV
+#endif
 
+#if defined(L_gcov_info_to_gcda) && !IN_GCOV_TOOL
+#define NEED_L_GCOV_INFO_TO_GCDA
+#endif
+
+#ifdef NEED_L_GCOV
 /* A utility function for outputting errors.  */
 static int gcov_error (const char *, ...);
 
@@ -343,6 +364,51 @@  read_error:
   return -1;
 }
 
+/* Write the DATA of LENGTH characters to the gcov file.  */
+
+static void
+gcov_dump_handler (const void *data,
+		   unsigned length,
+		   void *arg ATTRIBUTE_UNUSED)
+{
+  gcov_write (data, length);
+}
+
+/* Allocate SIZE characters and return the address of the allocated memory.  */
+
+static void *
+gcov_allocate_handler (unsigned size, void *arg ATTRIBUTE_UNUSED)
+{
+  return xmalloc (size);
+}
+#endif /* NEED_L_GCOV */
+
+#if defined(NEED_L_GCOV) || defined(NEED_L_GCOV_INFO_TO_GCDA)
+/* Dump the WORD using the DUMP handler called with ARG.  */
+
+static inline void
+dump_unsigned (gcov_unsigned_t word,
+	       void (*dump_fn) (const void *, unsigned, void *),
+	       void *arg)
+{
+  (*dump_fn) (&word, sizeof (word), arg);
+}
+
+/* Dump the COUNTER using the DUMP handler called with ARG.  */
+
+static inline void
+dump_counter (gcov_type counter,
+	      void (*dump_fn) (const void *, unsigned, void *),
+	      void *arg)
+{
+  dump_unsigned ((gcov_unsigned_t)counter, dump_fn, arg);
+
+  if (sizeof (counter) > sizeof (gcov_unsigned_t))
+    dump_unsigned ((gcov_unsigned_t)(counter >> 32), dump_fn, arg);
+  else
+    dump_unsigned (0, dump_fn, arg);
+}
+
 #define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
 
 /* Store all TOP N counters where each has a dynamic length.  */
@@ -350,7 +416,10 @@  read_error:
 static void
 write_topn_counters (const struct gcov_ctr_info *ci_ptr,
 		     unsigned t_ix,
-		     gcov_unsigned_t n_counts)
+		     gcov_unsigned_t n_counts,
+		     void (*dump_fn) (const void *, unsigned, void *),
+		     void *(*allocate_fn)(unsigned, void *),
+		     void *arg)
 {
   unsigned counters = n_counts / GCOV_TOPN_MEM_COUNTERS;
   gcc_assert (n_counts % GCOV_TOPN_MEM_COUNTERS == 0);
@@ -365,46 +434,49 @@  write_topn_counters (const struct gcov_ctr_info *ci_ptr,
   if (list_sizes == NULL || counters > list_size_length)
     {
       list_size_length = MAX (LIST_SIZE_MIN_LENGTH, 2 * counters);
-#if HAVE_SYS_MMAN_H
+#if !defined(inhibit_libc) && HAVE_SYS_MMAN_H
       list_sizes
 	= (unsigned *)malloc_mmap (list_size_length * sizeof (unsigned));
 #endif
 
       /* Malloc fallback.  */
       if (list_sizes == NULL)
-	list_sizes = (unsigned *)xmalloc (list_size_length * sizeof (unsigned));
+	list_sizes =
+	  (unsigned *)(*allocate_fn) (list_size_length * sizeof (unsigned),
+				      arg);
     }
 
-  memset (list_sizes, 0, counters * sizeof (unsigned));
   unsigned pair_total = 0;
 
   for (unsigned i = 0; i < counters; i++)
     {
       gcov_type start = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 2];
+      unsigned sizes = 0;
+
       for (struct gcov_kvp *node = (struct gcov_kvp *)(intptr_t)start;
 	   node != NULL; node = node->next)
-	{
-	  ++pair_total;
-	  ++list_sizes[i];
-	}
+	++sizes;
+
+      pair_total += sizes;
+      list_sizes[i] = sizes;
     }
 
   unsigned disk_size = GCOV_TOPN_DISK_COUNTERS * counters + 2 * pair_total;
-  gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
-			 GCOV_TAG_COUNTER_LENGTH (disk_size));
+  dump_unsigned (GCOV_TAG_FOR_COUNTER (t_ix), dump_fn, arg),
+  dump_unsigned (GCOV_TAG_COUNTER_LENGTH (disk_size), dump_fn, arg);
 
   for (unsigned i = 0; i < counters; i++)
     {
-      gcov_write_counter (ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i]);
-      gcov_write_counter (list_sizes[i]);
+      dump_counter (ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i], dump_fn, arg);
+      dump_counter (list_sizes[i], dump_fn, arg);
       gcov_type start = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 2];
 
       unsigned j = 0;
       for (struct gcov_kvp *node = (struct gcov_kvp *)(intptr_t)start;
 	   j < list_sizes[i]; node = node->next, j++)
 	{
-	  gcov_write_counter (node->value);
-	  gcov_write_counter (node->count);
+	  dump_counter (node->value, dump_fn, arg);
+	  dump_counter (node->count, dump_fn, arg);
 	}
     }
 }
@@ -415,25 +487,36 @@  write_topn_counters (const struct gcov_ctr_info *ci_ptr,
 
 static void
 write_one_data (const struct gcov_info *gi_ptr,
-		const struct gcov_summary *prg_p)
+		const struct gcov_summary *prg_p,
+		void (*dump_fn) (const void *, unsigned, void *),
+		void *(*allocate_fn) (unsigned, void *),
+		void *arg)
 {
   unsigned f_ix;
 
-  gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION);
-  gcov_write_unsigned (gi_ptr->stamp);
+  dump_unsigned (GCOV_DATA_MAGIC, dump_fn, arg);
+  dump_unsigned (GCOV_VERSION, dump_fn, arg);
+  dump_unsigned (gi_ptr->stamp, dump_fn, arg);
 
+#ifdef NEED_L_GCOV
   /* Generate whole program statistics.  */
   gcov_write_summary (GCOV_TAG_OBJECT_SUMMARY, prg_p);
+#else
+  (void)prg_p;
+#endif
 
   /* Write execution counts for each function.  */
   for (f_ix = 0; f_ix != gi_ptr->n_functions; f_ix++)
     {
+#ifdef NEED_L_GCOV
       unsigned buffered = 0;
+#endif
       const struct gcov_fn_info *gfi_ptr;
       const struct gcov_ctr_info *ci_ptr;
       gcov_unsigned_t length;
       unsigned t_ix;
 
+#ifdef NEED_L_GCOV
       if (fn_buffer && fn_buffer->fn_ix == f_ix)
         {
           /* Buffered data from another program.  */
@@ -442,6 +525,7 @@  write_one_data (const struct gcov_info *gi_ptr,
           length = GCOV_TAG_FUNCTION_LENGTH;
         }
       else
+#endif
         {
           gfi_ptr = gi_ptr->functions[f_ix];
           if (gfi_ptr && gfi_ptr->key == gi_ptr)
@@ -450,13 +534,14 @@  write_one_data (const struct gcov_info *gi_ptr,
                 length = 0;
         }
 
-      gcov_write_tag_length (GCOV_TAG_FUNCTION, length);
+      dump_unsigned (GCOV_TAG_FUNCTION, dump_fn, arg);
+      dump_unsigned (length, dump_fn, arg);
       if (!length)
         continue;
 
-      gcov_write_unsigned (gfi_ptr->ident);
-      gcov_write_unsigned (gfi_ptr->lineno_checksum);
-      gcov_write_unsigned (gfi_ptr->cfg_checksum);
+      dump_unsigned (gfi_ptr->ident, dump_fn, arg);
+      dump_unsigned (gfi_ptr->lineno_checksum, dump_fn, arg);
+      dump_unsigned (gfi_ptr->cfg_checksum, dump_fn, arg);
 
       ci_ptr = gfi_ptr->ctrs;
       for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
@@ -469,39 +554,43 @@  write_one_data (const struct gcov_info *gi_ptr,
 	  n_counts = ci_ptr->num;
 
 	  if (t_ix == GCOV_COUNTER_V_TOPN || t_ix == GCOV_COUNTER_V_INDIR)
-	    write_topn_counters (ci_ptr, t_ix, n_counts);
+	    write_topn_counters (ci_ptr,
+				 t_ix,
+				 n_counts,
+				 dump_fn,
+				 allocate_fn,
+				 arg);
 	  else
 	    {
-	      /* Do not stream when all counters are zero.  */
-	      int all_zeros = 1;
-	      for (unsigned i = 0; i < n_counts; i++)
-		if (ci_ptr->values[i] != 0)
-		  {
-		    all_zeros = 0;
-		    break;
-		  }
-
-	      if (all_zeros)
-		gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
-				       GCOV_TAG_COUNTER_LENGTH (-n_counts));
+	      dump_unsigned (GCOV_TAG_FOR_COUNTER (t_ix), dump_fn, arg);
+	      if (are_all_counters_zero (ci_ptr))
+		/* Do not stream when all counters are zero.  */
+		dump_unsigned (GCOV_TAG_COUNTER_LENGTH (-n_counts),
+			       dump_fn,
+			       arg);
 	      else
 		{
-		  gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
-					 GCOV_TAG_COUNTER_LENGTH (n_counts));
+		  dump_unsigned (GCOV_TAG_COUNTER_LENGTH (n_counts),
+				 dump_fn,
+				 arg);
 		  for (unsigned i = 0; i < n_counts; i++)
-		    gcov_write_counter (ci_ptr->values[i]);
+		    dump_counter (ci_ptr->values[i], dump_fn, arg);
 		}
 	    }
 
 	  ci_ptr++;
 	}
+#ifdef NEED_L_GCOV
       if (buffered)
         fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
+#endif
     }
 
-  gcov_write_unsigned (0);
+  dump_unsigned (0, dump_fn, arg);
 }
+#endif /* NEED_L_GCOV || NEED_L_GCOV_INFO_TO_GCDA */
 
+#ifdef NEED_L_GCOV
 /* 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
@@ -550,7 +639,11 @@  dump_one_gcov (struct gcov_info *gi_ptr, struct gcov_filename *gf,
   summary = gi_ptr->summary;
 #endif
 
-  write_one_data (gi_ptr, &summary);
+  write_one_data (gi_ptr,
+		  &summary,
+		  gcov_dump_handler,
+		  gcov_allocate_handler,
+		  NULL);
   /* fall through */
 
 read_fatal:;
@@ -680,5 +773,20 @@  __gcov_init (struct gcov_info *info)
     }
 }
 #endif /* !IN_GCOV_TOOL */
-#endif /* L_gcov */
-#endif /* inhibit_libc */
+#endif /* NEED_L_GCOV */
+
+#ifdef NEED_L_GCOV_INFO_TO_GCDA
+/* Convert the gcov info to a gcda data stream.  It is intended for
+   free-standing environments which do not support the C library file I/O.  */
+
+void
+__gcov_info_to_gcda (const struct gcov_info *gi_ptr,
+		     void (*filename_fn) (const char *, void *),
+		     void (*dump_fn) (const void *, unsigned, void *),
+		     void *(*allocate_fn) (unsigned, void *),
+		     void *arg)
+{
+  (*filename_fn) (gi_ptr->filename, arg);
+  write_one_data (gi_ptr, NULL, dump_fn, allocate_fn, arg);
+}
+#endif /* NEED_L_GCOV_INFO_TO_GCDA */
diff --git a/libgcc/libgcov.h b/libgcc/libgcov.h
index 8d323db05386..9c537253293f 100644
--- a/libgcc/libgcov.h
+++ b/libgcc/libgcov.h
@@ -114,13 +114,11 @@  typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI)));
 #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
@@ -345,9 +343,6 @@  extern int __gcov_execve (const char *, char  *const [], char *const [])
 
 /* 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;