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