0
|
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.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
|
|
463 /** From the DbxDebugger interface */
|
|
464 public DbxOopHandle readOopHandle(long address)
|
|
465 throws UnmappedAddressException, UnalignedAddressException, NotInHeapException {
|
|
466 long value = readAddressValue(address);
|
|
467 return (value == 0 ? null : new DbxOopHandle(this, value));
|
|
468 }
|
|
469
|
|
470 //--------------------------------------------------------------------------------
|
|
471 // Thread context access. Can not be package private, but should
|
|
472 // only be accessed by the architecture-specific subpackages.
|
|
473
|
|
474 /** From the DbxDebugger interface. May have to redefine this later. */
|
|
475 public synchronized long[] getThreadIntegerRegisterSet(int tid) {
|
|
476 try {
|
|
477 printlnToOutput("thr_gregs " + tid);
|
|
478 int num = in.parseInt();
|
|
479 long[] res = new long[num];
|
|
480 for (int i = 0; i < num; i++) {
|
|
481 res[i] = in.parseAddress();
|
|
482 }
|
|
483 return res;
|
|
484 }
|
|
485 catch (Exception e) {
|
|
486 e.printStackTrace();
|
|
487 return null;
|
|
488 }
|
|
489 }
|
|
490
|
|
491 //--------------------------------------------------------------------------------
|
|
492 // Address access. Can not be package private, but should only be
|
|
493 // accessed by the architecture-specific subpackages.
|
|
494
|
|
495 /** From the Debugger interface */
|
|
496 public long getAddressValue(Address addr) {
|
|
497 if (addr == null) return 0;
|
|
498 return ((DbxAddress) addr).getValue();
|
|
499 }
|
|
500
|
|
501 /** From the DbxDebugger interface */
|
|
502 public Address newAddress(long value) {
|
|
503 if (value == 0) return null;
|
|
504 return new DbxAddress(this, value);
|
|
505 }
|
|
506
|
|
507 //--------------------------------------------------------------------------------
|
|
508 // Internals only below this point
|
|
509 //
|
|
510
|
|
511 private void launchProcess() throws IOException {
|
|
512 dbxProcess = Runtime.getRuntime().exec(dbxPathName);
|
|
513 // dbxOutStreamMonitor = new StreamMonitor(dbxProcess.getInputStream());
|
|
514 // dbxErrStreamMonitor = new StreamMonitor(dbxProcess.getErrorStream());
|
|
515 dbxOutStreamMonitor = new StreamMonitor(dbxProcess.getInputStream(), "dbx stdout", true);
|
|
516 dbxErrStreamMonitor = new StreamMonitor(dbxProcess.getErrorStream(), "dbx stderr", true);
|
|
517 }
|
|
518
|
|
519 /** Requires that dbxErrStreamMonitor has a trigger on "dbx: Cannot
|
|
520 find" with number DBX_MODULE_NOT_FOUND as well as one on "dbx:
|
|
521 warning:" (plus the serviceability agent's dbx module path name,
|
|
522 to avoid conflation with inability to load individual object
|
|
523 files) with number DBX_MODULE_FAILED_TO_LOAD. The former
|
|
524 indicates an absence of libsvc_agent_dbx.so, while the latter
|
|
525 indicates that the module failed to load, specifically because
|
|
526 the architecture was mismatched. (I don't see a way to detect
|
|
527 from the dbx command prompt whether it's running the v8 or v9
|
|
528 executbale, so we try to import both flavors of the import
|
|
529 module; the "v8" file name convention doesn't actually include
|
|
530 the v8 prefix, so this code should work for Intel as well.) */
|
|
531 private void importDbxModule() throws DebuggerException {
|
|
532 // Trigger for a successful load
|
|
533 dbxOutStreamMonitor.addTrigger("Defining svc_agent_run", DBX_MODULE_LOADED);
|
|
534 for (int i = 0; i < dbxSvcAgentDSOPathNames.length; i++) {
|
|
535 dbxOstr.println("import " + dbxSvcAgentDSOPathNames[i]);
|
|
536 dbxOstr.println("kprint -u2 \\(Ready\\)");
|
|
537 boolean seen = dbxErrStreamMonitor.waitFor("(Ready)", LONG_TIMEOUT);
|
|
538 if (!seen) {
|
|
539 detach();
|
|
540 throw new DebuggerException("Timed out while importing dbx module from file\n" + dbxSvcAgentDSOPathNames[i]);
|
|
541 }
|
|
542 List retVals = dbxErrStreamMonitor.getTriggersSeen();
|
|
543 if (retVals.contains(new Integer(DBX_MODULE_NOT_FOUND))) {
|
|
544 detach();
|
|
545 throw new DebuggerException("Unable to find the Serviceability Agent's dbx import module at pathname \"" +
|
|
546 dbxSvcAgentDSOPathNames[i] + "\"");
|
|
547 } else {
|
|
548 retVals = dbxOutStreamMonitor.getTriggersSeen();
|
|
549 if (retVals.contains(new Integer(DBX_MODULE_LOADED))) {
|
|
550 System.out.println("importDbxModule: imported " + dbxSvcAgentDSOPathNames[i]);
|
|
551 return;
|
|
552 }
|
|
553 }
|
|
554 }
|
|
555
|
|
556 // Failed to load all flavors
|
|
557 detach();
|
|
558 String errMsg = ("Unable to find a version of the Serviceability Agent's dbx import module\n" +
|
|
559 "matching the architecture of dbx at any of the following locations:");
|
|
560 for (int i = 0; i < dbxSvcAgentDSOPathNames.length; i++) {
|
|
561 errMsg = errMsg + "\n" + dbxSvcAgentDSOPathNames[i];
|
|
562 }
|
|
563 throw new DebuggerException(errMsg);
|
|
564 }
|
|
565
|
|
566 /** Terminate the debugger forcibly */
|
|
567 private void shutdown() {
|
|
568
|
|
569 if (dbxProcess != null) {
|
|
570 // See whether the process has exited and, if not, terminate it
|
|
571 // forcibly
|
|
572 try {
|
|
573 dbxProcess.exitValue();
|
|
574 }
|
|
575 catch (IllegalThreadStateException e) {
|
|
576 dbxProcess.destroy();
|
|
577 }
|
|
578 }
|
|
579
|
|
580 try {
|
|
581 if (importModuleSocket != null) {
|
|
582 importModuleSocket.close();
|
|
583 }
|
|
584 }
|
|
585 catch (IOException e) {
|
|
586 }
|
|
587
|
|
588 // Release references to all objects
|
|
589 clear();
|
|
590 clearCache();
|
|
591 }
|
|
592
|
|
593 /** Looks up an address in the remote process's address space.
|
|
594 Returns 0 if symbol not found or upon error. Package private to
|
|
595 allow DbxDebuggerRemoteIntfImpl access. */
|
|
596 synchronized long lookupInProcess(String objectName, String symbol) {
|
|
597 try {
|
|
598 printlnToOutput("lookup " + objectName + " " + symbol);
|
|
599 return in.parseAddress();
|
|
600 }
|
|
601 catch (Exception e) {
|
|
602 return 0;
|
|
603 }
|
|
604 }
|
|
605
|
|
606 /** This reads bytes from the remote process. */
|
|
607 public synchronized ReadResult readBytesFromProcess(long address, long numBytes)
|
|
608 throws DebuggerException {
|
|
609 if (numBytes < 0) {
|
|
610 throw new DebuggerException("Can not read negative number (" + numBytes + ") of bytes from process");
|
|
611 }
|
|
612 try {
|
|
613 String cmd = "peek " + utils.addressValueToString(address) + " " + numBytes;
|
|
614 printlnToOutput(cmd);
|
|
615 while (in.readByte() != 'B') {
|
|
616 }
|
|
617 byte res = in.readByte();
|
|
618 if (res == 0) {
|
|
619 System.err.println("Failing command: " + cmd);
|
|
620 throw new DebuggerException("Read of remote process address space failed");
|
|
621 }
|
|
622 // NOTE: must read ALL of the data regardless of whether we need
|
|
623 // to throw an UnmappedAddressException. Otherwise will corrupt
|
|
624 // the input stream each time we have a failure. Not good. Do
|
|
625 // not want to risk "flushing" the input stream in case a huge
|
|
626 // read has a hangup in the middle and we leave data on the
|
|
627 // stream.
|
|
628 byte[] buf = new byte[(int) numBytes];
|
|
629 boolean bailOut = false;
|
|
630 long failureAddress = 0;
|
|
631 int numReads = 0;
|
|
632 while (numBytes > 0) {
|
|
633 long len = in.readUnsignedInt();
|
|
634 boolean isMapped = ((in.readByte() == 0) ? false : true);
|
|
635 if (!isMapped) {
|
|
636 if (!bailOut) {
|
|
637 bailOut = true;
|
|
638 failureAddress = address;
|
|
639 }
|
|
640 } else {
|
|
641 // This won't work if we have unmapped regions, but if we do
|
|
642 // then we're going to throw an exception anyway
|
|
643
|
|
644 // NOTE: there is a factor of 20 speed difference between
|
|
645 // these two ways of doing this read.
|
|
646 in.readBytes(buf, 0, (int) len);
|
|
647 }
|
|
648
|
|
649 // Do NOT do this:
|
|
650 // for (int i = 0; i < (int) len; i++) {
|
|
651 // buf[i] = in.readByte();
|
|
652 // }
|
|
653
|
|
654 numBytes -= len;
|
|
655 address += len;
|
|
656 ++numReads;
|
|
657 }
|
|
658 if (Assert.ASSERTS_ENABLED) {
|
|
659 Assert.that(numBytes == 0, "Bug in debug server's implementation of peek: numBytesLeft == " +
|
|
660 numBytes + ", should be 0 (did " + numReads + " reads)");
|
|
661 }
|
|
662 if (bailOut) {
|
|
663 return new ReadResult(failureAddress);
|
|
664 }
|
|
665 return new ReadResult(buf);
|
|
666 }
|
|
667 catch (IOException e) {
|
|
668 throw new DebuggerException(e);
|
|
669 }
|
|
670 }
|
|
671
|
|
672 public void writeBytesToProcess(long address, long numBytes, byte[] data)
|
|
673 throws UnmappedAddressException, DebuggerException {
|
|
674 // FIXME
|
|
675 throw new DebuggerException("Unimplemented");
|
|
676 }
|
|
677
|
|
678 /** This provides DbxDebuggerRemoteIntfImpl access to readBytesFromProcess */
|
|
679 ReadResult readBytesFromProcessInternal(long address, long numBytes)
|
|
680 throws DebuggerException {
|
|
681 return readBytesFromProcess(address, numBytes);
|
|
682 }
|
|
683
|
|
684 /** Convenience routine */
|
|
685 private void printlnToOutput(String s) throws IOException {
|
|
686 out.println(s);
|
|
687 if (out.checkError()) {
|
|
688 throw new IOException("Error occurred while writing to debug server");
|
|
689 }
|
|
690 }
|
|
691
|
|
692 private void clear() {
|
|
693 dbxProcess = null;
|
|
694 dbxOstr = null;
|
|
695 out = null;
|
|
696 in = null;
|
|
697 importModuleSocket = null;
|
|
698 }
|
|
699
|
|
700 /** Connects to the dbx import module, setting up out and in
|
|
701 streams. Factored out to allow access to the dbx console. */
|
|
702 private void connectToImportModule() throws IOException {
|
|
703 // Try for 20 seconds to connect to dbx import module; time out
|
|
704 // with failure if didn't succeed
|
|
705 importModuleSocket = null;
|
|
706 long endTime = System.currentTimeMillis() + LONG_TIMEOUT;
|
|
707
|
|
708 while ((importModuleSocket == null) && (System.currentTimeMillis() < endTime)) {
|
|
709 try {
|
|
710 importModuleSocket = new Socket(InetAddress.getLocalHost(), PORT);
|
|
711 importModuleSocket.setTcpNoDelay(true);
|
|
712 }
|
|
713 catch (IOException e) {
|
|
714 // Swallow IO exceptions while attempting connection
|
|
715 try {
|
|
716 // Don't swamp the CPU
|
|
717 Thread.sleep(1000);
|
|
718 }
|
|
719 catch (InterruptedException ex) {
|
|
720 }
|
|
721 }
|
|
722 }
|
|
723
|
|
724 if (importModuleSocket == null) {
|
|
725 // Failed to connect because of timeout
|
|
726 detach();
|
|
727 throw new DebuggerException("Timed out while attempting to connect to remote dbx process");
|
|
728 }
|
|
729
|
|
730 out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(importModuleSocket.getOutputStream(), "US-ASCII")), true);
|
|
731 in = new InputLexer(new BufferedInputStream(importModuleSocket.getInputStream()));
|
|
732 }
|
|
733 }
|