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(int lwp_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 addr) {
 404         return new BsdThread(this, addr);
 405     }
 406 
 407     /** From the ThreadAccess interface via Debugger and JVMDebugger */
 408     public ThreadProxy getThreadForThreadId(long id) {
 409         return new BsdThread(this, id);
 410     }
 411 
 412     //----------------------------------------------------------------------
 413     // Internal routines (for implementation of BsdAddress).
 414     // These must not be called until the MachineDescription has been set up.
 415     //
 416 
 417     /** From the BsdDebugger interface */
 418     public String addressValueToString(long address) {
 419         return utils.addressValueToString(address);
 420     }
 421 
 422     /** From the BsdDebugger interface */
 423     public BsdAddress readAddress(long address)
 424             throws UnmappedAddressException, UnalignedAddressException {
 425         long value = readAddressValue(address);
 426         return (value == 0 ? null : new BsdAddress(this, value));
 427     }
 428     public BsdAddress readCompOopAddress(long address)
 429             throws UnmappedAddressException, UnalignedAddressException {
 430         long value = readCompOopAddressValue(address);
 431         return (value == 0 ? null : new BsdAddress(this, value));
 432     }
 433 
 434     public BsdAddress readCompKlassAddress(long address)
 435             throws UnmappedAddressException, UnalignedAddressException {
 436         long value = readCompKlassAddressValue(address);
 437         return (value == 0 ? null : new BsdAddress(this, value));
 438     }
 439 
 440     /** From the BsdDebugger interface */
 441     public BsdOopHandle readOopHandle(long address)
 442             throws UnmappedAddressException, UnalignedAddressException,
 443                 NotInHeapException {
 444         long value = readAddressValue(address);
 445         return (value == 0 ? null : new BsdOopHandle(this, value));
 446     }
 447     public BsdOopHandle readCompOopHandle(long address)
 448             throws UnmappedAddressException, UnalignedAddressException,
 449                 NotInHeapException {
 450         long value = readCompOopAddressValue(address);
 451         return (value == 0 ? null : new BsdOopHandle(this, value));
 452     }
 453 
 454     //----------------------------------------------------------------------
 455     // Thread context access
 456     //
 457 
 458     public synchronized long[] getThreadIntegerRegisterSet(int lwp_id)
 459                                             throws DebuggerException {
 460         requireAttach();
 461         if (isCore) {
 462             return getThreadIntegerRegisterSet0(lwp_id);
 463         } else {
 464             class GetThreadIntegerRegisterSetTask implements WorkerThreadTask {
 465                 int lwp_id;
 466                 long[] result;
 467                 public void doit(BsdDebuggerLocal debugger) {
 468                     result = debugger.getThreadIntegerRegisterSet0(lwp_id);
 469                 }
 470             }
 471 
 472             GetThreadIntegerRegisterSetTask task = new GetThreadIntegerRegisterSetTask();
 473             task.lwp_id = lwp_id;
 474             workerThread.execute(task);
 475             return task.result;
 476         }
 477     }
 478 
 479     /** Need to override this to relax alignment checks on x86. */
 480     public long readCInteger(long address, long numBytes, boolean isUnsigned)
 481         throws UnmappedAddressException, UnalignedAddressException {
 482         // Only slightly relaxed semantics -- this is a hack, but is
 483         // necessary on x86 where it seems the compiler is
 484         // putting some global 64-bit data on 32-bit boundaries
 485         if (numBytes == 8) {
 486             utils.checkAlignment(address, 4);
 487         } else {
 488             utils.checkAlignment(address, numBytes);
 489         }
 490         byte[] data = readBytes(address, numBytes);
 491         return utils.dataToCInteger(data, isUnsigned);
 492     }
 493 
 494     // Overridden from DebuggerBase because we need to relax alignment
 495     // constraints on x86
 496     public long readJLong(long address)
 497         throws UnmappedAddressException, UnalignedAddressException {
 498         utils.checkAlignment(address, jintSize);
 499         byte[] data = readBytes(address, jlongSize);
 500         return utils.dataToJLong(data, jlongSize);
 501     }
 502 
 503     //----------------------------------------------------------------------
 504     // Address access. Can not be package private, but should only be
 505     // accessed by the architecture-specific subpackages.
 506 
 507     /** From the BsdDebugger interface */
 508     public long getAddressValue(Address addr) {
 509       if (addr == null) return 0;
 510       return ((BsdAddress) addr).getValue();
 511     }
 512 
 513     /** From the BsdDebugger interface */
 514     public Address newAddress(long value) {
 515       if (value == 0) return null;
 516       return new BsdAddress(this, value);
 517     }
 518 
 519     /** From the BsdCDebugger interface */
 520     public List/*<ThreadProxy>*/ getThreadList() {
 521       requireAttach();
 522       return threadList;
 523     }
 524 
 525     /** From the BsdCDebugger interface */
 526     public List/*<LoadObject>*/ getLoadObjectList() {
 527       requireAttach();
 528       return loadObjectList;
 529     }
 530 
 531     /** From the BsdCDebugger interface */
 532     public synchronized ClosestSymbol lookup(long addr) {
 533        requireAttach();
 534        if (isCore) {
 535           return lookupByAddress0(addr);
 536        } else {
 537           class LookupByAddressTask implements WorkerThreadTask {
 538              long addr;
 539              ClosestSymbol result;
 540 
 541              public void doit(BsdDebuggerLocal debugger) {
 542                  result = debugger.lookupByAddress0(addr);
 543              }
 544           }
 545 
 546           LookupByAddressTask task = new LookupByAddressTask();
 547           task.addr = addr;
 548           workerThread.execute(task);
 549           return task.result;
 550        }
 551     }
 552 
 553     public CDebugger getCDebugger() {
 554       if (cdbg == null) {
 555          String cpu = getCPU();
 556          if (cpu.equals("ia64") ) {
 557             // IA-64 is not supported because of stack-walking issues
 558             return null;
 559          }
 560          cdbg = new BsdCDebugger(this);
 561       }
 562       return cdbg;
 563     }
 564 
 565     /** This reads bytes from the remote process. */
 566     public synchronized ReadResult readBytesFromProcess(long address,
 567             long numBytes) throws UnmappedAddressException, DebuggerException {
 568         requireAttach();
 569         if (isCore) {
 570             byte[] res = readBytesFromProcess0(address, numBytes);
 571             return (res != null)? new ReadResult(res) : new ReadResult(address);
 572         } else {
 573             class ReadBytesFromProcessTask implements WorkerThreadTask {
 574                 long address, numBytes;
 575                 ReadResult result;
 576                 public void doit(BsdDebuggerLocal debugger) {
 577                     byte[] res = debugger.readBytesFromProcess0(address, numBytes);
 578                     if (res != null)
 579                         result = new ReadResult(res);
 580                     else
 581                         result = new ReadResult(address);
 582                 }
 583             }
 584 
 585             ReadBytesFromProcessTask task = new ReadBytesFromProcessTask();
 586             task.address = address;
 587             task.numBytes = numBytes;
 588             workerThread.execute(task);
 589             return task.result;
 590         }
 591     }
 592 
 593     public void writeBytesToProcess(long address, long numBytes, byte[] data)
 594         throws UnmappedAddressException, DebuggerException {
 595         // FIXME
 596         throw new DebuggerException("Unimplemented");
 597     }
 598 
 599     static {
 600         System.loadLibrary("saproc");
 601         init0();
 602     }
 603 }