Mercurial > hg > truffle
annotate agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/DbxDebuggerLocal.java @ 650:54782a4cd321
6812971: SA: re-attaching to process fails
Summary: After attaching, de-attaching SA from a process, the second time attach() call fails. This happens because in VM.initialize(), Universe does not get re-initialized before it is accessed.
Reviewed-by: swamyv
author | poonam |
---|---|
date | Sun, 15 Mar 2009 18:11:46 -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.dbx; | |
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.dbx.sparc.*; | |
32 import sun.jvm.hotspot.debugger.dbx.x86.*; | |
33 import sun.jvm.hotspot.debugger.cdbg.CDebugger; | |
34 import sun.jvm.hotspot.utilities.*; | |
35 | |
36 /** <P> An implementation of the JVMDebugger interface which sits on | |
37 top of dbx and relies on the SA's dbx import module for | |
38 communication with the debugger. </P> | |
39 | |
40 <P> <B>NOTE</B> that since we have the notion of fetching "Java | |
41 primitive types" from the remote process (which might have | |
42 different sizes than we expect) we have a bootstrapping | |
43 problem. We need to know the sizes of these types before we can | |
44 fetch them. The current implementation solves this problem by | |
45 requiring that it be configured with these type sizes before they | |
46 can be fetched. The readJ(Type) routines here will throw a | |
47 RuntimeException if they are called before the debugger is | |
48 configured with the Java primitive type sizes. </P> | |
49 */ | |
50 | |
51 public class DbxDebuggerLocal extends DebuggerBase implements DbxDebugger { | |
52 // These may be set by DbxDebuggerRemote | |
53 protected boolean unalignedAccessesOkay; | |
54 protected DbxThreadFactory threadFactory; | |
55 | |
56 private String dbxPathName; | |
57 private String[] dbxSvcAgentDSOPathNames; | |
58 private Process dbxProcess; | |
59 private StreamMonitor dbxOutStreamMonitor; | |
60 private StreamMonitor dbxErrStreamMonitor; | |
61 private PrintWriter dbxOstr; | |
62 private PrintWriter out; | |
63 private InputLexer in; | |
64 private Socket importModuleSocket; | |
65 private static final int PORT = 21928; | |
66 private static final int LONG_TIMEOUT = 60000; | |
67 private static final int DBX_MODULE_NOT_FOUND = 101; | |
68 private static final int DBX_MODULE_LOADED = 102; | |
69 | |
70 //-------------------------------------------------------------------------------- | |
71 // Implementation of Debugger interface | |
72 // | |
73 | |
74 /** <P> machDesc may be null if it couldn't be determined yet; i.e., | |
75 if we're on SPARC, we need to ask the remote process whether | |
76 we're in 32- or 64-bit mode. </P> | |
77 | |
78 <P> useCache should be set to true if debugging is being done | |
79 locally, and to false if the debugger is being created for the | |
80 purpose of supporting remote debugging. </P> */ | |
81 public DbxDebuggerLocal(MachineDescription machDesc, | |
82 String dbxPathName, | |
83 String[] dbxSvcAgentDSOPathNames, | |
84 boolean useCache) { | |
85 this.machDesc = machDesc; | |
86 this.dbxPathName = dbxPathName; | |
87 this.dbxSvcAgentDSOPathNames = dbxSvcAgentDSOPathNames; | |
88 int cacheNumPages; | |
89 int cachePageSize; | |
90 if (PlatformInfo.getCPU().equals("sparc")) { | |
91 cacheNumPages = parseCacheNumPagesProperty(2048); | |
92 cachePageSize = 8192; | |
93 threadFactory = new DbxSPARCThreadFactory(this); | |
94 } else if (PlatformInfo.getCPU().equals("x86")) { | |
95 cacheNumPages = 4096; | |
96 cachePageSize = 4096; | |
97 threadFactory = new DbxX86ThreadFactory(this); | |
98 unalignedAccessesOkay = true; | |
99 } else { | |
100 throw new RuntimeException("Thread access for CPU architecture " + PlatformInfo.getCPU() + " not yet supported"); | |
101 } | |
102 if (useCache) { | |
103 // Cache portion of the remote process's address space. | |
104 // Fetching data over the socket connection to dbx is relatively | |
105 // slow. For now, this cache works best if it covers the entire | |
106 // heap of the remote process. FIXME: at least should make this | |
107 // tunable from the outside, i.e., via the UI. This is a 16 MB | |
108 // cache divided on SPARC into 2048 8K pages and on x86 into | |
109 // 4096 4K pages; the page size must be adjusted to be the OS's | |
110 // page size. (FIXME: should pick this up from the debugger.) | |
111 initCache(cachePageSize, cacheNumPages); | |
112 } | |
113 } | |
114 | |
115 /** Only called by DbxDebuggerRemote */ | |
116 protected DbxDebuggerLocal() { | |
117 } | |
118 | |
119 /** FIXME: implement this with a Runtime.exec() of ps followed by | |
120 parsing of its output */ | |
121 public boolean hasProcessList() throws DebuggerException { | |
122 return false; | |
123 } | |
124 | |
125 public List getProcessList() throws DebuggerException { | |
126 throw new DebuggerException("Not yet supported"); | |
127 } | |
128 | |
129 /** From the Debugger interface via JVMDebugger */ | |
130 public synchronized void attach(int processID) throws DebuggerException { | |
131 try { | |
132 launchProcess(); | |
133 dbxErrStreamMonitor.addTrigger("dbx: no process", 1); | |
134 dbxErrStreamMonitor.addTrigger("dbx: Cannot open", 1); | |
135 dbxErrStreamMonitor.addTrigger("dbx: Cannot find", DBX_MODULE_NOT_FOUND); | |
136 dbxOstr = new PrintWriter(dbxProcess.getOutputStream(), true); | |
137 dbxOstr.println("debug - " + processID); | |
138 dbxOstr.println("kprint -u2 \\(ready\\)"); | |
139 boolean seen = dbxErrStreamMonitor.waitFor("(ready)", LONG_TIMEOUT); | |
140 if (!seen) { | |
141 detach(); | |
142 throw new DebuggerException("Timed out while connecting to process " + processID); | |
143 } | |
144 List retVals = dbxErrStreamMonitor.getTriggersSeen(); | |
145 if (retVals.contains(new Integer(1))) { | |
146 detach(); | |
147 throw new DebuggerException("No such process " + processID); | |
148 } | |
149 | |
150 // Throws DebuggerException upon failure | |
151 importDbxModule(); | |
152 | |
153 dbxOstr.println("svc_agent_run"); | |
154 | |
155 connectToImportModule(); | |
156 | |
157 // Set "fail fast" mode on process memory reads | |
158 printlnToOutput("peek_fail_fast 1"); | |
159 } | |
160 catch (IOException e) { | |
161 detach(); | |
162 throw new DebuggerException("Error while connecting to dbx process", e); | |
163 } | |
164 } | |
165 | |
166 /** From the Debugger interface via JVMDebugger */ | |
167 public synchronized void attach(String executableName, String coreFileName) throws DebuggerException { | |
168 try { | |
169 launchProcess(); | |
170 // Missing executable | |
171 dbxErrStreamMonitor.addTrigger("dbx: Cannot open", 1); | |
172 // Missing core file | |
173 dbxErrStreamMonitor.addTrigger("dbx: can't read", 2); | |
174 // Corrupt executable | |
175 dbxErrStreamMonitor.addTrigger("dbx: File", 3); | |
176 // Corrupt core file | |
177 dbxErrStreamMonitor.addTrigger("dbx: Unable to read", 4); | |
178 // Mismatched core and executable | |
179 dbxErrStreamMonitor.addTrigger("dbx: core object name", 5); | |
180 // Missing loadobject | |
181 dbxErrStreamMonitor.addTrigger("dbx: can't stat", 6); | |
182 // Successful load of svc module | |
183 dbxOstr = new PrintWriter(dbxProcess.getOutputStream(), true); | |
184 dbxOstr.println("debug " + executableName + " " + coreFileName); | |
185 dbxOstr.println("kprint -u2 \\(ready\\)"); | |
186 boolean seen = dbxErrStreamMonitor.waitFor("(ready)", LONG_TIMEOUT); | |
187 if (!seen) { | |
188 detach(); | |
189 throw new DebuggerException("Timed out while attaching to core file"); | |
190 } | |
191 List retVals = dbxErrStreamMonitor.getTriggersSeen(); | |
192 if (retVals.size() > 0) { | |
193 detach(); | |
194 | |
195 if (retVals.contains(new Integer(1))) { | |
196 throw new DebuggerException("Can not find executable \"" + executableName + "\""); | |
197 } else if (retVals.contains(new Integer(2))) { | |
198 throw new DebuggerException("Can not find core file \"" + coreFileName + "\""); | |
199 } else if (retVals.contains(new Integer(3))) { | |
200 throw new DebuggerException("Corrupt executable \"" + executableName + "\""); | |
201 } else if (retVals.contains(new Integer(4))) { | |
202 throw new DebuggerException("Corrupt core file \"" + coreFileName + "\""); | |
203 } else if (retVals.contains(new Integer(5))) { | |
204 throw new DebuggerException("Mismatched core file/executable \"" + coreFileName + "\"/\"" + executableName + "\""); | |
205 } else { | |
206 throw new DebuggerException("Couldn't find all loaded libraries for executable \"" + executableName + "\""); | |
207 } | |
208 } | |
209 | |
210 // Throws DebuggerException upon failure | |
211 importDbxModule(); | |
212 | |
213 dbxOstr.println("svc_agent_run"); | |
214 | |
215 connectToImportModule(); | |
216 | |
217 // Set "fail fast" mode on process memory reads | |
218 printlnToOutput("peek_fail_fast 1"); | |
219 } | |
220 catch (IOException e) { | |
221 detach(); | |
222 throw new DebuggerException("Error while connecting to dbx process", e); | |
223 } | |
224 } | |
225 | |
226 /** From the Debugger interface via JVMDebugger */ | |
227 public synchronized boolean detach() { | |
228 try { | |
229 if (dbxProcess == null) { | |
230 return false; | |
231 } | |
232 | |
233 if (out != null && dbxOstr != null) { | |
234 printlnToOutput("exit"); | |
235 dbxOstr.println("exit"); | |
236 | |
237 // Wait briefly for the process to exit (FIXME: should make this | |
238 // nicer) | |
239 try { | |
240 Thread.sleep(500); | |
241 } | |
242 catch (InterruptedException e) { | |
243 } | |
244 } | |
245 | |
246 shutdown(); | |
247 | |
248 return true; | |
249 } catch (IOException e) { | |
250 e.printStackTrace(); | |
251 return false; | |
252 } | |
253 } | |
254 | |
255 /** From the Debugger interface via JVMDebugger */ | |
256 public Address parseAddress(String addressString) throws NumberFormatException { | |
257 long addr = utils.scanAddress(addressString); | |
258 if (addr == 0) { | |
259 return null; | |
260 } | |
261 return new DbxAddress(this, addr); | |
262 } | |
263 | |
264 /** From the Debugger interface via JVMDebugger */ | |
265 public String getOS() { | |
266 return PlatformInfo.getOS(); | |
267 } | |
268 | |
269 /** From the Debugger interface via JVMDebugger */ | |
270 public String getCPU() { | |
271 return PlatformInfo.getCPU(); | |
272 } | |
273 | |
274 public boolean hasConsole() throws DebuggerException { | |
275 return true; | |
276 } | |
277 | |
278 public synchronized String consoleExecuteCommand(String cmd) throws DebuggerException { | |
279 try { | |
280 // A little tricky. We need to cause the dbx import module to | |
281 // exit, then print our command on dbx's stdin along with a | |
282 // command which will allow our StreamMonitors to | |
283 // resynchronize. We need save the output from the StreamMonitors | |
284 // along the way. | |
285 printlnToOutput("exit"); | |
286 importModuleSocket.close(); | |
287 importModuleSocket = null; | |
288 out = null; | |
289 in = null; | |
290 dbxOstr.println("kprint \\(ready\\)"); | |
291 dbxOstr.flush(); | |
292 dbxOutStreamMonitor.waitFor("(ready)", LONG_TIMEOUT); | |
293 | |
294 dbxOutStreamMonitor.startCapture(); | |
295 dbxErrStreamMonitor.startCapture(); | |
296 dbxOstr.println(cmd); | |
297 dbxOstr.println("kprint \\(ready\\)"); | |
298 dbxOutStreamMonitor.waitFor("(ready)", LONG_TIMEOUT); | |
299 String result = dbxOutStreamMonitor.stopCapture(); | |
300 String result2 = dbxErrStreamMonitor.stopCapture(); | |
301 result = result + result2; | |
302 // Cut out the "(ready)" string | |
303 StringBuffer outBuf = new StringBuffer(result.length()); | |
304 BufferedReader reader = new BufferedReader(new StringReader(result)); | |
305 // FIXME: bug in BufferedReader? readLine returns null when | |
306 // ready() returns true. | |
307 String line = null; | |
308 do { | |
309 line = reader.readLine(); | |
310 if ((line != null) && (!line.equals("(ready)"))) { | |
311 outBuf.append(line); | |
312 outBuf.append("\n"); | |
313 } | |
314 } while (line != null); | |
315 dbxOstr.println("svc_agent_run"); | |
316 dbxOstr.flush(); | |
317 | |
318 connectToImportModule(); | |
319 | |
320 return outBuf.toString(); | |
321 } | |
322 catch (IOException e) { | |
323 detach(); | |
324 throw new DebuggerException("Error while executing command on dbx console", e); | |
325 } | |
326 } | |
327 | |
328 public String getConsolePrompt() throws DebuggerException { | |
329 return "(dbx) "; | |
330 } | |
331 | |
332 public CDebugger getCDebugger() throws DebuggerException { | |
333 return null; | |
334 } | |
335 | |
336 /** From the SymbolLookup interface via Debugger and JVMDebugger */ | |
337 public synchronized Address lookup(String objectName, String symbol) { | |
338 long addr = lookupInProcess(objectName, symbol); | |
339 if (addr == 0) { | |
340 return null; | |
341 } | |
342 return new DbxAddress(this, addr); | |
343 } | |
344 | |
345 /** From the SymbolLookup interface via Debugger and JVMDebugger */ | |
346 public synchronized OopHandle lookupOop(String objectName, String symbol) { | |
347 long addr = lookupInProcess(objectName, symbol); | |
348 if (addr == 0) { | |
349 return null; | |
350 } | |
351 return new DbxOopHandle(this, addr); | |
352 } | |
353 | |
354 /** From the Debugger interface */ | |
355 public MachineDescription getMachineDescription() { | |
356 return machDesc; | |
357 } | |
358 | |
359 /** Internal routine supporting lazy setting of MachineDescription, | |
360 since on SPARC we will need to query the remote process to ask | |
361 it what its data model is (32- or 64-bit). NOTE that this is NOT | |
362 present in the DbxDebugger interface because it should not be | |
363 called across the wire (until we support attaching to multiple | |
364 remote processes via RMI -- see the documentation for | |
365 DbxDebuggerRemoteIntf.) */ | |
366 public void setMachineDescription(MachineDescription machDesc) { | |
367 this.machDesc = machDesc; | |
368 setBigEndian(machDesc.isBigEndian()); | |
369 utils = new DebuggerUtilities(machDesc.getAddressSize(), machDesc.isBigEndian()); | |
370 } | |
371 | |
372 /** Internal routine which queries the remote process about its data | |
373 model -- i.e., size of addresses. Returns -1 upon error. | |
374 Currently supported return values are 32 and 64. NOTE that this | |
375 is NOT present in the DbxDebugger interface because it should | |
376 not be called across the wire (until we support attaching to | |
377 multiple remote processes via RMI -- see the documentation for | |
378 DbxDebuggerRemoteIntf.) */ | |
379 public int getRemoteProcessAddressSize() { | |
380 if (dbxProcess == null) { | |
381 throw new RuntimeException("Not attached to remote process"); | |
382 } | |
383 | |
384 try { | |
385 printlnToOutput("address_size"); | |
386 int i = in.parseInt(); | |
387 return i; | |
388 } | |
389 catch (IOException e) { | |
390 return -1; | |
391 } | |
392 } | |
393 | |
394 //-------------------------------------------------------------------------------- | |
395 // Implementation of ThreadAccess interface | |
396 // | |
397 | |
398 /** From the ThreadAccess interface via Debugger and JVMDebugger */ | |
399 public ThreadProxy getThreadForIdentifierAddress(Address addr) { | |
400 return threadFactory.createThreadWrapper(addr); | |
401 } | |
402 | |
403 public ThreadProxy getThreadForThreadId(long id) { | |
404 return threadFactory.createThreadWrapper(id); | |
405 } | |
406 | |
407 //---------------------------------------------------------------------- | |
408 // Overridden from DebuggerBase because we need to relax alignment | |
409 // constraints on x86 | |
410 | |
411 public long readJLong(long address) | |
412 throws UnmappedAddressException, UnalignedAddressException { | |
413 checkJavaConfigured(); | |
414 // FIXME: allow this to be configurable. Undesirable to add a | |
415 // dependency on the runtime package here, though, since this | |
416 // package should be strictly underneath it. | |
417 if (unalignedAccessesOkay) { | |
418 utils.checkAlignment(address, jintSize); | |
419 } else { | |
420 utils.checkAlignment(address, jlongSize); | |
421 } | |
422 byte[] data = readBytes(address, jlongSize); | |
423 return utils.dataToJLong(data, jlongSize); | |
424 } | |
425 | |
426 //-------------------------------------------------------------------------------- | |
427 // Internal routines (for implementation of DbxAddress). | |
428 // These must not be called until the MachineDescription has been set up. | |
429 // | |
430 | |
431 /** From the DbxDebugger interface */ | |
432 public String addressValueToString(long address) { | |
433 return utils.addressValueToString(address); | |
434 } | |
435 | |
436 /** Need to override this to relax alignment checks on Solaris/x86. */ | |
437 public long readCInteger(long address, long numBytes, boolean isUnsigned) | |
438 throws UnmappedAddressException, UnalignedAddressException { | |
439 checkConfigured(); | |
440 if (!unalignedAccessesOkay) { | |
441 utils.checkAlignment(address, numBytes); | |
442 } else { | |
443 // Only slightly relaxed semantics -- this is a hack, but is | |
444 // necessary on Solaris/x86 where it seems the compiler is | |
445 // putting some global 64-bit data on 32-bit boundaries | |
446 if (numBytes == 8) { | |
447 utils.checkAlignment(address, 4); | |
448 } else { | |
449 utils.checkAlignment(address, numBytes); | |
450 } | |
451 } | |
452 byte[] data = readBytes(address, numBytes); | |
453 return utils.dataToCInteger(data, isUnsigned); | |
454 } | |
455 | |
456 /** From the DbxDebugger interface */ | |
457 public DbxAddress readAddress(long address) | |
458 throws UnmappedAddressException, UnalignedAddressException { | |
459 long value = readAddressValue(address); | |
460 return (value == 0 ? null : new DbxAddress(this, value)); | |
461 } | |
462 | |
113
ba764ed4b6f2
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
0
diff
changeset
|
463 public DbxAddress readCompOopAddress(long address) |
ba764ed4b6f2
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
0
diff
changeset
|
464 throws UnmappedAddressException, UnalignedAddressException { |
ba764ed4b6f2
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
0
diff
changeset
|
465 long value = readCompOopAddressValue(address); |
ba764ed4b6f2
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
0
diff
changeset
|
466 return (value == 0 ? null : new DbxAddress(this, value)); |
ba764ed4b6f2
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
0
diff
changeset
|
467 } |
ba764ed4b6f2
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
0
diff
changeset
|
468 |
0 | 469 /** From the DbxDebugger interface */ |
470 public DbxOopHandle readOopHandle(long address) | |
471 throws UnmappedAddressException, UnalignedAddressException, NotInHeapException { | |
472 long value = readAddressValue(address); | |
473 return (value == 0 ? null : new DbxOopHandle(this, value)); | |
474 } | |
113
ba764ed4b6f2
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
0
diff
changeset
|
475 public DbxOopHandle readCompOopHandle(long address) |
ba764ed4b6f2
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
0
diff
changeset
|
476 throws UnmappedAddressException, UnalignedAddressException, NotInHeapException { |
ba764ed4b6f2
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
0
diff
changeset
|
477 long value = readCompOopAddressValue(address); |
ba764ed4b6f2
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
0
diff
changeset
|
478 return (value == 0 ? null : new DbxOopHandle(this, value)); |
ba764ed4b6f2
6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
coleenp
parents:
0
diff
changeset
|
479 } |
0 | 480 |
481 //-------------------------------------------------------------------------------- | |
482 // Thread context access. Can not be package private, but should | |
483 // only be accessed by the architecture-specific subpackages. | |
484 | |
485 /** From the DbxDebugger interface. May have to redefine this later. */ | |
486 public synchronized long[] getThreadIntegerRegisterSet(int tid) { | |
487 try { | |
488 printlnToOutput("thr_gregs " + tid); | |
489 int num = in.parseInt(); | |
490 long[] res = new long[num]; | |
491 for (int i = 0; i < num; i++) { | |
492 res[i] = in.parseAddress(); | |
493 } | |
494 return res; | |
495 } | |
496 catch (Exception e) { | |
497 e.printStackTrace(); | |
498 return null; | |
499 } | |
500 } | |
501 | |
502 //-------------------------------------------------------------------------------- | |
503 // Address access. Can not be package private, but should only be | |
504 // accessed by the architecture-specific subpackages. | |
505 | |
506 /** From the Debugger interface */ | |
507 public long getAddressValue(Address addr) { | |
508 if (addr == null) return 0; | |
509 return ((DbxAddress) addr).getValue(); | |
510 } | |
511 | |
512 /** From the DbxDebugger interface */ | |
513 public Address newAddress(long value) { | |
514 if (value == 0) return null; | |
515 return new DbxAddress(this, value); | |
516 } | |
517 | |
518 //-------------------------------------------------------------------------------- | |
519 // Internals only below this point | |
520 // | |
521 | |
522 private void launchProcess() throws IOException { | |
523 dbxProcess = Runtime.getRuntime().exec(dbxPathName); | |
524 // dbxOutStreamMonitor = new StreamMonitor(dbxProcess.getInputStream()); | |
525 // dbxErrStreamMonitor = new StreamMonitor(dbxProcess.getErrorStream()); | |
526 dbxOutStreamMonitor = new StreamMonitor(dbxProcess.getInputStream(), "dbx stdout", true); | |
527 dbxErrStreamMonitor = new StreamMonitor(dbxProcess.getErrorStream(), "dbx stderr", true); | |
528 } | |
529 | |
530 /** Requires that dbxErrStreamMonitor has a trigger on "dbx: Cannot | |
531 find" with number DBX_MODULE_NOT_FOUND as well as one on "dbx: | |
532 warning:" (plus the serviceability agent's dbx module path name, | |
533 to avoid conflation with inability to load individual object | |
534 files) with number DBX_MODULE_FAILED_TO_LOAD. The former | |
535 indicates an absence of libsvc_agent_dbx.so, while the latter | |
536 indicates that the module failed to load, specifically because | |
537 the architecture was mismatched. (I don't see a way to detect | |
538 from the dbx command prompt whether it's running the v8 or v9 | |
539 executbale, so we try to import both flavors of the import | |
540 module; the "v8" file name convention doesn't actually include | |
541 the v8 prefix, so this code should work for Intel as well.) */ | |
542 private void importDbxModule() throws DebuggerException { | |
543 // Trigger for a successful load | |
544 dbxOutStreamMonitor.addTrigger("Defining svc_agent_run", DBX_MODULE_LOADED); | |
545 for (int i = 0; i < dbxSvcAgentDSOPathNames.length; i++) { | |
546 dbxOstr.println("import " + dbxSvcAgentDSOPathNames[i]); | |
547 dbxOstr.println("kprint -u2 \\(Ready\\)"); | |
548 boolean seen = dbxErrStreamMonitor.waitFor("(Ready)", LONG_TIMEOUT); | |
549 if (!seen) { | |
550 detach(); | |
551 throw new DebuggerException("Timed out while importing dbx module from file\n" + dbxSvcAgentDSOPathNames[i]); | |
552 } | |
553 List retVals = dbxErrStreamMonitor.getTriggersSeen(); | |
554 if (retVals.contains(new Integer(DBX_MODULE_NOT_FOUND))) { | |
555 detach(); | |
556 throw new DebuggerException("Unable to find the Serviceability Agent's dbx import module at pathname \"" + | |
557 dbxSvcAgentDSOPathNames[i] + "\""); | |
558 } else { | |
559 retVals = dbxOutStreamMonitor.getTriggersSeen(); | |
560 if (retVals.contains(new Integer(DBX_MODULE_LOADED))) { | |
561 System.out.println("importDbxModule: imported " + dbxSvcAgentDSOPathNames[i]); | |
562 return; | |
563 } | |
564 } | |
565 } | |
566 | |
567 // Failed to load all flavors | |
568 detach(); | |
569 String errMsg = ("Unable to find a version of the Serviceability Agent's dbx import module\n" + | |
570 "matching the architecture of dbx at any of the following locations:"); | |
571 for (int i = 0; i < dbxSvcAgentDSOPathNames.length; i++) { | |
572 errMsg = errMsg + "\n" + dbxSvcAgentDSOPathNames[i]; | |
573 } | |
574 throw new DebuggerException(errMsg); | |
575 } | |
576 | |
577 /** Terminate the debugger forcibly */ | |
578 private void shutdown() { | |
579 | |
580 if (dbxProcess != null) { | |
581 // See whether the process has exited and, if not, terminate it | |
582 // forcibly | |
583 try { | |
584 dbxProcess.exitValue(); | |
585 } | |
586 catch (IllegalThreadStateException e) { | |
587 dbxProcess.destroy(); | |
588 } | |
589 } | |
590 | |
591 try { | |
592 if (importModuleSocket != null) { | |
593 importModuleSocket.close(); | |
594 } | |
595 } | |
596 catch (IOException e) { | |
597 } | |
598 | |
599 // Release references to all objects | |
600 clear(); | |
601 clearCache(); | |
602 } | |
603 | |
604 /** Looks up an address in the remote process's address space. | |
605 Returns 0 if symbol not found or upon error. Package private to | |
606 allow DbxDebuggerRemoteIntfImpl access. */ | |
607 synchronized long lookupInProcess(String objectName, String symbol) { | |
608 try { | |
609 printlnToOutput("lookup " + objectName + " " + symbol); | |
610 return in.parseAddress(); | |
611 } | |
612 catch (Exception e) { | |
613 return 0; | |
614 } | |
615 } | |
616 | |
617 /** This reads bytes from the remote process. */ | |
618 public synchronized ReadResult readBytesFromProcess(long address, long numBytes) | |
619 throws DebuggerException { | |
620 if (numBytes < 0) { | |
621 throw new DebuggerException("Can not read negative number (" + numBytes + ") of bytes from process"); | |
622 } | |
623 try { | |
624 String cmd = "peek " + utils.addressValueToString(address) + " " + numBytes; | |
625 printlnToOutput(cmd); | |
626 while (in.readByte() != 'B') { | |
627 } | |
628 byte res = in.readByte(); | |
629 if (res == 0) { | |
630 System.err.println("Failing command: " + cmd); | |
631 throw new DebuggerException("Read of remote process address space failed"); | |
632 } | |
633 // NOTE: must read ALL of the data regardless of whether we need | |
634 // to throw an UnmappedAddressException. Otherwise will corrupt | |
635 // the input stream each time we have a failure. Not good. Do | |
636 // not want to risk "flushing" the input stream in case a huge | |
637 // read has a hangup in the middle and we leave data on the | |
638 // stream. | |
639 byte[] buf = new byte[(int) numBytes]; | |
640 boolean bailOut = false; | |
641 long failureAddress = 0; | |
642 int numReads = 0; | |
643 while (numBytes > 0) { | |
644 long len = in.readUnsignedInt(); | |
645 boolean isMapped = ((in.readByte() == 0) ? false : true); | |
646 if (!isMapped) { | |
647 if (!bailOut) { | |
648 bailOut = true; | |
649 failureAddress = address; | |
650 } | |
651 } else { | |
652 // This won't work if we have unmapped regions, but if we do | |
653 // then we're going to throw an exception anyway | |
654 | |
655 // NOTE: there is a factor of 20 speed difference between | |
656 // these two ways of doing this read. | |
657 in.readBytes(buf, 0, (int) len); | |
658 } | |
659 | |
660 // Do NOT do this: | |
661 // for (int i = 0; i < (int) len; i++) { | |
662 // buf[i] = in.readByte(); | |
663 // } | |
664 | |
665 numBytes -= len; | |
666 address += len; | |
667 ++numReads; | |
668 } | |
669 if (Assert.ASSERTS_ENABLED) { | |
670 Assert.that(numBytes == 0, "Bug in debug server's implementation of peek: numBytesLeft == " + | |
671 numBytes + ", should be 0 (did " + numReads + " reads)"); | |
672 } | |
673 if (bailOut) { | |
674 return new ReadResult(failureAddress); | |
675 } | |
676 return new ReadResult(buf); | |
677 } | |
678 catch (IOException e) { | |
679 throw new DebuggerException(e); | |
680 } | |
681 } | |
682 | |
683 public void writeBytesToProcess(long address, long numBytes, byte[] data) | |
684 throws UnmappedAddressException, DebuggerException { | |
685 // FIXME | |
686 throw new DebuggerException("Unimplemented"); | |
687 } | |
688 | |
689 /** This provides DbxDebuggerRemoteIntfImpl access to readBytesFromProcess */ | |
690 ReadResult readBytesFromProcessInternal(long address, long numBytes) | |
691 throws DebuggerException { | |
692 return readBytesFromProcess(address, numBytes); | |
693 } | |
694 | |
695 /** Convenience routine */ | |
696 private void printlnToOutput(String s) throws IOException { | |
697 out.println(s); | |
698 if (out.checkError()) { | |
699 throw new IOException("Error occurred while writing to debug server"); | |
700 } | |
701 } | |
702 | |
703 private void clear() { | |
704 dbxProcess = null; | |
705 dbxOstr = null; | |
706 out = null; | |
707 in = null; | |
708 importModuleSocket = null; | |
709 } | |
710 | |
711 /** Connects to the dbx import module, setting up out and in | |
712 streams. Factored out to allow access to the dbx console. */ | |
713 private void connectToImportModule() throws IOException { | |
714 // Try for 20 seconds to connect to dbx import module; time out | |
715 // with failure if didn't succeed | |
716 importModuleSocket = null; | |
717 long endTime = System.currentTimeMillis() + LONG_TIMEOUT; | |
718 | |
719 while ((importModuleSocket == null) && (System.currentTimeMillis() < endTime)) { | |
720 try { | |
721 importModuleSocket = new Socket(InetAddress.getLocalHost(), PORT); | |
722 importModuleSocket.setTcpNoDelay(true); | |
723 } | |
724 catch (IOException e) { | |
725 // Swallow IO exceptions while attempting connection | |
726 try { | |
727 // Don't swamp the CPU | |
728 Thread.sleep(1000); | |
729 } | |
730 catch (InterruptedException ex) { | |
731 } | |
732 } | |
733 } | |
734 | |
735 if (importModuleSocket == null) { | |
736 // Failed to connect because of timeout | |
737 detach(); | |
738 throw new DebuggerException("Timed out while attempting to connect to remote dbx process"); | |
739 } | |
740 | |
741 out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(importModuleSocket.getOutputStream(), "US-ASCII")), true); | |
742 in = new InputLexer(new BufferedInputStream(importModuleSocket.getInputStream())); | |
743 } | |
744 } |