Panda3D
 All Classes Functions Variables Enumerations
memoryUsage.cxx
1 // Filename: memoryUsage.cxx
2 // Created by: drose (25May00)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "memoryUsage.h"
16 
17 #ifdef DO_MEMORY_USAGE
18 
19 #include "memoryUsagePointers.h"
20 #include "trueClock.h"
21 #include "typedReferenceCount.h"
22 #include "mutexImpl.h"
23 #include "interrogate_request.h"
24 
25 #if (defined(WIN32_VC) || defined (WIN64_VC)) && defined(_DEBUG)
26 #include <crtdbg.h>
27 #endif
28 
29 #include "config_express.h"
30 #include "configVariableInt64.h"
31 #include <algorithm>
32 #include <iterator>
33 
34 MemoryUsage *MemoryUsage::_global_ptr;
35 
36 // This flag is used to protect the operator new/delete handlers
37 // against recursive entry.
38 bool MemoryUsage::_recursion_protect = false;
39 
40 // The cutoff ages, in seconds, for the various buckets in the AgeHistogram.
41 double MemoryUsage::AgeHistogram::_cutoff[MemoryUsage::AgeHistogram::num_buckets] = {
42  0.0,
43  0.1,
44  1.0,
45  10.0,
46  60.0,
47 };
48 
49 
50 ////////////////////////////////////////////////////////////////////
51 // Function: MemoryUsage::TypeHistogram::add_info
52 // Access: Public
53 // Description: Adds a single entry to the histogram.
54 ////////////////////////////////////////////////////////////////////
55 void MemoryUsage::TypeHistogram::
56 add_info(TypeHandle type, MemoryInfo *info) {
57  _counts[type].add_info(info);
58 }
59 
60 
61 // This class is a temporary class used only in TypeHistogram::show(),
62 // below, to sort the types in descending order by counts.
63 class TypeHistogramCountSorter {
64 public:
65  TypeHistogramCountSorter(const MemoryUsagePointerCounts &count,
66  TypeHandle type) :
67  _count(count),
68  _type(type)
69  {
70  }
71  bool operator < (const TypeHistogramCountSorter &other) const {
72  return other._count < _count;
73  }
74  MemoryUsagePointerCounts _count;
75  TypeHandle _type;
76 };
77 
78 ////////////////////////////////////////////////////////////////////
79 // Function: MemoryUsage::TypeHistogram::show
80 // Access: Public
81 // Description: Shows the contents of the histogram to nout.
82 ////////////////////////////////////////////////////////////////////
83 void MemoryUsage::TypeHistogram::
84 show() const {
85  // First, copy the relevant information to a vector so we can sort
86  // by counts. Don't use a pvector.
87  typedef vector<TypeHistogramCountSorter> CountSorter;
88  CountSorter count_sorter;
89  Counts::const_iterator ci;
90  for (ci = _counts.begin(); ci != _counts.end(); ++ci) {
91  count_sorter.push_back
92  (TypeHistogramCountSorter((*ci).second, (*ci).first));
93  }
94 
95  sort(count_sorter.begin(), count_sorter.end());
96 
97  CountSorter::const_iterator vi;
98  for (vi = count_sorter.begin(); vi != count_sorter.end(); ++vi) {
99  TypeHandle type = (*vi)._type;
100  if (type == TypeHandle::none()) {
101  nout << "unknown";
102  } else {
103  nout << type;
104  }
105  nout << " : " << (*vi)._count << "\n";
106  }
107 }
108 
109 ////////////////////////////////////////////////////////////////////
110 // Function: MemoryUsage::TypeHistogram::clear
111 // Access: Public
112 // Description: Resets the histogram in preparation for new data.
113 ////////////////////////////////////////////////////////////////////
114 void MemoryUsage::TypeHistogram::
115 clear() {
116  _counts.clear();
117 }
118 
119 ////////////////////////////////////////////////////////////////////
120 // Function: MemoryUsage::AgeHistogram::Constructor
121 // Access: Public
122 // Description:
123 ////////////////////////////////////////////////////////////////////
124 MemoryUsage::AgeHistogram::
125 AgeHistogram() {
126  clear();
127 }
128 
129 ////////////////////////////////////////////////////////////////////
130 // Function: MemoryUsage::AgeHistogram::add_info
131 // Access: Public
132 // Description: Adds a single entry to the histogram.
133 ////////////////////////////////////////////////////////////////////
134 void MemoryUsage::AgeHistogram::
135 add_info(double age, MemoryInfo *info) {
136  int bucket = choose_bucket(age);
137  nassertv(bucket >= 0 && bucket < num_buckets);
138  _counts[bucket].add_info(info);
139 }
140 
141 ////////////////////////////////////////////////////////////////////
142 // Function: MemoryUsage::AgeHistogram::show
143 // Access: Public
144 // Description: Shows the contents of the histogram to nout.
145 ////////////////////////////////////////////////////////////////////
146 void MemoryUsage::AgeHistogram::
147 show() const {
148  for (int i = 0; i < num_buckets - 1; i++) {
149  nout << _cutoff[i] << " to " << _cutoff[i + 1] << " seconds old : ";
150  _counts[i].output(nout);
151  nout << "\n";
152  }
153  nout << _cutoff[num_buckets - 1] << " seconds old and up : ";
154  _counts[num_buckets - 1].output(nout);
155  nout << "\n";
156 }
157 
158 ////////////////////////////////////////////////////////////////////
159 // Function: MemoryUsage::AgeHistogram::clear
160 // Access: Public
161 // Description: Resets the histogram in preparation for new data.
162 ////////////////////////////////////////////////////////////////////
163 void MemoryUsage::AgeHistogram::
164 clear() {
165  for (int i = 0; i < num_buckets; i++) {
166  _counts[i].clear();
167  }
168 }
169 
170 ////////////////////////////////////////////////////////////////////
171 // Function: MemoryUsage::AgeHistogram::choose_bucket
172 // Access: Private
173 // Description:
174 ////////////////////////////////////////////////////////////////////
175 int MemoryUsage::AgeHistogram::
176 choose_bucket(double age) const {
177  for (int i = num_buckets - 1; i >= 0; i--) {
178  if (age >= _cutoff[i]) {
179  return i;
180  }
181  }
182  express_cat.error()
183  << "No suitable bucket for age " << age << "\n";
184  return 0;
185 }
186 
187 ////////////////////////////////////////////////////////////////////
188 // Function: MemoryUsage::heap_alloc_single
189 // Access: Public, Virtual
190 // Description: Allocates a block of memory from the heap, similar to
191 // malloc(). This will never return NULL; it will abort
192 // instead if memory is not available.
193 ////////////////////////////////////////////////////////////////////
194 void *MemoryUsage::
195 heap_alloc_single(size_t size) {
196  void *ptr;
197 
198  if (_recursion_protect) {
199  ptr = MemoryHook::heap_alloc_single(size);
200  if (express_cat.is_spam()) {
201  express_cat.spam()
202  << "Allocating pointer " << (void *)ptr
203  << " during recursion protect.\n";
204  }
205 
206  } else {
207  if (_track_memory_usage) {
208  ptr = MemoryHook::heap_alloc_single(size);
209  /*
210  if (express_cat.is_spam()) {
211  express_cat.spam()
212  << "Allocating pointer " << (void *)ptr
213  << " of size " << size << ".\n";
214  }
215  */
216 
217  get_global_ptr()->ns_record_void_pointer(ptr, size);
218 
219  } else {
220  ptr = MemoryHook::heap_alloc_single(size);
221  }
222  }
223 
224  return ptr;
225 }
226 
227 ////////////////////////////////////////////////////////////////////
228 // Function: MemoryUsage::heap_free_single
229 // Access: Public, Virtual
230 // Description: Releases a block of memory previously allocated via
231 // heap_alloc_single.
232 ////////////////////////////////////////////////////////////////////
233 void MemoryUsage::
234 heap_free_single(void *ptr) {
235  if (_recursion_protect) {
236  if (express_cat.is_spam()) {
237  express_cat.spam()
238  << "Deleting pointer " << (void *)ptr
239  << " during recursion protect.\n";
240  }
242 
243  } else {
244  if (_track_memory_usage) {
245  /*
246  if (express_cat.is_spam()) {
247  express_cat.spam()
248  << "Removing pointer " << (void *)ptr << "\n";
249  }
250  */
251  ns_remove_void_pointer(ptr);
253  } else {
255  }
256  }
257 }
258 
259 ////////////////////////////////////////////////////////////////////
260 // Function: MemoryUsage::heap_alloc_array
261 // Access: Public, Virtual
262 // Description: Allocates a block of memory from the heap, similar to
263 // malloc(). This will never return NULL; it will abort
264 // instead if memory is not available.
265 ////////////////////////////////////////////////////////////////////
266 void *MemoryUsage::
267 heap_alloc_array(size_t size) {
268  void *ptr;
269 
270  if (_recursion_protect) {
271  ptr = MemoryHook::heap_alloc_array(size);
272  if (express_cat.is_spam()) {
273  express_cat.spam()
274  << "Allocating array pointer " << (void *)ptr
275  << " during recursion protect.\n";
276  }
277 
278  } else {
279  if (_track_memory_usage) {
280  ptr = MemoryHook::heap_alloc_array(size);
281  /*
282  if (express_cat.is_spam()) {
283  express_cat.spam()
284  << "Allocating array pointer " << (void *)ptr
285  << " of size " << size << ".\n";
286  }
287  */
288 
289  get_global_ptr()->ns_record_void_pointer(ptr, size);
290 
291  } else {
292  ptr = MemoryHook::heap_alloc_array(size);
293  }
294  }
295 
296  return ptr;
297 }
298 
299 ////////////////////////////////////////////////////////////////////
300 // Function: MemoryUsage::heap_realloc_array
301 // Access: Public, Virtual
302 // Description: Resizes a block of memory previously returned from
303 // heap_alloc_array.
304 ////////////////////////////////////////////////////////////////////
305 void *MemoryUsage::
306 heap_realloc_array(void *ptr, size_t size) {
307  if (_recursion_protect) {
308  ptr = MemoryHook::heap_realloc_array(ptr, size);
309  if (express_cat.is_spam()) {
310  express_cat.spam()
311  << "Reallocating array pointer " << (void *)ptr
312  << " during recursion protect.\n";
313  }
314 
315  } else {
316  if (_track_memory_usage) {
317  get_global_ptr()->ns_remove_void_pointer(ptr);
318  ptr = MemoryHook::heap_realloc_array(ptr, size);
319  /*
320  if (express_cat.is_spam()) {
321  express_cat.spam()
322  << "Reallocating array pointer " << (void *)ptr
323  << " to size " << size << ".\n";
324  }
325  */
326 
327  get_global_ptr()->ns_record_void_pointer(ptr, size);
328 
329  } else {
330  ptr = MemoryHook::heap_realloc_array(ptr, size);
331  }
332  }
333 
334  return ptr;
335 }
336 
337 ////////////////////////////////////////////////////////////////////
338 // Function: MemoryUsage::heap_free_array
339 // Access: Public, Virtual
340 // Description: Releases a block of memory previously allocated via
341 // heap_alloc_array.
342 ////////////////////////////////////////////////////////////////////
343 void MemoryUsage::
344 heap_free_array(void *ptr) {
345  if (_recursion_protect) {
346  if (express_cat.is_spam()) {
347  express_cat.spam()
348  << "Deleting pointer " << (void *)ptr
349  << " during recursion protect.\n";
350  }
352 
353  } else {
354  if (_track_memory_usage) {
355  /*
356  if (express_cat.is_spam()) {
357  express_cat.spam()
358  << "Removing pointer " << (void *)ptr << "\n";
359  }
360  */
361  ns_remove_void_pointer(ptr);
363  } else {
365  }
366  }
367 }
368 
369 ////////////////////////////////////////////////////////////////////
370 // Function: MemoryUsage::mark_pointer
371 // Access: Public, Virtual
372 // Description: This special method exists only to provide a callback
373 // hook into MemoryUsage. It indicates that the
374 // indicated pointer, allocated from somewhere other
375 // than a call to heap_alloc(), now contains a pointer
376 // to the indicated ReferenceCount object. If orig_size
377 // is 0, it indicates that the ReferenceCount object has
378 // been destroyed.
379 ////////////////////////////////////////////////////////////////////
380 void MemoryUsage::
381 mark_pointer(void *ptr, size_t size, ReferenceCount *ref_ptr) {
382  if (_recursion_protect || !_track_memory_usage) {
383  return;
384  }
385 
386  if (express_cat.is_spam()) {
387  express_cat.spam()
388  << "Marking pointer " << ptr << ", size " << size
389  << ", ref_ptr = " << ref_ptr << "\n";
390  }
391 
392  if (size != 0) {
393  // We're recording this pointer as now in use.
394  ns_record_void_pointer(ptr, size);
395 
396  if (ref_ptr != (ReferenceCount *)NULL) {
397  // Make the pointer typed. This is particularly necessary in
398  // case the ref_ptr is a different value than the base void
399  // pointer; this may be our only opportunity to associate the
400  // two pointers.
401  Table::iterator ti;
402  ti = _table.find(ptr);
403  nassertv(ti != _table.end());
404  MemoryInfo *info = (*ti).second;
405 
406  info->_ref_ptr = ref_ptr;
407  info->_static_type = ReferenceCount::get_class_type();
408  info->_dynamic_type = ReferenceCount::get_class_type();
409  info->_flags |= MemoryInfo::F_reconsider_dynamic_type;
410 
411  if (ref_ptr != ptr) {
412  _recursion_protect = true;
413 
414  pair<Table::iterator, bool> insert_result =
415  _table.insert(Table::value_type((void *)ref_ptr, info));
416  assert(insert_result.first != _table.end());
417  if (!insert_result.second) {
418  express_cat.warning()
419  << "Attempt to mark pointer " << ptr << " as ReferenceCount "
420  << ref_ptr << ", which was already allocated.\n";
421  }
422 
423  _recursion_protect = false;
424  }
425  }
426 
427  } else {
428  // We're removing this pointer from use.
429  ns_remove_void_pointer(ptr);
430  }
431 }
432 
433 #if (defined(WIN32_VC) || defined (WIN64_VC))&& defined(_DEBUG)
434 ////////////////////////////////////////////////////////////////////
435 // Function: MemoryUsage::win32_malloc_hook
436 // Access: Public, Static
437 // Description: This callback is attached to the Win32 debug malloc
438 // system to be called whenever a pointer is allocated,
439 // reallocated, or freed. It's used to track the total
440 // memory allocated via calls to malloc().
441 ////////////////////////////////////////////////////////////////////
442 int MemoryUsage::
443 win32_malloc_hook(int alloc_type, void *ptr,
444  size_t size, int block_use, long request,
445  const unsigned char *filename, int line) {
446  MemoryUsage *mu = get_global_ptr();
447  int increment = 0;
448  switch (alloc_type) {
449  case _HOOK_ALLOC:
450  increment = size;
451  break;
452 
453  case _HOOK_REALLOC:
454  increment = size - _msize(ptr);
455  break;
456 
457  case _HOOK_FREE:
458  increment = - ((int)_msize(ptr));
459  break;
460  }
461 
462  mu->_total_size += increment;
463  return true;
464 }
465 #endif // WIN32_VC && _DEBUG
466 
467 
468 
469 ////////////////////////////////////////////////////////////////////
470 // Function: MemoryUsage::Constructor
471 // Access: Private
472 // Description:
473 ////////////////////////////////////////////////////////////////////
474 MemoryUsage::
475 MemoryUsage(const MemoryHook &copy) : MemoryHook(copy) {
476  // We must get these variables here instead of in
477  // config_express.cxx, because we need to know it at static init
478  // time, and who knows when the code in config_express will be
479  // executed.
480 
481  _track_memory_usage = ConfigVariableBool
482  ("track-memory-usage", false,
483  PRC_DESC("Set this to true to enable full-force tracking of C++ allocations "
484  "and recordkeeping by type. It's quite expensive."));
485 
486  // Since enabling this after startup might cause bogus errors, we'd
487  // like to know if this happened, so we can squelch those error
488  // messages.
489  _startup_track_memory_usage = _track_memory_usage;
490 
491  // Make sure the express category has been instantiated.
492  express_cat->is_info();
493 
494  _report_memory_usage = ConfigVariableBool
495  ("report-memory-usage", false,
496  PRC_DESC("Set this true to enable automatic reporting of allocated objects "
497  "at the interval specified by report-memory-interval. This also "
498  "requires track-memory-usage."));
499  _report_memory_interval = ConfigVariableDouble
500  ("report-memory-interval", 5.0,
501  PRC_DESC("This is the interval, in seconds, for reports of currently allocated "
502  "memory, when report-memory-usage is true."));
503  _last_report_time = 0.0;
504 
505  _count_memory_usage = false;
506 
507  PN_int64 max_heap_size = ConfigVariableInt64
508  ("max-heap-size", 0,
509  PRC_DESC("If this is nonzero, it is the maximum number of bytes expected "
510  "to be allocated on the heap before we enter report-memory-usage "
511  "mode automatically. The assumption is that once this limit "
512  "has been crossed, we must be leaking."));
513  if (max_heap_size != 0) {
514  _max_heap_size = (size_t)max_heap_size;
515  }
516 
517 #ifdef USE_MEMORY_NOWRAPPERS
518 #error Cannot compile MemoryUsage without malloc wrappers!
519 #endif
520 
521 #if (defined(WIN32_VC) || defined(WIN64_VC)) && defined(_DEBUG)
522  // On a debug Windows build, we can set this malloc hook which
523  // allows tracking every malloc call, even from subordinate
524  // libraries.
525  _CrtSetAllocHook(&win32_malloc_hook);
526  _count_memory_usage = true;
527 #endif
528 
529  _info_set_dirty = false;
530  _freeze_index = 0;
531  _count = 0;
532  _current_cpp_size = 0;
533  _total_cpp_size = 0;
534  _total_size = 0;
535 }
536 
537 ////////////////////////////////////////////////////////////////////
538 // Function: MemoryUsage::overflow_heap_size
539 // Access: Protected, Virtual
540 // Description: This callback method is called whenever the total
541 // allocated heap size exceeds _max_heap_size. It's
542 // mainly intended for reporting memory leaks, on the
543 // assumption that once we cross some specified
544 // threshold, we're just leaking memory.
545 ////////////////////////////////////////////////////////////////////
546 void MemoryUsage::
547 overflow_heap_size() {
548  MemoryHook::overflow_heap_size();
549 
550  express_cat.error()
551  << "Total allocated memory has reached "
552  << get_panda_heap_single_size() + get_panda_heap_array_size()
553  << " bytes."
554  << "\n heap single: " << get_panda_heap_single_size()
555  << "\n heap array: " << get_panda_heap_array_size()
556  << "\n heap overhead: " << get_panda_heap_overhead()
557  << "\n mmap: " << get_panda_mmap_size()
558  << "\n external: " << get_external_size()
559  << "\n total: " << get_total_size()
560  << "\n";
561 
562  // Turn on spamful debugging.
563  _track_memory_usage = true;
564  _report_memory_usage = true;
565 }
566 
567 ////////////////////////////////////////////////////////////////////
568 // Function: MemoryUsage::ns_record_pointer
569 // Access: Private
570 // Description: Indicates that the given pointer has been recently
571 // allocated.
572 ////////////////////////////////////////////////////////////////////
573 void MemoryUsage::
574 ns_record_pointer(ReferenceCount *ptr) {
575  if (_track_memory_usage) {
576  // We have to protect modifications to the table from recursive
577  // calls by toggling _recursion_protect while we adjust it.
578  _recursion_protect = true;
579  pair<Table::iterator, bool> insert_result =
580  _table.insert(Table::value_type((void *)ptr, (MemoryInfo *)NULL));
581 
582  // This shouldn't fail.
583  assert(insert_result.first != _table.end());
584 
585  if (insert_result.second) {
586  (*insert_result.first).second = new MemoryInfo;
587  _info_set_dirty = true;
588  ++_count;
589  }
590 
591  MemoryInfo *info = (*insert_result.first).second;
592 
593  // We might already have a ReferenceCount pointer, thanks to a
594  // previous call to mark_pointer().
595  nassertv(info->_ref_ptr == NULL || info->_ref_ptr == ptr);
596 
597  info->_ref_ptr = ptr;
598  info->_static_type = ReferenceCount::get_class_type();
599  info->_dynamic_type = ReferenceCount::get_class_type();
600  info->_time = TrueClock::get_global_ptr()->get_long_time();
601  info->_freeze_index = _freeze_index;
602  info->_flags |= MemoryInfo::F_reconsider_dynamic_type;
603 
604  // We close the recursion_protect flag all the way down here, so
605  // that we also protect ourselves against a possible recursive
606  // call in TrueClock::get_global_ptr().
607  _recursion_protect = false;
608 
609  if (_report_memory_usage) {
610  double now = TrueClock::get_global_ptr()->get_long_time();
611  if (now - _last_report_time > _report_memory_interval) {
612  _last_report_time = now;
613  express_cat.info()
614  << "*** Current memory usage: " << get_total_size() << "\n";
615  show_current_types();
616  }
617  }
618  }
619 }
620 
621 ////////////////////////////////////////////////////////////////////
622 // Function: MemoryUsage::ns_update_type
623 // Access: Private
624 // Description: Associates the indicated type with the given pointer.
625 // This should be called by functions (e.g. the
626 // constructor) that know more specifically what type of
627 // thing we've got; otherwise, the MemoryUsage database
628 // will know only that it's a "ReferenceCount".
629 ////////////////////////////////////////////////////////////////////
630 void MemoryUsage::
631 ns_update_type(ReferenceCount *ptr, TypeHandle type) {
632  if (_track_memory_usage) {
633  Table::iterator ti;
634  ti = _table.find(ptr);
635  if (ti == _table.end()) {
636  if (_startup_track_memory_usage) {
637  express_cat.error()
638  << "Attempt to update type to " << type << " for unrecorded pointer "
639  << (void *)ptr << "!\n";
640  nassertv(false);
641  }
642  return;
643  }
644 
645  MemoryInfo *info = (*ti).second;
646 
647  info->update_type_handle(info->_static_type, type);
648  info->determine_dynamic_type();
649 
650  consolidate_void_ptr(info);
651  }
652 }
653 
654 ////////////////////////////////////////////////////////////////////
655 // Function: MemoryUsage::ns_update_type
656 // Access: Private
657 // Description: Associates the indicated type with the given pointer.
658 // This flavor of update_type() also passes in the
659 // pointer as a TypedObject, and useful for objects that
660 // are, in fact, TypedObjects. Once the MemoryUsage
661 // database has the pointer as a TypedObject it doesn't
662 // need any more help.
663 ////////////////////////////////////////////////////////////////////
664 void MemoryUsage::
665 ns_update_type(ReferenceCount *ptr, TypedObject *typed_ptr) {
666  if (_track_memory_usage) {
667  Table::iterator ti;
668  ti = _table.find(ptr);
669  if (ti == _table.end()) {
670  if (_startup_track_memory_usage) {
671  express_cat.error()
672  << "Attempt to update type to " << typed_ptr->get_type()
673  << " for unrecorded pointer "
674  << (void *)ptr << "!\n";
675  }
676  return;
677  }
678 
679  MemoryInfo *info = (*ti).second;
680  info->_typed_ptr = typed_ptr;
681  info->determine_dynamic_type();
682 
683  consolidate_void_ptr(info);
684  }
685 }
686 
687 ////////////////////////////////////////////////////////////////////
688 // Function: MemoryUsage::ns_remove_pointer
689 // Access: Private
690 // Description: Indicates that the given pointer has been recently
691 // freed.
692 ////////////////////////////////////////////////////////////////////
693 void MemoryUsage::
694 ns_remove_pointer(ReferenceCount *ptr) {
695  if (_track_memory_usage) {
696  Table::iterator ti;
697  ti = _table.find(ptr);
698  if (ti == _table.end()) {
699  if (_startup_track_memory_usage) {
700  express_cat.error()
701  << "Attempt to remove pointer " << (void *)ptr
702  << ", not in table.\n"
703  << "Possibly a double-destruction.\n";
704  nassertv(false);
705  }
706  return;
707  }
708 
709  MemoryInfo *info = (*ti).second;
710 
711  if (info->_ref_ptr == NULL) {
712  express_cat.error()
713  << "Pointer " << (void *)ptr << " deleted twice!\n";
714  return;
715  }
716  nassertv(info->_ref_ptr == ptr);
717 
718  if (express_cat.is_spam()) {
719  express_cat.spam()
720  << "Removing ReferenceCount pointer " << (void *)ptr << "\n";
721  }
722 
723  info->_ref_ptr = (ReferenceCount *)NULL;
724  info->_typed_ptr = (TypedObject *)NULL;
725 
726  if (info->_freeze_index == _freeze_index) {
727  double now = TrueClock::get_global_ptr()->get_long_time();
728 
729  // We have to protect modifications to the table from recursive
730  // calls by toggling _recursion_protect while we adjust it.
731  _recursion_protect = true;
732  _trend_types.add_info(info->get_type(), info);
733  _trend_ages.add_info(now - info->_time, info);
734  _recursion_protect = false;
735  }
736 
737  if (ptr != info->_void_ptr || info->_void_ptr == NULL) {
738  // Remove the entry from the table.
739 
740  // We have to protect modifications to the table from recursive
741  // calls by toggling _recursion_protect while we adjust it.
742  _recursion_protect = true;
743  _table.erase(ti);
744  _recursion_protect = false;
745 
746  if (info->_void_ptr == NULL) {
747  // That was the last entry. Remove it altogether.
748  _total_cpp_size -= info->_size;
749  if (info->_freeze_index == _freeze_index) {
750  _current_cpp_size -= info->_size;
751  _count--;
752  }
753 
754  _info_set_dirty = true;
755  delete info;
756  }
757  }
758  }
759 }
760 
761 ////////////////////////////////////////////////////////////////////
762 // Function: MemoryUsage::ns_record_void_pointer
763 // Access: Private
764 // Description: Records a pointer that's not even necessarily a
765 // ReferenceCount object (but for which we know the size
766 // of the allocated structure).
767 ////////////////////////////////////////////////////////////////////
768 void MemoryUsage::
769 ns_record_void_pointer(void *ptr, size_t size) {
770  if (_track_memory_usage) {
771  if (express_cat.is_spam()) {
772  express_cat.spam()
773  << "Recording void pointer " << (void *)ptr << "\n";
774  }
775 
776  // We have to protect modifications to the table from recursive
777  // calls by toggling _recursion_protect while we adjust it.
778 
779  _recursion_protect = true;
780  pair<Table::iterator, bool> insert_result =
781  _table.insert(Table::value_type((void *)ptr, (MemoryInfo *)NULL));
782 
783  assert(insert_result.first != _table.end());
784 
785  if (insert_result.second) {
786  (*insert_result.first).second = new MemoryInfo;
787  _info_set_dirty = true;
788  ++_count;
789  }
790 
791  MemoryInfo *info = (*insert_result.first).second;
792 
793  // We shouldn't already have a void pointer.
794  if (info->_void_ptr != (void *)NULL) {
795  express_cat.error()
796  << "Void pointer " << (void *)ptr << " recorded twice!\n";
797  nassertv(false);
798  }
799 
800  if (info->_freeze_index == _freeze_index) {
801  _current_cpp_size += size - info->_size;
802  } else {
803  _current_cpp_size += size;
804  }
805  _total_cpp_size += size - info->_size;
806 
807  info->_void_ptr = ptr;
808  info->_size = size;
809  info->_time = TrueClock::get_global_ptr()->get_long_time();
810  info->_freeze_index = _freeze_index;
811  info->_flags |= MemoryInfo::F_size_known;
812 
813  // We close the recursion_protect flag all the way down here, so
814  // that we also protect ourselves against a possible recursive
815  // call in TrueClock::get_global_ptr().
816  _recursion_protect = false;
817  }
818 }
819 
820 ////////////////////////////////////////////////////////////////////
821 // Function: MemoryUsage::ns_remove_void_pointer
822 // Access: Private
823 // Description: Removes a pointer previously recorded via
824 // record_void_pointer.
825 ////////////////////////////////////////////////////////////////////
826 void MemoryUsage::
827 ns_remove_void_pointer(void *ptr) {
828  if (_track_memory_usage) {
829  if (express_cat.is_spam()) {
830  express_cat.spam()
831  << "Removing void pointer " << (void *)ptr << "\n";
832  }
833 
834  Table::iterator ti;
835  ti = _table.find(ptr);
836  if (ti == _table.end()) {
837  // The pointer we tried to delete was not recorded in the table.
838 
839  // We can't report this as an error, because (a) we might have
840  // removed the void pointer entry already when we consolidated,
841  // and (b) a few objects might have been created during static
842  // init time, before we grabbed the operator new/delete function
843  // handlers.
844  return;
845  }
846 
847  MemoryInfo *info = (*ti).second;
848 
849  if (info->_void_ptr == (void *)NULL) {
850  express_cat.error()
851  << "Pointer " << (void *)ptr << " deleted twice!\n";
852  return;
853  }
854  nassertv(info->_void_ptr == ptr);
855 
856  if (info->_ref_ptr != (ReferenceCount *)NULL) {
857  express_cat.error()
858  << "Pointer " << (void *)ptr
859  << " did not destruct before being deleted!\n";
860  if (info->_ref_ptr != ptr) {
861  remove_pointer(info->_ref_ptr);
862  }
863  }
864 
865  info->_void_ptr = NULL;
866 
867  // Remove it from the table.
868 
869  // We have to protect modifications to the table from recursive
870  // calls by toggling _recursion_protect while we adjust it.
871  _recursion_protect = true;
872  _table.erase(ti);
873  _recursion_protect = false;
874 
875  _total_cpp_size -= info->_size;
876  if (info->_freeze_index == _freeze_index) {
877  --_count;
878  _current_cpp_size -= info->_size;
879  }
880 
881  _info_set_dirty = true;
882  delete info;
883  }
884 }
885 
886 ////////////////////////////////////////////////////////////////////
887 // Function: MemoryUsage::ns_get_num_pointers
888 // Access: Private
889 // Description: Returns the number of pointers currently active.
890 ////////////////////////////////////////////////////////////////////
891 int MemoryUsage::
892 ns_get_num_pointers() {
893  nassertr(_track_memory_usage, 0);
894  return _count;
895 }
896 
897 ////////////////////////////////////////////////////////////////////
898 // Function: MemoryUsage::ns_get_pointers
899 // Access: Private
900 // Description: Fills the indicated MemoryUsagePointers with the set
901 // of all pointers currently active.
902 ////////////////////////////////////////////////////////////////////
903 void MemoryUsage::
904 ns_get_pointers(MemoryUsagePointers &result) {
905  nassertv(_track_memory_usage);
906  result.clear();
907 
908  if (_info_set_dirty) {
909  refresh_info_set();
910  }
911 
912  double now = TrueClock::get_global_ptr()->get_long_time();
913  InfoSet::iterator si;
914  for (si = _info_set.begin(); si != _info_set.end(); ++si) {
915  MemoryInfo *info = (*si);
916  if (info->_freeze_index == _freeze_index &&
917  info->_ref_ptr != (ReferenceCount *)NULL) {
918  result.add_entry(info->_ref_ptr, info->_typed_ptr, info->get_type(),
919  now - info->_time);
920  }
921  }
922 }
923 
924 ////////////////////////////////////////////////////////////////////
925 // Function: MemoryUsage::ns_get_pointers_of_type
926 // Access: Private
927 // Description: Fills the indicated MemoryUsagePointers with the set
928 // of all pointers of the indicated type currently
929 // active.
930 ////////////////////////////////////////////////////////////////////
931 void MemoryUsage::
932 ns_get_pointers_of_type(MemoryUsagePointers &result, TypeHandle type) {
933  nassertv(_track_memory_usage);
934  result.clear();
935 
936  if (_info_set_dirty) {
937  refresh_info_set();
938  }
939 
940  double now = TrueClock::get_global_ptr()->get_long_time();
941  InfoSet::iterator si;
942  for (si = _info_set.begin(); si != _info_set.end(); ++si) {
943  MemoryInfo *info = (*si);
944  if (info->_freeze_index == _freeze_index &&
945  info->_ref_ptr != (ReferenceCount *)NULL) {
946  TypeHandle info_type = info->get_type();
947  if (info_type != TypeHandle::none() &&
948  info_type.is_derived_from(type)) {
949  result.add_entry(info->_ref_ptr, info->_typed_ptr, info_type,
950  now - info->_time);
951  }
952  }
953  }
954 }
955 
956 ////////////////////////////////////////////////////////////////////
957 // Function: MemoryUsage::ns_get_pointers_of_age
958 // Access: Private
959 // Description: Fills the indicated MemoryUsagePointers with the set
960 // of all pointers that were allocated within the range
961 // of the indicated number of seconds ago.
962 ////////////////////////////////////////////////////////////////////
963 void MemoryUsage::
964 ns_get_pointers_of_age(MemoryUsagePointers &result,
965  double from, double to) {
966  nassertv(_track_memory_usage);
967  result.clear();
968 
969  if (_info_set_dirty) {
970  refresh_info_set();
971  }
972 
973  double now = TrueClock::get_global_ptr()->get_long_time();
974  InfoSet::iterator si;
975  for (si = _info_set.begin(); si != _info_set.end(); ++si) {
976  MemoryInfo *info = (*si);
977  if (info->_freeze_index == _freeze_index &&
978  info->_ref_ptr != (ReferenceCount *)NULL) {
979  double age = now - info->_time;
980  if ((age >= from && age <= to) ||
981  (age >= to && age <= from)) {
982  result.add_entry(info->_ref_ptr, info->_typed_ptr, info->get_type(), age);
983  }
984  }
985  }
986 }
987 
988 ////////////////////////////////////////////////////////////////////
989 // Function: MemoryUsage::ns_get_pointers_with_zero_count
990 // Access: Private
991 // Description: Fills the indicated MemoryUsagePointers with the set
992 // of all currently active pointers (that is, pointers
993 // allocated since the last call to freeze(), and not
994 // yet freed) that have a zero reference count.
995 //
996 // Generally, an undeleted pointer with a zero reference
997 // count means its reference count has never been
998 // incremented beyond zero (since once it has been
999 // incremented, the only way it can return to zero would
1000 // free the pointer). This may include objects that are
1001 // allocated statically or on the stack, which are never
1002 // intended to be deleted. Or, it might represent a
1003 // programmer or compiler error.
1004 //
1005 // This function has the side-effect of incrementing
1006 // each of their reference counts by one, thus
1007 // preventing them from ever being freed--but since they
1008 // hadn't been freed anyway, probably no additional harm
1009 // is done.
1010 ////////////////////////////////////////////////////////////////////
1011 void MemoryUsage::
1012 ns_get_pointers_with_zero_count(MemoryUsagePointers &result) {
1013  nassertv(_track_memory_usage);
1014  result.clear();
1015 
1016  if (_info_set_dirty) {
1017  refresh_info_set();
1018  }
1019 
1020  double now = TrueClock::get_global_ptr()->get_long_time();
1021  InfoSet::iterator si;
1022  for (si = _info_set.begin(); si != _info_set.end(); ++si) {
1023  MemoryInfo *info = (*si);
1024  if (info->_freeze_index == _freeze_index &&
1025  info->_ref_ptr != (ReferenceCount *)NULL) {
1026  if (info->_ref_ptr->get_ref_count() == 0) {
1027  info->_ref_ptr->ref();
1028  result.add_entry(info->_ref_ptr, info->_typed_ptr, info->get_type(),
1029  now - info->_time);
1030  }
1031  }
1032  }
1033 }
1034 
1035 ////////////////////////////////////////////////////////////////////
1036 // Function: MemoryUsage::ns_freeze
1037 // Access: Private
1038 // Description: 'Freezes' all pointers currently stored so that they
1039 // are no longer reported; only newly allocate pointers
1040 // from this point on will appear in future information
1041 // requests. This makes it easier to differentiate
1042 // between continuous leaks and one-time memory
1043 // allocations.
1044 ////////////////////////////////////////////////////////////////////
1045 void MemoryUsage::
1046 ns_freeze() {
1047  _count = 0;
1048  _current_cpp_size = 0;
1049  _trend_types.clear();
1050  _trend_ages.clear();
1051  _freeze_index++;
1052 }
1053 
1054 ////////////////////////////////////////////////////////////////////
1055 // Function: MemoryUsage::ns_show_current_types
1056 // Access: Private
1057 // Description: Shows the breakdown of types of all of the
1058 // active pointers.
1059 ////////////////////////////////////////////////////////////////////
1060 void MemoryUsage::
1061 ns_show_current_types() {
1062  nassertv(_track_memory_usage);
1063  TypeHistogram hist;
1064 
1065  if (_info_set_dirty) {
1066  refresh_info_set();
1067  }
1068 
1069  _recursion_protect = true;
1070  InfoSet::iterator si;
1071  for (si = _info_set.begin(); si != _info_set.end(); ++si) {
1072  MemoryInfo *info = (*si);
1073  if (info->_freeze_index == _freeze_index) {
1074  hist.add_info(info->get_type(), info);
1075  }
1076  }
1077  hist.show();
1078  _recursion_protect = false;
1079 }
1080 
1081 ////////////////////////////////////////////////////////////////////
1082 // Function: MemoryUsage::ns_show_trend_types
1083 // Access: Private
1084 // Description: Shows the breakdown of types of all of the
1085 // pointers allocated and freed since the last call to
1086 // freeze().
1087 ////////////////////////////////////////////////////////////////////
1088 void MemoryUsage::
1089 ns_show_trend_types() {
1090  _trend_types.show();
1091 }
1092 
1093 ////////////////////////////////////////////////////////////////////
1094 // Function: MemoryUsage::ns_show_current_ages
1095 // Access: Private
1096 // Description: Shows the breakdown of ages of all of the
1097 // active pointers.
1098 ////////////////////////////////////////////////////////////////////
1099 void MemoryUsage::
1100 ns_show_current_ages() {
1101  nassertv(_track_memory_usage);
1102 
1103  AgeHistogram hist;
1104  double now = TrueClock::get_global_ptr()->get_long_time();
1105 
1106  _recursion_protect = true;
1107  InfoSet::iterator si;
1108  for (si = _info_set.begin(); si != _info_set.end(); ++si) {
1109  MemoryInfo *info = (*si);
1110  if (info->_freeze_index == _freeze_index) {
1111  hist.add_info(now - info->_time, info);
1112  }
1113  }
1114 
1115  hist.show();
1116  _recursion_protect = false;
1117 }
1118 
1119 ////////////////////////////////////////////////////////////////////
1120 // Function: MemoryUsage::ns_show_trend_ages
1121 // Access: Private
1122 // Description: Shows the breakdown of ages of all of the
1123 // pointers allocated and freed since the last call to
1124 // freeze().
1125 ////////////////////////////////////////////////////////////////////
1126 void MemoryUsage::
1127 ns_show_trend_ages() {
1128  _trend_ages.show();
1129 }
1130 
1131 ////////////////////////////////////////////////////////////////////
1132 // Function: MemoryUsage::consolidate_void_ptr
1133 // Access: Private
1134 // Description: If the size information has not yet been determined
1135 // for this pointer, checks to see if it has possibly
1136 // been recorded under the TypedObject pointer (this
1137 // will happen when the class inherits from TypedObject
1138 // before ReferenceCount, e.g. TypedReferenceCount).
1139 ////////////////////////////////////////////////////////////////////
1140 void MemoryUsage::
1141 consolidate_void_ptr(MemoryInfo *info) {
1142  if (info->is_size_known()) {
1143  // We already know the size, so no sweat.
1144  return;
1145  }
1146 
1147  if (info->_typed_ptr == (TypedObject *)NULL) {
1148  // We don't have a typed pointer for this thing yet.
1149  return;
1150  }
1151 
1152  TypedObject *typed_ptr = info->_typed_ptr;
1153 
1154  if ((void *)typed_ptr == (void *)info->_ref_ptr) {
1155  // The TypedObject pointer is the same pointer as the
1156  // ReferenceCount pointer, so there's no point in looking it up
1157  // separately. Actually, this really shouldn't even be possible.
1158  return;
1159  }
1160 
1161  nassertv(info->_void_ptr == NULL);
1162 
1163  Table::iterator ti;
1164  ti = _table.find(typed_ptr);
1165  if (ti == _table.end()) {
1166  // No entry for the typed pointer, either.
1167  return;
1168  }
1169 
1170  // We do have an entry! Copy over the relevant pieces.
1171  MemoryInfo *typed_info = (*ti).second;
1172 
1173  nassertv(typed_info->_void_ptr == typed_ptr &&
1174  typed_info->_ref_ptr == NULL);
1175 
1176  info->_void_ptr = typed_info->_void_ptr;
1177  if (typed_info->is_size_known()) {
1178  info->_size = typed_info->get_size();
1179  info->_flags |= MemoryInfo::F_size_known;
1180  if (typed_info->_freeze_index == _freeze_index) {
1181  _current_cpp_size += info->_size;
1182  }
1183  }
1184 
1185  // Now that we've consolidated the pointers, remove the entry for
1186  // the typed pointer.
1187  if (info->_freeze_index == _freeze_index) {
1188  _count--;
1189  _current_cpp_size -= info->_size;
1190  }
1191 
1192  _info_set_dirty = true;
1193  delete typed_info;
1194 
1195  (*ti).second = info;
1196 }
1197 
1198 ////////////////////////////////////////////////////////////////////
1199 // Function: MemoryUsage::refresh_info_set
1200 // Access: Private
1201 // Description: Recomputes the _info_set table, if necessary. This
1202 // table stores a unique entry for each MemoryInfo
1203 // object in _table.
1204 ////////////////////////////////////////////////////////////////////
1205 void MemoryUsage::
1206 refresh_info_set() {
1207  if (!_info_set_dirty) {
1208  return;
1209  }
1210 
1211  // We have to protect modifications to the table from recursive
1212  // calls by toggling _recursion_protect while we adjust it.
1213  _recursion_protect = true;
1214 
1215  _info_set.clear();
1216  Table::iterator ti;
1217  for (ti = _table.begin(); ti != _table.end(); ++ti) {
1218  _info_set.insert((*ti).second);
1219  }
1220 
1221  _recursion_protect = false;
1222 
1223  _info_set_dirty = false;
1224 }
1225 
1226 
1227 #endif // DO_MEMORY_USAGE
static TrueClock * get_global_ptr()
Returns a pointer to the one TrueClock object in the world.
Definition: trueClock.I:81
bool is_derived_from(TypeHandle parent, TypedObject *object=(TypedObject *) NULL) const
Returns true if this type is derived from the indicated type, false otherwise.
Definition: typeHandle.I:152
virtual void heap_free_array(void *ptr)
Releases a block of memory previously allocated via heap_alloc_array.
static TypeHandle none()
Returns a special zero-valued TypeHandle that is used to indicate no type.
Definition: typeHandle.I:274
virtual void * heap_alloc_single(size_t size)
Allocates a block of memory from the heap, similar to malloc().
This is a convenience class to specialize ConfigVariable as a boolean type.
This is an abstract class that all classes which use TypeHandle, and also provide virtual functions t...
Definition: typedObject.h:98
This is a convenience class to specialize ConfigVariable as a 64-bit integer type.
This is a convenience class to specialize ConfigVariable as a floating-point type.
virtual void heap_free_single(void *ptr)
Releases a block of memory previously allocated via heap_alloc_single.
A base class for all things that want to be reference-counted.
virtual void * heap_alloc_array(size_t size)
Allocates a block of memory from the heap, similar to malloc().
This class provides a wrapper around the various possible malloc schemes Panda might employ...
Definition: memoryHook.h:43
virtual void * heap_realloc_array(void *ptr, size_t size)
Resizes a block of memory previously returned from heap_alloc_array.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85