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