sourcepawn/jit/sp_vm_engine.cpp
author David Anderson <dvander@alliedmods.net>
Fri Jul 11 05:14:32 2008 +0000 (2008-07-11)
branchrefac-jit
changeset 2190 880e81f30b5e
parent 2174 01c5adfae757
permissions -rw-r--r--
GetContext() now returns 'this' as a hack (shouldn't+can't be accessed anyway)
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourcePawn
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include "sp_vm_types.h"
    33 #include <sh_memory.h>
    34 /* HACK to avoid including sourcehook.h for just the SH_ASSERT definition */
    35 #if !defined  SH_ASSERT
    36 	#define SH_ASSERT(x, info)
    37 	#include <sh_pagealloc.h>
    38 	#undef SH_ASSERT
    39 #else
    40 	#include <sh_pagealloc.h>
    41 #endif
    42 
    43 #include <malloc.h>
    44 #include <string.h>
    45 #include <assert.h>
    46 #include "sp_file_headers.h"
    47 #include "sp_vm_engine.h"
    48 #include "zlib/zlib.h"
    49 #include "sp_vm_basecontext.h"
    50 
    51 SourcePawnEngine g_engine1;
    52 
    53 #if defined WIN32
    54  #define WIN32_LEAN_AND_MEAN
    55  #include <windows.h>
    56 #elif defined __GNUC__
    57  #include <sys/mman.h>
    58 #endif
    59 
    60 #define INVALID_CIP			0xFFFFFFFF
    61 
    62 using namespace SourcePawn;
    63 
    64 SourceHook::CPageAlloc g_ExeMemory(16);
    65 
    66 #define ERROR_MESSAGE_MAX		25
    67 static const char *g_ErrorMsgTable[] = 
    68 {
    69 	NULL,
    70 	"Unrecognizable file format",
    71 	"Decompressor was not found",
    72 	"Not enough space on the heap",
    73 	"Invalid parameter or parameter type",
    74 	"Invalid plugin address",
    75 	"Object or index not found",
    76 	"Invalid index or index not found",
    77 	"Not enough space on the stack",
    78 	"Debug section not found or debug not enabled",
    79 	"Invalid instruction",
    80 	"Invalid memory access",
    81 	"Stack went below stack boundary",
    82 	"Heap went below heap boundary",
    83 	"Divide by zero",
    84 	"Array index is out of bounds",
    85 	"Instruction contained invalid parameter",
    86 	"Stack memory leaked by native",
    87 	"Heap memory leaked by native",
    88 	"Dynamic array is too big",
    89 	"Tracker stack is out of bounds",
    90 	"Native is not bound",
    91 	"Maximum number of parameters reached",
    92 	"Native detected error",
    93 	"Plugin not runnable",
    94 	"Call was aborted",
    95 };
    96 
    97 const char *SourcePawnEngine::GetErrorString(int error)
    98 {
    99 	if (error < 1 || error > ERROR_MESSAGE_MAX)
   100 	{
   101 		return NULL;
   102 	}
   103 
   104 	return g_ErrorMsgTable[error];
   105 }
   106 
   107 SourcePawnEngine::SourcePawnEngine()
   108 {
   109 	m_pDebugHook = NULL;
   110 	m_CallStack = NULL;
   111 	m_FreedCalls = NULL;
   112 	m_CurChain = 0;
   113 #if 0
   114 	m_pFreeFuncs = NULL;
   115 #endif
   116 }
   117 
   118 SourcePawnEngine::~SourcePawnEngine()
   119 {
   120 	TracedCall *pTemp;
   121 	while (m_FreedCalls)
   122 	{
   123 		pTemp = m_FreedCalls->next;
   124 		delete m_FreedCalls;
   125 		m_FreedCalls = pTemp;
   126 	}
   127 
   128 #if 0
   129 	CFunction *pNext;
   130 	while (m_pFreeFuncs)
   131 	{
   132 		pNext = m_pFreeFuncs->m_pNext;
   133 		delete m_pFreeFuncs;
   134 		m_pFreeFuncs = pNext;
   135 	}
   136 #endif
   137 }
   138 
   139 void *SourcePawnEngine::ExecAlloc(size_t size)
   140 {
   141 #if defined WIN32
   142 	return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
   143 #elif defined __GNUC__
   144 	void *base = memalign(sysconf(_SC_PAGESIZE), size);
   145 	if (mprotect(base, size, PROT_READ|PROT_WRITE|PROT_EXEC) != 0)
   146 	{
   147 		free(base);
   148 		return NULL;
   149 	}
   150 	return base;
   151 #endif
   152 }
   153 
   154 void *SourcePawnEngine::AllocatePageMemory(size_t size)
   155 {
   156 	return g_ExeMemory.Alloc(size);
   157 }
   158 
   159 void SourcePawnEngine::SetReadExecute(void *ptr)
   160 {
   161 	g_ExeMemory.SetRE(ptr);
   162 }
   163 
   164 void SourcePawnEngine::SetReadWrite(void *ptr)
   165 {
   166 	g_ExeMemory.SetRW(ptr);
   167 }
   168 
   169 void SourcePawnEngine::FreePageMemory(void *ptr)
   170 {
   171 	g_ExeMemory.Free(ptr);
   172 }
   173 
   174 void SourcePawnEngine::ExecFree(void *address)
   175 {
   176 #if defined WIN32
   177 	VirtualFree(address, 0, MEM_RELEASE);
   178 #elif defined __GNUC__
   179 	free(address);
   180 #endif
   181 }
   182 
   183 void *SourcePawnEngine::BaseAlloc(size_t size)
   184 {
   185 	return malloc(size);
   186 }
   187 
   188 void SourcePawnEngine::BaseFree(void *memory)
   189 {
   190 	free(memory);
   191 }
   192 
   193 sp_plugin_t *SourcePawnEngine::LoadFromFilePointer(FILE *fp, int *err)
   194 {
   195 	if (err != NULL)
   196 	{
   197 		*err = SP_ERROR_ABORTED;
   198 	}
   199 
   200 	return NULL;
   201 }
   202 
   203 sp_plugin_t *SourcePawnEngine::LoadFromMemory(void *base, sp_plugin_t *plugin, int *err)
   204 {
   205 	if (err != NULL)
   206 	{
   207 		*err = SP_ERROR_ABORTED;
   208 	}
   209 
   210 	return NULL;
   211 }
   212 
   213 int SourcePawnEngine::FreeFromMemory(sp_plugin_t *plugin)
   214 {
   215 	return SP_ERROR_ABORTED;
   216 }
   217 
   218 IDebugListener *SourcePawnEngine::SetDebugListener(IDebugListener *pListener)
   219 {
   220 	IDebugListener *old = m_pDebugHook;
   221 
   222 	m_pDebugHook = pListener;
   223 
   224 	return old;
   225 }
   226 
   227 unsigned int SourcePawnEngine::GetContextCallCount()
   228 {
   229 	if (!m_CallStack)
   230 	{
   231 		return 0;
   232 	}
   233 
   234 	return m_CallStack->chain;
   235 }
   236 
   237 TracedCall *SourcePawnEngine::MakeTracedCall(bool new_chain)
   238 {
   239 	TracedCall *pCall;
   240 
   241 	if (!m_FreedCalls)
   242 	{
   243 		pCall = new TracedCall;
   244 	} else {
   245 		/* Unlink the head node from the free list */
   246 		pCall = m_FreedCalls;
   247 		m_FreedCalls = m_FreedCalls->next;
   248 	}
   249 
   250 	/* Link as the head node into the call stack */
   251 	pCall->next = m_CallStack;
   252 
   253 	if (new_chain)
   254 	{
   255 		pCall->chain = ++m_CurChain;
   256 	} else {
   257 		pCall->chain = m_CurChain;
   258 	}
   259 
   260 	m_CallStack = pCall;
   261 
   262 	return pCall;
   263 }
   264 
   265 void SourcePawnEngine::FreeTracedCall(TracedCall *pCall)
   266 {
   267 	/* Check if this is the top of the call stack */
   268 	if (pCall == m_CallStack)
   269 	{
   270 		m_CallStack = m_CallStack->next;
   271 	}
   272 
   273 	/* Add this to our linked list of freed calls */
   274 	if (!m_FreedCalls)
   275 	{
   276 		m_FreedCalls = pCall;
   277 		m_FreedCalls->next = NULL;
   278 	} else {
   279 		pCall->next = m_FreedCalls;
   280 		m_FreedCalls = pCall;
   281 	}
   282 }
   283 
   284 void SourcePawnEngine::PushTracer(BaseContext *ctx)
   285 {
   286 	TracedCall *pCall = MakeTracedCall(true);
   287 
   288 	pCall->cip = INVALID_CIP;
   289 	pCall->ctx = ctx;
   290 	pCall->frm = INVALID_CIP;
   291 }
   292 
   293 void SourcePawnEngine::RunTracer(BaseContext *ctx, uint32_t frame, uint32_t codeip)
   294 {
   295 	assert(m_CallStack != NULL);
   296 	assert(m_CallStack->ctx == ctx);
   297 	assert(m_CallStack->chain == m_CurChain);
   298 
   299 	if (m_CallStack->cip == INVALID_CIP)
   300 	{
   301 		/* We aren't logging anything yet, so begin the trace */
   302 		m_CallStack->cip = codeip;
   303 		m_CallStack->frm = frame;
   304 	} else {
   305 		if (m_CallStack->frm > frame)
   306 		{
   307 			/* The last frame has moved down the stack, 
   308 			 * so we have to push a new call onto our list.
   309 			 */
   310 			TracedCall *pCall = MakeTracedCall(false);
   311 			pCall->ctx = ctx;
   312 			pCall->frm = frame;
   313 		} else if (m_CallStack->frm < frame) {
   314 			/* The last frame has moved up the stack,
   315 			 * so we have to pop the call from our list.
   316 			 */
   317 			FreeTracedCall(m_CallStack);
   318 		}
   319 		/* no matter where we are, update the cip */
   320 		m_CallStack->cip = codeip;
   321 	}
   322 }
   323 
   324 void SourcePawnEngine::PopTracer(int error, const char *msg)
   325 {
   326 	assert(m_CallStack != NULL);
   327 
   328 	if (error != SP_ERROR_NONE && m_pDebugHook)
   329 	{
   330 		uint32_t native = INVALID_CIP;
   331 
   332 		if (m_CallStack->ctx->GetCtx()->n_err)
   333 		{
   334 			native = m_CallStack->ctx->GetCtx()->n_idx;
   335 		}
   336 
   337 		CContextTrace trace(m_CallStack, error, msg, native);
   338 		m_pDebugHook->OnContextExecuteError(m_CallStack->ctx, &trace);
   339 	}
   340 
   341 	/* Now pop the error chain  */
   342 	while (m_CallStack && m_CallStack->chain == m_CurChain)
   343 	{
   344 		FreeTracedCall(m_CallStack);
   345 	}
   346 
   347 	m_CurChain--;
   348 }
   349 
   350 unsigned int SourcePawnEngine::GetEngineAPIVersion()
   351 {
   352 	return SOURCEPAWN_ENGINE_API_VERSION;
   353 }
   354 
   355 CContextTrace::CContextTrace(TracedCall *pStart, int error, const char *msg, uint32_t native) : 
   356  m_Error(error), m_pMsg(msg), m_pStart(pStart), m_pIterator(pStart), m_Native(native)
   357 {
   358 }
   359 
   360 bool CContextTrace::DebugInfoAvailable()
   361 {
   362 	return m_pStart->ctx->IsDebugging();
   363 }
   364 
   365 const char *CContextTrace::GetCustomErrorString()
   366 {
   367 	return m_pMsg;
   368 }
   369 
   370 int CContextTrace::GetErrorCode()
   371 {
   372 	return m_Error;
   373 }
   374 
   375 const char *CContextTrace::GetErrorString()
   376 {
   377 	if (m_Error > ERROR_MESSAGE_MAX || 
   378 		m_Error < 1)
   379 	{
   380 		return "Invalid error code";
   381 	} else {
   382 		return g_ErrorMsgTable[m_Error];
   383 	}
   384 }
   385 
   386 void CContextTrace::ResetTrace()
   387 {
   388 	m_pIterator = m_pStart;
   389 }
   390 
   391 bool CContextTrace::GetTraceInfo(CallStackInfo *trace)
   392 {
   393 	if (!m_pIterator || (m_pIterator->chain != m_pStart->chain))
   394 	{
   395 		return false;
   396 	}
   397 
   398 	if (m_pIterator->cip == INVALID_CIP)
   399 	{
   400 		return false;
   401 	}
   402 
   403 	IPluginContext *pContext = m_pIterator->ctx;
   404 	IPluginDebugInfo *pInfo = pContext->GetDebugInfo();
   405 
   406 	if (!pInfo)
   407 	{
   408 		return false;
   409 	}
   410 
   411 	if (!trace)
   412 	{
   413 		m_pIterator = m_pIterator->next;
   414 		return true;
   415 	}
   416 
   417 	if (pInfo->LookupFile(m_pIterator->cip, &(trace->filename)) != SP_ERROR_NONE)
   418 	{
   419 		trace->filename = NULL;
   420 	}
   421 
   422 	if (pInfo->LookupFunction(m_pIterator->cip, &(trace->function)) != SP_ERROR_NONE)
   423 	{
   424 		trace->function = NULL;
   425 	}
   426 
   427 	if (pInfo->LookupLine(m_pIterator->cip, &(trace->line)) != SP_ERROR_NONE)
   428 	{
   429 		trace->line = 0;
   430 	}
   431 
   432 	m_pIterator = m_pIterator->next;
   433 
   434 	return true;
   435 }
   436 
   437 const char *CContextTrace::GetLastNative(uint32_t *index)
   438 {
   439 	if (m_Native == INVALID_CIP)
   440 	{
   441 		return NULL;
   442 	}
   443 
   444 	sp_native_t *native;
   445 	if (m_pIterator->ctx->GetNativeByIndex(m_Native, &native) != SP_ERROR_NONE)
   446 	{
   447 		return NULL;
   448 	}
   449 
   450 	if (index)
   451 	{
   452 		*index = m_Native;
   453 	}
   454 
   455 	return native->name;
   456 }