source: src/Helpers/MemDebug.cpp@ ff58f1

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 ff58f1 was bf3817, checked in by Frederik Heber <heber@…>, 15 years ago

Added ifdef HAVE_CONFIG and config.h include to each and every cpp file.

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