hotspot/agent/src/share/classes/sun/jvm/hotspot/livejvm/ServiceabilityAgentJVMDIModule.java
changeset 15830 f89fb3fb1554
parent 15829 72415be10117
parent 15821 0e39dbe7fe97
child 15832 6c7a82c0b538
equal deleted inserted replaced
15829:72415be10117 15830:f89fb3fb1554
     1 /*
       
     2  * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  *
       
    23  */
       
    24 
       
    25 package sun.jvm.hotspot.livejvm;
       
    26 
       
    27 import sun.jvm.hotspot.debugger.*;
       
    28 import sun.jvm.hotspot.oops.*;
       
    29 import sun.jvm.hotspot.runtime.*;
       
    30 
       
    31 /** Provides Java programming language-level interaction with a live
       
    32     Java HotSpot VM via the use of the SA's JVMDI module. This is an
       
    33     experimental mechanism. The BugSpot debugger should be converted
       
    34     to use the JVMDI/JDWP-based JDI implementation for live process
       
    35     interaction once the JDI binding for the SA is complete. */
       
    36 
       
    37 public class ServiceabilityAgentJVMDIModule {
       
    38   private Debugger dbg;
       
    39   private String[] saLibNames;
       
    40   private String   saLibName;
       
    41   private boolean  attached;
       
    42 
       
    43   private boolean  suspended;
       
    44 
       
    45   private static final int JVMDI_EVENT_BREAKPOINT = 2;
       
    46   private static final int JVMDI_EVENT_EXCEPTION = 4;
       
    47 
       
    48   private static long timeoutMillis = 3000;
       
    49 
       
    50   // Values in target process
       
    51   // Events sent from VM to SA
       
    52   private CIntegerAccessor saAttached;
       
    53   private CIntegerAccessor saEventPending;
       
    54   private CIntegerAccessor saEventKind;
       
    55   // Exception events
       
    56   private JNIHandleAccessor saExceptionThread;
       
    57   private JNIHandleAccessor saExceptionClass;
       
    58   private JNIid             saExceptionMethod;
       
    59   private CIntegerAccessor  saExceptionLocation;
       
    60   private JNIHandleAccessor saExceptionException;
       
    61   private JNIHandleAccessor saExceptionCatchClass;
       
    62   private JNIid             saExceptionCatchMethod;
       
    63   private CIntegerAccessor  saExceptionCatchLocation;
       
    64   // Breakpoint events
       
    65   private JNIHandleAccessor saBreakpointThread;
       
    66   private JNIHandleAccessor saBreakpointClass;
       
    67   private JNIid             saBreakpointMethod;
       
    68   private CIntegerAccessor  saBreakpointLocation;
       
    69   // Commands sent by the SA to the VM
       
    70   private int               SA_CMD_SUSPEND_ALL;
       
    71   private int               SA_CMD_RESUME_ALL;
       
    72   private int               SA_CMD_TOGGLE_BREAKPOINT;
       
    73   private int               SA_CMD_BUF_SIZE;
       
    74   private CIntegerAccessor  saCmdPending;
       
    75   private CIntegerAccessor  saCmdType;
       
    76   private CIntegerAccessor  saCmdResult;
       
    77   private CStringAccessor   saCmdResultErrMsg;
       
    78   // Toggle breakpoint command arguments
       
    79   private CStringAccessor   saCmdBkptSrcFileName;
       
    80   private CStringAccessor   saCmdBkptPkgName;
       
    81   private CIntegerAccessor  saCmdBkptLineNumber;
       
    82   private CIntegerAccessor  saCmdBkptResWasError;
       
    83   private CIntegerAccessor  saCmdBkptResLineNumber;
       
    84   private CIntegerAccessor  saCmdBkptResBCI;
       
    85   private CIntegerAccessor  saCmdBkptResWasSet;
       
    86   private CStringAccessor   saCmdBkptResMethodName;
       
    87   private CStringAccessor   saCmdBkptResMethodSig;
       
    88 
       
    89   public ServiceabilityAgentJVMDIModule(Debugger dbg, String[] saLibNames) {
       
    90     this.dbg = dbg;
       
    91     this.saLibNames = saLibNames;
       
    92   }
       
    93 
       
    94   /** Indicates whether a call to attach() should complete without an
       
    95       exception. */
       
    96   public boolean canAttach() {
       
    97     return setupLookup("SA_CMD_SUSPEND_ALL");
       
    98   }
       
    99 
       
   100   /** Attempt to initiate a connection with the JVMDI module in the
       
   101       target VM. */
       
   102   public void attach() throws DebuggerException {
       
   103     if (!canAttach()) {
       
   104       throw new DebuggerException("Unable to initiate symbol lookup in SA's JVMDI module");
       
   105     }
       
   106 
       
   107     if (attached) {
       
   108       throw new DebuggerException("Already attached");
       
   109     }
       
   110 
       
   111     // Attempt to look up well-known symbols in the target VM.
       
   112     SA_CMD_SUSPEND_ALL      = lookupConstInt("SA_CMD_SUSPEND_ALL");
       
   113     SA_CMD_RESUME_ALL       = lookupConstInt("SA_CMD_RESUME_ALL");
       
   114     SA_CMD_TOGGLE_BREAKPOINT = lookupConstInt("SA_CMD_TOGGLE_BREAKPOINT");
       
   115     SA_CMD_BUF_SIZE         = lookupConstInt("SA_CMD_BUF_SIZE");
       
   116 
       
   117     saAttached              = lookupCInt("saAttached");
       
   118     saEventPending          = lookupCInt("saEventPending");
       
   119     saEventKind             = lookupCInt("saEventKind");
       
   120     saCmdPending            = lookupCInt("saCmdPending");
       
   121     saCmdType               = lookupCInt("saCmdType");
       
   122     saCmdResult             = lookupCInt("saCmdResult");
       
   123     saCmdResultErrMsg       = lookupCString("saCmdResultErrMsg", SA_CMD_BUF_SIZE);
       
   124     // Toggling of breakpoints
       
   125     saCmdBkptSrcFileName    = lookupCString("saCmdBkptSrcFileName", SA_CMD_BUF_SIZE);
       
   126     saCmdBkptPkgName        = lookupCString("saCmdBkptPkgName", SA_CMD_BUF_SIZE);
       
   127     saCmdBkptLineNumber     = lookupCInt("saCmdBkptLineNumber");
       
   128     saCmdBkptResWasError    = lookupCInt("saCmdBkptResWasError");
       
   129     saCmdBkptResLineNumber  = lookupCInt("saCmdBkptResLineNumber");
       
   130     saCmdBkptResBCI         = lookupCInt("saCmdBkptResBCI");
       
   131     saCmdBkptResWasSet      = lookupCInt("saCmdBkptResWasSet");
       
   132     saCmdBkptResMethodName  = lookupCString("saCmdBkptResMethodName", SA_CMD_BUF_SIZE);
       
   133     saCmdBkptResMethodSig   = lookupCString("saCmdBkptResMethodSig", SA_CMD_BUF_SIZE);
       
   134 
       
   135     // Check for existence of symbols needed later
       
   136     // FIXME: should probably cache these since we can't support the
       
   137     // -Xrun module or the VM getting unloaded anyway
       
   138     lookup("saExceptionThread");
       
   139     lookup("saExceptionClass");
       
   140     lookup("saExceptionMethod");
       
   141     lookup("saExceptionLocation");
       
   142     lookup("saExceptionException");
       
   143     lookup("saExceptionCatchClass");
       
   144     lookup("saExceptionCatchMethod");
       
   145     lookup("saExceptionCatchLocation");
       
   146     lookup("saBreakpointThread");
       
   147     lookup("saBreakpointClass");
       
   148     lookup("saBreakpointMethod");
       
   149     lookup("saBreakpointLocation");
       
   150 
       
   151     saAttached.setValue(1);
       
   152     attached = true;
       
   153   }
       
   154 
       
   155   public void detach() {
       
   156     saAttached.setValue(0);
       
   157     attached = false;
       
   158     saLibName = null;
       
   159   }
       
   160 
       
   161   /** Set the timeout value (in milliseconds) for the VM to reply to
       
   162       commands. Once this timeout has elapsed, the VM is assumed to
       
   163       have disconnected. Defaults to 3000 milliseconds (3 seconds). */
       
   164   public void setCommandTimeout(long millis) {
       
   165     timeoutMillis = millis;
       
   166   }
       
   167 
       
   168   /** Get the timeout value (in milliseconds) for the VM to reply to
       
   169       commands. Once this timeout has elapsed, the VM is assumed to
       
   170       have disconnected. Defaults to 3000 milliseconds (3 seconds). */
       
   171   public long getCommandTimeout() {
       
   172     return timeoutMillis;
       
   173   }
       
   174 
       
   175   /** Indicates whether a Java debug event is pending */
       
   176   public boolean eventPending() {
       
   177     return (saEventPending.getValue() != 0);
       
   178   }
       
   179 
       
   180   /** Poll for event; returns null if none pending. */
       
   181   public Event eventPoll() {
       
   182     if (saEventPending.getValue() == 0) {
       
   183       return null;
       
   184     }
       
   185 
       
   186     int kind = (int) saEventKind.getValue();
       
   187     switch (kind) {
       
   188     case JVMDI_EVENT_EXCEPTION: {
       
   189       JNIHandleAccessor thread = lookupJNIHandle("saExceptionThread");
       
   190       JNIHandleAccessor clazz = lookupJNIHandle("saExceptionClass");
       
   191       JNIid method = lookupJNIid("saExceptionMethod");
       
   192       CIntegerAccessor location = lookupCInt("saExceptionLocation");
       
   193       JNIHandleAccessor exception = lookupJNIHandle("saExceptionException");
       
   194       JNIHandleAccessor catchClass = lookupJNIHandle("saExceptionCatchClass");
       
   195       JNIid catchMethod = lookupJNIid("saExceptionCatchMethod");
       
   196       CIntegerAccessor catchLocation = lookupCInt("saExceptionCatchLocation");
       
   197       return new ExceptionEvent(thread.getValue(), clazz.getValue(), method,
       
   198                                 (int) location.getValue(), exception.getValue(),
       
   199                                 catchClass.getValue(), catchMethod, (int) catchLocation.getValue());
       
   200     }
       
   201 
       
   202     case JVMDI_EVENT_BREAKPOINT: {
       
   203       JNIHandleAccessor thread = lookupJNIHandle("saBreakpointThread");
       
   204       JNIHandleAccessor clazz = lookupJNIHandle("saBreakpointClass");
       
   205       JNIid method = lookupJNIid("saBreakpointMethod");
       
   206       CIntegerAccessor location = lookupCInt("saBreakpointLocation");
       
   207       return new BreakpointEvent(thread.getValue(), clazz.getValue(),
       
   208                                  method, (int) location.getValue());
       
   209     }
       
   210 
       
   211     default:
       
   212       throw new DebuggerException("Unsupported event type " + kind);
       
   213     }
       
   214   }
       
   215 
       
   216   /** Continue past current event */
       
   217   public void eventContinue() {
       
   218     saEventPending.setValue(0);
       
   219   }
       
   220 
       
   221   /** Suspend all Java threads in the target VM. Throws
       
   222       DebuggerException if the VM disconnected. */
       
   223   public void suspend() {
       
   224     saCmdType.setValue(SA_CMD_SUSPEND_ALL);
       
   225     saCmdPending.setValue(1);
       
   226     waitForCommandCompletion();
       
   227     suspended = true;
       
   228   }
       
   229 
       
   230   /** Resume all Java threads in the target VM. Throws
       
   231       DebuggerException if the VM disconnected. */
       
   232   public void resume() {
       
   233     saCmdType.setValue(SA_CMD_RESUME_ALL);
       
   234     saCmdPending.setValue(1);
       
   235     waitForCommandCompletion();
       
   236     suspended = false;
       
   237   }
       
   238 
       
   239   /** Indicates whether all Java threads have been suspended via this
       
   240       interface. */
       
   241   public boolean isSuspended() {
       
   242     return suspended;
       
   243   }
       
   244 
       
   245   /** Information about toggling of breakpoints */
       
   246   public static class BreakpointToggleResult {
       
   247     private boolean success;
       
   248     private String errMsg;
       
   249     private int lineNumber;
       
   250     private int bci;
       
   251     private boolean wasSet;
       
   252     private String methodName;
       
   253     private String methodSig;
       
   254 
       
   255     /** Success constructor */
       
   256     public BreakpointToggleResult(int lineNumber, int bci, boolean wasSet,
       
   257                                   String methodName, String methodSig) {
       
   258       this.lineNumber = lineNumber;
       
   259       this.bci = bci;
       
   260       this.wasSet = wasSet;
       
   261       this.methodName = methodName;
       
   262       this.methodSig = methodSig;
       
   263       success = true;
       
   264     }
       
   265 
       
   266     /** Failure constructor */
       
   267     public BreakpointToggleResult(String errMsg) {
       
   268       this.errMsg = errMsg;
       
   269       success = false;
       
   270     }
       
   271 
       
   272     /** Indicates whether this represents a successful return or not */
       
   273     public boolean getSuccess() { return success; }
       
   274 
       
   275     /** Valid only if getSuccess() returns false */
       
   276     public String getErrMsg() { return errMsg; }
       
   277 
       
   278     /** Line number at which breakpoint toggle occurred; valid only if
       
   279         getSuccess() returns true. */
       
   280     public int getLineNumber() { return lineNumber; }
       
   281 
       
   282     /** BCI at which breakpoint toggle occurred; valid only if
       
   283         getSuccess() returns true. */
       
   284     public int getBCI() { return bci; }
       
   285 
       
   286     /** Indicates whether the breakpoint toggle was the set of a
       
   287         breakpoint or not; valid only if getSuccess() returns true. */
       
   288     public boolean getWasSet() { return wasSet; }
       
   289 
       
   290     /** Method name in which the breakpoint toggle occurred; valid
       
   291         only if getSuccess() returns true. */
       
   292     public String getMethodName() { return methodName; }
       
   293 
       
   294     /** Method signature in which the breakpoint toggle occurred;
       
   295         valid only if getSuccess() returns true. */
       
   296     public String getMethodSignature() { return methodSig; }
       
   297   }
       
   298 
       
   299   /** Toggle a breakpoint. Throws DebuggerException if a real error
       
   300       occurred; otherwise returns non-null BreakpointToggleResult. The
       
   301       work of scanning the loaded classes is done in the target VM
       
   302       because it turns out to be significantly faster than scanning
       
   303       through the system dictionary from the SA, and interactivity
       
   304       when setting breakpoints is important. */
       
   305   public BreakpointToggleResult toggleBreakpoint(String srcFileName,
       
   306                                                  String pkgName,
       
   307                                                  int lineNo) {
       
   308     saCmdBkptSrcFileName.setValue(srcFileName);
       
   309     saCmdBkptPkgName.setValue(pkgName);
       
   310     saCmdBkptLineNumber.setValue(lineNo);
       
   311     saCmdType.setValue(SA_CMD_TOGGLE_BREAKPOINT);
       
   312     saCmdPending.setValue(1);
       
   313     if (waitForCommandCompletion(true)) {
       
   314       return new BreakpointToggleResult((int) saCmdBkptResLineNumber.getValue(),
       
   315                                         (int) saCmdBkptResBCI.getValue(),
       
   316                                         (saCmdBkptResWasSet.getValue() != 0),
       
   317                                         saCmdBkptResMethodName.getValue(),
       
   318                                         saCmdBkptResMethodSig.getValue());
       
   319     } else {
       
   320       return new BreakpointToggleResult(saCmdResultErrMsg.getValue());
       
   321     }
       
   322   }
       
   323 
       
   324 
       
   325   //----------------------------------------------------------------------
       
   326   // Internals only below this point
       
   327   //
       
   328 
       
   329   private CIntegerAccessor lookupCInt(String symbolName) {
       
   330     return new CIntegerAccessor(lookup(symbolName), 4, false);
       
   331   }
       
   332 
       
   333   private CStringAccessor lookupCString(String symbolName, int bufLen) {
       
   334     return new CStringAccessor(lookup(symbolName), bufLen);
       
   335   }
       
   336 
       
   337   private JNIHandleAccessor lookupJNIHandle(String symbolName) {
       
   338     return new JNIHandleAccessor(lookup(symbolName), VM.getVM().getObjectHeap());
       
   339   }
       
   340 
       
   341   private JNIid lookupJNIid(String symbolName) {
       
   342     Address idAddr = lookup(symbolName).getAddressAt(0);
       
   343     if (idAddr == null) {
       
   344       return null;
       
   345     }
       
   346     return new JNIid(idAddr, VM.getVM().getObjectHeap());
       
   347   }
       
   348 
       
   349   private int lookupConstInt(String symbolName) {
       
   350     Address addr = lookup(symbolName);
       
   351     return (int) addr.getCIntegerAt(0, 4, false);
       
   352   }
       
   353 
       
   354   private boolean setupLookup(String symbolName) {
       
   355     if (saLibName == null) {
       
   356       for (int i = 0; i < saLibNames.length; i++) {
       
   357         Address addr = dbg.lookup(saLibNames[i], symbolName);
       
   358         if (addr != null) {
       
   359           saLibName = saLibNames[i];
       
   360           return true;
       
   361         }
       
   362       }
       
   363       return false;
       
   364     }
       
   365     return true;
       
   366   }
       
   367 
       
   368   private Address lookup(String symbolName) {
       
   369     if (saLibName == null) {
       
   370       for (int i = 0; i < saLibNames.length; i++) {
       
   371         Address addr = dbg.lookup(saLibNames[i], symbolName);
       
   372         if (addr != null) {
       
   373           saLibName = saLibNames[i];
       
   374           return addr;
       
   375         }
       
   376       }
       
   377       throw new DebuggerException("Unable to find symbol " + symbolName + " in any of the known names for the SA");
       
   378     }
       
   379 
       
   380     Address addr = dbg.lookup(saLibName, symbolName);
       
   381     if (addr == null) {
       
   382       throw new DebuggerException("Unable to find symbol " + symbolName + " in " + saLibName);
       
   383     }
       
   384     return addr;
       
   385   }
       
   386 
       
   387   private void waitForCommandCompletion() {
       
   388     waitForCommandCompletion(false);
       
   389   }
       
   390 
       
   391   /** Returns true if command succeeded, false if not */
       
   392   private boolean waitForCommandCompletion(boolean forBreakpoint) {
       
   393     long start = System.currentTimeMillis();
       
   394     long cur = start;
       
   395     while ((saCmdPending.getValue() != 0) &&
       
   396            (cur - start < timeoutMillis)) {
       
   397       try {
       
   398         java.lang.Thread.currentThread().sleep(10);
       
   399       } catch (InterruptedException e) {
       
   400       }
       
   401       cur = System.currentTimeMillis();
       
   402     }
       
   403     if (saCmdPending.getValue() != 0) {
       
   404       detach();
       
   405       throw new DebuggerException("VM appears to have died");
       
   406     }
       
   407     boolean succeeded = saCmdResult.getValue() == 0;
       
   408     if (!succeeded &&
       
   409         (!forBreakpoint || saCmdBkptResWasError.getValue() != 0)) {
       
   410       String err = saCmdResultErrMsg.getValue();
       
   411       throw new DebuggerException("Error executing JVMDI command: " + err);
       
   412     }
       
   413     return succeeded;
       
   414   }
       
   415 }