From 6a97d73167adcbc26960c61afc723a424846b06d Mon Sep 17 00:00:00 2001 From: David Anderson Date: Tue, 26 Jul 2005 21:31:21 +0000 Subject: [PATCH] Committed brand new debugger system --- amxmodx/CForward.cpp | 45 +++- amxmodx/CPlugin.cpp | 15 +- amxmodx/CPlugin.h | 4 +- amxmodx/JIT/amxjitsn.o | Bin 26528 -> 26112 bytes amxmodx/JIT/amxjitsn.obj | Bin 27061 -> 26592 bytes amxmodx/amx.h | 16 +- amxmodx/amxmodx.cpp | 2 +- amxmodx/modules.cpp | 374 +++++++++++++++++++++++++-------- amxmodx/msvc/amxmodx_mm.vcproj | 10 +- 9 files changed, 363 insertions(+), 103 deletions(-) diff --git a/amxmodx/CForward.cpp b/amxmodx/CForward.cpp index 6641d4ff..50ba729b 100755 --- a/amxmodx/CForward.cpp +++ b/amxmodx/CForward.cpp @@ -31,6 +31,8 @@ #include "amxmodx.h" +void AMXAPI amxx_InvalidateTrace(AMX *amx); + CForward::CForward(const char *name, ForwardExecType et, int numParams, const ForwardParam *paramTypes) { m_FuncName = name; @@ -70,6 +72,11 @@ cell CForward::execute(cell *params, ForwardPreparedArray *preparedArrays) { if (iter->pPlugin->isExecutable(iter->func)) { + // Get debug info + AMX *amx = (*iter).pPlugin->getAMX(); + AMX_DBGINFO *pInfo = (AMX_DBGINFO *)(amx->userdata[2]); + if (pInfo) + pInfo->error = AMX_ERR_NONE; // handle strings & arrays int i, ax=0; for (i = 0; i < m_NumParams; ++i) @@ -86,7 +93,7 @@ cell CForward::execute(cell *params, ForwardPreparedArray *preparedArrays) else if (m_ParamTypes[i] == FP_ARRAY) { cell *tmp; - amx_Allot(iter->pPlugin->getAMX(), preparedArrays[params[i]].size, + amx_Allot(amx, preparedArrays[params[i]].size, &realParams[i], &tmp); physAddrs[i] = tmp; if (preparedArrays[params[i]].type == Type_Cell) @@ -108,14 +115,25 @@ cell CForward::execute(cell *params, ForwardPreparedArray *preparedArrays) //Push the parameters in reverse order. Weird, unfriendly part of Small 3.0! for (i=m_NumParams-1; i>=0; i--) { - amx_Push(iter->pPlugin->getAMX(), realParams[i]); + amx_Push(amx, realParams[i]); } // exec cell retVal; - int err = amx_Exec(iter->pPlugin->getAMX(), &retVal, iter->func); + int err = amx_Exec(amx, &retVal, iter->func); // log runtime error, if any if (err != AMX_ERR_NONE) - LogError(iter->pPlugin->getAMX(), err, ""); + { + //Did something else set an error? + if (pInfo && pInfo->error != AMX_ERR_NONE) + { + //we don't care, something else logged the error. + } else { + //nothing logged the error so spit it out anyway + LogError(amx, err, ""); + } + } + amxx_InvalidateTrace(amx); + amx->error = AMX_ERR_NONE; // cleanup strings & arrays for (i = 0; i < m_NumParams; ++i) @@ -211,6 +229,10 @@ cell CSPForward::execute(cell *params, ForwardPreparedArray *preparedArrays) if (!pPlugin->isExecutable(m_Func)) return 0; + AMX_DBGINFO *pInfo = (AMX_DBGINFO *)(m_Amx->userdata[2]); + if (pInfo) + pInfo->error = AMX_ERR_NONE; + // handle strings & arrays int i; for (i = 0; i < m_NumParams; ++i) @@ -251,10 +273,19 @@ cell CSPForward::execute(cell *params, ForwardPreparedArray *preparedArrays) // exec cell retVal; int err = amx_Exec(m_Amx, &retVal, m_Func); - - // log runtime error, if any if (err != AMX_ERR_NONE) - LogError(m_Amx, err, ""); + { + //Did something else set an error? + if (pInfo && pInfo->error != AMX_ERR_NONE) + { + //we don't care, something else logged the error. + } else { + //nothing logged the error so spit it out anyway + LogError(m_Amx, err, ""); + } + } + amxx_InvalidateTrace(m_Amx); + m_Amx->error = AMX_ERR_NONE; // cleanup strings & arrays for (i = 0; i < m_NumParams; ++i) diff --git a/amxmodx/CPlugin.cpp b/amxmodx/CPlugin.cpp index b6c10710..37a4693a 100755 --- a/amxmodx/CPlugin.cpp +++ b/amxmodx/CPlugin.cpp @@ -137,7 +137,7 @@ const char* CPluginMngr::CPlugin::getStatus() const { switch(status){ case ps_running: { - if (getAMX()->flags & AMX_FLAG_DEBUG) + if (m_Debug) { return "debug"; } else { @@ -172,8 +172,17 @@ CPluginMngr::CPlugin::CPlugin(int i, const char* p,const char* n, char* e, int d paused_fun = 0; next = 0; id = i; - m_PauseFwd = registerSPForwardByName(&amx, "plugin_pause"); - m_UnpauseFwd = registerSPForwardByName(&amx, "plugin_unpause"); + if (status == ps_running) + { + m_PauseFwd = registerSPForwardByName(&amx, "plugin_pause"); + m_UnpauseFwd = registerSPForwardByName(&amx, "plugin_unpause"); + if (amx.flags & AMX_FLAG_DEBUG) + { + m_Debug = true; + } else { + m_Debug = false; + } + } } CPluginMngr::CPlugin::~CPlugin( ) diff --git a/amxmodx/CPlugin.h b/amxmodx/CPlugin.h index 55efb3e2..6411ebae 100755 --- a/amxmodx/CPlugin.h +++ b/amxmodx/CPlugin.h @@ -42,7 +42,7 @@ enum { ps_paused, ps_running, ps_stopped, - ps_locked + ps_locked, }; class CPluginMngr @@ -72,6 +72,7 @@ public: int id; CPlugin(int i , const char* p,const char* n, char* e, int d); ~CPlugin( ); + bool m_Debug; public: @@ -98,6 +99,7 @@ public: void unpauseFunction( int id ); void setStatus( int a ); const char* getStatus() const; + inline bool isDebug() const { return m_Debug; } }; private: diff --git a/amxmodx/JIT/amxjitsn.o b/amxmodx/JIT/amxjitsn.o index 541585fb27e1596dd605c630ea9d1cad00b07bcf..89ffa4928e0b7f3a224e0a64f03328df0014b067 100755 GIT binary patch literal 26112 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 ,params[3]); diff --git a/amxmodx/modules.cpp b/amxmodx/modules.cpp index 05a16f1c..20997e1f 100755 --- a/amxmodx/modules.cpp +++ b/amxmodx/modules.cpp @@ -38,6 +38,7 @@ #include "osdep.h" // sleep, etc #include "CFile.h" #include "amxxfile.h" +#include "amxdbg.h" CList g_modules; CList g_loadedscripts; @@ -88,10 +89,170 @@ void* alloc_amxmemory(void** p, int size) void free_amxmemory(void **ptr) { - delete[] *ptr; + delete[] (unsigned char *)(*ptr); *ptr = 0; } +void amxx_FreeTrace(AMX_DBGINFO *pInfo) +{ + amx_trace *pTrace = pInfo->pTrace; + amx_trace *pTemp = NULL; + + while (pTrace) + { + pTemp = pTrace->next; + delete pTrace; + pTrace = pTemp; + } + + pInfo->pTrace = NULL; + pInfo->pTraceFrm = NULL; + pInfo->pTraceEnd = NULL; +} + +//returns true if this was the last call +bool amxx_RemTraceCall(AMX_DBGINFO *pInfo) +{ + amx_trace *pTrace = pInfo->pTraceFrm; + + assert(pTrace != NULL); + + pInfo->pTraceFrm = pTrace->prev; + pTrace->used = false; + + if (pInfo->pTraceFrm == NULL) + { + //invalidate the trace + pInfo->frm = 0; + return true; + } + + return false; +} + +void amxx_FreeDebug(AMX *amx) +{ + AMX_DBGINFO *pInfo = (AMX_DBGINFO *)amx->userdata[2]; + if (pInfo) + { + AMX_DBG *pDbg = (AMX_DBG *)pInfo->pDebug; + if (pDbg) + { + dbg_FreeInfo(pDbg); + delete pDbg; + } + if (pInfo->pTrace) + amxx_FreeTrace(pInfo); + delete pInfo; + amx->userdata[2] = NULL; + } +} + +amx_trace *amxx_AddTraceCall(AMX_DBGINFO *pInfo) +{ + amx_trace *pTrace = NULL; + + if (pInfo->pTrace == NULL) + { + pTrace = new amx_trace; + memset(pTrace, 0, sizeof(amx_trace)); + pInfo->pTrace = pTrace; + pInfo->pTraceFrm = pTrace; + pInfo->pTraceEnd = pTrace; + } else if (pInfo->pTraceFrm == NULL) { + pTrace = pInfo->pTrace; + pInfo->pTraceFrm = pTrace; + } else { + if (pInfo->pTraceFrm->next == NULL) + { + //if we are at the end of the list... + assert(pInfo->pTraceFrm == pInfo->pTraceEnd); + pTrace = new amx_trace; + memset(pTrace, 0, sizeof(amx_trace)); + pTrace->prev = pInfo->pTraceEnd; + pInfo->pTraceEnd->next = pTrace; + pInfo->pTraceEnd = pTrace; + pInfo->pTraceFrm = pTrace; + } else { + //we are somewhere else. whatever. + pTrace = pInfo->pTraceFrm->next; + pInfo->pTraceFrm = pTrace; + } + } + + pTrace->used = true; + + return pTrace; +} + +void AMXAPI amxx_InvalidateTrace(AMX *amx) +{ + AMX_DBGINFO *pInfo = (AMX_DBGINFO *)(amx->userdata[2]); + if (!pInfo) + return; + amx_trace *pTrace = pInfo->pTrace; + + while (pTrace && pTrace->used) + { + pTrace->used = false; + pTrace = pTrace->next; + } + + pInfo->pTraceFrm = NULL; + pInfo->frm = 0; +} + +int AMXAPI amxx_DebugHook(AMX *amx) +{ + AMX_DBGINFO *pInfo = (AMX_DBGINFO *)amx->userdata[2]; + + if ( !(amx->flags & AMX_FLAG_DEBUG) || !pInfo ) + return AMX_ERR_DEBUG; + + enum StackState + { + Stack_Same, + Stack_Push, + Stack_Pop, + }; + + StackState state = Stack_Same; + + if (!pInfo->frm) + { + pInfo->frm = amx->frm; + state = Stack_Push; + } else { + //Are we stepping through a different frame? + if (amx->frm < pInfo->frm) + { + pInfo->frm = amx->frm; + state = Stack_Push; + } else if (amx->frm > pInfo->frm) { + pInfo->frm = amx->frm; + state = Stack_Pop; + } + } + + if (state == Stack_Push) + { + amx_trace *pTrace = amxx_AddTraceCall(pInfo); + pTrace->frm = amx->cip; + } else if (state == Stack_Pop) { + if (amxx_RemTraceCall(pInfo)) + { + pInfo->frm = 0; + } + } else if (state == Stack_Same) { + //save the cip + amx_trace *pTrace = pInfo->pTraceFrm; + assert(pTrace != NULL); + pTrace->frm = amx->cip; + } + + return AMX_ERR_NONE; +} + int load_amxscript(AMX *amx, void **program, const char *filename, char error[64], int debug) { *error = 0; @@ -155,12 +316,36 @@ int load_amxscript(AMX *amx, void **program, const char *filename, char error[64 int err; memset(amx, 0, sizeof(*amx)); + bool will_be_debugged = false; + + tagAMX_DBG *pDbg = NULL; if ((int)CVAR_GET_FLOAT("amx_debug") >= 2 || debug) { - if ((amx->flags & AMX_FLAG_DEBUG) != 0) + if ((hdr->file_version < CUR_FILE_VERSION)) { - //:TODO: debug support + sprintf(error, "Plugin needs newer debug version info"); + return (amx->error = AMX_ERR_VERSION); + } else if ((hdr->flags & AMX_FLAG_DEBUG) != 0) { + will_be_debugged = true; + char *addr = (char *)hdr + hdr->size; + pDbg = new tagAMX_DBG; + memset(pDbg, 0, sizeof(AMX_DBG)); + + int err = dbg_LoadInfo(pDbg, addr); + + if (err != AMX_ERR_NONE) + { + dbg_FreeInfo(pDbg); + delete pDbg; + sprintf(error, "Debug loading error %d", err); + return (amx->error = AMX_ERR_INIT); + } + + amx->flags |= AMX_FLAG_DEBUG; + } else { + sprintf(error,"Plugin not compiled with debug option"); + return (amx->error = AMX_ERR_INIT); } } else { #ifdef JIT @@ -171,11 +356,31 @@ int load_amxscript(AMX *amx, void **program, const char *filename, char error[64 if ((err = amx_Init( amx, *program )) != AMX_ERR_NONE) { + if (pDbg) + { + dbg_FreeInfo(pDbg); + delete pDbg; + } sprintf(error,"Load error %d (invalid file format or version)", err); return (amx->error = AMX_ERR_INIT); } - LOG_MESSAGE(PLID, "AMX: %p FLAGS: %d\n", amx, amx->flags); + AMX_DBGINFO *pInfo = new AMX_DBGINFO; + memset(pInfo, 0, sizeof(AMX_DBGINFO)); + amx->userdata[2] = (void *)pInfo; + + pInfo->error = AMX_ERR_NONE; + pInfo->pDebug = (void *)pDbg; + + if (will_be_debugged) + { + amx->flags |= AMX_FLAG_DEBUG; + amx_SetDebugHook(amx, amxx_DebugHook); + } else { + //set this again because amx_Init() erases it! + amx->flags |= AMX_FLAG_JITC; + amx->sysreq_d = NULL; + } #ifdef JIT if (amx->flags & AMX_FLAG_JITC) @@ -355,6 +560,7 @@ int set_amxnatives(AMX* amx,char error[128]) int unload_amxscript(AMX* amx, void** program) { + amxx_FreeDebug(amx); CList::iterator a = g_loadedscripts.find( amx ); if ( a ) a.remove(); char *prg = (char *)*program; @@ -363,7 +569,6 @@ int unload_amxscript(AMX* amx, void** program) return AMX_ERR_NONE; } - AMX* get_amxscript(int id , void** code, const char** filename) { CList::iterator a = g_loadedscripts.begin(); @@ -1180,9 +1385,27 @@ void MNF_Log(const char *fmt, ...) AMXXLOG_Log("%s", msg); } +bool amxx_GetPluginData(AMX *amx, cell addr, long &line, const char *&filename, const char *&function) +{ + AMX_DBGINFO *pInfo = (AMX_DBGINFO *)(amx->userdata[2]); + + if (pInfo && pInfo->pDebug) + { + AMX_DBG *pDbg = (AMX_DBG *)pInfo->pDebug; + dbg_LookupFunction(pDbg, addr, &function); + dbg_LookupLine(pDbg, addr, &line); + dbg_LookupFile(pDbg, addr, &filename); + + return true; + } + + return false; +} + //by BAILOPAN // generic error printing routine -void GenericError(AMX *amx, int err, int line, char buf[], const char *file) +// for pawn 3.0 this is just a wrapper +const char *GenericError(int err) { static const char *amx_errs[] = { @@ -1199,7 +1422,7 @@ void GenericError(AMX *amx, int err, int line, char buf[], const char *file) "native", "divide", "sleep", - NULL, + "invalid access state", NULL, NULL, "out of memory", //16 @@ -1217,31 +1440,10 @@ void GenericError(AMX *amx, int err, int line, char buf[], const char *file) //does this plugin have line ops? const char *geterr = NULL; if (err > 26 || err < 0) - geterr = NULL; + geterr = ""; else geterr = amx_errs[err]; - if (!(amx->flags & AMX_FLAG_DEBUG)) - { - if (geterr == NULL) - { - sprintf(buf, "Run time error %d (plugin \"%s\" - debug not enabled).", err, g_plugins.findPluginFast(amx)->getName()); - } else { - sprintf(buf, "Run time error %d (%s) (plugin \"%s\") - debug not enabled.", err, geterr, g_plugins.findPluginFast(amx)->getName()); - } - } else { - if (geterr == NULL) - { - sprintf(buf, "Run time error %d on line %d (%s \"%s\").", err, line, (file?"file":"plugin"), (file?file:g_plugins.findPluginFast(amx)->getName())); - } else { - if (err == AMX_ERR_NATIVE && amx->userdata[2]) - { - geterr = (char *)(amx->userdata[2]); - sprintf(buf, "Native error in \"%s\" on line %d (%s \"%s\").", geterr, line, (file?"file":"plugin"), (file?file:g_plugins.findPluginFast(amx)->getName())); - } else { - sprintf(buf, "Run time error %d (%s) on line %d (%s \"%s\").", err, geterr, line, (file?"file":"plugin"), (file?file:g_plugins.findPluginFast(amx)->getName())); - } - } - } + return geterr; } //by BAILOPAN @@ -1250,71 +1452,69 @@ void LogError(AMX *amx, int err, const char *fmt, ...) { //does this plugin have debug info? va_list arg; - //AMX_DBG *dbg = (AMX_DBG *)(amx->userdata[0]); + AMX_DBGINFO *pInfo = (AMX_DBGINFO *)(amx->userdata[2]); + CPluginMngr::CPlugin *pPlugin = g_plugins.findPluginFast(amx); static char buf[1024]; static char vbuf[1024]; *buf = 0; *vbuf = 0; - va_start(arg, fmt); - vsprintf(vbuf, fmt, arg); - va_end(arg); -#if 0 - if (!dbg || !(dbg->tail)) + if (fmt[0] == '\0') { - if (dbg && amx->curfile < dbg->numFiles && amx->curfile >= 0) - { - GenericError(amx, err, amx->curline, buf, dbg->files[amx->curfile]); - } else { - GenericError(amx, err, amx->curline, buf, NULL); - } - AMXXLOG_Log("[AMXX] %s", buf); - if (*vbuf) - { - AMXXLOG_Log("%s", vbuf); - } - if (!dbg) - { - AMXXLOG_Log("[AMXX] To enable debug mode, add \" debug\" after the plugin name in plugins.ini (without quotes)."); - } + _snprintf(vbuf, sizeof(vbuf)-1, "Run time error %d (%s)", err, GenericError(err)); } else { - AMX_TRACE *t = dbg->tail; - AMX_DEBUGCALL tracer = (AMX_DEBUGCALL)(amx->userdata[1]); - //actuall - cell line = amx->curline; - cell file = amx->curfile; - int i = 0; - GenericError(amx, err, line, buf, NULL); - AMXXLOG_Log("[AMXX] %s", buf); - if (*vbuf) - { - AMXXLOG_Log("%s", vbuf); - } - AMXXLOG_Log("[AMXX] Debug Trace =>"); - //log the error right away - if (file >= dbg->numFiles || file < 0) - { - AMXXLOG_Log("[AMXX] [%d] Line %d, File \"%s\"", i++, line, g_plugins.findPluginFast(amx)->getName()); - } else { - AMXXLOG_Log("[AMXX] [%d] Line %d, File \"%s\"", i++, line, dbg->files[file]); - } - while (t != NULL) - { - line = t->line; - file = t->file; - if (file >= dbg->numFiles) - { - AMXXLOG_Log("[AMXX] [%d] Line %d, File \"%s\"", i++, line, g_plugins.findPluginFast(amx)->getName()); - } else { - AMXXLOG_Log("[AMXX] [%d] Line %d, File \"%s\"", i++, line, dbg->files[file]); - } - if (tracer) - (tracer)(amx, 1); //pop - t = dbg->tail; - } + va_start(arg, fmt); + vsprintf(vbuf, fmt, arg); + va_end(arg); } -#endif - amx_RaiseError(amx, err); + + bool invalidate = false; + AMXXLOG_Log("[AMXX] %s", vbuf); + if (!pInfo || !(amx->flags & AMX_FLAG_DEBUG) || !pInfo->pDebug) + { + AMXXLOG_Log("[AMXX] Debug is not enabled (plugin \"%s\")", pPlugin->getName()); + invalidate = true; + } else { + long line; + const char *filename = NULL; + const char *function = NULL; + amx_trace *pTrace = pInfo->pTraceFrm; + int i=0, iLine; + cell frame; + + AMXXLOG_Log("[AMXX] Displaying call trace (plugin \"%s\")", pPlugin->getName()); + while (pTrace) + { + frame = pTrace->frm; + + if (amxx_GetPluginData(amx, frame, line, filename, function)) + { + //line seems to be 1 off o_O + iLine = static_cast(line) + 1; + AMXXLOG_Log("[AMXX] [%d] %s::%s (line %d)", + i, + filename?filename:"", + function?function:"", + iLine + ); + } + + pTrace->used = false; + pTrace = pTrace->prev; + i++; + } + //by now we have already invalidated + pInfo->pTraceFrm = NULL; + pInfo->frm = 0; + } + + if (invalidate) + amxx_InvalidateTrace(amx); + + //set these so ForwardMngr knows not to call us again + //This will also halt the script! + amx->error = err; + pInfo->error = err; } void MNF_MergeDefinitionFile(const char *file) diff --git a/amxmodx/msvc/amxmodx_mm.vcproj b/amxmodx/msvc/amxmodx_mm.vcproj index e7a85095..da7b687f 100755 --- a/amxmodx/msvc/amxmodx_mm.vcproj +++ b/amxmodx/msvc/amxmodx_mm.vcproj @@ -442,7 +442,7 @@ InlineFunctionExpansion="1" FavorSizeOrSpeed="1" AdditionalIncludeDirectories="..\..\metamod\metamod,..\..\hlsdk\sourcecode\common,..\..\hlsdk\sourcecode\engine,..\..\hlsdk\sourcecode\dlls,..\..\hlsdk\sourcecode\pm_shared,..\extra\include" - PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;amxmodx_EXPORTS;MEMORY_TEST;JIT" + PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;amxmodx_EXPORTS;MEMORY_TEST;JIT;ASM32;PAWN_CELL_SIZE=32" StringPooling="TRUE" RuntimeLibrary="4" EnableFunctionLevelLinking="TRUE" @@ -460,7 +460,7 @@ + + @@ -721,6 +724,9 @@ + +