From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.220.28]) by sourceware.org (Postfix) with ESMTPS id 2B2493858024 for ; Thu, 5 Aug 2021 12:54:02 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 2B2493858024 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=suse.cz Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=suse.cz Received: from imap1.suse-dmz.suse.de (imap1.suse-dmz.suse.de [192.168.254.73]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id 5FE4221F25; Thu, 5 Aug 2021 12:54:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.cz; s=susede2_rsa; t=1628168040; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc: mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=o+JItMSb75TDoy4Nc9voOwot+j6LduJtJIir+nV8zg8=; b=pJHHx1ZQbRJwStOcW5jgSxEkZ6vMCnr90ZocMYPXY0V4oHtW1lgkGG/yaRKQCDzpa9kkHf 5ZOWq2aowoEgK2sxNKOylArSsydHplsNDoyFb2cmLGxBELOqglY+tLM9GqvH1cwcAgCefF qMz3EfSPV++Ox/i75Ua0r1XXPrMz0Gs= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.cz; s=susede2_ed25519; t=1628168040; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc: mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=o+JItMSb75TDoy4Nc9voOwot+j6LduJtJIir+nV8zg8=; b=NO9fBe/+JRVuC0i1N+K3+ZiWLthsvby0P2BiQ42rBKlptnDm31Ey4cJBUZNwjCOpaxKUlG zY9FtEJXqPmOCvCw== Received: from imap1.suse-dmz.suse.de (imap1.suse-dmz.suse.de [192.168.254.73]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by imap1.suse-dmz.suse.de (Postfix) with ESMTPS id 4982E13A46; Thu, 5 Aug 2021 12:54:00 +0000 (UTC) Received: from dovecot-director2.suse.de ([192.168.254.65]) by imap1.suse-dmz.suse.de with ESMTPSA id 8k7mEGjfC2H6JQAAGKfGzw (envelope-from ); Thu, 05 Aug 2021 12:54:00 +0000 Subject: Re: [PATCH v3] gcov: Add __gcov_info_to_gdca() To: Sebastian Huber , gcc-patches@gcc.gnu.org References: <20210723093911.81759-1-sebastian.huber@embedded-brains.de> From: =?UTF-8?Q?Martin_Li=c5=a1ka?= Message-ID: Date: Thu, 5 Aug 2021 14:53:59 +0200 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.12.0 MIME-Version: 1.0 In-Reply-To: <20210723093911.81759-1-sebastian.huber@embedded-brains.de> Content-Type: text/plain; charset=utf-8; format=flowed Content-Language: en-US Content-Transfer-Encoding: 7bit X-Spam-Status: No, score=-10.9 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, NICE_REPLY_A, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 05 Aug 2021 12:54:05 -0000 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 > #include > #include > > 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, 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 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 ): New. > (#include ): 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 > +#include > +#include > + > +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 > + > +/* 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 > - > #if GCOV_LOCKED > #include > #include > @@ -51,8 +63,17 @@ void __gcov_init (struct gcov_info *p __attribute__ ((unused))) {} > #include > #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; >