Fix a bug in Cachegrind:  when invalidating BBs, the lookup in the BBCC_table
sometimes failed, causing an assertion failure.  This is because the debug
info for the code address, which is used in the lookup,
can change -- eg. "myprint.c:myprint()" is found at instrumentation, but by the
time the invalidation occurs, it's changed to "myprint.c:???".  So it now falls
back to a slow exhaustive search of the table.

This was causing cachegrind/tests/dlclose to fail, and should hopefully fix
bug #72781.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@2218 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/cachegrind/cg_main.c b/cachegrind/cg_main.c
index 54850a9..2f83e76 100644
--- a/cachegrind/cg_main.c
+++ b/cachegrind/cg_main.c
@@ -399,6 +399,63 @@
     return hash_value;
 }
 
+/* This is a backup for get_BBCC() when removing BBs from the table.
+ * Necessary because the debug info can change when code is removed.  For
+ * example, when inserting, the info might be "myprint.c:myprint()", but
+ * upon removal, the info might be "myprint.c:???", which causes the
+ * hash-lookup to fail (but it doesn't always happen).  So we do a horrible,
+ * slow search through all the file nodes and function nodes (but we can do
+ * 3rd stage with the fast hash-lookup). */
+static BBCC* get_BBCC_slow_removal(Addr bb_orig_addr)
+{
+   Int        i, j;
+   UInt       BBCC_hash;
+   file_node *curr_file_node;
+   fn_node   *curr_fn_node;
+   BBCC     **prev_BBCC_next_ptr, *curr_BBCC;
+
+   for (i = 0; i < N_FILE_ENTRIES; i++) {
+
+      for (curr_file_node = BBCC_table[i]; 
+           NULL != curr_file_node;
+           curr_file_node = curr_file_node->next) 
+      {
+         for (j = 0; j < N_FN_ENTRIES; j++) {
+
+            for (curr_fn_node = curr_file_node->fns[j];
+                 NULL != curr_fn_node;
+                 curr_fn_node = curr_fn_node->next) 
+            {
+               BBCC_hash = bb_orig_addr % N_BBCC_ENTRIES;
+               prev_BBCC_next_ptr = &(curr_fn_node->BBCCs[BBCC_hash]);
+               curr_BBCC = curr_fn_node->BBCCs[BBCC_hash];
+
+               while (NULL != curr_BBCC) {
+                  if (bb_orig_addr == curr_BBCC->orig_addr) {
+                     // Found it!
+                     sk_assert(curr_BBCC->array_size > 0 
+                            && curr_BBCC->array_size < 1000000);
+                     if (VG_(clo_verbosity) > 2) {
+                         VG_(message)(Vg_DebugMsg, "did slow BB removal");
+                     }
+
+                     // Remove curr_BBCC from chain;  it will be used and
+                     // free'd by the caller.
+                     *prev_BBCC_next_ptr = curr_BBCC->next;
+                     return curr_BBCC;
+                  }
+
+                  prev_BBCC_next_ptr = &(curr_BBCC->next);
+                  curr_BBCC = curr_BBCC->next;
+               }
+            }
+         }
+      }
+   }
+   VG_(printf)("failing BB address: %p\n", bb_orig_addr);
+   VG_(skin_panic)("slow BB removal failed");
+}
+
 /* Do a three step traversal: by filename, then fn_name, then instr_addr.
  * In all cases prepends new nodes to their chain.  Returns a pointer to the
  * cost centre.  Also sets BB_seen_before by reference. 
@@ -449,18 +506,23 @@
    }
    if (curr_BBCC == NULL) {
 
-      sk_assert(False == remove);
-
-      curr_fn_node->BBCCs[BBCC_hash] = curr_BBCC = 
-         new_BBCC(bb_orig_addr, cb, curr_fn_node->BBCCs[BBCC_hash]);
-      *BB_seen_before = False;
+      if (remove == False) {
+         curr_fn_node->BBCCs[BBCC_hash] = curr_BBCC = 
+            new_BBCC(bb_orig_addr, cb, curr_fn_node->BBCCs[BBCC_hash]);
+         *BB_seen_before = False;
+      } else {
+         // Ok, BB not found when removing:  the debug info must have
+         // changed.  Do a slow removal.
+         curr_BBCC = get_BBCC_slow_removal(bb_orig_addr);
+         *BB_seen_before = True;
+      }
 
    } else {
       sk_assert(bb_orig_addr == curr_BBCC->orig_addr);
       sk_assert(curr_BBCC->array_size > 0 && curr_BBCC->array_size < 1000000);
       if (VG_(clo_verbosity) > 2) {
           VG_(message)(Vg_DebugMsg, 
-            "BB retranslation, retrieving from BBCC table");
+            "BB retranslation/invalidation, retrieving from BBCC table");
       }
       *BB_seen_before = True;