00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "memoryUsage.h"
00016
00017 #ifdef DO_MEMORY_USAGE
00018
00019 #include "memoryUsagePointers.h"
00020 #include "trueClock.h"
00021 #include "typedReferenceCount.h"
00022 #include "mutexImpl.h"
00023 #include "interrogate_request.h"
00024
00025 #if (defined(WIN32_VC) || defined (WIN64_VC)) && defined(_DEBUG)
00026 #include <crtdbg.h>
00027 #endif
00028
00029 #include "config_express.h"
00030 #include <algorithm>
00031 #include <iterator>
00032
00033 MemoryUsage *MemoryUsage::_global_ptr;
00034
00035
00036
00037 bool MemoryUsage::_recursion_protect = false;
00038
00039
00040 double MemoryUsage::AgeHistogram::_cutoff[MemoryUsage::AgeHistogram::num_buckets] = {
00041 0.0,
00042 0.1,
00043 1.0,
00044 10.0,
00045 60.0,
00046 };
00047
00048
00049
00050
00051
00052
00053
00054 void MemoryUsage::TypeHistogram::
00055 add_info(TypeHandle type, MemoryInfo *info) {
00056 _counts[type].add_info(info);
00057 }
00058
00059
00060
00061
00062 class TypeHistogramCountSorter {
00063 public:
00064 TypeHistogramCountSorter(const MemoryUsagePointerCounts &count,
00065 TypeHandle type) :
00066 _count(count),
00067 _type(type)
00068 {
00069 }
00070 bool operator < (const TypeHistogramCountSorter &other) const {
00071 return other._count < _count;
00072 }
00073 MemoryUsagePointerCounts _count;
00074 TypeHandle _type;
00075 };
00076
00077
00078
00079
00080
00081
00082 void MemoryUsage::TypeHistogram::
00083 show() const {
00084
00085
00086 typedef vector<TypeHistogramCountSorter> CountSorter;
00087 CountSorter count_sorter;
00088 Counts::const_iterator ci;
00089 for (ci = _counts.begin(); ci != _counts.end(); ++ci) {
00090 count_sorter.push_back
00091 (TypeHistogramCountSorter((*ci).second, (*ci).first));
00092 }
00093
00094 sort(count_sorter.begin(), count_sorter.end());
00095
00096 CountSorter::const_iterator vi;
00097 for (vi = count_sorter.begin(); vi != count_sorter.end(); ++vi) {
00098 TypeHandle type = (*vi)._type;
00099 if (type == TypeHandle::none()) {
00100 nout << "unknown";
00101 } else {
00102 nout << type;
00103 }
00104 nout << " : " << (*vi)._count << "\n";
00105 }
00106 }
00107
00108
00109
00110
00111
00112
00113 void MemoryUsage::TypeHistogram::
00114 clear() {
00115 _counts.clear();
00116 }
00117
00118
00119
00120
00121
00122
00123 MemoryUsage::AgeHistogram::
00124 AgeHistogram() {
00125 clear();
00126 }
00127
00128
00129
00130
00131
00132
00133 void MemoryUsage::AgeHistogram::
00134 add_info(double age, MemoryInfo *info) {
00135 int bucket = choose_bucket(age);
00136 nassertv(bucket >= 0 && bucket < num_buckets);
00137 _counts[bucket].add_info(info);
00138 }
00139
00140
00141
00142
00143
00144
00145 void MemoryUsage::AgeHistogram::
00146 show() const {
00147 for (int i = 0; i < num_buckets - 1; i++) {
00148 nout << _cutoff[i] << " to " << _cutoff[i + 1] << " seconds old : ";
00149 _counts[i].output(nout);
00150 nout << "\n";
00151 }
00152 nout << _cutoff[num_buckets - 1] << " seconds old and up : ";
00153 _counts[num_buckets - 1].output(nout);
00154 nout << "\n";
00155 }
00156
00157
00158
00159
00160
00161
00162 void MemoryUsage::AgeHistogram::
00163 clear() {
00164 for (int i = 0; i < num_buckets; i++) {
00165 _counts[i].clear();
00166 }
00167 }
00168
00169
00170
00171
00172
00173
00174 int MemoryUsage::AgeHistogram::
00175 choose_bucket(double age) const {
00176 for (int i = num_buckets - 1; i >= 0; i--) {
00177 if (age >= _cutoff[i]) {
00178 return i;
00179 }
00180 }
00181 express_cat.error()
00182 << "No suitable bucket for age " << age << "\n";
00183 return 0;
00184 }
00185
00186
00187
00188
00189
00190
00191
00192
00193 void *MemoryUsage::
00194 heap_alloc_single(size_t size) {
00195 void *ptr;
00196
00197 if (_recursion_protect) {
00198 ptr = MemoryHook::heap_alloc_single(size);
00199 if (express_cat.is_spam()) {
00200 express_cat.spam()
00201 << "Allocating pointer " << (void *)ptr
00202 << " during recursion protect.\n";
00203 }
00204
00205 } else {
00206 if (_track_memory_usage) {
00207 ptr = MemoryHook::heap_alloc_single(size);
00208
00209
00210
00211
00212
00213
00214
00215
00216 get_global_ptr()->ns_record_void_pointer(ptr, size);
00217
00218 } else {
00219 ptr = MemoryHook::heap_alloc_single(size);
00220 }
00221 }
00222
00223 return ptr;
00224 }
00225
00226
00227
00228
00229
00230
00231
00232 void MemoryUsage::
00233 heap_free_single(void *ptr) {
00234 if (_recursion_protect) {
00235 if (express_cat.is_spam()) {
00236 express_cat.spam()
00237 << "Deleting pointer " << (void *)ptr
00238 << " during recursion protect.\n";
00239 }
00240 MemoryHook::heap_free_single(ptr);
00241
00242 } else {
00243 if (_track_memory_usage) {
00244
00245
00246
00247
00248
00249
00250 ns_remove_void_pointer(ptr);
00251 MemoryHook::heap_free_single(ptr);
00252 } else {
00253 MemoryHook::heap_free_single(ptr);
00254 }
00255 }
00256 }
00257
00258
00259
00260
00261
00262
00263
00264
00265 void *MemoryUsage::
00266 heap_alloc_array(size_t size) {
00267 void *ptr;
00268
00269 if (_recursion_protect) {
00270 ptr = MemoryHook::heap_alloc_array(size);
00271 if (express_cat.is_spam()) {
00272 express_cat.spam()
00273 << "Allocating array pointer " << (void *)ptr
00274 << " during recursion protect.\n";
00275 }
00276
00277 } else {
00278 if (_track_memory_usage) {
00279 ptr = MemoryHook::heap_alloc_array(size);
00280
00281
00282
00283
00284
00285
00286
00287
00288 get_global_ptr()->ns_record_void_pointer(ptr, size);
00289
00290 } else {
00291 ptr = MemoryHook::heap_alloc_array(size);
00292 }
00293 }
00294
00295 return ptr;
00296 }
00297
00298
00299
00300
00301
00302
00303
00304 void *MemoryUsage::
00305 heap_realloc_array(void *ptr, size_t size) {
00306 if (_recursion_protect) {
00307 ptr = MemoryHook::heap_realloc_array(ptr, size);
00308 if (express_cat.is_spam()) {
00309 express_cat.spam()
00310 << "Reallocating array pointer " << (void *)ptr
00311 << " during recursion protect.\n";
00312 }
00313
00314 } else {
00315 if (_track_memory_usage) {
00316 get_global_ptr()->ns_remove_void_pointer(ptr);
00317 ptr = MemoryHook::heap_realloc_array(ptr, size);
00318
00319
00320
00321
00322
00323
00324
00325
00326 get_global_ptr()->ns_record_void_pointer(ptr, size);
00327
00328 } else {
00329 ptr = MemoryHook::heap_realloc_array(ptr, size);
00330 }
00331 }
00332
00333 return ptr;
00334 }
00335
00336
00337
00338
00339
00340
00341
00342 void MemoryUsage::
00343 heap_free_array(void *ptr) {
00344 if (_recursion_protect) {
00345 if (express_cat.is_spam()) {
00346 express_cat.spam()
00347 << "Deleting pointer " << (void *)ptr
00348 << " during recursion protect.\n";
00349 }
00350 MemoryHook::heap_free_array(ptr);
00351
00352 } else {
00353 if (_track_memory_usage) {
00354
00355
00356
00357
00358
00359
00360 ns_remove_void_pointer(ptr);
00361 MemoryHook::heap_free_array(ptr);
00362 } else {
00363 MemoryHook::heap_free_array(ptr);
00364 }
00365 }
00366 }
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379 void MemoryUsage::
00380 mark_pointer(void *ptr, size_t size, ReferenceCount *ref_ptr) {
00381 if (_recursion_protect || !_track_memory_usage) {
00382 return;
00383 }
00384
00385 if (express_cat.is_spam()) {
00386 express_cat.spam()
00387 << "Marking pointer " << ptr << ", size " << size
00388 << ", ref_ptr = " << ref_ptr << "\n";
00389 }
00390
00391 if (size != 0) {
00392
00393 ns_record_void_pointer(ptr, size);
00394
00395 if (ref_ptr != (ReferenceCount *)NULL) {
00396
00397
00398
00399
00400 Table::iterator ti;
00401 ti = _table.find(ptr);
00402 nassertv(ti != _table.end());
00403 MemoryInfo *info = (*ti).second;
00404
00405 info->_ref_ptr = ref_ptr;
00406 info->_static_type = ReferenceCount::get_class_type();
00407 info->_dynamic_type = ReferenceCount::get_class_type();
00408 info->_flags |= MemoryInfo::F_reconsider_dynamic_type;
00409
00410 if (ref_ptr != ptr) {
00411 _recursion_protect = true;
00412
00413 pair<Table::iterator, bool> insert_result =
00414 _table.insert(Table::value_type((void *)ref_ptr, info));
00415 assert(insert_result.first != _table.end());
00416 if (!insert_result.second) {
00417 express_cat.warning()
00418 << "Attempt to mark pointer " << ptr << " as ReferenceCount "
00419 << ref_ptr << ", which was already allocated.\n";
00420 }
00421
00422 _recursion_protect = false;
00423 }
00424 }
00425
00426 } else {
00427
00428 ns_remove_void_pointer(ptr);
00429 }
00430 }
00431
00432 #if (defined(WIN32_VC) || defined (WIN64_VC))&& defined(_DEBUG)
00433
00434
00435
00436
00437
00438
00439
00440
00441 int MemoryUsage::
00442 win32_malloc_hook(int alloc_type, void *ptr,
00443 size_t size, int block_use, long request,
00444 const unsigned char *filename, int line) {
00445 MemoryUsage *mu = get_global_ptr();
00446 int increment = 0;
00447 switch (alloc_type) {
00448 case _HOOK_ALLOC:
00449 increment = size;
00450 break;
00451
00452 case _HOOK_REALLOC:
00453 increment = size - _msize(ptr);
00454 break;
00455
00456 case _HOOK_FREE:
00457 increment = - ((int)_msize(ptr));
00458 break;
00459 }
00460
00461 mu->_total_size += increment;
00462 return true;
00463 }
00464 #endif // WIN32_VC && _DEBUG
00465
00466
00467
00468
00469
00470
00471
00472
00473 MemoryUsage::
00474 MemoryUsage(const MemoryHook ©) : MemoryHook(copy) {
00475
00476
00477
00478
00479
00480 _track_memory_usage = ConfigVariableBool
00481 ("track-memory-usage", false,
00482 PRC_DESC("Set this to true to enable full-force tracking of C++ allocations "
00483 "and recordkeeping by type. It's quite expensive."));
00484
00485
00486
00487
00488 _startup_track_memory_usage = _track_memory_usage;
00489
00490
00491 express_cat->is_info();
00492
00493 _report_memory_usage = ConfigVariableBool
00494 ("report-memory-usage", false,
00495 PRC_DESC("Set this true to enable automatic reporting of allocated objects "
00496 "at the interval specified by report-memory-interval. This also "
00497 "requires track-memory-usage."));
00498 _report_memory_interval = ConfigVariableDouble
00499 ("report-memory-interval", 5.0,
00500 PRC_DESC("This is the interval, in seconds, for reports of currently allocated "
00501 "memory, when report-memory-usage is true."));
00502 _last_report_time = 0.0;
00503
00504 _count_memory_usage = false;
00505
00506 PN_int64 max_heap_size = ConfigVariableInt64
00507 ("max-heap-size", 0,
00508 PRC_DESC("If this is nonzero, it is the maximum number of bytes expected "
00509 "to be allocated on the heap before we enter report-memory-usage "
00510 "mode automatically. The assumption is that once this limit "
00511 "has been crossed, we must be leaking."));
00512 if (max_heap_size != 0) {
00513 _max_heap_size = (size_t)max_heap_size;
00514 }
00515
00516 #ifdef USE_MEMORY_NOWRAPPERS
00517 #error Cannot compile MemoryUsage without malloc wrappers!
00518 #endif
00519
00520 #if (defined(WIN32_VC) || defined(WIN64_VC)) && defined(_DEBUG)
00521
00522
00523
00524 _CrtSetAllocHook(&win32_malloc_hook);
00525 _count_memory_usage = true;
00526 #endif
00527
00528 _info_set_dirty = false;
00529 _freeze_index = 0;
00530 _count = 0;
00531 _current_cpp_size = 0;
00532 _total_cpp_size = 0;
00533 _total_size = 0;
00534 }
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545 void MemoryUsage::
00546 overflow_heap_size() {
00547 MemoryHook::overflow_heap_size();
00548
00549 express_cat.error()
00550 << "Total allocated memory has reached "
00551 << get_panda_heap_single_size() + get_panda_heap_array_size()
00552 << " bytes."
00553 << "\n heap single: " << get_panda_heap_single_size()
00554 << "\n heap array: " << get_panda_heap_array_size()
00555 << "\n heap overhead: " << get_panda_heap_overhead()
00556 << "\n mmap: " << get_panda_mmap_size()
00557 << "\n external: " << get_external_size()
00558 << "\n total: " << get_total_size()
00559 << "\n";
00560
00561
00562 _track_memory_usage = true;
00563 _report_memory_usage = true;
00564 }
00565
00566
00567
00568
00569
00570
00571
00572 MemoryUsage *MemoryUsage::
00573 get_global_ptr() {
00574 if (_global_ptr == (MemoryUsage *)NULL) {
00575 init_memory_hook();
00576 _global_ptr = new MemoryUsage(*memory_hook);
00577 memory_hook = _global_ptr;
00578 }
00579
00580 return _global_ptr;
00581 }
00582
00583
00584
00585
00586
00587
00588
00589
00590 void MemoryUsage::
00591 ns_record_pointer(ReferenceCount *ptr) {
00592 if (_track_memory_usage) {
00593
00594
00595 _recursion_protect = true;
00596 pair<Table::iterator, bool> insert_result =
00597 _table.insert(Table::value_type((void *)ptr, (MemoryInfo *)NULL));
00598
00599
00600 assert(insert_result.first != _table.end());
00601
00602 if (insert_result.second) {
00603 (*insert_result.first).second = new MemoryInfo;
00604 _info_set_dirty = true;
00605 ++_count;
00606 }
00607
00608 MemoryInfo *info = (*insert_result.first).second;
00609
00610
00611
00612 nassertv(info->_ref_ptr == NULL || info->_ref_ptr == ptr);
00613
00614 info->_ref_ptr = ptr;
00615 info->_static_type = ReferenceCount::get_class_type();
00616 info->_dynamic_type = ReferenceCount::get_class_type();
00617 info->_time = TrueClock::get_global_ptr()->get_long_time();
00618 info->_freeze_index = _freeze_index;
00619 info->_flags |= MemoryInfo::F_reconsider_dynamic_type;
00620
00621
00622
00623
00624 _recursion_protect = false;
00625
00626 if (_report_memory_usage) {
00627 double now = TrueClock::get_global_ptr()->get_long_time();
00628 if (now - _last_report_time > _report_memory_interval) {
00629 _last_report_time = now;
00630 express_cat.info()
00631 << "*** Current memory usage: " << get_total_size() << "\n";
00632 show_current_types();
00633 }
00634 }
00635 }
00636 }
00637
00638
00639
00640
00641
00642
00643
00644
00645
00646
00647 void MemoryUsage::
00648 ns_update_type(ReferenceCount *ptr, TypeHandle type) {
00649 if (_track_memory_usage) {
00650 Table::iterator ti;
00651 ti = _table.find(ptr);
00652 if (ti == _table.end()) {
00653 if (_startup_track_memory_usage) {
00654 express_cat.error()
00655 << "Attempt to update type to " << type << " for unrecorded pointer "
00656 << (void *)ptr << "!\n";
00657 nassertv(false);
00658 }
00659 return;
00660 }
00661
00662 MemoryInfo *info = (*ti).second;
00663
00664 info->update_type_handle(info->_static_type, type);
00665 info->determine_dynamic_type();
00666
00667 consolidate_void_ptr(info);
00668 }
00669 }
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680
00681 void MemoryUsage::
00682 ns_update_type(ReferenceCount *ptr, TypedObject *typed_ptr) {
00683 if (_track_memory_usage) {
00684 Table::iterator ti;
00685 ti = _table.find(ptr);
00686 if (ti == _table.end()) {
00687 if (_startup_track_memory_usage) {
00688 express_cat.error()
00689 << "Attempt to update type to " << typed_ptr->get_type()
00690 << " for unrecorded pointer "
00691 << (void *)ptr << "!\n";
00692 }
00693 return;
00694 }
00695
00696 MemoryInfo *info = (*ti).second;
00697 info->_typed_ptr = typed_ptr;
00698 info->determine_dynamic_type();
00699
00700 consolidate_void_ptr(info);
00701 }
00702 }
00703
00704
00705
00706
00707
00708
00709
00710 void MemoryUsage::
00711 ns_remove_pointer(ReferenceCount *ptr) {
00712 if (_track_memory_usage) {
00713 Table::iterator ti;
00714 ti = _table.find(ptr);
00715 if (ti == _table.end()) {
00716 if (_startup_track_memory_usage) {
00717 express_cat.error()
00718 << "Attempt to remove pointer " << (void *)ptr
00719 << ", not in table.\n"
00720 << "Possibly a double-destruction.\n";
00721 nassertv(false);
00722 }
00723 return;
00724 }
00725
00726 MemoryInfo *info = (*ti).second;
00727
00728 if (info->_ref_ptr == NULL) {
00729 express_cat.error()
00730 << "Pointer " << (void *)ptr << " deleted twice!\n";
00731 return;
00732 }
00733 nassertv(info->_ref_ptr == ptr);
00734
00735 if (express_cat.is_spam()) {
00736 express_cat.spam()
00737 << "Removing ReferenceCount pointer " << (void *)ptr << "\n";
00738 }
00739
00740 info->_ref_ptr = (ReferenceCount *)NULL;
00741 info->_typed_ptr = (TypedObject *)NULL;
00742
00743 if (info->_freeze_index == _freeze_index) {
00744 double now = TrueClock::get_global_ptr()->get_long_time();
00745
00746
00747
00748 _recursion_protect = true;
00749 _trend_types.add_info(info->get_type(), info);
00750 _trend_ages.add_info(now - info->_time, info);
00751 _recursion_protect = false;
00752 }
00753
00754 if (ptr != info->_void_ptr || info->_void_ptr == NULL) {
00755
00756
00757
00758
00759 _recursion_protect = true;
00760 _table.erase(ti);
00761 _recursion_protect = false;
00762
00763 if (info->_void_ptr == NULL) {
00764
00765 _total_cpp_size -= info->_size;
00766 if (info->_freeze_index == _freeze_index) {
00767 _current_cpp_size -= info->_size;
00768 _count--;
00769 }
00770
00771 _info_set_dirty = true;
00772 delete info;
00773 }
00774 }
00775 }
00776 }
00777
00778
00779
00780
00781
00782
00783
00784
00785 void MemoryUsage::
00786 ns_record_void_pointer(void *ptr, size_t size) {
00787 if (_track_memory_usage) {
00788 if (express_cat.is_spam()) {
00789 express_cat.spam()
00790 << "Recording void pointer " << (void *)ptr << "\n";
00791 }
00792
00793
00794
00795
00796 _recursion_protect = true;
00797 pair<Table::iterator, bool> insert_result =
00798 _table.insert(Table::value_type((void *)ptr, (MemoryInfo *)NULL));
00799
00800 assert(insert_result.first != _table.end());
00801
00802 if (insert_result.second) {
00803 (*insert_result.first).second = new MemoryInfo;
00804 _info_set_dirty = true;
00805 ++_count;
00806 }
00807
00808 MemoryInfo *info = (*insert_result.first).second;
00809
00810
00811 if (info->_void_ptr != (void *)NULL) {
00812 express_cat.error()
00813 << "Void pointer " << (void *)ptr << " recorded twice!\n";
00814 nassertv(false);
00815 }
00816
00817 if (info->_freeze_index == _freeze_index) {
00818 _current_cpp_size += size - info->_size;
00819 } else {
00820 _current_cpp_size += size;
00821 }
00822 _total_cpp_size += size - info->_size;
00823
00824 info->_void_ptr = ptr;
00825 info->_size = size;
00826 info->_time = TrueClock::get_global_ptr()->get_long_time();
00827 info->_freeze_index = _freeze_index;
00828 info->_flags |= MemoryInfo::F_size_known;
00829
00830
00831
00832
00833 _recursion_protect = false;
00834 }
00835 }
00836
00837
00838
00839
00840
00841
00842
00843 void MemoryUsage::
00844 ns_remove_void_pointer(void *ptr) {
00845 if (_track_memory_usage) {
00846 if (express_cat.is_spam()) {
00847 express_cat.spam()
00848 << "Removing void pointer " << (void *)ptr << "\n";
00849 }
00850
00851 Table::iterator ti;
00852 ti = _table.find(ptr);
00853 if (ti == _table.end()) {
00854
00855
00856
00857
00858
00859
00860
00861 return;
00862 }
00863
00864 MemoryInfo *info = (*ti).second;
00865
00866 if (info->_void_ptr == (void *)NULL) {
00867 express_cat.error()
00868 << "Pointer " << (void *)ptr << " deleted twice!\n";
00869 return;
00870 }
00871 nassertv(info->_void_ptr == ptr);
00872
00873 if (info->_ref_ptr != (ReferenceCount *)NULL) {
00874 express_cat.error()
00875 << "Pointer " << (void *)ptr
00876 << " did not destruct before being deleted!\n";
00877 if (info->_ref_ptr != ptr) {
00878 remove_pointer(info->_ref_ptr);
00879 }
00880 }
00881
00882 info->_void_ptr = NULL;
00883
00884
00885
00886
00887
00888 _recursion_protect = true;
00889 _table.erase(ti);
00890 _recursion_protect = false;
00891
00892 _total_cpp_size -= info->_size;
00893 if (info->_freeze_index == _freeze_index) {
00894 --_count;
00895 _current_cpp_size -= info->_size;
00896 }
00897
00898 _info_set_dirty = true;
00899 delete info;
00900 }
00901 }
00902
00903
00904
00905
00906
00907
00908 int MemoryUsage::
00909 ns_get_num_pointers() {
00910 nassertr(_track_memory_usage, 0);
00911 return _count;
00912 }
00913
00914
00915
00916
00917
00918
00919
00920 void MemoryUsage::
00921 ns_get_pointers(MemoryUsagePointers &result) {
00922 nassertv(_track_memory_usage);
00923 result.clear();
00924
00925 if (_info_set_dirty) {
00926 refresh_info_set();
00927 }
00928
00929 double now = TrueClock::get_global_ptr()->get_long_time();
00930 InfoSet::iterator si;
00931 for (si = _info_set.begin(); si != _info_set.end(); ++si) {
00932 MemoryInfo *info = (*si);
00933 if (info->_freeze_index == _freeze_index &&
00934 info->_ref_ptr != (ReferenceCount *)NULL) {
00935 result.add_entry(info->_ref_ptr, info->_typed_ptr, info->get_type(),
00936 now - info->_time);
00937 }
00938 }
00939 }
00940
00941
00942
00943
00944
00945
00946
00947
00948 void MemoryUsage::
00949 ns_get_pointers_of_type(MemoryUsagePointers &result, TypeHandle type) {
00950 nassertv(_track_memory_usage);
00951 result.clear();
00952
00953 if (_info_set_dirty) {
00954 refresh_info_set();
00955 }
00956
00957 double now = TrueClock::get_global_ptr()->get_long_time();
00958 InfoSet::iterator si;
00959 for (si = _info_set.begin(); si != _info_set.end(); ++si) {
00960 MemoryInfo *info = (*si);
00961 if (info->_freeze_index == _freeze_index &&
00962 info->_ref_ptr != (ReferenceCount *)NULL) {
00963 TypeHandle info_type = info->get_type();
00964 if (info_type != TypeHandle::none() &&
00965 info_type.is_derived_from(type)) {
00966 result.add_entry(info->_ref_ptr, info->_typed_ptr, info_type,
00967 now - info->_time);
00968 }
00969 }
00970 }
00971 }
00972
00973
00974
00975
00976
00977
00978
00979
00980 void MemoryUsage::
00981 ns_get_pointers_of_age(MemoryUsagePointers &result,
00982 double from, double to) {
00983 nassertv(_track_memory_usage);
00984 result.clear();
00985
00986 if (_info_set_dirty) {
00987 refresh_info_set();
00988 }
00989
00990 double now = TrueClock::get_global_ptr()->get_long_time();
00991 InfoSet::iterator si;
00992 for (si = _info_set.begin(); si != _info_set.end(); ++si) {
00993 MemoryInfo *info = (*si);
00994 if (info->_freeze_index == _freeze_index &&
00995 info->_ref_ptr != (ReferenceCount *)NULL) {
00996 double age = now - info->_time;
00997 if ((age >= from && age <= to) ||
00998 (age >= to && age <= from)) {
00999 result.add_entry(info->_ref_ptr, info->_typed_ptr, info->get_type(), age);
01000 }
01001 }
01002 }
01003 }
01004
01005
01006
01007
01008
01009
01010
01011
01012
01013
01014
01015
01016
01017
01018
01019
01020
01021
01022
01023
01024
01025
01026
01027
01028 void MemoryUsage::
01029 ns_get_pointers_with_zero_count(MemoryUsagePointers &result) {
01030 nassertv(_track_memory_usage);
01031 result.clear();
01032
01033 if (_info_set_dirty) {
01034 refresh_info_set();
01035 }
01036
01037 double now = TrueClock::get_global_ptr()->get_long_time();
01038 InfoSet::iterator si;
01039 for (si = _info_set.begin(); si != _info_set.end(); ++si) {
01040 MemoryInfo *info = (*si);
01041 if (info->_freeze_index == _freeze_index &&
01042 info->_ref_ptr != (ReferenceCount *)NULL) {
01043 if (info->_ref_ptr->get_ref_count() == 0) {
01044 info->_ref_ptr->ref();
01045 result.add_entry(info->_ref_ptr, info->_typed_ptr, info->get_type(),
01046 now - info->_time);
01047 }
01048 }
01049 }
01050 }
01051
01052
01053
01054
01055
01056
01057
01058
01059
01060
01061
01062 void MemoryUsage::
01063 ns_freeze() {
01064 _count = 0;
01065 _current_cpp_size = 0;
01066 _trend_types.clear();
01067 _trend_ages.clear();
01068 _freeze_index++;
01069 }
01070
01071
01072
01073
01074
01075
01076
01077 void MemoryUsage::
01078 ns_show_current_types() {
01079 nassertv(_track_memory_usage);
01080 TypeHistogram hist;
01081
01082 if (_info_set_dirty) {
01083 refresh_info_set();
01084 }
01085
01086 _recursion_protect = true;
01087 InfoSet::iterator si;
01088 for (si = _info_set.begin(); si != _info_set.end(); ++si) {
01089 MemoryInfo *info = (*si);
01090 if (info->_freeze_index == _freeze_index) {
01091 hist.add_info(info->get_type(), info);
01092 }
01093 }
01094 hist.show();
01095 _recursion_protect = false;
01096 }
01097
01098
01099
01100
01101
01102
01103
01104
01105 void MemoryUsage::
01106 ns_show_trend_types() {
01107 _trend_types.show();
01108 }
01109
01110
01111
01112
01113
01114
01115
01116 void MemoryUsage::
01117 ns_show_current_ages() {
01118 nassertv(_track_memory_usage);
01119
01120 AgeHistogram hist;
01121 double now = TrueClock::get_global_ptr()->get_long_time();
01122
01123 _recursion_protect = true;
01124 InfoSet::iterator si;
01125 for (si = _info_set.begin(); si != _info_set.end(); ++si) {
01126 MemoryInfo *info = (*si);
01127 if (info->_freeze_index == _freeze_index) {
01128 hist.add_info(now - info->_time, info);
01129 }
01130 }
01131
01132 hist.show();
01133 _recursion_protect = false;
01134 }
01135
01136
01137
01138
01139
01140
01141
01142
01143 void MemoryUsage::
01144 ns_show_trend_ages() {
01145 _trend_ages.show();
01146 }
01147
01148
01149
01150
01151
01152
01153
01154
01155
01156
01157 void MemoryUsage::
01158 consolidate_void_ptr(MemoryInfo *info) {
01159 if (info->is_size_known()) {
01160
01161 return;
01162 }
01163
01164 if (info->_typed_ptr == (TypedObject *)NULL) {
01165
01166 return;
01167 }
01168
01169 TypedObject *typed_ptr = info->_typed_ptr;
01170
01171 if ((void *)typed_ptr == (void *)info->_ref_ptr) {
01172
01173
01174
01175 return;
01176 }
01177
01178 nassertv(info->_void_ptr == NULL);
01179
01180 Table::iterator ti;
01181 ti = _table.find(typed_ptr);
01182 if (ti == _table.end()) {
01183
01184 return;
01185 }
01186
01187
01188 MemoryInfo *typed_info = (*ti).second;
01189
01190 nassertv(typed_info->_void_ptr == typed_ptr &&
01191 typed_info->_ref_ptr == NULL);
01192
01193 info->_void_ptr = typed_info->_void_ptr;
01194 if (typed_info->is_size_known()) {
01195 info->_size = typed_info->get_size();
01196 info->_flags |= MemoryInfo::F_size_known;
01197 if (typed_info->_freeze_index == _freeze_index) {
01198 _current_cpp_size += info->_size;
01199 }
01200 }
01201
01202
01203
01204 if (info->_freeze_index == _freeze_index) {
01205 _count--;
01206 _current_cpp_size -= info->_size;
01207 }
01208
01209 _info_set_dirty = true;
01210 delete typed_info;
01211
01212 (*ti).second = info;
01213 }
01214
01215
01216
01217
01218
01219
01220
01221
01222 void MemoryUsage::
01223 refresh_info_set() {
01224 if (!_info_set_dirty) {
01225 return;
01226 }
01227
01228
01229
01230 _recursion_protect = true;
01231
01232 _info_set.clear();
01233 Table::iterator ti;
01234 for (ti = _table.begin(); ti != _table.end(); ++ti) {
01235 _info_set.insert((*ti).second);
01236 }
01237
01238 _recursion_protect = false;
01239
01240 _info_set_dirty = false;
01241 }
01242
01243
01244 #endif // DO_MEMORY_USAGE