00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include <cstdio>
00028 #include <string>
00029
00030
00031 #include "TSystem.h"
00032 #include "TEnv.h"
00033 #include "TError.h"
00034 #include "Riostream.h"
00035 #include "TObject.h"
00036 #include "TFile.h"
00037 #include "TTree.h"
00038 #include "TObjString.h"
00039
00040 #include "TMemStatDepend.h"
00041 #include "TMemStatInfo.h"
00042 #include "TMemStatManager.h"
00043
00044 const char * const g_cszFileName("memstat.root");
00045 const Int_t g_STHashSize(65536);
00046
00047 ClassImp(TMemStatManager)
00048
00049 TMemStatManager * TMemStatManager::fgInstance = NULL;
00050
00051
00052
00053
00054
00055
00056 TMemStatManager::TMemStatManager():
00057 TObject(),
00058 fSTHashTable(g_STHashSize, -1),
00059 fCount(0),
00060 fStampNumber(0),
00061 fStackVector(),
00062 fStampVector(),
00063 fStampTime(),
00064 fCodeInfoArray() ,
00065 fCodeInfoMap(),
00066 fDebugLevel(0),
00067 fStampCallBack(0),
00068 fPreviousMallocHook(TMemStatDepend::GetMallocHook()),
00069 fPreviousFreeHook(TMemStatDepend::GetFreeHook()),
00070 fLastStamp(),
00071 fCurrentStamp(),
00072 fAutoStampSize(2000000),
00073 fAutoStampN(200000),
00074 fAutoStampDumpSize(50000),
00075 fMinStampSize(100),
00076 fSize(65536),
00077 fLeak(NULL),
00078 fAllocCount(0),
00079 fMultDeleteTable(),
00080 fDumpTree(0),
00081 fDumpSysTree(0),
00082 fUseGNUBuildinBacktrace(kFALSE)
00083 {
00084
00085
00086 SetBit(kUserDisable, kTRUE);
00087 fStampCallBack = TMemStatManager::SAddStamps;
00088
00089 }
00090
00091
00092 void TMemStatManager::Init()
00093 {
00094
00095
00096 SetBit(kUserDisable, kTRUE);
00097
00098 fStampNumber = 0;
00099 fAllocCount = 0;
00100 FreeHashtable();
00101 fLeak = (TMemTable_t **) malloc(sizeof(void *) * fSize);
00102 fMultDeleteTable.fLeaks = 0;
00103 fMultDeleteTable.fAllocCount = 0;
00104 fMultDeleteTable.fTableSize = 0;
00105 fStackVector.reserve(fSize);
00106 fStampVector.reserve(fSize*10);
00107 fCodeInfoArray.reserve(fSize);
00108 fStampTime.reserve(fSize);
00109 fStampTime[0] = TTimeStamp();
00110 for (int i = 0; i < fSize; ++i) {
00111 fLeak[i] = (TMemTable_t *) malloc(sizeof(TMemTable_t));
00112 fLeak[i]->fAllocCount = 0;
00113 fLeak[i]->fMemSize = 0;
00114 fLeak[i]->fFirstFreeSpot = 0;
00115 fLeak[i]->fTableSize = 0;
00116 fLeak[i]->fLeaks = 0;
00117 }
00118
00119 fCount = 0;
00120
00121 SetBit(kUserDisable, kTRUE);
00122 }
00123
00124
00125 TMemStatManager* TMemStatManager::GetInstance()
00126 {
00127
00128
00129
00130 if (!fgInstance) {
00131 fgInstance = new TMemStatManager;
00132 fgInstance->Init();
00133 }
00134 return fgInstance;
00135 }
00136
00137
00138 void TMemStatManager::Close()
00139 {
00140
00141
00142 delete fgInstance;
00143 fgInstance = NULL;
00144 }
00145
00146
00147 TMemStatManager::~TMemStatManager()
00148 {
00149
00150
00151
00152 if (this != TMemStatManager::GetInstance())
00153 return;
00154 SetBit(kStatDisable);
00155 Disable();
00156 AddStamps("End");
00157 DumpTo(kTree, kTRUE, "End");
00158 DumpTo(kSysTree, kTRUE, "End");
00159 Disable();
00160
00161 FreeHashtable();
00162 }
00163
00164
00165 void TMemStatManager::Enable()
00166 {
00167
00168
00169 if (this != GetInstance())
00170 return;
00171
00172
00173 TMemStatDepend::SetMallocHook(AllocHook);
00174 TMemStatDepend::SetFreeHook(FreeHook);
00175 SetBit(kUserDisable, kFALSE);
00176 }
00177
00178
00179 void TMemStatManager::Disable()
00180 {
00181
00182
00183 if (this != GetInstance())
00184 return;
00185
00186
00187 TMemStatDepend::SetMallocHook(fPreviousMallocHook);
00188 TMemStatDepend::SetFreeHook(fPreviousFreeHook);
00189 SetBit(kUserDisable, kTRUE);
00190 }
00191
00192
00193 void *TMemStatManager::AllocHook(size_t size, const void* )
00194 {
00195
00196
00197 TMemStatManager* instance = TMemStatManager::GetInstance();
00198 TMemStatDepend::SetMallocHook(instance->fPreviousMallocHook);
00199 void *p = instance->AddPointer(size);
00200 TMemStatDepend::SetMallocHook(AllocHook);
00201 return p;
00202 }
00203
00204
00205 void TMemStatManager::FreeHook(void* ptr, const void* )
00206 {
00207
00208
00209 TMemStatManager* instance = TMemStatManager::GetInstance();
00210 TMemStatDepend::SetFreeHook(instance->fPreviousFreeHook);
00211 instance->FreePointer(ptr);
00212 TMemStatDepend::SetFreeHook(FreeHook);
00213 }
00214
00215
00216 TMemStatStackInfo *TMemStatManager::STAddInfo(int size, void **stackptrs)
00217 {
00218
00219
00220
00221 const UInt_t currentSize = fStackVector.size();
00222 if (currentSize >= fStackVector.capacity())
00223 fStackVector.reserve(2*currentSize + 1);
00224
00225 fStackVector.push_back(TMemStatStackInfo());
00226 TMemStatStackInfo *info = &(fStackVector[currentSize]);
00227 info->Init(size, stackptrs, this, currentSize);
00228 info->fStackID = currentSize;
00229
00230
00231 const int hash = int(info->Hash() % g_STHashSize);
00232 Int_t hashIndex = fSTHashTable[hash];
00233 TMemStatStackInfo *info2 = NULL;
00234
00235 if (-1 == hashIndex) {
00236 fSTHashTable[hash] = info->fStackID;
00237 } else {
00238 info2 = &fStackVector[hashIndex];
00239 while (hashIndex >= 0) {
00240 hashIndex = info2->fNextHash;
00241 if (hashIndex >= 0)
00242 info2 = &fStackVector[hashIndex];
00243 }
00244 info2->fNextHash = info->fStackID;
00245 }
00246
00247 ++fCount;
00248 fStackVector.push_back(*info);
00249 return info;
00250 }
00251
00252
00253 TMemStatStackInfo *TMemStatManager::STFindInfo(int size, void **stackptrs)
00254 {
00255
00256
00257 const int hash = int(TMemStatStackInfo::HashStack(size, (void **)stackptrs) % g_STHashSize);
00258
00259 if (fSTHashTable[hash] < 0)
00260 return STAddInfo(size, stackptrs);
00261
00262 Int_t hashIndex = fSTHashTable[hash];
00263 TMemStatStackInfo *info = NULL;
00264
00265 info = &fStackVector[hashIndex];
00266 while (hashIndex >= 0) {
00267 if (info->Equal(size, stackptrs) == 1)
00268 return info;
00269 hashIndex = info->fNextHash;
00270 if (hashIndex >= 0)
00271 info = &fStackVector[hashIndex];
00272 }
00273 return STAddInfo(size, stackptrs);
00274 }
00275
00276
00277 void TMemStatManager::SAddStamps(const char * stampname)
00278 {
00279
00280
00281
00282 TMemStatManager *man = GetInstance();
00283 man->AddStamps(stampname);
00284 }
00285
00286
00287 void TMemStatManager::AddStamps(const char * stampname)
00288 {
00289
00290
00291 const UInt_t ssize = fStackVector.size();
00292 for (UInt_t i = 0; i < ssize; ++i) {
00293 if (fStackVector[i].fCurrentStamp.fAllocSize > fMinStampSize)
00294 fStackVector[i].MakeStamp(fStampNumber);
00295 }
00296 const UInt_t csize = fCodeInfoArray.size();
00297 for (UInt_t i = 0; i < csize; ++i) {
00298 if (fCodeInfoArray[i].fCurrentStamp.fAllocSize > fMinStampSize)
00299 fCodeInfoArray[i].MakeStamp(fStampNumber);
00300 }
00301
00302 fCurrentStamp.fID = -1;
00303 fCurrentStamp.fStampNumber = fStampNumber;
00304 AddStamp() = fCurrentStamp;
00305
00306 fStampTime[fStampNumber] = TTimeStamp();
00307 if (fStampVector.size() >= fAutoStampDumpSize || stampname) {
00308 DumpTo(kTree, kTRUE, stampname);
00309 DumpTo(kSysTree, kTRUE, stampname);
00310 }
00311 ++fStampNumber;
00312 }
00313
00314
00315 TMemStatInfoStamp &TMemStatManager::AddStamp()
00316 {
00317
00318
00319 const UInt_t size = fStampVector.size();
00320 fStampVector.push_back(TMemStatInfoStamp());
00321 TMemStatInfoStamp &stamp = fStampVector[size];
00322 stamp.fStampNumber = fStampNumber;
00323 return stamp;
00324 }
00325
00326
00327 TMemStatCodeInfo &TMemStatManager::GetCodeInfo(void *address)
00328 {
00329
00330
00331 TMemStatCodeInfo *info(NULL);
00332 const UInt_t index = fCodeInfoMap[address];
00333 if (index > 0) {
00334 info = &(fCodeInfoArray[fCodeInfoMap[address]]);
00335 } else {
00336 const UInt_t size = fCodeInfoArray.size();
00337 fCodeInfoArray.push_back(TMemStatCodeInfo());
00338 info = &(fCodeInfoArray[size]);
00339 fCodeInfoMap[address] = size;
00340 info->fCodeID = size;
00341 info->fCurrentStamp.fID = info->fCodeID;
00342 info->fLastStamp.fID = info->fCodeID;
00343 }
00344 return *info;
00345 }
00346
00347
00348 void TMemStatManager::RehashLeak(int newSize)
00349 {
00350
00351
00352 if (newSize <= fSize)
00353 return;
00354 TMemTable_t **newLeak = (TMemTable_t **) malloc(sizeof(void *) * newSize);
00355 for (int i = 0; i < newSize; ++i) {
00356
00357 newLeak[i] = (TMemTable_t *) malloc(sizeof(TMemTable_t));
00358 newLeak[i]->fAllocCount = 0;
00359 newLeak[i]->fMemSize = 0;
00360 newLeak[i]->fFirstFreeSpot = 0;
00361 newLeak[i]->fTableSize = 0;
00362 newLeak[i]->fLeaks = 0;
00363 }
00364 for (int ib = 0; ib < fSize; ++ib) {
00365 TMemTable_t *branch = fLeak[ib];
00366 for (int i = 0; i < branch->fTableSize; i++)
00367 if (branch->fLeaks[i].fAddress != 0) {
00368 int hash = int(TString::Hash(&branch->fLeaks[i].fAddress, sizeof(void*)) % newSize);
00369 TMemTable_t *newbranch = newLeak[hash];
00370 if (newbranch->fAllocCount >= newbranch->fTableSize) {
00371 int newTableSize =
00372 newbranch->fTableSize ==
00373 0 ? 16 : newbranch->fTableSize * 2;
00374 newbranch->fLeaks =
00375 (TMemInfo_t *) realloc(newbranch->fLeaks,
00376 sizeof(TMemInfo_t) * newTableSize);
00377 if (!newbranch->fLeaks) {
00378 Error("TMemStatManager::AddPointer", "realloc failure");
00379 _exit(1);
00380 }
00381 memset(newbranch->fLeaks + newbranch->fTableSize, 0,
00382 sizeof(TMemInfo_t) * (newTableSize -
00383 newbranch->fTableSize));
00384 newbranch->fTableSize = newTableSize;
00385 }
00386 memcpy(&newbranch->fLeaks[newbranch->fAllocCount],
00387 &branch->fLeaks[i], sizeof(TMemInfo_t));
00388 newbranch->fAllocCount++;
00389 newbranch->fMemSize += branch->fLeaks[i].fSize;
00390 }
00391 free(branch->fLeaks);
00392 free(branch);
00393 }
00394 free(fLeak);
00395 fLeak = newLeak;
00396 fSize = newSize;
00397 }
00398
00399
00400 void *TMemStatManager::AddPointer(size_t size, void *ptr)
00401 {
00402
00403
00404 if (TestBit(kUserDisable) || TestBit(kStatDisable)) {
00405 return malloc(size);
00406 }
00407
00408 Bool_t status = TestBit(kStatRoutine);
00409 SetBit(kStatRoutine, kTRUE);
00410
00411 void *p = NULL;
00412
00413 if (ptr == 0) {
00414 p = malloc(size);
00415 if (!p) {
00416 Error("TMemStatManager::AddPointer", "malloc failure");
00417 TMemStatManager::GetInstance()->Disable();
00418 TMemStatManager::GetInstance()->Close();
00419 _exit(1);
00420 }
00421 } else {
00422 p = realloc((char *) ptr, size);
00423 if (!p) {
00424 Error("TMemStatManager::AddPointer", "realloc failure");
00425 TMemStatManager::GetInstance()->Disable();
00426 TMemStatManager::GetInstance()->Close();
00427 _exit(1);
00428 }
00429 SetBit(kStatRoutine, status);
00430 return p;
00431 }
00432 if (status) {
00433 SetBit(kStatRoutine, status);
00434 return p;
00435 }
00436
00437 if (!fSize)
00438 Init();
00439 ++fAllocCount;
00440 if ((fAllocCount / fSize) > 128)
00441 RehashLeak(fSize * 2);
00442 int hash = int(TString::Hash(&p, sizeof(void*)) % fSize);
00443 TMemTable_t *branch = fLeak[hash];
00444 branch->fAllocCount++;
00445 branch->fMemSize += size;
00446
00447 fCurrentStamp.Inc(size);
00448 if ((fCurrentStamp.fTotalAllocCount - fLastStamp.fTotalAllocCount) > fAutoStampN ||
00449 (fCurrentStamp.fAllocCount - fLastStamp.fAllocCount) > Int_t(fAutoStampN) ||
00450 (fCurrentStamp.fTotalAllocSize - fLastStamp.fTotalAllocSize) > fAutoStampSize ||
00451 (fCurrentStamp.fAllocSize - fLastStamp.fAllocSize) > Int_t(fAutoStampSize)) {
00452 AddStamps();
00453 fLastStamp = fCurrentStamp;
00454 if (fAutoStampN < 0.001*fLastStamp.fTotalAllocCount) fAutoStampN = 1 + UInt_t(0.001 * fLastStamp.fTotalAllocCount);
00455 if (fAutoStampSize < 0.001*fLastStamp.fTotalAllocSize) fAutoStampSize = 1 + UInt_t(0.001 * fLastStamp.fTotalAllocSize);
00456 }
00457
00458 for (;;) {
00459 for (int i = branch->fFirstFreeSpot; i < branch->fTableSize; ++i)
00460 if (branch->fLeaks[i].fAddress == 0) {
00461 branch->fLeaks[i].fAddress = p;
00462 branch->fLeaks[i].fSize = size;
00463 void *stptr[TMemStatStackInfo::kStackHistorySize + 1];
00464 int stackentries = TMemStatDepend::Backtrace(stptr, TMemStatStackInfo::kStackHistorySize, fUseGNUBuildinBacktrace);
00465 TMemStatStackInfo *info = STFindInfo(stackentries, stptr);
00466 info->Inc(size, this);
00467 if (info->fCurrentStamp.fStampNumber == 0) {
00468 info->MakeStamp(fStampNumber);
00469 }
00470 branch->fLeaks[i].fStackIndex = info->fStackID;
00471 branch->fFirstFreeSpot = i + 1;
00472 SetBit(kStatRoutine, status);
00473 return p;
00474 }
00475
00476 int newTableSize =
00477 branch->fTableSize == 0 ? 16 : branch->fTableSize * 2;
00478 branch->fLeaks =
00479 (TMemInfo_t *) realloc(branch->fLeaks,
00480 sizeof(TMemInfo_t) * newTableSize);
00481 if (!branch->fLeaks) {
00482 Error("TMemStatManager::AddPointer", "realloc failure (2)");
00483 _exit(1);
00484 }
00485 memset(branch->fLeaks + branch->fTableSize, 0, sizeof(TMemInfo_t) *
00486 (newTableSize - branch->fTableSize));
00487 branch->fTableSize = newTableSize;
00488 }
00489 }
00490
00491
00492 void TMemStatManager::FreePointer(void *p)
00493 {
00494
00495
00496 if (p == 0)
00497 return;
00498 if (TestBit(kUserDisable) || TestBit(kStatDisable)) {
00499 free(p);
00500 return;
00501 }
00502
00503 const Bool_t status = TestBit(kStatRoutine);
00504 SetBit(kStatRoutine, kTRUE);
00505
00506 if (status) {
00507 SetBit(kStatRoutine, status);
00508 return;
00509 }
00510
00511 const int hash = static_cast<int>(TString::Hash(&p, sizeof(void*)) % fSize);
00512 --fAllocCount;
00513 TMemTable_t *branch = fLeak[hash];
00514 for (int i = 0; i < branch->fTableSize; i++) {
00515 if (branch->fLeaks[i].fAddress == p) {
00516 branch->fLeaks[i].fAddress = 0;
00517 branch->fMemSize -= branch->fLeaks[i].fSize;
00518 if (i < branch->fFirstFreeSpot)
00519 branch->fFirstFreeSpot = i;
00520 free(p);
00521 TMemStatStackInfo *info =
00522 &(fStackVector[branch->fLeaks[i].fStackIndex]);
00523 info->Dec(branch->fLeaks[i].fSize, this);
00524 fCurrentStamp.Dec(branch->fLeaks[i].fSize);
00525 branch->fAllocCount--;
00526 SetBit(kStatRoutine, status);
00527 return;
00528 }
00529 }
00530
00531
00532
00533
00534
00535 if (fMultDeleteTable.fTableSize + 1 > fMultDeleteTable.fAllocCount) {
00536 int newTableSize =
00537 fMultDeleteTable.fTableSize ==
00538 0 ? 16 : fMultDeleteTable.fTableSize * 2;
00539 fMultDeleteTable.fLeaks =
00540 (TMemInfo_t *) realloc(fMultDeleteTable.fLeaks,
00541 sizeof(TMemInfo_t) * newTableSize);
00542 fMultDeleteTable.fAllocCount = newTableSize;
00543 }
00544
00545 fMultDeleteTable.fLeaks[fMultDeleteTable.fTableSize].fAddress = 0;
00546
00547 void *stptr[TMemStatStackInfo::kStackHistorySize + 1];
00548 int stackentries = TMemStatDepend::Backtrace(stptr, TMemStatStackInfo::kStackHistorySize, fUseGNUBuildinBacktrace);
00549 TMemStatStackInfo *info = STFindInfo(stackentries, stptr);
00550 info->Dec(0, this);
00551 fMultDeleteTable.fLeaks[fMultDeleteTable.fTableSize].fStackIndex =
00552 info->fStackID;
00553 fMultDeleteTable.fTableSize++;
00554 SetBit(kStatRoutine, status);
00555 }
00556
00557
00558 void TMemStatManager::DumpTo(EDumpTo _DumpTo, Bool_t _clearStamps, const char *_stampName)
00559 {
00560
00561 const Bool_t status = TestBit(TMemStatManager::kStatDisable);
00562 SetBit(TMemStatManager::kStatDisable, kTRUE);
00563 if (!fDumpFile.get())
00564 fDumpFile.reset( TFile::Open(g_cszFileName, "recreate") );
00565
00566 TTimeStamp stamp;
00567 MemInfo_t memInfo;
00568 ProcInfo_t procInfo;
00569 gSystem->GetMemInfo(&memInfo);
00570 gSystem->GetProcInfo(&procInfo);
00571 Float_t memUsage[4] = { memInfo.fMemUsed, memInfo.fSwapUsed,
00572 procInfo.fMemResident*0.001, procInfo.fMemVirtual*0.001};
00573
00574 TTimeStamp *ptimeStamp(new TTimeStamp);
00575
00576 auto_ptr<TTimeStamp> ptimeStamp_(ptimeStamp);
00577
00578 TObjString *pnameStamp =
00579 (_stampName != 0) ? new TObjString(_stampName) : new TObjString(Form("autoStamp%d", fStampNumber));
00580
00581 auto_ptr<TObjString> pnameStamp_(pnameStamp);
00582
00583 const TMemStatManager * pmanager = this;
00584 Int_t stampNumber = fStampNumber;
00585
00586
00587 TMemStatInfoStamp *currentStamp(new TMemStatInfoStamp(fCurrentStamp));
00588
00589 auto_ptr<TMemStatInfoStamp> currentStamp_(currentStamp);
00590
00591 TTree *pDumpTo(NULL);
00592 bool bNewTree = false;
00593 switch (_DumpTo) {
00594 case kTree:
00595 if (!fDumpTree) {
00596 fDumpTree = new TTree("MemStat", "MemStat");
00597 bNewTree = true;
00598 }
00599 pDumpTo = fDumpTree;
00600 break;
00601 case kSysTree:
00602 if (!fDumpSysTree) {
00603 fDumpSysTree = new TTree("MemSys", "MemSys");
00604 bNewTree = true;
00605 }
00606 pDumpTo = fDumpSysTree;
00607 break;
00608 default:
00609 return;
00610 }
00611
00612 if (bNewTree) {
00613 if (kTree == _DumpTo)
00614 pDumpTo->Branch("Manager", "TMemStatManager", &pmanager);
00615 pDumpTo->Branch("StampTime.", "TTimeStamp", &ptimeStamp);
00616 pDumpTo->Branch("StampName.", "TObjString", &pnameStamp);
00617 pDumpTo->Branch("StampNumber", &stampNumber, "StampNumber/I");
00618 pDumpTo->Branch("CurrentStamp", "TMemStatInfoStamp", ¤tStamp);
00619 pDumpTo->Branch("Mem0", &memUsage[0], "Mem0/F");
00620 pDumpTo->Branch("Mem1", &memUsage[1], "Mem1/F");
00621 pDumpTo->Branch("Mem2", &memUsage[2], "Mem2/F");
00622 pDumpTo->Branch("Mem3", &memUsage[3], "Mem3/F");
00623 } else {
00624 if (kTree == _DumpTo)
00625 pDumpTo->SetBranchAddress("Manager", &pmanager);
00626 pDumpTo->SetBranchAddress("StampTime.", &ptimeStamp);
00627 pDumpTo->SetBranchAddress("StampName.", &pnameStamp);
00628 pDumpTo->SetBranchAddress("StampNumber", &stampNumber);
00629 pDumpTo->SetBranchAddress("CurrentStamp", ¤tStamp);
00630 pDumpTo->SetBranchAddress("Mem0", &memUsage[0]);
00631 pDumpTo->SetBranchAddress("Mem1", &memUsage[1]);
00632 pDumpTo->SetBranchAddress("Mem2", &memUsage[2]);
00633 pDumpTo->SetBranchAddress("Mem3", &memUsage[3]);
00634 }
00635
00636 pDumpTo->Fill();
00637 pDumpTo->AutoSave("Stat");
00638 if (_clearStamps)
00639 fStampVector.clear();
00640 SetBit(TMemStatManager::kStatDisable, status);
00641 }