00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012 #include <cstdlib>
00013
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
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
00069 }
00070
00071
00072 void TMemStatMng::Init()
00073 {
00074
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);
00097 fHbtids->SetDirectory(0);
00098
00099 fDumpTree->GetUserInfo()->Add(fHbtids);
00100 fDumpTree->GetUserInfo()->Add(fFAddrsList);
00101
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
00119
00120
00121 if(!fgInstance) {
00122 fgInstance = new TMemStatMng;
00123 fgInstance->Init();
00124 }
00125 return fgInstance;
00126 }
00127
00128
00129 void TMemStatMng::Close()
00130 {
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
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
00182
00183
00184
00185 delete fgInstance;
00186 fgInstance = NULL;
00187 }
00188
00189
00190 TMemStatMng::~TMemStatMng()
00191 {
00192
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
00207
00208
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
00225
00226 fMaxCalls = maxcalls;
00227 }
00228
00229
00230 void TMemStatMng::Enable()
00231 {
00232
00233
00234 if(this != GetInstance())
00235 return;
00236 #if defined(__APPLE__)
00237 TMemStatHook::trackZoneMalloc(MacAllocHook, MacFreeHook);
00238 #else
00239
00240 TMemStatHook::SetMallocHook(AllocHook);
00241 TMemStatHook::SetFreeHook(FreeHook);
00242 #endif
00243 }
00244
00245
00246 void TMemStatMng::Disable()
00247 {
00248
00249
00250
00251 if(this != GetInstance())
00252 return;
00253 #if defined(__APPLE__)
00254 TMemStatHook::untrackZoneMalloc();
00255 #else
00256
00257 TMemStatHook::SetMallocHook(fPreviousMallocHook);
00258 TMemStatHook::SetFreeHook(fPreviousFreeHook);
00259 #endif
00260 }
00261
00262
00263 void TMemStatMng::MacAllocHook(void *ptr, size_t size)
00264 {
00265
00266
00267
00268
00269 TMemStatMng* instance = TMemStatMng::GetInstance();
00270
00271 instance->Disable();
00272
00273
00274 instance->AddPointer(ptr, Int_t(size));
00275
00276
00277 instance->Enable();
00278 }
00279
00280
00281 void TMemStatMng::MacFreeHook(void *ptr)
00282 {
00283
00284
00285
00286
00287 TMemStatMng* instance = TMemStatMng::GetInstance();
00288
00289 instance->Disable();
00290
00291
00292 instance->AddPointer(ptr, -1);
00293
00294
00295 instance->Enable();
00296 }
00297
00298
00299 void *TMemStatMng::AllocHook(size_t size, const void* )
00300 {
00301
00302
00303
00304 TMemStatMng* instance = TMemStatMng::GetInstance();
00305
00306 instance->Disable();
00307
00308
00309 void *result = malloc(size);
00310
00311 instance->AddPointer(result, Int_t(size));
00312
00313
00314
00315 instance->Enable();
00316
00317 return result;
00318 }
00319
00320
00321 void TMemStatMng::FreeHook(void* ptr, const void* )
00322 {
00323
00324
00325
00326 TMemStatMng* instance = TMemStatMng::GetInstance();
00327
00328 instance->Disable();
00329
00330
00331 free(ptr);
00332
00333
00334 instance->AddPointer(ptr, -1);
00335
00336
00337 instance->Enable();
00338 }
00339
00340
00341 Int_t TMemStatMng::generateBTID(UChar_t *CRCdigest, Int_t stackEntries,
00342 void **stackPointers)
00343 {
00344
00345
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
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
00372 const int nbins = fHbtids->GetNbinsX();
00373
00374 if(fBTCount + stackEntries + 1 >= nbins) {
00375 fHbtids->SetBins(nbins * 2, 0, 1);
00376 }
00377
00378 int *btids = fHbtids->GetArray();
00379
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
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
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
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
00407 if(!fFAddrs.add(func_addr, idx))
00408 Error("AddPointer", "Can't add a function return address to the container");
00409 }
00410
00411
00412 btids[fBTCount++] = idx;
00413 }
00414
00415 } else {
00416
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
00429
00430
00431
00432 void *stptr[g_BTStackLevel + 1];
00433 const int stackentries = getBacktrace(stptr, g_BTStackLevel, fUseGNUBuiltinBacktrace);
00434
00435
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
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
00466
00467
00468
00469
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
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 }