目錄
1、UI界面卡頓問題排查
2、軟件CPU占用高問題排查
3、UI界面顯示異常(GDI對象泄漏導致窗口繪制異常)問題排查
4、軟件線程堵塞(包含線程死鎖)問題排查
5、最后
C++軟件異常排查從入門到精通系列教程(核心精品專欄,訂閱量已達5000多個,歡迎訂閱,持續更新...)https://blog.csdn.net/chenlycly/article/details/125529931C/C++實戰專欄(重點專欄,專欄文章已更新500多篇,訂閱量已達3000多個,歡迎訂閱,持續更新中...)
https://blog.csdn.net/chenlycly/article/details/140824370C++ 軟件開發從入門到實戰(重點專欄,專欄文章已更新300多篇,歡迎訂閱,持續更新中...)
https://blog.csdn.net/chenlycly/category_12695902.htmlVC++常用功能開發匯總(專欄文章列表,歡迎訂閱,持續更新...)
https://blog.csdn.net/chenlycly/article/details/124272585C++軟件分析工具從入門到精通案例集錦(專欄文章,持續更新中...)
https://blog.csdn.net/chenlycly/article/details/131405795開源組件及數據庫技術(專欄文章,持續更新中...)
https://blog.csdn.net/chenlycly/category_12458859.html網絡編程與網絡問題分享(專欄文章,持續更新中...)
https://blog.csdn.net/chenlycly/category_2276111.html? ? ? ?我們在開發調試C++軟件的過程中,時常會遇到軟件UI界面卡頓、CPU占用高、GDI對象泄漏、線程堵塞(死鎖)這些常見問題,本文對這些問題的分析與排查方法進行詳細的總結,并給出相關的實戰分析實例,供大家借鑒或參考。
1、UI界面卡頓問題排查
? ? ? ?當UI界面時不時出現卡頓,可能是UI線程執行了不該執行的、比較耗時的操作引起的。我們在項目中遇到過,一個問題場景是在UI線程中向文件寫日志,一個問題場景是在UI線程的代碼中調用了Sleep函數。
? ? ? ?對于寫日志到文件中,會比較耗時,會直接導致UI線程的短暫堵塞。因為寫文件時的文件IO操作,相對與內存讀寫操作會慢很多,一般不能在UI線程中向文件中寫日志,應該把寫日志的操作放到新開的線程中。我們在項目中曾經遇到過這樣的問題,將寫日志的代碼移到新開的線程中。
? ? ? ?對于在UI線程中調用Sleep函數,當UI線程執行到Sleep函數調用時,UI線程會進入短暫的睡眠狀態,UI線程會短暫的掛起不執行,所以會出現UI界面卡頓。我們在項目中遇到的問題場景是這樣的,同事為了實現某個功能,從網上拷貝了一段代碼,將該段代碼直接放置在UI線程中。代碼中設置了定時器,定時執行該段代碼,而這段代碼中調用了Sleep函數:
void CalCPURate()
{// ...Sleep(500);// ...
}
上述代碼運行在UI線程中。當UI線程執行到Sleep函數時,UI線程就會被掛起不執行,從而導致UI界面的堵塞。
? ? ? ?這個UI界面時不時卡頓問題,最初是測試人員發現的,他們反饋這個問題是最近才出現的,前端時間是沒有的。但負責開發當前軟件的同事嘗試查了一下源碼,并沒有找出問題。于是我建議同事用歷史版本比對法,在時間上使用二分法,安裝不同時間點的版本,看看問題是從哪天出現的,引發問題的代碼點應該就在前一天提交的代碼中。最后定位了問題,是從網上拷貝的代碼中調用了Sleep導致的堵塞。
? ? ? ?關于UI界面卡頓,我之前做過詳細的總結,可以查看我的文章:
C++程序卡死、UI界面卡頓問題的原因分析與總結https://blog.csdn.net/chenlycly/article/details/128703097
2、軟件CPU占用高問題排查
? ? ? ?軟件占用CPU高,一般是因為程序中在不停歇的執行代碼,一般是代碼發生死循環導致的。代碼發生死循環,可能是以下幾個原因導致的:
1)在for或while循環中循環條件有問題,導致循環一直結束不了。比如因為手誤,將循環條件中的i<=5,錯誤寫成了i=5賦值操作:
for ( int i = 0; i = nChannelNum - 1; i++)
{// ......
}
導致一直跳不出循環。再比如循環條件中引用了一個變量,而這個變量在某種場景下出現了異常大的值:(變量nChannelNum的值是一個異常大的值)
for ( int i = 0; i < nChannelNum; i++)
{// ......
}
導致代碼一直在不停的循環。
2)窗口消息引發的死循環。這個在項目中遇到過,在一個窗口消息A的消息響應函數中調用某個函數或者某段代碼,這段代碼中又產生了窗口消息B,然后窗口消息B的響應函數中又觸發了窗口消息A的產生,這樣就產生了窗口消息引發的代碼死循環調用,引發死循環。
3)和平臺服務器業務交互消息引發的死循環。這個在項目中也遇到過,客戶端調用了A接口與服務器交互,然后服務器給出了回應消息,然后客戶端在處理該回應消息的響應函數中又調用了A接口,這樣就形成了循環閉環,從而產生了由業務消息引發的死循環。
? ? ? ?如果軟件占用的CPU較高,可能會導致當前電腦占用的總CPU比較高,會導致電腦系統出現卡頓或者明顯的卡頓。可以通過查看Windows任務管理器查看軟件的CPU占用比例和整個電腦的CPU占用比例。
? ? ? 對于軟件CPU占用高的排查,可以借助Process Explorer、Windbg等工具進行分析。可以用Process Explorer中查看軟件進程中所有線程的CPU占用情況,找到占用CPU最高的線程:
雙擊查看該線程的函數調用堆棧:
通過調用堆棧分析占用CPU高的原因。
? ? ? ?Process Explorer中查看的線程函數調用堆棧可能不準,可以將Windbg附加到軟件進程上使用~*kn命令將所有線程的函數調用堆棧打印出來:
然后利用Process Explorer中現實的線程id,到Windbg中找到對應的線程即可。必要時,可以在Windbg中設置斷點進行動態調試,看看具體是哪個函數中發生了死循環。
? ? ? ? 關于高CPU占用的實戰分析案例,我寫過多篇文章,可以查看:
使用Process Explorer/Process Hacker和Windbg高效排查C++程序高CPU占用問題https://blog.csdn.net/chenlycly/article/details/140731953使用Process Explorer和Clumsy工具定位軟件高CPU占用問題
https://blog.csdn.net/chenlycly/article/details/130038272
? ? ? ?在這里,給大家重點推薦一下我的幾個熱門暢銷專欄,歡迎訂閱:(博客主頁還有其他專欄,可以去查看)
專欄1:(該精品技術專欄的訂閱量已達到50000多個,專欄中包含大量項目實戰分析案例,有很強的實戰參考價值,廣受好評!專欄文章持續更新中,已經更新到200篇以上!歡迎訂閱!)
C++軟件調試與異常排查從入門到精通系列文章匯總https://blog.csdn.net/chenlycly/article/details/125529931
本專欄根據多年C++軟件異常排查的項目實踐,系統地總結了引發C++軟件異常的常見原因以及排查C++軟件異常的常用思路與方法,詳細講述了C++軟件的調試方法與手段,以圖文并茂的方式給出具體的項目問題實戰分析實例(很有實戰參考價值),帶領大家逐步掌握C++軟件調試與異常排查的相關技術,適合基礎進階和想做技術提升的相關C++開發人員!
考察一個開發人員的水平,一是看其編碼及設計能力,二是要看其軟件調試能力!所以軟件調試能力(排查軟件異常的能力)很重要,必須重視起來!能解決一般人解決不了的問題,既能提升個人能力及價值,也能體現對團隊及公司的貢獻!
專欄中的文章都是通過項目實戰總結出來的,包含大量項目問題實戰分析案例,有很強的實戰參考價值!專欄文章還在持續更新中,預計文章篇數能更新到200篇以上!
專欄2:(本專欄涵蓋了C++多方面的內容,是當前重點打造的專欄,訂閱量已達30000多個,專欄文章已經更新到500多篇,持續更新中...)
C/C++實戰進階(專欄文章,持續更新中...)https://blog.csdn.net/chenlycly/category_11931267.html
以多年的開發實戰為基礎,總結并講解一些的C/C++基礎與項目實戰進階內容,以圖文并茂的方式對相關知識點進行詳細地展開與闡述!專欄涉及了C/C++領域多個方面的內容,包括C++基礎及編程要點(模版泛型編程、STL容器及算法函數的使用等)、數據結構與算法、C++11及以上新特性(不僅看開源代碼會用到,日常編碼中也會用到部分新特性,面試時也會涉及到)、常用C++開源庫的介紹與使用、代碼分享(調用系統API、使用開源庫)、常用編程技術(動態庫、多線程、多進程、數據庫及網絡編程等)、軟件UI編程(Win32/duilib/QT/MFC)、C++軟件調試技術(排查軟件異常的手段與方法、分析C++軟件異常的基礎知識、常用軟件分析工具使用、實戰問題分析案例等)、設計模式、網絡基礎知識與網絡問題分析進階內容等。
專欄3:??
C++常用軟件分析工具從入門到精通案例集錦匯總(專欄文章,持續更新中...)https://blog.csdn.net/chenlycly/article/details/131405795
常用的C++軟件輔助分析工具有SPY++、PE工具、Dependency Walker、GDIView、Process Explorer、Process Monitor、API Monitor、Clumsy、Windbg、IDA Pro等,本專欄詳細介紹如何使用這些工具去巧妙地分析和解決日常工作中遇到的問題,很有實戰參考價值!
專欄4:???
VC++常用功能開發匯總(專欄文章,持續更新中...)https://blog.csdn.net/chenlycly/article/details/124272585
將10多年C++開發實踐中常用的功能,以高質量的代碼展現出來。這些常用的高質量規范代碼,可以直接拿到項目中使用,能有效地解決軟件開發過程中遇到的問題。
專欄5:?
C++ 軟件開發從入門到精通(專欄文章,持續更新中...)https://blog.csdn.net/chenlycly/category_12695902.html
根據多年C++軟件開發實踐,詳細地總結了C/C++軟件開發相關技術實現細節,分享了大量的實戰案例,很有實戰參考價值。
3、UI界面顯示異常(GDI對象泄漏導致窗口繪制異常)問題排查
? ? ? ?UI界面顯示不正常,窗口繪制異常,可能是軟件進程占用的GDI對象總數異常高導致的。這類問題我們在項目中多次遇到了,一般是使用GDI對象繪圖時有GDI對象泄漏導致的。GDI對象泄漏是指GDI對象使用完成后,沒有調用Delete或者Release相關接口將GDI對象釋放,從而產生了泄漏。
? ? ? ?對于GDI對象泄漏問題,我們可以借助GDIView工具來輔助排查。當UI界面顯示異常時,可以打開系統的任務管理器:
如果我們軟件進程的GDI對象異常的高,接近進程10000個上限,那肯定是GDI對象引起的。當進程的GDI對象達到或接近10000個上限時,程序中調用的GDI繪圖API函數可能會失敗,導致界面繪制失敗,從而引發界面顯示異常。
? ? ? ?可以使用GDIView查看到底是哪個GDI對象個數異常的高:
這樣排查起來會比較有針對性。但光知道發生泄漏的GDI對象類型,可能還是較難定位問題。可能軟件從上到下有很多模塊,不確定GDI對象泄漏發生在哪個模塊。此外,可能發生泄漏的代碼平時很難被頻繁的執行,只是在某種特定的場景下才會被頻繁地執行,這種情況比較具有隱蔽性,或者是這個問題隱藏的比較深。這兩類比較有代表性的問題場景,我們在項目中都遇到過。
? ? ? ?所以,除了使用GDIView工具之外,可能還需要結合歷史版本比對法(下面給出的案例會講到歷史版本比對法),找到發生泄漏的時間點,到svn或git上查看前一天提交的代碼或者底層發布過來的模塊,可能就能找到引發問題的代碼點了。
? ? ? ?之前寫過多篇排查GDI對象泄漏的案例,可以去查看這些文章:
使用GDIView工具排查GDI對象泄漏問題(實戰分析案例)https://blog.csdn.net/chenlycly/article/details/125399896使用GDIView工具排查GDI對象泄漏案例的若干細節總結(歷史版本比對法)
https://blog.csdn.net/chenlycly/article/details/141526436使用GDIView工具排查GDI對象泄漏導致C++程序UI界面繪制異常的問題
https://blog.csdn.net/chenlycly/article/details/140731065可以到GDIView等工具官網上或者微軟MSDN上查看文檔化說明去解決問題
https://blog.csdn.net/chenlycly/article/details/139565010使用GDI對象繪制UI時需要注意的若干細節問題總結
https://blog.csdn.net/chenlycly/article/details/144233359
4、軟件線程堵塞(包含線程死鎖)問題排查
? ? ? ?線程中調用了某個接口,該接口一直沒返回,這樣就會導致調用該接口的線程發生堵塞。如果UI線程堵塞了,就會導致UI界面卡死不可操作(無法點擊);如果業務線程堵塞了,則會導致業務出現異常(但UI界面不會卡死,UI界面可以正常點擊)。導致接口堵塞不返回,可能有以下幾個原因:
1)多線程死鎖:接口中申請了鎖,但鎖被其他線程占用,導致一直拿不到鎖,接口不返回,即產生了多線程死鎖。
2)安全軟件攔截:接口中執行了寫注冊表等操作,PC上安裝的安全軟件可能認為是不安全的,直接將操作攔截了,可能會導致接口不返回,這個場景我們在項目中遇到過。
3)接口中發生了死循環:接口中有for或while循環,因為某種原因到之后導致死循環,導致接口一直不返回。
? ? ? ?如果堵塞的是UI線程,則UI界面不可操作,UI線程卡死了,一般是調用底層的接口沒返回導致的,這種堵塞能被用戶第一時間察覺。如果堵塞的是業務線程,就要根據UI界面表現現象以及打印日志去判斷。比如加入會議后,看不到會議中的視頻圖像,可能是底層音視頻處理模塊中繪制視頻的業務線程出現了堵塞,沒法繪制視頻畫面。
? ? ? ?對于死循環引發的線程堵塞,會有個典型的特征,會導致高CPU占用,高CPU的排查方法我們上面已經說過了。
? ? ? ?對于安全軟件攔截引發的線程堵塞,可以將Windbg附加到出問題的進程上,查看所有線程的調用堆棧,找到目標線程堆棧中調用的函數,看看是否執行了寫注冊表等安全軟件比較敏感的操作,找出可能的原因。必要時可以借助日志去輔助分析。之前就遇到過這類案例,我寫了專題文章,可以去查看文章:
使用Windbg排查C++軟件安裝包安裝時被安全防護軟件攔截導致安裝線程堵塞卡住的問題https://blog.csdn.net/chenlycly/article/details/143670974
文章中有一點需要注意一下,雖然將騰訊電腦管家軟件退出了,但其實時防護模塊還在后臺運行,還是會持續攔截,除非將騰訊電腦管家卸載掉。
? ? ? ?對于多線程死鎖,也是將Windbg附加到出問題的進程上,查看所有線程的函數調用堆棧,看看哪些線程調用了WaitForSingleObject等等待函數,結合這些線程堆棧中調用的函數,確定這些線程和哪些業務有關。必要時也要結合日志去排查并最終定位問題。之前寫過使用Windbg排查多線程死鎖的實戰案例,可以去查看我的文章:
使用Windbg分析多線程臨界區死鎖問題分享https://blog.csdn.net/chenlycly/article/details/128532743
5、最后
? ? ? 上述內容均是從項目中遇到的實戰問題總結歸納出來的,很有實戰參考價值,希望能幫到大家。