Exemple #1
0
void loom_asset_registerType(unsigned int type, LoomAssetDeserializeCallback deserializer, LoomAssetRecognizerCallback recognizer)
{
    lmAssert(gAssetDeserializerMap.find(type) == UT_NPOS, "Asset type already registered!");

    gAssetDeserializerMap.insert(type, deserializer);
    gRecognizerList.push_back(recognizer);
}
Exemple #2
0
static loom_asset_t *loom_asset_getAssetByName(const char *name, int create)
{
    utHashedString key        = platform_normalizePath(name);
    loom_asset_t   **assetPtr = gAssetHash.get(key);
    loom_asset_t   *asset     = assetPtr ? *assetPtr : NULL;

    if ((asset == NULL) && create)
    {
        // Create one.
        asset       = lmNew(gAssetAllocator) loom_asset_t;
        asset->name = strdup(name);
        gAssetHash.insert(key, asset);
    }

    return asset;
}
Exemple #3
0
void loom_asset_initialize(const char *rootUri)
{
    // Set up the lock for the mutex.
    lmAssert(gAssetLock == NULL, "Double initialization!");
    gAssetLock = loom_mutex_create();

    // Note the CWD.
    char tmpBuff[1024];
    platform_getCurrentWorkingDir(tmpBuff, 1024);
    lmLog(gAssetLogGroup, "Current working directory ='%s'", tmpBuff);

    // And the allocator.
    //gAssetAllocator = loom_allocator_initializeTrackerProxyAllocator(loom_allocator_getGlobalHeap());
    gAssetAllocator = (loom_allocator_getGlobalHeap());

    // Clear, it might have been filled up before (for unit tests)
    gAssetLoadQueue.clear();
    gAssetHash.clear();

    // Asset server connection state.
    gAssetServerSocketLock = loom_mutex_create();

    // And set up some default asset types.
    loom_asset_registerType(LATText, loom_asset_textDeserializer, loom_asset_textRecognizer);
    loom_asset_registerType(LATBinary, loom_asset_binaryDeserializer, loom_asset_binaryRecognizer);

    loom_asset_registerImageAsset();
    loom_asset_registerSoundAsset();
    loom_asset_registerScriptAsset();

    // Listen to log and send it if we have a connection.
    loom_log_addListener(loom_asset_logListener, NULL);
}
Exemple #4
0
    static void regenerateSourceBreakpoints()
    {
        sourceBreakpoints.clear();

        for (UTsize i = 0; i < breakpoints.size(); i++)
        {
            Breakpoint *bp = breakpoints.at(i);

            utFastStringHash fhash(bp->source);

            if (sourceBreakpoints.find(fhash) == UT_NPOS)
            {
                utArray<Breakpoint *> bps;
                sourceBreakpoints.insert(fhash, bps);
            }

            sourceBreakpoints.get(fhash)->push_back(bp);
        }
    }
Exemple #5
0
// Clears the asset name cache that is built up
// through loom_asset_lock and others
static void loom_asset_clear()
{
    utHashTableIterator<utHashTable<utHashedString, loom_asset_t *> > assetIterator(gAssetHash);
    while (assetIterator.hasMoreElements())
    {
        utHashedString key = assetIterator.peekNextKey();
        lmDelete(NULL, assetIterator.peekNextValue());
        assetIterator.next();
    }
    gAssetHash.clear();
}
Exemple #6
0
static loom_asset_t *loom_asset_getAssetByName(const char *name, int create)
{
    loom_mutex_lock(gAssetLock);
    static char normalized[4096];
    strncpy(normalized, name, sizeof(normalized));
    platform_normalizePath(normalized);
    utHashedString key        = normalized;
    loom_mutex_unlock(gAssetLock);
    loom_asset_t   **assetPtr = gAssetHash.get(key);
    loom_asset_t   *asset     = assetPtr ? *assetPtr : NULL;

    if ((asset == NULL) && create)
    {
        // Create one.
        asset       = lmNew(gAssetAllocator) loom_asset_t;
        asset->name = name;
        gAssetHash.insert(key, asset);
    }

    return asset;
}
Exemple #7
0
void loom_asset_shutdown()
{
    loom_asset_flushAll();

    // Clear out our queues and maps.
    gAssetDeserializerMap.clear();
    gRecognizerList.clear();

    lmAssert(gAssetLock != NULL, "Shutdown without being initialized!");
    loom_mutex_destroy(gAssetLock);
    gAssetLock = NULL;
}
Exemple #8
0
// Helper to deserialize an asset, routing to the right function by type.
static void *loom_asset_deserializeAsset(const utString &path, int type, int size, void *ptr, LoomAssetCleanupCallback *dtor)
{
    lmAssert(gAssetDeserializerMap.find(type) != UT_NPOS, "Can't deserialize asset, no deserializer was set for type %x!", type);
    LoomAssetDeserializeCallback ladc = *gAssetDeserializerMap.get(type);

    if (ladc == NULL)
    {
        lmLogError(gAssetLogGroup, "Failed deserialize asset '%s', deserializer was not found for type '%x'!", path.c_str(), type);
        return NULL;
    }

   void *assetBits = ladc(ptr, size, dtor);

    if (assetBits == NULL)
    {
        lmLogError(gAssetLogGroup, "Failed to deserialize asset '%s', deserializer returned NULL for type '%x'!", path.c_str(), type);
        return NULL;
    }

    return assetBits;
}
    const char *getHeaderField(const char *key)
    {
        utString *val = header.get(key);

        if (val != NULL)
        {
            return val->c_str();
        }
        else
        {
            return NULL;
        }
    }
Exemple #10
0
static inline void primeProfiler()
{
    const char       *path = ".......";
    LoomProfilerRoot **prd = dynamicProfilerRoots[path];

    if (prd == NULL)
    {
        dynamicProfilerRoots.insert(path, new LoomProfilerRoot(strdup(path)));
        prd = dynamicProfilerRoots[path];
    }

    gLoomProfiler->hashPush(*prd);
    gLoomProfiler->hashPop(*prd);
}
Exemple #11
0
void LSProfiler::enterMethod(const char *fullPath)
{
    if (!isEnabled())
    {
        return;
    }

    //printf("Entering %s\n", fullPath);

    LoomProfilerRoot **prd = dynamicProfilerRoots[fullPath];
    if (prd == NULL)
    {
        dynamicProfilerRoots.insert(fullPath, new LoomProfilerRoot(strdup(fullPath)));
        prd = dynamicProfilerRoots[fullPath];
    }

    gLoomProfiler->hashPush(*prd);
}
Exemple #12
0
    // tests whether the given breakpoint exists
    static bool hasBreakpoint(const char *source, int line)
    {
        utArray<Breakpoint *> *bps = sourceBreakpoints.get(utFastStringHash(source));

        if (!bps)
        {
            return false;
        }

        for (UTsize i = 0; i < bps->size(); i++)
        {
            if (bps->at(i)->line == line)
            {
                return true;
            }
        }

        return false;
    }
Exemple #13
0
void loom_asset_registerType(unsigned int type, LoomAssetDeserializeCallback deserializer, LoomAssetRecognizerCallback recognizer)
{
    gAssetDeserializerMap.insert(type, deserializer);
    gRecognizerList.push_back(recognizer);
}
Exemple #14
0
namespace LS
{
static utHashTable<utFastStringHash, LoomProfilerRoot *> dynamicProfilerRoots;

bool LSProfiler::enabled = false;
utStack<MethodBase *> LSProfiler::methodStack;
utHashTable<utPointerHashKey, LSProfilerTypeAllocation *> LSProfiler::allocations;
utHashTable<utPointerHashKey, MethodAllocation> *LSProfiler::sortMethods = NULL;

lmDefineLogGroup(gProfilerLogGroup, "profiler", 1, LoomLogInfo);

static inline void primeProfiler()
{
    const char       *path = ".......";
    LoomProfilerRoot **prd = dynamicProfilerRoots[path];

    if (prd == NULL)
    {
        dynamicProfilerRoots.insert(path, new LoomProfilerRoot(strdup(path)));
        prd = dynamicProfilerRoots[path];
    }

    gLoomProfiler->hashPush(*prd);
    gLoomProfiler->hashPop(*prd);
}


static inline bool shouldFilterFunction(const char *fullPath)
{
    if (!strncmp(fullPath, "system.Profiler.", 16))
    {
        return true;
    }

    if (!strncmp(fullPath, "system.debugger.", 16))
    {
        return true;
    }

    if (!strncmp(fullPath, "system.BaseDelegate.", 20))
    {
        return true;
    }

    if (!strncmp(fullPath, "system.Coroutine.", 17))
    {
        return true;
    }

    return false;
}


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);
    }
}


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;
}


void LSProfiler::enable(lua_State *L)
{
    if (enabled)
    {
        return;
    }

    enabled = true;

    updateState(L);
}

void LSProfiler::disable(lua_State *L)
{
    if (!enabled)
    {
        return;
    }

    enabled = false;

    updateState(L);
}

void LSProfiler::updateState(lua_State *L) {
    gLoomProfiler->enable(enabled);

    // push pop to get the profiler enabled and on the next profiler frame
    primeProfiler();

    methodStack.clear();
    clearAllocations();

    if (enabled)
    {
        utStack<MethodBase *> stack;

        getCurrentStack(L, stack);

        while (stack.size())
        {
            if (!shouldFilterFunction(stack.peek(0)->getFullMemberName()))
            {
                methodStack.push(stack.peek(0));
                enterMethod(stack.peek(0)->getFullMemberName());
            }

            stack.pop();
        }

        lua_sethook(L, profileHook, LUA_MASKRET | LUA_MASKCALL, 1);
    }
    else
    {
        lua_sethook(L, profileHook, 0, 1);
    }
}


void LSProfiler::enterMethod(const char *fullPath)
{
    if (!isEnabled())
    {
        return;
    }

    //printf("Entering %s\n", fullPath);

    LoomProfilerRoot **prd = dynamicProfilerRoots[fullPath];
    if (prd == NULL)
    {
        dynamicProfilerRoots.insert(fullPath, new LoomProfilerRoot(strdup(fullPath)));
        prd = dynamicProfilerRoots[fullPath];
    }

    gLoomProfiler->hashPush(*prd);
}


void LSProfiler::leaveMethod(const char *fullPath)
{
    if (!isEnabled())
    {
        return;
    }

    //printf("Leaving %s\n", fullPath);

    LoomProfilerRoot **prd = dynamicProfilerRoots[fullPath];
    lmAssert(prd, "Should never leave a method we didn't enter first!");
    gLoomProfiler->hashPop(*prd);
}


// 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());
            }
        }
    }
}


void LSProfiler::dump(lua_State *L)
{
    dumpAllocations(L);

    while (methodStack.size())
    {
        leaveMethod(methodStack.peek(0)->getFullMemberName());
        methodStack.pop();
    }

    gLoomProfiler->dumpToConsole();

#ifdef LOOM_ENABLE_JIT
    lmLog(gProfilerLogGroup, "");
    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 timings");
#endif
}


void LSProfiler::reset(lua_State *L)
{
    if (gLoomProfiler)
    {
        gLoomProfiler->reset();
    }

    disable(L);

    methodStack.clear();
    clearAllocations();
}

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());
        }
    }

}

int LSProfiler::sortMethodsByTotalCount(const void *a, const void *b)
{
    int va = sortMethods->at(*(UTsize*)a).totalCount;
    int vb = sortMethods->at(*(UTsize*)b).totalCount;
    return va > vb ? -1 : va < vb ? 1 : 0;
}

int LSProfiler::sortAllocsByTotal(const void *a, const void *b)
{
    int va = ((LSProfilerTypeAllocation*)a)->total;
    int vb = ((LSProfilerTypeAllocation*)b)->total;
    return va > vb ? -1 : va < vb ? 1 : 0;
}

}
Exemple #15
0
void DLLEXPORT assetAgent_set(const char *key, const char *value) {
    utFastStringHash hash = utFastStringHash(key);
    gOptions.set(hash, value);
}
 void setHeaderField(const char *key, const char *value)
 {
     header.insert(key, value);
 }
Exemple #17
0
static utString* optionGet(const char *key) {
    utFastStringHash hash = utFastStringHash(key);
    return gOptions.get(hash);
}