diff options
Diffstat (limited to 'test')
| -rw-r--r-- | test/166-bad-interface-super/expected.txt | 2 | ||||
| -rw-r--r-- | test/166-bad-interface-super/info.txt | 1 | ||||
| -rw-r--r-- | test/166-bad-interface-super/jasmin/BadSuper1.j | 17 | ||||
| -rw-r--r-- | test/166-bad-interface-super/jasmin/BadSuper2.j | 17 | ||||
| -rw-r--r-- | test/166-bad-interface-super/src/BaseClass.java | 18 | ||||
| -rw-r--r-- | test/166-bad-interface-super/src/BaseInterface.java | 18 | ||||
| -rw-r--r-- | test/166-bad-interface-super/src/Main.java | 31 | ||||
| -rw-r--r-- | test/655-jit-clinit/src/Main.java | 4 | ||||
| -rw-r--r-- | test/667-jit-jni-stub/expected.txt | 1 | ||||
| -rw-r--r-- | test/667-jit-jni-stub/info.txt | 1 | ||||
| -rw-r--r-- | test/667-jit-jni-stub/jit_jni_stub_test.cc | 63 | ||||
| -rwxr-xr-x | test/667-jit-jni-stub/run | 18 | ||||
| -rw-r--r-- | test/667-jit-jni-stub/src/Main.java | 180 | ||||
| -rw-r--r-- | test/Android.bp | 1 | ||||
| -rw-r--r-- | test/common/runtime_state.cc | 25 |
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, |