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