diff options
Diffstat (limited to 'test')
25 files changed, 1188 insertions, 6 deletions
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..1fd330afd4 --- /dev/null +++ b/test/616-cha-interface-default/info.txt @@ -0,0 +1 @@ +Test for Class Hierarchy Analysis (CHA) on interface method. 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/Main.java b/test/616-cha-interface-default/src/Main.java new file mode 100644 index 0000000000..81895b00ac --- /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. + */ + +interface Base { + default public void foo(int i) { + if (i != 1) { + printError("error1"); + } + } + + default void printError(String msg) { + System.out.println(msg); + } +} + +class Main1 implements Base { +} + +class Main2 extends Main1 { + public void bar() {} +} + +class Main3 implements Base { + public void foo(int i) { + if (i != 3) { + printError("error3"); + } + } +} + +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(); + } + + sMain1.foo(getValue(sMain1.getClass())); + sMain2.foo(getValue(sMain2.getClass())); + + 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. + sMain1.foo(getValue(sMain1.getClass())); + if ((createMain3 || wait) && sHasJIT && !sIsOptimizing) { + // This method should be deoptimized right after Main3 is created. + assertIsInterpreted(); + } + + if (sMain3 != null) { + sMain3.foo(getValue(sMain3.getClass())); + } + } + + // 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..aee6a95aa8 --- /dev/null +++ b/test/616-cha-interface/src/Main.java @@ -0,0 +1,159 @@ +/* + * 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); +} + +class Main1 implements Base { + public void foo(int i) { + if (i != 1) { + printError("error1"); + } + } + + 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); + + 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/639-checker-code-sinking/expected.txt b/test/639-checker-code-sinking/expected.txt new file mode 100644 index 0000000000..52e756c231 --- /dev/null +++ b/test/639-checker-code-sinking/expected.txt @@ -0,0 +1,3 @@ +0 +class java.lang.Object +43 diff --git a/test/639-checker-code-sinking/info.txt b/test/639-checker-code-sinking/info.txt new file mode 100644 index 0000000000..9722bdff2e --- /dev/null +++ b/test/639-checker-code-sinking/info.txt @@ -0,0 +1 @@ +Checker tests for the code sinking optimization pass. diff --git a/test/639-checker-code-sinking/src/Main.java b/test/639-checker-code-sinking/src/Main.java new file mode 100644 index 0000000000..1da19b687c --- /dev/null +++ b/test/639-checker-code-sinking/src/Main.java @@ -0,0 +1,355 @@ +/* + * 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) { + testSimpleUse(); + testTwoUses(); + testFieldStores(doThrow); + testFieldStoreCycle(); + testArrayStores(); + testOnlyStoreUses(); + testNoUse(); + testPhiInput(); + testVolatileStore(); + doThrow = true; + try { + testInstanceSideEffects(); + } catch (Error e) { + // expected + System.out.println(e.getMessage()); + } + try { + testStaticSideEffects(); + } catch (Error e) { + // expected + System.out.println(e.getMessage()); + } + + try { + testStoreStore(doThrow); + } catch (Error e) { + // expected + System.out.println(e.getMessage()); + } + } + + /// CHECK-START: void Main.testSimpleUse() code_sinking (before) + /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object + /// CHECK: NewInstance [<<LoadClass>>] + /// CHECK: If + /// CHECK: begin_block + /// CHECK: Throw + + /// CHECK-START: void Main.testSimpleUse() code_sinking (after) + /// CHECK-NOT: NewInstance + /// CHECK: If + /// CHECK: begin_block + /// CHECK: <<Error:l\d+>> LoadClass class_name:java.lang.Error + /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object + /// CHECK-NOT: begin_block + /// CHECK: NewInstance [<<LoadClass>>] + /// CHECK-NOT: begin_block + /// CHECK: NewInstance [<<Error>>] + /// CHECK: Throw + public static void testSimpleUse() { + Object o = new Object(); + if (doThrow) { + throw new Error(o.toString()); + } + } + + /// CHECK-START: void Main.testTwoUses() code_sinking (before) + /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object + /// CHECK: NewInstance [<<LoadClass>>] + /// CHECK: If + /// CHECK: begin_block + /// CHECK: Throw + + /// CHECK-START: void Main.testTwoUses() code_sinking (after) + /// CHECK-NOT: NewInstance + /// CHECK: If + /// CHECK: begin_block + /// CHECK: <<Error:l\d+>> LoadClass class_name:java.lang.Error + /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object + /// CHECK-NOT: begin_block + /// CHECK: NewInstance [<<LoadClass>>] + /// CHECK-NOT: begin_block + /// CHECK: NewInstance [<<Error>>] + /// CHECK: Throw + public static void testTwoUses() { + Object o = new Object(); + if (doThrow) { + throw new Error(o.toString() + o.toString()); + } + } + + /// CHECK-START: void Main.testFieldStores(boolean) code_sinking (before) + /// CHECK: <<Int42:i\d+>> IntConstant 42 + /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main + /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] + /// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int42>>] + /// CHECK: If + /// CHECK: begin_block + /// CHECK: Throw + + /// CHECK-START: void Main.testFieldStores(boolean) code_sinking (after) + /// CHECK: <<Int42:i\d+>> IntConstant 42 + /// CHECK-NOT: NewInstance + /// CHECK: If + /// CHECK: begin_block + /// CHECK: <<Error:l\d+>> LoadClass class_name:java.lang.Error + /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main + /// CHECK-NOT: begin_block + /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] + /// CHECK-NOT: begin_block + /// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int42>>] + /// CHECK-NOT: begin_block + /// CHECK: NewInstance [<<Error>>] + /// CHECK: Throw + public static void testFieldStores(boolean doThrow) { + Main m = new Main(); + m.intField = 42; + if (doThrow) { + throw new Error(m.toString()); + } + } + + /// CHECK-START: void Main.testFieldStoreCycle() code_sinking (before) + /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main + /// CHECK: <<NewInstance1:l\d+>> NewInstance [<<LoadClass>>] + /// CHECK: <<NewInstance2:l\d+>> NewInstance [<<LoadClass>>] + /// CHECK: InstanceFieldSet [<<NewInstance1>>,<<NewInstance2>>] + /// CHECK: InstanceFieldSet [<<NewInstance2>>,<<NewInstance1>>] + /// CHECK: If + /// CHECK: begin_block + /// CHECK: Throw + + // TODO(ngeoffray): Handle allocation/store cycles. + /// CHECK-START: void Main.testFieldStoreCycle() code_sinking (after) + /// CHECK: begin_block + /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main + /// CHECK: <<NewInstance1:l\d+>> NewInstance [<<LoadClass>>] + /// CHECK: <<NewInstance2:l\d+>> NewInstance [<<LoadClass>>] + /// CHECK: InstanceFieldSet [<<NewInstance1>>,<<NewInstance2>>] + /// CHECK: InstanceFieldSet [<<NewInstance2>>,<<NewInstance1>>] + /// CHECK: If + /// CHECK: begin_block + /// CHECK: Throw + public static void testFieldStoreCycle() { + Main m1 = new Main(); + Main m2 = new Main(); + m1.objectField = m2; + m2.objectField = m1; + if (doThrow) { + throw new Error(m1.toString() + m2.toString()); + } + } + + /// CHECK-START: void Main.testArrayStores() code_sinking (before) + /// CHECK: <<Int1:i\d+>> IntConstant 1 + /// CHECK: <<Int0:i\d+>> IntConstant 0 + /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object[] + /// CHECK: <<NewArray:l\d+>> NewArray [<<LoadClass>>,<<Int1>>] + /// CHECK: ArraySet [<<NewArray>>,<<Int0>>,<<NewArray>>] + /// CHECK: If + /// CHECK: begin_block + /// CHECK: Throw + + /// CHECK-START: void Main.testArrayStores() code_sinking (after) + /// CHECK: <<Int1:i\d+>> IntConstant 1 + /// CHECK: <<Int0:i\d+>> IntConstant 0 + /// CHECK-NOT: NewArray + /// CHECK: If + /// CHECK: begin_block + /// CHECK: <<Error:l\d+>> LoadClass class_name:java.lang.Error + /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object[] + /// CHECK-NOT: begin_block + /// CHECK: <<NewArray:l\d+>> NewArray [<<LoadClass>>,<<Int1>>] + /// CHECK-NOT: begin_block + /// CHECK: ArraySet [<<NewArray>>,<<Int0>>,<<NewArray>>] + /// CHECK-NOT: begin_block + /// CHECK: NewInstance [<<Error>>] + /// CHECK: Throw + public static void testArrayStores() { + Object[] o = new Object[1]; + o[0] = o; + if (doThrow) { + throw new Error(o.toString()); + } + } + + // Make sure code sinking does not crash on dead allocations. + public static void testOnlyStoreUses() { + Main m = new Main(); + Object[] o = new Object[1]; // dead allocation, should eventually be removed b/35634932. + o[0] = m; + o = null; // Avoid environment uses for the array allocation. + if (doThrow) { + throw new Error(m.toString()); + } + } + + // Make sure code sinking does not crash on dead code. + public static void testNoUse() { + Main m = new Main(); + boolean load = Main.doLoop; // dead code, not removed because of environment use. + // Ensure one environment use for the static field + $opt$noinline$foo(); + load = false; + if (doThrow) { + throw new Error(m.toString()); + } + } + + // Make sure we can move code only used by a phi. + /// CHECK-START: void Main.testPhiInput() code_sinking (before) + /// CHECK: <<Null:l\d+>> NullConstant + /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object + /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] + /// CHECK: If + /// CHECK: begin_block + /// CHECK: Phi [<<Null>>,<<NewInstance>>] + /// CHECK: Throw + + /// CHECK-START: void Main.testPhiInput() code_sinking (after) + /// CHECK: <<Null:l\d+>> NullConstant + /// CHECK-NOT: NewInstance + /// CHECK: If + /// CHECK: begin_block + /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object + /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] + /// CHECK: begin_block + /// CHECK: Phi [<<Null>>,<<NewInstance>>] + /// CHECK: <<Error:l\d+>> LoadClass class_name:java.lang.Error + /// CHECK: NewInstance [<<Error>>] + /// CHECK: Throw + public static void testPhiInput() { + Object f = new Object(); + if (doThrow) { + Object o = null; + int i = 2; + if (doLoop) { + o = f; + i = 42; + } + throw new Error(o.toString() + i); + } + } + + static void $opt$noinline$foo() {} + + // Check that we do not move volatile stores. + /// CHECK-START: void Main.testVolatileStore() code_sinking (before) + /// CHECK: <<Int42:i\d+>> IntConstant 42 + /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main + /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] + /// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int42>>] + /// CHECK: If + /// CHECK: begin_block + /// CHECK: Throw + + /// CHECK-START: void Main.testVolatileStore() code_sinking (after) + /// CHECK: <<Int42:i\d+>> IntConstant 42 + /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main + /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] + /// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int42>>] + /// CHECK: If + /// CHECK: begin_block + /// CHECK: Throw + public static void testVolatileStore() { + Main m = new Main(); + m.volatileField = 42; + if (doThrow) { + throw new Error(m.toString()); + } + } + + public static void testInstanceSideEffects() { + int a = mainField.intField; + $noinline$changeIntField(); + if (doThrow) { + throw new Error("" + a); + } + } + + static void $noinline$changeIntField() { + mainField.intField = 42; + } + + public static void testStaticSideEffects() { + Object o = obj; + $noinline$changeStaticObjectField(); + if (doThrow) { + throw new Error(o.getClass().toString()); + } + } + + static void $noinline$changeStaticObjectField() { + obj = new Main(); + } + + // Test that we preserve the order of stores. + /// CHECK-START: void Main.testStoreStore(boolean) code_sinking (before) + /// CHECK: <<Int42:i\d+>> IntConstant 42 + /// CHECK: <<Int43:i\d+>> IntConstant 43 + /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main + /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] + /// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int42>>] + /// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int43>>] + /// CHECK: If + /// CHECK: begin_block + /// CHECK: Throw + + /// CHECK-START: void Main.testStoreStore(boolean) code_sinking (after) + /// CHECK: <<Int42:i\d+>> IntConstant 42 + /// CHECK: <<Int43:i\d+>> IntConstant 43 + /// CHECK-NOT: NewInstance + /// CHECK: If + /// CHECK: begin_block + /// CHECK: <<Error:l\d+>> LoadClass class_name:java.lang.Error + /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main + /// CHECK-NOT: begin_block + /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] + /// CHECK-NOT: begin_block + /// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int42>>] + /// CHECK-NOT: begin_block + /// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int43>>] + /// CHECK-NOT: begin_block + /// CHECK: NewInstance [<<Error>>] + /// CHECK: Throw + public static void testStoreStore(boolean doThrow) { + Main m = new Main(); + m.intField = 42; + m.intField = 43; + if (doThrow) { + throw new Error(m.$opt$noinline$toString()); + } + } + + public String $opt$noinline$toString() { + return "" + intField; + } + + volatile int volatileField; + int intField; + Object objectField; + static boolean doThrow; + static boolean doLoop; + static Main mainField = new Main(); + static Object obj = new Object(); +} diff --git a/test/903-hello-tagging/expected.txt b/test/903-hello-tagging/expected.txt index 872b79b518..acfdbd810b 100644 --- a/test/903-hello-tagging/expected.txt +++ b/test/903-hello-tagging/expected.txt @@ -8,3 +8,4 @@ [<null;1>, <null;1>, <null;2>, <null;2>, <null;3>, <null;3>, <null;4>, <null;4>, <null;5>, <null;5>, <null;6>, <null;6>, <null;7>, <null;7>, <null;8>, <null;8>, <null;9>, <null;9>] 18 [<1;0>, <2;0>, <3;0>, <4;0>, <5;0>, <6;0>, <7;0>, <8;0>, <9;0>, <11;0>, <12;0>, <13;0>, <14;0>, <15;0>, <16;0>, <17;0>, <18;0>, <19;0>] +[100, 101, 102, 103, 104, 105, 106, 107, 108, 109] diff --git a/test/903-hello-tagging/src/Main.java b/test/903-hello-tagging/src/Main.java index 2f0365a921..48896b236a 100644 --- a/test/903-hello-tagging/src/Main.java +++ b/test/903-hello-tagging/src/Main.java @@ -22,6 +22,7 @@ public class Main { public static void main(String[] args) { doTest(); testGetTaggedObjects(); + testTags(); } public static void doTest() { @@ -35,6 +36,12 @@ public class Main { } } + public static void testTags() { + Object o = new Object(); + long[] res = testTagsInDifferentEnvs(o, 100, 10); + System.out.println(Arrays.toString(res)); + } + private static WeakReference<Object> test() { Object o1 = new Object(); setTag(o1, 1); @@ -166,4 +173,5 @@ public class Main { private static native long getTag(Object o); private static native Object[] getTaggedObjects(long[] searchTags, boolean returnObjects, boolean returnTags); + private static native long[] testTagsInDifferentEnvs(Object o, long baseTag, int n); } diff --git a/test/903-hello-tagging/tagging.cc b/test/903-hello-tagging/tagging.cc index f74c1fc2ea..6177263cd2 100644 --- a/test/903-hello-tagging/tagging.cc +++ b/test/903-hello-tagging/tagging.cc @@ -139,6 +139,62 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getTaggedObjects(JNIEnv* env return resultArray; } +static jvmtiEnv* CreateJvmtiEnv(JNIEnv* env) { + JavaVM* jvm; + CHECK_EQ(0, env->GetJavaVM(&jvm)); + + jvmtiEnv* new_jvmti_env; + CHECK_EQ(0, jvm->GetEnv(reinterpret_cast<void**>(&new_jvmti_env), JVMTI_VERSION_1_0)); + + jvmtiCapabilities capa; + memset(&capa, 0, sizeof(jvmtiCapabilities)); + capa.can_tag_objects = 1; + jvmtiError error = new_jvmti_env->AddCapabilities(&capa); + CHECK_EQ(JVMTI_ERROR_NONE, error); + + return new_jvmti_env; +} + +static void SetTag(jvmtiEnv* env, jobject obj, jlong tag) { + jvmtiError ret = env->SetTag(obj, tag); + CHECK_EQ(JVMTI_ERROR_NONE, ret); +} + +static jlong GetTag(jvmtiEnv* env, jobject obj) { + jlong tag; + jvmtiError ret = env->GetTag(obj, &tag); + CHECK_EQ(JVMTI_ERROR_NONE, ret); + return tag; +} + +extern "C" JNIEXPORT jlongArray JNICALL Java_Main_testTagsInDifferentEnvs( + JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject obj, jlong base_tag, jint count) { + std::unique_ptr<jvmtiEnv*[]> envs = std::unique_ptr<jvmtiEnv*[]>(new jvmtiEnv*[count]); + envs[0] = jvmti_env; + for (int32_t i = 1; i != count; ++i) { + envs[i] = CreateJvmtiEnv(env); + } + + for (int32_t i = 0; i != count; ++i) { + SetTag(envs[i], obj, base_tag + i); + } + std::unique_ptr<jlong[]> vals = std::unique_ptr<jlong[]>(new jlong[count]); + for (int32_t i = 0; i != count; ++i) { + vals[i] = GetTag(envs[i], obj); + } + + for (int32_t i = 1; i != count; ++i) { + CHECK_EQ(JVMTI_ERROR_NONE, envs[i]->DisposeEnvironment()); + } + + jlongArray res = env->NewLongArray(count); + if (res == nullptr) { + return nullptr; + } + env->SetLongArrayRegion(res, 0, count, vals.get()); + return res; +} + } // namespace Test903HelloTagging } // namespace art diff --git a/test/913-heaps/expected.txt b/test/913-heaps/expected.txt index e81cb9c50c..46805d7272 100644 --- a/test/913-heaps/expected.txt +++ b/test/913-heaps/expected.txt @@ -119,3 +119,178 @@ root@root --(thread)--> 1@1000 [size=16, length=-1] 5@1002 --(field@24)--> 6@1000 [size=16, length=-1] 5@1002 --(field@28)--> 1@1000 [size=16, length=-1] --- +--- heap_filter --- +---- tagged objects +--- +--- +--- +--- +---- untagged objects +root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 32])--> 1@1000 [size=16, length=-1] +root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=132, length=-1] +root@root --(system-class)--> 2@0 [size=32, length=-1] +root@root --(thread)--> 3000@0 [size=132, length=-1] +0@0 --(array-element@0)--> 1@1000 [size=16, length=-1] +1001@0 --(superclass)--> 1000@0 [size=123, length=-1] +1002@0 --(interface)--> 2001@0 [size=124, length=-1] +1002@0 --(superclass)--> 1001@0 [size=123, length=-1] +1@1000 --(class)--> 1000@0 [size=123, length=-1] +1@1000 --(field@12)--> 3@1001 [size=24, length=-1] +1@1000 --(field@8)--> 2@1000 [size=16, length=-1] +2001@0 --(interface)--> 2000@0 [size=124, length=-1] +2@1000 --(class)--> 1000@0 [size=123, length=-1] +3@1001 --(class)--> 1001@0 [size=123, length=-1] +3@1001 --(field@16)--> 4@1000 [size=16, length=-1] +3@1001 --(field@20)--> 5@1002 [size=32, length=-1] +4@1000 --(class)--> 1000@0 [size=123, length=-1] +5@1002 --(class)--> 1002@0 [size=123, length=-1] +5@1002 --(field@24)--> 6@1000 [size=16, length=-1] +5@1002 --(field@28)--> 1@1000 [size=16, length=-1] +6@1000 --(class)--> 1000@0 [size=123, length=-1] +--- +1001@0 --(superclass)--> 1000@0 [size=123, length=-1] +1002@0 --(interface)--> 2001@0 [size=124, length=-1] +1002@0 --(superclass)--> 1001@0 [size=123, length=-1] +1@1000 --(class)--> 1000@0 [size=123, length=-1] +1@1000 --(field@12)--> 3@1001 [size=24, length=-1] +1@1000 --(field@8)--> 2@1000 [size=16, length=-1] +2001@0 --(interface)--> 2000@0 [size=124, length=-1] +2@1000 --(class)--> 1000@0 [size=123, length=-1] +3@1001 --(class)--> 1001@0 [size=123, length=-1] +3@1001 --(field@16)--> 4@1000 [size=16, length=-1] +3@1001 --(field@20)--> 5@1002 [size=32, length=-1] +4@1000 --(class)--> 1000@0 [size=123, length=-1] +5@1002 --(class)--> 1002@0 [size=123, length=-1] +5@1002 --(field@24)--> 6@1000 [size=16, length=-1] +5@1002 --(field@28)--> 1@1000 [size=16, length=-1] +6@1000 --(class)--> 1000@0 [size=123, length=-1] +--- +root@root --(jni-global)--> 1@1000 [size=16, length=-1] +root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1] +root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=13,location= 10])--> 1@1000 [size=16, length=-1] +root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 10])--> 1@1000 [size=16, length=-1] +root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1] +root@root --(system-class)--> 2@0 [size=32, length=-1] +root@root --(thread)--> 1@1000 [size=16, length=-1] +root@root --(thread)--> 3000@0 [size=132, length=-1] +1001@0 --(superclass)--> 1000@0 [size=123, length=-1] +1002@0 --(interface)--> 2001@0 [size=124, length=-1] +1002@0 --(superclass)--> 1001@0 [size=123, length=-1] +1@1000 --(class)--> 1000@0 [size=123, length=-1] +1@1000 --(field@12)--> 3@1001 [size=24, length=-1] +1@1000 --(field@8)--> 2@1000 [size=16, length=-1] +2001@0 --(interface)--> 2000@0 [size=124, length=-1] +2@1000 --(class)--> 1000@0 [size=123, length=-1] +3@1001 --(class)--> 1001@0 [size=123, length=-1] +3@1001 --(field@16)--> 4@1000 [size=16, length=-1] +3@1001 --(field@20)--> 5@1002 [size=32, length=-1] +4@1000 --(class)--> 1000@0 [size=123, length=-1] +5@1002 --(class)--> 1002@0 [size=123, length=-1] +5@1002 --(field@24)--> 6@1000 [size=16, length=-1] +5@1002 --(field@28)--> 1@1000 [size=16, length=-1] +6@1000 --(class)--> 1000@0 [size=123, length=-1] +--- +1001@0 --(superclass)--> 1000@0 [size=123, length=-1] +1002@0 --(interface)--> 2001@0 [size=124, length=-1] +1002@0 --(superclass)--> 1001@0 [size=123, length=-1] +1@1000 --(class)--> 1000@0 [size=123, length=-1] +1@1000 --(field@12)--> 3@1001 [size=24, length=-1] +1@1000 --(field@8)--> 2@1000 [size=16, length=-1] +2001@0 --(interface)--> 2000@0 [size=124, length=-1] +2@1000 --(class)--> 1000@0 [size=123, length=-1] +3@1001 --(class)--> 1001@0 [size=123, length=-1] +3@1001 --(field@16)--> 4@1000 [size=16, length=-1] +3@1001 --(field@20)--> 5@1002 [size=32, length=-1] +4@1000 --(class)--> 1000@0 [size=123, length=-1] +5@1002 --(class)--> 1002@0 [size=123, length=-1] +5@1002 --(field@24)--> 6@1000 [size=16, length=-1] +5@1002 --(field@28)--> 1@1000 [size=16, length=-1] +6@1000 --(class)--> 1000@0 [size=123, length=-1] +--- +---- tagged classes +root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=132, length=-1] +root@root --(system-class)--> 2@0 [size=32, length=-1] +root@root --(thread)--> 3000@0 [size=132, length=-1] +1001@0 --(superclass)--> 1000@0 [size=123, length=-1] +1002@0 --(interface)--> 2001@0 [size=124, length=-1] +1002@0 --(superclass)--> 1001@0 [size=123, length=-1] +1@1000 --(class)--> 1000@0 [size=123, length=-1] +2001@0 --(interface)--> 2000@0 [size=124, length=-1] +2@1000 --(class)--> 1000@0 [size=123, length=-1] +3@1001 --(class)--> 1001@0 [size=123, length=-1] +4@1000 --(class)--> 1000@0 [size=123, length=-1] +5@1002 --(class)--> 1002@0 [size=123, length=-1] +6@1000 --(class)--> 1000@0 [size=123, length=-1] +--- +1001@0 --(superclass)--> 1000@0 [size=123, length=-1] +1002@0 --(interface)--> 2001@0 [size=124, length=-1] +1002@0 --(superclass)--> 1001@0 [size=123, length=-1] +1@1000 --(class)--> 1000@0 [size=123, length=-1] +2001@0 --(interface)--> 2000@0 [size=124, length=-1] +2@1000 --(class)--> 1000@0 [size=123, length=-1] +3@1001 --(class)--> 1001@0 [size=123, length=-1] +4@1000 --(class)--> 1000@0 [size=123, length=-1] +5@1002 --(class)--> 1002@0 [size=123, length=-1] +6@1000 --(class)--> 1000@0 [size=123, length=-1] +--- +root@root --(system-class)--> 2@0 [size=32, length=-1] +root@root --(thread)--> 3000@0 [size=132, length=-1] +1001@0 --(superclass)--> 1000@0 [size=123, length=-1] +1002@0 --(interface)--> 2001@0 [size=124, length=-1] +1002@0 --(superclass)--> 1001@0 [size=123, length=-1] +1@1000 --(class)--> 1000@0 [size=123, length=-1] +2001@0 --(interface)--> 2000@0 [size=124, length=-1] +2@1000 --(class)--> 1000@0 [size=123, length=-1] +3@1001 --(class)--> 1001@0 [size=123, length=-1] +4@1000 --(class)--> 1000@0 [size=123, length=-1] +5@1002 --(class)--> 1002@0 [size=123, length=-1] +6@1000 --(class)--> 1000@0 [size=123, length=-1] +--- +1001@0 --(superclass)--> 1000@0 [size=123, length=-1] +1002@0 --(interface)--> 2001@0 [size=124, length=-1] +1002@0 --(superclass)--> 1001@0 [size=123, length=-1] +1@1000 --(class)--> 1000@0 [size=123, length=-1] +2001@0 --(interface)--> 2000@0 [size=124, length=-1] +2@1000 --(class)--> 1000@0 [size=123, length=-1] +3@1001 --(class)--> 1001@0 [size=123, length=-1] +4@1000 --(class)--> 1000@0 [size=123, length=-1] +5@1002 --(class)--> 1002@0 [size=123, length=-1] +6@1000 --(class)--> 1000@0 [size=123, length=-1] +--- +---- untagged classes +root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 32])--> 1@1000 [size=16, length=-1] +0@0 --(array-element@0)--> 1@1000 [size=16, length=-1] +1@1000 --(field@12)--> 3@1001 [size=24, length=-1] +1@1000 --(field@8)--> 2@1000 [size=16, length=-1] +3@1001 --(field@16)--> 4@1000 [size=16, length=-1] +3@1001 --(field@20)--> 5@1002 [size=32, length=-1] +5@1002 --(field@24)--> 6@1000 [size=16, length=-1] +5@1002 --(field@28)--> 1@1000 [size=16, length=-1] +--- +1@1000 --(field@12)--> 3@1001 [size=24, length=-1] +1@1000 --(field@8)--> 2@1000 [size=16, length=-1] +3@1001 --(field@16)--> 4@1000 [size=16, length=-1] +3@1001 --(field@20)--> 5@1002 [size=32, length=-1] +5@1002 --(field@24)--> 6@1000 [size=16, length=-1] +5@1002 --(field@28)--> 1@1000 [size=16, length=-1] +--- +root@root --(jni-global)--> 1@1000 [size=16, length=-1] +root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1] +root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=13,location= 10])--> 1@1000 [size=16, length=-1] +root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 10])--> 1@1000 [size=16, length=-1] +root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1] +root@root --(thread)--> 1@1000 [size=16, length=-1] +1@1000 --(field@12)--> 3@1001 [size=24, length=-1] +1@1000 --(field@8)--> 2@1000 [size=16, length=-1] +3@1001 --(field@16)--> 4@1000 [size=16, length=-1] +3@1001 --(field@20)--> 5@1002 [size=32, length=-1] +5@1002 --(field@24)--> 6@1000 [size=16, length=-1] +5@1002 --(field@28)--> 1@1000 [size=16, length=-1] +--- +1@1000 --(field@12)--> 3@1001 [size=24, length=-1] +1@1000 --(field@8)--> 2@1000 [size=16, length=-1] +3@1001 --(field@16)--> 4@1000 [size=16, length=-1] +3@1001 --(field@20)--> 5@1002 [size=32, length=-1] +5@1002 --(field@24)--> 6@1000 [size=16, length=-1] +5@1002 --(field@28)--> 1@1000 [size=16, length=-1] +--- diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc index 1de1a695c2..99bc48eeec 100644 --- a/test/913-heaps/heaps.cc +++ b/test/913-heaps/heaps.cc @@ -18,6 +18,7 @@ #include <stdio.h> #include <string.h> +#include <iostream> #include <vector> #include "android-base/stringprintf.h" @@ -29,6 +30,7 @@ #include "native_stack_dump.h" #include "openjdkjvmti/jvmti.h" #include "runtime.h" +#include "scoped_thread_state_change-inl.h" #include "thread-inl.h" #include "thread_list.h" @@ -279,8 +281,14 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_followReferences(JNIEnv* env jlong size, jint length, const jvmtiHeapReferenceInfo* reference_info) + REQUIRES_SHARED(Locks::mutator_lock_) : Elem(referrer, referree, size, length) { memcpy(&info_, reference_info, sizeof(jvmtiHeapReferenceInfo)); + // Debug stack trace for failure condition. Remove when done. + if (info_.stack_local.depth == 3 && info_.stack_local.slot == 13) { + DumpNativeStack(std::cerr, GetTid()); + Thread::Current()->DumpJavaStack(std::cerr, false, false); + } } protected: diff --git a/test/913-heaps/run b/test/913-heaps/run index c6e62ae6cd..dd35526d25 100755 --- a/test/913-heaps/run +++ b/test/913-heaps/run @@ -14,4 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -./default-run "$@" --jvmti +./default-run "$@" --jvmti -Xcompiler-option -g diff --git a/test/913-heaps/src/Main.java b/test/913-heaps/src/Main.java index 7a91d1f325..df89f347e0 100644 --- a/test/913-heaps/src/Main.java +++ b/test/913-heaps/src/Main.java @@ -31,6 +31,17 @@ public class Main { // Test klass filter. System.out.println("--- klass ---"); new TestConfig(A.class, 0).doFollowReferencesTest(); + + // Test heap filter. + System.out.println("--- heap_filter ---"); + System.out.println("---- tagged objects"); + new TestConfig(null, 0x4).doFollowReferencesTest(); + System.out.println("---- untagged objects"); + new TestConfig(null, 0x8).doFollowReferencesTest(); + System.out.println("---- tagged classes"); + new TestConfig(null, 0x10).doFollowReferencesTest(); + System.out.println("---- untagged classes"); + new TestConfig(null, 0x20).doFollowReferencesTest(); } public static void doTest() throws Exception { diff --git a/test/etc/default-build b/test/etc/default-build index 431896631d..d74b24d985 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -97,7 +97,7 @@ JAVAC_EXPERIMENTAL_ARGS["agents"]="-source 1.8 -target 1.8" while true; do if [ "x$1" = "x--dx-option" ]; then shift - on="$1" + option="$1" DX_FLAGS="${DX_FLAGS} $option" shift elif [ "x$1" = "x--jvm" ]; then @@ -209,9 +209,9 @@ if [ ${HAS_SRC_DEX2OAT_UNRESOLVED} = "true" ]; then ${JACK} --import classes.jill.jar --output-dex . else if [ ${NEED_DEX} = "true" ]; then - ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 classes-ex + ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 ${DX_FLAGS} classes-ex zip ${TEST_NAME}-ex.jar classes.dex - ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes + ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 ${DX_FLAGS} classes fi fi else diff --git a/test/run-test b/test/run-test index e808deef52..6134a14696 100755 --- a/test/run-test +++ b/test/run-test @@ -247,6 +247,11 @@ while true; do option="$1" run_args="${run_args} -Xcompiler-option $option" shift + elif [ "x$1" = "x--build-option" ]; then + shift + option="$1" + build_args="${build_args} $option" + shift elif [ "x$1" = "x--runtime-option" ]; then shift option="$1" @@ -611,6 +616,7 @@ if [ "$usage" = "yes" ]; then echo " Runtime Options:" echo " -O Run non-debug rather than debug build (off by default)." echo " -Xcompiler-option Pass an option to the compiler." + echo " --build-option Pass an option to the build script." echo " --runtime-option Pass an option to the runtime." echo " --debug Wait for a debugger to attach." echo " --debuggable Whether to compile Java code for a debugger." |