From 6a8fc0c31a9ae759fe9bf59b5418abf2af938f91 Mon Sep 17 00:00:00 2001 From: Martin Liska Date: Tue, 16 Feb 2021 16:28:06 +0100 Subject: [PATCH] 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. --- .../gcc.dg/tree-prof/indir-call-prof-malloc.c | 2 +- gcc/testsuite/gcc.dg/tree-prof/pr97461.c | 2 +- libgcc/libgcov-driver.c | 55 ++++++++++++++++--- libgcc/libgcov.h | 17 +++++- 4 files changed, 63 insertions(+), 13 deletions(-) diff --git a/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-malloc.c b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-malloc.c index 454e224c95f..7bda4aedfc8 100644 --- a/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-malloc.c +++ b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-malloc.c @@ -1,4 +1,4 @@ -/* { dg-options "-O2 -ldl" } */ +/* { dg-options "-O2 -ldl -fprofile-correction" } */ #define _GNU_SOURCE #include diff --git a/gcc/testsuite/gcc.dg/tree-prof/pr97461.c b/gcc/testsuite/gcc.dg/tree-prof/pr97461.c index 213fac9af04..f684be4d80f 100644 --- a/gcc/testsuite/gcc.dg/tree-prof/pr97461.c +++ b/gcc/testsuite/gcc.dg/tree-prof/pr97461.c @@ -1,5 +1,5 @@ /* PR gcov-profile/97461 */ -/* { dg-options "-O2 -ldl" } */ +/* { dg-options "-O2 -ldl -fprofile-correction" } */ #define _GNU_SOURCE diff --git a/libgcc/libgcov-driver.c b/libgcc/libgcov-driver.c index 91462350132..a1338b6e525 100644 --- a/libgcc/libgcov-driver.c +++ b/libgcc/libgcov-driver.c @@ -42,6 +42,10 @@ void __gcov_init (struct gcov_info *p __attribute__ ((unused))) {} #include #endif +#if HAVE_SYS_MMAN_H +#include +#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. */ diff --git a/libgcc/libgcov.h b/libgcc/libgcov.h index acdb7cd1500..2780cc098c8 100644 --- a/libgcc/libgcov.h +++ b/libgcc/libgcov.h @@ -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;