/* * 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.ins.method; import static com.sun.max.tele.MaxProcessState.*; import java.util.*; import com.sun.max.ins.*; import com.sun.max.ins.gui.*; import com.sun.max.ins.method.MethodViewContainer.MethodViewManager; import com.sun.max.ins.view.InspectionViews.ViewKind; import com.sun.max.lang.*; import com.sun.max.tele.*; import com.sun.max.tele.method.*; import com.sun.max.tele.object.*; import com.sun.max.unsafe.*; /** * A view that can present one or more code representations of a method. Method views are unique, keyed from * an instance of {@link TeleClassMethodActor}. * <p> * Views are managed via the container class {@link MethodViewContainer}, in which method views are displayed. * <p> * Instance view creation follows the user focus, and a static listener here drives the factory for method views. */ public abstract class MethodView<View_Kind extends MethodView> extends AbstractView<View_Kind> { private static final int TRACE_VALUE = 2; private static final ViewKind VIEW_KIND = ViewKind.METHOD_CODE; private static ViewFocusListener methodFocusListener; /** * Gets (singleton) listener that can be added to focus listeners, which causes the Method view factory * to create/highlight a view on the method in focus. * * @param inspection */ static ViewFocusListener methodFocusListener(final Inspection inspection) { if (methodFocusListener == null) { methodFocusListener = new InspectionFocusAdapter() { @Override public void codeLocationFocusSet(MaxCodeLocation codeLocation, boolean interactiveForNative) { if (codeLocation != null && ViewKind.METHODS.viewManager().isActive()) { try { final MethodView methodView = MethodView.make(inspection, codeLocation, interactiveForNative); if (methodView != null) { methodView.setCodeLocationFocus(); methodView.highlightIfNotVisible(); } } catch (MaxVMBusyException maxVMBusyException) { inspection.announceVMBusyFailure("Can't view method"); } } } }; } return methodFocusListener; } /** * Makes a view displaying code for the method pointed to by the instructionPointer. Should always work for Java * methods. For native functions, only works if the code block is already known to the Inspector or if the * user supplies some additional information at an optional prompt. * * @param address machine code location in the VM. * @param interactive Should user be prompted for additional address information in case the location is unknown * native code. * @return A possibly new view, null if unable to view. * @throws MaxVMBusyException if creation of a new view fails because the VM is unavailable */ private static MethodView make(final Inspection inspection, Address address, boolean interactive) throws MaxVMBusyException { MethodView methodView = null; final MaxCompilation compilation = inspection.vm().machineCode().findCompilation(address); if (compilation != null) { // Java method methodView = make(inspection, compilation, MethodCodeKind.MACHINE_CODE); } else { final TeleNativeFunction nativeFunction = inspection.vm().machineCode().findNativeFunction(address); if (nativeFunction != null) { // Some other kind of known native machine code methodView = make(inspection, nativeFunction); } else if (interactive) { // Code location is not in a Java method or runtime stub or native library. // Give the user a chance to guess at its length so we can register and view it final MutableInnerClassGlobal<MethodView> result = new MutableInnerClassGlobal<MethodView>(); final String defaultDescription = "Native code @0x" + address.toHexString(); new NativeLocationInputDialog(inspection, "Name unknown native code", address, MaxNativeFunction.DEFAULT_DISCONNECTED_CODE_LENGTH, defaultDescription) { @Override public void entered(Address nativeAddress, long nBytes, String enteredName) { try { String name = enteredName; if (name == null || name.equals("")) { name = defaultDescription; } final TeleNativeFunction nativeFunction = vm().machineCode().registerNativeFunction(nativeAddress, nBytes, name); result.setValue(MethodView.make(inspection, nativeFunction)); // inspection.focus().setCodeLocation(new TeleCodeLocation(inspection.vm(), nativeAddress)); } catch (IllegalArgumentException illegalArgumentException) { inspection.gui().errorMessage("Specified native function code range overlaps region already registered in Inpsector"); } catch (MaxVMBusyException maxVMBusyException) { inspection.announceVMBusyFailure("View native code"); } catch (MaxInvalidAddressException e) { inspection.gui().errorMessage("Unable to read memory at " + nativeAddress.to0xHexString()); e.printStackTrace(); } } @Override public boolean isValidSize(long nBytes) { return nBytes > 0; } }; methodView = result.value(); } } return methodView; } private static final Map<MaxMachineCodeRoutine, MethodView> machineCodeToMethodView = new IdentityHashMap<MaxMachineCodeRoutine, MethodView>(); private static final Map<TeleClassMethodActor, MethodView> teleClassMethodActorToMethodView = new IdentityHashMap<TeleClassMethodActor, MethodView>(); /** * Makes a view displaying code for specified code location. Should always work for * Java methods. For native methods, only works if the code block is already known. * * @param codeLocation a code location * @return A possibly new view, null if unable to view. * @throws MaxVMBusyException if trying to visit a method not yet seen and the VM is unavailable */ private static MethodView make(Inspection inspection, MaxCodeLocation codeLocation, boolean interactiveForNative) throws MaxVMBusyException { if (codeLocation.hasAddress()) { return make(inspection, codeLocation.address(), interactiveForNative); } if (codeLocation.hasTeleClassMethodActor()) { // TODO (mlvdv) Select the specified bytecode position return make(inspection, codeLocation.teleClassMethodActor(), MethodCodeKind.BYTECODES); } // Has neither machine nor bytecode location specified. return null; } /** * Display a view for a Java method, showing the kind of code requested if available. If a view for the * method doesn't exist, create a new one and display the kind of code requested if available. if an view for * the method does exist, add a display of the kind of code requested if available. * * @return A possibly new view for the method. * @throws MaxVMBusyException if creation of a new method view fails because the VM is unavailable */ private static JavaMethodView make(Inspection inspection, TeleClassMethodActor teleClassMethodActor, MethodCodeKind codeKind) throws MaxVMBusyException { JavaMethodView javaMethodView = null; // If there are compilations, then inspect in association with the most recent final MaxCompilation compilation = inspection.vm().machineCode().latestCompilation(teleClassMethodActor); if (compilation != null) { return make(inspection, compilation, codeKind); } final MethodView methodView = teleClassMethodActorToMethodView.get(teleClassMethodActor); if (methodView == null) { final MethodViewManager methodViewManager = (MethodViewManager) ViewKind.METHODS.viewManager(); final MethodViewContainer container = methodViewManager.activateView(); inspection.vm().acquireLegacyVMAccess(); try { javaMethodView = new JavaMethodView(inspection, container, teleClassMethodActor, codeKind); container.add(javaMethodView); teleClassMethodActorToMethodView.put(teleClassMethodActor, javaMethodView); } finally { inspection.vm().releaseLegacyVMAccess(); } } else { javaMethodView = (JavaMethodView) methodView; } return javaMethodView; } /** * Gets the {@link MethodView} associated with a specific compilation of a Java method in the VM, * creating a new one if necessary, and makes the requested code visible. * * @return a possibly new view with the specified code visible. * @throws MaxVMBusyException if can't create a new method view because the VM is unavailable */ private static JavaMethodView make(Inspection inspection, MaxCompilation compilation, MethodCodeKind codeKind) throws MaxVMBusyException { JavaMethodView javaMethodView = null; // Is there already a view open that is bound to this compilation? MethodView methodView = machineCodeToMethodView.get(compilation); if (methodView == null) { // No existing view is bound to this compilation; see if there is a view for this method that is // unbound inspection.vm().acquireLegacyVMAccess(); try { TeleClassMethodActor teleClassMethodActor = compilation.getTeleClassMethodActor(); if (teleClassMethodActor != null) { methodView = teleClassMethodActorToMethodView.get(teleClassMethodActor); } final MethodViewManager methodViewManager = (MethodViewManager) ViewKind.METHODS.viewManager(); final MethodViewContainer container = methodViewManager.activateView(); if (methodView == null) { // No existing view exists for this method; create new one bound to this compilation javaMethodView = new JavaMethodView(inspection, container, compilation, codeKind); } else { // A view exists for the method, but not bound to any compilation; bind it to this compilation // TODO (mlvdv) Temp patch; just create a new one in this case too. javaMethodView = new JavaMethodView(inspection, container, compilation, codeKind); } if (javaMethodView != null) { container.add(javaMethodView); machineCodeToMethodView.put(compilation, javaMethodView); } } finally { inspection.vm().releaseLegacyVMAccess(); } } else { // An existing view is bound to this method & compilation; ensure that it has the requested code view javaMethodView = (JavaMethodView) methodView; javaMethodView.viewCodeKind(codeKind); } return javaMethodView; } /** * @return A possibly new view for a block of native code in the VM already known to the Inspector. * @throws MaxVMBusyException if a new view cannot be created because the VM is unavailable */ private static NativeMethodView make(Inspection inspection, MaxNativeFunction nativeFunction) throws MaxVMBusyException { NativeMethodView nativeMethodView = null; MethodView methodView = machineCodeToMethodView.get(nativeFunction); if (methodView == null) { inspection.vm().acquireLegacyVMAccess(); try { final MethodViewManager methodViewManager = (MethodViewManager) ViewKind.METHODS.viewManager(); final MethodViewContainer container = methodViewManager.activateView(); nativeMethodView = new NativeMethodView(inspection, container, nativeFunction); container.add(nativeMethodView); machineCodeToMethodView.put(nativeFunction, nativeMethodView); } finally { inspection.vm().releaseLegacyVMAccess(); } } else { nativeMethodView = (NativeMethodView) methodView; } return nativeMethodView; } private final MethodViewContainer container; protected MethodView(Inspection inspection, MethodViewContainer container) { super(inspection, VIEW_KIND, null); this.container = container; } /** * Updates the code selection to agree with the current focus. */ private void setCodeLocationFocus() { codeLocationFocusSet(inspection().focus().codeLocation(), false); } @Override public void breakpointStateChanged() { // TODO (mlvdv) Data reading PATCH, there should be a more systematic way of handling this. if (vm().state().processState() != TERMINATED) { forceRefresh(); } } public void close() { container.close(this); } public void closeOthers() { container.closeOthers(this); } /** * @return Local {@link MaxMachineCodeRoutine} for the method in the VM; null if not bound to compiled code yet. */ public abstract MaxMachineCodeRoutine compilation(); /** * @return Java method information; null if not known to be associated with a Java method. */ public abstract TeleClassMethodActor teleClassMethodActor(); /** * @return Text suitable for a tool tip. */ public abstract String getToolTip(); /** * Prints the content of the method display. */ public abstract void print(); /** * @param codeViewer Code view that should be closed and removed from the visual inspection; if this is the only * view in the method inspection, then dispose of the method inspection as well. */ public abstract void closeCodeViewer(CodeViewer codeViewer); @Override public void viewClosing() { machineCodeToMethodView.remove(compilation()); teleClassMethodActorToMethodView.remove(teleClassMethodActor()); super.viewClosing(); } }