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.InvocationHandler;
22 import java.lang.reflect.Method;
23 import java.lang.reflect.Proxy;
24 import java.nio.ByteBuffer;
25 import java.util.concurrent.Semaphore;
26 import java.util.Arrays;
27 import java.util.Collection;
28 import java.util.List;
29 import java.util.Set;
30 import java.util.function.Function;
31 import java.util.function.Predicate;
32 import java.util.function.Supplier;
33 import java.util.function.Consumer;
34 
35 public class Test1939 {
36   public static interface SafepointFunction {
invoke( Thread thread, Method target, int depth)37     public void invoke(
38         Thread thread,
39         Method target,
40         int depth) throws Exception;
41   }
42 
43   public static interface GetterFunction {
GetVar(Thread t, int depth)44     public Object GetVar(Thread t, int depth);
45   }
46 
SafeToString(Object o)47   public static String SafeToString(Object o) {
48     if (o instanceof Method && Proxy.isProxyClass(((Method)o).getDeclaringClass())) {
49       // TODO This currently only really works on ART. It would be good if we could make it work for
50       // the RI as well.
51       return o.toString().replaceFirst("Proxy[0-9]+", "__PROXY__");
52     } else {
53       return o.toString();
54     }
55   }
56 
NamedGet(final String type, final GetterFunction get)57   public static SafepointFunction NamedGet(final String type, final GetterFunction get) {
58     return new SafepointFunction() {
59       public void invoke(Thread t, Method method, int depth) {
60         try {
61           Object res = get.GetVar(t, depth);
62           System.out.println(this + " on " + method + " got value: " + SafeToString(res));
63         } catch (Exception e) {
64           System.out.println(this + " on " + method + " failed due to " + e.getMessage());
65         }
66       }
67       public String toString() {
68         return "\"Get" + type + "\"";
69       }
70     };
71   }
72 
73   public static class TestCase {
74     public final Object thiz;
75     public final Method target;
76 
77     public TestCase(Method target) {
78       this(null, target);
79     }
80     public TestCase(Object thiz, Method target) {
81       this.thiz = thiz;
82       this.target = target;
83     }
84 
85     public static class ThreadPauser implements Runnable {
86       public final Semaphore sem_wakeup_main;
87       public final Semaphore sem_wait;
88 
89       public ThreadPauser() {
90         sem_wakeup_main = new Semaphore(0);
91         sem_wait = new Semaphore(0);
92       }
93 
94       public void run() {
95         try {
96           sem_wakeup_main.release();
97           sem_wait.acquire();
98         } catch (Exception e) {
99           throw new Error("Error with semaphores!", e);
100         }
101       }
102 
103       public void waitForOtherThreadToPause() throws Exception {
104         sem_wakeup_main.acquire();
105       }
106 
107       public void wakeupOtherThread() throws Exception {
108         sem_wait.release();
109       }
110     }
111 
112     public void exec(final SafepointFunction safepoint) throws Exception {
113       System.out.println("Running " + target + " with " + safepoint + " on remote thread.");
114       final ThreadPauser pause = new ThreadPauser();
115       Thread remote = new Thread(
116           () -> {
117             try {
118               target.invoke(thiz, pause);
119             } catch (Exception e) {
120               throw new Error("Error invoking remote thread " + Thread.currentThread(), e);
121             }
122           },
123           "remote thread for " + target + " with " + safepoint);
124       remote.start();
125       pause.waitForOtherThreadToPause();
126       try {
127         Suspension.suspend(remote);
128         StackTrace.StackFrameData frame = findStackFrame(remote);
129         safepoint.invoke(remote, target, frame.depth);
130       } finally {
131         Suspension.resume(remote);
132         pause.wakeupOtherThread();
133         remote.join();
134       }
135     }
136 
137     private StackTrace.StackFrameData findStackFrame(Thread thr) {
138       for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(thr)) {
139         if (frame.method.equals(target) ||
140             (frame.method.getName().equals(target.getName()) &&
141              Arrays.deepEquals(frame.method.getParameterTypes(), target.getParameterTypes()) &&
142              ((Method)frame.method).getReturnType().equals(target.getReturnType()))) {
143           return frame;
144         }
145       }
146       throw new Error("Unable to find stack frame in method " + target + " on thread " + thr);
147     }
148   }
149 
150   public static Method getMethod(Class<?> klass, String name) throws Exception {
151     return klass.getDeclaredMethod(name, Runnable.class);
152   }
153 
154   public static interface Foo {
155     public void InterfaceProxyMethod(Runnable r);
156   }
157 
158   public static Object getProxyObject(final Class... k) {
159     return Proxy.newProxyInstance(
160         Test1939.class.getClassLoader(),
161         k,
162         (p, m, a) -> {
163           if (m.getName().equals("toString")) {
164             return "Proxy for " + Arrays.toString(k);
165           } else {
166             ((Runnable)a[0]).run();
167             return null;
168           }
169         });
170   }
171 
172   public static void run() throws Exception {
173     Locals.EnableLocalVariableAccess();
174     TestCase test = new TestCase(
175         getProxyObject(Foo.class), getMethod(Foo.class, "InterfaceProxyMethod"));
176     test.exec(NamedGet("This", Locals::GetLocalInstance));
177     test.exec(NamedGet("LocalReference0", (t, d) -> Locals.GetLocalVariableObject(t, d, 0)));
178     test.exec(NamedGet("ProxyFrameLocation", (t, d) -> Long.valueOf(GetFrameLocation(t, d))));
179     test.exec(NamedGet("ProxyFrameMethod", Test1939::GetFrameMethod));
180   }
181 
182   public static native long GetFrameLocation(Thread thr, int depth);
183   public static native Executable GetFrameMethod(Thread thr, int depth);
184 }
185 
186