memstat.C

Go to the documentation of this file.
00001 // script post-processing the file generated by TMemStat (default memstat.root)
00002 //
00003 // To use the class TMemStat, add the following statement at the beginning
00004 // of your script or program
00005 //     TMemStat mm("gnubuiltin");
00006 // or in an interactive session do something like:
00007 //    root > TMemStat mm("gnubuiltin");
00008 //    root > .x somescript.C
00009 //    root > .q
00010 // TMemStat records all the calls to malloc and free and write a TTree
00011 // with the position where the memory is allocated/freed , as well as
00012 // the number of bytes.
00013 //
00014 // This script creates 2 canvases.
00015 // -In canvas1 it displays a dynamic histogram showing for pages (10 kbytes by default)
00016 //  the percentage of the page used.
00017 //  A summary pave shows the total memory still in use when the TMemStat object
00018 //  goes out of scope and the average occupancy of the pages.
00019 //  The average occupancy gives a good indication of the memory fragmentation.
00020 //
00021 // -In canvas2 it displays the histogram of memory leaks in decreasing order.
00022 //  when moving the mouse on this canvas, a tooltip shows the backtrace for the leak
00023 //  in the bin below the mouse.
00024 //
00025 // The script can be executed simply as
00026 //   root > .x memstat.C   (or via ACLIC  .x memstat.C+ )
00027 // or specifying arguments
00028 //   root > .x memstat.C+(0.01,"mydir/mymemstat.root");
00029 //
00030 // The first argument to the script is the percentage of the time of the original job
00031 // that produced the file after which the display is updated. By default update=0.01,
00032 // ie 100 time intervals will be shown.
00033 // The second argument is the imput file name (result of TMemStat).
00034 // If this argument is omitted, the script will take the most recent file
00035 // generated by TMemStat.
00036 //
00037 //Author: Rene Brun 7 July 2010
00038       
00039 #include "TMath.h"
00040 #include "TFile.h"
00041 #include "TTree.h"
00042 #include "TCanvas.h"
00043 #include "TStyle.h"
00044 #include "TH1.h"
00045 #include "TPaveText.h"
00046 #include "TPaveLabel.h"
00047 #include "TSystem.h"
00048 #include "TGClient.h"
00049 #include "TGToolTip.h"
00050 #include "TRootCanvas.h"
00051 
00052 TFile *f;
00053 TTree *T;
00054 TH1D *h;
00055 TH1D *halloc, *hfree;
00056 TH1I *hleaks, *hentry;
00057 TGToolTip *gTip = 0;
00058 TObjArray *btidlist=0;
00059 Double_t *V1, *V2, *V3, *V4;
00060 
00061 void EventInfo(Int_t event, Int_t px, Int_t py, TObject *selected);
00062 
00063 void memstat(double update=0.01, const char* fname="*") {
00064    // Open the memstat data file, then call TTree::Draw to precompute
00065    // the arrays of positions and nbytes per entry.
00066    // update is the time interval in the data file  in seconds after which
00067    // the display is updated. For example is the job producing the memstat.root file
00068    // took 100s to execute, an update of 0.1s will generate 1000 time views of
00069    // the memory use.
00070    // if fname=="*" (default), the most recent file memstat*.root will be taken.
00071    
00072    TString s;
00073    if (!fname || strlen(fname) <5 || strstr(fname,"*")) {
00074       //take the most recent file memstat*.root
00075       s = gSystem->GetFromPipe("ls -lrt memstat*.root");
00076       Int_t ns = s.Length();
00077       fname = strstr(s.Data()+ns-25,"memstat");
00078    }
00079    printf("Analyzing file: %s\n",fname);
00080    f = TFile::Open(fname);
00081    if (!f) {
00082       printf("Cannot open file %s\n",fname);
00083       return;
00084    }
00085    T = (TTree*)f->Get("T");
00086    if (!T) {
00087       printf("cannot find the TMemStat TTree named T in file %s\n",fname);
00088       return;
00089    }
00090    if (update <= 0) {
00091       printf("Illegal update value %g, changed to 0.01\n",update);
00092       update = 0.01;
00093    }
00094    if (update < 0.001) printf("Warning update parameter is very small, processing may be slow\n");
00095    
00096    
00097    Long64_t nentries = T->GetEntries();
00098    T->SetEstimate(nentries+10);
00099    Long64_t nsel = T->Draw("pos:nbytes:time:btid","","goff");
00100    
00101    //now we compute the best binning for the histogram
00102    Int_t nbytes;
00103    Double_t pos;
00104    V1 = T->GetV1();
00105    V2 = T->GetV2();
00106    V3 = T->GetV3();
00107    V4 = T->GetV4();
00108    Long64_t imean = (Long64_t)TMath::Mean(nsel,V1);
00109    Long64_t irms  = (Long64_t)TMath::RMS(nsel,V1);
00110    //Long64_t bw = 10000;
00111    Long64_t bw = 1000;
00112    imean = imean - imean%bw;
00113    irms = irms -irms%bw;
00114    Int_t nbins = Int_t(4*irms/bw);
00115    Long64_t ivmin = imean -bw*nbins/2;
00116    Long64_t ivmax = ivmin+bw*nbins;
00117    if (ivmax > 2000000000 && ivmin <2000000000) {
00118       //the data set has been likely generated on a 32 bits machine
00119       //we are mostly interested by the small allocations, so we select
00120       //only values below 2 GBytes
00121       printf("memory locations above 2GBytes will be ignored\n");
00122       nsel = T->Draw("pos:nbytes:time:btid","pos <2e9","goff");
00123       V1 = T->GetV1();
00124       V2 = T->GetV2();
00125       V3 = T->GetV3();
00126       V4 = T->GetV4();
00127       imean = (Long64_t)TMath::Mean(nsel,V1);
00128       irms = (Long64_t)TMath::RMS(nsel,V1);
00129       bw = 10000;
00130       imean = imean - imean%bw;
00131       irms = irms -irms%bw;
00132       nbins = Int_t(4*irms/bw);
00133       ivmin = imean -bw*nbins/2;
00134       ivmax = ivmin+bw*nbins;
00135    } 
00136    update *= 0.0001*V3[nsel-1]; //convert time per cent in seconds
00137    Long64_t nvm = Long64_t(ivmax-ivmin+1);
00138    Long64_t *nbold = new Long64_t[nvm];
00139    Int_t *ientry  = new Int_t[nvm];
00140    memset(nbold,0,nvm*8);
00141    Double_t dv = (ivmax-ivmin)/nbins;
00142    h = new TH1D("h",Form("%s;pos;per cent of pages used",fname),nbins,ivmin,ivmax);
00143    TAxis *axis = h->GetXaxis();
00144    gStyle->SetOptStat("ie");
00145    h->SetFillColor(kRed);
00146    h->SetMinimum(0);
00147    h->SetMaximum(100);
00148    halloc = new TH1D("halloc",Form("%s;pos;number of mallocs",fname),nbins,ivmin,ivmax);
00149    hfree  = new TH1D("hfree", Form("%s;pos;number of frees",fname),nbins,ivmin,ivmax);
00150    //open a canvas and draw the empty histogram
00151    TCanvas *c1 = new TCanvas("c1","c1",1200,600);
00152    c1->SetFrameFillColor(kYellow-3);
00153    c1->SetGridx();
00154    c1->SetGridy();
00155    h->Draw();
00156    //create a TPaveText to show the summary results
00157    TPaveText *pvt = new TPaveText(.5,.9,.75,.99,"brNDC");
00158    pvt->Draw();
00159    //create a TPaveLabel to show the time
00160    TPaveLabel *ptime = new TPaveLabel(.905,.7,.995,.76,"time","brNDC");
00161    ptime->SetFillColor(kYellow-3);
00162    ptime->Draw();
00163    //draw producer identifier
00164    TNamed *named = (TNamed*)T->GetUserInfo()->FindObject("SysInfo");
00165    TText tmachine;
00166    tmachine.SetTextSize(0.02);
00167    tmachine.SetNDC();
00168    if (named) tmachine.DrawText(0.01,0.01,named->GetTitle());
00169 
00170    //start loop on selected rows
00171    Int_t bin,nb=0,j;
00172    Long64_t ipos;
00173    Double_t dbin,rest,time;
00174    Double_t updateLast = 0;
00175    Int_t nleaks = 0;
00176    Int_t i;
00177    for (i=0;i<nsel;i++) {
00178       pos    = V1[i];
00179       ipos = (Long64_t)(pos-ivmin);
00180       nbytes = (Int_t)V2[i];
00181       time = 0.0001*V3[i];
00182       bin = axis->FindBin(pos);
00183       if (bin<1 || bin>nbins) continue;
00184       dbin = axis->GetBinUpEdge(bin)-pos;
00185       if (nbytes > 0) {
00186          halloc->Fill(pos);
00187          if (dbin > nbytes) dbin = nbytes;
00188          //fill bytes in the first page
00189          h->AddBinContent(bin,100*dbin/dv);
00190          //fill bytes in full following pages
00191          nb = Int_t((nbytes-dbin)/dv);
00192          if (bin+nb >nbins) nb = nbins-bin;
00193          for (j=1;j<=nb;j++) h->AddBinContent(bin+j,100);
00194          //fill the bytes remaining in last page
00195          rest = nbytes-nb*dv-dbin;
00196          if (rest > 0) h->AddBinContent(bin+nb+1,100*rest/dv);
00197          //we save nbytes at pos. This info will be used when we free this slot
00198          if (nbold[ipos] > 0) printf("reallocating %d bytes (was %lld) at %lld, entry=%d\n",nbytes,nbold[ipos],ipos,i);
00199          if (nbold[ipos] == 0) {
00200             nleaks++;            
00201             //save the Tree entry number where we made this allocation
00202             ientry[ipos] = i;
00203          }
00204          nbold[ipos] = nbytes;
00205       } else {
00206          hfree->Fill(pos);
00207          nbytes = nbold[ipos];
00208          if (bin+nb >nbins) nb = nbins-bin;
00209          nbold[ipos] = 0; nleaks--;
00210          if (nbytes <= 0) continue;
00211          //fill bytes free in the first page
00212          if (dbin > nbytes) dbin = nbytes;
00213          h->AddBinContent(bin,-100*dbin/dv);
00214          //fill bytes free in full following pages
00215          nb = Int_t((nbytes-dbin)/dv);
00216          if (bin+nb >nbins) nb = nbins-bin;
00217          for (j=1;j<=nb;j++) h->AddBinContent(bin+j,-100);
00218          //fill the bytes free in  in last page
00219          rest = nbytes-nb*dv-dbin;
00220          if (rest > 0) h->AddBinContent(bin+nb+1,-100*rest/dv);
00221 
00222       }
00223       if (time -updateLast > update) {
00224          //update canvas at regular intervals
00225          updateLast = time;
00226          h->SetEntries(i);
00227          c1->Modified();
00228          pvt->GetListOfLines()->Delete();
00229          Double_t mbytes = 0;
00230          Int_t nonEmpty = 0;
00231          Double_t w;
00232          for (Int_t k=1;k<nbins;k++) {
00233             w = h->GetBinContent(k);
00234             if (w > 0) {
00235                nonEmpty++;
00236                mbytes += 0.01*w*dv;
00237             }
00238          }
00239          Double_t occupancy = mbytes/(nonEmpty*0.01*dv);
00240          pvt->AddText(Form("memory used = %g Mbytes",mbytes*1e-6));
00241          pvt->AddText(Form("page occupancy = %f per cent",occupancy));
00242          pvt->AddText("(for non empty pages only)");
00243          ptime->SetLabel(Form("%g sec",time));
00244          
00245          c1->Update();
00246          gSystem->ProcessEvents();
00247       }
00248    }
00249    h->SetEntries(nsel);
00250    Int_t nlmax = nleaks;
00251    nleaks += 1000;
00252    Int_t *lindex  = new Int_t[nleaks];
00253    Int_t *entry   = new Int_t[nleaks];
00254    Int_t *ileaks  = new Int_t[nleaks];
00255 
00256    nleaks =0;
00257    for (Int_t ii=0;ii<nvm;ii++) {
00258       if (nbold[ii] > 0) {
00259          ileaks[nleaks] = (Int_t)nbold[ii];
00260          entry[nleaks]  = ientry[ii];
00261          nleaks++;
00262          if (nleaks > nlmax) break;
00263       }
00264    }
00265 
00266    TMath::Sort(nleaks,ileaks,lindex);
00267    hentry = new TH1I("hentry","leak entry index",nleaks,0,nleaks);
00268    hleaks = new TH1I("hleaks","leaks;leak number;nbytes in leak",nleaks,0,nleaks);
00269    for (Int_t k=0;k<nleaks;k++) {
00270       Int_t kk = lindex[k];
00271       i = entry[kk];
00272       hentry->SetBinContent(k+1,i);
00273       hleaks->SetBinContent(k+1,ileaks[kk]);
00274    }
00275    hentry->SetEntries(nleaks);
00276    hleaks->SetEntries(nleaks);
00277    
00278    //open a second canvas and draw the histogram with leaks in decreasing order
00279    TCanvas *c2 = new TCanvas("c2","c2",1200,600);
00280    c2->SetFrameFillColor(kCyan-6);
00281    c2->SetGridx();
00282    c2->SetGridy();
00283    c2->SetLogy();
00284    hleaks->SetFillColor(kRed-3);
00285    if (nleaks > 1000) hleaks->GetXaxis()->SetRange(1,1000);
00286    hleaks->Draw();
00287    //draw producer identifier
00288    if (named) tmachine.DrawText(0.01,0.01,named->GetTitle());
00289    
00290    //construct the tooltip
00291    TRootCanvas *rc = (TRootCanvas *)c2->GetCanvasImp();
00292    TGMainFrame *frm = dynamic_cast<TGMainFrame *>(rc);
00293    // create the tooltip with a timeout of 250 ms
00294    if (!gTip) gTip = new TGToolTip(gClient->GetDefaultRoot(), frm, "", 250);
00295    c2->Connect("ProcessedEvent(Int_t, Int_t, Int_t, TObject*)",
00296                0, 0, "EventInfo(Int_t, Int_t, Int_t, TObject*)");
00297 
00298 }
00299 
00300 //______________________________________________________________________
00301 void EventInfo(Int_t event, Int_t px, Int_t , TObject *selected)
00302 {
00303    //draw the tooltip showing the backtrace for the bin at px
00304    if (!gTip) return;
00305    gTip->Hide();
00306    if (event == kMouseLeave)
00307       return;
00308    Double_t xpx  = gPad->AbsPixeltoX(px);
00309    Int_t bin = hleaks->GetXaxis()->FindBin(xpx);
00310    if (bin <=0 || bin > hleaks->GetXaxis()->GetNbins()) return;
00311    Int_t nbytes = (Int_t)hleaks->GetBinContent(bin);
00312    Int_t entry  = (Int_t)hentry->GetBinContent(bin);
00313    Int_t btid   = (Int_t)V4[entry];
00314    Double_t  time = 0.0001*V3[entry];
00315    TH1I *hbtids = (TH1I*)T->GetUserInfo()->FindObject("btids");
00316    if (!hbtids) return;
00317    if (!btidlist) btidlist = (TObjArray*)T->GetUserInfo()->FindObject("FAddrsList");
00318    if (!btidlist) btidlist = (TObjArray*)f->Get("FAddrsList"); //old memstat files
00319    if (!btidlist) return;
00320    Int_t nbt = (Int_t)hbtids->GetBinContent(btid-1);
00321    TString ttip;
00322    for (Int_t i=0;i<nbt;i++) {
00323       Int_t j = (Int_t)hbtids->GetBinContent(btid+i);
00324       TNamed *nm = (TNamed*)btidlist->At(j);
00325       if (nm==0) break;
00326       char *title = (char*)nm->GetTitle();
00327       Int_t nch = strlen(title);
00328       if (nch < 20) continue;
00329       if (nch > 100) title[100] =0;
00330       const char *bar = strstr(title,"| ");
00331       if (!bar) continue;
00332       if (strstr(bar,"operator new")) continue;
00333       if (strstr(bar,"libMemStat")) continue;
00334       if (strstr(bar,"G__Exception")) continue;
00335       ttip += TString::Format("%2d %s\n",i,bar+1);
00336    }
00337    
00338    if (selected) {
00339       TString form1 = TString::Format("  Leak number=%d, leaking %d bytes at entry=%d    time=%gseconds\n\n",bin,nbytes,entry,time);
00340       gTip->SetText(TString::Format("%s%s",form1.Data(),ttip.Data() ));
00341       gTip->SetPosition(px+15, 100);
00342       gTip->Reset();
00343    }
00344 }

Generated on Tue Jul 5 15:44:51 2011 for ROOT_528-00b_version by  doxygen 1.5.1