author | coleenp |
Sun, 13 Apr 2008 17:43:42 -0400 | |
changeset 360 | 21d113ecbf6a |
parent 1 | 489c9b5090e2 |
child 670 | ddf3e9583f2f |
permissions | -rw-r--r-- |
1 | 1 |
/* |
2 |
* Copyright 2000-2004 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.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 |
||
360
21d113ecbf6a
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
1
diff
changeset
|
309 |
public Win32Address readCompOopAddress(long address) |
21d113ecbf6a
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
1
diff
changeset
|
310 |
throws UnmappedAddressException, UnalignedAddressException { |
21d113ecbf6a
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
1
diff
changeset
|
311 |
return (Win32Address) newAddress(readCompOopAddressValue(address)); |
21d113ecbf6a
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
1
diff
changeset
|
312 |
} |
21d113ecbf6a
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
1
diff
changeset
|
313 |
|
1 | 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 |
} |
|
360
21d113ecbf6a
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
1
diff
changeset
|
320 |
public Win32OopHandle readCompOopHandle(long address) |
21d113ecbf6a
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
1
diff
changeset
|
321 |
throws UnmappedAddressException, UnalignedAddressException, NotInHeapException { |
21d113ecbf6a
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
1
diff
changeset
|
322 |
long value = readCompOopAddressValue(address); |
21d113ecbf6a
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
1
diff
changeset
|
323 |
return (value == 0 ? null : new Win32OopHandle(this, value)); |
21d113ecbf6a
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
1
diff
changeset
|
324 |
} |
1 | 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 |
} |