Path: blob/master/test/hotspot/jtreg/vmTestbase/nsk/share/jdb/Jdb.java
41161 views
/*1* Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223package nsk.share.jdb;2425import nsk.share.*;26import nsk.share.jpda.*;2728import java.util.*;29import java.io.*;3031import java.util.regex.*;3233/**34* Wrapper of <i>jdb</i>.s35* This class provides abilities to launch it, to send command,36* to read reply on the command, to set breakpoint on entry in debugggee's method.37*/38public class Jdb extends LocalProcess implements Finalizable {39/** File to log <i>stdout</i> stream */40static final String JDB_STDOUT_FILE = "jdb.stdout";41/** File to log <i>stderr</i> stream */42static final String JDB_STDERR_FILE = "jdb.stderr";43/** File to log input jdb's commands */44static final String JDB_COMMANDS_FILE = "jdb.commands";4546/** File to log emulated <i>jdb</i> session composed from commands and <i>jdb</i> replies on them */47static final String JDB_SESSION_FILE = "jdb.session";4849/** Pattern for message of listening at address. */50public static final String LISTENING_AT_ADDRESS = "Listening at address:";5152/** Pattern for message of a breakpoint hit. */53public static final String BREAKPOINT_HIT = "Breakpoint hit:";5455/** Pattern for message of an application exit. */56public static final String APPLICATION_EXIT = "The application exited";5758/** Pattern for message of an application disconnect. */59public static final String APPLICATION_DISCONNECTED = "The application has been disconnected";6061/** Pattern for message of connector name in the supported connectors list. */62public static final String SUPPORTED_CONNECTOR_NAME = "Connector:";6364/** Pattern for message of transport name in the supported connectors list. */65public static final String SUPPORTED_TRANSPORT_NAME = "Transport: ";6667/** This is jdb's prompt when debuggee is not started nor suspended after breakpoint */68public static final String SIMPLE_PROMPT = "> ";6970public static final String lineSeparator = System.getProperty("line.separator");7172/** Internal streams handlers */73private static PrintStream jdbStdinWriter;74private static JdbStdoutReader jdbStdoutReader;75private static JdbStderrReader jdbStderrReader;7677private static PrintStream fout;78private static PrintStream flog;79private static PrintStream fin;8081/** Particular ident in a compound prompt, or null if any. */82private String compoundPromptIdent = null;8384/** <i>Launcher</i> that creates this <i>Jdb</i> object. */85private static Launcher launcher = null;8687/** Internal buffer to save all not-null string from <i>jdb</i> stdout */88volatile private static StringBuffer stdoutBuffer = new StringBuffer();8990volatile private Object startNotify = new Object();9192/** Returns <i>Launcher</i> that created this <i>Jdb</i> object. */93public static Launcher getLauncher() {94return launcher;95}9697public void finalizeAtExit() throws Throwable {98finalize();99}100101public void finalize() throws Throwable {102if (fout != null) {103// fout.flush();104fout.close();105}106if (flog != null) {107flog.close();108}109if (fin != null) {110fin.close();111}112if (jdbStdoutReader != null) {113jdbStdoutReader.close();114}115if (jdbStderrReader != null) {116jdbStderrReader.close();117}118super.finalize();119}120121/** Create <i>Jdb</i> object. */122public Jdb (Launcher launcher) {123super();124this.launcher = launcher;125}126127/** Set particular ident for compound prompt; or null for any ident. */128void setCompoundPromptIdent(String ident) {129compoundPromptIdent = ident;130}131132/**133* Launch <i>jdb</i> with options defined in <i>launchCmdArgs</i>.134* Full path to <i>jdb</i> must be defined as first element in <i>launchCmdArgs</i>.135*/136public void launch(String[] launchCmdArgs) throws IOException {137super.launch(launchCmdArgs);138redirectStreams();139}140141/**142* Launch <i>jdb</i> with options defined in <i>launchCmdLine</i>.143* Full path to <i>jdb</i> must be defined as first token in <i>launchCmdLine</i>.144*/145public void launch(String launchCmdLine) throws IOException {146super.launch(launchCmdLine);147redirectStreams();148}149150/**151* Gets <i>stdin, stdout, stderr</i> streams of the <i>jdb's</i> process and152* redirects them to special streams handlers.153*/154private void redirectStreams() {155OutputStream jdbStdin = this.getStdin();156if (jdbStdin == null) {157throw new Failure("jdb stdin after launching is null");158}159jdbStdinWriter = new PrintStream(jdbStdin, true);160161String fileStdout = getLauncher().getJdbArgumentHandler().getWorkDir() + File.separator + JDB_STDOUT_FILE;162InputStream jdbStdout = this.getStdout();163if (jdbStdout == null) {164throw new Failure("jdb stdout after launching is null");165}166167launcher.getLog().display("Creating file for jdb stdout stream: " + fileStdout);168try {169fout = new PrintStream(new BufferedOutputStream(new FileOutputStream(fileStdout)));170} catch (Exception e) {171e.printStackTrace(getLauncher().getLog().getOutStream());172throw new Failure("Caught unexpected exception while creating file for jdb stdout stream: " + e);173}174175String fileCommands = getLauncher().getJdbArgumentHandler().getWorkDir() + File.separator + JDB_COMMANDS_FILE;176try {177fin = new PrintStream(new BufferedOutputStream(new FileOutputStream(fileCommands)));178} catch (Exception e) {179e.printStackTrace(getLauncher().getLog().getOutStream());180throw new Failure("Caught unexpected exception while creating file for jdb input commands: " + e);181}182183String fileSession = getLauncher().getJdbArgumentHandler().getWorkDir() + File.separator + JDB_SESSION_FILE;184launcher.getLog().display("Creating file for jdb session: " + fileSession);185try {186flog = new PrintStream(new BufferedOutputStream(new FileOutputStream(fileSession)));187} catch (Exception e) {188e.printStackTrace(getLauncher().getLog().getOutStream());189throw new Failure("Caught unexpected exception while creating file for jdb session: " + e);190}191192String fileStderr = getLauncher().getJdbArgumentHandler().getWorkDir() + File.separator + JDB_STDERR_FILE;193InputStream jdbStderr = this.getStderr();194if (jdbStderr == null) {195throw new Failure("jdb stderr after launching is null");196}197198jdbStdoutReader = new JdbStdoutReader(this);199startReader(jdbStdoutReader);200201jdbStderrReader = new JdbStderrReader(this, fileStderr);202startReader(jdbStderrReader);203}204205/** Starts reading threads for jdb streams. */206private void startReader (Thread reader) {207long max = getLauncher().getJdbArgumentHandler().getWaitTime() * 60 * 1000; // maximum time to wait.208boolean notified = false;209synchronized (startNotify) {210reader.start();211try {212startNotify.wait(max);213notified = true;214} catch (InterruptedException ie) {215ie.printStackTrace(getLauncher().getLog().getOutStream());216throw new Failure("Caught InterruptedException while waiting for start of : " + reader, ie);217}218}219if (!notified) {220throw new Failure("Main thread was not notified during " + max + " milliseconds" +221"\n\t waiting for start of : " + reader);222}223}224225/**226* Waits for given stream reader of the <i>jdb's</i> process to finish227* or interrupts after given timeout.228*/229private void waitForReader(Thread reader, long timeMillisec) {230if (reader != null) {231try {232reader.join(timeMillisec);233} catch (InterruptedException ie) {234ie.printStackTrace(getLauncher().getLog().getOutStream());235throw new Failure("Caught interrupted exception while waiting for reader finished:\n\t" + ie);236}237if (reader.isAlive()) {238getLauncher().getLog().display("Interrupting reader not finished for timeout: " + timeMillisec + " millisec");239reader.interrupt();240}241}242}243244/**245* Waits for all readers of redirected streams of the <i>jdb's</i> process246* to finish.247*/248private void waitForAllReaders(long timeMillisec) {249waitForReader(jdbStdoutReader, timeMillisec);250waitForReader(jdbStderrReader, timeMillisec);251}252253/**254* Wait until the jdb process shutdown or crash255* and all redirected stream readers finished.256*/257public int waitFor() throws InterruptedException {258int exitCode = super.waitFor();259waitForAllReaders(0);260return exitCode;261}262263/**264* Wait until the process shutdown or crash for given timeout in milliseconds,265* and all redirected stream readers finished.266* Returns <code>LocalProcess.PROCESS_IS_ALIVE</code> if process is not terminated267* after timeout.268*/269public int waitFor(long timeMillisec) throws InterruptedException {270int exitCode = super.waitFor(timeMillisec);271if (exitCode != LocalProcess.PROCESS_IS_ALIVE) {272waitForAllReaders(timeMillisec);273}274return exitCode;275}276277/**278* Writes <i>jdbCommand</i> to <i>jdb's</i> input stream.279*/280public synchronized void sendCommand(String jdbCommand) {281282if (terminated()) {283throw new Failure("Attempt to send command :" + jdbCommand + "\t to terminated jdb.");284}285286if (jdbCommand != null) {287String logCmd;288if (!jdbCommand.endsWith(lineSeparator)) {289logCmd = jdbCommand;290jdbCommand += lineSeparator;291} else {292// we don't want to log the line separator293logCmd = jdbCommand.substring(0, jdbCommand.length() - lineSeparator.length());294}295launcher.getLog().display("Sending command: " + logCmd);296297jdbStdinWriter.print(jdbCommand);298jdbStdinWriter.flush();299300synchronized(flog) {301flog.print(/*LOG_COMMAND_PREFIX +*/ jdbCommand);302flog.flush();303}304305fin.print(jdbCommand);306fin.flush();307308if (jdbStdinWriter.checkError()) {309throw new Failure("Unexpected IO error while writing command <" + jdbCommand + "> to jdb stdin stream");310}311}312}313314/**315* Sends command to <i>jdb's</i> input stream, waits for compound promt received,316* and then returns reply from <i>jdb's</i> output stream.317*318* @param command string representing full command with all arguments if any.319*/320public String[] receiveReplyFor(String command) {321return receiveReplyFor(command, true);322}323324/**325* Sends command to <i>jdb's</i> input stream, waits for promt received,326* and then returns reply from <i>jdb's</i> output stream.327*328* @param command string representing full command with all arguments if any.329* @param compoundPromptOnly read <i>output</i> until compound prompt is found.330*/331public String[] receiveReplyFor(String command, boolean compoundPromptOnly) {332return receiveReplyFor(command, compoundPromptOnly, 1);333}334335/**336* Sends command to <i>jdb's</i> input stream, waits for given number of promts received,337* and then returns reply from <i>jdb's</i> output stream.338*339* @param command string representing full command with all arguments if any.340* @param compoundPromptOnly read <i>output</i> until compound prompt is found.341* @param count number of prompt instances to found.342*/343public String[] receiveReplyFor(String command, boolean compoundPromptOnly, int count) {344if (command == null) {345return null;346}347348int startPos = stdoutBuffer.length();349sendCommand(command);350return receiveReply(startPos, compoundPromptOnly, count);351}352353/**354* Sends command to <i>jdb's</i> input stream, waits for specified message to be received,355* and then returns reply from <i>jdb's</i> output stream.356*357* @param command string representing full command with all arguments if any.358* @param waitMsg string representing the message that must be sent back before returing.359*/360public String[] receiveReplyForWithMessageWait(String command, String waitMsg) {361if (command == null) {362return null;363}364365int startPos = stdoutBuffer.length();366sendCommand(command);367waitForMessage(startPos, waitMsg);368return receiveReply(startPos, true, 1);369}370371/**372* Waits for compound prompt and returns reply from <i>jdb</i> stdout373* beginning from <i>startPos</i> in the <i>stdoutBuffer</i>.374*375* @param startPos start position for search in <i>stdoutBuffer</i>.376*/377public String[] receiveReply(int startPos) {378return receiveReply(startPos, true);379}380381/**382* Waits for particular prompt and returns reply from <i>jdb</i> stdout383* beginning from <i>startPos</i> in the <i>stdoutBuffer</i>.384*385* @param startPos start position for search in <i>stdoutBuffer</i>.386* @param compoundPromptOnly waits for compound prompt only.387*/388public String[] receiveReply(int startPos, boolean compoundPromptOnly) {389return receiveReply(startPos, compoundPromptOnly, 1);390}391392/**393* Waits for <i>count</i> number of prompts and returns reply from <i>jdb</i> stdout394* beginning from <i>startPos</i> in the <i>stdoutBuffer</i>.395*396* @param startPos start position for search in <i>stdoutBuffer</i>.397* @param compoundPromptOnly waits for compound prompt only.398* @param count number of prompt instances to wait for.399*/400public String[] receiveReply(int startPos, boolean compoundPromptOnly, int count) {401nsk.share.Failure e = null;402try {403waitForPrompt(startPos, compoundPromptOnly, count);404} catch (nsk.share.Failure nsf) {405e = nsf;406launcher.getLog().display("receiveReply FAILED due to \"" + e + "\".");407launcher.getLog().display("Pending reply output follows:");408}409410String reply = stdoutBuffer.substring(startPos, stdoutBuffer.length());411String[] replyArr = toStringArray(reply);412413// Send reply to the logfile. This complements sendCommand(), which does the same.414for (int i = 0; i < replyArr.length; i++) {415launcher.getLog().display("reply[" + i + "]: " + replyArr[i]);416}417418if (e != null) throw e;419return replyArr;420}421422/**423* Reads <i>JDB_STDOUT_FILE</i> file until prompt is found in the <i>stdoutBuffer</i>.424*425* @param startPos start position for search in <i>stdoutBuffer</i>.426* @param compoundPromptOnly search for compound prompt only.427* @throws Failure if prompt is not encountered during <i>WaitTime</i>.428* @return number of prompt instances really found.429*/430public int waitForPrompt(int startPos, boolean compoundPromptOnly) {431return waitForPrompt(startPos, compoundPromptOnly, 1);432}433434/**435* Reads <i>JDB_STDOUT_FILE</i> file until prompt is found in the <i>stdoutBuffer</i>436* <i>count</i> times.437*438* @param startPos start position for search in <i>stdoutBuffer</i>.439* @param compoundPromptOnly search for compound prompt only.440* @throws Failure if prompt is not encountered <i>count</i> times during <i>WaitTime</i>.441* @return number of prompt instances actually found442*443* @see #setCompoundPromptIdent(String)444*/445public int waitForPrompt(int startPos, boolean compoundPromptOnly, int count) {446447long delta = 200; // time in milliseconds to wait at every iteration.448long total = 0; // total time has waited.449long max = getLauncher().getJdbArgumentHandler().getWaitTime() * 60 * 1000; // maximum time to wait.450451if (count <= 0) {452throw new TestBug("Wrong number of prompts count in Jdb.waitForPrompt(): " + count);453}454455Object dummy = new Object();456while ((total += delta) <= max) {457int found = 0;458459// check if compound prompt is found460{461found = findPrompt(stdoutBuffer, true, startPos);462if (found >= count) {463return found;464}465}466467// check also if simple prompt is found468if (!compoundPromptOnly) {469found += findPrompt(stdoutBuffer, false, startPos);470if (found >= count) {471return found;472}473}474475// exit loop when a debugged application exited476if (stdoutBuffer.indexOf(APPLICATION_EXIT) >= 0 || stdoutBuffer.indexOf(APPLICATION_DISCONNECTED) >= 0) {477return found;478} else if (startPos > 0 && !jdbStdoutReader.isAlive()) {479return found;480}481482// sleep for awhile483synchronized(dummy) {484try {485dummy.wait(delta);486} catch (InterruptedException ie) {487ie.printStackTrace(getLauncher().getLog().getOutStream());488throw new Failure("Caught interrupted exception while waiting for jdb prompt:\n\t" + ie);489}490}491}492493Pattern debuggeeExceptionPattern = Pattern.compile("Exception occurred: (?<DebuggeeException>\\S+) \\(uncaught\\)");494String buf = stdoutBuffer.toString();495Matcher m = debuggeeExceptionPattern.matcher(buf);496497if (m.find(startPos)) {498throw new DebuggeeUncaughtException(m.group("DebuggeeException"));499}500501String times = (count > 1 ? count + " times " : "");502throw new Failure("Prompt is not received " + times + "during " + total + " milliseconds.");503}504505/**506* Reads <i>JDB_STDOUT_FILE</i> file until expected message is found in the <i>stdoutBuffer</i>.507*508* @param startPos start position for search in <i>stdoutBuffer</i>.509* @throws Failure if expected message is not encountered during <i>WaitTime</i>.510* @return number of messages actually found511*/512public int waitForMessage(int startPos, String message) {513514long delta = 200; // time in milliseconds to wait at every iteration.515long total = 0; // total time has waited.516long max = getLauncher().getJdbArgumentHandler().getWaitTime() * 60 * 1000; // maximum time to wait.517518Object dummy = new Object();519while ((total += delta) <= max) {520int found = 0;521522// search for message523{524found = findMessage(startPos, message);525if (found > 0) {526return found;527}528}529530// exit loop when a debugged application exited.531if (stdoutBuffer.indexOf(APPLICATION_EXIT) >= 0 || stdoutBuffer.indexOf(APPLICATION_DISCONNECTED) >= 0) {532return found;533} else if (startPos > 0 && !jdbStdoutReader.isAlive()) {534return found;535}536537// spleep for awhile538synchronized(dummy) {539try {540dummy.wait(delta);541} catch (InterruptedException ie) {542ie.printStackTrace(getLauncher().getLog().getOutStream());543throw new Failure("Caught interrupted exception while waiting for jdb reply:\n\t" + ie);544}545}546547}548549// If we never recieved the expected reply, display a warning, and also550// display what we did recieve. This is accomplished by calling receiveReply().551Log log = getLauncher().getLog();552log.display("WARNING: message not recieved: " + message);553log.display("Remaining debugger output follows:");554receiveReply(startPos);555throw new Failure("Expected message not received during " + total + " milliseconds:"556+ "\n\t" + message);557}558559/**560* Find message in <i>JDB_STDOUT_FILE</i> file starting from <i>startPos</i>.561*562* @param startPos start position for search in <i>stdoutBuffer</i>.563* @return number of messages actually found564*/565public int findMessage(int startPos, String message) {566int bufLength = stdoutBuffer.length();567int msgLength = message.length();568int found = 0;569570for (int pos = startPos; pos < bufLength; ) {571pos = stdoutBuffer.indexOf(message, pos);572if (pos < 0) break;573found++;574pos += msgLength;575}576return found;577}578579/**580* Searches input lines for <i>jdb</i> prompt of particular kind.581* starting from <code>startPos</code>.582* The possible prompt kinds are simple prompt "> " and compound prompt,583* that looks like '.*\[[0-9]*\] ' regexp on a single line.584* For example, 'main[1] ' (see setCompoundPromptIdent(String)).585* <p>586* In order to make compatible with jdk prior to 1.4.0 avoid using587* java.util.regex classes.588*589* @return number of prompt instances found590*591* @see #setCompoundPromptIdent(String)592*/593int findPrompt(StringBuffer lines, boolean compoundPromptOnly, int startPos) {594595final String nameDelimiters = "-_";596597int noPrompt = -1; // prompt is not found;598int simplePrompt = 1;599int complexPrompt = 2;600601int length = lines.length();602int found = 0;603604// search for simple prompt605if (!compoundPromptOnly) {606int promptLength = SIMPLE_PROMPT.length();607for (int pos = startPos; pos < length; ) {608pos = lines.indexOf(SIMPLE_PROMPT, pos);609if (pos < 0) break;610found++;611pos += promptLength;612}613return found;614}615616// search for compound prompt617StringBuffer prompt = new StringBuffer(100);618searching:619for (int pos = startPos; pos < length; ) {620621// skip each simbol not suitable for prompt begin622if (!Character.isLetterOrDigit(lines.charAt(pos))) {623pos++;624continue searching;625}626627// check for compound prompt628prompt.setLength(0);629630// read name (letters or digits or delimiters)631while (nameDelimiters.indexOf(lines.charAt(pos)) > 0632|| Character.isLetterOrDigit(lines.charAt(pos))633|| lines.charAt(pos) == '-'634|| lines.charAt(pos) == '_') {635prompt.append(lines.charAt(pos++));636if (pos >= length) {637break searching;638}639}640641// read opening '['642if (lines.charAt(pos) != '[') {643continue searching;644}645prompt.append(lines.charAt(pos++));646if (pos >= length) {647break searching;648}649650// read number (digits)651if (!Character.isDigit(lines.charAt(pos))){652continue searching;653}654while (Character.isDigit(lines.charAt(pos))) {655prompt.append(lines.charAt(pos++));656if (pos >= length) {657break searching;658}659}660661// read closing ']'662if (lines.charAt(pos) != ']') {663continue searching;664}665prompt.append(lines.charAt(pos++));666if (pos >= length) {667break searching;668}669670// read last ' '671if (lines.charAt(pos) != ' ') {672continue searching;673}674prompt.append(lines.charAt(pos++));675676// check if not particular ident found677if (compoundPromptIdent != null678&& !prompt.toString().startsWith(compoundPromptIdent + "[")) {679continue searching;680}681682// compound prompt found683found++;684}685686return found;687}688689/**690* Splits string which may include line separators to string array.691*692*/693public static String[] toStringArray (String string) {694Vector<String> v = new Vector<String>();695int ind;696for (ind = 0; ind < string.length(); ) {697int i = string.indexOf(lineSeparator, ind);698if (i >= 0) {699v.add(string.substring(ind, i));700ind = i + lineSeparator.length();701} else {702v.add(string.substring(ind));703break;704}705}706String[] result = new String [v.size()];707v.toArray(result);708return result;709}710711/**712* Set breakpoint for debuggee on method invocation.713*/714public void setBreakpointInMethod(String methodName) {715String nextCommand = JdbCommand.stop_in + methodName;716String[] reply = receiveReplyFor(nextCommand);717718Paragrep grep = new Paragrep(reply);719if (grep.find("Unable to set") > 0) {720throw new Failure("jdb failed to set breakpoint in method: " + methodName);721}722if (grep.find("Set breakpoint") <= 0 && grep.find("Deferring breakpoint") <= 0) {723throw new Failure("jdb did not set breakpoint in method: " + methodName);724}725}726727/**728* Set deferred breakpoint for debuggee on method invocation.729* This method must be used before <run> command.730*/731public void setDeferredBreakpointInMethod(String methodName) {732String nextCommand = JdbCommand.stop_in + methodName;733String[] reply = receiveReplyFor(nextCommand, false);734735Paragrep grep = new Paragrep(reply);736if (grep.find("Unable to set") > 0) {737throw new Failure("jdb failed to set deffered breakpoint in method: " + methodName);738}739if (grep.find("Set breakpoint") <= 0 && grep.find("Deferring breakpoint") <= 0) {740throw new Failure("jdb did not set deffered breakpoint in method: " + methodName);741}742}743744/**745* Returns true if reply contains breakpoint message.746*/747public boolean isAtBreakpoint(String[] reply) {748return isAtBreakpoint(reply, "", "");749}750751/**752* Returns true if reply contains breakpoint message in certain method.753*/754public boolean isAtBreakpoint(String[] reply, String method) {755return isAtBreakpoint(reply, method, "");756}757758/**759* Returns true if reply contains breakpoint message in certain method760* and in certain thread id.761*/762public boolean isAtBreakpoint(String[] reply, String method, String thread) {763boolean result = false;764Vector<String> v = new Vector<String>();765Paragrep grep = new Paragrep(reply);766767v.add(BREAKPOINT_HIT);768if (method.length() > 0) {769v.add(method);770}771if (thread.length() > 0) {772v.add(thread);773}774if (grep.find(v) > 0) {775result = true;776}777return result;778}779780/**781* Load and start execution of given debuggee's class with arguments.782*/783public void startDebuggeeClass(String classWithArgs) {784String[] reply = receiveReplyFor(JdbCommand.run + " " + classWithArgs);785786// give one more chance to reach breakpoint787if (!isAtBreakpoint(getTotalReply(), "main")) {788waitForMessage(0, BREAKPOINT_HIT);789}790}791792/**793* Start execution of pre-loaded debuggee's class.794*/795public void startDebuggeeClass() {796String[] reply = receiveReplyFor(JdbCommand.run);797798// give one more chance to reach breakpoint799if (!isAtBreakpoint(getTotalReply(), "main")) {800waitForMessage(0, BREAKPOINT_HIT);801}802}803804/**805* Returns as string array all id's for a given <i>threadName</i>.806*/807public String[] getThreadIds(String threadName) {808809if (!threadName.startsWith("(")) {810threadName = "(" + threadName;811}812if (!threadName.endsWith(")")) {813threadName = threadName + ")";814}815816Vector<String> v = new Vector<String>();817String[] reply = receiveReplyFor(JdbCommand.threads);818Paragrep grep = new Paragrep(reply);819820String[] found = grep.findStrings(threadName);821for (int i = 0; i < found.length; i++) {822String string = found[i];823int j = string.indexOf(threadName);824if (j >= 0) {825j += threadName.length();826String threadId = string.substring(j, string.indexOf(" ", j));827v.add(threadId);828}829}830831String[] result = new String [v.size()];832v.toArray(result);833return result;834}835836/**837* Quit <i>jdb</i> using "quit" command.838*/839public void quit() {840if (!terminated()) {841sendCommand(JdbCommand.quit);842}843}844845/**846* Sends "cont" command up to maxTimes until debuggee exit.847*/848public void contToExit (int maxTimes) {849boolean exited = false;850for (int i = 0; i < maxTimes; i++) {851if (!terminated()) {852String [] reply = receiveReplyFor(JdbCommand.cont);853Paragrep grep = new Paragrep(reply);854if (grep.find(APPLICATION_EXIT) > 0) {855exited = true;856break;857}858} else {859exited = true;860break;861}862}863if (!exited) {864if (terminated()) {865exited = true;866} else {867quit();868throw new Failure("Debuggee did not exit after " + maxTimes + " <cont> commands");869}870}871}872873/**874* Returns string array containing all strings from <i>jdb</i> stdout.875*/876public String[] getTotalReply() {877return toStringArray(stdoutBuffer.toString());878}879880/**881* Prints given message to log files and adds to <i>stdoutBuffer</i>.882*/883public void logToFile(String s) {884synchronized(fout) {885fout.print(s);886fout.flush();887}888synchronized(stdoutBuffer) {889stdoutBuffer.append(s);890}891synchronized(flog) {892flog.print(s);893flog.flush();894}895}896897898/**899* Starts jdb with attaching connector. Makes several tries during <i>waitTime</i>900* until success. Unsuccessful launches are caused that the debuggee is not yet901* ready to accept debugger.902*/903public static Jdb startAttachingJdb (Launcher launcher, String[] jdbCmdArgs, String message)904throws IOException {905Jdb jdb = null;906907long delta = Launcher.DEBUGGEE_START_DELAY; // time in milliseconds to wait at every iteration.908long max = getLauncher().getJdbArgumentHandler().getWaitTime() * 60 * 1000; // maximum time to wait.909910int result = -1;911boolean found = false;912913long start = System.currentTimeMillis();914915while (!found && (System.currentTimeMillis() - start)<= max) {916917jdb = new Jdb(launcher);918jdb.launch(jdbCmdArgs);919920while (!found && (System.currentTimeMillis() - start)<= max) {921922try {923Thread.currentThread().sleep(delta);924} catch (InterruptedException ie) {925ie.printStackTrace(getLauncher().getLog().getOutStream());926throw new Failure("Caught unexpected InterruptedException while sleep in waiting for debuggee's start:\n\t"927+ ie);928}929930if (jdb.terminated() ||931!jdbStdoutReader.isAlive() ||932stdoutBuffer.indexOf(APPLICATION_EXIT) >= 0 ||933stdoutBuffer.indexOf(APPLICATION_DISCONNECTED) >= 0) {934935System.out.println("Unsuccessful launch of attaching jdb. Next try...");936try {937jdb.finalize();938} catch (Throwable t) {939t.printStackTrace(getLauncher().getLog().getOutStream());940throw new Failure("Caught unexpected error while finalizing jdb: " + t);941}942break;943944} else if (stdoutBuffer.length() > 0) {945result = stdoutBuffer.indexOf(message);946if (result >= 0) {947found = true; // exit loop948}949}950}951952}953954if (result < 0) {955throw new Failure("Launched jdb could not attach to debuggee during " + max + " milliseconds.");956}957958return jdb;959}960961/**962* Waits for jdb to print message about listening at address for connection,963* and returns this address string.964*/965public String waitForListeningJdb() {966967waitForMessage(0, LISTENING_AT_ADDRESS);968int msgStart = stdoutBuffer.indexOf(LISTENING_AT_ADDRESS);969int msgEnd = stdoutBuffer.indexOf("\n", msgStart);970int promptLen = LISTENING_AT_ADDRESS.length();971972/*973* The LISTENING_AT_ADDRESS string and the terminating "\n"974* may or may not be included in the same read so we allow975* this message to be terminated by "\n" or NULL.976*/977if (msgEnd < 0) {978msgEnd = stdoutBuffer.length();979}980981if (msgEnd <= 0 || msgEnd - msgStart <= promptLen) {982throw new Failure("Unknown format of message: " + LISTENING_AT_ADDRESS);983}984985int addrStart = msgStart + promptLen;986String address = stdoutBuffer.substring(addrStart, msgEnd).trim();987988if (address.length() <= 0) {989throw new Failure("Empty address in message: " + LISTENING_AT_ADDRESS);990}991992return address;993}994995// ---------------------------------------------- //996997class JdbStdoutReader extends Thread {998private Jdb jdb = null;999private InputStream in = null;10001001volatile boolean stop = false;10021003public JdbStdoutReader (Jdb jdb) {1004super("jdb stdout reader");1005this.jdb = jdb;1006this.in = jdb.getStdout();1007if (in == null) {1008throw new Failure("Can not get jdb stdout stream");1009}1010this.setDaemon(true);1011}10121013public String toString() {1014return getClass().getName() + '@' + Integer.toHexString(hashCode());1015}10161017public void run() {1018synchronized(jdb.startNotify) {1019jdb.startNotify.notifyAll();1020}10211022long delta = 10; // time in milliseconds to wait at every iteration.1023boolean jdbWasTerminated = false;1024while (!stop) {1025if(jdb.terminated())1026jdbWasTerminated = true;1027try {1028int size = in.available();1029if (size > 0) {1030byte[] buffer = new byte [size];1031int result = in.read(buffer, 0, size);1032if (result < 0) {1033throw new Failure("No bytes read from jdb's output stream ");1034} else if (result < size) {1035throw new Failure("Number bytes read from jdb's output stream are less than available " +1036"\n\t available : " + size + ", read : " + result);1037}1038logToFile(new String(buffer, 0, result));1039}1040} catch (Exception e) {1041e.printStackTrace(jdb.getLauncher().getLog().getOutStream());1042throw new Failure("Caught unexpected exception while reading jdb's stdout stream: " + e);1043}1044if(jdbWasTerminated)1045break;1046try {1047sleep(delta);1048} catch (InterruptedException ie) {1049ie.printStackTrace(jdb.getLauncher().getLog().getOutStream());1050throw new Failure("Caught interrupted exception while waiting for jdb reply:\n\t" + ie);1051}1052}1053}10541055public void close() {1056stop = true;1057try {1058if (in != null) {1059in.close();1060}1061} catch (IOException ioe) {1062ioe.printStackTrace(jdb.getLauncher().getLog().getOutStream());1063throw new Failure("Caught unexpected IOException while closing jdb stdout stream: " + ioe);1064}1065}1066}10671068/** Handler for <i>jdb</i> stderr stream. */1069class JdbStderrReader extends Thread {10701071private Jdb jdb = null;1072private volatile boolean cancelled = false;1073private boolean empty = true;10741075private BufferedReader bin;1076private PrintStream fout;1077private String fileName;10781079JdbStderrReader (Jdb jdb, String jdbStderrFile) {1080super("jdb stderr reader");1081this.jdb = jdb;1082InputStream in = jdb.getStderr();1083if (in == null) {1084throw new Failure("Can not get jdb stderr stream");1085}1086this.bin = new BufferedReader(new InputStreamReader(in));1087this.setDaemon(true);10881089this.fileName = jdbStderrFile;10901091launcher.getLog().display("Creating file for jdb stderr stream: " + fileName);1092try {1093this.fout = new PrintStream(new BufferedOutputStream(new FileOutputStream(fileName)));1094} catch (Exception e) {1095e.printStackTrace(jdb.getLauncher().getLog().getOutStream());1096throw new Failure("Caught unexpected exception while creating file for jdb stderr stream: " + e);1097}1098}10991100public void run () {1101synchronized(jdb.startNotify) {1102jdb.startNotify.notifyAll();1103}11041105long delta = 10; // time in milliseconds to wait at every iteration.11061107while (!cancelled) {1108String line = null;1109try {1110line = bin.readLine();1111if (line == null)1112break; //EOF1113} catch (IOException ioe) {1114ioe.printStackTrace(jdb.getLauncher().getLog().getOutStream());1115throw new Failure("Caught unexpected IOException while reading from jdb stderr: " + ioe);1116}11171118if (line != null) {1119empty = false;1120logToFile(line);1121}11221123try {1124sleep(delta);1125} catch (InterruptedException ie) {1126throw new Failure("Caught interrupted exception while waiting for jdb reply:\n\t" + ie);1127}1128}1129close();1130}11311132/**1133* Signal to <i>run()</i> method that it should terminate,1134* and wait until it is finished.1135*/1136public void cancel () {1137cancelled = true;1138while (this.isAlive()) {1139try {1140this.join();1141} catch (InterruptedException ie) {1142close();1143throw new Failure("Caught InterruptedException while waiting for JdbStderrReader termination " + ie);1144}1145}1146close();1147}11481149public void close() {1150if (fout != null) {1151synchronized (fout) {1152fout.close();1153}1154}11551156try {1157if (bin != null) {1158bin.close();1159}1160} catch (IOException ioe) {1161ioe.printStackTrace(jdb.getLauncher().getLog().getOutStream());1162throw new Failure("Caught unexpected IOException while closing jdb stderr stream: " + ioe);1163}1164if (!empty) {1165// Should not throw exception here because of non-empty stderr in case of unsuccessful launch of attaching jdb.1166jdb.getLauncher().getLog().display("JdbStderrReader: jdb's stderr is not empty. Check jdb.stderr file");1167}1168}11691170public String getFileName () {1171return this.fileName;1172}11731174public void logToFile(String line) {1175synchronized (fout) {1176fout.println(line);1177fout.flush();1178}1179}1180} // end of JdbStderrReader1181} // end of Jdb118211831184