--- old/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32DebuggerLocal.java Fri Sep 9 14:17:43 2011 +++ /dev/null Fri Sep 9 14:17:22 2011 @@ -1,1083 +0,0 @@ -/* - * Copyright (c) 2000, 2008, 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.win32; - -import java.io.*; -import java.net.*; -import java.util.*; -import sun.jvm.hotspot.debugger.*; -import sun.jvm.hotspot.debugger.x86.*; -import sun.jvm.hotspot.debugger.win32.coff.*; -import sun.jvm.hotspot.debugger.cdbg.*; -import sun.jvm.hotspot.debugger.cdbg.basic.BasicDebugEvent; -import sun.jvm.hotspot.utilities.*; -import sun.jvm.hotspot.utilities.memo.*; - -/**

An implementation of the JVMDebugger interface which talks to - the Free Windows Debug Server (FwDbgSrv) over a socket to - implement attach/detach and read from process memory. All DLL and - symbol table management is done in Java.

- -

NOTE that since we have the notion of fetching "Java - primitive types" from the remote process (which might have - different sizes than we expect) we have a bootstrapping - problem. We need to know the sizes of these types before we can - fetch them. The current implementation solves this problem by - requiring that it be configured with these type sizes before they - can be fetched. The readJ(Type) routines here will throw a - RuntimeException if they are called before the debugger is - configured with the Java primitive type sizes.

*/ - -public class Win32DebuggerLocal extends DebuggerBase implements Win32Debugger { - private Socket debuggerSocket; - private boolean attached; - // FIXME: update when core files supported - private long pid; - // Communication with debug server - private PrintWriter out; - private DataOutputStream rawOut; - private InputLexer in; - private static final int PORT = 27000; - private PageCache cache; - private static final long SHORT_TIMEOUT = 2000; - private static final long LONG_TIMEOUT = 20000; - - // Symbol lookup support - // This is a map of library names to DLLs - private Map nameToDllMap; - - // C/C++ debugging support - private List/**/ loadObjects; - private CDebugger cdbg; - - // ProcessControl support - private boolean suspended; - // Maps Long objects (addresses) to Byte objects (original instructions) - // (Longs used instead of Addresses to properly represent breakpoints at 0x0 if needed) - private Map breakpoints; - // Current debug event, if any - private DebugEvent curDebugEvent; - - //-------------------------------------------------------------------------------- - // Implementation of Debugger interface - // - - /**

machDesc may not be null.

- -

useCache should be set to true if debugging is being done - locally, and to false if the debugger is being created for the - purpose of supporting remote debugging.

*/ - public Win32DebuggerLocal(MachineDescription machDesc, - boolean useCache) throws DebuggerException { - this.machDesc = machDesc; - utils = new DebuggerUtilities(machDesc.getAddressSize(), machDesc.isBigEndian()); - if (useCache) { - // Cache portion of the remote process's address space. - // Fetching data over the socket connection to dbx is slow. - // Might be faster if we were using a binary protocol to talk to - // dbx, but would have to test. For now, this cache works best - // if it covers the entire heap of the remote process. FIXME: at - // least should make this tunable from the outside, i.e., via - // the UI. This is a cache of 4096 4K pages, or 16 MB. The page - // size must be adjusted to be the hardware's page size. - // (FIXME: should pick this up from the debugger.) - initCache(4096, parseCacheNumPagesProperty(4096)); - } - // FIXME: add instantiation of thread factory - - try { - connectToDebugServer(); - } catch (IOException e) { - throw new DebuggerException(e); - } - } - - /** From the Debugger interface via JVMDebugger */ - public boolean hasProcessList() throws DebuggerException { - return true; - } - - /** From the Debugger interface via JVMDebugger */ - public List getProcessList() throws DebuggerException { - List processes = new ArrayList(); - - try { - printlnToOutput("proclist"); - int num = in.parseInt(); - for (int i = 0; i < num; i++) { - int pid = in.parseInt(); - String name = parseString(); - // NOTE: Win32 hack - if (name.equals("")) { - name = "System Idle Process"; - } - processes.add(new ProcessInfo(name, pid)); - } - return processes; - } - catch (IOException e) { - throw new DebuggerException(e); - } - } - - /** From the Debugger interface via JVMDebugger */ - public synchronized void attach(int processID) throws DebuggerException { - if (attached) { - // FIXME: update when core files supported - throw new DebuggerException("Already attached to process " + pid); - } - - try { - printlnToOutput("attach " + processID); - if (!in.parseBoolean()) { - throw new DebuggerException("Error attaching to process, or no such process"); - } - - attached = true; - pid = processID; - suspended = true; - breakpoints = new HashMap(); - curDebugEvent = null; - nameToDllMap = null; - loadObjects = null; - } - catch (IOException e) { - throw new DebuggerException(e); - } - } - - /** From the Debugger interface via JVMDebugger */ - public synchronized void attach(String executableName, String coreFileName) throws DebuggerException { - throw new DebuggerException("Core files not yet supported on Win32"); - } - - /** From the Debugger interface via JVMDebugger */ - public synchronized boolean detach() { - if (!attached) { - return false; - } - - attached = false; - suspended = false; - breakpoints = null; - - // Close all open DLLs - if (nameToDllMap != null) { - for (Iterator iter = nameToDllMap.values().iterator(); iter.hasNext(); ) { - DLL dll = (DLL) iter.next(); - dll.close(); - } - nameToDllMap = null; - loadObjects = null; - } - - cdbg = null; - clearCache(); - - try { - printlnToOutput("detach"); - return in.parseBoolean(); - } - catch (IOException e) { - throw new DebuggerException(e); - } - } - - /** From the Debugger interface via JVMDebugger */ - public Address parseAddress(String addressString) throws NumberFormatException { - return newAddress(utils.scanAddress(addressString)); - } - - /** From the Debugger interface via JVMDebugger */ - public String getOS() { - return PlatformInfo.getOS(); - } - - /** From the Debugger interface via JVMDebugger */ - public String getCPU() { - return PlatformInfo.getCPU(); - } - - public boolean hasConsole() throws DebuggerException { - return false; - } - - public String consoleExecuteCommand(String cmd) throws DebuggerException { - throw new DebuggerException("No debugger console available on Win32"); - } - - public String getConsolePrompt() throws DebuggerException { - return null; - } - - public CDebugger getCDebugger() throws DebuggerException { - if (cdbg == null) { - cdbg = new Win32CDebugger(this); - } - return cdbg; - } - - /** From the SymbolLookup interface via Debugger and JVMDebugger */ - public synchronized Address lookup(String objectName, String symbol) { - if (!attached) { - return null; - } - return newAddress(lookupInProcess(objectName, symbol)); - } - - /** From the SymbolLookup interface via Debugger and JVMDebugger */ - public synchronized OopHandle lookupOop(String objectName, String symbol) { - Address addr = lookup(objectName, symbol); - if (addr == null) { - return null; - } - return addr.addOffsetToAsOopHandle(0); - } - - /** From the Debugger interface */ - public MachineDescription getMachineDescription() { - return machDesc; - } - - //-------------------------------------------------------------------------------- - // Implementation of ThreadAccess interface - // - - /** From the ThreadAccess interface via Debugger and JVMDebugger */ - public ThreadProxy getThreadForIdentifierAddress(Address addr) { - return new Win32Thread(this, addr); - } - - public ThreadProxy getThreadForThreadId(long handle) { - return new Win32Thread(this, handle); - } - - //---------------------------------------------------------------------- - // Overridden from DebuggerBase because we need to relax alignment - // constraints on x86 - - public long readJLong(long address) - throws UnmappedAddressException, UnalignedAddressException { - checkJavaConfigured(); - // 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. - // utils.checkAlignment(address, jlongSize); - utils.checkAlignment(address, jintSize); - byte[] data = readBytes(address, jlongSize); - return utils.dataToJLong(data, jlongSize); - } - - //-------------------------------------------------------------------------------- - // Internal routines (for implementation of Win32Address). - // These must not be called until the MachineDescription has been set up. - // - - /** From the Win32Debugger interface */ - public String addressValueToString(long address) { - return utils.addressValueToString(address); - } - - /** From the Win32Debugger interface */ - public Win32Address readAddress(long address) - throws UnmappedAddressException, UnalignedAddressException { - return (Win32Address) newAddress(readAddressValue(address)); - } - - public Win32Address readCompOopAddress(long address) - throws UnmappedAddressException, UnalignedAddressException { - return (Win32Address) newAddress(readCompOopAddressValue(address)); - } - - /** From the Win32Debugger interface */ - public Win32OopHandle readOopHandle(long address) - throws UnmappedAddressException, UnalignedAddressException, NotInHeapException { - long value = readAddressValue(address); - return (value == 0 ? null : new Win32OopHandle(this, value)); - } - public Win32OopHandle readCompOopHandle(long address) - throws UnmappedAddressException, UnalignedAddressException, NotInHeapException { - long value = readCompOopAddressValue(address); - return (value == 0 ? null : new Win32OopHandle(this, value)); - } - - /** From the Win32Debugger interface */ - public void writeAddress(long address, Win32Address value) { - writeAddressValue(address, getAddressValue(value)); - } - - /** From the Win32Debugger interface */ - public void writeOopHandle(long address, Win32OopHandle value) { - writeAddressValue(address, getAddressValue(value)); - } - - //-------------------------------------------------------------------------------- - // Thread context access - // - - public synchronized long[] getThreadIntegerRegisterSet(int threadHandleValue, - boolean mustDuplicateHandle) - throws DebuggerException { - if (!suspended) { - throw new DebuggerException("Process not suspended"); - } - - try { - int handle = threadHandleValue; - if (mustDuplicateHandle) { - printlnToOutput("duphandle 0x" + Integer.toHexString(threadHandleValue)); - if (!in.parseBoolean()) { - throw new DebuggerException("Error duplicating thread handle 0x" + threadHandleValue); - } - handle = (int) in.parseAddress(); // Must close to avoid leaks - } - printlnToOutput("getcontext 0x" + Integer.toHexString(handle)); - if (!in.parseBoolean()) { - if (mustDuplicateHandle) { - printlnToOutput("closehandle 0x" + Integer.toHexString(handle)); - } - String failMessage = "GetThreadContext failed for thread handle 0x" + - Integer.toHexString(handle); - if (mustDuplicateHandle) { - failMessage = failMessage + ", duplicated from thread handle " + - Integer.toHexString(threadHandleValue); - } - throw new DebuggerException(failMessage); - } - // Otherwise, parse all registers. See - // src/os/win32/agent/README-commands.txt for the format. - // Note the array we have to return has to match that specified by - // X86ThreadContext.java. - int numRegs = 22; - long[] winRegs = new long[numRegs]; - for (int i = 0; i < numRegs; i++) { - winRegs[i] = in.parseAddress(); - } - if (mustDuplicateHandle) { - // Clean up after ourselves - printlnToOutput("closehandle 0x" + Integer.toHexString(handle)); - } - // Now create the real return value - long[] retval = new long[X86ThreadContext.NPRGREG]; - retval[X86ThreadContext.EAX] = winRegs[0]; - retval[X86ThreadContext.EBX] = winRegs[1]; - retval[X86ThreadContext.ECX] = winRegs[2]; - retval[X86ThreadContext.EDX] = winRegs[3]; - retval[X86ThreadContext.ESI] = winRegs[4]; - retval[X86ThreadContext.EDI] = winRegs[5]; - retval[X86ThreadContext.EBP] = winRegs[6]; - retval[X86ThreadContext.ESP] = winRegs[7]; - retval[X86ThreadContext.EIP] = winRegs[8]; - retval[X86ThreadContext.DS] = winRegs[9]; - retval[X86ThreadContext.ES] = winRegs[10]; - retval[X86ThreadContext.FS] = winRegs[11]; - retval[X86ThreadContext.GS] = winRegs[12]; - retval[X86ThreadContext.CS] = winRegs[13]; - retval[X86ThreadContext.SS] = winRegs[14]; - retval[X86ThreadContext.EFL] = winRegs[15]; - retval[X86ThreadContext.DR0] = winRegs[16]; - retval[X86ThreadContext.DR1] = winRegs[17]; - retval[X86ThreadContext.DR2] = winRegs[18]; - retval[X86ThreadContext.DR3] = winRegs[19]; - retval[X86ThreadContext.DR6] = winRegs[20]; - retval[X86ThreadContext.DR7] = winRegs[21]; - return retval; - } catch (IOException e) { - throw new DebuggerException(e); - } - } - - public synchronized void setThreadIntegerRegisterSet(int threadHandleValue, - boolean mustDuplicateHandle, - long[] context) - throws DebuggerException { - if (!suspended) { - throw new DebuggerException("Process not suspended"); - } - - try { - int handle = threadHandleValue; - if (mustDuplicateHandle) { - printlnToOutput("duphandle 0x" + Integer.toHexString(threadHandleValue)); - if (!in.parseBoolean()) { - throw new DebuggerException("Error duplicating thread handle 0x" + threadHandleValue); - } - handle = (int) in.parseAddress(); // Must close to avoid leaks - } - // Change order of registers to match that of debug server - long[] winRegs = new long[context.length]; - winRegs[0] = context[X86ThreadContext.EAX]; - winRegs[1] = context[X86ThreadContext.EBX]; - winRegs[2] = context[X86ThreadContext.ECX]; - winRegs[3] = context[X86ThreadContext.EDX]; - winRegs[4] = context[X86ThreadContext.ESI]; - winRegs[5] = context[X86ThreadContext.EDI]; - winRegs[6] = context[X86ThreadContext.EBP]; - winRegs[7] = context[X86ThreadContext.ESP]; - winRegs[8] = context[X86ThreadContext.EIP]; - winRegs[9] = context[X86ThreadContext.DS]; - winRegs[10] = context[X86ThreadContext.ES]; - winRegs[11] = context[X86ThreadContext.FS]; - winRegs[12] = context[X86ThreadContext.GS]; - winRegs[13] = context[X86ThreadContext.CS]; - winRegs[14] = context[X86ThreadContext.SS]; - winRegs[15] = context[X86ThreadContext.EFL]; - winRegs[16] = context[X86ThreadContext.DR0]; - winRegs[17] = context[X86ThreadContext.DR1]; - winRegs[18] = context[X86ThreadContext.DR2]; - winRegs[19] = context[X86ThreadContext.DR3]; - winRegs[20] = context[X86ThreadContext.DR6]; - winRegs[21] = context[X86ThreadContext.DR7]; - StringBuffer cmd = new StringBuffer(); - cmd.append("setcontext 0x"); - cmd.append(Integer.toHexString(threadHandleValue)); - for (int i = 0; i < context.length; i++) { - cmd.append(" 0x"); - cmd.append(Long.toHexString(winRegs[i])); - } - printlnToOutput(cmd.toString()); - boolean res = in.parseBoolean(); - if (mustDuplicateHandle) { - printlnToOutput("closehandle 0x" + Integer.toHexString(handle)); - } - if (!res) { - String failMessage = "SetThreadContext failed for thread handle 0x" + - Integer.toHexString(handle); - if (mustDuplicateHandle) { - failMessage = failMessage + ", duplicated from thread handle " + - Integer.toHexString(threadHandleValue); - } - throw new DebuggerException(failMessage); - } - } catch (IOException e) { - throw new DebuggerException(e); - } - } - - /** Fetches the Win32 LDT_ENTRY for the given thread and selector. - This data structure allows the conversion of a segment-relative - address to a linear virtual address. For example, it allows the - expression of operations like "mov eax, fs:[18h]", which fetches - the thread information block, allowing access to the thread - ID. */ - public synchronized Win32LDTEntry getThreadSelectorEntry(int threadHandleValue, - boolean mustDuplicateHandle, - int selector) - throws DebuggerException { - try { - int handle = threadHandleValue; - if (mustDuplicateHandle) { - printlnToOutput("duphandle 0x" + Integer.toHexString(threadHandleValue)); - if (!in.parseBoolean()) { - throw new DebuggerException("Error duplicating thread handle 0x" + threadHandleValue); - } - handle = (int) in.parseAddress(); // Must close to avoid leaks - } - printlnToOutput("selectorentry 0x" + Integer.toHexString(handle) + " " + selector); - if (!in.parseBoolean()) { - if (mustDuplicateHandle) { - printlnToOutput("closehandle 0x" + Integer.toHexString(handle)); - } - throw new DebuggerException("GetThreadContext failed for thread handle 0x" + handle + - ", duplicated from thread handle " + threadHandleValue); - } - // Parse result. See - // src/os/win32/agent/README-commands.txt for the format. - short limitLow = (short) in.parseAddress(); - short baseLow = (short) in.parseAddress(); - byte baseMid = (byte) in.parseAddress(); - byte flags1 = (byte) in.parseAddress(); - byte flags2 = (byte) in.parseAddress(); - byte baseHi = (byte) in.parseAddress(); - return new Win32LDTEntry(limitLow, baseLow, baseMid, flags1, flags2, baseHi); - } catch (IOException e) { - throw new DebuggerException(e); - } - } - - public synchronized List getThreadList() throws DebuggerException { - if (!suspended) { - throw new DebuggerException("Process not suspended"); - } - - try { - printlnToOutput("threadlist"); - List ret = new ArrayList(); - int numThreads = in.parseInt(); - for (int i = 0; i < numThreads; i++) { - int handle = (int) in.parseAddress(); - ret.add(new Win32Thread(this, handle)); - } - return ret; - } catch (IOException e) { - throw new DebuggerException(e); - } - } - - public synchronized List getLoadObjectList() throws DebuggerException { - if (!suspended) { - throw new DebuggerException("Process not suspended"); - } - - try { - if (loadObjects == null) { - loadObjects = new ArrayList(); - nameToDllMap = new HashMap(); - // Get list of library names and base addresses - printlnToOutput("libinfo"); - int numInfo = in.parseInt(); - - for (int i = 0; i < numInfo; i++) { - // NOTE: because Win32 is case insensitive, we standardize on - // lowercase file names. - String fullPathName = parseString().toLowerCase(); - Address base = newAddress(in.parseAddress()); - - File file = new File(fullPathName); - long size = file.length(); - DLL dll = new DLL(this, fullPathName, size, base); - String name = file.getName(); - nameToDllMap.put(name, dll); - loadObjects.add(dll); - } - } - } catch (IOException e) { - throw new DebuggerException(e); - } - - return loadObjects; - } - - //---------------------------------------------------------------------- - // Process control access - // - - public synchronized void writeBytesToProcess(long startAddress, long numBytes, byte[] data) - throws UnmappedAddressException, DebuggerException { - try { - printToOutput("poke 0x" + Long.toHexString(startAddress) + - " |"); - writeIntToOutput((int) numBytes); - writeToOutput(data, 0, (int) numBytes); - printlnToOutput(""); - if (!in.parseBoolean()) { - throw new UnmappedAddressException(startAddress); - } - } catch (IOException e) { - throw new DebuggerException(e); - } - } - - public synchronized void suspend() throws DebuggerException { - try { - if (suspended) { - throw new DebuggerException("Process already suspended"); - } - printlnToOutput("suspend"); - suspended = true; - enableCache(); - reresolveLoadObjects(); - } catch (IOException e) { - throw new DebuggerException(e); - } - } - - public synchronized void resume() throws DebuggerException { - try { - if (!suspended) { - throw new DebuggerException("Process not suspended"); - } - disableCache(); - printlnToOutput("resume"); - suspended = false; - } catch (IOException e) { - throw new DebuggerException(e); - } - } - - public synchronized boolean isSuspended() throws DebuggerException { - return suspended; - } - - public synchronized void setBreakpoint(Address addr) throws DebuggerException { - if (!suspended) { - throw new DebuggerException("Process not suspended"); - } - - long addrVal = getAddressValue(addr); - Long where = new Long(addrVal); - if (breakpoints.get(where) != null) { - throw new DebuggerException("Breakpoint already set at " + addr); - } - Byte what = new Byte(readBytes(addrVal, 1)[0]); - // Now put 0xCC (int 3) at the target address, fail if can not - writeBytesToProcess(addrVal, 1, new byte[] { (byte) 0xCC }); - // OK, the breakpoint is set. - breakpoints.put(where, what); - } - - public synchronized void clearBreakpoint(Address addr) throws DebuggerException { - if (!suspended) { - throw new DebuggerException("Process not suspended"); - } - - long addrVal = getAddressValue(addr); - Long where = new Long(addrVal); - Byte what = (Byte) breakpoints.get(where); - if (what == null) { - throw new DebuggerException("Breakpoint not set at " + addr); - } - // Put original data back at address - writeBytesToProcess(addrVal, 1, new byte[] { what.byteValue() }); - // OK, breakpoint is cleared - breakpoints.remove(where); - } - - public synchronized boolean isBreakpointSet(Address addr) throws DebuggerException { - return (breakpoints.get(new Long(getAddressValue(addr))) != null); - } - - // Following constants taken from winnt.h - private static final int EXCEPTION_DEBUG_EVENT = 1; - private static final int LOAD_DLL_DEBUG_EVENT = 6; - private static final int UNLOAD_DLL_DEBUG_EVENT = 7; - private static final int EXCEPTION_ACCESS_VIOLATION = 0xC0000005; - private static final int EXCEPTION_BREAKPOINT = 0x80000003; - private static final int EXCEPTION_SINGLE_STEP = 0x80000004; - - public synchronized DebugEvent debugEventPoll() throws DebuggerException { - if (curDebugEvent != null) { - return curDebugEvent; - } - - try { - printlnToOutput("pollevent"); - if (!in.parseBoolean()) { - return null; - } - // Otherwise, got a debug event. Need to figure out what kind it is. - int handle = (int) in.parseAddress(); - ThreadProxy thread = new Win32Thread(this, handle); - int code = in.parseInt(); - DebugEvent ev = null; - switch (code) { - case LOAD_DLL_DEBUG_EVENT: { - Address addr = newAddress(in.parseAddress()); - ev = BasicDebugEvent.newLoadObjectLoadEvent(thread, addr); - break; - } - - case UNLOAD_DLL_DEBUG_EVENT: { - Address addr = newAddress(in.parseAddress()); - ev = BasicDebugEvent.newLoadObjectUnloadEvent(thread, addr); - break; - } - - case EXCEPTION_DEBUG_EVENT: { - int exceptionCode = in.parseInt(); - Address pc = newAddress(in.parseAddress()); - switch (exceptionCode) { - case EXCEPTION_ACCESS_VIOLATION: - boolean wasWrite = in.parseBoolean(); - Address addr = newAddress(in.parseAddress()); - ev = BasicDebugEvent.newAccessViolationEvent(thread, pc, wasWrite, addr); - break; - - case EXCEPTION_BREAKPOINT: - ev = BasicDebugEvent.newBreakpointEvent(thread, pc); - break; - - case EXCEPTION_SINGLE_STEP: - ev = BasicDebugEvent.newSingleStepEvent(thread, pc); - break; - - default: - ev = BasicDebugEvent.newUnknownEvent(thread, - "Exception 0x" + Integer.toHexString(exceptionCode) + - " at PC " + pc); - break; - } - break; - } - - default: - ev = BasicDebugEvent.newUnknownEvent(thread, - "Debug event " + code + " occurred"); - break; - } - if (Assert.ASSERTS_ENABLED) { - Assert.that(ev != null, "Must have created event"); - } - curDebugEvent = ev; - } catch (IOException e) { - throw new DebuggerException(e); - } - - return curDebugEvent; - } - - public synchronized void debugEventContinue() throws DebuggerException { - if (curDebugEvent == null) { - throw new DebuggerException("No debug event pending"); - } - - try { - /////////////////////////////////////////////////////////////////// - // // - // FIXME: this **must** be modified to handle breakpoint events - // properly. Must temporarily remove the breakpoint and enable - // single-stepping mode (hiding those single-step events from - // the user unless they have been requested; currently there is - // no way to request single-step events; and it isn't clear how - // to enable them or how the hardware and/or OS typically - // supports them, i.e., are they on a per-process or per-thread - // level?) until the process steps past the breakpoint, then put - // the breakpoint back. - // // - /////////////////////////////////////////////////////////////////// - - DebugEvent.Type t = curDebugEvent.getType(); - boolean shouldPassOn = true; - if (t == DebugEvent.Type.BREAKPOINT) { - // FIXME: correct algorithm appears to be as follows: - // - // 1. Check to see whether we know about this breakpoint. If - // not, it's requested by the user's program and we should - // ignore it (not pass it on to the program). - // - // 2. Replace the original opcode. - // - // 3. Set single-stepping mode in the debug registers. - // - // 4. Back up the PC. - // - // 5. In debugEventPoll(), watch for a single-step event on - // this thread. When we get it, put the breakpoint back. Only - // deliver that single-step event if the user has requested - // single-step events (FIXME: must figure out whether they are - // per-thread or per-process, and also expose a way to turn - // them on.) - - // To make breakpoints work for now, we will just back up the - // PC, which we have to do in order to not disrupt the program - // execution in case the user decides to disable the breakpoint. - - if (breakpoints.get(new Long(getAddressValue(curDebugEvent.getPC()))) != null) { - System.err.println("Backing up PC due to breakpoint"); - X86ThreadContext ctx = (X86ThreadContext) curDebugEvent.getThread().getContext(); - ctx.setRegister(X86ThreadContext.EIP, ctx.getRegister(X86ThreadContext.EIP) - 1); - curDebugEvent.getThread().setContext(ctx); - } else { - System.err.println("Skipping back up of PC since I didn't know about this breakpoint"); - System.err.println("Known breakpoints:"); - for (Iterator iter = breakpoints.keySet().iterator(); iter.hasNext(); ) { - System.err.println(" 0x" + Long.toHexString(((Long) iter.next()).longValue())); - } - } - shouldPassOn = false; - } else if (t == DebugEvent.Type.SINGLE_STEP) { - shouldPassOn = false; - } - // Other kinds of debug events are either ignored if passed on - // or probably should be passed on so the program exits - // FIXME: generate process exiting events (should be easy) - - int val = (shouldPassOn ? 1 : 0); - printlnToOutput("continueevent " + val); - if (!in.parseBoolean()) { - throw new DebuggerException("Unknown error while attempting to continue past debug event"); - } - curDebugEvent = null; - } catch (IOException e) { - throw new DebuggerException(e); - } - } - - //-------------------------------------------------------------------------------- - // Address access - // - - /** From the Debugger interface */ - public long getAddressValue(Address addr) { - if (addr == null) return 0; - return ((Win32Address) addr).getValue(); - } - - /** From the Win32Debugger interface */ - public Address newAddress(long value) { - if (value == 0) return null; - return new Win32Address(this, value); - } - - //-------------------------------------------------------------------------------- - // Internals only below this point - // - - private String parseString() throws IOException { - int charSize = in.parseInt(); - int numChars = in.parseInt(); - in.skipByte(); - String str; - if (charSize == 1) { - str = in.readByteString(numChars); - } else { - str = in.readCharString(numChars); - } - return str; - } - - /** Looks up an address in the remote process's address space. - Returns 0 if symbol not found or upon error. Package private to - allow Win32DebuggerRemoteIntfImpl access. NOTE that this returns - a long instead of an Address because we do not want to serialize - Addresses. */ - synchronized long lookupInProcess(String objectName, String symbol) { - // NOTE: this assumes that process is suspended (which is probably - // necessary assumption given that DLLs can be loaded/unloaded as - // process runs). Should update documentation. - if (nameToDllMap == null) { - getLoadObjectList(); - } - DLL dll = (DLL) nameToDllMap.get(objectName); - // The DLL can be null because we use this to search through known - // DLLs in HotSpotTypeDataBase (for example) - if (dll != null) { - Win32Address addr = (Win32Address) dll.lookupSymbol(symbol); - if (addr != null) { - return addr.getValue(); - } - } - return 0; - } - - /** This reads bytes from the remote process. */ - public synchronized ReadResult readBytesFromProcess(long address, long numBytes) - throws UnmappedAddressException, DebuggerException { - try { - String cmd = "peek " + utils.addressValueToString(address) + " " + numBytes; - printlnToOutput(cmd); - while (in.readByte() != 'B') { - } - byte res = in.readByte(); - if (res == 0) { - System.err.println("Failing command: " + cmd); - throw new DebuggerException("Read of remote process address space failed"); - } - // NOTE: must read ALL of the data regardless of whether we need - // to throw an UnmappedAddressException. Otherwise will corrupt - // the input stream each time we have a failure. Not good. Do - // not want to risk "flushing" the input stream in case a huge - // read has a hangup in the middle and we leave data on the - // stream. - byte[] buf = new byte[(int) numBytes]; - boolean bailOut = false; - long failureAddress = 0; - while (numBytes > 0) { - long len = in.readUnsignedInt(); - boolean isMapped = ((in.readByte() == 0) ? false : true); - if (!isMapped) { - if (!bailOut) { - bailOut = true; - failureAddress = address; - } - } else { - // This won't work if we have unmapped regions, but if we do - // then we're going to throw an exception anyway - - // NOTE: there is a factor of 20 speed difference between - // these two ways of doing this read. - in.readBytes(buf, 0, (int) len); - } - - // Do NOT do this: - // for (int i = 0; i < (int) len; i++) { - // buf[i] = in.readByte(); - // } - - numBytes -= len; - address += len; - } - if (Assert.ASSERTS_ENABLED) { - Assert.that(numBytes == 0, "Bug in debug server's implementation of peek"); - } - if (bailOut) { - return new ReadResult(failureAddress); - } - return new ReadResult(buf); - } - catch (IOException e) { - throw new DebuggerException(e); - } - } - - /** Convenience routines */ - private void printlnToOutput(String s) throws IOException { - out.println(s); - if (out.checkError()) { - throw new IOException("Error occurred while writing to debug server"); - } - } - - private void printToOutput(String s) throws IOException { - out.print(s); - if (out.checkError()) { - throw new IOException("Error occurred while writing to debug server"); - } - } - - private void writeIntToOutput(int val) throws IOException { - rawOut.writeInt(val); - rawOut.flush(); - } - - private void writeToOutput(byte[] buf, int off, int len) throws IOException { - rawOut.write(buf, off, len); - rawOut.flush(); - } - - /** Connects to the debug server, setting up out and in streams. */ - private void connectToDebugServer() throws IOException { - // Try for a short period of time to connect to debug server; time out - // with failure if didn't succeed - debuggerSocket = null; - long endTime = System.currentTimeMillis() + SHORT_TIMEOUT; - - while ((debuggerSocket == null) && (System.currentTimeMillis() < endTime)) { - try { - // FIXME: this does not work if we are on a DHCP machine which - // did not get an IP address this session. It appears to use - // an old cached address and the connection does not actually - // succeed. Must file a bug. - // debuggerSocket = new Socket(InetAddress.getLocalHost(), PORT); - debuggerSocket = new Socket(InetAddress.getByName("127.0.0.1"), PORT); - debuggerSocket.setTcpNoDelay(true); - } - catch (IOException e) { - // Swallow IO exceptions while attempting connection - debuggerSocket = null; - try { - // Don't swamp the CPU - Thread.sleep(750); - } - catch (InterruptedException ex) { - } - } - } - - if (debuggerSocket == null) { - // Failed to connect because of timeout - throw new DebuggerException("Timed out while attempting to connect to debug server (please start SwDbgSrv.exe)"); - } - - out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(debuggerSocket.getOutputStream(), "US-ASCII")), true); - rawOut = new DataOutputStream(new BufferedOutputStream(debuggerSocket.getOutputStream())); - in = new InputLexer(new BufferedInputStream(debuggerSocket.getInputStream())); - } - - private DLL findDLLByName(String fullPathName) { - for (Iterator iter = loadObjects.iterator(); iter.hasNext(); ) { - DLL dll = (DLL) iter.next(); - if (dll.getName().equals(fullPathName)) { - return dll; - } - } - return null; - } - - private void reresolveLoadObjects() throws DebuggerException { - try { - // It is too expensive to throw away the loadobject list every - // time the process is suspended, largely because of debug - // information re-parsing. When we suspend the target process we - // instead fetch the list of loaded libraries in the target and - // see whether any loadobject needs to be thrown away (because it - // was unloaded) or invalidated (because it was unloaded and - // reloaded at a different target address). Note that we don't - // properly handle the case of a loaded DLL being unloaded, - // recompiled, and reloaded. We could handle this by keeping a - // time stamp. - - if (loadObjects == null) { - return; - } - - // Need to create new list since have to figure out which ones - // were unloaded - List newLoadObjects = new ArrayList(); - - // Get list of library names and base addresses - printlnToOutput("libinfo"); - int numInfo = in.parseInt(); - - for (int i = 0; i < numInfo; i++) { - // NOTE: because Win32 is case insensitive, we standardize on - // lowercase file names. - String fullPathName = parseString().toLowerCase(); - Address base = newAddress(in.parseAddress()); - - // Look for full path name in DLL list - DLL dll = findDLLByName(fullPathName); - boolean mustLoad = true; - if (dll != null) { - loadObjects.remove(dll); - - // See whether base addresses match; otherwise, need to reload - if (AddressOps.equal(base, dll.getBase())) { - mustLoad = false; - } - } - - if (mustLoad) { - // Create new DLL - File file = new File(fullPathName); - long size = file.length(); - String name = file.getName(); - dll = new DLL(this, fullPathName, size, base); - nameToDllMap.put(name, dll); - } - newLoadObjects.add(dll); - } - - // All remaining entries in loadObjects have to be removed from - // the nameToDllMap - for (Iterator dllIter = loadObjects.iterator(); dllIter.hasNext(); ) { - DLL dll = (DLL) dllIter.next(); - for (Iterator iter = nameToDllMap.keySet().iterator(); iter.hasNext(); ) { - String name = (String) iter.next(); - if (nameToDllMap.get(name) == dll) { - nameToDllMap.remove(name); - break; - } - } - } - - loadObjects = newLoadObjects; - } catch (IOException e) { - loadObjects = null; - nameToDllMap = null; - throw new DebuggerException(e); - } - } -}