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