static void _getCurrentStack(lua_State *L, utStack<stackinfo>& stack) { int top = lua_gettop(L); lua_Debug lstack; int stackFrame = 0; MethodBase *lastMethod = NULL; while (true) { // if we get a null result here, we are out of stack if (!lua_getstack(L, stackFrame++, &lstack)) { lua_settop(L, top); return; } // something bad in denmark if (!lua_getinfo(L, "fSl", &lstack)) { lua_settop(L, top); return; } bool cfunc = false; if (lua_iscfunction(L, -1)) { cfunc = true; } lua_rawgeti(L, LUA_GLOBALSINDEX, LSINDEXMETHODLOOKUP); lua_pushvalue(L, -2); lua_rawget(L, -2); if (lua_isnil(L, -1)) { lua_settop(L, top); continue; } MethodBase *methodBase = (MethodBase *)lua_topointer(L, -1); lua_settop(L, top); // we only want the root call, not the pcall wrapper if (cfunc && (lastMethod == methodBase)) { continue; } lastMethod = methodBase; stackinfo si; si.methodBase = methodBase; si.source = methodBase->isNative() ? "[NATIVE]" : lstack.source; si.linenumber = lstack.currentline == -1 ? 0 : lstack.currentline; stack.push(si); } }
// retrieve a Vector of callstack info, or nil in the case of an invalid stack static int getCallStack(lua_State *L, EventType eventType) { int top = lua_gettop(L); lua_Debug ar; int nstack = 0; _CallStackInfo cstack[MAX_CALLSTACK]; // look for method infos for (int i = 0; i < MAX_CALLSTACK; i++) { // if we get a null result here, we are out of stack if (!lua_getstack(L, i, &ar)) { break; } // get function, source, and line if (!lua_getinfo(L, "fSl", &ar)) { continue; } // if we're a cfunction we're not interested in debugging it // as we're a LoomScript debugger! if (lua_iscfunction(L, -1)) { lua_pop(L, 1); // if we are at the top of the stack and we are not an assert event // return a null stack as this isn't an interesting stack to the // debugger if (!i && (eventType != ASSERT_EVENT)) { lua_pushnil(L); return 1; } continue; } // lookup the stack frame's function in the global // function table lua_rawgeti(L, LUA_GLOBALSINDEX, LSINDEXMETHODLOOKUP); lua_pushvalue(L, -2); lua_rawget(L, -2); // if we aren't found in the global functions if (lua_isnil(L, -1)) { // we are at the top of the stack, so this is an invalid stack if (!i) { break; } lua_pop(L, 2); continue; } // get the MethodBase MethodBase *methodBase = (MethodBase *)lua_topointer(L, -1); // if we're native and at the top of the stack, invalid stack if (!i && methodBase->isNative()) { break; } // if we should filter, we need to stop here if (filterMethodBase(methodBase)) { break; } // store out the stack info to our stack array _CallStackInfo *cs = &cstack[nstack++]; cs->method = methodBase; if (!methodBase->isNative()) { cs->source = ar.source; cs->line = ar.currentline; } else { cs->source = "[Native]"; cs->line = 0; } // early our for line events when we have no breakpoints // and haven't hit a Debug.debug() if (eventType == LINE_EVENT) { // check breakpoints if (!i && !stepping) { // we only stop on a breakpoint if (!hasBreakpoint(cs->source, cs->line) && !debugBreak) { lastSourceEvent[0] = 0; lua_pushnil(L); return 1; } } } lua_pop(L, 3); // pop the function, methodbase, and methodlookup } lua_settop(L, top); // reset stack // did we get anything, if not bail early if (!nstack) { lua_pushnil(L); return 1; } // check if the event is from the same line/source as last event _CallStackInfo *cs = &cstack[0]; if (eventType == LINE_EVENT) { // we have a match, so bail if (!strcmp(lastSourceEvent, cs->source) && (lastLineEvent == cs->line)) { lua_pushnil(L); return 1; } // cache for next line event lmAssert(strlen(cs->source) < 2000, "source file > 2000 charaters"); strcpy(lastSourceEvent, cs->source); lastLineEvent = cs->line; } else { // clear cached event lastSourceEvent[0] = 0; lastLineEvent = -1; } // We return a Vector.<CallStackInfo> Type *vectorType = LSLuaState::getLuaState(L)->getType("system.Vector"); Type *csiType = LSLuaState::getLuaState(L)->getType( "system.CallStackInfo"); int sourceOrdinal = csiType->getMemberOrdinal("source"); int lineOrdinal = csiType->getMemberOrdinal("line"); int methodOrdinal = csiType->getMemberOrdinal("method"); // create the vector instance lsr_createinstance(L, vectorType); lua_rawgeti(L, -1, LSINDEXVECTOR); // now we need to loop through and setup our CallStackInfos and get them into our return vector for (UTsize i = 0; i < (UTsize)nstack; i++) { cs = &cstack[i]; lua_pushnumber(L, i); // note that system.CallStackInfo is not a native class, so we just fiddle // it with indexes lsr_createinstance(L, csiType); int csiIdx = lua_gettop(L); lua_pushnumber(L, sourceOrdinal); lua_pushstring(L, cs->source); lua_rawset(L, csiIdx); lua_pushnumber(L, lineOrdinal); lua_pushnumber(L, cs->line); lua_rawset(L, csiIdx); lua_pushnumber(L, methodOrdinal); if (cs->method->isMethod()) { lualoom_pushnative<MethodInfo>(L, (MethodInfo *)cs->method); } else if (cs->method->isConstructor()) { lualoom_pushnative<ConstructorInfo>(L, (ConstructorInfo *)cs->method); } else { lmAssert(0, "Attempting to debug non-method %s:%s", cs->method->getDeclaringType()->getFullName().c_str(), cs->method->getName()); } assert(lua_istable(L, -1)); lua_rawset(L, csiIdx); lua_rawset(L, -3); } lua_pop(L, 1); // pop the vector table lsr_vector_set_length(L, -1, nstack); lmAssert(lua_gettop(L) - top == 1, "getCallStack - stack wasn't properly cleaned up"); return 1; }
/* * Static methods use the lsr_method closure which is generated at class initialization time * Instance methods are bound to a generated lsr_method closure the first time the method is referenced (at runtime) * lsr_method is a thin wrapper which handles catching native calls for profiling, default arguments, etc */ int lsr_method(lua_State *L) { int nargs = lua_gettop(L); MethodBase *method = (MethodBase *)lua_topointer(L, lua_upvalueindex(1)); bool staticCall = method->isStatic(); if (staticCall) { lua_pushvalue(L, lua_upvalueindex(2)); lua_insert(L, 1); // method } else { lua_pushvalue(L, lua_upvalueindex(2)); lua_insert(L, 1); // this (can be a loomscript table or luabridge userdata lua_pushvalue(L, lua_upvalueindex(3)); lua_insert(L, 1); // method } int dargs = nargs; int fidx = method->getFirstDefaultParm(); int varArgIdx = method->getVarArgIndex(); int varArgVectorIdx = lua_gettop(L); // don't consider the varargs in when // checking whether we need to insert default arguments if ((fidx != -1) && (varArgIdx != -1)) { dargs--; } if (dargs < method->getNumParameters()) { // TODO: Report line numbers LOOM-603 // if we have var args and not enough parameters, VM will insert null for ...args value // otherwise, we have a compiler error if (varArgIdx < 0) { lmAssert(fidx >= 0, "Method '%s::%s' called with too few arguments.", method->getDeclaringType()->getFullName().c_str(), method->getStringSignature().c_str()); } bool inserted = false; for (int i = dargs; i < method->getNumParameters(); i++) { if (i == varArgIdx) { break; } lua_pushvalue(L, lua_upvalueindex(i - fidx + (staticCall ? 3 : 4))); inserted = true; nargs++; } // if we inserted *and* have var args, we // need to shift the var args to the end if (inserted && (varArgIdx != -1)) { lua_pushvalue(L, varArgVectorIdx); lua_remove(L, varArgVectorIdx); } } LSLuaState *ls = LSLuaState::getLuaState(L); // error handling lua_getglobal(L, "__ls_traceback"); lua_insert(L, 1); if (lua_pcall(L, nargs + (staticCall ? 0 : 1), LUA_MULTRET, 1)) { ls->triggerRuntimeError("Error calling %s:%s", method->getDeclaringType()->getFullName().c_str(), method->getName()); } // get rid of the traceback lua_remove(L, 1); int nreturn = lua_gettop(L); if (nreturn == 1) { if (method->isNative() && method->isMethod() && lua_isuserdata(L, -1)) { lualoom_pushnative_userdata(L, ((MethodInfo *)method)->getReturnType(), -1); } } return nreturn; }
MethodBase *LSProfiler::getTopMethod(lua_State *L) { int top = lua_gettop(L); lua_Debug stack; // if we get a null result here, we are out of stack if (!lua_getstack(L, 0, &stack)) { lua_settop(L, top); return NULL; } if (!lua_getinfo(L, "f", &stack)) { lua_settop(L, top); return NULL; } bool iscfunc = false; if (lua_iscfunction(L, -1)) { iscfunc = true; } #ifdef LOOM_ENABLE_JIT // We do not receive line return hooks for native calls under JIT :( // So, if we want to profile native methods, we need to be under interpreted VM if (iscfunc) { lua_settop(L, top); return NULL; } #endif lua_rawgeti(L, LUA_GLOBALSINDEX, LSINDEXMETHODLOOKUP); lua_pushvalue(L, -2); lua_rawget(L, -2); if (lua_isnil(L, -1)) { lua_settop(L, top); return NULL; } MethodBase *methodBase = (MethodBase *)lua_topointer(L, -1); lua_settop(L, top); if (iscfunc && !methodBase->isNative()) { return NULL; } if (shouldFilterFunction(methodBase->getFullMemberName())) { return NULL; } return methodBase; }