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