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