#include "pfStackTrace.h"
#include "pfMapFile.h"
#include "pfMapFileEntry.h"
#include <string.h>
#include <stdio.h>

#pragma optimize( "y", off )

extern "C" struct IMAGE_DOS_HEADER __ImageBase;

//-----------------------------------------------------------------------------

#define MAX_DEPTH 32

//-----------------------------------------------------------------------------

namespace dev
{

static long getCallerFromStack( unsigned long stackPtr, int index )
{
#if /*defined(_DEBUG) && */defined(_MSC_VER) && defined(_M_IX86)

	long caller = 0;
	__asm
	{
		mov ebx, stackPtr
		mov ecx, index
		inc ecx			
		xor eax, eax
StackTrace_getCaller_next:
		mov eax, [ebx+4]
		mov ebx, [ebx]
		test eax,eax
		jz StackTrace_getCallerFromStack_done
		dec ecx
		jnz StackTrace_getCaller_next
StackTrace_getCallerFromStack_done:
		mov caller, eax
	}
	return caller;

#else

	return 0;

#endif
}

static long getCaller( int index )
{
#if /*defined(_DEBUG) && */defined(_MSC_VER) && defined(_M_IX86)

	long caller = 0;
	__asm
	{
		mov ebx, ebp
		mov ecx, index
		inc ecx
		xor eax, eax
StackTrace_getCaller_next:
		mov eax, [ebx+4]
		mov ebx, [ebx]
		test eax,eax
		jz StackTrace_getCaller_done
		dec ecx
		jnz StackTrace_getCaller_next
StackTrace_getCaller_done:
		mov caller, eax
	}
	return caller;

#else

	return 0;

#endif
}

int StackTrace::printStackTrace( MapFile** map, int maps,
	int initLevel, int maxDepth,
	char* buffer, int bufferSize, unsigned long stackPtr, unsigned long opPtr )
{
	if ( maxDepth > MAX_DEPTH )
		maxDepth = MAX_DEPTH;
	bool	sucks = false;

	// list callers
	long callersAddr[MAX_DEPTH];
	int callers = 0;
	int i;
	for ( i = initLevel ; i < maxDepth ; ++i )
	{
		long addr;
		if( stackPtr != 0 )
		{
			if( i == initLevel )
				addr = opPtr;
			else
				addr = getCallerFromStack( stackPtr, i - initLevel - 1 );
		}
		else
			addr = getCaller( i );
		callersAddr[callers++] = addr;

		// end tracing here if the entry is not in a map file
		if( map != 0 )
		{
			int entry = -1;
			for ( int j = 0 ; j < maps ; ++j )
			{
				entry = map[j]->findEntry( addr );
				if ( -1 != entry )
					break;
			}
			if ( -1 == entry )
			{
				sucks = true;
				break;
			}
		}
	}


	int needed = 0;
	if ( bufferSize > 0 )
		*buffer = 0;

	sprintf( buffer, "Call stack (%d levels%s):\r\n", callers - initLevel, sucks ? ", truncated" : "" );
	needed = strlen( buffer );

	// output call stack
	for ( i = initLevel ; i < callers ; ++i )
	{
		long addr = callersAddr[callers-i-1];

		// find entry info
		int entry = -1;
		const MapFile* entryMap = 0;
		for ( int j = 0 ; j < maps ; ++j )
		{
			entry = map[j]->findEntry( addr );
			if ( -1 != entry )
			{
				entryMap = map[j];
				break;
			}
		}

		// format entry to tempory buf
		char buf[MapFileEntry::MAX_NAME+MAX_DEPTH+20];	// name + margin + hex number
		buf[0] = 0;
		for ( int k = initLevel-1 ; k < i ; ++k )
			strcat( buf, " " );
		if ( !entryMap )
			sprintf( buf+strlen(buf), "0x%08X\r\n", addr );
		else
		{
			const MapFileEntry &en = entryMap->getEntry( entry );
			long entryAddr = en.rvabase();
			long stackAddr = addr;
#ifdef WIN32
			stackAddr -= (long)&__ImageBase;
			stackAddr += entryMap->loadAddress();
#endif

			sprintf( buf+strlen(buf), "%s (0x%08X + 0x%08X)\r\n", en.name(), entryAddr, stackAddr - entryAddr );
		}

		// append temporary buf to output buffer if space left
		needed += strlen( buf );
		if ( needed < bufferSize )
			strcat( buffer, buf );
	}

	// terminate output buffer
	if ( needed < bufferSize )
		buffer[needed] = 0;
	else if ( bufferSize > 0 )
		buffer[bufferSize-1] = 0;
	return needed;
}


} // dev
/*
 * Copyright (c) 2001 Jani Kajala
 *
 * Permission to use, copy, modify, distribute and sell this
 * software and its documentation for any purpose is hereby
 * granted without fee, provided that the above copyright notice
 * appear in all copies and that both that copyright notice and
 * this permission notice appear in supporting documentation.
 * Jani Kajala makes no representations about the suitability 
 * of this software for any purpose. It is provided "as is" 
 * without express or implied warranty.
 */