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