| /* |
| * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| * |
| */ |
| |
| package sun.jvm.hotspot.debugger.remote; |
| |
| import java.rmi.*; |
| import java.util.*; |
| import java.lang.reflect.*; |
| |
| import sun.jvm.hotspot.debugger.*; |
| import sun.jvm.hotspot.debugger.cdbg.*; |
| import sun.jvm.hotspot.debugger.remote.sparc.*; |
| import sun.jvm.hotspot.debugger.remote.x86.*; |
| import sun.jvm.hotspot.debugger.remote.amd64.*; |
| |
| /** An implementation of Debugger which wraps a |
| RemoteDebugger, providing remote debugging via RMI. |
| This implementation provides caching of the remote process's |
| address space on the local machine where the user interface is |
| running. */ |
| |
| public class RemoteDebuggerClient extends DebuggerBase implements JVMDebugger { |
| private RemoteDebugger remoteDebugger; |
| private RemoteThreadFactory threadFactory; |
| private boolean unalignedAccessesOkay = false; |
| private static final int cacheSize = 16 * 1024 * 1024; // 16 MB |
| |
| public RemoteDebuggerClient(RemoteDebugger remoteDebugger) throws DebuggerException { |
| super(); |
| try { |
| this.remoteDebugger = remoteDebugger; |
| machDesc = remoteDebugger.getMachineDescription(); |
| utils = new DebuggerUtilities(machDesc.getAddressSize(), machDesc.isBigEndian()); |
| int cacheNumPages; |
| int cachePageSize; |
| String cpu = remoteDebugger.getCPU(); |
| // page size. (FIXME: should pick this up from the remoteDebugger.) |
| if (cpu.equals("sparc")) { |
| threadFactory = new RemoteSPARCThreadFactory(this); |
| cachePageSize = 8192; |
| cacheNumPages = parseCacheNumPagesProperty(cacheSize / cachePageSize); |
| } else if (cpu.equals("x86")) { |
| threadFactory = new RemoteX86ThreadFactory(this); |
| cachePageSize = 4096; |
| cacheNumPages = parseCacheNumPagesProperty(cacheSize / cachePageSize); |
| unalignedAccessesOkay = true; |
| } else if (cpu.equals("amd64") || cpu.equals("x86_64")) { |
| threadFactory = new RemoteAMD64ThreadFactory(this); |
| cachePageSize = 4096; |
| cacheNumPages = parseCacheNumPagesProperty(cacheSize / cachePageSize); |
| unalignedAccessesOkay = true; |
| } else { |
| try { |
| Class tf = Class.forName("sun.jvm.hotspot.debugger.remote." + |
| cpu.toLowerCase() + ".Remote" + cpu.toUpperCase() + |
| "ThreadFactory"); |
| Constructor[] ctf = tf.getConstructors(); |
| threadFactory = (RemoteThreadFactory)ctf[0].newInstance(this); |
| } catch (Exception e) { |
| throw new DebuggerException("Thread access for CPU architecture " + cpu + " not yet supported"); |
| } |
| cachePageSize = 4096; |
| cacheNumPages = parseCacheNumPagesProperty(cacheSize / cachePageSize); |
| unalignedAccessesOkay = false; |
| } |
| |
| // Cache portion of the remote process's address space. |
| initCache(cachePageSize, cacheNumPages); |
| |
| jbooleanSize = remoteDebugger.getJBooleanSize(); |
| jbyteSize = remoteDebugger.getJByteSize(); |
| jcharSize = remoteDebugger.getJCharSize(); |
| jdoubleSize = remoteDebugger.getJDoubleSize(); |
| jfloatSize = remoteDebugger.getJFloatSize(); |
| jintSize = remoteDebugger.getJIntSize(); |
| jlongSize = remoteDebugger.getJLongSize(); |
| jshortSize = remoteDebugger.getJShortSize(); |
| javaPrimitiveTypesConfigured = true; |
| narrowOopBase = remoteDebugger.getNarrowOopBase(); |
| narrowOopShift = remoteDebugger.getNarrowOopShift(); |
| narrowKlassBase = remoteDebugger.getNarrowKlassBase(); |
| narrowKlassShift = remoteDebugger.getNarrowKlassShift(); |
| heapOopSize = remoteDebugger.getHeapOopSize(); |
| klassPtrSize = remoteDebugger.getKlassPtrSize(); |
| } |
| catch (RemoteException e) { |
| throw new DebuggerException(e); |
| } |
| } |
| |
| public long[] getThreadIntegerRegisterSet(Address addr) { |
| try { |
| return remoteDebugger.getThreadIntegerRegisterSet(getAddressValue(addr), true); |
| } |
| catch (RemoteException e) { |
| throw new DebuggerException(e); |
| } |
| } |
| |
| public long[] getThreadIntegerRegisterSet(long id) { |
| try { |
| return remoteDebugger.getThreadIntegerRegisterSet(id, false); |
| } |
| catch (RemoteException e) { |
| throw new DebuggerException(e); |
| } |
| } |
| |
| /** Unimplemented in this class (remote remoteDebugger should already be attached) */ |
| public boolean hasProcessList() throws DebuggerException { |
| throw new DebuggerException("Should not be called on RemoteDebuggerClient"); |
| } |
| |
| /** Unimplemented in this class (remote remoteDebugger should already be attached) */ |
| public List getProcessList() throws DebuggerException { |
| throw new DebuggerException("Should not be called on RemoteDebuggerClient"); |
| } |
| |
| /** Unimplemented in this class (remote remoteDebugger should already be attached) */ |
| public void attach(int processID) throws DebuggerException { |
| throw new DebuggerException("Should not be called on RemoteDebuggerClient"); |
| } |
| |
| /** Unimplemented in this class (remote remoteDebugger should already be attached) */ |
| public void attach(String executableName, String coreFileName) throws DebuggerException { |
| throw new DebuggerException("Should not be called on RemoteDebuggerClient"); |
| } |
| |
| /** Unimplemented in this class (remote remoteDebugger can not be detached) */ |
| public boolean detach() { |
| throw new DebuggerException("Should not be called on RemoteDebuggerClient"); |
| } |
| |
| public Address parseAddress(String addressString) throws NumberFormatException { |
| long addr = utils.scanAddress(addressString); |
| if (addr == 0) { |
| return null; |
| } |
| return new RemoteAddress(this, addr); |
| } |
| |
| public String getOS() throws DebuggerException { |
| try { |
| return remoteDebugger.getOS(); |
| } |
| catch (RemoteException e) { |
| throw new DebuggerException(e); |
| } |
| } |
| |
| public String getCPU() { |
| try { |
| return remoteDebugger.getCPU(); |
| } |
| catch (RemoteException e) { |
| throw new DebuggerException(e); |
| } |
| } |
| |
| public boolean hasConsole() throws DebuggerException { |
| try { |
| return remoteDebugger.hasConsole(); |
| } catch (RemoteException e) { |
| throw new DebuggerException(e); |
| } |
| } |
| |
| public String consoleExecuteCommand(String cmd) throws DebuggerException { |
| try { |
| return remoteDebugger.consoleExecuteCommand(cmd); |
| } |
| catch (RemoteException e) { |
| throw new DebuggerException(e); |
| } |
| } |
| |
| public String getConsolePrompt() throws DebuggerException { |
| try { |
| return remoteDebugger.getConsolePrompt(); |
| } catch (RemoteException e) { |
| throw new DebuggerException(e); |
| } |
| } |
| |
| public CDebugger getCDebugger() throws DebuggerException { |
| return null; |
| } |
| |
| //-------------------------------------------------------------------------------- |
| // Implementation of SymbolLookup interface |
| |
| public Address lookup(String objectName, String symbol) { |
| try { |
| long addr = remoteDebugger.lookupInProcess(objectName, symbol); |
| if (addr == 0) { |
| return null; |
| } |
| return new RemoteAddress(this, addr); |
| } |
| catch (RemoteException e) { |
| throw new DebuggerException(e); |
| } |
| } |
| |
| public OopHandle lookupOop(String objectName, String symbol) { |
| try { |
| long addr = remoteDebugger.lookupInProcess(objectName, symbol); |
| if (addr == 0) { |
| return null; |
| } |
| return new RemoteOopHandle(this, addr); |
| } |
| catch (RemoteException e) { |
| throw new DebuggerException(e); |
| } |
| } |
| |
| /** Need to override this to relax alignment checks on x86. */ |
| public long readCInteger(long address, long numBytes, boolean isUnsigned) |
| throws UnmappedAddressException, UnalignedAddressException { |
| if (!unalignedAccessesOkay) { |
| utils.checkAlignment(address, numBytes); |
| } else { |
| // Only slightly relaxed semantics -- this is a hack, but is |
| // necessary on x86 where it seems the compiler is |
| // putting some global 64-bit data on 32-bit boundaries |
| if (numBytes == 8) { |
| utils.checkAlignment(address, 4); |
| } else { |
| utils.checkAlignment(address, numBytes); |
| } |
| } |
| byte[] data = readBytes(address, numBytes); |
| return utils.dataToCInteger(data, isUnsigned); |
| } |
| |
| // Overridden from DebuggerBase because we need to relax alignment |
| // constraints on x86 |
| public long readJLong(long address) |
| throws UnmappedAddressException, UnalignedAddressException { |
| // FIXME: allow this to be configurable. Undesirable to add a |
| // dependency on the runtime package here, though, since this |
| // package should be strictly underneath it. |
| if (unalignedAccessesOkay) { |
| utils.checkAlignment(address, jintSize); |
| } else { |
| utils.checkAlignment(address, jlongSize); |
| } |
| byte[] data = readBytes(address, jlongSize); |
| return utils.dataToJLong(data, jlongSize); |
| } |
| |
| |
| //-------------------------------------------------------------------------------- |
| // Implementation of JVMDebugger interface |
| // |
| |
| /** Unimplemented in this class (remote remoteDebugger should already be configured) */ |
| public void configureJavaPrimitiveTypeSizes(long jbooleanSize, |
| long jbyteSize, |
| long jcharSize, |
| long jdoubleSize, |
| long jfloatSize, |
| long jintSize, |
| long jlongSize, |
| long jshortSize) { |
| throw new DebuggerException("Should not be called on RemoteDebuggerClient"); |
| } |
| |
| public void setMachineDescription(MachineDescription machDesc) { |
| throw new DebuggerException("Should not be called on RemoteDebuggerClient"); |
| } |
| |
| public int getRemoteProcessAddressSize() { |
| throw new DebuggerException("Should not be called on RemoteDebuggerClient"); |
| } |
| |
| public String addressValueToString(long addr) { |
| return utils.addressValueToString(addr); |
| } |
| |
| public long getAddressValue(Address addr) throws DebuggerException { |
| if (addr == null) return 0; |
| return ((RemoteAddress) addr).getValue(); |
| } |
| |
| public Address newAddress(long value) { |
| if (value == 0) return null; |
| return new RemoteAddress(this, value); |
| } |
| |
| RemoteAddress readAddress(long address) |
| throws UnmappedAddressException, UnalignedAddressException { |
| long value = readAddressValue(address); |
| return (value == 0 ? null : new RemoteAddress(this, value)); |
| } |
| |
| RemoteAddress readCompOopAddress(long address) |
| throws UnmappedAddressException, UnalignedAddressException { |
| long value = readCompOopAddressValue(address); |
| return (value == 0 ? null : new RemoteAddress(this, value)); |
| } |
| |
| RemoteAddress readCompKlassAddress(long address) |
| throws UnmappedAddressException, UnalignedAddressException { |
| long value = readCompKlassAddressValue(address); |
| return (value == 0 ? null : new RemoteAddress(this, value)); |
| } |
| |
| RemoteOopHandle readOopHandle(long address) |
| throws UnmappedAddressException, UnalignedAddressException, NotInHeapException { |
| long value = readAddressValue(address); |
| return (value == 0 ? null : new RemoteOopHandle(this, value)); |
| } |
| |
| RemoteOopHandle readCompOopHandle(long address) |
| throws UnmappedAddressException, UnalignedAddressException, NotInHeapException { |
| long value = readCompOopAddressValue(address); |
| return (value == 0 ? null : new RemoteOopHandle(this, value)); |
| } |
| |
| boolean areThreadsEqual(Address addr1, Address addr2) { |
| try { |
| return remoteDebugger.areThreadsEqual(getAddressValue(addr1), true, |
| getAddressValue(addr2), true); |
| } catch (RemoteException e) { |
| } |
| return false; |
| } |
| |
| boolean areThreadsEqual(long id1, long id2) { |
| try { |
| return remoteDebugger.areThreadsEqual(id1, false, id2, false); |
| } catch (RemoteException e) { |
| } |
| return false; |
| } |
| |
| boolean areThreadsEqual(Address addr1, long id2) { |
| try { |
| return remoteDebugger.areThreadsEqual(getAddressValue(addr1), true, id2, false); |
| } catch (RemoteException e) { |
| } |
| return false; |
| } |
| |
| boolean areThreadsEqual(long id1, Address addr2) { |
| try { |
| return remoteDebugger.areThreadsEqual(id1, false, getAddressValue(addr2), true); |
| } catch (RemoteException e) { |
| } |
| return false; |
| } |
| |
| int getThreadHashCode(Address a) { |
| try { |
| return remoteDebugger.getThreadHashCode(getAddressValue(a), true); |
| } catch (RemoteException e) { |
| } |
| return a.hashCode(); |
| } |
| |
| int getThreadHashCode(long id) { |
| try { |
| return remoteDebugger.getThreadHashCode(id, false); |
| } catch (RemoteException e) { |
| } |
| return (int) id; |
| } |
| |
| public ThreadProxy getThreadForIdentifierAddress(Address addr) { |
| return threadFactory.createThreadWrapper(addr); |
| } |
| |
| public ThreadProxy getThreadForThreadId(long id) { |
| return threadFactory.createThreadWrapper(id); |
| } |
| |
| public MachineDescription getMachineDescription() throws DebuggerException { |
| return machDesc; |
| } |
| |
| /** This reads bytes from the remote process. */ |
| public ReadResult readBytesFromProcess(long address, long numBytes) { |
| try { |
| return remoteDebugger.readBytesFromProcess(address, numBytes); |
| } |
| catch (RemoteException e) { |
| throw new DebuggerException(e); |
| } |
| } |
| |
| public void writeBytesToProcess(long a, long b, byte[] c) { |
| throw new DebuggerException("Unimplemented!"); |
| } |
| } |