#include <cstdio>
#include <string>
#include <malloc.h>
#include "TSystem.h"
#include "TEnv.h"
#include "TError.h"
#include "Riostream.h"
#include "TObject.h"
#include "TFile.h"
#include "TTree.h"
#include "TObjString.h"
#include "TMemStatDepend.h"
#include "TMemStatInfo.h"
#include "TMemStatManager.h"
const char * const g_cszFileName("memstat.root");
const Int_t g_STHashSize(65536);
ClassImp(TMemStatManager)
TMemStatManager * TMemStatManager::fgInstance = NULL;
TMemStatManager::TMemStatManager():
TObject(),
fSTHashTable(g_STHashSize, -1),
fCount(0),
fStampNumber(0),
fStackVector(),
fStampVector(),
fStampTime(),
fCodeInfoArray() ,
fCodeInfoMap(),
fDebugLevel(0),
fStampCallBack(0),
fPreviousMallocHook(TMemStatDepend::GetMallocHook()),
fPreviousFreeHook(TMemStatDepend::GetFreeHook()),
fLastStamp(),
fCurrentStamp(),
fAutoStampSize(2000000),
fAutoStampN(200000),
fAutoStampDumpSize(50000),
fMinStampSize(100),
fSize(65536),
fLeak(NULL),
fAllocCount(0),
fMultDeleteTable(),
fDumpTree(0),
fDumpSysTree(0),
fUseGNUBuildinBacktrace(kFALSE)
{
SetBit(kUserDisable, kTRUE);
fStampCallBack = TMemStatManager::SAddStamps;
}
void TMemStatManager::Init()
{
SetBit(kUserDisable, kTRUE);
fStampNumber = 0;
fAllocCount = 0;
FreeHashtable();
fLeak = (TMemTable_t **) malloc(sizeof(void *) * fSize);
fMultDeleteTable.fLeaks = 0;
fMultDeleteTable.fAllocCount = 0;
fMultDeleteTable.fTableSize = 0;
fStackVector.reserve(fSize);
fStampVector.reserve(fSize*10);
fCodeInfoArray.reserve(fSize);
fStampTime.reserve(fSize);
fStampTime[0] = TTimeStamp();
for (int i = 0; i < fSize; ++i) {
fLeak[i] = (TMemTable_t *) malloc(sizeof(TMemTable_t));
fLeak[i]->fAllocCount = 0;
fLeak[i]->fMemSize = 0;
fLeak[i]->fFirstFreeSpot = 0;
fLeak[i]->fTableSize = 0;
fLeak[i]->fLeaks = 0;
}
fCount = 0;
SetBit(kUserDisable, kTRUE);
}
TMemStatManager* TMemStatManager::GetInstance()
{
if (!fgInstance) {
fgInstance = new TMemStatManager;
fgInstance->Init();
}
return fgInstance;
}
void TMemStatManager::Close()
{
delete fgInstance;
fgInstance = NULL;
}
TMemStatManager::~TMemStatManager()
{
if (this != TMemStatManager::GetInstance())
return;
SetBit(kStatDisable);
Disable();
AddStamps("End");
DumpTo(kTree, kTRUE, "End");
DumpTo(kSysTree, kTRUE, "End");
Disable();
FreeHashtable();
}
void TMemStatManager::Enable()
{
if (this != GetInstance())
return;
TMemStatDepend::SetMallocHook(AllocHook);
TMemStatDepend::SetFreeHook(FreeHook);
SetBit(kUserDisable, kFALSE);
}
void TMemStatManager::Disable()
{
if (this != GetInstance())
return;
TMemStatDepend::SetMallocHook(fPreviousMallocHook);
TMemStatDepend::SetFreeHook(fPreviousFreeHook);
SetBit(kUserDisable, kTRUE);
}
void *TMemStatManager::AllocHook(size_t size, const void* )
{
TMemStatManager* instance = TMemStatManager::GetInstance();
TMemStatDepend::SetMallocHook(instance->fPreviousMallocHook);
void *p = instance->AddPointer(size);
TMemStatDepend::SetMallocHook(AllocHook);
return p;
}
void TMemStatManager::FreeHook(void* ptr, const void* )
{
TMemStatManager* instance = TMemStatManager::GetInstance();
TMemStatDepend::SetFreeHook(instance->fPreviousFreeHook);
instance->FreePointer(ptr);
TMemStatDepend::SetFreeHook(FreeHook);
}
TMemStatStackInfo *TMemStatManager::STAddInfo(int size, void **stackptrs)
{
const UInt_t currentSize = fStackVector.size();
if (currentSize >= fStackVector.capacity())
fStackVector.reserve(2*currentSize + 1);
fStackVector.push_back(TMemStatStackInfo());
TMemStatStackInfo *info = &(fStackVector[currentSize]);
info->Init(size, stackptrs, this, currentSize);
info->fStackID = currentSize;
const int hash = int(info->Hash() % g_STHashSize);
Int_t hashIndex = fSTHashTable[hash];
TMemStatStackInfo *info2 = NULL;
if (-1 == hashIndex) {
fSTHashTable[hash] = info->fStackID;
} else {
info2 = &fStackVector[hashIndex];
while (hashIndex >= 0) {
hashIndex = info2->fNextHash;
if (hashIndex >= 0)
info2 = &fStackVector[hashIndex];
}
info2->fNextHash = info->fStackID;
}
++fCount;
fStackVector.push_back(*info);
return info;
}
TMemStatStackInfo *TMemStatManager::STFindInfo(int size, void **stackptrs)
{
const int hash = int(TMemStatStackInfo::HashStack(size, (void **)stackptrs) % g_STHashSize);
if (fSTHashTable[hash] < 0)
return STAddInfo(size, stackptrs);
Int_t hashIndex = fSTHashTable[hash];
TMemStatStackInfo *info = NULL;
info = &fStackVector[hashIndex];
while (hashIndex >= 0) {
if (info->Equal(size, stackptrs) == 1)
return info;
hashIndex = info->fNextHash;
if (hashIndex >= 0)
info = &fStackVector[hashIndex];
}
return STAddInfo(size, stackptrs);
}
void TMemStatManager::SAddStamps(const char * stampname)
{
TMemStatManager *man = GetInstance();
man->AddStamps(stampname);
}
void TMemStatManager::AddStamps(const char * stampname)
{
const UInt_t ssize = fStackVector.size();
for (UInt_t i = 0; i < ssize; ++i) {
if (fStackVector[i].fCurrentStamp.fAllocSize > fMinStampSize)
fStackVector[i].MakeStamp(fStampNumber);
}
const UInt_t csize = fCodeInfoArray.size();
for (UInt_t i = 0; i < csize; ++i) {
if (fCodeInfoArray[i].fCurrentStamp.fAllocSize > fMinStampSize)
fCodeInfoArray[i].MakeStamp(fStampNumber);
}
fCurrentStamp.fID = -1;
fCurrentStamp.fStampNumber = fStampNumber;
AddStamp() = fCurrentStamp;
fStampTime[fStampNumber] = TTimeStamp();
if (fStampVector.size() >= fAutoStampDumpSize || stampname) {
DumpTo(kTree, kTRUE, stampname);
DumpTo(kSysTree, kTRUE, stampname);
}
++fStampNumber;
}
TMemStatInfoStamp &TMemStatManager::AddStamp()
{
const UInt_t size = fStampVector.size();
fStampVector.push_back(TMemStatInfoStamp());
TMemStatInfoStamp &stamp = fStampVector[size];
stamp.fStampNumber = fStampNumber;
return stamp;
}
TMemStatCodeInfo &TMemStatManager::GetCodeInfo(void *address)
{
TMemStatCodeInfo *info(NULL);
const UInt_t index = fCodeInfoMap[address];
if (index > 0) {
info = &(fCodeInfoArray[fCodeInfoMap[address]]);
} else {
const UInt_t size = fCodeInfoArray.size();
fCodeInfoArray.push_back(TMemStatCodeInfo());
info = &(fCodeInfoArray[size]);
fCodeInfoMap[address] = size;
info->fCodeID = size;
info->fCurrentStamp.fID = info->fCodeID;
info->fLastStamp.fID = info->fCodeID;
}
return *info;
}
void TMemStatManager::RehashLeak(int newSize)
{
if (newSize <= fSize)
return;
TMemTable_t **newLeak = (TMemTable_t **) malloc(sizeof(void *) * newSize);
for (int i = 0; i < newSize; ++i) {
newLeak[i] = (TMemTable_t *) malloc(sizeof(TMemTable_t));
newLeak[i]->fAllocCount = 0;
newLeak[i]->fMemSize = 0;
newLeak[i]->fFirstFreeSpot = 0;
newLeak[i]->fTableSize = 0;
newLeak[i]->fLeaks = 0;
}
for (int ib = 0; ib < fSize; ++ib) {
TMemTable_t *branch = fLeak[ib];
for (int i = 0; i < branch->fTableSize; i++)
if (branch->fLeaks[i].fAddress != 0) {
int hash = int(TString::Hash(&branch->fLeaks[i].fAddress, sizeof(void*)) % newSize);
TMemTable_t *newbranch = newLeak[hash];
if (newbranch->fAllocCount >= newbranch->fTableSize) {
int newTableSize =
newbranch->fTableSize ==
0 ? 16 : newbranch->fTableSize * 2;
newbranch->fLeaks =
(TMemInfo_t *) realloc(newbranch->fLeaks,
sizeof(TMemInfo_t) * newTableSize);
if (!newbranch->fLeaks) {
Error("TMemStatManager::AddPointer", "realloc failure");
_exit(1);
}
memset(newbranch->fLeaks + newbranch->fTableSize, 0,
sizeof(TMemInfo_t) * (newTableSize -
newbranch->fTableSize));
newbranch->fTableSize = newTableSize;
}
memcpy(&newbranch->fLeaks[newbranch->fAllocCount],
&branch->fLeaks[i], sizeof(TMemInfo_t));
newbranch->fAllocCount++;
newbranch->fMemSize += branch->fLeaks[i].fSize;
}
free(branch->fLeaks);
free(branch);
}
free(fLeak);
fLeak = newLeak;
fSize = newSize;
}
void *TMemStatManager::AddPointer(size_t size, void *ptr)
{
if (TestBit(kUserDisable) || TestBit(kStatDisable)) {
return malloc(size);
}
Bool_t status = TestBit(kStatRoutine);
SetBit(kStatRoutine, kTRUE);
void *p = NULL;
if (ptr == 0) {
p = malloc(size);
if (!p) {
Error("TMemStatManager::AddPointer", "malloc failure");
TMemStatManager::GetInstance()->Disable();
TMemStatManager::GetInstance()->Close();
_exit(1);
}
} else {
p = realloc((char *) ptr, size);
if (!p) {
Error("TMemStatManager::AddPointer", "realloc failure");
TMemStatManager::GetInstance()->Disable();
TMemStatManager::GetInstance()->Close();
_exit(1);
}
SetBit(kStatRoutine, status);
return p;
}
if (status) {
SetBit(kStatRoutine, status);
return p;
}
if (!fSize)
Init();
++fAllocCount;
if ((fAllocCount / fSize) > 128)
RehashLeak(fSize * 2);
int hash = int(TString::Hash(&p, sizeof(void*)) % fSize);
TMemTable_t *branch = fLeak[hash];
branch->fAllocCount++;
branch->fMemSize += size;
fCurrentStamp.Inc(size);
if ((fCurrentStamp.fTotalAllocCount - fLastStamp.fTotalAllocCount) > fAutoStampN ||
(fCurrentStamp.fAllocCount - fLastStamp.fAllocCount) > Int_t(fAutoStampN) ||
(fCurrentStamp.fTotalAllocSize - fLastStamp.fTotalAllocSize) > fAutoStampSize ||
(fCurrentStamp.fAllocSize - fLastStamp.fAllocSize) > Int_t(fAutoStampSize)) {
AddStamps();
fLastStamp = fCurrentStamp;
if (fAutoStampN < 0.001*fLastStamp.fTotalAllocCount) fAutoStampN = 1 + UInt_t(0.001 * fLastStamp.fTotalAllocCount);
if (fAutoStampSize < 0.001*fLastStamp.fTotalAllocSize) fAutoStampSize = 1 + UInt_t(0.001 * fLastStamp.fTotalAllocSize);
}
for (;;) {
for (int i = branch->fFirstFreeSpot; i < branch->fTableSize; ++i)
if (branch->fLeaks[i].fAddress == 0) {
branch->fLeaks[i].fAddress = p;
branch->fLeaks[i].fSize = size;
void *stptr[TMemStatStackInfo::kStackHistorySize + 1];
int stackentries = TMemStatDepend::Backtrace(stptr, TMemStatStackInfo::kStackHistorySize, fUseGNUBuildinBacktrace);
TMemStatStackInfo *info = STFindInfo(stackentries, stptr);
info->Inc(size, this);
if (info->fCurrentStamp.fStampNumber == 0) {
info->MakeStamp(fStampNumber);
}
branch->fLeaks[i].fStackIndex = info->fStackID;
branch->fFirstFreeSpot = i + 1;
SetBit(kStatRoutine, status);
return p;
}
int newTableSize =
branch->fTableSize == 0 ? 16 : branch->fTableSize * 2;
branch->fLeaks =
(TMemInfo_t *) realloc(branch->fLeaks,
sizeof(TMemInfo_t) * newTableSize);
if (!branch->fLeaks) {
Error("TMemStatManager::AddPointer", "realloc failure (2)");
_exit(1);
}
memset(branch->fLeaks + branch->fTableSize, 0, sizeof(TMemInfo_t) *
(newTableSize - branch->fTableSize));
branch->fTableSize = newTableSize;
}
}
void TMemStatManager::FreePointer(void *p)
{
if (p == 0)
return;
if (TestBit(kUserDisable) || TestBit(kStatDisable)) {
free(p);
return;
}
const Bool_t status = TestBit(kStatRoutine);
SetBit(kStatRoutine, kTRUE);
if (status) {
SetBit(kStatRoutine, status);
return;
}
const int hash = static_cast<int>(TString::Hash(&p, sizeof(void*)) % fSize);
--fAllocCount;
TMemTable_t *branch = fLeak[hash];
for (int i = 0; i < branch->fTableSize; i++) {
if (branch->fLeaks[i].fAddress == p) {
branch->fLeaks[i].fAddress = 0;
branch->fMemSize -= branch->fLeaks[i].fSize;
if (i < branch->fFirstFreeSpot)
branch->fFirstFreeSpot = i;
free(p);
TMemStatStackInfo *info =
&(fStackVector[branch->fLeaks[i].fStackIndex]);
info->Dec(branch->fLeaks[i].fSize, this);
fCurrentStamp.Dec(branch->fLeaks[i].fSize);
branch->fAllocCount--;
SetBit(kStatRoutine, status);
return;
}
}
if (fMultDeleteTable.fTableSize + 1 > fMultDeleteTable.fAllocCount) {
int newTableSize =
fMultDeleteTable.fTableSize ==
0 ? 16 : fMultDeleteTable.fTableSize * 2;
fMultDeleteTable.fLeaks =
(TMemInfo_t *) realloc(fMultDeleteTable.fLeaks,
sizeof(TMemInfo_t) * newTableSize);
fMultDeleteTable.fAllocCount = newTableSize;
}
fMultDeleteTable.fLeaks[fMultDeleteTable.fTableSize].fAddress = 0;
void *stptr[TMemStatStackInfo::kStackHistorySize + 1];
int stackentries = TMemStatDepend::Backtrace(stptr, TMemStatStackInfo::kStackHistorySize, fUseGNUBuildinBacktrace);
TMemStatStackInfo *info = STFindInfo(stackentries, stptr);
info->Dec(0, this);
fMultDeleteTable.fLeaks[fMultDeleteTable.fTableSize].fStackIndex =
info->fStackID;
fMultDeleteTable.fTableSize++;
SetBit(kStatRoutine, status);
}
void TMemStatManager::DumpTo(EDumpTo _DumpTo, Bool_t _clearStamps, const char *_stampName)
{
const Bool_t status = TestBit(TMemStatManager::kStatDisable);
SetBit(TMemStatManager::kStatDisable, kTRUE);
if (!fDumpFile.get())
fDumpFile.reset( TFile::Open(g_cszFileName, "recreate") );
TTimeStamp stamp;
MemInfo_t memInfo;
ProcInfo_t procInfo;
gSystem->GetMemInfo(&memInfo);
gSystem->GetProcInfo(&procInfo);
Float_t memUsage[4] = { memInfo.fMemUsed, memInfo.fSwapUsed,
procInfo.fMemResident*0.001, procInfo.fMemVirtual*0.001};
TTimeStamp *ptimeStamp(new TTimeStamp);
auto_ptr<TTimeStamp> ptimeStamp_(ptimeStamp);
TObjString *pnameStamp =
(_stampName != 0) ? new TObjString(_stampName) : new TObjString(Form("autoStamp%d", fStampNumber));
auto_ptr<TObjString> pnameStamp_(pnameStamp);
const TMemStatManager * pmanager = this;
Int_t stampNumber = fStampNumber;
TMemStatInfoStamp *currentStamp(new TMemStatInfoStamp(fCurrentStamp));
auto_ptr<TMemStatInfoStamp> currentStamp_(currentStamp);
TTree *pDumpTo(NULL);
bool bNewTree = false;
switch (_DumpTo) {
case kTree:
if (!fDumpTree) {
fDumpTree = new TTree("MemStat", "MemStat");
bNewTree = true;
}
pDumpTo = fDumpTree;
break;
case kSysTree:
if (!fDumpSysTree) {
fDumpSysTree = new TTree("MemSys", "MemSys");
bNewTree = true;
}
pDumpTo = fDumpSysTree;
break;
default:
return;
}
if (bNewTree) {
if (kTree == _DumpTo)
pDumpTo->Branch("Manager", "TMemStatManager", &pmanager);
pDumpTo->Branch("StampTime.", "TTimeStamp", &ptimeStamp);
pDumpTo->Branch("StampName.", "TObjString", &pnameStamp);
pDumpTo->Branch("StampNumber", &stampNumber, "StampNumber/I");
pDumpTo->Branch("CurrentStamp", "TMemStatInfoStamp", ¤tStamp);
pDumpTo->Branch("Mem0", &memUsage[0], "Mem0/F");
pDumpTo->Branch("Mem1", &memUsage[1], "Mem1/F");
pDumpTo->Branch("Mem2", &memUsage[2], "Mem2/F");
pDumpTo->Branch("Mem3", &memUsage[3], "Mem3/F");
} else {
if (kTree == _DumpTo)
pDumpTo->SetBranchAddress("Manager", &pmanager);
pDumpTo->SetBranchAddress("StampTime.", &ptimeStamp);
pDumpTo->SetBranchAddress("StampName.", &pnameStamp);
pDumpTo->SetBranchAddress("StampNumber", &stampNumber);
pDumpTo->SetBranchAddress("CurrentStamp", ¤tStamp);
pDumpTo->SetBranchAddress("Mem0", &memUsage[0]);
pDumpTo->SetBranchAddress("Mem1", &memUsage[1]);
pDumpTo->SetBranchAddress("Mem2", &memUsage[2]);
pDumpTo->SetBranchAddress("Mem3", &memUsage[3]);
}
pDumpTo->Fill();
pDumpTo->AutoSave("Stat");
if (_clearStamps)
fStampVector.clear();
SetBit(TMemStatManager::kStatDisable, status);
}
Last change: Fri Jul 4 14:51:19 2008
Last generated: 2008-07-04 14:51
This page has been automatically generated. If you have any comments or suggestions about the page layout send a mail to ROOT support, or contact the developers with any questions or problems regarding ROOT.