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.

407 lines
11 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //////////////////////////////////////////////////////////////////////////////////////
  3. //
  4. // Written by Zoltan Csizmadia, [email protected]
  5. // For companies(Austin,TX): If you would like to get my resume, send an email.
  6. //
  7. // The source is free, but if you want to use it, mention my name and e-mail address
  8. //
  9. // History:
  10. // 1.0 Initial version Zoltan Csizmadia
  11. //
  12. //////////////////////////////////////////////////////////////////////////////////////
  13. //
  14. // ExtendedTrace.cpp
  15. //
  16. // Include StdAfx.h, if you're using precompiled
  17. // header through StdAfx.h
  18. //#include "stdafx.h"
  19. #if defined(_DEBUG) && defined(WIN32)
  20. #include "tier0/valve_off.h"
  21. #include <stdio.h>
  22. #include <windows.h>
  23. #include <tchar.h>
  24. #include <ImageHlp.h>
  25. #include "tier0/valve_on.h"
  26. #include "ExtendedTrace.h"
  27. #define BUFFERSIZE 0x200
  28. extern void OutputDebugStringFormat( const char *pMsg, ... );
  29. // Unicode safe char* -> TCHAR* conversion
  30. void PCSTR2LPTSTR( PCSTR lpszIn, LPTSTR lpszOut )
  31. {
  32. #if defined(UNICODE)||defined(_UNICODE)
  33. ULONG index = 0;
  34. PCSTR lpAct = lpszIn;
  35. for( ; ; lpAct++ )
  36. {
  37. lpszOut[index++] = (TCHAR)(*lpAct);
  38. if ( *lpAct == 0 )
  39. break;
  40. }
  41. #else
  42. // This is trivial :)
  43. strcpy( lpszOut, lpszIn );
  44. #endif
  45. }
  46. // Let's figure out the path for the symbol files
  47. // Search path= ".;%_NT_SYMBOL_PATH%;%_NT_ALTERNATE_SYMBOL_PATH%;%SYSTEMROOT%;%SYSTEMROOT%\System32;" + lpszIniPath
  48. // Note: There is no size check for lpszSymbolPath!
  49. void InitSymbolPath( PSTR lpszSymbolPath, PCSTR lpszIniPath )
  50. {
  51. CHAR lpszPath[BUFFERSIZE];
  52. // Creating the default path
  53. // ".;%_NT_SYMBOL_PATH%;%_NT_ALTERNATE_SYMBOL_PATH%;%SYSTEMROOT%;%SYSTEMROOT%\System32;"
  54. strcpy( lpszSymbolPath, "." );
  55. // environment variable _NT_SYMBOL_PATH
  56. if ( GetEnvironmentVariableA( "_NT_SYMBOL_PATH", lpszPath, BUFFERSIZE ) )
  57. {
  58. strcat( lpszSymbolPath, ";" );
  59. strcat( lpszSymbolPath, lpszPath );
  60. }
  61. // environment variable _NT_ALTERNATE_SYMBOL_PATH
  62. if ( GetEnvironmentVariableA( "_NT_ALTERNATE_SYMBOL_PATH", lpszPath, BUFFERSIZE ) )
  63. {
  64. strcat( lpszSymbolPath, ";" );
  65. strcat( lpszSymbolPath, lpszPath );
  66. }
  67. // environment variable SYSTEMROOT
  68. if ( GetEnvironmentVariableA( "SYSTEMROOT", lpszPath, BUFFERSIZE ) )
  69. {
  70. strcat( lpszSymbolPath, ";" );
  71. strcat( lpszSymbolPath, lpszPath );
  72. strcat( lpszSymbolPath, ";" );
  73. // SYSTEMROOT\System32
  74. strcat( lpszSymbolPath, lpszPath );
  75. strcat( lpszSymbolPath, "\\System32" );
  76. }
  77. // Add user defined path
  78. if ( lpszIniPath != NULL )
  79. if ( lpszIniPath[0] != '\0' )
  80. {
  81. strcat( lpszSymbolPath, ";" );
  82. strcat( lpszSymbolPath, lpszIniPath );
  83. }
  84. }
  85. // Uninitialize the loaded symbol files
  86. BOOL UninitSymInfo()
  87. {
  88. return SymCleanup( GetCurrentProcess() );
  89. }
  90. // Initializes the symbol files
  91. BOOL InitSymInfo( PCSTR lpszInitialSymbolPath )
  92. {
  93. CHAR lpszSymbolPath[BUFFERSIZE];
  94. DWORD symOptions = SymGetOptions();
  95. symOptions |= SYMOPT_LOAD_LINES;
  96. symOptions &= ~SYMOPT_UNDNAME;
  97. SymSetOptions( symOptions );
  98. // Get the search path for the symbol files
  99. InitSymbolPath( lpszSymbolPath, lpszInitialSymbolPath );
  100. return SymInitialize( GetCurrentProcess(), lpszSymbolPath, TRUE);
  101. }
  102. // Get the module name from a given address
  103. BOOL GetModuleNameFromAddress( UINT address, LPTSTR lpszModule )
  104. {
  105. BOOL ret = FALSE;
  106. IMAGEHLP_MODULE moduleInfo;
  107. ::ZeroMemory( &moduleInfo, sizeof(moduleInfo) );
  108. moduleInfo.SizeOfStruct = sizeof(moduleInfo);
  109. if ( SymGetModuleInfo( GetCurrentProcess(), (DWORD)address, &moduleInfo ) )
  110. {
  111. // Got it!
  112. PCSTR2LPTSTR( moduleInfo.ModuleName, lpszModule );
  113. ret = TRUE;
  114. }
  115. else
  116. // Not found :(
  117. _tcscpy( lpszModule, _T("?") );
  118. return ret;
  119. }
  120. // Get function prototype and parameter info from ip address and stack address
  121. BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, LPTSTR lpszSymbol )
  122. {
  123. BOOL ret = FALSE;
  124. DWORD dwDisp = 0;
  125. DWORD dwSymSize = 10000;
  126. TCHAR lpszUnDSymbol[BUFFERSIZE]=_T("?");
  127. CHAR lpszNonUnicodeUnDSymbol[BUFFERSIZE]="?";
  128. LPTSTR lpszParamSep = NULL;
  129. LPCTSTR lpszParsed = lpszUnDSymbol;
  130. PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL)GlobalAlloc( GMEM_FIXED, dwSymSize );
  131. ::ZeroMemory( pSym, dwSymSize );
  132. pSym->SizeOfStruct = dwSymSize;
  133. pSym->MaxNameLength = dwSymSize - sizeof(IMAGEHLP_SYMBOL);
  134. // Set the default to unknown
  135. _tcscpy( lpszSymbol, _T("?") );
  136. // Get symbol info for IP
  137. if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, &dwDisp, pSym ) )
  138. {
  139. // Make the symbol readable for humans
  140. UnDecorateSymbolName( pSym->Name, lpszNonUnicodeUnDSymbol, BUFFERSIZE,
  141. UNDNAME_COMPLETE |
  142. UNDNAME_NO_THISTYPE |
  143. UNDNAME_NO_SPECIAL_SYMS |
  144. UNDNAME_NO_MEMBER_TYPE |
  145. UNDNAME_NO_MS_KEYWORDS |
  146. UNDNAME_NO_ACCESS_SPECIFIERS );
  147. // Symbol information is ANSI string
  148. PCSTR2LPTSTR( lpszNonUnicodeUnDSymbol, lpszUnDSymbol );
  149. // I am just smarter than the symbol file :)
  150. if ( _tcscmp(lpszUnDSymbol, _T("_WinMain@16")) == 0 )
  151. _tcscpy(lpszUnDSymbol, _T("WinMain(HINSTANCE,HINSTANCE,LPCTSTR,int)"));
  152. else
  153. if ( _tcscmp(lpszUnDSymbol, _T("_main")) == 0 )
  154. _tcscpy(lpszUnDSymbol, _T("main(int,TCHAR * *)"));
  155. else
  156. if ( _tcscmp(lpszUnDSymbol, _T("_mainCRTStartup")) == 0 )
  157. _tcscpy(lpszUnDSymbol, _T("mainCRTStartup()"));
  158. else
  159. if ( _tcscmp(lpszUnDSymbol, _T("_wmain")) == 0 )
  160. _tcscpy(lpszUnDSymbol, _T("wmain(int,TCHAR * *,TCHAR * *)"));
  161. else
  162. if ( _tcscmp(lpszUnDSymbol, _T("_wmainCRTStartup")) == 0 )
  163. _tcscpy(lpszUnDSymbol, _T("wmainCRTStartup()"));
  164. lpszSymbol[0] = _T('\0');
  165. // Let's go through the stack, and modify the function prototype, and insert the actual
  166. // parameter values from the stack
  167. if ( _tcsstr( lpszUnDSymbol, _T("(void)") ) == NULL && _tcsstr( lpszUnDSymbol, _T("()") ) == NULL)
  168. {
  169. ULONG index = 0;
  170. for( ; ; index++ )
  171. {
  172. lpszParamSep = _tcschr( lpszParsed, _T(',') );
  173. if ( lpszParamSep == NULL )
  174. break;
  175. *lpszParamSep = _T('\0');
  176. _tcscat( lpszSymbol, lpszParsed );
  177. _stprintf( lpszSymbol + _tcslen(lpszSymbol), _T("=0x%08X,"), *((ULONG*)(stackAddress) + 2 + index) );
  178. lpszParsed = lpszParamSep + 1;
  179. }
  180. lpszParamSep = _tcschr( lpszParsed, _T(')') );
  181. if ( lpszParamSep != NULL )
  182. {
  183. *lpszParamSep = _T('\0');
  184. _tcscat( lpszSymbol, lpszParsed );
  185. _stprintf( lpszSymbol + _tcslen(lpszSymbol), _T("=0x%08X)"), *((ULONG*)(stackAddress) + 2 + index) );
  186. lpszParsed = lpszParamSep + 1;
  187. }
  188. }
  189. _tcscat( lpszSymbol, lpszParsed );
  190. ret = TRUE;
  191. }
  192. GlobalFree( pSym );
  193. return ret;
  194. }
  195. // Get source file name and line number from IP address
  196. // The output format is: "sourcefile(linenumber)" or
  197. // "modulename!address" or
  198. // "address"
  199. BOOL GetSourceInfoFromAddress( UINT address, LPTSTR lpszSourceInfo )
  200. {
  201. BOOL ret = FALSE;
  202. IMAGEHLP_LINE lineInfo;
  203. DWORD dwDisp;
  204. TCHAR lpszFileName[BUFFERSIZE] = _T("");
  205. TCHAR lpModuleInfo[BUFFERSIZE] = _T("");
  206. _tcscpy( lpszSourceInfo, _T("?(?)") );
  207. ::ZeroMemory( &lineInfo, sizeof( lineInfo ) );
  208. lineInfo.SizeOfStruct = sizeof( lineInfo );
  209. if ( SymGetLineFromAddr( GetCurrentProcess(), address, &dwDisp, &lineInfo ) )
  210. {
  211. // Got it. Let's use "sourcefile(linenumber)" format
  212. PCSTR2LPTSTR( lineInfo.FileName, lpszFileName );
  213. _stprintf( lpszSourceInfo, _T("%s(%d)"), lpszFileName, lineInfo.LineNumber );
  214. ret = TRUE;
  215. }
  216. else
  217. {
  218. // There is no source file information. :(
  219. // Let's use the "modulename!address" format
  220. GetModuleNameFromAddress( address, lpModuleInfo );
  221. if ( lpModuleInfo[0] == _T('?') || lpModuleInfo[0] == _T('\0'))
  222. // There is no modulename information. :((
  223. // Let's use the "address" format
  224. _stprintf( lpszSourceInfo, _T("?") );
  225. else
  226. _stprintf( lpszSourceInfo, _T("%s"), lpModuleInfo );
  227. ret = FALSE;
  228. }
  229. return ret;
  230. }
  231. // TRACE message with source link.
  232. // The format is: sourcefile(linenumber) : message
  233. void SrcLinkTrace( LPCTSTR lpszMessage, LPCTSTR lpszFileName, ULONG nLineNumber )
  234. {
  235. OutputDebugStringFormat(
  236. _T("%s(%d) : %s"),
  237. lpszFileName,
  238. nLineNumber,
  239. lpszMessage );
  240. }
  241. void StackTrace( HANDLE hThread, LPCTSTR lpszMessage )
  242. {
  243. STACKFRAME callStack;
  244. BOOL bResult;
  245. CONTEXT context;
  246. TCHAR symInfo[BUFFERSIZE] = _T("?");
  247. TCHAR srcInfo[BUFFERSIZE] = _T("?");
  248. HANDLE hProcess = GetCurrentProcess();
  249. // If it's not this thread, let's suspend it, and resume it at the end
  250. if ( hThread != GetCurrentThread() )
  251. if ( SuspendThread( hThread ) == -1 )
  252. {
  253. // whaaat ?!
  254. OutputDebugStringFormat( _T("Call stack info(thread=0x%X) failed.\n") );
  255. return;
  256. }
  257. ::ZeroMemory( &context, sizeof(context) );
  258. context.ContextFlags = CONTEXT_FULL;
  259. if ( !GetThreadContext( hThread, &context ) )
  260. {
  261. OutputDebugStringFormat( _T("Call stack info(thread=0x%X) failed.\n") );
  262. return;
  263. }
  264. ::ZeroMemory( &callStack, sizeof(callStack) );
  265. callStack.AddrPC.Offset = context.Eip;
  266. callStack.AddrStack.Offset = context.Esp;
  267. callStack.AddrFrame.Offset = context.Ebp;
  268. callStack.AddrPC.Mode = AddrModeFlat;
  269. callStack.AddrStack.Mode = AddrModeFlat;
  270. callStack.AddrFrame.Mode = AddrModeFlat;
  271. for( ULONG index = 0; ; index++ )
  272. {
  273. bResult = StackWalk(
  274. IMAGE_FILE_MACHINE_I386,
  275. hProcess,
  276. hThread,
  277. &callStack,
  278. NULL,
  279. NULL,
  280. SymFunctionTableAccess,
  281. SymGetModuleBase,
  282. NULL);
  283. if ( index == 0 )
  284. continue;
  285. if( !bResult || callStack.AddrFrame.Offset == 0 )
  286. break;
  287. GetFunctionInfoFromAddresses( callStack.AddrPC.Offset, callStack.AddrFrame.Offset, symInfo );
  288. GetSourceInfoFromAddress( callStack.AddrPC.Offset, srcInfo );
  289. OutputDebugStringFormat( _T(" %s : %s\n"), srcInfo, symInfo );
  290. }
  291. if ( hThread != GetCurrentThread() )
  292. ResumeThread( hThread );
  293. }
  294. void FunctionParameterInfo()
  295. {
  296. STACKFRAME callStack;
  297. BOOL bResult = FALSE;
  298. CONTEXT context;
  299. TCHAR lpszFnInfo[BUFFERSIZE];
  300. HANDLE hProcess = GetCurrentProcess();
  301. HANDLE hThread = GetCurrentThread();
  302. ::ZeroMemory( &context, sizeof(context) );
  303. context.ContextFlags = CONTEXT_FULL;
  304. if ( !GetThreadContext( hThread, &context ) )
  305. {
  306. OutputDebugStringFormat( _T("Function info(thread=0x%X) failed.\n") );
  307. return;
  308. }
  309. ::ZeroMemory( &callStack, sizeof(callStack) );
  310. callStack.AddrPC.Offset = context.Eip;
  311. callStack.AddrStack.Offset = context.Esp;
  312. callStack.AddrFrame.Offset = context.Ebp;
  313. callStack.AddrPC.Mode = AddrModeFlat;
  314. callStack.AddrStack.Mode = AddrModeFlat;
  315. callStack.AddrFrame.Mode = AddrModeFlat;
  316. for( ULONG index = 0; index < 2; index++ )
  317. {
  318. bResult = StackWalk(
  319. IMAGE_FILE_MACHINE_I386,
  320. hProcess,
  321. hThread,
  322. &callStack,
  323. NULL,
  324. NULL,
  325. SymFunctionTableAccess,
  326. SymGetModuleBase,
  327. NULL);
  328. }
  329. if ( bResult && callStack.AddrFrame.Offset != 0)
  330. {
  331. GetFunctionInfoFromAddresses( callStack.AddrPC.Offset, callStack.AddrFrame.Offset, lpszFnInfo );
  332. OutputDebugStringFormat( _T("Function info(thread=0x%X) : %s\n"), GetCurrentThreadId(), lpszFnInfo );
  333. }
  334. else
  335. OutputDebugStringFormat( _T("Function info(thread=0x%X) failed.\n") );
  336. }
  337. #endif //_DEBUG && WIN32