Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/test/hotspot/jtreg/vmTestbase/nsk/share/jdb/Jdb.java
41161 views
1
/*
2
* Copyright (c) 2002, 2018, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20
* or visit www.oracle.com if you need additional information or have any
21
* questions.
22
*/
23
24
package nsk.share.jdb;
25
26
import nsk.share.*;
27
import nsk.share.jpda.*;
28
29
import java.util.*;
30
import java.io.*;
31
32
import java.util.regex.*;
33
34
/**
35
* Wrapper of <i>jdb</i>.s
36
* This class provides abilities to launch it, to send command,
37
* to read reply on the command, to set breakpoint on entry in debugggee's method.
38
*/
39
public class Jdb extends LocalProcess implements Finalizable {
40
/** File to log <i>stdout</i> stream */
41
static final String JDB_STDOUT_FILE = "jdb.stdout";
42
/** File to log <i>stderr</i> stream */
43
static final String JDB_STDERR_FILE = "jdb.stderr";
44
/** File to log input jdb's commands */
45
static final String JDB_COMMANDS_FILE = "jdb.commands";
46
47
/** File to log emulated <i>jdb</i> session composed from commands and <i>jdb</i> replies on them */
48
static final String JDB_SESSION_FILE = "jdb.session";
49
50
/** Pattern for message of listening at address. */
51
public static final String LISTENING_AT_ADDRESS = "Listening at address:";
52
53
/** Pattern for message of a breakpoint hit. */
54
public static final String BREAKPOINT_HIT = "Breakpoint hit:";
55
56
/** Pattern for message of an application exit. */
57
public static final String APPLICATION_EXIT = "The application exited";
58
59
/** Pattern for message of an application disconnect. */
60
public static final String APPLICATION_DISCONNECTED = "The application has been disconnected";
61
62
/** Pattern for message of connector name in the supported connectors list. */
63
public static final String SUPPORTED_CONNECTOR_NAME = "Connector:";
64
65
/** Pattern for message of transport name in the supported connectors list. */
66
public static final String SUPPORTED_TRANSPORT_NAME = "Transport: ";
67
68
/** This is jdb's prompt when debuggee is not started nor suspended after breakpoint */
69
public static final String SIMPLE_PROMPT = "> ";
70
71
public static final String lineSeparator = System.getProperty("line.separator");
72
73
/** Internal streams handlers */
74
private static PrintStream jdbStdinWriter;
75
private static JdbStdoutReader jdbStdoutReader;
76
private static JdbStderrReader jdbStderrReader;
77
78
private static PrintStream fout;
79
private static PrintStream flog;
80
private static PrintStream fin;
81
82
/** Particular ident in a compound prompt, or null if any. */
83
private String compoundPromptIdent = null;
84
85
/** <i>Launcher</i> that creates this <i>Jdb</i> object. */
86
private static Launcher launcher = null;
87
88
/** Internal buffer to save all not-null string from <i>jdb</i> stdout */
89
volatile private static StringBuffer stdoutBuffer = new StringBuffer();
90
91
volatile private Object startNotify = new Object();
92
93
/** Returns <i>Launcher</i> that created this <i>Jdb</i> object. */
94
public static Launcher getLauncher() {
95
return launcher;
96
}
97
98
public void finalizeAtExit() throws Throwable {
99
finalize();
100
}
101
102
public void finalize() throws Throwable {
103
if (fout != null) {
104
// fout.flush();
105
fout.close();
106
}
107
if (flog != null) {
108
flog.close();
109
}
110
if (fin != null) {
111
fin.close();
112
}
113
if (jdbStdoutReader != null) {
114
jdbStdoutReader.close();
115
}
116
if (jdbStderrReader != null) {
117
jdbStderrReader.close();
118
}
119
super.finalize();
120
}
121
122
/** Create <i>Jdb</i> object. */
123
public Jdb (Launcher launcher) {
124
super();
125
this.launcher = launcher;
126
}
127
128
/** Set particular ident for compound prompt; or null for any ident. */
129
void setCompoundPromptIdent(String ident) {
130
compoundPromptIdent = ident;
131
}
132
133
/**
134
* Launch <i>jdb</i> with options defined in <i>launchCmdArgs</i>.
135
* Full path to <i>jdb</i> must be defined as first element in <i>launchCmdArgs</i>.
136
*/
137
public void launch(String[] launchCmdArgs) throws IOException {
138
super.launch(launchCmdArgs);
139
redirectStreams();
140
}
141
142
/**
143
* Launch <i>jdb</i> with options defined in <i>launchCmdLine</i>.
144
* Full path to <i>jdb</i> must be defined as first token in <i>launchCmdLine</i>.
145
*/
146
public void launch(String launchCmdLine) throws IOException {
147
super.launch(launchCmdLine);
148
redirectStreams();
149
}
150
151
/**
152
* Gets <i>stdin, stdout, stderr</i> streams of the <i>jdb's</i> process and
153
* redirects them to special streams handlers.
154
*/
155
private void redirectStreams() {
156
OutputStream jdbStdin = this.getStdin();
157
if (jdbStdin == null) {
158
throw new Failure("jdb stdin after launching is null");
159
}
160
jdbStdinWriter = new PrintStream(jdbStdin, true);
161
162
String fileStdout = getLauncher().getJdbArgumentHandler().getWorkDir() + File.separator + JDB_STDOUT_FILE;
163
InputStream jdbStdout = this.getStdout();
164
if (jdbStdout == null) {
165
throw new Failure("jdb stdout after launching is null");
166
}
167
168
launcher.getLog().display("Creating file for jdb stdout stream: " + fileStdout);
169
try {
170
fout = new PrintStream(new BufferedOutputStream(new FileOutputStream(fileStdout)));
171
} catch (Exception e) {
172
e.printStackTrace(getLauncher().getLog().getOutStream());
173
throw new Failure("Caught unexpected exception while creating file for jdb stdout stream: " + e);
174
}
175
176
String fileCommands = getLauncher().getJdbArgumentHandler().getWorkDir() + File.separator + JDB_COMMANDS_FILE;
177
try {
178
fin = new PrintStream(new BufferedOutputStream(new FileOutputStream(fileCommands)));
179
} catch (Exception e) {
180
e.printStackTrace(getLauncher().getLog().getOutStream());
181
throw new Failure("Caught unexpected exception while creating file for jdb input commands: " + e);
182
}
183
184
String fileSession = getLauncher().getJdbArgumentHandler().getWorkDir() + File.separator + JDB_SESSION_FILE;
185
launcher.getLog().display("Creating file for jdb session: " + fileSession);
186
try {
187
flog = new PrintStream(new BufferedOutputStream(new FileOutputStream(fileSession)));
188
} catch (Exception e) {
189
e.printStackTrace(getLauncher().getLog().getOutStream());
190
throw new Failure("Caught unexpected exception while creating file for jdb session: " + e);
191
}
192
193
String fileStderr = getLauncher().getJdbArgumentHandler().getWorkDir() + File.separator + JDB_STDERR_FILE;
194
InputStream jdbStderr = this.getStderr();
195
if (jdbStderr == null) {
196
throw new Failure("jdb stderr after launching is null");
197
}
198
199
jdbStdoutReader = new JdbStdoutReader(this);
200
startReader(jdbStdoutReader);
201
202
jdbStderrReader = new JdbStderrReader(this, fileStderr);
203
startReader(jdbStderrReader);
204
}
205
206
/** Starts reading threads for jdb streams. */
207
private void startReader (Thread reader) {
208
long max = getLauncher().getJdbArgumentHandler().getWaitTime() * 60 * 1000; // maximum time to wait.
209
boolean notified = false;
210
synchronized (startNotify) {
211
reader.start();
212
try {
213
startNotify.wait(max);
214
notified = true;
215
} catch (InterruptedException ie) {
216
ie.printStackTrace(getLauncher().getLog().getOutStream());
217
throw new Failure("Caught InterruptedException while waiting for start of : " + reader, ie);
218
}
219
}
220
if (!notified) {
221
throw new Failure("Main thread was not notified during " + max + " milliseconds" +
222
"\n\t waiting for start of : " + reader);
223
}
224
}
225
226
/**
227
* Waits for given stream reader of the <i>jdb's</i> process to finish
228
* or interrupts after given timeout.
229
*/
230
private void waitForReader(Thread reader, long timeMillisec) {
231
if (reader != null) {
232
try {
233
reader.join(timeMillisec);
234
} catch (InterruptedException ie) {
235
ie.printStackTrace(getLauncher().getLog().getOutStream());
236
throw new Failure("Caught interrupted exception while waiting for reader finished:\n\t" + ie);
237
}
238
if (reader.isAlive()) {
239
getLauncher().getLog().display("Interrupting reader not finished for timeout: " + timeMillisec + " millisec");
240
reader.interrupt();
241
}
242
}
243
}
244
245
/**
246
* Waits for all readers of redirected streams of the <i>jdb's</i> process
247
* to finish.
248
*/
249
private void waitForAllReaders(long timeMillisec) {
250
waitForReader(jdbStdoutReader, timeMillisec);
251
waitForReader(jdbStderrReader, timeMillisec);
252
}
253
254
/**
255
* Wait until the jdb process shutdown or crash
256
* and all redirected stream readers finished.
257
*/
258
public int waitFor() throws InterruptedException {
259
int exitCode = super.waitFor();
260
waitForAllReaders(0);
261
return exitCode;
262
}
263
264
/**
265
* Wait until the process shutdown or crash for given timeout in milliseconds,
266
* and all redirected stream readers finished.
267
* Returns <code>LocalProcess.PROCESS_IS_ALIVE</code> if process is not terminated
268
* after timeout.
269
*/
270
public int waitFor(long timeMillisec) throws InterruptedException {
271
int exitCode = super.waitFor(timeMillisec);
272
if (exitCode != LocalProcess.PROCESS_IS_ALIVE) {
273
waitForAllReaders(timeMillisec);
274
}
275
return exitCode;
276
}
277
278
/**
279
* Writes <i>jdbCommand</i> to <i>jdb's</i> input stream.
280
*/
281
public synchronized void sendCommand(String jdbCommand) {
282
283
if (terminated()) {
284
throw new Failure("Attempt to send command :" + jdbCommand + "\t to terminated jdb.");
285
}
286
287
if (jdbCommand != null) {
288
String logCmd;
289
if (!jdbCommand.endsWith(lineSeparator)) {
290
logCmd = jdbCommand;
291
jdbCommand += lineSeparator;
292
} else {
293
// we don't want to log the line separator
294
logCmd = jdbCommand.substring(0, jdbCommand.length() - lineSeparator.length());
295
}
296
launcher.getLog().display("Sending command: " + logCmd);
297
298
jdbStdinWriter.print(jdbCommand);
299
jdbStdinWriter.flush();
300
301
synchronized(flog) {
302
flog.print(/*LOG_COMMAND_PREFIX +*/ jdbCommand);
303
flog.flush();
304
}
305
306
fin.print(jdbCommand);
307
fin.flush();
308
309
if (jdbStdinWriter.checkError()) {
310
throw new Failure("Unexpected IO error while writing command <" + jdbCommand + "> to jdb stdin stream");
311
}
312
}
313
}
314
315
/**
316
* Sends command to <i>jdb's</i> input stream, waits for compound promt received,
317
* and then returns reply from <i>jdb's</i> output stream.
318
*
319
* @param command string representing full command with all arguments if any.
320
*/
321
public String[] receiveReplyFor(String command) {
322
return receiveReplyFor(command, true);
323
}
324
325
/**
326
* Sends command to <i>jdb's</i> input stream, waits for promt received,
327
* and then returns reply from <i>jdb's</i> output stream.
328
*
329
* @param command string representing full command with all arguments if any.
330
* @param compoundPromptOnly read <i>output</i> until compound prompt is found.
331
*/
332
public String[] receiveReplyFor(String command, boolean compoundPromptOnly) {
333
return receiveReplyFor(command, compoundPromptOnly, 1);
334
}
335
336
/**
337
* Sends command to <i>jdb's</i> input stream, waits for given number of promts received,
338
* and then returns reply from <i>jdb's</i> output stream.
339
*
340
* @param command string representing full command with all arguments if any.
341
* @param compoundPromptOnly read <i>output</i> until compound prompt is found.
342
* @param count number of prompt instances to found.
343
*/
344
public String[] receiveReplyFor(String command, boolean compoundPromptOnly, int count) {
345
if (command == null) {
346
return null;
347
}
348
349
int startPos = stdoutBuffer.length();
350
sendCommand(command);
351
return receiveReply(startPos, compoundPromptOnly, count);
352
}
353
354
/**
355
* Sends command to <i>jdb's</i> input stream, waits for specified message to be received,
356
* and then returns reply from <i>jdb's</i> output stream.
357
*
358
* @param command string representing full command with all arguments if any.
359
* @param waitMsg string representing the message that must be sent back before returing.
360
*/
361
public String[] receiveReplyForWithMessageWait(String command, String waitMsg) {
362
if (command == null) {
363
return null;
364
}
365
366
int startPos = stdoutBuffer.length();
367
sendCommand(command);
368
waitForMessage(startPos, waitMsg);
369
return receiveReply(startPos, true, 1);
370
}
371
372
/**
373
* Waits for compound prompt and returns reply from <i>jdb</i> stdout
374
* beginning from <i>startPos</i> in the <i>stdoutBuffer</i>.
375
*
376
* @param startPos start position for search in <i>stdoutBuffer</i>.
377
*/
378
public String[] receiveReply(int startPos) {
379
return receiveReply(startPos, true);
380
}
381
382
/**
383
* Waits for particular prompt and returns reply from <i>jdb</i> stdout
384
* beginning from <i>startPos</i> in the <i>stdoutBuffer</i>.
385
*
386
* @param startPos start position for search in <i>stdoutBuffer</i>.
387
* @param compoundPromptOnly waits for compound prompt only.
388
*/
389
public String[] receiveReply(int startPos, boolean compoundPromptOnly) {
390
return receiveReply(startPos, compoundPromptOnly, 1);
391
}
392
393
/**
394
* Waits for <i>count</i> number of prompts and returns reply from <i>jdb</i> stdout
395
* beginning from <i>startPos</i> in the <i>stdoutBuffer</i>.
396
*
397
* @param startPos start position for search in <i>stdoutBuffer</i>.
398
* @param compoundPromptOnly waits for compound prompt only.
399
* @param count number of prompt instances to wait for.
400
*/
401
public String[] receiveReply(int startPos, boolean compoundPromptOnly, int count) {
402
nsk.share.Failure e = null;
403
try {
404
waitForPrompt(startPos, compoundPromptOnly, count);
405
} catch (nsk.share.Failure nsf) {
406
e = nsf;
407
launcher.getLog().display("receiveReply FAILED due to \"" + e + "\".");
408
launcher.getLog().display("Pending reply output follows:");
409
}
410
411
String reply = stdoutBuffer.substring(startPos, stdoutBuffer.length());
412
String[] replyArr = toStringArray(reply);
413
414
// Send reply to the logfile. This complements sendCommand(), which does the same.
415
for (int i = 0; i < replyArr.length; i++) {
416
launcher.getLog().display("reply[" + i + "]: " + replyArr[i]);
417
}
418
419
if (e != null) throw e;
420
return replyArr;
421
}
422
423
/**
424
* Reads <i>JDB_STDOUT_FILE</i> file until prompt is found in the <i>stdoutBuffer</i>.
425
*
426
* @param startPos start position for search in <i>stdoutBuffer</i>.
427
* @param compoundPromptOnly search for compound prompt only.
428
* @throws Failure if prompt is not encountered during <i>WaitTime</i>.
429
* @return number of prompt instances really found.
430
*/
431
public int waitForPrompt(int startPos, boolean compoundPromptOnly) {
432
return waitForPrompt(startPos, compoundPromptOnly, 1);
433
}
434
435
/**
436
* Reads <i>JDB_STDOUT_FILE</i> file until prompt is found in the <i>stdoutBuffer</i>
437
* <i>count</i> times.
438
*
439
* @param startPos start position for search in <i>stdoutBuffer</i>.
440
* @param compoundPromptOnly search for compound prompt only.
441
* @throws Failure if prompt is not encountered <i>count</i> times during <i>WaitTime</i>.
442
* @return number of prompt instances actually found
443
*
444
* @see #setCompoundPromptIdent(String)
445
*/
446
public int waitForPrompt(int startPos, boolean compoundPromptOnly, int count) {
447
448
long delta = 200; // time in milliseconds to wait at every iteration.
449
long total = 0; // total time has waited.
450
long max = getLauncher().getJdbArgumentHandler().getWaitTime() * 60 * 1000; // maximum time to wait.
451
452
if (count <= 0) {
453
throw new TestBug("Wrong number of prompts count in Jdb.waitForPrompt(): " + count);
454
}
455
456
Object dummy = new Object();
457
while ((total += delta) <= max) {
458
int found = 0;
459
460
// check if compound prompt is found
461
{
462
found = findPrompt(stdoutBuffer, true, startPos);
463
if (found >= count) {
464
return found;
465
}
466
}
467
468
// check also if simple prompt is found
469
if (!compoundPromptOnly) {
470
found += findPrompt(stdoutBuffer, false, startPos);
471
if (found >= count) {
472
return found;
473
}
474
}
475
476
// exit loop when a debugged application exited
477
if (stdoutBuffer.indexOf(APPLICATION_EXIT) >= 0 || stdoutBuffer.indexOf(APPLICATION_DISCONNECTED) >= 0) {
478
return found;
479
} else if (startPos > 0 && !jdbStdoutReader.isAlive()) {
480
return found;
481
}
482
483
// sleep for awhile
484
synchronized(dummy) {
485
try {
486
dummy.wait(delta);
487
} catch (InterruptedException ie) {
488
ie.printStackTrace(getLauncher().getLog().getOutStream());
489
throw new Failure("Caught interrupted exception while waiting for jdb prompt:\n\t" + ie);
490
}
491
}
492
}
493
494
Pattern debuggeeExceptionPattern = Pattern.compile("Exception occurred: (?<DebuggeeException>\\S+) \\(uncaught\\)");
495
String buf = stdoutBuffer.toString();
496
Matcher m = debuggeeExceptionPattern.matcher(buf);
497
498
if (m.find(startPos)) {
499
throw new DebuggeeUncaughtException(m.group("DebuggeeException"));
500
}
501
502
String times = (count > 1 ? count + " times " : "");
503
throw new Failure("Prompt is not received " + times + "during " + total + " milliseconds.");
504
}
505
506
/**
507
* Reads <i>JDB_STDOUT_FILE</i> file until expected message is found in the <i>stdoutBuffer</i>.
508
*
509
* @param startPos start position for search in <i>stdoutBuffer</i>.
510
* @throws Failure if expected message is not encountered during <i>WaitTime</i>.
511
* @return number of messages actually found
512
*/
513
public int waitForMessage(int startPos, String message) {
514
515
long delta = 200; // time in milliseconds to wait at every iteration.
516
long total = 0; // total time has waited.
517
long max = getLauncher().getJdbArgumentHandler().getWaitTime() * 60 * 1000; // maximum time to wait.
518
519
Object dummy = new Object();
520
while ((total += delta) <= max) {
521
int found = 0;
522
523
// search for message
524
{
525
found = findMessage(startPos, message);
526
if (found > 0) {
527
return found;
528
}
529
}
530
531
// exit loop when a debugged application exited.
532
if (stdoutBuffer.indexOf(APPLICATION_EXIT) >= 0 || stdoutBuffer.indexOf(APPLICATION_DISCONNECTED) >= 0) {
533
return found;
534
} else if (startPos > 0 && !jdbStdoutReader.isAlive()) {
535
return found;
536
}
537
538
// spleep for awhile
539
synchronized(dummy) {
540
try {
541
dummy.wait(delta);
542
} catch (InterruptedException ie) {
543
ie.printStackTrace(getLauncher().getLog().getOutStream());
544
throw new Failure("Caught interrupted exception while waiting for jdb reply:\n\t" + ie);
545
}
546
}
547
548
}
549
550
// If we never recieved the expected reply, display a warning, and also
551
// display what we did recieve. This is accomplished by calling receiveReply().
552
Log log = getLauncher().getLog();
553
log.display("WARNING: message not recieved: " + message);
554
log.display("Remaining debugger output follows:");
555
receiveReply(startPos);
556
throw new Failure("Expected message not received during " + total + " milliseconds:"
557
+ "\n\t" + message);
558
}
559
560
/**
561
* Find message in <i>JDB_STDOUT_FILE</i> file starting from <i>startPos</i>.
562
*
563
* @param startPos start position for search in <i>stdoutBuffer</i>.
564
* @return number of messages actually found
565
*/
566
public int findMessage(int startPos, String message) {
567
int bufLength = stdoutBuffer.length();
568
int msgLength = message.length();
569
int found = 0;
570
571
for (int pos = startPos; pos < bufLength; ) {
572
pos = stdoutBuffer.indexOf(message, pos);
573
if (pos < 0) break;
574
found++;
575
pos += msgLength;
576
}
577
return found;
578
}
579
580
/**
581
* Searches input lines for <i>jdb</i> prompt of particular kind.
582
* starting from <code>startPos</code>.
583
* The possible prompt kinds are simple prompt "> " and compound prompt,
584
* that looks like '.*\[[0-9]*\] ' regexp on a single line.
585
* For example, 'main[1] ' (see setCompoundPromptIdent(String)).
586
* <p>
587
* In order to make compatible with jdk prior to 1.4.0 avoid using
588
* java.util.regex classes.
589
*
590
* @return number of prompt instances found
591
*
592
* @see #setCompoundPromptIdent(String)
593
*/
594
int findPrompt(StringBuffer lines, boolean compoundPromptOnly, int startPos) {
595
596
final String nameDelimiters = "-_";
597
598
int noPrompt = -1; // prompt is not found;
599
int simplePrompt = 1;
600
int complexPrompt = 2;
601
602
int length = lines.length();
603
int found = 0;
604
605
// search for simple prompt
606
if (!compoundPromptOnly) {
607
int promptLength = SIMPLE_PROMPT.length();
608
for (int pos = startPos; pos < length; ) {
609
pos = lines.indexOf(SIMPLE_PROMPT, pos);
610
if (pos < 0) break;
611
found++;
612
pos += promptLength;
613
}
614
return found;
615
}
616
617
// search for compound prompt
618
StringBuffer prompt = new StringBuffer(100);
619
searching:
620
for (int pos = startPos; pos < length; ) {
621
622
// skip each simbol not suitable for prompt begin
623
if (!Character.isLetterOrDigit(lines.charAt(pos))) {
624
pos++;
625
continue searching;
626
}
627
628
// check for compound prompt
629
prompt.setLength(0);
630
631
// read name (letters or digits or delimiters)
632
while (nameDelimiters.indexOf(lines.charAt(pos)) > 0
633
|| Character.isLetterOrDigit(lines.charAt(pos))
634
|| lines.charAt(pos) == '-'
635
|| lines.charAt(pos) == '_') {
636
prompt.append(lines.charAt(pos++));
637
if (pos >= length) {
638
break searching;
639
}
640
}
641
642
// read opening '['
643
if (lines.charAt(pos) != '[') {
644
continue searching;
645
}
646
prompt.append(lines.charAt(pos++));
647
if (pos >= length) {
648
break searching;
649
}
650
651
// read number (digits)
652
if (!Character.isDigit(lines.charAt(pos))){
653
continue searching;
654
}
655
while (Character.isDigit(lines.charAt(pos))) {
656
prompt.append(lines.charAt(pos++));
657
if (pos >= length) {
658
break searching;
659
}
660
}
661
662
// read closing ']'
663
if (lines.charAt(pos) != ']') {
664
continue searching;
665
}
666
prompt.append(lines.charAt(pos++));
667
if (pos >= length) {
668
break searching;
669
}
670
671
// read last ' '
672
if (lines.charAt(pos) != ' ') {
673
continue searching;
674
}
675
prompt.append(lines.charAt(pos++));
676
677
// check if not particular ident found
678
if (compoundPromptIdent != null
679
&& !prompt.toString().startsWith(compoundPromptIdent + "[")) {
680
continue searching;
681
}
682
683
// compound prompt found
684
found++;
685
}
686
687
return found;
688
}
689
690
/**
691
* Splits string which may include line separators to string array.
692
*
693
*/
694
public static String[] toStringArray (String string) {
695
Vector<String> v = new Vector<String>();
696
int ind;
697
for (ind = 0; ind < string.length(); ) {
698
int i = string.indexOf(lineSeparator, ind);
699
if (i >= 0) {
700
v.add(string.substring(ind, i));
701
ind = i + lineSeparator.length();
702
} else {
703
v.add(string.substring(ind));
704
break;
705
}
706
}
707
String[] result = new String [v.size()];
708
v.toArray(result);
709
return result;
710
}
711
712
/**
713
* Set breakpoint for debuggee on method invocation.
714
*/
715
public void setBreakpointInMethod(String methodName) {
716
String nextCommand = JdbCommand.stop_in + methodName;
717
String[] reply = receiveReplyFor(nextCommand);
718
719
Paragrep grep = new Paragrep(reply);
720
if (grep.find("Unable to set") > 0) {
721
throw new Failure("jdb failed to set breakpoint in method: " + methodName);
722
}
723
if (grep.find("Set breakpoint") <= 0 && grep.find("Deferring breakpoint") <= 0) {
724
throw new Failure("jdb did not set breakpoint in method: " + methodName);
725
}
726
}
727
728
/**
729
* Set deferred breakpoint for debuggee on method invocation.
730
* This method must be used before <run> command.
731
*/
732
public void setDeferredBreakpointInMethod(String methodName) {
733
String nextCommand = JdbCommand.stop_in + methodName;
734
String[] reply = receiveReplyFor(nextCommand, false);
735
736
Paragrep grep = new Paragrep(reply);
737
if (grep.find("Unable to set") > 0) {
738
throw new Failure("jdb failed to set deffered breakpoint in method: " + methodName);
739
}
740
if (grep.find("Set breakpoint") <= 0 && grep.find("Deferring breakpoint") <= 0) {
741
throw new Failure("jdb did not set deffered breakpoint in method: " + methodName);
742
}
743
}
744
745
/**
746
* Returns true if reply contains breakpoint message.
747
*/
748
public boolean isAtBreakpoint(String[] reply) {
749
return isAtBreakpoint(reply, "", "");
750
}
751
752
/**
753
* Returns true if reply contains breakpoint message in certain method.
754
*/
755
public boolean isAtBreakpoint(String[] reply, String method) {
756
return isAtBreakpoint(reply, method, "");
757
}
758
759
/**
760
* Returns true if reply contains breakpoint message in certain method
761
* and in certain thread id.
762
*/
763
public boolean isAtBreakpoint(String[] reply, String method, String thread) {
764
boolean result = false;
765
Vector<String> v = new Vector<String>();
766
Paragrep grep = new Paragrep(reply);
767
768
v.add(BREAKPOINT_HIT);
769
if (method.length() > 0) {
770
v.add(method);
771
}
772
if (thread.length() > 0) {
773
v.add(thread);
774
}
775
if (grep.find(v) > 0) {
776
result = true;
777
}
778
return result;
779
}
780
781
/**
782
* Load and start execution of given debuggee's class with arguments.
783
*/
784
public void startDebuggeeClass(String classWithArgs) {
785
String[] reply = receiveReplyFor(JdbCommand.run + " " + classWithArgs);
786
787
// give one more chance to reach breakpoint
788
if (!isAtBreakpoint(getTotalReply(), "main")) {
789
waitForMessage(0, BREAKPOINT_HIT);
790
}
791
}
792
793
/**
794
* Start execution of pre-loaded debuggee's class.
795
*/
796
public void startDebuggeeClass() {
797
String[] reply = receiveReplyFor(JdbCommand.run);
798
799
// give one more chance to reach breakpoint
800
if (!isAtBreakpoint(getTotalReply(), "main")) {
801
waitForMessage(0, BREAKPOINT_HIT);
802
}
803
}
804
805
/**
806
* Returns as string array all id's for a given <i>threadName</i>.
807
*/
808
public String[] getThreadIds(String threadName) {
809
810
if (!threadName.startsWith("(")) {
811
threadName = "(" + threadName;
812
}
813
if (!threadName.endsWith(")")) {
814
threadName = threadName + ")";
815
}
816
817
Vector<String> v = new Vector<String>();
818
String[] reply = receiveReplyFor(JdbCommand.threads);
819
Paragrep grep = new Paragrep(reply);
820
821
String[] found = grep.findStrings(threadName);
822
for (int i = 0; i < found.length; i++) {
823
String string = found[i];
824
int j = string.indexOf(threadName);
825
if (j >= 0) {
826
j += threadName.length();
827
String threadId = string.substring(j, string.indexOf(" ", j));
828
v.add(threadId);
829
}
830
}
831
832
String[] result = new String [v.size()];
833
v.toArray(result);
834
return result;
835
}
836
837
/**
838
* Quit <i>jdb</i> using "quit" command.
839
*/
840
public void quit() {
841
if (!terminated()) {
842
sendCommand(JdbCommand.quit);
843
}
844
}
845
846
/**
847
* Sends "cont" command up to maxTimes until debuggee exit.
848
*/
849
public void contToExit (int maxTimes) {
850
boolean exited = false;
851
for (int i = 0; i < maxTimes; i++) {
852
if (!terminated()) {
853
String [] reply = receiveReplyFor(JdbCommand.cont);
854
Paragrep grep = new Paragrep(reply);
855
if (grep.find(APPLICATION_EXIT) > 0) {
856
exited = true;
857
break;
858
}
859
} else {
860
exited = true;
861
break;
862
}
863
}
864
if (!exited) {
865
if (terminated()) {
866
exited = true;
867
} else {
868
quit();
869
throw new Failure("Debuggee did not exit after " + maxTimes + " <cont> commands");
870
}
871
}
872
}
873
874
/**
875
* Returns string array containing all strings from <i>jdb</i> stdout.
876
*/
877
public String[] getTotalReply() {
878
return toStringArray(stdoutBuffer.toString());
879
}
880
881
/**
882
* Prints given message to log files and adds to <i>stdoutBuffer</i>.
883
*/
884
public void logToFile(String s) {
885
synchronized(fout) {
886
fout.print(s);
887
fout.flush();
888
}
889
synchronized(stdoutBuffer) {
890
stdoutBuffer.append(s);
891
}
892
synchronized(flog) {
893
flog.print(s);
894
flog.flush();
895
}
896
}
897
898
899
/**
900
* Starts jdb with attaching connector. Makes several tries during <i>waitTime</i>
901
* until success. Unsuccessful launches are caused that the debuggee is not yet
902
* ready to accept debugger.
903
*/
904
public static Jdb startAttachingJdb (Launcher launcher, String[] jdbCmdArgs, String message)
905
throws IOException {
906
Jdb jdb = null;
907
908
long delta = Launcher.DEBUGGEE_START_DELAY; // time in milliseconds to wait at every iteration.
909
long max = getLauncher().getJdbArgumentHandler().getWaitTime() * 60 * 1000; // maximum time to wait.
910
911
int result = -1;
912
boolean found = false;
913
914
long start = System.currentTimeMillis();
915
916
while (!found && (System.currentTimeMillis() - start)<= max) {
917
918
jdb = new Jdb(launcher);
919
jdb.launch(jdbCmdArgs);
920
921
while (!found && (System.currentTimeMillis() - start)<= max) {
922
923
try {
924
Thread.currentThread().sleep(delta);
925
} catch (InterruptedException ie) {
926
ie.printStackTrace(getLauncher().getLog().getOutStream());
927
throw new Failure("Caught unexpected InterruptedException while sleep in waiting for debuggee's start:\n\t"
928
+ ie);
929
}
930
931
if (jdb.terminated() ||
932
!jdbStdoutReader.isAlive() ||
933
stdoutBuffer.indexOf(APPLICATION_EXIT) >= 0 ||
934
stdoutBuffer.indexOf(APPLICATION_DISCONNECTED) >= 0) {
935
936
System.out.println("Unsuccessful launch of attaching jdb. Next try...");
937
try {
938
jdb.finalize();
939
} catch (Throwable t) {
940
t.printStackTrace(getLauncher().getLog().getOutStream());
941
throw new Failure("Caught unexpected error while finalizing jdb: " + t);
942
}
943
break;
944
945
} else if (stdoutBuffer.length() > 0) {
946
result = stdoutBuffer.indexOf(message);
947
if (result >= 0) {
948
found = true; // exit loop
949
}
950
}
951
}
952
953
}
954
955
if (result < 0) {
956
throw new Failure("Launched jdb could not attach to debuggee during " + max + " milliseconds.");
957
}
958
959
return jdb;
960
}
961
962
/**
963
* Waits for jdb to print message about listening at address for connection,
964
* and returns this address string.
965
*/
966
public String waitForListeningJdb() {
967
968
waitForMessage(0, LISTENING_AT_ADDRESS);
969
int msgStart = stdoutBuffer.indexOf(LISTENING_AT_ADDRESS);
970
int msgEnd = stdoutBuffer.indexOf("\n", msgStart);
971
int promptLen = LISTENING_AT_ADDRESS.length();
972
973
/*
974
* The LISTENING_AT_ADDRESS string and the terminating "\n"
975
* may or may not be included in the same read so we allow
976
* this message to be terminated by "\n" or NULL.
977
*/
978
if (msgEnd < 0) {
979
msgEnd = stdoutBuffer.length();
980
}
981
982
if (msgEnd <= 0 || msgEnd - msgStart <= promptLen) {
983
throw new Failure("Unknown format of message: " + LISTENING_AT_ADDRESS);
984
}
985
986
int addrStart = msgStart + promptLen;
987
String address = stdoutBuffer.substring(addrStart, msgEnd).trim();
988
989
if (address.length() <= 0) {
990
throw new Failure("Empty address in message: " + LISTENING_AT_ADDRESS);
991
}
992
993
return address;
994
}
995
996
// ---------------------------------------------- //
997
998
class JdbStdoutReader extends Thread {
999
private Jdb jdb = null;
1000
private InputStream in = null;
1001
1002
volatile boolean stop = false;
1003
1004
public JdbStdoutReader (Jdb jdb) {
1005
super("jdb stdout reader");
1006
this.jdb = jdb;
1007
this.in = jdb.getStdout();
1008
if (in == null) {
1009
throw new Failure("Can not get jdb stdout stream");
1010
}
1011
this.setDaemon(true);
1012
}
1013
1014
public String toString() {
1015
return getClass().getName() + '@' + Integer.toHexString(hashCode());
1016
}
1017
1018
public void run() {
1019
synchronized(jdb.startNotify) {
1020
jdb.startNotify.notifyAll();
1021
}
1022
1023
long delta = 10; // time in milliseconds to wait at every iteration.
1024
boolean jdbWasTerminated = false;
1025
while (!stop) {
1026
if(jdb.terminated())
1027
jdbWasTerminated = true;
1028
try {
1029
int size = in.available();
1030
if (size > 0) {
1031
byte[] buffer = new byte [size];
1032
int result = in.read(buffer, 0, size);
1033
if (result < 0) {
1034
throw new Failure("No bytes read from jdb's output stream ");
1035
} else if (result < size) {
1036
throw new Failure("Number bytes read from jdb's output stream are less than available " +
1037
"\n\t available : " + size + ", read : " + result);
1038
}
1039
logToFile(new String(buffer, 0, result));
1040
}
1041
} catch (Exception e) {
1042
e.printStackTrace(jdb.getLauncher().getLog().getOutStream());
1043
throw new Failure("Caught unexpected exception while reading jdb's stdout stream: " + e);
1044
}
1045
if(jdbWasTerminated)
1046
break;
1047
try {
1048
sleep(delta);
1049
} catch (InterruptedException ie) {
1050
ie.printStackTrace(jdb.getLauncher().getLog().getOutStream());
1051
throw new Failure("Caught interrupted exception while waiting for jdb reply:\n\t" + ie);
1052
}
1053
}
1054
}
1055
1056
public void close() {
1057
stop = true;
1058
try {
1059
if (in != null) {
1060
in.close();
1061
}
1062
} catch (IOException ioe) {
1063
ioe.printStackTrace(jdb.getLauncher().getLog().getOutStream());
1064
throw new Failure("Caught unexpected IOException while closing jdb stdout stream: " + ioe);
1065
}
1066
}
1067
}
1068
1069
/** Handler for <i>jdb</i> stderr stream. */
1070
class JdbStderrReader extends Thread {
1071
1072
private Jdb jdb = null;
1073
private volatile boolean cancelled = false;
1074
private boolean empty = true;
1075
1076
private BufferedReader bin;
1077
private PrintStream fout;
1078
private String fileName;
1079
1080
JdbStderrReader (Jdb jdb, String jdbStderrFile) {
1081
super("jdb stderr reader");
1082
this.jdb = jdb;
1083
InputStream in = jdb.getStderr();
1084
if (in == null) {
1085
throw new Failure("Can not get jdb stderr stream");
1086
}
1087
this.bin = new BufferedReader(new InputStreamReader(in));
1088
this.setDaemon(true);
1089
1090
this.fileName = jdbStderrFile;
1091
1092
launcher.getLog().display("Creating file for jdb stderr stream: " + fileName);
1093
try {
1094
this.fout = new PrintStream(new BufferedOutputStream(new FileOutputStream(fileName)));
1095
} catch (Exception e) {
1096
e.printStackTrace(jdb.getLauncher().getLog().getOutStream());
1097
throw new Failure("Caught unexpected exception while creating file for jdb stderr stream: " + e);
1098
}
1099
}
1100
1101
public void run () {
1102
synchronized(jdb.startNotify) {
1103
jdb.startNotify.notifyAll();
1104
}
1105
1106
long delta = 10; // time in milliseconds to wait at every iteration.
1107
1108
while (!cancelled) {
1109
String line = null;
1110
try {
1111
line = bin.readLine();
1112
if (line == null)
1113
break; //EOF
1114
} catch (IOException ioe) {
1115
ioe.printStackTrace(jdb.getLauncher().getLog().getOutStream());
1116
throw new Failure("Caught unexpected IOException while reading from jdb stderr: " + ioe);
1117
}
1118
1119
if (line != null) {
1120
empty = false;
1121
logToFile(line);
1122
}
1123
1124
try {
1125
sleep(delta);
1126
} catch (InterruptedException ie) {
1127
throw new Failure("Caught interrupted exception while waiting for jdb reply:\n\t" + ie);
1128
}
1129
}
1130
close();
1131
}
1132
1133
/**
1134
* Signal to <i>run()</i> method that it should terminate,
1135
* and wait until it is finished.
1136
*/
1137
public void cancel () {
1138
cancelled = true;
1139
while (this.isAlive()) {
1140
try {
1141
this.join();
1142
} catch (InterruptedException ie) {
1143
close();
1144
throw new Failure("Caught InterruptedException while waiting for JdbStderrReader termination " + ie);
1145
}
1146
}
1147
close();
1148
}
1149
1150
public void close() {
1151
if (fout != null) {
1152
synchronized (fout) {
1153
fout.close();
1154
}
1155
}
1156
1157
try {
1158
if (bin != null) {
1159
bin.close();
1160
}
1161
} catch (IOException ioe) {
1162
ioe.printStackTrace(jdb.getLauncher().getLog().getOutStream());
1163
throw new Failure("Caught unexpected IOException while closing jdb stderr stream: " + ioe);
1164
}
1165
if (!empty) {
1166
// Should not throw exception here because of non-empty stderr in case of unsuccessful launch of attaching jdb.
1167
jdb.getLauncher().getLog().display("JdbStderrReader: jdb's stderr is not empty. Check jdb.stderr file");
1168
}
1169
}
1170
1171
public String getFileName () {
1172
return this.fileName;
1173
}
1174
1175
public void logToFile(String line) {
1176
synchronized (fout) {
1177
fout.println(line);
1178
fout.flush();
1179
}
1180
}
1181
} // end of JdbStderrReader
1182
} // end of Jdb
1183
1184