summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/166-bad-interface-super/expected.txt2
-rw-r--r--test/166-bad-interface-super/info.txt1
-rw-r--r--test/166-bad-interface-super/jasmin/BadSuper1.j17
-rw-r--r--test/166-bad-interface-super/jasmin/BadSuper2.j17
-rw-r--r--test/166-bad-interface-super/src/BaseClass.java18
-rw-r--r--test/166-bad-interface-super/src/BaseInterface.java18
-rw-r--r--test/166-bad-interface-super/src/Main.java31
-rw-r--r--test/655-jit-clinit/src/Main.java4
-rw-r--r--test/667-jit-jni-stub/expected.txt1
-rw-r--r--test/667-jit-jni-stub/info.txt1
-rw-r--r--test/667-jit-jni-stub/jit_jni_stub_test.cc63
-rwxr-xr-xtest/667-jit-jni-stub/run18
-rw-r--r--test/667-jit-jni-stub/src/Main.java180
-rw-r--r--test/Android.bp1
-rw-r--r--test/common/runtime_state.cc25
15 files changed, 391 insertions, 6 deletions
diff --git a/test/166-bad-interface-super/expected.txt b/test/166-bad-interface-super/expected.txt
new file mode 100644
index 0000000000..c49f6d255f
--- /dev/null
+++ b/test/166-bad-interface-super/expected.txt
@@ -0,0 +1,2 @@
+Caught java.lang.ClassFormatError when trying to resolve BadSuper1.
+Caught java.lang.ClassFormatError when trying to resolve BadSuper2.
diff --git a/test/166-bad-interface-super/info.txt b/test/166-bad-interface-super/info.txt
new file mode 100644
index 0000000000..bcba8c0d89
--- /dev/null
+++ b/test/166-bad-interface-super/info.txt
@@ -0,0 +1 @@
+Test that linking an interface declaring a superclass other than j.l.Object throws CFE.
diff --git a/test/166-bad-interface-super/jasmin/BadSuper1.j b/test/166-bad-interface-super/jasmin/BadSuper1.j
new file mode 100644
index 0000000000..f96564ec73
--- /dev/null
+++ b/test/166-bad-interface-super/jasmin/BadSuper1.j
@@ -0,0 +1,17 @@
+; 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.
+
+.interface public BadSuper1
+.super BaseInterface
+
diff --git a/test/166-bad-interface-super/jasmin/BadSuper2.j b/test/166-bad-interface-super/jasmin/BadSuper2.j
new file mode 100644
index 0000000000..584bd2094d
--- /dev/null
+++ b/test/166-bad-interface-super/jasmin/BadSuper2.j
@@ -0,0 +1,17 @@
+; 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.
+
+.interface public BadSuper2
+.super BaseClass
+
diff --git a/test/166-bad-interface-super/src/BaseClass.java b/test/166-bad-interface-super/src/BaseClass.java
new file mode 100644
index 0000000000..6ea1ad3f3b
--- /dev/null
+++ b/test/166-bad-interface-super/src/BaseClass.java
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+
+class BaseClass {
+}
diff --git a/test/166-bad-interface-super/src/BaseInterface.java b/test/166-bad-interface-super/src/BaseInterface.java
new file mode 100644
index 0000000000..7872a43794
--- /dev/null
+++ b/test/166-bad-interface-super/src/BaseInterface.java
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+
+interface BaseInterface {
+}
diff --git a/test/166-bad-interface-super/src/Main.java b/test/166-bad-interface-super/src/Main.java
new file mode 100644
index 0000000000..3df2574678
--- /dev/null
+++ b/test/166-bad-interface-super/src/Main.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+class Main {
+ public static void main(String[] args) throws Exception {
+ tryResolveClassExpectingCFE("BadSuper1");
+ tryResolveClassExpectingCFE("BadSuper2");
+ }
+
+ public static void tryResolveClassExpectingCFE(String className) throws Exception {
+ try {
+ Class.forName(className);
+ } catch (ClassFormatError e) {
+ System.out.println(
+ "Caught " + e.getClass().getName() + " when trying to resolve " + className + ".");
+ }
+ }
+}
diff --git a/test/655-jit-clinit/src/Main.java b/test/655-jit-clinit/src/Main.java
index 44b315478f..2fb8f2a86e 100644
--- a/test/655-jit-clinit/src/Main.java
+++ b/test/655-jit-clinit/src/Main.java
@@ -23,7 +23,7 @@ public class Main {
Foo.hotMethod();
}
- public native static boolean isJitCompiled(Class<?> cls, String methodName);
+ public native static boolean hasJitCompiledEntrypoint(Class<?> cls, String methodName);
private native static boolean hasJit();
}
@@ -36,7 +36,7 @@ class Foo {
static {
array = new Object[10000];
- while (!Main.isJitCompiled(Foo.class, "hotMethod")) {
+ while (!Main.hasJitCompiledEntrypoint(Foo.class, "hotMethod")) {
Foo.hotMethod();
try {
// Sleep to give a chance for the JIT to compile `hotMethod`.
diff --git a/test/667-jit-jni-stub/expected.txt b/test/667-jit-jni-stub/expected.txt
new file mode 100644
index 0000000000..6a5618ebc6
--- /dev/null
+++ b/test/667-jit-jni-stub/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/667-jit-jni-stub/info.txt b/test/667-jit-jni-stub/info.txt
new file mode 100644
index 0000000000..6f25c44592
--- /dev/null
+++ b/test/667-jit-jni-stub/info.txt
@@ -0,0 +1 @@
+Tests for JITting and collecting JNI stubs.
diff --git a/test/667-jit-jni-stub/jit_jni_stub_test.cc b/test/667-jit-jni-stub/jit_jni_stub_test.cc
new file mode 100644
index 0000000000..82e06fc018
--- /dev/null
+++ b/test/667-jit-jni-stub/jit_jni_stub_test.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright 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.
+ */
+
+#include <jni.h>
+
+#include "jit/jit.h"
+#include "jit/jit_code_cache.h"
+#include "mirror/class.h"
+#include "mirror/string.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+
+namespace art {
+
+// Local class declared as a friend of JitCodeCache so that we can access its internals.
+class JitJniStubTestHelper {
+ public:
+ static bool isNextJitGcFull(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
+ CHECK(Runtime::Current()->GetJit() != nullptr);
+ jit::JitCodeCache* cache = Runtime::Current()->GetJit()->GetCodeCache();
+ MutexLock mu(self, cache->lock_);
+ return cache->ShouldDoFullCollection();
+ }
+};
+
+// Calls through to a static method with signature "()V".
+extern "C" JNIEXPORT
+void Java_Main_callThrough(JNIEnv* env, jclass, jclass klass, jstring methodName) {
+ ScopedObjectAccess soa(Thread::Current());
+ std::string name = soa.Decode<mirror::String>(methodName)->ToModifiedUtf8();
+ jmethodID method = env->GetStaticMethodID(klass, name.c_str(), "()V");
+ CHECK(method != nullptr) << soa.Decode<mirror::Class>(klass)->PrettyDescriptor() << "." << name;
+ env->CallStaticVoidMethod(klass, method);
+}
+
+extern "C" JNIEXPORT
+void Java_Main_jitGc(JNIEnv*, jclass) {
+ CHECK(Runtime::Current()->GetJit() != nullptr);
+ jit::JitCodeCache* cache = Runtime::Current()->GetJit()->GetCodeCache();
+ ScopedObjectAccess soa(Thread::Current());
+ cache->GarbageCollectCache(Thread::Current());
+}
+
+extern "C" JNIEXPORT
+jboolean Java_Main_isNextJitGcFull(JNIEnv*, jclass) {
+ ScopedObjectAccess soa(Thread::Current());
+ return JitJniStubTestHelper::isNextJitGcFull(soa.Self());
+}
+
+} // namespace art
diff --git a/test/667-jit-jni-stub/run b/test/667-jit-jni-stub/run
new file mode 100755
index 0000000000..1877be482e
--- /dev/null
+++ b/test/667-jit-jni-stub/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# 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.
+
+# Disable AOT compilation of JNI stubs.
+${RUN} "${@}" --no-prebuild --no-dex2oat
diff --git a/test/667-jit-jni-stub/src/Main.java b/test/667-jit-jni-stub/src/Main.java
new file mode 100644
index 0000000000..b867970eab
--- /dev/null
+++ b/test/667-jit-jni-stub/src/Main.java
@@ -0,0 +1,180 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+ if (isAotCompiled(Main.class, "hasJit")) {
+ throw new Error("This test must be run with --no-prebuild --no-dex2oat!");
+ }
+ if (!hasJit()) {
+ return;
+ }
+
+ testCompilationUseAndCollection();
+ testMixedFramesOnStack();
+ }
+
+ public static void testCompilationUseAndCollection() {
+ // Test that callThrough() can be JIT-compiled.
+ assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough"));
+ assertFalse(hasJitCompiledCode(Main.class, "callThrough"));
+ ensureCompiledCallThroughEntrypoint(/* call */ true);
+ assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough"));
+ assertTrue(hasJitCompiledCode(Main.class, "callThrough"));
+
+ // Use callThrough() once again now that the method has a JIT-compiled stub.
+ callThrough(Main.class, "doNothing");
+
+ // Test that GC with the JIT-compiled stub on the stack does not collect it.
+ // Also tests stack walk over the JIT-compiled stub.
+ callThrough(Main.class, "testGcWithCallThroughStubOnStack");
+
+ // Test that, when marking used methods before a full JIT GC, a single execution
+ // of the GenericJNI trampoline can save the compiled stub from being collected.
+ testSingleInvocationTriggersRecompilation();
+
+ // Test that the JNI compiled stub can actually be collected.
+ testStubCanBeCollected();
+ }
+
+ public static void testGcWithCallThroughStubOnStack() {
+ // Check that this method was called via JIT-compiled callThrough() stub.
+ assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough"));
+ // This assertion also exercises stack walk over the JIT-compiled callThrough() stub.
+ assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough"));
+
+ doJitGcsUntilFullJitGcIsScheduled();
+ // The callThrough() on the stack above this method is using the compiled stub,
+ // so the JIT GC should not remove the compiled code.
+ jitGc();
+ assertTrue(hasJitCompiledCode(Main.class, "callThrough"));
+ }
+
+ public static void testSingleInvocationTriggersRecompilation() {
+ // After scheduling a full JIT GC, single call through the GenericJNI
+ // trampoline should ensure that the compiled stub is used again.
+ doJitGcsUntilFullJitGcIsScheduled();
+ callThrough(Main.class, "doNothing");
+ ensureCompiledCallThroughEntrypoint(/* call */ false); // Wait for the compilation task to run.
+ assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough"));
+ jitGc(); // This JIT GC should not collect the callThrough() stub.
+ assertTrue(hasJitCompiledCode(Main.class, "callThrough"));
+ }
+
+ public static void testMixedFramesOnStack() {
+ // Starts without a compiled JNI stub for callThrough().
+ assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough"));
+ assertFalse(hasJitCompiledCode(Main.class, "callThrough"));
+ callThrough(Main.class, "testMixedFramesOnStackStage2");
+ // We have just returned through the JIT-compiled JNI stub, so it must still
+ // be compiled (though not necessarily with the entrypoint pointing to it).
+ assertTrue(hasJitCompiledCode(Main.class, "callThrough"));
+ // Though the callThrough() is on the stack, that frame is using the GenericJNI
+ // and does not prevent the collection of the JNI stub.
+ testStubCanBeCollected();
+ }
+
+ public static void testMixedFramesOnStackStage2() {
+ // We cannot assert that callThrough() has no JIT compiled stub as that check
+ // may race against the compilation task. Just check the caller.
+ assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough"));
+ // Now ensure that the JNI stub is compiled and used.
+ ensureCompiledCallThroughEntrypoint(/* call */ true);
+ callThrough(Main.class, "testMixedFramesOnStackStage3");
+ }
+
+ public static void testMixedFramesOnStackStage3() {
+ // Check that this method was called via JIT-compiled callThrough() stub.
+ assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough"));
+ // This assertion also exercises stack walk over the JIT-compiled callThrough() stub.
+ assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough"));
+ // For a good measure, try a JIT GC.
+ jitGc();
+ }
+
+ public static void testStubCanBeCollected() {
+ assertTrue(hasJitCompiledCode(Main.class, "callThrough"));
+ doJitGcsUntilFullJitGcIsScheduled();
+ assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough"));
+ assertTrue(hasJitCompiledCode(Main.class, "callThrough"));
+ jitGc(); // JIT GC without callThrough() on the stack should collect the callThrough() stub.
+ assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough"));
+ assertFalse(hasJitCompiledCode(Main.class, "callThrough"));
+ }
+
+ public static void doJitGcsUntilFullJitGcIsScheduled() {
+ // We enter with a compiled stub for callThrough() but we also need the entrypoint to be set.
+ assertTrue(hasJitCompiledCode(Main.class, "callThrough"));
+ ensureCompiledCallThroughEntrypoint(/* call */ true);
+ // Perform JIT GC until the next GC is marked to do full collection.
+ do {
+ assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough"));
+ callThrough(Main.class, "jitGc"); // JIT GC with callThrough() safely on the stack.
+ } while (!isNextJitGcFull());
+ // The JIT GC before the full collection resets entrypoints and waits to see
+ // if the methods are still in use.
+ assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough"));
+ assertTrue(hasJitCompiledCode(Main.class, "callThrough"));
+ }
+
+ public static void ensureCompiledCallThroughEntrypoint(boolean call) {
+ int count = 0;
+ while (!hasJitCompiledEntrypoint(Main.class, "callThrough")) {
+ // If `call` is true, also exercise the `callThrough()` method to increase hotness.
+ int limit = call ? 1 << Math.min(count, 12) : 0;
+ for (int i = 0; i < limit; ++i) {
+ callThrough(Main.class, "doNothing");
+ }
+ try {
+ // Sleep to give a chance for the JIT to compile `hasJit` stub.
+ Thread.sleep(100);
+ } catch (Exception e) {
+ // Ignore
+ }
+ if (++count == 50) {
+ throw new Error("TIMEOUT");
+ }
+ };
+ }
+
+ public static void assertTrue(boolean value) {
+ if (!value) {
+ throw new AssertionError("Expected true!");
+ }
+ }
+
+ public static void assertFalse(boolean value) {
+ if (value) {
+ throw new AssertionError("Expected false!");
+ }
+ }
+
+ public static void doNothing() { }
+ public static void throwError() { throw new Error(); }
+
+ // Note that the callThrough()'s shorty differs from shorties of the other
+ // native methods used in this test because of the return type `void.`
+ public native static void callThrough(Class<?> cls, String methodName);
+
+ public native static void jitGc();
+ public native static boolean isNextJitGcFull();
+
+ public native static boolean isAotCompiled(Class<?> cls, String methodName);
+ public native static boolean hasJitCompiledEntrypoint(Class<?> cls, String methodName);
+ public native static boolean hasJitCompiledCode(Class<?> cls, String methodName);
+ private native static boolean hasJit();
+}
diff --git a/test/Android.bp b/test/Android.bp
index 8f29251907..2d526d256c 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -384,6 +384,7 @@ cc_defaults {
"656-annotation-lookup-generic-jni/test.cc",
"661-oat-writer-layout/oat_writer_layout.cc",
"664-aget-verifier/aget-verifier.cc",
+ "667-jit-jni-stub/jit_jni_stub_test.cc",
"708-jit-cache-churn/jit.cc",
],
shared_libs: [
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index df497c1181..34580800cc 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -152,10 +152,10 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isAotCompiled(JNIEnv* env,
return method->GetOatMethodQuickCode(kRuntimePointerSize) != nullptr;
}
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_isJitCompiled(JNIEnv* env,
- jclass,
- jclass cls,
- jstring method_name) {
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJitCompiledEntrypoint(JNIEnv* env,
+ jclass,
+ jclass cls,
+ jstring method_name) {
jit::Jit* jit = GetJitIfEnabled();
if (jit == nullptr) {
return false;
@@ -169,6 +169,23 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isJitCompiled(JNIEnv* env,
return jit->GetCodeCache()->ContainsPc(method->GetEntryPointFromQuickCompiledCode());
}
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJitCompiledCode(JNIEnv* env,
+ jclass,
+ jclass cls,
+ jstring method_name) {
+ jit::Jit* jit = GetJitIfEnabled();
+ if (jit == nullptr) {
+ return false;
+ }
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ ScopedUtfChars chars(env, method_name);
+ CHECK(chars.c_str() != nullptr);
+ ArtMethod* method = soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName(
+ chars.c_str(), kRuntimePointerSize);
+ return jit->GetCodeCache()->ContainsMethod(method);
+}
+
extern "C" JNIEXPORT void JNICALL Java_Main_ensureJitCompiled(JNIEnv* env,
jclass,
jclass cls,