RE: Call stack

"Daniel Vogel" <[email protected]> Wed, 9 Feb 2005 13:49:44 -0500
Newsgroups gmane.games.devel.windows
Message-ID <[email protected]>
> with the SP2 upgrade.  StackWalk won't walk the whole stack - 
> it just gives up after returning 2 call stack frames.  I've 

I attached the code of our stack walking implementation (sorry for the
formatting, notepad didn't like our usage of tabs). It works fine for
walking the stack after a crash as we have easy access to all the
information we need to feed StackWalk64.

> the stack.  I have plenty of cases in the engine where I 
> capture a call stack for debugging later if something else 

IMO StackWalk64 is mostly useless for this case as you cannot call
GetThreadContext on a running thread.

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/b
ase/getthreadcontext.asp

I assume you're doing this as that's exactly what we did with our proxy
malloc and it broke with SP2 as well.

-- Daniel, Epic Games Inc.
stackwalk.txt (text/plain, 3 KB)
INT CreateMiniDump( LPEXCEPTION_POINTERS ExceptionInfo )
{
	// Try to create file for minidump.
	HANDLE FileHandle	= TCHAR_CALL_OS( 
								CreateFileW( MiniDumpFilenameW, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ), 
								CreateFileA( MiniDumpFilenameA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ) 
						);

	// Write a minidump.

	if( FileHandle )
	{
		MINIDUMP_EXCEPTION_INFORMATION DumpExceptionInfo;
	
		DumpExceptionInfo.ThreadId			= GetCurrentThreadId();
		DumpExceptionInfo.ExceptionPointers	= ExceptionInfo;
		DumpExceptionInfo.ClientPointers	= true;

		MiniDumpWriteDump( GetCurrentProcess(), GetCurrentProcessId(), FileHandle, MiniDumpNormal, &DumpExceptionInfo, NULL, NULL );
		CloseHandle( FileHandle );
	}

	// Walk the stack.

	// Variables for stack walking.
	HANDLE				Process		= GetCurrentProcess(),
						Thread		= GetCurrentThread();
	DWORD64				Offset64	= 0;
	DWORD				Offset		= 0,
						SymOptions	= SymGetOptions();
	SYMBOL_INFO*		Symbol		= (SYMBOL_INFO*) appSystemMalloc( sizeof(SYMBOL_INFO) + MAX_SYMBOL_NAME_LENGTH );
	IMAGEHLP_LINE64		Line;
	STACKFRAME64		StackFrame;

	// Initialize stack frame.
	memset( &StackFrame, 0, sizeof(StackFrame) );
	StackFrame.AddrPC.Offset		= ExceptionInfo->ContextRecord->Eip;
	StackFrame.AddrPC.Mode			= AddrModeFlat;
	StackFrame.AddrFrame.Offset		= ExceptionInfo->ContextRecord->Ebp;
	StackFrame.AddrFrame.Mode		= AddrModeFlat;
	StackFrame.AddrStack.Offset		= ExceptionInfo->ContextRecord->Esp;
	StackFrame.AddrStack.Mode		= AddrModeFlat;
	StackFrame.AddrBStore.Mode		= AddrModeFlat;
	StackFrame.AddrReturn.Mode		= AddrModeFlat;

	// Set symbol options.
	SymOptions						|= SYMOPT_LOAD_LINES;
	SymOptions						|= SYMOPT_UNDNAME;
	SymOptions						|= SYMOPT_EXACT_SYMBOLS;
	SymSetOptions( SymOptions );

	// Initialize symbol.
	memset( Symbol, 0, sizeof(SYMBOL_INFO) + MAX_SYMBOL_NAME_LENGTH );
	Symbol->SizeOfStruct			= sizeof(SYMBOL_INFO);
	Symbol->MaxNameLen				= MAX_SYMBOL_NAME_LENGTH;

	// Initialize line number info.
	memset( &Line, 0, sizeof(Line) );
	Line.SizeOfStruct				= sizeof(Line);

	// Load symbols.
	SymInitialize( GetCurrentProcess(), ".", 1 );

	while( 1 )
	{
		if( !StackWalk64( IMAGE_FILE_MACHINE_I386, Process, Thread, &StackFrame, ExceptionInfo->ContextRecord, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL ) )
			break;

		// Warning - ANSI_TO_TCHAR uses alloca which might not be safe during an exception handler - INVESTIGATE!
		if( SymFromAddr( Process, StackFrame.AddrPC.Offset, &Offset64, Symbol ) && SymGetLineFromAddr64( Process, StackFrame.AddrPC.Offset, &Offset, &Line ) )
		{
			appStrncat( GErrorHist, *FString::Printf(TEXT("%s [Line %i] <- "),ANSI_TO_TCHAR(Symbol->Name), Line.LineNumber), ARRAY_COUNT(GErrorHist) );
			debugf(TEXT("%45s   Line %4i of %s"), ANSI_TO_TCHAR(Symbol->Name), Line.LineNumber, ANSI_TO_TCHAR(Line.FileName) );
		}
	}

	SymCleanup( Process );
	appSystemFree( Symbol );
	
	return EXCEPTION_EXECUTE_HANDLER;
}