TMemStatMng.cxx

Go to the documentation of this file.
00001 // @(#)root/memstat:$Id: TMemStatMng.cxx 36990 2010-11-26 19:44:18Z anar $
00002 // Author: Anar Manafov (A.Manafov@gsi.de) 2008-03-02
00003 
00004 /*************************************************************************
00005 * Copyright (C) 1995-2010, Rene Brun and Fons Rademakers.               *
00006 * All rights reserved.                                                  *
00007 *                                                                       *
00008 * For the licensing terms see $ROOTSYS/LICENSE.                         *
00009 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
00010 *************************************************************************/
00011 // STD
00012 #include <cstdlib>
00013 // ROOT
00014 #include "TSystem.h"
00015 #include "TEnv.h"
00016 #include "TError.h"
00017 #include "Riostream.h"
00018 #include "TObject.h"
00019 #include "TFile.h"
00020 #include "TTree.h"
00021 #include "TArrayL64.h"
00022 #include "TH1.h"
00023 #include "TMD5.h"
00024 #include "TMath.h"
00025 // Memstat
00026 #include "TMemStatBacktrace.h"
00027 #include "TMemStatMng.h"
00028 
00029 using namespace memstat;
00030 
00031 ClassImp(TMemStatMng)
00032 
00033 TMemStatMng* TMemStatMng::fgInstance = NULL;
00034 
00035 //****************************************************************************//
00036 //
00037 //****************************************************************************//
00038 
00039 TMemStatMng::TMemStatMng():
00040    TObject(),
00041 #if !defined(__APPLE__)
00042    fPreviousMallocHook(TMemStatHook::GetMallocHook()),
00043    fPreviousFreeHook(TMemStatHook::GetFreeHook()),
00044 #endif
00045    fDumpFile(NULL),
00046    fDumpTree(NULL),
00047    fUseGNUBuiltinBacktrace(kFALSE),
00048    fBeginTime(0),
00049    fPos(0),
00050    fTimems(0),
00051    fNBytes(0),
00052    fBtID(0),
00053    fMaxCalls(5000000),
00054    fBufferSize(10000),
00055    fBufN(0),
00056    fBufPos(0),
00057    fBufTimems(0),
00058    fBufNBytes(0),
00059    fBufBtID(0),
00060    fIndex(0),
00061    fMustWrite(0),
00062    fFAddrsList(0),
00063    fHbtids(0),
00064    fBTCount(0),
00065    fBTIDCount(0),
00066    fSysInfo(0)
00067 {
00068    // Default constructor
00069 }
00070 
00071 //______________________________________________________________________________
00072 void TMemStatMng::Init()
00073 {
00074    //Initialize MemStat manager - used only by instance method
00075 
00076    fBeginTime = fTimeStamp.AsDouble();
00077 
00078    fDumpFile = new TFile(Form("memstat_%d.root", gSystem->GetPid()), "recreate");
00079    Int_t opt = 200000;
00080    if(!fDumpTree) {
00081       fDumpTree = new TTree("T", "Memory Statistics");
00082       fDumpTree->Branch("pos",   &fPos,   "pos/l", opt);
00083       fDumpTree->Branch("time",  &fTimems, "time/I", opt);
00084       fDumpTree->Branch("nbytes", &fNBytes, "nbytes/I", opt);
00085       fDumpTree->Branch("btid",  &fBtID,  "btid/I", opt);
00086    }
00087 
00088    fBTCount = 0;
00089 
00090    fBTIDCount = 0;
00091 
00092    fFAddrsList = new TObjArray();
00093    fFAddrsList->SetOwner(kTRUE);
00094    fFAddrsList->SetName("FAddrsList");
00095 
00096    fHbtids  = new TH1I("btids", "table of btids", 10000, 0, 1);   //where fHbtids is a member of the manager class
00097    fHbtids->SetDirectory(0);
00098    // save the histogram and the TObjArray to the tree header
00099    fDumpTree->GetUserInfo()->Add(fHbtids);
00100    fDumpTree->GetUserInfo()->Add(fFAddrsList);
00101    // save the system info to a tree header
00102    string sSysInfo(gSystem->GetBuildNode());
00103    sSysInfo += " | ";
00104    sSysInfo += gSystem->GetBuildCompilerVersion();
00105    sSysInfo += " | ";
00106    sSysInfo += gSystem->GetFlagsDebug();
00107    sSysInfo += " ";
00108    sSysInfo += gSystem->GetFlagsOpt();
00109    fSysInfo = new TNamed("SysInfo", sSysInfo.c_str());
00110 
00111    fDumpTree->GetUserInfo()->Add(fSysInfo);
00112    fDumpTree->SetAutoSave(10000000);
00113 }
00114 
00115 //______________________________________________________________________________
00116 TMemStatMng* TMemStatMng::GetInstance()
00117 {
00118    // GetInstance - a static function
00119    // Initialize a singleton of MemStat manager
00120 
00121    if(!fgInstance) {
00122       fgInstance = new TMemStatMng;
00123       fgInstance->Init();
00124    }
00125    return fgInstance;
00126 }
00127 
00128 //______________________________________________________________________________
00129 void TMemStatMng::Close()
00130 {
00131    // Close - a static function
00132    // This method stops the manager,
00133    // flashes all the buffered data and closes the output tree.
00134 
00135    // TODO: This is a temporary solution until we find a properalgorithm for SaveData
00136    //fgInstance->fDumpFile->WriteObject(fgInstance->fFAddrsList, "FAddrsList");
00137 
00138 /*  ofstream f("mem_stat_debug.txt");
00139   int *btids = fgInstance->fHbtids->GetArray();
00140    if( !btids )
00141       return;
00142    int btid(1);
00143    int count(0);
00144    bool bStop(false);
00145    int count_empty(0);
00146    for (int i = 0; i < fgInstance->fBTChecksums.size(); ++i)
00147    {
00148      if (bStop)
00149          break;
00150      count = btids[btid-1];
00151      f << "++++++++++++++++++++++++\n";
00152      f << "BTID: " << btid << "\n";
00153      if ( count <= 0 )
00154         ++count_empty;
00155      for (int j = btid+1; j <= (btid+count); ++j )
00156          {
00157            TNamed *nm = (TNamed*)fgInstance->fFAddrsList->At(btids[j]);
00158            if( !nm )
00159                {
00160                    f << "Bad ID" << endl;
00161                    bStop = true;
00162                 }
00163            f << "-------> " << nm->GetTitle() << "\n";
00164          }
00165      btid = btid + count + 1;
00166    }
00167    f.close();
00168    ::Info("TMemStatMng::Close", "btids without a stack %d\n", count_empty);
00169 */
00170 
00171    // to be documented
00172    fgInstance->FillTree();
00173    fgInstance->Disable();
00174    fgInstance->fDumpTree->AutoSave();
00175    fgInstance->fDumpTree->GetUserInfo()->Delete();
00176 
00177    ::Info("TMemStatMng::Close", "Tree saved to file %s\n", fgInstance->fDumpFile->GetName());
00178    ::Info("TMemStatMng::Close", "Tree entries = %d, file size = %g MBytes\n", (Int_t)fgInstance->fDumpTree->GetEntries(),1e-6*Double_t(fgInstance->fDumpFile->GetEND()));
00179 
00180    delete fgInstance->fDumpFile;
00181    //fgInstance->fDumpFile->Close();
00182    //delete fgInstance->fFAddrsList;
00183    //delete fgInstance->fSysInfo;
00184 
00185    delete fgInstance;
00186    fgInstance = NULL;
00187 }
00188 
00189 //______________________________________________________________________________
00190 TMemStatMng::~TMemStatMng()
00191 {
00192    // if an instance is destructed - the hooks are reseted to old hooks
00193 
00194    if(this != TMemStatMng::GetInstance())
00195       return;
00196 
00197    Info("~TMemStatMng", ">>> All free/malloc calls count: %d", fBTIDCount);
00198    Info("~TMemStatMng", ">>> Unique BTIDs count: %zu", fBTChecksums.size());
00199    
00200    Disable();
00201 }
00202 
00203 //______________________________________________________________________________
00204 void TMemStatMng::SetBufferSize(Int_t buffersize)
00205 {
00206    // Set the maximum number of alloc/free calls to be buffered.
00207    //if the alloc and free are in the buffer, the corresponding entries
00208    //are not saved tio the Tree, reducing considerably the Tree output size
00209 
00210    fBufferSize = buffersize;
00211    if (fBufferSize < 1) fBufferSize = 1;
00212    fBufN = 0;
00213    fBufPos    = new ULong64_t[fBufferSize];
00214    fBufTimems = new Int_t[fBufferSize];
00215    fBufNBytes = new Int_t[fBufferSize];
00216    fBufBtID   = new Int_t[fBufferSize];
00217    fIndex     = new Int_t[fBufferSize];
00218    fMustWrite = new Bool_t[fBufferSize];
00219 }
00220 
00221 //______________________________________________________________________________
00222 void TMemStatMng::SetMaxCalls(Int_t maxcalls)
00223 {
00224    // Set the maximum number of new/delete registered in the output Tree.
00225 
00226    fMaxCalls = maxcalls;
00227 }
00228 
00229 //______________________________________________________________________________
00230 void TMemStatMng::Enable()
00231 {
00232    // Enable memory hooks
00233 
00234    if(this != GetInstance())
00235       return;
00236 #if defined(__APPLE__)
00237    TMemStatHook::trackZoneMalloc(MacAllocHook, MacFreeHook);
00238 #else
00239    // set hook to our functions
00240    TMemStatHook::SetMallocHook(AllocHook);
00241    TMemStatHook::SetFreeHook(FreeHook);
00242 #endif
00243 }
00244 
00245 //______________________________________________________________________________
00246 void TMemStatMng::Disable()
00247 {
00248    // Disble memory hooks
00249 
00250    //FillTree();
00251    if(this != GetInstance())
00252       return;
00253 #if defined(__APPLE__)
00254    TMemStatHook::untrackZoneMalloc();
00255 #else
00256    // set hook to our functions
00257    TMemStatHook::SetMallocHook(fPreviousMallocHook);
00258    TMemStatHook::SetFreeHook(fPreviousFreeHook);
00259 #endif
00260 }
00261 
00262 //______________________________________________________________________________
00263 void TMemStatMng::MacAllocHook(void *ptr, size_t size)
00264 {
00265    // AllocHook - a static function
00266    // a special memory hook for Mac OS X memory zones.
00267    // Triggered when memory is allocated.
00268 
00269    TMemStatMng* instance = TMemStatMng::GetInstance();
00270    // Restore all old hooks
00271    instance->Disable();
00272 
00273    // Call our routine
00274    instance->AddPointer(ptr, Int_t(size));
00275 
00276    // Restore our own hooks
00277    instance->Enable();
00278 }
00279 
00280 //______________________________________________________________________________
00281 void TMemStatMng::MacFreeHook(void *ptr)
00282 {
00283    // AllocHook - a static function
00284    // a special memory hook for Mac OS X memory zones.
00285    // Triggered when memory is deallocated.
00286 
00287    TMemStatMng* instance = TMemStatMng::GetInstance();
00288    // Restore all old hooks
00289    instance->Disable();
00290 
00291    // Call our routine
00292    instance->AddPointer(ptr, -1);
00293 
00294    // Restore our own hooks
00295    instance->Enable();
00296 }
00297 
00298 //______________________________________________________________________________
00299 void *TMemStatMng::AllocHook(size_t size, const void* /*caller*/)
00300 {
00301    // AllocHook - a static function
00302    // A glibc memory allocation hook.
00303 
00304    TMemStatMng* instance = TMemStatMng::GetInstance();
00305    // Restore all old hooks
00306    instance->Disable();
00307 
00308    // Call recursively
00309    void *result = malloc(size);
00310    // Call our routine
00311    instance->AddPointer(result, Int_t(size));
00312    //  TTimer::SingleShot(0, "TYamsMemMng", instance, "SaveData()");
00313 
00314    // Restore our own hooks
00315    instance->Enable();
00316 
00317    return result;
00318 }
00319 
00320 //______________________________________________________________________________
00321 void TMemStatMng::FreeHook(void* ptr, const void* /*caller*/)
00322 {
00323    // FreeHook - a static function
00324    // A glibc memory deallocation hook.
00325 
00326    TMemStatMng* instance = TMemStatMng::GetInstance();
00327    // Restore all old hooks
00328    instance->Disable();
00329 
00330    // Call recursively
00331    free(ptr);
00332 
00333    // Call our routine
00334    instance->AddPointer(ptr, -1);
00335 
00336    // Restore our own hooks
00337    instance->Enable();
00338 }
00339 
00340 //______________________________________________________________________________
00341 Int_t TMemStatMng::generateBTID(UChar_t *CRCdigest, Int_t stackEntries,
00342                                 void **stackPointers)
00343 {
00344    // An internal function, which returns a bitid for a corresponding CRC digest
00345    // cache variables
00346    static Int_t old_btid = -1;
00347    static SCustomDigest old_digest;
00348 
00349    Int_t ret_val = -1;
00350    bool startCheck(false);
00351    if(old_btid >= 0) {
00352       for(int i = 0; i < g_digestSize; ++i) {
00353          if(old_digest.fValue[i] != CRCdigest[i]) {
00354             startCheck = true;
00355             break;
00356          }
00357       }
00358       ret_val = old_btid;
00359    } else {
00360       startCheck = true;
00361    }
00362 
00363    // return cached value
00364    if(!startCheck)
00365       return ret_val;
00366 
00367    old_digest = SCustomDigest(CRCdigest);
00368    CRCSet_t::const_iterator found = fBTChecksums.find(CRCdigest);
00369 
00370    if(fBTChecksums.end() == found) {
00371       // check the size of the BT array container
00372       const int nbins = fHbtids->GetNbinsX();
00373       //check that the current allocation in fHbtids is enough, otherwise expend it with
00374       if(fBTCount + stackEntries + 1 >= nbins) {
00375          fHbtids->SetBins(nbins * 2, 0, 1);
00376       }
00377 
00378       int *btids = fHbtids->GetArray();
00379       // the first value is a number of entries in a given stack
00380       btids[fBTCount++] = stackEntries;
00381       ret_val = fBTCount;
00382       if(stackEntries <= 0) {
00383          Warning("AddPointer",
00384                  "A number of stack entries is equal or less than zero. For btid %d", ret_val);
00385       }
00386 
00387       // add new BT's CRC value
00388       pair<CRCSet_t::iterator, bool> res = fBTChecksums.insert(CRCSet_t::value_type(CRCdigest, ret_val));
00389       if(!res.second)
00390          Error("AddPointer", "Can't added a new BTID to the container.");
00391 
00392       // save all symbols of this BT
00393       for(int i = 0; i < stackEntries; ++i) {
00394          ULong_t func_addr = (ULong_t)(stackPointers[i]);
00395          Int_t idx = fFAddrs.find(func_addr);
00396          // check, whether it's a new symbol
00397          if(idx < 0) {
00398             TString strFuncAddr;
00399             strFuncAddr += func_addr;
00400             TString strSymbolInfo;
00401             getSymbolFullInfo(stackPointers[i], &strSymbolInfo);
00402 
00403             TNamed *nm = new TNamed(strFuncAddr, strSymbolInfo);
00404             fFAddrsList->Add(nm);
00405             idx = fFAddrsList->GetEntriesFast() - 1;
00406             // TODO: more detailed error message...
00407             if(!fFAddrs.add(func_addr, idx))
00408                Error("AddPointer", "Can't add a function return address to the container");
00409          }
00410 
00411          // even if we have -1 as an index we add it to the container
00412          btids[fBTCount++] = idx;
00413       }
00414 
00415    } else {
00416       // reuse an existing BT
00417       ret_val = found->second;
00418    }
00419 
00420    old_btid = ret_val;
00421 
00422    return ret_val;
00423 }
00424 
00425 //______________________________________________________________________________
00426 void TMemStatMng::AddPointer(void *ptr, Int_t size)
00427 {
00428    // Add pointer to table.
00429    // This method is called every time when any of the hooks are triggered.
00430    // The memory de-/allocation information will is recorded.
00431 
00432    void *stptr[g_BTStackLevel + 1];
00433    const int stackentries = getBacktrace(stptr, g_BTStackLevel, fUseGNUBuiltinBacktrace);
00434 
00435    // save only unique BTs
00436    TMD5 md5;
00437    md5.Update(reinterpret_cast<UChar_t*>(stptr), sizeof(void*) * stackentries);
00438    UChar_t digest[g_digestSize];
00439    md5.Final(digest);
00440 
00441    // for Debug. A counter of all (de)allacations.
00442    ++fBTIDCount;
00443 
00444    Int_t btid(generateBTID(digest, stackentries, stptr));   
00445 
00446    if(btid <= 0)
00447       Error("AddPointer", "bad BT id");
00448 
00449    fTimeStamp.Set();
00450    Double_t CurTime = fTimeStamp.AsDouble();
00451    fBufTimems[fBufN] = Int_t(10000.*(CurTime - fBeginTime));
00452    ULong_t ul = (ULong_t)(ptr);
00453    fBufPos[fBufN]    = (ULong64_t)(ul);
00454    fBufNBytes[fBufN] = size;
00455    fBufBtID[fBufN]   = btid;
00456    fBufN++;
00457    if (fBufN >= fBufferSize) {
00458       FillTree();
00459    }
00460 }
00461 
00462 //______________________________________________________________________________
00463 void TMemStatMng::FillTree()
00464 {
00465    //loop on all entries in the buffer and fill the output Tree
00466    //entries with alloc and free in the buffer are eliminated
00467    
00468    
00469    //eliminate alloc/free pointing to the same location in the current buffer
00470    TMath::Sort(fBufN,fBufPos,fIndex,kFALSE);
00471    memset(fMustWrite,0,fBufN*sizeof(Bool_t));
00472    Int_t i=0,j;
00473    while (i<fBufN) {
00474       Int_t indi = fIndex[i];
00475       Int_t indmin = indi;
00476       Int_t indmax = indi;
00477       j = i+1;;
00478       ULong64_t pos = fBufPos[indi];
00479       while (j < fBufN) {
00480          Int_t indj = fIndex[j];
00481          ULong64_t posj = fBufPos[indj];
00482          if (posj != pos) break;
00483          if (indmin > indj) indmin = indj;
00484          if (indmax < indj) indmax = indj;
00485          j++;
00486       }
00487       if (indmin == indmax) fMustWrite[indmin] = kTRUE;
00488       if (fBufNBytes[indmin] == -1) fMustWrite[indmin] = kTRUE;
00489       if (fBufNBytes[indmax] > 0)   fMustWrite[indmax] = kTRUE;
00490       i = j;  
00491    }
00492    
00493    // now fill the Tree with the remaining allocs/frees
00494    for (i=0;i<fBufN;i++) {
00495       if (!fMustWrite[i]) continue;
00496       fPos    = fBufPos[i];
00497       fTimems = fBufTimems[i];
00498       fNBytes = fBufNBytes[i];
00499       fBtID   = fBufBtID[i];
00500       fDumpTree->Fill();
00501    }
00502    
00503    fBufN = 0;
00504    if (fDumpTree->GetEntries() >= fMaxCalls) TMemStatMng::GetInstance()->Disable();
00505 }   

Generated on Tue Jul 5 14:44:00 2011 for ROOT_528-00b_version by  doxygen 1.5.1