forked from root-project/root
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmemstatExample.C
More file actions
366 lines (348 loc) · 12.5 KB
/
memstatExample.C
File metadata and controls
366 lines (348 loc) · 12.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
/// \file
/// \ingroup tutorial_memstat
/// Script post-processing the file generated by TMemStat (default memstat.root)
///
/// To use the class TMemStat, add the following statement at the beginning
/// of your script or program
///
/// ~~~ {.cpp}
/// TMemStat mm("gnubuiltin");
/// ~~~
///
/// or in an interactive session do something like:
///
/// ~~~ {.cpp}
/// root > TMemStat mm("gnubuiltin");
/// root > .x somescript.C
/// root > .q
/// ~~~
///
/// TMemStat records all the calls to malloc and free and write a TTree
/// with the position where the memory is allocated/freed , as well as
/// the number of bytes.
///
/// This script creates 2 canvases.
///
/// - In canvas1 it displays a dynamic histogram showing for pages (10 kbytes by default)
/// the percentage of the page used.
/// A summary pave shows the total memory still in use when the TMemStat object
/// goes out of scope and the average occupancy of the pages.
/// The average occupancy gives a good indication of the memory fragmentation.
///
/// - In canvas2 it displays the histogram of memory leaks in decreasing order.
/// when moving the mouse on this canvas, a tooltip shows the backtrace for the leak
/// in the bin below the mouse.
///
/// The script can be executed simply as
///
/// ~~~ {.cpp}
/// root > .x memstat.C (or via ACLIC .x memstat.C+ )
/// ~~~
///
/// or specifying arguments
///
/// ~~~ {.cpp}
/// root > .x memstat.C+(0.01,"mydir/mymemstat.root");
/// ~~~
///
/// The first argument to the script is the percentage of the time of the original job
/// that produced the file after which the display is updated. By default update=0.01,
/// ie 100 time intervals will be shown.
/// The second argument is the input file name (result of TMemStat).
/// If this argument is omitted, the script will take the most recent file
/// generated by TMemStat.
///
/// \macro_image
/// \macro_output
/// \macro_code
///
/// \author Rene Brun 7 July 2010
#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"
TFile *f;
TTree *T;
TH1D *h;
TH1D *halloc, *hfree;
TH1I *hleaks, *hentry;
TGToolTip *gTip = 0;
TObjArray *btidlist=0;
Double_t *V1, *V2, *V3, *V4;
void EventInfo(Int_t event, Int_t px, Int_t py, TObject *selected);
void memstatExample(double update=0.01, const char* fname="*") {
// Open the memstat data file, then call TTree::Draw to precompute
// the arrays of positions and nbytes per entry.
// update is the time interval in the data file in seconds after which
// the display is updated. For example is the job producing the memstat.root file
// took 100s to execute, an update of 0.1s will generate 1000 time views of
// the memory use.
// if fname=="*" (default), the most recent file memstat*.root will be taken.
TString s;
if (!fname || strlen(fname) <5 || strstr(fname,"*")) {
//take the most recent file memstat*.root
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);
f = TFile::Open(fname);
if (!f) {
printf("Cannot open file %s\n",fname);
return;
}
T = (TTree*)f->Get("T");
if (!T) {
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");
Long64_t nentries = T->GetEntries();
T->SetEstimate(nentries+10);
Long64_t nsel = T->Draw("pos:nbytes:time:btid","","goff");
//now we compute the best binning for the histogram
Int_t nbytes;
Double_t pos;
V1 = T->GetV1();
V2 = T->GetV2();
V3 = T->GetV3();
V4 = T->GetV4();
Long64_t imean = (Long64_t)TMath::Mean(nsel,V1);
Long64_t irms = (Long64_t)TMath::RMS(nsel,V1);
//Long64_t bw = 10000;
Long64_t bw = 1000;
imean = imean - imean%bw;
irms = irms -irms%bw;
Int_t nbins = Int_t(4*irms/bw);
Long64_t ivmin = imean -bw*nbins/2;
Long64_t ivmax = ivmin+bw*nbins;
if (ivmax > 2000000000 && ivmin <2000000000) {
//the data set has been likely generated on a 32 bits machine
//we are mostly interested by the small allocations, so we select
//only values below 2 GBytes
printf("memory locations above 2GBytes will be ignored\n");
nsel = T->Draw("pos:nbytes:time:btid","pos <2e9","goff");
V1 = T->GetV1();
V2 = T->GetV2();
V3 = T->GetV3();
V4 = T->GetV4();
imean = (Long64_t)TMath::Mean(nsel,V1);
irms = (Long64_t)TMath::RMS(nsel,V1);
bw = 10000;
imean = imean - imean%bw;
irms = irms -irms%bw;
nbins = Int_t(4*irms/bw);
ivmin = imean -bw*nbins/2;
ivmax = ivmin+bw*nbins;
}
update *= 0.0001*V3[nsel-1]; //convert time per cent in seconds
Long64_t nvm = Long64_t(ivmax-ivmin+1);
Long64_t *nbold = new Long64_t[nvm];
Int_t *ientry = new Int_t[nvm];
memset(nbold,0,nvm*8);
Double_t dv = (ivmax-ivmin)/nbins;
h = new TH1D("h",Form("%s;pos;per cent of pages used",fname),nbins,ivmin,ivmax);
TAxis *axis = h->GetXaxis();
gStyle->SetOptStat("ie");
h->SetFillColor(kRed);
h->SetMinimum(0);
h->SetMaximum(100);
halloc = new TH1D("halloc",Form("%s;pos;number of mallocs",fname),nbins,ivmin,ivmax);
hfree = new TH1D("hfree", Form("%s;pos;number of frees",fname),nbins,ivmin,ivmax);
//open a canvas and draw the empty histogram
TCanvas *c1 = new TCanvas("c1","c1",1200,600);
c1->SetFrameFillColor(kYellow-3);
c1->SetGridx();
c1->SetGridy();
h->Draw();
//create a TPaveText to show the summary results
TPaveText *pvt = new TPaveText(.5,.9,.75,.99,"brNDC");
pvt->Draw();
//create a TPaveLabel to show the time
TPaveLabel *ptime = new TPaveLabel(.905,.7,.995,.76,"time","brNDC");
ptime->SetFillColor(kYellow-3);
ptime->Draw();
//draw producer identifier
TNamed *named = (TNamed*)T->GetUserInfo()->FindObject("SysInfo");
TText tmachine;
tmachine.SetTextSize(0.02);
tmachine.SetNDC();
if (named) tmachine.DrawText(0.01,0.01,named->GetTitle());
//start loop on selected rows
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 = V1[i];
ipos = (Long64_t)(pos-ivmin);
nbytes = (Int_t)V2[i];
time = 0.0001*V3[i];
bin = axis->FindBin(pos);
if (bin<1 || bin>nbins) continue;
dbin = axis->GetBinUpEdge(bin)-pos;
if (nbytes > 0) {
halloc->Fill(pos);
if (dbin > nbytes) dbin = nbytes;
//fill bytes in the first page
h->AddBinContent(bin,100*dbin/dv);
//fill bytes in full following pages
nb = Int_t((nbytes-dbin)/dv);
if (bin+nb >nbins) nb = nbins-bin;
for (j=1;j<=nb;j++) h->AddBinContent(bin+j,100);
//fill the bytes remaining in last page
rest = nbytes-nb*dv-dbin;
if (rest > 0) h->AddBinContent(bin+nb+1,100*rest/dv);
//we save nbytes at pos. This info will be used when we free this slot
if (nbold[ipos] > 0) printf("reallocating %d bytes (was %lld) at %lld, entry=%d\n",nbytes,nbold[ipos],ipos,i);
if (nbold[ipos] == 0) {
nleaks++;
//save the Tree entry number where we made this allocation
ientry[ipos] = i;
}
nbold[ipos] = nbytes;
} else {
hfree->Fill(pos);
nbytes = nbold[ipos];
if (bin+nb >nbins) nb = nbins-bin;
nbold[ipos] = 0; nleaks--;
if (nbytes <= 0) continue;
//fill bytes free in the first page
if (dbin > nbytes) dbin = nbytes;
h->AddBinContent(bin,-100*dbin/dv);
//fill bytes free in full following pages
nb = Int_t((nbytes-dbin)/dv);
if (bin+nb >nbins) nb = nbins-bin;
for (j=1;j<=nb;j++) h->AddBinContent(bin+j,-100);
//fill the bytes free in in last page
rest = nbytes-nb*dv-dbin;
if (rest > 0) h->AddBinContent(bin+nb+1,-100*rest/dv);
}
if (time -updateLast > update) {
//update canvas at regular intervals
updateLast = time;
h->SetEntries(i);
c1->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));
c1->Update();
gSystem->ProcessEvents();
}
}
h->SetEntries(nsel);
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);
hentry = new TH1I("hentry","leak entry index",nleaks,0,nleaks);
hleaks = new TH1I("hleaks","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];
hentry->SetBinContent(k+1,i);
hleaks->SetBinContent(k+1,ileaks[kk]);
}
hentry->SetEntries(nleaks);
hleaks->SetEntries(nleaks);
//open a second canvas and draw the histogram with leaks in decreasing order
TCanvas *c2 = new TCanvas("c2","c2",1200,600);
c2->SetFrameFillColor(kCyan-6);
c2->SetGridx();
c2->SetGridy();
c2->SetLogy();
hleaks->SetFillColor(kRed-3);
if (nleaks > 1000) hleaks->GetXaxis()->SetRange(1,1000);
hleaks->Draw();
//draw producer identifier
if (named) tmachine.DrawText(0.01,0.01,named->GetTitle());
//construct the tooltip
TRootCanvas *rc = (TRootCanvas *)c2->GetCanvasImp();
TGMainFrame *frm = dynamic_cast<TGMainFrame *>(rc);
// create the tooltip with a timeout of 250 ms
if (!gTip) gTip = new TGToolTip(gClient->GetDefaultRoot(), frm, "", 250);
c2->Connect("ProcessedEvent(Int_t, Int_t, Int_t, TObject*)",
0, 0, "EventInfo(Int_t, Int_t, Int_t, TObject*)");
}
//______________________________________________________________________
void EventInfo(Int_t event, Int_t px, Int_t , TObject *selected)
{
//draw the tooltip showing the backtrace for the bin at px
if (!gTip) return;
gTip->Hide();
if (event == kMouseLeave)
return;
Double_t xpx = gPad->AbsPixeltoX(px);
Int_t bin = hleaks->GetXaxis()->FindBin(xpx);
if (bin <=0 || bin > hleaks->GetXaxis()->GetNbins()) return;
Int_t nbytes = (Int_t)hleaks->GetBinContent(bin);
Int_t entry = (Int_t)hentry->GetBinContent(bin);
Int_t btid = (Int_t)V4[entry];
Double_t time = 0.0001*V3[entry];
TH1I *hbtids = (TH1I*)T->GetUserInfo()->FindObject("btids");
if (!hbtids) return;
if (!btidlist) btidlist = (TObjArray*)T->GetUserInfo()->FindObject("FAddrsList");
if (!btidlist) btidlist = (TObjArray*)f->Get("FAddrsList"); //old memstat files
if (!btidlist) return;
Int_t nbt = (Int_t)hbtids->GetBinContent(btid-1);
TString ttip;
for (Int_t i=0;i<nbt;i++) {
Int_t j = (Int_t)hbtids->GetBinContent(btid+i);
TNamed *nm = (TNamed*)btidlist->At(j);
if (nm==0) break;
char *title = (char*)nm->GetTitle();
Int_t nch = strlen(title);
if (nch < 20) continue;
if (nch > 100) title[100] =0;
const char *bar = strstr(title,"| ");
if (!bar) continue;
if (strstr(bar,"operator new")) continue;
if (strstr(bar,"libMemStat")) continue;
if (strstr(bar,"G__Exception")) continue;
ttip += TString::Format("%2d %s\n",i,bar+1);
}
if (selected) {
TString form1 = TString::Format(" Leak number=%d, leaking %d bytes at entry=%d time=%gseconds\n\n",bin,nbytes,entry,time);
gTip->SetText(TString::Format("%s%s",form1.Data(),ttip.Data() ));
gTip->SetPosition(px+15, 100);
gTip->Reset();
}
}