- 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;
|
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];
|
char path[PLATFORM_MAX_PATH];
|
||||||
|
|
||||||
g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "sourcemod_fatal.log");
|
g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "sourcemod_fatal.log");
|
||||||
|
|
||||||
FILE *fp = fopen(path, "at");
|
FILE *fp = fopen(path, "at");
|
||||||
if (fp)
|
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);
|
fclose(fp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include "ShareSys.h"
|
#include "ShareSys.h"
|
||||||
#include "PluginSys.h"
|
#include "PluginSys.h"
|
||||||
#include "ExtensionSys.h"
|
#include "ExtensionSys.h"
|
||||||
|
#include "Logger.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@ -260,6 +261,24 @@ bool HandleSystem::FindHandleType(const char *name, HandleType_t *type)
|
|||||||
return true;
|
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,
|
HandleError HandleSystem::MakePrimHandle(HandleType_t type,
|
||||||
QHandle **in_pHandle,
|
QHandle **in_pHandle,
|
||||||
unsigned int *in_index,
|
unsigned int *in_index,
|
||||||
@ -267,7 +286,9 @@ HandleError HandleSystem::MakePrimHandle(HandleType_t type,
|
|||||||
IdentityToken_t *owner,
|
IdentityToken_t *owner,
|
||||||
bool identity)
|
bool identity)
|
||||||
{
|
{
|
||||||
|
HandleError err;
|
||||||
unsigned int owner_index = 0;
|
unsigned int owner_index = 0;
|
||||||
|
bool retried_alloc = false;
|
||||||
|
|
||||||
if (owner && (IdentityHandle(owner, &owner_index) != HandleError_None))
|
if (owner && (IdentityHandle(owner, &owner_index) != HandleError_None))
|
||||||
{
|
{
|
||||||
@ -275,15 +296,13 @@ HandleError HandleSystem::MakePrimHandle(HandleType_t type,
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsigned int handle;
|
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];
|
QHandle *pHandle = &m_Handles[handle];
|
||||||
@ -329,7 +348,9 @@ HandleError HandleSystem::MakePrimHandle(HandleType_t type,
|
|||||||
pIdentity->ch_prev = handle;
|
pIdentity->ch_prev = handle;
|
||||||
pIdentity->ch_next = handle;
|
pIdentity->ch_next = handle;
|
||||||
pHandle->ch_prev = 0;
|
pHandle->ch_prev = 0;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
/* Link previous node to us (forward) */
|
/* Link previous node to us (forward) */
|
||||||
m_Handles[pIdentity->ch_next].ch_next = handle;
|
m_Handles[pIdentity->ch_next].ch_next = handle;
|
||||||
/* Link us to previous node (backwards) */
|
/* Link us to previous node (backwards) */
|
||||||
@ -338,7 +359,9 @@ HandleError HandleSystem::MakePrimHandle(HandleType_t type,
|
|||||||
pIdentity->ch_next = handle;
|
pIdentity->ch_next = handle;
|
||||||
}
|
}
|
||||||
pIdentity->refcount++;
|
pIdentity->refcount++;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
pHandle->ch_prev = 0;
|
pHandle->ch_prev = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -918,11 +941,65 @@ bool HandleSystem::InitAccessDefaults(TypeAccess *pTypeAccess, HandleAccess *pHa
|
|||||||
return true;
|
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)
|
void HandleSystem::Dump(FILE *fp)
|
||||||
{
|
{
|
||||||
fprintf(fp, "%-10.10s\t%-20.20s\t%-20.20s\n", "Handle", "Owner", "Type");
|
fprintf(fp, "%-10.10s\t%-20.20s\t%-20.20s\n", "Handle", "Owner", "Type");
|
||||||
fprintf(fp, "---------------------------------------------\n");
|
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)
|
if (m_Handles[i].set != HandleSet_Used)
|
||||||
{
|
{
|
||||||
@ -938,14 +1015,20 @@ void HandleSystem::Dump(FILE *fp)
|
|||||||
if (pOwner == g_pCoreIdent)
|
if (pOwner == g_pCoreIdent)
|
||||||
{
|
{
|
||||||
owner = "CORE";
|
owner = "CORE";
|
||||||
} else if (pOwner == g_PluginSys.GetIdentity()) {
|
}
|
||||||
|
else if (pOwner == g_PluginSys.GetIdentity())
|
||||||
|
{
|
||||||
owner = "PLUGINSYS";
|
owner = "PLUGINSYS";
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
CExtension *ext = g_Extensions.GetExtensionFromIdent(pOwner);
|
CExtension *ext = g_Extensions.GetExtensionFromIdent(pOwner);
|
||||||
if (ext)
|
if (ext)
|
||||||
{
|
{
|
||||||
owner = ext->GetFilename();
|
owner = ext->GetFilename();
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
CPlugin *pPlugin = g_PluginSys.GetPluginFromIdentity(pOwner);
|
CPlugin *pPlugin = g_PluginSys.GetPluginFromIdentity(pOwner);
|
||||||
if (pPlugin)
|
if (pPlugin)
|
||||||
{
|
{
|
||||||
@ -953,7 +1036,9 @@ void HandleSystem::Dump(FILE *fp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
owner = "NONE";
|
owner = "NONE";
|
||||||
}
|
}
|
||||||
const char *type = "ANON";
|
const char *type = "ANON";
|
||||||
|
@ -201,6 +201,9 @@ protected:
|
|||||||
void UnlinkHandleFromOwner(QHandle *pHandle, unsigned int index);
|
void UnlinkHandleFromOwner(QHandle *pHandle, unsigned int index);
|
||||||
HandleError CloneHandle(QHandle *pHandle, unsigned int index, Handle_t *newhandle, IdentityToken_t *newOwner);
|
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);
|
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:
|
private:
|
||||||
QHandle *m_Handles;
|
QHandle *m_Handles;
|
||||||
QHandleType *m_Types;
|
QHandleType *m_Types;
|
||||||
|
@ -1558,6 +1558,15 @@ bool CPluginManager::UnloadPlugin(IPlugin *plugin)
|
|||||||
return false;
|
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 */
|
/* Remove us from the lookup table and linked list */
|
||||||
m_plugins.remove(pPlugin);
|
m_plugins.remove(pPlugin);
|
||||||
sm_trie_delete(m_LoadLookup, pPlugin->m_filename);
|
sm_trie_delete(m_LoadLookup, pPlugin->m_filename);
|
||||||
@ -2682,3 +2691,20 @@ void CPluginManager::UnloadAll()
|
|||||||
UnloadPlugin((*iter));
|
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);
|
CPlugin *GetPluginByOrder(int num);
|
||||||
|
|
||||||
|
int GetOrderOfPlugin(IPlugin *pl);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal version of FindPluginByContext()
|
* Internal version of FindPluginByContext()
|
||||||
*/
|
*/
|
||||||
|
@ -1088,3 +1088,8 @@ cell_t *BaseContext::GetNullRef(SP_NULL_TYPE type)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool BaseContext::IsInExec()
|
||||||
|
{
|
||||||
|
return m_InExec;
|
||||||
|
}
|
||||||
|
@ -96,6 +96,7 @@ namespace SourcePawn
|
|||||||
int LookupLine(ucell_t addr, uint32_t *line);
|
int LookupLine(ucell_t addr, uint32_t *line);
|
||||||
public:
|
public:
|
||||||
void SetContext(sp_context_t *_ctx);
|
void SetContext(sp_context_t *_ctx);
|
||||||
|
bool IsInExec();
|
||||||
private:
|
private:
|
||||||
void SetErrorMessage(const char *msg, va_list ap);
|
void SetErrorMessage(const char *msg, va_list ap);
|
||||||
void FlushFunctionCache();
|
void FlushFunctionCache();
|
||||||
|
@ -599,6 +599,13 @@ namespace SourcePawn
|
|||||||
* @param native Native function to bind.
|
* @param native Native function to bind.
|
||||||
*/
|
*/
|
||||||
virtual int BindNativeToIndex(uint32_t index, SPVM_NATIVE_FUNC native) =0;
|
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