1 /*
   2  * Copyright 2002-2004 Sun Microsystems, Inc.  All Rights Reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  20  * CA 95054 USA or visit www.sun.com if you need additional information or
  21  * have any questions.
  22  *  
  23  */
  24 
  25 package sun.jvm.hotspot.debugger.proc;
  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.cdbg.*;
  32 import sun.jvm.hotspot.debugger.proc.amd64.*;
  33 import sun.jvm.hotspot.debugger.proc.sparc.*;
  34 import sun.jvm.hotspot.debugger.proc.x86.*;
  35 import sun.jvm.hotspot.debugger.amd64.*;
  36 import sun.jvm.hotspot.debugger.sparc.*;
  37 import sun.jvm.hotspot.debugger.x86.*;
  38 import sun.jvm.hotspot.utilities.*;
  39 
  40 /** <P> An implementation of the JVMDebugger interface which sits on
  41  * top of proc and relies on the SA's proc import module for
  42  * communication with the debugger. </P>
  43  *
  44  * <P> <B>NOTE</B> that since we have the notion of fetching "Java
  45  * primitive types" from the remote process (which might have
  46  * different sizes than we expect) we have a bootstrapping
  47  * problem. We need to know the sizes of these types before we can
  48  * fetch them. The current implementation solves this problem by
  49  * requiring that it be configured with these type sizes before they
  50  * can be fetched. The readJ(Type) routines here will throw a
  51  * RuntimeException if they are called before the debugger is
  52  * configured with the Java primitive type sizes. </P>
  53  */
  54 
  55 public class ProcDebuggerLocal extends DebuggerBase implements ProcDebugger {
  56     
  57     
  58     protected static final int cacheSize = 16 * 1024 * 1024; // 16 MB
  59     
  60     //------------------------------------------------------------------------
  61     // Implementation of Debugger interface
  62     //
  63     
  64     /** <P> machDesc may be null if it couldn't be determined yet; i.e.,
  65      * if we're on SPARC, we need to ask the remote process whether
  66      * we're in 32- or 64-bit mode. </P>
  67      *
  68      * <P> useCache should be set to true if debugging is being done
  69      * locally, and to false if the debugger is being created for the
  70      * purpose of supporting remote debugging. </P> */
  71     public ProcDebuggerLocal(MachineDescription machDesc, boolean useCache) {
  72         this.machDesc = machDesc;
  73         int cacheNumPages;
  74         int cachePageSize;
  75         
  76         final String cpu = PlatformInfo.getCPU();
  77         if (cpu.equals("sparc")) {
  78             threadFactory = new ProcSPARCThreadFactory(this);
  79             pcRegIndex = SPARCThreadContext.R_PC;
  80             fpRegIndex = SPARCThreadContext.R_I6;
  81         } else if (cpu.equals("x86")) {
  82             threadFactory = new ProcX86ThreadFactory(this);
  83             pcRegIndex = X86ThreadContext.EIP;
  84             fpRegIndex = X86ThreadContext.EBP;
  85             unalignedAccessesOkay = true;
  86         } else if (cpu.equals("amd64")) {
  87             threadFactory = new ProcAMD64ThreadFactory(this);
  88             pcRegIndex = AMD64ThreadContext.RIP;
  89             fpRegIndex = AMD64ThreadContext.RBP;
  90         } else {
  91             throw new RuntimeException("Thread access for CPU architecture " + PlatformInfo.getCPU() + " not yet supported");
  92         }
  93         if (useCache) {
  94             // Cache portion of the remote process's address space.
  95             // For now, this cache works best if it covers the entire
  96             // heap of the remote process. FIXME: at least should make this
  97             // tunable from the outside, i.e., via the UI. This is a 16 MB
  98             // cache divided on SPARC into 2048 8K pages and on x86 into
  99             // 4096 4K pages; the page size must be adjusted to be the OS's
 100             // page size.
 101             
 102             cachePageSize = getPageSize();
 103             cacheNumPages = parseCacheNumPagesProperty(cacheSize / cachePageSize);
 104             initCache(cachePageSize, cacheNumPages);
 105         }
 106         
 107         resetNativePointers();
 108         clearCacheFields();
 109     }
 110     
 111     /** FIXME: implement this with a Runtime.exec() of ps followed by
 112      * parsing of its output */
 113     public boolean hasProcessList() throws DebuggerException {
 114         return false;
 115     }
 116     
 117     public List getProcessList() throws DebuggerException {
 118         throw new DebuggerException("Not yet supported");
 119     }
 120     
 121     
 122     /** From the Debugger interface via JVMDebugger */
 123     public synchronized void attach(int processID) throws DebuggerException {
 124         checkAttached();
 125         isCore = false;
 126         attach0(new Integer(processID).toString());
 127         attached = true;
 128         suspended = true;
 129     }
 130     
 131     /** From the Debugger interface via JVMDebugger */
 132     public synchronized void attach
 133     (String executableName, String coreFileName) throws DebuggerException {
 134         checkAttached();
 135         isCore = true;
 136         topFrameCache = new HashMap();
 137         attach0(executableName, coreFileName);
 138         attached = true;
 139         suspended = true;
 140     }
 141     
 142     /** From the Debugger interface via JVMDebugger */
 143     public synchronized boolean detach() {
 144         if (! attached) {
 145             return false;
 146         }
 147         
 148         try {
 149             if (p_ps_prochandle == 0L) {
 150                 return false;
 151             }
 152             detach0();
 153             clearCache();
 154             return true;
 155         } catch (Exception e) {
 156             e.printStackTrace();
 157             return false;
 158         } finally {
 159             resetNativePointers();
 160             clearCacheFields();
 161             suspended = false;
 162             attached = false;
 163         }
 164     }
 165     
 166     public synchronized void suspend() throws DebuggerException {
 167         requireAttach();
 168         if (suspended) {
 169             throw new DebuggerException("Process already suspended");
 170         }
 171         suspend0();
 172         suspended = true;
 173         enableCache();
 174         reresolveLoadObjects();
 175     }
 176     
 177     public synchronized void resume() throws DebuggerException {
 178         requireAttach();
 179         if (!suspended) {
 180             throw new DebuggerException("Process not suspended");
 181         }
 182         resume0();
 183         disableCache();
 184         suspended = false;
 185     }
 186     
 187     public synchronized boolean isSuspended() throws DebuggerException {
 188         requireAttach();
 189         return suspended;
 190     }
 191     
 192     /** From the Debugger interface via JVMDebugger */
 193     public Address parseAddress(String addressString) throws NumberFormatException {
 194         long addr = utils.scanAddress(addressString);
 195         if (addr == 0) {
 196             return null;
 197         }
 198         return new ProcAddress(this, addr);
 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 false;
 213     }
 214     
 215     public String consoleExecuteCommand(String cmd) throws DebuggerException {
 216         throw new DebuggerException("Can't execute console commands");
 217     }
 218     
 219     public String getConsolePrompt() throws DebuggerException {
 220         return "";
 221     }
 222     
 223     public CDebugger getCDebugger() throws DebuggerException {
 224         if (cdbg == null) {
 225             cdbg = new ProcCDebugger(this);
 226         }
 227         return cdbg;
 228     }
 229     
 230     /** From the SymbolLookup interface via Debugger and JVMDebugger */
 231     public synchronized Address lookup(String objectName, String symbol) {
 232         requireAttach();
 233         long addr = lookupByName0(objectName, symbol);
 234         if (addr == 0) {
 235             return null;
 236         }
 237         return new ProcAddress(this, addr);
 238     }
 239     
 240     /** From the SymbolLookup interface via Debugger and JVMDebugger */
 241     public synchronized OopHandle lookupOop(String objectName, String symbol) {
 242         Address addr = lookup(objectName, symbol);
 243         if (addr == null) {
 244             return null;
 245         }
 246         return addr.addOffsetToAsOopHandle(0);
 247     }
 248     
 249     /** From the ProcDebugger interface */
 250     public MachineDescription getMachineDescription() {
 251         return machDesc;
 252     }
 253     
 254     /** Internal routine supporting lazy setting of MachineDescription,
 255      * since on SPARC we will need to query the remote process to ask
 256      * it what its data model is (32- or 64-bit).
 257      */
 258     
 259     public void setMachineDescription(MachineDescription machDesc) {
 260         this.machDesc = machDesc;
 261         setBigEndian(machDesc.isBigEndian());
 262         utils = new DebuggerUtilities(machDesc.getAddressSize(), machDesc.isBigEndian());
 263     }
 264     
 265     public synchronized int getRemoteProcessAddressSize()
 266     throws DebuggerException {
 267         requireAttach();
 268         return getRemoteProcessAddressSize0();
 269     }
 270     
 271     //--------------------------------------------------------------------------------
 272     // Implementation of ThreadAccess interface
 273     //
 274     
 275     /** From the ThreadAccess interface via Debugger and JVMDebugger */
 276     public ThreadProxy getThreadForIdentifierAddress(Address addr) {
 277         return threadFactory.createThreadWrapper(addr);
 278     }
 279     
 280     public ThreadProxy getThreadForThreadId(long id) {
 281         return threadFactory.createThreadWrapper(id);
 282     }
 283     
 284     //----------------------------------------------------------------------
 285     // Overridden from DebuggerBase because we need to relax alignment
 286     // constraints on x86
 287     
 288     public long readJLong(long address)
 289     throws UnmappedAddressException, UnalignedAddressException {
 290         checkJavaConfigured();
 291         // FIXME: allow this to be configurable. Undesirable to add a
 292         // dependency on the runtime package here, though, since this
 293         // package should be strictly underneath it.
 294         if (unalignedAccessesOkay) {
 295             utils.checkAlignment(address, jintSize);
 296         } else {
 297             utils.checkAlignment(address, jlongSize);
 298         }
 299         byte[] data = readBytes(address, jlongSize);
 300         return utils.dataToJLong(data, jlongSize);
 301     }
 302     
 303     //--------------------------------------------------------------------------------
 304     // Internal routines (for implementation of ProcAddress).
 305     // These must not be called until the MachineDescription has been set up.
 306     //
 307     
 308     /** From the ProcDebugger interface */
 309     public String addressValueToString(long address) {
 310         return utils.addressValueToString(address);
 311     }
 312     
 313     /** Need to override this to relax alignment checks on Solaris/x86. */
 314     public long readCInteger(long address, long numBytes, boolean isUnsigned)
 315     throws UnmappedAddressException, UnalignedAddressException {
 316         checkConfigured();
 317         if (!unalignedAccessesOkay) {
 318             utils.checkAlignment(address, numBytes);
 319         } else {
 320             // Only slightly relaxed semantics -- this is a hack, but is
 321             // necessary on Solaris/x86 where it seems the compiler is
 322             // putting some global 64-bit data on 32-bit boundaries
 323             if (numBytes == 8) {
 324                 utils.checkAlignment(address, 4);
 325             } else {
 326                 utils.checkAlignment(address, numBytes);
 327             }
 328         }
 329         byte[] data = readBytes(address, numBytes);
 330         return utils.dataToCInteger(data, isUnsigned);
 331     }
 332     
 333     /** From the ProcDebugger interface */
 334     public ProcAddress readAddress(long address)
 335     throws UnmappedAddressException, UnalignedAddressException {
 336         long value = readAddressValue(address);
 337         return (value == 0 ? null : new ProcAddress(this, value));
 338     }
 339     
 340     /** From the ProcDebugger interface */
 341     public ProcOopHandle readOopHandle(long address)
 342     throws UnmappedAddressException, UnalignedAddressException, NotInHeapException {
 343         long value = readAddressValue(address);
 344         return (value == 0 ? null : new ProcOopHandle(this, value));
 345     }
 346     
 347     public void writeBytesToProcess(long address, long numBytes, byte[] data)
 348     throws UnmappedAddressException, DebuggerException {
 349         if (isCore) {
 350             throw new DebuggerException("Attached to a core file!");
 351         }
 352         writeBytesToProcess0(address, numBytes, data);
 353     }
 354     
 355     public synchronized ReadResult readBytesFromProcess(long address, long numBytes)
 356     throws DebuggerException {
 357         requireAttach();
 358         byte[] res = readBytesFromProcess0(address, numBytes);
 359         if(res != null)
 360             return new ReadResult(res);
 361         else
 362             return new ReadResult(address);
 363     }
 364     
 365     protected int getPageSize() {
 366         int pagesize = getPageSize0();
 367         if (pagesize == -1) {
 368             // return the hard coded default value.
 369             pagesize = (PlatformInfo.getCPU().equals("x86"))? 4096 : 8192;
 370         }
 371         return pagesize;
 372     }
 373     
 374     //--------------------------------------------------------------------------------
 375     // Thread context access. Can not be package private, but should
 376     // only be accessed by the architecture-specific subpackages.
 377     
 378     /** From the ProcDebugger interface. May have to redefine this later. */
 379     public synchronized long[] getThreadIntegerRegisterSet(int tid) {
 380         requireAttach();
 381         return getThreadIntegerRegisterSet0(tid);
 382     }
 383     
 384     //--------------------------------------------------------------------------------
 385     // Address access. Can not be package private, but should only be
 386     // accessed by the architecture-specific subpackages.
 387     
 388     /** From the ProcDebugger interface */
 389     public long getAddressValue(Address addr) {
 390         if (addr == null) return 0;
 391         return ((ProcAddress) addr).getValue();
 392     }
 393     
 394     /** From the ProcDebugger interface */
 395     public Address newAddress(long value) {
 396         if (value == 0) return null;
 397         return new ProcAddress(this, value);
 398     }
 399     
 400     /** From the ProcDebugger interface */
 401     public synchronized List getThreadList() throws DebuggerException {
 402         requireAttach();
 403         List res = null;
 404         if (isCore && (threadListCache != null)) {
 405             res = threadListCache;
 406         } else {
 407             res = new ArrayList();
 408             fillThreadList0(res);
 409             if (isCore) {
 410                 threadListCache = res;
 411             }
 412         }
 413         return res;
 414     }
 415     
 416     /** From the ProcDebugger interface */
 417     public synchronized List getLoadObjectList() throws DebuggerException {
 418         requireAttach();
 419         if (!suspended) {
 420             throw new DebuggerException("Process not suspended");
 421         }
 422         
 423         if (loadObjectCache == null) {
 424             updateLoadObjectCache();
 425         }
 426         return loadObjectCache;
 427     }
 428     
 429     /** From the ProcDebugger interface */
 430     public synchronized CFrame topFrameForThread(ThreadProxy thread)
 431     throws DebuggerException {
 432         requireAttach();
 433         CFrame res = null;
 434         if (isCore && ((res = (CFrame) topFrameCache.get(thread)) != null)) {
 435             return res;
 436         } else {
 437             ThreadContext context = thread.getContext();
 438             int numRegs = context.getNumRegisters();
 439             long[] regs = new long[numRegs];
 440             for (int i = 0; i < numRegs; i++) {
 441                 regs[i] = context.getRegister(i);
 442             }
 443             res = fillCFrameList0(regs);
 444             if (isCore) {
 445                 topFrameCache.put(thread, res);
 446             }
 447             return res;
 448         }
 449     }
 450     
 451     /** From the ProcDebugger interface */
 452     public synchronized ClosestSymbol lookup(long address) {
 453         requireAttach();
 454         return lookupByAddress0(address);
 455     }
 456     
 457     /** From the ProcDebugger interface */
 458     public String demangle(String name) {
 459         return demangle0(name);
 460     }
 461     
 462     //------------- Internals only below this point --------------------
 463     //
 464     //
 465     
 466     private void updateLoadObjectCache() {
 467         List res = new ArrayList();
 468         nameToDsoMap = new HashMap();
 469         fillLoadObjectList0(res);
 470         loadObjectCache = sortLoadObjects(res);
 471     }
 472     
 473     // sort load objects by base address
 474     private static List sortLoadObjects(List in) {
 475         // sort the list by base address
 476         Object[] arr = in.toArray();
 477         Arrays.sort(arr, loadObjectComparator);
 478         return Arrays.asList(arr);
 479     }
 480     
 481     private long lookupByName(String objectName, String symbolName)
 482     throws DebuggerException {
 483         // NOTE: this assumes that process is suspended (which is probably
 484         // necessary assumption given that DSOs can be loaded/unloaded as
 485         // process runs). Should update documentation.
 486         if (nameToDsoMap == null) {
 487             getLoadObjectList();
 488         }
 489         SharedObject dso = (SharedObject) nameToDsoMap.get(objectName);
 490         // The DSO can be null because we use this to search through known
 491         // DSOs in HotSpotTypeDataBase (for example)
 492         if (dso != null) {
 493             ProcAddress addr = (ProcAddress) dso.lookupSymbol(symbolName);
 494             if (addr != null) {
 495                 return addr.getValue();
 496             }
 497         }
 498         return 0;
 499     }
 500     
 501     private SharedObject findDSOByName(String fullPathName) {
 502         if (loadObjectCache == null)
 503             return null;
 504         for (Iterator iter = loadObjectCache.iterator(); iter.hasNext(); ) {
 505             SharedObject dso = (SharedObject) iter.next();
 506             if (dso.getName().equals(fullPathName)) {
 507                 return dso;
 508             }
 509         }
 510         return null;
 511     }
 512     
 513     private void reresolveLoadObjects() throws DebuggerException {
 514         if (loadObjectCache == null) {
 515             return;
 516         }
 517         updateLoadObjectCache();
 518     }
 519     
 520     
 521     private void checkAttached() {
 522         if (attached) {
 523             if (isCore) {
 524                 throw new DebuggerException("already attached to a core file!");
 525             } else {
 526                 throw new DebuggerException("already attached to a process!");
 527             }
 528         }
 529     }
 530     
 531     private void requireAttach() {
 532         if (! attached) {
 533             throw new RuntimeException("not attached to a process or core file!");
 534         }
 535     }
 536     
 537     private void clearCacheFields() {
 538         loadObjectCache = null;
 539         nameToDsoMap    = null;
 540         threadListCache = null;
 541         topFrameCache   = null;
 542     }
 543     
 544     private void resetNativePointers() {
 545         p_ps_prochandle          = 0L;
 546         
 547         // reset thread_db pointers
 548         libthread_db_handle    = 0L;
 549         p_td_thragent_t        = 0L;
 550         p_td_init              = 0L;
 551         p_td_ta_new            = 0L;
 552         p_td_ta_delete         = 0L;
 553         p_td_ta_thr_iter       = 0L;
 554         p_td_thr_get_info      = 0L;
 555         p_td_ta_map_id2thr     = 0L;
 556         p_td_thr_getgregs      = 0L;
 557         
 558         // part of class sharing workaround
 559         classes_jsa_fd         = -1;
 560         p_file_map_header      = 0L;
 561     }
 562     
 563     // native methods and native helpers
 564     
 565     // attach, detach
 566     private native void attach0(String pid) throws DebuggerException;
 567     private native void attach0(String executableFile, String coreFileName) throws DebuggerException;
 568     private native void detach0() throws DebuggerException;
 569     
 570     // address size, page size
 571     private native int getRemoteProcessAddressSize0() throws DebuggerException;
 572     private native int getPageSize0() throws DebuggerException;
 573     
 574     // threads, stacks
 575     private native long[] getThreadIntegerRegisterSet0(long tid) throws DebuggerException;
 576     private native void   fillThreadList0(List l) throws DebuggerException;
 577     
 578     // fills stack frame list given reg set of the top frame and top frame
 579     private native ProcCFrame fillCFrameList0(long[] regs) throws DebuggerException;
 580     
 581     // helper called by fillCFrameList0
 582     private ProcCFrame createSenderFrame(ProcCFrame f, long pc, long fp) {
 583         ProcCFrame sender = new ProcCFrame(this, newAddress(pc), newAddress(fp));
 584         if (f != null) {
 585             f.setSender(sender);
 586         }
 587         return sender;
 588     }
 589     
 590     // shared objects
 591     private native void fillLoadObjectList0(List l) throws DebuggerException;
 592     
 593     // helper called by fillLoadObjectList0
 594     private LoadObject createLoadObject(String fileName, long textsize, long base) {
 595         File f = new File(fileName);
 596         Address baseAddr = newAddress(base);
 597         SharedObject res = findDSOByName(fileName);
 598         if (res != null) {
 599             // already in cache. just change the base, if needed
 600             Address oldBase = res.getBase();
 601             if (! baseAddr.equals(oldBase)) {
 602                 res.setBase(baseAddr);
 603             }
 604         } else {
 605             // new shared object.
 606             res = new SharedObject(this, fileName, f.length(), baseAddr);
 607         }
 608         nameToDsoMap.put(f.getName(), res);
 609         return res;
 610     }
 611     
 612     // symbol-to-pc
 613     private native long lookupByName0(String objectName, String symbolName) throws DebuggerException;
 614     private native ClosestSymbol lookupByAddress0(long address) throws DebuggerException;
 615     
 616     // helper called by lookupByAddress0
 617     private ClosestSymbol createClosestSymbol(String name, long offset) {
 618         return new ClosestSymbol(name, offset);
 619     }
 620     
 621     // process read/write
 622     private native byte[] readBytesFromProcess0(long address, long numBytes) throws DebuggerException;
 623     private native void writeBytesToProcess0(long address, long numBytes, byte[] data) throws DebuggerException;
 624     
 625     // process control
 626     private native void suspend0() throws DebuggerException;
 627     private native void resume0() throws DebuggerException;
 628     
 629     // demangle a C++ name
 630     private native String demangle0(String name);
 631     
 632     // init JNI ids to fields, methods
 633     private native static void initIDs() throws DebuggerException;
 634     private static LoadObjectComparator loadObjectComparator;
 635     
 636     static {
 637         System.loadLibrary("saproc");
 638         initIDs();
 639         loadObjectComparator = new LoadObjectComparator();
 640     }
 641     
 642     private boolean unalignedAccessesOkay;
 643     private ProcThreadFactory threadFactory;
 644     
 645     // indices of PC and FP registers in gregset
 646     private int pcRegIndex;
 647     private int fpRegIndex;
 648     
 649     // Symbol lookup support
 650     // This is a map of library names to DSOs
 651     private Map nameToDsoMap;  // Map<String, SharedObject>
 652     
 653     // C/C++ debugging support
 654     private List/*<LoadObject>*/ loadObjects;
 655     private CDebugger cdbg;
 656     
 657     // ProcessControl support
 658     private boolean suspended;
 659     
 660     // libproc handle
 661     private long p_ps_prochandle;
 662     
 663     // libthread.so's dlopen handle, thread agent
 664     // and function pointers
 665     private long libthread_db_handle;
 666     private long p_td_thragent_t;
 667     private long p_td_init;
 668     private long p_td_ta_new;
 669     private long p_td_ta_delete;
 670     private long p_td_ta_thr_iter;
 671     private long p_td_thr_get_info;
 672     private long p_td_ta_map_id2thr;
 673     private long p_td_thr_getgregs;
 674     
 675     // part of class sharing workaround
 676     private int classes_jsa_fd;
 677     private long p_file_map_header;
 678     
 679     private boolean attached = false;
 680     private boolean isCore;
 681     
 682     // for core files, we cache load object list, thread list, top frames etc.
 683     // for processes we cache load object list and sync. it during suspend.
 684     private List threadListCache;
 685     private List loadObjectCache;
 686     private Map  topFrameCache;      // Map<ThreadProxy, CFrame>
 687 }