0024947: Redesign OCCT legacy type system -- automatic
[occt.git] / src / NCollection / NCollection_BaseAllocator.cxx
1 // Created on: 2002-04-12
2 // Created by: Alexander KARTOMIN (akm)
3 // Copyright (c) 2002-2014 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 // Purpose:     Implementation of the BaseAllocator class
17
18 #include <NCollection_BaseAllocator.hxx>
19 #include <NCollection_IncAllocator.hxx>
20 #include <NCollection_DataMap.hxx>
21 #include <NCollection_Map.hxx>
22 #include <NCollection_List.hxx>
23 #include <Standard_Mutex.hxx>
24 #include <fstream>
25 #include <iomanip>
26
27
28 //=======================================================================
29 //function : Allocate
30 //purpose  : Standard allocation
31 //=======================================================================
32
33 void* NCollection_BaseAllocator::Allocate(const size_t size)
34
35   return Standard::Allocate(size);
36 }
37
38 //=======================================================================
39 //function : Free
40 //purpose  : Standard deallocation
41 //=======================================================================
42
43 void  NCollection_BaseAllocator::Free(void *anAddress)
44
45   if (anAddress) Standard::Free(anAddress); 
46 }
47
48 //=======================================================================
49 //function : CommonBaseAllocator
50 //purpose  : Creates the only one BaseAllocator
51 //=======================================================================
52
53 const Handle(NCollection_BaseAllocator)& 
54        NCollection_BaseAllocator::CommonBaseAllocator(void)
55
56   static Handle(NCollection_BaseAllocator) pAllocator = 
57     new NCollection_BaseAllocator;
58   return pAllocator;
59 }
60
61 // global variable to ensure that allocator will be created during loading the library
62 static Handle(NCollection_BaseAllocator) theAllocInit = 
63   NCollection_BaseAllocator::CommonBaseAllocator();
64
65 //=======================================================================
66 /**
67  * Structure for collecting statistics about blocks of one size
68  */
69 //=======================================================================
70 struct StorageInfo
71 {
72   Standard_Size roundSize;
73   int nbAlloc;
74   int nbFree;
75   StorageInfo()
76     : roundSize(0), nbAlloc(0), nbFree(0) {}
77   StorageInfo(Standard_Size theSize)
78     : roundSize(theSize), nbAlloc(0), nbFree(0) {}
79 };
80
81 //=======================================================================
82 /**
83  * Static data map (block_size -> StorageInfo)
84  */
85 //=======================================================================
86 static NCollection_DataMap<Standard_Size, StorageInfo>& StorageMap()
87 {
88   static NCollection_IncAllocator TheAlloc;
89   static NCollection_DataMap<Standard_Size, StorageInfo>
90     TheMap (1, & TheAlloc);
91   return TheMap;
92 }
93
94 //=======================================================================
95 /**
96  * Static data map (address -> AllocationID)
97  */
98 //=======================================================================
99 static NCollection_DataMap<Standard_Address, Standard_Size>& StorageIDMap()
100 {
101   static NCollection_IncAllocator TheAlloc;
102   static NCollection_DataMap<Standard_Address, Standard_Size>
103     TheMap (1, & TheAlloc);
104   return TheMap;
105 }
106
107 //=======================================================================
108 /**
109  * Static map (AllocationID)
110  */
111 //=======================================================================
112 static NCollection_Map<Standard_Size>& StorageIDSet()
113 {
114   static NCollection_IncAllocator TheAlloc;
115   static NCollection_Map<Standard_Size> TheMap (1, & TheAlloc);
116   return TheMap;
117 }
118
119 //=======================================================================
120 /**
121  * Exported value to set the block size for which it is required 
122  * collecting alive allocation IDs.
123  * The method NCollection_BaseAllocator::PrintMemUsageStatistics
124  * dumps all alive IDs into the file alive.d in the current directory.
125  */
126 //=======================================================================
127 Standard_EXPORT Standard_Size& StandardCallBack_CatchSize()
128 {
129   static Standard_Size Value = 0;
130   return Value;
131 }
132
133 //=======================================================================
134 /**
135  * Exported value to set the allocation ID for which it is required 
136  * to set a breakpoint on the moment of allocation or freeing.
137  * See the method NCollection_BaseAllocator::StandardCallBack
138  * where the value StandardCallBack_CatchID() is compared to the current ID.
139  * There you can place a break point at the stub assignment statement "a =".
140  */
141 //=======================================================================
142 Standard_EXPORT Standard_Size& StandardCallBack_CatchID()
143 {
144   static Standard_Size Value = 0;
145   return Value;
146 }
147
148 //=======================================================================
149 /**
150  * Static value of the current allocation ID. It provides unique
151  * numbering of allocation events.
152  */
153 //=======================================================================
154 static Standard_Size CurrentID = 0;
155
156 //=======================================================================
157 /**
158  * Exported function to reset the callback system to the initial state
159  */
160 //=======================================================================
161 Standard_EXPORT void StandardCallBack_Reset()
162 {
163   StorageMap().Clear();
164   StorageIDMap().Clear();
165   StorageIDSet().Clear();
166   CurrentID = 0;
167   StandardCallBack_CatchSize() = 0;
168   StandardCallBack_CatchID() = 0;
169 }
170
171 namespace {
172   // dummy function for break point
173   inline void place_for_break_point () {}
174 };
175
176 //=======================================================================
177 //function : StandardCallBack
178 //purpose  : Callback function to register alloc/free calls
179 //=======================================================================
180
181 void NCollection_BaseAllocator::StandardCallBack
182                     (const Standard_Boolean theIsAlloc,
183                      const Standard_Address theStorage,
184                      const Standard_Size theRoundSize,
185                      const Standard_Size /*theSize*/)
186 {
187   static Standard_Mutex aMutex;
188   aMutex.Lock();
189   // statistics by storage size
190   NCollection_DataMap<Standard_Size, StorageInfo>& aStMap = StorageMap();
191   if (!aStMap.IsBound(theRoundSize))
192   {
193     StorageInfo aEmpty(theRoundSize);
194     aStMap.Bind(theRoundSize, aEmpty);
195   }
196   StorageInfo& aInfo = aStMap(theRoundSize);
197   if (theIsAlloc)
198     aInfo.nbAlloc++;
199   else
200     aInfo.nbFree++;
201
202   if (theRoundSize == StandardCallBack_CatchSize())
203   {
204     // statistics by alive objects
205     NCollection_DataMap<Standard_Address, Standard_Size>& aStIDMap = StorageIDMap();
206     NCollection_Map<Standard_Size>& aStIDSet = StorageIDSet();
207     if (theIsAlloc)
208     {
209       aStIDMap.Bind(theStorage, ++CurrentID);
210       aStIDSet.Add(CurrentID);
211       if (CurrentID == StandardCallBack_CatchID())
212       {
213         // Place for break point for allocation of investigated ID
214         place_for_break_point();
215       }
216     }
217     else
218     {
219       if (aStIDMap.IsBound(theStorage))
220       {
221         Standard_Size anID = aStIDMap(theStorage);
222         aStIDSet.Remove(anID);
223         if (anID == StandardCallBack_CatchID())
224         {
225           // Place for break point for freeing of investigated ID
226           place_for_break_point();
227         }
228       }
229     }
230   }
231
232   aMutex.Unlock();
233 }
234
235 //=======================================================================
236 //function : PrintMemUsageStatistics
237 //purpose  : Prints memory usage statistics cumulated by StandardCallBack
238 //=======================================================================
239
240 void NCollection_BaseAllocator::PrintMemUsageStatistics()
241 {
242   // sort by roundsize
243   NCollection_List<StorageInfo> aColl;
244   NCollection_List<StorageInfo>::Iterator itLst;
245   NCollection_DataMap<Standard_Size, StorageInfo>::Iterator itMap(StorageMap());
246   for (; itMap.More(); itMap.Next())
247   {
248     for (itLst.Init(aColl); itLst.More(); itLst.Next())
249       if (itMap.Value().roundSize < itLst.Value().roundSize)
250         break;
251     if (itLst.More())
252       aColl.InsertBefore(itMap.Value(), itLst);
253     else
254       aColl.Append(itMap.Value());
255   }
256   Standard_Size aTotAlloc = 0;
257   Standard_Size aTotLeft = 0;
258
259   // print
260   std::ofstream aFileOut ("memstat.d", std::ios_base::trunc | std::ios_base::out);
261   if (!aFileOut.is_open())
262   {
263     std::cout << "failure writing file memstat.d" << std::endl;
264     return;
265   }
266   aFileOut.imbue (std::locale ("C"));
267
268   // header
269   aFileOut << std::setw(20) << "BlockSize"   << ' '
270            << std::setw(12) << "NbAllocated" << ' '
271            << std::setw(12) << "NbLeft"      << ' '
272            << std::setw(20) << "Allocated"   << ' '
273            << std::setw(20) << "Left"        << '\n';
274
275   // body
276   for (itLst.Init(aColl); itLst.More(); itLst.Next())
277   {
278     const StorageInfo& aInfo = itLst.Value();
279     Standard_Integer nbLeft = aInfo.nbAlloc - aInfo.nbFree;
280     Standard_Size aSizeAlloc = aInfo.nbAlloc * aInfo.roundSize;
281     Standard_Size aSizeLeft = nbLeft * aInfo.roundSize;
282
283     aFileOut << std::setw(20) << aInfo.roundSize << ' '
284              << std::setw(12) << aInfo.nbAlloc   << ' '
285              << std::setw(12) << nbLeft          << ' '
286              << std::setw(20) << aSizeAlloc      << ' '
287              << std::setw(20) << aSizeLeft       << '\n';
288
289     aTotAlloc += aSizeAlloc;
290     aTotLeft  += aSizeLeft;
291   }
292
293   // footer
294   aFileOut << std::setw(20) << "Total:"  << ' '
295            << std::setw(12) << ""        << ' '
296            << std::setw(12) << ""        << ' '
297            << std::setw(20) << aTotAlloc << ' '
298            << std::setw(20) << aTotLeft  << '\n';
299
300   if (!StorageIDSet().IsEmpty())
301   {
302     aFileOut << "Alive allocation numbers of size=" << StandardCallBack_CatchSize() << '\n';
303     for (NCollection_Map<Standard_Size>::Iterator itMap1(StorageIDSet()); itMap1.More(); itMap1.Next())
304     {
305       aFileOut << itMap1.Key() << '\n';
306     }
307   }
308   aFileOut.close();
309 }