source: src/Helpers/MemDebug.cpp@ ad7270

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

Reduced initial verbosity of code.

FIX:

  • initUI() - no more admonishing of missing bond length table.

The following messages have been placed in comments:

  • Registering of Action/Options.
  • Looking for KnownTypes.
  • Stating MenuNames, Separators and submenu creations.
  • No more listing of parsed elements.
  • No more listing of thermostats.

The following messages have been added:

  • prepended scanning arguments in CommandLineParser::scan...().
  • prepended running arguments in CommandLineWindow::display().
  • additional new line after ProgamHeader().
  • correct CommandLine UI setting to occur before arguments are parsed.
  • changed verbosity levels of CommandLineParser::scanforSequenceOfArguments(), also all use DoLog(), i.e. global verbosity makes them be listed again.

NOTE:

  • MemDebug: as delete NULL is safe, we do not warn when this occurs. Also, in Qt this happens a lot.

TESTFIX:

  • setting global verbosity to 1, messed with Standard_Options/1. There we set verbosity to 9 to avoid this in the future.
  • 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.