2007-08-11 04:53:35 +00:00
|
|
|
package lua.debug;
|
|
|
|
|
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
import java.util.StringTokenizer;
|
|
|
|
|
|
|
|
|
|
import lua.CallInfo;
|
|
|
|
|
import lua.StackState;
|
|
|
|
|
import lua.io.LocVars;
|
2007-09-18 22:55:22 +00:00
|
|
|
import lua.io.Proto;
|
2007-08-11 04:53:35 +00:00
|
|
|
|
|
|
|
|
public class DebugStackState extends StackState implements DebugRequestListener {
|
2007-09-18 22:55:22 +00:00
|
|
|
|
2007-08-11 04:53:35 +00:00
|
|
|
public Map<Integer,Boolean> breakpoints = new HashMap<Integer,Boolean>();
|
|
|
|
|
private boolean exiting = false;
|
|
|
|
|
private boolean suspended = false;
|
|
|
|
|
private boolean stepping = false;
|
|
|
|
|
private int lastline = -1;
|
|
|
|
|
|
|
|
|
|
public DebugStackState() {
|
|
|
|
|
}
|
|
|
|
|
|
2007-09-18 22:55:22 +00:00
|
|
|
private String getFileLine(int cindex) {
|
|
|
|
|
String func = "?";
|
|
|
|
|
String line = "?";
|
|
|
|
|
String source = "?";
|
|
|
|
|
if ( cindex >= 0 ) {
|
|
|
|
|
CallInfo call = this.calls[cindex];
|
|
|
|
|
Proto p = call.closure.p;
|
|
|
|
|
if ( p != null && p.source != null )
|
|
|
|
|
source = p.source.toJavaString();
|
2007-09-20 18:17:10 +00:00
|
|
|
if ( p.lineinfo != null && p.lineinfo.length > call.pc-1 )
|
|
|
|
|
line = String.valueOf( p.lineinfo[call.pc-1] );
|
2007-09-18 22:55:22 +00:00
|
|
|
// TODO: reverse lookup on function name ????
|
|
|
|
|
func = call.closure.luaAsString().toJavaString();
|
|
|
|
|
}
|
|
|
|
|
return source+":"+line+"("+func+")";
|
|
|
|
|
}
|
|
|
|
|
|
2007-09-21 00:34:14 +00:00
|
|
|
private String addLineInfo( String message ) {
|
|
|
|
|
return getFileLine(cc)+": "+message;
|
|
|
|
|
}
|
|
|
|
|
|
2007-09-20 18:17:10 +00:00
|
|
|
private void printLuaTrace(String message) {
|
2007-09-21 00:34:14 +00:00
|
|
|
System.out.println( "Lua error: "+addLineInfo( message ) );
|
2007-09-18 22:55:22 +00:00
|
|
|
for ( int cindex=cc-1; cindex>=0; cindex-- )
|
2007-09-20 18:17:10 +00:00
|
|
|
System.out.println( "\tcalled by "+getFileLine( cindex ) );
|
2007-09-18 22:55:22 +00:00
|
|
|
}
|
|
|
|
|
|
2007-09-21 00:34:14 +00:00
|
|
|
protected void debugPcallError(Throwable t) {
|
|
|
|
|
System.out.println(addLineInfo("(caught in pcall) "+t.getMessage()));
|
|
|
|
|
System.out.flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void lua_error(String message) {
|
|
|
|
|
throw new RuntimeException( message );
|
|
|
|
|
}
|
|
|
|
|
|
2007-09-18 22:55:22 +00:00
|
|
|
// intercept exceptions and fill in line numbers
|
|
|
|
|
public void exec() {
|
|
|
|
|
try {
|
|
|
|
|
super.exec();
|
2007-09-20 18:17:10 +00:00
|
|
|
} catch ( RuntimeException t ) {
|
|
|
|
|
t.printStackTrace();
|
2007-09-21 00:34:14 +00:00
|
|
|
printLuaTrace(t.getMessage());
|
2007-09-18 22:55:22 +00:00
|
|
|
System.out.flush();
|
2007-09-21 00:34:14 +00:00
|
|
|
throw t;
|
2007-09-18 22:55:22 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-08-11 04:53:35 +00:00
|
|
|
// debug hooks
|
|
|
|
|
public void debugHooks( int pc ) {
|
|
|
|
|
if ( exiting )
|
|
|
|
|
throw new java.lang.RuntimeException("exiting");
|
2007-08-11 05:29:56 +00:00
|
|
|
|
2007-09-21 00:34:14 +00:00
|
|
|
// make sure line numbers are current in any stack traces
|
|
|
|
|
calls[cc].pc = pc;
|
|
|
|
|
|
2007-08-11 04:53:35 +00:00
|
|
|
synchronized ( this ) {
|
2007-08-11 05:29:56 +00:00
|
|
|
|
|
|
|
|
// anytime the line doesn't change we keep going
|
2007-08-11 04:53:35 +00:00
|
|
|
int[] li = calls[cc].closure.p.lineinfo;
|
|
|
|
|
int line = (li!=null && li.length>pc? li[pc]: -1);
|
2007-08-11 05:29:56 +00:00
|
|
|
if ( lastline == line )
|
|
|
|
|
return;
|
2007-08-11 04:53:35 +00:00
|
|
|
|
|
|
|
|
// save line in case next op is a step
|
|
|
|
|
lastline = line;
|
2007-08-11 05:29:56 +00:00
|
|
|
if ( stepping )
|
|
|
|
|
stepping = false;
|
2007-08-11 04:53:35 +00:00
|
|
|
|
2007-08-11 05:29:56 +00:00
|
|
|
// check for a break point if we aren't suspended already
|
|
|
|
|
if ( ! suspended ) {
|
|
|
|
|
if ( breakpoints.containsKey(line) )
|
|
|
|
|
suspended = true;
|
|
|
|
|
else
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2007-08-11 04:53:35 +00:00
|
|
|
// wait for a state change
|
|
|
|
|
while ( suspended && (!exiting) && (!stepping) ) {
|
|
|
|
|
try {
|
|
|
|
|
this.wait();
|
|
|
|
|
} catch ( InterruptedException ie ) {
|
|
|
|
|
ie.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------ commands coming from the debugger -------------------
|
|
|
|
|
|
|
|
|
|
public enum RequestType {
|
|
|
|
|
suspend,
|
|
|
|
|
resume,
|
|
|
|
|
exit,
|
|
|
|
|
set,
|
|
|
|
|
clear,
|
|
|
|
|
callgraph,
|
|
|
|
|
stack,
|
|
|
|
|
step,
|
|
|
|
|
variable,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public String handleRequest(String request) {
|
|
|
|
|
StringTokenizer st = new StringTokenizer( request );
|
|
|
|
|
String req = st.nextToken();
|
|
|
|
|
RequestType rt = RequestType.valueOf(req);
|
|
|
|
|
switch ( rt ) {
|
|
|
|
|
case suspend: suspend(); return "true";
|
|
|
|
|
case resume: resume(); return "true";
|
|
|
|
|
case exit: exit(); return "true";
|
|
|
|
|
case set: set( Integer.parseInt(st.nextToken()) ); return "true";
|
|
|
|
|
case clear: clear( Integer.parseInt(st.nextToken()) ); return "true";
|
|
|
|
|
case callgraph: return callgraph();
|
|
|
|
|
case stack: return stack();
|
|
|
|
|
case step: step(); return "true";
|
|
|
|
|
case variable:
|
|
|
|
|
String N = st.nextToken();
|
|
|
|
|
String M = st.nextToken();
|
|
|
|
|
return variable( Integer.parseInt(N), Integer.parseInt(M) );
|
|
|
|
|
}
|
|
|
|
|
throw new java.lang.IllegalArgumentException( "unkown request type: "+req );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* suspend the execution
|
|
|
|
|
*/
|
|
|
|
|
public void suspend() {
|
|
|
|
|
synchronized ( this ) {
|
|
|
|
|
suspended = true;
|
|
|
|
|
stepping = false;
|
|
|
|
|
lastline = -1;
|
|
|
|
|
this.notify();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* resume the execution
|
|
|
|
|
*/
|
|
|
|
|
public void resume() {
|
|
|
|
|
synchronized ( this ) {
|
|
|
|
|
suspended = false;
|
|
|
|
|
this.notify();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* terminate the execution
|
|
|
|
|
*/
|
|
|
|
|
public void exit() {
|
|
|
|
|
synchronized ( this ) {
|
|
|
|
|
exiting = true;
|
|
|
|
|
this.notify();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* set breakpoint at line N
|
|
|
|
|
* @param N the line to set the breakpoint at
|
|
|
|
|
*/
|
|
|
|
|
public void set( int N ) {
|
|
|
|
|
synchronized ( this ) {
|
|
|
|
|
breakpoints.put( N, Boolean.TRUE );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* clear breakpoint at line N
|
|
|
|
|
*/
|
|
|
|
|
public void clear( int N ) {
|
|
|
|
|
synchronized ( this ) {
|
|
|
|
|
breakpoints.remove( N );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* return the current call graph (i.e. stack frames from
|
|
|
|
|
* old to new, include information about file, method, etc.)
|
|
|
|
|
*/
|
|
|
|
|
public String callgraph() {
|
|
|
|
|
int n = cc;
|
|
|
|
|
if ( n < 0 || n >= calls.length )
|
2007-08-11 05:29:56 +00:00
|
|
|
return "";
|
2007-08-11 04:53:35 +00:00
|
|
|
StringBuffer sb = new StringBuffer();
|
2007-08-11 05:29:56 +00:00
|
|
|
for ( int i=0; i<=n; i++ ) {
|
2007-08-11 04:53:35 +00:00
|
|
|
CallInfo ci = calls[i];
|
|
|
|
|
// TODO: fill this out with proper format, names, etc.
|
2007-08-11 05:29:56 +00:00
|
|
|
sb.append( String.valueOf(ci.closure.p) );
|
2007-08-11 04:53:35 +00:00
|
|
|
sb.append( "\n" );
|
|
|
|
|
}
|
|
|
|
|
return sb.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* return the content of the current stack frame,
|
|
|
|
|
* listing the (variable, value) pairs
|
|
|
|
|
*/
|
|
|
|
|
public String stack() {
|
|
|
|
|
CallInfo ci;
|
|
|
|
|
if ( cc < 0 || cc >= calls.length || (ci=calls[cc]) == null )
|
|
|
|
|
return "<out of scope>";
|
|
|
|
|
LocVars[] lv = ci.closure.p.locvars;
|
|
|
|
|
int n = (lv != null? lv.length: 0);
|
|
|
|
|
StringBuffer sb = new StringBuffer();
|
|
|
|
|
for ( int i=0; i<n; i++ ) {
|
|
|
|
|
// TODO: figure out format
|
|
|
|
|
sb.append( "(" + lv[i].varname + "," + super.stack[ci.base+i] + ")\n" );
|
|
|
|
|
}
|
|
|
|
|
return sb.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* single step forward (go to next statement)
|
|
|
|
|
*/
|
|
|
|
|
public void step() {
|
|
|
|
|
synchronized ( this ) {
|
|
|
|
|
stepping = true;
|
|
|
|
|
this.notify();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* return the value of variable M from the stack frame N
|
|
|
|
|
* (stack frames are indexed from 0)
|
|
|
|
|
*/
|
|
|
|
|
public String variable( int N, int M ) {
|
|
|
|
|
CallInfo ci;
|
|
|
|
|
if ( M < 0 || M >= calls.length || (ci=calls[M]) == null )
|
|
|
|
|
return "<out of scope>";
|
|
|
|
|
return String.valueOf( super.stack[ci.base] );
|
|
|
|
|
}
|
|
|
|
|
}
|