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