diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index 1509bd19f..385b1f659 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -42,6 +42,76 @@ const cLuaState::cRet cLuaState::Return = {}; +//////////////////////////////////////////////////////////////////////////////// +// cLuaStateTracker: + +void cLuaStateTracker::Add(cLuaState & a_LuaState) +{ + auto & Instance = Get(); + cCSLock Lock(Instance.m_CSLuaStates); + Instance.m_LuaStates.push_back(&a_LuaState); +} + + + + +void cLuaStateTracker::Del(cLuaState & a_LuaState) +{ + auto & Instance = Get(); + cCSLock Lock(Instance.m_CSLuaStates); + Instance.m_LuaStates.erase( + std::remove_if( + Instance.m_LuaStates.begin(), Instance.m_LuaStates.end(), + [&a_LuaState](cLuaStatePtr a_StoredLuaState) + { + return (&a_LuaState == a_StoredLuaState); + } + ), + Instance.m_LuaStates.end() + ); +} + + + + + +AString cLuaStateTracker::GetStats(void) +{ + auto & Instance = Get(); + cCSLock Lock(Instance.m_CSLuaStates); + AString res; + int Total = 0; + for (auto state: Instance.m_LuaStates) + { + int Mem = 0; + if (!state->Call("collectgarbage", "count", cLuaState::Return, Mem)) + { + res.append(Printf("Cannot query memory for state \"%s\"\n", state->GetSubsystemName().c_str())); + } + else + { + res.append(Printf("State \"%s\" is using %d KiB of memory\n", state->GetSubsystemName().c_str(), Mem)); + Total += Mem; + } + } + res.append(Printf("Total memory used by Lua: %d KiB\n", Total)); + return res; +} + + + + + +cLuaStateTracker & cLuaStateTracker::Get(void) +{ + static cLuaStateTracker Inst; // The singleton + return Inst; +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cLuaState: @@ -98,6 +168,7 @@ void cLuaState::Create(void) m_LuaState = lua_open(); luaL_openlibs(m_LuaState); m_IsOwned = true; + cLuaStateTracker::Add(*this); } @@ -133,6 +204,7 @@ void cLuaState::Close(void) Detach(); return; } + cLuaStateTracker::Del(*this); lua_close(m_LuaState); m_LuaState = nullptr; m_IsOwned = false; diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index 269a10369..4f688be6c 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -210,6 +210,9 @@ public: /** Returns true if the m_LuaState is valid */ bool IsValid(void) const { return (m_LuaState != nullptr); } + + /** Returns the name of the subsystem, as specified when the instance was created. */ + AString GetSubsystemName(void) const { return m_SubsystemName; } /** Adds the specified path to package. */ void AddPackagePath(const AString & a_PathVariable, const AString & a_Path); @@ -521,3 +524,37 @@ protected: + +/** Keeps track of all create cLuaState instances. +Can query each for the amount of currently used memory. */ +class cLuaStateTracker +{ +public: + /** Adds the specified Lua state to the internal list of LuaStates. */ + static void Add(cLuaState & a_LuaState); + + /** Deletes the specified Lua state from the internal list of LuaStates. */ + static void Del(cLuaState & a_LuaState); + + /** Returns the statistics for all the registered LuaStates. */ + static AString GetStats(void); + +protected: + typedef cLuaState * cLuaStatePtr; + typedef std::vector cLuaStatePtrs; + + /** The internal list of LuaStates. + Protected against multithreaded access by m_CSLuaStates. */ + cLuaStatePtrs m_LuaStates; + + /** Protects m_LuaStates against multithreaded access. */ + cCriticalSection m_CSLuaStates; + + + /** Returns the single instance of this class. */ + static cLuaStateTracker & Get(void); +}; + + + + diff --git a/src/Server.cpp b/src/Server.cpp index a21248695..12c45467d 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -145,6 +145,8 @@ cServer::cServer(void) : m_ShouldLoadOfflinePlayerData(false), m_ShouldLoadNamedPlayerData(true) { + // Initialize the LuaStateTracker singleton before the app goes multithreaded: + cLuaStateTracker::GetStats(); } @@ -522,6 +524,13 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac a_Output.Finished(); return; } + + else if (split[0].compare("luastats") == 0) + { + a_Output.Out(cLuaStateTracker::GetStats()); + a_Output.Finished(); + return; + } #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) else if (split[0].compare("dumpmem") == 0) {