├── .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 | --------------------------------------------------------------------------------