示例#1
0
GCallStack::GCallStack(HANDLE hThread, CONTEXT& c)
{
	if (!_bLockInit) // only init the single instance of the CRITICAL_SECTION 1 time for the many instances of GCallStack
	{
		InitializeCriticalSection(&_DbgHelpLock);
		_bLockInit = true;
	}

	DWORD imageType = IMAGE_FILE_MACHINE_I386;
	HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, GetCurrentProcessId());
	int frameNum = 0; // counts walked frames
	int MAX_STACK_FRAMES = 7777; // in C# the maximum stack frames imposed by the language is 1000.  Arbitrary limit to guarantee no condition of infinate walking in corrupted memory.
	DWORD offsetFromLine; // tells us line number in the source file
#if defined(_LINUX64) || defined(_WIN64) || defined(_IOS)
	unsigned __int64 offsetFromSymbol; // tells us how far from the symbol we were
#else
	DWORD offsetFromSymbol; // tells us how far from the symbol we were
#endif

	DWORD symOptions; // symbol handler settings
	IMAGEHLP_SYMBOL *pSym = (IMAGEHLP_SYMBOL *) malloc( IMGSYMLEN + MAXNAMELEN );

	GString strStackName(MAXNAMELEN + 512); // undecorated method/function name + Source file and line number

	IMAGEHLP_MODULE Module;
	IMAGEHLP_LINE Line;
	STACKFRAME64 s; // in/out stackframe
	memset( &s, '\0', sizeof s );

//  note: converted code from [std::string symSearchPath] to [GString symSearchPath] so it will compile with the _UNICODE build flag - 8/18/2014
	GString symSearchPath;

#ifdef _UNICODE
	wchar_t *tt = 0, *p;
	tt = new wchar_t[TTBUFLEN];
#else
	char *tt = 0, *p;
	tt = new char[TTBUFLEN];
#endif

	// build symbol search path from:
	symSearchPath = "";
	// current directory
	if (GetCurrentDirectory(TTBUFLEN, tt))
		symSearchPath << tt << "; ";
	// dir with executable
	if ( GetModuleFileName( 0, tt, TTBUFLEN ) )
	{
#ifdef _UNICODE
		for (p = tt + wcslen(tt) - 1; p >= tt; --p)
#else
		for (p = tt + strlen(tt) - 1; p >= tt; --p)	// VC6 does not have a _tcsclen() and we still support VC6
#endif
		{
			// locate the rightmost path separator
			if ( *p == '\\' || *p == '/' || *p == ':' )
				break;
		}
		// if we found one, p is pointing at it; if not, tt only contains an exe name (no path), and p points before its first byte
		if ( p != tt ) // path sep found?
		{
			if ( *p == ':' ) // we leave colons in place
				++ p;
			*p = '\0'; // eliminate the exe name and last path sep
			symSearchPath << tt << "; "; 
		}
	}
	// environment variable _NT_SYMBOL_PATH
	GString g("_NT_SYMBOL_PATH");
	if (GetEnvironmentVariable(g, tt, TTBUFLEN))
		symSearchPath << tt << "; ";
	// environment variable _NT_ALTERNATE_SYMBOL_PATH
	g = "_NT_ALTERNATE_SYMBOL_PATH";
	if (GetEnvironmentVariable(g, tt, TTBUFLEN))
		symSearchPath << tt << "; ";
	// environment variable SYSTEMROOT
	g = "SYSTEMROOT";
	if (GetEnvironmentVariable(g, tt, TTBUFLEN))
		symSearchPath << tt << "; ";

	if ( symSearchPath.size() > 0 ) // if we added anything, we have a trailing semicolon
		symSearchPath = symSearchPath.substr( 0, symSearchPath.size() - 1 );

	// 8/20/2014 note: In older Windows API's SymInitialize()'s 2nd argument was not defined as "const char *", it was only "char *" 
	// Although "const" was not defined, the API call is "const" in behavior.  In newer versions of the Windows API this has been fixed.
	// In newer versions - SymInitialize's 2nd argument may resolve to either "const char *" OR "const wchar_t *", and in those builds the
	// GString has a default conversion to the correct string type, however in the older build configurations, GString does not (and should not)
	// know how to resolve to a "char *" by default, so in that case the preprocessor directive isolates the code needed to convert to "char *" 

#if defined(_MSC_VER) && _MSC_VER <= 1200
	if (!SymInitialize(hProcess, symSearchPath.Buf(),	false))	// symSearchPath == (char *)
#else
	if (!SymInitialize(hProcess, symSearchPath,			true))  // symSearchPath == (const char *)  --OR--  (const wchar_t *) depending on the _UNICODE preprocessor definition
#endif
	{
		goto tagCleanUp;
	}

	symOptions = SymGetOptions();
	symOptions |= SYMOPT_LOAD_LINES;
	symOptions &= ~SYMOPT_UNDNAME;
	SymSetOptions( symOptions );

	enumAndLoadModuleSymbols( hProcess, GetCurrentProcessId() );

	// init STACKFRAME for first call, definitions found in ImageHlp.h
#ifdef _M_IX86
	imageType = IMAGE_FILE_MACHINE_I386;
	s.AddrPC.Offset = c.Eip;
	s.AddrPC.Mode = AddrModeFlat;
	s.AddrFrame.Offset = c.Ebp;
	s.AddrFrame.Mode = AddrModeFlat;
	s.AddrStack.Offset = c.Esp;
	s.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
	imageType = IMAGE_FILE_MACHINE_AMD64;
	s.AddrPC.Offset = c.Rip;
	s.AddrPC.Mode = AddrModeFlat;
	s.AddrFrame.Offset = c.Rsp;
	s.AddrFrame.Mode = AddrModeFlat;
	s.AddrStack.Offset = c.Rsp;
	s.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
	imageType = IMAGE_FILE_MACHINE_IA64;
	s.AddrPC.Offset = c.StIIP;
	s.AddrPC.Mode = AddrModeFlat;
	s.AddrFrame.Offset = c.IntSp;
	s.AddrFrame.Mode = AddrModeFlat;
	s.AddrBStore.Offset = c.RsBSP;
	s.AddrBStore.Mode = AddrModeFlat;
	s.AddrStack.Offset = c.IntSp;
	s.AddrStack.Mode = AddrModeFlat;
#endif

	memset( pSym, '\0', IMGSYMLEN + MAXNAMELEN );
	pSym->SizeOfStruct = IMGSYMLEN;
	pSym->MaxNameLength = MAXNAMELEN;

	memset( &Line, '\0', sizeof Line );
	Line.SizeOfStruct = sizeof Line;

	memset( &Module, '\0', sizeof Module );
	Module.SizeOfStruct = sizeof Module;

	offsetFromSymbol = 0;

	
	//	DbgHelp is single threaded, so acquire a lock.
	EnterCriticalSection(&_DbgHelpLock);

	while ( frameNum < MAX_STACK_FRAMES )
	{
		// get next stack frame (StackWalk(), SymFunctionTableAccess(), SymGetModuleBase())
		// if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can
		// assume that either you are done, or that the stack is so hosed that the next deeper frame could not be found.
#ifdef _WIN64
		if (!StackWalk64(imageType, hProcess, hThread, &s, &c, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
#else
		if (!StackWalk(imageType, hProcess, hThread, &s, &c, NULL, SymFunctionTableAccess, SymGetModuleBase, NULL))
#endif
			break;  // Maybe it failed, maybe we have finished walking the stack

		if ( s.AddrPC.Offset != 0 )
		{ 
			// Most likely a valid stack rame
			
			// show procedure info 
			if ( ! SymGetSymFromAddr64( hProcess, s.AddrPC.Offset, &offsetFromSymbol, pSym ) )
			{
				break;
			}
			else
			{
				// UnDecorateSymbolName() to get the Class::Method or function() name in tyhe callstack
				strStackName.Empty();
 				UnDecorateSymbolName(pSym->Name, strStackName._str, MAXNAMELEN, UNDNAME_COMPLETE);
				strStackName.SetLength(strlen(strStackName._str));

				// SymGetLineFromAddr() to get the source.cpp and the line number 
				IMAGEHLP_LINE64 Line;
				if (SymGetLineFromAddr64(hProcess, s.AddrPC.Offset, &offsetFromLine, &Line) != FALSE)
				{
					GString g(Line.FileName);  // Line.FileName conains the "c:\Full\Path\TO\Source.cpp"
					
					// Builds string "Foo::Bar[Source.cpp]@777"
					strStackName << "[" << g.StartingAt(g.ReverseFind("\\") + 1) << "]@" << Line.LineNumber; 
				}

				// add the GString to the GStringList, do not add frame 0 because it will always be GException::GSeception where we divided by 0
				if (frameNum > 0)
					_stk += strStackName;
			}
		}
		else
		{
			// base reached
			SetLastError(0);
			break;
		}

		++frameNum;
	}
	
	LeaveCriticalSection(&_DbgHelpLock);


	// de-init symbol handler etc. (SymCleanup())
	SymCleanup( hProcess );
	free( pSym );
tagCleanUp:;	
	delete [] tt;
	CloseHandle(hProcess);
}
示例#2
0
	//*************************************************************************
	// Method:		GetStackInfo
	// Description: Gets the call stack for the specified thread
	//
	// Parameters:
	//	hThread - the handle to the target thread
	//	threadContext - the context of the target thread
	//
	// Return Value: (FrameInfo *) An array containing stack trace data
	//*************************************************************************
	SiUtils::SiArray <FrameInfo *> StackWalker::GetStackInfo(HANDLE hThread, CONTEXT &threadContext)
	{
		//Clear the frame array of any previous data
		frameArray.Clear();

		DWORD imageType = IMAGE_FILE_MACHINE_I386;
		HANDLE hProcess = GetCurrentProcess();
		
		int frameNum;						// counts walked frames
		DWORD offsetFromSymbol;				// tells us how far from the symbol we were
		DWORD symOptions;					// symbol handler settings
		IMAGEHLP_SYMBOL *pSym = (IMAGEHLP_SYMBOL *) malloc( IMGSYMLEN + MAXNAMELEN );
		char undName[MAXNAMELEN];			// undecorated name
		char undFullName[MAXNAMELEN];		// undecorated name with all shenanigans
		IMAGEHLP_MODULE Module;
		IMAGEHLP_LINE Line;
		std::string symSearchPath;
		char *tt = 0, *p;

		STACKFRAME s;						// in/out stackframe
		memset( &s, '\0', sizeof s );

		tt = new char[TTBUFLEN];
		// build symbol search path from:
		symSearchPath = "";
		// current directory
		if (GetCurrentDirectory(TTBUFLEN, tt ))
			symSearchPath += tt + std::string( ";" );
		
		// dir with executable
		if (GetModuleFileName(0, tt, TTBUFLEN))
		{
			for ( p = tt + strlen( tt ) - 1; p >= tt; -- p )
			{
				// locate the rightmost path separator
				if ( *p == '\\' || *p == '/' || *p == ':' )
					break;
			}
			
			if ( p != tt )
			{
				if ( *p == ':' ) // we leave colons in place
					++ p;
				*p = '\0'; // eliminate the exe name and last path sep
				symSearchPath += tt + std::string( ";" );
			}
		}
		
		// environment variable _NT_SYMBOL_PATH
		if ( GetEnvironmentVariable( "_NT_SYMBOL_PATH", tt, TTBUFLEN ) )
			symSearchPath += tt + std::string( ";" );
		// environment variable _NT_ALTERNATE_SYMBOL_PATH
		if ( GetEnvironmentVariable( "_NT_ALTERNATE_SYMBOL_PATH", tt, TTBUFLEN ) )
			symSearchPath += tt + std::string( ";" );
		// environment variable SYSTEMROOT
		if ( GetEnvironmentVariable( "SYSTEMROOT", tt, TTBUFLEN ) )
			symSearchPath += tt + std::string( ";" );

		if ( symSearchPath.size() > 0 ) // if we added anything, we have a trailing semicolon
			symSearchPath = symSearchPath.substr( 0, symSearchPath.size() - 1 );

		strncpy( tt, symSearchPath.c_str(), TTBUFLEN );
		tt[TTBUFLEN - 1] = '\0'; // if strncpy() overruns, it doesn't add the null terminator

		// init symbol handler stuff (SymInitialize())
		if (!SymInitialize(hProcess, tt, false ))
			goto cleanup;

		// SymGetOptions()
		symOptions = SymGetOptions();
		symOptions |= SYMOPT_LOAD_LINES;
		symOptions &= ~SYMOPT_UNDNAME;
		SymSetOptions( symOptions );

		// Enumerate modules and tell imagehlp.dll about them.
		enumAndLoadModuleSymbols( hProcess, GetCurrentProcessId() );

		// init STACKFRAME for first call
		// Notes: AddrModeFlat is just an assumption. I hate VDM debugging.
		// Notes: will have to be #ifdef-ed for Alphas; MIPSes are dead anyway,
		// and good riddance.
		s.AddrPC.Offset = threadContext.Eip;
		s.AddrPC.Mode = AddrModeFlat;
		s.AddrFrame.Offset = threadContext.Ebp;
		s.AddrFrame.Mode = AddrModeFlat;

		memset( pSym, '\0', IMGSYMLEN + MAXNAMELEN );
		pSym->SizeOfStruct = IMGSYMLEN;
		pSym->MaxNameLength = MAXNAMELEN;

		memset( &Line, '\0', sizeof Line );
		Line.SizeOfStruct = sizeof Line;

		memset( &Module, '\0', sizeof Module );
		Module.SizeOfStruct = sizeof Module;

		offsetFromSymbol = 0;

		for ( frameNum = 0; ; ++ frameNum )
		{
			FrameInfo * frameInfo = new FrameInfo();

			if (!StackWalk( imageType, hProcess, hThread, &s, &threadContext, NULL,
				SymFunctionTableAccess, SymGetModuleBase, NULL ) )
				break;

			frameInfo->frameNumber = frameNum;
			frameInfo->IsWOWFarCall = s.Far;
			frameInfo->IsVirtualFrame = s.Virtual;
			frameInfo->Eip = s.AddrPC.Offset;				//if 0, the no symbols
			frameInfo->ReturnAddr = s.AddrReturn.Offset;
			frameInfo->FramePtr = s.AddrFrame.Offset;
			frameInfo->StackPtr = s.AddrStack.Offset;

			if (s.AddrPC.Offset == 0)
			{
				//printf( "(-nosymbols- PC == 0)\n" );
			}
			else
			{ // we seem to have a valid PC
				// show procedure info (SymGetSymFromAddr())
				if ( ! SymGetSymFromAddr( hProcess, s.AddrPC.Offset, &offsetFromSymbol, pSym ) )
				{
					/*if ( GetLastError() != 487 )
						printf( "SymGetSymFromAddr(): GetLastError() = %lu\n", GetLastError() );*/
				}
				else
				{
					// UnDecorateSymbolName()
					UnDecorateSymbolName( pSym->Name, undName, MAXNAMELEN, UNDNAME_NAME_ONLY );
					UnDecorateSymbolName( pSym->Name, undFullName, MAXNAMELEN, UNDNAME_COMPLETE );

					strcpy (frameInfo->undecoratedName, undName);
					strcpy (frameInfo->undecoratedFullName, undFullName);
					strcpy (frameInfo->signature, pSym->Name );
					frameInfo->offsetFromSymbol = offsetFromSymbol;
				}

				// show module info (SymGetModuleInfo())
				if (SymGetModuleInfo( hProcess, s.AddrPC.Offset, &Module ) )
				{	
					strcpy (frameInfo->ModuleName, Module.ModuleName);
					strcpy (frameInfo->ImageName, Module.ImageName);
					frameInfo->BaseOfImage = Module.BaseOfImage;

				}

			} // we seem to have a valid PC

			// no return address means no deeper stackframe
			if ( s.AddrReturn.Offset == 0 )
			{
				// avoid misunderstandings in the printf() following the loop
				SetLastError( 0 );
				break;
			}
			
			this->frameArray.Add (frameInfo);

		} // for ( frameNum )

	cleanup:
		// de-init symbol handler etc. (SymCleanup())
		SymCleanup( hProcess );
		free( pSym );
		delete [] tt;
		return frameArray;
	}
GCallStack::GCallStack(HANDLE hThread, CONTEXT& c)
{
	DWORD imageType = IMAGE_FILE_MACHINE_I386;
	HANDLE hProcess = GetCurrentProcess();

	int frameNum; // counts walked frames

#if defined(_LINUX64) || defined(_WIN64) || defined(_IOS)
	unsigned __int64 offsetFromSymbol; // tells us how far from the symbol we were
#else
	DWORD offsetFromSymbol; // tells us how far from the symbol we were
#endif



	DWORD symOptions; // symbol handler settings
	IMAGEHLP_SYMBOL *pSym = (IMAGEHLP_SYMBOL *) malloc( IMGSYMLEN + MAXNAMELEN );
	char undFullName[MAXNAMELEN]; // undecorated name with all shenanigans

	IMAGEHLP_MODULE Module;
	IMAGEHLP_LINE Line;
	std::string symSearchPath;
	char *tt = 0, *p;

	STACKFRAME s; // in/out stackframe
	memset( &s, '\0', sizeof s );

	tt = new char[TTBUFLEN];

	// build symbol search path from:
	symSearchPath = "";
	// current directory
	if ( GetCurrentDirectory( TTBUFLEN, tt ) )
		symSearchPath += tt + std::string( ";" );
	// dir with executable
	if ( GetModuleFileName( 0, tt, TTBUFLEN ) )
	{
		for ( p = tt + strlen( tt ) - 1; p >= tt; -- p )
		{
			// locate the rightmost path separator
			if ( *p == '\\' || *p == '/' || *p == ':' )
				break;
		}
		// if we found one, p is pointing at it; if not, tt only contains
		// an exe name (no path), and p points before its first byte
		if ( p != tt ) // path sep found?
		{
			if ( *p == ':' ) // we leave colons in place
				++ p;
			*p = '\0'; // eliminate the exe name and last path sep
			symSearchPath += tt + std::string( ";" );
		}
	}
	// environment variable _NT_SYMBOL_PATH
	if ( GetEnvironmentVariable( "_NT_SYMBOL_PATH", tt, TTBUFLEN ) )
		symSearchPath += tt + std::string( ";" );
	// environment variable _NT_ALTERNATE_SYMBOL_PATH
	if ( GetEnvironmentVariable( "_NT_ALTERNATE_SYMBOL_PATH", tt, TTBUFLEN ) )
		symSearchPath += tt + std::string( ";" );
	// environment variable SYSTEMROOT
	if ( GetEnvironmentVariable( "SYSTEMROOT", tt, TTBUFLEN ) )
		symSearchPath += tt + std::string( ";" );

	if ( symSearchPath.size() > 0 ) // if we added anything, we have a trailing semicolon
		symSearchPath = symSearchPath.substr( 0, symSearchPath.size() - 1 );

	// why oh why does SymInitialize() want a writeable string?
	strncpy( tt, symSearchPath.c_str(), TTBUFLEN );
	tt[TTBUFLEN - 1] = '\0'; // if strncpy() overruns, it doesn't add the null terminator

	// init symbol handler stuff (SymInitialize())
	if ( ! SymInitialize( hProcess, tt, false ) )
	{
		goto tagCleanUp;
	}

	symOptions = SymGetOptions();
	symOptions |= SYMOPT_LOAD_LINES;
	symOptions &= ~SYMOPT_UNDNAME;
	SymSetOptions( symOptions ); // SymSetOptions()

	enumAndLoadModuleSymbols( hProcess, GetCurrentProcessId() );

	// init STACKFRAME for first call
	// Notes: will have to be #ifdef-ed for Alphas; MIPSes are dead anyway,
#ifndef _WIN64
	s.AddrPC.Offset = c.Eip;
	s.AddrPC.Mode = AddrModeFlat;
	s.AddrFrame.Offset = c.Ebp;
	s.AddrFrame.Mode = AddrModeFlat;
#endif

	memset( pSym, '\0', IMGSYMLEN + MAXNAMELEN );
	pSym->SizeOfStruct = IMGSYMLEN;
	pSym->MaxNameLength = MAXNAMELEN;

	memset( &Line, '\0', sizeof Line );
	Line.SizeOfStruct = sizeof Line;

	memset( &Module, '\0', sizeof Module );
	Module.SizeOfStruct = sizeof Module;

	offsetFromSymbol = 0;

	for ( frameNum = 0; ;)
	{
		// get next stack frame (StackWalk(), SymFunctionTableAccess(), SymGetModuleBase())
		// if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can
		// assume that either you are done, or that the stack is so hosed that the next
		// deeper frame could not be found.
		if ( ! StackWalk( imageType, hProcess, hThread, &s, &c, NULL,
			SymFunctionTableAccess, SymGetModuleBase, NULL ) )
			break;

		if ( s.AddrPC.Offset != 0 )
		{ // we seem to have a valid PC
			// show procedure info (SymGetSymFromAddr())
			if ( ! SymGetSymFromAddr( hProcess, s.AddrPC.Offset, &offsetFromSymbol, pSym ) )
			{
				break;
			}
			else
			{
				// UnDecorateSymbolName()
				UnDecorateSymbolName( pSym->Name, undFullName, MAXNAMELEN, UNDNAME_COMPLETE );
				if (!frameNum && strstr(undFullName, "Exception"))
					continue;
				_stk += undFullName;
			}
		} // we seem to have a valid PC

		// no return address means no deeper stackframe
		if ( s.AddrReturn.Offset == 0 )
		{
			// avoid misunderstandings in the printf() following the loop
			SetLastError( 0 );
			break;
		}
		++frameNum;
	}

	// de-init symbol handler etc. (SymCleanup())
	SymCleanup( hProcess );
	free( pSym );
tagCleanUp:;	
	delete [] tt;
}