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;