diff options
Diffstat (limited to 'test')
| -rw-r--r-- | test/168-vmstack-annotated/expected.txt | 0 | ||||
| -rw-r--r-- | test/168-vmstack-annotated/info.txt | 1 | ||||
| -rw-r--r-- | test/168-vmstack-annotated/run | 18 | ||||
| -rw-r--r-- | test/168-vmstack-annotated/src/Main.java | 225 | ||||
| -rw-r--r-- | test/672-checker-throw-method/expected.txt | 1 | ||||
| -rw-r--r-- | test/672-checker-throw-method/info.txt | 1 | ||||
| -rw-r--r-- | test/672-checker-throw-method/src/Main.java | 244 | ||||
| -rw-r--r-- | test/HiddenApi/Main.java | 26 | ||||
| -rw-r--r-- | test/knownfailures.json | 10 |
9 files changed, 525 insertions, 1 deletions
diff --git a/test/168-vmstack-annotated/expected.txt b/test/168-vmstack-annotated/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/168-vmstack-annotated/expected.txt diff --git a/test/168-vmstack-annotated/info.txt b/test/168-vmstack-annotated/info.txt new file mode 100644 index 0000000000..d849bc31ed --- /dev/null +++ b/test/168-vmstack-annotated/info.txt @@ -0,0 +1 @@ +Regression test for b/68703210 diff --git a/test/168-vmstack-annotated/run b/test/168-vmstack-annotated/run new file mode 100644 index 0000000000..93654113e6 --- /dev/null +++ b/test/168-vmstack-annotated/run @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Use a smaller heap so it's easier to potentially fill up. +exec ${RUN} $@ --runtime-option -Xmx2m diff --git a/test/168-vmstack-annotated/src/Main.java b/test/168-vmstack-annotated/src/Main.java new file mode 100644 index 0000000000..8234f945c0 --- /dev/null +++ b/test/168-vmstack-annotated/src/Main.java @@ -0,0 +1,225 @@ +/* + * 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.Thread.State; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; + +public class Main { + + static class Runner implements Runnable { + List<Object> locks; + List<CyclicBarrier> barriers; + + public Runner(List<Object> locks, List<CyclicBarrier> barriers) { + this.locks = locks; + this.barriers = barriers; + } + + @Override + public void run() { + step(locks, barriers); + } + + private void step(List<Object> l, List<CyclicBarrier> b) { + if (l.isEmpty()) { + // Nothing to do, sleep indefinitely. + try { + Thread.sleep(100000000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } else { + Object lockObject = l.remove(0); + CyclicBarrier barrierObject = b.remove(0); + + if (lockObject == null) { + // No lock object: only take barrier, recurse. + try { + barrierObject.await(); + } catch (InterruptedException | BrokenBarrierException e) { + throw new RuntimeException(e); + } + step(l, b); + } else if (barrierObject != null) { + // Have barrier: sync, wait and recurse. + synchronized(lockObject) { + try { + barrierObject.await(); + } catch (InterruptedException | BrokenBarrierException e) { + throw new RuntimeException(e); + } + step(l, b); + } + } else { + // Sync, and get next step (which is assumed to have object and barrier). + synchronized (lockObject) { + Object lockObject2 = l.remove(0); + CyclicBarrier barrierObject2 = b.remove(0); + synchronized(lockObject2) { + try { + barrierObject2.await(); + } catch (InterruptedException | BrokenBarrierException e) { + throw new RuntimeException(e); + } + step(l, b); + } + } + } + } + } + } + + public static void main(String[] args) throws Exception { + try { + testCluster1(); + } catch (Exception e) { + Map<Thread,StackTraceElement[]> stacks = Thread.getAllStackTraces(); + for (Map.Entry<Thread,StackTraceElement[]> entry : stacks.entrySet()) { + System.out.println(entry.getKey()); + System.out.println(Arrays.toString(entry.getValue())); + } + throw e; + } + } + + private static void testCluster1() throws Exception { + // Test setup (at deadlock): + // + // Thread 1: + // #0 step: synchornized(o3) { synchronized(o2) } + // #1 step: synchronized(o1) + // + // Thread 2: + // #0 step: synchronized(o1) + // #1 step: synchronized(o4) { synchronized(o2) } + // + LinkedList<Object> l1 = new LinkedList<>(); + LinkedList<CyclicBarrier> b1 = new LinkedList<>(); + LinkedList<Object> l2 = new LinkedList<>(); + LinkedList<CyclicBarrier> b2 = new LinkedList<>(); + + Object o1 = new Object(); + Object o2 = new Object(); + Object o3 = new Object(); + Object o4 = new Object(); + + l1.add(o1); + l1.add(o3); + l1.add(o2); + l2.add(o4); + l2.add(o2); + l2.add(o1); + + CyclicBarrier c1 = new CyclicBarrier(3); + CyclicBarrier c2 = new CyclicBarrier(2); + b1.add(c1); + b1.add(null); + b1.add(c2); + b2.add(null); + b2.add(c1); + b2.add(c2); + + Thread t1 = new Thread(new Runner(l1, b1)); + t1.setDaemon(true); + t1.start(); + Thread t2 = new Thread(new Runner(l2, b2)); + t2.setDaemon(true); + t2.start(); + + c1.await(); + + waitNotRunnable(t1); + waitNotRunnable(t2); + Thread.sleep(250); // Unfortunately this seems necessary. :-( + + // Thread 1. + { + Object[] stack1 = getAnnotatedStack(t1); + assertBlockedOn(stack1[0], o2); // Blocked on o2. + assertLocks(stack1[0], o3); // Locked o3. + assertStackTraceElementStep(stack1[0]); + + assertBlockedOn(stack1[1], null); // Frame can't be blocked. + assertLocks(stack1[1], o1); // Locked o1. + assertStackTraceElementStep(stack1[1]); + } + + // Thread 2. + { + Object[] stack2 = getAnnotatedStack(t2); + assertBlockedOn(stack2[0], o1); // Blocked on o1. + assertLocks(stack2[0]); // Nothing locked. + assertStackTraceElementStep(stack2[0]); + + assertBlockedOn(stack2[1], null); // Frame can't be blocked. + assertLocks(stack2[1], o4, o2); // Locked o4, o2. + assertStackTraceElementStep(stack2[1]); + } + } + + private static void waitNotRunnable(Thread t) throws InterruptedException { + while (t.getState() == State.RUNNABLE) { + Thread.sleep(100); + } + } + + private static Object[] getAnnotatedStack(Thread t) throws Exception { + Class<?> vmStack = Class.forName("dalvik.system.VMStack"); + Method m = vmStack.getDeclaredMethod("getAnnotatedThreadStackTrace", Thread.class); + return (Object[]) m.invoke(null, t); + } + + private static void assertEquals(Object o1, Object o2) { + if (o1 != o2) { + throw new RuntimeException("Expected " + o1 + " == " + o2); + } + } + private static void assertLocks(Object fromTrace, Object... locks) throws Exception { + Object fieldValue = fromTrace.getClass().getDeclaredMethod("getHeldLocks"). + invoke(fromTrace); + assertEquals((Object[]) fieldValue, + (locks == null) ? null : (locks.length == 0 ? null : locks)); + } + private static void assertBlockedOn(Object fromTrace, Object block) throws Exception { + Object fieldValue = fromTrace.getClass().getDeclaredMethod("getBlockedOn"). + invoke(fromTrace); + assertEquals(fieldValue, block); + } + private static void assertEquals(Object[] o1, Object[] o2) { + if (!Arrays.equals(o1, o2)) { + throw new RuntimeException( + "Expected " + Arrays.toString(o1) + " == " + Arrays.toString(o2)); + } + } + private static void assertStackTraceElementStep(Object o) throws Exception { + Object fieldValue = o.getClass().getDeclaredMethod("getStackTraceElement").invoke(o); + if (fieldValue instanceof StackTraceElement) { + StackTraceElement elem = (StackTraceElement) fieldValue; + if (!elem.getMethodName().equals("step")) { + throw new RuntimeException("Expected step method"); + } + return; + } + throw new RuntimeException("Expected StackTraceElement " + fieldValue + " / " + o); + } +} + diff --git a/test/672-checker-throw-method/expected.txt b/test/672-checker-throw-method/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/672-checker-throw-method/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/672-checker-throw-method/info.txt b/test/672-checker-throw-method/info.txt new file mode 100644 index 0000000000..250810be15 --- /dev/null +++ b/test/672-checker-throw-method/info.txt @@ -0,0 +1 @@ +Test detecting throwing methods for code sinking. diff --git a/test/672-checker-throw-method/src/Main.java b/test/672-checker-throw-method/src/Main.java new file mode 100644 index 0000000000..ceb5eb784c --- /dev/null +++ b/test/672-checker-throw-method/src/Main.java @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Tests for detecting throwing methods for code sinking. + */ +public class Main { + + // + // Some "runtime library" methods. + // + + static private void doThrow(String par) { + throw new Error("you are null: " + par); + } + + static private void checkNotNullDirect(Object obj, String par) { + if (obj == null) + throw new Error("you are null: " + par); + } + + static private void checkNotNullSplit(Object obj, String par) { + if (obj == null) + doThrow(par); + } + + // + // Various ways of enforcing non-null parameter. + // In all cases, par should be subject to code sinking. + // + + /// CHECK-START: void Main.doit1(int[]) code_sinking (before) + /// CHECK: begin_block + /// CHECK: <<Str:l\d+>> LoadString + /// CHECK: <<Tst:z\d+>> NotEqual + /// CHECK: If [<<Tst>>] + /// CHECK: end_block + /// CHECK: begin_block + /// CHECK: InvokeVirtual [{{l\d+}},<<Str>>] + /// CHECK: Throw + /// CHECK: end_block + // + /// CHECK-START: void Main.doit1(int[]) code_sinking (after) + /// CHECK: begin_block + /// CHECK: <<Tst:z\d+>> NotEqual + /// CHECK: If [<<Tst>>] + /// CHECK: end_block + /// CHECK: begin_block + /// CHECK: <<Str:l\d+>> LoadString + /// CHECK: InvokeVirtual [{{l\d+}},<<Str>>] + /// CHECK: Throw + /// CHECK: end_block + static public void doit1(int[] a) { + String par = "a"; + if (a == null) + throw new Error("you are null: " + par); + for (int i = 0; i < a.length; i++) { + a[i] = 1; + } + } + + /// CHECK-START: void Main.doit2(int[]) code_sinking (before) + /// CHECK: begin_block + /// CHECK: <<Str:l\d+>> LoadString + /// CHECK: <<Tst:z\d+>> NotEqual + /// CHECK: If [<<Tst>>] + /// CHECK: end_block + /// CHECK: begin_block + /// CHECK: InvokeStaticOrDirect [<<Str>>] method_name:Main.doThrow + /// CHECK: end_block + // + /// CHECK-START: void Main.doit2(int[]) code_sinking (after) + /// CHECK: begin_block + /// CHECK: <<Tst:z\d+>> NotEqual + /// CHECK: If [<<Tst>>] + /// CHECK: end_block + /// CHECK: begin_block + /// CHECK: <<Str:l\d+>> LoadString + /// CHECK: InvokeStaticOrDirect [<<Str>>] method_name:Main.doThrow + /// CHECK: end_block + static public void doit2(int[] a) { + String par = "a"; + if (a == null) + doThrow(par); + for (int i = 0; i < a.length; i++) { + a[i] = 2; + } + } + + /// CHECK-START: void Main.doit3(int[]) code_sinking (before) + /// CHECK: begin_block + /// CHECK: <<Str:l\d+>> LoadString + /// CHECK: <<Tst:z\d+>> NotEqual + /// CHECK: If [<<Tst>>] + /// CHECK: end_block + /// CHECK: begin_block + /// CHECK: InvokeVirtual [{{l\d+}},<<Str>>] + /// CHECK: Throw + /// CHECK: end_block + // + /// CHECK-START: void Main.doit3(int[]) code_sinking (after) + /// CHECK: begin_block + /// CHECK: <<Tst:z\d+>> NotEqual + /// CHECK: If [<<Tst>>] + /// CHECK: end_block + /// CHECK: begin_block + /// CHECK: <<Str:l\d+>> LoadString + /// CHECK: InvokeVirtual [{{l\d+}},<<Str>>] + /// CHECK: Throw + /// CHECK: end_block + static public void doit3(int[] a) { + String par = "a"; + checkNotNullDirect(a, par); + for (int i = 0; i < a.length; i++) { + a[i] = 3; + } + } + + /// CHECK-START: void Main.doit4(int[]) code_sinking (before) + /// CHECK: begin_block + /// CHECK: <<Str:l\d+>> LoadString + /// CHECK: <<Tst:z\d+>> NotEqual + /// CHECK: If [<<Tst>>] + /// CHECK: end_block + /// CHECK: begin_block + /// CHECK: InvokeStaticOrDirect [<<Str>>] method_name:Main.doThrow + /// CHECK: end_block + // + /// CHECK-START: void Main.doit4(int[]) code_sinking (after) + /// CHECK: begin_block + /// CHECK: <<Tst:z\d+>> NotEqual + /// CHECK: If [<<Tst>>] + /// CHECK: end_block + /// CHECK: begin_block + /// CHECK: <<Str:l\d+>> LoadString + /// CHECK: InvokeStaticOrDirect [<<Str>>] method_name:Main.doThrow + /// CHECK: end_block + static public void doit4(int[] a) { + String par = "a"; + checkNotNullSplit(a, par); // resembles Kotlin runtime lib + // (test is lined, doThrow is not) + for (int i = 0; i < a.length; i++) { + a[i] = 4; + } + } + + // Ensures Phi values are merged properly. + static public int doit5(int[] a) { + int t = 100; + String par = "a"; + if (a == null) { + doThrow(par); + } else { + t = 1000; + } + for (int i = 0; i < a.length; i++) { + a[i] = 5; + } + // Phi on t, even though doThrow never reaches. + return t; + } + + // + // Test driver. + // + + static public void main(String[] args) { + int[] a = new int[100]; + for (int i = 0; i < 100; i++) { + a[i] = 0; + } + + try { + doit1(null); + System.out.println("should not reach this!"); + } catch (Error e) { + doit1(a); + } + for (int i = 0; i < 100; i++) { + expectEquals(1, a[i]); + } + + try { + doit2(null); + System.out.println("should not reach this!"); + } catch (Error e) { + doit2(a); + } + for (int i = 0; i < 100; i++) { + expectEquals(2, a[i]); + } + + try { + doit3(null); + System.out.println("should not reach this!"); + } catch (Error e) { + doit3(a); + } + for (int i = 0; i < 100; i++) { + expectEquals(3, a[i]); + } + + try { + doit4(null); + System.out.println("should not reach this!"); + } catch (Error e) { + doit4(a); + } + for (int i = 0; i < 100; i++) { + expectEquals(4, a[i]); + } + + try { + doit5(null); + System.out.println("should not reach this!"); + } catch (Error e) { + expectEquals(1000, doit5(a)); + } + for (int i = 0; i < 100; i++) { + expectEquals(5, a[i]); + } + + System.out.println("passed"); + } + + private static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } +} diff --git a/test/HiddenApi/Main.java b/test/HiddenApi/Main.java new file mode 100644 index 0000000000..187dd6e599 --- /dev/null +++ b/test/HiddenApi/Main.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class Main { + public int ifield; + private static Object sfield; + + void imethod(long x) {} + public static void smethod(Object x) {} + + public native void inmethod(char x); + protected native static void snmethod(Integer x); +} diff --git a/test/knownfailures.json b/test/knownfailures.json index 9db8e9df0a..b39d6072fa 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -412,7 +412,8 @@ { "tests": [ "961-default-iface-resolution-gen", - "964-default-iface-init-gen" + "964-default-iface-init-gen", + "968-default-partial-compile-gen" ], "description": ["Tests that just take too long with jvmti-stress"], "variant": "jvmti-stress | redefine-stress | trace-stress | step-stress" @@ -647,6 +648,13 @@ "bug": "b/64683522" }, { + "tests": ["628-vdex", + "629-vdex-speed", + "634-vdex-duplicate"], + "variant": "cdex-fast", + "description": ["Tests that depend on input-vdex are not supported with compact dex"] + }, + { "tests": "661-oat-writer-layout", "variant": "interp-ac | interpreter | jit | no-dex2oat | no-prebuild | no-image | trace | redefine-stress | jvmti-stress", "description": ["Test is designed to only check --compiler-filter=speed"] |