diff --git a/CHANGELOG.md b/CHANGELOG.md index 77613bf18c..1e4bfdcebb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ Here we document changes that affect the public API or changes that needs to be communicated to other developers. +## 2018-09-21 +Settings are no longer shared between executables. I.e. The Inviwo app and the integration test will not use the same settings any more. We now prefix the settings with the InviwoApplication display name. This also implies that any existing Inviwo app settings will be lost. To keep old setting one can prefix all the ".ivs" file in the inviwo settings folder with "Inviwo_". On windows the inviwo settings can be found in %APPDATA%/inviwo. + +Added System settings for breaking into the debugger on various log message levels, and on throwing exceptions. Also added an option to add stacktraces to exceptions. All to help with debugging. + +The inviwo app will now catch uncaught inviwo exceptions in main and present an dialog with information and an option to continue or abort. It will also give an option to save your workspace before closing. + +InviwoApplicationQt now has the same order of constructor arguments as InviwoApplication. + ## 2018-08-21 The property class identifier system no longer uses the `InviwoPropertyInfo` / `PropertyClassIdentifier` macros but rather implements ``` diff --git a/apps/inviwo/inviwo.cpp b/apps/inviwo/inviwo.cpp index 188744ccac..ffc85726a0 100644 --- a/apps/inviwo/inviwo.cpp +++ b/apps/inviwo/inviwo.cpp @@ -35,12 +35,15 @@ #include #include #include -#include +#include #include #include #include "inviwosplashscreen.h" #include +#include +#include + #include #include #include @@ -52,7 +55,7 @@ int main(int argc, char** argv) { inviwo::LogCentral::init(&logger); auto logCounter = std::make_shared(); logger.registerLogger(logCounter); - inviwo::InviwoApplicationQt inviwoApp("Inviwo", argc, argv); + inviwo::InviwoApplicationQt inviwoApp(argc, argv, "Inviwo"); inviwoApp.setWindowIcon(QIcon(":/inviwo/inviwo_light.png")); inviwoApp.setAttribute(Qt::AA_NativeWindows); QFile styleSheetFile(":/stylesheets/inviwo.qss"); @@ -107,21 +110,61 @@ int main(int argc, char** argv) { logCounter.reset(); // process last arguments - if (!clp.getQuitApplicationAfterStartup()) { + if (clp.getQuitApplicationAfterStartup()) { + mainWin.exitInviwo(false); + return 0; + } + + while (true) { try { return inviwoApp.exec(); } catch (const inviwo::Exception& e) { - inviwo::util::log(e.getContext(), e.getMessage()); - QMessageBox::critical(nullptr, "Fatal Error", QString::fromStdString(e.getMessage())); + { + inviwo::util::log(e.getContext(), e.getMessage()); + std::stringstream ss; + auto j = inviwo::util::make_ostream_joiner(ss, "\n"); + std::copy(e.getStack().begin(), e.getStack().end(), j); + LogErrorCustom("Inviwo", ss.str()); + } + { + std::stringstream ss; + ss << e.getMessage(); + if (!e.getStack().empty()) { + ss << "\nStack Trace:\n"; + auto j = inviwo::util::make_ostream_joiner(ss, "\n"); + if (std::distance(e.getStack().begin(), e.getStack().end()) > 10) { + std::copy(e.getStack().begin(), e.getStack().begin() + 10, j); + ss << "\n..."; + } else { + std::copy(e.getStack().begin(), e.getStack().end(), j); + } + } + ss << "\nApplication state might be corrupted, be warned."; + auto res = QMessageBox::critical( + &mainWin, "Fatal Error", QString::fromStdString(ss.str()), + QMessageBox::Ignore | QMessageBox::Close, QMessageBox::Close); + if (res == QMessageBox::Close) { + mainWin.askToSaveWorkspaceChanges(); + return 1; + } + } + } catch (const std::exception& e) { - LogErrorCustom("inviwo.cpp", e.what()); - QMessageBox::critical(nullptr, "Fatal Error", e.what()); + LogErrorCustom("Inviwo", e.what()); + std::stringstream ss; + ss << e.what(); + ss << "\nApplication state might be corrupted, be warned."; + auto res = + QMessageBox::critical(&mainWin, "Fatal Error", QString::fromStdString(ss.str()), + QMessageBox::Ignore | QMessageBox::Close, QMessageBox::Close); + if (res == QMessageBox::Close) { + mainWin.askToSaveWorkspaceChanges(); + return 1; + } } catch (...) { - LogErrorCustom("inviwo.cpp", "Uncaught exception, terminating"); + LogErrorCustom("Inviwo", "Uncaught exception, terminating"); QMessageBox::critical(nullptr, "Fatal Error", "Uncaught exception, terminating"); + return 1; } - } else { - mainWin.exitInviwo(false); - return 0; } } diff --git a/apps/minimals/glfw/glfwminimum.cpp b/apps/minimals/glfw/glfwminimum.cpp index 683673d6e1..9dd007a597 100644 --- a/apps/minimals/glfw/glfwminimum.cpp +++ b/apps/minimals/glfw/glfwminimum.cpp @@ -56,7 +56,7 @@ int main(int argc, char** argv) { auto logger = std::make_shared(); LogCentral::getPtr()->registerLogger(logger); - InviwoApplication inviwoApp(argc, argv, "Inviwo v" + IVW_VERSION + " - GLFWApp"); + InviwoApplication inviwoApp(argc, argv, "Inviwo-GLFW"); inviwoApp.printApplicationInfo(); inviwoApp.setPostEnqueueFront([]() { glfwPostEmptyEvent(); }); inviwoApp.setProgressCallback([](std::string m) { diff --git a/apps/minimals/qt/qtminimum.cpp b/apps/minimals/qt/qtminimum.cpp index 9a738b9ae1..9e22c06f04 100644 --- a/apps/minimals/qt/qtminimum.cpp +++ b/apps/minimals/qt/qtminimum.cpp @@ -49,8 +49,7 @@ int main(int argc, char** argv) { auto logger = std::make_shared(); LogCentral::getPtr()->registerLogger(logger); - std::string appName = "Inviwo v" + IVW_VERSION + " - QtApp"; - InviwoApplicationQt inviwoApp(appName, argc, argv); + InviwoApplicationQt inviwoApp(argc, argv, "Inviwo-Qt"); inviwoApp.printApplicationInfo(); inviwoApp.setAttribute(Qt::AA_NativeWindows); inviwoApp.setProgressCallback([](std::string m) { diff --git a/ext/stackwalker/StackWalker.cpp b/ext/stackwalker/StackWalker.cpp index 388f4f878f..7008ac67ea 100644 --- a/ext/stackwalker/StackWalker.cpp +++ b/ext/stackwalker/StackWalker.cpp @@ -1,7 +1,9 @@ /********************************************************************** - * + * * StackWalker.cpp - * http://stackwalker.codeplex.com/ + * https://github.com/JochenKalmbach/StackWalker + * + * Old location: http://stackwalker.codeplex.com/ * * * History: @@ -9,14 +11,14 @@ * http://www.codeproject.com/threads/StackWalker.asp * 2005-07-28 v2 - Changed the params of the constructor and ShowCallstack * (to simplify the usage) - * 2005-08-01 v3 - Changed to use 'CONTEXT_FULL' instead of CONTEXT_ALL + * 2005-08-01 v3 - Changed to use 'CONTEXT_FULL' instead of CONTEXT_ALL * (should also be enough) * - Changed to compile correctly with the PSDK of VC7.0 * (GetFileVersionInfoSizeA and GetFileVersionInfoA is wrongly defined: - * it uses LPSTR instead of LPCSTR as first paremeter) + * it uses LPSTR instead of LPCSTR as first parameter) * - Added declarations to support VC5/6 without using 'dbghelp.h' - * - Added a 'pUserData' member to the ShowCallstack function and the - * PReadProcessMemoryRoutine declaration (to pass some user-defined data, + * - Added a 'pUserData' member to the ShowCallstack function and the + * PReadProcessMemoryRoutine declaration (to pass some user-defined data, * which can be used in the readMemoryFunction-callback) * 2005-08-02 v4 - OnSymInit now also outputs the OS-Version by default * - Added example for doing an exception-callstack-walking in main.cpp @@ -41,7 +43,7 @@ * 2008-11-27 v8 Debugging Tools for Windows are now stored in a different directory * Thanks to Luiz Salamon which reported this "bug"... * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=2822736#xx2822736xx - * 2009-04-10 v9 License slihtly corrected ( replaced) + * 2009-04-10 v9 License slightly corrected ( replaced) * 2009-11-01 v10 Moved to http://stackwalker.codeplex.com/ * 2009-11-02 v11 Now try to use IMAGEHLP_MODULE64_V3 if available * 2010-04-15 v12 Added support for VS2010 RTM @@ -56,147 +58,140 @@ * Copyright (c) 2005-2013, Jochen Kalmbach * All rights reserved. * - * Redistribution and use in source and binary forms, with or without modification, + * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of Jochen Kalmbach nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of Jochen Kalmbach nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * **********************************************************************/ -#include -#include -#include -#include -#pragma comment(lib, "version.lib") // for "VerQueryValue" -#pragma warning(disable:4826) #include "StackWalker.h" +#include +#include +#include +#include +#pragma comment(lib, "version.lib") // for "VerQueryValue" +#pragma warning(disable : 4826) + // If VC7 and later, then use the shipped 'dbghelp.h'-file -#pragma pack(push,8) +#pragma pack(push, 8) #if _MSC_VER >= 1300 #include #else // inline the important dbghelp.h-declarations... -typedef enum { - SymNone = 0, - SymCoff, - SymCv, - SymPdb, - SymExport, - SymDeferred, - SymSym, - SymDia, - SymVirtual, - NumSymTypes +typedef enum +{ + SymNone = 0, + SymCoff, + SymCv, + SymPdb, + SymExport, + SymDeferred, + SymSym, + SymDia, + SymVirtual, + NumSymTypes } SYM_TYPE; -typedef struct _IMAGEHLP_LINE64 { - DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_LINE64) - PVOID Key; // internal - DWORD LineNumber; // line number in file - PCHAR FileName; // full filename - DWORD64 Address; // first instruction of line +typedef struct _IMAGEHLP_LINE64 +{ + DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_LINE64) + PVOID Key; // internal + DWORD LineNumber; // line number in file + PCHAR FileName; // full filename + DWORD64 Address; // first instruction of line } IMAGEHLP_LINE64, *PIMAGEHLP_LINE64; -typedef struct _IMAGEHLP_MODULE64 { - DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64) - DWORD64 BaseOfImage; // base load address of module - DWORD ImageSize; // virtual size of the loaded module - DWORD TimeDateStamp; // date/time stamp from pe header - DWORD CheckSum; // checksum from the pe header - DWORD NumSyms; // number of symbols in the symbol table - SYM_TYPE SymType; // type of symbols loaded - CHAR ModuleName[32]; // module name - CHAR ImageName[256]; // image name - CHAR LoadedImageName[256]; // symbol file name +typedef struct _IMAGEHLP_MODULE64 +{ + DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64) + DWORD64 BaseOfImage; // base load address of module + DWORD ImageSize; // virtual size of the loaded module + DWORD TimeDateStamp; // date/time stamp from pe header + DWORD CheckSum; // checksum from the pe header + DWORD NumSyms; // number of symbols in the symbol table + SYM_TYPE SymType; // type of symbols loaded + CHAR ModuleName[32]; // module name + CHAR ImageName[256]; // image name + CHAR LoadedImageName[256]; // symbol file name } IMAGEHLP_MODULE64, *PIMAGEHLP_MODULE64; -typedef struct _IMAGEHLP_SYMBOL64 { - DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_SYMBOL64) - DWORD64 Address; // virtual address including dll base address - DWORD Size; // estimated size of symbol, can be zero - DWORD Flags; // info about the symbols, see the SYMF defines - DWORD MaxNameLength; // maximum size of symbol name in 'Name' - CHAR Name[1]; // symbol name (null terminated string) +typedef struct _IMAGEHLP_SYMBOL64 +{ + DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_SYMBOL64) + DWORD64 Address; // virtual address including dll base address + DWORD Size; // estimated size of symbol, can be zero + DWORD Flags; // info about the symbols, see the SYMF defines + DWORD MaxNameLength; // maximum size of symbol name in 'Name' + CHAR Name[1]; // symbol name (null terminated string) } IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64; -typedef enum { - AddrMode1616, - AddrMode1632, - AddrModeReal, - AddrModeFlat +typedef enum +{ + AddrMode1616, + AddrMode1632, + AddrModeReal, + AddrModeFlat } ADDRESS_MODE; -typedef struct _tagADDRESS64 { - DWORD64 Offset; - WORD Segment; - ADDRESS_MODE Mode; +typedef struct _tagADDRESS64 +{ + DWORD64 Offset; + WORD Segment; + ADDRESS_MODE Mode; } ADDRESS64, *LPADDRESS64; -typedef struct _KDHELP64 { - DWORD64 Thread; - DWORD ThCallbackStack; - DWORD ThCallbackBStore; - DWORD NextCallback; - DWORD FramePointer; - DWORD64 KiCallUserMode; - DWORD64 KeUserCallbackDispatcher; - DWORD64 SystemRangeStart; - DWORD64 Reserved[8]; +typedef struct _KDHELP64 +{ + DWORD64 Thread; + DWORD ThCallbackStack; + DWORD ThCallbackBStore; + DWORD NextCallback; + DWORD FramePointer; + DWORD64 KiCallUserMode; + DWORD64 KeUserCallbackDispatcher; + DWORD64 SystemRangeStart; + DWORD64 Reserved[8]; } KDHELP64, *PKDHELP64; -typedef struct _tagSTACKFRAME64 { - ADDRESS64 AddrPC; // program counter - ADDRESS64 AddrReturn; // return address - ADDRESS64 AddrFrame; // frame pointer - ADDRESS64 AddrStack; // stack pointer - ADDRESS64 AddrBStore; // backing store pointer - PVOID FuncTableEntry; // pointer to pdata/fpo or NULL - DWORD64 Params[4]; // possible arguments to the function - BOOL Far; // WOW far call - BOOL Virtual; // is this a virtual frame? - DWORD64 Reserved[3]; - KDHELP64 KdHelp; +typedef struct _tagSTACKFRAME64 +{ + ADDRESS64 AddrPC; // program counter + ADDRESS64 AddrReturn; // return address + ADDRESS64 AddrFrame; // frame pointer + ADDRESS64 AddrStack; // stack pointer + ADDRESS64 AddrBStore; // backing store pointer + PVOID FuncTableEntry; // pointer to pdata/fpo or NULL + DWORD64 Params[4]; // possible arguments to the function + BOOL Far; // WOW far call + BOOL Virtual; // is this a virtual frame? + DWORD64 Reserved[3]; + KDHELP64 KdHelp; } STACKFRAME64, *LPSTACKFRAME64; -typedef -BOOL -(__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)( - HANDLE hProcess, - DWORD64 qwBaseAddress, - PVOID lpBuffer, - DWORD nSize, - LPDWORD lpNumberOfBytesRead - ); -typedef -PVOID -(__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)( - HANDLE hProcess, - DWORD64 AddrBase - ); -typedef -DWORD64 -(__stdcall *PGET_MODULE_BASE_ROUTINE64)( - HANDLE hProcess, - DWORD64 Address - ); -typedef -DWORD64 -(__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)( - HANDLE hProcess, - HANDLE hThread, - LPADDRESS64 lpaddr - ); +typedef BOOL(__stdcall* PREAD_PROCESS_MEMORY_ROUTINE64)(HANDLE hProcess, + DWORD64 qwBaseAddress, + PVOID lpBuffer, + DWORD nSize, + LPDWORD lpNumberOfBytesRead); +typedef PVOID(__stdcall* PFUNCTION_TABLE_ACCESS_ROUTINE64)(HANDLE hProcess, DWORD64 AddrBase); +typedef DWORD64(__stdcall* PGET_MODULE_BASE_ROUTINE64)(HANDLE hProcess, DWORD64 Address); +typedef DWORD64(__stdcall* PTRANSLATE_ADDRESS_ROUTINE64)(HANDLE hProcess, + HANDLE hThread, + LPADDRESS64 lpaddr); + +// clang-format off #define SYMOPT_CASE_INSENSITIVE 0x00000001 #define SYMOPT_UNDNAME 0x00000002 #define SYMOPT_DEFERRED_LOADS 0x00000004 @@ -217,16 +212,17 @@ DWORD64 #define SYMOPT_NO_IMAGE_SEARCH 0x00020000 #define SYMOPT_SECURE 0x00040000 #define SYMOPT_DEBUG 0x80000000 -#define UNDNAME_COMPLETE (0x0000) // Enable full undecoration -#define UNDNAME_NAME_ONLY (0x1000) // Crack only the name for primary declaration; -#endif // _MSC_VER < 1300 +#define UNDNAME_COMPLETE (0x0000) // Enable full undecoration +#define UNDNAME_NAME_ONLY (0x1000) // Crack only the name for primary declaration; +// clang-format on + +#endif // _MSC_VER < 1300 #pragma pack(pop) // Some missing defines (for VC5/6): #ifndef INVALID_FILE_ATTRIBUTES #define INVALID_FILE_ATTRIBUTES ((DWORD)-1) -#endif - +#endif // secure-CRT_functions are only available starting with VC8 #if _MSC_VER < 1400 @@ -234,45 +230,26 @@ DWORD64 #define strncpy_s(dst, len, src, maxLen) strncpy(dst, len, src) #define strcat_s(dst, len, src) strcat(dst, src) #define _snprintf_s _snprintf -#define cat_str _tcscat +#define _tcscat_s _tcscat #endif static void MyStrCpy(char* szDest, size_t nMaxDestSize, const char* szSrc) { - if (nMaxDestSize <= 0) return; - if (strlen(szSrc) < nMaxDestSize) - { - strcpy_s(szDest, nMaxDestSize, szSrc); - } - else - { - strncpy_s(szDest, nMaxDestSize, szSrc, nMaxDestSize); - szDest[nMaxDestSize-1] = 0; - } -} // MyStrCpy + if (nMaxDestSize <= 0) + return; + strncpy_s(szDest, nMaxDestSize, szSrc, _TRUNCATE); + // INFO: _TRUNCATE will ensure that it is null-terminated; + // but with older compilers (<1400) it uses "strncpy" and this does not!) + szDest[nMaxDestSize - 1] = 0; +} // MyStrCpy // Normally it should be enough to use 'CONTEXT_FULL' (better would be 'CONTEXT_ALL') #define USED_CONTEXT_FLAGS CONTEXT_FULL -#include - -template -void cat_str(A* dst,B* src){ - if(sizeof(A)==1){ - std::stringstream wss; - wss << dst << src; - memcpy(dst,wss.str().c_str(),wss.str().size()*1); - }else if(sizeof(A)==2){ - std::wstringstream wss; - wss << dst << src; - memcpy(dst,wss.str().c_str(),wss.str().size()*2); - } -} - class StackWalkerInternal { public: - StackWalkerInternal(StackWalker *parent, HANDLE hProcess) + StackWalkerInternal(StackWalker* parent, HANDLE hProcess) { m_parent = parent; m_hDbhHelp = NULL; @@ -295,12 +272,12 @@ class StackWalkerInternal ~StackWalkerInternal() { if (pSC != NULL) - pSC(m_hProcess); // SymCleanup + pSC(m_hProcess); // SymCleanup if (m_hDbhHelp != NULL) FreeLibrary(m_hDbhHelp); m_hDbhHelp = NULL; m_parent = NULL; - if(m_szSymPath != NULL) + if (m_szSymPath != NULL) free(m_szSymPath); m_szSymPath = NULL; } @@ -309,20 +286,20 @@ class StackWalkerInternal if (m_parent == NULL) return FALSE; // Dynamically load the Entry-Points for dbghelp.dll: - // First try to load the newsest one from + // First try to load the newest one from TCHAR szTemp[4096]; - // But before wqe do this, we first check if the ".local" file exists + // But before we do this, we first check if the ".local" file exists if (GetModuleFileName(NULL, szTemp, 4096) > 0) { - cat_str(szTemp, L".local"); + _tcscat_s(szTemp, _T(".local")); if (GetFileAttributes(szTemp) == INVALID_FILE_ATTRIBUTES) { // ".local" file does not exist, so we can try to load the dbghelp.dll from the "Debugging Tools for Windows" - // Ok, first try the new path according to the archtitecture: + // Ok, first try the new path according to the architecture: #ifdef _M_IX86 - if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(L"ProgramFiles", szTemp, 4096) > 0) ) + if ((m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0)) { - cat_str(szTemp, L"\\Debugging Tools for Windows (x86)\\dbghelp.dll"); + _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (x86)\\dbghelp.dll")); // now check if the file exists: if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) { @@ -330,9 +307,9 @@ class StackWalkerInternal } } #elif _M_X64 - if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(L"ProgramFiles", szTemp, 4096) > 0) ) + if ((m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0)) { - cat_str(szTemp, L"\\Debugging Tools for Windows (x64)\\dbghelp.dll"); + _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (x64)\\dbghelp.dll")); // now check if the file exists: if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) { @@ -340,9 +317,9 @@ class StackWalkerInternal } } #elif _M_IA64 - if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(L"ProgramFiles", szTemp, 4096) > 0) ) + if ((m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0)) { - cat_str(szTemp, L"\\Debugging Tools for Windows (ia64)\\dbghelp.dll"); + _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (ia64)\\dbghelp.dll")); // now check if the file exists: if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) { @@ -351,9 +328,9 @@ class StackWalkerInternal } #endif // If still not found, try the old directories... - if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(L"ProgramFiles", szTemp, 4096) > 0) ) + if ((m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0)) { - cat_str(szTemp, L"\\Debugging Tools for Windows\\dbghelp.dll"); + _tcscat_s(szTemp, _T("\\Debugging Tools for Windows\\dbghelp.dll")); // now check if the file exists: if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) { @@ -362,9 +339,9 @@ class StackWalkerInternal } #if defined _M_X64 || defined _M_IA64 // Still not found? Then try to load the (old) 64-Bit version: - if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(L"ProgramFiles", szTemp, 4096) > 0) ) + if ((m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0)) { - cat_str(szTemp, _T("\\Debugging Tools for Windows 64-Bit\\dbghelp.dll")); + _tcscat_s(szTemp, _T("\\Debugging Tools for Windows 64-Bit\\dbghelp.dll")); if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) { m_hDbhHelp = LoadLibrary(szTemp); @@ -373,29 +350,29 @@ class StackWalkerInternal #endif } } - if (m_hDbhHelp == NULL) // if not already loaded, try to load a default-one - m_hDbhHelp = LoadLibrary( L"dbghelp.dll" ); + if (m_hDbhHelp == NULL) // if not already loaded, try to load a default-one + m_hDbhHelp = LoadLibrary(_T("dbghelp.dll")); if (m_hDbhHelp == NULL) return FALSE; - pSI = (tSI) GetProcAddress(m_hDbhHelp, "SymInitialize" ); - pSC = (tSC) GetProcAddress(m_hDbhHelp, "SymCleanup" ); - - pSW = (tSW) GetProcAddress(m_hDbhHelp, "StackWalk64" ); - pSGO = (tSGO) GetProcAddress(m_hDbhHelp, "SymGetOptions" ); - pSSO = (tSSO) GetProcAddress(m_hDbhHelp, "SymSetOptions" ); - - pSFTA = (tSFTA) GetProcAddress(m_hDbhHelp, "SymFunctionTableAccess64" ); - pSGLFA = (tSGLFA) GetProcAddress(m_hDbhHelp, "SymGetLineFromAddr64" ); - pSGMB = (tSGMB) GetProcAddress(m_hDbhHelp, "SymGetModuleBase64" ); - pSGMI = (tSGMI) GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64" ); - pSGSFA = (tSGSFA) GetProcAddress(m_hDbhHelp, "SymGetSymFromAddr64" ); - pUDSN = (tUDSN) GetProcAddress(m_hDbhHelp, "UnDecorateSymbolName" ); - pSLM = (tSLM) GetProcAddress(m_hDbhHelp, "SymLoadModule64" ); - pSGSP =(tSGSP) GetProcAddress(m_hDbhHelp, "SymGetSearchPath" ); - - if ( pSC == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL || - pSGO == NULL || pSGSFA == NULL || pSI == NULL || pSSO == NULL || - pSW == NULL || pUDSN == NULL || pSLM == NULL ) + pSI = (tSI)GetProcAddress(m_hDbhHelp, "SymInitialize"); + pSC = (tSC)GetProcAddress(m_hDbhHelp, "SymCleanup"); + + pSW = (tSW)GetProcAddress(m_hDbhHelp, "StackWalk64"); + pSGO = (tSGO)GetProcAddress(m_hDbhHelp, "SymGetOptions"); + pSSO = (tSSO)GetProcAddress(m_hDbhHelp, "SymSetOptions"); + + pSFTA = (tSFTA)GetProcAddress(m_hDbhHelp, "SymFunctionTableAccess64"); + pSGLFA = (tSGLFA)GetProcAddress(m_hDbhHelp, "SymGetLineFromAddr64"); + pSGMB = (tSGMB)GetProcAddress(m_hDbhHelp, "SymGetModuleBase64"); + pSGMI = (tSGMI)GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64"); + pSGSFA = (tSGSFA)GetProcAddress(m_hDbhHelp, "SymGetSymFromAddr64"); + pUDSN = (tUDSN)GetProcAddress(m_hDbhHelp, "UnDecorateSymbolName"); + pSLM = (tSLM)GetProcAddress(m_hDbhHelp, "SymLoadModule64"); + pSGSP = (tSGSP)GetProcAddress(m_hDbhHelp, "SymGetSearchPath"); + + if (pSC == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL || pSGO == NULL || + pSGSFA == NULL || pSI == NULL || pSSO == NULL || pSW == NULL || pUDSN == NULL || + pSLM == NULL) { FreeLibrary(m_hDbhHelp); m_hDbhHelp = NULL; @@ -408,8 +385,8 @@ class StackWalkerInternal m_szSymPath = _strdup(szSymPath); if (this->pSI(m_hProcess, m_szSymPath, FALSE) == FALSE) this->m_parent->OnDbgHelpErr("SymInitialize", GetLastError(), 0); - - DWORD symOptions = this->pSGO(); // SymGetOptions + + DWORD symOptions = this->pSGO(); // SymGetOptions symOptions |= SYMOPT_LOAD_LINES; symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS; //symOptions |= SYMOPT_NO_PROMPTS; @@ -422,7 +399,7 @@ class StackWalkerInternal if (this->pSGSP(m_hProcess, buf, StackWalker::STACKWALK_MAX_NAMELEN) == FALSE) this->m_parent->OnDbgHelpErr("SymGetSearchPath", GetLastError(), 0); } - char szUserName[1024] = {0}; + char szUserName[1024] = {0}; DWORD dwSize = 1024; GetUserNameA(szUserName, &dwSize); this->m_parent->OnSymInit(buf, symOptions, szUserName); @@ -430,174 +407,185 @@ class StackWalkerInternal return TRUE; } - StackWalker *m_parent; + StackWalker* m_parent; HMODULE m_hDbhHelp; - HANDLE m_hProcess; - LPSTR m_szSymPath; - -#pragma pack(push,8) -typedef struct IMAGEHLP_MODULE64_V3 { - DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64) - DWORD64 BaseOfImage; // base load address of module - DWORD ImageSize; // virtual size of the loaded module - DWORD TimeDateStamp; // date/time stamp from pe header - DWORD CheckSum; // checksum from the pe header - DWORD NumSyms; // number of symbols in the symbol table - SYM_TYPE SymType; // type of symbols loaded - CHAR ModuleName[32]; // module name - CHAR ImageName[256]; // image name - CHAR LoadedImageName[256]; // symbol file name + HANDLE m_hProcess; + LPSTR m_szSymPath; + +#pragma pack(push, 8) + typedef struct IMAGEHLP_MODULE64_V3 + { + DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64) + DWORD64 BaseOfImage; // base load address of module + DWORD ImageSize; // virtual size of the loaded module + DWORD TimeDateStamp; // date/time stamp from pe header + DWORD CheckSum; // checksum from the pe header + DWORD NumSyms; // number of symbols in the symbol table + SYM_TYPE SymType; // type of symbols loaded + CHAR ModuleName[32]; // module name + CHAR ImageName[256]; // image name + CHAR LoadedImageName[256]; // symbol file name // new elements: 07-Jun-2002 - CHAR LoadedPdbName[256]; // pdb file name - DWORD CVSig; // Signature of the CV record in the debug directories - CHAR CVData[MAX_PATH * 3]; // Contents of the CV record - DWORD PdbSig; // Signature of PDB - GUID PdbSig70; // Signature of PDB (VC 7 and up) - DWORD PdbAge; // DBI age of pdb - BOOL PdbUnmatched; // loaded an unmatched pdb - BOOL DbgUnmatched; // loaded an unmatched dbg - BOOL LineNumbers; // we have line number information - BOOL GlobalSymbols; // we have internal symbol information - BOOL TypeInfo; // we have type information + CHAR LoadedPdbName[256]; // pdb file name + DWORD CVSig; // Signature of the CV record in the debug directories + CHAR CVData[MAX_PATH * 3]; // Contents of the CV record + DWORD PdbSig; // Signature of PDB + GUID PdbSig70; // Signature of PDB (VC 7 and up) + DWORD PdbAge; // DBI age of pdb + BOOL PdbUnmatched; // loaded an unmatched pdb + BOOL DbgUnmatched; // loaded an unmatched dbg + BOOL LineNumbers; // we have line number information + BOOL GlobalSymbols; // we have internal symbol information + BOOL TypeInfo; // we have type information // new elements: 17-Dec-2003 - BOOL SourceIndexed; // pdb supports source server - BOOL Publics; // contains public symbols -}; + BOOL SourceIndexed; // pdb supports source server + BOOL Publics; // contains public symbols + }; -typedef struct IMAGEHLP_MODULE64_V2 { - DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64) - DWORD64 BaseOfImage; // base load address of module - DWORD ImageSize; // virtual size of the loaded module - DWORD TimeDateStamp; // date/time stamp from pe header - DWORD CheckSum; // checksum from the pe header - DWORD NumSyms; // number of symbols in the symbol table - SYM_TYPE SymType; // type of symbols loaded - CHAR ModuleName[32]; // module name - CHAR ImageName[256]; // image name - CHAR LoadedImageName[256]; // symbol file name -}; + typedef struct IMAGEHLP_MODULE64_V2 + { + DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64) + DWORD64 BaseOfImage; // base load address of module + DWORD ImageSize; // virtual size of the loaded module + DWORD TimeDateStamp; // date/time stamp from pe header + DWORD CheckSum; // checksum from the pe header + DWORD NumSyms; // number of symbols in the symbol table + SYM_TYPE SymType; // type of symbols loaded + CHAR ModuleName[32]; // module name + CHAR ImageName[256]; // image name + CHAR LoadedImageName[256]; // symbol file name + }; #pragma pack(pop) - // SymCleanup() - typedef BOOL (__stdcall *tSC)( IN HANDLE hProcess ); + typedef BOOL(__stdcall* tSC)(IN HANDLE hProcess); tSC pSC; // SymFunctionTableAccess64() - typedef PVOID (__stdcall *tSFTA)( HANDLE hProcess, DWORD64 AddrBase ); + typedef PVOID(__stdcall* tSFTA)(HANDLE hProcess, DWORD64 AddrBase); tSFTA pSFTA; // SymGetLineFromAddr64() - typedef BOOL (__stdcall *tSGLFA)( IN HANDLE hProcess, IN DWORD64 dwAddr, - OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line ); + typedef BOOL(__stdcall* tSGLFA)(IN HANDLE hProcess, + IN DWORD64 dwAddr, + OUT PDWORD pdwDisplacement, + OUT PIMAGEHLP_LINE64 Line); tSGLFA pSGLFA; // SymGetModuleBase64() - typedef DWORD64 (__stdcall *tSGMB)( IN HANDLE hProcess, IN DWORD64 dwAddr ); + typedef DWORD64(__stdcall* tSGMB)(IN HANDLE hProcess, IN DWORD64 dwAddr); tSGMB pSGMB; // SymGetModuleInfo64() - typedef BOOL (__stdcall *tSGMI)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT IMAGEHLP_MODULE64_V3 *ModuleInfo ); + typedef BOOL(__stdcall* tSGMI)(IN HANDLE hProcess, + IN DWORD64 dwAddr, + OUT IMAGEHLP_MODULE64_V3* ModuleInfo); tSGMI pSGMI; // SymGetOptions() - typedef DWORD (__stdcall *tSGO)( VOID ); + typedef DWORD(__stdcall* tSGO)(VOID); tSGO pSGO; // SymGetSymFromAddr64() - typedef BOOL (__stdcall *tSGSFA)( IN HANDLE hProcess, IN DWORD64 dwAddr, - OUT PDWORD64 pdwDisplacement, OUT PIMAGEHLP_SYMBOL64 Symbol ); + typedef BOOL(__stdcall* tSGSFA)(IN HANDLE hProcess, + IN DWORD64 dwAddr, + OUT PDWORD64 pdwDisplacement, + OUT PIMAGEHLP_SYMBOL64 Symbol); tSGSFA pSGSFA; // SymInitialize() - typedef BOOL (__stdcall *tSI)( IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess ); + typedef BOOL(__stdcall* tSI)(IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess); tSI pSI; // SymLoadModule64() - typedef DWORD64 (__stdcall *tSLM)( IN HANDLE hProcess, IN HANDLE hFile, - IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll ); + typedef DWORD64(__stdcall* tSLM)(IN HANDLE hProcess, + IN HANDLE hFile, + IN PSTR ImageName, + IN PSTR ModuleName, + IN DWORD64 BaseOfDll, + IN DWORD SizeOfDll); tSLM pSLM; // SymSetOptions() - typedef DWORD (__stdcall *tSSO)( IN DWORD SymOptions ); + typedef DWORD(__stdcall* tSSO)(IN DWORD SymOptions); tSSO pSSO; // StackWalk64() - typedef BOOL (__stdcall *tSW)( - DWORD MachineType, - HANDLE hProcess, - HANDLE hThread, - LPSTACKFRAME64 StackFrame, - PVOID ContextRecord, - PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, - PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, - PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, - PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress ); + typedef BOOL(__stdcall* tSW)(DWORD MachineType, + HANDLE hProcess, + HANDLE hThread, + LPSTACKFRAME64 StackFrame, + PVOID ContextRecord, + PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, + PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, + PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, + PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress); tSW pSW; // UnDecorateSymbolName() - typedef DWORD (__stdcall WINAPI *tUDSN)( PCSTR DecoratedName, PSTR UnDecoratedName, - DWORD UndecoratedLength, DWORD Flags ); + typedef DWORD(__stdcall WINAPI* tUDSN)(PCSTR DecoratedName, + PSTR UnDecoratedName, + DWORD UndecoratedLength, + DWORD Flags); tUDSN pUDSN; - typedef BOOL (__stdcall WINAPI *tSGSP)(HANDLE hProcess, PSTR SearchPath, DWORD SearchPathLength); + typedef BOOL(__stdcall WINAPI* tSGSP)(HANDLE hProcess, PSTR SearchPath, DWORD SearchPathLength); tSGSP pSGSP; - private: - // **************************************** ToolHelp32 ************************ - #define MAX_MODULE_NAME32 255 - #define TH32CS_SNAPMODULE 0x00000008 - #pragma pack( push, 8 ) +// **************************************** ToolHelp32 ************************ +#define MAX_MODULE_NAME32 255 +#define TH32CS_SNAPMODULE 0x00000008 +#pragma pack(push, 8) typedef struct tagMODULEENTRY32 { - DWORD dwSize; - DWORD th32ModuleID; // This module - DWORD th32ProcessID; // owning process - DWORD GlblcntUsage; // Global usage count on the module - DWORD ProccntUsage; // Module usage count in th32ProcessID's context - BYTE * modBaseAddr; // Base address of module in th32ProcessID's context - DWORD modBaseSize; // Size in bytes of module starting at modBaseAddr - HMODULE hModule; // The hModule of this module in th32ProcessID's context - char szModule[MAX_MODULE_NAME32 + 1]; - char szExePath[MAX_PATH]; + DWORD dwSize; + DWORD th32ModuleID; // This module + DWORD th32ProcessID; // owning process + DWORD GlblcntUsage; // Global usage count on the module + DWORD ProccntUsage; // Module usage count in th32ProcessID's context + BYTE* modBaseAddr; // Base address of module in th32ProcessID's context + DWORD modBaseSize; // Size in bytes of module starting at modBaseAddr + HMODULE hModule; // The hModule of this module in th32ProcessID's context + char szModule[MAX_MODULE_NAME32 + 1]; + char szExePath[MAX_PATH]; } MODULEENTRY32; - typedef MODULEENTRY32 * PMODULEENTRY32; - typedef MODULEENTRY32 * LPMODULEENTRY32; - #pragma pack( pop ) + typedef MODULEENTRY32* PMODULEENTRY32; + typedef MODULEENTRY32* LPMODULEENTRY32; +#pragma pack(pop) BOOL GetModuleListTH32(HANDLE hProcess, DWORD pid) { // CreateToolhelp32Snapshot() - typedef HANDLE (__stdcall *tCT32S)(DWORD dwFlags, DWORD th32ProcessID); + typedef HANDLE(__stdcall * tCT32S)(DWORD dwFlags, DWORD th32ProcessID); // Module32First() - typedef BOOL (__stdcall *tM32F)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); + typedef BOOL(__stdcall * tM32F)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); // Module32Next() - typedef BOOL (__stdcall *tM32N)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); + typedef BOOL(__stdcall * tM32N)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); // try both dlls... - const TCHAR *dllname[] = { L"kernel32.dll", L"tlhelp32.dll" }; - HINSTANCE hToolhelp = NULL; - tCT32S pCT32S = NULL; - tM32F pM32F = NULL; - tM32N pM32N = NULL; + const TCHAR* dllname[] = {_T("kernel32.dll"), _T("tlhelp32.dll")}; + HINSTANCE hToolhelp = NULL; + tCT32S pCT32S = NULL; + tM32F pM32F = NULL; + tM32N pM32N = NULL; - HANDLE hSnap; + HANDLE hSnap; MODULEENTRY32 me; me.dwSize = sizeof(me); - BOOL keepGoing; + BOOL keepGoing; size_t i; - for (i = 0; i<(sizeof(dllname) / sizeof(dllname[0])); i++ ) + for (i = 0; i < (sizeof(dllname) / sizeof(dllname[0])); i++) { - hToolhelp = LoadLibrary( dllname[i] ); + hToolhelp = LoadLibrary(dllname[i]); if (hToolhelp == NULL) continue; - pCT32S = (tCT32S) GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot"); - pM32F = (tM32F) GetProcAddress(hToolhelp, "Module32First"); - pM32N = (tM32N) GetProcAddress(hToolhelp, "Module32Next"); - if ( (pCT32S != NULL) && (pM32F != NULL) && (pM32N != NULL) ) + pCT32S = (tCT32S)GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot"); + pM32F = (tM32F)GetProcAddress(hToolhelp, "Module32First"); + pM32N = (tM32N)GetProcAddress(hToolhelp, "Module32Next"); + if ((pCT32S != NULL) && (pM32F != NULL) && (pM32N != NULL)) break; // found the functions! FreeLibrary(hToolhelp); hToolhelp = NULL; @@ -606,127 +594,136 @@ typedef struct IMAGEHLP_MODULE64_V2 { if (hToolhelp == NULL) return FALSE; - hSnap = pCT32S( TH32CS_SNAPMODULE, pid ); - if (hSnap == (HANDLE) -1) + hSnap = pCT32S(TH32CS_SNAPMODULE, pid); + if (hSnap == (HANDLE)-1) { FreeLibrary(hToolhelp); return FALSE; } - keepGoing = !!pM32F( hSnap, &me ); + keepGoing = !!pM32F(hSnap, &me); int cnt = 0; while (keepGoing) { - this->LoadModule(hProcess, me.szExePath, me.szModule, (DWORD64) me.modBaseAddr, me.modBaseSize); + this->LoadModule(hProcess, me.szExePath, me.szModule, (DWORD64)me.modBaseAddr, + me.modBaseSize); cnt++; - keepGoing = !!pM32N( hSnap, &me ); + keepGoing = !!pM32N(hSnap, &me); } CloseHandle(hSnap); FreeLibrary(hToolhelp); if (cnt <= 0) return FALSE; return TRUE; - } // GetModuleListTH32 + } // GetModuleListTH32 // **************************************** PSAPI ************************ - typedef struct _MODULEINFO { - LPVOID lpBaseOfDll; - DWORD SizeOfImage; - LPVOID EntryPoint; + typedef struct _MODULEINFO + { + LPVOID lpBaseOfDll; + DWORD SizeOfImage; + LPVOID EntryPoint; } MODULEINFO, *LPMODULEINFO; BOOL GetModuleListPSAPI(HANDLE hProcess) { // EnumProcessModules() - typedef BOOL (__stdcall *tEPM)(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded ); + typedef BOOL(__stdcall * tEPM)(HANDLE hProcess, HMODULE * lphModule, DWORD cb, + LPDWORD lpcbNeeded); // GetModuleFileNameEx() - typedef DWORD (__stdcall *tGMFNE)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize ); + typedef DWORD(__stdcall * tGMFNE)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, + DWORD nSize); // GetModuleBaseName() - typedef DWORD (__stdcall *tGMBN)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize ); + typedef DWORD(__stdcall * tGMBN)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, + DWORD nSize); // GetModuleInformation() - typedef BOOL (__stdcall *tGMI)(HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize ); + typedef BOOL(__stdcall * tGMI)(HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize); HINSTANCE hPsapi; - tEPM pEPM; - tGMFNE pGMFNE; - tGMBN pGMBN; - tGMI pGMI; + tEPM pEPM; + tGMFNE pGMFNE; + tGMBN pGMBN; + tGMI pGMI; DWORD i; //ModuleEntry e; - DWORD cbNeeded; - MODULEINFO mi; - HMODULE *hMods = 0; - char *tt = NULL; - char *tt2 = NULL; + DWORD cbNeeded; + MODULEINFO mi; + HMODULE* hMods = 0; + char* tt = NULL; + char* tt2 = NULL; const SIZE_T TTBUFLEN = 8096; - int cnt = 0; + int cnt = 0; - hPsapi = LoadLibrary( L"psapi.dll" ); + hPsapi = LoadLibrary(_T("psapi.dll")); if (hPsapi == NULL) return FALSE; - pEPM = (tEPM) GetProcAddress( hPsapi, "EnumProcessModules" ); - pGMFNE = (tGMFNE) GetProcAddress( hPsapi, "GetModuleFileNameExA" ); - pGMBN = (tGMFNE) GetProcAddress( hPsapi, "GetModuleBaseNameA" ); - pGMI = (tGMI) GetProcAddress( hPsapi, "GetModuleInformation" ); - if ( (pEPM == NULL) || (pGMFNE == NULL) || (pGMBN == NULL) || (pGMI == NULL) ) + pEPM = (tEPM)GetProcAddress(hPsapi, "EnumProcessModules"); + pGMFNE = (tGMFNE)GetProcAddress(hPsapi, "GetModuleFileNameExA"); + pGMBN = (tGMFNE)GetProcAddress(hPsapi, "GetModuleBaseNameA"); + pGMI = (tGMI)GetProcAddress(hPsapi, "GetModuleInformation"); + if ((pEPM == NULL) || (pGMFNE == NULL) || (pGMBN == NULL) || (pGMI == NULL)) { - // we couldnīt find all functions + // we couldn't find all functions FreeLibrary(hPsapi); return FALSE; } - hMods = (HMODULE*) malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof HMODULE)); - tt = (char*) malloc(sizeof(char) * TTBUFLEN); - tt2 = (char*) malloc(sizeof(char) * TTBUFLEN); - if ( (hMods == NULL) || (tt == NULL) || (tt2 == NULL) ) + hMods = (HMODULE*)malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof(HMODULE))); + tt = (char*)malloc(sizeof(char) * TTBUFLEN); + tt2 = (char*)malloc(sizeof(char) * TTBUFLEN); + if ((hMods == NULL) || (tt == NULL) || (tt2 == NULL)) goto cleanup; - if ( ! pEPM( hProcess, hMods, TTBUFLEN, &cbNeeded ) ) + if (!pEPM(hProcess, hMods, TTBUFLEN, &cbNeeded)) { //_ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle ); goto cleanup; } - if ( cbNeeded > TTBUFLEN ) + if (cbNeeded > TTBUFLEN) { //_ftprintf(fLogFile, _T("%lu: More than %lu module handles. Huh?\n"), g_dwShowCount, lenof( hMods ) ); goto cleanup; } - for ( i = 0; i < cbNeeded / sizeof hMods[0]; i++ ) + for (i = 0; i < cbNeeded / sizeof(hMods[0]); i++) { // base address, size - pGMI(hProcess, hMods[i], &mi, sizeof mi ); + pGMI(hProcess, hMods[i], &mi, sizeof(mi)); // image file name tt[0] = 0; - pGMFNE(hProcess, hMods[i], tt, TTBUFLEN ); + pGMFNE(hProcess, hMods[i], tt, TTBUFLEN); // module name tt2[0] = 0; - pGMBN(hProcess, hMods[i], tt2, TTBUFLEN ); + pGMBN(hProcess, hMods[i], tt2, TTBUFLEN); - DWORD dwRes = this->LoadModule(hProcess, tt, tt2, (DWORD64) mi.lpBaseOfDll, mi.SizeOfImage); + DWORD dwRes = this->LoadModule(hProcess, tt, tt2, (DWORD64)mi.lpBaseOfDll, mi.SizeOfImage); if (dwRes != ERROR_SUCCESS) this->m_parent->OnDbgHelpErr("LoadModule", dwRes, 0); cnt++; } cleanup: - if (hPsapi != NULL) FreeLibrary(hPsapi); - if (tt2 != NULL) free(tt2); - if (tt != NULL) free(tt); - if (hMods != NULL) free(hMods); + if (hPsapi != NULL) + FreeLibrary(hPsapi); + if (tt2 != NULL) + free(tt2); + if (tt != NULL) + free(tt); + if (hMods != NULL) + free(hMods); return cnt != 0; - } // GetModuleListPSAPI + } // GetModuleListPSAPI DWORD LoadModule(HANDLE hProcess, LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size) { - CHAR *szImg = _strdup(img); - CHAR *szMod = _strdup(mod); + CHAR* szImg = _strdup(img); + CHAR* szMod = _strdup(mod); DWORD result = ERROR_SUCCESS; - if ( (szImg == NULL) || (szMod == NULL) ) + if ((szImg == NULL) || (szMod == NULL)) result = ERROR_NOT_ENOUGH_MEMORY; else { @@ -734,14 +731,14 @@ typedef struct IMAGEHLP_MODULE64_V2 { result = GetLastError(); } ULONGLONG fileVersion = 0; - if ( (m_parent != NULL) && (szImg != NULL) ) + if ((m_parent != NULL) && (szImg != NULL)) { - // try to retrive the file-version: - if ( (this->m_parent->m_options & StackWalker::RetrieveFileVersion) != 0) + // try to retrieve the file-version: + if ((this->m_parent->m_options & StackWalker::RetrieveFileVersion) != 0) { - VS_FIXEDFILEINFO *fInfo = NULL; - DWORD dwHandle; - DWORD dwSize = GetFileVersionInfoSizeA(szImg, &dwHandle); + VS_FIXEDFILEINFO* fInfo = NULL; + DWORD dwHandle; + DWORD dwSize = GetFileVersionInfoSizeA(szImg, &dwHandle); if (dwSize > 0) { LPVOID vData = malloc(dwSize); @@ -749,13 +746,14 @@ typedef struct IMAGEHLP_MODULE64_V2 { { if (GetFileVersionInfoA(szImg, dwHandle, dwSize, vData) != 0) { - UINT len; - TCHAR szSubBlock[] = L"\\"; - if (VerQueryValue(vData, szSubBlock, (LPVOID*) &fInfo, &len) == 0) + UINT len; + TCHAR szSubBlock[] = _T("\\"); + if (VerQueryValue(vData, szSubBlock, (LPVOID*)&fInfo, &len) == 0) fInfo = NULL; else { - fileVersion = ((ULONGLONG)fInfo->dwFileVersionLS) + ((ULONGLONG)fInfo->dwFileVersionMS << 32); + fileVersion = + ((ULONGLONG)fInfo->dwFileVersionLS) + ((ULONGLONG)fInfo->dwFileVersionMS << 32); } } free(vData); @@ -763,32 +761,32 @@ typedef struct IMAGEHLP_MODULE64_V2 { } } - // Retrive some additional-infos about the module + // Retrieve some additional-infos about the module IMAGEHLP_MODULE64_V3 Module; - const char *szSymType = "-unknown-"; + const char* szSymType = "-unknown-"; if (this->GetModuleInfo(hProcess, baseAddr, &Module) != FALSE) { - switch(Module.SymType) + switch (Module.SymType) { case SymNone: szSymType = "-nosymbols-"; break; - case SymCoff: // 1 + case SymCoff: // 1 szSymType = "COFF"; break; - case SymCv: // 2 + case SymCv: // 2 szSymType = "CV"; break; - case SymPdb: // 3 + case SymPdb: // 3 szSymType = "PDB"; break; - case SymExport: // 4 + case SymExport: // 4 szSymType = "-exported-"; break; - case SymDeferred: // 5 + case SymDeferred: // 5 szSymType = "-deferred-"; break; - case SymSym: // 6 + case SymSym: // 6 szSymType = "SYM"; break; case 7: // SymDia: @@ -802,12 +800,16 @@ typedef struct IMAGEHLP_MODULE64_V2 { LPCSTR pdbName = Module.LoadedImageName; if (Module.LoadedPdbName[0] != 0) pdbName = Module.LoadedPdbName; - this->m_parent->OnLoadModule(img, mod, baseAddr, size, result, szSymType, pdbName, fileVersion); + this->m_parent->OnLoadModule(img, mod, baseAddr, size, result, szSymType, pdbName, + fileVersion); } - if (szImg != NULL) free(szImg); - if (szMod != NULL) free(szMod); + if (szImg != NULL) + free(szImg); + if (szMod != NULL) + free(szMod); return result; } + public: BOOL LoadModules(HANDLE hProcess, DWORD dwProcessId) { @@ -818,18 +820,18 @@ typedef struct IMAGEHLP_MODULE64_V2 { return GetModuleListPSAPI(hProcess); } - - BOOL GetModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULE64_V3 *pModuleInfo) + BOOL GetModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULE64_V3* pModuleInfo) { memset(pModuleInfo, 0, sizeof(IMAGEHLP_MODULE64_V3)); - if(this->pSGMI == NULL) + if (this->pSGMI == NULL) { SetLastError(ERROR_DLL_INIT_FAILED); return FALSE; } // First try to use the larger ModuleInfo-Structure pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V3); - void *pData = malloc(4096); // reserve enough memory, so the bug in v6.3.5.1 does not lead to memory-overwrites... + void* pData = malloc( + 4096); // reserve enough memory, so the bug in v6.3.5.1 does not lead to memory-overwrites... if (pData == NULL) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); @@ -839,7 +841,7 @@ typedef struct IMAGEHLP_MODULE64_V2 { static bool s_useV3Version = true; if (s_useV3Version) { - if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V3*) pData) != FALSE) + if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V3*)pData) != FALSE) { // only copy as much memory as is reserved... memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V3)); @@ -847,13 +849,13 @@ typedef struct IMAGEHLP_MODULE64_V2 { free(pData); return TRUE; } - s_useV3Version = false; // to prevent unneccessarry calls with the larger struct... + s_useV3Version = false; // to prevent unnecessary calls with the larger struct... } - // could not retrive the bigger structure, try with the smaller one (as defined in VC7.1)... + // could not retrieve the bigger structure, try with the smaller one (as defined in VC7.1)... pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2); memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64_V2)); - if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V3*) pData) != FALSE) + if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V3*)pData) != FALSE) { // only copy as much memory as is reserved... memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V2)); @@ -916,11 +918,11 @@ BOOL StackWalker::LoadModules() return TRUE; // Build the sym-path: - char *szSymPath = NULL; - if ( (this->m_options & SymBuildPath) != 0) + char* szSymPath = NULL; + if ((this->m_options & SymBuildPath) != 0) { const size_t nSymPathLen = 4096; - szSymPath = (char*) malloc(nSymPathLen); + szSymPath = (char*)malloc(nSymPathLen); if (szSymPath == NULL) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); @@ -937,11 +939,11 @@ BOOL StackWalker::LoadModules() strcat_s(szSymPath, nSymPathLen, ".;"); const size_t nTempLen = 1024; - char szTemp[nTempLen]; + char szTemp[nTempLen]; // Now add the current directory: if (GetCurrentDirectoryA(nTempLen, szTemp) > 0) { - szTemp[nTempLen-1] = 0; + szTemp[nTempLen - 1] = 0; strcat_s(szSymPath, nSymPathLen, szTemp); strcat_s(szSymPath, nSymPathLen, ";"); } @@ -949,16 +951,16 @@ BOOL StackWalker::LoadModules() // Now add the path for the main-module: if (GetModuleFileNameA(NULL, szTemp, nTempLen) > 0) { - szTemp[nTempLen-1] = 0; - for (char *p = (szTemp+strlen(szTemp)-1); p >= szTemp; --p) + szTemp[nTempLen - 1] = 0; + for (char* p = (szTemp + strlen(szTemp) - 1); p >= szTemp; --p) { // locate the rightmost path separator - if ( (*p == '\\') || (*p == '/') || (*p == ':') ) + if ((*p == '\\') || (*p == '/') || (*p == ':')) { *p = 0; break; } - } // for (search for path separator...) + } // for (search for path separator...) if (strlen(szTemp) > 0) { strcat_s(szSymPath, nSymPathLen, szTemp); @@ -967,19 +969,19 @@ BOOL StackWalker::LoadModules() } if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", szTemp, nTempLen) > 0) { - szTemp[nTempLen-1] = 0; + szTemp[nTempLen - 1] = 0; strcat_s(szSymPath, nSymPathLen, szTemp); strcat_s(szSymPath, nSymPathLen, ";"); } if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", szTemp, nTempLen) > 0) { - szTemp[nTempLen-1] = 0; + szTemp[nTempLen - 1] = 0; strcat_s(szSymPath, nSymPathLen, szTemp); strcat_s(szSymPath, nSymPathLen, ";"); } if (GetEnvironmentVariableA("SYSTEMROOT", szTemp, nTempLen) > 0) { - szTemp[nTempLen-1] = 0; + szTemp[nTempLen - 1] = 0; strcat_s(szSymPath, nSymPathLen, szTemp); strcat_s(szSymPath, nSymPathLen, ";"); // also add the "system32"-directory: @@ -988,24 +990,27 @@ BOOL StackWalker::LoadModules() strcat_s(szSymPath, nSymPathLen, ";"); } - if ( (this->m_options & SymUseSymSrv) != 0) + if ((this->m_options & SymUseSymSrv) != 0) { if (GetEnvironmentVariableA("SYSTEMDRIVE", szTemp, nTempLen) > 0) { - szTemp[nTempLen-1] = 0; + szTemp[nTempLen - 1] = 0; strcat_s(szSymPath, nSymPathLen, "SRV*"); strcat_s(szSymPath, nSymPathLen, szTemp); strcat_s(szSymPath, nSymPathLen, "\\websymbols"); strcat_s(szSymPath, nSymPathLen, "*http://msdl.microsoft.com/download/symbols;"); } else - strcat_s(szSymPath, nSymPathLen, "SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols;"); + strcat_s(szSymPath, nSymPathLen, + "SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols;"); } - } // if SymBuildPath + } // if SymBuildPath // First Init the whole stuff... BOOL bRet = this->m_sw->Init(szSymPath); - if (szSymPath != NULL) free(szSymPath); szSymPath = NULL; + if (szSymPath != NULL) + free(szSymPath); + szSymPath = NULL; if (bRet == FALSE) { this->OnDbgHelpErr("Error while initializing dbghelp.dll", 0, 0); @@ -1019,27 +1024,29 @@ BOOL StackWalker::LoadModules() return bRet; } - // The following is used to pass the "userData"-Pointer to the user-provided readMemoryFunction // This has to be done due to a problem with the "hProcess"-parameter in x64... -// Because this class is in no case multi-threading-enabled (because of the limitations +// Because this class is in no case multi-threading-enabled (because of the limitations // of dbghelp.dll) it is "safe" to use a static-variable static StackWalker::PReadProcessMemoryRoutine s_readMemoryFunction = NULL; -static LPVOID s_readMemoryFunction_UserData = NULL; +static LPVOID s_readMemoryFunction_UserData = NULL; -BOOL StackWalker::ShowCallstack(HANDLE hThread, const CONTEXT *context, PReadProcessMemoryRoutine readMemoryFunction, LPVOID pUserData) +BOOL StackWalker::ShowCallstack(HANDLE hThread, + const CONTEXT* context, + PReadProcessMemoryRoutine readMemoryFunction, + LPVOID pUserData) { - CONTEXT c; - CallstackEntry csEntry; - IMAGEHLP_SYMBOL64 *pSym = NULL; + CONTEXT c; + CallstackEntry csEntry; + IMAGEHLP_SYMBOL64* pSym = NULL; StackWalkerInternal::IMAGEHLP_MODULE64_V3 Module; - IMAGEHLP_LINE64 Line; - int frameNum; - bool bLastEntryCalled = true; - int curRecursionCount = 0; + IMAGEHLP_LINE64 Line; + int frameNum; + bool bLastEntryCalled = true; + int curRecursionCount = 0; if (m_modulesLoaded == FALSE) - this->LoadModules(); // ignore the result... + this->LoadModules(); // ignore the result... if (this->m_sw->m_hDbhHelp == NULL) { @@ -1053,15 +1060,26 @@ BOOL StackWalker::ShowCallstack(HANDLE hThread, const CONTEXT *context, PReadPro if (context == NULL) { // If no context is provided, capture the context + // See: https://stackwalker.codeplex.com/discussions/446958 +#if _WIN32_WINNT <= 0x0501 + // If we need to support XP, we need to use the "old way", because "GetThreadId" is not available! if (hThread == GetCurrentThread()) +#else + if (GetThreadId(hThread) == GetCurrentThreadId()) +#endif { - GET_CURRENT_CONTEXT(c, USED_CONTEXT_FLAGS); + GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, USED_CONTEXT_FLAGS); } else { SuspendThread(hThread); memset(&c, 0, sizeof(CONTEXT)); c.ContextFlags = USED_CONTEXT_FLAGS; + + // TODO: Detect if you want to get a thread context of a different process, which is running a different processor architecture... + // This does only work if we are x64 and the target process is x64 or x86; + // It cannot work, if this process is x64 and the target process is x64... this is not supported... + // See also: http://www.howzatt.demon.co.uk/articles/DebuggingInWin64.html if (GetThreadContext(hThread, &c) == FALSE) { ResumeThread(hThread); @@ -1107,8 +1125,9 @@ BOOL StackWalker::ShowCallstack(HANDLE hThread, const CONTEXT *context, PReadPro #error "Platform not supported!" #endif - pSym = (IMAGEHLP_SYMBOL64 *) malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN); - if (!pSym) goto cleanup; // not enough memory... + pSym = (IMAGEHLP_SYMBOL64*)malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN); + if (!pSym) + goto cleanup; // not enough memory... memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN); pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); pSym->MaxNameLength = STACKWALK_MAX_NAMELEN; @@ -1119,14 +1138,15 @@ BOOL StackWalker::ShowCallstack(HANDLE hThread, const CONTEXT *context, PReadPro memset(&Module, 0, sizeof(Module)); Module.SizeOfStruct = sizeof(Module); - for (frameNum = 0; ; ++frameNum ) + for (frameNum = 0;; ++frameNum) { // get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64()) // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can // assume that either you are done, or that the stack is so hosed that the next // deeper frame could not be found. - // CONTEXT need not to be suplied if imageTyp is IMAGE_FILE_MACHINE_I386! - if ( ! this->m_sw->pSW(imageType, this->m_hProcess, hThread, &s, &c, myReadProcMem, this->m_sw->pSFTA, this->m_sw->pSGMB, NULL) ) + // CONTEXT need not to be supplied if imageTyp is IMAGE_FILE_MACHINE_I386! + if (!this->m_sw->pSW(imageType, this->m_hProcess, hThread, &s, &c, myReadProcMem, + this->m_sw->pSFTA, this->m_sw->pSGMB, NULL)) { // INFO: "StackWalk64" does not set "GetLastError"... this->OnDbgHelpErr("StackWalk64", 0, s.AddrPC.Offset); @@ -1145,7 +1165,7 @@ BOOL StackWalker::ShowCallstack(HANDLE hThread, const CONTEXT *context, PReadPro csEntry.moduleName[0] = 0; if (s.AddrPC.Offset == s.AddrReturn.Offset) { - if ( (this->m_MaxRecursionCount > 0) && (curRecursionCount > m_MaxRecursionCount) ) + if ((this->m_MaxRecursionCount > 0) && (curRecursionCount > m_MaxRecursionCount)) { this->OnDbgHelpErr("StackWalk64-Endless-Callstack!", 0, s.AddrPC.Offset); break; @@ -1158,12 +1178,13 @@ BOOL StackWalker::ShowCallstack(HANDLE hThread, const CONTEXT *context, PReadPro { // we seem to have a valid PC // show procedure info (SymGetSymFromAddr64()) - if (this->m_sw->pSGSFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromSmybol), pSym) != FALSE) + if (this->m_sw->pSGSFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromSmybol), + pSym) != FALSE) { MyStrCpy(csEntry.name, STACKWALK_MAX_NAMELEN, pSym->Name); // UnDecorateSymbolName() - this->m_sw->pUDSN( pSym->Name, csEntry.undName, STACKWALK_MAX_NAMELEN, UNDNAME_NAME_ONLY ); - this->m_sw->pUDSN( pSym->Name, csEntry.undFullName, STACKWALK_MAX_NAMELEN, UNDNAME_COMPLETE ); + this->m_sw->pUDSN(pSym->Name, csEntry.undName, STACKWALK_MAX_NAMELEN, UNDNAME_NAME_ONLY); + this->m_sw->pUDSN(pSym->Name, csEntry.undFullName, STACKWALK_MAX_NAMELEN, UNDNAME_COMPLETE); } else { @@ -1171,9 +1192,10 @@ BOOL StackWalker::ShowCallstack(HANDLE hThread, const CONTEXT *context, PReadPro } // show line number info, NT5.0-method (SymGetLineFromAddr64()) - if (this->m_sw->pSGLFA != NULL ) + if (this->m_sw->pSGLFA != NULL) { // yes, we have SymGetLineFromAddr64() - if (this->m_sw->pSGLFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromLine), &Line) != FALSE) + if (this->m_sw->pSGLFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromLine), + &Line) != FALSE) { csEntry.lineNumber = Line.LineNumber; MyStrCpy(csEntry.lineFileName, STACKWALK_MAX_NAMELEN, Line.FileName); @@ -1185,43 +1207,43 @@ BOOL StackWalker::ShowCallstack(HANDLE hThread, const CONTEXT *context, PReadPro } // yes, we have SymGetLineFromAddr64() // show module info (SymGetModuleInfo64()) - if (this->m_sw->GetModuleInfo(this->m_hProcess, s.AddrPC.Offset, &Module ) != FALSE) + if (this->m_sw->GetModuleInfo(this->m_hProcess, s.AddrPC.Offset, &Module) != FALSE) { // got module info OK - switch ( Module.SymType ) + switch (Module.SymType) { - case SymNone: - csEntry.symTypeString = "-nosymbols-"; - break; - case SymCoff: - csEntry.symTypeString = "COFF"; - break; - case SymCv: - csEntry.symTypeString = "CV"; - break; - case SymPdb: - csEntry.symTypeString = "PDB"; - break; - case SymExport: - csEntry.symTypeString = "-exported-"; - break; - case SymDeferred: - csEntry.symTypeString = "-deferred-"; - break; - case SymSym: - csEntry.symTypeString = "SYM"; - break; + case SymNone: + csEntry.symTypeString = "-nosymbols-"; + break; + case SymCoff: + csEntry.symTypeString = "COFF"; + break; + case SymCv: + csEntry.symTypeString = "CV"; + break; + case SymPdb: + csEntry.symTypeString = "PDB"; + break; + case SymExport: + csEntry.symTypeString = "-exported-"; + break; + case SymDeferred: + csEntry.symTypeString = "-deferred-"; + break; + case SymSym: + csEntry.symTypeString = "SYM"; + break; #if API_VERSION_NUMBER >= 9 - case SymDia: - csEntry.symTypeString = "DIA"; - break; + case SymDia: + csEntry.symTypeString = "DIA"; + break; #endif - case 8: //SymVirtual: - csEntry.symTypeString = "Virtual"; - break; - default: - //_snprintf( ty, sizeof ty, "symtype=%ld", (long) Module.SymType ); - csEntry.symTypeString = NULL; - break; + case 8: //SymVirtual: + csEntry.symTypeString = "Virtual"; + break; + default: + //_snprintf( ty, sizeof(ty), "symtype=%ld", (long) Module.SymType ); + csEntry.symTypeString = NULL; + break; } MyStrCpy(csEntry.moduleName, STACKWALK_MAX_NAMELEN, Module.ModuleName); @@ -1239,7 +1261,7 @@ BOOL StackWalker::ShowCallstack(HANDLE hThread, const CONTEXT *context, PReadPro et = firstEntry; bLastEntryCalled = false; this->OnCallstackEntry(et, csEntry); - + if (s.AddrReturn.Offset == 0) { bLastEntryCalled = true; @@ -1249,11 +1271,12 @@ BOOL StackWalker::ShowCallstack(HANDLE hThread, const CONTEXT *context, PReadPro } } // for ( frameNum ) - cleanup: - if (pSym) free( pSym ); +cleanup: + if (pSym) + free(pSym); if (bLastEntryCalled == false) - this->OnCallstackEntry(lastEntry, csEntry); + this->OnCallstackEntry(lastEntry, csEntry); if (context == NULL) ResumeThread(hThread); @@ -1261,48 +1284,104 @@ BOOL StackWalker::ShowCallstack(HANDLE hThread, const CONTEXT *context, PReadPro return TRUE; } -BOOL __stdcall StackWalker::myReadProcMem( - HANDLE hProcess, - DWORD64 qwBaseAddress, - PVOID lpBuffer, - DWORD nSize, - LPDWORD lpNumberOfBytesRead - ) +BOOL StackWalker::ShowObject(LPVOID pObject) +{ + // Load modules if not done yet + if (m_modulesLoaded == FALSE) + this->LoadModules(); // ignore the result... + + // Verify that the DebugHelp.dll was actually found + if (this->m_sw->m_hDbhHelp == NULL) + { + SetLastError(ERROR_DLL_INIT_FAILED); + return FALSE; + } + + // SymGetSymFromAddr64() is required + if (this->m_sw->pSGSFA == NULL) + return FALSE; + + // Show object info (SymGetSymFromAddr64()) + DWORD64 dwAddress = DWORD64(pObject); + DWORD64 dwDisplacement = 0; + IMAGEHLP_SYMBOL64* pSym = + (IMAGEHLP_SYMBOL64*)malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN); + memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN); + pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); + pSym->MaxNameLength = STACKWALK_MAX_NAMELEN; + if (this->m_sw->pSGSFA(this->m_hProcess, dwAddress, &dwDisplacement, pSym) == FALSE) + { + this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), dwAddress); + return FALSE; + } + // Object name output + this->OnOutput(pSym->Name); + + free(pSym); + return TRUE; +}; + +BOOL __stdcall StackWalker::myReadProcMem(HANDLE hProcess, + DWORD64 qwBaseAddress, + PVOID lpBuffer, + DWORD nSize, + LPDWORD lpNumberOfBytesRead) { if (s_readMemoryFunction == NULL) { SIZE_T st; - BOOL bRet = ReadProcessMemory(hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, &st); - *lpNumberOfBytesRead = (DWORD) st; + BOOL bRet = ReadProcessMemory(hProcess, (LPVOID)qwBaseAddress, lpBuffer, nSize, &st); + *lpNumberOfBytesRead = (DWORD)st; //printf("ReadMemory: hProcess: %p, baseAddr: %p, buffer: %p, size: %d, read: %d, result: %d\n", hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, (DWORD) st, (DWORD) bRet); return bRet; } else { - return s_readMemoryFunction(hProcess, qwBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead, s_readMemoryFunction_UserData); + return s_readMemoryFunction(hProcess, qwBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead, + s_readMemoryFunction_UserData); } } -void StackWalker::OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion) +void StackWalker::OnLoadModule(LPCSTR img, + LPCSTR mod, + DWORD64 baseAddr, + DWORD size, + DWORD result, + LPCSTR symType, + LPCSTR pdbName, + ULONGLONG fileVersion) { - CHAR buffer[STACKWALK_MAX_NAMELEN]; + CHAR buffer[STACKWALK_MAX_NAMELEN]; + size_t maxLen = STACKWALK_MAX_NAMELEN; +#if _MSC_VER >= 1400 + maxLen = _TRUNCATE; +#endif if (fileVersion == 0) - _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s'\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName); + _snprintf_s(buffer, maxLen, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s'\n", + img, mod, (LPVOID)baseAddr, size, result, symType, pdbName); else { - DWORD v4 = (DWORD) (fileVersion & 0xFFFF); - DWORD v3 = (DWORD) ((fileVersion>>16) & 0xFFFF); - DWORD v2 = (DWORD) ((fileVersion>>32) & 0xFFFF); - DWORD v1 = (DWORD) ((fileVersion>>48) & 0xFFFF); - _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s', fileVersion: %d.%d.%d.%d\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName, v1, v2, v3, v4); + DWORD v4 = (DWORD)(fileVersion & 0xFFFF); + DWORD v3 = (DWORD)((fileVersion >> 16) & 0xFFFF); + DWORD v2 = (DWORD)((fileVersion >> 32) & 0xFFFF); + DWORD v1 = (DWORD)((fileVersion >> 48) & 0xFFFF); + _snprintf_s( + buffer, maxLen, + "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s', fileVersion: %d.%d.%d.%d\n", + img, mod, (LPVOID)baseAddr, size, result, symType, pdbName, v1, v2, v3, v4); } + buffer[STACKWALK_MAX_NAMELEN - 1] = 0; // be sure it is NULL terminated OnOutput(buffer); } -void StackWalker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry) +void StackWalker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry& entry) { - CHAR buffer[STACKWALK_MAX_NAMELEN]; - if ( (eType != lastEntry) && (entry.offset != 0) ) + CHAR buffer[STACKWALK_MAX_NAMELEN]; + size_t maxLen = STACKWALK_MAX_NAMELEN; +#if _MSC_VER >= 1400 + maxLen = _TRUNCATE; +#endif + if ((eType != lastEntry) && (entry.offset != 0)) { if (entry.name[0] == 0) MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, "(function-name not available)"); @@ -1315,26 +1394,40 @@ void StackWalker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &ent MyStrCpy(entry.lineFileName, STACKWALK_MAX_NAMELEN, "(filename not available)"); if (entry.moduleName[0] == 0) MyStrCpy(entry.moduleName, STACKWALK_MAX_NAMELEN, "(module-name not available)"); - _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%p (%s): %s: %s\n", (LPVOID) entry.offset, entry.moduleName, entry.lineFileName, entry.name); + _snprintf_s(buffer, maxLen, "%p (%s): %s: %s\n", (LPVOID)entry.offset, entry.moduleName, + entry.lineFileName, entry.name); } else - _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s (%d): %s\n", entry.lineFileName, entry.lineNumber, entry.name); - buffer[STACKWALK_MAX_NAMELEN-1] = 0; + _snprintf_s(buffer, maxLen, "%s (%d): %s\n", entry.lineFileName, entry.lineNumber, + entry.name); + buffer[STACKWALK_MAX_NAMELEN - 1] = 0; OnOutput(buffer); } } void StackWalker::OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) { - CHAR buffer[STACKWALK_MAX_NAMELEN]; - _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "ERROR: %s, GetLastError: %d (Address: %p)\n", szFuncName, gle, (LPVOID) addr); + CHAR buffer[STACKWALK_MAX_NAMELEN]; + size_t maxLen = STACKWALK_MAX_NAMELEN; +#if _MSC_VER >= 1400 + maxLen = _TRUNCATE; +#endif + _snprintf_s(buffer, maxLen, "ERROR: %s, GetLastError: %d (Address: %p)\n", szFuncName, gle, + (LPVOID)addr); + buffer[STACKWALK_MAX_NAMELEN - 1] = 0; OnOutput(buffer); } void StackWalker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName) { - CHAR buffer[STACKWALK_MAX_NAMELEN]; - _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n", szSearchPath, symOptions, szUserName); + CHAR buffer[STACKWALK_MAX_NAMELEN]; + size_t maxLen = STACKWALK_MAX_NAMELEN; +#if _MSC_VER >= 1400 + maxLen = _TRUNCATE; +#endif + _snprintf_s(buffer, maxLen, "SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n", + szSearchPath, symOptions, szUserName); + buffer[STACKWALK_MAX_NAMELEN - 1] = 0; OnOutput(buffer); // Also display the OS-version #if _MSC_VER <= 1200 @@ -1343,22 +1436,30 @@ void StackWalker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUser ver.dwOSVersionInfoSize = sizeof(ver); if (GetVersionExA(&ver) != FALSE) { - _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "OS-Version: %d.%d.%d (%s)\n", - ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber, - ver.szCSDVersion); + _snprintf_s(buffer, maxLen, "OS-Version: %d.%d.%d (%s)\n", ver.dwMajorVersion, + ver.dwMinorVersion, ver.dwBuildNumber, ver.szCSDVersion); + buffer[STACKWALK_MAX_NAMELEN - 1] = 0; OnOutput(buffer); } #else OSVERSIONINFOEXA ver; ZeroMemory(&ver, sizeof(OSVERSIONINFOEXA)); ver.dwOSVersionInfoSize = sizeof(ver); - if (GetVersionExA( (OSVERSIONINFOA*) &ver) != FALSE) +#if _MSC_VER >= 1900 +#pragma warning(push) +#pragma warning(disable : 4996) +#endif + if (GetVersionExA((OSVERSIONINFOA*)&ver) != FALSE) { - _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "OS-Version: %d.%d.%d (%s) 0x%x-0x%x\n", - ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber, - ver.szCSDVersion, ver.wSuiteMask, ver.wProductType); + _snprintf_s(buffer, maxLen, "OS-Version: %d.%d.%d (%s) 0x%x-0x%x\n", ver.dwMajorVersion, + ver.dwMinorVersion, ver.dwBuildNumber, ver.szCSDVersion, ver.wSuiteMask, + ver.wProductType); + buffer[STACKWALK_MAX_NAMELEN - 1] = 0; OnOutput(buffer); } +#if _MSC_VER >= 1900 +#pragma warning(pop) +#endif #endif } diff --git a/ext/stackwalker/StackWalker.h b/ext/stackwalker/StackWalker.h index 8b01c0214c..53a00dce0b 100644 --- a/ext/stackwalker/StackWalker.h +++ b/ext/stackwalker/StackWalker.h @@ -1,5 +1,10 @@ +#ifndef __STACKWALKER_H__ +#define __STACKWALKER_H__ + +#if defined(_MSC_VER) + /********************************************************************** - * + * * StackWalker.h * * @@ -9,30 +14,30 @@ * Copyright (c) 2005-2009, Jochen Kalmbach * All rights reserved. * - * Redistribution and use in source and binary forms, with or without modification, + * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of Jochen Kalmbach nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of Jochen Kalmbach nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * **********************************************************************/ -// #pragma once is supported starting with _MCS_VER 1000, +// #pragma once is supported starting with _MSC_VER 1000, // so we need not to check the version (because we only support _MSC_VER >= 1100)! #pragma once @@ -44,6 +49,10 @@ #endif #include +#if _MSC_VER >= 1900 +#pragma warning(disable : 4091) +#endif + // special defines for VC5/6 (if no actual PSDK is installed): #if _MSC_VER < 1300 typedef unsigned __int64 DWORD64, *PDWORD64; @@ -52,8 +61,7 @@ typedef unsigned __int64 SIZE_T, *PSIZE_T; #else typedef unsigned long SIZE_T, *PSIZE_T; #endif -#endif // _MSC_VER < 1300 - +#endif // _MSC_VER < 1300 #ifdef INVIWO_ALL_DYN_LINK //DYNAMIC @@ -83,119 +91,137 @@ class IVW_STACKTRACE_API StackWalker public: typedef enum StackWalkOptions { - // No addition info will be retrived + // No addition info will be retrieved // (only the address is available) RetrieveNone = 0, - + // Try to get the symbol-name RetrieveSymbol = 1, - + // Try to get the line for this symbol RetrieveLine = 2, - + // Try to retrieve the module-infos RetrieveModuleInfo = 4, - + // Also retrieve the version for the DLL/EXE RetrieveFileVersion = 8, - - // Contains all the abouve + + // Contains all the above RetrieveVerbose = 0xF, - + // Generate a "good" symbol-search-path SymBuildPath = 0x10, - + // Also use the public Microsoft-Symbol-Server SymUseSymSrv = 0x20, - - // Contains all the abouve "Sym"-options + + // Contains all the above "Sym"-options SymAll = 0x30, - + // Contains all options (default) OptionsAll = 0x3F } StackWalkOptions; - StackWalker( - int options = OptionsAll, // 'int' is by design, to combine the enum-flags - LPCSTR szSymPath = NULL, - DWORD dwProcessId = GetCurrentProcessId(), - HANDLE hProcess = GetCurrentProcess() - ); + StackWalker(int options = OptionsAll, // 'int' is by design, to combine the enum-flags + LPCSTR szSymPath = NULL, + DWORD dwProcessId = GetCurrentProcessId(), + HANDLE hProcess = GetCurrentProcess()); StackWalker(DWORD dwProcessId, HANDLE hProcess); virtual ~StackWalker(); - typedef BOOL (__stdcall *PReadProcessMemoryRoutine)( - HANDLE hProcess, - DWORD64 qwBaseAddress, - PVOID lpBuffer, - DWORD nSize, - LPDWORD lpNumberOfBytesRead, - LPVOID pUserData // optional data, which was passed in "ShowCallstack" - ); + typedef BOOL(__stdcall* PReadProcessMemoryRoutine)( + HANDLE hProcess, + DWORD64 qwBaseAddress, + PVOID lpBuffer, + DWORD nSize, + LPDWORD lpNumberOfBytesRead, + LPVOID pUserData // optional data, which was passed in "ShowCallstack" + ); BOOL LoadModules(); BOOL ShowCallstack( - HANDLE hThread = GetCurrentThread(), - const CONTEXT *context = NULL, - PReadProcessMemoryRoutine readMemoryFunction = NULL, - LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback - ); + HANDLE hThread = GetCurrentThread(), + const CONTEXT* context = NULL, + PReadProcessMemoryRoutine readMemoryFunction = NULL, + LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback + ); + + BOOL ShowObject(LPVOID pObject); #if _MSC_VER >= 1300 -// due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public" -// in older compilers in order to use it... starting with VC7 we can declare it as "protected" + // due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public" + // in older compilers in order to use it... starting with VC7 we can declare it as "protected" protected: #endif - enum { STACKWALK_MAX_NAMELEN = 1024 }; // max name length for found symbols + enum + { + STACKWALK_MAX_NAMELEN = 1024 + }; // max name length for found symbols protected: // Entry for each Callstack-Entry typedef struct CallstackEntry { - DWORD64 offset; // if 0, we have no valid entry - CHAR name[STACKWALK_MAX_NAMELEN]; - CHAR undName[STACKWALK_MAX_NAMELEN]; - CHAR undFullName[STACKWALK_MAX_NAMELEN]; + DWORD64 offset; // if 0, we have no valid entry + CHAR name[STACKWALK_MAX_NAMELEN]; + CHAR undName[STACKWALK_MAX_NAMELEN]; + CHAR undFullName[STACKWALK_MAX_NAMELEN]; DWORD64 offsetFromSmybol; - DWORD offsetFromLine; - DWORD lineNumber; - CHAR lineFileName[STACKWALK_MAX_NAMELEN]; - DWORD symType; - LPCSTR symTypeString; - CHAR moduleName[STACKWALK_MAX_NAMELEN]; + DWORD offsetFromLine; + DWORD lineNumber; + CHAR lineFileName[STACKWALK_MAX_NAMELEN]; + DWORD symType; + LPCSTR symTypeString; + CHAR moduleName[STACKWALK_MAX_NAMELEN]; DWORD64 baseOfImage; - CHAR loadedImageName[STACKWALK_MAX_NAMELEN]; + CHAR loadedImageName[STACKWALK_MAX_NAMELEN]; } CallstackEntry; - enum CallstackEntryType {firstEntry, nextEntry, lastEntry}; + typedef enum CallstackEntryType + { + firstEntry, + nextEntry, + lastEntry + } CallstackEntryType; virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName); - virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion); - virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry); + virtual void OnLoadModule(LPCSTR img, + LPCSTR mod, + DWORD64 baseAddr, + DWORD size, + DWORD result, + LPCSTR symType, + LPCSTR pdbName, + ULONGLONG fileVersion); + virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry& entry); virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr); virtual void OnOutput(LPCSTR szText); - StackWalkerInternal *m_sw; - HANDLE m_hProcess; - DWORD m_dwProcessId; - BOOL m_modulesLoaded; - LPSTR m_szSymPath; + StackWalkerInternal* m_sw; + HANDLE m_hProcess; + DWORD m_dwProcessId; + BOOL m_modulesLoaded; + LPSTR m_szSymPath; int m_options; int m_MaxRecursionCount; - static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead); + static BOOL __stdcall myReadProcMem(HANDLE hProcess, + DWORD64 qwBaseAddress, + PVOID lpBuffer, + DWORD nSize, + LPDWORD lpNumberOfBytesRead); friend StackWalkerInternal; -}; // class StackWalker - +}; // class StackWalker // The "ugly" assembler-implementation is needed for systems before XP -// If you have a new PSDK and you only compile for XP and later, then you can use +// If you have a new PSDK and you only compile for XP and later, then you can use // the "RtlCaptureContext" -// Currently there is no define which determines the PSDK-Version... -// So we just use the compiler-version (and assumes that the PSDK is +// Currently there is no define which determines the PSDK-Version... +// So we just use the compiler-version (and assumes that the PSDK is // the one which was installed by the VS-IDE) // INFO: If you want, you can use the RtlCaptureContext if you only target XP and later... @@ -204,40 +230,54 @@ class IVW_STACKTRACE_API StackWalker #if defined(_M_IX86) #ifdef CURRENT_THREAD_VIA_EXCEPTION -// TODO: The following is not a "good" implementation, +// TODO: The following is not a "good" implementation, // because the callstack is only valid in the "__except" block... -#define GET_CURRENT_CONTEXT(c, contextFlags) \ - do { \ - memset(&c, 0, sizeof(CONTEXT)); \ - EXCEPTION_POINTERS *pExp = NULL; \ - __try { \ - throw 0; \ - } __except( ( (pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER)) {} \ - if (pExp != NULL) \ - memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \ - c.ContextFlags = contextFlags; \ - } while(0); +#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \ + do \ + { \ + memset(&c, 0, sizeof(CONTEXT)); \ + EXCEPTION_POINTERS* pExp = NULL; \ + __try \ + { \ + throw 0; \ + } \ + __except (((pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER \ + : EXCEPTION_EXECUTE_HANDLER)) \ + { \ + } \ + if (pExp != NULL) \ + memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \ + c.ContextFlags = contextFlags; \ + } while (0); #else +// clang-format off // The following should be enough for walking the callstack... -#define GET_CURRENT_CONTEXT(c, contextFlags) \ - do { \ - memset(&c, 0, sizeof(CONTEXT)); \ - c.ContextFlags = contextFlags; \ - __asm call x \ - __asm x: pop eax \ - __asm mov c.Eip, eax \ - __asm mov c.Ebp, ebp \ - __asm mov c.Esp, esp \ - } while(0); +#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \ + do \ + { \ + memset(&c, 0, sizeof(CONTEXT)); \ + c.ContextFlags = contextFlags; \ + __asm call x \ + __asm x: pop eax \ + __asm mov c.Eip, eax \ + __asm mov c.Ebp, ebp \ + __asm mov c.Esp, esp \ + } while (0) +// clang-format on #endif #else // The following is defined for x86 (XP and higher), x64 and IA64: -#define GET_CURRENT_CONTEXT(c, contextFlags) \ - do { \ - memset(&c, 0, sizeof(CONTEXT)); \ - c.ContextFlags = contextFlags; \ - RtlCaptureContext(&c); \ -} while(0); +#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \ + do \ + { \ + memset(&c, 0, sizeof(CONTEXT)); \ + c.ContextFlags = contextFlags; \ + RtlCaptureContext(&c); \ + } while (0); #endif + +#endif //defined(_MSC_VER) + +#endif // __STACKWALKER_H__ \ No newline at end of file diff --git a/include/inviwo/core/util/exception.h b/include/inviwo/core/util/exception.h index 2f8bb20c84..f45a2591ec 100644 --- a/include/inviwo/core/util/exception.h +++ b/include/inviwo/core/util/exception.h @@ -47,11 +47,10 @@ namespace inviwo { struct IVW_CORE_API ExceptionContext { ExceptionContext(std::string caller = "", std::string file = "", std::string function = "", int line = 0); - - const std::string& getCaller(); - const std::string& getFile(); - const std::string& getFunction(); - const int& getLine(); + const std::string& getCaller() const; + const std::string& getFile() const; + const std::string& getFunction() const; + const int& getLine() const; private: std::string caller_; @@ -76,10 +75,12 @@ class IVW_CORE_API Exception : public std::exception { virtual std::string getMessage() const noexcept; virtual const char* what() const noexcept override; virtual const ExceptionContext& getContext() const; + const std::vector& getStack() const; private: std::string message_; ExceptionContext context_; + std::vector stack_; }; class IVW_CORE_API RangeException : public Exception { diff --git a/include/inviwo/core/util/settings/systemsettings.h b/include/inviwo/core/util/settings/systemsettings.h index fd4ff6bca4..0c3eba5554 100644 --- a/include/inviwo/core/util/settings/systemsettings.h +++ b/include/inviwo/core/util/settings/systemsettings.h @@ -58,6 +58,8 @@ class IVW_CORE_API SystemSettings : public Settings { BoolProperty runtimeModuleReloading_; BoolProperty enableResourceManager_; TemplateOptionProperty breakOnMessage_; + BoolProperty breakOnException_; + BoolProperty stackTraceInException_; static size_t defaultPoolSize(); }; diff --git a/include/inviwo/qt/applicationbase/inviwoapplicationqt.h b/include/inviwo/qt/applicationbase/inviwoapplicationqt.h index 41d260c2a7..71e20ab918 100644 --- a/include/inviwo/qt/applicationbase/inviwoapplicationqt.h +++ b/include/inviwo/qt/applicationbase/inviwoapplicationqt.h @@ -65,7 +65,7 @@ class IVW_QTAPPLICATIONBASE_API InviwoQtEvent : public QEvent { class IVW_QTAPPLICATIONBASE_API InviwoApplicationQt : public QApplication, public InviwoApplication { public: - InviwoApplicationQt(std::string displayName, int& argc, char** argv); + InviwoApplicationQt(int& argc, char** argv, const std::string& displayName); virtual ~InviwoApplicationQt() = default; virtual void registerFileObserver(FileObserver* fileObserver) override; diff --git a/include/inviwo/qt/editor/inviwomainwindow.h b/include/inviwo/qt/editor/inviwomainwindow.h index 9c457f19cd..4b71b0f4de 100644 --- a/include/inviwo/qt/editor/inviwomainwindow.h +++ b/include/inviwo/qt/editor/inviwomainwindow.h @@ -107,6 +107,7 @@ class IVW_QTEDITOR_API InviwoMainWindow : public QMainWindow, public NetworkEdit * leaves the current workspace file as current workspace */ void saveWorkspaceAsCopy(); + bool askToSaveWorkspaceChanges(); void exitInviwo(bool saveIfModified = true); void showAboutBox(); @@ -135,8 +136,6 @@ class IVW_QTEDITOR_API InviwoMainWindow : public QMainWindow, public NetworkEdit void saveCanvases(std::string path, std::string fileName); void getScreenGrab(std::string path, std::string fileName); - bool askToSaveWorkspaceChanges(); - void addToRecentWorkspaces(QString workspaceFileName); /** diff --git a/modules/cimg/tests/unittests/cimg-unittest-main.cpp b/modules/cimg/tests/unittests/cimg-unittest-main.cpp index 758007d186..e9bc310a82 100644 --- a/modules/cimg/tests/unittests/cimg-unittest-main.cpp +++ b/modules/cimg/tests/unittests/cimg-unittest-main.cpp @@ -50,7 +50,7 @@ int main(int argc, char** argv) { inviwo::LogCentral::init(); - InviwoApplication app(argc, argv, "inviwo"); + InviwoApplication app(argc, argv, "Inviwo-Unittests-CImg"); { std::vector> modules; modules.emplace_back(createCoreModule()); diff --git a/modules/python3/tests/unittests/python3-unittest-main.cpp b/modules/python3/tests/unittests/python3-unittest-main.cpp index 9b66bf4198..1e40630021 100644 --- a/modules/python3/tests/unittests/python3-unittest-main.cpp +++ b/modules/python3/tests/unittests/python3-unittest-main.cpp @@ -53,7 +53,7 @@ int main(int argc, char** argv) { auto logger = std::make_shared(); LogCentral::getPtr()->setLogLevel(LogLevel::Error); LogCentral::getPtr()->registerLogger(logger); - InviwoApplication app(argc, argv, "inviwo"); + InviwoApplication app(argc, argv, "Inviwo-Unittests-Python"); { std::vector> modules; diff --git a/src/core/properties/boolcompositeproperty.cpp b/src/core/properties/boolcompositeproperty.cpp index 895feb016a..5c7b3f143f 100644 --- a/src/core/properties/boolcompositeproperty.cpp +++ b/src/core/properties/boolcompositeproperty.cpp @@ -44,13 +44,11 @@ BoolCompositeProperty::BoolCompositeProperty(std::string identifier, std::string checked_.setVisible(false); checked_.setCurrentStateAsDefault(); addProperty(checked_); - checked_.onChange([this]() { Property::propertyModified(); }); } BoolCompositeProperty::BoolCompositeProperty(const BoolCompositeProperty& rhs) : CompositeProperty(rhs), checked_{rhs.checked_} { addProperty(checked_); - checked_.onChange([this]() { Property::propertyModified(); }); } BoolCompositeProperty* BoolCompositeProperty::clone() const { diff --git a/src/core/tests/unittests/inviwo-core-unittest-main.cpp b/src/core/tests/unittests/inviwo-core-unittest-main.cpp index ee3132af5d..e71f44b6fa 100644 --- a/src/core/tests/unittests/inviwo-core-unittest-main.cpp +++ b/src/core/tests/unittests/inviwo-core-unittest-main.cpp @@ -50,7 +50,7 @@ int main(int argc, char** argv) { auto logger = std::make_shared(); LogCentral::getPtr()->setLogLevel(LogLevel::Error); LogCentral::getPtr()->registerLogger(logger); - InviwoApplication app(argc, argv, "inviwo"); + InviwoApplication app(argc, argv, "Inviwo-Unittests-Core"); { std::vector> modules; diff --git a/src/core/util/exception.cpp b/src/core/util/exception.cpp index 4feed11550..faf00a51b6 100644 --- a/src/core/util/exception.cpp +++ b/src/core/util/exception.cpp @@ -29,11 +29,46 @@ #include #include +#include + +#include +#include namespace inviwo { +namespace { + +std::vector stackTrace() { + if (InviwoApplication::isInitialized()) { + auto app = InviwoApplication::getPtr(); + if (auto sys = app->getSettingsByType()) { + if (sys->stackTraceInException_) { + auto stack = getStackTrace(); + const auto offset = std::min(size_t(4), stack.size()); + stack.erase(stack.begin(), stack.begin() + offset); + return stack; + } + } + } + return {}; +} + +bool breakOnException() { + if (InviwoApplication::isInitialized()) { + auto app = InviwoApplication::getPtr(); + if (auto sys = app->getSettingsByType()) { + return sys->breakOnException_; + } + } + return false; +} + +} // namespace + Exception::Exception(const std::string& message, ExceptionContext context) - : std::exception(), message_(message), context_(context) {} + : std::exception(), message_(message), context_(std::move(context)), stack_{stackTrace()} { + if (breakOnException()) util::debugBreak(); +} Exception::~Exception() noexcept = default; @@ -42,6 +77,8 @@ const char* Exception::what() const noexcept { return message_.c_str(); } const ExceptionContext& Exception::getContext() const { return context_; } +const std::vector& Exception::getStack() const { return stack_; } + RangeException::RangeException(const std::string& message, ExceptionContext context) : Exception(message, context) {} @@ -84,12 +121,12 @@ ExceptionContext::ExceptionContext(std::string caller, std::string file, std::st int line) : caller_(caller), file_(file), function_(function), line_(line) {} -const std::string& ExceptionContext::getCaller() { return caller_; } +const std::string& ExceptionContext::getCaller() const { return caller_; } -const std::string& ExceptionContext::getFile() { return file_; } +const std::string& ExceptionContext::getFile() const { return file_; } -const std::string& ExceptionContext::getFunction() { return function_; } +const std::string& ExceptionContext::getFunction() const { return function_; } -const int& ExceptionContext::getLine() { return line_; } +const int& ExceptionContext::getLine() const { return line_; } } // namespace inviwo diff --git a/src/core/util/settings/settings.cpp b/src/core/util/settings/settings.cpp index 2714a5fa75..8c2897ce97 100644 --- a/src/core/util/settings/settings.cpp +++ b/src/core/util/settings/settings.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -53,18 +54,18 @@ void Settings::addProperty(Property& property) { addProperty(&property, false); std::string Settings::getIdentifier() { return identifier_; } std::string Settings::getFileName() const { - std::string filename = identifier_; - replaceInString(filename, " ", "_"); - return filesystem::getPath(PathType::Settings, "/" + filename + ".ivs", true); + auto settingname = identifier_; + replaceInString(settingname, " ", "_"); + const auto appname = util::stripIdentifier(app_->getDisplayName()); + return filesystem::getPath(PathType::Settings, "/" + appname + "_" + settingname + ".ivs", + true); } -InviwoApplication* Settings::getInviwoApplication() { - return app_; -} +InviwoApplication* Settings::getInviwoApplication() { return app_; } void Settings::load() { util::KeepTrueWhileInScope guard{&isDeserializing_}; - const auto filename = getFileName(); + auto filename = getFileName(); if (filesystem::fileExists(filename)) { // An error is not critical as the default setting will be used. diff --git a/src/core/util/settings/systemsettings.cpp b/src/core/util/settings/systemsettings.cpp index d2db919b37..4128134b27 100644 --- a/src/core/util/settings/systemsettings.cpp +++ b/src/core/util/settings/systemsettings.cpp @@ -57,7 +57,9 @@ SystemSettings::SystemSettings(InviwoApplication* app) "Break on Message", {MessageBreakLevel::Off, MessageBreakLevel::Error, MessageBreakLevel::Warn, MessageBreakLevel::Info}, - 0} { + 0} + , breakOnException_{"breakOnException", "Break on Exception", false} + , stackTraceInException_{"stackTraceInException", "Create Stack Trace for Exceptions", false} { addProperty(applicationUsageMode_); addProperty(poolSize_); @@ -71,6 +73,8 @@ SystemSettings::SystemSettings(InviwoApplication* app) addProperty(runtimeModuleReloading_); addProperty(enableResourceManager_); addProperty(breakOnMessage_); + addProperty(breakOnException_); + addProperty(stackTraceInException_); logStackTraceProperty_.onChange( [this]() { LogCentral::getPtr()->setLogStacktrace(logStackTraceProperty_.get()); }); diff --git a/src/core/util/stacktrace.cpp b/src/core/util/stacktrace.cpp index 35cfe50fdc..1ef7242b58 100644 --- a/src/core/util/stacktrace.cpp +++ b/src/core/util/stacktrace.cpp @@ -24,7 +24,7 @@ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * *********************************************************************************/ #include @@ -41,13 +41,12 @@ #include - - #if defined(_MSC_VER) class StackWalkerToVector : public StackWalker { public: - StackWalkerToVector(StackWalkOptions level , std::vector* vector):StackWalker(level),vector_(vector) { - } + StackWalkerToVector(StackWalkOptions level, std::vector* vector) + : StackWalker(level), vector_(vector) {} + protected: virtual void OnOutput(LPCSTR szText) { std::string str(szText); @@ -55,12 +54,14 @@ class StackWalkerToVector : public StackWalker { if (str.find("StackWalker") != std::string::npos) { vector_->clear(); } else if (str.find("ERROR: ") != std::string::npos) { - // return; + // return; } else if (str.find("filename not available") != std::string::npos) { - // return; + // return; } - inviwo::replaceInString(str,"\n",""); //remove new line character at the end of the string that we get fro stack walker + inviwo::replaceInString( + str, "\n", + ""); // remove new line character at the end of the string that we get fro stack walker vector_->push_back(str); } @@ -69,7 +70,6 @@ class StackWalkerToVector : public StackWalker { }; #endif - namespace inviwo { std::vector getStackTrace() { @@ -85,12 +85,12 @@ std::vector getStackTrace() { int status; std::string line = strings[i]; size_t start = line.find("("); - size_t end = line.find("+"); - std::string className = line.substr(start+1,end - start - 1); - char* fixedClass = abi::__cxa_demangle(className.c_str(),0,0,&status); + size_t end = line.find("+"); + std::string className = line.substr(start + 1, end - start - 1); + char* fixedClass = abi::__cxa_demangle(className.c_str(), 0, 0, &status); if (!status && fixedClass) { - replaceInString(line,className,std::string(fixedClass)); + replaceInString(line, className, std::string(fixedClass)); free(fixedClass); } @@ -100,12 +100,10 @@ std::vector getStackTrace() { free(strings); #elif defined(_MSC_VER) - StackWalkerToVector sw(StackWalker::OptionsAll,&lines); - sw.LoadModules(); + StackWalkerToVector sw(StackWalker::OptionsAll, &lines); sw.ShowCallstack(); #endif return lines; } - -} // namespace \ No newline at end of file +} // namespace inviwo diff --git a/src/qt/applicationbase/inviwoapplicationqt.cpp b/src/qt/applicationbase/inviwoapplicationqt.cpp index 6d903efbc4..a3deca5dc6 100644 --- a/src/qt/applicationbase/inviwoapplicationqt.cpp +++ b/src/qt/applicationbase/inviwoapplicationqt.cpp @@ -55,7 +55,7 @@ namespace inviwo { -InviwoApplicationQt::InviwoApplicationQt(std::string displayName, int& argc, char** argv) +InviwoApplicationQt::InviwoApplicationQt(int& argc, char** argv, const std::string& displayName) : QApplication(argc, argv) , InviwoApplication(argc, argv, displayName) , mainWindow_(nullptr) diff --git a/tests/integrationtests/inviwo-integrationtests.cpp b/tests/integrationtests/inviwo-integrationtests.cpp index 7102059dca..8c8cc00d60 100644 --- a/tests/integrationtests/inviwo-integrationtests.cpp +++ b/tests/integrationtests/inviwo-integrationtests.cpp @@ -65,7 +65,7 @@ int main(int argc, char** argv) { auto logCounter = std::make_shared(); LogCentral::getPtr()->registerLogger(logCounter); - InviwoApplication inviwoApp(argc, argv, "unittest " + IVW_VERSION); + InviwoApplication inviwoApp(argc, argv, "Inviwo-IntegrationTests"); inviwoApp.setPostEnqueueFront([]() { glfwPostEmptyEvent(); }); inviwoApp.setProgressCallback([](std::string m) { LogCentral::getPtr()->log("InviwoApplication", LogLevel::Info, LogAudience::User, "", diff --git a/tests/integrationtests/inviwoapplication-test.cpp b/tests/integrationtests/inviwoapplication-test.cpp index acff56ee8d..b08365e801 100644 --- a/tests/integrationtests/inviwoapplication-test.cpp +++ b/tests/integrationtests/inviwoapplication-test.cpp @@ -24,7 +24,7 @@ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * *********************************************************************************/ #include @@ -34,18 +34,11 @@ #include -namespace inviwo{ - -TEST(InviwoApplicationTest,initTest) { - InviwoApplication* app = InviwoApplication::getPtr(); - ASSERT_TRUE(app!=0); -} - +namespace inviwo { -TEST(InviwoApplicationTest,displayNameTest) { +TEST(InviwoApplicationTest, initTest) { InviwoApplication* app = InviwoApplication::getPtr(); - ASSERT_TRUE(app!=0); - EXPECT_TRUE(app->getDisplayName().find(IVW_VERSION)!=std::string::npos); + ASSERT_TRUE(app != 0); } -} \ No newline at end of file +} // namespace inviwo