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