open_toontown_panda3d/panda/src/express/memoryUsage.cxx

621 lines
21 KiB
C++

// Filename: memoryUsage.cxx
// Created by: drose (25May00)
//
////////////////////////////////////////////////////////////////////
#include "memoryUsage.h"
#include "memoryUsagePointers.h"
#include "clockObject.h"
#include "typedReferenceCount.h"
#ifndef NDEBUG
// Nothing in this module gets compiled in NDEBUG mode.
#include "config_express.h"
#include <algorithm>
MemoryUsage *MemoryUsage::_global_ptr = (MemoryUsage *)NULL;
// The cutoff ages, in seconds, for the various buckets in the AgeHistogram.
double MemoryUsage::AgeHistogram::_cutoff[MemoryUsage::AgeHistogram::num_buckets] = {
0.0,
0.1,
1.0,
10.0,
60.0,
};
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::MemoryInfo::get_type
// Access: Public
// Description: Returns the best known type, dynamic or static, of
// the pointer.
////////////////////////////////////////////////////////////////////
TypeHandle MemoryUsage::MemoryInfo::
get_type() {
// If we don't want to consider the dynamic type any further, use
// what we've got.
if (!_reconsider_dynamic_type) {
if (_dynamic_type == TypeHandle::none()) {
return _static_type;
}
return _dynamic_type;
}
// Otherwise, examine the pointer again and make sure it's still the
// best information we have. We have to do this each time because
// if we happen to be examining the pointer from within the
// constructor or destructor, its dynamic type will appear to be
// less-specific than it actually is, so our idea of what type this
// thing is could change from time to time.
determine_dynamic_type();
// Now return the more specific of the two.
TypeHandle type = _static_type;
update_type_handle(type, _dynamic_type);
return type;
}
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::MemoryInfo::determine_dynamic_type
// Access: Public
// Description: Tries to determine the actual type of the object to
// which this thing is pointed, if possible.
////////////////////////////////////////////////////////////////////
void MemoryUsage::MemoryInfo::
determine_dynamic_type() {
if (_reconsider_dynamic_type && _static_type != TypeHandle::none()) {
// See if we know enough now to infer the dynamic type from the
// pointer. We can do this only if our static type is known to
// inherit from TypedReferenceCount--see the comments about this
// sort of thing in MemoryUsagePointers::get_typed_pointer().
if (_static_type.is_derived_from(TypedReferenceCount::get_class_type())) {
TypedReferenceCount *typed_ref = (TypedReferenceCount *)_ptr;
TypeHandle got_type = typed_ref->get_type();
if (got_type == TypeHandle::none()) {
express_cat.warning()
<< "Found an unregistered type in a " << _static_type
<< " pointer:\n"
<< "Check derived types of " << _static_type
<< " and make sure that all are being initialized.\n";
_dynamic_type = _static_type;
_reconsider_dynamic_type = false;
return;
}
update_type_handle(_dynamic_type, got_type);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::MemoryInfo::update_type_handle
// Access: Public
// Description: Updates the given destination TypeHandle with the
// refined TypeHandle, if it is in fact more specific
// than the original value for the destination.
////////////////////////////////////////////////////////////////////
void MemoryUsage::MemoryInfo::
update_type_handle(TypeHandle &destination, TypeHandle refined) {
if (refined == TypeHandle::none()) {
express_cat.error()
<< "Attempt to update type of " << (void *)_ptr
<< "(type is " << get_type()
<< ") to an undefined type!\n";
} else if (destination == refined) {
// Updating with the same type, no problem.
} else if (destination.is_derived_from(refined)) {
// Updating with a less-specific type, no problem.
} else if (refined.is_derived_from(destination)) {
// Updating with a more-specific type, no problem.
destination = refined;
} else {
express_cat.error()
<< "Pointer " << (void *)_ptr << " previously indicated as type "
<< destination << " is now type " << refined << "!\n";
destination = refined;
}
}
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::TypeHistogram::add_info
// Access: Public
// Description: Adds a single entry to the histogram.
////////////////////////////////////////////////////////////////////
void MemoryUsage::TypeHistogram::
add_info(TypeHandle type) {
_counts[type]++;
}
// This class is a temporary class used only in TypeHistogram::show(),
// below, to sort the types in descending order by counts.
class TypeHistogramCountSorter {
public:
TypeHistogramCountSorter(int count, TypeHandle type) {
_count = count;
_type = type;
}
bool operator < (const TypeHistogramCountSorter &other) const {
return _count > other._count;
}
int _count;
TypeHandle _type;
};
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::TypeHistogram::show
// Access: Public
// Description: Shows the contents of the histogram to nout.
////////////////////////////////////////////////////////////////////
void MemoryUsage::TypeHistogram::
show() const {
// First, copy the relevant information to a vector so we can sort
// by counts.
vector<TypeHistogramCountSorter> count_sorter;
Counts::const_iterator ci;
for (ci = _counts.begin(); ci != _counts.end(); ++ci) {
count_sorter.push_back
(TypeHistogramCountSorter((*ci).second, (*ci).first));
}
sort(count_sorter.begin(), count_sorter.end());
vector<TypeHistogramCountSorter>::const_iterator vi;
for (vi = count_sorter.begin(); vi != count_sorter.end(); ++vi) {
nout << (*vi)._type << " : " << (*vi)._count << " pointers.\n";
}
}
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::TypeHistogram::clear
// Access: Public
// Description: Resets the histogram in preparation for new data.
////////////////////////////////////////////////////////////////////
void MemoryUsage::TypeHistogram::
clear() {
_counts.clear();
}
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::AgeHistogram::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
MemoryUsage::AgeHistogram::
AgeHistogram() {
clear();
}
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::AgeHistogram::add_info
// Access: Public
// Description: Adds a single entry to the histogram.
////////////////////////////////////////////////////////////////////
void MemoryUsage::AgeHistogram::
add_info(double age) {
int bucket = choose_bucket(age);
nassertv(bucket >= 0 && bucket < num_buckets);
_counts[bucket]++;
}
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::AgeHistogram::show
// Access: Public
// Description: Shows the contents of the histogram to nout.
////////////////////////////////////////////////////////////////////
void MemoryUsage::AgeHistogram::
show() const {
for (int i = 0; i < num_buckets - 1; i++) {
nout << _cutoff[i] << " to " << _cutoff[i + 1] << " seconds old : "
<< _counts[i] << " pointers.\n";
}
nout << _cutoff[num_buckets - 1] << " seconds old and up : "
<< _counts[num_buckets - 1] << " pointers.\n";
}
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::AgeHistogram::clear
// Access: Public
// Description: Resets the histogram in preparation for new data.
////////////////////////////////////////////////////////////////////
void MemoryUsage::AgeHistogram::
clear() {
for (int i = 0; i < num_buckets; i++) {
_counts[i] = 0;
}
}
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::AgeHistogram::choose_bucket
// Access: Private
// Description:
////////////////////////////////////////////////////////////////////
int MemoryUsage::AgeHistogram::
choose_bucket(double age) const {
for (int i = num_buckets - 1; i >= 0; i--) {
if (age >= _cutoff[i]) {
return i;
}
}
nassertr(false, 0);
return 0;
}
#if defined(__GNUC__) && !defined(NDEBUG)
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::record_pointer
// Access: Public, Static
// Description: Indicates that the given pointer has been recently
// allocated.
////////////////////////////////////////////////////////////////////
void MemoryUsage::
record_pointer(ReferenceCount *ptr) {
get_global_ptr()->ns_record_pointer(ptr);
}
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::update_type
// Access: Public, Static
// Description: Associates the indicated type with the given pointer.
////////////////////////////////////////////////////////////////////
void MemoryUsage::
update_type(ReferenceCount *ptr, TypeHandle type) {
get_global_ptr()->ns_update_type(ptr, type);
}
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::remove_pointer
// Access: Public, Static
// Description: Indicates that the given pointer has been recently
// freed.
////////////////////////////////////////////////////////////////////
void MemoryUsage::
remove_pointer(ReferenceCount *ptr) {
get_global_ptr()->ns_remove_pointer(ptr);
}
#endif // __GNUC__ && !NDEBUG
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::Constructor
// Access: Private
// Description:
////////////////////////////////////////////////////////////////////
MemoryUsage::
MemoryUsage() {
// We must get this here instead of in config_express.cxx, because we
// need to know it at static init time, and who knows when the code
// in config_express will be executed.
_track_memory_usage =
config_express.GetBool("track-memory-usage", false);
_freeze_index = 0;
_count = 0;
}
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::get_global_ptr
// Access: Private, Static
// Description: Returns the pointer to the only MemoryUsage object in
// the world.
////////////////////////////////////////////////////////////////////
MemoryUsage *MemoryUsage::
get_global_ptr() {
if (_global_ptr == (MemoryUsage *)NULL) {
_global_ptr = new MemoryUsage;
}
return _global_ptr;
}
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::ns_record_pointer
// Access: Private
// Description: Indicates that the given pointer has been recently
// allocated.
////////////////////////////////////////////////////////////////////
void MemoryUsage::
ns_record_pointer(ReferenceCount *ptr) {
if (_track_memory_usage) {
MemoryInfo info;
info._ptr = ptr;
info._static_type = ReferenceCount::get_class_type();
info._dynamic_type = ReferenceCount::get_class_type();
info._time = ClockObject::get_global_clock()->get_real_time();
info._freeze_index = _freeze_index;
info._reconsider_dynamic_type = true;
Table::iterator ti;
ti = _table.find(ptr);
if (ti != _table.end()) {
express_cat.error() << "Pointer " << (void *)ptr << " recorded twice!\n";
(*ti).second = info;
} else {
_table[ptr] = info;
_count++;
}
}
}
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::ns_update_type
// Access: Private
// Description: Associates the indicated type with the given pointer.
////////////////////////////////////////////////////////////////////
void MemoryUsage::
ns_update_type(ReferenceCount *ptr, TypeHandle type) {
if (_track_memory_usage) {
Table::iterator ti;
ti = _table.find(ptr);
if (ti == _table.end()) {
express_cat.error()
<< "Attempt to update type to " << type << " for unrecorded pointer "
<< (void *)ptr << "!\n";
return;
}
MemoryInfo &info = (*ti).second;
info.update_type_handle(info._static_type, type);
info.determine_dynamic_type();
}
}
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::ns_remove_pointer
// Access: Private
// Description: Indicates that the given pointer has been recently
// freed.
////////////////////////////////////////////////////////////////////
void MemoryUsage::
ns_remove_pointer(ReferenceCount *ptr) {
if (_track_memory_usage) {
Table::iterator ti;
ti = _table.find(ptr);
if (ti == _table.end()) {
express_cat.error()
<< "Attempt to remove pointer " << (void *)ptr
<< ", not in table.\n"
<< "Possibly a double-destruction.\n";
nassertv(false);
return;
}
MemoryInfo &info = (*ti).second;
if (info._freeze_index == _freeze_index) {
double now = ClockObject::get_global_clock()->get_real_time();
_count--;
_trend_types.add_info(info.get_type());
_trend_ages.add_info(now - info._time);
}
_table.erase(ti);
}
}
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::ns_get_num_pointers
// Access: Private
// Description: Returns the number of pointers currently active.
////////////////////////////////////////////////////////////////////
int MemoryUsage::
ns_get_num_pointers() {
nassertr(_track_memory_usage, 0);
return _count;
}
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::ns_get_pointers
// Access: Private
// Description: Fills the indicated MemoryUsagePointers with the set
// of all pointers currently active.
////////////////////////////////////////////////////////////////////
void MemoryUsage::
ns_get_pointers(MemoryUsagePointers &result) {
nassertv(_track_memory_usage);
result.clear();
double now = ClockObject::get_global_clock()->get_real_time();
Table::iterator ti;
for (ti = _table.begin(); ti != _table.end(); ++ti) {
MemoryInfo &info = (*ti).second;
if (info._freeze_index == _freeze_index) {
result.add_entry((*ti).first, info.get_type(), now - info._time);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::ns_get_pointers_of_type
// Access: Private
// Description: Fills the indicated MemoryUsagePointers with the set
// of all pointers of the indicated type currently
// active.
////////////////////////////////////////////////////////////////////
void MemoryUsage::
ns_get_pointers_of_type(MemoryUsagePointers &result, TypeHandle type) {
nassertv(_track_memory_usage);
result.clear();
double now = ClockObject::get_global_clock()->get_real_time();
Table::iterator ti;
for (ti = _table.begin(); ti != _table.end(); ++ti) {
MemoryInfo &info = (*ti).second;
if (info._freeze_index == _freeze_index) {
TypeHandle info_type = info.get_type();
if (info_type != TypeHandle::none() &&
info_type.is_derived_from(type)) {
result.add_entry((*ti).first, info_type, now - info._time);
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::ns_get_pointers_of_age
// Access: Private
// Description: Fills the indicated MemoryUsagePointers with the set
// of all pointers that were allocated within the range
// of the indicated number of seconds ago.
////////////////////////////////////////////////////////////////////
void MemoryUsage::
ns_get_pointers_of_age(MemoryUsagePointers &result,
double from, double to) {
nassertv(_track_memory_usage);
result.clear();
double now = ClockObject::get_global_clock()->get_real_time();
Table::iterator ti;
for (ti = _table.begin(); ti != _table.end(); ++ti) {
MemoryInfo &info = (*ti).second;
if (info._freeze_index == _freeze_index) {
double age = now - info._time;
if ((age >= from && age <= to) ||
(age >= to && age <= from)) {
result.add_entry((*ti).first, info.get_type(), age);
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::ns_get_pointers_with_zero_count
// Access: Private
// Description: Fills the indicated MemoryUsagePointers with the set
// of all currently active pointers (that is, pointers
// allocated since the last call to freeze(), and not
// yet freed) that have a zero reference count.
//
// Generally, an undeleted pointer with a zero reference
// count means its reference count has never been
// incremented beyond zero (since once it has been
// incremented, the only way it can return to zero would
// free the pointer). This may include objects that are
// allocated statically or on the stack, which are never
// intended to be deleted. Or, it might represent a
// programmer or compiler error.
//
// This function has the side-effect of incrementing
// each of their reference counts by one, thus
// preventing them from ever being freed--but since they
// hadn't been freed anyway, probably no additional harm
// is done.
////////////////////////////////////////////////////////////////////
void MemoryUsage::
ns_get_pointers_with_zero_count(MemoryUsagePointers &result) {
nassertv(_track_memory_usage);
result.clear();
double now = ClockObject::get_global_clock()->get_real_time();
Table::iterator ti;
for (ti = _table.begin(); ti != _table.end(); ++ti) {
MemoryInfo &info = (*ti).second;
if (info._freeze_index == _freeze_index) {
if ((*ti).first->get_count() == 0) {
(*ti).first->ref();
result.add_entry((*ti).first, info.get_type(), now - info._time);
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::ns_freeze
// Access: Private
// Description: 'Freezes' all pointers currently stored so that they
// are no longer reported; only newly allocate pointers
// from this point on will appear in future information
// requests. This makes it easier to differentiate
// between continuous leaks and one-time memory
// allocations.
////////////////////////////////////////////////////////////////////
void MemoryUsage::
ns_freeze() {
_count = 0;
_trend_types.clear();
_trend_ages.clear();
_freeze_index++;
}
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::ns_show_current_types
// Access: Private
// Description: Shows the breakdown of types of all of the
// active pointers.
////////////////////////////////////////////////////////////////////
void MemoryUsage::
ns_show_current_types() {
TypeHistogram hist;
Table::iterator ti;
for (ti = _table.begin(); ti != _table.end(); ++ti) {
MemoryInfo &info = (*ti).second;
if (info._freeze_index == _freeze_index) {
hist.add_info(info.get_type());
}
}
hist.show();
}
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::ns_show_trend_types
// Access: Private
// Description: Shows the breakdown of types of all of the
// pointers allocated and freed since the last call to
// freeze().
////////////////////////////////////////////////////////////////////
void MemoryUsage::
ns_show_trend_types() {
_trend_types.show();
}
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::ns_show_current_ages
// Access: Private
// Description: Shows the breakdown of ages of all of the
// active pointers.
////////////////////////////////////////////////////////////////////
void MemoryUsage::
ns_show_current_ages() {
AgeHistogram hist;
double now = ClockObject::get_global_clock()->get_real_time();
Table::iterator ti;
for (ti = _table.begin(); ti != _table.end(); ++ti) {
MemoryInfo &info = (*ti).second;
if (info._freeze_index == _freeze_index) {
hist.add_info(now - info._time);
}
}
hist.show();
}
////////////////////////////////////////////////////////////////////
// Function: MemoryUsage::ns_show_trend_ages
// Access: Private
// Description: Shows the breakdown of ages of all of the
// pointers allocated and freed since the last call to
// freeze().
////////////////////////////////////////////////////////////////////
void MemoryUsage::
ns_show_trend_ages() {
_trend_ages.show();
}
#endif // NDEBUG