diff options
Diffstat (limited to 'test')
49 files changed, 1774 insertions, 36 deletions
diff --git a/test/071-dexfile-get-static-size/build b/test/071-dexfile-get-static-size/build new file mode 100755 index 0000000000..0bba66d065 --- /dev/null +++ b/test/071-dexfile-get-static-size/build @@ -0,0 +1,30 @@ +#!/bin/bash +# +# 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. + +./default-build "$@" + +# Create and add as resources to the test jar file: +# 1. test1.dex +# 2. test2.dex +# 3. test-jar.jar, containing test1.dex as classes.dex +# 4. multi-jar.jar, containing test1.dex as classes.dex and test2.dex as classes2.dex +mkdir test-jar +cp test1.dex test-jar/classes.dex +cp test2.dex test-jar/classes2.dex +zip -j test-jar.jar test-jar/classes.dex +zip -j multi-jar.jar test-jar/classes.dex test-jar/classes2.dex +jar uf ${TEST_NAME}.jar test1.dex test2.dex test-jar.jar multi-jar.jar + diff --git a/test/071-dexfile-get-static-size/expected.txt b/test/071-dexfile-get-static-size/expected.txt new file mode 100644 index 0000000000..dfb77c3a2f --- /dev/null +++ b/test/071-dexfile-get-static-size/expected.txt @@ -0,0 +1,4 @@ +Size for test1.dex: 1864 +Size for test2.dex: 1264 +Size for test-jar.jar: 1864 +Size for multi-jar.jar: 3128 diff --git a/test/071-dexfile-get-static-size/info.txt b/test/071-dexfile-get-static-size/info.txt new file mode 100644 index 0000000000..5b528e81b4 --- /dev/null +++ b/test/071-dexfile-get-static-size/info.txt @@ -0,0 +1,3 @@ +Test DexFile.getStaticSizeOfDexFile API. + +test1.dex and test2.dex are arbitrary valid dex files. diff --git a/test/071-dexfile-get-static-size/src/Main.java b/test/071-dexfile-get-static-size/src/Main.java new file mode 100644 index 0000000000..4bf453801e --- /dev/null +++ b/test/071-dexfile-get-static-size/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.io.InputStream; +import java.io.OutputStream; +import java.io.FileOutputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +public class Main { + private static void extractResource(String resource, String filename) throws Exception { + ClassLoader loader = Main.class.getClassLoader(); + InputStream is = loader.getResourceAsStream(resource); + OutputStream os = new FileOutputStream(filename); + int read; + byte[] buf = new byte[4096]; + while ((read = is.read(buf)) >= 0) { + os.write(buf, 0, read); + } + is.close(); + os.close(); + } + + private static long getDexFileSize(String filename) throws Exception { + ClassLoader loader = Main.class.getClassLoader(); + Class<?> DexFile = loader.loadClass("dalvik.system.DexFile"); + Method DexFile_loadDex = DexFile.getMethod("loadDex", + String.class, + String.class, + Integer.TYPE); + Method DexFile_getStaticSizeOfDexFile = DexFile.getMethod("getStaticSizeOfDexFile"); + Object dexFile = DexFile_loadDex.invoke(null, filename, null, 0); + return (Long) DexFile_getStaticSizeOfDexFile.invoke(dexFile); + } + + private static void test(String resource) throws Exception { + String filename = System.getenv("DEX_LOCATION") + "/" + resource; + extractResource(resource, filename); + long size = getDexFileSize(filename); + System.out.println("Size for " + resource + ": " + size); + } + + public static void main(String[] args) throws Exception { + test("test1.dex"); + test("test2.dex"); + test("test-jar.jar"); + test("multi-jar.jar"); + } +} diff --git a/test/071-dexfile-get-static-size/test1.dex b/test/071-dexfile-get-static-size/test1.dex Binary files differnew file mode 100644 index 0000000000..84602d03c2 --- /dev/null +++ b/test/071-dexfile-get-static-size/test1.dex diff --git a/test/071-dexfile-get-static-size/test2.dex b/test/071-dexfile-get-static-size/test2.dex Binary files differnew file mode 100644 index 0000000000..a07c46ef59 --- /dev/null +++ b/test/071-dexfile-get-static-size/test2.dex diff --git a/test/099-vmdebug/expected.txt b/test/099-vmdebug/expected.txt index b8d72f66f8..f7801de62f 100644 --- a/test/099-vmdebug/expected.txt +++ b/test/099-vmdebug/expected.txt @@ -23,3 +23,9 @@ Instances of null 0 Instances of ClassA assignable 3 Array counts [2, 1, 0] Array counts assignable [3, 1, 0] +ClassD got 3, combined mask: 13 +ClassE got 2, combined mask: 18 +null got 0 +ClassD assignable got 5, combined mask: 31 +ClassE assignable got 2, combined mask: 18 +null assignable got 0 diff --git a/test/099-vmdebug/info.txt b/test/099-vmdebug/info.txt index 7f88086986..873429e076 100644 --- a/test/099-vmdebug/info.txt +++ b/test/099-vmdebug/info.txt @@ -1 +1 @@ -Tests of private dalvik.system.VMDebug support for method tracing. +Tests of dalvik.system.VMDebug APIs. diff --git a/test/099-vmdebug/src/Main.java b/test/099-vmdebug/src/Main.java index 90ad3155ca..e0d829a0d6 100644 --- a/test/099-vmdebug/src/Main.java +++ b/test/099-vmdebug/src/Main.java @@ -33,6 +33,7 @@ public class Main { } testMethodTracing(); testCountInstances(); + testGetInstances(); testRuntimeStat(); testRuntimeStats(); } @@ -249,6 +250,59 @@ public class Main { System.out.println("Array counts assignable " + Arrays.toString(counts)); } + static class ClassD { + public int mask; + + public ClassD(int mask) { + this.mask = mask; + } + } + + static class ClassE extends ClassD { + public ClassE(int mask) { + super(mask); + } + } + + private static void testGetInstances() throws Exception { + ArrayList<Object> l = new ArrayList<Object>(); + l.add(new ClassD(0x01)); + l.add(new ClassE(0x02)); + l.add(new ClassD(0x04)); + l.add(new ClassD(0x08)); + l.add(new ClassE(0x10)); + Runtime.getRuntime().gc(); + Class<?>[] classes = new Class<?>[] {ClassD.class, ClassE.class, null}; + Object[][] instances = VMDebug.getInstancesOfClasses(classes, false); + + int mask = 0; + for (Object instance : instances[0]) { + mask |= ((ClassD)instance).mask; + } + System.out.println("ClassD got " + instances[0].length + ", combined mask: " + mask); + + mask = 0; + for (Object instance : instances[1]) { + mask |= ((ClassD)instance).mask; + } + System.out.println("ClassE got " + instances[1].length + ", combined mask: " + mask); + System.out.println("null got " + instances[2].length); + + instances = VMDebug.getInstancesOfClasses(classes, true); + mask = 0; + for (Object instance : instances[0]) { + mask |= ((ClassD)instance).mask; + } + System.out.println("ClassD assignable got " + instances[0].length + ", combined mask: " + mask); + + mask = 0; + for (Object instance : instances[1]) { + mask |= ((ClassD)instance).mask; + } + System.out.println("ClassE assignable got " + instances[1].length + ", combined mask: " + mask); + System.out.println("null assignable got " + instances[2].length); + } + private static class VMDebug { private static final Method startMethodTracingMethod; private static final Method stopMethodTracingMethod; @@ -257,6 +311,7 @@ public class Main { private static final Method getRuntimeStatsMethod; private static final Method countInstancesOfClassMethod; private static final Method countInstancesOfClassesMethod; + private static final Method getInstancesOfClassesMethod; static { try { Class<?> c = Class.forName("dalvik.system.VMDebug"); @@ -270,6 +325,8 @@ public class Main { Class.class, Boolean.TYPE); countInstancesOfClassesMethod = c.getDeclaredMethod("countInstancesOfClasses", Class[].class, Boolean.TYPE); + getInstancesOfClassesMethod = c.getDeclaredMethod("getInstancesOfClasses", + Class[].class, Boolean.TYPE); } catch (Exception e) { throw new RuntimeException(e); } @@ -300,5 +357,9 @@ public class Main { return (long[]) countInstancesOfClassesMethod.invoke( null, new Object[]{classes, assignable}); } + public static Object[][] getInstancesOfClasses(Class<?>[] classes, boolean assignable) throws Exception { + return (Object[][]) getInstancesOfClassesMethod.invoke( + null, new Object[]{classes, assignable}); + } } } diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc index 58b33be573..66270fd5c7 100644 --- a/test/137-cfi/cfi.cc +++ b/test/137-cfi/cfi.cc @@ -104,20 +104,6 @@ static void MoreErrorInfo(pid_t pid, bool sig_quit_on_fail) { } #endif -// Currently we have to fall back to our own loader for the boot image when it's compiled PIC -// because its base is zero. Thus in-process unwinding through it won't work. This is a helper -// detecting this. -#if __linux__ -static bool IsPicImage() { - std::vector<gc::space::ImageSpace*> image_spaces = - Runtime::Current()->GetHeap()->GetBootImageSpaces(); - CHECK(!image_spaces.empty()); // We should be running with an image. - const OatFile* oat_file = image_spaces[0]->GetOatFile(); - CHECK(oat_file != nullptr); // We should have an oat file to go with the image. - return oat_file->IsPic(); -} -#endif - extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess( JNIEnv*, jobject, @@ -125,11 +111,6 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess( jint, jboolean) { #if __linux__ - if (IsPicImage()) { - LOG(INFO) << "Image is pic, in-process unwinding check bypassed."; - return JNI_TRUE; - } - // TODO: What to do on Valgrind? std::unique_ptr<Backtrace> bt(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, GetTid())); 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/167-visit-locks/expected.txt b/test/167-visit-locks/expected.txt new file mode 100644 index 0000000000..5157c64c83 --- /dev/null +++ b/test/167-visit-locks/expected.txt @@ -0,0 +1,3 @@ +JNI_OnLoad called +First +Second diff --git a/test/167-visit-locks/info.txt b/test/167-visit-locks/info.txt new file mode 100644 index 0000000000..d849bc31ed --- /dev/null +++ b/test/167-visit-locks/info.txt @@ -0,0 +1 @@ +Regression test for b/68703210 diff --git a/test/167-visit-locks/run b/test/167-visit-locks/run new file mode 100644 index 0000000000..93654113e6 --- /dev/null +++ b/test/167-visit-locks/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. + +# Use a smaller heap so it's easier to potentially fill up. +exec ${RUN} $@ --runtime-option -Xmx2m diff --git a/test/167-visit-locks/smali/TestSync.smali b/test/167-visit-locks/smali/TestSync.smali new file mode 100644 index 0000000000..5e68ad7003 --- /dev/null +++ b/test/167-visit-locks/smali/TestSync.smali @@ -0,0 +1,119 @@ +.class LTestSync; +.super Ljava/lang/Object; +.source "Main.java" + + +# direct methods +.method constructor <init>()V + .registers 1 + + .prologue + .line 6 + invoke-direct {p0}, Ljava/lang/Object;-><init>()V + + return-void +.end method + +.method public static run()V + # v0-v2 were generated by javac+dx for the original src code, keeping them. + # v10..v19 are for tracking, aliasing and manipulating the first lock. + # v20..v29 are for tracking, aliasing and manipulating the second lock. + .registers 30 + + .prologue + .line 8 + const-string v1, "First" + + .line 9 + const-string v2, "Second" + + move-object v10, v1 + const v1, 0x1 + + .line 10 + monitor-enter v10 + + # Introduce a range of dead copies. + move-object v11, v10 + move-object v12, v10 + move-object v13, v10 + move-object v14, v10 + move-object v15, v10 + move-object/16 v16, v10 + move-object/16 v17, v10 + move-object/16 v18, v10 + + # Introduce a copy that we'll use for unlock. + move-object/16 v19, v10 + + # Clobber the original alias. + const v10, 0x3 + + move-object/16 v20, v2 + const v2, 0x2 + + .line 11 + :try_start_b + monitor-enter v20 + :try_end_c + + # Introduce a range of dead copies. + move-object/16 v21, v20 + move-object/16 v22, v20 + move-object/16 v23, v20 + move-object/16 v24, v20 + move-object/16 v25, v20 + move-object/16 v26, v20 + move-object/16 v27, v20 + + # Introduce another copy that we will hold live. + move-object/16 v28, v20 + + # Clobber the original alias. + const v20, 0x5 + + # Introduce another copy that we'll use for unlock. + move-object/16 v29, v28 + + .catchall {:try_start_b .. :try_end_c} :catchall_15 + + .line 12 + :try_start_c + invoke-static/range { v28 }, LMain;->run(Ljava/lang/Object;)V + + .line 13 + monitor-exit v29 + :try_end_10 + .catchall {:try_start_c .. :try_end_10} :catchall_12 + + .line 14 + :try_start_10 + monitor-exit v19 + :try_end_11 + .catchall {:try_start_10 .. :try_end_11} :catchall_15 + + .line 15 + return-void + + .line 13 + :catchall_12 + move-exception v0 + + :try_start_13 + monitor-exit v29 + :try_end_14 + .catchall {:try_start_13 .. :try_end_14} :catchall_12 + + :try_start_14 + throw v0 + + .line 14 + :catchall_15 + move-exception v0 + + monitor-exit v19 + :try_end_17 + .catchall {:try_start_14 .. :try_end_17} :catchall_15 + + throw v0 +.end method diff --git a/test/167-visit-locks/src/Main.java b/test/167-visit-locks/src/Main.java new file mode 100644 index 0000000000..d8da92715a --- /dev/null +++ b/test/167-visit-locks/src/Main.java @@ -0,0 +1,29 @@ +/* + * 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]); + + Class.forName("TestSync").getMethod("run").invoke(null); + } + + public static void run(Object o) { + testVisitLocks(); + } + + public static native void testVisitLocks(); +} diff --git a/test/167-visit-locks/visit_locks.cc b/test/167-visit-locks/visit_locks.cc new file mode 100644 index 0000000000..e79c880639 --- /dev/null +++ b/test/167-visit-locks/visit_locks.cc @@ -0,0 +1,74 @@ +/* + * 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. + */ + +#include "jni.h" + +#include <iostream> + +#include "android-base/logging.h" + +#include "arch/context.h" +#include "art_method.h" +#include "base/macros.h" +#include "base/mutex.h" +#include "mirror/object-inl.h" +#include "mirror/string.h" +#include "monitor.h" +#include "scoped_thread_state_change-inl.h" +#include "stack.h" +#include "thread-current-inl.h" + +namespace art { + +extern "C" JNIEXPORT void JNICALL Java_Main_testVisitLocks(JNIEnv*, jclass) { + ScopedObjectAccess soa(Thread::Current()); + + class VisitLocks : public StackVisitor { + public: + VisitLocks(Thread* thread, Context* context) + : StackVisitor(thread, context, StackWalkKind::kIncludeInlinedFrames) { + } + + bool VisitFrame() OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* m = GetMethod(); + + // Ignore runtime methods. + if (m == nullptr || m->IsRuntimeMethod()) { + return true; + } + + if (m->PrettyMethod() == "void TestSync.run()") { + // Interesting frame. + Monitor::VisitLocks(this, Callback, nullptr); + return false; + } + + return true; + } + + static void Callback(mirror::Object* obj, void*) REQUIRES_SHARED(Locks::mutator_lock_) { + CHECK(obj != nullptr); + CHECK(obj->IsString()); + std::cerr << obj->AsString()->ToModifiedUtf8() << std::endl; + } + }; + Context* context = Context::Create(); + VisitLocks vl(soa.Self(), context); + vl.WalkStack(); + delete context; +} + +} // namespace art diff --git a/test/1940-ddms-ext/expected.txt b/test/1940-ddms-ext/expected.txt index cf4ad50e90..62d3b7bd4c 100644 --- a/test/1940-ddms-ext/expected.txt +++ b/test/1940-ddms-ext/expected.txt @@ -5,3 +5,6 @@ MyDdmHandler: Chunk returned: Chunk(Type: 0xFADE7357, Len: 8, data: [0, 0, 0, 0, JVMTI returned chunk: Chunk(Type: 0xFADE7357, Len: 8, data: [0, 0, 0, 0, 0, -128, 0, 37]) Sending chunk: Chunk(Type: 0xDEADBEEF, Len: 8, data: [9, 10, 11, 12, 13, 14, 15, 16]) Chunk published: Chunk(Type: 0xDEADBEEF, Len: 8, data: [9, 10, 11, 12, 13, 14, 15, 16]) +Saw expected thread events. +Expected chunk type published: 1213221190 +Expected chunk type published: 1297109829 diff --git a/test/1940-ddms-ext/src-art/art/Test1940.java b/test/1940-ddms-ext/src-art/art/Test1940.java index f0ee7102a9..9f79eaebba 100644 --- a/test/1940-ddms-ext/src-art/art/Test1940.java +++ b/test/1940-ddms-ext/src-art/art/Test1940.java @@ -17,6 +17,7 @@ package art; import org.apache.harmony.dalvik.ddmc.*; +import dalvik.system.VMDebug; import java.lang.reflect.Method; import java.util.Arrays; @@ -30,6 +31,12 @@ public class Test1940 { public static final int MY_DDMS_TYPE = 0xDEADBEEF; public static final int MY_DDMS_RESPONSE_TYPE = 0xFADE7357; + public static final boolean PRINT_ALL_CHUNKS = false; + + public static interface DdmHandler { + public void HandleChunk(int type, byte[] data); + } + public static final class TestError extends Error { public TestError(String s) { super(s); } } @@ -69,11 +76,38 @@ public class Test1940 { public static final ChunkHandler SINGLE_HANDLER = new MyDdmHandler(); + public static DdmHandler CURRENT_HANDLER; + public static void HandlePublish(int type, byte[] data) { - System.out.println("Chunk published: " + printChunk(new Chunk(type, data, 0, data.length))); + if (PRINT_ALL_CHUNKS) { + System.out.println( + "Unknown Chunk published: " + printChunk(new Chunk(type, data, 0, data.length))); + } + CURRENT_HANDLER.HandleChunk(type, data); + } + + // TYPE Thread Create + public static final int TYPE_THCR = 0x54484352; + // Type Thread name + public static final int TYPE_THNM = 0x54484E4D; + // Type Thread death. + public static final int TYPE_THDE = 0x54484445; + // Type Heap info + public static final int TYPE_HPIF = 0x48504946; + // Type Trace Results + public static final int TYPE_MPSE = 0x4D505345; + + public static boolean IsFromThread(Thread t, byte[] data) { + // DDMS always puts the thread-id as the first 4 bytes. + ByteBuffer b = ByteBuffer.wrap(data); + b.order(ByteOrder.BIG_ENDIAN); + return b.getInt() == (int) t.getId(); } public static void run() throws Exception { + CURRENT_HANDLER = (type, data) -> { + System.out.println("Chunk published: " + printChunk(new Chunk(type, data, 0, data.length))); + }; initializeTest( Test1940.class, Test1940.class.getDeclaredMethod("HandlePublish", Integer.TYPE, new byte[0].getClass())); @@ -90,8 +124,70 @@ public class Test1940 { MY_DDMS_TYPE, new byte[] { 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 }, 0, 8); System.out.println("Sending chunk: " + printChunk(c)); DdmServer.sendChunk(c); + + // Test thread chunks are sent. + final boolean[] types_seen = new boolean[] { false, false, false }; + CURRENT_HANDLER = (type, cdata) -> { + switch (type) { + case TYPE_THCR: + types_seen[0] = true; + break; + case TYPE_THNM: + types_seen[1] = true; + break; + case TYPE_THDE: + types_seen[2] = true; + break; + default: + // We don't want to print other types. + break; + } + }; + DdmVmInternal.threadNotify(true); + final Thread thr = new Thread(() -> { return; }, "THREAD"); + thr.start(); + thr.join(); + DdmVmInternal.threadNotify(false); + // Make sure we saw at least one of Thread-create, Thread name, & thread death. + if (!types_seen[0] || !types_seen[1] || !types_seen[2]) { + System.out.println("Didn't see expected chunks for thread creation! got: " + + Arrays.toString(types_seen)); + } else { + System.out.println("Saw expected thread events."); + } + + // Test heap chunks are sent. + CURRENT_HANDLER = (type, cdata) -> { + // The actual data is far to noisy for this test as it includes information about global heap + // state. + if (type == TYPE_HPIF) { + System.out.println("Expected chunk type published: " + type); + } + }; + final int HPIF_WHEN_NOW = 1; + if (!DdmVmInternal.heapInfoNotify(HPIF_WHEN_NOW)) { + System.out.println("Unexpected failure for heapInfoNotify!"); + } + + // method Tracing + CURRENT_HANDLER = (type, cdata) -> { + // This chunk includes timing and thread information so we just check the type. + if (type == TYPE_MPSE) { + System.out.println("Expected chunk type published: " + type); + } + }; + VMDebug.startMethodTracingDdms(/*size: default*/0, + /*flags: none*/ 0, + /*sampling*/ false, + /*interval*/ 0); + doNothing(); + doNothing(); + doNothing(); + doNothing(); + VMDebug.stopMethodTracing(); } + private static void doNothing() {} private static Chunk processChunk(byte[] val) { return processChunk(new Chunk(MY_DDMS_TYPE, val, 0, val.length)); } diff --git a/test/1941-dispose-stress/dispose_stress.cc b/test/1941-dispose-stress/dispose_stress.cc new file mode 100644 index 0000000000..e8fcc775e9 --- /dev/null +++ b/test/1941-dispose-stress/dispose_stress.cc @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#include <atomic> + +#include "android-base/logging.h" +#include "jni.h" +#include "scoped_local_ref.h" +#include "scoped_primitive_array.h" + +#include "jvmti.h" + +// Test infrastructure +#include "jvmti_helper.h" +#include "test_env.h" + +namespace art { +namespace Test1941DisposeStress { + +extern "C" JNIEXPORT jlong JNICALL Java_art_Test1941_AllocEnv(JNIEnv* env, jclass) { + JavaVM* vm = nullptr; + if (env->GetJavaVM(&vm) != 0) { + ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException")); + env->ThrowNew(rt_exception.get(), "Unable to get JavaVM"); + return -1; + } + jvmtiEnv* new_env = nullptr; + if (vm->GetEnv(reinterpret_cast<void**>(&new_env), JVMTI_VERSION_1_0) != 0) { + ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException")); + env->ThrowNew(rt_exception.get(), "Unable to create new jvmtiEnv"); + return -1; + } + return static_cast<jlong>(reinterpret_cast<intptr_t>(new_env)); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1941_FreeEnv(JNIEnv* env, + jclass, + jlong jvmti_env_ptr) { + JvmtiErrorToException(env, + jvmti_env, + reinterpret_cast<jvmtiEnv*>(jvmti_env_ptr)->DisposeEnvironment()); +} + +} // namespace Test1941DisposeStress +} // namespace art + diff --git a/test/1941-dispose-stress/expected.txt b/test/1941-dispose-stress/expected.txt new file mode 100644 index 0000000000..ca2eddc7b8 --- /dev/null +++ b/test/1941-dispose-stress/expected.txt @@ -0,0 +1 @@ +fib(20) is 6765 diff --git a/test/1941-dispose-stress/info.txt b/test/1941-dispose-stress/info.txt new file mode 100644 index 0000000000..e4a584e46f --- /dev/null +++ b/test/1941-dispose-stress/info.txt @@ -0,0 +1,3 @@ +Test basic JVMTI single step functionality. + +Ensures that we can receive single step events from JVMTI. diff --git a/test/1941-dispose-stress/run b/test/1941-dispose-stress/run new file mode 100755 index 0000000000..51875a7e86 --- /dev/null +++ b/test/1941-dispose-stress/run @@ -0,0 +1,18 @@ +#!/bin/bash +# +# 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. + +# Ask for stack traces to be dumped to a file rather than to stdout. +./default-run "$@" --jvmti diff --git a/test/1941-dispose-stress/src/Main.java b/test/1941-dispose-stress/src/Main.java new file mode 100644 index 0000000000..2fe6b818a0 --- /dev/null +++ b/test/1941-dispose-stress/src/Main.java @@ -0,0 +1,21 @@ +/* + * 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 { + art.Test1941.run(); + } +} diff --git a/test/1941-dispose-stress/src/art/Breakpoint.java b/test/1941-dispose-stress/src/art/Breakpoint.java new file mode 100644 index 0000000000..bbb89f707f --- /dev/null +++ b/test/1941-dispose-stress/src/art/Breakpoint.java @@ -0,0 +1,202 @@ +/* + * 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 art; + +import java.lang.reflect.Executable; +import java.util.HashSet; +import java.util.Set; +import java.util.Objects; + +public class Breakpoint { + public static class Manager { + public static class BP { + public final Executable method; + public final long location; + + public BP(Executable method) { + this(method, getStartLocation(method)); + } + + public BP(Executable method, long location) { + this.method = method; + this.location = location; + } + + @Override + public boolean equals(Object other) { + return (other instanceof BP) && + method.equals(((BP)other).method) && + location == ((BP)other).location; + } + + @Override + public String toString() { + return method.toString() + " @ " + getLine(); + } + + @Override + public int hashCode() { + return Objects.hash(method, location); + } + + public int getLine() { + try { + LineNumber[] lines = getLineNumberTable(method); + int best = -1; + for (LineNumber l : lines) { + if (l.location > location) { + break; + } else { + best = l.line; + } + } + return best; + } catch (Exception e) { + return -1; + } + } + } + + private Set<BP> breaks = new HashSet<>(); + + public void setBreakpoints(BP... bs) { + for (BP b : bs) { + if (breaks.add(b)) { + Breakpoint.setBreakpoint(b.method, b.location); + } + } + } + public void setBreakpoint(Executable method, long location) { + setBreakpoints(new BP(method, location)); + } + + public void clearBreakpoints(BP... bs) { + for (BP b : bs) { + if (breaks.remove(b)) { + Breakpoint.clearBreakpoint(b.method, b.location); + } + } + } + public void clearBreakpoint(Executable method, long location) { + clearBreakpoints(new BP(method, location)); + } + + public void clearAllBreakpoints() { + clearBreakpoints(breaks.toArray(new BP[0])); + } + } + + public static void startBreakpointWatch(Class<?> methodClass, + Executable breakpointReached, + Thread thr) { + startBreakpointWatch(methodClass, breakpointReached, false, thr); + } + + /** + * Enables the trapping of breakpoint events. + * + * If allowRecursive == true then breakpoints will be sent even if one is currently being handled. + */ + public static native void startBreakpointWatch(Class<?> methodClass, + Executable breakpointReached, + boolean allowRecursive, + Thread thr); + public static native void stopBreakpointWatch(Thread thr); + + public static final class LineNumber implements Comparable<LineNumber> { + public final long location; + public final int line; + + private LineNumber(long loc, int line) { + this.location = loc; + this.line = line; + } + + public boolean equals(Object other) { + return other instanceof LineNumber && ((LineNumber)other).line == line && + ((LineNumber)other).location == location; + } + + public int compareTo(LineNumber other) { + int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line)); + if (v != 0) { + return v; + } else { + return Long.valueOf(location).compareTo(Long.valueOf(other.location)); + } + } + } + + public static native void setBreakpoint(Executable m, long loc); + public static void setBreakpoint(Executable m, LineNumber l) { + setBreakpoint(m, l.location); + } + + public static native void clearBreakpoint(Executable m, long loc); + public static void clearBreakpoint(Executable m, LineNumber l) { + clearBreakpoint(m, l.location); + } + + private static native Object[] getLineNumberTableNative(Executable m); + public static LineNumber[] getLineNumberTable(Executable m) { + Object[] nativeTable = getLineNumberTableNative(m); + long[] location = (long[])(nativeTable[0]); + int[] lines = (int[])(nativeTable[1]); + if (lines.length != location.length) { + throw new Error("Lines and locations have different lengths!"); + } + LineNumber[] out = new LineNumber[lines.length]; + for (int i = 0; i < lines.length; i++) { + out[i] = new LineNumber(location[i], lines[i]); + } + return out; + } + + public static native long getStartLocation(Executable m); + + public static int locationToLine(Executable m, long location) { + try { + Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m); + int best = -1; + for (Breakpoint.LineNumber l : lines) { + if (l.location > location) { + break; + } else { + best = l.line; + } + } + return best; + } catch (Exception e) { + return -1; + } + } + + public static long lineToLocation(Executable m, int line) throws Exception { + try { + Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m); + for (Breakpoint.LineNumber l : lines) { + if (l.line == line) { + return l.location; + } + } + throw new Exception("Unable to find line " + line + " in " + m); + } catch (Exception e) { + throw new Exception("Unable to get line number info for " + m, e); + } + } +} + diff --git a/test/1941-dispose-stress/src/art/Test1941.java b/test/1941-dispose-stress/src/art/Test1941.java new file mode 100644 index 0000000000..d5a9de6cab --- /dev/null +++ b/test/1941-dispose-stress/src/art/Test1941.java @@ -0,0 +1,72 @@ +/* + * 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 art; + +import java.util.Arrays; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; + +public class Test1941 { + public static final boolean PRINT_CNT = false; + public static long CNT = 0; + + // Method with multiple paths we can break on. + public static long fib(long f) { + if (f < 0) { + throw new IllegalArgumentException("Bad argument f < 0: f = " + f); + } else if (f == 0) { + return 0; + } else if (f == 1) { + return 1; + } else { + return fib(f - 1) + fib(f - 2); + } + } + + public static void notifySingleStep(Thread thr, Executable e, long loc) { + // Don't bother actually doing anything. + } + + public static void LoopAllocFreeEnv() { + while (!Thread.interrupted()) { + CNT++; + long env = AllocEnv(); + FreeEnv(env); + } + } + + public static native long AllocEnv(); + public static native void FreeEnv(long env); + + public static void run() throws Exception { + Thread thr = new Thread(Test1941::LoopAllocFreeEnv, "LoopNative"); + thr.start(); + Trace.enableSingleStepTracing(Test1941.class, + Test1941.class.getDeclaredMethod( + "notifySingleStep", Thread.class, Executable.class, Long.TYPE), + null); + + System.out.println("fib(20) is " + fib(20)); + + thr.interrupt(); + thr.join(); + Trace.disableTracing(null); + if (PRINT_CNT) { + System.out.println("Number of envs created/destroyed: " + CNT); + } + } +} diff --git a/test/1941-dispose-stress/src/art/Trace.java b/test/1941-dispose-stress/src/art/Trace.java new file mode 100644 index 0000000000..8999bb1368 --- /dev/null +++ b/test/1941-dispose-stress/src/art/Trace.java @@ -0,0 +1,68 @@ +/* + * 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 art; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +public class Trace { + public static native void enableTracing(Class<?> methodClass, + Method entryMethod, + Method exitMethod, + Method fieldAccess, + Method fieldModify, + Method singleStep, + Thread thr); + public static native void disableTracing(Thread thr); + + public static void enableFieldTracing(Class<?> methodClass, + Method fieldAccess, + Method fieldModify, + Thread thr) { + enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr); + } + + public static void enableMethodTracing(Class<?> methodClass, + Method entryMethod, + Method exitMethod, + Thread thr) { + enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr); + } + + public static void enableSingleStepTracing(Class<?> methodClass, + Method singleStep, + Thread thr) { + enableTracing(methodClass, null, null, null, null, singleStep, thr); + } + + public static native void watchFieldAccess(Field f); + public static native void watchFieldModification(Field f); + public static native void watchAllFieldAccesses(); + public static native void watchAllFieldModifications(); + + // the names, arguments, and even line numbers of these functions are embedded in the tests so we + // need to add to the bottom and not modify old ones to maintain compat. + public static native void enableTracing2(Class<?> methodClass, + Method entryMethod, + Method exitMethod, + Method fieldAccess, + Method fieldModify, + Method singleStep, + Method ThreadStart, + Method ThreadEnd, + Thread thr); +} 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..f235c6bc90 --- /dev/null +++ b/test/667-jit-jni-stub/run @@ -0,0 +1,19 @@ +#!/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. +# Ensure this test is not subject to unexpected code collection. +${RUN} "${@}" --no-prebuild --no-dex2oat --runtime-option -Xjitinitialsize:32M 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/669-checker-break/expected.txt b/test/669-checker-break/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/669-checker-break/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/669-checker-break/info.txt b/test/669-checker-break/info.txt new file mode 100644 index 0000000000..3408b3b238 --- /dev/null +++ b/test/669-checker-break/info.txt @@ -0,0 +1 @@ +Test optimizations of "break" loops. diff --git a/test/669-checker-break/src/Main.java b/test/669-checker-break/src/Main.java new file mode 100644 index 0000000000..e59061b1aa --- /dev/null +++ b/test/669-checker-break/src/Main.java @@ -0,0 +1,328 @@ +/* + * 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. + */ + +/** + * Tests for optimizations of break-loops, i.e. loops that break + * out of a while-true loop when the end condition is satisfied. + * In particular, the tests focus on break-loops that can be + * rewritten into regular countable loops (this may improve certain + * loops generated by the Kotlin compiler for inclusive ranges). + */ +public class Main { + + /// CHECK-START: int Main.breakLoop(int[]) induction_var_analysis (before) + /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Nil:l\d+>> NullCheck [<<Par>>] loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Zero>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Bnd:i\d+>> BoundsCheck [<<Phi>>,{{i\d+}}] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [<<Nil>>,<<Bnd>>,<<One>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<NE:z\d+>> NotEqual [{{i\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<NE>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<One>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-START: int Main.breakLoop(int[]) induction_var_analysis (after) + /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Nil:l\d+>> NullCheck [<<Par>>] loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Zero>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<LE:z\d+>> LessThanOrEqual [<<Phi>>,{{i\d+}}] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<LE>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Bnd:i\d+>> BoundsCheck [<<Phi>>,{{i\d+}}] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [<<Nil>>,<<Bnd>>,<<One>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<One>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-START-ARM64: int Main.breakLoop(int[]) loop_optimization (after) + /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none + /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Four:i\d+>> IntConstant 4 loop:none + /// CHECK-DAG: <<Nil:l\d+>> NullCheck [<<Par>>] loop:none + /// CHECK-DAG: <<Rep:d\d+>> VecReplicateScalar [<<One>>] loop:none + /// CHECK-DAG: VecStore [<<Nil>>,<<Phi:i\d+>>,<<Rep>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: Add [<<Phi>>,<<Four>>] loop:<<Loop>> outer_loop:none + static int breakLoop(int[] a) { + int l = 0; + int u = a.length - 1; + int i = l; + if (l <= u) { + while (true) { + a[i] = 1; + if (i == u) break; + i++; + } + } + return i; + } + + /// CHECK-START: int Main.breakLoopDown(int[]) induction_var_analysis (before) + /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<MOne:i\d+>> IntConstant -1 loop:none + /// CHECK-DAG: <<Two:i\d+>> IntConstant 2 loop:none + /// CHECK-DAG: <<Nil:l\d+>> NullCheck [<<Par>>] loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi [{{i\d+}},<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Bnd:i\d+>> BoundsCheck [<<Phi>>,{{i\d+}}] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [<<Nil>>,<<Bnd>>,<<Two>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<NE:z\d+>> NotEqual [<<Phi>>,<<Zero>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<NE>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<MOne>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-START: int Main.breakLoopDown(int[]) induction_var_analysis (after) + /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<MOne:i\d+>> IntConstant -1 loop:none + /// CHECK-DAG: <<Two:i\d+>> IntConstant 2 loop:none + /// CHECK-DAG: <<Nil:l\d+>> NullCheck [<<Par>>] loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi [{{i\d+}},<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<GE:z\d+>> GreaterThanOrEqual [<<Phi>>,<<Zero>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<GE>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Bnd:i\d+>> BoundsCheck [<<Phi>>,{{i\d+}}] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [<<Nil>>,<<Bnd>>,<<Two>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<MOne>>] loop:<<Loop>> outer_loop:none + static int breakLoopDown(int[] a) { + int l = 0; + int u = a.length - 1; + int i = u; + if (u >= l) { + while (true) { + a[i] = 2; + if (i == l) break; + i--; + } + } + return i; + } + + /// CHECK-START: int Main.breakLoopSafeConst(int[]) induction_var_analysis (before) + /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none + /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Three:i\d+>> IntConstant 3 loop:none + /// CHECK-DAG: <<L1:i\d+>> IntConstant 2147483631 loop:none + /// CHECK-DAG: <<L2:i\d+>> IntConstant 2147483646 loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi [<<L1>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Sub:i\d+>> Sub [<<Phi>>,<<L1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Nil:l\d+>> NullCheck [<<Par>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Bnd:i\d+>> BoundsCheck [<<Sub>>,{{i\d+}}] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [<<Nil>>,<<Bnd>>,<<Three>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<NE:z\d+>> NotEqual [<<Phi>>,<<L2>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<NE>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<One>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-START: int Main.breakLoopSafeConst(int[]) induction_var_analysis (after) + /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none + /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Three:i\d+>> IntConstant 3 loop:none + /// CHECK-DAG: <<L1:i\d+>> IntConstant 2147483631 loop:none + /// CHECK-DAG: <<L2:i\d+>> IntConstant 2147483646 loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi [<<L1>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<LE:z\d+>> LessThanOrEqual [<<Phi>>,<<L2>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Sub:i\d+>> Sub [<<Phi>>,<<L1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Nil:l\d+>> NullCheck [<<Par>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Bnd:i\d+>> BoundsCheck [<<Sub>>,{{i\d+}}] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [<<Nil>>,<<Bnd>>,<<Three>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<One>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-START-ARM64: int Main.breakLoopSafeConst(int[]) loop_optimization (after) + /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none + /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Three:i\d+>> IntConstant 3 loop:none + /// CHECK-DAG: <<Four:i\d+>> IntConstant 4 loop:none + /// CHECK-DAG: <<Rep:d\d+>> VecReplicateScalar [<<Three>>] loop:none + /// CHECK-DAG: VecStore [<<Par>>,<<Phi:i\d+>>,<<Rep>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: Add [<<Phi>>,<<Four>>] loop:<<Loop>> outer_loop:none + static int breakLoopSafeConst(int[] a) { + int l = Integer.MAX_VALUE - 16; + int u = Integer.MAX_VALUE - 1; + int i = l; + if (l <= u) { // will be removed by simplifier + while (true) { + a[i - l] = 3; + if (i == u) break; + i++; + } + } + return i; + } + + /// CHECK-START: int Main.breakLoopUnsafeConst(int[]) induction_var_analysis (before) + /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none + /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Four:i\d+>> IntConstant 4 loop:none + /// CHECK-DAG: <<L1:i\d+>> IntConstant 2147483632 loop:none + /// CHECK-DAG: <<L2:i\d+>> IntConstant 2147483647 loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi [<<L1>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Sub:i\d+>> Sub [<<Phi>>,<<L1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Nil:l\d+>> NullCheck [<<Par>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Bnd:i\d+>> BoundsCheck [<<Sub>>,{{i\d+}}] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [<<Nil>>,<<Bnd>>,<<Four>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<NE:z\d+>> NotEqual [<<Phi>>,<<L2>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<NE>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<One>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-START: int Main.breakLoopUnsafeConst(int[]) induction_var_analysis (after) + /// CHECK-DAG: NotEqual + /// CHECK-NOT: LessThanOrEqual + static int breakLoopUnsafeConst(int[] a) { + int l = Integer.MAX_VALUE - 15; + int u = Integer.MAX_VALUE; + int i = l; + if (l <= u) { // will be removed by simplifier + while (true) { + a[i - l] = 4; + if (i == u) break; // rewriting exit not safe! + i++; + } + } + return i; + } + + /// CHECK-START: int Main.breakLoopNastyPhi(int[]) induction_var_analysis (before) + /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Five:i\d+>> IntConstant 5 loop:none + /// CHECK-DAG: <<M123:i\d+>> IntConstant -123 loop:none + /// CHECK-DAG: <<Nil:l\d+>> NullCheck [<<Par>>] loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Zero>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Wrap:i\d+>> Phi [<<M123>>,<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Bnd:i\d+>> BoundsCheck [<<Phi>>,{{i\d+}}] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [<<Nil>>,<<Bnd>>,<<Five>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<NE:z\d+>> NotEqual [{{i\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<NE>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<One>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Comb:i\d+>> Phi [<<M123>>,<<Wrap>>] loop:none + /// CHECK-DAG: Return [<<Comb>>] loop:none + // + /// CHECK-START: int Main.breakLoopNastyPhi(int[]) induction_var_analysis (after) + /// CHECK-DAG: NotEqual + /// CHECK-NOT: LessThanOrEqual + static int breakLoopNastyPhi(int[] a) { + int l = 0; + int u = a.length - 1; + int x = -123; + if (l <= u) { + int i = l; + while (true) { + a[i] = 5; + if (i == u) break; + x = i; + i++; + } + } + return x; // keep another phi live + } + + /// CHECK-START: int Main.breakLoopReduction(int[]) induction_var_analysis (before) + /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Nil:l\d+>> NullCheck [<<Par>>] loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Zero>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Red:i\d+>> Phi [<<Zero>>,<<RedI:i\d+>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Bnd:i\d+>> BoundsCheck [<<Phi>>,{{i\d+}}] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Get:i\d+>> ArrayGet [<<Nil>>,<<Bnd>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<RedI>> Add [<<Red>>,<<Get>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<NE:z\d+>> NotEqual [{{i\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<NE>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<One>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Comb:i\d+>> Phi [<<Zero>>,<<RedI>>] loop:none + /// CHECK-DAG: Return [<<Comb>>] loop:none + // + /// CHECK-START: int Main.breakLoopReduction(int[]) induction_var_analysis (after) + /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Nil:l\d+>> NullCheck [<<Par>>] loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Zero>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Red:i\d+>> Phi [<<Zero>>,<<RedI:i\d+>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<LE:z\d+>> LessThanOrEqual [<<Phi>>,{{i\d+}}] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<LE>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Bnd:i\d+>> BoundsCheck [<<Phi>>,{{i\d+}}] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Get:i\d+>> ArrayGet [<<Nil>>,<<Bnd>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<RedI>> Add [<<Red>>,<<Get>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<One>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Comb:i\d+>> Phi [<<Zero>>,<<Red>>] loop:none + /// CHECK-DAG: Return [<<Comb>>] loop:none + // + /// CHECK-START-ARM64: int Main.breakLoopReduction(int[]) loop_optimization (after) + /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Exp:d\d+>> VecSetScalars [<<Zero>>] loop:none + /// CHECK-DAG: <<VPhi:d\d+>> Phi [<<Exp>>,<<VAdd:d\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<VLoad:d\d+>> VecLoad loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<VAdd>> VecAdd [<<VPhi>>,<<VLoad>>] loop:<<Loop>> outer_loop:none + static int breakLoopReduction(int[] a) { + int l = 0; + int u = a.length - 1; + int x = 0; + if (l <= u) { + int i = l; + while (true) { + x += a[i]; + if (i == u) break; + i++; + } + } + return x; + } + + // + // Test driver. + // + + public static void main(String[] args) { + int[] a = new int[100]; + + expectEquals(99, breakLoop(a)); + for (int i = 0; i < a.length; i++) { + expectEquals(1, a[i]); + } + + expectEquals(0, breakLoopDown(a)); + for (int i = 0; i < a.length; i++) { + expectEquals(2, a[i]); + } + + expectEquals(Integer.MAX_VALUE - 1, breakLoopSafeConst(a)); + for (int i = 0; i < a.length; i++) { + int e = i < 16 ? 3 : 2; + expectEquals(e, a[i]); + } + + expectEquals(Integer.MAX_VALUE, breakLoopUnsafeConst(a)); + for (int i = 0; i < a.length; i++) { + int e = i < 16 ? 4 : 2; + expectEquals(e, a[i]); + } + + expectEquals(98, breakLoopNastyPhi(a)); + for (int i = 0; i < a.length; i++) { + expectEquals(5, a[i]); + } + + expectEquals(500, breakLoopReduction(a)); + + System.out.println("passed"); + } + + private static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } +} diff --git a/test/711-checker-type-conversion/expected.txt b/test/711-checker-type-conversion/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/711-checker-type-conversion/expected.txt diff --git a/test/711-checker-type-conversion/info.txt b/test/711-checker-type-conversion/info.txt new file mode 100644 index 0000000000..5b63572a76 --- /dev/null +++ b/test/711-checker-type-conversion/info.txt @@ -0,0 +1 @@ +Tests for type conversion elimination. diff --git a/test/711-checker-type-conversion/src/Main.java b/test/711-checker-type-conversion/src/Main.java new file mode 100644 index 0000000000..2c9c3a157e --- /dev/null +++ b/test/711-checker-type-conversion/src/Main.java @@ -0,0 +1,77 @@ +/* + * 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 assertByteEquals(byte expected, byte result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + /// CHECK-START: byte Main.getByte1() constant_folding (before) + /// CHECK: TypeConversion + /// CHECK: TypeConversion + /// CHECK: Add + /// CHECK: TypeConversion + + /// CHECK-START: byte Main.getByte1() constant_folding (after) + /// CHECK-NOT: TypeConversion + /// CHECK-NOT: Add + + static byte getByte1() { + int i = -2; + int j = -3; + return (byte)((byte)i + (byte)j); + } + + /// CHECK-START: byte Main.getByte2() constant_folding (before) + /// CHECK: TypeConversion + /// CHECK: TypeConversion + /// CHECK: Add + /// CHECK: TypeConversion + + /// CHECK-START: byte Main.getByte2() constant_folding (after) + /// CHECK-NOT: TypeConversion + /// CHECK-NOT: Add + + static byte getByte2() { + int i = -100; + int j = -101; + return (byte)((byte)i + (byte)j); + } + + /// CHECK-START: byte Main.getByte3() constant_folding (before) + /// CHECK: TypeConversion + /// CHECK: TypeConversion + /// CHECK: Add + /// CHECK: TypeConversion + + /// CHECK-START: byte Main.getByte2() constant_folding (after) + /// CHECK-NOT: TypeConversion + /// CHECK-NOT: Add + + static byte getByte3() { + long i = 0xabcdabcdabcdL; + return (byte)((byte)i + (byte)i); + } + + public static void main(String[] args) { + assertByteEquals(getByte1(), (byte)-5); + assertByteEquals(getByte2(), (byte)(-201)); + assertByteEquals(getByte3(), (byte)(0xcd + 0xcd)); + } +} diff --git a/test/Android.bp b/test/Android.bp index ba24119e9c..01e424d5e3 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -259,6 +259,7 @@ art_cc_defaults { "1932-monitor-events-misc/monitor_misc.cc", "1934-jvmti-signal-thread/signal_threads.cc", "1939-proxy-frames/local_instance.cc", + "1941-dispose-stress/dispose_stress.cc", ], shared_libs: [ "libbase", @@ -363,8 +364,9 @@ cc_defaults { "141-class-unload/jni_unload.cc", "148-multithread-gc-annotations/gc_coverage.cc", "149-suspend-all-stress/suspend_all.cc", - "203-multi-checkpoint/multi_checkpoint.cc", "154-gc-loop/heap_interface.cc", + "167-visit-locks/visit_locks.cc", + "203-multi-checkpoint/multi_checkpoint.cc", "454-get-vreg/get_vreg_jni.cc", "457-regs/regs_jni.cc", "461-get-reference-vreg/get_reference_vreg_jni.cc", @@ -383,6 +385,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, diff --git a/test/knownfailures.json b/test/knownfailures.json index c6b6574f1b..5b2ebf58a4 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -202,6 +202,12 @@ "bug": "http://b/34369284" }, { + "tests": "1940-ddms-ext", + "description": ["Test expects to be able to start tracing but we cannot", + "do that if tracing is already ongoing."], + "variant": "trace | stream" + }, + { "tests": "137-cfi", "description": ["This test unrolls and expects managed frames, but", "tracing means we run the interpreter."], diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index e7503827f2..554b8a5429 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -874,7 +874,7 @@ def parse_option(): global run_all_configs parser = argparse.ArgumentParser(description="Runs all or a subset of the ART test suite.") - parser.add_argument('-t', '--test', dest='test', help='name of the test') + parser.add_argument('-t', '--test', action='append', dest='tests', help='name(s) of the test(s)') parser.add_argument('-j', type=int, dest='n_thread') parser.add_argument('--timeout', default=timeout, type=int, dest='timeout') for variant in TOTAL_VARIANTS_SET: @@ -906,10 +906,12 @@ def parse_option(): options = setup_env_for_build_target(target_config[options['build_target']], parser, options) - test = '' + tests = None env.EXTRA_DISABLED_TESTS.update(set(options['skips'])) - if options['test']: - test = parse_test_name(options['test']) + if options['tests']: + tests = set() + for test_name in options['tests']: + tests |= parse_test_name(test_name) for variant_type in VARIANT_TYPE_DICT: for variant in VARIANT_TYPE_DICT[variant_type]: @@ -935,11 +937,11 @@ def parse_option(): if options['run_all']: run_all_configs = True - return test + return tests def main(): gather_test_info() - user_requested_test = parse_option() + user_requested_tests = parse_option() setup_test_env() if build: build_targets = '' @@ -956,8 +958,8 @@ def main(): build_command += ' dist' if subprocess.call(build_command.split()): sys.exit(1) - if user_requested_test: - test_runner_thread = threading.Thread(target=run_tests, args=(user_requested_test,)) + if user_requested_tests: + test_runner_thread = threading.Thread(target=run_tests, args=(user_requested_tests,)) else: test_runner_thread = threading.Thread(target=run_tests, args=(RUN_TEST_SET,)) test_runner_thread.daemon = True |