#include <cstdlib>
#include "TSystem.h"
#include "TEnv.h"
#include "TError.h"
#include "Riostream.h"
#include "TObject.h"
#include "TFile.h"
#include "TTree.h"
#include "TArrayL64.h"
#include "TH1.h"
#include "TMD5.h"
#include "TMath.h"
#include "TMemStatBacktrace.h"
#include "TMemStatMng.h"
using namespace Memstat;
ClassImp(TMemStatMng)
TMemStatMng* TMemStatMng::fgInstance = NULL;
TMemStatMng::TMemStatMng():
TObject(),
#if !defined(__APPLE__)
fPreviousMallocHook(TMemStatHook::GetMallocHook()),
fPreviousFreeHook(TMemStatHook::GetFreeHook()),
#endif
fDumpFile(NULL),
fDumpTree(NULL),
fUseGNUBuiltinBacktrace(kFALSE),
fBeginTime(0),
fPos(0),
fTimems(0),
fNBytes(0),
fBtID(0),
fMaxCalls(5000000),
fBufferSize(10000),
fBufN(0),
fBufPos(0),
fBufTimems(0),
fBufNBytes(0),
fBufBtID(0),
fIndex(0),
fMustWrite(0),
fFAddrsList(0),
fHbtids(0),
fBTCount(0),
fBTIDCount(0),
fSysInfo(0)
{
}
void TMemStatMng::Init()
{
fBeginTime = fTimeStamp.AsDouble();
fDumpFile = new TFile(Form("memstat_%d.root", gSystem->GetPid()), "recreate");
Int_t opt = 200000;
if(!fDumpTree) {
fDumpTree = new TTree("T", "Memory Statistics");
fDumpTree->Branch("pos", &fPos, "pos/l", opt);
fDumpTree->Branch("time", &fTimems, "time/I", opt);
fDumpTree->Branch("nbytes", &fNBytes, "nbytes/I", opt);
fDumpTree->Branch("btid", &fBtID, "btid/I", opt);
}
fBTCount = 0;
fBTIDCount = 0;
fFAddrsList = new TObjArray();
fFAddrsList->SetOwner(kTRUE);
fFAddrsList->SetName("FAddrsList");
fHbtids = new TH1I("btids", "table of btids", 10000, 0, 1);
fHbtids->SetDirectory(0);
fDumpTree->GetUserInfo()->Add(fHbtids);
fDumpTree->GetUserInfo()->Add(fFAddrsList);
std::string sSysInfo(gSystem->GetBuildNode());
sSysInfo += " | ";
sSysInfo += gSystem->GetBuildCompilerVersion();
sSysInfo += " | ";
sSysInfo += gSystem->GetFlagsDebug();
sSysInfo += " ";
sSysInfo += gSystem->GetFlagsOpt();
fSysInfo = new TNamed("SysInfo", sSysInfo.c_str());
fDumpTree->GetUserInfo()->Add(fSysInfo);
fDumpTree->SetAutoSave(10000000);
}
TMemStatMng* TMemStatMng::GetInstance()
{
if(!fgInstance) {
fgInstance = new TMemStatMng;
fgInstance->Init();
}
return fgInstance;
}
void TMemStatMng::Close()
{
fgInstance->FillTree();
fgInstance->Disable();
fgInstance->fDumpTree->AutoSave();
fgInstance->fDumpTree->GetUserInfo()->Delete();
::Info("TMemStatMng::Close", "Tree saved to file %s\n", fgInstance->fDumpFile->GetName());
::Info("TMemStatMng::Close", "Tree entries = %d, file size = %g MBytes\n", (Int_t)fgInstance->fDumpTree->GetEntries(),1e-6*Double_t(fgInstance->fDumpFile->GetEND()));
delete fgInstance->fDumpFile;
delete fgInstance;
fgInstance = NULL;
}
TMemStatMng::~TMemStatMng()
{
if(this != TMemStatMng::GetInstance())
return;
Info("~TMemStatMng", ">>> All free/malloc calls count: %d", fBTIDCount);
Info("~TMemStatMng", ">>> Unique BTIDs count: %zu", fBTChecksums.size());
Disable();
}
void TMemStatMng::SetBufferSize(Int_t buffersize)
{
fBufferSize = buffersize;
if (fBufferSize < 1) fBufferSize = 1;
fBufN = 0;
fBufPos = new ULong64_t[fBufferSize];
fBufTimems = new Int_t[fBufferSize];
fBufNBytes = new Int_t[fBufferSize];
fBufBtID = new Int_t[fBufferSize];
fIndex = new Int_t[fBufferSize];
fMustWrite = new Bool_t[fBufferSize];
}
void TMemStatMng::SetMaxCalls(Int_t maxcalls)
{
fMaxCalls = maxcalls;
}
void TMemStatMng::Enable()
{
if(this != GetInstance())
return;
#if defined(__APPLE__)
TMemStatHook::trackZoneMalloc(MacAllocHook, MacFreeHook);
#else
TMemStatHook::SetMallocHook(AllocHook);
TMemStatHook::SetFreeHook(FreeHook);
#endif
}
void TMemStatMng::Disable()
{
if(this != GetInstance())
return;
#if defined(__APPLE__)
TMemStatHook::untrackZoneMalloc();
#else
TMemStatHook::SetMallocHook(fPreviousMallocHook);
TMemStatHook::SetFreeHook(fPreviousFreeHook);
#endif
}
void TMemStatMng::MacAllocHook(void *ptr, size_t size)
{
TMemStatMng* instance = TMemStatMng::GetInstance();
instance->Disable();
instance->AddPointer(ptr, Int_t(size));
instance->Enable();
}
void TMemStatMng::MacFreeHook(void *ptr)
{
TMemStatMng* instance = TMemStatMng::GetInstance();
instance->Disable();
instance->AddPointer(ptr, -1);
instance->Enable();
}
void *TMemStatMng::AllocHook(size_t size, const void* )
{
TMemStatMng* instance = TMemStatMng::GetInstance();
instance->Disable();
void *result = malloc(size);
instance->AddPointer(result, Int_t(size));
instance->Enable();
return result;
}
void TMemStatMng::FreeHook(void* ptr, const void* )
{
TMemStatMng* instance = TMemStatMng::GetInstance();
instance->Disable();
free(ptr);
instance->AddPointer(ptr, -1);
instance->Enable();
}
Int_t TMemStatMng::generateBTID(UChar_t *CRCdigest, Int_t stackEntries,
void **stackPointers)
{
static Int_t old_btid = -1;
static SCustomDigest old_digest;
Int_t ret_val = -1;
bool startCheck(false);
if(old_btid >= 0) {
for(int i = 0; i < g_digestSize; ++i) {
if(old_digest.fValue[i] != CRCdigest[i]) {
startCheck = true;
break;
}
}
ret_val = old_btid;
} else {
startCheck = true;
}
if(!startCheck)
return ret_val;
old_digest = SCustomDigest(CRCdigest);
CRCSet_t::const_iterator found = fBTChecksums.find(CRCdigest);
if(fBTChecksums.end() == found) {
const int nbins = fHbtids->GetNbinsX();
if(fBTCount + stackEntries + 1 >= nbins) {
fHbtids->SetBins(nbins * 2, 0, 1);
}
int *btids = fHbtids->GetArray();
btids[fBTCount++] = stackEntries;
ret_val = fBTCount;
if(stackEntries <= 0) {
Warning("AddPointer",
"A number of stack entries is equal or less than zero. For btid %d", ret_val);
}
std::pair<CRCSet_t::iterator, bool> res = fBTChecksums.insert(CRCSet_t::value_type(CRCdigest, ret_val));
if(!res.second)
Error("AddPointer", "Can't added a new BTID to the container.");
for(int i = 0; i < stackEntries; ++i) {
ULong_t func_addr = (ULong_t)(stackPointers[i]);
Int_t idx = fFAddrs.find(func_addr);
if(idx < 0) {
TString strFuncAddr;
strFuncAddr += func_addr;
TString strSymbolInfo;
getSymbolFullInfo(stackPointers[i], &strSymbolInfo);
TNamed *nm = new TNamed(strFuncAddr, strSymbolInfo);
fFAddrsList->Add(nm);
idx = fFAddrsList->GetEntriesFast() - 1;
if(!fFAddrs.add(func_addr, idx))
Error("AddPointer", "Can't add a function return address to the container");
}
btids[fBTCount++] = idx;
}
} else {
ret_val = found->second;
}
old_btid = ret_val;
return ret_val;
}
void TMemStatMng::AddPointer(void *ptr, Int_t size)
{
void *stptr[g_BTStackLevel + 1];
const int stackentries = getBacktrace(stptr, g_BTStackLevel, fUseGNUBuiltinBacktrace);
TMD5 md5;
md5.Update(reinterpret_cast<UChar_t*>(stptr), sizeof(void*) * stackentries);
UChar_t digest[g_digestSize];
md5.Final(digest);
++fBTIDCount;
Int_t btid(generateBTID(digest, stackentries, stptr));
if(btid <= 0)
Error("AddPointer", "bad BT id");
fTimeStamp.Set();
Double_t CurTime = fTimeStamp.AsDouble();
fBufTimems[fBufN] = Int_t(10000.*(CurTime - fBeginTime));
ULong_t ul = (ULong_t)(ptr);
fBufPos[fBufN] = (ULong64_t)(ul);
fBufNBytes[fBufN] = size;
fBufBtID[fBufN] = btid;
fBufN++;
if (fBufN >= fBufferSize) {
FillTree();
}
}
void TMemStatMng::FillTree()
{
TMath::Sort(fBufN,fBufPos,fIndex,kFALSE);
memset(fMustWrite,0,fBufN*sizeof(Bool_t));
Int_t i=0,j;
while (i<fBufN) {
Int_t indi = fIndex[i];
Int_t indmin = indi;
Int_t indmax = indi;
j = i+1;;
ULong64_t pos = fBufPos[indi];
while (j < fBufN) {
Int_t indj = fIndex[j];
ULong64_t posj = fBufPos[indj];
if (posj != pos) break;
if (indmin > indj) indmin = indj;
if (indmax < indj) indmax = indj;
j++;
}
if (indmin == indmax) fMustWrite[indmin] = kTRUE;
if (fBufNBytes[indmin] == -1) fMustWrite[indmin] = kTRUE;
if (fBufNBytes[indmax] > 0) fMustWrite[indmax] = kTRUE;
i = j;
}
for (i=0;i<fBufN;i++) {
if (!fMustWrite[i]) continue;
fPos = fBufPos[i];
fTimems = fBufTimems[i];
fNBytes = fBufNBytes[i];
fBtID = fBufBtID[i];
fDumpTree->Fill();
}
fBufN = 0;
if (fDumpTree->GetEntries() >= fMaxCalls) TMemStatMng::GetInstance()->Disable();
}