Counter Strike : Global Offensive Source Code
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.

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