OCC22143 Optimization of memory usage and developing tools for catching memory leaks...
[occt.git] / src / NCollection / NCollection_BaseAllocator.cxx
index 3e9c209..1fae714 100755 (executable)
@@ -6,6 +6,7 @@
 
 #include <NCollection_BaseAllocator.hxx>
 #include <NCollection_DataMap.hxx>
+#include <NCollection_Map.hxx>
 #include <NCollection_List.hxx>
 #include <Standard_Mutex.hxx>
 
@@ -50,10 +51,10 @@ static Handle(NCollection_BaseAllocator) theAllocInit =
   NCollection_BaseAllocator::CommonBaseAllocator();
 
 //=======================================================================
-//function : StandardCallBack
-//purpose  : Callback function to register alloc/free calls
+/**
+ * Structure for collecting statistics about blocks of one size
+ */
 //=======================================================================
-
 struct StorageInfo
 {
   Standard_Size roundSize;
@@ -65,29 +66,157 @@ struct StorageInfo
     : roundSize(theSize), nbAlloc(0), nbFree(0) {}
 };
 
-static NCollection_DataMap<Standard_Size, StorageInfo> StorageMap;
+//=======================================================================
+/**
+ * Static data map (block_size -> StorageInfo)
+ */
+//=======================================================================
+static NCollection_DataMap<Standard_Size, StorageInfo>& StorageMap()
+{
+  static NCollection_IncAllocator TheAlloc;
+  static NCollection_DataMap<Standard_Size, StorageInfo>
+    TheMap (1, & TheAlloc);
+  return TheMap;
+}
+
+//=======================================================================
+/**
+ * Static data map (address -> AllocationID)
+ */
+//=======================================================================
+static NCollection_DataMap<Standard_Address, Standard_Size>& StorageIDMap()
+{
+  static NCollection_IncAllocator TheAlloc;
+  static NCollection_DataMap<Standard_Address, Standard_Size>
+    TheMap (1, & TheAlloc);
+  return TheMap;
+}
+
+//=======================================================================
+/**
+ * Static map (AllocationID)
+ */
+//=======================================================================
+static NCollection_Map<Standard_Size>& StorageIDSet()
+{
+  static NCollection_IncAllocator TheAlloc;
+  static NCollection_Map<Standard_Size> TheMap (1, & TheAlloc);
+  return TheMap;
+}
+
+//=======================================================================
+/**
+ * Exported value to set the block size for which it is required 
+ * collecting alive allocation IDs.
+ * The method NCollection_BaseAllocator::PrintMemUsageStatistics
+ * dumps all alive IDs into the file alive.d in the current directory.
+ */
+//=======================================================================
+Standard_EXPORT Standard_Size& StandardCallBack_CatchSize()
+{
+  static Standard_Size Value = 0;
+  return Value;
+}
+
+//=======================================================================
+/**
+ * Exported value to set the allocation ID for which it is required 
+ * to set a breakpoint on the moment of allocation or freeing.
+ * See the method NCollection_BaseAllocator::StandardCallBack
+ * where the value StandardCallBack_CatchID() is compared to the current ID.
+ * There you can place a break point at the stub assignment statement "a =".
+ */
+//=======================================================================
+Standard_EXPORT Standard_Size& StandardCallBack_CatchID()
+{
+  static Standard_Size Value = 0;
+  return Value;
+}
+
+//=======================================================================
+/**
+ * Static value of the current allocation ID. It provides unique
+ * numbering of allocation events.
+ */
+//=======================================================================
+static Standard_Size CurrentID = 0;
+
+//=======================================================================
+/**
+ * Exported function to reset the callback system to the initial state
+ */
+//=======================================================================
+Standard_EXPORT void StandardCallBack_Reset()
+{
+  StorageMap().Clear();
+  StorageIDMap().Clear();
+  StorageIDSet().Clear();
+  CurrentID = 0;
+  StandardCallBack_CatchSize() = 0;
+  StandardCallBack_CatchID() = 0;
+}
+
+//=======================================================================
+//function : StandardCallBack
+//purpose  : Callback function to register alloc/free calls
+//=======================================================================
 
 void NCollection_BaseAllocator::StandardCallBack
                     (const Standard_Boolean theIsAlloc,
-                     const Standard_Address /*theStorage*/,
+                     const Standard_Address theStorage,
                      const Standard_Size theRoundSize,
                      const Standard_Size /*theSize*/)
 {
-  static int aLock = 0;
-  if (aLock)
-    return;
-  aLock = 1;
-  if (!StorageMap.IsBound(theRoundSize))
+  static Standard_Mutex aMutex;
+  Standard_Boolean isReentrant = Standard::IsReentrant();
+  if (isReentrant)
+    aMutex.Lock();
+  // statistics by storage size
+  NCollection_DataMap<Standard_Size, StorageInfo>& aStMap = StorageMap();
+  if (!aStMap.IsBound(theRoundSize))
   {
     StorageInfo aEmpty(theRoundSize);
-    StorageMap.Bind(theRoundSize, aEmpty);
+    aStMap.Bind(theRoundSize, aEmpty);
   }
-  StorageInfo& aInfo = StorageMap(theRoundSize);
+  StorageInfo& aInfo = aStMap(theRoundSize);
   if (theIsAlloc)
     aInfo.nbAlloc++;
   else
     aInfo.nbFree++;
-  aLock = 0;
+
+  if (theRoundSize == StandardCallBack_CatchSize())
+  {
+    // statistics by alive objects
+    NCollection_DataMap<Standard_Address, Standard_Size>& aStIDMap = StorageIDMap();
+    NCollection_Map<Standard_Size>& aStIDSet = StorageIDSet();
+    int a;
+    if (theIsAlloc)
+    {
+      aStIDMap.Bind(theStorage, ++CurrentID);
+      aStIDSet.Add(CurrentID);
+      if (CurrentID == StandardCallBack_CatchID())
+      {
+        // Place for break point for allocation of investigated ID
+        a = 1;
+      }
+    }
+    else
+    {
+      if (aStIDMap.IsBound(theStorage))
+      {
+        Standard_Size anID = aStIDMap(theStorage);
+        aStIDSet.Remove(anID);
+        if (anID == StandardCallBack_CatchID())
+        {
+          // Place for break point for freeing of investigated ID
+          a = 0;
+        }
+      }
+    }
+  }
+
+  if (isReentrant)
+    aMutex.Unlock();
 }
 
 //=======================================================================
@@ -100,7 +229,7 @@ void NCollection_BaseAllocator::PrintMemUsageStatistics()
   // sort by roundsize
   NCollection_List<StorageInfo> aColl;
   NCollection_List<StorageInfo>::Iterator itLst;
-  NCollection_DataMap<Standard_Size, StorageInfo>::Iterator itMap(StorageMap);
+  NCollection_DataMap<Standard_Size, StorageInfo>::Iterator itMap(StorageMap());
   for (; itMap.More(); itMap.Next())
   {
     for (itLst.Init(aColl); itLst.More(); itLst.Next())
@@ -114,20 +243,34 @@ void NCollection_BaseAllocator::PrintMemUsageStatistics()
   Standard_Size aTotAlloc = 0;
   Standard_Size aTotLeft = 0;
   // print
-  printf("%12s %12s %12s %12s %12s\n",
-         "BlockSize", "NbAllocated", "NbLeft", "Allocated", "Left");
+  FILE * ff = fopen("memstat.d", "wt");
+  if (ff == NULL)
+  {
+    cout << "failure writing file memstat.d" << endl;
+    return;
+  }
+  fprintf(ff, "%12s %12s %12s %12s %12s\n",
+          "BlockSize", "NbAllocated", "NbLeft", "Allocated", "Left");
   for (itLst.Init(aColl); itLst.More(); itLst.Next())
   {
     const StorageInfo& aInfo = itLst.Value();
     Standard_Integer nbLeft = aInfo.nbAlloc - aInfo.nbFree;
     Standard_Size aSizeAlloc = aInfo.nbAlloc * aInfo.roundSize;
     Standard_Size aSizeLeft = nbLeft * aInfo.roundSize;
-    printf("%12d %12d %12d %12d %12d\n", aInfo.roundSize,
-           aInfo.nbAlloc, nbLeft, aSizeAlloc, aSizeLeft);
+    fprintf(ff, "%12d %12d %12d %12d %12d\n", aInfo.roundSize,
+            aInfo.nbAlloc, nbLeft, aSizeAlloc, aSizeLeft);
     aTotAlloc += aSizeAlloc;
     aTotLeft += aSizeLeft;
   }
-  printf("%12s %12s %12s %12d %12d\n", "Total:", "", "",
-         aTotAlloc, aTotLeft);
-  fflush(stdout);
+  fprintf(ff, "%12s %12s %12s %12d %12d\n", "Total:", "", "",
+          aTotAlloc, aTotLeft);
+
+  if (!StorageIDSet().IsEmpty())
+  {
+    fprintf(ff, "Alive allocation numbers of size=%d\n", StandardCallBack_CatchSize());
+    NCollection_Map<Standard_Size>::Iterator itMap1(StorageIDSet());
+    for (; itMap1.More(); itMap1.Next())
+      fprintf(ff, "%d\n", itMap1.Key());
+  }
+  fclose(ff);
 }