/** 
 * @file llwindebug.cpp
 * @brief Windows debugging functions
 *
 * $LicenseInfo:firstyear=2004&license=viewergpl$
 * 
 * Copyright (c) 2004-2009, Linden Research, Inc.
 * 
 * Second Life Viewer Source Code
 * The source code in this file ("Source Code") is provided by Linden Lab
 * to you under the terms of the GNU General Public License, version 2.0
 * ("GPL"), unless you have obtained a separate licensing agreement
 * ("Other License"), formally executed by you and Linden Lab.  Terms of
 * the GPL can be found in doc/GPL-license.txt in this distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
 * 
 * There are special exceptions to the terms and conditions of the GPL as
 * it is applied to this Source Code. View the full text of the exception
 * in the file doc/FLOSS-exception.txt in this software distribution, or
 * online at
 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
 * 
 * By copying, modifying or distributing this software, you acknowledge
 * that you have read and understood your obligations described above,
 * and agree to abide by those obligations.
 * 
 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
 * COMPLETENESS OR PERFORMANCE.
 * $/LicenseInfo$
 */

#include "llviewerprecompiledheaders.h"

#include <tchar.h>
#include <tlhelp32.h>
#include "llwindebug.h"
#include "llviewercontrol.h"
#include "lldir.h"
#include "llsd.h"
#include "llsdserialize.h"

#pragma warning(disable: 4200)	//nonstandard extension used : zero-sized array in struct/union
#pragma warning(disable: 4100)	//unreferenced formal parameter


/*
LLSD Block for Windows Dump Information
<llsd>
  <map>
    <key>Platform</key>
    <string></string>
    <key>Process</key>
    <string></string>
    <key>Module</key>
    <string></string>
    <key>DateModified</key>
    <string></string>
    <key>ExceptionCode</key>
    <string></string>
    <key>ExceptionRead/WriteAddress</key>
    <string></string>
    <key>Instruction</key>
    <string></string>
    <key>Registers</key>
    <map>
      <!-- Continued for all registers -->
      <key>EIP</key>
      <string>...</string>
      <!-- ... -->
    </map>
    <key>Call Stack</key>
    <array>
      <!-- One map per stack frame -->
      <map>
	<key>ModuleName</key>
	<string></string>
	<key>ModuleBaseAddress</key>
	<string></string>
	<key>ModuleOffsetAddress</key>
	<string></string>
	<key>Parameters</key>
	<array>
	  <string></string>
	</array>
      </map>
      <!-- ... -->
    </array>
  </map>
</llsd>

*/


extern void (*gCrashCallback)(void);

// based on dbghelp.h
typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
									CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
									CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
									CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
									);

MINIDUMPWRITEDUMP f_mdwp = NULL;

#undef UNICODE

static LPTOP_LEVEL_EXCEPTION_FILTER gFilterFunc = NULL;

HMODULE	hDbgHelp;

// Tool Help functions.
typedef	HANDLE (WINAPI * CREATE_TOOL_HELP32_SNAPSHOT)(DWORD dwFlags, DWORD th32ProcessID);
typedef	BOOL (WINAPI * MODULE32_FIRST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
typedef	BOOL (WINAPI * MODULE32_NEST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);

CREATE_TOOL_HELP32_SNAPSHOT	CreateToolhelp32Snapshot_;
MODULE32_FIRST	Module32First_;
MODULE32_NEST	Module32Next_;

#define	DUMP_SIZE_MAX	8000	//max size of our dump
#define	CALL_TRACE_MAX	((DUMP_SIZE_MAX - 2000) / (MAX_PATH + 40))	//max number of traced calls
#define	NL				L"\r\n"	//new line


typedef struct STACK
{
	STACK *	Ebp;
	PBYTE	Ret_Addr;
	DWORD	Param[0];
} STACK, * PSTACK;

BOOL WINAPI Get_Module_By_Ret_Addr(PBYTE Ret_Addr, LPWSTR Module_Name, PBYTE & Module_Addr);
void WINAPI Get_Call_Stack(const EXCEPTION_RECORD* exception_record, 
						   const CONTEXT* context_record, 
						   LLSD& info);

void printError( CHAR* msg )
{
  DWORD eNum;
  TCHAR sysMsg[256];
  TCHAR* p;

  eNum = GetLastError( );
  FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
         NULL, eNum,
         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
         sysMsg, 256, NULL );

  // Trim the end of the line and terminate it with a null
  p = sysMsg;
  while( ( *p > 31 ) || ( *p == 9 ) )
    ++p;
  do { *p-- = 0; } while( ( p >= sysMsg ) &&
                          ( ( *p == '.' ) || ( *p < 33 ) ) );

  // Display the message
  printf( "\n  WARNING: %s failed with error %d (%s)", msg, eNum, sysMsg );
}

BOOL GetProcessThreadIDs(DWORD process_id, std::vector<DWORD>& thread_ids) 
{ 
  HANDLE hThreadSnap = INVALID_HANDLE_VALUE; 
  THREADENTRY32 te32; 
 
  // Take a snapshot of all running threads  
  hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 ); 
  if( hThreadSnap == INVALID_HANDLE_VALUE ) 
    return( FALSE ); 
 
  // Fill in the size of the structure before using it. 
  te32.dwSize = sizeof(THREADENTRY32 ); 
 
  // Retrieve information about the first thread,
  // and exit if unsuccessful
  if( !Thread32First( hThreadSnap, &te32 ) ) 
  {
    printError( "Thread32First" );  // Show cause of failure
    CloseHandle( hThreadSnap );     // Must clean up the snapshot object!
    return( FALSE );
  }

  // Now walk the thread list of the system,
  // and display information about each thread
  // associated with the specified process
  do 
  { 
    if( te32.th32OwnerProcessID == process_id )
    {
      thread_ids.push_back(te32.th32ThreadID); 
    }
  } while( Thread32Next(hThreadSnap, &te32 ) ); 

//  Don't forget to clean up the snapshot object.
  CloseHandle( hThreadSnap );
  return( TRUE );
}

BOOL GetThreadCallStack(DWORD thread_id, LLSD& info)
{
    if(GetCurrentThreadId() == thread_id)
    {
        // Early exit for the current thread.
        // Suspending the current thread would be a bad idea.
        // Plus you can't retrieve a valid current thread context.
        return false;
    }

    HANDLE thread_handle = INVALID_HANDLE_VALUE; 
    thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, thread_id);
    if(INVALID_HANDLE_VALUE == thread_handle)
    {
        return FALSE;
    }

    BOOL result = false;
    if(-1 != SuspendThread(thread_handle))
    {
        CONTEXT context_struct;
        context_struct.ContextFlags = CONTEXT_FULL;
        if(GetThreadContext(thread_handle, &context_struct))
        {
            Get_Call_Stack(NULL, &context_struct, info);
            result = true;
        }
        ResumeThread(thread_handle);
    }
    else
    {
        // Couldn't suspend thread.
    }

    CloseHandle(thread_handle);
    return result;
}


//Windows Call Stack Construction idea from 
//http://www.codeproject.com/tools/minidump.asp

//****************************************************************************************
BOOL WINAPI Get_Module_By_Ret_Addr(PBYTE Ret_Addr, LPWSTR Module_Name, PBYTE & Module_Addr)
//****************************************************************************************
// Find module by Ret_Addr (address in the module).
// Return Module_Name (full path) and Module_Addr (start address).
// Return TRUE if found.
{
	MODULEENTRY32	M = {sizeof(M)};
	HANDLE	hSnapshot;

	bool found = false;
	
	if (CreateToolhelp32Snapshot_)
	{
		hSnapshot = CreateToolhelp32Snapshot_(TH32CS_SNAPMODULE, 0);
		
		if ((hSnapshot != INVALID_HANDLE_VALUE) &&
			Module32First_(hSnapshot, &M))
		{
			do
			{
				if (DWORD(Ret_Addr - M.modBaseAddr) < M.modBaseSize)
				{
					lstrcpyn(Module_Name, M.szExePath, MAX_PATH);
					Module_Addr = M.modBaseAddr;
					found = true;
					break;
				}
			} while (Module32Next_(hSnapshot, &M));
		}

		CloseHandle(hSnapshot);
	}

	return found;
} //Get_Module_By_Ret_Addr

bool has_valid_call_before(PDWORD cur_stack_loc)
{
	PBYTE p_first_byte = (PBYTE)(*cur_stack_loc - 1);
	PBYTE p_second_byte = (PBYTE)(*cur_stack_loc -2);
	PBYTE p_fifth_byte = (PBYTE)(*cur_stack_loc - 5);
	PBYTE p_sixth_byte = (PBYTE)(*cur_stack_loc - 6);

	// make sure we can read it
	if(IsBadReadPtr(p_sixth_byte, 6 * sizeof(BYTE)))
	{
		return false;
	}

	// check for 9a + 4 bytes
	if(*p_fifth_byte == 0x9A)
	{
		return true;
	}

	// Check for E8 + 4 bytes and last byte is 00 or FF
	if(*p_fifth_byte == 0xE8 && (*p_first_byte == 0x00 || *p_first_byte == 0xFF))
	{
		return true;
	}
	
	// the other is six bytes
	if(*p_sixth_byte == 0xFF || *p_second_byte == 0xFF)
	{
		return true;
	}

	return false;
}

PBYTE get_valid_frame(PBYTE esp)
{
	PDWORD cur_stack_loc = NULL;
	const int max_search = 400;
	WCHAR	module_name[MAX_PATH];
	PBYTE	module_addr = 0;

	// round to highest multiple of four
	esp = (esp + (4 - ((int)esp % 4)) % 4);

	// scroll through stack a few hundred places.
	for (cur_stack_loc = (PDWORD) esp; cur_stack_loc < (PDWORD)esp + max_search; cur_stack_loc += 1)
	{
		// if you can read the pointer,
		if (IsBadReadPtr(cur_stack_loc, sizeof(PDWORD)))
		{
			continue;
		}

		//  check if it's in a module
		if (!Get_Module_By_Ret_Addr((PBYTE)*cur_stack_loc, module_name, module_addr))
		{
			continue;
		}

		// check if the code before the instruction ptr is a call 
		if(!has_valid_call_before(cur_stack_loc))
		{
			continue;
		}
		
		// if these all pass, return that ebp, otherwise continue till we're dead
		return (PBYTE)(cur_stack_loc - 1);
	}

	return NULL;
}

bool shouldUseStackWalker(PSTACK Ebp, int max_depth)
{
	WCHAR	Module_Name[MAX_PATH];
	PBYTE	Module_Addr = 0;
	int depth = 0;

	while (depth < max_depth) 
	{
		if (IsBadReadPtr(Ebp, sizeof(PSTACK)) || 
			IsBadReadPtr(Ebp->Ebp, sizeof(PSTACK)) ||
			Ebp->Ebp < Ebp ||
			Ebp->Ebp - Ebp > 0xFFFFFF ||
			IsBadCodePtr(FARPROC(Ebp->Ebp->Ret_Addr)) ||
			!Get_Module_By_Ret_Addr(Ebp->Ebp->Ret_Addr, Module_Name, Module_Addr))
		{
			return true;
		}
		depth++;
		Ebp = Ebp->Ebp;
	}

	return false;
}

//******************************************************************
void WINAPI Get_Call_Stack(const EXCEPTION_RECORD* exception_record, 
						   const CONTEXT* context_record, 
						   LLSD& info)
//******************************************************************
// Fill Str with call stack info.
// pException can be either GetExceptionInformation() or NULL.
// If pException = NULL - get current call stack.
{
	LPWSTR	Module_Name = new WCHAR[MAX_PATH];
	PBYTE	Module_Addr = 0;
	LLSD params;
	PBYTE	Esp = NULL;
	LLSD tmp_info;

	bool fake_frame = false;
	bool ebp_used = false;
	const int HEURISTIC_MAX_WALK = 20;
	int heuristic_walk_i = 0;
	int Ret_Addr_I = 0;

	STACK	Stack = {0, 0};
	PSTACK	Ebp;

	if (exception_record && context_record)		//fake frame for exception address
	{
		Stack.Ebp = (PSTACK)(context_record->Ebp);
		Stack.Ret_Addr = (PBYTE)exception_record->ExceptionAddress;
		Ebp = &Stack;
		Esp = (PBYTE) context_record->Esp;
		fake_frame = true;
	}
	else if(context_record)
	{
        Ebp = (PSTACK)(context_record->Ebp);
		Esp = (PBYTE)(context_record->Esp);
	}
	else
	{
		Ebp = (PSTACK)&exception_record - 1;	//frame addr of Get_Call_Stack()
		Esp = (PBYTE)&exception_record;

		// Skip frame of Get_Call_Stack().
		if (!IsBadReadPtr(Ebp, sizeof(PSTACK)))
			Ebp = Ebp->Ebp;		//caller ebp
	}

	// Trace CALL_TRACE_MAX calls maximum - not to exceed DUMP_SIZE_MAX.
	// Break trace on wrong stack frame.
	for (Ret_Addr_I = 0;
		heuristic_walk_i < HEURISTIC_MAX_WALK && 
		Ret_Addr_I < CALL_TRACE_MAX && !IsBadReadPtr(Ebp, sizeof(PSTACK)) && !IsBadCodePtr(FARPROC(Ebp->Ret_Addr));
		Ret_Addr_I++)
	{
		// If module with Ebp->Ret_Addr found.
		if (Get_Module_By_Ret_Addr(Ebp->Ret_Addr, Module_Name, Module_Addr))
		{
			// Save module's address and full path.
			tmp_info["CallStack"][Ret_Addr_I]["ModuleName"] = ll_convert_wide_to_string(Module_Name);
			tmp_info["CallStack"][Ret_Addr_I]["ModuleAddress"] = (int)Module_Addr;
			tmp_info["CallStack"][Ret_Addr_I]["CallOffset"] = (int)(Ebp->Ret_Addr - Module_Addr);

			// Save 5 params of the call. We don't know the real number of params.
			if (fake_frame && !Ret_Addr_I)	//fake frame for exception address
				params[0] = "Exception Offset";
			else if (!IsBadReadPtr(Ebp, sizeof(PSTACK) + 5 * sizeof(DWORD)))
			{
				for(int j = 0; j < 5; ++j)
				{
					params[j] = (int)Ebp->Param[j];
				}
			}
			tmp_info["CallStack"][Ret_Addr_I]["Parameters"] = params;
		}

		tmp_info["CallStack"][Ret_Addr_I]["ReturnAddress"] = (int)Ebp->Ret_Addr;

		// get ready for next frame
		// Set ESP to just after return address.  Not the real esp, but just enough after the return address
		if(!fake_frame) {
			Esp = (PBYTE)Ebp + 8;
		} 
		else
		{
			fake_frame = false;
		}

		// is next ebp valid?
		// only run if we've never found a good ebp
		// and make sure the one after is valid as well
		if(	!ebp_used && 
			shouldUseStackWalker(Ebp, 2))
		{
			heuristic_walk_i++;
			PBYTE new_ebp = get_valid_frame(Esp);
			if (new_ebp != NULL)
			{
				Ebp = (PSTACK)new_ebp;
			}
		}
		else
		{
			ebp_used = true;
			Ebp = Ebp->Ebp;
		}
	}
/* TODO remove or turn this code back on to edit the stack after i see a few raw ones. -Palmer
	// Now go back through and edit out heuristic stacks that could very well be bogus.
	// Leave the top and the last 3 stack chosen by the heuristic, however.
	if(heuristic_walk_i > 2)
	{
		info["CallStack"][0] = tmp_info["CallStack"][0];
		std::string ttest = info["CallStack"][0]["ModuleName"];
		for(int cur_frame = 1; 
			(cur_frame + heuristic_walk_i - 2 < Ret_Addr_I); 
			++cur_frame)
		{
			// edit out the middle heuristic found frames
			info["CallStack"][cur_frame] = tmp_info["CallStack"][cur_frame + heuristic_walk_i - 2];
		}
	}
	else
	{
		info = tmp_info;
	}
*/
	info = tmp_info;
	info["HeuristicWalkI"] = heuristic_walk_i;
	info["EbpUsed"] = ebp_used;

} //Get_Call_Stack

//***********************************
void WINAPI Get_Version_Str(LLSD& info)
//***********************************
// Fill Str with Windows version.
{
	OSVERSIONINFOEX	V = {sizeof(OSVERSIONINFOEX)};	//EX for NT 5.0 and later

	if (!GetVersionEx((POSVERSIONINFO)&V))
	{
		ZeroMemory(&V, sizeof(V));
		V.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
		GetVersionEx((POSVERSIONINFO)&V);
	}

	if (V.dwPlatformId != VER_PLATFORM_WIN32_NT)
		V.dwBuildNumber = LOWORD(V.dwBuildNumber);	//for 9x HIWORD(dwBuildNumber) = 0x04xx

	info["Platform"] = llformat("Windows:  %d.%d.%d, SP %d.%d, Product Type %d",	//SP - service pack, Product Type - VER_NT_WORKSTATION,...
		V.dwMajorVersion, V.dwMinorVersion, V.dwBuildNumber, V.wServicePackMajor, V.wServicePackMinor, V.wProductType);
} //Get_Version_Str

//*************************************************************
LLSD WINAPI Get_Exception_Info(PEXCEPTION_POINTERS pException)
//*************************************************************
// Allocate Str[DUMP_SIZE_MAX] and return Str with dump, if !pException - just return call stack in Str.
{
	LLSD info;
	LPWSTR		Str;
	int			Str_Len;
//	int			i;
	LPWSTR		Module_Name = new WCHAR[MAX_PATH];
	PBYTE		Module_Addr;
	HANDLE		hFile;
	FILETIME	Last_Write_Time;
	FILETIME	Local_File_Time;
	SYSTEMTIME	T;

	Str = new WCHAR[DUMP_SIZE_MAX];
	Str_Len = 0;
	if (!Str)
		return NULL;
	
	Get_Version_Str(info);
	
	GetModuleFileName(NULL, Str, MAX_PATH);
	info["Process"] = ll_convert_wide_to_string(Str);
	info["ThreadID"] = (S32)GetCurrentThreadId();

	// If exception occurred.
	if (pException)
	{
		EXCEPTION_RECORD &	E = *pException->ExceptionRecord;
		CONTEXT &			C = *pException->ContextRecord;

		// If module with E.ExceptionAddress found - save its path and date.
		if (Get_Module_By_Ret_Addr((PBYTE)E.ExceptionAddress, Module_Name, Module_Addr))
		{
			info["Module"] = ll_convert_wide_to_string(Module_Name);

			if ((hFile = CreateFile(Module_Name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
				FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE)
			{
				if (GetFileTime(hFile, NULL, NULL, &Last_Write_Time))
				{
					FileTimeToLocalFileTime(&Last_Write_Time, &Local_File_Time);
					FileTimeToSystemTime(&Local_File_Time, &T);

					info["DateModified"] = llformat("%02d/%02d/%d", T.wMonth, T.wDay, T.wYear);
				}
				CloseHandle(hFile);
			}
		}
		else
		{
			info["ExceptionAddr"] = (int)E.ExceptionAddress;
		}
		
		info["ExceptionCode"] = (int)E.ExceptionCode;
		
		/*
		//TODO: Fix this
		if (E.ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
		{
			// Access violation type - Write/Read.
			LLSD exception_info;
			exception_info["Type"] = E.ExceptionInformation[0] ? "Write" : "Read";
			exception_info["Address"] = llformat("%08x", E.ExceptionInformation[1]);
			info["Exception Information"] = exception_info;
		}
		*/

		
		// Save instruction that caused exception.
		/*
		std::string str;
		for (i = 0; i < 16; i++)
			str += llformat(" %02X", PBYTE(E.ExceptionAddress)[i]);
		info["Instruction"] = str;
		*/
		LLSD registers;
		registers["EAX"] = (int)C.Eax;
		registers["EBX"] = (int)C.Ebx;
		registers["ECX"] = (int)C.Ecx;
		registers["EDX"] = (int)C.Edx;
		registers["ESI"] = (int)C.Esi;
		registers["EDI"] = (int)C.Edi;
		registers["ESP"] = (int)C.Esp;
		registers["EBP"] = (int)C.Ebp;
		registers["EIP"] = (int)C.Eip;
		registers["EFlags"] = (int)C.EFlags;
		info["Registers"] = registers;
	} //if (pException)
	
	// Save call stack info.
	Get_Call_Stack(pException->ExceptionRecord, pException->ContextRecord, info);

	return info;
} //Get_Exception_Info

#define UNICODE


class LLMemoryReserve {
public:
	LLMemoryReserve();
	~LLMemoryReserve();
	void reserve();
	void release();
protected:
	unsigned char *mReserve;
	static const size_t MEMORY_RESERVATION_SIZE;
};

LLMemoryReserve::LLMemoryReserve() :
	mReserve(NULL)
{
};

LLMemoryReserve::~LLMemoryReserve()
{
	release();
}

// I dunno - this just seemed like a pretty good value.
const size_t LLMemoryReserve::MEMORY_RESERVATION_SIZE = 5 * 1024 * 1024;

void LLMemoryReserve::reserve()
{
	if(NULL == mReserve)
		mReserve = new unsigned char[MEMORY_RESERVATION_SIZE];
};

void LLMemoryReserve::release()
{
	delete [] mReserve;
	mReserve = NULL;
};

static LLMemoryReserve gEmergencyMemoryReserve;

#ifndef _M_IX86
	#error "The following code only works for x86!"
#endif
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI MyDummySetUnhandledExceptionFilter(
	LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
{
	if(lpTopLevelExceptionFilter ==  gFilterFunc)
		return gFilterFunc;

	llinfos << "Someone tried to set the exception filter. Listing call stack modules" << llendl;
	LLSD cs_info;
	Get_Call_Stack(NULL, NULL, cs_info);
	
	if(cs_info.has("CallStack") && cs_info["CallStack"].isArray())
	{
		LLSD cs = cs_info["CallStack"];
		for(LLSD::array_iterator i = cs.beginArray(); 
			i != cs.endArray(); 
			++i)
		{
			llinfos << "Module: " << (*i)["ModuleName"] << llendl;
		}
	}
	
	return gFilterFunc;
}

BOOL PreventSetUnhandledExceptionFilter()
{
	HMODULE hKernel32 = LoadLibrary(_T("kernel32.dll"));
	if (hKernel32 == NULL) 
		return FALSE;

	void *pOrgEntry = GetProcAddress(hKernel32, "SetUnhandledExceptionFilter");
	if(pOrgEntry == NULL) 
		return FALSE;
	
	unsigned char newJump[ 100 ];
	DWORD dwOrgEntryAddr = (DWORD)pOrgEntry;
	dwOrgEntryAddr += 5; // add 5 for 5 op-codes for jmp far
	void *pNewFunc = &MyDummySetUnhandledExceptionFilter;
	DWORD dwNewEntryAddr = (DWORD) pNewFunc;
	DWORD dwRelativeAddr = dwNewEntryAddr - dwOrgEntryAddr;

	newJump[ 0 ] = 0xE9;  // JMP absolute
	memcpy(&newJump[ 1 ], &dwRelativeAddr, sizeof(pNewFunc));
	SIZE_T bytesWritten;
	BOOL bRet = WriteProcessMemory(GetCurrentProcess(),
	pOrgEntry, newJump, sizeof(pNewFunc) + 1, &bytesWritten);
	return bRet;
}

// static
void  LLWinDebug::initExceptionHandler(LPTOP_LEVEL_EXCEPTION_FILTER filter_func)
{

	static bool s_first_run = true;
	// Load the dbghelp dll now, instead of waiting for the crash.
	// Less potential for stack mangling

	if (s_first_run)
	{
		// First, try loading from the directory that the app resides in.
		std::string local_dll_name = gDirUtilp->findFile("dbghelp.dll", gDirUtilp->getWorkingDir(), gDirUtilp->getExecutableDir());

		HMODULE hDll = NULL;
		hDll = LoadLibraryA(local_dll_name.c_str());
		if (!hDll)
		{
			hDll = LoadLibrary(L"dbghelp.dll");
		}

		if (!hDll)
		{
			LL_WARNS("AppInit") << "Couldn't find dbghelp.dll!" << LL_ENDL;
		}
		else
		{
			f_mdwp = (MINIDUMPWRITEDUMP) GetProcAddress(hDll, "MiniDumpWriteDump");

			if (!f_mdwp)
			{
				FreeLibrary(hDll);
				hDll = NULL;
			}
		}

		gEmergencyMemoryReserve.reserve();

		s_first_run = false;
	}

	// Try to get Tool Help library functions.
	HMODULE hKernel32;
	hKernel32 = GetModuleHandle(_T("KERNEL32"));
	CreateToolhelp32Snapshot_ = (CREATE_TOOL_HELP32_SNAPSHOT)GetProcAddress(hKernel32, "CreateToolhelp32Snapshot");
	Module32First_ = (MODULE32_FIRST)GetProcAddress(hKernel32, "Module32FirstW");
	Module32Next_ = (MODULE32_NEST)GetProcAddress(hKernel32, "Module32NextW");

    LPTOP_LEVEL_EXCEPTION_FILTER prev_filter;
	prev_filter = SetUnhandledExceptionFilter(filter_func);

	// *REMOVE:Mani
	//PreventSetUnhandledExceptionFilter();

	if(prev_filter != gFilterFunc)
	{
		LL_WARNS("AppInit") 
			<< "Replacing unknown exception (" << (void *)prev_filter << ") with (" << (void *)filter_func << ") !" << LL_ENDL;
	}
	
	gFilterFunc = filter_func;
}

bool LLWinDebug::checkExceptionHandler()
{
	bool ok = true;
	LPTOP_LEVEL_EXCEPTION_FILTER prev_filter;
	prev_filter = SetUnhandledExceptionFilter(gFilterFunc);

	if (prev_filter != gFilterFunc)
	{
		LL_WARNS("AppInit") << "Our exception handler (" << (void *)gFilterFunc << ") replaced with " << prev_filter << "!" << LL_ENDL;
		ok = false;
	}

	if (prev_filter == NULL)
	{
		ok = FALSE;
		if (gFilterFunc == NULL)
		{
			LL_WARNS("AppInit") << "Exception handler uninitialized." << LL_ENDL;
		}
		else
		{
			LL_WARNS("AppInit") << "Our exception handler (" << (void *)gFilterFunc << ") replaced with NULL!" << LL_ENDL;
		}
	}

	return ok;
}

void LLWinDebug::writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMATION *ExInfop, const std::string& filename)
{
	if(f_mdwp == NULL || gDirUtilp == NULL) 
	{
		return;
		//write_debug("No way to generate a minidump, no MiniDumpWriteDump function!\n");
	}
	else
	{
		std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, filename);

		HANDLE hFile = CreateFileA(dump_path.c_str(),
									GENERIC_WRITE,
									FILE_SHARE_WRITE,
									NULL,
									CREATE_ALWAYS,
									FILE_ATTRIBUTE_NORMAL,
									NULL);

		if (hFile != INVALID_HANDLE_VALUE)
		{
			// Write the dump, ignoring the return value
			f_mdwp(GetCurrentProcess(),
					GetCurrentProcessId(),
					hFile,
					type,
					ExInfop,
					NULL,
					NULL);

			CloseHandle(hFile);
		}

	}
}

// static
void LLWinDebug::generateCrashStacks(struct _EXCEPTION_POINTERS *exception_infop)
{
	// *NOTE:Mani - This method is no longer the exception handler.
	// Its called from viewer_windows_exception_handler() and other places.

	// 
	// Let go of a bunch of reserved memory to give library calls etc
	// a chance to execute normally in the case that we ran out of
	// memory.
	//
	LLSD info;
	std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
												"SecondLifeException");
	std::string log_path = dump_path + ".log";

	if (exception_infop)
	{
		// Since there is exception info... Release the hounds.
		gEmergencyMemoryReserve.release();

		if(gSavedSettings.getControl("SaveMinidump").notNull() && gSavedSettings.getBOOL("SaveMinidump"))
		{
			_MINIDUMP_EXCEPTION_INFORMATION ExInfo;

			ExInfo.ThreadId = ::GetCurrentThreadId();
			ExInfo.ExceptionPointers = exception_infop;
			ExInfo.ClientPointers = NULL;

			writeDumpToFile(MiniDumpNormal, &ExInfo, "SecondLife.dmp");
			writeDumpToFile((MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory), &ExInfo, "SecondLifePlus.dmp");
		}

		info = Get_Exception_Info(exception_infop);
	}

	LLSD threads;
    std::vector<DWORD> thread_ids;
    GetProcessThreadIDs(GetCurrentProcessId(), thread_ids);

    for(std::vector<DWORD>::iterator th_itr = thread_ids.begin(); 
                th_itr != thread_ids.end();
                ++th_itr)
    {
        LLSD thread_info;
        if(*th_itr != GetCurrentThreadId())
        {
            GetThreadCallStack(*th_itr, thread_info);
        }

        if(thread_info)
        {
            threads[llformat("ID %d", *th_itr)] = thread_info;
        }
    }

    info["Threads"] = threads;

	llofstream out_file(log_path);
	LLSDSerialize::toPrettyXML(info, out_file);
	out_file.close();
}

void LLWinDebug::clearCrashStacks()
{
	LLSD info;
	std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLifeException.log");
	LLFile::remove(dump_path);
}
