source: src/Helpers/MemDebug.cpp@ 6d574a

Action_Thermostats Add_AtomRandomPerturbation Add_FitFragmentPartialChargesAction Add_RotateAroundBondAction Add_SelectAtomByNameAction Added_ParseSaveFragmentResults AddingActions_SaveParseParticleParameters Adding_Graph_to_ChangeBondActions Adding_MD_integration_tests Adding_ParticleName_to_Atom Adding_StructOpt_integration_tests AtomFragments Automaking_mpqc_open AutomationFragmentation_failures Candidate_v1.5.4 Candidate_v1.6.0 Candidate_v1.6.1 ChangeBugEmailaddress ChangingTestPorts ChemicalSpaceEvaluator CombiningParticlePotentialParsing Combining_Subpackages Debian_Package_split Debian_package_split_molecuildergui_only Disabling_MemDebug Docu_Python_wait EmpiricalPotential_contain_HomologyGraph EmpiricalPotential_contain_HomologyGraph_documentation Enable_parallel_make_install Enhance_userguide Enhanced_StructuralOptimization Enhanced_StructuralOptimization_continued Example_ManyWaysToTranslateAtom Exclude_Hydrogens_annealWithBondGraph FitPartialCharges_GlobalError Fix_BoundInBox_CenterInBox_MoleculeActions Fix_ChargeSampling_PBC Fix_ChronosMutex Fix_FitPartialCharges Fix_FitPotential_needs_atomicnumbers Fix_ForceAnnealing Fix_IndependentFragmentGrids Fix_ParseParticles Fix_ParseParticles_split_forward_backward_Actions Fix_PopActions Fix_QtFragmentList_sorted_selection Fix_Restrictedkeyset_FragmentMolecule Fix_StatusMsg Fix_StepWorldTime_single_argument Fix_Verbose_Codepatterns Fix_fitting_potentials Fixes ForceAnnealing_goodresults ForceAnnealing_oldresults ForceAnnealing_tocheck ForceAnnealing_with_BondGraph ForceAnnealing_with_BondGraph_continued ForceAnnealing_with_BondGraph_continued_betteresults ForceAnnealing_with_BondGraph_contraction-expansion FragmentAction_writes_AtomFragments FragmentMolecule_checks_bonddegrees GeometryObjects Gui_Fixes Gui_displays_atomic_force_velocity ImplicitCharges IndependentFragmentGrids IndependentFragmentGrids_IndividualZeroInstances IndependentFragmentGrids_IntegrationTest IndependentFragmentGrids_Sole_NN_Calculation JobMarket_RobustOnKillsSegFaults JobMarket_StableWorkerPool JobMarket_unresolvable_hostname_fix MoreRobust_FragmentAutomation ODR_violation_mpqc_open PartialCharges_OrthogonalSummation PdbParser_setsAtomName PythonUI_with_named_parameters QtGui_reactivate_TimeChanged_changes Recreated_GuiChecks Rewrite_FitPartialCharges RotateToPrincipalAxisSystem_UndoRedo SaturateAtoms_findBestMatching SaturateAtoms_singleDegree StoppableMakroAction Subpackage_CodePatterns Subpackage_JobMarket Subpackage_LinearAlgebra Subpackage_levmar Subpackage_mpqc_open Subpackage_vmg Switchable_LogView ThirdParty_MPQC_rebuilt_buildsystem TrajectoryDependenant_MaxOrder TremoloParser_IncreasedPrecision TremoloParser_MultipleTimesteps TremoloParser_setsAtomName Ubuntu_1604_changes stable
Last change on this file since 6d574a was 6d574a, checked in by Tillmann Crueger <crueger@…>, 15 years ago

Replaced several old-style asserts with more usable ASSERTs

  • Property mode set to 100644
File size: 13.7 KB
Line 
1/*
2 * MemDebug.cpp
3 *
4 * Created on: Apr 28, 2010
5 * Author: crueger
6 */
7
8#ifndef NDEBUG
9#ifndef NO_MEMDEBUG
10
11#include <iostream>
12#include <cstdlib>
13#include <cstring>
14#include <boost/thread.hpp>
15
16#ifdef __GNUC__
17#include <execinfo.h>
18#include <cxxabi.h>
19#endif
20
21using namespace std;
22
23// we need our own low level mutexex, since we cannot assure the time of construction and destruction
24// otherwise
25#if defined(unix) || defined(__unix)
26
27#include <pthread.h>
28#include <cassert>
29#define mutex_t pthread_mutex_t
30#define mutex_init PTHREAD_MUTEX_INITIALIZER
31#define mutex_lock(mtx) \
32 do{\
33 int res = pthread_mutex_lock(&(mtx));\
34 assert(!res && "Could not lock mutex!");\
35 }while(0)
36
37#define mutex_unlock(mtx) \
38 do{\
39 int res = pthread_mutex_unlock(&(mtx));\
40 assert(!res && "Could not unlock mutex!");\
41 }while(0)
42
43#else
44# error "No thread structure defined for this plattform..."
45#endif
46
47namespace Memory {
48
49 // This struct is added before each memory chunk
50 // and contains tracking information. Anything used
51 // to track memory cannot use any dynamic memory, so
52 // we have to resort to classic C-idioms here.
53 // This struct also contains pointers to the next
54 // an previous chunks to allow fast traversion of
55 // all allocated memory blocks
56 struct entry_t {
57 // we seperate the tracking info from the rest
58 // A checksum will be calculated for this part of
59 // the struct, so the information in here should
60 // not change during the lifetime of the memory
61 struct info_t {
62 enum {length = 64};
63 char file[length+1];
64 int line;
65#ifdef __GNUC__ // function tracking only works with GCC
66 // function names can get looooong
67 enum {length2 = 256};
68 char function[length2+1];
69#endif
70 size_t nbytes;
71 bool isUsed;
72 void *location;
73 } info;
74 bool isIgnored;
75 char checksum;
76 entry_t *prev;
77 entry_t *next;
78 };
79
80
81 mutex_t memorylock = mutex_init;
82
83 // start and end of the doubly-linked list
84 entry_t *begin=0;
85 entry_t *end=0;
86
87 // current amount of allocated memory
88 size_t state = 0;
89 // maximum amount of allocated memory
90 size_t max = 0;
91 // number of allocations that have been done so far
92 unsigned int allocs = 0;
93
94
95 // this sets the alignment of the returned memory block
96 // malloc guarantees an alignment at the 8 byte border,
97 // so we just do the same
98 const int alignment = 8;
99
100 // calculates a simple checksum for the info block
101 // the checksum is used to find memory corruptions
102 inline char calcChecksum(entry_t::info_t *info){
103 char *buffer = (char*)info;
104 char checksum =0;
105 for(size_t i=0;i<sizeof(entry_t::info_t);i++){
106 checksum+=buffer[i];
107 }
108 return checksum;
109 }
110
111 // gets the next alignet point which is greater than nbytes
112 // this function is only called a fixed number of times, so
113 // there is no need to optimize
114 inline size_t doAlign(size_t nbytes){
115 int nonaligned = nbytes % alignment;
116 if(nonaligned) {
117 return(nbytes - nonaligned + alignment);
118 }
119 else{
120 return nbytes;
121 }
122 }
123
124 // Output some state information
125 void getState(){
126 cout << "Maximum allocated Memory: " << max << " bytes" << endl;
127 cout << "Currently allocated Memory: " << state <<" bytes" << endl;
128 cout << allocs << " allocated chunks total" << endl;
129
130 // simple traversal of the chunk list
131 for(entry_t *pos=begin;pos;pos=pos->next){
132 cout << "\nChunk of " << pos->info.nbytes << " bytes" << " still available" << endl;
133#ifdef __GNUC__
134 cout << "Chunk reserved at: " << pos->info.function
135 << " (" << pos->info.file << ":" << pos->info.line << ")" << endl;
136#else
137 cout << "Chunk reserved at: " << pos->info.file << ":" << pos->info.line << endl;
138#endif
139 }
140 }
141
142 // Adds an entry to the linked list
143 void addEntry(entry_t *entry){
144 // check if the entry is already in the list
145 if(!entry->isIgnored)
146 return;
147
148 mutex_lock(Memory::memorylock);
149
150 entry->next=0; // the created block is last in the list
151 entry->prev=Memory::end; // the created block is last in the list
152 if(!Memory::begin){
153 // the list was empty... start a new one
154 Memory::begin=entry;
155 }
156 else {
157 // other blocks present... we can add to the last one
158 Memory::end->next=entry;
159 }
160 Memory::end=entry;
161
162 // update some global info
163 Memory::state += entry->info.nbytes;
164 if(Memory::state>Memory::max){
165 Memory::max = Memory::state;
166 }
167 ++Memory::allocs;
168 // done with the list... it is safe to unlock now
169 mutex_unlock(Memory::memorylock);
170 entry->isIgnored = false;
171 }
172
173 // Deletes an entry from the linked list
174 void deleteEntry(entry_t *entry){
175 if(entry->isIgnored)
176 return;
177
178 mutex_lock(memorylock);
179 if(entry->prev){
180 entry->prev->next = entry->next;
181 }
182 else{
183 // this node was the beginning of the list
184 begin = entry->next;
185 }
186
187 if(entry->next){
188 entry->next->prev = entry->prev;
189 }
190 else{
191 // this node was the end of the list
192 end = entry->prev;
193 }
194 Memory::state -= entry->info.nbytes;
195 mutex_unlock(memorylock);
196 entry->isIgnored = true;
197
198 }
199
200 void _ignore(void *ptr){
201 // just deletes the node from the list, but leaves the info intact
202 static const size_t entrySpace = Memory::doAlign(sizeof(Memory::entry_t));
203 entry_t *entry = (Memory::entry_t*)((char*)ptr-entrySpace);
204 deleteEntry(entry);
205 }
206
207#ifdef __GNUC__
208 // this function let's us find the caller's name
209 char* getCaller(){
210 // stack looks like this:
211 // getCaller();
212 // operator new();
213 // function_we_are_looking_for(); <-
214 const size_t max_depth = 3;
215 void* stack_addrs[max_depth];
216 size_t stack_depth;
217 char **stack_strings=0;
218 const char *func_name=0;
219 const char *toplevel = "Global scope";
220 char *retval=0;
221
222 // get the backtrace, depth three
223 stack_depth = backtrace(stack_addrs,max_depth);
224 stack_strings = backtrace_symbols(stack_addrs, stack_depth);
225 // used later for demangling
226 // reserved here, so we can free it unconditionally
227 char *dm_function = static_cast<char*>(malloc(entry_t::info_t::length2));
228 if(!dm_function){
229 // malloc failed... we are out of luck
230 throw std::bad_alloc();
231 }
232
233 // see if we found our function name
234 if(stack_depth==max_depth){
235 // find the mangled function name
236 char *begin = stack_strings[max_depth-1];
237 // function name starts with a (
238 while(*begin && *begin!='(') ++begin;
239 char *end=begin;
240 while(*end && *end!='+') ++end;
241
242 // see if we found our function name
243 if(*begin && *end){
244 *begin++ = 0;
245 *end = 0;
246 // use the C++ demangler
247
248 size_t sz = entry_t::info_t::length2;
249 int status;
250 char *func_ret = abi::__cxa_demangle(begin, dm_function, &sz, &status);
251 if(func_ret){
252 // abi might have realloced...
253 dm_function = func_ret;
254 func_name = dm_function;
255 }
256 else{
257 // demangling failed... get the function name without demangling
258 func_name = begin;
259 }
260 }
261 else{
262 // function name not found... get the whole line
263 func_name = stack_strings[max_depth-1];
264 }
265
266 }
267 else{
268 func_name = toplevel;
269 }
270
271 // now we copy the desired function name
272 if((retval = static_cast<char*>(malloc(strlen(func_name)+1)))){
273 // we know that the string will fit, so strcpy is safe here
274 strcpy(retval,func_name);
275 }
276 else{
277 free(stack_strings); // malloc()ed by backtrace_symbols
278 free(dm_function);
279 // uh-uh ... seems we are out of luck for allocations now
280 throw std::bad_alloc();
281 }
282 free(dm_function);
283 free(stack_strings); // malloc()ed by backtrace_symbols
284 return retval;
285 }
286#endif
287}
288
289#ifdef __GNUC__
290
291void *operator new(size_t nbytes,const char* file, int line, const char* func) throw(std::bad_alloc) {
292
293 // to avoid allocations of 0 bytes if someone screws up
294 // allocation with 0 byte size are undefined behavior, so we are
295 // free to handle it this way
296 if(!nbytes) {
297 nbytes = 1;
298 }
299
300 // get the size of the entry, including alignment
301 static const size_t entrySpace = Memory::doAlign(sizeof(Memory::entry_t));
302
303 void *res;
304 if(!(res=malloc(entrySpace + nbytes))){
305 // new must throw, when space is low
306 throw std::bad_alloc();
307 }
308
309 // build the entry in front of the space
310 Memory::entry_t *entry = (Memory::entry_t*) res;
311 memset(res,0,entrySpace);
312 entry->info.nbytes = nbytes;
313 entry->info.isUsed = true;
314 strncpy(entry->info.file,file,Memory::entry_t::info_t::length);
315 entry->info.file[Memory::entry_t::info_t::length] = '\0';
316 entry->info.line=line;
317 strncpy(entry->info.function,func,Memory::entry_t::info_t::length2);
318 entry->info.function[Memory::entry_t::info_t::length2] = '\0';
319 // the space starts behind the info
320 entry->info.location = (char*)res + entrySpace;
321
322 // mark the block as not in the list (will be changed by addEntry)
323 entry->isIgnored = true;
324 Memory::addEntry(entry);
325
326 // get the checksum...
327 entry->checksum = Memory::calcChecksum(&entry->info);
328
329 // ok, space is prepared... the user can have it.
330 // the rest (constructor, deleting when something is thrown etc)
331 // is handled automatically
332 return entry->info.location;
333}
334
335#else
336
337void *operator new(size_t nbytes,const char* file, int line) throw(std::bad_alloc) {
338
339 // to avoid allocations of 0 bytes if someone screws up
340 // allocation with 0 byte size are undefined behavior, so we are
341 // free to handle it this way
342 if(!nbytes) {
343 nbytes = 1;
344 }
345
346 // get the size of the entry, including alignment
347 static const size_t entrySpace = Memory::doAlign(sizeof(Memory::entry_t));
348
349 void *res;
350 if(!(res=malloc(entrySpace + nbytes))){
351 // new must throw, when space is low
352 throw std::bad_alloc();
353 }
354
355 // build the entry in front of the space
356 Memory::entry_t *entry = (Memory::entry_t*) res;
357 memset(res,0,entrySpace);
358 entry->info.nbytes = nbytes;
359 entry->info.isUsed = true;
360 strncpy(entry->info.file,file,Memory::entry_t::info_t::length);
361 entry->info.file[Memory::entry_t::info_t::length] = '\0';
362 entry->info.line=line;
363 // the space starts behind the info
364 entry->info.location = (char*)res + entrySpace;
365
366 // mark the block as not in the list (will be changed by addEntry)
367 entry->isIgnored = true;
368 Memory::addEntry(entry);
369
370 // get the checksum...
371 entry->checksum = Memory::calcChecksum(&entry->info);
372 // this will be set to true, when the block is removed from
373 // the list for any reason
374 entry->isIgnored = false;
375
376 // ok, space is prepared... the user can have it.
377 // the rest (constructor, deleting when something is thrown etc)
378 // is handled automatically
379 return entry->info.location;
380}
381
382#endif
383
384void *operator new(size_t nbytes) throw(std::bad_alloc) {
385 // Just forward to the other operator, when we do not know from
386 // where the allocation came
387#ifdef __GNUC__
388 // this might throw bad_alloc
389 char *caller = Memory::getCaller();
390 void* retval = 0;
391
392 // if this throws, we have to clean up the caller anyway
393 try{
394 retval = operator new(nbytes,"Unknown",0,caller);
395 }
396 catch(...)
397 {
398 free(caller); // malloc()ed by Memory::getCaller();
399 throw;
400 }
401 free(caller); // malloc()ed by Memory::getCaller();
402 return retval;
403#else
404 return operator new(nbytes,"Unknown",0);
405#endif
406}
407
408#ifdef __GNUC__
409
410void *operator new[] (size_t nbytes,const char* file, int line, const char* func) throw(std::bad_alloc) {
411 // The difference between new and new[] is just for compiler bookkeeping.
412 return operator new(nbytes,file,line,func);
413}
414
415#else
416
417void *operator new[] (size_t nbytes,const char* file, int line) throw(std::bad_alloc) {
418 // The difference between new and new[] is just for compiler bookkeeping.
419 return operator new(nbytes,file,line);
420}
421
422#endif
423
424void *operator new[] (size_t nbytes) throw(std::bad_alloc) {
425 // Forward again
426#ifdef __GNUC__
427 // this might throw bad_alloc
428 char *caller = Memory::getCaller();
429 void *retval=0;
430
431 // if this throws, we have to clean up the caller anyway
432 try{
433 retval = operator new[] (nbytes,"Unknown",0,caller);
434 }
435 catch(...)
436 {
437 free(caller); // malloc()ed by Memory::getCaller();
438 throw;
439 }
440 free(caller); // malloc()ed by Memory::getCaller();
441 return retval;
442#else
443 return operator new[] (nbytes,"Unknown",0);
444#endif
445}
446
447void operator delete(void *ptr) throw() {
448 if(!ptr){
449 cerr << "Warning: Deleting NULL pointer" << endl;
450 return;
451 }
452
453 // get the size for the entry, including alignment
454 static const size_t entrySpace = Memory::doAlign(sizeof(Memory::entry_t));
455
456 // get the position for the entry from the pointer the user gave us
457 Memory::entry_t *entry = (Memory::entry_t*)((char*)ptr-entrySpace);
458
459 // let's see if the checksum is still matching
460 if(Memory::calcChecksum(&entry->info)!=entry->checksum){
461 cerr << "Possible memory corruption detected!" << endl;
462 cerr << "Trying to recover allocation information..." << endl;
463 cerr << "Memory was allocated at " << entry->info.file << ":" << entry->info.line << endl;
464 terminate();
465 }
466
467 // this will destroy the checksum, so double deletes are caught
468 entry->info.isUsed = false;
469 Memory::deleteEntry(entry);
470
471 // delete the space reserved by malloc
472 free((char*)ptr-entrySpace);
473}
474
475// operator that is called when the constructor throws
476// do not call manually
477void operator delete(void *ptr,const char*, int) throw() {
478 operator delete(ptr);
479}
480
481void operator delete[](void *ptr){
482 // again difference between delete and delete[] is just in compiler bookkeeping
483 operator delete(ptr);
484}
485
486// and another operator that can be called when a constructor throws
487void operator delete[](void *ptr,const char*, int) throw(){
488 operator delete(ptr);
489}
490#endif
491#endif
Note: See TracBrowser for help on using the repository browser.