1 /*
   2  * Copyright (c) 2002, 2013, 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.bsd;
  26 
  27 import java.io.*;
  28 import java.net.*;
  29 import java.util.*;
  30 import sun.jvm.hotspot.debugger.*;
  31 import sun.jvm.hotspot.debugger.x86.*;
  32 import sun.jvm.hotspot.debugger.cdbg.*;
  33 import sun.jvm.hotspot.utilities.*;
  34 import sun.jvm.hotspot.runtime.VM;
  35 import sun.jvm.hotspot.runtime.Threads;
  36 import sun.jvm.hotspot.runtime.JavaThread;
  37 import java.lang.reflect.*;
  38 
  39 /** <P> An implementation of the JVMDebugger interface. The basic debug
  40     facilities are implemented through ptrace interface in the JNI code
  41     (libsaproc.so). Library maps and symbol table management are done in
  42     JNI. </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 public class BsdDebuggerLocal extends DebuggerBase implements BsdDebugger {
  55     private boolean useGCC32ABI;
  56     private boolean attached;
  57     private long    p_ps_prochandle;      // native debugger handle
  58     private long    symbolicator;         // macosx symbolicator handle
  59     private long    task;                 // macosx task handle
  60     private boolean isCore;
  61     private boolean isDarwin;             // variant for bsd
  62 
  63     // CDebugger support
  64     private BsdCDebugger cdbg;
  65 
  66     // threadList and loadObjectList are filled by attach0 method
  67     private List threadList;
  68     private List loadObjectList;
  69 
  70     // called by native method lookupByAddress0
  71     private ClosestSymbol createClosestSymbol(String name, long offset) {
  72        return new ClosestSymbol(name, offset);
  73     }
  74 
  75     // called by native method attach0
  76     private LoadObject createLoadObject(String fileName, long textsize,
  77                                         long base) {
  78        File f = new File(fileName);
  79        Address baseAddr = newAddress(base);
  80        return new SharedObject(this, fileName, f.length(), baseAddr);
  81     }
  82 
  83     // native methods
  84 
  85     private native static void init0()
  86                                 throws DebuggerException;
  87     private native void attach0(int pid)
  88                                 throws DebuggerException;
  89     private native void attach0(String execName, String coreName)
  90                                 throws DebuggerException;
  91     private native void detach0()
  92                                 throws DebuggerException;
  93     private native long lookupByName0(String objectName, String symbol)
  94                                 throws DebuggerException;
  95     private native ClosestSymbol lookupByAddress0(long address)
  96                                 throws DebuggerException;
  97     private native long[] getThreadIntegerRegisterSet0(long unique_thread_id)
  98                                 throws DebuggerException;
  99     private native byte[] readBytesFromProcess0(long address, long numBytes)
 100                                 throws DebuggerException;
 101     public native static int  getAddressSize() ;
 102 
 103     // Note on Bsd threads are really processes. When target process is
 104     // attached by a serviceability agent thread, only that thread can do
 105     // ptrace operations on the target. This is because from kernel's point
 106     // view, other threads are just separate processes and they are not
 107     // attached to the target. When they attempt to make ptrace calls,
 108     // an ESRCH error will be returned as kernel believes target is not
 109     // being traced by the caller.
 110     // To work around the problem, we use a worker thread here to handle
 111     // all JNI functions that are making ptrace calls.
 112 
 113     interface WorkerThreadTask {
 114        public void doit(BsdDebuggerLocal debugger) throws DebuggerException;
 115     }
 116 
 117     class BsdDebuggerLocalWorkerThread extends Thread {
 118        BsdDebuggerLocal debugger;
 119        WorkerThreadTask task;
 120        DebuggerException lastException;
 121 
 122        public BsdDebuggerLocalWorkerThread(BsdDebuggerLocal debugger) {
 123          this.debugger = debugger;
 124          setDaemon(true);
 125        }
 126 
 127        public void run() {
 128           synchronized (workerThread) {
 129              for (;;) {
 130                 if (task != null) {
 131                    lastException = null;
 132                    try {
 133                       task.doit(debugger);
 134                    } catch (DebuggerException exp) {
 135                       lastException = exp;
 136                    }
 137                    task = null;
 138                    workerThread.notifyAll();
 139                 }
 140 
 141                 try {
 142                    workerThread.wait();
 143                 } catch (InterruptedException x) {}
 144              }
 145           }
 146        }
 147 
 148        public WorkerThreadTask execute(WorkerThreadTask task) throws DebuggerException {
 149           synchronized (workerThread) {
 150              this.task = task;
 151              workerThread.notifyAll();
 152              while (this.task != null) {
 153                 try {
 154                    workerThread.wait();
 155                 } catch (InterruptedException x) {}
 156              }
 157              if (lastException != null) {
 158                 throw new DebuggerException(lastException);
 159              } else {
 160                 return task;
 161              }
 162           }
 163        }
 164     }
 165 
 166     private BsdDebuggerLocalWorkerThread workerThread = null;
 167 
 168     //----------------------------------------------------------------------
 169     // Implementation of Debugger interface
 170     //
 171 
 172     /** <P> machDesc may not be null. </P>
 173 
 174     <P> useCache should be set to true if debugging is being done
 175     locally, and to false if the debugger is being created for the
 176     purpose of supporting remote debugging. </P> */
 177     public BsdDebuggerLocal(MachineDescription machDesc,
 178                               boolean useCache) throws DebuggerException {
 179         this.machDesc = machDesc;
 180         utils = new DebuggerUtilities(machDesc.getAddressSize(),
 181                                       machDesc.isBigEndian()) {
 182            public void checkAlignment(long address, long alignment) {
 183              // Need to override default checkAlignment because we need to
 184              // relax alignment constraints on Bsd/x86
 185              if ( (address % alignment != 0)
 186                 &&(alignment != 8 || address % 4 != 0)) {
 187                 throw new UnalignedAddressException(
 188                         "Trying to read at address: "
 189                       + addressValueToString(address)
 190                       + " with alignment: " + alignment,
 191                         address);
 192              }
 193            }
 194         };
 195 
 196         if (useCache) {
 197             // FIXME: re-test necessity of cache on Bsd, where data
 198             // fetching is faster
 199             // Cache portion of the remote process's address space.
 200             // Fetching data over the socket connection to dbx is slow.
 201             // Might be faster if we were using a binary protocol to talk to
 202             // dbx, but would have to test. For now, this cache works best
 203             // if it covers the entire heap of the remote process. FIXME: at
 204             // least should make this tunable from the outside, i.e., via
 205             // the UI. This is a cache of 4096 4K pages, or 16 MB. The page
 206             // size must be adjusted to be the hardware's page size.
 207             // (FIXME: should pick this up from the debugger.)
 208             if (getCPU().equals("ia64")) {
 209               initCache(16384, parseCacheNumPagesProperty(1024));
 210             } else {
 211               initCache(4096, parseCacheNumPagesProperty(4096));
 212             }
 213         }
 214 
 215         isDarwin = getOS().equals("darwin");
 216         workerThread = new BsdDebuggerLocalWorkerThread(this);
 217         workerThread.start();
 218     }
 219 
 220     /** From the Debugger interface via JVMDebugger */
 221     public boolean hasProcessList() throws DebuggerException {
 222         return false;
 223     }
 224 
 225     /** From the Debugger interface via JVMDebugger */
 226     public List getProcessList() throws DebuggerException {
 227         throw new DebuggerException("getProcessList not implemented yet");
 228     }
 229 
 230     private void checkAttached() throws DebuggerException {
 231         if (attached) {
 232             if (isCore) {
 233                 throw new DebuggerException("attached to a core dump already");
 234             } else {
 235                 throw new DebuggerException("attached to a process already");
 236             }
 237         }
 238     }
 239 
 240     private void requireAttach() {
 241         if (! attached) {
 242             throw new RuntimeException("not attached to a process or a core!");
 243         }
 244     }
 245 
 246     /* called from attach methods */
 247     private void findABIVersion() throws DebuggerException {
 248         String libjvmName = isDarwin ? "libjvm.dylib" : "libjvm.so";
 249         String libjvm_gName = isDarwin? "libjvm_g.dylib" : "libjvm_g.so";
 250         String javaThreadVt = isDarwin ? "_vt_10JavaThread" : "__vt_10JavaThread";
 251         if (lookupByName0(libjvmName, javaThreadVt) != 0 ||
 252             lookupByName0(libjvm_gName, javaThreadVt) != 0) {
 253             // old C++ ABI
 254             useGCC32ABI = false;
 255         } else {
 256             // new C++ ABI
 257             useGCC32ABI = true;
 258         }
 259     }
 260 
 261     /** From the Debugger interface via JVMDebugger */
 262     public synchronized void attach(int processID) throws DebuggerException {
 263         checkAttached();
 264         threadList = new ArrayList();
 265         loadObjectList = new ArrayList();
 266         class AttachTask implements WorkerThreadTask {
 267            int pid;
 268            public void doit(BsdDebuggerLocal debugger) {
 269               debugger.attach0(pid);
 270               debugger.attached = true;
 271               debugger.isCore = false;
 272               findABIVersion();
 273            }
 274         }
 275 
 276         AttachTask task = new AttachTask();
 277         task.pid = processID;
 278         workerThread.execute(task);
 279     }
 280 
 281     /** From the Debugger interface via JVMDebugger */
 282     public synchronized void attach(String execName, String coreName) {
 283         checkAttached();
 284         threadList = new ArrayList();
 285         loadObjectList = new ArrayList();
 286         attach0(execName, coreName);
 287         attached = true;
 288         isCore = true;
 289         findABIVersion();
 290     }
 291 
 292     /** From the Debugger interface via JVMDebugger */
 293     public synchronized boolean detach() {
 294         if (!attached) {
 295             return false;
 296         }
 297 
 298         threadList = null;
 299         loadObjectList = null;
 300 
 301         if (isCore) {
 302             detach0();
 303             attached = false;
 304             return true;
 305         } else {
 306             class DetachTask implements WorkerThreadTask {
 307                 boolean result = false;
 308 
 309                 public void doit(BsdDebuggerLocal debugger) {
 310                     debugger.detach0();
 311                     debugger.attached = false;
 312                     result = true;
 313                 }
 314             }
 315 
 316             DetachTask task = new DetachTask();
 317             workerThread.execute(task);
 318             return task.result;
 319         }
 320     }
 321 
 322     /** From the Debugger interface via JVMDebugger */
 323     public Address parseAddress(String addressString)
 324             throws NumberFormatException {
 325         long addr = utils.scanAddress(addressString);
 326         if (addr == 0) {
 327             return null;
 328         }
 329         return new BsdAddress(this, addr);
 330     }
 331 
 332     /** From the Debugger interface via JVMDebugger */
 333     public String getOS() {
 334         return PlatformInfo.getOS();
 335     }
 336 
 337     /** From the Debugger interface via JVMDebugger */
 338     public String getCPU() {
 339         return PlatformInfo.getCPU();
 340     }
 341 
 342     public boolean hasConsole() throws DebuggerException {
 343         return false;
 344     }
 345 
 346     public String consoleExecuteCommand(String cmd) throws DebuggerException {
 347         throw new DebuggerException("No debugger console available on Bsd");
 348     }
 349 
 350     public String getConsolePrompt() throws DebuggerException {
 351         return null;
 352     }
 353 
 354     /* called from lookup */
 355     private long handleGCC32ABI(long addr, String symbol) throws DebuggerException {
 356         if (useGCC32ABI && symbol.startsWith("_ZTV")) {
 357             return addr + (2 * machDesc.getAddressSize());
 358         } else {
 359             return addr;
 360         }
 361     }
 362 
 363     /** From the SymbolLookup interface via Debugger and JVMDebugger */
 364     public synchronized Address lookup(String objectName, String symbol) {
 365         requireAttach();
 366         if (!attached) {
 367             return null;
 368         }
 369 
 370         if (isCore) {
 371             // MacOSX symbol with "_" as leading
 372             long addr = lookupByName0(objectName, isDarwin ? "_" + symbol : symbol);
 373             return (addr == 0)? null : new BsdAddress(this, handleGCC32ABI(addr, symbol));
 374         } else {
 375             class LookupByNameTask implements WorkerThreadTask {
 376                 String objectName, symbol;
 377                 Address result;
 378 
 379                 public void doit(BsdDebuggerLocal debugger) {
 380                     long addr = debugger.lookupByName0(objectName, symbol);
 381                     result = (addr == 0 ? null : new BsdAddress(debugger, handleGCC32ABI(addr, symbol)));
 382                 }
 383             }
 384 
 385             LookupByNameTask task = new LookupByNameTask();
 386             task.objectName = objectName;
 387             task.symbol = symbol;
 388             workerThread.execute(task);
 389             return task.result;
 390         }
 391     }
 392 
 393     /** From the SymbolLookup interface via Debugger and JVMDebugger */
 394     public synchronized OopHandle lookupOop(String objectName, String symbol) {
 395         Address addr = lookup(objectName, symbol);
 396         if (addr == null) {
 397             return null;
 398         }
 399         return addr.addOffsetToAsOopHandle(0);
 400     }
 401 
 402     /** From the Debugger interface */
 403     public MachineDescription getMachineDescription() {
 404         return machDesc;
 405     }
 406 
 407     //----------------------------------------------------------------------
 408     // Implementation of ThreadAccess interface
 409     //
 410 
 411     /** From the ThreadAccess interface via Debugger and JVMDebugger */
 412     public ThreadProxy getThreadForIdentifierAddress(Address threadIdAddr, Address uniqueThreadIdAddr) {
 413         return new BsdThread(this, threadIdAddr, uniqueThreadIdAddr);
 414     }
 415 
 416     @Override
 417     public ThreadProxy getThreadForIdentifierAddress(Address addr) {
 418         throw new RuntimeException("unimplemented");
 419     }
 420 
 421     /** From the ThreadAccess interface via Debugger and JVMDebugger */
 422     public ThreadProxy getThreadForThreadId(long id) {
 423         return new BsdThread(this, id);
 424     }
 425 
 426     //----------------------------------------------------------------------
 427     // Internal routines (for implementation of BsdAddress).
 428     // These must not be called until the MachineDescription has been set up.
 429     //
 430 
 431     /** From the BsdDebugger interface */
 432     public String addressValueToString(long address) {
 433         return utils.addressValueToString(address);
 434     }
 435 
 436     /** From the BsdDebugger interface */
 437     public BsdAddress readAddress(long address)
 438             throws UnmappedAddressException, UnalignedAddressException {
 439         long value = readAddressValue(address);
 440         return (value == 0 ? null : new BsdAddress(this, value));
 441     }
 442     public BsdAddress readCompOopAddress(long address)
 443             throws UnmappedAddressException, UnalignedAddressException {
 444         long value = readCompOopAddressValue(address);
 445         return (value == 0 ? null : new BsdAddress(this, value));
 446     }
 447 
 448     public BsdAddress readCompKlassAddress(long address)
 449             throws UnmappedAddressException, UnalignedAddressException {
 450         long value = readCompKlassAddressValue(address);
 451         return (value == 0 ? null : new BsdAddress(this, value));
 452     }
 453 
 454     /** From the BsdDebugger interface */
 455     public BsdOopHandle readOopHandle(long address)
 456             throws UnmappedAddressException, UnalignedAddressException,
 457                 NotInHeapException {
 458         long value = readAddressValue(address);
 459         return (value == 0 ? null : new BsdOopHandle(this, value));
 460     }
 461     public BsdOopHandle readCompOopHandle(long address)
 462             throws UnmappedAddressException, UnalignedAddressException,
 463                 NotInHeapException {
 464         long value = readCompOopAddressValue(address);
 465         return (value == 0 ? null : new BsdOopHandle(this, value));
 466     }
 467 
 468     //----------------------------------------------------------------------
 469     // Thread context access
 470     //
 471 
 472     public synchronized long[] getThreadIntegerRegisterSet(long unique_thread_id)
 473                                             throws DebuggerException {
 474         requireAttach();
 475         if (isCore) {
 476             return getThreadIntegerRegisterSet0(unique_thread_id);
 477         } else {
 478             class GetThreadIntegerRegisterSetTask implements WorkerThreadTask {
 479                 long unique_thread_id;
 480                 long[] result;
 481                 public void doit(BsdDebuggerLocal debugger) {
 482                     result = debugger.getThreadIntegerRegisterSet0(unique_thread_id);
 483                 }
 484             }
 485 
 486             GetThreadIntegerRegisterSetTask task = new GetThreadIntegerRegisterSetTask();
 487             task.unique_thread_id = unique_thread_id;
 488             workerThread.execute(task);
 489             return task.result;
 490         }
 491     }
 492 
 493     /** Need to override this to relax alignment checks on x86. */
 494     public long readCInteger(long address, long numBytes, boolean isUnsigned)
 495         throws UnmappedAddressException, UnalignedAddressException {
 496         // Only slightly relaxed semantics -- this is a hack, but is
 497         // necessary on x86 where it seems the compiler is
 498         // putting some global 64-bit data on 32-bit boundaries
 499         if (numBytes == 8) {
 500             utils.checkAlignment(address, 4);
 501         } else {
 502             utils.checkAlignment(address, numBytes);
 503         }
 504         byte[] data = readBytes(address, numBytes);
 505         return utils.dataToCInteger(data, isUnsigned);
 506     }
 507 
 508     // Overridden from DebuggerBase because we need to relax alignment
 509     // constraints on x86
 510     public long readJLong(long address)
 511         throws UnmappedAddressException, UnalignedAddressException {
 512         utils.checkAlignment(address, jintSize);
 513         byte[] data = readBytes(address, jlongSize);
 514         return utils.dataToJLong(data, jlongSize);
 515     }
 516 
 517     //----------------------------------------------------------------------
 518     // Address access. Can not be package private, but should only be
 519     // accessed by the architecture-specific subpackages.
 520 
 521     /** From the BsdDebugger interface */
 522     public long getAddressValue(Address addr) {
 523       if (addr == null) return 0;
 524       return ((BsdAddress) addr).getValue();
 525     }
 526 
 527     /** From the BsdDebugger interface */
 528     public Address newAddress(long value) {
 529       if (value == 0) return null;
 530       return new BsdAddress(this, value);
 531     }
 532 
 533     /** From the BsdCDebugger interface */
 534     public List/*<ThreadProxy>*/ getThreadList() {
 535       requireAttach();
 536       return threadList;
 537     }
 538 
 539     /** From the BsdCDebugger interface */
 540     public List/*<LoadObject>*/ getLoadObjectList() {
 541       requireAttach();
 542       return loadObjectList;
 543     }
 544 
 545     /** From the BsdCDebugger interface */
 546     public synchronized ClosestSymbol lookup(long addr) {
 547        requireAttach();
 548        if (isCore) {
 549           return lookupByAddress0(addr);
 550        } else {
 551           class LookupByAddressTask implements WorkerThreadTask {
 552              long addr;
 553              ClosestSymbol result;
 554 
 555              public void doit(BsdDebuggerLocal debugger) {
 556                  result = debugger.lookupByAddress0(addr);
 557              }
 558           }
 559 
 560           LookupByAddressTask task = new LookupByAddressTask();
 561           task.addr = addr;
 562           workerThread.execute(task);
 563           return task.result;
 564        }
 565     }
 566 
 567     public CDebugger getCDebugger() {
 568       if (cdbg == null) {
 569          String cpu = getCPU();
 570          if (cpu.equals("ia64") ) {
 571             // IA-64 is not supported because of stack-walking issues
 572             return null;
 573          }
 574          cdbg = new BsdCDebugger(this);
 575       }
 576       return cdbg;
 577     }
 578 
 579     /** This reads bytes from the remote process. */
 580     public synchronized ReadResult readBytesFromProcess(long address,
 581             long numBytes) throws UnmappedAddressException, DebuggerException {
 582         requireAttach();
 583         if (isCore) {
 584             byte[] res = readBytesFromProcess0(address, numBytes);
 585             return (res != null)? new ReadResult(res) : new ReadResult(address);
 586         } else {
 587             class ReadBytesFromProcessTask implements WorkerThreadTask {
 588                 long address, numBytes;
 589                 ReadResult result;
 590                 public void doit(BsdDebuggerLocal debugger) {
 591                     byte[] res = debugger.readBytesFromProcess0(address, numBytes);
 592                     if (res != null)
 593                         result = new ReadResult(res);
 594                     else
 595                         result = new ReadResult(address);
 596                 }
 597             }
 598 
 599             ReadBytesFromProcessTask task = new ReadBytesFromProcessTask();
 600             task.address = address;
 601             task.numBytes = numBytes;
 602             workerThread.execute(task);
 603             return task.result;
 604         }
 605     }
 606 
 607     public void writeBytesToProcess(long address, long numBytes, byte[] data)
 608         throws UnmappedAddressException, DebuggerException {
 609         // FIXME
 610         throw new DebuggerException("Unimplemented");
 611     }
 612 
 613     /** this functions used for core file reading and called from native attach0,
 614         it returns an array of long integers as
 615         [thread_id, stack_start, stack_end, thread_id, stack_start, stack_end, ....] for
 616         all java threads recorded in Threads. Also adds the ThreadProxy to threadList */
 617     public long[] getJavaThreadsInfo() {
 618         requireAttach();
 619         Threads threads = VM.getVM().getThreads();
 620         int len = threads.getNumberOfThreads();
 621         long[] result = new long[len * 3];    // triple
 622         JavaThread t = threads.first();
 623         long beg, end;
 624         int i = 0;
 625         while (t != null) {
 626             end = t.getStackBaseValue();
 627             beg = end - t.getStackSize();
 628             BsdThread bsdt = (BsdThread)t.getThreadProxy();
 629             long uid = bsdt.getUniqueThreadId();
 630             if (threadList != null) threadList.add(bsdt);
 631             result[i] = uid;
 632             result[i + 1] = beg;
 633             result[i + 2] = end; 
 634             t = t.next();
 635             i += 3;
 636         }
 637         return result;
 638     }
 639 
 640     static {
 641         System.loadLibrary("saproc");
 642         init0();
 643     }
 644 }