duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

StackWalker.h (9933B)


      1 #ifndef __STACKWALKER_H__
      2 #define __STACKWALKER_H__
      3 
      4 #if defined(_MSC_VER)
      5 
      6 /**********************************************************************
      7  *
      8  * StackWalker.h
      9  *
     10  *
     11  *
     12  * LICENSE (http://www.opensource.org/licenses/bsd-license.php)
     13  *
     14  *   Copyright (c) 2005-2009, Jochen Kalmbach
     15  *   All rights reserved.
     16  *
     17  *   Redistribution and use in source and binary forms, with or without modification,
     18  *   are permitted provided that the following conditions are met:
     19  *
     20  *   Redistributions of source code must retain the above copyright notice,
     21  *   this list of conditions and the following disclaimer.
     22  *   Redistributions in binary form must reproduce the above copyright notice,
     23  *   this list of conditions and the following disclaimer in the documentation
     24  *   and/or other materials provided with the distribution.
     25  *   Neither the name of Jochen Kalmbach nor the names of its contributors may be
     26  *   used to endorse or promote products derived from this software without
     27  *   specific prior written permission.
     28  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     29  *   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     30  *   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     31  *   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
     32  *   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     33  *   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     34  *   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     35  *   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     36  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     37  *   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     38  *
     39  * **********************************************************************/
     40 // #pragma once is supported starting with _MSC_VER 1000,
     41 // so we need not to check the version (because we only support _MSC_VER >= 1100)!
     42 #pragma once
     43 
     44 #include <windows.h>
     45 
     46 #if _MSC_VER >= 1900
     47 #pragma warning(disable : 4091)
     48 #endif
     49 
     50 // special defines for VC5/6 (if no actual PSDK is installed):
     51 #if _MSC_VER < 1300
     52 typedef unsigned __int64 DWORD64, *PDWORD64;
     53 #if defined(_WIN64)
     54 typedef unsigned __int64 SIZE_T, *PSIZE_T;
     55 #else
     56 typedef unsigned long SIZE_T, *PSIZE_T;
     57 #endif
     58 #endif // _MSC_VER < 1300
     59 
     60 class StackWalkerInternal; // forward
     61 class StackWalker
     62 {
     63 public:
     64   typedef enum StackWalkOptions
     65   {
     66     // No addition info will be retrieved
     67     // (only the address is available)
     68     RetrieveNone = 0,
     69 
     70     // Try to get the symbol-name
     71     RetrieveSymbol = 1,
     72 
     73     // Try to get the line for this symbol
     74     RetrieveLine = 2,
     75 
     76     // Try to retrieve the module-infos
     77     RetrieveModuleInfo = 4,
     78 
     79     // Also retrieve the version for the DLL/EXE
     80     RetrieveFileVersion = 8,
     81 
     82     // Contains all the above
     83     RetrieveVerbose = 0xF,
     84 
     85     // Generate a "good" symbol-search-path
     86     SymBuildPath = 0x10,
     87 
     88     // Also use the public Microsoft-Symbol-Server
     89     SymUseSymSrv = 0x20,
     90 
     91     // Contains all the above "Sym"-options
     92     SymAll = 0x30,
     93 
     94     // Contains all options (default)
     95     OptionsAll = 0x3F
     96   } StackWalkOptions;
     97 
     98   StackWalker(int    options = OptionsAll, // 'int' is by design, to combine the enum-flags
     99               LPCSTR szSymPath = NULL,
    100               DWORD  dwProcessId = GetCurrentProcessId(),
    101               HANDLE hProcess = GetCurrentProcess());
    102   StackWalker(DWORD dwProcessId, HANDLE hProcess);
    103   virtual ~StackWalker();
    104 
    105   static HMODULE LoadDbgHelpLibrary();
    106 
    107   typedef BOOL(__stdcall* PReadProcessMemoryRoutine)(
    108       HANDLE  hProcess,
    109       DWORD64 qwBaseAddress,
    110       PVOID   lpBuffer,
    111       DWORD   nSize,
    112       LPDWORD lpNumberOfBytesRead,
    113       LPVOID  pUserData // optional data, which was passed in "ShowCallstack"
    114   );
    115 
    116   BOOL LoadModules();
    117 
    118   BOOL ShowCallstack(
    119       HANDLE                    hThread = GetCurrentThread(),
    120       const CONTEXT*            context = NULL,
    121       PReadProcessMemoryRoutine readMemoryFunction = NULL,
    122       LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback
    123   );
    124 
    125   BOOL ShowObject(LPVOID pObject);
    126 
    127 #if _MSC_VER >= 1300
    128   // due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public"
    129   // in older compilers in order to use it... starting with VC7 we can declare it as "protected"
    130 protected:
    131 #endif
    132   enum
    133   {
    134     STACKWALK_MAX_NAMELEN = 1024
    135   }; // max name length for found symbols
    136 
    137 protected:
    138   // Entry for each Callstack-Entry
    139   typedef struct CallstackEntry
    140   {
    141     DWORD64 offset; // if 0, we have no valid entry
    142     CHAR    name[STACKWALK_MAX_NAMELEN];
    143     CHAR    undName[STACKWALK_MAX_NAMELEN];
    144     CHAR    undFullName[STACKWALK_MAX_NAMELEN];
    145     DWORD64 offsetFromSmybol;
    146     DWORD   offsetFromLine;
    147     DWORD   lineNumber;
    148     CHAR    lineFileName[STACKWALK_MAX_NAMELEN];
    149     DWORD   symType;
    150     LPCSTR  symTypeString;
    151     CHAR    moduleName[STACKWALK_MAX_NAMELEN];
    152     DWORD64 baseOfImage;
    153     CHAR    loadedImageName[STACKWALK_MAX_NAMELEN];
    154   } CallstackEntry;
    155 
    156   typedef enum CallstackEntryType
    157   {
    158     firstEntry,
    159     nextEntry,
    160     lastEntry
    161   } CallstackEntryType;
    162 
    163   virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName);
    164   virtual void OnLoadModule(LPCSTR    img,
    165                             LPCSTR    mod,
    166                             DWORD64   baseAddr,
    167                             DWORD     size,
    168                             DWORD     result,
    169                             LPCSTR    symType,
    170                             LPCSTR    pdbName,
    171                             ULONGLONG fileVersion);
    172   virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry& entry);
    173   virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr);
    174   virtual void OnOutput(LPCSTR szText);
    175 
    176   StackWalkerInternal* m_sw;
    177   HANDLE               m_hProcess;
    178   DWORD                m_dwProcessId;
    179   BOOL                 m_modulesLoaded;
    180   LPSTR                m_szSymPath;
    181 
    182   int m_options;
    183   int m_MaxRecursionCount;
    184 
    185   static BOOL __stdcall myReadProcMem(HANDLE  hProcess,
    186                                       DWORD64 qwBaseAddress,
    187                                       PVOID   lpBuffer,
    188                                       DWORD   nSize,
    189                                       LPDWORD lpNumberOfBytesRead);
    190 
    191   friend StackWalkerInternal;
    192 }; // class StackWalker
    193 
    194 // The "ugly" assembler-implementation is needed for systems before XP
    195 // If you have a new PSDK and you only compile for XP and later, then you can use
    196 // the "RtlCaptureContext"
    197 // Currently there is no define which determines the PSDK-Version...
    198 // So we just use the compiler-version (and assumes that the PSDK is
    199 // the one which was installed by the VS-IDE)
    200 
    201 // INFO: If you want, you can use the RtlCaptureContext if you only target XP and later...
    202 //       But I currently use it in x64/IA64 environments...
    203 //#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400)
    204 
    205 #if defined(_M_IX86)
    206 #ifdef CURRENT_THREAD_VIA_EXCEPTION
    207 // TODO: The following is not a "good" implementation,
    208 // because the callstack is only valid in the "__except" block...
    209 #define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags)               \
    210   do                                                                            \
    211   {                                                                             \
    212     memset(&c, 0, sizeof(CONTEXT));                                             \
    213     EXCEPTION_POINTERS* pExp = NULL;                                            \
    214     __try                                                                       \
    215     {                                                                           \
    216       throw 0;                                                                  \
    217     }                                                                           \
    218     __except (((pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER   \
    219                                                   : EXCEPTION_EXECUTE_HANDLER)) \
    220     {                                                                           \
    221     }                                                                           \
    222     if (pExp != NULL)                                                           \
    223       memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT));                         \
    224     c.ContextFlags = contextFlags;                                              \
    225   } while (0);
    226 #else
    227 // clang-format off
    228 // The following should be enough for walking the callstack...
    229 #define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
    230   do                                                              \
    231   {                                                               \
    232     memset(&c, 0, sizeof(CONTEXT));                               \
    233     c.ContextFlags = contextFlags;                                \
    234     __asm    call x                                               \
    235     __asm x: pop eax                                              \
    236     __asm    mov c.Eip, eax                                       \
    237     __asm    mov c.Ebp, ebp                                       \
    238     __asm    mov c.Esp, esp                                       \
    239   } while (0)
    240 // clang-format on
    241 #endif
    242 
    243 #else
    244 
    245 // The following is defined for x86 (XP and higher), x64 and IA64:
    246 #define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
    247   do                                                              \
    248   {                                                               \
    249     memset(&c, 0, sizeof(CONTEXT));                               \
    250     c.ContextFlags = contextFlags;                                \
    251     RtlCaptureContext(&c);                                        \
    252   } while (0);
    253 #endif
    254 
    255 #endif //defined(_MSC_VER)
    256 
    257 #endif // __STACKWALKER_H__