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 // TODO Rename test to set-get-local-prim 34 35 public class Test1912 { 36 public static final String TARGET_VAR = "TARGET"; 37 reportValue(Object val)38 public static void reportValue(Object val) { 39 if (val instanceof Character) { 40 val = "<Char: " + Character.getNumericValue(((Character)val).charValue()) + ">"; 41 } 42 System.out.println("\tValue is '" + val + 43 "' (class: " + (val != null ? val.getClass().toString() : "null") + ")"); 44 } 45 NullObjectMethod(Runnable safepoint)46 public static void NullObjectMethod(Runnable safepoint) { 47 Object TARGET = null; 48 safepoint.run(); 49 reportValue(TARGET); 50 } ObjectMethod(Runnable safepoint)51 public static void ObjectMethod(Runnable safepoint) { 52 Object TARGET = "TARGET OBJECT"; 53 safepoint.run(); 54 reportValue(TARGET); 55 } BooleanMethod(Runnable safepoint)56 public static void BooleanMethod(Runnable safepoint) { 57 boolean TARGET = false; 58 safepoint.run(); 59 reportValue(TARGET); 60 } ByteMethod(Runnable safepoint)61 public static void ByteMethod(Runnable safepoint) { 62 byte TARGET = 8; 63 safepoint.run(); 64 reportValue(TARGET); 65 } CharMethod(Runnable safepoint)66 public static void CharMethod(Runnable safepoint) { 67 char TARGET = 'q'; 68 safepoint.run(); 69 reportValue(TARGET); 70 } ShortMethod(Runnable safepoint)71 public static void ShortMethod(Runnable safepoint) { 72 short TARGET = 321; 73 safepoint.run(); 74 reportValue(TARGET); 75 } IntMethod(Runnable safepoint)76 public static void IntMethod(Runnable safepoint) { 77 int TARGET = 42; 78 safepoint.run(); 79 reportValue(TARGET); 80 } LongMethod(Runnable safepoint)81 public static void LongMethod(Runnable safepoint) { 82 long TARGET = 9001; 83 safepoint.run(); 84 reportValue(TARGET); 85 } FloatMethod(Runnable safepoint)86 public static void FloatMethod(Runnable safepoint) { 87 float TARGET = 1.618f; 88 safepoint.run(); 89 reportValue(TARGET); 90 } DoubleMethod(Runnable safepoint)91 public static void DoubleMethod(Runnable safepoint) { 92 double TARGET = 3.1415d; 93 safepoint.run(); 94 reportValue(TARGET); 95 } 96 97 public static interface SafepointFunction { 98 public void invoke(Thread thread, Method target, Locals.VariableDescription TARGET_desc, int depth)99 invoke(Thread thread, Method target, Locals.VariableDescription TARGET_desc, int depth) 100 throws Exception; 101 } 102 103 public static interface SetterFunction { SetVar(Thread t, int depth, int slot, Object v)104 public void SetVar(Thread t, int depth, int slot, Object v); 105 } 106 GetVar(Thread t, int depth, int slot)107 public static interface GetterFunction { public Object GetVar(Thread t, int depth, int slot); } 108 109 public static SafepointFunction NamedSet(final String type, final SetterFunction get, final Object v)110 NamedSet(final String type, final SetterFunction get, final Object v) { 111 return new SafepointFunction() { 112 public void invoke(Thread t, Method method, Locals.VariableDescription desc, int depth) { 113 try { 114 get.SetVar(t, depth, desc.slot, v); 115 System.out.println(this + " on " + method + " set value: " + v); 116 } catch (Exception e) { 117 System.out.println(this + " on " + method + " failed to set value " + v + " due to " + 118 e.getMessage()); 119 } 120 } 121 public String toString() { 122 return "\"Set" + type + "\""; 123 } 124 }; 125 } 126 127 public static SafepointFunction NamedGet(final String type, final GetterFunction get) { 128 return new SafepointFunction() { 129 public void invoke(Thread t, Method method, Locals.VariableDescription desc, int depth) { 130 try { 131 Object res = get.GetVar(t, depth, desc.slot); 132 System.out.println(this + " on " + method + " got value: " + res); 133 } catch (Exception e) { 134 System.out.println(this + " on " + method + " failed due to " + e.getMessage()); 135 } 136 } 137 public String toString() { 138 return "\"Get" + type + "\""; 139 } 140 }; 141 } 142 143 public static class TestCase { 144 public final Method target; 145 146 public TestCase(Method target) { 147 this.target = target; 148 } 149 150 public static class ThreadPauser implements Runnable { 151 public final Semaphore sem_wakeup_main; 152 public final Semaphore sem_wait; 153 154 public ThreadPauser() { 155 sem_wakeup_main = new Semaphore(0); 156 sem_wait = new Semaphore(0); 157 } 158 159 public void run() { 160 try { 161 sem_wakeup_main.release(); 162 sem_wait.acquire(); 163 } catch (Exception e) { 164 throw new Error("Error with semaphores!", e); 165 } 166 } 167 168 public void waitForOtherThreadToPause() throws Exception { 169 sem_wakeup_main.acquire(); 170 } 171 172 public void wakeupOtherThread() throws Exception { 173 sem_wait.release(); 174 } 175 } 176 177 public void exec(final SafepointFunction safepoint) throws Exception { 178 System.out.println("Running " + target + " with " + safepoint + " on remote thread."); 179 final ThreadPauser pause = new ThreadPauser(); 180 Thread remote = new Thread(() -> { 181 try { 182 target.invoke(null, pause); 183 } catch (Exception e) { 184 throw new Error("Error invoking remote thread " + Thread.currentThread(), e); 185 } 186 }, "remote thread for " + target + " with " + safepoint); 187 remote.start(); 188 pause.waitForOtherThreadToPause(); 189 try { 190 Suspension.suspend(remote); 191 StackTrace.StackFrameData frame = findStackFrame(remote); 192 Locals.VariableDescription desc = findTargetVar(frame.current_location); 193 safepoint.invoke(remote, target, desc, frame.depth); 194 } finally { 195 Suspension.resume(remote); 196 pause.wakeupOtherThread(); 197 remote.join(); 198 } 199 } 200 201 private Locals.VariableDescription findTargetVar(long loc) { 202 for (Locals.VariableDescription var : Locals.GetLocalVariableTable(target)) { 203 if (var.start_location <= loc && var.length + var.start_location > loc && 204 var.name.equals(TARGET_VAR)) { 205 return var; 206 } 207 } 208 throw new Error("Unable to find variable " + TARGET_VAR + " in " + target + " at loc " + loc); 209 } 210 211 private StackTrace.StackFrameData findStackFrame(Thread thr) { 212 for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(thr)) { 213 if (frame.method.equals(target)) { 214 return frame; 215 } 216 } 217 throw new Error("Unable to find stack frame in method " + target + " on thread " + thr); 218 } 219 } 220 public static Method getMethod(String name) throws Exception { 221 return Test1912.class.getDeclaredMethod(name, Runnable.class); 222 } 223 224 public static void run() throws Exception { 225 Locals.EnableLocalVariableAccess(); 226 final TestCase[] MAIN_TEST_CASES = new TestCase[] { 227 new TestCase(getMethod("IntMethod")), new TestCase(getMethod("LongMethod")), 228 new TestCase(getMethod("FloatMethod")), new TestCase(getMethod("DoubleMethod")), 229 new TestCase(getMethod("ObjectMethod")), new TestCase(getMethod("NullObjectMethod")), 230 }; 231 232 final SafepointFunction[] SAFEPOINTS = new SafepointFunction[] { 233 NamedGet("Int", Locals::GetLocalVariableInt), 234 NamedGet("Long", Locals::GetLocalVariableLong), 235 NamedGet("Float", Locals::GetLocalVariableFloat), 236 NamedGet("Double", Locals::GetLocalVariableDouble), 237 NamedGet("Object", Locals::GetLocalVariableObject), 238 NamedSet("Int", Locals::SetLocalVariableInt, Integer.MAX_VALUE), 239 NamedSet("Long", Locals::SetLocalVariableLong, Long.MAX_VALUE), 240 NamedSet("Float", Locals::SetLocalVariableFloat, 9.2f), 241 NamedSet("Double", Locals::SetLocalVariableDouble, 12.4d), 242 NamedSet("Object", Locals::SetLocalVariableObject, "NEW_VALUE_FOR_SET"), 243 NamedSet("NullObject", Locals::SetLocalVariableObject, null), 244 }; 245 246 for (TestCase t : MAIN_TEST_CASES) { 247 for (SafepointFunction s : SAFEPOINTS) { 248 t.exec(s); 249 } 250 } 251 252 // Test int for small values. 253 new TestCase(getMethod("BooleanMethod")) 254 .exec(NamedSet("IntBoolSize", Locals::SetLocalVariableInt, 1)); 255 new TestCase(getMethod("ByteMethod")) 256 .exec(NamedSet("IntByteSize", Locals::SetLocalVariableInt, Byte.MAX_VALUE - 1)); 257 258 new TestCase(getMethod("CharMethod")) 259 .exec(NamedSet("IntCharSize", Locals::SetLocalVariableInt, Character.MAX_VALUE - 1)); 260 new TestCase(getMethod("ShortMethod")) 261 .exec(NamedSet("IntShortSize", Locals::SetLocalVariableInt, Short.MAX_VALUE - 1)); 262 } 263 } 264