1.1 --- a/core/interfaces/IHandleSys.h Mon Jan 01 01:08:36 2007 +0000
1.2 +++ b/core/interfaces/IHandleSys.h Mon Jan 01 01:09:53 2007 +0000
1.3 @@ -100,7 +100,7 @@
1.4 *
1.5 * @param name Name of handle type (NULL or "" to be anonymous)
1.6 * @param dispatch Pointer to a valid IHandleTypeDispatch object.
1.7 - * @return A new HandleType_t unique ID.
1.8 + * @return A new HandleType_t unique ID, or 0 on failure.
1.9 */
1.10 virtual HandleType_t CreateType(const char *name,
1.11 IHandleTypeDispatch *dispatch) =0;
1.12 @@ -116,7 +116,7 @@
1.13 * @param security Pointer to a temporary HandleSecurity object, NULL to use default
1.14 * or inherited permissions.
1.15 * @param ident Security token for any permissions.
1.16 - * @return A new HandleType_t unique ID.
1.17 + * @return A new HandleType_t unique ID, or 0 on failure.
1.18 */
1.19 virtual HandleType_t CreateTypeEx(const char *name,
1.20 IHandleTypeDispatch *dispatch,
2.1 --- a/core/msvc8/sourcemod_mm.vcproj Mon Jan 01 01:08:36 2007 +0000
2.2 +++ b/core/msvc8/sourcemod_mm.vcproj Mon Jan 01 01:09:53 2007 +0000
2.3 @@ -488,10 +488,6 @@
2.4 Name="Header Files"
2.5 >
2.6 <File
2.7 - RelativePath="..\..\sourcepawn\include\sp_typeutil.h"
2.8 - >
2.9 - </File>
2.10 - <File
2.11 RelativePath="..\vm\sp_vm_basecontext.h"
2.12 >
2.13 </File>
2.14 @@ -520,6 +516,10 @@
2.15 >
2.16 </File>
2.17 <File
2.18 + RelativePath="..\..\sourcepawn\include\sp_typeutil.h"
2.19 + >
2.20 + </File>
2.21 + <File
2.22 RelativePath="..\..\sourcepawn\include\sp_vm_api.h"
2.23 >
2.24 </File>
2.25 @@ -531,10 +531,6 @@
2.26 RelativePath="..\..\sourcepawn\include\sp_vm_types.h"
2.27 >
2.28 </File>
2.29 - <File
2.30 - RelativePath="..\..\sourcepawn\include\sp_vm_typeutil.h"
2.31 - >
2.32 - </File>
2.33 </Filter>
2.34 </Filter>
2.35 </Files>
3.1 --- a/core/vm/sp_vm_basecontext.cpp Mon Jan 01 01:08:36 2007 +0000
3.2 +++ b/core/vm/sp_vm_basecontext.cpp Mon Jan 01 01:09:53 2007 +0000
3.3 @@ -1,18 +1,32 @@
3.4 #include <string.h>
3.5 +#include <stdarg.h>
3.6 #include <assert.h>
3.7 #include <limits.h>
3.8 #include "sp_vm_api.h"
3.9 #include "sp_vm_basecontext.h"
3.10 +#include "sp_vm_engine.h"
3.11
3.12 using namespace SourcePawn;
3.13
3.14 +extern SourcePawnEngine g_SourcePawn;
3.15 +
3.16 #define CELLBOUNDMAX (INT_MAX/sizeof(cell_t))
3.17 #define STACKMARGIN ((cell_t)(16*sizeof(cell_t)))
3.18
3.19 +int GlobalDebugBreak(sp_context_t *ctx, uint32_t frm, uint32_t cip)
3.20 +{
3.21 + g_SourcePawn.RunTracer(ctx, frm, cip);
3.22 +
3.23 + return SP_ERROR_NONE;
3.24 +}
3.25 +
3.26 BaseContext::BaseContext(sp_context_t *_ctx)
3.27 {
3.28 ctx = _ctx;
3.29 ctx->context = this;
3.30 + ctx->dbreak = GlobalDebugBreak;
3.31 + m_InExec = false;
3.32 + m_CustomMsg = false;
3.33 }
3.34
3.35 IVirtualMachine *BaseContext::GetVirtualMachine()
3.36 @@ -74,11 +88,26 @@
3.37 cell_t save_sp = ctx->sp;
3.38 cell_t save_hp = ctx->hp;
3.39
3.40 + bool wasExec = m_InExec;
3.41 +
3.42 + /* Clear the error state, if any */
3.43 + ctx->n_err = SP_ERROR_NONE;
3.44 + ctx->n_idx = 0;
3.45 + m_InExec = true;
3.46 + m_MsgCache[0] = '\0';
3.47 + m_CustomMsg = false;
3.48 +
3.49 + g_SourcePawn.PushTracer(ctx);
3.50 +
3.51 err = vm->ContextExecute(ctx, code_addr, result);
3.52 -
3.53 +
3.54 + m_InExec = wasExec;
3.55 +
3.56 /**
3.57 - * :TODO: turn this into an error check
3.58 + * :TODO: Calling from a plugin in here will erase the cached message...
3.59 + * Should that be documented?
3.60 */
3.61 + g_SourcePawn.PopTracer(err, m_CustomMsg ? m_MsgCache : NULL);
3.62
3.63 #if defined _DEBUG
3.64 if (err == SP_ERROR_NONE)
3.65 @@ -96,6 +125,51 @@
3.66 return err;
3.67 }
3.68
3.69 +void BaseContext::SetErrorMessage(const char *msg, va_list ap)
3.70 +{
3.71 + m_CustomMsg = true;
3.72 +
3.73 + vsnprintf(m_MsgCache, sizeof(m_MsgCache), msg, ap);
3.74 +}
3.75 +
3.76 +void BaseContext::ThrowNativeErrorEx(int error, const char *msg, ...)
3.77 +{
3.78 + if (!m_InExec)
3.79 + {
3.80 + return;
3.81 + }
3.82 +
3.83 + ctx->n_err = error;
3.84 +
3.85 + if (msg)
3.86 + {
3.87 + va_list ap;
3.88 + va_start(ap, msg);
3.89 + SetErrorMessage(msg, ap);
3.90 + va_end(ap);
3.91 + }
3.92 +}
3.93 +
3.94 +cell_t BaseContext::ThrowNativeError(const char *msg, ...)
3.95 +{
3.96 + if (!m_InExec)
3.97 + {
3.98 + return 0;
3.99 + }
3.100 +
3.101 + ctx->n_err = SP_ERROR_NATIVE;
3.102 +
3.103 + if (msg)
3.104 + {
3.105 + va_list ap;
3.106 + va_start(ap, msg);
3.107 + SetErrorMessage(msg, ap);
3.108 + va_end(ap);
3.109 + }
3.110 +
3.111 + return 0;
3.112 +}
3.113 +
3.114 int BaseContext::HeapAlloc(unsigned int cells, cell_t *local_addr, cell_t **phys_addr)
3.115 {
3.116 cell_t *addr;
4.1 --- a/core/vm/sp_vm_basecontext.h Mon Jan 01 01:08:36 2007 +0000
4.2 +++ b/core/vm/sp_vm_basecontext.h Mon Jan 01 01:09:53 2007 +0000
4.3 @@ -42,12 +42,19 @@
4.4 virtual int BindNative(sp_nativeinfo_t *native);
4.5 virtual int BindNativeToAny(SPVM_NATIVE_FUNC native);
4.6 virtual int Execute(funcid_t funcid, cell_t *result);
4.7 + virtual void ThrowNativeErrorEx(int error, const char *msg, ...);
4.8 + virtual cell_t ThrowNativeError(const char *msg, ...);
4.9 public: //IPluginDebugInfo
4.10 virtual int LookupFile(ucell_t addr, const char **filename);
4.11 virtual int LookupFunction(ucell_t addr, const char **name);
4.12 virtual int LookupLine(ucell_t addr, uint32_t *line);
4.13 private:
4.14 + void SetErrorMessage(const char *msg, va_list ap);
4.15 + private:
4.16 sp_context_t *ctx;
4.17 + char m_MsgCache[1024];
4.18 + bool m_CustomMsg;
4.19 + bool m_InExec;
4.20 };
4.21 };
4.22
5.1 --- a/core/vm/sp_vm_engine.cpp Mon Jan 01 01:08:36 2007 +0000
5.2 +++ b/core/vm/sp_vm_engine.cpp Mon Jan 01 01:09:53 2007 +0000
5.3 @@ -1,5 +1,6 @@
5.4 #include <malloc.h>
5.5 #include <string.h>
5.6 +#include <assert.h>
5.7 #include "sp_file_headers.h"
5.8 #include "sp_vm_types.h"
5.9 #include "sp_vm_engine.h"
5.10 @@ -13,8 +14,60 @@
5.11 #include <sys/mman.h>
5.12 #endif
5.13
5.14 +#define INVALID_CIP 0xFFFFFFFF
5.15 +
5.16 using namespace SourcePawn;
5.17
5.18 +#define ERROR_MESSAGE_MAX 23
5.19 +static const char *g_ErrorMsgTable[] =
5.20 +{
5.21 + NULL,
5.22 + "Unrecognizable file format",
5.23 + "Decompressor was not found",
5.24 + "Not enough space on the heap",
5.25 + "Invalid parameter or parameter type",
5.26 + "Invalid plugin address",
5.27 + "Object or index not found",
5.28 + "Invalid index or index not found",
5.29 + "Not enough space on the stack",
5.30 + "Debug section not found or debug not enabled",
5.31 + "Invalid instruction",
5.32 + "Invalid memory access",
5.33 + "Stack went below stack boundary",
5.34 + "Heap went below heap boundary",
5.35 + "Divide by zero",
5.36 + "Array index is out of bounds",
5.37 + "Instruction contained invalid parameter",
5.38 + "Stack memory leaked by native",
5.39 + "Heap memory leaked by native",
5.40 + "Dynamic array is too big",
5.41 + "Tracker stack is out of bounds",
5.42 + "Native was never bound",
5.43 + "Maximum number of parameters reached",
5.44 + "Native detected error",
5.45 +};
5.46 +
5.47 +SourcePawnEngine::SourcePawnEngine()
5.48 +{
5.49 + m_pDebugHook = NULL;
5.50 + m_CallStack = NULL;
5.51 + m_FreedCalls = NULL;
5.52 + m_CurChain = 0;
5.53 +}
5.54 +
5.55 +SourcePawnEngine::~SourcePawnEngine()
5.56 +{
5.57 + assert(m_CallStack == NULL);
5.58 +
5.59 + TracedCall *pTemp;
5.60 + while (m_FreedCalls)
5.61 + {
5.62 + pTemp = m_FreedCalls->next;
5.63 + delete pTemp;
5.64 + m_FreedCalls = pTemp;
5.65 + }
5.66 +}
5.67 +
5.68 void *SourcePawnEngine::ExecAlloc(size_t size)
5.69 {
5.70 #if defined WIN32
5.71 @@ -302,3 +355,224 @@
5.72
5.73 return SP_ERROR_NONE;
5.74 }
5.75 +
5.76 +IDebugListener *SourcePawnEngine::SetDebugListener(IDebugListener *pListener)
5.77 +{
5.78 + IDebugListener *old = m_pDebugHook;
5.79 +
5.80 + m_pDebugHook = pListener;
5.81 +
5.82 + return old;
5.83 +}
5.84 +
5.85 +unsigned int SourcePawnEngine::GetContextCallCount()
5.86 +{
5.87 + if (!m_CallStack)
5.88 + {
5.89 + return 0;
5.90 + }
5.91 +
5.92 + return m_CallStack->chain;
5.93 +}
5.94 +
5.95 +TracedCall *SourcePawnEngine::MakeTracedCall(bool new_chain)
5.96 +{
5.97 + TracedCall *pCall;
5.98 +
5.99 + if (!m_FreedCalls)
5.100 + {
5.101 + pCall = new TracedCall;
5.102 + } else {
5.103 + /* Unlink the head node from the free list */
5.104 + pCall = m_FreedCalls;
5.105 + m_FreedCalls = m_FreedCalls->next;
5.106 + if (m_FreedCalls)
5.107 + {
5.108 + m_FreedCalls->prev = NULL;
5.109 + }
5.110 + }
5.111 +
5.112 + /* Link as the head node into the call stack */
5.113 + pCall->next = m_CallStack;
5.114 + pCall->prev = NULL;
5.115 +
5.116 + if (new_chain)
5.117 + {
5.118 + pCall->chain = ++m_CurChain;
5.119 + } else {
5.120 + pCall->chain = m_CurChain;
5.121 + }
5.122 +
5.123 + if (m_CallStack)
5.124 + {
5.125 + m_CallStack->prev = pCall;
5.126 + }
5.127 + m_CallStack = pCall;
5.128 +
5.129 + return pCall;
5.130 +}
5.131 +
5.132 +void SourcePawnEngine::FreeTracedCall(TracedCall *pCall)
5.133 +{
5.134 + /* Check if this is the top of the call stack */
5.135 + if (pCall == m_CallStack)
5.136 + {
5.137 + m_CallStack = m_CallStack->next;
5.138 + if (m_CallStack)
5.139 + {
5.140 + m_CallStack->prev = NULL;
5.141 + }
5.142 + }
5.143 +
5.144 + /* Add this to our linked list of freed calls */
5.145 + if (!m_FreedCalls)
5.146 + {
5.147 + m_FreedCalls = pCall;
5.148 + m_FreedCalls->next = NULL;
5.149 + m_FreedCalls->prev = NULL;
5.150 + } else {
5.151 + pCall->next = m_FreedCalls;
5.152 + pCall->prev = NULL;
5.153 + m_FreedCalls->prev = pCall;
5.154 + m_FreedCalls = pCall;
5.155 + }
5.156 +}
5.157 +
5.158 +void SourcePawnEngine::PushTracer(sp_context_t *ctx)
5.159 +{
5.160 + TracedCall *pCall = MakeTracedCall(true);
5.161 +
5.162 + pCall->cip = INVALID_CIP;
5.163 + pCall->ctx = ctx;
5.164 + pCall->frm = INVALID_CIP;
5.165 +}
5.166 +
5.167 +void SourcePawnEngine::RunTracer(sp_context_t *ctx, uint32_t frame, uint32_t codeip)
5.168 +{
5.169 + assert(m_CallStack != NULL);
5.170 + assert(m_CallStack->ctx == ctx);
5.171 + assert(m_CallStack->chain == m_CurChain);
5.172 +
5.173 + if (m_CallStack->cip == INVALID_CIP)
5.174 + {
5.175 + /* We aren't logging anything yet, so begin the trace */
5.176 + m_CallStack->cip = codeip;
5.177 + m_CallStack->frm = frame;
5.178 + } else {
5.179 + if (m_CallStack->frm > frame)
5.180 + {
5.181 + /* The last frame has moved down the stack,
5.182 + * so we have to push a new call onto our list.
5.183 + */
5.184 + TracedCall *pCall = MakeTracedCall(false);
5.185 + pCall->frm = frame;
5.186 + } else if (m_CallStack->frm < frame) {
5.187 + /* The last frame has moved up the stack,
5.188 + * so we have to pop the call from our list.
5.189 + */
5.190 + FreeTracedCall(m_CallStack);
5.191 + }
5.192 + /* no matter where we are, update the cip */
5.193 + m_CallStack->cip = codeip;
5.194 + }
5.195 +}
5.196 +
5.197 +void SourcePawnEngine::PopTracer(int error, const char *msg)
5.198 +{
5.199 + assert(m_CallStack != NULL);
5.200 +
5.201 + if (error != SP_ERROR_NONE && m_pDebugHook)
5.202 + {
5.203 + CContextTrace trace(m_CallStack, error, msg);
5.204 + m_pDebugHook->OnContextExecuteError(m_CallStack->ctx->context, &trace);
5.205 + }
5.206 +
5.207 + /* Now pop the error chain */
5.208 + while (m_CallStack && m_CallStack->chain == m_CurChain)
5.209 + {
5.210 + FreeTracedCall(m_CallStack);
5.211 + }
5.212 +
5.213 + m_CurChain--;
5.214 +}
5.215 +
5.216 +CContextTrace::CContextTrace(TracedCall *pStart, int error, const char *msg) :
5.217 + m_Error(error), m_pMsg(msg), m_pStart(pStart), m_pIterator(pStart)
5.218 +{
5.219 +}
5.220 +
5.221 +bool CContextTrace::DebugInfoAvailable()
5.222 +{
5.223 + return ((m_pStart->ctx->flags & SP_FLAG_DEBUG) == SP_FLAG_DEBUG);
5.224 +}
5.225 +
5.226 +const char *CContextTrace::GetCustomErrorString()
5.227 +{
5.228 + return m_pMsg;
5.229 +}
5.230 +
5.231 +int CContextTrace::GetErrorCode()
5.232 +{
5.233 + return m_Error;
5.234 +}
5.235 +
5.236 +const char *CContextTrace::GetErrorString()
5.237 +{
5.238 + if (m_Error > ERROR_MESSAGE_MAX ||
5.239 + m_Error < 1)
5.240 + {
5.241 + return "Invalid error code";
5.242 + } else {
5.243 + return g_ErrorMsgTable[m_Error];
5.244 + }
5.245 +}
5.246 +
5.247 +void CContextTrace::ResetTrace()
5.248 +{
5.249 + m_pIterator = m_pStart;
5.250 +}
5.251 +
5.252 +bool CContextTrace::GetTraceInfo(CallStackInfo *trace)
5.253 +{
5.254 + if (m_pIterator->chain != m_pStart->chain)
5.255 + {
5.256 + return false;
5.257 + }
5.258 +
5.259 + if (m_pIterator->cip == INVALID_CIP)
5.260 + {
5.261 + return false;
5.262 + }
5.263 +
5.264 + IPluginContext *pContext = m_pIterator->ctx->context;
5.265 + IPluginDebugInfo *pInfo = pContext->GetDebugInfo();
5.266 +
5.267 + m_pIterator = m_pIterator->next;
5.268 +
5.269 + if (!pInfo)
5.270 + {
5.271 + return false;
5.272 + }
5.273 +
5.274 + if (!trace)
5.275 + {
5.276 + return true;
5.277 + }
5.278 +
5.279 + if (pInfo->LookupFile(m_pIterator->cip, &(trace->filename)) != SP_ERROR_NONE)
5.280 + {
5.281 + trace->filename = NULL;
5.282 + }
5.283 +
5.284 + if (pInfo->LookupFunction(m_pIterator->cip, &(trace->function)) != SP_ERROR_NONE)
5.285 + {
5.286 + trace->function = NULL;
5.287 + }
5.288 +
5.289 + if (pInfo->LookupLine(m_pIterator->cip, &(trace->line)) != SP_ERROR_NONE)
5.290 + {
5.291 + trace->line = 0;
5.292 + }
5.293 +
5.294 + return true;
5.295 +}
6.1 --- a/core/vm/sp_vm_engine.h Mon Jan 01 01:08:36 2007 +0000
6.2 +++ b/core/vm/sp_vm_engine.h Mon Jan 01 01:09:53 2007 +0000
6.3 @@ -5,83 +5,78 @@
6.4
6.5 namespace SourcePawn
6.6 {
6.7 + struct TracedCall
6.8 + {
6.9 + uint32_t cip;
6.10 + uint32_t frm;
6.11 + sp_context_t *ctx;
6.12 + TracedCall *next;
6.13 + TracedCall *prev;
6.14 + unsigned int chain;
6.15 + };
6.16 +
6.17 +
6.18 + class CContextTrace : public IContextTrace
6.19 + {
6.20 + public:
6.21 + CContextTrace(TracedCall *pStart, int error, const char *msg);
6.22 + public:
6.23 + virtual int GetErrorCode();
6.24 + virtual const char *GetErrorString();
6.25 + virtual bool DebugInfoAvailable();
6.26 + virtual const char *GetCustomErrorString();
6.27 + virtual bool GetTraceInfo(CallStackInfo *trace);
6.28 + virtual void ResetTrace();
6.29 + private:
6.30 + TracedCall *m_pStart;
6.31 + TracedCall *m_pIterator;
6.32 + const char *m_pMsg;
6.33 + int m_Error;
6.34 + };
6.35 +
6.36 +
6.37 class SourcePawnEngine : public ISourcePawnEngine
6.38 {
6.39 public:
6.40 - /**
6.41 - * Loads a named file from a file pointer.
6.42 - * Using this means base memory will be allocated by the VM.
6.43 - * Note: The file handle position may be undefined on entry, and is
6.44 - * always undefined on conclusion.
6.45 - *
6.46 - * @param fp File pointer. May be at any offset. Not closed on return.
6.47 - * @param err Optional error code pointer.
6.48 - * @return A new plugin structure.
6.49 - */
6.50 + SourcePawnEngine();
6.51 + ~SourcePawnEngine();
6.52 + public: //ISourcePawnEngine
6.53 sp_plugin_t *LoadFromFilePointer(FILE *fp, int *err);
6.54 -
6.55 + sp_plugin_t *LoadFromMemory(void *base, sp_plugin_t *plugin, int *err);
6.56 + int FreeFromMemory(sp_plugin_t *plugin);
6.57 + IPluginContext *CreateBaseContext(sp_context_t *ctx);
6.58 + void FreeBaseContext(IPluginContext *ctx);
6.59 + void *BaseAlloc(size_t size);
6.60 + void BaseFree(void *memory);
6.61 + void *ExecAlloc(size_t size);
6.62 + void ExecFree(void *address);
6.63 + IDebugListener *SetDebugListener(IDebugListener *pListener);
6.64 + unsigned int GetContextCallCount();
6.65 + public: //Debugger Stuff
6.66 /**
6.67 - * Loads a file from a base memory address.
6.68 - *
6.69 - * @param base Base address of the plugin's memory region.
6.70 - * @param plugin If NULL, a new plugin pointer is returned.
6.71 - * Otherwise, the passed pointer is used.
6.72 - * @param err Optional error code pointer.
6.73 - * @return The resulting plugin pointer.
6.74 + * @brief Pushes a context onto the top of the call tracer.
6.75 + *
6.76 + * @param ctx Plugin context.
6.77 */
6.78 - sp_plugin_t *LoadFromMemory(void *base, sp_plugin_t *plugin, int *err);
6.79 -
6.80 - /**
6.81 - * Frees all of the memory associated with a plugin file.
6.82 - * If allocated using SP_LoadFromMemory, the base and plugin pointer
6.83 - * itself are not freed (so this may end up doing nothing).
6.84 - */
6.85 - int FreeFromMemory(sp_plugin_t *plugin);
6.86 + void PushTracer(sp_context_t *ctx);
6.87
6.88 /**
6.89 - * Creates a new IContext from a context handle.
6.90 - *
6.91 - * @param ctx Context to use as a basis for the IPluginContext.
6.92 - * @return New IPluginContext handle.
6.93 + * @brief Pops a plugin off the call tracer.
6.94 */
6.95 - IPluginContext *CreateBaseContext(sp_context_t *ctx);
6.96 -
6.97 - /**
6.98 - * Frees a context.
6.99 - *
6.100 - * @param ctx Context pointer to free.
6.101 - */
6.102 - void FreeBaseContext(IPluginContext *ctx);
6.103 + void PopTracer(int error, const char *msg);
6.104
6.105 /**
6.106 - * Allocates memory.
6.107 - *
6.108 - * @param size Size of memory to allocate.
6.109 - * @return Pointer to memory, NULL if allocation failed.
6.110 - */
6.111 - void *BaseAlloc(size_t size);
6.112 -
6.113 - /**
6.114 - * Frees memory allocated with BaseAlloc.
6.115 - *
6.116 - * @param memory Memory address to free.
6.117 + * @brief Runs tracer from a debug break.
6.118 */
6.119 - void BaseFree(void *memory);
6.120 -
6.121 - /**
6.122 - * Allocates executable memory.
6.123 - *
6.124 - * @param size Size of memory to allocate.
6.125 - * @return Pointer to memory, NULL if allocation failed.
6.126 - */
6.127 - void *ExecAlloc(size_t size);
6.128 -
6.129 - /**
6.130 - * Frees executable memory.
6.131 - *
6.132 - * @param address Address to free.
6.133 - */
6.134 - void ExecFree(void *address);
6.135 + void RunTracer(sp_context_t *ctx, uint32_t frame, uint32_t codeip);
6.136 + private:
6.137 + TracedCall *MakeTracedCall(bool new_chain);
6.138 + void FreeTracedCall(TracedCall *pCall);
6.139 + private:
6.140 + IDebugListener *m_pDebugHook;
6.141 + TracedCall *m_FreedCalls;
6.142 + TracedCall *m_CallStack;
6.143 + unsigned int m_CurChain;
6.144 };
6.145 };
6.146