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