#include <sys/stat.h>
#endif
+#if HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
#ifdef L_gcov
/* A utility function for outputting errors. */
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);
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. */
*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. */
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;