summaryrefslogtreecommitdiff
path: root/tier0/stacktools.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /tier0/stacktools.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'tier0/stacktools.cpp')
-rw-r--r--tier0/stacktools.cpp1707
1 files changed, 1707 insertions, 0 deletions
diff --git a/tier0/stacktools.cpp b/tier0/stacktools.cpp
new file mode 100644
index 0000000..7b0da4f
--- /dev/null
+++ b/tier0/stacktools.cpp
@@ -0,0 +1,1707 @@
+//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
+//
+// Purpose:
+//
+// $Workfile: $
+// $NoKeywords: $
+//===========================================================================//
+
+#include "pch_tier0.h"
+#include "tier0/stacktools.h"
+#include "tier0/threadtools.h"
+#include "tier0/icommandline.h"
+
+#include "tier0/valve_off.h"
+
+#if defined( PLATFORM_WINDOWS_PC )
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <dbghelp.h>
+#endif
+
+#if defined( PLATFORM_X360 )
+#include <xbdm.h>
+#include "xbox/xbox_console.h"
+#include "xbox/xbox_vxconsole.h"
+#include <map>
+#include <set>
+#endif
+
+#if defined( LINUX )
+#include <execinfo.h>
+#endif
+
+#include "tier0/valve_on.h"
+
+#include "tier0/memdbgon.h"
+
+
+#if !defined( ENABLE_RUNTIME_STACK_TRANSLATION ) //disable the whole toolset
+
+#if defined( LINUX )
+
+int GetCallStack( void **pReturnAddressesOut, int iArrayCount, int iSkipCount )
+{
+ return backtrace( pReturnAddressesOut, iArrayCount );
+}
+
+int GetCallStack_Fast( void **pReturnAddressesOut, int iArrayCount, int iSkipCount )
+{
+ return backtrace( pReturnAddressesOut, iArrayCount );
+}
+
+#else
+
+int GetCallStack( void **pReturnAddressesOut, int iArrayCount, int iSkipCount )
+{
+ return 0;
+}
+
+int GetCallStack_Fast( void **pReturnAddressesOut, int iArrayCount, int iSkipCount )
+{
+ return 0;
+}
+
+#endif
+
+//where we'll find our PDB's for win32. Translation will not work until this has been called once (even if with NULL)
+void SetStackTranslationSymbolSearchPath( const char *szSemicolonSeparatedList )
+{
+}
+
+void StackToolsNotify_LoadedLibrary( const char *szLibName )
+{
+}
+
+int TranslateStackInfo( const void * const *pCallStack, int iCallStackCount, tchar *szOutput, int iOutBufferSize, const tchar *szEntrySeparator, TranslateStackInfo_StyleFlags_t style )
+{
+ if( iOutBufferSize > 0 )
+ *szOutput = '\0';
+
+ return 0;
+}
+
+void PreloadStackInformation( const void **pAddresses, int iAddressCount )
+{
+}
+
+bool GetFileAndLineFromAddress( const void *pAddress, tchar *pFileNameOut, int iMaxFileNameLength, uint32 &iLineNumberOut, uint32 *pDisplacementOut )
+{
+ if( iMaxFileNameLength > 0 )
+ *pFileNameOut = '\0';
+
+ return false;
+}
+
+bool GetSymbolNameFromAddress( const void *pAddress, tchar *pSymbolNameOut, int iMaxSymbolNameLength, uint64 *pDisplacementOut )
+{
+ if( iMaxSymbolNameLength > 0 )
+ *pSymbolNameOut = '\0';
+
+ return false;
+}
+
+bool GetModuleNameFromAddress( const void *pAddress, tchar *pModuleNameOut, int iMaxModuleNameLength )
+{
+ if( iMaxModuleNameLength > 0 )
+ *pModuleNameOut = '\0';
+
+ return false;
+}
+
+#else //#if !defined( ENABLE_RUNTIME_STACK_TRANSLATION )
+
+//===============================================================================================================
+// Shared Windows/X360 code
+//===============================================================================================================
+
+CTHREADLOCALPTR( CStackTop_Base ) g_StackTop;
+
+class CStackTop_FriendFuncs : public CStackTop_Base
+{
+public:
+ friend int AppendParentStackTrace( void **pReturnAddressesOut, int iArrayCount, int iAlreadyFilled );
+ friend int GetCallStack_Fast( void **pReturnAddressesOut, int iArrayCount, int iSkipCount );
+};
+
+
+inline int AppendParentStackTrace( void **pReturnAddressesOut, int iArrayCount, int iAlreadyFilled )
+{
+ CStackTop_FriendFuncs *pTop = (CStackTop_FriendFuncs *)(CStackTop_Base *)g_StackTop;
+ if( pTop != NULL )
+ {
+ if( pTop->m_pReplaceAddress != NULL )
+ {
+ for( int i = iAlreadyFilled; --i >= 0; )
+ {
+ if( pReturnAddressesOut[i] == pTop->m_pReplaceAddress )
+ {
+ iAlreadyFilled = i;
+ break;
+ }
+ }
+ }
+
+ if( pTop->m_iParentStackTraceLength != 0 )
+ {
+ int iCopy = MIN( iArrayCount - iAlreadyFilled, pTop->m_iParentStackTraceLength );
+ memcpy( pReturnAddressesOut + iAlreadyFilled, pTop->m_pParentStackTrace, iCopy * sizeof( void * ) );
+ iAlreadyFilled += iCopy;
+ }
+ }
+
+ return iAlreadyFilled;
+}
+
+inline bool ValidStackAddress( void *pAddress, const void *pNoLessThan, const void *pNoGreaterThan )
+{
+ if( (uintp)pAddress & 3 )
+ return false;
+ if( pAddress < pNoLessThan ) //frame pointer traversal should always increase the pointer
+ return false;
+ if( pAddress > pNoGreaterThan ) //never traverse outside the stack (Oh 0xCCCCCCCC, how I hate you)
+ return false;
+
+#if defined( WIN32 ) && !defined( _X360 ) && 1
+ if( IsBadReadPtr( pAddress, (sizeof( void * ) * 2) ) ) //safety net, but also throws an exception (handled internally) to stop bad access
+ return false;
+#endif
+
+ return true;
+}
+
+#pragma auto_inline( off )
+int GetCallStack_Fast( void **pReturnAddressesOut, int iArrayCount, int iSkipCount )
+{
+ //Only tested in windows. This function won't work with frame pointer omission enabled. "vpc /nofpo" all projects
+#if (defined( TIER0_FPO_DISABLED ) || defined( _DEBUG )) &&\
+ (defined( WIN32 ) && !defined( _X360 ) && !defined(_M_X64))
+ void *pStackCrawlEBP;
+ __asm
+ {
+ mov [pStackCrawlEBP], ebp;
+ }
+
+ /*
+ With frame pointer omission disabled, this should be the pattern all the way up the stack
+ [ebp+00] Old ebp value
+ [ebp+04] Return address
+ */
+
+ void *pNoLessThan = pStackCrawlEBP; //impossible for a valid stack to traverse before this address
+ int i;
+
+ CStackTop_FriendFuncs *pTop = (CStackTop_FriendFuncs *)(CStackTop_Base *)g_StackTop;
+ if( pTop != NULL ) //we can do fewer error checks if we have a valid reference point for the top of the stack
+ {
+ void *pNoGreaterThan = pTop->m_pStackBase;
+
+ //skips
+ for( i = 0; i != iSkipCount; ++i )
+ {
+ if( (pStackCrawlEBP < pNoLessThan) || (pStackCrawlEBP > pNoGreaterThan) )
+ return AppendParentStackTrace( pReturnAddressesOut, iArrayCount, 0 );
+
+ pNoLessThan = pStackCrawlEBP;
+ pStackCrawlEBP = *(void **)pStackCrawlEBP; //should be pointing at old ebp value
+ }
+
+ //store
+ for( i = 0; i != iArrayCount; ++i )
+ {
+ if( (pStackCrawlEBP < pNoLessThan) || (pStackCrawlEBP > pNoGreaterThan) )
+ break;
+
+ pReturnAddressesOut[i] = *((void **)pStackCrawlEBP + 1);
+
+ pNoLessThan = pStackCrawlEBP;
+ pStackCrawlEBP = *(void **)pStackCrawlEBP; //should be pointing at old ebp value
+ }
+
+ return AppendParentStackTrace( pReturnAddressesOut, iArrayCount, i );
+ }
+ else
+ {
+ void *pNoGreaterThan = ((unsigned char *)pNoLessThan) + (1024 * 1024); //standard stack is 1MB. TODO: Get actual stack end address if available since this check isn't foolproof
+
+ //skips
+ for( i = 0; i != iSkipCount; ++i )
+ {
+ if( !ValidStackAddress( pStackCrawlEBP, pNoLessThan, pNoGreaterThan ) )
+ return AppendParentStackTrace( pReturnAddressesOut, iArrayCount, 0 );
+
+ pNoLessThan = pStackCrawlEBP;
+ pStackCrawlEBP = *(void **)pStackCrawlEBP; //should be pointing at old ebp value
+ }
+
+ //store
+ for( i = 0; i != iArrayCount; ++i )
+ {
+ if( !ValidStackAddress( pStackCrawlEBP, pNoLessThan, pNoGreaterThan ) )
+ break;
+
+ pReturnAddressesOut[i] = *((void **)pStackCrawlEBP + 1);
+
+ pNoLessThan = pStackCrawlEBP;
+ pStackCrawlEBP = *(void **)pStackCrawlEBP; //should be pointing at old ebp value
+ }
+
+ return AppendParentStackTrace( pReturnAddressesOut, iArrayCount, i );
+ }
+#endif
+
+ return 0;
+}
+#pragma auto_inline( on )
+
+
+
+
+
+
+#if defined( WIN32 ) && !defined( _X360 )
+//===============================================================================================================
+// Windows version of the toolset
+//===============================================================================================================
+
+
+#if defined( TIER0_FPO_DISABLED )
+//# define USE_CAPTURESTACKBACKTRACE //faster than StackWalk64, but only works on XP or newer and only with Frame Pointer Omission optimization disabled(/Oy-) for every function it traces through
+#endif
+
+
+#if defined(_M_IX86) || defined(_M_X64)
+# define USE_STACKWALK64
+# if defined(_M_IX86)
+# define STACKWALK64_MACHINETYPE IMAGE_FILE_MACHINE_I386
+# else
+# define STACKWALK64_MACHINETYPE IMAGE_FILE_MACHINE_AMD64
+# endif
+#endif
+
+typedef DWORD (WINAPI *PFN_SymGetOptions)( VOID );
+typedef DWORD (WINAPI *PFN_SymSetOptions)( IN DWORD SymOptions );
+typedef BOOL (WINAPI *PFN_SymSetSearchPath)( IN HANDLE hProcess, IN PSTR SearchPath );
+typedef BOOL (WINAPI *PFN_SymInitialize)( IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess );
+typedef BOOL (WINAPI *PFN_SymCleanup)( IN HANDLE hProcess );
+typedef BOOL (WINAPI *PFN_SymEnumerateModules64)( IN HANDLE hProcess, IN PSYM_ENUMMODULES_CALLBACK64 EnumModulesCallback, IN PVOID UserContext );
+typedef BOOL (WINAPI *PFN_EnumerateLoadedModules64)( IN HANDLE hProcess, IN PENUMLOADED_MODULES_CALLBACK64 EnumLoadedModulesCallback, IN PVOID UserContext );
+typedef DWORD64 (WINAPI *PFN_SymLoadModule64)( IN HANDLE hProcess, IN HANDLE hFile, IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll );
+typedef BOOL (WINAPI *PFN_SymUnloadModule64)( IN HANDLE hProcess, IN DWORD64 BaseOfDll );
+typedef BOOL (WINAPI *PFN_SymFromAddr)( IN HANDLE hProcess, IN DWORD64 Address, OUT PDWORD64 Displacement, IN OUT PSYMBOL_INFO Symbol );
+typedef BOOL (WINAPI *PFN_SymGetLineFromAddr64)( IN HANDLE hProcess, IN DWORD64 qwAddr, OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line64 );
+typedef BOOL (WINAPI *PFN_SymGetModuleInfo64)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PIMAGEHLP_MODULE64 ModuleInfo );
+typedef BOOL (WINAPI *PFN_StackWalk64)( DWORD MachineType, HANDLE hProcess, HANDLE hThread, LPSTACKFRAME64 StackFrame, PVOID ContextRecord, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress );
+typedef USHORT (WINAPI *PFN_CaptureStackBackTrace)( IN ULONG FramesToSkip, IN ULONG FramesToCapture, OUT PVOID *BackTrace, OUT OPTIONAL PULONG BackTraceHash );
+
+
+DWORD WINAPI SymGetOptions_DummyFn( VOID )
+{
+ return 0;
+}
+
+DWORD WINAPI SymSetOptions_DummyFn( IN DWORD SymOptions )
+{
+ return 0;
+}
+
+BOOL WINAPI SymSetSearchPath_DummyFn( IN HANDLE hProcess, IN PSTR SearchPath )
+{
+ return FALSE;
+}
+
+BOOL WINAPI SymInitialize_DummyFn( IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess )
+{
+ return FALSE;
+}
+
+BOOL WINAPI SymCleanup_DummyFn( IN HANDLE hProcess )
+{
+ return TRUE;
+}
+
+BOOL WINAPI SymEnumerateModules64_DummyFn( IN HANDLE hProcess, IN PSYM_ENUMMODULES_CALLBACK64 EnumModulesCallback, IN PVOID UserContext )
+{
+ return FALSE;
+}
+
+BOOL WINAPI EnumerateLoadedModules64_DummyFn( IN HANDLE hProcess, IN PENUMLOADED_MODULES_CALLBACK64 EnumLoadedModulesCallback, IN PVOID UserContext )
+{
+ return FALSE;
+}
+
+DWORD64 WINAPI SymLoadModule64_DummyFn( IN HANDLE hProcess, IN HANDLE hFile, IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll )
+{
+ return 0;
+}
+
+BOOL WINAPI SymUnloadModule64_DummyFn( IN HANDLE hProcess, IN DWORD64 BaseOfDll )
+{
+ return FALSE;
+}
+
+BOOL WINAPI SymFromAddr_DummyFn( IN HANDLE hProcess, IN DWORD64 Address, OUT PDWORD64 Displacement, IN OUT PSYMBOL_INFO Symbol )
+{
+ return FALSE;
+}
+
+BOOL WINAPI SymGetLineFromAddr64_DummyFn( IN HANDLE hProcess, IN DWORD64 qwAddr, OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line64 )
+{
+ return FALSE;
+}
+
+BOOL WINAPI SymGetModuleInfo64_DummyFn( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PIMAGEHLP_MODULE64 ModuleInfo )
+{
+ return FALSE;
+}
+
+
+
+BOOL WINAPI StackWalk64_DummyFn( DWORD MachineType, HANDLE hProcess, HANDLE hThread, LPSTACKFRAME64 StackFrame, PVOID ContextRecord, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress )
+{
+ return FALSE;
+}
+
+USHORT WINAPI CaptureStackBackTrace_DummyFn( IN ULONG FramesToSkip, IN ULONG FramesToCapture, OUT PVOID *BackTrace, OUT OPTIONAL PULONG BackTraceHash )
+{
+ return 0;
+}
+
+class CHelperFunctionsLoader
+{
+public:
+ CHelperFunctionsLoader( void )
+ {
+ m_bIsInitialized = false;
+ m_bShouldReloadSymbols = false;
+ m_hDbgHelpDll = NULL;
+ m_szPDBSearchPath = NULL;
+ m_pSymInitialize = SymInitialize_DummyFn;
+ m_pSymCleanup = SymCleanup_DummyFn;
+ m_pSymSetOptions = SymSetOptions_DummyFn;
+ m_pSymGetOptions = SymGetOptions_DummyFn;
+ m_pSymSetSearchPath = SymSetSearchPath_DummyFn;
+ m_pSymEnumerateModules64 = SymEnumerateModules64_DummyFn;
+ m_pEnumerateLoadedModules64 = EnumerateLoadedModules64_DummyFn;
+ m_pSymLoadModule64 = SymLoadModule64_DummyFn;
+ m_pSymUnloadModule64 = SymUnloadModule64_DummyFn;
+ m_pSymFromAddr = SymFromAddr_DummyFn;
+ m_pSymGetLineFromAddr64 = SymGetLineFromAddr64_DummyFn;
+ m_pSymGetModuleInfo64 = SymGetModuleInfo64_DummyFn;
+
+#if defined( USE_STACKWALK64 )
+ m_pStackWalk64 = StackWalk64_DummyFn;
+#endif
+
+#if defined( USE_CAPTURESTACKBACKTRACE )
+ m_pCaptureStackBackTrace = CaptureStackBackTrace_DummyFn;
+ m_hNTDllDll = NULL;
+#endif
+ }
+
+ ~CHelperFunctionsLoader( void )
+ {
+ m_pSymCleanup( m_hProcess );
+
+ if( m_hDbgHelpDll != NULL )
+ ::FreeLibrary( m_hDbgHelpDll );
+
+#if defined( USE_CAPTURESTACKBACKTRACE )
+ if( m_hNTDllDll != NULL )
+ ::FreeLibrary( m_hNTDllDll );
+#endif
+
+ if( m_szPDBSearchPath != NULL )
+ delete []m_szPDBSearchPath;
+ }
+
+ static BOOL CALLBACK UnloadSymbolsCallback( PSTR ModuleName, DWORD64 BaseOfDll, PVOID UserContext )
+ {
+ const CHelperFunctionsLoader *pThis = ((CHelperFunctionsLoader *)UserContext);
+ pThis->m_pSymUnloadModule64( pThis->m_hProcess, BaseOfDll );
+ return TRUE;
+ }
+
+#if _MSC_VER >= 1600
+ static BOOL CALLBACK LoadSymbolsCallback( PCSTR ModuleName, DWORD64 ModuleBase, ULONG ModuleSize, PVOID UserContext )
+#else
+ static BOOL CALLBACK LoadSymbolsCallback( PSTR ModuleName, DWORD64 ModuleBase, ULONG ModuleSize, PVOID UserContext )
+#endif
+ {
+ const CHelperFunctionsLoader *pThis = ((CHelperFunctionsLoader *)UserContext);
+ //SymLoadModule64( IN HANDLE hProcess, IN HANDLE hFile, IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll );
+ pThis->m_pSymLoadModule64( pThis->m_hProcess, NULL, (PSTR)ModuleName, (PSTR)ModuleName, ModuleBase, ModuleSize );
+ return TRUE;
+ }
+
+ void TryLoadingNewSymbols( void )
+ {
+ AUTO_LOCK( m_Mutex );
+
+ if( m_bIsInitialized )
+ {
+ //m_pSymEnumerateModules64( m_hProcess, UnloadSymbolsCallback, this ); //unloaded modules we've already loaded
+ m_pEnumerateLoadedModules64( m_hProcess, LoadSymbolsCallback, this ); //load everything
+ m_bShouldReloadSymbols = false;
+ }
+ }
+
+ void SetStackTranslationSymbolSearchPath( const char *szSemicolonSeparatedList )
+ {
+ AUTO_LOCK( m_Mutex );
+
+ if( m_szPDBSearchPath != NULL )
+ delete []m_szPDBSearchPath;
+
+ if( szSemicolonSeparatedList == NULL )
+ {
+ m_szPDBSearchPath = NULL;
+ return;
+ }
+
+ int iLength = (int)strlen( szSemicolonSeparatedList ) + 1;
+ char *pNewPath = new char [iLength];
+ memcpy( pNewPath, szSemicolonSeparatedList, iLength );
+ m_szPDBSearchPath = pNewPath;
+
+ //re-init search paths. Or if we haven't yet loaded dbghelp.dll, this will go to the dummy function and do nothing
+ m_pSymSetSearchPath( m_hProcess, m_szPDBSearchPath );
+ //TryLoadingNewSymbols();
+ m_bShouldReloadSymbols = true;
+ }
+
+ bool GetSymbolNameFromAddress( const void *pAddress, tchar *pSymbolNameOut, int iMaxSymbolNameLength, uint64 *pDisplacementOut )
+ {
+ if( pAddress == NULL )
+ return false;
+
+ AUTO_LOCK( m_Mutex );
+
+ unsigned char genericbuffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME*sizeof(TCHAR)];
+
+ ((PSYMBOL_INFO)genericbuffer)->SizeOfStruct = sizeof(SYMBOL_INFO);
+ ((PSYMBOL_INFO)genericbuffer)->MaxNameLen = MAX_SYM_NAME;
+
+ DWORD64 dwDisplacement;
+ if( m_pSymFromAddr( m_hProcess, (DWORD64)pAddress, &dwDisplacement, (PSYMBOL_INFO)genericbuffer) )
+ {
+ strncpy( pSymbolNameOut, ((PSYMBOL_INFO)genericbuffer)->Name, iMaxSymbolNameLength );
+ if( pDisplacementOut != NULL )
+ *pDisplacementOut = dwDisplacement;
+ return true;
+ }
+
+ return false;
+ }
+
+ bool GetFileAndLineFromAddress( const void *pAddress, tchar *pFileNameOut, int iMaxFileNameLength, uint32 &iLineNumberOut, uint32 *pDisplacementOut )
+ {
+ if( pAddress == NULL )
+ return false;
+
+ AUTO_LOCK( m_Mutex );
+
+ tchar szBuffer[1024];
+ szBuffer[0] = _T('\0');
+
+ IMAGEHLP_LINE64 imageHelpLine64;
+ imageHelpLine64.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+ imageHelpLine64.FileName = szBuffer;
+
+ DWORD dwDisplacement;
+ if( m_pSymGetLineFromAddr64( m_hProcess, (DWORD64)pAddress, &dwDisplacement, &imageHelpLine64 ) )
+ {
+ strncpy( pFileNameOut, imageHelpLine64.FileName, iMaxFileNameLength );
+ iLineNumberOut = imageHelpLine64.LineNumber;
+
+ if( pDisplacementOut != NULL )
+ *pDisplacementOut = dwDisplacement;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ bool GetModuleNameFromAddress( const void *pAddress, tchar *pModuleNameOut, int iMaxModuleNameLength )
+ {
+ AUTO_LOCK( m_Mutex );
+ IMAGEHLP_MODULE64 moduleInfo;
+
+ moduleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
+
+ if ( m_pSymGetModuleInfo64( m_hProcess, (DWORD64)pAddress, &moduleInfo ) )
+ {
+ strncpy( pModuleNameOut, moduleInfo.ModuleName, iMaxModuleNameLength );
+ return true;
+ }
+
+ return false;
+ }
+
+ //only returns false if we ran out of buffer space.
+ bool TranslatePointer( const void * const pAddress, tchar *pTranslationOut, int iTranslationBufferLength, TranslateStackInfo_StyleFlags_t style )
+ {
+ //AUTO_LOCK( m_Mutex );
+
+ if( pTranslationOut == NULL )
+ return false;
+
+ if( iTranslationBufferLength <= 0 )
+ return false;
+
+ //sample desired output
+ // valid translation - "tier0.dll!CHelperFunctionsLoader::TranslatePointer - u:\Dev\L4D\src\tier0\stacktools.cpp(162) + 4 bytes"
+ // fallback translation - "tier0.dll!0x01234567"
+
+ tchar *pWrite = pTranslationOut;
+ *pWrite = '\0';
+ int iLength;
+
+ if( style & TSISTYLEFLAG_MODULENAME )
+ {
+ if( !this->GetModuleNameFromAddress( pAddress, pWrite, iTranslationBufferLength ) )
+ strncpy( pWrite, "unknown_module", iTranslationBufferLength );
+
+ iLength = (int)strlen( pWrite );
+ pWrite += iLength;
+ iTranslationBufferLength -= iLength;
+
+ if( iTranslationBufferLength < 2 )
+ return false; //need more buffer
+
+ if( style & TSISTYLEFLAG_SYMBOLNAME )
+ {
+ *pWrite = '!';
+ ++pWrite;
+ --iTranslationBufferLength;
+ *pWrite = '\0';
+ }
+ }
+
+ //use symbol name to test if the rest is going to work. So grab it whether they want it or not
+ if( !this->GetSymbolNameFromAddress( pAddress, pWrite, iTranslationBufferLength, NULL ) )
+ {
+ int nBytesWritten = _snprintf( pWrite, iTranslationBufferLength, "0x%p", pAddress );
+ if ( nBytesWritten < 0 )
+ {
+ *pWrite = '\0'; // if we can't write all of the line/lineandoffset, don't write any at all
+ return false;
+ }
+ return true;
+ }
+ else if( style & TSISTYLEFLAG_SYMBOLNAME )
+ {
+ iLength = (int)strlen( pWrite );
+ pWrite += iLength;
+ iTranslationBufferLength -= iLength;
+ }
+ else
+ {
+ *pWrite = '\0'; //symbol name lookup worked, but unwanted, discard
+ }
+
+ if( style & (TSISTYLEFLAG_FULLPATH | TSISTYLEFLAG_SHORTPATH | TSISTYLEFLAG_LINE | TSISTYLEFLAG_LINEANDOFFSET) )
+ {
+ if( pWrite != pTranslationOut ) //if we've written anything yet, separate the printed data from the file name and line
+ {
+ if( iTranslationBufferLength < 6 )
+ return false; //need more buffer
+
+ pWrite[0] = ' '; //append " - "
+ pWrite[1] = '-';
+ pWrite[2] = ' ';
+ pWrite[3] = '\0';
+ pWrite += 3;
+ iTranslationBufferLength -= 3;
+ }
+
+ uint32 iLine;
+ uint32 iDisplacement;
+ char szFileName[MAX_PATH];
+ if( this->GetFileAndLineFromAddress( pAddress, szFileName, MAX_PATH, iLine, &iDisplacement ) )
+ {
+ if( style & TSISTYLEFLAG_FULLPATH )
+ {
+ iLength = (int)strlen( szFileName );
+ if ( iTranslationBufferLength < iLength + 1 )
+ return false;
+
+ memcpy( pWrite, szFileName, iLength + 1 );
+ pWrite += iLength;
+ iTranslationBufferLength -= iLength;
+ }
+ else if( style & TSISTYLEFLAG_SHORTPATH )
+ {
+ //shorten the path and copy
+ iLength = (int)strlen( szFileName );
+ char *pShortened = szFileName + iLength;
+ int iSlashesAllowed = 3;
+ while( pShortened > szFileName )
+ {
+ if( (*pShortened == '\\') || (*pShortened == '/') )
+ {
+ --iSlashesAllowed;
+ if( iSlashesAllowed == 0 )
+ break;
+ }
+
+ --pShortened;
+ }
+
+ iLength = (int)strlen( pShortened );
+ if( iTranslationBufferLength < iLength + 1 )
+ {
+ //Remove the " - " that we can't append to
+ pWrite -= 3;
+ iTranslationBufferLength += 3;
+ *pWrite = '\0';
+ return false;
+ }
+
+ memcpy( pWrite, szFileName, iLength + 1 );
+ pWrite += iLength;
+ iTranslationBufferLength -= iLength;
+ }
+
+ if( style & (TSISTYLEFLAG_LINE | TSISTYLEFLAG_LINEANDOFFSET) )
+ {
+ int nBytesWritten = _snprintf( pWrite, iTranslationBufferLength, ((style & TSISTYLEFLAG_LINEANDOFFSET) && (iDisplacement != 0)) ? "(%d) + %d bytes" : "(%d)", iLine, iDisplacement );
+ if ( nBytesWritten < 0 )
+ {
+ *pWrite = '\0'; // if we can't write all of the line/lineandoffset, don't write any at all
+ return false;
+ }
+
+ pWrite += nBytesWritten;
+ iTranslationBufferLength -= nBytesWritten;
+ }
+ }
+ else
+ {
+ //Remove the " - " that we didn't append to
+ pWrite -= 3;
+ iTranslationBufferLength += 3;
+ *pWrite = '\0';
+ }
+ }
+
+ return true;
+ }
+
+
+ //about to actually use the functions, load if necessary
+ void EnsureReady( void )
+ {
+ if( m_bIsInitialized )
+ {
+ if( m_bShouldReloadSymbols )
+ TryLoadingNewSymbols();
+
+ return;
+ }
+
+ AUTO_LOCK( m_Mutex );
+
+ //Only enabled for P4 and Steam Beta builds
+ if( (CommandLine()->FindParm( "-steam" ) != 0) && //is steam
+ (CommandLine()->FindParm( "-internalbuild" ) == 0) ) //is not steam beta
+ {
+ //disable the toolset by falsifying initialized state
+ m_bIsInitialized = true;
+ return;
+ }
+
+ m_hProcess = GetCurrentProcess();
+ if( m_hProcess == NULL )
+ return;
+
+ m_bIsInitialized = true;
+
+ // get the function pointer directly so that we don't have to include the .lib, and that
+ // we can easily change it to using our own dll when this code is used on win98/ME/2K machines
+ m_hDbgHelpDll = ::LoadLibrary( "DbgHelp.dll" );
+ if ( !m_hDbgHelpDll )
+ {
+ //it's possible it's just way too early to initialize (as shown with attempts at using these tools in the memory allocator)
+ if( m_szPDBSearchPath == NULL ) //not a rock solid check, but pretty good compromise between endless failing initialization and general failure due to trying too early
+ m_bIsInitialized = false;
+
+ return;
+ }
+
+ m_pSymInitialize = (PFN_SymInitialize) ::GetProcAddress( m_hDbgHelpDll, "SymInitialize" );
+ if( m_pSymInitialize == NULL )
+ {
+ //very bad
+ ::FreeLibrary( m_hDbgHelpDll );
+ m_hDbgHelpDll = NULL;
+ m_pSymInitialize = SymInitialize_DummyFn;
+ return;
+ }
+
+ m_pSymCleanup = (PFN_SymCleanup) ::GetProcAddress( m_hDbgHelpDll, "SymCleanup" );
+ if( m_pSymCleanup == NULL )
+ m_pSymCleanup = SymCleanup_DummyFn;
+
+ m_pSymGetOptions = (PFN_SymGetOptions) ::GetProcAddress( m_hDbgHelpDll, "SymGetOptions" );
+ if( m_pSymGetOptions == NULL )
+ m_pSymGetOptions = SymGetOptions_DummyFn;
+
+ m_pSymSetOptions = (PFN_SymSetOptions) ::GetProcAddress( m_hDbgHelpDll, "SymSetOptions" );
+ if( m_pSymSetOptions == NULL )
+ m_pSymSetOptions = SymSetOptions_DummyFn;
+
+ m_pSymSetSearchPath = (PFN_SymSetSearchPath) ::GetProcAddress( m_hDbgHelpDll, "SymSetSearchPath" );
+ if( m_pSymSetSearchPath == NULL )
+ m_pSymSetSearchPath = SymSetSearchPath_DummyFn;
+
+ m_pSymEnumerateModules64 = (PFN_SymEnumerateModules64) ::GetProcAddress( m_hDbgHelpDll, "SymEnumerateModules64" );
+ if( m_pSymEnumerateModules64 == NULL )
+ m_pSymEnumerateModules64 = SymEnumerateModules64_DummyFn;
+
+ m_pEnumerateLoadedModules64 = (PFN_EnumerateLoadedModules64) ::GetProcAddress( m_hDbgHelpDll, "EnumerateLoadedModules64" );
+ if( m_pEnumerateLoadedModules64 == NULL )
+ m_pEnumerateLoadedModules64 = EnumerateLoadedModules64_DummyFn;
+
+ m_pSymLoadModule64 = (PFN_SymLoadModule64) ::GetProcAddress( m_hDbgHelpDll, "SymLoadModule64" );
+ if( m_pSymLoadModule64 == NULL )
+ m_pSymLoadModule64 = SymLoadModule64_DummyFn;
+
+ m_pSymUnloadModule64 = (PFN_SymUnloadModule64) ::GetProcAddress( m_hDbgHelpDll, "SymUnloadModule64" );
+ if( m_pSymUnloadModule64 == NULL )
+ m_pSymUnloadModule64 = SymUnloadModule64_DummyFn;
+
+ m_pSymFromAddr = (PFN_SymFromAddr) ::GetProcAddress( m_hDbgHelpDll, "SymFromAddr" );
+ if( m_pSymFromAddr == NULL )
+ m_pSymFromAddr = SymFromAddr_DummyFn;
+
+ m_pSymGetLineFromAddr64 = (PFN_SymGetLineFromAddr64) ::GetProcAddress( m_hDbgHelpDll, "SymGetLineFromAddr64" );
+ if( m_pSymGetLineFromAddr64 == NULL )
+ m_pSymGetLineFromAddr64 = SymGetLineFromAddr64_DummyFn;
+
+ m_pSymGetModuleInfo64 = (PFN_SymGetModuleInfo64) ::GetProcAddress( m_hDbgHelpDll, "SymGetModuleInfo64" );
+ if( m_pSymGetModuleInfo64 == NULL )
+ m_pSymGetModuleInfo64 = SymGetModuleInfo64_DummyFn;
+
+#if defined( USE_STACKWALK64 )
+ m_pStackWalk64 = (PFN_StackWalk64) ::GetProcAddress( m_hDbgHelpDll, "StackWalk64" );
+ if( m_pStackWalk64 == NULL )
+ m_pStackWalk64 = StackWalk64_DummyFn;
+#endif
+
+
+#if defined( USE_CAPTURESTACKBACKTRACE )
+ m_hNTDllDll = ::LoadLibrary( "ntdll.dll" );
+
+ m_pCaptureStackBackTrace = (PFN_CaptureStackBackTrace) ::GetProcAddress( m_hNTDllDll, "RtlCaptureStackBackTrace" );
+ if( m_pCaptureStackBackTrace == NULL )
+ m_pCaptureStackBackTrace = CaptureStackBackTrace_DummyFn;
+#endif
+
+
+ m_pSymSetOptions( m_pSymGetOptions() |
+ SYMOPT_DEFERRED_LOADS | //load on demand
+ SYMOPT_EXACT_SYMBOLS | //don't load the wrong file
+ SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS | //don't prompt ever
+ SYMOPT_LOAD_LINES ); //load line info
+
+ m_pSymInitialize( m_hProcess, m_szPDBSearchPath, FALSE );
+ TryLoadingNewSymbols();
+ }
+
+ bool m_bIsInitialized;
+ bool m_bShouldReloadSymbols;
+ HANDLE m_hProcess;
+ HMODULE m_hDbgHelpDll;
+ char *m_szPDBSearchPath;
+ CThreadFastMutex m_Mutex; //DbgHelp functions are all single threaded.
+
+ PFN_SymInitialize m_pSymInitialize;
+ PFN_SymCleanup m_pSymCleanup;
+ PFN_SymGetOptions m_pSymGetOptions;
+ PFN_SymSetOptions m_pSymSetOptions;
+ PFN_SymSetSearchPath m_pSymSetSearchPath;
+ PFN_SymEnumerateModules64 m_pSymEnumerateModules64;
+ PFN_EnumerateLoadedModules64 m_pEnumerateLoadedModules64;
+ PFN_SymLoadModule64 m_pSymLoadModule64;
+ PFN_SymUnloadModule64 m_pSymUnloadModule64;
+
+ PFN_SymFromAddr m_pSymFromAddr;
+ PFN_SymGetLineFromAddr64 m_pSymGetLineFromAddr64;
+ PFN_SymGetModuleInfo64 m_pSymGetModuleInfo64;
+
+#if defined( USE_STACKWALK64 )
+ PFN_StackWalk64 m_pStackWalk64;
+#endif
+
+#if defined( USE_CAPTURESTACKBACKTRACE )
+ HMODULE m_hNTDllDll;
+ PFN_CaptureStackBackTrace m_pCaptureStackBackTrace;
+#endif
+};
+static CHelperFunctionsLoader s_HelperFunctions;
+
+
+
+#if defined( USE_STACKWALK64 ) //most reliable method thanks to boatloads of windows helper functions. Also the slowest.
+int CrawlStack_StackWalk64( CONTEXT *pExceptionContext, void **pReturnAddressesOut, int iArrayCount, int iSkipCount )
+{
+ s_HelperFunctions.EnsureReady();
+
+ AUTO_LOCK( s_HelperFunctions.m_Mutex );
+
+ CONTEXT currentContext;
+ memcpy( &currentContext, pExceptionContext, sizeof( CONTEXT ) );
+
+ STACKFRAME64 sfFrame = { 0 }; //memset(&sfFrame, 0x0, sizeof(sfFrame));
+ sfFrame.AddrPC.Mode = sfFrame.AddrFrame.Mode = AddrModeFlat;
+#ifdef _M_X64
+ sfFrame.AddrPC.Offset = currentContext.Rip;
+ sfFrame.AddrFrame.Offset = currentContext.Rbp; // ????
+#else
+ sfFrame.AddrPC.Offset = currentContext.Eip;
+ sfFrame.AddrFrame.Offset = currentContext.Ebp;
+#endif
+
+ HANDLE hThread = GetCurrentThread();
+
+ int i;
+ for( i = 0; i != iSkipCount; ++i ) //skip entries that the requesting function thinks are uninformative
+ {
+ if(!s_HelperFunctions.m_pStackWalk64( STACKWALK64_MACHINETYPE, s_HelperFunctions.m_hProcess, hThread, &sfFrame, &currentContext, NULL, NULL, NULL, NULL ) ||
+ (sfFrame.AddrFrame.Offset == 0) )
+ {
+ return 0;
+ }
+ }
+
+ for( i = 0; i != iArrayCount; ++i )
+ {
+ if(!s_HelperFunctions.m_pStackWalk64( STACKWALK64_MACHINETYPE, s_HelperFunctions.m_hProcess, hThread, &sfFrame, &currentContext, NULL, NULL, NULL, NULL ) ||
+ (sfFrame.AddrFrame.Offset == 0) )
+ {
+ break;
+ }
+ pReturnAddressesOut[i] = (void *)sfFrame.AddrPC.Offset;
+ }
+
+ return i;
+}
+
+void GetCallStackReturnAddresses_Exception( void **CallStackReturnAddresses, int *pRetCount, int iSkipCount, _EXCEPTION_POINTERS * pExceptionInfo )
+{
+ int iCount = CrawlStack_StackWalk64( pExceptionInfo->ContextRecord, CallStackReturnAddresses, *pRetCount, iSkipCount + 1 ); //skipping RaiseException()
+ *pRetCount = iCount;
+}
+#endif //#if defined( USE_STACKWALK64 )
+
+
+
+int GetCallStack( void **pReturnAddressesOut, int iArrayCount, int iSkipCount )
+{
+ s_HelperFunctions.EnsureReady();
+
+ ++iSkipCount; //skip this function
+
+#if defined( USE_CAPTURESTACKBACKTRACE )
+ if( s_HelperFunctions.m_pCaptureStackBackTrace != CaptureStackBackTrace_DummyFn )
+ {
+ //docs state a total limit of 63 back traces between skipped and stored
+ int iRetVal = s_HelperFunctions.m_pCaptureStackBackTrace( iSkipCount, MIN( iArrayCount, 63 - iSkipCount ), pReturnAddressesOut, NULL );
+ return AppendParentStackTrace( pReturnAddressesOut, iArrayCount, iRetVal );
+ }
+#endif
+#if defined( USE_STACKWALK64 )
+ if( s_HelperFunctions.m_pStackWalk64 != StackWalk64_DummyFn )
+ {
+ int iInOutArrayCount = iArrayCount; //array count becomes both input and output with exception handler version
+ __try
+ {
+ ::RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, NULL );
+ }
+ __except ( GetCallStackReturnAddresses_Exception( pReturnAddressesOut, &iInOutArrayCount, iSkipCount, GetExceptionInformation() ), EXCEPTION_EXECUTE_HANDLER )
+ {
+ return AppendParentStackTrace( pReturnAddressesOut, iArrayCount, iInOutArrayCount );
+ }
+ }
+#endif
+
+ return GetCallStack_Fast( pReturnAddressesOut, iArrayCount, iSkipCount );
+}
+
+void SetStackTranslationSymbolSearchPath( const char *szSemicolonSeparatedList )
+{
+ s_HelperFunctions.SetStackTranslationSymbolSearchPath( szSemicolonSeparatedList );
+}
+
+void StackToolsNotify_LoadedLibrary( const char *szLibName )
+{
+ s_HelperFunctions.m_bShouldReloadSymbols = true;
+}
+
+int TranslateStackInfo( const void * const *pCallStack, int iCallStackCount, tchar *szOutput, int iOutBufferSize, const tchar *szEntrySeparator, TranslateStackInfo_StyleFlags_t style )
+{
+ s_HelperFunctions.EnsureReady();
+ tchar *szStartOutput = szOutput;
+
+ if( szEntrySeparator == NULL )
+ szEntrySeparator = _T("");
+
+ int iSeparatorSize = (int)strlen( szEntrySeparator );
+
+ for( int i = 0; i < iCallStackCount; ++i )
+ {
+ if( !s_HelperFunctions.TranslatePointer( pCallStack[i], szOutput, iOutBufferSize, style ) )
+ {
+ return i;
+ }
+
+ int iLength = (int)strlen( szOutput );
+ szOutput += iLength;
+ iOutBufferSize -= iLength;
+
+ if( iOutBufferSize > iSeparatorSize )
+ {
+ memcpy( szOutput, szEntrySeparator, iSeparatorSize * sizeof( tchar ) );
+ szOutput += iSeparatorSize;
+ iOutBufferSize -= iSeparatorSize;
+ }
+ *szOutput = '\0';
+ }
+
+ szOutput -= iSeparatorSize;
+ if( szOutput >= szStartOutput )
+ *szOutput = '\0';
+
+ return iCallStackCount;
+}
+
+void PreloadStackInformation( void * const *pAddresses, int iAddressCount )
+{
+ //nop on anything but 360
+}
+
+bool GetFileAndLineFromAddress( const void *pAddress, tchar *pFileNameOut, int iMaxFileNameLength, uint32 &iLineNumberOut, uint32 *pDisplacementOut )
+{
+ s_HelperFunctions.EnsureReady();
+ return s_HelperFunctions.GetFileAndLineFromAddress( pAddress, pFileNameOut, iMaxFileNameLength, iLineNumberOut, pDisplacementOut );
+}
+
+bool GetSymbolNameFromAddress( const void *pAddress, tchar *pSymbolNameOut, int iMaxSymbolNameLength, uint64 *pDisplacementOut )
+{
+ s_HelperFunctions.EnsureReady();
+ return s_HelperFunctions.GetSymbolNameFromAddress( pAddress, pSymbolNameOut, iMaxSymbolNameLength, pDisplacementOut );
+}
+
+bool GetModuleNameFromAddress( const void *pAddress, tchar *pModuleNameOut, int iMaxModuleNameLength )
+{
+ s_HelperFunctions.EnsureReady();
+ return s_HelperFunctions.GetModuleNameFromAddress( pAddress, pModuleNameOut, iMaxModuleNameLength );
+}
+
+#else //#if defined( WIN32 ) && !defined( _X360 )
+
+//===============================================================================================================
+// X360 version of the toolset
+//===============================================================================================================
+
+class C360StackTranslationHelper
+{
+public:
+ C360StackTranslationHelper( void )
+ {
+ m_bInitialized = true;
+ }
+
+ ~C360StackTranslationHelper( void )
+ {
+ StringSet_t::const_iterator iter;
+
+ //module names
+ {
+ iter = m_ModuleNameSet.begin();
+ while(iter != m_ModuleNameSet.end())
+ {
+ char *pModuleName = (char*)(*iter);
+ delete []pModuleName;
+ iter++;
+ }
+ m_ModuleNameSet.clear();
+ }
+
+ //file names
+ {
+ iter = m_FileNameSet.begin();
+ while(iter != m_FileNameSet.end())
+ {
+ char *pFileName = (char*)(*iter);
+ delete []pFileName;
+ iter++;
+ }
+ m_FileNameSet.clear();
+ }
+
+ //symbol names
+ {
+ iter = m_SymbolNameSet.begin();
+ while(iter != m_SymbolNameSet.end())
+ {
+ char *pSymbolName = (char*)(*iter);
+ delete []pSymbolName;
+ iter++;
+ }
+ m_SymbolNameSet.clear();
+ }
+
+ m_bInitialized = false;
+ }
+
+private:
+ struct StackAddressInfo_t;
+public:
+
+ inline StackAddressInfo_t *CreateEntry( const FullStackInfo_t &info )
+ {
+ std::pair<AddressInfoMapIter_t, bool> retval = m_AddressInfoMap.insert( AddressInfoMapEntry_t( info.pAddress, StackAddressInfo_t() ) );
+ if( retval.first->second.szModule != NULL )
+ return &retval.first->second; //already initialized
+
+ retval.first->second.iLine = info.iLine;
+
+
+ //share strings
+
+ //module
+ {
+ const char *pModuleName;
+ StringSet_t::const_iterator iter = m_ModuleNameSet.find( info.szModuleName );
+ if ( iter == m_ModuleNameSet.end() )
+ {
+ int nLen = strlen(info.szModuleName) + 1;
+ pModuleName = new char [nLen];
+ memcpy( (char *)pModuleName, info.szModuleName, nLen );
+ m_ModuleNameSet.insert( pModuleName );
+ }
+ else
+ {
+ pModuleName = (char *)(*iter);
+ }
+
+ retval.first->second.szModule = pModuleName;
+ }
+
+ //file
+ {
+ const char *pFileName;
+ StringSet_t::const_iterator iter = m_FileNameSet.find( info.szFileName );
+ if ( iter == m_FileNameSet.end() )
+ {
+ int nLen = strlen(info.szFileName) + 1;
+ pFileName = new char [nLen];
+ memcpy( (char *)pFileName, info.szFileName, nLen );
+ m_FileNameSet.insert( pFileName );
+ }
+ else
+ {
+ pFileName = (char *)(*iter);
+ }
+
+ retval.first->second.szFileName = pFileName;
+ }
+
+ //symbol
+ {
+ const char *pSymbolName;
+ StringSet_t::const_iterator iter = m_SymbolNameSet.find( info.szSymbol );
+ if ( iter == m_SymbolNameSet.end() )
+ {
+ int nLen = strlen(info.szSymbol) + 1;
+ pSymbolName = new char [nLen];
+ memcpy( (char *)pSymbolName, info.szSymbol, nLen );
+ m_SymbolNameSet.insert( pSymbolName );
+ }
+ else
+ {
+ pSymbolName = (char *)(*iter);
+ }
+
+ retval.first->second.szSymbol = pSymbolName;
+ }
+
+ return &retval.first->second;
+ }
+
+ inline StackAddressInfo_t *FindInfoEntry( const void *pAddress )
+ {
+ AddressInfoMapIter_t Iter = m_AddressInfoMap.find( pAddress );
+ if( Iter != m_AddressInfoMap.end() )
+ return &Iter->second;
+
+ return NULL;
+ }
+
+ inline int RetrieveStackInfo( const void * const *pAddresses, FullStackInfo_t *pReturnedStructs, int iAddressCount )
+ {
+ int ReturnedTranslatedCount = -1;
+ //construct the message
+ // Header Finished Count(out) Input Count Input Array Returned data write address
+ int iMessageSize = 2 + sizeof( int * ) + sizeof( uint32 ) + (sizeof( void * ) * iAddressCount) + sizeof( FullStackInfo_t * );
+ uint8 *pMessage = (uint8 *)stackalloc( iMessageSize );
+ uint8 *pMessageWrite = pMessage;
+ pMessageWrite[0] = XBX_DBG_BNH_STACKTRANSLATOR; //have this message handled by stack translator handler
+ pMessageWrite[1] = ST_BHC_GETTRANSLATIONINFO;
+ pMessageWrite += 2;
+ *(int **)pMessageWrite = (int *)BigDWord( (DWORD)&ReturnedTranslatedCount );
+ pMessageWrite += sizeof( int * );
+ *(uint32 *)pMessageWrite = (uint32)BigDWord( (DWORD)iAddressCount );
+ pMessageWrite += sizeof( uint32 );
+ memcpy( pMessageWrite, pAddresses, iAddressCount * sizeof( void * ) );
+ pMessageWrite += iAddressCount * sizeof( void * );
+ *(FullStackInfo_t **)pMessageWrite = (FullStackInfo_t *)BigDWord( (DWORD)pReturnedStructs );
+ bool bSuccess = XBX_SendBinaryData( pMessage, iMessageSize, false, 30000 );
+ ReturnedTranslatedCount = BigDWord( ReturnedTranslatedCount );
+
+ if( bSuccess && (ReturnedTranslatedCount > 0) )
+ {
+ return ReturnedTranslatedCount;
+ }
+ return 0;
+ }
+
+ inline StackAddressInfo_t *CreateEntry( const void *pAddress )
+ {
+ //ask VXConsole for information about the addresses we're clueless about
+
+ FullStackInfo_t ReturnedData;
+ ReturnedData.pAddress = pAddress;
+ ReturnedData.szFileName[0] = '\0'; //strncpy( ReturnedData.szFileName, "FileUninitialized", sizeof( ReturnedData.szFileName ) );
+ ReturnedData.szModuleName[0] = '\0'; //strncpy( ReturnedData.szModuleName, "ModuleUninitialized", sizeof( ReturnedData.szModuleName ) );
+ ReturnedData.szSymbol[0] = '\0'; //strncpy( ReturnedData.szSymbol, "SymbolUninitialized", sizeof( ReturnedData.szSymbol ) );
+ ReturnedData.iLine = 0;
+ ReturnedData.iSymbolOffset = 0;
+
+ int iTranslated = RetrieveStackInfo( &pAddress, &ReturnedData, 1 );
+
+ if( iTranslated == 1 )
+ {
+ //store
+ return CreateEntry( ReturnedData );
+ }
+
+ return FindInfoEntry( pAddress ); //probably won't work, but last ditch.
+ }
+
+ inline StackAddressInfo_t *FindOrCreateEntry( const void *pAddress )
+ {
+ StackAddressInfo_t *pReturn = FindInfoEntry( pAddress );
+ if( pReturn == NULL )
+ {
+ pReturn = CreateEntry( pAddress );
+ }
+
+ return pReturn;
+ }
+
+ inline void LoadStackInformation( void * const *pAddresses, int iAddressCount )
+ {
+ Assert( (iAddressCount > 0) && (pAddresses != NULL) );
+
+ int iNeedLoading = 0;
+ void **pNeedLoading = (void **)stackalloc( sizeof( const void * ) * iAddressCount ); //addresses we need to ask VXConsole about
+
+ for( int i = 0; i != iAddressCount; ++i )
+ {
+ if( FindInfoEntry( pAddresses[i] ) == NULL )
+ {
+ //need to load this address
+ pNeedLoading[iNeedLoading] = pAddresses[i];
+ ++iNeedLoading;
+ }
+ }
+
+ if( iNeedLoading != 0 )
+ {
+ //ask VXConsole for information about the addresses we're clueless about
+ FullStackInfo_t *pReturnedStructs = (FullStackInfo_t *)stackalloc( sizeof( FullStackInfo_t ) * iNeedLoading );
+
+ for( int i = 0; i < iNeedLoading; ++i )
+ {
+ pReturnedStructs[i].pAddress = 0;
+ pReturnedStructs[i].szFileName[0] = '\0'; //strncpy( pReturnedStructs[i].szFileName, "FileUninitialized", sizeof( pReturnedStructs[i].szFileName ) );
+ pReturnedStructs[i].szModuleName[0] = '\0'; //strncpy( pReturnedStructs[i].szModuleName, "ModuleUninitialized", sizeof( pReturnedStructs[i].szModuleName ) );
+ pReturnedStructs[i].szSymbol[0] = '\0'; //strncpy( pReturnedStructs[i].szSymbol, "SymbolUninitialized", sizeof( pReturnedStructs[i].szSymbol ) );
+ pReturnedStructs[i].iLine = 0;
+ pReturnedStructs[i].iSymbolOffset = 0;
+ }
+
+ int iTranslated = RetrieveStackInfo( pNeedLoading, pReturnedStructs, iNeedLoading );
+
+ if( iTranslated == iNeedLoading )
+ {
+ //store
+ for( int i = 0; i < iTranslated; ++i )
+ {
+ CreateEntry( pReturnedStructs[i] );
+ }
+ }
+ }
+ }
+
+ inline bool GetFileAndLineFromAddress( const void *pAddress, tchar *pFileNameOut, int iMaxFileNameLength, uint32 &iLineNumberOut, uint32 *pDisplacementOut )
+ {
+ StackAddressInfo_t *pInfo = FindOrCreateEntry( pAddress );
+ if( pInfo && (pInfo->szFileName[0] != '\0') )
+ {
+ strncpy( pFileNameOut, pInfo->szFileName, iMaxFileNameLength );
+ iLineNumberOut = pInfo->iLine;
+
+ if( pDisplacementOut )
+ *pDisplacementOut = 0; //can't get line displacement on 360
+
+ return true;
+ }
+
+ return false;
+ }
+
+ inline bool GetSymbolNameFromAddress( const void *pAddress, tchar *pSymbolNameOut, int iMaxSymbolNameLength, uint64 *pDisplacementOut )
+ {
+ StackAddressInfo_t *pInfo = FindOrCreateEntry( pAddress );
+ if( pInfo && (pInfo->szSymbol[0] != '\0') )
+ {
+ strncpy( pSymbolNameOut, pInfo->szSymbol, iMaxSymbolNameLength );
+
+ if( pDisplacementOut )
+ *pDisplacementOut = pInfo->iSymbolOffset;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ inline bool GetModuleNameFromAddress( const void *pAddress, tchar *pModuleNameOut, int iMaxModuleNameLength )
+ {
+ StackAddressInfo_t *pInfo = FindOrCreateEntry( pAddress );
+ if( pInfo && (pInfo->szModule[0] != '\0') )
+ {
+ strncpy( pModuleNameOut, pInfo->szModule, iMaxModuleNameLength );
+ return true;
+ }
+
+ return false;
+ }
+
+
+ CThreadFastMutex m_hMutex;
+
+private:
+
+#pragma pack(push)
+#pragma pack(1)
+ struct StackAddressInfo_t
+ {
+ StackAddressInfo_t( void ) : szModule(NULL), szFileName(NULL), szSymbol(NULL), iLine(0), iSymbolOffset(0) {}
+ const char *szModule;
+ const char *szFileName;
+ const char *szSymbol;
+ uint32 iLine;
+ uint32 iSymbolOffset;
+ };
+#pragma pack(pop)
+
+ typedef std::map< const void *, StackAddressInfo_t, std::less<const void *>> AddressInfoMap_t;
+ typedef AddressInfoMap_t::iterator AddressInfoMapIter_t;
+ typedef AddressInfoMap_t::value_type AddressInfoMapEntry_t;
+
+ class CStringLess
+ {
+ public:
+ bool operator()(const char *pszLeft, const char *pszRight ) const
+ {
+ return ( V_tier0_stricmp( pszLeft, pszRight ) < 0 );
+ }
+ };
+
+ typedef std::set<const char *, CStringLess> StringSet_t;
+
+ AddressInfoMap_t m_AddressInfoMap; //TODO: retire old entries?
+ StringSet_t m_ModuleNameSet;
+ StringSet_t m_FileNameSet;
+ StringSet_t m_SymbolNameSet;
+ bool m_bInitialized;
+
+};
+static C360StackTranslationHelper s_360StackTranslator;
+
+
+int GetCallStack( void **pReturnAddressesOut, int iArrayCount, int iSkipCount )
+{
+ ++iSkipCount; //skip this function
+
+ //DmCaptureStackBackTrace() has no skip functionality, so we need to grab everything and skip within that list
+ void **pAllResults = (void **)stackalloc( sizeof( void * ) * (iArrayCount + iSkipCount) );
+ if( DmCaptureStackBackTrace( iArrayCount + iSkipCount, pAllResults ) == XBDM_NOERR )
+ {
+ for( int i = 0; i != iSkipCount; ++i )
+ {
+ if( *pAllResults == NULL ) //DmCaptureStackBackTrace() NULL terminates the list instead of telling us how many were returned
+ return AppendParentStackTrace( pReturnAddressesOut, iArrayCount, 0 );
+
+ ++pAllResults; //move the pointer forward so the second loop indices match up
+ }
+
+ for( int i = 0; i != iArrayCount; ++i )
+ {
+ if( pAllResults[i] == NULL ) //DmCaptureStackBackTrace() NULL terminates the list instead of telling us how many were returned
+ return AppendParentStackTrace( pReturnAddressesOut, iArrayCount, i );
+
+ pReturnAddressesOut[i] = pAllResults[i];
+ }
+
+ return iArrayCount; //no room to append parent
+ }
+
+ return GetCallStack_Fast( pReturnAddressesOut, iArrayCount, iSkipCount );
+}
+
+
+void SetStackTranslationSymbolSearchPath( const char *szSemicolonSeparatedList )
+{
+ //nop on 360
+}
+
+
+void StackToolsNotify_LoadedLibrary( const char *szLibName )
+{
+ //send off the notice to VXConsole
+ uint8 message[2];
+ message[0] = XBX_DBG_BNH_STACKTRANSLATOR; //have this message handled by stack translator handler
+ message[1] = ST_BHC_LOADEDLIBARY; //loaded a library notification
+ XBX_SendBinaryData( message, 2 );
+}
+
+
+int TranslateStackInfo( const void * const *pCallStack, int iCallStackCount, tchar *szOutput, int iOutBufferSize, const tchar *szEntrySeparator, TranslateStackInfo_StyleFlags_t style )
+{
+ if( iCallStackCount == 0 )
+ {
+ if( iOutBufferSize > 1 )
+ *szOutput = '\0';
+
+ return 0;
+ }
+
+ if( szEntrySeparator == NULL )
+ szEntrySeparator = "";
+
+ int iSeparatorLength = strlen( szEntrySeparator ) + 1;
+ int iDataSize = (sizeof( void * ) * iCallStackCount) + (iSeparatorLength * sizeof( tchar )) + 1; //1 for style flags
+
+ //360 is incapable of translation on it's own. Encode the stack for translation in VXConsole
+ //Encoded section is as such ":CSDECODE[encoded binary]"
+ int iEncodedSize = -EncodeBinaryToString( NULL, iDataSize, NULL, 0 ); //get needed buffer size
+ static const tchar cControlPrefix[] = XBX_CALLSTACKDECODEPREFIX;
+ const size_t cControlLength = (sizeof( cControlPrefix )/sizeof(tchar)) - 1; //-1 to remove null terminator
+
+ if( iOutBufferSize > (iEncodedSize + (int)cControlLength + 2) ) //+2 for ']' and null term
+ {
+ COMPILE_TIME_ASSERT( TSISTYLEFLAG_LAST < (1<<8) ); //need to update the encoder/decoder to use more than a byte for style flags
+
+ uint8 *pData = (uint8 *)stackalloc( iDataSize );
+ pData[0] = (uint8)style;
+ memcpy( pData + 1, szEntrySeparator, iSeparatorLength * sizeof( tchar ) );
+ memcpy( pData + 1 + (iSeparatorLength * sizeof( tchar )), pCallStack, iCallStackCount * sizeof( void * ) );
+
+ memcpy( szOutput, XBX_CALLSTACKDECODEPREFIX, cControlLength * sizeof( tchar ) );
+ int iLength = cControlLength + EncodeBinaryToString( pData, iDataSize, &szOutput[cControlLength], (iOutBufferSize - cControlLength) );
+
+ szOutput[iLength] = ']';
+ szOutput[iLength + 1] = '\0';
+ }
+
+ return iCallStackCount;
+}
+
+void PreloadStackInformation( void * const *pAddresses, int iAddressCount )
+{
+ AUTO_LOCK( s_360StackTranslator.m_hMutex );
+ s_360StackTranslator.LoadStackInformation( pAddresses, iAddressCount );
+}
+
+bool GetFileAndLineFromAddress( const void *pAddress, tchar *pFileNameOut, int iMaxFileNameLength, uint32 &iLineNumberOut, uint32 *pDisplacementOut )
+{
+ AUTO_LOCK( s_360StackTranslator.m_hMutex );
+ return s_360StackTranslator.GetFileAndLineFromAddress( pAddress, pFileNameOut, iMaxFileNameLength, iLineNumberOut, pDisplacementOut );
+}
+
+bool GetSymbolNameFromAddress( const void *pAddress, tchar *pSymbolNameOut, int iMaxSymbolNameLength, uint64 *pDisplacementOut )
+{
+ AUTO_LOCK( s_360StackTranslator.m_hMutex );
+ return s_360StackTranslator.GetSymbolNameFromAddress( pAddress, pSymbolNameOut, iMaxSymbolNameLength, pDisplacementOut );
+}
+
+bool GetModuleNameFromAddress( const void *pAddress, tchar *pModuleNameOut, int iMaxModuleNameLength )
+{
+ AUTO_LOCK( s_360StackTranslator.m_hMutex );
+ return s_360StackTranslator.GetModuleNameFromAddress( pAddress, pModuleNameOut, iMaxModuleNameLength );
+}
+
+#endif //#else //#if defined( WIN32 ) && !defined( _X360 )
+
+#endif //#if !defined( ENABLE_RUNTIME_STACK_TRANSLATION )
+
+
+
+
+CCallStackStorage::CCallStackStorage( FN_GetCallStack GetStackFunction, uint32 iSkipCalls )
+{
+ iValidEntries = GetStackFunction( pStack, ARRAYSIZE( pStack ), iSkipCalls + 1 );
+}
+
+
+
+CStackTop_CopyParentStack::CStackTop_CopyParentStack( void * const *pParentStackTrace, int iParentStackTraceLength )
+{
+#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
+ //miniature version of GetCallStack_Fast()
+#if (defined( TIER0_FPO_DISABLED ) || defined( _DEBUG )) &&\
+ (defined( WIN32 ) && !defined( _X360 ) && !defined(_M_X64))
+ void *pStackCrawlEBP;
+ __asm
+ {
+ mov [pStackCrawlEBP], ebp;
+ }
+ pStackCrawlEBP = *(void **)pStackCrawlEBP;
+ m_pReplaceAddress = *((void **)pStackCrawlEBP + 1);
+ m_pStackBase = (void *)((void **)pStackCrawlEBP + 1);
+#else
+ m_pReplaceAddress = NULL;
+ m_pStackBase = this;
+#endif
+
+ m_pParentStackTrace = NULL;
+
+ if( (pParentStackTrace != NULL) && (iParentStackTraceLength > 0) )
+ {
+ while( (iParentStackTraceLength > 0) && (pParentStackTrace[iParentStackTraceLength - 1] == NULL) )
+ {
+ --iParentStackTraceLength;
+ }
+
+ if( iParentStackTraceLength > 0 )
+ {
+ m_pParentStackTrace = new void * [iParentStackTraceLength];
+ memcpy( (void **)m_pParentStackTrace, pParentStackTrace, sizeof( void * ) * iParentStackTraceLength );
+ }
+ }
+
+ m_iParentStackTraceLength = iParentStackTraceLength;
+
+ m_pPrevTop = g_StackTop;
+ g_StackTop = this;
+ Assert( (CStackTop_Base *)g_StackTop == this );
+#endif //#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
+}
+
+CStackTop_CopyParentStack::~CStackTop_CopyParentStack( void )
+{
+#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
+ Assert( (CStackTop_Base *)g_StackTop == this );
+ g_StackTop = m_pPrevTop;
+
+ if( m_pParentStackTrace != NULL )
+ {
+ delete []m_pParentStackTrace;
+ }
+#endif
+}
+
+
+
+CStackTop_ReferenceParentStack::CStackTop_ReferenceParentStack( void * const *pParentStackTrace, int iParentStackTraceLength )
+{
+#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
+ //miniature version of GetCallStack_Fast()
+#if (defined( TIER0_FPO_DISABLED ) || defined( _DEBUG )) &&\
+ (defined( WIN32 ) && !defined( _X360 ) && !defined(_M_X64))
+ void *pStackCrawlEBP;
+ __asm
+ {
+ mov [pStackCrawlEBP], ebp;
+ }
+ pStackCrawlEBP = *(void **)pStackCrawlEBP;
+ m_pReplaceAddress = *((void **)pStackCrawlEBP + 1);
+ m_pStackBase = (void *)((void **)pStackCrawlEBP + 1);
+#else
+ m_pReplaceAddress = NULL;
+ m_pStackBase = this;
+#endif
+
+ m_pParentStackTrace = pParentStackTrace;
+
+ if( (pParentStackTrace != NULL) && (iParentStackTraceLength > 0) )
+ {
+ while( (iParentStackTraceLength > 0) && (pParentStackTrace[iParentStackTraceLength - 1] == NULL) )
+ {
+ --iParentStackTraceLength;
+ }
+ }
+
+ m_iParentStackTraceLength = iParentStackTraceLength;
+
+ m_pPrevTop = g_StackTop;
+ g_StackTop = this;
+ Assert( (CStackTop_Base *)g_StackTop == this );
+#endif //#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
+}
+
+CStackTop_ReferenceParentStack::~CStackTop_ReferenceParentStack( void )
+{
+#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
+ Assert( (CStackTop_Base *)g_StackTop == this );
+ g_StackTop = m_pPrevTop;
+
+ ReleaseParentStackReferences();
+#endif
+}
+
+void CStackTop_ReferenceParentStack::ReleaseParentStackReferences( void )
+{
+#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
+ m_pParentStackTrace = NULL;
+ m_iParentStackTraceLength = 0;
+#endif
+}
+
+
+
+
+//Encodes data so that every byte's most significant bit is a 1. Ensuring no null terminators.
+//This puts the encoded data in the 128-255 value range. Leaving all standard ascii characters for control.
+//Returns string length (not including the written null terminator as is standard).
+//Or if the buffer is too small. Returns negative of necessary buffer size (including room needed for null terminator)
+int EncodeBinaryToString( const void *pToEncode, int iDataLength, char *pEncodeOut, int iEncodeBufferSize )
+{
+ int iEncodedSize = iDataLength;
+ iEncodedSize += (iEncodedSize + 6) / 7; //Have 1 control byte for every 7 actual bytes
+ iEncodedSize += sizeof( uint32 ) + 1; //data size at the beginning of the blob and null terminator at the end
+
+ if( (iEncodedSize > iEncodeBufferSize) || (pEncodeOut == NULL) || (pToEncode == NULL) )
+ return -iEncodedSize; //not enough room
+
+ uint8 *pEncodeWrite = (uint8 *)pEncodeOut;
+
+ //first encode the data size. Encodes lowest 28 bits and discards the high 4
+ pEncodeWrite[0] = ((iDataLength >> 21) & 0xFF) | 0x80;
+ pEncodeWrite[1] = ((iDataLength >> 14) & 0xFF) | 0x80;
+ pEncodeWrite[2] = ((iDataLength >> 7) & 0xFF) | 0x80;
+ pEncodeWrite[3] = ((iDataLength >> 0) & 0xFF) | 0x80;
+ pEncodeWrite += 4;
+
+ const uint8 *pEncodeRead = (const uint8 *)pToEncode;
+ const uint8 *pEncodeStop = pEncodeRead + iDataLength;
+ uint8 *pEncodeWriteLastControlByte = pEncodeWrite;
+ int iControl = 0;
+
+ //Encode the data
+ while( pEncodeRead < pEncodeStop )
+ {
+ if( iControl == 0 )
+ {
+ pEncodeWriteLastControlByte = pEncodeWrite;
+ *pEncodeWriteLastControlByte = 0x80;
+ }
+ else
+ {
+ *pEncodeWrite = *pEncodeRead | 0x80; //encoded data always has the MSB bit set (cheap avoidance of null terminators)
+ *pEncodeWriteLastControlByte |= (((*pEncodeRead) & 0x80) ^ 0x80) >> iControl; //We use the control byte to XOR the MSB back to original values on decode
+ ++pEncodeRead;
+ }
+
+ ++pEncodeWrite;
+ ++iControl;
+ iControl &= 7; //8->0
+ }
+ *pEncodeWrite = '\0';
+
+ return iEncodedSize - 1;
+}
+
+//Decodes a string produced by EncodeBinaryToString(). Safe to decode in place if you don't mind trashing your string, binary byte count always less than string byte count.
+//Returns:
+// >= 0 is the decoded data size
+// INT_MIN (most negative value possible) indicates an improperly formatted string (not our data)
+// all other negative values are the negative of how much dest buffer size is necessary.
+int DecodeBinaryFromString( const char *pString, void *pDestBuffer, int iDestBufferSize, char **ppParseFinishOut )
+{
+ const uint8 *pDecodeRead = (const uint8 *)pString;
+
+ if( (pDecodeRead[0] < 0x80) || (pDecodeRead[1] < 0x80) || (pDecodeRead[2] < 0x80) || (pDecodeRead[3] < 0x80) )
+ {
+ if( ppParseFinishOut != NULL )
+ *ppParseFinishOut = (char *)pString;
+
+ return INT_MIN; //Don't know what the string is, but it's not our format
+ }
+
+ int iDecodedSize = 0;
+ iDecodedSize |= (pDecodeRead[0] & 0x7F) << 21;
+ iDecodedSize |= (pDecodeRead[1] & 0x7F) << 14;
+ iDecodedSize |= (pDecodeRead[2] & 0x7F) << 7;
+ iDecodedSize |= (pDecodeRead[3] & 0x7F) << 0;
+ pDecodeRead += 4;
+
+ int iTextLength = iDecodedSize;
+ iTextLength += (iTextLength + 6) / 7; //Have 1 control byte for every 7 actual bytes
+
+ //make sure it's formatted properly
+ for( int i = 0; i != iTextLength; ++i )
+ {
+ if( pDecodeRead[i] < 0x80 ) //encoded data always has MSB set
+ {
+ if( ppParseFinishOut != NULL )
+ *ppParseFinishOut = (char *)pString;
+
+ return INT_MIN; //either not our data, or part of the string is missing
+ }
+ }
+
+ if( iDestBufferSize < iDecodedSize )
+ {
+ if( ppParseFinishOut != NULL )
+ *ppParseFinishOut = (char *)pDecodeRead;
+
+ return -iDecodedSize; //dest buffer not big enough to hold the data
+ }
+
+ const uint8 *pStopDecoding = pDecodeRead + iTextLength;
+ uint8 *pDecodeWrite = (uint8 *)pDestBuffer;
+ int iControl = 0;
+ int iLSBXOR = 0;
+
+ while( pDecodeRead < pStopDecoding )
+ {
+ if( iControl == 0 )
+ {
+ iLSBXOR = *pDecodeRead;
+ }
+ else
+ {
+ *pDecodeWrite = *pDecodeRead ^ ((iLSBXOR << iControl) & 0x80);
+ ++pDecodeWrite;
+ }
+
+ ++pDecodeRead;
+ ++iControl;
+ iControl &= 7; //8->0
+ }
+
+ if( ppParseFinishOut != NULL )
+ *ppParseFinishOut = (char *)pDecodeRead;
+
+ return iDecodedSize;
+}
+
+