diff --git a/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/EventThread.java b/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/EventThread.java index 10eef377795..3f5ac1c49d9 100644 --- a/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/EventThread.java +++ b/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/EventThread.java @@ -4,14 +4,14 @@ */ package org.eclipse.cdt.debug.mi.core; - + import java.io.IOException; import org.eclipse.cdt.debug.mi.core.event.MIEvent; /** - * Transmission command thread blocks on the command Queue - * and wake cmd are available and push them to gdb out channel. + * Event Thread blocks on the event Queue, wakes up + * when events are available and notify all the observers. */ public class EventThread extends Thread { @@ -22,29 +22,24 @@ public class EventThread extends Thread { session = s; } - public void run () { - try { - while (true) { - MIEvent event = null; - Queue eventQueue = session.getEventQueue(); - // removeItem() will block until an item is available. - try { - event = (MIEvent)eventQueue.removeItem(); - } catch (InterruptedException e) { - // signal by the session of time to die. - if (session.getChannelOutputStream() == null) { - throw new IOException(); - } - //e.printStackTrace(); - } - try { - session.notifyObservers(event); - } catch (Exception e) { - e.printStackTrace(); - } + public void run() { + // signal by the session of time to die. + while (session.getChannelOutputStream() != null) { + MIEvent event = null; + Queue eventQueue = session.getEventQueue(); + // removeItem() will block until an item is available. + try { + event = (MIEvent) eventQueue.removeItem(); + } catch (InterruptedException e) { + //e.printStackTrace(); + } + try { + if (event != null) { + session.notifyObservers(event); + } + } catch (Exception e) { + e.printStackTrace(); } - } catch (IOException e) { - //e.printStackTrace(); } } } diff --git a/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/MIInferior.java b/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/MIInferior.java index 20405fbbd87..8329628c5a1 100644 --- a/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/MIInferior.java +++ b/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/MIInferior.java @@ -3,65 +3,71 @@ package org.eclipse.cdt.debug.mi.core; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; import org.eclipse.cdt.debug.mi.core.command.CLICommand; import org.eclipse.cdt.debug.mi.core.command.CommandFactory; import org.eclipse.cdt.debug.mi.core.command.MIExecAbort; -import org.eclipse.cdt.debug.mi.core.command.MIGDBExit; import org.eclipse.cdt.debug.mi.core.command.MIGDBShowExitCode; -import org.eclipse.cdt.debug.mi.core.event.MIExitEvent; import org.eclipse.cdt.debug.mi.core.event.MIInferiorExitEvent; import org.eclipse.cdt.debug.mi.core.output.MIGDBShowExitCodeInfo; /** - * @author alain - * - * To change this generated comment edit the template variable "typecomment": - * Window>Preferences>Java>Templates. - * To enable and disable the creation of type comments go to - * Window>Preferences>Java>Code Generation. */ public class MIInferior extends Process { - public final static int SUSPENDED = 1; - public final static int RUNNING = 2; - public final static int TERMINATED = 4; + final static int SUSPENDED = 1; + final static int RUNNING = 2; + final static int TERMINATED = 4; + + boolean connected = false; int state = 0; + MISession session; + OutputStream out; + PipedInputStream in; + PipedOutputStream inPiped; + + PipedInputStream err; + PipedOutputStream errPiped; + MIInferior(MISession mi) { session = mi; - out = new OutputStream() { - StringBuffer buf = new StringBuffer(); - public void write(int b) throws IOException { - buf.append(b); - if (b == '\n') { - flush(); - } - } - // Encapsulate the string sent to gdb in a fake command. - // and post it to the TxThread. - public void flush() throws IOException { - CLICommand cmd = new CLICommand(buf.toString()) { - public void setToken(int token) { - // override to do nothing; - } - }; - try { - session.postCommand(cmd); - } catch (MIException e) { - throw new IOException("no mi session"); - } - } - }; } /** * @see java.lang.Process#getOutputStream() */ public OutputStream getOutputStream() { + if (out == null) { + out = new OutputStream() { + StringBuffer buf = new StringBuffer(); + public void write(int b) throws IOException { + buf.append(b); + if (b == '\n') { + flush(); + } + } + // Encapsulate the string sent to gdb in a fake command. + // and post it to the TxThread. + public void flush() throws IOException { + CLICommand cmd = new CLICommand(buf.toString()) { + public void setToken(int token) { + // override to do nothing; + } + }; + try { + session.postCommand(cmd); + } catch (MIException e) { + throw new IOException("no mi session"); + } + } + }; + } return out; } @@ -69,15 +75,30 @@ public class MIInferior extends Process { * @see java.lang.Process#getInputStream() */ public InputStream getInputStream() { - return session.getTargetStream(); + if (in == null) { + try { + inPiped = new PipedOutputStream(); + in = new PipedInputStream(inPiped); + } catch (IOException e) { + } + } + return in; } /** * @see java.lang.Process#getErrorStream() */ public InputStream getErrorStream() { - // FIXME the same as output?? - return session.getTargetStream(); + // FIXME: We do not have any err stream from gdb/mi + // so this gdb err channel instead. + if (err == null) { + try { + errPiped = new PipedOutputStream(); + err = new PipedInputStream(errPiped); + } catch (IOException e) { + } + } + return err; } /** @@ -96,7 +117,7 @@ public class MIInferior extends Process { * @see java.lang.Process#exitValue() */ public int exitValue() { - if (isTerminated()) { + if (isTerminated() && !session.isTerminated()) { CommandFactory factory = session.getCommandFactory(); MIGDBShowExitCode code = factory.createMIGDBShowExitCode(); try { @@ -114,29 +135,15 @@ public class MIInferior extends Process { * @see java.lang.Process#destroy() */ public void destroy() { -/* if (!isTerminated()) { CommandFactory factory = session.getCommandFactory(); MIExecAbort abort = factory.createMIExecAbort(); try { session.postCommand(abort); - setTerminated(); session.getRxThread().fireEvent(new MIInferiorExitEvent()); } catch (MIException e) { } - } -*/ - if (!isTerminated()) { - if (!isSuspended()) - { - // interrupt execution - } - CommandFactory factory = session.getCommandFactory(); - MIGDBExit exit = factory.createMIGDBExit(); - try { - session.postCommand(exit); - } catch (MIException e) { - } + setTerminated(); } } @@ -152,6 +159,18 @@ public class MIInferior extends Process { return state == TERMINATED; } + public boolean isConnected() { + return connected; + } + + public synchronized void setConnected() { + connected = true; + } + + public synchronized void setDisConnected() { + connected = false; + } + public synchronized void setSuspended() { state = SUSPENDED; } @@ -162,6 +181,31 @@ public class MIInferior extends Process { public synchronized void setTerminated() { state = TERMINATED; + // Close the streams. + try { + if (inPiped != null) { + inPiped.close(); + inPiped = null; + } + } catch (IOException e) { + e.printStackTrace(); + } + try { + if (errPiped != null) { + errPiped.close(); + errPiped = null; + } + } catch (IOException e) { + e.printStackTrace(); + } notifyAll(); } + + public OutputStream getPipedOutputStream() { + return inPiped; + } + + public OutputStream getPipedErrorStream() { + return errPiped; + } } diff --git a/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/MIPlugin.java b/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/MIPlugin.java index a3679b1a027..f90be9b33bf 100644 --- a/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/MIPlugin.java +++ b/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/MIPlugin.java @@ -51,10 +51,10 @@ public class MIPlugin extends Plugin { String[]args = new String[]{"gdb", "-q", "-i", "mi", program}; Process gdb = Runtime.getRuntime().exec(args); MISession session = createMISession(gdb.getInputStream(), gdb.getOutputStream()); - /* + ///* try { CommandFactory factory = session.getCommandFactory(); - MIBreakInsert bkpt= factory.createMIBreakInsert(true, false, null, 0, "main"); + MIBreakInsert bkpt= factory.createMIBreakInsert(true, false, null, 0, "routine"); session.postCommand(bkpt); MIInfo info = bkpt.getMIInfo(); if (info == null) { @@ -63,7 +63,7 @@ public class MIPlugin extends Plugin { } catch (MIException e) { throw new IOException("Failed to attach"); } - */ + //*/ return new CSession(session); } diff --git a/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/MISession.java b/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/MISession.java index 1ebdf256b95..0193eb64da3 100644 --- a/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/MISession.java +++ b/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/MISession.java @@ -15,6 +15,7 @@ import org.eclipse.cdt.debug.mi.core.command.Command; import org.eclipse.cdt.debug.mi.core.command.CommandFactory; import org.eclipse.cdt.debug.mi.core.command.MIGDBExit; import org.eclipse.cdt.debug.mi.core.command.MIGDBSet; +import org.eclipse.cdt.debug.mi.core.output.MIInfo; import org.eclipse.cdt.debug.mi.core.output.MIOutput; import org.eclipse.cdt.debug.mi.core.output.MIParser; @@ -39,10 +40,6 @@ public class MISession extends Observable { PipedInputStream miInPipe; PipedOutputStream miOutPipe; - PipedInputStream targetInPipe; - PipedOutputStream targetOutPipe; - PipedInputStream logInPipe; - PipedOutputStream logOutPipe; CommandFactory factory; @@ -61,59 +58,62 @@ public class MISession extends Observable { public MISession(InputStream i, OutputStream o) { inChannel = i; outChannel = o; + factory = new CommandFactory(); + parser = new MIParser(); + txQueue = new CommandQueue(); rxQueue = new CommandQueue(); eventQueue = new Queue(); + txThread = new TxThread(this); rxThread = new RxThread(this); eventThread = new EventThread(this); + txThread.start(); rxThread.start(); eventThread.start(); - try { - miOutPipe = new PipedOutputStream(); - miInPipe = new PipedInputStream(miOutPipe); - targetOutPipe = new PipedOutputStream(); - targetInPipe = new PipedInputStream(targetOutPipe); - logOutPipe = new PipedOutputStream(); - logInPipe = new PipedInputStream(logOutPipe); - } catch (IOException e) { - } - inferior = new MIInferior(this); try { - postCommand(new MIGDBSet(new String[]{"confirm", "off"})); + // Disable a certain number of irritations from gdb. + // Like confirmation and screen size. + MIInfo info; + + MIGDBSet confirm = new MIGDBSet(new String[]{"confirm", "off"}); + postCommand(confirm); + info = confirm.getMIInfo(); + + MIGDBSet width = new MIGDBSet(new String[]{"width", "99999999"}); + postCommand(width); + info = confirm.getMIInfo(); + + MIGDBSet height = new MIGDBSet(new String[]{"height", "99999999"}); + postCommand(height); + info = confirm.getMIInfo(); } catch (MIException e) { + // FIXME: Do not catch the exception but pass it up. } } /** - * get Console Stream. + * get MI Console Stream. */ public InputStream getMIStream() { + if (miInPipe == null) { + try { + miOutPipe = new PipedOutputStream(); + miInPipe = new PipedInputStream(miOutPipe); + } catch (IOException e) { + } + } return miInPipe; } /** - * Get Target Stream. - */ - public InputStream getTargetStream() { - return targetInPipe; - } - - /** - * Get Log Stream - */ - public InputStream getLogStream() { - return logInPipe; - } - - /** - * For example the CDI/MI adapters uses the command + * For example the CDI/MI bridge uses the command * factory to create MI commands this allow overloading. */ public CommandFactory getCommandFactory() { @@ -121,21 +121,21 @@ public class MISession extends Observable { } /** - * Set a new factory to use in CDI/MI adapters. + * Set a new factory to use for command. */ public void setCommandFactory(CommandFactory f) { factory = f; } /** - * Return the MI main parser. + * Return the MI parser. */ public MIParser getMIParser() { return parser; } /** - * Reset the parser. + * Reset the MI parser. */ public void setMIParser(MIParser p) { parser = p; @@ -156,6 +156,7 @@ public class MISession extends Observable { } /** + * equivalent to: * postCommand(cmd, 10 secs) */ public void postCommand(Command cmd) throws MIException { @@ -163,13 +164,14 @@ public class MISession extends Observable { } /** - * Sends a command to gdb. + * Sends a command to gdb, and wait(timeout) for a response. */ - public void postCommand(Command cmd, long timeout) throws MIException { + static int number = 1; + public synchronized void postCommand(Command cmd, long timeout) throws MIException { - MIPlugin.getDefault().debugLog(cmd.toString()); +MIPlugin.getDefault().debugLog(number++ + " " + cmd.toString()); - // Test if we in a sane state. + // Test if we are in a sane state. if (!txThread.isAlive() || !rxThread.isAlive()) { throw new MIException("{R,T}xThread terminated"); } @@ -178,53 +180,57 @@ public class MISession extends Observable { // Wait for the response or timedout synchronized (cmd) { - // Do not wait for command if time out is 0 - if (timeout > 0) { - // RxThread will set the MIOutput on the cmd - // when the response arrive. - while (cmd.getMIOutput() == null) { - try { - cmd.wait(timeout); - if (cmd.getMIOutput() == null) { - throw new MIException("Timedout"); - } - } catch (InterruptedException e) { + // RxThread will set the MIOutput on the cmd + // when the response arrive. + while (cmd.getMIOutput() == null) { + try { + cmd.wait(timeout); + if (cmd.getMIOutput() == null) { + throw new MIException("Timedout"); } + } catch (InterruptedException e) { } } } } + /** + * Return the inferior "Process". + */ public MIInferior getMIInferior() { return inferior; } + /** + * Check if the gdb session is terminated. + */ public boolean isTerminated() { return (!txThread.isAlive() || !rxThread.isAlive()); } /** - * Close the MISession. + * Terminate the MISession. */ public void terminate() { - // Destroy any MI Inferior + // Destroy any MI Inferior(Process) inferior.destroy(); - // send the exit. + // send the exit(-gdb-exit). try { MIGDBExit exit = factory.createMIGDBExit(); postCommand(exit); } catch (MIException e) { } - // Explicitely close the channels + // Close the input GDB prompt try { inChannel.close(); } catch (IOException e) { } inChannel = null; + // Close the output GDB prompt try { outChannel.close(); } catch (IOException e) { @@ -232,30 +238,39 @@ public class MISession extends Observable { // This is __needed__ to stop the txThread and eventThread. outChannel = null; - // Make sure all threads are gone. + // Kill the Transmition thread. try { if (txThread.isAlive()) { txThread.interrupt(); } - txThread.join(); + txThread.join(cmdTimeout); } catch (InterruptedException e) { } + // Kill the Receiving Thread. try { if (rxThread.isAlive()) { rxThread.interrupt(); } - rxThread.join(); + rxThread.join(cmdTimeout); } catch (InterruptedException e) { } + // Kill the event Thread. try { if (eventThread.isAlive()) { eventThread.interrupt(); } - eventThread.join(); + eventThread.join(cmdTimeout); } catch (InterruptedException e) { } + + // Destroy the MI console stream. + try { + miOutPipe.close(); + miInPipe = null; + } catch (IOException e) { + } } /** @@ -271,14 +286,6 @@ public class MISession extends Observable { return miOutPipe; } - OutputStream getTargetPipe() { - return targetOutPipe; - } - - OutputStream getLogPipe() { - return logOutPipe; - } - CommandQueue getTxQueue() { return txQueue; } diff --git a/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/RxThread.java b/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/RxThread.java index cf2413b7174..ecc9072cd7c 100644 --- a/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/RxThread.java +++ b/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/RxThread.java @@ -44,15 +44,17 @@ import org.eclipse.cdt.debug.mi.core.output.MITargetStreamOutput; import org.eclipse.cdt.debug.mi.core.output.MIValue; /** - * Receiving thread of gdb, read the input channel. + * Receiving thread of gdb response output. */ public class RxThread extends Thread { final MISession session; + List oobList; public RxThread(MISession s) { super("MI RX Thread"); session = s; + oobList = new ArrayList(); } /* @@ -62,7 +64,6 @@ public class RxThread extends Thread { public void run () { BufferedReader reader = new BufferedReader(new InputStreamReader(session.getChannelInputStream())); - StringBuffer buffer = new StringBuffer(); try { while (true) { String line; @@ -121,20 +122,33 @@ MIPlugin.getDefault().debugLog(line); fireEvent(event); } else if ("exit".equals(state)) { session.getMIInferior().setTerminated(); - fireEvent(new MIExitEvent()); + MIEvent event = new MIExitEvent(); + fireEvent(event); + } else if ("connected".equals(state)) { + session.getMIInferior().setConnected(); } + // Clear the accumulate oobList on each new Result Command + // response. + MIOOBRecord [] oobRecords = + (MIOOBRecord[])oobList.toArray(new MIOOBRecord[0]); + oobList.clear(); + // Notify the waiting command. if (cmd != null) { synchronized (cmd) { + // Set the accumulate console Stream + response.setMIOOBRecords(oobRecords); cmd.setMIOutput(response); cmd.notifyAll(); } } + // Some result record contains informaton specific to oob. // This will happen when CLI-Command is use, for example // doing "run" will block and return a breakpointhit processMIOOBRecord(rr, list); + } // Process OOBs @@ -162,35 +176,33 @@ MIPlugin.getDefault().debugLog(line); void processMIOOBRecord(MIAsyncRecord async, List list) { if (async instanceof MIExecAsyncOutput) { MIExecAsyncOutput exec = (MIExecAsyncOutput)async; - MIEvent e = null; - // Change of state. String state = exec.getAsyncClass(); if ("stopped".equals(state)) { + MIEvent e = null; session.getMIInferior().setSuspended(); - } - - MIResult[] results = exec.getMIResults(); - for (int i = 0; i < results.length; i++) { - String var = results[i].getVariable(); - MIValue val = results[i].getMIValue(); - if (var.equals("reason")) { - if (val instanceof MIConst) { - String reason = ((MIConst)val).getString(); - e = createEvent(reason, exec); - if (e != null) { - list.add(e); + MIResult[] results = exec.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue val = results[i].getMIValue(); + if (var.equals("reason")) { + if (val instanceof MIConst) { + String reason =((MIConst)val).getString(); + e = createEvent(reason, exec); + if (e != null) { + list.add(e); + } } } } - } - // HACK: GDB for temporary breakpoints will not send the - // "reason" ??? Fake this as breakpoint-hit - if (e == null) { - e = createEvent("breakpoint-hit", exec); - if (e != null) { - list.add(e); + // HACK: GDB for temporary breakpoints will not send the + // "reason" ??? Fake this as breakpoint-hit + if (e == null) { + e = createEvent("breakpoint-hit", exec); + if (e != null) { + list.add(e); + } } } } else if (async instanceof MIStatusAsyncOutput) { @@ -214,8 +226,11 @@ MIPlugin.getDefault().debugLog(line); } } } + // Accumulate the Console Stream Output response for parsing. + // Some commands will put valuable info in the Console Stream. + oobList.add(stream); } else if (stream instanceof MITargetStreamOutput) { - OutputStream target = session.getTargetPipe(); + OutputStream target = session.getMIInferior().getPipedOutputStream(); if (target != null) { MITargetStreamOutput out = (MITargetStreamOutput)stream; String str = out.getString(); @@ -228,7 +243,7 @@ MIPlugin.getDefault().debugLog(line); } } } else if (stream instanceof MILogStreamOutput) { - OutputStream log = session.getLogPipe(); + OutputStream log = session.getMIInferior().getPipedErrorStream(); if (log != null) { MILogStreamOutput out = (MILogStreamOutput)stream; String str = out.getString(); @@ -310,13 +325,13 @@ MIPlugin.getDefault().debugLog(line); event = new MIFunctionFinishedEvent(rr); } } else if ("exited-normally".equals(reason)) { -// session.getMIInferior().setTerminated(); + session.getMIInferior().setTerminated(); event = new MIInferiorExitEvent(); } else if ("exited-signalled".equals(reason)) { -// session.getMIInferior().setTerminated(); + session.getMIInferior().setTerminated(); event = new MIInferiorExitEvent(); } else if ("exited".equals(reason)) { -// session.getMIInferior().setTerminated(); + session.getMIInferior().setTerminated(); event = new MIInferiorExitEvent(); } return event; diff --git a/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/TxThread.java b/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/TxThread.java index 0eba0c30125..ab8acbc7b27 100644 --- a/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/TxThread.java +++ b/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/TxThread.java @@ -28,17 +28,14 @@ public class TxThread extends Thread { public void run () { try { - while (true) { + // signal by the session of time to die. + while (session.getChannelOutputStream() != null) { Command cmd = null; CommandQueue txQueue = session.getTxQueue(); // removeCommand() will block until a command is available. try { cmd = txQueue.removeCommand(); } catch (InterruptedException e) { - // signal by the session of time to die. - if (session.getChannelOutputStream() == null) { - throw new IOException(); - } //e.printStackTrace(); } @@ -55,8 +52,10 @@ public class TxThread extends Thread { // shove in the pipe String str = cmd.toString(); OutputStream out = session.getChannelOutputStream(); - out.write(str.getBytes()); - out.flush(); + if (out != null) { + out.write(str.getBytes()); + out.flush(); + } } } } catch (IOException e) {