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 }