source: src/Helpers/MemDebug.cpp@ 097902

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 097902 was 097902, checked in by Tillmann Crueger <crueger@…>, 14 years ago

Made the Programm dump some memory statistics to a file when an assertion fails

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