core/vm/sp_vm_engine.cpp
author David Anderson <dvander@alliedmods.net>
Mon Jan 01 01:09:53 2007 +0000 (2007-01-01)
changeset 240 1360e8aa47d7
parent 172 d8a8186eccbf
child 246 e27e6519c856
permissions -rw-r--r--
initial import of new debugger API
     1 #include <malloc.h>
     2 #include <string.h>
     3 #include <assert.h>
     4 #include "sp_file_headers.h"
     5 #include "sp_vm_types.h"
     6 #include "sp_vm_engine.h"
     7 #include "zlib/zlib.h"
     8 #include "sp_vm_basecontext.h"
     9 
    10 #if defined WIN32
    11  #define WIN32_LEAN_AND_MEAN
    12  #include <windows.h>
    13 #else if defined __GNUC__
    14  #include <sys/mman.h>
    15 #endif
    16 
    17 #define INVALID_CIP			0xFFFFFFFF
    18 
    19 using namespace SourcePawn;
    20 
    21 #define ERROR_MESSAGE_MAX		23
    22 static const char *g_ErrorMsgTable[] = 
    23 {
    24 	NULL,
    25 	"Unrecognizable file format",
    26 	"Decompressor was not found",
    27 	"Not enough space on the heap",
    28 	"Invalid parameter or parameter type",
    29 	"Invalid plugin address",
    30 	"Object or index not found",
    31 	"Invalid index or index not found",
    32 	"Not enough space on the stack",
    33 	"Debug section not found or debug not enabled",
    34 	"Invalid instruction",
    35 	"Invalid memory access",
    36 	"Stack went below stack boundary",
    37 	"Heap went below heap boundary",
    38 	"Divide by zero",
    39 	"Array index is out of bounds",
    40 	"Instruction contained invalid parameter",
    41 	"Stack memory leaked by native",
    42 	"Heap memory leaked by native",
    43 	"Dynamic array is too big",
    44 	"Tracker stack is out of bounds",
    45 	"Native was never bound",
    46 	"Maximum number of parameters reached",
    47 	"Native detected error",
    48 };
    49 
    50 SourcePawnEngine::SourcePawnEngine()
    51 {
    52 	m_pDebugHook = NULL;
    53 	m_CallStack = NULL;
    54 	m_FreedCalls = NULL;
    55 	m_CurChain = 0;
    56 }
    57 
    58 SourcePawnEngine::~SourcePawnEngine()
    59 {
    60 	assert(m_CallStack == NULL);
    61 
    62 	TracedCall *pTemp;
    63 	while (m_FreedCalls)
    64 	{
    65 		pTemp = m_FreedCalls->next;
    66 		delete pTemp;
    67 		m_FreedCalls = pTemp;
    68 	}
    69 }
    70 
    71 void *SourcePawnEngine::ExecAlloc(size_t size)
    72 {
    73 #if defined WIN32
    74 	return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    75 #else if defined __GNUC__
    76 	void *base = memalign(sysconf(_SC_PAGESIZE), size);
    77 	if (mprotect(base, size, PROT_READ|PROT_WRITE|PROT_EXEC) != 0)
    78 	{
    79 		free(base);
    80 		return NULL;
    81 	}
    82 	return base;
    83 #endif
    84 }
    85 
    86 void SourcePawnEngine::ExecFree(void *address)
    87 {
    88 #if defined WIN32
    89 	VirtualFree(address, 0, MEM_RELEASE);
    90 #else if defined __GNUC__
    91 	free(address);
    92 #endif
    93 }
    94 
    95 void *SourcePawnEngine::BaseAlloc(size_t size)
    96 {
    97 	return malloc(size);
    98 }
    99 
   100 void SourcePawnEngine::BaseFree(void *memory)
   101 {
   102 	free(memory);
   103 }
   104 
   105 IPluginContext *SourcePawnEngine::CreateBaseContext(sp_context_t *ctx)
   106 {
   107 	return new BaseContext(ctx);
   108 }
   109 
   110 void SourcePawnEngine::FreeBaseContext(IPluginContext *ctx)
   111 {
   112 	delete ctx;
   113 }
   114 
   115 sp_plugin_t *_ReadPlugin(sp_file_hdr_t *hdr, uint8_t *base, sp_plugin_t *plugin, int *err)
   116 {
   117 	char *nameptr;
   118 	uint8_t sectnum = 0;
   119 	sp_file_section_t *secptr = (sp_file_section_t *)(base + sizeof(sp_file_hdr_t));
   120 
   121 	memset(plugin, 0, sizeof(sp_plugin_t));
   122 
   123 	plugin->base = base;
   124 
   125 	while (sectnum < hdr->sections)
   126 	{
   127 		nameptr = (char *)(base + hdr->stringtab + secptr->nameoffs);
   128 
   129 		if (!(plugin->pcode) && !strcmp(nameptr, ".code"))
   130 		{
   131 			sp_file_code_t *cod = (sp_file_code_t *)(base + secptr->dataoffs);
   132 			plugin->pcode = base + secptr->dataoffs + cod->code;
   133 			plugin->pcode_size = cod->codesize;
   134 			plugin->flags = cod->flags;
   135 		}
   136 		else if (!(plugin->data) && !strcmp(nameptr, ".data"))
   137 		{
   138 			sp_file_data_t *dat = (sp_file_data_t *)(base + secptr->dataoffs);
   139 			plugin->data = base + secptr->dataoffs + dat->data;
   140 			plugin->data_size = dat->datasize;
   141 			plugin->memory = dat->memsize;
   142 		}
   143 		else if (!(plugin->info.publics) && !strcmp(nameptr, ".publics"))
   144 		{
   145 			plugin->info.publics_num = secptr->size / sizeof(sp_file_publics_t);
   146 			plugin->info.publics = (sp_file_publics_t *)(base + secptr->dataoffs);
   147 		}
   148 		else if (!(plugin->info.pubvars) && !strcmp(nameptr, ".pubvars"))
   149 		{
   150 			plugin->info.pubvars_num = secptr->size / sizeof(sp_file_pubvars_t);
   151 			plugin->info.pubvars = (sp_file_pubvars_t *)(base + secptr->dataoffs);
   152 		}
   153 		else if (!(plugin->info.natives) && !strcmp(nameptr, ".natives"))
   154 		{
   155 			plugin->info.natives_num = secptr->size / sizeof(sp_file_natives_t);
   156 			plugin->info.natives = (sp_file_natives_t *)(base + secptr->dataoffs);
   157 		}
   158 		else if (!(plugin->info.lib) && !strcmp(nameptr, ".libraries"))
   159 		{
   160 			plugin->info.libraries_num = secptr->size / sizeof(sp_file_libraries_t);
   161 			plugin->info.lib = (sp_file_libraries_t *)(base + secptr->dataoffs);
   162 		}
   163 		else if (!(plugin->info.stringbase) && !strcmp(nameptr, ".names"))
   164 		{
   165 			plugin->info.stringbase = (const char *)(base + secptr->dataoffs);
   166 		}
   167 		else if (!(plugin->debug.files) && !strcmp(nameptr, ".dbg.files"))
   168 		{
   169 			plugin->debug.files = (sp_fdbg_file_t *)(base + secptr->dataoffs);
   170 		}
   171 		else if (!(plugin->debug.lines) && !strcmp(nameptr, ".dbg.lines"))
   172 		{
   173 			plugin->debug.lines = (sp_fdbg_line_t *)(base + secptr->dataoffs);
   174 		}
   175 		else if (!(plugin->debug.symbols) && !strcmp(nameptr, ".dbg.symbols"))
   176 		{
   177 			plugin->debug.symbols = (sp_fdbg_symbol_t *)(base + secptr->dataoffs);
   178 		}
   179 		else if (!(plugin->debug.lines_num) && !strcmp(nameptr, ".dbg.info"))
   180 		{
   181 			sp_fdbg_info_t *inf = (sp_fdbg_info_t *)(base + secptr->dataoffs);
   182 			plugin->debug.files_num = inf->num_files;
   183 			plugin->debug.lines_num = inf->num_lines;
   184 			plugin->debug.syms_num = inf->num_syms;
   185 		}
   186 		else if (!(plugin->debug.stringbase) && !strcmp(nameptr, ".dbg.strings"))
   187 		{
   188 			plugin->debug.stringbase = (const char *)(base + secptr->dataoffs);
   189 		}
   190 
   191 		secptr++;
   192 		sectnum++;
   193 	}
   194 
   195 	if (!(plugin->pcode) || !(plugin->data) || !(plugin->info.stringbase))
   196 	{
   197 		goto return_error;
   198 	}
   199 
   200 	if ((plugin->flags & SP_FLAG_DEBUG) && (!(plugin->debug.files) || !(plugin->debug.lines) || !(plugin->debug.symbols)))
   201 	{
   202 		goto return_error;
   203 	}
   204 
   205 	if (err)
   206 	{
   207 		*err = SP_ERROR_NONE;
   208 	}
   209 
   210 	return plugin;
   211 
   212 return_error:
   213 	if (err)
   214 	{
   215 		*err = SP_ERROR_FILE_FORMAT;
   216 	}
   217 
   218 	return NULL;
   219 }
   220 
   221 sp_plugin_t *SourcePawnEngine::LoadFromFilePointer(FILE *fp, int *err)
   222 {
   223 	sp_file_hdr_t hdr;
   224 	sp_plugin_t *plugin;
   225 	uint8_t *base;
   226 	int z_result;
   227 	int error;
   228 
   229 	if (!fp)
   230 	{
   231 		error = SP_ERROR_NOT_FOUND;
   232 		goto return_error;
   233 	}
   234 
   235 	/* Rewind for safety */
   236 	rewind(fp);
   237 	fread(&hdr, sizeof(sp_file_hdr_t), 1, fp);
   238 
   239 	if (hdr.magic != SPFILE_MAGIC)
   240 	{
   241 		error = SP_ERROR_FILE_FORMAT;
   242 		goto return_error;
   243 	}
   244 
   245 	switch (hdr.compression)
   246 	{
   247 	case SPFILE_COMPRESSION_GZ:
   248 		{
   249 			uint32_t uncompsize = hdr.imagesize - hdr.dataoffs;
   250 			uint32_t compsize = hdr.disksize - hdr.dataoffs;
   251 			uint32_t sectsize = hdr.dataoffs - sizeof(sp_file_hdr_t);
   252 			uLongf destlen = uncompsize;
   253 
   254 			char *tempbuf = (char *)malloc(compsize);
   255 			void *uncompdata = malloc(uncompsize);
   256 			void *sectheader = malloc(sectsize);
   257 
   258 			fread(sectheader, sectsize, 1, fp);
   259 			fread(tempbuf, compsize, 1, fp);
   260 
   261 			z_result = uncompress((Bytef *)uncompdata, &destlen, (Bytef *)tempbuf, compsize);
   262 			free(tempbuf);
   263 			if (z_result != Z_OK)
   264 			{
   265 				free(sectheader);
   266 				free(uncompdata);
   267 				error = SP_ERROR_DECOMPRESSOR;
   268 				goto return_error;
   269 			}
   270 
   271 			base = (uint8_t *)malloc(hdr.imagesize);
   272 			memcpy(base, &hdr, sizeof(sp_file_hdr_t));
   273 			memcpy(base + sizeof(sp_file_hdr_t), sectheader, sectsize);
   274 			free(sectheader);
   275 			memcpy(base + hdr.dataoffs, uncompdata, uncompsize);
   276 			free(uncompdata);
   277 			break;
   278 		}
   279 	case SPFILE_COMPRESSION_NONE:
   280 		{
   281 			base = (uint8_t *)malloc(hdr.imagesize);
   282 			rewind(fp);
   283 			fread(base, hdr.imagesize, 1, fp);
   284 			break;
   285 		}
   286 	default:
   287 		{
   288 			error = SP_ERROR_DECOMPRESSOR;
   289 			goto return_error;
   290 		}
   291 	}
   292 
   293 	plugin = (sp_plugin_t *)malloc(sizeof(sp_plugin_t));
   294 	if (!_ReadPlugin(&hdr, base, plugin, err))
   295 	{
   296 		free(plugin);
   297 		free(base);
   298 		return NULL;
   299 	}
   300 
   301 	plugin->allocflags = 0;
   302 
   303 	return plugin;
   304 
   305 return_error:
   306 	if (err)
   307 	{
   308 		*err = error;
   309 	}
   310 	return NULL;
   311 }
   312 
   313 sp_plugin_t *SourcePawnEngine::LoadFromMemory(void *base, sp_plugin_t *plugin, int *err)
   314 {
   315 	sp_file_hdr_t hdr;
   316 	uint8_t noptr = 0;
   317 
   318 	memcpy(&hdr, base, sizeof(sp_file_hdr_t));
   319 
   320 	if (!plugin)
   321 	{
   322 		plugin = (sp_plugin_t *)malloc(sizeof(sp_plugin_t));
   323 		noptr = 1;
   324 	}
   325 
   326 	if (!_ReadPlugin(&hdr, (uint8_t *)base, plugin, err))
   327 	{
   328 		if (noptr)
   329 		{
   330 			free(plugin);
   331 		}
   332 		return NULL;
   333 	}
   334 
   335 	if (!noptr)
   336 	{
   337 		plugin->allocflags |= SP_FA_SELF_EXTERNAL;
   338 	}
   339 	plugin->allocflags |= SP_FA_BASE_EXTERNAL;
   340 
   341 	return plugin;
   342 }
   343 
   344 int SourcePawnEngine::FreeFromMemory(sp_plugin_t *plugin)
   345 {
   346 	if (!(plugin->allocflags & SP_FA_BASE_EXTERNAL))
   347 	{
   348 		free(plugin->base);
   349 		plugin->base = NULL;
   350 	}
   351 	if (!(plugin->allocflags & SP_FA_SELF_EXTERNAL))
   352 	{
   353 		free(plugin);
   354 	}
   355 
   356 	return SP_ERROR_NONE;
   357 }
   358 
   359 IDebugListener *SourcePawnEngine::SetDebugListener(IDebugListener *pListener)
   360 {
   361 	IDebugListener *old = m_pDebugHook;
   362 
   363 	m_pDebugHook = pListener;
   364 
   365 	return old;
   366 }
   367 
   368 unsigned int SourcePawnEngine::GetContextCallCount()
   369 {
   370 	if (!m_CallStack)
   371 	{
   372 		return 0;
   373 	}
   374 
   375 	return m_CallStack->chain;
   376 }
   377 
   378 TracedCall *SourcePawnEngine::MakeTracedCall(bool new_chain)
   379 {
   380 	TracedCall *pCall;
   381 
   382 	if (!m_FreedCalls)
   383 	{
   384 		pCall = new TracedCall;
   385 	} else {
   386 		/* Unlink the head node from the free list */
   387 		pCall = m_FreedCalls;
   388 		m_FreedCalls = m_FreedCalls->next;
   389 		if (m_FreedCalls)
   390 		{
   391 			m_FreedCalls->prev = NULL;
   392 		}
   393 	}
   394 
   395 	/* Link as the head node into the call stack */
   396 	pCall->next = m_CallStack;
   397 	pCall->prev = NULL;
   398 
   399 	if (new_chain)
   400 	{
   401 		pCall->chain = ++m_CurChain;
   402 	} else {
   403 		pCall->chain = m_CurChain;
   404 	}
   405 
   406 	if (m_CallStack)
   407 	{
   408 		m_CallStack->prev = pCall;
   409 	}
   410 	m_CallStack = pCall;
   411 
   412 	return pCall;
   413 }
   414 
   415 void SourcePawnEngine::FreeTracedCall(TracedCall *pCall)
   416 {
   417 	/* Check if this is the top of the call stack */
   418 	if (pCall == m_CallStack)
   419 	{
   420 		m_CallStack = m_CallStack->next;
   421 		if (m_CallStack)
   422 		{
   423 			m_CallStack->prev = NULL;
   424 		}
   425 	}
   426 
   427 	/* Add this to our linked list of freed calls */
   428 	if (!m_FreedCalls)
   429 	{
   430 		m_FreedCalls = pCall;
   431 		m_FreedCalls->next = NULL;
   432 		m_FreedCalls->prev = NULL;
   433 	} else {
   434 		pCall->next = m_FreedCalls;
   435 		pCall->prev = NULL;
   436 		m_FreedCalls->prev = pCall;
   437 		m_FreedCalls = pCall;
   438 	}
   439 }
   440 
   441 void SourcePawnEngine::PushTracer(sp_context_t *ctx)
   442 {
   443 	TracedCall *pCall = MakeTracedCall(true);
   444 
   445 	pCall->cip = INVALID_CIP;
   446 	pCall->ctx = ctx;
   447 	pCall->frm = INVALID_CIP;
   448 }
   449 
   450 void SourcePawnEngine::RunTracer(sp_context_t *ctx, uint32_t frame, uint32_t codeip)
   451 {
   452 	assert(m_CallStack != NULL);
   453 	assert(m_CallStack->ctx == ctx);
   454 	assert(m_CallStack->chain == m_CurChain);
   455 
   456 	if (m_CallStack->cip == INVALID_CIP)
   457 	{
   458 		/* We aren't logging anything yet, so begin the trace */
   459 		m_CallStack->cip = codeip;
   460 		m_CallStack->frm = frame;
   461 	} else {
   462 		if (m_CallStack->frm > frame)
   463 		{
   464 			/* The last frame has moved down the stack, 
   465 			 * so we have to push a new call onto our list.
   466 			 */
   467 			TracedCall *pCall = MakeTracedCall(false);
   468 			pCall->frm = frame;
   469 		} else if (m_CallStack->frm < frame) {
   470 			/* The last frame has moved up the stack,
   471 			 * so we have to pop the call from our list.
   472 			 */
   473 			FreeTracedCall(m_CallStack);
   474 		}
   475 		/* no matter where we are, update the cip */
   476 		m_CallStack->cip = codeip;
   477 	}
   478 }
   479 
   480 void SourcePawnEngine::PopTracer(int error, const char *msg)
   481 {
   482 	assert(m_CallStack != NULL);
   483 
   484 	if (error != SP_ERROR_NONE && m_pDebugHook)
   485 	{
   486 		CContextTrace trace(m_CallStack, error, msg);
   487 		m_pDebugHook->OnContextExecuteError(m_CallStack->ctx->context, &trace);
   488 	}
   489 
   490 	/* Now pop the error chain  */
   491 	while (m_CallStack && m_CallStack->chain == m_CurChain)
   492 	{
   493 		FreeTracedCall(m_CallStack);
   494 	}
   495 
   496 	m_CurChain--;
   497 }
   498 
   499 CContextTrace::CContextTrace(TracedCall *pStart, int error, const char *msg) : 
   500  m_Error(error), m_pMsg(msg), m_pStart(pStart), m_pIterator(pStart)
   501 {
   502 }
   503 
   504 bool CContextTrace::DebugInfoAvailable()
   505 {
   506 	return ((m_pStart->ctx->flags & SP_FLAG_DEBUG) == SP_FLAG_DEBUG);
   507 }
   508 
   509 const char *CContextTrace::GetCustomErrorString()
   510 {
   511 	return m_pMsg;
   512 }
   513 
   514 int CContextTrace::GetErrorCode()
   515 {
   516 	return m_Error;
   517 }
   518 
   519 const char *CContextTrace::GetErrorString()
   520 {
   521 	if (m_Error > ERROR_MESSAGE_MAX || 
   522 		m_Error < 1)
   523 	{
   524 		return "Invalid error code";
   525 	} else {
   526 		return g_ErrorMsgTable[m_Error];
   527 	}
   528 }
   529 
   530 void CContextTrace::ResetTrace()
   531 {
   532 	m_pIterator = m_pStart;
   533 }
   534 
   535 bool CContextTrace::GetTraceInfo(CallStackInfo *trace)
   536 {
   537 	if (m_pIterator->chain != m_pStart->chain)
   538 	{
   539 		return false;
   540 	}
   541 
   542 	if (m_pIterator->cip == INVALID_CIP)
   543 	{
   544 		return false;
   545 	}
   546 
   547 	IPluginContext *pContext = m_pIterator->ctx->context;
   548 	IPluginDebugInfo *pInfo = pContext->GetDebugInfo();
   549 
   550 	m_pIterator = m_pIterator->next;
   551 
   552 	if (!pInfo)
   553 	{
   554 		return false;
   555 	}
   556 
   557 	if (!trace)
   558 	{
   559 		return true;
   560 	}
   561 
   562 	if (pInfo->LookupFile(m_pIterator->cip, &(trace->filename)) != SP_ERROR_NONE)
   563 	{
   564 		trace->filename = NULL;
   565 	}
   566 
   567 	if (pInfo->LookupFunction(m_pIterator->cip, &(trace->function)) != SP_ERROR_NONE)
   568 	{
   569 		trace->function = NULL;
   570 	}
   571 
   572 	if (pInfo->LookupLine(m_pIterator->cip, &(trace->line)) != SP_ERROR_NONE)
   573 	{
   574 		trace->line = 0;
   575 	}
   576 
   577 	return true;
   578 }