- 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:
parent
00ec666f18
commit
af68caaf85
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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";
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -399,6 +399,8 @@ public:
|
||||
*/
|
||||
CPlugin *GetPluginByOrder(int num);
|
||||
|
||||
int GetOrderOfPlugin(IPlugin *pl);
|
||||
|
||||
/**
|
||||
* Internal version of FindPluginByContext()
|
||||
*/
|
||||
|
@ -1088,3 +1088,8 @@ cell_t *BaseContext::GetNullRef(SP_NULL_TYPE type)
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool BaseContext::IsInExec()
|
||||
{
|
||||
return m_InExec;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user