1 /*
   2  * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 package sun.jvm.hotspot.debugger.remote;
  26 
  27 import java.rmi.*;
  28 import java.util.*;
  29 import java.lang.reflect.*;
  30 
  31 import sun.jvm.hotspot.debugger.*;
  32 import sun.jvm.hotspot.debugger.cdbg.*;
  33 import sun.jvm.hotspot.debugger.remote.sparc.*;
  34 import sun.jvm.hotspot.debugger.remote.x86.*;
  35 import sun.jvm.hotspot.debugger.remote.amd64.*;
  36 
  37 /** An implementation of Debugger which wraps a
  38     RemoteDebugger, providing remote debugging via RMI.
  39     This implementation provides caching of the remote process's
  40     address space on the local machine where the user interface is
  41     running. */
  42 
  43 public class RemoteDebuggerClient extends DebuggerBase implements JVMDebugger {
  44   private RemoteDebugger remoteDebugger;
  45   private RemoteThreadFactory threadFactory;
  46   private boolean unalignedAccessesOkay = false;
  47   private static final int cacheSize = 16 * 1024 * 1024; // 16 MB
  48 
  49   public RemoteDebuggerClient(RemoteDebugger remoteDebugger) throws DebuggerException {
  50     super();
  51     try {
  52       this.remoteDebugger = remoteDebugger;
  53       machDesc = remoteDebugger.getMachineDescription();
  54       utils = new DebuggerUtilities(machDesc.getAddressSize(), machDesc.isBigEndian());
  55       int cacheNumPages;
  56       int cachePageSize;
  57       String cpu = remoteDebugger.getCPU();
  58       // page size. (FIXME: should pick this up from the remoteDebugger.)
  59       if (cpu.equals("sparc")) {
  60         threadFactory = new RemoteSPARCThreadFactory(this);
  61         cachePageSize = 8192;
  62         cacheNumPages = parseCacheNumPagesProperty(cacheSize / cachePageSize);
  63       } else if (cpu.equals("x86")) {
  64         threadFactory = new RemoteX86ThreadFactory(this);
  65         cachePageSize = 4096;
  66         cacheNumPages = parseCacheNumPagesProperty(cacheSize / cachePageSize);
  67         unalignedAccessesOkay = true;
  68       } else if (cpu.equals("amd64") || cpu.equals("x86_64")) {
  69         threadFactory = new RemoteAMD64ThreadFactory(this);
  70         cachePageSize = 4096;
  71         cacheNumPages = parseCacheNumPagesProperty(cacheSize / cachePageSize);
  72         unalignedAccessesOkay = true;
  73       } else {
  74         try {
  75           Class tf = Class.forName("sun.jvm.hotspot.debugger.remote." +
  76             cpu.toLowerCase() + ".Remote" + cpu.toUpperCase() +
  77             "ThreadFactory");
  78           Constructor[] ctf = tf.getConstructors();
  79           threadFactory = (RemoteThreadFactory)ctf[0].newInstance(this);
  80         } catch (Exception e) {
  81           throw new DebuggerException("Thread access for CPU architecture " + cpu + " not yet supported");
  82         }
  83         cachePageSize = 4096;
  84         cacheNumPages = parseCacheNumPagesProperty(cacheSize / cachePageSize);
  85         unalignedAccessesOkay = false;
  86       }
  87 
  88       // Cache portion of the remote process's address space.
  89       initCache(cachePageSize, cacheNumPages);
  90 
  91       jbooleanSize = remoteDebugger.getJBooleanSize();
  92       jbyteSize    = remoteDebugger.getJByteSize();
  93       jcharSize    = remoteDebugger.getJCharSize();
  94       jdoubleSize  = remoteDebugger.getJDoubleSize();
  95       jfloatSize   = remoteDebugger.getJFloatSize();
  96       jintSize     = remoteDebugger.getJIntSize();
  97       jlongSize    = remoteDebugger.getJLongSize();
  98       jshortSize   = remoteDebugger.getJShortSize();
  99       javaPrimitiveTypesConfigured = true;
 100       narrowOopBase  = remoteDebugger.getNarrowOopBase();
 101       narrowOopShift = remoteDebugger.getNarrowOopShift();
 102       heapOopSize  = remoteDebugger.getHeapOopSize();
 103     }
 104     catch (RemoteException e) {
 105       throw new DebuggerException(e);
 106     }
 107   }
 108 
 109   public long[] getThreadIntegerRegisterSet(Address addr) {
 110     try {
 111       return remoteDebugger.getThreadIntegerRegisterSet(getAddressValue(addr), true);
 112     }
 113     catch (RemoteException e) {
 114       throw new DebuggerException(e);
 115     }
 116   }
 117 
 118   public long[] getThreadIntegerRegisterSet(long id) {
 119     try {
 120       return remoteDebugger.getThreadIntegerRegisterSet(id, false);
 121     }
 122     catch (RemoteException e) {
 123       throw new DebuggerException(e);
 124     }
 125   }
 126 
 127   /** Unimplemented in this class (remote remoteDebugger should already be attached) */
 128   public boolean hasProcessList() throws DebuggerException {
 129     throw new DebuggerException("Should not be called on RemoteDebuggerClient");
 130   }
 131 
 132   /** Unimplemented in this class (remote remoteDebugger should already be attached) */
 133   public List getProcessList() throws DebuggerException {
 134     throw new DebuggerException("Should not be called on RemoteDebuggerClient");
 135   }
 136 
 137   /** Unimplemented in this class (remote remoteDebugger should already be attached) */
 138   public void attach(int processID) throws DebuggerException {
 139     throw new DebuggerException("Should not be called on RemoteDebuggerClient");
 140   }
 141 
 142   /** Unimplemented in this class (remote remoteDebugger should already be attached) */
 143   public void attach(String executableName, String coreFileName) throws DebuggerException {
 144     throw new DebuggerException("Should not be called on RemoteDebuggerClient");
 145   }
 146 
 147   /** Unimplemented in this class (remote remoteDebugger can not be detached) */
 148   public boolean detach() {
 149     throw new DebuggerException("Should not be called on RemoteDebuggerClient");
 150   }
 151 
 152   public Address parseAddress(String addressString) throws NumberFormatException {
 153     long addr = utils.scanAddress(addressString);
 154     if (addr == 0) {
 155       return null;
 156     }
 157     return new RemoteAddress(this, addr);
 158   }
 159 
 160   public String getOS() throws DebuggerException {
 161     try {
 162       return remoteDebugger.getOS();
 163     }
 164     catch (RemoteException e) {
 165       throw new DebuggerException(e);
 166     }
 167   }
 168 
 169   public String getCPU() {
 170     try {
 171       return remoteDebugger.getCPU();
 172     }
 173     catch (RemoteException e) {
 174       throw new DebuggerException(e);
 175     }
 176   }
 177 
 178   public boolean hasConsole() throws DebuggerException {
 179     try {
 180        return remoteDebugger.hasConsole();
 181     } catch (RemoteException e) {
 182        throw new DebuggerException(e);
 183     }
 184   }
 185 
 186   public String consoleExecuteCommand(String cmd) throws DebuggerException {
 187     try {
 188       return remoteDebugger.consoleExecuteCommand(cmd);
 189     }
 190     catch (RemoteException e) {
 191       throw new DebuggerException(e);
 192     }
 193   }
 194 
 195   public String getConsolePrompt() throws DebuggerException {
 196     try {
 197       return remoteDebugger.getConsolePrompt();
 198     } catch (RemoteException e) {
 199       throw new DebuggerException(e);
 200     }
 201   }
 202 
 203   public CDebugger getCDebugger() throws DebuggerException {
 204     return null;
 205   }
 206 
 207   //--------------------------------------------------------------------------------
 208   // Implementation of SymbolLookup interface
 209 
 210   public Address lookup(String objectName, String symbol) {
 211     try {
 212       long addr = remoteDebugger.lookupInProcess(objectName, symbol);
 213       if (addr == 0) {
 214         return null;
 215       }
 216       return new RemoteAddress(this, addr);
 217     }
 218     catch (RemoteException e) {
 219       throw new DebuggerException(e);
 220     }
 221   }
 222 
 223   public OopHandle lookupOop(String objectName, String symbol) {
 224     try {
 225       long addr = remoteDebugger.lookupInProcess(objectName, symbol);
 226       if (addr == 0) {
 227         return null;
 228       }
 229       return new RemoteOopHandle(this, addr);
 230     }
 231     catch (RemoteException e) {
 232       throw new DebuggerException(e);
 233     }
 234   }
 235 
 236   /** Need to override this to relax alignment checks on x86. */
 237   public long readCInteger(long address, long numBytes, boolean isUnsigned)
 238     throws UnmappedAddressException, UnalignedAddressException {
 239     if (!unalignedAccessesOkay) {
 240       utils.checkAlignment(address, numBytes);
 241     } else {
 242       // Only slightly relaxed semantics -- this is a hack, but is
 243       // necessary on x86 where it seems the compiler is
 244       // putting some global 64-bit data on 32-bit boundaries
 245       if (numBytes == 8) {
 246         utils.checkAlignment(address, 4);
 247       } else {
 248         utils.checkAlignment(address, numBytes);
 249       }
 250     }
 251     byte[] data = readBytes(address, numBytes);
 252     return utils.dataToCInteger(data, isUnsigned);
 253   }
 254 
 255   // Overridden from DebuggerBase because we need to relax alignment
 256   // constraints on x86
 257   public long readJLong(long address)
 258     throws UnmappedAddressException, UnalignedAddressException {
 259     // FIXME: allow this to be configurable. Undesirable to add a
 260     // dependency on the runtime package here, though, since this
 261     // package should be strictly underneath it.
 262     if (unalignedAccessesOkay) {
 263       utils.checkAlignment(address, jintSize);
 264     } else {
 265       utils.checkAlignment(address, jlongSize);
 266     }
 267     byte[] data = readBytes(address, jlongSize);
 268     return utils.dataToJLong(data, jlongSize);
 269   }
 270 
 271 
 272   //--------------------------------------------------------------------------------
 273   // Implementation of JVMDebugger interface
 274   //
 275 
 276   /** Unimplemented in this class (remote remoteDebugger should already be configured) */
 277   public void configureJavaPrimitiveTypeSizes(long jbooleanSize,
 278                                               long jbyteSize,
 279                                               long jcharSize,
 280                                               long jdoubleSize,
 281                                               long jfloatSize,
 282                                               long jintSize,
 283                                               long jlongSize,
 284                                               long jshortSize) {
 285     throw new DebuggerException("Should not be called on RemoteDebuggerClient");
 286   }
 287 
 288   public void setMachineDescription(MachineDescription machDesc) {
 289     throw new DebuggerException("Should not be called on RemoteDebuggerClient");
 290   }
 291 
 292   public int getRemoteProcessAddressSize() {
 293     throw new DebuggerException("Should not be called on RemoteDebuggerClient");
 294   }
 295 
 296   public String addressValueToString(long addr) {
 297     return utils.addressValueToString(addr);
 298   }
 299 
 300   public long getAddressValue(Address addr) throws DebuggerException {
 301     if (addr == null) return 0;
 302     return ((RemoteAddress) addr).getValue();
 303   }
 304 
 305   public Address newAddress(long value) {
 306     if (value == 0) return null;
 307     return new RemoteAddress(this, value);
 308   }
 309 
 310   RemoteAddress readAddress(long address)
 311     throws UnmappedAddressException, UnalignedAddressException {
 312     long value = readAddressValue(address);
 313     return (value == 0 ? null : new RemoteAddress(this, value));
 314   }
 315 
 316   RemoteAddress readCompOopAddress(long address)
 317     throws UnmappedAddressException, UnalignedAddressException {
 318     long value = readCompOopAddressValue(address);
 319     return (value == 0 ? null : new RemoteAddress(this, value));
 320   }
 321 
 322   RemoteOopHandle readOopHandle(long address)
 323     throws UnmappedAddressException, UnalignedAddressException, NotInHeapException {
 324     long value = readAddressValue(address);
 325     return (value == 0 ? null : new RemoteOopHandle(this, value));
 326   }
 327 
 328   RemoteOopHandle readCompOopHandle(long address)
 329     throws UnmappedAddressException, UnalignedAddressException, NotInHeapException {
 330     long value = readCompOopAddressValue(address);
 331     return (value == 0 ? null : new RemoteOopHandle(this, value));
 332   }
 333 
 334   boolean areThreadsEqual(Address addr1, Address addr2) {
 335     try {
 336        return remoteDebugger.areThreadsEqual(getAddressValue(addr1), true,
 337                                              getAddressValue(addr2), true);
 338     } catch (RemoteException e) {
 339     }
 340     return false;
 341   }
 342 
 343   boolean areThreadsEqual(long id1, long id2) {
 344     try {
 345        return remoteDebugger.areThreadsEqual(id1, false, id2, false);
 346     } catch (RemoteException e) {
 347     }
 348     return false;
 349   }
 350 
 351   boolean areThreadsEqual(Address addr1, long id2) {
 352     try {
 353        return remoteDebugger.areThreadsEqual(getAddressValue(addr1), true, id2, false);
 354     } catch (RemoteException e) {
 355     }
 356     return false;
 357   }
 358 
 359   boolean areThreadsEqual(long id1, Address addr2) {
 360     try {
 361        return remoteDebugger.areThreadsEqual(id1, false, getAddressValue(addr2), true);
 362     } catch (RemoteException e) {
 363     }
 364     return false;
 365   }
 366 
 367   int getThreadHashCode(Address a) {
 368     try {
 369        return remoteDebugger.getThreadHashCode(getAddressValue(a), true);
 370     } catch (RemoteException e) {
 371     }
 372     return a.hashCode();
 373   }
 374 
 375   int getThreadHashCode(long id) {
 376     try {
 377        return remoteDebugger.getThreadHashCode(id, false);
 378     } catch (RemoteException e) {
 379     }
 380     return (int) id;
 381   }
 382 
 383   public ThreadProxy getThreadForIdentifierAddress(Address addr) {
 384      return threadFactory.createThreadWrapper(addr);
 385   }
 386 
 387   public ThreadProxy getThreadForThreadId(long id) {
 388      return threadFactory.createThreadWrapper(id);
 389   }
 390 
 391   public MachineDescription getMachineDescription() throws DebuggerException {
 392      return machDesc;
 393   }
 394 
 395   /** This reads bytes from the remote process. */
 396   public ReadResult readBytesFromProcess(long address, long numBytes) {
 397     try {
 398       return remoteDebugger.readBytesFromProcess(address, numBytes);
 399     }
 400     catch (RemoteException e) {
 401       throw new DebuggerException(e);
 402     }
 403   }
 404 
 405   public void writeBytesToProcess(long a, long b, byte[] c) {
 406      throw new DebuggerException("Unimplemented!");
 407   }
 408 }