source: src/Helpers/MemDebug.cpp@ 9cd807

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 9cd807 was bf3817, checked in by Frederik Heber <heber@…>, 14 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
Line 
1/*
2 * MemDebug.cpp
3 *
4 * Created on: Apr 28, 2010
5 * Author: crueger
6 */
7
8// include config.h
9#ifdef HAVE_CONFIG_H
10#include <config.h>
11#endif
12
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
32
33#include <iostream>
34#include <cstdlib>
35#include <cstring>
36#include <boost/thread.hpp>
37
38#ifdef __GNUC__
39#include <execinfo.h>
40#include <cxxabi.h>
41#endif
42
43using namespace std;
44
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{\
55 int res = pthread_mutex_lock(&(mtx));\
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
69namespace Memory {
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
78 struct entry_t {
79 typedef unsigned int checksum_t;
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
84 struct info_t {
85 enum {length = 64};
86 char file[length+1];
87 int line;
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
93 size_t nbytes;
94 bool isUsed;
95 void *location;
96 } info;
97 bool isIgnored;
98 checksum_t checksum;
99 entry_t *prev;
100 entry_t *next;
101 };
102
103
104 mutex_t memorylock = mutex_init;
105
106 // start and end of the doubly-linked list
107 entry_t *begin=0;
108 entry_t *end=0;
109
110 // current amount of allocated memory
111 size_t state = 0;
112 // maximum amount of allocated memory
113 size_t max = 0;
114 // number of allocations that have been done so far
115 unsigned int allocs = 0;
116
117
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
121 const int alignment = 8;
122
123 // calculates a simple checksum for the info block
124 // the checksum is used to find memory corruptions
125 inline entry_t::checksum_t calcChecksum(entry_t::info_t *info){
126 char *buffer = (char*)info;
127 entry_t::checksum_t checksum =0;
128 for(size_t i=0;i<sizeof(entry_t::info_t);i++){
129 checksum+=buffer[i];
130 }
131 return checksum;
132 }
133
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
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
147 // Output some state information
148 void getState(){
149 cout << "Maximum allocated Memory: " << max << " bytes" << endl;
150 cout << "Currently allocated Memory: " << state <<" bytes" << endl;
151 cout << allocs << " allocated chunks total" << endl;
152
153 // simple traversal of the chunk list
154 for(entry_t *pos=begin;pos;pos=pos->next){
155 cout << "\nChunk of " << pos->info.nbytes << " bytes" << " still available" << endl;
156#ifdef __GNUC__
157 cout << "Chunk reserved at: " << pos->info.function
158 << " (" << pos->info.file << ":" << pos->info.line << ")" << endl;
159#else
160 cout << "Chunk reserved at: " << pos->info.file << ":" << pos->info.line << endl;
161#endif
162 }
163 }
164
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
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
224 // Deletes an entry from the linked list
225 void deleteEntry(entry_t *entry){
226 if(entry->isIgnored)
227 return;
228
229 mutex_lock(memorylock);
230 if(entry->prev){
231 entry->prev->next = entry->next;
232 }
233 else{
234 // this node was the beginning of the list
235 begin = entry->next;
236 }
237
238 if(entry->next){
239 entry->next->prev = entry->prev;
240 }
241 else{
242 // this node was the end of the list
243 end = entry->prev;
244 }
245 Memory::state -= entry->info.nbytes;
246 mutex_unlock(memorylock);
247 entry->isIgnored = true;
248
249 }
250
251 void _ignore(void *ptr){
252 // just deletes the node from the list, but leaves the info intact
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 }
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
338}
339
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
373 // mark the block as not in the list (will be changed by addEntry)
374 entry->isIgnored = true;
375 Memory::addEntry(entry);
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
388void *operator new(size_t nbytes,const char* file, int line) throw(std::bad_alloc) {
389
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
393 if(!nbytes) {
394 nbytes = 1;
395 }
396
397 // get the size of the entry, including alignment
398 static const size_t entrySpace = Memory::doAlign(sizeof(Memory::entry_t));
399
400 void *res;
401 if(!(res=malloc(entrySpace + nbytes))){
402 // new must throw, when space is low
403 throw std::bad_alloc();
404 }
405
406 // build the entry in front of the space
407 Memory::entry_t *entry = (Memory::entry_t*) res;
408 memset(res,0,entrySpace);
409 entry->info.nbytes = nbytes;
410 entry->info.isUsed = true;
411 strncpy(entry->info.file,file,Memory::entry_t::info_t::length);
412 entry->info.file[Memory::entry_t::info_t::length] = '\0';
413 entry->info.line=line;
414 // the space starts behind the info
415 entry->info.location = (char*)res + entrySpace;
416
417 // mark the block as not in the list (will be changed by addEntry)
418 entry->isIgnored = true;
419 Memory::addEntry(entry);
420
421 // get the checksum...
422 entry->checksum = Memory::calcChecksum(&entry->info);
423 // this will be set to true, when the block is removed from
424 // the list for any reason
425 entry->isIgnored = false;
426
427 // ok, space is prepared... the user can have it.
428 // the rest (constructor, deleting when something is thrown etc)
429 // is handled automatically
430 return entry->info.location;
431}
432
433#endif
434
435void *operator new(size_t nbytes) throw(std::bad_alloc) {
436 // Just forward to the other operator, when we do not know from
437 // where the allocation came
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
455 return operator new(nbytes,"Unknown",0);
456#endif
457}
458
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
468void *operator new[] (size_t nbytes,const char* file, int line) throw(std::bad_alloc) {
469 // The difference between new and new[] is just for compiler bookkeeping.
470 return operator new(nbytes,file,line);
471}
472
473#endif
474
475void *operator new[] (size_t nbytes) throw(std::bad_alloc) {
476 // Forward again
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
494 return operator new[] (nbytes,"Unknown",0);
495#endif
496}
497
498void operator delete(void *ptr) throw() {
499 if(!ptr){
500 cerr << "Warning: Deleting NULL pointer" << endl;
501 return;
502 }
503
504 // get the size for the entry, including alignment
505 static const size_t entrySpace = Memory::doAlign(sizeof(Memory::entry_t));
506
507 // get the position for the entry from the pointer the user gave us
508 Memory::entry_t *entry = (Memory::entry_t*)((char*)ptr-entrySpace);
509
510 // let's see if the checksum is still matching
511 if(Memory::calcChecksum(&entry->info)!=entry->checksum){
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;
515 terminate();
516 }
517
518 // this will destroy the checksum, so double deletes are caught
519 entry->info.isUsed = false;
520 Memory::deleteEntry(entry);
521
522 // delete the space reserved by malloc
523 free((char*)ptr-entrySpace);
524}
525
526// operator that is called when the constructor throws
527// do not call manually
528void operator delete(void *ptr,const char*, int) throw() {
529 operator delete(ptr);
530}
531
532void operator delete[](void *ptr){
533 // again difference between delete and delete[] is just in compiler bookkeeping
534 operator delete(ptr);
535}
536
537// and another operator that can be called when a constructor throws
538void operator delete[](void *ptr,const char*, int) throw(){
539 operator delete(ptr);
540}
541#endif
Note: See TracBrowser for help on using the repository browser.