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