本系列文章由 @lonelyrains 出品,转载请注明出处。
文章链接: http://blog.csdn.net/lonelyrains/article/details/9428395
笔者在VS2008和VC6.0两种常用平台应用此工具,发现VC6.0版本不支持__FUNCTION__宏,有高手自定义的__FUNCTION__实现在此。
有几个问题需要注意:
1、在MFC环境下报错的问题:
①#error WINDOWS.H already included. MFC apps must not #include <windows.h>。无奈的解决方法是把自动添加上的调试头文件#include语句添加到afx相关的头文件之后。
②unexpected endif:需要添加stafx.h的预编译头文件,然后把xtrace.cpp里的#if defined(_DEBUG) && defined(WIN32)和对应的#endif去掉
2、在Unicode环境下报错的问题:
添加强制转换
最终的xtrace.cpp文件代码如下:
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * xtrace.cpp by Hector J. Rivas, [email protected] from "ExtendedTrace" * by Zoltan Csizmadia, [email protected] * * A Win32 VC++ 6.0 implementation of the __FUNCTION__ macro that works for me. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "StdAfx.h" #include <stdio.h> #include <windows.h> #include <tchar.h> #include <ImageHlp.h> #include "xtrace.h" // Unicode safe char* -> TCHAR* conversion void PCSTR2LPTSTR(PCSTR lpszIn, LPTSTR lpszOut) { #if defined(UNICODE) || defined(_UNICODE) ULONG index = 0; PCSTR lpAct = lpszIn; for(;; lpAct++) { lpszOut[index++] = (TCHAR)(*lpAct); if (*lpAct == 0) break; } #else strcpy(lpszOut, lpszIn); #endif } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * InitSymPath(): figure out the path for the symbol files; the search path is: * * . + * __FILE__ (path) + Debug + * %_NT_SYMBOL_PATH% + * %_NT_ALTERNATE_SYMBOL_PATH% + * %SYSTEMROOT% + * %SYSTEMROOT%\System32 + * lpszIniPath * * NOTES: There is no size check for lpszSymbolPath. If you want to limit the macro to * symbols in your debug executable, you can omit the environment variables (default). * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void InitSymPath(PSTR lpszSymbolPath, PCSTR lpszIniPath, BOOL bSysPath) { CHAR lpszPath[BUFFERSIZE] = ""; CHAR lpszTemp[BUFFERSIZE] = ""; // create the default path strcpy(lpszSymbolPath, ".;"); // get the current path sprintf(lpszTemp, __FILE__); strcpy(lpszPath, strrev(strchr(strrev(lpszTemp), '\\'))); strcat(lpszPath, "Debug"); strcat(lpszSymbolPath, lpszPath); if (bSysPath) { // environment variable _NT_SYMBOL_PATH if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", lpszPath, BUFFERSIZE)) { strcat(lpszSymbolPath, ";"); strcat(lpszSymbolPath, lpszPath); } // environment variable _NT_ALTERNATE_SYMBOL_PATH if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", lpszPath, BUFFERSIZE)) { strcat(lpszSymbolPath, ";"); strcat(lpszSymbolPath, lpszPath); } // environment variable SYSTEMROOT if (GetEnvironmentVariableA("SYSTEMROOT", lpszPath, BUFFERSIZE)) { strcat(lpszSymbolPath, ";"); strcat(lpszSymbolPath, lpszPath); // SYSTEMROOT\System32 strcat(lpszSymbolPath, ";"); strcat(lpszSymbolPath, lpszPath); strcat(lpszSymbolPath, "\\System32"); } } // Add any user defined path if (lpszIniPath != NULL) { if (lpszIniPath[0] != '\0') { strcat(lpszSymbolPath, ";"); strcat(lpszSymbolPath, lpszIniPath); } } } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * InitSymInfo(): initializes the symbol files * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ BOOL InitSymInfo(PCSTR lpszInitialSymbolPath, BOOL bSysPath) { CHAR lpszSymbolPath[BUFFERSIZE]; DWORD symOptions = SymGetOptions(); // set current image help API options; according to the SDK docs, with // SYMOPT_DEFERRED_LOADS: "Symbols are not loaded until a reference is made // requiring the symbols be loaded. This is the fastest, most efficient way to use // the symbol handler.". SYMOPT_UNDNAME is excluded to do the undecoration // ourselves. symOptions |= SYMOPT_DEFERRED_LOADS; symOptions &= ~SYMOPT_UNDNAME; SymSetOptions(symOptions); // get the search path for the symbol files InitSymPath(lpszSymbolPath, lpszInitialSymbolPath, bSysPath); return SymInitialize(GetCurrentProcess(), lpszSymbolPath, TRUE); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * GetFuncInfo(): Get function prototype from address and stack address * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ BOOL GetFuncInfo(ULONG fnAddress, ULONG stackAddress, LPTSTR lpszSymbol) { BOOL ret = FALSE; DWORD dwDisp = 0, dwSymSize = 10000; TCHAR lpszUDSymbol[BUFFERSIZE] = _T("?"); CHAR lpszANSIUDSymbol[BUFFERSIZE] = "?"; PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL)GlobalAlloc(GMEM_FIXED, dwSymSize); ZeroMemory(pSym, dwSymSize); pSym->SizeOfStruct = dwSymSize; pSym->MaxNameLength = dwSymSize - sizeof(IMAGEHLP_SYMBOL); // Set the default to unknown _tcscpy(lpszSymbol, _T("?")); // Get symbol info if (SymGetSymFromAddr(GetCurrentProcess(), (ULONG)fnAddress, &dwDisp, pSym)) { // Make the symbol readable for humans UnDecorateSymbolName(pSym->Name, lpszANSIUDSymbol, BUFFERSIZE, UNDNAME_COMPLETE | UNDNAME_NO_THISTYPE | UNDNAME_NO_SPECIAL_SYMS | UNDNAME_NO_MEMBER_TYPE | UNDNAME_NO_MS_KEYWORDS | UNDNAME_NO_ACCESS_SPECIFIERS | UNDNAME_NO_ARGUMENTS); // Symbol information is ANSI string PCSTR2LPTSTR(lpszANSIUDSymbol, lpszUDSymbol); lpszSymbol[0] = _T('\0'); _tcscat(lpszSymbol, lpszUDSymbol); ret = TRUE; } GlobalFree(pSym); return ret; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * GetFuncName(): return the undecorated function name * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ LPCTSTR GetFuncName() { BOOL bResult = FALSE; STACKFRAME callStack; CONTEXT context; TCHAR lpszFnInfo[BUFFERSIZE]; HANDLE hProcess = GetCurrentProcess(); HANDLE hThread = GetCurrentThread(); // initialize a context struct to retrieve processor-specific register data ZeroMemory(&context, sizeof(context)); context.ContextFlags = CONTEXT_FULL; if (!GetThreadContext(hThread, &context)) return L""; // initialize a stack frame struct in preparation to walk the stack ZeroMemory(&callStack, sizeof(callStack)); callStack.AddrPC.Offset = context.Eip; callStack.AddrStack.Offset = context.Esp; callStack.AddrFrame.Offset = context.Ebp; callStack.AddrPC.Mode = AddrModeFlat; callStack.AddrStack.Mode = AddrModeFlat; callStack.AddrFrame.Mode = AddrModeFlat; // obtain a stack trace of the calling function (i.e., omit this one) for (ULONG n = 0; n < 2; n++) { bResult = StackWalk(IMAGE_FILE_MACHINE_I386, hProcess, hThread, &callStack, NULL, NULL, SymFunctionTableAccess, SymGetModuleBase, NULL); } if (bResult && callStack.AddrFrame.Offset != 0) { GetFuncInfo(callStack.AddrPC.Offset, callStack.AddrFrame.Offset, lpszFnInfo); // from now on its all personal display preferences with string manipulation // tokenize the undecorated returned symbol to omit the class name CHAR* lpszToken = strtok((char *)lpszFnInfo, "::"); CHAR lpszLast[BUFFERSIZE] = ""; while (lpszToken != NULL) { strcpy(lpszLast, lpszToken); lpszToken = strtok(NULL, "::"); } // append a delimiter, so that our display in printf instructions is // 'functionname: message' for debug builds and 'message' for release builds, // using the format string "%smessage" and __FUNCTION__ as an argument strcat(lpszLast, ": "); return (const unsigned short *)lpszLast; } return L""; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * KillSymInfo(): uninitialize the loaded symbol files * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ BOOL KillSymInfo() { return SymCleanup(GetCurrentProcess()); }