檢查 Run Time Memory Error 的工具
目 前市面上有不少專門檢查run time memory error 的工具,例如Purify、Insure++、Bounds Checker 以及Valgrind 等等;其中,Purify、Insure++與Valgrind,是筆者曾經實際使用過的產品;然而,就這三項工具的功能完備性來說,筆者主觀地認為 Purify 是小勝幾籌;再者,就使用者介面的簡易明瞭性來說,筆者也是主觀地較偏好 Purify。因此,後續的章節內容與其範例程式之偵測與說明,都將以 Purify 這項工具為主軸。
Purify 的歷史
Purify 的產品雛形,是由一位名叫 Reed Hastings 的工程師所獨力研發而成。憑藉著Purify 的產品雛形,Reed Hastings 於1991 年在美國加州創立了 Pure Software 公司;創業後的四年之間,公司的年營業額每年皆呈倍數成長,亮麗的表現使得 Pure Software 公司獲得 Morgan Stanley 的青睞,並在 Morgan Stanley 的輔導之下,順利地於1995 年在 NASDAQ 掛牌上市。1997 年,Rational Software 公司以七億五千萬美金併購了 Pure Software 公司;而Rational Software 公司則於2003 年,被IBM 以二十億美金所併購。
Pure Software 的創辦人 ── Reed Hastings,在大學時期的主修科目是數學,並曾經得到系上的 Smyth Prize。他在1983 年大學畢業之後,隨即加入由美國總統 Kennedy 所創立的和平志工隊 (Peace Corps Volunteer),到非洲的 Swaziland 教授兩年的數學課程;之後,他回到校園攻讀 Computer Science,並於1988 年取得 Stanford University 電腦科學系的碩士學位。
Purify 產品目前已被更名為 PurifyPlus,而且也推出了 Linux 平台的版本;本書在範例程式當中所使用的 PurifyPlus,則是由 IBM 網站上所下載的 trial version。
備註:最近幾年很紅的線上租片公司 Netflix,就是由 Reed Hastings 所創辦的。
Uninitialized Memory Read (UMR)
所 謂「Uninitialized Memory Read」,是指某個變數尚未給定初始值之前(此時的變數內容值很可能是個亂數值),程式就去讀取它的內容值。除了少數的情況之外,UMR 通常不會直接造成 coredump 或 segmentation fault,但它有可能會造成不正確的運算結果,抑或間接地引發後續的 coredump 或 segmentation fault。
The Operation Beyond Array Bounds
不 論何種 data type 的 array,其 array element 的個數都是有限的。第一個 array element 的 starting address,是 array 的 lower bound;而最後一個 array element 的 ending address,則是 array 的 upper bound;因此,對 array 的任何運作,都必須侷限在 lower bound 與 upper bound 的範疇之內,才是合法有效的運作;如果存取的運作超出這個範疇,則程式很可能會因此發生 coredump 或 segmentation fault;因為存的運作(write operation),有可能破壞了其他資料結構的內容值;而取的運作(read operation),則是從錯誤的地方去讀取了某個值,而這個值很可能會造成後續的程式無法順利地運作。
Stack Bound Read/Write (SBR/SBW)
所謂「Stack Bound Read/Write」,是指程式對array object 的存取運作,超越了array object 的有效範疇,而且該 array object 的 storage,是從 stack frame 配置而來。
Array Bound Read/Write (ABR/ABW)
所 謂「Array Bound Read」或「Array Bound Write」,是指程式對 array object 的存取運作,超出其有效的範疇,而且 array object 的 storage,是從 data segment 或 heap area 配置而來。
Null Pointer Read/Write (NPR/NPW)
所謂「Null Pointer Read」或「Null Pointer Write」,是指某個 pointer 指向 NULL,亦即該 pointer 沒有指向任何的 object,但程式卻未能檢測其值是否為 NULL,即逕行透過它來執行存取的運作。
Freeing Mismatched Memory (FMM)
所 謂「Freeing Mismatched Memory」,是指「配置記憶體的函式」與「釋放記憶體的函式」不一致;例如,程式呼叫 calloc 函式,要到了一塊記憶體,使用完之後卻呼叫 delete 去歸還記憶體。當程式帶有 FMM 的錯誤時,是不太容易察覺的;因為 compiler 無法找出 FMM 的錯誤,而且 FMM 的錯誤通常不會直接造成 coredump;此時,PurifyPlus 即可上場救援,透過它來找出 FMM 的錯誤。對於可以取得或釋放記憶體資源的相關函式,PurifyPlus 能夠檢測出兩者是否一致的搭配組合為:
"new" versus "delete"
"new []" versus "delete []"
"malloc" versus "free"
"calloc" versus "free"
"realloc" versus "free"
"XtMalloc" versus "XtFree" (X Windows APIs)
"new []" versus "delete []"
"malloc" versus "free"
"calloc" versus "free"
"realloc" versus "free"
"XtMalloc" versus "XtFree" (X Windows APIs)
Free Memory Read/Write (FMR/FMW)
所 謂「Free Memory Read」或「Free Memory Write」,是指之前取得的記憶體資源,已經歸還給系統,但因程式的疏失或邏輯上的錯誤,使得某個 pointer 還依然指向該記憶體資源,並陸續地透過該 pointer 對其執行讀取或寫入的運作。
Dangling Pointer 與FMR/FMW
所 謂「Dangling Pointer」,是指某個 pointer 在一開始時,是指向一個活生生的 object,之後這個 object 可能因為衰老而壽終正寢,抑或遭逢意外而不幸身亡,但該 pointer 卻渾然不知,還是繼續對這個已經不存在的 object 執行讀取或寫入的運作,並因此引發FMR 或FMW 的錯誤。
之前所看到的 fmr_fmw.c 程式,由於主要目的是在做示範,故其所犯之錯誤看來是十分愚蠢;但在實際的軟體研發過程中,由dangling pointer 所引發的 FMR 與 FMW,是很有可能發生的。舉例來說,現在一般的軟體架構,大都採取 layered design 的設計;也就是說,有些 software components 是隸屬於底層,假設叫做 kernel layer components,簡稱 kernel layer;而有些則是隸屬於上層,假設叫做 application layer components,簡稱 application layer;當 kernel layer 遇到某個 CreateMyObj event,並因此配置了許多data type 叫做 MyObj 的 objects;之後、application layer 遇到 GetMyObjHdl event,因此application layer 向kernel layer 要求,希望能取得由 kernel layer 所配置的所有資料型態為 MyObj 的object 之address (即handle),並透過 array of pointer 或 pointer pool,在 application layer 記下這些 handles;當 kernel layer 遇到DestroyMyObj event,並因此將所有資料型態為 MyObj 的 objects 歸還給作業系統,但卻在歸還之後,忘了通知 application layer,它已經將所有的 objects 都釋放掉了;此時,application layer 認為它所保持的handles,依然指向實質存在的 objects,但因實際上都已經不存在,所以這些 handles 也就變成了dangling pointers,因此後續的讀或寫之運作,將會引發 FMR 或 FMW 的錯誤。
這種由 layered design 所引起的 dangling pointer,可以透過 callback mechanism 來解決;亦即 application layer 必須向 kernel layer 註冊 DestroyMyObj event 以及 callback function;而 kernel layer 在遇到 DestroyMyObj event 時,一定要透過 callback function 去通知 application layer,讓 application layer 有機會對 array of pointer 或 pointer pool 的內容,執行適當的清除運作。
Free Unallocated Memory (FUM)
所謂「Free Unallocated Memory」,是指程式所指定要歸還的記憶體資源之位址,之前已經被歸還過了,抑或從來就不曾被配置過;因此,該次的歸還運作是多此一舉。
Memory Leak (MLK)
所謂「Memory Leak」,是指程式沒有將記憶體資源做妥善的利用,以致於造成閒置或浪費的情形;而這種情形就如同沒有將水龍頭關緊,使得涓滴的自來水不斷地被浪費掉,因此將之稱為 memory leak。
筆 者將memory leak 的來源分為兩種:一、lost memory block;二、undeallocated memory block。其中,lost memory block 是指程式把某塊記憶體資源給搞丟了;而 undeallocated memory block 則是指程式已經不再使用某塊記憶體資源,但仍然把持著該記憶體資源的使用權利 (有可能是「忘記」或是「不願意」釋放記憶體資源)。
Lost Memory Block
所 謂「Lost Memory Block」,是指原本指向某 2 MB memory block 的 pointer,被程式更動後指向另外一塊 4 MB memory block,導致沒有任何的 pointer 指向之前的 2 MB memory block;此時,這塊 2 MB memory block 就成了被搞丟、被遺忘的記憶體資源。
若以日常生活的例子來比喻 lost memory block,它就像是自己買了一把螺絲起子,但在使用過後,由於沒有保持物歸原處的習慣,用完就隨手丟、隨處放,導致最後忘了螺絲起子放在哪裡,而且怎麼 找就是找不到;通常,在遍尋不可得的情況下,只好到修繕材料店再買一把螺絲起子;但如此一來,就造成了金錢與資源的浪費。
Undeallocated Memory Block
所謂「Undeallocated Memory Block」,是指程式已經不再使用某塊記憶體資源,但仍然把持著該記憶體資源的使用權利,使得這塊記憶體資源持續地處於閒置的狀態,直到 process 結束。
造成 undeallocated memory block 的原因有兩種:一、忘記歸還記憶體資源;二、故意佔著不放。通常,真正的原因是「忘記」,很少有程式故意佔著記憶體資源而不使用。
No comments:
Post a Comment