自分の下位コードライブラリ(8)——関数呼び出しスタックを取得する

18684 ワード

今日はツールクラスを置いて、現在のスレッド/すべてのスレッドの現在の関数呼び出しスタックを取得します.
主にlogを打つときに呼び出しスタックを一緒にファイルに出力できるようにするためです.
PS:正しいロゴを出すにはPDBファイルに依存する
VSのようなアウトプットウィンドウへの$CALLSTACKの出力
主にdbghelpのいくつかのAPIを使用しています
参考:
http://www.cnblogs.com/zplutor/archive/2011/04/23/2025549.html
http://www.codeproject.com/KB/threads/StackWalker/StackWalker.zip
不要な機能をいくつか剥離し、2つのインタフェースしか提供していません.
//現在のプロセスで指定したスレッド番号のスレッド呼び出しスタックを印刷する
BOOL GetCallstack(std::vector<:string>& callStacks, DWORD dwThreadId);
//現在のプロセスのすべてのスレッドの呼び出しスタックBOOL GetCallstack(std::vector<:string>&callStacks);
次のコードを直接入力します.
CStackWalker.h:
#ifndef _CStackWalker_h_
#define _CStackWalker_h_

#include 

#include 
#include 

#include 

#include 
#include 

#undef MODULEENTRY32
#undef PMODULEENTRY32
#undef LPMODULEENTRY32

class StackWalkerInterface
{
public:
	StackWalkerInterface();

	~StackWalkerInterface();

	// SymInitialize()
	typedef BOOL (__stdcall *fpSymInitialize)(
		IN HANDLE hProcess,
		IN PSTR UserSearchPath, 
		IN BOOL fInvadeProcess
		);
	fpSymInitialize m_fpSymInitialize;

	// SymCleanup()
	typedef BOOL (__stdcall *fpSymCleanup)(
		IN HANDLE hProcess
		);
	fpSymCleanup m_fpSymCleanup;

	// StackWalk64()
	typedef BOOL (__stdcall *fpStackWalk64)(
		DWORD MachineType, 
		HANDLE hProcess,
		HANDLE hThread, 
		LPSTACKFRAME64 StackFrame, 
		PVOID ContextRecord,
		PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
		PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
		PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
		PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
		);
	fpStackWalk64 m_fpStackWalk64;

	// SymSetContext()
	typedef BOOL (__stdcall *fpSymSetContext)(
		HANDLE hProcess,
		PIMAGEHLP_STACK_FRAME StackFrame,
		PIMAGEHLP_CONTEXT Context
		);
	fpSymSetContext m_fpSymSetContext;

	// SymEnumSymbols()
	typedef BOOL (__stdcall *fpSymEnumSymbols)(
		HANDLE hProcess,
		ULONG64 BaseOfDll,
		PCTSTR Mask,
		PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback,
		PVOID UserContext
		);
	fpSymEnumSymbols m_fpSymEnumSymbols;

	// SymGetOptions()
	typedef DWORD (__stdcall *fpSymGetOptions)();
	fpSymGetOptions m_fpSymGetOptions;

	// SymSetOptions()
	typedef DWORD (__stdcall *fpSymSetOptions)(IN DWORD SymOptions);
	fpSymSetOptions m_fpSymSetOptions;

	// SymFunctionTableAccess64()
	typedef PVOID (__stdcall *fpSymFunctionTableAccess64)(
		HANDLE hProcess,
		DWORD64 AddrBase
		);
	fpSymFunctionTableAccess64 m_fpSymFunctionTableAccess64;

	// SymGetLineFromAddr64()
	typedef BOOL (__stdcall *fpSymGetLineFromAddr64)(
		IN HANDLE hProcess,
		IN DWORD64 dwAddr,
		OUT PDWORD pdwDisplacement,
		OUT PIMAGEHLP_LINE64 Line
		);
	fpSymGetLineFromAddr64 m_fpSymGetLineFromAddr64;

	// SymGetModuleBase64()
	typedef DWORD64 (__stdcall *fpSymGetModuleBase64)(
		IN HANDLE hProcess,
		IN DWORD64 dwAddr
		);
	fpSymGetModuleBase64 m_fpSymGetModuleBase64;

	// SymGetSymFromAddr64()
	typedef BOOL (__stdcall *fpSymGetSymFromAddr64)(
		IN HANDLE hProcess,
		IN DWORD64 dwAddr,
		OUT PDWORD64 pdwDisplacement,
		OUT PIMAGEHLP_SYMBOL64 Symbol );
	fpSymGetSymFromAddr64 m_fpSymGetSymFromAddr64;

	// UnDecorateSymbolName()
	typedef DWORD (__stdcall WINAPI *fpUnDecorateSymbolName)(
		PCSTR DecoratedName,
		PSTR UnDecoratedName,
		DWORD UndecoratedLength,
		DWORD Flags );
	fpUnDecorateSymbolName m_fpUnDecorateSymbolName;

	// SymLoadModule64()
	typedef DWORD64 (__stdcall *fpSymLoadModule64)(
		IN HANDLE hProcess,
		IN HANDLE hFile,
		IN PSTR ImageName,
		IN PSTR ModuleName,
		IN DWORD64 BaseOfDll,
		IN DWORD SizeOfDll
		);
	fpSymLoadModule64 m_fpSymLoadModule64;

	
	
	// CreateToolhelp32Snapshot()
	typedef HANDLE (__stdcall *fpCreateToolhelp32Snapshot)(
		DWORD dwFlags, 
		DWORD th32ProcessID
		);
	fpCreateToolhelp32Snapshot m_fpCreateToolhelp32Snapshot;
	
	// Module32First()
	typedef BOOL (__stdcall *fpModule32First)(
		HANDLE hSnapshot, 
		LPMODULEENTRY32 lpme
		);
	fpModule32First m_fpModule32First;

	// Module32Next()
	typedef BOOL (__stdcall *fpModule32Next)(
		HANDLE hSnapshot, 
		LPMODULEENTRY32 lpme
		);
	fpModule32Next m_fpModule32Next;

	// Thread32First()
	typedef BOOL (__stdcall *fpThread32First)(
		HANDLE hSnapshot, 
		LPTHREADENTRY32 lpme
		);
	fpThread32First m_fpThread32First;

	// Thread32Next()
	typedef BOOL (__stdcall *fpThread32Next)(
		HANDLE hSnapshot, 
		LPTHREADENTRY32 lpme
		);
	fpThread32Next m_fpThread32Next;

	bool Init();
	bool CanUse();
	
private:
#ifdef UNICODE
	HMODULE LoadDll(const WCHAR *pDllName);
#else
	HMODULE LoadDll(const char *pDllName);
#endif	
	FARPROC LoadAPI(HMODULE dllHandle, const char *pAPIName);

	HMODULE m_hDbhHelp;
	HMODULE m_hToolhelp;

	bool m_bCanUse;
};


const DWORD STACKWALK_MAX_NAMELEN = 1024;

class CStackWalker
{
public:
	CStackWalker(void);
	~CStackWalker(void);

	BOOL GetCallstack(std::vector<:string>& callStacks, DWORD dwThreadId);
	BOOL GetCallstack(std::vector<:string>& callStacks);

private:
	StackWalkerInterface m_swi;

	HANDLE m_hProcess;
	DWORD m_dwProcessId;
	HANDLE m_hThread;
	DWORD m_dwThreadId;

	//      
	char m_StackInfo[STACKWALK_MAX_NAMELEN];

	//    -     
	std::map m_ModuleList;
	BOOL GetModuleList();
};

extern CStackWalker g_StackWalker;

#endif

CStackWalker.cpp:
#include "CStackWalker.h"

StackWalkerInterface::StackWalkerInterface()
{
	m_hDbhHelp = NULL;
	m_hToolhelp = NULL;

	m_fpCreateToolhelp32Snapshot = NULL;
	m_fpModule32First = NULL;
	m_fpModule32Next = NULL;
	m_fpThread32First = NULL;
	m_fpThread32Next = NULL;

	m_fpSymInitialize = NULL;
	m_fpSymCleanup = NULL;

	m_fpStackWalk64 = NULL;
	m_fpSymSetContext = NULL;
	m_fpSymEnumSymbols = NULL;

	m_fpSymGetOptions = NULL;
	m_fpSymSetOptions = NULL;

	m_fpSymGetModuleBase64 = NULL;

	m_fpSymFunctionTableAccess64 = NULL;

	m_fpSymGetLineFromAddr64 = NULL;

	m_fpSymGetSymFromAddr64 = NULL;

	m_fpSymLoadModule64 = NULL;

	m_fpUnDecorateSymbolName = NULL;

	m_bCanUse = false;
}

StackWalkerInterface::~StackWalkerInterface()
{
	if (m_hDbhHelp != NULL)
	{
		FreeLibrary(m_hDbhHelp);
		m_hDbhHelp = NULL;
	}
	if (m_hToolhelp != NULL)
	{
		FreeLibrary(m_hToolhelp);
		m_hToolhelp = NULL;
	}
}

#ifdef UNICODE
HMODULE StackWalkerInterface::LoadDll(const WCHAR *pDllName)
#else
HMODULE StackWalkerInterface::LoadDll(const char *pDllName)
#endif
{
	HMODULE dllHandle = LoadLibrary(pDllName);
	if (NULL != dllHandle)
	{
		return dllHandle;
	}
	else
	{
		printf("StackWalkerInterface LoadLibrary \"%s\" Error code 0x%08x!
", pDllName, GetLastError()); return NULL; } } FARPROC StackWalkerInterface::LoadAPI(HMODULE dllHandle, const char *pAPIName) { FARPROC apiAddr = GetProcAddress(dllHandle, pAPIName); if (NULL != apiAddr) { return apiAddr; } else { printf("StackWalkerInterface GetProcAddress \"%s\" Error code 0x%08x!
", pAPIName, GetLastError()); return NULL; } } bool StackWalkerInterface::Init() { bool res = false; // dbghelp.dll >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> m_hDbhHelp = LoadDll(_T("dbghelp.dll")); if (NULL == m_hDbhHelp) { return false; } m_fpSymInitialize = (fpSymInitialize) LoadAPI(m_hDbhHelp, "SymInitialize" ); if (NULL == m_fpSymInitialize) { return false; } m_fpSymCleanup = (fpSymCleanup) LoadAPI(m_hDbhHelp, "SymCleanup" ); if (NULL == m_fpSymCleanup) { return false; } m_fpStackWalk64 = (fpStackWalk64) LoadAPI(m_hDbhHelp, "StackWalk64" ); if (NULL == m_fpStackWalk64) { return false; } m_fpSymSetContext = (fpSymSetContext) LoadAPI(m_hDbhHelp, "SymSetContext" ); if (NULL == m_fpSymSetContext) { return false; } m_fpSymEnumSymbols = (fpSymEnumSymbols) LoadAPI(m_hDbhHelp, "SymEnumSymbols" ); if (NULL == m_fpSymEnumSymbols) { return false; } m_fpSymGetOptions = (fpSymGetOptions) LoadAPI(m_hDbhHelp, "SymGetOptions" ); if (NULL == m_fpSymGetOptions) { return false; } m_fpSymSetOptions = (fpSymSetOptions) LoadAPI(m_hDbhHelp, "SymSetOptions" ); if (NULL == m_fpSymSetOptions) { return false; } m_fpSymFunctionTableAccess64 = (fpSymFunctionTableAccess64) LoadAPI(m_hDbhHelp, "SymFunctionTableAccess64" ); if (NULL == m_fpSymFunctionTableAccess64) { return false; } m_fpSymGetLineFromAddr64 = (fpSymGetLineFromAddr64) LoadAPI(m_hDbhHelp, "SymGetLineFromAddr64" ); if (NULL == m_fpSymGetLineFromAddr64) { return false; } m_fpSymGetModuleBase64 = (fpSymGetModuleBase64) LoadAPI(m_hDbhHelp, "SymGetModuleBase64" ); if (NULL == m_fpSymGetModuleBase64) { return false; } m_fpSymGetSymFromAddr64 = (fpSymGetSymFromAddr64) LoadAPI(m_hDbhHelp, "SymGetSymFromAddr64" ); if (NULL == m_fpSymGetSymFromAddr64) { return false; } m_fpUnDecorateSymbolName = (fpUnDecorateSymbolName) LoadAPI(m_hDbhHelp, "UnDecorateSymbolName" ); if (NULL == m_fpUnDecorateSymbolName) { return false; } m_fpSymLoadModule64 = (fpSymLoadModule64) LoadAPI(m_hDbhHelp, "SymLoadModule64" ); if (NULL == m_fpSymLoadModule64) { return false; } // kernel32.dll >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> m_hToolhelp = LoadDll(_T("kernel32.dll")); if (NULL == m_hToolhelp) { return false; } m_fpCreateToolhelp32Snapshot = (fpCreateToolhelp32Snapshot)LoadAPI(m_hToolhelp, "CreateToolhelp32Snapshot"); if (NULL == m_fpCreateToolhelp32Snapshot) { return false; } m_fpModule32First = (fpModule32First) LoadAPI(m_hToolhelp, "Module32First"); if (NULL == m_fpModule32First) { return false; } m_fpModule32Next = (fpModule32Next) LoadAPI(m_hToolhelp, "Module32Next"); if (NULL == m_fpModule32Next) { return false; } m_fpThread32First = (fpThread32First) LoadAPI(m_hToolhelp, "Thread32First"); if (NULL == m_fpThread32First) { return false; } m_fpThread32Next = (fpThread32Next) LoadAPI(m_hToolhelp, "Thread32Next"); if (NULL == m_fpThread32Next) { return false; } m_bCanUse = true; return true; } bool StackWalkerInterface::CanUse() { return m_bCanUse; } CStackWalker::CStackWalker(void) { m_hProcess = GetCurrentProcess(); m_dwProcessId = GetCurrentProcessId(); BOOL res = m_swi.Init(); if (!res) { printf("StackWalkerInterface Init() Error!
"); return; } res = m_swi.m_fpSymInitialize(m_hProcess, NULL, TRUE); if (!res) { printf("SymInitialize() Error code 0x%08x!
", GetLastError()); return; } DWORD symOptions = m_swi.m_fpSymGetOptions(); symOptions |= SYMOPT_LOAD_LINES; symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS; symOptions = m_swi.m_fpSymSetOptions(symOptions); GetModuleList(); } CStackWalker::~CStackWalker(void) { m_swi.m_fpSymCleanup(m_hProcess); m_ModuleList.clear(); } BOOL CStackWalker::GetModuleList() { m_ModuleList.clear(); HANDLE hSnap = INVALID_HANDLE_VALUE; hSnap = m_swi.m_fpCreateToolhelp32Snapshot(TH32CS_SNAPMODULE, m_dwProcessId); if (hSnap == INVALID_HANDLE_VALUE) { printf("CreateToolhelp32Snapshot() Error code 0x%08x!
", GetLastError()); return FALSE; } MODULEENTRY32 me; memset(&me, 0, sizeof(me)); me.dwSize = sizeof(me); BOOL res = FALSE; int cnt = 0; res = m_swi.m_fpModule32First(hSnap, &me); while (res) { m_swi.m_fpSymLoadModule64(m_hProcess, 0, me.szExePath, me.szModule, (DWORD64) me.modBaseAddr, me.modBaseSize); std::string name = me.szModule; m_ModuleList[(DWORD64)me.modBaseAddr] = name; cnt++; res = m_swi.m_fpModule32Next( hSnap, &me ); } CloseHandle(hSnap); if (cnt > 0) { res = TRUE; } return res; } BOOL CStackWalker::GetCallstack(std::vector<:string>& callStacks, DWORD dwThreadId) { if (!m_swi.CanUse()) { return FALSE; } HANDLE hSnap = m_swi.m_fpCreateToolhelp32Snapshot(TH32CS_SNAPALL, m_dwProcessId); if (hSnap == INVALID_HANDLE_VALUE) { printf("CreateToolhelp32Snapshot() Error code 0x%08x!
", GetLastError()); return FALSE; } HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, dwThreadId); DWORD hCurrThreadId = ::GetCurrentThreadId(); if (hThread != NULL) { CONTEXT context; if (dwThreadId != hCurrThreadId) { SuspendThread(hThread); memset(&context, 0, sizeof(CONTEXT)); context.ContextFlags = CONTEXT_FULL; if (!GetThreadContext(hThread, &context)) { ResumeThread(hThread); return FALSE; } } else { memset(&context, 0, sizeof(CONTEXT)); context.ContextFlags = CONTEXT_FULL; __asm call x __asm x: pop eax __asm mov context.Eip, eax __asm mov context.Ebp, ebp __asm mov context.Esp, esp } STACKFRAME64 stackframe; memset(&stackframe, 0, sizeof(stackframe)); DWORD imageType; #ifdef _M_IX86 imageType = IMAGE_FILE_MACHINE_I386; stackframe.AddrPC.Offset = context.Eip; stackframe.AddrPC.Mode = AddrModeFlat; stackframe.AddrFrame.Offset = context.Ebp; stackframe.AddrFrame.Mode = AddrModeFlat; stackframe.AddrStack.Offset = context.Esp; stackframe.AddrStack.Mode = AddrModeFlat; #elif _M_X64 imageType = IMAGE_FILE_MACHINE_AMD64; stackframe.AddrPC.Offset = context.Rip; stackframe.AddrPC.Mode = AddrModeFlat; stackframe.AddrFrame.Offset = context.Rsp; stackframe.AddrFrame.Mode = AddrModeFlat; stackframe.AddrStack.Offset = context.Rsp; stackframe.AddrStack.Mode = AddrModeFlat; #else #error "Not Supported!" #endif char sysbuff[sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN]; IMAGEHLP_SYMBOL64 *pSym = (IMAGEHLP_SYMBOL64 *)sysbuff; memset(pSym, 0x00, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN); pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); pSym->MaxNameLength = STACKWALK_MAX_NAMELEN; IMAGEHLP_LINE64 line; memset(&line, 0x00, sizeof(line)); line.SizeOfStruct = sizeof(line); while (true) { if (!m_swi.m_fpStackWalk64(imageType, m_hProcess, hThread, &stackframe, &context, NULL, m_swi.m_fpSymFunctionTableAccess64, m_swi.m_fpSymGetModuleBase64, NULL) ) { printf("StackWalk64() Error code 0x%08x!
", GetLastError()); return FALSE; } if (stackframe.AddrPC.Offset == stackframe.AddrReturn.Offset) { break; } if (stackframe.AddrPC.Offset != 0) { memset(m_StackInfo, 0x00, sizeof(char) * STACKWALK_MAX_NAMELEN); // m_ModuleList DWORD64 moduleBase = m_swi.m_fpSymGetModuleBase64(m_hProcess, stackframe.AddrPC.Offset); std::map::iterator module = m_ModuleList.find(moduleBase); if (module != m_ModuleList.end()) { const char *name = module->second.c_str(); strcat_s(m_StackInfo, STACKWALK_MAX_NAMELEN, name); } else { strcat_s(m_StackInfo, STACKWALK_MAX_NAMELEN, "unknow module"); } strcat_s(m_StackInfo, STACKWALK_MAX_NAMELEN, ": "); // DWORD dwDisplacementFromLine; if (m_swi.m_fpSymGetLineFromAddr64(m_hProcess, stackframe.AddrPC.Offset, &dwDisplacementFromLine, &line)) { char name[50] = {0}; _itoa_s(line.LineNumber, name, 50, 10); strcat_s(m_StackInfo, STACKWALK_MAX_NAMELEN, line.FileName); strcat_s(m_StackInfo, STACKWALK_MAX_NAMELEN, "("); strcat_s(m_StackInfo, STACKWALK_MAX_NAMELEN, name); strcat_s(m_StackInfo, STACKWALK_MAX_NAMELEN, ")"); } else { strcat_s(m_StackInfo, STACKWALK_MAX_NAMELEN, "unknow file"); } strcat_s(m_StackInfo, STACKWALK_MAX_NAMELEN, ": "); // DWORD64 dwDisplacementFromFunc; if (m_swi.m_fpSymGetSymFromAddr64(m_hProcess, stackframe.AddrPC.Offset, &dwDisplacementFromFunc, pSym)) { if(pSym->Name[0] == '?') { // -> char name[STACKWALK_MAX_NAMELEN] = {0}; m_swi.m_fpUnDecorateSymbolName(pSym->Name, name, STACKWALK_MAX_NAMELEN, UNDNAME_NAME_ONLY); strcat_s(m_StackInfo, STACKWALK_MAX_NAMELEN, name); } else { strcat_s(m_StackInfo, STACKWALK_MAX_NAMELEN, pSym->Name); } } else { strcat_s(m_StackInfo, STACKWALK_MAX_NAMELEN, "unknow func"); } strcat_s(m_StackInfo, STACKWALK_MAX_NAMELEN, ": "); std::string oneCallStack = m_StackInfo; callStacks.push_back(oneCallStack); } if (stackframe.AddrReturn.Offset == 0) { break; } } if (dwThreadId != hCurrThreadId) { ResumeThread(hThread); } CloseHandle(hThread); callStacks.push_back("
"); } CloseHandle(hSnap); return TRUE; } BOOL CStackWalker::GetCallstack(std::vector<:string>& callStacks) { if (!m_swi.CanUse()) { return FALSE; } HANDLE hSnap = m_swi.m_fpCreateToolhelp32Snapshot(TH32CS_SNAPALL, m_dwProcessId); if (hSnap == INVALID_HANDLE_VALUE) { return FALSE; } THREADENTRY32 te; memset(&te, 0, sizeof(te)); te.dwSize = sizeof(te); BOOL res = m_swi.m_fpThread32First(hSnap, &te); while (res) { if (te.th32OwnerProcessID == m_dwProcessId) { GetCallstack(callStacks, te.th32ThreadID); } res = m_swi.m_fpThread32Next(hSnap, &te); } CloseHandle(hSnap); return TRUE; } CStackWalker g_StackWalker;

試験手順:
2つのスレッドを作成し、各スレッドの中でFunAとFunBのループ呼び出しを行い、スレッドが1 s実行した後、現在のプロセスのすべてのスレッドの現在の呼び出しスタックを印刷します.
#include "CStackWalker.h"

DWORD WINAPI Run(LPVOID lpParam);
void FunB();
void FunA(int a);

int _tmain(int argc, _TCHAR* argv[])
{
	HANDLE hThread[2] = {0};
	for (int i = 0; i < 2; i++)
	{
		hThread[i] = CreateThread(NULL, 0, Run, 0, 0, NULL);
	}

	Sleep(1000);

	std::vector<:string> callStacks;
	g_StackWalker.GetCallstack(callStacks);
	for (int i = 0; i < callStacks.size(); i++)
	{
		printf("%s
", callStacks[i].c_str()); } return 0; } void FunB() { Sleep(100); FunA(rand()); } void FunA(int a) { Sleep(100); FunB(); } DWORD WINAPI Run(LPVOID lpParam) { FunB(); return 0; }

テスト結果