1 /* 2 * Copyright 2000-2008 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 public Win32Address readCompOopAddress(long address) 310 throws UnmappedAddressException, UnalignedAddressException { 311 return (Win32Address) newAddress(readCompOopAddressValue(address)); 312 } 313 314 /** From the Win32Debugger interface */ 315 public Win32OopHandle readOopHandle(long address) 316 throws UnmappedAddressException, UnalignedAddressException, NotInHeapException { 317 long value = readAddressValue(address); 318 return (value == 0 ? null : new Win32OopHandle(this, value)); 319 } 320 public Win32OopHandle readCompOopHandle(long address) 321 throws UnmappedAddressException, UnalignedAddressException, NotInHeapException { 322 long value = readCompOopAddressValue(address); 323 return (value == 0 ? null : new Win32OopHandle(this, value)); 324 } 325 326 /** From the Win32Debugger interface */ 327 public void writeAddress(long address, Win32Address value) { 328 writeAddressValue(address, getAddressValue(value)); 329 } 330 331 /** From the Win32Debugger interface */ 332 public void writeOopHandle(long address, Win32OopHandle value) { 333 writeAddressValue(address, getAddressValue(value)); 334 } 335 336 //-------------------------------------------------------------------------------- 337 // Thread context access 338 // 339 340 public synchronized long[] getThreadIntegerRegisterSet(int threadHandleValue, 341 boolean mustDuplicateHandle) 342 throws DebuggerException { 343 if (!suspended) { 344 throw new DebuggerException("Process not suspended"); 345 } 346 347 try { 348 int handle = threadHandleValue; 349 if (mustDuplicateHandle) { 350 printlnToOutput("duphandle 0x" + Integer.toHexString(threadHandleValue)); 351 if (!in.parseBoolean()) { 352 throw new DebuggerException("Error duplicating thread handle 0x" + threadHandleValue); 353 } 354 handle = (int) in.parseAddress(); // Must close to avoid leaks 355 } 356 printlnToOutput("getcontext 0x" + Integer.toHexString(handle)); 357 if (!in.parseBoolean()) { 358 if (mustDuplicateHandle) { 359 printlnToOutput("closehandle 0x" + Integer.toHexString(handle)); 360 } 361 String failMessage = "GetThreadContext failed for thread handle 0x" + 362 Integer.toHexString(handle); 363 if (mustDuplicateHandle) { 364 failMessage = failMessage + ", duplicated from thread handle " + 365 Integer.toHexString(threadHandleValue); 366 } 367 throw new DebuggerException(failMessage); 368 } 369 // Otherwise, parse all registers. See 370 // src/os/win32/agent/README-commands.txt for the format. 371 // Note the array we have to return has to match that specified by 372 // X86ThreadContext.java. 373 int numRegs = 22; 374 long[] winRegs = new long[numRegs]; 375 for (int i = 0; i < numRegs; i++) { 376 winRegs[i] = in.parseAddress(); 377 } 378 if (mustDuplicateHandle) { 379 // Clean up after ourselves 380 printlnToOutput("closehandle 0x" + Integer.toHexString(handle)); 381 } 382 // Now create the real return value 383 long[] retval = new long[X86ThreadContext.NPRGREG]; 384 retval[X86ThreadContext.EAX] = winRegs[0]; 385 retval[X86ThreadContext.EBX] = winRegs[1]; 386 retval[X86ThreadContext.ECX] = winRegs[2]; 387 retval[X86ThreadContext.EDX] = winRegs[3]; 388 retval[X86ThreadContext.ESI] = winRegs[4]; 389 retval[X86ThreadContext.EDI] = winRegs[5]; 390 retval[X86ThreadContext.EBP] = winRegs[6]; 391 retval[X86ThreadContext.ESP] = winRegs[7]; 392 retval[X86ThreadContext.EIP] = winRegs[8]; 393 retval[X86ThreadContext.DS] = winRegs[9]; 394 retval[X86ThreadContext.ES] = winRegs[10]; 395 retval[X86ThreadContext.FS] = winRegs[11]; 396 retval[X86ThreadContext.GS] = winRegs[12]; 397 retval[X86ThreadContext.CS] = winRegs[13]; 398 retval[X86ThreadContext.SS] = winRegs[14]; 399 retval[X86ThreadContext.EFL] = winRegs[15]; 400 retval[X86ThreadContext.DR0] = winRegs[16]; 401 retval[X86ThreadContext.DR1] = winRegs[17]; 402 retval[X86ThreadContext.DR2] = winRegs[18]; 403 retval[X86ThreadContext.DR3] = winRegs[19]; 404 retval[X86ThreadContext.DR6] = winRegs[20]; 405 retval[X86ThreadContext.DR7] = winRegs[21]; 406 return retval; 407 } catch (IOException e) { 408 throw new DebuggerException(e); 409 } 410 } 411 412 public synchronized void setThreadIntegerRegisterSet(int threadHandleValue, 413 boolean mustDuplicateHandle, 414 long[] context) 415 throws DebuggerException { 416 if (!suspended) { 417 throw new DebuggerException("Process not suspended"); 418 } 419 420 try { 421 int handle = threadHandleValue; 422 if (mustDuplicateHandle) { 423 printlnToOutput("duphandle 0x" + Integer.toHexString(threadHandleValue)); 424 if (!in.parseBoolean()) { 425 throw new DebuggerException("Error duplicating thread handle 0x" + threadHandleValue); 426 } 427 handle = (int) in.parseAddress(); // Must close to avoid leaks 428 } 429 // Change order of registers to match that of debug server 430 long[] winRegs = new long[context.length]; 431 winRegs[0] = context[X86ThreadContext.EAX]; 432 winRegs[1] = context[X86ThreadContext.EBX]; 433 winRegs[2] = context[X86ThreadContext.ECX]; 434 winRegs[3] = context[X86ThreadContext.EDX]; 435 winRegs[4] = context[X86ThreadContext.ESI]; 436 winRegs[5] = context[X86ThreadContext.EDI]; 437 winRegs[6] = context[X86ThreadContext.EBP]; 438 winRegs[7] = context[X86ThreadContext.ESP]; 439 winRegs[8] = context[X86ThreadContext.EIP]; 440 winRegs[9] = context[X86ThreadContext.DS]; 441 winRegs[10] = context[X86ThreadContext.ES]; 442 winRegs[11] = context[X86ThreadContext.FS]; 443 winRegs[12] = context[X86ThreadContext.GS]; 444 winRegs[13] = context[X86ThreadContext.CS]; 445 winRegs[14] = context[X86ThreadContext.SS]; 446 winRegs[15] = context[X86ThreadContext.EFL]; 447 winRegs[16] = context[X86ThreadContext.DR0]; 448 winRegs[17] = context[X86ThreadContext.DR1]; 449 winRegs[18] = context[X86ThreadContext.DR2]; 450 winRegs[19] = context[X86ThreadContext.DR3]; 451 winRegs[20] = context[X86ThreadContext.DR6]; 452 winRegs[21] = context[X86ThreadContext.DR7]; 453 StringBuffer cmd = new StringBuffer(); 454 cmd.append("setcontext 0x"); 455 cmd.append(Integer.toHexString(threadHandleValue)); 456 for (int i = 0; i < context.length; i++) { 457 cmd.append(" 0x"); 458 cmd.append(Long.toHexString(winRegs[i])); 459 } 460 printlnToOutput(cmd.toString()); 461 boolean res = in.parseBoolean(); 462 if (mustDuplicateHandle) { 463 printlnToOutput("closehandle 0x" + Integer.toHexString(handle)); 464 } 465 if (!res) { 466 String failMessage = "SetThreadContext failed for thread handle 0x" + 467 Integer.toHexString(handle); 468 if (mustDuplicateHandle) { 469 failMessage = failMessage + ", duplicated from thread handle " + 470 Integer.toHexString(threadHandleValue); 471 } 472 throw new DebuggerException(failMessage); 473 } 474 } catch (IOException e) { 475 throw new DebuggerException(e); 476 } 477 } 478 479 /** Fetches the Win32 LDT_ENTRY for the given thread and selector. 480 This data structure allows the conversion of a segment-relative 481 address to a linear virtual address. For example, it allows the 482 expression of operations like "mov eax, fs:[18h]", which fetches 483 the thread information block, allowing access to the thread 484 ID. */ 485 public synchronized Win32LDTEntry getThreadSelectorEntry(int threadHandleValue, 486 boolean mustDuplicateHandle, 487 int selector) 488 throws DebuggerException { 489 try { 490 int handle = threadHandleValue; 491 if (mustDuplicateHandle) { 492 printlnToOutput("duphandle 0x" + Integer.toHexString(threadHandleValue)); 493 if (!in.parseBoolean()) { 494 throw new DebuggerException("Error duplicating thread handle 0x" + threadHandleValue); 495 } 496 handle = (int) in.parseAddress(); // Must close to avoid leaks 497 } 498 printlnToOutput("selectorentry 0x" + Integer.toHexString(handle) + " " + selector); 499 if (!in.parseBoolean()) { 500 if (mustDuplicateHandle) { 501 printlnToOutput("closehandle 0x" + Integer.toHexString(handle)); 502 } 503 throw new DebuggerException("GetThreadContext failed for thread handle 0x" + handle + 504 ", duplicated from thread handle " + threadHandleValue); 505 } 506 // Parse result. See 507 // src/os/win32/agent/README-commands.txt for the format. 508 short limitLow = (short) in.parseAddress(); 509 short baseLow = (short) in.parseAddress(); 510 byte baseMid = (byte) in.parseAddress(); 511 byte flags1 = (byte) in.parseAddress(); 512 byte flags2 = (byte) in.parseAddress(); 513 byte baseHi = (byte) in.parseAddress(); 514 return new Win32LDTEntry(limitLow, baseLow, baseMid, flags1, flags2, baseHi); 515 } catch (IOException e) { 516 throw new DebuggerException(e); 517 } 518 } 519 520 public synchronized List getThreadList() throws DebuggerException { 521 if (!suspended) { 522 throw new DebuggerException("Process not suspended"); 523 } 524 525 try { 526 printlnToOutput("threadlist"); 527 List ret = new ArrayList(); 528 int numThreads = in.parseInt(); 529 for (int i = 0; i < numThreads; i++) { 530 int handle = (int) in.parseAddress(); 531 ret.add(new Win32Thread(this, handle)); 532 } 533 return ret; 534 } catch (IOException e) { 535 throw new DebuggerException(e); 536 } 537 } 538 539 public synchronized List getLoadObjectList() throws DebuggerException { 540 if (!suspended) { 541 throw new DebuggerException("Process not suspended"); 542 } 543 544 try { 545 if (loadObjects == null) { 546 loadObjects = new ArrayList(); 547 nameToDllMap = new HashMap(); 548 // Get list of library names and base addresses 549 printlnToOutput("libinfo"); 550 int numInfo = in.parseInt(); 551 552 for (int i = 0; i < numInfo; i++) { 553 // NOTE: because Win32 is case insensitive, we standardize on 554 // lowercase file names. 555 String fullPathName = parseString().toLowerCase(); 556 Address base = newAddress(in.parseAddress()); 557 558 File file = new File(fullPathName); 559 long size = file.length(); 560 DLL dll = new DLL(this, fullPathName, size, base); 561 String name = file.getName(); 562 nameToDllMap.put(name, dll); 563 loadObjects.add(dll); 564 } 565 } 566 } catch (IOException e) { 567 throw new DebuggerException(e); 568 } 569 570 return loadObjects; 571 } 572 573 //---------------------------------------------------------------------- 574 // Process control access 575 // 576 577 public synchronized void writeBytesToProcess(long startAddress, long numBytes, byte[] data) 578 throws UnmappedAddressException, DebuggerException { 579 try { 580 printToOutput("poke 0x" + Long.toHexString(startAddress) + 581 " |"); 582 writeIntToOutput((int) numBytes); 583 writeToOutput(data, 0, (int) numBytes); 584 printlnToOutput(""); 585 if (!in.parseBoolean()) { 586 throw new UnmappedAddressException(startAddress); 587 } 588 } catch (IOException e) { 589 throw new DebuggerException(e); 590 } 591 } 592 593 public synchronized void suspend() throws DebuggerException { 594 try { 595 if (suspended) { 596 throw new DebuggerException("Process already suspended"); 597 } 598 printlnToOutput("suspend"); 599 suspended = true; 600 enableCache(); 601 reresolveLoadObjects(); 602 } catch (IOException e) { 603 throw new DebuggerException(e); 604 } 605 } 606 607 public synchronized void resume() throws DebuggerException { 608 try { 609 if (!suspended) { 610 throw new DebuggerException("Process not suspended"); 611 } 612 disableCache(); 613 printlnToOutput("resume"); 614 suspended = false; 615 } catch (IOException e) { 616 throw new DebuggerException(e); 617 } 618 } 619 620 public synchronized boolean isSuspended() throws DebuggerException { 621 return suspended; 622 } 623 624 public synchronized void setBreakpoint(Address addr) throws DebuggerException { 625 if (!suspended) { 626 throw new DebuggerException("Process not suspended"); 627 } 628 629 long addrVal = getAddressValue(addr); 630 Long where = new Long(addrVal); 631 if (breakpoints.get(where) != null) { 632 throw new DebuggerException("Breakpoint already set at " + addr); 633 } 634 Byte what = new Byte(readBytes(addrVal, 1)[0]); 635 // Now put 0xCC (int 3) at the target address, fail if can not 636 writeBytesToProcess(addrVal, 1, new byte[] { (byte) 0xCC }); 637 // OK, the breakpoint is set. 638 breakpoints.put(where, what); 639 } 640 641 public synchronized void clearBreakpoint(Address addr) throws DebuggerException { 642 if (!suspended) { 643 throw new DebuggerException("Process not suspended"); 644 } 645 646 long addrVal = getAddressValue(addr); 647 Long where = new Long(addrVal); 648 Byte what = (Byte) breakpoints.get(where); 649 if (what == null) { 650 throw new DebuggerException("Breakpoint not set at " + addr); 651 } 652 // Put original data back at address 653 writeBytesToProcess(addrVal, 1, new byte[] { what.byteValue() }); 654 // OK, breakpoint is cleared 655 breakpoints.remove(where); 656 } 657 658 public synchronized boolean isBreakpointSet(Address addr) throws DebuggerException { 659 return (breakpoints.get(new Long(getAddressValue(addr))) != null); 660 } 661 662 // Following constants taken from winnt.h 663 private static final int EXCEPTION_DEBUG_EVENT = 1; 664 private static final int LOAD_DLL_DEBUG_EVENT = 6; 665 private static final int UNLOAD_DLL_DEBUG_EVENT = 7; 666 private static final int EXCEPTION_ACCESS_VIOLATION = 0xC0000005; 667 private static final int EXCEPTION_BREAKPOINT = 0x80000003; 668 private static final int EXCEPTION_SINGLE_STEP = 0x80000004; 669 670 public synchronized DebugEvent debugEventPoll() throws DebuggerException { 671 if (curDebugEvent != null) { 672 return curDebugEvent; 673 } 674 675 try { 676 printlnToOutput("pollevent"); 677 if (!in.parseBoolean()) { 678 return null; 679 } 680 // Otherwise, got a debug event. Need to figure out what kind it is. 681 int handle = (int) in.parseAddress(); 682 ThreadProxy thread = new Win32Thread(this, handle); 683 int code = in.parseInt(); 684 DebugEvent ev = null; 685 switch (code) { 686 case LOAD_DLL_DEBUG_EVENT: { 687 Address addr = newAddress(in.parseAddress()); 688 ev = BasicDebugEvent.newLoadObjectLoadEvent(thread, addr); 689 break; 690 } 691 692 case UNLOAD_DLL_DEBUG_EVENT: { 693 Address addr = newAddress(in.parseAddress()); 694 ev = BasicDebugEvent.newLoadObjectUnloadEvent(thread, addr); 695 break; 696 } 697 698 case EXCEPTION_DEBUG_EVENT: { 699 int exceptionCode = in.parseInt(); 700 Address pc = newAddress(in.parseAddress()); 701 switch (exceptionCode) { 702 case EXCEPTION_ACCESS_VIOLATION: 703 boolean wasWrite = in.parseBoolean(); 704 Address addr = newAddress(in.parseAddress()); 705 ev = BasicDebugEvent.newAccessViolationEvent(thread, pc, wasWrite, addr); 706 break; 707 708 case EXCEPTION_BREAKPOINT: 709 ev = BasicDebugEvent.newBreakpointEvent(thread, pc); 710 break; 711 712 case EXCEPTION_SINGLE_STEP: 713 ev = BasicDebugEvent.newSingleStepEvent(thread, pc); 714 break; 715 716 default: 717 ev = BasicDebugEvent.newUnknownEvent(thread, 718 "Exception 0x" + Integer.toHexString(exceptionCode) + 719 " at PC " + pc); 720 break; 721 } 722 break; 723 } 724 725 default: 726 ev = BasicDebugEvent.newUnknownEvent(thread, 727 "Debug event " + code + " occurred"); 728 break; 729 } 730 if (Assert.ASSERTS_ENABLED) { 731 Assert.that(ev != null, "Must have created event"); 732 } 733 curDebugEvent = ev; 734 } catch (IOException e) { 735 throw new DebuggerException(e); 736 } 737 738 return curDebugEvent; 739 } 740 741 public synchronized void debugEventContinue() throws DebuggerException { 742 if (curDebugEvent == null) { 743 throw new DebuggerException("No debug event pending"); 744 } 745 746 try { 747 /////////////////////////////////////////////////////////////////// 748 // // 749 // FIXME: this **must** be modified to handle breakpoint events 750 // properly. Must temporarily remove the breakpoint and enable 751 // single-stepping mode (hiding those single-step events from 752 // the user unless they have been requested; currently there is 753 // no way to request single-step events; and it isn't clear how 754 // to enable them or how the hardware and/or OS typically 755 // supports them, i.e., are they on a per-process or per-thread 756 // level?) until the process steps past the breakpoint, then put 757 // the breakpoint back. 758 // // 759 /////////////////////////////////////////////////////////////////// 760 761 DebugEvent.Type t = curDebugEvent.getType(); 762 boolean shouldPassOn = true; 763 if (t == DebugEvent.Type.BREAKPOINT) { 764 // FIXME: correct algorithm appears to be as follows: 765 // 766 // 1. Check to see whether we know about this breakpoint. If 767 // not, it's requested by the user's program and we should 768 // ignore it (not pass it on to the program). 769 // 770 // 2. Replace the original opcode. 771 // 772 // 3. Set single-stepping mode in the debug registers. 773 // 774 // 4. Back up the PC. 775 // 776 // 5. In debugEventPoll(), watch for a single-step event on 777 // this thread. When we get it, put the breakpoint back. Only 778 // deliver that single-step event if the user has requested 779 // single-step events (FIXME: must figure out whether they are 780 // per-thread or per-process, and also expose a way to turn 781 // them on.) 782 783 // To make breakpoints work for now, we will just back up the 784 // PC, which we have to do in order to not disrupt the program 785 // execution in case the user decides to disable the breakpoint. 786 787 if (breakpoints.get(new Long(getAddressValue(curDebugEvent.getPC()))) != null) { 788 System.err.println("Backing up PC due to breakpoint"); 789 X86ThreadContext ctx = (X86ThreadContext) curDebugEvent.getThread().getContext(); 790 ctx.setRegister(X86ThreadContext.EIP, ctx.getRegister(X86ThreadContext.EIP) - 1); 791 curDebugEvent.getThread().setContext(ctx); 792 } else { 793 System.err.println("Skipping back up of PC since I didn't know about this breakpoint"); 794 System.err.println("Known breakpoints:"); 795 for (Iterator iter = breakpoints.keySet().iterator(); iter.hasNext(); ) { 796 System.err.println(" 0x" + Long.toHexString(((Long) iter.next()).longValue())); 797 } 798 } 799 shouldPassOn = false; 800 } else if (t == DebugEvent.Type.SINGLE_STEP) { 801 shouldPassOn = false; 802 } 803 // Other kinds of debug events are either ignored if passed on 804 // or probably should be passed on so the program exits 805 // FIXME: generate process exiting events (should be easy) 806 807 int val = (shouldPassOn ? 1 : 0); 808 printlnToOutput("continueevent " + val); 809 if (!in.parseBoolean()) { 810 throw new DebuggerException("Unknown error while attempting to continue past debug event"); 811 } 812 curDebugEvent = null; 813 } catch (IOException e) { 814 throw new DebuggerException(e); 815 } 816 } 817 818 //-------------------------------------------------------------------------------- 819 // Address access 820 // 821 822 /** From the Debugger interface */ 823 public long getAddressValue(Address addr) { 824 if (addr == null) return 0; 825 return ((Win32Address) addr).getValue(); 826 } 827 828 /** From the Win32Debugger interface */ 829 public Address newAddress(long value) { 830 if (value == 0) return null; 831 return new Win32Address(this, value); 832 } 833 834 //-------------------------------------------------------------------------------- 835 // Internals only below this point 836 // 837 838 private String parseString() throws IOException { 839 int charSize = in.parseInt(); 840 int numChars = in.parseInt(); 841 in.skipByte(); 842 String str; 843 if (charSize == 1) { 844 str = in.readByteString(numChars); 845 } else { 846 str = in.readCharString(numChars); 847 } 848 return str; 849 } 850 851 /** Looks up an address in the remote process's address space. 852 Returns 0 if symbol not found or upon error. Package private to 853 allow Win32DebuggerRemoteIntfImpl access. NOTE that this returns 854 a long instead of an Address because we do not want to serialize 855 Addresses. */ 856 synchronized long lookupInProcess(String objectName, String symbol) { 857 // NOTE: this assumes that process is suspended (which is probably 858 // necessary assumption given that DLLs can be loaded/unloaded as 859 // process runs). Should update documentation. 860 if (nameToDllMap == null) { 861 getLoadObjectList(); 862 } 863 DLL dll = (DLL) nameToDllMap.get(objectName); 864 // The DLL can be null because we use this to search through known 865 // DLLs in HotSpotTypeDataBase (for example) 866 if (dll != null) { 867 Win32Address addr = (Win32Address) dll.lookupSymbol(symbol); 868 if (addr != null) { 869 return addr.getValue(); 870 } 871 } 872 return 0; 873 } 874 875 /** This reads bytes from the remote process. */ 876 public synchronized ReadResult readBytesFromProcess(long address, long numBytes) 877 throws UnmappedAddressException, DebuggerException { 878 try { 879 String cmd = "peek " + utils.addressValueToString(address) + " " + numBytes; 880 printlnToOutput(cmd); 881 while (in.readByte() != 'B') { 882 } 883 byte res = in.readByte(); 884 if (res == 0) { 885 System.err.println("Failing command: " + cmd); 886 throw new DebuggerException("Read of remote process address space failed"); 887 } 888 // NOTE: must read ALL of the data regardless of whether we need 889 // to throw an UnmappedAddressException. Otherwise will corrupt 890 // the input stream each time we have a failure. Not good. Do 891 // not want to risk "flushing" the input stream in case a huge 892 // read has a hangup in the middle and we leave data on the 893 // stream. 894 byte[] buf = new byte[(int) numBytes]; 895 boolean bailOut = false; 896 long failureAddress = 0; 897 while (numBytes > 0) { 898 long len = in.readUnsignedInt(); 899 boolean isMapped = ((in.readByte() == 0) ? false : true); 900 if (!isMapped) { 901 if (!bailOut) { 902 bailOut = true; 903 failureAddress = address; 904 } 905 } else { 906 // This won't work if we have unmapped regions, but if we do 907 // then we're going to throw an exception anyway 908 909 // NOTE: there is a factor of 20 speed difference between 910 // these two ways of doing this read. 911 in.readBytes(buf, 0, (int) len); 912 } 913 914 // Do NOT do this: 915 // for (int i = 0; i < (int) len; i++) { 916 // buf[i] = in.readByte(); 917 // } 918 919 numBytes -= len; 920 address += len; 921 } 922 if (Assert.ASSERTS_ENABLED) { 923 Assert.that(numBytes == 0, "Bug in debug server's implementation of peek"); 924 } 925 if (bailOut) { 926 return new ReadResult(failureAddress); 927 } 928 return new ReadResult(buf); 929 } 930 catch (IOException e) { 931 throw new DebuggerException(e); 932 } 933 } 934 935 /** Convenience routines */ 936 private void printlnToOutput(String s) throws IOException { 937 out.println(s); 938 if (out.checkError()) { 939 throw new IOException("Error occurred while writing to debug server"); 940 } 941 } 942 943 private void printToOutput(String s) throws IOException { 944 out.print(s); 945 if (out.checkError()) { 946 throw new IOException("Error occurred while writing to debug server"); 947 } 948 } 949 950 private void writeIntToOutput(int val) throws IOException { 951 rawOut.writeInt(val); 952 rawOut.flush(); 953 } 954 955 private void writeToOutput(byte[] buf, int off, int len) throws IOException { 956 rawOut.write(buf, off, len); 957 rawOut.flush(); 958 } 959 960 /** Connects to the debug server, setting up out and in streams. */ 961 private void connectToDebugServer() throws IOException { 962 // Try for a short period of time to connect to debug server; time out 963 // with failure if didn't succeed 964 debuggerSocket = null; 965 long endTime = System.currentTimeMillis() + SHORT_TIMEOUT; 966 967 while ((debuggerSocket == null) && (System.currentTimeMillis() < endTime)) { 968 try { 969 // FIXME: this does not work if we are on a DHCP machine which 970 // did not get an IP address this session. It appears to use 971 // an old cached address and the connection does not actually 972 // succeed. Must file a bug. 973 // debuggerSocket = new Socket(InetAddress.getLocalHost(), PORT); 974 debuggerSocket = new Socket(InetAddress.getByName("127.0.0.1"), PORT); 975 debuggerSocket.setTcpNoDelay(true); 976 } 977 catch (IOException e) { 978 // Swallow IO exceptions while attempting connection 979 debuggerSocket = null; 980 try { 981 // Don't swamp the CPU 982 Thread.sleep(750); 983 } 984 catch (InterruptedException ex) { 985 } 986 } 987 } 988 989 if (debuggerSocket == null) { 990 // Failed to connect because of timeout 991 throw new DebuggerException("Timed out while attempting to connect to debug server (please start SwDbgSrv.exe)"); 992 } 993 994 out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(debuggerSocket.getOutputStream(), "US-ASCII")), true); 995 rawOut = new DataOutputStream(new BufferedOutputStream(debuggerSocket.getOutputStream())); 996 in = new InputLexer(new BufferedInputStream(debuggerSocket.getInputStream())); 997 } 998 999 private DLL findDLLByName(String fullPathName) { 1000 for (Iterator iter = loadObjects.iterator(); iter.hasNext(); ) { 1001 DLL dll = (DLL) iter.next(); 1002 if (dll.getName().equals(fullPathName)) { 1003 return dll; 1004 } 1005 } 1006 return null; 1007 } 1008 1009 private void reresolveLoadObjects() throws DebuggerException { 1010 try { 1011 // It is too expensive to throw away the loadobject list every 1012 // time the process is suspended, largely because of debug 1013 // information re-parsing. When we suspend the target process we 1014 // instead fetch the list of loaded libraries in the target and 1015 // see whether any loadobject needs to be thrown away (because it 1016 // was unloaded) or invalidated (because it was unloaded and 1017 // reloaded at a different target address). Note that we don't 1018 // properly handle the case of a loaded DLL being unloaded, 1019 // recompiled, and reloaded. We could handle this by keeping a 1020 // time stamp. 1021 1022 if (loadObjects == null) { 1023 return; 1024 } 1025 1026 // Need to create new list since have to figure out which ones 1027 // were unloaded 1028 List newLoadObjects = new ArrayList(); 1029 1030 // Get list of library names and base addresses 1031 printlnToOutput("libinfo"); 1032 int numInfo = in.parseInt(); 1033 1034 for (int i = 0; i < numInfo; i++) { 1035 // NOTE: because Win32 is case insensitive, we standardize on 1036 // lowercase file names. 1037 String fullPathName = parseString().toLowerCase(); 1038 Address base = newAddress(in.parseAddress()); 1039 1040 // Look for full path name in DLL list 1041 DLL dll = findDLLByName(fullPathName); 1042 boolean mustLoad = true; 1043 if (dll != null) { 1044 loadObjects.remove(dll); 1045 1046 // See whether base addresses match; otherwise, need to reload 1047 if (AddressOps.equal(base, dll.getBase())) { 1048 mustLoad = false; 1049 } 1050 } 1051 1052 if (mustLoad) { 1053 // Create new DLL 1054 File file = new File(fullPathName); 1055 long size = file.length(); 1056 String name = file.getName(); 1057 dll = new DLL(this, fullPathName, size, base); 1058 nameToDllMap.put(name, dll); 1059 } 1060 newLoadObjects.add(dll); 1061 } 1062 1063 // All remaining entries in loadObjects have to be removed from 1064 // the nameToDllMap 1065 for (Iterator dllIter = loadObjects.iterator(); dllIter.hasNext(); ) { 1066 DLL dll = (DLL) dllIter.next(); 1067 for (Iterator iter = nameToDllMap.keySet().iterator(); iter.hasNext(); ) { 1068 String name = (String) iter.next(); 1069 if (nameToDllMap.get(name) == dll) { 1070 nameToDllMap.remove(name); 1071 break; 1072 } 1073 } 1074 } 1075 1076 loadObjects = newLoadObjects; 1077 } catch (IOException e) { 1078 loadObjects = null; 1079 nameToDllMap = null; 1080 throw new DebuggerException(e); 1081 } 1082 } 1083 }