source: src/Helpers/MemDebug.cpp@ 88104f

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

Made the memory debugger thread safe

  • Property mode set to 100644
File size: 7.5 KB
RevLine 
[632bc3]1/*
2 * MemDebug.cpp
3 *
4 * Created on: Apr 28, 2010
5 * Author: crueger
6 */
7
8#include <iostream>
9#include <cstdlib>
[28c351]10#include <cstring>
[f2f4ae]11#include <boost/thread.hpp>
[632bc3]12
13using namespace std;
14
[5702280]15#ifndef NDBEGUG
16#ifndef NO_MEMDEBUG
17
[632bc3]18namespace Memory {
[d79639]19
20 // This struct is added before each memory chunk
21 // and contains tracking information. Anything used
22 // to track memory cannot use any dynamic memory, so
23 // we have to resort to classic C-idioms here.
24 // This struct also contains pointers to the next
25 // an previous chunks to allow fast traversion of
26 // all allocated memory blocks
[632bc3]27 struct entry_t {
[d79639]28 // we seperate the tracking info from the rest
29 // A checksum will be calculated for this part of
30 // the struct, so the information in here should
31 // not change during the lifetime of the memory
[632bc3]32 struct info_t {
[d79639]33 enum {length = 64};
[1f7864]34 char file[length+1];
[632bc3]35 int line;
36 size_t nbytes;
37 bool isUsed;
38 void *location;
39 } info;
40 bool isIgnored;
41 char checksum;
42 entry_t *prev;
43 entry_t *next;
44 };
45
[f2f4ae]46 boost::mutex memorylock;
47
[d79639]48 // start and end of the doubly-linked list
[632bc3]49 entry_t *begin=0;
50 entry_t *end=0;
51
[d79639]52 // current amount of allocated memory
[632bc3]53 size_t state = 0;
[d79639]54 // maximum amount of allocated memory
[632bc3]55 size_t max = 0;
[d79639]56 // number of allocations that have been done so far
57 unsigned int allocs = 0;
58
[632bc3]59
[d79639]60 // this sets the alignment of the returned memory block
61 // malloc guarantees an alignment at the 8 byte border,
62 // so we just do the same
[632bc3]63 const int alignment = 8;
64
[d79639]65 // calculates a simple checksum for the info block
66 // the checksum is used to find memory corruptions
[632bc3]67 inline char calcChecksum(entry_t::info_t *info){
68 char *buffer = (char*)info;
69 char checksum =0;
70 for(size_t i=0;i<sizeof(entry_t::info_t);i++){
71 checksum+=buffer[i];
72 }
73 return checksum;
74 }
75
[d79639]76 // gets the next alignet point which is greater than nbytes
77 // this function is only called a fixed number of times, so
78 // there is no need to optimize
[632bc3]79 inline size_t doAlign(size_t nbytes){
80 int nonaligned = nbytes % alignment;
81 if(nonaligned) {
82 return(nbytes - nonaligned + alignment);
83 }
84 else{
85 return nbytes;
86 }
87 }
88
[d79639]89 // Output some state information
[632bc3]90 void getState(){
91 cout << "Maximum allocated Memory: " << max << " bytes" << endl;
92 cout << "Currently allocated Memory: " << state <<" bytes" << endl;
[d79639]93 cout << allocs << " allocated chunks total" << endl;
[632bc3]94
[d79639]95 // simple traversal of the chunk list
[632bc3]96 for(entry_t *pos=begin;pos;pos=pos->next){
97 cout << "\nChunk of " << pos->info.nbytes << " bytes" << " still available" << endl;
98 cout << "Chunk reserved at: " << pos->info.file << ":" << pos->info.line << endl;
99 }
100 }
101
[d79639]102 // Deletes an entry from the linked list
[632bc3]103 void deleteEntry(entry_t *entry){
104 if(entry->isIgnored)
105 return;
[d79639]106
[632bc3]107 if(entry->prev){
108 entry->prev->next = entry->next;
109 }
110 else{
[d79639]111 // this node was the beginning of the list
[632bc3]112 begin = entry->next;
113 }
114
115 if(entry->next){
116 entry->next->prev = entry->prev;
117 }
118 else{
[d79639]119 // this node was the end of the list
[632bc3]120 end = entry->prev;
121 }
122 entry->isIgnored = true;
123 Memory::state -= entry->info.nbytes;
124 }
125
126 void _ignore(void *ptr){
[d79639]127 // just deletes the node from the list, but leaves the info intact
[632bc3]128 static const size_t entrySpace = Memory::doAlign(sizeof(Memory::entry_t));
129 entry_t *entry = (Memory::entry_t*)((char*)ptr-entrySpace);
130 deleteEntry(entry);
131 }
132}
133
134void *operator new(size_t nbytes,const char* file, int line) throw(std::bad_alloc) {
135
[f2f4ae]136 // we need to lock, so that no one changes the linked list while we are here
137 boost::mutex::scoped_lock guard(Memory::memorylock);
138
[d79639]139 // to avoid allocations of 0 bytes if someone screws up
140 // allocation with 0 byte size are undefined behavior, so we are
141 // free to handle it this way
[632bc3]142 if(!nbytes) {
143 nbytes = 1;
144 }
145
[d79639]146 // get the size of the entry, including alignment
[632bc3]147 static const size_t entrySpace = Memory::doAlign(sizeof(Memory::entry_t));
148
149 void *res;
150 if(!(res=malloc(entrySpace + nbytes))){
[d79639]151 // new must throw, when space is low
[632bc3]152 throw std::bad_alloc();
153 }
154
[d79639]155 // we got the space, so update the global info
[632bc3]156 Memory::state += nbytes;
157 if(Memory::state>Memory::max){
158 Memory::max = Memory::state;
159 }
[d79639]160 Memory::allocs++;
[632bc3]161
[d79639]162 // build the entry in front of the space
[632bc3]163 Memory::entry_t *entry = (Memory::entry_t*) res;
[68f03d]164 memset(res,0,entrySpace);
[632bc3]165 entry->info.nbytes = nbytes;
166 entry->info.isUsed = true;
[d79639]167 strncpy(entry->info.file,file,Memory::entry_t::info_t::length);
[1f7864]168 entry->info.file[Memory::entry_t::info_t::length] = '\0';
[632bc3]169 entry->info.line=line;
[d79639]170 // the space starts behind the info
[632bc3]171 entry->info.location = (char*)res + entrySpace;
172
[d79639]173 // add the entry at the end of the list
174 entry->next=0; // the created block is last in the list
175 entry->prev=Memory::end; // the created block is last in the list
[632bc3]176 if(!Memory::begin){
[d79639]177 // the list was empty... start a new one
[632bc3]178 Memory::begin=entry;
179 }
180 else {
[d79639]181 // other blocks present... we can add to the last one
[632bc3]182 Memory::end->next=entry;
183 }
184 Memory::end=entry;
185
[d79639]186 // get the checksum...
[632bc3]187 entry->checksum = Memory::calcChecksum(&entry->info);
[d79639]188 // this will be set to true, when the block is removed from
189 // the list for any reason
[632bc3]190 entry->isIgnored = false;
191
[d79639]192 // ok, space is prepared... the user can have it.
193 // the rest (constructor, deleting when something is thrown etc)
194 // is handled automatically
[632bc3]195 return entry->info.location;
196}
197
198void *operator new(size_t nbytes) throw(std::bad_alloc) {
[d79639]199 // Just forward to the other operator, when we do not know from
200 // where the allocation came
[632bc3]201 return operator new(nbytes,"Unknown",0);
202}
203
204void *operator new[] (size_t nbytes,const char* file, int line) throw(std::bad_alloc) {
[d79639]205 // The difference between new and new[] is just for compiler bookkeeping.
[632bc3]206 return operator new(nbytes,file,line);
207}
208
209void *operator new[] (size_t nbytes) throw(std::bad_alloc) {
[d79639]210 // Forward again
[632bc3]211 return operator new[] (nbytes,"Unknown",0);
212}
213
214void operator delete(void *ptr) throw() {
[93dfc7b]215 if(!ptr){
216 cerr << "Warning: Deleting NULL pointer" << endl;
217 return;
218 }
[f2f4ae]219
220 // we need to lock, so the linked list does not changed while we are in here
221 boost::mutex::scoped_lock guard(Memory::memorylock);
222
[d79639]223 // get the size for the entry, including alignment
[632bc3]224 static const size_t entrySpace = Memory::doAlign(sizeof(Memory::entry_t));
225
[d79639]226 // get the position for the entry from the pointer the user gave us
[632bc3]227 Memory::entry_t *entry = (Memory::entry_t*)((char*)ptr-entrySpace);
228
[d79639]229 // let's see if the checksum is still matching
[632bc3]230 if(Memory::calcChecksum(&entry->info)!=entry->checksum){
[93dfc7b]231 cerr << "Possible memory corruption detected!" << endl;
232 cerr << "Trying to recover allocation information..." << endl;
233 cerr << "Memory was allocated at " << entry->info.file << ":" << entry->info.line << endl;
[632bc3]234 terminate();
235 }
236
[d79639]237 // this will destroy the checksum, so double deletes are caught
[632bc3]238 entry->info.isUsed = false;
239 Memory::deleteEntry(entry);
240
[d79639]241 // delete the space reserved by malloc
[632bc3]242 free((char*)ptr-entrySpace);
243}
244
[d79639]245// operator that is called when the constructor throws
246// do not call manually
[632bc3]247void operator delete(void *ptr,const char*, int) throw() {
248 operator delete(ptr);
249}
250
251void operator delete[](void *ptr){
[d79639]252 // again difference between delete and delete[] is just in compiler bookkeeping
[632bc3]253 operator delete(ptr);
254}
255
[d79639]256// and another operator that can be called when a constructor throws
[632bc3]257void operator delete[](void *ptr,const char*, int) throw(){
258 operator delete(ptr);
259}
[5702280]260#endif
261#endif
Note: See TracBrowser for help on using the repository browser.