標籤:

c++ 程序運行時異常處理,怎麼定位到出錯代碼行?

這個問題困擾我好長時間了, 一直在網上尋找答案,總覺得還缺了點什麼, 以前是學Java的,代碼的任何問題都可以在異常堆棧里定位到,寫過java的朋友應該都知道, 可是c++就蛋疼了, 尤其我搞qt開發 ,用的是mingw庫。

像下面這種, lua關閉兩次,導致程序崩潰。下面的代碼是簡化的,錯誤也很明顯,我希望有個通用的解決方案,出現莫名其妙的錯誤的時候可以儘快找到出錯位置。

說下我嘗試過得途徑,開發工具是qtcreator

1、 調試 ,程序主要是網路通信和lua腳本交互,經常是調試的時候沒問題,release版本運行的時候出錯, 試過點上面的調試程序,然後查看堆棧,看到的是KiUserExceptionDispatcher 之類的東西,好像並沒有什麼用

2、 打日誌 ,多線程的網路通信+ lua腳本,通過日誌來找出錯位置的話,感覺還不如好好捋一遍代碼了,難度一樣很大

3、 生成dmp文件, 通過 SetUnhandledExceptionFilter 方法有時候能夠成功生成dmp文件,可是放到windbg裡面也沒找到有用的信息,另外mingw據考證是沒法生成pdb符號文件的。

4、如代碼上可見, try catch並沒有用,同時 __try __except 貌似需要vc?

簡單點說,有沒有一個解決方案,能夠像java程序一樣定位到出錯的代碼行以及列印堆棧信息。

有知道的朋友希望能給出實際代碼+截圖,代碼複雜點沒關係,

拒絕抖機靈or裝逼, 也盡量別貼外鏈文章了, 糾結了好幾個月,網上能找到的文章都看了個遍。


#define MY_THROW(...)
assert();
someloglib::log("fuck:",__FILE__,__LINE__);
throw __VA_ARGS__

斷言異常log,啥都有了.


boost.stacktrace正在評審,如果你懶得等用windbg或者libunwind也能湊合一下,就是有點麻煩,而且C/C++程序經常有指針跑飛寫亂stack的情況,這時候你也幹不了什麼。


你從一開始就選錯了方案,mingw是用來編譯那些VC++編譯不了的跨平台的開源代碼庫的,而不是用來編譯整個工程的。

因為個別開源代碼用了很多 GCC 特性,無法用VC++編譯。而mingw作為一個windows編譯器是不合格的,缺點太多了。

Qt是完全支持用 VC++編譯的,而你的調試問題到了VC++下就完全不是問題了。

在VC++下,可以選擇在release模式下也生成PDB文件,這樣調試起來和debug區別就很小了。


換IDE

如下圖。。

輕鬆看到問題。。


MinGW好辦,

用backtrace函數,取得堆棧。

之後用cxxabi::demangle,把被mangle的各種函數名,轉換為可讀的。。

vc不需要demangle

clang和gcc需要demangle。

手裡代碼都是商業的,不方便貼圖。

我正在寫一個開源庫,等有空實現這個功能。


很遺憾並不存在一個完美的解決方案。C++(包括其它任意native語言)要想在出現「異常」的時候能抓住現場,需要做大量工作的,而這些工作,是建立在對當前體系結構和操作系統相關知識充分了解之下的。相關知識如下:

了解當前體系結構下CPU提供的異常處理機制。

了解當前操作系統提供的異常處理機制。

當前C++編譯器是如何使用上述機制來實現try...catch的。

當前C++編譯器是如何使用棧內存的。

當前C++編譯器提供的crt 函數和基本的系統API哪些能「安全的運行」。

當前C++編譯器是如何生成調試信息。

這些知識的掌握需要不少的時間和實際經驗,一個成熟的解決方案包括異常的現場保存,現場分析,上報,後台分析(可使用調試信息),構建後的調試信息保存,一般都是由公司里的資深架構師完成。如果你在上述系統的支持下能獨立完成絕大多數異常的分析,已經是相當有經驗的native 工程師了。

最後給一點實際的建議:

1.google有一個開源項目breakpad,基本實現了上面所說的解決方案。你可以直接使用,但並不會是完美的,最終還是會有1,2個麻煩的問題(你用lua C API 一定會碰到)需要你自己從原理往上分析。

2.C++語言的異常處理就是雞肋,解決的問題不多,帶來的麻煩不小。請完全不要用,尤其不要寫catch(...)這樣的代碼。 秉承這個原則以後,至少不需要掌握「當前C++編譯器是如何使用上述機制來實現try...catch的。」 這個知識了。


簡單的說,兩個問題。

1.mingw沒有win平台的異常處理,可以用本地函數SetUnhandledExceptionFilter自己寫一個。

2.mingw沒有生成配套pdb,可以用cv2pdb生成一個。但release版本沒有-g參數,本來就沒有調試信息,就沒有辦法了。

所以最好辦法就是,能用vs就上vs了,啥都有。


先要弄明白一點:語言層次的exception和操作系統層次提供的exception是兩回事。


推薦閱讀:

如何優雅的處理(或忽略)c++函數返回值代表的錯誤?
python程序報錯後除了try except之外有沒有好的辦法再次啟動?

TAG:C | 異常處理 |