Visit class roots from ClassLoader::VisitReferences

This causes the classes of a class loader to get marked when that
class loader gets marked instead of during class root visiting.

Bug: 22720414

Change-Id: If53f042aff1d9f7bf94ecbe6886601edda029b7d
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 0886e32..f19263d 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -37,6 +37,7 @@
 #include "base/unix_file/fd_file.h"
 #include "base/value_object.h"
 #include "class_linker-inl.h"
+#include "class_table-inl.h"
 #include "compiler_callbacks.h"
 #include "debugger.h"
 #include "dex_file-inl.h"
@@ -582,6 +583,7 @@
 
   // Setup the ClassLoader, verifying the object_size_.
   class_root = FindSystemClass(self, "Ljava/lang/ClassLoader;");
+  class_root->SetClassLoaderClass();
   CHECK_EQ(class_root->GetObjectSize(), mirror::ClassLoader::InstanceSize());
   SetClassRoot(kJavaLangClassLoader, class_root);
 
@@ -1273,15 +1275,10 @@
     // Moving concurrent:
     // Need to make sure to not copy ArtMethods without doing read barriers since the roots are
     // marked concurrently and we don't hold the classlinker_classes_lock_ when we do the copy.
-    boot_class_table_.VisitRoots(visitor, flags);
+    boot_class_table_.VisitRoots(buffered_visitor);
     for (GcRoot<mirror::ClassLoader>& root : class_loaders_) {
       // May be null for boot ClassLoader.
       root.VisitRoot(visitor, RootInfo(kRootVMInternal));
-      ClassTable* const class_table = root.Read()->GetClassTable();
-      if (class_table != nullptr) {
-        // May be null if we have no classes.
-        class_table->VisitRoots(visitor, flags);
-      }
     }
   } else if ((flags & kVisitRootFlagNewRoots) != 0) {
     for (auto& root : new_class_roots_) {
@@ -2810,6 +2807,10 @@
   }
   VerifyObject(klass);
   class_table->InsertWithHash(klass, hash);
+  if (class_loader != nullptr) {
+    // This is necessary because we need to have the card dirtied for remembered sets.
+    Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader);
+  }
   if (log_new_class_table_roots_) {
     new_class_roots_.push_back(GcRoot<mirror::Class>(klass));
   }
@@ -4375,6 +4376,11 @@
     klass->SetFinalizable();
   }
 
+  // Inherit class loader flag form super class.
+  if (super->IsClassLoaderClass()) {
+    klass->SetClassLoaderClass();
+  }
+
   // Inherit reference flags (if any) from the superclass.
   int reference_flags = (super->GetAccessFlags() & kAccReferenceFlagsMask);
   if (reference_flags != 0) {
diff --git a/runtime/class_table-inl.h b/runtime/class_table-inl.h
new file mode 100644
index 0000000..dc60a2c
--- /dev/null
+++ b/runtime/class_table-inl.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_CLASS_TABLE_INL_H_
+#define ART_RUNTIME_CLASS_TABLE_INL_H_
+
+#include "class_table.h"
+
+namespace art {
+
+template<class Visitor>
+void ClassTable::VisitRoots(Visitor& visitor) {
+  for (ClassSet& class_set : classes_) {
+    for (GcRoot<mirror::Class>& root : class_set) {
+      visitor.VisitRoot(root.AddressWithoutBarrier());
+    }
+  }
+}
+
+template<class Visitor>
+void ClassTable::VisitRoots(const Visitor& visitor) {
+  for (ClassSet& class_set : classes_) {
+    for (GcRoot<mirror::Class>& root : class_set) {
+      visitor.VisitRoot(root.AddressWithoutBarrier());
+    }
+  }
+}
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_CLASS_TABLE_INL_H_
diff --git a/runtime/class_table.cc b/runtime/class_table.cc
index c245d4e..fc8e6c4 100644
--- a/runtime/class_table.cc
+++ b/runtime/class_table.cc
@@ -61,16 +61,6 @@
   return existing;
 }
 
-void ClassTable::VisitRoots(RootVisitor* visitor, VisitRootFlags flags ATTRIBUTE_UNUSED) {
-  BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(
-      visitor, RootInfo(kRootStickyClass));
-  for (ClassSet& class_set : classes_) {
-    for (GcRoot<mirror::Class>& root : class_set) {
-      buffered_visitor.VisitRoot(root);
-    }
-  }
-}
-
 bool ClassTable::Visit(ClassVisitor* visitor) {
   for (ClassSet& class_set : classes_) {
     for (GcRoot<mirror::Class>& root : class_set) {
diff --git a/runtime/class_table.h b/runtime/class_table.h
index 4182954..6b18d90 100644
--- a/runtime/class_table.h
+++ b/runtime/class_table.h
@@ -67,8 +67,15 @@
   mirror::Class* UpdateClass(const char* descriptor, mirror::Class* new_klass, size_t hash)
       REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
 
-  void VisitRoots(RootVisitor* visitor, VisitRootFlags flags)
-      REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+  // NO_THREAD_SAFETY_ANALYSIS for object marking requiring heap bitmap lock.
+  template<class Visitor>
+  void VisitRoots(Visitor& visitor)
+      SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_)
+      NO_THREAD_SAFETY_ANALYSIS;
+  template<class Visitor>
+  void VisitRoots(const Visitor& visitor)
+      SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_)
+      NO_THREAD_SAFETY_ANALYSIS;
 
   // Return false if the callback told us to exit.
   bool Visit(ClassVisitor* visitor)
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index e2bcca2..5799b66 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -1276,7 +1276,7 @@
   explicit MarkVisitor(MarkSweep* const mark_sweep) ALWAYS_INLINE : mark_sweep_(mark_sweep) {
   }
 
-  void operator()(mirror::Object* obj, MemberOffset offset, bool /* is_static */) const
+  void operator()(mirror::Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const
       ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_)
       REQUIRES(Locks::heap_bitmap_lock_) {
     if (kCheckLocks) {
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index e67ea3f..713797f 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -883,6 +883,7 @@
                      gc::EqAllocRecordTypesPtr<gc::AllocRecordStackTraceElement>> frames_;
   std::unordered_map<const mirror::Object*, const gc::AllocRecordStackTrace*> allocation_records_;
 
+  friend class GcRootVisitor;
   DISALLOW_COPY_AND_ASSIGN(Hprof);
 };
 
@@ -1023,12 +1024,47 @@
   ++objects_in_segment_;
 }
 
+// Use for visiting the GcRoots held live by ArtFields, ArtMethods, and ClassLoaders.
+class GcRootVisitor {
+ public:
+  explicit GcRootVisitor(Hprof* hprof) : hprof_(hprof) {}
+
+  void operator()(mirror::Object* obj ATTRIBUTE_UNUSED,
+                  MemberOffset offset ATTRIBUTE_UNUSED,
+                  bool is_static ATTRIBUTE_UNUSED) const {}
+
+  // Note that these don't have read barriers. Its OK however since the GC is guaranteed to not be
+  // running during the hprof dumping process.
+  void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    if (!root->IsNull()) {
+      VisitRoot(root);
+    }
+  }
+
+  void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    mirror::Object* obj = root->AsMirrorPtr();
+    // The two cases are either classes or dex cache arrays. If it is a dex cache array, then use
+    // VM internal. Otherwise the object is a declaring class of an ArtField or ArtMethod or a
+    // class from a ClassLoader.
+    hprof_->VisitRoot(obj, RootInfo(obj->IsClass() ? kRootStickyClass : kRootVMInternal));
+  }
+
+
+ private:
+  Hprof* const hprof_;
+};
+
 void Hprof::DumpHeapObject(mirror::Object* obj) {
   // Ignore classes that are retired.
   if (obj->IsClass() && obj->AsClass()->IsRetired()) {
     return;
   }
 
+  GcRootVisitor visitor(this);
+  obj->VisitReferences<true>(visitor, VoidFunctor());
+
   gc::Heap* const heap = Runtime::Current()->GetHeap();
   const gc::space::ContinuousSpace* const space = heap->FindContinuousSpaceFromObject(obj, true);
   HprofHeapId heap_type = HPROF_HEAP_APP;
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 513ab37..dc60a38 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -236,6 +236,15 @@
     SetAccessFlags(flags | kAccClassIsStringClass);
   }
 
+  ALWAYS_INLINE bool IsClassLoaderClass() SHARED_REQUIRES(Locks::mutator_lock_) {
+    return (GetField32(AccessFlagsOffset()) & kAccClassIsClassLoaderClass) != 0;
+  }
+
+  ALWAYS_INLINE void SetClassLoaderClass() SHARED_REQUIRES(Locks::mutator_lock_) {
+    uint32_t flags = GetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_));
+    SetAccessFlags(flags | kAccClassIsClassLoaderClass);
+  }
+
   // Returns true if the class is abstract.
   ALWAYS_INLINE bool IsAbstract() SHARED_REQUIRES(Locks::mutator_lock_) {
     return (GetAccessFlags() & kAccAbstract) != 0;
diff --git a/runtime/mirror/class_loader-inl.h b/runtime/mirror/class_loader-inl.h
new file mode 100644
index 0000000..35f3664
--- /dev/null
+++ b/runtime/mirror/class_loader-inl.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_MIRROR_CLASS_LOADER_INL_H_
+#define ART_RUNTIME_MIRROR_CLASS_LOADER_INL_H_
+
+#include "class_loader.h"
+
+#include "base/mutex-inl.h"
+#include "class_table-inl.h"
+
+namespace art {
+namespace mirror {
+
+template <const bool kVisitClass, VerifyObjectFlags kVerifyFlags, typename Visitor>
+inline void ClassLoader::VisitReferences(mirror::Class* klass, const Visitor& visitor) {
+  // Visit instance fields first.
+  VisitInstanceFieldsReferences<kVisitClass>(klass, visitor);
+  // Visit classes loaded after.
+  ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+  ClassTable* const class_table = GetClassTable();
+  if (class_table != nullptr) {
+    class_table->VisitRoots(visitor);
+  }
+}
+
+}  // namespace mirror
+}  // namespace art
+
+#endif  // ART_RUNTIME_MIRROR_CLASS_LOADER_INL_H_
diff --git a/runtime/mirror/class_loader.h b/runtime/mirror/class_loader.h
index 940aaa6..21c652a 100644
--- a/runtime/mirror/class_loader.h
+++ b/runtime/mirror/class_loader.h
@@ -26,6 +26,8 @@
 
 namespace mirror {
 
+class Class;
+
 // C++ mirror of java.lang.ClassLoader
 class MANAGED ClassLoader : public Object {
  public:
@@ -44,6 +46,12 @@
     SetField64<false>(OFFSET_OF_OBJECT_MEMBER(ClassLoader, class_table_),
                       reinterpret_cast<uint64_t>(class_table));
   }
+  // Visit instance fields of the class loader as well as its associated classes.
+  // Null class loader is handled by ClassLinker::VisitClassRoots.
+  template <const bool kVisitClass, VerifyObjectFlags kVerifyFlags, typename Visitor>
+  void VisitReferences(mirror::Class* klass, const Visitor& visitor)
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!Locks::classlinker_classes_lock_);
 
  private:
   // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index c5610b5..7b1660b 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -25,6 +25,7 @@
 #include "array-inl.h"
 #include "class.h"
 #include "class_linker.h"
+#include "class_loader-inl.h"
 #include "lock_word-inl.h"
 #include "monitor.h"
 #include "object_array-inl.h"
@@ -997,6 +998,18 @@
   klass->VisitFieldsReferences<kVisitClass, true>(0, visitor);
 }
 
+
+template<VerifyObjectFlags kVerifyFlags>
+inline bool Object::IsClassLoader() {
+  return GetClass<kVerifyFlags>()->IsClassLoaderClass();
+}
+
+template<VerifyObjectFlags kVerifyFlags>
+inline mirror::ClassLoader* Object::AsClassLoader() {
+  DCHECK(IsClassLoader<kVerifyFlags>());
+  return down_cast<mirror::ClassLoader*>(this);
+}
+
 template <const bool kVisitClass, VerifyObjectFlags kVerifyFlags, typename Visitor,
     typename JavaLangRefVisitor>
 inline void Object::VisitReferences(const Visitor& visitor,
@@ -1010,6 +1023,9 @@
     } else if (kVisitClass) {
       visitor(this, ClassOffset(), false);
     }
+  } else if (klass->IsClassLoaderClass()) {
+    mirror::ClassLoader* class_loader = AsClassLoader<kVerifyFlags>();
+    class_loader->VisitReferences<kVisitClass, kVerifyFlags>(klass, visitor);
   } else {
     DCHECK(!klass->IsVariableSize());
     VisitInstanceFieldsReferences<kVisitClass>(klass, visitor);
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index eea9f37..4967a14 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -37,6 +37,7 @@
 
 class Array;
 class Class;
+class ClassLoader;
 class FinalizerReference;
 template<class T> class ObjectArray;
 template<class T> class PrimitiveArray;
@@ -156,6 +157,11 @@
   template<class T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   ObjectArray<T>* AsObjectArray() SHARED_REQUIRES(Locks::mutator_lock_);
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  bool IsClassLoader() SHARED_REQUIRES(Locks::mutator_lock_);
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  ClassLoader* AsClassLoader() SHARED_REQUIRES(Locks::mutator_lock_);
+
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
            ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
   bool IsArrayInstance() SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/modifiers.h b/runtime/modifiers.h
index 8586dd1..8b363a6 100644
--- a/runtime/modifiers.h
+++ b/runtime/modifiers.h
@@ -55,6 +55,9 @@
 // Special runtime-only flags.
 // Note: if only kAccClassIsReference is set, we have a soft reference.
 
+// class is ClassLoader or one of its subclasses
+static constexpr uint32_t kAccClassIsClassLoaderClass   = 0x10000000;
+
 // class/ancestor overrides finalize()
 static constexpr uint32_t kAccClassIsFinalizable        = 0x80000000;
 // class is a soft/weak/phantom ref