From patchwork Tue Nov 17 09:57:41 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sebastian Huber X-Patchwork-Id: 1401446 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=sourceware.org; envelope-from=gcc-patches-bounces@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=embedded-brains.de Received: from sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4Cb1b53ddrz9sRK for ; Tue, 17 Nov 2020 20:57:52 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id A1A9D397240E; Tue, 17 Nov 2020 09:57:48 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from dedi548.your-server.de (dedi548.your-server.de [85.10.215.148]) by sourceware.org (Postfix) with ESMTPS id 9B6403858019 for ; Tue, 17 Nov 2020 09:57:45 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 9B6403858019 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=embedded-brains.de Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=sebastian.huber@embedded-brains.de Received: from sslproxy01.your-server.de ([78.46.139.224]) by dedi548.your-server.de with esmtpsa (TLSv1.3:TLS_AES_256_GCM_SHA384:256) (Exim 4.92.3) (envelope-from ) id 1kexk6-00019N-GU; Tue, 17 Nov 2020 10:57:42 +0100 Received: from [82.100.198.138] (helo=mail.embedded-brains.de) by sslproxy01.your-server.de with esmtpsa (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.92) (envelope-from ) id 1kexk6-0005iM-DF; Tue, 17 Nov 2020 10:57:42 +0100 Received: from localhost (localhost.localhost [127.0.0.1]) by mail.embedded-brains.de (Postfix) with ESMTP id 5314A2A1610; Tue, 17 Nov 2020 10:54:35 +0100 (CET) Received: from mail.embedded-brains.de ([127.0.0.1]) by localhost (zimbra.eb.localhost [127.0.0.1]) (amavisd-new, port 10032) with ESMTP id G7vYE2lmNE3i; Tue, 17 Nov 2020 10:54:34 +0100 (CET) Received: from localhost (localhost.localhost [127.0.0.1]) by mail.embedded-brains.de (Postfix) with ESMTP id A5DAA2A165B; Tue, 17 Nov 2020 10:54:34 +0100 (CET) X-Virus-Scanned: amavisd-new at zimbra.eb.localhost Received: from mail.embedded-brains.de ([127.0.0.1]) by localhost (zimbra.eb.localhost [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id h66RtU3iadTT; Tue, 17 Nov 2020 10:54:34 +0100 (CET) Received: from linux-diu0.suse (unknown [192.168.96.161]) by mail.embedded-brains.de (Postfix) with ESMTP id 820442A1610; Tue, 17 Nov 2020 10:54:34 +0100 (CET) From: Sebastian Huber To: gcc-patches@gcc.gnu.org Subject: [PATCH] gcov: Add __gcov_info_to_gdca() Date: Tue, 17 Nov 2020 10:57:41 +0100 Message-Id: <20201117095741.3143-1-sebastian.huber@embedded-brains.de> X-Mailer: git-send-email 2.26.2 MIME-Version: 1.0 X-Authenticated-Sender: smtp-embedded@poldinet.de X-Virus-Scanned: Clear (ClamAV 0.102.4/25990/Mon Nov 16 14:19:13 2020) X-Spam-Status: No, score=-11.7 required=5.0 tests=BAYES_00, GIT_PATCH_0, KAM_DMARC_STATUS, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) 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: , Errors-To: gcc-patches-bounces@gcc.gnu.org Sender: "Gcc-patches" This is a proposal to get the gcda data for a gcda info in a free-standing 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: #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; unsigned i; c = d; for (i = 0; i < n; ++i) { printf("%02x", c[i]); } } int main() { __asm__ volatile (".set my_info, .LPBX2"); __gcov_info_to_gcda(my_info, filename, dump, NULL); return 0; } gcc/ * doc/invoke.texi (fprofile-info-section): Mention __gcov_info_to_gdca(). libgcc/ Makefile.in (LIBGCOV_DRIVER): Add _gcov_info_to_gcda. gcov.h (gcov_info): Declare. (__gcov_info_to_gdca): Likewise. libgcov-driver.c (gcov_are_all_counters_zero): New. (write_one_data): Use gcov_are_all_counters_zero(). (gcov_fn_info_to_gcda): New. (__gcov_info_to_gcda): Likewise. --- gcc/doc/invoke.texi | 73 ++++++++++++++++++++---- libgcc/Makefile.in | 2 +- libgcc/gcov.h | 15 +++++ libgcc/libgcov-driver.c | 120 ++++++++++++++++++++++++++++++++++++---- 4 files changed, 188 insertions(+), 22 deletions(-) diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 3510a54c6c4..09cb4922f5e 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -14248,17 +14248,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 : @@ -14269,6 +14269,57 @@ 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 + +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 +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, arg); + putchar ('\n'); + ++info; + @} +@} + +int +main() +@{ + dump_gcov_info(); + return 0; +@} +@end smallexample + @item -fprofile-note=@var{path} @opindex fprofile-note diff --git a/libgcc/Makefile.in b/libgcc/Makefile.in index d6075d32bd4..c22413d768c 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 0e3eed31032..371ee6feb11 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,17 @@ extern void __gcov_reset (void); extern void __gcov_dump (void); +/* Convert the gcov information to a gcda data stream. The first callback is + called exactly once with the filename associated with the gcov information. + The filename may be NULL. Afterwards, the second callback is subsequently + called with chunks (the begin and length of the chunk are passed as the + first two arguments) of the gcda data stream. The fourth 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 *, + void (*) (const char *, void *), + void (*) (const void *, unsigned, void *), + void *); + #endif /* GCC_GCOV_H */ diff --git a/libgcc/libgcov-driver.c b/libgcc/libgcov-driver.c index e53e4dc392a..4191b89a6f7 100644 --- a/libgcc/libgcov-driver.c +++ b/libgcc/libgcov-driver.c @@ -26,6 +26,18 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see #include "libgcov.h" #include "gcov-io.h" +/* Return 1, if all counter values are zero, otherwise 0. */ + +static inline int +gcov_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. */ @@ -428,16 +440,8 @@ write_one_data (const struct gcov_info *gi_ptr, write_top_counters (ci_ptr, t_ix, n_counts); 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) + if (gcov_are_all_counters_zero (ci_ptr)) + /* Do not stream when all counters are zero. */ gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix), GCOV_TAG_COUNTER_LENGTH (-n_counts)); else @@ -637,3 +641,99 @@ __gcov_init (struct gcov_info *info) #endif /* !IN_GCOV_TOOL */ #endif /* L_gcov */ #endif /* inhibit_libc */ + +#if !IN_GCOV_TOOL +#ifdef L_gcov_info_to_gcda +static void +gcov_fn_info_to_gcda (const struct gcov_info *gi_ptr, + const struct gcov_fn_info *gfi_ptr, + void (*dump) (const void *, unsigned, void *), + void *arg) +{ + const struct gcov_ctr_info *ci_ptr = gfi_ptr->ctrs; + + for (unsigned t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++) + { + if (!gi_ptr->merge[t_ix]) + continue; + + if (t_ix != GCOV_COUNTER_V_TOPN && t_ix != GCOV_COUNTER_V_INDIR) + { + gcov_unsigned_t word = GCOV_TAG_FOR_COUNTER (t_ix); + (*dump) (&word, sizeof (word), arg); + gcov_position_t n_counts = ci_ptr->num; + + if (gcov_are_all_counters_zero (ci_ptr)) + { + /* Do not stream when all counters are zero. */ + word = GCOV_TAG_COUNTER_LENGTH (-n_counts); + (*dump) (&word, sizeof (word), arg); + } + else + { + word = GCOV_TAG_COUNTER_LENGTH (n_counts); + (*dump) (&word, sizeof (word), arg); + + for (unsigned i = 0; i < n_counts; i++) + { + gcov_type value = ci_ptr->values[i]; + word = (gcov_unsigned_t) value; + (*dump) (&word, sizeof (word), arg); + + if (sizeof (value) > sizeof (word)) + word = (gcov_unsigned_t) (value >> 32); + else + word = 0; + + (*dump) (&word, sizeof (word), arg); + } + } + } + + ci_ptr++; + } +} + +/* Convert the gcov info to a gcda data stream. This function does not support + whole program statistics and top counters. It is intended for free-standing + environments which do not support the C library file I/O. For the data + format, see also write_one_data(). */ + +void +__gcov_info_to_gcda (const struct gcov_info *gi_ptr, + void (*filename) (const char *, void *), + void (*dump) (const void *, unsigned, void *), + void *arg) +{ + (*filename) (gi_ptr->filename, arg); + gcov_unsigned_t word = GCOV_DATA_MAGIC; + (*dump) (&word, sizeof (word), arg); + (*dump) (&gi_ptr->version, sizeof (gi_ptr->version), arg); + (*dump) (&gi_ptr->stamp, sizeof (gi_ptr->stamp), arg); + + for (unsigned f_ix = 0; f_ix != gi_ptr->n_functions; f_ix++) + { + word = GCOV_TAG_FUNCTION; + (*dump) (&word, sizeof (word), arg); + + const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix]; + if (gfi_ptr && gfi_ptr->key == gi_ptr) + word = GCOV_TAG_FUNCTION_LENGTH; + else + word = 0; + (*dump) (&word, sizeof (word), arg); + if (!word) + continue; + + (*dump) (&gfi_ptr->ident, sizeof (gfi_ptr->ident), arg); + (*dump) (&gfi_ptr->lineno_checksum, + sizeof (gfi_ptr->lineno_checksum), arg); + (*dump) (&gfi_ptr->cfg_checksum, sizeof (gfi_ptr->cfg_checksum), arg); + gcov_fn_info_to_gcda (gi_ptr, gfi_ptr, dump, arg); + } + + word = 0; + (*dump) (&word, sizeof (word), arg); +} +#endif /* L_gcov_info_to_gcda */ +#endif /* !IN_GCOV_TOOL */