• <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>

            woaidongmao

            文章均收錄自他人博客,但不喜標(biāo)題前加-[轉(zhuǎn)貼],因其丑陋,見諒!~
            隨筆 - 1469, 文章 - 0, 評論 - 661, 引用 - 0
            數(shù)據(jù)加載中……

            也談Release版本排錯(cuò)

            通常Release除錯(cuò)都是先通過SetUnhandledExceptionFilter捕獲異常,然后生成報(bào)告文件,最后定位代碼行,主要以下兩種方法:

            (一)通過遍歷調(diào)用棧,將其調(diào)用棧信息輸出到文件。然后查找出錯(cuò)地址。

            查找方式有兩種:

            1)通過編譯器生成的包含行信息的map文件定位出錯(cuò)位置。

            通過在工程屬性”-“link”-“Project Options”手工輸入 /mapinfo:lines,生成包含行信息map文件。查找時(shí)首先根據(jù)出錯(cuò)地址范圍找到obj文件名,查看obj文件對應(yīng)的行信息,根據(jù)出錯(cuò)地址范圍定位代碼行。

            2)通過編譯器生成的pdb文件定位出錯(cuò)位置。

            debug版本會(huì)自動(dòng)生成pdb文件,Release版本需要在工程屬性”-“link”面板中勾上選項(xiàng)“Generate debug info”,然后在工程屬性” -“C/C++”面板的“Debug Info”列表框選中“Program Database”

            pdb文件中查找出錯(cuò)地址所在的代碼行,需要通過dbghelp庫(包含在windbg目錄下),通過SymFromAddr函數(shù)可以獲取符號(hào)信息,SymGetLineFromAddr64獲取所在代碼行。

            遍歷調(diào)用棧方法方法也有兩種:

            1)自己遍歷調(diào)用棧

            這種方法的缺陷是Release版本通常會(huì)使用FPOFrame-Pointer Omisstion) 優(yōu)化,(注:在VC編譯器中可以在工程屬性”—> C/C++”—>“Project Options中去掉選項(xiàng)Oy-關(guān)閉PFO優(yōu)化),PFO優(yōu)化主要是通過省略調(diào)用時(shí)棧指針的保存恢復(fù)等操作提高代碼效率。下面自己遍歷調(diào)用棧的方法對采用了FPO優(yōu)化的模塊可能會(huì)遍歷不完全,遺漏掉一些函數(shù)。因此,即使自己的模塊關(guān)閉了FPO,但第三方模塊使用了FPO,如果報(bào)錯(cuò)的地址位于第三方dll內(nèi)(例如mfc42.dll),將有可能回溯不到自己模塊內(nèi)有問題的函數(shù),從而很難定位bug

            自己遍歷基于以下原理(這個(gè)原理只適用于沒有采用FPO優(yōu)化的函數(shù)):

                1 函數(shù)調(diào)用時(shí)call指令將返回地址(通常是下一條指令的地址)壓入堆棧 。

                2 函數(shù)運(yùn)行第一行會(huì)將 ebp壓入堆棧,保存它以使得當(dāng)函數(shù)返回能恢復(fù)ebp

                3 Copy當(dāng)前棧位置esp ebp

            4.然后esp自減以空出棧空間容納函數(shù)的局部變量

            因此當(dāng)前函數(shù)內(nèi)的ebp即為第2步壓入ebp后的棧頂位置,由此可推導(dǎo)出上一層函數(shù)的ebp[ebp],而上一層函數(shù)返回地址即為前一個(gè)壓入棧的值,即[ebp+4],由此可以一步步往上回溯調(diào)用棧。

            2)通過dbghelp庫函數(shù)StackWalk64遍歷堆棧。

            這種方式可以選擇是否加載pdb,對于做了那些被FPO優(yōu)化的函數(shù),pdb保存了相關(guān)數(shù)據(jù)來幫助遍歷調(diào)用棧,如果不能加載到正確的pdbStackWalk64將使用前面介紹的基于ebp的方式遍歷調(diào)用棧,從而漏掉那些被FPO優(yōu)化的函數(shù)。

            (二)通過生成mini dump文件定位bug

            通過dbghelp庫函數(shù)MiniDumpWriteDump將出錯(cuò)時(shí)信息寫入文件,然后用windbg打開dump文件,配置好symbols路徑,exe文件路徑,source code 路徑,輸入.ecxr命令,就可以查看詳細(xì)的調(diào)用棧,并能自動(dòng)打開源文件定位到代碼行。因此這種方法是簡單和最可靠的方法。

            下面是一個(gè)簡單的dume類,只要添入到工程即可,出錯(cuò)時(shí)會(huì)自動(dòng)生成dum文件。

            #include <windows.h>

            #include <tchar.h>

            #include <assert.h>

            //for VC6

            #ifndef __in_bcount_opt

            #define __in_bcount_opt(x)

            #endif

            #ifndef __out_bcount_opt

            #define __out_bcount_opt(x)

            #endif

            //end (for VC6)

            #include "dbghelp.h"

            typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(

             IN HANDLE hProcess,

             IN DWORD ProcessId,

             IN HANDLE hFile,

             IN MINIDUMP_TYPE DumpType,

             IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL

             IN CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, OPTIONAL

             IN CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL

             );

            class CMiniDumper

            {

            public:

            CMiniDumper();

            private:

            static LPTOP_LEVEL_EXCEPTION_FILTER s_pPrevFilter;

            static long WINAPI UnhandledExceptionFilter( struct _EXCEPTION_POINTERS *pExceptionInfo );

            };

            CMiniDumper g_minObject;

            LPTOP_LEVEL_EXCEPTION_FILTER CMiniDumper::s_pPrevFilter = 0;

            CMiniDumper::CMiniDumper()

            {

            assert(!s_pPrevFilter);

            s_pPrevFilter = ::SetUnhandledExceptionFilter(UnhandledExceptionFilter);

            }

            long CMiniDumper::UnhandledExceptionFilter( struct _EXCEPTION_POINTERS *pExceptionInfo )

            {

            long ret = EXCEPTION_CONTINUE_SEARCH;

            TCHAR szDbgHelpPath[_MAX_PATH] = {0};

            TCHAR szDumpPath[_MAX_PATH] = {0};

            TCHAR szPath[_MAX_PATH] = {0};

            if (GetModuleFileName(NULL, szPath, _MAX_PATH))

            {

            TCHAR szDrive[_MAX_DRIVE] = {0};

            TCHAR szDir[_MAX_DIR] = {0};

            TCHAR szFileName[_MAX_FNAME] = {0};

            _tsplitpath(szPath, szDrive, szDir, szFileName, 0);

            _tcsncat(szDbgHelpPath, szDrive, _MAX_PATH);

            _tcsncat(szDbgHelpPath, szDir, _MAX_PATH - _tcslen(szDbgHelpPath) - 1);

            _tcsncat(szDbgHelpPath, _T("dbghelp.dll"), _MAX_PATH - _tcslen(szDbgHelpPath) - 1);

            _tcsncat(szDumpPath, szDrive, _MAX_PATH);

            _tcsncat(szDumpPath, szDir, _MAX_PATH - _tcslen(szDumpPath) - 1);

            _tcsncat(szDumpPath, szFileName, _MAX_PATH - _tcslen(szDumpPath) - 1);

            _tcsncat(szDumpPath, _T(".dmp"), _MAX_PATH - _tcslen(szDumpPath) - 1);

            }

            HMODULE hDll = ::LoadLibrary(szDbgHelpPath);

            if (hDll==NULL)

            hDll = ::LoadLibrary(_T("dbghelp.dll"));

            assert(hDll);

            if (hDll)

            {

            MINIDUMPWRITEDUMP pWriteDumpFun = (MINIDUMPWRITEDUMP)::GetProcAddress(hDll, "MiniDumpWriteDump");

            if (pWriteDumpFun)

            {

            // create the file

            HANDLE hFile = ::CreateFile(szDumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,

            FILE_ATTRIBUTE_NORMAL, NULL);

            if (hFile != INVALID_HANDLE_VALUE)

            {

            _MINIDUMP_EXCEPTION_INFORMATION ExInfo;

            ExInfo.ThreadId = ::GetCurrentThreadId();

            ExInfo.ExceptionPointers = pExceptionInfo;

            ExInfo.ClientPointers = FALSE;

            // write the dump

            if (pWriteDumpFun(GetCurrentProcess(), GetCurrentProcessId(),

            hFile, MiniDumpNormal, pExceptionInfo!=0? &ExInfo: 0, NULL, NULL))

            ret = EXCEPTION_EXECUTE_HANDLER;

            ::CloseHandle(hFile);

            }

            }

            }

            if (s_pPrevFilter)

            ret = s_pPrevFilter(pExceptionInfo);

            return ret;

            }

             

            posted on 2009-10-22 19:28 肥仔 閱讀(2113) 評論(0)  編輯 收藏 引用 所屬分類: 調(diào)試

            久久久久国产亚洲AV麻豆| 99精品国产综合久久久久五月天| 国产精品福利一区二区久久| 波多野结衣中文字幕久久| 超级碰久久免费公开视频| 国产精品久久久久久久久鸭| 久久精品国产99国产精品澳门| 国产呻吟久久久久久久92| 伊人久久一区二区三区无码| 久久99国产综合精品女同| 久久97久久97精品免视看| 久久久久久伊人高潮影院| 久久免费国产精品一区二区| 亚洲国产精品成人久久蜜臀 | 久久亚洲AV成人出白浆无码国产| 久久99精品国产麻豆| 久久久免费观成人影院 | 久久99精品国产99久久6男男| 国产精品成人99久久久久91gav| 亚洲国产精品成人AV无码久久综合影院 | 久久久久人妻精品一区二区三区| 亚洲国产成人久久综合一| 色综合久久天天综线观看| 91精品国产高清91久久久久久| 久久久久久久亚洲精品| 欧美丰满熟妇BBB久久久| 精品久久久久久国产牛牛app| 无码人妻精品一区二区三区久久 | 综合人妻久久一区二区精品 | 亚洲乱码精品久久久久..| 88久久精品无码一区二区毛片 | 久久天天婷婷五月俺也去| 久久免费国产精品一区二区| 国产69精品久久久久9999APGF| 韩国三级中文字幕hd久久精品 | 亚洲国产综合久久天堂| 丰满少妇人妻久久久久久4| 久久亚洲精品人成综合网| 中文字幕无码久久人妻| 狠狠久久综合| 久久国产精品-久久精品|