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.Method;
20 import java.util.concurrent.Semaphore;
21 import java.util.function.ToIntFunction;
22 
23 public class Test1967 {
24   public static final String TARGET_VAR = "TARGET";
25 
reportValue(Object val)26   public static void reportValue(Object val) {
27     if (val instanceof Character) {
28       val = "<Char: " + Character.getNumericValue(((Character) val).charValue()) + ">";
29     }
30     System.out.println(
31         "\tValue is '"
32             + val
33             + "' (class: "
34             + (val != null ? val.getClass().toString() : "null")
35             + ")");
36   }
37 
ObjectMethod(Runnable safepoint)38   public static void ObjectMethod(Runnable safepoint) {
39     Object TARGET = "TARGET OBJECT";
40     safepoint.run();
41     reportValue(TARGET);
42   }
43 
IntMethod(Runnable safepoint)44   public static void IntMethod(Runnable safepoint) {
45     int TARGET = 42;
46     safepoint.run();
47     reportValue(TARGET);
48   }
49 
LongMethod(Runnable safepoint)50   public static void LongMethod(Runnable safepoint) {
51     long TARGET = 9001;
52     safepoint.run();
53     reportValue(TARGET);
54   }
55 
56   public static interface SafepointFunction {
invoke( Thread thread, Method target, Locals.VariableDescription TARGET_desc, int depth)57     public void invoke(
58         Thread thread, Method target, Locals.VariableDescription TARGET_desc, int depth)
59         throws Exception;
60   }
61 
62   public static interface SetterFunction {
SetVar(Thread t, int depth, int slot, Object v)63     public void SetVar(Thread t, int depth, int slot, Object v);
64   }
65 
66   public static interface GetterFunction {
GetVar(Thread t, int depth, int slot)67     public Object GetVar(Thread t, int depth, int slot);
68   }
69 
BadSet( final String type, final SetterFunction get, final Object v, final ToIntFunction<Integer> transform)70   public static SafepointFunction BadSet(
71       final String type,
72       final SetterFunction get,
73       final Object v,
74       final ToIntFunction<Integer> transform) {
75     return new SafepointFunction() {
76       public void invoke(Thread t, Method method, Locals.VariableDescription desc, int depth) {
77         int real_slot = transform.applyAsInt(desc.slot);
78         try {
79           get.SetVar(t, depth, real_slot, v);
80           System.out.println(this + " on " + method + " set value: " + v);
81         } catch (Exception e) {
82           System.out.println(
83               this + " on " + method + " failed to set value " + v + " due to " + e.getMessage());
84         }
85       }
86 
87       public String toString() {
88         return "\"Set" + type + "\"";
89       }
90     };
91   }
92 
93   public static SafepointFunction BadGet(
94       final String type, final GetterFunction get, final ToIntFunction<Integer> transform) {
95     return new SafepointFunction() {
96       public void invoke(Thread t, Method method, Locals.VariableDescription desc, int depth) {
97         int real_slot = transform.applyAsInt(desc.slot);
98         try {
99           Object res = get.GetVar(t, depth, real_slot);
100           System.out.println(this + " on " + method + " got value: " + res);
101         } catch (Exception e) {
102           System.out.println(this + " on " + method + " failed due to " + e.getMessage());
103         }
104       }
105 
106       public String toString() {
107         return "\"Get" + type + "\"";
108       }
109     };
110   }
111 
112   public static class TestCase {
113     public final Method target;
114 
115     public TestCase(Method target) {
116       this.target = target;
117     }
118 
119     public static class ThreadPauser implements Runnable {
120       public final Semaphore sem_wakeup_main;
121       public final Semaphore sem_wait;
122 
123       public ThreadPauser() {
124         sem_wakeup_main = new Semaphore(0);
125         sem_wait = new Semaphore(0);
126       }
127 
128       public void run() {
129         try {
130           sem_wakeup_main.release();
131           sem_wait.acquire();
132         } catch (Exception e) {
133           throw new Error("Error with semaphores!", e);
134         }
135       }
136 
137       public void waitForOtherThreadToPause() throws Exception {
138         sem_wakeup_main.acquire();
139       }
140 
141       public void wakeupOtherThread() throws Exception {
142         sem_wait.release();
143       }
144     }
145 
146     public void exec(final SafepointFunction safepoint) throws Exception {
147       System.out.println("Running " + target + " with " + safepoint + " on remote thread.");
148       final ThreadPauser pause = new ThreadPauser();
149       Thread remote =
150           new Thread(
151               () -> {
152                 try {
153                   target.invoke(null, pause);
154                 } catch (Exception e) {
155                   throw new Error("Error invoking remote thread " + Thread.currentThread(), e);
156                 }
157               },
158               "remote thread for " + target + " with " + safepoint);
159       remote.start();
160       pause.waitForOtherThreadToPause();
161       try {
162         Suspension.suspend(remote);
163         StackTrace.StackFrameData frame = findStackFrame(remote);
164         Locals.VariableDescription desc = findTargetVar(frame.current_location);
165         safepoint.invoke(remote, target, desc, frame.depth);
166       } finally {
167         Suspension.resume(remote);
168         pause.wakeupOtherThread();
169         remote.join();
170       }
171     }
172 
173     private Locals.VariableDescription findTargetVar(long loc) {
174       for (Locals.VariableDescription var : Locals.GetLocalVariableTable(target)) {
175         if (var.start_location <= loc
176             && var.length + var.start_location > loc
177             && var.name.equals(TARGET_VAR)) {
178           return var;
179         }
180       }
181       throw new Error("Unable to find variable " + TARGET_VAR + " in " + target + " at loc " + loc);
182     }
183 
184     private StackTrace.StackFrameData findStackFrame(Thread thr) {
185       for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(thr)) {
186         if (frame.method.equals(target)) {
187           return frame;
188         }
189       }
190       throw new Error("Unable to find stack frame in method " + target + " on thread " + thr);
191     }
192   }
193 
194   public static Method getMethod(String name) throws Exception {
195     return Test1967.class.getDeclaredMethod(name, Runnable.class);
196   }
197 
198   public static void run() throws Exception {
199     Locals.EnableLocalVariableAccess();
200     final TestCase[] MAIN_TEST_CASES =
201         new TestCase[] {
202           new TestCase(getMethod("IntMethod")),
203           new TestCase(getMethod("LongMethod")),
204           new TestCase(getMethod("ObjectMethod")),
205         };
206 
207     final SafepointFunction[] SAFEPOINTS =
208         new SafepointFunction[] {
209           BadGet("Int_at_too_high", Locals::GetLocalVariableInt, (i) -> i + 100),
210           BadGet("Long_at_too_high", Locals::GetLocalVariableLong, (i) -> i + 100),
211           BadGet("Object_at_too_high", Locals::GetLocalVariableObject, (i) -> i + 100),
212           BadSet("Int_at_too_high", Locals::SetLocalVariableInt, Integer.MAX_VALUE, (i) -> i + 100),
213           BadSet("Long_at_too_high", Locals::SetLocalVariableLong, Long.MAX_VALUE, (i) -> i + 100),
214           BadSet(
215               "Object_at_too_high", Locals::SetLocalVariableObject, "NEW_FOR_SET", (i) -> i + 100),
216           BadGet("Int_at_negative", Locals::GetLocalVariableInt, (i) -> -1),
217           BadGet("Long_at_negative", Locals::GetLocalVariableLong, (i) -> -1),
218           BadGet("Object_at_negative", Locals::GetLocalVariableObject, (i) -> -1),
219           BadSet("Int_at_negative", Locals::SetLocalVariableInt, Integer.MAX_VALUE, (i) -> -1),
220           BadSet("Long_at_negative", Locals::SetLocalVariableLong, Long.MAX_VALUE, (i) -> -1),
221           BadSet("Object_at_negative", Locals::SetLocalVariableObject, "NEW_FOR_SET", (i) -> -1),
222         };
223 
224     for (TestCase t : MAIN_TEST_CASES) {
225       for (SafepointFunction s : SAFEPOINTS) {
226         t.exec(s);
227       }
228     }
229   }
230 }
231