Overview of MI package.
This package provides facilities for sending MI commands to an MI
engine and processing MI records returned from the engine.
It is based on this
MI specification.
Terms
MI Machine Interface
engine A process that receives MI commands via its stdin and sends MI
records via it's stdout.
Goals
R1 Engine independence.
While MI was specifically designed for interaction with gdb, this package
does not assume that and mostly works with the syntax of MI.
For example, the value of the prompt is not hard-coded and neither are
various variable names.
If you "grep -i gdb *.java" you should not find any hits other than in URLs.
R2 Connection independence.
This package does not provide services for starting the engine nor
facilities for reading and writing IO.
o Formed MICommands are given to it and passed on via the
MICOmmandInjector interface.
o Raw lines are given to it and dispatched via abstract methods
in MIProxy and MICommand.
R3 NetBeans independence.
This package does not import any netbeans APIs and constitutes a
"library". Therefore it can be used in other applications.
Command management
[ TBF ]
Multi-threading considerations
[ TBF ]
Cookbook
1. Run a gdb process and get it's i/o streams.
This is the absolute minimum. A more sophisticated setup could use a
org.netbeans.lib.terminalemulator.Term and create a variation on
org.netbeans.lib.terminalemulator.LineDiscipline to intercept lines
coming from gdb and inject lines to gdb while echoing all (or some)
of it in the Term.
2. Create a MICommandInjector to send commands to gdb:
class MyInjector implements MICommandInjector {
protected void inject(String line) {
gdbWriter.write(line);
gdbWriter.flush();
}
}
MICommandInjector injector = new MyInjector();
3. Create a MIProxy:
class MyMIProxy extends MIProxy {
public MyMIProxy(MICommandInjector injector) {
super(injector, "(gdb)");
}
// All the protected methods are optional.
// MIProxy will automatically dispatch 'result' and
// 'async' records via their MICommands.
//
// If you anticipate stream records you should override
// logStreamOutput(), consoleStreamOutput(), and
// targetStreamOutput(). For example:
protected void consoleStreamOutput(MIRecord record) {
String data = record.stream();
// send data to proper console
}
}
MIProxy myMIProxy = new MyMIProxy(injector);
4. Start a thread to read gdb output and have it do this:
...
String line = gdbBufferedReader.readLine();
myMIProxy.processLine(line);
// processLine() will ultimately call back on specific MICommands
// submitted earlier.
5. For each action being performed create an MICOmmand and send it via
the proxy:
public void stepInto() {
MICommand cmd = new MICommand("-exec-step") {
public void onDone(MIRecord record) {
finish();
}
public void onRunning(MIRecord record) {
// adjust running/stopped state.
}
public void onError(MIRecord record) {
// adjust running/stopped state.
// post error as needed
finish();
}
public void onRunning(MIRecord record) {
// adjust running/stopped state.
finish();
}
public void onExit(MIRecord record) {
// unexpected: post error as needed
finish();
}
public void onOther(MIRecord record) {
// unexpected: post error as needed
finish();
}
}
myMIProxy.send(cmd);
}
NOTE:
It's unclear to me which class of record actually finishes a
particular command.
- It's concievable that one can receive multiple errors.
- Also one can receive a 'running' followed by an 'error' and
so the running state needs to be re-adjusted.
- A step seems to get terminated by a 'stopped'; I've never seen
a 'done', so perhaps one should process onDone() as unexpected?
With time all of this will hopefully become clearer.
TODO
- c-string parser needs to accept octal and other less common escapes.
- Provide a variable "intern"ing facility so variable matching can be done
using '==' instead of equals.