From a7fe40899597d4132d4b9bb91c1c29f7f45c0ea0 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sun, 31 Dec 2006 22:33:47 +0000 Subject: [PATCH] debug break now uses context struct instead of context interface err is renamed to 'n_err' 'n_err' is now a 'native only' member, for native errors only --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40242 --- sourcepawn/include/sp_vm_api.h | 126 +++++++++++++++++++++++--- sourcepawn/include/sp_vm_base.h | 2 + sourcepawn/include/sp_vm_types.h | 6 +- sourcepawn/jit/x86/jit_x86.cpp | 42 +++++---- sourcepawn/jit/x86/opcode_helpers.cpp | 36 ++++---- sourcepawn/jit/x86/opcode_helpers.h | 4 +- 6 files changed, 165 insertions(+), 51 deletions(-) diff --git a/sourcepawn/include/sp_vm_api.h b/sourcepawn/include/sp_vm_api.h index 73db0195..a144d6d7 100644 --- a/sourcepawn/include/sp_vm_api.h +++ b/sourcepawn/include/sp_vm_api.h @@ -314,6 +314,67 @@ namespace SourcePawn virtual int Execute(uint32_t funcid, cell_t *result) =0; }; + struct ErrorTraceInfo + { + const char *filename; + unsigned int line; + const char *function; + }; + + class IContextErrorInfo + { + /** + * @brief Returns the integer error code. + * + * @return Integer error code. + */ + virtual int GetErrorCode() =0; + + /** + * @brief Returns a string describing the error. + * + * @return Error string. + */ + virtual const char *GetErrorString() =0; + + /** + * @brief Returns whether debug info is available. + * + * @return True if debug info is available, false otherwise. + */ + virtual bool DebugInfoAvailable() =0; + + /** + * @brief Returns a custom error message. + * + * @return A pointer to a custom error message, or NULL otherwise. + */ + virtual const char *GetCustomErrorString() =0; + + /** + * @brief Returns the number of calls in the call backtrace. + * NOTE: Tracers are ordered from 0 to N-1, where 0 is the top of the trace. + * + * @return Number of calls in the trace. + */ + virtual unsigned int TraceCallCount() =0; + + /** + * @brief Returns trace info for a specific point in the backtrace. + * + * @param call The call trace index (from 0 to N-1). + * @param trace An ErrorTraceInfo buffer to store information. + * @return True if successful, false otherwise. + */ + virtual bool GetTraceInfo(unsigned int call, ErrorTraceInfo *trace) =0; + }; + + class IDebugListener + { + public: + virtual void OnContextExecuteError(IPluginContext *ctx, IContextErrorInfo *error) =0; + }; + /** * @brief Contains helper functions used by VMs and the host app */ @@ -321,17 +382,17 @@ namespace SourcePawn { public: /** - * Loads a named file from a file pointer. - * Using this means base memory will be allocated by the VM. - * - * @param fp File pointer. May be at any offset. Not closed on return. - * @param err Optional error code pointer. - * @return A new plugin structure. - */ + * @brief Loads a named file from a file pointer. + * Note: Using this means the memory will be allocated by the VM. + * + * @param fp File pointer. May be at any offset. Not closed on return. + * @param err Optional error code pointer. + * @return A new plugin structure. + */ virtual sp_plugin_t *LoadFromFilePointer(FILE *fp, int *err) =0; /** - * Loads a file from a base memory address. + * @brief Loads a file from a base memory address. * * @param base Base address of the plugin's memory region. * @param plugin If NULL, a new plugin pointer is returned. @@ -349,7 +410,7 @@ namespace SourcePawn virtual int FreeFromMemory(sp_plugin_t *plugin) =0; /** - * Creates a new IContext from a context handle. + * @brief Creates a new IContext from a context handle. * * @param ctx Context to use as a basis for the IPluginContext. * @return New IPluginContext handle. @@ -357,14 +418,14 @@ namespace SourcePawn virtual IPluginContext *CreateBaseContext(sp_context_t *ctx) =0; /** - * Frees a base context. Does not free the sp_context_t it holds. + * @brief Frees a base context. Does not free the sp_context_t it holds. * * @param ctx Context pointer to free. */ virtual void FreeBaseContext(IPluginContext *ctx) =0; /** - * Allocates large blocks of temporary memory. + * @brief Allocates large blocks of temporary memory. * * @param size Size of memory to allocate. * @return Pointer to memory, NULL if allocation failed. @@ -372,14 +433,14 @@ namespace SourcePawn virtual void *BaseAlloc(size_t size) =0; /** - * Frees memory allocated with BaseAlloc. + * @brief Frees memory allocated with BaseAlloc. * * @param memory Memory address to free. */ virtual void BaseFree(void *memory) =0; /** - * Allocates executable memory. + * @brief Allocates executable memory. * * @param size Size of memory to allocate. * @return Pointer to memory, NULL if allocation failed. @@ -387,13 +448,50 @@ namespace SourcePawn virtual void *ExecAlloc(size_t size) =0; /** - * Frees executable memory. + * @brief Frees executable memory. * * @param address Address to free. */ virtual void ExecFree(void *address) =0; + + /** + * @brief Sets the debug listener. + * + * @param listener Pointer to an IDebugListener. + * @return Old IDebugListener, or NULL if none. + */ + virtual IDebugListener *SetDebugListener(IDebugListener *pListener) =0; + + /** + * @brief Returns the number of plugins on the call stack. + * + * @return Number of contexts in the call stack. + */ + virtual unsigned int GetContextCallCount() =0; + + /** + * @brief Throws an error and halts any current execution. + * + * @param error The error number to set. + * @param msg Custom error message format. NULL to use default. + * @param ... Message format arguments, if any. + */ + virtual void ThrowNativeErrorEx(int error, const char *msg, ...) =0; + + /** + * @brief Throws a native error and halts any current execution. + * NOTE: This is a wrapper around ThrowError() for convenience. + * + * @param msg Custom error message format. NULL to set no message. + * @param ... Message format arguments, if any. + * @return 0 for convenience. + */ + virtual void ThrowNativeError(const char *msg, ...) =0; }; + /** + * @brief Dummy class for encapsulating private compilation data. + */ class ICompilation { public: diff --git a/sourcepawn/include/sp_vm_base.h b/sourcepawn/include/sp_vm_base.h index 63dc63b9..4fff0f4c 100644 --- a/sourcepawn/include/sp_vm_base.h +++ b/sourcepawn/include/sp_vm_base.h @@ -3,6 +3,8 @@ #include +/* :TODO: rename this to sp_vm_linkage.h */ + #if defined WIN32 #define EXPORT_LINK extern "C" __declspec(dllexport) #else if defined __GNUC__ diff --git a/sourcepawn/include/sp_vm_types.h b/sourcepawn/include/sp_vm_types.h index e2f94114..4bb95964 100644 --- a/sourcepawn/include/sp_vm_types.h +++ b/sourcepawn/include/sp_vm_types.h @@ -37,6 +37,7 @@ typedef uint32_t funcid_t; #define SP_ERROR_TRACKER_BOUNDS 20 /* Tracker stack is out of bounds */ #define SP_ERROR_INVALID_NATIVE 21 /* Native was pending or invalid */ #define SP_ERROR_PARAMS_MAX 22 /* Maximum number of parameters reached */ +#define SP_ERROR_NATIVE 23 /* Error originates from a native */ /********************************************** *** The following structures are reference structures. @@ -204,7 +205,7 @@ typedef struct sp_debug_symbol_s * [1] - frm * [2] - cip */ -typedef int (*SPVM_DEBUGBREAK)(SourcePawn::IPluginContext *, uint32_t, uint32_t); +typedef int (*SPVM_DEBUGBREAK)(struct sp_context_s *, uint32_t, uint32_t); #define SPFLAG_PLUGIN_DEBUG (1<<0) /* plugin is in debug mode */ @@ -234,8 +235,9 @@ typedef struct sp_context_s cell_t hp; /* heap pointer */ cell_t sp; /* stack pointer */ cell_t frm; /* frame pointer */ - int32_t err; /* error code */ uint32_t pushcount; /* push count */ + int32_t n_err; /* error code set by a native */ + uint32_t n_idx; /* current native index being executed */ /* context rebased database */ sp_public_t *publics; /* public functions table */ sp_pubvar_t *pubvars; /* public variables table */ diff --git a/sourcepawn/jit/x86/jit_x86.cpp b/sourcepawn/jit/x86/jit_x86.cpp index 3cafd163..593567ce 100644 --- a/sourcepawn/jit/x86/jit_x86.cpp +++ b/sourcepawn/jit/x86/jit_x86.cpp @@ -1631,7 +1631,7 @@ inline void WriteOp_Sysreq_N(JitWriter *jit) //cmp [ecx+err], 0 //jnz :error IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_CONTEXT); - IA32_Cmp_Rm_Disp8_Imm8(jit, AMX_REG_TMP, offsetof(sp_context_t, err), 0); + IA32_Cmp_Rm_Disp8_Imm8(jit, AMX_REG_TMP, offsetof(sp_context_t, n_err), 0); IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, data->jit_extern_error); /* restore what we damaged */ @@ -1678,12 +1678,14 @@ inline void WriteOp_Tracker_Push_C(JitWriter *jit) IA32_Write_Jump32_Abs(jit, call, JIT_VerifyOrAllocateTracker); /* Check for errors */ - //pop eax - //cmp [eax+err], 0 + //cmp eax, 0 //jnz :error + IA32_Cmp_Rm_Imm32(jit, MOD_REG, REG_EAX, 0); + IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, data->jit_return); + + /* Restore */ + //pop eax IA32_Pop_Reg(jit, REG_EAX); - IA32_Cmp_Rm_Disp8_Imm8(jit, REG_EAX, offsetof(sp_context_t, err), 0); - IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, data->jit_error_tracker_bounds); /* Push the value into the stack and increment pCur */ //mov edx, [eax+vm[]] @@ -1722,12 +1724,14 @@ inline void WriteOp_Tracker_Pop_SetHeap(JitWriter *jit) IA32_Write_Jump32_Abs(jit, call, JIT_VerifyLowBoundTracker); /* Check for errors */ - //pop eax - //cmp [eax+err], 0 + //cmp eax, 0 //jnz :error + IA32_Cmp_Rm_Imm32(jit, MOD_REG, REG_EAX, 0); + IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, data->jit_return); + + /* Restore */ + //pop eax IA32_Pop_Reg(jit, REG_EAX); - IA32_Cmp_Rm_Disp8_Imm8(jit, REG_EAX, offsetof(sp_context_t, err), 0); - IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, data->jit_error_tracker_bounds); /* Pop the value from the stack and decrease the heap by it*/ //mov edx, [eax+vm[]] @@ -1768,11 +1772,13 @@ inline void WriteOp_Stradjust_Pri(JitWriter *jit) cell_t NativeCallback(sp_context_t *ctx, ucell_t native_idx, cell_t *params) { sp_native_t *native = &ctx->natives[native_idx]; + + ctx->n_idx = native_idx; /* Technically both aren't needed, I guess */ if (native->status == SP_NATIVE_UNBOUND) { - ctx->err = SP_ERROR_INVALID_NATIVE; + ctx->n_err = SP_ERROR_INVALID_NATIVE; return 0; } @@ -1782,7 +1788,7 @@ cell_t NativeCallback(sp_context_t *ctx, ucell_t native_idx, cell_t *params) static cell_t InvalidNative(IPluginContext *pCtx, const cell_t *params) { sp_context_t *ctx = pCtx->GetContext(); - ctx->err = SP_ERROR_INVALID_NATIVE; + ctx->n_err = SP_ERROR_INVALID_NATIVE; return 0; } @@ -1792,37 +1798,39 @@ cell_t NativeCallback_Debug(sp_context_t *ctx, ucell_t native_idx, cell_t *param cell_t save_sp = ctx->sp; cell_t save_hp = ctx->hp; + ctx->n_idx = native_idx; + if (ctx->hp < ctx->heap_base) { - ctx->err = SP_ERROR_HEAPMIN; + ctx->n_err = SP_ERROR_HEAPMIN; return 0; } if (ctx->hp + STACK_MARGIN > ctx->sp) { - ctx->err = SP_ERROR_STACKLOW; + ctx->n_err = SP_ERROR_STACKLOW; return 0; } if ((uint32_t)ctx->sp >= ctx->mem_size) { - ctx->err = SP_ERROR_STACKMIN; + ctx->n_err = SP_ERROR_STACKMIN; return 0; } cell_t result = NativeCallback(ctx, native_idx, params); - if (ctx->err != SP_ERROR_NONE) + if (ctx->n_err != SP_ERROR_NONE) { return result; } if (save_sp != ctx->sp) { - ctx->err = SP_ERROR_STACKLEAK; + ctx->n_err = SP_ERROR_STACKLEAK; return result; } else if (save_hp != ctx->hp) { - ctx->err = SP_ERROR_HEAPLEAK; + ctx->n_err = SP_ERROR_HEAPLEAK; return result; } diff --git a/sourcepawn/jit/x86/opcode_helpers.cpp b/sourcepawn/jit/x86/opcode_helpers.cpp index 6b366ade..c7609032 100644 --- a/sourcepawn/jit/x86/opcode_helpers.cpp +++ b/sourcepawn/jit/x86/opcode_helpers.cpp @@ -146,7 +146,7 @@ void Write_BreakDebug(JitWriter *jit) //add esp, 8 //popad IA32_Push_Rm_Disp8(jit, AMX_REG_INFO, AMX_INFO_FRAME); //:TODO: move to regs and push? and dont disp for 0 - IA32_Push_Rm_Disp8(jit, AMX_REG_TMP, offsetof(sp_context_t, context)); + IA32_Push_Reg(jit, AMX_REG_TMP); IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_TMP, offsetof(sp_context_t, dbreak)); IA32_Call_Reg(jit, AMX_REG_TMP); IA32_Add_Rm_Imm8(jit, REG_ESP, 4*2, MOD_REG); @@ -166,7 +166,7 @@ void Write_GetError(JitWriter *jit) //mov eax, [eax+ctx.error] //jmp [jit_return] IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_CONTEXT); - IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EAX, offsetof(sp_context_t, err)); + IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EAX, offsetof(sp_context_t, n_err)); IA32_Jump_Imm32_Abs(jit, data->jit_return); } @@ -435,7 +435,7 @@ void WriteOp_Sysreq_C_Function(JitWriter *jit) //cmp [ecx+err], 0 //jnz :error IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_CONTEXT); - IA32_Cmp_Rm_Disp8_Imm8(jit, AMX_REG_TMP, offsetof(sp_context_t, err), 0); + IA32_Cmp_Rm_Disp8_Imm8(jit, AMX_REG_TMP, offsetof(sp_context_t, n_err), 0); IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, data->jit_extern_error); /* restore what we damaged */ @@ -662,7 +662,7 @@ void WriteOp_Sysreq_N_Function(JitWriter *jit) //cmp [ecx+err], 0 //jnz :error IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_CONTEXT); - IA32_Cmp_Rm_Disp8_Imm8(jit, AMX_REG_TMP, offsetof(sp_context_t, err), 0); + IA32_Cmp_Rm_Disp8_Imm8(jit, AMX_REG_TMP, offsetof(sp_context_t, n_err), 0); IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, data->jit_extern_error); /* restore what we damaged */ @@ -713,13 +713,15 @@ void WriteOp_Tracker_Push_Reg(JitWriter *jit, uint8_t reg) IA32_Write_Jump32_Abs(jit, call, JIT_VerifyOrAllocateTracker); /* Check for errors */ - //pop eax - //cmp [eax+err], 0 + //cmp eax, 0 //jnz :error - IA32_Pop_Reg(jit, REG_EAX); - IA32_Cmp_Rm_Disp8_Imm8(jit, REG_EAX, offsetof(sp_context_t, err), 0); + IA32_Cmp_Rm_Imm32(jit, MOD_REG, REG_EAX, 0); IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, data->jit_error_tracker_bounds); + /* Restore */ + //pop eax + IA32_Pop_Reg(jit, REG_EAX); + /* Push the register into the stack and increment pCur */ //mov edx, [eax+vm[]] //mov eax, [edx+pcur] @@ -742,14 +744,13 @@ void WriteOp_Tracker_Push_Reg(JitWriter *jit, uint8_t reg) IA32_Pop_Reg(jit, AMX_REG_PRI); } -void JIT_VerifyOrAllocateTracker(sp_context_t *ctx) +int JIT_VerifyOrAllocateTracker(sp_context_t *ctx) { tracker_t *trk = (tracker_t *)(ctx->vm[JITVARS_TRACKER]); if ((size_t)(trk->pCur - trk->pBase) >= trk->size) { - ctx->err = SP_ERROR_TRACKER_BOUNDS; - return; + return SP_ERROR_TRACKER_BOUNDS; } if (trk->pCur+1 - (trk->pBase + trk->size) == 0) @@ -760,20 +761,23 @@ void JIT_VerifyOrAllocateTracker(sp_context_t *ctx) if (!trk->pBase) { - ctx->err = SP_ERROR_TRACKER_BOUNDS; - return; + return SP_ERROR_TRACKER_BOUNDS; } trk->pCur = trk->pBase + disp; } + + return SP_ERROR_NONE; } -void JIT_VerifyLowBoundTracker(sp_context_t *ctx) +int JIT_VerifyLowBoundTracker(sp_context_t *ctx) { tracker_t *trk = (tracker_t *)(ctx->vm[JITVARS_TRACKER]); if (trk->pCur <= trk->pBase) { - ctx->err = SP_ERROR_TRACKER_BOUNDS; + return SP_ERROR_TRACKER_BOUNDS; } -} \ No newline at end of file + + return SP_ERROR_NONE; +} diff --git a/sourcepawn/jit/x86/opcode_helpers.h b/sourcepawn/jit/x86/opcode_helpers.h index f8045c16..4ee72424 100644 --- a/sourcepawn/jit/x86/opcode_helpers.h +++ b/sourcepawn/jit/x86/opcode_helpers.h @@ -69,8 +69,8 @@ void Macro_PushN(JitWriter *jit, int i); /** * Bound checking for the tracker stack, */ -void JIT_VerifyLowBoundTracker(sp_context_t *ctx); -void JIT_VerifyOrAllocateTracker(sp_context_t *ctx); +int JIT_VerifyLowBoundTracker(sp_context_t *ctx); +int JIT_VerifyOrAllocateTracker(sp_context_t *ctx); /** * Writes the push into tracker function.