• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.concurrent.Semaphore;
24 import java.util.Arrays;
25 import java.util.Collection;
26 import java.util.List;
27 import java.util.Set;
28 import java.util.function.Function;
29 import java.util.function.Predicate;
30 import java.util.function.Supplier;
31 import java.util.function.Consumer;
32 
33 public class Test1913 {
34   public static final String TARGET_VAR = "TARGET";
35 
36   public static interface TestInterface {
doNothing()37     public default void doNothing() {}
38   }
39   public static class TestClass1 implements TestInterface {
40     public String id;
TestClass1(String id)41     public TestClass1(String id) { this.id = id; }
toString()42     public String toString() { return String.format("TestClass1(\"%s\")", id); }
43   }
44 
45   public static class TestClass1ext extends TestClass1 {
TestClass1ext(String id)46     public TestClass1ext(String id) { super(id); }
toString()47     public String toString() { return String.format("TestClass1ext(\"%s\")", super.toString()); }
48   }
49   public static class TestClass2 {
50     public String id;
TestClass2(String id)51     public TestClass2(String id) { this.id = id; }
toString()52     public String toString() { return String.format("TestClass2(\"%s\")", id); }
53   }
54   public static class TestClass2impl extends TestClass2 implements TestInterface {
TestClass2impl(String id)55     public TestClass2impl(String id) { super(id); }
toString()56     public String toString() { return String.format("TestClass2impl(\"%s\")", super.toString()); }
57   }
58 
reportValue(Object val)59   public static void reportValue(Object val) {
60     System.out.println("\tValue is '" + val + "' (class: "
61         + (val != null ? val.getClass() : "NULL") + ")");
62   }
63 
PrimitiveMethod(Runnable safepoint)64   public static void PrimitiveMethod(Runnable safepoint) {
65     int TARGET = 42;
66     safepoint.run();
67     reportValue(TARGET);
68   }
69 
70   // b/64115302: Needed to make sure that DX doesn't change the type of TARGET to TestClass1.
AsObject(Object o)71   private static Object AsObject(Object o) { return o; }
ObjectMethod(Runnable safepoint)72   public static void ObjectMethod(Runnable safepoint) {
73     Object TARGET = AsObject(new TestClass1("ObjectMethod"));
74     safepoint.run();
75     reportValue(TARGET);
76   }
77 
InterfaceMethod(Runnable safepoint)78   public static void InterfaceMethod(Runnable safepoint) {
79     TestInterface TARGET = new TestClass1("InterfaceMethod");
80     safepoint.run();
81     reportValue(TARGET);
82   }
83 
SpecificClassMethod(Runnable safepoint)84   public static void SpecificClassMethod(Runnable safepoint) {
85     TestClass1 TARGET = new TestClass1("SpecificClassMethod");
86     safepoint.run();
87     reportValue(TARGET);
88   }
89 
90   public static interface SafepointFunction {
invoke( Thread thread, Method target, Locals.VariableDescription TARGET_desc, int depth)91     public void invoke(
92         Thread thread,
93         Method target,
94         Locals.VariableDescription TARGET_desc,
95         int depth) throws Exception;
96   }
97 
98   public static interface SetterFunction {
SetVar(Thread t, int depth, int slot, Object v)99     public void SetVar(Thread t, int depth, int slot, Object v);
100   }
101 
102   public static interface GetterFunction {
GetVar(Thread t, int depth, int slot)103     public Object GetVar(Thread t, int depth, int slot);
104   }
105 
NamedSet( final String type, final SetterFunction get, final Object v)106   public static SafepointFunction NamedSet(
107       final String type, final SetterFunction get, final Object v) {
108     return new SafepointFunction() {
109       public void invoke(Thread t, Method method, Locals.VariableDescription desc, int depth) {
110         try {
111           get.SetVar(t, depth, desc.slot, v);
112           System.out.println(this + " on " + method + " set value: " + v);
113         } catch (Exception e) {
114           System.out.println(
115               this + " on " + method + " failed to set value " + v + " due to " + e.getMessage());
116         }
117       }
118       public String toString() {
119         return "\"Set" + type + "\"";
120       }
121     };
122   }
123 
124   public static SafepointFunction NamedGet(final String type, final GetterFunction get) {
125     return new SafepointFunction() {
126       public void invoke(Thread t, Method method, Locals.VariableDescription desc, int depth) {
127         try {
128           Object res = get.GetVar(t, depth, desc.slot);
129           System.out.println(this + " on " + method + " got value: " + res);
130         } catch (Exception e) {
131           System.out.println(this + " on " + method + " failed due to " + e.getMessage());
132         }
133       }
134       public String toString() {
135         return "\"Get" + type + "\"";
136       }
137     };
138   }
139 
140   public static class TestCase {
141     public final Method target;
142 
143     public TestCase(Method target) {
144       this.target = target;
145     }
146 
147     public static class ThreadPauser implements Runnable {
148       public final Semaphore sem_wakeup_main;
149       public final Semaphore sem_wait;
150 
151       public ThreadPauser() {
152         sem_wakeup_main = new Semaphore(0);
153         sem_wait = new Semaphore(0);
154       }
155 
156       public void run() {
157         try {
158           sem_wakeup_main.release();
159           sem_wait.acquire();
160         } catch (Exception e) {
161           throw new Error("Error with semaphores!", e);
162         }
163       }
164 
165       public void waitForOtherThreadToPause() throws Exception {
166         sem_wakeup_main.acquire();
167       }
168 
169       public void wakeupOtherThread() throws Exception {
170         sem_wait.release();
171       }
172     }
173 
174     public void exec(final SafepointFunction safepoint) throws Exception {
175       System.out.println("Running " + target + " with " + safepoint + " on remote thread.");
176       final ThreadPauser pause = new ThreadPauser();
177       Thread remote = new Thread(
178           () -> {
179             try {
180               target.invoke(null, pause);
181             } catch (Exception e) {
182               throw new Error("Error invoking remote thread " + Thread.currentThread(), e);
183             }
184           },
185           "remote thread for " + target + " with " + safepoint);
186       remote.start();
187       pause.waitForOtherThreadToPause();
188       try {
189         Suspension.suspend(remote);
190         StackTrace.StackFrameData frame = findStackFrame(remote);
191         Locals.VariableDescription desc = findTargetVar(frame.current_location);
192         safepoint.invoke(remote, target, desc, frame.depth);
193       } finally {
194         Suspension.resume(remote);
195         pause.wakeupOtherThread();
196         remote.join();
197       }
198     }
199 
200     private Locals.VariableDescription findTargetVar(long loc) {
201       for (Locals.VariableDescription var : Locals.GetLocalVariableTable(target)) {
202         if (var.start_location <= loc &&
203             var.length + var.start_location > loc &&
204             var.name.equals(TARGET_VAR)) {
205           return var;
206         }
207       }
208       throw new Error(
209           "Unable to find variable " + TARGET_VAR + " in " + target + " at loc " + loc);
210     }
211 
212     private StackTrace.StackFrameData findStackFrame(Thread thr) {
213       for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(thr)) {
214         if (frame.method.equals(target)) {
215           return frame;
216         }
217       }
218       throw new Error("Unable to find stack frame in method " + target + " on thread " + thr);
219     }
220   }
221   public static Method getMethod(String name) throws Exception {
222     return Test1913.class.getDeclaredMethod(name, Runnable.class);
223   }
224 
225   public static void run() throws Exception {
226     Locals.EnableLocalVariableAccess();
227     final TestCase[] MAIN_TEST_CASES = new TestCase[] {
228       new TestCase(getMethod("ObjectMethod")),
229       new TestCase(getMethod("InterfaceMethod")),
230       new TestCase(getMethod("SpecificClassMethod")),
231       new TestCase(getMethod("PrimitiveMethod")),
232     };
233 
234     final SetterFunction set_obj = Locals::SetLocalVariableObject;
235     final SafepointFunction[] SAFEPOINTS = new SafepointFunction[] {
236       NamedGet("GetObject",      Locals::GetLocalVariableObject),
237       NamedSet("Null",           set_obj, null),
238       NamedSet("TestClass1",     set_obj, new TestClass1("Set TestClass1")),
239       NamedSet("TestClass1ext",  set_obj, new TestClass1ext("Set TestClass1ext")),
240       NamedSet("TestClass2",     set_obj, new TestClass2("Set TestClass2")),
241       NamedSet("TestClass2impl", set_obj, new TestClass2impl("Set TestClass2impl")),
242     };
243 
244     for (TestCase t: MAIN_TEST_CASES) {
245       for (SafepointFunction s : SAFEPOINTS) {
246         t.exec(s);
247       }
248     }
249   }
250 }
251 
252