extended_trace.cpp 13 KB

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