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 }