source: src/Helpers/MemDebug.cpp@ 51a5c8

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 51a5c8 was f0f1cc, checked in by Tillmann Crueger <crueger@…>, 15 years ago

Simplified the #ifdef and #define structure in MemDebug.hpp

  • Property mode set to 100644
File size: 13.9 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 // we seperate the tracking info from the rest
75 // A checksum will be calculated for this part of
76 // the struct, so the information in here should
77 // not change during the lifetime of the memory
78 struct info_t {
79 enum {length = 64};
80 char file[length+1];
81 int line;
82#ifdef __GNUC__ // function tracking only works with GCC
83 // function names can get looooong
84 enum {length2 = 256};
85 char function[length2+1];
86#endif
87 size_t nbytes;
88 bool isUsed;
89 void *location;
90 } info;
91 bool isIgnored;
92 char checksum;
93 entry_t *prev;
94 entry_t *next;
95 };
96
97
98 mutex_t memorylock = mutex_init;
99
100 // start and end of the doubly-linked list
101 entry_t *begin=0;
102 entry_t *end=0;
103
104 // current amount of allocated memory
105 size_t state = 0;
106 // maximum amount of allocated memory
107 size_t max = 0;
108 // number of allocations that have been done so far
109 unsigned int allocs = 0;
110
111
112 // this sets the alignment of the returned memory block
113 // malloc guarantees an alignment at the 8 byte border,
114 // so we just do the same
115 const int alignment = 8;
116
117 // calculates a simple checksum for the info block
118 // the checksum is used to find memory corruptions
119 inline char calcChecksum(entry_t::info_t *info){
120 char *buffer = (char*)info;
121 char checksum =0;
122 for(size_t i=0;i<sizeof(entry_t::info_t);i++){
123 checksum+=buffer[i];
124 }
125 return checksum;
126 }
127
128 // gets the next alignet point which is greater than nbytes
129 // this function is only called a fixed number of times, so
130 // there is no need to optimize
131 inline size_t doAlign(size_t nbytes){
132 int nonaligned = nbytes % alignment;
133 if(nonaligned) {
134 return(nbytes - nonaligned + alignment);
135 }
136 else{
137 return nbytes;
138 }
139 }
140
141 // Output some state information
142 void getState(){
143 cout << "Maximum allocated Memory: " << max << " bytes" << endl;
144 cout << "Currently allocated Memory: " << state <<" bytes" << endl;
145 cout << allocs << " allocated chunks total" << endl;
146
147 // simple traversal of the chunk list
148 for(entry_t *pos=begin;pos;pos=pos->next){
149 cout << "\nChunk of " << pos->info.nbytes << " bytes" << " still available" << endl;
150#ifdef __GNUC__
151 cout << "Chunk reserved at: " << pos->info.function
152 << " (" << pos->info.file << ":" << pos->info.line << ")" << endl;
153#else
154 cout << "Chunk reserved at: " << pos->info.file << ":" << pos->info.line << endl;
155#endif
156 }
157 }
158
159 // Adds an entry to the linked list
160 void addEntry(entry_t *entry){
161 // check if the entry is already in the list
162 if(!entry->isIgnored)
163 return;
164
165 mutex_lock(Memory::memorylock);
166
167 entry->next=0; // the created block is last in the list
168 entry->prev=Memory::end; // the created block is last in the list
169 if(!Memory::begin){
170 // the list was empty... start a new one
171 Memory::begin=entry;
172 }
173 else {
174 // other blocks present... we can add to the last one
175 Memory::end->next=entry;
176 }
177 Memory::end=entry;
178
179 // update some global info
180 Memory::state += entry->info.nbytes;
181 if(Memory::state>Memory::max){
182 Memory::max = Memory::state;
183 }
184 ++Memory::allocs;
185 // done with the list... it is safe to unlock now
186 mutex_unlock(Memory::memorylock);
187 entry->isIgnored = false;
188 }
189
190 // Deletes an entry from the linked list
191 void deleteEntry(entry_t *entry){
192 if(entry->isIgnored)
193 return;
194
195 mutex_lock(memorylock);
196 if(entry->prev){
197 entry->prev->next = entry->next;
198 }
199 else{
200 // this node was the beginning of the list
201 begin = entry->next;
202 }
203
204 if(entry->next){
205 entry->next->prev = entry->prev;
206 }
207 else{
208 // this node was the end of the list
209 end = entry->prev;
210 }
211 Memory::state -= entry->info.nbytes;
212 mutex_unlock(memorylock);
213 entry->isIgnored = true;
214
215 }
216
217 void _ignore(void *ptr){
218 // just deletes the node from the list, but leaves the info intact
219 static const size_t entrySpace = Memory::doAlign(sizeof(Memory::entry_t));
220 entry_t *entry = (Memory::entry_t*)((char*)ptr-entrySpace);
221 deleteEntry(entry);
222 }
223
224#ifdef __GNUC__
225 // this function let's us find the caller's name
226 char* getCaller(){
227 // stack looks like this:
228 // getCaller();
229 // operator new();
230 // function_we_are_looking_for(); <-
231 const size_t max_depth = 3;
232 void* stack_addrs[max_depth];
233 size_t stack_depth;
234 char **stack_strings=0;
235 const char *func_name=0;
236 const char *toplevel = "Global scope";
237 char *retval=0;
238
239 // get the backtrace, depth three
240 stack_depth = backtrace(stack_addrs,max_depth);
241 stack_strings = backtrace_symbols(stack_addrs, stack_depth);
242 // used later for demangling
243 // reserved here, so we can free it unconditionally
244 char *dm_function = static_cast<char*>(malloc(entry_t::info_t::length2));
245 if(!dm_function){
246 // malloc failed... we are out of luck
247 throw std::bad_alloc();
248 }
249
250 // see if we found our function name
251 if(stack_depth==max_depth){
252 // find the mangled function name
253 char *begin = stack_strings[max_depth-1];
254 // function name starts with a (
255 while(*begin && *begin!='(') ++begin;
256 char *end=begin;
257 while(*end && *end!='+') ++end;
258
259 // see if we found our function name
260 if(*begin && *end){
261 *begin++ = 0;
262 *end = 0;
263 // use the C++ demangler
264
265 size_t sz = entry_t::info_t::length2;
266 int status;
267 char *func_ret = abi::__cxa_demangle(begin, dm_function, &sz, &status);
268 if(func_ret){
269 // abi might have realloced...
270 dm_function = func_ret;
271 func_name = dm_function;
272 }
273 else{
274 // demangling failed... get the function name without demangling
275 func_name = begin;
276 }
277 }
278 else{
279 // function name not found... get the whole line
280 func_name = stack_strings[max_depth-1];
281 }
282
283 }
284 else{
285 func_name = toplevel;
286 }
287
288 // now we copy the desired function name
289 if((retval = static_cast<char*>(malloc(strlen(func_name)+1)))){
290 // we know that the string will fit, so strcpy is safe here
291 strcpy(retval,func_name);
292 }
293 else{
294 free(stack_strings); // malloc()ed by backtrace_symbols
295 free(dm_function);
296 // uh-uh ... seems we are out of luck for allocations now
297 throw std::bad_alloc();
298 }
299 free(dm_function);
300 free(stack_strings); // malloc()ed by backtrace_symbols
301 return retval;
302 }
303#endif
304}
305
306#ifdef __GNUC__
307
308void *operator new(size_t nbytes,const char* file, int line, const char* func) throw(std::bad_alloc) {
309
310 // to avoid allocations of 0 bytes if someone screws up
311 // allocation with 0 byte size are undefined behavior, so we are
312 // free to handle it this way
313 if(!nbytes) {
314 nbytes = 1;
315 }
316
317 // get the size of the entry, including alignment
318 static const size_t entrySpace = Memory::doAlign(sizeof(Memory::entry_t));
319
320 void *res;
321 if(!(res=malloc(entrySpace + nbytes))){
322 // new must throw, when space is low
323 throw std::bad_alloc();
324 }
325
326 // build the entry in front of the space
327 Memory::entry_t *entry = (Memory::entry_t*) res;
328 memset(res,0,entrySpace);
329 entry->info.nbytes = nbytes;
330 entry->info.isUsed = true;
331 strncpy(entry->info.file,file,Memory::entry_t::info_t::length);
332 entry->info.file[Memory::entry_t::info_t::length] = '\0';
333 entry->info.line=line;
334 strncpy(entry->info.function,func,Memory::entry_t::info_t::length2);
335 entry->info.function[Memory::entry_t::info_t::length2] = '\0';
336 // the space starts behind the info
337 entry->info.location = (char*)res + entrySpace;
338
339 // mark the block as not in the list (will be changed by addEntry)
340 entry->isIgnored = true;
341 Memory::addEntry(entry);
342
343 // get the checksum...
344 entry->checksum = Memory::calcChecksum(&entry->info);
345
346 // ok, space is prepared... the user can have it.
347 // the rest (constructor, deleting when something is thrown etc)
348 // is handled automatically
349 return entry->info.location;
350}
351
352#else
353
354void *operator new(size_t nbytes,const char* file, int line) throw(std::bad_alloc) {
355
356 // to avoid allocations of 0 bytes if someone screws up
357 // allocation with 0 byte size are undefined behavior, so we are
358 // free to handle it this way
359 if(!nbytes) {
360 nbytes = 1;
361 }
362
363 // get the size of the entry, including alignment
364 static const size_t entrySpace = Memory::doAlign(sizeof(Memory::entry_t));
365
366 void *res;
367 if(!(res=malloc(entrySpace + nbytes))){
368 // new must throw, when space is low
369 throw std::bad_alloc();
370 }
371
372 // build the entry in front of the space
373 Memory::entry_t *entry = (Memory::entry_t*) res;
374 memset(res,0,entrySpace);
375 entry->info.nbytes = nbytes;
376 entry->info.isUsed = true;
377 strncpy(entry->info.file,file,Memory::entry_t::info_t::length);
378 entry->info.file[Memory::entry_t::info_t::length] = '\0';
379 entry->info.line=line;
380 // the space starts behind the info
381 entry->info.location = (char*)res + entrySpace;
382
383 // mark the block as not in the list (will be changed by addEntry)
384 entry->isIgnored = true;
385 Memory::addEntry(entry);
386
387 // get the checksum...
388 entry->checksum = Memory::calcChecksum(&entry->info);
389 // this will be set to true, when the block is removed from
390 // the list for any reason
391 entry->isIgnored = false;
392
393 // ok, space is prepared... the user can have it.
394 // the rest (constructor, deleting when something is thrown etc)
395 // is handled automatically
396 return entry->info.location;
397}
398
399#endif
400
401void *operator new(size_t nbytes) throw(std::bad_alloc) {
402 // Just forward to the other operator, when we do not know from
403 // where the allocation came
404#ifdef __GNUC__
405 // this might throw bad_alloc
406 char *caller = Memory::getCaller();
407 void* retval = 0;
408
409 // if this throws, we have to clean up the caller anyway
410 try{
411 retval = operator new(nbytes,"Unknown",0,caller);
412 }
413 catch(...)
414 {
415 free(caller); // malloc()ed by Memory::getCaller();
416 throw;
417 }
418 free(caller); // malloc()ed by Memory::getCaller();
419 return retval;
420#else
421 return operator new(nbytes,"Unknown",0);
422#endif
423}
424
425#ifdef __GNUC__
426
427void *operator new[] (size_t nbytes,const char* file, int line, const char* func) throw(std::bad_alloc) {
428 // The difference between new and new[] is just for compiler bookkeeping.
429 return operator new(nbytes,file,line,func);
430}
431
432#else
433
434void *operator new[] (size_t nbytes,const char* file, int line) throw(std::bad_alloc) {
435 // The difference between new and new[] is just for compiler bookkeeping.
436 return operator new(nbytes,file,line);
437}
438
439#endif
440
441void *operator new[] (size_t nbytes) throw(std::bad_alloc) {
442 // Forward again
443#ifdef __GNUC__
444 // this might throw bad_alloc
445 char *caller = Memory::getCaller();
446 void *retval=0;
447
448 // if this throws, we have to clean up the caller anyway
449 try{
450 retval = operator new[] (nbytes,"Unknown",0,caller);
451 }
452 catch(...)
453 {
454 free(caller); // malloc()ed by Memory::getCaller();
455 throw;
456 }
457 free(caller); // malloc()ed by Memory::getCaller();
458 return retval;
459#else
460 return operator new[] (nbytes,"Unknown",0);
461#endif
462}
463
464void operator delete(void *ptr) throw() {
465 if(!ptr){
466 cerr << "Warning: Deleting NULL pointer" << endl;
467 return;
468 }
469
470 // get the size for the entry, including alignment
471 static const size_t entrySpace = Memory::doAlign(sizeof(Memory::entry_t));
472
473 // get the position for the entry from the pointer the user gave us
474 Memory::entry_t *entry = (Memory::entry_t*)((char*)ptr-entrySpace);
475
476 // let's see if the checksum is still matching
477 if(Memory::calcChecksum(&entry->info)!=entry->checksum){
478 cerr << "Possible memory corruption detected!" << endl;
479 cerr << "Trying to recover allocation information..." << endl;
480 cerr << "Memory was allocated at " << entry->info.file << ":" << entry->info.line << endl;
481 terminate();
482 }
483
484 // this will destroy the checksum, so double deletes are caught
485 entry->info.isUsed = false;
486 Memory::deleteEntry(entry);
487
488 // delete the space reserved by malloc
489 free((char*)ptr-entrySpace);
490}
491
492// operator that is called when the constructor throws
493// do not call manually
494void operator delete(void *ptr,const char*, int) throw() {
495 operator delete(ptr);
496}
497
498void operator delete[](void *ptr){
499 // again difference between delete and delete[] is just in compiler bookkeeping
500 operator delete(ptr);
501}
502
503// and another operator that can be called when a constructor throws
504void operator delete[](void *ptr,const char*, int) throw(){
505 operator delete(ptr);
506}
507#endif
Note: See TracBrowser for help on using the repository browser.