Mercurial > hg > truffle
diff agent/src/share/classes/sun/jvm/hotspot/livejvm/ServiceabilityAgentJVMDIModule.java @ 0:a61af66fc99e jdk7-b24
Initial load
author | duke |
---|---|
date | Sat, 01 Dec 2007 00:00:00 +0000 |
parents | |
children | c18cbe5936b8 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/src/share/classes/sun/jvm/hotspot/livejvm/ServiceabilityAgentJVMDIModule.java Sat Dec 01 00:00:00 2007 +0000 @@ -0,0 +1,415 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.livejvm; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; + +/** Provides Java programming language-level interaction with a live + Java HotSpot VM via the use of the SA's JVMDI module. This is an + experimental mechanism. The BugSpot debugger should be converted + to use the JVMDI/JDWP-based JDI implementation for live process + interaction once the JDI binding for the SA is complete. */ + +public class ServiceabilityAgentJVMDIModule { + private Debugger dbg; + private String[] saLibNames; + private String saLibName; + private boolean attached; + + private boolean suspended; + + private static final int JVMDI_EVENT_BREAKPOINT = 2; + private static final int JVMDI_EVENT_EXCEPTION = 4; + + private static long timeoutMillis = 3000; + + // Values in target process + // Events sent from VM to SA + private CIntegerAccessor saAttached; + private CIntegerAccessor saEventPending; + private CIntegerAccessor saEventKind; + // Exception events + private JNIHandleAccessor saExceptionThread; + private JNIHandleAccessor saExceptionClass; + private JNIid saExceptionMethod; + private CIntegerAccessor saExceptionLocation; + private JNIHandleAccessor saExceptionException; + private JNIHandleAccessor saExceptionCatchClass; + private JNIid saExceptionCatchMethod; + private CIntegerAccessor saExceptionCatchLocation; + // Breakpoint events + private JNIHandleAccessor saBreakpointThread; + private JNIHandleAccessor saBreakpointClass; + private JNIid saBreakpointMethod; + private CIntegerAccessor saBreakpointLocation; + // Commands sent by the SA to the VM + private int SA_CMD_SUSPEND_ALL; + private int SA_CMD_RESUME_ALL; + private int SA_CMD_TOGGLE_BREAKPOINT; + private int SA_CMD_BUF_SIZE; + private CIntegerAccessor saCmdPending; + private CIntegerAccessor saCmdType; + private CIntegerAccessor saCmdResult; + private CStringAccessor saCmdResultErrMsg; + // Toggle breakpoint command arguments + private CStringAccessor saCmdBkptSrcFileName; + private CStringAccessor saCmdBkptPkgName; + private CIntegerAccessor saCmdBkptLineNumber; + private CIntegerAccessor saCmdBkptResWasError; + private CIntegerAccessor saCmdBkptResLineNumber; + private CIntegerAccessor saCmdBkptResBCI; + private CIntegerAccessor saCmdBkptResWasSet; + private CStringAccessor saCmdBkptResMethodName; + private CStringAccessor saCmdBkptResMethodSig; + + public ServiceabilityAgentJVMDIModule(Debugger dbg, String[] saLibNames) { + this.dbg = dbg; + this.saLibNames = saLibNames; + } + + /** Indicates whether a call to attach() should complete without an + exception. */ + public boolean canAttach() { + return setupLookup("SA_CMD_SUSPEND_ALL"); + } + + /** Attempt to initiate a connection with the JVMDI module in the + target VM. */ + public void attach() throws DebuggerException { + if (!canAttach()) { + throw new DebuggerException("Unable to initiate symbol lookup in SA's JVMDI module"); + } + + if (attached) { + throw new DebuggerException("Already attached"); + } + + // Attempt to look up well-known symbols in the target VM. + SA_CMD_SUSPEND_ALL = lookupConstInt("SA_CMD_SUSPEND_ALL"); + SA_CMD_RESUME_ALL = lookupConstInt("SA_CMD_RESUME_ALL"); + SA_CMD_TOGGLE_BREAKPOINT = lookupConstInt("SA_CMD_TOGGLE_BREAKPOINT"); + SA_CMD_BUF_SIZE = lookupConstInt("SA_CMD_BUF_SIZE"); + + saAttached = lookupCInt("saAttached"); + saEventPending = lookupCInt("saEventPending"); + saEventKind = lookupCInt("saEventKind"); + saCmdPending = lookupCInt("saCmdPending"); + saCmdType = lookupCInt("saCmdType"); + saCmdResult = lookupCInt("saCmdResult"); + saCmdResultErrMsg = lookupCString("saCmdResultErrMsg", SA_CMD_BUF_SIZE); + // Toggling of breakpoints + saCmdBkptSrcFileName = lookupCString("saCmdBkptSrcFileName", SA_CMD_BUF_SIZE); + saCmdBkptPkgName = lookupCString("saCmdBkptPkgName", SA_CMD_BUF_SIZE); + saCmdBkptLineNumber = lookupCInt("saCmdBkptLineNumber"); + saCmdBkptResWasError = lookupCInt("saCmdBkptResWasError"); + saCmdBkptResLineNumber = lookupCInt("saCmdBkptResLineNumber"); + saCmdBkptResBCI = lookupCInt("saCmdBkptResBCI"); + saCmdBkptResWasSet = lookupCInt("saCmdBkptResWasSet"); + saCmdBkptResMethodName = lookupCString("saCmdBkptResMethodName", SA_CMD_BUF_SIZE); + saCmdBkptResMethodSig = lookupCString("saCmdBkptResMethodSig", SA_CMD_BUF_SIZE); + + // Check for existence of symbols needed later + // FIXME: should probably cache these since we can't support the + // -Xrun module or the VM getting unloaded anyway + lookup("saExceptionThread"); + lookup("saExceptionClass"); + lookup("saExceptionMethod"); + lookup("saExceptionLocation"); + lookup("saExceptionException"); + lookup("saExceptionCatchClass"); + lookup("saExceptionCatchMethod"); + lookup("saExceptionCatchLocation"); + lookup("saBreakpointThread"); + lookup("saBreakpointClass"); + lookup("saBreakpointMethod"); + lookup("saBreakpointLocation"); + + saAttached.setValue(1); + attached = true; + } + + public void detach() { + saAttached.setValue(0); + attached = false; + saLibName = null; + } + + /** Set the timeout value (in milliseconds) for the VM to reply to + commands. Once this timeout has elapsed, the VM is assumed to + have disconnected. Defaults to 3000 milliseconds (3 seconds). */ + public void setCommandTimeout(long millis) { + timeoutMillis = millis; + } + + /** Get the timeout value (in milliseconds) for the VM to reply to + commands. Once this timeout has elapsed, the VM is assumed to + have disconnected. Defaults to 3000 milliseconds (3 seconds). */ + public long getCommandTimeout() { + return timeoutMillis; + } + + /** Indicates whether a Java debug event is pending */ + public boolean eventPending() { + return (saEventPending.getValue() != 0); + } + + /** Poll for event; returns null if none pending. */ + public Event eventPoll() { + if (saEventPending.getValue() == 0) { + return null; + } + + int kind = (int) saEventKind.getValue(); + switch (kind) { + case JVMDI_EVENT_EXCEPTION: { + JNIHandleAccessor thread = lookupJNIHandle("saExceptionThread"); + JNIHandleAccessor clazz = lookupJNIHandle("saExceptionClass"); + JNIid method = lookupJNIid("saExceptionMethod"); + CIntegerAccessor location = lookupCInt("saExceptionLocation"); + JNIHandleAccessor exception = lookupJNIHandle("saExceptionException"); + JNIHandleAccessor catchClass = lookupJNIHandle("saExceptionCatchClass"); + JNIid catchMethod = lookupJNIid("saExceptionCatchMethod"); + CIntegerAccessor catchLocation = lookupCInt("saExceptionCatchLocation"); + return new ExceptionEvent(thread.getValue(), clazz.getValue(), method, + (int) location.getValue(), exception.getValue(), + catchClass.getValue(), catchMethod, (int) catchLocation.getValue()); + } + + case JVMDI_EVENT_BREAKPOINT: { + JNIHandleAccessor thread = lookupJNIHandle("saBreakpointThread"); + JNIHandleAccessor clazz = lookupJNIHandle("saBreakpointClass"); + JNIid method = lookupJNIid("saBreakpointMethod"); + CIntegerAccessor location = lookupCInt("saBreakpointLocation"); + return new BreakpointEvent(thread.getValue(), clazz.getValue(), + method, (int) location.getValue()); + } + + default: + throw new DebuggerException("Unsupported event type " + kind); + } + } + + /** Continue past current event */ + public void eventContinue() { + saEventPending.setValue(0); + } + + /** Suspend all Java threads in the target VM. Throws + DebuggerException if the VM disconnected. */ + public void suspend() { + saCmdType.setValue(SA_CMD_SUSPEND_ALL); + saCmdPending.setValue(1); + waitForCommandCompletion(); + suspended = true; + } + + /** Resume all Java threads in the target VM. Throws + DebuggerException if the VM disconnected. */ + public void resume() { + saCmdType.setValue(SA_CMD_RESUME_ALL); + saCmdPending.setValue(1); + waitForCommandCompletion(); + suspended = false; + } + + /** Indicates whether all Java threads have been suspended via this + interface. */ + public boolean isSuspended() { + return suspended; + } + + /** Information about toggling of breakpoints */ + public static class BreakpointToggleResult { + private boolean success; + private String errMsg; + private int lineNumber; + private int bci; + private boolean wasSet; + private String methodName; + private String methodSig; + + /** Success constructor */ + public BreakpointToggleResult(int lineNumber, int bci, boolean wasSet, + String methodName, String methodSig) { + this.lineNumber = lineNumber; + this.bci = bci; + this.wasSet = wasSet; + this.methodName = methodName; + this.methodSig = methodSig; + success = true; + } + + /** Failure constructor */ + public BreakpointToggleResult(String errMsg) { + this.errMsg = errMsg; + success = false; + } + + /** Indicates whether this represents a successful return or not */ + public boolean getSuccess() { return success; } + + /** Valid only if getSuccess() returns false */ + public String getErrMsg() { return errMsg; } + + /** Line number at which breakpoint toggle occurred; valid only if + getSuccess() returns true. */ + public int getLineNumber() { return lineNumber; } + + /** BCI at which breakpoint toggle occurred; valid only if + getSuccess() returns true. */ + public int getBCI() { return bci; } + + /** Indicates whether the breakpoint toggle was the set of a + breakpoint or not; valid only if getSuccess() returns true. */ + public boolean getWasSet() { return wasSet; } + + /** Method name in which the breakpoint toggle occurred; valid + only if getSuccess() returns true. */ + public String getMethodName() { return methodName; } + + /** Method signature in which the breakpoint toggle occurred; + valid only if getSuccess() returns true. */ + public String getMethodSignature() { return methodSig; } + } + + /** Toggle a breakpoint. Throws DebuggerException if a real error + occurred; otherwise returns non-null BreakpointToggleResult. The + work of scanning the loaded classes is done in the target VM + because it turns out to be significantly faster than scanning + through the system dictionary from the SA, and interactivity + when setting breakpoints is important. */ + public BreakpointToggleResult toggleBreakpoint(String srcFileName, + String pkgName, + int lineNo) { + saCmdBkptSrcFileName.setValue(srcFileName); + saCmdBkptPkgName.setValue(pkgName); + saCmdBkptLineNumber.setValue(lineNo); + saCmdType.setValue(SA_CMD_TOGGLE_BREAKPOINT); + saCmdPending.setValue(1); + if (waitForCommandCompletion(true)) { + return new BreakpointToggleResult((int) saCmdBkptResLineNumber.getValue(), + (int) saCmdBkptResBCI.getValue(), + (saCmdBkptResWasSet.getValue() != 0), + saCmdBkptResMethodName.getValue(), + saCmdBkptResMethodSig.getValue()); + } else { + return new BreakpointToggleResult(saCmdResultErrMsg.getValue()); + } + } + + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private CIntegerAccessor lookupCInt(String symbolName) { + return new CIntegerAccessor(lookup(symbolName), 4, false); + } + + private CStringAccessor lookupCString(String symbolName, int bufLen) { + return new CStringAccessor(lookup(symbolName), bufLen); + } + + private JNIHandleAccessor lookupJNIHandle(String symbolName) { + return new JNIHandleAccessor(lookup(symbolName), VM.getVM().getObjectHeap()); + } + + private JNIid lookupJNIid(String symbolName) { + Address idAddr = lookup(symbolName).getAddressAt(0); + if (idAddr == null) { + return null; + } + return new JNIid(idAddr, VM.getVM().getObjectHeap()); + } + + private int lookupConstInt(String symbolName) { + Address addr = lookup(symbolName); + return (int) addr.getCIntegerAt(0, 4, false); + } + + private boolean setupLookup(String symbolName) { + if (saLibName == null) { + for (int i = 0; i < saLibNames.length; i++) { + Address addr = dbg.lookup(saLibNames[i], symbolName); + if (addr != null) { + saLibName = saLibNames[i]; + return true; + } + } + return false; + } + return true; + } + + private Address lookup(String symbolName) { + if (saLibName == null) { + for (int i = 0; i < saLibNames.length; i++) { + Address addr = dbg.lookup(saLibNames[i], symbolName); + if (addr != null) { + saLibName = saLibNames[i]; + return addr; + } + } + throw new DebuggerException("Unable to find symbol " + symbolName + " in any of the known names for the SA"); + } + + Address addr = dbg.lookup(saLibName, symbolName); + if (addr == null) { + throw new DebuggerException("Unable to find symbol " + symbolName + " in " + saLibName); + } + return addr; + } + + private void waitForCommandCompletion() { + waitForCommandCompletion(false); + } + + /** Returns true if command succeeded, false if not */ + private boolean waitForCommandCompletion(boolean forBreakpoint) { + long start = System.currentTimeMillis(); + long cur = start; + while ((saCmdPending.getValue() != 0) && + (cur - start < timeoutMillis)) { + try { + java.lang.Thread.currentThread().sleep(10); + } catch (InterruptedException e) { + } + cur = System.currentTimeMillis(); + } + if (saCmdPending.getValue() != 0) { + detach(); + throw new DebuggerException("VM appears to have died"); + } + boolean succeeded = saCmdResult.getValue() == 0; + if (!succeeded && + (!forBreakpoint || saCmdBkptResWasError.getValue() != 0)) { + String err = saCmdResultErrMsg.getValue(); + throw new DebuggerException("Error executing JVMDI command: " + err); + } + return succeeded; + } +}