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     // 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            public void doit(LinuxDebuggerLocal debugger) {
 325               debugger.attach0(pid);
 326               debugger.attached = true;
 327               debugger.isCore = false;
 328               findABIVersion();
 329            }
 330         }
 331 
 332         AttachTask task = new AttachTask();
 333         task.pid = processID;

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