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