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