I've committed this patch, last in my current series. It teaches the gcov machinery about shared objects that are also built with coverage information. The basic idea is to chain gcov_root structures together onto a single global chain -- checking the GCOV_VERSION number so that only compatible gcov modules get chained.

When __gcov_rest or __gcov_dump are called, all compatible modules are cleared or dumped. The existing atexit machinery is used to dump the current module only when it is exited/unloaded.

nathan
2014-09-17  Nathan sidwell  <nat...@acm.org>

        * Makefile.in (LIBGCOV_INTERFACE): Add _gcov_dump from ...
        (LIBGCOV_DRIVER): ... here.
        * libgcov-driver.c (gcov_master): New.
        (gcov_exit): Remove from master chain.
        (__gcov_init): Add to master chain if version compatible.  Don't
        clear the version.
        * libgcov_interface (__gcov_flust): Call gcov_dump_int.
        (gcov_reset_int): Clear master chain, if compatible.
        (gcov_dump_int): New internal interface.  Dump master chain, if
        compatible.
        (gcov_dump): Alias for gcov_dump_int.
        * libgcov.h (struct gcov_root): Add next and prev fields.
        (struct gcov_master): New struct.
        (__gcov_master): New.
        (gcov_dump_int): Declare.

Index: Makefile.in
===================================================================
--- Makefile.in (revision 215334)
+++ Makefile.in (working copy)
@@ -858,9 +858,10 @@ LIBGCOV_PROFILER = _gcov_interval_profil
        _gcov_one_value_profiler _gcov_indirect_call_profiler           \
        _gcov_average_profiler _gcov_ior_profiler                       \
        _gcov_indirect_call_profiler_v2 _gcov_time_profiler
-LIBGCOV_INTERFACE = _gcov_flush _gcov_fork _gcov_execl _gcov_execlp    \
+LIBGCOV_INTERFACE = _gcov_dump _gcov_flush _gcov_fork                  \
+       _gcov_execl _gcov_execlp                                        \
        _gcov_execle _gcov_execv _gcov_execvp _gcov_execve _gcov_reset
-LIBGCOV_DRIVER = _gcov _gcov_dump
+LIBGCOV_DRIVER = _gcov
 
 libgcov-merge-objects = $(patsubst %,%$(objext),$(LIBGCOV_MERGE))
 libgcov-profiler-objects = $(patsubst %,%$(objext),$(LIBGCOV_PROFILER))
Index: libgcov-driver.c
===================================================================
--- libgcov-driver.c    (revision 215334)
+++ libgcov-driver.c    (working copy)
@@ -777,13 +777,23 @@ __gcov_dump_one (struct gcov_root *root)
   root->run_counted = 1;
 }
 
-/* Per-program/shared-object gcov state.  */
+/* Per-dynamic-object gcov state.  */
 struct gcov_root __gcov_root;
 
+/* Exactly one of these will be live in the process image.  */
+struct gcov_master __gcov_master = 
+  {GCOV_VERSION, 0};
+
 static void
 gcov_exit (void)
 {
   __gcov_dump_one (&__gcov_root);
+  if (__gcov_root.next)
+    __gcov_root.next->prev = __gcov_root.prev;
+  if (__gcov_root.prev)
+    __gcov_root.prev->next = __gcov_root.next;
+  else
+    __gcov_master.root = __gcov_root.next;
 }
 
 /* Add a new object file onto the bb chain.  Invoked automatically
@@ -797,12 +807,21 @@ __gcov_init (struct gcov_info *info)
   if (gcov_version (info, info->version, 0))
     {
       if (!__gcov_root.list)
-        atexit (gcov_exit);
+       {
+         /* Add to master list and at exit function.  */
+         if (gcov_version (NULL, __gcov_master.version, "<master>"))
+           {
+             __gcov_root.next = __gcov_master.root;
+             if (__gcov_master.root)
+               __gcov_master.root->prev = &__gcov_root;
+             __gcov_master.root = &__gcov_root;
+           }
+         atexit (gcov_exit);
+       }
 
       info->next = __gcov_root.list;
       __gcov_root.list = info;
     }
-  info->version = 0;
 }
 #endif /* !IN_GCOV_TOOL */
 #endif /* L_gcov */
Index: libgcov-interface.c
===================================================================
--- libgcov-interface.c (revision 215334)
+++ libgcov-interface.c (working copy)
@@ -85,7 +85,7 @@ __gcov_flush (void)
   init_mx_once ();
   __gthread_mutex_lock (&__gcov_flush_mx);
 
-  __gcov_dump_one (&__gcov_root);
+  __gcov_dump_int ();
   __gcov_reset_int ();
 
   __gthread_mutex_unlock (&__gcov_flush_mx);
@@ -132,8 +132,16 @@ gcov_clear (const struct gcov_info *list
 void
 __gcov_reset_int (void)
 {
-  gcov_clear (__gcov_root.list);
-  __gcov_root.dumped = 0;
+  struct gcov_root *root;
+
+  /* If we're compatible with the master, iterate over everything,
+     otherise just do us.  */
+  for (root = __gcov_master.version == GCOV_VERSION
+        ? __gcov_master.root : &__gcov_root; root; root = root->next)
+    {
+      gcov_clear (root->list);
+      root->dumped = 0;
+    }
 }
 
 ALIAS_void_fn (__gcov_reset_int, __gcov_reset);
@@ -145,11 +153,19 @@ ALIAS_void_fn (__gcov_reset_int, __gcov_
    so far, in order to collect profile in region of interest.  */
 
 void
-__gcov_dump (void)
+__gcov_dump_int (void)
 {
-  __gcov_dump_one (&__gcov_root);
+  struct gcov_root *root;
+
+  /* If we're compatible with the master, iterate over everything,
+     otherise just do us.  */
+  for (root = __gcov_master.version == GCOV_VERSION
+        ? __gcov_master.root : &__gcov_root; root; root = root->next)
+    __gcov_dump_one (root);
 }
 
+ALIAS_void_fn (__gcov_dump_int, __gcov_dump);
+
 #endif /* L_gcov_dump */
 
 #ifdef L_gcov_fork
@@ -169,8 +185,8 @@ __gcov_fork (void)
 #endif
 
 #ifdef L_gcov_execl
-/* A wrapper for the execl function.  Flushes the accumulated profiling data, 
so
-   that they are not lost.  */
+/* A wrapper for the execl function.  Flushes the accumulated
+   profiling data, so that they are not lost.  */
 
 int
 __gcov_execl (const char *path, char *arg, ...)
Index: libgcov.h
===================================================================
--- libgcov.h   (revision 215334)
+++ libgcov.h   (working copy)
@@ -212,10 +212,21 @@ struct gcov_root
   struct gcov_info *list;
   unsigned dumped : 1; /* counts have been dumped.  */
   unsigned run_counted : 1;  /* run has been accounted for.  */
+  struct gcov_root *next;
+  struct gcov_root *prev;
 };
 
 extern struct gcov_root __gcov_root ATTRIBUTE_HIDDEN;
 
+struct gcov_master
+{
+  gcov_unsigned_t version;
+  struct gcov_root *root;
+};
+  
+/* Exactly one of these will be active in the process.  */
+extern struct gcov_master __gcov_master;
+
 /* Dump a set of gcov objects.  */
 extern void __gcov_dump_one (struct gcov_root *) ATTRIBUTE_HIDDEN;
 
@@ -230,8 +241,9 @@ extern void __gcov_flush (void) ATTRIBUT
 extern void __gcov_reset (void);
 extern void __gcov_reset_int (void) ATTRIBUTE_HIDDEN;
 
-/* Function to enable early write of profile information so far.  */
+/* User function to enable early write of profile information so far.  */
 extern void __gcov_dump (void);
+extern void __gcov_dump_int (void) ATTRIBUTE_HIDDEN;
 
 /* The merge function that just sums the counters.  */
 extern void __gcov_merge_add (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;

Reply via email to