source: src/Helpers/MemDebug.cpp@ f4d063

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

MEMFIX: MemDebug ignores all unknown sources. Removed all dependencies around main(), moved to builder_init.?pp.

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