source: molecuilder/src/Helpers/MemDebug.cpp@ 16eb32

Last change on this file since 16eb32 was 16eb32, checked in by Tillmann Crueger <crueger@…>, 16 years ago

FIX: Stupid bug in the Debug-new that destroyed some memory

  • Property mode set to 100644
File size: 7.0 KB
RevLine 
[90c4460]1/*
2 * MemDebug.cpp
3 *
4 * Created on: Apr 28, 2010
5 * Author: crueger
6 */
7
8#include <iostream>
9#include <cstdlib>
10
11using namespace std;
12
13namespace Memory {
[5d1a94]14
15 // This struct is added before each memory chunk
16 // and contains tracking information. Anything used
17 // to track memory cannot use any dynamic memory, so
18 // we have to resort to classic C-idioms here.
19 // This struct also contains pointers to the next
20 // an previous chunks to allow fast traversion of
21 // all allocated memory blocks
[90c4460]22 struct entry_t {
[5d1a94]23 // we seperate the tracking info from the rest
24 // A checksum will be calculated for this part of
25 // the struct, so the information in here should
26 // not change during the lifetime of the memory
[90c4460]27 struct info_t {
[5d1a94]28 enum {length = 64};
[16eb32]29 char file[length+1];
[90c4460]30 int line;
31 size_t nbytes;
32 bool isUsed;
33 void *location;
34 } info;
35 bool isIgnored;
36 char checksum;
37 entry_t *prev;
38 entry_t *next;
39 };
40
[5d1a94]41 // start and end of the doubly-linked list
[90c4460]42 entry_t *begin=0;
43 entry_t *end=0;
44
[5d1a94]45 // current amount of allocated memory
[90c4460]46 size_t state = 0;
[5d1a94]47 // maximum amount of allocated memory
[90c4460]48 size_t max = 0;
[5d1a94]49 // number of allocations that have been done so far
50 unsigned int allocs = 0;
51
[90c4460]52
[5d1a94]53 // this sets the alignment of the returned memory block
54 // malloc guarantees an alignment at the 8 byte border,
55 // so we just do the same
[90c4460]56 const int alignment = 8;
57
[5d1a94]58 // calculates a simple checksum for the info block
59 // the checksum is used to find memory corruptions
[90c4460]60 inline char calcChecksum(entry_t::info_t *info){
61 char *buffer = (char*)info;
62 char checksum =0;
63 for(size_t i=0;i<sizeof(entry_t::info_t);i++){
64 checksum+=buffer[i];
65 }
66 return checksum;
67 }
68
[5d1a94]69 // gets the next alignet point which is greater than nbytes
70 // this function is only called a fixed number of times, so
71 // there is no need to optimize
[90c4460]72 inline size_t doAlign(size_t nbytes){
73 int nonaligned = nbytes % alignment;
74 if(nonaligned) {
75 return(nbytes - nonaligned + alignment);
76 }
77 else{
78 return nbytes;
79 }
80 }
81
[5d1a94]82 // Output some state information
[90c4460]83 void getState(){
84 cout << "Maximum allocated Memory: " << max << " bytes" << endl;
85 cout << "Currently allocated Memory: " << state <<" bytes" << endl;
[5d1a94]86 cout << allocs << " allocated chunks total" << endl;
[90c4460]87
[5d1a94]88 // simple traversal of the chunk list
[90c4460]89 for(entry_t *pos=begin;pos;pos=pos->next){
90 cout << "\nChunk of " << pos->info.nbytes << " bytes" << " still available" << endl;
91 cout << "Chunk reserved at: " << pos->info.file << ":" << pos->info.line << endl;
92 }
93 }
94
[5d1a94]95 // Deletes an entry from the linked list
[90c4460]96 void deleteEntry(entry_t *entry){
97 if(entry->isIgnored)
98 return;
[5d1a94]99
[90c4460]100 if(entry->prev){
101 entry->prev->next = entry->next;
102 }
103 else{
[5d1a94]104 // this node was the beginning of the list
[90c4460]105 begin = entry->next;
106 }
107
108 if(entry->next){
109 entry->next->prev = entry->prev;
110 }
111 else{
[5d1a94]112 // this node was the end of the list
[90c4460]113 end = entry->prev;
114 }
115 entry->isIgnored = true;
116 Memory::state -= entry->info.nbytes;
117 }
118
119 void _ignore(void *ptr){
[5d1a94]120 // just deletes the node from the list, but leaves the info intact
[90c4460]121 static const size_t entrySpace = Memory::doAlign(sizeof(Memory::entry_t));
122 entry_t *entry = (Memory::entry_t*)((char*)ptr-entrySpace);
123 deleteEntry(entry);
124 }
125}
126
127void *operator new(size_t nbytes,const char* file, int line) throw(std::bad_alloc) {
128
[5d1a94]129 // to avoid allocations of 0 bytes if someone screws up
130 // allocation with 0 byte size are undefined behavior, so we are
131 // free to handle it this way
[90c4460]132 if(!nbytes) {
133 nbytes = 1;
134 }
135
[5d1a94]136 // get the size of the entry, including alignment
[90c4460]137 static const size_t entrySpace = Memory::doAlign(sizeof(Memory::entry_t));
138
139 void *res;
140 if(!(res=malloc(entrySpace + nbytes))){
[5d1a94]141 // new must throw, when space is low
[90c4460]142 throw std::bad_alloc();
143 }
144
[5d1a94]145 // we got the space, so update the global info
[90c4460]146 Memory::state += nbytes;
147 if(Memory::state>Memory::max){
148 Memory::max = Memory::state;
149 }
[5d1a94]150 Memory::allocs++;
[90c4460]151
[5d1a94]152 // build the entry in front of the space
[90c4460]153 Memory::entry_t *entry = (Memory::entry_t*) res;
154 entry->info.nbytes = nbytes;
155 entry->info.isUsed = true;
[5d1a94]156 strncpy(entry->info.file,file,Memory::entry_t::info_t::length);
[16eb32]157 entry->info.file[Memory::entry_t::info_t::length] = '\0';
[90c4460]158 entry->info.line=line;
[5d1a94]159 // the space starts behind the info
[90c4460]160 entry->info.location = (char*)res + entrySpace;
161
[5d1a94]162 // add the entry at the end of the list
163 entry->next=0; // the created block is last in the list
164 entry->prev=Memory::end; // the created block is last in the list
[90c4460]165 if(!Memory::begin){
[5d1a94]166 // the list was empty... start a new one
[90c4460]167 Memory::begin=entry;
168 }
169 else {
[5d1a94]170 // other blocks present... we can add to the last one
[90c4460]171 Memory::end->next=entry;
172 }
173 Memory::end=entry;
174
[5d1a94]175 // get the checksum...
[90c4460]176 entry->checksum = Memory::calcChecksum(&entry->info);
[5d1a94]177 // this will be set to true, when the block is removed from
178 // the list for any reason
[90c4460]179 entry->isIgnored = false;
180
[5d1a94]181 // ok, space is prepared... the user can have it.
182 // the rest (constructor, deleting when something is thrown etc)
183 // is handled automatically
[90c4460]184 return entry->info.location;
185}
186
187void *operator new(size_t nbytes) throw(std::bad_alloc) {
[5d1a94]188 // Just forward to the other operator, when we do not know from
189 // where the allocation came
[90c4460]190 return operator new(nbytes,"Unknown",0);
191}
192
193void *operator new[] (size_t nbytes,const char* file, int line) throw(std::bad_alloc) {
[5d1a94]194 // The difference between new and new[] is just for compiler bookkeeping.
[90c4460]195 return operator new(nbytes,file,line);
196}
197
198void *operator new[] (size_t nbytes) throw(std::bad_alloc) {
[5d1a94]199 // Forward again
[90c4460]200 return operator new[] (nbytes,"Unknown",0);
201}
202
203void operator delete(void *ptr) throw() {
[5d1a94]204 // get the size for the entry, including alignment
[90c4460]205 static const size_t entrySpace = Memory::doAlign(sizeof(Memory::entry_t));
206
[5d1a94]207 // get the position for the entry from the pointer the user gave us
[90c4460]208 Memory::entry_t *entry = (Memory::entry_t*)((char*)ptr-entrySpace);
209
[5d1a94]210 // let's see if the checksum is still matching
[90c4460]211 if(Memory::calcChecksum(&entry->info)!=entry->checksum){
212 cout << "Possible memory corruption detected!" << endl;
213 cout << "Trying to recover allocation information..." << endl;
214 cout << "Memory was allocated at " << entry->info.file << ":" << entry->info.line << endl;
215 terminate();
216 }
217
[5d1a94]218 // this will destroy the checksum, so double deletes are caught
[90c4460]219 entry->info.isUsed = false;
220 Memory::deleteEntry(entry);
221
[5d1a94]222 // delete the space reserved by malloc
[90c4460]223 free((char*)ptr-entrySpace);
224}
225
[5d1a94]226// operator that is called when the constructor throws
227// do not call manually
[90c4460]228void operator delete(void *ptr,const char*, int) throw() {
229 operator delete(ptr);
230}
231
232void operator delete[](void *ptr){
[5d1a94]233 // again difference between delete and delete[] is just in compiler bookkeeping
[90c4460]234 operator delete(ptr);
235}
236
[5d1a94]237// and another operator that can be called when a constructor throws
[90c4460]238void operator delete[](void *ptr,const char*, int) throw(){
239 operator delete(ptr);
240}
Note: See TracBrowser for help on using the repository browser.