public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] gcov: Add __gcov_info_to_gdca()
@ 2020-11-17  9:57 Sebastian Huber
  2020-11-20  8:37 ` Martin Liška
  2020-11-23 14:30 ` Martin Liška
  0 siblings, 2 replies; 16+ messages in thread
From: Sebastian Huber @ 2020-11-17  9:57 UTC (permalink / raw)
  To: 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 <gcov.h>
  #include <stdio.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;
    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 <gcov.h>
+#include <stdio.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
+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 */
-- 
2.26.2


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

* Re: [PATCH] gcov: Add __gcov_info_to_gdca()
  2020-11-17  9:57 [PATCH] gcov: Add __gcov_info_to_gdca() Sebastian Huber
@ 2020-11-20  8:37 ` Martin Liška
  2020-11-20  9:25   ` Sebastian Huber
  2020-11-23 14:30 ` Martin Liška
  1 sibling, 1 reply; 16+ messages in thread
From: Martin Liška @ 2020-11-20  8:37 UTC (permalink / raw)
  To: Sebastian Huber, gcc-patches

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

On 11/17/20 10:57 AM, Sebastian Huber wrote:
> 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:

Hello.

I'm not pretty sure how this set up is going to work. Can you please explain me that?

I was thinking about your needs and I can imagine various techniques how to generate
gcda files format:

1) embedded system can override fopen, fwrite, fseek to a functions that do a remote
write-related functions

2) - use -fprofile-info-section
    - run an app on an embedded system and do a memory dump to a terminal/console
    - take the memory dump to a host system (with IO), run __gcov_init_from_memory_dump (...)
      and then do a normal __gcov_dump

What do you think about it?

Btw. I'm planning to commit in next stage1 removal of the internal I/O buffering.
Martin

[-- Attachment #2: 0001-gcov-Use-system-IO-buffering.patch --]
[-- Type: text/x-patch, Size: 16676 bytes --]

From 5a17015c096012b9e43a8dd45768a8d5fb3a3aee Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Wed, 18 Nov 2020 16:13:23 +0100
Subject: [PATCH] gcov: Use system IO buffering

gcc/ChangeLog:

	* gcov-io.c (gcov_write_block): Remove.
	(gcov_write_words): Likewise.
	(gcov_read_words): Re-implement using gcov_read_bytes.
	(gcov_allocate): Remove.
	(GCOV_BLOCK_SIZE): Likewise.
	(struct gcov_var): Remove most of the fields.
	(gcov_position): Implement with ftell.
	(gcov_rewrite): Remove setting of start and offset fields.
	(from_file): Re-format.
	(gcov_open): Remove setbuf call. It should not be needed.
	(gcov_close): Remove internal buffer handling.
	(gcov_magic): Use __builtin_bswap32.
	(gcov_write_counter): Use directly gcov_write_unsigned.
	(gcov_write_string): Use direct fwrite and do not round
	to 4 bytes.
	(gcov_seek): Use directly fseek.
	(gcov_write_tag): Use gcov_write_unsigned directly.
	(gcov_write_length): Likewise.
	(gcov_write_tag_length): Likewise.
	(gcov_read_bytes): Use directly fread.
	(gcov_read_unsigned): Use gcov_read_words.
	(gcov_read_counter): Likewise.
	(gcov_read_string): Use gcov_read_bytes.
	* gcov-io.h (GCOV_WORD_SIZE): Adjust to reflect
	that size is not in bytes, not words (4B).
	(GCOV_TAG_FUNCTION_LENGTH): Likewise.
	(GCOV_TAG_ARCS_LENGTH): Likewise.
	(GCOV_TAG_ARCS_NUM): Likewise.
	(GCOV_TAG_COUNTER_LENGTH): Likewise.
	(GCOV_TAG_COUNTER_NUM): Likewise.
	(GCOV_TAG_SUMMARY_LENGTH): Likewise.

libgcc/ChangeLog:

	* libgcov-driver.c: Fix GNU coding style.
---
 gcc/gcov-io.c           | 282 +++++++++-------------------------------
 gcc/gcov-io.h           |  17 ++-
 libgcc/libgcov-driver.c |   2 +-
 3 files changed, 75 insertions(+), 226 deletions(-)

diff --git a/gcc/gcov-io.c b/gcc/gcov-io.c
index 4db56f8aacf..c3ca404f8b5 100644
--- a/gcc/gcov-io.c
+++ b/gcc/gcov-io.c
@@ -27,40 +27,14 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 /* Routines declared in gcov-io.h.  This file should be #included by
    another source file, after having #included gcov-io.h.  */
 
-#if !IN_GCOV
-static void gcov_write_block (unsigned);
-static gcov_unsigned_t *gcov_write_words (unsigned);
-#endif
-static const gcov_unsigned_t *gcov_read_words (unsigned);
-#if !IN_LIBGCOV
-static void gcov_allocate (unsigned);
-#endif
-
-/* Optimum number of gcov_unsigned_t's read from or written to disk.  */
-#define GCOV_BLOCK_SIZE (1 << 10)
+static gcov_unsigned_t *gcov_read_words (void *buffer, unsigned);
 
 struct gcov_var
 {
   FILE *file;
-  gcov_position_t start;	/* Position of first byte of block */
-  unsigned offset;		/* Read/write position within the block.  */
-  unsigned length;		/* Read limit in the block.  */
-  unsigned overread;		/* Number of words overread.  */
   int error;			/* < 0 overflow, > 0 disk error.  */
-  int mode;	                /* < 0 writing, > 0 reading */
+  int mode;			/* < 0 writing, > 0 reading */
   int endian;			/* Swap endianness.  */
-#if IN_LIBGCOV
-  /* Holds one block plus 4 bytes, thus all coverage reads & writes
-     fit within this buffer and we always can transfer GCOV_BLOCK_SIZE
-     to and from the disk. libgcov never backtracks and only writes 4
-     or 8 byte objects.  */
-  gcov_unsigned_t buffer[GCOV_BLOCK_SIZE + 1];
-#else
-  /* Holds a variable length block, as the compiler can write
-     strings and needs to backtrack.  */
-  size_t alloc;
-  gcov_unsigned_t *buffer;
-#endif
 } gcov_var;
 
 /* Save the current position in the gcov file.  */
@@ -71,8 +45,7 @@ static inline
 gcov_position_t
 gcov_position (void)
 {
-  gcov_nonruntime_assert (gcov_var.mode > 0); 
-  return gcov_var.start + gcov_var.offset;
+  return ftell (gcov_var.file);
 }
 
 /* Return nonzero if the error flag is set.  */
@@ -92,20 +65,16 @@ GCOV_LINKAGE inline void
 gcov_rewrite (void)
 {
   gcov_var.mode = -1; 
-  gcov_var.start = 0;
-  gcov_var.offset = 0;
   fseek (gcov_var.file, 0L, SEEK_SET);
 }
 #endif
 
-static inline gcov_unsigned_t from_file (gcov_unsigned_t value)
+static inline gcov_unsigned_t
+from_file (gcov_unsigned_t value)
 {
 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
   if (gcov_var.endian)
-    {
-      value = (value >> 16) | (value << 16);
-      value = ((value & 0xff00ff) << 8) | ((value >> 8) & 0xff00ff);
-    }
+    return __builtin_bswap32 (value);
 #endif
   return value;
 }
@@ -140,9 +109,6 @@ gcov_open (const char *name, int mode)
 #endif
 
   gcov_nonruntime_assert (!gcov_var.file);
-  gcov_var.start = 0;
-  gcov_var.offset = gcov_var.length = 0;
-  gcov_var.overread = -1u;
   gcov_var.error = 0;
 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
   gcov_var.endian = 0;
@@ -192,8 +158,6 @@ gcov_open (const char *name, int mode)
 
   gcov_var.mode = mode ? mode : 1;
 
-  setbuf (gcov_var.file, (char *)0);
-
   return 1;
 }
 
@@ -205,19 +169,9 @@ gcov_close (void)
 {
   if (gcov_var.file)
     {
-#if !IN_GCOV
-      if (gcov_var.offset && gcov_var.mode < 0)
-	gcov_write_block (gcov_var.offset);
-#endif
       fclose (gcov_var.file);
       gcov_var.file = 0;
-      gcov_var.length = 0;
     }
-#if !IN_LIBGCOV
-  free (gcov_var.buffer);
-  gcov_var.alloc = 0;
-  gcov_var.buffer = 0;
-#endif
   gcov_var.mode = 0;
   return gcov_var.error;
 }
@@ -232,9 +186,8 @@ gcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected)
 {
   if (magic == expected)
     return 1;
-  magic = (magic >> 16) | (magic << 16);
-  magic = ((magic & 0xff00ff) << 8) | ((magic >> 8) & 0xff00ff);
-  if (magic == expected)
+
+  if (__builtin_bswap32 (magic) == expected)
     {
       gcov_var.endian = 1;
       return -1;
@@ -243,71 +196,15 @@ gcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected)
 }
 #endif
 
-#if !IN_LIBGCOV
-static void
-gcov_allocate (unsigned length)
-{
-  size_t new_size = gcov_var.alloc;
-
-  if (!new_size)
-    new_size = GCOV_BLOCK_SIZE;
-  new_size += length;
-  new_size *= 2;
-
-  gcov_var.alloc = new_size;
-  gcov_var.buffer = XRESIZEVAR (gcov_unsigned_t, gcov_var.buffer, new_size << 2);
-}
-#endif
-
 #if !IN_GCOV
-/* Write out the current block, if needs be.  */
-
-static void
-gcov_write_block (unsigned size)
-{
-  if (fwrite (gcov_var.buffer, size << 2, 1, gcov_var.file) != 1)
-    gcov_var.error = 1;
-  gcov_var.start += size;
-  gcov_var.offset -= size;
-}
-
-/* Allocate space to write BYTES bytes to the gcov file. Return a
-   pointer to those bytes, or NULL on failure.  */
-
-static gcov_unsigned_t *
-gcov_write_words (unsigned words)
-{
-  gcov_unsigned_t *result;
-
-  gcov_nonruntime_assert (gcov_var.mode < 0);
-#if IN_LIBGCOV
-  if (gcov_var.offset >= GCOV_BLOCK_SIZE)
-    {
-      gcov_write_block (GCOV_BLOCK_SIZE);
-      if (gcov_var.offset)
-	{
-	  memcpy (gcov_var.buffer, gcov_var.buffer + GCOV_BLOCK_SIZE, 4);
-	}
-    }
-#else
-  if (gcov_var.offset + words > gcov_var.alloc)
-    gcov_allocate (gcov_var.offset + words);
-#endif
-  result = &gcov_var.buffer[gcov_var.offset];
-  gcov_var.offset += words;
-
-  return result;
-}
-
-/* Write unsigned VALUE to coverage file.  Sets error flag
-   appropriately.  */
+/* Write unsigned VALUE to coverage file.  */
 
 GCOV_LINKAGE void
 gcov_write_unsigned (gcov_unsigned_t value)
 {
-  gcov_unsigned_t *buffer = gcov_write_words (1);
-
-  buffer[0] = value;
+  gcov_unsigned_t r = fwrite (&value, sizeof (value), 1, gcov_var.file);
+  if (r != 1)
+    gcov_var.error = 1;
 }
 
 /* Write counter VALUE to coverage file.  Sets error flag
@@ -317,13 +214,11 @@ gcov_write_unsigned (gcov_unsigned_t value)
 GCOV_LINKAGE void
 gcov_write_counter (gcov_type value)
 {
-  gcov_unsigned_t *buffer = gcov_write_words (2);
-
-  buffer[0] = (gcov_unsigned_t) value;
+  gcov_write_unsigned ((gcov_unsigned_t) value);
   if (sizeof (value) > sizeof (gcov_unsigned_t))
-    buffer[1] = (gcov_unsigned_t) (value >> 32);
+    gcov_write_unsigned ((gcov_unsigned_t) (value >> 32));
   else
-    buffer[1] = 0;
+    gcov_write_unsigned (0);
 }
 #endif /* IN_LIBGCOV */
 
@@ -335,23 +230,16 @@ GCOV_LINKAGE void
 gcov_write_string (const char *string)
 {
   unsigned length = 0;
-  unsigned alloc = 0;
-  gcov_unsigned_t *buffer;
 
   if (string)
-    {
-      length = strlen (string);
-      alloc = (length + 4) >> 2;
-    }
-
-  buffer = gcov_write_words (1 + alloc);
+    length = strlen (string) + 1;
 
-  buffer[0] = alloc;
-
-  if (alloc > 0)
+  gcov_write_unsigned (length);
+  if (length > 0)
     {
-      buffer[alloc] = 0; /* place nul terminators.  */
-      memcpy (&buffer[1], string, length);
+      gcov_unsigned_t r = fwrite (string, length, 1, gcov_var.file);
+      if (r != 1)
+	gcov_var.error = 1;
     }
 }
 #endif
@@ -388,6 +276,14 @@ gcov_write_filename (const char *filename)
 }
 #endif
 
+/* Move to a given position in a gcov file.  */
+
+GCOV_LINKAGE void
+gcov_seek (gcov_position_t base)
+{
+  fseek (gcov_var.file, base, SEEK_SET);
+}
+
 #if !IN_LIBGCOV
 /* Write a tag TAG and reserve space for the record length. Return a
    value to be used for gcov_write_length.  */
@@ -395,11 +291,9 @@ gcov_write_filename (const char *filename)
 GCOV_LINKAGE gcov_position_t
 gcov_write_tag (gcov_unsigned_t tag)
 {
-  gcov_position_t result = gcov_var.start + gcov_var.offset;
-  gcov_unsigned_t *buffer = gcov_write_words (2);
-
-  buffer[0] = tag;
-  buffer[1] = 0;
+  gcov_position_t result = gcov_position ();
+  gcov_write_unsigned (tag);
+  gcov_write_unsigned (0);
 
   return result;
 }
@@ -412,19 +306,13 @@ gcov_write_tag (gcov_unsigned_t tag)
 GCOV_LINKAGE void
 gcov_write_length (gcov_position_t position)
 {
-  unsigned offset;
-  gcov_unsigned_t length;
-  gcov_unsigned_t *buffer;
-
+  gcov_position_t current_position = gcov_position ();
   gcov_nonruntime_assert (gcov_var.mode < 0);
-  gcov_nonruntime_assert (position + 2 <= gcov_var.start + gcov_var.offset);
-  gcov_nonruntime_assert (position >= gcov_var.start);
-  offset = position - gcov_var.start;
-  length = gcov_var.offset - offset - 2;
-  buffer = (gcov_unsigned_t *) &gcov_var.buffer[offset];
-  buffer[1] = length;
-  if (gcov_var.offset >= GCOV_BLOCK_SIZE)
-    gcov_write_block (gcov_var.offset);
+  gcov_nonruntime_assert (current_position >= position + 2 * GCOV_WORD_SIZE);
+
+  gcov_seek (position + GCOV_WORD_SIZE);
+  gcov_write_unsigned (current_position - position - 2 * GCOV_WORD_SIZE);
+  gcov_seek (current_position);
 }
 
 #else /* IN_LIBGCOV */
@@ -434,10 +322,8 @@ gcov_write_length (gcov_position_t position)
 GCOV_LINKAGE void
 gcov_write_tag_length (gcov_unsigned_t tag, gcov_unsigned_t length)
 {
-  gcov_unsigned_t *buffer = gcov_write_words (2);
-
-  buffer[0] = tag;
-  buffer[1] = length;
+  gcov_write_unsigned (tag);
+  gcov_write_unsigned (length);
 }
 
 /* Write a summary structure to the gcov file.  Return nonzero on
@@ -455,52 +341,28 @@ gcov_write_summary (gcov_unsigned_t tag, const struct gcov_summary *summary)
 
 #endif /*!IN_GCOV */
 
-/* Return a pointer to read BYTES bytes from the gcov file. Returns
+/* Return a pointer to read COUNT bytes from the gcov file.  Returns
    NULL on failure (read past EOF).  */
 
-static const gcov_unsigned_t *
-gcov_read_words (unsigned words)
+static void *
+gcov_read_bytes (void *buffer, unsigned count)
 {
-  const gcov_unsigned_t *result;
-  unsigned excess = gcov_var.length - gcov_var.offset;
-
   if (gcov_var.mode <= 0)
     return NULL;
 
-  if (excess < words)
-    {
-      gcov_var.start += gcov_var.offset;
-      if (excess)
-	{
-#if IN_LIBGCOV
-	  memcpy (gcov_var.buffer, gcov_var.buffer + gcov_var.offset, 4);
-#else
-	  memmove (gcov_var.buffer, gcov_var.buffer + gcov_var.offset,
-		   excess * 4);
-#endif
-	}
-      gcov_var.offset = 0;
-      gcov_var.length = excess;
-#if IN_LIBGCOV
-      excess = GCOV_BLOCK_SIZE;
-#else
-      if (gcov_var.length + words > gcov_var.alloc)
-	gcov_allocate (gcov_var.length + words);
-      excess = gcov_var.alloc - gcov_var.length;
-#endif
-      excess = fread (gcov_var.buffer + gcov_var.length,
-		      1, excess << 2, gcov_var.file) >> 2;
-      gcov_var.length += excess;
-      if (gcov_var.length < words)
-	{
-	  gcov_var.overread += words - gcov_var.length;
-	  gcov_var.length = 0;
-	  return 0;
-	}
-    }
-  result = &gcov_var.buffer[gcov_var.offset];
-  gcov_var.offset += words;
-  return result;
+  unsigned read = fread (buffer, count, 1, gcov_var.file);
+  if (read != 1)
+    return NULL;
+
+  return buffer;
+}
+
+/* Read WORDS gcov_unsigned_t values from gcov file.  */
+
+static gcov_unsigned_t *
+gcov_read_words (void *buffer, unsigned words)
+{
+  return (gcov_unsigned_t *)gcov_read_bytes (buffer, GCOV_WORD_SIZE * words);
 }
 
 /* Read unsigned value from a coverage file. Sets error flag on file
@@ -510,10 +372,12 @@ GCOV_LINKAGE gcov_unsigned_t
 gcov_read_unsigned (void)
 {
   gcov_unsigned_t value;
-  const gcov_unsigned_t *buffer = gcov_read_words (1);
+  gcov_unsigned_t allocated_buffer[1];
+  gcov_unsigned_t *buffer = gcov_read_words (&allocated_buffer, 1);
 
   if (!buffer)
     return 0;
+
   value = from_file (buffer[0]);
   return value;
 }
@@ -525,7 +389,8 @@ GCOV_LINKAGE gcov_type
 gcov_read_counter (void)
 {
   gcov_type value;
-  const gcov_unsigned_t *buffer = gcov_read_words (2);
+  gcov_unsigned_t allocated_buffer[2];
+  gcov_unsigned_t *buffer = gcov_read_words (&allocated_buffer, 2);
 
   if (!buffer)
     return 0;
@@ -602,7 +467,8 @@ gcov_read_string (void)
   if (!length)
     return 0;
 
-  return (const char *) gcov_read_words (length);
+  void *buffer = XNEWVEC (char *, length);
+  return (const char *) gcov_read_bytes (buffer, length);
 }
 #endif
 
@@ -624,27 +490,7 @@ gcov_sync (gcov_position_t base, gcov_unsigned_t length)
 {
   gcov_nonruntime_assert (gcov_var.mode > 0);
   base += length;
-  if (base - gcov_var.start <= gcov_var.length)
-    gcov_var.offset = base - gcov_var.start;
-  else
-    {
-      gcov_var.offset = gcov_var.length = 0;
-      fseek (gcov_var.file, base << 2, SEEK_SET);
-      gcov_var.start = ftell (gcov_var.file) >> 2;
-    }
-}
-#endif
-
-#if IN_LIBGCOV
-/* Move to a given position in a gcov file.  */
-
-GCOV_LINKAGE void
-gcov_seek (gcov_position_t base)
-{
-  if (gcov_var.offset)
-    gcov_write_block (gcov_var.offset);
-  fseek (gcov_var.file, base << 2, SEEK_SET);
-  gcov_var.start = ftell (gcov_var.file) >> 2;
+  fseek (gcov_var.file, base, SEEK_SET);
 }
 #endif
 
diff --git a/gcc/gcov-io.h b/gcc/gcov-io.h
index 4e95c7c82ee..528c950b184 100644
--- a/gcc/gcov-io.h
+++ b/gcc/gcov-io.h
@@ -241,22 +241,25 @@ typedef uint64_t gcov_type_unsigned;
 /* The record tags.  Values [1..3f] are for tags which may be in either
    file.  Values [41..9f] for those in the note file and [a1..ff] for
    the data file.  The tag value zero is used as an explicit end of
-   file marker -- it is not required to be present.  */
+   file marker -- it is not required to be present.
+   All length values are in bytes.  */
+
+#define GCOV_WORD_SIZE		4
 
 #define GCOV_TAG_FUNCTION	 ((gcov_unsigned_t)0x01000000)
-#define GCOV_TAG_FUNCTION_LENGTH (3)
+#define GCOV_TAG_FUNCTION_LENGTH (3 * GCOV_WORD_SIZE)
 #define GCOV_TAG_BLOCKS		 ((gcov_unsigned_t)0x01410000)
 #define GCOV_TAG_BLOCKS_LENGTH(NUM) (NUM)
 #define GCOV_TAG_ARCS		 ((gcov_unsigned_t)0x01430000)
-#define GCOV_TAG_ARCS_LENGTH(NUM)  (1 + (NUM) * 2)
-#define GCOV_TAG_ARCS_NUM(LENGTH)  (((LENGTH) - 1) / 2)
+#define GCOV_TAG_ARCS_LENGTH(NUM)  (1 + (NUM) * 2 * GCOV_WORD_SIZE)
+#define GCOV_TAG_ARCS_NUM(LENGTH)  (((LENGTH / GCOV_WORD_SIZE) - 1) / 2)
 #define GCOV_TAG_LINES		 ((gcov_unsigned_t)0x01450000)
 #define GCOV_TAG_COUNTER_BASE 	 ((gcov_unsigned_t)0x01a10000)
-#define GCOV_TAG_COUNTER_LENGTH(NUM) ((NUM) * 2)
-#define GCOV_TAG_COUNTER_NUM(LENGTH) ((LENGTH) / 2)
+#define GCOV_TAG_COUNTER_LENGTH(NUM) ((NUM) * 2 * GCOV_WORD_SIZE)
+#define GCOV_TAG_COUNTER_NUM(LENGTH) ((LENGTH / GCOV_WORD_SIZE) / 2)
 #define GCOV_TAG_OBJECT_SUMMARY  ((gcov_unsigned_t)0xa1000000)
 #define GCOV_TAG_PROGRAM_SUMMARY ((gcov_unsigned_t)0xa3000000) /* Obsolete */
-#define GCOV_TAG_SUMMARY_LENGTH (2)
+#define GCOV_TAG_SUMMARY_LENGTH (2 * GCOV_WORD_SIZE)
 #define GCOV_TAG_AFDO_FILE_NAMES ((gcov_unsigned_t)0xaa000000)
 #define GCOV_TAG_AFDO_FUNCTION ((gcov_unsigned_t)0xac000000)
 #define GCOV_TAG_AFDO_WORKING_SET ((gcov_unsigned_t)0xaf000000)
diff --git a/libgcc/libgcov-driver.c b/libgcc/libgcov-driver.c
index e53e4dc392a..5413d2dfeca 100644
--- a/libgcc/libgcov-driver.c
+++ b/libgcc/libgcov-driver.c
@@ -514,7 +514,7 @@ read_fatal:;
     fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
 
   if ((error = gcov_close ()))
-    gcov_error (error  < 0 ?
+    gcov_error (error < 0 ?
 		GCOV_PROF_PREFIX "Overflow writing\n" :
 		GCOV_PROF_PREFIX "Error writing\n",
                 gf->filename);
-- 
2.29.2


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

* Re: [PATCH] gcov: Add __gcov_info_to_gdca()
  2020-11-20  8:37 ` Martin Liška
@ 2020-11-20  9:25   ` Sebastian Huber
  2020-11-20  9:49     ` Martin Liška
  0 siblings, 1 reply; 16+ messages in thread
From: Sebastian Huber @ 2020-11-20  9:25 UTC (permalink / raw)
  To: Martin Liška, gcc-patches

On 20/11/2020 09:37, Martin Liška wrote:

> On 11/17/20 10:57 AM, Sebastian Huber wrote:
>> 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:
>
> Hello.
>
> I'm not pretty sure how this set up is going to work. Can you please 
> explain me that?
>
> I was thinking about your needs and I can imagine various techniques 
> how to generate
> gcda files format:
>
> 1) embedded system can override fopen, fwrite, fseek to a functions 
> that do a remote
> write-related functions
Yes, this is one option, however, the inhibit_libc disables quite a lot 
of libgcov functionality if Newlib is used for example.
>
> 2) - use -fprofile-info-section
>    - run an app on an embedded system and do a memory dump to a 
> terminal/console
>    - take the memory dump to a host system (with IO), run 
> __gcov_init_from_memory_dump (...)
>      and then do a normal __gcov_dump

I am not sure if a plain memory dump really simplifies things. You have 
to get the filename separately since it is only referenced in gcov_info 
and not included in the structure:

struct gcov_info
{
[...]
   const char *filename;        /* output file name */
[...]
#ifndef IN_GCOV_TOOL
   const struct gcov_fn_info *const *functions; /* pointer to pointers
                                                   to function 
information  */
[...]
#endif /* !IN_GCOV_TOOL */
};

Also the gcov_fn_info is not embedded in the gcov_info structure. If you 
do a plain memory dump, then you dump also pointers and how do you deal 
with these pointers on the host? You would need some extra information 
to describe the memory dump. So, why not use the gcda format for this? 
It is also more compact since zero value counters are skipped. Serial 
lines are slow, so less data to transfer is good.

/* 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 *gi_ptr,
                  void (*filename) (const char *name, void *arg),
                  void (*dump) (const void *begin, unsigned size, void 
*arg),

                  void *arg);

If __gcov_info_to_gcda() is correctly implemented, then this should give 
you directly gcda files if you use something like this:

#include <gcov.h>
#include <stdio.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)
{
   FILE **file = arg;
   *file = fopen(f, "rb");
}

static void
dump (const void *d, unsigned n, void *arg)
{
   FILE **file = arg;
   fwrite(d, n, 1, *file);
}

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)
   {
     FILE *file = NULL;
     __gcov_info_to_gcda (*info, filename, dump, &file);
     fclose(file);
     ++info;
   }
}

int
main()
{
   dump_gcov_info();
   return 0;
}

The callback functions give the user the full control how the data of 
the gcda file is encoded for the transfer to a host. No gcov internals 
are exposed.

-- 
embedded brains GmbH
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
PGP: Public key available on request.

embedded brains GmbH
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/


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

* Re: [PATCH] gcov: Add __gcov_info_to_gdca()
  2020-11-20  9:25   ` Sebastian Huber
@ 2020-11-20  9:49     ` Martin Liška
  2020-11-20 10:11       ` Sebastian Huber
  0 siblings, 1 reply; 16+ messages in thread
From: Martin Liška @ 2020-11-20  9:49 UTC (permalink / raw)
  To: Sebastian Huber, gcc-patches

On 11/20/20 10:25 AM, Sebastian Huber wrote:
> On 20/11/2020 09:37, Martin Liška wrote:
> 
>> On 11/17/20 10:57 AM, Sebastian Huber wrote:
>>> 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:
>>
>> Hello.
>>
>> I'm not pretty sure how this set up is going to work. Can you please explain me that?
>>
>> I was thinking about your needs and I can imagine various techniques how to generate
>> gcda files format:
>>
>> 1) embedded system can override fopen, fwrite, fseek to a functions that do a remote
>> write-related functions
> Yes, this is one option, however, the inhibit_libc disables quite a lot of libgcov functionality if Newlib is used for example.

I see. Btw do you have available Newlib in the embedded environment? If so, what I/O functionality is provided?

>>
>> 2) - use -fprofile-info-section
>>    - run an app on an embedded system and do a memory dump to a terminal/console
>>    - take the memory dump to a host system (with IO), run __gcov_init_from_memory_dump (...)
>>      and then do a normal __gcov_dump
> 
> I am not sure if a plain memory dump really simplifies things. You have to get the filename separately since it is only referenced in gcov_info and not included in the structure:
> 
> struct gcov_info
> {
> [...]
>    const char *filename;        /* output file name */
> [...]
> #ifndef IN_GCOV_TOOL
>    const struct gcov_fn_info *const *functions; /* pointer to pointers
>                                                    to function information  */
> [...]
> #endif /* !IN_GCOV_TOOL */
> };

I see!

> 
> Also the gcov_fn_info is not embedded in the gcov_info structure. If you do a plain memory dump, then you dump also pointers and how do you deal with these pointers on the host? You would need some extra information to describe the memory dump. So, why not use the gcda format for this? It is also more compact since zero value counters are skipped. Serial lines are slow, so less data to transfer is good.
> 
> /* 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 *gi_ptr,
>                   void (*filename) (const char *name, void *arg),
>                   void (*dump) (const void *begin, unsigned size, void *arg),
> 
>                   void *arg);
> 
> If __gcov_info_to_gcda() is correctly implemented, then this should give you directly gcda files if you use something like this:
> 
> #include <gcov.h>
> #include <stdio.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)
> {
>    FILE **file = arg;
>    *file = fopen(f, "rb");
> }
> 
> static void
> dump (const void *d, unsigned n, void *arg)
> {
>    FILE **file = arg;
>    fwrite(d, n, 1, *file);
> }
> 
> 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)
>    {
>      FILE *file = NULL;
>      __gcov_info_to_gcda (*info, filename, dump, &file);
>      fclose(file);
>      ++info;
>    }
> }
> 
> int
> main()
> {
>    dump_gcov_info();
>    return 0;
> }
> 
> The callback functions give the user the full control how the data of the gcda file is encoded for the transfer to a host. No gcov internals are exposed.
> 

All right. Btw. how will you implement these 2 callbacks on the embedded target?
Apart from these 2 hooks, I bet you will also need gcov_position and gcov_seek functions,
can be seen in my sent patch.

Martin

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

* Re: [PATCH] gcov: Add __gcov_info_to_gdca()
  2020-11-20  9:49     ` Martin Liška
@ 2020-11-20 10:11       ` Sebastian Huber
  2020-11-20 15:25         ` Martin Liška
  0 siblings, 1 reply; 16+ messages in thread
From: Sebastian Huber @ 2020-11-20 10:11 UTC (permalink / raw)
  To: Martin Liška, gcc-patches

On 20/11/2020 10:49, Martin Liška wrote:

> On 11/20/20 10:25 AM, Sebastian Huber wrote:
>> On 20/11/2020 09:37, Martin Liška wrote:
>>
>>> On 11/17/20 10:57 AM, Sebastian Huber wrote:
>>>> 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:
>>>
>>> Hello.
>>>
>>> I'm not pretty sure how this set up is going to work. Can you please 
>>> explain me that?
>>>
>>> I was thinking about your needs and I can imagine various techniques 
>>> how to generate
>>> gcda files format:
>>>
>>> 1) embedded system can override fopen, fwrite, fseek to a functions 
>>> that do a remote
>>> write-related functions
>> Yes, this is one option, however, the inhibit_libc disables quite a 
>> lot of libgcov functionality if Newlib is used for example.
>
> I see. Btw do you have available Newlib in the embedded environment? 
> If so, what I/O functionality is provided?
Yes, I use Newlib with the RTEMS real-time operating system. Newlib 
provides the standard C library I/O functions (fopen, etc.). However, 
having Newlib available doesn't mean that every application uses its. 
Applications are statically linked with the operating system and Newlib. 
They only use what is required. Some applications cannot use the 
standard C library I/O since they use a lot of infrastructure and 
memory. You can do a lot of things with just a couple of KiBs available.
>
>>>
>>> 2) - use -fprofile-info-section
>>>    - run an app on an embedded system and do a memory dump to a 
>>> terminal/console
>>>    - take the memory dump to a host system (with IO), run 
>>> __gcov_init_from_memory_dump (...)
>>>      and then do a normal __gcov_dump
>>
>> I am not sure if a plain memory dump really simplifies things. You 
>> have to get the filename separately since it is only referenced in 
>> gcov_info and not included in the structure:
>>
>> struct gcov_info
>> {
>> [...]
>>    const char *filename;        /* output file name */
>> [...]
>> #ifndef IN_GCOV_TOOL
>>    const struct gcov_fn_info *const *functions; /* pointer to pointers
>>                                                    to function 
>> information  */
>> [...]
>> #endif /* !IN_GCOV_TOOL */
>> };
>
> I see!
>
>>
>> Also the gcov_fn_info is not embedded in the gcov_info structure. If 
>> you do a plain memory dump, then you dump also pointers and how do 
>> you deal with these pointers on the host? You would need some extra 
>> information to describe the memory dump. So, why not use the gcda 
>> format for this? It is also more compact since zero value counters 
>> are skipped. Serial lines are slow, so less data to transfer is good.
>>
>> /* 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 *gi_ptr,
>>                   void (*filename) (const char *name, void *arg),
>>                   void (*dump) (const void *begin, unsigned size, 
>> void *arg),
>>
>>                   void *arg);
>>
>> If __gcov_info_to_gcda() is correctly implemented, then this should 
>> give you directly gcda files if you use something like this:
>>
>> #include <gcov.h>
>> #include <stdio.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)
>> {
>>    FILE **file = arg;
>>    *file = fopen(f, "rb");
>> }
>>
>> static void
>> dump (const void *d, unsigned n, void *arg)
>> {
>>    FILE **file = arg;
>>    fwrite(d, n, 1, *file);
>> }
>>
>> 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)
>>    {
>>      FILE *file = NULL;
>>      __gcov_info_to_gcda (*info, filename, dump, &file);
>>      fclose(file);
>>      ++info;
>>    }
>> }
>>
>> int
>> main()
>> {
>>    dump_gcov_info();
>>    return 0;
>> }
>>
>> The callback functions give the user the full control how the data of 
>> the gcda file is encoded for the transfer to a host. No gcov 
>> internals are exposed.
>>
>
> All right. Btw. how will you implement these 2 callbacks on the 
> embedded target?

One options is to convert the gcov info to YAML:

gcov-info:

- file: filename1

   data: <... base64 encoded data from __gcov_info_to_gcda ... >

- file: filename2

   data: ...

Then send the data to the host via a serial line. On the host read the 
data, parse the YAML, and create the gcda files. The 
__gcov_info_to_gcda() needs about 408 bytes of ARM Thumb-2 code and no 
data. You need a polled character output function, the linker set 
iteration and two callbacks. So, you can easily dump the gcov 
information with about 1KiB of code and no data except a small stack.

> Apart from these 2 hooks, I bet you will also need gcov_position and 
> gcov_seek functions,
> can be seen in my sent patch.
For what do I need them?

-- 
embedded brains GmbH
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
PGP: Public key available on request.

embedded brains GmbH
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/


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

* Re: [PATCH] gcov: Add __gcov_info_to_gdca()
  2020-11-20 10:11       ` Sebastian Huber
@ 2020-11-20 15:25         ` Martin Liška
  2020-11-20 16:14           ` Sebastian Huber
  0 siblings, 1 reply; 16+ messages in thread
From: Martin Liška @ 2020-11-20 15:25 UTC (permalink / raw)
  To: Sebastian Huber, gcc-patches

On 11/20/20 11:11 AM, Sebastian Huber wrote:
> On 20/11/2020 10:49, Martin Liška wrote:
> 
>> On 11/20/20 10:25 AM, Sebastian Huber wrote:
>>> On 20/11/2020 09:37, Martin Liška wrote:
>>>
>>>> On 11/17/20 10:57 AM, Sebastian Huber wrote:
>>>>> 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:
>>>>
>>>> Hello.
>>>>
>>>> I'm not pretty sure how this set up is going to work. Can you please explain me that?
>>>>
>>>> I was thinking about your needs and I can imagine various techniques how to generate
>>>> gcda files format:
>>>>
>>>> 1) embedded system can override fopen, fwrite, fseek to a functions that do a remote
>>>> write-related functions
>>> Yes, this is one option, however, the inhibit_libc disables quite a lot of libgcov functionality if Newlib is used for example.
>>
>> I see. Btw do you have available Newlib in the embedded environment? If so, what I/O functionality is provided?
> Yes, I use Newlib with the RTEMS real-time operating system. Newlib provides the standard C library I/O functions (fopen, etc.). However, having Newlib available doesn't mean that every application uses its. Applications are statically linked with the operating system and Newlib. They only use what is required. Some applications cannot use the standard C library I/O since they use a lot of infrastructure and memory. You can do a lot of things with just a couple of KiBs available.

I see.

>>
>>>>
>>>> 2) - use -fprofile-info-section
>>>>    - run an app on an embedded system and do a memory dump to a terminal/console
>>>>    - take the memory dump to a host system (with IO), run __gcov_init_from_memory_dump (...)
>>>>      and then do a normal __gcov_dump
>>>
>>> I am not sure if a plain memory dump really simplifies things. You have to get the filename separately since it is only referenced in gcov_info and not included in the structure:
>>>
>>> struct gcov_info
>>> {
>>> [...]
>>>    const char *filename;        /* output file name */
>>> [...]
>>> #ifndef IN_GCOV_TOOL
>>>    const struct gcov_fn_info *const *functions; /* pointer to pointers
>>>                                                    to function information  */
>>> [...]
>>> #endif /* !IN_GCOV_TOOL */
>>> };
>>
>> I see!
>>
>>>
>>> Also the gcov_fn_info is not embedded in the gcov_info structure. If you do a plain memory dump, then you dump also pointers and how do you deal with these pointers on the host? You would need some extra information to describe the memory dump. So, why not use the gcda format for this? It is also more compact since zero value counters are skipped. Serial lines are slow, so less data to transfer is good.
>>>
>>> /* 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 *gi_ptr,
>>>                   void (*filename) (const char *name, void *arg),
>>>                   void (*dump) (const void *begin, unsigned size, void *arg),
>>>
>>>                   void *arg);
>>>
>>> If __gcov_info_to_gcda() is correctly implemented, then this should give you directly gcda files if you use something like this:
>>>
>>> #include <gcov.h>
>>> #include <stdio.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)
>>> {
>>>    FILE **file = arg;
>>>    *file = fopen(f, "rb");
>>> }
>>>
>>> static void
>>> dump (const void *d, unsigned n, void *arg)
>>> {
>>>    FILE **file = arg;
>>>    fwrite(d, n, 1, *file);
>>> }
>>>
>>> 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)
>>>    {
>>>      FILE *file = NULL;
>>>      __gcov_info_to_gcda (*info, filename, dump, &file);
>>>      fclose(file);
>>>      ++info;
>>>    }
>>> }
>>>
>>> int
>>> main()
>>> {
>>>    dump_gcov_info();
>>>    return 0;
>>> }
>>>
>>> The callback functions give the user the full control how the data of the gcda file is encoded for the transfer to a host. No gcov internals are exposed.
>>>
>>
>> All right. Btw. how will you implement these 2 callbacks on the embedded target?
> 
> One options is to convert the gcov info to YAML:
> 
> gcov-info:
> 
> - file: filename1
> 
>    data: <... base64 encoded data from __gcov_info_to_gcda ... >
> 
> - file: filename2
> 
>    data: ...
> 
> Then send the data to the host via a serial line. On the host read the data, parse the YAML, and create the gcda files. The __gcov_info_to_gcda() needs about 408 bytes of ARM Thumb-2 code and no data. You need a polled character output function, the linker set iteration and two callbacks. So, you can easily dump the gcov information with about 1KiB of code and no data except a small stack.
> 
>> Apart from these 2 hooks, I bet you will also need gcov_position and gcov_seek functions,
>> can be seen in my sent patch.
> For what do I need them?
> 

I prefer the way with the 2 extra hooks.
Can you please prepare a patch where the newly added functions __gcov_info_to_gcda and __gcov_fn_info_to_gcda
will be used in libgcov (with the hooks equal to fopen and fwrite?

Thanks,
Martin


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

* Re: [PATCH] gcov: Add __gcov_info_to_gdca()
  2020-11-20 15:25         ` Martin Liška
@ 2020-11-20 16:14           ` Sebastian Huber
  2020-11-23 12:25             ` Sebastian Huber
  0 siblings, 1 reply; 16+ messages in thread
From: Sebastian Huber @ 2020-11-20 16:14 UTC (permalink / raw)
  To: Martin Liška, gcc-patches

On 20/11/2020 16:25, Martin Liška wrote:

>>> Apart from these 2 hooks, I bet you will also need gcov_position and 
>>> gcov_seek functions,
>>> can be seen in my sent patch.
>> For what do I need them?
>>
>
> I prefer the way with the 2 extra hooks.
> Can you please prepare a patch where the newly added functions 
> __gcov_info_to_gcda and __gcov_fn_info_to_gcda
> will be used in libgcov (with the hooks equal to fopen and fwrite? 

I am not really sure what I should do. Do you mean that write_one_data() 
should be rewritten to use __gcov_info_to_gcda() with hooks that use 
gcov_write_unsigned()?

The write_one_data() also has a const struct gcov_summary *prg_p 
pointer. What should an external user provide for this pointer? For 
example &gi_ptr->summary?

The write_one_data() has this code

       if (fn_buffer && fn_buffer->fn_ix == f_ix)
         {
           /* Buffered data from another program.  */
           buffered = 1;
           gfi_ptr = &fn_buffer->info;
           length = GCOV_TAG_FUNCTION_LENGTH;
         }

which uses a global variable

/* buffer for the fn_data from another program.  */
static struct gcov_fn_buffer *fn_buffer;

For this handling we would need a new hook to do this:

       if (buffered)
         fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);

I don't know for what we need seek and position hooks.

-- 
embedded brains GmbH
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
PGP: Public key available on request.

embedded brains GmbH
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/


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

* Re: [PATCH] gcov: Add __gcov_info_to_gdca()
  2020-11-20 16:14           ` Sebastian Huber
@ 2020-11-23 12:25             ` Sebastian Huber
  2020-11-23 14:24               ` Martin Liška
  0 siblings, 1 reply; 16+ messages in thread
From: Sebastian Huber @ 2020-11-23 12:25 UTC (permalink / raw)
  To: Martin Liška, gcc-patches

On 20/11/2020 17:14, Sebastian Huber wrote:

> On 20/11/2020 16:25, Martin Liška wrote:
>
>>>> Apart from these 2 hooks, I bet you will also need gcov_position 
>>>> and gcov_seek functions,
>>>> can be seen in my sent patch.
>>> For what do I need them?
>>>
>>
>> I prefer the way with the 2 extra hooks.
>> Can you please prepare a patch where the newly added functions 
>> __gcov_info_to_gcda and __gcov_fn_info_to_gcda
>> will be used in libgcov (with the hooks equal to fopen and fwrite? 
>
> I am not really sure what I should do. Do you mean that 
> write_one_data() should be rewritten to use __gcov_info_to_gcda() with 
> hooks that use gcov_write_unsigned()?
>
> The write_one_data() also has a const struct gcov_summary *prg_p 
> pointer. What should an external user provide for this pointer? For 
> example &gi_ptr->summary?
>
> The write_one_data() has this code
>
>       if (fn_buffer && fn_buffer->fn_ix == f_ix)
>         {
>           /* Buffered data from another program.  */
>           buffered = 1;
>           gfi_ptr = &fn_buffer->info;
>           length = GCOV_TAG_FUNCTION_LENGTH;
>         }
>
> which uses a global variable
>
> /* buffer for the fn_data from another program.  */
> static struct gcov_fn_buffer *fn_buffer;
>
> For this handling we would need a new hook to do this:
>
>       if (buffered)
>         fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
>
> I don't know for what we need seek and position hooks.

Refactoring write_one_data() to use hooks requires that

gcov_write_counter()

gcov_write_tag_length()

gcov_write_summary()

move from gcc/gcov-io.c to libgcc/libgcov-buffer.c. They can be made 
static. I am not sure if the external symbols can be removed

/* In libgcov we need these functions to be extern, so prefix them with
    __gcov.  In libgcov they must also be hidden so that the instance in
    the executable is not also used in a DSO.  */
#define gcov_write_tag_length __gcov_write_tag_length
#define gcov_write_counter __gcov_write_counter
#define gcov_write_summary __gcov_write_summary

without breaking anything? What is the performance impact if only 
gcov_write_unsigned() is used by libgcc/libgcov-driver.c?

-- 
embedded brains GmbH
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
PGP: Public key available on request.

embedded brains GmbH
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/


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

* Re: [PATCH] gcov: Add __gcov_info_to_gdca()
  2020-11-23 12:25             ` Sebastian Huber
@ 2020-11-23 14:24               ` Martin Liška
  0 siblings, 0 replies; 16+ messages in thread
From: Martin Liška @ 2020-11-23 14:24 UTC (permalink / raw)
  To: Sebastian Huber, gcc-patches

On 11/23/20 1:25 PM, Sebastian Huber wrote:
> On 20/11/2020 17:14, Sebastian Huber wrote:
> 
>> On 20/11/2020 16:25, Martin Liška wrote:
>>
>>>>> Apart from these 2 hooks, I bet you will also need gcov_position and gcov_seek functions,
>>>>> can be seen in my sent patch.
>>>> For what do I need them?
>>>>
>>>
>>> I prefer the way with the 2 extra hooks.
>>> Can you please prepare a patch where the newly added functions __gcov_info_to_gcda and __gcov_fn_info_to_gcda
>>> will be used in libgcov (with the hooks equal to fopen and fwrite? 
>>
>> I am not really sure what I should do. Do you mean that write_one_data() should be rewritten to use __gcov_info_to_gcda() with hooks that use gcov_write_unsigned()?
>>
>> The write_one_data() also has a const struct gcov_summary *prg_p pointer. What should an external user provide for this pointer? For example &gi_ptr->summary?
>>
>> The write_one_data() has this code
>>
>>       if (fn_buffer && fn_buffer->fn_ix == f_ix)
>>         {
>>           /* Buffered data from another program.  */
>>           buffered = 1;
>>           gfi_ptr = &fn_buffer->info;
>>           length = GCOV_TAG_FUNCTION_LENGTH;
>>         }
>>
>> which uses a global variable
>>
>> /* buffer for the fn_data from another program.  */
>> static struct gcov_fn_buffer *fn_buffer;
>>
>> For this handling we would need a new hook to do this:
>>
>>       if (buffered)
>>         fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
>>
>> I don't know for what we need seek and position hooks.
> 
> Refactoring write_one_data() to use hooks requires that
> 
> gcov_write_counter()
> 
> gcov_write_tag_length()
> 
> gcov_write_summary()
> 
> move from gcc/gcov-io.c to libgcc/libgcov-buffer.c. They can be made static. I am not sure if the external symbols can be removed
> 
> /* In libgcov we need these functions to be extern, so prefix them with
>     __gcov.  In libgcov they must also be hidden so that the instance in
>     the executable is not also used in a DSO.  */
> #define gcov_write_tag_length __gcov_write_tag_length
> #define gcov_write_counter __gcov_write_counter
> #define gcov_write_summary __gcov_write_summary
> 
> without breaking anything? What is the performance impact if only gcov_write_unsigned() is used by libgcc/libgcov-driver.c?
> 

All right. It seems that your original patch would be a simpler approach,
I'll comment the patch in a moment.

Thanks,
Martin

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

* Re: [PATCH] gcov: Add __gcov_info_to_gdca()
  2020-11-17  9:57 [PATCH] gcov: Add __gcov_info_to_gdca() Sebastian Huber
  2020-11-20  8:37 ` Martin Liška
@ 2020-11-23 14:30 ` Martin Liška
  2020-11-23 14:35   ` Sebastian Huber
  1 sibling, 1 reply; 16+ messages in thread
From: Martin Liška @ 2020-11-23 14:30 UTC (permalink / raw)
  To: Sebastian Huber, gcc-patches

On 11/17/20 10:57 AM, Sebastian Huber wrote:
> 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 <gcov.h>
>    #include <stdio.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;
>      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 <gcov.h>
> +#include <stdio.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
> +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)

Hello.

I would prefer a better names for the hooks. What about something like
open_filename_hook and write_data_hook?

> +{
> +  (*filename) (gi_ptr->filename, arg);
> +  gcov_unsigned_t word = GCOV_DATA_MAGIC;
> +  (*dump) (&word, sizeof (word), arg);

And I would add a new macro like
#define GCOV_WRITE_DATA(data) (*write_data_hook) (&DATA, sizeof (DATA), arg

What do you think?

Note that we already entered a code freeze before the patch was sent to the mailing list.
That means we can install it in the next stage1.

Martin

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


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

* Re: [PATCH] gcov: Add __gcov_info_to_gdca()
  2020-11-23 14:30 ` Martin Liška
@ 2020-11-23 14:35   ` Sebastian Huber
  2020-11-23 14:49     ` Martin Liška
  0 siblings, 1 reply; 16+ messages in thread
From: Sebastian Huber @ 2020-11-23 14:35 UTC (permalink / raw)
  To: Martin Liška, gcc-patches

Hello Martin,

On 23/11/2020 15:30, Martin Liška wrote:
>> +/* 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)
>
> Hello.
>
> I would prefer a better names for the hooks. What about something like
> open_filename_hook and write_data_hook?
>
>> +{
>> +  (*filename) (gi_ptr->filename, arg);
>> +  gcov_unsigned_t word = GCOV_DATA_MAGIC;
>> +  (*dump) (&word, sizeof (word), arg);
>
> And I would add a new macro like
> #define GCOV_WRITE_DATA(data) (*write_data_hook) (&DATA, sizeof 
> (DATA), arg
>
> What do you think?
sounds good.
>
> Note that we already entered a code freeze before the patch was sent 
> to the mailing list.
> That means we can install it in the next stage1. 
If I have to wait for next stage 1, I can also try to refactor 
write_one_data() after your patch which removes the buffering. This 
would avoid some duplicated code, however, it would require some changes 
in existing code. Is it allowed to remove external (hidden?) symbols 
from libgcov?

-- 
embedded brains GmbH
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
PGP: Public key available on request.

embedded brains GmbH
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/


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

* Re: [PATCH] gcov: Add __gcov_info_to_gdca()
  2020-11-23 14:35   ` Sebastian Huber
@ 2020-11-23 14:49     ` Martin Liška
  2020-11-23 14:50       ` Sebastian Huber
  0 siblings, 1 reply; 16+ messages in thread
From: Martin Liška @ 2020-11-23 14:49 UTC (permalink / raw)
  To: Sebastian Huber, gcc-patches

On 11/23/20 3:35 PM, Sebastian Huber wrote:
> If I have to wait for next stage 1, I can also try to refactor write_one_data() after your patch which removes the buffering.

Yes, please build your patches on top of the file buffering removal.

> This would avoid some duplicated code, however, it would require some changes in existing code. Is it allowed to remove external (hidden?) symbols from libgcov?

Which functions do you mean?

Martin

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

* Re: [PATCH] gcov: Add __gcov_info_to_gdca()
  2020-11-23 14:49     ` Martin Liška
@ 2020-11-23 14:50       ` Sebastian Huber
  2020-11-23 14:55         ` Martin Liška
  0 siblings, 1 reply; 16+ messages in thread
From: Sebastian Huber @ 2020-11-23 14:50 UTC (permalink / raw)
  To: Martin Liška, gcc-patches

On 23/11/2020 15:49, Martin Liška wrote:

> On 11/23/20 3:35 PM, Sebastian Huber wrote:
>> If I have to wait for next stage 1, I can also try to refactor 
>> write_one_data() after your patch which removes the buffering.
>
> Yes, please build your patches on top of the file buffering removal.
Ok.
>
>> This would avoid some duplicated code, however, it would require some 
>> changes in existing code. Is it allowed to remove external (hidden?) 
>> symbols from libgcov?
>
> Which functions do you mean?
Refactoring write_one_data() to use hooks requires that

gcov_write_counter()

gcov_write_tag_length()

gcov_write_summary()

move from gcc/gcov-io.c to libgcc/libgcov-buffer.c. They can be made 
static. I am not sure if the external symbols can be removed

/* In libgcov we need these functions to be extern, so prefix them with
     __gcov.  In libgcov they must also be hidden so that the instance in
     the executable is not also used in a DSO.  */
#define gcov_write_tag_length __gcov_write_tag_length
#define gcov_write_counter __gcov_write_counter
#define gcov_write_summary __gcov_write_summary

-- 
embedded brains GmbH
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
PGP: Public key available on request.

embedded brains GmbH
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/


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

* Re: [PATCH] gcov: Add __gcov_info_to_gdca()
  2020-11-23 14:50       ` Sebastian Huber
@ 2020-11-23 14:55         ` Martin Liška
  0 siblings, 0 replies; 16+ messages in thread
From: Martin Liška @ 2020-11-23 14:55 UTC (permalink / raw)
  To: Sebastian Huber, gcc-patches

On 11/23/20 3:50 PM, Sebastian Huber wrote:
> On 23/11/2020 15:49, Martin Liška wrote:
> 
>> On 11/23/20 3:35 PM, Sebastian Huber wrote:
>>> If I have to wait for next stage 1, I can also try to refactor write_one_data() after your patch which removes the buffering.
>>
>> Yes, please build your patches on top of the file buffering removal.
> Ok.
>>
>>> This would avoid some duplicated code, however, it would require some changes in existing code. Is it allowed to remove external (hidden?) symbols from libgcov?
>>
>> Which functions do you mean?
> Refactoring write_one_data() to use hooks requires that
> 
> gcov_write_counter()
> 
> gcov_write_tag_length()
> 
> gcov_write_summary()

I bet these 3 can be actually moved to gcov-io.h, these functions are very small.
So yes, it should be doable.

Martin

> 
> move from gcc/gcov-io.c to libgcc/libgcov-buffer.c. They can be made static. I am not sure if the external symbols can be removed
> 
> /* In libgcov we need these functions to be extern, so prefix them with
>      __gcov.  In libgcov they must also be hidden so that the instance in
>      the executable is not also used in a DSO.  */
> #define gcov_write_tag_length __gcov_write_tag_length
> #define gcov_write_counter __gcov_write_counter
> #define gcov_write_summary __gcov_write_summary
> 


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

* Re: [PATCH] gcov: Add __gcov_info_to_gdca()
  2021-07-13 13:03 Sebastian Huber
@ 2021-07-13 13:11 ` Sebastian Huber
  0 siblings, 0 replies; 16+ messages in thread
From: Sebastian Huber @ 2021-07-13 13:11 UTC (permalink / raw)
  To: gcc-patches

On 13/07/2021 15:03, Sebastian Huber wrote:
>     memset (list_sizes, 0, counters * sizeof (unsigned));

Sorry, I just realized that memset() cannot be used if inhibit_libc is 
defined. I will send a v2 of the 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/

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

* [PATCH] gcov: Add __gcov_info_to_gdca()
@ 2021-07-13 13:03 Sebastian Huber
  2021-07-13 13:11 ` Sebastian Huber
  0 siblings, 1 reply; 16+ messages in thread
From: Sebastian Huber @ 2021-07-13 13:03 UTC (permalink / raw)
  To: gcc-patches

Add __gcov_info_to_gcda() to libgcov 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 (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;
  }

gcc/

	* gcc/gcov-io.h (gcov_write): Declare.
	* gcc/gcov-io.c (gcov_write): New.
	* 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 (are_all_counters_zero): New.
	(dump_handler): Likewise.
	(allocate_handler): Likewise.
	(dump_unsigned): Likewise.
	(dump_counter): Likewise.
	(write_topn_counters): Add dump, allocate, and arg parameters.  Use
	dump_unsigned() and dump_counter().
	(write_one_data): Add dump, allocate, 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           |  10 +++
 gcc/gcov-io.h           |   1 +
 libgcc/Makefile.in      |   2 +-
 libgcc/gcov.h           |  17 +++++
 libgcc/libgcov-driver.c | 155 +++++++++++++++++++++++++++++++---------
 6 files changed, 218 insertions(+), 47 deletions(-)

diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index e67d47af676d..2c514acf2003 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -14782,17 +14782,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      :
@@ -14803,6 +14803,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..8155cd129f6e 100644
--- a/gcc/gcov-io.c
+++ b/gcc/gcov-io.c
@@ -227,6 +227,16 @@ gcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected)
 #endif
 
 #if !IN_GCOV
+/* Write DATA of LENGTH characters to coverage file.  */
+
+GCOV_LINKAGE void
+gcov_write (const void *data, unsigned length)
+{
+  gcov_unsigned_t r = fwrite (data, length, 1, gcov_var.file);
+  if (r != 1)
+    gcov_var.error = 1;
+}
+
 /* Write unsigned VALUE to coverage file.  */
 
 GCOV_LINKAGE void
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/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..eea97fb8e9c9 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,19 @@ 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 callback parameters) of the gcda data stream.  The third callback
+   shall allocate memory with a size in characters specified by the first
+   callback parameter.  The fifth 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 *(*) (unsigned, void *),
+				 void *);
+
 #endif /* GCC_GCOV_H */
diff --git a/libgcc/libgcov-driver.c b/libgcc/libgcov-driver.c
index df7ccb235677..4109f15e7ddb 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
+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.  */
 
@@ -51,8 +63,10 @@ void __gcov_init (struct gcov_info *p __attribute__ ((unused))) {}
 #include <sys/mman.h>
 #endif
 
-#ifdef L_gcov
+#endif /* inhibit_libc */
 
+#ifdef L_gcov
+#if !defined(inhibit_libc)
 /* A utility function for outputting errors.  */
 static int gcov_error (const char *, ...);
 
@@ -343,6 +357,44 @@ read_error:
   return -1;
 }
 
+static void
+dump_handler (const void *data, unsigned length, void *unused)
+{
+  (void)unused;
+  gcov_write (data, length);
+}
+
+static void *
+allocate_handler (unsigned size, void *unused)
+{
+  (void)unused;
+  return xmalloc (size);
+}
+#endif /* inhibit_libc */
+#endif /* L_gcov */
+
+#if defined(L_gcov) || defined(L_gcov_info_to_gcda)
+static inline void
+dump_unsigned (gcov_unsigned_t word,
+	       void (*dump) (const void *, unsigned, void *),
+	       void *arg)
+{
+  (*dump) (&word, sizeof (word), arg);
+}
+
+static inline void
+dump_counter (gcov_type counter,
+	      void (*dump) (const void *, unsigned, void *),
+	      void *arg)
+{
+  dump_unsigned ((gcov_unsigned_t)counter, dump, arg);
+
+  if (sizeof (counter) > sizeof (gcov_unsigned_t))
+    dump_unsigned ((gcov_unsigned_t)(counter >> 32), dump, arg);
+  else
+    dump_unsigned (0, dump, arg);
+}
+
 #define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
 
 /* Store all TOP N counters where each has a dynamic length.  */
@@ -350,7 +402,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) (const void *, unsigned, void *),
+		     void *(*allocate)(unsigned, void *),
+		     void *arg)
 {
   unsigned counters = n_counts / GCOV_TOPN_MEM_COUNTERS;
   gcc_assert (n_counts % GCOV_TOPN_MEM_COUNTERS == 0);
@@ -365,14 +420,15 @@ 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) (list_size_length * sizeof (unsigned), arg);
     }
 
   memset (list_sizes, 0, counters * sizeof (unsigned));
@@ -390,21 +446,21 @@ write_topn_counters (const struct gcov_ctr_info *ci_ptr,
     }
 
   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, arg),
+  dump_unsigned (GCOV_TAG_COUNTER_LENGTH (disk_size), dump, 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, arg);
+      dump_counter (list_sizes[i], dump, 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, arg);
+	  dump_counter (node->count, dump, arg);
 	}
     }
 }
@@ -415,25 +471,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) (const void *, unsigned, void *),
+		void *(*allocate) (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, arg);
+  dump_unsigned (GCOV_VERSION, dump, arg);
+  dump_unsigned (gi_ptr->stamp, dump, arg);
 
+#if defined(L_gcov) && !defined(inhibit_libc)
   /* 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++)
     {
+#if defined(L_gcov) && !defined(inhibit_libc)
       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;
 
+#if defined(L_gcov) && !defined(inhibit_libc)
       if (fn_buffer && fn_buffer->fn_ix == f_ix)
         {
           /* Buffered data from another program.  */
@@ -442,6 +509,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 +518,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, arg);
+      dump_unsigned (length, dump, 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, arg);
+      dump_unsigned (gfi_ptr->lineno_checksum, dump, arg);
+      dump_unsigned (gfi_ptr->cfg_checksum, dump, arg);
 
       ci_ptr = gfi_ptr->ctrs;
       for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
@@ -469,39 +538,37 @@ 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, allocate, 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, 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, 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,
+				 arg);
 		  for (unsigned i = 0; i < n_counts; i++)
-		    gcov_write_counter (ci_ptr->values[i]);
+		    dump_counter (ci_ptr->values[i], dump, arg);
 		}
 	    }
 
 	  ci_ptr++;
 	}
+#if defined(L_gcov) && !defined(inhibit_libc)
       if (buffered)
         fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
+#endif
     }
 
-  gcov_write_unsigned (0);
+  dump_unsigned (0, dump, arg);
 }
+#endif /* L_gcov || L_gcov_info_to_gcda */
 
+#if defined(L_gcov)
+#if !defined(inhibit_libc)
 /* 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 +617,7 @@ 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, dump_handler, allocate_handler, NULL);
   /* fall through */
 
 read_fatal:;
@@ -682,3 +749,21 @@ __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
+/* 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) (const char *, void *),
+		     void (*dump) (const void *, unsigned, void *),
+		     void *(*allocate) (unsigned, void *),
+		     void *arg)
+{
+  (*filename) (gi_ptr->filename, arg);
+  write_one_data (gi_ptr, NULL, dump, allocate, arg);
+}
+#endif /* L_gcov_info_to_gcda */
+#endif /* !IN_GCOV_TOOL */
-- 
2.26.2


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

end of thread, other threads:[~2021-07-13 13:11 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-17  9:57 [PATCH] gcov: Add __gcov_info_to_gdca() Sebastian Huber
2020-11-20  8:37 ` Martin Liška
2020-11-20  9:25   ` Sebastian Huber
2020-11-20  9:49     ` Martin Liška
2020-11-20 10:11       ` Sebastian Huber
2020-11-20 15:25         ` Martin Liška
2020-11-20 16:14           ` Sebastian Huber
2020-11-23 12:25             ` Sebastian Huber
2020-11-23 14:24               ` Martin Liška
2020-11-23 14:30 ` Martin Liška
2020-11-23 14:35   ` Sebastian Huber
2020-11-23 14:49     ` Martin Liška
2020-11-23 14:50       ` Sebastian Huber
2020-11-23 14:55         ` Martin Liška
2021-07-13 13:03 Sebastian Huber
2021-07-13 13:11 ` Sebastian Huber

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