#include "TMemStatShow.h"
#include "TMath.h"
#include "TFile.h"
#include "TTree.h"
#include "TCanvas.h"
#include "TStyle.h"
#include "TH1.h"
#include "TPaveText.h"
#include "TPaveLabel.h"
#include "TSystem.h"
#include "TGClient.h"
#include "TGToolTip.h"
#include "TRootCanvas.h"
TTree *TMemStatShow::fgT = 0;
TH1D *TMemStatShow::fgHalloc = 0;
TH1D *TMemStatShow::fgHfree = 0;
TH1D *TMemStatShow::fgH = 0;
TH1I *TMemStatShow::fgHleaks = 0;
TH1I *TMemStatShow::fgHentry = 0;
TH1I *TMemStatShow::fgHdiff = 0;
TGToolTip *TMemStatShow::fgTip1 = 0;
TGToolTip *TMemStatShow::fgTip2 = 0;
TObjArray *TMemStatShow::fgBtidlist = 0;
Double_t *TMemStatShow::fgV1 = 0;
Double_t *TMemStatShow::fgV2 = 0;
Double_t *TMemStatShow::fgV3 = 0;
Double_t *TMemStatShow::fgV4 = 0;
TCanvas *TMemStatShow::fgC1 = 0;
TCanvas *TMemStatShow::fgC2 = 0;
TCanvas *TMemStatShow::fgC3 = 0;
Long64_t TMemStatShow::fgEntryFirst = 0;
Long64_t TMemStatShow::fgEntryN = 0;
Long64_t TMemStatShow::fgAddressFirst = 0;
Long64_t TMemStatShow::fgAddressN = 0;
void TMemStatShow::SetAddressRange(Long64_t nbytes, Long64_t first)
{
fgAddressFirst = first;
fgAddressN = nbytes;
}
void TMemStatShow::SetEntryRange(Long64_t nentries, Long64_t first)
{
fgEntryFirst = first;
fgEntryN = nentries;
}
void TMemStatShow::Show(double update, int nbigleaks, const char* fname)
{
TString s;
if (!fname || strlen(fname) <5 || strstr(fname,"*")) {
s = gSystem->GetFromPipe("ls -lrt memstat*.root");
Int_t ns = s.Length();
fname = strstr(s.Data()+ns-25,"memstat");
}
printf("Analyzing file: %s\n",fname);
TFile *f = TFile::Open(fname);
if (!f) {
printf("Cannot open file %s\n",fname);
return;
}
fgT = (TTree*)f->Get("T");
if (!fgT) {
printf("cannot find the TMemStat TTree named T in file %s\n",fname);
return;
}
if (update <= 0) {
printf("Illegal update value %g, changed to 0.01\n",update);
update = 0.01;
}
if (update < 0.001) printf("Warning update parameter is very small, processing may be slow\n");
MemInfo_t minfo;
gSystem->GetMemInfo(&minfo);
Int_t nfree = minfo.fMemTotal - minfo.fMemUsed;
printf("TMemStat::Show info: you are running on a machine with %d free MBytes of memory\n",nfree);
Long64_t nfreebytes = 200000*Long64_t(nfree);
if (fgAddressN <=0) fgAddressN = nfreebytes;
Long64_t nentries = fgT->GetEntries();
if (fgEntryN > 0 && nentries > fgEntryN) nentries = fgEntryN;
if (2*8*nentries > 4*nfreebytes) {
nentries = 4*nfreebytes/16;
printf("not enough memory, restricting analysis to %lld entries\n",nentries);
}
fgT->SetEstimate(nentries);
Long64_t nsel = fgT->Draw("pos","pos>0","goff",nentries);
fgV1 = fgT->GetV1();
Long64_t ivmin = (Long64_t)TMath::MinElement(nsel,fgV1);
Long64_t ivmax = (Long64_t)TMath::MaxElement(nsel,fgV1);
if (ivmax-ivmin > fgAddressN) ivmax = ivmin+fgAddressN;
printf("TMemStatShow::Show will analyze only %lld bytes in its first pass\n",ivmax);
fgTip1 = 0;
fgTip2 = 0;
fgBtidlist = 0;
Long64_t ne = nfreebytes/32LL;
if (ne < nentries) nentries = ne;
fgT->SetEstimate(nentries+10);
printf("sel: ivmin=%lld, ivmax=%lld, nentries=%lld\n",ivmin,ivmax,nentries);
nsel = fgT->Draw("pos:nbytes:time:btid",
TString::Format("pos>%g && pos<%g",Double_t(ivmin),Double_t(ivmax)),
"goff",nentries,fgEntryFirst);
Int_t nbytes;
Double_t pos;
fgV1 = fgT->GetV1();
fgV2 = fgT->GetV2();
fgV3 = fgT->GetV3();
fgV4 = fgT->GetV4();
ivmin = (Long64_t)TMath::MinElement(nsel,fgV1);
ivmax = (Long64_t)TMath::MaxElement(nsel,fgV1);
Long64_t bw = 1000;
Double_t dvv = (Double_t(ivmax) - Double_t(ivmin))/Double_t(bw);
Long64_t nbins = Long64_t(dvv);
ivmin = ivmin -ivmin%bw;
ivmax = ivmin+bw*nbins;
Long64_t nvm = Long64_t(ivmax-ivmin+1);
printf("==>The data Tree contains %lld entries with addresses in range[%lld,%lld]\n",nsel,ivmin,ivmax);
ne = 1000000LL*nfree/32LL;
if (ne < 0) return;
if (ne < nentries) {
nsel = fgT->Draw("pos:nbytes:time:btid",
TString::Format("pos>=%g && pos<%g",Double_t(ivmin),Double_t(ivmax)),"goff",ne,fgEntryFirst);
fgV1 = fgT->GetV1();
fgV2 = fgT->GetV2();
fgV3 = fgT->GetV3();
fgV4 = fgT->GetV4();
ivmin = (Long64_t)TMath::MinElement(nsel,fgV1);
ivmax = (Long64_t)TMath::MaxElement(nsel,fgV1);
bw = 10000;
dvv = (Double_t(ivmax) - Double_t(ivmin))/Double_t(bw);
nbins = Long64_t(dvv+0.5);
ivmin = ivmin -ivmin%bw;
ivmax = ivmin+bw*nbins;
printf("==>Address range or/and Entry range is too large\n");
printf("==>restricting the analysis range to [%lld,%lld] and %lld entries\n",ivmin,ivmax,ne);
printf("==>you can restrict the address range with TMemStatShow::SetAddressRange\n");
printf("==>you can restrict the entries range with TMemStatShow::SetEntryRange\n");
}
update *= 0.0001*fgV3[nsel-1];
nvm = Long64_t(ivmax-ivmin);
Long64_t *nbold = new Long64_t[nvm];
Int_t *ientry = new Int_t[nvm];
if (!nbold || !ientry) {
printf("you do not have enough memory to run, %lld bytes needed\n",12*nvm);
return;
}
memset(nbold,0,nvm*8);
memset(ientry,0,nvm*4);
Double_t dv = (ivmax-ivmin)/nbins;
TH1D *h = new TH1D("h",Form("%s;pos;per cent of pages used",fname),nbins,ivmin,ivmax);
fgH = h;
TAxis *axis = h->GetXaxis();
gStyle->SetOptStat("ie");
h->SetFillColor(kRed);
h->SetMinimum(0);
h->SetMaximum(100);
fgHalloc = new TH1D("fgHalloc",Form("%s;pos;number of mallocs",fname),nbins,ivmin,ivmax);
fgHfree = new TH1D("fgHfree", Form("%s;pos;number of frees",fname),nbins,ivmin,ivmax);
fgHdiff = new TH1I("fgHdiff","",1000,0,1e5);
fgC1 = new TCanvas("fgC1","c1",1200,600);
fgC1->SetFrameFillColor(kYellow-3);
fgC1->SetGridx();
fgC1->SetGridy();
h->Draw();
TPaveText *pvt = new TPaveText(.5,.9,.75,.99,"brNDC");
pvt->Draw();
TPaveLabel *ptime = new TPaveLabel(.905,.7,.995,.76,"time","brNDC");
ptime->SetFillColor(kYellow-3);
ptime->Draw();
TNamed *named = (TNamed*)fgT->GetUserInfo()->FindObject("SysInfo");
TText tmachine;
tmachine.SetTextSize(0.02);
tmachine.SetNDC();
if (named) tmachine.DrawText(0.01,0.01,named->GetTitle());
Int_t bin,nb=0,j;
Long64_t ipos;
Double_t dbin,rest,time;
Double_t updateLast = 0;
Int_t nleaks = 0;
Int_t i;
for (i=0;i<nsel;i++) {
pos = fgV1[i];
ipos = (Long64_t)(pos-ivmin);
nbytes = (Int_t)fgV2[i];
time = 0.0001*fgV3[i];
bin = axis->FindBin(pos);
if (bin<1 || bin>nbins) continue;
dbin = axis->GetBinUpEdge(bin)-pos;
if (nbytes > 0) {
ientry[ipos] = i;
fgHalloc->Fill(pos);
if (dbin > nbytes) dbin = nbytes;
h->AddBinContent(bin,100*dbin/dv);
nb = Int_t((nbytes-dbin)/dv);
if (bin+nb >nbins) nb = nbins-bin;
for (j=1;j<=nb;j++) h->AddBinContent(bin+j,100);
rest = nbytes-nb*dv-dbin;
if (rest > 0) h->AddBinContent(bin+nb+1,100*rest/dv);
if (nbold[ipos] == 0) {
nleaks++;
ientry[ipos] = i;
}
nbold[ipos] = nbytes;
} else {
fgHfree->Fill(pos);
nbytes = nbold[ipos];
if (bin+nb >nbins) nb = nbins-bin;
nbold[ipos] = 0; nleaks--;
fgHdiff->Fill(i-ientry[ipos]);
if (nbytes <= 0) continue;
if (dbin > nbytes) dbin = nbytes;
h->AddBinContent(bin,-100*dbin/dv);
nb = Int_t((nbytes-dbin)/dv);
if (bin+nb >nbins) nb = nbins-bin;
for (j=1;j<=nb;j++) h->AddBinContent(bin+j,-100);
rest = nbytes-nb*dv-dbin;
if (rest > 0) h->AddBinContent(bin+nb+1,-100*rest/dv);
}
if (time -updateLast > update) {
updateLast = time;
h->SetEntries(i);
fgC1->Modified();
pvt->GetListOfLines()->Delete();
Double_t mbytes = 0;
Int_t nonEmpty = 0;
Double_t w;
for (Int_t k=1;k<nbins;k++) {
w = h->GetBinContent(k);
if (w > 0) {
nonEmpty++;
mbytes += 0.01*w*dv;
}
}
Double_t occupancy = mbytes/(nonEmpty*0.01*dv);
pvt->AddText(Form("memory used = %g Mbytes",mbytes*1e-6));
pvt->AddText(Form("page occupancy = %f per cent",occupancy));
pvt->AddText("(for non empty pages only)");
ptime->SetLabel(Form("%g sec",time));
fgC1->Update();
gSystem->ProcessEvents();
}
}
h->SetEntries(nsel);
if (nleaks < 0) nleaks=0;
Int_t nlmax = nleaks;
nleaks += 1000;
Int_t *lindex = new Int_t[nleaks];
Int_t *entry = new Int_t[nleaks];
Int_t *ileaks = new Int_t[nleaks];
nleaks =0;
for (Int_t ii=0;ii<nvm;ii++) {
if (nbold[ii] > 0) {
ileaks[nleaks] = (Int_t)nbold[ii];
entry[nleaks] = ientry[ii];
nleaks++;
if (nleaks > nlmax) break;
}
}
TMath::Sort(nleaks,ileaks,lindex);
fgHentry = new TH1I("fgHentry","leak entry index",nleaks,0,nleaks);
fgHleaks = new TH1I("fgHleaks","leaks;leak number;nbytes in leak",nleaks,0,nleaks);
for (Int_t k=0;k<nleaks;k++) {
Int_t kk = lindex[k];
i = entry[kk];
fgHentry->SetBinContent(k+1,i);
fgHleaks->SetBinContent(k+1,ileaks[kk]);
}
delete [] ileaks;
delete [] entry;
delete [] lindex;
delete [] nbold;
delete [] ientry;
fgHentry->SetEntries(nleaks);
fgHleaks->SetEntries(nleaks);
fgC1->Modified();
fgC1->Update();
TRootCanvas *rc1 = (TRootCanvas *)fgC1->GetCanvasImp();
TGMainFrame *frm1 = dynamic_cast<TGMainFrame *>(rc1);
if (!fgTip1) fgTip1 = new TGToolTip(gClient->GetDefaultRoot(), frm1, "", 250);
fgC1->Connect("ProcessedEvent(Int_t, Int_t, Int_t, TObject*)",
"TMemStatShow", 0, "EventInfo1(Int_t, Int_t, Int_t, TObject*)");
if (nbigleaks <= 0) return;
fgC2 = new TCanvas("fgC2","c2",1200,600);
fgC2->SetFrameFillColor(kCyan-6);
fgC2->SetGridx();
fgC2->SetGridy();
fgC2->SetLogy();
fgHleaks->SetFillColor(kRed-3);
if (nleaks > 1000) fgHleaks->GetXaxis()->SetRange(1,1000);
fgHleaks->Draw();
if (named) tmachine.DrawText(0.01,0.01,named->GetTitle());
TRootCanvas *rc2 = (TRootCanvas *)fgC2->GetCanvasImp();
TGMainFrame *frm2 = dynamic_cast<TGMainFrame *>(rc2);
if (!fgTip2) fgTip2 = new TGToolTip(gClient->GetDefaultRoot(), frm2, "", 250);
fgC2->Connect("ProcessedEvent(Int_t, Int_t, Int_t, TObject*)",
"TMemStatShow", 0, "EventInfo2(Int_t, Int_t, Int_t, TObject*)");
fgC3 = new TCanvas("fgC3","c3",1200,600);
fgC3->SetFrameFillColor(kCyan-6);
fgC3->SetGridx();
fgC3->SetGridy();
fgC3->SetLogx();
fgC3->SetLeftMargin(0.05);
fgC3->SetRightMargin(0.7);
TH1I *htotleaks = new TH1I("htotleaks","main leaks sorted by btids",100,0,0);
Int_t l;
for (l=1;l<=nleaks;l++) {
TString btstring = "";
TMemStatShow::FillBTString(l,1,btstring);
htotleaks->Fill(btstring.Data()+2,fgHleaks->GetBinContent(l));
}
Double_t tsize = 0.03;
if (nbigleaks > 30) tsize = 0.02;
htotleaks->LabelsOption(">");
htotleaks->GetXaxis()->SetRange(1,nbigleaks);
htotleaks->GetXaxis()->SetLabelSize(tsize);
htotleaks->GetYaxis()->SetLabelSize(tsize);
htotleaks->SetFillColor(kBlue-3);
htotleaks->Draw("hbar2 y+");
Double_t xr = 0.96*fgC3->GetLeftMargin();
Double_t xr2 = 1.04*fgC3->GetLeftMargin();
Double_t ytop = 1-fgC3->GetTopMargin();
Double_t ylow = fgC3->GetBottomMargin();
Double_t dy = (ytop-ylow)/nbigleaks;
TString btstring;
TText tnl;
tnl.SetNDC();
tnl.SetTextSize(tsize);
tnl.SetTextAlign(32);
TText tnl2;
tnl2.SetNDC();
tnl2.SetTextSize(tsize);
tnl2.SetTextAlign(12);
tnl2.SetTextColor(kYellow);
for (Int_t lb=1;lb<=nbigleaks;lb++) {
if (htotleaks->GetBinContent(lb) <= 0) continue;
const char *label = htotleaks->GetXaxis()->GetBinLabel(lb);
Int_t nchlabel = strlen(label);
if (nchlabel == 0) htotleaks->GetXaxis()->SetBinLabel(lb,"???");
Int_t nl =0;
for (l=1;l<=nleaks;l++) {
btstring = "";
TMemStatShow::FillBTString(l,1,btstring);
if (nchlabel > 0) {
if (!strncmp(btstring.Data()+2,label,nchlabel)) nl++;
} else {
if (btstring.Length() == 0) nl++;
}
}
Double_t yr = ylow +(lb-0.5)*dy;
tnl.DrawText(xr,yr,Form("%d",nl));
Int_t nbmean = Int_t(htotleaks->GetBinContent(lb)/nl);
if (lb == 1) tnl2.DrawText(xr2,yr,Form("%d bytes/alloc",nbmean));
else tnl2.DrawText(xr2,yr,Form("%d",nbmean));
}
tnl.DrawText(xr,ytop+0.015,"nallocs");
tnl.DrawText(1-fgC3->GetRightMargin(),0.5*ylow,"nbytes");
if (named) tmachine.DrawText(0.01,0.01,named->GetTitle());
}
void TMemStatShow::EventInfo1(Int_t event, Int_t px, Int_t , TObject *selected)
{
if (!fgTip1) return;
fgTip1->Hide();
if (event == kMouseLeave)
return;
Double_t xpx = fgC1->AbsPixeltoX(px);
Double_t xpx1 = fgC1->AbsPixeltoX(px+1);
Int_t bin = fgH->GetXaxis()->FindBin(xpx);
Int_t bin1 = fgH->GetXaxis()->FindBin(xpx1);
while (bin <= bin1) {
if (fgH->GetBinContent(bin) > 0) break;
bin++;
}
if (fgH->GetBinContent(bin) <= 0) return;
if (bin <=0 || bin > fgH->GetXaxis()->GetNbins()) return;
Double_t posmin = fgH->GetXaxis()->GetBinLowEdge(bin);
Double_t posmax = fgH->GetXaxis()->GetBinUpEdge(bin);
Int_t nsel = (Int_t)fgT->GetSelectedRows();
Int_t entry = 0;
Int_t nhits = 0;
Int_t nbytes = 0;
for (Int_t i=0;i<nsel;i++) {
if (fgV2[i] < 0) continue;
if (fgV1[i] < posmax && fgV1[i]+fgV2[i] >posmin) {
entry = i;
nbytes = (Int_t)fgV2[i];
nhits++;
}
}
if (!nhits) return;
Double_t time = 0.0001*fgV3[entry];
TString ttip;
TMemStatShow::FillBTString(entry,0,ttip);
if (selected) {
TString form1 = TString::Format(" Alloc(%d) at %lld of %d bytes, time=%gseconds\n\n",nhits,Long64_t(fgV1[entry]),nbytes,time);
fgTip1->SetText(TString::Format("%s%s",form1.Data(),ttip.Data() ));
fgTip1->SetPosition(px+15, 100);
fgTip1->Reset();
}
}
void TMemStatShow::EventInfo2(Int_t event, Int_t px, Int_t , TObject *selected)
{
if (!fgTip2) return;
fgTip2->Hide();
if (event == kMouseLeave)
return;
Double_t xpx = fgC2->AbsPixeltoX(px);
Int_t bin = fgHleaks->GetXaxis()->FindBin(xpx);
if (bin <=0 || bin > fgHleaks->GetXaxis()->GetNbins()) return;
Int_t nbytes = (Int_t)fgHleaks->GetBinContent(bin);
Int_t entry = (Int_t)fgHentry->GetBinContent(bin);
Double_t time = 0.0001*fgV3[entry];
TString ttip;
TMemStatShow::FillBTString(entry,0,ttip);
if (selected) {
TString form1 = TString::Format(" Leak number=%d, leaking %d bytes at entry=%d time=%gseconds\n\n",bin,nbytes,entry,time);
fgTip2->SetText(TString::Format("%s%s",form1.Data(),ttip.Data() ));
fgTip2->SetPosition(px+15, 100);
fgTip2->Reset();
}
}
void TMemStatShow::FillBTString(Int_t entry,Int_t mode,TString &btstring)
{
Int_t btid = (Int_t)fgV4[entry];
TH1I *hbtids = (TH1I*)fgT->GetUserInfo()->FindObject("btids");
if (!hbtids) return;
if (!fgBtidlist) fgBtidlist = (TObjArray*)fgT->GetUserInfo()->FindObject("FAddrsList");
if (!fgBtidlist) fgBtidlist = (TObjArray*)gFile->Get("FAddrsList");
if (!fgBtidlist) return;
Int_t nbt = (Int_t)hbtids->GetBinContent(btid-1);
for (Int_t i=0;i<nbt;i++) {
Int_t j = (Int_t)hbtids->GetBinContent(btid+i);
TNamed *nm = (TNamed*)fgBtidlist->At(j);
if (nm==0) break;
char *title = (char*)nm->GetTitle();
Int_t nch = strlen(title);
if (nch < 10) continue;
if (strstr(title,"malloc")) continue;
if (strstr(title,"memstat")) continue;
if (strstr(title,"TMemStatHook")) continue;
char *bar = strchr(title+5,'|');
if (!bar) bar = title;
if (strstr(bar,"operator new")) continue;
if (strstr(bar,"libMemStat")) continue;
if (strstr(bar,"G__Exception")) continue;
if (mode) {
btstring += TString::Format("%s ",bar);
if (btstring.Length() > 80) return;
} else {
btstring += TString::Format("%2d %s\n",i,bar+1);
}
}
}