實驗三說明文檔:
題目:
Take Assessment: Exercise 3: Debugging Malloc Lab
Debugging Malloc Lab: Detecting Memory-Related Errors
Introduction
The usual implementation of malloc and free are unforgiving to errors in their callers' code, including cases where the programmer overflows an array, forgets to free memory, or frees a memory block twice. This often does not affect the program immediately, waiting until the corrupted memory is used later (in the case of overwrites) or gradually accumulating allocated but unused blocks. Thus, debugging can be extremely difficult.
In this assignment, you will write a wrapper for the malloc package that will catch errors in the code that calls malloc and free. The skills you will have learned upon the completion of this exercise are pointer arithmetic and a greater understanding of the consequences of subtle memory mistakes.
原文:http://blog.csdn.net/qq_34861102/article/details/78575533
實驗目的:
- 本次實驗的目的在於通過自己編寫自定義的
malloc
和free
函數以達到可以定位問題所在的行號,方便調試的目的
- 本次實驗的目的在於通過自己編寫自定義的
實驗思路
首先是題目中提示要通過自定義
header
和footer
結構來記錄在內存分配和釋放中的相關信息以實現可以在問題出現的時候定位到問題的具體信息。示意圖如下:其次,實驗要求中要求實現
PrintAllocatedBlocks
和HeapCheck
這兩個函數,對於這兩個函數的功能的實現則需要自己定義相關的數據結構來實現。PrintAllocatedBlocks
函數要實現的是打印當前由malloc
函數所分配的塊的文件名和行號。HeapCheck
函數實現對當前已分配塊的錯誤信息檢查以及相關的輸出。可以看到,對於這兩個函數的實現都需要遍歷每一個塊中含有的header
和footer
結構來實現,基於此,這裏我想到的一個解決的方案是使用鏈表的結構來進行函數功能的實現。
實驗實現
header
結構和footer
結構的實現按照題目中的信息:
Information that you might want to store in this extra (header, footer) area include: • a "fence" immediately around the requested payload with a known value like 0xCCDEADCC, so that you can check if it has been changed when the block is freed. • the size of the block • a checksum for the header to ensure that it has not been corrupted (A checksum of a sequence of bits is calculated by counting the number of "1" bits in the stream. For example, the checksum for "1000100010001000" is 4. It is a simple error detection mechanism.) • the filename and line number of the MALLOC() call
可以寫出來
header
結構和footer
結構應該含有的參數:// the definition of header typedef struct { int checksum; char * filename; int linenumber; int size; // the size of payload int fence; // fence indicating whether writing past header } header; // the definition of footer typedef struct { int fence; // fence indicating whether writing past footer } footer;
鏈表結構的實現
使用鏈表結構的同時要考慮到關聯到
header
結構和footer
結構,因此這裏新定義一個結構體爲鏈表的元素,同時初始化一個節點爲鏈表的初始節點://to record the information for the extra function typedef struct node { header *h; struct node *next; } footer_node; static footer_node header_node = { NULL, NULL };
MyMalloc
函數的實現初始化
header
和footer
計算
header
的checksum
值,這裏通過上一次實驗中涉及到的相關位運算可以較爲簡便的計算checksum
。值得注意的是這裏的temp
是header
的地址,要以char *
的格式聲明。(對於其他格式如int *
會導致進行++
操作的時候導致實際地址變化大於 1),size
爲header
的長度。while(size--) { // for the char the num of bits is 8 for(int i = 0; i < 8; i++) { checksum += (*temp >> i) & 0x1; } temp ++; }
分配地址,這裏我分配地址的代碼爲:
void *temp = malloc(sizeof(header) + sizeof(footer_node) + size + sizeof(footer) + BUFFER + BUFFER);
在題目原有的基礎上增加了
BUFFER
結構和元素FOOTER_NODE
結構,原因在於:實際中,如果直接將鏈表的元素放在塊之後,對於部分測試會因爲相關的地址操作會修改鏈表的元素而導致程序的出現讀寫錯誤。(這裏可以從
http://danluu.com/malloc-tutorial/
中看到對於malloc
操作地址分配的實現,可以判斷得出上述結論)因此這裏元素前後增加一個BUFFER
使得測試代碼中的操作不會對記錄鏈表產生影響。示意圖如下:
向鏈表中添加元素
通過向之前提到的初始鏈表中添加鏈表元素完成信息的連續記錄,這裏是將上面地址示意圖的
FOOTER_NODE
加入到鏈表之中。FOOTER_NODE
結構中的h
元素爲當前塊中header
fn->h = h; fn->next = NULL; footer_node *ptmp = &header_node; while (ptmp->next){ ptmp = ptmp->next; } ptmp->next = fn;
依照地址示意圖尋找到
PAYLOAD
的最低地址返回即可。return (void *)((char *)temp + sizeof(header));
MyFree
函數的實現對鏈表中的元素進行判斷,如果鏈表中不含當前
FOOTER_NODE
,則表明當前塊已經被釋放過了,則應該出現錯誤 4 。if (-1 == deleteNode(h)){ error(4, filename, linenumber); return; }
通過當前塊
PAYLOAD
的值尋找到header
header *h = (header *)((char *)ptr - sizeof(header));
根據
header
包含的內容進行其餘錯誤的判斷://get the footer information if (msize < 0) errorfl(3, mfilename, mlinenumber, filename, linenumber); else ffence = ((footer *)((char *)ptr + msize))->fence; //check whether writing past header if (mfence != FENCENUM) { errorfl(1, mfilename, mlinenumber, filename, linenumber); } // check whether writing past footer if (ffence != FENCENUM) { errorfl(2, mfilename, mlinenumber, filename, linenumber); } //check whether header information corrupts h->checksum = 0; if (CalcheckSum((char *)h, sizeof(header)) != mchecksum) { errorfl(3, mfilename, mlinenumber, filename, linenumber); }
PrintAllocatedBlocks
和HeapCheck
函數的實現對於
PrintAllocatedBlocks
和HeapCheck
函數,直接對鏈表進行遍歷,其中PrintAllocatedBlocks
調用PRINTBLOCK
即可。HeapCheck
函數仿照MyFree
函數進行錯誤的判斷,其中將打印函數errorfl
替換成PRINTERROR
即可。
實驗結果