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