source: src/Helpers/MemDebug.cpp@ bcf653

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

Added copyright note to each .cpp file and an extensive one to builder.cpp.

  • Property mode set to 100644
File size: 15.5 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 Memory::addEntry(entry);
383
384 // get the checksum...
385 entry->checksum = Memory::calcChecksum(&entry->info);
386
387 // ok, space is prepared... the user can have it.
388 // the rest (constructor, deleting when something is thrown etc)
389 // is handled automatically
390 return entry->info.location;
391}
392
393#else
394
395void *operator new(size_t nbytes,const char* file, int line) throw(std::bad_alloc) {
396
397 // to avoid allocations of 0 bytes if someone screws up
398 // allocation with 0 byte size are undefined behavior, so we are
399 // free to handle it this way
400 if(!nbytes) {
401 nbytes = 1;
402 }
403
404 // get the size of the entry, including alignment
405 static const size_t entrySpace = Memory::doAlign(sizeof(Memory::entry_t));
406
407 void *res;
408 if(!(res=malloc(entrySpace + nbytes))){
409 // new must throw, when space is low
410 throw std::bad_alloc();
411 }
412
413 // build the entry in front of the space
414 Memory::entry_t *entry = (Memory::entry_t*) res;
415 memset(res,0,entrySpace);
416 entry->info.nbytes = nbytes;
417 entry->info.isUsed = true;
418 strncpy(entry->info.file,file,Memory::entry_t::info_t::length);
419 entry->info.file[Memory::entry_t::info_t::length] = '\0';
420 entry->info.line=line;
421 // the space starts behind the info
422 entry->info.location = (char*)res + entrySpace;
423
424 // mark the block as not in the list (will be changed by addEntry)
425 entry->isIgnored = true;
426 Memory::addEntry(entry);
427
428 // get the checksum...
429 entry->checksum = Memory::calcChecksum(&entry->info);
430 // this will be set to true, when the block is removed from
431 // the list for any reason
432 entry->isIgnored = false;
433
434 // ok, space is prepared... the user can have it.
435 // the rest (constructor, deleting when something is thrown etc)
436 // is handled automatically
437 return entry->info.location;
438}
439
440#endif
441
442void *operator new(size_t nbytes) throw(std::bad_alloc) {
443 // Just forward to the other operator, when we do not know from
444 // where the allocation came
445#ifdef __GNUC__
446 // this might throw bad_alloc
447 char *caller = Memory::getCaller();
448 void* retval = 0;
449
450 // if this throws, we have to clean up the caller anyway
451 try{
452 retval = operator new(nbytes,"Unknown",0,caller);
453 }
454 catch(...)
455 {
456 free(caller); // malloc()ed by Memory::getCaller();
457 throw;
458 }
459 free(caller); // malloc()ed by Memory::getCaller();
460 return retval;
461#else
462 return operator new(nbytes,"Unknown",0);
463#endif
464}
465
466#ifdef __GNUC__
467
468void *operator new[] (size_t nbytes,const char* file, int line, const char* func) throw(std::bad_alloc) {
469 // The difference between new and new[] is just for compiler bookkeeping.
470 return operator new(nbytes,file,line,func);
471}
472
473#else
474
475void *operator new[] (size_t nbytes,const char* file, int line) throw(std::bad_alloc) {
476 // The difference between new and new[] is just for compiler bookkeeping.
477 return operator new(nbytes,file,line);
478}
479
480#endif
481
482void *operator new[] (size_t nbytes) throw(std::bad_alloc) {
483 // Forward again
484#ifdef __GNUC__
485 // this might throw bad_alloc
486 char *caller = Memory::getCaller();
487 void *retval=0;
488
489 // if this throws, we have to clean up the caller anyway
490 try{
491 retval = operator new[] (nbytes,"Unknown",0,caller);
492 }
493 catch(...)
494 {
495 free(caller); // malloc()ed by Memory::getCaller();
496 throw;
497 }
498 free(caller); // malloc()ed by Memory::getCaller();
499 return retval;
500#else
501 return operator new[] (nbytes,"Unknown",0);
502#endif
503}
504
505void operator delete(void *ptr) throw() {
506 if(!ptr){
507 cerr << "Warning: Deleting NULL pointer" << endl;
508 return;
509 }
510
511 // get the size for the entry, including alignment
512 static const size_t entrySpace = Memory::doAlign(sizeof(Memory::entry_t));
513
514 // get the position for the entry from the pointer the user gave us
515 Memory::entry_t *entry = (Memory::entry_t*)((char*)ptr-entrySpace);
516
517 // let's see if the checksum is still matching
518 if(Memory::calcChecksum(&entry->info)!=entry->checksum){
519 cerr << "Possible memory corruption detected!" << endl;
520 cerr << "Trying to recover allocation information..." << endl;
521 cerr << "Memory was allocated at " << entry->info.file << ":" << entry->info.line << endl;
522 terminate();
523 }
524
525 // this will destroy the checksum, so double deletes are caught
526 entry->info.isUsed = false;
527 Memory::deleteEntry(entry);
528
529 // delete the space reserved by malloc
530 free((char*)ptr-entrySpace);
531}
532
533// operator that is called when the constructor throws
534// do not call manually
535void operator delete(void *ptr,const char*, int) throw() {
536 operator delete(ptr);
537}
538
539void operator delete[](void *ptr){
540 // again difference between delete and delete[] is just in compiler bookkeeping
541 operator delete(ptr);
542}
543
544// and another operator that can be called when a constructor throws
545void operator delete[](void *ptr,const char*, int) throw(){
546 operator delete(ptr);
547}
548#endif
Note: See TracBrowser for help on using the repository browser.