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); } }
static int _length(lua_State *L) { lmAssert(lua_isfunction(L, 1) || lua_iscfunction(L, 1), "Non-function in Function._length"); // first look in the global method lookup to see if we have a method base to go off lua_rawgeti(L, LUA_GLOBALSINDEX, LSINDEXMETHODLOOKUP); lua_pushvalue(L, 1); lua_rawget(L, -2); if (!lua_isnil(L, -1)) { MethodBase *methodBase = (MethodBase *)lua_topointer(L, -1); lua_pushnumber(L, methodBase->getNumParameters()); return 1; } // we don't, so we better be a local function with an upvalue at index 1 describing the number of parameters const char *upvalue = lua_getupvalue(L, 1, 1); lmAssert(upvalue, "Internal Error: funcinfo not at upvalue 1"); #ifdef LOOM_DEBUG lmAssert(!strncmp(upvalue, "__ls_funcinfo_arginfo", 21), "Internal Error: funcinfo not __ls_funcinfo_arginfo"); #endif lmAssert(lua_isnumber(L, -1), "Internal Error: __ls_funcinfo_arginfo not a number"); // number of args stored in upper 16 bits, so shift and return lua_pushnumber(L, ((unsigned int) lua_tonumber(L, -1)) >> 16); return 1; }
const char *lsr_objecttostring(lua_State *L, int index) { index = lua_absindex(L, index); if (lua_isstring(L, index)) { return lua_tostring(L, index); } if (lua_isnumber(L, index)) { static char nbuffer[1024]; snprintf(nbuffer, 1024, "%f", lua_tonumber(L, 1)); return nbuffer; } if (lua_isboolean(L, index)) { return lua_toboolean(L, index) ? "true" : "false"; } if (lua_isfunction(L, index) || lua_iscfunction(L, index)) { lua_rawgeti(L, LUA_GLOBALSINDEX, LSINDEXMETHODLOOKUP); lua_pushvalue(L, 1); lua_rawget(L, -2); if (lua_isnil(L, -1)) { lua_pop(L, 2); // anonymous function return "system.Function"; } MethodBase *methodBase = (MethodBase *)lua_topointer(L, -1); lua_pop(L, 2); return methodBase->getStringSignature().c_str(); } if (lua_isnil(L, index)) { return "null"; } lualoom_getmember(L, index, "toString"); lua_call(L, 0, 1); const char *sreturn = lua_tostring(L, -1); lua_pop(L, 1); return sreturn; }
static int create(lua_State *L) { if (lua_iscfunction(L, 1)) { // get the method lua_getupvalue(L, 1, 1); MethodBase *method = (MethodBase *)lua_topointer(L, -1); lua_pop(L, 1); // set the _initMethod on the coroutine // instance, as methods require a setup resume // call (they start in suspended state) lua_rawgeti(L, 2, LSINDEXTYPE); Type *type = (Type *)lua_topointer(L, -1); lua_pop(L, 1); assert(type); lua_pushboolean(L, 1); lualoom_setmember(L, 2, "_initMethod"); // if we're not static, we also need to store off the instance if (!method->isStatic()) { lua_getupvalue(L, 1, 2); lualoom_setmember(L, 2, "_this"); } int methodIdx = method->isStatic() ? 2 : 3; lua_getglobal(L, "coroutine"); lua_getfield(L, -1, "create"); lua_getupvalue(L, 1, methodIdx); lua_call(L, 1, LUA_MULTRET); return 1; } if (lua_isfunction(L, 1)) { lua_getglobal(L, "coroutine"); lua_getfield(L, -1, "create"); lua_pushvalue(L, 1); lua_call(L, 1, LUA_MULTRET); lmAssert(lua_isthread(L, -1), "non thread"); return 1; } lua_pushnil(L); return 1; }
Expression *TypeCompiler::visit(CallExpression *call) { MethodBase *methodBase = call->methodBase; call->function->visitExpression(this); // check whether we're calling a methodbase if (methodBase) { lmAssert(methodBase->isMethod(), "Non-method called"); MethodInfo *method = (MethodInfo *)methodBase; generateCall(&call->function->e, call->arguments, method); call->e = call->function->e; } else { lmAssert(call->function->type, "Untyped call"); // if we're calling a delegate we need to load up the call method if (call->function->type->isDelegate()) { MethodInfo *method = (MethodInfo *)call->function->type->findMember("call"); lmAssert(method, "delegate with no call method"); ExpDesc right; BC::initExpDesc(&right, VKNUM, 0); right.u.nval = method->getOrdinal(); BC::expToNextReg(cs->fs, &call->function->e); BC::expToNextReg(cs->fs, &right); BC::expToVal(cs->fs, &right); BC::indexed(cs->fs, &call->function->e, &right); generateCall(&call->function->e, call->arguments, NULL); call->e = call->function->e; } else { // we're directly calling a local, instance (bound), or static method of type Function generateCall(&call->function->e, call->arguments, NULL); call->e = call->function->e; } } return call; }
// Main lua VM debug hook void LSProfiler::profileHook(lua_State *L, lua_Debug *ar) { // Only process calls from the main thread for now #ifdef LOOM_ENABLE_JIT lua_State *mainL = mainthread(G(L)); #else lua_State *mainL = L->l_G->mainthread; #endif if (mainL != L) return; MethodBase *methodBase = NULL; if (ar->event == LUA_HOOKCALL) { methodBase = getTopMethod(L); if (methodBase) { methodStack.push(methodBase); enterMethod(methodBase->getFullMemberName()); } } if (ar->event == LUA_HOOKRET) { methodBase = getTopMethod(L); if (methodBase) { if (methodStack.size()) { methodStack.pop(); leaveMethod(methodBase->getFullMemberName()); } } } }
PropertyInfo *PropertyInfoReader::deserializePropertyInfo(Type *declaringType, json_t *json) { PropertyInfo *pi = new PropertyInfo(); MemberInfoReader::deserialize(pi, json); // handle attr json_t *marray = json_object_get(json, "propertyattributes"); for (size_t i = 0; i < json_array_size(marray); i++) { utString modifier = json_string_value(json_array_get(marray, i)); if (modifier == "static") { pi->attr.isStatic = true; } else if (modifier == "public") { pi->attr.isPublic = true; } else if (modifier == "private") { pi->attr.isPrivate = true; } else if (modifier == "protected") { pi->attr.isProtected = true; } else if (modifier == "native") { pi->attr.isNative = true; } } utString stype = json_string_value(json_object_get(json, "type")); if (stype.size() > 0) { // a shortcut? pi->type = declaringType->getModule()->getAssembly()->getLuaState()->getType( stype.c_str()); assert(pi->type); } json_t *getter = json_object_get(json, "getter"); json_t *setter = json_object_get(json, "setter"); if (getter) { MethodBase *m = NULL; m = MethodReader::deserializeMethodInfo(declaringType, getter); assert(m->isMethod()); pi->getter = (MethodInfo *)m; m->setPropertyInfo(pi); } if (setter) { MethodBase *m = NULL; m = MethodReader::deserializeMethodInfo(declaringType, setter); assert(m->isMethod()); pi->setter = (MethodInfo *)m; m->setPropertyInfo(pi); } json_t *ttypes = json_object_get(json, "templatetypes"); if (ttypes && json_is_object(ttypes)) { TemplateInfo *info = MemberInfoReader::readTemplateTypeInfo(ttypes); assert(info); info->resolveTypes(Assembly::getAssembly(declaringType)->getLuaState()); pi->setTemplateInfo(info); } return pi; }
// 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; }
static int lsr_instanceindex(lua_State *L) { // we hit the instance index metamethod when we can't find a value // in the instance's table, this is a native or a bound method lua_rawgeti(L, 1, LSINDEXTYPE); Type *type = (Type *)lua_topointer(L, -1); lua_pop(L, 1); lmAssert(type, "Missing type on instance index"); if (lua_isnumber(L, 2)) { int ordinal = (int)lua_tonumber(L, 2); MemberInfo *mi = type->getMemberInfoByOrdinal(ordinal); lmAssert(mi, "Unable to find ordinal %s %i", type->getFullName().c_str(), ordinal); if (mi->isStatic()) { lua_rawgeti(L, 1, LSINDEXCLASS); lua_replace(L, 1); lua_gettable(L, 1); return 1; } // we need to look in the class, the result will be cached to instance MethodBase *method = NULL; if (mi->isMethod()) { method = (MethodBase *)mi; } if (method) { lua_rawgeti(L, 1, LSINDEXCLASS); assert(!lua_isnil(L, -1)); int clsIdx = lua_gettop(L); if (!method->isStatic()) { if (method->isFastCall()) { // get the fast call structure pointer lua_pushlightuserdata(L, method->getFastCall()); // get the the "this" if (lua_type(L, 1) == LUA_TTABLE) { lua_rawgeti(L, 1, LSINDEXNATIVE); } else { lua_pushvalue(L, 1); } // unwrap this Detail::UserdataPtr *p1 = (Detail::UserdataPtr *)lua_topointer(L, -1); lua_pushlightuserdata(L, p1->getPointer()); lua_replace(L, -2); lua_pushnumber(L, ordinal); lua_gettable(L, clsIdx); } else { lua_pushlightuserdata(L, method); lua_pushvalue(L, 1); lua_pushnumber(L, ordinal); lua_gettable(L, clsIdx); } assert(!lua_isnil(L, -1)); int nup = 3; int fd = method->getFirstDefaultParm(); if (fd != -1) { utString dname; if (method->isConstructor()) { dname = "__ls_constructor"; } else { dname = method->getName(); } dname += "__default_args"; lua_getfield(L, clsIdx, dname.c_str()); int dargs = lua_gettop(L); for (int i = fd; i < method->getNumParameters(); i++) { lua_pushnumber(L, i); lua_gettable(L, dargs); nup++; } lua_remove(L, dargs); } // check for fast path if (method->isFastCall()) { if (method->getNumParameters()) { // setter lua_pushcclosure(L, lsr_method_fastcall_set, nup); } else { // getter lua_pushcclosure(L, lsr_method_fastcall_get, nup); } } else { // bind the instance method lua_pushcclosure(L, lsr_method, nup); } // store to method lookup, this is worth it // as only happens once per instance method bind // and can now lookup MethodBase on any function // in one table lookup lua_rawgeti(L, LUA_GLOBALSINDEX, LSINDEXMETHODLOOKUP); lua_pushvalue(L, -2); lua_pushlightuserdata(L, method); lua_rawset(L, -3); lua_pop(L, 1); // cache to instance lua_pushvalue(L, -1); lua_rawseti(L, 1, ordinal); return 1; } else { assert(0); } } FieldInfo *field = NULL; if (mi->isField()) { field = (FieldInfo *)mi; } if (field && field->isNative()) { // for primitive fields, we could generate // a direct lookup in the LSINDEXNATIVE table // in bytecode gen, however this only saves for // native vars and not properties // (which should be using fast path anyway and complicates bytecode) // get the native userdata lua_rawgeti(L, 1, LSINDEXNATIVE); lua_pushnumber(L, field->getOrdinal()); lua_gettable(L, -2); // if we are native we need to wrap if (lua_isuserdata(L, -1)) { lualoom_pushnative_userdata(L, field->getType(), -1); } return 1; } // if we get here the value actually is null lua_pushnil(L); return 1; } // if we hit here, this should be an interface access where we have to // look up by string (and cache as these are only ever instance methods) const char *name = lua_tostring(L, 2); const char *pname = name; MemberInfo *mi = NULL; if (!strncmp(name, "__pget_", 7)) { pname = &name[7]; mi = type->findMember(pname, true); lmAssert(mi && mi->isProperty(), "Could not find property getter for '%s' on type '%s'", pname, type->getFullName().c_str()); mi = ((PropertyInfo *)mi)->getGetMethod(); lmAssert(mi, "Found NULL property getter for '%s' on type '%s'", pname, type->getFullName().c_str()); } else if (!strncmp(name, "__pset_", 7)) { pname = &name[7]; mi = type->findMember(pname, true); lmAssert(mi && mi->isProperty(), "Could not find property setter for '%s' on type '%s'", pname, type->getFullName().c_str()); mi = ((PropertyInfo *)mi)->getSetMethod(); lmAssert(mi, "Found NULL property setter for '%s' on type '%s'", pname, type->getFullName().c_str()); } else { mi = type->findMember(name, true); lmAssert(mi, "Unable to find member '%s' via string on instance of type %s", name, type->getFullName().c_str()); assert(mi); assert(mi->isMethod()); assert(mi->getOrdinal()); } lua_pushnumber(L, mi->getOrdinal()); lua_gettable(L, 1); lua_pushstring(L, name); lua_pushvalue(L, -2); lua_rawset(L, 1); return 1; }
void LSProfiler::getCurrentStack(lua_State *L, utStack<MethodBase *>& 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, "f", &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); #ifdef LOOM_ENABLE_JIT // We do not receive line return hooks for native calls under JIT :( // So, don't add to initial stack if (cfunc) { continue; } #endif if (shouldFilterFunction(methodBase->getFullMemberName())) { continue; } // we only want the root call, not the pcall wrapper if (cfunc && (lastMethod == methodBase)) { continue; } lastMethod = methodBase; stack.push(methodBase); } }
void LSProfiler::dumpAllocations(lua_State *L) { lua_gc(L, LUA_GCCOLLECT, 0); #ifdef LOOM_ENABLE_JIT lmLog(gProfilerLogGroup, "Please note: Profiling under JIT does not include native function calls."); lmLog(gProfilerLogGroup, "switch to the interpreted VM in order to gather native method allocation"); #endif utList<LSProfilerTypeAllocation *> allocs; utHashTable<utPointerHashKey, MethodAllocation> methodAggr; // Prepare for object allocation dump for (UTsize i = 0; i < allocations.size(); i++) { LSProfilerTypeAllocation* alloc = allocations.at(i); allocs.push_back(alloc); } lmLog(gProfilerLogGroup, ""); lmLog(gProfilerLogGroup, "Object Allocation Dump"); lmLog(gProfilerLogGroup, "----------------------"); while (allocs.size()) { int best = -1; LSProfilerTypeAllocation *bestType = NULL; for (UTsize i = 0; i < allocs.size(); i++) { LSProfilerTypeAllocation *alloc = allocs.at(i); if (alloc->alive > best) { best = alloc->alive; bestType = alloc; } } lmAssert(bestType, "couldn't get bestType"); allocs.erase(bestType); lmLog(gProfilerLogGroup, "Alive: %i (%i KiB), Total: %i (%i KiB), Type: %s", bestType->alive, bestType->memoryCurrent/1024, bestType->total, bestType->memoryTotal/1024, bestType->type->getFullName().c_str()); if (bestType->anonTotal) { lmLog(gProfilerLogGroup, " Alive: %i, Total %i (Anonymous)", bestType->anonTotal, bestType->anonCurrent); } for (UTsize j = 0; j < bestType->methodCurrent.size(); j++) { MethodBase *methodBase = (MethodBase *)bestType->methodCurrent.keyAt(j).key(); lmAssert(methodBase == (MethodBase *)bestType->methodTotal.keyAt(j).key(), "mismatched methodbase"); int *value1 = bestType->methodCurrent.get(methodBase); int *value2 = bestType->methodTotal.get(methodBase); lmAssert(value1 && value2, "bad pointer on allocation"); lmLog(gProfilerLogGroup, " Alive %i, Total %i (%s)", (*value1), (*value2), methodBase->getFullMemberName()); } } // Aggregate method allocations for (UTsize i = 0; i < allocations.size(); i++) { LSProfilerTypeAllocation* alloc = allocations.at(i); utHashTable<utPointerHashKey, int> &methodCurrent = alloc->methodCurrent; utHashTable<utPointerHashKey, int> &methodTotal = alloc->methodTotal; for (UTsize j = 0; j < methodCurrent.size(); j++) { utPointerHashKey key = methodCurrent.keyAt(j); int *current = methodCurrent.get(key); lmAssert(current != NULL, "Internal hash table error"); int *total = methodTotal.get(key); lmAssert(total != NULL, "Internal total hash table inconsistency error"); MethodAllocation *aggr = methodAggr.get(key); if (aggr == NULL) { // First seen method, insert initial values MethodAllocation ma; ma.currentCount = *current; ma.totalCount = *total; ma.currentBytes = alloc->memoryCurrent; ma.totalBytes = alloc->memoryTotal; ma.allocations.push_back(alloc); methodAggr.insert(key, ma); } else { // Method already seen, update tracked values aggr->currentCount += *current; aggr->totalCount += *total; aggr->currentBytes += alloc->memoryCurrent; aggr->totalBytes += alloc->memoryTotal; aggr->allocations.push_back(alloc); } } } utArray<int> sortByTotal; sortByTotal.resize(methodAggr.size()); for (UTsize i = 0; i < sortByTotal.size(); i++) sortByTotal[i] = i; sortMethods = &methodAggr; SDL_qsort((void*)sortByTotal.ptr(), sortByTotal.size(), sizeof(int), sortMethodsByTotalCount); sortMethods = NULL; lmLog(gProfilerLogGroup, ""); lmLog(gProfilerLogGroup, "Aggregated Method Allocation"); lmLog(gProfilerLogGroup, "----------------------------"); for (UTsize i = 0; i < methodAggr.size(); i++) { utPointerHashKey key = methodAggr.keyAt(sortByTotal[i]); MethodBase *methodBase = (MethodBase *)key.key(); lmAssert(methodBase != NULL, "Internal aggregated method base missing"); MethodAllocation &ma = *methodAggr.get(key); lmLog(gProfilerLogGroup, "Total: %i (%i KiB), Alive: %i (%i KiB), Method: %s", ma.totalCount, ma.totalBytes/1024, ma.currentCount, ma.currentBytes/1024, methodBase->getFullMemberName()); //SDL_qsort((void*)ma.allocations.ptr(), ma.allocations.size(), sizeof(LSProfilerTypeAllocation*), sortAllocsByTotal); for (UTsize j = 0; j < ma.allocations.size(); j++) { LSProfilerTypeAllocation* alloc = ma.allocations.at(j); int &methodCurrent = *alloc->methodCurrent.get(key); int &methodTotal = *alloc->methodTotal.get(key); lmLog(gProfilerLogGroup, " Total: %i / %i (%i kiB), Alive: %i / %i (%i KiB), Type: %s", methodTotal, alloc->total, alloc->memoryTotal / 1024, methodCurrent, alloc->alive, alloc->memoryCurrent / 1024, alloc->type->getFullName().c_str()); } } }
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; }
static int _call(lua_State *L) { // position 1 is the function // position 2 is the thisObject // position 3 is the varargs // look in global method lookup for methodbase lua_rawgeti(L, LUA_GLOBALSINDEX, LSINDEXMETHODLOOKUP); lua_pushvalue(L, 1); lua_rawget(L, -2); MethodBase *methodBase = NULL; int varArgs = -1; if (!lua_isnil(L, -1)) { methodBase = (MethodBase *)lua_topointer(L, -1); varArgs = methodBase->getVarArgIndex(); lua_pop(L, 2); } else { // we better be a local function with an upvalue at index 1 describing the parameter index of varargs const char *upvalue = lua_getupvalue(L, 1, 1); lmAssert(upvalue, "Internal Error: funcinfo not at upvalue 1"); #ifdef LOOM_DEBUG lmAssert(!strncmp(upvalue, "__ls_funcinfo_arginfo", 21), "Internal Error: funcinfo not __ls_funcinfo_arginfo"); #endif lmAssert(lua_isnumber(L, -1), "Internal Error: __ls_funcinfo_arginfo not a number"); // vararg count is packed into lower 16 bits, with 0xFFFF stored for no-varargs unsigned int mask = ((( unsigned int) lua_tonumber(L, -1)) & 0x0000FFFF); varArgs = mask == 0xFFFF ? -1 : mask; lua_pop(L, 3); } // check for static call if (lua_isnil(L, 2)) { // remove the this object (which should be null for static call/apply) lua_remove(L, 2); } else { // otherwise, we better be an instance method int top = lua_gettop(L); if (!methodBase) { lua_pushstring(L, "MethodBase is missing from function table in Function.call(this, ...)"); lua_error(L); } lua_pushnumber(L, methodBase->getOrdinal()); lua_gettable(L, 2); if (lua_isnil(L, -1)) { char error[512]; snprintf(error, 512, "Unable to resolve instance method %s for Function.call(this, ...)", methodBase->getStringSignature().c_str()); lua_pushstring(L, error); lua_error(L); } lua_replace(L, 1); lua_settop(L, top); lua_remove(L, 2); } int nargs = 0; if (!lua_isnil(L, 2)) { // we have a varargs array of values // get the length int vlength = lsr_vector_get_length(L, 2); // retrieve the interval vector store lua_rawgeti(L, 2, LSINDEXVECTOR); int vindex = lua_gettop(L); // loop through the values and unwind for (int i = 0; i < vlength; i++) { // if we hit the varArgs index, the rest wants to be a vector if (i == varArgs) { // we're at the var args argument and have some left if (i) { // shift and store new length for (int j = 0; j < vlength - i; j++) { lua_rawgeti(L, vindex, j + varArgs); lua_rawseti(L, vindex, j); } lsr_vector_set_length(L, 2, vlength - varArgs); } // reuse the varargs vector in the call, as an arg lua_pushvalue(L, 2); nargs++; // outta here break; } else { // unwind and keep going lua_rawgeti(L, vindex, i); nargs++; } } // remove vector table lua_remove(L, vindex); // ... and varargs lua_remove(L, 2); } else { // no args, so remove the null lua_remove(L, 2); } // and call lua_call(L, nargs, 1); return 1; }