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