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

            牽著老婆滿街逛

            嚴以律己,寬以待人. 三思而后行.
            GMail/GTalk: yanglinbo#google.com;
            MSN/Email: tx7do#yahoo.com.cn;
            QQ: 3 0 3 3 9 6 9 2 0 .

            調(diào)試技巧之調(diào)用堆棧

            簡單介紹

            調(diào)試是程序開發(fā)者必備技巧。如果不會調(diào)試,自己寫的程序一旦出問題,往往無從下手。本人總結10年使用VC經(jīng)驗,對調(diào)試技巧做一個粗淺的介紹。希望對大家有所幫助。

            今天簡單的介紹介紹調(diào)用堆棧。調(diào)用堆棧在我的專欄的文章VC調(diào)試入門提了一下,但是沒有詳細介紹。

            首先介紹一下什么叫調(diào)用堆棧:假設我們有幾個函數(shù),分別是function1,function2,function3,funtion4,且function1調(diào)用function2,function2調(diào)用function3,function3調(diào)用function4。在function4運行過程中,我們可以從線程當前堆棧中了解到調(diào)用他的那幾個函數(shù)分別是誰。把函數(shù)的順序關系看,function4、function3、function2、function1呈現(xiàn)出一種“堆?!钡奶卣?,最后被調(diào)用的函數(shù)出現(xiàn)在最上方。因此稱呼這種關系為調(diào)用堆棧(call stack)。

            當故障發(fā)生時,如果程序被中斷,我們基本上只可以看到最后出錯的函數(shù)。利用call stack,我們可以知道當出錯函數(shù)被誰調(diào)用的時候出錯。這樣一層層的看上去,有時可以猜測出錯誤的原因。常見的這種中斷時ASSERT宏導致的中斷。

            在程序被中斷時,debug工具條的右側(cè)倒數(shù)第二個按鈕一般是call stack按鈕,這個按鈕被按下后,你就可以看到當前的調(diào)用堆棧。

            實例一:介紹

            我們首先演示一下調(diào)用堆棧。首先我們創(chuàng)建一個名為Debug的對話框工程。工程創(chuàng)建好以后,雙擊OK按鈕創(chuàng)建消息映射函數(shù),并添加如下代碼:

            void CDebugDlg::OnOK() 
            { 
            
            // TODO: Add extra validation here ASSERT(FALSE);
            }

            我們按F5開始調(diào)試程序。程序運行后,點擊OK按鈕,程序就會被中斷。這時查看call stack窗口,就會發(fā)現(xiàn)內(nèi)容如下:

            CDebugDlg::OnOK() line 176 + 34 bytes
            _AfxDispatchCmdMsg(CCmdTarget * 0x0012fe74 {CDebugDlg}, unsigned int 1, int 0, void (void)* 0x5f402a00 `vcall'(void), void * 0x00000000, unsigned int 12, AFX_CMDHANDLERINFO * 0x00000000) line 88
            CCmdTarget::OnCmdMsg(unsigned int 1, int 0, void * 0x00000000, AFX_CMDHANDLERINFO * 0x00000000) line 302 + 39 bytes
            CDialog::OnCmdMsg(unsigned int 1, int 0, void * 0x00000000, AFX_CMDHANDLERINFO * 0x00000000) line 97 + 24 bytes
            CWnd::OnCommand(unsigned int 1, long 656988) line 2088
            CWnd::OnWndMsg(unsigned int 273, unsigned int 1, long 656988, long * 0x0012f83c) line 1597 + 28 bytes
            CWnd::WindowProc(unsigned int 273, unsigned int 1, long 656988) line 1585 + 30 bytes
            AfxCallWndProc(CWnd * 0x0012fe74 {CDebugDlg hWnd=???}, HWND__ * 0x001204b0, unsigned int 273, unsigned int 1, long 656988) line 215 + 26 bytes
            AfxWndProc(HWND__ * 0x001204b0, unsigned int 273, unsigned int 1, long 656988) line 368
            AfxWndProcBase(HWND__ * 0x001204b0, unsigned int 273, unsigned int 1, long 656988) line 220 + 21 bytes
            USER32! 77d48709()
            USER32! 77d487eb()
            USER32! 77d4b368()
            USER32! 77d4b3b4()
            NTDLL! 7c90eae3()
            USER32! 77d4b7ab()
            USER32! 77d7fc9d()
            USER32! 77d76530()
            USER32! 77d58386()
            USER32! 77d5887a()
            USER32! 77d48709()
            USER32! 77d487eb()
            USER32! 77d489a5()
            USER32! 77d489e8()
            USER32! 77d6e819()
            USER32! 77d65ce2()
            CWnd::IsDialogMessageA(tagMSG * 0x004167d8 {msg=0x00000202 wp=0x00000000 lp=0x000f001c}) line 182
            CWnd::PreTranslateInput(tagMSG * 0x004167d8 {msg=0x00000202 wp=0x00000000 lp=0x000f001c}) line 3424
            CDialog::PreTranslateMessage(tagMSG * 0x004167d8 {msg=0x00000202 wp=0x00000000 lp=0x000f001c}) line 92
            CWnd::WalkPreTranslateTree(HWND__ * 0x001204b0, tagMSG * 0x004167d8 {msg=0x00000202 wp=0x00000000 lp=0x000f001c}) line 2667 + 18 bytes
            CWinThread::PreTranslateMessage(tagMSG * 0x004167d8 {msg=0x00000202 wp=0x00000000 lp=0x000f001c}) line 665 + 18 bytes
            CWinThread::PumpMessage() line 841 + 30 bytes
            CWnd::RunModalLoop(unsigned long 4) line 3478 + 19 bytes
            CDialog::DoModal() line 536 + 12 bytes
            CDebugApp::InitInstance() line 59 + 8 bytes
            AfxWinMain(HINSTANCE__ * 0x00400000, HINSTANCE__ * 0x00000000, char * 0x00141f00, int 1) line 39 + 11 bytes
            WinMain(HINSTANCE__ * 0x00400000, HINSTANCE__ * 0x00000000, char * 0x00141f00, int 1) line 30
            WinMainCRTStartup() line 330 + 54 bytes
            KERNEL32! 7c816d4f()
            

            這里,CDebugDialog::OnOK作為整個調(diào)用鏈中最后被調(diào)用的函數(shù)出現(xiàn)在call stack的最上方,而內(nèi)核中程序的啟動函數(shù)Kernel32! 7c816d4f()則作為棧底出現(xiàn)在最下方。

            實例二:學習處理方法

            微軟提供了MDI/SDI模型提供文檔處理的建議結構。有些時候,大家希望控制某個環(huán)節(jié)。例如,我們希望彈出自己的打開文件對話框,但是并不想自己實現(xiàn)整個文檔的打開過程,而更愿意MFC完成其他部分的工作??墒?,我們并不清楚MFC是怎么處理文檔的,也不清楚如何插入自定義代碼。

            幸運的是,我們知道當一個文檔被打開以后,系統(tǒng)會調(diào)用CDocument派生類的Serialize函數(shù),我們可以利用這一點來跟蹤MFC的處理過程。

            我們首先創(chuàng)建一個缺省的SDI工程Test1,并在CTest1Doc::Serialize函數(shù)的開頭增加一個斷點,運行程序,并打開一個文件。這時,我們可以看到調(diào)用堆棧是(我只截取了感興趣的一段):

            CTest1Doc::Serialize(CArchive & {...}) line 66
            CDocument::OnOpenDocument(const char * 0x0012f54c) line 714
            CSingleDocTemplate::OpenDocumentFile(const char * 0x0012f54c, int 1) line 168 + 15 bytes
            CDocManager::OpenDocumentFile(const char * 0x0042241c) line 953
            CWinApp::OpenDocumentFile(const char * 0x0042241c) line 93
            CDocManager::OnFileOpen() line 841
            CWinApp::OnFileOpen() line 37
            _AfxDispatchCmdMsg(CCmdTarget * 0x004177f0 class CTest1App  theApp, unsigned int 57601, int 0, void (void)* 0x00402898 CWinApp::OnFileOpen, void * 0x00000000, unsigned int 12, AFX_CMDHANDLERINFO * 0x00000000) line 88
            CCmdTarget::OnCmdMsg(unsigned int 57601, int 0, void * 0x00000000, AFX_CMDHANDLERINFO * 0x00000000) line 302 + 39 bytes
            CFrameWnd::OnCmdMsg(unsigned int 57601, int 0, void * 0x00000000, AFX_CMDHANDLERINFO * 0x00000000) line 899 + 33 bytes
            CWnd::OnCommand(unsigned int 57601, long 132158) line 2088
            CFrameWnd::OnCommand(unsigned int 57601, long 132158) line 317
            

            從上面的調(diào)用堆???,這個過程由一個WM_COMMAND消息觸發(fā)(因為我們用菜單打開文件),由CWinApp::OnFileOpen最先開始實際處理過程,這個函數(shù)調(diào)用CDocManager::OnFileOpen打開文檔。

            我們首先雙擊CWinApp::OnFileOpen() line 37打開CWinApp::OnFileOpen,它的處理過程是:

                ASSERT(m_pDocManager != NULL);
                m_pDocManager->OnFileOpen();
            

            m_pDocManager是一個CDocManager類的實例指針,我們雙擊CDocManager::OnFileOpen行,看該函數(shù)的實現(xiàn):

            void CDocManager::OnFileOpen()
            {
             // prompt the user (with all document templates)
             CString newName;
             if (!DoPromptFileName(newName, AFX_IDS_OPENFILE,
               OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, TRUE, NULL))
              return; // open cancelled
             AfxGetApp()->OpenDocumentFile(newName);
              // if returns NULL, the user has already been alerted
            }
            

            很顯然,該函數(shù)首先調(diào)用DoPromptFileName函數(shù)來獲得一個文件名,然后在繼續(xù)后續(xù)的打開過程。

            順這這個線索下去,我們一定能找到插入我們文件打開對話框的位置。由于這不是我們研究的重點,后續(xù)的分析我就不再詳述。

            實例三:內(nèi)存訪問越界

            在Debug版本的VC程序中,程序會給每塊new出來的內(nèi)存,預留幾個字節(jié)作為越界檢測之用。在釋放內(nèi)存時,系統(tǒng)會檢查這幾個字節(jié),判斷是否有內(nèi)存訪問越界的可能。

            我們借用前一個實例程序,在CTest1App::InitInstance的開頭添加以下幾行代碼:

                char * p = new char[10];
                memset(p,0,100);
                delete []p;
                return FALSE;
            

            很顯然,這段代碼申請了10字節(jié)內(nèi)存,但是使用了100字節(jié)。我們在memset(p,0,100);這行加一個斷點,然后執(zhí)行程序,斷點到達后,我們觀察p指向的內(nèi)存的值(利用Debug工具條的Memory功能),可以發(fā)現(xiàn)它的值是:

              CD CD CD CD CD CD CD CD
              CD CD FD FD FD FD FD FD
              00 00 00 00 00 00 00 00
              ......
            

            根據(jù)經(jīng)驗,p實際被分配了16個字節(jié),后6個字節(jié)用于保護。我們按F5全速執(zhí)行程序,會發(fā)現(xiàn)如下的錯誤信息被彈出:

               Debug Error!
               Program: c:\temp\test1\Debug\test1.exe
               DAMAGE: after normal block (#55) at 0x00421AB0
               Press Retry to debug the application
            

            該信息提示,在正常內(nèi)存塊0x00421AB0后的內(nèi)存被破壞(內(nèi)存訪問越界),我們點擊Retry進入調(diào)試狀態(tài),發(fā)現(xiàn)調(diào)用堆棧是:

            _free_dbg_lk(void * 0x00421ab0, int 1) line 1033 + 60 bytes
            _free_dbg(void * 0x00421ab0, int 1) line 970 + 13 bytes
            operator delete(void * 0x00421ab0) line 351 + 12 bytes
            CTest1App::InitInstance() line 54 + 15 bytes
            

            很顯然,這個錯誤是在調(diào)用delete時遇到的,出現(xiàn)在CTest1App::InitInstance() line 54 + 15 bytes之處。我們很容易根據(jù)這個信息找到,是在釋放哪塊內(nèi)存時出現(xiàn)問題,之后,我們只需要根據(jù)這個內(nèi)存的訪問過程確定哪兒出錯,這將大大降低調(diào)試的難度。

            實例四:子類化

            子類化是我們修改一個現(xiàn)有控件實現(xiàn)新功能的常用方法,我們借用實例一中的Debug對話框工程來演示我過去學習子類化的一個故事。我們創(chuàng)建一個缺省的名為Debug的對話框工程,并按照下列步驟進行實例化:

            1. 在對話框資源中增加一個Edit控件
            2. 用class wizard為CEdit派生一個類CMyEdit(由于今天不關心子類化的具體細節(jié),因此這個類不作任何修改)
            3. 為Edit控件,增加一個控件類型變量m_edit,其類型為CMyEdit
            4. 在OnInitDialog中增加如下語句:
              m_edit.SubclassDlgItem(IDC_EDIT1,this);

            我們運行這個程序,會遇到這樣的錯誤:

            				
            Debug Assertion Failed! Application:C:\temp\Debug\Debug\Debug.exe File:Wincore.cpp Line:311 For information on how your program can cause an assertion failure, see Visual C++ documentation on asserts. (Press Retry to debug the application)

            點擊Retry進入調(diào)試狀態(tài),我們可以看到調(diào)用堆棧為:

            CWnd::Attach(HWND__ * 0x000205a8) line 311 + 28 bytes
            CWnd::SubclassWindow(HWND__ * 0x000205a8) line 3845 + 12 bytes
            CWnd::SubclassDlgItem(unsigned int 1000, CWnd * 0x0012fe34 {CDebugDlg hWnd=0x001d058a}) line 3883 + 12 bytes
            CDebugDlg::OnInitDialog() line 120
            

            可以看出在Attach句柄時出現(xiàn)問題,出問題行的代碼為:

                ASSERT(m_hWnd == NULL);  
            

            這說明我們在子類化時不應該綁定控件,我們刪除CDebugDialog::DoDataExchange中的下面一行:

                DDX_Control(pDX, IDC_EDIT1, m_edit);
            
            問題就得到解決

            總結

            簡而言之,call stack是調(diào)試中必須掌握的一個技術,但是程序員需要豐富的經(jīng)驗才能很好的掌握和使用它。你不僅僅需要熟知C++語法,還需要對相關的平臺、軟件設計思路有一定的了解。我的文章只能算一個粗淺的介紹,畢竟我在這方面也不算高手。希望對新進有一定的幫助。

            posted on 2006-07-11 13:39 楊粼波 閱讀(351) 評論(0)  編輯 收藏 引用

            欧美国产成人久久精品| 久久久久久亚洲精品无码| 无码人妻久久一区二区三区蜜桃| 久久成人精品视频| 精品久久久久久无码专区不卡 | 久久国产劲爆AV内射—百度| 久久精品国产72国产精福利| 久久久精品国产亚洲成人满18免费网站| 97久久久久人妻精品专区| 精品国际久久久久999波多野| 久久久久久久久无码精品亚洲日韩| 蜜臀av性久久久久蜜臀aⅴ| 亚洲AV日韩精品久久久久| 亚洲熟妇无码另类久久久| 日产精品久久久一区二区| 欧美喷潮久久久XXXXx| 国产成人久久精品区一区二区| 色综合久久中文色婷婷| 久久人人爽人人爽人人片AV麻豆 | 国产精品久久网| 国产精品99久久久久久猫咪 | 亚洲精品无码专区久久同性男| 久久精品免费全国观看国产| 无码国产69精品久久久久网站| 精品精品国产自在久久高清| 久久精品无码一区二区app| 久久无码中文字幕东京热| 国内精品久久人妻互换| 久久国产精品一区| 综合人妻久久一区二区精品| 精品999久久久久久中文字幕| 国产—久久香蕉国产线看观看| 国产精品成人久久久| 色综合久久综精品| 国产成人无码精品久久久性色| 国产精品99久久久久久董美香| 777午夜精品久久av蜜臀| 亚洲乱亚洲乱淫久久| 午夜不卡久久精品无码免费| 国产精品九九久久免费视频 | 久久91综合国产91久久精品|