1 /*----------------------------------------------------------------------
\r
2 John Robbins - Microsoft Systems Journal Bugslayer Column - Feb 99
\r
3 ----------------------------------------------------------------------*/
\r
6 #include "StackTrace.h"
\r
7 #include "SymbolEngine.h"
\r
9 // 4710: inline function not inlined
\r
10 #pragma warning(disable: 4710)
\r
11 #pragma warning(disable: 4786)
\r
12 #pragma warning(push, 3)
\r
14 #pragma warning(pop)
\r
16 /*//////////////////////////////////////////////////////////////////////
\r
18 //////////////////////////////////////////////////////////////////////*/
\r
20 // The symbol engine. Indexed by process-id so there is no collision between processes.
\r
21 #pragma warning(push, 3)
\r
22 typedef std::map<DWORD, CSymbolEngine> TSymbolEngineMap;
\r
23 static TSymbolEngineMap g_cSymMap;
\r
24 #pragma warning(pop)
\r
26 static CSymbolEngine & GetSymbolEngine()
\r
28 DWORD CurrProcessId = GetCurrentProcessId();
\r
29 TSymbolEngineMap::iterator iter;
\r
30 iter = g_cSymMap.lower_bound(CurrProcessId);
\r
31 if (iter == g_cSymMap.end() || iter->first != CurrProcessId) {
\r
33 HANDLE hProcess = GetCurrentProcess ( ) ;
\r
34 DWORD dwOpts = SymGetOptions ( ) ;
\r
36 // Turn on load lines.
\r
37 SymSetOptions ( dwOpts |
\r
38 SYMOPT_LOAD_LINES ) ;
\r
39 iter = g_cSymMap.insert(iter, std::make_pair(CurrProcessId, CSymbolEngine()));
\r
40 if ( FALSE == iter->second.SymInitialize ( hProcess ,
\r
44 OutputDebugString ( "DiagAssert : Unable to initialize the "
\r
45 "symbol engine!!!\n" ) ;
\r
52 return iter->second;
\r
55 static DWORD_PTR __stdcall GetModBase ( HANDLE hProcess , DWORD_PTR dwAddr )
\r
57 // Check in the symbol engine first.
\r
58 IMAGEHLP_MODULE stIHM ;
\r
59 CSymbolEngine & cSym = GetSymbolEngine();
\r
60 // This is what the MFC stack trace routines forgot to do so their
\r
61 // code will not get the info out of the symbol engine.
\r
62 stIHM.SizeOfStruct = sizeof ( IMAGEHLP_MODULE ) ;
\r
64 if ( cSym.SymGetModuleInfo ( dwAddr , &stIHM ) )
\r
66 return ( stIHM.BaseOfImage ) ;
\r
70 // Let's go fishing.
\r
71 MEMORY_BASIC_INFORMATION stMBI ;
\r
73 if ( 0 != VirtualQueryEx ( hProcess ,
\r
76 sizeof ( stMBI ) ) )
\r
79 DWORD dwNameLen = 0 ;
\r
80 TCHAR szFile[ MAX_PATH ] ;
\r
82 dwNameLen = GetModuleFileName ( (HINSTANCE)
\r
83 stMBI.AllocationBase ,
\r
86 HANDLE hFile = NULL ;
\r
88 if ( 0 != dwNameLen )
\r
90 hFile = CreateFile ( szFile ,
\r
101 cSym.SymLoadModule ( hFile ,
\r
102 ( dwNameLen ? szFile : NULL ) ,
\r
104 (DWORD)stMBI.AllocationBase ,
\r
106 ::CloseHandle(hFile);
\r
108 #ifdef NOTDEF_DEBUG
\r
111 ATLTRACE ( "SymLoadModule failed : 0x%08X\n" ,
\r
112 GetLastError ( ) ) ;
\r
115 return ( (DWORD)stMBI.AllocationBase ) ;
\r
121 static void PrintAddress (DWORD_PTR address, const char *ImageName,
\r
122 const char *FunctionName, DWORD_PTR functionDisp,
\r
123 const char *Filename, DWORD LineNumber, DWORD lineDisp,
\r
124 void * /* data, unused */ )
\r
126 static char buffer [ MAX_PATH*2 + 512 ];
\r
127 LPTSTR pCurrPos = buffer ;
\r
128 // Always stick the address in first.
\r
129 pCurrPos += _snprintf ( pCurrPos , sizeof buffer - (pCurrPos - buffer), addressFormat , address ) ;
\r
131 if (ImageName != NULL) {
\r
132 LPCTSTR szName = strchr ( ImageName , ( '\\' ) ) ;
\r
133 if ( NULL != szName ) {
\r
136 szName = const_cast<char *>(ImageName) ;
\r
138 pCurrPos += _snprintf ( pCurrPos , sizeof buffer - (pCurrPos - buffer), ( "%s: " ) , szName ) ;
\r
140 pCurrPos += _snprintf ( pCurrPos , sizeof buffer - (pCurrPos - buffer), ( "<unknown module>: " ) );
\r
142 if (FunctionName != NULL) {
\r
143 if ( 0 == functionDisp ) {
\r
144 pCurrPos += _snprintf ( pCurrPos , sizeof buffer - (pCurrPos - buffer), ( "%s" ) , FunctionName);
\r
146 pCurrPos += _snprintf ( pCurrPos , sizeof buffer - (pCurrPos - buffer),
\r
147 ( "%s + %d bytes" ) ,
\r
151 if (Filename != NULL) {
\r
152 // Put this on the next line and indented a bit.
\r
153 pCurrPos += _snprintf( pCurrPos, sizeof buffer - (pCurrPos - buffer), "-\n");
\r
154 OutputDebugString(buffer);
\r
156 pCurrPos += _snprintf ( pCurrPos , sizeof buffer - (pCurrPos - buffer),
\r
157 ( "\t\t%s, Line %d" ) ,
\r
160 if ( 0 != lineDisp )
\r
162 pCurrPos += _snprintf ( pCurrPos , sizeof buffer - (pCurrPos - buffer),
\r
163 ( " + %d bytes" ) ,
\r
168 pCurrPos += _snprintf ( pCurrPos , sizeof buffer - (pCurrPos - buffer), ( "<unknown symbol>" ) ) ;
\r
171 pCurrPos += _snprintf ( pCurrPos , sizeof buffer - (pCurrPos - buffer), ( "\n" ) ) ;
\r
172 OutputDebugString ( buffer );
\r
176 void AddressToSymbol(DWORD_PTR dwAddr, TraceCallbackFunction pFunction, LPVOID data)
\r
178 char szTemp [ MAX_PATH + sizeof ( IMAGEHLP_SYMBOL ) ] ;
\r
180 PIMAGEHLP_SYMBOL pIHS = (PIMAGEHLP_SYMBOL)&szTemp ;
\r
182 IMAGEHLP_MODULE stIHM ;
\r
183 IMAGEHLP_LINE stIHL ;
\r
185 bool haveModule = false;
\r
186 bool haveFunction = false;
\r
187 bool haveLine = false;
\r
189 CSymbolEngine & cSym = GetSymbolEngine();
\r
191 SecureZeroMemory ( pIHS , MAX_PATH + sizeof ( IMAGEHLP_SYMBOL ) ) ;
\r
192 SecureZeroMemory ( &stIHM , sizeof ( IMAGEHLP_MODULE ) ) ;
\r
193 SecureZeroMemory ( &stIHL , sizeof ( IMAGEHLP_LINE ) ) ;
\r
195 pIHS->SizeOfStruct = sizeof ( IMAGEHLP_SYMBOL ) ;
\r
196 pIHS->Address = dwAddr ;
\r
197 pIHS->MaxNameLength = MAX_PATH ;
\r
199 stIHM.SizeOfStruct = sizeof ( IMAGEHLP_MODULE ) ;
\r
202 // Get the module name.
\r
203 haveModule = (0 != cSym.SymGetModuleInfo ( dwAddr , &stIHM ));
\r
205 // Get the function.
\r
206 DWORD_PTR dwFuncDisp=0 ;
\r
207 DWORD dwLineDisp=0;
\r
208 if ( 0 != cSym.SymGetSymFromAddr ( dwAddr , &dwFuncDisp , pIHS ) )
\r
210 haveFunction = true;
\r
213 // If I got a symbol, give the source and line a whirl.
\r
216 stIHL.SizeOfStruct = sizeof ( IMAGEHLP_LINE ) ;
\r
218 haveLine = 0 != cSym.SymGetLineFromAddr ( dwAddr ,
\r
222 if (pFunction != NULL) {
\r
224 pFunction(dwAddr, haveModule ? stIHM.ImageName : NULL,
\r
225 haveFunction ? pIHS->Name : NULL, dwFuncDisp,
\r
226 haveLine ? stIHL.FileName : NULL, haveLine ? stIHL.LineNumber : 0, dwLineDisp,
\r
231 void DoStackTrace ( int numSkip, int depth, TraceCallbackFunction pFunction, CONTEXT *pContext, LPVOID data )
\r
233 HANDLE hProcess = GetCurrentProcess ( ) ;
\r
235 if (pFunction == NULL) {
\r
236 pFunction = PrintAddress;
\r
239 // The symbol engine is initialized so do the stack walk.
\r
241 // The thread information - if not supplied.
\r
243 if (pContext == NULL) {
\r
245 stCtx.ContextFlags = CONTEXT_FULL ;
\r
247 if ( GetThreadContext ( GetCurrentThread ( ) , &stCtx ) )
\r
252 if (pContext != NULL) {
\r
253 STACKFRAME stFrame ;
\r
256 SecureZeroMemory ( &stFrame , sizeof ( STACKFRAME ) ) ;
\r
258 stFrame.AddrPC.Mode = AddrModeFlat ;
\r
260 #if defined (_M_IX86)
\r
261 dwMachine = IMAGE_FILE_MACHINE_I386 ;
\r
262 stFrame.AddrPC.Offset = pContext->Eip ;
\r
263 stFrame.AddrStack.Offset = pContext->Esp ;
\r
264 stFrame.AddrStack.Mode = AddrModeFlat ;
\r
265 stFrame.AddrFrame.Offset = pContext->Ebp ;
\r
266 stFrame.AddrFrame.Mode = AddrModeFlat ;
\r
268 #elif defined (_M_AMD64)
\r
269 dwMachine = IMAGE_FILE_MACHINE_AMD64 ;
\r
270 stFrame.AddrPC.Offset = pContext->Rip ;
\r
271 stFrame.AddrStack.Offset = pContext->Rsp ;
\r
272 stFrame.AddrStack.Mode = AddrModeFlat ;
\r
273 stFrame.AddrFrame.Offset = pContext->Rbp ;
\r
274 stFrame.AddrFrame.Mode = AddrModeFlat ;
\r
276 #elif defined (_M_ALPHA)
\r
277 dwMachine = IMAGE_FILE_MACHINE_ALPHA ;
\r
278 stFrame.AddrPC.Offset = (unsigned long)pContext->Fir ;
\r
280 #error ( "Unknown machine!" )
\r
283 // Loop for the first <depth> stack elements.
\r
284 for ( int i = 0 ; i < depth ; i++ )
\r
286 if ( FALSE == StackWalk ( dwMachine ,
\r
289 GetCurrentThread(),
\r
293 SymFunctionTableAccess ,
\r
299 if ( i >= numSkip )
\r
301 // Also check that the address is not zero. Sometimes
\r
302 // StackWalk returns TRUE with a frame of zero.
\r
303 if ( 0 != stFrame.AddrPC.Offset )
\r
305 AddressToSymbol ( stFrame.AddrPC.Offset, pFunction, data ) ;
\r