On 2/17/21 9:36 AM, Martin Liška wrote:
I'll write it even more robust...

This is more elegant approach I've just tested on the instrumented clang.

Patch can bootstrap on x86_64-linux-gnu and survives regression tests.

Ready to be installed?
Thanks,
Martin
>From a8a879ba8a8ce3e90a712ff4c436b5d993faad7d Mon Sep 17 00:00:00 2001
From: Martin Liska <mli...@suse.cz>
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-merge.c (__gcov_merge_topn): Use renamed function.

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/indir-call-prof-malloc.c |  2 +-
 libgcc/libgcov-driver.c                       | 37 +++++++++++++++----
 libgcc/libgcov-merge.c                        |  1 +
 3 files changed, 31 insertions(+), 9 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 <stdio.h>
diff --git a/libgcc/libgcov-driver.c b/libgcc/libgcov-driver.c
index e474e032b54..e6efa97254e 100644
--- a/libgcc/libgcov-driver.c
+++ b/libgcc/libgcov-driver.c
@@ -337,32 +337,53 @@ 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,
-		    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.  */
+  unsigned *list_sizes = (unsigned *) xcalloc (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)
+	   node != NULL; node = node->next, j++)
 	{
 	  gcov_write_counter (node->value);
 	  gcov_write_counter (node->count);
+
+	  /* Stop when we reach expected number of items.  */
+	  if (j + 1 == list_sizes[i])
+	    break;
 	}
     }
+
+  free (list_sizes);
 }
 
 /* Write counters in GI_PTR and the summary in PRG to a gcda file. In
@@ -425,7 +446,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

Reply via email to