1 /* 2 * Copyright 2000-2004 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 20 * CA 95054 USA or visit www.sun.com if you need additional information or 21 * have any questions. 22 * 23 */ 24 25 package sun.jvm.hotspot.debugger.win32; 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.x86.*; 32 import sun.jvm.hotspot.debugger.win32.coff.*; 33 import sun.jvm.hotspot.debugger.cdbg.*; 34 import sun.jvm.hotspot.debugger.cdbg.basic.BasicDebugEvent; 35 import sun.jvm.hotspot.utilities.*; 36 import sun.jvm.hotspot.utilities.memo.*; 37 38 /** <P> An implementation of the JVMDebugger interface which talks to 39 the Free Windows Debug Server (FwDbgSrv) over a socket to 40 implement attach/detach and read from process memory. All DLL and 41 symbol table management is done in Java. </P> 42 43 <P> <B>NOTE</B> that since we have the notion of fetching "Java 44 primitive types" from the remote process (which might have 45 different sizes than we expect) we have a bootstrapping 46 problem. We need to know the sizes of these types before we can 47 fetch them. The current implementation solves this problem by 48 requiring that it be configured with these type sizes before they 49 can be fetched. The readJ(Type) routines here will throw a 50 RuntimeException if they are called before the debugger is 51 configured with the Java primitive type sizes. </P> */ 52 53 public class Win32DebuggerLocal extends DebuggerBase implements Win32Debugger { 54 private Socket debuggerSocket; 55 private boolean attached; 56 // FIXME: update when core files supported 57 private long pid; 58 // Communication with debug server 59 private PrintWriter out; 60 private DataOutputStream rawOut; 61 private InputLexer in; 62 private static final int PORT = 27000; 63 private PageCache cache; 64 private static final long SHORT_TIMEOUT = 2000; 65 private static final long LONG_TIMEOUT = 20000; 66 67 // Symbol lookup support 68 // This is a map of library names to DLLs 69 private Map nameToDllMap; 70 71 // C/C++ debugging support 72 private List/*<LoadObject>*/ loadObjects; 73 private CDebugger cdbg; 74 75 // ProcessControl support 76 private boolean suspended; 77 // Maps Long objects (addresses) to Byte objects (original instructions) 78 // (Longs used instead of Addresses to properly represent breakpoints at 0x0 if needed) 79 private Map breakpoints; 80 // Current debug event, if any 81 private DebugEvent curDebugEvent; 82 83 //-------------------------------------------------------------------------------- 84 // Implementation of Debugger interface 85 // 86 87 /** <P> machDesc may not be null. </P> 88 89 <P> useCache should be set to true if debugging is being done 90 locally, and to false if the debugger is being created for the 91 purpose of supporting remote debugging. </P> */ 92 public Win32DebuggerLocal(MachineDescription machDesc, 93 boolean useCache) throws DebuggerException { 94 this.machDesc = machDesc; 95 utils = new DebuggerUtilities(machDesc.getAddressSize(), machDesc.isBigEndian()); 96 if (useCache) { 97 // Cache portion of the remote process's address space. 98 // Fetching data over the socket connection to dbx is slow. 99 // Might be faster if we were using a binary protocol to talk to 100 // dbx, but would have to test. For now, this cache works best 101 // if it covers the entire heap of the remote process. FIXME: at 102 // least should make this tunable from the outside, i.e., via 103 // the UI. This is a cache of 4096 4K pages, or 16 MB. The page 104 // size must be adjusted to be the hardware's page size. 105 // (FIXME: should pick this up from the debugger.) 106 initCache(4096, parseCacheNumPagesProperty(4096)); 107 } 108 // FIXME: add instantiation of thread factory 109 110 try { 111 connectToDebugServer(); 112 } catch (IOException e) { 113 throw new DebuggerException(e); 114 } 115 } 116 117 /** From the Debugger interface via JVMDebugger */ 118 public boolean hasProcessList() throws DebuggerException { 119 return true; 120 } 121 122 /** From the Debugger interface via JVMDebugger */ 123 public List getProcessList() throws DebuggerException { 124 List processes = new ArrayList(); 125 126 try { 127 printlnToOutput("proclist"); 128 int num = in.parseInt(); 129 for (int i = 0; i < num; i++) { 130 int pid = in.parseInt(); 131 String name = parseString(); 132 // NOTE: Win32 hack 133 if (name.equals("")) { 134 name = "System Idle Process"; 135 } 136 processes.add(new ProcessInfo(name, pid)); 137 } 138 return processes; 139 } 140 catch (IOException e) { 141 throw new DebuggerException(e); 142 } 143 } 144 145 /** From the Debugger interface via JVMDebugger */ 146 public synchronized void attach(int processID) throws DebuggerException { 147 if (attached) { 148 // FIXME: update when core files supported 149 throw new DebuggerException("Already attached to process " + pid); 150 } 151 152 try { 153 printlnToOutput("attach " + processID); 154 if (!in.parseBoolean()) { 155 throw new DebuggerException("Error attaching to process, or no such process"); 156 } 157 158 attached = true; 159 pid = processID; 160 suspended = true; 161 breakpoints = new HashMap(); 162 curDebugEvent = null; 163 nameToDllMap = null; 164 loadObjects = null; 165 } 166 catch (IOException e) { 167 throw new DebuggerException(e); 168 } 169 } 170 171 /** From the Debugger interface via JVMDebugger */ 172 public synchronized void attach(String executableName, String coreFileName) throws DebuggerException { 173 throw new DebuggerException("Core files not yet supported on Win32"); 174 } 175 176 /** From the Debugger interface via JVMDebugger */ 177 public synchronized boolean detach() { 178 if (!attached) { 179 return false; 180 } 181 182 attached = false; 183 suspended = false; 184 breakpoints = null; 185 186 // Close all open DLLs 187 if (nameToDllMap != null) { 188 for (Iterator iter = nameToDllMap.values().iterator(); iter.hasNext(); ) { 189 DLL dll = (DLL) iter.next(); 190 dll.close(); 191 } 192 nameToDllMap = null; 193 loadObjects = null; 194 } 195 196 cdbg = null; 197 clearCache(); 198 199 try { 200 printlnToOutput("detach"); 201 return in.parseBoolean(); 202 } 203 catch (IOException e) { 204 throw new DebuggerException(e); 205 } 206 } 207 208 /** From the Debugger interface via JVMDebugger */ 209 public Address parseAddress(String addressString) throws NumberFormatException { 210 return newAddress(utils.scanAddress(addressString)); 211 } 212 213 /** From the Debugger interface via JVMDebugger */ 214 public String getOS() { 215 return PlatformInfo.getOS(); 216 } 217 218 /** From the Debugger interface via JVMDebugger */ 219 public String getCPU() { 220 return PlatformInfo.getCPU(); 221 } 222 223 public boolean hasConsole() throws DebuggerException { 224 return false; 225 } 226 227 public String consoleExecuteCommand(String cmd) throws DebuggerException { 228 throw new DebuggerException("No debugger console available on Win32"); 229 } 230 231 public String getConsolePrompt() throws DebuggerException { 232 return null; 233 } 234 235 public CDebugger getCDebugger() throws DebuggerException { 236 if (cdbg == null) { 237 cdbg = new Win32CDebugger(this); 238 } 239 return cdbg; 240 } 241 242 /** From the SymbolLookup interface via Debugger and JVMDebugger */ 243 public synchronized Address lookup(String objectName, String symbol) { 244 if (!attached) { 245 return null; 246 } 247 return newAddress(lookupInProcess(objectName, symbol)); 248 } 249 250 /** From the SymbolLookup interface via Debugger and JVMDebugger */ 251 public synchronized OopHandle lookupOop(String objectName, String symbol) { 252 Address addr = lookup(objectName, symbol); 253 if (addr == null) { 254 return null; 255 } 256 return addr.addOffsetToAsOopHandle(0); 257 } 258 259 /** From the Debugger interface */ 260 public MachineDescription getMachineDescription() { 261 return machDesc; 262 } 263 264 //-------------------------------------------------------------------------------- 265 // Implementation of ThreadAccess interface 266 // 267 268 /** From the ThreadAccess interface via Debugger and JVMDebugger */ 269 public ThreadProxy getThreadForIdentifierAddress(Address addr) { 270 return new Win32Thread(this, addr); 271 } 272 273 public ThreadProxy getThreadForThreadId(long handle) { 274 return new Win32Thread(this, handle); 275 } 276 277 //---------------------------------------------------------------------- 278 // Overridden from DebuggerBase because we need to relax alignment 279 // constraints on x86 280 281 public long readJLong(long address) 282 throws UnmappedAddressException, UnalignedAddressException { 283 checkJavaConfigured(); 284 // FIXME: allow this to be configurable. Undesirable to add a 285 // dependency on the runtime package here, though, since this 286 // package should be strictly underneath it. 287 // utils.checkAlignment(address, jlongSize); 288 utils.checkAlignment(address, jintSize); 289 byte[] data = readBytes(address, jlongSize); 290 return utils.dataToJLong(data, jlongSize); 291 } 292 293 //-------------------------------------------------------------------------------- 294 // Internal routines (for implementation of Win32Address). 295 // These must not be called until the MachineDescription has been set up. 296 // 297 298 /** From the Win32Debugger interface */ 299 public String addressValueToString(long address) { 300 return utils.addressValueToString(address); 301 } 302 303 /** From the Win32Debugger interface */ 304 public Win32Address readAddress(long address) 305 throws UnmappedAddressException, UnalignedAddressException { 306 return (Win32Address) newAddress(readAddressValue(address)); 307 } 308 309 /** From the Win32Debugger interface */ 310 public Win32OopHandle readOopHandle(long address) 311 throws UnmappedAddressException, UnalignedAddressException, NotInHeapException { 312 long value = readAddressValue(address); 313 return (value == 0 ? null : new Win32OopHandle(this, value)); 314 } 315 316 /** From the Win32Debugger interface */ 317 public void writeAddress(long address, Win32Address value) { 318 writeAddressValue(address, getAddressValue(value)); 319 } 320 321 /** From the Win32Debugger interface */ 322 public void writeOopHandle(long address, Win32OopHandle value) { 323 writeAddressValue(address, getAddressValue(value)); 324 } 325 326 //-------------------------------------------------------------------------------- 327 // Thread context access 328 // 329 330 public synchronized long[] getThreadIntegerRegisterSet(int threadHandleValue, 331 boolean mustDuplicateHandle) 332 throws DebuggerException { 333 if (!suspended) { 334 throw new DebuggerException("Process not suspended"); 335 } 336 337 try { 338 int handle = threadHandleValue; 339 if (mustDuplicateHandle) { 340 printlnToOutput("duphandle 0x" + Integer.toHexString(threadHandleValue)); 341 if (!in.parseBoolean()) { 342 throw new DebuggerException("Error duplicating thread handle 0x" + threadHandleValue); 343 } 344 handle = (int) in.parseAddress(); // Must close to avoid leaks 345 } 346 printlnToOutput("getcontext 0x" + Integer.toHexString(handle)); 347 if (!in.parseBoolean()) { 348 if (mustDuplicateHandle) { 349 printlnToOutput("closehandle 0x" + Integer.toHexString(handle)); 350 } 351 String failMessage = "GetThreadContext failed for thread handle 0x" + 352 Integer.toHexString(handle); 353 if (mustDuplicateHandle) { 354 failMessage = failMessage + ", duplicated from thread handle " + 355 Integer.toHexString(threadHandleValue); 356 } 357 throw new DebuggerException(failMessage); 358 } 359 // Otherwise, parse all registers. See 360 // src/os/win32/agent/README-commands.txt for the format. 361 // Note the array we have to return has to match that specified by 362 // X86ThreadContext.java. 363 int numRegs = 22; 364 long[] winRegs = new long[numRegs]; 365 for (int i = 0; i < numRegs; i++) { 366 winRegs[i] = in.parseAddress(); 367 } 368 if (mustDuplicateHandle) { 369 // Clean up after ourselves 370 printlnToOutput("closehandle 0x" + Integer.toHexString(handle)); 371 } 372 // Now create the real return value 373 long[] retval = new long[X86ThreadContext.NPRGREG]; 374 retval[X86ThreadContext.EAX] = winRegs[0]; 375 retval[X86ThreadContext.EBX] = winRegs[1]; 376 retval[X86ThreadContext.ECX] = winRegs[2]; 377 retval[X86ThreadContext.EDX] = winRegs[3]; 378 retval[X86ThreadContext.ESI] = winRegs[4]; 379 retval[X86ThreadContext.EDI] = winRegs[5]; 380 retval[X86ThreadContext.EBP] = winRegs[6]; 381 retval[X86ThreadContext.ESP] = winRegs[7]; 382 retval[X86ThreadContext.EIP] = winRegs[8]; 383 retval[X86ThreadContext.DS] = winRegs[9]; 384 retval[X86ThreadContext.ES] = winRegs[10]; 385 retval[X86ThreadContext.FS] = winRegs[11]; 386 retval[X86ThreadContext.GS] = winRegs[12]; 387 retval[X86ThreadContext.CS] = winRegs[13]; 388 retval[X86ThreadContext.SS] = winRegs[14]; 389 retval[X86ThreadContext.EFL] = winRegs[15]; 390 retval[X86ThreadContext.DR0] = winRegs[16]; 391 retval[X86ThreadContext.DR1] = winRegs[17]; 392 retval[X86ThreadContext.DR2] = winRegs[18]; 393 retval[X86ThreadContext.DR3] = winRegs[19]; 394 retval[X86ThreadContext.DR6] = winRegs[20]; 395 retval[X86ThreadContext.DR7] = winRegs[21]; 396 return retval; 397 } catch (IOException e) { 398 throw new DebuggerException(e); 399 } 400 } 401 402 public synchronized void setThreadIntegerRegisterSet(int threadHandleValue, 403 boolean mustDuplicateHandle, 404 long[] context) 405 throws DebuggerException { 406 if (!suspended) { 407 throw new DebuggerException("Process not suspended"); 408 } 409 410 try { 411 int handle = threadHandleValue; 412 if (mustDuplicateHandle) { 413 printlnToOutput("duphandle 0x" + Integer.toHexString(threadHandleValue)); 414 if (!in.parseBoolean()) { 415 throw new DebuggerException("Error duplicating thread handle 0x" + threadHandleValue); 416 } 417 handle = (int) in.parseAddress(); // Must close to avoid leaks 418 } 419 // Change order of registers to match that of debug server 420 long[] winRegs = new long[context.length]; 421 winRegs[0] = context[X86ThreadContext.EAX]; 422 winRegs[1] = context[X86ThreadContext.EBX]; 423 winRegs[2] = context[X86ThreadContext.ECX]; 424 winRegs[3] = context[X86ThreadContext.EDX]; 425 winRegs[4] = context[X86ThreadContext.ESI]; 426 winRegs[5] = context[X86ThreadContext.EDI]; 427 winRegs[6] = context[X86ThreadContext.EBP]; 428 winRegs[7] = context[X86ThreadContext.ESP]; 429 winRegs[8] = context[X86ThreadContext.EIP]; 430 winRegs[9] = context[X86ThreadContext.DS]; 431 winRegs[10] = context[X86ThreadContext.ES]; 432 winRegs[11] = context[X86ThreadContext.FS]; 433 winRegs[12] = context[X86ThreadContext.GS]; 434 winRegs[13] = context[X86ThreadContext.CS]; 435 winRegs[14] = context[X86ThreadContext.SS]; 436 winRegs[15] = context[X86ThreadContext.EFL]; 437 winRegs[16] = context[X86ThreadContext.DR0]; 438 winRegs[17] = context[X86ThreadContext.DR1]; 439 winRegs[18] = context[X86ThreadContext.DR2]; 440 winRegs[19] = context[X86ThreadContext.DR3]; 441 winRegs[20] = context[X86ThreadContext.DR6]; 442 winRegs[21] = context[X86ThreadContext.DR7]; 443 StringBuffer cmd = new StringBuffer(); 444 cmd.append("setcontext 0x"); 445 cmd.append(Integer.toHexString(threadHandleValue)); 446 for (int i = 0; i < context.length; i++) { 447 cmd.append(" 0x"); 448 cmd.append(Long.toHexString(winRegs[i])); 449 } 450 printlnToOutput(cmd.toString()); 451 boolean res = in.parseBoolean(); 452 if (mustDuplicateHandle) { 453 printlnToOutput("closehandle 0x" + Integer.toHexString(handle)); 454 } 455 if (!res) { 456 String failMessage = "SetThreadContext failed for thread handle 0x" + 457 Integer.toHexString(handle); 458 if (mustDuplicateHandle) { 459 failMessage = failMessage + ", duplicated from thread handle " + 460 Integer.toHexString(threadHandleValue); 461 } 462 throw new DebuggerException(failMessage); 463 } 464 } catch (IOException e) { 465 throw new DebuggerException(e); 466 } 467 } 468 469 /** Fetches the Win32 LDT_ENTRY for the given thread and selector. 470 This data structure allows the conversion of a segment-relative 471 address to a linear virtual address. For example, it allows the 472 expression of operations like "mov eax, fs:[18h]", which fetches 473 the thread information block, allowing access to the thread 474 ID. */ 475 public synchronized Win32LDTEntry getThreadSelectorEntry(int threadHandleValue, 476 boolean mustDuplicateHandle, 477 int selector) 478 throws DebuggerException { 479 try { 480 int handle = threadHandleValue; 481 if (mustDuplicateHandle) { 482 printlnToOutput("duphandle 0x" + Integer.toHexString(threadHandleValue)); 483 if (!in.parseBoolean()) { 484 throw new DebuggerException("Error duplicating thread handle 0x" + threadHandleValue); 485 } 486 handle = (int) in.parseAddress(); // Must close to avoid leaks 487 } 488 printlnToOutput("selectorentry 0x" + Integer.toHexString(handle) + " " + selector); 489 if (!in.parseBoolean()) { 490 if (mustDuplicateHandle) { 491 printlnToOutput("closehandle 0x" + Integer.toHexString(handle)); 492 } 493 throw new DebuggerException("GetThreadContext failed for thread handle 0x" + handle + 494 ", duplicated from thread handle " + threadHandleValue); 495 } 496 // Parse result. See 497 // src/os/win32/agent/README-commands.txt for the format. 498 short limitLow = (short) in.parseAddress(); 499 short baseLow = (short) in.parseAddress(); 500 byte baseMid = (byte) in.parseAddress(); 501 byte flags1 = (byte) in.parseAddress(); 502 byte flags2 = (byte) in.parseAddress(); 503 byte baseHi = (byte) in.parseAddress(); 504 return new Win32LDTEntry(limitLow, baseLow, baseMid, flags1, flags2, baseHi); 505 } catch (IOException e) { 506 throw new DebuggerException(e); 507 } 508 } 509 510 public synchronized List getThreadList() throws DebuggerException { 511 if (!suspended) { 512 throw new DebuggerException("Process not suspended"); 513 } 514 515 try { 516 printlnToOutput("threadlist"); 517 List ret = new ArrayList(); 518 int numThreads = in.parseInt(); 519 for (int i = 0; i < numThreads; i++) { 520 int handle = (int) in.parseAddress(); 521 ret.add(new Win32Thread(this, handle)); 522 } 523 return ret; 524 } catch (IOException e) { 525 throw new DebuggerException(e); 526 } 527 } 528 529 public synchronized List getLoadObjectList() throws DebuggerException { 530 if (!suspended) { 531 throw new DebuggerException("Process not suspended"); 532 } 533 534 try { 535 if (loadObjects == null) { 536 loadObjects = new ArrayList(); 537 nameToDllMap = new HashMap(); 538 // Get list of library names and base addresses 539 printlnToOutput("libinfo"); 540 int numInfo = in.parseInt(); 541 542 for (int i = 0; i < numInfo; i++) { 543 // NOTE: because Win32 is case insensitive, we standardize on 544 // lowercase file names. 545 String fullPathName = parseString().toLowerCase(); 546 Address base = newAddress(in.parseAddress()); 547 548 File file = new File(fullPathName); 549 long size = file.length(); 550 DLL dll = new DLL(this, fullPathName, size, base); 551 String name = file.getName(); 552 nameToDllMap.put(name, dll); 553 loadObjects.add(dll); 554 } 555 } 556 } catch (IOException e) { 557 throw new DebuggerException(e); 558 } 559 560 return loadObjects; 561 } 562 563 //---------------------------------------------------------------------- 564 // Process control access 565 // 566 567 public synchronized void writeBytesToProcess(long startAddress, long numBytes, byte[] data) 568 throws UnmappedAddressException, DebuggerException { 569 try { 570 printToOutput("poke 0x" + Long.toHexString(startAddress) + 571 " |"); 572 writeIntToOutput((int) numBytes); 573 writeToOutput(data, 0, (int) numBytes); 574 printlnToOutput(""); 575 if (!in.parseBoolean()) { 576 throw new UnmappedAddressException(startAddress); 577 } 578 } catch (IOException e) { 579 throw new DebuggerException(e); 580 } 581 } 582 583 public synchronized void suspend() throws DebuggerException { 584 try { 585 if (suspended) { 586 throw new DebuggerException("Process already suspended"); 587 } 588 printlnToOutput("suspend"); 589 suspended = true; 590 enableCache(); 591 reresolveLoadObjects(); 592 } catch (IOException e) { 593 throw new DebuggerException(e); 594 } 595 } 596 597 public synchronized void resume() throws DebuggerException { 598 try { 599 if (!suspended) { 600 throw new DebuggerException("Process not suspended"); 601 } 602 disableCache(); 603 printlnToOutput("resume"); 604 suspended = false; 605 } catch (IOException e) { 606 throw new DebuggerException(e); 607 } 608 } 609 610 public synchronized boolean isSuspended() throws DebuggerException { 611 return suspended; 612 } 613 614 public synchronized void setBreakpoint(Address addr) throws DebuggerException { 615 if (!suspended) { 616 throw new DebuggerException("Process not suspended"); 617 } 618 619 long addrVal = getAddressValue(addr); 620 Long where = new Long(addrVal); 621 if (breakpoints.get(where) != null) { 622 throw new DebuggerException("Breakpoint already set at " + addr); 623 } 624 Byte what = new Byte(readBytes(addrVal, 1)[0]); 625 // Now put 0xCC (int 3) at the target address, fail if can not 626 writeBytesToProcess(addrVal, 1, new byte[] { (byte) 0xCC }); 627 // OK, the breakpoint is set. 628 breakpoints.put(where, what); 629 } 630 631 public synchronized void clearBreakpoint(Address addr) throws DebuggerException { 632 if (!suspended) { 633 throw new DebuggerException("Process not suspended"); 634 } 635 636 long addrVal = getAddressValue(addr); 637 Long where = new Long(addrVal); 638 Byte what = (Byte) breakpoints.get(where); 639 if (what == null) { 640 throw new DebuggerException("Breakpoint not set at " + addr); 641 } 642 // Put original data back at address 643 writeBytesToProcess(addrVal, 1, new byte[] { what.byteValue() }); 644 // OK, breakpoint is cleared 645 breakpoints.remove(where); 646 } 647 648 public synchronized boolean isBreakpointSet(Address addr) throws DebuggerException { 649 return (breakpoints.get(new Long(getAddressValue(addr))) != null); 650 } 651 652 // Following constants taken from winnt.h 653 private static final int EXCEPTION_DEBUG_EVENT = 1; 654 private static final int LOAD_DLL_DEBUG_EVENT = 6; 655 private static final int UNLOAD_DLL_DEBUG_EVENT = 7; 656 private static final int EXCEPTION_ACCESS_VIOLATION = 0xC0000005; 657 private static final int EXCEPTION_BREAKPOINT = 0x80000003; 658 private static final int EXCEPTION_SINGLE_STEP = 0x80000004; 659 660 public synchronized DebugEvent debugEventPoll() throws DebuggerException { 661 if (curDebugEvent != null) { 662 return curDebugEvent; 663 } 664 665 try { 666 printlnToOutput("pollevent"); 667 if (!in.parseBoolean()) { 668 return null; 669 } 670 // Otherwise, got a debug event. Need to figure out what kind it is. 671 int handle = (int) in.parseAddress(); 672 ThreadProxy thread = new Win32Thread(this, handle); 673 int code = in.parseInt(); 674 DebugEvent ev = null; 675 switch (code) { 676 case LOAD_DLL_DEBUG_EVENT: { 677 Address addr = newAddress(in.parseAddress()); 678 ev = BasicDebugEvent.newLoadObjectLoadEvent(thread, addr); 679 break; 680 } 681 682 case UNLOAD_DLL_DEBUG_EVENT: { 683 Address addr = newAddress(in.parseAddress()); 684 ev = BasicDebugEvent.newLoadObjectUnloadEvent(thread, addr); 685 break; 686 } 687 688 case EXCEPTION_DEBUG_EVENT: { 689 int exceptionCode = in.parseInt(); 690 Address pc = newAddress(in.parseAddress()); 691 switch (exceptionCode) { 692 case EXCEPTION_ACCESS_VIOLATION: 693 boolean wasWrite = in.parseBoolean(); 694 Address addr = newAddress(in.parseAddress()); 695 ev = BasicDebugEvent.newAccessViolationEvent(thread, pc, wasWrite, addr); 696 break; 697 698 case EXCEPTION_BREAKPOINT: 699 ev = BasicDebugEvent.newBreakpointEvent(thread, pc); 700 break; 701 702 case EXCEPTION_SINGLE_STEP: 703 ev = BasicDebugEvent.newSingleStepEvent(thread, pc); 704 break; 705 706 default: 707 ev = BasicDebugEvent.newUnknownEvent(thread, 708 "Exception 0x" + Integer.toHexString(exceptionCode) + 709 " at PC " + pc); 710 break; 711 } 712 break; 713 } 714 715 default: 716 ev = BasicDebugEvent.newUnknownEvent(thread, 717 "Debug event " + code + " occurred"); 718 break; 719 } 720 if (Assert.ASSERTS_ENABLED) { 721 Assert.that(ev != null, "Must have created event"); 722 } 723 curDebugEvent = ev; 724 } catch (IOException e) { 725 throw new DebuggerException(e); 726 } 727 728 return curDebugEvent; 729 } 730 731 public synchronized void debugEventContinue() throws DebuggerException { 732 if (curDebugEvent == null) { 733 throw new DebuggerException("No debug event pending"); 734 } 735 736 try { 737 /////////////////////////////////////////////////////////////////// 738 // // 739 // FIXME: this **must** be modified to handle breakpoint events 740 // properly. Must temporarily remove the breakpoint and enable 741 // single-stepping mode (hiding those single-step events from 742 // the user unless they have been requested; currently there is 743 // no way to request single-step events; and it isn't clear how 744 // to enable them or how the hardware and/or OS typically 745 // supports them, i.e., are they on a per-process or per-thread 746 // level?) until the process steps past the breakpoint, then put 747 // the breakpoint back. 748 // // 749 /////////////////////////////////////////////////////////////////// 750 751 DebugEvent.Type t = curDebugEvent.getType(); 752 boolean shouldPassOn = true; 753 if (t == DebugEvent.Type.BREAKPOINT) { 754 // FIXME: correct algorithm appears to be as follows: 755 // 756 // 1. Check to see whether we know about this breakpoint. If 757 // not, it's requested by the user's program and we should 758 // ignore it (not pass it on to the program). 759 // 760 // 2. Replace the original opcode. 761 // 762 // 3. Set single-stepping mode in the debug registers. 763 // 764 // 4. Back up the PC. 765 // 766 // 5. In debugEventPoll(), watch for a single-step event on 767 // this thread. When we get it, put the breakpoint back. Only 768 // deliver that single-step event if the user has requested 769 // single-step events (FIXME: must figure out whether they are 770 // per-thread or per-process, and also expose a way to turn 771 // them on.) 772 773 // To make breakpoints work for now, we will just back up the 774 // PC, which we have to do in order to not disrupt the program 775 // execution in case the user decides to disable the breakpoint. 776 777 if (breakpoints.get(new Long(getAddressValue(curDebugEvent.getPC()))) != null) { 778 System.err.println("Backing up PC due to breakpoint"); 779 X86ThreadContext ctx = (X86ThreadContext) curDebugEvent.getThread().getContext(); 780 ctx.setRegister(X86ThreadContext.EIP, ctx.getRegister(X86ThreadContext.EIP) - 1); 781 curDebugEvent.getThread().setContext(ctx); 782 } else { 783 System.err.println("Skipping back up of PC since I didn't know about this breakpoint"); 784 System.err.println("Known breakpoints:"); 785 for (Iterator iter = breakpoints.keySet().iterator(); iter.hasNext(); ) { 786 System.err.println(" 0x" + Long.toHexString(((Long) iter.next()).longValue())); 787 } 788 } 789 shouldPassOn = false; 790 } else if (t == DebugEvent.Type.SINGLE_STEP) { 791 shouldPassOn = false; 792 } 793 // Other kinds of debug events are either ignored if passed on 794 // or probably should be passed on so the program exits 795 // FIXME: generate process exiting events (should be easy) 796 797 int val = (shouldPassOn ? 1 : 0); 798 printlnToOutput("continueevent " + val); 799 if (!in.parseBoolean()) { 800 throw new DebuggerException("Unknown error while attempting to continue past debug event"); 801 } 802 curDebugEvent = null; 803 } catch (IOException e) { 804 throw new DebuggerException(e); 805 } 806 } 807 808 //-------------------------------------------------------------------------------- 809 // Address access 810 // 811 812 /** From the Debugger interface */ 813 public long getAddressValue(Address addr) { 814 if (addr == null) return 0; 815 return ((Win32Address) addr).getValue(); 816 } 817 818 /** From the Win32Debugger interface */ 819 public Address newAddress(long value) { 820 if (value == 0) return null; 821 return new Win32Address(this, value); 822 } 823 824 //-------------------------------------------------------------------------------- 825 // Internals only below this point 826 // 827 828 private String parseString() throws IOException { 829 int charSize = in.parseInt(); 830 int numChars = in.parseInt(); 831 in.skipByte(); 832 String str; 833 if (charSize == 1) { 834 str = in.readByteString(numChars); 835 } else { 836 str = in.readCharString(numChars); 837 } 838 return str; 839 } 840 841 /** Looks up an address in the remote process's address space. 842 Returns 0 if symbol not found or upon error. Package private to 843 allow Win32DebuggerRemoteIntfImpl access. NOTE that this returns 844 a long instead of an Address because we do not want to serialize 845 Addresses. */ 846 synchronized long lookupInProcess(String objectName, String symbol) { 847 // NOTE: this assumes that process is suspended (which is probably 848 // necessary assumption given that DLLs can be loaded/unloaded as 849 // process runs). Should update documentation. 850 if (nameToDllMap == null) { 851 getLoadObjectList(); 852 } 853 DLL dll = (DLL) nameToDllMap.get(objectName); 854 // The DLL can be null because we use this to search through known 855 // DLLs in HotSpotTypeDataBase (for example) 856 if (dll != null) { 857 Win32Address addr = (Win32Address) dll.lookupSymbol(symbol); 858 if (addr != null) { 859 return addr.getValue(); 860 } 861 } 862 return 0; 863 } 864 865 /** This reads bytes from the remote process. */ 866 public synchronized ReadResult readBytesFromProcess(long address, long numBytes) 867 throws UnmappedAddressException, DebuggerException { 868 try { 869 String cmd = "peek " + utils.addressValueToString(address) + " " + numBytes; 870 printlnToOutput(cmd); 871 while (in.readByte() != 'B') { 872 } 873 byte res = in.readByte(); 874 if (res == 0) { 875 System.err.println("Failing command: " + cmd); 876 throw new DebuggerException("Read of remote process address space failed"); 877 } 878 // NOTE: must read ALL of the data regardless of whether we need 879 // to throw an UnmappedAddressException. Otherwise will corrupt 880 // the input stream each time we have a failure. Not good. Do 881 // not want to risk "flushing" the input stream in case a huge 882 // read has a hangup in the middle and we leave data on the 883 // stream. 884 byte[] buf = new byte[(int) numBytes]; 885 boolean bailOut = false; 886 long failureAddress = 0; 887 while (numBytes > 0) { 888 long len = in.readUnsignedInt(); 889 boolean isMapped = ((in.readByte() == 0) ? false : true); 890 if (!isMapped) { 891 if (!bailOut) { 892 bailOut = true; 893 failureAddress = address; 894 } 895 } else { 896 // This won't work if we have unmapped regions, but if we do 897 // then we're going to throw an exception anyway 898 899 // NOTE: there is a factor of 20 speed difference between 900 // these two ways of doing this read. 901 in.readBytes(buf, 0, (int) len); 902 } 903 904 // Do NOT do this: 905 // for (int i = 0; i < (int) len; i++) { 906 // buf[i] = in.readByte(); 907 // } 908 909 numBytes -= len; 910 address += len; 911 } 912 if (Assert.ASSERTS_ENABLED) { 913 Assert.that(numBytes == 0, "Bug in debug server's implementation of peek"); 914 } 915 if (bailOut) { 916 return new ReadResult(failureAddress); 917 } 918 return new ReadResult(buf); 919 } 920 catch (IOException e) { 921 throw new DebuggerException(e); 922 } 923 } 924 925 /** Convenience routines */ 926 private void printlnToOutput(String s) throws IOException { 927 out.println(s); 928 if (out.checkError()) { 929 throw new IOException("Error occurred while writing to debug server"); 930 } 931 } 932 933 private void printToOutput(String s) throws IOException { 934 out.print(s); 935 if (out.checkError()) { 936 throw new IOException("Error occurred while writing to debug server"); 937 } 938 } 939 940 private void writeIntToOutput(int val) throws IOException { 941 rawOut.writeInt(val); 942 rawOut.flush(); 943 } 944 945 private void writeToOutput(byte[] buf, int off, int len) throws IOException { 946 rawOut.write(buf, off, len); 947 rawOut.flush(); 948 } 949 950 /** Connects to the debug server, setting up out and in streams. */ 951 private void connectToDebugServer() throws IOException { 952 // Try for a short period of time to connect to debug server; time out 953 // with failure if didn't succeed 954 debuggerSocket = null; 955 long endTime = System.currentTimeMillis() + SHORT_TIMEOUT; 956 957 while ((debuggerSocket == null) && (System.currentTimeMillis() < endTime)) { 958 try { 959 // FIXME: this does not work if we are on a DHCP machine which 960 // did not get an IP address this session. It appears to use 961 // an old cached address and the connection does not actually 962 // succeed. Must file a bug. 963 // debuggerSocket = new Socket(InetAddress.getLocalHost(), PORT); 964 debuggerSocket = new Socket(InetAddress.getByName("127.0.0.1"), PORT); 965 debuggerSocket.setTcpNoDelay(true); 966 } 967 catch (IOException e) { 968 // Swallow IO exceptions while attempting connection 969 debuggerSocket = null; 970 try { 971 // Don't swamp the CPU 972 Thread.sleep(750); 973 } 974 catch (InterruptedException ex) { 975 } 976 } 977 } 978 979 if (debuggerSocket == null) { 980 // Failed to connect because of timeout 981 throw new DebuggerException("Timed out while attempting to connect to debug server (please start SwDbgSrv.exe)"); 982 } 983 984 out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(debuggerSocket.getOutputStream(), "US-ASCII")), true); 985 rawOut = new DataOutputStream(new BufferedOutputStream(debuggerSocket.getOutputStream())); 986 in = new InputLexer(new BufferedInputStream(debuggerSocket.getInputStream())); 987 } 988 989 private DLL findDLLByName(String fullPathName) { 990 for (Iterator iter = loadObjects.iterator(); iter.hasNext(); ) { 991 DLL dll = (DLL) iter.next(); 992 if (dll.getName().equals(fullPathName)) { 993 return dll; 994 } 995 } 996 return null; 997 } 998 999 private void reresolveLoadObjects() throws DebuggerException { 1000 try { 1001 // It is too expensive to throw away the loadobject list every 1002 // time the process is suspended, largely because of debug 1003 // information re-parsing. When we suspend the target process we 1004 // instead fetch the list of loaded libraries in the target and 1005 // see whether any loadobject needs to be thrown away (because it 1006 // was unloaded) or invalidated (because it was unloaded and 1007 // reloaded at a different target address). Note that we don't 1008 // properly handle the case of a loaded DLL being unloaded, 1009 // recompiled, and reloaded. We could handle this by keeping a 1010 // time stamp. 1011 1012 if (loadObjects == null) { 1013 return; 1014 } 1015 1016 // Need to create new list since have to figure out which ones 1017 // were unloaded 1018 List newLoadObjects = new ArrayList(); 1019 1020 // Get list of library names and base addresses 1021 printlnToOutput("libinfo"); 1022 int numInfo = in.parseInt(); 1023 1024 for (int i = 0; i < numInfo; i++) { 1025 // NOTE: because Win32 is case insensitive, we standardize on 1026 // lowercase file names. 1027 String fullPathName = parseString().toLowerCase(); 1028 Address base = newAddress(in.parseAddress()); 1029 1030 // Look for full path name in DLL list 1031 DLL dll = findDLLByName(fullPathName); 1032 boolean mustLoad = true; 1033 if (dll != null) { 1034 loadObjects.remove(dll); 1035 1036 // See whether base addresses match; otherwise, need to reload 1037 if (AddressOps.equal(base, dll.getBase())) { 1038 mustLoad = false; 1039 } 1040 } 1041 1042 if (mustLoad) { 1043 // Create new DLL 1044 File file = new File(fullPathName); 1045 long size = file.length(); 1046 String name = file.getName(); 1047 dll = new DLL(this, fullPathName, size, base); 1048 nameToDllMap.put(name, dll); 1049 } 1050 newLoadObjects.add(dll); 1051 } 1052 1053 // All remaining entries in loadObjects have to be removed from 1054 // the nameToDllMap 1055 for (Iterator dllIter = loadObjects.iterator(); dllIter.hasNext(); ) { 1056 DLL dll = (DLL) dllIter.next(); 1057 for (Iterator iter = nameToDllMap.keySet().iterator(); iter.hasNext(); ) { 1058 String name = (String) iter.next(); 1059 if (nameToDllMap.get(name) == dll) { 1060 nameToDllMap.remove(name); 1061 break; 1062 } 1063 } 1064 } 1065 1066 loadObjects = newLoadObjects; 1067 } catch (IOException e) { 1068 loadObjects = null; 1069 nameToDllMap = null; 1070 throw new DebuggerException(e); 1071 } 1072 } 1073 }