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