source: src/Helpers/MemDebug.cpp@ 6d6b54

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

Made the memory-tracker output the function from which it was called.

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