Committed new debugger with AMX fixes
This commit is contained in:
784
amxmodx/debugger.cpp
Executable file
784
amxmodx/debugger.cpp
Executable file
@@ -0,0 +1,784 @@
|
||||
/* AMX Mod X
|
||||
*
|
||||
* by the AMX Mod X Development Team
|
||||
* originally developed by OLO
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* In addition, as a special exception, the author gives permission to
|
||||
* link the code of this program with the Half-Life Game Engine ("HL
|
||||
* Engine") and Modified Game Libraries ("MODs") developed by Valve,
|
||||
* L.L.C ("Valve"). You must obey the GNU General Public License in all
|
||||
* respects for all of the code used other than the HL Engine and MODs
|
||||
* from Valve. If you modify this file, you may extend this exception
|
||||
* to your version of the file, but you are not obligated to do so. If
|
||||
* you do not wish to do so, delete this exception statement from your
|
||||
* version.
|
||||
*/
|
||||
|
||||
#include "amxmodx.h"
|
||||
#include "debugger.h"
|
||||
|
||||
#if !defined WIN32 && !defined _WIN32
|
||||
#define _snprintf sprintf
|
||||
#endif
|
||||
|
||||
/**
|
||||
* 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.size())
|
||||
{
|
||||
Tracer *pTracer = new Tracer();
|
||||
m_pCalls.push_back(pTracer);
|
||||
assert(m_Top == (m_pCalls.size() - 1));
|
||||
}
|
||||
|
||||
m_pCalls[m_Top]->Reset();
|
||||
}
|
||||
|
||||
void Debugger::EndExec()
|
||||
{
|
||||
assert(m_Top >= 0 && m_Top < (int)m_pCalls.size());
|
||||
|
||||
m_pCalls[m_Top]->Reset();
|
||||
|
||||
m_Top--;
|
||||
}
|
||||
|
||||
void Debugger::StepI()
|
||||
{
|
||||
assert(m_Top >= 0 && m_Top < (int)m_pCalls.size());
|
||||
|
||||
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.size());
|
||||
|
||||
return m_pCalls[m_Top]->m_Error;
|
||||
}
|
||||
|
||||
void Debugger::SetTracedError(int error)
|
||||
{
|
||||
assert(m_Top >= 0 && m_Top < (int)m_pCalls.size());
|
||||
|
||||
m_pCalls[m_Top]->m_Error = error;
|
||||
}
|
||||
|
||||
trace_info_t *Debugger::GetTraceStart() const
|
||||
{
|
||||
assert(m_Top >= 0 && m_Top < (int)m_pCalls.size());
|
||||
|
||||
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.size());
|
||||
|
||||
return (m_pCalls[m_Top]->m_Error != AMX_ERR_NONE);
|
||||
}
|
||||
|
||||
#define FLAG_INDIRECT (1<<0)
|
||||
|
||||
//vaddr - the address of our current index vector
|
||||
//base - the base address of which the array is offset to
|
||||
//dim - the current dimension to search
|
||||
//dimNum - the number of dimensions total
|
||||
//sizes[] - an array containing the dimension sizes
|
||||
//Indexes[] - an output array to contain each dimension's index
|
||||
int WalkArray(cell *vaddr, unsigned char *base, cell *addr, int dim, int dimNum, int &flags, int sizes[], int Indexes[])
|
||||
{
|
||||
cell *my_addr;
|
||||
int idx = 0;
|
||||
|
||||
//if we are the second to last walker, we only need to check the ranges of our vector.
|
||||
if (dim == dimNum - 2)
|
||||
{
|
||||
my_addr = vaddr;
|
||||
//first check the actual vectors themselves
|
||||
for (int i=0; i<sizes[dim]; i++)
|
||||
{
|
||||
if (addr == my_addr)
|
||||
return i;
|
||||
my_addr++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//otherwise, search lower vectors!
|
||||
//vaddr is the address where we can start reading vectors
|
||||
flags |= FLAG_INDIRECT;
|
||||
for (int i=0; i<sizes[dim]; i++)
|
||||
{
|
||||
//the next vector is offset from the last address!
|
||||
//this is funky but that's the internal implementation
|
||||
my_addr = (cell *)((char *)vaddr + i*sizeof(cell) + vaddr[i]);
|
||||
idx = WalkArray(my_addr, base, addr, dim+1, dimNum, flags, sizes, Indexes);
|
||||
if (idx != -1)
|
||||
{
|
||||
Indexes[dim+1] = idx;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Debugger::FormatError(char *buffer, size_t maxLength)
|
||||
{
|
||||
if (!ErrorExists())
|
||||
return -1;
|
||||
|
||||
assert(m_Top >= 0 && m_Top < (int)m_pCalls.size());
|
||||
|
||||
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 += _snprintf(buffer, maxLength, "Run time error %d: %s ", error, gen_err);
|
||||
buffer += size;
|
||||
maxLength -= size;
|
||||
|
||||
if (error == AMX_ERR_NATIVE)
|
||||
{
|
||||
char native_name[32];
|
||||
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;
|
||||
} else if (instr == OP_SYSREQ_PRI) {
|
||||
num = (int)m_pAmx->pri;
|
||||
}
|
||||
if (num)
|
||||
amx_err = amx_GetNative(m_pAmx, (int)*p_cip, native_name);
|
||||
else
|
||||
amx_err = AMX_ERR_NOTFOUND;
|
||||
if (!amx_err)
|
||||
size += _snprintf(buffer, maxLength, "(native \"%s\")", native_name);
|
||||
} else if (error == AMX_ERR_BOUNDS) {
|
||||
tagAMX_DBG *pDbg = m_pAmxDbg;
|
||||
int symbols = pDbg->hdr->symbols;
|
||||
int index = 0;
|
||||
tagAMX_DBG_SYMBOL **pSymbols = pDbg->symboltbl;
|
||||
tagAMX_DBG_SYMBOL *pSymbol, *pLastSymbol=NULL;
|
||||
const tagAMX_DBG_SYMDIM *pDims;
|
||||
ucell addr = 0;
|
||||
int flags = 0;
|
||||
char v_class, i_dent;
|
||||
cell *arr_addr=NULL, *p_addr=NULL;
|
||||
unsigned char *data = m_pAmx->base + ((AMX_HEADER *)m_pAmx->base)->dat;
|
||||
bool valid=false;
|
||||
//we can't really browse the assembly because
|
||||
// we've no idea what the peephole optimizer did.
|
||||
// so we're gonna try to go out on a limb and guess.
|
||||
if (m_pAmx->alt < 0)
|
||||
{
|
||||
//take a guess that it's local
|
||||
addr = m_pAmx->alt - pTrace->frm;
|
||||
v_class = 1;
|
||||
} else {
|
||||
//take a guess that it's a global
|
||||
//it won't be global if it's passed in from the stack frame, however
|
||||
// doing this with a hardcoded array size is quite rare, and is probably passed
|
||||
// as iREFARRAY not iARRAY!
|
||||
addr = m_pAmx->alt;
|
||||
v_class = 0;
|
||||
}
|
||||
bool found = false;
|
||||
bool _found = true;
|
||||
static char _msgbuf[255];
|
||||
size_t _size = 0;
|
||||
//take a pre-emptive guess at the v_class!
|
||||
//are we GLOBAL (0) or LOCAL (1) ?
|
||||
if (m_pAmx->alt < 0)
|
||||
{
|
||||
v_class = 1;
|
||||
i_dent = iARRAY;
|
||||
arr_addr = (cell *)(data + pTrace->frm + m_pAmx->alt);
|
||||
} else {
|
||||
//it's greater than 0, check other things!
|
||||
if (m_pAmx->alt >= m_pAmx->hlw &&
|
||||
m_pAmx->alt <= m_pAmx->stp)
|
||||
{
|
||||
//it's in the stack somewhere... guess that it's a local!
|
||||
v_class = 1;
|
||||
//relocate it
|
||||
m_pAmx->alt -= pTrace->frm;
|
||||
//alt cannot be zero
|
||||
if (m_pAmx->alt < 0)
|
||||
i_dent = iARRAY;
|
||||
else
|
||||
i_dent = iREFARRAY;
|
||||
arr_addr = (cell *)(data + pTrace->frm + m_pAmx->alt);
|
||||
} else {
|
||||
//guess that it's DAT
|
||||
v_class = 0;
|
||||
i_dent = iARRAY;
|
||||
arr_addr = (cell *)(data + m_pAmx->alt);
|
||||
}
|
||||
}
|
||||
for (index = 0; index < symbols; index++)
|
||||
{
|
||||
pSymbol = pSymbols[index];
|
||||
if (pSymbol->codestart <= (ucell)cip &&
|
||||
pSymbol->codeend >= (ucell)cip &&
|
||||
(pSymbol->ident == iARRAY || pSymbol->ident == iREFARRAY))
|
||||
{
|
||||
amx_err = dbg_GetArrayDim(pDbg, pSymbol, &pDims);
|
||||
if (amx_err != AMX_ERR_NONE)
|
||||
continue;
|
||||
//calculate the size of the array. this is important!
|
||||
ucell size = pDims[0].size;
|
||||
ucell aggre = pDims[0].size;
|
||||
valid = false;
|
||||
for (int16_t i=1; i<pSymbol->dim; i++)
|
||||
{
|
||||
aggre *= pDims[i].size;
|
||||
size += aggre;
|
||||
}
|
||||
if (pSymbol->vclass != v_class)
|
||||
continue;
|
||||
if (pSymbol->ident != i_dent)
|
||||
continue;
|
||||
if (v_class == 1)
|
||||
{
|
||||
if (i_dent == iARRAY)
|
||||
{
|
||||
p_addr = (cell *)(data + pTrace->frm + pSymbol->address);
|
||||
} else if (i_dent == iREFARRAY) {
|
||||
//get the variable off the stack, by reference
|
||||
ucell _addr = (ucell)*((cell *)(data + pTrace->frm + pSymbol->address));
|
||||
p_addr = (cell *)(data + _addr);
|
||||
}
|
||||
} else if (v_class == 0) {
|
||||
p_addr = (cell *)(data + pSymbol->address);
|
||||
}
|
||||
int *sizes = new int[pSymbol->dim];
|
||||
int *indexes = new int[pSymbol->dim];
|
||||
for (int i=0; i<pSymbol->dim; i++)
|
||||
{
|
||||
sizes[i] = pDims[i].size;
|
||||
indexes[i] = -1;
|
||||
}
|
||||
flags = 0;
|
||||
if (pSymbol->dim >= 2)
|
||||
{
|
||||
int dims = pSymbol->dim;
|
||||
indexes[0] = WalkArray(p_addr, data, arr_addr, 0, pSymbol->dim, flags, sizes, indexes);
|
||||
if (indexes[0] == -1)
|
||||
{
|
||||
while (indexes[0] == -1 && --dims > 0)
|
||||
{
|
||||
flags = 0;
|
||||
indexes[0] = WalkArray(p_addr, data, arr_addr, 0, dims, flags, sizes, indexes);
|
||||
}
|
||||
}
|
||||
//find the last known good dimension
|
||||
for (dims=pSymbol->dim-1; dims>=0; dims--)
|
||||
{
|
||||
if (indexes[dims] != -1)
|
||||
break;
|
||||
}
|
||||
//check for the "impossible" case.
|
||||
//if we have [X][-1], and X is zero, the array did not walk properly.
|
||||
if (dims >= 0
|
||||
&& indexes[dims] == 0
|
||||
&& !(flags & FLAG_INDIRECT)
|
||||
&& dims < pSymbol->dim - 1)
|
||||
{
|
||||
//here we have the dreaded MIXED CASE. we don't know whether
|
||||
//[-][X] or [0][-] (where - is a bad input) was intended.
|
||||
//first, we take a guess by checking the bounds.
|
||||
cell *_cip = (cell *)_CipAsVa(cip);
|
||||
_cip -= 1;
|
||||
cell bounds = *_cip;
|
||||
if (sizes[dims] != sizes[dims+1])
|
||||
{
|
||||
//we were checking initial bounds
|
||||
if (bounds == sizes[dims] - 1)
|
||||
{
|
||||
indexes[dims] = m_pAmx->pri;
|
||||
} else if (bounds == sizes[dims+1] - 1) {
|
||||
indexes[dims + 1] = m_pAmx->pri;
|
||||
indexes[dims] = 0;
|
||||
} else {
|
||||
//this should really never happen...
|
||||
_found = false;
|
||||
}
|
||||
} else {
|
||||
_found = false;
|
||||
}
|
||||
if (!_found)
|
||||
{
|
||||
//we still don't have a good approximation.
|
||||
//the user did something like:
|
||||
//new X[40][40]
|
||||
//we could do some really complicated and random guesswork
|
||||
// but fact is, we have no way of deterministically knowing
|
||||
// what the user intended.
|
||||
}
|
||||
} else {
|
||||
//set the last know index to our culprit
|
||||
indexes[dims + 1] = m_pAmx->pri;
|
||||
}
|
||||
} else {
|
||||
indexes[0] = m_pAmx->pri;
|
||||
}
|
||||
_size += _snprintf(&(_msgbuf[_size]), sizeof(_msgbuf)-_size, "(array \"%s", pSymbol->name);
|
||||
for (int i=0; i<pSymbol->dim; i++)
|
||||
_size += _snprintf(&(_msgbuf[_size]), sizeof(_msgbuf)-_size, "[%d]", pDims[i].size);
|
||||
if (_found)
|
||||
{
|
||||
_size += _snprintf(&(_msgbuf[_size]), sizeof(_msgbuf)-_size, "\") (indexed \"");
|
||||
for (int i=0; i<pSymbol->dim; i++)
|
||||
{
|
||||
if (indexes[i] == -1)
|
||||
_size += _snprintf(&(_msgbuf[_size]), sizeof(_msgbuf)-_size, "[]");
|
||||
else
|
||||
_size += _snprintf(&(_msgbuf[_size]), sizeof(_msgbuf)-_size, "[%d]", indexes[i]);
|
||||
}
|
||||
_size += _snprintf(&(_msgbuf[_size]), sizeof(_msgbuf)-_size, "\")");
|
||||
} else {
|
||||
_size += _snprintf(&(_msgbuf[_size]), sizeof(_msgbuf)-_size, "\") (unknown index \"%d\")", m_pAmx->pri);
|
||||
}
|
||||
found = true;
|
||||
delete [] indexes;
|
||||
delete [] sizes;
|
||||
break;
|
||||
} /* symbol validation */
|
||||
} /* is in valid ranges */
|
||||
if (!found)
|
||||
_msgbuf[0] = '\0';
|
||||
|
||||
size += _snprintf(buffer, maxLength, "%s", _msgbuf);
|
||||
}
|
||||
|
||||
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",
|
||||
NULL,
|
||||
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;
|
||||
|
||||
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.size(); i++)
|
||||
delete m_pCalls[i];
|
||||
|
||||
m_pCalls.clear();
|
||||
}
|
||||
|
||||
Debugger::~Debugger()
|
||||
{
|
||||
Clear();
|
||||
}
|
Reference in New Issue
Block a user