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