SymFromAddr触发访问违例:Boost Test项目C++性能分析器故障排查
简易C++性能分析器在大型Boost Test项目中触发访问违例
我基于一篇教程编写了一款简易C++性能分析器,核心是用dbghlp.h的SymFromAddr()将函数地址解析为函数名,函数地址通过_penter和_pexit汇编代码获取,以DWORD64类型传入FindFunction()。
工具在小型示例程序中正常运行,但集成到Boost Test的大型项目时触发以下错误:
Exception thrown at 0x00007FFBDBA6B699 (KernelBase.dll) in MyTest.exe: 0xC0000005: Access violation reading location 0xFFFFFFFFFFFFFFFF.
在该大型项目中,调用GetCurrentProcess()并打印输出也会抛出相同异常,但小型项目无此问题。
我曾认为问题是SymFromAddr()非线程安全,于是将其封装为线程安全单例,确保同一时间仅一个调用,但问题未解决。
C++实现代码
#include "pch.h" #include "SymbolResolverSingleton.h" #include <imagehlp.h> #include <iostream> #include <mutex> #include <strsafe.h> //Base address of the loaded module static DWORD64 dwBaseAddr = 0; //Flag to indicate the result of symbol initialization static BOOL bInitResult = FALSE; SymbolResolverSingleton::SymbolResolverSingleton(const void* pAddress) { InitSymbols(pAddress); } SymbolResolverSingleton::~SymbolResolverSingleton() = default; void SymbolResolverSingleton::InitSymbols(const void* pAddress) { //Query the memory char moduleName[MAX_PATH]; //TCHAR modShortNameBuf[MAX_PATH]; MEMORY_BASIC_INFORMATION mbi; //Get the module name where the address is available VirtualQuery((void*)pAddress, &mbi, sizeof(mbi)); GetModuleFileNameA((HMODULE)mbi.AllocationBase, moduleName, MAX_PATH); //Initialize the symbols bInitResult = SymInitialize(GetCurrentProcess(), moduleName, TRUE); //Load the module dwBaseAddr = SymLoadModule64(GetCurrentProcess(), NULL, (PCSTR)moduleName, NULL, (DWORD)mbi.AllocationBase, 0); //Set the options SymSetOptions(SymGetOptions() & ~SYMOPT_UNDNAME); } void SymbolResolverSingleton::FindFunction(DWORD64 pa, char*& szFuncName) { DWORD64 symDisplacement; if (dwBaseAddr) { //Allocate the memory for PSYMBOL_INFO TCHAR buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; memset(&buffer, 0, sizeof(buffer)); PSYMBOL_INFO pSymbolInfo = (PSYMBOL_INFO)buffer; pSymbolInfo->SizeOfStruct = sizeof(SYMBOL_INFO); pSymbolInfo->MaxNameLen = MAX_SYM_NAME; //Get the name of the symbol using the address BOOL bResult = SymFromAddr(GetCurrentProcess(), pa, &symDisplacement, pSymbolInfo); LPVOID lpMsgBuf; LPVOID lpDisplayBuf; //If symbol is found, then get its undecorated name.The name could be a decorated one //as the name mangling schemes are deployed if (bResult) { char undName[1024]; if (UnDecorateSymbolName(pSymbolInfo->Name, undName, sizeof(undName), UNDNAME_NO_MS_KEYWORDS | UNDNAME_NO_ACCESS_SPECIFIERS | UNDNAME_NO_FUNCTION_RETURNS | UNDNAME_NO_ALLOCATION_MODEL | UNDNAME_NO_ALLOCATION_LANGUAGE | UNDNAME_NO_ARGUMENTS | UNDNAME_NO_SPECIAL_SYMS | UNDNAME_NO_MEMBER_TYPE)) { //Ignore the unnecessary calls emulated from std library if (strstr(undName, "std::")) { //Skip the symbol } else { strcpy_s(undName, pSymbolInfo->Name); szFuncName = new char[strlen(undName) + 2]; strcpy_s(szFuncName, strlen(undName) + 1, undName); } } } else { DWORD lastError = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, lastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL); lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, (lstrlen((LPCTSTR)lpMsgBuf) + 40) * sizeof(TCHAR)); StringCchPrintf((LPTSTR)lpDisplayBuf, LocalSize(lpDisplayBuf) / sizeof(TCHAR), TEXT("failed with error %d: %s"), lastError, lpMsgBuf); } } } //define static methods outside of class SymbolResolverSingleton* SymbolResolverSingleton::pinstance_{ nullptr }; std::mutex SymbolResolverSingleton::mutex_; SymbolResolverSingleton* SymbolResolverSingleton::GetInstance(void* pAddress) { if (pinstance_ == nullptr) { std::lock_guard<std::mutex> lock(mutex_); if (pinstance_ == nullptr) { pinstance_ = new SymbolResolverSingleton(pAddress); } } return pinstance_; }
头文件代码
#pragma once #include <mutex> class SymbolResolverSingleton { static SymbolResolverSingleton* pinstance_; static std::mutex mutex_; void InitSymbols(const void* pAddress); protected: SymbolResolverSingleton(const void* pAddress); ~SymbolResolverSingleton(); public: // prevent cloning SymbolResolverSingleton(SymbolResolverSingleton& other) = delete; //prevent assigning void operator= (const SymbolResolverSingleton&) = delete; static SymbolResolverSingleton* GetInstance(void* pAddress); void FindFunction(DWORD64 pa, char*& szFuncName); };
汇编代码
extern TimeStart:PROC ; from dll extern TimeStop:PROC ; from dll .code PUSHREGS macro push rax push rcx push rdx push r8 push r9 push r10 push r11 endm POPREGS macro pop r11 pop r10 pop r9 pop r8 pop rdx pop rcx pop rax endm _penter PROC ; Save non-volatile registers PUSHREGS sub rsp, 32 ; Allocate space for args that do not fit in registers if needed ; Get the return address of the function mov rcx,rsp mov rcx,qword ptr[rcx+88] ; go up 88 bytes (7 registers * 8 bytes + 32 bytes) sub rcx,5 call TimeStart ; Restore non-volatile registers add rsp, 32 ; Adjust stack pointer to deallocate space for xmm registers if needed POPREGS ret _penter ENDP _pexit PROC ; Save non-volatile registers PUSHREGS sub rsp, 32 ; Allocate space for xmm registers if needed ; Get the return address of the function mov rcx,rsp mov rcx,qword ptr[rcx+88] ; go up 88 bytes (7 registers * 8 bytes + 32 bytes) call TimeStop ; Restore non-volatile registers add rsp, 32 ; Adjust stack pointer to deallocate space for xmm registers if needed POPREGS ret _pexit ENDP END
内容的提问来源于stack exchange,提问作者foskimova
相关产品推荐
相关产品推荐

