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