OSDN Git Service

Show selected refs in list ctrl
[tortoisegit/TortoiseGitJp.git] / src / crashrpt / StackTrace.cpp
1 /*----------------------------------------------------------------------\r
2    John Robbins - Microsoft Systems Journal Bugslayer Column - Feb 99\r
3 ----------------------------------------------------------------------*/\r
4 #include <stdafx.h>\r
5 \r
6 #include "StackTrace.h"\r
7 #include "SymbolEngine.h"\r
8 \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
13 #include <map>\r
14 #pragma warning(pop)\r
15 \r
16 /*//////////////////////////////////////////////////////////////////////\r
17                            File Scope Globals\r
18 //////////////////////////////////////////////////////////////////////*/\r
19 \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
25 \r
26 static CSymbolEngine & GetSymbolEngine()\r
27 {\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
32                 CSymbolEngine   cSym;\r
33             HANDLE hProcess = GetCurrentProcess ( ) ;\r
34         DWORD dwOpts = SymGetOptions ( ) ;\r
35 \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
41                                              NULL     ,\r
42                                              TRUE     ) )\r
43             {\r
44             OutputDebugString ( "DiagAssert : Unable to initialize the "\r
45                     "symbol engine!!!\n" ) ;\r
46 \r
47 #ifdef _DEBUG\r
48             DebugBreak ( ) ;\r
49 #endif\r
50             }\r
51       }\r
52         return iter->second;\r
53 }\r
54 \r
55 static DWORD_PTR __stdcall GetModBase ( HANDLE hProcess , DWORD_PTR dwAddr )\r
56 {\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
63 \r
64     if ( cSym.SymGetModuleInfo ( dwAddr , &stIHM ) )\r
65     {\r
66         return ( stIHM.BaseOfImage ) ;\r
67     }\r
68     else\r
69     {\r
70         // Let's go fishing.\r
71         MEMORY_BASIC_INFORMATION stMBI ;\r
72 \r
73         if ( 0 != VirtualQueryEx ( hProcess         ,\r
74                                    (LPCVOID)dwAddr  ,\r
75                                    &stMBI           ,\r
76                                    sizeof ( stMBI )  ) )\r
77         {\r
78             // Try and load it.\r
79             DWORD dwNameLen = 0 ;\r
80             TCHAR szFile[ MAX_PATH ] ;\r
81             szFile[0] = '\0';\r
82             dwNameLen = GetModuleFileName ( (HINSTANCE)\r
83                                                 stMBI.AllocationBase ,\r
84                                             szFile                   ,\r
85                                             MAX_PATH                  );\r
86             HANDLE hFile = NULL ;\r
87 \r
88             if ( 0 != dwNameLen )\r
89             {\r
90                 hFile = CreateFile ( szFile       ,\r
91                                      GENERIC_READ    ,\r
92                                      FILE_SHARE_READ ,\r
93                                      NULL            ,\r
94                                      OPEN_EXISTING   ,\r
95                                      0               ,\r
96                                      0                ) ;\r
97             }\r
98 #ifdef NOTDEF_DEBUG\r
99             DWORD dwRet =\r
100 #endif\r
101             cSym.SymLoadModule ( hFile                            ,\r
102                                   ( dwNameLen ? szFile : NULL )    ,\r
103                                    NULL                             ,\r
104                                    (DWORD)stMBI.AllocationBase      ,\r
105                                    0                                 );\r
106                         ::CloseHandle(hFile);\r
107                         \r
108 #ifdef NOTDEF_DEBUG\r
109             if ( 0 == dwRet )\r
110             {\r
111                 ATLTRACE ( "SymLoadModule failed : 0x%08X\n" ,\r
112                         GetLastError ( )                   ) ;\r
113             }\r
114 #endif  // _DEBUG\r
115             return ( (DWORD)stMBI.AllocationBase ) ;\r
116         }\r
117     }\r
118     return ( 0 ) ;\r
119 }\r
120 \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
125 {\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
130 \r
131         if (ImageName != NULL) {\r
132                 LPCTSTR szName = strchr ( ImageName ,  ( '\\' ) ) ;\r
133                 if ( NULL != szName ) {\r
134                         szName++ ;\r
135                 } else {\r
136                         szName = const_cast<char *>(ImageName) ;\r
137                 }\r
138                 pCurrPos += _snprintf ( pCurrPos ,  sizeof buffer - (pCurrPos - buffer), ( "%s: " ) , szName ) ;\r
139         } else {\r
140         pCurrPos += _snprintf ( pCurrPos , sizeof buffer - (pCurrPos - buffer),  ( "<unknown module>: " ) );\r
141         }\r
142         if (FunctionName != NULL) {\r
143         if ( 0 == functionDisp ) {\r
144             pCurrPos += _snprintf ( pCurrPos , sizeof buffer - (pCurrPos - buffer),  ( "%s" ) , FunctionName);\r
145                 } else {\r
146             pCurrPos += _snprintf ( pCurrPos               , sizeof buffer - (pCurrPos - buffer), \r
147                                     ( "%s + %d bytes" ) ,\r
148                                    FunctionName             ,\r
149                                    functionDisp                  ) ;\r
150                 }\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
155             pCurrPos = buffer;\r
156             pCurrPos += _snprintf ( pCurrPos                  , sizeof buffer - (pCurrPos - buffer), \r
157                                    ( "\t\t%s, Line %d" ) ,\r
158                                   Filename             ,\r
159                                   LineNumber            ) ;\r
160             if ( 0 != lineDisp )\r
161                   {\r
162                 pCurrPos += _snprintf ( pCurrPos             , sizeof buffer - (pCurrPos - buffer), \r
163                                         ( " + %d bytes" ) ,\r
164                                        lineDisp                ) ;\r
165                   }\r
166                 }\r
167         } else {\r
168         pCurrPos += _snprintf ( pCurrPos , sizeof buffer - (pCurrPos - buffer),  ( "<unknown symbol>" ) ) ;\r
169         }\r
170     // Tack on a CRLF.\r
171     pCurrPos += _snprintf ( pCurrPos , sizeof buffer - (pCurrPos - buffer),  ( "\n" ) ) ;\r
172     OutputDebugString ( buffer );\r
173 }\r
174 \r
175 \r
176 void AddressToSymbol(DWORD_PTR dwAddr, TraceCallbackFunction pFunction, LPVOID data)\r
177 {\r
178     char szTemp [ MAX_PATH + sizeof ( IMAGEHLP_SYMBOL ) ] ;\r
179 \r
180     PIMAGEHLP_SYMBOL pIHS = (PIMAGEHLP_SYMBOL)&szTemp ;\r
181 \r
182     IMAGEHLP_MODULE stIHM ;\r
183     IMAGEHLP_LINE stIHL ;\r
184 \r
185         bool haveModule = false;\r
186         bool haveFunction = false;\r
187         bool haveLine = false;\r
188 \r
189         CSymbolEngine & cSym = GetSymbolEngine();\r
190 \r
191     SecureZeroMemory ( pIHS , MAX_PATH + sizeof ( IMAGEHLP_SYMBOL ) ) ;\r
192     SecureZeroMemory ( &stIHM , sizeof ( IMAGEHLP_MODULE ) ) ;\r
193     SecureZeroMemory ( &stIHL , sizeof ( IMAGEHLP_LINE ) ) ;\r
194 \r
195     pIHS->SizeOfStruct = sizeof ( IMAGEHLP_SYMBOL ) ;\r
196     pIHS->Address = dwAddr ;\r
197     pIHS->MaxNameLength = MAX_PATH ;\r
198 \r
199     stIHM.SizeOfStruct = sizeof ( IMAGEHLP_MODULE ) ;\r
200 \r
201 \r
202     // Get the module name.\r
203         haveModule = (0 != cSym.SymGetModuleInfo ( dwAddr , &stIHM ));\r
204 \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
209       {\r
210                 haveFunction = true;\r
211 \r
212 \r
213         // If I got a symbol, give the source and line a whirl.\r
214 \r
215 \r
216         stIHL.SizeOfStruct = sizeof ( IMAGEHLP_LINE ) ;\r
217 \r
218         haveLine = 0 != cSym.SymGetLineFromAddr ( dwAddr  ,\r
219                                               &dwLineDisp ,\r
220                                               &stIHL   );\r
221       }\r
222         if (pFunction != NULL) {\r
223 \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
227                         data);\r
228         }\r
229 }\r
230 \r
231 void DoStackTrace ( int numSkip, int depth, TraceCallbackFunction pFunction, CONTEXT *pContext, LPVOID data )\r
232 {\r
233     HANDLE hProcess = GetCurrentProcess ( ) ;\r
234 \r
235         if (pFunction == NULL) {\r
236                 pFunction = PrintAddress;\r
237         }\r
238 \r
239     // The symbol engine is initialized so do the stack walk.\r
240 \r
241     // The thread information - if not supplied.\r
242     CONTEXT    stCtx  ;\r
243         if (pContext == NULL) {\r
244 \r
245                 stCtx.ContextFlags = CONTEXT_FULL ;\r
246 \r
247                 if ( GetThreadContext ( GetCurrentThread ( ) , &stCtx ) )\r
248                   {\r
249                         pContext = &stCtx;\r
250                 }\r
251         }\r
252         if (pContext != NULL) {\r
253         STACKFRAME stFrame ;\r
254         DWORD      dwMachine ;\r
255 \r
256         SecureZeroMemory ( &stFrame , sizeof ( STACKFRAME ) ) ;\r
257 \r
258         stFrame.AddrPC.Mode = AddrModeFlat ;\r
259 \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
267 \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
275 \r
276 #elif defined (_M_ALPHA)\r
277         dwMachine                = IMAGE_FILE_MACHINE_ALPHA ;\r
278         stFrame.AddrPC.Offset    = (unsigned long)pContext->Fir ;\r
279 #else\r
280 #error ( "Unknown machine!" )\r
281 #endif\r
282 \r
283         // Loop for the first <depth> stack elements.\r
284         for ( int i = 0 ; i < depth ; i++ )\r
285             {\r
286             if ( FALSE == StackWalk ( dwMachine              ,\r
287                                       hProcess               ,\r
288                                       //hProcess               ,\r
289                                                                           GetCurrentThread(),\r
290                                       &stFrame               ,\r
291                                       pContext               ,\r
292                                       NULL                   ,\r
293                                       SymFunctionTableAccess ,\r
294                                       GetModBase             ,\r
295                                       NULL                    ) )\r
296                   {\r
297                 break ;\r
298                   }\r
299             if ( i >= numSkip )\r
300                   {\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
304                         {\r
305                                     AddressToSymbol ( stFrame.AddrPC.Offset, pFunction, data ) ;\r
306                         }\r
307                   }\r
308             }\r
309 \r
310       }\r
311 }\r