1 /* 2 * Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 package sun.jvm.hotspot.debugger.windbg; 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.amd64.*; 32 import sun.jvm.hotspot.debugger.x86.*; 33 import sun.jvm.hotspot.debugger.windbg.amd64.*; 34 import sun.jvm.hotspot.debugger.windbg.x86.*; 35 import sun.jvm.hotspot.debugger.win32.coff.*; 36 import sun.jvm.hotspot.debugger.cdbg.*; 37 import sun.jvm.hotspot.debugger.cdbg.basic.BasicDebugEvent; 38 import sun.jvm.hotspot.utilities.*; 39 import sun.jvm.hotspot.utilities.memo.*; 40 import sun.jvm.hotspot.runtime.*; 41 42 /** <P> An implementation of the JVMDebugger interface which talks to 43 windbg and symbol table management is done in Java. </P> 44 45 <P> <B>NOTE</B> that since we have the notion of fetching "Java 46 primitive types" from the remote process (which might have 47 different sizes than we expect) we have a bootstrapping 48 problem. We need to know the sizes of these types before we can 49 fetch them. The current implementation solves this problem by 50 requiring that it be configured with these type sizes before they 51 can be fetched. The readJ(Type) routines here will throw a 52 RuntimeException if they are called before the debugger is 53 configured with the Java primitive type sizes. </P> */ 54 55 public class WindbgDebuggerLocal extends DebuggerBase implements WindbgDebugger { 56 private PageCache cache; 57 private boolean attached; 58 private boolean isCore; 59 60 // Symbol lookup support 61 // This is a map of library names to DLLs 62 private Map<String, DLL> nameToDllMap; 63 64 // C/C++ debugging support 65 private List<LoadObject> loadObjects; 66 private CDebugger cdbg; 67 68 // thread access 69 private Map<Long, long[]> threadIntegerRegisterSet; 70 private List<ThreadProxy> threadList; 71 72 // windbg native interface pointers 73 74 private long ptrIDebugClient; 75 private long ptrIDebugControl; 76 private long ptrIDebugDataSpaces; 77 private long ptrIDebugOutputCallbacks; 78 private long ptrIDebugAdvanced; 79 private long ptrIDebugSymbols; 80 private long ptrIDebugSystemObjects; 81 82 private WindbgThreadFactory threadFactory; 83 84 //-------------------------------------------------------------------------------- 85 // Implementation of Debugger interface 86 // 87 88 /** <P> machDesc may not be null. </P> 89 90 <P> useCache should be set to true if debugging is being done 91 locally, and to false if the debugger is being created for the 92 purpose of supporting remote debugging. </P> */ 93 public WindbgDebuggerLocal(MachineDescription machDesc, 94 boolean useCache) throws DebuggerException { 95 this.machDesc = machDesc; 96 utils = new DebuggerUtilities(machDesc.getAddressSize(), machDesc.isBigEndian()) { 97 public void checkAlignment(long address, long alignment) { 98 // Need to override default checkAlignment because we need to 99 // relax alignment constraints on Windows/x86 100 if ( (address % alignment != 0) 101 &&(alignment != 8 || address % 4 != 0)) { 102 throw new UnalignedAddressException( 103 "Trying to read at address: " 104 + addressValueToString(address) 105 + " with alignment: " + alignment, 106 address); 107 } 108 } 109 }; 110 111 String cpu = PlatformInfo.getCPU(); 112 if (cpu.equals("x86")) { 113 threadFactory = new WindbgX86ThreadFactory(this); 114 } else if (cpu.equals("amd64")) { 115 threadFactory = new WindbgAMD64ThreadFactory(this); 116 } 117 118 if (useCache) { 119 // Cache portion of the remote process's address space. 120 // Fetching data over the socket connection to dbx is slow. 121 // Might be faster if we were using a binary protocol to talk to 122 // dbx, but would have to test. For now, this cache works best 123 // if it covers the entire heap of the remote process. FIXME: at 124 // least should make this tunable from the outside, i.e., via 125 // the UI. This is a cache of 4096 4K pages, or 16 MB. The page 126 // size must be adjusted to be the hardware's page size. 127 // (FIXME: should pick this up from the debugger.) 128 initCache(4096, 4096); 129 } 130 // FIXME: add instantiation of thread factory 131 132 } 133 134 /** From the Debugger interface via JVMDebugger */ 135 public boolean hasProcessList() throws DebuggerException { 136 return false; 137 } 138 139 /** From the Debugger interface via JVMDebugger */ 140 public List<ProcessInfo> getProcessList() throws DebuggerException { 141 return null; 142 } 143 144 145 /** From the Debugger interface via JVMDebugger */ 146 public synchronized void attach(int processID) throws DebuggerException { 147 attachInit(); 148 attach0(processID); 149 attached = true; 150 isCore = false; 151 } 152 153 /** From the Debugger interface via JVMDebugger */ 154 public synchronized void attach(String executableName, String coreFileName) throws DebuggerException { 155 attachInit(); 156 attach0(executableName, coreFileName); 157 attached = true; 158 isCore = true; 159 } 160 161 public List<LoadObject> getLoadObjectList() { 162 requireAttach(); 163 return loadObjects; 164 } 165 166 /** From the Debugger interface via JVMDebugger */ 167 public synchronized boolean detach() { 168 if ( ! attached) 169 return false; 170 171 // Close all open DLLs 172 if (nameToDllMap != null) { 173 for (Iterator iter = nameToDllMap.values().iterator(); iter.hasNext(); ) { 174 DLL dll = (DLL) iter.next(); 175 dll.close(); 176 } 177 nameToDllMap = null; 178 loadObjects = null; 179 } 180 181 cdbg = null; 182 clearCache(); 183 184 threadIntegerRegisterSet = null; 185 threadList = null; 186 try { 187 detach0(); 188 } finally { 189 attached = false; 190 resetNativePointers(); 191 } 192 return true; 193 } 194 195 196 /** From the Debugger interface via JVMDebugger */ 197 public Address parseAddress(String addressString) throws NumberFormatException { 198 return newAddress(utils.scanAddress(addressString)); 199 } 200 201 /** From the Debugger interface via JVMDebugger */ 202 public String getOS() { 203 return PlatformInfo.getOS(); 204 } 205 206 /** From the Debugger interface via JVMDebugger */ 207 public String getCPU() { 208 return PlatformInfo.getCPU(); 209 } 210 211 public boolean hasConsole() throws DebuggerException { 212 return true; 213 } 214 215 public synchronized String consoleExecuteCommand(String cmd) throws DebuggerException { 216 requireAttach(); 217 if (! attached) { 218 throw new DebuggerException("debugger not yet attached to a Dr. Watson dump!"); 219 } 220 221 return consoleExecuteCommand0(cmd); 222 } 223 224 public String getConsolePrompt() throws DebuggerException { 225 return "(windbg)"; 226 } 227 228 public CDebugger getCDebugger() throws DebuggerException { 229 if (cdbg == null) { 230 cdbg = new WindbgCDebugger(this); 231 } 232 return cdbg; 233 } 234 235 /** From the SymbolLookup interface via Debugger and JVMDebugger */ 236 public synchronized Address lookup(String objectName, String symbol) { 237 requireAttach(); 238 return newAddress(lookupByName(objectName, symbol)); 239 } 240 241 /** From the SymbolLookup interface via Debugger and JVMDebugger */ 242 public synchronized OopHandle lookupOop(String objectName, String symbol) { 243 Address addr = lookup(objectName, symbol); 244 if (addr == null) { 245 return null; 246 } 247 return addr.addOffsetToAsOopHandle(0); 248 } 249 250 public synchronized ClosestSymbol lookup(long address) { 251 return lookupByAddress0(address); 252 } 253 254 /** From the Debugger interface */ 255 public MachineDescription getMachineDescription() { 256 return machDesc; 257 } 258 259 //-------------------------------------------------------------------------------- 260 // Implementation of ThreadAccess interface 261 // 262 263 264 /** From the ThreadAccess interface via Debugger and JVMDebugger */ 265 public ThreadProxy getThreadForIdentifierAddress(Address addr) { 266 return threadFactory.createThreadWrapper(addr); 267 } 268 269 public ThreadProxy getThreadForThreadId(long handle) { 270 // with windbg we can't make out using handle 271 throw new DebuggerException("Unimplemented!"); 272 } 273 274 public long getThreadIdFromSysId(long sysId) throws DebuggerException { 275 requireAttach(); 276 return getThreadIdFromSysId0(sysId); 277 } 278 279 //---------------------------------------------------------------------- 280 // Overridden from DebuggerBase because we need to relax alignment 281 // constraints on x86 282 283 public long readJLong(long address) 284 throws UnmappedAddressException, UnalignedAddressException { 285 checkJavaConfigured(); 286 // FIXME: allow this to be configurable. Undesirable to add a 287 // dependency on the runtime package here, though, since this 288 // package should be strictly underneath it. 289 // utils.checkAlignment(address, jlongSize); 290 utils.checkAlignment(address, jintSize); 291 byte[] data = readBytes(address, jlongSize); 292 return utils.dataToJLong(data, jlongSize); 293 } 294 295 //-------------------------------------------------------------------------------- 296 // Internal routines (for implementation of WindbgAddress). 297 // These must not be called until the MachineDescription has been set up. 298 // 299 300 /** From the WindbgDebugger interface */ 301 public String addressValueToString(long address) { 302 return utils.addressValueToString(address); 303 } 304 305 /** From the WindbgDebugger interface */ 306 public WindbgAddress readAddress(long address) 307 throws UnmappedAddressException, UnalignedAddressException { 308 return (WindbgAddress) newAddress(readAddressValue(address)); 309 } 310 311 public WindbgAddress readCompOopAddress(long address) 312 throws UnmappedAddressException, UnalignedAddressException { 313 return (WindbgAddress) newAddress(readCompOopAddressValue(address)); 314 } 315 316 public WindbgAddress readCompKlassAddress(long address) 317 throws UnmappedAddressException, UnalignedAddressException { 318 return (WindbgAddress) newAddress(readCompKlassAddressValue(address)); 319 } 320 321 /** From the WindbgDebugger interface */ 322 public WindbgOopHandle readOopHandle(long address) 323 throws UnmappedAddressException, UnalignedAddressException, NotInHeapException { 324 long value = readAddressValue(address); 325 return (value == 0 ? null : new WindbgOopHandle(this, value)); 326 } 327 public WindbgOopHandle readCompOopHandle(long address) 328 throws UnmappedAddressException, UnalignedAddressException, NotInHeapException { 329 long value = readCompOopAddressValue(address); 330 return (value == 0 ? null : new WindbgOopHandle(this, value)); 331 } 332 333 /** From the WindbgDebugger interface */ 334 public int getAddressSize() { 335 return (int) machDesc.getAddressSize(); 336 } 337 338 //-------------------------------------------------------------------------------- 339 // Thread context access 340 // 341 342 private synchronized void setThreadIntegerRegisterSet(long threadId, 343 long[] regs) { 344 threadIntegerRegisterSet.put(threadId, regs); 345 } 346 347 private synchronized void addThread(long sysId) { 348 threadList.add(threadFactory.createThreadWrapper(sysId)); 349 } 350 351 public synchronized long[] getThreadIntegerRegisterSet(long threadId) 352 throws DebuggerException { 353 requireAttach(); 354 return (long[]) threadIntegerRegisterSet.get(threadId); 355 } 356 357 public synchronized List<ThreadProxy> getThreadList() throws DebuggerException { 358 requireAttach(); 359 return threadList; 360 } 361 362 private String findFullPath(String file) { 363 File f = new File(file); 364 if (f.exists()) { 365 return file; 366 } else { 367 // remove path part, if any. 368 file = f.getName(); 369 StringTokenizer st = new StringTokenizer(imagePath, File.pathSeparator); 370 while (st.hasMoreTokens()) { 371 f = new File(st.nextToken(), file); 372 if (f.exists()) { 373 return f.getPath(); 374 } 375 } 376 } 377 return null; 378 } 379 380 private synchronized void addLoadObject(String file, long size, long base) { 381 String path = findFullPath(file); 382 if (path != null) { 383 DLL dll = null; 384 if (useNativeLookup) { 385 dll = new DLL(this, path, size,newAddress(base)) { 386 public ClosestSymbol closestSymbolToPC(Address pcAsAddr) { 387 long pc = getAddressValue(pcAsAddr); 388 ClosestSymbol sym = lookupByAddress0(pc); 389 if (sym == null) { 390 return super.closestSymbolToPC(pcAsAddr); 391 } else { 392 return sym; 393 } 394 } 395 }; 396 } else { 397 dll = new DLL(this, path, size, newAddress(base)); 398 } 399 loadObjects.add(dll); 400 nameToDllMap.put(new File(file).getName(), dll); 401 } 402 } 403 404 //-------------------------------------------------------------------------------- 405 // Address access 406 // 407 408 /** From the Debugger interface */ 409 public long getAddressValue(Address addr) { 410 if (addr == null) return 0; 411 return ((WindbgAddress) addr).getValue(); 412 } 413 414 /** From the WindbgDebugger interface */ 415 public Address newAddress(long value) { 416 if (value == 0) return null; 417 return new WindbgAddress(this, value); 418 } 419 420 //-------------------------------------------------------------------------------- 421 // Internals only below this point 422 // 423 424 // attach/detach helpers 425 private void checkAttached() { 426 if (attached) { 427 String msg = (isCore)? "already attached to a Dr. Watson dump!" : 428 "already attached to a process!"; 429 throw new DebuggerException(msg); 430 } 431 } 432 433 private void requireAttach() { 434 if (!attached) { 435 throw new RuntimeException("not attached to a process or Dr Watson dump"); 436 } 437 } 438 439 private void attachInit() { 440 checkAttached(); 441 loadObjects = new ArrayList<>(); 442 nameToDllMap = new HashMap<>(); 443 threadIntegerRegisterSet = new HashMap<>(); 444 threadList = new ArrayList<>(); 445 } 446 447 private void resetNativePointers() { 448 ptrIDebugClient = 0L; 449 ptrIDebugControl = 0L; 450 ptrIDebugDataSpaces = 0L; 451 ptrIDebugOutputCallbacks = 0L; 452 ptrIDebugAdvanced = 0L; 453 ptrIDebugSymbols = 0L; 454 ptrIDebugSystemObjects = 0L; 455 } 456 457 synchronized long lookupByName(String objectName, String symbol) { 458 long res = 0L; 459 if (useNativeLookup) { 460 res = lookupByName0(objectName, symbol); 461 if (res != 0L) { 462 return res; 463 } // else fallthru... 464 } 465 466 DLL dll = (DLL) nameToDllMap.get(objectName); 467 // The DLL can be null because we use this to search through known 468 // DLLs in HotSpotTypeDataBase (for example) 469 if (dll != null) { 470 WindbgAddress addr = (WindbgAddress) dll.lookupSymbol(symbol); 471 if (addr != null) { 472 return addr.getValue(); 473 } 474 } 475 return 0L; 476 } 477 478 /** This reads bytes from the remote process. */ 479 public synchronized ReadResult readBytesFromProcess(long address, long numBytes) 480 throws UnmappedAddressException, DebuggerException { 481 requireAttach(); 482 byte[] res = readBytesFromProcess0(address, numBytes); 483 if(res != null) 484 return new ReadResult(res); 485 else 486 return new ReadResult(address); 487 } 488 489 490 private DLL findDLLByName(String fullPathName) { 491 for (Iterator iter = loadObjects.iterator(); iter.hasNext(); ) { 492 DLL dll = (DLL) iter.next(); 493 if (dll.getName().equals(fullPathName)) { 494 return dll; 495 } 496 } 497 return null; 498 } 499 500 public void writeBytesToProcess(long address, long numBytes, byte[] data) 501 throws UnmappedAddressException, DebuggerException { 502 // FIXME 503 throw new DebuggerException("Unimplemented"); 504 } 505 506 private static String imagePath; 507 private static String symbolPath; 508 private static boolean useNativeLookup; 509 510 static { 511 512 /* 513 * saproc.dll depends on dbgeng.dll which itself depends on 514 * dbghelp.dll. We have to make sure that the dbgeng.dll and 515 * dbghelp.dll that we load are compatible with each other. We 516 * load both of those libraries from the same directory based 517 * on the theory that co-located libraries are compatible. 518 * 519 * On Windows 2000 and earlier, dbgeng.dll and dbghelp.dll were 520 * not included as part of the standard system directory. On 521 * systems newer than Windows 2000, dbgeng.dll and dbghelp.dll 522 * are included in the standard system directory. However, the 523 * versions included in the standard system directory may not 524 * be able to handle symbol information for the newer compilers. 525 * 526 * We search for and explicitly load the libraries using the 527 * following directory search order: 528 * 529 * - java.home/bin (same as $JAVA_HOME/jre/bin) 530 * - dir named by DEBUGGINGTOOLSFORWINDOWS environment variable 531 * - various "Debugging Tools For Windows" program directories 532 * - the system directory ($SYSROOT/system32) 533 * 534 * If SA is invoked with -Dsun.jvm.hotspot.loadLibrary.DEBUG=1, 535 * then debug messages about library loading are printed to 536 * System.err. 537 */ 538 539 String dbgengPath = null; 540 String dbghelpPath = null; 541 String saprocPath = null; 542 List<String> searchList = new ArrayList<>(); 543 544 boolean loadLibraryDEBUG = 545 System.getProperty("sun.jvm.hotspot.loadLibrary.DEBUG") != null; 546 547 { 548 // First place to search is co-located with saproc.dll in 549 // $JAVA_HOME/jre/bin (java.home property is set to $JAVA_HOME/jre): 550 searchList.add(System.getProperty("java.home") + File.separator + "bin"); 551 saprocPath = (String) searchList.get(0) + File.separator + 552 "saproc.dll"; 553 554 // second place to search is specified by an environment variable: 555 String DTFWHome = System.getenv("DEBUGGINGTOOLSFORWINDOWS"); 556 if (DTFWHome != null) { 557 searchList.add(DTFWHome); 558 } 559 560 // The third place to search is the install directory for the 561 // "Debugging Tools For Windows" package; so far there are three 562 // name variations that we know of: 563 String sysRoot = System.getenv("SYSTEMROOT"); 564 DTFWHome = sysRoot + File.separator + ".." + File.separator + 565 "Program Files" + File.separator + "Debugging Tools For Windows"; 566 searchList.add(DTFWHome); 567 568 // Only add the search path for the current CPU architecture: 569 String cpu = PlatformInfo.getCPU(); 570 if (cpu.equals("x86")) { 571 searchList.add(DTFWHome + " (x86)"); 572 } else if (cpu.equals("amd64")) { 573 searchList.add(DTFWHome + " (x64)"); 574 } 575 // The last place to search is the system directory: 576 searchList.add(sysRoot + File.separator + "system32"); 577 } 578 579 for (int i = 0; i < searchList.size(); i++) { 580 File dir = new File((String) searchList.get(i)); 581 if (!dir.exists()) { 582 if (loadLibraryDEBUG) { 583 System.err.println("DEBUG: '" + searchList.get(i) + 584 "': directory does not exist."); 585 } 586 // this search directory doesn't exist so skip it 587 continue; 588 } 589 590 dbgengPath = (String) searchList.get(i) + File.separator + "dbgeng.dll"; 591 dbghelpPath = (String) searchList.get(i) + File.separator + "dbghelp.dll"; 592 593 File feng = new File(dbgengPath); 594 File fhelp = new File(dbghelpPath); 595 if (feng.exists() && fhelp.exists()) { 596 // both files exist so we have a match 597 break; 598 } 599 600 // At least one of the files does not exist; no warning if both 601 // don't exist. If just one doesn't exist then we don't check 602 // loadLibraryDEBUG because we have a mis-configured system. 603 if (feng.exists()) { 604 System.err.println("WARNING: found '" + dbgengPath + 605 "' but did not find '" + dbghelpPath + "'; ignoring '" + 606 dbgengPath + "'."); 607 } else if (fhelp.exists()) { 608 System.err.println("WARNING: found '" + dbghelpPath + 609 "' but did not find '" + dbgengPath + "'; ignoring '" + 610 dbghelpPath + "'."); 611 } else if (loadLibraryDEBUG) { 612 System.err.println("DEBUG: searched '" + searchList.get(i) + 613 "': dbgeng.dll and dbghelp.dll were not found."); 614 } 615 dbgengPath = null; 616 dbghelpPath = null; 617 } 618 619 if (dbgengPath == null || dbghelpPath == null) { 620 // at least one of the files wasn't found anywhere we searched 621 String mesg = null; 622 623 if (dbgengPath == null && dbghelpPath == null) { 624 mesg = "dbgeng.dll and dbghelp.dll cannot be found. "; 625 } else if (dbgengPath == null) { 626 mesg = "dbgeng.dll cannot be found (dbghelp.dll was found). "; 627 } else { 628 mesg = "dbghelp.dll cannot be found (dbgeng.dll was found). "; 629 } 630 throw new UnsatisfiedLinkError(mesg + 631 "Please search microsoft.com for 'Debugging Tools For Windows', " + 632 "and either download it to the default location, or download it " + 633 "to a custom location and set environment variable " + 634 "'DEBUGGINGTOOLSFORWINDOWS' to the pathname of that location."); 635 } 636 637 // NOTE: The order of loads is important! If we load dbgeng.dll 638 // first, then the dependency - dbghelp.dll - will be loaded 639 // from usual DLL search thereby defeating the purpose! 640 if (loadLibraryDEBUG) { 641 System.err.println("DEBUG: loading '" + dbghelpPath + "'."); 642 } 643 System.load(dbghelpPath); 644 if (loadLibraryDEBUG) { 645 System.err.println("DEBUG: loading '" + dbgengPath + "'."); 646 } 647 System.load(dbgengPath); 648 649 // Now, load saproc.dll 650 if (loadLibraryDEBUG) { 651 System.err.println("DEBUG: loading '" + saprocPath + "'."); 652 } 653 System.load(saprocPath); 654 655 // where do I find '.exe', '.dll' files? 656 imagePath = System.getProperty("sun.jvm.hotspot.debugger.windbg.imagePath"); 657 if (imagePath == null) { 658 imagePath = System.getenv("PATH"); 659 } 660 661 // where do I find '.pdb', '.dbg' files? 662 symbolPath = System.getProperty("sun.jvm.hotspot.debugger.windbg.symbolPath"); 663 664 // mostly, debug files would be find where .dll's, .exe's are found. 665 if (symbolPath == null) { 666 symbolPath = imagePath; 667 } 668 669 // should we parse DLL symbol table in Java code or use 670 // Windbg's native lookup facility? By default, we use 671 // native lookup so that we can take advantage of '.pdb' 672 // files, if available. 673 useNativeLookup = true; 674 String str = System.getProperty("sun.jvm.hotspot.debugger.windbg.disableNativeLookup"); 675 if (str != null) { 676 useNativeLookup = false; 677 } 678 679 initIDs(); 680 } 681 682 // native methods 683 private static native void initIDs(); 684 private native void attach0(String executableName, String coreFileName); 685 private native void attach0(int processID); 686 private native void detach0(); 687 private native byte[] readBytesFromProcess0(long address, long numBytes) 688 throws UnmappedAddressException, DebuggerException; 689 private native long getThreadIdFromSysId0(long sysId); 690 private native String consoleExecuteCommand0(String cmd); 691 private native long lookupByName0(String objName, String symName); 692 private native ClosestSymbol lookupByAddress0(long address); 693 694 // helper called lookupByAddress0 695 private ClosestSymbol createClosestSymbol(String symbol, long diff) { 696 return new ClosestSymbol(symbol, diff); 697 } 698 }