/* * Copyright (c) 2010, 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.method; import com.sun.cri.ci.*; import com.sun.max.tele.*; import com.sun.max.tele.object.*; import com.sun.max.tele.util.*; import com.sun.max.unsafe.*; import com.sun.max.vm.actor.member.*; // TODO (mlvdv) extend code location to represent source code // TODO (mlvdv) extend code location to represent ranges of instructions // TODO (mlvdv) complete instruction mapping code bytecode <-> machine code, see VmBytecodeBreakpoint /** * Location of a code instruction in the VM. This might be * specified in terms of one or more representations: * compiled machine code, bytecode, source code, or some * combination that represents an equivalent location in each. An instance may * not have all kinds of information. Some kinds of additional information cannot be * determined until an initial reading of the VM state has been completed. Some kinds * of information cannot be determined until a specified class is loaded into the VM. * <p> * A location with an address (subclasses of {@link MachineCodeLocation} is assumed * to refer uniquely to a single compilation of a method, * where the actual method may or (in rare cases) may not be known. * <p> * A location originally specified without an address (subclasses of {@link BytecodeLocation}) * is assumed to refer to the method in general, and by implication to every machine code * compilation. * <p> * This class is intended to encapsulate as many techniques for mapping among code locations * as possible. */ public abstract class CodeLocation extends AbstractVmHolder implements MaxCodeLocation { private final String description; private TeleCompilation compilation; private CiDebugInfo debugInfo = null; private CodeLocation(TeleVM vm, String description) { super(vm); this.description = description; } public final boolean hasAddress() { final RemoteCodePointer codePointer = codePointer(); return codePointer != null && codePointer.isCodeLive(); } public final Address address() { return codePointer() == null ? Address.zero() : codePointer().getAddress(); } public final TeleCompilation compilation() { if (compilation == null && hasAddress()) { compilation = findCompilation(codePointer()); } return compilation; } public final CiDebugInfo debugInfo() { if (debugInfo == null && codePointer() != null && compilation() != null) { final MaxMachineCodeInfo machineCodeInfo = compilation().getMachineCodeInfo(); final int instructionIndex = machineCodeInfo.findInstructionIndex(codePointer().getAddress()); if (instructionIndex >= 0) { debugInfo = machineCodeInfo.debugInfoAt(instructionIndex); } } return debugInfo; } public int bci() { CiDebugInfo info = debugInfo(); if (info == null) { return -1; } CiCodePos codePos = info.codePos; if (codePos == null) { return -1; } while (codePos.caller != null) { codePos = codePos.caller; } return codePos.bci; } public final String description() { return description; } public abstract RemoteCodePointer codePointer(); @Override public String toString() { final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append("{"); if (hasAddress()) { sb.append(" 0x").append(address().toHexString()).append(", "); } if (hasTeleClassMethodActor()) { sb.append(teleClassMethodActor().getName()).append("(), bci=").append(bci()); } sb.append("}"); return sb.toString(); } /** * Attempt to create an abstract key for a method located in the VM. Null if * the information cannot be determined at present, for example if the VM is busy. * * @param teleClassMethodActor location in a method's bytecode, specified by loaded * method description in the VM. * @return a new key describing the method */ protected MethodKey teleClassMethodActorToMethodKey(TeleClassMethodActor teleClassMethodActor) { assert teleClassMethodActor != null; if (vm().tryLock()) { try { return new MethodKey.DefaultMethodKey(teleClassMethodActor().methodActor()); } finally { vm().unlock(); } } return null; } protected TeleCompilation findCompilation(RemoteCodePointer codePointer) { if (codePointer != null) { return vm().machineCode().findCompilation(codePointer); } return null; } /** * A code location that refers to a method in general, and not to any specific compilation. * */ public abstract static class BytecodeLocation extends CodeLocation { private BytecodeLocation(TeleVM vm, String description) { super(vm, description); } @Override public RemoteCodePointer codePointer() { return null; } public boolean isSameAs(MaxCodeLocation codeLocation) { if (this.hasMethodKey() && codeLocation instanceof BytecodeLocation) { return methodKey().equals(codeLocation.methodKey()); } return false; } } /** * A code location that refers to an instruction in a specific compilation of a method, even in the * (occasional) case where details of the method are not yet known, or in the rare * cases where the address isn't yet known because not enough of the VM state * has been modeled to determine it. * */ public abstract static class MachineCodeLocation extends CodeLocation { private volatile MethodKey methodKey = null; private MachineCodeLocation(TeleVM vm, String description) { super(vm, description); } public final boolean isSameAs(MaxCodeLocation codeLocation) { if (this.hasAddress() && codeLocation instanceof MachineCodeLocation) { return address().equals(codeLocation.address()); } return false; } public final boolean hasMethodKey() { return methodKey() != null; } public final MethodKey methodKey() { if (methodKey == null && teleClassMethodActor() != null) { methodKey = teleClassMethodActorToMethodKey(teleClassMethodActor()); } return methodKey; } } /** * A code location in the VM specified by an abstract description of a method, which may * not yet be loaded in the VM. The implied bytecode position in the method is -1; this refers * to the first bytecode instruction and to the beginning of the prologue in any machine code compilation. * It is not bound to any particular compilation, and so never had a machine code address. * * @see MaxCodeLocationManager#createBytecodeLocation(MethodKey, String) */ private static final class MethodKeyLocation extends BytecodeLocation { private final MethodKey methodKey; private volatile TeleClassMethodActor teleClassMethodActor = null; private MethodKeyLocation(TeleVM vm, MethodKey methodKey, String description) { super(vm, description); TeleError.check(methodKey != null); this.methodKey = methodKey; } public boolean hasTeleClassMethodActor() { return teleClassMethodActor() != null; } public TeleClassMethodActor teleClassMethodActor() { if (teleClassMethodActor == null) { teleClassMethodActor = vm().methods().findClassMethodActor(methodKey); } return teleClassMethodActor; } public boolean hasMethodKey() { return true; } public MethodKey methodKey() { return methodKey; } } /** * A code location in the VM specified only as a bytecode position in a loaded classfile method. * It is not bound to any particular compilation, and so never had a machine code address. * * @see MaxCodeLocationManager#createBytecodeLocation(TeleClassMethodActor, int, String) */ private static final class ClassMethodActorLocation extends BytecodeLocation { private final TeleClassMethodActor teleClassMethodActor; private final int bci; private volatile MethodKey methodKey = null; private ClassMethodActorLocation(TeleVM vm, TeleClassMethodActor teleClassMethodActor, int bci, String description) { super(vm, description); TeleError.check(teleClassMethodActor != null); TeleError.check(bci >= -1); this.teleClassMethodActor = teleClassMethodActor; this.bci = bci; } public boolean hasTeleClassMethodActor() { return true; } public TeleClassMethodActor teleClassMethodActor() { return teleClassMethodActor; } @Override public int bci() { return bci; } public boolean hasMethodKey() { return methodKey() != null; } public MethodKey methodKey() { if (methodKey == null) { methodKey = teleClassMethodActorToMethodKey(teleClassMethodActor); } return methodKey; } } /** * A code location in the VM specified only as an location in compiled code. * <p> * Additional information about the compilation, the method, and an equivalent * bytecode location will be discovered when possible. */ private static final class PointerCodeLocation extends MachineCodeLocation { // TODO (mlvdv) distinguish between cases where we are able to locate // a method compilation and those where we are not. The latter would // be typical for external code locations, but there still seems to be // the possibility in the startup cycle of having an address for which // we locate the compilation only somewhat later. private final RemoteCodePointer codePointer; private volatile TeleClassMethodActor teleClassMethodActor = null; private PointerCodeLocation(TeleVM vm, RemoteCodePointer codePointer, String description) { super(vm, description); TeleError.check(codePointer != null); this.codePointer = codePointer; } public boolean hasTeleClassMethodActor() { return teleClassMethodActor() != null; } public TeleClassMethodActor teleClassMethodActor() { if (teleClassMethodActor == null) { if (vm().tryLock()) { try { final TeleCompilation compilation = findCompilation(codePointer); if (compilation != null) { teleClassMethodActor = compilation.getTeleClassMethodActor(); } } finally { vm().unlock(); } } } return teleClassMethodActor; } @Override public RemoteCodePointer codePointer() { return codePointer; } } /** * A code location in the VM specified both as a bytecode position in a loaded classfile and the * memory location of the corresponding machine code instruction in a compilation of the method. * * @see MaxCodeLocationManager#createMachineCodeLocation(Address, TeleClassMethodActor, int, String) */ private static final class ClassMethodActorAddressLocation extends MachineCodeLocation { private final RemoteCodePointer codePointer; private final TeleClassMethodActor teleClassMethodActor; private final int bci; private ClassMethodActorAddressLocation(TeleVM vm, RemoteCodePointer codePointer, TeleClassMethodActor teleClassMethodActor, int bci, String description) { super(vm, description); TeleError.check(codePointer != null); TeleError.check(teleClassMethodActor != null); TeleError.check(bci >= -1); this.codePointer = codePointer; this.teleClassMethodActor = teleClassMethodActor; this.bci = bci; } public boolean hasTeleClassMethodActor() { return true; } public TeleClassMethodActor teleClassMethodActor() { return teleClassMethodActor; } @Override public RemoteCodePointer codePointer() { return codePointer; } } /** * A code location specified as the entry of a method known to be compiled into the * boot image, identified by an annotation in the VM source code and made available * with static accessors. * <p> * The location corresponds to the beginning of the compiled method prologue, which * is equivalent to a bytecode position specification of -1. */ static final class MethodAccessLocation extends MachineCodeLocation { private final TeleMethodAccess teleMethodAccess; private volatile RemoteCodePointer codePointer = null; private volatile TeleClassMethodActor teleClassMethodActor = null; private MethodAccessLocation(TeleVM vm, TeleMethodAccess teleMethodAccess, String description) { super(vm, description); TeleError.check(teleMethodAccess != null); this.teleMethodAccess = teleMethodAccess; } public boolean hasTeleClassMethodActor() { return teleClassMethodActor() != null; } public TeleClassMethodActor teleClassMethodActor() { if (teleClassMethodActor == null) { if (vm().tryLock()) { try { teleClassMethodActor = teleMethodAccess.teleClassMethodActor(); } finally { vm().unlock(); } } } return teleClassMethodActor; } @Override public RemoteCodePointer codePointer() { if (codePointer == null && teleClassMethodActor() != null) { if (vm().tryLock()) { try { final TeleTargetMethod javaTargetMethod = teleClassMethodActor().getCurrentCompilation(); if (javaTargetMethod != null) { codePointer = vm().machineCode().makeCodePointer(javaTargetMethod.callEntryPoint()); } } catch (InvalidCodeAddressException e) { } finally { vm().unlock(); } } } return codePointer; } } /** * Singleton for creating representations of code locations in the VM. */ public static final class VmCodeLocationManager extends AbstractVmHolder implements MaxCodeLocationManager { private static final int TRACE_VALUE = 1; private static VmCodeLocationManager codeLocationManager; /** * Create a manager for creating code locations in VM memory. */ public static VmCodeLocationManager make(TeleVM vm) { if (codeLocationManager == null) { codeLocationManager = new VmCodeLocationManager(vm); } return codeLocationManager; } private VmCodeLocationManager(TeleVM vm) { super(vm); } public BytecodeLocation createBytecodeLocation(MethodKey methodKey, String description) throws TeleError { return new MethodKeyLocation(vm(), methodKey, description); } public BytecodeLocation createBytecodeLocation(TeleClassMethodActor teleClassMethodActor, int bci, String description) { return new ClassMethodActorLocation(vm(), teleClassMethodActor, bci, description); } public MachineCodeLocation createMachineCodeLocation(Address address, String description) throws InvalidCodeAddressException { return new PointerCodeLocation(vm(), vm().machineCode().makeCodePointer(address), description); } public MachineCodeLocation createMachineCodeLocation(Address address, TeleClassMethodActor teleClassMethodActor, int bci, String description) throws TeleError, InvalidCodeAddressException { return new ClassMethodActorAddressLocation(vm(), vm().machineCode().makeCodePointer(address), teleClassMethodActor, bci, description); } public MachineCodeLocation createMachineCodeLocation(RemoteCodePointer codePointer, String description) throws TeleError { return new PointerCodeLocation(vm(), codePointer, description); } /** * Creates a code location in the VM specified by a predefined method accessor for compiled methods in * the boot image. Resolution of the accessor into other VM-related information is delayed, so that * these location instances can be created without any other VM-related services, early in the * startup cycle. * * @param teleMethodAccess a statically defined accessor for a specially marked method in VM code * @param description a human-readable description, suitable for a menu or for debugging * @return a new location * @throws TeleError if teleMethodAccess is null */ public MachineCodeLocation createMachineCodeLocation(TeleMethodAccess teleMethodAccess, String description) throws TeleError { return new MethodAccessLocation(vm(), teleMethodAccess, description); } // // private static HashMap<RemoteCodePointer, WeakReference<MachineCodeLocation> > pointerToLocation = new HashMap<RemoteCodePointer, WeakReference<MachineCodeLocation> >(); // // // private static class Count implements Comparable<Count> { // String string; // int value; // // @Override // public int compareTo(Count o) { // return value - o.value; // } // } // // static HashMap<String, Count> locationsPerString = new HashMap<String, Count>() { // // @Override // public Count get(Object key) { // Count count = super.get(key); // if (count == null) { // count = new Count(); // count.string = (String) key; // put((String) key, count); // } // return count; // } // }; // // static { // if (Trace.hasLevel(1)) { // Runtime.getRuntime().addShutdownHook(new Thread("LocationsPerDescriptionPrinter") { // // @Override // public void run() { // SortedSet<Count> set = new TreeSet<Count>(locationsPerString.values()); // System.out.println("Machine code locations created (by description):"); // for (Count c : set) { // System.out.println(" " + c.value + "\t" + c.string); // } // // // int count = 0; // for (WeakReference<MachineCodeLocation> weakRef : pointerToLocation.values()) { // if (weakRef.get() != null) { // count++; // } // } // System.out.println("Total Locations=" + pointerToLocation.size() + ", active=" + count); // } // }); // } // } } }