Committed new debugger with AMX fixes
This commit is contained in:
		| @@ -30,8 +30,7 @@ | |||||||
| */ | */ | ||||||
|  |  | ||||||
| #include "amxmodx.h" | #include "amxmodx.h" | ||||||
|  | #include "debugger.h" | ||||||
| void AMXAPI amxx_InvalidateTrace(AMX *amx); |  | ||||||
|  |  | ||||||
| CForward::CForward(const char *name, ForwardExecType et, int numParams, const ForwardParam *paramTypes) | CForward::CForward(const char *name, ForwardExecType et, int numParams, const ForwardParam *paramTypes) | ||||||
| { | { | ||||||
| @@ -74,9 +73,9 @@ cell CForward::execute(cell *params, ForwardPreparedArray *preparedArrays) | |||||||
| 		{ | 		{ | ||||||
| 			// Get debug info | 			// Get debug info | ||||||
| 			AMX *amx = (*iter).pPlugin->getAMX(); | 			AMX *amx = (*iter).pPlugin->getAMX(); | ||||||
| 			AMX_DBGINFO *pInfo = (AMX_DBGINFO *)(amx->userdata[2]); | 			Debugger *pDebugger = (Debugger *)amx->userdata[UD_DEBUGGER]; | ||||||
| 			if (pInfo) | 			if (pDebugger) | ||||||
| 				pInfo->error = AMX_ERR_NONE; | 				pDebugger->BeginExec(); | ||||||
| 			// handle strings & arrays | 			// handle strings & arrays | ||||||
| 			int i, ax=0; | 			int i, ax=0; | ||||||
| 			for (i = 0; i < m_NumParams; ++i) | 			for (i = 0; i < m_NumParams; ++i) | ||||||
| @@ -124,16 +123,17 @@ cell CForward::execute(cell *params, ForwardPreparedArray *preparedArrays) | |||||||
| 			if (err != AMX_ERR_NONE) | 			if (err != AMX_ERR_NONE) | ||||||
| 			{ | 			{ | ||||||
| 				//Did something else set an error? | 				//Did something else set an error? | ||||||
| 				if (pInfo && pInfo->error != AMX_ERR_NONE) | 				if (pDebugger && pDebugger->ErrorExists()) | ||||||
| 				{ | 				{ | ||||||
| 					//we don't care, something else logged the error. | 					//we don't care, something else logged the error. | ||||||
| 				} else { | 				} else if (err != -1) { | ||||||
| 					//nothing logged the error so spit it out anyway | 					//nothing logged the error so spit it out anyway | ||||||
| 					LogError(amx, err, ""); | 					LogError(amx, err, NULL); | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			amxx_InvalidateTrace(amx); |  | ||||||
| 			amx->error = AMX_ERR_NONE; | 			amx->error = AMX_ERR_NONE; | ||||||
|  | 			if (pDebugger) | ||||||
|  | 				pDebugger->EndExec(); | ||||||
|  |  | ||||||
| 			// cleanup strings & arrays | 			// cleanup strings & arrays | ||||||
| 			for (i = 0; i < m_NumParams; ++i) | 			for (i = 0; i < m_NumParams; ++i) | ||||||
| @@ -229,9 +229,9 @@ cell CSPForward::execute(cell *params, ForwardPreparedArray *preparedArrays) | |||||||
| 	if (!pPlugin->isExecutable(m_Func)) | 	if (!pPlugin->isExecutable(m_Func)) | ||||||
| 		return 0; | 		return 0; | ||||||
|  |  | ||||||
| 	AMX_DBGINFO *pInfo = (AMX_DBGINFO *)(m_Amx->userdata[2]); | 	Debugger *pDebugger = (Debugger *)m_Amx->userdata[UD_DEBUGGER]; | ||||||
| 	if (pInfo) | 	if (pDebugger) | ||||||
| 		pInfo->error = AMX_ERR_NONE; | 		pDebugger->BeginExec(); | ||||||
|  |  | ||||||
| 	// handle strings & arrays | 	// handle strings & arrays | ||||||
| 	int i; | 	int i; | ||||||
| @@ -276,15 +276,16 @@ cell CSPForward::execute(cell *params, ForwardPreparedArray *preparedArrays) | |||||||
| 	if (err != AMX_ERR_NONE) | 	if (err != AMX_ERR_NONE) | ||||||
| 	{ | 	{ | ||||||
| 		//Did something else set an error? | 		//Did something else set an error? | ||||||
| 		if (pInfo && pInfo->error != AMX_ERR_NONE) | 		if (pDebugger && pDebugger->ErrorExists()) | ||||||
| 		{ | 		{ | ||||||
| 			//we don't care, something else logged the error. | 			//we don't care, something else logged the error. | ||||||
| 		} else { | 		} else if (err != -1) { | ||||||
| 			//nothing logged the error so spit it out anyway | 			//nothing logged the error so spit it out anyway | ||||||
| 			LogError(m_Amx, err, ""); | 			LogError(m_Amx, err, NULL); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	amxx_InvalidateTrace(m_Amx); | 	if (pDebugger) | ||||||
|  | 		pDebugger->EndExec(); | ||||||
| 	m_Amx->error = AMX_ERR_NONE; | 	m_Amx->error = AMX_ERR_NONE; | ||||||
|  |  | ||||||
| 	// cleanup strings & arrays | 	// cleanup strings & arrays | ||||||
|   | |||||||
| @@ -134,7 +134,7 @@ void CPluginMngr::clear() { | |||||||
|  |  | ||||||
| CPluginMngr::CPlugin* CPluginMngr::findPluginFast(AMX *amx)  | CPluginMngr::CPlugin* CPluginMngr::findPluginFast(AMX *amx)  | ||||||
| {  | {  | ||||||
| 	return (CPlugin*)(amx->userdata[3]);  | 	return (CPlugin*)(amx->userdata[UD_FINDPLUGIN]);  | ||||||
| } | } | ||||||
|  |  | ||||||
| CPluginMngr::CPlugin* CPluginMngr::findPlugin(AMX *amx) { | CPluginMngr::CPlugin* CPluginMngr::findPlugin(AMX *amx) { | ||||||
| @@ -197,7 +197,7 @@ CPluginMngr::CPlugin::CPlugin(int i, const char* p,const char* n, char* e, int d | |||||||
| 	} else { | 	} else { | ||||||
| 		status = ps_bad_load; | 		status = ps_bad_load; | ||||||
| 	} | 	} | ||||||
| 	amx.userdata[3] = this; | 	amx.userdata[UD_FINDPLUGIN] = this; | ||||||
| 	paused_fun = 0; | 	paused_fun = 0; | ||||||
| 	next = 0; | 	next = 0; | ||||||
| 	id = i; | 	id = i; | ||||||
|   | |||||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -450,11 +450,15 @@ int AMXAPI amx_Callback(AMX *amx, cell index, cell *result, cell *params) | |||||||
|    * This trick cannot work in the JIT, because the program would need to |    * This trick cannot work in the JIT, because the program would need to | ||||||
|    * be re-JIT-compiled after patching a P-code instruction. |    * be re-JIT-compiled after patching a P-code instruction. | ||||||
|    */ |    */ | ||||||
|   #if defined JIT && !defined NDEBUG | #if !defined JIT | ||||||
|     if ((amx->flags & AMX_FLAG_JITC)!=0) |   if (amx->sysreq_d != 0) | ||||||
|       assert(amx->sysreq_d==0); |   { | ||||||
|   #endif |     //if we're about to patch this, and we're debugging, don't patch! | ||||||
|   if (amx->sysreq_d!=0) { |     //otherwise we won't be able to get back native names | ||||||
|  |     if (amx->flags & AMX_FLAG_DEBUG) | ||||||
|  |     { | ||||||
|  |       amx->sysreq_d = 0; | ||||||
|  | 	} else { | ||||||
|       /* at the point of the call, the CIP pseudo-register points directly |       /* at the point of the call, the CIP pseudo-register points directly | ||||||
|        * behind the SYSREQ instruction and its parameter. |        * behind the SYSREQ instruction and its parameter. | ||||||
|        */ |        */ | ||||||
| @@ -467,11 +471,13 @@ int AMXAPI amx_Callback(AMX *amx, cell index, cell *result, cell *params) | |||||||
|       if (*(cell*)code!=OP_SYSREQ_PRI) { |       if (*(cell*)code!=OP_SYSREQ_PRI) { | ||||||
|         assert(*(cell*)(code-sizeof(cell))==OP_SYSREQ_C); |         assert(*(cell*)(code-sizeof(cell))==OP_SYSREQ_C); | ||||||
|         assert(*(cell*)code==index); |         assert(*(cell*)code==index); | ||||||
| #endif | #endif	//defined __GNU__ || defined ASM32 | ||||||
|         *(cell*)(code-sizeof(cell))=amx->sysreq_d; |         *(cell*)(code-sizeof(cell))=amx->sysreq_d; | ||||||
|         *(cell*)code=(cell)f; |         *(cell*)code=(cell)f; | ||||||
|       } /* if */ |       } /* if */ | ||||||
|     } /* if */ |     } /* if */ | ||||||
|  |   } /* if */ | ||||||
|  | #endif	//!defined JIT | ||||||
|  |  | ||||||
|   /* Note: |   /* Note: | ||||||
|    *   params[0] == number of bytes for the additional parameters passed to the native function |    *   params[0] == number of bytes for the additional parameters passed to the native function | ||||||
| @@ -547,6 +553,7 @@ static int amx_BrowseRelocate(AMX *amx) | |||||||
|      */ |      */ | ||||||
|     if ((amx->flags & AMX_FLAG_JITC)==0 && sizeof(AMX_NATIVE)<=sizeof(cell)) |     if ((amx->flags & AMX_FLAG_JITC)==0 && sizeof(AMX_NATIVE)<=sizeof(cell)) | ||||||
|       amx->sysreq_d=opcode_list[OP_SYSREQ_D]; |       amx->sysreq_d=opcode_list[OP_SYSREQ_D]; | ||||||
|  | 	amx->userdata[UD_OPCODELIST] = (void *)opcode_list; | ||||||
|   #else |   #else | ||||||
|     /* ANSI C |     /* ANSI C | ||||||
|      * to use direct system requests, a function pointer must fit in a cell; |      * to use direct system requests, a function pointer must fit in a cell; | ||||||
| @@ -554,6 +561,7 @@ static int amx_BrowseRelocate(AMX *amx) | |||||||
|      */ |      */ | ||||||
|     if (sizeof(AMX_NATIVE)<=sizeof(cell)) |     if (sizeof(AMX_NATIVE)<=sizeof(cell)) | ||||||
|       amx->sysreq_d=OP_SYSREQ_D; |       amx->sysreq_d=OP_SYSREQ_D; | ||||||
|  | 	amx->userdata[UD_OPCODELIST] = (long)NULL; | ||||||
|   #endif |   #endif | ||||||
|  |  | ||||||
|   /* start browsing code */ |   /* start browsing code */ | ||||||
| @@ -1676,7 +1684,13 @@ int AMXAPI amx_PushString(AMX *amx, cell *amx_addr, cell **phys_addr, const char | |||||||
| #define SKIPPARAM(n)    ( cip=(cell *)cip+(n) ) | #define SKIPPARAM(n)    ( cip=(cell *)cip+(n) ) | ||||||
| #define PUSH(v)         ( stk-=sizeof(cell), *(cell *)(data+(int)stk)=v ) | #define PUSH(v)         ( stk-=sizeof(cell), *(cell *)(data+(int)stk)=v ) | ||||||
| #define POP(v)          ( v=*(cell *)(data+(int)stk), stk+=sizeof(cell) ) | #define POP(v)          ( v=*(cell *)(data+(int)stk), stk+=sizeof(cell) ) | ||||||
| #define ABORT(amx,v)    { (amx)->stk=reset_stk; (amx)->hea=reset_hea; return v; } | #define ABORT(amx,v)    { (amx)->stk=reset_stk; \ | ||||||
|  | 						  (amx)->hea=reset_hea; \ | ||||||
|  | 						  (amx)->cip=(cell)cip; \ | ||||||
|  | 						  (amx)->pri=pri; \ | ||||||
|  | 						  (amx)->alt=alt; \ | ||||||
|  | 						  return v; \ | ||||||
|  | 						} | ||||||
|  |  | ||||||
| #define CHKMARGIN()     if (hea+STKMARGIN>stk) return AMX_ERR_STACKERR | #define CHKMARGIN()     if (hea+STKMARGIN>stk) return AMX_ERR_STACKERR | ||||||
| #define CHKSTACK()      if (stk>amx->stp) return AMX_ERR_STACKLOW | #define CHKSTACK()      if (stk>amx->stp) return AMX_ERR_STACKLOW | ||||||
| @@ -2471,6 +2485,8 @@ static const void * const amx_opcodelist[] = { | |||||||
|     amx->hea=hea; |     amx->hea=hea; | ||||||
|     amx->frm=frm; |     amx->frm=frm; | ||||||
|     amx->stk=stk; |     amx->stk=stk; | ||||||
|  |     amx->pri=pri; | ||||||
|  |     amx->alt=alt; | ||||||
|     num=amx->callback(amx,pri,&pri,(cell *)(data+(int)stk)); |     num=amx->callback(amx,pri,&pri,(cell *)(data+(int)stk)); | ||||||
|     if (num!=AMX_ERR_NONE) { |     if (num!=AMX_ERR_NONE) { | ||||||
|       if (num==AMX_ERR_SLEEP) { |       if (num==AMX_ERR_SLEEP) { | ||||||
| @@ -2490,6 +2506,8 @@ static const void * const amx_opcodelist[] = { | |||||||
|     amx->hea=hea; |     amx->hea=hea; | ||||||
|     amx->frm=frm; |     amx->frm=frm; | ||||||
|     amx->stk=stk; |     amx->stk=stk; | ||||||
|  |     amx->pri=pri; | ||||||
|  |     amx->alt=alt; | ||||||
|     num=amx->callback(amx,offs,&pri,(cell *)(data+(int)stk)); |     num=amx->callback(amx,offs,&pri,(cell *)(data+(int)stk)); | ||||||
|     if (num!=AMX_ERR_NONE) { |     if (num!=AMX_ERR_NONE) { | ||||||
|       if (num==AMX_ERR_SLEEP) { |       if (num==AMX_ERR_SLEEP) { | ||||||
| @@ -2509,6 +2527,8 @@ static const void * const amx_opcodelist[] = { | |||||||
|     amx->hea=hea; |     amx->hea=hea; | ||||||
|     amx->frm=frm; |     amx->frm=frm; | ||||||
|     amx->stk=stk; |     amx->stk=stk; | ||||||
|  |     amx->pri=pri; | ||||||
|  |     amx->alt=alt; | ||||||
|     pri=((AMX_NATIVE)offs)(amx,(cell *)(data+(int)stk)); |     pri=((AMX_NATIVE)offs)(amx,(cell *)(data+(int)stk)); | ||||||
|     if (amx->error!=AMX_ERR_NONE) { |     if (amx->error!=AMX_ERR_NONE) { | ||||||
|       if (amx->error==AMX_ERR_SLEEP) { |       if (amx->error==AMX_ERR_SLEEP) { | ||||||
| @@ -2578,6 +2598,8 @@ static const void * const amx_opcodelist[] = { | |||||||
|       amx->frm=frm; |       amx->frm=frm; | ||||||
|       amx->stk=stk; |       amx->stk=stk; | ||||||
|       amx->hea=hea; |       amx->hea=hea; | ||||||
|  |       amx->pri=pri; | ||||||
|  |       amx->alt=alt; | ||||||
|       amx->cip=(cell)((unsigned char*)cip-code); |       amx->cip=(cell)((unsigned char*)cip-code); | ||||||
|       num=amx->debug(amx); |       num=amx->debug(amx); | ||||||
|       if (num!=AMX_ERR_NONE) { |       if (num!=AMX_ERR_NONE) { | ||||||
| @@ -3445,6 +3467,8 @@ int AMXAPI amx_Exec(AMX *amx, cell *retval, int index) | |||||||
|       amx->hea=hea; |       amx->hea=hea; | ||||||
|       amx->frm=frm; |       amx->frm=frm; | ||||||
|       amx->stk=stk; |       amx->stk=stk; | ||||||
|  |       amx->pri=pri; | ||||||
|  |       amx->alt=alt; | ||||||
|       num=amx->callback(amx,pri,&pri,(cell *)(data+(int)stk)); |       num=amx->callback(amx,pri,&pri,(cell *)(data+(int)stk)); | ||||||
|       if (num!=AMX_ERR_NONE) { |       if (num!=AMX_ERR_NONE) { | ||||||
|         if (num==AMX_ERR_SLEEP) { |         if (num==AMX_ERR_SLEEP) { | ||||||
| @@ -3464,6 +3488,8 @@ int AMXAPI amx_Exec(AMX *amx, cell *retval, int index) | |||||||
|       amx->hea=hea; |       amx->hea=hea; | ||||||
|       amx->frm=frm; |       amx->frm=frm; | ||||||
|       amx->stk=stk; |       amx->stk=stk; | ||||||
|  |       amx->pri=pri; | ||||||
|  |       amx->alt=alt; | ||||||
|       num=amx->callback(amx,offs,&pri,(cell *)(data+(int)stk)); |       num=amx->callback(amx,offs,&pri,(cell *)(data+(int)stk)); | ||||||
|       if (num!=AMX_ERR_NONE) { |       if (num!=AMX_ERR_NONE) { | ||||||
|         if (num==AMX_ERR_SLEEP) { |         if (num==AMX_ERR_SLEEP) { | ||||||
| @@ -3483,6 +3509,8 @@ int AMXAPI amx_Exec(AMX *amx, cell *retval, int index) | |||||||
|       amx->hea=hea; |       amx->hea=hea; | ||||||
|       amx->frm=frm; |       amx->frm=frm; | ||||||
|       amx->stk=stk; |       amx->stk=stk; | ||||||
|  |       amx->pri=pri; | ||||||
|  |       amx->alt=alt; | ||||||
|       pri=((AMX_NATIVE)offs)(amx,(cell *)(data+(int)stk)); |       pri=((AMX_NATIVE)offs)(amx,(cell *)(data+(int)stk)); | ||||||
|       if (amx->error!=AMX_ERR_NONE) { |       if (amx->error!=AMX_ERR_NONE) { | ||||||
|         if (amx->error==AMX_ERR_SLEEP) { |         if (amx->error==AMX_ERR_SLEEP) { | ||||||
| @@ -3546,6 +3574,8 @@ int AMXAPI amx_Exec(AMX *amx, cell *retval, int index) | |||||||
|         amx->frm=frm; |         amx->frm=frm; | ||||||
|         amx->stk=stk; |         amx->stk=stk; | ||||||
|         amx->hea=hea; |         amx->hea=hea; | ||||||
|  |         amx->pri=pri; | ||||||
|  |         amx->alt=alt; | ||||||
|         amx->cip=(cell)((unsigned char*)cip-code); |         amx->cip=(cell)((unsigned char*)cip-code); | ||||||
|         num=amx->debug(amx); |         num=amx->debug(amx); | ||||||
|         if (num!=AMX_ERR_NONE) { |         if (num!=AMX_ERR_NONE) { | ||||||
|   | |||||||
| @@ -242,6 +242,7 @@ typedef struct tagAMX { | |||||||
|   long usertags[AMX_USERNUM] PACKED; |   long usertags[AMX_USERNUM] PACKED; | ||||||
|   //okay userdata[3] in AMX Mod X is for the CPlugin * pointer |   //okay userdata[3] in AMX Mod X is for the CPlugin * pointer | ||||||
|   //we're also gonna set userdata[2] to a special debug structure |   //we're also gonna set userdata[2] to a special debug structure | ||||||
|  |   //lastly, userdata[1] is for opcode_list from amx_BrowseRelocate | ||||||
|   void _FAR *userdata[AMX_USERNUM] PACKED; |   void _FAR *userdata[AMX_USERNUM] PACKED; | ||||||
|   /* native functions can raise an error */ |   /* native functions can raise an error */ | ||||||
|   int error             PACKED; |   int error             PACKED; | ||||||
| @@ -335,23 +336,9 @@ enum { | |||||||
|   #define AMX_COMPACTMARGIN 64 |   #define AMX_COMPACTMARGIN 64 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| struct amx_trace | #define UD_FINDPLUGIN	3 | ||||||
| { | #define UD_DEBUGGER		2 | ||||||
| 	cell frm; | #define UD_OPCODELIST	1 | ||||||
| 	amx_trace *prev; |  | ||||||
| 	amx_trace *next; |  | ||||||
| 	bool used; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct AMX_DBGINFO |  | ||||||
| { |  | ||||||
| 	void *pDebug;			//Pointer to debug data |  | ||||||
| 	int error;				//non-amx_Exec() error setting |  | ||||||
| 	amx_trace *pTrace;		//Pointer to stack trace  |  | ||||||
| 	amx_trace *pTraceFrm; |  | ||||||
| 	amx_trace *pTraceEnd; |  | ||||||
| 	cell frm; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| /* for native functions that use floating point parameters, the following | /* for native functions that use floating point parameters, the following | ||||||
|  * two macros are convenient for casting a "cell" into a "float" type _without_ |  * two macros are convenient for casting a "cell" into a "float" type _without_ | ||||||
|   | |||||||
| @@ -129,6 +129,28 @@ | |||||||
|         jg      near err_stack |         jg      near err_stack | ||||||
| %endmacro | %endmacro | ||||||
|  |  | ||||||
|  | ;Normal abort, saves pri/alt | ||||||
|  | %macro	_ABORT 1 | ||||||
|  | 		mov		ebp,amx | ||||||
|  |         mov     [ebp+_pri], dword eax  ; store values in AMX structure (PRI, ALT) | ||||||
|  |         mov     [ebp+_alt], dword edx  ; store values in AMX structure (PRI, ALT) | ||||||
|  | 		mov		[ebp+_error], dword %1 | ||||||
|  | 		jmp		_return | ||||||
|  | %endmacro | ||||||
|  |  | ||||||
|  | ;Checked abort, saves nothing and uses a conditional | ||||||
|  | %macro	_CHKABORT 1 | ||||||
|  | 		mov		ebp,amx | ||||||
|  | 		mov		[ebp+_error], %1 | ||||||
|  | 		cmp		%1, AMX_ERR_NONE | ||||||
|  | 		jne		_return | ||||||
|  | %endmacro | ||||||
|  |  | ||||||
|  | ;Fast abort, only aborts, nothing else | ||||||
|  | %macro	_FASTABORT 0 | ||||||
|  | 		jmp		_return | ||||||
|  | %endmacro | ||||||
|  |  | ||||||
| %macro  _CHKHEAP 0 | %macro  _CHKHEAP 0 | ||||||
|         mov     ebp,amx |         mov     ebp,amx | ||||||
|         mov     ebp,[ebp+_hlw] |         mov     ebp,[ebp+_hlw] | ||||||
| @@ -1196,8 +1218,7 @@ OP_HALT: | |||||||
|         mov     eax,esi         ; EAX=CIP |         mov     eax,esi         ; EAX=CIP | ||||||
|         sub     eax,code |         sub     eax,code | ||||||
|         mov     [ebp+_cip],eax |         mov     [ebp+_cip],eax | ||||||
|         mov     eax,ebx         ; return the parameter of the HALT opcode |         _ABORT	ebx | ||||||
|         jmp     _return |  | ||||||
|  |  | ||||||
|  |  | ||||||
| OP_BOUNDS: | OP_BOUNDS: | ||||||
| @@ -1221,6 +1242,7 @@ OP_SYSREQ_PRI: | |||||||
|         mov     alt,edx         ; save ALT |         mov     alt,edx         ; save ALT | ||||||
|  |  | ||||||
|         mov     [ebp+_stk],ecx  ; store values in AMX structure (STK, HEA, FRM) |         mov     [ebp+_stk],ecx  ; store values in AMX structure (STK, HEA, FRM) | ||||||
|  |         ;we don't save regs since they're useless after this | ||||||
|         mov     ecx,hea |         mov     ecx,hea | ||||||
|         mov     ebx,frm |         mov     ebx,frm | ||||||
|         mov     [ebp+_hea],ecx |         mov     [ebp+_hea],ecx | ||||||
| @@ -1251,8 +1273,7 @@ OP_SYSREQ_PRI: | |||||||
|         pop     edi             ; restore saved registers |         pop     edi             ; restore saved registers | ||||||
|         pop     esi |         pop     esi | ||||||
|         pop     ebp |         pop     ebp | ||||||
|         cmp     eax,AMX_ERR_NONE |         _CHKABORT eax			; if result was invalid, leave | ||||||
|         jne     near _return    ; return error code, if any |  | ||||||
|  |  | ||||||
|         mov     eax,pri         ; get retval into eax (PRI) |         mov     eax,pri         ; get retval into eax (PRI) | ||||||
|         mov     edx,alt         ; restore ALT |         mov     edx,alt         ; restore ALT | ||||||
| @@ -1293,8 +1314,8 @@ OP_SYSREQ_D:                    ; (TR) | |||||||
|         pop     edi             ; restore saved registers |         pop     edi             ; restore saved registers | ||||||
|         pop     esi |         pop     esi | ||||||
|         pop     ebp |         pop     ebp | ||||||
|         cmp     DWORD [ebp+_error],AMX_ERR_NONE |         mov		eax,[ebp+_error] | ||||||
|         jne     near _return    ; return error code, if any |         _CHKABORT eax | ||||||
|  |  | ||||||
|         ; function result is in eax (PRI) |         ; function result is in eax (PRI) | ||||||
|         mov     edx,alt         ; restore ALT |         mov     edx,alt         ; restore ALT | ||||||
| @@ -1416,7 +1437,7 @@ OP_BREAK: | |||||||
|         mov     [ebp+_error],eax   ; save EAX (error code) before restoring all regs |         mov     [ebp+_error],eax   ; save EAX (error code) before restoring all regs | ||||||
|         _RESTOREREGS               ; abort run, but restore stack first |         _RESTOREREGS               ; abort run, but restore stack first | ||||||
|         mov     eax,[ebp+_error]   ; get error code in EAX back again |         mov     eax,[ebp+_error]   ; get error code in EAX back again | ||||||
|         jmp     _return         ; return error code |         _FASTABORT | ||||||
|     break_noabort: |     break_noabort: | ||||||
|         _RESTOREREGS |         _RESTOREREGS | ||||||
|         mov     eax,[ebp+_pri]  ; restore PRI and ALT |         mov     eax,[ebp+_pri]  ; restore PRI and ALT | ||||||
| @@ -1425,43 +1446,34 @@ OP_BREAK: | |||||||
|  |  | ||||||
|  |  | ||||||
| OP_INVALID: | OP_INVALID: | ||||||
|         mov     eax,AMX_ERR_INVINSTR |         _ABORT	AMX_ERR_INVINSTR | ||||||
|         jmp     _return |  | ||||||
|  |  | ||||||
| err_call: | err_call: | ||||||
|         mov     eax,AMX_ERR_CALLBACK |         _ABORT	AMX_ERR_CALLBACK | ||||||
|         jmp     _return |  | ||||||
|  |  | ||||||
| err_stack: | err_stack: | ||||||
|         mov     eax,AMX_ERR_STACKERR |         _ABORT	AMX_ERR_STACKERR | ||||||
|         jmp     _return |  | ||||||
|  |  | ||||||
| err_stacklow: | err_stacklow: | ||||||
|         mov     eax,AMX_ERR_STACKLOW |         _ABORT	AMX_ERR_STACKLOW | ||||||
|         jmp     _return |  | ||||||
|  |  | ||||||
| err_memaccess: | err_memaccess: | ||||||
|         mov     eax,AMX_ERR_MEMACCESS |         _ABORT	AMX_ERR_MEMACCESS | ||||||
|         jmp     _return |  | ||||||
|  |  | ||||||
| err_bounds: | err_bounds: | ||||||
|         mov     eax,AMX_ERR_BOUNDS |         _ABORT	AMX_ERR_BOUNDS | ||||||
|         jmp     _return |  | ||||||
|  |  | ||||||
| err_heaplow: | err_heaplow: | ||||||
|         mov     eax,AMX_ERR_HEAPLOW |         _ABORT	AMX_ERR_HEAPLOW | ||||||
|         jmp     _return |  | ||||||
|  |  | ||||||
| err_divide: | err_divide: | ||||||
|         mov     eax,AMX_ERR_DIVIDE |         _ABORT	AMX_ERR_DIVIDE | ||||||
|         jmp     _return |  | ||||||
|  |  | ||||||
|  |  | ||||||
| _return: | _return: | ||||||
|         ; save a few parameters, mostly for the "sleep"function |         ; save a few parameters, mostly for the "sleep"function | ||||||
|         mov     ebp,amx         ; get amx into ebp |         mov     ebp,amx         ; get amx into ebp | ||||||
|         mov     [ebp+_pri],eax  ; store values in AMX structure (PRI, ALT) |         mov		[ebp+_cip],esi	; get corrected cip for amxmodx | ||||||
|         mov     [ebp+_alt],edx  ; store values in AMX structure (PRI, ALT) |         mov		eax,[ebp+_error]; get error code | ||||||
|  |  | ||||||
|         pop     esi             ; remove FRM from stack |         pop     esi             ; remove FRM from stack | ||||||
|  |  | ||||||
|   | |||||||
| @@ -80,7 +80,6 @@ extern  AMX_NATIVE_INFO float_Natives[]; | |||||||
| extern  AMX_NATIVE_INFO string_Natives[]; | extern  AMX_NATIVE_INFO string_Natives[]; | ||||||
| extern  AMX_NATIVE_INFO vault_Natives[]; | extern  AMX_NATIVE_INFO vault_Natives[]; | ||||||
|  |  | ||||||
|  |  | ||||||
| #ifndef __linux__ | #ifndef __linux__ | ||||||
| #define DLLOAD(path) (DLHANDLE)LoadLibrary(path) | #define DLLOAD(path) (DLHANDLE)LoadLibrary(path) | ||||||
| #define DLPROC(m,func) GetProcAddress(m,func) | #define DLPROC(m,func) GetProcAddress(m,func) | ||||||
| @@ -133,7 +132,6 @@ struct WeaponsVault { | |||||||
| struct fakecmd_t { | struct fakecmd_t { | ||||||
|   char args[256]; |   char args[256]; | ||||||
|   const char *argv[3]; |   const char *argv[3]; | ||||||
|   //char argv[3][128]; |  | ||||||
|   int argc; |   int argc; | ||||||
|   bool fake; |   bool fake; | ||||||
| }; | }; | ||||||
|   | |||||||
							
								
								
									
										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(); | ||||||
|  | } | ||||||
							
								
								
									
										136
									
								
								amxmodx/debugger.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										136
									
								
								amxmodx/debugger.h
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,136 @@ | |||||||
|  | /* 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. | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #ifndef _INCLUDE_DEBUGGER_H_ | ||||||
|  | #define _INCLUDE_DEBUGGER_H_ | ||||||
|  |  | ||||||
|  | #include "CVector.h" | ||||||
|  | #include "amxdbg.h" | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Third revision of the AMX Mod X Plugin Debugger. | ||||||
|  |  * This final, object oriented version is safe for multiple calls and lets you  | ||||||
|  |  *  fine-tune error handling. | ||||||
|  |  *  -BAILOPAN | ||||||
|  |  */ | ||||||
|  | class Debugger | ||||||
|  | { | ||||||
|  | public: | ||||||
|  | 	class Tracer | ||||||
|  | 	{ | ||||||
|  | 	public: | ||||||
|  | 		struct trace_info | ||||||
|  | 		{ | ||||||
|  | 			trace_info() : cip(0), frm(0), used(false), next(NULL), prev(NULL) | ||||||
|  | 			{ | ||||||
|  | 			}; | ||||||
|  | 			cell cip; | ||||||
|  | 			cell frm; | ||||||
|  | 			trace_info *next; | ||||||
|  | 			trace_info *prev; | ||||||
|  | 			bool used; | ||||||
|  | 		}; | ||||||
|  | 	public: | ||||||
|  | 		Tracer() : m_Error(0), m_pStart(NULL), m_pEnd(NULL), m_Reset(true) { }; | ||||||
|  | 		~Tracer(); | ||||||
|  | 	public: | ||||||
|  | 		void StepI(cell frm, cell cip); | ||||||
|  | 		void Reset(); | ||||||
|  | 		void Clear(); | ||||||
|  | 		Debugger::Tracer::trace_info *GetStart() const; | ||||||
|  | 		Debugger::Tracer::trace_info *GetEnd() const; | ||||||
|  | 	public: | ||||||
|  | 		int m_Error; | ||||||
|  | 	private: | ||||||
|  | 		trace_info *m_pStart; | ||||||
|  | 		trace_info *m_pEnd; | ||||||
|  | 		bool m_Reset; | ||||||
|  | 	}; | ||||||
|  | public: | ||||||
|  | 	Debugger(AMX *pAmx, AMX_DBG *pAmxDbg) :  | ||||||
|  | 	  m_pAmx(pAmx), m_pAmxDbg(pAmxDbg), m_Top(-1) | ||||||
|  | 	  { | ||||||
|  | 		  _CacheAmxOpcodeList(); | ||||||
|  | 	  }; | ||||||
|  | 	~Debugger(); | ||||||
|  | public: | ||||||
|  | 	//Begin a trace for a function | ||||||
|  | 	void BeginExec(); | ||||||
|  |  | ||||||
|  | 	//Step through one instruction | ||||||
|  | 	void StepI(); | ||||||
|  |  | ||||||
|  | 	//Get/set the last traced error | ||||||
|  | 	int GetTracedError(); | ||||||
|  | 	void SetTracedError(int error); | ||||||
|  |  | ||||||
|  | 	//Get the first trace info of the call stack | ||||||
|  | 	Debugger::Tracer::trace_info *GetTraceStart() const; | ||||||
|  |  | ||||||
|  | 	//Get extra info about the call stack | ||||||
|  | 	bool GetTraceInfo(Debugger::Tracer::trace_info *pTraceInfo, long &line, const char *&function, const char *&file); | ||||||
|  |  | ||||||
|  | 	//Get the next trace in the call stack, NULL if none | ||||||
|  | 	Debugger::Tracer::trace_info *GetNextTrace(Debugger::Tracer::trace_info *pTraceInfo); | ||||||
|  |  | ||||||
|  | 	//Returns true if an error exists | ||||||
|  | 	bool ErrorExists(); | ||||||
|  |  | ||||||
|  | 	//Formats the error message into a buffer.   | ||||||
|  | 	//returns length of data copied, or -1 if there is no error. | ||||||
|  | 	int FormatError(char *buffer, size_t maxLength); | ||||||
|  |  | ||||||
|  | 	//End a trace | ||||||
|  | 	void EndExec(); | ||||||
|  |  | ||||||
|  | 	//Reset the internal states as if the debugger was inactive | ||||||
|  | 	void Reset(); | ||||||
|  |  | ||||||
|  | 	//Destroy internal states for shutdown | ||||||
|  | 	void Clear(); | ||||||
|  | public: | ||||||
|  | 	//generic static opcode breaker | ||||||
|  | 	static int AMXAPI DebugHook(AMX *amx); | ||||||
|  | private: | ||||||
|  | 	void _CacheAmxOpcodeList(); | ||||||
|  | 	int _GetOpcodeFromCip(cell cip, cell *&addr); | ||||||
|  | 	cell _CipAsVa(cell cip); | ||||||
|  | public: | ||||||
|  | 	AMX *m_pAmx; | ||||||
|  | 	AMX_DBG *m_pAmxDbg; | ||||||
|  | 	int m_Top; | ||||||
|  | 	cell *m_pOpcodeList; | ||||||
|  | 	CVector<Tracer *> m_pCalls; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | typedef Debugger::Tracer::trace_info trace_info_t; | ||||||
|  |  | ||||||
|  | #endif //_INCLUDE_DEBUGGER_H_ | ||||||
| @@ -42,6 +42,7 @@ | |||||||
| #include "amxdbg.h" | #include "amxdbg.h" | ||||||
| #include "newmenus.h" | #include "newmenus.h" | ||||||
| #include "natives.h" | #include "natives.h" | ||||||
|  | #include "debugger.h" | ||||||
|  |  | ||||||
| CList<CModule,const char*> g_modules; | CList<CModule,const char*> g_modules; | ||||||
| CList<CScript,AMX*> g_loadedscripts; | CList<CScript,AMX*> g_loadedscripts; | ||||||
| @@ -96,166 +97,6 @@ void free_amxmemory(void **ptr) | |||||||
| 	*ptr = 0; | 	*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) | int load_amxscript(AMX *amx, void **program, const char *filename, char error[64], int debug) | ||||||
| { | { | ||||||
| 	*error = 0; | 	*error = 0; | ||||||
| @@ -368,23 +209,19 @@ int load_amxscript(AMX *amx, void **program, const char *filename, char error[64 | |||||||
| 		return (amx->error = AMX_ERR_INIT); | 		return (amx->error = AMX_ERR_INIT); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	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) | 	if (will_be_debugged) | ||||||
| 	{ | 	{ | ||||||
| #ifdef JIT |  | ||||||
| 		amx->flags |= AMX_FLAG_DEBUG; | 		amx->flags |= AMX_FLAG_DEBUG; | ||||||
| #endif | 		amx->flags &= (~AMX_FLAG_JITC); | ||||||
| 		amx_SetDebugHook(amx, amxx_DebugHook); | 		amx_SetDebugHook(amx, &Debugger::DebugHook); | ||||||
|  |  | ||||||
|  | 		Debugger *pDebugger = new Debugger(amx, pDbg); | ||||||
|  | 		amx->userdata[UD_DEBUGGER] = pDebugger; | ||||||
| 	} else { | 	} else { | ||||||
| 		//set this again because amx_Init() erases it! |  | ||||||
| #ifdef JIT | #ifdef JIT | ||||||
|  | 		//set this again because amx_Init() erases it! | ||||||
| 		amx->flags |= AMX_FLAG_JITC; | 		amx->flags |= AMX_FLAG_JITC; | ||||||
|  | 		amx->flags &= (~AMX_FLAG_DEBUG); | ||||||
| 		amx->sysreq_d = NULL; | 		amx->sysreq_d = NULL; | ||||||
| #endif | #endif | ||||||
| 	} | 	} | ||||||
| @@ -594,7 +431,9 @@ int set_amxnatives(AMX* amx,char error[128]) | |||||||
| int unload_amxscript(AMX* amx, void** program) | int unload_amxscript(AMX* amx, void** program) | ||||||
| { | { | ||||||
| 	int flags = amx->flags; | 	int flags = amx->flags; | ||||||
| 	amxx_FreeDebug(amx); | 	Debugger *pDebugger = (Debugger *)amx->userdata[UD_DEBUGGER]; | ||||||
|  | 	if (pDebugger) | ||||||
|  | 		delete pDebugger; | ||||||
| 	CList<CScript,AMX*>::iterator a = g_loadedscripts.find( amx  ); | 	CList<CScript,AMX*>::iterator a = g_loadedscripts.find( amx  ); | ||||||
| 	if ( a ) a.remove(); | 	if ( a ) a.remove(); | ||||||
| 	char *prg = (char *)*program; | 	char *prg = (char *)*program; | ||||||
| @@ -1443,137 +1282,72 @@ void MNF_Log(const char *fmt, ...) | |||||||
| 	AMXXLOG_Log("%s", msg); | 	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 |  | ||||||
| //  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", |  | ||||||
| 		"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 = ""; |  | ||||||
| 	else |  | ||||||
| 		geterr = amx_errs[err]; |  | ||||||
| 	return geterr; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| //by BAILOPAN | //by BAILOPAN | ||||||
| // debugger engine front end | // debugger engine front end | ||||||
| void LogError(AMX *amx, int err, const char *fmt, ...) | void LogError(AMX *amx, int err, const char *fmt, ...) | ||||||
| { | { | ||||||
| 	//does this plugin have debug info? | 	Debugger *pDebugger = (Debugger *)amx->userdata[UD_DEBUGGER]; | ||||||
| 	va_list arg; |  | ||||||
| 	AMX_DBGINFO *pInfo = (AMX_DBGINFO *)(amx->userdata[2]); |  | ||||||
| 	const char *name = get_amxscriptname(amx); |  | ||||||
| 	static char buf[1024]; |  | ||||||
| 	static char vbuf[1024]; |  | ||||||
| 	*buf = 0; |  | ||||||
| 	*vbuf = 0; |  | ||||||
|  |  | ||||||
| 	if (fmt[0] == '\0') | 	amx->error = err; | ||||||
|  | 	CPluginMngr::CPlugin *pl = g_plugins.findPluginFast(amx); | ||||||
|  |  | ||||||
|  | 	const char *filename = ""; | ||||||
|  | 	if (pl) | ||||||
| 	{ | 	{ | ||||||
| 		_snprintf(vbuf, sizeof(vbuf)-1, "Run time error %d (%s)", err, GenericError(err)); | 		filename = pl->getName(); | ||||||
| 	} else { | 	} else { | ||||||
| 		va_start(arg, fmt); | 		CList<CScript,AMX*>::iterator a = g_loadedscripts.find(amx); | ||||||
| 		vsprintf(vbuf, fmt, arg); | 		if (a) | ||||||
| 		va_end(arg); | 			filename = (*a).getName(); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	bool invalidate = false; | 	static char msg_buffer[4096]; | ||||||
| 	AMXXLOG_Log("[AMXX] %s", vbuf); | 	if (fmt != NULL) | ||||||
| 	if (!pInfo || !(amx->flags & AMX_FLAG_DEBUG) || !pInfo->pDebug) |  | ||||||
| 	{ | 	{ | ||||||
|  | 		va_list ap; | ||||||
|  | 		va_start(ap, fmt); | ||||||
|  | 		_vsnprintf(msg_buffer, sizeof(msg_buffer)-1, fmt, ap); | ||||||
|  | 		va_end(ap); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 		AMXXLOG_Log("[AMXX] Debug is not enabled (plugin \"%s\")", name); | 	if (fmt != NULL) | ||||||
| 		invalidate = true; | 		AMXXLOG_Log("%s", msg_buffer); | ||||||
| 	} 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\")", name); | 	if (!pDebugger) | ||||||
|  | 	{ | ||||||
|  | 		//give the module's error first.  makes the report look nicer. | ||||||
|  | 		AMXXLOG_Log("[AMXX] Run time error %d (plugin \"%s\") - debug not enabled!", err, filename); | ||||||
|  | 		AMXXLOG_Log("[AMXX] To enable debug mode, add \"debug\" after the plugin name in plugins.ini (without quotes)."); | ||||||
|  | 		//destroy original error code so the original is not displayed again | ||||||
|  | 		amx->error = -1; | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	pDebugger->SetTracedError(err); | ||||||
|  |  | ||||||
|  | 	char buffer[512]; | ||||||
|  | 	pDebugger->FormatError(buffer, sizeof(buffer)-1); | ||||||
|  |  | ||||||
|  | 	AMXXLOG_Log("[AMXX] Displaying debug trace (plugin \"%s\")", filename); | ||||||
|  | 	AMXXLOG_Log("[AMXX] %s", buffer); | ||||||
|  | 	 | ||||||
|  | 	int count = 0; | ||||||
|  | 	long lLine; | ||||||
|  | 	const char *file, *function; | ||||||
|  | 	trace_info_t *pTrace = pDebugger->GetTraceStart(); | ||||||
| 	while (pTrace) | 	while (pTrace) | ||||||
| 	{ | 	{ | ||||||
| 			frame = pTrace->frm; | 		pDebugger->GetTraceInfo(pTrace, lLine, function, file); | ||||||
|  | 		AMXXLOG_Log( | ||||||
| 			if (amxx_GetPluginData(amx, frame, line, filename, function)) | 			"[AMXX]    [%d] %s::%s (line %d)", | ||||||
| 			{ | 			count, | ||||||
| 				//line seems to be 1 off o_O | 			file, | ||||||
| 				iLine = static_cast<int>(line) + 1; | 			function, | ||||||
| 				AMXXLOG_Log("[AMXX]    [%d] %s::%s (line %d)",  | 			(int)(lLine + 1) | ||||||
| 						i,  |  | ||||||
| 						filename?filename:"", |  | ||||||
| 						function?function:"", |  | ||||||
| 						iLine |  | ||||||
| 			); | 			); | ||||||
|  | 		count++; | ||||||
|  | 		pTrace = pDebugger->GetNextTrace(pTrace); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 			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) | void MNF_MergeDefinitionFile(const char *file) | ||||||
|   | |||||||
| @@ -42,12 +42,13 @@ | |||||||
| 			<Tool | 			<Tool | ||||||
| 				Name="VCLinkerTool" | 				Name="VCLinkerTool" | ||||||
| 				AdditionalOptions="/MACHINE:I386" | 				AdditionalOptions="/MACHINE:I386" | ||||||
| 				AdditionalDependencies="odbc32.lib odbccp32.lib ..\zlib\zlib.lib" | 				AdditionalDependencies="..\JIT\natives-x86.obj ..\zlib\zlib.lib" | ||||||
| 				OutputFile="debug/amxmodx_mm.dll" | 				OutputFile="debug/amxmodx_mm.dll" | ||||||
| 				Version="0.1" | 				Version="0.1" | ||||||
| 				LinkIncremental="1" | 				LinkIncremental="1" | ||||||
| 				SuppressStartupBanner="TRUE" | 				SuppressStartupBanner="TRUE" | ||||||
| 				AdditionalLibraryDirectories="..\extra\lib_win32" | 				AdditionalLibraryDirectories="..\extra\lib_win32" | ||||||
|  | 				IgnoreDefaultLibraryNames="MSVCRT" | ||||||
| 				ModuleDefinitionFile="" | 				ModuleDefinitionFile="" | ||||||
| 				GenerateDebugInformation="TRUE" | 				GenerateDebugInformation="TRUE" | ||||||
| 				ProgramDatabaseFile=".\debug/amxx_mm.pdb" | 				ProgramDatabaseFile=".\debug/amxx_mm.pdb" | ||||||
| @@ -639,6 +640,9 @@ | |||||||
| 			<File | 			<File | ||||||
| 				RelativePath="..\CVault.cpp"> | 				RelativePath="..\CVault.cpp"> | ||||||
| 			</File> | 			</File> | ||||||
|  | 			<File | ||||||
|  | 				RelativePath="..\debugger.cpp"> | ||||||
|  | 			</File> | ||||||
| 			<File | 			<File | ||||||
| 				RelativePath="..\emsg.cpp"> | 				RelativePath="..\emsg.cpp"> | ||||||
| 			</File> | 			</File> | ||||||
| @@ -797,6 +801,9 @@ | |||||||
| 			<File | 			<File | ||||||
| 				RelativePath="..\CVector.h"> | 				RelativePath="..\CVector.h"> | ||||||
| 			</File> | 			</File> | ||||||
|  | 			<File | ||||||
|  | 				RelativePath="..\debugger.h"> | ||||||
|  | 			</File> | ||||||
| 			<File | 			<File | ||||||
| 				RelativePath="..\fakemeta.h"> | 				RelativePath="..\fakemeta.h"> | ||||||
| 			</File> | 			</File> | ||||||
|   | |||||||
| @@ -27,8 +27,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US | |||||||
| // | // | ||||||
|  |  | ||||||
| VS_VERSION_INFO VERSIONINFO | VS_VERSION_INFO VERSIONINFO | ||||||
|  FILEVERSION 1,5,5,0 |  FILEVERSION 1,5,6,0 | ||||||
|  PRODUCTVERSION 1,5,5,0 |  PRODUCTVERSION 1,5,6,0 | ||||||
|  FILEFLAGSMASK 0x17L |  FILEFLAGSMASK 0x17L | ||||||
| #ifdef _DEBUG | #ifdef _DEBUG | ||||||
|  FILEFLAGS 0x1L |  FILEFLAGS 0x1L | ||||||
| @@ -45,12 +45,12 @@ BEGIN | |||||||
|         BEGIN |         BEGIN | ||||||
|             VALUE "Comments", "AMX Mod X" |             VALUE "Comments", "AMX Mod X" | ||||||
|             VALUE "FileDescription", "AMX Mod X" |             VALUE "FileDescription", "AMX Mod X" | ||||||
|             VALUE "FileVersion", "1.55" |             VALUE "FileVersion", "1.56" | ||||||
|             VALUE "InternalName", "amxmodx" |             VALUE "InternalName", "amxmodx" | ||||||
|             VALUE "LegalCopyright", "Copyright (c) 2004-2005, AMX Mod X Dev Team" |             VALUE "LegalCopyright", "Copyright (c) 2004-2005, AMX Mod X Dev Team" | ||||||
|             VALUE "OriginalFilename", "amxmodx_mm.dll" |             VALUE "OriginalFilename", "amxmodx_mm.dll" | ||||||
|             VALUE "ProductName", "AMX Mod X" |             VALUE "ProductName", "AMX Mod X" | ||||||
|             VALUE "ProductVersion", "1.55" |             VALUE "ProductVersion", "1.56" | ||||||
|         END |         END | ||||||
|     END |     END | ||||||
|     BLOCK "VarFileInfo" |     BLOCK "VarFileInfo" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user