2010-05-14 6 views
11

यह कल से Critique my heap debugger का अनुवर्ती है। जैसा कि बिटसी द्वारा सुझाया गया है, अब मैं एक अलग हस्तलिखित हैशटेबल में आवंटित ब्लॉक के बारे में मेटाडेटा रखता हूं। नष्ट करने के लिए पारित कर दिया (अधिक वर्बोज़ डिबगिंग आउटपुट के साथ अब)क्रिटिक मेरे गैर-घुसपैठ ढेर डीबगर

  1. मेमोरी लीक
  2. अवैध संकेत (जो भी डबल हटाता का ख्याल रखता है)
  3. :

    ढेर डिबगर अब निम्न त्रुटियों के प्रकार का पता लगाता है हटाएँ (बनाम गैर सरणी सरणी) का

  4. गलत रूप
  5. बफर overflows
  6. बफर underflows

चर्चा करने के लिए स्वतंत्र महसूस करें और अग्रिम धन्यवाद!

#include <cstdio> 
#include <cstdlib> 
#include <cstring> 
#include <new> 

namespace 
{ 
    // I don't want to #include <algorithm> for a single function template :) 
    template <typename T> 
    void my_swap(T& x, T& y) 
    { 
     T z(x); 
     x = y; 
     y = z; 
    } 

    typedef unsigned char byte; 

    const byte CANARY[] = {0x5A, 0xFE, 0x6A, 0x8D, 
          0x5A, 0xFE, 0x6A, 0x8D, 
          0x5A, 0xFE, 0x6A, 0x8D, 
          0x5A, 0xFE, 0x6A, 0x8D}; 

    bool canary_dead(const byte* cage) 
    { 
     bool dead = memcmp(cage, CANARY, sizeof CANARY); 
     if (dead) 
     { 
      for (size_t i = 0; i < sizeof CANARY; ++i) 
      { 
       byte b = cage[i]; 
       printf(b == CANARY[i] ? "__ " : "%2X ", b); 
      } 
      putchar('\n'); 
     } 
     return dead; 
    } 

    enum kind_of_memory {AVAILABLE, TOMBSTONE, NON_ARRAY_MEMORY, ARRAY_MEMORY}; 

    const char* kind_string[] = {0, 0, "non-array memory", " array memory"}; 

    struct metadata 
    { 
     byte* address; 
     size_t size; 
     kind_of_memory kind; 

     bool in_use() const 
     { 
      return kind & 2; 
     } 

     void print() const 
     { 
      printf("%s at %p (%d bytes)\n", kind_string[kind], address, size); 
     } 

     bool must_keep_searching_for(void* address) 
     { 
      return kind == TOMBSTONE || (in_use() && address != this->address); 
     } 

     bool canaries_alive() const 
     { 
      bool alive = true; 
      if (canary_dead(address - sizeof CANARY)) 
      { 
       printf("ERROR: buffer underflow at %p\n", address); 
       alive = false; 
      } 
      if (canary_dead(address + size)) 
      { 
       printf("ERROR:  buffer overflow at %p\n", address); 
       alive = false; 
      } 
      return alive; 
     } 
    }; 

    const size_t MINIMUM_CAPACITY = 11; 

    class hashtable 
    { 
     metadata* data; 
     size_t used; 
     size_t capacity; 
     size_t tombstones; 

    public: 

     size_t size() const 
     { 
      return used - tombstones; 
     } 

     void print() const 
     { 
      for (size_t i = 0; i < capacity; ++i) 
      { 
       if (data[i].in_use()) 
       { 
        printf(":(leaked "); 
        data[i].print(); 
       } 
      } 
     } 

     hashtable() 
     { 
      used = 0; 
      capacity = MINIMUM_CAPACITY; 
      data = static_cast<metadata*>(calloc(capacity, sizeof(metadata))); 
      tombstones = 0; 
     } 

     ~hashtable() 
     { 
      free(data); 
     } 

     hashtable(const hashtable& that) 
     { 
      used = 0; 
      capacity = 3 * that.size() | 1; 
      if (capacity < MINIMUM_CAPACITY) capacity = MINIMUM_CAPACITY; 
      data = static_cast<metadata*>(calloc(capacity, sizeof(metadata))); 
      tombstones = 0; 

      for (size_t i = 0; i < that.capacity; ++i) 
      { 
       if (that.data[i].in_use()) 
       { 
        insert_unsafe(that.data[i]); 
       } 
      } 
     } 

     hashtable& operator=(hashtable copy) 
     { 
      swap(copy); 
      return *this; 
     } 

     void swap(hashtable& that) 
     { 
      my_swap(data, that.data); 
      my_swap(used, that.used); 
      my_swap(capacity, that.capacity); 
      my_swap(tombstones, that.tombstones); 
     } 

     void insert_unsafe(const metadata& x) 
     { 
      *find(x.address) = x; 
      ++used; 
     } 

     void insert(const metadata& x) 
     { 
      if (2 * used >= capacity) 
      { 
       hashtable copy(*this); 
       swap(copy); 
      } 
      insert_unsafe(x); 
     } 

     metadata* find(void* address) 
     { 
      size_t index = reinterpret_cast<size_t>(address) % capacity; 
      while (data[index].must_keep_searching_for(address)) 
      { 
       ++index; 
       if (index == capacity) index = 0; 
      } 
      return &data[index]; 
     } 

     void erase(metadata* it) 
     { 
      it->kind = TOMBSTONE; 
      ++tombstones; 
     } 
    } the_hashset; 

    struct heap_debugger 
    { 
     heap_debugger() 
     { 
      puts("heap debugger started"); 
     } 

     ~heap_debugger() 
     { 
      the_hashset.print(); 
      puts("heap debugger shutting down"); 
     } 
    } the_heap_debugger; 

    void* allocate(size_t size, kind_of_memory kind) throw (std::bad_alloc) 
    { 
     byte* raw = static_cast<byte*>(malloc(size + 2 * sizeof CANARY)); 
     if (raw == 0) throw std::bad_alloc(); 

     memcpy(raw, CANARY, sizeof CANARY); 
     byte* payload = raw + sizeof CANARY; 
     memcpy(payload + size, CANARY, sizeof CANARY); 

     metadata md = {payload, size, kind}; 
     the_hashset.insert(md); 
     printf("allocated "); 
     md.print(); 
     return payload; 
    } 

    void release(void* payload, kind_of_memory kind) throw() 
    { 
     if (payload == 0) return; 

     metadata* p = the_hashset.find(payload); 

     if (!p->in_use()) 
     { 
      printf("ERROR: no dynamic memory at %p\n", payload); 
     } 
     else if (p->kind != kind) 
     { 
      printf("ERROR:wrong form of delete at %p\n", payload); 
     } 
     else if (p->canaries_alive()) 
     { 
      printf("releasing "); 
      p->print(); 
      free(static_cast<byte*>(payload) - sizeof CANARY); 
      the_hashset.erase(p); 
     } 
    } 
} 

void* operator new(size_t size) throw (std::bad_alloc) 
{ 
    return allocate(size, NON_ARRAY_MEMORY); 
} 

void* operator new[](size_t size) throw (std::bad_alloc) 
{ 
    return allocate(size, ARRAY_MEMORY); 
} 

void operator delete(void* payload) throw() 
{ 
    release(payload, NON_ARRAY_MEMORY); 
} 

void operator delete[](void* payload) throw() 
{ 
    release(payload, ARRAY_MEMORY); 
} 

int main() 
{ 
    int* p = new int[1]; 
    delete p; // wrong form of delete 
    delete[] p; // ok 
    delete p; // no dynamic memory (double delete) 

    p = new int[1]; 
    p[-1] = 0xcafebabe; 
    p[+1] = 0x12345678; 
    delete[] p; // underflow and overflow prevent release 
       // p is not released, hence leak 
} 
+0

क्या आपने अभी इस विषय के कल के संस्करण को संपादित नहीं किया है, बल्कि एक ही विषय पर एक और प्रश्न शुरू करने के बजाय? –

+1

@ पॉल आर: यह एक पूरी तरह से अलग है कि यह काम कर सकता है। – UncleBens

+0

अपवाद विनिर्देश का उपयोग न करें, अगर आप लोगों को इसके बारे में जानना चाहते हैं तो बस इसे टिप्पणी में रखें :) –

उत्तर

4

बहुत अच्छा, वास्तव में। आपके कैनरी वास्तव में ओवरफ्लो/अंडरफ्लो के कुछ वास्तविक मामलों को प्रकट कर सकते हैं (हालांकि उनमें से सभी मैथ्यूयू ने इंगित नहीं किया)।

और क्या। आप बहु-थ्रेडेड एप्लिकेशन के साथ कुछ समस्याओं में भाग ले सकते हैं। शायद समवर्ती पहुंच से हैशटेबल की रक्षा करें?

अब जब आप प्रत्येक आवंटन और डीलोकेशन लॉग करते हैं, तो आप (यदि आप चाहें) प्रोग्राम के परीक्षण के बारे में अधिक जानकारी प्रदान कर सकते हैं। किसी भी समय आवंटन की कुल और औसत संख्या जानना दिलचस्प हो सकता है? आवंटित कुल, अधिकतम, न्यूनतम और औसत बाइट, और आवंटन की औसत आयु।

यदि आप विभिन्न धागे की तुलना करना चाहते हैं, कम से कम Pthreads के साथ आप उन्हें pthread_self() के साथ पहचान सकते हैं। यह ढेर डीबगर एक काफी उपयोगी विश्लेषण उपकरण बन सकता है।

2

क्या आप एक बहुत कमजोर मॉलोक का उपयोग कर रहे हैं जिसमें पहले से ही इस तरह की चीजें नहीं बनाई गई हैं? क्योंकि यदि यह वहां है, तो आप थोड़ा लाभ के लिए ओवरहेड को दोगुना कर रहे हैं। साथ ही, इस प्रकार की प्रणाली वास्तव में छोटे ऑब्जेक्ट आवंटन करते समय दर्द होता है या उनके साथ अप्रभावी होता है क्योंकि लोग 1 आवंटन करते हैं और स्मृति को स्वयं प्रबंधित करते हैं।

जहां तक ​​कोड का सवाल है, ऐसा लगता है कि यह वही करेगा जो आप कहते हैं कि यह करेगा और यह अच्छी तरह डिज़ाइन किया गया है और पढ़ने में आसान है। लेकिन, यदि आप ऐसा करने की परेशानी से गुज़रने जा रहे हैं, तो प्रबंधित कंटेनर/पॉइंटर्स/ऑपरेटर [] चीज़ों का उपयोग करके स्रोत पर प्रवाह के नीचे/नीचे क्यों नहीं आते हैं। इस तरह, आप मुफ्त में यह पता लगाने की बजाय विफलता के स्थान पर डीबग कर सकते हैं कि कुछ बुराई हुई है।

ऐसी क्षमताएं हैं जिनके बारे में मुझे यकीन है कि दूसरों को मिल जाएगा, लेकिन कुछ मिनट के लिए आपके कोड को देखने के बाद ये मेरे सिर के शीर्ष से कुछ विचार हैं।

2

मुझे अंडरफ्लो/ओवरफ्लो का पता लगाने के बारे में आश्चर्य है।

मेरा मतलब है, अगर मैं एक 10 तत्वों सरणियों है, तो यह आप अगर मैं -1 और 10 पर लिखते हैं, लेकिन क्या अगर मैं 20 पर लिखने का पता लगा लेंगे लगता है? बफर ओवररन (संगत) के हिस्से के रूप में अंडरफ्लो या ओवरफ्लो जरूरी नहीं है।

इसके अलावा, ब्लॉक की रिहाई को रोकने की बात क्या है? यह ब्लॉक (अपेक्षाकृत) ठीक है, यह पड़ोसियों (दुर्भाग्य से) दूषित है।

वैसे भी, यह मेरे लिए बहुत अच्छा लगता है, हालांकि मेरे पास प्रति समारोह एक से अधिक रिटर्न होगा क्योंकि एकल निकास में कोई बात नहीं है। आप सी ++ एक से अधिक सी प्रोग्रामर लगते हैं :)

+0

अंडरफ्लो और ओवरफ़्लो दोनों दिशाओं में 16 बाइट्स तक पाए जाते हैं। यदि यह आपके लिए पर्याप्त नहीं है, तो बस 'कैनरी' सरणी का आकार बढ़ाएं ;-) – fredoverflow

संबंधित मुद्दे