| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| import art.Locals; |
| import art.StackTrace; |
| import art.Suspension; |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.Executable; |
| import java.lang.reflect.Method; |
| import java.nio.ByteBuffer; |
| import java.time.Instant; |
| import java.util.concurrent.Semaphore; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.function.Function; |
| import java.util.function.Predicate; |
| import java.util.function.Supplier; |
| import java.util.function.Consumer; |
| |
| public class Main { |
| public static final int SET_VALUE = 1337; |
| public static final String TARGET_VAR = "TARGET"; |
| |
| public static void main(String[] args) throws Exception { |
| System.loadLibrary(args[0]); |
| Locals.EnableLocalVariableAccess(); |
| runGet(); |
| runSet(); |
| } |
| |
| public static void reportValue(Object val) { |
| System.out.println("\tValue is '" + val + "'"); |
| } |
| |
| public static class IntRunner implements Runnable { |
| private volatile boolean continueBusyLoop; |
| private volatile boolean inBusyLoop; |
| private final boolean expectOsr; |
| public IntRunner(boolean expectOsr) { |
| this.continueBusyLoop = true; |
| this.inBusyLoop = false; |
| this.expectOsr = expectOsr; |
| } |
| public void run() { |
| int TARGET = 42; |
| boolean normalJit = hasJit() && getJitThreshold() != 0; // Excluding JIT-at-first-use. |
| if (normalJit && expectOsr && !Main.isInterpreted()) { |
| System.out.println("Unexpectedly in jit code prior to restarting the JIT!"); |
| } |
| startJit(); |
| // We will suspend the thread during this loop. |
| while (continueBusyLoop) { |
| inBusyLoop = true; |
| } |
| // Wait up to 300 seconds for OSR to kick in if we expect it. If we don't give up after only |
| // 3 seconds. |
| Instant osrDeadline = Instant.now().plusSeconds(expectOsr ? 600 : 3); |
| do { |
| // Don't actually do anything here. |
| inBusyLoop = true; |
| } while (normalJit && !Main.isInOsrCode("run") && osrDeadline.compareTo(Instant.now()) > 0); |
| // We shouldn't be doing OSR since we are using JVMTI and the set prevents OSR. |
| // Set local will also push us to interpreter but the get local may remain in compiled code. |
| if (normalJit) { |
| boolean inOsr = Main.isInOsrCode("run"); |
| if (expectOsr && !inOsr) { |
| throw new Error( |
| "Expected to be in OSR but was not. interpreter: " + Main.isInterpreted()); |
| } else if (!expectOsr && inOsr) { |
| throw new Error( |
| "Expected not to be in OSR but was. interpreter: " + Main.isInterpreted()); |
| } |
| } |
| reportValue(TARGET); |
| } |
| public void waitForBusyLoopStart() { while (!inBusyLoop) {} } |
| public void finish() { |
| continueBusyLoop = false; |
| } |
| } |
| |
| public static void runGet() throws Exception { |
| Method target = IntRunner.class.getDeclaredMethod("run"); |
| // Stop jit temporarily. It will be restarted by the test itself. |
| stopJit(); |
| // Get Int. |
| IntRunner int_runner = new IntRunner(true); |
| Thread target_get = new Thread(int_runner, "GetLocalInt - Target"); |
| target_get.start(); |
| int_runner.waitForBusyLoopStart(); |
| try { |
| Suspension.suspend(target_get); |
| } catch (Exception e) { |
| System.out.println("FAIL: got " + e); |
| e.printStackTrace(); |
| int_runner.finish(); |
| target_get.join(); |
| return; |
| } |
| try { |
| StackTrace.StackFrameData frame = FindStackFrame(target_get, target); |
| int depth = frame.depth; |
| if (depth != 0) { throw new Error("Expected depth 0 but got " + depth); } |
| int slot = FindSlot(frame); |
| int value = Locals.GetLocalVariableInt(target_get, depth, slot); |
| System.out.println("From GetLocalInt(), value is " + value); |
| } finally { |
| Suspension.resume(target_get); |
| int_runner.finish(); |
| target_get.join(); |
| } |
| } |
| |
| public static void runSet() throws Exception { |
| Method target = IntRunner.class.getDeclaredMethod("run"); |
| // Stop jit temporarily. It will be restarted by the test itself. |
| stopJit(); |
| // Set Int. Even if we start out in JIT code somehow we should be pushed out of it. |
| IntRunner int_runner = new IntRunner(false); |
| Thread target_set = new Thread(int_runner, "SetLocalInt - Target"); |
| target_set.start(); |
| int_runner.waitForBusyLoopStart(); |
| try { |
| Suspension.suspend(target_set); |
| } catch (Exception e) { |
| System.out.println("FAIL: got " + e); |
| e.printStackTrace(); |
| int_runner.finish(); |
| target_set.join(); |
| return; |
| } |
| try { |
| StackTrace.StackFrameData frame = FindStackFrame(target_set, target); |
| int depth = frame.depth; |
| if (depth != 0) { throw new Error("Expected depth 0 but got " + depth); } |
| int slot = FindSlot(frame); |
| System.out.println("Setting TARGET to " + SET_VALUE); |
| Locals.SetLocalVariableInt(target_set, depth, slot, SET_VALUE); |
| } finally { |
| Suspension.resume(target_set); |
| int_runner.finish(); |
| target_set.join(); |
| } |
| } |
| |
| public static int FindSlot(StackTrace.StackFrameData frame) throws Exception { |
| long loc = frame.current_location; |
| for (Locals.VariableDescription var : Locals.GetLocalVariableTable(frame.method)) { |
| if (var.start_location <= loc && |
| var.length + var.start_location > loc && |
| var.name.equals(TARGET_VAR)) { |
| return var.slot; |
| } |
| } |
| throw new Error( |
| "Unable to find variable " + TARGET_VAR + " in " + frame.method + " at loc " + loc); |
| } |
| |
| private static StackTrace.StackFrameData FindStackFrame(Thread thr, Method target) { |
| for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(thr)) { |
| if (frame.method.equals(target)) { |
| return frame; |
| } |
| } |
| throw new Error("Unable to find stack frame in method " + target + " on thread " + thr); |
| } |
| |
| public static native boolean isInterpreted(); |
| public static native boolean isInOsrCode(String methodName); |
| public static native boolean stopJit(); |
| public static native boolean startJit(); |
| public static native boolean hasJit(); |
| public static native int getJitThreshold(); |
| } |