Disable class unloading when tracing is active

Tracing keeps a lot of pointers to ArtMethods that could belong to
classes that we are going to unload. A quick fix for this is to
disable class unloading when tracing is active.

Bug: 24414774
Bug: 22720414
Change-Id: Ia5619cbd7c9fd558eaa2a5000871d287213c4a76
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index b0590e2..acb39c5 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -79,6 +79,7 @@
 #include "scoped_thread_state_change.h"
 #include "handle_scope-inl.h"
 #include "thread-inl.h"
+#include "trace.h"
 #include "utils.h"
 #include "utils/dex_cache_arrays_layout-inl.h"
 #include "verifier/method_verifier.h"
@@ -1299,6 +1300,9 @@
 }
 
 void ClassLinker::VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags) {
+  // Acquire tracing_enabled before locking class linker lock to prevent lock order violation. Since
+  // enabling tracing requires the mutator lock, there are no race conditions here.
+  const bool tracing_enabled = Trace::IsTracingEnabled();
   Thread* const self = Thread::Current();
   WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
   BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(
@@ -1320,6 +1324,14 @@
     // 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(buffered_visitor);
+
+    // If tracing is enabled, then mark all the class loaders to prevent unloading.
+    if (tracing_enabled) {
+      for (const ClassLoaderData& data : class_loaders_) {
+        GcRoot<mirror::Object> root(GcRoot<mirror::Object>(self->DecodeJObject(data.weak_root)));
+        root.VisitRoot(visitor, RootInfo(kRootVMInternal));
+      }
+    }
   } else if ((flags & kVisitRootFlagNewRoots) != 0) {
     for (auto& root : new_class_roots_) {
       mirror::Class* old_ref = root.Read<kWithoutReadBarrier>();
diff --git a/runtime/trace.cc b/runtime/trace.cc
index eb0ea68..745aa63 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -1058,4 +1058,9 @@
   return the_trace_->buffer_size_;
 }
 
+bool Trace::IsTracingEnabled() {
+  MutexLock mu(Thread::Current(), *Locks::trace_lock_);
+  return the_trace_ != nullptr;
+}
+
 }  // namespace art
diff --git a/runtime/trace.h b/runtime/trace.h
index 87a691d..356a81f 100644
--- a/runtime/trace.h
+++ b/runtime/trace.h
@@ -183,6 +183,9 @@
   static TraceMode GetMode() REQUIRES(!Locks::trace_lock_);
   static size_t GetBufferSize() REQUIRES(!Locks::trace_lock_);
 
+  // Used by class linker to prevent class unloading.
+  static bool IsTracingEnabled() REQUIRES(!Locks::trace_lock_);
+
  private:
   Trace(File* trace_file, const char* trace_name, size_t buffer_size, int flags,
         TraceOutputMode output_mode, TraceMode trace_mode);
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index a103eac..db16b97 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -369,6 +369,7 @@
 # This test dynamically enables tracing to force a deoptimization. This makes the test meaningless
 # when already tracing, and writes an error message that we do not want to check for.
 TEST_ART_BROKEN_TRACING_RUN_TESTS := \
+  087-gc-after-link \
   137-cfi \
   141-class-unload \
   802-deoptimization