Hello.
As Honza noticed, when using GCC 11 during train run of Clang leads to
very slow training run. Apparently, it's caused by a corrupted TOP N profile.
The patch fixed that by preallocating a buffer that is latter stream out.
With the patch, I can profiledbootstrap GCC and the instrumented clang finished
in ~6 minutes its training run.
Patch can bootstrap on x86_64-linux-gnu and survives regression tests.
Ready to be installed?
Thanks,
Martin
libgcc/ChangeLog:
PR gcov-profile/99105
* libgcov-driver.c (write_top_counters): Rename to ...
(write_topn_counters): ... this.
Make a temporary allocation of counters before streaming. That
helps in multi-threaded environment.
(write_one_data): Use renamed function.
* libgcov-merge.c (__gcov_merge_topn): Add assert about tracked
number of counters.
---
libgcc/libgcov-driver.c | 40 ++++++++++++++++++++++++++++++----------
libgcc/libgcov-merge.c | 1 +
2 files changed, 31 insertions(+), 10 deletions(-)
diff --git a/libgcc/libgcov-driver.c b/libgcc/libgcov-driver.c
index e474e032b54..74c68ab04e2 100644
--- a/libgcc/libgcov-driver.c
+++ b/libgcc/libgcov-driver.c
@@ -337,7 +337,7 @@ read_error:
/* Store all TOP N counters where each has a dynamic length. */
static void
-write_top_counters (const struct gcov_ctr_info *ci_ptr,
+write_topn_counters (const struct gcov_ctr_info *ci_ptr,
unsigned t_ix,
gcov_unsigned_t n_counts)
{
@@ -347,22 +347,42 @@ write_top_counters (const struct gcov_ctr_info *ci_ptr,
for (unsigned i = 0; i < counters; i++)
pair_total += ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 1];
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));
+
+ /* It can happen in multi-threaded environment that number of counters is
smaller.
+ Because of that we build a buffer which we stream latter in this
function. */
+ gcov_type *buffer
+ = (gcov_type *) xmalloc (disk_size * sizeof (gcov_type));
+ gcov_type *ptr = buffer;
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);
+ (*ptr++) = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i];
+ /* Skip pair count for now. */
+ gcov_type *pair_count_ptr = ptr;
+ ++ptr;
+
+ unsigned pair_count = 0;
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)
+ node != NULL; node = node->next, ++pair_count)
{
- gcov_write_counter (node->value);
- gcov_write_counter (node->count);
+ (*ptr++) = node->value;
+ (*ptr++) = node->count;
}
+
+ *pair_count_ptr = pair_count;
}
+
+ unsigned int length = ptr - buffer;
+ gcc_assert (length <= disk_size);
+ gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
+ GCOV_TAG_COUNTER_LENGTH (length));
+
+ /* FIXME: use a block store */
+ for (unsigned i = 0; i < length; i++)
+ gcov_write_counter (buffer[i]);
+
+ free (buffer);
}
/* Write counters in GI_PTR and the summary in PRG to a gcda file. In
@@ -425,7 +445,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-merge.c b/libgcc/libgcov-merge.c
index 7db188a4f4c..3379b688128 100644
--- a/libgcc/libgcov-merge.c
+++ b/libgcc/libgcov-merge.c
@@ -109,6 +109,7 @@ __gcov_merge_topn (gcov_type *counters, unsigned n_counters)
/* First value is number of total executions of the profiler. */
gcov_type all = gcov_get_counter_ignore_scaling (-1);
gcov_type n = gcov_get_counter_ignore_scaling (-1);
+ gcc_assert (n <= GCOV_TOPN_MAXIMUM_TRACKED_VALUES);
unsigned full = all < 0;
gcov_type *total = &counters[GCOV_TOPN_MEM_COUNTERS * i];
--
2.30.0