From db8cdc119d51331cfe870105f2547bd33c26f632 Mon Sep 17 00:00:00 2001 From: David Rose Date: Thu, 23 Dec 2004 19:10:35 +0000 Subject: [PATCH] split out PStatClientImpl --- panda/src/pstatclient/Sources.pp | 8 +- panda/src/pstatclient/pStatClient.I | 146 +++- panda/src/pstatclient/pStatClient.cxx | 682 +++--------------- panda/src/pstatclient/pStatClient.h | 88 +-- panda/src/pstatclient/pStatClientImpl.I | 121 ++++ panda/src/pstatclient/pStatClientImpl.cxx | 448 ++++++++++++ panda/src/pstatclient/pStatClientImpl.h | 129 ++++ panda/src/pstatclient/pStatThread.I | 2 +- .../pstatclient/pstatclient_composite1.cxx | 1 + 9 files changed, 981 insertions(+), 644 deletions(-) create mode 100644 panda/src/pstatclient/pStatClientImpl.I create mode 100644 panda/src/pstatclient/pStatClientImpl.cxx create mode 100644 panda/src/pstatclient/pStatClientImpl.h diff --git a/panda/src/pstatclient/Sources.pp b/panda/src/pstatclient/Sources.pp index 60bd4178e2..040e9185d3 100644 --- a/panda/src/pstatclient/Sources.pp +++ b/panda/src/pstatclient/Sources.pp @@ -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 \ diff --git a/panda/src/pstatclient/pStatClient.I b/panda/src/pstatclient/pStatClient.I index d420a3d81c..89beec2cdb 100644 --- a/panda/src/pstatclient/pStatClient.I +++ b/panda/src/pstatclient/pStatClient.I @@ -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(); +} diff --git a/panda/src/pstatclient/pStatClient.cxx b/panda/src/pstatclient/pStatClient.cxx index af044a276f..6381507110 100644 --- a/panda/src/pstatclient/pStatClient.cxx +++ b/panda/src/pstatclient/pStatClient.cxx @@ -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 - -#ifdef WIN32_VC -#define WINDOWS_LEAN_AND_MEAN -#include -#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 diff --git a/panda/src/pstatclient/pStatClient.h b/panda/src/pstatclient/pStatClient.h index fb122074d8..b98c6d1eac 100644 --- a/panda/src/pstatclient/pStatClient.h +++ b/panda/src/pstatclient/pStatClient.h @@ -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 ThingsByName; ThingsByName _threads_by_name; @@ -167,43 +154,18 @@ private: typedef pvector 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" diff --git a/panda/src/pstatclient/pStatClientImpl.I b/panda/src/pstatclient/pStatClientImpl.I new file mode 100644 index 0000000000..4fe0905780 --- /dev/null +++ b/panda/src/pstatclient/pStatClientImpl.I @@ -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); +} diff --git a/panda/src/pstatclient/pStatClientImpl.cxx b/panda/src/pstatclient/pStatClientImpl.cxx new file mode 100644 index 0000000000..bd0aba74de --- /dev/null +++ b/panda/src/pstatclient/pStatClientImpl.cxx @@ -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 + +#ifdef WIN32_VC +#include +#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 diff --git a/panda/src/pstatclient/pStatClientImpl.h b/panda/src/pstatclient/pStatClientImpl.h new file mode 100644 index 0000000000..1d83a87e8c --- /dev/null +++ b/panda/src/pstatclient/pStatClientImpl.h @@ -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 + diff --git a/panda/src/pstatclient/pStatThread.I b/panda/src/pstatclient/pStatThread.I index 406eef9de2..c5f02ece98 100644 --- a/panda/src/pstatclient/pStatThread.I +++ b/panda/src/pstatclient/pStatThread.I @@ -99,6 +99,6 @@ operator = (const PStatThread ©) { INLINE void PStatThread:: new_frame() { #ifdef DO_PSTATS - _client->new_frame(_index); + _client->get_impl()->new_frame(_index); #endif } diff --git a/panda/src/pstatclient/pstatclient_composite1.cxx b/panda/src/pstatclient/pstatclient_composite1.cxx index 0e05d4655b..cff704072c 100644 --- a/panda/src/pstatclient/pstatclient_composite1.cxx +++ b/panda/src/pstatclient/pstatclient_composite1.cxx @@ -1,5 +1,6 @@ #include "config_pstats.cxx" #include "pStatClient.cxx" +#include "pStatClientImpl.cxx" #include "pStatClientVersion.cxx" #include "pStatClientControlMessage.cxx"