/* * Copyright (c) 2007, 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.tele.object; import java.io.*; import java.lang.reflect.*; import java.util.*; import com.sun.max.annotate.*; import com.sun.max.jdwp.vm.data.*; import com.sun.max.jdwp.vm.proxy.*; import com.sun.max.program.*; import com.sun.max.tele.*; import com.sun.max.tele.data.*; import com.sun.max.tele.reference.*; import com.sun.max.tele.util.*; import com.sun.max.vm.actor.member.*; import com.sun.max.vm.bytecode.*; import com.sun.max.vm.classfile.*; import com.sun.max.vm.classfile.LineNumberTable.Entry; import com.sun.max.vm.classfile.constant.*; import com.sun.max.vm.compiler.RuntimeCompiler.Nature; import com.sun.max.vm.compiler.target.*; /** * Canonical surrogate for an object of type {@link ClassMethodActor} in the VM. * <p> * This object also provides access to compilations of the method in the VM, of which * there are 0, 1, or 2 in ordinary cases: possibly a "baseline" compilation and possibly * an "optimized" compilation. * <p> * The update strategy for this class relies upon the immutability of the class used to record the current * compilation state for the method: {@link Compilations}. If that object has not changed in * the {@link ClassMethodActor} then the fields holding compilations will not be revisited. * <p> * Note, however, that there can be active compilations of the method in situations where * an activation record exists on the stack for a compilation that might have been subsequently * replaced: deoptimized, replaced by a baseline compilation, and optimized again. No record * of these is kept. Any forgotten compilation in this situation will have been invalidated. * * @see ClassMethodActor * @see Compilations * @see TeleObject */ public abstract class TeleClassMethodActor extends TeleMethodActor implements MethodProvider { private static final int TRACE_VALUE = 2; private static List<TeleTargetMethod> EMPTY = Collections.emptyList(); private TeleObject currentCompiledState = null; /** * Currently available compilations. */ private TeleTargetMethod baselineTargetMethod = null; private TeleTargetMethod optimizedTargetMethod = null; /** * A cached list of currently available compilations. */ private List<TeleTargetMethod> compilations = EMPTY; /** * Constructs a {@link TeleObject} specialized for dealing with the information stored in a VM * {@link ClassMethodActor}. * <p> * This constructor follows no {@linkplain RemoteReference references} and in particular does not attempt to decode the compilation state * of the method. This avoids the infinite regress that can occur when the constructor for a mutually referential * object such as a {@link TeleTargetMethod} attempts to do the same thing. * * @param classMethodActorReference reference to an instance of {@link ClassMethodActor} in the VM. */ protected TeleClassMethodActor(TeleVM vm, RemoteReference classMethodActorReference) { super(vm, classMethodActorReference); } boolean updatingCompiledState; /** {@inheritDoc} * <p> * The compilation history associated with a {@link ClassMethodActor} in the VM * is assumed to be represented by an immutable object that is replaced each * time there is some change in the compilation state. */ @Override protected boolean updateObjectCache(long epoch, StatsPrinter statsPrinter) { if (!super.updateObjectCache(epoch, statsPrinter)) { return false; } try { final RemoteReference compiledStateReference = fields().ClassMethodActor_compiledState.readRemoteReference(reference()); if (compiledStateReference.isZero()) { clearCompiledState(); } else if (updatingCompiledState) { // We're already in an update; prevent infinite recursion Trace.line(1, "Cut off infinite recursion between TeleClassMethodActor.updateObjectCache() and TeleTargetMethod.updateObjectCache()"); } else { updatingCompiledState = true; // the method has been compiled; see if it has changed since the last update. final TeleObject compiledState = objects().makeTeleObject(compiledStateReference); if (compiledState == null) { clearCompiledState(); } else if (compiledState != currentCompiledState) { translateCompiledState(compiledState); currentCompiledState = compiledState; } updatingCompiledState = false; } } catch (DataIOError dataIOError) { updatingCompiledState = false; // If something goes wrong, delay the cache update until next time. return false; } return true; } private void clearCompiledState() { currentCompiledState = null; baselineTargetMethod = null; optimizedTargetMethod = null; compilations = EMPTY; } /** * @return local {@link ClassMethodActor} corresponding the VM's {@link ClassMethodActor} for this method. */ public ClassMethodActor classMethodActor() { return (ClassMethodActor) methodActor(); } @Override public boolean isSubstituted() { final ClassMethodActor classMethodActor = classMethodActor(); if (classMethodActor != null) { return METHOD_SUBSTITUTIONS.Static.findSubstituteFor(classMethodActor) != null; } return false; } @Override public TeleClassMethodActor getTeleClassMethodActorForObject() { return this; } @Override public TeleCodeAttribute getTeleCodeAttribute() { if (vm().tryLock()) { try { final RemoteReference codeAttributeReference = fields().ClassMethodActor_codeAttribute.readRemoteReference(reference()); return (TeleCodeAttribute) objects().makeTeleObject(codeAttributeReference); } finally { vm().unlock(); } } return null; } public TargetMethodAccess[] getTargetMethods() { TeleError.unimplemented(); return null; } /** * Decodes the content of the field in {@link ClassMethodActor} that holds the * compilation history. For efficiency, it is stored differently depending on the * length of the history and whether a compilation is currently underway. This method * must agree with the design of {@link Compilations}. * * @see ClassMethodActor * @see Compilations * @param compiledState an object that represents a polymorphic encoding of the current compilation state */ private void translateCompiledState(TeleObject compiledState) { if (compiledState == null) { clearCompiledState(); } else if (compiledState.classActorForObjectType().javaClass() == Compilations.class) { RemoteReference optimizedReference = fields().Compilations_optimized.readRemoteReference(compiledState.reference()); RemoteReference baselineReference = fields().Compilations_baseline.readRemoteReference(compiledState.reference()); compilations = new ArrayList<TeleTargetMethod>(2); if (optimizedReference.isZero()) { optimizedTargetMethod = null; } else { optimizedTargetMethod = (TeleTargetMethod) objects().makeTeleObject(optimizedReference); compilations.add(optimizedTargetMethod); } if (baselineReference.isZero()) { baselineTargetMethod = null; } else { baselineTargetMethod = (TeleTargetMethod) objects().makeTeleObject(baselineReference); compilations.add(baselineTargetMethod); } } else if (compiledState.classActorForObjectType().javaClass() == Compilation.class) { // this is a compilation that is currently underway, get the previous compiled state from it RemoteReference prevCompilationsReference = fields().Compilation_prevCompilations.readRemoteReference(compiledState.reference()); if (!prevCompilationsReference.isZero()) { translateCompiledState(objects().makeTeleObject(prevCompilationsReference)); } else { // this is the first compilation, no previous state clearCompiledState(); } } } /** * @return the number of times this method has been compiled */ public final int compilationCount() { return compilations.size(); } /** * @return all compilations of the method, in order, oldest first. */ public Iterable<TeleTargetMethod> compilations() { return compilations; } /** * Gets the most recent, most optimized compilation of this method in the VM, * null if no compilations. */ public TeleTargetMethod getCurrentCompilation() { if (optimizedTargetMethod != null) { return optimizedTargetMethod; } return baselineTargetMethod; } /** * @return the specified compilation of the method in the VM, first compilation at index=0; null if no such compilation. */ public TeleTargetMethod getCompilation(Nature nature) { switch (nature) { case BASELINE: return baselineTargetMethod; case OPT: return optimizedTargetMethod; default: return null; } } @Override public LineTableEntry[] getLineTable() { final ClassMethodActor classMethodActor = this.classMethodActor(); final Entry[] entries = classMethodActor.codeAttribute().lineNumberTable().entries(); final LineTableEntry[] result = new LineTableEntry[entries.length]; for (int i = 0; i < result.length; i++) { result[i] = new LineTableEntry(entries[i].bci(), entries[i].lineNumber()); } return result; } @Override public VariableTableEntry[] getVariableTable() { final ClassMethodActor classMethodActor = this.classMethodActor(); final LocalVariableTable.Entry[] entries = classMethodActor.codeAttribute().localVariableTable().entries(); final VariableTableEntry[] result = new VariableTableEntry[entries.length]; for (int i = 0; i < result.length; i++) { String signature = null; if (entries[i].signatureIndex() == 0) { signature = entries[i].descriptor(classMethodActor.codeAttribute().cp).toString(); } else { signature = entries[i].signature(classMethodActor.codeAttribute().cp).toString(); } // TODO: Check if generic signature can be retrieved! final String genericSignature = signature; result[i] = new VariableTableEntry(entries[i].startBCI(), entries[i].length(), entries[i].name(classMethodActor.codeAttribute().cp).string, entries[i].slot(), signature, genericSignature); } return result; } public final String getName() { return actorName().string; } public void writeSummary(PrintStream printStream) { final TeleCodeAttribute codeAttribute = getTeleCodeAttribute(); // Always use the {@link ConstantPool} taken from the {@link CodeAttribute}; in a substituted method, the // constant pool for the bytecodes is the one from the origin of the substitution, not the current holder of the method. final TeleConstantPool teleConstantPool = codeAttribute.getTeleConstantPool(); final ConstantPool constantPool = teleConstantPool.getTeleHolder().classActor().constantPool(); final byte[] methodBytes = codeAttribute.readBytecodes(); final PrintWriter printWriter = new PrintWriter(printStream); printWriter.println("code for: " + classMethodActor().format("%H.%n(%p)")); final BytecodePrinter bytecodePrinter = new BytecodePrinter(printWriter, constantPool); final BytecodeScanner bytecodeScanner = new BytecodeScanner(bytecodePrinter); try { bytecodeScanner.scan(new BytecodeBlock(methodBytes)); } catch (Throwable throwable) { printWriter.flush(); ProgramWarning.message("could not print bytecodes: " + throwable); } printWriter.flush(); } static class NativeStubCodeAttributeDeepCopier extends DeepCopier { TeleCodeAttribute teleCodeAttribute; NativeStubCodeAttributeDeepCopier(DeepCopier parent, TeleCodeAttribute teleCodeAttribute) { super(parent); this.teleCodeAttribute = teleCodeAttribute; } @Override protected Object makeDeepCopy(FieldActor fieldActor, TeleObject teleObject) { if (fieldActor.type().name().contains("ConstantPool") && fieldActor.name().equals("cp")) { return teleCodeAttribute.getTeleConstantPool().deepCopy(this); } return super.makeDeepCopy(fieldActor, teleObject); } } /** * {@inheritDoc} * <p> * Default deep copying behavior is to use local copies of {@link ClassMethodActor} in place of those in the VM, * since they are presumed to be equivalent by virtue of being loaded from the same class files. In particular, * debugging information generated by the compiler is embedded in the {@link ClassMethodActor}'s * {@link CodeAttribute}. * <p> * This can create an inconsistency in the case of auto-generated native stubs, where the bytecodes are created at * runtime <i>and</i> may depend on tracing options set in the target VM. * <p> * In this case, we deep copy the {@link CodeAttribute} from the VM. */ @Override public Object createDeepCopy(DeepCopier context) { Object result = super.createDeepCopy(context); ClassMethodActor classMethodActor = classMethodActor(); if (classMethodActor != null && isNative() && !isSubstituted()) { // By default the CodeAttribute field defaults to the local instance // We force the target copy and the associated ConstantPool TeleCodeAttribute teleCodeAttribute = getTeleCodeAttribute(); CodeAttribute codeAttribute = null; if (teleCodeAttribute != null) { DeepCopier dc = new NativeStubCodeAttributeDeepCopier(context, teleCodeAttribute); codeAttribute = (CodeAttribute) teleCodeAttribute.deepCopy(dc); } Field codeAttributeField = fields().ClassMethodActor_codeAttribute.fieldActor().toJava(); codeAttributeField.setAccessible(true); Field compileeField = fields().ClassMethodActor_compilee.fieldActor().toJava(); compileeField.setAccessible(true); try { // Patch into the local ClassMethodActor instance a copy of the method's code from the VM. codeAttributeField.set(classMethodActor, codeAttribute); // Force the ClassMethodActor's "compilee" field to point to itself, so that the code won't get changed. compileeField.set(classMethodActor, classMethodActor); } catch (IllegalArgumentException e) { TeleWarning.message("Failed to patch local ClassMethodActor with copied code"); e.printStackTrace(); } catch (IllegalAccessException e) { TeleWarning.message("Failed to patch local ClassMethodActor with copied code"); e.printStackTrace(); } Trace.line(TRACE_VALUE, tracePrefix() + "Using copied remote CodeAttribute for " + classMethodActor.name()); } return result; } }