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