方法:
- 使用.map以及.cod定位bug(暴力崩潰)
- 使用.dmp定位bug(優雅崩潰)
注意事項:在最後面^_^。
完整項目源碼請點擊這裏!
一.使用.map以及.cod定位bug(暴力崩潰)
首先介紹在軟件異常崩潰後如何使用.map以及.cod定位bug。
需要進行的工程項目屬性配置:
- xxx屬性->配置屬性->C/C++->輸出文件->彙編程序輸出,選擇 程序集、機器碼和源代碼 (/FAcs)
- xxx屬性->配置屬性 -> 鏈接器 -> 調試 -> 生成映射文件,選擇 是 (/MAP)
設置好項目屬性後,按下F7進行編譯,可以看到工程目錄下生成對應的.map文件:
那.cod文件在哪裏呢?
找不到搜索一下就知道了:
可以看到每個.cpp文件會對應的生成一個.cod。
下面來演示一下如何使用.map以及.cod精確定位到程序崩潰的位置:
首先,運行.exe,讓它暴力的崩掉:
這是一件很恐怖的事情!
接下來,我們點擊一下“查看問題詳細信息”這個擴展按鈕;
涼涼!差點。還好這次我們還有神器.map和.cod。
下面就是見證奇蹟的地方:
a1. 打開.map文件(vs就可以打開了,當然用記事本也是沒有問題滴),我們可以看到開頭的是這幾行:
ExceptionHandleTest:工程名
Timestamp is 5c7de239 (Tue Mar 05 10:43:05 2019):工程是什麼時間編譯的
Preferred load address is 0000000140000000:(三角形重點)基地址
接下來是data段了,這裏不用管。
再繼續往下看,可以看到畫紅色底線這行了,重點又來了:
Rva:偏移地址
Base:基地址
紅色線下面開始就是函數信息,而我們需要的信息隱藏在其中。
在Windows彈出的崩潰信息窗口可以看到
偏移地址:0000000000001ce5
而基地址(在.map開頭):0000000140000000
那麼軟件崩潰的Rva+Base就是0000000140001ce5這裏了,當然.map裏並沒有對每一個地址都做詳盡的記錄,我們要做的是找到比這個地址小一點的地方就能大概找到崩潰的位置了。
在本例中,Ctrl+F,輸入0140001c進行查找
剛好找到一個比0000000140001ce5稍微要小的地址,我們來看一下這一行告訴了我們什麼信息
可以看到,這這裏是ExceptionHandleTestDlg的fun函數裏,對應地址是0000000140001c90。到了這裏可以大概定位到了崩潰函數的位置了。但是我們還可以更進一步定位到是在哪一行代碼崩潰的。
終於輪到我們的.cod出場了。
找到ExceptionHandleTestDlg.cod,雙擊打開,查找到以?fun@CExceptionHandleTestDlg開頭的一行
包含這一串字符的會有不少行,但是以其開頭的只有這一行,對,就是藍色這一行。
然後我們來看一看我們找到的這一行下面的是什麼
; 179 : int b[10]; --> 行數 : 源碼
0001d c7 44 24 60 00 --> 偏移地址 機器碼
接下來我們就要做一個簡單的計算,以確定是在哪一行崩了的。
從windows彈出的軟件崩潰提示信息窗口中我們得知的:
異常偏移地址Rva:0000000000001ce5
那麼異常地址 expeAddr = expeRva + Base = 0000000140001ce5
而我們找到的函數地址 findAddr = 0000000140001c90
那麼導致崩潰對應的代碼所在行的偏移地址 = expeAddr - findAddr = 0x0055
那麼我們找到fun函數下偏移0055的地方:
從這行往上一點點,我們看到了: ; 193 : *c = 5;
好開心,終於找到你了。
然後打開CExceptionHandleTestDlg.cpp,找到第193行,可以看到:
我的天,竟然定義了指針,沒開闢內存就直接賦值了。
至此,利用.map和.cod兩大神器找崩結束,修bug就交給你了。
二.使用.dmp定位bug(優雅崩潰)
首先需要進行的工程項目屬性配置:
1.xxx屬性 -> 配置屬性 -> C/C++ -> 常規 -> 調試信息格式,選擇 程序數據庫 (/Zi)
- xxx屬性 -> 配置屬性 -> C/C++ -> 優化 -> 優化,選擇 已禁用 (/Od)
(淺嘗不選這個也行,尚未深究)
- xxx屬性 -> 配置屬性 -> 連接器 -> 調試 -> 生成調試信息,選擇 是
接下來就是如何生成.bmp文件了,詳情觀看一下例程。
在CExceptionHandleTestApp.h類中#include "HiExceptionHandle.h",並定義一個HiExceptionHandle類對象excep:
然後在啓動軟件時,運行的比較早的函數中添加函數調用:excep.RunCrashHandler();
然後,F7編譯。
接着再次讓軟件進入崩潰:
可以看到這次顯示竟然不是Windows自動彈出的“xxx停止工作”那個讓人絕望的窗口了,而是彈出我們自己設定的窗口,點擊確定就可以優雅的關掉軟件了,並且可以看到關掉軟件後生成了.dmp文件:
下面就讓我們來試一下如何.dmp來快速定位使軟件崩潰的位置吧。
雙擊打開HiMiniDump.dmp(VS2010即可打開),進入以下界面:
在該界面中,我們可以看到並不明顯的右上角“操作”這個框框:
那麼在此呢,我們需要保證幾個東西的一致性,即.exe .pdb .dmp是同一個版本的,當然還有相對應的源碼也要保持一致。
然後接下來呢,有兩種情況:
- .dmp文件是跟我們工程是放在一起的,那麼直接點擊“使用 僅限本機 進行調試”,即可定位到使程序發生崩潰的地方啦啦啦。
- 我們.exe是發佈出去的,是在另一臺電腦上使用的,或者隨便找個文件夾丟進去測試的,當我們用VS2010打開.dmp文件,需要點擊“設置符號路徑”進行簡單的設置。例如我在“D:\\新建文件夾\\Release”進行測試,
那麼設置一下符號路徑:
(其實我在本機上也不用這麼設置,但是拷貝到另一臺電腦測試時就要)
點擊“確定”,然後點擊“使用 僅限本機 進行調試”,進入以下界面:
點擊“中斷”,這裏需要稍等一會兒,莫心急哈。
然後,我們把“調用堆棧”的滑條滑到最上面,你就能驚奇的發現點蛛絲馬跡:
對!就是這個:
> ExceptionHandleTest.exe!CExceptionHandleTestDlg::fun(int * a) 行 193 + 0x5 字節 C++
然後興奮的雙擊它,額!怎麼彈出下面這個框的?
嗦嘎,原來是需要對應的源文件,那就找到這個.cpp,拷過來瞧一瞧會發生點什麼吧。
把對應的.cpp放進去後,打開,竟然,竟然神奇的定位到了念念不忘的可愛的bug的位置了:
至此,完成了使軟件崩潰的bug的查找,完善bug就交給你了。
注意事項:
以上兩種方法的使用都有前提,那就是要保證用到的文件要保持版本一致性,所以要做好相應的備份哦!
另外,在測試中發現數組越界導致的崩潰竟無法定位到,好傷心,各位大神大咖有什麼建議意見還望不吝指點小弟迷津。感激閱讀!
完整工程源碼請點擊這裏!