# Cinder

- Main Page
- Related Pages
- Namespaces
- Classes
- Files

- File List
- File Members

# include/cinder/msw/StackWalker.h

Go to the documentation of this file.

    00001 /**********************************************************************
    00002  *
    00003  * StackWalker.h
    00004  *
    00005  *
    00006  *
    00007  * LICENSE (http://www.opensource.org/licenses/bsd-license.php)
    00008  *
    00009  *   Copyright (c) 2005-2009, Jochen Kalmbach
    00010  *   All rights reserved.
    00011  *
    00012  *   Redistribution and use in source and binary forms, with or without modification,
    00013  *   are permitted provided that the following conditions are met:
    00014  *
    00015  *   Redistributions of source code must retain the above copyright notice,
    00016  *   this list of conditions and the following disclaimer.
    00017  *   Redistributions in binary form must reproduce the above copyright notice,
    00018  *   this list of conditions and the following disclaimer in the documentation
    00019  *   and/or other materials provided with the distribution.
    00020  *   Neither the name of Jochen Kalmbach nor the names of its contributors may be
    00021  *   used to endorse or promote products derived from this software without
    00022  *   specific prior written permission.
    00023  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    00024  *   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
    00025  *   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    00026  *   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
    00027  *   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    00028  *   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    00029  *   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    00030  *   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    00031  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    00032  *   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    00033  *
    00034  * **********************************************************************/
    00035 // #pragma once is supported starting with _MCS_VER 1000,
    00036 // so we need not to check the version (because we only support _MSC_VER >= 1100)!
    00037 #pragma once
    00038
    00039 #include <windows.h>
    00040
    00041 // special defines for VC5/6 (if no actual PSDK is installed):
    00042 #if _MSC_VER < 1300
    00043 typedef unsigned __int64 DWORD64, *PDWORD64;
    00044 #if defined(_WIN64)
    00045 typedef unsigned __int64 SIZE_T, *PSIZE_T;
    00046 #else
    00047 typedef unsigned long SIZE_T, *PSIZE_T;
    00048 #endif
    00049 #endif  // _MSC_VER < 1300
    00050
    00051 class StackWalkerInternal;  // forward
    00052 class StackWalker
    00053 {
    00054 public:
    00055   typedef enum StackWalkOptions
    00056   {
    00057     // No addition info will be retrived
    00058     // (only the address is available)
    00059     RetrieveNone = 0,
    00060
    00061     // Try to get the symbol-name
    00062     RetrieveSymbol = 1,
    00063
    00064     // Try to get the line for this symbol
    00065     RetrieveLine = 2,
    00066
    00067     // Try to retrieve the module-infos
    00068     RetrieveModuleInfo = 4,
    00069
    00070     // Also retrieve the version for the DLL/EXE
    00071     RetrieveFileVersion = 8,
    00072
    00073     // Contains all the abouve
    00074     RetrieveVerbose = 0xF,
    00075
    00076     // Generate a "good" symbol-search-path
    00077     SymBuildPath = 0x10,
    00078
    00079     // Also use the public Microsoft-Symbol-Server
    00080     SymUseSymSrv = 0x20,
    00081
    00082     // Contains all the abouve "Sym"-options
    00083     SymAll = 0x30,
    00084
    00085     // Contains all options (default)
    00086     OptionsAll = 0x3F
    00087   } StackWalkOptions;
    00088
    00089   StackWalker(
    00090     int options = OptionsAll, // 'int' is by design, to combine the enum-flags
    00091     LPCSTR szSymPath = NULL,
    00092     DWORD dwProcessId = GetCurrentProcessId(),
    00093     HANDLE hProcess = GetCurrentProcess()
    00094     );
    00095   StackWalker(DWORD dwProcessId, HANDLE hProcess);
    00096   virtual ~StackWalker();
    00097
    00098   typedef BOOL (__stdcall *PReadProcessMemoryRoutine)(
    00099     HANDLE      hProcess,
    00100     DWORD64     qwBaseAddress,
    00101     PVOID       lpBuffer,
    00102     DWORD       nSize,
    00103     LPDWORD     lpNumberOfBytesRead,
    00104     LPVOID      pUserData  // optional data, which was passed in "ShowCallstack"
    00105     );
    00106
    00107   BOOL LoadModules();
    00108
    00109   BOOL ShowCallstack(
    00110     HANDLE hThread = GetCurrentThread(),
    00111     const CONTEXT *context = NULL,
    00112     PReadProcessMemoryRoutine readMemoryFunction = NULL,
    00113     LPVOID pUserData = NULL  // optional to identify some data in the 'readMemoryFunction'-callback
    00114     );
    00115
    00116 #if _MSC_VER >= 1300
    00117 // due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public"
    00118 // in older compilers in order to use it... starting with VC7 we can declare it as "protected"
    00119 protected:
    00120 #endif
    00121     enum { STACKWALK_MAX_NAMELEN = 1024 }; // max name length for found symbols
    00122
    00123 protected:
    00124   // Entry for each Callstack-Entry
    00125   typedef struct CallstackEntry
    00126   {
    00127     DWORD64 offset;  // if 0, we have no valid entry
    00128     CHAR name[STACKWALK_MAX_NAMELEN];
    00129     CHAR undName[STACKWALK_MAX_NAMELEN];
    00130     CHAR undFullName[STACKWALK_MAX_NAMELEN];
    00131     DWORD64 offsetFromSmybol;
    00132     DWORD offsetFromLine;
    00133     DWORD lineNumber;
    00134     CHAR lineFileName[STACKWALK_MAX_NAMELEN];
    00135     DWORD symType;
    00136     LPCSTR symTypeString;
    00137     CHAR moduleName[STACKWALK_MAX_NAMELEN];
    00138     DWORD64 baseOfImage;
    00139     CHAR loadedImageName[STACKWALK_MAX_NAMELEN];
    00140   } CallstackEntry;
    00141
    00142   typedef enum CallstackEntryType {firstEntry, nextEntry, lastEntry};
    00143
    00144   virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName);
    00145   virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion);
    00146   virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry);
    00147   virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr);
    00148   virtual void OnOutput(LPCSTR szText);
    00149
    00150   StackWalkerInternal *m_sw;
    00151   HANDLE m_hProcess;
    00152   DWORD m_dwProcessId;
    00153   BOOL m_modulesLoaded;
    00154   LPSTR m_szSymPath;
    00155
    00156   int m_options;
    00157   int m_MaxRecursionCount;
    00158
    00159   static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead);
    00160
    00161   friend StackWalkerInternal;
    00162 };  // class StackWalker
    00163
    00164
    00165 // The "ugly" assembler-implementation is needed for systems before XP
    00166 // If you have a new PSDK and you only compile for XP and later, then you can use
    00167 // the "RtlCaptureContext"
    00168 // Currently there is no define which determines the PSDK-Version...
    00169 // So we just use the compiler-version (and assumes that the PSDK is
    00170 // the one which was installed by the VS-IDE)
    00171
    00172 // INFO: If you want, you can use the RtlCaptureContext if you only target XP and later...
    00173 //       But I currently use it in x64/IA64 environments...
    00174 //#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400)
    00175
    00176 #if defined(_M_IX86)
    00177 #ifdef CURRENT_THREAD_VIA_EXCEPTION
    00178 // TODO: The following is not a "good" implementation,
    00179 // because the callstack is only valid in the "__except" block...
    00180 #define GET_CURRENT_CONTEXT(c, contextFlags) \
    00181   do { \
    00182     memset(&c, 0, sizeof(CONTEXT)); \
    00183     EXCEPTION_POINTERS *pExp = NULL; \
    00184     __try { \
    00185       throw 0; \
    00186     } __except( ( (pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER)) {} \
    00187     if (pExp != NULL) \
    00188       memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \
    00189       c.ContextFlags = contextFlags; \
    00190   } while(0);
    00191 #else
    00192 // The following should be enough for walking the callstack...
    00193 #define GET_CURRENT_CONTEXT(c, contextFlags) \
    00194   do { \
    00195     memset(&c, 0, sizeof(CONTEXT)); \
    00196     c.ContextFlags = contextFlags; \
    00197     __asm    call x \
    00198     __asm x: pop eax \
    00199     __asm    mov c.Eip, eax \
    00200     __asm    mov c.Ebp, ebp \
    00201     __asm    mov c.Esp, esp \
    00202   } while(0);
    00203 #endif
    00204
    00205 #else
    00206
    00207 // The following is defined for x86 (XP and higher), x64 and IA64:
    00208 #define GET_CURRENT_CONTEXT(c, contextFlags) \
    00209   do { \
    00210     memset(&c, 0, sizeof(CONTEXT)); \
    00211     c.ContextFlags = contextFlags; \
    00212     RtlCaptureContext(&c); \
    00213 } while(0);
    00214 #endif