├── .gitignore
├── LICENSE
├── README.md
├── example
└── example.cpp
└── stackwalkerc.h
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | *.exe
3 | *.obj
4 | *.pdb
5 | *.ilk
6 | *.obj
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 2-Clause License
2 |
3 | Copyright (c) 2021, Sepehr Taghdisian (septag@protonmail.com)
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # stackwalkerc - Windows single header stack walker in C (DbgHelp.DLL)
2 |
3 | ## Features
4 |
5 | - Can be used in C or C++ code
6 | - Super simple API
7 | - Single header library makes integration into projects easy and fast
8 | - Overridable memory allocations and assertion
9 | - Zero heap allocations in callstack function (sw_show_callstack)
10 | - No extra header inclusions except lightweight *stdbool.h* and *stdint.h* for delclarations
11 | - Reporting of loaded modules and symbol search paths of the executable
12 | - Callstack reporting of other threads and processes
13 | - Callstack reporting in system exceptions
14 | - Does not depend on DbgHelp.DLL to be included with the executable, this library dynamically loads the DbgHelp.dll from common system paths
15 | - Fast code-path for current thread callstack captures
16 | - Manual callstack resolving for lazy symbol resolves
17 |
18 | ## Usage
19 | This is a single header library, so all you need is include the file in your source and you are good to go:
20 |
21 | ```cpp
22 | // SW_IMPL includes implementation as well, you can skip this if you only want the declarations
23 | #define SW_IMPL
24 | #include "stackwalker.h"
25 | ```
26 |
27 | Optionally, you can override some base functionality like function visibility, assertion and malloc, before including *stackwalkerc.h*:
28 |
29 | ```cpp
30 | #define SW_API_DECL static // declars everything as static so only visible to current translation unit
31 | #define SW_ASSERT(e) MyAssert(e) // override assert
32 | #define SW_MALLOC(size) MyMalloc(size) // override default stdc malloc
33 | #define SW_FREE(ptr) MyFree(ptr) // override default stdc free
34 | #define SW_IMPL
35 | #include "stackwalker.h"
36 | ```
37 |
38 | The API usage is super simple. There is more functionality like listing symbol search paths and loaded modules, for more detailed example, check out [example.cpp](example/example.cpp) for details.
39 |
40 | ```cpp
41 | static void callstack_entry(const sw_callstack_entry* entry, void* userptr)
42 | {
43 | printf("\t%s(%d): %s\n", entry->line_filename, entry->line, entry->und_name);
44 | }
45 |
46 | static void error_msg(const char* filename, uint32_t gle, uint64_t addr, void* userptr)
47 | {
48 | printf("error: %s, ErrorCode: %u\n", filename, gle);
49 | }
50 |
51 | int main(int argc, char* argv[])
52 | {
53 | sw_callbacks callbacks = {
54 | .callstack_entry = callstack_entry,
55 | .error_msg = error_msg
56 | };
57 | sw_context* stackwalk = sw_create_context_capture(SW_OPTIONS_ALL, callbacks, NULL);
58 | if (!stackwalk) {
59 | puts("ERROR: stackwalk init");
60 | return -1;
61 | }
62 | sw_show_callstack(stackwalk, NULL); // Second parameter NULL means that stackwalker should resolve for current thread
63 | sw_destroy_context(g_stackwalk);
64 | return 0;
65 | }
66 | ```
67 |
68 | To build the example, just make sure you have the right compiler flags (build debug symbols) and also link with *Version.lib*. The main API will be loaded dyamically from *dbghelp.dll* in common system paths (see `sw__init_internal` to see the search paths for the DLL file):
69 |
70 | MSVC:
71 | ```
72 | cd example
73 | cl /Od /Zi Version.lib example.cpp
74 | ```
75 |
76 | Clang (win):
77 | ```
78 | cd example
79 | clang -g -O0 -lVersion example.cpp -o example.exe
80 | ```
81 |
82 |
83 | ## Acknowledgments
84 | Almost all of the Windows API usage for StackWalk are taken from [StackWalker](https://github.com/JochenKalmbach/StackWalker) project by *Jochen Kalmbach*. For detailed information on how to use the API, read the *Kalmbach's* article on [CodeProject](https://www.codeproject.com/Articles/11132/Walking-the-callstack-2).
85 | This is actually a much more simplified (and improved imo) and straight-to-the-point version of *StackWalker* library.
86 | This project only supports msvc2015+/clang(windows) compilers, if you prefer C++ API or want support for older Visual studio versions, check out Kalmbach's StackWalker library mentioned above.
87 |
88 | [License (BSD 2-clause)](https://github.com/septag/stackwalkerc/blob/master/LICENSE)
89 | --------------------------------------------------------------------------
90 |
91 |
92 |
93 |
94 |
95 | Copyright 2021 Sepehr Taghdisian. All rights reserved.
96 |
97 | https://github.com/septag/stackwalker.c
98 |
99 | Redistribution and use in source and binary forms, with or without
100 | modification, are permitted provided that the following conditions are met:
101 |
102 | 1. Redistributions of source code must retain the above copyright notice,
103 | this list of conditions and the following disclaimer.
104 |
105 | 2. Redistributions in binary form must reproduce the above copyright notice,
106 | this list of conditions and the following disclaimer in the documentation
107 | and/or other materials provided with the distribution.
108 |
109 | THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR
110 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
111 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
112 | EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
113 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
114 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
115 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
116 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
117 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
118 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
119 |
--------------------------------------------------------------------------------
/example/example.cpp:
--------------------------------------------------------------------------------
1 | #define SW_IMPL
2 | #include "../stackwalkerc.h"
3 |
4 | #include
5 |
6 | static void symbol_init(const char* search_path, uint32_t sym_opts, void* userptr)
7 | {
8 | printf("Search path: %s\n", search_path);
9 | }
10 |
11 | static void load_module(const char* img, const char* module, uint64_t base_addr, uint32_t size, void* userptr)
12 | {
13 | printf("Load module:\n\timage: %s\n\tmodule: %s\n", img, module);
14 | }
15 |
16 | static void callstack_entry(const sw_callstack_entry* entry, void* userptr)
17 | {
18 | printf("\t%s(%d): %s\n", entry->line_filename, entry->line, entry->und_name);
19 | }
20 |
21 | static void callstack_begin(void* userptr)
22 | {
23 | puts("Callstack:");
24 | }
25 |
26 | static void callstack_end(void* userptr)
27 | {
28 | puts("EndCallstack");
29 | }
30 |
31 | static sw_context* g_stackwalk;
32 |
33 | template
34 | class Test
35 | {
36 | public:
37 | void SomeFunc(_T c)
38 | {
39 | sw_show_callstack(g_stackwalk, NULL);
40 | }
41 | };
42 |
43 | static void foo2(void)
44 | {
45 | Test t;
46 | t.SomeFunc(1);
47 | }
48 |
49 | static void foo(void)
50 | {
51 | foo2();
52 | }
53 |
54 | int main(int argc, char* argv[])
55 | {
56 | sw_callbacks callbacks;
57 | callbacks.load_module = load_module;
58 | callbacks.callstack_begin = callstack_begin;
59 | callbacks.callstack_entry = callstack_entry;
60 | callbacks.callstack_end = callstack_end;
61 | callbacks.symbol_init = symbol_init;
62 | g_stackwalk = sw_create_context_capture(SW_OPTIONS_ALL, callbacks, NULL);
63 | if (!g_stackwalk) {
64 | puts("ERROR: stackwalk init");
65 | return -1;
66 | }
67 | foo(); // go through some functions and finally call show_callstack
68 | sw_destroy_context(g_stackwalk);
69 | return 1;
70 | }
--------------------------------------------------------------------------------
/stackwalkerc.h:
--------------------------------------------------------------------------------
1 |
2 | // Windows single header stack walker in C (DbgHelp)
3 | // Copyright (c) 2021, Sepehr Taghdisian.
4 | // Copyright (c) 2005 - 2019, Jochen Kalmbach. All rights reserved.
5 | // Usage:
6 | // #define SW_IMPL
7 | // #include "stackwalkerc.h"
8 | //
9 | // sw_context* ctx = sw_create_context_capture(SW_OPTIONS_ALL, callbacks, NULL);
10 | // sw_show_callstack(ctx);
11 | //
12 | // The usage is super simple, you can see the options/callbacks and check example.cpp
13 | //
14 | // History:
15 | // 1.0.0: Initial version
16 | // 1.0.1: Bugs fixed, taking more sw_option flags into action
17 | // 1.1.0: Added extra userptr to show_callstack_userptr to override the callbacks ptr per-callstack
18 | // 1.2.0: Added fast backtrace implementation for captures in current thread.
19 | // Added utility function sw_load_dbghelp
20 | // Added limiter function sw_set_callstack_limits
21 | // 1.3.0 Added more advanced functions for resolving callstack lazily, sw_capture_current, sw_resolve_callstack
22 | // 1.4.0 Added module cache and reload modules on-demand
23 | // 1.5.0 House cleaning, added sw_set_dbghelp_hintpath, ditched error_msg callback for SW_LOG_ERROR macro
24 | // 1.6.0 [BREAKING] added optional "hash" argument to `sw_capture_current`
25 | // 1.6.1 sw_resolve_callstack skips non-existing symbols with "NA" entries
26 | //
27 | #pragma once
28 |
29 | #include
30 | #include
31 |
32 | #ifndef SW_API_DECL
33 | #define SW_API_DECL
34 | #endif
35 |
36 | #ifndef SW_API_IMPL
37 | #define SW_API_IMPL SW_API_DECL
38 | #endif
39 |
40 | #ifndef SW_MAX_NAME_LEN
41 | #define SW_MAX_NAME_LEN 1024
42 | #endif
43 |
44 | #ifndef SW_MAX_FRAMES
45 | #define SW_MAX_FRAMES 64
46 | #endif
47 |
48 | typedef enum sw_options
49 | {
50 | SW_OPTIONS_NONE = 0,
51 | SW_OPTIONS_SYMBOL = 0x1, // Get symbol names
52 | SW_OPTIONS_SOURCEPOS = 0x2, // Get symbol file+line
53 | SW_OPTIONS_MODULEINFO = 0x4, // Get module information
54 | SW_OPTIONS_VERBOSE = 0xf, // All above options
55 | SW_OPTIONS_SYMBUILDPATH = 0x10, // Generate a good symbol search path
56 | SW_OPTIONS_SYMUSESERVER = 0x20, // Use public microsoft symbol server
57 | SW_OPTIONS_SYMALL = 0x30, // All symbol options
58 | SW_OPTIONS_ALL = 0x3f // All options
59 | } sw_options;
60 |
61 | typedef void* sw_sys_handle; // HANDLE
62 | typedef void* sw_exception_pointers; // PEXCEPTION_POINTERS
63 | typedef struct sw_context sw_context;
64 |
65 | typedef struct sw_callstack_entry
66 | {
67 | uint64_t offset;
68 | char name[SW_MAX_NAME_LEN];
69 | char und_name[SW_MAX_NAME_LEN];
70 | uint64_t offset_from_symbol;
71 | uint32_t offset_from_line;
72 | uint32_t line;
73 | char line_filename[SW_MAX_NAME_LEN];
74 | uint32_t symbol_type;
75 | const char* symbol_type_str;
76 | char module_name[SW_MAX_NAME_LEN];
77 | uint64_t baseof_image;
78 | char loaded_image_name[SW_MAX_NAME_LEN];
79 | } sw_callstack_entry;
80 |
81 | typedef struct sw_callbacks
82 | {
83 | void (*symbol_init)(const char* search_path, uint32_t sym_opts, void* userptr);
84 | void (*load_module)(const char* img, const char* module, uint64_t base_addr, uint32_t size, void* userptr);
85 | void (*callstack_begin)(void* userptr);
86 | void (*callstack_entry)(const sw_callstack_entry* entry, void* userptr);
87 | void (*callstack_end)(void* userptr);
88 | } sw_callbacks;
89 |
90 | #ifdef __cplusplus
91 | extern "C" {
92 | #endif
93 |
94 | SW_API_DECL sw_context* sw_create_context_capture(uint32_t options, sw_callbacks callbacks, void* userptr);
95 | SW_API_DECL sw_context* sw_create_context_capture_other(uint32_t options, uint32_t process_id,
96 | sw_sys_handle process, sw_callbacks callbacks, void* userptr);
97 | SW_API_DECL sw_context* sw_create_context_exception(uint32_t options,
98 | sw_exception_pointers exp_ptrs,
99 | sw_callbacks callbacks, void* userptr);
100 | SW_API_DECL sw_context* sw_create_context_catch(uint32_t options, sw_callbacks callbacks, void* userptr);
101 |
102 | SW_API_DECL void sw_destroy_context(sw_context* ctx);
103 |
104 | SW_API_DECL void sw_set_symbol_path(sw_context* ctx, const char* sympath);
105 | SW_API_DECL void sw_set_callstack_limits(sw_context* ctx, uint32_t frames_to_skip, uint32_t frames_to_capture);
106 | SW_API_DECL bool sw_show_callstack_userptr(sw_context* ctx, sw_sys_handle thread_hdl /*=NULL*/, void* callbacks_userptr);
107 | SW_API_DECL bool sw_show_callstack(sw_context* ctx, sw_sys_handle thread_hdl /*=NULL*/);
108 |
109 | // manual/advanced functions
110 | SW_API_DECL sw_sys_handle sw_load_dbghelp(void);
111 | SW_API_DECL uint16_t sw_capture_current(sw_context* ctx, void* symbols[SW_MAX_FRAMES], uint32_t* hash);
112 | SW_API_DECL uint16_t sw_resolve_callstack(sw_context* ctx, void* symbols[SW_MAX_FRAMES],
113 | sw_callstack_entry entries[SW_MAX_FRAMES], uint16_t num_entries);
114 | SW_API_DECL void sw_reload_modules(sw_context* ctx);
115 | SW_API_DECL bool sw_get_symbol_module(sw_context* ctx, void* symbol, char module_name[32]);
116 | SW_API_DECL void sw_set_dbghelp_hintpath(const char* path);
117 |
118 | #ifdef __cplusplus
119 | }
120 | #endif
121 |
122 | #ifdef SW_IMPL
123 |
124 | #ifndef _WIN32
125 | #error "Platforms other than Windows are not supported"
126 | #endif
127 |
128 | #define WIN32_LEAN_AND_MEAN
129 | #pragma warning(push)
130 | #pragma warning(disable : 5105)
131 | #include
132 | #pragma warning(pop)
133 | #include // alloca, malloc
134 | #include // strlen, strcat_s
135 |
136 | #ifndef SW_ASSERT
137 | # include
138 | # define SW_ASSERT(e) assert(e)
139 | #endif
140 |
141 | #ifndef SW_LOG_ERROR
142 | # include
143 | # define SW_LOG_ERROR(err_fmt, ...) printf(err_fmt "\n", ##__VA_ARGS__)
144 | #endif
145 |
146 | #ifndef SW_MALLOC
147 | # define SW_MALLOC(size) malloc(size)
148 | # define SW_FREE(ptr) free(ptr)
149 | #endif
150 |
151 | #define _SW_UNUSED(x) (void)(x)
152 |
153 | #ifndef _SW_PRIVATE
154 | # if defined(__GNUC__) || defined(__clang__)
155 | # define _SW_PRIVATE __attribute__((unused)) static
156 | # else
157 | # define _SW_PRIVATE static
158 | # endif
159 | #endif
160 |
161 | _SW_PRIVATE char* sw__strcpy(char* dst, size_t dst_sz, const char* src)
162 | {
163 | SW_ASSERT(dst);
164 | SW_ASSERT(src);
165 | SW_ASSERT(dst_sz > 0);
166 |
167 | const size_t len = strlen(src);
168 | const size_t _max = dst_sz - 1;
169 | const size_t num = (len < _max ? len : _max);
170 | memcpy(dst, src, num);
171 | dst[num] = '\0';
172 |
173 | return dst;
174 | }
175 |
176 | #pragma pack(push, 8)
177 | #include
178 |
179 | typedef struct _IMAGEHLP_MODULE64_V3
180 | {
181 | DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64)
182 | DWORD64 BaseOfImage; // base load address of module
183 | DWORD ImageSize; // virtual size of the loaded module
184 | DWORD TimeDateStamp; // date/time stamp from pe header
185 | DWORD CheckSum; // checksum from the pe header
186 | DWORD NumSyms; // number of symbols in the symbol table
187 | SYM_TYPE SymType; // type of symbols loaded
188 | CHAR ModuleName[32]; // module name
189 | CHAR ImageName[256]; // image name
190 | CHAR LoadedImageName[256]; // symbol file name
191 | // new elements: 07-Jun-2002
192 | CHAR LoadedPdbName[256]; // pdb file name
193 | DWORD CVSig; // Signature of the CV record in the debug directories
194 | CHAR CVData[MAX_PATH * 3]; // Contents of the CV record
195 | DWORD PdbSig; // Signature of PDB
196 | GUID PdbSig70; // Signature of PDB (VC 7 and up)
197 | DWORD PdbAge; // DBI age of pdb
198 | BOOL PdbUnmatched; // loaded an unmatched pdb
199 | BOOL DbgUnmatched; // loaded an unmatched dbg
200 | BOOL LineNumbers; // we have line number information
201 | BOOL GlobalSymbols; // we have internal symbol information
202 | BOOL TypeInfo; // we have type information
203 | // new elements: 17-Dec-2003
204 | BOOL SourceIndexed; // pdb supports source server
205 | BOOL Publics; // contains public symbols
206 | } IMAGEHLP_MODULE64_V3, *PIMAGEHLP_MODULE64_V3;
207 |
208 | typedef struct _IMAGEHLP_MODULE64_V2
209 | {
210 | DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64)
211 | DWORD64 BaseOfImage; // base load address of module
212 | DWORD ImageSize; // virtual size of the loaded module
213 | DWORD TimeDateStamp; // date/time stamp from pe header
214 | DWORD CheckSum; // checksum from the pe header
215 | DWORD NumSyms; // number of symbols in the symbol table
216 | SYM_TYPE SymType; // type of symbols loaded
217 | CHAR ModuleName[32]; // module name
218 | CHAR ImageName[256]; // image name
219 | CHAR LoadedImageName[256]; // symbol file name
220 | } IMAGEHLP_MODULE64_V2, *PIMAGEHLP_MODULE64_V2;
221 | #pragma pack(pop)
222 |
223 | typedef BOOL(__stdcall* SymCleanup_t)(IN HANDLE process);
224 | typedef PVOID(__stdcall* SymFunctionTableAccess64_t)(HANDLE process, DWORD64 AddrBase);
225 | typedef BOOL(__stdcall* SymGetLineFromAddr64_t)(IN HANDLE process,
226 | IN DWORD64 dwAddr,
227 | OUT PDWORD pdwDisplacement,
228 | OUT PIMAGEHLP_LINE64 line);
229 | typedef DWORD64(__stdcall* SymGetModuleBase64_t)(IN HANDLE process, IN DWORD64 dwAddr);
230 | typedef BOOL(__stdcall* SymGetModuleInfo64_t)(IN HANDLE process,
231 | IN DWORD64 dwAddr,
232 | OUT IMAGEHLP_MODULE64_V3* ModuleInfo);
233 | typedef DWORD(__stdcall* SymGetOptions_t)(VOID);
234 | typedef BOOL(__stdcall* SymGetSymFromAddr64_t)(IN HANDLE process,
235 | IN DWORD64 dwAddr,
236 | OUT PDWORD64 pdwDisplacement,
237 | OUT PIMAGEHLP_SYMBOL64 Symbol);
238 | typedef BOOL(__stdcall* SymInitialize_t)(IN HANDLE process, IN LPCSTR UserSearchPath, IN BOOL fInvadeProcess);
239 | typedef DWORD64(__stdcall* SymLoadModule64_t)(IN HANDLE process,
240 | IN HANDLE hFile,
241 | IN LPCSTR ImageName,
242 | IN LPCSTR ModuleName,
243 | IN DWORD64 BaseOfDll,
244 | IN DWORD SizeOfDll);
245 | typedef BOOL(__stdcall* SymUnloadModule64_t)(IN HANDLE hProcess, IN DWORD64 BaseOfDll);
246 | typedef DWORD(__stdcall* SymSetOptions_t)(IN DWORD SymOptions);
247 | typedef BOOL(__stdcall* StackWalk64_t)(DWORD MachineType,
248 | HANDLE process,
249 | HANDLE hThread,
250 | LPSTACKFRAME64 StackFrame,
251 | PVOID ContextRecord,
252 | PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
253 | PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
254 | PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
255 | PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress);
256 | typedef DWORD(__stdcall WINAPI* UnDecorateSymbolName_t)(PCSTR DecoratedName,
257 | PSTR UnDecoratedName,
258 | DWORD UndecoratedLength,
259 | DWORD Flags);
260 | typedef BOOL(__stdcall WINAPI* SymGetSearchPath_t)(HANDLE process, PSTR SearchPath, DWORD SearchPathLength);
261 | typedef BOOL(__stdcall WINAPI* EnumerateLoadedModules64_t)(HANDLE hProcess, PENUMLOADED_MODULES_CALLBACK64 EnumLoadedModulesCallback, PVOID UserContext);
262 |
263 | typedef BOOL(__stdcall* ReadProcessMemoryRoutine_t)(
264 | HANDLE process,
265 | DWORD64 qwBaseAddress,
266 | PVOID lpBuffer,
267 | DWORD nSize,
268 | LPDWORD lpNumberOfBytesRead,
269 | LPVOID pUserData); // optional data, which was passed in "show_callstack"
270 |
271 |
272 | // **************************************** ToolHelp32 ************************
273 | #define MAX_MODULE_NAME32 255
274 | #define TH32CS_SNAPMODULE 0x00000008
275 | #pragma pack(push, 8)
276 | typedef struct tagMODULEENTRY32
277 | {
278 | DWORD dwSize;
279 | DWORD th32ModuleID; // This module
280 | DWORD th32ProcessID; // owning process
281 | DWORD GlblcntUsage; // Global usage count on the module
282 | DWORD ProccntUsage; // module usage count in th32ProcessID's context
283 | BYTE *modBaseAddr; // Base address of module in th32ProcessID's context
284 | DWORD modBaseSize; // Size in bytes of module starting at modBaseAddr
285 | HMODULE hModule; // The hModule of this module in th32ProcessID's context
286 | char szModule[MAX_MODULE_NAME32 + 1];
287 | char szExePath[MAX_PATH];
288 | } MODULEENTRY32;
289 | typedef MODULEENTRY32 *PMODULEENTRY32;
290 | typedef MODULEENTRY32 *LPMODULEENTRY32;
291 | #pragma pack(pop)
292 |
293 | // **************************************** PSAPI ************************
294 | typedef struct _MODULEINFO
295 | {
296 | LPVOID lpBaseOfDll;
297 | DWORD SizeOfImage;
298 | LPVOID EntryPoint;
299 | } MODULEINFO, *LPMODULEINFO;
300 |
301 | // Normally it should be enough to use 'CONTEXT_FULL' (better would be 'CONTEXT_ALL')
302 | #define USED_CONTEXT_FLAGS CONTEXT_FULL
303 |
304 | // only available on msvc2015+ (_MSC_VER >= 1900)
305 | #ifdef __cplusplus
306 | extern "C" {
307 | #endif
308 | void** __cdecl __current_exception_context();
309 | #ifdef __cplusplus
310 | }
311 | #endif
312 |
313 | typedef struct sw_module_cache_item
314 | {
315 | char name[32];
316 | DWORD64 base_addr;
317 | } sw_module_cache_item;
318 |
319 | typedef struct sw_context_internal
320 | {
321 | sw_context* parent;
322 | CONTEXT ctx;
323 | HMODULE dbg_help;
324 | HANDLE process;
325 | SymCleanup_t fSymCleanup;
326 | SymFunctionTableAccess64_t fSymFunctionTableAccess64;
327 | SymGetModuleBase64_t fSymGetModuleBase64;
328 | SymGetModuleInfo64_t fSymGetModuleInfo64;
329 |
330 | SymGetOptions_t fSymGetOptions;
331 | SymGetSymFromAddr64_t fSymGetSymFromAddr64;
332 | SymGetLineFromAddr64_t fSymGetLineFromAddr64;
333 | SymInitialize_t fSymInitialize;
334 | SymLoadModule64_t fSymLoadModule64;
335 | SymUnloadModule64_t fSymUnloadModule64;
336 |
337 | SymSetOptions_t fSymSetOptions;
338 | StackWalk64_t fStackWalk64;
339 | UnDecorateSymbolName_t fUnDecorateSymbolName;
340 | SymGetSearchPath_t fSymGetSearchPath;
341 | EnumerateLoadedModules64_t fEnumerateLoadedModules64;
342 |
343 | CRITICAL_SECTION modules_cs;
344 | uint32_t num_modules;
345 | uint32_t max_modules;
346 | sw_module_cache_item* modules;
347 | } sw_context_internal;
348 |
349 | typedef struct sw_context
350 | {
351 | sw_callbacks callbacks;
352 | void* callbacks_userptr;
353 | sw_sys_handle process;
354 | uint32_t process_id;
355 | bool modules_loaded;
356 | bool reload_modules;
357 | bool fatal_error; // cannot recover anymore
358 | char sympath[SW_MAX_NAME_LEN];
359 | uint32_t options;
360 | uint32_t max_recursion;
361 | sw_context_internal internal;
362 | uint32_t frames_to_skip;
363 | uint32_t frames_to_capture;
364 | } sw_context;
365 |
366 | static char sw__dbghelp_hintpath[SW_MAX_NAME_LEN];
367 |
368 | _SW_PRIVATE bool sw__get_module_info(sw_context_internal* ctxi, HANDLE process, DWORD64 baseAddr, IMAGEHLP_MODULE64_V3* modinfo)
369 | {
370 | memset(modinfo, 0, sizeof(IMAGEHLP_MODULE64_V3));
371 | if (ctxi->fSymGetModuleInfo64 == NULL) {
372 | SetLastError(ERROR_DLL_INIT_FAILED);
373 | return false;
374 | }
375 | // First try to use the larger ModuleInfo-Structure
376 | modinfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V3);
377 | char data[4096]; // reserve enough memory, so the bug in v6.3.5.1 does not lead to memory-overwrites...
378 |
379 | memcpy(data, modinfo, sizeof(IMAGEHLP_MODULE64_V3));
380 | static bool use_v3_version = true;
381 | if (use_v3_version) {
382 | if (ctxi->fSymGetModuleInfo64(process, baseAddr, (IMAGEHLP_MODULE64_V3*)data) != FALSE) {
383 | // only copy as much memory as is reserved...
384 | memcpy(modinfo, data, sizeof(IMAGEHLP_MODULE64_V3));
385 | modinfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V3);
386 | return true;
387 | }
388 | use_v3_version = false; // to prevent unnecessary calls with the larger struct...
389 | }
390 |
391 | // could not retrieve the bigger structure, try with the smaller one (as defined in VC7.1)...
392 | modinfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2);
393 | memcpy(data, modinfo, sizeof(IMAGEHLP_MODULE64_V2));
394 | if (ctxi->fSymGetModuleInfo64(process, baseAddr, (IMAGEHLP_MODULE64_V3*)data) != FALSE) {
395 | // only copy as much memory as is reserved...
396 | memcpy(modinfo, data, sizeof(IMAGEHLP_MODULE64_V2));
397 | modinfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2);
398 | return true;
399 | }
400 | SetLastError(ERROR_DLL_INIT_FAILED);
401 | return false;
402 | }
403 |
404 | _SW_PRIVATE bool sw__get_module_info_csentry(sw_context_internal* ctxi, HANDLE process, DWORD64 baseAddr,
405 | sw_callstack_entry* cs_entry)
406 | {
407 | IMAGEHLP_MODULE64_V3 module;
408 | bool r = sw__get_module_info(ctxi, process, baseAddr, &module);
409 | if (r) {
410 | SW_ASSERT(cs_entry);
411 | cs_entry->symbol_type = module.SymType;
412 |
413 | switch (module.SymType) {
414 | case SymNone: cs_entry->symbol_type_str = "-nosymbols-"; break;
415 | case SymCoff: cs_entry->symbol_type_str = "COFF"; break;
416 | case SymCv: cs_entry->symbol_type_str = "CV"; break;
417 | case SymPdb: cs_entry->symbol_type_str = "PDB"; break;
418 | case SymExport: cs_entry->symbol_type_str = "-exported-"; break;
419 | case SymDeferred: cs_entry->symbol_type_str = "-deferred-"; break;
420 | case SymSym: cs_entry->symbol_type_str = "SYM"; break;
421 | #if API_VERSION_NUMBER >= 9
422 | case SymDia: cs_entry->symbol_type_str = "DIA"; break;
423 | #endif
424 | case 8: cs_entry->symbol_type_str = "Virtual"; break;
425 | default: cs_entry->symbol_type_str = NULL; break;
426 | }
427 |
428 | sw__strcpy(cs_entry->module_name, sizeof(cs_entry->module_name), module.ModuleName);
429 | cs_entry->baseof_image = module.BaseOfImage;
430 | sw__strcpy(cs_entry->loaded_image_name, sizeof(cs_entry->module_name), module.LoadedImageName);
431 | } else {
432 | sw__strcpy(cs_entry->module_name, sizeof(cs_entry->module_name), "[NA]");
433 | sw__strcpy(cs_entry->loaded_image_name, sizeof(cs_entry->module_name), "[NA]");
434 | }
435 |
436 | return r;
437 | }
438 |
439 | static BOOL sw__enum_loaded_modules_cb(PCSTR module_path, ULONGLONG module_base, ULONG module_size, PVOID userptr)
440 | {
441 | // resolve module name
442 | char module_name[SW_MAX_NAME_LEN];
443 | const char* backslash = strrchr(module_path, '\\');
444 | if (backslash) {
445 | sw__strcpy(module_name, sizeof(module_name), backslash+1);
446 | } else {
447 | sw__strcpy(module_name, sizeof(module_name), module_path);
448 | }
449 | sw_context_internal* ctxi = (sw_context_internal*)userptr;
450 | EnterCriticalSection(&ctxi->modules_cs);
451 | // don't Load modules that are already in the cache
452 | for (uint32_t mi = 0; mi < ctxi->num_modules; mi++) {
453 | if (strcmp(ctxi->modules[mi].name, module_name) == 0) {
454 | LeaveCriticalSection(&ctxi->modules_cs);
455 | return ERROR_SUCCESS;
456 | }
457 | }
458 | LeaveCriticalSection(&ctxi->modules_cs);
459 |
460 | DWORD64 mod_addr = ctxi->fSymLoadModule64(ctxi->process, NULL, module_path, module_name, module_base, module_size);
461 | if (mod_addr == 0) {
462 | return FALSE;
463 | } else {
464 | EnterCriticalSection(&ctxi->modules_cs);
465 |
466 | // Add the module to cache
467 | if (ctxi->num_modules == ctxi->max_modules) {
468 | ctxi->max_modules <<= 1;
469 | sw_module_cache_item* modules = (sw_module_cache_item*)SW_MALLOC(ctxi->max_modules*sizeof(sw_module_cache_item));
470 | if (!modules) {
471 | LeaveCriticalSection(&ctxi->modules_cs);
472 | SW_ASSERT(0);
473 | return 0;
474 | }
475 | memcpy(modules, ctxi->modules, ctxi->num_modules*sizeof(sw_module_cache_item));
476 | SW_FREE(ctxi->modules);
477 | ctxi->modules = modules;
478 | }
479 |
480 | sw_module_cache_item* mcache = &ctxi->modules[ctxi->num_modules++];
481 | sw__strcpy(mcache->name, sizeof(mcache->name), module_name);
482 | mcache->base_addr = mod_addr;
483 | LeaveCriticalSection(&ctxi->modules_cs);
484 | }
485 |
486 | if (ctxi->parent->callbacks.load_module) {
487 | ctxi->parent->callbacks.load_module(module_path, module_name, mod_addr, module_size, ctxi->parent->callbacks_userptr);
488 | }
489 |
490 | return TRUE;
491 | }
492 |
493 | _SW_PRIVATE bool sw__get_module_list_TH32(sw_context_internal* ctxi, DWORD pid)
494 | {
495 | typedef HANDLE(__stdcall * CreateToolhelp32Snapshot_t)(DWORD dwFlags, DWORD th32ProcessID);
496 | typedef BOOL(__stdcall * Module32First_t)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
497 | typedef BOOL(__stdcall * Module32Next_t)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
498 |
499 | // try both dlls...
500 | const char* dllname[] = { "kernel32.dll", "tlhelp32.dll" };
501 | HINSTANCE hToolhelp = NULL;
502 | CreateToolhelp32Snapshot_t fCreateToolhelp32Snapshot = NULL;
503 | Module32First_t fModule32First = NULL;
504 | Module32Next_t fModule32Next = NULL;
505 |
506 | HANDLE hSnap;
507 | MODULEENTRY32 me;
508 | me.dwSize = sizeof(me);
509 | BOOL keepGoing;
510 | size_t i;
511 |
512 | for (i = 0; i < (sizeof(dllname) / sizeof(dllname[0])); i++) {
513 | hToolhelp = LoadLibraryA(dllname[i]);
514 | if (hToolhelp == NULL)
515 | continue;
516 | fCreateToolhelp32Snapshot = (CreateToolhelp32Snapshot_t)GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot");
517 | fModule32First = (Module32First_t)GetProcAddress(hToolhelp, "Module32First");
518 | fModule32Next = (Module32Next_t)GetProcAddress(hToolhelp, "Module32Next");
519 | if ((fCreateToolhelp32Snapshot != NULL) && (fModule32First != NULL) && (fModule32Next != NULL))
520 | break; // found the functions!
521 | FreeLibrary(hToolhelp);
522 | hToolhelp = NULL;
523 | }
524 |
525 | if (hToolhelp == NULL)
526 | return FALSE;
527 |
528 | hSnap = fCreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
529 | if (hSnap == (HANDLE)-1) {
530 | FreeLibrary(hToolhelp);
531 | return FALSE;
532 | }
533 |
534 | keepGoing = !!fModule32First(hSnap, &me);
535 | int cnt = 0;
536 | while (keepGoing) {
537 | sw__enum_loaded_modules_cb(me.szExePath, (DWORD64)me.modBaseAddr, me.modBaseSize, ctxi);
538 | cnt++;
539 | keepGoing = !!fModule32Next(hSnap, &me);
540 | }
541 | CloseHandle(hSnap);
542 | FreeLibrary(hToolhelp);
543 | return (cnt <= 0) ? false : true;
544 | } // sw__get_module_list_TH32
545 |
546 | _SW_PRIVATE bool sw__load_modules_internal(sw_context_internal* ctxi, HANDLE process, DWORD process_id)
547 | {
548 | if (!sw__get_module_list_TH32(ctxi, process_id)) {
549 | return ctxi->fEnumerateLoadedModules64(process, sw__enum_loaded_modules_cb, ctxi) ? true : false;
550 | }
551 | return true;
552 | }
553 |
554 | _SW_PRIVATE PCONTEXT get_current_exception_context()
555 | {
556 | PCONTEXT* pctx = (PCONTEXT*)__current_exception_context();
557 | return pctx ? *pctx : NULL;
558 | }
559 |
560 | _SW_PRIVATE sw_context* sw__create_context(uint32_t options, uint32_t process_id, sw_sys_handle process,
561 | PCONTEXT context_win, sw_callbacks callbacks, void* userptr)
562 | {
563 | SW_ASSERT(process);
564 |
565 | sw_context* ctx = (sw_context*)SW_MALLOC(sizeof(sw_context));
566 | memset(ctx, 0x0, sizeof(sw_context));
567 |
568 | ctx->options = options;
569 | ctx->max_recursion = 1000;
570 | ctx->process_id = process_id;
571 | ctx->process = process;
572 | ctx->callbacks = callbacks;
573 | ctx->callbacks_userptr = userptr;
574 | ctx->internal.process = process;
575 | ctx->internal.parent = ctx;
576 | ctx->internal.ctx.ContextFlags = 0;
577 | if (context_win)
578 | ctx->internal.ctx = *context_win;
579 | ctx->frames_to_skip = 0;
580 | ctx->frames_to_capture = 0xffffffff;
581 | return ctx;
582 | }
583 |
584 | SW_API_IMPL sw_sys_handle sw_load_dbghelp()
585 | {
586 | HMODULE dbg_help = NULL;
587 | // Dynamically load the Entry-Points for dbghelp.dll:
588 | // First try to load the newest one from
589 | TCHAR temp[4096];
590 |
591 | if (sw__dbghelp_hintpath[0]) {
592 | sw__strcpy(temp, sizeof(temp), sw__dbghelp_hintpath);
593 | strcat_s(temp, sizeof(temp), "\\dbghelp.dll");
594 | if (GetFileAttributes(temp) != INVALID_FILE_ATTRIBUTES) {
595 | dbg_help = LoadLibraryA(temp);
596 | }
597 | }
598 |
599 | // ".local" file does not exist, so we can try to load the dbghelp.dll from the
600 | // "Debugging Tools for Windows" Ok, first try the new path according to the
601 | // architecture:
602 | #ifdef _M_IX86
603 | if ((dbg_help == NULL) && (GetEnvironmentVariableA("ProgramFiles", temp, sizeof(temp)) > 0)) {
604 | strcat_s(temp, sizeof(temp), "\\Debugging Tools for Windows (x86)\\dbghelp.dll");
605 | // now check if the file exists:
606 | if (GetFileAttributes(temp) != INVALID_FILE_ATTRIBUTES) {
607 | dbg_help = LoadLibraryA(temp);
608 | }
609 | }
610 | #elif _M_X64
611 | if ((dbg_help == NULL) && (GetEnvironmentVariableA("ProgramFiles", temp, sizeof(temp)) > 0)) {
612 | strcat_s(temp, sizeof(temp), "\\Debugging Tools for Windows (x64)\\dbghelp.dll");
613 | // now check if the file exists:
614 | if (GetFileAttributes(temp) != INVALID_FILE_ATTRIBUTES) {
615 | dbg_help = LoadLibraryA(temp);
616 | }
617 | }
618 | #elif _M_IA64
619 | if ((dbg_help == NULL) &&
620 | (GetEnvironmentVariableA("ProgramFiles", temp, sizeof(temp)) > 0)) {
621 | strcat_s(temp, sizeof(temp), "\\Debugging Tools for Windows (ia64)\\dbghelp.dll");
622 | // now check if the file exists:
623 | if (GetFileAttributes(temp) != INVALID_FILE_ATTRIBUTES) {
624 | dbg_help = LoadLibraryA(temp);
625 | }
626 | }
627 | #endif
628 |
629 | // If still not found, try the old directories...
630 | if ((dbg_help == NULL) && (GetEnvironmentVariableA("ProgramFiles", temp, sizeof(temp)) > 0)) {
631 | strcat_s(temp, sizeof(temp), "\\Debugging Tools for Windows\\dbghelp.dll");
632 | // now check if the file exists:
633 | if (GetFileAttributes(temp) != INVALID_FILE_ATTRIBUTES) {
634 | dbg_help = LoadLibraryA(temp);
635 | }
636 | }
637 | #if defined _M_X64 || defined _M_IA64
638 | // Still not found? Then try to load the (old) 64-Bit version:
639 | if ((dbg_help == NULL) &&
640 | (GetEnvironmentVariableA("ProgramFiles", temp, sizeof(temp)) > 0)) {
641 | strcat_s(temp, sizeof(temp), "\\Debugging Tools for Windows 64-Bit\\dbghelp.dll");
642 | if (GetFileAttributes(temp) != INVALID_FILE_ATTRIBUTES) {
643 | dbg_help = LoadLibraryA(temp);
644 | }
645 | }
646 | #endif
647 | if (dbg_help == NULL) // if not already loaded, try to load a default-one
648 | dbg_help = LoadLibraryA("dbghelp.dll");
649 |
650 | return dbg_help;
651 | }
652 |
653 | _SW_PRIVATE bool sw__init_internal(sw_context_internal* ctxi, const char* sympath)
654 | {
655 | ctxi->dbg_help = (HMODULE)sw_load_dbghelp();
656 | if (ctxi->dbg_help == NULL) {
657 | return false;
658 | }
659 |
660 | ctxi->fSymInitialize = (SymInitialize_t)GetProcAddress(ctxi->dbg_help, "SymInitialize");
661 | ctxi->fSymCleanup = (SymCleanup_t)GetProcAddress(ctxi->dbg_help, "SymCleanup");
662 |
663 | ctxi->fStackWalk64 = (StackWalk64_t)GetProcAddress(ctxi->dbg_help, "StackWalk64");
664 | ctxi->fSymGetOptions = (SymGetOptions_t)GetProcAddress(ctxi->dbg_help, "SymGetOptions");
665 | ctxi->fSymSetOptions = (SymSetOptions_t)GetProcAddress(ctxi->dbg_help, "SymSetOptions");
666 |
667 | ctxi->fSymFunctionTableAccess64 = (SymFunctionTableAccess64_t)GetProcAddress(ctxi->dbg_help, "SymFunctionTableAccess64");
668 | ctxi->fSymGetLineFromAddr64 = (SymGetLineFromAddr64_t)GetProcAddress(ctxi->dbg_help, "SymGetLineFromAddr64");
669 | ctxi->fSymGetModuleBase64 = (SymGetModuleBase64_t)GetProcAddress(ctxi->dbg_help, "SymGetModuleBase64");
670 | ctxi->fSymGetModuleInfo64 = (SymGetModuleInfo64_t)GetProcAddress(ctxi->dbg_help, "SymGetModuleInfo64");
671 | ctxi->fSymGetSymFromAddr64 = (SymGetSymFromAddr64_t)GetProcAddress(ctxi->dbg_help, "SymGetSymFromAddr64");
672 | ctxi->fUnDecorateSymbolName = (UnDecorateSymbolName_t)GetProcAddress(ctxi->dbg_help, "UnDecorateSymbolName");
673 | ctxi->fSymLoadModule64 = (SymLoadModule64_t)GetProcAddress(ctxi->dbg_help, "SymLoadModule64");
674 | ctxi->fSymUnloadModule64 = (SymUnloadModule64_t)GetProcAddress(ctxi->dbg_help, "SymUnloadModule64");
675 | ctxi->fSymGetSearchPath = (SymGetSearchPath_t)GetProcAddress(ctxi->dbg_help, "SymGetSearchPath");
676 | ctxi->fEnumerateLoadedModules64 = (EnumerateLoadedModules64_t)GetProcAddress(ctxi->dbg_help, "EnumerateLoadedModules64");
677 |
678 | if (ctxi->fSymCleanup == NULL || ctxi->fSymFunctionTableAccess64 == NULL || ctxi->fSymGetModuleBase64 == NULL ||
679 | ctxi->fSymGetModuleInfo64 == NULL || ctxi->fSymGetOptions == NULL || ctxi->fSymGetSymFromAddr64 == NULL ||
680 | ctxi->fSymInitialize == NULL || ctxi->fSymSetOptions == NULL || ctxi->fStackWalk64 == NULL ||
681 | ctxi->fUnDecorateSymbolName == NULL || ctxi->fSymLoadModule64 == NULL || ctxi->fSymUnloadModule64 == NULL ||
682 | ctxi->fEnumerateLoadedModules64 == NULL)
683 | {
684 | FreeLibrary(ctxi->dbg_help);
685 | ctxi->dbg_help = NULL;
686 | ctxi->fSymCleanup = NULL;
687 | return false;
688 | }
689 |
690 | // SymInitialize
691 | if (ctxi->fSymInitialize(ctxi->process, sympath, FALSE) == FALSE) {
692 | SW_LOG_ERROR("SymInitialize failed (ErrorCode=%lu)", GetLastError());
693 | return false;
694 | }
695 |
696 | DWORD sym_opts = ctxi->fSymGetOptions(); // SymGetOptions
697 | sym_opts |= SYMOPT_LOAD_LINES;
698 | sym_opts |= SYMOPT_FAIL_CRITICAL_ERRORS;
699 | sym_opts = ctxi->fSymSetOptions(sym_opts);
700 |
701 | char buf[SW_MAX_NAME_LEN] = { 0 };
702 | if (ctxi->fSymGetSearchPath != NULL) {
703 | if (ctxi->fSymGetSearchPath(ctxi->process, buf, SW_MAX_NAME_LEN) == FALSE)
704 | SW_LOG_ERROR("SymGetSearchPath failed (ErrorCode=%lu)", GetLastError());
705 | }
706 |
707 | if (ctxi->parent->callbacks.symbol_init) {
708 | ctxi->parent->callbacks.symbol_init(buf, sym_opts, ctxi->parent->callbacks_userptr);
709 | }
710 |
711 | // initialize module cache
712 | InitializeCriticalSection(&ctxi->modules_cs);
713 | ctxi->max_modules = 32;
714 | ctxi->modules = (sw_module_cache_item*)SW_MALLOC(ctxi->max_modules*sizeof(sw_module_cache_item));
715 | if (!ctxi->modules) {
716 | return false;
717 | }
718 |
719 | return true;
720 | }
721 |
722 | _SW_PRIVATE bool sw__load_modules(sw_context* ctx)
723 | {
724 | if (ctx->fatal_error)
725 | return false;
726 |
727 | if (ctx->modules_loaded)
728 | return true;
729 |
730 | // Build the sym-path:
731 | char sympath[SW_MAX_NAME_LEN]; sympath[0] = 0;
732 | if ((ctx->options & SW_OPTIONS_SYMBUILDPATH) != 0) {
733 | // Now first add the (optional) provided sympath:
734 | if (ctx->sympath[0]) {
735 | strcat_s(sympath, sizeof(sympath), ctx->sympath);
736 | strcat_s(sympath, sizeof(sympath), ";");
737 | }
738 |
739 | strcat_s(sympath, sizeof(sympath), ".;");
740 |
741 | char temp[1024];
742 | // Now add the current directory:
743 | if (GetCurrentDirectoryA(sizeof(temp), temp) > 0) {
744 | temp[sizeof(temp) - 1] = 0;
745 | strcat_s(sympath, sizeof(sympath), temp);
746 | strcat_s(sympath, sizeof(sympath), ";");
747 | }
748 |
749 | // Now add the path for the main-module:
750 | if (GetModuleFileNameA(NULL, temp, sizeof(temp)) > 0) {
751 | temp[sizeof(temp) - 1] = 0;
752 | for (char* p = (temp + strlen(temp) - 1); p >= temp; --p) {
753 | // locate the rightmost path separator
754 | if ((*p == '\\') || (*p == '/') || (*p == ':')) {
755 | *p = 0;
756 | break;
757 | }
758 | } // for (search for path separator...)
759 | if (strlen(temp) > 0) {
760 | strcat_s(sympath, sizeof(sympath), temp);
761 | strcat_s(sympath, sizeof(sympath), ";");
762 | }
763 | }
764 | if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", temp, sizeof(temp)) > 0) {
765 | temp[sizeof(temp) - 1] = 0;
766 | strcat_s(sympath, sizeof(sympath), temp);
767 | strcat_s(sympath, sizeof(sympath), ";");
768 | }
769 | if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", temp, sizeof(temp)) > 0) {
770 | temp[sizeof(temp) - 1] = 0;
771 | strcat_s(sympath, sizeof(sympath), temp);
772 | strcat_s(sympath, sizeof(sympath), ";");
773 | }
774 | if (GetEnvironmentVariableA("SYSTEMROOT", temp, sizeof(temp)) > 0) {
775 | temp[sizeof(temp) - 1] = 0;
776 | strcat_s(sympath, sizeof(sympath), temp);
777 | strcat_s(sympath, sizeof(sympath), ";");
778 | // also add the "system32"-directory:
779 | strcat_s(temp, sizeof(temp), "\\system32");
780 | strcat_s(sympath, sizeof(sympath), temp);
781 | strcat_s(sympath, sizeof(sympath), ";");
782 | }
783 |
784 | if ((ctx->options & SW_OPTIONS_SYMUSESERVER) != 0) {
785 | if (GetEnvironmentVariableA("SYSTEMDRIVE", temp, sizeof(temp)) > 0) {
786 | temp[sizeof(temp) - 1] = 0;
787 | strcat_s(sympath, sizeof(sympath), "SRV*");
788 | strcat_s(sympath, sizeof(sympath), temp);
789 | strcat_s(sympath, sizeof(sympath), "\\websymbols");
790 | strcat_s(sympath, sizeof(sympath), "*https://msdl.microsoft.com/download/symbols;");
791 | } else
792 | strcat_s(sympath, sizeof(sympath),
793 | "SRV*c:\\websymbols*https://msdl.microsoft.com/download/symbols;");
794 | }
795 | } // if SW_OPTIONS_SYMBUILDPATH
796 |
797 | // First Init the whole stuff...
798 | if (!sw__init_internal(&ctx->internal, sympath)) {
799 | SW_LOG_ERROR("Error initializing dbghelp.dll");
800 | SetLastError(ERROR_DLL_INIT_FAILED);
801 | ctx->fatal_error = true;
802 | return false;
803 | }
804 |
805 | if (sw__load_modules_internal(&ctx->internal, ctx->process, ctx->process_id)) {
806 | ctx->modules_loaded = true;
807 | }
808 | return ctx->modules_loaded;
809 | }
810 |
811 | SW_API_IMPL sw_context* sw_create_context_capture(uint32_t options, sw_callbacks callbacks, void* userptr)
812 | {
813 | return sw__create_context(options, GetCurrentProcessId(), GetCurrentProcess(), NULL, callbacks, userptr);
814 | }
815 |
816 | SW_API_IMPL sw_context* sw_create_context_capture_other(uint32_t options, uint32_t process_id,
817 | sw_sys_handle process, sw_callbacks callbacks,
818 | void* userptr)
819 | {
820 | SW_ASSERT(process_id);
821 | SW_ASSERT(process);
822 |
823 | return sw__create_context(options, process_id, process, NULL, callbacks, userptr);
824 | }
825 |
826 | SW_API_IMPL sw_context* sw_create_context_exception(uint32_t options,
827 | sw_exception_pointers exp_ptrs,
828 | sw_callbacks callbacks, void* userptr)
829 | {
830 | SW_ASSERT(exp_ptrs);
831 |
832 | PCONTEXT ctx_win = ((PEXCEPTION_POINTERS)exp_ptrs)->ContextRecord;
833 | return sw__create_context(options, GetCurrentProcessId(), GetCurrentProcess(), ctx_win, callbacks, userptr);
834 | }
835 |
836 | SW_API_IMPL sw_context* sw_create_context_catch(uint32_t options, sw_callbacks callbacks, void* userptr)
837 | {
838 | PCONTEXT ctx_win = get_current_exception_context();
839 | SW_ASSERT(ctx_win);
840 |
841 | return sw__create_context(options, GetCurrentProcessId(), GetCurrentProcess(), ctx_win, callbacks, userptr);
842 | }
843 |
844 | SW_API_IMPL void sw_destroy_context(sw_context* ctx)
845 | {
846 | if (ctx) {
847 | SW_FREE(ctx->internal.modules);
848 | DeleteCriticalSection(&ctx->internal.modules_cs);
849 |
850 | if (ctx->internal.fSymCleanup)
851 | ctx->internal.fSymCleanup(ctx->process);
852 | if (ctx->internal.dbg_help)
853 | FreeLibrary(ctx->internal.dbg_help);
854 |
855 | memset(ctx, 0x0, sizeof(*ctx));
856 | SW_FREE(ctx);
857 | }
858 | }
859 |
860 | SW_API_IMPL void sw_set_symbol_path(sw_context* ctx, const char* sympath)
861 | {
862 | SW_ASSERT(ctx);
863 | SW_ASSERT(sympath);
864 |
865 | sw__strcpy(ctx->sympath, sizeof(ctx->sympath), sympath);
866 | ctx->options |= SW_OPTIONS_SYMBUILDPATH;
867 | }
868 |
869 | SW_API_IMPL void sw_set_callstack_limits(sw_context* ctx, uint32_t frames_to_skip, uint32_t frames_to_capture)
870 | {
871 | ctx->frames_to_skip = frames_to_skip;
872 | ctx->frames_to_capture = frames_to_capture;
873 | }
874 |
875 | // The following is used to pass the "userData"-Pointer to the user-provided readMemoryFunction
876 | // This has to be done due to a problem with the "hProcess"-parameter in x64...
877 | // Because this class is in no case multi-threading-enabled (because of the limitations
878 | // of dbghelp.dll) it is "safe" to use a static-variable
879 | static ReadProcessMemoryRoutine_t s_read_mem_func = NULL;
880 | static LPVOID s_read_mem_func_userdata = NULL;
881 |
882 | _SW_PRIVATE BOOL __stdcall sw__read_proc_mem(HANDLE hProcess,
883 | DWORD64 qwBaseAddress,
884 | PVOID lpBuffer,
885 | DWORD nSize,
886 | LPDWORD lpNumberOfBytesRead)
887 | {
888 | if (s_read_mem_func == NULL) {
889 | SIZE_T st;
890 | BOOL ret = ReadProcessMemory(hProcess, (LPVOID)qwBaseAddress, lpBuffer, nSize, &st);
891 | *lpNumberOfBytesRead = (DWORD)st;
892 | return ret;
893 | } else {
894 | return s_read_mem_func(hProcess, qwBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead, s_read_mem_func_userdata);
895 | }
896 | }
897 |
898 | static bool sw_show_callstack_fast(sw_context* ctx, void* callbacks_userptr)
899 | {
900 | sw_callstack_entry cs_entry;
901 | IMAGEHLP_LINE64 line;
902 | uint32_t max_frames = ctx->frames_to_capture != 0xffffffff ? ctx->frames_to_capture : SW_MAX_FRAMES;
903 | max_frames = max_frames > SW_MAX_FRAMES ? SW_MAX_FRAMES : max_frames;
904 |
905 | void** backtrace = (void**)alloca(sizeof(void*) * max_frames);
906 | if (!backtrace) {
907 | return false;
908 | }
909 |
910 | uint32_t frames_to_skip = ctx->frames_to_skip > 2 ? ctx->frames_to_skip : 2;
911 | USHORT num_captures = RtlCaptureStackBackTrace(frames_to_skip, max_frames, backtrace, NULL);
912 | if (num_captures == 0) {
913 | return true;
914 | }
915 |
916 | IMAGEHLP_SYMBOL64* symbol = (IMAGEHLP_SYMBOL64*)alloca(sizeof(IMAGEHLP_SYMBOL64) + SW_MAX_NAME_LEN);
917 | if (!symbol) {
918 | return false;
919 | }
920 | memset(symbol, 0, sizeof(IMAGEHLP_SYMBOL64) + SW_MAX_NAME_LEN);
921 | symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
922 | symbol->MaxNameLength = SW_MAX_NAME_LEN;
923 |
924 | if (ctx->callbacks.callstack_begin) {
925 | ctx->callbacks.callstack_begin(callbacks_userptr);
926 | }
927 |
928 | for (USHORT i = 0; i < num_captures; i++) {
929 | memset(&cs_entry, 0x0, sizeof(cs_entry));
930 | if ((ctx->options & SW_OPTIONS_SYMBOL) && ctx->internal.fSymGetSymFromAddr64 != NULL) {
931 | if (ctx->internal.fSymGetSymFromAddr64(ctx->process, (DWORD64)backtrace[i], &(cs_entry.offset_from_symbol), symbol) != FALSE) {
932 |
933 | sw__strcpy(cs_entry.name, sizeof(cs_entry.name), symbol->Name);
934 | sw__strcpy(cs_entry.und_name, sizeof(cs_entry.und_name), symbol->Name);
935 | } else {
936 | DWORD gle = GetLastError();
937 | if (gle == ERROR_INVALID_ADDRESS)
938 | break;
939 | else if (gle == ERROR_MOD_NOT_FOUND)
940 | continue;
941 |
942 | SW_LOG_ERROR("SymGetSymFromAddr64 failed (ErrorCode=%lu)", gle);
943 | break;
944 | }
945 | }
946 |
947 | // show line number info, NT5.0-method (SymGetLineFromAddr64())
948 | if ((ctx->options && SW_OPTIONS_SOURCEPOS) && ctx->internal.fSymGetLineFromAddr64 != NULL) { // yes, we have SymGetLineFromAddr64()
949 | if (ctx->internal.fSymGetLineFromAddr64(ctx->process, (DWORD64)backtrace[i], (PDWORD)&(cs_entry.offset_from_line), &line) != FALSE) {
950 | cs_entry.line = line.LineNumber;
951 | sw__strcpy(cs_entry.line_filename, SW_MAX_NAME_LEN, line.FileName);
952 | } else {
953 | DWORD gle = GetLastError();
954 | if (gle == ERROR_INVALID_ADDRESS)
955 | break;
956 | else if (gle == ERROR_MOD_NOT_FOUND)
957 | continue;
958 |
959 | SW_LOG_ERROR("SymGetLineFromAddr64 failed (ErrorCode=%lu)", gle);
960 | break;
961 | }
962 | } // yes, we have SymGetLineFromAddr64()
963 |
964 | // show module info (SymGetModuleInfo64())
965 | if (ctx->options & SW_OPTIONS_MODULEINFO) {
966 | if (!sw__get_module_info_csentry(&ctx->internal, ctx->process, (uint64_t)backtrace[i], &cs_entry)) { // got module info OK
967 | SW_LOG_ERROR("SymGetModuleInfo64 failed (ErrorCode=%lu)", GetLastError());
968 | }
969 | }
970 |
971 | if (ctx->callbacks.callstack_entry) {
972 | ctx->callbacks.callstack_entry(&cs_entry, callbacks_userptr);
973 | }
974 | }
975 |
976 | if (ctx->callbacks.callstack_end) {
977 | ctx->callbacks.callstack_end(callbacks_userptr);
978 | }
979 |
980 | return true;
981 | }
982 |
983 | SW_API_IMPL bool sw_show_callstack_userptr(sw_context* ctx, sw_sys_handle thread_hdl, void* callbacks_userptr)
984 | {
985 | CONTEXT c;
986 | callbacks_userptr = callbacks_userptr ? callbacks_userptr : ctx->callbacks_userptr;
987 |
988 | if (thread_hdl == NULL) {
989 | thread_hdl = GetCurrentThread();
990 | }
991 |
992 | if (!ctx->modules_loaded) {
993 | sw__load_modules(ctx);
994 | }
995 |
996 | if (ctx->reload_modules) {
997 | sw__load_modules_internal(&ctx->internal, ctx->process, ctx->process_id);
998 | ctx->reload_modules = false;
999 | }
1000 |
1001 | if (ctx->internal.dbg_help == NULL) {
1002 | SetLastError(ERROR_DLL_INIT_FAILED);
1003 | return false;
1004 | }
1005 |
1006 | // If no context is provided, capture the context
1007 | // See: https://stackwalker.codeplex.com/discussions/446958
1008 | if (GetThreadId(thread_hdl) == GetCurrentThreadId()) {
1009 | if (ctx->internal.ctx.ContextFlags != 0) {
1010 | c = ctx->internal.ctx; // context taken at Init
1011 | } else {
1012 | // Take the faster path
1013 | return sw_show_callstack_fast(ctx, callbacks_userptr);
1014 | }
1015 | } else {
1016 | SuspendThread(thread_hdl);
1017 | memset(&c, 0, sizeof(CONTEXT));
1018 | c.ContextFlags = USED_CONTEXT_FLAGS;
1019 |
1020 | // TODO: Detect if you want to get a thread context of a different process, which is
1021 | // running a different processor architecture... This does only work if we are x64 and
1022 | // the target process is x64 or x86; It cannot work, if this process is x64 and the
1023 | // target process is x64... this is not supported... See also:
1024 | // http://www.howzatt.demon.co.uk/articles/DebuggingInWin64.html
1025 | if (GetThreadContext(thread_hdl, &c) == FALSE) {
1026 | ResumeThread(thread_hdl);
1027 | return false;
1028 | }
1029 | }
1030 |
1031 | sw_callstack_entry cs_entry;
1032 | IMAGEHLP_SYMBOL64* symbol = NULL;
1033 | IMAGEHLP_LINE64 line;
1034 | uint32_t frame_num;
1035 | uint32_t cur_recursion_count = 0;
1036 |
1037 | // init STACKFRAME for first call
1038 | STACKFRAME64 s; // in/out stackframe
1039 | memset(&s, 0, sizeof(s));
1040 | DWORD image_type;
1041 | #ifdef _M_IX86
1042 | // normally, call ImageNtHeader() and use machine info from PE header
1043 | image_type = IMAGE_FILE_MACHINE_I386;
1044 | s.AddrPC.Offset = c.Eip;
1045 | s.AddrPC.Mode = AddrModeFlat;
1046 | s.AddrFrame.Offset = c.Ebp;
1047 | s.AddrFrame.Mode = AddrModeFlat;
1048 | s.AddrStack.Offset = c.Esp;
1049 | s.AddrStack.Mode = AddrModeFlat;
1050 | #elif _M_X64
1051 | image_type = IMAGE_FILE_MACHINE_AMD64;
1052 | s.AddrPC.Offset = c.Rip;
1053 | s.AddrPC.Mode = AddrModeFlat;
1054 | s.AddrFrame.Offset = c.Rsp;
1055 | s.AddrFrame.Mode = AddrModeFlat;
1056 | s.AddrStack.Offset = c.Rsp;
1057 | s.AddrStack.Mode = AddrModeFlat;
1058 | #elif _M_IA64
1059 | image_type = IMAGE_FILE_MACHINE_IA64;
1060 | s.AddrPC.Offset = c.StIIP;
1061 | s.AddrPC.Mode = AddrModeFlat;
1062 | s.AddrFrame.Offset = c.IntSp;
1063 | s.AddrFrame.Mode = AddrModeFlat;
1064 | s.AddrBStore.Offset = c.RsBSP;
1065 | s.AddrBStore.Mode = AddrModeFlat;
1066 | s.AddrStack.Offset = c.IntSp;
1067 | s.AddrStack.Mode = AddrModeFlat;
1068 | #else
1069 | # error "Platform not supported!"
1070 | #endif
1071 |
1072 | symbol = (IMAGEHLP_SYMBOL64*)alloca(sizeof(IMAGEHLP_SYMBOL64) + SW_MAX_NAME_LEN);
1073 | if (!symbol) {
1074 | ResumeThread(thread_hdl);
1075 | return false;
1076 | }
1077 | memset(symbol, 0, sizeof(IMAGEHLP_SYMBOL64) + SW_MAX_NAME_LEN);
1078 | symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
1079 | symbol->MaxNameLength = SW_MAX_NAME_LEN;
1080 |
1081 | memset(&line, 0, sizeof(line));
1082 | line.SizeOfStruct = sizeof(line);
1083 |
1084 | uint32_t frames_to_skip = ctx->frames_to_skip > 1 ? ctx->frames_to_skip : 1;
1085 | uint32_t max_frames = ctx->frames_to_capture != 0xffffffff ? ctx->frames_to_capture : SW_MAX_FRAMES;
1086 | max_frames = max_frames > SW_MAX_FRAMES ? SW_MAX_FRAMES : max_frames;
1087 |
1088 | if (ctx->callbacks.callstack_begin) {
1089 | ctx->callbacks.callstack_begin(callbacks_userptr);
1090 | }
1091 |
1092 | for (frame_num = 0;; ++frame_num) {
1093 | // get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64())
1094 | // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can
1095 | // assume that either you are done, or that the stack is so hosed that the next
1096 | // deeper frame could not be found.
1097 | // CONTEXT need not to be supplied if imageTyp is IMAGE_FILE_MACHINE_I386!
1098 | if (!ctx->internal.fStackWalk64(image_type, ctx->process, thread_hdl, &s, &c, sw__read_proc_mem,
1099 | ctx->internal.fSymFunctionTableAccess64,
1100 | ctx->internal.fSymGetModuleBase64, NULL)) {
1101 | // INFO: "StackWalk64" does not set "GetLastError"...
1102 | SW_LOG_ERROR("StackWalk64 failed");
1103 | break;
1104 | }
1105 |
1106 | if (frame_num < frames_to_skip) {
1107 | continue;
1108 | }
1109 | if (frame_num >= max_frames) {
1110 | break;
1111 | }
1112 |
1113 | cs_entry.offset = s.AddrPC.Offset;
1114 | cs_entry.name[0] = 0;
1115 | cs_entry.und_name[0] = 0;
1116 | cs_entry.offset_from_symbol = 0;
1117 | cs_entry.offset_from_line = 0;
1118 | cs_entry.line_filename[0] = 0;
1119 | cs_entry.line = 0;
1120 | cs_entry.loaded_image_name[0] = 0;
1121 | cs_entry.module_name[0] = 0;
1122 | if (s.AddrPC.Offset == s.AddrReturn.Offset) {
1123 | if ((ctx->max_recursion > 0) && (cur_recursion_count > ctx->max_recursion)) {
1124 | SW_LOG_ERROR("StackWalk64-Endless-Callstack!");
1125 | break;
1126 | }
1127 | cur_recursion_count++;
1128 | } else {
1129 | cur_recursion_count = 0;
1130 | }
1131 |
1132 | if (s.AddrPC.Offset != 0) {
1133 | // we seem to have a valid PC
1134 | // show procedure info (SymGetSymFromAddr64())
1135 | if ((ctx->options & SW_OPTIONS_SYMBOL) && ctx->internal.fSymGetSymFromAddr64 != NULL) {
1136 | if (ctx->internal.fSymGetSymFromAddr64(ctx->process, s.AddrPC.Offset, &(cs_entry.offset_from_symbol), symbol) != FALSE) {
1137 |
1138 | sw__strcpy(cs_entry.name, SW_MAX_NAME_LEN, symbol->Name);
1139 | ctx->internal.fUnDecorateSymbolName(symbol->Name, cs_entry.und_name, SW_MAX_NAME_LEN, UNDNAME_COMPLETE);
1140 | } else {
1141 | DWORD gle = GetLastError();
1142 | if (gle == ERROR_INVALID_ADDRESS)
1143 | break;
1144 | else if (gle == ERROR_MOD_NOT_FOUND)
1145 | continue;
1146 | SW_LOG_ERROR("SymGetSymFromAddr64 failed (ErrorCode=%lu)", gle);
1147 | }
1148 | }
1149 |
1150 | // show line number info, NT5.0-method (SymGetLineFromAddr64())
1151 | if ((ctx->options && SW_OPTIONS_SOURCEPOS) && ctx->internal.fSymGetLineFromAddr64 != NULL) { // yes, we have SymGetLineFromAddr64()
1152 | if (ctx->internal.fSymGetLineFromAddr64(ctx->process, s.AddrPC.Offset, (PDWORD)&(cs_entry.offset_from_line), &line) != FALSE) {
1153 | cs_entry.line = line.LineNumber;
1154 | sw__strcpy(cs_entry.line_filename, SW_MAX_NAME_LEN, line.FileName);
1155 | } else {
1156 | DWORD gle = GetLastError();
1157 | if (gle == ERROR_INVALID_ADDRESS)
1158 | break;
1159 | else if (gle == ERROR_MOD_NOT_FOUND)
1160 | continue;
1161 | SW_LOG_ERROR("SymGetLineFromAddr64 (ErrorCode=%lu)", gle);
1162 | }
1163 | } // yes, we have SymGetLineFromAddr64()
1164 |
1165 | // show module info (SymGetModuleInfo64())
1166 | if (ctx->options & SW_OPTIONS_MODULEINFO) {
1167 | if (!sw__get_module_info_csentry(&ctx->internal, ctx->process, s.AddrPC.Offset, &cs_entry)) { // got module info OK
1168 | SW_LOG_ERROR("SymGetModuleInfo64 (ErrorCode=%lu)", GetLastError());
1169 | }
1170 | }
1171 | } // s.AddrPC.Offset != 0
1172 |
1173 | // we skip the first one, because it's always from this function
1174 | if (ctx->callbacks.callstack_entry) {
1175 | ctx->callbacks.callstack_entry(&cs_entry, callbacks_userptr);
1176 | }
1177 | } // for ( frame_num )
1178 |
1179 | if (ctx->callbacks.callstack_end && frame_num > 1 ) {
1180 | ctx->callbacks.callstack_end(callbacks_userptr);
1181 | }
1182 |
1183 | return true;
1184 | }
1185 |
1186 | SW_API_IMPL bool sw_show_callstack(sw_context* ctx, sw_sys_handle thread_hdl)
1187 | {
1188 | return sw_show_callstack_userptr(ctx, thread_hdl, NULL);
1189 | }
1190 |
1191 | SW_API_IMPL uint16_t sw_capture_current(sw_context* ctx, void* symbols[SW_MAX_FRAMES], uint32_t* hash)
1192 | {
1193 | SW_ASSERT(ctx);
1194 |
1195 | uint32_t max_frames = ctx->frames_to_capture != 0xffffffff ? ctx->frames_to_capture : SW_MAX_FRAMES;
1196 | max_frames = max_frames > SW_MAX_FRAMES ? SW_MAX_FRAMES : max_frames;
1197 |
1198 | uint32_t frames_to_skip = ctx->frames_to_skip > 1 ? ctx->frames_to_skip : 1;
1199 | return (uint16_t)RtlCaptureStackBackTrace(frames_to_skip, max_frames, symbols, (PDWORD)hash);
1200 | }
1201 |
1202 | SW_API_IMPL uint16_t sw_resolve_callstack(sw_context* ctx, void* symbols[SW_MAX_FRAMES],
1203 | sw_callstack_entry entries[SW_MAX_FRAMES], uint16_t num_entries)
1204 | {
1205 | SW_ASSERT(ctx);
1206 | IMAGEHLP_LINE64 line;
1207 |
1208 | if (!sw__load_modules(ctx)) {
1209 | return 0;
1210 | }
1211 |
1212 | if (ctx->reload_modules) {
1213 | sw__load_modules_internal(&ctx->internal, ctx->process, ctx->process_id);
1214 | ctx->reload_modules = false;
1215 | }
1216 |
1217 | IMAGEHLP_SYMBOL64* symbol = (IMAGEHLP_SYMBOL64*)alloca(sizeof(IMAGEHLP_SYMBOL64) + SW_MAX_NAME_LEN);
1218 | if (!symbol) {
1219 | return false;
1220 | }
1221 | memset(symbol, 0, sizeof(IMAGEHLP_SYMBOL64) + SW_MAX_NAME_LEN);
1222 | symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
1223 | symbol->MaxNameLength = SW_MAX_NAME_LEN;
1224 |
1225 | uint16_t count = 0;
1226 | for (uint16_t i = 0; i < num_entries; i++) {
1227 | sw_callstack_entry entry = {0};
1228 | if ((ctx->options & SW_OPTIONS_SYMBOL) && ctx->internal.fSymGetSymFromAddr64 != NULL) {
1229 | if (ctx->internal.fSymGetSymFromAddr64(ctx->process, (DWORD64)symbols[i], &(entry.offset_from_symbol), symbol) != FALSE) {
1230 | sw__strcpy(entry.name, sizeof(entry.name), symbol->Name);
1231 | sw__strcpy(entry.und_name, sizeof(entry.und_name), symbol->Name);
1232 | } else {
1233 | DWORD gle = GetLastError();
1234 | if (gle != ERROR_INVALID_ADDRESS && gle != ERROR_MOD_NOT_FOUND) {
1235 | SW_LOG_ERROR("SymGetSymFromAddr64 failed (ErrorCode=%lu)", gle);
1236 | break;
1237 | }
1238 | sw__strcpy(entry.name, sizeof(entry.name), "[NA]");
1239 | sw__strcpy(entry.und_name, sizeof(entry.und_name), "[NA]");
1240 | }
1241 | }
1242 |
1243 | // show line number info, NT5.0-method (SymGetLineFromAddr64())
1244 | if ((ctx->options && SW_OPTIONS_SOURCEPOS) && ctx->internal.fSymGetLineFromAddr64 != NULL) { // yes, we have SymGetLineFromAddr64()
1245 | if (ctx->internal.fSymGetLineFromAddr64(ctx->process, (DWORD64)symbols[i],
1246 | (PDWORD)&(entry.offset_from_line), &line) != FALSE)
1247 | {
1248 | entry.line = line.LineNumber;
1249 | sw__strcpy(entry.line_filename, SW_MAX_NAME_LEN, line.FileName);
1250 | } else {
1251 | DWORD gle = GetLastError();
1252 | if (gle != ERROR_INVALID_ADDRESS && gle != ERROR_MOD_NOT_FOUND) {
1253 | SW_LOG_ERROR("SymGetLineFromAddr64 failed (ErrorCode=%lu)", gle);
1254 | break;
1255 | }
1256 | sw__strcpy(entry.line_filename, SW_MAX_NAME_LEN, "[NA]");
1257 | }
1258 | } // yes, we have SymGetLineFromAddr64()
1259 |
1260 | // show module info (SymGetModuleInfo64())
1261 | if (ctx->options & SW_OPTIONS_MODULEINFO) {
1262 | sw__get_module_info_csentry(&ctx->internal, ctx->process, (uint64_t)symbols[i], &entry);
1263 | }
1264 |
1265 | memcpy(&entries[count++], &entry, sizeof(sw_callstack_entry));
1266 | }
1267 |
1268 | return count;
1269 | }
1270 |
1271 | SW_API_IMPL void sw_reload_modules(sw_context* ctx)
1272 | {
1273 | SW_ASSERT(ctx);
1274 | ctx->reload_modules = true;
1275 | }
1276 |
1277 | SW_API_IMPL bool sw_get_symbol_module(sw_context* ctx, void* symbol, char module_name[32])
1278 | {
1279 | if (!sw__load_modules(ctx)) {
1280 | return false;
1281 | }
1282 |
1283 | if (ctx->reload_modules) {
1284 | sw__load_modules_internal(&ctx->internal, ctx->process, ctx->process_id);
1285 | ctx->reload_modules = false;
1286 | }
1287 |
1288 | SW_ASSERT(ctx);
1289 | if (ctx->options & SW_OPTIONS_MODULEINFO) {
1290 | IMAGEHLP_MODULE64_V3 modinfo;
1291 | if (!sw__get_module_info(&ctx->internal, ctx->process, (DWORD64)symbol, &modinfo)) {
1292 | SW_LOG_ERROR("SymGetModuleInfo64 failed (ErrorCode=%lu)", GetLastError());
1293 | } else {
1294 | sw__strcpy(module_name, 32, modinfo.ModuleName);
1295 | return true;
1296 | }
1297 | }
1298 | return false;
1299 | }
1300 |
1301 | SW_API_IMPL void sw_set_dbghelp_hintpath(const char* path)
1302 | {
1303 | sw__strcpy(sw__dbghelp_hintpath, sizeof(sw__dbghelp_hintpath), path);
1304 | }
1305 |
1306 | #endif // SW_IMPL
1307 |
--------------------------------------------------------------------------------