Add helgrind intercepts to have helgrind understanding Ada tasks terination rules
A recent gnatpro version is needed for this to work.

Thanks to these intercepts, some false positive errors are avoided,
and helgrind properly recuperates some internal memory associated
to the terminated task.



git-svn-id: svn://svn.valgrind.org/valgrind/trunk@14046 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/helgrind/hg_main.c b/helgrind/hg_main.c
index 5e12520..f5d02b2 100644
--- a/helgrind/hg_main.c
+++ b/helgrind/hg_main.c
@@ -1670,34 +1670,11 @@
    }
 }
 
-
+/* generate a dependence from the hbthr_q quitter to the hbthr_s stayer. */
 static
-void evh__HG_PTHREAD_JOIN_POST ( ThreadId stay_tid, Thread* quit_thr )
+void generate_quitter_stayer_dependence (Thr* hbthr_q, Thr* hbthr_s)
 {
-   Thread*  thr_s;
-   Thread*  thr_q;
-   Thr*     hbthr_s;
-   Thr*     hbthr_q;
    SO*      so;
-
-   if (SHOW_EVENTS >= 1)
-      VG_(printf)("evh__post_thread_join(stayer=%d, quitter=%p)\n",
-                  (Int)stay_tid, quit_thr );
-
-   tl_assert(HG_(is_sane_ThreadId)(stay_tid));
-
-   thr_s = map_threads_maybe_lookup( stay_tid );
-   thr_q = quit_thr;
-   tl_assert(thr_s != NULL);
-   tl_assert(thr_q != NULL);
-   tl_assert(thr_s != thr_q);
-
-   hbthr_s = thr_s->hbthr;
-   hbthr_q = thr_q->hbthr;
-   tl_assert(hbthr_s != hbthr_q);
-   tl_assert( libhb_get_Thr_hgthread(hbthr_s) == thr_s );
-   tl_assert( libhb_get_Thr_hgthread(hbthr_q) == thr_q );
-
    /* Allocate a temporary synchronisation object and use it to send
       an imaginary message from the quitter to the stayer, the purpose
       being to generate a dependence from the quitter to the
@@ -1721,6 +1698,36 @@
       notified here multiple times for the same joinee.)  See also
       comments in helgrind/tests/jointwice.c. */
    libhb_joinedwith_done(hbthr_q);
+}
+
+
+static
+void evh__HG_PTHREAD_JOIN_POST ( ThreadId stay_tid, Thread* quit_thr )
+{
+   Thread*  thr_s;
+   Thread*  thr_q;
+   Thr*     hbthr_s;
+   Thr*     hbthr_q;
+
+   if (SHOW_EVENTS >= 1)
+      VG_(printf)("evh__post_thread_join(stayer=%d, quitter=%p)\n",
+                  (Int)stay_tid, quit_thr );
+
+   tl_assert(HG_(is_sane_ThreadId)(stay_tid));
+
+   thr_s = map_threads_maybe_lookup( stay_tid );
+   thr_q = quit_thr;
+   tl_assert(thr_s != NULL);
+   tl_assert(thr_q != NULL);
+   tl_assert(thr_s != thr_q);
+
+   hbthr_s = thr_s->hbthr;
+   hbthr_q = thr_q->hbthr;
+   tl_assert(hbthr_s != hbthr_q);
+   tl_assert( libhb_get_Thr_hgthread(hbthr_s) == thr_s );
+   tl_assert( libhb_get_Thr_hgthread(hbthr_q) == thr_q );
+
+   generate_quitter_stayer_dependence (hbthr_q, hbthr_s);
 
    /* evh__pre_thread_ll_exit issues an error message if the exiting
       thread holds any locks.  No need to check here. */
@@ -4740,6 +4747,26 @@
    }
 }
 
+/* A list of Ada dependent tasks and their masters. Used for implementing
+   the Ada task termination semantic as implemented by the
+   gcc gnat Ada runtime. */
+typedef
+   struct { 
+      void* dependent; // Ada Task Control Block of the Dependent
+      void* master;    // ATCB of the master
+      Word  master_level; // level of dependency between master and dependent
+      Thread* hg_dependent; // helgrind Thread* for dependent task.
+   }
+   GNAT_dmml;
+static XArray* gnat_dmmls;   /* of GNAT_dmml */
+static void gnat_dmmls_INIT (void)
+{
+   if (UNLIKELY(gnat_dmmls == NULL)) {
+      gnat_dmmls = VG_(newXA) (HG_(zalloc), "hg.gnat_md.1",
+                               HG_(free),
+                               sizeof(GNAT_dmml) );
+   }
+}
 static void print_monitor_help ( void )
 {
    VG_(gdb_printf) 
@@ -4936,6 +4963,60 @@
          break;
       }
 
+      /* This thread (tid) is informing us of its master. */
+      case _VG_USERREQ__HG_GNAT_MASTER_HOOK: {
+         GNAT_dmml dmml;
+         dmml.dependent = (void*)args[1];
+         dmml.master = (void*)args[2];
+         dmml.master_level = (Word)args[3];
+         dmml.hg_dependent = map_threads_maybe_lookup( tid );
+         tl_assert(dmml.hg_dependent);
+
+         if (0)
+         VG_(printf)("HG_GNAT_MASTER_HOOK (tid %d): "
+                     "dependent = %p master = %p master_level = %ld"
+                     " dependent Thread* = %p\n",
+                     (Int)tid, dmml.dependent, dmml.master, dmml.master_level,
+                     dmml.hg_dependent);
+         gnat_dmmls_INIT();
+         VG_(addToXA) (gnat_dmmls, &dmml);
+         break;
+      }
+
+      /* This thread (tid) is informing us that it has completed a
+         master. */
+      case _VG_USERREQ__HG_GNAT_MASTER_COMPLETED_HOOK: {
+         Word n;
+         const Thread *stayer = map_threads_maybe_lookup( tid );
+         const void *master = (void*)args[1];
+         const Word master_level = (Word) args[2];
+         tl_assert(stayer);
+
+         if (0)
+         VG_(printf)("HG_GNAT_MASTER_COMPLETED_HOOK (tid %d): "
+                     "self_id = %p master_level = %ld Thread* = %p\n",
+                     (Int)tid, master, master_level, stayer);
+
+         gnat_dmmls_INIT();
+         /* Reverse loop on the array, simulating a pthread_join for
+            the Dependent tasks of the completed master, and removing
+            them from the array. */
+         for (n = VG_(sizeXA) (gnat_dmmls) - 1; n >= 0; n--) {
+            GNAT_dmml *dmml = (GNAT_dmml*) VG_(indexXA)(gnat_dmmls, n);
+            if (dmml->master == master
+                && dmml->master_level == master_level) {
+               if (0)
+               VG_(printf)("quitter %p dependency to stayer %p\n",
+                           dmml->hg_dependent->hbthr,  stayer->hbthr);
+               tl_assert(dmml->hg_dependent->hbthr != stayer->hbthr);
+               generate_quitter_stayer_dependence (dmml->hg_dependent->hbthr,
+                                                   stayer->hbthr);
+               VG_(removeIndexXA) (gnat_dmmls, n);
+            }
+         }
+         break;
+      }
+
       /* EXPOSITION only: by intercepting lock init events we can show
          the user where the lock was initialised, rather than only
          being able to show where it was first locked.  Intercepting