- 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
This commit is contained in:
David Anderson 2007-10-22 08:53:15 +00:00
parent 00ec666f18
commit af68caaf85
8 changed files with 149 additions and 28 deletions

View File

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

View File

@ -33,6 +33,7 @@
#include "ShareSys.h"
#include "PluginSys.h"
#include "ExtensionSys.h"
#include "Logger.h"
#include <assert.h>
#include <string.h>
@ -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";

View File

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

View File

@ -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<CPlugin *>::iterator iter;
for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++, id++)
{
if ((*iter) == pl)
{
return id;
}
}
return -1;
}

View File

@ -399,6 +399,8 @@ public:
*/
CPlugin *GetPluginByOrder(int num);
int GetOrderOfPlugin(IPlugin *pl);
/**
* Internal version of FindPluginByContext()
*/

View File

@ -1088,3 +1088,8 @@ cell_t *BaseContext::GetNullRef(SP_NULL_TYPE type)
return NULL;
}
#endif
bool BaseContext::IsInExec()
{
return m_InExec;
}

View File

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

View File

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