• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            大龍的博客

            常用鏈接

            統(tǒng)計

            最新評論

            記錄程序崩潰時的調(diào)用堆棧

            在程序release之后,不可避免的會存在一些bug,測試人員和最終用戶如何在發(fā)現(xiàn)bug之后指導(dǎo)開發(fā)人員進行更正呢?在MS的網(wǎng)站上,有一篇名為"Under the hook"的文章,講述了如何把程序崩潰時的函數(shù)調(diào)用情況記錄為日志的方法,對此感興趣的讀者可以去看一看原文,那里提供源代碼和原理的說明。

            文章的作者提供了一個MSJExceptionHandler類來實現(xiàn)這一功能,這個類的使用方法很簡單,只要把這個類加入到你的工程中并和你的程序一起編譯就可以了,由于在這個類的實現(xiàn)文件中把自己定義為一個全局的類對象,所以,不用加入任何代碼,#include都不需要。

            當程序崩潰時,MSJExceptionHandler就會把崩潰時的堆棧調(diào)用情況記錄在一個.rpt文件中,軟件的測試人員或最終用戶只要把這個文件發(fā)給你,而你使用記事本打開這個文件就可以查看崩潰原因了。你需要在發(fā)行軟件的時候,為你的程序生成一個或幾個map文件,用于定位出錯的文件和函數(shù)。(我的另一篇blog中有關(guān)于生成map文件和定位錯誤的詳細說明)為了方便使用,這里附上該類的完整代碼:

            // msjexhnd.h

            #ifndef __MSJEXHND_H__
            #define __MSJEXHND_H__

            class MSJExceptionHandler
            {
                public:
               
                MSJExceptionHandler( );
                ~MSJExceptionHandler( );
               
                void SetLogFileName( PTSTR pszLogFileName );

                private:

                // entry point where control comes on an unhandled exception
                static LONG WINAPI MSJUnhandledExceptionFilter(
                                            PEXCEPTION_POINTERS pExceptionInfo );

                // where report info is extracted and generated
                static void GenerateExceptionReport( PEXCEPTION_POINTERS pExceptionInfo );

                // Helper functions
                static LPTSTR GetExceptionString( DWORD dwCode );
                static BOOL GetLogicalAddress(  PVOID addr, PTSTR szModule, DWORD len,
                                                DWORD& section, DWORD& offset );
                static void IntelStackWalk( PCONTEXT pContext );
                static int __cdecl _tprintf(const TCHAR * format, ...);

                // Variables used by the class
                static TCHAR m_szLogFileName[MAX_PATH];
                static LPTOP_LEVEL_EXCEPTION_FILTER m_previousFilter;
                static HANDLE m_hReportFile;
            };

            extern MSJExceptionHandler g_MSJExceptionHandler;   //  global instance of class

            #endif

            // msjexhnd.cpp

            //==========================================
            // Matt Pietrek
            // Microsoft Systems Journal, April 1997
            // FILE: MSJEXHND.CPP
            //==========================================

            #include <windows.h>
            #include <tchar.h>
            #include "msjexhnd.h"

            //============================== Global Variables =============================

            //
            // Declare the static variables of the MSJExceptionHandler class
            //

            TCHAR MSJExceptionHandler::m_szLogFileName[MAX_PATH];
            LPTOP_LEVEL_EXCEPTION_FILTER MSJExceptionHandler::m_previousFilter;
            HANDLE MSJExceptionHandler::m_hReportFile;


            MSJExceptionHandler g_MSJExceptionHandler;  // Declare global instance of class

            //============================== Class Methods =============================

            //=============
            // Constructor
            //=============

            MSJExceptionHandler::MSJExceptionHandler( )
            {
                // Install the unhandled exception filter function
                m_previousFilter = SetUnhandledExceptionFilter(MSJUnhandledExceptionFilter);

                // Figure out what the report file will be named, and store it away
                GetModuleFileName( 0, m_szLogFileName, MAX_PATH );

                // Look for the '.' before the "EXE" extension.  Replace the extension
                // with "RPT"

                PTSTR pszDot = _tcsrchr( m_szLogFileName, _T('.') );
                if ( pszDot )
                {
                    pszDot++;   // Advance past the '.'
                    if ( _tcslen(pszDot) >= 3 )
                        _tcscpy( pszDot, _T("RPT") );   // "RPT" -> "Report"
                }
            }

            //============
            // Destructor
            //============

            MSJExceptionHandler::~MSJExceptionHandler( )
            {
                SetUnhandledExceptionFilter( m_previousFilter );
            }

            //==============================================================
            // Lets user change the name of the report file to be generated
            //==============================================================

            void MSJExceptionHandler::SetLogFileName( PTSTR pszLogFileName )
            {
                _tcscpy( m_szLogFileName, pszLogFileName );
            }

            //===========================================================
            // Entry point where control comes on an unhandled exception
            //===========================================================

            LONG WINAPI MSJExceptionHandler::MSJUnhandledExceptionFilter(
                                                PEXCEPTION_POINTERS pExceptionInfo )
            {
                m_hReportFile = CreateFile( m_szLogFileName,
                                            GENERIC_WRITE,
                                            0,
                                            0,
                                            OPEN_ALWAYS,
                                            FILE_FLAG_WRITE_THROUGH,
                                            0 );

                if ( m_hReportFile )
                {
                    SetFilePointer( m_hReportFile, 0, 0, FILE_END );

                    GenerateExceptionReport( pExceptionInfo );

                    CloseHandle( m_hReportFile );
                    m_hReportFile = 0;
                }

                if ( m_previousFilter )
                    return m_previousFilter( pExceptionInfo );
                else
                    return EXCEPTION_CONTINUE_SEARCH;
            }

            //===========================================================================
            // Open the report file, and write the desired information to it.  Called by
            // MSJUnhandledExceptionFilter                                              
            //===========================================================================
            void MSJExceptionHandler::GenerateExceptionReport(
                PEXCEPTION_POINTERS pExceptionInfo )
            {
                // Start out with a banner
                _tprintf( _T("http://=====================================================\n") );

                PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;

                // First print information about the type of fault
                _tprintf(   _T("Exception code: %08X %s\n"),
                            pExceptionRecord->ExceptionCode,
                            GetExceptionString(pExceptionRecord->ExceptionCode) );

                // Now print information about where the fault occured
                TCHAR szFaultingModule[MAX_PATH];
                DWORD section, offset;
                GetLogicalAddress(  pExceptionRecord->ExceptionAddress,
                                    szFaultingModule,
                                    sizeof( szFaultingModule ),
                                    section, offset );

                _tprintf( _T("Fault address:  %08X %02X:%08X %s\n"),
                            pExceptionRecord->ExceptionAddress,
                            section, offset, szFaultingModule );

                PCONTEXT pCtx = pExceptionInfo->ContextRecord;

                // Show the registers
                #ifdef _M_IX86  // Intel Only!
                _tprintf( _T("\nRegisters:\n") );

                _tprintf(_T("EAX:%08X\nEBX:%08X\nECX:%08X\nEDX:%08X\nESI:%08X\nEDI:%08X\n"),
                        pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx, pCtx->Esi, pCtx->Edi );

                _tprintf( _T("CS:EIP:%04X:%08X\n"), pCtx->SegCs, pCtx->Eip );
                _tprintf( _T("SS:ESP:%04X:%08X  EBP:%08X\n"),
                            pCtx->SegSs, pCtx->Esp, pCtx->Ebp );
                _tprintf( _T("DS:%04X  ES:%04X  FS:%04X  GS:%04X\n"),
                            pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs );
                _tprintf( _T("Flags:%08X\n"), pCtx->EFlags );

                // Walk the stack using x86 specific code
                IntelStackWalk( pCtx );

                #endif

                _tprintf( _T("\n") );
            }

            //======================================================================
            // Given an exception code, returns a pointer to a static string with a
            // description of the exception                                        
            //======================================================================

            LPTSTR MSJExceptionHandler::GetExceptionString( DWORD dwCode )
            {
                #define EXCEPTION( x ) case EXCEPTION_##x: return _T(#x);

                switch ( dwCode )
                {
                    EXCEPTION( ACCESS_VIOLATION )
                    EXCEPTION( DATATYPE_MISALIGNMENT )
                    EXCEPTION( BREAKPOINT )
                    EXCEPTION( SINGLE_STEP )
                    EXCEPTION( ARRAY_BOUNDS_EXCEEDED )
                    EXCEPTION( FLT_DENORMAL_OPERAND )
                    EXCEPTION( FLT_DIVIDE_BY_ZERO )
                    EXCEPTION( FLT_INEXACT_RESULT )
                    EXCEPTION( FLT_INVALID_OPERATION )
                    EXCEPTION( FLT_OVERFLOW )
                    EXCEPTION( FLT_STACK_CHECK )
                    EXCEPTION( FLT_UNDERFLOW )
                    EXCEPTION( INT_DIVIDE_BY_ZERO )
                    EXCEPTION( INT_OVERFLOW )
                    EXCEPTION( PRIV_INSTRUCTION )
                    EXCEPTION( IN_PAGE_ERROR )
                    EXCEPTION( ILLEGAL_INSTRUCTION )
                    EXCEPTION( NONCONTINUABLE_EXCEPTION )
                    EXCEPTION( STACK_OVERFLOW )
                    EXCEPTION( INVALID_DISPOSITION )
                    EXCEPTION( GUARD_PAGE )
                    EXCEPTION( INVALID_HANDLE )
                }

                // If not one of the "known" exceptions, try to get the string
                // from NTDLL.DLL's message table.

                static TCHAR szBuffer[512] = { 0 };

                FormatMessage(  FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,
                                GetModuleHandle( _T("NTDLL.DLL") ),
                                dwCode, 0, szBuffer, sizeof( szBuffer ), 0 );

                return szBuffer;
            }

            //==============================================================================
            // Given a linear address, locates the module, section, and offset containing 
            // that address.                                                              
            //                                                                            
            // Note: the szModule paramater buffer is an output buffer of length specified
            // by the len parameter (in characters!)                                      
            //==============================================================================
            BOOL MSJExceptionHandler::GetLogicalAddress(
                    PVOID addr, PTSTR szModule, DWORD len, DWORD& section, DWORD& offset )
            {
                MEMORY_BASIC_INFORMATION mbi;

                if ( !VirtualQuery( addr, &mbi, sizeof(mbi) ) )
                    return FALSE;

                DWORD hMod = (DWORD)mbi.AllocationBase;

                if ( !GetModuleFileName( (HMODULE)hMod, szModule, len ) )
                    return FALSE;

                // Point to the DOS header in memory
                PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod;

                // From the DOS header, find the NT (PE) header
                PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(hMod + pDosHdr->e_lfanew);

                PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION( pNtHdr );

                DWORD rva = (DWORD)addr - hMod; // RVA is offset from module load address

                // Iterate through the section table, looking for the one that encompasses
                // the linear address.

                for (   unsigned i = 0;
                        i < pNtHdr->FileHeader.NumberOfSections;
                        i++, pSection++ )
                {
                    DWORD sectionStart = pSection->VirtualAddress;
                    DWORD sectionEnd = sectionStart
                                + max(pSection->SizeOfRawData, pSection->Misc.VirtualSize);

                    // Is the address in this section???
                    if ( (rva >= sectionStart) && (rva <= sectionEnd) )
                    {
                        // Yes, address is in the section.  Calculate section and offset,
                        // and store in the "section" & "offset" params, which were
                        // passed by reference.

                        section = i+1;
                        offset = rva - sectionStart;
                        return TRUE;
                    }
                }

                return FALSE;   // Should never get here!
            }

            //============================================================
            // Walks the stack, and writes the results to the report file
            //============================================================

            void MSJExceptionHandler::IntelStackWalk( PCONTEXT pContext )
            {
                _tprintf( _T("\nCall stack:\n") );

                _tprintf( _T("Address   Frame     Logical addr  Module\n") );

                DWORD pc = pContext->Eip;
                PDWORD pFrame, pPrevFrame;
               
                pFrame = (PDWORD)pContext->Ebp;

                do
                {
                    TCHAR szModule[MAX_PATH] = _T("");
                    DWORD section = 0, offset = 0;

                    GetLogicalAddress((PVOID)pc, szModule,sizeof(szModule),section,offset );

                    _tprintf( _T("%08X  %08X  %04X:%08X %s\n"),
                                pc, pFrame, section, offset, szModule );

                    pc = pFrame[1];

                    pPrevFrame = pFrame;

                    pFrame = (PDWORD)pFrame[0]; // precede to next higher frame on stack

                    if ( (DWORD)pFrame & 3 )    // Frame pointer must be aligned on a
                        break;                  // DWORD boundary.  Bail if not so.

                    if ( pFrame <= pPrevFrame )
                        break;

                    // Can two DWORDs be read from the supposed frame address?         
                    if ( IsBadWritePtr(pFrame, sizeof(PVOID)*2) )
                        break;

                } while ( 1 );
            }

            //============================================================================
            // Helper function that writes to the report file, and allows the user to use
            // printf style formating                                                    
            //============================================================================

            int __cdecl MSJExceptionHandler::_tprintf(const TCHAR * format, ...)
            {
                TCHAR szBuff[1024];
                int retValue;
                DWORD cbWritten;
                va_list argptr;
                     
                va_start( argptr, format );
                retValue = wvsprintf( szBuff, format, argptr );
                va_end( argptr );

                WriteFile( m_hReportFile, szBuff, retValue * sizeof(TCHAR), &cbWritten, 0 );

                return retValue;
            }

            posted on 2007-12-05 17:02 大龍 閱讀(3576) 評論(0)  編輯 收藏 引用


            只有注冊用戶登錄后才能發(fā)表評論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            理论片午午伦夜理片久久| 欧美久久一区二区三区| 综合久久给合久久狠狠狠97色| 久久久久亚洲AV成人片| 久久人人爽人人爽人人片AV麻烦| 久久久久人妻精品一区三寸蜜桃| 亚洲国产精品久久久久婷婷老年 | 久久婷婷色香五月综合激情 | 亚洲午夜久久久影院伊人| 日本久久中文字幕| 久久久久久国产精品美女| 精品久久久久久无码人妻热| 品成人欧美大片久久国产欧美... 品成人欧美大片久久国产欧美 | 人人狠狠综合久久88成人| 色欲综合久久躁天天躁蜜桃| 久久综合狠狠综合久久| 丁香五月网久久综合| 香蕉久久一区二区不卡无毒影院| 国产亚洲美女精品久久久久狼| 日本三级久久网| 欧美一级久久久久久久大| 亚洲精品无码久久久久AV麻豆| 77777亚洲午夜久久多人| 久久亚洲国产成人精品性色| 色成年激情久久综合| 午夜精品久久久内射近拍高清 | 久久精品中文字幕一区| 亚洲欧美成人久久综合中文网| 久久久SS麻豆欧美国产日韩| 久久99国产综合精品| 久久久久国产精品嫩草影院| 亚洲国产精品一区二区久久hs| 国产精品久久久久影院嫩草| 久久亚洲欧洲国产综合| 人妻无码αv中文字幕久久琪琪布| 久久九九青青国产精品| 亚洲国产精品一区二区三区久久| 久久精品国产久精国产思思| 日本精品久久久久影院日本| 久久伊人精品青青草原高清| 97香蕉久久夜色精品国产 |