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