[vm, reload] Debugging code for become conflict.

TEST=ci
Bug: https://github.com/dart-lang/sdk/issues/56583
Change-Id: I8afba7689c91ebbd03ec4fcee22782205dc7959f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/383540
Reviewed-by: Alexander Aprelev <aam@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
diff --git a/runtime/vm/heap/become.cc b/runtime/vm/heap/become.cc
index 732375b..f1be403 100644
--- a/runtime/vm/heap/become.cc
+++ b/runtime/vm/heap/become.cc
@@ -228,9 +228,12 @@
   Thread::Current()->isolate_group()->set_become(nullptr);
 }
 
-void Become::Add(const Object& before, const Object& after) {
+void Become::Add(const Object& before,
+                 const Object& after,
+                 const char* whence) {
   pointers_.Add(before.ptr());
   pointers_.Add(after.ptr());
+  whence_.Add(whence);
 }
 
 void Become::VisitObjectPointers(ObjectPointerVisitor* visitor) {
@@ -272,11 +275,77 @@
   FATAL("become: %s", message);
 }
 
+struct PtrIntTrait {
+  typedef ObjectPtr Key;
+  typedef intptr_t Value;
+  typedef struct {
+    ObjectPtr key;
+    intptr_t value;
+  } Pair;
+
+  static Key KeyOf(Pair kv) { return kv.key; }
+  static Value ValueOf(Pair kv) { return kv.value; }
+  static uword Hash(Key key) {
+    return (static_cast<uword>(key) * 92821) ^ (static_cast<uword>(key) >> 8);
+  }
+  static bool IsKeyEqual(Pair kv, Key key) { return kv.key == key; }
+};
+
 void Become::Forward() {
   if (pointers_.length() == 0) {
     return;
   }
 
+  {
+    intptr_t conflicts = 0;
+    MallocDirectChainedHashMap<PtrIntTrait> map(pointers_.length() / 2);
+    for (intptr_t i = 0; i < pointers_.length(); i += 2) {
+      ObjectPtr before = pointers_[i];
+      auto* pair = map.Lookup(before);
+      if (pair == nullptr) {
+        map.Insert({before, i + 1});  // 0 value is sentinel.
+      } else {
+        intptr_t j = pair->value - 1;  // 0 value is sentinel.
+        ObjectPtr before1 = pointers_[j];
+        ObjectPtr after1 = pointers_[j + 1];
+        ObjectPtr before2 = pointers_[i];
+        ObjectPtr after2 = pointers_[i + 1];
+        const char* whence1 = whence_[j / 2];
+        const char* whence2 = whence_[i / 2];
+
+        OS::PrintErr("become conflict:\n");
+        OS::PrintErr("whence1 = %s\n", whence1);
+        OS::PrintErr("whence2 = %s\n", whence2);
+
+        OS::PrintErr("before1 cid = %" Pd "\n", before1.GetClassId());
+        OS::PrintErr(" after1 cid = %" Pd "\n", after1.GetClassId());
+        OS::PrintErr("before2 cid = %" Pd "\n", before2.GetClassId());
+        OS::PrintErr(" after2 cid = %" Pd "\n", after2.GetClassId());
+
+        OS::PrintErr("before1 size = %" Pd "\n", before1.untag()->HeapSize());
+        OS::PrintErr(" after1 size = %" Pd "\n", after1.untag()->HeapSize());
+        OS::PrintErr("before2 size = %" Pd "\n", before2.untag()->HeapSize());
+        OS::PrintErr(" after2 size = %" Pd "\n", after2.untag()->HeapSize());
+
+#ifndef PRODUCT
+        ClassTable* class_table = IsolateGroup::Current()->class_table();
+        OS::PrintErr("before1 cls = %s\n",
+                     class_table->UserVisibleNameFor(before1.GetClassId()));
+        OS::PrintErr(" after1 cls = %s\n",
+                     class_table->UserVisibleNameFor(after1.GetClassId()));
+        OS::PrintErr("before2 cls = %s\n",
+                     class_table->UserVisibleNameFor(before2.GetClassId()));
+        OS::PrintErr(" after2 cls = %s\n",
+                     class_table->UserVisibleNameFor(after2.GetClassId()));
+#endif
+        conflicts++;
+      }
+    }
+    if (conflicts != 0) {
+      FATAL("%" Pd " become conflicts", conflicts);
+    }
+  }
+
   Thread* thread = Thread::Current();
   auto heap = thread->isolate_group()->heap();
 
@@ -325,6 +394,7 @@
   }
 #endif
   pointers_.Clear();
+  whence_.Clear();
 }
 
 void Become::FollowForwardingPointers(Thread* thread) {
diff --git a/runtime/vm/heap/become.h b/runtime/vm/heap/become.h
index 1d4bb3b..940e8da 100644
--- a/runtime/vm/heap/become.h
+++ b/runtime/vm/heap/become.h
@@ -98,7 +98,7 @@
   Become();
   ~Become();
 
-  void Add(const Object& before, const Object& after);
+  void Add(const Object& before, const Object& after, const char* whence);
   void Forward();
   void Exchange() { UNIMPLEMENTED(); }
 
@@ -115,6 +115,7 @@
 
  private:
   MallocGrowableArray<ObjectPtr> pointers_;
+  MallocGrowableArray<const char*> whence_;
   DISALLOW_COPY_AND_ASSIGN(Become);
 };
 
diff --git a/runtime/vm/heap/become_test.cc b/runtime/vm/heap/become_test.cc
index e83fbf1..152cfd5 100644
--- a/runtime/vm/heap/become_test.cc
+++ b/runtime/vm/heap/become_test.cc
@@ -22,7 +22,7 @@
   EXPECT(before_obj.ptr() != after_obj.ptr());
 
   Become become;
-  become.Add(before_obj, after_obj);
+  become.Add(before_obj, after_obj, "test");
   become.Forward();
 
   EXPECT(before_obj.ptr() == after_obj.ptr());
@@ -64,7 +64,7 @@
   EXPECT_EQ(no_peer, heap->GetPeer(after_obj.ptr()));
 
   Become become;
-  become.Add(before_obj, after_obj);
+  become.Add(before_obj, after_obj, "test");
   become.Forward();
 
   EXPECT(before_obj.ptr() == after_obj.ptr());
@@ -86,7 +86,7 @@
   EXPECT_EQ(no_id, heap->GetObjectId(after_obj.ptr()));
 
   Become become;
-  become.Add(before_obj, after_obj);
+  become.Add(before_obj, after_obj, "test");
   become.Forward();
 
   EXPECT(before_obj.ptr() == after_obj.ptr());
@@ -112,7 +112,7 @@
             isolate->forward_table_old()->GetValueExclusive(after_obj.ptr()));
 
   Become become;
-  become.Add(before_obj, after_obj);
+  become.Add(before_obj, after_obj, "test");
   become.Forward();
 
   EXPECT(before_obj.ptr() == after_obj.ptr());
@@ -138,7 +138,7 @@
   EXPECT(before_obj.ptr() != after_obj.ptr());
 
   Become become;
-  become.Add(before_obj, after_obj);
+  become.Add(before_obj, after_obj, "test");
   become.Forward();
 
   EXPECT(before_obj.ptr() == after_obj.ptr());
@@ -167,7 +167,7 @@
   }
 
   Become become;
-  become.Add(old_element, new_element);
+  become.Add(old_element, new_element, "test");
   become.Forward();
 
   EXPECT(old_element.ptr() == new_element.ptr());
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 792bfa6..0f04c1a 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -992,7 +992,7 @@
         }
 
         if (old_value.ptr() != new_value.ptr()) {
-          become->Add(old_value, new_value);
+          become->Add(old_value, new_value, "enum forwarding");
         }
         if (new_value.IsCanonical()) {
           cls.InsertCanonicalConstant(zone, new_value);
@@ -1009,7 +1009,7 @@
         } else {
           new_value = old_value.Canonicalize(thread);
           if (old_value.ptr() != new_value.ptr()) {
-            become->Add(old_value, new_value);
+            become->Add(old_value, new_value, "constant forwarding");
           }
         }
       }
diff --git a/runtime/vm/isolate_reload.cc b/runtime/vm/isolate_reload.cc
index 65cc2ae..504880f 100644
--- a/runtime/vm/isolate_reload.cc
+++ b/runtime/vm/isolate_reload.cc
@@ -357,7 +357,9 @@
     // instances of any class with the old size.
     Become::MakeDummyObject(before);
 
-    become->Add(before, after);
+    become->Add(
+        before, after,
+        is_canonical ? "instance morphing canonical" : "instance morphing");
   }
 }
 
@@ -1279,7 +1281,7 @@
   IG->class_table()->SetAt(old_cls.id(), new_cls.ptr());
   new_cls.CopyCanonicalConstants(old_cls);
   new_cls.CopyDeclarationType(old_cls);
-  AddBecomeMapping(old_cls, new_cls);
+  AddBecomeMapping(old_cls, new_cls, "class forwarding");
   AddClassMapping(new_cls, old_cls);
 }
 
@@ -2555,7 +2557,7 @@
       // Replaced class.
       AddLibraryMapping(replacement_or_new, old);
 
-      AddBecomeMapping(old, replacement_or_new);
+      AddBecomeMapping(old, replacement_or_new, "library forwarding");
     }
   }
 }
@@ -2664,12 +2666,13 @@
                                                  const Field& new_field) {
   ASSERT(old_field.is_static());
   ASSERT(new_field.is_static());
-  AddBecomeMapping(old_field, new_field);
+  AddBecomeMapping(old_field, new_field, "static field");
 }
 
 void ProgramReloadContext::AddBecomeMapping(const Object& old,
-                                            const Object& neu) {
-  become_.Add(old, neu);
+                                            const Object& neu,
+                                            const char* whence) {
+  become_.Add(old, neu, whence);
 }
 
 void ProgramReloadContext::RestoreClassHierarchyInvariants() {
diff --git a/runtime/vm/isolate_reload.h b/runtime/vm/isolate_reload.h
index d4b5e66..263b486 100644
--- a/runtime/vm/isolate_reload.h
+++ b/runtime/vm/isolate_reload.h
@@ -381,7 +381,9 @@
   void AddLibraryMapping(const Library& replacement_or_new,
                          const Library& original);
   void AddStaticFieldMapping(const Field& old_field, const Field& new_field);
-  void AddBecomeMapping(const Object& old, const Object& neu);
+  void AddBecomeMapping(const Object& old,
+                        const Object& neu,
+                        const char* whence);
   void RestoreClassHierarchyInvariants();
 
   Become become_;
diff --git a/runtime/vm/object_reload.cc b/runtime/vm/object_reload.cc
index 8a6545e..24a468b 100644
--- a/runtime/vm/object_reload.cc
+++ b/runtime/vm/object_reload.cc
@@ -338,7 +338,7 @@
         if (old_closure.IsCanonical()) {
           new_closure.SetCanonical();
         }
-        irc->AddBecomeMapping(old_closure, new_closure);
+        irc->AddBecomeMapping(old_closure, new_closure, "static tear-off");
       }
     }
   }
@@ -758,7 +758,7 @@
       // The replacement of the old prefix with the new prefix
       // in Isolate::loaded_prefixes_set_ implicitly carried
       // the loaded state over to the new prefix.
-      context->AddBecomeMapping(original_prefix, prefix);
+      context->AddBecomeMapping(original_prefix, prefix, "library prefix");
     }
   }
 }