diff options
Diffstat (limited to 'test')
49 files changed, 1210 insertions, 70 deletions
diff --git a/test/004-SignalTest/expected.txt b/test/004-SignalTest/expected.txt index b3a0e1cbe0..847b56f823 100644 --- a/test/004-SignalTest/expected.txt +++ b/test/004-SignalTest/expected.txt @@ -3,4 +3,8 @@ init signal test Caught NullPointerException Caught StackOverflowError signal caught +unblocked signal received +unblocking blocked signal +blocked signal received +signal handler done Signal test OK diff --git a/test/004-SignalTest/signaltest.cc b/test/004-SignalTest/signaltest.cc index 6dd63551fd..a58a0752c9 100644 --- a/test/004-SignalTest/signaltest.cc +++ b/test/004-SignalTest/signaltest.cc @@ -18,13 +18,14 @@ #include <signal.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <sys/ucontext.h> #include <unistd.h> #include "base/macros.h" static int signal_count; -static const int kMaxSignal = 2; +static const int kMaxSignal = 1; #if defined(__i386__) || defined(__x86_64__) #if defined(__APPLE__) @@ -47,6 +48,17 @@ static const int kMaxSignal = 2; #endif #endif +#define BLOCKED_SIGNAL SIGUSR1 +#define UNBLOCKED_SIGNAL SIGUSR2 + +static void blocked_signal(int sig ATTRIBUTE_UNUSED) { + printf("blocked signal received\n"); +} + +static void unblocked_signal(int sig ATTRIBUTE_UNUSED) { + printf("unblocked signal received\n"); +} + static void signalhandler(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED, void* context) { printf("signal caught\n"); @@ -54,6 +66,16 @@ static void signalhandler(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UN if (signal_count > kMaxSignal) { abort(); } + + raise(UNBLOCKED_SIGNAL); + raise(BLOCKED_SIGNAL); + printf("unblocking blocked signal\n"); + + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, BLOCKED_SIGNAL); + sigprocmask(SIG_UNBLOCK, &mask, nullptr); + #if defined(__arm__) struct ucontext *uc = reinterpret_cast<struct ucontext*>(context); struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext); @@ -71,6 +93,8 @@ static void signalhandler(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UN #else UNUSED(context); #endif + + printf("signal handler done\n"); } static struct sigaction oldaction; @@ -78,13 +102,21 @@ static struct sigaction oldaction; extern "C" JNIEXPORT void JNICALL Java_Main_initSignalTest(JNIEnv*, jclass) { struct sigaction action; action.sa_sigaction = signalhandler; - sigemptyset(&action.sa_mask); + sigfillset(&action.sa_mask); + sigdelset(&action.sa_mask, UNBLOCKED_SIGNAL); action.sa_flags = SA_SIGINFO | SA_ONSTACK; #if !defined(__APPLE__) && !defined(__mips__) action.sa_restorer = nullptr; #endif sigaction(SIGSEGV, &action, &oldaction); + struct sigaction check; + sigaction(SIGSEGV, nullptr, &check); + if (memcmp(&action, &check, sizeof(action)) != 0) { + printf("sigaction returned different value\n"); + } + signal(BLOCKED_SIGNAL, blocked_signal); + signal(UNBLOCKED_SIGNAL, unblocked_signal); } extern "C" JNIEXPORT void JNICALL Java_Main_terminateSignalTest(JNIEnv*, jclass) { @@ -96,6 +128,12 @@ extern "C" JNIEXPORT void JNICALL Java_Main_terminateSignalTest(JNIEnv*, jclass) char *go_away_compiler = nullptr; extern "C" JNIEXPORT jint JNICALL Java_Main_testSignal(JNIEnv*, jclass) { + // Unblock UNBLOCKED_SIGNAL. + sigset_t mask; + memset(&mask, 0, sizeof(mask)); + sigaddset(&mask, UNBLOCKED_SIGNAL); + sigprocmask(SIG_UNBLOCK, &mask, nullptr); + #if defined(__arm__) || defined(__i386__) || defined(__aarch64__) // On supported architectures we cause a real SEGV. *go_away_compiler = 'a'; diff --git a/test/115-native-bridge/nativebridge.cc b/test/115-native-bridge/nativebridge.cc index 41329af138..f913cf69d0 100644 --- a/test/115-native-bridge/nativebridge.cc +++ b/test/115-native-bridge/nativebridge.cc @@ -395,20 +395,6 @@ extern "C" bool native_bridge_isCompatibleWith(uint32_t bridge_version ATTRIBUTE #endif #endif -static bool cannot_be_blocked(int signum) { - // These two sigs cannot be blocked anywhere. - if ((signum == SIGKILL) || (signum == SIGSTOP)) { - return true; - } - - // The invalid rt_sig cannot be blocked. - if (((signum >= 32) && (signum < SIGRTMIN)) || (signum > SIGRTMAX)) { - return true; - } - - return false; -} - // A dummy special handler, continueing after the faulting location. This code comes from // 004-SignalTest. static bool nb_signalhandler(int sig, siginfo_t* info ATTRIBUTE_UNUSED, void* context) { @@ -433,22 +419,6 @@ static bool nb_signalhandler(int sig, siginfo_t* info ATTRIBUTE_UNUSED, void* co #endif } - // Before invoking this handler, all other unclaimed signals must be blocked. - // We're trying to check the signal mask to verify its status here. - sigset_t tmpset; - sigemptyset(&tmpset); - sigprocmask(SIG_SETMASK, nullptr, &tmpset); - int other_claimed = (sig == SIGSEGV) ? SIGILL : SIGSEGV; - for (int signum = 0; signum < NSIG; ++signum) { - if (cannot_be_blocked(signum)) { - continue; - } else if ((sigismember(&tmpset, signum)) && (signum == other_claimed)) { - printf("ERROR: The claimed signal %d is blocked\n", signum); - } else if ((!sigismember(&tmpset, signum)) && (signum != other_claimed)) { - printf("ERROR: The unclaimed signal %d is not blocked\n", signum); - } - } - // We handled this... return true; } diff --git a/test/616-cha-abstract/src/Main.java b/test/616-cha-abstract/src/Main.java index e1d7db170d..b33f575dec 100644 --- a/test/616-cha-abstract/src/Main.java +++ b/test/616-cha-abstract/src/Main.java @@ -39,8 +39,8 @@ class Main2 extends Main1 { } public class Main { - static Main1 sMain1; - static Main1 sMain2; + static Base sMain1; + static Base sMain2; static boolean sIsOptimizing = true; static boolean sHasJIT = true; diff --git a/test/616-cha-interface-default/expected.txt b/test/616-cha-interface-default/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/616-cha-interface-default/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/616-cha-interface-default/info.txt b/test/616-cha-interface-default/info.txt new file mode 100644 index 0000000000..11baa1f0f2 --- /dev/null +++ b/test/616-cha-interface-default/info.txt @@ -0,0 +1,2 @@ +Test for Class Hierarchy Analysis (CHA) on interface method. +Test it under multidex configuration to check cross-dex inlining. diff --git a/test/616-cha-interface-default/multidex.jpp b/test/616-cha-interface-default/multidex.jpp new file mode 100644 index 0000000000..b0d200ea38 --- /dev/null +++ b/test/616-cha-interface-default/multidex.jpp @@ -0,0 +1,3 @@ +Main: + @@com.android.jack.annotations.ForceInMainDex + class Main diff --git a/test/616-cha-interface-default/run b/test/616-cha-interface-default/run new file mode 100644 index 0000000000..d8b4f0d26c --- /dev/null +++ b/test/616-cha-interface-default/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. + +# Run without an app image to prevent the classes to be loaded at startup. +exec ${RUN} "${@}" --no-app-image diff --git a/test/616-cha-interface-default/src-multidex/Base.java b/test/616-cha-interface-default/src-multidex/Base.java new file mode 100644 index 0000000000..2cbcb500c4 --- /dev/null +++ b/test/616-cha-interface-default/src-multidex/Base.java @@ -0,0 +1,41 @@ +/* + * 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 Base { + default public int foo(int i) { + if (i != 1) { + return -2; + } + return i + 10; + } + + // Test default method that's not inlined. + default public int $noinline$bar() { + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + return -1; + } + + default void printError(String msg) { + System.out.println(msg); + } +} diff --git a/test/616-cha-interface-default/src/Main.java b/test/616-cha-interface-default/src/Main.java new file mode 100644 index 0000000000..951607d2cf --- /dev/null +++ b/test/616-cha-interface-default/src/Main.java @@ -0,0 +1,176 @@ +/* + * 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 Main1 implements Base { +} + +class Main2 extends Main1 { + public void foobar() {} +} + +class Main3 implements Base { + public int foo(int i) { + if (i != 3) { + printError("error3"); + } + return -(i + 10); + } +} + +public class Main { + static Base sMain1; + static Base sMain2; + static Base sMain3; + + static boolean sIsOptimizing = true; + static boolean sHasJIT = true; + static volatile boolean sOtherThreadStarted; + + private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) { + if (hasSingleImplementation(clazz, method_name) != b) { + System.out.println(clazz + "." + method_name + + " doesn't have single implementation value of " + b); + } + } + + static int getValue(Class<?> cls) { + if (cls == Main1.class || cls == Main2.class) { + return 1; + } + return 3; + } + + // sMain1.foo()/sMain2.foo() will be always be Base.foo() before Main3 is loaded/linked. + // So sMain1.foo() can be devirtualized to Base.foo() and be inlined. + // After Dummy.createMain3() which links in Main3, live testImplement() on stack + // should be deoptimized. + static void testImplement(boolean createMain3, boolean wait, boolean setHasJIT) { + if (setHasJIT) { + if (isInterpreted()) { + sHasJIT = false; + } + return; + } + + if (createMain3 && (sIsOptimizing || sHasJIT)) { + assertIsManaged(); + } + + if (sMain1.foo(getValue(sMain1.getClass())) != 11) { + System.out.println("11 expected."); + } + if (sMain1.$noinline$bar() != -1) { + System.out.println("-1 expected."); + } + if (sMain2.foo(getValue(sMain2.getClass())) != 11) { + System.out.println("11 expected."); + } + + if (createMain3) { + // Wait for the other thread to start. + while (!sOtherThreadStarted); + // Create an Main2 instance and assign it to sMain2. + // sMain1 is kept the same. + sMain3 = Dummy.createMain3(); + // Wake up the other thread. + synchronized(Main.class) { + Main.class.notify(); + } + } else if (wait) { + // This is the other thread. + synchronized(Main.class) { + sOtherThreadStarted = true; + // Wait for Main2 to be linked and deoptimization is triggered. + try { + Main.class.wait(); + } catch (Exception e) { + } + } + } + + // There should be a deoptimization here right after Main3 is linked by + // calling Dummy.createMain3(), even though sMain1 didn't change. + // The behavior here would be different if inline-cache is used, which + // doesn't deoptimize since sMain1 still hits the type cache. + if (sMain1.foo(getValue(sMain1.getClass())) != 11) { + System.out.println("11 expected."); + } + if ((createMain3 || wait) && sHasJIT && !sIsOptimizing) { + // This method should be deoptimized right after Main3 is created. + assertIsInterpreted(); + } + + if (sMain3 != null) { + if (sMain3.foo(getValue(sMain3.getClass())) != -13) { + System.out.println("-13 expected."); + } + } + } + + // Test scenarios under which CHA-based devirtualization happens, + // and class loading that implements a method can invalidate compiled code. + public static void main(String[] args) { + System.loadLibrary(args[0]); + + if (isInterpreted()) { + sIsOptimizing = false; + } + + // sMain1 is an instance of Main1. + // sMain2 is an instance of Main2. + // Neither Main1 nor Main2 override default method Base.foo(). + // Main3 hasn't bee loaded yet. + sMain1 = new Main1(); + sMain2 = new Main2(); + + ensureJitCompiled(Main.class, "testImplement"); + testImplement(false, false, true); + + if (sHasJIT && !sIsOptimizing) { + assertSingleImplementation(Base.class, "foo", true); + assertSingleImplementation(Main1.class, "foo", true); + } else { + // Main3 is verified ahead-of-time so it's linked in already. + } + + // Create another thread that also calls sMain1.foo(). + // Try to test suspend and deopt another thread. + new Thread() { + public void run() { + testImplement(false, true, false); + } + }.start(); + + // This will create Main3 instance in the middle of testImplement(). + testImplement(true, false, false); + assertSingleImplementation(Base.class, "foo", false); + assertSingleImplementation(Main1.class, "foo", true); + assertSingleImplementation(sMain3.getClass(), "foo", true); + } + + private static native void ensureJitCompiled(Class<?> itf, String method_name); + private static native void assertIsInterpreted(); + private static native void assertIsManaged(); + private static native boolean isInterpreted(); + private static native boolean hasSingleImplementation(Class<?> clazz, String method_name); +} + +// Put createMain3() in another class to avoid class loading due to verifier. +class Dummy { + static Base createMain3() { + return new Main3(); + } +} diff --git a/test/616-cha-interface/expected.txt b/test/616-cha-interface/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/616-cha-interface/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/616-cha-interface/info.txt b/test/616-cha-interface/info.txt new file mode 100644 index 0000000000..1fd330afd4 --- /dev/null +++ b/test/616-cha-interface/info.txt @@ -0,0 +1 @@ +Test for Class Hierarchy Analysis (CHA) on interface method. diff --git a/test/616-cha-interface/run b/test/616-cha-interface/run new file mode 100644 index 0000000000..d8b4f0d26c --- /dev/null +++ b/test/616-cha-interface/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. + +# Run without an app image to prevent the classes to be loaded at startup. +exec ${RUN} "${@}" --no-app-image diff --git a/test/616-cha-interface/src/Main.java b/test/616-cha-interface/src/Main.java new file mode 100644 index 0000000000..3c9349663d --- /dev/null +++ b/test/616-cha-interface/src/Main.java @@ -0,0 +1,173 @@ +/* + * 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 Base { + void foo(int i); + void $noinline$bar(); +} + +class Main1 implements Base { + public void foo(int i) { + if (i != 1) { + printError("error1"); + } + } + + // Test rewriting invoke-interface into invoke-virtual when inlining fails. + public void $noinline$bar() { + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + } + + void printError(String msg) { + System.out.println(msg); + } +} + +class Main2 extends Main1 { + public void foo(int i) { + if (i != 2) { + printError("error2"); + } + } +} + +public class Main { + static Base sMain1; + static Base sMain2; + + static boolean sIsOptimizing = true; + static boolean sHasJIT = true; + static volatile boolean sOtherThreadStarted; + + private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) { + if (hasSingleImplementation(clazz, method_name) != b) { + System.out.println(clazz + "." + method_name + + " doesn't have single implementation value of " + b); + } + } + + // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked. + // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined. + // After Dummy.createMain2() which links in Main2, live testImplement() on stack + // should be deoptimized. + static void testImplement(boolean createMain2, boolean wait, boolean setHasJIT) { + if (setHasJIT) { + if (isInterpreted()) { + sHasJIT = false; + } + return; + } + + if (createMain2 && (sIsOptimizing || sHasJIT)) { + assertIsManaged(); + } + + sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2); + sMain1.$noinline$bar(); + + if (createMain2) { + // Wait for the other thread to start. + while (!sOtherThreadStarted); + // Create an Main2 instance and assign it to sMain2. + // sMain1 is kept the same. + sMain2 = Dummy.createMain2(); + // Wake up the other thread. + synchronized(Main.class) { + Main.class.notify(); + } + } else if (wait) { + // This is the other thread. + synchronized(Main.class) { + sOtherThreadStarted = true; + // Wait for Main2 to be linked and deoptimization is triggered. + try { + Main.class.wait(); + } catch (Exception e) { + } + } + } + + // There should be a deoptimization here right after Main2 is linked by + // calling Dummy.createMain2(), even though sMain1 didn't change. + // The behavior here would be different if inline-cache is used, which + // doesn't deoptimize since sMain1 still hits the type cache. + sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2); + if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) { + // This method should be deoptimized right after Main2 is created. + assertIsInterpreted(); + } + + if (sMain2 != null) { + sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2); + } + } + + // Test scenarios under which CHA-based devirtualization happens, + // and class loading that overrides a method can invalidate compiled code. + public static void main(String[] args) { + System.loadLibrary(args[0]); + + if (isInterpreted()) { + sIsOptimizing = false; + } + + // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet. + sMain1 = new Main1(); + + ensureJitCompiled(Main.class, "testImplement"); + testImplement(false, false, true); + + if (sHasJIT && !sIsOptimizing) { + assertSingleImplementation(Base.class, "foo", true); + assertSingleImplementation(Main1.class, "foo", true); + } else { + // Main2 is verified ahead-of-time so it's linked in already. + } + + // Create another thread that also calls sMain1.foo(). + // Try to test suspend and deopt another thread. + new Thread() { + public void run() { + testImplement(false, true, false); + } + }.start(); + + // This will create Main2 instance in the middle of testImplement(). + testImplement(true, false, false); + assertSingleImplementation(Base.class, "foo", false); + assertSingleImplementation(Main1.class, "foo", false); + } + + private static native void ensureJitCompiled(Class<?> itf, String method_name); + private static native void assertIsInterpreted(); + private static native void assertIsManaged(); + private static native boolean isInterpreted(); + private static native boolean hasSingleImplementation(Class<?> clazz, String method_name); +} + +// Put createMain2() in another class to avoid class loading due to verifier. +class Dummy { + static Main1 createMain2() { + return new Main2(); + } +} diff --git a/test/616-cha-miranda/expected.txt b/test/616-cha-miranda/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/616-cha-miranda/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/616-cha-miranda/info.txt b/test/616-cha-miranda/info.txt new file mode 100644 index 0000000000..c46f33f613 --- /dev/null +++ b/test/616-cha-miranda/info.txt @@ -0,0 +1 @@ +Test for Class Hierarchy Analysis (CHA) on miranda method. diff --git a/test/616-cha-miranda/run b/test/616-cha-miranda/run new file mode 100644 index 0000000000..d8b4f0d26c --- /dev/null +++ b/test/616-cha-miranda/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. + +# Run without an app image to prevent the classes to be loaded at startup. +exec ${RUN} "${@}" --no-app-image diff --git a/test/616-cha-miranda/src/Main.java b/test/616-cha-miranda/src/Main.java new file mode 100644 index 0000000000..e548482eb3 --- /dev/null +++ b/test/616-cha-miranda/src/Main.java @@ -0,0 +1,163 @@ +/* + * 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 Iface { + public void foo(int i); +} + +abstract class Base implements Iface { + // Iface.foo(int) will be added as a miranda method. + + void printError(String msg) { + System.out.println(msg); + } +} + +class Main1 extends Base { + public void foo(int i) { + if (i != 1) { + printError("error1"); + } + } +} + +class Main2 extends Main1 { + public void foo(int i) { + if (i != 2) { + printError("error2"); + } + } +} + +public class Main { + static Base sMain1; + static Base sMain2; + + static boolean sIsOptimizing = true; + static boolean sHasJIT = true; + static volatile boolean sOtherThreadStarted; + + private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) { + if (hasSingleImplementation(clazz, method_name) != b) { + System.out.println(clazz + "." + method_name + + " doesn't have single implementation value of " + b); + } + } + + // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked. + // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined. + // After Dummy.createMain2() which links in Main2, live testOverride() on stack + // should be deoptimized. + static void testOverride(boolean createMain2, boolean wait, boolean setHasJIT) { + if (setHasJIT) { + if (isInterpreted()) { + sHasJIT = false; + } + return; + } + + if (createMain2 && (sIsOptimizing || sHasJIT)) { + assertIsManaged(); + } + + sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2); + + if (createMain2) { + // Wait for the other thread to start. + while (!sOtherThreadStarted); + // Create an Main2 instance and assign it to sMain2. + // sMain1 is kept the same. + sMain2 = Dummy.createMain2(); + // Wake up the other thread. + synchronized(Main.class) { + Main.class.notify(); + } + } else if (wait) { + // This is the other thread. + synchronized(Main.class) { + sOtherThreadStarted = true; + // Wait for Main2 to be linked and deoptimization is triggered. + try { + Main.class.wait(); + } catch (Exception e) { + } + } + } + + // There should be a deoptimization here right after Main2 is linked by + // calling Dummy.createMain2(), even though sMain1 didn't change. + // The behavior here would be different if inline-cache is used, which + // doesn't deoptimize since sMain1 still hits the type cache. + sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2); + if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) { + // This method should be deoptimized right after Main2 is created. + assertIsInterpreted(); + } + + if (sMain2 != null) { + sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2); + } + } + + // Test scenarios under which CHA-based devirtualization happens, + // and class loading that overrides a method can invalidate compiled code. + public static void main(String[] args) { + System.loadLibrary(args[0]); + + if (isInterpreted()) { + sIsOptimizing = false; + } + + // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet. + sMain1 = new Main1(); + + ensureJitCompiled(Main.class, "testOverride"); + testOverride(false, false, true); + + if (sHasJIT && !sIsOptimizing) { + assertSingleImplementation(Base.class, "foo", true); + assertSingleImplementation(Main1.class, "foo", true); + } else { + // Main2 is verified ahead-of-time so it's linked in already. + } + + // Create another thread that also calls sMain1.foo(). + // Try to test suspend and deopt another thread. + new Thread() { + public void run() { + testOverride(false, true, false); + } + }.start(); + + // This will create Main2 instance in the middle of testOverride(). + testOverride(true, false, false); + assertSingleImplementation(Base.class, "foo", false); + assertSingleImplementation(Main1.class, "foo", false); + } + + private static native void ensureJitCompiled(Class<?> itf, String method_name); + private static native void assertIsInterpreted(); + private static native void assertIsManaged(); + private static native boolean isInterpreted(); + private static native boolean hasSingleImplementation(Class<?> clazz, String method_name); +} + +// Put createMain2() in another class to avoid class loading due to verifier. +class Dummy { + static Main1 createMain2() { + return new Main2(); + } +} diff --git a/test/616-cha-proxy-method-inline/expected.txt b/test/616-cha-proxy-method-inline/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/616-cha-proxy-method-inline/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/616-cha-proxy-method-inline/info.txt b/test/616-cha-proxy-method-inline/info.txt new file mode 100644 index 0000000000..012685547c --- /dev/null +++ b/test/616-cha-proxy-method-inline/info.txt @@ -0,0 +1 @@ +Test for Class Hierarchy Analysis (CHA) on inlining a cross-dex proxy method. diff --git a/test/616-cha-proxy-method-inline/multidex.jpp b/test/616-cha-proxy-method-inline/multidex.jpp new file mode 100644 index 0000000000..b0d200ea38 --- /dev/null +++ b/test/616-cha-proxy-method-inline/multidex.jpp @@ -0,0 +1,3 @@ +Main: + @@com.android.jack.annotations.ForceInMainDex + class Main diff --git a/test/616-cha-proxy-method-inline/run b/test/616-cha-proxy-method-inline/run new file mode 100644 index 0000000000..d8b4f0d26c --- /dev/null +++ b/test/616-cha-proxy-method-inline/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. + +# Run without an app image to prevent the classes to be loaded at startup. +exec ${RUN} "${@}" --no-app-image diff --git a/test/616-cha-proxy-method-inline/src-multidex/Foo.java b/test/616-cha-proxy-method-inline/src-multidex/Foo.java new file mode 100644 index 0000000000..9deca3e646 --- /dev/null +++ b/test/616-cha-proxy-method-inline/src-multidex/Foo.java @@ -0,0 +1,19 @@ +/* + * 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 Foo { + public Object bar(Object obj); +} diff --git a/test/616-cha-proxy-method-inline/src/Main.java b/test/616-cha-proxy-method-inline/src/Main.java new file mode 100644 index 0000000000..be7bc820b3 --- /dev/null +++ b/test/616-cha-proxy-method-inline/src/Main.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; + +class DebugProxy implements java.lang.reflect.InvocationHandler { + private Object obj; + static Class<?>[] interfaces = {Foo.class}; + + public static Object newInstance(Object obj) { + return java.lang.reflect.Proxy.newProxyInstance( + Foo.class.getClassLoader(), + interfaces, + new DebugProxy(obj)); + } + + private DebugProxy(Object obj) { + this.obj = obj; + } + + public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { + Object result; + if (obj == null) { + return null; + } + try { + System.out.println("before invoking method " + m.getName()); + result = m.invoke(obj, args); + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } catch (Exception e) { + throw new RuntimeException("unexpected invocation exception: " + e.getMessage()); + } finally { + System.out.println("after invoking method " + m.getName()); + } + return result; + } +} + +public class Main { + public static void call(Foo foo) { + if (foo == null) { + return; + } + foo.bar(null); + } + + public static void main(String[] args) { + System.loadLibrary(args[0]); + Foo foo = (Foo)DebugProxy.newInstance(null); + ensureJitCompiled(Main.class, "call"); + call(foo); + } + + private static native void ensureJitCompiled(Class<?> itf, String method_name); +} diff --git a/test/644-checker-deopt/expected.txt b/test/644-checker-deopt/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/644-checker-deopt/expected.txt diff --git a/test/644-checker-deopt/info.txt b/test/644-checker-deopt/info.txt new file mode 100644 index 0000000000..c5fb12c570 --- /dev/null +++ b/test/644-checker-deopt/info.txt @@ -0,0 +1,2 @@ +Regression test for making sure HDeoptimize is executed before +the code it should have prevented executing. diff --git a/test/644-checker-deopt/profile b/test/644-checker-deopt/profile new file mode 100644 index 0000000000..cb261cc694 --- /dev/null +++ b/test/644-checker-deopt/profile @@ -0,0 +1,2 @@ +LMain;->inlineMonomorphic(LMain;)I+LMain; +LMain;->inlinePolymorphic(LMain;)I+LMain;,LSubMain; diff --git a/test/644-checker-deopt/run b/test/644-checker-deopt/run new file mode 100644 index 0000000000..146e180000 --- /dev/null +++ b/test/644-checker-deopt/run @@ -0,0 +1,17 @@ +#!/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. + +exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile diff --git a/test/644-checker-deopt/src/Main.java b/test/644-checker-deopt/src/Main.java new file mode 100644 index 0000000000..17c80a6057 --- /dev/null +++ b/test/644-checker-deopt/src/Main.java @@ -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. + */ + +public class Main { + + /// CHECK-START: int Main.inlineMonomorphic(Main) inliner (before) + /// CHECK: InvokeVirtual method_name:Main.getValue + + /// CHECK-START: int Main.inlineMonomorphic(Main) inliner (after) + /// CHECK-NOT: InvokeVirtual method_name:Main.getValue + + /// CHECK-START: int Main.inlineMonomorphic(Main) licm (before) + /// CHECK: <<Deopt:l\d+>> Deoptimize + /// CHECK: InstanceFieldGet [<<Deopt>>] field_name:Main.value + + /// CHECK-START: int Main.inlineMonomorphic(Main) licm (after) + /// CHECK: <<Deopt:l\d+>> Deoptimize + /// CHECK: InstanceFieldGet [<<Deopt>>] field_name:Main.value + + public static int inlineMonomorphic(Main a) { + if (a == null) { + return 42; + } + int i = 0; + while (i < 100) { + i += a.getValue(); + } + return i; + } + + /// CHECK-START: int Main.inlinePolymorphic(Main) inliner (before) + /// CHECK: InvokeVirtual method_name:Main.getValue + + /// CHECK-START: int Main.inlinePolymorphic(Main) inliner (after) + /// CHECK-NOT: InvokeVirtual method_name:Main.getValue + + /// CHECK-START: int Main.inlineMonomorphic(Main) licm (before) + /// CHECK: <<Deopt:l\d+>> Deoptimize + /// CHECK: InstanceFieldGet [<<Deopt>>] field_name:Main.value + + /// CHECK-START: int Main.inlineMonomorphic(Main) licm (after) + /// CHECK: <<Deopt:l\d+>> Deoptimize + /// CHECK: InstanceFieldGet [<<Deopt>>] field_name:Main.value + public static int inlinePolymorphic(Main a) { + return a.getValue(); + } + + public int getValue() { + return value; + } + + public static void main(String[] args) { + inlineMonomorphic(new Main()); + } + + int value = 1; +} + +// Add a subclass of 'Main' to write the polymorphic inline cache in the profile. +class SubMain extends Main { +} diff --git a/test/901-hello-ti-agent/basics.cc b/test/901-hello-ti-agent/basics.cc index 91662770be..cbd768663b 100644 --- a/test/901-hello-ti-agent/basics.cc +++ b/test/901-hello-ti-agent/basics.cc @@ -16,6 +16,8 @@ #include "901-hello-ti-agent/basics.h" +#include <thread> + #include <jni.h> #include <stdio.h> #include <string.h> @@ -159,5 +161,19 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_checkLivePhase( return (current_phase == JVMTI_PHASE_LIVE) ? JNI_TRUE : JNI_FALSE; } +static void CallJvmtiFunction(jvmtiEnv* env, jclass klass, jvmtiError* err) { + jint n; + jmethodID* methods = nullptr; + *err = env->GetClassMethods(klass, &n, &methods); +} + +extern "C" JNIEXPORT jboolean JNICALL Java_Main_checkUnattached( + JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass) { + jvmtiError res = JVMTI_ERROR_NONE; + std::thread t1(CallJvmtiFunction, jvmti_env, Main_klass, &res); + t1.join(); + return res == JVMTI_ERROR_UNATTACHED_THREAD; +} + } // namespace Test901HelloTi } // namespace art diff --git a/test/901-hello-ti-agent/expected.txt b/test/901-hello-ti-agent/expected.txt index c4b24cba90..eb5b6a2f93 100644 --- a/test/901-hello-ti-agent/expected.txt +++ b/test/901-hello-ti-agent/expected.txt @@ -3,6 +3,7 @@ VMStart VMInit Hello, world! Agent in live phase. +Received expected error for unattached JVMTI calls 0 1 2 diff --git a/test/901-hello-ti-agent/src/Main.java b/test/901-hello-ti-agent/src/Main.java index 4d62ed3f5d..556e05b5d0 100644 --- a/test/901-hello-ti-agent/src/Main.java +++ b/test/901-hello-ti-agent/src/Main.java @@ -21,6 +21,9 @@ public class Main { if (checkLivePhase()) { System.out.println("Agent in live phase."); } + if (checkUnattached()) { + System.out.println("Received expected error for unattached JVMTI calls"); + } set(0); // OTHER set(1); // GC @@ -41,4 +44,5 @@ public class Main { private static native boolean checkLivePhase(); private static native void setVerboseFlag(int flag, boolean value); + private static native boolean checkUnattached(); } diff --git a/test/931-agent-thread/agent_thread.cc b/test/931-agent-thread/agent_thread.cc index f8f9e48657..2e6bd46f4e 100644 --- a/test/931-agent-thread/agent_thread.cc +++ b/test/931-agent-thread/agent_thread.cc @@ -36,7 +36,8 @@ namespace Test930AgentThread { struct AgentData { AgentData() : main_thread(nullptr), jvmti_env(nullptr), - b(2) { + b(2), + priority(0) { } jthread main_thread; diff --git a/test/981-dedup-original-dex/expected.txt b/test/981-dedup-original-dex/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/981-dedup-original-dex/expected.txt diff --git a/test/981-dedup-original-dex/info.txt b/test/981-dedup-original-dex/info.txt new file mode 100644 index 0000000000..62696e00d7 --- /dev/null +++ b/test/981-dedup-original-dex/info.txt @@ -0,0 +1,4 @@ +Tests basic functions in the jvmti plugin. + +This checks that we do not needlessly duplicate the contents of retransformed +classes original dex files. diff --git a/test/981-dedup-original-dex/run b/test/981-dedup-original-dex/run new file mode 100755 index 0000000000..e92b873956 --- /dev/null +++ b/test/981-dedup-original-dex/run @@ -0,0 +1,17 @@ +#!/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-run "$@" --jvmti diff --git a/test/981-dedup-original-dex/src/Main.java b/test/981-dedup-original-dex/src/Main.java new file mode 100644 index 0000000000..cd3f007532 --- /dev/null +++ b/test/981-dedup-original-dex/src/Main.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Field; +import java.util.Base64; + +import dalvik.system.ClassExt; + +public class Main { + + /** + * base64 encoded class/dex file for + * class Transform { + * public void sayHi() { + * System.out.println("Goodbye"); + * } + * } + */ + private static final byte[] DEX_BYTES_1 = Base64.getDecoder().decode( + "ZGV4CjAzNQCLXSBQ5FiS3f16krSYZFF8xYZtFVp0GRXMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" + + "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" + + "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" + + "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" + + "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" + + "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" + + "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" + + "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" + + "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTMuMzYAA291" + + "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAAcOhQAAAAEBAICABKACAQG4Ag0AAAAAAAAAAQAAAAAA" + + "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" + + "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" + + "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA="); + + /** + * base64 encoded class/dex file for + * class Transform2 { + * public void sayHi() { + * System.out.println("Goodbye2"); + * } + * } + */ + private static final byte[] DEX_BYTES_2 = Base64.getDecoder().decode( + "ZGV4CjAzNQAjXDED2iflQ3NXbPtBRVjQVMqoDU9nDz/QAgAAcAAAAHhWNBIAAAAAAAAAADACAAAO" + + "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACwAQAAIAEAAGIB" + + "AABqAQAAdAEAAIIBAACZAQAArQEAAMEBAADVAQAA5gEAAOkBAADtAQAAAQIAAAYCAAAPAgAAAgAA" + + "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" + + "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAACECAAAA" + + "AAAAAQABAAEAAAAWAgAABAAAAHAQAwAAAA4AAwABAAIAAAAbAgAACQAAAGIAAAAbAQEAAABuIAIA" + + "EAAOAAAAAQAAAAMABjxpbml0PgAIR29vZGJ5ZTIADExUcmFuc2Zvcm0yOwAVTGphdmEvaW8vUHJp" + + "bnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEv" + + "bGFuZy9TeXN0ZW07AA9UcmFuc2Zvcm0yLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTQuMzAA" + + "A291dAAHcHJpbnRsbgAFc2F5SGkAAQAHDgADAAcOhwAAAAEBAICABKACAQG4AgANAAAAAAAAAAEA" + + "AAAAAAAAAQAAAA4AAABwAAAAAgAAAAYAAACoAAAAAwAAAAIAAADAAAAABAAAAAEAAADYAAAABQAA" + + "AAQAAADgAAAABgAAAAEAAAAAAQAAASAAAAIAAAAgAQAAARAAAAEAAABcAQAAAiAAAA4AAABiAQAA" + + "AyAAAAIAAAAWAgAAACAAAAEAAAAhAgAAABAAAAEAAAAwAgAA"); + + public static void main(String[] args) { + try { + doTest(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static void assertSame(Object a, Object b) throws Exception { + if (a != b) { + throw new AssertionError("'" + (a != null ? a.toString() : "null") + "' is not the same as " + + "'" + (b != null ? b.toString() : "null") + "'"); + } + } + + private static Object getObjectField(Object o, String name) throws Exception { + return getObjectField(o, o.getClass(), name); + } + + private static Object getObjectField(Object o, Class<?> type, String name) throws Exception { + Field f = type.getDeclaredField(name); + f.setAccessible(true); + return f.get(o); + } + + private static Object getOriginalDexFile(Class<?> k) throws Exception { + ClassExt ext_data_object = (ClassExt) getObjectField(k, "extData"); + if (ext_data_object == null) { + return null; + } + + return getObjectField(ext_data_object, "originalDexFile"); + } + + public static void doTest() throws Exception { + // Make sure both of these are loaded prior to transformations being added so they have the same + // original dex files. + Transform t1 = new Transform(); + Transform2 t2 = new Transform2(); + + assertSame(null, getOriginalDexFile(t1.getClass())); + assertSame(null, getOriginalDexFile(t2.getClass())); + assertSame(null, getOriginalDexFile(Main.class)); + + addCommonTransformationResult("Transform", new byte[0], DEX_BYTES_1); + addCommonTransformationResult("Transform2", new byte[0], DEX_BYTES_2); + enableCommonRetransformation(true); + doCommonClassRetransformation(Transform.class, Transform2.class); + + assertSame(getOriginalDexFile(t1.getClass()), getOriginalDexFile(t2.getClass())); + assertSame(null, getOriginalDexFile(Main.class)); + // Make sure that the original dex file is a DexCache object. + assertSame(getOriginalDexFile(t1.getClass()).getClass(), Class.forName("java.lang.DexCache")); + + // Check that we end up with a byte[] if we do a direct RedefineClasses + enableCommonRetransformation(false); + doCommonClassRedefinition(Transform.class, new byte[0], DEX_BYTES_1); + assertSame((new byte[0]).getClass(), getOriginalDexFile(t1.getClass()).getClass()); + } + + // Transforms the class + private static native void doCommonClassRetransformation(Class<?>... target); + private static native void doCommonClassRedefinition(Class<?> target, + byte[] class_file, + byte[] dex_file); + private static native void enableCommonRetransformation(boolean enable); + private static native void addCommonTransformationResult(String target_name, + byte[] class_bytes, + byte[] dex_bytes); +} diff --git a/test/981-dedup-original-dex/src/Transform.java b/test/981-dedup-original-dex/src/Transform.java new file mode 100644 index 0000000000..3c97907ddc --- /dev/null +++ b/test/981-dedup-original-dex/src/Transform.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2016 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 Transform { + public void sayHi() { + System.out.println("hello"); + } +} diff --git a/test/981-dedup-original-dex/src/Transform2.java b/test/981-dedup-original-dex/src/Transform2.java new file mode 100644 index 0000000000..eb22842184 --- /dev/null +++ b/test/981-dedup-original-dex/src/Transform2.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2016 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 Transform2 { + public void sayHi() { + System.out.println("hello2"); + } +} diff --git a/test/982-ok-no-retransform/expected.txt b/test/982-ok-no-retransform/expected.txt new file mode 100644 index 0000000000..317e9677c3 --- /dev/null +++ b/test/982-ok-no-retransform/expected.txt @@ -0,0 +1,2 @@ +hello +hello diff --git a/test/982-ok-no-retransform/info.txt b/test/982-ok-no-retransform/info.txt new file mode 100644 index 0000000000..875a5f6ec1 --- /dev/null +++ b/test/982-ok-no-retransform/info.txt @@ -0,0 +1 @@ +Tests basic functions in the jvmti plugin. diff --git a/test/982-ok-no-retransform/run b/test/982-ok-no-retransform/run new file mode 100755 index 0000000000..c6e62ae6cd --- /dev/null +++ b/test/982-ok-no-retransform/run @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 2016 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-run "$@" --jvmti diff --git a/test/982-ok-no-retransform/src/Main.java b/test/982-ok-no-retransform/src/Main.java new file mode 100644 index 0000000000..7bb4a46155 --- /dev/null +++ b/test/982-ok-no-retransform/src/Main.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2016 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.util.Base64; +public class Main { + + public static void main(String[] args) { + doTest(new Transform()); + } + + public static void doTest(Transform t) { + t.sayHi(); + enableCommonRetransformation(true); + doCommonClassRetransformation(Transform.class); + t.sayHi(); + } + + // Transforms the class + private static native void doCommonClassRetransformation(Class<?>... target); + private static native void enableCommonRetransformation(boolean enable); +} diff --git a/test/982-ok-no-retransform/src/Transform.java b/test/982-ok-no-retransform/src/Transform.java new file mode 100644 index 0000000000..8e8af355da --- /dev/null +++ b/test/982-ok-no-retransform/src/Transform.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2016 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 Transform { + public void sayHi() { + // Use lower 'h' to make sure the string will have a different string id + // than the transformation (the transformation code is the same except + // the actual printed String, which was making the test inacurately passing + // in JIT mode when loading the string from the dex cache, as the string ids + // of the two different strings were the same). + // We know the string ids will be different because lexicographically: + // "Goodbye" < "LTransform;" < "hello". + System.out.println("hello"); + } +} diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 703b911f0f..cc015b031d 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -138,6 +138,7 @@ TEST_ART_TARGET_SYNC_DEPS += $(TARGET_OUT_JAVA_LIBRARIES)/conscrypt-testdex.jar # specific version depending on the compiler. ART_TEST_HOST_RUN_TEST_DEPENDENCIES := \ $(ART_HOST_EXECUTABLES) \ + $(HOST_OUT_EXECUTABLES)/hprof-conv \ $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libtiagent) \ $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libtiagentd) \ $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libartagent) \ @@ -177,8 +178,6 @@ host_prereq_rules += $(HOST_JACK_CLASSPATH_DEPENDENCIES) # Required for dx, jasmin, smali, dexmerger, jack. host_prereq_rules += $(TEST_ART_RUN_TEST_DEPENDENCIES) -host_prereq_rules += $(HOST_OUT_EXECUTABLES)/hprof-conv - # Classpath for Jack compilation for target. target_prereq_rules := $(TARGET_JACK_CLASSPATH_DEPENDENCIES) @@ -225,18 +224,6 @@ test-art-host-run-test-dependencies : $(host_prereq_rules) test-art-target-run-test-dependencies : $(target_prereq_rules) test-art-run-test-dependencies : test-art-host-run-test-dependencies test-art-target-run-test-dependencies -# Generate list of dependencies required for given target - HOST or TARGET, IMAGE_TYPE, -# COMPILER_TYPE and ADDRESS_SIZE. -$(foreach target, $(TARGET_TYPES), \ - $(foreach image, $(IMAGE_TYPES), \ - $(foreach compiler, $(COMPILER_TYPES), \ - $(foreach address_size, $(ALL_ADDRESS_SIZES), $(eval \ - $(call core-image-dependencies,$(target),$(image),$(compiler),$(address_size))))))) - -test-art-host-run-test-dependencies : $(host_prereq_rules) -test-art-target-run-test-dependencies : $(target_prereq_rules) -test-art-run-test-dependencies : test-art-host-run-test-dependencies test-art-target-run-test-dependencies - # Create a rule to build and run a test group of the following form: # test-art-{1: host target}-run-test define define-test-art-host-or-target-run-test-group @@ -259,8 +246,6 @@ host_prereq_rules := target_prereq_rules := core-image-dependencies := name-to-var := -ART_TEST_HOST_RUN_TEST_DEPENDENCIES := -TEST_ART_TARGET_SYNC_DEPS := define-test-art-host-or-target-run-test-group := TARGET_TYPES := COMPILER_TYPES := diff --git a/test/knownfailures.json b/test/knownfailures.json index 2de34ca44f..8aa0c55839 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -332,8 +332,12 @@ { "tests": ["912-classes", "616-cha", - "616-cha-abstract"], - "bug": "http://b/36344364 http://b36344221", + "616-cha-abstract", + "616-cha-interface", + "616-cha-interface-default", + "616-cha-miranda", + "616-cha-proxy-method-inline"], + "bug": "http://b/36344364 http://b/36344221", "variant": "no-dex2oat | relocate-npatchoat" }, { @@ -358,9 +362,9 @@ "variant": "interp-ac" }, { - "tests": "638-checker-inline-caches", - "description": ["Disable 638-checker-inline-caches temporarily until a fix", - "arrives."], + "tests": ["638-checker-inline-caches", + "644-checker-deopt"], + "description": ["Disabled temporarily until a fix arrives."], "bug": "http://b/36371709" } ] diff --git a/test/testrunner/run_build_test_target.py b/test/testrunner/run_build_test_target.py index 835b678cd6..282ac484b6 100755 --- a/test/testrunner/run_build_test_target.py +++ b/test/testrunner/run_build_test_target.py @@ -51,7 +51,10 @@ if target.get('target'): build_command += ' -j' + str(n_threads) build_command += ' -C ' + env.ANDROID_BUILD_TOP build_command += ' ' + target.get('target') - print build_command.split() + # Add 'dist' to avoid Jack issues b/36169180. + build_command += ' dist' + sys.stdout.write(str(build_command)) + sys.stdout.flush() if subprocess.call(build_command.split()): sys.exit(1) @@ -64,7 +67,8 @@ if target.get('run-tests'): run_test_command += ['--host'] run_test_command += ['--verbose'] - print run_test_command + sys.stdout.write(str(run_test_command)) + sys.stdout.flush() if subprocess.call(run_test_command): sys.exit(1) diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index 3203f7ad84..149578d9ed 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -53,6 +53,7 @@ import os import re import subprocess import sys +import tempfile import threading import time @@ -433,8 +434,10 @@ def run_tests(tests): options_test += ' --instruction-set-features ' + \ env.HOST_2ND_ARCH_PREFIX_DEX2OAT_HOST_INSTRUCTION_SET_FEATURES - options_test = (' --output-path %s/run-test-output/%s') % ( - env.ART_HOST_TEST_DIR, test_name) + options_test + # TODO(http://36039166): This is a temporary solution to + # fix build breakages. + options_test = (' --output-path %s') % ( + tempfile.mkdtemp(dir=env.ART_HOST_TEST_DIR)) + options_test run_test_sh = env.ANDROID_BUILD_TOP + '/art/test/run-test' command = run_test_sh + ' ' + options_test + ' ' + test @@ -480,7 +483,7 @@ def run_test(command, test, test_variant, test_name): if test_passed: print_test_info(test_name, 'PASS') else: - failed_tests.append(test_name) + failed_tests.append((test_name, script_output)) if not env.ART_TEST_KEEP_GOING: stop_testrunner = True print_test_info(test_name, 'FAIL', ('%s\n%s') % ( @@ -491,13 +494,13 @@ def run_test(command, test, test_variant, test_name): else: print_test_info(test_name, '') except subprocess.TimeoutExpired as e: - failed_tests.append(test_name) - print_test_info(test_name, 'TIMEOUT', 'timed out in %d\n%s' % ( + failed_tests.append((test_name, 'Timed out in %d seconds')) + print_test_info(test_name, 'TIMEOUT', 'Timed out in %d seconds\n%s' % ( timeout, command)) except Exception as e: - failed_tests.append(test_name) - print_test_info(test_name, 'FAIL') - print_text(('%s\n%s\n\n') % (command, str(e))) + failed_tests.append((test_name, str(e))) + print_test_info(test_name, 'FAIL', + ('%s\n%s\n\n') % (command, str(e))) finally: semaphore.release() @@ -711,16 +714,16 @@ def print_analysis(): # Prints the list of skipped tests, if any. if skipped_tests: - print_text(COLOR_SKIP + 'SKIPPED TESTS' + COLOR_NORMAL + '\n') + print_text(COLOR_SKIP + 'SKIPPED TESTS: ' + COLOR_NORMAL + '\n') for test in skipped_tests: print_text(test + '\n') print_text('\n') # Prints the list of failed tests, if any. if failed_tests: - print_text(COLOR_ERROR + 'FAILED TESTS' + COLOR_NORMAL + '\n') - for test in failed_tests: - print_text(test + '\n') + print_text(COLOR_ERROR + 'FAILED: ' + COLOR_NORMAL + '\n') + for test_info in failed_tests: + print_text(('%s\n%s\n' % (test_info[0], test_info[1]))) def parse_test_name(test_name): diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc index fddae3af02..68c7d50dbc 100644 --- a/test/ti-agent/common_load.cc +++ b/test/ti-agent/common_load.cc @@ -121,6 +121,8 @@ static AgentLib agents[] = { { "943-private-recursive-jit", common_redefine::OnLoad, nullptr }, { "944-transform-classloaders", common_redefine::OnLoad, nullptr }, { "945-obsolete-native", common_redefine::OnLoad, nullptr }, + { "981-dedup-original-dex", common_retransform::OnLoad, nullptr }, + { "982-ok-no-retransform", common_retransform::OnLoad, nullptr }, }; static AgentLib* FindAgent(char* name) { |