Add debug hook functions to LuaState

This commit is contained in:
James Roseborough
2009-03-24 22:44:07 +00:00
parent 0f40feb625
commit 643af145d3
5 changed files with 238 additions and 48 deletions

View File

@@ -56,25 +56,23 @@ public class DebugLib extends LFunction {
private static final int DEBUG = 1; private static final int DEBUG = 1;
private static final int GETFENV = 2; private static final int GETFENV = 2;
private static final int GETHOOK = 3; private static final int GETHOOK = 3;
private static final int GETHOOKCOUNT = 4; private static final int GETINFO = 4;
private static final int GETHOOKMASK = 5; private static final int GETLOCAL = 5;
private static final int GETINFO = 6; private static final int GETMETATABLE = 6;
private static final int GETLOCAL = 7; private static final int GETREGISTRY = 7;
private static final int GETMETATABLE = 8; private static final int GETUPVALUE = 8;
private static final int GETREGISTRY = 9; private static final int SETFENV = 9;
private static final int GETUPVALUE = 10; private static final int SETHOOK = 10;
private static final int SETFENV = 11; private static final int SETLOCAL = 11;
private static final int SETHOOK = 12; private static final int SETMETATABLE = 12;
private static final int SETLOCAL = 13; private static final int SETUPVALUE = 13;
private static final int SETMETATABLE = 14; private static final int TRACEBACK = 14;
private static final int SETUPVALUE = 15;
private static final int TRACEBACK = 16;
public static void install( LTable globals ) { public static void install( LuaState vm ) {
LTable debug = new LTable(); LTable debug = new LTable();
for (int i = 0; i < NAMES.length; i++) for (int i = 0; i < NAMES.length; i++)
debug.put(NAMES[i], new DebugLib(i + 1)); debug.put(NAMES[i], new DebugLib(i + 1));
globals.put("debug", debug); vm._G.put("debug", debug);
PackageLib.setIsLoaded("debug", debug); PackageLib.setIsLoaded("debug", debug);
} }
@@ -91,7 +89,7 @@ public class DebugLib extends LFunction {
public boolean luaStackCall( LuaState vm ) { public boolean luaStackCall( LuaState vm ) {
switch ( id ) { switch ( id ) {
case INSTALL: case INSTALL:
install(vm._G); install(vm);
break; break;
case DEBUG: case DEBUG:
debug(vm); debug(vm);
@@ -102,12 +100,6 @@ public class DebugLib extends LFunction {
case GETHOOK: case GETHOOK:
gethook(vm); gethook(vm);
break; break;
case GETHOOKCOUNT:
gethookcount(vm);
break;
case GETHOOKMASK:
gethookmask(vm);
break;
case GETINFO: case GETINFO:
getinfo(vm); getinfo(vm);
break; break;
@@ -148,29 +140,37 @@ public class DebugLib extends LFunction {
} }
private void debug(LuaState vm) { private void debug(LuaState vm) {
// TODO Auto-generated method stub // TODO: interactive console impl
vm.resettop(); vm.resettop();
} }
private void gethook(LuaState vm) { private void gethook(LuaState vm) {
// TODO Auto-generated method stub LuaState threadVm = vm;
if ( vm.gettop() >= 2 )
threadVm = vm.checkthread(2).vm;
vm.resettop(); vm.resettop();
} vm.pushlvalue(threadVm.gethook());
vm.pushinteger(threadVm.gethookmask());
private void gethookcount(LuaState vm) { vm.pushinteger(threadVm.gethookcount());
// TODO Auto-generated method stub
vm.resettop();
vm.pushinteger(0);
}
private void gethookmask(LuaState vm) {
// TODO Auto-generated method stub
vm.resettop();
vm.pushinteger(0);
} }
private void sethook(LuaState vm) { private void sethook(LuaState vm) {
// TODO Auto-generated method stub LuaState threadVm = vm;
if ( vm.gettop() >= 4 ) {
threadVm = vm.checkthread(2).vm;
vm.remove(2);
}
LFunction func = vm.checkfunction(2);
LString str = vm.checklstring(3);
int count = vm.optint(4,0);
int mask = 0;
for ( int i=0; i<str.m_length; i++ )
switch ( str.m_bytes[str.m_offset+i] ) {
case 'c': mask |= LuaState.LUA_MASKCALL; break;
case 'l': mask |= LuaState.LUA_MASKLINE; break;
case 'r': mask |= LuaState.LUA_MASKRET; break;
}
threadVm.sethook(func, mask, count);
vm.resettop(); vm.resettop();
} }
@@ -213,7 +213,8 @@ public class DebugLib extends LFunction {
for (int i = 0, n = what.length(); i < n; i++) { for (int i = 0, n = what.length(); i < n; i++) {
switch (what.charAt(i)) { switch (what.charAt(i)) {
case 'S': { case 'S': {
info.put("source", (closure!=null? closure.p.source: new LString("?"))); info.put("source", (closure!=null? closure.p.source: new LString("@?")));
info.put("short_src", (closure!=null? closure.p.source.substring(1, closure.p.source.m_length-1): new LString("?")));
info.put("linedefined", (closure!=null? closure.p.linedefined: 0)); info.put("linedefined", (closure!=null? closure.p.linedefined: 0));
info.put("lastlinedefined", (closure!=null? closure.p.lastlinedefined: 0)); info.put("lastlinedefined", (closure!=null? closure.p.lastlinedefined: 0));
info.put("what", new LString(what)); info.put("what", new LString(what));
@@ -235,12 +236,13 @@ public class DebugLib extends LFunction {
break; break;
} }
case 'f': { case 'f': {
vm.pushlvalue( func!=null? func: LNil.NIL ); if ( func != null )
info.put( "func", func );
break; break;
} }
case 'L': { case 'L': {
LTable lines = new LTable(); LTable lines = new LTable();
vm.pushlvalue(lines); info.put("activelines", lines);
if ( closure != null ) if ( closure != null )
for ( int j=0, k=1; j<closure.p.lineinfo.length; j++, k++ ) for ( int j=0, k=1; j<closure.p.lineinfo.length; j++, k++ )
lines.put(k, LInteger.valueOf(closure.p.lineinfo[j])); lines.put(k, LInteger.valueOf(closure.p.lineinfo[j]));

View File

@@ -357,5 +357,4 @@ public class Lua {
"value", "value",
}; };
} }

View File

@@ -69,6 +69,7 @@ import org.luaj.lib.TableLib;
* *
*/ */
public class LuaState extends Lua { public class LuaState extends Lua {
/* thread status; 0 is OK */ /* thread status; 0 is OK */
public static final int LUA_YIELD = 1; public static final int LUA_YIELD = 1;
public static final int LUA_ERRRUN = 2; public static final int LUA_ERRRUN = 2;
@@ -80,6 +81,16 @@ public class LuaState extends Lua {
private static final int LUA_MINCALLS = 10; private static final int LUA_MINCALLS = 10;
private static final int MAXTAGLOOP = 100; private static final int MAXTAGLOOP = 100;
// hook function values
private static final int LUA_HOOKCALL = 0;
private static final int LUA_HOOKRET = 1;
private static final int LUA_HOOKLINE = 2;
private static final int LUA_HOOKCOUNT = 3;
private static final int LUA_HOOKTAILRET = 4;
public static final int LUA_MASKCALL = (1 << LUA_HOOKCALL);
public static final int LUA_MASKRET = (1 << LUA_HOOKRET);
public static final int LUA_MASKLINE = (1 << LUA_HOOKLINE);
public int base = 0; public int base = 0;
public int top = 0; public int top = 0;
protected int nresults = -1; protected int nresults = -1;
@@ -92,12 +103,16 @@ public class LuaState extends Lua {
static LuaState mainState; static LuaState mainState;
public LTable _G; public LTable _G;
// main debug hook, overridden by DebugStackState // debug hooks - these MUST NOT be initialized,
protected void debugHooks(int pc) { // so that a later obfuscation step can decide to remove them.
} private boolean hooksenabled;
protected void debugAssert(boolean b) { private int hookmask;
} private int hookcount;
private LFunction hookfunc;
private int hookincr;
private int hookline;
protected void debugAssert(boolean b) {}
// ------------------- constructors --------------------- // ------------------- constructors ---------------------
/** /**
@@ -525,7 +540,9 @@ public class LuaState extends Lua {
ci.top = top; ci.top = top;
// allow debug hooks a chance to operate // allow debug hooks a chance to operate
debugHooks( ci.pc ); if ( hooksenabled ) {
debugBytecodeHooks( ci.pc );
}
// advance program counter // advance program counter
i = code[ci.pc++]; i = code[ci.pc++];
@@ -695,6 +712,11 @@ public class LuaState extends Lua {
// number of return values we need // number of return values we need
c = LuaState.GETARG_C(i); c = LuaState.GETARG_C(i);
// call hook
if ( hooksenabled ) {
debugCallHooks( ci.pc-1 );
}
// make or set up the call // make or set up the call
this.nresults = c - 1; this.nresults = c - 1;
if (this.stack[base].luaStackCall(this)) if (this.stack[base].luaStackCall(this))
@@ -713,6 +735,12 @@ public class LuaState extends Lua {
} }
case LuaState.OP_TAILCALL: { case LuaState.OP_TAILCALL: {
// return hook
if ( hooksenabled ) {
debugTailReturnHooks( ci.pc-1 );
}
// close up values
closeUpVals(base); closeUpVals(base);
// copy down the frame before calling! // copy down the frame before calling!
@@ -755,6 +783,12 @@ public class LuaState extends Lua {
} }
case LuaState.OP_RETURN: { case LuaState.OP_RETURN: {
// return hook
if ( hooksenabled ) {
debugReturnHooks( ci.pc-1 );
}
// close up values
closeUpVals( base ); closeUpVals( base );
// number of return vals to return // number of return vals to return
@@ -2326,4 +2360,119 @@ public class LuaState extends Lua {
top = oldtop; top = oldtop;
} }
} }
// ===========================================================================
// Debug hooks.
// These should be obfuscated out when sethook is never called
// cannot be called from the application.
//
/**
* Set the hook function.
*
* @param func LFunction to call on the hook event
* @param mask combination of LuaState.LUA_MASKLINE, LuaState.LUA_MASKCALL, and LuaState.LUA_MASKRET
* @param count 0, or number of bytecodes between count events.
*/
public void sethook( LFunction func, int mask, int count ) {
hooksenabled = (mask != 0);
hookfunc = func;
hookmask = mask;
hookcount = count;
}
/**
* Get the current hook function, if any.
*
* @return LFunction that is set as the current hook function, or null if not set.
*/
public LFunction gethook() {
return hookfunc;
}
/**
* Get the current hook count.
*
* @return current count, which is # of bytecodes between "count" hook calls
*/
public int gethookcount() {
return hookcount;
}
/**
* Get the current hook mask.
*
* @return current mask as a combination of
* LuaState.LUA_MASKLINE, LuaState.LUA_MASKCALL, and LuaState.LUA_MASKRET
*/
public int gethookmask() {
return hookmask;
}
// line number and count hooks
private void debugBytecodeHooks(int pc) {
if ( hookfunc != null && (hookmask & LUA_MASKLINE) != 0 ) {
int line = debugGetLineNumber(calls[cc]);
if ( line != hookline ) {
hookline = line;
debugCallHook(LUA_HOOKLINE, line);
}
if (hookcount != 0) {
if ( --hookincr <= 0 ) {
hookincr = hookcount;
debugCallHook(LUA_HOOKCOUNT, -1);
}
}
}
}
private void debugCallHooks(int pc) {
if ( hookfunc != null && ((hookmask & LUA_MASKCALL) != 0) ) {
debugCallHook(LUA_HOOKCALL, debugGetLineNumber(calls[cc]));
hookline = -1;
}
}
private void debugReturnHooks(int pc) {
if ( hookfunc != null && ((hookmask & LUA_MASKRET) != 0) ) {
debugCallHook(LUA_HOOKRET, debugGetLineNumber(calls[cc]));
hookline = -1;
}
}
private void debugTailReturnHooks(int pc) {
if ( hookfunc != null && ((hookmask & LUA_MASKRET) != 0) ) {
debugCallHook(LUA_HOOKTAILRET, debugGetLineNumber(calls[cc]));
hookline = -1;
}
}
private int debugGetLineNumber(CallInfo ci) {
int[] lineNumbers = ci.closure.p.lineinfo;
int pc = getCurrentPc(ci);
int line = (lineNumbers != null && lineNumbers.length > pc ?
lineNumbers[pc] :
-1);
return line;
}
private void debugCallHook(int mask, int newline) {
int prevmask = hookmask;
try {
hookmask = 0;
this.pushfunction(hookfunc);
switch ( mask ) {
default: this.pushstring("line"); break;
case LUA_HOOKCOUNT: this.pushstring("count"); break;
case LUA_HOOKCALL: this.pushstring("call"); break;
case LUA_HOOKRET: this.pushstring("return"); break;
case LUA_HOOKTAILRET: this.pushstring("tail return"); break;
}
this.pushinteger(newline);
this.pcall(2, 0, 0);
} finally {
hookmask = prevmask;
}
}
} }

40
src/test/res/debuglib.lua Normal file
View File

@@ -0,0 +1,40 @@
local print = print
print( 'has debug', debug~=nil )
local printinfo = function(...)
for i,a in ipairs(arg) do
if type(a) == 'table' then
print( ' source: '..tostring(a.source) )
print( ' short_src: '..tostring(a.short_src) )
print( ' what: '..tostring(a.what) )
print( ' currentline: '..tostring(a.currentline) )
print( ' linedefined: '..tostring(a.linedefined) )
print( ' lastlinedefined: '..tostring(a.lastlinedefined) )
else
print( tostring(a) )
end
end
end
function test()
local x = 5
function f()
x = x + 1
return x
end
function g()
x = x - 1
return x
end
print(f())
print(g())
return f, g
end
local e,f,g = pcall( test )
print( 'e,f,g', e, type(f), type(g) )
printinfo( 'debug.getinfo(f,"Sl")', pcall(debug.getinfo, f, "Sl") )
printinfo( 'debug.getinfo(g,"Sl")', pcall(debug.getinfo, g, "Sl") )
printinfo( 'debug.getinfo(test,"Sl")', pcall(debug.getinfo, test, "Sl") )

View File

@@ -1 +1 @@
version: 0.92 version: 0.93