Stack Trace для бедных :)
От: Tom Россия http://www.RSDN.ru
Дата: 23.08.05 10:44
Оценка: 14 (3)
Понадобилось самое простое решение данной задачи.
class StackTraceEngine
{
public:
    HMODULE m_hDbhHelp;
    HANDLE m_hProcess;

    // SymGetLineFromAddr64()
    typedef BOOL (__stdcall *tfnSymGetLineFromAddr64)( IN HANDLE hProcess, IN DWORD64 dwAddr,
        OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line );
    tfnSymGetLineFromAddr64 pfnSymGetLineFromAddr64;

    // SymGetSymFromAddr64()
    typedef BOOL (__stdcall *tfnSymGetSymFromAddr64)( IN HANDLE hProcess, IN DWORD64 dwAddr,
        OUT PDWORD64 pdwDisplacement, OUT PIMAGEHLP_SYMBOL64 Symbol );
    tfnSymGetSymFromAddr64 pfnSymGetSymFromAddr64;

    // SymInitialize()
    typedef BOOL (__stdcall *tfnSymInitialize)( IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess );
    tfnSymInitialize pfnSymInitialize;

    // SymSetOptions()
    typedef DWORD (__stdcall *tfnSymSetOptions)( IN DWORD SymOptions );
    tfnSymSetOptions pfnSymSetOptions;

    // StackWalk64()
    typedef BOOL (__stdcall *tfnStackWalk64)( 
        DWORD MachineType, 
        HANDLE hProcess,
        HANDLE hThread, 
        LPSTACKFRAME64 StackFrame, 
        PVOID ContextRecord,
        PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
        PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
        PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
        PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress );
    tfnStackWalk64 pfnStackWalk64;

    // UnDecorateSymbolName()
    typedef DWORD (__stdcall WINAPI *tfnUnDecorateSymbolName)( PCSTR DecoratedName, PSTR UnDecoratedName,
        DWORD UndecoratedLength, DWORD Flags );
    tfnUnDecorateSymbolName pfnUnDecorateSymbolName;
    
    // SymFunctionTableAccess64()
    typedef PVOID (__stdcall *tfnSymFunctionTableAccess64)( HANDLE hProcess, DWORD64 AddrBase );
    tfnSymFunctionTableAccess64 pfnSymFunctionTableAccess64;

    // SymGetModuleBase64()
    typedef DWORD64 (__stdcall *tfnSymGetModuleBase64)( IN HANDLE hProcess, IN DWORD64 dwAddr );
    tfnSymGetModuleBase64 pfnSymGetModuleBase64;

    StackTraceEngine(HANDLE hProcess) :
        m_hDbhHelp(0),
        m_hProcess(hProcess),
        pfnSymGetLineFromAddr64(0),
        pfnSymGetSymFromAddr64(0),
        pfnSymInitialize(0),
        pfnSymSetOptions(0),
        pfnStackWalk64(0),
        pfnUnDecorateSymbolName(0),
        pfnSymFunctionTableAccess64(0),
        pfnSymGetModuleBase64(0)
    {
    }

    HRESULT Init()
    {
        BOOL Ret;

        //
        // Load dbghelp.dll dynamically.
        //
        if ((m_hDbhHelp = LoadLibrary(L"dbghelp.dll")) == 0)
        {
            return HRESULT_FROM_WIN32(GetLastError());
        }

        //
        // Load procedures.
        //
        pfnSymInitialize = (tfnSymInitialize) GetProcAddress(m_hDbhHelp, "SymInitialize" );
        pfnStackWalk64 = (tfnStackWalk64) GetProcAddress(m_hDbhHelp, "StackWalk64" );
        pfnSymGetLineFromAddr64 = (tfnSymGetLineFromAddr64) GetProcAddress(m_hDbhHelp, "SymGetLineFromAddr64" );
        pfnSymGetSymFromAddr64 = (tfnSymGetSymFromAddr64) GetProcAddress(m_hDbhHelp, "SymGetSymFromAddr64" );
        pfnUnDecorateSymbolName = (tfnUnDecorateSymbolName) GetProcAddress(m_hDbhHelp, "UnDecorateSymbolName" );
        pfnSymFunctionTableAccess64 = (tfnSymFunctionTableAccess64) GetProcAddress(m_hDbhHelp, "SymFunctionTableAccess64" );
        pfnSymGetModuleBase64 = (tfnSymGetModuleBase64) GetProcAddress(m_hDbhHelp, "SymGetModuleBase64" );
        pfnSymSetOptions = (tfnSymSetOptions) GetProcAddress(m_hDbhHelp, "SymSetOptions" );

        if ((pfnSymInitialize == 0) ||
            (pfnStackWalk64 == 0) ||
            (pfnSymGetLineFromAddr64 == 0) ||
            (pfnSymGetSymFromAddr64 == 0) ||
            (pfnUnDecorateSymbolName == 0) ||
            (pfnSymFunctionTableAccess64 == 0) ||
            (pfnSymGetModuleBase64 == 0) ||
            (pfnSymSetOptions == 0))
        {
            FreeLibrary(m_hDbhHelp);
            m_hDbhHelp = 0;
            return E_UNEXPECTED;
        }

        //
        // Initialize symbols engine.
        //
        if ((Ret = pfnSymSetOptions(SYMOPT_LOAD_LINES)) == FALSE)
        {
            return HRESULT_FROM_WIN32(GetLastError());
        }

        if ((Ret = pfnSymInitialize(
            m_hProcess,
            0,
            TRUE)) == FALSE)
        {
            return HRESULT_FROM_WIN32(GetLastError());
        }

        return S_OK;
    }

    HRESULT WalkStack(
        char* StackTrace,
        DWORD StackTraceSize)
    {
        if ((StackTrace == 0) ||
            (IsBadWritePtr(StackTrace, StackTraceSize)))
        {
            return E_INVALIDARG;
        }

        //
        // Init out parameters.
        //
        StackTrace[0] = '\0';

        //
        // The following should be enough for walking the call stack...
        //
        CONTEXT ctx = {0};

        __asm    call x;
        __asm x: pop eax;
        __asm    mov ctx.Eip, eax;
        __asm    mov ctx.Ebp, ebp;
        __asm    mov ctx.Esp, esp;

        //
        // Init STACKFRAME for the first call
        //
        STACKFRAME64 stfrm = {0}; // in/out stackframe
        stfrm.AddrPC.Offset = ctx.Eip;
        stfrm.AddrPC.Mode = AddrModeFlat;

        stfrm.AddrFrame.Offset = ctx.Ebp;
        stfrm.AddrFrame.Mode = AddrModeFlat;

        stfrm.AddrStack.Offset = ctx.Esp;
        stfrm.AddrStack.Mode = AddrModeFlat;

        //
        // Symbol info memory buffer.
        //
        char SymbolBuffer[65535] = {0};
        IMAGEHLP_SYMBOL64 *pSymbol = reinterpret_cast<IMAGEHLP_SYMBOL64*>(&SymbolBuffer);
        pSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);

        DWORD SymbolNameSize = (DWORD)sizeof(SymbolBuffer) - offsetof(IMAGEHLP_SYMBOL64, Name);
        pSymbol->MaxNameLength = SymbolNameSize;

        HANDLE hCurrentThread = GetCurrentThread();

        //
        // Line info.
        //
        IMAGEHLP_LINE64 Line = {0};
        Line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);

        while(true)
        {
            //
            // IMAGE_FILE_MACHINE_I386 is only supported now.
            // I have no other platforms to check this code :(
            //
            pfnStackWalk64(
                IMAGE_FILE_MACHINE_I386,
                m_hProcess,
                hCurrentThread,
                &stfrm,
                &ctx,
                0,
                pfnSymFunctionTableAccess64,
                pfnSymGetModuleBase64,
                0);

            if (stfrm.AddrPC.Offset == 0)
                break;

            //
            // Get procedure info.
            //
            DWORD64 SymbolDisplacement = 0;
            pfnSymGetSymFromAddr64(
                m_hProcess,
                stfrm.AddrPC.Offset,
                &SymbolDisplacement,
                pSymbol);

            //
            // Get line info.
            //
            DWORD LineDisplacement = 0;
            pfnSymGetLineFromAddr64(
                m_hProcess,
                stfrm.AddrPC.Offset,
                &LineDisplacement,
                &Line);

            //
            // Format and store current frame.
            //
            size_t SymbolNameRealSize = 0;
            StringCchLengthA(
                pSymbol->Name,
                SymbolNameSize,
                &SymbolNameRealSize);

            //
            // Append symbol name.
            //
            if (SymbolNameRealSize == 0)
            {
                StringCchCatA(
                    StackTrace,
                    StackTraceSize,
                    "Procedure: <unknown>");
            }
            else
            {
                char buff[16384];
                StringCchPrintfA(
                    buff,
                    sizeof(buff),
                    "Procedure: %s",
                    pSymbol->Name);

                StringCchCatA(
                    StackTrace,
                    StackTraceSize,
                    buff);
            }

            //
            // Append symbol location
            //
            if (Line.FileName[0] == 0)
            {
                StringCchCatA(
                    StackTrace,
                    StackTraceSize,
                    ", File: <unknown>, Line: <unknown>\n");
            }
            else
            {
                char buff[16384];
                StringCchPrintfA(
                    buff,
                    sizeof(buff),
                    " , File: %s, Line: %d\n",
                    Line.FileName,
                    Line.LineNumber);

                StringCchCatA(
                    StackTrace,
                    StackTraceSize,
                    buff);
            }
        }

        return S_OK;
    }
};


Применять примерно так:
void DoStackTrace(void)
{
    HANDLE hProcess = OpenProcess(
        PROCESS_ALL_ACCESS,
        FALSE,
        GetCurrentProcessId());

    StackTraceEngine se(hProcess);
    se.Init();

    char buff[65535];
    se.WalkStack(
        buff,
        sizeof(buff));
    printf("%s", buff);
}


как альтернативу смотреть:
http://www.codeproject.com/threads/StackWalker.asp

Мне он не подошёл обьёмом. ~1200 строк.
... << RSDN@Home 1.1.4 beta 4 rev. 303>>
Народная мудрось
всем все никому ничего(с).
Re: Stack Trace для бедных :)
От: Sheridan Россия  
Дата: 24.08.05 13:01
Оценка:
прочитал как Star Track...
Некоторое время тупо думал — а при чем тут исходники и как это сочетается с бедными...

[RSDN@Home][1.2.0][alpha][607]
[Благочестие, ханжество, суеверие — три разницы. [К. Прутков]]
Matrix has you...
Re[2]: Stack Trace для бедных :)
От: Tom Россия http://www.RSDN.ru
Дата: 24.08.05 14:00
Оценка:
Здравствуйте, Sheridan, Вы писали:

S> прочитал как Star Track...

S>Некоторое время тупо думал — а при чем тут исходники и как это сочетается с бедными...

Исходники тут при том что в сообщении реализация класса. Бедные тут при том, что реализация данной фичи — минимальная без лишних наворотов
... << RSDN@Home 1.1.4 beta 4 rev. 303>>
Народная мудрось
всем все никому ничего(с).
Re: Stack Trace для бедных :)
От: IPv6 Россия http://www.lumarnia.com/
Дата: 08.09.05 15:30
Оценка:
Здравствуйте, Tom, Вы писали:

Tom>Понадобилось самое простое решение данной задачи.

Tom>как альтернативу смотреть:
Tom>http://www.codeproject.com/threads/StackWalker.asp

не очень хорошо оно работает... не вытаскиваются имена файлов/номера строк
File: <unknown>, Line: <unknown>
+вместо названий функций оффсет от какой-то одной определенной в модуле.
чего-то не хватает
Re[2]: Stack Trace для бедных :)
От: Tom Россия http://www.RSDN.ru
Дата: 08.09.05 15:49
Оценка:
Вот новая версия:

cpp
// RpcExStackTraceEngine.cpp
//
//////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "RpcExStackTraceEngine.h"

RpcExStackTraceEngine::RpcExStackTraceEngine() :
    m_hDbhHelp(0),
    m_hProcess(0),
    pfnSymGetLineFromAddr64(0),
    pfnSymGetSymFromAddr64(0),
    pfnSymInitialize(0),
    pfnSymSetOptions(0),
    pfnSymGetOptions(0),
    pfnStackWalk64(0),
    pfnUnDecorateSymbolName(0),
    pfnSymFunctionTableAccess64(0),
    pfnSymGetModuleBase64(0),
    m_InitializationState(IS_NotPerformed)
{
}

HRESULT RpcExStackTraceEngine::Initialize()
{
    BOOL Ret;

    CRpcExCritSecLock m_DbgHelpLock(m_DbgHelpGuard);
    
    if (m_InitializationState != IS_NotPerformed)
    {
        return E_UNEXPECTED;
    }

    //
    // Assume initialization is failed.
    // I'll set this flag to the IS_Done in the
    // end of this function.
    //
    m_InitializationState = IS_Failed;

    //
    // Load dbghelp.dll dynamically.
    //
    if ((m_hDbhHelp = LoadLibrary(L"dbghelp.dll")) == 0)
    {
        return HRESULT_FROM_WIN32(GetLastError());
    }

    //
    // Load procedures.
    //
    pfnSymInitialize = (tfnSymInitialize) GetProcAddress(m_hDbhHelp, "SymInitialize" );
    pfnStackWalk64 = (tfnStackWalk64) GetProcAddress(m_hDbhHelp, "StackWalk64" );
    pfnSymGetLineFromAddr64 = (tfnSymGetLineFromAddr64) GetProcAddress(m_hDbhHelp, "SymGetLineFromAddr64" );
    pfnSymGetSymFromAddr64 = (tfnSymGetSymFromAddr64) GetProcAddress(m_hDbhHelp, "SymGetSymFromAddr64" );
    pfnUnDecorateSymbolName = (tfnUnDecorateSymbolName) GetProcAddress(m_hDbhHelp, "UnDecorateSymbolName" );
    pfnSymFunctionTableAccess64 = (tfnSymFunctionTableAccess64) GetProcAddress(m_hDbhHelp, "SymFunctionTableAccess64" );
    pfnSymGetModuleBase64 = (tfnSymGetModuleBase64) GetProcAddress(m_hDbhHelp, "SymGetModuleBase64" );
    pfnSymSetOptions = (tfnSymSetOptions) GetProcAddress(m_hDbhHelp, "SymSetOptions" );
    pfnSymGetOptions = (tfnSymGetOptions) GetProcAddress(m_hDbhHelp, "SymGetOptions" );

    if ((pfnSymInitialize == 0) ||
        (pfnStackWalk64 == 0) ||
        (pfnSymGetLineFromAddr64 == 0) ||
        (pfnSymGetSymFromAddr64 == 0) ||
        (pfnUnDecorateSymbolName == 0) ||
        (pfnSymFunctionTableAccess64 == 0) ||
        (pfnSymGetModuleBase64 == 0) ||
        (pfnSymSetOptions == 0) ||
        (pfnSymGetOptions == 0))
    {
        FreeLibrary(m_hDbhHelp);
        m_hDbhHelp = 0;
        return E_UNEXPECTED;
    }

    DWORD dwOpts = pfnSymGetOptions() ;

    //
    // Initialize symbols engine.
    //
    if ((Ret = pfnSymSetOptions(dwOpts | SYMOPT_LOAD_LINES)) == FALSE)
    {
        return HRESULT_FROM_WIN32(GetLastError());
    }

    m_hProcess = GetCurrentProcess();
    
        
    // 
    // SymInitialize 2-nd parameter description from the MSDN.
    // Pointer to a null-terminated string that specifies a path,
    // or series of paths separated by a semicolon (;),
    // that is used to search for symbol files.
    // If a value of NULL is used, then the library attempts to
    // form a symbol path from the following sources: 
    //   The current working directory of the application 
    //   The _NT_SYMBOL_PATH environment variable 
    //   The _NT_ALTERNATE_SYMBOL_PATH environment variable 
    //
        
    if ((Ret = pfnSymInitialize(
        m_hProcess,
        0,
        TRUE)) == FALSE)
    {
        return HRESULT_FROM_WIN32(GetLastError());
    }

    m_InitializationState = IS_Done;

    return S_OK;
}

RpcExStackTraceEngine::InitializationState RpcExStackTraceEngine::GetInitializationState()
{
    return m_InitializationState;
}

// Declare these so they are accessable anywhere in the project.
extern "C" void * _ReturnAddress ( void ) ;
#pragma intrinsic ( _ReturnAddress )

HRESULT RpcExStackTraceEngine::WalkStack(
    WCHAR* StackTrace,
    DWORD StackTraceSize, /*in characters*/
    DWORD MaxStackTraceDepth)
{
    CRpcExCritSecLock m_DbgHelpLock(m_DbgHelpGuard);

    if ((StackTrace == 0) ||
        (IsBadWritePtr(StackTrace, StackTraceSize*sizeof(WCHAR))))
    {
        //
        // Grrrr,
        // I donТt like bad buffers.
        //
        return E_INVALIDARG;
    }

    if (m_InitializationState != IS_Done)
    {
        //
        // Looks like initialization failed
        // or not performed yet.
        //
        return E_UNEXPECTED;
    }

    //
    // Init out parameters.
    //
    StackTrace[0] = L'\0';

    HANDLE hCurrentThread = GetCurrentThread();

    //
    // The following should be enough for walking the call stack...
    //
    CONTEXT ctx = {0};
    ctx.ContextFlags = CONTEXT_FULL;
    
    __asm    call x;
    __asm x: pop eax;
    __asm    mov ctx.Eip, eax;
    __asm    mov ctx.Ebp, ebp;
    __asm    mov ctx.Esp, esp;

    //
    // Init STACKFRAME for the first call
    //
    STACKFRAME64 stfrm = {0}; // in/out stackframe
    stfrm.AddrPC.Offset = ctx.Eip;
    stfrm.AddrPC.Mode = AddrModeFlat;

    stfrm.AddrStack.Offset = ctx.Esp;
    stfrm.AddrStack.Mode = AddrModeFlat;

    stfrm.AddrFrame.Offset = ctx.Ebp;
    stfrm.AddrFrame.Mode = AddrModeFlat;

    //
    // Symbol info memory buffer.
    //
    char SymbolBuffer[65535] = {0};
    IMAGEHLP_SYMBOL64 *pSymbol = reinterpret_cast<IMAGEHLP_SYMBOL64*>(&SymbolBuffer);
    pSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);

    DWORD SymbolNameSize = (DWORD)sizeof(SymbolBuffer) - offsetof(IMAGEHLP_SYMBOL64, Name);
    pSymbol->MaxNameLength = SymbolNameSize;

    //
    // Line info.
    //
    IMAGEHLP_LINE64 Line = {0};
    Line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);

    for(ULONG StackNumber = 0; StackNumber < MaxStackTraceDepth; ++StackNumber)
    {
        //
        // TODO:
        //    Use heap instead of stack.
        //    USES_CONVERSION uses stack to store strings.
        //
        USES_CONVERSION;

        //
        // IMAGE_FILE_MACHINE_I386 is only supported now.
        // I have no other platforms to check this code :(
        //
        pfnStackWalk64(
            IMAGE_FILE_MACHINE_I386,
            m_hProcess,
            hCurrentThread,
            &stfrm,
            &ctx,
            0,
            pfnSymFunctionTableAccess64,
            pfnSymGetModuleBase64,
            0);

        if (stfrm.AddrPC.Offset == 0)
            break;

        //
        // If StackWalk64 fails it does not cleans ->Name.
        // We must do this manually.
        //
        pSymbol->Name[0] = '\0';

        //
        // Get procedure info.
        //
        DWORD64 SymbolDisplacement = 0;
        pfnSymGetSymFromAddr64(
            m_hProcess,
            stfrm.AddrPC.Offset,
            &SymbolDisplacement,
            pSymbol);

        //
        // Get line info.
        //
        DWORD LineDisplacement = 0;
        pfnSymGetLineFromAddr64(
            m_hProcess,
            stfrm.AddrPC.Offset,
            &LineDisplacement,
            &Line);

        //
        // Append symbol name.
        //
        if ((pSymbol->Name == 0) ||
            (pSymbol->Name[0] == L'\0'))
        {
            StringCchCat(
                StackTrace,
                StackTraceSize,
                L"Procedure: <unknown>");
        }
        else
        {
            StringCchCat(
                StackTrace,
                StackTraceSize,
                L"Procedure: ");

            StringCchCat(
                StackTrace,
                StackTraceSize,
                A2CW(pSymbol->Name));
        }

        //
        // Append symbol location
        //
        if ((Line.FileName == 0) ||
            (Line.FileName[0] == L'\0'))
        {
            StringCchCat(
                StackTrace,
                StackTraceSize,
                L", File: <unknown>, Line: <unknown>\n");
        }
        else
        {
            StringCchCat(
                StackTrace,
                StackTraceSize,
                L" , File: ");

            StringCchCat(
                StackTrace,
                StackTraceSize,
                A2CW(Line.FileName));

            StringCchCat(
                StackTrace,
                StackTraceSize,
                L", Line: ");

            WCHAR LineNumberBuffer[16];
            _itow(Line.LineNumber, LineNumberBuffer, 10);
            StringCchCat(
                StackTrace,
                StackTraceSize,
                LineNumberBuffer);

            StringCchCat(
                StackTrace,
                StackTraceSize,
                L"\n");
        }
    }

    return S_OK;
}


h
// RpcExStackTraceEngine.h
//
//////////////////////////////////////////////////////////////////////
#if !defined(__RPC_EX_STACK_TRACE_ENGINE_INCLUDED__)
#define __RPC_EX_STACK_TRACE_ENGINE_INCLUDED__

#include "dbghelp.h"

/**
 * This is class help to obtain call stack as a
 * simple string. It uses "dbghelp.dll" to walk
 * the call stack and search symbols in the debug
 * information. It is recommended to have
 * only one instance of this class.
 *
 * WARNING:
 *   ASSERT's and TRACE's implementation use
 *   this class to show call stack, so it is
 *   not possible to use ASSERT's in this class.
 */
class RpcExStackTraceEngine
{
public:
    enum InitializationState
    {
        IS_NotPerformed,
        IS_Failed,
        IS_Done
    };

private:
    /**
     * HMODULE of the "dbghelp.dll"
     * We're loading "dbghelp.dll" dynamically to avoid 
     * dependency from "dbghelp.dll" because if dependency
     * is present loading RpcExFramework library will fail
     * if "dbghelp.dll" is absent.
     */
    HMODULE m_hDbhHelp;

    /**
     * Handle of the current process.
     * We need it in almost all Sym***
     * operations.
     */
    HANDLE m_hProcess;

    /**
     * RpcExStackTraceEngine initialization state.
     * We need this because:
     *   We must do some work in the WalkStack implementation
     *   only if initialization success.
     *   We must not do some work in the Initialize only
     *   if Initialize has not been called before. 
     */
    InitializationState m_InitializationState;

    /**
     * All "dbghelp.dll" functions are single threaded.
     * So we must protect calls to the "dbghelp.dll"
     * with critical section.
     */
    CComAutoCriticalSection m_DbgHelpGuard;

    //
    // Pointers to the "dbghelp.dll" functions.
    //
    
    // SymGetLineFromAddr64()
    typedef BOOL (__stdcall *tfnSymGetLineFromAddr64)(
        HANDLE hProcess,
        DWORD64 dwAddr,
        PDWORD pdwDisplacement,
        PIMAGEHLP_LINE64 Line);
    tfnSymGetLineFromAddr64 pfnSymGetLineFromAddr64;

    // SymGetSymFromAddr64()
    typedef BOOL (__stdcall *tfnSymGetSymFromAddr64)(
        HANDLE hProcess,
        DWORD64 dwAddr,
        PDWORD64 pdwDisplacement,
        PIMAGEHLP_SYMBOL64 Symbol);
    tfnSymGetSymFromAddr64 pfnSymGetSymFromAddr64;

    // SymInitialize()
    typedef BOOL (__stdcall *tfnSymInitialize)(
        HANDLE hProcess,
        PSTR UserSearchPath,
        BOOL fInvadeProcess );
    tfnSymInitialize pfnSymInitialize;

    // SymSetOptions()
    typedef DWORD (__stdcall *tfnSymSetOptions)(
        DWORD SymOptions);
    tfnSymSetOptions pfnSymSetOptions;

    // SymGetOptions()
    typedef DWORD (__stdcall *tfnSymGetOptions)();
    tfnSymGetOptions pfnSymGetOptions;

    // StackWalk64()
    typedef BOOL (__stdcall *tfnStackWalk64)( 
        DWORD MachineType, 
        HANDLE hProcess,
        HANDLE hThread, 
        LPSTACKFRAME64 StackFrame, 
        PVOID ContextRecord,
        PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
        PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
        PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
        PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress );
    tfnStackWalk64 pfnStackWalk64;

    // UnDecorateSymbolName()
    typedef DWORD (__stdcall WINAPI *tfnUnDecorateSymbolName)(
        PCSTR DecoratedName,
        PSTR UnDecoratedName,
        DWORD UndecoratedLength,
        DWORD Flags);
    tfnUnDecorateSymbolName pfnUnDecorateSymbolName;
    
    // SymFunctionTableAccess64()
    typedef PVOID (__stdcall *tfnSymFunctionTableAccess64)(
        HANDLE hProcess,
        DWORD64 AddrBase);
    tfnSymFunctionTableAccess64 pfnSymFunctionTableAccess64;

    // SymGetModuleBase64()
    typedef DWORD64 (__stdcall *tfnSymGetModuleBase64)(
        HANDLE hProcess,
        DWORD64 dwAddr );
    tfnSymGetModuleBase64 pfnSymGetModuleBase64;

public:
    RpcExStackTraceEngine();

    /**
     * Initializes the RpcExStackTraceEngine().
     * In fact we just initializing symbols engine.
     */
    HRESULT Initialize();

    /**
     * Returns result of the Initialize() call.
     */
    InitializationState GetInitializationState();

    /**
     * Returns stack trace as a string.
     */
    HRESULT WalkStack(
        WCHAR* StackTrace,
        DWORD StackTraceSize, /*in characters*/
        DWORD MaxStackTraceDepth);
};

#endif // __RPC_EX_STACK_TRACE_ENGINE_INCLUDED__


Для того, что бы всё работало см выделенное болдом.
Тоесть лучше всего скопировать pdb-шки в каталог с EXE-шником
... << RSDN@Home 1.1.4 beta 4 rev. 303>>
Народная мудрось
всем все никому ничего(с).
Re[3]: Stack Trace для бедных :)
От: Tom Россия http://www.RSDN.ru
Дата: 08.09.05 15:51
Оценка:
Забыл сказать. Ессно рядом должен лежать dbghelp.dll
... << RSDN@Home 1.1.4 beta 4 rev. 303>>
Народная мудрось
всем все никому ничего(с).
Re[4]: Stack Trace для бедных :)
От: IPv6 Россия http://www.lumarnia.com/
Дата: 09.09.05 07:39
Оценка:
Здравствуйте, Tom, Вы писали:

Tom>Забыл сказать. Ессно рядом должен лежать dbghelp.dll


Во, заработало! %) Спасибо! %))
Правда после изменения одного (жирным)

        CONTEXT ctx = {0};
        if(pContext==0){
            ctx.ContextFlags = CONTEXT_FULL ;
            if ( !GetThreadContext ( GetCurrentThread ( ) , &ctx ) ){
                __asm    call x;
                __asm x: pop eax;
                __asm    mov ctx.Eip, eax;
                __asm    mov ctx.Ebp, ebp;
                __asm    mov ctx.Esp, esp;
            }
        }else{
            memcpy(&ctx,pContext,sizeof(ctx));
        }


+ чтобы этот стеквалк можно было дергать из обработчика MS-Specific эксепшинов (которые в __try) добавил параметр в заголовок

HRESULT WalkStack(
        char* StackTrace,
        DWORD StackTraceSize,
    CONTEXT* pContext)...

CString DoStackTrace(CONTEXT* pContext=0)...

и из обработчика исключения (типа 0xC00...05 к примеру) дергается так


BOOL Debug_WriteStack(_EXCEPTION_POINTERS* pt)
{
    WriteLogString(">>> SOFTWARE EXCEPTION STACK BEGIN >>>",0);
    WriteLogString(DoStackTrace(pt->ContextRecord),0);
    WriteLogString("<<< SOFTWARE EXCEPTION STACK  END  <<<",0);
    return TRUE;
}
Re[5]: Stack Trace для бедных :)
От: Tom Россия http://www.RSDN.ru
Дата: 09.09.05 08:33
Оценка:
IP>
IP>            if ( !GetThreadContext ( GetCurrentThread ( ) , &ctx ) ){
IP>

Гмммммм. У меня как раз проблемма была в том, что данная функция не хотела работать и если переделываю на неё — ничего не работает
... << RSDN@Home 1.1.4 beta 4 rev. 303>>
Народная мудрось
всем все никому ничего(с).
Re[6]: Stack Trace для бедных :)
От: Tom Россия http://www.RSDN.ru
Дата: 09.09.05 08:36
Оценка:
Здравствуйте, Tom, Вы писали:

IP>>
IP>>            if ( !GetThreadContext ( GetCurrentThread ( ) , &ctx ) ){
IP>>

Tom>Гмммммм. У меня как раз проблемма была в том, что данная функция не хотела работать и если переделываю на неё — ничего не работает

Вот кстате из MSDN:

You cannot get a valid context for a running thread. Use the SuspendThread function to suspend the thread before calling GetThreadContext.

... << RSDN@Home 1.1.4 beta 4 rev. 303>>
Народная мудрось
всем все никому ничего(с).
Re[7]: Stack Trace для бедных :)
От: IPv6 Россия http://www.lumarnia.com/
Дата: 09.09.05 09:41
Оценка:
Здравствуйте, Tom, Вы писали:

Tom>Здравствуйте, Tom, Вы писали:


IP>>>
IP>>>            if ( !GetThreadContext ( GetCurrentThread ( ) , &ctx ) ){
IP>>>

Tom>>Гмммммм. У меня как раз проблемма была в том, что данная функция не хотела работать и если переделываю на неё — ничего не работает

Tom>Вот кстате из MSDN:

Tom>

Tom>You cannot get a valid context for a running thread. Use the SuspendThread function to suspend the thread before calling GetThreadContext.


у меня наоборот %( серьезно!
а как именно "не работало"? можно ли чтобы работало то что работает в текущем варианте?
Re[7]: Stack Trace для бедных :)
От: IPv6 Россия http://www.lumarnia.com/
Дата: 09.09.05 09:44
Оценка:
Здравствуйте, Tom, Вы писали:

Tom>Вот кстате из MSDN:

Tom>

Tom>You cannot get a valid context for a running thread. Use the SuspendThread function to suspend the thread before calling GetThreadContext.


если поток обвалился по жесткому эксепшену (типа C005, это мой случай) то он наверно априори Suspended и поэтому работает?
Re[8]: Stack Trace для бедных :)
От: Tom Россия http://www.RSDN.ru
Дата: 09.09.05 10:01
Оценка:
IP>если поток обвалился по жесткому эксепшену (типа C005, это мой случай) то он наверно априори Suspended и поэтому работает?
А я трейс делаю не из эксепшинов, а в асертах его применяю.
... << RSDN@Home 1.1.4 beta 4 rev. 303>>
Народная мудрось
всем все никому ничего(с).
Re[8]: Stack Trace для бедных :)
От: Tom Россия http://www.RSDN.ru
Дата: 09.09.05 10:01
Оценка:
IP>у меня наоборот %( серьезно!
гм.

IP>а как именно "не работало"? можно ли чтобы работало то что работает в текущем варианте?

В трейсе был бред, 2 функции, которые явно не в тему. Типа:

Procedure: KiFastSystemCallRet, File: <unknown>, Line: <unknown>
Procedure: SymFunctionTableAccess64, File: <unknown>, Line: <unknown>

... << RSDN@Home 1.1.4 beta 4 rev. 303>>
Народная мудрось
всем все никому ничего(с).
Re[9]: Stack Trace для бедных :)
От: IPv6 Россия http://www.lumarnia.com/
Дата: 09.09.05 10:19
Оценка:
Здравствуйте, Tom, Вы писали:

IP>>если поток обвалился по жесткому эксепшену (типа C005, это мой случай) то он наверно априори Suspended и поэтому работает?

Tom>А я трейс делаю не из эксепшинов, а в асертах его применяю.
а я наоборот... хотя я поторопился (когда экспериментировал почему не срабатывало получение имен файлов из пдбшек) — сейчас сделал получение контекста как в оригинале (через асм) — все работает для текущего потока.
так что еще раз спасибо за небольшое и удобное решение! %)
Re[9]: Stack Trace для бедных :)
От: IPv6 Россия http://www.lumarnia.com/
Дата: 09.09.05 10:48
Оценка:
Здравствуйте, Tom, Вы писали:

IP>>если поток обвалился по жесткому эксепшену (типа C005, это мой случай) то он наверно априори Suspended и поэтому работает?

Tom>А я трейс делаю не из эксепшинов, а в асертах его применяю.

кстати, внесу свою лепту. для применения в ассертах удобно использовать такую штуку — глобальный хук на ассерты в приложении:
int DebugHelper(int nRptType, char *szMsg, int *retVal);
/** Класс обеспечивающий запись стека вызовов при срабатывании assert'a */
class CAssertWrapper
{
public:
    /** Конструктор
    Устанавливает хук на ассерты
    */
    CAssertWrapper()
    {
        // Устанавливаем хук на ассерты - в любом случае!!!
        _CrtSetReportHook(DebugHelper);
    };

    /** Деструктор
    Снимает хук
    */
    ~CAssertWrapper()
    {
        _CrtSetReportHook(NULL);
    };

    /** Запись отладочной информации в файл
    @param szText
    */
    BOOL WriteAssert(const char* szText)
    {
        ***
    }
};

/** Экземпляр класса CAssertWrapper, который устанавливает и снимает хук */
__declspec ( dllexport ) CAssertWrapper assertWrapperObject;

int DebugHelper(int nRptType, char *szMsg, int *retVal)
{
    BOOL bHandled=FALSE;
    if(nRptType!=_CRT_WARN){//_CRT_ERROR,_CRT_ASSERT
        _CrtSetReportHook(NULL);// Чтобы небыло супер циклов..
        bHandled=assertWrapperObject.WriteAssert(szMsg);
        _CrtSetReportHook(DebugHelper);
    }
    return bHandled;
}


и все ассерты (ваши/MFCшные и т.п) будут проходить через WriteAssert, где можно тащить стек и показывать его к примеру или сохранять в файл на будущее ну и т.п. %) Тоже удобно %)
Re[10]: Stack Trace для бедных :)
От: Tom Россия http://www.RSDN.ru
Дата: 09.09.05 11:06
Оценка:
IP>а я наоборот... хотя я поторопился (когда экспериментировал почему не срабатывало получение имен файлов из пдбшек) — сейчас сделал получение контекста как в оригинале (через асм) — все работает для текущего потока.
IP>так что еще раз спасибо за небольшое и удобное решение! %)

Уффф. Ты меня успокоил. Я уже начал новый Debugging Tools качать и с контекстом разбираться
... << RSDN@Home 1.1.4 beta 4 rev. 303>>
Народная мудрось
всем все никому ничего(с).
Re[10]: Stack Trace для бедных :)
От: Tom Россия http://www.RSDN.ru
Дата: 09.09.05 11:12
Оценка:
IP>кстати, внесу свою лепту. для применения в ассертах удобно использовать такую штуку — глобальный хук на ассерты в приложении:
У меня всё более запущено. Своя реализация макросов и окна. Как то так:

#define RPC_EX_ASSERT(expr) \
do {if (!(expr)) \
{ \
    if (g_RpcExAssertFlags & RPC_EX_ASSERT_2_DEBUGGER) \
    { \
        RpcExMessage2Debugger( \
            UNICODE_STRING(__FILE__), \
            __LINE__, \
            UNICODE_STRING(#expr)); \
    }; \
    if (g_RpcExAssertFlags & RPC_EX_ASSERT_2_NTLOG) \
    { \
        RpcExMessage2NTLog( \
            UNICODE_STRING(__FILE__), \
            __LINE__, \
            UNICODE_STRING(#expr)); \
    }; \
    if ((((RpcExIsInNTService() == FALSE) && (g_RpcExAssertFlags & RPC_EX_ASSERT_2_DESKTOP))|| \
        ((RpcExIsInNTService() == TRUE) && (g_RpcExAssertFlags & RPC_EX_ASSERT_2_DESKTOP_IN_SERVICE))) && \
        (RpcExIsInteractive() == TRUE)) \
    { \
        RpcExMessage2Desktop( \
            UNICODE_STRING(__FILE__), \
            __LINE__, \
            UNICODE_STRING(#expr)); \
    }; \
} \
} while(0) \
... << RSDN@Home 1.1.4 beta 4 rev. 303>>
Народная мудрось
всем все никому ничего(с).
Re[3]: Stack Trace для бедных :)
От: Zibir Чехия www.tigru.org
Дата: 28.11.08 15:47
Оценка:
Попробовал себе поставить всё это, но получилось только в дебаг версии. В релизе она не может работать?
Re[4]: Stack Trace для бедных :)
От: Блудов Павел Россия  
Дата: 01.12.08 09:30
Оценка:
Здравствуйте, Zibir, Вы писали:

Z>Попробовал себе поставить всё это, но получилось только в дебаг версии. В релизе она не может работать?

Может. Нужно включите в настройках генерацию .pdb файла. По умолчанию выключено.
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.