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