gcov: Add __gcov_info_to_gdca()
Add __gcov_info_to_gcda() to libgcov to get the gcda data for a gcda info in a freestanding environment. It is intended to be used with the -fprofile-info-section option. A crude test program which doesn't use a linker script is (use "gcc -coverage -fprofile-info-section -lgcov 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; } With this patch, <stdint.h> is included in libgcov-driver.c even if inhibit_libc is defined. This header file should be also available for freestanding environments. If this is not the case, then we have to define intptr_t somehow. The patch removes one use of memset() which makes the <string.h> include superfluous. gcc/ * gcov-io.h (gcov_write): Declare. * gcov-io.c (gcov_write): New. (gcov_write_counter): Remove. (gcov_write_tag_length): Likewise. (gcov_write_summary): Replace gcov_write_tag_length() with calls to gcov_write_unsigned(). * doc/invoke.texi (fprofile-info-section): Mention __gcov_info_to_gdca(). gcc/testsuite/ * gcc.dg/gcov-info-to-gcda.c: New test. libgcc/ * Makefile.in (LIBGCOV_DRIVER): Add _gcov_info_to_gcda. * gcov.h (gcov_info): Declare. (__gcov_info_to_gdca): Likewise. * libgcov.h (gcov_write_counter): Remove. (gcov_write_tag_length): Likewise. * libgcov-driver.c (#include <stdint.h>): New. (#include <string.h>): Remove. (NEED_L_GCOV): Conditionally define. (NEED_L_GCOV_INFO_TO_GCDA): Likewise. (are_all_counters_zero): New. (gcov_dump_handler): Likewise. (gcov_allocate_handler): Likewise. (dump_unsigned): Likewise. (dump_counter): Likewise. (write_topn_counters): Add dump_fn, allocate_fn, and arg parameters. Use dump_unsigned() and dump_counter(). (write_one_data): Add dump_fn, allocate_fn, and arg parameters. Use dump_unsigned(), dump_counter(), and are_all_counters_zero(). (__gcov_info_to_gcda): New.
This commit is contained in:
parent
a3d3e8c362
commit
9124bbe185
8 changed files with 304 additions and 88 deletions
|
@ -14801,17 +14801,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 :
|
||||
|
@ -14822,6 +14822,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" (info));
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -229,6 +229,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
|
||||
|
@ -239,21 +249,6 @@ gcov_write_unsigned (gcov_unsigned_t value)
|
|||
gcov_var.error = 1;
|
||||
}
|
||||
|
||||
/* Write counter VALUE to coverage file. Sets error flag
|
||||
appropriately. */
|
||||
|
||||
#if IN_LIBGCOV
|
||||
GCOV_LINKAGE void
|
||||
gcov_write_counter (gcov_type value)
|
||||
{
|
||||
gcov_write_unsigned ((gcov_unsigned_t) value);
|
||||
if (sizeof (value) > sizeof (gcov_unsigned_t))
|
||||
gcov_write_unsigned ((gcov_unsigned_t) (value >> 32));
|
||||
else
|
||||
gcov_write_unsigned (0);
|
||||
}
|
||||
#endif /* IN_LIBGCOV */
|
||||
|
||||
#if !IN_LIBGCOV
|
||||
/* Write STRING to coverage file. Sets error flag on file
|
||||
error, overflow flag on overflow */
|
||||
|
@ -349,22 +344,13 @@ gcov_write_length (gcov_position_t position)
|
|||
|
||||
#else /* IN_LIBGCOV */
|
||||
|
||||
/* Write a tag TAG and length LENGTH. */
|
||||
|
||||
GCOV_LINKAGE void
|
||||
gcov_write_tag_length (gcov_unsigned_t tag, gcov_unsigned_t length)
|
||||
{
|
||||
gcov_write_unsigned (tag);
|
||||
gcov_write_unsigned (length);
|
||||
}
|
||||
|
||||
/* Write a summary structure to the gcov file. Return nonzero on
|
||||
overflow. */
|
||||
/* Write a summary structure to the gcov file. */
|
||||
|
||||
GCOV_LINKAGE void
|
||||
gcov_write_summary (gcov_unsigned_t tag, const struct gcov_summary *summary)
|
||||
{
|
||||
gcov_write_tag_length (tag, GCOV_TAG_SUMMARY_LENGTH);
|
||||
gcov_write_unsigned (tag);
|
||||
gcov_write_unsigned (GCOV_TAG_SUMMARY_LENGTH);
|
||||
gcov_write_unsigned (summary->runs);
|
||||
gcov_write_unsigned (summary->sum_max);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
60
gcc/testsuite/gcc.dg/gcov-info-to-gcda.c
Normal file
60
gcc/testsuite/gcc.dg/gcov-info-to-gcda.c
Normal file
|
@ -0,0 +1,60 @@
|
|||
/* { dg-do run } */
|
||||
/* { dg-skip-if "profile-info-section" { powerpc-ibm-aix* } } */
|
||||
/* { dg-options "-fprofile-arcs -fprofile-info-section" } */
|
||||
|
||||
#define assert(expr) \
|
||||
((expr) \
|
||||
? (void)0 \
|
||||
: (__builtin_printf ("%s:%i: Assertion `%s' failed.\n", \
|
||||
__FILE__, __LINE__, #expr), \
|
||||
__builtin_abort ()))
|
||||
|
||||
struct gcov_info;
|
||||
|
||||
extern void
|
||||
__gcov_info_to_gcda (const struct gcov_info *__info,
|
||||
void (*__filename_fn) (const char *, void *),
|
||||
void (*__dump_fn) (const void *, unsigned, void *),
|
||||
void *(*__allocate_fn) (unsigned, void *),
|
||||
void *__arg);
|
||||
|
||||
extern const struct gcov_info *my_info;
|
||||
|
||||
static unsigned counter;
|
||||
|
||||
static void
|
||||
filename (const char *f, void *arg)
|
||||
{
|
||||
assert (arg == &counter);
|
||||
assert (__builtin_strstr (f, "gcov-info-to-gcda.c") == 0);
|
||||
}
|
||||
|
||||
static void
|
||||
dump (const void *d, unsigned n, void *arg)
|
||||
{
|
||||
unsigned *m = (unsigned *)arg;
|
||||
assert (arg == &counter);
|
||||
|
||||
if (*m == 0)
|
||||
{
|
||||
const unsigned *u = d;
|
||||
assert (*u == 0x67636461);
|
||||
}
|
||||
|
||||
*m += n;
|
||||
}
|
||||
|
||||
static void *
|
||||
allocate (unsigned length, void *arg)
|
||||
{
|
||||
assert (arg == &counter);
|
||||
return __builtin_malloc (length);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
__asm__ volatile (".set my_info, .LPBX2");
|
||||
__gcov_info_to_gcda (my_info, filename, dump, allocate, &counter);
|
||||
assert (counter > 4);
|
||||
return 0;
|
||||
}
|
|
@ -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,21 @@ extern void __gcov_reset (void);
|
|||
|
||||
extern void __gcov_dump (void);
|
||||
|
||||
/* Convert the gcov information referenced by INFO to a gcda data stream.
|
||||
The FILENAME_FN callback is called exactly once with the filename associated
|
||||
with the gcov information. The filename may be NULL. Afterwards, the
|
||||
DUMP_FN callback is subsequently called with chunks (the begin and length of
|
||||
the chunk are passed as the first two callback parameters) of the gcda data
|
||||
stream. The ALLOCATE_FN callback shall allocate memory with a size in
|
||||
characters specified by the first callback parameter. The ARG parameter is
|
||||
a user-provided argument passed as the last argument to the callback
|
||||
functions. */
|
||||
|
||||
extern void
|
||||
__gcov_info_to_gcda (const struct gcov_info *__info,
|
||||
void (*__filename_fn) (const char *, void *),
|
||||
void (*__dump_fn) (const void *, unsigned, void *),
|
||||
void *(*__allocate_fn) (unsigned, void *),
|
||||
void *__arg);
|
||||
|
||||
#endif /* GCC_GCOV_H */
|
||||
|
|
|
@ -26,6 +26,20 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
|||
#include "libgcov.h"
|
||||
#include "gcov-io.h"
|
||||
|
||||
#include <stdint.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. */
|
||||
|
||||
|
@ -35,8 +49,6 @@ void __gcov_init (struct gcov_info *p __attribute__ ((unused))) {}
|
|||
|
||||
#else /* inhibit_libc */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if GCOV_LOCKED
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
@ -51,8 +63,17 @@ void __gcov_init (struct gcov_info *p __attribute__ ((unused))) {}
|
|||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#ifdef L_gcov
|
||||
#endif /* inhibit_libc */
|
||||
|
||||
#if defined(L_gcov) && !defined(inhibit_libc)
|
||||
#define NEED_L_GCOV
|
||||
#endif
|
||||
|
||||
#if defined(L_gcov_info_to_gcda) && !IN_GCOV_TOOL
|
||||
#define NEED_L_GCOV_INFO_TO_GCDA
|
||||
#endif
|
||||
|
||||
#ifdef NEED_L_GCOV
|
||||
/* A utility function for outputting errors. */
|
||||
static int gcov_error (const char *, ...);
|
||||
|
||||
|
@ -343,6 +364,51 @@ read_error:
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* Write the DATA of LENGTH characters to the gcov file. */
|
||||
|
||||
static void
|
||||
gcov_dump_handler (const void *data,
|
||||
unsigned length,
|
||||
void *arg ATTRIBUTE_UNUSED)
|
||||
{
|
||||
gcov_write (data, length);
|
||||
}
|
||||
|
||||
/* Allocate SIZE characters and return the address of the allocated memory. */
|
||||
|
||||
static void *
|
||||
gcov_allocate_handler (unsigned size, void *arg ATTRIBUTE_UNUSED)
|
||||
{
|
||||
return xmalloc (size);
|
||||
}
|
||||
#endif /* NEED_L_GCOV */
|
||||
|
||||
#if defined(NEED_L_GCOV) || defined(NEED_L_GCOV_INFO_TO_GCDA)
|
||||
/* Dump the WORD using the DUMP handler called with ARG. */
|
||||
|
||||
static inline void
|
||||
dump_unsigned (gcov_unsigned_t word,
|
||||
void (*dump_fn) (const void *, unsigned, void *),
|
||||
void *arg)
|
||||
{
|
||||
(*dump_fn) (&word, sizeof (word), arg);
|
||||
}
|
||||
|
||||
/* Dump the COUNTER using the DUMP handler called with ARG. */
|
||||
|
||||
static inline void
|
||||
dump_counter (gcov_type counter,
|
||||
void (*dump_fn) (const void *, unsigned, void *),
|
||||
void *arg)
|
||||
{
|
||||
dump_unsigned ((gcov_unsigned_t)counter, dump_fn, arg);
|
||||
|
||||
if (sizeof (counter) > sizeof (gcov_unsigned_t))
|
||||
dump_unsigned ((gcov_unsigned_t)(counter >> 32), dump_fn, arg);
|
||||
else
|
||||
dump_unsigned (0, dump_fn, arg);
|
||||
}
|
||||
|
||||
#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
|
||||
|
||||
/* Store all TOP N counters where each has a dynamic length. */
|
||||
|
@ -350,7 +416,10 @@ read_error:
|
|||
static void
|
||||
write_topn_counters (const struct gcov_ctr_info *ci_ptr,
|
||||
unsigned t_ix,
|
||||
gcov_unsigned_t n_counts)
|
||||
gcov_unsigned_t n_counts,
|
||||
void (*dump_fn) (const void *, unsigned, void *),
|
||||
void *(*allocate_fn)(unsigned, void *),
|
||||
void *arg)
|
||||
{
|
||||
unsigned counters = n_counts / GCOV_TOPN_MEM_COUNTERS;
|
||||
gcc_assert (n_counts % GCOV_TOPN_MEM_COUNTERS == 0);
|
||||
|
@ -365,46 +434,49 @@ write_topn_counters (const struct gcov_ctr_info *ci_ptr,
|
|||
if (list_sizes == NULL || counters > list_size_length)
|
||||
{
|
||||
list_size_length = MAX (LIST_SIZE_MIN_LENGTH, 2 * counters);
|
||||
#if HAVE_SYS_MMAN_H
|
||||
#if !defined(inhibit_libc) && HAVE_SYS_MMAN_H
|
||||
list_sizes
|
||||
= (unsigned *)malloc_mmap (list_size_length * sizeof (unsigned));
|
||||
#endif
|
||||
|
||||
/* Malloc fallback. */
|
||||
if (list_sizes == NULL)
|
||||
list_sizes = (unsigned *)xmalloc (list_size_length * sizeof (unsigned));
|
||||
list_sizes =
|
||||
(unsigned *)(*allocate_fn) (list_size_length * sizeof (unsigned),
|
||||
arg);
|
||||
}
|
||||
|
||||
memset (list_sizes, 0, counters * sizeof (unsigned));
|
||||
unsigned pair_total = 0;
|
||||
|
||||
for (unsigned i = 0; i < counters; i++)
|
||||
{
|
||||
gcov_type start = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 2];
|
||||
unsigned sizes = 0;
|
||||
|
||||
for (struct gcov_kvp *node = (struct gcov_kvp *)(intptr_t)start;
|
||||
node != NULL; node = node->next)
|
||||
{
|
||||
++pair_total;
|
||||
++list_sizes[i];
|
||||
}
|
||||
++sizes;
|
||||
|
||||
pair_total += sizes;
|
||||
list_sizes[i] = sizes;
|
||||
}
|
||||
|
||||
unsigned disk_size = GCOV_TOPN_DISK_COUNTERS * counters + 2 * pair_total;
|
||||
gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
|
||||
GCOV_TAG_COUNTER_LENGTH (disk_size));
|
||||
dump_unsigned (GCOV_TAG_FOR_COUNTER (t_ix), dump_fn, arg),
|
||||
dump_unsigned (GCOV_TAG_COUNTER_LENGTH (disk_size), dump_fn, arg);
|
||||
|
||||
for (unsigned i = 0; i < counters; i++)
|
||||
{
|
||||
gcov_write_counter (ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i]);
|
||||
gcov_write_counter (list_sizes[i]);
|
||||
dump_counter (ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i], dump_fn, arg);
|
||||
dump_counter (list_sizes[i], dump_fn, arg);
|
||||
gcov_type start = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 2];
|
||||
|
||||
unsigned j = 0;
|
||||
for (struct gcov_kvp *node = (struct gcov_kvp *)(intptr_t)start;
|
||||
j < list_sizes[i]; node = node->next, j++)
|
||||
{
|
||||
gcov_write_counter (node->value);
|
||||
gcov_write_counter (node->count);
|
||||
dump_counter (node->value, dump_fn, arg);
|
||||
dump_counter (node->count, dump_fn, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -415,25 +487,34 @@ 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 ATTRIBUTE_UNUSED,
|
||||
void (*dump_fn) (const void *, unsigned, void *),
|
||||
void *(*allocate_fn) (unsigned, void *),
|
||||
void *arg)
|
||||
{
|
||||
unsigned f_ix;
|
||||
|
||||
gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION);
|
||||
gcov_write_unsigned (gi_ptr->stamp);
|
||||
dump_unsigned (GCOV_DATA_MAGIC, dump_fn, arg);
|
||||
dump_unsigned (GCOV_VERSION, dump_fn, arg);
|
||||
dump_unsigned (gi_ptr->stamp, dump_fn, arg);
|
||||
|
||||
#ifdef NEED_L_GCOV
|
||||
/* Generate whole program statistics. */
|
||||
gcov_write_summary (GCOV_TAG_OBJECT_SUMMARY, prg_p);
|
||||
#endif
|
||||
|
||||
/* Write execution counts for each function. */
|
||||
for (f_ix = 0; f_ix != gi_ptr->n_functions; f_ix++)
|
||||
{
|
||||
#ifdef NEED_L_GCOV
|
||||
unsigned buffered = 0;
|
||||
#endif
|
||||
const struct gcov_fn_info *gfi_ptr;
|
||||
const struct gcov_ctr_info *ci_ptr;
|
||||
gcov_unsigned_t length;
|
||||
unsigned t_ix;
|
||||
|
||||
#ifdef NEED_L_GCOV
|
||||
if (fn_buffer && fn_buffer->fn_ix == f_ix)
|
||||
{
|
||||
/* Buffered data from another program. */
|
||||
|
@ -442,6 +523,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 +532,14 @@ write_one_data (const struct gcov_info *gi_ptr,
|
|||
length = 0;
|
||||
}
|
||||
|
||||
gcov_write_tag_length (GCOV_TAG_FUNCTION, length);
|
||||
dump_unsigned (GCOV_TAG_FUNCTION, dump_fn, arg);
|
||||
dump_unsigned (length, dump_fn, arg);
|
||||
if (!length)
|
||||
continue;
|
||||
|
||||
gcov_write_unsigned (gfi_ptr->ident);
|
||||
gcov_write_unsigned (gfi_ptr->lineno_checksum);
|
||||
gcov_write_unsigned (gfi_ptr->cfg_checksum);
|
||||
dump_unsigned (gfi_ptr->ident, dump_fn, arg);
|
||||
dump_unsigned (gfi_ptr->lineno_checksum, dump_fn, arg);
|
||||
dump_unsigned (gfi_ptr->cfg_checksum, dump_fn, arg);
|
||||
|
||||
ci_ptr = gfi_ptr->ctrs;
|
||||
for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
|
||||
|
@ -469,39 +552,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_fn, allocate_fn,
|
||||
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_fn, arg);
|
||||
if (are_all_counters_zero (ci_ptr))
|
||||
/* Do not stream when all counters are zero. */
|
||||
dump_unsigned (GCOV_TAG_COUNTER_LENGTH (-n_counts),
|
||||
dump_fn, arg);
|
||||
else
|
||||
{
|
||||
gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
|
||||
GCOV_TAG_COUNTER_LENGTH (n_counts));
|
||||
dump_unsigned (GCOV_TAG_COUNTER_LENGTH (n_counts),
|
||||
dump_fn, arg);
|
||||
for (unsigned i = 0; i < n_counts; i++)
|
||||
gcov_write_counter (ci_ptr->values[i]);
|
||||
dump_counter (ci_ptr->values[i], dump_fn, arg);
|
||||
}
|
||||
}
|
||||
|
||||
ci_ptr++;
|
||||
}
|
||||
#ifdef NEED_L_GCOV
|
||||
if (buffered)
|
||||
fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
|
||||
#endif
|
||||
}
|
||||
|
||||
gcov_write_unsigned (0);
|
||||
dump_unsigned (0, dump_fn, arg);
|
||||
}
|
||||
#endif /* NEED_L_GCOV || NEED_L_GCOV_INFO_TO_GCDA */
|
||||
|
||||
#ifdef NEED_L_GCOV
|
||||
/* Dump the coverage counts for one gcov_info object. We merge with existing
|
||||
counts when possible, to avoid growing the .da files ad infinitum. We use
|
||||
this program's checksum to make sure we only accumulate whole program
|
||||
|
@ -550,7 +631,8 @@ dump_one_gcov (struct gcov_info *gi_ptr, struct gcov_filename *gf,
|
|||
summary = gi_ptr->summary;
|
||||
#endif
|
||||
|
||||
write_one_data (gi_ptr, &summary);
|
||||
write_one_data (gi_ptr, &summary, gcov_dump_handler, gcov_allocate_handler,
|
||||
NULL);
|
||||
/* fall through */
|
||||
|
||||
read_fatal:;
|
||||
|
@ -680,5 +762,20 @@ __gcov_init (struct gcov_info *info)
|
|||
}
|
||||
}
|
||||
#endif /* !IN_GCOV_TOOL */
|
||||
#endif /* L_gcov */
|
||||
#endif /* inhibit_libc */
|
||||
#endif /* NEED_L_GCOV */
|
||||
|
||||
#ifdef NEED_L_GCOV_INFO_TO_GCDA
|
||||
/* Convert the gcov info to a gcda data stream. It is intended for
|
||||
free-standing environments which do not support the C library file I/O. */
|
||||
|
||||
void
|
||||
__gcov_info_to_gcda (const struct gcov_info *gi_ptr,
|
||||
void (*filename_fn) (const char *, void *),
|
||||
void (*dump_fn) (const void *, unsigned, void *),
|
||||
void *(*allocate_fn) (unsigned, void *),
|
||||
void *arg)
|
||||
{
|
||||
(*filename_fn) (gi_ptr->filename, arg);
|
||||
write_one_data (gi_ptr, NULL, dump_fn, allocate_fn, arg);
|
||||
}
|
||||
#endif /* NEED_L_GCOV_INFO_TO_GCDA */
|
||||
|
|
|
@ -114,13 +114,11 @@ typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI)));
|
|||
#define gcov_var __gcov_var
|
||||
#define gcov_open __gcov_open
|
||||
#define gcov_close __gcov_close
|
||||
#define gcov_write_tag_length __gcov_write_tag_length
|
||||
#define gcov_position __gcov_position
|
||||
#define gcov_seek __gcov_seek
|
||||
#define gcov_rewrite __gcov_rewrite
|
||||
#define gcov_is_error __gcov_is_error
|
||||
#define gcov_write_unsigned __gcov_write_unsigned
|
||||
#define gcov_write_counter __gcov_write_counter
|
||||
#define gcov_write_summary __gcov_write_summary
|
||||
#define gcov_read_unsigned __gcov_read_unsigned
|
||||
#define gcov_read_counter __gcov_read_counter
|
||||
|
@ -345,9 +343,6 @@ extern int __gcov_execve (const char *, char *const [], char *const [])
|
|||
|
||||
/* Functions that only available in libgcov. */
|
||||
GCOV_LINKAGE int gcov_open (const char */*name*/) ATTRIBUTE_HIDDEN;
|
||||
GCOV_LINKAGE void gcov_write_counter (gcov_type) ATTRIBUTE_HIDDEN;
|
||||
GCOV_LINKAGE void gcov_write_tag_length (gcov_unsigned_t, gcov_unsigned_t)
|
||||
ATTRIBUTE_HIDDEN;
|
||||
GCOV_LINKAGE void gcov_write_summary (gcov_unsigned_t /*tag*/,
|
||||
const struct gcov_summary *)
|
||||
ATTRIBUTE_HIDDEN;
|
||||
|
|
Loading…
Add table
Reference in a new issue