張 玲,李 艷,胡 術(shù),李 璞,潘 倩
(1.四川大學(xué) 計算機基礎(chǔ)教學(xué)實驗中心,四川 成都 610065;2.四川大學(xué) 計算機學(xué)院,四川 成都 610065;3.四川大學(xué) 視覺合成圖形圖像技術(shù)國防重點學(xué)科實驗室,四川 成都 610065)
一種基于Qt的系統(tǒng)內(nèi)存泄漏檢測方法
張 玲1,李 艷2,3,胡 術(shù)2,3,李 璞2,3,潘 倩2,3
(1.四川大學(xué) 計算機基礎(chǔ)教學(xué)實驗中心,四川 成都 610065;2.四川大學(xué) 計算機學(xué)院,四川 成都 610065;3.四川大學(xué) 視覺合成圖形圖像技術(shù)國防重點學(xué)科實驗室,四川 成都 610065)
在軟件開發(fā)中,用戶界面程序提高了軟件系統(tǒng)易操作性、用戶體驗度等非功能性需求。長時間、復(fù)雜流程的大型軟件系統(tǒng)對人機界面的穩(wěn)定性則提出了較高要求,不能出現(xiàn)內(nèi)存泄漏、不能中途異常退出。針對使用圖形用戶界面應(yīng)用程序框架Qt開發(fā)的用戶界面程序的內(nèi)存泄漏問題,提出了一種基于Qt的人機界面程序的源碼靜態(tài)內(nèi)存檢測方法。該方法針對Qt控件對象是否存在父控件的兩種內(nèi)存泄漏情況,識別所需檢查的目標對象是否為Qt控件類對象,是否存在內(nèi)存泄漏。該方法提供的Qt控件對象內(nèi)存檢測能力可準確識別目標對象是否存在內(nèi)存泄漏、手工刪除錯誤等問題,便于開發(fā)人員及時檢查錯誤,修正缺陷,減少程序運行中的內(nèi)存泄漏問題,以滿足大型系統(tǒng)的軟件質(zhì)量需求。
Qt;內(nèi)存泄漏;遍歷匹配;泄漏檢測
當(dāng)下,大型軟件系統(tǒng)開發(fā)人機界面所需的開源圖形用戶界面程序框架選擇具有多樣性,如提供了對Windows本地組件的訪問方式,兼容了Linux及其他Mono平臺的微軟.NET開發(fā)框架圖形用戶界面Windows.Froms;用于形狀、文檔、圖像、視頻、動畫、三維及用于放置控件、內(nèi)容的控件模型框架,支持流動文字、3D視覺效果,基于Vista的用戶界面框架WPF;用于提供原生Mac OSX應(yīng)用程序開發(fā)的類庫MonoMac等。文中對面向?qū)ο?、易于使用、允許真正的組件編程并具有優(yōu)良的跨平臺特性的Qt[1]框架開發(fā)的人機界面進行闡述。
Qt是一個跨平臺的基于C++編程語言的圖形用戶界面應(yīng)用程序框架,該框架提供給應(yīng)用程序開發(fā)者建立藝術(shù)級的圖形用戶界面所需的按鈕、滾動條、菜單及其他對象等功能[2]。在Qt中,Qobject類作為本庫大多數(shù)類的基類,Qobject對象總是以樹狀結(jié)構(gòu)組織自己。在創(chuàng)建一個Qobject對象時,可指定其父對象(也被稱為父控件),新創(chuàng)建的對象將被加入到其父對象的子對象(也被稱為子控件)列表中。當(dāng)父對象被析構(gòu)時,該父對象對應(yīng)列表中的所有子對象也將被析構(gòu),同理可知,當(dāng)某個Qobject對象被析構(gòu)時,它會將自己從所屬父對象的列表中刪除,以避免父對象被析構(gòu)時,再次析構(gòu)自己。由于Qobject對象具備上述析構(gòu)功能,當(dāng)使用new操作符在堆中創(chuàng)建Qobject對象時,不需要使用delete操作符析構(gòu)它們。
綜上所述,由于Qt的特殊性,開發(fā)人員在使用Qt開發(fā)軟件系統(tǒng)的人機界面時,極易寫出內(nèi)存處理不適當(dāng)?shù)某绦?,最終導(dǎo)致內(nèi)存泄露。文中將詳細闡述在系統(tǒng)開發(fā)中進行Qt代碼靜態(tài)內(nèi)存檢查的方法。
由于動態(tài)分配的內(nèi)存沒有及時、正確地釋放而引起的內(nèi)存泄漏[3],直接影響了程序的可用性和穩(wěn)定性。內(nèi)存泄漏分為常發(fā)性內(nèi)存泄漏、偶發(fā)性內(nèi)存泄漏、一次性內(nèi)存泄漏及隱式內(nèi)存泄漏等四類情況[4]。在高級語言編程中,內(nèi)存泄漏通常指進程運行時沒有調(diào)用delete操作符將動態(tài)分配的內(nèi)存釋放,但指向該內(nèi)存的指針卻被指向其他內(nèi)存或被銷毀,導(dǎo)致該內(nèi)存丟失,系統(tǒng)失去了對該內(nèi)存的控制權(quán),從而造成了內(nèi)存浪費[5]。目前,為解決內(nèi)存泄漏問題,一些高級編程語言通過垃圾收集機制[6]跟蹤內(nèi)存的使用情況,并按照固定時間間隔周期性自動回收不再使用的動態(tài)分配內(nèi)存,但目前C++并不支持該機制。此外,標準庫中相關(guān)函數(shù)提供了內(nèi)存泄漏識別、檢測功能,如使用調(diào)試堆棧函數(shù)和在需要檢測內(nèi)存泄漏的位置添加輸入調(diào)試信息函數(shù)用于輸出內(nèi)存泄漏的地址及其內(nèi)容,但該C標準函數(shù)輸出的內(nèi)存泄露信息不能產(chǎn)生具體的堆棧調(diào)用結(jié)果,從而無法得知內(nèi)存泄漏的具體對象。Insure++[7]是運行時檢測工具,能檢測C/C++中編程和運行時錯誤,檢驗靜態(tài)、堆棧以及動態(tài)分配內(nèi)存操作的有效性,但不支持第三方庫函數(shù)的內(nèi)存檢測[8]。文中提出一種基于Qt開發(fā)的軟件系統(tǒng)用戶界面程序的內(nèi)存泄漏檢測方法。針對Qt中兩種專屬內(nèi)存泄漏情況即Qt控件對象是否存在父控件提供了靜態(tài)檢測能力,能夠通過在相關(guān)配置文件中添加新的Qt控件對象識別所需檢測的具體對象,準確識別該對象是否存在內(nèi)存泄漏,能夠有效地減少程序運行中的內(nèi)存泄漏問題。
Qt內(nèi)存泄漏的原因呈現(xiàn)多變性,文中主要實現(xiàn)了Qt控件對象是否存在父控件兩種情況的內(nèi)存檢測功能。Qt控件對象在沒有父控件[9]的條件下,若程序在堆中通過new操作符創(chuàng)建了一個對象,則必須通過delete操作符析構(gòu)該對象完成刪除操作,否則程序會出現(xiàn)內(nèi)存泄露問題;相反地,Qt控件對象在具有父控件的條件下,則無需開發(fā)人員通過delete操作符手動刪除該Qt控件對象,因為該控件對象的父控件在析構(gòu)時,會自動析構(gòu)自己對應(yīng)子控件列表中的控件,為Qt提供了自動內(nèi)存機制,在此情況下,開發(fā)人員若誤寫刪除代碼,可能造成冗余刪除;除此之外,Qt中還有其他屬于C++編程語言的內(nèi)存泄露和冗余析構(gòu)的情況[10]。文中提出的方法目前尚未涉及這些情況的檢測,該方法實現(xiàn)了Qt控件對象是否有父控件的內(nèi)存泄漏檢測功能?;赒t的系統(tǒng)內(nèi)存檢測方法由掃描工程文件中的所有.cpp文件和.h文件,解析各.cpp文件及.h文件中的new、delete操作符,存儲創(chuàng)建和刪除對象的信息,整合Qt控件對象創(chuàng)建、刪除的統(tǒng)一完整信息,檢測信息輸出結(jié)果等5個模塊組成。Qt內(nèi)存靜態(tài)檢測框架如圖1所示。
圖1 Qt內(nèi)存靜態(tài)檢測框架
MS Windows系統(tǒng)中采用_findfirst()、_findnext()函數(shù)遍歷需要檢測的工程文件下的所有文件,將掃描得到的.h文件和.cpp文件存放在同一個順序容器vector中[11],文件掃描流程如圖2所示。
圖2 掃描工程所有.cpp和.h文件流程
遍歷順序容器vector,依次對各.cpp文件、.h文件執(zhí)行g(shù)etLine()函數(shù)[12],獲取當(dāng)前文件中的每一行程序,識別程序中由new、delete操作符執(zhí)行的對象,并判斷由new創(chuàng)建的對象是否為Qt控件類。delete操作符直接刪除對象,不能得到對象類名,從而不能判斷刪除的對象是否是Qt控件類對象,因此不需判斷由delete操作符刪除的對象是否是Qt控件類,只需存儲由delete操作符刪除的所有的對象信息。解析工程中的各文件流程如圖3所示。
3.2.1 識別注釋、非注釋
讀取文件每行程序,首先判斷該行程序是否為注釋,一般情況下,編程語言的注釋由行注釋(“//”)和段注釋(“/*XXXX*/”)組成。注釋與代碼需區(qū)別對待,通過申明一個bool類型的變量result來區(qū)別被檢測的代碼是否為注釋,變量result由解析代碼的函數(shù)返回值更新。
圖3 解析文件流程
若被檢測代碼為行注釋,則解析進程直接跳過該行即可,并將變量result設(shè)置為commentEnd(表示注釋結(jié)束);若為段注釋,且在一行代碼沒有同時出現(xiàn)“/*”和“*/”,則result值為commentBegin(表示注釋開始),反之,若在一行代碼中同時存在“/*”和“*/”,則result值為commentEnd(表示注釋結(jié)束)。
進程在解析每行代碼時,需根據(jù)result值選擇不同的解析函數(shù)parseComment()(解析注釋的函數(shù)),parseCode()(解析代碼的函數(shù))進行new、delete操作符的檢測操作。
3.2.2 識別Qt控件對象
在解析文件的各行代碼中,當(dāng)代碼出現(xiàn)new操作符創(chuàng)建對象時,解析語句獲得創(chuàng)建對象的類型并根據(jù)該對象的類名來判斷是否為Qt控件類[13]。文中識別Qt控件對象共分為兩步:識別程序中創(chuàng)建或析構(gòu)對象的語句;判斷識別出的對象是否為Qt類。delete對象語句的識別處理方式與new對象語句相同,不同之處在于因delete語句無法得到對象所屬類名,進而無法判斷被刪對象是不是Qt控件類。下面將以new操作符創(chuàng)建的對象為示例來闡述識別Qt控件對象的具體過程。
(1)識別程序中創(chuàng)建對象的語句。
一般情況下,程序創(chuàng)建Qt控件對象的結(jié)構(gòu)如下所示:
QPushButon m_button=new QPushButton(Parent);
此例代碼最具標志性的詞分別是new操作符和“=”,而創(chuàng)建對象的new操作符應(yīng)保證是獨立的,需排除“***new***”情況的出現(xiàn)。
為識別創(chuàng)建的對象,需從程序中獲取對象的信息,包括對象名(m_button)、類名(QPushButton)以及父對象(parent)。而對象信息的標識符則是通過“=”、“new”以及“()”等來獲取各標識符的begin(第一個字符在整行string中的索引)和end(最后一個字符在整行string中的索引),然后通過begin和end獲得對象信息。C++中,string類提供了可直接使用的接口用于查找目標對象的信息內(nèi)容,substr(begin,len)用于截取子串,find(“arg”)用于查找索引值,配合使用substr()、find()函數(shù)即可完成對象名、類名及其父對象的查找工作。
(2)判斷步驟(1)對象是否為Qt控件對象。
因考慮到識別所有的Qt控件對象[14]是一項繁雜的工作,目前僅是添加一些常用的Qt控件對象以供識別。當(dāng)然,程序并沒有固定的Qt控件對象,若出現(xiàn)新的Qt控件對象需要識別,在配置文件InheritTree.ini中手動添加新Qt對象即可進行識別操作。Qt控件對象所屬類均具有繼承關(guān)系,且對象的最終父對象都為QWidget類。針對該繼承樹關(guān)系,文中通過讀取配置文件InheritTree.ini內(nèi)容建立了一個樹結(jié)構(gòu),如圖4所示。
圖4 讀inheritTree.ini配置文件生成Qt控件類的繼承樹
配置文件InheritTree.ini文件結(jié)構(gòu)如下例所示:
[樹的高度]
depthNum=n
[高度0]
NodeNum=1 //當(dāng)前高度下節(jié)點數(shù)目
QWidget=NULL//等號左邊是空間類,右邊是起父類
[高度1]
...
[高度n-1]
...
樹結(jié)構(gòu)提供了接口findChild(string key,POINTER &p),指針p指向樹中關(guān)鍵字為key的節(jié)點,如果樹中不存在關(guān)鍵字為key的節(jié)點,則p的值為NULL。該樹結(jié)構(gòu)可判斷樹中是否存在關(guān)鍵字為key的節(jié)點,因此,把待判斷對象的類名作為實參傳給key,即可判斷此類是否為Qt控件類,也就能判斷被檢測對象是不是Qt控件對象。
本步驟將解析得到的通過new操作符創(chuàng)建的Qt控件對象信息和通過delete操作符刪除的Qt控件對象信息分別存儲在結(jié)構(gòu)體中,而兩結(jié)構(gòu)體對象分別存儲在newOBJMap、deleteOBJMap中。
(1)操作符new語句的信息結(jié)構(gòu)體newOBJ包含創(chuàng)建的對象名、該對象名所屬的父對象名以及該對象的創(chuàng)建位置,其中對象創(chuàng)建位置包括文件名、代碼行數(shù):
Struct newOBJ
{
String OBJName;//對象名
String Parent;//父對象名
Position m_position;//記錄對象創(chuàng)建的位置,包括所屬文件,對應(yīng)代碼行
};
(2)操作符delete語句的信息結(jié)構(gòu)體deleteOBJ包含刪除的對象名以及記錄對象刪除的位置,該對象的位置包括所屬文件名、代碼行數(shù):
Struct deleteOBJ
{
String OBJName;//對象名
Positionm_position;//記錄對象刪除的位置,包括所屬文件,對應(yīng)代碼行
};
本步驟將存儲在newOBJMap、deleteOBJMap中的結(jié)構(gòu)體信息整合到QTObjectMap,整合流程如圖5所示。
通過循環(huán)迭代newOBJMap中的各對象信息結(jié)構(gòu)體,從結(jié)構(gòu)體中獲取各Qt控件類對象,通過遍歷deleteOBJMap中各結(jié)構(gòu)體查找與該Qt控件對象名相符的對象,若具有相同對象名,則將newOBJMap、deleteOBJMap中屬于該對象的結(jié)構(gòu)體信息整合到QTOBJ結(jié)構(gòu)體中,并把QTOBJ結(jié)構(gòu)體對象插入到QTObjectMap對象中。QTOBJ結(jié)構(gòu)體包含Qt控件對象名、該對象的父對象名、記錄該對象創(chuàng)建及刪除的位置、該對象是否通過delete操作符手動刪除。
圖5 合并newOBJMap和deleteOBJMap的流程
Struct QTOBJ
{
String OBJName;//對象名
String Parent;//父對象名
Position m_position;//記錄對象創(chuàng)建的位置,包括所屬文件,對應(yīng)代碼行
Position m_position;//記錄對象刪除的位置,包括所屬文件,對應(yīng)代碼行
Bool isDelete;//記錄是否在程序中手動刪除
};
根據(jù)QTObjectMap中的信息輸出檢測結(jié)果。在QTObjectMap中的各QTOBJ結(jié)構(gòu)體對象,若結(jié)構(gòu)體有父控件,則無需通過delete操作符手動刪除該Qt對象,否則冗余delete;若結(jié)構(gòu)體沒有父控件,則必須通過delete操作符手動刪除,否則內(nèi)存泄漏。最后將結(jié)果輸出到DOS界面和日志文件中。其中檢測過程的偽代碼如下:
If(QTOBJ.parent!=“ ”)//此QTOBJ控件對象擁有父控件
{
If(QTOBJ.isdelete==true)//程序中出現(xiàn)了手動刪除
檢測出情況2:手動刪除錯誤
}
If(QTOBJ.parent==“ ”)//此QTOBJ控件對象沒有父控件
{
If(QTOBJ.isdelete==false)//程序中沒有出現(xiàn)手動刪除
檢測出情況1:疑似內(nèi)存泄露
}
使用Qt代碼靜態(tài)檢查[15]的方法開發(fā)一個可執(zhí)行程序,該程序運行時需將被檢查的代碼所在的目錄作為運行參數(shù),目錄可包含子目錄。該執(zhí)行程序通過打印輸出信息方便開發(fā)人員修改代碼,對基于圖形用戶界面程序框架Qt開發(fā)的航管訓(xùn)練系統(tǒng)進行描述。以檢查“E://航管訓(xùn)練”工程文件目錄下的所有文件為例,輸出示例:
(1)疑似內(nèi)存泄露:objectName(控件對象名)E://航管訓(xùn)練//a.cpp(創(chuàng)建對象文件全目錄)600(行號);
(2)疑似手工刪除錯誤:objectName(控件名)parentName(父控件名)E://航管訓(xùn)練//a.cpp(創(chuàng)建對象文件全目錄)600(行號)E://航管訓(xùn)練//a.cpp(刪除對象文件全目錄)(行號)。
內(nèi)存泄漏是軟件開發(fā)中的常見錯誤,針對在開發(fā)軟件系統(tǒng)人機界面出現(xiàn)的內(nèi)存泄漏情況,提出了一種基于Qt開發(fā)的軟件系統(tǒng)程序內(nèi)存檢測方法。該方法可以有效檢測Qt常見內(nèi)存泄漏、準確定位內(nèi)存泄漏位置并打印輸出該信息,便于開發(fā)人員及時修正錯誤。Qt內(nèi)存泄漏的原因多種多樣,該方法主要實現(xiàn)了Qt控件對象是否存在父控件兩種情況的內(nèi)存檢測功能,并沒有涉及到屬于C++語言使用中的其他內(nèi)存泄漏和冗余delete情況的檢測。
[1] BLANCHETTE J,SUMMERFIELD M.C++ GUI programming with Qt 4[M].2nd ed.[s.l.]:[s.n.],2015.
[2] 李全虎.交互界面開發(fā)工具-Qt[J].中國科技信息,2005(5):33.
[3] III W P A,LEVINE F E,REYNOLDS W R,et al.Method and system for shadow heap memory leak detection and other heap analysis in an object-oriented environment during real-time trace processing:US,US6658652[P].2003.
[4] 柳 青,楊英豪,孫永超.C++內(nèi)存檢測的研究[J].電子工業(yè)專用設(shè)備,2014,43(12):35-38.
[5] 道夫曼,紐伯格.C++內(nèi)存管理[M].北京:學(xué)苑出版社,1994.
[6] 謝之易.一種新的適用于面向?qū)ο蟪绦蛟O(shè)計語言的保守式垃圾收集機制[J].計算機應(yīng)用與軟件,2008,25(1):96-99.
[7] NANCE J.Product review:insure++[J].Linux Journal,1998,1998(51):14.
[8] 吳 民,涂奉生.內(nèi)存泄漏的動態(tài)跟蹤分析[J].計算機工程與應(yīng)用,2005,41(14):18-20.
[9] 王衛(wèi)東,屈 洋.可視化圖形界面中有關(guān)控件的屬性類的研究[J].微機發(fā)展(現(xiàn)更名:計算機技術(shù)與發(fā)展),2005,15(12):27-28.
[10] XU G.Distinguished paper precise memory leak detection for java software using container profiling*[J].ACM Transactions on Software Engineering & Methodology,2008,22(3):151-160.
[11] 余 鋒,祝曉鷹.用ActiveX控件實現(xiàn)目錄遍歷[J].電腦編程技巧與維護,2001(4):43-46.
[12] LIPPMAN S B,LAJOIE J,MOO B E.C++ Primer中文版[M].北京:電子工業(yè)出版社,2013.
[13] WILLIS T,NEWSOME B.Visual Basic 2010入門經(jīng)典[M].第6版.北京:清華大學(xué)出版社,2011.
[14] 李 彬.Linux Qt GUI開發(fā)詳解[M].北京:北京航空航天大學(xué)出版社,2013.
[15] DATHATHRI R,REDDY C,RAMASHEKAR T,et al.Dynamic memory access monitoring based on tagged memory[C]//International conference on parallel architectures and compilation techniques.[s.l.]:IEEE,2013:409-410.
AMemoryLeakageDetectionMethodforSoftwareSystemBasedonQt
ZHANG Ling1,LI Yan2,3,HU Shu2,3,LI Pu2,3,PAN Qian2,3
(1.Computer Teaching Experiment Center,Sichuan University,Chengdu 610065 China;2.Department of Computer,Sichuan University,Chengdu 610065,China;3.National Key Laboratory of Fundamental Science on Synthetic Vision,Sichuan University,Chengdu 610065,China)
In software development,the user interface program improves the non-functional requirements of software system,such as easy operation,user experience and so on.The large-scale training system of long-term and complex process gives the higher requirements to the stability of human-computer interface,no memory leak,no halfway abnormal exit.Aiming at the memory leakage of the user interface program developed by graphical user interface application framework Qt,a source static memory detection method of Qt-based human-computer interface program is presented.There are two types of memory leaks for the Qt control object for the parent control.The method identifies whether the target object to be checked is a Qt control class object,and whether there is a memory leak.The memory detection of Qt control object provided by this method can accurately identify whether the target object has a memory leak and manually remove the faults,which makes it easy for the developers to check the errors and correct the defects in time,and to reduce the program running in the memory leakage,meeting the functional requirements of large-scale aviation training system.
Qt;memory leakage;traversal matching;leak detection
TP301
A
1673-629X(2017)12-0119-05
10.3969/j.issn.1673-629X.2017.12.026
2017-01-04
2017-05-03 < class="emphasis_bold">網(wǎng)絡(luò)出版時間
時間:2017-09-27
中國民航創(chuàng)新基金項目(MHRD20150228)
張 玲(1964-),女,實驗師,研究方向為計算機應(yīng)用、仿真與網(wǎng)絡(luò)。
http://kns.cnki.net/kcms/detail/61.1450.TP.20170927.0958.050.html