1 /* 2 * Copyright (c) 2000, 2013, 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 if (os.equals("darwin")) { 315 setupDebuggerDarwin(); 316 } else { 317 // Add support for more operating systems here 318 throw new DebuggerException("Operating system " + os + " not yet supported"); 319 } 320 321 if (isServer) { 322 RemoteDebuggerServer remote = null; 323 try { 324 remote = new RemoteDebuggerServer(debugger); 325 } 326 catch (RemoteException rem) { 327 throw new DebuggerException(rem); 328 } 329 RMIHelper.rebind(serverID, remote); 330 } 331 } else { 332 // 333 // Remote mode (client attaching to server) 334 // 335 336 // Create and install a security manager 337 338 // FIXME: currently commented out because we were having 339 // security problems since we're "in the sun.* hierarchy" here. 340 // Perhaps a permissive policy file would work around this. In 341 // the long run, will probably have to move into com.sun.*. 342 343 // if (System.getSecurityManager() == null) { 344 // System.setSecurityManager(new RMISecurityManager()); 345 // } 346 347 connectRemoteDebugger(); 348 } 349 } 350 351 private void setupVM() { 352 // We need to instantiate a HotSpotTypeDataBase on both the client 353 // and server machine. On the server it is only currently used to 354 // configure the Java primitive type sizes (which we should 355 // consider making constant). On the client it is used to 356 // configure the VM. 357 358 try { 359 if (os.equals("solaris")) { 360 db = new HotSpotTypeDataBase(machDesc, 361 new HotSpotSolarisVtblAccess(debugger, jvmLibNames), 362 debugger, jvmLibNames); 363 } else if (os.equals("win32")) { 364 db = new HotSpotTypeDataBase(machDesc, 365 new Win32VtblAccess(debugger, jvmLibNames), 366 debugger, jvmLibNames); 367 } else if (os.equals("linux")) { 368 db = new HotSpotTypeDataBase(machDesc, 369 new LinuxVtblAccess(debugger, jvmLibNames), 370 debugger, jvmLibNames); 371 } else if (os.equals("bsd")) { 372 db = new HotSpotTypeDataBase(machDesc, 373 new BsdVtblAccess(debugger, jvmLibNames), 374 debugger, jvmLibNames); 375 } else if (os.equals("darwin")) { 376 db = new HotSpotTypeDataBase(machDesc, 377 new BsdVtblAccess(debugger, jvmLibNames), 378 debugger, jvmLibNames); 379 } else { 380 throw new DebuggerException("OS \"" + os + "\" not yet supported (no VtblAccess yet)"); 381 } 382 } 383 catch (NoSuchSymbolException e) { 384 throw new DebuggerException("Doesn't appear to be a HotSpot VM (could not find symbol \"" + 385 e.getSymbol() + "\" in remote process)"); 386 } 387 388 if (startupMode != REMOTE_MODE) { 389 // Configure the debugger with the primitive type sizes just obtained from the VM 390 debugger.configureJavaPrimitiveTypeSizes(db.getJBooleanType().getSize(), 391 db.getJByteType().getSize(), 392 db.getJCharType().getSize(), 393 db.getJDoubleType().getSize(), 394 db.getJFloatType().getSize(), 395 db.getJIntType().getSize(), 396 db.getJLongType().getSize(), 397 db.getJShortType().getSize()); 398 } 399 400 if (!isServer) { 401 // Do not initialize the VM on the server (unnecessary, since it's 402 // instantiated on the client) 403 try { 404 VM.initialize(db, debugger); 405 } catch (DebuggerException e) { 406 throw (e); 407 } catch (Exception e) { 408 throw new DebuggerException(e); 409 } 410 } 411 } 412 413 //-------------------------------------------------------------------------------- 414 // OS-specific debugger setup/connect routines 415 // 416 417 // 418 // Solaris 419 // 420 421 private void setupDebuggerSolaris() { 422 setupJVMLibNamesSolaris(); 423 ProcDebuggerLocal dbg = new ProcDebuggerLocal(null, true); 424 debugger = dbg; 425 attachDebugger(); 426 427 // Set up CPU-dependent stuff 428 if (cpu.equals("x86")) { 429 machDesc = new MachineDescriptionIntelX86(); 430 } else if (cpu.equals("sparc")) { 431 int addressSize = dbg.getRemoteProcessAddressSize(); 432 if (addressSize == -1) { 433 throw new DebuggerException("Error occurred while trying to determine the remote process's " + 434 "address size"); 435 } 436 437 if (addressSize == 32) { 438 machDesc = new MachineDescriptionSPARC32Bit(); 439 } else if (addressSize == 64) { 440 machDesc = new MachineDescriptionSPARC64Bit(); 441 } else { 442 throw new DebuggerException("Address size " + addressSize + " is not supported on SPARC"); 443 } 444 } else if (cpu.equals("amd64")) { 445 machDesc = new MachineDescriptionAMD64(); 446 } else { 447 throw new DebuggerException("Solaris only supported on sparc/sparcv9/x86/amd64"); 448 } 449 450 dbg.setMachineDescription(machDesc); 451 return; 452 } 453 454 private void connectRemoteDebugger() throws DebuggerException { 455 RemoteDebugger remote = 456 (RemoteDebugger) RMIHelper.lookup(debugServerID); 457 debugger = new RemoteDebuggerClient(remote); 458 machDesc = ((RemoteDebuggerClient) debugger).getMachineDescription(); 459 os = debugger.getOS(); 460 if (os.equals("solaris")) { 461 setupJVMLibNamesSolaris(); 462 } else if (os.equals("win32")) { 463 setupJVMLibNamesWin32(); 464 } else if (os.equals("linux")) { 465 setupJVMLibNamesLinux(); 466 } else if (os.equals("bsd")) { 467 setupJVMLibNamesBsd(); 468 } else if (os.equals("darwin")) { 469 setupJVMLibNamesDarwin(); 470 } else { 471 throw new RuntimeException("Unknown OS type"); 472 } 473 474 cpu = debugger.getCPU(); 475 } 476 477 private void setupJVMLibNamesSolaris() { 478 jvmLibNames = new String[] { "libjvm.so", "libjvm_g.so", "gamma_g" }; 479 } 480 481 // 482 // Win32 483 // 484 485 private void setupDebuggerWin32() { 486 setupJVMLibNamesWin32(); 487 488 if (cpu.equals("x86")) { 489 machDesc = new MachineDescriptionIntelX86(); 490 } else if (cpu.equals("amd64")) { 491 machDesc = new MachineDescriptionAMD64(); 492 } else if (cpu.equals("ia64")) { 493 machDesc = new MachineDescriptionIA64(); 494 } else { 495 throw new DebuggerException("Win32 supported under x86, amd64 and ia64 only"); 496 } 497 498 // Note we do not use a cache for the local debugger in server 499 // mode; it will be taken care of on the client side (once remote 500 // debugging is implemented). 501 502 debugger = new WindbgDebuggerLocal(machDesc, !isServer); 503 504 attachDebugger(); 505 506 // FIXME: add support for server mode 507 } 508 509 private void setupJVMLibNamesWin32() { 510 jvmLibNames = new String[] { "jvm.dll", "jvm_g.dll" }; 511 } 512 513 // 514 // Linux 515 // 516 517 private void setupDebuggerLinux() { 518 setupJVMLibNamesLinux(); 519 520 if (cpu.equals("x86")) { 521 machDesc = new MachineDescriptionIntelX86(); 522 } else if (cpu.equals("ia64")) { 523 machDesc = new MachineDescriptionIA64(); 524 } else if (cpu.equals("amd64")) { 525 machDesc = new MachineDescriptionAMD64(); 526 } else if (cpu.equals("sparc")) { 527 if (LinuxDebuggerLocal.getAddressSize()==8) { 528 machDesc = new MachineDescriptionSPARC64Bit(); 529 } else { 530 machDesc = new MachineDescriptionSPARC32Bit(); 531 } 532 } else { 533 try { 534 machDesc = (MachineDescription) 535 Class.forName("sun.jvm.hotspot.debugger.MachineDescription" + 536 cpu.toUpperCase()).newInstance(); 537 } catch (Exception e) { 538 throw new DebuggerException("Linux not supported on machine type " + cpu); 539 } 540 } 541 542 LinuxDebuggerLocal dbg = 543 new LinuxDebuggerLocal(machDesc, !isServer); 544 debugger = dbg; 545 546 attachDebugger(); 547 } 548 549 private void setupJVMLibNamesLinux() { 550 jvmLibNames = new String[] { "libjvm.so", "libjvm_g.so" }; 551 } 552 553 // 554 // BSD 555 // 556 557 private void setupDebuggerBsd() { 558 setupJVMLibNamesBsd(); 559 560 if (cpu.equals("x86")) { 561 machDesc = new MachineDescriptionIntelX86(); 562 } else if (cpu.equals("amd64") || cpu.equals("x86_64")) { 563 machDesc = new MachineDescriptionAMD64(); 564 } else { 565 throw new DebuggerException("BSD only supported on x86/x86_64. Current arch: " + cpu); 566 } 567 568 BsdDebuggerLocal dbg = new BsdDebuggerLocal(machDesc, !isServer); 569 debugger = dbg; 570 571 attachDebugger(); 572 } 573 574 private void setupJVMLibNamesBsd() { 575 jvmLibNames = new String[] { "libjvm.so", "libjvm_g.so" }; 576 } 577 578 // 579 // Darwin 580 // 581 582 private void setupDebuggerDarwin() { 583 setupJVMLibNamesDarwin(); 584 585 if (cpu.equals("amd64") || cpu.equals("x86_64")) { 586 machDesc = new MachineDescriptionAMD64(); 587 } else { 588 throw new DebuggerException("Darwin only supported on x86_64. Current arch: " + cpu); 589 } 590 591 BsdDebuggerLocal dbg = new BsdDebuggerLocal(machDesc, !isServer); 592 debugger = dbg; 593 594 attachDebugger(); 595 } 596 597 private void setupJVMLibNamesDarwin() { 598 jvmLibNames = new String[] { "libjvm.dylib", "libjvm_g.dylib" }; 599 } 600 601 /** Convenience routine which should be called by per-platform 602 debugger setup. Should not be called when startupMode is 603 REMOTE_MODE. */ 604 private void attachDebugger() { 605 if (startupMode == PROCESS_MODE) { 606 debugger.attach(pid); 607 } else if (startupMode == CORE_FILE_MODE) { 608 debugger.attach(javaExecutableName, coreFileName); 609 } else { 610 throw new DebuggerException("Should not call attach() for startupMode == " + startupMode); 611 } 612 } 613 }