Fix sanity check crash in Helgrind.  Partial fix for #255353.
(Philippe Waroquiers, philippe.waroquiers@skynet.be)



git-svn-id: svn://svn.valgrind.org/valgrind/trunk@11575 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/helgrind/hg_main.c b/helgrind/hg_main.c
index f22f39a..21a473d 100644
--- a/helgrind/hg_main.c
+++ b/helgrind/hg_main.c
@@ -132,7 +132,9 @@
 /* Admin linked list of Threads */
 static Thread* admin_threads = NULL;
 
-/* Admin linked list of Locks */
+/* Admin double linked list of Locks */
+/* We need a double linked list to properly and efficiently
+   handle del_LockN. */
 static Lock* admin_locks = NULL;
 
 /* Mapping table for core ThreadIds to Thread* */
@@ -182,10 +184,15 @@
 }
 
 // Make a new lock which is unlocked (hence ownerless)
+// and insert the new lock in admin_locks double linked list.
 static Lock* mk_LockN ( LockKind kind, Addr guestaddr ) {
    static ULong unique = 0;
    Lock* lock             = HG_(zalloc)( "hg.mk_Lock.1", sizeof(Lock) );
-   lock->admin            = admin_locks;
+   lock->admin_next       = admin_locks;
+   lock->admin_prev       = NULL;
+   if (admin_locks)
+      admin_locks->admin_prev = lock;
+   admin_locks            = lock;
    lock->unique           = unique++;
    lock->magic            = LockN_MAGIC;
    lock->appeared_at      = NULL;
@@ -196,12 +203,11 @@
    lock->heldW            = False;
    lock->heldBy           = NULL;
    tl_assert(HG_(is_sane_LockN)(lock));
-   admin_locks            = lock;
    return lock;
 }
 
 /* Release storage for a Lock.  Also release storage in .heldBy, if
-   any. */
+   any. Removes from admin_locks double linked list. */
 static void del_LockN ( Lock* lk ) 
 {
    tl_assert(HG_(is_sane_LockN)(lk));
@@ -209,6 +215,16 @@
    libhb_so_dealloc(lk->hbso);
    if (lk->heldBy)
       VG_(deleteBag)( lk->heldBy );
+   if (admin_locks == lk) {
+      admin_locks = lk->admin_next;
+      if (admin_locks)
+         admin_locks->admin_prev = NULL;
+   }
+   else {
+      lk->admin_prev->admin_next = lk->admin_next;
+      lk->admin_next->admin_prev = lk->admin_prev;
+   }
+      
    VG_(memset)(lk, 0xAA, sizeof(*lk));
    HG_(free)(lk);
 }
@@ -446,8 +462,9 @@
 {
    space(d+0); VG_(printf)("Lock %p (ga %#lx) {\n", lk, lk->guestaddr);
    if (sHOW_ADMIN) {
-      space(d+3); VG_(printf)("admin  %p\n",   lk->admin);
-      space(d+3); VG_(printf)("magic  0x%x\n", (UInt)lk->magic);
+      space(d+3); VG_(printf)("admin_n  %p\n",   lk->admin_next);
+      space(d+3); VG_(printf)("admin_p  %p\n",   lk->admin_prev);
+      space(d+3); VG_(printf)("magic    0x%x\n", (UInt)lk->magic);
    }
    space(d+3); VG_(printf)("unique %llu\n", lk->unique);
    space(d+3); VG_(printf)("kind   %s\n", show_LockKind(lk->kind));
@@ -471,11 +488,11 @@
 {
    Int   i, n;
    Lock* lk;
-   for (n = 0, lk = admin_locks;  lk;  n++, lk = lk->admin) {
+   for (n = 0, lk = admin_locks;  lk;  n++, lk = lk->admin_next) {
       /* nothing */
    }
    space(d); VG_(printf)("admin_locks (%d records) {\n", n);
-   for (i = 0, lk = admin_locks;  lk;  i++, lk = lk->admin) {
+   for (i = 0, lk = admin_locks;  lk;  i++, lk = lk->admin_next) {
       if (0) {
          space(n); 
          VG_(printf)("admin_locks record %d of %d:\n", i, n);
@@ -843,7 +860,7 @@
    Lock*     lk;
    Int       i;
    // # entries in admin_locks == # entries in map_locks
-   for (i = 0, lk = admin_locks;  lk;  i++, lk = lk->admin)
+   for (i = 0, lk = admin_locks;  lk;  i++, lk = lk->admin_next)
       ;
    if (i != VG_(sizeFM)(map_locks)) BAD("1");
    // for each entry (gla, lk) in map_locks
@@ -855,7 +872,7 @@
    }
    VG_(doneIterFM)( map_locks );
    // scan through admin_locks ...
-   for (lk = admin_locks; lk; lk = lk->admin) {
+   for (lk = admin_locks; lk; lk = lk->admin_next) {
       // lock is sane.  Quite comprehensive, also checks that
       // referenced (holder) threads are sane.
       if (!HG_(is_sane_LockN)(lk)) BAD("3");