15 #include "memoryUsage.h" 17 #ifdef DO_MEMORY_USAGE 19 #include "memoryUsagePointers.h" 20 #include "trueClock.h" 21 #include "typedReferenceCount.h" 22 #include "mutexImpl.h" 23 #include "interrogate_request.h" 25 #if (defined(WIN32_VC) || defined (WIN64_VC)) && defined(_DEBUG) 29 #include "config_express.h" 30 #include "configVariableInt64.h" 34 MemoryUsage *MemoryUsage::_global_ptr;
38 bool MemoryUsage::_recursion_protect =
false;
41 double MemoryUsage::AgeHistogram::_cutoff[MemoryUsage::AgeHistogram::num_buckets] = {
55 void MemoryUsage::TypeHistogram::
57 _counts[type].add_info(info);
63 class TypeHistogramCountSorter {
65 TypeHistogramCountSorter(
const MemoryUsagePointerCounts &count,
71 bool operator < (
const TypeHistogramCountSorter &other)
const {
72 return other._count < _count;
74 MemoryUsagePointerCounts _count;
83 void MemoryUsage::TypeHistogram::
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));
95 sort(count_sorter.begin(), count_sorter.end());
97 CountSorter::const_iterator vi;
98 for (vi = count_sorter.begin(); vi != count_sorter.end(); ++vi) {
105 nout <<
" : " << (*vi)._count <<
"\n";
114 void MemoryUsage::TypeHistogram::
124 MemoryUsage::AgeHistogram::
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);
146 void MemoryUsage::AgeHistogram::
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);
153 nout << _cutoff[num_buckets - 1] <<
" seconds old and up : ";
154 _counts[num_buckets - 1].output(nout);
163 void MemoryUsage::AgeHistogram::
165 for (
int i = 0; i < num_buckets; i++) {
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]) {
183 <<
"No suitable bucket for age " << age <<
"\n";
195 heap_alloc_single(
size_t size) {
198 if (_recursion_protect) {
200 if (express_cat.is_spam()) {
202 <<
"Allocating pointer " << (
void *)ptr
203 <<
" during recursion protect.\n";
207 if (_track_memory_usage) {
217 get_global_ptr()->ns_record_void_pointer(ptr, size);
234 heap_free_single(
void *ptr) {
235 if (_recursion_protect) {
236 if (express_cat.is_spam()) {
238 <<
"Deleting pointer " << (
void *)ptr
239 <<
" during recursion protect.\n";
244 if (_track_memory_usage) {
251 ns_remove_void_pointer(ptr);
267 heap_alloc_array(
size_t size) {
270 if (_recursion_protect) {
272 if (express_cat.is_spam()) {
274 <<
"Allocating array pointer " << (
void *)ptr
275 <<
" during recursion protect.\n";
279 if (_track_memory_usage) {
289 get_global_ptr()->ns_record_void_pointer(ptr, size);
306 heap_realloc_array(
void *ptr,
size_t size) {
307 if (_recursion_protect) {
309 if (express_cat.is_spam()) {
311 <<
"Reallocating array pointer " << (
void *)ptr
312 <<
" during recursion protect.\n";
316 if (_track_memory_usage) {
317 get_global_ptr()->ns_remove_void_pointer(ptr);
327 get_global_ptr()->ns_record_void_pointer(ptr, size);
344 heap_free_array(
void *ptr) {
345 if (_recursion_protect) {
346 if (express_cat.is_spam()) {
348 <<
"Deleting pointer " << (
void *)ptr
349 <<
" during recursion protect.\n";
354 if (_track_memory_usage) {
361 ns_remove_void_pointer(ptr);
382 if (_recursion_protect || !_track_memory_usage) {
386 if (express_cat.is_spam()) {
388 <<
"Marking pointer " << ptr <<
", size " << size
389 <<
", ref_ptr = " << ref_ptr <<
"\n";
394 ns_record_void_pointer(ptr, size);
402 ti = _table.find(ptr);
403 nassertv(ti != _table.end());
404 MemoryInfo *info = (*ti).second;
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;
411 if (ref_ptr != ptr) {
412 _recursion_protect =
true;
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";
423 _recursion_protect =
false;
429 ns_remove_void_pointer(ptr);
433 #if (defined(WIN32_VC) || defined (WIN64_VC))&& defined(_DEBUG) 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();
448 switch (alloc_type) {
454 increment = size - _msize(ptr);
458 increment = - ((int)_msize(ptr));
462 mu->_total_size += increment;
465 #endif // WIN32_VC && _DEBUG 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."));
489 _startup_track_memory_usage = _track_memory_usage;
492 express_cat->is_info();
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."));
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;
505 _count_memory_usage =
false;
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;
517 #ifdef USE_MEMORY_NOWRAPPERS 518 #error Cannot compile MemoryUsage without malloc wrappers! 521 #if (defined(WIN32_VC) || defined(WIN64_VC)) && defined(_DEBUG) 525 _CrtSetAllocHook(&win32_malloc_hook);
526 _count_memory_usage =
true;
529 _info_set_dirty =
false;
532 _current_cpp_size = 0;
547 overflow_heap_size() {
548 MemoryHook::overflow_heap_size();
551 <<
"Total allocated memory has reached " 552 << get_panda_heap_single_size() + get_panda_heap_array_size()
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()
563 _track_memory_usage =
true;
564 _report_memory_usage =
true;
575 if (_track_memory_usage) {
578 _recursion_protect =
true;
579 pair<Table::iterator, bool> insert_result =
580 _table.insert(Table::value_type((
void *)ptr, (MemoryInfo *)NULL));
583 assert(insert_result.first != _table.end());
585 if (insert_result.second) {
586 (*insert_result.first).second =
new MemoryInfo;
587 _info_set_dirty =
true;
591 MemoryInfo *info = (*insert_result.first).second;
595 nassertv(info->_ref_ptr == NULL || info->_ref_ptr == ptr);
597 info->_ref_ptr = ptr;
598 info->_static_type = ReferenceCount::get_class_type();
599 info->_dynamic_type = ReferenceCount::get_class_type();
601 info->_freeze_index = _freeze_index;
602 info->_flags |= MemoryInfo::F_reconsider_dynamic_type;
607 _recursion_protect =
false;
609 if (_report_memory_usage) {
611 if (now - _last_report_time > _report_memory_interval) {
612 _last_report_time = now;
614 <<
"*** Current memory usage: " << get_total_size() <<
"\n";
615 show_current_types();
632 if (_track_memory_usage) {
634 ti = _table.find(ptr);
635 if (ti == _table.end()) {
636 if (_startup_track_memory_usage) {
638 <<
"Attempt to update type to " << type <<
" for unrecorded pointer " 639 << (
void *)ptr <<
"!\n";
645 MemoryInfo *info = (*ti).second;
647 info->update_type_handle(info->_static_type, type);
648 info->determine_dynamic_type();
650 consolidate_void_ptr(info);
666 if (_track_memory_usage) {
668 ti = _table.find(ptr);
669 if (ti == _table.end()) {
670 if (_startup_track_memory_usage) {
672 <<
"Attempt to update type to " << typed_ptr->get_type()
673 <<
" for unrecorded pointer " 674 << (
void *)ptr <<
"!\n";
679 MemoryInfo *info = (*ti).second;
680 info->_typed_ptr = typed_ptr;
681 info->determine_dynamic_type();
683 consolidate_void_ptr(info);
695 if (_track_memory_usage) {
697 ti = _table.find(ptr);
698 if (ti == _table.end()) {
699 if (_startup_track_memory_usage) {
701 <<
"Attempt to remove pointer " << (
void *)ptr
702 <<
", not in table.\n" 703 <<
"Possibly a double-destruction.\n";
709 MemoryInfo *info = (*ti).second;
711 if (info->_ref_ptr == NULL) {
713 <<
"Pointer " << (
void *)ptr <<
" deleted twice!\n";
716 nassertv(info->_ref_ptr == ptr);
718 if (express_cat.is_spam()) {
720 <<
"Removing ReferenceCount pointer " << (
void *)ptr <<
"\n";
726 if (info->_freeze_index == _freeze_index) {
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;
737 if (ptr != info->_void_ptr || info->_void_ptr == NULL) {
742 _recursion_protect =
true;
744 _recursion_protect =
false;
746 if (info->_void_ptr == NULL) {
748 _total_cpp_size -= info->_size;
749 if (info->_freeze_index == _freeze_index) {
750 _current_cpp_size -= info->_size;
754 _info_set_dirty =
true;
769 ns_record_void_pointer(
void *ptr,
size_t size) {
770 if (_track_memory_usage) {
771 if (express_cat.is_spam()) {
773 <<
"Recording void pointer " << (
void *)ptr <<
"\n";
779 _recursion_protect =
true;
780 pair<Table::iterator, bool> insert_result =
781 _table.insert(Table::value_type((
void *)ptr, (MemoryInfo *)NULL));
783 assert(insert_result.first != _table.end());
785 if (insert_result.second) {
786 (*insert_result.first).second =
new MemoryInfo;
787 _info_set_dirty =
true;
791 MemoryInfo *info = (*insert_result.first).second;
794 if (info->_void_ptr != (
void *)NULL) {
796 <<
"Void pointer " << (
void *)ptr <<
" recorded twice!\n";
800 if (info->_freeze_index == _freeze_index) {
801 _current_cpp_size += size - info->_size;
803 _current_cpp_size += size;
805 _total_cpp_size += size - info->_size;
807 info->_void_ptr = ptr;
810 info->_freeze_index = _freeze_index;
811 info->_flags |= MemoryInfo::F_size_known;
816 _recursion_protect =
false;
827 ns_remove_void_pointer(
void *ptr) {
828 if (_track_memory_usage) {
829 if (express_cat.is_spam()) {
831 <<
"Removing void pointer " << (
void *)ptr <<
"\n";
835 ti = _table.find(ptr);
836 if (ti == _table.end()) {
847 MemoryInfo *info = (*ti).second;
849 if (info->_void_ptr == (
void *)NULL) {
851 <<
"Pointer " << (
void *)ptr <<
" deleted twice!\n";
854 nassertv(info->_void_ptr == ptr);
858 <<
"Pointer " << (
void *)ptr
859 <<
" did not destruct before being deleted!\n";
860 if (info->_ref_ptr != ptr) {
861 remove_pointer(info->_ref_ptr);
865 info->_void_ptr = NULL;
871 _recursion_protect =
true;
873 _recursion_protect =
false;
875 _total_cpp_size -= info->_size;
876 if (info->_freeze_index == _freeze_index) {
878 _current_cpp_size -= info->_size;
881 _info_set_dirty =
true;
892 ns_get_num_pointers() {
893 nassertr(_track_memory_usage, 0);
904 ns_get_pointers(MemoryUsagePointers &result) {
905 nassertv(_track_memory_usage);
908 if (_info_set_dirty) {
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 &&
918 result.add_entry(info->_ref_ptr, info->_typed_ptr, info->get_type(),
932 ns_get_pointers_of_type(MemoryUsagePointers &result,
TypeHandle type) {
933 nassertv(_track_memory_usage);
936 if (_info_set_dirty) {
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 &&
949 result.add_entry(info->_ref_ptr, info->_typed_ptr, info_type,
964 ns_get_pointers_of_age(MemoryUsagePointers &result,
965 double from,
double to) {
966 nassertv(_track_memory_usage);
969 if (_info_set_dirty) {
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 &&
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);
1012 ns_get_pointers_with_zero_count(MemoryUsagePointers &result) {
1013 nassertv(_track_memory_usage);
1016 if (_info_set_dirty) {
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 &&
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(),
1048 _current_cpp_size = 0;
1049 _trend_types.clear();
1050 _trend_ages.clear();
1061 ns_show_current_types() {
1062 nassertv(_track_memory_usage);
1065 if (_info_set_dirty) {
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);
1078 _recursion_protect =
false;
1089 ns_show_trend_types() {
1090 _trend_types.show();
1100 ns_show_current_ages() {
1101 nassertv(_track_memory_usage);
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);
1116 _recursion_protect =
false;
1127 ns_show_trend_ages() {
1141 consolidate_void_ptr(MemoryInfo *info) {
1142 if (info->is_size_known()) {
1154 if ((
void *)typed_ptr == (
void *)info->_ref_ptr) {
1161 nassertv(info->_void_ptr == NULL);
1164 ti = _table.find(typed_ptr);
1165 if (ti == _table.end()) {
1171 MemoryInfo *typed_info = (*ti).second;
1173 nassertv(typed_info->_void_ptr == typed_ptr &&
1174 typed_info->_ref_ptr == NULL);
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;
1187 if (info->_freeze_index == _freeze_index) {
1189 _current_cpp_size -= info->_size;
1192 _info_set_dirty =
true;
1195 (*ti).second = info;
1206 refresh_info_set() {
1207 if (!_info_set_dirty) {
1213 _recursion_protect =
true;
1217 for (ti = _table.begin(); ti != _table.end(); ++ti) {
1218 _info_set.insert((*ti).second);
1221 _recursion_protect =
false;
1223 _info_set_dirty =
false;
1227 #endif // DO_MEMORY_USAGE static TrueClock * get_global_ptr()
Returns a pointer to the one TrueClock object in the world.
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.
virtual void * heap_alloc_single(size_t size)
Allocates a block of memory from the heap, similar to malloc().
bool is_derived_from(TypeHandle parent, TypedObject *object=(TypedObject *) NULL) const
Returns true if this type is derived from the indicated type, false otherwise.
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...
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...
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.