21 #if (defined(WIN32_VC) || defined (WIN64_VC)) && defined(_DEBUG) 36 bool MemoryUsage::_recursion_protect =
false;
39 double MemoryUsage::AgeHistogram::_cutoff[MemoryUsage::AgeHistogram::num_buckets] = {
50 void MemoryUsage::TypeHistogram::
52 #ifdef DO_MEMORY_USAGE 53 _counts[type].add_info(info);
57 #ifdef DO_MEMORY_USAGE 60 class TypeHistogramCountSorter {
68 bool operator < (
const TypeHistogramCountSorter &other)
const {
69 return other._count < _count;
79 void MemoryUsage::TypeHistogram::
81 #ifdef DO_MEMORY_USAGE 84 typedef std::vector<TypeHistogramCountSorter> CountSorter;
85 CountSorter count_sorter;
86 Counts::const_iterator ci;
87 for (ci = _counts.begin(); ci != _counts.end(); ++ci) {
88 count_sorter.push_back
89 (TypeHistogramCountSorter((*ci).second, (*ci).first));
92 sort(count_sorter.begin(), count_sorter.end());
94 CountSorter::const_iterator vi;
95 for (vi = count_sorter.begin(); vi != count_sorter.end(); ++vi) {
97 if (type == TypeHandle::none()) {
102 nout <<
" : " << (*vi)._count <<
"\n";
110 void MemoryUsage::TypeHistogram::
118 MemoryUsage::AgeHistogram::
126 void MemoryUsage::AgeHistogram::
127 add_info(
double age, MemoryInfo *info) {
128 #ifdef DO_MEMORY_USAGE 129 int bucket = choose_bucket(age);
130 nassertv(bucket >= 0 && bucket < num_buckets);
131 _counts[bucket].add_info(info);
138 void MemoryUsage::AgeHistogram::
140 #ifdef DO_MEMORY_USAGE 141 for (
int i = 0; i < num_buckets - 1; i++) {
142 nout << _cutoff[i] <<
" to " << _cutoff[i + 1] <<
" seconds old : ";
143 _counts[i].output(nout);
146 nout << _cutoff[num_buckets - 1] <<
" seconds old and up : ";
147 _counts[num_buckets - 1].output(nout);
155 void MemoryUsage::AgeHistogram::
157 #ifdef DO_MEMORY_USAGE 158 for (
int i = 0; i < num_buckets; i++) {
167 int MemoryUsage::AgeHistogram::
168 choose_bucket(
double age)
const {
169 #ifdef DO_MEMORY_USAGE 170 for (
int i = num_buckets - 1; i >= 0; i--) {
171 if (age >= _cutoff[i]) {
176 <<
"No suitable bucket for age " << age <<
"\n";
186 heap_alloc_single(
size_t size) {
187 #ifdef DO_MEMORY_USAGE 190 if (_recursion_protect) {
192 if (express_cat.is_spam()) {
194 <<
"Allocating pointer " << (
void *)ptr
195 <<
" during recursion protect.\n";
199 if (_track_memory_usage) {
209 get_global_ptr()->ns_record_void_pointer(ptr, size);
226 heap_free_single(
void *ptr) {
227 #ifdef DO_MEMORY_USAGE 228 if (_recursion_protect) {
229 if (express_cat.is_spam()) {
231 <<
"Deleting pointer " << (
void *)ptr
232 <<
" during recursion protect.\n";
237 if (_track_memory_usage) {
244 ns_remove_void_pointer(ptr);
260 heap_alloc_array(
size_t size) {
261 #ifdef DO_MEMORY_USAGE 264 if (_recursion_protect) {
266 if (express_cat.is_spam()) {
268 <<
"Allocating array pointer " << (
void *)ptr
269 <<
" during recursion protect.\n";
273 if (_track_memory_usage) {
283 get_global_ptr()->ns_record_void_pointer(ptr, size);
300 heap_realloc_array(
void *ptr,
size_t size) {
301 #ifdef DO_MEMORY_USAGE 302 if (_recursion_protect) {
304 if (express_cat.is_spam()) {
306 <<
"Reallocating array pointer " << (
void *)ptr
307 <<
" during recursion protect.\n";
311 if (_track_memory_usage) {
312 get_global_ptr()->ns_remove_void_pointer(ptr);
322 get_global_ptr()->ns_record_void_pointer(ptr, size);
339 heap_free_array(
void *ptr) {
340 #ifdef DO_MEMORY_USAGE 341 if (_recursion_protect) {
342 if (express_cat.is_spam()) {
344 <<
"Deleting pointer " << (
void *)ptr
345 <<
" during recursion protect.\n";
350 if (_track_memory_usage) {
357 ns_remove_void_pointer(ptr);
377 #ifdef DO_MEMORY_USAGE 378 if (_recursion_protect || !_track_memory_usage) {
382 if (express_cat.is_spam()) {
384 <<
"Marking pointer " << ptr <<
", size " << size
385 <<
", ref_ptr = " << ref_ptr <<
"\n";
390 ns_record_void_pointer(ptr, size);
392 if (ref_ptr !=
nullptr) {
397 ti = _table.find(ptr);
398 nassertv(ti != _table.end());
399 MemoryInfo *info = (*ti).second;
401 info->_ref_ptr = ref_ptr;
402 info->_static_type = ReferenceCount::get_class_type();
403 info->_dynamic_type = ReferenceCount::get_class_type();
404 info->_flags |= MemoryInfo::F_reconsider_dynamic_type;
406 if (ref_ptr != ptr) {
407 _recursion_protect =
true;
409 pair<Table::iterator, bool> insert_result =
410 _table.insert(Table::value_type((
void *)ref_ptr, info));
411 assert(insert_result.first != _table.end());
412 if (!insert_result.second) {
413 express_cat.warning()
414 <<
"Attempt to mark pointer " << ptr <<
" as ReferenceCount " 415 << ref_ptr <<
", which was already allocated.\n";
418 _recursion_protect =
false;
424 ns_remove_void_pointer(ptr);
429 #if (defined(WIN32_VC) || defined (WIN64_VC))&& defined(_DEBUG) 436 win32_malloc_hook(
int alloc_type,
void *ptr,
437 size_t size,
int block_use,
long request,
438 const unsigned char *filename,
int line) {
441 switch (alloc_type) {
447 increment = size - _msize(ptr);
451 increment = - ((int)_msize(ptr));
455 mu->_total_size += increment;
458 #endif // WIN32_VC && _DEBUG 468 _info_set_dirty(false),
471 _current_cpp_size(0),
475 _track_memory_usage(false),
476 _startup_track_memory_usage(false),
477 _count_memory_usage(false),
478 _report_memory_usage(false),
479 _report_memory_interval(0.0),
480 _last_report_time(0.0) {
482 #ifdef DO_MEMORY_USAGE 488 (
"track-memory-usage",
false,
489 PRC_DESC(
"Set this to true to enable full-force tracking of C++ allocations " 490 "and recordkeeping by type. It's quite expensive."));
494 _startup_track_memory_usage = _track_memory_usage;
497 express_cat->is_info();
500 (
"report-memory-usage",
false,
501 PRC_DESC(
"Set this true to enable automatic reporting of allocated objects " 502 "at the interval specified by report-memory-interval. This also " 503 "requires track-memory-usage."));
505 (
"report-memory-interval", 5.0,
506 PRC_DESC(
"This is the interval, in seconds, for reports of currently allocated " 507 "memory, when report-memory-usage is true."));
511 PRC_DESC(
"If this is nonzero, it is the maximum number of bytes expected " 512 "to be allocated on the heap before we enter report-memory-usage " 513 "mode automatically. The assumption is that once this limit " 514 "has been crossed, we must be leaking."));
515 if (max_heap_size != 0) {
516 _max_heap_size = (size_t)max_heap_size;
519 #ifdef USE_MEMORY_NOWRAPPERS 520 #error Cannot compile MemoryUsage without malloc wrappers! 523 #if (defined(WIN32_VC) || defined(WIN64_VC)) && defined(_DEBUG) 526 _CrtSetAllocHook(&win32_malloc_hook);
527 _count_memory_usage =
true;
529 #endif // DO_MEMORY_USAGE 536 init_memory_usage() {
537 #ifdef DO_MEMORY_USAGE 540 memory_hook = _global_ptr;
556 overflow_heap_size() {
557 #ifdef DO_MEMORY_USAGE 558 MemoryHook::overflow_heap_size();
561 <<
"Total allocated memory has reached " 573 _track_memory_usage =
true;
574 _report_memory_usage =
true;
583 #ifdef DO_MEMORY_USAGE 584 if (_track_memory_usage) {
587 _recursion_protect =
true;
588 pair<Table::iterator, bool> insert_result =
589 _table.insert(Table::value_type((
void *)ptr,
nullptr));
592 assert(insert_result.first != _table.end());
594 if (insert_result.second) {
595 (*insert_result.first).second =
new MemoryInfo;
596 _info_set_dirty =
true;
600 MemoryInfo *info = (*insert_result.first).second;
604 nassertv(info->_ref_ptr ==
nullptr || info->_ref_ptr == ptr);
606 info->_ref_ptr = ptr;
607 info->_static_type = ReferenceCount::get_class_type();
608 info->_dynamic_type = ReferenceCount::get_class_type();
610 info->_freeze_index = _freeze_index;
611 info->_flags |= MemoryInfo::F_reconsider_dynamic_type;
616 _recursion_protect =
false;
618 if (_report_memory_usage) {
620 if (now - _last_report_time > _report_memory_interval) {
621 _last_report_time = now;
636 ns_record_pointer(
void *ptr,
TypeHandle type) {
637 #ifdef DO_MEMORY_USAGE 638 if (_track_memory_usage) {
641 _recursion_protect =
true;
642 pair<Table::iterator, bool> insert_result =
643 _table.insert(Table::value_type(ptr,
nullptr));
646 assert(insert_result.first != _table.end());
648 if (insert_result.second) {
649 (*insert_result.first).second =
new MemoryInfo;
650 _info_set_dirty =
true;
654 MemoryInfo *info = (*insert_result.first).second;
658 nassertv(info->_void_ptr == ptr && info->_ref_ptr ==
nullptr);
660 info->_void_ptr = ptr;
661 info->_static_type = type;
662 info->_dynamic_type = type;
664 info->_freeze_index = _freeze_index;
665 info->_flags |= MemoryInfo::F_reconsider_dynamic_type;
670 _recursion_protect =
false;
672 if (_report_memory_usage) {
674 if (now - _last_report_time > _report_memory_interval) {
675 _last_report_time = now;
693 #ifdef DO_MEMORY_USAGE 694 if (_track_memory_usage) {
696 ti = _table.find(ptr);
697 if (ti == _table.end()) {
698 if (_startup_track_memory_usage) {
700 <<
"Attempt to update type to " << type <<
" for unrecorded pointer " 707 MemoryInfo *info = (*ti).second;
709 info->update_type_handle(info->_static_type, type);
710 info->determine_dynamic_type();
712 consolidate_void_ptr(info);
724 ns_update_type(
void *ptr,
TypedObject *typed_ptr) {
725 #ifdef DO_MEMORY_USAGE 726 if (_track_memory_usage) {
728 ti = _table.find(ptr);
729 if (ti == _table.end()) {
730 if (_startup_track_memory_usage) {
732 <<
"Attempt to update type to " << typed_ptr->get_type()
733 <<
" for unrecorded pointer " 739 MemoryInfo *info = (*ti).second;
740 info->_typed_ptr = typed_ptr;
741 info->determine_dynamic_type();
743 consolidate_void_ptr(info);
753 #ifdef DO_MEMORY_USAGE 754 if (_track_memory_usage) {
756 ti = _table.find(ptr);
757 if (ti == _table.end()) {
758 if (_startup_track_memory_usage) {
760 <<
"Attempt to remove pointer " << (
void *)ptr
761 <<
", not in table.\n" 762 <<
"Possibly a double-destruction.\n";
768 MemoryInfo *info = (*ti).second;
770 if (info->_ref_ptr ==
nullptr) {
772 <<
"Pointer " << (
void *)ptr <<
" deleted twice!\n";
775 nassertv(info->_ref_ptr == ptr);
777 if (express_cat.is_spam()) {
779 <<
"Removing ReferenceCount pointer " << (
void *)ptr <<
"\n";
782 info->_ref_ptr =
nullptr;
783 info->_typed_ptr =
nullptr;
785 if (info->_freeze_index == _freeze_index) {
790 _recursion_protect =
true;
791 _trend_types.add_info(info->get_type(), info);
792 _trend_ages.add_info(now - info->_time, info);
793 _recursion_protect =
false;
796 if (ptr != info->_void_ptr || info->_void_ptr ==
nullptr) {
801 _recursion_protect =
true;
803 _recursion_protect =
false;
805 if (info->_void_ptr ==
nullptr) {
807 _total_cpp_size -= info->_size;
808 if (info->_freeze_index == _freeze_index) {
809 _current_cpp_size -= info->_size;
813 _info_set_dirty =
true;
826 ns_record_void_pointer(
void *ptr,
size_t size) {
827 #ifdef DO_MEMORY_USAGE 828 if (_track_memory_usage) {
829 if (express_cat.is_spam()) {
831 <<
"Recording void pointer " << (
void *)ptr <<
"\n";
837 _recursion_protect =
true;
838 pair<Table::iterator, bool> insert_result =
839 _table.insert(Table::value_type((
void *)ptr,
nullptr));
841 assert(insert_result.first != _table.end());
843 if (insert_result.second) {
844 (*insert_result.first).second =
new MemoryInfo;
845 _info_set_dirty =
true;
849 MemoryInfo *info = (*insert_result.first).second;
852 if (info->_void_ptr !=
nullptr) {
854 <<
"Void pointer " << (
void *)ptr <<
" recorded twice!\n";
858 if (info->_freeze_index == _freeze_index) {
859 _current_cpp_size += size - info->_size;
861 _current_cpp_size += size;
863 _total_cpp_size += size - info->_size;
865 info->_void_ptr = ptr;
868 info->_freeze_index = _freeze_index;
869 info->_flags |= MemoryInfo::F_size_known;
874 _recursion_protect =
false;
883 ns_remove_void_pointer(
void *ptr) {
884 #ifdef DO_MEMORY_USAGE 885 if (_track_memory_usage) {
886 if (express_cat.is_spam()) {
888 <<
"Removing void pointer " << (
void *)ptr <<
"\n";
892 ti = _table.find(ptr);
893 if (ti == _table.end()) {
903 MemoryInfo *info = (*ti).second;
905 if (info->_void_ptr ==
nullptr) {
907 <<
"Pointer " << (
void *)ptr <<
" deleted twice!\n";
910 nassertv(info->_void_ptr == ptr);
912 if (info->_ref_ptr !=
nullptr) {
914 <<
"Pointer " << (
void *)ptr
915 <<
" did not destruct before being deleted!\n";
916 if (info->_ref_ptr != ptr) {
921 info->_void_ptr =
nullptr;
927 _recursion_protect =
true;
929 _recursion_protect =
false;
931 _total_cpp_size -= info->_size;
932 if (info->_freeze_index == _freeze_index) {
934 _current_cpp_size -= info->_size;
937 _info_set_dirty =
true;
947 ns_get_num_pointers() {
948 #ifdef DO_MEMORY_USAGE 949 nassertr(_track_memory_usage, 0);
962 #ifdef DO_MEMORY_USAGE 963 nassertv(_track_memory_usage);
966 if (_info_set_dirty) {
971 InfoSet::iterator si;
972 for (si = _info_set.begin(); si != _info_set.end(); ++si) {
973 MemoryInfo *info = (*si);
974 if (info->_freeze_index == _freeze_index &&
975 info->_ref_ptr !=
nullptr) {
976 result.add_entry(info->_ref_ptr, info->_typed_ptr, info->get_type(),
989 #ifdef DO_MEMORY_USAGE 990 nassertv(_track_memory_usage);
993 if (_info_set_dirty) {
998 InfoSet::iterator si;
999 for (si = _info_set.begin(); si != _info_set.end(); ++si) {
1000 MemoryInfo *info = (*si);
1001 if (info->_freeze_index == _freeze_index &&
1002 info->_ref_ptr !=
nullptr) {
1004 if (info_type != TypeHandle::none() &&
1006 result.add_entry(info->_ref_ptr, info->_typed_ptr, info_type,
1020 double from,
double to) {
1021 #ifdef DO_MEMORY_USAGE 1022 nassertv(_track_memory_usage);
1025 if (_info_set_dirty) {
1030 InfoSet::iterator si;
1031 for (si = _info_set.begin(); si != _info_set.end(); ++si) {
1032 MemoryInfo *info = (*si);
1033 if (info->_freeze_index == _freeze_index &&
1034 info->_ref_ptr !=
nullptr) {
1035 double age = now - info->_time;
1036 if ((age >= from && age <= to) ||
1037 (age >= to && age <= from)) {
1038 result.add_entry(info->_ref_ptr, info->_typed_ptr, info->get_type(), age);
1063 #ifdef DO_MEMORY_USAGE 1064 nassertv(_track_memory_usage);
1067 if (_info_set_dirty) {
1072 InfoSet::iterator si;
1073 for (si = _info_set.begin(); si != _info_set.end(); ++si) {
1074 MemoryInfo *info = (*si);
1075 if (info->_freeze_index == _freeze_index &&
1076 info->_ref_ptr !=
nullptr) {
1077 if (info->_ref_ptr->get_ref_count() == 0) {
1078 info->_ref_ptr->ref();
1079 result.add_entry(info->_ref_ptr, info->_typed_ptr, info->get_type(),
1095 #ifdef DO_MEMORY_USAGE 1097 _current_cpp_size = 0;
1098 _trend_types.clear();
1099 _trend_ages.clear();
1108 ns_show_current_types() {
1109 #ifdef DO_MEMORY_USAGE 1110 nassertv(_track_memory_usage);
1113 if (_info_set_dirty) {
1117 _recursion_protect =
true;
1118 InfoSet::iterator si;
1119 for (si = _info_set.begin(); si != _info_set.end(); ++si) {
1120 MemoryInfo *info = (*si);
1121 if (info->_freeze_index == _freeze_index) {
1122 hist.add_info(info->get_type(), info);
1126 _recursion_protect =
false;
1135 ns_show_trend_types() {
1136 #ifdef DO_MEMORY_USAGE 1137 _trend_types.show();
1145 ns_show_current_ages() {
1146 #ifdef DO_MEMORY_USAGE 1147 nassertv(_track_memory_usage);
1152 _recursion_protect =
true;
1153 InfoSet::iterator si;
1154 for (si = _info_set.begin(); si != _info_set.end(); ++si) {
1155 MemoryInfo *info = (*si);
1156 if (info->_freeze_index == _freeze_index) {
1157 hist.add_info(now - info->_time, info);
1162 _recursion_protect =
false;
1171 ns_show_trend_ages() {
1175 #ifdef DO_MEMORY_USAGE 1184 consolidate_void_ptr(MemoryInfo *info) {
1185 if (info->is_size_known()) {
1190 if (info->_typed_ptr ==
nullptr) {
1197 if ((
void *)typed_ptr == (
void *)info->_ref_ptr) {
1204 nassertv(info->_void_ptr ==
nullptr);
1207 ti = _table.find(typed_ptr);
1208 if (ti == _table.end()) {
1214 MemoryInfo *typed_info = (*ti).second;
1216 nassertv(typed_info->_void_ptr == typed_ptr &&
1217 typed_info->_ref_ptr ==
nullptr);
1219 info->_void_ptr = typed_info->_void_ptr;
1220 if (typed_info->is_size_known()) {
1221 info->_size = typed_info->get_size();
1222 info->_flags |= MemoryInfo::F_size_known;
1223 if (typed_info->_freeze_index == _freeze_index) {
1224 _current_cpp_size += info->_size;
1230 if (info->_freeze_index == _freeze_index) {
1232 _current_cpp_size -= info->_size;
1235 _info_set_dirty =
true;
1238 (*ti).second = info;
1246 refresh_info_set() {
1247 if (!_info_set_dirty) {
1253 _recursion_protect =
true;
1257 for (ti = _table.begin(); ti != _table.end(); ++ti) {
1258 _info_set.insert((*ti).second);
1261 _recursion_protect =
false;
1263 _info_set_dirty =
false;
1266 #endif // DO_MEMORY_USAGE static TrueClock * get_global_ptr()
Returns a pointer to the one TrueClock object in the world.
get_panda_heap_array_size
Returns the total number of bytes allocated from the heap from code within Panda, for arrays.
virtual void heap_free_array(void *ptr)
Releases a block of memory previously allocated via heap_alloc_array.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_panda_mmap_size
Returns the total number of bytes allocated from the virtual memory pool from code within Panda.
get_external_size
Returns the total number of bytes of allocated memory in the heap that Panda didn't seem to be respon...
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.
void clear()
Empties the set of pointers.
This is an abstract class that all classes which use TypeHandle, and also provide virtual functions t...
bool is_derived_from(TypeHandle parent, TypedObject *object=nullptr) const
Returns true if this type is derived from the indicated type, false otherwise.
This is a convenience class to specialize ConfigVariable as a 64-bit integer type.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_panda_heap_overhead
Returns the extra bytes allocated from the system that are not immediately used for holding allocated...
This is a convenience class to specialize ConfigVariable as a floating- point type.
This is a list of pointers returned by a MemoryUsage object in response to some query.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static void remove_pointer(ReferenceCount *ptr)
Indicates that the given pointer has been recently freed.
virtual void heap_free_single(void *ptr)
Releases a block of memory previously allocated via heap_alloc_single.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_panda_heap_single_size
Returns the total number of bytes allocated from the heap from code within Panda, for individual obje...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This class is used strictly for debugging purposes, specifically for tracking memory leaks of referen...
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().
get_total_size
Returns the total size of allocated memory consumed by the process, as nearly as can be determined.
This class provides a wrapper around the various possible malloc schemes Panda might employ.
static void show_current_types()
Shows the breakdown of types of all of the active pointers.
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is a supporting class for MemoryUsage.