Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1707 lines
52 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $Workfile: $
  6. // $NoKeywords: $
  7. //===========================================================================//
  8. #include "pch_tier0.h"
  9. #include "tier0/stacktools.h"
  10. #include "tier0/threadtools.h"
  11. #include "tier0/icommandline.h"
  12. #include "tier0/valve_off.h"
  13. #if defined( PLATFORM_WINDOWS_PC )
  14. #define WIN32_LEAN_AND_MEAN
  15. #include <windows.h>
  16. #include <dbghelp.h>
  17. #endif
  18. #if defined( PLATFORM_X360 )
  19. #include <xbdm.h>
  20. #include "xbox/xbox_console.h"
  21. #include "xbox/xbox_vxconsole.h"
  22. #include <map>
  23. #include <set>
  24. #endif
  25. #if defined( LINUX )
  26. #include <execinfo.h>
  27. #endif
  28. #include "tier0/valve_on.h"
  29. #include "tier0/memdbgon.h"
  30. #if !defined( ENABLE_RUNTIME_STACK_TRANSLATION ) //disable the whole toolset
  31. #if defined( LINUX )
  32. int GetCallStack( void **pReturnAddressesOut, int iArrayCount, int iSkipCount )
  33. {
  34. return backtrace( pReturnAddressesOut, iArrayCount );
  35. }
  36. int GetCallStack_Fast( void **pReturnAddressesOut, int iArrayCount, int iSkipCount )
  37. {
  38. return backtrace( pReturnAddressesOut, iArrayCount );
  39. }
  40. #else
  41. int GetCallStack( void **pReturnAddressesOut, int iArrayCount, int iSkipCount )
  42. {
  43. return 0;
  44. }
  45. int GetCallStack_Fast( void **pReturnAddressesOut, int iArrayCount, int iSkipCount )
  46. {
  47. return 0;
  48. }
  49. #endif
  50. //where we'll find our PDB's for win32. Translation will not work until this has been called once (even if with NULL)
  51. void SetStackTranslationSymbolSearchPath( const char *szSemicolonSeparatedList )
  52. {
  53. }
  54. void StackToolsNotify_LoadedLibrary( const char *szLibName )
  55. {
  56. }
  57. int TranslateStackInfo( const void * const *pCallStack, int iCallStackCount, tchar *szOutput, int iOutBufferSize, const tchar *szEntrySeparator, TranslateStackInfo_StyleFlags_t style )
  58. {
  59. if( iOutBufferSize > 0 )
  60. *szOutput = '\0';
  61. return 0;
  62. }
  63. void PreloadStackInformation( const void **pAddresses, int iAddressCount )
  64. {
  65. }
  66. bool GetFileAndLineFromAddress( const void *pAddress, tchar *pFileNameOut, int iMaxFileNameLength, uint32 &iLineNumberOut, uint32 *pDisplacementOut )
  67. {
  68. if( iMaxFileNameLength > 0 )
  69. *pFileNameOut = '\0';
  70. return false;
  71. }
  72. bool GetSymbolNameFromAddress( const void *pAddress, tchar *pSymbolNameOut, int iMaxSymbolNameLength, uint64 *pDisplacementOut )
  73. {
  74. if( iMaxSymbolNameLength > 0 )
  75. *pSymbolNameOut = '\0';
  76. return false;
  77. }
  78. bool GetModuleNameFromAddress( const void *pAddress, tchar *pModuleNameOut, int iMaxModuleNameLength )
  79. {
  80. if( iMaxModuleNameLength > 0 )
  81. *pModuleNameOut = '\0';
  82. return false;
  83. }
  84. #else //#if !defined( ENABLE_RUNTIME_STACK_TRANSLATION )
  85. //===============================================================================================================
  86. // Shared Windows/X360 code
  87. //===============================================================================================================
  88. CTHREADLOCALPTR( CStackTop_Base ) g_StackTop;
  89. class CStackTop_FriendFuncs : public CStackTop_Base
  90. {
  91. public:
  92. friend int AppendParentStackTrace( void **pReturnAddressesOut, int iArrayCount, int iAlreadyFilled );
  93. friend int GetCallStack_Fast( void **pReturnAddressesOut, int iArrayCount, int iSkipCount );
  94. };
  95. inline int AppendParentStackTrace( void **pReturnAddressesOut, int iArrayCount, int iAlreadyFilled )
  96. {
  97. CStackTop_FriendFuncs *pTop = (CStackTop_FriendFuncs *)(CStackTop_Base *)g_StackTop;
  98. if( pTop != NULL )
  99. {
  100. if( pTop->m_pReplaceAddress != NULL )
  101. {
  102. for( int i = iAlreadyFilled; --i >= 0; )
  103. {
  104. if( pReturnAddressesOut[i] == pTop->m_pReplaceAddress )
  105. {
  106. iAlreadyFilled = i;
  107. break;
  108. }
  109. }
  110. }
  111. if( pTop->m_iParentStackTraceLength != 0 )
  112. {
  113. int iCopy = MIN( iArrayCount - iAlreadyFilled, pTop->m_iParentStackTraceLength );
  114. memcpy( pReturnAddressesOut + iAlreadyFilled, pTop->m_pParentStackTrace, iCopy * sizeof( void * ) );
  115. iAlreadyFilled += iCopy;
  116. }
  117. }
  118. return iAlreadyFilled;
  119. }
  120. inline bool ValidStackAddress( void *pAddress, const void *pNoLessThan, const void *pNoGreaterThan )
  121. {
  122. if( (uintp)pAddress & 3 )
  123. return false;
  124. if( pAddress < pNoLessThan ) //frame pointer traversal should always increase the pointer
  125. return false;
  126. if( pAddress > pNoGreaterThan ) //never traverse outside the stack (Oh 0xCCCCCCCC, how I hate you)
  127. return false;
  128. #if defined( WIN32 ) && !defined( _X360 ) && 1
  129. if( IsBadReadPtr( pAddress, (sizeof( void * ) * 2) ) ) //safety net, but also throws an exception (handled internally) to stop bad access
  130. return false;
  131. #endif
  132. return true;
  133. }
  134. #pragma auto_inline( off )
  135. int GetCallStack_Fast( void **pReturnAddressesOut, int iArrayCount, int iSkipCount )
  136. {
  137. //Only tested in windows. This function won't work with frame pointer omission enabled. "vpc /nofpo" all projects
  138. #if (defined( TIER0_FPO_DISABLED ) || defined( _DEBUG )) &&\
  139. (defined( WIN32 ) && !defined( _X360 ) && !defined(_M_X64))
  140. void *pStackCrawlEBP;
  141. __asm
  142. {
  143. mov [pStackCrawlEBP], ebp;
  144. }
  145. /*
  146. With frame pointer omission disabled, this should be the pattern all the way up the stack
  147. [ebp+00] Old ebp value
  148. [ebp+04] Return address
  149. */
  150. void *pNoLessThan = pStackCrawlEBP; //impossible for a valid stack to traverse before this address
  151. int i;
  152. CStackTop_FriendFuncs *pTop = (CStackTop_FriendFuncs *)(CStackTop_Base *)g_StackTop;
  153. if( pTop != NULL ) //we can do fewer error checks if we have a valid reference point for the top of the stack
  154. {
  155. void *pNoGreaterThan = pTop->m_pStackBase;
  156. //skips
  157. for( i = 0; i != iSkipCount; ++i )
  158. {
  159. if( (pStackCrawlEBP < pNoLessThan) || (pStackCrawlEBP > pNoGreaterThan) )
  160. return AppendParentStackTrace( pReturnAddressesOut, iArrayCount, 0 );
  161. pNoLessThan = pStackCrawlEBP;
  162. pStackCrawlEBP = *(void **)pStackCrawlEBP; //should be pointing at old ebp value
  163. }
  164. //store
  165. for( i = 0; i != iArrayCount; ++i )
  166. {
  167. if( (pStackCrawlEBP < pNoLessThan) || (pStackCrawlEBP > pNoGreaterThan) )
  168. break;
  169. pReturnAddressesOut[i] = *((void **)pStackCrawlEBP + 1);
  170. pNoLessThan = pStackCrawlEBP;
  171. pStackCrawlEBP = *(void **)pStackCrawlEBP; //should be pointing at old ebp value
  172. }
  173. return AppendParentStackTrace( pReturnAddressesOut, iArrayCount, i );
  174. }
  175. else
  176. {
  177. 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
  178. //skips
  179. for( i = 0; i != iSkipCount; ++i )
  180. {
  181. if( !ValidStackAddress( pStackCrawlEBP, pNoLessThan, pNoGreaterThan ) )
  182. return AppendParentStackTrace( pReturnAddressesOut, iArrayCount, 0 );
  183. pNoLessThan = pStackCrawlEBP;
  184. pStackCrawlEBP = *(void **)pStackCrawlEBP; //should be pointing at old ebp value
  185. }
  186. //store
  187. for( i = 0; i != iArrayCount; ++i )
  188. {
  189. if( !ValidStackAddress( pStackCrawlEBP, pNoLessThan, pNoGreaterThan ) )
  190. break;
  191. pReturnAddressesOut[i] = *((void **)pStackCrawlEBP + 1);
  192. pNoLessThan = pStackCrawlEBP;
  193. pStackCrawlEBP = *(void **)pStackCrawlEBP; //should be pointing at old ebp value
  194. }
  195. return AppendParentStackTrace( pReturnAddressesOut, iArrayCount, i );
  196. }
  197. #endif
  198. return 0;
  199. }
  200. #pragma auto_inline( on )
  201. #if defined( WIN32 ) && !defined( _X360 )
  202. //===============================================================================================================
  203. // Windows version of the toolset
  204. //===============================================================================================================
  205. #if defined( TIER0_FPO_DISABLED )
  206. //# 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
  207. #endif
  208. #if defined(_M_IX86) || defined(_M_X64)
  209. # define USE_STACKWALK64
  210. # if defined(_M_IX86)
  211. # define STACKWALK64_MACHINETYPE IMAGE_FILE_MACHINE_I386
  212. # else
  213. # define STACKWALK64_MACHINETYPE IMAGE_FILE_MACHINE_AMD64
  214. # endif
  215. #endif
  216. typedef DWORD (WINAPI *PFN_SymGetOptions)( VOID );
  217. typedef DWORD (WINAPI *PFN_SymSetOptions)( IN DWORD SymOptions );
  218. typedef BOOL (WINAPI *PFN_SymSetSearchPath)( IN HANDLE hProcess, IN PSTR SearchPath );
  219. typedef BOOL (WINAPI *PFN_SymInitialize)( IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess );
  220. typedef BOOL (WINAPI *PFN_SymCleanup)( IN HANDLE hProcess );
  221. typedef BOOL (WINAPI *PFN_SymEnumerateModules64)( IN HANDLE hProcess, IN PSYM_ENUMMODULES_CALLBACK64 EnumModulesCallback, IN PVOID UserContext );
  222. typedef BOOL (WINAPI *PFN_EnumerateLoadedModules64)( IN HANDLE hProcess, IN PENUMLOADED_MODULES_CALLBACK64 EnumLoadedModulesCallback, IN PVOID UserContext );
  223. typedef DWORD64 (WINAPI *PFN_SymLoadModule64)( IN HANDLE hProcess, IN HANDLE hFile, IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll );
  224. typedef BOOL (WINAPI *PFN_SymUnloadModule64)( IN HANDLE hProcess, IN DWORD64 BaseOfDll );
  225. typedef BOOL (WINAPI *PFN_SymFromAddr)( IN HANDLE hProcess, IN DWORD64 Address, OUT PDWORD64 Displacement, IN OUT PSYMBOL_INFO Symbol );
  226. typedef BOOL (WINAPI *PFN_SymGetLineFromAddr64)( IN HANDLE hProcess, IN DWORD64 qwAddr, OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line64 );
  227. typedef BOOL (WINAPI *PFN_SymGetModuleInfo64)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PIMAGEHLP_MODULE64 ModuleInfo );
  228. 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 );
  229. typedef USHORT (WINAPI *PFN_CaptureStackBackTrace)( IN ULONG FramesToSkip, IN ULONG FramesToCapture, OUT PVOID *BackTrace, OUT OPTIONAL PULONG BackTraceHash );
  230. DWORD WINAPI SymGetOptions_DummyFn( VOID )
  231. {
  232. return 0;
  233. }
  234. DWORD WINAPI SymSetOptions_DummyFn( IN DWORD SymOptions )
  235. {
  236. return 0;
  237. }
  238. BOOL WINAPI SymSetSearchPath_DummyFn( IN HANDLE hProcess, IN PSTR SearchPath )
  239. {
  240. return FALSE;
  241. }
  242. BOOL WINAPI SymInitialize_DummyFn( IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess )
  243. {
  244. return FALSE;
  245. }
  246. BOOL WINAPI SymCleanup_DummyFn( IN HANDLE hProcess )
  247. {
  248. return TRUE;
  249. }
  250. BOOL WINAPI SymEnumerateModules64_DummyFn( IN HANDLE hProcess, IN PSYM_ENUMMODULES_CALLBACK64 EnumModulesCallback, IN PVOID UserContext )
  251. {
  252. return FALSE;
  253. }
  254. BOOL WINAPI EnumerateLoadedModules64_DummyFn( IN HANDLE hProcess, IN PENUMLOADED_MODULES_CALLBACK64 EnumLoadedModulesCallback, IN PVOID UserContext )
  255. {
  256. return FALSE;
  257. }
  258. DWORD64 WINAPI SymLoadModule64_DummyFn( IN HANDLE hProcess, IN HANDLE hFile, IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll )
  259. {
  260. return 0;
  261. }
  262. BOOL WINAPI SymUnloadModule64_DummyFn( IN HANDLE hProcess, IN DWORD64 BaseOfDll )
  263. {
  264. return FALSE;
  265. }
  266. BOOL WINAPI SymFromAddr_DummyFn( IN HANDLE hProcess, IN DWORD64 Address, OUT PDWORD64 Displacement, IN OUT PSYMBOL_INFO Symbol )
  267. {
  268. return FALSE;
  269. }
  270. BOOL WINAPI SymGetLineFromAddr64_DummyFn( IN HANDLE hProcess, IN DWORD64 qwAddr, OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line64 )
  271. {
  272. return FALSE;
  273. }
  274. BOOL WINAPI SymGetModuleInfo64_DummyFn( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PIMAGEHLP_MODULE64 ModuleInfo )
  275. {
  276. return FALSE;
  277. }
  278. 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 )
  279. {
  280. return FALSE;
  281. }
  282. USHORT WINAPI CaptureStackBackTrace_DummyFn( IN ULONG FramesToSkip, IN ULONG FramesToCapture, OUT PVOID *BackTrace, OUT OPTIONAL PULONG BackTraceHash )
  283. {
  284. return 0;
  285. }
  286. class CHelperFunctionsLoader
  287. {
  288. public:
  289. CHelperFunctionsLoader( void )
  290. {
  291. m_bIsInitialized = false;
  292. m_bShouldReloadSymbols = false;
  293. m_hDbgHelpDll = NULL;
  294. m_szPDBSearchPath = NULL;
  295. m_pSymInitialize = SymInitialize_DummyFn;
  296. m_pSymCleanup = SymCleanup_DummyFn;
  297. m_pSymSetOptions = SymSetOptions_DummyFn;
  298. m_pSymGetOptions = SymGetOptions_DummyFn;
  299. m_pSymSetSearchPath = SymSetSearchPath_DummyFn;
  300. m_pSymEnumerateModules64 = SymEnumerateModules64_DummyFn;
  301. m_pEnumerateLoadedModules64 = EnumerateLoadedModules64_DummyFn;
  302. m_pSymLoadModule64 = SymLoadModule64_DummyFn;
  303. m_pSymUnloadModule64 = SymUnloadModule64_DummyFn;
  304. m_pSymFromAddr = SymFromAddr_DummyFn;
  305. m_pSymGetLineFromAddr64 = SymGetLineFromAddr64_DummyFn;
  306. m_pSymGetModuleInfo64 = SymGetModuleInfo64_DummyFn;
  307. #if defined( USE_STACKWALK64 )
  308. m_pStackWalk64 = StackWalk64_DummyFn;
  309. #endif
  310. #if defined( USE_CAPTURESTACKBACKTRACE )
  311. m_pCaptureStackBackTrace = CaptureStackBackTrace_DummyFn;
  312. m_hNTDllDll = NULL;
  313. #endif
  314. }
  315. ~CHelperFunctionsLoader( void )
  316. {
  317. m_pSymCleanup( m_hProcess );
  318. if( m_hDbgHelpDll != NULL )
  319. ::FreeLibrary( m_hDbgHelpDll );
  320. #if defined( USE_CAPTURESTACKBACKTRACE )
  321. if( m_hNTDllDll != NULL )
  322. ::FreeLibrary( m_hNTDllDll );
  323. #endif
  324. if( m_szPDBSearchPath != NULL )
  325. delete []m_szPDBSearchPath;
  326. }
  327. static BOOL CALLBACK UnloadSymbolsCallback( PSTR ModuleName, DWORD64 BaseOfDll, PVOID UserContext )
  328. {
  329. const CHelperFunctionsLoader *pThis = ((CHelperFunctionsLoader *)UserContext);
  330. pThis->m_pSymUnloadModule64( pThis->m_hProcess, BaseOfDll );
  331. return TRUE;
  332. }
  333. #if _MSC_VER >= 1600
  334. static BOOL CALLBACK LoadSymbolsCallback( PCSTR ModuleName, DWORD64 ModuleBase, ULONG ModuleSize, PVOID UserContext )
  335. #else
  336. static BOOL CALLBACK LoadSymbolsCallback( PSTR ModuleName, DWORD64 ModuleBase, ULONG ModuleSize, PVOID UserContext )
  337. #endif
  338. {
  339. const CHelperFunctionsLoader *pThis = ((CHelperFunctionsLoader *)UserContext);
  340. //SymLoadModule64( IN HANDLE hProcess, IN HANDLE hFile, IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll );
  341. pThis->m_pSymLoadModule64( pThis->m_hProcess, NULL, (PSTR)ModuleName, (PSTR)ModuleName, ModuleBase, ModuleSize );
  342. return TRUE;
  343. }
  344. void TryLoadingNewSymbols( void )
  345. {
  346. AUTO_LOCK( m_Mutex );
  347. if( m_bIsInitialized )
  348. {
  349. //m_pSymEnumerateModules64( m_hProcess, UnloadSymbolsCallback, this ); //unloaded modules we've already loaded
  350. m_pEnumerateLoadedModules64( m_hProcess, LoadSymbolsCallback, this ); //load everything
  351. m_bShouldReloadSymbols = false;
  352. }
  353. }
  354. void SetStackTranslationSymbolSearchPath( const char *szSemicolonSeparatedList )
  355. {
  356. AUTO_LOCK( m_Mutex );
  357. if( m_szPDBSearchPath != NULL )
  358. delete []m_szPDBSearchPath;
  359. if( szSemicolonSeparatedList == NULL )
  360. {
  361. m_szPDBSearchPath = NULL;
  362. return;
  363. }
  364. int iLength = (int)strlen( szSemicolonSeparatedList ) + 1;
  365. char *pNewPath = new char [iLength];
  366. memcpy( pNewPath, szSemicolonSeparatedList, iLength );
  367. m_szPDBSearchPath = pNewPath;
  368. //re-init search paths. Or if we haven't yet loaded dbghelp.dll, this will go to the dummy function and do nothing
  369. m_pSymSetSearchPath( m_hProcess, m_szPDBSearchPath );
  370. //TryLoadingNewSymbols();
  371. m_bShouldReloadSymbols = true;
  372. }
  373. bool GetSymbolNameFromAddress( const void *pAddress, tchar *pSymbolNameOut, int iMaxSymbolNameLength, uint64 *pDisplacementOut )
  374. {
  375. if( pAddress == NULL )
  376. return false;
  377. AUTO_LOCK( m_Mutex );
  378. unsigned char genericbuffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME*sizeof(TCHAR)];
  379. ((PSYMBOL_INFO)genericbuffer)->SizeOfStruct = sizeof(SYMBOL_INFO);
  380. ((PSYMBOL_INFO)genericbuffer)->MaxNameLen = MAX_SYM_NAME;
  381. DWORD64 dwDisplacement;
  382. if( m_pSymFromAddr( m_hProcess, (DWORD64)pAddress, &dwDisplacement, (PSYMBOL_INFO)genericbuffer) )
  383. {
  384. strncpy( pSymbolNameOut, ((PSYMBOL_INFO)genericbuffer)->Name, iMaxSymbolNameLength );
  385. if( pDisplacementOut != NULL )
  386. *pDisplacementOut = dwDisplacement;
  387. return true;
  388. }
  389. return false;
  390. }
  391. bool GetFileAndLineFromAddress( const void *pAddress, tchar *pFileNameOut, int iMaxFileNameLength, uint32 &iLineNumberOut, uint32 *pDisplacementOut )
  392. {
  393. if( pAddress == NULL )
  394. return false;
  395. AUTO_LOCK( m_Mutex );
  396. tchar szBuffer[1024];
  397. szBuffer[0] = _T('\0');
  398. IMAGEHLP_LINE64 imageHelpLine64;
  399. imageHelpLine64.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
  400. imageHelpLine64.FileName = szBuffer;
  401. DWORD dwDisplacement;
  402. if( m_pSymGetLineFromAddr64( m_hProcess, (DWORD64)pAddress, &dwDisplacement, &imageHelpLine64 ) )
  403. {
  404. strncpy( pFileNameOut, imageHelpLine64.FileName, iMaxFileNameLength );
  405. iLineNumberOut = imageHelpLine64.LineNumber;
  406. if( pDisplacementOut != NULL )
  407. *pDisplacementOut = dwDisplacement;
  408. return true;
  409. }
  410. return false;
  411. }
  412. bool GetModuleNameFromAddress( const void *pAddress, tchar *pModuleNameOut, int iMaxModuleNameLength )
  413. {
  414. AUTO_LOCK( m_Mutex );
  415. IMAGEHLP_MODULE64 moduleInfo;
  416. moduleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
  417. if ( m_pSymGetModuleInfo64( m_hProcess, (DWORD64)pAddress, &moduleInfo ) )
  418. {
  419. strncpy( pModuleNameOut, moduleInfo.ModuleName, iMaxModuleNameLength );
  420. return true;
  421. }
  422. return false;
  423. }
  424. //only returns false if we ran out of buffer space.
  425. bool TranslatePointer( const void * const pAddress, tchar *pTranslationOut, int iTranslationBufferLength, TranslateStackInfo_StyleFlags_t style )
  426. {
  427. //AUTO_LOCK( m_Mutex );
  428. if( pTranslationOut == NULL )
  429. return false;
  430. if( iTranslationBufferLength <= 0 )
  431. return false;
  432. //sample desired output
  433. // valid translation - "tier0.dll!CHelperFunctionsLoader::TranslatePointer - u:\Dev\L4D\src\tier0\stacktools.cpp(162) + 4 bytes"
  434. // fallback translation - "tier0.dll!0x01234567"
  435. tchar *pWrite = pTranslationOut;
  436. *pWrite = '\0';
  437. int iLength;
  438. if( style & TSISTYLEFLAG_MODULENAME )
  439. {
  440. if( !this->GetModuleNameFromAddress( pAddress, pWrite, iTranslationBufferLength ) )
  441. strncpy( pWrite, "unknown_module", iTranslationBufferLength );
  442. iLength = (int)strlen( pWrite );
  443. pWrite += iLength;
  444. iTranslationBufferLength -= iLength;
  445. if( iTranslationBufferLength < 2 )
  446. return false; //need more buffer
  447. if( style & TSISTYLEFLAG_SYMBOLNAME )
  448. {
  449. *pWrite = '!';
  450. ++pWrite;
  451. --iTranslationBufferLength;
  452. *pWrite = '\0';
  453. }
  454. }
  455. //use symbol name to test if the rest is going to work. So grab it whether they want it or not
  456. if( !this->GetSymbolNameFromAddress( pAddress, pWrite, iTranslationBufferLength, NULL ) )
  457. {
  458. int nBytesWritten = _snprintf( pWrite, iTranslationBufferLength, "0x%p", pAddress );
  459. if ( nBytesWritten < 0 )
  460. {
  461. *pWrite = '\0'; // if we can't write all of the line/lineandoffset, don't write any at all
  462. return false;
  463. }
  464. return true;
  465. }
  466. else if( style & TSISTYLEFLAG_SYMBOLNAME )
  467. {
  468. iLength = (int)strlen( pWrite );
  469. pWrite += iLength;
  470. iTranslationBufferLength -= iLength;
  471. }
  472. else
  473. {
  474. *pWrite = '\0'; //symbol name lookup worked, but unwanted, discard
  475. }
  476. if( style & (TSISTYLEFLAG_FULLPATH | TSISTYLEFLAG_SHORTPATH | TSISTYLEFLAG_LINE | TSISTYLEFLAG_LINEANDOFFSET) )
  477. {
  478. if( pWrite != pTranslationOut ) //if we've written anything yet, separate the printed data from the file name and line
  479. {
  480. if( iTranslationBufferLength < 6 )
  481. return false; //need more buffer
  482. pWrite[0] = ' '; //append " - "
  483. pWrite[1] = '-';
  484. pWrite[2] = ' ';
  485. pWrite[3] = '\0';
  486. pWrite += 3;
  487. iTranslationBufferLength -= 3;
  488. }
  489. uint32 iLine;
  490. uint32 iDisplacement;
  491. char szFileName[MAX_PATH];
  492. if( this->GetFileAndLineFromAddress( pAddress, szFileName, MAX_PATH, iLine, &iDisplacement ) )
  493. {
  494. if( style & TSISTYLEFLAG_FULLPATH )
  495. {
  496. iLength = (int)strlen( szFileName );
  497. if ( iTranslationBufferLength < iLength + 1 )
  498. return false;
  499. memcpy( pWrite, szFileName, iLength + 1 );
  500. pWrite += iLength;
  501. iTranslationBufferLength -= iLength;
  502. }
  503. else if( style & TSISTYLEFLAG_SHORTPATH )
  504. {
  505. //shorten the path and copy
  506. iLength = (int)strlen( szFileName );
  507. char *pShortened = szFileName + iLength;
  508. int iSlashesAllowed = 3;
  509. while( pShortened > szFileName )
  510. {
  511. if( (*pShortened == '\\') || (*pShortened == '/') )
  512. {
  513. --iSlashesAllowed;
  514. if( iSlashesAllowed == 0 )
  515. break;
  516. }
  517. --pShortened;
  518. }
  519. iLength = (int)strlen( pShortened );
  520. if( iTranslationBufferLength < iLength + 1 )
  521. {
  522. //Remove the " - " that we can't append to
  523. pWrite -= 3;
  524. iTranslationBufferLength += 3;
  525. *pWrite = '\0';
  526. return false;
  527. }
  528. memcpy( pWrite, szFileName, iLength + 1 );
  529. pWrite += iLength;
  530. iTranslationBufferLength -= iLength;
  531. }
  532. if( style & (TSISTYLEFLAG_LINE | TSISTYLEFLAG_LINEANDOFFSET) )
  533. {
  534. int nBytesWritten = _snprintf( pWrite, iTranslationBufferLength, ((style & TSISTYLEFLAG_LINEANDOFFSET) && (iDisplacement != 0)) ? "(%d) + %d bytes" : "(%d)", iLine, iDisplacement );
  535. if ( nBytesWritten < 0 )
  536. {
  537. *pWrite = '\0'; // if we can't write all of the line/lineandoffset, don't write any at all
  538. return false;
  539. }
  540. pWrite += nBytesWritten;
  541. iTranslationBufferLength -= nBytesWritten;
  542. }
  543. }
  544. else
  545. {
  546. //Remove the " - " that we didn't append to
  547. pWrite -= 3;
  548. iTranslationBufferLength += 3;
  549. *pWrite = '\0';
  550. }
  551. }
  552. return true;
  553. }
  554. //about to actually use the functions, load if necessary
  555. void EnsureReady( void )
  556. {
  557. if( m_bIsInitialized )
  558. {
  559. if( m_bShouldReloadSymbols )
  560. TryLoadingNewSymbols();
  561. return;
  562. }
  563. AUTO_LOCK( m_Mutex );
  564. //Only enabled for P4 and Steam Beta builds
  565. if( (CommandLine()->FindParm( "-steam" ) != 0) && //is steam
  566. (CommandLine()->FindParm( "-internalbuild" ) == 0) ) //is not steam beta
  567. {
  568. //disable the toolset by falsifying initialized state
  569. m_bIsInitialized = true;
  570. return;
  571. }
  572. m_hProcess = GetCurrentProcess();
  573. if( m_hProcess == NULL )
  574. return;
  575. m_bIsInitialized = true;
  576. // get the function pointer directly so that we don't have to include the .lib, and that
  577. // we can easily change it to using our own dll when this code is used on win98/ME/2K machines
  578. m_hDbgHelpDll = ::LoadLibrary( "DbgHelp.dll" );
  579. if ( !m_hDbgHelpDll )
  580. {
  581. //it's possible it's just way too early to initialize (as shown with attempts at using these tools in the memory allocator)
  582. 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
  583. m_bIsInitialized = false;
  584. return;
  585. }
  586. m_pSymInitialize = (PFN_SymInitialize) ::GetProcAddress( m_hDbgHelpDll, "SymInitialize" );
  587. if( m_pSymInitialize == NULL )
  588. {
  589. //very bad
  590. ::FreeLibrary( m_hDbgHelpDll );
  591. m_hDbgHelpDll = NULL;
  592. m_pSymInitialize = SymInitialize_DummyFn;
  593. return;
  594. }
  595. m_pSymCleanup = (PFN_SymCleanup) ::GetProcAddress( m_hDbgHelpDll, "SymCleanup" );
  596. if( m_pSymCleanup == NULL )
  597. m_pSymCleanup = SymCleanup_DummyFn;
  598. m_pSymGetOptions = (PFN_SymGetOptions) ::GetProcAddress( m_hDbgHelpDll, "SymGetOptions" );
  599. if( m_pSymGetOptions == NULL )
  600. m_pSymGetOptions = SymGetOptions_DummyFn;
  601. m_pSymSetOptions = (PFN_SymSetOptions) ::GetProcAddress( m_hDbgHelpDll, "SymSetOptions" );
  602. if( m_pSymSetOptions == NULL )
  603. m_pSymSetOptions = SymSetOptions_DummyFn;
  604. m_pSymSetSearchPath = (PFN_SymSetSearchPath) ::GetProcAddress( m_hDbgHelpDll, "SymSetSearchPath" );
  605. if( m_pSymSetSearchPath == NULL )
  606. m_pSymSetSearchPath = SymSetSearchPath_DummyFn;
  607. m_pSymEnumerateModules64 = (PFN_SymEnumerateModules64) ::GetProcAddress( m_hDbgHelpDll, "SymEnumerateModules64" );
  608. if( m_pSymEnumerateModules64 == NULL )
  609. m_pSymEnumerateModules64 = SymEnumerateModules64_DummyFn;
  610. m_pEnumerateLoadedModules64 = (PFN_EnumerateLoadedModules64) ::GetProcAddress( m_hDbgHelpDll, "EnumerateLoadedModules64" );
  611. if( m_pEnumerateLoadedModules64 == NULL )
  612. m_pEnumerateLoadedModules64 = EnumerateLoadedModules64_DummyFn;
  613. m_pSymLoadModule64 = (PFN_SymLoadModule64) ::GetProcAddress( m_hDbgHelpDll, "SymLoadModule64" );
  614. if( m_pSymLoadModule64 == NULL )
  615. m_pSymLoadModule64 = SymLoadModule64_DummyFn;
  616. m_pSymUnloadModule64 = (PFN_SymUnloadModule64) ::GetProcAddress( m_hDbgHelpDll, "SymUnloadModule64" );
  617. if( m_pSymUnloadModule64 == NULL )
  618. m_pSymUnloadModule64 = SymUnloadModule64_DummyFn;
  619. m_pSymFromAddr = (PFN_SymFromAddr) ::GetProcAddress( m_hDbgHelpDll, "SymFromAddr" );
  620. if( m_pSymFromAddr == NULL )
  621. m_pSymFromAddr = SymFromAddr_DummyFn;
  622. m_pSymGetLineFromAddr64 = (PFN_SymGetLineFromAddr64) ::GetProcAddress( m_hDbgHelpDll, "SymGetLineFromAddr64" );
  623. if( m_pSymGetLineFromAddr64 == NULL )
  624. m_pSymGetLineFromAddr64 = SymGetLineFromAddr64_DummyFn;
  625. m_pSymGetModuleInfo64 = (PFN_SymGetModuleInfo64) ::GetProcAddress( m_hDbgHelpDll, "SymGetModuleInfo64" );
  626. if( m_pSymGetModuleInfo64 == NULL )
  627. m_pSymGetModuleInfo64 = SymGetModuleInfo64_DummyFn;
  628. #if defined( USE_STACKWALK64 )
  629. m_pStackWalk64 = (PFN_StackWalk64) ::GetProcAddress( m_hDbgHelpDll, "StackWalk64" );
  630. if( m_pStackWalk64 == NULL )
  631. m_pStackWalk64 = StackWalk64_DummyFn;
  632. #endif
  633. #if defined( USE_CAPTURESTACKBACKTRACE )
  634. m_hNTDllDll = ::LoadLibrary( "ntdll.dll" );
  635. m_pCaptureStackBackTrace = (PFN_CaptureStackBackTrace) ::GetProcAddress( m_hNTDllDll, "RtlCaptureStackBackTrace" );
  636. if( m_pCaptureStackBackTrace == NULL )
  637. m_pCaptureStackBackTrace = CaptureStackBackTrace_DummyFn;
  638. #endif
  639. m_pSymSetOptions( m_pSymGetOptions() |
  640. SYMOPT_DEFERRED_LOADS | //load on demand
  641. SYMOPT_EXACT_SYMBOLS | //don't load the wrong file
  642. SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS | //don't prompt ever
  643. SYMOPT_LOAD_LINES ); //load line info
  644. m_pSymInitialize( m_hProcess, m_szPDBSearchPath, FALSE );
  645. TryLoadingNewSymbols();
  646. }
  647. bool m_bIsInitialized;
  648. bool m_bShouldReloadSymbols;
  649. HANDLE m_hProcess;
  650. HMODULE m_hDbgHelpDll;
  651. char *m_szPDBSearchPath;
  652. CThreadFastMutex m_Mutex; //DbgHelp functions are all single threaded.
  653. PFN_SymInitialize m_pSymInitialize;
  654. PFN_SymCleanup m_pSymCleanup;
  655. PFN_SymGetOptions m_pSymGetOptions;
  656. PFN_SymSetOptions m_pSymSetOptions;
  657. PFN_SymSetSearchPath m_pSymSetSearchPath;
  658. PFN_SymEnumerateModules64 m_pSymEnumerateModules64;
  659. PFN_EnumerateLoadedModules64 m_pEnumerateLoadedModules64;
  660. PFN_SymLoadModule64 m_pSymLoadModule64;
  661. PFN_SymUnloadModule64 m_pSymUnloadModule64;
  662. PFN_SymFromAddr m_pSymFromAddr;
  663. PFN_SymGetLineFromAddr64 m_pSymGetLineFromAddr64;
  664. PFN_SymGetModuleInfo64 m_pSymGetModuleInfo64;
  665. #if defined( USE_STACKWALK64 )
  666. PFN_StackWalk64 m_pStackWalk64;
  667. #endif
  668. #if defined( USE_CAPTURESTACKBACKTRACE )
  669. HMODULE m_hNTDllDll;
  670. PFN_CaptureStackBackTrace m_pCaptureStackBackTrace;
  671. #endif
  672. };
  673. static CHelperFunctionsLoader s_HelperFunctions;
  674. #if defined( USE_STACKWALK64 ) //most reliable method thanks to boatloads of windows helper functions. Also the slowest.
  675. int CrawlStack_StackWalk64( CONTEXT *pExceptionContext, void **pReturnAddressesOut, int iArrayCount, int iSkipCount )
  676. {
  677. s_HelperFunctions.EnsureReady();
  678. AUTO_LOCK( s_HelperFunctions.m_Mutex );
  679. CONTEXT currentContext;
  680. memcpy( &currentContext, pExceptionContext, sizeof( CONTEXT ) );
  681. STACKFRAME64 sfFrame = { 0 }; //memset(&sfFrame, 0x0, sizeof(sfFrame));
  682. sfFrame.AddrPC.Mode = sfFrame.AddrFrame.Mode = AddrModeFlat;
  683. #ifdef _M_X64
  684. sfFrame.AddrPC.Offset = currentContext.Rip;
  685. sfFrame.AddrFrame.Offset = currentContext.Rbp; // ????
  686. #else
  687. sfFrame.AddrPC.Offset = currentContext.Eip;
  688. sfFrame.AddrFrame.Offset = currentContext.Ebp;
  689. #endif
  690. HANDLE hThread = GetCurrentThread();
  691. int i;
  692. for( i = 0; i != iSkipCount; ++i ) //skip entries that the requesting function thinks are uninformative
  693. {
  694. if(!s_HelperFunctions.m_pStackWalk64( STACKWALK64_MACHINETYPE, s_HelperFunctions.m_hProcess, hThread, &sfFrame, &currentContext, NULL, NULL, NULL, NULL ) ||
  695. (sfFrame.AddrFrame.Offset == 0) )
  696. {
  697. return 0;
  698. }
  699. }
  700. for( i = 0; i != iArrayCount; ++i )
  701. {
  702. if(!s_HelperFunctions.m_pStackWalk64( STACKWALK64_MACHINETYPE, s_HelperFunctions.m_hProcess, hThread, &sfFrame, &currentContext, NULL, NULL, NULL, NULL ) ||
  703. (sfFrame.AddrFrame.Offset == 0) )
  704. {
  705. break;
  706. }
  707. pReturnAddressesOut[i] = (void *)sfFrame.AddrPC.Offset;
  708. }
  709. return i;
  710. }
  711. void GetCallStackReturnAddresses_Exception( void **CallStackReturnAddresses, int *pRetCount, int iSkipCount, _EXCEPTION_POINTERS * pExceptionInfo )
  712. {
  713. int iCount = CrawlStack_StackWalk64( pExceptionInfo->ContextRecord, CallStackReturnAddresses, *pRetCount, iSkipCount + 1 ); //skipping RaiseException()
  714. *pRetCount = iCount;
  715. }
  716. #endif //#if defined( USE_STACKWALK64 )
  717. int GetCallStack( void **pReturnAddressesOut, int iArrayCount, int iSkipCount )
  718. {
  719. s_HelperFunctions.EnsureReady();
  720. ++iSkipCount; //skip this function
  721. #if defined( USE_CAPTURESTACKBACKTRACE )
  722. if( s_HelperFunctions.m_pCaptureStackBackTrace != CaptureStackBackTrace_DummyFn )
  723. {
  724. //docs state a total limit of 63 back traces between skipped and stored
  725. int iRetVal = s_HelperFunctions.m_pCaptureStackBackTrace( iSkipCount, MIN( iArrayCount, 63 - iSkipCount ), pReturnAddressesOut, NULL );
  726. return AppendParentStackTrace( pReturnAddressesOut, iArrayCount, iRetVal );
  727. }
  728. #endif
  729. #if defined( USE_STACKWALK64 )
  730. if( s_HelperFunctions.m_pStackWalk64 != StackWalk64_DummyFn )
  731. {
  732. int iInOutArrayCount = iArrayCount; //array count becomes both input and output with exception handler version
  733. __try
  734. {
  735. ::RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, NULL );
  736. }
  737. __except ( GetCallStackReturnAddresses_Exception( pReturnAddressesOut, &iInOutArrayCount, iSkipCount, GetExceptionInformation() ), EXCEPTION_EXECUTE_HANDLER )
  738. {
  739. return AppendParentStackTrace( pReturnAddressesOut, iArrayCount, iInOutArrayCount );
  740. }
  741. }
  742. #endif
  743. return GetCallStack_Fast( pReturnAddressesOut, iArrayCount, iSkipCount );
  744. }
  745. void SetStackTranslationSymbolSearchPath( const char *szSemicolonSeparatedList )
  746. {
  747. s_HelperFunctions.SetStackTranslationSymbolSearchPath( szSemicolonSeparatedList );
  748. }
  749. void StackToolsNotify_LoadedLibrary( const char *szLibName )
  750. {
  751. s_HelperFunctions.m_bShouldReloadSymbols = true;
  752. }
  753. int TranslateStackInfo( const void * const *pCallStack, int iCallStackCount, tchar *szOutput, int iOutBufferSize, const tchar *szEntrySeparator, TranslateStackInfo_StyleFlags_t style )
  754. {
  755. s_HelperFunctions.EnsureReady();
  756. tchar *szStartOutput = szOutput;
  757. if( szEntrySeparator == NULL )
  758. szEntrySeparator = _T("");
  759. int iSeparatorSize = (int)strlen( szEntrySeparator );
  760. for( int i = 0; i < iCallStackCount; ++i )
  761. {
  762. if( !s_HelperFunctions.TranslatePointer( pCallStack[i], szOutput, iOutBufferSize, style ) )
  763. {
  764. return i;
  765. }
  766. int iLength = (int)strlen( szOutput );
  767. szOutput += iLength;
  768. iOutBufferSize -= iLength;
  769. if( iOutBufferSize > iSeparatorSize )
  770. {
  771. memcpy( szOutput, szEntrySeparator, iSeparatorSize * sizeof( tchar ) );
  772. szOutput += iSeparatorSize;
  773. iOutBufferSize -= iSeparatorSize;
  774. }
  775. *szOutput = '\0';
  776. }
  777. szOutput -= iSeparatorSize;
  778. if( szOutput >= szStartOutput )
  779. *szOutput = '\0';
  780. return iCallStackCount;
  781. }
  782. void PreloadStackInformation( void * const *pAddresses, int iAddressCount )
  783. {
  784. //nop on anything but 360
  785. }
  786. bool GetFileAndLineFromAddress( const void *pAddress, tchar *pFileNameOut, int iMaxFileNameLength, uint32 &iLineNumberOut, uint32 *pDisplacementOut )
  787. {
  788. s_HelperFunctions.EnsureReady();
  789. return s_HelperFunctions.GetFileAndLineFromAddress( pAddress, pFileNameOut, iMaxFileNameLength, iLineNumberOut, pDisplacementOut );
  790. }
  791. bool GetSymbolNameFromAddress( const void *pAddress, tchar *pSymbolNameOut, int iMaxSymbolNameLength, uint64 *pDisplacementOut )
  792. {
  793. s_HelperFunctions.EnsureReady();
  794. return s_HelperFunctions.GetSymbolNameFromAddress( pAddress, pSymbolNameOut, iMaxSymbolNameLength, pDisplacementOut );
  795. }
  796. bool GetModuleNameFromAddress( const void *pAddress, tchar *pModuleNameOut, int iMaxModuleNameLength )
  797. {
  798. s_HelperFunctions.EnsureReady();
  799. return s_HelperFunctions.GetModuleNameFromAddress( pAddress, pModuleNameOut, iMaxModuleNameLength );
  800. }
  801. #else //#if defined( WIN32 ) && !defined( _X360 )
  802. //===============================================================================================================
  803. // X360 version of the toolset
  804. //===============================================================================================================
  805. class C360StackTranslationHelper
  806. {
  807. public:
  808. C360StackTranslationHelper( void )
  809. {
  810. m_bInitialized = true;
  811. }
  812. ~C360StackTranslationHelper( void )
  813. {
  814. StringSet_t::const_iterator iter;
  815. //module names
  816. {
  817. iter = m_ModuleNameSet.begin();
  818. while(iter != m_ModuleNameSet.end())
  819. {
  820. char *pModuleName = (char*)(*iter);
  821. delete []pModuleName;
  822. iter++;
  823. }
  824. m_ModuleNameSet.clear();
  825. }
  826. //file names
  827. {
  828. iter = m_FileNameSet.begin();
  829. while(iter != m_FileNameSet.end())
  830. {
  831. char *pFileName = (char*)(*iter);
  832. delete []pFileName;
  833. iter++;
  834. }
  835. m_FileNameSet.clear();
  836. }
  837. //symbol names
  838. {
  839. iter = m_SymbolNameSet.begin();
  840. while(iter != m_SymbolNameSet.end())
  841. {
  842. char *pSymbolName = (char*)(*iter);
  843. delete []pSymbolName;
  844. iter++;
  845. }
  846. m_SymbolNameSet.clear();
  847. }
  848. m_bInitialized = false;
  849. }
  850. private:
  851. struct StackAddressInfo_t;
  852. public:
  853. inline StackAddressInfo_t *CreateEntry( const FullStackInfo_t &info )
  854. {
  855. std::pair<AddressInfoMapIter_t, bool> retval = m_AddressInfoMap.insert( AddressInfoMapEntry_t( info.pAddress, StackAddressInfo_t() ) );
  856. if( retval.first->second.szModule != NULL )
  857. return &retval.first->second; //already initialized
  858. retval.first->second.iLine = info.iLine;
  859. //share strings
  860. //module
  861. {
  862. const char *pModuleName;
  863. StringSet_t::const_iterator iter = m_ModuleNameSet.find( info.szModuleName );
  864. if ( iter == m_ModuleNameSet.end() )
  865. {
  866. int nLen = strlen(info.szModuleName) + 1;
  867. pModuleName = new char [nLen];
  868. memcpy( (char *)pModuleName, info.szModuleName, nLen );
  869. m_ModuleNameSet.insert( pModuleName );
  870. }
  871. else
  872. {
  873. pModuleName = (char *)(*iter);
  874. }
  875. retval.first->second.szModule = pModuleName;
  876. }
  877. //file
  878. {
  879. const char *pFileName;
  880. StringSet_t::const_iterator iter = m_FileNameSet.find( info.szFileName );
  881. if ( iter == m_FileNameSet.end() )
  882. {
  883. int nLen = strlen(info.szFileName) + 1;
  884. pFileName = new char [nLen];
  885. memcpy( (char *)pFileName, info.szFileName, nLen );
  886. m_FileNameSet.insert( pFileName );
  887. }
  888. else
  889. {
  890. pFileName = (char *)(*iter);
  891. }
  892. retval.first->second.szFileName = pFileName;
  893. }
  894. //symbol
  895. {
  896. const char *pSymbolName;
  897. StringSet_t::const_iterator iter = m_SymbolNameSet.find( info.szSymbol );
  898. if ( iter == m_SymbolNameSet.end() )
  899. {
  900. int nLen = strlen(info.szSymbol) + 1;
  901. pSymbolName = new char [nLen];
  902. memcpy( (char *)pSymbolName, info.szSymbol, nLen );
  903. m_SymbolNameSet.insert( pSymbolName );
  904. }
  905. else
  906. {
  907. pSymbolName = (char *)(*iter);
  908. }
  909. retval.first->second.szSymbol = pSymbolName;
  910. }
  911. return &retval.first->second;
  912. }
  913. inline StackAddressInfo_t *FindInfoEntry( const void *pAddress )
  914. {
  915. AddressInfoMapIter_t Iter = m_AddressInfoMap.find( pAddress );
  916. if( Iter != m_AddressInfoMap.end() )
  917. return &Iter->second;
  918. return NULL;
  919. }
  920. inline int RetrieveStackInfo( const void * const *pAddresses, FullStackInfo_t *pReturnedStructs, int iAddressCount )
  921. {
  922. int ReturnedTranslatedCount = -1;
  923. //construct the message
  924. // Header Finished Count(out) Input Count Input Array Returned data write address
  925. int iMessageSize = 2 + sizeof( int * ) + sizeof( uint32 ) + (sizeof( void * ) * iAddressCount) + sizeof( FullStackInfo_t * );
  926. uint8 *pMessage = (uint8 *)stackalloc( iMessageSize );
  927. uint8 *pMessageWrite = pMessage;
  928. pMessageWrite[0] = XBX_DBG_BNH_STACKTRANSLATOR; //have this message handled by stack translator handler
  929. pMessageWrite[1] = ST_BHC_GETTRANSLATIONINFO;
  930. pMessageWrite += 2;
  931. *(int **)pMessageWrite = (int *)BigDWord( (DWORD)&ReturnedTranslatedCount );
  932. pMessageWrite += sizeof( int * );
  933. *(uint32 *)pMessageWrite = (uint32)BigDWord( (DWORD)iAddressCount );
  934. pMessageWrite += sizeof( uint32 );
  935. memcpy( pMessageWrite, pAddresses, iAddressCount * sizeof( void * ) );
  936. pMessageWrite += iAddressCount * sizeof( void * );
  937. *(FullStackInfo_t **)pMessageWrite = (FullStackInfo_t *)BigDWord( (DWORD)pReturnedStructs );
  938. bool bSuccess = XBX_SendBinaryData( pMessage, iMessageSize, false, 30000 );
  939. ReturnedTranslatedCount = BigDWord( ReturnedTranslatedCount );
  940. if( bSuccess && (ReturnedTranslatedCount > 0) )
  941. {
  942. return ReturnedTranslatedCount;
  943. }
  944. return 0;
  945. }
  946. inline StackAddressInfo_t *CreateEntry( const void *pAddress )
  947. {
  948. //ask VXConsole for information about the addresses we're clueless about
  949. FullStackInfo_t ReturnedData;
  950. ReturnedData.pAddress = pAddress;
  951. ReturnedData.szFileName[0] = '\0'; //strncpy( ReturnedData.szFileName, "FileUninitialized", sizeof( ReturnedData.szFileName ) );
  952. ReturnedData.szModuleName[0] = '\0'; //strncpy( ReturnedData.szModuleName, "ModuleUninitialized", sizeof( ReturnedData.szModuleName ) );
  953. ReturnedData.szSymbol[0] = '\0'; //strncpy( ReturnedData.szSymbol, "SymbolUninitialized", sizeof( ReturnedData.szSymbol ) );
  954. ReturnedData.iLine = 0;
  955. ReturnedData.iSymbolOffset = 0;
  956. int iTranslated = RetrieveStackInfo( &pAddress, &ReturnedData, 1 );
  957. if( iTranslated == 1 )
  958. {
  959. //store
  960. return CreateEntry( ReturnedData );
  961. }
  962. return FindInfoEntry( pAddress ); //probably won't work, but last ditch.
  963. }
  964. inline StackAddressInfo_t *FindOrCreateEntry( const void *pAddress )
  965. {
  966. StackAddressInfo_t *pReturn = FindInfoEntry( pAddress );
  967. if( pReturn == NULL )
  968. {
  969. pReturn = CreateEntry( pAddress );
  970. }
  971. return pReturn;
  972. }
  973. inline void LoadStackInformation( void * const *pAddresses, int iAddressCount )
  974. {
  975. Assert( (iAddressCount > 0) && (pAddresses != NULL) );
  976. int iNeedLoading = 0;
  977. void **pNeedLoading = (void **)stackalloc( sizeof( const void * ) * iAddressCount ); //addresses we need to ask VXConsole about
  978. for( int i = 0; i != iAddressCount; ++i )
  979. {
  980. if( FindInfoEntry( pAddresses[i] ) == NULL )
  981. {
  982. //need to load this address
  983. pNeedLoading[iNeedLoading] = pAddresses[i];
  984. ++iNeedLoading;
  985. }
  986. }
  987. if( iNeedLoading != 0 )
  988. {
  989. //ask VXConsole for information about the addresses we're clueless about
  990. FullStackInfo_t *pReturnedStructs = (FullStackInfo_t *)stackalloc( sizeof( FullStackInfo_t ) * iNeedLoading );
  991. for( int i = 0; i < iNeedLoading; ++i )
  992. {
  993. pReturnedStructs[i].pAddress = 0;
  994. pReturnedStructs[i].szFileName[0] = '\0'; //strncpy( pReturnedStructs[i].szFileName, "FileUninitialized", sizeof( pReturnedStructs[i].szFileName ) );
  995. pReturnedStructs[i].szModuleName[0] = '\0'; //strncpy( pReturnedStructs[i].szModuleName, "ModuleUninitialized", sizeof( pReturnedStructs[i].szModuleName ) );
  996. pReturnedStructs[i].szSymbol[0] = '\0'; //strncpy( pReturnedStructs[i].szSymbol, "SymbolUninitialized", sizeof( pReturnedStructs[i].szSymbol ) );
  997. pReturnedStructs[i].iLine = 0;
  998. pReturnedStructs[i].iSymbolOffset = 0;
  999. }
  1000. int iTranslated = RetrieveStackInfo( pNeedLoading, pReturnedStructs, iNeedLoading );
  1001. if( iTranslated == iNeedLoading )
  1002. {
  1003. //store
  1004. for( int i = 0; i < iTranslated; ++i )
  1005. {
  1006. CreateEntry( pReturnedStructs[i] );
  1007. }
  1008. }
  1009. }
  1010. }
  1011. inline bool GetFileAndLineFromAddress( const void *pAddress, tchar *pFileNameOut, int iMaxFileNameLength, uint32 &iLineNumberOut, uint32 *pDisplacementOut )
  1012. {
  1013. StackAddressInfo_t *pInfo = FindOrCreateEntry( pAddress );
  1014. if( pInfo && (pInfo->szFileName[0] != '\0') )
  1015. {
  1016. strncpy( pFileNameOut, pInfo->szFileName, iMaxFileNameLength );
  1017. iLineNumberOut = pInfo->iLine;
  1018. if( pDisplacementOut )
  1019. *pDisplacementOut = 0; //can't get line displacement on 360
  1020. return true;
  1021. }
  1022. return false;
  1023. }
  1024. inline bool GetSymbolNameFromAddress( const void *pAddress, tchar *pSymbolNameOut, int iMaxSymbolNameLength, uint64 *pDisplacementOut )
  1025. {
  1026. StackAddressInfo_t *pInfo = FindOrCreateEntry( pAddress );
  1027. if( pInfo && (pInfo->szSymbol[0] != '\0') )
  1028. {
  1029. strncpy( pSymbolNameOut, pInfo->szSymbol, iMaxSymbolNameLength );
  1030. if( pDisplacementOut )
  1031. *pDisplacementOut = pInfo->iSymbolOffset;
  1032. return true;
  1033. }
  1034. return false;
  1035. }
  1036. inline bool GetModuleNameFromAddress( const void *pAddress, tchar *pModuleNameOut, int iMaxModuleNameLength )
  1037. {
  1038. StackAddressInfo_t *pInfo = FindOrCreateEntry( pAddress );
  1039. if( pInfo && (pInfo->szModule[0] != '\0') )
  1040. {
  1041. strncpy( pModuleNameOut, pInfo->szModule, iMaxModuleNameLength );
  1042. return true;
  1043. }
  1044. return false;
  1045. }
  1046. CThreadFastMutex m_hMutex;
  1047. private:
  1048. #pragma pack(push)
  1049. #pragma pack(1)
  1050. struct StackAddressInfo_t
  1051. {
  1052. StackAddressInfo_t( void ) : szModule(NULL), szFileName(NULL), szSymbol(NULL), iLine(0), iSymbolOffset(0) {}
  1053. const char *szModule;
  1054. const char *szFileName;
  1055. const char *szSymbol;
  1056. uint32 iLine;
  1057. uint32 iSymbolOffset;
  1058. };
  1059. #pragma pack(pop)
  1060. typedef std::map< const void *, StackAddressInfo_t, std::less<const void *>> AddressInfoMap_t;
  1061. typedef AddressInfoMap_t::iterator AddressInfoMapIter_t;
  1062. typedef AddressInfoMap_t::value_type AddressInfoMapEntry_t;
  1063. class CStringLess
  1064. {
  1065. public:
  1066. bool operator()(const char *pszLeft, const char *pszRight ) const
  1067. {
  1068. return ( V_tier0_stricmp( pszLeft, pszRight ) < 0 );
  1069. }
  1070. };
  1071. typedef std::set<const char *, CStringLess> StringSet_t;
  1072. AddressInfoMap_t m_AddressInfoMap; //TODO: retire old entries?
  1073. StringSet_t m_ModuleNameSet;
  1074. StringSet_t m_FileNameSet;
  1075. StringSet_t m_SymbolNameSet;
  1076. bool m_bInitialized;
  1077. };
  1078. static C360StackTranslationHelper s_360StackTranslator;
  1079. int GetCallStack( void **pReturnAddressesOut, int iArrayCount, int iSkipCount )
  1080. {
  1081. ++iSkipCount; //skip this function
  1082. //DmCaptureStackBackTrace() has no skip functionality, so we need to grab everything and skip within that list
  1083. void **pAllResults = (void **)stackalloc( sizeof( void * ) * (iArrayCount + iSkipCount) );
  1084. if( DmCaptureStackBackTrace( iArrayCount + iSkipCount, pAllResults ) == XBDM_NOERR )
  1085. {
  1086. for( int i = 0; i != iSkipCount; ++i )
  1087. {
  1088. if( *pAllResults == NULL ) //DmCaptureStackBackTrace() NULL terminates the list instead of telling us how many were returned
  1089. return AppendParentStackTrace( pReturnAddressesOut, iArrayCount, 0 );
  1090. ++pAllResults; //move the pointer forward so the second loop indices match up
  1091. }
  1092. for( int i = 0; i != iArrayCount; ++i )
  1093. {
  1094. if( pAllResults[i] == NULL ) //DmCaptureStackBackTrace() NULL terminates the list instead of telling us how many were returned
  1095. return AppendParentStackTrace( pReturnAddressesOut, iArrayCount, i );
  1096. pReturnAddressesOut[i] = pAllResults[i];
  1097. }
  1098. return iArrayCount; //no room to append parent
  1099. }
  1100. return GetCallStack_Fast( pReturnAddressesOut, iArrayCount, iSkipCount );
  1101. }
  1102. void SetStackTranslationSymbolSearchPath( const char *szSemicolonSeparatedList )
  1103. {
  1104. //nop on 360
  1105. }
  1106. void StackToolsNotify_LoadedLibrary( const char *szLibName )
  1107. {
  1108. //send off the notice to VXConsole
  1109. uint8 message[2];
  1110. message[0] = XBX_DBG_BNH_STACKTRANSLATOR; //have this message handled by stack translator handler
  1111. message[1] = ST_BHC_LOADEDLIBARY; //loaded a library notification
  1112. XBX_SendBinaryData( message, 2 );
  1113. }
  1114. int TranslateStackInfo( const void * const *pCallStack, int iCallStackCount, tchar *szOutput, int iOutBufferSize, const tchar *szEntrySeparator, TranslateStackInfo_StyleFlags_t style )
  1115. {
  1116. if( iCallStackCount == 0 )
  1117. {
  1118. if( iOutBufferSize > 1 )
  1119. *szOutput = '\0';
  1120. return 0;
  1121. }
  1122. if( szEntrySeparator == NULL )
  1123. szEntrySeparator = "";
  1124. int iSeparatorLength = strlen( szEntrySeparator ) + 1;
  1125. int iDataSize = (sizeof( void * ) * iCallStackCount) + (iSeparatorLength * sizeof( tchar )) + 1; //1 for style flags
  1126. //360 is incapable of translation on it's own. Encode the stack for translation in VXConsole
  1127. //Encoded section is as such ":CSDECODE[encoded binary]"
  1128. int iEncodedSize = -EncodeBinaryToString( NULL, iDataSize, NULL, 0 ); //get needed buffer size
  1129. static const tchar cControlPrefix[] = XBX_CALLSTACKDECODEPREFIX;
  1130. const size_t cControlLength = (sizeof( cControlPrefix )/sizeof(tchar)) - 1; //-1 to remove null terminator
  1131. if( iOutBufferSize > (iEncodedSize + (int)cControlLength + 2) ) //+2 for ']' and null term
  1132. {
  1133. COMPILE_TIME_ASSERT( TSISTYLEFLAG_LAST < (1<<8) ); //need to update the encoder/decoder to use more than a byte for style flags
  1134. uint8 *pData = (uint8 *)stackalloc( iDataSize );
  1135. pData[0] = (uint8)style;
  1136. memcpy( pData + 1, szEntrySeparator, iSeparatorLength * sizeof( tchar ) );
  1137. memcpy( pData + 1 + (iSeparatorLength * sizeof( tchar )), pCallStack, iCallStackCount * sizeof( void * ) );
  1138. memcpy( szOutput, XBX_CALLSTACKDECODEPREFIX, cControlLength * sizeof( tchar ) );
  1139. int iLength = cControlLength + EncodeBinaryToString( pData, iDataSize, &szOutput[cControlLength], (iOutBufferSize - cControlLength) );
  1140. szOutput[iLength] = ']';
  1141. szOutput[iLength + 1] = '\0';
  1142. }
  1143. return iCallStackCount;
  1144. }
  1145. void PreloadStackInformation( void * const *pAddresses, int iAddressCount )
  1146. {
  1147. AUTO_LOCK( s_360StackTranslator.m_hMutex );
  1148. s_360StackTranslator.LoadStackInformation( pAddresses, iAddressCount );
  1149. }
  1150. bool GetFileAndLineFromAddress( const void *pAddress, tchar *pFileNameOut, int iMaxFileNameLength, uint32 &iLineNumberOut, uint32 *pDisplacementOut )
  1151. {
  1152. AUTO_LOCK( s_360StackTranslator.m_hMutex );
  1153. return s_360StackTranslator.GetFileAndLineFromAddress( pAddress, pFileNameOut, iMaxFileNameLength, iLineNumberOut, pDisplacementOut );
  1154. }
  1155. bool GetSymbolNameFromAddress( const void *pAddress, tchar *pSymbolNameOut, int iMaxSymbolNameLength, uint64 *pDisplacementOut )
  1156. {
  1157. AUTO_LOCK( s_360StackTranslator.m_hMutex );
  1158. return s_360StackTranslator.GetSymbolNameFromAddress( pAddress, pSymbolNameOut, iMaxSymbolNameLength, pDisplacementOut );
  1159. }
  1160. bool GetModuleNameFromAddress( const void *pAddress, tchar *pModuleNameOut, int iMaxModuleNameLength )
  1161. {
  1162. AUTO_LOCK( s_360StackTranslator.m_hMutex );
  1163. return s_360StackTranslator.GetModuleNameFromAddress( pAddress, pModuleNameOut, iMaxModuleNameLength );
  1164. }
  1165. #endif //#else //#if defined( WIN32 ) && !defined( _X360 )
  1166. #endif //#if !defined( ENABLE_RUNTIME_STACK_TRANSLATION )
  1167. CCallStackStorage::CCallStackStorage( FN_GetCallStack GetStackFunction, uint32 iSkipCalls )
  1168. {
  1169. iValidEntries = GetStackFunction( pStack, ARRAYSIZE( pStack ), iSkipCalls + 1 );
  1170. }
  1171. CStackTop_CopyParentStack::CStackTop_CopyParentStack( void * const *pParentStackTrace, int iParentStackTraceLength )
  1172. {
  1173. #if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
  1174. //miniature version of GetCallStack_Fast()
  1175. #if (defined( TIER0_FPO_DISABLED ) || defined( _DEBUG )) &&\
  1176. (defined( WIN32 ) && !defined( _X360 ) && !defined(_M_X64))
  1177. void *pStackCrawlEBP;
  1178. __asm
  1179. {
  1180. mov [pStackCrawlEBP], ebp;
  1181. }
  1182. pStackCrawlEBP = *(void **)pStackCrawlEBP;
  1183. m_pReplaceAddress = *((void **)pStackCrawlEBP + 1);
  1184. m_pStackBase = (void *)((void **)pStackCrawlEBP + 1);
  1185. #else
  1186. m_pReplaceAddress = NULL;
  1187. m_pStackBase = this;
  1188. #endif
  1189. m_pParentStackTrace = NULL;
  1190. if( (pParentStackTrace != NULL) && (iParentStackTraceLength > 0) )
  1191. {
  1192. while( (iParentStackTraceLength > 0) && (pParentStackTrace[iParentStackTraceLength - 1] == NULL) )
  1193. {
  1194. --iParentStackTraceLength;
  1195. }
  1196. if( iParentStackTraceLength > 0 )
  1197. {
  1198. m_pParentStackTrace = new void * [iParentStackTraceLength];
  1199. memcpy( (void **)m_pParentStackTrace, pParentStackTrace, sizeof( void * ) * iParentStackTraceLength );
  1200. }
  1201. }
  1202. m_iParentStackTraceLength = iParentStackTraceLength;
  1203. m_pPrevTop = g_StackTop;
  1204. g_StackTop = this;
  1205. Assert( (CStackTop_Base *)g_StackTop == this );
  1206. #endif //#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
  1207. }
  1208. CStackTop_CopyParentStack::~CStackTop_CopyParentStack( void )
  1209. {
  1210. #if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
  1211. Assert( (CStackTop_Base *)g_StackTop == this );
  1212. g_StackTop = m_pPrevTop;
  1213. if( m_pParentStackTrace != NULL )
  1214. {
  1215. delete []m_pParentStackTrace;
  1216. }
  1217. #endif
  1218. }
  1219. CStackTop_ReferenceParentStack::CStackTop_ReferenceParentStack( void * const *pParentStackTrace, int iParentStackTraceLength )
  1220. {
  1221. #if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
  1222. //miniature version of GetCallStack_Fast()
  1223. #if (defined( TIER0_FPO_DISABLED ) || defined( _DEBUG )) &&\
  1224. (defined( WIN32 ) && !defined( _X360 ) && !defined(_M_X64))
  1225. void *pStackCrawlEBP;
  1226. __asm
  1227. {
  1228. mov [pStackCrawlEBP], ebp;
  1229. }
  1230. pStackCrawlEBP = *(void **)pStackCrawlEBP;
  1231. m_pReplaceAddress = *((void **)pStackCrawlEBP + 1);
  1232. m_pStackBase = (void *)((void **)pStackCrawlEBP + 1);
  1233. #else
  1234. m_pReplaceAddress = NULL;
  1235. m_pStackBase = this;
  1236. #endif
  1237. m_pParentStackTrace = pParentStackTrace;
  1238. if( (pParentStackTrace != NULL) && (iParentStackTraceLength > 0) )
  1239. {
  1240. while( (iParentStackTraceLength > 0) && (pParentStackTrace[iParentStackTraceLength - 1] == NULL) )
  1241. {
  1242. --iParentStackTraceLength;
  1243. }
  1244. }
  1245. m_iParentStackTraceLength = iParentStackTraceLength;
  1246. m_pPrevTop = g_StackTop;
  1247. g_StackTop = this;
  1248. Assert( (CStackTop_Base *)g_StackTop == this );
  1249. #endif //#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
  1250. }
  1251. CStackTop_ReferenceParentStack::~CStackTop_ReferenceParentStack( void )
  1252. {
  1253. #if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
  1254. Assert( (CStackTop_Base *)g_StackTop == this );
  1255. g_StackTop = m_pPrevTop;
  1256. ReleaseParentStackReferences();
  1257. #endif
  1258. }
  1259. void CStackTop_ReferenceParentStack::ReleaseParentStackReferences( void )
  1260. {
  1261. #if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
  1262. m_pParentStackTrace = NULL;
  1263. m_iParentStackTraceLength = 0;
  1264. #endif
  1265. }
  1266. //Encodes data so that every byte's most significant bit is a 1. Ensuring no null terminators.
  1267. //This puts the encoded data in the 128-255 value range. Leaving all standard ascii characters for control.
  1268. //Returns string length (not including the written null terminator as is standard).
  1269. //Or if the buffer is too small. Returns negative of necessary buffer size (including room needed for null terminator)
  1270. int EncodeBinaryToString( const void *pToEncode, int iDataLength, char *pEncodeOut, int iEncodeBufferSize )
  1271. {
  1272. int iEncodedSize = iDataLength;
  1273. iEncodedSize += (iEncodedSize + 6) / 7; //Have 1 control byte for every 7 actual bytes
  1274. iEncodedSize += sizeof( uint32 ) + 1; //data size at the beginning of the blob and null terminator at the end
  1275. if( (iEncodedSize > iEncodeBufferSize) || (pEncodeOut == NULL) || (pToEncode == NULL) )
  1276. return -iEncodedSize; //not enough room
  1277. uint8 *pEncodeWrite = (uint8 *)pEncodeOut;
  1278. //first encode the data size. Encodes lowest 28 bits and discards the high 4
  1279. pEncodeWrite[0] = ((iDataLength >> 21) & 0xFF) | 0x80;
  1280. pEncodeWrite[1] = ((iDataLength >> 14) & 0xFF) | 0x80;
  1281. pEncodeWrite[2] = ((iDataLength >> 7) & 0xFF) | 0x80;
  1282. pEncodeWrite[3] = ((iDataLength >> 0) & 0xFF) | 0x80;
  1283. pEncodeWrite += 4;
  1284. const uint8 *pEncodeRead = (const uint8 *)pToEncode;
  1285. const uint8 *pEncodeStop = pEncodeRead + iDataLength;
  1286. uint8 *pEncodeWriteLastControlByte = pEncodeWrite;
  1287. int iControl = 0;
  1288. //Encode the data
  1289. while( pEncodeRead < pEncodeStop )
  1290. {
  1291. if( iControl == 0 )
  1292. {
  1293. pEncodeWriteLastControlByte = pEncodeWrite;
  1294. *pEncodeWriteLastControlByte = 0x80;
  1295. }
  1296. else
  1297. {
  1298. *pEncodeWrite = *pEncodeRead | 0x80; //encoded data always has the MSB bit set (cheap avoidance of null terminators)
  1299. *pEncodeWriteLastControlByte |= (((*pEncodeRead) & 0x80) ^ 0x80) >> iControl; //We use the control byte to XOR the MSB back to original values on decode
  1300. ++pEncodeRead;
  1301. }
  1302. ++pEncodeWrite;
  1303. ++iControl;
  1304. iControl &= 7; //8->0
  1305. }
  1306. *pEncodeWrite = '\0';
  1307. return iEncodedSize - 1;
  1308. }
  1309. //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.
  1310. //Returns:
  1311. // >= 0 is the decoded data size
  1312. // INT_MIN (most negative value possible) indicates an improperly formatted string (not our data)
  1313. // all other negative values are the negative of how much dest buffer size is necessary.
  1314. int DecodeBinaryFromString( const char *pString, void *pDestBuffer, int iDestBufferSize, char **ppParseFinishOut )
  1315. {
  1316. const uint8 *pDecodeRead = (const uint8 *)pString;
  1317. if( (pDecodeRead[0] < 0x80) || (pDecodeRead[1] < 0x80) || (pDecodeRead[2] < 0x80) || (pDecodeRead[3] < 0x80) )
  1318. {
  1319. if( ppParseFinishOut != NULL )
  1320. *ppParseFinishOut = (char *)pString;
  1321. return INT_MIN; //Don't know what the string is, but it's not our format
  1322. }
  1323. int iDecodedSize = 0;
  1324. iDecodedSize |= (pDecodeRead[0] & 0x7F) << 21;
  1325. iDecodedSize |= (pDecodeRead[1] & 0x7F) << 14;
  1326. iDecodedSize |= (pDecodeRead[2] & 0x7F) << 7;
  1327. iDecodedSize |= (pDecodeRead[3] & 0x7F) << 0;
  1328. pDecodeRead += 4;
  1329. int iTextLength = iDecodedSize;
  1330. iTextLength += (iTextLength + 6) / 7; //Have 1 control byte for every 7 actual bytes
  1331. //make sure it's formatted properly
  1332. for( int i = 0; i != iTextLength; ++i )
  1333. {
  1334. if( pDecodeRead[i] < 0x80 ) //encoded data always has MSB set
  1335. {
  1336. if( ppParseFinishOut != NULL )
  1337. *ppParseFinishOut = (char *)pString;
  1338. return INT_MIN; //either not our data, or part of the string is missing
  1339. }
  1340. }
  1341. if( iDestBufferSize < iDecodedSize )
  1342. {
  1343. if( ppParseFinishOut != NULL )
  1344. *ppParseFinishOut = (char *)pDecodeRead;
  1345. return -iDecodedSize; //dest buffer not big enough to hold the data
  1346. }
  1347. const uint8 *pStopDecoding = pDecodeRead + iTextLength;
  1348. uint8 *pDecodeWrite = (uint8 *)pDestBuffer;
  1349. int iControl = 0;
  1350. int iLSBXOR = 0;
  1351. while( pDecodeRead < pStopDecoding )
  1352. {
  1353. if( iControl == 0 )
  1354. {
  1355. iLSBXOR = *pDecodeRead;
  1356. }
  1357. else
  1358. {
  1359. *pDecodeWrite = *pDecodeRead ^ ((iLSBXOR << iControl) & 0x80);
  1360. ++pDecodeWrite;
  1361. }
  1362. ++pDecodeRead;
  1363. ++iControl;
  1364. iControl &= 7; //8->0
  1365. }
  1366. if( ppParseFinishOut != NULL )
  1367. *ppParseFinishOut = (char *)pDecodeRead;
  1368. return iDecodedSize;
  1369. }