Mercurial > hg > truffle
comparison agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebuggerLocal.java @ 0:a61af66fc99e jdk7-b24
Initial load
author | duke |
---|---|
date | Sat, 01 Dec 2007 00:00:00 +0000 |
parents | |
children | ba764ed4b6f2 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:a61af66fc99e |
---|---|
1 /* | |
2 * Copyright 2002-2006 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 | |
427 /** From the LinuxDebugger interface */ | |
428 public LinuxOopHandle readOopHandle(long address) | |
429 throws UnmappedAddressException, UnalignedAddressException, | |
430 NotInHeapException { | |
431 long value = readAddressValue(address); | |
432 return (value == 0 ? null : new LinuxOopHandle(this, value)); | |
433 } | |
434 | |
435 //---------------------------------------------------------------------- | |
436 // Thread context access | |
437 // | |
438 | |
439 public synchronized long[] getThreadIntegerRegisterSet(int lwp_id) | |
440 throws DebuggerException { | |
441 requireAttach(); | |
442 if (isCore) { | |
443 return getThreadIntegerRegisterSet0(lwp_id); | |
444 } else { | |
445 class GetThreadIntegerRegisterSetTask implements WorkerThreadTask { | |
446 int lwp_id; | |
447 long[] result; | |
448 public void doit(LinuxDebuggerLocal debugger) { | |
449 result = debugger.getThreadIntegerRegisterSet0(lwp_id); | |
450 } | |
451 } | |
452 | |
453 GetThreadIntegerRegisterSetTask task = new GetThreadIntegerRegisterSetTask(); | |
454 task.lwp_id = lwp_id; | |
455 workerThread.execute(task); | |
456 return task.result; | |
457 } | |
458 } | |
459 | |
460 /** Need to override this to relax alignment checks on x86. */ | |
461 public long readCInteger(long address, long numBytes, boolean isUnsigned) | |
462 throws UnmappedAddressException, UnalignedAddressException { | |
463 // Only slightly relaxed semantics -- this is a hack, but is | |
464 // necessary on x86 where it seems the compiler is | |
465 // putting some global 64-bit data on 32-bit boundaries | |
466 if (numBytes == 8) { | |
467 utils.checkAlignment(address, 4); | |
468 } else { | |
469 utils.checkAlignment(address, numBytes); | |
470 } | |
471 byte[] data = readBytes(address, numBytes); | |
472 return utils.dataToCInteger(data, isUnsigned); | |
473 } | |
474 | |
475 // Overridden from DebuggerBase because we need to relax alignment | |
476 // constraints on x86 | |
477 public long readJLong(long address) | |
478 throws UnmappedAddressException, UnalignedAddressException { | |
479 utils.checkAlignment(address, jintSize); | |
480 byte[] data = readBytes(address, jlongSize); | |
481 return utils.dataToJLong(data, jlongSize); | |
482 } | |
483 | |
484 //---------------------------------------------------------------------- | |
485 // Address access. Can not be package private, but should only be | |
486 // accessed by the architecture-specific subpackages. | |
487 | |
488 /** From the LinuxDebugger interface */ | |
489 public long getAddressValue(Address addr) { | |
490 if (addr == null) return 0; | |
491 return ((LinuxAddress) addr).getValue(); | |
492 } | |
493 | |
494 /** From the LinuxDebugger interface */ | |
495 public Address newAddress(long value) { | |
496 if (value == 0) return null; | |
497 return new LinuxAddress(this, value); | |
498 } | |
499 | |
500 /** From the LinuxCDebugger interface */ | |
501 public List/*<ThreadProxy>*/ getThreadList() { | |
502 requireAttach(); | |
503 return threadList; | |
504 } | |
505 | |
506 /** From the LinuxCDebugger interface */ | |
507 public List/*<LoadObject>*/ getLoadObjectList() { | |
508 requireAttach(); | |
509 return loadObjectList; | |
510 } | |
511 | |
512 /** From the LinuxCDebugger interface */ | |
513 public synchronized ClosestSymbol lookup(long addr) { | |
514 requireAttach(); | |
515 if (isCore) { | |
516 return lookupByAddress0(addr); | |
517 } else { | |
518 class LookupByAddressTask implements WorkerThreadTask { | |
519 long addr; | |
520 ClosestSymbol result; | |
521 | |
522 public void doit(LinuxDebuggerLocal debugger) { | |
523 result = debugger.lookupByAddress0(addr); | |
524 } | |
525 } | |
526 | |
527 LookupByAddressTask task = new LookupByAddressTask(); | |
528 task.addr = addr; | |
529 workerThread.execute(task); | |
530 return task.result; | |
531 } | |
532 } | |
533 | |
534 public CDebugger getCDebugger() { | |
535 if (cdbg == null) { | |
536 String cpu = getCPU(); | |
537 if (cpu.equals("ia64") ) { | |
538 // IA-64 is not supported because of stack-walking issues | |
539 return null; | |
540 } | |
541 cdbg = new LinuxCDebugger(this); | |
542 } | |
543 return cdbg; | |
544 } | |
545 | |
546 /** This reads bytes from the remote process. */ | |
547 public synchronized ReadResult readBytesFromProcess(long address, | |
548 long numBytes) throws UnmappedAddressException, DebuggerException { | |
549 requireAttach(); | |
550 if (isCore) { | |
551 byte[] res = readBytesFromProcess0(address, numBytes); | |
552 return (res != null)? new ReadResult(res) : new ReadResult(address); | |
553 } else { | |
554 class ReadBytesFromProcessTask implements WorkerThreadTask { | |
555 long address, numBytes; | |
556 ReadResult result; | |
557 public void doit(LinuxDebuggerLocal debugger) { | |
558 byte[] res = debugger.readBytesFromProcess0(address, numBytes); | |
559 if (res != null) | |
560 result = new ReadResult(res); | |
561 else | |
562 result = new ReadResult(address); | |
563 } | |
564 } | |
565 | |
566 ReadBytesFromProcessTask task = new ReadBytesFromProcessTask(); | |
567 task.address = address; | |
568 task.numBytes = numBytes; | |
569 workerThread.execute(task); | |
570 return task.result; | |
571 } | |
572 } | |
573 | |
574 public void writeBytesToProcess(long address, long numBytes, byte[] data) | |
575 throws UnmappedAddressException, DebuggerException { | |
576 // FIXME | |
577 throw new DebuggerException("Unimplemented"); | |
578 } | |
579 | |
580 static { | |
581 System.loadLibrary("saproc"); | |
582 init0(); | |
583 } | |
584 } |