1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package art;
18 
19 import java.lang.reflect.Constructor;
20 import java.lang.reflect.Executable;
21 import java.lang.reflect.Method;
22 import java.nio.ByteBuffer;
23 import java.util.Arrays;
24 import java.util.Collection;
25 import java.util.List;
26 import java.util.Set;
27 import java.util.concurrent.Semaphore;
28 import java.util.function.Consumer;
29 import java.util.function.Function;
30 import java.util.function.IntConsumer;
31 import java.util.function.IntFunction;
32 import java.util.function.Predicate;
33 import java.util.function.Supplier;
34 
35 public class Test1965 {
36   public static final String TARGET_VAR = "TARGET";
37 
reportValue(Object val)38   public static void reportValue(Object val) {
39     if (val instanceof Character) {
40       val = "<Char: " + Character.getNumericValue(((Character)val).charValue()) + ">";
41     }
42     System.out.println("\tValue is '" + val +
43                        "' (class: " + (val != null ? val.getClass().toString() : "null") + ")");
44   }
45 
46   public static interface SafepointFunction {
invoke(Thread thread, Method target, int slot, int depth)47     public void invoke(Thread thread, Method target, int slot, int depth) throws Exception;
48   }
49 
50   public static interface SetterFunction {
SetVar(Thread t, int depth, int slot, Object v)51     public void SetVar(Thread t, int depth, int slot, Object v);
52   }
53 
GetVar(Thread t, int depth, int slot)54   public static interface GetterFunction { public Object GetVar(Thread t, int depth, int slot); }
55 
56   public static SafepointFunction
NamedSet(final String type, final SetterFunction get, final Object v)57   NamedSet(final String type, final SetterFunction get, final Object v) {
58     return new SafepointFunction() {
59       public void invoke(Thread t, Method method, int slot, int depth) {
60         try {
61           get.SetVar(t, depth, slot, v);
62           System.out.println(this + " on " + method + " set value: " + v);
63         } catch (Exception e) {
64           System.out.println(this + " on " + method + " failed to set value " + v + " due to " +
65                              e.getMessage());
66         }
67       }
68       public String toString() {
69         return "\"Set" + type + "\"";
70       }
71     };
72   }
73 
74   public static SafepointFunction NamedGet(final String type, final GetterFunction get) {
75     return new SafepointFunction() {
76       public void invoke(Thread t, Method method, int slot, int depth) {
77         try {
78           Object res = get.GetVar(t, depth, slot);
79           System.out.println(this + " on " + method + " got value: " + res);
80         } catch (Exception e) {
81           System.out.println(this + " on " + method + " failed due to " + e.getMessage());
82         }
83       }
84       public String toString() {
85         return "\"Get" + type + "\"";
86       }
87     };
88   }
89 
90   public static class TestCase {
91     public final Method target;
92 
93     public TestCase(Method target) {
94       this.target = target;
95     }
96 
97     public static class ThreadPauser implements IntConsumer {
98       public final Semaphore sem_wakeup_main;
99       public final Semaphore sem_wait;
100       public int slot = -1;
101 
102       public ThreadPauser() {
103         sem_wakeup_main = new Semaphore(0);
104         sem_wait = new Semaphore(0);
105       }
106 
107       public void accept(int v) {
108         try {
109           slot = v;
110           sem_wakeup_main.release();
111           sem_wait.acquire();
112         } catch (Exception e) {
113           throw new Error("Error with semaphores!", e);
114         }
115       }
116 
117       public void waitForOtherThreadToPause() throws Exception {
118         sem_wakeup_main.acquire();
119       }
120 
121       public void wakeupOtherThread() throws Exception {
122         sem_wait.release();
123       }
124     }
125 
126     public void exec(final SafepointFunction safepoint) throws Exception {
127       System.out.println("Running " + target + " with " + safepoint + " on remote thread.");
128       final ThreadPauser pause = new ThreadPauser();
129       final Consumer<?> reporter = Test1965::reportValue;
130       Thread remote = new Thread(() -> {
131         try {
132           target.invoke(null, pause, reporter);
133         } catch (Exception e) {
134           throw new Error("Error invoking remote thread " + Thread.currentThread(), e);
135         }
136       }, "remote thread for " + target + " with " + safepoint);
137       remote.start();
138       pause.waitForOtherThreadToPause();
139       try {
140         Suspension.suspend(remote);
141         StackTrace.StackFrameData frame = findStackFrame(remote);
142         safepoint.invoke(remote, target, pause.slot, frame.depth);
143       } finally {
144         Suspension.resume(remote);
145         pause.wakeupOtherThread();
146         remote.join();
147       }
148     }
149 
150     private Locals.VariableDescription findTargetVar(long loc) {
151       for (Locals.VariableDescription var : Locals.GetLocalVariableTable(target)) {
152         if (var.start_location <= loc && var.length + var.start_location > loc &&
153             var.name.equals(TARGET_VAR)) {
154           return var;
155         }
156       }
157       throw new Error("Unable to find variable " + TARGET_VAR + " in " + target + " at loc " + loc);
158     }
159 
160     private StackTrace.StackFrameData findStackFrame(Thread thr) {
161       for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(thr)) {
162         if (frame.method.equals(target)) {
163           return frame;
164         }
165       }
166       throw new Error("Unable to find stack frame in method " + target + " on thread " + thr);
167     }
168   }
169   public static Method getMethod(String name) throws Exception {
170     return Class.forName("art_test.TestCases1965")
171         .getDeclaredMethod(name, IntConsumer.class, Consumer.class);
172   }
173 
174   public static void run() throws Exception {
175     Locals.EnableLocalVariableAccess();
176     final TestCase[] MAIN_TEST_CASES = new TestCase[] {
177       new TestCase(getMethod("IntMethod")),    new TestCase(getMethod("LongMethod")),
178       new TestCase(getMethod("FloatMethod")),  new TestCase(getMethod("DoubleMethod")),
179       new TestCase(getMethod("ObjectMethod")), new TestCase(getMethod("NullObjectMethod")),
180     };
181 
182     final SafepointFunction[] SAFEPOINTS = new SafepointFunction[] {
183       NamedGet("Int", Locals::GetLocalVariableInt),
184       NamedGet("Long", Locals::GetLocalVariableLong),
185       NamedGet("Float", Locals::GetLocalVariableFloat),
186       NamedGet("Double", Locals::GetLocalVariableDouble),
187       NamedGet("Object", Locals::GetLocalVariableObject),
188       NamedSet("Int", Locals::SetLocalVariableInt, Integer.MAX_VALUE),
189       NamedSet("Long", Locals::SetLocalVariableLong, Long.MAX_VALUE),
190       NamedSet("Float", Locals::SetLocalVariableFloat, 9.2f),
191       NamedSet("Double", Locals::SetLocalVariableDouble, 12.4d),
192       NamedSet("Object", Locals::SetLocalVariableObject, "NEW_VALUE_FOR_SET"),
193       NamedSet("NullObject", Locals::SetLocalVariableObject, null),
194     };
195 
196     for (TestCase t : MAIN_TEST_CASES) {
197       for (SafepointFunction s : SAFEPOINTS) {
198         t.exec(s);
199       }
200     }
201 
202     // Test int for small values.
203     new TestCase(getMethod("BooleanMethod"))
204         .exec(NamedSet("IntBoolSize", Locals::SetLocalVariableInt, 1));
205     new TestCase(getMethod("ByteMethod"))
206         .exec(NamedSet("IntByteSize", Locals::SetLocalVariableInt, Byte.MAX_VALUE - 1));
207 
208     new TestCase(getMethod("CharMethod"))
209         .exec(NamedSet("IntCharSize", Locals::SetLocalVariableInt, Character.MAX_VALUE - 1));
210     new TestCase(getMethod("ShortMethod"))
211         .exec(NamedSet("IntShortSize", Locals::SetLocalVariableInt, Short.MAX_VALUE - 1));
212   }
213 }
214