GP-840: Punting when GDB asks for follow-on input, to avoid hang.

This commit is contained in:
Dan 2021-04-21 14:39:51 -04:00
parent 2891486a2a
commit 75f950880e
3 changed files with 73 additions and 6 deletions

View file

@ -77,7 +77,7 @@ public class GdbManagerImpl implements GdbManager {
static {
if (LOG_IO) {
try {
DBG_LOG = new PrintWriter(new FileOutputStream(new File("DBG.log")));
DBG_LOG = new PrintWriter(new FileOutputStream(new File("GDB.log")));
}
catch (FileNotFoundException e) {
throw new AssertionError(e);
@ -810,6 +810,7 @@ public class GdbManagerImpl implements GdbManager {
/**
* Schedule a line of GDB output for processing
*
* <p>
* Before the implementation started using a PTY, the channel was used to distinguish whether
* the line was read from stdout or stderr. Now, all output is assumed to be from stdout.
*
@ -1487,6 +1488,18 @@ public class GdbManagerImpl implements GdbManager {
}
}
@Internal
public void injectInput(Interpreter interpreter, String input) {
PrintWriter writer = getWriter(interpreter);
writer.print(input);
writer.flush();
}
@Internal
public void synthesizeConsoleOut(Channel channel, String line) {
listenersConsoleOutput.fire.output(channel, line);
}
@Override
public synchronized GdbState getState() {
return state.get();

View file

@ -18,9 +18,11 @@ package agent.gdb.manager.impl.cmd;
import org.apache.commons.text.StringEscapeUtils;
import agent.gdb.manager.GdbManager;
import agent.gdb.manager.GdbManager.Channel;
import agent.gdb.manager.evt.AbstractGdbCompletedCommandEvent;
import agent.gdb.manager.evt.GdbConsoleOutputEvent;
import agent.gdb.manager.impl.*;
import agent.gdb.manager.impl.GdbManagerImpl.Interpreter;
/**
* Implementation of {@link GdbManager#console(String)} and similar
@ -40,22 +42,62 @@ public class GdbConsoleExecCommand extends AbstractGdbCommandWithThreadAndFrameI
this.to = to;
}
/**
* TODO: I think there should be a separate command for arbitrary CLI input. I'm not sure yet
* whether it should wait in the queue or just be sent immediately.
*/
@Override
public Interpreter getInterpreter() {
/*if (to == Output.CONSOLE && manager.hasCli() && threadId == null && frameId == null) {
return Interpreter.CLI;
}*/
return Interpreter.MI2;
}
@Override
public String encode(String threadPart, String framePart) {
return "-interpreter-exec" + threadPart + framePart + " console \"" +
StringEscapeUtils.escapeJava(command) + "\"";
switch (getInterpreter()) {
case CLI:
return command;
case MI2:
return "-interpreter-exec" + threadPart + framePart + " console \"" +
StringEscapeUtils.escapeJava(command) + "\"";
default:
throw new AssertionError();
}
}
@Override
public boolean handle(GdbEvent<?> evt, GdbPendingCommand<?> pending) {
if (getInterpreter() == Interpreter.CLI) {
// At the very least, I should expect to see the (gdb) prompt.
if (evt instanceof GdbConsoleOutputEvent) {
GdbConsoleOutputEvent out = (GdbConsoleOutputEvent) evt;
if (out.getInterpreter() == Interpreter.CLI) {
return true;
}
}
return false;
}
// MI2
if (evt instanceof AbstractGdbCompletedCommandEvent) {
pending.claim(evt);
return true;
}
else if (evt instanceof GdbConsoleOutputEvent && to == Output.CAPTURE) {
else if (evt instanceof GdbConsoleOutputEvent) {
GdbConsoleOutputEvent out = (GdbConsoleOutputEvent) evt;
if (out.getInterpreter() == getInterpreter()) {
pending.steal(evt);
// This is not a great check...
if (out.getInterpreter() == Interpreter.MI2 && ">".equals(out.getOutput().trim()) &&
!command.trim().startsWith("ec")) {
manager.injectInput(Interpreter.MI2, "end\n");
manager.synthesizeConsoleOut(Channel.STDERR,
"Ghidra GDB Agent: Multi-line / follow-up input is not currently supported. " +
"I just typed 'end' for you.\n");
}
if (to == Output.CAPTURE) {
if (out.getInterpreter() == getInterpreter()) {
pending.steal(evt);
}
}
}
return false;
@ -63,6 +105,10 @@ public class GdbConsoleExecCommand extends AbstractGdbCommandWithThreadAndFrameI
@Override
public String complete(GdbPendingCommand<?> pending) {
if (getInterpreter() == Interpreter.CLI) {
return null;
}
// MI2
pending.checkCompletion(AbstractGdbCompletedCommandEvent.class);
if (to == Output.CONSOLE) {

View file

@ -32,6 +32,14 @@ import ghidra.dbg.target.schema.*;
import ghidra.dbg.util.PathUtils;
import ghidra.util.Msg;
/**
* TODO: We should probably expose the raw CLI (if available) via TargetConsole, and perhaps re-work
* the UI to use it when available. This could more generally solve the multi-line input thing, and
* provide a distinction between API access (where {@link TargetInterpreter} makes more sense), and
* I/O access (where {@link TargetConsole}) makes more sense. I'm hoping this will also allow the
* CLI to prompt the user when appropriate, e.g., on {@code quit} when an inferior is active. NOTE:
* Probably should not expose raw MI2 via TargetConsole
*/
@TargetObjectSchemaInfo(
name = "Session",
elements = {