From 39d80300b3c769c3a7805a46ee5facc6adf1a4d0 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Wed, 30 Mar 2022 21:45:23 +0200 Subject: [PATCH] gcov: Add __gcov_filename_to_gcfn() gcc/ * doc/invoke.texi (fprofile-info-section): Mention __gcov_filename_to_gcfn(). Use "freestanding" to match with C11 standard language. Fix minor example code issues. * gcov-io.h (GCOV_FILENAME_MAGIC): Define and document. gcc/testsuite/ * gcc.dg/gcov-info-to-gcda.c: Test __gcov_filename_to_gcfn(). libgcc/ * gcov.h (__gcov_info_to_gcda): Mention __gcov_filename_to_gcfn(). (__gcov_filename_to_gcfn): Declare and document. * libgcov-driver.c (dump_string): New. (__gcov_filename_to_gcfn): Likewise. (__gcov_info_to_gcda): Adjust comment to match C11 standard language. --- gcc/doc/invoke.texi | 26 ++++++++--------- gcc/gcov-io.h | 24 ++++++++++------ gcc/testsuite/gcc.dg/gcov-info-to-gcda.c | 36 +++++++++++++++++++----- libgcc/gcov.h | 17 ++++++++++- libgcc/libgcov-driver.c | 32 ++++++++++++++++++++- 5 files changed, 105 insertions(+), 30 deletions(-) diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index b2d2cea4557..7cff38b780e 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -15462,7 +15462,7 @@ 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 +used in hosted environments such as GNU/Linux. It targets freestanding environments (for example embedded systems) with limited resources which do not support constructors/destructors or the C library file I/O. @@ -15487,14 +15487,8 @@ for example like this: #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); -@} +extern const struct gcov_info *const __gcov_info_start[]; +extern const struct gcov_info *const __gcov_info_end[]; static void dump (const void *d, unsigned n, void *arg) @@ -15505,6 +15499,12 @@ dump (const void *d, unsigned n, void *arg) printf ("%02x", c[i]); @} +static void +filename (const char *f, void *arg) +@{ + __gcov_filename_to_gcfn (f, dump, arg ); +@} + static void * allocate (unsigned length, void *arg) @{ @@ -15514,8 +15514,8 @@ allocate (unsigned length, void *arg) static void dump_gcov_info (void) @{ - const struct gcov_info **info = __gcov_info_start; - const struct gcov_info **end = __gcov_info_end; + const struct gcov_info *const *info = __gcov_info_start; + const struct gcov_info *const *end = __gcov_info_end; /* Obfuscate variable to prevent compiler optimizations. */ __asm__ ("" : "+r" (info)); @@ -15530,9 +15530,9 @@ dump_gcov_info (void) @} int -main() +main (void) @{ - dump_gcov_info(); + dump_gcov_info (); return 0; @} @end smallexample diff --git a/gcc/gcov-io.h b/gcc/gcov-io.h index 204ae0ccf7f..30947634d73 100644 --- a/gcc/gcov-io.h +++ b/gcc/gcov-io.h @@ -60,14 +60,21 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see file : int32:magic int32:version int32:stamp record* - The magic ident is different for the notes and the data files. The - magic ident is used to determine the endianness of the file, when - reading. The version is the same for both files and is derived - from gcc's version number. The stamp value is used to synchronize - note and data files and to synchronize merging within a data - file. It need not be an absolute time stamp, merely a ticker that - increments fast enough and cycles slow enough to distinguish - different compile/run/compile cycles. + A filename header may be used to provide a filename for the data in + a stream of data to support gcov in freestanding environments. This + header is used by the merge-stream subcommand of the gcov-tool. The + format of the filename header is + + filename-header : int32:magic int32:version string + + The magic ident is different for the notes and the data files as + well as the filename header. The magic ident is used to determine + the endianness of the file, when reading. The version is the same + for both files and is derived from gcc's version number. The stamp + value is used to synchronize note and data files and to synchronize + merging within a data file. It need not be an absolute time stamp, + merely a ticker that increments fast enough and cycles slow enough + to distinguish different compile/run/compile cycles. Although the ident and version are formally 32 bit numbers, they are derived from 4 character ASCII strings. The version number @@ -228,6 +235,7 @@ typedef uint64_t gcov_type_unsigned; /* File magic. Must not be palindromes. */ #define GCOV_DATA_MAGIC ((gcov_unsigned_t)0x67636461) /* "gcda" */ #define GCOV_NOTE_MAGIC ((gcov_unsigned_t)0x67636e6f) /* "gcno" */ +#define GCOV_FILENAME_MAGIC ((gcov_unsigned_t)0x6763666e) /* "gcfn" */ #include "version.h" diff --git a/gcc/testsuite/gcc.dg/gcov-info-to-gcda.c b/gcc/testsuite/gcc.dg/gcov-info-to-gcda.c index 4583360feef..ca9b3e831fe 100644 --- a/gcc/testsuite/gcc.dg/gcov-info-to-gcda.c +++ b/gcc/testsuite/gcc.dg/gcov-info-to-gcda.c @@ -17,16 +17,20 @@ __gcov_info_to_gcda (const struct gcov_info *__info, void *(*__allocate_fn) (unsigned, void *), void *__arg); +extern void +__gcov_filename_to_gcfn (const char *__filename, + void (*__dump_fn) (const void *, 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 unsigned counter_after_filename; + +static int check_zero; + +static int check_after_filename; static void dump (const void *d, unsigned n, void *arg) @@ -35,14 +39,30 @@ dump (const void *d, unsigned n, void *arg) assert (arg == &counter); if (*m == 0) + { + const unsigned *u = d; + assert (*u == 0x6763666e); + check_zero = 1; + } + else if (*m == counter_after_filename) { const unsigned *u = d; assert (*u == 0x67636461); + check_after_filename = 1; } *m += n; } +static void +filename (const char *f, void *arg) +{ + assert (arg == &counter); + assert (__builtin_strstr (f, "gcov-info-to-gcda.c") == 0); + __gcov_filename_to_gcfn (f, dump, arg); + counter_after_filename = counter; +} + static void * allocate (unsigned length, void *arg) { @@ -54,6 +74,8 @@ int main() { __asm__ volatile (".set my_info, .LPBX2"); __gcov_info_to_gcda (my_info, filename, dump, allocate, &counter); - assert (counter > 4); + assert (counter > 8); + assert (check_zero); + assert (check_after_filename); return 0; } diff --git a/libgcc/gcov.h b/libgcc/gcov.h index cea93023920..cdd4206f625 100644 --- a/libgcc/gcov.h +++ b/libgcc/gcov.h @@ -43,7 +43,8 @@ extern void __gcov_dump (void); 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. */ + functions. It is recommended to use the __gcov_filename_to_gcfn() + in the filename callback function. */ extern void __gcov_info_to_gcda (const struct gcov_info *__info, @@ -52,4 +53,18 @@ __gcov_info_to_gcda (const struct gcov_info *__info, void *(*__allocate_fn) (unsigned, void *), void *__arg); +/* Convert the FILENAME to a gcfn data stream. 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 gcfn data stream. + The ARG parameter is a user-provided argument passed as the last + argument to the DUMP_FN callback function. This function is intended + to be used by the filename callback of __gcov_info_to_gcda(). The gcfn + data stream is used by the merge-stream subcommand of the gcov-tool to + get the filename associated with a gcda data stream. */ + +extern void +__gcov_filename_to_gcfn (const char *__filename, + void (*__dump_fn) (const void *, unsigned, void *), + void *__arg); + #endif /* GCC_GCOV_H */ diff --git a/libgcc/libgcov-driver.c b/libgcc/libgcov-driver.c index 10831e84b61..d4517d269eb 100644 --- a/libgcc/libgcov-driver.c +++ b/libgcc/libgcov-driver.c @@ -410,6 +410,23 @@ dump_counter (gcov_type counter, dump_unsigned (0, dump_fn, arg); } +/* Dump the STRING using the DUMP handler called with ARG. */ + +static inline void +dump_string (const char *string, + void (*dump_fn) (const void *, unsigned, void *), + void *arg) +{ + unsigned length = 0; + + if (string) + length = strlen (string) + 1; + + dump_unsigned (length, dump_fn, arg); + if (string) + (*dump_fn) (string, length, arg); +} + #define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) /* Store all TOP N counters where each has a dynamic length. */ @@ -768,7 +785,7 @@ __gcov_init (struct gcov_info *info) #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. */ + freestanding environments which do not support the C library file I/O. */ void __gcov_info_to_gcda (const struct gcov_info *gi_ptr, @@ -780,4 +797,17 @@ __gcov_info_to_gcda (const struct gcov_info *gi_ptr, (*filename_fn) (gi_ptr->filename, arg); write_one_data (gi_ptr, NULL, dump_fn, allocate_fn, arg); } + +/* Convert the filename to a gcfn data stream. It is intended for + freestanding environments which do not support the C library file I/O. */ + +void +__gcov_filename_to_gcfn (const char *filename, + void (*dump_fn) (const void *, unsigned, void *), + void *arg) +{ + dump_unsigned (GCOV_FILENAME_MAGIC, dump_fn, arg); + dump_unsigned (GCOV_VERSION, dump_fn, arg); + dump_string (filename, dump_fn, arg); +} #endif /* NEED_L_GCOV_INFO_TO_GCDA */