Added new debug feature: Performance Monitor. (#1019)

* Performance Monitor.

Search big performance issues for any plugin that in debug mode. No server slow down.
Output is plugin and function name, and server execution lag in ms. Output target: current amxlog file.

* Adding cvar to enable perflog. Force for all plugin if amx_debug is 2.

* Update cvar name

* Use another clock from chrono

* Fix debug check

* Skip internal amxx exec

* Move includes

* Fix debug check in amx_ExecPerf

* Tried to compile without min/max undefined
This commit is contained in:
Unreal Karaulov 2021-12-06 05:38:49 +03:00 committed by GitHub
parent 6e8554d1d2
commit 8ee3da24cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 88 additions and 12 deletions

View File

@ -122,8 +122,8 @@ cell CForward::execute(cell *params, ForwardPreparedArray *preparedArrays)
#if defined BINLOG_ENABLED #if defined BINLOG_ENABLED
g_BinLog.WriteOp(BinLog_CallPubFunc, iter->pPlugin->getId(), iter->func); g_BinLog.WriteOp(BinLog_CallPubFunc, iter->pPlugin->getId(), iter->func);
#endif #endif
int err = amx_Exec(amx, &retVal, iter->func);
int err = amx_ExecPerf(amx, &retVal, iter->func);
// log runtime error, if any // log runtime error, if any
if (err != AMX_ERR_NONE) if (err != AMX_ERR_NONE)
{ {
@ -327,8 +327,7 @@ cell CSPForward::execute(cell *params, ForwardPreparedArray *preparedArrays)
#if defined BINLOG_ENABLED #if defined BINLOG_ENABLED
g_BinLog.WriteOp(BinLog_CallPubFunc, pPlugin->getId(), m_Func); g_BinLog.WriteOp(BinLog_CallPubFunc, pPlugin->getId(), m_Func);
#endif #endif
int err = amx_Exec(m_Amx, &retVal, m_Func); int err = amx_ExecPerf(m_Amx, &retVal, m_Func);
if (err != AMX_ERR_NONE) if (err != AMX_ERR_NONE)
{ {
//Did something else set an error? //Did something else set an error?

View File

@ -66,6 +66,9 @@
#include <windows.h> #include <windows.h>
#endif #endif
#include <chrono>
#include <amxmodx.h>
#include <CPlugin.h>
/* When one or more of the AMX_funcname macris are defined, we want /* When one or more of the AMX_funcname macris are defined, we want
* to compile only those functions. However, when none of these macros * to compile only those functions. However, when none of these macros
@ -4173,3 +4176,37 @@ int AMXAPI amx_GetStringOld(char *dest,const cell *source,int use_wchar)
dest[len]='\0'; /* store terminator */ dest[len]='\0'; /* store terminator */
return AMX_ERR_NONE; return AMX_ERR_NONE;
} }
int AMXAPI amx_ExecPerf(AMX* amx, cell* retval, int index)
{
CPluginMngr::CPlugin* perf_Plug = g_plugins.findPluginFast(amx);
if (amxmodx_perflog->value > 0.0f && perf_Plug && (perf_Plug->isDebug() || (int)amxmodx_debug->value == 2))
{
char perf_funcname[sNAMEMAX + 1];
perf_funcname[0] = '\0';
amx_GetPublic(perf_Plug->getAMX(), index, perf_funcname);
if (perf_funcname[0] == '\0')
sprintf(perf_funcname, "Unknown_ID%d", index);
const char* perf_plugname = perf_Plug->getName();
if (!perf_plugname || perf_plugname[0] == '\0')
perf_plugname = "Unknown_plugin";
using std::chrono::steady_clock;
using std::chrono::duration_cast;
using std::chrono::duration;
using std::chrono::microseconds;
auto t1 = steady_clock::now();
int err = amx_Exec(amx, retval, index);
auto ms_int = duration_cast<microseconds>(steady_clock::now() - t1);
auto ms_float = (float)(ms_int.count() / 1000.0f);
if (ms_float >= amxmodx_perflog->value)
{
AMXXLOG_Log("[%s] performance issue. Function %s executed more than %.*fms.", perf_plugname, perf_funcname, 1, ms_float);
}
return err;
}
return amx_Exec(amx, retval, index);
}

View File

@ -382,6 +382,7 @@ int AMXAPI amx_Callback(AMX *amx, cell index, cell *result, cell *params);
int AMXAPI amx_CheckNatives(AMX *amx, AMX_NATIVE_FILTER nf); int AMXAPI amx_CheckNatives(AMX *amx, AMX_NATIVE_FILTER nf);
int AMXAPI amx_Cleanup(AMX *amx); int AMXAPI amx_Cleanup(AMX *amx);
int AMXAPI amx_Clone(AMX *amxClone, AMX *amxSource, void *data); int AMXAPI amx_Clone(AMX *amxClone, AMX *amxSource, void *data);
int AMXAPI amx_ExecPerf(AMX* amx, cell* retval, int index);
int AMXAPI amx_Exec(AMX *amx, cell *retval, int index); int AMXAPI amx_Exec(AMX *amx, cell *retval, int index);
int AMXAPI amx_FindNative(AMX *amx, const char *name, int *index); int AMXAPI amx_FindNative(AMX *amx, const char *name, int *index);
int AMXAPI amx_FindPublic(AMX *amx, const char *funcname, int *index); int AMXAPI amx_FindPublic(AMX *amx, const char *funcname, int *index);

View File

@ -3709,7 +3709,7 @@ static cell AMX_NATIVE_CALL callfunc_end(AMX *amx, cell *params)
amx_Push(pAmx, gparams[i]); amx_Push(pAmx, gparams[i]);
} }
err = amx_Exec(pAmx, &retVal, func); err = amx_ExecPerf(pAmx, &retVal, func);
if (err != AMX_ERR_NONE) if (err != AMX_ERR_NONE)
{ {

View File

@ -196,7 +196,9 @@ extern bool g_official_mod;
extern bool g_dontprecache; extern bool g_dontprecache;
extern int g_srvindex; extern int g_srvindex;
extern cvar_t* amxmodx_version; extern cvar_t* amxmodx_version;
extern cvar_t* amxmodx_debug;
extern cvar_t* amxmodx_language; extern cvar_t* amxmodx_language;
extern cvar_t* amxmodx_perflog;
extern cvar_t* hostname; extern cvar_t* hostname;
extern cvar_t* mp_timelimit; extern cvar_t* mp_timelimit;
extern fakecmd_t g_fakecmd; extern fakecmd_t g_fakecmd;

View File

@ -728,7 +728,7 @@ int Handler::HandleModule(const char *module, bool isClass)
m_pAmx->flags |= AMX_FLAG_PRENIT; m_pAmx->flags |= AMX_FLAG_PRENIT;
amx_Push(m_pAmx, isClass ? 1 : 0); amx_Push(m_pAmx, isClass ? 1 : 0);
amx_PushString(m_pAmx, &hea_addr, &phys_addr, module, 0, 0); amx_PushString(m_pAmx, &hea_addr, &phys_addr, module, 0, 0);
int err = amx_Exec(m_pAmx, &retval, m_iModFunc); int err = amx_ExecPerf(m_pAmx, &retval, m_iModFunc);
amx_Release(m_pAmx, hea_addr); amx_Release(m_pAmx, hea_addr);
m_pAmx->flags &= ~AMX_FLAG_PRENIT; m_pAmx->flags &= ~AMX_FLAG_PRENIT;
@ -768,7 +768,7 @@ int Handler::HandleNative(const char *native, int index, int trap)
amx_Push(m_pAmx, trap); amx_Push(m_pAmx, trap);
amx_Push(m_pAmx, index); amx_Push(m_pAmx, index);
amx_PushString(m_pAmx, &hea_addr, &phys_addr, native, 0, 0); amx_PushString(m_pAmx, &hea_addr, &phys_addr, native, 0, 0);
int err = amx_Exec(m_pAmx, &retval, m_iNatFunc); int err = amx_ExecPerf(m_pAmx, &retval, m_iNatFunc);
if (err != AMX_ERR_NONE) if (err != AMX_ERR_NONE)
{ {
//LogError() took care of something for us. //LogError() took care of something for us.
@ -841,7 +841,7 @@ int Handler::HandleError(const char *msg)
amx_PushString(m_pAmx, &hea_addr, &phys_addr, msg, 0, 0); amx_PushString(m_pAmx, &hea_addr, &phys_addr, msg, 0, 0);
amx_Push(m_pAmx, pDebugger ? 1 : 0); amx_Push(m_pAmx, pDebugger ? 1 : 0);
amx_Push(m_pAmx, error); amx_Push(m_pAmx, error);
int err = amx_Exec(m_pAmx, &result, m_iErrFunc); int err = amx_ExecPerf(m_pAmx, &result, m_iErrFunc);
if (err != AMX_ERR_NONE) if (err != AMX_ERR_NONE)
{ {
//handle this manually. //handle this manually.

View File

@ -135,9 +135,14 @@ cvar_t init_amxmodx_debug = {"amx_debug", "1", FCVAR_SPONLY};
cvar_t init_amxmodx_mldebug = {"amx_mldebug", "", FCVAR_SPONLY}; cvar_t init_amxmodx_mldebug = {"amx_mldebug", "", FCVAR_SPONLY};
cvar_t init_amxmodx_language = {"amx_language", "en", FCVAR_SERVER}; cvar_t init_amxmodx_language = {"amx_language", "en", FCVAR_SERVER};
cvar_t init_amxmodx_cl_langs = {"amx_client_languages", "1", FCVAR_SERVER}; cvar_t init_amxmodx_cl_langs = {"amx_client_languages", "1", FCVAR_SERVER};
cvar_t init_amxmodx_perflog = { "amx_perflog_ms", "1.0", FCVAR_SPONLY };
cvar_t* amxmodx_version = NULL; cvar_t* amxmodx_version = NULL;
cvar_t* amxmodx_modules = NULL; cvar_t* amxmodx_modules = NULL;
cvar_t* amxmodx_debug = NULL;
cvar_t* amxmodx_language = NULL; cvar_t* amxmodx_language = NULL;
cvar_t* amxmodx_perflog = NULL;
cvar_t* hostname = NULL; cvar_t* hostname = NULL;
cvar_t* mp_timelimit = NULL; cvar_t* mp_timelimit = NULL;
@ -1627,9 +1632,12 @@ C_DLLEXPORT int Meta_Attach(PLUG_LOADTIME now, META_FUNCTIONS *pFunctionTable, m
CVAR_REGISTER(&init_amxmodx_mldebug); CVAR_REGISTER(&init_amxmodx_mldebug);
CVAR_REGISTER(&init_amxmodx_language); CVAR_REGISTER(&init_amxmodx_language);
CVAR_REGISTER(&init_amxmodx_cl_langs); CVAR_REGISTER(&init_amxmodx_cl_langs);
CVAR_REGISTER(&init_amxmodx_perflog);
amxmodx_version = CVAR_GET_POINTER(init_amxmodx_version.name); amxmodx_version = CVAR_GET_POINTER(init_amxmodx_version.name);
amxmodx_debug = CVAR_GET_POINTER(init_amxmodx_debug.name);
amxmodx_language = CVAR_GET_POINTER(init_amxmodx_language.name); amxmodx_language = CVAR_GET_POINTER(init_amxmodx_language.name);
amxmodx_perflog = CVAR_GET_POINTER(init_amxmodx_perflog.name);
REG_SVR_COMMAND("amxx", amx_command); REG_SVR_COMMAND("amxx", amx_command);

View File

@ -187,7 +187,7 @@ int load_amxscript_internal(AMX *amx, void **program, const char *filename, char
bool will_be_debugged = false; bool will_be_debugged = false;
tagAMX_DBG *pDbg = NULL; tagAMX_DBG *pDbg = NULL;
if ((int)CVAR_GET_FLOAT("amx_debug") >= 2 || debug) if ((int)amxmodx_debug->value == 2 || debug)
{ {
if ((hdr->file_version < CUR_FILE_VERSION)) if ((hdr->file_version < CUR_FILE_VERSION))
{ {
@ -544,7 +544,7 @@ int set_amxnatives(AMX* amx, char error[128])
if (amx_FindPublic(amx, "plugin_natives", &idx) == AMX_ERR_NONE) if (amx_FindPublic(amx, "plugin_natives", &idx) == AMX_ERR_NONE)
{ {
if ((err = amx_Exec(amx, &retval, idx)) != AMX_ERR_NONE) if ((err = amx_ExecPerf(amx, &retval, idx)) != AMX_ERR_NONE)
{ {
Debugger::GenericMessage(amx, err); Debugger::GenericMessage(amx, err);
AMXXLOG_Log("An error occurred in plugin_natives. This is dangerous!"); AMXXLOG_Log("An error occurred in plugin_natives. This is dangerous!");

View File

@ -112,8 +112,7 @@ int amxx_DynaCallback(int idx, AMX *amx, cell *params)
pDebugger->BeginExec(); pDebugger->BeginExec();
} }
err=amx_Exec(pNative->amx, &ret, pNative->func); err = amx_ExecPerf(pNative->amx, &ret, pNative->func);
if (err != AMX_ERR_NONE) if (err != AMX_ERR_NONE)
{ {
if (pDebugger && pDebugger->ErrorExists()) if (pDebugger && pDebugger->ErrorExists())

View File

@ -181,3 +181,13 @@ amx_debug 1
// //
// Default value: "" // Default value: ""
amx_mldebug "" amx_mldebug ""
// Performance monitor
//
// If function executed more than amx_perflog_ms milliseconds
// print plugin and function name to current amxmodx log file.
//
// Time in milliseconds
// Default value: 1.0
//
amx_perflog_ms 1.0

View File

@ -221,3 +221,12 @@ amx_statsx_freeze -2.0
// Default value: 0 // Default value: 0
amx_restrmapsettings 0 amx_restrmapsettings 0
// Performance monitor
//
// If function executed more than amx_perflog_ms milliseconds
// print plugin and function name to current amxmodx log file.
//
// Time in milliseconds
// Default value: 1.0
//
amx_perflog_ms 1.0

View File

@ -218,3 +218,14 @@ amx_idle_ignore_immunity 1
// //
// Default value: 4 // Default value: 4
amx_unstuck_frequency 4 amx_unstuck_frequency 4
// Performance monitor
//
// If function executed more than amx_perflog_ms milliseconds
// print plugin and function name to current amxmodx log file.
//
// Time in milliseconds
// Default value: 1.0
//
amx_perflog_ms 1.0