C++从LPEXCEPTION_POINTERS获取调用堆栈
阅读原文时间:2023年07月11日阅读:2

#pragma once

#include
#include

struct FunctionCall
{
DWORD64 Address;
std::string ModuleName;
std::string FunctionName;
std::string FileName;
int LineNumber;

public:
FunctionCall() :
Address(0),
ModuleName(""),
FunctionName(""),
FileName(""),
LineNumber(0)
{
}

public:
static std::string GetFileName(const std::string& fullpath)
{
size_t index = fullpath.find_last_of('\\');
if (index == std::string::npos)
{
return fullpath;
}

    return fullpath.substr(index + 1);  
}  

};

class StackTracer
{
public:
static std::string GetExceptionStackTrace(LPEXCEPTION_POINTERS e);

private:
// Always return EXCEPTION_EXECUTE_HANDLER after getting the call stack
LONG ExceptionFilter(LPEXCEPTION_POINTERS e);

// return the exception message along with call stacks  
std::string GetExceptionMsg();

// Return exception code and call stack data structure so that  
// user could customize their own message format  
DWORD GetExceptionCode();  
std::vector<FunctionCall> GetExceptionCallStack();

private:
StackTracer(void);
~StackTracer(void);

// The main function to handle exception  
LONG \_\_stdcall HandleException(LPEXCEPTION\_POINTERS e);

// Work through the stack upwards to get the entire call stack  
void TraceCallStack(CONTEXT\* pContext);

private:
DWORD m_dwExceptionCode;

std::vector<FunctionCall> m\_vecCallStack;

typedef std::map<DWORD, const char\*> CodeDescMap;  
CodeDescMap m\_mapCodeDesc;

DWORD m\_dwMachineType; // Machine type matters when trace the call stack (StackWalk64)

};

#include "stdafx.h"
#include "StackTracer.h"
#include
#include

#pragma warning(push)
#pragma warning(disable : 4091)
#include
#pragma warning(pop)

#pragma comment(lib, "Dbghelp.lib")

const int CALLSTACK_DEPTH = 24;

// Translate exception code to description
#define CODE_DESCR(code) CodeDescMap::value_type(code, #code)

StackTracer::StackTracer(void)
:m_dwExceptionCode(0)
{
// Get machine type
m_dwMachineType = 0;
size_t Count = 256;
TCHAR wszProcessor[256] = { 0 };
::_tgetenv_s(&Count, wszProcessor, _T("PROCESSOR_ARCHITECTURE"));

if (wszProcessor)  
{  
    if ((!wcscmp(\_T("EM64T"), wszProcessor)) || !wcscmp(\_T("AMD64"), wszProcessor))  
    {  
        m\_dwMachineType = IMAGE\_FILE\_MACHINE\_AMD64;  
    }  
    else if (!wcscmp(\_T("x86"), wszProcessor))  
    {  
        m\_dwMachineType = IMAGE\_FILE\_MACHINE\_I386;  
    }  
}

// Exception code description  
m\_mapCodeDesc.insert(CODE\_DESCR(EXCEPTION\_ACCESS\_VIOLATION));  
m\_mapCodeDesc.insert(CODE\_DESCR(EXCEPTION\_DATATYPE\_MISALIGNMENT));  
m\_mapCodeDesc.insert(CODE\_DESCR(EXCEPTION\_BREAKPOINT));  
m\_mapCodeDesc.insert(CODE\_DESCR(EXCEPTION\_SINGLE\_STEP));  
m\_mapCodeDesc.insert(CODE\_DESCR(EXCEPTION\_ARRAY\_BOUNDS\_EXCEEDED));  
m\_mapCodeDesc.insert(CODE\_DESCR(EXCEPTION\_FLT\_DENORMAL\_OPERAND));  
m\_mapCodeDesc.insert(CODE\_DESCR(EXCEPTION\_FLT\_DIVIDE\_BY\_ZERO));  
m\_mapCodeDesc.insert(CODE\_DESCR(EXCEPTION\_FLT\_INEXACT\_RESULT));  
m\_mapCodeDesc.insert(CODE\_DESCR(EXCEPTION\_FLT\_INVALID\_OPERATION));  
m\_mapCodeDesc.insert(CODE\_DESCR(EXCEPTION\_FLT\_OVERFLOW));  
m\_mapCodeDesc.insert(CODE\_DESCR(EXCEPTION\_FLT\_STACK\_CHECK));  
m\_mapCodeDesc.insert(CODE\_DESCR(EXCEPTION\_FLT\_UNDERFLOW));  
m\_mapCodeDesc.insert(CODE\_DESCR(EXCEPTION\_INT\_DIVIDE\_BY\_ZERO));  
m\_mapCodeDesc.insert(CODE\_DESCR(EXCEPTION\_INT\_OVERFLOW));  
m\_mapCodeDesc.insert(CODE\_DESCR(EXCEPTION\_PRIV\_INSTRUCTION));  
m\_mapCodeDesc.insert(CODE\_DESCR(EXCEPTION\_IN\_PAGE\_ERROR));  
m\_mapCodeDesc.insert(CODE\_DESCR(EXCEPTION\_ILLEGAL\_INSTRUCTION));  
m\_mapCodeDesc.insert(CODE\_DESCR(EXCEPTION\_NONCONTINUABLE\_EXCEPTION));  
m\_mapCodeDesc.insert(CODE\_DESCR(EXCEPTION\_STACK\_OVERFLOW));  
m\_mapCodeDesc.insert(CODE\_DESCR(EXCEPTION\_INVALID\_DISPOSITION));  
m\_mapCodeDesc.insert(CODE\_DESCR(EXCEPTION\_GUARD\_PAGE));  
m\_mapCodeDesc.insert(CODE\_DESCR(EXCEPTION\_INVALID\_HANDLE));  
//m\_mapCodeDesc.insert(CODE\_DESCR(EXCEPTION\_POSSIBLE\_DEADLOCK));  
// Any other exception code???  

}

StackTracer::~StackTracer(void)
{
}

std::string StackTracer::GetExceptionStackTrace(LPEXCEPTION_POINTERS e)
{
StackTracer tracer;
tracer.HandleException(e);

return tracer.GetExceptionMsg();  

}

LONG StackTracer::ExceptionFilter(LPEXCEPTION_POINTERS e)
{
return HandleException(e);
}

std::string StackTracer::GetExceptionMsg()
{
std::ostringstream m_ostringstream;

// Exception Code  
CodeDescMap::iterator itc = m\_mapCodeDesc.find(m\_dwExceptionCode);

char Code\[72\];  
sprintf\_s(Code, "0x%x", m\_dwExceptionCode);

m\_ostringstream << "Exception Code: " << Code << "\\r\\n";

if (itc != m\_mapCodeDesc.end())  
{  
    m\_ostringstream << "Exception: " << itc->second << "\\r\\n";  
}               

// m\_ostringstream << "------------------------------------------------------------------\\r\\n";

// Call Stack  
std::vector<FunctionCall>::iterator itbegin = m\_vecCallStack.begin();  
std::vector<FunctionCall>::iterator itend = m\_vecCallStack.end();  
std::vector<FunctionCall>::iterator it;  
for (it = itbegin; it < itend; it++)  
{  
    std::string strModule = it->ModuleName.empty() ? "UnknownModule" : it->ModuleName;

    m\_ostringstream << strModule << " ";  
    char Addrs\[128\];  
    sprintf\_s(Addrs, "0x%llx", it->Address);  
    m\_ostringstream << Addrs;

    if (!it->FunctionName.empty())  
    {  
        m\_ostringstream << " " << it->FunctionName;  
    }

    if (!it->FileName.empty())  
    {  
        m\_ostringstream << " " << it->FileName << "\[" << it->LineNumber << "\]";  
    }

    m\_ostringstream << "\\r\\n";  
}

return m\_ostringstream.str();  

}

DWORD StackTracer::GetExceptionCode()
{
return m_dwExceptionCode;
}

std::vector StackTracer::GetExceptionCallStack()
{
return m_vecCallStack;
}

LONG __stdcall StackTracer::HandleException(LPEXCEPTION_POINTERS e)
{
m_dwExceptionCode = e->ExceptionRecord->ExceptionCode;
m_vecCallStack.clear();

HANDLE hProcess = INVALID\_HANDLE\_VALUE;

// Initializes the symbol handler  
if (!SymInitialize(GetCurrentProcess(), NULL, TRUE))  
{  
    SymCleanup(hProcess);  
    return EXCEPTION\_EXECUTE\_HANDLER;  
}

// Work through the call stack upwards.  
TraceCallStack(e->ContextRecord);

// ...  
SymCleanup(hProcess);

return(EXCEPTION\_EXECUTE\_HANDLER);  

}

// Work through the stack to get the entire call stack
void StackTracer::TraceCallStack(CONTEXT* pContext)
{
// Initialize stack frame
STACKFRAME64 sf;
memset(&sf, 0, sizeof(STACKFRAME));

#if defined(_WIN64)
sf.AddrPC.Offset = pContext->Rip;
sf.AddrStack.Offset = pContext->Rsp;
sf.AddrFrame.Offset = pContext->Rbp;
#elif defined(WIN32)
sf.AddrPC.Offset = pContext->Eip;
sf.AddrStack.Offset = pContext->Esp;
sf.AddrFrame.Offset = pContext->Ebp;
#endif
sf.AddrPC.Mode = AddrModeFlat;
sf.AddrStack.Mode = AddrModeFlat;
sf.AddrFrame.Mode = AddrModeFlat;

if (0 == m\_dwMachineType)  
    return;

// Walk through the stack frames.  
HANDLE hProcess = GetCurrentProcess();  
HANDLE hThread = GetCurrentThread();  
while (StackWalk64(m\_dwMachineType, hProcess, hThread, &sf, pContext, 0, SymFunctionTableAccess64, SymGetModuleBase64, 0))  
{  
    if (sf.AddrFrame.Offset == 0 || m\_vecCallStack.size() >= CALLSTACK\_DEPTH)  
        break;

    // 1. Get function name at the address  
    const int nBuffSize = (sizeof(SYMBOL\_INFO) + MAX\_SYM\_NAME \* sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64);  
    ULONG64 symbolBuffer\[nBuffSize\];  
    PSYMBOL\_INFO pSymbol = (PSYMBOL\_INFO)symbolBuffer;

    pSymbol->SizeOfStruct = sizeof(SYMBOL\_INFO);  
    pSymbol->MaxNameLen = MAX\_SYM\_NAME;

    FunctionCall curCall;  
    curCall.Address = sf.AddrPC.Offset;

    DWORD64 moduleBase = SymGetModuleBase64(hProcess, sf.AddrPC.Offset);  
    char ModuleName\[MAX\_PATH\];  
    if (moduleBase && GetModuleFileNameA((HINSTANCE)moduleBase, ModuleName, MAX\_PATH))  
    {  
        curCall.ModuleName = FunctionCall::GetFileName(ModuleName);  
    }

    DWORD64 dwSymDisplacement = 0;  
    if (SymFromAddr(hProcess, sf.AddrPC.Offset, &dwSymDisplacement, pSymbol))  
    {  
        curCall.FunctionName = std::string(pSymbol->Name);  
    }

    //2. get line and file name at the address  
    IMAGEHLP\_LINE64 lineInfo = { sizeof(IMAGEHLP\_LINE64) };  
    DWORD dwLineDisplacement = 0;

    if (SymGetLineFromAddr64(hProcess, sf.AddrPC.Offset, &dwLineDisplacement, &lineInfo))  
    {  
        curCall.FileName = FunctionCall::GetFileName(std::string(lineInfo.FileName));  
        curCall.LineNumber = lineInfo.LineNumber;  
    }

    // Call stack stored  
    m\_vecCallStack.push\_back(curCall);  
}  

}

  参考并优化自:https://www.codeproject.com/Articles/41923/Get-the-call-stack-when-an-exception-is-being-caug

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章