comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:a61af66fc99e
1 /*
2 * Copyright 2002 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20 * CA 95054 USA or visit www.sun.com if you need additional information or
21 * have any 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 }