From af68caaf85af178db4b036f07bb97604013f57d1 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 22 Oct 2007 08:53:15 +0000 Subject: [PATCH] - sourcemod now intelligently unloads plugins which are leaking insane amounts of handles. these unloads get logged to sourcemod_fatal.log - unloading is now delayed if a plugin is in the middle of a callstack --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%401644 --- core/Logger.cpp | 22 +++---- core/systems/HandleSys.cpp | 111 ++++++++++++++++++++++++++++++---- core/systems/HandleSys.h | 3 + core/systems/PluginSys.cpp | 26 ++++++++ core/systems/PluginSys.h | 2 + core/vm/sp_vm_basecontext.cpp | 5 ++ core/vm/sp_vm_basecontext.h | 1 + public/sourcepawn/sp_vm_api.h | 7 +++ 8 files changed, 149 insertions(+), 28 deletions(-) diff --git a/core/Logger.cpp b/core/Logger.cpp index c383e0b6..e1c3df40 100644 --- a/core/Logger.cpp +++ b/core/Logger.cpp @@ -487,26 +487,18 @@ void Logger::LogFatal(const char *msg, ...) */ va_list ap; - char buffer[3072]; - - va_start(ap, msg); - UTIL_FormatArgs(buffer, sizeof(buffer), msg, ap); - va_end(ap); - - char date[32]; - time_t t; - GetAdjustedTime(&t); - tm *curtime = localtime(&t); - strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime); - - g_SMAPI->ConPrintf("L %s: %s\n", date, buffer); - char path[PLATFORM_MAX_PATH]; + g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "sourcemod_fatal.log"); + FILE *fp = fopen(path, "at"); if (fp) { - fprintf(fp, "%s\n", buffer); + m_Active = true; + va_start(ap, msg); + LogToOpenFileEx(fp, msg, ap); + va_end(ap); + m_Active = false; fclose(fp); } } diff --git a/core/systems/HandleSys.cpp b/core/systems/HandleSys.cpp index 7dfb3f09..6c63fe62 100644 --- a/core/systems/HandleSys.cpp +++ b/core/systems/HandleSys.cpp @@ -33,6 +33,7 @@ #include "ShareSys.h" #include "PluginSys.h" #include "ExtensionSys.h" +#include "Logger.h" #include #include @@ -260,6 +261,24 @@ bool HandleSystem::FindHandleType(const char *name, HandleType_t *type) return true; } +HandleError HandleSystem::TryAllocHandle(unsigned int *handle) +{ + if (m_FreeHandles == 0) + { + if (m_HandleTail >= HANDLESYS_MAX_HANDLES) + { + return HandleError_Limit;; + } + *handle = ++m_HandleTail; + } + else + { + *handle = m_Handles[m_FreeHandles--].freeID; + } + + return HandleError_None; +} + HandleError HandleSystem::MakePrimHandle(HandleType_t type, QHandle **in_pHandle, unsigned int *in_index, @@ -267,7 +286,9 @@ HandleError HandleSystem::MakePrimHandle(HandleType_t type, IdentityToken_t *owner, bool identity) { + HandleError err; unsigned int owner_index = 0; + bool retried_alloc = false; if (owner && (IdentityHandle(owner, &owner_index) != HandleError_None)) { @@ -275,15 +296,13 @@ HandleError HandleSystem::MakePrimHandle(HandleType_t type, } unsigned int handle; - if (m_FreeHandles == 0) + if ((err = TryAllocHandle(&handle)) != HandleError_None) { - if (m_HandleTail >= HANDLESYS_MAX_HANDLES) + if (!TryAndFreeSomeHandles() + || (err = TryAllocHandle(&handle)) != HandleError_None) { - return HandleError_Limit;; + return err; } - handle = ++m_HandleTail; - } else { - handle = m_Handles[m_FreeHandles--].freeID; } QHandle *pHandle = &m_Handles[handle]; @@ -329,7 +348,9 @@ HandleError HandleSystem::MakePrimHandle(HandleType_t type, pIdentity->ch_prev = handle; pIdentity->ch_next = handle; pHandle->ch_prev = 0; - } else { + } + else + { /* Link previous node to us (forward) */ m_Handles[pIdentity->ch_next].ch_next = handle; /* Link us to previous node (backwards) */ @@ -338,7 +359,9 @@ HandleError HandleSystem::MakePrimHandle(HandleType_t type, pIdentity->ch_next = handle; } pIdentity->refcount++; - } else { + } + else + { pHandle->ch_prev = 0; } @@ -918,11 +941,65 @@ bool HandleSystem::InitAccessDefaults(TypeAccess *pTypeAccess, HandleAccess *pHa return true; } +bool HandleSystem::TryAndFreeSomeHandles() +{ + IPluginIterator *pl_iter = g_PluginSys.GetPluginIterator(); + IPlugin *highest_owner = NULL; + unsigned int highest_handle_count = 0; + + /* Search all plugins */ + while (pl_iter->MorePlugins()) + { + IPlugin *plugin = pl_iter->GetPlugin(); + IdentityToken_t *identity = plugin->GetIdentity(); + unsigned int handle_count = 0; + + if (identity == NULL) + { + continue; + } + + /* Search all handles */ + for (unsigned int i = 1; i <= m_HandleTail; i++) + { + if (m_Handles[i].set != HandleSet_Used) + { + continue; + } + if (m_Handles[i].owner == identity) + { + handle_count++; + } + } + + if (handle_count > highest_handle_count) + { + highest_owner = plugin; + highest_handle_count = handle_count; + } + + pl_iter->NextPlugin(); + } + + if (highest_owner == NULL || highest_handle_count == 0) + { + return false; + } + + g_Logger.LogFatal("[SM] MEMORY LEAK DETECTED IN PLUGIN (file \"%s\")", highest_owner->GetFilename()); + g_Logger.LogFatal("[SM] Reloading plugin to free %d handles.", highest_handle_count); + g_Logger.LogFatal("[SM] Contact the author(s) of this plugin to correct this error.", highest_handle_count); + + highest_owner->GetContext()->n_err = SP_ERROR_MEMACCESS; + + return g_PluginSys.UnloadPlugin(highest_owner); +} + void HandleSystem::Dump(FILE *fp) { fprintf(fp, "%-10.10s\t%-20.20s\t%-20.20s\n", "Handle", "Owner", "Type"); fprintf(fp, "---------------------------------------------\n"); - for (unsigned int i=1; i<=m_HandleTail; i++) + for (unsigned int i = 1; i <= m_HandleTail; i++) { if (m_Handles[i].set != HandleSet_Used) { @@ -938,14 +1015,20 @@ void HandleSystem::Dump(FILE *fp) if (pOwner == g_pCoreIdent) { owner = "CORE"; - } else if (pOwner == g_PluginSys.GetIdentity()) { + } + else if (pOwner == g_PluginSys.GetIdentity()) + { owner = "PLUGINSYS"; - } else { + } + else + { CExtension *ext = g_Extensions.GetExtensionFromIdent(pOwner); if (ext) { owner = ext->GetFilename(); - } else { + } + else + { CPlugin *pPlugin = g_PluginSys.GetPluginFromIdentity(pOwner); if (pPlugin) { @@ -953,7 +1036,9 @@ void HandleSystem::Dump(FILE *fp) } } } - } else { + } + else + { owner = "NONE"; } const char *type = "ANON"; diff --git a/core/systems/HandleSys.h b/core/systems/HandleSys.h index dfcc960d..e0ef22b4 100644 --- a/core/systems/HandleSys.h +++ b/core/systems/HandleSys.h @@ -201,6 +201,9 @@ protected: void UnlinkHandleFromOwner(QHandle *pHandle, unsigned int index); HandleError CloneHandle(QHandle *pHandle, unsigned int index, Handle_t *newhandle, IdentityToken_t *newOwner); Handle_t CreateHandleInt(HandleType_t type, void *object, const HandleSecurity *pSec, HandleError *err, const HandleAccess *pAccess, bool identity); + + bool TryAndFreeSomeHandles(); + HandleError TryAllocHandle(unsigned int *handle); private: QHandle *m_Handles; QHandleType *m_Types; diff --git a/core/systems/PluginSys.cpp b/core/systems/PluginSys.cpp index d8e4e975..a7bb7341 100644 --- a/core/systems/PluginSys.cpp +++ b/core/systems/PluginSys.cpp @@ -1558,6 +1558,15 @@ bool CPluginManager::UnloadPlugin(IPlugin *plugin) return false; } + IPluginContext *pContext = plugin->GetBaseContext(); + if (pContext->IsInExec()) + { + char buffer[255]; + UTIL_Format(buffer, sizeof(buffer), "sm plugins unload %s\n", plugin->GetFilename()); + engine->ServerCommand(buffer); + return false; + } + /* Remove us from the lookup table and linked list */ m_plugins.remove(pPlugin); sm_trie_delete(m_LoadLookup, pPlugin->m_filename); @@ -2682,3 +2691,20 @@ void CPluginManager::UnloadAll() UnloadPlugin((*iter)); } } + +int CPluginManager::GetOrderOfPlugin(IPlugin *pl) +{ + int id = 1; + List::iterator iter; + + for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++, id++) + { + if ((*iter) == pl) + { + return id; + } + } + + return -1; +} + diff --git a/core/systems/PluginSys.h b/core/systems/PluginSys.h index 60b97dc1..cc71c4ea 100644 --- a/core/systems/PluginSys.h +++ b/core/systems/PluginSys.h @@ -399,6 +399,8 @@ public: */ CPlugin *GetPluginByOrder(int num); + int GetOrderOfPlugin(IPlugin *pl); + /** * Internal version of FindPluginByContext() */ diff --git a/core/vm/sp_vm_basecontext.cpp b/core/vm/sp_vm_basecontext.cpp index e47da310..0745015b 100644 --- a/core/vm/sp_vm_basecontext.cpp +++ b/core/vm/sp_vm_basecontext.cpp @@ -1088,3 +1088,8 @@ cell_t *BaseContext::GetNullRef(SP_NULL_TYPE type) return NULL; } #endif + +bool BaseContext::IsInExec() +{ + return m_InExec; +} diff --git a/core/vm/sp_vm_basecontext.h b/core/vm/sp_vm_basecontext.h index 322ca35d..ce399fd6 100644 --- a/core/vm/sp_vm_basecontext.h +++ b/core/vm/sp_vm_basecontext.h @@ -96,6 +96,7 @@ namespace SourcePawn int LookupLine(ucell_t addr, uint32_t *line); public: void SetContext(sp_context_t *_ctx); + bool IsInExec(); private: void SetErrorMessage(const char *msg, va_list ap); void FlushFunctionCache(); diff --git a/public/sourcepawn/sp_vm_api.h b/public/sourcepawn/sp_vm_api.h index 3f54d777..36fd5935 100644 --- a/public/sourcepawn/sp_vm_api.h +++ b/public/sourcepawn/sp_vm_api.h @@ -599,6 +599,13 @@ namespace SourcePawn * @param native Native function to bind. */ virtual int BindNativeToIndex(uint32_t index, SPVM_NATIVE_FUNC native) =0; + + /** + * @brief Returns if there is currently an execution in progress. + * + * @return True if in exec, false otherwise. + */ + virtual bool IsInExec() =0; };