Merge "Add an option to the art script to not cleanup."
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 3ac87c5..fc14da1 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -7119,7 +7119,7 @@
                                                                       method_alignment_);
   const size_t old_methods_ptr_size = (old_methods != nullptr) ? old_size : 0;
   auto* methods = reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(
-      Runtime::Current()->GetLinearAlloc()->Realloc(
+      class_linker_->GetAllocatorForClassLoader(klass_->GetClassLoader())->Realloc(
           self_, old_methods, old_methods_ptr_size, new_size));
   CHECK(methods != nullptr);  // Native allocation failure aborts.
 
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 19df0d2..2e06536 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -634,7 +634,7 @@
 void StackVisitor::SanityCheckFrame() const {
   if (kIsDebugBuild) {
     ArtMethod* method = GetMethod();
-    auto* declaring_class = method->GetDeclaringClass();
+    mirror::Class* declaring_class = method->GetDeclaringClass();
     // Runtime methods have null declaring class.
     if (!method->IsRuntimeMethod()) {
       CHECK(declaring_class != nullptr);
@@ -647,11 +647,14 @@
     LinearAlloc* const linear_alloc = runtime->GetLinearAlloc();
     if (!linear_alloc->Contains(method)) {
       // Check class linker linear allocs.
-      mirror::Class* klass = method->GetDeclaringClass();
+      // We get the canonical method as copied methods may have their declaring
+      // class from another class loader.
+      ArtMethod* canonical = method->GetCanonicalMethod();
+      mirror::Class* klass = canonical->GetDeclaringClass();
       LinearAlloc* const class_linear_alloc = (klass != nullptr)
           ? runtime->GetClassLinker()->GetAllocatorForClassLoader(klass->GetClassLoader())
           : linear_alloc;
-      if (!class_linear_alloc->Contains(method)) {
+      if (!class_linear_alloc->Contains(canonical)) {
         // Check image space.
         bool in_image = false;
         for (auto& space : runtime->GetHeap()->GetContinuousSpaces()) {
@@ -660,14 +663,14 @@
             const auto& header = image_space->GetImageHeader();
             const ImageSection& methods = header.GetMethodsSection();
             const ImageSection& runtime_methods = header.GetRuntimeMethodsSection();
-            const size_t offset =  reinterpret_cast<const uint8_t*>(method) - image_space->Begin();
+            const size_t offset =  reinterpret_cast<const uint8_t*>(canonical) - image_space->Begin();
             if (methods.Contains(offset) || runtime_methods.Contains(offset)) {
               in_image = true;
               break;
             }
           }
         }
-        CHECK(in_image) << method->PrettyMethod() << " not in linear alloc or image";
+        CHECK(in_image) << canonical->PrettyMethod() << " not in linear alloc or image";
       }
     }
     if (cur_quick_frame_ != nullptr) {
diff --git a/test/661-classloader-allocator/expected.txt b/test/661-classloader-allocator/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/661-classloader-allocator/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/661-classloader-allocator/info.txt b/test/661-classloader-allocator/info.txt
new file mode 100644
index 0000000..872b5e0
--- /dev/null
+++ b/test/661-classloader-allocator/info.txt
@@ -0,0 +1,3 @@
+Regression test for copied methods which used to not
+be (re-)allocated with the right class loader allocator,
+which crashed the JIT profiler.
diff --git a/test/661-classloader-allocator/src-ex/OtherClass.java b/test/661-classloader-allocator/src-ex/OtherClass.java
new file mode 100644
index 0000000..e59cb95
--- /dev/null
+++ b/test/661-classloader-allocator/src-ex/OtherClass.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package p1;
+
+interface Itf {
+  public default int defaultMethod() {
+    return 42;
+  }
+}
+
+public class OtherClass implements Itf {
+  public void foo() {
+  }
+}
diff --git a/test/661-classloader-allocator/src/Main.java b/test/661-classloader-allocator/src/Main.java
new file mode 100644
index 0000000..40f8f7a
--- /dev/null
+++ b/test/661-classloader-allocator/src/Main.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+import java.lang.reflect.Constructor;
+
+public class Main {
+  static final String DEX_FILE =
+      System.getenv("DEX_LOCATION") + "/661-classloader-allocator-ex.jar";
+  static final String LIBRARY_SEARCH_PATH = System.getProperty("java.library.path");
+
+  private static void doUnloading() {
+    // Stop the JIT to ensure its threads and work queue are not keeping classes
+    // artifically alive.
+    stopJit();
+    // Do multiple GCs to prevent rare flakiness if some other thread is keeping the
+    // classloader live.
+    for (int i = 0; i < 5; ++i) {
+      Runtime.getRuntime().gc();
+    }
+    startJit();
+  }
+
+  public static void main(String[] args) throws Exception {
+    System.loadLibrary(args[0]);
+    loadClass();
+    doUnloading();
+    // fetchProfiles iterate over the ProfilingInfo, we used to crash in the presence
+    // of unloaded copied methods.
+    fetchProfiles();
+  }
+
+  public static void loadClass() throws Exception {
+    Class<?> pathClassLoader = Class.forName("dalvik.system.PathClassLoader");
+    if (pathClassLoader == null) {
+      throw new AssertionError("Couldn't find path class loader class");
+    }
+    Constructor<?> constructor =
+      pathClassLoader.getDeclaredConstructor(String.class, String.class, ClassLoader.class);
+    ClassLoader loader = (ClassLoader) constructor.newInstance(
+      DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader());
+    Class<?> otherClass = loader.loadClass("p1.OtherClass");
+    ensureJitCompiled(otherClass, "foo");
+  }
+
+  public static native void ensureJitCompiled(Class<?> cls, String methodName);
+  public static native void fetchProfiles();
+  public static native void stopJit();
+  public static native void startJit();
+}
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index 7c0ed69..60c7315 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -254,4 +254,17 @@
   return Runtime::Current()->GetNumberOfDeoptimizations();
 }
 
+extern "C" JNIEXPORT void JNICALL Java_Main_fetchProfiles(JNIEnv*, jclass) {
+  jit::Jit* jit = GetJitIfEnabled();
+  if (jit == nullptr) {
+    return;
+  }
+  jit::JitCodeCache* code_cache = jit->GetCodeCache();
+  std::vector<ProfileMethodInfo> unused_vector;
+  std::set<std::string> unused_locations;
+  unused_locations.insert("fake_location");
+  ScopedObjectAccess soa(Thread::Current());
+  code_cache->GetProfiledMethods(unused_locations, unused_vector);
+}
+
 }  // namespace art