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