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.Method; 20 import java.util.concurrent.Semaphore; 21 import java.util.function.ToIntFunction; 22 23 public class Test1967 { 24 public static final String TARGET_VAR = "TARGET"; 25 reportValue(Object val)26 public static void reportValue(Object val) { 27 if (val instanceof Character) { 28 val = "<Char: " + Character.getNumericValue(((Character) val).charValue()) + ">"; 29 } 30 System.out.println( 31 "\tValue is '" 32 + val 33 + "' (class: " 34 + (val != null ? val.getClass().toString() : "null") 35 + ")"); 36 } 37 ObjectMethod(Runnable safepoint)38 public static void ObjectMethod(Runnable safepoint) { 39 Object TARGET = "TARGET OBJECT"; 40 safepoint.run(); 41 reportValue(TARGET); 42 } 43 IntMethod(Runnable safepoint)44 public static void IntMethod(Runnable safepoint) { 45 int TARGET = 42; 46 safepoint.run(); 47 reportValue(TARGET); 48 } 49 LongMethod(Runnable safepoint)50 public static void LongMethod(Runnable safepoint) { 51 long TARGET = 9001; 52 safepoint.run(); 53 reportValue(TARGET); 54 } 55 56 public static interface SafepointFunction { invoke( Thread thread, Method target, Locals.VariableDescription TARGET_desc, int depth)57 public void invoke( 58 Thread thread, Method target, Locals.VariableDescription TARGET_desc, int depth) 59 throws Exception; 60 } 61 62 public static interface SetterFunction { SetVar(Thread t, int depth, int slot, Object v)63 public void SetVar(Thread t, int depth, int slot, Object v); 64 } 65 66 public static interface GetterFunction { GetVar(Thread t, int depth, int slot)67 public Object GetVar(Thread t, int depth, int slot); 68 } 69 BadSet( final String type, final SetterFunction get, final Object v, final ToIntFunction<Integer> transform)70 public static SafepointFunction BadSet( 71 final String type, 72 final SetterFunction get, 73 final Object v, 74 final ToIntFunction<Integer> transform) { 75 return new SafepointFunction() { 76 public void invoke(Thread t, Method method, Locals.VariableDescription desc, int depth) { 77 int real_slot = transform.applyAsInt(desc.slot); 78 try { 79 get.SetVar(t, depth, real_slot, v); 80 System.out.println(this + " on " + method + " set value: " + v); 81 } catch (Exception e) { 82 System.out.println( 83 this + " on " + method + " failed to set value " + v + " due to " + e.getMessage()); 84 } 85 } 86 87 public String toString() { 88 return "\"Set" + type + "\""; 89 } 90 }; 91 } 92 93 public static SafepointFunction BadGet( 94 final String type, final GetterFunction get, final ToIntFunction<Integer> transform) { 95 return new SafepointFunction() { 96 public void invoke(Thread t, Method method, Locals.VariableDescription desc, int depth) { 97 int real_slot = transform.applyAsInt(desc.slot); 98 try { 99 Object res = get.GetVar(t, depth, real_slot); 100 System.out.println(this + " on " + method + " got value: " + res); 101 } catch (Exception e) { 102 System.out.println(this + " on " + method + " failed due to " + e.getMessage()); 103 } 104 } 105 106 public String toString() { 107 return "\"Get" + type + "\""; 108 } 109 }; 110 } 111 112 public static class TestCase { 113 public final Method target; 114 115 public TestCase(Method target) { 116 this.target = target; 117 } 118 119 public static class ThreadPauser implements Runnable { 120 public final Semaphore sem_wakeup_main; 121 public final Semaphore sem_wait; 122 123 public ThreadPauser() { 124 sem_wakeup_main = new Semaphore(0); 125 sem_wait = new Semaphore(0); 126 } 127 128 public void run() { 129 try { 130 sem_wakeup_main.release(); 131 sem_wait.acquire(); 132 } catch (Exception e) { 133 throw new Error("Error with semaphores!", e); 134 } 135 } 136 137 public void waitForOtherThreadToPause() throws Exception { 138 sem_wakeup_main.acquire(); 139 } 140 141 public void wakeupOtherThread() throws Exception { 142 sem_wait.release(); 143 } 144 } 145 146 public void exec(final SafepointFunction safepoint) throws Exception { 147 System.out.println("Running " + target + " with " + safepoint + " on remote thread."); 148 final ThreadPauser pause = new ThreadPauser(); 149 Thread remote = 150 new Thread( 151 () -> { 152 try { 153 target.invoke(null, pause); 154 } catch (Exception e) { 155 throw new Error("Error invoking remote thread " + Thread.currentThread(), e); 156 } 157 }, 158 "remote thread for " + target + " with " + safepoint); 159 remote.start(); 160 pause.waitForOtherThreadToPause(); 161 try { 162 Suspension.suspend(remote); 163 StackTrace.StackFrameData frame = findStackFrame(remote); 164 Locals.VariableDescription desc = findTargetVar(frame.current_location); 165 safepoint.invoke(remote, target, desc, frame.depth); 166 } finally { 167 Suspension.resume(remote); 168 pause.wakeupOtherThread(); 169 remote.join(); 170 } 171 } 172 173 private Locals.VariableDescription findTargetVar(long loc) { 174 for (Locals.VariableDescription var : Locals.GetLocalVariableTable(target)) { 175 if (var.start_location <= loc 176 && var.length + var.start_location > loc 177 && var.name.equals(TARGET_VAR)) { 178 return var; 179 } 180 } 181 throw new Error("Unable to find variable " + TARGET_VAR + " in " + target + " at loc " + loc); 182 } 183 184 private StackTrace.StackFrameData findStackFrame(Thread thr) { 185 for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(thr)) { 186 if (frame.method.equals(target)) { 187 return frame; 188 } 189 } 190 throw new Error("Unable to find stack frame in method " + target + " on thread " + thr); 191 } 192 } 193 194 public static Method getMethod(String name) throws Exception { 195 return Test1967.class.getDeclaredMethod(name, Runnable.class); 196 } 197 198 public static void run() throws Exception { 199 Locals.EnableLocalVariableAccess(); 200 final TestCase[] MAIN_TEST_CASES = 201 new TestCase[] { 202 new TestCase(getMethod("IntMethod")), 203 new TestCase(getMethod("LongMethod")), 204 new TestCase(getMethod("ObjectMethod")), 205 }; 206 207 final SafepointFunction[] SAFEPOINTS = 208 new SafepointFunction[] { 209 BadGet("Int_at_too_high", Locals::GetLocalVariableInt, (i) -> i + 100), 210 BadGet("Long_at_too_high", Locals::GetLocalVariableLong, (i) -> i + 100), 211 BadGet("Object_at_too_high", Locals::GetLocalVariableObject, (i) -> i + 100), 212 BadSet("Int_at_too_high", Locals::SetLocalVariableInt, Integer.MAX_VALUE, (i) -> i + 100), 213 BadSet("Long_at_too_high", Locals::SetLocalVariableLong, Long.MAX_VALUE, (i) -> i + 100), 214 BadSet( 215 "Object_at_too_high", Locals::SetLocalVariableObject, "NEW_FOR_SET", (i) -> i + 100), 216 BadGet("Int_at_negative", Locals::GetLocalVariableInt, (i) -> -1), 217 BadGet("Long_at_negative", Locals::GetLocalVariableLong, (i) -> -1), 218 BadGet("Object_at_negative", Locals::GetLocalVariableObject, (i) -> -1), 219 BadSet("Int_at_negative", Locals::SetLocalVariableInt, Integer.MAX_VALUE, (i) -> -1), 220 BadSet("Long_at_negative", Locals::SetLocalVariableLong, Long.MAX_VALUE, (i) -> -1), 221 BadSet("Object_at_negative", Locals::SetLocalVariableObject, "NEW_FOR_SET", (i) -> -1), 222 }; 223 224 for (TestCase t : MAIN_TEST_CASES) { 225 for (SafepointFunction s : SAFEPOINTS) { 226 t.exec(s); 227 } 228 } 229 } 230 } 231