/* * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.max.vm.ext.jvmti; import static com.sun.max.vm.MaxineVM.*; import java.util.*; import com.sun.cri.bytecode.*; import com.sun.max.annotate.*; import com.sun.max.unsafe.*; import com.sun.max.vm.*; import com.sun.max.vm.actor.member.*; import com.sun.max.vm.classfile.*; import com.sun.max.vm.classfile.constant.*; import com.sun.max.vm.compiler.RuntimeCompiler.Nature; import com.sun.max.vm.compiler.deopt.*; import com.sun.max.vm.compiler.deps.*; import com.sun.max.vm.compiler.target.*; import com.sun.max.vm.ext.jvmti.JVMTIThreadFunctions.*; import com.sun.max.vm.jni.*; import com.sun.max.vm.log.hosted.*; import com.sun.max.vm.thread.*; /** * Everything concerned with modifications to compiled code for JVMTI code events. */ public class JVMTICode { /** * Handle any change in compiled code for the methods on the (logical) call stack * necessary for the pervasive events such as SINGLE_STEP, FRAME_POP, for a single thread. * This check occurs just before the thread is resumed. */ private static void checkDeOptForEvent(JVMTI.Env jvmtiEnv, VmThread vmThread) { long codeEventSettings = JVMTIEvents.codeEventSettings(jvmtiEnv, vmThread); if (codeEventSettings != 0) { FindAppFramesStackTraceVisitor stackTraceVisitor = SingleThreadStackTraceVmOperation.invoke(vmThread); // we only deopt the top frame, which means we need to handle leaving the frame later. // if we are in thread termination, stack may be empty if (stackTraceVisitor.stackElements.size() > 0) { checkDeOptForMethod(stackTraceVisitor.getStackElement(0).classMethodActor, codeEventSettings); } } else { // is it worth reopting? perhaps if we are resuming without, say, single step set and // the code contains single step event calls. They won't be delivered but they reduce // performance noticeably. } } static void checkDeOptForMethod(ClassMethodActor classMethodActor, long codeEventSettings) { checkDeOptForTargetMethod(classMethodActor.currentTargetMethod(), codeEventSettings); } static void checkDeOptForTargetMethod(TargetMethod targetMethod, long codeEventSettings) { // we check here if the code is already adequate for the settings we want if (JVMTI_DependencyProcessor.checkSettings(targetMethod.classMethodActor, codeEventSettings)) { return; } ArrayList<TargetMethod> targetMethods = new ArrayList<TargetMethod>(); targetMethod.finalizeReferenceMaps(); targetMethods.add(targetMethod); compileAndDeopt(targetMethods); } static void checkDeOptForInvokeInSingleStep(ClassMethodActor classMethodActor, int bci) { CodeAttribute codeAttribute = classMethodActor.codeAttribute(); ConstantPool cp = codeAttribute.cp; int index = Bytes.beU2(codeAttribute.code(), bci + 1); ClassMethodRefConstant calleeClassMethodRef = cp.classMethodAt(index); if (calleeClassMethodRef.isResolvableWithoutClassLoading(cp)) { ClassMethodActor calleeMethodActor = (ClassMethodActor) calleeClassMethodRef.resolve(cp, index); TargetMethod targetMethod = calleeMethodActor.currentTargetMethod(); if (targetMethod != null) { checkDeOptForTargetMethod(targetMethod, JVMTIEvents.E.SINGLE_STEP.bit); } } // hasn't been loaded/compiled yet, instrumentation will happen as part of loading/compiling during invoke } static void compileAndDeopt(ArrayList<TargetMethod> targetMethods) { // compile the methods first, in case of a method used by the compilation system (VM debugging) for (TargetMethod targetMethod : targetMethods) { if (logger.enabled()) { logger.logCompileForDeopt(targetMethod.classMethodActor); } // This forces the compilation vm().compilationBroker.compile(targetMethod.classMethodActor, Nature.BASELINE, true); } // Calling this multiple times for different threads is harmless as it takes care to // filter out already invalidated methods. This may also think it needs to recompile // the method we just compiled but the new TM won't be invalidated so it will just use it. new Deoptimization(targetMethods).go(); } /** * A new breakpoint is being set. * @param classMethodActor */ static void deOptForNewBreakpoint(ClassMethodActor classMethodActor) { TargetMethod targetMethod = classMethodActor.currentTargetMethod(); ArrayList<TargetMethod> inliners = InlinedMethodDependencyProcessor.getInliners(classMethodActor); // There are three possibilities to consider: // 1. It was inlined everywhere so never compiled in isolation (targetMethod == null) && inliners.size() > 0 // 2. It was inlined somewhere but also compiled in isolation (targetMethod != null) && inliners.size() > 0 // 3. It has never been compiled or inlined. (targetMethod == null) && inliners.size() == 0 // Of these case 2 is the least likely. if (targetMethod != null) { // It was compiled already, need to recompile, and may have been inlined // Potentially need to deopt the code in all threads, but note that it // may not be active on any thread stack, in which case we just need to recompile // and patch call sites. If it is not active, Deoptimization.go does // invalidation but does not recompile the method. long codeEventSettings = JVMTIEvents.codeEventSettings(null, null); checkDeOptForMethod(classMethodActor, codeEventSettings); // recheck targetMethod = classMethodActor.currentTargetMethod(); assert targetMethod != null && JVMTI_DependencyProcessor.checkSettings(classMethodActor, codeEventSettings); } else { // Never compiled, but may have been inlined if (inliners.size() == 0) { // Case 3 requires nothing to be done as the breakpoint will be // added when the method is compiled. return; } } // Reach here if never compiled but inlined, or compiled and inlined // Now handle deopt of any inliners. if (inliners.size() > 0) { // all the inliners need to be deopted and the inlinee needs to be (re)compiled (TODO check latter) compileAndDeopt(inliners); // If never compiled, then similar to case 3, else was recompiled already } } static void resumeThreadNotify(JVMTI.Env jvmtiEnv, VmThread vmThread) { checkDeOptForEvent(jvmtiEnv, vmThread); } static void suspendThreadNotify(JVMTI.Env jvmtiEnv, VmThread vmThread) { } static void resumeThreadListNotify(JVMTI.Env jvmtiEnv, Set<VmThread> set) { for (VmThread vmThread : set) { resumeThreadNotify(jvmtiEnv, vmThread); } } static void suspendThreadListNotify(JVMTI.Env jvmtiEnv, Set<VmThread> set) { for (VmThread vmThread : set) { suspendThreadNotify(jvmtiEnv, vmThread); } } // Logging @HOSTED_ONLY @VMLoggerInterface(noTrace = true) private interface JVMTICodeLoggerInterface { void compileForDeopt(@VMLogParam(name = "methodActor") MethodActor methodActor); } private static final JVMTICodeLogger logger = new JVMTICodeLogger(); private static class JVMTICodeLogger extends JVMTICodeLoggerAuto { JVMTICodeLogger() { super("JVMTICode", "log JVMTI compiled code operations"); } @Override protected void logArg(int argNum, Word argValue) { Log.print(MethodID.toMethodActor(MethodID.fromWord(argValue))); } } // START GENERATED CODE private static abstract class JVMTICodeLoggerAuto extends com.sun.max.vm.log.VMLogger { public enum Operation { CompileForDeopt; @SuppressWarnings("hiding") public static final Operation[] VALUES = values(); } private static final int[] REFMAPS = null; protected JVMTICodeLoggerAuto(String name, String optionDescription) { super(name, Operation.VALUES.length, optionDescription, REFMAPS); } @Override public String operationName(int opCode) { return Operation.VALUES[opCode].name(); } @INLINE public final void logCompileForDeopt(MethodActor methodActor) { log(Operation.CompileForDeopt.ordinal(), methodActorArg(methodActor)); } } // END GENERATED CODE }