1 /* |
|
2 * Copyright (c) 2002, 2012, 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.bugspot; |
|
26 |
|
27 import java.io.PrintStream; |
|
28 import java.net.*; |
|
29 import java.rmi.*; |
|
30 import sun.jvm.hotspot.*; |
|
31 import sun.jvm.hotspot.debugger.*; |
|
32 import sun.jvm.hotspot.debugger.bsd.*; |
|
33 import sun.jvm.hotspot.debugger.proc.*; |
|
34 import sun.jvm.hotspot.debugger.cdbg.*; |
|
35 import sun.jvm.hotspot.debugger.windbg.*; |
|
36 import sun.jvm.hotspot.debugger.linux.*; |
|
37 import sun.jvm.hotspot.debugger.sparc.*; |
|
38 import sun.jvm.hotspot.debugger.remote.*; |
|
39 import sun.jvm.hotspot.livejvm.*; |
|
40 import sun.jvm.hotspot.memory.*; |
|
41 import sun.jvm.hotspot.oops.*; |
|
42 import sun.jvm.hotspot.runtime.*; |
|
43 import sun.jvm.hotspot.types.*; |
|
44 import sun.jvm.hotspot.utilities.*; |
|
45 |
|
46 /** <P> This class wraps the basic functionality for connecting to the |
|
47 * target process or debug server. It makes it simple to start up the |
|
48 * debugging system. </P> |
|
49 * |
|
50 * <P> This agent (as compared to the HotSpotAgent) can connect to |
|
51 * and interact with arbitrary processes. If the target process |
|
52 * happens to be a HotSpot JVM, the Java debugging features of the |
|
53 * Serviceability Agent are enabled. Further, if the Serviceability |
|
54 * Agent's JVMDI module is loaded into the target VM, interaction |
|
55 * with the live Java program is possible, specifically the catching |
|
56 * of exceptions and setting of breakpoints. </P> |
|
57 * |
|
58 * <P> The BugSpot debugger requires that the underlying Debugger |
|
59 * support C/C++ debugging via the CDebugger interface. </P> |
|
60 * |
|
61 * <P> FIXME: especially with the addition of remote debugging, this |
|
62 * has turned into a mess; needs rethinking. </P> */ |
|
63 |
|
64 public class BugSpotAgent { |
|
65 |
|
66 private JVMDebugger debugger; |
|
67 private MachineDescription machDesc; |
|
68 private TypeDataBase db; |
|
69 |
|
70 private String os; |
|
71 private String cpu; |
|
72 private String fileSep; |
|
73 |
|
74 // The system can work in several ways: |
|
75 // - Attaching to local process |
|
76 // - Attaching to local core file |
|
77 // - Connecting to remote debug server |
|
78 // - Starting debug server for process |
|
79 // - Starting debug server for core file |
|
80 |
|
81 // These are options for the "client" side of things |
|
82 private static final int PROCESS_MODE = 0; |
|
83 private static final int CORE_FILE_MODE = 1; |
|
84 private static final int REMOTE_MODE = 2; |
|
85 private int startupMode; |
|
86 |
|
87 // This indicates whether we are really starting a server or not |
|
88 private boolean isServer; |
|
89 |
|
90 // All possible required information for connecting |
|
91 private int pid; |
|
92 private String executableName; |
|
93 private String coreFileName; |
|
94 private String debugServerID; |
|
95 |
|
96 // All needed information for server side |
|
97 private String serverID; |
|
98 |
|
99 // Indicates whether we are attached to a HotSpot JVM or not |
|
100 private boolean javaMode; |
|
101 |
|
102 // Indicates whether we have process control over a live HotSpot JVM |
|
103 // or not; non-null if so. |
|
104 private ServiceabilityAgentJVMDIModule jvmdi; |
|
105 // While handling C breakpoints interactivity with the Java program |
|
106 // is forbidden. Too many invariants are broken while the target is |
|
107 // stopped at a C breakpoint to risk making JVMDI calls. |
|
108 private boolean javaInteractionDisabled; |
|
109 |
|
110 private String[] jvmLibNames; |
|
111 private String[] saLibNames; |
|
112 |
|
113 // FIXME: make these configurable, i.e., via a dotfile; also |
|
114 // consider searching within the JDK from which this Java executable |
|
115 // comes to find them |
|
116 private static final String defaultDbxPathPrefix = "/net/jano.eng/export/disk05/hotspot/sa"; |
|
117 private static final String defaultDbxSvcAgentDSOPathPrefix = "/net/jano.eng/export/disk05/hotspot/sa"; |
|
118 |
|
119 private static final boolean DEBUG; |
|
120 static { |
|
121 DEBUG = System.getProperty("sun.jvm.hotspot.bugspot.BugSpotAgent.DEBUG") |
|
122 != null; |
|
123 } |
|
124 |
|
125 static void debugPrintln(String str) { |
|
126 if (DEBUG) { |
|
127 System.err.println(str); |
|
128 } |
|
129 } |
|
130 |
|
131 static void showUsage() { |
|
132 System.out.println(" You can also pass these -D options to java to specify where to find dbx and the \n" + |
|
133 " Serviceability Agent plugin for dbx:"); |
|
134 System.out.println(" -DdbxPathName=<path-to-dbx-executable>\n" + |
|
135 " Default is derived from dbxPathPrefix"); |
|
136 System.out.println(" or"); |
|
137 System.out.println(" -DdbxPathPrefix=<xxx>\n" + |
|
138 " where xxx is the path name of a dir structure that contains:\n" + |
|
139 " <os>/<arch>/bin/dbx\n" + |
|
140 " The default is " + defaultDbxPathPrefix); |
|
141 System.out.println(" and"); |
|
142 System.out.println(" -DdbxSvcAgentDSOPathName=<path-to-dbx-serviceability-agent-module>\n" + |
|
143 " Default is determined from dbxSvcAgentDSOPathPrefix"); |
|
144 System.out.println(" or"); |
|
145 System.out.println(" -DdbxSvcAgentDSOPathPrefix=<xxx>\n" + |
|
146 " where xxx is the pathname of a dir structure that contains:\n" + |
|
147 " <os>/<arch>/bin/lib/libsvc_agent_dbx.so\n" + |
|
148 " The default is " + defaultDbxSvcAgentDSOPathPrefix); |
|
149 } |
|
150 |
|
151 public BugSpotAgent() { |
|
152 // for non-server add shutdown hook to clean-up debugger in case |
|
153 // of forced exit. For remote server, shutdown hook is added by |
|
154 // DebugServer. |
|
155 Runtime.getRuntime().addShutdownHook(new java.lang.Thread( |
|
156 new Runnable() { |
|
157 public void run() { |
|
158 synchronized (BugSpotAgent.this) { |
|
159 if (!isServer) { |
|
160 detach(); |
|
161 } |
|
162 } |
|
163 } |
|
164 })); |
|
165 } |
|
166 |
|
167 //-------------------------------------------------------------------------------- |
|
168 // Accessors (once the system is set up) |
|
169 // |
|
170 |
|
171 public synchronized Debugger getDebugger() { |
|
172 return debugger; |
|
173 } |
|
174 |
|
175 public synchronized CDebugger getCDebugger() { |
|
176 return getDebugger().getCDebugger(); |
|
177 } |
|
178 |
|
179 public synchronized ProcessControl getProcessControl() { |
|
180 return getCDebugger().getProcessControl(); |
|
181 } |
|
182 |
|
183 public synchronized TypeDataBase getTypeDataBase() { |
|
184 return db; |
|
185 } |
|
186 |
|
187 /** Indicates whether the target process is suspended |
|
188 completely. Equivalent to getProcessControl().isSuspended(). */ |
|
189 public synchronized boolean isSuspended() throws DebuggerException { |
|
190 return getProcessControl().isSuspended(); |
|
191 } |
|
192 |
|
193 /** Suspends the target process completely. Equivalent to |
|
194 getProcessControl().suspend(). */ |
|
195 public synchronized void suspend() throws DebuggerException { |
|
196 getProcessControl().suspend(); |
|
197 } |
|
198 |
|
199 /** Resumes the target process completely. Equivalent to |
|
200 getProcessControl().suspend(). */ |
|
201 public synchronized void resume() throws DebuggerException { |
|
202 getProcessControl().resume(); |
|
203 } |
|
204 |
|
205 /** Indicates whether we are attached to a Java HotSpot virtual |
|
206 machine */ |
|
207 public synchronized boolean isJavaMode() { |
|
208 return javaMode; |
|
209 } |
|
210 |
|
211 /** Temporarily disables interaction with the target process via |
|
212 JVMDI. This is done while the target process is stopped at a C |
|
213 breakpoint. Can be called even if the JVMDI agent has not been |
|
214 initialized. */ |
|
215 public synchronized void disableJavaInteraction() { |
|
216 javaInteractionDisabled = true; |
|
217 } |
|
218 |
|
219 /** Re-enables interaction with the target process via JVMDI. This |
|
220 is done while the target process is continued past a C |
|
221 braekpoint. Can be called even if the JVMDI agent has not been |
|
222 initialized. */ |
|
223 public synchronized void enableJavaInteraction() { |
|
224 javaInteractionDisabled = false; |
|
225 } |
|
226 |
|
227 /** Indicates whether Java interaction has been disabled */ |
|
228 public synchronized boolean isJavaInteractionDisabled() { |
|
229 return javaInteractionDisabled; |
|
230 } |
|
231 |
|
232 /** Indicates whether we can talk to the Serviceability Agent's |
|
233 JVMDI module to be able to set breakpoints */ |
|
234 public synchronized boolean canInteractWithJava() { |
|
235 return (jvmdi != null) && !javaInteractionDisabled; |
|
236 } |
|
237 |
|
238 /** Suspends all Java threads in the target process. Can only be |
|
239 called if we are attached to a HotSpot JVM and can connect to |
|
240 the SA's JVMDI module. Must not be called when the target |
|
241 process has been suspended with suspend(). */ |
|
242 public synchronized void suspendJava() throws DebuggerException { |
|
243 if (!canInteractWithJava()) { |
|
244 throw new DebuggerException("Could not connect to SA's JVMDI module"); |
|
245 } |
|
246 if (jvmdi.isSuspended()) { |
|
247 throw new DebuggerException("Target process already suspended via JVMDI"); |
|
248 } |
|
249 jvmdi.suspend(); |
|
250 } |
|
251 |
|
252 /** Resumes all Java threads in the target process. Can only be |
|
253 called if we are attached to a HotSpot JVM and can connect to |
|
254 the SA's JVMDI module. Must not be called when the target |
|
255 process has been suspended with suspend(). */ |
|
256 public synchronized void resumeJava() throws DebuggerException { |
|
257 if (!canInteractWithJava()) { |
|
258 throw new DebuggerException("Could not connect to SA's JVMDI module"); |
|
259 } |
|
260 if (!jvmdi.isSuspended()) { |
|
261 throw new DebuggerException("Target process already resumed via JVMDI"); |
|
262 } |
|
263 jvmdi.resume(); |
|
264 } |
|
265 |
|
266 /** Indicates whether the target process has been suspended at the |
|
267 Java language level via the SA's JVMDI module */ |
|
268 public synchronized boolean isJavaSuspended() throws DebuggerException { |
|
269 return jvmdi.isSuspended(); |
|
270 } |
|
271 |
|
272 /** Toggle a Java breakpoint at the given location. */ |
|
273 public synchronized ServiceabilityAgentJVMDIModule.BreakpointToggleResult |
|
274 toggleJavaBreakpoint(String srcFileName, |
|
275 String pkgName, |
|
276 int lineNo) { |
|
277 if (!canInteractWithJava()) { |
|
278 throw new DebuggerException("Could not connect to SA's JVMDI module; can not toggle Java breakpoints"); |
|
279 } |
|
280 return jvmdi.toggleBreakpoint(srcFileName, pkgName, lineNo); |
|
281 } |
|
282 |
|
283 /** Access to JVMDI module's eventPending */ |
|
284 public synchronized boolean javaEventPending() throws DebuggerException { |
|
285 if (!canInteractWithJava()) { |
|
286 throw new DebuggerException("Could not connect to SA's JVMDI module; can not poll for Java debug events"); |
|
287 } |
|
288 return jvmdi.eventPending(); |
|
289 } |
|
290 |
|
291 /** Access to JVMDI module's eventPoll */ |
|
292 public synchronized Event javaEventPoll() throws DebuggerException { |
|
293 if (!canInteractWithJava()) { |
|
294 throw new DebuggerException("Could not connect to SA's JVMDI module; can not poll for Java debug events"); |
|
295 } |
|
296 return jvmdi.eventPoll(); |
|
297 } |
|
298 |
|
299 /** Access to JVMDI module's eventContinue */ |
|
300 public synchronized void javaEventContinue() throws DebuggerException { |
|
301 if (!canInteractWithJava()) { |
|
302 throw new DebuggerException("Could not connect to SA's JVMDI module; can not continue past Java debug events"); |
|
303 } |
|
304 jvmdi.eventContinue(); |
|
305 } |
|
306 |
|
307 |
|
308 // FIXME: add other accessors. For example, suspension and |
|
309 // resumption should be done through this interface, as well as |
|
310 // interaction with the live Java process such as breakpoint setting. |
|
311 // Probably should not expose the ServiceabilityAgentJVMDIModule |
|
312 // from this interface. |
|
313 |
|
314 //-------------------------------------------------------------------------------- |
|
315 // Client-side operations |
|
316 // |
|
317 |
|
318 /** This attaches to a process running on the local machine. */ |
|
319 public synchronized void attach(int processID) |
|
320 throws DebuggerException { |
|
321 if (debugger != null) { |
|
322 throw new DebuggerException("Already attached"); |
|
323 } |
|
324 pid = processID; |
|
325 startupMode = PROCESS_MODE; |
|
326 isServer = false; |
|
327 go(); |
|
328 } |
|
329 |
|
330 /** This opens a core file on the local machine */ |
|
331 public synchronized void attach(String executableName, String coreFileName) |
|
332 throws DebuggerException { |
|
333 if (debugger != null) { |
|
334 throw new DebuggerException("Already attached"); |
|
335 } |
|
336 if ((executableName == null) || (coreFileName == null)) { |
|
337 throw new DebuggerException("Both the core file name and executable name must be specified"); |
|
338 } |
|
339 this.executableName = executableName; |
|
340 this.coreFileName = coreFileName; |
|
341 startupMode = CORE_FILE_MODE; |
|
342 isServer = false; |
|
343 go(); |
|
344 } |
|
345 |
|
346 /** This attaches to a "debug server" on a remote machine; this |
|
347 remote server has already attached to a process or opened a |
|
348 core file and is waiting for RMI calls on the Debugger object to |
|
349 come in. */ |
|
350 public synchronized void attach(String remoteServerID) |
|
351 throws DebuggerException { |
|
352 if (debugger != null) { |
|
353 throw new DebuggerException("Already attached to a process"); |
|
354 } |
|
355 if (remoteServerID == null) { |
|
356 throw new DebuggerException("Debug server id must be specified"); |
|
357 } |
|
358 |
|
359 debugServerID = remoteServerID; |
|
360 startupMode = REMOTE_MODE; |
|
361 isServer = false; |
|
362 go(); |
|
363 } |
|
364 |
|
365 /** This should only be called by the user on the client machine, |
|
366 not the server machine */ |
|
367 public synchronized boolean detach() throws DebuggerException { |
|
368 if (isServer) { |
|
369 throw new DebuggerException("Should not call detach() for server configuration"); |
|
370 } |
|
371 return detachInternal(); |
|
372 } |
|
373 |
|
374 //-------------------------------------------------------------------------------- |
|
375 // Server-side operations |
|
376 // |
|
377 |
|
378 /** This attaches to a process running on the local machine and |
|
379 starts a debug server, allowing remote machines to connect and |
|
380 examine this process. uniqueID is used to uniquely identify the |
|
381 debuggee */ |
|
382 public synchronized void startServer(int processID, String uniqueID) |
|
383 throws DebuggerException { |
|
384 if (debugger != null) { |
|
385 throw new DebuggerException("Already attached"); |
|
386 } |
|
387 pid = processID; |
|
388 startupMode = PROCESS_MODE; |
|
389 isServer = true; |
|
390 serverID = uniqueID; |
|
391 go(); |
|
392 } |
|
393 |
|
394 /** This attaches to a process running on the local machine and |
|
395 starts a debug server, allowing remote machines to connect and |
|
396 examine this process. */ |
|
397 public synchronized void startServer(int processID) |
|
398 throws DebuggerException { |
|
399 startServer(processID, null); |
|
400 } |
|
401 |
|
402 /** This opens a core file on the local machine and starts a debug |
|
403 server, allowing remote machines to connect and examine this |
|
404 core file. uniqueID is used to uniquely identify the |
|
405 debuggee */ |
|
406 public synchronized void startServer(String executableName, String coreFileName, |
|
407 String uniqueID) |
|
408 throws DebuggerException { |
|
409 if (debugger != null) { |
|
410 throw new DebuggerException("Already attached"); |
|
411 } |
|
412 if ((executableName == null) || (coreFileName == null)) { |
|
413 throw new DebuggerException("Both the core file name and Java executable name must be specified"); |
|
414 } |
|
415 this.executableName = executableName; |
|
416 this.coreFileName = coreFileName; |
|
417 startupMode = CORE_FILE_MODE; |
|
418 isServer = true; |
|
419 serverID = uniqueID; |
|
420 go(); |
|
421 } |
|
422 |
|
423 /** This opens a core file on the local machine and starts a debug |
|
424 server, allowing remote machines to connect and examine this |
|
425 core file.*/ |
|
426 public synchronized void startServer(String executableName, String coreFileName) |
|
427 throws DebuggerException { |
|
428 startServer(executableName, coreFileName, null); |
|
429 } |
|
430 |
|
431 /** This may only be called on the server side after startServer() |
|
432 has been called */ |
|
433 public synchronized boolean shutdownServer() throws DebuggerException { |
|
434 if (!isServer) { |
|
435 throw new DebuggerException("Should not call shutdownServer() for client configuration"); |
|
436 } |
|
437 return detachInternal(); |
|
438 } |
|
439 |
|
440 |
|
441 //-------------------------------------------------------------------------------- |
|
442 // Internals only below this point |
|
443 // |
|
444 |
|
445 private boolean detachInternal() { |
|
446 if (debugger == null) { |
|
447 return false; |
|
448 } |
|
449 if (canInteractWithJava()) { |
|
450 jvmdi.detach(); |
|
451 jvmdi = null; |
|
452 } |
|
453 boolean retval = true; |
|
454 if (!isServer) { |
|
455 VM.shutdown(); |
|
456 } |
|
457 // We must not call detach() if we are a client and are connected |
|
458 // to a remote debugger |
|
459 Debugger dbg = null; |
|
460 DebuggerException ex = null; |
|
461 if (isServer) { |
|
462 try { |
|
463 RMIHelper.unbind(serverID); |
|
464 } |
|
465 catch (DebuggerException de) { |
|
466 ex = de; |
|
467 } |
|
468 dbg = debugger; |
|
469 } else { |
|
470 if (startupMode != REMOTE_MODE) { |
|
471 dbg = debugger; |
|
472 } |
|
473 } |
|
474 if (dbg != null) { |
|
475 retval = dbg.detach(); |
|
476 } |
|
477 |
|
478 debugger = null; |
|
479 machDesc = null; |
|
480 db = null; |
|
481 if (ex != null) { |
|
482 throw(ex); |
|
483 } |
|
484 return retval; |
|
485 } |
|
486 |
|
487 private void go() { |
|
488 setupDebugger(); |
|
489 javaMode = setupVM(); |
|
490 } |
|
491 |
|
492 private void setupDebugger() { |
|
493 if (startupMode != REMOTE_MODE) { |
|
494 // |
|
495 // Local mode (client attaching to local process or setting up |
|
496 // server, but not client attaching to server) |
|
497 // |
|
498 |
|
499 try { |
|
500 os = PlatformInfo.getOS(); |
|
501 cpu = PlatformInfo.getCPU(); |
|
502 } |
|
503 catch (UnsupportedPlatformException e) { |
|
504 throw new DebuggerException(e); |
|
505 } |
|
506 fileSep = System.getProperty("file.separator"); |
|
507 |
|
508 if (os.equals("solaris")) { |
|
509 setupDebuggerSolaris(); |
|
510 } else if (os.equals("win32")) { |
|
511 setupDebuggerWin32(); |
|
512 } else if (os.equals("linux")) { |
|
513 setupDebuggerLinux(); |
|
514 } else if (os.equals("bsd")) { |
|
515 setupDebuggerBsd(); |
|
516 } else { |
|
517 // Add support for more operating systems here |
|
518 throw new DebuggerException("Operating system " + os + " not yet supported"); |
|
519 } |
|
520 if (isServer) { |
|
521 RemoteDebuggerServer remote = null; |
|
522 try { |
|
523 remote = new RemoteDebuggerServer(debugger); |
|
524 } |
|
525 catch (RemoteException rem) { |
|
526 throw new DebuggerException(rem); |
|
527 } |
|
528 RMIHelper.rebind(serverID, remote); |
|
529 } |
|
530 } else { |
|
531 // |
|
532 // Remote mode (client attaching to server) |
|
533 // |
|
534 |
|
535 // Create and install a security manager |
|
536 |
|
537 // FIXME: currently commented out because we were having |
|
538 // security problems since we're "in the sun.* hierarchy" here. |
|
539 // Perhaps a permissive policy file would work around this. In |
|
540 // the long run, will probably have to move into com.sun.*. |
|
541 |
|
542 // if (System.getSecurityManager() == null) { |
|
543 // System.setSecurityManager(new RMISecurityManager()); |
|
544 // } |
|
545 |
|
546 connectRemoteDebugger(); |
|
547 } |
|
548 } |
|
549 |
|
550 private boolean setupVM() { |
|
551 // We need to instantiate a HotSpotTypeDataBase on both the client |
|
552 // and server machine. On the server it is only currently used to |
|
553 // configure the Java primitive type sizes (which we should |
|
554 // consider making constant). On the client it is used to |
|
555 // configure the VM. |
|
556 |
|
557 try { |
|
558 if (os.equals("solaris")) { |
|
559 db = new HotSpotTypeDataBase(machDesc, new HotSpotSolarisVtblAccess(debugger, jvmLibNames), |
|
560 debugger, jvmLibNames); |
|
561 } else if (os.equals("win32")) { |
|
562 db = new HotSpotTypeDataBase(machDesc, new Win32VtblAccess(debugger, jvmLibNames), |
|
563 debugger, jvmLibNames); |
|
564 } else if (os.equals("linux")) { |
|
565 db = new HotSpotTypeDataBase(machDesc, new LinuxVtblAccess(debugger, jvmLibNames), |
|
566 debugger, jvmLibNames); |
|
567 } else if (os.equals("bsd")) { |
|
568 db = new HotSpotTypeDataBase(machDesc, new BsdVtblAccess(debugger, jvmLibNames), |
|
569 debugger, jvmLibNames); |
|
570 } else { |
|
571 throw new DebuggerException("OS \"" + os + "\" not yet supported (no VtblAccess implemented yet)"); |
|
572 } |
|
573 } |
|
574 catch (NoSuchSymbolException e) { |
|
575 e.printStackTrace(); |
|
576 return false; |
|
577 } |
|
578 |
|
579 if (startupMode != REMOTE_MODE) { |
|
580 // Configure the debugger with the primitive type sizes just obtained from the VM |
|
581 debugger.configureJavaPrimitiveTypeSizes(db.getJBooleanType().getSize(), |
|
582 db.getJByteType().getSize(), |
|
583 db.getJCharType().getSize(), |
|
584 db.getJDoubleType().getSize(), |
|
585 db.getJFloatType().getSize(), |
|
586 db.getJIntType().getSize(), |
|
587 db.getJLongType().getSize(), |
|
588 db.getJShortType().getSize()); |
|
589 } |
|
590 |
|
591 if (!isServer) { |
|
592 // Do not initialize the VM on the server (unnecessary, since it's |
|
593 // instantiated on the client) |
|
594 VM.initialize(db, debugger); |
|
595 } |
|
596 |
|
597 try { |
|
598 jvmdi = new ServiceabilityAgentJVMDIModule(debugger, saLibNames); |
|
599 if (jvmdi.canAttach()) { |
|
600 jvmdi.attach(); |
|
601 jvmdi.setCommandTimeout(6000); |
|
602 debugPrintln("Attached to Serviceability Agent's JVMDI module."); |
|
603 // Jog VM to suspended point with JVMDI module |
|
604 resume(); |
|
605 suspendJava(); |
|
606 suspend(); |
|
607 debugPrintln("Suspended all Java threads."); |
|
608 } else { |
|
609 debugPrintln("Could not locate SA's JVMDI module; skipping attachment"); |
|
610 jvmdi = null; |
|
611 } |
|
612 } catch (Exception e) { |
|
613 e.printStackTrace(); |
|
614 jvmdi = null; |
|
615 } |
|
616 |
|
617 return true; |
|
618 } |
|
619 |
|
620 //-------------------------------------------------------------------------------- |
|
621 // OS-specific debugger setup/connect routines |
|
622 // |
|
623 |
|
624 // |
|
625 // Solaris |
|
626 // |
|
627 |
|
628 private void setupDebuggerSolaris() { |
|
629 setupJVMLibNamesSolaris(); |
|
630 ProcDebuggerLocal dbg = new ProcDebuggerLocal(null, true); |
|
631 debugger = dbg; |
|
632 attachDebugger(); |
|
633 |
|
634 // Set up CPU-dependent stuff |
|
635 if (cpu.equals("x86")) { |
|
636 machDesc = new MachineDescriptionIntelX86(); |
|
637 } else if (cpu.equals("sparc")) { |
|
638 int addressSize = dbg.getRemoteProcessAddressSize(); |
|
639 if (addressSize == -1) { |
|
640 throw new DebuggerException("Error occurred while trying to determine the remote process's address size"); |
|
641 } |
|
642 |
|
643 if (addressSize == 32) { |
|
644 machDesc = new MachineDescriptionSPARC32Bit(); |
|
645 } else if (addressSize == 64) { |
|
646 machDesc = new MachineDescriptionSPARC64Bit(); |
|
647 } else { |
|
648 throw new DebuggerException("Address size " + addressSize + " is not supported on SPARC"); |
|
649 } |
|
650 } else if (cpu.equals("amd64")) { |
|
651 machDesc = new MachineDescriptionAMD64(); |
|
652 } else { |
|
653 throw new DebuggerException("Solaris only supported on sparc/sparcv9/x86/amd64"); |
|
654 } |
|
655 |
|
656 dbg.setMachineDescription(machDesc); |
|
657 } |
|
658 |
|
659 private void connectRemoteDebugger() throws DebuggerException { |
|
660 RemoteDebugger remote = |
|
661 (RemoteDebugger) RMIHelper.lookup(debugServerID); |
|
662 debugger = new RemoteDebuggerClient(remote); |
|
663 machDesc = ((RemoteDebuggerClient) debugger).getMachineDescription(); |
|
664 os = debugger.getOS(); |
|
665 if (os.equals("solaris")) { |
|
666 setupJVMLibNamesSolaris(); |
|
667 } else if (os.equals("win32")) { |
|
668 setupJVMLibNamesWin32(); |
|
669 } else if (os.equals("linux")) { |
|
670 setupJVMLibNamesLinux(); |
|
671 } else if (os.equals("bsd")) { |
|
672 setupJVMLibNamesBsd(); |
|
673 } else { |
|
674 throw new RuntimeException("Unknown OS type"); |
|
675 } |
|
676 |
|
677 cpu = debugger.getCPU(); |
|
678 } |
|
679 |
|
680 private void setupJVMLibNamesSolaris() { |
|
681 jvmLibNames = new String[] { "libjvm.so", "libjvm_g.so", "gamma_g" }; |
|
682 saLibNames = new String[] { "libsa.so", "libsa_g.so" }; |
|
683 } |
|
684 |
|
685 // |
|
686 // Win32 |
|
687 // |
|
688 |
|
689 private void setupDebuggerWin32() { |
|
690 setupJVMLibNamesWin32(); |
|
691 |
|
692 if (cpu.equals("x86")) { |
|
693 machDesc = new MachineDescriptionIntelX86(); |
|
694 } else if (cpu.equals("amd64")) { |
|
695 machDesc = new MachineDescriptionAMD64(); |
|
696 } else if (cpu.equals("ia64")) { |
|
697 machDesc = new MachineDescriptionIA64(); |
|
698 } else { |
|
699 throw new DebuggerException("Win32 supported under x86, amd64 and ia64 only"); |
|
700 } |
|
701 |
|
702 // Note we do not use a cache for the local debugger in server |
|
703 // mode; it will be taken care of on the client side (once remote |
|
704 // debugging is implemented). |
|
705 |
|
706 debugger = new WindbgDebuggerLocal(machDesc, !isServer); |
|
707 |
|
708 attachDebugger(); |
|
709 } |
|
710 |
|
711 private void setupJVMLibNamesWin32() { |
|
712 jvmLibNames = new String[] { "jvm.dll", "jvm_g.dll" }; |
|
713 saLibNames = new String[] { "sa.dll", "sa_g.dll" }; |
|
714 } |
|
715 |
|
716 // |
|
717 // Linux |
|
718 // |
|
719 |
|
720 private void setupDebuggerLinux() { |
|
721 setupJVMLibNamesLinux(); |
|
722 |
|
723 if (cpu.equals("x86")) { |
|
724 machDesc = new MachineDescriptionIntelX86(); |
|
725 } else if (cpu.equals("ia64")) { |
|
726 machDesc = new MachineDescriptionIA64(); |
|
727 } else if (cpu.equals("amd64")) { |
|
728 machDesc = new MachineDescriptionAMD64(); |
|
729 } else if (cpu.equals("sparc")) { |
|
730 if (LinuxDebuggerLocal.getAddressSize()==8) { |
|
731 machDesc = new MachineDescriptionSPARC64Bit(); |
|
732 } else { |
|
733 machDesc = new MachineDescriptionSPARC32Bit(); |
|
734 } |
|
735 } else { |
|
736 try { |
|
737 machDesc = (MachineDescription) |
|
738 Class.forName("sun.jvm.hotspot.debugger.MachineDescription" + |
|
739 cpu.toUpperCase()).newInstance(); |
|
740 } catch (Exception e) { |
|
741 throw new DebuggerException("unsupported machine type"); |
|
742 } |
|
743 } |
|
744 |
|
745 |
|
746 // Note we do not use a cache for the local debugger in server |
|
747 // mode; it will be taken care of on the client side (once remote |
|
748 // debugging is implemented). |
|
749 |
|
750 debugger = new LinuxDebuggerLocal(machDesc, !isServer); |
|
751 attachDebugger(); |
|
752 } |
|
753 |
|
754 private void setupJVMLibNamesLinux() { |
|
755 // same as solaris |
|
756 setupJVMLibNamesSolaris(); |
|
757 } |
|
758 |
|
759 // |
|
760 // BSD |
|
761 // |
|
762 |
|
763 private void setupDebuggerBsd() { |
|
764 setupJVMLibNamesBsd(); |
|
765 |
|
766 if (cpu.equals("x86")) { |
|
767 machDesc = new MachineDescriptionIntelX86(); |
|
768 } else if (cpu.equals("amd64") || (cpu.equals("x86_64"))) { |
|
769 machDesc = new MachineDescriptionAMD64(); |
|
770 } else { |
|
771 throw new DebuggerException("Bsd only supported on x86/x86_64. Current arch: " + cpu); |
|
772 } |
|
773 |
|
774 // Note we do not use a cache for the local debugger in server |
|
775 // mode; it will be taken care of on the client side (once remote |
|
776 // debugging is implemented). |
|
777 |
|
778 debugger = new BsdDebuggerLocal(machDesc, !isServer); |
|
779 attachDebugger(); |
|
780 } |
|
781 |
|
782 private void setupJVMLibNamesBsd() { |
|
783 // same as solaris |
|
784 setupJVMLibNamesSolaris(); |
|
785 } |
|
786 |
|
787 /** Convenience routine which should be called by per-platform |
|
788 debugger setup. Should not be called when startupMode is |
|
789 REMOTE_MODE. */ |
|
790 private void attachDebugger() { |
|
791 if (startupMode == PROCESS_MODE) { |
|
792 debugger.attach(pid); |
|
793 } else if (startupMode == CORE_FILE_MODE) { |
|
794 debugger.attach(executableName, coreFileName); |
|
795 } else { |
|
796 throw new DebuggerException("Should not call attach() for startupMode == " + startupMode); |
|
797 } |
|
798 } |
|
799 } |
|