1 /*
   2  * Copyright (c) 2000, 2008, 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.debugger.dbx;
  26 
  27 import java.io.*;
  28 import java.net.*;
  29 import java.util.*;
  30 import sun.jvm.hotspot.debugger.*;
  31 import sun.jvm.hotspot.debugger.dbx.sparc.*;
  32 import sun.jvm.hotspot.debugger.dbx.x86.*;
  33 import sun.jvm.hotspot.debugger.cdbg.CDebugger;
  34 import sun.jvm.hotspot.utilities.*;
  35 
  36 /** <P> An implementation of the JVMDebugger interface which sits on
  37     top of dbx and relies on the SA's dbx import module for
  38     communication with the debugger. </P>
  39 
  40     <P> <B>NOTE</B> that since we have the notion of fetching "Java
  41     primitive types" from the remote process (which might have
  42     different sizes than we expect) we have a bootstrapping
  43     problem. We need to know the sizes of these types before we can
  44     fetch them. The current implementation solves this problem by
  45     requiring that it be configured with these type sizes before they
  46     can be fetched. The readJ(Type) routines here will throw a
  47     RuntimeException if they are called before the debugger is
  48     configured with the Java primitive type sizes. </P>
  49 */
  50 
  51 public class DbxDebuggerLocal extends DebuggerBase implements DbxDebugger {
  52   // These may be set by DbxDebuggerRemote
  53   protected boolean unalignedAccessesOkay;
  54   protected DbxThreadFactory threadFactory;
  55 
  56   private String dbxPathName;
  57   private String[] dbxSvcAgentDSOPathNames;
  58   private Process dbxProcess;
  59   private StreamMonitor dbxOutStreamMonitor;
  60   private StreamMonitor dbxErrStreamMonitor;
  61   private PrintWriter dbxOstr;
  62   private PrintWriter out;
  63   private InputLexer in;
  64   private Socket importModuleSocket;
  65   private static final int PORT = 21928;
  66   private static final int  LONG_TIMEOUT = 60000;
  67   private static final int  DBX_MODULE_NOT_FOUND      = 101;
  68   private static final int  DBX_MODULE_LOADED         = 102;
  69 
  70   //--------------------------------------------------------------------------------
  71   // Implementation of Debugger interface
  72   //
  73 
  74   /** <P> machDesc may be null if it couldn't be determined yet; i.e.,
  75       if we're on SPARC, we need to ask the remote process whether
  76       we're in 32- or 64-bit mode. </P>
  77 
  78       <P> useCache should be set to true if debugging is being done
  79       locally, and to false if the debugger is being created for the
  80       purpose of supporting remote debugging. </P> */
  81   public DbxDebuggerLocal(MachineDescription machDesc,
  82                           String dbxPathName,
  83                           String[] dbxSvcAgentDSOPathNames,
  84                           boolean useCache) {
  85     this.machDesc = machDesc;
  86     this.dbxPathName = dbxPathName;
  87     this.dbxSvcAgentDSOPathNames = dbxSvcAgentDSOPathNames;
  88     int cacheNumPages;
  89     int cachePageSize;
  90     if (PlatformInfo.getCPU().equals("sparc")) {
  91       cacheNumPages = parseCacheNumPagesProperty(2048);
  92       cachePageSize = 8192;
  93       threadFactory = new DbxSPARCThreadFactory(this);
  94     } else if (PlatformInfo.getCPU().equals("x86")) {
  95       cacheNumPages = 4096;
  96       cachePageSize = 4096;
  97       threadFactory = new DbxX86ThreadFactory(this);
  98       unalignedAccessesOkay = true;
  99     } else {
 100       throw new RuntimeException("Thread access for CPU architecture " + PlatformInfo.getCPU() + " not yet supported");
 101     }
 102     if (useCache) {
 103       // Cache portion of the remote process's address space.
 104       // Fetching data over the socket connection to dbx is relatively
 105       // slow. For now, this cache works best if it covers the entire
 106       // heap of the remote process. FIXME: at least should make this
 107       // tunable from the outside, i.e., via the UI. This is a 16 MB
 108       // cache divided on SPARC into 2048 8K pages and on x86 into
 109       // 4096 4K pages; the page size must be adjusted to be the OS's
 110       // page size. (FIXME: should pick this up from the debugger.)
 111       initCache(cachePageSize, cacheNumPages);
 112     }
 113   }
 114 
 115   /** Only called by DbxDebuggerRemote */
 116   protected DbxDebuggerLocal() {
 117   }
 118 
 119   /** FIXME: implement this with a Runtime.exec() of ps followed by
 120       parsing of its output */
 121   public boolean hasProcessList() throws DebuggerException {
 122     return false;
 123   }
 124 
 125   public List getProcessList() throws DebuggerException {
 126     throw new DebuggerException("Not yet supported");
 127   }
 128 
 129   /** From the Debugger interface via JVMDebugger */
 130   public synchronized void attach(int processID) throws DebuggerException {
 131     try {
 132       launchProcess();
 133       dbxErrStreamMonitor.addTrigger("dbx: no process", 1);
 134       dbxErrStreamMonitor.addTrigger("dbx: Cannot open", 1);
 135       dbxErrStreamMonitor.addTrigger("dbx: Cannot find", DBX_MODULE_NOT_FOUND);
 136       dbxOstr = new PrintWriter(dbxProcess.getOutputStream(), true);
 137       dbxOstr.println("debug - " + processID);
 138       dbxOstr.println("kprint -u2 \\(ready\\)");
 139       boolean seen = dbxErrStreamMonitor.waitFor("(ready)", LONG_TIMEOUT);
 140       if (!seen) {
 141         detach();
 142         throw new DebuggerException("Timed out while connecting to process " + processID);
 143       }
 144       List retVals = dbxErrStreamMonitor.getTriggersSeen();
 145       if (retVals.contains(new Integer(1))) {
 146         detach();
 147         throw new DebuggerException("No such process " + processID);
 148       }
 149 
 150       // Throws DebuggerException upon failure
 151       importDbxModule();
 152 
 153       dbxOstr.println("svc_agent_run");
 154 
 155       connectToImportModule();
 156 
 157       // Set "fail fast" mode on process memory reads
 158       printlnToOutput("peek_fail_fast 1");
 159     }
 160     catch (IOException e) {
 161       detach();
 162       throw new DebuggerException("Error while connecting to dbx process", e);
 163     }
 164   }
 165 
 166   /** From the Debugger interface via JVMDebugger */
 167   public synchronized void attach(String executableName, String coreFileName) throws DebuggerException {
 168     try {
 169       launchProcess();
 170       // Missing executable
 171       dbxErrStreamMonitor.addTrigger("dbx: Cannot open", 1);
 172       // Missing core file
 173       dbxErrStreamMonitor.addTrigger("dbx: can't read", 2);
 174       // Corrupt executable
 175       dbxErrStreamMonitor.addTrigger("dbx: File", 3);
 176       // Corrupt core file
 177       dbxErrStreamMonitor.addTrigger("dbx: Unable to read", 4);
 178       // Mismatched core and executable
 179       dbxErrStreamMonitor.addTrigger("dbx: core object name", 5);
 180       // Missing loadobject
 181       dbxErrStreamMonitor.addTrigger("dbx: can't stat", 6);
 182       // Successful load of svc module
 183       dbxOstr = new PrintWriter(dbxProcess.getOutputStream(), true);
 184       dbxOstr.println("debug " + executableName + " " + coreFileName);
 185       dbxOstr.println("kprint -u2 \\(ready\\)");
 186       boolean seen = dbxErrStreamMonitor.waitFor("(ready)", LONG_TIMEOUT);
 187       if (!seen) {
 188         detach();
 189         throw new DebuggerException("Timed out while attaching to core file");
 190       }
 191       List retVals = dbxErrStreamMonitor.getTriggersSeen();
 192       if (retVals.size() > 0) {
 193         detach();
 194 
 195         if (retVals.contains(new Integer(1))) {
 196           throw new DebuggerException("Can not find executable \"" + executableName + "\"");
 197         } else if (retVals.contains(new Integer(2))) {
 198           throw new DebuggerException("Can not find core file \"" + coreFileName + "\"");
 199         } else if (retVals.contains(new Integer(3))) {
 200           throw new DebuggerException("Corrupt executable \"" + executableName + "\"");
 201         } else if (retVals.contains(new Integer(4))) {
 202           throw new DebuggerException("Corrupt core file \"" + coreFileName + "\"");
 203         } else if (retVals.contains(new Integer(5))) {
 204           throw new DebuggerException("Mismatched core file/executable \"" + coreFileName + "\"/\"" + executableName + "\"");
 205         } else {
 206           throw new DebuggerException("Couldn't find all loaded libraries for executable \"" + executableName + "\"");
 207         }
 208       }
 209 
 210       // Throws DebuggerException upon failure
 211       importDbxModule();
 212 
 213       dbxOstr.println("svc_agent_run");
 214 
 215       connectToImportModule();
 216 
 217       // Set "fail fast" mode on process memory reads
 218       printlnToOutput("peek_fail_fast 1");
 219     }
 220     catch (IOException e) {
 221       detach();
 222       throw new DebuggerException("Error while connecting to dbx process", e);
 223     }
 224   }
 225 
 226   /** From the Debugger interface via JVMDebugger */
 227   public synchronized boolean detach() {
 228     try {
 229       if (dbxProcess == null) {
 230         return false;
 231       }
 232 
 233       if (out != null && dbxOstr != null) {
 234         printlnToOutput("exit");
 235         dbxOstr.println("exit");
 236 
 237         // Wait briefly for the process to exit (FIXME: should make this
 238         // nicer)
 239         try {
 240           Thread.sleep(500);
 241         }
 242         catch (InterruptedException e) {
 243         }
 244       }
 245 
 246       shutdown();
 247 
 248       return true;
 249     } catch (IOException e) {
 250       e.printStackTrace();
 251       return false;
 252     }
 253   }
 254 
 255   /** From the Debugger interface via JVMDebugger */
 256   public Address parseAddress(String addressString) throws NumberFormatException {
 257     long addr = utils.scanAddress(addressString);
 258     if (addr == 0) {
 259       return null;
 260     }
 261     return new DbxAddress(this, addr);
 262   }
 263 
 264   /** From the Debugger interface via JVMDebugger */
 265   public String getOS() {
 266     return PlatformInfo.getOS();
 267   }
 268 
 269   /** From the Debugger interface via JVMDebugger */
 270   public String getCPU() {
 271     return PlatformInfo.getCPU();
 272   }
 273 
 274   public boolean hasConsole() throws DebuggerException {
 275     return true;
 276   }
 277 
 278   public synchronized String consoleExecuteCommand(String cmd) throws DebuggerException {
 279     try {
 280       // A little tricky. We need to cause the dbx import module to
 281       // exit, then print our command on dbx's stdin along with a
 282       // command which will allow our StreamMonitors to
 283       // resynchronize. We need save the output from the StreamMonitors
 284       // along the way.
 285       printlnToOutput("exit");
 286       importModuleSocket.close();
 287       importModuleSocket = null;
 288       out = null;
 289       in = null;
 290       dbxOstr.println("kprint \\(ready\\)");
 291       dbxOstr.flush();
 292       dbxOutStreamMonitor.waitFor("(ready)", LONG_TIMEOUT);
 293 
 294       dbxOutStreamMonitor.startCapture();
 295       dbxErrStreamMonitor.startCapture();
 296       dbxOstr.println(cmd);
 297       dbxOstr.println("kprint \\(ready\\)");
 298       dbxOutStreamMonitor.waitFor("(ready)", LONG_TIMEOUT);
 299       String result = dbxOutStreamMonitor.stopCapture();
 300       String result2 = dbxErrStreamMonitor.stopCapture();
 301       result = result + result2;
 302       // Cut out the "(ready)" string
 303       StringBuffer outBuf = new StringBuffer(result.length());
 304       BufferedReader reader = new BufferedReader(new StringReader(result));
 305       // FIXME: bug in BufferedReader? readLine returns null when
 306       // ready() returns true.
 307       String line = null;
 308       do {
 309         line = reader.readLine();
 310         if ((line != null) && (!line.equals("(ready)"))) {
 311           outBuf.append(line);
 312           outBuf.append("\n");
 313         }
 314       } while (line != null);
 315       dbxOstr.println("svc_agent_run");
 316       dbxOstr.flush();
 317 
 318       connectToImportModule();
 319 
 320       return outBuf.toString();
 321     }
 322     catch (IOException e) {
 323       detach();
 324       throw new DebuggerException("Error while executing command on dbx console", e);
 325     }
 326   }
 327 
 328   public String getConsolePrompt() throws DebuggerException {
 329     return "(dbx) ";
 330   }
 331 
 332   public CDebugger getCDebugger() throws DebuggerException {
 333     return null;
 334   }
 335 
 336   /** From the SymbolLookup interface via Debugger and JVMDebugger */
 337   public synchronized Address lookup(String objectName, String symbol) {
 338     long addr = lookupInProcess(objectName, symbol);
 339     if (addr == 0) {
 340       return null;
 341     }
 342     return new DbxAddress(this, addr);
 343   }
 344 
 345   /** From the SymbolLookup interface via Debugger and JVMDebugger */
 346   public synchronized OopHandle lookupOop(String objectName, String symbol) {
 347     long addr = lookupInProcess(objectName, symbol);
 348     if (addr == 0) {
 349       return null;
 350     }
 351     return new DbxOopHandle(this, addr);
 352   }
 353 
 354   /** From the Debugger interface */
 355   public MachineDescription getMachineDescription() {
 356     return machDesc;
 357   }
 358 
 359   /** Internal routine supporting lazy setting of MachineDescription,
 360       since on SPARC we will need to query the remote process to ask
 361       it what its data model is (32- or 64-bit). NOTE that this is NOT
 362       present in the DbxDebugger interface because it should not be
 363       called across the wire (until we support attaching to multiple
 364       remote processes via RMI -- see the documentation for
 365       DbxDebuggerRemoteIntf.) */
 366   public void setMachineDescription(MachineDescription machDesc) {
 367     this.machDesc = machDesc;
 368     setBigEndian(machDesc.isBigEndian());
 369     utils = new DebuggerUtilities(machDesc.getAddressSize(), machDesc.isBigEndian());
 370   }
 371 
 372   /** Internal routine which queries the remote process about its data
 373       model -- i.e., size of addresses. Returns -1 upon error.
 374       Currently supported return values are 32 and 64. NOTE that this
 375       is NOT present in the DbxDebugger interface because it should
 376       not be called across the wire (until we support attaching to
 377       multiple remote processes via RMI -- see the documentation for
 378       DbxDebuggerRemoteIntf.) */
 379   public int getRemoteProcessAddressSize() {
 380     if (dbxProcess == null) {
 381       throw new RuntimeException("Not attached to remote process");
 382     }
 383 
 384     try {
 385       printlnToOutput("address_size");
 386       int i = in.parseInt();
 387       return i;
 388     }
 389     catch (IOException e) {
 390       return -1;
 391     }
 392   }
 393 
 394   //--------------------------------------------------------------------------------
 395   // Implementation of ThreadAccess interface
 396   //
 397 
 398   /** From the ThreadAccess interface via Debugger and JVMDebugger */
 399   public ThreadProxy getThreadForIdentifierAddress(Address addr) {
 400     return threadFactory.createThreadWrapper(addr);
 401   }
 402 
 403   public ThreadProxy getThreadForThreadId(long id) {
 404     return threadFactory.createThreadWrapper(id);
 405   }
 406 
 407   //----------------------------------------------------------------------
 408   // Overridden from DebuggerBase because we need to relax alignment
 409   // constraints on x86
 410 
 411   public long readJLong(long address)
 412     throws UnmappedAddressException, UnalignedAddressException {
 413     checkJavaConfigured();
 414     // FIXME: allow this to be configurable. Undesirable to add a
 415     // dependency on the runtime package here, though, since this
 416     // package should be strictly underneath it.
 417     if (unalignedAccessesOkay) {
 418       utils.checkAlignment(address, jintSize);
 419     } else {
 420       utils.checkAlignment(address, jlongSize);
 421     }
 422     byte[] data = readBytes(address, jlongSize);
 423     return utils.dataToJLong(data, jlongSize);
 424   }
 425 
 426   //--------------------------------------------------------------------------------
 427   // Internal routines (for implementation of DbxAddress).
 428   // These must not be called until the MachineDescription has been set up.
 429   //
 430 
 431   /** From the DbxDebugger interface */
 432   public String addressValueToString(long address) {
 433     return utils.addressValueToString(address);
 434   }
 435 
 436   /** Need to override this to relax alignment checks on Solaris/x86. */
 437   public long readCInteger(long address, long numBytes, boolean isUnsigned)
 438     throws UnmappedAddressException, UnalignedAddressException {
 439     checkConfigured();
 440     if (!unalignedAccessesOkay) {
 441       utils.checkAlignment(address, numBytes);
 442     } else {
 443       // Only slightly relaxed semantics -- this is a hack, but is
 444       // necessary on Solaris/x86 where it seems the compiler is
 445       // putting some global 64-bit data on 32-bit boundaries
 446       if (numBytes == 8) {
 447         utils.checkAlignment(address, 4);
 448       } else {
 449         utils.checkAlignment(address, numBytes);
 450       }
 451     }
 452     byte[] data = readBytes(address, numBytes);
 453     return utils.dataToCInteger(data, isUnsigned);
 454   }
 455 
 456   /** From the DbxDebugger interface */
 457   public DbxAddress readAddress(long address)
 458     throws UnmappedAddressException, UnalignedAddressException {
 459     long value = readAddressValue(address);
 460     return (value == 0 ? null : new DbxAddress(this, value));
 461   }
 462 
 463   public DbxAddress readCompOopAddress(long address)
 464     throws UnmappedAddressException, UnalignedAddressException {
 465     long value = readCompOopAddressValue(address);
 466     return (value == 0 ? null : new DbxAddress(this, value));
 467   }
 468 
 469   /** From the DbxDebugger interface */
 470   public DbxOopHandle readOopHandle(long address)
 471     throws UnmappedAddressException, UnalignedAddressException, NotInHeapException {
 472     long value = readAddressValue(address);
 473     return (value == 0 ? null : new DbxOopHandle(this, value));
 474   }
 475   public DbxOopHandle readCompOopHandle(long address)
 476     throws UnmappedAddressException, UnalignedAddressException, NotInHeapException {
 477     long value = readCompOopAddressValue(address);
 478     return (value == 0 ? null : new DbxOopHandle(this, value));
 479   }
 480 
 481   //--------------------------------------------------------------------------------
 482   // Thread context access. Can not be package private, but should
 483   // only be accessed by the architecture-specific subpackages.
 484 
 485   /** From the DbxDebugger interface. May have to redefine this later. */
 486   public synchronized long[] getThreadIntegerRegisterSet(int tid) {
 487     try {
 488       printlnToOutput("thr_gregs " + tid);
 489       int num = in.parseInt();
 490       long[] res = new long[num];
 491       for (int i = 0; i < num; i++) {
 492         res[i] = in.parseAddress();
 493       }
 494       return res;
 495     }
 496     catch (Exception e) {
 497       e.printStackTrace();
 498       return null;
 499     }
 500   }
 501 
 502   //--------------------------------------------------------------------------------
 503   // Address access. Can not be package private, but should only be
 504   // accessed by the architecture-specific subpackages.
 505 
 506   /** From the Debugger interface */
 507   public long getAddressValue(Address addr) {
 508     if (addr == null) return 0;
 509     return ((DbxAddress) addr).getValue();
 510   }
 511 
 512   /** From the DbxDebugger interface */
 513   public Address newAddress(long value) {
 514     if (value == 0) return null;
 515     return new DbxAddress(this, value);
 516   }
 517 
 518   //--------------------------------------------------------------------------------
 519   // Internals only below this point
 520   //
 521 
 522   private void launchProcess() throws IOException {
 523     dbxProcess = Runtime.getRuntime().exec(dbxPathName);
 524     //      dbxOutStreamMonitor = new StreamMonitor(dbxProcess.getInputStream());
 525     //      dbxErrStreamMonitor = new StreamMonitor(dbxProcess.getErrorStream());
 526     dbxOutStreamMonitor = new StreamMonitor(dbxProcess.getInputStream(), "dbx stdout", true);
 527     dbxErrStreamMonitor = new StreamMonitor(dbxProcess.getErrorStream(), "dbx stderr", true);
 528   }
 529 
 530   /** Requires that dbxErrStreamMonitor has a trigger on "dbx: Cannot
 531       find" with number DBX_MODULE_NOT_FOUND as well as one on "dbx:
 532       warning:" (plus the serviceability agent's dbx module path name,
 533       to avoid conflation with inability to load individual object
 534       files) with number DBX_MODULE_FAILED_TO_LOAD. The former
 535       indicates an absence of libsvc_agent_dbx.so, while the latter
 536       indicates that the module failed to load, specifically because
 537       the architecture was mismatched. (I don't see a way to detect
 538       from the dbx command prompt whether it's running the v8 or v9
 539       executbale, so we try to import both flavors of the import
 540       module; the "v8" file name convention doesn't actually include
 541       the v8 prefix, so this code should work for Intel as well.) */
 542   private void importDbxModule() throws DebuggerException {
 543     // Trigger for a successful load
 544     dbxOutStreamMonitor.addTrigger("Defining svc_agent_run", DBX_MODULE_LOADED);
 545     for (int i = 0; i < dbxSvcAgentDSOPathNames.length; i++) {
 546       dbxOstr.println("import " + dbxSvcAgentDSOPathNames[i]);
 547       dbxOstr.println("kprint -u2 \\(Ready\\)");
 548       boolean seen = dbxErrStreamMonitor.waitFor("(Ready)", LONG_TIMEOUT);
 549       if (!seen) {
 550         detach();
 551         throw new DebuggerException("Timed out while importing dbx module from file\n" + dbxSvcAgentDSOPathNames[i]);
 552       }
 553       List retVals = dbxErrStreamMonitor.getTriggersSeen();
 554       if (retVals.contains(new Integer(DBX_MODULE_NOT_FOUND))) {
 555         detach();
 556         throw new DebuggerException("Unable to find the Serviceability Agent's dbx import module at pathname \"" +
 557                                     dbxSvcAgentDSOPathNames[i] + "\"");
 558       } else {
 559         retVals = dbxOutStreamMonitor.getTriggersSeen();
 560         if (retVals.contains(new Integer(DBX_MODULE_LOADED))) {
 561           System.out.println("importDbxModule: imported " +  dbxSvcAgentDSOPathNames[i]);
 562           return;
 563         }
 564       }
 565     }
 566 
 567     // Failed to load all flavors
 568     detach();
 569     String errMsg = ("Unable to find a version of the Serviceability Agent's dbx import module\n" +
 570                      "matching the architecture of dbx at any of the following locations:");
 571     for (int i = 0; i < dbxSvcAgentDSOPathNames.length; i++) {
 572       errMsg = errMsg + "\n" + dbxSvcAgentDSOPathNames[i];
 573     }
 574     throw new DebuggerException(errMsg);
 575   }
 576 
 577   /** Terminate the debugger forcibly */
 578   private void shutdown() {
 579 
 580     if (dbxProcess != null) {
 581       // See whether the process has exited and, if not, terminate it
 582       // forcibly
 583       try {
 584         dbxProcess.exitValue();
 585       }
 586       catch (IllegalThreadStateException e) {
 587         dbxProcess.destroy();
 588       }
 589     }
 590 
 591     try {
 592       if (importModuleSocket != null) {
 593         importModuleSocket.close();
 594       }
 595     }
 596     catch (IOException e) {
 597     }
 598 
 599     // Release references to all objects
 600     clear();
 601     clearCache();
 602   }
 603 
 604   /** Looks up an address in the remote process's address space.
 605       Returns 0 if symbol not found or upon error. Package private to
 606       allow DbxDebuggerRemoteIntfImpl access. */
 607   synchronized long lookupInProcess(String objectName, String symbol) {
 608     try {
 609       printlnToOutput("lookup " + objectName + " " + symbol);
 610       return in.parseAddress();
 611     }
 612     catch (Exception e) {
 613       return 0;
 614     }
 615   }
 616 
 617   /** This reads bytes from the remote process. */
 618   public synchronized ReadResult readBytesFromProcess(long address, long numBytes)
 619     throws DebuggerException {
 620     if (numBytes < 0) {
 621       throw new DebuggerException("Can not read negative number (" + numBytes + ") of bytes from process");
 622     }
 623     try {
 624       String cmd = "peek " + utils.addressValueToString(address) + " " + numBytes;
 625       printlnToOutput(cmd);
 626       while (in.readByte() != 'B') {
 627       }
 628       byte res = in.readByte();
 629       if (res == 0) {
 630         System.err.println("Failing command: " + cmd);
 631         throw new DebuggerException("Read of remote process address space failed");
 632       }
 633       // NOTE: must read ALL of the data regardless of whether we need
 634       // to throw an UnmappedAddressException. Otherwise will corrupt
 635       // the input stream each time we have a failure. Not good. Do
 636       // not want to risk "flushing" the input stream in case a huge
 637       // read has a hangup in the middle and we leave data on the
 638       // stream.
 639       byte[] buf = new byte[(int) numBytes];
 640       boolean bailOut = false;
 641       long failureAddress = 0;
 642       int numReads = 0;
 643       while (numBytes > 0) {
 644         long len = in.readUnsignedInt();
 645         boolean isMapped = ((in.readByte() == 0) ? false : true);
 646         if (!isMapped) {
 647           if (!bailOut) {
 648             bailOut = true;
 649             failureAddress = address;
 650           }
 651         } else {
 652           // This won't work if we have unmapped regions, but if we do
 653           // then we're going to throw an exception anyway
 654 
 655           // NOTE: there is a factor of 20 speed difference between
 656           // these two ways of doing this read.
 657           in.readBytes(buf, 0, (int) len);
 658         }
 659 
 660         // Do NOT do this:
 661         //        for (int i = 0; i < (int) len; i++) {
 662         //          buf[i] = in.readByte();
 663         //        }
 664 
 665         numBytes -= len;
 666         address += len;
 667         ++numReads;
 668       }
 669       if (Assert.ASSERTS_ENABLED) {
 670         Assert.that(numBytes == 0, "Bug in debug server's implementation of peek: numBytesLeft == " +
 671                     numBytes + ", should be 0 (did " + numReads + " reads)");
 672       }
 673       if (bailOut) {
 674         return new ReadResult(failureAddress);
 675       }
 676       return new ReadResult(buf);
 677     }
 678     catch (IOException e) {
 679       throw new DebuggerException(e);
 680     }
 681   }
 682 
 683   public void writeBytesToProcess(long address, long numBytes, byte[] data)
 684     throws UnmappedAddressException, DebuggerException {
 685     // FIXME
 686     throw new DebuggerException("Unimplemented");
 687   }
 688 
 689   /** This provides DbxDebuggerRemoteIntfImpl access to readBytesFromProcess */
 690   ReadResult readBytesFromProcessInternal(long address, long numBytes)
 691     throws DebuggerException {
 692     return readBytesFromProcess(address, numBytes);
 693   }
 694 
 695   /** Convenience routine */
 696   private void printlnToOutput(String s) throws IOException {
 697     out.println(s);
 698     if (out.checkError()) {
 699       throw new IOException("Error occurred while writing to debug server");
 700     }
 701   }
 702 
 703   private void clear() {
 704     dbxProcess = null;
 705     dbxOstr = null;
 706     out = null;
 707     in = null;
 708     importModuleSocket = null;
 709   }
 710 
 711   /** Connects to the dbx import module, setting up out and in
 712       streams. Factored out to allow access to the dbx console. */
 713   private void connectToImportModule() throws IOException {
 714     // Try for 20 seconds to connect to dbx import module; time out
 715     // with failure if didn't succeed
 716     importModuleSocket = null;
 717     long endTime = System.currentTimeMillis() + LONG_TIMEOUT;
 718 
 719     while ((importModuleSocket == null) && (System.currentTimeMillis() < endTime)) {
 720       try {
 721         importModuleSocket = new Socket(InetAddress.getLocalHost(), PORT);
 722         importModuleSocket.setTcpNoDelay(true);
 723       }
 724       catch (IOException e) {
 725         // Swallow IO exceptions while attempting connection
 726         try {
 727           // Don't swamp the CPU
 728           Thread.sleep(1000);
 729         }
 730         catch (InterruptedException ex) {
 731         }
 732       }
 733     }
 734 
 735     if (importModuleSocket == null) {
 736       // Failed to connect because of timeout
 737       detach();
 738       throw new DebuggerException("Timed out while attempting to connect to remote dbx process");
 739     }
 740 
 741     out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(importModuleSocket.getOutputStream(), "US-ASCII")), true);
 742     in = new InputLexer(new BufferedInputStream(importModuleSocket.getInputStream()));
 743   }
 744 }