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