open_toontown_panda3d/panda/src/pstatclient/pStatClient.cxx

1178 lines
44 KiB
C++

// Filename: pStatClient.cxx
// Created by: drose (09Jul00)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "pStatClient.h"
#ifdef DO_PSTATS
// This file only defines anything interesting if DO_PSTATS is
// defined.
#include "pStatClientImpl.h"
#include "pStatClientControlMessage.h"
#include "pStatServerControlMessage.h"
#include "pStatCollector.h"
#include "pStatThread.h"
#include "config_pstats.h"
#include "pStatProperties.h"
#include "thread.h"
#include "clockObject.h"
#include "neverFreeMemory.h"
PStatCollector PStatClient::_heap_total_size_pcollector("System memory:Heap");
PStatCollector PStatClient::_heap_overhead_size_pcollector("System memory:Heap:Overhead");
PStatCollector PStatClient::_heap_single_size_pcollector("System memory:Heap:Single");
PStatCollector PStatClient::_heap_single_other_size_pcollector("System memory:Heap:Single:Other");
PStatCollector PStatClient::_heap_array_size_pcollector("System memory:Heap:Array");
PStatCollector PStatClient::_heap_array_other_size_pcollector("System memory:Heap:Array:Other");
PStatCollector PStatClient::_heap_external_size_pcollector("System memory:Heap:External");
PStatCollector PStatClient::_mmap_size_pcollector("System memory:MMap");
PStatCollector PStatClient::_mmap_nf_unused_size_pcollector("System memory:MMap:NeverFree:Unused");
PStatCollector PStatClient::_mmap_dc_active_other_size_pcollector("System memory:MMap:NeverFree:Active:Other");
PStatCollector PStatClient::_mmap_dc_inactive_other_size_pcollector("System memory:MMap:NeverFree:Inactive:Other");
PStatCollector PStatClient::_pstats_pcollector("*:PStats");
PStatCollector PStatClient::_clock_wait_pcollector("Wait:Clock Wait:Sleep");
PStatCollector PStatClient::_clock_busy_wait_pcollector("Wait:Clock Wait:Spin");
PStatCollector PStatClient::_thread_block_pcollector("Wait:Thread block");
PStatClient *PStatClient::_global_pstats = NULL;
// This class is used to report memory usage per TypeHandle. We
// create one of these for each TypeHandle in the system.
class TypeHandleCollector {
public:
PStatCollector _mem_class[TypeHandle::MC_limit];
};
typedef pvector<TypeHandleCollector> TypeHandleCols;
static TypeHandleCols type_handle_cols;
////////////////////////////////////////////////////////////////////
// Function: PStatClient::PerThreadData::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
PStatClient::PerThreadData::
PerThreadData() {
_has_level = false;
_level = 0.0;
_nested_count = 0;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
PStatClient::
PStatClient() :
_lock("PStatClient::_lock"),
_impl(NULL)
{
_collectors = NULL;
_collectors_size = 0;
_num_collectors = 0;
_threads = NULL;
_threads_size = 0;
_num_threads = 0;
// We always have a collector at index 0 named "Frame". This tracks
// the total frame time and is the root of all other collectors. We
// have to make this one by hand since it's the root.
Collector *collector = new Collector(0, "Frame");
//collector->_def = new PStatCollectorDef(0, "Frame");
//collector->_def->_parent_index = 0;
//collector->_def->_suggested_color.set(0.5, 0.5, 0.5);
add_collector(collector);
// The main thread is always at index 0.
make_thread(Thread::get_main_thread());
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
PStatClient::
~PStatClient() {
disconnect();
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_collector
// Access: Published
// Description: Returns the nth collector.
////////////////////////////////////////////////////////////////////
PStatCollector PStatClient::
get_collector(int index) const {
nassertr(index >= 0 && index < AtomicAdjust::get(_num_collectors), PStatCollector());
return PStatCollector((PStatClient *)this, index);
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_collector_name
// Access: Published
// Description: Returns the name of the indicated collector.
////////////////////////////////////////////////////////////////////
string PStatClient::
get_collector_name(int index) const {
nassertr(index >= 0 && index < AtomicAdjust::get(_num_collectors), string());
return get_collector_ptr(index)->get_name();
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_collector_fullname
// Access: Published
// Description: Returns the "full name" of the indicated collector.
// This will be the concatenation of all of the
// collector's parents' names (except Frame) and the
// collector's own name.
////////////////////////////////////////////////////////////////////
string PStatClient::
get_collector_fullname(int index) const {
nassertr(index >= 0 && index < AtomicAdjust::get(_num_collectors), string());
Collector *collector = get_collector_ptr(index);
int parent_index = collector->get_parent_index();
if (parent_index == 0) {
return collector->get_name();
} else {
return get_collector_fullname(parent_index) + ":" +
collector->get_name();
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_thread
// Access: Published
// Description: Returns the nth thread.
////////////////////////////////////////////////////////////////////
PStatThread PStatClient::
get_thread(int index) const {
ReMutexHolder holder(_lock);
nassertr(index >= 0 && index < _num_threads, PStatThread());
return PStatThread((PStatClient *)this, index);
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_main_thread
// Access: Published
// Description: Returns a handle to the client's Main thread. This
// is the thread that started the application.
////////////////////////////////////////////////////////////////////
PStatThread PStatClient::
get_main_thread() const {
return PStatThread((PStatClient *)this, 0);
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_current_thread
// Access: Published
// Description: Returns a handle to the currently-executing thread.
// This is the thread that PStatCollectors will be
// counted in if they do not specify otherwise.
////////////////////////////////////////////////////////////////////
PStatThread PStatClient::
get_current_thread() const {
if (!client_is_connected()) {
// No need to make the relatively expensive call to
// Thread::get_current_thread() if we're not even connected.
return get_main_thread();
}
return PStatThread(Thread::get_current_thread(), (PStatClient *)this);
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::main_tick
// Access: Published, Static
// Description: A convenience function to call new_frame() on the
// global PStatClient's main thread, and any other
// threads with a sync_name of "Main".
////////////////////////////////////////////////////////////////////
void PStatClient::
main_tick() {
// We have code here to report the memory usage. We can't put this
// code inside the MemoryUsage class, where it fits a little better,
// simply because MemoryUsage is a very low-level class that doesn't
// know about PStatClient.
#ifdef DO_MEMORY_USAGE
if (is_connected()) {
_heap_total_size_pcollector.set_level(MemoryUsage::get_total_size());
_heap_overhead_size_pcollector.set_level(MemoryUsage::get_panda_heap_overhead());
_heap_single_size_pcollector.set_level(MemoryUsage::get_panda_heap_single_size());
_heap_array_size_pcollector.set_level(MemoryUsage::get_panda_heap_array_size());
_heap_external_size_pcollector.set_level(MemoryUsage::get_external_size());
_mmap_size_pcollector.set_level(MemoryUsage::get_panda_mmap_size());
TypeRegistry *type_reg = TypeRegistry::ptr();
int num_typehandles = type_reg->get_num_typehandles();
while ((int)type_handle_cols.size() < num_typehandles) {
type_handle_cols.push_back(TypeHandleCollector());
}
size_t single_total_usage = 0;
size_t array_total_usage = 0;
size_t dc_active_total_usage = 0;
size_t dc_inactive_total_usage = 0;
int i;
for (i = 0; i < num_typehandles; ++i) {
TypeHandle type = type_reg->get_typehandle(i);
for (int mi = 0; mi < (int)TypeHandle::MC_limit; ++mi) {
TypeHandle::MemoryClass mc = (TypeHandle::MemoryClass)mi;
size_t usage = type.get_memory_usage(mc);
switch (mc) {
case TypeHandle::MC_singleton:
single_total_usage += usage;
break;
case TypeHandle::MC_array:
array_total_usage += usage;
break;
case TypeHandle::MC_deleted_chain_active:
dc_active_total_usage += usage;
break;
case TypeHandle::MC_deleted_chain_inactive:
dc_inactive_total_usage += usage;
break;
case TypeHandle::MC_limit:
// Not used.
break;
}
}
}
size_t min_usage = (single_total_usage + array_total_usage + dc_active_total_usage + dc_inactive_total_usage) / 1024;
if (!pstats_mem_other) {
min_usage = 0;
}
size_t single_other_usage = single_total_usage;
size_t array_other_usage = array_total_usage;
size_t dc_active_other_usage = dc_active_total_usage;
size_t dc_inactive_other_usage = dc_inactive_total_usage;
for (i = 0; i < num_typehandles; ++i) {
TypeHandle type = type_reg->get_typehandle(i);
for (int mi = 0; mi < (int)TypeHandle::MC_limit; ++mi) {
TypeHandle::MemoryClass mc = (TypeHandle::MemoryClass)mi;
PStatCollector &col = type_handle_cols[i]._mem_class[mi];
size_t usage = type.get_memory_usage(mc);
if (usage > min_usage || col.is_valid()) {
// We have some memory usage on this TypeHandle. See if we
// have a collector for it.
if (!col.is_valid()) {
const char *category = "";
switch (mc) {
case TypeHandle::MC_singleton:
category = "Heap:Single";
break;
case TypeHandle::MC_array:
category = "Heap:Array";
break;
case TypeHandle::MC_deleted_chain_active:
category = "MMap:NeverFree:Active";
break;
case TypeHandle::MC_deleted_chain_inactive:
category = "MMap:NeverFree:Inactive";
break;
case TypeHandle::MC_limit:
// Not used.
break;
}
ostringstream strm;
strm << "System memory:" << category << ":" << type;
col = PStatCollector(strm.str());
}
col.set_level(usage);
switch (mc) {
case TypeHandle::MC_singleton:
single_other_usage -= usage;
break;
case TypeHandle::MC_array:
array_other_usage -= usage;
break;
case TypeHandle::MC_deleted_chain_active:
dc_active_other_usage -= usage;
break;
case TypeHandle::MC_deleted_chain_inactive:
dc_inactive_other_usage -= usage;
break;
case TypeHandle::MC_limit:
// Not used.
break;
}
}
}
}
_mmap_nf_unused_size_pcollector.set_level(NeverFreeMemory::get_total_unused());
// The remaining amount--all collectors smaller than 0.1% of the
// total--go into "other".
_heap_single_other_size_pcollector.set_level(single_other_usage);
_heap_array_other_size_pcollector.set_level(array_other_usage);
_mmap_dc_active_other_size_pcollector.set_level(dc_active_other_usage);
_mmap_dc_inactive_other_size_pcollector.set_level(dc_inactive_other_usage);
}
#endif // DO_MEMORY_USAGE
get_global_pstats()->client_main_tick();
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::thread_tick
// Access: Published, Static
// Description: A convenience function to call new_frame() on any
// threads with the indicated sync_name
////////////////////////////////////////////////////////////////////
void PStatClient::
thread_tick(const string &sync_name) {
get_global_pstats()->client_thread_tick(sync_name);
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::client_main_tick
// Access: Published
// Description: A convenience function to call new_frame() on the
// given PStatClient's main thread, and any other
// threads with a sync_name of "Main".
////////////////////////////////////////////////////////////////////
void PStatClient::
client_main_tick() {
ReMutexHolder holder(_lock);
if (has_impl()) {
if (!_impl->client_is_connected()) {
client_disconnect();
return;
}
_impl->client_main_tick();
MultiThingsByName::const_iterator ni =
_threads_by_sync_name.find("Main");
if (ni != _threads_by_sync_name.end()) {
const vector_int &indices = (*ni).second;
for (vector_int::const_iterator vi = indices.begin();
vi != indices.end();
++vi) {
_impl->new_frame(*vi);
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::client_thread_tick
// Access: Published
// Description: A convenience function to call new_frame() on all of
// the threads with the indicated sync name.
////////////////////////////////////////////////////////////////////
void PStatClient::
client_thread_tick(const string &sync_name) {
ReMutexHolder holder(_lock);
if (has_impl()) {
MultiThingsByName::const_iterator ni =
_threads_by_sync_name.find(sync_name);
if (ni != _threads_by_sync_name.end()) {
const vector_int &indices = (*ni).second;
for (vector_int::const_iterator vi = indices.begin();
vi != indices.end();
++vi) {
_impl->new_frame(*vi);
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::client_disconnect
// Access: Published
// Description: The nonstatic implementation of disconnect().
////////////////////////////////////////////////////////////////////
void PStatClient::
client_disconnect() {
ReMutexHolder holder(_lock);
if (has_impl()) {
_impl->client_disconnect();
delete _impl;
_impl = NULL;
}
ThreadPointer *threads = (ThreadPointer *)_threads;
for (int ti = 0; ti < _num_threads; ++ti) {
InternalThread *thread = threads[ti];
thread->_frame_number = 0;
thread->_is_active = false;
thread->_next_packet = 0.0;
thread->_frame_data.clear();
}
CollectorPointer *collectors = (CollectorPointer *)_collectors;
for (int ci = 0; ci < _num_collectors; ++ci) {
Collector *collector = collectors[ci];
PerThread::iterator ii;
for (ii = collector->_per_thread.begin();
ii != collector->_per_thread.end();
++ii) {
(*ii)._nested_count = 0;
}
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_global_pstats
// Access: Published, Static
// Description: Returns a pointer to the global PStatClient object.
// It's legal to declare your own PStatClient locally,
// but it's also convenient to have a global one that
// everyone can register with. This is the global one.
////////////////////////////////////////////////////////////////////
PStatClient *PStatClient::
get_global_pstats() {
if (_global_pstats == (PStatClient *)NULL) {
_global_pstats = new PStatClient;
ClockObject::_start_clock_wait = start_clock_wait;
ClockObject::_start_clock_busy_wait = start_clock_busy_wait;
ClockObject::_stop_clock_wait = stop_clock_wait;
}
return _global_pstats;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::make_collector_with_relname
// Access: Private
// Description: Returns a PStatCollector suitable for measuring
// categories with the indicated name. This is normally
// called by a PStatCollector constructor.
//
// The name may contain colons; if it does, it specifies
// a relative path to the client indicated by the parent
// index.
////////////////////////////////////////////////////////////////////
PStatCollector PStatClient::
make_collector_with_relname(int parent_index, string relname) {
ReMutexHolder holder(_lock);
if (relname.empty()) {
relname = "Unnamed";
}
// Skip any colons at the beginning of the name.
size_t start = 0;
while (start < relname.size() && relname[start] == ':') {
start++;
}
// If the name contains a colon (after the initial colon), it means
// we are making a nested collector.
size_t colon = relname.find(':', start);
while (colon != string::npos) {
string parent_name = relname.substr(start, colon - start);
PStatCollector parent_collector =
make_collector_with_name(parent_index, parent_name);
parent_index = parent_collector._index;
relname = relname.substr(colon + 1);
start = 0;
colon = relname.find(':');
}
string name = relname.substr(start);
return make_collector_with_name(parent_index, name);
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::make_collector_with_name
// Access: Private
// Description: Returns a PStatCollector suitable for measuring
// categories with the indicated name. This is normally
// called by a PStatCollector constructor.
//
// The name should not contain colons.
////////////////////////////////////////////////////////////////////
PStatCollector PStatClient::
make_collector_with_name(int parent_index, const string &name) {
ReMutexHolder holder(_lock);
nassertr(parent_index >= 0 && parent_index < _num_collectors,
PStatCollector());
Collector *parent = get_collector_ptr(parent_index);
// A special case: if we asked for a child the same name as its
// parent, we really meant the parent. That is, "Frame:Frame" is
// really the same collector as "Frame".
if (parent->get_name() == name) {
return PStatCollector(this, parent_index);
}
ThingsByName::const_iterator ni = parent->_children.find(name);
if (ni != parent->_children.end()) {
// We already had a collector by this name; return it.
int index = (*ni).second;
nassertr(index >= 0 && index < _num_collectors, PStatCollector());
return PStatCollector(this, (*ni).second);
}
// Create a new collector for this name.
int new_index = _num_collectors;
parent->_children.insert(ThingsByName::value_type(name, new_index));
Collector *collector = new Collector(parent_index, name);
// collector->_def = new PStatCollectorDef(new_index, name);
// collector->_def->set_parent(*_collectors[parent_index]._def);
// initialize_collector_def(this, collector->_def);
// We need one PerThreadData for each thread.
while ((int)collector->_per_thread.size() < _num_threads) {
collector->_per_thread.push_back(PerThreadData());
}
add_collector(collector);
return PStatCollector(this, new_index);
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::do_get_current_thread
// Access: Private
// Description: Similar to get_current_thread, but does not grab the
// lock.
////////////////////////////////////////////////////////////////////
PStatThread PStatClient::
do_get_current_thread() const {
Thread *thread = Thread::get_current_thread();
int thread_index = thread->get_pstats_index();
if (thread_index != -1) {
return PStatThread((PStatClient *)this, thread_index);
}
// This is the first time we have encountered this current Thread.
// Make a new PStatThread object for it.
return ((PStatClient *)this)->do_make_thread(thread);
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::make_thread
// Access: Private
// Description: Returns a PStatThread for the indicated Panda Thread
// object. This is normally called by a PStatThread
// constructor.
////////////////////////////////////////////////////////////////////
PStatThread PStatClient::
make_thread(Thread *thread) {
ReMutexHolder holder(_lock);
return do_make_thread(thread);
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::do_make_thread
// Access: Private
// Description: As above, but assumes the lock is already held.
////////////////////////////////////////////////////////////////////
PStatThread PStatClient::
do_make_thread(Thread *thread) {
int thread_index = thread->get_pstats_index();
if (thread_index != -1) {
return PStatThread((PStatClient *)this, thread_index);
}
MultiThingsByName::const_iterator ni =
_threads_by_name.find(thread->get_name());
if (ni != _threads_by_name.end()) {
// We have seen a thread with this name before. Can we re-use any
// of them?
const vector_int &indices = (*ni).second;
for (vector_int::const_iterator vi = indices.begin();
vi != indices.end();
++vi) {
int index = (*vi);
nassertr(index >= 0 && index < _num_threads, PStatThread());
ThreadPointer *threads = (ThreadPointer *)_threads;
if (threads[index]->_thread.was_deleted() &&
threads[index]->_sync_name == thread->get_sync_name()) {
// Yes, re-use this one.
threads[index]->_thread = thread;
thread->set_pstats_index(index);
thread->set_pstats_callback(this);
return PStatThread(this, index);
}
}
}
// Create a new PStatsThread for this thread pointer.
int new_index = _num_threads;
thread->set_pstats_index(new_index);
thread->set_pstats_callback(this);
_threads_by_name[thread->get_name()].push_back(new_index);
_threads_by_sync_name[thread->get_sync_name()].push_back(new_index);
InternalThread *pthread = new InternalThread(thread);
add_thread(pthread);
// We need an additional PerThreadData for this thread in all of the
// collectors.
CollectorPointer *collectors = (CollectorPointer *)_collectors;
for (int ci = 0; ci < _num_collectors; ++ci) {
Collector *collector = collectors[ci];
collector->_per_thread.push_back(PerThreadData());
nassertr((int)collector->_per_thread.size() == _num_threads, PStatThread());
}
return PStatThread(this, new_index);
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::is_active
// Access: Private
// Description: Returns true if the indicated collector/thread
// combination is active, and we are transmitting stats
// data, or false otherwise.
//
// Normally you would not use this interface directly;
// instead, call PStatCollector::is_active().
////////////////////////////////////////////////////////////////////
bool PStatClient::
is_active(int collector_index, int thread_index) const {
nassertr(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors), false);
nassertr(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads), false);
return (client_is_connected() &&
get_collector_ptr(collector_index)->is_active() &&
get_thread_ptr(thread_index)->_is_active);
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::is_started
// Access: Private
// Description: Returns true if the indicated collector/thread
// combination has been started, or false otherwise.
//
// Normally you would not use this interface directly;
// instead, call PStatCollector::is_started().
////////////////////////////////////////////////////////////////////
bool PStatClient::
is_started(int collector_index, int thread_index) const {
nassertr(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors), false);
nassertr(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads), false);
Collector *collector = get_collector_ptr(collector_index);
InternalThread *thread = get_thread_ptr(thread_index);
if (client_is_connected() && collector->is_active() && thread->_is_active) {
LightMutexHolder holder(thread->_thread_lock);
if (collector->_per_thread[thread_index]._nested_count == 0) {
// Not started.
return false;
}
// Started.
return true;
}
// Not even connected.
return false;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::start
// Access: Private
// Description: Marks the indicated collector index as started.
// Normally you would not use this interface directly;
// instead, call PStatCollector::start().
////////////////////////////////////////////////////////////////////
void PStatClient::
start(int collector_index, int thread_index) {
#ifdef _DEBUG
nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors));
nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads));
#endif
Collector *collector = get_collector_ptr(collector_index);
InternalThread *thread = get_thread_ptr(thread_index);
if (client_is_connected() && collector->is_active() && thread->_is_active) {
LightMutexHolder holder(thread->_thread_lock);
if (collector->_per_thread[thread_index]._nested_count == 0) {
// This collector wasn't already started in this thread; record
// a new data point.
if (thread->_thread_active) {
thread->_frame_data.add_start(collector_index, get_real_time());
}
}
collector->_per_thread[thread_index]._nested_count++;
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::start
// Access: Private
// Description: Marks the indicated collector index as started.
// Normally you would not use this interface directly;
// instead, call PStatCollector::start().
////////////////////////////////////////////////////////////////////
void PStatClient::
start(int collector_index, int thread_index, double as_of) {
#ifdef _DEBUG
nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors));
nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads));
#endif
Collector *collector = get_collector_ptr(collector_index);
InternalThread *thread = get_thread_ptr(thread_index);
if (client_is_connected() && collector->is_active() && thread->_is_active) {
LightMutexHolder holder(thread->_thread_lock);
if (collector->_per_thread[thread_index]._nested_count == 0) {
// This collector wasn't already started in this thread; record
// a new data point.
if (thread->_thread_active) {
thread->_frame_data.add_start(collector_index, as_of);
}
}
collector->_per_thread[thread_index]._nested_count++;
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::stop
// Access: Private
// Description: Marks the indicated collector index as stopped.
// Normally you would not use this interface directly;
// instead, call PStatCollector::stop().
////////////////////////////////////////////////////////////////////
void PStatClient::
stop(int collector_index, int thread_index) {
#ifdef _DEBUG
nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors));
nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads));
#endif
Collector *collector = get_collector_ptr(collector_index);
InternalThread *thread = get_thread_ptr(thread_index);
if (client_is_connected() && collector->is_active() && thread->_is_active) {
LightMutexHolder holder(thread->_thread_lock);
if (collector->_per_thread[thread_index]._nested_count == 0) {
if (pstats_cat.is_debug()) {
pstats_cat.debug()
<< "Collector " << get_collector_fullname(collector_index)
<< " was already stopped in thread " << get_thread_name(thread_index)
<< "!\n";
}
return;
}
collector->_per_thread[thread_index]._nested_count--;
if (collector->_per_thread[thread_index]._nested_count == 0) {
// This collector has now been completely stopped; record a new
// data point.
if (thread->_thread_active) {
thread->_frame_data.add_stop(collector_index, get_real_time());
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::stop
// Access: Private
// Description: Marks the indicated collector index as stopped.
// Normally you would not use this interface directly;
// instead, call PStatCollector::stop().
////////////////////////////////////////////////////////////////////
void PStatClient::
stop(int collector_index, int thread_index, double as_of) {
#ifdef _DEBUG
nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors));
nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads));
#endif
Collector *collector = get_collector_ptr(collector_index);
InternalThread *thread = get_thread_ptr(thread_index);
if (client_is_connected() && collector->is_active() && thread->_is_active) {
LightMutexHolder holder(thread->_thread_lock);
if (collector->_per_thread[thread_index]._nested_count == 0) {
if (pstats_cat.is_debug()) {
pstats_cat.debug()
<< "Collector " << get_collector_fullname(collector_index)
<< " was already stopped in thread " << get_thread_name(thread_index)
<< "!\n";
}
return;
}
collector->_per_thread[thread_index]._nested_count--;
if (collector->_per_thread[thread_index]._nested_count == 0) {
// This collector has now been completely stopped; record a new
// data point.
thread->_frame_data.add_stop(collector_index, as_of);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::clear_level
// Access: Private
// Description: Removes the level value from the indicated collector.
// The collector will no longer be reported as having
// any particular level value.
//
// Normally you would not use this interface directly;
// instead, call PStatCollector::clear_level().
////////////////////////////////////////////////////////////////////
void PStatClient::
clear_level(int collector_index, int thread_index) {
#ifdef _DEBUG
nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors));
nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads));
#endif
Collector *collector = get_collector_ptr(collector_index);
InternalThread *thread = get_thread_ptr(thread_index);
LightMutexHolder holder(thread->_thread_lock);
collector->_per_thread[thread_index]._has_level = true;
collector->_per_thread[thread_index]._level = 0.0;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::set_level
// Access: Private
// Description: Sets the level value for the indicated collector to
// the given amount.
//
// Normally you would not use this interface directly;
// instead, call PStatCollector::set_level().
////////////////////////////////////////////////////////////////////
void PStatClient::
set_level(int collector_index, int thread_index, double level) {
#ifdef _DEBUG
nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors));
nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads));
#endif
Collector *collector = get_collector_ptr(collector_index);
InternalThread *thread = get_thread_ptr(thread_index);
// We don't want to condition this on whether the client is already
// connected or the collector is already active, since we might
// connect the client later, and we will want to have an accurate
// value at that time.
LightMutexHolder holder(thread->_thread_lock);
level *= collector->get_def(this, collector_index)->_factor;
collector->_per_thread[thread_index]._has_level = true;
collector->_per_thread[thread_index]._level = level;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::add_level
// Access: Private
// Description: Adds the given value (which may be negative) to the
// current value for the given collector. If the
// collector does not already have a level value, it is
// initialized to 0.
//
// Normally you would not use this interface directly;
// instead, call PStatCollector::add_level().
////////////////////////////////////////////////////////////////////
void PStatClient::
add_level(int collector_index, int thread_index, double increment) {
#ifdef _DEBUG
nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors));
nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads));
#endif
Collector *collector = get_collector_ptr(collector_index);
InternalThread *thread = get_thread_ptr(thread_index);
LightMutexHolder holder(thread->_thread_lock);
increment *= collector->get_def(this, collector_index)->_factor;
collector->_per_thread[thread_index]._has_level = true;
collector->_per_thread[thread_index]._level += increment;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_level
// Access: Private
// Description: Returns the current level value of the given collector.
//
// Normally you would not use this interface directly;
// instead, call PStatCollector::get_level().
////////////////////////////////////////////////////////////////////
double PStatClient::
get_level(int collector_index, int thread_index) const {
#ifdef _DEBUG
nassertr(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors), 0.0f);
nassertr(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads), 0.0f);
#endif
Collector *collector = get_collector_ptr(collector_index);
InternalThread *thread = get_thread_ptr(thread_index);
LightMutexHolder holder(thread->_thread_lock);
double factor = collector->get_def(this, collector_index)->_factor;
return collector->_per_thread[thread_index]._level / factor;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::start_clock_wait
// Access: Private, Static
// Description: This function is added as a hook into ClockObject, so
// that we may time the delay for
// ClockObject::wait_until(), used for certain special
// clock modes.
//
// This callback is a hack around the fact that we can't
// let the ClockObject directly create a PStatCollector,
// because the pstatclient module depends on putil.
////////////////////////////////////////////////////////////////////
void PStatClient::
start_clock_wait() {
_clock_wait_pcollector.start();
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::start_clock_busy_wait
// Access: Private, Static
// Description: This function is added as a hook into ClockObject, so
// that we may time the delay for
// ClockObject::wait_until(), used for certain special
// clock modes.
//
// This callback is a hack around the fact that we can't
// let the ClockObject directly create a PStatCollector,
// because the pstatclient module depends on putil.
////////////////////////////////////////////////////////////////////
void PStatClient::
start_clock_busy_wait() {
_clock_wait_pcollector.stop();
_clock_busy_wait_pcollector.start();
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::stop_clock_wait
// Access: Private, Static
// Description: This function is added as a hook into ClockObject, so
// that we may time the delay for
// ClockObject::wait_until(), used for certain special
// clock modes.
//
// This callback is a hack around the fact that we can't
// let the ClockObject directly create a PStatCollector,
// because the pstatclient module depends on putil.
////////////////////////////////////////////////////////////////////
void PStatClient::
stop_clock_wait() {
_clock_busy_wait_pcollector.stop();
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::add_collector
// Access: Private
// Description: Adds a new Collector entry to the _collectors array,
// in a thread-safe manner. Assumes _lock is already
// held.
////////////////////////////////////////////////////////////////////
void PStatClient::
add_collector(PStatClient::Collector *collector) {
if (_num_collectors >= _collectors_size) {
// We need to grow the array. We have to be careful here, because
// there might be clients accessing the array right now who are
// not protected by the lock.
int new_collectors_size = (_collectors_size == 0) ? 128 : _collectors_size * 2;
CollectorPointer *new_collectors = new CollectorPointer[new_collectors_size];
memcpy(new_collectors, _collectors, _num_collectors * sizeof(CollectorPointer));
AtomicAdjust::set_ptr(_collectors, new_collectors);
AtomicAdjust::set(_collectors_size, new_collectors_size);
// Now, we still have the old array, which we allow to leak. We
// should delete it, but there might be a thread out there that's
// still trying to access it, so we can't safely delete it; and it
// doesn't really matter much, since it's not a big leak. (We
// will only reallocate the array so many times in an application,
// and then no more.)
new_collectors[_num_collectors] = collector;
AtomicAdjust::inc(_num_collectors);
} else {
CollectorPointer *collectors = (CollectorPointer *)_collectors;
collectors[_num_collectors] = collector;
AtomicAdjust::inc(_num_collectors);
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::add_thread
// Access: Private
// Description: Adds a new InternalThread entry to the _threads
// array, in a thread-safe manner. Assumes _lock is
// already held.
////////////////////////////////////////////////////////////////////
void PStatClient::
add_thread(PStatClient::InternalThread *thread) {
if (_num_threads >= _threads_size) {
// We need to grow the array. We have to be careful here, because
// there might be clients accessing the array right now who are
// not protected by the lock.
int new_threads_size = (_threads_size == 0) ? 128 : _threads_size * 2;
ThreadPointer *new_threads = new ThreadPointer[new_threads_size];
memcpy(new_threads, _threads, _num_threads * sizeof(ThreadPointer));
// We assume that assignment to a pointer and to an int are each
// atomic.
AtomicAdjust::set_ptr(_threads, new_threads);
AtomicAdjust::set(_threads_size, new_threads_size);
// Now, we still have the old array, which we allow to leak. We
// should delete it, but there might be a thread out there that's
// still trying to access it, so we can't safely delete it; and it
// doesn't really matter much, since it's not a big leak. (We
// will only reallocate the array so many times in an application,
// and then no more.)
new_threads[_num_threads] = thread;
AtomicAdjust::inc(_num_threads);
} else {
ThreadPointer *threads = (ThreadPointer *)_threads;
threads[_num_threads] = thread;
AtomicAdjust::inc(_num_threads);
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::deactivate_hook
// Access: Public, Virtual
// Description: Called when the thread is deactivated (swapped for
// another running thread). This is intended to provide
// a callback hook for PStats to assign time to
// individual threads properly, particularly in the
// SIMPLE_THREADS case.
////////////////////////////////////////////////////////////////////
void PStatClient::
deactivate_hook(Thread *thread) {
// We shouldn't use a mutex here, because this code is only called
// during the SIMPLE_THREADS case, so a mutex isn't necessary; and
// because we are called during a context switch, so a mutex might
// be dangerous.
if (_impl == NULL) {
return;
}
int thread_index = thread->get_pstats_index();
InternalThread *ithread = get_thread_ptr(thread_index);
if (ithread->_thread_active) {
// Start _thread_block_pcollector, by hand, being careful not to
// grab any mutexes while we do it.
double now = _impl->get_real_time();
ithread->_frame_data.add_start(_thread_block_pcollector.get_index(), now);
ithread->_thread_active = false;
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::activate_hook
// Access: Public, Virtual
// Description: Called when the thread is activated (resumes
// execution). This is intended to provide a callback
// hook for PStats to assign time to individual threads
// properly, particularly in the SIMPLE_THREADS case.
////////////////////////////////////////////////////////////////////
void PStatClient::
activate_hook(Thread *thread) {
// We shouldn't use a mutex here, because this code is only called
// during the SIMPLE_THREADS case, so a mutex isn't necessary; and
// because we are called during a context switch, so a mutex might
// be dangerous.
if (_impl == NULL) {
return;
}
InternalThread *ithread = get_thread_ptr(thread->get_pstats_index());
if (!ithread->_thread_active) {
double now = _impl->get_real_time();
ithread->_frame_data.add_stop(_thread_block_pcollector.get_index(), now);
ithread->_thread_active = true;
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::Collector::make_def
// Access: Private
// Description: Creates the new PStatCollectorDef for this collector.
////////////////////////////////////////////////////////////////////
void PStatClient::Collector::
make_def(const PStatClient *client, int this_index) {
ReMutexHolder holder(client->_lock);
if (_def == (PStatCollectorDef *)NULL) {
_def = new PStatCollectorDef(this_index, _name);
if (_parent_index != this_index) {
const PStatCollectorDef *parent_def =
client->get_collector_def(_parent_index);
_def->set_parent(*parent_def);
}
initialize_collector_def(client, _def);
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::Collector::make_def
// Access: Private
// Description: Creates the new PStatCollectorDef for this collector.
////////////////////////////////////////////////////////////////////
PStatClient::InternalThread::
InternalThread(Thread *thread) :
_thread(thread),
_name(thread->get_name()),
_sync_name(thread->get_sync_name()),
_is_active(false),
_frame_number(0),
_next_packet(0.0),
_thread_active(true),
_thread_lock(string("PStatClient::InternalThread ") + thread->get_name())
{
}
#endif // DO_PSTATS