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