windows-nt/Source/XPSP1/NT/enduser/speech/common/include/assertwithstack.cpp
2020-09-26 16:20:57 +08:00

707 lines
20 KiB
C++

//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#ifdef ASSERT_WITH_STACK
#ifndef _WIN64
#include "AssertWithStack.h"
//
//--- Macros ------------------------------------------------------------------
//
#define COUNT_OF(x) (sizeof(x) / sizeof(x[0]))
//
// Types and Constants --------------------------------------------------------
//
struct __SYMBOL_INFO
{
DWORD dwOffset;
char achModule[cchMaxAssertModuleLen];
char achSymbol[cchMaxAssertSymbolLen];
};
//--- Function Pointers to APIs in IMAGEHLP.DLL. Loaded dynamically. ---------
typedef LPAPI_VERSION (__stdcall *pfnImgHlp_ImagehlpApiVersionEx)(
LPAPI_VERSION AppVersion
);
typedef BOOL (__stdcall *pfnImgHlp_StackWalk)(
DWORD MachineType,
HANDLE hProcess,
HANDLE hThread,
LPSTACKFRAME StackFrame,
LPVOID ContextRecord,
PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine,
PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine,
PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine,
PTRANSLATE_ADDRESS_ROUTINE TranslateAddress
);
typedef BOOL (__stdcall *pfnImgHlp_SymGetModuleInfo)(
IN HANDLE hProcess,
IN DWORD dwAddr,
OUT PIMAGEHLP_MODULE ModuleInfo
);
typedef LPVOID (__stdcall *pfnImgHlp_SymFunctionTableAccess)(
HANDLE hProcess,
DWORD AddrBase
);
typedef BOOL (__stdcall *pfnImgHlp_SymGetSymFromAddr)(
IN HANDLE hProcess,
IN DWORD dwAddr,
OUT PDWORD pdwDisplacement,
OUT PIMAGEHLP_SYMBOL Symbol
);
typedef BOOL (__stdcall *pfnImgHlp_SymInitialize)(
IN HANDLE hProcess,
IN LPSTR UserSearchPath,
IN BOOL fInvadeProcess
);
typedef BOOL (__stdcall *pfnImgHlp_SymUnDName)(
IN PIMAGEHLP_SYMBOL sym, // Symbol to undecorate
OUT LPSTR UnDecName, // Buffer to store undecorated name in
IN DWORD UnDecNameLength // Size of the buffer
);
typedef BOOL (__stdcall *pfnImgHlp_SymLoadModule)(
IN HANDLE hProcess,
IN HANDLE hFile,
IN PSTR ImageName,
IN PSTR ModuleName,
IN DWORD BaseOfDll,
IN DWORD SizeOfDll
);
struct IMGHLPFN_LOAD
{
LPSTR pszFnName;
LPVOID * ppvfn;
};
//
// Globals --------------------------------------------------------------------
//
static BOOL g_fLoadedImageHlp = FALSE; // set to true on success
static BOOL g_fLoadedImageHlpFailed = FALSE; // set to true on failure
static HINSTANCE g_hinstImageHlp = NULL;
static HANDLE g_hProcess = NULL;
pfnImgHlp_ImagehlpApiVersionEx _ImagehlpApiVersionEx;
pfnImgHlp_StackWalk _StackWalk;
pfnImgHlp_SymGetModuleInfo _SymGetModuleInfo;
pfnImgHlp_SymFunctionTableAccess _SymFunctionTableAccess;
pfnImgHlp_SymGetSymFromAddr _SymGetSymFromAddr;
pfnImgHlp_SymInitialize _SymInitialize;
pfnImgHlp_SymUnDName _SymUnDName;
pfnImgHlp_SymLoadModule _SymLoadModule;
IMGHLPFN_LOAD ailFuncList[] =
{
{ "ImagehlpApiVersionEx", (LPVOID*)&_ImagehlpApiVersionEx },
{ "StackWalk", (LPVOID*)&_StackWalk },
{ "SymGetModuleInfo", (LPVOID*)&_SymGetModuleInfo },
{ "SymFunctionTableAccess", (LPVOID*)&_SymFunctionTableAccess },
{ "SymGetSymFromAddr", (LPVOID*)&_SymGetSymFromAddr },
{ "SymInitialize", (LPVOID*)&_SymInitialize },
{ "SymUnDName", (LPVOID*)&_SymUnDName },
{ "SymLoadModule", (LPVOID*)&_SymLoadModule },
};
//
//--- Forward declarations ----------------------------------------------------
//
static int Dummy1();
static int Dummy2();
/****************************************************************************
* Dummy1 *
*--------*
* Description:
* A placeholder function used to determine if addresses being retrieved
* are for functions in this compilation unit or not.
*
* WARNING!! This function must be the first function in this
* compilation unit
****************************************************************************/
static int Dummy1()
{
return 1;
}
/****************************************************************************
* IsWin95 *
*---------*
* Description:
* Are we running on Win95 or not. Some of the logic contained here
* differs on Windows 9x.
*
* Return:
* TRUE - If we're running on a Win 9x platform
* FALSE - If we're running on a non-Win 9x platform
****************************************************************************/
static BOOL IsWin95()
{
return GetVersion() & 0x80000000;
}
/****************************************************************************
* MagicInit *
*-----------*
* Description:
* Initializes the symbol loading code. Currently called (if necessary)
* at the beginning of each method that might need ImageHelp to be
* loaded.
****************************************************************************/
void MagicInit()
{
if (g_fLoadedImageHlp || g_fLoadedImageHlpFailed)
{
return;
}
g_hProcess = GetCurrentProcess();
//
// Try to load imagehlp.dll
//
g_hinstImageHlp = LoadLibraryA("imagehlp.dll");
_ASSERT(g_hinstImageHlp);
if (NULL == g_hinstImageHlp)
{
g_fLoadedImageHlpFailed = TRUE;
return;
}
//
// Try to get the API entrypoints in imagehlp.dll
//
for (int i = 0; i < COUNT_OF(ailFuncList); i++)
{
*(ailFuncList[i].ppvfn) = GetProcAddress(
g_hinstImageHlp,
ailFuncList[i].pszFnName);
_ASSERT(*(ailFuncList[i].ppvfn));
if (!*(ailFuncList[i].ppvfn))
{
g_fLoadedImageHlpFailed = TRUE;
return;
}
}
API_VERSION AppVersion = { 4, 0, API_VERSION_NUMBER, 0 };
LPAPI_VERSION papiver = _ImagehlpApiVersionEx(&AppVersion);
//
// We assume any version 4 or greater is OK.
//
_ASSERT(papiver->Revision >= 4);
if (papiver->Revision < 4)
{
g_fLoadedImageHlpFailed = TRUE;
return;
}
g_fLoadedImageHlp = TRUE;
//
// Initialize imagehlp.dll
//
_SymInitialize(g_hProcess, NULL, FALSE);
return;
}
/****************************************************************************
* FillSymbolInfo *
*----------------*
* Description:
* Fills in a __SYMBOL_INFO structure
****************************************************************************/
void FillSymbolInfo
(
__SYMBOL_INFO *psi,
DWORD dwAddr
)
{
if (!g_fLoadedImageHlp)
{
return;
}
_ASSERT(psi);
memset(psi, 0, sizeof(__SYMBOL_INFO));
IMAGEHLP_MODULE mi;
mi.SizeOfStruct = sizeof(mi);
if (!_SymGetModuleInfo(g_hProcess, dwAddr, &mi))
{
strncpy(psi->achModule, "<no module>", sizeof(psi->achModule)-1);
}
else
{
strncpy(psi->achModule, mi.ModuleName, sizeof(psi->achModule)-1);
strupr(psi->achModule);
}
CHAR rgchUndec[256];
CHAR * pszSymbol = NULL;
// Name field of IMAGEHLP_SYMBOL is dynamically sized.
// Pad with space for 255 characters.
union
{
CHAR rgchSymbol[sizeof(IMAGEHLP_SYMBOL) + 255];
IMAGEHLP_SYMBOL sym;
};
__try
{
sym.SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
sym.Address = dwAddr;
sym.MaxNameLength = 255;
if (_SymGetSymFromAddr(g_hProcess, dwAddr, &psi->dwOffset, &sym))
{
pszSymbol = sym.Name;
if (_SymUnDName(&sym, rgchUndec, COUNT_OF(rgchUndec)-1))
{
pszSymbol = rgchUndec;
}
}
else
{
pszSymbol = "<no symbol>";
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
pszSymbol = "<EX: no symbol>";
psi->dwOffset = dwAddr - mi.BaseOfImage;
}
strncpy(psi->achSymbol, pszSymbol, COUNT_OF(psi->achSymbol)-1);
}
/****************************************************************************
* FunctionTableAccess *
*---------------------*
* Description:
* Helper for imagehlp's StackWalk API.
****************************************************************************/
LPVOID __stdcall FunctionTableAccess
(
HANDLE hProcess,
DWORD dwPCAddr
)
{
return _SymFunctionTableAccess( hProcess, dwPCAddr );
}
/****************************************************************************
* GetModuleBase *
*---------------*
* Description:
* Helper for imagehlp's StackWalk API. Retrieves the base address of
* the module containing the giving virtual address.
*
* NOTE: If the module information for the given module hasnot yet been
* loaded, then it is loaded on this call.
*
* Return:
* Base virtual address where the module containing ReturnAddress is
* loaded, or 0 if the address cannot be determined.
****************************************************************************/
DWORD __stdcall GetModuleBase
(
HANDLE hProcess,
DWORD dwAddr
)
{
IMAGEHLP_MODULE ModuleInfo;
ModuleInfo.SizeOfStruct = sizeof(ModuleInfo);
if (_SymGetModuleInfo(hProcess, dwAddr, &ModuleInfo))
{
return ModuleInfo.BaseOfImage;
}
else
{
MEMORY_BASIC_INFORMATION mbi;
if (VirtualQueryEx(hProcess, (LPVOID)dwAddr, &mbi, sizeof(mbi)))
{
if (IsWin95() || (mbi.Type & MEM_IMAGE))
{
char achFile[MAX_PATH] = {0};
DWORD cch;
cch = GetModuleFileNameA(
(HINSTANCE)mbi.AllocationBase,
achFile,
MAX_PATH);
// Ignore the return code since we can't do anything with it.
_SymLoadModule(
hProcess,
NULL,
((cch) ? achFile : NULL),
NULL,
(DWORD)mbi.AllocationBase,
0);
return (DWORD)mbi.AllocationBase;
}
}
}
return 0;
}
/****************************************************************************
* GetStackBacktrace *
*-------------------*
* Description:
* Gets a stacktrace of the current stack, including symbols.
*
* Return:
* The number of elements actually retrieved.
****************************************************************************/
UINT GetStackBacktrace
(
UINT ifrStart, // How many stack elements to skip before starting.
UINT cfrTotal, // How many elements to trace after starting.
DWORD *pdwEip, // Array to be filled with stack addresses.
__SYMBOL_INFO *psiSymbols // This array is filled with symbol information.
// It should be big enough to hold cfrTotal elts.
// If NULL, no symbol information is stored.
)
{
DWORD * pdw = pdwEip;
__SYMBOL_INFO * psi = psiSymbols;
MagicInit();
memset(pdwEip, 0, cfrTotal * sizeof(DWORD));
if (psiSymbols)
{
memset(psiSymbols, 0, cfrTotal * sizeof(__SYMBOL_INFO));
}
if (!g_fLoadedImageHlp)
{
return 0;
}
HANDLE hThread;
hThread = GetCurrentThread();
CONTEXT context;
context.ContextFlags = CONTEXT_FULL;
if (GetThreadContext(hThread, &context))
{
STACKFRAME stkfrm;
memset(&stkfrm, 0, sizeof(STACKFRAME));
stkfrm.AddrPC.Mode = AddrModeFlat;
DWORD dwMachType;
#if defined(_M_IX86)
dwMachType = IMAGE_FILE_MACHINE_I386;
stkfrm.AddrPC.Offset = context.Eip; // Program Counter
stkfrm.AddrStack.Offset = context.Esp; // Stack Pointer
stkfrm.AddrStack.Mode = AddrModeFlat;
stkfrm.AddrFrame.Offset = context.Ebp; // Frame Pointer
stkfrm.AddrFrame.Mode = AddrModeFlat;
#elif defined(_M_MRX000)
dwMachType = IMAGE_FILE_MACHINE_R4000;
stkfrm.AddrPC.Offset = context.Fir; // Program Counter
#elif defined(_M_ALPHA)
dwMachType = IMAGE_FILE_MACHINE_ALPHA;
stkfrm.AddrPC.Offset = (unsigned long) context.Fir; // Program Counter
#elif defined(_M_PPC)
dwMachType = IMAGE_FILE_MACHINE_POWERPC;
stkfrm.AddrPC.Offset = context.Iar; // Program Counter
#elif
#error("Unknown Target Machine");
#endif
// Ignore this function (GetStackBackTrace)
ifrStart += 1;
for (UINT i = 0; i < ifrStart + cfrTotal; i++)
{
if (!_StackWalk(dwMachType,
g_hProcess,
hThread,
&stkfrm,
&context,
NULL,
FunctionTableAccess,
GetModuleBase,
NULL))
{
break;
}
if (i >= ifrStart &&
((void*)stkfrm.AddrPC.Offset < (void*)Dummy1 ||
(void*)stkfrm.AddrPC.Offset > (void*)Dummy2))
{
*pdw++ = stkfrm.AddrPC.Offset;
if (psi)
{
FillSymbolInfo(psi++, stkfrm.AddrPC.Offset);
}
}
}
}
return pdw - pdwEip;
}
/****************************************************************************
* GetStringFromSymbolInfo *
*-------------------------*
* Description:
* Actually prints the info into the string for the symbol.
****************************************************************************/
void GetStringFromSymbolInfo
(
DWORD dwAddr,
__SYMBOL_INFO *psi, // @parm Pointer to __SYMBOL_INFO. Can be NULL.
CHAR *pszString // @parm Place to put string.
)
{
_ASSERT(pszString);
// <module>! <symbol> + 0x<offset> 0x<addr>\n
if (psi)
{
wsprintfA(pszString,
"%s! %s + 0x%X (0x%08X)",
(psi->achModule[0]) ? psi->achModule : "<no module>",
(psi->achSymbol[0]) ? psi->achSymbol : "<no symbol>",
psi->dwOffset,
dwAddr);
}
else
{
wsprintfA(pszString, "<symbols not available> (0x%08X)", dwAddr);
}
_ASSERT(strlen(pszString) < cchMaxAssertStackLevelStringLen);
}
/****************************************************************************
* GetStringFromStackLevels *
*--------------------------*
* Description:
* Retrieves a string from the stack frame. If more than one frame, they
* are separated by newlines
****************************************************************************/
void GetStringFromStackLevels
(
UINT ifrStart, // @parm How many stack elements to skip before starting.
UINT cfrTotal, // @parm How many elements to trace after starting.
// Can't be more than cfrMaxAssertStackLevels.
CHAR *pszString // @parm Place to put string.
// Max size will be cchMaxAssertStackLevelStringLen * cfrTotal.
)
{
_ASSERT(pszString);
_ASSERT(cfrTotal < cfrMaxAssertStackLevels);
*pszString = '\0';
if (cfrTotal == 0)
{
return;
}
DWORD rgdwStackAddrs[cfrMaxAssertStackLevels];
__SYMBOL_INFO rgsi[cfrMaxAssertStackLevels];
// Ignore this function (GetStringFromStackLevels)
ifrStart += 1;
UINT uiRetrieved =
GetStackBacktrace(ifrStart, cfrTotal, rgdwStackAddrs, rgsi);
// First level
CHAR aszLevel[cchMaxAssertStackLevelStringLen];
GetStringFromSymbolInfo(rgdwStackAddrs[0], &rgsi[0], aszLevel);
strcpy(pszString, aszLevel);
// Additional levels
for (UINT i = 1; i < uiRetrieved; ++i)
{
strcat(pszString, "\n");
GetStringFromSymbolInfo(rgdwStackAddrs[i],
&rgsi[i], aszLevel);
strcat(pszString, aszLevel);
}
_ASSERT(strlen(pszString) <= cchMaxAssertStackLevelStringLen * cfrTotal);
}
/****************************************************************************
* GetAddrFromStackLevel *
*-----------------------*
* Description:
* Retrieves the address of the next instruction to be executed on a
* particular stack frame.
*
* Return:
* The address of the next instruction,
* 0 if there's an error.
****************************************************************************/
DWORD GetAddrFromStackLevel
(
UINT ifrStart // How many stack elements to skip before starting.
)
{
MagicInit();
if (!g_fLoadedImageHlp)
{
return 0;
}
HANDLE hThread;
hThread = GetCurrentThread();
CONTEXT context;
context.ContextFlags = CONTEXT_FULL;
if (GetThreadContext(hThread, &context))
{
STACKFRAME stkfrm;
memset(&stkfrm, 0, sizeof(STACKFRAME));
stkfrm.AddrPC.Mode = AddrModeFlat;
DWORD dwMachType;
#if defined(_M_IX86)
dwMachType = IMAGE_FILE_MACHINE_I386;
stkfrm.AddrPC.Offset = context.Eip; // Program Counter
stkfrm.AddrStack.Offset = context.Esp; // Stack Pointer
stkfrm.AddrStack.Mode = AddrModeFlat;
stkfrm.AddrFrame.Offset = context.Ebp; // Frame Pointer
stkfrm.AddrFrame.Mode = AddrModeFlat;
#elif defined(_M_MRX000)
dwMachType = IMAGE_FILE_MACHINE_R4000;
stkfrm.AddrPC.Offset = context.Fir; // Program Counter
#elif defined(_M_ALPHA)
dwMachType = IMAGE_FILE_MACHINE_ALPHA;
stkfrm.AddrPC.Offset = (unsigned long) context.Fir; // Program Counter
#elif defined(_M_PPC)
dwMachType = IMAGE_FILE_MACHINE_POWERPC;
stkfrm.AddrPC.Offset = context.Iar; // Program Counter
#elif
#error("Unknown Target Machine");
#endif
// Ignore this function (GetStackBackTrace) and the one below
ifrStart += 2;
for (UINT i = 0; i < ifrStart; i++)
{
if (!_StackWalk(dwMachType,
g_hProcess,
hThread,
&stkfrm,
&context,
NULL,
FunctionTableAccess,
GetModuleBase,
NULL))
{
break;
}
}
return stkfrm.AddrPC.Offset;
}
return 0;
}
/****************************************************************************
* GetStringFromAddr *
*-------------------*
* Description:
* Returns a string from an address.
****************************************************************************/
void GetStringFromAddr
(
DWORD dwAddr,
CHAR *szString // Place to put string.
// Buffer must hold at least cchMaxAssertStackLevelStringLen.
)
{
_ASSERT(szString);
__SYMBOL_INFO si;
FillSymbolInfo(&si, dwAddr);
wsprintfA(szString,
"%s! %s + 0x%X (0x%08X)",
(si.achModule[0]) ? si.achModule : "<no module>",
(si.achSymbol[0]) ? si.achSymbol : "<no symbol>",
si.dwOffset,
dwAddr);
}
/****************************************************************************
* MagicDeinit *
*-------------*
* Description:
* Cleans up for the symbol loading code. Should be called before exit
* to free the dynamically loaded imagehlp.dll.
****************************************************************************/
void MagicDeinit(void)
{
if (g_hinstImageHlp)
{
FreeLibrary(g_hinstImageHlp);
g_hinstImageHlp = NULL;
g_fLoadedImageHlp = FALSE;
}
}
static int Dummy2()
{
return 2;
}
#endif // _WIN64
#endif // ASSERT_WITH_STACK