1 /**********************************************************************
2  *
3  * StackWalker.cpp
4  * http://stackwalker.codeplex.com/
5  *
6  *
7  * History:
8  *  2005-07-27   v1    - First public release on http://www.codeproject.com/
9  *                       http://www.codeproject.com/threads/StackWalker.asp
10  *  2005-07-28   v2    - Changed the params of the constructor and ShowCallstack
11  *                       (to simplify the usage)
12  *  2005-08-01   v3    - Changed to use 'CONTEXT_FULL' instead of CONTEXT_ALL
13  *                       (should also be enough)
14  *                     - Changed to compile correctly with the PSDK of VC7.0
15  *                       (GetFileVersionInfoSizeA and GetFileVersionInfoA is wrongly defined:
16  *                        it uses LPSTR instead of LPCSTR as first paremeter)
17  *                     - Added declarations to support VC5/6 without using 'dbghelp.h'
18  *                     - Added a 'pUserData' member to the ShowCallstack function and the
19  *                       PReadProcessMemoryRoutine declaration (to pass some user-defined data,
20  *                       which can be used in the readMemoryFunction-callback)
21  *  2005-08-02   v4    - OnSymInit now also outputs the OS-Version by default
22  *                     - Added example for doing an exception-callstack-walking in main.cpp
23  *                       (thanks to owillebo: http://www.codeproject.com/script/profile/whos_who.asp?id=536268)
24  *  2005-08-05   v5    - Removed most Lint (http://www.gimpel.com/) errors... thanks to Okko Willeboordse!
25  *  2008-08-04   v6    - Fixed Bug: Missing LEAK-end-tag
26  *                       http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=2502890#xx2502890xx
27  *                       Fixed Bug: Compiled with "WIN32_LEAN_AND_MEAN"
28  *                       http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=1824718#xx1824718xx
29  *                       Fixed Bug: Compiling with "/Wall"
30  *                       http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=2638243#xx2638243xx
31  *                       Fixed Bug: Now checking SymUseSymSrv
32  *                       http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=1388979#xx1388979xx
33  *                       Fixed Bug: Support for recursive function calls
34  *                       http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=1434538#xx1434538xx
35  *                       Fixed Bug: Missing FreeLibrary call in "GetModuleListTH32"
36  *                       http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=1326923#xx1326923xx
37  *                       Fixed Bug: SymDia is number 7, not 9!
38  *  2008-09-11   v7      For some (undocumented) reason, dbhelp.h is needing a packing of 8!
39  *                       Thanks to Teajay which reported the bug...
40  *                       http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=2718933#xx2718933xx
41  *  2008-11-27   v8      Debugging Tools for Windows are now stored in a different directory
42  *                       Thanks to Luiz Salamon which reported this "bug"...
43  *                       http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=2822736#xx2822736xx
44  *  2009-04-10   v9      License slihtly corrected (<ORGANIZATION> replaced)
45  *  2009-11-01   v10     Moved to http://stackwalker.codeplex.com/
46  *  2009-11-02   v11     Now try to use IMAGEHLP_MODULE64_V3 if available
47  *  2010-04-15   v12     Added support for VS2010 RTM
48  *  2010-05-25   v13     Now using secure MyStrcCpy. Thanks to luke.simon:
49  *                       http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=3477467#xx3477467xx
50  *  2013-01-07   v14     Runtime Check Error VS2010 Debug Builds fixed:
51  *                       http://stackwalker.codeplex.com/workitem/10511
52  *
53  *
54  * LICENSE (http://www.opensource.org/licenses/bsd-license.php)
55  *
56  *   Copyright (c) 2005-2013, Jochen Kalmbach
57  *   All rights reserved.
58  *
59  *   Redistribution and use in source and binary forms, with or without modification,
60  *   are permitted provided that the following conditions are met:
61  *
62  *   Redistributions of source code must retain the above copyright notice,
63  *   this list of conditions and the following disclaimer.
64  *   Redistributions in binary form must reproduce the above copyright notice,
65  *   this list of conditions and the following disclaimer in the documentation
66  *   and/or other materials provided with the distribution.
67  *   Neither the name of Jochen Kalmbach nor the names of its contributors may be
68  *   used to endorse or promote products derived from this software without
69  *   specific prior written permission.
70  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
71  *   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
72  *   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
73  *   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
74  *   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
75  *   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
76  *   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
77  *   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
78  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
79  *   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
80  *
81  **********************************************************************/
82 #include <windows.h>
83 #include <tchar.h>
84 #include <stdio.h>
85 #include <stdlib.h>
86 #pragma comment(lib, "version.lib")  // for "VerQueryValue"
87 #pragma warning(disable:4826)
88 
89 #include "StackWalker.h"
90 
91 
92 // If VC7 and later, then use the shipped 'dbghelp.h'-file
93 #pragma pack(push,8)
94 #if _MSC_VER >= 1300
95 #include <dbghelp.h>
96 #else
97 // inline the important dbghelp.h-declarations...
98 typedef enum {
99     SymNone = 0,
100     SymCoff,
101     SymCv,
102     SymPdb,
103     SymExport,
104     SymDeferred,
105     SymSym,
106     SymDia,
107     SymVirtual,
108     NumSymTypes
109 } SYM_TYPE;
110 typedef struct _IMAGEHLP_LINE64 {
111     DWORD                       SizeOfStruct;           // set to sizeof(IMAGEHLP_LINE64)
112     PVOID                       Key;                    // internal
113     DWORD                       LineNumber;             // line number in file
114     PCHAR                       FileName;               // full filename
115     DWORD64                     Address;                // first instruction of line
116 } IMAGEHLP_LINE64, *PIMAGEHLP_LINE64;
117 typedef struct _IMAGEHLP_MODULE64 {
118     DWORD                       SizeOfStruct;           // set to sizeof(IMAGEHLP_MODULE64)
119     DWORD64                     BaseOfImage;            // base load address of module
120     DWORD                       ImageSize;              // virtual size of the loaded module
121     DWORD                       TimeDateStamp;          // date/time stamp from pe header
122     DWORD                       CheckSum;               // checksum from the pe header
123     DWORD                       NumSyms;                // number of symbols in the symbol table
124     SYM_TYPE                    SymType;                // type of symbols loaded
125     CHAR                        ModuleName[32];         // module name
126     CHAR                        ImageName[256];         // image name
127     CHAR                        LoadedImageName[256];   // symbol file name
128 } IMAGEHLP_MODULE64, *PIMAGEHLP_MODULE64;
129 typedef struct _IMAGEHLP_SYMBOL64 {
130     DWORD                       SizeOfStruct;           // set to sizeof(IMAGEHLP_SYMBOL64)
131     DWORD64                     Address;                // virtual address including dll base address
132     DWORD                       Size;                   // estimated size of symbol, can be zero
133     DWORD                       Flags;                  // info about the symbols, see the SYMF defines
134     DWORD                       MaxNameLength;          // maximum size of symbol name in 'Name'
135     CHAR                        Name[1];                // symbol name (null terminated string)
136 } IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64;
137 typedef enum {
138     AddrMode1616,
139     AddrMode1632,
140     AddrModeReal,
141     AddrModeFlat
142 } ADDRESS_MODE;
143 typedef struct _tagADDRESS64 {
144     DWORD64       Offset;
145     WORD          Segment;
146     ADDRESS_MODE  Mode;
147 } ADDRESS64, *LPADDRESS64;
148 typedef struct _KDHELP64 {
149     DWORD64   Thread;
150     DWORD   ThCallbackStack;
151     DWORD   ThCallbackBStore;
152     DWORD   NextCallback;
153     DWORD   FramePointer;
154     DWORD64   KiCallUserMode;
155     DWORD64   KeUserCallbackDispatcher;
156     DWORD64   SystemRangeStart;
157     DWORD64  Reserved[8];
158 } KDHELP64, *PKDHELP64;
159 typedef struct _tagSTACKFRAME64 {
160     ADDRESS64   AddrPC;               // program counter
161     ADDRESS64   AddrReturn;           // return address
162     ADDRESS64   AddrFrame;            // frame pointer
163     ADDRESS64   AddrStack;            // stack pointer
164     ADDRESS64   AddrBStore;           // backing store pointer
165     PVOID       FuncTableEntry;       // pointer to pdata/fpo or NULL
166     DWORD64     Params[4];            // possible arguments to the function
167     BOOL        Far;                  // WOW far call
168     BOOL        Virtual;              // is this a virtual frame?
169     DWORD64     Reserved[3];
170     KDHELP64    KdHelp;
171 } STACKFRAME64, *LPSTACKFRAME64;
172 typedef
173 BOOL
174 (__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)(
175     HANDLE      hProcess,
176     DWORD64     qwBaseAddress,
177     PVOID       lpBuffer,
178     DWORD       nSize,
179     LPDWORD     lpNumberOfBytesRead
180     );
181 typedef
182 PVOID
183 (__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)(
184     HANDLE  hProcess,
185     DWORD64 AddrBase
186     );
187 typedef
188 DWORD64
189 (__stdcall *PGET_MODULE_BASE_ROUTINE64)(
190     HANDLE  hProcess,
191     DWORD64 Address
192     );
193 typedef
194 DWORD64
195 (__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)(
196     HANDLE    hProcess,
197     HANDLE    hThread,
198     LPADDRESS64 lpaddr
199     );
200 #define SYMOPT_CASE_INSENSITIVE         0x00000001
201 #define SYMOPT_UNDNAME                  0x00000002
202 #define SYMOPT_DEFERRED_LOADS           0x00000004
203 #define SYMOPT_NO_CPP                   0x00000008
204 #define SYMOPT_LOAD_LINES               0x00000010
205 #define SYMOPT_OMAP_FIND_NEAREST        0x00000020
206 #define SYMOPT_LOAD_ANYTHING            0x00000040
207 #define SYMOPT_IGNORE_CVREC             0x00000080
208 #define SYMOPT_NO_UNQUALIFIED_LOADS     0x00000100
209 #define SYMOPT_FAIL_CRITICAL_ERRORS     0x00000200
210 #define SYMOPT_EXACT_SYMBOLS            0x00000400
211 #define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS   0x00000800
212 #define SYMOPT_IGNORE_NT_SYMPATH        0x00001000
213 #define SYMOPT_INCLUDE_32BIT_MODULES    0x00002000
214 #define SYMOPT_PUBLICS_ONLY             0x00004000
215 #define SYMOPT_NO_PUBLICS               0x00008000
216 #define SYMOPT_AUTO_PUBLICS             0x00010000
217 #define SYMOPT_NO_IMAGE_SEARCH          0x00020000
218 #define SYMOPT_SECURE                   0x00040000
219 #define SYMOPT_DEBUG                    0x80000000
220 #define UNDNAME_COMPLETE                 (0x0000)  // Enable full undecoration
221 #define UNDNAME_NAME_ONLY                (0x1000)  // Crack only the name for primary declaration;
222 #endif  // _MSC_VER < 1300
223 #pragma pack(pop)
224 
225 // Some missing defines (for VC5/6):
226 #ifndef INVALID_FILE_ATTRIBUTES
227 #define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
228 #endif
229 
230 
231 // secure-CRT_functions are only available starting with VC8
232 #if _MSC_VER < 1400
233 #define strcpy_s(dst, len, src) strcpy(dst, src)
234 #define strncpy_s(dst, len, src, maxLen) strncpy(dst, len, src)
235 #define strcat_s(dst, len, src) strcat(dst, src)
236 #define _snprintf_s _snprintf
237 #define _tcscat_s _tcscat
238 #endif
239 
MyStrCpy(char * szDest,size_t nMaxDestSize,const char * szSrc)240 static void MyStrCpy(char* szDest, size_t nMaxDestSize, const char* szSrc)
241 {
242   if (nMaxDestSize <= 0) return;
243   strncpy_s(szDest, nMaxDestSize, szSrc, _TRUNCATE);
244   szDest[nMaxDestSize-1] = 0;  // INFO: _TRUNCATE will ensure that it is nul-terminated; but with older compilers (<1400) it uses "strncpy" and this does not!)
245 }  // MyStrCpy
246 
247 // Normally it should be enough to use 'CONTEXT_FULL' (better would be 'CONTEXT_ALL')
248 #define USED_CONTEXT_FLAGS CONTEXT_FULL
249 
250 
251 class StackWalkerInternal
252 {
253 public:
StackWalkerInternal(StackWalker * parent,HANDLE hProcess)254   StackWalkerInternal(StackWalker *parent, HANDLE hProcess)
255   {
256     m_parent = parent;
257     m_hDbhHelp = NULL;
258     pSC = NULL;
259     m_hProcess = hProcess;
260     m_szSymPath = NULL;
261     pSFTA = NULL;
262     pSGLFA = NULL;
263     pSGMB = NULL;
264     pSGMI = NULL;
265     pSGO = NULL;
266     pSGSFA = NULL;
267     pSI = NULL;
268     pSLM = NULL;
269     pSSO = NULL;
270     pSW = NULL;
271     pUDSN = NULL;
272     pSGSP = NULL;
273   }
~StackWalkerInternal()274   ~StackWalkerInternal()
275   {
276     if (pSC != NULL)
277       pSC(m_hProcess);  // SymCleanup
278     if (m_hDbhHelp != NULL)
279       FreeLibrary(m_hDbhHelp);
280     m_hDbhHelp = NULL;
281     m_parent = NULL;
282     if(m_szSymPath != NULL)
283       free(m_szSymPath);
284     m_szSymPath = NULL;
285   }
Init(LPCSTR szSymPath)286   BOOL Init(LPCSTR szSymPath)
287   {
288     if (m_parent == NULL)
289       return FALSE;
290     // Dynamically load the Entry-Points for dbghelp.dll:
291     // First try to load the newsest one from
292     TCHAR szTemp[4096];
293     // But before wqe do this, we first check if the ".local" file exists
294     if (GetModuleFileName(NULL, szTemp, 4096) > 0)
295     {
296       _tcscat_s(szTemp, _T(".local"));
297       if (GetFileAttributes(szTemp) == INVALID_FILE_ATTRIBUTES)
298       {
299         // ".local" file does not exist, so we can try to load the dbghelp.dll from the "Debugging Tools for Windows"
300         // Ok, first try the new path according to the archtitecture:
301 #ifdef _M_IX86
302         if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) )
303         {
304           _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (x86)\\dbghelp.dll"));
305           // now check if the file exists:
306           if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
307           {
308             m_hDbhHelp = LoadLibrary(szTemp);
309           }
310         }
311 #elif _M_X64
312         if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) )
313         {
314           _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (x64)\\dbghelp.dll"));
315           // now check if the file exists:
316           if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
317           {
318             m_hDbhHelp = LoadLibrary(szTemp);
319           }
320         }
321 #elif _M_IA64
322         if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) )
323         {
324           _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (ia64)\\dbghelp.dll"));
325           // now check if the file exists:
326           if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
327           {
328             m_hDbhHelp = LoadLibrary(szTemp);
329           }
330         }
331 #endif
332         // If still not found, try the old directories...
333         if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) )
334         {
335           _tcscat_s(szTemp, _T("\\Debugging Tools for Windows\\dbghelp.dll"));
336           // now check if the file exists:
337           if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
338           {
339             m_hDbhHelp = LoadLibrary(szTemp);
340           }
341         }
342 #if defined _M_X64 || defined _M_IA64
343         // Still not found? Then try to load the (old) 64-Bit version:
344         if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) )
345         {
346           _tcscat_s(szTemp, _T("\\Debugging Tools for Windows 64-Bit\\dbghelp.dll"));
347           if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
348           {
349             m_hDbhHelp = LoadLibrary(szTemp);
350           }
351         }
352 #endif
353       }
354     }
355     if (m_hDbhHelp == NULL)  // if not already loaded, try to load a default-one
356       m_hDbhHelp = LoadLibrary( _T("dbghelp.dll") );
357     if (m_hDbhHelp == NULL)
358       return FALSE;
359     pSI = (tSI) GetProcAddress(m_hDbhHelp, "SymInitialize" );
360     pSC = (tSC) GetProcAddress(m_hDbhHelp, "SymCleanup" );
361 
362     pSW = (tSW) GetProcAddress(m_hDbhHelp, "StackWalk64" );
363     pSGO = (tSGO) GetProcAddress(m_hDbhHelp, "SymGetOptions" );
364     pSSO = (tSSO) GetProcAddress(m_hDbhHelp, "SymSetOptions" );
365 
366     pSFTA = (tSFTA) GetProcAddress(m_hDbhHelp, "SymFunctionTableAccess64" );
367     pSGLFA = (tSGLFA) GetProcAddress(m_hDbhHelp, "SymGetLineFromAddr64" );
368     pSGMB = (tSGMB) GetProcAddress(m_hDbhHelp, "SymGetModuleBase64" );
369     pSGMI = (tSGMI) GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64" );
370     pSGSFA = (tSGSFA) GetProcAddress(m_hDbhHelp, "SymGetSymFromAddr64" );
371     pUDSN = (tUDSN) GetProcAddress(m_hDbhHelp, "UnDecorateSymbolName" );
372     pSLM = (tSLM) GetProcAddress(m_hDbhHelp, "SymLoadModule64" );
373     pSGSP =(tSGSP) GetProcAddress(m_hDbhHelp, "SymGetSearchPath" );
374 
375     if ( pSC == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL ||
376       pSGO == NULL || pSGSFA == NULL || pSI == NULL || pSSO == NULL ||
377       pSW == NULL || pUDSN == NULL || pSLM == NULL )
378     {
379       FreeLibrary(m_hDbhHelp);
380       m_hDbhHelp = NULL;
381       pSC = NULL;
382       return FALSE;
383     }
384 
385     // SymInitialize
386     if (szSymPath != NULL)
387       m_szSymPath = _strdup(szSymPath);
388     if (this->pSI(m_hProcess, m_szSymPath, FALSE) == FALSE)
389       this->m_parent->OnDbgHelpErr("SymInitialize", GetLastError(), 0);
390 
391     DWORD symOptions = this->pSGO();  // SymGetOptions
392     symOptions |= SYMOPT_LOAD_LINES;
393     symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS;
394     //symOptions |= SYMOPT_NO_PROMPTS;
395     // SymSetOptions
396     symOptions = this->pSSO(symOptions);
397 
398     char buf[StackWalker::STACKWALK_MAX_NAMELEN] = {0};
399     if (this->pSGSP != NULL)
400     {
401       if (this->pSGSP(m_hProcess, buf, StackWalker::STACKWALK_MAX_NAMELEN) == FALSE)
402         this->m_parent->OnDbgHelpErr("SymGetSearchPath", GetLastError(), 0);
403     }
404     char szUserName[1024] = {0};
405     DWORD dwSize = 1024;
406     GetUserNameA(szUserName, &dwSize);
407     this->m_parent->OnSymInit(buf, symOptions, szUserName);
408 
409     return TRUE;
410   }
411 
412   StackWalker *m_parent;
413 
414   HMODULE m_hDbhHelp;
415   HANDLE m_hProcess;
416   LPSTR m_szSymPath;
417 
418 #pragma pack(push,8)
419 typedef struct IMAGEHLP_MODULE64_V3 {
420     DWORD    SizeOfStruct;           // set to sizeof(IMAGEHLP_MODULE64)
421     DWORD64  BaseOfImage;            // base load address of module
422     DWORD    ImageSize;              // virtual size of the loaded module
423     DWORD    TimeDateStamp;          // date/time stamp from pe header
424     DWORD    CheckSum;               // checksum from the pe header
425     DWORD    NumSyms;                // number of symbols in the symbol table
426     SYM_TYPE SymType;                // type of symbols loaded
427     CHAR     ModuleName[32];         // module name
428     CHAR     ImageName[256];         // image name
429     CHAR     LoadedImageName[256];   // symbol file name
430     // new elements: 07-Jun-2002
431     CHAR     LoadedPdbName[256];     // pdb file name
432     DWORD    CVSig;                  // Signature of the CV record in the debug directories
433     CHAR     CVData[MAX_PATH * 3];   // Contents of the CV record
434     DWORD    PdbSig;                 // Signature of PDB
435     GUID     PdbSig70;               // Signature of PDB (VC 7 and up)
436     DWORD    PdbAge;                 // DBI age of pdb
437     BOOL     PdbUnmatched;           // loaded an unmatched pdb
438     BOOL     DbgUnmatched;           // loaded an unmatched dbg
439     BOOL     LineNumbers;            // we have line number information
440     BOOL     GlobalSymbols;          // we have internal symbol information
441     BOOL     TypeInfo;               // we have type information
442     // new elements: 17-Dec-2003
443     BOOL     SourceIndexed;          // pdb supports source server
444     BOOL     Publics;                // contains public symbols
445 };
446 
447 typedef struct IMAGEHLP_MODULE64_V2 {
448     DWORD    SizeOfStruct;           // set to sizeof(IMAGEHLP_MODULE64)
449     DWORD64  BaseOfImage;            // base load address of module
450     DWORD    ImageSize;              // virtual size of the loaded module
451     DWORD    TimeDateStamp;          // date/time stamp from pe header
452     DWORD    CheckSum;               // checksum from the pe header
453     DWORD    NumSyms;                // number of symbols in the symbol table
454     SYM_TYPE SymType;                // type of symbols loaded
455     CHAR     ModuleName[32];         // module name
456     CHAR     ImageName[256];         // image name
457     CHAR     LoadedImageName[256];   // symbol file name
458 };
459 #pragma pack(pop)
460 
461 
462   // SymCleanup()
463   typedef BOOL (__stdcall *tSC)( IN HANDLE hProcess );
464   tSC pSC;
465 
466   // SymFunctionTableAccess64()
467   typedef PVOID (__stdcall *tSFTA)( HANDLE hProcess, DWORD64 AddrBase );
468   tSFTA pSFTA;
469 
470   // SymGetLineFromAddr64()
471   typedef BOOL (__stdcall *tSGLFA)( IN HANDLE hProcess, IN DWORD64 dwAddr,
472     OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line );
473   tSGLFA pSGLFA;
474 
475   // SymGetModuleBase64()
476   typedef DWORD64 (__stdcall *tSGMB)( IN HANDLE hProcess, IN DWORD64 dwAddr );
477   tSGMB pSGMB;
478 
479   // SymGetModuleInfo64()
480   typedef BOOL (__stdcall *tSGMI)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT IMAGEHLP_MODULE64_V3 *ModuleInfo );
481   tSGMI pSGMI;
482 
483   // SymGetOptions()
484   typedef DWORD (__stdcall *tSGO)( VOID );
485   tSGO pSGO;
486 
487   // SymGetSymFromAddr64()
488   typedef BOOL (__stdcall *tSGSFA)( IN HANDLE hProcess, IN DWORD64 dwAddr,
489     OUT PDWORD64 pdwDisplacement, OUT PIMAGEHLP_SYMBOL64 Symbol );
490   tSGSFA pSGSFA;
491 
492   // SymInitialize()
493   typedef BOOL (__stdcall *tSI)( IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess );
494   tSI pSI;
495 
496   // SymLoadModule64()
497   typedef DWORD64 (__stdcall *tSLM)( IN HANDLE hProcess, IN HANDLE hFile,
498     IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll );
499   tSLM pSLM;
500 
501   // SymSetOptions()
502   typedef DWORD (__stdcall *tSSO)( IN DWORD SymOptions );
503   tSSO pSSO;
504 
505   // StackWalk64()
506   typedef BOOL (__stdcall *tSW)(
507     DWORD MachineType,
508     HANDLE hProcess,
509     HANDLE hThread,
510     LPSTACKFRAME64 StackFrame,
511     PVOID ContextRecord,
512     PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
513     PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
514     PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
515     PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress );
516   tSW pSW;
517 
518   // UnDecorateSymbolName()
519   typedef DWORD (__stdcall WINAPI *tUDSN)( PCSTR DecoratedName, PSTR UnDecoratedName,
520     DWORD UndecoratedLength, DWORD Flags );
521   tUDSN pUDSN;
522 
523   typedef BOOL (__stdcall WINAPI *tSGSP)(HANDLE hProcess, PSTR SearchPath, DWORD SearchPathLength);
524   tSGSP pSGSP;
525 
526 
527 private:
528   // **************************************** ToolHelp32 ************************
529   #define MAX_MODULE_NAME32 255
530   #define TH32CS_SNAPMODULE   0x00000008
531   #pragma pack( push, 8 )
532   typedef struct tagMODULEENTRY32
533   {
534       DWORD   dwSize;
535       DWORD   th32ModuleID;       // This module
536       DWORD   th32ProcessID;      // owning process
537       DWORD   GlblcntUsage;       // Global usage count on the module
538       DWORD   ProccntUsage;       // Module usage count in th32ProcessID's context
539       BYTE  * modBaseAddr;        // Base address of module in th32ProcessID's context
540       DWORD   modBaseSize;        // Size in bytes of module starting at modBaseAddr
541       HMODULE hModule;            // The hModule of this module in th32ProcessID's context
542       char    szModule[MAX_MODULE_NAME32 + 1];
543       char    szExePath[MAX_PATH];
544   } MODULEENTRY32;
545   typedef MODULEENTRY32 *  PMODULEENTRY32;
546   typedef MODULEENTRY32 *  LPMODULEENTRY32;
547   #pragma pack( pop )
548 
GetModuleListTH32(HANDLE hProcess,DWORD pid)549   BOOL GetModuleListTH32(HANDLE hProcess, DWORD pid)
550   {
551     // CreateToolhelp32Snapshot()
552     typedef HANDLE (__stdcall *tCT32S)(DWORD dwFlags, DWORD th32ProcessID);
553     // Module32First()
554     typedef BOOL (__stdcall *tM32F)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
555     // Module32Next()
556     typedef BOOL (__stdcall *tM32N)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
557 
558     // try both dlls...
559     const TCHAR *dllname[] = { _T("kernel32.dll"), _T("tlhelp32.dll") };
560     HINSTANCE hToolhelp = NULL;
561     tCT32S pCT32S = NULL;
562     tM32F pM32F = NULL;
563     tM32N pM32N = NULL;
564 
565     HANDLE hSnap;
566     MODULEENTRY32 me;
567     me.dwSize = sizeof(me);
568     BOOL keepGoing;
569     size_t i;
570 
571     for (i = 0; i<(sizeof(dllname) / sizeof(dllname[0])); i++ )
572     {
573       hToolhelp = LoadLibrary( dllname[i] );
574       if (hToolhelp == NULL)
575         continue;
576       pCT32S = (tCT32S) GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot");
577       pM32F = (tM32F) GetProcAddress(hToolhelp, "Module32First");
578       pM32N = (tM32N) GetProcAddress(hToolhelp, "Module32Next");
579       if ( (pCT32S != NULL) && (pM32F != NULL) && (pM32N != NULL) )
580         break; // found the functions!
581       FreeLibrary(hToolhelp);
582       hToolhelp = NULL;
583     }
584 
585     if (hToolhelp == NULL)
586       return FALSE;
587 
588     hSnap = pCT32S( TH32CS_SNAPMODULE, pid );
589     if (hSnap == (HANDLE) -1)
590     {
591       FreeLibrary(hToolhelp);
592       return FALSE;
593     }
594 
595     keepGoing = !!pM32F( hSnap, &me );
596     int cnt = 0;
597     while (keepGoing)
598     {
599       this->LoadModule(hProcess, me.szExePath, me.szModule, (DWORD64) me.modBaseAddr, me.modBaseSize);
600       cnt++;
601       keepGoing = !!pM32N( hSnap, &me );
602     }
603     CloseHandle(hSnap);
604     FreeLibrary(hToolhelp);
605     if (cnt <= 0)
606       return FALSE;
607     return TRUE;
608   }  // GetModuleListTH32
609 
610   // **************************************** PSAPI ************************
611   typedef struct _MODULEINFO {
612       LPVOID lpBaseOfDll;
613       DWORD SizeOfImage;
614       LPVOID EntryPoint;
615   } MODULEINFO, *LPMODULEINFO;
616 
GetModuleListPSAPI(HANDLE hProcess)617   BOOL GetModuleListPSAPI(HANDLE hProcess)
618   {
619     // EnumProcessModules()
620     typedef BOOL (__stdcall *tEPM)(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded );
621     // GetModuleFileNameEx()
622     typedef DWORD (__stdcall *tGMFNE)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );
623     // GetModuleBaseName()
624     typedef DWORD (__stdcall *tGMBN)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );
625     // GetModuleInformation()
626     typedef BOOL (__stdcall *tGMI)(HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize );
627 
628     HINSTANCE hPsapi;
629     tEPM pEPM;
630     tGMFNE pGMFNE;
631     tGMBN pGMBN;
632     tGMI pGMI;
633 
634     DWORD i;
635     //ModuleEntry e;
636     DWORD cbNeeded;
637     MODULEINFO mi;
638     HMODULE *hMods = 0;
639     char *tt = NULL;
640     char *tt2 = NULL;
641     const SIZE_T TTBUFLEN = 8096;
642     int cnt = 0;
643 
644     hPsapi = LoadLibrary( _T("psapi.dll") );
645     if (hPsapi == NULL)
646       return FALSE;
647 
648     pEPM = (tEPM) GetProcAddress( hPsapi, "EnumProcessModules" );
649     pGMFNE = (tGMFNE) GetProcAddress( hPsapi, "GetModuleFileNameExA" );
650     pGMBN = (tGMFNE) GetProcAddress( hPsapi, "GetModuleBaseNameA" );
651     pGMI = (tGMI) GetProcAddress( hPsapi, "GetModuleInformation" );
652     if ( (pEPM == NULL) || (pGMFNE == NULL) || (pGMBN == NULL) || (pGMI == NULL) )
653     {
654       // we couldn�t find all functions
655       FreeLibrary(hPsapi);
656       return FALSE;
657     }
658 
659     hMods = (HMODULE*) malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof(HMODULE)));
660     tt = (char*) malloc(sizeof(char) * TTBUFLEN);
661     tt2 = (char*) malloc(sizeof(char) * TTBUFLEN);
662     if ( (hMods == NULL) || (tt == NULL) || (tt2 == NULL) )
663       goto cleanup;
664 
665     if ( ! pEPM( hProcess, hMods, TTBUFLEN, &cbNeeded ) )
666     {
667       //_ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle );
668       goto cleanup;
669     }
670 
671     if ( cbNeeded > TTBUFLEN )
672     {
673       //_ftprintf(fLogFile, _T("%lu: More than %lu module handles. Huh?\n"), g_dwShowCount, lenof( hMods ) );
674       goto cleanup;
675     }
676 
677     for ( i = 0; i < cbNeeded / sizeof(hMods[0]); i++ )
678     {
679       // base address, size
680       pGMI(hProcess, hMods[i], &mi, sizeof(mi));
681       // image file name
682       tt[0] = 0;
683       pGMFNE(hProcess, hMods[i], tt, TTBUFLEN );
684       // module name
685       tt2[0] = 0;
686       pGMBN(hProcess, hMods[i], tt2, TTBUFLEN );
687 
688       DWORD dwRes = this->LoadModule(hProcess, tt, tt2, (DWORD64) mi.lpBaseOfDll, mi.SizeOfImage);
689       if (dwRes != ERROR_SUCCESS)
690         this->m_parent->OnDbgHelpErr("LoadModule", dwRes, 0);
691       cnt++;
692     }
693 
694   cleanup:
695     if (hPsapi != NULL) FreeLibrary(hPsapi);
696     if (tt2 != NULL) free(tt2);
697     if (tt != NULL) free(tt);
698     if (hMods != NULL) free(hMods);
699 
700     return cnt != 0;
701   }  // GetModuleListPSAPI
702 
LoadModule(HANDLE hProcess,LPCSTR img,LPCSTR mod,DWORD64 baseAddr,DWORD size)703   DWORD LoadModule(HANDLE hProcess, LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size)
704   {
705     CHAR *szImg = _strdup(img);
706     CHAR *szMod = _strdup(mod);
707     DWORD result = ERROR_SUCCESS;
708     if ( (szImg == NULL) || (szMod == NULL) )
709       result = ERROR_NOT_ENOUGH_MEMORY;
710     else
711     {
712       if (pSLM(hProcess, 0, szImg, szMod, baseAddr, size) == 0)
713         result = GetLastError();
714     }
715     ULONGLONG fileVersion = 0;
716     if ( (m_parent != NULL) && (szImg != NULL) )
717     {
718       // try to retrive the file-version:
719       if ( (this->m_parent->m_options & StackWalker::RetrieveFileVersion) != 0)
720       {
721         VS_FIXEDFILEINFO *fInfo = NULL;
722         DWORD dwHandle;
723         DWORD dwSize = GetFileVersionInfoSizeA(szImg, &dwHandle);
724         if (dwSize > 0)
725         {
726           LPVOID vData = malloc(dwSize);
727           if (vData != NULL)
728           {
729             if (GetFileVersionInfoA(szImg, dwHandle, dwSize, vData) != 0)
730             {
731               UINT len;
732               TCHAR szSubBlock[] = _T("\\");
733               if (VerQueryValue(vData, szSubBlock, (LPVOID*) &fInfo, &len) == 0)
734                 fInfo = NULL;
735               else
736               {
737                 fileVersion = ((ULONGLONG)fInfo->dwFileVersionLS) + ((ULONGLONG)fInfo->dwFileVersionMS << 32);
738               }
739             }
740             free(vData);
741           }
742         }
743       }
744 
745       // Retrive some additional-infos about the module
746       IMAGEHLP_MODULE64_V3 Module;
747       const char *szSymType = "-unknown-";
748       if (this->GetModuleInfo(hProcess, baseAddr, &Module) != FALSE)
749       {
750         switch(Module.SymType)
751         {
752           case SymNone:
753             szSymType = "-nosymbols-";
754             break;
755           case SymCoff:  // 1
756             szSymType = "COFF";
757             break;
758           case SymCv:  // 2
759             szSymType = "CV";
760             break;
761           case SymPdb:  // 3
762             szSymType = "PDB";
763             break;
764           case SymExport:  // 4
765             szSymType = "-exported-";
766             break;
767           case SymDeferred:  // 5
768             szSymType = "-deferred-";
769             break;
770           case SymSym:  // 6
771             szSymType = "SYM";
772             break;
773           case 7: // SymDia:
774             szSymType = "DIA";
775             break;
776           case 8: //SymVirtual:
777             szSymType = "Virtual";
778             break;
779         }
780       }
781       LPCSTR pdbName = Module.LoadedImageName;
782       if (Module.LoadedPdbName[0] != 0)
783         pdbName = Module.LoadedPdbName;
784       this->m_parent->OnLoadModule(img, mod, baseAddr, size, result, szSymType, pdbName, fileVersion);
785     }
786     if (szImg != NULL) free(szImg);
787     if (szMod != NULL) free(szMod);
788     return result;
789   }
790 public:
LoadModules(HANDLE hProcess,DWORD dwProcessId)791   BOOL LoadModules(HANDLE hProcess, DWORD dwProcessId)
792   {
793     // first try toolhelp32
794     if (GetModuleListTH32(hProcess, dwProcessId))
795       return true;
796     // then try psapi
797     return GetModuleListPSAPI(hProcess);
798   }
799 
800 
GetModuleInfo(HANDLE hProcess,DWORD64 baseAddr,IMAGEHLP_MODULE64_V3 * pModuleInfo)801   BOOL GetModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULE64_V3 *pModuleInfo)
802   {
803     memset(pModuleInfo, 0, sizeof(IMAGEHLP_MODULE64_V3));
804     if(this->pSGMI == NULL)
805     {
806       SetLastError(ERROR_DLL_INIT_FAILED);
807       return FALSE;
808     }
809     // First try to use the larger ModuleInfo-Structure
810     pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V3);
811     void *pData = malloc(4096); // reserve enough memory, so the bug in v6.3.5.1 does not lead to memory-overwrites...
812     if (pData == NULL)
813     {
814       SetLastError(ERROR_NOT_ENOUGH_MEMORY);
815       return FALSE;
816     }
817     memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64_V3));
818     static bool s_useV3Version = true;
819     if (s_useV3Version)
820     {
821       if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V3*) pData) != FALSE)
822       {
823         // only copy as much memory as is reserved...
824         memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V3));
825         pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V3);
826         free(pData);
827         return TRUE;
828       }
829       s_useV3Version = false;  // to prevent unneccessarry calls with the larger struct...
830     }
831 
832     // could not retrive the bigger structure, try with the smaller one (as defined in VC7.1)...
833     pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2);
834     memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64_V2));
835     if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V3*) pData) != FALSE)
836     {
837       // only copy as much memory as is reserved...
838       memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V2));
839       pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2);
840       free(pData);
841       return TRUE;
842     }
843     free(pData);
844     SetLastError(ERROR_DLL_INIT_FAILED);
845     return FALSE;
846   }
847 };
848 
849 // #############################################################
StackWalker(DWORD dwProcessId,HANDLE hProcess)850 StackWalker::StackWalker(DWORD dwProcessId, HANDLE hProcess)
851 {
852   this->m_options = OptionsAll;
853   this->m_modulesLoaded = FALSE;
854   this->m_hProcess = hProcess;
855   this->m_sw = new StackWalkerInternal(this, this->m_hProcess);
856   this->m_dwProcessId = dwProcessId;
857   this->m_szSymPath = NULL;
858   this->m_MaxRecursionCount = 1000;
859 }
StackWalker(int options,LPCSTR szSymPath,DWORD dwProcessId,HANDLE hProcess)860 StackWalker::StackWalker(int options, LPCSTR szSymPath, DWORD dwProcessId, HANDLE hProcess)
861 {
862   this->m_options = options;
863   this->m_modulesLoaded = FALSE;
864   this->m_hProcess = hProcess;
865   this->m_sw = new StackWalkerInternal(this, this->m_hProcess);
866   this->m_dwProcessId = dwProcessId;
867   if (szSymPath != NULL)
868   {
869     this->m_szSymPath = _strdup(szSymPath);
870     this->m_options |= SymBuildPath;
871   }
872   else
873     this->m_szSymPath = NULL;
874   this->m_MaxRecursionCount = 1000;
875 }
876 
~StackWalker()877 StackWalker::~StackWalker()
878 {
879   if (m_szSymPath != NULL)
880     free(m_szSymPath);
881   m_szSymPath = NULL;
882   if (this->m_sw != NULL)
883     delete this->m_sw;
884   this->m_sw = NULL;
885 }
886 
LoadModules()887 BOOL StackWalker::LoadModules()
888 {
889   if (this->m_sw == NULL)
890   {
891     SetLastError(ERROR_DLL_INIT_FAILED);
892     return FALSE;
893   }
894   if (m_modulesLoaded != FALSE)
895     return TRUE;
896 
897   // Build the sym-path:
898   char *szSymPath = NULL;
899   if ( (this->m_options & SymBuildPath) != 0)
900   {
901     const size_t nSymPathLen = 4096;
902     szSymPath = (char*) malloc(nSymPathLen);
903     if (szSymPath == NULL)
904     {
905       SetLastError(ERROR_NOT_ENOUGH_MEMORY);
906       return FALSE;
907     }
908     szSymPath[0] = 0;
909     // Now first add the (optional) provided sympath:
910     if (this->m_szSymPath != NULL)
911     {
912       strcat_s(szSymPath, nSymPathLen, this->m_szSymPath);
913       strcat_s(szSymPath, nSymPathLen, ";");
914     }
915 
916     strcat_s(szSymPath, nSymPathLen, ".;");
917 
918     const size_t nTempLen = 1024;
919     char szTemp[nTempLen];
920     // Now add the current directory:
921     if (GetCurrentDirectoryA(nTempLen, szTemp) > 0)
922     {
923       szTemp[nTempLen-1] = 0;
924       strcat_s(szSymPath, nSymPathLen, szTemp);
925       strcat_s(szSymPath, nSymPathLen, ";");
926     }
927 
928     // Now add the path for the main-module:
929     if (GetModuleFileNameA(NULL, szTemp, nTempLen) > 0)
930     {
931       szTemp[nTempLen-1] = 0;
932       for (char *p = (szTemp+strlen(szTemp)-1); p >= szTemp; --p)
933       {
934         // locate the rightmost path separator
935         if ( (*p == '\\') || (*p == '/') || (*p == ':') )
936         {
937           *p = 0;
938           break;
939         }
940       }  // for (search for path separator...)
941       if (strlen(szTemp) > 0)
942       {
943         strcat_s(szSymPath, nSymPathLen, szTemp);
944         strcat_s(szSymPath, nSymPathLen, ";");
945       }
946     }
947     if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", szTemp, nTempLen) > 0)
948     {
949       szTemp[nTempLen-1] = 0;
950       strcat_s(szSymPath, nSymPathLen, szTemp);
951       strcat_s(szSymPath, nSymPathLen, ";");
952     }
953     if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", szTemp, nTempLen) > 0)
954     {
955       szTemp[nTempLen-1] = 0;
956       strcat_s(szSymPath, nSymPathLen, szTemp);
957       strcat_s(szSymPath, nSymPathLen, ";");
958     }
959     if (GetEnvironmentVariableA("SYSTEMROOT", szTemp, nTempLen) > 0)
960     {
961       szTemp[nTempLen-1] = 0;
962       strcat_s(szSymPath, nSymPathLen, szTemp);
963       strcat_s(szSymPath, nSymPathLen, ";");
964       // also add the "system32"-directory:
965       strcat_s(szTemp, nTempLen, "\\system32");
966       strcat_s(szSymPath, nSymPathLen, szTemp);
967       strcat_s(szSymPath, nSymPathLen, ";");
968     }
969 
970     if ( (this->m_options & SymUseSymSrv) != 0)
971     {
972       if (GetEnvironmentVariableA("SYSTEMDRIVE", szTemp, nTempLen) > 0)
973       {
974         szTemp[nTempLen-1] = 0;
975         strcat_s(szSymPath, nSymPathLen, "SRV*");
976         strcat_s(szSymPath, nSymPathLen, szTemp);
977         strcat_s(szSymPath, nSymPathLen, "\\websymbols");
978         strcat_s(szSymPath, nSymPathLen, "*http://msdl.microsoft.com/download/symbols;");
979       }
980       else
981         strcat_s(szSymPath, nSymPathLen, "SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols;");
982     }
983   }  // if SymBuildPath
984 
985   // First Init the whole stuff...
986   BOOL bRet = this->m_sw->Init(szSymPath);
987   if (szSymPath != NULL) free(szSymPath); szSymPath = NULL;
988   if (bRet == FALSE)
989   {
990     this->OnDbgHelpErr("Error while initializing dbghelp.dll", 0, 0);
991     SetLastError(ERROR_DLL_INIT_FAILED);
992     return FALSE;
993   }
994 
995   bRet = this->m_sw->LoadModules(this->m_hProcess, this->m_dwProcessId);
996   if (bRet != FALSE)
997     m_modulesLoaded = TRUE;
998   return bRet;
999 }
1000 
1001 
1002 // The following is used to pass the "userData"-Pointer to the user-provided readMemoryFunction
1003 // This has to be done due to a problem with the "hProcess"-parameter in x64...
1004 // Because this class is in no case multi-threading-enabled (because of the limitations
1005 // of dbghelp.dll) it is "safe" to use a static-variable
1006 static StackWalker::PReadProcessMemoryRoutine s_readMemoryFunction = NULL;
1007 static LPVOID s_readMemoryFunction_UserData = NULL;
1008 
ShowCallstack(HANDLE hThread,const CONTEXT * context,PReadProcessMemoryRoutine readMemoryFunction,LPVOID pUserData)1009 BOOL StackWalker::ShowCallstack(HANDLE hThread, const CONTEXT *context, PReadProcessMemoryRoutine readMemoryFunction, LPVOID pUserData)
1010 {
1011   CONTEXT c;
1012   CallstackEntry csEntry;
1013   IMAGEHLP_SYMBOL64 *pSym = NULL;
1014   StackWalkerInternal::IMAGEHLP_MODULE64_V3 Module;
1015   IMAGEHLP_LINE64 Line;
1016   int frameNum;
1017   bool bLastEntryCalled = true;
1018   int curRecursionCount = 0;
1019 
1020   if (m_modulesLoaded == FALSE)
1021     this->LoadModules();  // ignore the result...
1022 
1023   if (this->m_sw->m_hDbhHelp == NULL)
1024   {
1025     SetLastError(ERROR_DLL_INIT_FAILED);
1026     return FALSE;
1027   }
1028 
1029   s_readMemoryFunction = readMemoryFunction;
1030   s_readMemoryFunction_UserData = pUserData;
1031 
1032   if (context == NULL)
1033   {
1034     // If no context is provided, capture the context
1035     // See: https://stackwalker.codeplex.com/discussions/446958
1036 #if true || _WIN32_WINNT <= 0x0501
1037       // If we need to support XP, we need to use the "old way", because "GetThreadId" is not available!
1038     if (hThread == GetCurrentThread())
1039 #else
1040     if (GetThreadId(hThread) == GetCurrentThreadId())
1041 #endif
1042     {
1043       GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, USED_CONTEXT_FLAGS);
1044     }
1045     else
1046     {
1047       SuspendThread(hThread);
1048       memset(&c, 0, sizeof(CONTEXT));
1049       c.ContextFlags = USED_CONTEXT_FLAGS;
1050       if (GetThreadContext(hThread, &c) == FALSE)
1051       {
1052         ResumeThread(hThread);
1053         return FALSE;
1054       }
1055     }
1056   }
1057   else
1058     c = *context;
1059 
1060   // init STACKFRAME for first call
1061   STACKFRAME64 s; // in/out stackframe
1062   memset(&s, 0, sizeof(s));
1063   DWORD imageType;
1064 #ifdef _M_IX86
1065   // normally, call ImageNtHeader() and use machine info from PE header
1066   imageType = IMAGE_FILE_MACHINE_I386;
1067   s.AddrPC.Offset = c.Eip;
1068   s.AddrPC.Mode = AddrModeFlat;
1069   s.AddrFrame.Offset = c.Ebp;
1070   s.AddrFrame.Mode = AddrModeFlat;
1071   s.AddrStack.Offset = c.Esp;
1072   s.AddrStack.Mode = AddrModeFlat;
1073 #elif _M_X64
1074   imageType = IMAGE_FILE_MACHINE_AMD64;
1075   s.AddrPC.Offset = c.Rip;
1076   s.AddrPC.Mode = AddrModeFlat;
1077   s.AddrFrame.Offset = c.Rsp;
1078   s.AddrFrame.Mode = AddrModeFlat;
1079   s.AddrStack.Offset = c.Rsp;
1080   s.AddrStack.Mode = AddrModeFlat;
1081 #elif _M_IA64
1082   imageType = IMAGE_FILE_MACHINE_IA64;
1083   s.AddrPC.Offset = c.StIIP;
1084   s.AddrPC.Mode = AddrModeFlat;
1085   s.AddrFrame.Offset = c.IntSp;
1086   s.AddrFrame.Mode = AddrModeFlat;
1087   s.AddrBStore.Offset = c.RsBSP;
1088   s.AddrBStore.Mode = AddrModeFlat;
1089   s.AddrStack.Offset = c.IntSp;
1090   s.AddrStack.Mode = AddrModeFlat;
1091 #else
1092 #error "Platform not supported!"
1093 #endif
1094 
1095   pSym = (IMAGEHLP_SYMBOL64 *) malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
1096   if (!pSym) goto cleanup;  // not enough memory...
1097   memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
1098   pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
1099   pSym->MaxNameLength = STACKWALK_MAX_NAMELEN;
1100 
1101   memset(&Line, 0, sizeof(Line));
1102   Line.SizeOfStruct = sizeof(Line);
1103 
1104   memset(&Module, 0, sizeof(Module));
1105   Module.SizeOfStruct = sizeof(Module);
1106 
1107   for (frameNum = 0; ; ++frameNum )
1108   {
1109     // get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64())
1110     // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can
1111     // assume that either you are done, or that the stack is so hosed that the next
1112     // deeper frame could not be found.
1113     // CONTEXT need not to be suplied if imageTyp is IMAGE_FILE_MACHINE_I386!
1114     if ( ! this->m_sw->pSW(imageType, this->m_hProcess, hThread, &s, &c, myReadProcMem, this->m_sw->pSFTA, this->m_sw->pSGMB, NULL) )
1115     {
1116       // INFO: "StackWalk64" does not set "GetLastError"...
1117       this->OnDbgHelpErr("StackWalk64", 0, s.AddrPC.Offset);
1118       break;
1119     }
1120 
1121     csEntry.offset = s.AddrPC.Offset;
1122     csEntry.name[0] = 0;
1123     csEntry.undName[0] = 0;
1124     csEntry.undFullName[0] = 0;
1125     csEntry.offsetFromSmybol = 0;
1126     csEntry.offsetFromLine = 0;
1127     csEntry.lineFileName[0] = 0;
1128     csEntry.lineNumber = 0;
1129     csEntry.loadedImageName[0] = 0;
1130     csEntry.moduleName[0] = 0;
1131     if (s.AddrPC.Offset == s.AddrReturn.Offset)
1132     {
1133       if ( (this->m_MaxRecursionCount > 0) && (curRecursionCount > m_MaxRecursionCount) )
1134       {
1135         this->OnDbgHelpErr("StackWalk64-Endless-Callstack!", 0, s.AddrPC.Offset);
1136         break;
1137       }
1138       curRecursionCount++;
1139     }
1140     else
1141       curRecursionCount = 0;
1142     if (s.AddrPC.Offset != 0)
1143     {
1144       // we seem to have a valid PC
1145       // show procedure info (SymGetSymFromAddr64())
1146       if (this->m_sw->pSGSFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromSmybol), pSym) != FALSE)
1147       {
1148         MyStrCpy(csEntry.name, STACKWALK_MAX_NAMELEN, pSym->Name);
1149         // UnDecorateSymbolName()
1150         this->m_sw->pUDSN( pSym->Name, csEntry.undName, STACKWALK_MAX_NAMELEN, UNDNAME_NAME_ONLY );
1151         this->m_sw->pUDSN( pSym->Name, csEntry.undFullName, STACKWALK_MAX_NAMELEN, UNDNAME_COMPLETE );
1152       }
1153       else
1154       {
1155         this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), s.AddrPC.Offset);
1156       }
1157 
1158       // show line number info, NT5.0-method (SymGetLineFromAddr64())
1159       if (this->m_sw->pSGLFA != NULL )
1160       { // yes, we have SymGetLineFromAddr64()
1161         if (this->m_sw->pSGLFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromLine), &Line) != FALSE)
1162         {
1163           csEntry.lineNumber = Line.LineNumber;
1164           MyStrCpy(csEntry.lineFileName, STACKWALK_MAX_NAMELEN, Line.FileName);
1165         }
1166         else
1167         {
1168           this->OnDbgHelpErr("SymGetLineFromAddr64", GetLastError(), s.AddrPC.Offset);
1169         }
1170       } // yes, we have SymGetLineFromAddr64()
1171 
1172       // show module info (SymGetModuleInfo64())
1173       if (this->m_sw->GetModuleInfo(this->m_hProcess, s.AddrPC.Offset, &Module ) != FALSE)
1174       { // got module info OK
1175         switch ( Module.SymType )
1176         {
1177         case SymNone:
1178           csEntry.symTypeString = "-nosymbols-";
1179           break;
1180         case SymCoff:
1181           csEntry.symTypeString = "COFF";
1182           break;
1183         case SymCv:
1184           csEntry.symTypeString = "CV";
1185           break;
1186         case SymPdb:
1187           csEntry.symTypeString = "PDB";
1188           break;
1189         case SymExport:
1190           csEntry.symTypeString = "-exported-";
1191           break;
1192         case SymDeferred:
1193           csEntry.symTypeString = "-deferred-";
1194           break;
1195         case SymSym:
1196           csEntry.symTypeString = "SYM";
1197           break;
1198 #if API_VERSION_NUMBER >= 9
1199         case SymDia:
1200           csEntry.symTypeString = "DIA";
1201           break;
1202 #endif
1203         case 8: //SymVirtual:
1204           csEntry.symTypeString = "Virtual";
1205           break;
1206         default:
1207           //_snprintf( ty, sizeof(ty), "symtype=%ld", (long) Module.SymType );
1208           csEntry.symTypeString = NULL;
1209           break;
1210         }
1211 
1212         MyStrCpy(csEntry.moduleName, STACKWALK_MAX_NAMELEN, Module.ModuleName);
1213         csEntry.baseOfImage = Module.BaseOfImage;
1214         MyStrCpy(csEntry.loadedImageName, STACKWALK_MAX_NAMELEN, Module.LoadedImageName);
1215       } // got module info OK
1216       else
1217       {
1218         this->OnDbgHelpErr("SymGetModuleInfo64", GetLastError(), s.AddrPC.Offset);
1219       }
1220     } // we seem to have a valid PC
1221 
1222     CallstackEntryType et = nextEntry;
1223     if (frameNum == 0)
1224       et = firstEntry;
1225     bLastEntryCalled = false;
1226     this->OnCallstackEntry(et, csEntry);
1227 
1228     if (s.AddrReturn.Offset == 0)
1229     {
1230       bLastEntryCalled = true;
1231       this->OnCallstackEntry(lastEntry, csEntry);
1232       SetLastError(ERROR_SUCCESS);
1233       break;
1234     }
1235   } // for ( frameNum )
1236 
1237   cleanup:
1238     if (pSym) free( pSym );
1239 
1240   if (bLastEntryCalled == false)
1241       this->OnCallstackEntry(lastEntry, csEntry);
1242 
1243   if (context == NULL)
1244     ResumeThread(hThread);
1245 
1246   return TRUE;
1247 }
1248 
myReadProcMem(HANDLE hProcess,DWORD64 qwBaseAddress,PVOID lpBuffer,DWORD nSize,LPDWORD lpNumberOfBytesRead)1249 BOOL __stdcall StackWalker::myReadProcMem(
1250     HANDLE      hProcess,
1251     DWORD64     qwBaseAddress,
1252     PVOID       lpBuffer,
1253     DWORD       nSize,
1254     LPDWORD     lpNumberOfBytesRead
1255     )
1256 {
1257   if (s_readMemoryFunction == NULL)
1258   {
1259     SIZE_T st;
1260     BOOL bRet = ReadProcessMemory(hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, &st);
1261     *lpNumberOfBytesRead = (DWORD) st;
1262     //printf("ReadMemory: hProcess: %p, baseAddr: %p, buffer: %p, size: %d, read: %d, result: %d\n", hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, (DWORD) st, (DWORD) bRet);
1263     return bRet;
1264   }
1265   else
1266   {
1267     return s_readMemoryFunction(hProcess, qwBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead, s_readMemoryFunction_UserData);
1268   }
1269 }
1270 
OnLoadModule(LPCSTR img,LPCSTR mod,DWORD64 baseAddr,DWORD size,DWORD result,LPCSTR symType,LPCSTR pdbName,ULONGLONG fileVersion)1271 void StackWalker::OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion)
1272 {
1273   CHAR buffer[STACKWALK_MAX_NAMELEN];
1274   if (fileVersion == 0)
1275     _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s'\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName);
1276   else
1277   {
1278     DWORD v4 = (DWORD) (fileVersion & 0xFFFF);
1279     DWORD v3 = (DWORD) ((fileVersion>>16) & 0xFFFF);
1280     DWORD v2 = (DWORD) ((fileVersion>>32) & 0xFFFF);
1281     DWORD v1 = (DWORD) ((fileVersion>>48) & 0xFFFF);
1282     _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s', fileVersion: %d.%d.%d.%d\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName, v1, v2, v3, v4);
1283   }
1284   OnOutput(buffer);
1285 }
1286 
OnCallstackEntry(CallstackEntryType eType,CallstackEntry & entry)1287 void StackWalker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry)
1288 {
1289   CHAR buffer[STACKWALK_MAX_NAMELEN];
1290   if ( (eType != lastEntry) && (entry.offset != 0) )
1291   {
1292     if (entry.name[0] == 0)
1293       MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, "(function-name not available)");
1294     if (entry.undName[0] != 0)
1295       MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, entry.undName);
1296     if (entry.undFullName[0] != 0)
1297       MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, entry.undFullName);
1298     if (entry.lineFileName[0] == 0)
1299     {
1300       MyStrCpy(entry.lineFileName, STACKWALK_MAX_NAMELEN, "(filename not available)");
1301       if (entry.moduleName[0] == 0)
1302         MyStrCpy(entry.moduleName, STACKWALK_MAX_NAMELEN, "(module-name not available)");
1303       _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%p (%s): %s: %s\n", (LPVOID) entry.offset, entry.moduleName, entry.lineFileName, entry.name);
1304     }
1305     else
1306       _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s (%d): %s\n", entry.lineFileName, entry.lineNumber, entry.name);
1307     buffer[STACKWALK_MAX_NAMELEN-1] = 0;
1308     OnOutput(buffer);
1309   }
1310 }
1311 
OnDbgHelpErr(LPCSTR szFuncName,DWORD gle,DWORD64 addr)1312 void StackWalker::OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr)
1313 {
1314   CHAR buffer[STACKWALK_MAX_NAMELEN];
1315   _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "ERROR: %s, GetLastError: %d (Address: %p)\n", szFuncName, gle, (LPVOID) addr);
1316   OnOutput(buffer);
1317 }
1318 
OnSymInit(LPCSTR szSearchPath,DWORD symOptions,LPCSTR szUserName)1319 void StackWalker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName)
1320 {
1321   CHAR buffer[STACKWALK_MAX_NAMELEN];
1322   _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n", szSearchPath, symOptions, szUserName);
1323   OnOutput(buffer);
1324   // Also display the OS-version
1325 #if _MSC_VER <= 1200
1326   OSVERSIONINFOA ver;
1327   ZeroMemory(&ver, sizeof(OSVERSIONINFOA));
1328   ver.dwOSVersionInfoSize = sizeof(ver);
1329   if (GetVersionExA(&ver) != FALSE)
1330   {
1331     _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "OS-Version: %d.%d.%d (%s)\n",
1332       ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber,
1333       ver.szCSDVersion);
1334     OnOutput(buffer);
1335   }
1336 #else
1337   OSVERSIONINFOEXA ver;
1338   ZeroMemory(&ver, sizeof(OSVERSIONINFOEXA));
1339   ver.dwOSVersionInfoSize = sizeof(ver);
1340   if (GetVersionExA( (OSVERSIONINFOA*) &ver) != FALSE)
1341   {
1342     _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "OS-Version: %d.%d.%d (%s) 0x%x-0x%x\n",
1343       ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber,
1344       ver.szCSDVersion, ver.wSuiteMask, ver.wProductType);
1345     OnOutput(buffer);
1346   }
1347 #endif
1348 }
1349 
OnOutput(LPCSTR buffer)1350 void StackWalker::OnOutput(LPCSTR buffer)
1351 {
1352   OutputDebugStringA(buffer);
1353 }
1354