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 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, 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     // Returns -1 if the process is running in the host.
 288     public int getHostPID(int id) {
 289         return (nspidMap == null) ? -1 : nspidMap.get(id);
 290     }
 291 
 292     // Fill namespace PID map from procfs.
 293     // This method scans all tasks (/proc/<PID>/task) in the process.
 294     private void fillNSpidMap(Path proc) {
 295         Path task = Paths.get(proc.toString(), "task");
 296         try (var tasks = Files.list(task)) {
 297             nspidMap = tasks.filter(p -> !p.toString().startsWith("."))
 298                             .collect(Collectors.toMap(p -> Integer.valueOf(getNamespacePID(Paths.get(p.toString(), "status"))),
 299                                                       p -> Integer.valueOf(p.toFile().getName())));
 300         } catch (IOException e) {
 301             throw new UncheckedIOException(e);
 302         }
 303     }
 304 
 305     /** From the Debugger interface via JVMDebugger */
 306     public synchronized void attach(int processID) throws DebuggerException {
 307         checkAttached();
 308         threadList = new ArrayList();
 309         loadObjectList = new ArrayList();
 310 
 311         Path proc = Paths.get("/proc", Integer.toString(processID));
 312         int NSpid = getNamespacePID(Paths.get(proc.toString(), "status"));
 313         if (NSpid != processID) {
 314             // If PID different from namespace PID, we can assume the process
 315             // is running in the container.
 316             // So we need to set SA_ALTROOT environment variable that SA reads
 317             // binaries in the container.
 318             setSAAltRoot0(Paths.get(proc.toString(), "root").toString());
 319             fillNSpidMap(proc);
 320         }
 321 
 322         class AttachTask implements WorkerThreadTask {
 323            int pid;
 324            boolean isInContainer;
 325            public void doit(LinuxDebuggerLocal debugger) {
 326               debugger.attach0(pid, isInContainer);
 327               debugger.attached = true;
 328               debugger.isCore = false;
 329               findABIVersion();
 330            }
 331         }
 332 
 333         AttachTask task = new AttachTask();
 334         task.pid = processID;
 335         task.isInContainer = (processID != NSpid);
 336         workerThread.execute(task);
 337     }
 338 
 339     /** From the Debugger interface via JVMDebugger */
 340     public synchronized void attach(String execName, String coreName) {
 341         checkAttached();
 342         threadList = new ArrayList();
 343         loadObjectList = new ArrayList();
 344         attach0(execName, coreName);
 345         attached = true;
 346         isCore = true;
 347         findABIVersion();
 348     }
 349 
 350     /** From the Debugger interface via JVMDebugger */
 351     public synchronized boolean detach() {
 352         if (!attached) {
 353             return false;
 354         }
 355 
 356         threadList = null;
 357         loadObjectList = null;
 358 
 359         if (isCore) {
 360             detach0();
 361             attached = false;
 362             return true;
 363         } else {
 364             class DetachTask implements WorkerThreadTask {
 365                 boolean result = false;
 366 
 367                 public void doit(LinuxDebuggerLocal debugger) {
 368                     debugger.detach0();
 369                     debugger.attached = false;
 370                     result = true;
 371                 }
 372             }
 373 
 374             DetachTask task = new DetachTask();
 375             workerThread.execute(task);
 376             return task.result;
 377         }
 378     }
 379 
 380     /** From the Debugger interface via JVMDebugger */
 381     public Address parseAddress(String addressString)
 382             throws NumberFormatException {
 383         long addr = utils.scanAddress(addressString);
 384         if (addr == 0) {
 385             return null;
 386         }
 387         return new LinuxAddress(this, addr);
 388     }
 389 
 390     /** From the Debugger interface via JVMDebugger */
 391     public String getOS() {
 392         return PlatformInfo.getOS();
 393     }
 394 
 395     /** From the Debugger interface via JVMDebugger */
 396     public String getCPU() {
 397         return PlatformInfo.getCPU();
 398     }
 399 
 400     public boolean hasConsole() throws DebuggerException {
 401         return false;
 402     }
 403 
 404     public String consoleExecuteCommand(String cmd) throws DebuggerException {
 405         throw new DebuggerException("No debugger console available on Linux");
 406     }
 407 
 408     public String getConsolePrompt() throws DebuggerException {
 409         return null;
 410     }
 411 
 412     /* called from lookup */
 413     private long handleGCC32ABI(long addr, String symbol) throws DebuggerException {
 414         if (useGCC32ABI && symbol.startsWith("_ZTV")) {
 415             return addr + (2 * machDesc.getAddressSize());
 416         } else {
 417             return addr;
 418         }
 419     }
 420 
 421     /** From the SymbolLookup interface via Debugger and JVMDebugger */
 422     public synchronized Address lookup(String objectName, String symbol) {
 423         requireAttach();
 424         if (!attached) {
 425             return null;
 426         }
 427 
 428         if (isCore) {
 429             long addr = lookupByName0(objectName, symbol);
 430             return (addr == 0)? null : new LinuxAddress(this, handleGCC32ABI(addr, symbol));
 431         } else {
 432             class LookupByNameTask implements WorkerThreadTask {
 433                 String objectName, symbol;
 434                 Address result;
 435 
 436                 public void doit(LinuxDebuggerLocal debugger) {
 437                     long addr = debugger.lookupByName0(objectName, symbol);
 438                     result = (addr == 0 ? null : new LinuxAddress(debugger, handleGCC32ABI(addr, symbol)));
 439                 }
 440             }
 441 
 442             LookupByNameTask task = new LookupByNameTask();
 443             task.objectName = objectName;
 444             task.symbol = symbol;
 445             workerThread.execute(task);
 446             return task.result;
 447         }
 448     }
 449 
 450     /** From the SymbolLookup interface via Debugger and JVMDebugger */
 451     public synchronized OopHandle lookupOop(String objectName, String symbol) {
 452         Address addr = lookup(objectName, symbol);
 453         if (addr == null) {
 454             return null;
 455         }
 456         return addr.addOffsetToAsOopHandle(0);
 457     }
 458 
 459     /** From the Debugger interface */
 460     public MachineDescription getMachineDescription() {
 461         return machDesc;
 462     }
 463 
 464     //----------------------------------------------------------------------
 465     // Implementation of ThreadAccess interface
 466     //
 467 
 468     /** From the ThreadAccess interface via Debugger and JVMDebugger */
 469     public ThreadProxy getThreadForIdentifierAddress(Address addr) {
 470         return new LinuxThread(this, addr);
 471     }
 472 
 473     /** From the ThreadAccess interface via Debugger and JVMDebugger */
 474     public ThreadProxy getThreadForThreadId(long id) {
 475         return new LinuxThread(this, id);
 476     }
 477 
 478     //----------------------------------------------------------------------
 479     // Internal routines (for implementation of LinuxAddress).
 480     // These must not be called until the MachineDescription has been set up.
 481     //
 482 
 483     /** From the LinuxDebugger interface */
 484     public String addressValueToString(long address) {
 485         return utils.addressValueToString(address);
 486     }
 487 
 488     /** From the LinuxDebugger interface */
 489     public LinuxAddress readAddress(long address)
 490             throws UnmappedAddressException, UnalignedAddressException {
 491         long value = readAddressValue(address);
 492         return (value == 0 ? null : new LinuxAddress(this, value));
 493     }
 494     public LinuxAddress readCompOopAddress(long address)
 495             throws UnmappedAddressException, UnalignedAddressException {
 496         long value = readCompOopAddressValue(address);
 497         return (value == 0 ? null : new LinuxAddress(this, value));
 498     }
 499 
 500     public LinuxAddress readCompKlassAddress(long address)
 501             throws UnmappedAddressException, UnalignedAddressException {
 502         long value = readCompKlassAddressValue(address);
 503         return (value == 0 ? null : new LinuxAddress(this, value));
 504     }
 505 
 506     /** From the LinuxDebugger interface */
 507     public LinuxOopHandle readOopHandle(long address)
 508             throws UnmappedAddressException, UnalignedAddressException,
 509                 NotInHeapException {
 510         long value = readAddressValue(address);
 511         return (value == 0 ? null : new LinuxOopHandle(this, value));
 512     }
 513     public LinuxOopHandle readCompOopHandle(long address)
 514             throws UnmappedAddressException, UnalignedAddressException,
 515                 NotInHeapException {
 516         long value = readCompOopAddressValue(address);
 517         return (value == 0 ? null : new LinuxOopHandle(this, value));
 518     }
 519 
 520     //----------------------------------------------------------------------
 521     // Thread context access
 522     //
 523 
 524     public synchronized long[] getThreadIntegerRegisterSet(int lwp_id)
 525                                             throws DebuggerException {
 526         requireAttach();
 527         if (isCore) {
 528             return getThreadIntegerRegisterSet0(lwp_id);
 529         } else {
 530             class GetThreadIntegerRegisterSetTask implements WorkerThreadTask {
 531                 int lwp_id;
 532                 long[] result;
 533                 public void doit(LinuxDebuggerLocal debugger) {
 534                     result = debugger.getThreadIntegerRegisterSet0(lwp_id);
 535                 }
 536             }
 537 
 538             GetThreadIntegerRegisterSetTask task = new GetThreadIntegerRegisterSetTask();
 539             task.lwp_id = lwp_id;
 540             workerThread.execute(task);
 541             return task.result;
 542         }
 543     }
 544 
 545     /** Need to override this to relax alignment checks on x86. */
 546     public long readCInteger(long address, long numBytes, boolean isUnsigned)
 547         throws UnmappedAddressException, UnalignedAddressException {
 548         // Only slightly relaxed semantics -- this is a hack, but is
 549         // necessary on x86 where it seems the compiler is
 550         // putting some global 64-bit data on 32-bit boundaries
 551         if (numBytes == 8) {
 552             utils.checkAlignment(address, 4);
 553         } else {
 554             utils.checkAlignment(address, numBytes);
 555         }
 556         byte[] data = readBytes(address, numBytes);
 557         return utils.dataToCInteger(data, isUnsigned);
 558     }
 559 
 560     // Overridden from DebuggerBase because we need to relax alignment
 561     // constraints on x86
 562     public long readJLong(long address)
 563         throws UnmappedAddressException, UnalignedAddressException {
 564         utils.checkAlignment(address, jintSize);
 565         byte[] data = readBytes(address, jlongSize);
 566         return utils.dataToJLong(data, jlongSize);
 567     }
 568 
 569     //----------------------------------------------------------------------
 570     // Address access. Can not be package private, but should only be
 571     // accessed by the architecture-specific subpackages.
 572 
 573     /** From the LinuxDebugger interface */
 574     public long getAddressValue(Address addr) {
 575       if (addr == null) return 0;
 576       return ((LinuxAddress) addr).getValue();
 577     }
 578 
 579     /** From the LinuxDebugger interface */
 580     public Address newAddress(long value) {
 581       if (value == 0) return null;
 582       return new LinuxAddress(this, value);
 583     }
 584 
 585     /** From the LinuxCDebugger interface */
 586     public List/*<ThreadProxy>*/ getThreadList() {
 587       requireAttach();
 588       return threadList;
 589     }
 590 
 591     /** From the LinuxCDebugger interface */
 592     public List/*<LoadObject>*/ getLoadObjectList() {
 593       requireAttach();
 594       return loadObjectList;
 595     }
 596 
 597     /** From the LinuxCDebugger interface */
 598     public synchronized ClosestSymbol lookup(long addr) {
 599        requireAttach();
 600        if (isCore) {
 601           return lookupByAddress0(addr);
 602        } else {
 603           class LookupByAddressTask implements WorkerThreadTask {
 604              long addr;
 605              ClosestSymbol result;
 606 
 607              public void doit(LinuxDebuggerLocal debugger) {
 608                  result = debugger.lookupByAddress0(addr);
 609              }
 610           }
 611 
 612           LookupByAddressTask task = new LookupByAddressTask();
 613           task.addr = addr;
 614           workerThread.execute(task);
 615           return task.result;
 616        }
 617     }
 618 
 619     public CDebugger getCDebugger() {
 620       if (cdbg == null) {
 621          cdbg = new LinuxCDebugger(this);
 622       }
 623       return cdbg;
 624     }
 625 
 626     /** This reads bytes from the remote process. */
 627     public synchronized ReadResult readBytesFromProcess(long address,
 628             long numBytes) throws UnmappedAddressException, DebuggerException {
 629         requireAttach();
 630         if (isCore) {
 631             byte[] res = readBytesFromProcess0(address, numBytes);
 632             return (res != null)? new ReadResult(res) : new ReadResult(address);
 633         } else {
 634             class ReadBytesFromProcessTask implements WorkerThreadTask {
 635                 long address, numBytes;
 636                 ReadResult result;
 637                 public void doit(LinuxDebuggerLocal debugger) {
 638                     byte[] res = debugger.readBytesFromProcess0(address, numBytes);
 639                     if (res != null)
 640                         result = new ReadResult(res);
 641                     else
 642                         result = new ReadResult(address);
 643                 }
 644             }
 645 
 646             ReadBytesFromProcessTask task = new ReadBytesFromProcessTask();
 647             task.address = address;
 648             task.numBytes = numBytes;
 649             workerThread.execute(task);
 650             return task.result;
 651         }
 652     }
 653 
 654     public void writeBytesToProcess(long address, long numBytes, byte[] data)
 655         throws UnmappedAddressException, DebuggerException {
 656         // FIXME
 657         throw new DebuggerException("Unimplemented");
 658     }
 659 
 660     static {
 661         System.loadLibrary("saproc");
 662         init0();
 663     }
 664 }
--- EOF ---