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