// Created on: 2002-04-12 // Created by: Alexander KARTOMIN (akm) // Copyright (c) 2002-2014 OPEN CASCADE SAS // // This file is part of Open CASCADE Technology software library. // // This library is free software; you can redistribute it and/or modify it under // the terms of the GNU Lesser General Public License version 2.1 as published // by the Free Software Foundation, with special exception defined in the file // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT // distribution for complete text of the license and disclaimer of any warranty. // // Alternatively, this file may be used under the terms of Open CASCADE // commercial license or contractual agreement. // Purpose: Implementation of the BaseAllocator class #include #include #include #include #include #include #include #include //======================================================================= //function : Allocate //purpose : Standard allocation //======================================================================= void* NCollection_BaseAllocator::Allocate(const size_t size) { return Standard::Allocate(size); } //======================================================================= //function : Free //purpose : Standard deallocation //======================================================================= void NCollection_BaseAllocator::Free(void *anAddress) { if (anAddress) Standard::Free(anAddress); } //======================================================================= //function : CommonBaseAllocator //purpose : Creates the only one BaseAllocator //======================================================================= const Handle(NCollection_BaseAllocator)& NCollection_BaseAllocator::CommonBaseAllocator(void) { static Handle(NCollection_BaseAllocator) pAllocator = new NCollection_BaseAllocator; return pAllocator; } // global variable to ensure that allocator will be created during loading the library static Handle(NCollection_BaseAllocator) theAllocInit = NCollection_BaseAllocator::CommonBaseAllocator(); //======================================================================= /** * Structure for collecting statistics about blocks of one size */ //======================================================================= struct StorageInfo { Standard_Size roundSize; int nbAlloc; int nbFree; StorageInfo() : roundSize(0), nbAlloc(0), nbFree(0) {} StorageInfo(Standard_Size theSize) : roundSize(theSize), nbAlloc(0), nbFree(0) {} }; //======================================================================= /** * Static data map (block_size -> StorageInfo) */ //======================================================================= static NCollection_DataMap& StorageMap() { static NCollection_IncAllocator TheAlloc; static NCollection_DataMap TheMap (1, & TheAlloc); return TheMap; } //======================================================================= /** * Static data map (address -> AllocationID) */ //======================================================================= static NCollection_DataMap& StorageIDMap() { static NCollection_IncAllocator TheAlloc; static NCollection_DataMap TheMap (1, & TheAlloc); return TheMap; } //======================================================================= /** * Static map (AllocationID) */ //======================================================================= static NCollection_Map& StorageIDSet() { static NCollection_IncAllocator TheAlloc; static NCollection_Map 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; } namespace { // dummy function for break point inline void place_for_break_point () {} }; //======================================================================= //function : StandardCallBack //purpose : Callback function to register alloc/free calls //======================================================================= void NCollection_BaseAllocator::StandardCallBack (const Standard_Boolean theIsAlloc, const Standard_Address theStorage, const Standard_Size theRoundSize, const Standard_Size /*theSize*/) { static Standard_Mutex aMutex; aMutex.Lock(); // statistics by storage size NCollection_DataMap& aStMap = StorageMap(); if (!aStMap.IsBound(theRoundSize)) { StorageInfo aEmpty(theRoundSize); aStMap.Bind(theRoundSize, aEmpty); } StorageInfo& aInfo = aStMap(theRoundSize); if (theIsAlloc) aInfo.nbAlloc++; else aInfo.nbFree++; if (theRoundSize == StandardCallBack_CatchSize()) { // statistics by alive objects NCollection_DataMap& aStIDMap = StorageIDMap(); NCollection_Map& aStIDSet = StorageIDSet(); if (theIsAlloc) { aStIDMap.Bind(theStorage, ++CurrentID); aStIDSet.Add(CurrentID); if (CurrentID == StandardCallBack_CatchID()) { // Place for break point for allocation of investigated ID place_for_break_point(); } } 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 place_for_break_point(); } } } } aMutex.Unlock(); } //======================================================================= //function : PrintMemUsageStatistics //purpose : Prints memory usage statistics cumulated by StandardCallBack //======================================================================= void NCollection_BaseAllocator::PrintMemUsageStatistics() { // sort by roundsize NCollection_List aColl; NCollection_List::Iterator itLst; NCollection_DataMap::Iterator itMap(StorageMap()); for (; itMap.More(); itMap.Next()) { for (itLst.Init(aColl); itLst.More(); itLst.Next()) if (itMap.Value().roundSize < itLst.Value().roundSize) break; if (itLst.More()) aColl.InsertBefore(itMap.Value(), itLst); else aColl.Append(itMap.Value()); } Standard_Size aTotAlloc = 0; Standard_Size aTotLeft = 0; // print std::ofstream aFileOut ("memstat.d", std::ios_base::trunc | std::ios_base::out); if (!aFileOut.is_open()) { std::cout << "failure writing file memstat.d" << std::endl; return; } aFileOut.imbue (std::locale ("C")); // header aFileOut << std::setw(20) << "BlockSize" << ' ' << std::setw(12) << "NbAllocated" << ' ' << std::setw(12) << "NbLeft" << ' ' << std::setw(20) << "Allocated" << ' ' << std::setw(20) << "Left" << '\n'; // body 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; aFileOut << std::setw(20) << aInfo.roundSize << ' ' << std::setw(12) << aInfo.nbAlloc << ' ' << std::setw(12) << nbLeft << ' ' << std::setw(20) << aSizeAlloc << ' ' << std::setw(20) << aSizeLeft << '\n'; aTotAlloc += aSizeAlloc; aTotLeft += aSizeLeft; } // footer aFileOut << std::setw(20) << "Total:" << ' ' << std::setw(12) << "" << ' ' << std::setw(12) << "" << ' ' << std::setw(20) << aTotAlloc << ' ' << std::setw(20) << aTotLeft << '\n'; if (!StorageIDSet().IsEmpty()) { aFileOut << "Alive allocation numbers of size=" << StandardCallBack_CatchSize() << '\n'; for (NCollection_Map::Iterator itMap1(StorageIDSet()); itMap1.More(); itMap1.Next()) { aFileOut << itMap1.Key() << '\n'; } } aFileOut.close(); }