profiling: fix streaming of TOPN counters

libgcc/ChangeLog:

	PR gcov-profile/99105
	* libgcov-driver.c (write_top_counters): Rename to ...
	(write_topn_counters): ... this.
	(write_one_data): Pre-allocate buffer for number of items
	in the corresponding linked lists.
	* libgcov.h (malloc_mmap): New function.
	(allocate_gcov_kvp): Use it.

gcc/testsuite/ChangeLog:

	PR gcov-profile/99105
	* gcc.dg/tree-prof/indir-call-prof-malloc.c: Use profile
	correction as the wrapped malloc is called one more time
	from libgcov.
	* gcc.dg/tree-prof/pr97461.c: Likewise.
This commit is contained in:
Martin Liska 2021-02-16 16:28:06 +01:00
parent c981619632
commit 6a8fc0c31a
4 changed files with 63 additions and 13 deletions

View File

@ -1,4 +1,4 @@
/* { dg-options "-O2 -ldl" } */
/* { dg-options "-O2 -ldl -fprofile-correction" } */
#define _GNU_SOURCE
#include <stdio.h>

View File

@ -1,5 +1,5 @@
/* PR gcov-profile/97461 */
/* { dg-options "-O2 -ldl" } */
/* { dg-options "-O2 -ldl -fprofile-correction" } */
#define _GNU_SOURCE

View File

@ -42,6 +42,10 @@ void __gcov_init (struct gcov_info *p __attribute__ ((unused))) {}
#include <sys/stat.h>
#endif
#if HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#ifdef L_gcov
/* A utility function for outputting errors. */
@ -334,30 +338,65 @@ read_error:
return -1;
}
#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
/* Store all TOP N counters where each has a dynamic length. */
static void
write_top_counters (const struct gcov_ctr_info *ci_ptr,
unsigned t_ix,
gcov_unsigned_t n_counts)
write_topn_counters (const struct gcov_ctr_info *ci_ptr,
unsigned t_ix,
gcov_unsigned_t n_counts)
{
unsigned counters = n_counts / GCOV_TOPN_MEM_COUNTERS;
gcc_assert (n_counts % GCOV_TOPN_MEM_COUNTERS == 0);
/* It can happen in a multi-threaded environment that number of counters is
different from the size of the corresponding linked lists. */
#define LIST_SIZE_MIN_LENGTH 4 * 1024
static unsigned *list_sizes = NULL;
static unsigned list_size_length = 0;
if (list_sizes == NULL || counters > list_size_length)
{
list_size_length = MAX (LIST_SIZE_MIN_LENGTH, 2 * counters);
#if 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));
}
memset (list_sizes, 0, counters * sizeof (unsigned));
unsigned pair_total = 0;
for (unsigned i = 0; i < counters; i++)
pair_total += ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 1];
{
gcov_type start = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 2];
for (struct gcov_kvp *node = (struct gcov_kvp *)(intptr_t)start;
node != NULL; node = node->next)
{
++pair_total;
++list_sizes[i];
}
}
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));
for (unsigned i = 0; i < counters; i++)
{
gcov_type pair_count = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 1];
gcov_write_counter (ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i]);
gcov_write_counter (pair_count);
gcov_write_counter (list_sizes[i]);
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;
node != NULL; node = node->next)
j < list_sizes[i]; node = node->next, j++)
{
gcov_write_counter (node->value);
gcov_write_counter (node->count);
@ -425,7 +464,7 @@ 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_top_counters (ci_ptr, t_ix, n_counts);
write_topn_counters (ci_ptr, t_ix, n_counts);
else
{
/* Do not stream when all counters are zero. */

View File

@ -409,6 +409,19 @@ gcov_counter_add (gcov_type *counter, gcov_type value,
*counter += value;
}
#if HAVE_SYS_MMAN_H
/* Allocate LENGTH with mmap function. */
static inline void *
malloc_mmap (size_t length)
{
return mmap (NULL, length, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
}
#endif
/* Allocate gcov_kvp from statically pre-allocated pool,
or use heap otherwise. */
@ -424,9 +437,7 @@ allocate_gcov_kvp (void)
if (__gcov_kvp_dynamic_pool == NULL
|| __gcov_kvp_dynamic_pool_index >= __gcov_kvp_dynamic_pool_size)
{
void *ptr = mmap (NULL, MMAP_CHUNK_SIZE,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
void *ptr = malloc_mmap (MMAP_CHUNK_SIZE);
if (ptr != MAP_FAILED)
{
__gcov_kvp_dynamic_pool = ptr;