From add892968dfce0796e7e9c0afab4e4d9386769ef Mon Sep 17 00:00:00 2001 From: James Roseborough Date: Sat, 11 Aug 2007 04:53:35 +0000 Subject: [PATCH] Add DebugStackState to be used as a debugging VM in and IDE. --- src/main/java/lua/LuaJVM.java | 21 +- src/main/java/lua/debug/DebugStackState.java | 199 ++++++++++++++++++ .../java/lua/debug/DebugStackStateTest.java | 51 +++++ 3 files changed, 255 insertions(+), 16 deletions(-) create mode 100644 src/main/java/lua/debug/DebugStackState.java create mode 100644 src/test/java/lua/debug/DebugStackStateTest.java diff --git a/src/main/java/lua/LuaJVM.java b/src/main/java/lua/LuaJVM.java index ec6f8eef..0a19ddce 100644 --- a/src/main/java/lua/LuaJVM.java +++ b/src/main/java/lua/LuaJVM.java @@ -25,10 +25,12 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.StringTokenizer; import lua.addon.luajava.LuaJava; import lua.debug.DebugRequestListener; import lua.debug.DebugServer; +import lua.debug.DebugStackState; import lua.io.Closure; import lua.io.LoadState; import lua.io.Proto; @@ -57,6 +59,7 @@ public class LuaJVM implements DebugRequestListener { protected int eventPort; protected String script; protected String[] scriptArgs; + protected DebugStackState state; @SuppressWarnings("static-access") public LuaJVM() { @@ -150,7 +153,7 @@ public class LuaJVM implements DebugRequestListener { LuaJava.install(); // new lua state - StackState state = new StackState(); + state = new DebugStackState(); // convert args to lua int numOfScriptArgs = getScriptArgs().length; @@ -179,21 +182,7 @@ public class LuaJVM implements DebugRequestListener { * @see lua.debug.DebugRequestListener#handleRequest(java.lang.String) */ public String handleRequest(String request) { - //TODO: handle the following requests: - // suspend -- suspend the execution and listen for debug requests - // resume -- resume the execution - // exit -- exit the VM - // set N -- set breakpoint at line N - // clear N -- clear breakpoint at line N - // callgraph -- return the current call graph (i.e. stack frames from - // old to new, include information about file, method, etc.) - // stack -- return the content of the current stack frame, - // listing the (variable, value) pairs - // step -- single step forward (go to next statement) - // variable N M - // -- return the value of variable M from the stack frame N - // (stack frames are indexed from 0) - return null; + return state.handleRequest( request ); } public void stop() { diff --git a/src/main/java/lua/debug/DebugStackState.java b/src/main/java/lua/debug/DebugStackState.java new file mode 100644 index 00000000..fd285774 --- /dev/null +++ b/src/main/java/lua/debug/DebugStackState.java @@ -0,0 +1,199 @@ +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; +import lua.value.LValue; + +public class DebugStackState extends StackState implements DebugRequestListener { + + public Map breakpoints = new HashMap(); + private boolean exiting = false; + private boolean suspended = false; + private boolean stepping = false; + private int lastline = -1; + + public DebugStackState() { + StackState.debugHooksEnabled = true; + } + + // debug hooks + public void debugHooks( int pc ) { + if ( exiting ) + throw new java.lang.RuntimeException("exiting"); + if ( ! suspended ) + return; + synchronized ( this ) { + // vm is in suspended state, although it might also + // be stepping to the next instruction at the same time. + // in either case we need the current line number + int[] li = calls[cc].closure.p.lineinfo; + int line = (li!=null && li.length>pc? li[pc]: -1); + if ( stepping ) { + if ( lastline == line ) + return; + stepping = false; + } + + // save line in case next op is a step + lastline = line; + + // 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 ) + return ""; + StringBuffer sb = new StringBuffer(); + for ( int i=0; i= calls.length || (ci=calls[cc]) == null ) + return ""; + LocVars[] lv = ci.closure.p.locvars; + int n = (lv != null? lv.length: 0); + StringBuffer sb = new StringBuffer(); + for ( int i=0; i= calls.length || (ci=calls[M]) == null ) + return ""; + return String.valueOf( super.stack[ci.base] ); + } +} diff --git a/src/test/java/lua/debug/DebugStackStateTest.java b/src/test/java/lua/debug/DebugStackStateTest.java new file mode 100644 index 00000000..4b3658e2 --- /dev/null +++ b/src/test/java/lua/debug/DebugStackStateTest.java @@ -0,0 +1,51 @@ +package lua.debug; + +import java.io.IOException; +import java.io.InputStream; + +import junit.framework.TestCase; +import lua.io.Closure; +import lua.io.LoadState; +import lua.io.Proto; +import lua.value.LValue; + +public class DebugStackStateTest extends TestCase { + + public void testDebugStackState() throws InterruptedException, IOException { + String script = "/test6.luac"; + + // set up the vm + final DebugStackState state = new DebugStackState(); + InputStream is = getClass().getResourceAsStream( script ); + Proto p = LoadState.undump(state, is, script); + + // create closure and execute + final Closure c = new Closure( state, p ); + + // suspend the vm right away + state.suspend(); + + // start the call processing in its own thread + new Thread() { + public void run() { + try { + state.doCall( c, new LValue[0] ); + } catch ( Exception e ) { + e.printStackTrace(); + } + } + }.start(); + + // step for 25 steps + for ( int i=0; i<10; i++ ) { + state.step(); + Thread.sleep(1000); + System.out.println("--- callgraph="+state.callgraph() ); + System.out.println("--- stack="+state.stack() ); + System.out.println("--- variable(1,0)="+state.variable(1,0) ); + } + + // resume the vm + state.resume(); + } +}