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.Predicate;
31 import java.util.function.Supplier;
32 
33 // TODO Rename test to set-get-local-prim
34 
35 public class Test1912 {
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 
NullObjectMethod(Runnable safepoint)46   public static void NullObjectMethod(Runnable safepoint) {
47     Object TARGET = null;
48     safepoint.run();
49     reportValue(TARGET);
50   }
ObjectMethod(Runnable safepoint)51   public static void ObjectMethod(Runnable safepoint) {
52     Object TARGET = "TARGET OBJECT";
53     safepoint.run();
54     reportValue(TARGET);
55   }
BooleanMethod(Runnable safepoint)56   public static void BooleanMethod(Runnable safepoint) {
57     boolean TARGET = false;
58     safepoint.run();
59     reportValue(TARGET);
60   }
ByteMethod(Runnable safepoint)61   public static void ByteMethod(Runnable safepoint) {
62     byte TARGET = 8;
63     safepoint.run();
64     reportValue(TARGET);
65   }
CharMethod(Runnable safepoint)66   public static void CharMethod(Runnable safepoint) {
67     char TARGET = 'q';
68     safepoint.run();
69     reportValue(TARGET);
70   }
ShortMethod(Runnable safepoint)71   public static void ShortMethod(Runnable safepoint) {
72     short TARGET = 321;
73     safepoint.run();
74     reportValue(TARGET);
75   }
IntMethod(Runnable safepoint)76   public static void IntMethod(Runnable safepoint) {
77     int TARGET = 42;
78     safepoint.run();
79     reportValue(TARGET);
80   }
LongMethod(Runnable safepoint)81   public static void LongMethod(Runnable safepoint) {
82     long TARGET = 9001;
83     safepoint.run();
84     reportValue(TARGET);
85   }
FloatMethod(Runnable safepoint)86   public static void FloatMethod(Runnable safepoint) {
87     float TARGET = 1.618f;
88     safepoint.run();
89     reportValue(TARGET);
90   }
DoubleMethod(Runnable safepoint)91   public static void DoubleMethod(Runnable safepoint) {
92     double TARGET = 3.1415d;
93     safepoint.run();
94     reportValue(TARGET);
95   }
96 
97   public static interface SafepointFunction {
98     public void
invoke(Thread thread, Method target, Locals.VariableDescription TARGET_desc, int depth)99     invoke(Thread thread, Method target, Locals.VariableDescription TARGET_desc, int depth)
100         throws Exception;
101   }
102 
103   public static interface SetterFunction {
SetVar(Thread t, int depth, int slot, Object v)104     public void SetVar(Thread t, int depth, int slot, Object v);
105   }
106 
GetVar(Thread t, int depth, int slot)107   public static interface GetterFunction { public Object GetVar(Thread t, int depth, int slot); }
108 
109   public static SafepointFunction
NamedSet(final String type, final SetterFunction get, final Object v)110   NamedSet(final String type, final SetterFunction get, final Object v) {
111     return new SafepointFunction() {
112       public void invoke(Thread t, Method method, Locals.VariableDescription desc, int depth) {
113         try {
114           get.SetVar(t, depth, desc.slot, v);
115           System.out.println(this + " on " + method + " set value: " + v);
116         } catch (Exception e) {
117           System.out.println(this + " on " + method + " failed to set value " + v + " due to " +
118                              e.getMessage());
119         }
120       }
121       public String toString() {
122         return "\"Set" + type + "\"";
123       }
124     };
125   }
126 
127   public static SafepointFunction NamedGet(final String type, final GetterFunction get) {
128     return new SafepointFunction() {
129       public void invoke(Thread t, Method method, Locals.VariableDescription desc, int depth) {
130         try {
131           Object res = get.GetVar(t, depth, desc.slot);
132           System.out.println(this + " on " + method + " got value: " + res);
133         } catch (Exception e) {
134           System.out.println(this + " on " + method + " failed due to " + e.getMessage());
135         }
136       }
137       public String toString() {
138         return "\"Get" + type + "\"";
139       }
140     };
141   }
142 
143   public static class TestCase {
144     public final Method target;
145 
146     public TestCase(Method target) {
147       this.target = target;
148     }
149 
150     public static class ThreadPauser implements Runnable {
151       public final Semaphore sem_wakeup_main;
152       public final Semaphore sem_wait;
153 
154       public ThreadPauser() {
155         sem_wakeup_main = new Semaphore(0);
156         sem_wait = new Semaphore(0);
157       }
158 
159       public void run() {
160         try {
161           sem_wakeup_main.release();
162           sem_wait.acquire();
163         } catch (Exception e) {
164           throw new Error("Error with semaphores!", e);
165         }
166       }
167 
168       public void waitForOtherThreadToPause() throws Exception {
169         sem_wakeup_main.acquire();
170       }
171 
172       public void wakeupOtherThread() throws Exception {
173         sem_wait.release();
174       }
175     }
176 
177     public void exec(final SafepointFunction safepoint) throws Exception {
178       System.out.println("Running " + target + " with " + safepoint + " on remote thread.");
179       final ThreadPauser pause = new ThreadPauser();
180       Thread remote = new Thread(() -> {
181         try {
182           target.invoke(null, pause);
183         } catch (Exception e) {
184           throw new Error("Error invoking remote thread " + Thread.currentThread(), e);
185         }
186       }, "remote thread for " + target + " with " + safepoint);
187       remote.start();
188       pause.waitForOtherThreadToPause();
189       try {
190         Suspension.suspend(remote);
191         StackTrace.StackFrameData frame = findStackFrame(remote);
192         Locals.VariableDescription desc = findTargetVar(frame.current_location);
193         safepoint.invoke(remote, target, desc, frame.depth);
194       } finally {
195         Suspension.resume(remote);
196         pause.wakeupOtherThread();
197         remote.join();
198       }
199     }
200 
201     private Locals.VariableDescription findTargetVar(long loc) {
202       for (Locals.VariableDescription var : Locals.GetLocalVariableTable(target)) {
203         if (var.start_location <= loc && var.length + var.start_location > loc &&
204             var.name.equals(TARGET_VAR)) {
205           return var;
206         }
207       }
208       throw new Error("Unable to find variable " + TARGET_VAR + " in " + target + " at loc " + loc);
209     }
210 
211     private StackTrace.StackFrameData findStackFrame(Thread thr) {
212       for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(thr)) {
213         if (frame.method.equals(target)) {
214           return frame;
215         }
216       }
217       throw new Error("Unable to find stack frame in method " + target + " on thread " + thr);
218     }
219   }
220   public static Method getMethod(String name) throws Exception {
221     return Test1912.class.getDeclaredMethod(name, Runnable.class);
222   }
223 
224   public static void run() throws Exception {
225     Locals.EnableLocalVariableAccess();
226     final TestCase[] MAIN_TEST_CASES = new TestCase[] {
227       new TestCase(getMethod("IntMethod")),    new TestCase(getMethod("LongMethod")),
228       new TestCase(getMethod("FloatMethod")),  new TestCase(getMethod("DoubleMethod")),
229       new TestCase(getMethod("ObjectMethod")), new TestCase(getMethod("NullObjectMethod")),
230     };
231 
232     final SafepointFunction[] SAFEPOINTS = new SafepointFunction[] {
233       NamedGet("Int", Locals::GetLocalVariableInt),
234       NamedGet("Long", Locals::GetLocalVariableLong),
235       NamedGet("Float", Locals::GetLocalVariableFloat),
236       NamedGet("Double", Locals::GetLocalVariableDouble),
237       NamedGet("Object", Locals::GetLocalVariableObject),
238       NamedSet("Int", Locals::SetLocalVariableInt, Integer.MAX_VALUE),
239       NamedSet("Long", Locals::SetLocalVariableLong, Long.MAX_VALUE),
240       NamedSet("Float", Locals::SetLocalVariableFloat, 9.2f),
241       NamedSet("Double", Locals::SetLocalVariableDouble, 12.4d),
242       NamedSet("Object", Locals::SetLocalVariableObject, "NEW_VALUE_FOR_SET"),
243       NamedSet("NullObject", Locals::SetLocalVariableObject, null),
244     };
245 
246     for (TestCase t : MAIN_TEST_CASES) {
247       for (SafepointFunction s : SAFEPOINTS) {
248         t.exec(s);
249       }
250     }
251 
252     // Test int for small values.
253     new TestCase(getMethod("BooleanMethod"))
254         .exec(NamedSet("IntBoolSize", Locals::SetLocalVariableInt, 1));
255     new TestCase(getMethod("ByteMethod"))
256         .exec(NamedSet("IntByteSize", Locals::SetLocalVariableInt, Byte.MAX_VALUE - 1));
257 
258     new TestCase(getMethod("CharMethod"))
259         .exec(NamedSet("IntCharSize", Locals::SetLocalVariableInt, Character.MAX_VALUE - 1));
260     new TestCase(getMethod("ShortMethod"))
261         .exec(NamedSet("IntShortSize", Locals::SetLocalVariableInt, Short.MAX_VALUE - 1));
262   }
263 }
264