split out PStatClientImpl

This commit is contained in:
David Rose 2004-12-23 19:10:35 +00:00
parent 7d5ae1e3f6
commit db8cdc119d
9 changed files with 981 additions and 644 deletions

View File

@ -10,7 +10,9 @@
#define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx
#define SOURCES \
config_pstats.h pStatClient.I pStatClient.h pStatClientVersion.I \
config_pstats.h pStatClient.I pStatClient.h \
pStatClientImpl.I pStatClientImpl.h \
pStatClientVersion.I \
pStatClientVersion.h pStatClientControlMessage.h \
pStatCollector.I pStatCollector.h pStatCollectorDef.h \
pStatFrameData.I pStatFrameData.h pStatProperties.h \
@ -18,13 +20,15 @@
pStatTimer.I pStatTimer.h
#define INCLUDED_SOURCES \
config_pstats.cxx pStatClient.cxx pStatClientVersion.cxx \
config_pstats.cxx pStatClient.cxx pStatClientImpl.cxx \
pStatClientVersion.cxx \
pStatClientControlMessage.cxx pStatCollectorDef.cxx \
pStatFrameData.cxx pStatProperties.cxx \
pStatServerControlMessage.cxx
#define INSTALL_HEADERS \
config_pstats.h pStatClient.I pStatClient.h \
pStatClientImpl.I pStatClientImpl.h \
pStatClientVersion.I pStatClientVersion.h \
pStatClientControlMessage.h pStatCollector.I pStatCollector.h \
pStatCollectorDef.h pStatFrameData.I pStatFrameData.h \

View File

@ -19,29 +19,29 @@
////////////////////////////////////////////////////////////////////
// Function: PStatClient::set_client_name
// Access: Public
// Access: Published
// Description: Sets the name of the client. This is reported to the
// PStatsServer, and will presumably be written in the
// title bar or something.
////////////////////////////////////////////////////////////////////
INLINE void PStatClient::
set_client_name(const string &name) {
_client_name = name;
get_impl()->set_client_name(name);
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_client_name
// Access: Public
// Access: Published
// Description: Retrieves the name of the client as set.
////////////////////////////////////////////////////////////////////
INLINE string PStatClient::
get_client_name() const {
return _client_name;
return get_impl()->get_client_name();
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::set_max_rate
// Access: Public
// Access: Published
// Description: Controls the number of packets that will be sent to
// the server. Normally, one packet is sent per frame,
// but this can flood the server with more packets than
@ -56,24 +56,71 @@ get_client_name() const {
////////////////////////////////////////////////////////////////////
INLINE void PStatClient::
set_max_rate(float rate) {
_max_rate = rate;
get_impl()->set_max_rate(rate);
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_max_rate
// Access: Public
// Access: Published
// Description: Returns the maximum number of packets that will be
// sent to the server per second, per thread. See
// set_max_rate().
////////////////////////////////////////////////////////////////////
INLINE float PStatClient::
get_max_rate() const {
return _max_rate;
return get_impl()->get_max_rate();
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_num_collectors
// Access: Published
// Description: Returns the total number of collectors the Client
// knows about.
////////////////////////////////////////////////////////////////////
INLINE int PStatClient::
get_num_collectors() const {
return _collectors.size();
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_num_threads
// Access: Published
// Description: Returns the total number of threads the Client
// knows about.
////////////////////////////////////////////////////////////////////
INLINE int PStatClient::
get_num_threads() const {
return _threads.size();
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_thread_name
// Access: Published
// Description: Returns the name of the indicated thread.
////////////////////////////////////////////////////////////////////
INLINE string PStatClient::
get_thread_name(int index) const {
nassertr(index >= 0 && index < (int)_threads.size(), string());
return _threads[index]._name;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_clock
// Access: Published
// Description: Returns a reference to the PStatClient's clock
// object. It keeps its own clock, instead of using the
// global clock object, so the stats won't get mucked up
// if you put the global clock in non-real-time mode or
// something.
////////////////////////////////////////////////////////////////////
INLINE const ClockObject &PStatClient::
get_clock() const {
return get_impl()->get_clock();
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::connect
// Access: Published
// Access: Published, Static
// Description: Attempts to establish a connection to the indicated
// PStatServer. Returns true if successful, false on
// failure.
@ -85,7 +132,7 @@ connect(const string &hostname, int port) {
////////////////////////////////////////////////////////////////////
// Function: PStatClient::disconnect
// Access: Published
// Access: Published, Static
// Description: Closes the connection previously established.
////////////////////////////////////////////////////////////////////
INLINE void PStatClient::
@ -95,7 +142,7 @@ disconnect() {
////////////////////////////////////////////////////////////////////
// Function: PStatClient::is_connected
// Access: Published
// Access: Published, Static
// Description: Returns true if the client believes it is connected
// to a working PStatServer, false otherwise.
////////////////////////////////////////////////////////////////////
@ -106,7 +153,7 @@ is_connected() {
////////////////////////////////////////////////////////////////////
// Function: PStatClient::resume_after_pause
// Access: Published
// Access: Published, Static
// Description: Resumes the PStatClient after the simulation has been
// paused for a while. This allows the stats to
// continue exactly where it left off, instead of
@ -116,3 +163,78 @@ INLINE void PStatClient::
resume_after_pause() {
get_global_pstats()->client_resume_after_pause();
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::client_connect
// Access: Published
// Description: The nonstatic implementation of connect().
////////////////////////////////////////////////////////////////////
INLINE bool PStatClient::
client_connect(string hostname, int port) {
client_disconnect();
return get_impl()->client_connect(hostname, port);
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::client_is_connected
// Access: Published
// Description: The nonstatic implementation of is_connected().
////////////////////////////////////////////////////////////////////
INLINE bool PStatClient::
client_is_connected() const {
return has_impl() && _impl->client_is_connected();
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::client_resume_after_pause
// Access: Published
// Description: Resumes the PStatClient after the simulation has been
// paused for a while. This allows the stats to
// continue exactly where it left off, instead of
// leaving a big gap that would represent a chug.
////////////////////////////////////////////////////////////////////
INLINE void PStatClient::
client_resume_after_pause() {
if (has_impl()) {
_impl->client_resume_after_pause();
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::has_impl
// Access: Private
// Description: Returns true if the PStatClientImpl object has been
// created for this object yet, false otherwise.
////////////////////////////////////////////////////////////////////
INLINE bool PStatClient::
has_impl() const {
return (_impl != (PStatClientImpl *)NULL);
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_impl
// Access: Private
// Description: Returns the PStatClientImpl object for this object.
// If the PStatClientImpl object has not yet been
// created, implicitly creates it.
////////////////////////////////////////////////////////////////////
INLINE PStatClientImpl *PStatClient::
get_impl() {
if (_impl == (PStatClientImpl *)NULL) {
_impl = new PStatClientImpl(this);
}
return _impl;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_impl
// Access: Private
// Description: Returns the PStatClientImpl object for this object.
// If the PStatClientImpl object has not yet been
// created, implicitly creates it.
////////////////////////////////////////////////////////////////////
INLINE const PStatClientImpl *PStatClient::
get_impl() const {
return ((PStatClient *)this)->get_impl();
}

View File

@ -22,29 +22,21 @@
// 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 "cmath.h"
#include "mathNumbers.h"
#include <algorithm>
#ifdef WIN32_VC
#define WINDOWS_LEAN_AND_MEAN
#include <windows.h>
#undef WINDOWS_LEAN_AND_MEAN
#endif
PStatCollector PStatClient::_total_size_pcollector("Memory usage");
PStatCollector PStatClient::_cpp_size_pcollector("Memory usage:C++");
PStatCollector PStatClient::_interpreter_size_pcollector("Memory usage:Interpreter");
PStatCollector PStatClient::_pstats_pcollector("App:PStats");
PStatClient *PStatClient::_global_pstats = NULL;
PStatCollector _total_size_pcollector("Memory usage");
PStatCollector _cpp_size_pcollector("Memory usage:C++");
PStatCollector _interpreter_size_pcollector("Memory usage:Interpreter");
PStatCollector _pstats_pcollector("App:PStats");
////////////////////////////////////////////////////////////////////
// Function: PStatClient::PerThreadData::Constructor
@ -65,17 +57,8 @@ PerThreadData() {
////////////////////////////////////////////////////////////////////
PStatClient::
PStatClient() :
_reader(this, 0),
_writer(this, get_pstats_threaded_write() ? 1 : 0)
_impl(NULL)
{
_is_connected = false;
_got_udp_port = false;
_collectors_reported = 0;
_threads_reported = 0;
// Make sure our clock is in "normal" mode.
_clock.set_mode(ClockObject::M_normal);
// 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.
@ -87,28 +70,6 @@ PStatClient() :
// We also always have a thread at index 0 named "Main".
make_thread("Main");
_client_name = get_pstats_name();
_max_rate = get_pstats_max_rate();
_tcp_count = 1;
_udp_count = 1;
double pstats_tcp_ratio = get_pstats_tcp_ratio();
if (pstats_tcp_ratio >= 1.0f) {
_tcp_count_factor = 0.0f;
_udp_count_factor = 1.0f;
} else if (pstats_tcp_ratio <= 0.0f) {
_tcp_count_factor = 1.0f;
_udp_count_factor = 0.0f;
} else {
csincos(pstats_tcp_ratio * MathNumbers::pi_f / 2.0f,
&_udp_count_factor,
&_tcp_count_factor);
}
}
////////////////////////////////////////////////////////////////////
@ -119,22 +80,15 @@ PStatClient() :
PStatClient::
~PStatClient() {
disconnect();
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_num_collectors
// Access: Public
// Description: Returns the total number of collectors the Client
// knows about.
////////////////////////////////////////////////////////////////////
int PStatClient::
get_num_collectors() const {
return _collectors.size();
if (has_impl()) {
delete _impl;
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_collector
// Access: Public
// Access: Published
// Description: Returns the nth collector.
////////////////////////////////////////////////////////////////////
PStatCollector PStatClient::
@ -145,7 +99,7 @@ get_collector(int index) const {
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_collector_def
// Access: Public
// Access: Published
// Description: Returns the definition body of the nth collector.
////////////////////////////////////////////////////////////////////
const PStatCollectorDef &PStatClient::
@ -160,7 +114,7 @@ get_collector_def(int index) const {
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_collector_name
// Access: Public
// Access: Published
// Description: Returns the name of the indicated collector.
////////////////////////////////////////////////////////////////////
string PStatClient::
@ -173,7 +127,7 @@ get_collector_name(int index) const {
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_collector_fullname
// Access: Public
// 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
@ -191,20 +145,9 @@ get_collector_fullname(int index) const {
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_num_threads
// Access: Public
// Description: Returns the total number of threads the Client
// knows about.
////////////////////////////////////////////////////////////////////
int PStatClient::
get_num_threads() const {
return _threads.size();
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_thread
// Access: Public
// Access: Published
// Description: Returns the nth thread.
////////////////////////////////////////////////////////////////////
PStatThread PStatClient::
@ -213,38 +156,9 @@ get_thread(int index) const {
return PStatThread((PStatClient *)this, index);
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_thread_name
// Access: Public
// Description: Returns the name of the indicated thread.
////////////////////////////////////////////////////////////////////
string PStatClient::
get_thread_name(int index) const {
nassertr(index >= 0 && index < (int)_threads.size(), string());
return _threads[index]._name;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_clock
// Access: Public
// Description: Returns a reference to the PStatClient's clock
// object. It keeps its own clock, instead of using the
// global clock object, so the stats won't get mucked up
// if you put the global clock in non-real-time mode or
// something.
//
// On second thought, it works better to use the global
// clock, so we don't lose a lot of time in the stats
// while we're waiting at the prompt.
////////////////////////////////////////////////////////////////////
const ClockObject &PStatClient::
get_clock() const {
return _clock;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_main_thread
// Access: Public
// Access: Published
// Description: Returns a handle to the client's "Main", or default,
// thread. This is where collectors will be started and
// stopped if they don't specify otherwise.
@ -254,6 +168,94 @@ get_main_thread() const {
return PStatThread((PStatClient *)this, 0);
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::main_tick
// Access: Published, Static
// Description: A convenience function to call new_frame() on the
// global PStatClient's main thread.
////////////////////////////////////////////////////////////////////
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 (MemoryUsage::has_total_size()) {
_total_size_pcollector.set_level(MemoryUsage::get_total_size());
}
if (MemoryUsage::has_cpp_size()) {
_cpp_size_pcollector.set_level(MemoryUsage::get_cpp_size());
}
if (MemoryUsage::has_interpreter_size()) {
_interpreter_size_pcollector.set_level(MemoryUsage::get_interpreter_size());
}
#endif
get_global_pstats()->client_main_tick();
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::client_main_tick
// Access: Published
// Description: A convenience function to call new_frame() on the
// the given client's main thread.
////////////////////////////////////////////////////////////////////
void PStatClient::
client_main_tick() {
if (has_impl()) {
_impl->client_main_tick();
}
get_main_thread().new_frame();
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::client_disconnect
// Access: Published
// Description: The nonstatic implementation of disconnect().
////////////////////////////////////////////////////////////////////
void PStatClient::
client_disconnect() {
if (has_impl()) {
_impl->client_disconnect();
}
Threads::iterator ti;
for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
(*ti)._frame_number = 0;
(*ti)._is_active = false;
(*ti)._next_packet = 0.0;
(*ti)._frame_data.clear();
}
Collectors::iterator ci;
for (ci = _collectors.begin(); ci != _collectors.end(); ++ci) {
PerThread::iterator ii;
for (ii = (*ci)._per_thread.begin();
ii != (*ci)._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;
}
return _global_pstats;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::make_collector_with_relname
// Access: Private
@ -388,175 +390,6 @@ make_thread(const string &name) {
return PStatThread(this, new_index);
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::main_tick
// Access: Public, Static
// Description: A convenience function to call new_frame() on the
// global PStatClient's main thread.
////////////////////////////////////////////////////////////////////
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 (MemoryUsage::has_total_size()) {
_total_size_pcollector.set_level(MemoryUsage::get_total_size());
}
if (MemoryUsage::has_cpp_size()) {
_cpp_size_pcollector.set_level(MemoryUsage::get_cpp_size());
}
if (MemoryUsage::has_interpreter_size()) {
_interpreter_size_pcollector.set_level(MemoryUsage::get_interpreter_size());
}
#endif
get_global_pstats()->client_main_tick();
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::main_tick
// Access: Public, Static
// Description: A convenience function to call new_frame() on the
// the given client's main thread.
////////////////////////////////////////////////////////////////////
void PStatClient::
client_main_tick() {
_clock.tick();
get_main_thread().new_frame();
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_global_pstats
// Access: Public, 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;
}
return _global_pstats;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::client_connect
// Access: Private
// Description: The nonstatic implementation of connect().
////////////////////////////////////////////////////////////////////
bool PStatClient::
client_connect(string hostname, int port) {
client_disconnect();
if (hostname.empty()) {
hostname = pstats_host;
}
if (port < 0) {
port = pstats_port;
}
if (!_server.set_host(hostname, port)) {
pstats_cat.error()
<< "Unknown host: " << hostname << "\n";
return false;
}
_tcp_connection = open_TCP_client_connection(_server, 5000);
if (_tcp_connection.is_null()) {
pstats_cat.error()
<< "Couldn't connect to PStatServer at " << hostname << ":"
<< port << "\n";
return false;
}
// Make sure we're not queuing up multiple TCP sockets--we expect
// immediate writes of our TCP datagrams.
_tcp_connection->set_collect_tcp(false);
_reader.add_connection(_tcp_connection);
_is_connected = true;
_udp_connection = open_UDP_connection();
send_hello();
return _is_connected;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::client_disconnect
// Access: Private
// Description: The nonstatic implementation of disconnect().
////////////////////////////////////////////////////////////////////
void PStatClient::
client_disconnect() {
if (_is_connected) {
_reader.remove_connection(_tcp_connection);
close_connection(_tcp_connection);
close_connection(_udp_connection);
}
_tcp_connection.clear();
_udp_connection.clear();
_is_connected = false;
_got_udp_port = false;
_collectors_reported = 0;
_threads_reported = 0;
Threads::iterator ti;
for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
(*ti)._frame_number = 0;
(*ti)._is_active = false;
(*ti)._next_packet = 0.0;
(*ti)._frame_data.clear();
}
Collectors::iterator ci;
for (ci = _collectors.begin(); ci != _collectors.end(); ++ci) {
PerThread::iterator ii;
for (ii = (*ci)._per_thread.begin();
ii != (*ci)._per_thread.end();
++ii) {
(*ii)._nested_count = 0;
}
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::client_is_connected
// Access: Public
// Description: The nonstatic implementation of is_connected().
////////////////////////////////////////////////////////////////////
bool PStatClient::
client_is_connected() const {
return _is_connected;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::client_resume_after_pause
// Access: Published
// Description: Resumes the PStatClient after the simulation has been
// paused for a while. This allows the stats to
// continue exactly where it left off, instead of
// leaving a big gap that would represent a chug.
////////////////////////////////////////////////////////////////////
void PStatClient::
client_resume_after_pause() {
// Simply reset the clock to the beginning of the last frame. This
// may lose a frame, but on the other hand we won't skip a whole
// slew of frames either.
double frame_time = _clock.get_frame_time();
_clock.set_real_time(frame_time);
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::is_active
// Access: Private
@ -572,7 +405,7 @@ is_active(int collector_index, int thread_index) const {
nassertr(collector_index >= 0 && collector_index < (int)_collectors.size(), false);
nassertr(thread_index >= 0 && thread_index < (int)_threads.size(), false);
return (_is_connected &&
return (client_is_connected() &&
_collectors[collector_index]._def->_is_active &&
_threads[thread_index]._is_active);
}
@ -610,13 +443,14 @@ start(int collector_index, int thread_index) {
nassertv(thread_index >= 0 && thread_index < (int)_threads.size());
#endif
if (_collectors[collector_index]._def->_is_active &&
if (client_is_connected() &&
_collectors[collector_index]._def->_is_active &&
_threads[thread_index]._is_active) {
if (_collectors[collector_index]._per_thread[thread_index]._nested_count == 0) {
// This collector wasn't already started in this thread; record
// a new data point.
_threads[thread_index]._frame_data.add_start(collector_index,
_clock.get_real_time());
get_clock().get_real_time());
}
_collectors[collector_index]._per_thread[thread_index]._nested_count++;
}
@ -636,7 +470,8 @@ start(int collector_index, int thread_index, float as_of) {
nassertv(thread_index >= 0 && thread_index < (int)_threads.size());
#endif
if (_collectors[collector_index]._def->_is_active &&
if (client_is_connected() &&
_collectors[collector_index]._def->_is_active &&
_threads[thread_index]._is_active) {
if (_collectors[collector_index]._per_thread[thread_index]._nested_count == 0) {
// This collector wasn't already started in this thread; record
@ -661,7 +496,8 @@ stop(int collector_index, int thread_index) {
nassertv(thread_index >= 0 && thread_index < (int)_threads.size());
#endif
if (_collectors[collector_index]._def->_is_active &&
if (client_is_connected() &&
_collectors[collector_index]._def->_is_active &&
_threads[thread_index]._is_active) {
if (_collectors[collector_index]._per_thread[thread_index]._nested_count == 0) {
pstats_cat.warning()
@ -677,7 +513,7 @@ stop(int collector_index, int thread_index) {
// This collector has now been completely stopped; record a new
// data point.
_threads[thread_index]._frame_data.add_stop(collector_index,
_clock.get_real_time());
get_clock().get_real_time());
}
}
}
@ -696,7 +532,8 @@ stop(int collector_index, int thread_index, float as_of) {
nassertv(thread_index >= 0 && thread_index < (int)_threads.size());
#endif
if (_collectors[collector_index]._def->_is_active &&
if (client_is_connected() &&
_collectors[collector_index]._def->_is_active &&
_threads[thread_index]._is_active) {
if (_collectors[collector_index]._per_thread[thread_index]._nested_count == 0) {
pstats_cat.warning()
@ -745,7 +582,7 @@ clear_level(int collector_index, int thread_index) {
////////////////////////////////////////////////////////////////////
void PStatClient::
set_level(int collector_index, int thread_index, float level) {
if (_collectors[collector_index]._def->_is_active) {
if (client_is_connected() && _collectors[collector_index]._def->_is_active) {
level *= _collectors[collector_index]._def->_factor;
_collectors[collector_index]._per_thread[thread_index]._has_level = true;
_collectors[collector_index]._per_thread[thread_index]._level = level;
@ -765,7 +602,7 @@ set_level(int collector_index, int thread_index, float level) {
////////////////////////////////////////////////////////////////////
void PStatClient::
add_level(int collector_index, int thread_index, float increment) {
if (_collectors[collector_index]._def->_is_active) {
if (client_is_connected() && _collectors[collector_index]._def->_is_active) {
increment *= _collectors[collector_index]._def->_factor;
_collectors[collector_index]._per_thread[thread_index]._has_level = true;
_collectors[collector_index]._per_thread[thread_index]._level += increment;
@ -786,291 +623,4 @@ get_level(int collector_index, int thread_index) const {
_collectors[collector_index]._def->_factor;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::new_frame
// Access: Private
// Description: Called by the PStatThread interface at the beginning
// of every frame, for each thread. This resets the
// clocks for the new frame and transmits the data for
// the previous frame.
////////////////////////////////////////////////////////////////////
void PStatClient::
new_frame(int thread_index) {
nassertv(thread_index >= 0 && thread_index < (int)_threads.size());
Thread &thread = _threads[thread_index];
// If we're the main thread, we should exchange control packets with
// the server.
if (thread_index == 0) {
transmit_control_data();
}
// If we've got the UDP port by the time the frame starts, it's
// time to become active and start actually tracking data.
if (_got_udp_port) {
thread._is_active = true;
}
if (!thread._is_active) {
return;
}
float frame_start = _clock.get_real_time();
if (!thread._frame_data.is_empty()) {
// Collector 0 is the whole frame.
stop(0, thread_index, frame_start);
// Fill up the level data for all the collectors who have level
// data for this thread.
int num_collectors = _collectors.size();
for (int i = 0; i < num_collectors; i++) {
const PerThreadData &ptd = _collectors[i]._per_thread[thread_index];
if (ptd._has_level) {
thread._frame_data.add_level(i, ptd._level);
}
}
transmit_frame_data(thread_index);
}
thread._frame_data.clear();
thread._frame_number++;
start(0, thread_index, frame_start);
// Also record the time for the PStats operation itself.
start(_pstats_pcollector.get_index(), thread_index, frame_start);
stop(_pstats_pcollector.get_index(), thread_index, _clock.get_real_time());
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::transmit_frame_data
// Access: Private
// Description: Should be called once per frame per thread to
// transmit the latest data to the PStatServer.
////////////////////////////////////////////////////////////////////
void PStatClient::
transmit_frame_data(int thread_index) {
nassertv(thread_index >= 0 && thread_index < (int)_threads.size());
if (_is_connected && _threads[thread_index]._is_active) {
// We don't want to send too many packets in a hurry and flood the
// server. Check that enough time has elapsed for us to send a
// new packet. If not, we'll drop this packet on the floor and
// send a new one next time around.
float now = _clock.get_real_time();
if (now >= _threads[thread_index]._next_packet) {
// We don't want to send more than _max_rate UDP-size packets
// per second, per thread.
float packet_delay = 1.0 / _max_rate;
// Send new data.
NetDatagram datagram;
// We always start with a zero byte, to differentiate it from a
// control message.
datagram.add_uint8(0);
datagram.add_uint16(thread_index);
datagram.add_uint32(_threads[thread_index]._frame_number);
_threads[thread_index]._frame_data.write_datagram(datagram);
if (_writer.is_valid_for_udp(datagram)) {
if (_udp_count * _udp_count_factor < _tcp_count * _tcp_count_factor) {
// Send this one as a UDP packet.
nassertv(_got_udp_port);
_writer.send(datagram, _udp_connection, _server);
_udp_count++;
if (_udp_count == 0) {
// Wraparound!
_udp_count = 1;
_tcp_count = 1;
}
} else {
// Send this one as a TCP packet.
_writer.send(datagram, _tcp_connection);
_tcp_count++;
if (_tcp_count == 0) {
// Wraparound!
_udp_count = 1;
_tcp_count = 1;
}
}
} else {
_writer.send(datagram, _tcp_connection);
// If our packets are so large that we must ship them via TCP,
// then artificially slow down the packet rate even further.
int packet_ratio =
(datagram.get_length() + maximum_udp_datagram - 1) /
maximum_udp_datagram;
packet_delay *= (float)packet_ratio;
}
_threads[thread_index]._next_packet = now + packet_delay;
}
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::transmit_control_data
// Access: Private
// Description: Should be called once a frame to exchange control
// information with the server.
////////////////////////////////////////////////////////////////////
void PStatClient::
transmit_control_data() {
// Check for new messages from the server.
while (_is_connected && _reader.data_available()) {
NetDatagram datagram;
if (_reader.get_data(datagram)) {
PStatServerControlMessage message;
if (message.decode(datagram)) {
handle_server_control_message(message);
} else {
pstats_cat.error()
<< "Got unexpected message from server.\n";
}
}
}
if (_is_connected) {
report_new_collectors();
report_new_threads();
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::get_hostname
// Access: Private
// Description: Returns the current machine's hostname.
////////////////////////////////////////////////////////////////////
string PStatClient::
get_hostname() {
if (_hostname.empty()) {
char temp_buff[1024];
if (gethostname(temp_buff, 1024) == 0) {
_hostname = temp_buff;
} else {
_hostname = "unknown";
}
}
return _hostname;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::send_hello
// Access: Private
// Description: Sends the initial greeting message to the server.
////////////////////////////////////////////////////////////////////
void PStatClient::
send_hello() {
nassertv(_is_connected);
PStatClientControlMessage message;
message._type = PStatClientControlMessage::T_hello;
message._client_hostname = get_hostname();
message._client_progname = _client_name;
message._major_version = get_current_pstat_major_version();
message._minor_version = get_current_pstat_minor_version();
Datagram datagram;
message.encode(datagram);
_writer.send(datagram, _tcp_connection);
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::report_new_collectors
// Access: Private
// Description: Sends over any information about new Collectors that
// the user code might have recently created.
////////////////////////////////////////////////////////////////////
void PStatClient::
report_new_collectors() {
nassertv(_is_connected);
if (_collectors_reported < (int)_collectors.size()) {
PStatClientControlMessage message;
message._type = PStatClientControlMessage::T_define_collectors;
while (_collectors_reported < (int)_collectors.size()) {
message._collectors.push_back(_collectors[_collectors_reported]._def);
_collectors_reported++;
}
Datagram datagram;
message.encode(datagram);
_writer.send(datagram, _tcp_connection);
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::report_new_threads
// Access: Private
// Description: Sends over any information about new Threads that
// the user code might have recently created.
////////////////////////////////////////////////////////////////////
void PStatClient::
report_new_threads() {
nassertv(_is_connected);
if (_threads_reported < (int)_threads.size()) {
PStatClientControlMessage message;
message._type = PStatClientControlMessage::T_define_threads;
message._first_thread_index = _threads_reported;
while (_threads_reported < (int)_threads.size()) {
message._names.push_back(_threads[_threads_reported]._name);
_threads_reported++;
}
Datagram datagram;
message.encode(datagram);
_writer.send(datagram, _tcp_connection);
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::handle_server_control_message
// Access: Private
// Description: Called when a control message has been received by
// the server over the TCP connection.
////////////////////////////////////////////////////////////////////
void PStatClient::
handle_server_control_message(const PStatServerControlMessage &message) {
switch (message._type) {
case PStatServerControlMessage::T_hello:
pstats_cat.info()
<< "Connected to " << message._server_progname << " on "
<< message._server_hostname << "\n";
_server.set_port(message._udp_port);
_got_udp_port = true;
break;
default:
pstats_cat.error()
<< "Invalid control message received from server.\n";
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::connection_reset
// Access: Private, Virtual
// Description: Called by the internal net code when the connection
// has been lost.
////////////////////////////////////////////////////////////////////
void PStatClient::
connection_reset(const PT(Connection) &connection, PRErrorCode) {
if (connection == _tcp_connection) {
disconnect();
} else {
pstats_cat.warning()
<< "Ignoring spurious connection_reset() message\n";
}
}
#endif // DO_PSTATS

View File

@ -22,19 +22,10 @@
#include "pandabase.h"
#include "pStatFrameData.h"
#include "clockObject.h"
#include "pStatClientImpl.h"
#include "luse.h"
#include "pmap.h"
#ifdef HAVE_NET
#include "connectionManager.h"
#include "queuedConnectionReader.h"
#include "connectionWriter.h"
#include "netAddress.h"
#endif
class PStatServerControlMessage;
class PStatCollector;
class PStatCollectorDef;
class PStatThread;
@ -62,25 +53,26 @@ public:
PStatClient();
~PStatClient();
PUBLISHED:
INLINE void set_client_name(const string &name);
INLINE string get_client_name() const;
INLINE void set_max_rate(float rate);
INLINE float get_max_rate() const;
int get_num_collectors() const;
INLINE int get_num_collectors() const;
PStatCollector get_collector(int index) const;
const PStatCollectorDef &get_collector_def(int index) const;
string get_collector_name(int index) const;
string get_collector_fullname(int index) const;
int get_num_threads() const;
INLINE int get_num_threads() const;
PStatThread get_thread(int index) const;
string get_thread_name(int index) const;
INLINE string get_thread_name(int index) const;
const ClockObject &get_clock() const;
PStatThread get_main_thread() const;
PUBLISHED:
INLINE const ClockObject &get_clock() const;
INLINE static bool connect(const string &hostname = string(), int port = -1);
INLINE static void disconnect();
INLINE static bool is_connected();
@ -89,17 +81,20 @@ PUBLISHED:
static void main_tick();
public:
void client_main_tick();
INLINE bool client_connect(string hostname, int port);
void client_disconnect();
INLINE bool client_is_connected() const;
INLINE void client_resume_after_pause();
static PStatClient *get_global_pstats();
void client_main_tick();
bool client_connect(string hostname, int port);
void client_disconnect();
bool client_is_connected() const;
void client_resume_after_pause();
private:
INLINE bool has_impl() const;
INLINE PStatClientImpl *get_impl();
INLINE const PStatClientImpl *get_impl() const;
PStatCollector make_collector_with_relname(int parent_index, string relname);
PStatCollector make_collector_with_name(int parent_index, const string &name);
PStatThread make_thread(const string &name);
@ -117,14 +112,6 @@ private:
void add_level(int collector_index, int thread_index, float increment);
float get_level(int collector_index, int thread_index) const;
void new_frame(int thread_index);
void transmit_frame_data(int thread_index);
void transmit_control_data();
// Stats collecting stuff
ClockObject _clock;
// Not a phash_map, so the threads remain sorted by name.
typedef pmap<string, int> ThingsByName;
ThingsByName _threads_by_name;
@ -167,43 +154,18 @@ private:
typedef pvector<Thread> Threads;
Threads _threads;
PStatClientImpl *_impl;
private:
// Networking stuff
string get_hostname();
void send_hello();
void report_new_collectors();
void report_new_threads();
void handle_server_control_message(const PStatServerControlMessage &message);
virtual void connection_reset(const PT(Connection) &connection,
PRErrorCode errcode);
bool _is_connected;
bool _got_udp_port;
NetAddress _server;
QueuedConnectionReader _reader;
ConnectionWriter _writer;
PT(Connection) _tcp_connection;
PT(Connection) _udp_connection;
int _collectors_reported;
int _threads_reported;
string _hostname;
string _client_name;
float _max_rate;
float _tcp_count_factor;
float _udp_count_factor;
unsigned int _tcp_count;
unsigned int _udp_count;
static PStatCollector _total_size_pcollector;
static PStatCollector _cpp_size_pcollector;
static PStatCollector _interpreter_size_pcollector;
static PStatCollector _pstats_pcollector;
static PStatClient *_global_pstats;
friend class PStatCollector;
friend class PStatThread;
friend class PStatClientImpl;
};
#include "pStatClient.I"

View File

@ -0,0 +1,121 @@
// Filename: pStatClientImpl.I
// Created by: drose (23Dec04)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: PStatClientImpl::set_client_name
// Access: Public
// Description: Sets the name of the client. This is reported to the
// PStatsServer, and will presumably be written in the
// title bar or something.
////////////////////////////////////////////////////////////////////
INLINE void PStatClientImpl::
set_client_name(const string &name) {
_client_name = name;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClientImpl::get_client_name
// Access: Public
// Description: Retrieves the name of the client as set.
////////////////////////////////////////////////////////////////////
INLINE string PStatClientImpl::
get_client_name() const {
return _client_name;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClientImpl::set_max_rate
// Access: Public
// Description: Controls the number of packets that will be sent to
// the server. Normally, one packet is sent per frame,
// but this can flood the server with more packets than
// it can handle if the frame rate is especially good
// (e.g. if nothing is onscreen at the moment). Set
// this parameter to a reasonable number to prevent this
// from happening.
//
// This number specifies the maximum number of packets
// that will be sent to the server per second, per
// thread.
////////////////////////////////////////////////////////////////////
INLINE void PStatClientImpl::
set_max_rate(float rate) {
_max_rate = rate;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClientImpl::get_max_rate
// Access: Public
// Description: Returns the maximum number of packets that will be
// sent to the server per second, per thread. See
// set_max_rate().
////////////////////////////////////////////////////////////////////
INLINE float PStatClientImpl::
get_max_rate() const {
return _max_rate;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClientImpl::get_clock
// Access: Public
// Description: Returns a reference to the PStatClientImpl's clock
// object. It keeps its own clock, instead of using the
// global clock object, so the stats won't get mucked up
// if you put the global clock in non-real-time mode or
// something.
////////////////////////////////////////////////////////////////////
INLINE const ClockObject &PStatClientImpl::
get_clock() const {
return _clock;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClientImpl::client_main_tick
// Access: Public
// Description: Called only by PStatClient::client_main_tick().
////////////////////////////////////////////////////////////////////
INLINE void PStatClientImpl::
client_main_tick() {
_clock.tick();
}
////////////////////////////////////////////////////////////////////
// Function: PStatClientImpl::client_is_connected
// Access: Public
// Description: Called only by PStatClient::client_is_connected().
////////////////////////////////////////////////////////////////////
INLINE bool PStatClientImpl::
client_is_connected() const {
return _is_connected;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClientImpl::client_resume_after_pause
// Access: Public
// Description: Called only by PStatClient::client_resume_after_pause().
////////////////////////////////////////////////////////////////////
INLINE void PStatClientImpl::
client_resume_after_pause() {
// Simply reset the clock to the beginning of the last frame. This
// may lose a frame, but on the other hand we won't skip a whole
// slew of frames either.
double frame_time = _clock.get_frame_time();
_clock.set_real_time(frame_time);
}

View File

@ -0,0 +1,448 @@
// Filename: pStatClientImpl.cxx
// Created by: drose (23Dec04)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "pStatClientImpl.h"
// This file only defines anything if DO_PSTATS is defined.
#ifdef DO_PSTATS
#include "pStatClient.h"
#include "pStatClientControlMessage.h"
#include "pStatServerControlMessage.h"
#include "pStatCollector.h"
#include "pStatThread.h"
#include "config_pstats.h"
#include "pStatProperties.h"
#include "cmath.h"
#include "mathNumbers.h"
#include <algorithm>
#ifdef WIN32_VC
#include <windows.h>
#endif
////////////////////////////////////////////////////////////////////
// Function: PStatClientImpl::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
PStatClientImpl::
PStatClientImpl(PStatClient *client) :
_client(client),
_reader(this, 0),
_writer(this, get_pstats_threaded_write() ? 1 : 0)
{
_is_connected = false;
_got_udp_port = false;
_collectors_reported = 0;
_threads_reported = 0;
// Make sure our clock is in "normal" mode.
_clock.set_mode(ClockObject::M_normal);
_client_name = get_pstats_name();
_max_rate = get_pstats_max_rate();
_tcp_count = 1;
_udp_count = 1;
double pstats_tcp_ratio = get_pstats_tcp_ratio();
if (pstats_tcp_ratio >= 1.0f) {
_tcp_count_factor = 0.0f;
_udp_count_factor = 1.0f;
} else if (pstats_tcp_ratio <= 0.0f) {
_tcp_count_factor = 1.0f;
_udp_count_factor = 0.0f;
} else {
csincos(pstats_tcp_ratio * MathNumbers::pi_f / 2.0f,
&_udp_count_factor,
&_tcp_count_factor);
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClientImpl::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
PStatClientImpl::
~PStatClientImpl() {
nassertv(!_is_connected);
}
////////////////////////////////////////////////////////////////////
// Function: PStatClientImpl::client_connect
// Access: Public
// Description: Called only by PStatClient::client_connect().
////////////////////////////////////////////////////////////////////
bool PStatClientImpl::
client_connect(string hostname, int port) {
nassertr(!_is_connected, true);
if (hostname.empty()) {
hostname = pstats_host;
}
if (port < 0) {
port = pstats_port;
}
if (!_server.set_host(hostname, port)) {
pstats_cat.error()
<< "Unknown host: " << hostname << "\n";
return false;
}
_tcp_connection = open_TCP_client_connection(_server, 5000);
if (_tcp_connection.is_null()) {
pstats_cat.error()
<< "Couldn't connect to PStatServer at " << hostname << ":"
<< port << "\n";
return false;
}
// Make sure we're not queuing up multiple TCP sockets--we expect
// immediate writes of our TCP datagrams.
_tcp_connection->set_collect_tcp(false);
_reader.add_connection(_tcp_connection);
_is_connected = true;
_udp_connection = open_UDP_connection();
send_hello();
return _is_connected;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClientImpl::client_disconnect
// Access: Public
// Description: Called only by PStatClient::client_disconnect().
////////////////////////////////////////////////////////////////////
void PStatClientImpl::
client_disconnect() {
if (_is_connected) {
_reader.remove_connection(_tcp_connection);
close_connection(_tcp_connection);
close_connection(_udp_connection);
}
_tcp_connection.clear();
_udp_connection.clear();
_is_connected = false;
_got_udp_port = false;
_collectors_reported = 0;
_threads_reported = 0;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClientImpl::new_frame
// Access: Public
// Description: Called by the PStatThread interface at the beginning
// of every frame, for each thread. This resets the
// clocks for the new frame and transmits the data for
// the previous frame.
////////////////////////////////////////////////////////////////////
void PStatClientImpl::
new_frame(int thread_index) {
nassertv(thread_index >= 0 && thread_index < (int)_client->_threads.size());
PStatClient::Thread &thread = _client->_threads[thread_index];
// If we're the main thread, we should exchange control packets with
// the server.
if (thread_index == 0) {
transmit_control_data();
}
// If we've got the UDP port by the time the frame starts, it's
// time to become active and start actually tracking data.
if (_got_udp_port) {
thread._is_active = true;
}
if (!thread._is_active) {
return;
}
float frame_start = _clock.get_real_time();
if (!thread._frame_data.is_empty()) {
// Collector 0 is the whole frame.
_client->stop(0, thread_index, frame_start);
// Fill up the level data for all the collectors who have level
// data for this thread.
int num_collectors = _client->_collectors.size();
for (int i = 0; i < num_collectors; i++) {
const PStatClient::PerThreadData &ptd =
_client->_collectors[i]._per_thread[thread_index];
if (ptd._has_level) {
thread._frame_data.add_level(i, ptd._level);
}
}
transmit_frame_data(thread_index);
}
thread._frame_data.clear();
thread._frame_number++;
_client->start(0, thread_index, frame_start);
// Also record the time for the PStats operation itself.
int pstats_index = PStatClient::_pstats_pcollector.get_index();
_client->start(pstats_index, thread_index, frame_start);
_client->stop(pstats_index, thread_index, _clock.get_real_time());
}
////////////////////////////////////////////////////////////////////
// Function: PStatClientImpl::transmit_frame_data
// Access: Private
// Description: Should be called once per frame per thread to
// transmit the latest data to the PStatServer.
////////////////////////////////////////////////////////////////////
void PStatClientImpl::
transmit_frame_data(int thread_index) {
nassertv(thread_index >= 0 && thread_index < (int)_client->_threads.size());
if (_is_connected && _client->_threads[thread_index]._is_active) {
// We don't want to send too many packets in a hurry and flood the
// server. Check that enough time has elapsed for us to send a
// new packet. If not, we'll drop this packet on the floor and
// send a new one next time around.
float now = _clock.get_real_time();
if (now >= _client->_threads[thread_index]._next_packet) {
// We don't want to send more than _max_rate UDP-size packets
// per second, per thread.
float packet_delay = 1.0 / _max_rate;
// Send new data.
NetDatagram datagram;
// We always start with a zero byte, to differentiate it from a
// control message.
datagram.add_uint8(0);
datagram.add_uint16(thread_index);
datagram.add_uint32(_client->_threads[thread_index]._frame_number);
_client->_threads[thread_index]._frame_data.write_datagram(datagram);
if (_writer.is_valid_for_udp(datagram)) {
if (_udp_count * _udp_count_factor < _tcp_count * _tcp_count_factor) {
// Send this one as a UDP packet.
nassertv(_got_udp_port);
_writer.send(datagram, _udp_connection, _server);
_udp_count++;
if (_udp_count == 0) {
// Wraparound!
_udp_count = 1;
_tcp_count = 1;
}
} else {
// Send this one as a TCP packet.
_writer.send(datagram, _tcp_connection);
_tcp_count++;
if (_tcp_count == 0) {
// Wraparound!
_udp_count = 1;
_tcp_count = 1;
}
}
} else {
_writer.send(datagram, _tcp_connection);
// If our packets are so large that we must ship them via TCP,
// then artificially slow down the packet rate even further.
int packet_ratio =
(datagram.get_length() + maximum_udp_datagram - 1) /
maximum_udp_datagram;
packet_delay *= (float)packet_ratio;
}
_client->_threads[thread_index]._next_packet = now + packet_delay;
}
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClientImpl::transmit_control_data
// Access: Private
// Description: Should be called once a frame to exchange control
// information with the server.
////////////////////////////////////////////////////////////////////
void PStatClientImpl::
transmit_control_data() {
// Check for new messages from the server.
while (_is_connected && _reader.data_available()) {
NetDatagram datagram;
if (_reader.get_data(datagram)) {
PStatServerControlMessage message;
if (message.decode(datagram)) {
handle_server_control_message(message);
} else {
pstats_cat.error()
<< "Got unexpected message from server.\n";
}
}
}
if (_is_connected) {
report_new_collectors();
report_new_threads();
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClientImpl::get_hostname
// Access: Private
// Description: Returns the current machine's hostname.
////////////////////////////////////////////////////////////////////
string PStatClientImpl::
get_hostname() {
if (_hostname.empty()) {
char temp_buff[1024];
if (gethostname(temp_buff, 1024) == 0) {
_hostname = temp_buff;
} else {
_hostname = "unknown";
}
}
return _hostname;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClientImpl::send_hello
// Access: Private
// Description: Sends the initial greeting message to the server.
////////////////////////////////////////////////////////////////////
void PStatClientImpl::
send_hello() {
nassertv(_is_connected);
PStatClientControlMessage message;
message._type = PStatClientControlMessage::T_hello;
message._client_hostname = get_hostname();
message._client_progname = _client_name;
message._major_version = get_current_pstat_major_version();
message._minor_version = get_current_pstat_minor_version();
Datagram datagram;
message.encode(datagram);
_writer.send(datagram, _tcp_connection);
}
////////////////////////////////////////////////////////////////////
// Function: PStatClientImpl::report_new_collectors
// Access: Private
// Description: Sends over any information about new Collectors that
// the user code might have recently created.
////////////////////////////////////////////////////////////////////
void PStatClientImpl::
report_new_collectors() {
nassertv(_is_connected);
if (_collectors_reported < (int)_client->_collectors.size()) {
PStatClientControlMessage message;
message._type = PStatClientControlMessage::T_define_collectors;
while (_collectors_reported < (int)_client->_collectors.size()) {
message._collectors.push_back(_client->_collectors[_collectors_reported]._def);
_collectors_reported++;
}
Datagram datagram;
message.encode(datagram);
_writer.send(datagram, _tcp_connection);
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClientImpl::report_new_threads
// Access: Private
// Description: Sends over any information about new Threads that
// the user code might have recently created.
////////////////////////////////////////////////////////////////////
void PStatClientImpl::
report_new_threads() {
nassertv(_is_connected);
if (_threads_reported < (int)_client->_threads.size()) {
PStatClientControlMessage message;
message._type = PStatClientControlMessage::T_define_threads;
message._first_thread_index = _threads_reported;
while (_threads_reported < (int)_client->_threads.size()) {
message._names.push_back(_client->_threads[_threads_reported]._name);
_threads_reported++;
}
Datagram datagram;
message.encode(datagram);
_writer.send(datagram, _tcp_connection);
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClientImpl::handle_server_control_message
// Access: Private
// Description: Called when a control message has been received by
// the server over the TCP connection.
////////////////////////////////////////////////////////////////////
void PStatClientImpl::
handle_server_control_message(const PStatServerControlMessage &message) {
switch (message._type) {
case PStatServerControlMessage::T_hello:
pstats_cat.info()
<< "Connected to " << message._server_progname << " on "
<< message._server_hostname << "\n";
_server.set_port(message._udp_port);
_got_udp_port = true;
break;
default:
pstats_cat.error()
<< "Invalid control message received from server.\n";
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClientImpl::connection_reset
// Access: Private, Virtual
// Description: Called by the internal net code when the connection
// has been lost.
////////////////////////////////////////////////////////////////////
void PStatClientImpl::
connection_reset(const PT(Connection) &connection, PRErrorCode) {
if (connection == _tcp_connection) {
_client->client_disconnect();
} else {
pstats_cat.warning()
<< "Ignoring spurious connection_reset() message\n";
}
}
#endif // DO_PSTATS

View File

@ -0,0 +1,129 @@
// Filename: pStatClientImpl.h
// Created by: drose (23Dec04)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef PSTATCLIENTIMPL_H
#define PSTATCLIENTIMPL_H
#include "pandabase.h"
// This class doesn't exist at all unless DO_PSTATS is defined.
#ifdef DO_PSTATS
#include "pStatFrameData.h"
#include "connectionManager.h"
#include "queuedConnectionReader.h"
#include "connectionWriter.h"
#include "netAddress.h"
#include "clockObject.h"
#include "luse.h"
#include "pmap.h"
class PStatClient;
class PStatServerControlMessage;
class PStatCollector;
class PStatCollectorDef;
class PStatThread;
////////////////////////////////////////////////////////////////////
// Class : PStatClientImpl
// Description : This class is the implementation of the actual
// PStatClient class (which is just for interface). All
// of the stuff to manage sending stats up to the server
// is handled by this class.
//
// This separation between PStatClient and
// PStatClientImpl allows the global PStatClient to be
// constructed at static init time, without having to
// consult any config variables at that time. We don't
// actually do any real work until someone explicitly
// calls PStatClient::connect().
//
// This class doesn't exist at all unless DO_PSTATS is
// defined.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA PStatClientImpl : public ConnectionManager {
public:
PStatClientImpl(PStatClient *client);
~PStatClientImpl();
INLINE void set_client_name(const string &name);
INLINE string get_client_name() const;
INLINE void set_max_rate(float rate);
INLINE float get_max_rate() const;
INLINE const ClockObject &get_clock() const;
INLINE void client_main_tick();
bool client_connect(string hostname, int port);
void client_disconnect();
INLINE bool client_is_connected() const;
INLINE void client_resume_after_pause();
void new_frame(int thread_index);
private:
void transmit_frame_data(int thread_index);
void transmit_control_data();
// Stats collecting stuff
ClockObject _clock;
// Networking stuff
string get_hostname();
void send_hello();
void report_new_collectors();
void report_new_threads();
void handle_server_control_message(const PStatServerControlMessage &message);
virtual void connection_reset(const PT(Connection) &connection,
PRErrorCode errcode);
PStatClient *_client;
bool _is_connected;
bool _got_udp_port;
NetAddress _server;
QueuedConnectionReader _reader;
ConnectionWriter _writer;
PT(Connection) _tcp_connection;
PT(Connection) _udp_connection;
int _collectors_reported;
int _threads_reported;
string _hostname;
string _client_name;
float _max_rate;
float _tcp_count_factor;
float _udp_count_factor;
unsigned int _tcp_count;
unsigned int _udp_count;
};
#include "pStatClientImpl.I"
#endif // DO_PSTATS
#endif

View File

@ -99,6 +99,6 @@ operator = (const PStatThread &copy) {
INLINE void PStatThread::
new_frame() {
#ifdef DO_PSTATS
_client->new_frame(_index);
_client->get_impl()->new_frame(_index);
#endif
}

View File

@ -1,5 +1,6 @@
#include "config_pstats.cxx"
#include "pStatClient.cxx"
#include "pStatClientImpl.cxx"
#include "pStatClientVersion.cxx"
#include "pStatClientControlMessage.cxx"