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