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(int lwp_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 addr) { 404 return new BsdThread(this, addr); 405 } 406 407 /** From the ThreadAccess interface via Debugger and JVMDebugger */ 408 public ThreadProxy getThreadForThreadId(long id) { 409 return new BsdThread(this, id); 410 } 411 412 //---------------------------------------------------------------------- 413 // Internal routines (for implementation of BsdAddress). 414 // These must not be called until the MachineDescription has been set up. 415 // 416 417 /** From the BsdDebugger interface */ 418 public String addressValueToString(long address) { 419 return utils.addressValueToString(address); 420 } 421 422 /** From the BsdDebugger interface */ 423 public BsdAddress readAddress(long address) 424 throws UnmappedAddressException, UnalignedAddressException { 425 long value = readAddressValue(address); 426 return (value == 0 ? null : new BsdAddress(this, value)); 427 } 428 public BsdAddress readCompOopAddress(long address) 429 throws UnmappedAddressException, UnalignedAddressException { 430 long value = readCompOopAddressValue(address); 431 return (value == 0 ? null : new BsdAddress(this, value)); 432 } 433 434 /** From the BsdDebugger interface */ 435 public BsdOopHandle readOopHandle(long address) 436 throws UnmappedAddressException, UnalignedAddressException, 437 NotInHeapException { 438 long value = readAddressValue(address); 439 return (value == 0 ? null : new BsdOopHandle(this, value)); 440 } 441 public BsdOopHandle readCompOopHandle(long address) 442 throws UnmappedAddressException, UnalignedAddressException, 443 NotInHeapException { 444 long value = readCompOopAddressValue(address); 445 return (value == 0 ? null : new BsdOopHandle(this, value)); 446 } 447 448 //---------------------------------------------------------------------- 449 // Thread context access 450 // 451 452 public synchronized long[] getThreadIntegerRegisterSet(int lwp_id) 453 throws DebuggerException { 454 requireAttach(); 455 if (isCore) { 456 return getThreadIntegerRegisterSet0(lwp_id); 457 } else { 458 class GetThreadIntegerRegisterSetTask implements WorkerThreadTask { 459 int lwp_id; 460 long[] result; 461 public void doit(BsdDebuggerLocal debugger) { 462 result = debugger.getThreadIntegerRegisterSet0(lwp_id); 463 } 464 } 465 466 GetThreadIntegerRegisterSetTask task = new GetThreadIntegerRegisterSetTask(); 467 task.lwp_id = lwp_id; 468 workerThread.execute(task); 469 return task.result; 470 } 471 } 472 473 /** Need to override this to relax alignment checks on x86. */ 474 public long readCInteger(long address, long numBytes, boolean isUnsigned) 475 throws UnmappedAddressException, UnalignedAddressException { 476 // Only slightly relaxed semantics -- this is a hack, but is 477 // necessary on x86 where it seems the compiler is 478 // putting some global 64-bit data on 32-bit boundaries 479 if (numBytes == 8) { 480 utils.checkAlignment(address, 4); 481 } else { 482 utils.checkAlignment(address, numBytes); 483 } 484 byte[] data = readBytes(address, numBytes); 485 return utils.dataToCInteger(data, isUnsigned); 486 } 487 488 // Overridden from DebuggerBase because we need to relax alignment 489 // constraints on x86 490 public long readJLong(long address) 491 throws UnmappedAddressException, UnalignedAddressException { 492 utils.checkAlignment(address, jintSize); 493 byte[] data = readBytes(address, jlongSize); 494 return utils.dataToJLong(data, jlongSize); 495 } 496 497 //---------------------------------------------------------------------- 498 // Address access. Can not be package private, but should only be 499 // accessed by the architecture-specific subpackages. 500 501 /** From the BsdDebugger interface */ 502 public long getAddressValue(Address addr) { 503 if (addr == null) return 0; 504 return ((BsdAddress) addr).getValue(); 505 } 506 507 /** From the BsdDebugger interface */ 508 public Address newAddress(long value) { 509 if (value == 0) return null; 510 return new BsdAddress(this, value); 511 } 512 513 /** From the BsdCDebugger interface */ 514 public List/*<ThreadProxy>*/ getThreadList() { 515 requireAttach(); 516 return threadList; 517 } 518 519 /** From the BsdCDebugger interface */ 520 public List/*<LoadObject>*/ getLoadObjectList() { 521 requireAttach(); 522 return loadObjectList; 523 } 524 525 /** From the BsdCDebugger interface */ 526 public synchronized ClosestSymbol lookup(long addr) { 527 requireAttach(); 528 if (isCore) { 529 return lookupByAddress0(addr); 530 } else { 531 class LookupByAddressTask implements WorkerThreadTask { 532 long addr; 533 ClosestSymbol result; 534 535 public void doit(BsdDebuggerLocal debugger) { 536 result = debugger.lookupByAddress0(addr); 537 } 538 } 539 540 LookupByAddressTask task = new LookupByAddressTask(); 541 task.addr = addr; 542 workerThread.execute(task); 543 return task.result; 544 } 545 } 546 547 public CDebugger getCDebugger() { 548 if (cdbg == null) { 549 String cpu = getCPU(); 550 if (cpu.equals("ia64") ) { 551 // IA-64 is not supported because of stack-walking issues 552 return null; 553 } 554 cdbg = new BsdCDebugger(this); 555 } 556 return cdbg; 557 } 558 559 /** This reads bytes from the remote process. */ 560 public synchronized ReadResult readBytesFromProcess(long address, 561 long numBytes) throws UnmappedAddressException, DebuggerException { 562 requireAttach(); 563 if (isCore) { 564 byte[] res = readBytesFromProcess0(address, numBytes); 565 return (res != null)? new ReadResult(res) : new ReadResult(address); 566 } else { 567 class ReadBytesFromProcessTask implements WorkerThreadTask { 568 long address, numBytes; 569 ReadResult result; 570 public void doit(BsdDebuggerLocal debugger) { 571 byte[] res = debugger.readBytesFromProcess0(address, numBytes); 572 if (res != null) 573 result = new ReadResult(res); 574 else 575 result = new ReadResult(address); 576 } 577 } 578 579 ReadBytesFromProcessTask task = new ReadBytesFromProcessTask(); 580 task.address = address; 581 task.numBytes = numBytes; 582 workerThread.execute(task); 583 return task.result; 584 } 585 } 586 587 public void writeBytesToProcess(long address, long numBytes, byte[] data) 588 throws UnmappedAddressException, DebuggerException { 589 // FIXME 590 throw new DebuggerException("Unimplemented"); 591 } 592 593 static { 594 System.loadLibrary("saproc"); 595 init0(); 596 } 597 }