1 /* 2 * Copyright 2002-2004 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 20 * CA 95054 USA or visit www.sun.com if you need additional information or 21 * have any questions. 22 * 23 */ 24 25 package sun.jvm.hotspot.debugger.proc; 26 27 import java.io.*; 28 import java.net.*; 29 import java.util.*; 30 import sun.jvm.hotspot.debugger.*; 31 import sun.jvm.hotspot.debugger.cdbg.*; 32 import sun.jvm.hotspot.debugger.proc.amd64.*; 33 import sun.jvm.hotspot.debugger.proc.sparc.*; 34 import sun.jvm.hotspot.debugger.proc.x86.*; 35 import sun.jvm.hotspot.debugger.amd64.*; 36 import sun.jvm.hotspot.debugger.sparc.*; 37 import sun.jvm.hotspot.debugger.x86.*; 38 import sun.jvm.hotspot.utilities.*; 39 40 /** <P> An implementation of the JVMDebugger interface which sits on 41 * top of proc and relies on the SA's proc import module for 42 * communication with the debugger. </P> 43 * 44 * <P> <B>NOTE</B> that since we have the notion of fetching "Java 45 * primitive types" from the remote process (which might have 46 * different sizes than we expect) we have a bootstrapping 47 * problem. We need to know the sizes of these types before we can 48 * fetch them. The current implementation solves this problem by 49 * requiring that it be configured with these type sizes before they 50 * can be fetched. The readJ(Type) routines here will throw a 51 * RuntimeException if they are called before the debugger is 52 * configured with the Java primitive type sizes. </P> 53 */ 54 55 public class ProcDebuggerLocal extends DebuggerBase implements ProcDebugger { 56 57 58 protected static final int cacheSize = 16 * 1024 * 1024; // 16 MB 59 60 //------------------------------------------------------------------------ 61 // Implementation of Debugger interface 62 // 63 64 /** <P> machDesc may be null if it couldn't be determined yet; i.e., 65 * if we're on SPARC, we need to ask the remote process whether 66 * we're in 32- or 64-bit mode. </P> 67 * 68 * <P> useCache should be set to true if debugging is being done 69 * locally, and to false if the debugger is being created for the 70 * purpose of supporting remote debugging. </P> */ 71 public ProcDebuggerLocal(MachineDescription machDesc, boolean useCache) { 72 this.machDesc = machDesc; 73 int cacheNumPages; 74 int cachePageSize; 75 76 final String cpu = PlatformInfo.getCPU(); 77 if (cpu.equals("sparc")) { 78 threadFactory = new ProcSPARCThreadFactory(this); 79 pcRegIndex = SPARCThreadContext.R_PC; 80 fpRegIndex = SPARCThreadContext.R_I6; 81 } else if (cpu.equals("x86")) { 82 threadFactory = new ProcX86ThreadFactory(this); 83 pcRegIndex = X86ThreadContext.EIP; 84 fpRegIndex = X86ThreadContext.EBP; 85 unalignedAccessesOkay = true; 86 } else if (cpu.equals("amd64")) { 87 threadFactory = new ProcAMD64ThreadFactory(this); 88 pcRegIndex = AMD64ThreadContext.RIP; 89 fpRegIndex = AMD64ThreadContext.RBP; 90 } else { 91 throw new RuntimeException("Thread access for CPU architecture " + PlatformInfo.getCPU() + " not yet supported"); 92 } 93 if (useCache) { 94 // Cache portion of the remote process's address space. 95 // For now, this cache works best if it covers the entire 96 // heap of the remote process. FIXME: at least should make this 97 // tunable from the outside, i.e., via the UI. This is a 16 MB 98 // cache divided on SPARC into 2048 8K pages and on x86 into 99 // 4096 4K pages; the page size must be adjusted to be the OS's 100 // page size. 101 102 cachePageSize = getPageSize(); 103 cacheNumPages = parseCacheNumPagesProperty(cacheSize / cachePageSize); 104 initCache(cachePageSize, cacheNumPages); 105 } 106 107 resetNativePointers(); 108 clearCacheFields(); 109 } 110 111 /** FIXME: implement this with a Runtime.exec() of ps followed by 112 * parsing of its output */ 113 public boolean hasProcessList() throws DebuggerException { 114 return false; 115 } 116 117 public List getProcessList() throws DebuggerException { 118 throw new DebuggerException("Not yet supported"); 119 } 120 121 122 /** From the Debugger interface via JVMDebugger */ 123 public synchronized void attach(int processID) throws DebuggerException { 124 checkAttached(); 125 isCore = false; 126 attach0(new Integer(processID).toString()); 127 attached = true; 128 suspended = true; 129 } 130 131 /** From the Debugger interface via JVMDebugger */ 132 public synchronized void attach 133 (String executableName, String coreFileName) throws DebuggerException { 134 checkAttached(); 135 isCore = true; 136 topFrameCache = new HashMap(); 137 attach0(executableName, coreFileName); 138 attached = true; 139 suspended = true; 140 } 141 142 /** From the Debugger interface via JVMDebugger */ 143 public synchronized boolean detach() { 144 if (! attached) { 145 return false; 146 } 147 148 try { 149 if (p_ps_prochandle == 0L) { 150 return false; 151 } 152 detach0(); 153 clearCache(); 154 return true; 155 } catch (Exception e) { 156 e.printStackTrace(); 157 return false; 158 } finally { 159 resetNativePointers(); 160 clearCacheFields(); 161 suspended = false; 162 attached = false; 163 } 164 } 165 166 public synchronized void suspend() throws DebuggerException { 167 requireAttach(); 168 if (suspended) { 169 throw new DebuggerException("Process already suspended"); 170 } 171 suspend0(); 172 suspended = true; 173 enableCache(); 174 reresolveLoadObjects(); 175 } 176 177 public synchronized void resume() throws DebuggerException { 178 requireAttach(); 179 if (!suspended) { 180 throw new DebuggerException("Process not suspended"); 181 } 182 resume0(); 183 disableCache(); 184 suspended = false; 185 } 186 187 public synchronized boolean isSuspended() throws DebuggerException { 188 requireAttach(); 189 return suspended; 190 } 191 192 /** From the Debugger interface via JVMDebugger */ 193 public Address parseAddress(String addressString) throws NumberFormatException { 194 long addr = utils.scanAddress(addressString); 195 if (addr == 0) { 196 return null; 197 } 198 return new ProcAddress(this, addr); 199 } 200 201 /** From the Debugger interface via JVMDebugger */ 202 public String getOS() { 203 return PlatformInfo.getOS(); 204 } 205 206 /** From the Debugger interface via JVMDebugger */ 207 public String getCPU() { 208 return PlatformInfo.getCPU(); 209 } 210 211 public boolean hasConsole() throws DebuggerException { 212 return false; 213 } 214 215 public String consoleExecuteCommand(String cmd) throws DebuggerException { 216 throw new DebuggerException("Can't execute console commands"); 217 } 218 219 public String getConsolePrompt() throws DebuggerException { 220 return ""; 221 } 222 223 public CDebugger getCDebugger() throws DebuggerException { 224 if (cdbg == null) { 225 cdbg = new ProcCDebugger(this); 226 } 227 return cdbg; 228 } 229 230 /** From the SymbolLookup interface via Debugger and JVMDebugger */ 231 public synchronized Address lookup(String objectName, String symbol) { 232 requireAttach(); 233 long addr = lookupByName0(objectName, symbol); 234 if (addr == 0) { 235 return null; 236 } 237 return new ProcAddress(this, addr); 238 } 239 240 /** From the SymbolLookup interface via Debugger and JVMDebugger */ 241 public synchronized OopHandle lookupOop(String objectName, String symbol) { 242 Address addr = lookup(objectName, symbol); 243 if (addr == null) { 244 return null; 245 } 246 return addr.addOffsetToAsOopHandle(0); 247 } 248 249 /** From the ProcDebugger interface */ 250 public MachineDescription getMachineDescription() { 251 return machDesc; 252 } 253 254 /** Internal routine supporting lazy setting of MachineDescription, 255 * since on SPARC we will need to query the remote process to ask 256 * it what its data model is (32- or 64-bit). 257 */ 258 259 public void setMachineDescription(MachineDescription machDesc) { 260 this.machDesc = machDesc; 261 setBigEndian(machDesc.isBigEndian()); 262 utils = new DebuggerUtilities(machDesc.getAddressSize(), machDesc.isBigEndian()); 263 } 264 265 public synchronized int getRemoteProcessAddressSize() 266 throws DebuggerException { 267 requireAttach(); 268 return getRemoteProcessAddressSize0(); 269 } 270 271 //-------------------------------------------------------------------------------- 272 // Implementation of ThreadAccess interface 273 // 274 275 /** From the ThreadAccess interface via Debugger and JVMDebugger */ 276 public ThreadProxy getThreadForIdentifierAddress(Address addr) { 277 return threadFactory.createThreadWrapper(addr); 278 } 279 280 public ThreadProxy getThreadForThreadId(long id) { 281 return threadFactory.createThreadWrapper(id); 282 } 283 284 //---------------------------------------------------------------------- 285 // Overridden from DebuggerBase because we need to relax alignment 286 // constraints on x86 287 288 public long readJLong(long address) 289 throws UnmappedAddressException, UnalignedAddressException { 290 checkJavaConfigured(); 291 // FIXME: allow this to be configurable. Undesirable to add a 292 // dependency on the runtime package here, though, since this 293 // package should be strictly underneath it. 294 if (unalignedAccessesOkay) { 295 utils.checkAlignment(address, jintSize); 296 } else { 297 utils.checkAlignment(address, jlongSize); 298 } 299 byte[] data = readBytes(address, jlongSize); 300 return utils.dataToJLong(data, jlongSize); 301 } 302 303 //-------------------------------------------------------------------------------- 304 // Internal routines (for implementation of ProcAddress). 305 // These must not be called until the MachineDescription has been set up. 306 // 307 308 /** From the ProcDebugger interface */ 309 public String addressValueToString(long address) { 310 return utils.addressValueToString(address); 311 } 312 313 /** Need to override this to relax alignment checks on Solaris/x86. */ 314 public long readCInteger(long address, long numBytes, boolean isUnsigned) 315 throws UnmappedAddressException, UnalignedAddressException { 316 checkConfigured(); 317 if (!unalignedAccessesOkay) { 318 utils.checkAlignment(address, numBytes); 319 } else { 320 // Only slightly relaxed semantics -- this is a hack, but is 321 // necessary on Solaris/x86 where it seems the compiler is 322 // putting some global 64-bit data on 32-bit boundaries 323 if (numBytes == 8) { 324 utils.checkAlignment(address, 4); 325 } else { 326 utils.checkAlignment(address, numBytes); 327 } 328 } 329 byte[] data = readBytes(address, numBytes); 330 return utils.dataToCInteger(data, isUnsigned); 331 } 332 333 /** From the ProcDebugger interface */ 334 public ProcAddress readAddress(long address) 335 throws UnmappedAddressException, UnalignedAddressException { 336 long value = readAddressValue(address); 337 return (value == 0 ? null : new ProcAddress(this, value)); 338 } 339 340 /** From the ProcDebugger interface */ 341 public ProcOopHandle readOopHandle(long address) 342 throws UnmappedAddressException, UnalignedAddressException, NotInHeapException { 343 long value = readAddressValue(address); 344 return (value == 0 ? null : new ProcOopHandle(this, value)); 345 } 346 347 public void writeBytesToProcess(long address, long numBytes, byte[] data) 348 throws UnmappedAddressException, DebuggerException { 349 if (isCore) { 350 throw new DebuggerException("Attached to a core file!"); 351 } 352 writeBytesToProcess0(address, numBytes, data); 353 } 354 355 public synchronized ReadResult readBytesFromProcess(long address, long numBytes) 356 throws DebuggerException { 357 requireAttach(); 358 byte[] res = readBytesFromProcess0(address, numBytes); 359 if(res != null) 360 return new ReadResult(res); 361 else 362 return new ReadResult(address); 363 } 364 365 protected int getPageSize() { 366 int pagesize = getPageSize0(); 367 if (pagesize == -1) { 368 // return the hard coded default value. 369 pagesize = (PlatformInfo.getCPU().equals("x86"))? 4096 : 8192; 370 } 371 return pagesize; 372 } 373 374 //-------------------------------------------------------------------------------- 375 // Thread context access. Can not be package private, but should 376 // only be accessed by the architecture-specific subpackages. 377 378 /** From the ProcDebugger interface. May have to redefine this later. */ 379 public synchronized long[] getThreadIntegerRegisterSet(int tid) { 380 requireAttach(); 381 return getThreadIntegerRegisterSet0(tid); 382 } 383 384 //-------------------------------------------------------------------------------- 385 // Address access. Can not be package private, but should only be 386 // accessed by the architecture-specific subpackages. 387 388 /** From the ProcDebugger interface */ 389 public long getAddressValue(Address addr) { 390 if (addr == null) return 0; 391 return ((ProcAddress) addr).getValue(); 392 } 393 394 /** From the ProcDebugger interface */ 395 public Address newAddress(long value) { 396 if (value == 0) return null; 397 return new ProcAddress(this, value); 398 } 399 400 /** From the ProcDebugger interface */ 401 public synchronized List getThreadList() throws DebuggerException { 402 requireAttach(); 403 List res = null; 404 if (isCore && (threadListCache != null)) { 405 res = threadListCache; 406 } else { 407 res = new ArrayList(); 408 fillThreadList0(res); 409 if (isCore) { 410 threadListCache = res; 411 } 412 } 413 return res; 414 } 415 416 /** From the ProcDebugger interface */ 417 public synchronized List getLoadObjectList() throws DebuggerException { 418 requireAttach(); 419 if (!suspended) { 420 throw new DebuggerException("Process not suspended"); 421 } 422 423 if (loadObjectCache == null) { 424 updateLoadObjectCache(); 425 } 426 return loadObjectCache; 427 } 428 429 /** From the ProcDebugger interface */ 430 public synchronized CFrame topFrameForThread(ThreadProxy thread) 431 throws DebuggerException { 432 requireAttach(); 433 CFrame res = null; 434 if (isCore && ((res = (CFrame) topFrameCache.get(thread)) != null)) { 435 return res; 436 } else { 437 ThreadContext context = thread.getContext(); 438 int numRegs = context.getNumRegisters(); 439 long[] regs = new long[numRegs]; 440 for (int i = 0; i < numRegs; i++) { 441 regs[i] = context.getRegister(i); 442 } 443 res = fillCFrameList0(regs); 444 if (isCore) { 445 topFrameCache.put(thread, res); 446 } 447 return res; 448 } 449 } 450 451 /** From the ProcDebugger interface */ 452 public synchronized ClosestSymbol lookup(long address) { 453 requireAttach(); 454 return lookupByAddress0(address); 455 } 456 457 /** From the ProcDebugger interface */ 458 public String demangle(String name) { 459 return demangle0(name); 460 } 461 462 //------------- Internals only below this point -------------------- 463 // 464 // 465 466 private void updateLoadObjectCache() { 467 List res = new ArrayList(); 468 nameToDsoMap = new HashMap(); 469 fillLoadObjectList0(res); 470 loadObjectCache = sortLoadObjects(res); 471 } 472 473 // sort load objects by base address 474 private static List sortLoadObjects(List in) { 475 // sort the list by base address 476 Object[] arr = in.toArray(); 477 Arrays.sort(arr, loadObjectComparator); 478 return Arrays.asList(arr); 479 } 480 481 private long lookupByName(String objectName, String symbolName) 482 throws DebuggerException { 483 // NOTE: this assumes that process is suspended (which is probably 484 // necessary assumption given that DSOs can be loaded/unloaded as 485 // process runs). Should update documentation. 486 if (nameToDsoMap == null) { 487 getLoadObjectList(); 488 } 489 SharedObject dso = (SharedObject) nameToDsoMap.get(objectName); 490 // The DSO can be null because we use this to search through known 491 // DSOs in HotSpotTypeDataBase (for example) 492 if (dso != null) { 493 ProcAddress addr = (ProcAddress) dso.lookupSymbol(symbolName); 494 if (addr != null) { 495 return addr.getValue(); 496 } 497 } 498 return 0; 499 } 500 501 private SharedObject findDSOByName(String fullPathName) { 502 if (loadObjectCache == null) 503 return null; 504 for (Iterator iter = loadObjectCache.iterator(); iter.hasNext(); ) { 505 SharedObject dso = (SharedObject) iter.next(); 506 if (dso.getName().equals(fullPathName)) { 507 return dso; 508 } 509 } 510 return null; 511 } 512 513 private void reresolveLoadObjects() throws DebuggerException { 514 if (loadObjectCache == null) { 515 return; 516 } 517 updateLoadObjectCache(); 518 } 519 520 521 private void checkAttached() { 522 if (attached) { 523 if (isCore) { 524 throw new DebuggerException("already attached to a core file!"); 525 } else { 526 throw new DebuggerException("already attached to a process!"); 527 } 528 } 529 } 530 531 private void requireAttach() { 532 if (! attached) { 533 throw new RuntimeException("not attached to a process or core file!"); 534 } 535 } 536 537 private void clearCacheFields() { 538 loadObjectCache = null; 539 nameToDsoMap = null; 540 threadListCache = null; 541 topFrameCache = null; 542 } 543 544 private void resetNativePointers() { 545 p_ps_prochandle = 0L; 546 547 // reset thread_db pointers 548 libthread_db_handle = 0L; 549 p_td_thragent_t = 0L; 550 p_td_init = 0L; 551 p_td_ta_new = 0L; 552 p_td_ta_delete = 0L; 553 p_td_ta_thr_iter = 0L; 554 p_td_thr_get_info = 0L; 555 p_td_ta_map_id2thr = 0L; 556 p_td_thr_getgregs = 0L; 557 558 // part of class sharing workaround 559 classes_jsa_fd = -1; 560 p_file_map_header = 0L; 561 } 562 563 // native methods and native helpers 564 565 // attach, detach 566 private native void attach0(String pid) throws DebuggerException; 567 private native void attach0(String executableFile, String coreFileName) throws DebuggerException; 568 private native void detach0() throws DebuggerException; 569 570 // address size, page size 571 private native int getRemoteProcessAddressSize0() throws DebuggerException; 572 private native int getPageSize0() throws DebuggerException; 573 574 // threads, stacks 575 private native long[] getThreadIntegerRegisterSet0(long tid) throws DebuggerException; 576 private native void fillThreadList0(List l) throws DebuggerException; 577 578 // fills stack frame list given reg set of the top frame and top frame 579 private native ProcCFrame fillCFrameList0(long[] regs) throws DebuggerException; 580 581 // helper called by fillCFrameList0 582 private ProcCFrame createSenderFrame(ProcCFrame f, long pc, long fp) { 583 ProcCFrame sender = new ProcCFrame(this, newAddress(pc), newAddress(fp)); 584 if (f != null) { 585 f.setSender(sender); 586 } 587 return sender; 588 } 589 590 // shared objects 591 private native void fillLoadObjectList0(List l) throws DebuggerException; 592 593 // helper called by fillLoadObjectList0 594 private LoadObject createLoadObject(String fileName, long textsize, long base) { 595 File f = new File(fileName); 596 Address baseAddr = newAddress(base); 597 SharedObject res = findDSOByName(fileName); 598 if (res != null) { 599 // already in cache. just change the base, if needed 600 Address oldBase = res.getBase(); 601 if (! baseAddr.equals(oldBase)) { 602 res.setBase(baseAddr); 603 } 604 } else { 605 // new shared object. 606 res = new SharedObject(this, fileName, f.length(), baseAddr); 607 } 608 nameToDsoMap.put(f.getName(), res); 609 return res; 610 } 611 612 // symbol-to-pc 613 private native long lookupByName0(String objectName, String symbolName) throws DebuggerException; 614 private native ClosestSymbol lookupByAddress0(long address) throws DebuggerException; 615 616 // helper called by lookupByAddress0 617 private ClosestSymbol createClosestSymbol(String name, long offset) { 618 return new ClosestSymbol(name, offset); 619 } 620 621 // process read/write 622 private native byte[] readBytesFromProcess0(long address, long numBytes) throws DebuggerException; 623 private native void writeBytesToProcess0(long address, long numBytes, byte[] data) throws DebuggerException; 624 625 // process control 626 private native void suspend0() throws DebuggerException; 627 private native void resume0() throws DebuggerException; 628 629 // demangle a C++ name 630 private native String demangle0(String name); 631 632 // init JNI ids to fields, methods 633 private native static void initIDs() throws DebuggerException; 634 private static LoadObjectComparator loadObjectComparator; 635 636 static { 637 System.loadLibrary("saproc"); 638 initIDs(); 639 loadObjectComparator = new LoadObjectComparator(); 640 } 641 642 private boolean unalignedAccessesOkay; 643 private ProcThreadFactory threadFactory; 644 645 // indices of PC and FP registers in gregset 646 private int pcRegIndex; 647 private int fpRegIndex; 648 649 // Symbol lookup support 650 // This is a map of library names to DSOs 651 private Map nameToDsoMap; // Map<String, SharedObject> 652 653 // C/C++ debugging support 654 private List/*<LoadObject>*/ loadObjects; 655 private CDebugger cdbg; 656 657 // ProcessControl support 658 private boolean suspended; 659 660 // libproc handle 661 private long p_ps_prochandle; 662 663 // libthread.so's dlopen handle, thread agent 664 // and function pointers 665 private long libthread_db_handle; 666 private long p_td_thragent_t; 667 private long p_td_init; 668 private long p_td_ta_new; 669 private long p_td_ta_delete; 670 private long p_td_ta_thr_iter; 671 private long p_td_thr_get_info; 672 private long p_td_ta_map_id2thr; 673 private long p_td_thr_getgregs; 674 675 // part of class sharing workaround 676 private int classes_jsa_fd; 677 private long p_file_map_header; 678 679 private boolean attached = false; 680 private boolean isCore; 681 682 // for core files, we cache load object list, thread list, top frames etc. 683 // for processes we cache load object list and sync. it during suspend. 684 private List threadListCache; 685 private List loadObjectCache; 686 private Map topFrameCache; // Map<ThreadProxy, CFrame> 687 }