amxmodx/amxmodx/debugger.cpp
Karol Szuster 123246cc84 Port code to AMTL (#427)
* Remove CQueue

* Remove duplicated files

* Move sh_tinyhash to public/

* Remove sm_queue

Make NVault use of ke::Deque

* Remove sh_string (unused)

* Remove CList dependency from CTask

* Remove CList dependency from CEvent

* Remove CList dependency from modules' functions

* Remove CList dependency ForceObject

* Remove CList dependency Player Auth

* Remove left CList dependencies

* Fix msvc project files

* Update AMTL

* Use InlineList for CScript

* Use InlineList for CModule

* Use Vector for Player Auth
2017-04-14 19:36:59 +02:00

1026 lines
20 KiB
C++
Executable File

// vim: set ts=4 sw=4 tw=99 noet:
//
// AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO").
// Copyright (C) The AMX Mod X Development Team.
//
// This software is licensed under the GNU General Public License, version 3 or higher.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://alliedmods.net/amxmodx-license
#include "amxmodx.h"
#include "debugger.h"
#include "binlog.h"
/**
* AMX Mod X Debugging Engine
* Written by David "BAILOPAN" Anderson
*/
enum AmxOpcodes
{
OP_NONE, /* invalid opcode */
OP_LOAD_PRI,
OP_LOAD_ALT,
OP_LOAD_S_PRI,
OP_LOAD_S_ALT,
OP_LREF_PRI,
OP_LREF_ALT,
OP_LREF_S_PRI,
OP_LREF_S_ALT,
OP_LOAD_I,
OP_LODB_I,
OP_CONST_PRI,
OP_CONST_ALT,
OP_ADDR_PRI,
OP_ADDR_ALT,
OP_STOR_PRI,
OP_STOR_ALT,
OP_STOR_S_PRI,
OP_STOR_S_ALT,
OP_SREF_PRI,
OP_SREF_ALT,
OP_SREF_S_PRI,
OP_SREF_S_ALT,
OP_STOR_I,
OP_STRB_I,
OP_LIDX,
OP_LIDX_B,
OP_IDXADDR,
OP_IDXADDR_B,
OP_ALIGN_PRI,
OP_ALIGN_ALT,
OP_LCTRL,
OP_SCTRL,
OP_MOVE_PRI,
OP_MOVE_ALT,
OP_XCHG,
OP_PUSH_PRI,
OP_PUSH_ALT,
OP_PUSH_R,
OP_PUSH_C,
OP_PUSH,
OP_PUSH_S,
OP_POP_PRI,
OP_POP_ALT,
OP_STACK,
OP_HEAP,
OP_PROC,
OP_RET,
OP_RETN,
OP_CALL,
OP_CALL_PRI,
OP_JUMP,
OP_JREL,
OP_JZER,
OP_JNZ,
OP_JEQ,
OP_JNEQ,
OP_JLESS,
OP_JLEQ,
OP_JGRTR,
OP_JGEQ,
OP_JSLESS,
OP_JSLEQ,
OP_JSGRTR,
OP_JSGEQ,
OP_SHL,
OP_SHR,
OP_SSHR,
OP_SHL_C_PRI,
OP_SHL_C_ALT,
OP_SHR_C_PRI,
OP_SHR_C_ALT,
OP_SMUL,
OP_SDIV,
OP_SDIV_ALT,
OP_UMUL,
OP_UDIV,
OP_UDIV_ALT,
OP_ADD,
OP_SUB,
OP_SUB_ALT,
OP_AND,
OP_OR,
OP_XOR,
OP_NOT,
OP_NEG,
OP_INVERT,
OP_ADD_C,
OP_SMUL_C,
OP_ZERO_PRI,
OP_ZERO_ALT,
OP_ZERO,
OP_ZERO_S,
OP_SIGN_PRI,
OP_SIGN_ALT,
OP_EQ,
OP_NEQ,
OP_LESS,
OP_LEQ,
OP_GRTR,
OP_GEQ,
OP_SLESS,
OP_SLEQ,
OP_SGRTR,
OP_SGEQ,
OP_EQ_C_PRI,
OP_EQ_C_ALT,
OP_INC_PRI,
OP_INC_ALT,
OP_INC,
OP_INC_S,
OP_INC_I,
OP_DEC_PRI,
OP_DEC_ALT,
OP_DEC,
OP_DEC_S,
OP_DEC_I,
OP_MOVS,
OP_CMPS,
OP_FILL,
OP_HALT,
OP_BOUNDS,
OP_SYSREQ_PRI,
OP_SYSREQ_C,
OP_FILE, /* obsolete */
OP_LINE, /* obsolete */
OP_SYMBOL, /* obsolete */
OP_SRANGE, /* obsolete */
OP_JUMP_PRI,
OP_SWITCH,
OP_CASETBL,
OP_SWAP_PRI,
OP_SWAP_ALT,
OP_PUSHADDR,
OP_NOP,
OP_SYSREQ_D,
OP_SYMTAG, /* obsolete */
OP_BREAK,
/* ----- */
OP_NUM_OPCODES
} OPCODE;
const char *GenericError(int err);
Debugger::Tracer::~Tracer()
{
Clear();
}
void Debugger::Tracer::StepI(cell frm, cell cip)
{
if (m_pEnd == NULL)
{
assert(m_Reset);
if (m_pStart == NULL)
m_pStart = new trace_info();
m_pEnd = m_pStart;
m_Reset = true;
m_pEnd->cip = cip;
m_pEnd->frm = frm;
m_pEnd->used = true;
} else {
if (m_pEnd->frm > frm)
{
//the last frame has moved down the stack.
//push a new call onto our list
if (m_pEnd->next)
{
m_pEnd = m_pEnd->next;
m_pEnd->used = true;
} else {
trace_info *pInfo = new trace_info();
m_pEnd->next = pInfo;
pInfo->prev = m_pEnd;
pInfo->used = true;
m_pEnd = pInfo;
}
//if we're pushing a new call, save the initial frame
m_pEnd->frm = frm;
} else if (m_pEnd->frm < frm) {
//the last frame has moved up the stack.
//pop a call from our list
m_pEnd->used = false;
m_pEnd = m_pEnd->prev;
}
//no matter where we are, save the current cip
m_pEnd->cip = cip;
}
}
void Debugger::Tracer::Clear()
{
trace_info *pInfo, *pNext;
pInfo = m_pStart;
while (pInfo)
{
pNext = pInfo->next;
delete pInfo;
pInfo = pNext;
}
m_pStart = NULL;
m_pEnd = NULL;
m_Error = AMX_ERR_NONE;
m_Reset = true;
}
void Debugger::Tracer::Reset()
{
trace_info *pInfo = m_pStart;
while (pInfo && pInfo->used)
{
pInfo->used = false;
pInfo = pInfo->next;
}
m_pEnd = NULL;
m_Error = AMX_ERR_NONE;
m_Reset = true;
}
trace_info_t *Debugger::Tracer::GetStart() const
{
return m_pStart;
}
trace_info_t *Debugger::Tracer::GetEnd() const
{
return m_pEnd;
}
void Debugger::BeginExec()
{
m_Top++;
assert(m_Top >= 0);
if (m_Top >= (int)m_pCalls.length())
{
Tracer *pTracer = new Tracer();
m_pCalls.append(pTracer);
assert(m_Top == static_cast<int>(m_pCalls.length() - 1));
}
m_pCalls[m_Top]->Reset();
}
void Debugger::EndExec()
{
assert(m_Top >= 0 && m_Top < (int)m_pCalls.length());
m_pCalls[m_Top]->Reset();
m_Top--;
}
void Debugger::StepI()
{
assert(m_Top >= 0 && m_Top < (int)m_pCalls.length());
#if defined BINLOG_ENABLED
if (g_binlog_level & 32)
{
CPluginMngr::CPlugin *pl = g_plugins.findPluginFast(m_pAmx);
if (pl)
{
long line;
dbg_LookupLine(m_pAmxDbg, m_pAmx->cip, &line);
g_BinLog.WriteOp(BinLog_SetLine, pl->getId(), (int)(line + 1));
}
}
#endif
m_pCalls[m_Top]->StepI(m_pAmx->frm, m_pAmx->cip);
}
void Debugger::Reset()
{
//no call state
m_Top = -1;
}
int Debugger::GetTracedError()
{
assert(m_Top >= 0 && m_Top < (int)m_pCalls.length());
return m_pCalls[m_Top]->m_Error;
}
void Debugger::SetTracedError(int error)
{
assert(m_Top >= 0 && m_Top < (int)m_pCalls.length());
m_pCalls[m_Top]->m_Error = error;
}
trace_info_t *Debugger::GetTraceStart() const
{
assert(m_Top >= 0 && m_Top < (int)m_pCalls.length());
return m_pCalls[m_Top]->GetEnd();
}
bool Debugger::GetTraceInfo(trace_info_t *pTraceInfo, long &line, const char *&function, const char *&file)
{
cell addr = pTraceInfo->cip;
dbg_LookupFunction(m_pAmxDbg, addr, &function);
dbg_LookupLine(m_pAmxDbg, addr, &line);
dbg_LookupFile(m_pAmxDbg, addr, &file);
return true;
}
trace_info_t *Debugger::GetNextTrace(trace_info_t *pTraceInfo)
{
if (!pTraceInfo->prev || !pTraceInfo->prev->used)
return NULL;
return pTraceInfo->prev;
}
bool Debugger::ErrorExists()
{
assert(m_Top >= 0 && m_Top < (int)m_pCalls.length());
return (m_pCalls[m_Top]->m_Error != AMX_ERR_NONE);
}
int Debugger::FormatError(char *buffer, size_t maxLength)
{
if (!ErrorExists())
return -1;
assert(m_Top >= 0 && m_Top < (int)m_pCalls.length());
Tracer *pTracer = m_pCalls[m_Top];
int error = pTracer->m_Error;
const char *gen_err = GenericError(error);
int size = 0;
//trace_info_t *pTrace = pTracer->GetEnd();
//cell cip = _CipAsVa(m_pAmx->cip);
//cell *p_cip = NULL;
//int amx_err = AMX_ERR_NONE;
size += ke::SafeSprintf(buffer, maxLength, "Run time error %d: %s ", error, gen_err);
buffer += size;
maxLength -= size;
if (error == AMX_ERR_NATIVE || error == AMX_ERR_INVNATIVE)
{
char native_name[sNAMEMAX+1];
int num = 0;
/*//go two instructions back
cip -= (sizeof(cell) * 2);
int instr = _GetOpcodeFromCip(cip, p_cip);
if (instr == OP_SYSREQ_C)
{
num = (int)*p_cip;
}*/
//New code only requires this...
num = (int)(_INT_PTR)m_pAmx->usertags[UT_NATIVE];
/*amx_err = */amx_GetNative(m_pAmx, num, native_name);
/*if (num)
amx_err = amx_GetNative(m_pAmx, (int)*p_cip, native_name);
else
amx_err = AMX_ERR_NOTFOUND;*/
//if (!amx_err)
size += ke::SafeSprintf(buffer, maxLength, "(native \"%s\")", native_name);
}
return size;
}
cell Debugger::_CipAsVa(cell cip)
{
AMX_HEADER *hdr = (AMX_HEADER*)(m_pAmx->base);
unsigned char *code = m_pAmx->base + (int)hdr->cod;
if (cip >= (cell)code && cip < (cell)(m_pAmx->base + (int)hdr->dat))
{
return (cell)(cip-(cell)code);
} else {
return (cell)(code + cip);
}
}
int Debugger::_GetOpcodeFromCip(cell cip, cell *&addr)
{
AMX_HEADER *hdr = (AMX_HEADER*)(m_pAmx->base);
unsigned char *code = m_pAmx->base + (int)hdr->cod;
cell *p_cip = NULL;
//test if cip is between these
if (cip >= (cell)code && cip < (cell)(m_pAmx->base + (int)hdr->dat))
{
p_cip = (cell *)(cip);
} else {
p_cip = (cell *)(code + cip);
}
//move forward one entry
addr = p_cip + 1;
//p_cip should be aligned to an instruction!
cell instr = *p_cip;
if (instr < 1 || instr >= OP_NUM_OPCODES)
{
if (!m_pOpcodeList)
return 0;
//we have an invalid opcode, so try searching for it
for (cell i=1; i<OP_NUM_OPCODES; i++)
{
if ((cell)m_pOpcodeList[i] == instr)
{
instr = i;
break;
}
}
if (instr < 1 || instr >= OP_NUM_OPCODES)
instr = 0; //nothing found
}
return (int)instr;
}
void Debugger::_CacheAmxOpcodeList()
{
m_pOpcodeList = (cell *)m_pAmx->userdata[UD_OPCODELIST];
}
//by BAILOPAN
// generic error printing routine
// for pawn 3.0 this is just a wrapper
const char *GenericError(int err)
{
static const char *amx_errs[] =
{
NULL,
"forced exit",
"assertion failed",
"stack error",
"index out of bounds",
"memory access",
"invalid instruction",
"stack low",
"heap low",
"callback",
"native error",
"divide",
"sleep",
"invalid access state",
"native not found",
NULL,
"out of memory", //16
"bad file format",
"bad file version",
"function not found",
"invalid entry point",
"debugger cannot run",
"plugin un or re-initialized",
"userdata table full",
"JIT failed to initialize",
"parameter error",
"domain error",
};
//does this plugin have line ops?
const char *geterr = NULL;
if (err <= 26 && err > 0)
geterr = amx_errs[err];
return geterr ? geterr : "unknown error";
}
int AMXAPI Debugger::DebugHook(AMX *amx)
{
Debugger *pDebugger = NULL;
if (!amx || !(amx->flags & AMX_FLAG_DEBUG))
return AMX_ERR_NONE;
if (amx->flags & AMX_FLAG_PRENIT)
return AMX_ERR_NONE;
pDebugger = (Debugger *)amx->userdata[UD_DEBUGGER];
if (!pDebugger)
return AMX_ERR_NONE;
pDebugger->StepI();
return AMX_ERR_NONE;
}
void Debugger::Clear()
{
for (size_t i = 0; i < m_pCalls.length(); i++)
{
delete m_pCalls[i];
}
m_pCalls.clear();
}
void Debugger::DisplayTrace(const char *message)
{
if (message != NULL)
AMXXLOG_Error("%s", message);
char buffer[512];
int length = FormatError(buffer, sizeof(buffer)-1);
const char *filename = _GetFilename();
const char *version = _GetVersion();
AMXXLOG_Error("[AMXX] Displaying debug trace (plugin \"%s\", version \"%s\")", filename, version);
if (length != -1) // Don't show blank line if AMX_ERR_NONE is set since there is no error message.
{
AMXXLOG_Error("[AMXX] %s", buffer);
}
int count = 0;
long lLine;
const char *file, *function;
trace_info_t *pTrace = GetTraceStart();
while (pTrace)
{
GetTraceInfo(pTrace, lLine, function, file);
AMXXLOG_Error(
"[AMXX] [%d] %s::%s (line %d)",
count,
file,
function,
(int)(lLine + 1)
);
count++;
pTrace = GetNextTrace(pTrace);
}
}
const char *Debugger::_GetFilename()
{
if (m_FileName.length() < 1)
{
CPluginMngr::CPlugin *pl = g_plugins.findPluginFast(m_pAmx);
if (pl)
{
m_FileName = pl->getName();
}
else
{
for (auto script : g_loadedscripts)
{
if (script->getAMX() == m_pAmx)
{
m_FileName = script->getName();
break;
}
}
}
}
return m_FileName.chars();
}
const char *Debugger::_GetVersion()
{
if (m_Version.length() < 1)
{
const char *version = "";
CPluginMngr::CPlugin *pl = g_plugins.findPluginFast(m_pAmx);
if (pl)
{
version = pl->getVersion();
}
m_Version = version;
}
return m_Version.chars();
}
void Debugger::FmtGenericMsg(AMX *amx, int error, char buffer[], size_t maxLength)
{
const char *filename = "";
char native[sNAMEMAX+1];
for (auto script : g_loadedscripts)
{
if (script->getAMX() == amx)
{
filename = script->getName();
break;
}
}
size_t len = strlen(filename);
for (size_t i=len-1; i<len; i--)
{
if ((filename[i] == '/' || filename[i] == '\\') && i != len - 1)
{
filename = &(filename[i+1]);
break;
}
}
if (error == AMX_ERR_EXIT)
{
ke::SafeSprintf(buffer, maxLength, "Run time error %d (plugin \"%s\") - %s", error, filename, GenericError(AMX_ERR_EXIT));
} else if (error == AMX_ERR_NATIVE) {
amx_GetNative(amx, reinterpret_cast<long>(amx->usertags[UT_NATIVE]), native);
ke::SafeSprintf(buffer, maxLength, "Run time error %d (plugin \"%s\") (native \"%s\") - debug not enabled!", error, filename, native);
} else {
ke::SafeSprintf(buffer, maxLength, "Run time error %d (plugin \"%s\") - debug not enabled!", error, filename);
}
}
void Debugger::GenericMessage(AMX *amx, int err)
{
static char buffer[512];
buffer[0] = '\0';
Debugger::FmtGenericMsg(amx, err, buffer, sizeof(buffer)-1);
if (buffer[0] != '\0')
AMXXLOG_Error("[AMXX] %s", buffer);
}
Debugger::~Debugger()
{
Clear();
dbg_FreeInfo(m_pAmxDbg);
delete m_pAmxDbg;
}
int Handler::SetErrorHandler(const char *function)
{
int error;
error = amx_FindPublic(m_pAmx, function, &m_iErrFunc);
if (error != AMX_ERR_NONE && m_iErrFunc < 0)
m_iErrFunc = -1;
return error;
}
int Handler::SetModuleFilter(const char *function)
{
int error;
error = amx_FindPublic(m_pAmx, function, &m_iModFunc);
if (error != AMX_ERR_NONE && m_iModFunc < 0)
m_iModFunc = -1;
return error;
}
int Handler::SetNativeFilter(const char *function)
{
int error;
error = amx_FindPublic(m_pAmx, function, &m_iNatFunc);
if (error != AMX_ERR_NONE && !IsNativeFiltering())
m_iNatFunc = -1;
return error;
}
void Handler::SetErrorMsg(const char *msg)
{
if (!msg)
m_MsgCache = nullptr;
else
m_MsgCache = msg;
}
const char *Handler::GetLastMsg()
{
if (m_MsgCache.length() < 1)
return NULL;
return m_MsgCache.chars();
}
int Handler::HandleModule(const char *module, bool isClass)
{
if (m_iModFunc < 0)
return 0;
/**
* This is the most minimalistic handler of them all
*/
cell hea_addr, *phys_addr, retval;
Debugger *pd;
pd = DisableDebugHandler(m_pAmx);
//temporarily set prenit
m_pAmx->flags |= AMX_FLAG_PRENIT;
amx_Push(m_pAmx, isClass ? 1 : 0);
amx_PushString(m_pAmx, &hea_addr, &phys_addr, module, 0, 0);
int err = amx_Exec(m_pAmx, &retval, m_iModFunc);
amx_Release(m_pAmx, hea_addr);
m_pAmx->flags &= ~AMX_FLAG_PRENIT;
EnableDebugHandler(m_pAmx, pd);
if (err != AMX_ERR_NONE)
return 0;
return (int)retval;
}
int Handler::HandleNative(const char *native, int index, int trap)
{
if (!IsNativeFiltering())
return 0;
/**
* Our handler here is a bit different from HandleError().
* For one, there is no current error in pDebugger, so we
* don't have to save some states.
*/
m_InNativeFilter = true;
Debugger *pDebugger = (Debugger *)m_pAmx->userdata[UD_DEBUGGER];
if (pDebugger && trap)
pDebugger->BeginExec();
else if (pDebugger && !trap)
DisableDebugHandler(m_pAmx);
cell hea_addr, *phys_addr, retval;
if (!trap)
m_pAmx->flags |= AMX_FLAG_PRENIT;
amx_Push(m_pAmx, trap);
amx_Push(m_pAmx, index);
amx_PushString(m_pAmx, &hea_addr, &phys_addr, native, 0, 0);
int err = amx_Exec(m_pAmx, &retval, m_iNatFunc);
if (err != AMX_ERR_NONE)
{
//LogError() took care of something for us.
if (err == -1)
{
m_InNativeFilter = false;
amx_Release(m_pAmx, hea_addr);
return 1;
}
if (!trap)
{
AMXXLOG_Error("[AMXX] Runtime failure %d occurred in native filter. Aborting plugin load.", err);
return 0;
}
//handle this manually.
//we need to push this through an error filter, same as executeForwards!
if (pDebugger && pDebugger->ErrorExists())
{
//don't display, it was already handled.
} else if (err != -1) {
LogError(m_pAmx, err, NULL);
}
AMXXLOG_Error("[AMXX] NOTE: Runtime failures in native filters are not good!");
retval = 0;
}
if (!trap)
m_pAmx->flags &= ~AMX_FLAG_PRENIT;
if (pDebugger && trap)
pDebugger->EndExec();
else if (pDebugger && !trap)
EnableDebugHandler(m_pAmx, pDebugger);
amx_Release(m_pAmx, hea_addr);
m_InNativeFilter = false;
return (int)retval;
}
int Handler::HandleError(const char *msg)
{
if (m_iErrFunc <= 0)
return 0;
m_Handling = true;
m_pTrace = nullptr;
m_FmtCache = nullptr;
Debugger *pDebugger = (Debugger *)m_pAmx->userdata[UD_DEBUGGER];
int error = m_pAmx->error;
static char _buffer[512];
if (pDebugger)
{
pDebugger->SetTracedError(error);
m_pTrace = pDebugger->GetTraceStart();
pDebugger->FormatError(_buffer, sizeof(_buffer)-1);
m_FmtCache = _buffer;
pDebugger->BeginExec();
} else {
Debugger::FmtGenericMsg(m_pAmx, error, _buffer, sizeof(_buffer)-1);
m_FmtCache = _buffer;
}
SetErrorMsg(msg);
cell hea_addr, *phys_addr, result;
amx_PushString(m_pAmx, &hea_addr, &phys_addr, msg, 0, 0);
amx_Push(m_pAmx, pDebugger ? 1 : 0);
amx_Push(m_pAmx, error);
int err = amx_Exec(m_pAmx, &result, m_iErrFunc);
if (err != AMX_ERR_NONE)
{
//handle this manually.
if (pDebugger)
{
pDebugger->SetTracedError(err);
pDebugger->DisplayTrace(msg);
} else {
if (GetLastMsg())
AMXXLOG_Error("%s", GetLastMsg());
Debugger::GenericMessage(m_pAmx, err);
}
AMXXLOG_Error("[AMXX] NOTE: Runtime failures in an error filter are not good!");
}
if (pDebugger)
pDebugger->EndExec();
amx_Release(m_pAmx, hea_addr);
m_Handling = false;
m_pTrace = nullptr;
m_FmtCache = nullptr;
if (err != AMX_ERR_NONE || !result)
return 0;
return result;
}
static cell AMX_NATIVE_CALL set_error_filter(AMX *amx, cell *params)
{
int len;
char *function = get_amxstring(amx, params[1], 0, len);
Handler *pHandler = (Handler *)amx->userdata[UD_HANDLER];
if (!pHandler)
{
Debugger::GenericMessage(amx, AMX_ERR_NOTFOUND);
AMXXLOG_Error("[AMXX] Plugin not initialized correctly.");
return 0;
}
int err = pHandler->SetErrorHandler(function);
if (err != AMX_ERR_NONE)
{
Debugger::GenericMessage(amx, AMX_ERR_NOTFOUND);
AMXXLOG_Error("[AMXX] Function not found: %s", function);
return 0;
}
return 1;
}
static cell AMX_NATIVE_CALL dbg_trace_begin(AMX *amx, cell *params)
{
Handler *pHandler = (Handler *)amx->userdata[UD_HANDLER];
if (!pHandler)
return 0; //should never happen
trace_info_t *pTrace = pHandler->GetTrace();
return (cell)(pTrace);
}
static cell AMX_NATIVE_CALL dbg_trace_next(AMX *amx, cell *params)
{
Debugger *pDebugger = (Debugger *)amx->userdata[UD_DEBUGGER];
if (!pDebugger)
return 0;
trace_info_t *pTrace = (trace_info_t *)(params[1]);
if (pTrace)
return (cell)(pDebugger->GetNextTrace(pTrace));
return 0;
}
static cell AMX_NATIVE_CALL dbg_trace_info(AMX *amx, cell *params)
{
Debugger *pDebugger = (Debugger *)amx->userdata[UD_DEBUGGER];
if (!pDebugger)
return 0;
trace_info_t *pTrace = (trace_info_t *)(params[1]);
if (!pTrace)
return 0;
cell *line_addr = get_amxaddr(amx, params[2]);
long lLine=-1;
const char *function=NULL, *file=NULL;
pDebugger->GetTraceInfo(pTrace, lLine, function, file);
set_amxstring(amx, params[3], function ? function : "", params[4]);
set_amxstring(amx, params[5], file ? file : "", params[5]);
*line_addr = (cell)lLine + 1;
return 1;
}
static cell AMX_NATIVE_CALL dbg_fmt_error(AMX *amx, cell *params)
{
Handler *pHandler = (Handler *)amx->userdata[UD_HANDLER];
if (!pHandler)
return 0;
const char *str = pHandler->GetFmtCache();
set_amxstring(amx, params[1], str, params[2]);
return 1;
}
static cell AMX_NATIVE_CALL set_native_filter(AMX *amx, cell *params)
{
Handler *pHandler = (Handler *)amx->userdata[UD_HANDLER];
if (!pHandler)
{
Debugger::GenericMessage(amx, AMX_ERR_NOTFOUND);
AMXXLOG_Error("[AMXX] Plugin not initialized correctly.");
return 0;
}
if (!pHandler->IsNativeFiltering())
{
//we can only initialize this during PRENIT
if (! (amx->flags & AMX_FLAG_PRENIT) )
return 0;
}
int len;
char *func = get_amxstring(amx, params[1], 0, len);
int err = pHandler->SetNativeFilter(func);
if (err != AMX_ERR_NONE)
{
Debugger::GenericMessage(amx, AMX_ERR_NOTFOUND);
AMXXLOG_Error("[AMXX] Function not found: %s", function);
return 0;
}
return 1;
}
static cell AMX_NATIVE_CALL set_module_filter(AMX *amx, cell *params)
{
if ( !(amx->flags & AMX_FLAG_PRENIT) )
return -1;
Handler *pHandler = (Handler *)amx->userdata[UD_HANDLER];
if (!pHandler)
return -2;
int len;
char *function = get_amxstring(amx, params[1], 0, len);
return pHandler->SetModuleFilter(function);
}
AMX_NATIVE_INFO g_DebugNatives[] = {
{"set_error_filter", set_error_filter},
{"dbg_trace_begin", dbg_trace_begin},
{"dbg_trace_next", dbg_trace_next},
{"dbg_trace_info", dbg_trace_info},
{"dbg_fmt_error", dbg_fmt_error},
{"set_native_filter", set_native_filter},
{"set_module_filter", set_module_filter},
{NULL, NULL},
};