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