You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 18:24:51