1 /* 2 * Copyright (c) 2000, 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; 26 27 import java.io.PrintStream; 28 import java.net.*; 29 import java.rmi.*; 30 import sun.jvm.hotspot.debugger.*; 31 import sun.jvm.hotspot.debugger.bsd.*; 32 import sun.jvm.hotspot.debugger.proc.*; 33 import sun.jvm.hotspot.debugger.remote.*; 34 import sun.jvm.hotspot.debugger.windbg.*; 35 import sun.jvm.hotspot.debugger.linux.*; 36 import sun.jvm.hotspot.memory.*; 37 import sun.jvm.hotspot.oops.*; 38 import sun.jvm.hotspot.runtime.*; 39 import sun.jvm.hotspot.types.*; 40 import sun.jvm.hotspot.utilities.*; 41 42 /** <P> This class wraps much of the basic functionality and is the 43 * highest-level factory for VM data structures. It makes it simple 44 * to start up the debugging system. </P> 45 * 46 * <P> FIXME: especially with the addition of remote debugging, this 47 * has turned into a mess; needs rethinking. </P> 48 */ 49 50 public class HotSpotAgent { 51 private JVMDebugger debugger; 52 private MachineDescription machDesc; 53 private TypeDataBase db; 54 55 private String os; 56 private String cpu; 57 private String fileSep; 58 59 // The system can work in several ways: 60 // - Attaching to local process 61 // - Attaching to local core file 62 // - Connecting to remote debug server 63 // - Starting debug server for process 64 // - Starting debug server for core file 65 66 // These are options for the "client" side of things 67 private static final int PROCESS_MODE = 0; 68 private static final int CORE_FILE_MODE = 1; 69 private static final int REMOTE_MODE = 2; 70 private int startupMode; 71 72 // This indicates whether we are really starting a server or not 73 private boolean isServer; 74 75 // All possible required information for connecting 76 private int pid; 77 private String javaExecutableName; 78 private String coreFileName; 79 private String debugServerID; 80 81 // All needed information for server side 82 private String serverID; 83 84 private String[] jvmLibNames; 85 86 static void showUsage() { 87 } 88 89 public HotSpotAgent() { 90 // for non-server add shutdown hook to clean-up debugger in case 91 // of forced exit. For remote server, shutdown hook is added by 92 // DebugServer. 93 Runtime.getRuntime().addShutdownHook(new java.lang.Thread( 94 new Runnable() { 95 public void run() { 96 synchronized (HotSpotAgent.this) { 97 if (!isServer) { 98 detach(); 99 } 100 } 101 } 102 })); 103 } 104 105 //-------------------------------------------------------------------------------- 106 // Accessors (once the system is set up) 107 // 108 109 public synchronized Debugger getDebugger() { 110 return debugger; 111 } 112 113 public synchronized TypeDataBase getTypeDataBase() { 114 return db; 115 } 116 117 //-------------------------------------------------------------------------------- 118 // Client-side operations 119 // 120 121 /** This attaches to a process running on the local machine. */ 122 public synchronized void attach(int processID) 123 throws DebuggerException { 124 if (debugger != null) { 125 throw new DebuggerException("Already attached"); 126 } 127 pid = processID; 128 startupMode = PROCESS_MODE; 129 isServer = false; 130 go(); 131 } 132 133 /** This opens a core file on the local machine */ 134 public synchronized void attach(String javaExecutableName, String coreFileName) 135 throws DebuggerException { 136 if (debugger != null) { 137 throw new DebuggerException("Already attached"); 138 } 139 if ((javaExecutableName == null) || (coreFileName == null)) { 140 throw new DebuggerException("Both the core file name and Java executable name must be specified"); 141 } 142 this.javaExecutableName = javaExecutableName; 143 this.coreFileName = coreFileName; 144 startupMode = CORE_FILE_MODE; 145 isServer = false; 146 go(); 147 } 148 149 /** This attaches to a "debug server" on a remote machine; this 150 remote server has already attached to a process or opened a 151 core file and is waiting for RMI calls on the Debugger object to 152 come in. */ 153 public synchronized void attach(String remoteServerID) 154 throws DebuggerException { 155 if (debugger != null) { 156 throw new DebuggerException("Already attached to a process"); 157 } 158 if (remoteServerID == null) { 159 throw new DebuggerException("Debug server id must be specified"); 160 } 161 162 debugServerID = remoteServerID; 163 startupMode = REMOTE_MODE; 164 isServer = false; 165 go(); 166 } 167 168 /** This should only be called by the user on the client machine, 169 not the server machine */ 170 public synchronized boolean detach() throws DebuggerException { 171 if (isServer) { 172 throw new DebuggerException("Should not call detach() for server configuration"); 173 } 174 return detachInternal(); 175 } 176 177 //-------------------------------------------------------------------------------- 178 // Server-side operations 179 // 180 181 /** This attaches to a process running on the local machine and 182 starts a debug server, allowing remote machines to connect and 183 examine this process. Uses specified name to uniquely identify a 184 specific debuggee on the server */ 185 public synchronized void startServer(int processID, String uniqueID) { 186 if (debugger != null) { 187 throw new DebuggerException("Already attached"); 188 } 189 pid = processID; 190 startupMode = PROCESS_MODE; 191 isServer = true; 192 serverID = uniqueID; 193 go(); 194 } 195 196 /** This attaches to a process running on the local machine and 197 starts a debug server, allowing remote machines to connect and 198 examine this process. */ 199 public synchronized void startServer(int processID) 200 throws DebuggerException { 201 startServer(processID, null); 202 } 203 204 /** This opens a core file on the local machine and starts a debug 205 server, allowing remote machines to connect and examine this 206 core file. Uses supplied uniqueID to uniquely identify a specific 207 debugee */ 208 public synchronized void startServer(String javaExecutableName, 209 String coreFileName, 210 String uniqueID) { 211 if (debugger != null) { 212 throw new DebuggerException("Already attached"); 213 } 214 if ((javaExecutableName == null) || (coreFileName == null)) { 215 throw new DebuggerException("Both the core file name and Java executable name must be specified"); 216 } 217 this.javaExecutableName = javaExecutableName; 218 this.coreFileName = coreFileName; 219 startupMode = CORE_FILE_MODE; 220 isServer = true; 221 serverID = uniqueID; 222 go(); 223 } 224 225 /** This opens a core file on the local machine and starts a debug 226 server, allowing remote machines to connect and examine this 227 core file. */ 228 public synchronized void startServer(String javaExecutableName, String coreFileName) 229 throws DebuggerException { 230 startServer(javaExecutableName, coreFileName, null); 231 } 232 233 /** This may only be called on the server side after startServer() 234 has been called */ 235 public synchronized boolean shutdownServer() throws DebuggerException { 236 if (!isServer) { 237 throw new DebuggerException("Should not call shutdownServer() for client configuration"); 238 } 239 return detachInternal(); 240 } 241 242 243 //-------------------------------------------------------------------------------- 244 // Internals only below this point 245 // 246 247 private boolean detachInternal() { 248 if (debugger == null) { 249 return false; 250 } 251 boolean retval = true; 252 if (!isServer) { 253 VM.shutdown(); 254 } 255 // We must not call detach() if we are a client and are connected 256 // to a remote debugger 257 Debugger dbg = null; 258 DebuggerException ex = null; 259 if (isServer) { 260 try { 261 RMIHelper.unbind(serverID); 262 } 263 catch (DebuggerException de) { 264 ex = de; 265 } 266 dbg = debugger; 267 } else { 268 if (startupMode != REMOTE_MODE) { 269 dbg = debugger; 270 } 271 } 272 if (dbg != null) { 273 retval = dbg.detach(); 274 } 275 276 debugger = null; 277 machDesc = null; 278 db = null; 279 if (ex != null) { 280 throw(ex); 281 } 282 return retval; 283 } 284 285 private void go() { 286 setupDebugger(); 287 setupVM(); 288 } 289 290 private void setupDebugger() { 291 if (startupMode != REMOTE_MODE) { 292 // 293 // Local mode (client attaching to local process or setting up 294 // server, but not client attaching to server) 295 // 296 297 try { 298 os = PlatformInfo.getOS(); 299 cpu = PlatformInfo.getCPU(); 300 } 301 catch (UnsupportedPlatformException e) { 302 throw new DebuggerException(e); 303 } 304 fileSep = System.getProperty("file.separator"); 305 306 if (os.equals("solaris")) { 307 setupDebuggerSolaris(); 308 } else if (os.equals("win32")) { 309 setupDebuggerWin32(); 310 } else if (os.equals("linux")) { 311 setupDebuggerLinux(); 312 } else if (os.equals("bsd")) { 313 setupDebuggerBsd(); 314 } else { 315 // Add support for more operating systems here 316 throw new DebuggerException("Operating system " + os + " not yet supported"); 317 } 318 319 if (isServer) { 320 RemoteDebuggerServer remote = null; 321 try { 322 remote = new RemoteDebuggerServer(debugger); 323 } 324 catch (RemoteException rem) { 325 throw new DebuggerException(rem); 326 } 327 RMIHelper.rebind(serverID, remote); 328 } 329 } else { 330 // 331 // Remote mode (client attaching to server) 332 // 333 334 // Create and install a security manager 335 336 // FIXME: currently commented out because we were having 337 // security problems since we're "in the sun.* hierarchy" here. 338 // Perhaps a permissive policy file would work around this. In 339 // the long run, will probably have to move into com.sun.*. 340 341 // if (System.getSecurityManager() == null) { 342 // System.setSecurityManager(new RMISecurityManager()); 343 // } 344 345 connectRemoteDebugger(); 346 } 347 } 348 349 private void setupVM() { 350 // We need to instantiate a HotSpotTypeDataBase on both the client 351 // and server machine. On the server it is only currently used to 352 // configure the Java primitive type sizes (which we should 353 // consider making constant). On the client it is used to 354 // configure the VM. 355 356 try { 357 if (os.equals("solaris")) { 358 db = new HotSpotTypeDataBase(machDesc, 359 new HotSpotSolarisVtblAccess(debugger, jvmLibNames), 360 debugger, jvmLibNames); 361 } else if (os.equals("win32")) { 362 db = new HotSpotTypeDataBase(machDesc, 363 new Win32VtblAccess(debugger, jvmLibNames), 364 debugger, jvmLibNames); 365 } else if (os.equals("linux")) { 366 db = new HotSpotTypeDataBase(machDesc, 367 new LinuxVtblAccess(debugger, jvmLibNames), 368 debugger, jvmLibNames); 369 } else if (os.equals("bsd")) { 370 db = new HotSpotTypeDataBase(machDesc, 371 new BsdVtblAccess(debugger, jvmLibNames), 372 debugger, jvmLibNames); 373 } else { 374 throw new DebuggerException("OS \"" + os + "\" not yet supported (no VtblAccess yet)"); 375 } 376 } 377 catch (NoSuchSymbolException e) { 378 throw new DebuggerException("Doesn't appear to be a HotSpot VM (could not find symbol \"" + 379 e.getSymbol() + "\" in remote process)"); 380 } 381 382 if (startupMode != REMOTE_MODE) { 383 // Configure the debugger with the primitive type sizes just obtained from the VM 384 debugger.configureJavaPrimitiveTypeSizes(db.getJBooleanType().getSize(), 385 db.getJByteType().getSize(), 386 db.getJCharType().getSize(), 387 db.getJDoubleType().getSize(), 388 db.getJFloatType().getSize(), 389 db.getJIntType().getSize(), 390 db.getJLongType().getSize(), 391 db.getJShortType().getSize()); 392 } 393 394 if (!isServer) { 395 // Do not initialize the VM on the server (unnecessary, since it's 396 // instantiated on the client) 397 try { 398 VM.initialize(db, debugger); 399 } catch (DebuggerException e) { 400 throw (e); 401 } catch (Exception e) { 402 throw new DebuggerException(e); 403 } 404 } 405 } 406 407 //-------------------------------------------------------------------------------- 408 // OS-specific debugger setup/connect routines 409 // 410 411 // 412 // Solaris 413 // 414 415 private void setupDebuggerSolaris() { 416 setupJVMLibNamesSolaris(); 417 ProcDebuggerLocal dbg = new ProcDebuggerLocal(null, true); 418 debugger = dbg; 419 attachDebugger(); 420 421 // Set up CPU-dependent stuff 422 if (cpu.equals("x86")) { 423 machDesc = new MachineDescriptionIntelX86(); 424 } else if (cpu.equals("sparc")) { 425 int addressSize = dbg.getRemoteProcessAddressSize(); 426 if (addressSize == -1) { 427 throw new DebuggerException("Error occurred while trying to determine the remote process's " + 428 "address size"); 429 } 430 431 if (addressSize == 32) { 432 machDesc = new MachineDescriptionSPARC32Bit(); 433 } else if (addressSize == 64) { 434 machDesc = new MachineDescriptionSPARC64Bit(); 435 } else { 436 throw new DebuggerException("Address size " + addressSize + " is not supported on SPARC"); 437 } 438 } else if (cpu.equals("amd64")) { 439 machDesc = new MachineDescriptionAMD64(); 440 } else { 441 throw new DebuggerException("Solaris only supported on sparc/sparcv9/x86/amd64"); 442 } 443 444 dbg.setMachineDescription(machDesc); 445 return; 446 } 447 448 private void connectRemoteDebugger() throws DebuggerException { 449 RemoteDebugger remote = 450 (RemoteDebugger) RMIHelper.lookup(debugServerID); 451 debugger = new RemoteDebuggerClient(remote); 452 machDesc = ((RemoteDebuggerClient) debugger).getMachineDescription(); 453 os = debugger.getOS(); 454 if (os.equals("solaris")) { 455 setupJVMLibNamesSolaris(); 456 } else if (os.equals("win32")) { 457 setupJVMLibNamesWin32(); 458 } else if (os.equals("linux")) { 459 setupJVMLibNamesLinux(); 460 } else if (os.equals("bsd")) { 461 setupJVMLibNamesBsd(); 462 } else { 463 throw new RuntimeException("Unknown OS type"); 464 } 465 466 cpu = debugger.getCPU(); 467 } 468 469 private void setupJVMLibNamesSolaris() { 470 jvmLibNames = new String[] { "libjvm.so", "libjvm_g.so", "gamma_g" }; 471 } 472 473 // 474 // Win32 475 // 476 477 private void setupDebuggerWin32() { 478 setupJVMLibNamesWin32(); 479 480 if (cpu.equals("x86")) { 481 machDesc = new MachineDescriptionIntelX86(); 482 } else if (cpu.equals("amd64")) { 483 machDesc = new MachineDescriptionAMD64(); 484 } else if (cpu.equals("ia64")) { 485 machDesc = new MachineDescriptionIA64(); 486 } else { 487 throw new DebuggerException("Win32 supported under x86, amd64 and ia64 only"); 488 } 489 490 // Note we do not use a cache for the local debugger in server 491 // mode; it will be taken care of on the client side (once remote 492 // debugging is implemented). 493 494 debugger = new WindbgDebuggerLocal(machDesc, !isServer); 495 496 attachDebugger(); 497 498 // FIXME: add support for server mode 499 } 500 501 private void setupJVMLibNamesWin32() { 502 jvmLibNames = new String[] { "jvm.dll", "jvm_g.dll" }; 503 } 504 505 // 506 // Linux 507 // 508 509 private void setupDebuggerLinux() { 510 setupJVMLibNamesLinux(); 511 512 if (cpu.equals("x86")) { 513 machDesc = new MachineDescriptionIntelX86(); 514 } else if (cpu.equals("ia64")) { 515 machDesc = new MachineDescriptionIA64(); 516 } else if (cpu.equals("amd64")) { 517 machDesc = new MachineDescriptionAMD64(); 518 } else if (cpu.equals("sparc")) { 519 if (LinuxDebuggerLocal.getAddressSize()==8) { 520 machDesc = new MachineDescriptionSPARC64Bit(); 521 } else { 522 machDesc = new MachineDescriptionSPARC32Bit(); 523 } 524 } else { 525 try { 526 machDesc = (MachineDescription) 527 Class.forName("sun.jvm.hotspot.debugger.MachineDescription" + 528 cpu.toUpperCase()).newInstance(); 529 } catch (Exception e) { 530 throw new DebuggerException("Linux not supported on machine type " + cpu); 531 } 532 } 533 534 LinuxDebuggerLocal dbg = 535 new LinuxDebuggerLocal(machDesc, !isServer); 536 debugger = dbg; 537 538 attachDebugger(); 539 } 540 541 private void setupJVMLibNamesLinux() { 542 jvmLibNames = new String[] { "libjvm.so", "libjvm_g.so" }; 543 } 544 545 // 546 // BSD 547 // 548 549 private void setupDebuggerBsd() { 550 setupJVMLibNamesBsd(); 551 552 if (cpu.equals("x86")) { 553 machDesc = new MachineDescriptionIntelX86(); 554 } else if (cpu.equals("amd64") || cpu.equals("x86_64")) { 555 machDesc = new MachineDescriptionAMD64(); 556 } else { 557 throw new DebuggerException("BSD only supported on x86/x86_64. Current arch: " + cpu); 558 } 559 560 BsdDebuggerLocal dbg = new BsdDebuggerLocal(machDesc, !isServer); 561 debugger = dbg; 562 563 attachDebugger(); 564 } 565 566 private void setupJVMLibNamesBsd() { 567 jvmLibNames = new String[] { "libjvm.so", "libjvm_g.so" }; 568 } 569 570 /** Convenience routine which should be called by per-platform 571 debugger setup. Should not be called when startupMode is 572 REMOTE_MODE. */ 573 private void attachDebugger() { 574 if (startupMode == PROCESS_MODE) { 575 debugger.attach(pid); 576 } else if (startupMode == CORE_FILE_MODE) { 577 debugger.attach(javaExecutableName, coreFileName); 578 } else { 579 throw new DebuggerException("Should not call attach() for startupMode == " + startupMode); 580 } 581 } 582 }