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