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