libgcov: support overloaded malloc
authorMartin Liska <mliska@suse.cz>
Tue, 2 Jun 2020 11:31:48 +0000 (13:31 +0200)
committerMartin Liska <mliska@suse.cz>
Fri, 31 Jul 2020 08:57:50 +0000 (10:57 +0200)
gcc/ChangeLog:

* gcov-io.h (GCOV_PREALLOCATED_KVP): New.

libgcc/ChangeLog:

* libgcov-driver.c: Add __gcov_kvp_pool
and __gcov_kvp_pool_index variables.
* libgcov.h (allocate_gcov_kvp): New.
(gcov_topn_add_value): Use it.

gcc/testsuite/ChangeLog:

* gcc.dg/tree-prof/indir-call-prof-malloc.c: New test.

gcc/gcov-io.h
gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-malloc.c [new file with mode: 0644]
libgcc/libgcov-driver.c
libgcc/libgcov.h

index 940eb7d85615177f9db5e15073a88c2b7cec257f..4dba01c78ce5e9c348730253ebf362723e4c3523 100644 (file)
@@ -292,6 +292,9 @@ GCOV_COUNTERS
 /* Maximum number of tracked TOP N value profiles.  */
 #define GCOV_TOPN_MAXIMUM_TRACKED_VALUES 32
 
+/* Number of pre-allocated gcov_kvp structures.  */
+#define GCOV_PREALLOCATED_KVP 16
+
 /* Convert a counter index to a tag.  */
 #define GCOV_TAG_FOR_COUNTER(COUNT)                            \
        (GCOV_TAG_COUNTER_BASE + ((gcov_unsigned_t)(COUNT) << 17))
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
new file mode 100644 (file)
index 0000000..454e224
--- /dev/null
@@ -0,0 +1,49 @@
+/* { dg-options "-O2 -ldl" } */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdint.h>
+#include <dlfcn.h>
+
+int global;
+int global2;
+
+void report1 (size_t size)
+{
+  global++;
+}
+
+void report2 (size_t size)
+{
+  global2++;
+}
+
+typedef void (*tp) (size_t);
+static tp fns[] = {report1, report2};
+
+void* malloc(size_t size)
+{
+  static void* (*real_malloc)(size_t) = NULL;
+  if (!real_malloc)
+      real_malloc = dlsym(RTLD_NEXT, "malloc");
+
+  void *p = real_malloc (size);
+  fns[size % 2] (size);
+  // fprintf(stderr, "malloc(%d) = %p\n", size, p);
+  return p;
+}
+
+void *calloc (size_t n, size_t size)
+{
+  void *ptr = malloc (n * size);
+  __builtin_memset (ptr, 0, n * size);
+  return ptr;
+}
+
+void *ptr;
+
+int main()
+{
+  ptr = malloc (16);
+  return 0;
+}
index 2590593a58abd40addbe54bc877c2fb21c1f489f..58914268d4ece0b3a3a7dcb9cb21c4fa197fd770 100644 (file)
@@ -588,6 +588,12 @@ struct gcov_root __gcov_root;
 struct gcov_master __gcov_master = 
   {GCOV_VERSION, 0};
 
+/* Pool of pre-allocated gcov_kvp strutures.  */
+struct gcov_kvp __gcov_kvp_pool[GCOV_PREALLOCATED_KVP];
+
+/* Index to first free gcov_kvp in the pool.  */
+unsigned __gcov_kvp_pool_index;
+
 void
 __gcov_exit (void)
 {
index 81e18950a50077ce71bbd739f595ccfde646f4e8..8be5bebcac08a8f5d761eba75b700f1db8b6ced0 100644 (file)
@@ -250,6 +250,8 @@ struct indirect_call_tuple
   
 /* Exactly one of these will be active in the process.  */
 extern struct gcov_master __gcov_master;
+extern struct gcov_kvp __gcov_kvp_pool[GCOV_PREALLOCATED_KVP];
+extern unsigned __gcov_kvp_pool_index;
 
 /* Dump a set of gcov objects.  */
 extern void __gcov_dump_one (struct gcov_root *) ATTRIBUTE_HIDDEN;
@@ -402,6 +404,47 @@ gcov_counter_add (gcov_type *counter, gcov_type value,
     *counter += value;
 }
 
+/* Allocate gcov_kvp from heap.  If we are recursively called, then allocate
+   it from a list of pre-allocated pool.  */
+
+static inline struct gcov_kvp *
+allocate_gcov_kvp (void)
+{
+  struct gcov_kvp *new_node = NULL;
+
+  static
+#if defined(HAVE_CC_TLS)
+__thread
+#endif
+  volatile unsigned in_recursion ATTRIBUTE_UNUSED = 0;
+
+#if !defined(IN_GCOV_TOOL) && !defined(L_gcov_merge_topn)
+  if (__builtin_expect (in_recursion, 0))
+    {
+      unsigned index;
+#if GCOV_SUPPORTS_ATOMIC
+      index
+       = __atomic_fetch_add (&__gcov_kvp_pool_index, 1, __ATOMIC_RELAXED);
+#else
+      index = __gcov_kvp_pool_index++;
+#endif
+      if (index < GCOV_PREALLOCATED_KVP)
+       new_node = &__gcov_kvp_pool[index];
+      else
+       /* Do not crash in the situation.  */
+       return NULL;
+    }
+  else
+#endif
+    {
+      in_recursion = 1;
+      new_node = (struct gcov_kvp *)xcalloc (1, sizeof (struct gcov_kvp));
+      in_recursion = 0;
+    }
+
+  return new_node;
+}
+
 /* Add key value pair VALUE:COUNT to a top N COUNTERS.  When INCREMENT_TOTAL
    is true, add COUNT to total of the TOP counter.  If USE_ATOMIC is true,
    do it in atomic way.  */
@@ -443,8 +486,10 @@ gcov_topn_add_value (gcov_type *counters, gcov_type value, gcov_type count,
     }
   else
     {
-      struct gcov_kvp *new_node
-       = (struct gcov_kvp *)xcalloc (1, sizeof (struct gcov_kvp));
+      struct gcov_kvp *new_node = allocate_gcov_kvp ();
+      if (new_node == NULL)
+       return;
+
       new_node->value = value;
       new_node->count = count;