00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #ifdef DO_MEMORY_USAGE
00020
00021 #include "memoryUsagePointers.h"
00022 #include "trueClock.h"
00023 #include "typedReferenceCount.h"
00024
00025 #include "memoryUsage.h"
00026 #include "interrogate_request.h"
00027
00028 #if defined(WIN32_VC) && defined(_DEBUG)
00029 #include <crtdbg.h>
00030 #endif
00031
00032 #include "config_express.h"
00033 #include <algorithm>
00034
00035
00036
00037 static bool _is_cpp_operator = false;
00038
00039
00040
00041 static bool _recursion_protect = false;
00042
00043 MemoryUsage *MemoryUsage::_global_ptr = (MemoryUsage *)NULL;
00044
00045
00046 double MemoryUsage::AgeHistogram::_cutoff[MemoryUsage::AgeHistogram::num_buckets] = {
00047 0.0,
00048 0.1,
00049 1.0,
00050 10.0,
00051 60.0,
00052 };
00053
00054
00055
00056
00057
00058
00059
00060 void MemoryUsage::TypeHistogram::
00061 add_info(TypeHandle type, MemoryInfo &info) {
00062 _counts[type].add_info(info);
00063 }
00064
00065
00066
00067
00068 class TypeHistogramCountSorter {
00069 public:
00070 TypeHistogramCountSorter(const MemoryUsagePointerCounts &count,
00071 TypeHandle type) :
00072 _count(count),
00073 _type(type)
00074 {
00075 }
00076 bool operator < (const TypeHistogramCountSorter &other) const {
00077 return other._count < _count;
00078 }
00079 MemoryUsagePointerCounts _count;
00080 TypeHandle _type;
00081 };
00082
00083
00084
00085
00086
00087
00088 void MemoryUsage::TypeHistogram::
00089 show() const {
00090
00091
00092 typedef vector<TypeHistogramCountSorter, dallocator<TypeHistogramCountSorter> > CountSorter;
00093 CountSorter count_sorter;
00094 Counts::const_iterator ci;
00095 for (ci = _counts.begin(); ci != _counts.end(); ++ci) {
00096 count_sorter.push_back
00097 (TypeHistogramCountSorter((*ci).second, (*ci).first));
00098 }
00099
00100 sort(count_sorter.begin(), count_sorter.end());
00101
00102 CountSorter::const_iterator vi;
00103 for (vi = count_sorter.begin(); vi != count_sorter.end(); ++vi) {
00104 TypeHandle type = (*vi)._type;
00105 if (type == TypeHandle::none()) {
00106 nout << "unknown";
00107 } else {
00108 nout << type;
00109 }
00110 nout << " : " << (*vi)._count << "\n";
00111 }
00112 }
00113
00114
00115
00116
00117
00118
00119 void MemoryUsage::TypeHistogram::
00120 clear() {
00121 _counts.clear();
00122 }
00123
00124
00125
00126
00127
00128
00129 MemoryUsage::AgeHistogram::
00130 AgeHistogram() {
00131 clear();
00132 }
00133
00134
00135
00136
00137
00138
00139 void MemoryUsage::AgeHistogram::
00140 add_info(double age, MemoryInfo &info) {
00141 int bucket = choose_bucket(age);
00142 nassertv(bucket >= 0 && bucket < num_buckets);
00143 _counts[bucket].add_info(info);
00144 }
00145
00146
00147
00148
00149
00150
00151 void MemoryUsage::AgeHistogram::
00152 show() const {
00153 for (int i = 0; i < num_buckets - 1; i++) {
00154 nout << _cutoff[i] << " to " << _cutoff[i + 1] << " seconds old : ";
00155 _counts[i].output(nout);
00156 nout << "\n";
00157 }
00158 nout << _cutoff[num_buckets - 1] << " seconds old and up : ";
00159 _counts[num_buckets - 1].output(nout);
00160 nout << "\n";
00161 }
00162
00163
00164
00165
00166
00167
00168 void MemoryUsage::AgeHistogram::
00169 clear() {
00170 for (int i = 0; i < num_buckets; i++) {
00171 _counts[i].clear();
00172 }
00173 }
00174
00175
00176
00177
00178
00179
00180 int MemoryUsage::AgeHistogram::
00181 choose_bucket(double age) const {
00182 for (int i = num_buckets - 1; i >= 0; i--) {
00183 if (age >= _cutoff[i]) {
00184 return i;
00185 }
00186 }
00187 express_cat.error()
00188 << "No suitable bucket for age " << age << "\n";
00189 return 0;
00190 }
00191
00192 #if defined(__GNUC__)
00193
00194
00195
00196
00197
00198
00199
00200 void MemoryUsage::
00201 record_pointer(ReferenceCount *ptr) {
00202 get_global_ptr()->ns_record_pointer(ptr);
00203 }
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214 void MemoryUsage::
00215 update_type(ReferenceCount *ptr, TypeHandle type) {
00216 get_global_ptr()->ns_update_type(ptr, type);
00217 }
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229 void MemoryUsage::
00230 update_type(ReferenceCount *ptr, TypedObject *typed_ptr) {
00231 get_global_ptr()->ns_update_type(ptr, typed_ptr);
00232 }
00233
00234
00235
00236
00237
00238
00239
00240 void MemoryUsage::
00241 remove_pointer(ReferenceCount *ptr) {
00242 get_global_ptr()->ns_remove_pointer(ptr);
00243 }
00244
00245 #endif // __GNUC__
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256 void *MemoryUsage::
00257 operator_new_handler(size_t size) {
00258 void *ptr;
00259
00260 if (_recursion_protect) {
00261 ptr = default_operator_new(size);
00262
00263 } else {
00264 MemoryUsage *mu = get_global_ptr();
00265 if (mu->_track_memory_usage) {
00266 ptr = default_operator_new(size);
00267 get_global_ptr()->ns_record_void_pointer(ptr, size);
00268
00269 } else {
00270 _is_cpp_operator = true;
00271 ptr = default_operator_new(size);
00272 _is_cpp_operator = false;
00273 }
00274 }
00275
00276 return ptr;
00277 }
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288 void MemoryUsage::
00289 operator_delete_handler(void *ptr) {
00290 if (_recursion_protect) {
00291 default_operator_delete(ptr);
00292
00293 } else {
00294 MemoryUsage *mu = get_global_ptr();
00295 if (mu->_track_memory_usage) {
00296 mu->ns_remove_void_pointer(ptr);
00297 default_operator_delete(ptr);
00298 } else {
00299 _is_cpp_operator = true;
00300 default_operator_delete(ptr);
00301 _is_cpp_operator = false;
00302 }
00303 }
00304 }
00305
00306 #if defined(WIN32_VC) && defined(_DEBUG)
00307
00308
00309
00310
00311
00312
00313
00314
00315 int MemoryUsage::
00316 win32_malloc_hook(int alloc_type, void *ptr,
00317 size_t size, int block_use, long request,
00318 const unsigned char *filename, int line) {
00319 if (!_recursion_protect) {
00320 MemoryUsage *mu = get_global_ptr();
00321 if (mu->_count_memory_usage) {
00322 int increment = 0;
00323 switch (alloc_type) {
00324 case _HOOK_ALLOC:
00325 increment = size;
00326 break;
00327
00328 case _HOOK_REALLOC:
00329 increment = size - _msize(ptr);
00330 break;
00331
00332 case _HOOK_FREE:
00333 increment = - ((int)_msize(ptr));
00334 break;
00335 }
00336
00337 mu->_total_size += increment;
00338
00339
00340
00341
00342
00343
00344
00345
00346 #ifdef TRACK_IN_INTERPRETER
00347 if (in_interpreter) {
00348 mu->_interpreter_size += increment;
00349 }
00350 #endif
00351 }
00352 }
00353
00354 return true;
00355 }
00356 #endif // WIN32_VC && _DEBUG
00357
00358
00359
00360
00361
00362
00363
00364
00365 MemoryUsage::
00366 MemoryUsage() {
00367
00368
00369
00370
00371
00372
00373
00374
00375 _track_memory_usage =
00376 config_express.GetBool("track-memory-usage", false);
00377
00378
00379
00380
00381 #if defined(WIN32_VC) && defined(_DEBUG)
00382 _count_memory_usage = config_express.GetBool("count-memory-usage", false);
00383 #else
00384 _count_memory_usage = false;
00385 #endif
00386
00387 if (_track_memory_usage) {
00388
00389
00390
00391 global_operator_new = &operator_new_handler;
00392 global_operator_delete = &operator_delete_handler;
00393 }
00394
00395 #if defined(WIN32_VC) && defined(_DEBUG)
00396 if (_count_memory_usage) {
00397 global_operator_new = &operator_new_handler;
00398 global_operator_delete = &operator_delete_handler;
00399 _CrtSetAllocHook(&win32_malloc_hook);
00400 }
00401 #endif
00402
00403 _freeze_index = 0;
00404 _count = 0;
00405 _current_cpp_size = 0;
00406 _cpp_size = 0;
00407 _interpreter_size = 0;
00408 _total_size = 0;
00409 }
00410
00411
00412
00413
00414
00415
00416
00417 MemoryUsage *MemoryUsage::
00418 get_global_ptr() {
00419 if (_global_ptr == (MemoryUsage *)NULL) {
00420 _global_ptr = new MemoryUsage;
00421 }
00422 return _global_ptr;
00423 }
00424
00425
00426
00427
00428
00429
00430
00431
00432 void MemoryUsage::
00433 ns_record_pointer(ReferenceCount *ptr) {
00434 if (_track_memory_usage) {
00435
00436
00437 _recursion_protect = true;
00438 pair<Table::iterator, bool> insert_result =
00439 _table.insert(Table::value_type((void *)ptr, MemoryInfo()));
00440 _recursion_protect = false;
00441
00442
00443 assert(insert_result.first != _table.end());
00444
00445 if (insert_result.second) {
00446 _count++;
00447 }
00448
00449 MemoryInfo &info = (*insert_result.first).second;
00450
00451
00452 if ((info._flags & MemoryInfo::F_got_ref) != 0) {
00453 express_cat.error()
00454 << "Pointer " << (void *)ptr << " recorded twice!\n";
00455 }
00456
00457 info._void_ptr = (void *)ptr;
00458 info._ref_ptr = ptr;
00459 info._static_type = ReferenceCount::get_class_type();
00460 info._dynamic_type = ReferenceCount::get_class_type();
00461 info._time = TrueClock::get_ptr()->get_long_time();
00462 info._freeze_index = _freeze_index;
00463 info._flags |= (MemoryInfo::F_reconsider_dynamic_type | MemoryInfo::F_got_ref);
00464 }
00465 }
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476 void MemoryUsage::
00477 ns_update_type(ReferenceCount *ptr, TypeHandle type) {
00478 if (_track_memory_usage) {
00479 Table::iterator ti;
00480 ti = _table.find(ptr);
00481 if (ti == _table.end()) {
00482 express_cat.error()
00483 << "Attempt to update type to " << type << " for unrecorded pointer "
00484 << (void *)ptr << "!\n";
00485 return;
00486 }
00487
00488 MemoryInfo &info = (*ti).second;
00489 info.update_type_handle(info._static_type, type);
00490 info.determine_dynamic_type();
00491
00492 consolidate_void_ptr(info);
00493 }
00494 }
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506 void MemoryUsage::
00507 ns_update_type(ReferenceCount *ptr, TypedObject *typed_ptr) {
00508 if (_track_memory_usage) {
00509 Table::iterator ti;
00510 ti = _table.find(ptr);
00511 if (ti == _table.end()) {
00512 express_cat.error()
00513 << "Attempt to update type to " << typed_ptr->get_type()
00514 << " for unrecorded pointer "
00515 << (void *)ptr << "!\n";
00516 return;
00517 }
00518
00519 MemoryInfo &info = (*ti).second;
00520 info._typed_ptr = typed_ptr;
00521 info.determine_dynamic_type();
00522
00523 consolidate_void_ptr(info);
00524 }
00525 }
00526
00527
00528
00529
00530
00531
00532
00533 void MemoryUsage::
00534 ns_remove_pointer(ReferenceCount *ptr) {
00535 if (_track_memory_usage) {
00536 Table::iterator ti;
00537 ti = _table.find(ptr);
00538 if (ti == _table.end()) {
00539 express_cat.error()
00540 << "Attempt to remove pointer " << (void *)ptr
00541 << ", not in table.\n"
00542 << "Possibly a double-destruction.\n";
00543 nassertv(false);
00544 return;
00545 }
00546
00547 MemoryInfo &info = (*ti).second;
00548
00549 if ((info._flags & MemoryInfo::F_got_ref) == 0) {
00550 express_cat.error()
00551 << "Pointer " << (void *)ptr << " deleted twice!\n";
00552 return;
00553 }
00554
00555 info._flags &= ~MemoryInfo::F_got_ref;
00556
00557
00558
00559
00560 info._typed_ptr = (TypedObject *)NULL;
00561
00562 if (info._freeze_index == _freeze_index) {
00563 double now = TrueClock::get_ptr()->get_long_time();
00564
00565
00566
00567 _recursion_protect = true;
00568 _trend_types.add_info(info.get_type(), info);
00569 _trend_ages.add_info(now - info._time, info);
00570 _recursion_protect = false;
00571 }
00572
00573 if ((info._flags & (MemoryInfo::F_got_ref | MemoryInfo::F_got_void)) == 0) {
00574
00575
00576 if (info._freeze_index == _freeze_index) {
00577 _count--;
00578 _current_cpp_size -= info._size;
00579 }
00580 _cpp_size -= info._size;
00581
00582
00583
00584 _recursion_protect = true;
00585 _table.erase(ti);
00586 _recursion_protect = false;
00587 }
00588 }
00589 }
00590
00591
00592
00593
00594
00595
00596
00597
00598 void MemoryUsage::
00599 ns_record_void_pointer(void *ptr, size_t size) {
00600 if (_track_memory_usage) {
00601
00602
00603 _recursion_protect = true;
00604 pair<Table::iterator, bool> insert_result =
00605 _table.insert(Table::value_type((void *)ptr, MemoryInfo()));
00606 _recursion_protect = false;
00607
00608 assert(insert_result.first != _table.end());
00609
00610 if (insert_result.second) {
00611 _count++;
00612 }
00613
00614 MemoryInfo &info = (*insert_result.first).second;
00615
00616
00617 if ((info._flags & MemoryInfo::F_got_void) != 0) {
00618 express_cat.error()
00619 << "Pointer " << (void *)ptr << " recorded twice!\n";
00620 }
00621
00622 if (info._freeze_index == _freeze_index) {
00623 _current_cpp_size += size - info._size;
00624 } else {
00625 _current_cpp_size += size;
00626 }
00627 _cpp_size += size - info._size;
00628
00629 info._void_ptr = ptr;
00630 info._size = size;
00631 info._time = TrueClock::get_ptr()->get_long_time();
00632 info._freeze_index = _freeze_index;
00633 info._flags |= (MemoryInfo::F_got_void | MemoryInfo::F_size_known);
00634 }
00635 }
00636
00637
00638
00639
00640
00641
00642
00643 void MemoryUsage::
00644 ns_remove_void_pointer(void *ptr) {
00645 if (_track_memory_usage) {
00646 Table::iterator ti;
00647 ti = _table.find(ptr);
00648 if (ti == _table.end()) {
00649
00650
00651
00652
00653
00654
00655
00656 return;
00657 }
00658
00659 MemoryInfo &info = (*ti).second;
00660
00661 if ((info._flags & MemoryInfo::F_got_void) == 0) {
00662 express_cat.error()
00663 << "Pointer " << (void *)ptr << " deleted twice!\n";
00664 return;
00665 }
00666
00667 if ((info._flags & MemoryInfo::F_got_ref) != 0) {
00668 express_cat.error()
00669 << "Pointer " << (void *)ptr << " did not destruct before being deleted!\n";
00670 }
00671
00672 info._flags &= ~MemoryInfo::F_got_void;
00673
00674 if ((info._flags & (MemoryInfo::F_got_ref | MemoryInfo::F_got_void)) == 0) {
00675
00676
00677
00678 if (info._freeze_index == _freeze_index) {
00679 _count--;
00680 _current_cpp_size -= info._size;
00681 }
00682 _cpp_size -= info._size;
00683
00684
00685
00686 _recursion_protect = true;
00687 _table.erase(ti);
00688 _recursion_protect = false;
00689 }
00690 }
00691 }
00692
00693
00694
00695
00696
00697
00698
00699
00700 size_t MemoryUsage::
00701 ns_get_current_cpp_size() {
00702 return _current_cpp_size;
00703 }
00704
00705
00706
00707
00708
00709
00710
00711
00712 size_t MemoryUsage::
00713 ns_get_cpp_size() {
00714 return _cpp_size;
00715 }
00716
00717
00718
00719
00720
00721
00722
00723
00724
00725
00726 size_t MemoryUsage::
00727 ns_get_interpreter_size() {
00728 return _interpreter_size;
00729 }
00730
00731
00732
00733
00734
00735
00736
00737
00738
00739 size_t MemoryUsage::
00740 ns_get_total_size() {
00741 return _total_size;
00742 }
00743
00744
00745
00746
00747
00748
00749 int MemoryUsage::
00750 ns_get_num_pointers() {
00751 nassertr(_track_memory_usage, 0);
00752 return _count;
00753 }
00754
00755
00756
00757
00758
00759
00760
00761 void MemoryUsage::
00762 ns_get_pointers(MemoryUsagePointers &result) {
00763 nassertv(_track_memory_usage);
00764 result.clear();
00765
00766 double now = TrueClock::get_ptr()->get_long_time();
00767 Table::iterator ti;
00768 for (ti = _table.begin(); ti != _table.end(); ++ti) {
00769 MemoryInfo &info = (*ti).second;
00770 if (info._freeze_index == _freeze_index &&
00771 info._ref_ptr != (ReferenceCount *)NULL) {
00772 result.add_entry(info._ref_ptr, info._typed_ptr, info.get_type(),
00773 now - info._time);
00774 }
00775 }
00776 }
00777
00778
00779
00780
00781
00782
00783
00784
00785 void MemoryUsage::
00786 ns_get_pointers_of_type(MemoryUsagePointers &result, TypeHandle type) {
00787 nassertv(_track_memory_usage);
00788 result.clear();
00789
00790 double now = TrueClock::get_ptr()->get_long_time();
00791 Table::iterator ti;
00792 for (ti = _table.begin(); ti != _table.end(); ++ti) {
00793 MemoryInfo &info = (*ti).second;
00794 if (info._freeze_index == _freeze_index &&
00795 info._ref_ptr != (ReferenceCount *)NULL) {
00796 TypeHandle info_type = info.get_type();
00797 if (info_type != TypeHandle::none() &&
00798 info_type.is_derived_from(type)) {
00799 result.add_entry(info._ref_ptr, info._typed_ptr, info_type,
00800 now - info._time);
00801 }
00802 }
00803 }
00804 }
00805
00806
00807
00808
00809
00810
00811
00812
00813 void MemoryUsage::
00814 ns_get_pointers_of_age(MemoryUsagePointers &result,
00815 double from, double to) {
00816 nassertv(_track_memory_usage);
00817 result.clear();
00818
00819 double now = TrueClock::get_ptr()->get_long_time();
00820 Table::iterator ti;
00821 for (ti = _table.begin(); ti != _table.end(); ++ti) {
00822 MemoryInfo &info = (*ti).second;
00823 if (info._freeze_index == _freeze_index &&
00824 info._ref_ptr != (ReferenceCount *)NULL) {
00825 double age = now - info._time;
00826 if ((age >= from && age <= to) ||
00827 (age >= to && age <= from)) {
00828 result.add_entry(info._ref_ptr, info._typed_ptr, info.get_type(), age);
00829 }
00830 }
00831 }
00832 }
00833
00834
00835
00836
00837
00838
00839
00840
00841
00842
00843
00844
00845
00846
00847
00848
00849
00850
00851
00852
00853
00854
00855
00856
00857 void MemoryUsage::
00858 ns_get_pointers_with_zero_count(MemoryUsagePointers &result) {
00859 nassertv(_track_memory_usage);
00860 result.clear();
00861
00862 double now = TrueClock::get_ptr()->get_long_time();
00863 Table::iterator ti;
00864 for (ti = _table.begin(); ti != _table.end(); ++ti) {
00865 MemoryInfo &info = (*ti).second;
00866 if (info._freeze_index == _freeze_index &&
00867 info._ref_ptr != (ReferenceCount *)NULL) {
00868 if (info._ref_ptr->get_ref_count() == 0) {
00869 info._ref_ptr->ref();
00870 result.add_entry(info._ref_ptr, info._typed_ptr, info.get_type(),
00871 now - info._time);
00872 }
00873 }
00874 }
00875 }
00876
00877
00878
00879
00880
00881
00882
00883
00884
00885
00886
00887 void MemoryUsage::
00888 ns_freeze() {
00889 _count = 0;
00890 _current_cpp_size = 0;
00891 _trend_types.clear();
00892 _trend_ages.clear();
00893 _freeze_index++;
00894 }
00895
00896
00897
00898
00899
00900
00901
00902 void MemoryUsage::
00903 ns_show_current_types() {
00904 nassertv(_track_memory_usage);
00905
00906
00907 _recursion_protect = true;
00908
00909 TypeHistogram hist;
00910
00911 Table::iterator ti;
00912 for (ti = _table.begin(); ti != _table.end(); ++ti) {
00913 MemoryInfo &info = (*ti).second;
00914 if (info._freeze_index == _freeze_index) {
00915 hist.add_info(info.get_type(), info);
00916 }
00917 }
00918
00919 hist.show();
00920 _recursion_protect = false;
00921 }
00922
00923
00924
00925
00926
00927
00928
00929
00930 void MemoryUsage::
00931 ns_show_trend_types() {
00932 _trend_types.show();
00933 }
00934
00935
00936
00937
00938
00939
00940
00941 void MemoryUsage::
00942 ns_show_current_ages() {
00943 nassertv(_track_memory_usage);
00944
00945
00946
00947 _recursion_protect = true;
00948
00949 AgeHistogram hist;
00950 double now = TrueClock::get_ptr()->get_long_time();
00951
00952 Table::iterator ti;
00953 for (ti = _table.begin(); ti != _table.end(); ++ti) {
00954 MemoryInfo &info = (*ti).second;
00955 if (info._freeze_index == _freeze_index) {
00956 hist.add_info(now - info._time, info);
00957 }
00958 }
00959
00960 hist.show();
00961
00962 _recursion_protect = false;
00963 }
00964
00965
00966
00967
00968
00969
00970
00971
00972 void MemoryUsage::
00973 ns_show_trend_ages() {
00974 _trend_ages.show();
00975 }
00976
00977
00978
00979
00980
00981
00982
00983
00984
00985
00986 void MemoryUsage::
00987 consolidate_void_ptr(MemoryInfo &info) {
00988 if (info.is_size_known()) {
00989
00990 return;
00991 }
00992
00993 if (info.get_typed_ptr() == (TypedObject *)NULL) {
00994
00995 return;
00996 }
00997
00998 void *typed_ptr = (void *)info.get_typed_ptr();
00999
01000 if (typed_ptr == (void *)info.get_ref_ptr()) {
01001
01002
01003
01004 return;
01005 }
01006
01007 Table::iterator ti;
01008 ti = _table.find(typed_ptr);
01009 if (ti == _table.end()) {
01010
01011 return;
01012 }
01013
01014
01015 MemoryInfo &typed_info = (*ti).second;
01016
01017 if (typed_info.is_size_known()) {
01018 info._size = typed_info.get_size();
01019 info._flags |= MemoryInfo::F_size_known;
01020 if (typed_info._freeze_index == _freeze_index) {
01021 _current_cpp_size += info._size;
01022 }
01023 }
01024
01025
01026
01027 info._void_ptr = typed_ptr;
01028
01029
01030
01031 if (info._freeze_index == _freeze_index) {
01032 _count--;
01033 _current_cpp_size -= info._size;
01034 }
01035
01036
01037
01038 _recursion_protect = true;
01039 _table.erase(ti);
01040 _recursion_protect = false;
01041 }
01042
01043
01044 #endif // DO_MEMORY_USAGE