Debugging Malloc Lab: Detecting Memory-Related Errors解答


實驗三說明文檔

題目:

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


  • 實驗目的:

    • 本次實驗的目的在於通過自己編寫自定義的mallocfree函數以達到可以定位問題所在的行號,方便調試的目的
  • 實驗思路

    • 首先是題目中提示要通過自定義headerfooter結構來記錄在內存分配和釋放中的相關信息以實現可以在問題出現的時候定位到問題的具體信息。示意圖如下:

      這裏寫圖片描述

    • 其次,實驗要求中要求實現 PrintAllocatedBlocksHeapCheck 這兩個函數,對於這兩個函數的功能的實現則需要自己定義相關的數據結構來實現。

    • PrintAllocatedBlocks 函數要實現的是打印當前由 malloc 函數所分配的塊的文件名和行號HeapCheck函數實現對當前已分配塊的錯誤信息檢查以及相關的輸出。可以看到,對於這兩個函數的實現都需要遍歷每一個塊中含有的headerfooter結構來實現,基於此,這裏我想到的一個解決的方案是使用鏈表的結構來進行函數功能的實現。
  • 實驗實現

    • 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函數的實現

      • 初始化headerfooter

      • 計算 headerchecksum 值,這裏通過上一次實驗中涉及到的相關位運算可以較爲簡便的計算checksum 。值得注意的是這裏的tempheader的地址,要以char *的格式聲明。(對於其他格式如 int *會導致進行++操作的時候導致實際地址變化大於 1),sizeheader的長度。

        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);
         }
    • PrintAllocatedBlocksHeapCheck 函數的實現

      對於PrintAllocatedBlocksHeapCheck 函數,直接對鏈表進行遍歷,其中PrintAllocatedBlocks調用PRINTBLOCK即可

      HeapCheck 函數仿照MyFree函數進行錯誤的判斷,其中將打印函數errorfl替換成PRINTERROR即可。


  • 實驗結果

    這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章