Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  Jdb.java   Sprache: JAVA

 
/*
 * Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */


package nsk.share.jdb;

import nsk.share.*;
import nsk.share.jpda.*;

import java.util.*;
import java.io.*;

import java.util.regex.*;

/**
 * Wrapper of <i>jdb</i>.s
 * This class provides abilities to launch it, to send command,
 * to read reply on the command, to set breakpoint on entry in debugggee's method.
 */

public class Jdb extends LocalProcess implements Finalizable {
    /** File to log <i>stdout</i> stream */
    static final String JDB_STDOUT_FILE = "jdb.stdout";
    /** File to log <i>stderr</i> stream */
    static final String JDB_STDERR_FILE = "jdb.stderr";
    /** File to log input jdb's commands */
    static final String JDB_COMMANDS_FILE = "jdb.commands";

    /** File to log emulated <i>jdb</i> session composed from commands and <i>jdb</i> replies on them  */
    static final String JDB_SESSION_FILE = "jdb.session";

    /** Pattern for message of listening at address. */
    public static final String LISTENING_AT_ADDRESS = "Listening at address:";

    /** Pattern for message of a breakpoint hit. */
    public static final String BREAKPOINT_HIT = "Breakpoint hit:";

    /** Pattern for message of an application exit. */
    public static final String APPLICATION_EXIT = "The application exited";

    /** Pattern for message of an application disconnect. */
    public static final String APPLICATION_DISCONNECTED = "The application has been disconnected";

    /** Pattern for message of connector name in the supported connectors list. */
    public static final String SUPPORTED_CONNECTOR_NAME = "Connector:";

    /** Pattern for message of transport name in the supported connectors list. */
    public static final String SUPPORTED_TRANSPORT_NAME = "Transport: ";

    /** This is jdb's prompt when debuggee is not started nor suspended after breakpoint */
    public static final String SIMPLE_PROMPT = "> ";

    public static final String lineSeparator = System.getProperty("line.separator");

    /** Internal streams handlers */
    private static PrintStream     jdbStdinWriter;
    private static JdbStdoutReader jdbStdoutReader;
    private static JdbStderrReader jdbStderrReader;

    private static PrintStream fout;
    private static PrintStream flog;
    private static PrintStream fin;

    /** Particular ident in a compound prompt, or null if any. */
    private String compoundPromptIdent = null;

    /** <i>Launcher</i> that creates this <i>Jdb</i> object. */
    private static Launcher launcher = null;

    /** Internal buffer to save all not-null string from <i>jdb</i> stdout */
    volatile private static StringBuffer stdoutBuffer = new StringBuffer();

    volatile private Object startNotify = new Object();

    /** Returns <i>Launcher</i> that created this <i>Jdb</i> object. */
    public static Launcher getLauncher() {
        return launcher;
    }

    public void finalizeAtExit() throws Throwable {
        finalize();
    }

    public void finalize() throws Throwable {
        if (fout != null) {
//            fout.flush();
            fout.close();
        }
        if (flog != null) {
            flog.close();
        }
        if (fin != null) {
            fin.close();
        }
        if (jdbStdoutReader != null) {
            jdbStdoutReader.close();
        }
        if (jdbStderrReader != null) {
            jdbStderrReader.close();
        }
        super.finalize();
    }

    /** Create <i>Jdb</i> object. */
    public Jdb (Launcher launcher) {
        super();
        this.launcher = launcher;
    }

    /** Set particular ident for compound prompt; or null for any ident. */
    void setCompoundPromptIdent(String ident) {
        compoundPromptIdent = ident;
    }

    /**
     * Launch <i>jdb</i> with options defined in <i>launchCmdArgs</i>.
     * Full path to <i>jdb</i> must be defined as first element in <i>launchCmdArgs</i>.
     */

    public void launch(String[] launchCmdArgs) throws IOException {
        super.launch(launchCmdArgs);
        redirectStreams();
    }

    /**
     * Launch <i>jdb</i> with options defined in <i>launchCmdLine</i>.
     * Full path to <i>jdb</i> must be defined as first token in <i>launchCmdLine</i>.
     */

    public void launch(String launchCmdLine) throws IOException {
        super.launch(launchCmdLine);
        redirectStreams();
    }

    /**
     * Gets <i>stdin, stdout, stderr</i> streams of the <i>jdb's</i> process and
     * redirects them to special streams handlers.
     */

    private void redirectStreams() {
        OutputStream jdbStdin = this.getStdin();
        if (jdbStdin == null) {
            throw new Failure("jdb stdin after launching is null");
        }
        jdbStdinWriter = new PrintStream(jdbStdin, true);

        String fileStdout = getLauncher().getJdbArgumentHandler().getWorkDir() + File.separator + JDB_STDOUT_FILE;
        InputStream jdbStdout = this.getStdout();
        if (jdbStdout == null) {
            throw new Failure("jdb stdout after launching is null");
        }

        launcher.getLog().display("Creating file for jdb stdout stream: " + fileStdout);
        try {
            fout = new PrintStream(new BufferedOutputStream(new FileOutputStream(fileStdout)));
        } catch (Exception e) {
            e.printStackTrace(getLauncher().getLog().getOutStream());
            throw new Failure("Caught unexpected exception while creating file for jdb stdout stream: " + e);
        }

        String fileCommands = getLauncher().getJdbArgumentHandler().getWorkDir() + File.separator + JDB_COMMANDS_FILE;
        try {
            fin = new PrintStream(new BufferedOutputStream(new FileOutputStream(fileCommands)));
        } catch (Exception e) {
            e.printStackTrace(getLauncher().getLog().getOutStream());
            throw new Failure("Caught unexpected exception while creating file for jdb input commands: " + e);
        }

        String fileSession = getLauncher().getJdbArgumentHandler().getWorkDir() + File.separator + JDB_SESSION_FILE;
        launcher.getLog().display("Creating file for jdb session: " + fileSession);
        try {
            flog = new PrintStream(new BufferedOutputStream(new FileOutputStream(fileSession)));
        } catch (Exception e) {
            e.printStackTrace(getLauncher().getLog().getOutStream());
            throw new Failure("Caught unexpected exception while creating file for jdb session: " + e);
        }

        String fileStderr = getLauncher().getJdbArgumentHandler().getWorkDir() + File.separator + JDB_STDERR_FILE;
        InputStream jdbStderr = this.getStderr();
        if (jdbStderr == null) {
            throw new Failure("jdb stderr after launching is null");
        }

        jdbStdoutReader = new JdbStdoutReader(this);
        startReader(jdbStdoutReader);

        jdbStderrReader = new JdbStderrReader(this, fileStderr);
        startReader(jdbStderrReader);
    }

    /** Starts reading threads for jdb streams. */
    private void startReader (Thread reader) {
        long max = getLauncher().getJdbArgumentHandler().getWaitTime() * 60 * 1000;  // maximum time to wait.
        boolean notified = false;
        synchronized (startNotify) {
            reader.start();
            try {
                startNotify.wait(max);
                notified = true;
            } catch (InterruptedException ie) {
                ie.printStackTrace(getLauncher().getLog().getOutStream());
                throw new Failure("Caught InterruptedException while waiting for start of : " + reader, ie);
            }
        }
        if (!notified) {
            throw new Failure("Main thread was not notified during " + max + " milliseconds" +
                "\n\t waiting for start of : " + reader);
        }
    }

    /**
     * Waits for given stream reader of the <i>jdb's</i> process to finish
     * or interrupts after given timeout.
     */

    private void waitForReader(Thread reader, long timeMillisec) {
        if (reader != null) {
            try {
                reader.join(timeMillisec);
            } catch (InterruptedException ie) {
                ie.printStackTrace(getLauncher().getLog().getOutStream());
                throw new Failure("Caught interrupted exception while waiting for reader finished:\n\t" + ie);
            }
            if (reader.isAlive()) {
                getLauncher().getLog().display("Interrupting reader not finished for timeout: " + timeMillisec + " millisec");
                reader.interrupt();
            }
        }
    }

    /**
     * Waits for all readers of redirected streams of the <i>jdb's</i> process
     * to finish.
     */

    private void waitForAllReaders(long timeMillisec) {
        waitForReader(jdbStdoutReader, timeMillisec);
        waitForReader(jdbStderrReader, timeMillisec);
    }

    /**
      * Wait until the jdb process shutdown or crash
      * and all redirected stream readers finished.
      */

    public int waitFor() throws InterruptedException {
        int exitCode = super.waitFor();
        waitForAllReaders(0);
        return exitCode;
    }

    /**
     * Wait until the process shutdown or crash for given timeout in milliseconds,
     * and all redirected stream readers finished.
     * Returns <code>LocalProcess.PROCESS_IS_ALIVE</code> if process is not terminated
     * after timeout.
     */

    public int waitFor(long timeMillisec) throws InterruptedException {
        int exitCode = super.waitFor(timeMillisec);
        if (exitCode != LocalProcess.PROCESS_IS_ALIVE) {
            waitForAllReaders(timeMillisec);
        }
        return exitCode;
    }

    /**
     * Writes <i>jdbCommand</i> to <i>jdb's</i> input stream.
     */

    public synchronized void sendCommand(String jdbCommand) {

        if (terminated()) {
            throw new Failure("Attempt to send command :" + jdbCommand + "\t to terminated jdb.");
        }

        if (jdbCommand != null) {
            String logCmd;
            if (!jdbCommand.endsWith(lineSeparator)) {
                logCmd = jdbCommand;
                jdbCommand += lineSeparator;
            } else {
                // we don't want to log the line separator
                logCmd = jdbCommand.substring(0, jdbCommand.length() - lineSeparator.length());
            }
            launcher.getLog().display("Sending command: " + logCmd);

            jdbStdinWriter.print(jdbCommand);
            jdbStdinWriter.flush();

            synchronized(flog) {
                flog.print(/*LOG_COMMAND_PREFIX +*/ jdbCommand);
                flog.flush();
            }

            fin.print(jdbCommand);
            fin.flush();

            if (jdbStdinWriter.checkError()) {
                throw new Failure("Unexpected IO error while writing command <" + jdbCommand + "> to jdb stdin stream");
            }
        }
    }

    /**
     * Sends command to <i>jdb's</i> input stream, waits for compound promt received,
     * and then returns reply from <i>jdb's</i> output stream.
     *
     * @param command string representing full command with all arguments if any.
     */

    public String[] receiveReplyFor(String command) {
        return receiveReplyFor(command, true);
    }

    /**
     * Sends command to <i>jdb's</i> input stream, waits for promt received,
     * and then returns reply from <i>jdb's</i> output stream.
     *
     * @param command string representing full command with all arguments if any.
     * @param compoundPromptOnly read <i>output</i> until compound prompt is found.
     */

    public String[] receiveReplyFor(String command, boolean compoundPromptOnly) {
        return receiveReplyFor(command, compoundPromptOnly, 1);
    }

    /**
     * Sends command to <i>jdb's</i> input stream, waits for given number of promts received,
     * and then returns reply from <i>jdb's</i> output stream.
     *
     * @param command string representing full command with all arguments if any.
     * @param compoundPromptOnly read <i>output</i> until compound prompt is found.
     * @param count number of prompt instances to found.
     */

    public String[] receiveReplyFor(String command, boolean compoundPromptOnly, int count) {
        if (command == null) {
           return null;
        }

        int startPos = stdoutBuffer.length();
        sendCommand(command);
        return receiveReply(startPos, compoundPromptOnly, count);
    }

    /**
     * Sends command to <i>jdb's</i> input stream, waits for specified message to be received,
     * and then returns reply from <i>jdb's</i> output stream.
     *
     * @param command string representing full command with all arguments if any.
     * @param waitMsg string representing the message that must be sent back before returing.
     */

    public String[] receiveReplyForWithMessageWait(String command, String waitMsg) {
        if (command == null) {
           return null;
        }

        int startPos = stdoutBuffer.length();
        sendCommand(command);
        waitForMessage(startPos, waitMsg);
        return receiveReply(startPos, true, 1);
    }

    /**
     * Waits for compound prompt and returns reply from <i>jdb</i> stdout
     * beginning from <i>startPos</i> in the <i>stdoutBuffer</i>.
     *
     * @param startPos start position for search in <i>stdoutBuffer</i>.
     */

    public String[] receiveReply(int startPos) {
        return receiveReply(startPos, true);
    }

    /**
     * Waits for particular prompt and returns reply from <i>jdb</i> stdout
     * beginning from <i>startPos</i> in the <i>stdoutBuffer</i>.
     *
     * @param startPos start position for search in <i>stdoutBuffer</i>.
     * @param compoundPromptOnly waits for compound prompt only.
     */

    public String[] receiveReply(int startPos, boolean compoundPromptOnly) {
        return receiveReply(startPos, compoundPromptOnly, 1);
    }

    /**
     * Waits for <i>count</i> number of prompts and returns reply from <i>jdb</i> stdout
     * beginning from <i>startPos</i> in the <i>stdoutBuffer</i>.
     *
     * @param startPos start position for search in <i>stdoutBuffer</i>.
     * @param compoundPromptOnly waits for compound prompt only.
     * @param count number of prompt instances to wait for.
     */

    public String[] receiveReply(int startPos, boolean compoundPromptOnly, int count) {
        nsk.share.Failure e = null;
        try {
            waitForPrompt(startPos, compoundPromptOnly, count);
        } catch (nsk.share.Failure nsf) {
            e = nsf;
            launcher.getLog().display("receiveReply FAILED due to \"" + e + "\".");
            launcher.getLog().display("Pending reply output follows:");
        }

        String reply = stdoutBuffer.substring(startPos, stdoutBuffer.length());
        String[] replyArr = toStringArray(reply);

        // Send reply to the logfile. This complements sendCommand(), which does the same.
        for (int i = 0; i < replyArr.length; i++) {
            launcher.getLog().display("reply[" + i + "]: " + replyArr[i]);
        }

        if (e != nullthrow e;
        return replyArr;
    }

    /**
     * Reads <i>JDB_STDOUT_FILE</i> file until prompt is found in the <i>stdoutBuffer</i>.
     *
     * @param startPos start position for search in <i>stdoutBuffer</i>.
     * @param compoundPromptOnly search for compound prompt only.
     * @throws Failure if prompt is not encountered during <i>WaitTime</i>.
     * @return number of prompt instances really found.
     */

    public int waitForPrompt(int startPos, boolean compoundPromptOnly) {
        return waitForPrompt(startPos, compoundPromptOnly, 1);
    }

    /**
     * Reads <i>JDB_STDOUT_FILE</i> file until prompt is found in the <i>stdoutBuffer</i>
     * <i>count</i> times.
     *
     * @param startPos start position for search in <i>stdoutBuffer</i>.
     * @param compoundPromptOnly search for compound prompt only.
     * @throws Failure if prompt is not encountered <i>count</i> times during <i>WaitTime</i>.
     * @return number of prompt instances actually found
     *
     * @see #setCompoundPromptIdent(String)
     */

    public int waitForPrompt(int startPos, boolean compoundPromptOnly, int count) {

        long delta = 200; // time in milliseconds to wait at every iteration.
        long total = 0;    // total time has waited.
        long max = getLauncher().getJdbArgumentHandler().getWaitTime() * 60 * 1000;  // maximum time to wait.

        if (count <= 0) {
            throw new TestBug("Wrong number of prompts count in Jdb.waitForPrompt(): " + count);
        }

        Object dummy = new Object();
        while ((total += delta) <= max) {
            int found = 0;

            // check if compound prompt is found
            {
                found = findPrompt(stdoutBuffer, true, startPos);
                if (found >= count) {
                    return found;
                }
            }

            // check also if simple prompt is found
            if (!compoundPromptOnly) {
                found += findPrompt(stdoutBuffer, false, startPos);
                if (found >= count) {
                    return found;
                }
            }

            // exit loop when a debugged application exited
            if (stdoutBuffer.indexOf(APPLICATION_EXIT) >= 0 || stdoutBuffer.indexOf(APPLICATION_DISCONNECTED) >= 0) {
                return found;
            } else if (startPos > 0 && !jdbStdoutReader.isAlive()) {
                return found;
            }

            // sleep for awhile
            synchronized(dummy) {
                try {
                    dummy.wait(delta);
                } catch (InterruptedException ie) {
                    ie.printStackTrace(getLauncher().getLog().getOutStream());
                    throw new Failure("Caught interrupted exception while waiting for jdb prompt:\n\t" + ie);
                }
            }
        }

        Pattern debuggeeExceptionPattern = Pattern.compile("Exception occurred: (?\\S+) \\(uncaught\\)");
        String buf = stdoutBuffer.toString();
        Matcher m = debuggeeExceptionPattern.matcher(buf);

        if (m.find(startPos)) {
            throw new DebuggeeUncaughtException(m.group("DebuggeeException"));
        }

        String times = (count > 1 ? count + " times " : "");
        throw new Failure("Prompt is not received " + times + "during " + total + " milliseconds.");
    }

    /**
     * Reads <i>JDB_STDOUT_FILE</i> file until expected message is found in the <i>stdoutBuffer</i>.
     *
     * @param startPos start position for search in <i>stdoutBuffer</i>.
     * @throws Failure if expected message is not encountered during <i>WaitTime</i>.
     * @return number of messages actually found
     */

    public int waitForMessage(int startPos, String message) {

        long delta = 200; // time in milliseconds to wait at every iteration.
        long total = 0;    // total time has waited.
        long max = getLauncher().getJdbArgumentHandler().getWaitTime() * 60 * 1000;  // maximum time to wait.

        Object dummy = new Object();
        while ((total += delta) <= max) {
            int found = 0;

            // search for message
            {
                found = findMessage(startPos, message);
                if (found > 0) {
                    return found;
                }
            }

            // exit loop when a debugged application exited.
            if (stdoutBuffer.indexOf(APPLICATION_EXIT) >= 0 || stdoutBuffer.indexOf(APPLICATION_DISCONNECTED) >= 0) {
                return found;
            } else if (startPos > 0 && !jdbStdoutReader.isAlive()) {
                return found;
            }

            // spleep for awhile
            synchronized(dummy) {
                try {
                    dummy.wait(delta);
                } catch (InterruptedException ie) {
                    ie.printStackTrace(getLauncher().getLog().getOutStream());
                    throw new Failure("Caught interrupted exception while waiting for jdb reply:\n\t" + ie);
                }
            }

        }

        // If we never recieved the expected reply, display a warning, and also
        // display what we did recieve. This is accomplished by calling receiveReply().
        Log log = getLauncher().getLog();
        log.display("WARNING: message not recieved: " + message);
        log.display("Remaining debugger output follows:");
        receiveReply(startPos);
        throw new Failure("Expected message not received during " + total + " milliseconds:"
                            + "\n\t" + message);
    }

    /**
     * Find message in <i>JDB_STDOUT_FILE</i> file starting from <i>startPos</i>.
     *
     * @param startPos start position for search in <i>stdoutBuffer</i>.
     * @return number of messages actually found
     */

    public int findMessage(int startPos, String message) {
        int bufLength = stdoutBuffer.length();
        int msgLength = message.length();
        int found = 0;

        for (int pos = startPos; pos < bufLength; ) {
            pos = stdoutBuffer.indexOf(message, pos);
            if (pos < 0) break;
            found++;
            pos += msgLength;
        }
        return found;
    }

    /**
     * Searches input lines for <i>jdb</i> prompt of particular kind.
     * starting from <code>startPos</code>.
     * The possible prompt kinds are simple prompt "> " and compound prompt,
     * that looks like '.*\[[0-9]*\] ' regexp on a single line.
     * For example, 'main[1] ' (see setCompoundPromptIdent(String)).
     * <p>
     * In order to make compatible with jdk prior to 1.4.0 avoid using
     * java.util.regex classes.
     *
     * @return number of prompt instances found
     *
     * @see #setCompoundPromptIdent(String)
     */

    int findPrompt(StringBuffer lines, boolean compoundPromptOnly, int startPos) {

        final String nameDelimiters = "-_";

        int noPrompt = -1; // prompt is not found;
        int simplePrompt = 1;
        int complexPrompt = 2;

        int length = lines.length();
        int found = 0;

        // search for simple prompt
        if (!compoundPromptOnly) {
            int promptLength = SIMPLE_PROMPT.length();
            for (int pos = startPos; pos < length; ) {
                pos = lines.indexOf(SIMPLE_PROMPT, pos);
                if (pos < 0) break;
                found++;
                pos += promptLength;
            }
            return found;
        }

        // search for compound prompt
        StringBuffer prompt = new StringBuffer(100);
        searching:
        for (int pos = startPos; pos < length; ) {

            // skip each simbol not suitable for prompt begin
            if (!Character.isLetterOrDigit(lines.charAt(pos))) {
                pos++;
                continue searching;
            }

            // check for compound prompt
            prompt.setLength(0);

            // read name (letters or digits or delimiters)
            while (nameDelimiters.indexOf(lines.charAt(pos)) > 0
                        || Character.isLetterOrDigit(lines.charAt(pos))
                        || lines.charAt(pos) == '-'
                        || lines.charAt(pos) == '_') {
                prompt.append(lines.charAt(pos++));
                if (pos >= length) {
                    break searching;
                }
            }

            // read opening '['
            if (lines.charAt(pos) != '[') {
                continue searching;
            }
            prompt.append(lines.charAt(pos++));
            if (pos >= length) {
                break searching;
            }

            // read number (digits)
            if (!Character.isDigit(lines.charAt(pos))){
                continue searching;
            }
            while (Character.isDigit(lines.charAt(pos))) {
                prompt.append(lines.charAt(pos++));
                if (pos >= length) {
                    break searching;
                }
            }

            // read closing ']'
            if (lines.charAt(pos) != ']') {
                continue searching;
            }
            prompt.append(lines.charAt(pos++));
            if (pos >= length) {
                break searching;
            }

            // read last ' '
            if (lines.charAt(pos) != ' ') {
                continue searching;
            }
            prompt.append(lines.charAt(pos++));

            // check if not particular ident found
            if (compoundPromptIdent != null
                    && !prompt.toString().startsWith(compoundPromptIdent + "[")) {
                continue searching;
            }

            // compound prompt found
            found++;
        }

        return found;
    }

    /**
     * Splits string which may include line separators to string array.
     *
     */

    public static String[] toStringArray (String string) {
        Vector<String> v = new Vector<String>();
        int ind;
        for (ind = 0; ind < string.length(); ) {
            int i = string.indexOf(lineSeparator, ind);
            if (i >= 0) {
                v.add(string.substring(ind, i));
                ind = i + lineSeparator.length();
            } else {
                v.add(string.substring(ind));
                break;
            }
        }
        String[] result = new String [v.size()];
        v.toArray(result);
        return result;
    }

    /**
     * Set breakpoint for debuggee on method invocation.
     */

    public void setBreakpointInMethod(String methodName) {
        String nextCommand = JdbCommand.stop_in + methodName;
        String[] reply = receiveReplyFor(nextCommand);

        Paragrep grep = new Paragrep(reply);
        if (grep.find("Unable to set") > 0) {
            throw new Failure("jdb failed to set breakpoint in method: " + methodName);
        }
        if (grep.find("Set breakpoint") <= 0 && grep.find("Deferring breakpoint") <= 0) {
            throw new Failure("jdb did not set breakpoint in method: " + methodName);
        }
    }

    /**
     * Set deferred breakpoint for debuggee on method invocation.
     * This method must be used before <run> command.
     */

    public void setDeferredBreakpointInMethod(String methodName) {
        String nextCommand = JdbCommand.stop_in + methodName;
        String[] reply = receiveReplyFor(nextCommand, false);

        Paragrep grep = new Paragrep(reply);
        if (grep.find("Unable to set") > 0) {
            throw new Failure("jdb failed to set deffered breakpoint in method: " + methodName);
        }
        if (grep.find("Set breakpoint") <= 0 && grep.find("Deferring breakpoint") <= 0) {
            throw new Failure("jdb did not set deffered breakpoint in method: " + methodName);
        }
    }

    /**
     * Returns true if reply contains breakpoint message.
     */

    public boolean isAtBreakpoint(String[] reply) {
        return isAtBreakpoint(reply, """");
    }

    /**
     * Returns true if reply contains breakpoint message in certain method.
     */

    public boolean isAtBreakpoint(String[] reply, String method) {
        return isAtBreakpoint(reply, method, "");
    }

    /**
     * Returns true if reply contains breakpoint message in certain method
     * and in certain thread id.
     */

    public boolean isAtBreakpoint(String[] reply, String method, String thread) {
        boolean result = false;
        Vector<String> v = new Vector<String>();
        Paragrep grep = new Paragrep(reply);

        v.add(BREAKPOINT_HIT);
        if (method.length() > 0) {
            v.add(method);
        }
        if (thread.length() > 0) {
            v.add(thread);
        }
        if (grep.find(v) > 0) {
            result = true;
        }
        return result;
    }

    /**
     * Load and start execution of given debuggee's class with arguments.
     */

    public void startDebuggeeClass(String classWithArgs) {
        String[] reply = receiveReplyFor(JdbCommand.run + " " + classWithArgs);

        // give one more chance to reach breakpoint
        if (!isAtBreakpoint(getTotalReply(), "main")) {
            waitForMessage(0, BREAKPOINT_HIT);
        }
    }

    /**
     * Start execution of pre-loaded debuggee's class.
     */

    public void startDebuggeeClass() {
        String[] reply = receiveReplyFor(JdbCommand.run);

        // give one more chance to reach breakpoint
        if (!isAtBreakpoint(getTotalReply(), "main")) {
            waitForMessage(0, BREAKPOINT_HIT);
        }
    }

    /**
     * Returns as string array all id's for a given thread name of <i>threadName</i>.
     */

    public String[] getThreadIdsByName(String threadName) {
        Vector<String> v = new Vector<String>();
        String[] reply = receiveReplyFor(JdbCommand.threads);
        Paragrep grep = new Paragrep(reply);

        String[] found = grep.findStrings(threadName);
        for (int i = 0; i < found.length; i++) {
            String string = found[i];
            // Check for "(java.lang.Thread)" or "(java.lang.VirtualThread)"
            String searchString = "Thread)";
            int j = string.indexOf(searchString);
            if (j >= 0) {
               j += searchString.length(); // The threadID is right after the thread type
               String threadId = string.substring(j, string.indexOf(" ", j));
               v.add(threadId);
            }
        }

        String[] result = new String[v.size()];
        v.toArray(result);
        return result;
    }

    /**
     * Returns as string array all id's for a given class type of <i>threadType</i>.
     */

    public String[] getThreadIds(String threadType) {

        if (!threadType.startsWith("(")) {
            threadType = "(" + threadType;
        }
        if (!threadType.endsWith(")")) {
            threadType = threadType + ")";
        }

        Vector<String> v = new Vector<String>();
        String[] reply = receiveReplyFor(JdbCommand.threads);
        Paragrep grep = new Paragrep(reply);

        String[] found = grep.findStrings(threadType);
        for (int i = 0; i < found.length; i++) {
            String string = found[i];
            int j = string.indexOf(threadType);
            if (j >= 0) {
               j += threadType.length();
               String threadId = string.substring(j, string.indexOf(" ", j));
               v.add(threadId);
            }
        }

        String[] result = new String [v.size()];
        v.toArray(result);
        return result;
    }

    /**
     * Quit <i>jdb</i> using "quit" command.
     */

    public void quit() {
        if (!terminated()) {
            sendCommand(JdbCommand.quit);
        }
    }

    /**
     * Sends "cont" command up to maxTimes until debuggee exit.
     */

    public void contToExit (int maxTimes) {
        boolean exited = false;
        for (int i = 0; i < maxTimes; i++) {
            if (!terminated()) {
                String [] reply = receiveReplyFor(JdbCommand.cont);
                Paragrep grep = new Paragrep(reply);
                if (grep.find(APPLICATION_EXIT) > 0) {
                    exited = true;
                    break;
                }
            } else {
                exited = true;
                break;
            }
        }
        if (!exited) {
            if (terminated()) {
                exited = true;
            } else {
                quit();
                throw new Failure("Debuggee did not exit after " + maxTimes + " commands");
            }
        }
    }

    /**
     * Returns string array containing all strings from <i>jdb</i> stdout.
     */

    public String[] getTotalReply() {
        return toStringArray(stdoutBuffer.toString());
    }

    /**
     * Prints given message to log files and adds to <i>stdoutBuffer</i>.
     */

    public void logToFile(String s) {
        synchronized(fout) {
            fout.print(s);
            fout.flush();
        }
        synchronized(stdoutBuffer) {
            stdoutBuffer.append(s);
        }
        synchronized(flog) {
            flog.print(s);
            flog.flush();
        }
    }


    /**
     *  Starts jdb with attaching connector. Makes several tries during <i>waitTime</i>
     *  until success. Unsuccessful launches are caused that the debuggee is not yet
     *  ready to accept debugger.
     */

    public static Jdb startAttachingJdb (Launcher launcher, String[] jdbCmdArgs, String message)
            throws IOException  {
        Jdb jdb = null;

        long delta = Launcher.DEBUGGEE_START_DELAY; // time in milliseconds to wait at every iteration.
        long max = getLauncher().getJdbArgumentHandler().getWaitTime() * 60 * 1000;  // maximum time to wait.

        int result = -1;
        boolean found = false;

        long start = System.currentTimeMillis();

        while (!found && (System.currentTimeMillis() - start)<= max) {

            jdb = new Jdb(launcher);
            jdb.launch(jdbCmdArgs);

            while (!found && (System.currentTimeMillis() - start)<= max) {

                try {
                    Thread.currentThread().sleep(delta);
                } catch (InterruptedException ie) {
                    ie.printStackTrace(getLauncher().getLog().getOutStream());
                    throw new Failure("Caught unexpected InterruptedException while sleep in waiting for debuggee's start:\n\t"
                       + ie);
                }

                if (jdb.terminated() ||
                    !jdbStdoutReader.isAlive() ||
                    stdoutBuffer.indexOf(APPLICATION_EXIT) >= 0 ||
                    stdoutBuffer.indexOf(APPLICATION_DISCONNECTED) >= 0) {

                    System.out.println("Unsuccessful launch of attaching jdb. Next try...");
                    try {
                        jdb.finalize();
                    } catch (Throwable t) {
                        t.printStackTrace(getLauncher().getLog().getOutStream());
                        throw new Failure("Caught unexpected error while finalizing jdb: " + t);
                    }
                    break;

                } else if (stdoutBuffer.length() > 0) {
                    result = stdoutBuffer.indexOf(message);
                    if (result >= 0) {
                        found = true// exit loop
                    }
                }
            }

        }

        if (result < 0) {
            throw new Failure("Launched jdb could not attach to debuggee during " + max + " milliseconds.");
        }

        return jdb;
    }

    /**
     *  Waits for jdb to print message about listening at address for connection,
     *  and returns this address string.
     */

    public String waitForListeningJdb() {

        waitForMessage(0, LISTENING_AT_ADDRESS);
        int msgStart = stdoutBuffer.indexOf(LISTENING_AT_ADDRESS);
        int msgEnd = stdoutBuffer.indexOf("\n", msgStart);
        int promptLen = LISTENING_AT_ADDRESS.length();

        /*
         * The LISTENING_AT_ADDRESS string and the terminating "\n"
         * may or may not be included in the same read so we allow
         * this message to be terminated by "\n" or NULL.
         */

        if (msgEnd < 0) {
            msgEnd = stdoutBuffer.length();
        }

        if (msgEnd <= 0 || msgEnd - msgStart <= promptLen) {
            throw new Failure("Unknown format of message: " + LISTENING_AT_ADDRESS);
        }

        int addrStart = msgStart + promptLen;
        String address = stdoutBuffer.substring(addrStart, msgEnd).trim();

        if (address.length() <= 0) {
            throw new Failure("Empty address in message: " + LISTENING_AT_ADDRESS);
        }

        return address;
    }

    // ---------------------------------------------- //

    class JdbStdoutReader extends Thread {
        private Jdb jdb = null;
        private InputStream in = null;

        volatile boolean stop = false;

        public JdbStdoutReader (Jdb jdb) {
            super("jdb stdout reader");
            this.jdb = jdb;
            this.in = jdb.getStdout();
            if (in == null) {
                throw new Failure("Can not get jdb stdout stream");
            }
            this.setDaemon(true);
        }

        public String toString() {
            return getClass().getName() + '@' + Integer.toHexString(hashCode());
        }

        public void run() {
            synchronized(jdb.startNotify) {
                jdb.startNotify.notifyAll();
            }

            long delta = 10; // time in milliseconds to wait at every iteration.
            boolean jdbWasTerminated = false;
            while (!stop) {
                if(jdb.terminated())
                        jdbWasTerminated = true;
                try {
                    int size = in.available();
                    if (size > 0) {
                        byte[] buffer = new byte [size];
                        int result = in.read(buffer, 0, size);
                        if (result < 0) {
                            throw new Failure("No bytes read from jdb's output stream ");
                        } else if (result < size) {
                            throw new Failure("Number bytes read from jdb's output stream are less than available " +
                                 "\n\t available : " + size + ", read : " + result);
                        }
                        logToFile(new String(buffer, 0, result));
                    }
                } catch (Exception e) {
                    e.printStackTrace(jdb.getLauncher().getLog().getOutStream());
                    throw new Failure("Caught unexpected exception while reading jdb's stdout stream: " + e);
                }
                if(jdbWasTerminated)
                        break;
                try {
                    sleep(delta);
                } catch (InterruptedException ie) {
                    ie.printStackTrace(jdb.getLauncher().getLog().getOutStream());
                    throw new Failure("Caught interrupted exception while waiting for jdb reply:\n\t" + ie);
                }
            }
        }

        public void close() {
            stop = true;
            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException ioe) {
                ioe.printStackTrace(jdb.getLauncher().getLog().getOutStream());
                throw new Failure("Caught unexpected IOException while closing jdb stdout stream: " + ioe);
            }
        }
    }

    /** Handler for <i>jdb</i> stderr stream. */
    class JdbStderrReader extends Thread {

        private Jdb jdb = null;
        private volatile boolean cancelled = false;
        private boolean empty = true;

        private BufferedReader bin;
        private PrintStream fout;
        private String fileName;

        JdbStderrReader (Jdb jdb, String jdbStderrFile) {
            super("jdb stderr reader");
            this.jdb = jdb;
            InputStream in = jdb.getStderr();
            if (in == null) {
                throw new Failure("Can not get jdb stderr stream");
            }
            this.bin = new BufferedReader(new InputStreamReader(in));
            this.setDaemon(true);

            this.fileName = jdbStderrFile;

            launcher.getLog().display("Creating file for jdb stderr stream: " + fileName);
            try {
                this.fout = new PrintStream(new BufferedOutputStream(new FileOutputStream(fileName)));
            } catch (Exception e) {
                e.printStackTrace(jdb.getLauncher().getLog().getOutStream());
                throw new Failure("Caught unexpected exception while creating file for jdb stderr stream: " + e);
            }
        }

        public void run () {
            synchronized(jdb.startNotify) {
                jdb.startNotify.notifyAll();
            }

            long delta = 10; // time in milliseconds to wait at every iteration.

            while (!cancelled) {
                String line = null;
                try {
                    line = bin.readLine();
                    if (line == null)
                        break//EOF
                } catch (IOException ioe) {
                    ioe.printStackTrace(jdb.getLauncher().getLog().getOutStream());
                    throw new Failure("Caught unexpected IOException while reading from jdb stderr: " + ioe);
                }

                if (line != null) {
                   empty = false;
                   logToFile(line);
                }

                try {
                    sleep(delta);
                } catch (InterruptedException ie) {
                    throw new Failure("Caught interrupted exception while waiting for jdb reply:\n\t" + ie);
                }
            }
            close();
        }

        /**
         * Signal to <i>run()</i> method that it should terminate,
         * and wait until it is finished.
         */

        public void cancel () {
            cancelled = true;
            while (this.isAlive()) {
                try {
                    this.join();
                } catch (InterruptedException ie) {
                    close();
                    throw new Failure("Caught InterruptedException while waiting for JdbStderrReader termination " + ie);
                }
            }
            close();
        }

        public void close() {
            if (fout != null) {
                synchronized (fout) {
                    fout.close();
                }
            }

            try {
                if (bin != null) {
                    bin.close();
                }
            } catch (IOException ioe) {
                ioe.printStackTrace(jdb.getLauncher().getLog().getOutStream());
                throw new Failure("Caught unexpected IOException while closing jdb stderr stream: " + ioe);
            }
            if (!empty) {
                 // Should not throw exception here because of non-empty stderr in case of unsuccessful launch of attaching jdb.
                jdb.getLauncher().getLog().display("JdbStderrReader: jdb's stderr is not empty. Check jdb.stderr file");
            }
        }

        public String getFileName () {
            return this.fileName;
        }

        public void logToFile(String line) {
            synchronized (fout) {
                fout.println(line);
                fout.flush();
            }
        }
    } // end of JdbStderrReader
}  // end of Jdb

Messung V0.5
C=93 H=90 G=91

¤ Dauer der Verarbeitung: 0.13 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge