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