简单地使用sym*函数族导出函数栈(Based on x86)

有时我们需要导出当前线程的函数栈调用(特别是异常发生时),但是我们又不期望自己来实现这个功能,下面的源代码可以帮你解决。

导出函数栈其实就是sym*函数族的应用,该函数在dbghelp.dll中的,下面只是实现了简单的用法,使用的缺省的符号搜索路径,没有设置用户自定义的搜索路径:

这样有两方面的考虑: 

1. 这不是一个GUI程序,不想让使用者觉得麻烦,这里更合适的说法是提供了一个函数供用户调用。

2.一般说来,在编译链接时,搜索路径已经被嵌到符号文件中去了,如果有符号文件,一切OK。如果没有符号文件,抱歉,你设置了搜索路径也没有什么用处。

用法:

StackExplorer se(GetCurrentProcess());

se.DumpStack();

OK,就这么简单,如果你想接收输出信息,你可以替换其中的缺省参数。

//StackExplorer.h

#ifndef StackExplorer_h__
#define StackExplorer_h__
/********************************************************************
created: 22:8:2013   14:06
author: Shen Xiaolong

purpose: use dbghlp.dll to resolve symbol info. 
                refer to (CodeProject --> StackWalker)
*********************************************************************/


#include <string>
#include <vector>
#include <wtypes.h>
#include <winnt.h>
#include "config.h"


struct ModInfo
{
    stlString       m_name;
    PVOID           m_baseAddr;
    PVOID           m_entryPoint;   //for exe : main/winMain  for dll : DllMain
    stlString       m_entryName;
    DWORD           m_size;


    ModInfo()
    {
        m_baseAddr  = 0;
        m_entryPoint= 0;
        m_size      = 0;
    }
};


struct FuncInfo
{
    stlString       m_name;
    stlString       m_unDecorateName;
    PVOID           m_addr;
    DWORD           m_AsmOffset;
    FuncInfo()
    {
        m_addr      = 0;
        m_AsmOffset = 0;
    }
};


struct AddressInfo
{
    PVOID               m_addr;
    stlString           m_fileName;
    DWORD               m_lineNumber;
    ModInfo             m_mod;
    FuncInfo            m_func;
    AddressInfo()
    {  
        m_addr          = 0;
        m_lineNumber    = 0;
    }
};


struct CallStack
{
    stlString           m_funcName;
    stlString           m_srcFileName;
    int                 m_lineNumber;
    PVOID               m_funcAddr;
    DWORD               m_AsmOffset;
    CallStack()
    {
        m_lineNumber    = 0;
        m_funcAddr      = 0;
        m_AsmOffset     = 0;
    }
};


struct CallStackS
{
    DWORD                       m_processId;
    DWORD                       m_threadId;
    std::vector<CallStack>      m_stacks;


    CallStackS()
    {
        m_processId = 0;
        m_threadId  = 0;
    }
};


class StackExplorer
{
public:
    StackExplorer(_In_ HANDLE  hProcess = GetCurrentProcess());
    ~StackExplorer();


    BOOL resloveAddrInfo(_In_ PVOID addr, _Inout_ AddressInfo& addrInfo);
    BOOL resloveModuleInfo(_In_ PVOID addr, _Inout_ ModInfo& modInfo);
    BOOL resloveFuncInfo(_In_ PVOID addr, _Inout_ FuncInfo& funcInfo);
    BOOL resloveSourceInfo(_In_ PVOID addr, _Inout_ stlString& fileName, _Inout_ DWORD& lineNumber);


    BOOL DumpStack(_In_ HANDLE hThread = GetCurrentThread(),_In_ CONTEXT* pThrdContext=NULL,_In_ BOOL bPrint=TRUE,_Inout_ CallStackS* pOutputStks=NULL);
    void print(_In_ CallStackS const& cs);
    void print(_In_ CallStack const& sk);


private:
    HANDLE  m_hProcess;    
};


void handleException(_In_ _EXCEPTION_POINTERS * pExp );


/
// The "ugly" assembler-implementation is needed for systems before XP
// If you have a new PSDK and you only compile for XP and later, then you can use 
// the "RtlCaptureContext"
// Currently there is no define which determines the PSDK-Version... 
// So we just use the compiler-version (and assumes that the PSDK is 
// the one which was installed by the VS-IDE)


// INFO: If you want, you can use the RtlCaptureContext if you only target XP and later...
//       But I currently use it in x64/IA64 environments...
//#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400)


#if defined(_M_IX86)
    #ifdef CURRENT_THREAD_VIA_EXCEPTION
    // TODO: The following is not a "good" implementation, 
    // because the callstack is only valid in the "__except" block...
    #define GET_CURRENT_CONTEXT(c, contextFlags)            \
        do {                                                \
        memset(&c, 0, sizeof(CONTEXT));                     \
        EXCEPTION_POINTERS *pExp = NULL;                    \
        __try {                                             \
        throw 0;                                            \
    } __except( ( (pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER)) {} \
        if (pExp != NULL)                                   \
        memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT));   \
        c.ContextFlags = contextFlags;                      \
        } while(0);
    #else
    // The following should be enough for walking the callstack...
    #define GET_CURRENT_CONTEXT(c, contextFlags)    \
        do {                                        \
        memset(&c, 0, sizeof(CONTEXT));             \
        c.ContextFlags = contextFlags;              \
        __asm    call x                             \
        __asm x: pop eax                            \
        __asm    mov c.Eip, eax                     \
        __asm    mov c.Ebp, ebp                     \
        __asm    mov c.Esp, esp                     \
        } while(0);
    #endif


#else


    // The following is defined for x86 (XP and higher), x64 and IA64:
    #define GET_CURRENT_CONTEXT(c, contextFlags)    \
        do {                                        \
        memset(&c, 0, sizeof(CONTEXT));             \
        c.ContextFlags = contextFlags;              \
        RtlCaptureContext(&c);                      \
        } while(0);
    #endif


#endif // StackExplorer_h__

//StackExplorer.cpp

#include "StackExplorer.h"
#include <Psapi.h>
#include <imagehlp.h>
#include <Aclapi.h>
#include <iostream>
#include <iomanip>
#include "exptxt.h"
#include "algorithm.h"
#include "dataSet.h"
#include "functionObject.h"


/
#pragma comment(lib,"Psapi")
#pragma comment(lib,"imagehlp")


//Init symbols //
BOOL    g_bSymInited    = FALSE;
HANDLE  gs_hProcess     = NULL;


void __cdecl symClean()
{
    SymCleanup(gs_hProcess);
}


void __cdecl symInit()
{
    SymSetOptions(SymGetOptions() | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_LOAD_LINES );
    gs_hProcess = GetCurrentProcess();
    g_bSymInited  = SymInitialize(gs_hProcess, NULL, TRUE);


    atexit(symClean);
}


#define SECNAME ".CRT$XCA"
#pragma section(SECNAME,long, read) 
typedef void (__cdecl *_PVFV)();
__declspec(allocate(SECNAME)) _PVFV dummy[] = { symInit };


/


StackExplorer::StackExplorer(HANDLE  hProcess)
: m_hProcess(hProcess)
{
    if (!g_bSymInited)
    {
        symInit();
    }
}


StackExplorer::~StackExplorer()
{    
}


BOOL StackExplorer::resloveAddrInfo( _In_ PVOID addr, _Inout_ AddressInfo& addrInfo )
{
    if (!g_bSymInited)
    {
        return FALSE;
    }


    memset(&addrInfo,0,sizeof(AddressInfo));
    addrInfo.m_addr             = addr;
    resloveModuleInfo(addr,addrInfo.m_mod);    
    resloveFuncInfo(addr,addrInfo.m_func);    
    resloveSourceInfo(addr,addrInfo.m_fileName,addrInfo.m_lineNumber);
    return TRUE;
}


BOOL StackExplorer::resloveModuleInfo( _In_ PVOID addr, _Inout_ ModInfo& modInfo )
{
    if (!g_bSymInited)
    {
        return FALSE;
    }


    BOOL bRet = FALSE;


    MEMORY_BASIC_INFORMATION mbi;
    if (VirtualQuery(addr,&mbi,sizeof(mbi)))
    {
        modInfo.m_baseAddr   = mbi.AllocationBase;


        MODULEINFO mi;
        if (GetModuleInformation(m_hProcess,HMODULE(mbi.AllocationBase),&mi,sizeof(MODULEINFO)))
        {   //or SymGetModuleInfo 
            modInfo.m_size       = mi.SizeOfImage;
            modInfo.m_entryPoint = mi.EntryPoint;
            if (modInfo.m_entryPoint != addr)  //avoid fall in loop
            {
                FuncInfo fi;
                resloveFuncInfo(modInfo.m_entryPoint,fi);
                modInfo.m_entryName = fi.m_name;
            }
        }


        char moduleName[256] = {0};
        if(GetModuleBaseNameA(m_hProcess,HMODULE(mbi.AllocationBase),moduleName,256))
        {
            modInfo.m_name = moduleName;
        }


        return TRUE;
    }


    return FALSE;
}


BOOL StackExplorer::resloveFuncInfo( _In_ PVOID addr, _Inout_ FuncInfo& funcInfo )
{
    if (!g_bSymInited)
    {
        return FALSE;
    }


    DWORD64 asmDisplacement;
    ULONG64 buffer[(sizeof(SYMBOL_INFO)+MAX_SYM_NAME*sizeof(TCHAR)+sizeof(ULONG64)-1)/sizeof(ULONG64)];
    PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
    pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
    pSymbol->MaxNameLen = MAX_SYM_NAME; 


    if (SymFromAddr(m_hProcess,DWORD64(addr),&asmDisplacement,pSymbol))
    {
        funcInfo.m_addr  = PVOID(pSymbol->Address);
        funcInfo.m_name  = pSymbol->Name;        //UnDecorateSymbolName
        funcInfo.m_AsmOffset= DWORD(asmDisplacement);


        char undName[256]={0};
        UnDecorateSymbolName(pSymbol->Name,undName,sizeof(undName),UNDNAME_NAME_ONLY);
        funcInfo.m_unDecorateName = undName;
        return TRUE;
    }


    return FALSE;
}


BOOL StackExplorer::resloveSourceInfo(_In_ PVOID addr, _Inout_ std::string& fileName, _Inout_ DWORD& lineNumber)
{
    fileName    = "";
    lineNumber  = -1;
    if (!g_bSymInited)
    {
        return FALSE;
    }


    IMAGEHLP_LINE line;
    line.SizeOfStruct = sizeof(IMAGEHLP_LINE);
    DWORD lineDisplacement;
    if(SymGetLineFromAddr(m_hProcess,DWORD64(addr),&lineDisplacement,&line))
    {
        fileName    = line.FileName;
        lineNumber  = line.LineNumber;
        return TRUE;
    }


    return FALSE;
}


BOOL StackExplorer::DumpStack( _In_ HANDLE hThread ,_In_ CONTEXT* pThrdContext,_In_ BOOL bPrint,_Inout_ CallStackS* pOutputStks )
{
    if (!g_bSymInited)
    {
        return FALSE;
    }


    CallStackS cs;
    cs.m_processId  = GetProcessId(m_hProcess);
    cs.m_threadId   = GetThreadId(hThread); 


    bool bIsCurThread = hThread==GetCurrentThread();
    CONTEXT context; 
    if (pThrdContext)
    {
        context = *pThrdContext;
    }
    else
    {
        if (bIsCurThread)
        {
            GET_CURRENT_CONTEXT(context,CONTEXT_FULL);  //RtlCaptureContext(&context);
        }
        else
        {
            SuspendThread(hThread);
            GetThreadContext(hThread,&context);        
        }
    }


    STACKFRAME stackFrame;
    memset(&stackFrame, 0, sizeof(stackFrame));
    stackFrame.AddrPC.Mode = AddrModeFlat;
    stackFrame.AddrFrame.Mode = AddrModeFlat;
    stackFrame.AddrStack.Mode = AddrModeFlat;
    stackFrame.AddrReturn.Mode = AddrModeFlat;
    stackFrame.AddrBStore.Mode = AddrModeFlat;


    DWORD dwMachType;
#if defined(_M_IX86)
    dwMachType                   = IMAGE_FILE_MACHINE_I386;
    stackFrame.AddrPC.Offset     = context.Eip;  //program counter
    stackFrame.AddrStack.Offset  = context.Esp;  //stack pointer
    stackFrame.AddrFrame.Offset  = context.Ebp;  //frame pointer
#else
    //refer to ATL::_AtlThreadContextInfo::DoDumpStack to impl non-x86 machine
#error("Unknown Target Machine");
#endif


    while (StackWalk(dwMachType, m_hProcess, hThread,&stackFrame, &context, NULL,
        SymFunctionTableAccess, SymGetModuleBase, NULL))
    {
        if (stackFrame.AddrPC.Offset != 0)
        {
            CallStack   sk;            
            FuncInfo    fi;
            if (resloveFuncInfo(PVOID(stackFrame.AddrPC.Offset),fi))
            {
                sk.m_funcAddr   = fi.m_addr;
                sk.m_funcName   = fi.m_name;
                sk.m_AsmOffset     = fi.m_AsmOffset;
            }


            Static_Assert(sizeof(sk.m_lineNumber)>=sizeof(DWORD));
            if (resloveSourceInfo(PVOID(stackFrame.AddrPC.Offset),sk.m_srcFileName,(DWORD&)sk.m_lineNumber))
            {
                char dbgOut[256] = {0};
                sprintf_s(dbgOut,"%s(%d) :%s\n",sk.m_srcFileName.c_str(),sk.m_lineNumber,sk.m_funcName.c_str());
                OutputDebugStringA(dbgOut);
            }


            if (0 != sk.m_funcAddr)
            {
                cs.m_stacks.push_back(sk);
            }
        }
    }


    if (!bIsCurThread) ResumeThread(hThread);


    if (bPrint)
    {
        print(cs);
    }


    if (pOutputStks)
    {
        *pOutputStks = cs;
    }


    return TRUE;
}


void StackExplorer::print( CallStackS const& cs )
{
    using namespace std;
    cout << "********************************************************************************************\n"    ;
    cout << "  Dump current process[ " << cs.m_processId << " ] - thread [" << cs.m_threadId << "] stack : \n"  ; 
    cout << "********************************************************************************************\n"    ;
    cout << "Func. Addr\tAddr. offset\tFunc. Name\n";
    for (std::vector<CallStack>::const_iterator it=cs.m_stacks.begin();it != cs.m_stacks.end() ; ++it )
    {
        print(*it);
    }
}


void StackExplorer::print( _In_ CallStack const& sk)
{
    using namespace std;
    //cout << "\nFunc. Addr\tAddr. offset\tFunc. Name\n";
    if (sk.m_funcAddr)
    {
        cout << std::showbase << setiosflags(ios::uppercase) << hex 
            << sk.m_funcAddr << "\t" << setw(10) << sk.m_AsmOffset << "\t"
            << sk.m_funcName << endl; 
    }


    if (0 != sk.m_srcFileName.size())
    { 
        cout << sk.m_srcFileName << "(" << dec << sk.m_lineNumber << ")" << endl;
    }
}


/
void interpreteException(EXCEPTION_RECORD const& ExpRecord)
{   
    using namespace std;
    using namespace UtilExt;
    ExceptionItem const* pExpText = findImpl(makeDataSet(exps),makeFinder(ExpRecord.ExceptionCode,MMP(&ExceptionItem::m_expCode)));
    cout << std::showbase << setiosflags(ios::uppercase) << hex
        << "Exception Code\tMeaning\n"
        << ExpRecord.ExceptionCode  << "\t" 
        << pExpText->m_expText  << "\n"
        << "Continuable ?" << (EXCEPTION_NONCONTINUABLE==ExpRecord.ExceptionFlags ? "\tNo\n" : "\tYes\n" );
    if (ExpRecord.NumberParameters)
    {
        cout << "Available param(s):\t";
        for(DWORD i=0;i<ExpRecord.NumberParameters;++i)
        {                                             
            cout << ExpRecord.ExceptionInformation[i] << "\t";
        }


        switch(ExpRecord.ExceptionCode)
        { //ms-help://MS.MSDNQTR.v90.en/debug/base/exception_record_str.htm
        case EXCEPTION_ACCESS_VIOLATION:
        case EXCEPTION_IN_PAGE_ERROR:
            cout << ( 0 == ExpRecord.ExceptionInformation[0] ? "\nRead " : (1==ExpRecord.ExceptionInformation[0] ? "\nWrite":"\ndata execution prevention (DEP)")) 
                << " fails, Addr=" << ExpRecord.ExceptionInformation[1] << "\n";
            break;
        default:
            break;
        }
    }


    if (ExpRecord.ExceptionRecord)
    {
        cout << "Additional info " ;
        interpreteException(*ExpRecord.ExceptionRecord);
    }
}


void handleException(_In_ _EXCEPTION_POINTERS * pExp )
{
    interpreteException(*pExp->ExceptionRecord);


    using namespace std;
    StackExplorer se(GetCurrentProcess());
    AddressInfo ai;
    se.resloveAddrInfo(pExp->ExceptionRecord->ExceptionAddress,ai);
    char dbgOut[256] = {0};
    sprintf_s(dbgOut,"\nException code position:\n%s(%d) : %s\n",ai.m_fileName.c_str(),ai.m_lineNumber,ai.m_func.m_name.c_str());
    cout << dbgOut ;
    OutputDebugStringA(dbgOut);


    EXCEPTION_RECORD& rExp = *pExp->ExceptionRecord;
    cout << "Exception info \nExp. Code\tExp.Addr\tModule base addr\tModule name\n";
    cout.setf(ios_base::showbase);
    cout<< std::showbase << setiosflags(ios::uppercase) << hex
        << setw(10) << rExp.ExceptionCode << "\t" 
        << (DWORD)rExp.ExceptionAddress << "\t"           
        << (DWORD)ai.m_mod.m_baseAddr << "\t\t" 
        << ai.m_mod.m_name << endl;


    cout << "\nASM function Info\nFunc. Addr\tExp. offset\tFunc. Name\n";                
    cout << std::showbase << setiosflags(ios::uppercase) << hex 
        << (DWORD)ai.m_func.m_addr
        << "\t" << setw(10) << ai.m_func.m_AsmOffset << "\t" 
        << ai.m_func.m_name << endl;


    se.DumpStack(GetCurrentThread(),pExp->ContextRecord);
    return ;
}

用法及测试代码//

//test_StackExplorer.h

#ifndef test_StackExplorer_h__
#define test_StackExplorer_h__
#include "StackExplorer\StackExplorer.h"


//#define RUN_EXAMPLE_STACKEXPLORER


#ifdef COMPILE_EXAMPLE_ALL
    #define COMPILE_EXAMPLE_STACKEXPLORER
#endif


#ifdef RUN_EXAMPLE_ALL
    #define RUN_EXAMPLE_STACKEXPLORER
#endif


#if defined(RUN_EXAMPLE_STACKEXPLORER) && !defined(COMPILE_EXAMPLE_STACKEXPLORER)
    #define COMPILE_EXAMPLE_STACKEXPLORER
#endif


/
#ifdef COMPILE_EXAMPLE_STACKEXPLORER


int     g_int = 0;
int*    g_intP = NULL;


int Func5() 
{
//     StackExplorer se(GetCurrentProcess());
//     se.DumpStack(GetCurrentThread(),NULL);


    //RaiseException(1,0,2,0);    
    *g_intP = 0x5678;
    //return 5/g_int ;


    return 0;
}
void Func4() { Func5(); }
void Func3() 

    g_intP = (int*)0x1234;
    Func4(); 
}
void Func2() { Func3(); }


int testException()
{
    __try
    {
        Func2();        
    }
    __except(handleException(GetExceptionInformation()), EXCEPTION_EXECUTE_HANDLER)
    {
    }
    return 0;
}


#ifdef RUN_EXAMPLE_STACKEXPLORER
    InitRunFunc(testException);
#endif


#endif // COMPILE_EXAMPLE_STACKEXPLORER




#endif // test_StackExplorer_h__

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值