Mercurial > hg > truffle
annotate agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32DebuggerLocal.java @ 1385:bc32f286fae0
6945219: minor SA fixes
Reviewed-by: twisti
author | never |
---|---|
date | Tue, 20 Apr 2010 13:26:33 -0700 |
parents | d1605aabd0a1 |
children | c18cbe5936b8 |
rev | line source |
---|---|
0 | 1 /* |
196 | 2 * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. |
0 | 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.win32; | |
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.win32.coff.*; | |
33 import sun.jvm.hotspot.debugger.cdbg.*; | |
34 import sun.jvm.hotspot.debugger.cdbg.basic.BasicDebugEvent; | |
35 import sun.jvm.hotspot.utilities.*; | |
36 import sun.jvm.hotspot.utilities.memo.*; | |
37 | |
38 /** <P> An implementation of the JVMDebugger interface which talks to | |
39 the Free Windows Debug Server (FwDbgSrv) over a socket to | |
40 implement attach/detach and read from process memory. All DLL and | |
41 symbol table management is done in Java. </P> | |
42 | |
43 <P> <B>NOTE</B> that since we have the notion of fetching "Java | |
44 primitive types" from the remote process (which might have | |
45 different sizes than we expect) we have a bootstrapping | |
46 problem. We need to know the sizes of these types before we can | |
47 fetch them. The current implementation solves this problem by | |
48 requiring that it be configured with these type sizes before they | |
49 can be fetched. The readJ(Type) routines here will throw a | |
50 RuntimeException if they are called before the debugger is | |
51 configured with the Java primitive type sizes. </P> */ | |
52 | |
53 public class Win32DebuggerLocal extends DebuggerBase implements Win32Debugger { | |
54 private Socket debuggerSocket; | |
55 private boolean attached; | |
56 // FIXME: update when core files supported | |
57 private long pid; | |
58 // Communication with debug server | |
59 private PrintWriter out; | |
60 private DataOutputStream rawOut; | |
61 private InputLexer in; | |
62 private static final int PORT = 27000; | |
63 private PageCache cache; | |
64 private static final long SHORT_TIMEOUT = 2000; | |
65 private static final long LONG_TIMEOUT = 20000; | |
66 | |
67 // Symbol lookup support | |
68 // This is a map of library names to DLLs | |
69 private Map nameToDllMap; | |
70 | |
71 // C/C++ debugging support | |
72 private List/*<LoadObject>*/ loadObjects; | |
73 private CDebugger cdbg; | |
74 | |
75 // ProcessControl support | |
76 private boolean suspended; | |
77 // Maps Long objects (addresses) to Byte objects (original instructions) | |
78 // (Longs used instead of Addresses to properly represent breakpoints at 0x0 if needed) | |
79 private Map breakpoints; | |
80 // Current debug event, if any | |
81 private DebugEvent curDebugEvent; | |
82 | |
83 //-------------------------------------------------------------------------------- | |
84 // Implementation of Debugger interface | |
85 // | |
86 | |
87 /** <P> machDesc may not be null. </P> | |
88 | |
89 <P> useCache should be set to true if debugging is being done | |
90 locally, and to false if the debugger is being created for the | |
91 purpose of supporting remote debugging. </P> */ | |
92 public Win32DebuggerLocal(MachineDescription machDesc, | |
93 boolean useCache) throws DebuggerException { | |
94 this.machDesc = machDesc; | |
95 utils = new DebuggerUtilities(machDesc.getAddressSize(), machDesc.isBigEndian()); | |
96 if (useCache) { | |
97 // Cache portion of the remote process's address space. | |
98 // Fetching data over the socket connection to dbx is slow. | |
99 // Might be faster if we were using a binary protocol to talk to | |
100 // dbx, but would have to test. For now, this cache works best | |
101 // if it covers the entire heap of the remote process. FIXME: at | |
102 // least should make this tunable from the outside, i.e., via | |
103 // the UI. This is a cache of 4096 4K pages, or 16 MB. The page | |
104 // size must be adjusted to be the hardware's page size. | |
105 // (FIXME: should pick this up from the debugger.) | |
106 initCache(4096, parseCacheNumPagesProperty(4096)); | |
107 } | |
108 // FIXME: add instantiation of thread factory | |
109 | |
110 try { | |
111 connectToDebugServer(); | |
112 } catch (IOException e) { | |
113 throw new DebuggerException(e); | |
114 } | |
115 } | |
116 | |
117 /** From the Debugger interface via JVMDebugger */ | |
118 public boolean hasProcessList() throws DebuggerException { | |
119 return true; | |
120 } | |
121 | |
122 /** From the Debugger interface via JVMDebugger */ | |
123 public List getProcessList() throws DebuggerException { | |
124 List processes = new ArrayList(); | |
125 | |
126 try { | |
127 printlnToOutput("proclist"); | |
128 int num = in.parseInt(); | |
129 for (int i = 0; i < num; i++) { | |
130 int pid = in.parseInt(); | |
131 String name = parseString(); | |
132 // NOTE: Win32 hack | |
133 if (name.equals("")) { | |
134 name = "System Idle Process"; | |
135 } | |
136 processes.add(new ProcessInfo(name, pid)); | |
137 } | |
138 return processes; | |
139 } | |
140 catch (IOException e) { | |
141 throw new DebuggerException(e); | |
142 } | |
143 } | |
144 | |
145 /** From the Debugger interface via JVMDebugger */ | |
146 public synchronized void attach(int processID) throws DebuggerException { | |
147 if (attached) { | |
148 // FIXME: update when core files supported | |
149 throw new DebuggerException("Already attached to process " + pid); | |
150 } | |
151 | |
152 try { | |
153 printlnToOutput("attach " + processID); | |
154 if (!in.parseBoolean()) { | |
155 throw new DebuggerException("Error attaching to process, or no such process"); | |
156 } | |
157 | |
158 attached = true; | |
159 pid = processID; | |
160 suspended = true; | |
161 breakpoints = new HashMap(); | |
162 curDebugEvent = null; | |
163 nameToDllMap = null; | |
164 loadObjects = null; | |
165 } | |
166 catch (IOException e) { | |
167 throw new DebuggerException(e); | |
168 } | |
169 } | |
170 | |
171 /** From the Debugger interface via JVMDebugger */ | |
172 public synchronized void attach(String executableName, String coreFileName) throws DebuggerException { | |
173 throw new DebuggerException("Core files not yet supported on Win32"); | |
174 } | |
175 | |
176 /** From the Debugger interface via JVMDebugger */ | |
177 public synchronized boolean detach() { | |
178 if (!attached) { | |
179 return false; | |
180 } | |
181 | |
182 attached = false; | |
183 suspended = false; | |
184 breakpoints = null; | |
185 | |
186 // Close all open DLLs | |
187 if (nameToDllMap != null) { | |
188 for (Iterator iter = nameToDllMap.values().iterator(); iter.hasNext(); ) { | |
189 DLL dll = (DLL) iter.next(); | |
190 dll.close(); | |
191 } | |
192 nameToDllMap = null; | |
193 loadObjects = null; | |
194 } | |
195 | |
196 cdbg = null; | |
197 clearCache(); | |
198 | |
199 try { | |
200 printlnToOutput("detach"); | |
201 return in.parseBoolean(); | |
202 } | |
203 catch (IOException e) { | |
204 throw new DebuggerException(e); | |
205 } | |
206 } | |
207 | |
208 /** From the Debugger interface via JVMDebugger */ | |
209 public Address parseAddress(String addressString) throws NumberFormatException { | |
210 return newAddress(utils.scanAddress(addressString)); | |
211 } | |
212 | |
213 /** From the Debugger interface via JVMDebugger */ | |
214 public String getOS() { | |
215 return PlatformInfo.getOS(); | |
216 } | |
217 | |
218 /** From the Debugger interface via JVMDebugger */ | |
219 public String getCPU() { | |
220 return PlatformInfo.getCPU(); | |
221 } | |
222 | |
223 public boolean hasConsole() throws DebuggerException { | |
224 return false; | |
225 } | |
226 | |
227 public String consoleExecuteCommand(String cmd) throws DebuggerException { | |
228 throw new DebuggerException("No debugger console available on Win32"); | |
229 } | |
230 | |
231 public String getConsolePrompt() throws DebuggerException { | |
232 return null; | |
233 } | |
234 | |
235 public CDebugger getCDebugger() throws DebuggerException { | |
236 if (cdbg == null) { | |
237 cdbg = new Win32CDebugger(this); | |
238 } | |
239 return cdbg; | |
240 } | |
241 | |
242 /** From the SymbolLookup interface via Debugger and JVMDebugger */ | |
243 public synchronized Address lookup(String objectName, String symbol) { | |
244 if (!attached) { | |
245 return null; | |
246 } | |
247 return newAddress(lookupInProcess(objectName, symbol)); | |
248 } | |
249 | |
250 /** From the SymbolLookup interface via Debugger and JVMDebugger */ | |
251 public synchronized OopHandle lookupOop(String objectName, String symbol) { | |
252 Address addr = lookup(objectName, symbol); | |
253 if (addr == null) { | |
254 return null; | |
255 } | |
256 return addr.addOffsetToAsOopHandle(0); | |
257 } | |
258 | |
259 /** From the Debugger interface */ | |
260 public MachineDescription getMachineDescription() { | |
261 return machDesc; | |
262 } | |
263 | |
264 //-------------------------------------------------------------------------------- | |
265 // Implementation of ThreadAccess interface | |
266 // | |
267 | |
268 /** From the ThreadAccess interface via Debugger and JVMDebugger */ | |
269 public ThreadProxy getThreadForIdentifierAddress(Address addr) { | |
270 return new Win32Thread(this, addr); | |
271 } | |
272 | |
273 public ThreadProxy getThreadForThreadId(long handle) { | |
274 return new Win32Thread(this, handle); | |
275 } | |
276 | |
277 //---------------------------------------------------------------------- | |
278 // Overridden from DebuggerBase because we need to relax alignment | |
279 // constraints on x86 | |
280 | |
281 public long readJLong(long address) | |
282 throws UnmappedAddressException, UnalignedAddressException { | |
283 checkJavaConfigured(); | |
284 // FIXME: allow this to be configurable. Undesirable to add a | |
285 // dependency on the runtime package here, though, since this | |
286 // package should be strictly underneath it. | |
287 // utils.checkAlignment(address, jlongSize); | |
288 utils.checkAlignment(address, jintSize); | |
289 byte[] data = readBytes(address, jlongSize); | |
290 return utils.dataToJLong(data, jlongSize); | |
291 } | |
292 | |
293 //-------------------------------------------------------------------------------- | |
294 // Internal routines (for implementation of Win32Address). | |
295 // These must not be called until the MachineDescription has been set up. | |
296 // | |
297 | |
298 /** From the Win32Debugger interface */ | |
299 public String addressValueToString(long address) { | |
300 return utils.addressValueToString(address); | |
301 } | |
302 | |
303 /** From the Win32Debugger interface */ | |
304 public Win32Address readAddress(long address) | |
305 throws UnmappedAddressException, UnalignedAddressException { | |
306 return (Win32Address) newAddress(readAddressValue(address)); | |
307 } | |
308 | |
113
ba764ed4b6f2
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
0
diff
changeset
|
309 public Win32Address readCompOopAddress(long address) |
ba764ed4b6f2
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
0
diff
changeset
|
310 throws UnmappedAddressException, UnalignedAddressException { |
ba764ed4b6f2
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
0
diff
changeset
|
311 return (Win32Address) newAddress(readCompOopAddressValue(address)); |
ba764ed4b6f2
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
0
diff
changeset
|
312 } |
ba764ed4b6f2
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
0
diff
changeset
|
313 |
0 | 314 /** From the Win32Debugger interface */ |
315 public Win32OopHandle readOopHandle(long address) | |
316 throws UnmappedAddressException, UnalignedAddressException, NotInHeapException { | |
317 long value = readAddressValue(address); | |
318 return (value == 0 ? null : new Win32OopHandle(this, value)); | |
319 } | |
113
ba764ed4b6f2
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
0
diff
changeset
|
320 public Win32OopHandle readCompOopHandle(long address) |
ba764ed4b6f2
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
0
diff
changeset
|
321 throws UnmappedAddressException, UnalignedAddressException, NotInHeapException { |
ba764ed4b6f2
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
0
diff
changeset
|
322 long value = readCompOopAddressValue(address); |
ba764ed4b6f2
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
0
diff
changeset
|
323 return (value == 0 ? null : new Win32OopHandle(this, value)); |
ba764ed4b6f2
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
0
diff
changeset
|
324 } |
0 | 325 |
326 /** From the Win32Debugger interface */ | |
327 public void writeAddress(long address, Win32Address value) { | |
328 writeAddressValue(address, getAddressValue(value)); | |
329 } | |
330 | |
331 /** From the Win32Debugger interface */ | |
332 public void writeOopHandle(long address, Win32OopHandle value) { | |
333 writeAddressValue(address, getAddressValue(value)); | |
334 } | |
335 | |
336 //-------------------------------------------------------------------------------- | |
337 // Thread context access | |
338 // | |
339 | |
340 public synchronized long[] getThreadIntegerRegisterSet(int threadHandleValue, | |
341 boolean mustDuplicateHandle) | |
342 throws DebuggerException { | |
343 if (!suspended) { | |
344 throw new DebuggerException("Process not suspended"); | |
345 } | |
346 | |
347 try { | |
348 int handle = threadHandleValue; | |
349 if (mustDuplicateHandle) { | |
350 printlnToOutput("duphandle 0x" + Integer.toHexString(threadHandleValue)); | |
351 if (!in.parseBoolean()) { | |
352 throw new DebuggerException("Error duplicating thread handle 0x" + threadHandleValue); | |
353 } | |
354 handle = (int) in.parseAddress(); // Must close to avoid leaks | |
355 } | |
356 printlnToOutput("getcontext 0x" + Integer.toHexString(handle)); | |
357 if (!in.parseBoolean()) { | |
358 if (mustDuplicateHandle) { | |
359 printlnToOutput("closehandle 0x" + Integer.toHexString(handle)); | |
360 } | |
361 String failMessage = "GetThreadContext failed for thread handle 0x" + | |
362 Integer.toHexString(handle); | |
363 if (mustDuplicateHandle) { | |
364 failMessage = failMessage + ", duplicated from thread handle " + | |
365 Integer.toHexString(threadHandleValue); | |
366 } | |
367 throw new DebuggerException(failMessage); | |
368 } | |
369 // Otherwise, parse all registers. See | |
370 // src/os/win32/agent/README-commands.txt for the format. | |
371 // Note the array we have to return has to match that specified by | |
372 // X86ThreadContext.java. | |
373 int numRegs = 22; | |
374 long[] winRegs = new long[numRegs]; | |
375 for (int i = 0; i < numRegs; i++) { | |
376 winRegs[i] = in.parseAddress(); | |
377 } | |
378 if (mustDuplicateHandle) { | |
379 // Clean up after ourselves | |
380 printlnToOutput("closehandle 0x" + Integer.toHexString(handle)); | |
381 } | |
382 // Now create the real return value | |
383 long[] retval = new long[X86ThreadContext.NPRGREG]; | |
384 retval[X86ThreadContext.EAX] = winRegs[0]; | |
385 retval[X86ThreadContext.EBX] = winRegs[1]; | |
386 retval[X86ThreadContext.ECX] = winRegs[2]; | |
387 retval[X86ThreadContext.EDX] = winRegs[3]; | |
388 retval[X86ThreadContext.ESI] = winRegs[4]; | |
389 retval[X86ThreadContext.EDI] = winRegs[5]; | |
390 retval[X86ThreadContext.EBP] = winRegs[6]; | |
391 retval[X86ThreadContext.ESP] = winRegs[7]; | |
392 retval[X86ThreadContext.EIP] = winRegs[8]; | |
393 retval[X86ThreadContext.DS] = winRegs[9]; | |
394 retval[X86ThreadContext.ES] = winRegs[10]; | |
395 retval[X86ThreadContext.FS] = winRegs[11]; | |
396 retval[X86ThreadContext.GS] = winRegs[12]; | |
397 retval[X86ThreadContext.CS] = winRegs[13]; | |
398 retval[X86ThreadContext.SS] = winRegs[14]; | |
399 retval[X86ThreadContext.EFL] = winRegs[15]; | |
400 retval[X86ThreadContext.DR0] = winRegs[16]; | |
401 retval[X86ThreadContext.DR1] = winRegs[17]; | |
402 retval[X86ThreadContext.DR2] = winRegs[18]; | |
403 retval[X86ThreadContext.DR3] = winRegs[19]; | |
404 retval[X86ThreadContext.DR6] = winRegs[20]; | |
405 retval[X86ThreadContext.DR7] = winRegs[21]; | |
406 return retval; | |
407 } catch (IOException e) { | |
408 throw new DebuggerException(e); | |
409 } | |
410 } | |
411 | |
412 public synchronized void setThreadIntegerRegisterSet(int threadHandleValue, | |
413 boolean mustDuplicateHandle, | |
414 long[] context) | |
415 throws DebuggerException { | |
416 if (!suspended) { | |
417 throw new DebuggerException("Process not suspended"); | |
418 } | |
419 | |
420 try { | |
421 int handle = threadHandleValue; | |
422 if (mustDuplicateHandle) { | |
423 printlnToOutput("duphandle 0x" + Integer.toHexString(threadHandleValue)); | |
424 if (!in.parseBoolean()) { | |
425 throw new DebuggerException("Error duplicating thread handle 0x" + threadHandleValue); | |
426 } | |
427 handle = (int) in.parseAddress(); // Must close to avoid leaks | |
428 } | |
429 // Change order of registers to match that of debug server | |
430 long[] winRegs = new long[context.length]; | |
431 winRegs[0] = context[X86ThreadContext.EAX]; | |
432 winRegs[1] = context[X86ThreadContext.EBX]; | |
433 winRegs[2] = context[X86ThreadContext.ECX]; | |
434 winRegs[3] = context[X86ThreadContext.EDX]; | |
435 winRegs[4] = context[X86ThreadContext.ESI]; | |
436 winRegs[5] = context[X86ThreadContext.EDI]; | |
437 winRegs[6] = context[X86ThreadContext.EBP]; | |
438 winRegs[7] = context[X86ThreadContext.ESP]; | |
439 winRegs[8] = context[X86ThreadContext.EIP]; | |
440 winRegs[9] = context[X86ThreadContext.DS]; | |
441 winRegs[10] = context[X86ThreadContext.ES]; | |
442 winRegs[11] = context[X86ThreadContext.FS]; | |
443 winRegs[12] = context[X86ThreadContext.GS]; | |
444 winRegs[13] = context[X86ThreadContext.CS]; | |
445 winRegs[14] = context[X86ThreadContext.SS]; | |
446 winRegs[15] = context[X86ThreadContext.EFL]; | |
447 winRegs[16] = context[X86ThreadContext.DR0]; | |
448 winRegs[17] = context[X86ThreadContext.DR1]; | |
449 winRegs[18] = context[X86ThreadContext.DR2]; | |
450 winRegs[19] = context[X86ThreadContext.DR3]; | |
451 winRegs[20] = context[X86ThreadContext.DR6]; | |
452 winRegs[21] = context[X86ThreadContext.DR7]; | |
453 StringBuffer cmd = new StringBuffer(); | |
454 cmd.append("setcontext 0x"); | |
455 cmd.append(Integer.toHexString(threadHandleValue)); | |
456 for (int i = 0; i < context.length; i++) { | |
457 cmd.append(" 0x"); | |
458 cmd.append(Long.toHexString(winRegs[i])); | |
459 } | |
460 printlnToOutput(cmd.toString()); | |
461 boolean res = in.parseBoolean(); | |
462 if (mustDuplicateHandle) { | |
463 printlnToOutput("closehandle 0x" + Integer.toHexString(handle)); | |
464 } | |
465 if (!res) { | |
466 String failMessage = "SetThreadContext failed for thread handle 0x" + | |
467 Integer.toHexString(handle); | |
468 if (mustDuplicateHandle) { | |
469 failMessage = failMessage + ", duplicated from thread handle " + | |
470 Integer.toHexString(threadHandleValue); | |
471 } | |
472 throw new DebuggerException(failMessage); | |
473 } | |
474 } catch (IOException e) { | |
475 throw new DebuggerException(e); | |
476 } | |
477 } | |
478 | |
479 /** Fetches the Win32 LDT_ENTRY for the given thread and selector. | |
480 This data structure allows the conversion of a segment-relative | |
481 address to a linear virtual address. For example, it allows the | |
482 expression of operations like "mov eax, fs:[18h]", which fetches | |
483 the thread information block, allowing access to the thread | |
484 ID. */ | |
485 public synchronized Win32LDTEntry getThreadSelectorEntry(int threadHandleValue, | |
486 boolean mustDuplicateHandle, | |
487 int selector) | |
488 throws DebuggerException { | |
489 try { | |
490 int handle = threadHandleValue; | |
491 if (mustDuplicateHandle) { | |
492 printlnToOutput("duphandle 0x" + Integer.toHexString(threadHandleValue)); | |
493 if (!in.parseBoolean()) { | |
494 throw new DebuggerException("Error duplicating thread handle 0x" + threadHandleValue); | |
495 } | |
496 handle = (int) in.parseAddress(); // Must close to avoid leaks | |
497 } | |
498 printlnToOutput("selectorentry 0x" + Integer.toHexString(handle) + " " + selector); | |
499 if (!in.parseBoolean()) { | |
500 if (mustDuplicateHandle) { | |
501 printlnToOutput("closehandle 0x" + Integer.toHexString(handle)); | |
502 } | |
503 throw new DebuggerException("GetThreadContext failed for thread handle 0x" + handle + | |
504 ", duplicated from thread handle " + threadHandleValue); | |
505 } | |
506 // Parse result. See | |
507 // src/os/win32/agent/README-commands.txt for the format. | |
508 short limitLow = (short) in.parseAddress(); | |
509 short baseLow = (short) in.parseAddress(); | |
510 byte baseMid = (byte) in.parseAddress(); | |
511 byte flags1 = (byte) in.parseAddress(); | |
512 byte flags2 = (byte) in.parseAddress(); | |
513 byte baseHi = (byte) in.parseAddress(); | |
514 return new Win32LDTEntry(limitLow, baseLow, baseMid, flags1, flags2, baseHi); | |
515 } catch (IOException e) { | |
516 throw new DebuggerException(e); | |
517 } | |
518 } | |
519 | |
520 public synchronized List getThreadList() throws DebuggerException { | |
521 if (!suspended) { | |
522 throw new DebuggerException("Process not suspended"); | |
523 } | |
524 | |
525 try { | |
526 printlnToOutput("threadlist"); | |
527 List ret = new ArrayList(); | |
528 int numThreads = in.parseInt(); | |
529 for (int i = 0; i < numThreads; i++) { | |
530 int handle = (int) in.parseAddress(); | |
531 ret.add(new Win32Thread(this, handle)); | |
532 } | |
533 return ret; | |
534 } catch (IOException e) { | |
535 throw new DebuggerException(e); | |
536 } | |
537 } | |
538 | |
539 public synchronized List getLoadObjectList() throws DebuggerException { | |
540 if (!suspended) { | |
541 throw new DebuggerException("Process not suspended"); | |
542 } | |
543 | |
544 try { | |
545 if (loadObjects == null) { | |
546 loadObjects = new ArrayList(); | |
547 nameToDllMap = new HashMap(); | |
548 // Get list of library names and base addresses | |
549 printlnToOutput("libinfo"); | |
550 int numInfo = in.parseInt(); | |
551 | |
552 for (int i = 0; i < numInfo; i++) { | |
553 // NOTE: because Win32 is case insensitive, we standardize on | |
554 // lowercase file names. | |
555 String fullPathName = parseString().toLowerCase(); | |
556 Address base = newAddress(in.parseAddress()); | |
557 | |
558 File file = new File(fullPathName); | |
559 long size = file.length(); | |
560 DLL dll = new DLL(this, fullPathName, size, base); | |
561 String name = file.getName(); | |
562 nameToDllMap.put(name, dll); | |
563 loadObjects.add(dll); | |
564 } | |
565 } | |
566 } catch (IOException e) { | |
567 throw new DebuggerException(e); | |
568 } | |
569 | |
570 return loadObjects; | |
571 } | |
572 | |
573 //---------------------------------------------------------------------- | |
574 // Process control access | |
575 // | |
576 | |
577 public synchronized void writeBytesToProcess(long startAddress, long numBytes, byte[] data) | |
578 throws UnmappedAddressException, DebuggerException { | |
579 try { | |
580 printToOutput("poke 0x" + Long.toHexString(startAddress) + | |
581 " |"); | |
582 writeIntToOutput((int) numBytes); | |
583 writeToOutput(data, 0, (int) numBytes); | |
584 printlnToOutput(""); | |
585 if (!in.parseBoolean()) { | |
586 throw new UnmappedAddressException(startAddress); | |
587 } | |
588 } catch (IOException e) { | |
589 throw new DebuggerException(e); | |
590 } | |
591 } | |
592 | |
593 public synchronized void suspend() throws DebuggerException { | |
594 try { | |
595 if (suspended) { | |
596 throw new DebuggerException("Process already suspended"); | |
597 } | |
598 printlnToOutput("suspend"); | |
599 suspended = true; | |
600 enableCache(); | |
601 reresolveLoadObjects(); | |
602 } catch (IOException e) { | |
603 throw new DebuggerException(e); | |
604 } | |
605 } | |
606 | |
607 public synchronized void resume() throws DebuggerException { | |
608 try { | |
609 if (!suspended) { | |
610 throw new DebuggerException("Process not suspended"); | |
611 } | |
612 disableCache(); | |
613 printlnToOutput("resume"); | |
614 suspended = false; | |
615 } catch (IOException e) { | |
616 throw new DebuggerException(e); | |
617 } | |
618 } | |
619 | |
620 public synchronized boolean isSuspended() throws DebuggerException { | |
621 return suspended; | |
622 } | |
623 | |
624 public synchronized void setBreakpoint(Address addr) throws DebuggerException { | |
625 if (!suspended) { | |
626 throw new DebuggerException("Process not suspended"); | |
627 } | |
628 | |
629 long addrVal = getAddressValue(addr); | |
630 Long where = new Long(addrVal); | |
631 if (breakpoints.get(where) != null) { | |
632 throw new DebuggerException("Breakpoint already set at " + addr); | |
633 } | |
634 Byte what = new Byte(readBytes(addrVal, 1)[0]); | |
635 // Now put 0xCC (int 3) at the target address, fail if can not | |
636 writeBytesToProcess(addrVal, 1, new byte[] { (byte) 0xCC }); | |
637 // OK, the breakpoint is set. | |
638 breakpoints.put(where, what); | |
639 } | |
640 | |
641 public synchronized void clearBreakpoint(Address addr) throws DebuggerException { | |
642 if (!suspended) { | |
643 throw new DebuggerException("Process not suspended"); | |
644 } | |
645 | |
646 long addrVal = getAddressValue(addr); | |
647 Long where = new Long(addrVal); | |
648 Byte what = (Byte) breakpoints.get(where); | |
649 if (what == null) { | |
650 throw new DebuggerException("Breakpoint not set at " + addr); | |
651 } | |
652 // Put original data back at address | |
653 writeBytesToProcess(addrVal, 1, new byte[] { what.byteValue() }); | |
654 // OK, breakpoint is cleared | |
655 breakpoints.remove(where); | |
656 } | |
657 | |
658 public synchronized boolean isBreakpointSet(Address addr) throws DebuggerException { | |
659 return (breakpoints.get(new Long(getAddressValue(addr))) != null); | |
660 } | |
661 | |
662 // Following constants taken from winnt.h | |
663 private static final int EXCEPTION_DEBUG_EVENT = 1; | |
664 private static final int LOAD_DLL_DEBUG_EVENT = 6; | |
665 private static final int UNLOAD_DLL_DEBUG_EVENT = 7; | |
666 private static final int EXCEPTION_ACCESS_VIOLATION = 0xC0000005; | |
667 private static final int EXCEPTION_BREAKPOINT = 0x80000003; | |
668 private static final int EXCEPTION_SINGLE_STEP = 0x80000004; | |
669 | |
670 public synchronized DebugEvent debugEventPoll() throws DebuggerException { | |
671 if (curDebugEvent != null) { | |
672 return curDebugEvent; | |
673 } | |
674 | |
675 try { | |
676 printlnToOutput("pollevent"); | |
677 if (!in.parseBoolean()) { | |
678 return null; | |
679 } | |
680 // Otherwise, got a debug event. Need to figure out what kind it is. | |
681 int handle = (int) in.parseAddress(); | |
682 ThreadProxy thread = new Win32Thread(this, handle); | |
683 int code = in.parseInt(); | |
684 DebugEvent ev = null; | |
685 switch (code) { | |
686 case LOAD_DLL_DEBUG_EVENT: { | |
687 Address addr = newAddress(in.parseAddress()); | |
688 ev = BasicDebugEvent.newLoadObjectLoadEvent(thread, addr); | |
689 break; | |
690 } | |
691 | |
692 case UNLOAD_DLL_DEBUG_EVENT: { | |
693 Address addr = newAddress(in.parseAddress()); | |
694 ev = BasicDebugEvent.newLoadObjectUnloadEvent(thread, addr); | |
695 break; | |
696 } | |
697 | |
698 case EXCEPTION_DEBUG_EVENT: { | |
699 int exceptionCode = in.parseInt(); | |
700 Address pc = newAddress(in.parseAddress()); | |
701 switch (exceptionCode) { | |
702 case EXCEPTION_ACCESS_VIOLATION: | |
703 boolean wasWrite = in.parseBoolean(); | |
704 Address addr = newAddress(in.parseAddress()); | |
705 ev = BasicDebugEvent.newAccessViolationEvent(thread, pc, wasWrite, addr); | |
706 break; | |
707 | |
708 case EXCEPTION_BREAKPOINT: | |
709 ev = BasicDebugEvent.newBreakpointEvent(thread, pc); | |
710 break; | |
711 | |
712 case EXCEPTION_SINGLE_STEP: | |
713 ev = BasicDebugEvent.newSingleStepEvent(thread, pc); | |
714 break; | |
715 | |
716 default: | |
717 ev = BasicDebugEvent.newUnknownEvent(thread, | |
718 "Exception 0x" + Integer.toHexString(exceptionCode) + | |
719 " at PC " + pc); | |
720 break; | |
721 } | |
722 break; | |
723 } | |
724 | |
725 default: | |
726 ev = BasicDebugEvent.newUnknownEvent(thread, | |
727 "Debug event " + code + " occurred"); | |
728 break; | |
729 } | |
730 if (Assert.ASSERTS_ENABLED) { | |
731 Assert.that(ev != null, "Must have created event"); | |
732 } | |
733 curDebugEvent = ev; | |
734 } catch (IOException e) { | |
735 throw new DebuggerException(e); | |
736 } | |
737 | |
738 return curDebugEvent; | |
739 } | |
740 | |
741 public synchronized void debugEventContinue() throws DebuggerException { | |
742 if (curDebugEvent == null) { | |
743 throw new DebuggerException("No debug event pending"); | |
744 } | |
745 | |
746 try { | |
747 /////////////////////////////////////////////////////////////////// | |
748 // // | |
749 // FIXME: this **must** be modified to handle breakpoint events | |
750 // properly. Must temporarily remove the breakpoint and enable | |
751 // single-stepping mode (hiding those single-step events from | |
752 // the user unless they have been requested; currently there is | |
753 // no way to request single-step events; and it isn't clear how | |
754 // to enable them or how the hardware and/or OS typically | |
755 // supports them, i.e., are they on a per-process or per-thread | |
756 // level?) until the process steps past the breakpoint, then put | |
757 // the breakpoint back. | |
758 // // | |
759 /////////////////////////////////////////////////////////////////// | |
760 | |
761 DebugEvent.Type t = curDebugEvent.getType(); | |
762 boolean shouldPassOn = true; | |
763 if (t == DebugEvent.Type.BREAKPOINT) { | |
764 // FIXME: correct algorithm appears to be as follows: | |
765 // | |
766 // 1. Check to see whether we know about this breakpoint. If | |
767 // not, it's requested by the user's program and we should | |
768 // ignore it (not pass it on to the program). | |
769 // | |
770 // 2. Replace the original opcode. | |
771 // | |
772 // 3. Set single-stepping mode in the debug registers. | |
773 // | |
774 // 4. Back up the PC. | |
775 // | |
776 // 5. In debugEventPoll(), watch for a single-step event on | |
777 // this thread. When we get it, put the breakpoint back. Only | |
778 // deliver that single-step event if the user has requested | |
779 // single-step events (FIXME: must figure out whether they are | |
780 // per-thread or per-process, and also expose a way to turn | |
781 // them on.) | |
782 | |
783 // To make breakpoints work for now, we will just back up the | |
784 // PC, which we have to do in order to not disrupt the program | |
785 // execution in case the user decides to disable the breakpoint. | |
786 | |
787 if (breakpoints.get(new Long(getAddressValue(curDebugEvent.getPC()))) != null) { | |
788 System.err.println("Backing up PC due to breakpoint"); | |
789 X86ThreadContext ctx = (X86ThreadContext) curDebugEvent.getThread().getContext(); | |
790 ctx.setRegister(X86ThreadContext.EIP, ctx.getRegister(X86ThreadContext.EIP) - 1); | |
791 curDebugEvent.getThread().setContext(ctx); | |
792 } else { | |
793 System.err.println("Skipping back up of PC since I didn't know about this breakpoint"); | |
794 System.err.println("Known breakpoints:"); | |
795 for (Iterator iter = breakpoints.keySet().iterator(); iter.hasNext(); ) { | |
796 System.err.println(" 0x" + Long.toHexString(((Long) iter.next()).longValue())); | |
797 } | |
798 } | |
799 shouldPassOn = false; | |
800 } else if (t == DebugEvent.Type.SINGLE_STEP) { | |
801 shouldPassOn = false; | |
802 } | |
803 // Other kinds of debug events are either ignored if passed on | |
804 // or probably should be passed on so the program exits | |
805 // FIXME: generate process exiting events (should be easy) | |
806 | |
807 int val = (shouldPassOn ? 1 : 0); | |
808 printlnToOutput("continueevent " + val); | |
809 if (!in.parseBoolean()) { | |
810 throw new DebuggerException("Unknown error while attempting to continue past debug event"); | |
811 } | |
812 curDebugEvent = null; | |
813 } catch (IOException e) { | |
814 throw new DebuggerException(e); | |
815 } | |
816 } | |
817 | |
818 //-------------------------------------------------------------------------------- | |
819 // Address access | |
820 // | |
821 | |
822 /** From the Debugger interface */ | |
823 public long getAddressValue(Address addr) { | |
824 if (addr == null) return 0; | |
825 return ((Win32Address) addr).getValue(); | |
826 } | |
827 | |
828 /** From the Win32Debugger interface */ | |
829 public Address newAddress(long value) { | |
830 if (value == 0) return null; | |
831 return new Win32Address(this, value); | |
832 } | |
833 | |
834 //-------------------------------------------------------------------------------- | |
835 // Internals only below this point | |
836 // | |
837 | |
838 private String parseString() throws IOException { | |
839 int charSize = in.parseInt(); | |
840 int numChars = in.parseInt(); | |
841 in.skipByte(); | |
842 String str; | |
843 if (charSize == 1) { | |
844 str = in.readByteString(numChars); | |
845 } else { | |
846 str = in.readCharString(numChars); | |
847 } | |
848 return str; | |
849 } | |
850 | |
851 /** Looks up an address in the remote process's address space. | |
852 Returns 0 if symbol not found or upon error. Package private to | |
853 allow Win32DebuggerRemoteIntfImpl access. NOTE that this returns | |
854 a long instead of an Address because we do not want to serialize | |
855 Addresses. */ | |
856 synchronized long lookupInProcess(String objectName, String symbol) { | |
857 // NOTE: this assumes that process is suspended (which is probably | |
858 // necessary assumption given that DLLs can be loaded/unloaded as | |
859 // process runs). Should update documentation. | |
860 if (nameToDllMap == null) { | |
861 getLoadObjectList(); | |
862 } | |
863 DLL dll = (DLL) nameToDllMap.get(objectName); | |
864 // The DLL can be null because we use this to search through known | |
865 // DLLs in HotSpotTypeDataBase (for example) | |
866 if (dll != null) { | |
867 Win32Address addr = (Win32Address) dll.lookupSymbol(symbol); | |
868 if (addr != null) { | |
869 return addr.getValue(); | |
870 } | |
871 } | |
872 return 0; | |
873 } | |
874 | |
875 /** This reads bytes from the remote process. */ | |
876 public synchronized ReadResult readBytesFromProcess(long address, long numBytes) | |
877 throws UnmappedAddressException, DebuggerException { | |
878 try { | |
879 String cmd = "peek " + utils.addressValueToString(address) + " " + numBytes; | |
880 printlnToOutput(cmd); | |
881 while (in.readByte() != 'B') { | |
882 } | |
883 byte res = in.readByte(); | |
884 if (res == 0) { | |
885 System.err.println("Failing command: " + cmd); | |
886 throw new DebuggerException("Read of remote process address space failed"); | |
887 } | |
888 // NOTE: must read ALL of the data regardless of whether we need | |
889 // to throw an UnmappedAddressException. Otherwise will corrupt | |
890 // the input stream each time we have a failure. Not good. Do | |
891 // not want to risk "flushing" the input stream in case a huge | |
892 // read has a hangup in the middle and we leave data on the | |
893 // stream. | |
894 byte[] buf = new byte[(int) numBytes]; | |
895 boolean bailOut = false; | |
896 long failureAddress = 0; | |
897 while (numBytes > 0) { | |
898 long len = in.readUnsignedInt(); | |
899 boolean isMapped = ((in.readByte() == 0) ? false : true); | |
900 if (!isMapped) { | |
901 if (!bailOut) { | |
902 bailOut = true; | |
903 failureAddress = address; | |
904 } | |
905 } else { | |
906 // This won't work if we have unmapped regions, but if we do | |
907 // then we're going to throw an exception anyway | |
908 | |
909 // NOTE: there is a factor of 20 speed difference between | |
910 // these two ways of doing this read. | |
911 in.readBytes(buf, 0, (int) len); | |
912 } | |
913 | |
914 // Do NOT do this: | |
915 // for (int i = 0; i < (int) len; i++) { | |
916 // buf[i] = in.readByte(); | |
917 // } | |
918 | |
919 numBytes -= len; | |
920 address += len; | |
921 } | |
922 if (Assert.ASSERTS_ENABLED) { | |
923 Assert.that(numBytes == 0, "Bug in debug server's implementation of peek"); | |
924 } | |
925 if (bailOut) { | |
926 return new ReadResult(failureAddress); | |
927 } | |
928 return new ReadResult(buf); | |
929 } | |
930 catch (IOException e) { | |
931 throw new DebuggerException(e); | |
932 } | |
933 } | |
934 | |
935 /** Convenience routines */ | |
936 private void printlnToOutput(String s) throws IOException { | |
937 out.println(s); | |
938 if (out.checkError()) { | |
939 throw new IOException("Error occurred while writing to debug server"); | |
940 } | |
941 } | |
942 | |
943 private void printToOutput(String s) throws IOException { | |
944 out.print(s); | |
945 if (out.checkError()) { | |
946 throw new IOException("Error occurred while writing to debug server"); | |
947 } | |
948 } | |
949 | |
950 private void writeIntToOutput(int val) throws IOException { | |
951 rawOut.writeInt(val); | |
952 rawOut.flush(); | |
953 } | |
954 | |
955 private void writeToOutput(byte[] buf, int off, int len) throws IOException { | |
956 rawOut.write(buf, off, len); | |
957 rawOut.flush(); | |
958 } | |
959 | |
960 /** Connects to the debug server, setting up out and in streams. */ | |
961 private void connectToDebugServer() throws IOException { | |
962 // Try for a short period of time to connect to debug server; time out | |
963 // with failure if didn't succeed | |
964 debuggerSocket = null; | |
965 long endTime = System.currentTimeMillis() + SHORT_TIMEOUT; | |
966 | |
967 while ((debuggerSocket == null) && (System.currentTimeMillis() < endTime)) { | |
968 try { | |
969 // FIXME: this does not work if we are on a DHCP machine which | |
970 // did not get an IP address this session. It appears to use | |
971 // an old cached address and the connection does not actually | |
972 // succeed. Must file a bug. | |
973 // debuggerSocket = new Socket(InetAddress.getLocalHost(), PORT); | |
974 debuggerSocket = new Socket(InetAddress.getByName("127.0.0.1"), PORT); | |
975 debuggerSocket.setTcpNoDelay(true); | |
976 } | |
977 catch (IOException e) { | |
978 // Swallow IO exceptions while attempting connection | |
979 debuggerSocket = null; | |
980 try { | |
981 // Don't swamp the CPU | |
982 Thread.sleep(750); | |
983 } | |
984 catch (InterruptedException ex) { | |
985 } | |
986 } | |
987 } | |
988 | |
989 if (debuggerSocket == null) { | |
990 // Failed to connect because of timeout | |
991 throw new DebuggerException("Timed out while attempting to connect to debug server (please start SwDbgSrv.exe)"); | |
992 } | |
993 | |
994 out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(debuggerSocket.getOutputStream(), "US-ASCII")), true); | |
995 rawOut = new DataOutputStream(new BufferedOutputStream(debuggerSocket.getOutputStream())); | |
996 in = new InputLexer(new BufferedInputStream(debuggerSocket.getInputStream())); | |
997 } | |
998 | |
999 private DLL findDLLByName(String fullPathName) { | |
1000 for (Iterator iter = loadObjects.iterator(); iter.hasNext(); ) { | |
1001 DLL dll = (DLL) iter.next(); | |
1002 if (dll.getName().equals(fullPathName)) { | |
1003 return dll; | |
1004 } | |
1005 } | |
1006 return null; | |
1007 } | |
1008 | |
1009 private void reresolveLoadObjects() throws DebuggerException { | |
1010 try { | |
1011 // It is too expensive to throw away the loadobject list every | |
1012 // time the process is suspended, largely because of debug | |
1013 // information re-parsing. When we suspend the target process we | |
1014 // instead fetch the list of loaded libraries in the target and | |
1015 // see whether any loadobject needs to be thrown away (because it | |
1016 // was unloaded) or invalidated (because it was unloaded and | |
1017 // reloaded at a different target address). Note that we don't | |
1018 // properly handle the case of a loaded DLL being unloaded, | |
1019 // recompiled, and reloaded. We could handle this by keeping a | |
1020 // time stamp. | |
1021 | |
1022 if (loadObjects == null) { | |
1023 return; | |
1024 } | |
1025 | |
1026 // Need to create new list since have to figure out which ones | |
1027 // were unloaded | |
1028 List newLoadObjects = new ArrayList(); | |
1029 | |
1030 // Get list of library names and base addresses | |
1031 printlnToOutput("libinfo"); | |
1032 int numInfo = in.parseInt(); | |
1033 | |
1034 for (int i = 0; i < numInfo; i++) { | |
1035 // NOTE: because Win32 is case insensitive, we standardize on | |
1036 // lowercase file names. | |
1037 String fullPathName = parseString().toLowerCase(); | |
1038 Address base = newAddress(in.parseAddress()); | |
1039 | |
1040 // Look for full path name in DLL list | |
1041 DLL dll = findDLLByName(fullPathName); | |
1042 boolean mustLoad = true; | |
1043 if (dll != null) { | |
1044 loadObjects.remove(dll); | |
1045 | |
1046 // See whether base addresses match; otherwise, need to reload | |
1047 if (AddressOps.equal(base, dll.getBase())) { | |
1048 mustLoad = false; | |
1049 } | |
1050 } | |
1051 | |
1052 if (mustLoad) { | |
1053 // Create new DLL | |
1054 File file = new File(fullPathName); | |
1055 long size = file.length(); | |
1056 String name = file.getName(); | |
1057 dll = new DLL(this, fullPathName, size, base); | |
1058 nameToDllMap.put(name, dll); | |
1059 } | |
1060 newLoadObjects.add(dll); | |
1061 } | |
1062 | |
1063 // All remaining entries in loadObjects have to be removed from | |
1064 // the nameToDllMap | |
1065 for (Iterator dllIter = loadObjects.iterator(); dllIter.hasNext(); ) { | |
1066 DLL dll = (DLL) dllIter.next(); | |
1067 for (Iterator iter = nameToDllMap.keySet().iterator(); iter.hasNext(); ) { | |
1068 String name = (String) iter.next(); | |
1069 if (nameToDllMap.get(name) == dll) { | |
1070 nameToDllMap.remove(name); | |
1071 break; | |
1072 } | |
1073 } | |
1074 } | |
1075 | |
1076 loadObjects = newLoadObjects; | |
1077 } catch (IOException e) { | |
1078 loadObjects = null; | |
1079 nameToDllMap = null; | |
1080 throw new DebuggerException(e); | |
1081 } | |
1082 } | |
1083 } |