OCC22143 Optimization of memory usage and developing tools for catching memory leaks...
[occt.git] / src / NCollection / NCollection_BaseAllocator.cxx
1 // File:        NCollection_BaseAllocator.cxx
2 // Created:     Fri Apr 12 12:53:28 2002
3 // Author:      Alexander KARTOMIN (akm)
4 //              <a-kartomin@opencascade.com>
5 // Purpose:     Implementation of the BaseAllocator class
6
7 #include <NCollection_BaseAllocator.hxx>
8 #include <NCollection_DataMap.hxx>
9 #include <NCollection_Map.hxx>
10 #include <NCollection_List.hxx>
11 #include <Standard_Mutex.hxx>
12
13 IMPLEMENT_STANDARD_HANDLE(NCollection_BaseAllocator,MMgt_TShared)
14 IMPLEMENT_STANDARD_RTTIEXT(NCollection_BaseAllocator,MMgt_TShared)
15
16 //=======================================================================
17 //function : Allocate
18 //purpose  : Standard allocation
19 //=======================================================================
20
21 void* NCollection_BaseAllocator::Allocate(const size_t size)
22
23   return Standard::Allocate(size);
24 }
25
26 //=======================================================================
27 //function : Free
28 //purpose  : Standard deallocation
29 //=======================================================================
30
31 void  NCollection_BaseAllocator::Free(void *anAddress)
32
33   if (anAddress) Standard::Free((Standard_Address&)anAddress); 
34 }
35
36 //=======================================================================
37 //function : CommonBaseAllocator
38 //purpose  : Creates the only one BaseAllocator
39 //=======================================================================
40
41 const Handle(NCollection_BaseAllocator)& 
42        NCollection_BaseAllocator::CommonBaseAllocator(void)
43
44   static Handle(NCollection_BaseAllocator) pAllocator = 
45     new NCollection_BaseAllocator;
46   return pAllocator;
47 }
48
49 // global variable to ensure that allocator will be created during loading the library
50 static Handle(NCollection_BaseAllocator) theAllocInit = 
51   NCollection_BaseAllocator::CommonBaseAllocator();
52
53 //=======================================================================
54 /**
55  * Structure for collecting statistics about blocks of one size
56  */
57 //=======================================================================
58 struct StorageInfo
59 {
60   Standard_Size roundSize;
61   int nbAlloc;
62   int nbFree;
63   StorageInfo()
64     : roundSize(0), nbAlloc(0), nbFree(0) {}
65   StorageInfo(Standard_Size theSize)
66     : roundSize(theSize), nbAlloc(0), nbFree(0) {}
67 };
68
69 //=======================================================================
70 /**
71  * Static data map (block_size -> StorageInfo)
72  */
73 //=======================================================================
74 static NCollection_DataMap<Standard_Size, StorageInfo>& StorageMap()
75 {
76   static NCollection_IncAllocator TheAlloc;
77   static NCollection_DataMap<Standard_Size, StorageInfo>
78     TheMap (1, & TheAlloc);
79   return TheMap;
80 }
81
82 //=======================================================================
83 /**
84  * Static data map (address -> AllocationID)
85  */
86 //=======================================================================
87 static NCollection_DataMap<Standard_Address, Standard_Size>& StorageIDMap()
88 {
89   static NCollection_IncAllocator TheAlloc;
90   static NCollection_DataMap<Standard_Address, Standard_Size>
91     TheMap (1, & TheAlloc);
92   return TheMap;
93 }
94
95 //=======================================================================
96 /**
97  * Static map (AllocationID)
98  */
99 //=======================================================================
100 static NCollection_Map<Standard_Size>& StorageIDSet()
101 {
102   static NCollection_IncAllocator TheAlloc;
103   static NCollection_Map<Standard_Size> TheMap (1, & TheAlloc);
104   return TheMap;
105 }
106
107 //=======================================================================
108 /**
109  * Exported value to set the block size for which it is required 
110  * collecting alive allocation IDs.
111  * The method NCollection_BaseAllocator::PrintMemUsageStatistics
112  * dumps all alive IDs into the file alive.d in the current directory.
113  */
114 //=======================================================================
115 Standard_EXPORT Standard_Size& StandardCallBack_CatchSize()
116 {
117   static Standard_Size Value = 0;
118   return Value;
119 }
120
121 //=======================================================================
122 /**
123  * Exported value to set the allocation ID for which it is required 
124  * to set a breakpoint on the moment of allocation or freeing.
125  * See the method NCollection_BaseAllocator::StandardCallBack
126  * where the value StandardCallBack_CatchID() is compared to the current ID.
127  * There you can place a break point at the stub assignment statement "a =".
128  */
129 //=======================================================================
130 Standard_EXPORT Standard_Size& StandardCallBack_CatchID()
131 {
132   static Standard_Size Value = 0;
133   return Value;
134 }
135
136 //=======================================================================
137 /**
138  * Static value of the current allocation ID. It provides unique
139  * numbering of allocation events.
140  */
141 //=======================================================================
142 static Standard_Size CurrentID = 0;
143
144 //=======================================================================
145 /**
146  * Exported function to reset the callback system to the initial state
147  */
148 //=======================================================================
149 Standard_EXPORT void StandardCallBack_Reset()
150 {
151   StorageMap().Clear();
152   StorageIDMap().Clear();
153   StorageIDSet().Clear();
154   CurrentID = 0;
155   StandardCallBack_CatchSize() = 0;
156   StandardCallBack_CatchID() = 0;
157 }
158
159 //=======================================================================
160 //function : StandardCallBack
161 //purpose  : Callback function to register alloc/free calls
162 //=======================================================================
163
164 void NCollection_BaseAllocator::StandardCallBack
165                     (const Standard_Boolean theIsAlloc,
166                      const Standard_Address theStorage,
167                      const Standard_Size theRoundSize,
168                      const Standard_Size /*theSize*/)
169 {
170   static Standard_Mutex aMutex;
171   Standard_Boolean isReentrant = Standard::IsReentrant();
172   if (isReentrant)
173     aMutex.Lock();
174   // statistics by storage size
175   NCollection_DataMap<Standard_Size, StorageInfo>& aStMap = StorageMap();
176   if (!aStMap.IsBound(theRoundSize))
177   {
178     StorageInfo aEmpty(theRoundSize);
179     aStMap.Bind(theRoundSize, aEmpty);
180   }
181   StorageInfo& aInfo = aStMap(theRoundSize);
182   if (theIsAlloc)
183     aInfo.nbAlloc++;
184   else
185     aInfo.nbFree++;
186
187   if (theRoundSize == StandardCallBack_CatchSize())
188   {
189     // statistics by alive objects
190     NCollection_DataMap<Standard_Address, Standard_Size>& aStIDMap = StorageIDMap();
191     NCollection_Map<Standard_Size>& aStIDSet = StorageIDSet();
192     int a;
193     if (theIsAlloc)
194     {
195       aStIDMap.Bind(theStorage, ++CurrentID);
196       aStIDSet.Add(CurrentID);
197       if (CurrentID == StandardCallBack_CatchID())
198       {
199         // Place for break point for allocation of investigated ID
200         a = 1;
201       }
202     }
203     else
204     {
205       if (aStIDMap.IsBound(theStorage))
206       {
207         Standard_Size anID = aStIDMap(theStorage);
208         aStIDSet.Remove(anID);
209         if (anID == StandardCallBack_CatchID())
210         {
211           // Place for break point for freeing of investigated ID
212           a = 0;
213         }
214       }
215     }
216   }
217
218   if (isReentrant)
219     aMutex.Unlock();
220 }
221
222 //=======================================================================
223 //function : PrintMemUsageStatistics
224 //purpose  : Prints memory usage statistics cumulated by StandardCallBack
225 //=======================================================================
226
227 void NCollection_BaseAllocator::PrintMemUsageStatistics()
228 {
229   // sort by roundsize
230   NCollection_List<StorageInfo> aColl;
231   NCollection_List<StorageInfo>::Iterator itLst;
232   NCollection_DataMap<Standard_Size, StorageInfo>::Iterator itMap(StorageMap());
233   for (; itMap.More(); itMap.Next())
234   {
235     for (itLst.Init(aColl); itLst.More(); itLst.Next())
236       if (itMap.Value().roundSize < itLst.Value().roundSize)
237         break;
238     if (itLst.More())
239       aColl.InsertBefore(itMap.Value(), itLst);
240     else
241       aColl.Append(itMap.Value());
242   }
243   Standard_Size aTotAlloc = 0;
244   Standard_Size aTotLeft = 0;
245   // print
246   FILE * ff = fopen("memstat.d", "wt");
247   if (ff == NULL)
248   {
249     cout << "failure writing file memstat.d" << endl;
250     return;
251   }
252   fprintf(ff, "%12s %12s %12s %12s %12s\n",
253           "BlockSize", "NbAllocated", "NbLeft", "Allocated", "Left");
254   for (itLst.Init(aColl); itLst.More(); itLst.Next())
255   {
256     const StorageInfo& aInfo = itLst.Value();
257     Standard_Integer nbLeft = aInfo.nbAlloc - aInfo.nbFree;
258     Standard_Size aSizeAlloc = aInfo.nbAlloc * aInfo.roundSize;
259     Standard_Size aSizeLeft = nbLeft * aInfo.roundSize;
260     fprintf(ff, "%12d %12d %12d %12d %12d\n", aInfo.roundSize,
261             aInfo.nbAlloc, nbLeft, aSizeAlloc, aSizeLeft);
262     aTotAlloc += aSizeAlloc;
263     aTotLeft += aSizeLeft;
264   }
265   fprintf(ff, "%12s %12s %12s %12d %12d\n", "Total:", "", "",
266           aTotAlloc, aTotLeft);
267
268   if (!StorageIDSet().IsEmpty())
269   {
270     fprintf(ff, "Alive allocation numbers of size=%d\n", StandardCallBack_CatchSize());
271     NCollection_Map<Standard_Size>::Iterator itMap1(StorageIDSet());
272     for (; itMap1.More(); itMap1.Next())
273       fprintf(ff, "%d\n", itMap1.Key());
274   }
275   fclose(ff);
276 }