Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TStorage.cxx
Go to the documentation of this file.
1 // @(#)root/base:$Id$
2 // Author: Fons Rademakers 29/07/95
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2000, 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 
12 /** \class TStorage
13 \ingroup Base
14 
15 Storage manager. The storage manager works best in conjunction with
16 the custom ROOT new and delete operators defined in the file
17 NewDelete.cxx (libNew.so). Only when using the custom allocation
18 operators will memory usage statistics be gathered using the
19 TStorage EnterStat(), RemoveStat(), etc. functions.
20 Memory checking is by default enabled (when using libNew.so) and
21 usage statistics is gathered. Using the resource (in .rootrc):
22 Root.MemStat one can toggle statistics gathering on or off. More
23 specifically on can trap the allocation of a block of memory of a
24 certain size. This can be specified using the resource:
25 Root.MemStat.size, using the resource Root.MemStat.cnt one can
26 specify after how many allocations of this size the trap should
27 occur.
28 
29 Set the compile option R__NOSTATS to de-activate all memory checking
30 and statistics gathering in the system.
31 */
32 
33 #include <stdlib.h>
34 
35 #include "TROOT.h"
36 #include "TObjectTable.h"
37 #include "TError.h"
38 #include "TString.h"
39 #include "TVirtualMutex.h"
40 #include "TInterpreter.h"
41 
42 #if !defined(R__NOSTATS)
43 # define MEM_DEBUG
44 # define MEM_STAT
45 # define MEM_CHECKOBJECTPOINTERS
46 #endif
47 
48 #if defined(MEM_STAT) && !defined(MEM_DEBUG)
49 # define MEM_DEBUG
50 #endif
51 
52 #ifdef MEM_DEBUG
53 # ifdef R__B64
54 # define storage_size(p) ((size_t)(((size_t*)p)[-1]))
55 # else
56 # define storage_size(p) ((size_t)(((int*)p)[-2]))
57 # endif
58 #else
59 # define storage_size(p) ((size_t)0)
60 #endif
61 
62 #define PVOID (-1)
63 
64 size_t TStorage::fgMaxBlockSize;
65 FreeHookFun_t TStorage::fgFreeHook;
66 void *TStorage::fgFreeHookData;
67 ReAllocFun_t TStorage::fgReAllocHook;
68 ReAllocCFun_t TStorage::fgReAllocCHook;
69 Bool_t TStorage::fgHasCustomNewDelete;
70 
71 
72 ClassImp(TStorage);
73 
74 //------------------------------------------------------------------------------
75 
76 static const char *gSpaceErr = "storage exhausted";
77 
78 const size_t kObjMaxSize = 10024;
79 
80 static Bool_t gMemStatistics;
81 static Int_t gAllocated[kObjMaxSize], gFreed[kObjMaxSize];
82 static Int_t gAllocatedTotal, gFreedTotal;
83 static void **gTraceArray = 0;
84 static Int_t gTraceCapacity = 10, gTraceIndex = 0,
85  gMemSize = -1, gMemIndex = -1;
86 
87 // Used in NewDelete.cxx; set by TMapFile.
88 ROOT::Internal::FreeIfTMapFile_t *ROOT::Internal::gFreeIfTMapFile = nullptr;
89 void *ROOT::Internal::gMmallocDesc = 0; //is used and set in TMapFile
90 
91 
92 
93 ////////////////////////////////////////////////////////////////////////////////
94 /// Register a memory allocation operation. If desired one can trap an
95 /// allocation of a certain size in case one tries to find a memory
96 /// leak of that particular size. This function is only called via
97 /// the ROOT custom new operators.
98 
99 void TStorage::EnterStat(size_t size, void *p)
100 {
101  TStorage::SetMaxBlockSize(TMath::Max(TStorage::GetMaxBlockSize(), size));
102 
103  if (!gMemStatistics) return;
104 
105  if ((Int_t)size == gMemSize) {
106  if (gTraceIndex == gMemIndex)
107  Fatal("EnterStat", "trapped allocation %d", gMemIndex);
108 
109  if (!gTraceArray)
110  gTraceArray = (void**) malloc(sizeof(void*)*gTraceCapacity);
111 
112  if (gTraceIndex >= gTraceCapacity) {
113  gTraceCapacity = gTraceCapacity*2;
114  gTraceArray = (void**) realloc(gTraceArray, sizeof(void*)*gTraceCapacity);
115  }
116  gTraceArray[gTraceIndex++] = p;
117  }
118  if (size >= kObjMaxSize)
119  gAllocated[kObjMaxSize-1]++;
120  else
121  gAllocated[size]++;
122  gAllocatedTotal += size;
123 }
124 
125 ////////////////////////////////////////////////////////////////////////////////
126 /// Register a memory free operation. This function is only called via
127 /// the custom ROOT delete operator.
128 
129 void TStorage::RemoveStat(void *vp)
130 {
131  if (!gMemStatistics) return;
132 
133  size_t size = storage_size(vp);
134  if ((Int_t)size == gMemSize) {
135  for (int i = 0; i < gTraceIndex; i++)
136  if (gTraceArray[i] == vp) {
137  gTraceArray[i] = 0;
138  break;
139  }
140  }
141  if (size >= kObjMaxSize)
142  gFreed[kObjMaxSize-1]++;
143  else
144  gFreed[size]++;
145  gFreedTotal += size;
146 }
147 
148 ////////////////////////////////////////////////////////////////////////////////
149 /// Allocate a block of memory, that later can be resized using
150 /// TStorage::ReAlloc().
151 
152 void *TStorage::Alloc(size_t size)
153 {
154  static const char *where = "TStorage::Alloc";
155 
156 #ifndef WIN32
157  void *vp = ::operator new[](size);
158 #else
159  void *vp = ::operator new(size);
160 #endif
161  if (vp == 0)
162  Fatal(where, "%s", gSpaceErr);
163 
164  return vp;
165 }
166 
167 ////////////////////////////////////////////////////////////////////////////////
168 /// De-allocate block of memory, that was allocated via TStorage::Alloc().
169 
170 void TStorage::Dealloc(void *ptr)
171 {
172 #ifndef WIN32
173  ::operator delete[](ptr);
174 #else
175  ::operator delete(ptr);
176 #endif
177 }
178 
179 ////////////////////////////////////////////////////////////////////////////////
180 /// Reallocate (i.e. resize) block of memory. Don't use if size is larger
181 /// than old size, use ReAlloc(void *, size_t, size_t) instead.
182 
183 void *TStorage::ReAlloc(void *ovp, size_t size)
184 {
185  ::Obsolete("ReAlloc(void*,size_t)", "v5-34-00", "v6-02-00");
186  ::Info("ReAlloc(void*,size_t)", "please use ReAlloc(void*,size_t,size_t)");
187 
188  {
189  // Needs to be protected by global mutex
190  R__LOCKGUARD(gGlobalMutex);
191 
192  if (fgReAllocHook && fgHasCustomNewDelete && !TROOT::MemCheck())
193  return (*fgReAllocHook)(ovp, size);
194  }
195 
196  static const char *where = "TStorage::ReAlloc";
197 
198 #ifndef WIN32
199  void *vp = ::operator new[](size);
200 #else
201  void *vp = ::operator new(size);
202 #endif
203  if (vp == 0)
204  Fatal(where, "%s", gSpaceErr);
205 
206  if (ovp == 0)
207  return vp;
208 
209  memmove(vp, ovp, size);
210 #ifndef WIN32
211  ::operator delete[](ovp);
212 #else
213  ::operator delete(ovp);
214 #endif
215  return vp;
216 }
217 
218 ////////////////////////////////////////////////////////////////////////////////
219 /// Reallocate (i.e. resize) block of memory. Checks if current size is
220 /// equal to oldsize. If not memory was overwritten.
221 
222 void *TStorage::ReAlloc(void *ovp, size_t size, size_t oldsize)
223 {
224  // Needs to be protected by global mutex
225  {
226  R__LOCKGUARD(gGlobalMutex);
227 
228  if (fgReAllocCHook && fgHasCustomNewDelete && !TROOT::MemCheck())
229  return (*fgReAllocCHook)(ovp, size, oldsize);
230  }
231 
232  static const char *where = "TStorage::ReAlloc";
233 
234  if (oldsize == size)
235  return ovp;
236 
237 #ifndef WIN32
238  void *vp = ::operator new[](size);
239 #else
240  void *vp = ::operator new(size);
241 #endif
242  if (vp == 0)
243  Fatal(where, "%s", gSpaceErr);
244 
245  if (ovp == 0)
246  return vp;
247 
248  if (size > oldsize) {
249  memcpy(vp, ovp, oldsize);
250  memset((char*)vp+oldsize, 0, size-oldsize);
251  } else
252  memcpy(vp, ovp, size);
253 #ifndef WIN32
254  ::operator delete[](ovp);
255 #else
256  ::operator delete(ovp);
257 #endif
258  return vp;
259 }
260 
261 ////////////////////////////////////////////////////////////////////////////////
262 /// Reallocate (i.e. resize) array of chars. Size and oldsize are
263 /// in number of chars.
264 
265 char *TStorage::ReAllocChar(char *ovp, size_t size, size_t oldsize)
266 {
267  static const char *where = "TStorage::ReAllocChar";
268 
269  char *vp;
270  if (ovp == 0) {
271  vp = new char[size];
272  if (vp == 0)
273  Fatal(where, "%s", gSpaceErr);
274  return vp;
275  }
276  if (oldsize == size)
277  return ovp;
278 
279  vp = new char[size];
280  if (vp == 0)
281  Fatal(where, "%s", gSpaceErr);
282  if (size > oldsize) {
283  memcpy(vp, ovp, oldsize);
284  memset((char*)vp+oldsize, 0, size-oldsize);
285  } else
286  memcpy(vp, ovp, size);
287  delete [] ovp;
288  return vp;
289 }
290 
291 ////////////////////////////////////////////////////////////////////////////////
292 /// Reallocate (i.e. resize) array of integers. Size and oldsize are
293 /// number of integers (not number of bytes).
294 
295 Int_t *TStorage::ReAllocInt(Int_t *ovp, size_t size, size_t oldsize)
296 {
297  static const char *where = "TStorage::ReAllocInt";
298 
299  Int_t *vp;
300  if (ovp == 0) {
301  vp = new Int_t[size];
302  if (vp == 0)
303  Fatal(where, "%s", gSpaceErr);
304  return vp;
305  }
306  if (oldsize == size)
307  return ovp;
308 
309  vp = new Int_t[size];
310  if (vp == 0)
311  Fatal(where, "%s", gSpaceErr);
312  if (size > oldsize) {
313  memcpy(vp, ovp, oldsize*sizeof(Int_t));
314  memset((Int_t*)vp+oldsize, 0, (size-oldsize)*sizeof(Int_t));
315  } else
316  memcpy(vp, ovp, size*sizeof(Int_t));
317  delete [] ovp;
318  return vp;
319 }
320 
321 ////////////////////////////////////////////////////////////////////////////////
322 /// Used to allocate a TObject on the heap (via TObject::operator new()).
323 /// Directly after this routine one can call (in the TObject ctor)
324 /// TStorage::FilledByObjectAlloc() to find out if the just created object is on
325 /// the heap. This technique is necessary as there is one stack per thread
326 /// and we can not rely on comparison with the current stack memory position.
327 
328 void *TStorage::ObjectAlloc(size_t sz)
329 {
330  void* space = ::operator new(sz);
331  memset(space, kObjectAllocMemValue, sz);
332  return space;
333 }
334 
335 ////////////////////////////////////////////////////////////////////////////////
336 /// Used to allocate array of TObject on the heap (via TObject::operator new[]()).
337 /// Unlike the 'singular' ObjectAlloc, we do not mark those object has being
338 /// allocated on the heap as they can not be individually deleted.
339 
340 void *TStorage::ObjectAllocArray(size_t sz)
341 {
342  void* space = ::operator new(sz);
343  return space;
344 }
345 
346 ////////////////////////////////////////////////////////////////////////////////
347 /// Used to allocate a TObject on the heap (via TObject::operator new(size_t,void*))
348 /// in position vp. vp is already allocated (maybe on heap, maybe on
349 /// stack) so just return.
350 
351 void *TStorage::ObjectAlloc(size_t , void *vp)
352 {
353  return vp;
354 }
355 
356 ////////////////////////////////////////////////////////////////////////////////
357 /// Used to deallocate a TObject on the heap (via TObject::operator delete()).
358 
359 void TStorage::ObjectDealloc(void *vp)
360 {
361 
362  ::operator delete(vp);
363 }
364 
365 ////////////////////////////////////////////////////////////////////////////////
366 /// Used to deallocate a TObject on the heap (via TObject::operator delete(void*,void*)).
367 
368 void TStorage::ObjectDealloc(void *vp, void *ptr)
369 {
370  if (vp && ptr) { }
371 }
372 
373 #ifdef R__SIZEDDELETE
374 ////////////////////////////////////////////////////////////////////////////////
375 /// Used to deallocate a TObject on the heap (via TObject::operator delete()),
376 /// for sized deallocation.
377 
378 void TStorage::ObjectDealloc(void *vp, size_t size)
379 {
380  ::operator delete(vp, size);
381 }
382 #endif
383 
384 ////////////////////////////////////////////////////////////////////////////////
385 /// Set a free handler.
386 
387 void TStorage::SetFreeHook(FreeHookFun_t fh, void *data)
388 {
389  fgFreeHook = fh;
390  fgFreeHookData = data;
391 }
392 
393 ////////////////////////////////////////////////////////////////////////////////
394 /// Set a custom ReAlloc handlers. This function is typically
395 /// called via a static object in the ROOT libNew.so shared library.
396 
397 void TStorage::SetReAllocHooks(ReAllocFun_t rh1, ReAllocCFun_t rh2)
398 {
399  fgReAllocHook = rh1;
400  fgReAllocCHook = rh2;
401 }
402 
403 ////////////////////////////////////////////////////////////////////////////////
404 /// Print memory usage statistics.
405 
406 void TStorage::PrintStatistics()
407 {
408  // Needs to be protected by global mutex
409  R__LOCKGUARD(gGlobalMutex);
410 
411 #if defined(MEM_DEBUG) && defined(MEM_STAT)
412 
413  if (!gMemStatistics || !HasCustomNewDelete())
414  return;
415 
416  //Printf("");
417  Printf("Heap statistics");
418  Printf("%12s%12s%12s%12s", "size", "alloc", "free", "diff");
419  Printf("================================================");
420 
421  int i;
422  for (i = 0; i < (int)kObjMaxSize; i++)
423  if (gAllocated[i] != gFreed[i])
424  //if (gAllocated[i])
425  Printf("%12d%12d%12d%12d", i, gAllocated[i], gFreed[i],
426  gAllocated[i]-gFreed[i]);
427 
428  if (gAllocatedTotal != gFreedTotal) {
429  Printf("------------------------------------------------");
430  Printf("Total: %12d%12d%12d", gAllocatedTotal, gFreedTotal,
431  gAllocatedTotal-gFreedTotal);
432  }
433 
434  if (gMemSize != -1) {
435  Printf("------------------------------------------------");
436  for (i= 0; i < gTraceIndex; i++)
437  if (gTraceArray[i])
438  Printf("block %d of size %d not freed", i, gMemSize);
439  }
440  Printf("================================================");
441  Printf(" ");
442 #endif
443 }
444 
445 ////////////////////////////////////////////////////////////////////////////////
446 /// Enable memory usage statistics gathering. Size is the size of the memory
447 /// block that should be trapped and ix is after how many such allocations
448 /// the trap should happen.
449 
450 void TStorage::EnableStatistics(int size, int ix)
451 {
452 #ifdef MEM_STAT
453  gMemSize = size;
454  gMemIndex = ix;
455  gMemStatistics = kTRUE;
456 #else
457  int idum = size; int iidum = ix;
458 #endif
459 }
460 
461 ////////////////////////////////////////////////////////////////////////////////
462 
463 ULong_t TStorage::GetHeapBegin()
464 {
465  ::Obsolete("GetHeapBegin()", "v5-34-00", "v6-02-00");
466  //return begin of heap
467  return 0;
468 }
469 
470 ////////////////////////////////////////////////////////////////////////////////
471 
472 ULong_t TStorage::GetHeapEnd()
473 {
474  ::Obsolete("GetHeapBegin()", "v5-34-00", "v6-02-00");
475  //return end of heap
476  return 0;
477 }
478 
479 ////////////////////////////////////////////////////////////////////////////////
480 ///return static free hook data
481 
482 void *TStorage::GetFreeHookData()
483 {
484  return fgFreeHookData;
485 }
486 
487 ////////////////////////////////////////////////////////////////////////////////
488 ///return the has custom delete flag
489 
490 Bool_t TStorage::HasCustomNewDelete()
491 {
492  return fgHasCustomNewDelete;
493 }
494 
495 ////////////////////////////////////////////////////////////////////////////////
496 ///set the has custom delete flag
497 
498 void TStorage::SetCustomNewDelete()
499 {
500  fgHasCustomNewDelete = kTRUE;
501 }
502 
503 
504 ////////////////////////////////////////////////////////////////////////////////
505 ///add a range to the heap
506 
507 void TStorage::AddToHeap(ULong_t, ULong_t)
508 {
509  ::Obsolete("AddToHeap(ULong_t,ULong_t)", "v5-34-00", "v6-02-00");
510 }
511 
512 ////////////////////////////////////////////////////////////////////////////////
513 ///is object at p in the heap?
514 
515 Bool_t TStorage::IsOnHeap(void *)
516 {
517  ::Obsolete("IsOnHeap(void*)", "v5-34-00", "v6-02-00");
518  return false;
519 }
520