Logo ROOT   6.12/07
Reference Guide
TMemStatMng.cxx
Go to the documentation of this file.
1 // @(#)root/memstat:$Id$
2 // Author: Anar Manafov (A.Manafov@gsi.de) 2008-03-02
3 
4 /*************************************************************************
5 * Copyright (C) 1995-2010, Rene Brun and Fons Rademakers. *
6 * All rights reserved. *
7 * *
8 * For the licensing terms see $ROOTSYS/LICENSE. *
9 * For the list of contributors see $ROOTSYS/README/CREDITS. *
10 *************************************************************************/
11 // STD
12 #include <cstdlib>
13 // ROOT
14 #include "TSystem.h"
15 #include "TEnv.h"
16 #include "TError.h"
17 #include "Riostream.h"
18 #include "TObject.h"
19 #include "TFile.h"
20 #include "TTree.h"
21 #include "TArrayL64.h"
22 #include "TH1.h"
23 #include "TMD5.h"
24 #include "TMath.h"
25 // Memstat
26 #include "TMemStatBacktrace.h"
27 #include "TMemStatMng.h"
28 
29 using namespace Memstat;
30 
32 
34 
35 //****************************************************************************//
36 //
37 //****************************************************************************//
38 
40  TObject(),
41 #if !defined(__APPLE__)
42  fPreviousMallocHook(TMemStatHook::GetMallocHook()),
43  fPreviousFreeHook(TMemStatHook::GetFreeHook()),
44 #endif
45  fDumpFile(NULL),
46  fDumpTree(NULL),
47  fUseGNUBuiltinBacktrace(kFALSE),
48  fBeginTime(0),
49  fPos(0),
50  fTimems(0),
51  fNBytes(0),
52  fBtID(0),
53  fMaxCalls(5000000),
54  fBufferSize(10000),
55  fBufN(0),
56  fBufPos(0),
57  fBufTimems(0),
58  fBufNBytes(0),
59  fBufBtID(0),
60  fIndex(0),
61  fMustWrite(0),
62  fFAddrsList(0),
63  fHbtids(0),
64  fBTCount(0),
65  fBTIDCount(0),
66  fSysInfo(0)
67 {
68  // Default constructor
69 }
70 
71 ////////////////////////////////////////////////////////////////////////////////
72 ///Initialize MemStat manager - used only by instance method
73 
75 {
77 
78  fDumpFile = new TFile(Form("memstat_%d.root", gSystem->GetPid()), "recreate");
79  Int_t opt = 200000;
80  if(!fDumpTree) {
81  fDumpTree = new TTree("T", "Memory Statistics");
82  fDumpTree->Branch("pos", &fPos, "pos/l", opt);
83  fDumpTree->Branch("time", &fTimems, "time/I", opt);
84  fDumpTree->Branch("nbytes", &fNBytes, "nbytes/I", opt);
85  fDumpTree->Branch("btid", &fBtID, "btid/I", opt);
86  }
87 
88  fBTCount = 0;
89 
90  fBTIDCount = 0;
91 
92  fFAddrsList = new TObjArray();
94  fFAddrsList->SetName("FAddrsList");
95 
96  fHbtids = new TH1I("btids", "table of btids", 10000, 0, 1); //where fHbtids is a member of the manager class
98  // save the histogram and the TObjArray to the tree header
99  fDumpTree->GetUserInfo()->Add(fHbtids);
100  fDumpTree->GetUserInfo()->Add(fFAddrsList);
101  // save the system info to a tree header
102  std::string sSysInfo(gSystem->GetBuildNode());
103  sSysInfo += " | ";
104  sSysInfo += gSystem->GetBuildCompilerVersion();
105  sSysInfo += " | ";
106  sSysInfo += gSystem->GetFlagsDebug();
107  sSysInfo += " ";
108  sSysInfo += gSystem->GetFlagsOpt();
109  fSysInfo = new TNamed("SysInfo", sSysInfo.c_str());
110 
111  fDumpTree->GetUserInfo()->Add(fSysInfo);
112  fDumpTree->SetAutoSave(10000000);
113 }
114 
115 ////////////////////////////////////////////////////////////////////////////////
116 /// GetInstance - a static function
117 /// Initialize a singleton of MemStat manager
118 
120 {
121  if(!fgInstance) {
122  fgInstance = new TMemStatMng;
123  fgInstance->Init();
124  }
125  return fgInstance;
126 }
127 
128 ////////////////////////////////////////////////////////////////////////////////
129 /// Close - a static function
130 /// This method stops the manager,
131 /// flashes all the buffered data and closes the output tree.
132 
134 {
135  // TODO: This is a temporary solution until we find a properalgorithm for SaveData
136  //fgInstance->fDumpFile->WriteObject(fgInstance->fFAddrsList, "FAddrsList");
137 
138 /* std::ofstream f("mem_stat_debug.txt");
139  int *btids = fgInstance->fHbtids->GetArray();
140  if( !btids )
141  return;
142  int btid(1);
143  int count(0);
144  bool bStop(false);
145  int count_empty(0);
146  for (int i = 0; i < fgInstance->fBTChecksums.size(); ++i)
147  {
148  if (bStop)
149  break;
150  count = btids[btid-1];
151  f << "++++++++++++++++++++++++\n";
152  f << "BTID: " << btid << "\n";
153  if ( count <= 0 )
154  ++count_empty;
155  for (int j = btid+1; j <= (btid+count); ++j )
156  {
157  TNamed *nm = (TNamed*)fgInstance->fFAddrsList->At(btids[j]);
158  if( !nm )
159  {
160  f << "Bad ID" << std::endl;
161  bStop = true;
162  }
163  f << "-------> " << nm->GetTitle() << "\n";
164  }
165  btid = btid + count + 1;
166  }
167  f.close();
168  ::Info("TMemStatMng::Close", "btids without a stack %d\n", count_empty);
169 */
170 
171  // to be documented
172  fgInstance->FillTree();
173  fgInstance->Disable();
174  fgInstance->fDumpTree->AutoSave();
175  fgInstance->fDumpTree->GetUserInfo()->Delete();
176 
177  ::Info("TMemStatMng::Close", "Tree saved to file %s\n", fgInstance->fDumpFile->GetName());
178  ::Info("TMemStatMng::Close", "Tree entries = %d, file size = %g MBytes\n", (Int_t)fgInstance->fDumpTree->GetEntries(),1e-6*Double_t(fgInstance->fDumpFile->GetEND()));
179 
180  delete fgInstance->fDumpFile;
181  //fgInstance->fDumpFile->Close();
182  //delete fgInstance->fFAddrsList;
183  //delete fgInstance->fSysInfo;
184 
185  delete fgInstance;
186  fgInstance = NULL;
187 }
188 
189 ////////////////////////////////////////////////////////////////////////////////
190 /// if an instance is destructed - the hooks are reseted to old hooks
191 
193 {
194  if(this != TMemStatMng::GetInstance())
195  return;
196 
197  Info("~TMemStatMng", ">>> All free/malloc calls count: %d", fBTIDCount);
198  Info("~TMemStatMng", ">>> Unique BTIDs count: %zu", fBTChecksums.size());
199 
200  Disable();
201 }
202 
203 ////////////////////////////////////////////////////////////////////////////////
204 /// Set the maximum number of alloc/free calls to be buffered.
205 ///if the alloc and free are in the buffer, the corresponding entries
206 ///are not saved tio the Tree, reducing considerably the Tree output size
207 
209 {
210  fBufferSize = buffersize;
211  if (fBufferSize < 1) fBufferSize = 1;
212  fBufN = 0;
216  fBufBtID = new Int_t[fBufferSize];
217  fIndex = new Int_t[fBufferSize];
219 }
220 
221 ////////////////////////////////////////////////////////////////////////////////
222 /// Set the maximum number of new/delete registered in the output Tree.
223 
225 {
226  fMaxCalls = maxcalls;
227 }
228 
229 ////////////////////////////////////////////////////////////////////////////////
230 /// Enable memory hooks
231 
233 {
234  if(this != GetInstance())
235  return;
236 #if defined(__APPLE__)
237  TMemStatHook::trackZoneMalloc(MacAllocHook, MacFreeHook);
238 #else
239  // set hook to our functions
242 #endif
243 }
244 
245 ////////////////////////////////////////////////////////////////////////////////
246 /// Disble memory hooks
247 
249 {
250  //FillTree();
251  if(this != GetInstance())
252  return;
253 #if defined(__APPLE__)
254  TMemStatHook::untrackZoneMalloc();
255 #else
256  // set hook to our functions
259 #endif
260 }
261 
262 ////////////////////////////////////////////////////////////////////////////////
263 /// AllocHook - a static function
264 /// a special memory hook for Mac OS X memory zones.
265 /// Triggered when memory is allocated.
266 
267 void TMemStatMng::MacAllocHook(void *ptr, size_t size)
268 {
270  // Restore all old hooks
271  instance->Disable();
272 
273  // Call our routine
274  instance->AddPointer(ptr, Int_t(size));
275 
276  // Restore our own hooks
277  instance->Enable();
278 }
279 
280 ////////////////////////////////////////////////////////////////////////////////
281 /// AllocHook - a static function
282 /// a special memory hook for Mac OS X memory zones.
283 /// Triggered when memory is deallocated.
284 
286 {
288  // Restore all old hooks
289  instance->Disable();
290 
291  // Call our routine
292  instance->AddPointer(ptr, -1);
293 
294  // Restore our own hooks
295  instance->Enable();
296 }
297 
298 ////////////////////////////////////////////////////////////////////////////////
299 /// AllocHook - a static function
300 /// A glibc memory allocation hook.
301 
302 void *TMemStatMng::AllocHook(size_t size, const void* /*caller*/)
303 {
305  // Restore all old hooks
306  instance->Disable();
307 
308  // Call recursively
309  void *result = malloc(size);
310  // Call our routine
311  instance->AddPointer(result, Int_t(size));
312  // TTimer::SingleShot(0, "TYamsMemMng", instance, "SaveData()");
313 
314  // Restore our own hooks
315  instance->Enable();
316 
317  return result;
318 }
319 
320 ////////////////////////////////////////////////////////////////////////////////
321 /// FreeHook - a static function
322 /// A glibc memory deallocation hook.
323 
324 void TMemStatMng::FreeHook(void* ptr, const void* /*caller*/)
325 {
327  // Restore all old hooks
328  instance->Disable();
329 
330  // Call recursively
331  free(ptr);
332 
333  // Call our routine
334  instance->AddPointer(ptr, -1);
335 
336  // Restore our own hooks
337  instance->Enable();
338 }
339 
340 ////////////////////////////////////////////////////////////////////////////////
341 /// An internal function, which returns a bitid for a corresponding CRC digest
342 /// cache variables
343 
344 Int_t TMemStatMng::generateBTID(UChar_t *CRCdigest, Int_t stackEntries,
345  void **stackPointers)
346 {
347  static Int_t old_btid = -1;
348  static SCustomDigest old_digest;
349 
350  Int_t ret_val = -1;
351  bool startCheck(false);
352  if(old_btid >= 0) {
353  for(int i = 0; i < g_digestSize; ++i) {
354  if(old_digest.fValue[i] != CRCdigest[i]) {
355  startCheck = true;
356  break;
357  }
358  }
359  ret_val = old_btid;
360  } else {
361  startCheck = true;
362  }
363 
364  // return cached value
365  if(!startCheck)
366  return ret_val;
367 
368  old_digest = SCustomDigest(CRCdigest);
369  CRCSet_t::const_iterator found = fBTChecksums.find(CRCdigest);
370 
371  if(fBTChecksums.end() == found) {
372  // check the size of the BT array container
373  const int nbins = fHbtids->GetNbinsX();
374  //check that the current allocation in fHbtids is enough, otherwise expend it with
375  if(fBTCount + stackEntries + 1 >= nbins) {
376  fHbtids->SetBins(nbins * 2, 0, 1);
377  }
378 
379  int *btids = fHbtids->GetArray();
380  // the first value is a number of entries in a given stack
381  btids[fBTCount++] = stackEntries;
382  ret_val = fBTCount;
383  if(stackEntries <= 0) {
384  Warning("AddPointer",
385  "A number of stack entries is equal or less than zero. For btid %d", ret_val);
386  }
387 
388  // add new BT's CRC value
389  std::pair<CRCSet_t::iterator, bool> res = fBTChecksums.insert(CRCSet_t::value_type(CRCdigest, ret_val));
390  if(!res.second)
391  Error("AddPointer", "Can't added a new BTID to the container.");
392 
393  // save all symbols of this BT
394  for(int i = 0; i < stackEntries; ++i) {
395  ULong_t func_addr = (ULong_t)(stackPointers[i]);
396  Int_t idx = fFAddrs.find(func_addr);
397  // check, whether it's a new symbol
398  if(idx < 0) {
399  TString strFuncAddr;
400  strFuncAddr += func_addr;
401  TString strSymbolInfo;
402  getSymbolFullInfo(stackPointers[i], &strSymbolInfo);
403 
404  TNamed *nm = new TNamed(strFuncAddr, strSymbolInfo);
405  fFAddrsList->Add(nm);
406  idx = fFAddrsList->GetEntriesFast() - 1;
407  // TODO: more detailed error message...
408  if(!fFAddrs.add(func_addr, idx))
409  Error("AddPointer", "Can't add a function return address to the container");
410  }
411 
412  // even if we have -1 as an index we add it to the container
413  btids[fBTCount++] = idx;
414  }
415 
416  } else {
417  // reuse an existing BT
418  ret_val = found->second;
419  }
420 
421  old_btid = ret_val;
422 
423  return ret_val;
424 }
425 
426 ////////////////////////////////////////////////////////////////////////////////
427 /// Add pointer to table.
428 /// This method is called every time when any of the hooks are triggered.
429 /// The memory de-/allocation information will is recorded.
430 
431 void TMemStatMng::AddPointer(void *ptr, Int_t size)
432 {
433  void *stptr[g_BTStackLevel + 1];
434  const int stackentries = getBacktrace(stptr, g_BTStackLevel, fUseGNUBuiltinBacktrace);
435 
436  // save only unique BTs
437  TMD5 md5;
438  md5.Update(reinterpret_cast<UChar_t*>(stptr), sizeof(void*) * stackentries);
439  UChar_t digest[g_digestSize];
440  md5.Final(digest);
441 
442  // for Debug. A counter of all (de)allacations.
443  ++fBTIDCount;
444 
445  Int_t btid(generateBTID(digest, stackentries, stptr));
446 
447  if(btid <= 0)
448  Error("AddPointer", "bad BT id");
449 
450  fTimeStamp.Set();
451  Double_t CurTime = fTimeStamp.AsDouble();
452  fBufTimems[fBufN] = Int_t(10000.*(CurTime - fBeginTime));
453  ULong_t ul = (ULong_t)(ptr);
454  fBufPos[fBufN] = (ULong64_t)(ul);
455  fBufNBytes[fBufN] = size;
456  fBufBtID[fBufN] = btid;
457  fBufN++;
458  if (fBufN >= fBufferSize) {
459  FillTree();
460  }
461 }
462 
463 ////////////////////////////////////////////////////////////////////////////////
464 ///loop on all entries in the buffer and fill the output Tree
465 ///entries with alloc and free in the buffer are eliminated
466 
468 {
469 
470  //eliminate alloc/free pointing to the same location in the current buffer
472  memset(fMustWrite,0,fBufN*sizeof(Bool_t));
473  Int_t i=0,j;
474  while (i<fBufN) {
475  Int_t indi = fIndex[i];
476  Int_t indmin = indi;
477  Int_t indmax = indi;
478  j = i+1;;
479  ULong64_t pos = fBufPos[indi];
480  while (j < fBufN) {
481  Int_t indj = fIndex[j];
482  ULong64_t posj = fBufPos[indj];
483  if (posj != pos) break;
484  if (indmin > indj) indmin = indj;
485  if (indmax < indj) indmax = indj;
486  j++;
487  }
488  if (indmin == indmax) fMustWrite[indmin] = kTRUE;
489  if (fBufNBytes[indmin] == -1) fMustWrite[indmin] = kTRUE;
490  if (fBufNBytes[indmax] > 0) fMustWrite[indmax] = kTRUE;
491  i = j;
492  }
493 
494  // now fill the Tree with the remaining allocs/frees
495  for (i=0;i<fBufN;i++) {
496  if (!fMustWrite[i]) continue;
497  fPos = fBufPos[i];
498  fTimems = fBufTimems[i];
499  fNBytes = fBufNBytes[i];
500  fBtID = fBufBtID[i];
501  fDumpTree->Fill();
502  }
503 
504  fBufN = 0;
505  if (fDumpTree->GetEntries() >= fMaxCalls) TMemStatMng::GetInstance()->Disable();
506 }
void SetMaxCalls(Int_t maxcalls)
Set the maximum number of new/delete registered in the output Tree.
bool add(ULong_t addr, Int_t idx)
Definition: TMemStatMng.h:36
void Enable()
Enable memory hooks.
virtual int GetPid()
Get process id.
Definition: TSystem.cxx:715
An array of TObjects.
Definition: TObjArray.h:37
virtual const char * GetFlagsOpt() const
Return the optimization flags.
Definition: TSystem.cxx:3820
THist< 1, int, THistStatContent > TH1I
Definition: THist.hxx:287
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition: TObject.cxx:854
virtual const char * GetBuildCompilerVersion() const
Return the build compiler version.
Definition: TSystem.cxx:3784
ULong64_t * fBufPos
Definition: TMemStatMng.h:127
void FillTree()
loop on all entries in the buffer and fill the output Tree entries with alloc and free in the buffer ...
void Final()
MD5 finalization, ends an MD5 message-digest operation, writing the the message digest and zeroizing ...
Definition: TMD5.cxx:167
virtual void SetDirectory(TDirectory *dir)
By default when an histogram is created, it is added to the list of histogram objects in the current ...
Definition: TH1.cxx:8194
static TMemStatMng * fgInstance
tree to dump information
Definition: TMemStatMng.h:114
static void Close()
Close - a static function This method stops the manager, flashes all the buffered data and closes the...
virtual const char * GetBuildNode() const
Return the build node name.
Definition: TSystem.cxx:3792
Double_t AsDouble() const
Definition: TTimeStamp.h:138
virtual void SetBins(Int_t nx, Double_t xmin, Double_t xmax)
Redefine x axis parameters.
Definition: TH1.cxx:8024
virtual void SetOwner(Bool_t enable=kTRUE)
Set whether this collection is the owner (enable==true) of its content.
static void * AllocHook(size_t size, const void *)
AllocHook - a static function A glibc memory allocation hook.
const UShort_t g_digestSize
Definition: TMemStatMng.h:53
TObjArray * fFAddrsList
Definition: TMemStatMng.h:136
size_t getBacktrace(void **_trace, size_t _size, Bool_t _bUseGNUBuiltinBacktrace=kFALSE)
Get the backtrace _trace - array of pointers _size - maximal deepness of stack information _bUseGNUBu...
TTimeStamp fTimeStamp
Definition: TMemStatMng.h:118
void Init()
old free function
Definition: TMemStatMng.cxx:74
int Int_t
Definition: RtypesCore.h:41
static void SetFreeHook(FreeHookFunc_t p)
SetFreeHook - a static function Set pointer to function replacing free function.
bool Bool_t
Definition: RtypesCore.h:59
static void SetMallocHook(MallocHookFunc_t p)
SetMallocHook - a static function Set pointer to function replacing alloc function.
virtual const char * GetFlagsDebug() const
Return the debug flags.
Definition: TSystem.cxx:3812
#define malloc
Definition: civetweb.c:818
static void MacAllocHook(void *ptr, size_t size)
AllocHook - a static function a special memory hook for Mac OS X memory zones.
const size_t g_BTStackLevel
Definition: TMemStatDef.h:17
UChar_t fValue[g_digestSize]
Definition: TMemStatMng.h:62
This code implements the MD5 message-digest algorithm.
Definition: TMD5.h:44
The TNamed class is the base class for all named ROOT classes.
Definition: TNamed.h:29
TTree * fDumpTree
file to dump current information
Definition: TMemStatMng.h:113
void Set()
Set Date/Time to current time as reported by the system.
Definition: TTimeStamp.cxx:556
void Sort(Index n, const Element *a, Index *index, Bool_t down=kTRUE)
Definition: TMath.h:1150
const Int_t * GetArray() const
Definition: TArrayI.h:43
virtual ~TMemStatMng()
if an instance is destructed - the hooks are reseted to old hooks
Bool_t fUseGNUBuiltinBacktrace
Definition: TMemStatMng.h:117
static void MacFreeHook(void *ptr)
AllocHook - a static function a special memory hook for Mac OS X memory zones.
void Update(const UChar_t *buf, UInt_t len)
Update TMD5 object to reflect the concatenation of another buffer full of bytes.
Definition: TMD5.cxx:108
R__EXTERN TSystem * gSystem
Definition: TSystem.h:540
void AddPointer(void *ptr, Int_t size)
Add pointer to table.
Int_t GetEntriesFast() const
Definition: TObjArray.h:64
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition: TObject.cxx:880
char * Form(const char *fmt,...)
void SetName(const char *name)
Definition: TCollection.h:202
static constexpr double nm
const Bool_t kFALSE
Definition: RtypesCore.h:88
#define ClassImp(name)
Definition: Rtypes.h:359
double Double_t
Definition: RtypesCore.h:55
void Disable()
Disble memory hooks.
unsigned long long ULong64_t
Definition: RtypesCore.h:70
#define free
Definition: civetweb.c:821
unsigned long ULong_t
Definition: RtypesCore.h:51
void SetBufferSize(Int_t buffersize)
Set the maximum number of alloc/free calls to be buffered.
TMemStatHook::MallocHookFunc_t fPreviousMallocHook
Definition: TMemStatMng.h:97
you should not use this method at all Int_t Int_t Double_t Double_t Double_t e
Definition: TRolke.cxx:630
void getSymbolFullInfo(void *_pAddr, TString *_retInfo, const char *const _seporator=" | ")
Int_t find(ULong_t addr)
Definition: TMemStatMng.h:41
Mother of all ROOT objects.
Definition: TObject.h:37
Int_t generateBTID(UChar_t *CRCdigest, Int_t stackEntries, void **stackPointers)
An internal function, which returns a bitid for a corresponding CRC digest cache variables.
void Add(TObject *obj)
Definition: TObjArray.h:73
static void FreeHook(void *ptr, const void *)
FreeHook - a static function A glibc memory deallocation hook.
unsigned char UChar_t
Definition: RtypesCore.h:34
virtual Int_t GetNbinsX() const
Definition: TH1.h:291
static TMemStatMng * GetInstance()
GetInstance - a static function Initialize a singleton of MemStat manager.
TMemStatHook::FreeHookFunc_t fPreviousFreeHook
old malloc function
Definition: TMemStatMng.h:98
const Bool_t kTRUE
Definition: RtypesCore.h:87
TMemStatFAddrContainer fFAddrs
Definition: TMemStatMng.h:135
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition: TObject.cxx:866