I am currently trying to write a simple profiler based on this tutorial. It uses SymFromAddr of the dbghlp.h library to resolve function addresses to its name. The function address is passed as a DWORD64 to Findfunction() from another function that takes it from the _penter and _pexit assembler code.
I got it working with a small example program. However, when trying to use it in a bigger project with boost test it fails with “Exception thrown at 0x00007FFBDBA6B699 (KernelBase.dll) in MyTest.exe: 0xC0000005: Access violation reading location 0xFFFFFFFFFFFFFFFF.”
The same exception is thrown if I want to print the output of GetCurrentProcess() in the large project. But the same thing works in the smaller project.
I first thought it had to do with SymFromAddr not being thread safe. Therefore, I put it in a thread safe Singleton so that there is at most one call to it at a time. Unfortunately this changed nothing.
This is my code so far:
#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_;
}
and its header:
#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);
};
foskimova is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.